/* NutriDMS, role-specific screens
   Analytics · Calendar · Assignments · Audit · Integrations · Urgent · Published · Nutrition Review */

// ───────────────── ANALYTICS (Admin / Super Admin) ─────────────────
function AnalyticsScreen() {
  const [range, setRange] = React.useState("30d");
  const RANGES = { "7d": "last 7 days", "30d": "last 30 days", "90d": "last 90 days", "ytd": "year to date" };
  const KPIS = [
    { label: "Submissions", value: "142", icon: "upload-cloud", accent: "#1B7528", spark: [4,7,5,8,6,9,11,9,12,14], up: true, delta: "+18%", note: "vs prev." },
    { label: "Published", value: "86", icon: "globe", accent: "#6938EF", spark: [2,3,4,5,6,7,8,9,11,12], up: true, delta: "+12%", note: "vs prev." },
    { label: "Avg. time to publish", value: "3.4d", icon: "timer", accent: "#0BA5EC", spark: [6,5.5,5,4.6,4.2,4,3.8,3.6,3.5,3.4], up: false, delta: "-22%", note: "faster" },
    { label: "Active contributors", value: "11", icon: "users", accent: "#DD2590", spark: [7,8,8,9,9,10,10,11,11,11], up: true, delta: "+2", note: "this period" },
  ];
  return (
    <div>
      <Crumbs path={[{ label: "Analytics" }]} />
      <div className="an-hero">
        <div className="an-hero-txt">
          <span className="an-hero-eyebrow"><Icon name="line-chart" size={13} /> Platform analytics</span>
          <h1 className="an-hero-title">Analytics overview</h1>
          <p className="an-hero-sub">Performance, throughput and locale coverage · {RANGES[range]}</p>
        </div>
        <div className="an-range">
          {[["7d", "7d"], ["30d", "30d"], ["90d", "90d"], ["ytd", "YTD"]].map(([id, l]) => (
            <button key={id} className={range === id ? "on" : ""} onClick={() => setRange(id)}>{l}</button>
          ))}
        </div>
      </div>

      <ReportBar feature="Analytics" role={(typeof window.__role !== "undefined" ? window.__role : "super-admin")} />

      <div className="an-kpis">
        {KPIS.map((k) => (
          <div key={k.label} className="an-kpi" style={{ "--ac": k.accent }}>
            <div className="an-kpi-top">
              <span className="an-kpi-ic"><Icon name={k.icon} size={17} /></span>
              <span className={"an-kpi-delta " + (k.up ? "up" : "down")}><Icon name={k.up ? "trending-up" : "trending-down"} size={12} /> {k.delta}</span>
            </div>
            <div className="an-kpi-val">{k.value}</div>
            <div className="an-kpi-lbl">{k.label} <em>{k.note}</em></div>
            <AnSpark data={k.spark} color={k.accent} />
          </div>
        ))}
      </div>

      <div className="an-grid an-grid-2-1">
        <ThroughputChart />
        <PipelineFunnel />
      </div>
      <div className="an-grid an-grid-1-1">
        <LocaleCoverage />
        <ContributorLeaderboard />
      </div>
      <CuisineBreakdown />
    </div>
  );
}

function AnSpark({ data, color }) {
  const W = 200, H = 40, mn = Math.min(...data), mx = Math.max(...data), r = (mx - mn) || 1;
  const X = (i) => (i / (data.length - 1)) * W;
  const Y = (v) => H - 4 - ((v - mn) / r) * (H - 8);
  const line = data.map((v, i) => `${i ? "L" : "M"}${X(i)},${Y(v)}`).join(" ");
  const id = "sp" + Math.random().toString(36).slice(2, 7);
  return (
    <svg className="an-kpi-spark" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none">
      <defs><linearGradient id={id} x1="0" x2="0" y1="0" y2="1"><stop offset="0%" stopColor={color} stopOpacity=".22" /><stop offset="100%" stopColor={color} stopOpacity="0" /></linearGradient></defs>
      <path d={`${line} L${W},${H} L0,${H} Z`} fill={`url(#${id})`} />
      <path d={line} fill="none" stroke={color} strokeWidth="2" vectorEffect="non-scaling-stroke" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

function ThroughputChart() {
  // Stacked line for submissions vs publishes over 30 days
  const days = 30;
  const subs = [4,7,5,8,6,9,11,7,10,8,12,9,11,13,10,12,15,11,14,13,17,15,12,18,16,14,17,19,16,20];
  const pubs = [2,3,4,5,3,6,7,5,6,7,8,7,9,8,9,10,11,9,10,11,12,11,9,13,12,11,13,14,12,15];
  const max = Math.max(...subs);
  const W = 540, H = 240, P = 30;
  const x = (i) => P + (i / (days - 1)) * (W - P * 2);
  const y = (v) => H - P - (v / max) * (H - P * 2);
  const linePath = (arr) => arr.map((v, i) => `${i === 0 ? "M" : "L"}${x(i)},${y(v)}`).join(" ");
  const areaPath = (arr) => `${linePath(arr)} L${x(days - 1)},${H - P} L${x(0)},${H - P} Z`;
  return (
    <div className="card pad">
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 8 }}>
        <div>
          <h3 className="section-title" style={{ fontSize: 18, margin: 0 }}>Submissions vs publishes</h3>
          <p className="section-sub">Daily throughput, last 30 days</p>
        </div>
        <div style={{ display: "flex", gap: 14, fontSize: 12, fontWeight: 600 }}>
          <span style={{ display: "flex", alignItems: "center", gap: 6 }}><span style={{ width: 10, height: 10, borderRadius: 3, background: "var(--green-700)" }} />Submissions</span>
          <span style={{ display: "flex", alignItems: "center", gap: 6 }}><span style={{ width: 10, height: 10, borderRadius: 3, background: "#6938EF" }} />Published</span>
        </div>
      </div>
      <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%", height: "auto" }} className="an-chart">
        <defs>
          <linearGradient id="ga1" x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor="var(--green-700)" stopOpacity=".28" />
            <stop offset="100%" stopColor="var(--green-700)" stopOpacity="0" />
          </linearGradient>
          <linearGradient id="ga2" x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor="#6938EF" stopOpacity=".22" />
            <stop offset="100%" stopColor="#6938EF" stopOpacity="0" />
          </linearGradient>
        </defs>
        {[0, 0.25, 0.5, 0.75, 1].map((t, i) => (
          <g key={i}>
            <line x1={P} x2={W - P} y1={P + t * (H - P * 2)} y2={P + t * (H - P * 2)} stroke="var(--gray-100)" strokeWidth="1" />
            <text x={P - 8} y={P + t * (H - P * 2) + 3} textAnchor="end" fontSize="9" fill="var(--gray-400)" fontFamily="Manrope">{Math.round(max * (1 - t))}</text>
          </g>
        ))}
        <path d={areaPath(subs)} fill="url(#ga1)" />
        <path d={areaPath(pubs)} fill="url(#ga2)" />
        <path d={linePath(subs)} fill="none" stroke="var(--green-700)" strokeWidth="2.5" strokeLinejoin="round" />
        <path d={linePath(pubs)} fill="none" stroke="#6938EF" strokeWidth="2.5" strokeLinejoin="round" />
        {subs.map((v, i) => i % 5 === 4 ? <circle key={"s" + i} cx={x(i)} cy={y(v)} r="3.5" fill="#fff" stroke="var(--green-700)" strokeWidth="2" /> : null)}
        {pubs.map((v, i) => i % 5 === 4 ? <circle key={"p" + i} cx={x(i)} cy={y(v)} r="3.5" fill="#fff" stroke="#6938EF" strokeWidth="2" /> : null)}
      </svg>
    </div>
  );
}

function PipelineFunnel() {
  const steps = [
    { label: "Drafted",       value: 142, color: "var(--gray-400)" },
    { label: "Submitted",     value: 128, color: "var(--green-700)" },
    { label: "Nutrition ✓",   value: 112, color: "var(--info)", colorOverride: "#2A54E5" },
    { label: "Compliance ✓",  value: 98,  color: "var(--warning-500)" },
    { label: "Published",     value: 86,  color: "#6938EF" },
  ];
  const max = steps[0].value;
  return (
    <div className="card pad">
      <h3 className="section-title" style={{ fontSize: 18, margin: 0 }}>Publishing funnel</h3>
      <p className="section-sub" style={{ marginBottom: 14 }}>Drop-off at each stage</p>
      <div className="col" style={{ gap: 10 }}>
        {steps.map((s, i) => {
          const pct = (s.value / max) * 100;
          const fill = s.colorOverride || s.color;
          return (
            <div key={s.label}>
              <div style={{ display: "flex", justifyContent: "space-between", fontSize: 13, marginBottom: 4 }}>
                <span style={{ fontWeight: 600 }}>{s.label}</span>
                <span><strong>{s.value}</strong> <span className="muted" style={{ fontSize: 11 }}>· {Math.round(pct)}%</span></span>
              </div>
              <div style={{ height: 28, background: "var(--gray-100)", borderRadius: 8, overflow: "hidden" }}>
                <div style={{ width: `${pct}%`, height: "100%", background: fill, borderRadius: 8, transition: "width .4s ease" }} />
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function LocaleCoverage() {
  const locales = [
    { code: "en", label: "English",   total: 86, translated: 86 },
    { code: "es", label: "Español",   total: 86, translated: 71 },
    { code: "fr", label: "Français",  total: 86, translated: 64 },
    { code: "ja", label: "日本語",     total: 86, translated: 42 },
    { code: "nl", label: "Nederlands", total: 86, translated: 38 },
    { code: "zh", label: "中文",       total: 86, translated: 28 },
  ];
  return (
    <div className="card pad">
      <h3 className="section-title" style={{ fontSize: 18, margin: 0 }}>Locale coverage</h3>
      <p className="section-sub" style={{ marginBottom: 16 }}>How many published recipes exist in each language</p>
      <div className="col" style={{ gap: 12 }}>
        {locales.map((l) => {
          const pct = (l.translated / l.total) * 100;
          const langMeta = LANGUAGES.find((x) => x.code === l.code);
          return (
            <div key={l.code}>
              <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4 }}>
                <span style={{ fontWeight: 600, fontSize: 13 }}>{langMeta?.flag} {l.label}</span>
                <span style={{ fontSize: 13 }}><strong>{l.translated}</strong> <span className="muted">/ {l.total}</span></span>
              </div>
              <div className="progress" style={{ height: 8 }}>
                <i style={{ width: `${pct}%`, background: pct === 100 ? "var(--green-700)" : pct > 60 ? "var(--success-500)" : pct > 30 ? "var(--warning-500)" : "var(--error-500)" }} />
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function ContributorLeaderboard() {
  const rows = [
    { name: "Sarah Chen",    initials: "SC", published: 18, pending: 2, rate: 92 },
    { name: "Marco Rossi",   initials: "MR", published: 14, pending: 0, rate: 100 },
    { name: "Arjun Patel",   initials: "AP", published: 12, pending: 1, rate: 92 },
    { name: "Léa Moreau",    initials: "LM", published: 9,  pending: 2, rate: 81 },
    { name: "Min-Jae Park",  initials: "MP", published: 8,  pending: 3, rate: 72 },
  ];
  return (
    <div className="card pad">
      <h3 className="section-title" style={{ fontSize: 18, margin: 0 }}>Top contributors</h3>
      <p className="section-sub" style={{ marginBottom: 12 }}>Published recipes this period</p>
      <div className="col" style={{ gap: 8 }}>
        {rows.map((r, i) => (
          <div key={r.name} style={{ display: "flex", alignItems: "center", gap: 12, padding: "8px 0", borderBottom: i === rows.length - 1 ? "none" : "1px solid var(--gray-100)" }}>
            <div style={{ width: 22, fontSize: 12, color: "var(--gray-500)", fontWeight: 700, textAlign: "center" }}>#{i + 1}</div>
            <div className="avatar" style={{ background: i === 0 ? "var(--green-700)" : "var(--gray-100)", color: i === 0 ? "#fff" : "var(--gray-700)" }}>{r.initials}</div>
            <div style={{ flex: 1 }}>
              <div style={{ fontWeight: 600, fontSize: 14 }}>{r.name}</div>
              <div className="muted" style={{ fontSize: 12 }}>{r.published} published · {r.pending} pending</div>
            </div>
            <div style={{ display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 4 }}>
              <strong style={{ fontSize: 14 }}>{r.rate}%</strong>
              <div style={{ width: 60, height: 4, background: "var(--gray-100)", borderRadius: 2 }}>
                <div style={{ width: `${r.rate}%`, height: "100%", background: r.rate >= 90 ? "var(--green-700)" : "var(--warning-500)", borderRadius: 2 }} />
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function CuisineBreakdown() {
  const data = [
    { label: "Mediterranean", value: 18, color: "var(--green-700)" },
    { label: "Japanese",      value: 14, color: "#2A54E5" },
    { label: "Italian",       value: 12, color: "var(--error-600)" },
    { label: "Indian",        value: 11, color: "var(--warning-500)" },
    { label: "American",      value: 9,  color: "#6938EF" },
    { label: "French",        value: 8,  color: "#0BA5EC" },
    { label: "Korean",        value: 6,  color: "#DD2590" },
    { label: "Middle Eastern", value: 5, color: "#15B79E" },
    { label: "Modern",        value: 3,  color: "var(--gray-400)" },
  ];
  const max = Math.max(...data.map((d) => d.value));
  return (
    <div className="card pad">
      <h3 className="section-title" style={{ fontSize: 18, margin: 0 }}>Cuisines</h3>
      <p className="section-sub" style={{ marginBottom: 16 }}>Published recipes by cuisine</p>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 14 }}>
        {data.map((d) => (
          <div key={d.label}>
            <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4 }}>
              <span style={{ fontSize: 13, fontWeight: 600 }}>{d.label}</span>
              <strong style={{ fontSize: 13 }}>{d.value}</strong>
            </div>
            <div style={{ height: 6, background: "var(--gray-100)", borderRadius: 999 }}>
              <div style={{ width: `${(d.value / max) * 100}%`, height: "100%", background: d.color, borderRadius: 999 }} />
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ───────────────── PUBLISHING CALENDAR (Manager) ─────────────────
// Color tags a publish slot can carry (label shown in Schedule panel)
const PCAL_TAGS = [
  { id: "olive",      label: "Olive green",     color: "#4d7c0f" },
  { id: "featured",   label: "Featured purple", color: "#6938EF" },
  { id: "seasonal",   label: "Seasonal orange", color: "#E48900" },
  { id: "campaign",   label: "Campaign red",    color: "#E5484D" },
  { id: "ocean",      label: "Ocean blue",      color: "#2A54E5" },
  { id: "ingredient", label: "Ingredient teal", color: "#0E9384" },
];
const PCAL_TAG = (id) => PCAL_TAGS.find((t) => t.id === id) || PCAL_TAGS[0];
const PCAL_TZ = [
  { v: "WAT", label: "West African Time" },
  { v: "GMT", label: "Greenwich Mean Time" },
  { v: "CET", label: "Central European Time" },
  { v: "ET",  label: "Eastern Time" },
  { v: "JST", label: "Japan Standard Time" },
];
function pcalRecipe(id) { return (typeof RECIPES !== "undefined" ? RECIPES : []).find((r) => r.id === id) || null; }
function pcalIngredient(id) { return kbIngredients().find((i) => i.id === id) || null; }
// Resolve display fields for a scheduled slot from the live library record
function pcalResolve(ev) {
  if (ev.kind === "ingredient") {
    const g = pcalIngredient(ev.refId);
    return { name: g ? g.name : ev.name, sub: g ? `${g.canonical} · ${g.category}` : "Ingredient",
      author: g && g.contributor ? g.contributor.name : "—", meta: g ? g.category : "Ingredient", cover: "", record: g };
  }
  const r = pcalRecipe(ev.refId);
  return { name: r ? r.name : ev.name, sub: r ? `${r.cuisine} · ${r.category}` : "Recipe",
    author: r && r.contributor ? r.contributor.name : "—", meta: r ? r.cuisine : "Recipe", cover: r ? r.cover : "", record: r };
}

// June 2026 publishing schedule, recipes + ingredient library items
const PCAL_SEED = [
  { id: "ev-1",  kind: "recipe",     refId: "r-001", y: 2026, m: 5, d: 4,  time: "9:00 AM",  tz: "WAT", tag: "olive",      notes: "Lead breakfast feature for the week." },
  { id: "ev-2",  kind: "recipe",     refId: "r-002", y: 2026, m: 5, d: 7,  time: "12:00 PM", tz: "WAT", tag: "featured",   notes: "Homepage hero, pair with the salmon campaign." },
  { id: "ev-3",  kind: "recipe",     refId: "r-005", y: 2026, m: 5, d: 7,  time: "6:00 PM",  tz: "WAT", tag: "seasonal",   notes: "" },
  { id: "ev-4",  kind: "ingredient", refId: "ing_168474", y: 2026, m: 5, d: 9,  time: "10:00 AM", tz: "WAT", tag: "ingredient", notes: "Updated macros approved, publish to library." },
  { id: "ev-5",  kind: "recipe",     refId: "r-004", y: 2026, m: 5, d: 12, time: "11:00 AM", tz: "WAT", tag: "olive",      notes: "" },
  { id: "ev-6",  kind: "ingredient", refId: "ing_175167", y: 2026, m: 5, d: 16, time: "9:30 AM",  tz: "CET", tag: "ingredient", notes: "" },
  { id: "ev-7",  kind: "recipe",     refId: "r-003", y: 2026, m: 5, d: 18, time: "8:00 PM",  tz: "WAT", tag: "olive",      notes: "Weekend brunch slot." },
  { id: "ev-8",  kind: "recipe",     refId: "r-007", y: 2026, m: 5, d: 18, time: "1:00 PM",  tz: "JST", tag: "ocean",      notes: "" },
  { id: "ev-9",  kind: "recipe",     refId: "r-009", y: 2026, m: 5, d: 18, time: "5:00 PM",  tz: "CET", tag: "campaign",   notes: "Pizza Friday campaign." },
  { id: "ev-10", kind: "recipe",     refId: "r-006", y: 2026, m: 5, d: 21, time: "10:00 AM", tz: "WAT", tag: "olive",      notes: "" },
  { id: "ev-11", kind: "ingredient", refId: "ing_172262", y: 2026, m: 5, d: 24, time: "2:00 PM", tz: "ET",  tag: "ingredient", notes: "" },
  { id: "ev-12", kind: "recipe",     refId: "r-010", y: 2026, m: 5, d: 25, time: "12:00 PM", tz: "WAT", tag: "featured",   notes: "" },
  { id: "ev-13", kind: "recipe",     refId: "r-008", y: 2026, m: 5, d: 25, time: "8:00 AM",  tz: "WAT", tag: "seasonal",   notes: "" },
];

function PublishingCalendar() {
  const { openRecipe, openIngredient, toast } = useApp();
  const today = new Date(2026, 5, 18); // June 18, 2026
  const [cursor, setCursor] = React.useState(today);
  const [events, setEvents] = React.useState(PCAL_SEED);
  const [scheduleFor, setScheduleFor] = React.useState(null); // {} = new, or event = edit
  const [detail, setDetail] = React.useState(null);           // event being viewed
  const [hover, setHover] = React.useState(null);             // { ev, top, left }

  const year = cursor.getFullYear();
  const month = cursor.getMonth();
  const firstDay = new Date(year, month, 1);
  const startDow = (firstDay.getDay() + 6) % 7; // Monday-start
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  const monthLabel = cursor.toLocaleDateString("en-GB", { month: "long", year: "numeric" });

  const cells = [];
  for (let i = 0; i < startDow; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(d);
  while (cells.length < 42) cells.push(null);

  const eventsOn = (d) => events.filter((e) => e.y === year && e.m === month && e.d === d);

  const openSource = (ev) => {
    const r = pcalResolve(ev).record;
    if (!r) { toast("Source not found"); return; }
    if (ev.kind === "ingredient") openIngredient(r); else openRecipe(r);
  };
  const saveSchedule = (form) => {
    setEvents((prev) => {
      if (form.id) return prev.map((e) => (e.id === form.id ? { ...e, ...form } : e));
      return [...prev, { ...form, id: "ev-" + Date.now() }];
    });
    toast(form.id ? "Schedule updated" : "Recipe scheduled to publish");
    setScheduleFor(null); setDetail(null);
  };
  const unschedule = (ev) => { setEvents((prev) => prev.filter((e) => e.id !== ev.id)); toast("Unscheduled"); setDetail(null); };

  const showHover = (ev, el) => {
    const r = el.getBoundingClientRect();
    setHover({ ev, top: r.top, left: Math.min(r.left, window.innerWidth - 280) });
  };

  return (
    <div>
      <Crumbs path={[{ label: "Publishing Calendar" }]} />
      <div className="page-head pcal-head">
        <div>
          <h1 className="page-title">Publishing Calendar</h1>
          <p className="page-sub">Plan when approved recipes &amp; ingredients go live across locales.</p>
        </div>
        <div className="pcal-controls">
          <div className="pcal-nav">
            <button className="btn secondary icon-only" aria-label="Previous month" onClick={() => setCursor(new Date(year, month - 1, 1))}><Icon name="chevron-left" size={16} /></button>
            <div className="pcal-month">{monthLabel}</div>
            <button className="btn secondary icon-only" aria-label="Next month" onClick={() => setCursor(new Date(year, month + 1, 1))}><Icon name="chevron-right" size={16} /></button>
          </div>
          <button className="btn secondary" onClick={() => setCursor(today)}>Today</button>
          <button className="btn primary" onClick={() => setScheduleFor({ y: year, m: month })}><Icon name="plus" size={16} /> Schedule</button>
        </div>
      </div>

      <div className="card pcal-card">
        <div className="pcal-dow">
          {["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].map((d) => (
            <div key={d} className="pcal-dow-cell">{d}</div>
          ))}
        </div>
        <div className="pcal-grid">
          {cells.map((d, i) => {
            const isToday = d === today.getDate() && month === today.getMonth() && year === today.getFullYear();
            const isWeekend = (i % 7) >= 5;
            const list = d ? eventsOn(d) : [];
            const dow = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][i % 7];
            const shown = list.slice(0, 3);
            const extra = list.length - shown.length;
            return (
              <div key={i} className={`pcal-cell${isWeekend ? " weekend" : ""}${!d ? " empty" : ""}${list.length ? " has-events" : ""}`}>
                {d && (
                  <>
                    <div className="pcal-daynum-row">
                      <span className="pcal-dow-inline">{dow}</span>
                      <span className={`pcal-daynum${isToday ? " today" : ""}`}>{d}</span>
                    </div>
                    <div className="pcal-events">
                      {shown.map((ev) => {
                        const info = pcalResolve(ev);
                        const c = PCAL_TAG(ev.tag).color;
                        return (
                          <button
                            key={ev.id}
                            className="pcal-event"
                            style={{ background: c }}
                            onClick={() => { setHover(null); setDetail(ev); }}
                            onMouseEnter={(e) => showHover(ev, e.currentTarget)}
                            onMouseLeave={() => setHover(null)}
                          >
                            {ev.kind === "ingredient" && <Icon name="leaf" size={11} stroke={2.6} style={{ flexShrink: 0 }} />}
                            <span className="pcal-event-label">{info.name}</span>
                          </button>
                        );
                      })}
                      {extra > 0 && <span className="pcal-more">+{extra} more</span>}
                    </div>
                  </>
                )}
              </div>
            );
          })}
        </div>
      </div>

      <div className="pcal-legend">
        {PCAL_TAGS.map((t) => (
          <span key={t.id}><span className="sw" style={{ background: t.color }} />{t.label.replace(/ (green|purple|orange|red|blue|teal)$/i, "")}</span>
        ))}
      </div>

      {hover && <PcalHoverCard hover={hover} />}
      {scheduleFor && <PcalScheduleDrawer base={scheduleFor} onClose={() => setScheduleFor(null)} onSave={saveSchedule} />}
      {detail && <PcalDetailDrawer ev={detail} onClose={() => setDetail(null)} onEdit={(ev) => { setDetail(null); setScheduleFor(ev); }} onUnschedule={unschedule} onOpen={openSource} />}
    </div>
  );
}

// Hover popover, quick glance at a scheduled slot
function PcalHoverCard({ hover }) {
  const { ev } = hover;
  const info = pcalResolve(ev);
  const tag = PCAL_TAG(ev.tag);
  return ReactDOM.createPortal(
    <div className="pcal-hover" style={{ top: hover.top, left: hover.left }}>
      <div className="pcal-hover-top">
        {ev.kind === "recipe" && info.cover
          ? <div className="pcal-hover-thumb" style={{ backgroundImage: `url("${info.cover}")` }} />
          : <div className="pcal-hover-thumb leaf"><Icon name="leaf" size={18} stroke={1.9} /></div>}
        <div style={{ minWidth: 0 }}>
          <div className="pcal-hover-name">{info.name}</div>
          <div className="pcal-hover-sub">{info.sub}</div>
        </div>
      </div>
      <div className="pcal-hover-meta">
        <span><Icon name="clock" size={12} /> {ev.time} · {ev.tz}</span>
        <span><Icon name="user" size={12} /> {info.author}</span>
      </div>
      <div className="pcal-hover-tag"><span className="sw" style={{ background: tag.color }} /> {tag.label} · {ev.kind === "ingredient" ? "Ingredient" : "Recipe"}</div>
    </div>, document.body);
}

// Right slide-in: Schedule a recipe / ingredient
function PcalScheduleDrawer({ base, onClose, onSave }) {
  const editing = !!base.id;
  const recipeOpts = (typeof RECIPES !== "undefined" ? RECIPES : []).map((r) => ({ id: r.id, kind: "recipe", name: r.name }));
  const ingOpts = kbIngredients().map((g) => ({ id: g.id, kind: "ingredient", name: g.name }));
  const allOpts = [...recipeOpts, ...ingOpts];
  const initRef = base.refId || (recipeOpts[0] && recipeOpts[0].id);
  const initKind = base.kind || "recipe";
  const [refSel, setRefSel] = React.useState(`${initKind}:${initRef}`);
  const pad = (n) => String(n).padStart(2, "0");
  const [date, setDate] = React.useState(`${base.y != null ? base.y : 2026}-${pad((base.m != null ? base.m : 5) + 1)}-${pad(base.d || 18)}`);
  const [time, setTime] = React.useState(base.time || "12:00 PM");
  const [tz, setTz] = React.useState(base.tz || "WAT");
  const [tag, setTag] = React.useState(base.tag || "olive");
  const [notes, setNotes] = React.useState(base.notes || "");

  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, []);

  const submit = () => {
    const [kind, refId] = refSel.split(":");
    const dt = new Date(date + "T00:00:00");
    onSave({ id: base.id, kind, refId, y: dt.getFullYear(), m: dt.getMonth(), d: dt.getDate(), time, tz, tag, notes });
  };

  return ReactDOM.createPortal(
    <div className="qad-bg" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="qad" role="dialog" aria-label="Schedule">
        <div className="qad-head">
          <div style={{ minWidth: 0 }}>
            <h3 style={{ margin: 0, fontFamily: "var(--serif)", fontSize: 22, letterSpacing: "-.01em" }}>{editing ? "Edit schedule" : "Schedule a recipe"}</h3>
            <p style={{ margin: "4px 0 0", color: "var(--gray-600)", fontSize: 13 }}>Select an approved recipe or ingredient and set a date to publish.</p>
          </div>
          <button className="icon-btn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>

        <div className="qad-body">
          <div className="field">
            <label>Recipe / Ingredient</label>
            <select className="select" value={refSel} onChange={(e) => setRefSel(e.target.value)}>
              <optgroup label="Recipes">{recipeOpts.map((o) => <option key={o.id} value={`recipe:${o.id}`}>{o.name}</option>)}</optgroup>
              <optgroup label="Ingredients">{ingOpts.map((o) => <option key={o.id} value={`ingredient:${o.id}`}>{o.name}</option>)}</optgroup>
            </select>
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
            <div className="field"><label>Published date</label><input type="date" className="input" value={date} onChange={(e) => setDate(e.target.value)} /></div>
            <div className="field"><label>Published time</label>
              <select className="select" value={time} onChange={(e) => setTime(e.target.value)}>
                {["6:00 AM","8:00 AM","9:00 AM","9:30 AM","10:00 AM","11:00 AM","12:00 PM","1:00 PM","2:00 PM","5:00 PM","6:00 PM","8:00 PM"].map((t) => <option key={t}>{t}</option>)}
              </select>
            </div>
          </div>

          <div className="field"><label>Time zone</label>
            <select className="select" value={tz} onChange={(e) => setTz(e.target.value)}>
              {PCAL_TZ.map((z) => <option key={z.v} value={z.v}>{z.label}</option>)}
            </select>
          </div>

          <div className="field"><label>Color tag</label>
            <div className="pcal-tagpick">
              {PCAL_TAGS.map((t) => (
                <button key={t.id} type="button" className={`pcal-tagopt${tag === t.id ? " on" : ""}`} onClick={() => setTag(t.id)}>
                  <span className="sw" style={{ background: t.color }} /> {t.label}
                  {tag === t.id && <Icon name="check" size={14} style={{ marginLeft: "auto", color: t.color }} />}
                </button>
              ))}
            </div>
          </div>

          <div className="field" style={{ marginBottom: 0 }}><label>Editor notes <span className="muted" style={{ fontWeight: 500 }}>· optional</span></label>
            <textarea className="textarea" rows="3" value={notes} onChange={(e) => setNotes(e.target.value)} placeholder="What do you want the publisher to know?" />
          </div>
        </div>

        <div className="qad-foot">
          <button className="btn ghost" onClick={onClose}>Cancel</button>
          <button className="btn primary" onClick={submit}><Icon name="calendar-check" size={15} /> {editing ? "Save changes" : "Schedule"}</button>
        </div>
      </div>
    </div>, document.body);
}

// Right slide-in: scheduled item detail
function PcalDetailDrawer({ ev, onClose, onEdit, onUnschedule, onOpen }) {
  const info = pcalResolve(ev);
  const tag = PCAL_TAG(ev.tag);
  const dateLabel = new Date(ev.y, ev.m, ev.d).toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric" });
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, []);
  const isRec = ev.kind === "recipe";
  return ReactDOM.createPortal(
    <div className="qad-bg" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="qad" role="dialog" aria-label="Scheduled item">
        <div className="qad-head">
          <div style={{ minWidth: 0 }}>
            <h3 style={{ margin: 0, fontFamily: "var(--serif)", fontSize: 22, letterSpacing: "-.01em" }}>{info.name}</h3>
            <p style={{ margin: "4px 0 0", color: "var(--gray-600)", fontSize: 13 }}>Scheduled to publish on {dateLabel}</p>
          </div>
          <button className="icon-btn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>

        <div className="qad-body">
          {isRec && info.cover
            ? <div className="pcal-detail-cover" style={{ backgroundImage: `url("${info.cover}")` }} />
            : <div className="pcal-detail-cover leaf"><Icon name="leaf" size={40} stroke={1.5} /></div>}

          <div style={{ display: "flex", gap: 8, margin: "14px 0 4px", flexWrap: "wrap" }}>
            <span className="pill" style={{ background: "#EAF2FF", color: "#1849A9", border: "1px solid #B2CCFF" }}>Scheduled</span>
            <span className="pill neutral">{info.meta}</span>
            <span className="pill" style={{ background: "#fff", color: tag.color, border: `1px solid ${tag.color}55` }}><span className="sw" style={{ background: tag.color, width: 9, height: 9, borderRadius: 3, display: "inline-block" }} /> {tag.label}</span>
          </div>

          <div className="pcal-detail-grid">
            <div className="pcal-detail-cell"><span className="k"><Icon name="calendar" size={13} /> Published date</span><span className="v">{dateLabel}</span></div>
            <div className="pcal-detail-cell"><span className="k"><Icon name="clock" size={13} /> Time</span><span className="v">{ev.time}</span></div>
            <div className="pcal-detail-cell"><span className="k"><Icon name="user" size={13} /> Author</span><span className="v">{info.author}</span></div>
            <div className="pcal-detail-cell"><span className="k"><Icon name={isRec ? "utensils-crossed" : "leaf"} size={13} /> {isRec ? "Cuisine" : "Category"}</span><span className="v">{info.meta}</span></div>
            <div className="pcal-detail-cell wide"><span className="k"><Icon name="globe" size={13} /> Time zone</span><span className="v">{ev.tz}</span></div>
          </div>

          {ev.notes && <div className="pcal-detail-notes"><span className="k"><Icon name="sticky-note" size={13} /> Editor notes</span><p>{ev.notes}</p></div>}
        </div>

        <div className="qad-foot" style={{ justifyContent: "space-between" }}>
          <button className="btn ghost" style={{ color: "var(--error-600)" }} onClick={() => onUnschedule(ev)}><Icon name="calendar-x" size={15} /> Unschedule</button>
          <div style={{ display: "flex", gap: 10 }}>
            <button className="btn secondary" onClick={() => onEdit(ev)}><Icon name="pencil" size={15} /> Edit</button>
            <button className="btn primary" onClick={() => onOpen(ev)}>{isRec ? "Open recipe" : "Open ingredient"} <Icon name="arrow-right" size={15} /></button>
          </div>
        </div>
      </div>
    </div>, document.body);
}

// ───────────────── ASSIGNMENTS (Manager) ─────────────────
const DEFAULT_COLS = [
  { id: "to-assign",   title: "To assign",   color: "#667085" },
  { id: "in-progress", title: "In progress", color: "#f79009" },
  { id: "in-review",   title: "In review",   color: "#2A54E5" },
  { id: "done",        title: "Published",   color: "#3b7c0f", done: true },
];
const KB_COL_PALETTE = ["#667085", "#f79009", "#2A54E5", "#3b7c0f", "#6938EF", "#0E9384", "#dc6803", "#b42318", "#0086C9"];
const KB_DEFAULT_STAGES = DEFAULT_COLS;
function kbLoadCols() {
  try { const c = JSON.parse(localStorage.getItem("nutridms_kanban_cols")); if (Array.isArray(c) && c.length) return c; } catch (e) {}
  return DEFAULT_COLS.map(c => ({ ...c }));
}
function kbSaveCols(cols) { try { localStorage.setItem("nutridms_kanban_cols", JSON.stringify(cols)); } catch (e) {} }
const KB_COLS = DEFAULT_COLS; // default/back-compat; live columns come from state

// ── Multiple boards (projects) ──
function kbSaveProjects(projects) {
  // Persist a lean copy (drop heavy recipe/ingredient objects; rehydrate on load)
  try {
    const lean = projects.map(p => ({ id: p.id, name: p.name, color: p.color,
      cols: p.cols,
      board: Object.fromEntries(Object.entries(p.board).map(([k, arr]) => [k, arr.map(c => ({ ...c, recipe: null, ingredient: null }))])) }));
    localStorage.setItem("nutridms_kanban_projects", JSON.stringify(lean));
  } catch (e) {}
}
function kbRehydrateCard(c) {
  if (!c || typeof c !== "object") return null;
  if (c.kind === "ingredient") { const i = (typeof INGREDIENT_ITEMS !== "undefined" ? INGREDIENT_ITEMS : []).find(x => x.id === c.rid); return { ...c, ingredient: i || null, recipe: null }; }
  const r = RECIPES.find(x => x.id === c.rid); return { ...c, recipe: r || null, ingredient: null };
}
function kbNormalizeSavedProject(project, index) {
  if (!project || typeof project !== "object") return null;
  const sourceCols = Array.isArray(project.cols) && project.cols.length ? project.cols : DEFAULT_COLS;
  const seen = new Set();
  const cols = sourceCols
    .filter(col => {
      const id = col && typeof col.id === "string" ? col.id.trim() : "";
      if (!id || seen.has(id)) return false;
      seen.add(id);
      return true;
    })
    .map(col => ({
      ...col,
      id: col.id.trim(),
      title: typeof col.title === "string" && col.title.trim() ? col.title : col.id.trim(),
      color: typeof col.color === "string" && col.color ? col.color : "#667085",
    }));
  if (!cols.length) return null;

  const savedBoard = project.board && typeof project.board === "object" ? project.board : {};
  const board = {};
  const knownColumns = new Set(cols.map(col => col.id));
  cols.forEach(col => {
    const cards = Array.isArray(savedBoard[col.id]) ? savedBoard[col.id] : [];
    board[col.id] = cards.map(kbRehydrateCard).filter(Boolean);
  });
  Object.entries(savedBoard).forEach(([columnId, cards]) => {
    if (knownColumns.has(columnId) || !Array.isArray(cards)) return;
    board[cols[0].id].push(...cards.map(kbRehydrateCard).filter(Boolean));
  });

  return {
    ...project,
    id: typeof project.id === "string" && project.id.trim() ? project.id : "prj-restored-" + (index + 1),
    name: typeof project.name === "string" && project.name.trim() ? project.name : "Recovered board",
    color: typeof project.color === "string" && project.color ? project.color : KB_COL_PALETTE[index % KB_COL_PALETTE.length],
    cols,
    board,
  };
}
function kbBuildProjects(assigneeFor) {
  // Normalize saved projects so stale or partially-written browser state cannot crash the board.
  try {
    const raw = JSON.parse(localStorage.getItem("nutridms_kanban_projects"));
    if (Array.isArray(raw) && raw.length) {
      const restored = raw.map(kbNormalizeSavedProject).filter(Boolean);
      if (restored.length) return restored;
    }
  } catch (e) {}
  // Otherwise seed two starter boards
  const AV = KB_AV_COLORS;
  const mk = (r, col, extra = {}) => {
    if (!r || typeof r !== "object" || !r.id) return null;
    return { key: r.id + "-" + Math.random().toString(36).slice(2, 6), kind: "recipe", rid: r.id, name: r.name, sub: `${r.cuisine} · ${r.category}`, cover: r.cover, recipe: r, ingredient: null, task: null, note: "", assignee: col === "to-assign" ? null : (r.contributor ? { name: r.contributor.name, initials: r.contributor.initials, color: AV[r.id.length % AV.length] } : null), priority: r.priority || "low", blocked: false, ...extra };
  };
  const mkI = (ing, col, extra = {}) => {
    if (!ing || typeof ing !== "object" || !ing.id) return null;
    return { key: ing.id + "-" + Math.random().toString(36).slice(2, 6), kind: "ingredient", rid: ing.id, name: ing.name, sub: `${ing.canonical} · ${ing.category}`, cover: null, recipe: null, ingredient: ing, task: null, note: "", assignee: col === "to-assign" ? null : (assigneeFor ? assigneeFor("EN") : null), priority: "medium", blocked: false, ...extra };
  };
  const ING = kbIngredients();
  const editorialCols = DEFAULT_COLS.map(c => ({ ...c }));
  const idsOf = editorialCols.map(c => c.id);
  const has = (id) => idsOf.includes(id);
  const editorialBoard = {};
  editorialCols.forEach(c => editorialBoard[c.id] = []);
  // place seeds into matching default columns if present, else intake
  const put = (col, card) => { if (!card) return; const dest = has(col) ? col : editorialCols[0].id; editorialBoard[dest] = [...editorialBoard[dest], card]; };
  put("to-assign", mk(RECIPES[9], "to-assign", { task: "Add / replace images", note: "Hero shot is low-res, needs 3 plated angles." }));
  if (ING[7]) put("to-assign", mkI(ING[7], "to-assign", { task: "Add nutrition data", note: "Per-100g macros missing; cite USDA row." }));
  put("in-progress", mk(RECIPES[2], "in-progress", { task: "Write description", note: "Expand to 2-3 sentences on flavor + benefit." }));
  put("in-progress", mk(RECIPES[7], "in-progress"));
  if (ING[2]) put("in-progress", mkI(ING[2], "in-progress", { task: "Allergen review", note: "Confirm cross-contamination handling." }));
  put("in-review", mk(RECIPES[0], "in-review", { task: "Review nutrition data", note: "Verify sodium per serving vs USDA." }));
  put("in-review", mk(RECIPES[5], "in-review"));
  put("in-review", mk(RECIPES[6], "in-review"));
  put("done", mk(RECIPES[1], "done")); put("done", mk(RECIPES[3], "done")); put("done", mk(RECIPES[4], "done")); put("done", mk(RECIPES[8], "done"));

  // Second board: a launch project with its own stages
  const launchCols = [
    { id: "ls-ideas", title: "Ideas", color: "#6938EF" },
    { id: "ls-drafting", title: "Drafting", color: "#f79009" },
    { id: "ls-shoot", title: "Photo shoot", color: "#0086C9" },
    { id: "ls-review", title: "Review", color: "#2A54E5" },
    { id: "ls-live", title: "Live", color: "#3b7c0f", done: true },
  ];
  const launchBoard = {}; launchCols.forEach(c => launchBoard[c.id] = []);
  if (RECIPES[5]) launchBoard["ls-ideas"].push(mk(RECIPES[5], "ls-ideas", { task: "Concept brief", note: "Spring seasonal, citrus-forward." }));
  if (RECIPES[6]) launchBoard["ls-drafting"].push(mk(RECIPES[6], "ls-drafting", { task: "Write recipe", note: "Target 6 servings; keep under 500 kcal." }));
  if (RECIPES[3]) launchBoard["ls-shoot"].push(mk(RECIPES[3], "ls-shoot", { task: "Add / replace images", note: "Overhead + 45° hero shots." }));

  return [
    { id: "prj-editorial", name: "Editorial Pipeline", color: "#3b7c0f", cols: editorialCols, board: editorialBoard },
    { id: "prj-launch", name: "Spring Menu Launch", color: "#6938EF", cols: launchCols, board: launchBoard },
  ];
}

// ── Load-balancing config (set by Admin in Settings) ──
const DEFAULT_LOAD_CONFIG = {
  metric: "count",            // "count" | "weighted"
  weights: { high: 3, medium: 2, low: 1 },
  includeFinal: false,        // count items in the final/published stage?
  underMax: 2,                // <= underMax  → underloaded (has capacity)
  overMin: 6,                 // >= overMin   → overloaded
  hardCap: 9,                 // assignment above this is warned/blocked
  enforce: "warn",            // "off" | "warn" | "block"
  autoSuggest: true,
};
function kbLoadConfig() {
  try { const c = JSON.parse(localStorage.getItem("nutridms_load_config")); if (c) return { ...DEFAULT_LOAD_CONFIG, ...c, weights: { ...DEFAULT_LOAD_CONFIG.weights, ...(c.weights || {}) } }; } catch (e) {}
  return { ...DEFAULT_LOAD_CONFIG };
}
function kbCardPoints(card, cfg) {
  if (cfg.metric === "weighted") return cfg.weights[card.priority] || 1;
  return 1;
}
function kbClassify(points, cfg) {
  if (points <= cfg.underMax) return points === 0 ? "idle" : "under";
  if (points >= cfg.overMin) return "high";
  return "ok";
}

const KB_AV_COLORS = ["#3b7c0f", "#2A54E5", "#6938EF", "#dc6803", "#0E9384", "#b54708", "#b42318", "#175CD3"];
function kbAssignees() {
  const list = (typeof USERS !== "undefined" ? USERS : []).filter(u => u.status === "active");
  return list.map((u, i) => ({ id: u.id, name: u.name, initials: u.initials, role: u.role, color: KB_AV_COLORS[i % KB_AV_COLORS.length] }));
}
// Who each role may assign work to
const KB_ASSIGN_MATRIX = {
  "media-contributor": ["media-contributor"],
  "dietitian":   ["media-contributor"],
  "manager":     ["dietitian", "media-contributor"],
  "compliance":  ["dietitian", "manager", "media-contributor"],
  "admin":       ["media-contributor", "dietitian", "compliance", "manager", "admin"],
  "super-admin": ["media-contributor", "dietitian", "compliance", "manager", "admin", "super-admin"],
};
const KB_ALL_ROLES = ["media-contributor", "dietitian", "compliance", "manager", "admin", "super-admin"];
const ROLE_LABEL = { "media-contributor": "Contributor", "dietitian": "Dietitian", "compliance": "Compliance", "manager": "Manager", "admin": "Admin", "super-admin": "Super Admin" };
// Task types + the default "what to look into" prompt per item type
const KB_TASKS = {
  recipe: [
    { id: "images",     label: "Add / replace images",   look: "Needs 3 plated angles, well-lit, min 1600px." },
    { id: "description",label: "Write description",       look: "2-3 sentences: flavor, health benefit, what makes it unique." },
    { id: "nutrition",  label: "Review nutrition data",   look: "Verify macros per serving against USDA; check serving size." },
    { id: "steps",      label: "Fix cooking steps",       look: "Clarify times/heat; confirm each step references an ingredient." },
    { id: "compliance", label: "Compliance check",        look: "Allergen disclosure + label rules for target region." },
    { id: "edit",       label: "General edit",            look: "" },
  ],
  ingredient: [
    { id: "nutrition",  label: "Add nutrition data",      look: "Per-100g macros + micronutrients; cite the source row." },
    { id: "source",     label: "Verify source mapping",   look: "Confirm USDA/FDC ID and default serving size." },
    { id: "allergen",   label: "Allergen review",         look: "Confirm allergen flags + cross-contamination notes." },
    { id: "compliance", label: "Compliance check",        look: "Regional thresholds (sodium, additives) and labeling." },
    { id: "edit",       label: "General edit",            look: "" },
  ],
};
function kbIngredients() { return (typeof INGREDIENT_ITEMS !== "undefined" ? INGREDIENT_ITEMS : []); }

/* ───────────────── MY ASSIGNMENTS (personal workspace) ───────────────── */
const LEGACY_ASSIGNMENT_DEMO_ROWS = [
  { id: "ma1", item: "Mediterranean Quinoa Bowl", kind: "recipe", task: "Review Nutrition Data", status: "ready", priority: "high", due: "Jun 13", overdue: false, by: "Aisha Bello",
    checklist: [{ t: "Fix Calories", done: true, loc: { sec: "Nutrition", field: "Calories", type: "number", cur: "412 kcal" } }, { t: "Fix Servings", done: true, loc: { sec: "Basics", field: "Servings", type: "number", cur: "4" } }, { t: "Fix Protein", done: false, loc: { sec: "Nutrition", field: "Protein", type: "number", cur: "32 g" } }, { t: "Fix Gallery image #3", done: false, loc: { sec: "Media", field: "Gallery image #3", type: "image", cur: "low-res 640px" } }], comments: 2, attachments: 1 },
  { id: "ma2", item: "Avocado, Raw", kind: "ingredient", task: "Allergen Review", status: "in-progress", priority: "medium", due: "Jun 14", overdue: false, by: "Dana Liu",
    checklist: [{ t: "Fix Declared allergens", done: true, loc: { sec: "Allergens", field: "Declared allergens", type: "allergens", cur: "None" } }, { t: "Fix Cross-contamination", done: false, loc: { sec: "Allergens", field: "Cross-contamination", type: "text", cur: "(empty)" } }], comments: 0, attachments: 0 },
  { id: "ma3", item: "Sourdough Pancake Stack", kind: "recipe", task: "Fix Cooking Steps", status: "in-progress", priority: "medium", due: "Jun 12", overdue: false, by: "Aisha Bello",
    checklist: [{ t: "Clarify simmer time", done: false }, { t: "Reference each ingredient", done: false }], comments: 1, attachments: 0 },
  { id: "ma4", item: "Detox Tea Blend", kind: "recipe", task: "Compliance Check", status: "not-started", priority: "high", due: "Jun 9", overdue: true, by: "Dana Liu",
    checklist: [{ t: "Check caffeine disclosure", done: false }, { t: "Verify health claims", done: false }], comments: 3, attachments: 2 },
  { id: "ma5", item: "Greek Yogurt, plain", kind: "ingredient", task: "Add Nutrition Data", status: "not-started", priority: "low", due: "Jun 18", overdue: false, by: "Aisha Bello",
    checklist: [{ t: "Enter per-100g macros", done: false }, { t: "Cite USDA source", done: false }], comments: 0, attachments: 0 },
  { id: "ma6", item: "Keto Fat Burner Smoothie", kind: "recipe", task: "Review Nutrition Data", status: "ready", priority: "high", due: "Jun 13", overdue: false, by: "Khalid Idris",
    checklist: [{ t: "Validate fat content", done: true }, { t: "Check sugar values", done: true }], comments: 1, attachments: 0 },
  { id: "ma7", item: "Black bean, cooked", kind: "ingredient", task: "Verify Source Mapping", status: "in-progress", priority: "low", due: "Jun 20", overdue: false, by: "Aisha Bello",
    checklist: [{ t: "Attach FDC ID", done: true }, { t: "Set default serving", done: false }], comments: 0, attachments: 1 },
  { id: "ma8", item: "Roasted Salmon & Greens", kind: "recipe", task: "Compliance Check", status: "completed", priority: "medium", due: "Jun 8", overdue: false, by: "Dana Liu",
    checklist: [{ t: "Allergen disclosure", done: true }, { t: "Label rules", done: true }], comments: 2, attachments: 1 },
];
const MA_STATUS = {
  "not-started": { t: "Not Started", tone: "neutral" },
  "in-progress": { t: "In Progress", tone: "warning" },
  "ready": { t: "Ready for Review", tone: "info" },
  "completed": { t: "Completed", tone: "success" },
};
const MA_NEXT = { "not-started": "in-progress", "in-progress": "ready", "ready": "completed" };

/* Field catalog, exact steps + fields you can target, with input type */
/* Full field catalogs, every step & field of a recipe / ingredient, grouped to
   match the detail-page tabs so an assignment can target ANY part of the record. */
const CA_RECIPE_FIELDS = {
  "Basics": [["Recipe name","text"],["Description","text"],["Short summary","text"],["Cuisine","text"],["Category","text"],["Region","text"],["Servings","number"],["Prep time","number"],["Cooking time","number"],["Difficulty","text"]],
  "Notes & Story": [["Cultural note","text"],["Cultural origin / story","text"],["Health note","text"],["Chef's tip","text"],["Serving suggestion","text"]],
  "Image": [["Hero photo","image"],["Hero alt text","text"]],
  "Ingredients": [["Ingredient list","text"],["Quantities","text"],["Units","text"],["Master ingredient mapping","text"],["Substitutions","text"]],
  "Cooking Steps": [["All steps","text"],["Step order","number"],["Step time","number"],["Step heat / temperature","text"],["Step technique","text"],["Step image","image"]],
  "Nutrition": [["Calories","number"],["Protein","number"],["Carbs","number"],["Fat","number"],["Fiber","number"],["Sugar","number"],["Sodium","number"],["Serving size","text"],["Macros (per serving)","text"]],
  "Allergens": [["Declared allergens","allergens"],["May contain","allergens"],["Cross-contamination","text"]],
  "Complementary Dish": [["Pairing","text"],["Complementary side","text"],["Drink pairing","text"]],
  "Dietary Rules": [["Diabetes-friendly","text"],["Heart / low-sodium","text"],["Renal (potassium)","text"],["Pregnancy-safe","text"]],
  "Compliance Risk": [["Health claims","text"],["Caffeine disclosure","number"],["Label accuracy","text"],["Regional labeling","text"]],
  "Health Tags": [["Diet tags","text"],["Health tags","text"]],
  "Media": [["Gallery image","image"],["Gallery alt text","text"],["Video","url"]],
  "References": [["Cultural reference","text"],["Source link","url"],["Document","text"],["FDA / regulatory reference","text"]],
};
const CA_INGREDIENT_FIELDS = {
  "Basics": [["Name","text"],["Canonical name","text"],["Category","text"],["State","text"],["Priority","text"]],
  "Allergens": [["Declared allergens","allergens"],["May contain","allergens"],["Cross-contamination","text"]],
  "Macronutrients": [["Calories","number"],["Protein","number"],["Fat","number"],["Carbs","number"],["Sugar","number"],["Added sugar","number"],["Fiber","number"],["Caffeine","number"],["Sodium","number"]],
  "Fatty Acids": [["Saturated fat","number"],["Monounsaturated fat","number"],["Polyunsaturated fat","number"],["Trans fat","number"],["Cholesterol","number"]],
  "Micronutrients": [["Calcium","number"],["Iron","number"],["Magnesium","number"],["Phosphorus","number"],["Potassium","number"],["Sodium","number"],["Zinc","number"],["Vitamin A","number"],["Vitamin C","number"],["Vitamin D","number"],["Folate","number"],["Vitamin B12","number"]],
  "Sources & Availability": [["Primary data source","text"],["Source link","url"],["FDC ID","text"],["Default serving","number"],["Density","text"],["Storage","text"],["Shelf life","text"]],
  "Usage": [["Linked recipes","text"]],
  "Compliance": [["USDA match","text"],["Allergen profile","text"],["Source documentation","text"],["Dietitian verification","text"]],
};

/* Current values per field (greyed reference shown when a subtask targets it) */
const CA_FIELD_CUR = {
  "Calories": "412 kcal", "Protein": "32 g", "Carbs": "38 g", "Fat": "28 g", "Sugar": "29 g",
  "Macros (per serving)": "Calories 412 · Protein 32g", "Servings": "4", "Cooking time": "25 min",
  "Recipe name": "Mediterranean Quinoa Bowl", "Description": "A vibrant grain bowl…", "Cuisine": "Mediterranean", "Category": "Lunch",
  "Hero photo": "hero.jpg (low-res)", "Gallery image": "gallery-3.jpg (out of focus)", "Step image": "(none)",
  "Step text": "“simmer until done”", "Step time": "—", "Ingredient list": "9 ingredients", "Quantities": "see list",
  "Declared allergens": "None", "Cross-contamination": "(empty)", "Health claims": "“detox”", "Caffeine disclosure": "Not disclosed",
  "Tags": "Vegetarian, High-fiber", "Source link": "(empty)",
  "Name": "Chicken Breast, Skinless", "State": "Raw", "Saturated fat": "2.8 g", "Trans fat": "0 g", "Cholesterol": "0 mg",
  "Sodium": "74 mg", "Calcium": "120 mg", "Iron": "1.8 mg", "Vitamin C": "12 mg", "Caffeine": "0 mg", "FDC ID": "(empty)", "Default serving": "(empty)",
  "Region": "Mediterranean", "Prep time": "15 min", "Difficulty": "Easy", "Fiber": "9 g", "Serving size": "1 bowl (380g)",
  "May contain": "None", "Units": "metric (g, ml)", "Master ingredient mapping": "9 of 9 mapped", "Substitutions": "(none)",
  "Step order": "5 steps", "Step heat / temperature": "“medium”", "Step technique": "(unspecified)", "Diet tags": "Vegetarian, High-fiber", "Health tags": "Heart-healthy",
  "Diabetes-friendly": "Pass", "Heart / low-sodium": "Review", "Renal (potassium)": "Pass", "Pregnancy-safe": "Pass",
  "Label accuracy": "Unverified", "Regional labeling": "FDA (USA)", "Gallery alt text": "(empty)", "Video": "(none)", "Document": "(none)", "FDA / regulatory reference": "(none)",
  "Canonical name": "Gallus gallus domesticus", "Priority": "Medium", "Added sugar": "0 g", "Monounsaturated fat": "3.1 g", "Polyunsaturated fat": "1.4 g",
  "Magnesium": "34 mg", "Phosphorus": "210 mg", "Potassium": "210 mg", "Zinc": "0.9 mg", "Vitamin A": "40 mcg", "Vitamin D": "0 mcg", "Folate": "28 mcg", "Vitamin B12": "0.4 mcg",
  "Primary data source": "USDA FoodData Central", "Density": "1.03 g/ml", "Storage": "Refrigerated", "Shelf life": "7-14 days", "Linked recipes": "3 recipes",
  "USDA match": "Matched", "Allergen profile": "Declared", "Source documentation": "(missing)", "Dietitian verification": "Pending",
};

function caTrunc(s, n) { s = String(s || ""); return s.length > n ? s.slice(0, n).trim() + "…" : s; }

// Build the assignment field catalog for a SPECIFIC item, so reviewers can target
// the EXACT cooking steps, ingredient lines, description, and cultural notes of
// that record, not just generic field names.
function caCatalogFor(kind, item) {
  const base = kind === "ingredient" ? CA_INGREDIENT_FIELDS : CA_RECIPE_FIELDS;
  const catalog = {}; Object.keys(base).forEach((k) => { catalog[k] = base[k].map((p) => [...p]); });
  const cur = { ...CA_FIELD_CUR };
  if (!item) return { catalog, cur };

  if (kind === "recipe") {
    if (item.name) cur["Recipe name"] = item.name;
    if (item.description) { cur["Description"] = `“${caTrunc(item.description, 70)}”`; cur["Short summary"] = `“${caTrunc(item.description, 50)}”`; }
    if (item.cuisine) cur["Cuisine"] = item.cuisine;
    if (item.category) cur["Category"] = item.category;
    if (item.region) cur["Region"] = item.region;
    if (item.servings != null) cur["Servings"] = String(item.servings);
    if (item.duration != null) cur["Cooking time"] = item.duration + " min";
    if (item.calories != null) cur["Calories"] = item.calories + " kcal";
    if (item.protein != null) cur["Protein"] = item.protein + " g";
    if (item.carbs != null) cur["Carbs"] = item.carbs + " g";
    if (item.fat != null) cur["Fat"] = item.fat + " g";
    if (item.fiber != null) cur["Fiber"] = item.fiber + " g";
    if (item.diet && item.diet.length) cur["Diet tags"] = item.diet.join(", ");
    if (item.allergens && item.allergens.length) cur["Declared allergens"] = item.allergens.join(", ");

    // Each EXACT cooking step becomes its own targetable field
    const steps = item.steps || [];
    if (steps.length) {
      const stepFields = steps.map((s, i) => { const label = `Step ${i + 1}`; cur[label] = `“${caTrunc(s, 55)}”`; return [label, "text"]; });
      catalog["Cooking Steps"] = [...catalog["Cooking Steps"], ...stepFields];
    }
    // Each EXACT ingredient line becomes its own targetable field
    const ings = item.ingredients || [];
    if (ings.length) {
      const ingFields = ings.map((g, i) => { const label = `Ingredient ${i + 1}`; cur[label] = `“${caTrunc(g, 55)}”`; return [label, "text"]; });
      catalog["Ingredients"] = [...catalog["Ingredients"], ...ingFields];
    }
  } else {
    if (item.name) cur["Name"] = item.name;
    if (item.canonical) cur["Canonical name"] = item.canonical;
    if (item.category) cur["Category"] = item.category;
    if (item.nutr) { cur["Calories"] = item.nutr.calories + " kcal"; cur["Protein"] = item.nutr.p + " g"; cur["Carbs"] = item.nutr.c + " g"; cur["Fat"] = item.nutr.f + " g"; }
    if (item.allergens && item.allergens.length) cur["Declared allergens"] = item.allergens.join(", ");
  }
  return { catalog, cur };
}
/* Shared task field picker, Request-Guidance-style drill-down (area → field pills),
   single-select per task row, showing each field's current value. Used by Create
   Assignment, Quick Assign and the Quick Assign drawer. */
(function injectTFDStyles() {
  if (document.getElementById("tfd-styles")) return;
  const css = `
  .tfd { display: flex; flex-direction: column; gap: 7px; }
  .tfd-trigger { display: flex; align-items: center; gap: 8px; width: 100%; padding: 9px 12px; border: 1px solid var(--gray-200); border-radius: 9px; background: #fff; cursor: pointer; font-family: inherit; }
  .tfd-trigger:hover { border-color: #C7BCF8; }
  .tfd .cu-sub-cur { display: block; max-width: 100%; box-sizing: border-box; white-space: normal; overflow-wrap: anywhere; word-break: break-word; line-height: 1.4; }
  .tfd-path { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--gray-700); min-width: 0; }
  .tfd-path strong { color: #5925DC; font-weight: 700; }
  .tfd-path svg { color: var(--gray-400); flex-shrink: 0; }
  .tfd-panel { border: 1px solid #E0DBFB; border-radius: 11px; overflow: hidden; background: #FCFBFF; }
  .tfd-area { border-bottom: 1px solid #EEEAFB; }
  .tfd-area:last-child { border-bottom: 0; }
  .tfd-area-head { display: flex; align-items: center; width: 100%; padding: 9px 12px; border: 0; background: transparent; cursor: pointer; font-family: inherit; font-size: 12.5px; font-weight: 700; color: #4318a8; }
  .tfd-area.on .tfd-area-head { background: #F4F2FF; }
  .tfd-area-head svg { color: #8b7fd6; }
  .tfd-fields { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 9px; padding: 11px 12px 13px; align-items: stretch; }
  .tfd-fieldpill { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; width: 100%; box-sizing: border-box; min-width: 0; padding: 10px 12px; border: 1.5px solid var(--gray-200); border-radius: 12px; background: #fff; cursor: pointer; font-family: inherit; font-size: 12.5px; font-weight: 700; color: var(--gray-700); text-align: left; box-shadow: 0 1px 2px rgba(16,24,40,.04); transition: transform .16s cubic-bezier(.34,1.56,.64,1), box-shadow .16s ease, border-color .12s ease, background .12s ease; }
  .tfd-fieldpill:hover { transform: translateY(-3px) scale(1.02); box-shadow: 0 14px 26px -10px rgba(89,37,220,.45); border-color: #A78BFA; z-index: 1; }
  .tfd-fieldpill.on { border-color: #6938EF; background: #F4F2FF; color: #5925DC; box-shadow: 0 8px 18px -8px rgba(105,56,239,.4); }
  .tfd-fieldpill > span:first-child { display: inline-flex; align-items: center; gap: 5px; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tfd-fieldpill .tfd-fieldcur { font-size: 10.5px; font-weight: 600; color: #b45309; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tfd-fieldpill.on .tfd-fieldcur { color: #7c3f04; }
  .app[data-theme="dark"] .tfd-trigger, .app[data-theme="dark"] .tfd-panel, .app[data-theme="dark"] .tfd-fieldpill { background: #11161B; }
  `;
  const s = document.createElement("style"); s.id = "tfd-styles"; s.textContent = css; document.head.appendChild(s);
})();
function TaskFieldDrill({ catalog, cur, sec, field, onPick }) {
  const [open, setOpen] = React.useState(false);
  const [expanded, setExpanded] = React.useState(sec);
  const sections = Object.keys(catalog || {});
  const curVal = (cur || {})[field];
  return (
    <div className="tfd">
      <button type="button" className="tfd-trigger" onClick={() => { setExpanded(sec); setOpen((o) => !o); }}>
        <span className="tfd-path"><strong>{sec}</strong><Icon name="chevron-right" size={12} />{field}</span>
        <Icon name={open ? "chevron-up" : "chevron-down"} size={14} style={{ marginLeft: "auto", color: "var(--gray-400)" }} />
      </button>
      <span className="cu-sub-cur" title="Current value (read-only)">Current: {curVal || "—"}</span>
      {open && (
        <div className="tfd-panel">
          {sections.map((s) => {
            const on = expanded === s;
            return (
              <div key={s} className={`tfd-area ${on ? "on" : ""}`}>
                <button type="button" className="tfd-area-head" onClick={() => setExpanded(on ? null : s)}>
                  <span className="tfd-area-label">{s}</span>
                  <Icon name={on ? "chevron-down" : "chevron-right"} size={14} style={{ marginLeft: "auto" }} />
                </button>
                {on && (
                  <div className="tfd-fields">
                    {(catalog[s] || []).map(([f, t]) => (
                      <button key={f} type="button" className={`tfd-fieldpill ${field === f && sec === s ? "on" : ""}`} onClick={() => { onPick(s, f, t); setOpen(false); }}>
                        <span style={{ display: "inline-flex", alignItems: "center", gap: 5 }}>{field === f && sec === s && <Icon name="check" size={10} stroke={3} />}{f}</span>
                        <span className="tfd-fieldcur">Current: {(cur || {})[f] || "—"}</span>
                      </button>
                    ))}
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

function LegacyCreateAssignmentPage() {
  const { role, toast, setPage } = useApp();
  const ASSIGNEES = React.useMemo(kbAssignees, []);
  const allowedRoles = KB_ASSIGN_MATRIX[role] || KB_ALL_ROLES;
  const ME_BY_ROLE = { "media-contributor": "SC", "dietitian": "EN", "compliance": "DL", "manager": "AB", "admin": "AB", "super-admin": "AB" };
  const meUser = ASSIGNEES.find(a => a.initials === ME_BY_ROLE[role]) || ASSIGNEES[0];
  const allowedBase = ASSIGNEES.filter(a => allowedRoles.includes(a.role));
  const allowed = meUser && !allowedBase.some(a => a.id === meUser.id) ? [meUser, ...allowedBase] : allowedBase;
  // Workload per assignee (from any saved board) + lightest-load suggestion
  const wl = () => 0;
  const suggestion = allowed.length ? allowed.reduce((m, a) => wl(a) < wl(m) ? a : m, allowed[0]) : null;
  const ROLE_LBL = { "media-contributor": "Contributor", "dietitian": "Dietitian", "compliance": "Compliance", "manager": "Manager", "admin": "Admin", "super-admin": "Super Admin" };
  const [activityOpen, setActivityOpen] = React.useState(true);
  const [loraaOn, setLoraaOn] = React.useState(false);
  const [loraaExpanded, setLoraaExpanded] = React.useState(false);
  const [pickerOpen, setPickerOpen] = React.useState(false);
  const [pickerQ, setPickerQ] = React.useState("");
  const [chat, setChat] = React.useState([
    { who: "Eve Nakamura", init: "EN", color: "#2A6FDB", text: "Hey @You, please target the nutrition fields that failed QA.", at: "5m ago" },
    { who: meUser?.name || "You", init: meUser?.initials || "ME", color: meUser?.color || "#3b7c0f", text: "On it, adding the exact fields that need work now.", at: "now" },
  ]);
  const [draft, setDraft] = React.useState("");
  const [replyTo, setReplyTo] = React.useState(null);
  const [mOpen, setMOpen] = React.useState(false);
  const chatMention = (/@(\w*)$/.exec(draft) || [])[1] || "";
  const chatMatches = (typeof MA_MENTION !== "undefined" ? MA_MENTION : []).filter(n => n.toLowerCase().includes(chatMention.toLowerCase()));
  const sendChat = () => { if (!draft.trim()) return; setChat(c => [...c, { who: meUser?.name || "You", init: meUser?.initials || "ME", color: meUser?.color || "#3b7c0f", text: draft.trim(), at: "now", replyTo: replyTo != null ? { who: chat[replyTo].who, text: chat[replyTo].text } : null }]); setDraft(""); setReplyTo(null); };
  const renderChat = (s) => s.split(/(@[\w]+(?:\s[\w]+)?)/g).map((p, i) => /^@/.test(p) && (typeof MA_MENTION !== "undefined" ? MA_MENTION : []).some(n => p.includes(n.split(" ")[0])) ? <span key={i} className="ma-mention">{p}</span> : p);

  const [kind, setKind] = React.useState("recipe");
  const projects = React.useMemo(() => kbBuildProjects((init) => ASSIGNEES.find(a => a.initials === init) || ASSIGNEES[0]), []);
  const [boardId, setBoardId] = React.useState(projects[0]?.id);
  const board = projects.find(p => p.id === boardId) || projects[0];
  const boardCols = board ? board.cols : DEFAULT_COLS;
  const [stageId, setStageId] = React.useState(boardCols[0]?.id);
  React.useEffect(() => { setStageId(boardCols[0]?.id); }, [boardId]);
  const stageObj = boardCols.find(c => c.id === stageId) || boardCols[0];
  const recipes = RECIPES, ings = kbIngredients();
  const [recipeId, setRecipeId] = React.useState(recipes[0]?.id);
  const [ingredientId, setIngredientId] = React.useState(ings[0]?.id);
  const [assigneeId, setAssigneeId] = React.useState(meUser?.id);
  const [priority, setPriority] = React.useState("medium");
  const [due, setDue] = React.useState("");
  const [notes, setNotes] = React.useState("");
  const [rows, setRows] = React.useState([]); // {sec, field, type, note}

  // Prefill from a board card click (open existing assignment)
  React.useEffect(() => {
    let pf = null; try { pf = window.__caPrefill; window.__caPrefill = null; } catch (e) {}
    if (!pf) return;
    if (pf.kind) setKind(pf.kind);
    if (pf.boardId) setBoardId(pf.boardId);
    if (pf.stage) setStageId(pf.stage);
    if (pf.priority) setPriority(pf.priority);
    if (pf.note) setNotes(pf.note);
    if (pf.itemName) {
      const r = RECIPES.find(x => x.name === pf.itemName); const ing = kbIngredients().find(x => x.name === pf.itemName);
      if (pf.kind === "ingredient" && ing) setIngredientId(ing.id);
      else if (r) setRecipeId(r.id);
    }
    if (pf.assigneeInitials) { const a = ASSIGNEES.find(x => x.initials === pf.assigneeInitials); if (a) setAssigneeId(a.id); }
  }, []);

  const item = kind === "recipe" ? recipes.find(r => r.id === recipeId) : ings.find(i => i.id === ingredientId);
  const itemName = item ? item.name : "";
  const { catalog, cur } = React.useMemo(() => caCatalogFor(kind, item), [kind, item]);
  const sections = Object.keys(catalog);

  const addRow = () => { const sec = sections[0]; const [field, type] = catalog[sec][0]; setRows(r => [...r, { id: Date.now() + "" + r.length, sec, field, type, note: "" }]); };
  const [loraaScanning, setLoraaScanning] = React.useState(false);
  const loraaAnalyze = () => {
    setLoraaScanning(true);
    setTimeout(() => {
      // Loraa's flag heuristics, pick real fields from the catalog for this kind
      const wanted = kind === "recipe"
        ? [["Nutrition", "Calories", "Calorie total looks high vs. listed ingredients, re-verify per serving."],
           ["Allergens", "Declared allergens", "Possible undeclared allergen detected from ingredients, confirm."],
           ["Cooking Steps", "Step clarity", "Step 2 is ambiguous, clarify simmer time and heat."]]
        : [["Macronutrients", "Calories", "Per-100g calories differ from USDA reference, re-check."],
           ["Allergens", "Declared allergens", "Allergen profile may be incomplete, verify against source."],
           ["Sources", "Source link", "Source link missing or unverified, attach an authoritative citation."]];
      const flagged = wanted.map(([sec, wantField, note], i) => {
        const secKey = catalog[sec] ? sec : sections[0];
        const opt = (catalog[secKey] || []).find(([f]) => f === wantField) || catalog[secKey][0];
        return { id: "loraa" + Date.now() + i, sec: secKey, field: opt[0], type: opt[1], note, loraa: true };
      });
      setRows(flagged);
      setLoraaScanning(false);
      setLoraaExpanded(false);
      toast(`Loraa flagged ${flagged.length} fields, review the assignment panel`);
    }, 900);
  };
  const patchRow = (i, p) => setRows(rs => rs.map((r, j) => { if (j !== i) return r; const next = { ...r, ...p }; if (p.sec) { const [field, type] = catalog[p.sec][0]; next.field = field; next.type = type; } if (p.field) { const ft = catalog[next.sec].find(([f]) => f === p.field); if (ft) next.type = ft[1]; } return next; }));
  const removeRow = (i) => setRows(rs => rs.filter((_, j) => j !== i));

  const create = () => {
    const a = allowed.find(x => x.id === assigneeId);
    const id = "ca-" + Date.now();
    const checklist = (rows.length ? rows : [{ sec: sections[0], field: catalog[sections[0]][0][0], type: catalog[sections[0]][0][1], note: "" }]).map(r => ({
      t: `Fix ${r.field}`, done: false,
      loc: { sec: r.sec, field: r.field, type: r.type, cur: cur[r.field] || "(see item)", hint: r.note || `Correct the ${r.field.toLowerCase()} in ${r.sec}.` },
    }));
    const stageIdx = boardCols.findIndex(c => c.id === stageId);
    const maStatus = stageObj && stageObj.done ? "completed" : stageIdx <= 0 ? "not-started" : stageIdx >= boardCols.length - 2 ? "ready" : "in-progress";
    const card = { id, item: itemName, kind, task: rows.length > 1 ? `${rows.length} fixes` : (rows[0] ? `Fix ${rows[0].field}` : "Review"), status: maStatus, board: board?.name, stage: stageObj?.title, priority, due: due ? "Jun " + new Date(due).getDate() : "Jun 20", overdue: false, by: meUser?.name || "You", checklist, comments: 0, attachments: 0, assigneeInitials: a?.initials };
    toast(`Assignment created · ${checklist.length} checklist item${checklist.length === 1 ? "" : "s"}`);
    setPage("my-assignments");
  };

  return (
    <div className="cu">
      <div className="cu-topbar">
        <div className="cu-crumb"><Icon name="layout-grid" size={15} /> Team Board <Icon name="chevron-right" size={13} /> <strong>Create Assignment</strong></div>
        <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
          <button className="btn ghost sm" onClick={() => setPage("assignments")}><Icon name="x" size={15} /> Cancel</button>
          <button className="btn primary sm" disabled={!item || !assigneeId} onClick={create}><Icon name="send" size={14} /> Create Assignment</button>
          <button className="btn ghost sm" title={activityOpen ? "Collapse assignment panel" : "Show assignment panel"} onClick={() => setActivityOpen(o => !o)}><Icon name={activityOpen ? "panel-right-close" : "panel-right-open"} size={16} /></button>
        </div>
      </div>

      <div className={`cu-body ${activityOpen ? "" : "activity-collapsed"}`}>
        {/* MAIN task column */}
        <div className="cu-main">
          <div className="cu-toprow">
            <div className="cu-tasktype">
              <button className={`cu-seg ${kind === "recipe" ? "on" : ""}`} onClick={() => { setKind("recipe"); setRows([]); }}><Icon name="utensils-crossed" size={14} /> Recipe</button>
              <button className={`cu-seg ${kind === "ingredient" ? "on" : ""}`} onClick={() => { setKind("ingredient"); setRows([]); }}><Icon name="leaf" size={14} /> Ingredient</button>
            </div>
            <button type="button" className="cu-loraa-pill cu-loraa-animated" onClick={() => setLoraaExpanded(true)}>
              <span className="cu-loraa-logo sm"><img src="assets/loraa-logo.png" alt="Loraa" /></span>
              <span>Loraa{loraaOn ? " · on" : ""}</span>
              <Icon name="chevron-down" size={14} />
            </button>
            {loraaExpanded && (
              <div className="cu-loraa-overlay" onMouseDown={(e) => { if (e.target === e.currentTarget) setLoraaExpanded(false); }}>
                <div className="cu-loraa cu-loraa-pop cu-loraa-animated" role="dialog" aria-label="Loraa assistant">
                  <div className="cu-loraa-pop-head">
                    <span className="cu-loraa-logo"><img src="assets/loraa-logo.png" alt="Loraa" /></span>
                    <strong>Loraa is here to assist</strong>
                    <label className="cu-loraa-toggle" title="Loraa assist on/off"><span>{loraaOn ? "On" : "Off"}</span><button type="button" className={`la-toggle sm ${loraaOn ? "on" : ""}`} onClick={() => { setLoraaOn(o => !o); if (!loraaOn) toast("Loraa assist on"); }}><i/></button></label>
                    <button type="button" className="cu-loraa-collapse" title="Collapse" onClick={() => setLoraaExpanded(false)}><Icon name="x" size={16} /></button>
                  </div>
                  <p className="cu-loraa-pop-body">{loraaOn ? <>Loraa can analyze <strong>{itemName}</strong> and flag fields that need work, they'll appear as tasks in the assignment panel.</> : "Loraa is off. Turn her on and she'll flag fields that need work as a supportive, optional check."}</p>
                  {loraaOn && <button type="button" className="cu-loraa-analyze" disabled={loraaScanning} onClick={loraaAnalyze}>{loraaScanning ? <><Icon name="loader" size={14} className="spin" /> Analyzing…</> : <><Icon name="scan-search" size={14} /> Analyze &amp; flag fields</>}</button>}
                  <div className="cu-loraa-pop-foot"><Icon name="info" size={13} stroke={2.4} /><span>Loraa supports QA and compliance checks, but final review is required before publishing.</span></div>
                </div>
              </div>
            )}
          </div>

          {/* Meta card, search + board/status/assignee/priority/due */}
          <div className="cu-meta">
            <div className="cu-title-row cu-meta-search">
              <Icon name="target" size={20} style={{ color: "var(--green-700)", flexShrink: 0 }} />
              <div style={{ flex: 1, minWidth: 0 }}>
                <KbCombo items={kind === "recipe" ? recipes : ings} value={kind === "recipe" ? recipeId : ingredientId}
                  onSelect={(id) => kind === "recipe" ? setRecipeId(id) : setIngredientId(id)}
                  getLabel={(o) => o.name} getSub={(o) => kind === "recipe" ? `${o.cuisine} · ${o.category}` : (o.canonical || o.category)}
                  placeholder={`Search ${kind}s…`} />
              </div>
            </div>
            <div className="cu-meta-row">
              <span className="cu-meta-k"><Icon name="layout-grid" size={15} /> Board</span>
              <select className="cu-inline-select" value={boardId} onChange={e => setBoardId(e.target.value)}>{projects.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}</select>
            </div>
            <div className="cu-meta-row">
              <span className="cu-meta-k"><Icon name="circle-dot" size={15} /> Status</span>
              <div className="cu-stage-pick">
                <select className="cu-stage-select" value={stageId} onChange={e => setStageId(e.target.value)} style={{ color: stageObj?.color }}>
                  {boardCols.map(c => <option key={c.id} value={c.id}>{c.title}</option>)}
                </select>
                <span className="cu-stage-dot" style={{ background: stageObj?.color }} />
              </div>
            </div>
            <div className="cu-meta-row" style={{ alignItems: "flex-start" }}>
              <span className="cu-meta-k" style={{ marginTop: 4 }}><Icon name="user" size={15} /> Assignee</span>
              <div style={{ flex: 1, position: "relative" }}>
                <div className="cu-assign-head">
                  {(() => { const sel = allowed.find(a => a.id === assigneeId); return (
                    <button className="cu-assign-trigger" onClick={() => { setPickerOpen(o => !o); setPickerQ(""); }}>
                      {sel ? <><span className="kb-av" style={{ background: sel.color, width: 24, height: 24, fontSize: 9 }}>{sel.initials}</span><span className="cu-assign-name">{sel.name}{sel.id === meUser?.id ? " (You)" : ""}</span></> : <span className="muted">Assign to…</span>}
                      <Icon name="chevrons-up-down" size={14} style={{ color: "var(--gray-400)", marginLeft: "auto" }} />
                    </button>
                  ); })()}
                  {meUser && <button className={`cu-self-pill ${assigneeId === meUser.id ? "on" : ""}`} onClick={() => setAssigneeId(meUser.id)}><Icon name="user-check" size={13} /> Assign to myself</button>}
                </div>
                <div className="cu-assign-meta"><span>{ROLE_LBL[role] || "You"} can assign to {(allowedRoles || []).map(r => ROLE_LBL[r] || r).join(", ")}</span></div>
                {pickerOpen && (
                  <>
                    <div style={{ position: "fixed", inset: 0, zIndex: 20 }} onClick={() => setPickerOpen(false)} />
                    <div className="cu-picker">
                      <div className="cu-picker-search"><Icon name="search" size={15} /><input autoFocus placeholder="Search team members…" value={pickerQ} onChange={e => setPickerQ(e.target.value)} /></div>
                      {[...allowed].filter(a => !pickerQ || a.name.toLowerCase().includes(pickerQ.toLowerCase()) || (ROLE_LBL[a.role] || "").toLowerCase().includes(pickerQ.toLowerCase())).sort((a, b) => wl(a) - wl(b)).map(a => {
                        const n = wl(a); const lvl = n === 0 ? "free" : n >= 4 ? "high" : "ok";
                        return (
                          <button key={a.id} className={`cu-picker-row ${assigneeId === a.id ? "on" : ""}`} onClick={() => { setAssigneeId(a.id); setPickerOpen(false); }}>
                            <span className="kb-av" style={{ background: a.color, width: 30, height: 30 }}>{a.initials}</span>
                            <span className="cu-picker-id"><span className="cu-picker-name">{a.name} {a.id === suggestion?.id && <span className="cu-badge sug">Suggested</span>} {a.id === meUser?.id && <span className="cu-badge you">You</span>}</span><span className="cu-picker-role">{ROLE_LBL[a.role] || a.role}</span></span>
                            <span className="cu-picker-load">
                              <span className={`cu-load-tag ${lvl}`}>{n === 0 ? "free" : n + " active"}</span>
                              <span className="cu-load-bar"><i className={lvl} style={{ width: `${Math.min(100, (n / 5) * 100)}%` }} /></span>
                            </span>
                            {assigneeId === a.id && <Icon name="check" size={15} style={{ color: "var(--green-700)", flexShrink: 0 }} />}
                          </button>
                        );
                      })}
                    </div>
                  </>
                )}
                {suggestion && (
                  <button className="cu-suggest-chip" onClick={() => setAssigneeId(suggestion.id)}>
                    <Icon name="wand-2" size={13} /> <span><strong>{suggestion.name}</strong> has the lightest load ({wl(suggestion)} active), balance the team</span>
                    {assigneeId === suggestion.id ? <span className="cu-suggest-on">Selected</span> : <span className="cu-suggest-use">Use</span>}
                  </button>
                )}
              </div>
            </div>
            <div className="cu-meta-2col">
              <div className="cu-meta-row cu-meta-half">
                <span className="cu-meta-k"><Icon name="flag" size={15} /> Priority</span>
                <select className="cu-inline-select" value={priority} onChange={e => setPriority(e.target.value)}><option value="high">High</option><option value="medium">Medium</option><option value="low">Low</option></select>
              </div>
              <div className="cu-meta-row cu-meta-half">
                <span className="cu-meta-k"><Icon name="calendar" size={15} /> Due date</span>
                <input type="date" className="cu-inline-date" value={due} onChange={e => setDue(e.target.value)} />
              </div>
            </div>
          </div>

          <div className="cu-section">
            <div className="cu-section-h">Description</div>
            <textarea className="cu-desc" rows="2" value={notes} onChange={e => setNotes(e.target.value)} placeholder="What needs doing on this item…" />
          </div>

          {/* Comments chat (swapped into main) */}
          <div className="cu-section">
            <div className="cu-section-h">Comments</div>
            <div className="cu-chat cu-chat-bubbles">
              {chat.map((m, i) => {
                const mine = m.who === (meUser?.name || "You") || m.init === (meUser?.initials || "ME");
                return (
                <div key={i} className={`cu-cbubble ${mine ? "me" : "them"}`}>
                  <span className="cu-cbubble-av" style={{ background: mine ? (meUser?.color || "#3b7c0f") : (m.color || "#2A6FDB") }}>{m.init}</span>
                  <div className="cu-cbubble-col">
                    <div className="cu-cbubble-top"><strong>{mine ? "You" : m.who}</strong> <span className="muted">{m.at}</span></div>
                    {m.replyTo && <div className="cu-cbubble-quote"><span className="cu-cbubble-quote-who">{m.replyTo.who}</span>{m.replyTo.text.length > 50 ? m.replyTo.text.slice(0,50) + "…" : m.replyTo.text}</div>}
                    <div className="cu-cbubble-card">{renderChat(m.text)}</div>
                    <button className="cu-cbubble-reply" onClick={() => { setReplyTo(i); const ta = document.querySelector('.cu-chat-composer textarea'); ta && ta.focus(); }}><Icon name="reply" size={12} /> Reply</button>
                  </div>
                </div>
              );})}
            </div>
            <div className="cu-chat-composer">
              <span className="cu-chat-av sm" style={{ background: meUser?.color || "#3b7c0f" }}>{meUser?.initials || "ME"}</span>
              <div style={{ flex: 1, position: "relative" }}>
                {replyTo != null && chat[replyTo] && (
                  <div className="cu-reply-chip"><Icon name="reply" size={12} /> Replying to <strong>{chat[replyTo].who === (meUser?.name||"You") ? "yourself" : chat[replyTo].who}</strong><button onClick={() => setReplyTo(null)}><Icon name="x" size={12} /></button></div>
                )}
                <textarea className="textarea" rows="2" value={draft}
                  onChange={e => { setDraft(e.target.value); setMOpen(/@(\w*)$/.test(e.target.value.slice(0, e.target.selectionStart))); }}
                  onKeyDown={e => { if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); sendChat(); } }}
                  placeholder="Write a comment… use @ to mention" />
                {mOpen && chatMatches.length > 0 && (
                  <div className="ma-mention-pop">
                    {chatMatches.map(n => <button key={n} onClick={() => { setDraft(d => d.replace(/@(\w*)$/, "@" + n + " ")); setMOpen(false); }}><span className="cu-chat-av sm" style={{ background: "#6938EF" }}>{n.split(" ").map(w => w[0]).join("")}</span> {n}</button>)}
                  </div>
                )}
                <div className="cu-chat-bar"><Icon name="at-sign" size={15} /><Icon name="paperclip" size={15} /><button className="cu-send" disabled={!draft.trim()} onClick={sendChat}><Icon name="send" size={14} /></button></div>
              </div>
            </div>
          </div>
        </div>

        {/* TASKS sidebar (swapped) */}
        {activityOpen ? (
        <div className="cu-activity">
          <div className="cu-activity-h cu-activity-h-center"><strong>Assignment</strong><div style={{ display: "flex", alignItems: "center", gap: 10 }}><span className="cu-open">{rows.length} open</span><button className="cu-add-sub bold" onClick={addRow}><Icon name="plus" size={16} stroke={3} /></button></div></div>
          <div className="cu-tasks">
            {rows.length === 0 && <div className="cu-sub-empty">No tasks yet, click <strong>+</strong> to target the exact steps &amp; fields that need fixing.</div>}
            {rows.map((r, i) => (
              <div key={r.id} className="cu-task-card">
                <div className="cu-task-top">
                  <span className="cu-sub-ic"><Icon name="circle" size={14} /></span>
                  <span className={`ca-type-badge t-${r.type}`}>{r.type}</span>
                  {r.loraa && <span className="cu-loraa-flag"><img src="assets/loraa-logo.png" alt=""/> Flagged</span>}
                  <button className="cu-sub-del" onClick={() => removeRow(i)} title="Remove" style={{ marginLeft: "auto" }}><Icon name="trash-2" size={13} /></button>
                </div>
                <TaskFieldDrill catalog={catalog} cur={cur} sec={r.sec} field={r.field}
                  onPick={(s, f, t) => setRows(rs => rs.map((row, j) => j === i ? { ...row, sec: s, field: f, type: t } : row))} />
                <input className="cu-sub-note" value={r.note} onChange={e => patchRow(i, { note: e.target.value })} placeholder="What to look into…" />
              </div>
            ))}
            <button className="cu-add-task" onClick={addRow}><Icon name="plus" size={15} /> Add task</button>
          </div>
        </div>
        ) : (
          <button className="cu-activity-reopen" onClick={() => setActivityOpen(true)} title="Show assignment"><span>Assignment</span></button>
        )}
      </div>
    </div>
  );
}

function legacyAssignmentDemoRows() { return []; }
function LegacyMyAssignments() {
  const { role, openRecipe, openIngredient, toast, setDetailFocus, setPage } = useApp();
  const [items, setItems] = React.useState(() => [...legacyAssignmentDemoRows(), ...LEGACY_ASSIGNMENT_DEMO_ROWS]);
  const [tab, setTab] = React.useState("all");
  const [q, setQ] = React.useState("");
  const [openId, setOpenId] = React.useState(null);
  const [view, setView] = React.useState("cards");
  const [fix, setFix] = React.useState(null);          // { itemId, ci }
  const [comments, setComments] = React.useState(null); // itemId
  const [detailTab, setDetailTab] = React.useState(null); // "comment" | "attach" | "activity"
  const [threads, setThreads] = React.useState({
    ma1: [{ who: "Aisha Bello", init: "AB", text: "Please double-check the sodium against the latest USDA row.", at: "2h ago" }],
  });

  const setStatus = (id, status) => setItems(xs => xs.map(x => x.id === id ? { ...x, status } : x));
  const toggleCheck = (id, ci) => setItems(xs => xs.map(x => x.id === id ? { ...x, checklist: x.checklist.map((c, j) => j === ci ? { ...c, done: !c.done } : c) } : x));
  const resolveCheck = (id, ci, value) => setItems(xs => xs.map(x => x.id === id ? { ...x, status: x.status === "not-started" ? "in-progress" : x.status, checklist: x.checklist.map((c, j) => j === ci ? { ...c, done: true, fixedValue: value } : c) } : x));
  const advance = (it) => { const nx = MA_NEXT[it.status]; if (nx) { setStatus(it.id, nx); toast(`Moved to "${MA_STATUS[nx].t}"`); } };
  const complete = (it) => { if (it.checklist.some(c => !c.done)) { toast("Resolve all checklist items first"); return; } setStatus(it.id, "completed"); toast("Task marked complete"); };
  const addComment = (id, text, replyTo) => setThreads(t => ({ ...t, [id]: [...(t[id] || []), { who: "You", init: "ME", text, at: "just now", replyTo: replyTo || null, reactions: {} }] }));
  const reactComment = (id, idx, emoji) => setThreads(t => ({ ...t, [id]: (t[id]||[]).map((m,i)=> i!==idx ? m : { ...m, reactions: { ...(m.reactions||{}), [emoji]: ((m.reactions||{})[emoji]||0)+1 } }) }));

  const inTab = (it) => {
    if (tab === "all") return true;
    if (tab === "completed") return it.status === "completed";
    if (tab === "overdue") return it.overdue && it.status !== "completed";
    if (tab === "today") return it.due === "Jun 13" && it.status !== "completed";
    if (tab === "upcoming") return !it.overdue && it.due !== "Jun 13" && it.status !== "completed";
    return true;
  };
  const shown = items.filter(it => inTab(it) && (!q || (it.item + " " + it.task).toLowerCase().includes(q.toLowerCase())));
  const count = (fn) => items.filter(fn).length;
  const TABS = [
    ["all", "All", items.length],
    ["today", "Due Today", count(it => it.due === "Jun 13" && it.status !== "completed")],
    ["upcoming", "Upcoming", count(it => !it.overdue && it.due !== "Jun 13" && it.status !== "completed")],
    ["overdue", "Overdue", count(it => it.overdue && it.status !== "completed")],
    ["completed", "Completed", count(it => it.status === "completed")],
  ];
  const openItem = (it) => { setView("cards"); setOpenId(it.id); };
  const viewSource = (it, loc) => { if (loc && setDetailFocus) setDetailFocus({ kind: it.kind, sec: loc.sec, field: loc.field, label: loc.field || "field", cur: loc.cur }); const r = RECIPES.find(x => x.name === it.item); if (it.kind === "recipe" && r) openRecipe(r); else { const ing = kbIngredients().find(x => x.name === it.item); if (ing && openIngredient) openIngredient(ing); else toast("Opening " + it.item); } };
  const activeCount = items.filter(it => it.status !== "completed").length;

  return (
    <div>
      <Crumbs path={[{ label: "Assignments" }, { label: "My Assignments" }]} />
      <div className="page-head">
        <div>
          <h1 className="page-title">My Assignments</h1>
          <p className="page-sub">Your personal workspace, tasks assigned to you. <strong>{activeCount}</strong> active.</p>
        </div>
      </div>

      <div className="ma-toolbar">
        <div className="ma-count">Assigned to Me <span>{items.length}</span></div>
        <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
          <div className="kb-viewseg">
            {[["cards","layout-list","Cards"],["compact","rows-3","Compact"],["table","table","Table"],["calendar","calendar-days","Calendar"],["queue","inbox","Queue"]].map(([id,ic,label])=>(
              <button key={id} className={view===id?"on":""} onClick={()=>setView(id)} title={label}><Icon name={ic} size={15}/> <span className="kb-viewseg-l">{label}</span></button>
            ))}
          </div>
          <div className="ma-search">
            <Icon name="search" size={15} stroke={2.2}/>
            <input placeholder="Search assignments…" value={q} onChange={e=>setQ(e.target.value)}/>
          </div>
        </div>
      </div>

      <div className="ma-tabs">
        {TABS.map(([id, label, n]) => (
          <button key={id} className={`ma-tab ${tab===id?"on":""}`} onClick={()=>setTab(id)}>
            {label}{n>0 && <span className="ma-tab-c">{n}</span>}
          </button>
        ))}
      </div>

      {view==="cards" && <div className="ma-list">
        {shown.length === 0 && <div className="empty"><div className="icon"><Icon name="check-check" size={24}/></div><h3>Nothing here</h3><p>No assignments in this view.</p></div>}
        {shown.map(it => {
          const done = it.checklist.filter(c=>c.done).length;
          const expanded = openId === it.id;
          return (
            <div key={it.id} className={`ma-card ${it.overdue && it.status!=="completed" ? "overdue" : ""}`}>
              <div className="ma-card-main">
                <div className="ma-thumb">{it.kind === "ingredient" ? <Icon name="leaf" size={18} stroke={1.8}/> : <Icon name="utensils-crossed" size={18} stroke={1.8}/>}</div>
                <div className="ma-card-body">
                  <div className="ma-item">{it.item}</div>
                  <div className="ma-task"><Icon name="clipboard-list" size={13} stroke={2.2}/> {it.task}</div>
                  <div className="ma-meta">
                    <span className={`pill ${MA_STATUS[it.status].tone}`} style={{justifyContent:"center"}}>{MA_STATUS[it.status].t}</span>
                    <PriorityPill priority={it.priority}/>
                    <span className={`ma-due ${it.overdue && it.status!=="completed" ?"is-overdue":""}`}><Icon name="calendar" size={12} stroke={2.2}/> Due {it.due}</span>
                    <span className="ma-by">Assigned by {it.by}</span>
                  </div>
                </div>
                <div className="ma-card-actions">
                  <button className="btn secondary sm" onClick={()=>setOpenId(it.id)}>Open</button>
                </div>
              </div>

              {expanded && ReactDOM.createPortal((
                <div className="ma-modal-bg" onMouseDown={(e)=>{ if(e.target===e.currentTarget) setOpenId(null); }}>
                  <div className="ma-modal" onMouseDown={(e)=>e.stopPropagation()}>
                    <div className="ma-modal-head">
                      <div className="ma-thumb">{it.kind === "ingredient" ? <Icon name="leaf" size={18} stroke={1.8}/> : <Icon name="utensils-crossed" size={18} stroke={1.8}/>}</div>
                      <div className="ma-modal-titles">
                        <div className="ma-modal-title">{it.item}</div>
                        <div className="ma-modal-sub"><Icon name="list-checks" size={13} stroke={2.2}/> {it.checklist.length} {it.checklist.length===1?"fix":"fixes"}</div>
                        <div className="ma-modal-tags">
                          <span className={`pill ${MA_STATUS[it.status].tone}`}>{MA_STATUS[it.status].t}</span>
                          <PriorityPill priority={it.priority}/>
                          <span className={`ma-due ${it.overdue && it.status!=="completed" ?"is-overdue":""}`}><Icon name="calendar" size={12} stroke={2.2}/> Due {it.due}</span>
                          <span className="ma-by">Assigned by {it.by}</span>
                        </div>
                      </div>
                      <button className="btn secondary sm ma-modal-close" onClick={()=>setOpenId(null)}>Close</button>
                    </div>
                <div className="ma-detail">
                  <div className="ma-detail-col">
                    <div className="ma-panel">
                    <div className="ma-detail-h"><Icon name="list-checks" size={14} stroke={2.2}/> Assignment list <span className="muted">{done}/{it.checklist.length}</span></div>
                    {it.checklist.map((c, ci) => {
                      const loc = c.loc || {};
                      const fld = loc.field || c.t;
                      return (
                      <div key={ci} className={`ma-al-row ${c.done?"done":""}`}>
                        <div className="ma-al-top">
                          <span className={`ma-check-box ${c.done?"on":""}`}>{c.done ? <Icon name="check" size={11} stroke={3}/> : <Icon name="circle" size={11} stroke={2.2}/>}</span>
                          <button className="ma-al-field" title="Open this field in the library" onClick={()=>viewSource(it, loc)}>
                            {loc.sec && <span className="ma-al-sec">{loc.sec}</span>}
                            <span className="ma-al-name">{fld}</span>
                            <Icon name="external-link" size={12} stroke={2.4}/>
                          </button>
                          {loc.type && <span className={`ca-type-badge t-${loc.type}`}>{loc.type}</span>}
                        </div>
                        <div className="ma-al-cur">Flagged value: <span className={c.done?"struck":""}>{loc.cur || "—"}</span>{c.done && c.fixedValue && <span className="ma-al-newval"><Icon name="arrow-right" size={12} stroke={2.4}/> {c.fixedValue}</span>}</div>
                        {!c.done ? (
                          <MaFixInput type={loc.type} initial={c.fixedValue} disabled={it.status==="completed"} onSave={(val)=>resolveCheck(it.id, ci, val)} />
                        ) : (
                          <div className="ma-al-fixed"><Icon name="check-circle-2" size={13} stroke={2.4}/> Corrected <button className="ma-al-undo" onClick={()=>toggleCheck(it.id, ci)}>Undo</button></div>
                        )}
                      </div>
                    );})}
                    </div>
                  </div>
                  <div className="ma-detail-col">
                    <div className="ma-panel">
                      <div className="ma-detail-h"><Icon name="settings-2" size={14} stroke={2.2}/> Status</div>
                      <div className="ma-status-list">
                        {["not-started","in-progress","ready","completed"].map(s => {
                          const meta = MA_STATUS[s];
                          const on = it.status === s;
                          const isComplete = s === "completed";
                          const blocked = isComplete && done < it.checklist.length;
                          return (
                            <button key={s} className={`ma-status-opt ${on?"on":""} tone-${meta.tone}`} disabled={blocked} title={blocked ? `Fix all ${it.checklist.length} checklist items first (${done}/${it.checklist.length} done)` : ""} onClick={()=> isComplete ? complete(it) : setStatus(it.id, s)}>
                              {on
                                ? <span className="ma-status-sel"><Icon name="check-circle-2" size={16} stroke={2.2}/> {meta.t}</span>
                                : <><span className="ma-status-radio"/><span className="ma-status-label">{meta.t}</span></>}
                            </button>
                          );
                        })}
                      </div>
                      {it.status !== "completed" && done < it.checklist.length && <div className="ma-complete-hint">Resolve all {it.checklist.length} checklist items to mark this complete.</div>}
                    </div>

                    <div className="ma-panel">
                      <div className="ma-actions-tabs">
                        <button className={`ma-atab ${detailTab==="comment"?"on":""}`} onClick={()=>setDetailTab(detailTab==="comment"?null:"comment")}><Icon name="message-circle" size={14} stroke={2.2}/> Comment {(threads[it.id]?.length||0)>0 && <span className="ma-atab-count">{threads[it.id]?.length}</span>}</button>
                        <button className={`ma-atab ${detailTab==="attach"?"on":""}`} onClick={()=>setDetailTab(detailTab==="attach"?null:"attach")}><Icon name="paperclip" size={14} stroke={2.2}/> Attach</button>
                        <button className={`ma-atab ${detailTab==="activity"?"on":""}`} onClick={()=>setDetailTab(detailTab==="activity"?null:"activity")}><Icon name="history" size={14} stroke={2.2}/> Activity</button>
                      </div>
                      {detailTab==="comment" && <MaCommentsInline item={it} thread={threads[it.id]||[]} onClose={()=>setDetailTab(null)} onSend={(text, replyTo)=>addComment(it.id, text, replyTo)} onReact={(i,e)=>reactComment(it.id,i,e)} />}
                      {detailTab==="attach" && <MaAttachInline item={it} onClose={()=>setDetailTab(null)} onAdd={(f)=>{ toast(`Attached ${f}`); }} />}
                      {detailTab==="activity" && <MaActivityInline item={it} thread={threads[it.id]||[]} onClose={()=>setDetailTab(null)} />}
                    </div>
                  </div>
                </div>
                  </div>
                </div>
              ), document.body)}
            </div>
          );
        })}
      </div>}

      {view!=="cards" && (() => {
        const dueNum = it => parseInt((it.due||"").replace(/[^0-9]/g,""))||99;
        const list = [...shown];
        if (view==="compact") return (
          <div className="ma-compact">
            {list.map(it => (
              <div key={it.id} className={`ma-compact-row ${it.status==="completed"?"done":""}`} onClick={()=>openItem(it)}>
                <span className={`ma-compact-check ${it.status==="completed"?"on":""}`}>{it.status==="completed" && <Icon name="check" size={11} stroke={3}/>}</span>
                <div style={{flex:1,minWidth:0}}>
                  <div className="ma-compact-task">{it.task}</div>
                  <div className="ma-compact-sub">{it.item}</div>
                </div>
                <PriorityPill priority={it.priority}/>
                <span className="ma-compact-due">{it.due}</span>
              </div>
            ))}
            {list.length===0 && <div className="empty"><div className="icon"><Icon name="check-check" size={24}/></div><h3>Nothing here</h3></div>}
          </div>
        );
        if (view==="table") return (
          <div className="card" style={{overflow:"auto"}}>
            <table className="kb-table">
              <thead><tr><th>Assignment</th><th>Task</th><th>Status</th><th>Due</th><th>Priority</th><th>Assigned by</th></tr></thead>
              <tbody>
                {list.map(it => (
                  <tr key={it.id} onClick={()=>openItem(it)} style={{cursor:"pointer"}}>
                    <td style={{fontWeight:700}}>{it.item}</td>
                    <td className="muted">{it.task}</td>
                    <td><span className={`pill ${MA_STATUS[it.status].tone}`}>{MA_STATUS[it.status].t}</span></td>
                    <td className={it.overdue&&it.status!=="completed"?"":""} style={{color:it.overdue&&it.status!=="completed"?"var(--error-600)":""}}>{it.due}</td>
                    <td><PriorityPill priority={it.priority}/></td>
                    <td className="muted" style={{fontSize:12}}>{it.by}</td>
                  </tr>
                ))}
              </tbody>
            </table>
            {list.length===0 && <div className="empty"><div className="icon"><Icon name="table" size={24}/></div><h3>No rows</h3></div>}
          </div>
        );
        if (view==="calendar") {
          const byDay={}; list.forEach(it=>{ const d=dueNum(it); (byDay[d]=byDay[d]||[]).push(it); });
          const first=new Date(2026,5,1); const startDow=(first.getDay()+6)%7; const cells=[]; for(let i=0;i<startDow;i++)cells.push(null); for(let d=1;d<=30;d++)cells.push(d);
          return (
            <div className="kb-cal card">
              <div className="kb-cal-head"><strong>June 2026</strong><span className="muted">your assignments by due date</span></div>
              <div className="kb-cal-dow">{["Mon","Tue","Wed","Thu","Fri","Sat","Sun"].map(d=><span key={d}>{d}</span>)}</div>
              <div className="kb-cal-grid">
                {cells.map((d,i)=>(
                  <div key={i} className={`kb-cal-cell ${!d?"empty":""} ${d===12?"today":""}`}>
                    {d && <><span className="kb-cal-day">{d}</span><div className="kb-cal-events">
                      {(byDay[d]||[]).slice(0,3).map(it=><button key={it.id} className="kb-cal-ev" style={{borderLeftColor:it.priority==="high"?"#d92d20":it.priority==="medium"?"#dc6803":"#5fa92f"}} onClick={()=>openItem(it)} title={`${it.item} · ${it.task}`}>{it.task}</button>)}
                      {(byDay[d]||[]).length>3 && <span className="kb-cal-more">+{byDay[d].length-3}</span>}
                    </div></>}
                  </div>
                ))}
              </div>
            </div>
          );
        }
        if (view==="queue") {
          const today=13;
          const groups=[
            {id:"needs",t:"Needs Action",ic:"circle-dot",tone:"amber",fn:it=>it.status==="not-started"},
            {id:"today",t:"Due Today",ic:"calendar-clock",tone:"red",fn:it=>dueNum(it)<=today&&it.status!=="completed"},
            {id:"progress",t:"In Progress",ic:"loader",tone:"cobalt",fn:it=>it.status==="in-progress"},
            {id:"review",t:"Waiting on Review",ic:"eye",tone:"violet",fn:it=>it.status==="ready"},
            {id:"done",t:"Completed",ic:"check-circle-2",tone:"green",fn:it=>it.status==="completed"},
          ];
          return (
            <div className="kb-queue">
              {groups.map(g=>{ const gi=list.filter(g.fn); return (
                <div key={g.id} className="kb-queue-group">
                  <div className="kb-queue-h"><span className={`kb-queue-ic ${g.tone}`}><Icon name={g.ic} size={14}/></span><strong>{g.t}</strong><span className="kb-queue-n">{gi.length}</span></div>
                  <div className="kb-queue-items">
                    {gi.length===0 && <div className="kb-queue-empty">Nothing here</div>}
                    {gi.map(it=>(
                      <div key={it.id+g.id} className="kb-queue-card" onClick={()=>openItem(it)}>
                        <div style={{flex:1,minWidth:0}}><div className="kb-queue-name">{it.item}</div><div className="kb-queue-meta">{it.task} · {it.due}</div></div>
                        <PriorityPill priority={it.priority}/>
                      </div>
                    ))}
                  </div>
                </div>
              ); })}
            </div>
          );
        }
        return null;
      })()}
      <div className="ma-perm-note">
        <Icon name="info" size={13} stroke={2.2}/>
        <span>You can open tasks, comment, attach files, update checklist &amp; status, and mark complete. Reassigning, changing priority, and deleting are managed by your team lead in <strong>Assignments</strong>.</span>
      </div>
      {fix && (() => {
        const it = items.find(x => x.id === fix.itemId); if (!it) return null;
        const c = it.checklist[fix.ci];
        return <MaFixModal item={it} check={c} onClose={()=>setFix(null)} onResolve={(val)=>{ resolveCheck(it.id, fix.ci, val); setFix(null); toast("Fix applied & checklist item resolved"); }} viewSource={(loc)=>{ setDetailFocus && setDetailFocus({ kind: it.kind, sec: loc.sec, field: loc.field, label: c.t, cur: loc.cur }); viewSource(it); }} />;
      })()}
      {comments && !openId && (() => {
        const it = items.find(x => x.id === comments); if (!it) return null;
        return <MaCommentsModal item={it} thread={threads[it.id]||[]} onClose={()=>setComments(null)} onSend={(text, replyTo)=>addComment(it.id, text, replyTo)} />;
      })()}
    </div>
  );
}

/* Checklist "Fix" popup, shows the exact place to correct, ties to change-request stage */
const MA_FIX_LOC = {
  "Verify macros vs USDA": { sec: "Nutrition", field: "Macros (per serving)", cur: "Calories 412 · Protein 32g", hint: "Compare each macro to the cited USDA row.", type: "text" },
  "Check serving size": { sec: "Nutrition", field: "Serving size", cur: "4 servings", hint: "Confirm the serving basis matches the label.", type: "number", unit: "servings" },
  "Confirm calorie total": { sec: "Nutrition", field: "Calories", cur: "412 kcal", hint: "Recompute from ingredient quantities.", type: "number", unit: "kcal" },
  "Confirm allergen flags": { sec: "Allergens", field: "Declared allergens", cur: "None", hint: "Select each allergen this item contains.", type: "allergens" },
  "Add cross-contamination note": { sec: "Allergens", field: "Cross-contamination", cur: "(empty)", hint: "Add a handling note for shared equipment.", type: "text" },
  "Clarify simmer time": { sec: "Cooking Steps", field: "Step 2, simmer", cur: "“simmer until done”", hint: "State an exact time and heat level.", type: "text" },
  "Reference each ingredient": { sec: "Cooking Steps", field: "Step ingredients", cur: "2 of 5 referenced", hint: "Each step should name the ingredient it uses.", type: "text" },
  "Check caffeine disclosure": { sec: "Compliance Risk", field: "Caffeine disclosure", cur: "Not disclosed", hint: "Enter caffeine content (mg) to disclose.", type: "number", unit: "mg" },
  "Verify health claims": { sec: "Compliance Risk", field: "Health claims", cur: "“detox”", hint: "Remove unsupported claims per FDA.", type: "text" },
  "Enter per-100g macros": { sec: "Macronutrients", field: "Calories (per 100g)", cur: "(empty)", hint: "Enter the per-100g calorie value.", type: "number", unit: "kcal" },
  "Cite USDA source": { sec: "Sources", field: "Source link", cur: "(empty)", hint: "Paste the FDC URL.", type: "url" },
  "Validate fat content": { sec: "Macronutrients", field: "Fat", cur: "28 g", hint: "Verify against the source row.", type: "number", unit: "g" },
  "Check sugar values": { sec: "Macronutrients", field: "Sugar", cur: "29 g", hint: "Confirm added vs total sugar.", type: "number", unit: "g" },
  "Attach FDC ID": { sec: "Sources", field: "FDC ID", cur: "(empty)", hint: "Enter the FoodData Central ID.", type: "text" },
  "Set default serving": { sec: "Sources", field: "Default serving", cur: "(empty)", hint: "Set the default serving size.", type: "number", unit: "g" },
  "Allergen disclosure": { sec: "Allergens", field: "Disclosure", cur: "Complete", hint: "Confirm disclosure matches ingredients.", type: "text" },
  "Label rules": { sec: "Compliance Risk", field: "Label rules", cur: "Pass", hint: "Confirm regional label rules pass.", type: "text" },
  "Replace hero image": { sec: "Image", field: "Hero photo", cur: "low-res.jpg", hint: "Upload a sharp plated photo, min 1600px.", type: "image" },
  "Replace gallery image #3": { sec: "Media", field: "Gallery image #3", cur: "out-of-focus.jpg", hint: "Upload a replacement that is in focus.", type: "image" },
  "Add step photo": { sec: "Cooking Steps", field: "Step image", cur: "(none)", hint: "Upload a photo for this cooking step.", type: "image" },
};
const MA_ALLERGENS = ["Milk (Dairy)","Eggs","Fish","Shellfish","Peanuts","Tree Nut","Soy","Wheat (Gluten)","Sesame"];
function MaFixModal({ item, check, onClose, onResolve, viewSource }) {
  const loc = check.loc || MA_FIX_LOC[check.t] || { sec: "Details", field: check.t, cur: "(see item)", hint: "Make the correction described.", type: "text" };
  const type = loc.type || "text";
  const [val, setVal] = React.useState(check.fixedValue || "");
  const [img, setImg] = React.useState(null);
  const [alg, setAlg] = React.useState([]);
  const [makeCR, setMakeCR] = React.useState(false);
  const [stage, setStage] = React.useState(loc.sec);
  const stages = ["Basics","Image","Ingredients","Cooking Steps","Nutrition","Macronutrients","Allergens","Compliance Risk","Sources","References"];
  const pickImage = () => {
    const inp = document.createElement("input"); inp.type = "file"; inp.accept = "image/*";
    inp.onchange = (e) => { const f = e.target.files && e.target.files[0]; if (!f) return; const rd = new FileReader(); rd.onload = () => setImg({ url: rd.result, name: f.name }); rd.readAsDataURL(f); };
    inp.click();
  };
  const resolved = type === "image" ? !!img : type === "allergens" ? true : type === "number" ? (val !== "" && !isNaN(Number(val))) : !!val.trim();
  const resolveVal = type === "image" ? (img ? img.name : "") : type === "allergens" ? (alg.length ? alg.join(", ") : "None") : type === "number" ? `${val}${loc.unit ? " " + loc.unit : ""}` : val;
  const typeLabel = { image: "Image upload", number: "Number", url: "URL", text: "Text", allergens: "Allergen toggles" }[type];
  return (
    <Modal open onClose={onClose} width={560} title={`Fix · ${check.t}`} subtitle={`${item.item}, correct the exact field, then resolve this checklist item.`}
      footer={<>
        <button className="btn ghost" onClick={onClose}>Cancel</button>
        <button className="btn primary" disabled={!resolved} onClick={()=>onResolve(resolveVal)}><Icon name="check" size={14}/> Save &amp; resolve</button>
      </>}>
      <div className="ma-fix-loc">
        <div className="ma-fix-bread"><span>{item.kind==="ingredient"?"Ingredient":"Recipe"}</span> <Icon name="chevron-right" size={12}/> <span>{loc.sec}</span> <Icon name="chevron-right" size={12}/> <strong>{loc.field}</strong></div>
        <button className="ma-fix-jump" onClick={()=>viewSource(loc)}><Icon name="external-link" size={13}/> Open in {item.kind==="ingredient"?"ingredient":"recipe"} editor</button>
      </div>
      <div className="ma-fix-cur"><span className="l">Current value</span><span className="v">{loc.cur}</span><span className="ma-fix-type"><Icon name="type" size={11}/> {typeLabel}</span></div>
      <div className="ma-fix-hint"><Icon name="info" size={13}/> {loc.hint}</div>

      <div className="field" style={{marginTop:14}}>
        <label>Corrected value <span className="muted" style={{fontWeight:500}}>· {typeLabel.toLowerCase()} only</span></label>
        {type === "image" && (
          img
            ? <div className="ma-fix-imgwrap"><img src={img.url} alt={img.name}/><div className="ma-fix-imgmeta"><span>{img.name}</span><button className="btn ghost sm" onClick={()=>setImg(null)}><Icon name="x" size={13}/> Remove</button></div></div>
            : <button className="ma-fix-drop" onClick={pickImage}><Icon name="image-plus" size={22} stroke={1.8}/><span>Upload image</span><span className="ma-fix-drop-sub">JPG or PNG · the only accepted input here</span></button>
        )}
        {type === "number" && (
          <div className="ar-pair"><input type="number" step="any" value={val} onChange={e=>setVal(e.target.value)} placeholder={`Enter ${loc.field.toLowerCase()}…`} autoFocus/>{loc.unit && <span className="suffix">{loc.unit}</span>}</div>
        )}
        {type === "url" && (
          <input className="input" type="url" value={val} onChange={e=>setVal(e.target.value)} placeholder="https://fdc.nal.usda.gov/…" autoFocus/>
        )}
        {type === "text" && (
          <textarea className="textarea" rows="2" value={val} onChange={e=>setVal(e.target.value)} placeholder={`Enter the corrected ${loc.field.toLowerCase()}…`} autoFocus/>
        )}
        {type === "allergens" && (
          <div className="ma-fix-alg">{MA_ALLERGENS.map(a => <button key={a} type="button" className={`ar-tag ${alg.includes(a)?"on":""}`} onClick={()=>setAlg(s=>s.includes(a)?s.filter(x=>x!==a):[...s,a])}>{a}</button>)}</div>
        )}
        {type === "number" && val !== "" && isNaN(Number(val)) && <div className="ar-field-hint warn">Numbers only for this field.</div>}
      </div>

      <label className="ma-fix-cr">
        <input type="checkbox" checked={makeCR} onChange={e=>setMakeCR(e.target.checked)}/>
        <span>Log as a <strong>change request</strong> &amp; create a follow-up assignment</span>
      </label>
      {makeCR && (
        <div className="field" style={{marginTop:10}}>
          <label>Stage that needs the fix</label>
          <select className="select" value={stage} onChange={e=>setStage(e.target.value)}>{stages.map(s=><option key={s}>{s}</option>)}</select>
        </div>
      )}
    </Modal>
  );
}

/* Type-aware fix input, number / allergen tabs / text (auto-grow) / image upload */
const MA_ALLERGEN_OPTS = ["Milk (Dairy)","Eggs","Fish","Shellfish","Peanuts","Tree Nut","Soybeans","Wheat (Gluten)","Sesame","None"];
function MaFixInput({ type, initial, disabled, onSave }) {
  const [num, setNum] = React.useState(initial && /^[0-9.\-]+$/.test(initial) ? initial : "");
  const [text, setText] = React.useState(initial || "");
  const [picked, setPicked] = React.useState(initial ? initial.split(", ").filter(Boolean) : []);
  const [img, setImg] = React.useState(null);
  const taRef = React.useRef(null);
  const grow = (el) => { if (!el) return; el.style.height = "auto"; el.style.height = Math.min(el.scrollHeight, 180) + "px"; };

  if (type === "number") {
    return (
      <div className="ma-al-fix">
        <input className="ma-al-input" type="text" inputMode="decimal" value={num} disabled={disabled}
          placeholder="Enter corrected number…"
          onChange={(e)=>{ const v=e.target.value; if(v===""||/^[0-9]*\.?[0-9]*$/.test(v)) setNum(v); }}
          onKeyDown={(e)=>{ if(e.key==="Enter" && num.trim() && !disabled) onSave(num.trim()); }} />
        <button className="btn primary sm" disabled={disabled} onClick={()=>{ if(num.trim()) onSave(num.trim()); }}>Save</button>
      </div>
    );
  }
  if (type === "allergen" || type === "allergens" || type === "tabs" || type === "select") {
    const toggle = (a) => setPicked(p => a==="None" ? ["None"] : (p.includes(a) ? p.filter(x=>x!==a) : [...p.filter(x=>x!=="None"), a]));
    return (
      <div className="ma-al-fix-col">
        <div className="ma-allergen-tabs">
          {MA_ALLERGEN_OPTS.map(a => (
            <button key={a} type="button" className={`ma-allergen-tab ${picked.includes(a)?"on":""}`} disabled={disabled} onClick={()=>toggle(a)}>
              {picked.includes(a) && <Icon name="check" size={11} stroke={3}/>} {a}
            </button>
          ))}
        </div>
        <button className="btn primary sm" disabled={disabled || picked.length===0} onClick={()=>onSave(picked.join(", "))}>Save selection</button>
      </div>
    );
  }
  if (type === "image") {
    const pick = () => {
      const inp = document.createElement("input"); inp.type="file"; inp.accept="image/*";
      inp.onchange = (e)=>{ const f=e.target.files[0]; if(!f) return; const r=new FileReader(); r.onload=()=>setImg({ url:r.result, name:f.name }); r.readAsDataURL(f); };
      inp.click();
    };
    return (
      <div className="ma-al-fix-col">
        {img ? (
          <div className="ma-img-preview"><img src={img.url} alt={img.name}/><button className="ma-img-x" onClick={()=>setImg(null)}><Icon name="x" size={12} stroke={2.8}/></button></div>
        ) : (
          <button type="button" className="ma-img-drop" disabled={disabled} onClick={pick}><Icon name="image-plus" size={20} stroke={1.8}/><strong>Upload corrected image</strong><span>JPG or PNG, up to 5MB</span></button>
        )}
        <button className="btn primary sm" disabled={disabled || !img} onClick={()=>onSave(img? img.name : "")}>Save image</button>
      </div>
    );
  }
  // text, auto-grow, allows letters/numbers/symbols
  return (
    <div className="ma-al-fix ma-al-fix-text">
      <textarea ref={taRef} className="ma-al-input ma-al-textarea" rows="1" value={text} disabled={disabled}
        placeholder="Enter corrected value…"
        onChange={(e)=>{ setText(e.target.value); grow(e.target); }}
        onKeyDown={(e)=>{ if(e.key==="Enter" && !e.shiftKey && text.trim() && !disabled){ e.preventDefault(); onSave(text.trim()); } }} />
      <button className="btn primary sm ma-al-savebtn" disabled={disabled} onClick={()=>{ if(text.trim()) onSave(text.trim()); }}>Save</button>
    </div>
  );
}

/* Comments thread with @mention */
const MA_MENTION = ["Martha Houston","Yola Racheal","Joy Jimmy","William Bryson","John Roberts","Aisha Bello","Dana Liu","Eve Nakamura","Sarah Chen"];
const MA_AV_COLOR = { "Martha Houston":"#2f7d32", "Yola Racheal":"#6b3fa0", "Joy Jimmy":"#e8731f", "William Bryson":"#2563c9", "John Roberts":"#c0392b", "Aisha Bello":"#0d7d6b", "Dana Liu":"#b5179e", "Eve Nakamura":"#2A6FDB", "Sarah Chen":"#3b7c0f" };
const MA_FACE_EMOJIS = ["😀","😅","😊","😌","🤭","😜","😎","😒","🙌","🔥","✅","🎉"];
const maAvColor = (n) => MA_AV_COLOR[n] || "#667085";
function MaCommentsInline({ item, thread, onSend, onClose, onReact }) {
  const [text, setText] = React.useState("");
  const [replyTo, setReplyTo] = React.useState(null);
  const [mentionOpen, setMentionOpen] = React.useState(false);
  const [emojiFor, setEmojiFor] = React.useState(null);
  const [composerEmoji, setComposerEmoji] = React.useState(false);
  const EMOJIS = ["👍","👍🏽","👍🏿","👎","❤️","🎉","😀","😂","🙌","🙌🏽","🙏","🙏🏿","🔥","✅","👀","💯","👏","👏🏽","🤝","💪","✨","🚀"];
  const taRef = React.useRef(null);
  const onChange = (e) => { const v = e.target.value; setText(v); const m = /@(\w*)$/.exec(v.slice(0, e.target.selectionStart)); setMentionOpen(!!m); };
  const pickMention = (name) => { setText(t => t.replace(/@(\w*)$/, "@" + name + " ")); setMentionOpen(false); taRef.current && taRef.current.focus(); };
  const insertAt = () => { setText(t => (t + (t && !/\s$/.test(t) ? " " : "") + "@")); setMentionOpen(true); taRef.current && taRef.current.focus(); };
  const q = (/@(\w*)$/.exec(text) || [])[1] || "";
  const matches = MA_MENTION.filter(n => n.toLowerCase().includes(q.toLowerCase()));
  const renderText = (s) => s.split(/(@[\w]+(?:\s[\w]+)?)/g).map((p, i) => /^@/.test(p) && MA_MENTION.some(n => p.includes(n.split(" ")[0])) ? <span key={i} className="ma-mention">{p}</span> : p);
  const send = () => { if (!text.trim()) return; onSend(text.trim(), replyTo != null ? { who: thread[replyTo].who, text: thread[replyTo].text } : null); setText(""); setReplyTo(null); };
  return (
    <div className="ma-comments-card">
      <div className="ma-comments-head"><Icon name="message-circle" size={14} stroke={2.2}/> Comments <span className="muted">{thread.length}</span><button className="ma-comments-x" onClick={onClose} title="Hide comments"><Icon name="x" size={14} stroke={2.4}/></button></div>
      <div className="cu-chat cu-chat-bubbles" style={{ maxHeight: 240, overflowY: "auto", marginBottom: 12 }}>
        {thread.length === 0 && <div className="muted" style={{fontSize:12.5,padding:"8px 0",textAlign:"center"}}>No comments yet. Start the conversation below.</div>}
        {thread.map((m, i) => {
          const mine = m.who === "You" || m.init === "ME";
          return (
            <div key={i} className={`cu-cbubble ${mine?"me":"them"}`}>
              <span className="cu-cbubble-av" style={{ background: m.color || (mine ? "#3b7c0f" : "#2A6FDB") }}>{m.init}</span>
              <div className="cu-cbubble-col">
                <div className="cu-cbubble-top"><strong>{mine ? "You" : m.who}</strong> <span className="muted">{m.at}</span></div>
                {m.replyTo && <div className="cu-cbubble-quote"><span className="cu-cbubble-quote-who">{m.replyTo.who}</span>{m.replyTo.text.length > 50 ? m.replyTo.text.slice(0,50) + "…" : m.replyTo.text}</div>}
                <div className="cu-cbubble-card">{renderText(m.text)}</div>
                {m.reactions && Object.keys(m.reactions).length > 0 && (
                  <div className="ma-reactions">{Object.entries(m.reactions).map(([e,n])=><span key={e} className="ma-reaction" onClick={()=>onReact && onReact(i,e)}>{e} {n}</span>)}</div>
                )}
                <div className="cu-cbubble-acts">
                  <button className="cu-cbubble-reply" onClick={() => { setReplyTo(i); taRef.current && taRef.current.focus(); }}><Icon name="reply" size={12}/> Reply</button>
                  <button className="cu-cbubble-reply" onClick={() => onReact && onReact(i,"👍")}><Icon name="thumbs-up" size={12}/> Like</button>
                  <div style={{position:"relative"}}>
                    <button className="cu-cbubble-reply" onClick={() => setEmojiFor(emojiFor===i?null:i)}><Icon name="smile" size={12}/></button>
                    {emojiFor===i && <div className="ma-emoji-pop">{EMOJIS.map(e=><button key={e} onClick={()=>{ onReact && onReact(i,e); setEmojiFor(null); }}>{e}</button>)}</div>}
                  </div>
                </div>
              </div>
            </div>
          );
        })}
      </div>
      <div className="ma-composer">
        <div style={{position:"relative"}}>
          {replyTo != null && thread[replyTo] && (
            <div className="cu-reply-chip"><Icon name="reply" size={12}/> Replying to <strong>{thread[replyTo].who === "You" ? "yourself" : thread[replyTo].who}</strong><button onClick={() => setReplyTo(null)}><Icon name="x" size={12}/></button></div>
          )}
          <textarea ref={taRef} className="textarea" rows="2" value={text} onChange={onChange} placeholder="Write a comment…"/>
          {mentionOpen && matches.length > 0 && (
            <div className="ma-mention-pop">
              <div className="ma-mention-head">People</div>
              {matches.map(n => <button key={n} onClick={()=>pickMention(n)}><span className="ma-people-av" style={{background:maAvColor(n)}}>{n.split(" ").map(w=>w[0]).join("")}</span> {n}</button>)}
            </div>
          )}
          {composerEmoji && (
            <div className="ma-composer-emojipop">
              {MA_FACE_EMOJIS.map(e => <button key={e} type="button" onClick={()=>{ setText(t=>t+e); }}>{e}</button>)}
            </div>
          )}
          <div className="ma-composer-bar">
            <div className="ma-composer-tools">
              <button type="button" title="Mention someone" onClick={insertAt}><Icon name="at-sign" size={16} stroke={2.2}/></button>
              <button type="button" className={composerEmoji?"on":""} title="Emoji" onClick={()=>setComposerEmoji(v=>!v)}><Icon name="smile" size={16} stroke={2.2}/></button>
            </div>
            <button className="btn primary sm" disabled={!text.trim()} onClick={send}><Icon name="send" size={13}/> Send</button>
          </div>
        </div>
      </div>
    </div>
  );
}
/* Attach panel, drop docs/photos/files */
function MaAttachInline({ item, onClose, onAdd }) {
  const [files, setFiles] = React.useState(item.files || [{ n: "USDA-sodium-row.pdf", t: "doc" }]);
  const add = (n, t) => { setFiles(f => [...f, { n, t }]); onAdd && onAdd(n); };
  return (
    <div className="ma-comments-card">
      <div className="ma-comments-head"><Icon name="paperclip" size={14} stroke={2.2}/> Attachments <span className="muted">{files.length}</span><button className="ma-comments-x" onClick={onClose}><Icon name="x" size={14} stroke={2.4}/></button></div>
      <div className="ma-attach-drop" onClick={()=>add("upload-"+(files.length+1)+".pdf","doc")}>
        <Icon name="upload-cloud" size={22} stroke={1.8}/>
        <strong>Drop files or click to upload</strong>
        <span>Documents, photos, spreadsheets, up to 10MB each</span>
      </div>
      <div className="ma-attach-types">
        <button onClick={()=>add("note.docx","doc")}><Icon name="file-text" size={14}/> Document</button>
        <button onClick={()=>add("photo.jpg","img")}><Icon name="image" size={14}/> Photo</button>
        <button onClick={()=>add("data.csv","sheet")}><Icon name="table" size={14}/> File</button>
      </div>
      <div className="ma-attach-list">
        {files.map((f,i)=>(
          <div key={i} className="ma-attach-row">
            <span className="ma-attach-ic"><Icon name={f.t==="img"?"image":f.t==="sheet"?"table":"file-text"} size={14}/></span>
            <span className="ma-attach-name">{f.n}</span>
            <button onClick={()=>setFiles(x=>x.filter((_,j)=>j!==i))}><Icon name="trash-2" size={13}/></button>
          </div>
        ))}
      </div>
    </div>
  );
}

/* Activity log, timestamped events */
function MaActivityInline({ item, thread, onClose }) {
  const acts = [
    { ic: "user-plus", who: item.by || "Sarah Chen", act: "created this assignment", at: "Jun 10 · 9:24 AM" },
    { ic: "flag", who: "Loraa", act: `flagged ${item.checklist.length} fields for review`, at: "Jun 10 · 9:25 AM" },
    ...(thread||[]).map(m => ({ ic: "message-circle", who: m.who==="You"?"You":m.who, act: "commented", at: m.at })),
    ...(item.checklist||[]).filter(c=>c.done).map(c => ({ ic: "check-circle-2", who: "You", act: `resolved "${(c.loc&&c.loc.field)||c.t}"`, at: "just now" })),
  ];
  return (
    <div className="ma-comments-card">
      <div className="ma-comments-head"><Icon name="history" size={14} stroke={2.2}/> Activity <span className="muted">{acts.length}</span><button className="ma-comments-x" onClick={onClose}><Icon name="x" size={14} stroke={2.4}/></button></div>
      <div className="ma-activity-list">
        {acts.map((a,i)=>(
          <div key={i} className="ma-activity-row">
            <span className="ma-activity-ic"><Icon name={a.ic} size={13} stroke={2.2}/></span>
            <div className="ma-activity-body"><div className="ma-activity-t"><strong>{a.who}</strong> {a.act}</div><div className="ma-activity-at">{a.at}</div></div>
          </div>
        ))}
      </div>
    </div>
  );
}

function MaCommentsModal({ item, thread, onClose, onSend }) {
  const [text, setText] = React.useState("");
  const [replyTo, setReplyTo] = React.useState(null);
  const [mentionOpen, setMentionOpen] = React.useState(false);
  const taRef = React.useRef(null);
  const onChange = (e) => { const v = e.target.value; setText(v); const m = /@(\w*)$/.exec(v.slice(0, e.target.selectionStart)); setMentionOpen(!!m); };
  const pickMention = (name) => { setText(t => t.replace(/@(\w*)$/, "@" + name + " ")); setMentionOpen(false); taRef.current && taRef.current.focus(); };
  const q = (/@(\w*)$/.exec(text) || [])[1] || "";
  const matches = MA_MENTION.filter(n => n.toLowerCase().includes(q.toLowerCase()));
  const renderText = (s) => s.split(/(@[\w]+(?:\s[\w]+)?)/g).map((p, i) => /^@/.test(p) && MA_MENTION.some(n => p.includes(n.split(" ")[0])) ? <span key={i} className="ma-mention">{p}</span> : p);
  const send = () => { if (!text.trim()) return; onSend(text.trim(), replyTo != null ? { who: thread[replyTo].who, text: thread[replyTo].text } : null); setText(""); setReplyTo(null); };
  const [composerEmoji, setComposerEmoji] = React.useState(false);
  const insertAt = () => { setText(t => (t + (t && !/\s$/.test(t) ? " " : "") + "@")); setMentionOpen(true); taRef.current && taRef.current.focus(); };
  return (
    <Modal open onClose={onClose} width={520} title="Comments" subtitle={`${item.item} · ${item.task}, @mention a teammate to ask a question.`}
      footer={<button className="btn ghost" onClick={onClose}>Close</button>}>
      <div className="cu-chat cu-chat-bubbles" style={{ maxHeight: 340, overflowY: "auto", marginBottom: 14 }}>
        {thread.length === 0 && <div className="muted" style={{fontSize:13,padding:"8px 0",textAlign:"center"}}>No comments yet. Start the conversation below.</div>}
        {thread.map((m, i) => {
          const mine = m.who === "You" || m.init === "ME";
          return (
            <div key={i} className={`cu-cbubble ${mine?"me":"them"}`}>
              <span className="cu-cbubble-av" style={{ background: m.color || (mine ? "#3b7c0f" : "#2A6FDB") }}>{m.init}</span>
              <div className="cu-cbubble-col">
                <div className="cu-cbubble-top"><strong>{mine ? "You" : m.who}</strong> <span className="muted">{m.at}</span></div>
                {m.replyTo && <div className="cu-cbubble-quote"><span className="cu-cbubble-quote-who">{m.replyTo.who}</span>{m.replyTo.text.length > 50 ? m.replyTo.text.slice(0,50) + "…" : m.replyTo.text}</div>}
                <div className="cu-cbubble-card">{renderText(m.text)}</div>
                <button className="cu-cbubble-reply" onClick={() => { setReplyTo(i); taRef.current && taRef.current.focus(); }}><Icon name="reply" size={12}/> Reply</button>
              </div>
            </div>
          );
        })}
      </div>
      <div className="ma-composer">
        <div style={{position:"relative"}}>
          {replyTo != null && thread[replyTo] && (
            <div className="cu-reply-chip"><Icon name="reply" size={12}/> Replying to <strong>{thread[replyTo].who === "You" ? "yourself" : thread[replyTo].who}</strong><button onClick={() => setReplyTo(null)}><Icon name="x" size={12}/></button></div>
          )}
          <textarea ref={taRef} className="textarea" rows="2" value={text} onChange={onChange} placeholder="Write a comment…"/>
          {mentionOpen && matches.length > 0 && (
            <div className="ma-mention-pop">
              <div className="ma-mention-head">People</div>
              {matches.map(n => <button key={n} onClick={()=>pickMention(n)}><span className="ma-people-av" style={{background:maAvColor(n)}}>{n.split(" ").map(w=>w[0]).join("")}</span> {n}</button>)}
            </div>
          )}
          {composerEmoji && (
            <div className="ma-composer-emojipop">
              {MA_FACE_EMOJIS.map(e => <button key={e} type="button" onClick={()=>{ setText(t=>t+e); }}>{e}</button>)}
            </div>
          )}
          <div className="ma-composer-bar">
            <div className="ma-composer-tools">
              <button type="button" title="Mention someone" onClick={insertAt}><Icon name="at-sign" size={16} stroke={2.2}/></button>
              <button type="button" className={composerEmoji?"on":""} title="Emoji" onClick={()=>setComposerEmoji(v=>!v)}><Icon name="smile" size={16} stroke={2.2}/></button>
            </div>
            <button className="btn primary sm" disabled={!text.trim()} onClick={send}><Icon name="send" size={13}/> Send</button>
          </div>
        </div>
      </div>
    </Modal>
  );
}

function LegacyAssignmentsScreen({ autoCreate }) {
  const { openRecipe, openIngredient, toast, role, setPage } = useApp();
  const ASSIGNEES = React.useMemo(kbAssignees, []);
  const assigneeFor = (init) => ASSIGNEES.find(a => a.initials === init) || ASSIGNEES[0];
  const openCard = (card) => {
    try {
      window.__caPrefill = {
        kind: card.kind, itemName: card.name,
        assigneeInitials: card.assignee ? card.assignee.initials : null,
        priority: card.priority || "medium",
        task: card.task || "", note: card.note || "",
        stage: card.col, boardId: activeId,
      };
    } catch (e) {}
    setPage("create-assignment");
  };

  // ── Multiple boards (projects), each with its own stages + cards ──
  const [projects, setProjects] = React.useState(() => kbBuildProjects(assigneeFor));
  const [activeId, setActiveId] = React.useState(() => projects[0]?.id ?? "");
  const active = projects.find(p => p.id === activeId) || projects[0];
  const board = active?.board ?? {};
  const cols = active?.cols ?? [];
  React.useEffect(() => { kbSaveProjects(projects); }, [projects]);

  const patchProject = (id, fn) => setProjects(ps => ps.map(p => p.id === id ? fn(p) : p));
  const setBoard = (updater) => patchProject(activeId, p => ({ ...p, board: typeof updater === "function" ? updater(p.board) : updater }));

  const [drag, setDrag] = React.useState(null);      // { key, from }
  const [dropCol, setDropCol] = React.useState(null);
  const [menu, setMenu] = React.useState(null);       // card key
  const [colMenu, setColMenu] = React.useState(null);  // column id
  const [filter, setFilter] = React.useState({ priority: "all", assignee: "all" });
  const [filterOpen, setFilterOpen] = React.useState(false);
  const [kbShowAllPeople, setKbShowAllPeople] = React.useState(false);
  const [assignModal, setAssignModal] = React.useState(autoCreate ? { col: null } : null); // { col } or { col, editKey }
  const [view, setView] = React.useState("board");
  const [loadCfg, setLoadCfg] = React.useState(kbLoadConfig);
  const [stageEditor, setStageEditor] = React.useState(false);
  const [boardModal, setBoardModal] = React.useState(null); // { mode:"new"|"rename", project? }
  const [boardMenu, setBoardMenu] = React.useState(false);

  React.useEffect(() => {
    const onCfg = () => setLoadCfg(kbLoadConfig());
    window.addEventListener("nutridms-loadcfg", onCfg);
    return () => window.removeEventListener("nutridms-loadcfg", onCfg);
  }, []);

  const applyCols = (newCols) => patchProject(activeId, p => {
    const ids = newCols.map(c => c.id); const next = {};
    newCols.forEach(c => { next[c.id] = p.board[c.id] || []; });
    Object.keys(p.board).forEach(k => { if (!ids.includes(k)) next[newCols[0].id] = [...(next[newCols[0].id] || []), ...p.board[k]]; });
    return { ...p, cols: newCols, board: next };
  });
  const intakeId = cols[0] ? cols[0].id : "to-assign";
  const finalIds = cols.filter(c => c.done).map(c => c.id);

  // Project (board) CRUD
  const createBoard = (name) => {
    const id = "prj-" + Date.now();
    const ncols = KB_DEFAULT_STAGES.map(c => ({ ...c }));
    const nb = {}; ncols.forEach(c => nb[c.id] = []);
    setProjects(ps => [...ps, { id, name: name || "New board", color: KB_COL_PALETTE[ps.length % KB_COL_PALETTE.length], cols: ncols, board: nb }]);
    setActiveId(id); toast(`Board “${name || "New board"}” created`);
  };
  const renameBoard = (id, name) => patchProject(id, p => ({ ...p, name }));
  const deleteBoard = (id) => {
    setProjects(ps => { const next = ps.filter(p => p.id !== id); if (!next.length) return ps; if (id === activeId) setActiveId(next[0].id); return next; });
    toast("Board deleted");
  };
  const duplicateBoard = (id) => {
    const src = projects.find(p => p.id === id); if (!src) return;
    const nid = "prj-" + Date.now();
    const nb = {}; Object.keys(src.board).forEach(k => nb[k] = src.board[k].map(c => ({ ...c, key: c.rid + "-" + Math.random().toString(36).slice(2, 6) })));
    setProjects(ps => [...ps, { ...src, id: nid, name: src.name + " (copy)", cols: src.cols.map(c => ({ ...c })), board: nb }]);
    setActiveId(nid); toast("Board duplicated");
  };

  const total = Object.values(board).reduce((n, arr) => n + arr.length, 0);
  // Current workload per assignee, respecting the load-balancing config (metric, final-stage inclusion)
  const workload = React.useMemo(() => {
    const m = {};
    Object.entries(board).forEach(([col, arr]) => {
      if (!loadCfg.includeFinal && finalIds.includes(col)) return;
      arr.forEach(c => { if (c.assignee) m[c.assignee.initials] = (m[c.assignee.initials] || 0) + kbCardPoints(c, loadCfg); });
    });
    return m;
  }, [board, loadCfg, cols]);
  const allCards = React.useMemo(() => Object.entries(board).flatMap(([col, arr]) => arr.map(c => ({ ...c, col }))), [board]);
  const passFilter = (c) => (filter.priority === "all" || c.priority === filter.priority) && (filter.assignee === "all" || (c.assignee && c.assignee.initials === filter.assignee));

  const moveCard = (key, from, to) => {
    if (from === to) return;
    setBoard(b => {
      const card = b[from].find(c => c.key === key);
      if (!card) return b;
      const next = { ...b, [from]: b[from].filter(c => c.key !== key), [to]: [...(b[to] || []), { ...card, assignee: to !== intakeId && !card.assignee ? assigneeFor("AB") : card.assignee }] };
      return next;
    });
    const t = cols.find(c => c.id === to);
    toast(`Moved to “${t ? t.title : to}”`);
  };
  const patchCard = (key, patch) => setBoard(b => {
    const next = {}; for (const col of Object.keys(b)) next[col] = b[col].map(c => c.key === key ? { ...c, ...patch } : c); return next;
  });
  const removeCard = (key) => { setBoard(b => { const next = {}; for (const col of Object.keys(b)) next[col] = b[col].filter(c => c.key !== key); return next; }); toast("Removed from board"); };
  const addCard = ({ col, kind, item, assigneeId, priority, blocked, task, note }) => {
    const a = ASSIGNEES.find(x => x.id === assigneeId);
    const isRec = kind === "recipe";
    const dest = board[col] ? col : intakeId;
    const card = { key: item.id + "-" + Math.random().toString(36).slice(2, 6), kind, rid: item.id, name: item.name, sub: isRec ? `${item.cuisine} · ${item.category}` : `${item.canonical} · ${item.category}`, cover: isRec ? item.cover : null, recipe: isRec ? item : null, ingredient: isRec ? null : item, task: task || null, note: note || "", assignee: a ? { name: a.name, initials: a.initials, color: a.color } : null, priority, blocked: !!blocked };
    setBoard(b => ({ ...b, [dest]: [...(b[dest] || []), card] }));
    toast(`Assigned “${item.name}” → ${a ? a.name : "unassigned"}`);
  };

  if (!active) {
    return (
      <div>
        <Crumbs path={[{ label: "Assignments" }]} />
        <div className="card" role="status" style={{ minHeight: 280, padding: 32, display: "grid", placeItems: "center", textAlign: "center" }}>
          <div>
            <Icon name="kanban-square" size={28} />
            <h1 className="page-title" style={{ marginTop: 12 }}>Team Board</h1>
            <p className="page-sub">No valid boards were found. Create a clean board to continue.</p>
            <button className="btn primary" onClick={() => createBoard("Team Board")}><Icon name="plus" size={15} /> Create board</button>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div onClick={() => { setMenu(null); setColMenu(null); setFilterOpen(false); setBoardMenu(false); }}>
      <Crumbs path={[{ label: "Assignments" }]} />
      <div className="page-head">
        <div>
          <h1 className="page-title">Assignments</h1>
          <p className="page-sub">Organize work into boards. Each board has its own stages and cards, assign people to recipes &amp; ingredients with a task and notes, then move them through your workflow.</p>
        </div>
      </div>

      <div className="kb-layout">
      {/* Left sidebar: boards, contributors, this week */}
      <aside className="kb-side">
        <div className="kb-side-sec">
          <div className="kb-side-h">Boards</div>
          {projects.map(p => {
            const n = Object.values(p.board).reduce((s, a) => s + a.length, 0);
            const on = p.id === activeId;
            return (
              <div key={p.id} className={`kb-side-board ${on ? "on" : ""}`} onClick={(e) => { e.stopPropagation(); setActiveId(p.id); setView("board"); }}>
                <span className="kb-sb-dot" style={{ background: p.color }}><Icon name="folder" size={12} /></span>
                <span className="kb-sb-name">{p.name}</span>
                <span className="kb-sb-count">{n}</span>
                {on && (
                  <button className="kb-sb-menu-btn" onClick={(e) => { e.stopPropagation(); setBoardMenu(o => !o); }} title="Board options"><Icon name="more-vertical" size={16} /></button>
                )}
                {on && boardMenu && (
                  <div className="kb-menu" style={{ right: 6, top: 40, position: "absolute" }} onClick={e => e.stopPropagation()}>
                    <div className="kb-menu-label">“{active.name}”</div>
                    <button className="kb-menu-row" onClick={() => { setBoardMenu(false); setBoardModal({ mode: "rename", project: active }); }}><Icon name="pencil" size={15} /> Rename board</button>
                    <button className="kb-menu-row" onClick={() => { setBoardMenu(false); duplicateBoard(active.id); }}><Icon name="copy" size={15} /> Duplicate board</button>
                    <button className="kb-menu-row" onClick={() => { setBoardMenu(false); setStageEditor(true); }}><Icon name="columns-3" size={15} /> Customize stages</button>
                    <div className="kb-menu-sep" />
                    <button className="kb-menu-row danger" disabled={projects.length <= 1} onClick={() => { setBoardMenu(false); if (projects.length > 1) deleteBoard(active.id); }}><Icon name="trash-2" size={15} /> Delete board</button>
                  </div>
                )}
              </div>
            );
          })}
          <button className="kb-side-newboard" onClick={(e) => { e.stopPropagation(); setBoardModal({ mode: "new" }); }}><Icon name="plus" size={15} /> New board</button>
        </div>

        <div className="kb-side-sec">
          <div className="kb-side-h">Contributors</div>
          {(kbShowAllPeople ? ASSIGNEES : ASSIGNEES.slice(0, 6)).map(a => (
            <button key={a.id} className={"kb-side-person kb-side-person-btn" + (filter.assignee === a.initials ? " on" : "")} onClick={() => { setFilter(f => ({ ...f, assignee: f.assignee === a.initials ? "all" : a.initials })); }} title={"Show " + a.name + "'s cards"}>
              <span className="kb-sp-av" style={{ background: a.color || "var(--gray-400)" }}>{a.initials}</span>
              <div className="kb-sp-body">
                <div className="kb-sp-name">{a.name}</div>
                <div className="kb-sp-sub"><span className="kb-sp-dot" /> {a.role}</div>
              </div>
              <span className="kb-sp-load" title="Assigned in this board">{workload[a.initials] || 0}</span>
              {filter.assignee === a.initials && <span className="kb-sp-clear" title="Show all assignments"><Icon name="x" size={13} /></span>}
            </button>
          ))}
          {ASSIGNEES.length > 6 && (
            <button className="kb-side-seeall" onClick={() => setKbShowAllPeople(v => !v)}>
              {kbShowAllPeople ? "Show less" : "See all (" + ASSIGNEES.length + ")"}
              <Icon name={kbShowAllPeople ? "chevron-up" : "chevron-down"} size={14} />
            </button>
          )}
        </div>

        <div className="kb-side-sec">
          <div className="kb-side-h">This week</div>
          <div className="kb-side-stat">
            <div className="kb-ss-label">In pipeline</div>
            <div className="kb-ss-big">{total} item{total === 1 ? "" : "s"}</div>
            <div className="kb-ss-trend"><Icon name="check-circle-2" size={14} /> {cols.filter(c => c.done).reduce((s, c) => s + (board[c.id] || []).length, 0)} published <span>this board</span></div>
          </div>
        </div>
      </aside>

      <div className="kb-main">
      {/* View toolbar */}
      <div className="kb-toolbar">
        <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
          <div className="kb-viewseg">
            {[["board", "layout-grid", "Board"], ["list", "list", "List"], ["table", "table", "Table"], ["calendar", "calendar-days", "Calendar"], ["timeline", "gantt-chart", "Timeline"], ["queue", "inbox", "Queue"], ["workload", "users", "Workload"]].map(([id, ic, label]) => (
              <button key={id} className={view === id ? "on" : ""} onClick={(e) => { e.stopPropagation(); setView(id); }} title={label}><Icon name={ic} size={15} /> <span className="kb-viewseg-l">{label}</span></button>
            ))}
          </div>
          <span className="muted" style={{ fontSize: 12.5 }}>{total} item{total === 1 ? "" : "s"} · {cols.length} stages</span>
        </div>
        <div style={{ display: "flex", gap: 10, position: "relative", alignItems: "center" }}>
          <button className="btn secondary" onClick={(e) => { e.stopPropagation(); setStageEditor(true); }} title="Customize stages"><Icon name="columns-3" size={16} /> <span className="kb-viewseg-l">Stages</span></button>
          <button className="btn secondary" onClick={(e) => { e.stopPropagation(); setFilterOpen(o => !o); }}>
            <Icon name="filter" size={16} /> Filter{(filter.priority !== "all" || filter.assignee !== "all") ? " ·" : ""}{filter.priority !== "all" ? " " + filter.priority : ""}{filter.assignee !== "all" ? " " + filter.assignee : ""}
          </button>
          {filterOpen && (
            <div className="card" style={{ position: "absolute", top: 46, right: 120, width: 230, zIndex: 41, padding: 12 }} onClick={e => e.stopPropagation()}>
              <div className="kb-menu-label" style={{ padding: "2px 0 6px" }}>Priority</div>
              <div className="kb-sub" style={{ padding: "0 0 8px" }}>
                {["all", "high", "medium", "low"].map(p => <button key={p} className={filter.priority === p ? "on" : ""} onClick={() => setFilter(f => ({ ...f, priority: p }))} style={{ textTransform: "capitalize" }}>{p}</button>)}
              </div>
              <div className="kb-menu-label" style={{ padding: "2px 0 6px" }}>Assignee</div>
              <div className="kb-sub" style={{ padding: 0 }}>
                <button className={filter.assignee === "all" ? "on" : ""} onClick={() => setFilter(f => ({ ...f, assignee: "all" }))}>All</button>
                {ASSIGNEES.slice(0, 6).map(a => <button key={a.id} className={filter.assignee === a.initials ? "on" : ""} onClick={() => setFilter(f => ({ ...f, assignee: a.initials }))}>{a.initials}</button>)}
              </div>
              {(filter.priority !== "all" || filter.assignee !== "all") && <button className="btn ghost sm" style={{ marginTop: 10, width: "100%", justifyContent: "center" }} onClick={() => setFilter({ priority: "all", assignee: "all" })}>Clear filters</button>}
            </div>
          )}
          <button className="btn primary" onClick={(e) => { e.stopPropagation(); setPage("create-assignment"); }}><Icon name="user-plus" size={16} /> Assign work</button>
        </div>
      </div>

      {view === "board" && (<div className="kb-board" style={{ gridTemplateColumns: `repeat(${cols.length}, minmax(220px, 1fr))` }}>
        {cols.map((c) => {
          const cards = (board[c.id] || []).filter(passFilter);
          return (
            <div key={c.id}
              className={`kb-col ${dropCol === c.id ? "drop" : ""}`}
              onDragOver={(e) => { e.preventDefault(); if (dropCol !== c.id) setDropCol(c.id); }}
              onDragLeave={(e) => { if (e.currentTarget === e.target) setDropCol(null); }}
              onDrop={(e) => { e.preventDefault(); if (drag) moveCard(drag.key, drag.from, c.id); setDrag(null); setDropCol(null); }}>
              <div className="kb-col-head">
                <div className="kb-col-title">
                  <span className="dot" style={{ background: c.color }} />
                  <strong>{c.title}</strong>
                  <span className="pill neutral" style={{ fontSize: 11 }}>{cards.length}</span>
                </div>
                <div style={{ position: "relative" }}>
                  <button className="icon-btn" style={{ width: 24, height: 24 }} onClick={(e) => { e.stopPropagation(); setColMenu(colMenu === c.id ? null : c.id); }} title="Stage options"><Icon name="more-vertical" size={15} /></button>
                  {colMenu === c.id && (
                    <div className="kb-menu" style={{ top: 30, right: 0, width: 180 }} onClick={e => e.stopPropagation()}>
                      <button className="kb-menu-row" onClick={() => { setColMenu(null); setAssignModal({ col: c.id }); }}><Icon name="plus" size={15} /> Add item</button>
                      <button className="kb-menu-row" onClick={() => { setColMenu(null); setStageEditor(true); }}><Icon name="settings-2" size={15} /> Edit stages</button>
                    </div>
                  )}
                </div>
              </div>
              <div className="kb-col-cards">
                <button className="kb-add" onClick={(e) => { e.stopPropagation(); setAssignModal({ col: c.id }); }}><Icon name="plus" size={14} /> Add New Task</button>
                {cards.map((card) => (
                  <div key={card.key}
                    className={`kb-card ${drag && drag.key === card.key ? "dragging" : ""} ${card.blocked ? "blocked" : ""}`}
                    draggable
                    onDragStart={() => setDrag({ key: card.key, from: c.id })}
                    onDragEnd={() => { setDrag(null); setDropCol(null); }}
                    onClick={() => openCard(card)}>
                    <button className="kb-card-menu-btn" onClick={(e) => { e.stopPropagation(); setMenu(menu === card.key ? null : card.key); }}><Icon name="more-vertical" size={15} /></button>
                    {card.blocked && <div className="kb-blocked-tag"><Icon name="ban" size={11} /> Blocked</div>}
                    <div className="kb-card-top">
                      {card.kind === "ingredient"
                        ? <div className="kb-thumb kb-thumb-ing"><Icon name="leaf" size={18} /></div>
                        : <div className="kb-thumb" style={{ backgroundImage: `url("${card.cover}")` }} />}
                      <div style={{ flex: 1, minWidth: 0, paddingRight: 18 }}>
                        <div className="kb-card-name">{card.name}</div>
                        <div className="kb-card-meta"><span className={`kb-kind ${card.kind}`}>{card.kind === "ingredient" ? "Ingredient" : "Recipe"}</span> {card.sub}</div>
                      </div>
                    </div>
                    {card.task && <div className="kb-task"><Icon name="clipboard-list" size={11} /> {card.task}</div>}
                    {card.note && <div className="kb-note"><Icon name="search" size={11} /> <span>{card.note}</span></div>}
                    <div className="kb-card-foot">
                      <div className="kb-card-foot-l">
                        {card.assignee
                          ? <span className="kb-assignee"><span className="kb-av" style={{ background: card.assignee.color || "var(--gray-400)" }}>{card.assignee.initials}</span><span className="kb-assignee-name">{card.assignee.name.split(" ")[0]}</span></span>
                          : <span className="kb-assignee"><span className="kb-av" style={{ background: "var(--gray-200)", color: "var(--gray-500)" }}><Icon name="user" size={12} /></span><span className="kb-assignee-name" style={{ fontStyle: "italic" }}>Unassigned</span></span>}
                      </div>
                      <PriorityPill priority={card.priority} />
                    </div>

                    {menu === card.key && (
                      <div className="kb-menu" onClick={e => e.stopPropagation()}>
                        <button className="kb-menu-row" onClick={() => { setMenu(null); openCard(card); }}><Icon name="eye" size={15} /> Open {card.kind === "ingredient" ? "ingredient" : "recipe"}</button>
                        <button className="kb-menu-row" onClick={() => { setMenu(null); setAssignModal({ col: c.id, editKey: card.key }); }}><Icon name="user-cog" size={15} /> Reassign</button>
                        <div className="kb-menu-sep" />
                        <div className="kb-menu-label">Move to stage</div>
                        <div className="kb-sub">
                          {cols.filter(x => x.id !== c.id).map(x => <button key={x.id} onClick={() => { setMenu(null); moveCard(card.key, c.id, x.id); }}>{x.title}</button>)}
                        </div>
                        <div className="kb-menu-label">Priority</div>
                        <div className="kb-sub">
                          {["high", "medium", "low"].map(p => <button key={p} className={card.priority === p ? "on" : ""} onClick={() => { patchCard(card.key, { priority: p }); setMenu(null); toast("Priority set to " + p); }} style={{ textTransform: "capitalize" }}>{p}</button>)}
                        </div>
                        <div className="kb-menu-sep" />
                        <button className="kb-menu-row" onClick={() => { patchCard(card.key, { blocked: !card.blocked }); setMenu(null); toast(card.blocked ? "Unblocked" : "Marked blocked"); }}>
                          <Icon name={card.blocked ? "circle-check" : "ban"} size={15} /> {card.blocked ? "Unblock" : "Mark blocked"}
                        </button>
                        <button className="kb-menu-row danger" onClick={() => { setMenu(null); removeCard(card.key); }}><Icon name="trash-2" size={15} /> Remove</button>
                      </div>
                    )}
                  </div>
                ))}
              </div>
            </div>
          );
        })}
      </div>)}

      {view === "list" && <KbListView cards={allCards.filter(passFilter)} cols={cols} workload={workload} openCard={openCard} onReassign={(card) => setAssignModal({ col: card.col, editKey: card.key })} />}
      {view === "table" && <KbTableView cards={allCards.filter(passFilter)} cols={cols} openCard={openCard} onReassign={(card) => setAssignModal({ col: card.col, editKey: card.key })} />}
      {view === "calendar" && <KbCalendarView cards={allCards.filter(passFilter)} cols={cols} openCard={openCard} />}
      {view === "timeline" && <KbTimelineView cards={allCards.filter(passFilter)} cols={cols} openCard={openCard} />}
      {view === "queue" && <KbQueueView cards={allCards.filter(passFilter)} cols={cols} openCard={openCard} onReassign={(card) => setAssignModal({ col: card.col, editKey: card.key })} />}
      {view === "workload" && <KbWorkloadView cards={allCards} cols={cols} loadCfg={loadCfg} finalIds={finalIds} assignees={ASSIGNEES} workload={workload} openCard={openCard} onReassign={(card) => setAssignModal({ col: card.col, editKey: card.key })} onAssign={() => setAssignModal({ col: intakeId })} onAssignTo={(a) => setAssignModal({ col: intakeId, presetInitials: a.initials })} />}
      </div>
      </div>

      {stageEditor && <KbStageEditor cols={cols} board={board} boardName={active.name} onClose={() => setStageEditor(false)} onSave={(nc) => { applyCols(nc); setStageEditor(false); toast("Stages updated"); }} />}

      {boardModal && <KbBoardModal mode={boardModal.mode} project={boardModal.project}
        onClose={() => setBoardModal(null)}
        onSave={(name) => { if (boardModal.mode === "rename") { renameBoard(boardModal.project.id, name); toast("Board renamed"); } else { createBoard(name); } setBoardModal(null); }} />}

      {assignModal && (
        <AssignWorkModal
          col={assignModal.col}
          currentRole={role}
          workload={workload}
          presetInitials={assignModal.presetInitials}
          cols={cols}
          loadCfg={loadCfg}
          editCard={assignModal.editKey ? Object.values(board).flat().find(c => c.key === assignModal.editKey) : null}
          assignees={ASSIGNEES}
          onClose={() => setAssignModal(null)}
          onAssign={(payload) => {
            if (assignModal.editKey) {
              const a = ASSIGNEES.find(x => x.id === payload.assigneeId);
              patchCard(assignModal.editKey, { assignee: a ? { name: a.name, initials: a.initials, color: a.color } : null, priority: payload.priority, blocked: payload.blocked, task: payload.task, note: payload.note });
              toast("Assignment updated");
            } else {
              addCard(payload);
            }
            setAssignModal(null);
          }}
        />
      )}
    </div>
  );
}

function KbStageBadge({ col, cols }) {
  const list = cols || DEFAULT_COLS;
  const c = list.find(x => x.id === col) || list[0];
  return <span className="kb-stage-badge" style={{ color: c.color }}><span className="dot" style={{ background: c.color }} /> {c.title}</span>;
}

function KbListView({ cards, cols, workload, openCard, onReassign }) {
  const order = {}; (cols || DEFAULT_COLS).forEach((c, i) => { order[c.id] = i; });
  const sorted = [...cards].sort((a, b) => ((order[a.col] ?? 99) - (order[b.col] ?? 99)) || a.name.localeCompare(b.name));
  return (
    <div style={{ overflowX: "auto", padding: "4px 2px" }}>
      <div>
        <table className="kb-table" style={{ minWidth: 860 }}>
          <thead><tr>
            <th>Item</th><th>Task</th><th>Assignee</th><th>Stage</th><th>Priority</th><th style={{ textAlign: "right" }}>Action</th>
          </tr></thead>
          <tbody>
            {sorted.map(card => (
              <tr key={card.key} style={{ cursor: "pointer" }} onClick={() => openCard(card)}>
                <td>
                  <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                    {card.kind === "ingredient"
                      ? <div className="kb-thumb kb-thumb-ing" style={{ width: 32, height: 32 }}><Icon name="leaf" size={15} /></div>
                      : <div className="kb-thumb" style={{ width: 32, height: 32, backgroundImage: `url("${card.cover}")` }} />}
                    <div style={{ minWidth: 0 }}>
                      <div style={{ fontWeight: 700, fontSize: 13 }}>{card.name} {card.blocked && <span className="kb-blocked-tag" style={{ marginLeft: 4 }}><Icon name="ban" size={10} /> Blocked</span>}</div>
                      <div className="muted" style={{ fontSize: 11 }}><span className={`kb-kind ${card.kind}`}>{card.kind === "ingredient" ? "Ingredient" : "Recipe"}</span> {card.sub}</div>
                    </div>
                  </div>
                </td>
                <td>{card.task ? <span className="kb-task" style={{ marginTop: 0 }}><Icon name="clipboard-list" size={11} /> {card.task}</span> : <span className="muted">—</span>}</td>
                <td>
                  {card.assignee
                    ? <span className="kb-assignee"><span className="kb-av" style={{ background: card.assignee.color, width: 24, height: 24 }}>{card.assignee.initials}</span> <span style={{ fontSize: 12.5, fontWeight: 600 }}>{card.assignee.name}</span></span>
                    : <span className="muted" style={{ fontStyle: "italic", fontSize: 12.5 }}>Unassigned</span>}
                </td>
                <td><KbStageBadge col={card.col} cols={cols} /></td>
                <td><PriorityPill priority={card.priority} /></td>
                <td style={{ textAlign: "right" }} onClick={e => e.stopPropagation()}>
                  <button className="btn sm secondary" onClick={() => onReassign(card)}><Icon name="user-cog" size={14} /> Reassign</button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
        {sorted.length === 0 && <div className="empty"><div className="icon"><Icon name="inbox" size={24} /></div><h3>Nothing here</h3><p>No items match your filter.</p></div>}
      </div>
    </div>
  );
}

/* Synthesize a stable due-day (Jun) per card so date-based views work */
function kbDueDay(card) { let h = 0; const s = card.key || card.name || ""; for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0; return 9 + (h % 21); }
const KB_PRIO_RANK = { high: 0, medium: 1, low: 2 };

/* ── Calendar view ── */
function KbCalendarView({ cards, cols, openCard }) {
  const byDay = {}; cards.forEach(c => { const d = kbDueDay(c); (byDay[d] = byDay[d] || []).push(c); });
  const colOf = (id) => (cols || DEFAULT_COLS).find(x => x.id === id) || {};
  const first = new Date(2026, 5, 1); const startDow = (first.getDay() + 6) % 7; const days = 30;
  const cells = []; for (let i = 0; i < startDow; i++) cells.push(null); for (let d = 1; d <= days; d++) cells.push(d);
  const today = 12;
  return (
    <div className="kb-cal card">
      <div className="kb-cal-head"><strong>June 2026</strong><span className="muted">{cards.length} assignments by due date</span></div>
      <div className="kb-cal-dow">{["Mon","Tue","Wed","Thu","Fri","Sat","Sun"].map(d=><span key={d}>{d}</span>)}</div>
      <div className="kb-cal-grid">
        {cells.map((d, i) => (
          <div key={i} className={`kb-cal-cell ${!d?"empty":""} ${d===today?"today":""}`}>
            {d && <><span className="kb-cal-day">{d}</span>
              <div className="kb-cal-events">
                {(byDay[d]||[]).slice(0,3).map(c => (
                  <button key={c.key} className="kb-cal-ev" style={{ borderLeftColor: colOf(c.col).color || "#5fa92f" }} onClick={()=>openCard(c)} title={`${c.name} · ${c.task||""}`}>{c.name}</button>
                ))}
                {(byDay[d]||[]).length>3 && <span className="kb-cal-more">+{byDay[d].length-3} more</span>}
              </div></>}
          </div>
        ))}
      </div>
    </div>
  );
}

/* ── Timeline view (bars over the month) ── */
function KbTimelineView({ cards, cols, openCard }) {
  const colOf = (id) => (cols || DEFAULT_COLS).find(x => x.id === id) || {};
  const rows = [...cards].sort((a,b)=>kbDueDay(a)-kbDueDay(b));
  const span = 22, start = 9;
  return (
    <div className="kb-tl card">
      <div className="kb-tl-head"><span className="kb-tl-label-h">Assignment</span><div className="kb-tl-scale">{[9,13,17,21,25,29].map(d=><span key={d}>Jun {d}</span>)}</div></div>
      {rows.map(c => { const due = kbDueDay(c); const startD = Math.max(start, due-4); const left = ((startD-start)/span)*100; const width = ((due-startD+1)/span)*100;
        return (
          <div key={c.key} className="kb-tl-row" onClick={()=>openCard(c)}>
            <span className="kb-tl-label" title={c.name}>{c.name}</span>
            <div className="kb-tl-track">
              <div className="kb-tl-bar" style={{ left:`${left}%`, width:`${width}%`, background: (colOf(c.col).color||"#5fa92f") }}>
                <span>{c.task || colOf(c.col).title || ""}</span>
              </div>
            </div>
          </div>
        ); })}
      {rows.length===0 && <div className="empty"><div className="icon"><Icon name="calendar" size={24}/></div><h3>Nothing scheduled</h3></div>}
    </div>
  );
}

/* ── Table view (sortable spreadsheet) ── */
function KbTableView({ cards, cols, openCard, onReassign }) {
  const [sort, setSort] = React.useState({ k: "due", dir: 1 });
  const colOf = (id) => (cols || DEFAULT_COLS).find(x => x.id === id) || {};
  const val = (c, k) => k==="due"?kbDueDay(c): k==="priority"?KB_PRIO_RANK[c.priority]: k==="assignee"?(c.assignee?.name||"~"): k==="stage"?(colOf(c.col).title||""): (c[k]||"").toString().toLowerCase();
  const sorted = [...cards].sort((a,b)=>{ const x=val(a,sort.k), y=val(b,sort.k); return (x<y?-1:x>y?1:0)*sort.dir; });
  const th = (k, label) => <th onClick={()=>setSort(s=>({k, dir: s.k===k?-s.dir:1}))} style={{cursor:"pointer"}}>{label} {sort.k===k && <Icon name={sort.dir>0?"chevron-up":"chevron-down"} size={12}/>}</th>;
  return (
    <div className="card" style={{ overflow:"auto" }}>
      <table className="kb-table">
        <thead><tr>{th("name","Assignment")}{th("stage","Status")}{th("assignee","Owner")}{th("due","Due")}{th("priority","Priority")}<th>Category</th><th style={{textAlign:"right"}}>Action</th></tr></thead>
        <tbody>
          {sorted.map(c => (
            <tr key={c.key} onClick={()=>openCard(c)} style={{cursor:"pointer"}}>
              <td style={{fontWeight:700}}>{c.name}</td>
              <td><KbStageBadge col={c.col} cols={cols}/></td>
              <td>{c.assignee? <span className="kb-assignee"><span className="kb-av" style={{background:c.assignee.color,width:22,height:22,fontSize:9}}>{c.assignee.initials}</span> {c.assignee.name}</span> : <span className="muted">Unassigned</span>}</td>
              <td className="cc-num">Jun {kbDueDay(c)}</td>
              <td><PriorityPill priority={c.priority}/></td>
              <td className="muted" style={{fontSize:12}}>{(c.sub||"").split("·").pop()}</td>
              <td style={{textAlign:"right"}} onClick={e=>e.stopPropagation()}><button className="btn sm secondary" onClick={()=>onReassign(c)}><Icon name="user-cog" size={13}/> Reassign</button></td>
            </tr>
          ))}
        </tbody>
      </table>
      {sorted.length===0 && <div className="empty"><div className="icon"><Icon name="table" size={24}/></div><h3>No rows</h3></div>}
    </div>
  );
}

/* ── Queue view (grouped by action required) ── */
function KbQueueView({ cards, cols, openCard, onReassign }) {
  const finals = (cols||DEFAULT_COLS).filter(c=>c.done).map(c=>c.id);
  const today = 12;
  const groups = [
    { id:"needs", t:"Needs Action", ic:"circle-dot", tone:"amber", fn: c=>!c.assignee && !finals.includes(c.col) },
    { id:"today", t:"Due Today", ic:"calendar-clock", tone:"red", fn: c=>kbDueDay(c)<=today && !finals.includes(c.col) },
    { id:"blocked", t:"Blocked", ic:"ban", tone:"red", fn: c=>c.blocked },
    { id:"review", t:"Waiting on Review", ic:"eye", tone:"cobalt", fn: c=>/review/i.test((colOfTitle(c,cols))) && !finals.includes(c.col) },
    { id:"done", t:"Done", ic:"check-circle-2", tone:"green", fn: c=>finals.includes(c.col) },
  ];
  function colOfTitle(c, cols){ const x=(cols||DEFAULT_COLS).find(k=>k.id===c.col); return x?x.title:""; }
  const used = new Set();
  return (
    <div className="kb-queue">
      {groups.map(g => { const items = cards.filter(c => g.fn(c)); 
        return (
        <div key={g.id} className="kb-queue-group">
          <div className="kb-queue-h"><span className={`kb-queue-ic ${g.tone}`}><Icon name={g.ic} size={14}/></span><strong>{g.t}</strong><span className="kb-queue-n">{items.length}</span></div>
          <div className="kb-queue-items">
            {items.length===0 && <div className="kb-queue-empty">Nothing here</div>}
            {items.map(c => (
              <div key={c.key+g.id} className="kb-queue-card" onClick={()=>openCard(c)}>
                <div style={{flex:1,minWidth:0}}>
                  <div className="kb-queue-name">{c.name}</div>
                  <div className="kb-queue-meta">{c.task||colOfTitle(c,cols)} · Jun {kbDueDay(c)}</div>
                </div>
                <PriorityPill priority={c.priority}/>
                {c.assignee ? <span className="kb-av" style={{background:c.assignee.color,width:24,height:24,fontSize:9}}>{c.assignee.initials}</span>
                  : <button className="btn sm secondary" onClick={e=>{e.stopPropagation();onReassign(c);}}>Assign</button>}
              </div>
            ))}
          </div>
        </div>
      ); })}
    </div>
  );
}

function KbWorkloadView({ cards, cols, loadCfg, finalIds, assignees, workload, openCard, onReassign, onAssign, onAssignTo }) {
  const cfg = loadCfg || DEFAULT_LOAD_CONFIG;
  const finals = finalIds || (cols || DEFAULT_COLS).filter(c => c.done).map(c => c.id);
  const active = cards.filter(c => !finals.includes(c.col));
  const pts = (c) => kbCardPoints(c, cfg);
  const max = Math.max(1, cfg.overMin, ...assignees.map(a => workload[a.initials] || 0));
  const byInit = {};
  active.forEach(c => { if (c.assignee) (byInit[c.assignee.initials] = byInit[c.assignee.initials] || []).push(c); });
  const unassigned = active.filter(c => !c.assignee);
  const rows = [...assignees].sort((a, b) => (workload[b.initials] || 0) - (workload[a.initials] || 0));
  const totalPts = Object.values(workload).reduce((n, v) => n + v, 0);
  const avg = (totalPts / (assignees.length || 1));
  const overN = assignees.filter(a => (workload[a.initials] || 0) >= cfg.overMin).length;
  const underN = assignees.filter(a => (workload[a.initials] || 0) <= cfg.underMax).length;
  const metricLabel = cfg.metric === "weighted" ? "points" : "active";

  return (
    <div className="kb-load-wrap">
      <div className="kb-load-summary card">
        <div><div className="kb-load-summary-v">{active.length}</div><div className="kb-load-summary-l">Active items</div></div>
        <div><div className="kb-load-summary-v">{avg.toFixed(1)}</div><div className="kb-load-summary-l">Avg {metricLabel}/person</div></div>
        <div><div className="kb-load-summary-v" style={{ color: overN ? "var(--error-600)" : "var(--green-700)" }}>{overN}</div><div className="kb-load-summary-l">Overloaded (≥{cfg.overMin})</div></div>
        <div><div className="kb-load-summary-v" style={{ color: "var(--green-700)" }}>{underN}</div><div className="kb-load-summary-l">Has capacity (≤{cfg.underMax})</div></div>
        <div><div className="kb-load-summary-v" style={{ color: unassigned.length ? "var(--warning-600)" : "var(--green-700)" }}>{unassigned.length}</div><div className="kb-load-summary-l">Unassigned</div></div>
      </div>

      <div className="kb-load-legend">
        <span><i className="kb-load-sw idle" /> Available (0)</span>
        <span><i className="kb-load-sw under" /> Light (≤{cfg.underMax})</span>
        <span><i className="kb-load-sw ok" /> Balanced</span>
        <span><i className="kb-load-sw high" /> Overloaded (≥{cfg.overMin})</span>
        <span className="kb-load-measuring">Measuring: {cfg.metric === "weighted" ? "weighted by priority" : "item count"}{cfg.includeFinal ? " · incl. final stage" : ""}</span>
      </div>

      {unassigned.length > 0 && (
        <div className="card kb-load-person" style={{ borderColor: "var(--warning-500)", background: "var(--warning-50)" }}>
          <div className="kb-load-head">
            <span className="kb-assignee"><span className="kb-av" style={{ background: "var(--gray-300)", color: "var(--gray-600)" }}><Icon name="user" size={14} /></span> <strong>Unassigned</strong></span>
            <span className="pill warning" style={{ fontSize: 11 }}>{unassigned.length} need an owner</span>
          </div>
          <div className="kb-load-items">
            {unassigned.map(c => <button key={c.key} className="kb-load-chip" onClick={() => onReassign(c)} title="Assign owner">{c.name} <Icon name="user-plus" size={11} /></button>)}
          </div>
        </div>
      )}

      {rows.map(a => {
        const items = byInit[a.initials] || [];
        const n = workload[a.initials] || 0;
        const loadClass = kbClassify(n, cfg);
        const statusText = loadClass === "idle" ? "Available" : loadClass === "under" ? "Light load" : loadClass === "high" ? "Overloaded" : "Balanced";
        const statusIcon = loadClass === "high" ? "alert-triangle" : loadClass === "idle" ? "circle-check" : loadClass === "under" ? "trending-down" : "check";
        return (
          <div key={a.id} className={`kb-person ${loadClass}`}>
            <div className="kb-person-l">
              <span className="kb-av" style={{ background: a.color }}>{a.initials}</span>
              <div className="kb-person-id">
                <div className="kb-person-name">{a.name}</div>
                <div className="kb-person-role">{ROLE_LABEL[a.role] || a.role}</div>
              </div>
            </div>
            <div className="kb-person-mid">
              <div className="kb-person-barrow">
                <span className="kb-load-bar"><i className={loadClass} style={{ width: `${Math.min(100, (n / max) * 100)}%` }} /></span>
                <span className="kb-person-num">{n}<span>/{cfg.hardCap}</span></span>
              </div>
              <div className="kb-person-items">
                {items.length === 0
                  ? <span className="kb-person-empty">No active work, ready for new assignments.</span>
                  : items.map(c => <button key={c.key} className="kb-load-chip" onClick={() => openCard(c)} title={c.task || c.name}>{c.kind === "ingredient" ? <Icon name="leaf" size={11} /> : <Icon name="utensils-crossed" size={11} />} {c.name}</button>)}
              </div>
            </div>
            <div className="kb-person-r">
              <span className={`kb-load-count ${loadClass}`}><Icon name={statusIcon} size={12} /> {statusText}</span>
              <button className="btn primary sm" onClick={() => onAssignTo && onAssignTo(a)}><Icon name="user-plus" size={13} /> Assign</button>
            </div>
          </div>
        );
      })}
    </div>
  );
}

/* Stage editor, add / rename / recolor / reorder / delete board columns */
function KbStageEditor({ cols, board, boardName, onClose, onSave }) {
  const [list, setList] = React.useState(() => cols.map(c => ({ ...c })));
  const [colorOpen, setColorOpen] = React.useState(null);
  const [drag, setDrag] = React.useState(null);
  const [over, setOver] = React.useState(null);
  const count = (id) => (board[id] || []).length;
  const reorder = (from, to) => setList(l => { if (from === to || to == null) return l; const n = [...l]; const [m] = n.splice(from, 1); n.splice(to, 0, m); return n; });
  const slug = (s) => (s || "stage").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "stage";
  const patch = (i, p) => setList(l => l.map((c, j) => j === i ? { ...c, ...p } : c));
  const move = (i, d) => setList(l => { const j = i + d; if (j < 0 || j >= l.length) return l; const n = [...l]; [n[i], n[j]] = [n[j], n[i]]; return n; });
  const del = (i) => setList(l => l.length <= 1 ? l : l.filter((_, j) => j !== i));
  const add = () => setList(l => [...l, { id: "stage-" + Date.now(), title: "New stage", color: KB_COL_PALETTE[l.length % KB_COL_PALETTE.length] }]);
  const setDone = (i) => setList(l => l.map((c, j) => ({ ...c, done: j === i ? !c.done : c.done })));
  const save = () => {
    const seen = {};
    const finalized = list.map(c => {
      let id = c.id && !c.id.startsWith("stage-") ? c.id : slug(c.title);
      while (seen[id]) id = id + "-" + Math.floor(Math.random() * 99);
      seen[id] = 1;
      return { id, title: c.title.trim() || "Untitled", color: c.color, done: !!c.done };
    });
    onSave(finalized);
  };

  return (
    <Modal open onClose={onClose} width={600} title="Customize stages"
      subtitle={`Define the workflow columns for “${boardName || "this board"}”. Changes apply to the Board, List, and Workload views.`}
      footer={<><button className="btn ghost" onClick={onClose}>Cancel</button><button className="btn primary" onClick={save}><Icon name="check" size={15} /> Save stages</button></>}>
      <div className="kbs-head">
        <span style={{ width: 30 }} />
        <span>Stage</span>
        <span style={{ textAlign: "center" }}>Final</span>
        <span style={{ textAlign: "center" }}>Items</span>
        <span />
      </div>
      <div className="kb-stage-list">
        {list.map((c, i) => (
          <div key={i} className={`kbs-row ${drag === i ? "dragging" : ""} ${over === i && drag !== i ? "dropover" : ""}`}
            draggable
            onDragStart={(e) => { setDrag(i); e.dataTransfer.effectAllowed = "move"; }}
            onDragOver={(e) => { e.preventDefault(); if (over !== i) setOver(i); }}
            onDrop={(e) => { e.preventDefault(); reorder(drag, i); setDrag(null); setOver(null); }}
            onDragEnd={() => { setDrag(null); setOver(null); }}>
            <div className="kbs-reorder">
              <span className="kbs-grip" title="Drag to reorder"><Icon name="grip-vertical" size={15} /></span>
              <button disabled={i === 0} onClick={() => move(i, -1)} title="Move up"><Icon name="chevron-up" size={13} /></button>
              <button disabled={i === list.length - 1} onClick={() => move(i, 1)} title="Move down"><Icon name="chevron-down" size={13} /></button>
            </div>
            <div className="kbs-main">
              <button type="button" className="kbs-swatch" style={{ background: c.color }} onClick={() => setColorOpen(colorOpen === i ? null : i)} title="Change color">
                {colorOpen === i && (
                  <div className="kbs-palette" onClick={e => e.stopPropagation()}>
                    {KB_COL_PALETTE.map(col => <button key={col} type="button" className={c.color === col ? "on" : ""} style={{ background: col }} onClick={() => { patch(i, { color: col }); setColorOpen(null); }} />)}
                  </div>
                )}
              </button>
              <input className="kbs-name" value={c.title} onChange={e => patch(i, { title: e.target.value })} placeholder="Stage name" />
              {i === 0 && <span className="kbs-intake" title="New / unassigned work lands here">Intake</span>}
            </div>
            <button className={`kbs-final ${c.done ? "on" : ""}`} onClick={() => setDone(i)} title="Final / published stage (excluded from workload by default)"><Icon name="flag" size={13} /> {c.done ? "Final" : "Set final"}</button>
            <span className="kbs-count">{count(c.id)}</span>
            <button className="kbs-del" disabled={list.length <= 1} onClick={() => del(i)} title={count(c.id) ? "Cards here move to the first stage" : "Delete stage"}><Icon name="trash-2" size={15} /></button>
          </div>
        ))}
      </div>
      <button className="kbs-add" onClick={add}><Icon name="plus" size={15} /> Add stage</button>
      <div className="kb-stage-hint"><Icon name="info" size={13} /> The first stage is the intake column for new/unassigned work. Deleting a stage moves its items to the first stage.</div>
    </Modal>
  );
}

/* Create / rename a board */
function KbBoardModal({ mode, project, onClose, onSave }) {
  const [name, setName] = React.useState(mode === "rename" && project ? project.name : "");
  return (
    <Modal open onClose={onClose} width={460}
      title={mode === "rename" ? "Rename board" : "New board"}
      subtitle={mode === "rename" ? "Give this board a clear, descriptive name." : "Create a separate board with its own stages and cards, e.g. a menu launch, a seasonal campaign, or a compliance sweep."}
      footer={<><button className="btn ghost" onClick={onClose}>Cancel</button><button className="btn primary" disabled={!name.trim()} onClick={() => onSave(name.trim())}><Icon name={mode === "rename" ? "check" : "plus"} size={15} /> {mode === "rename" ? "Save" : "Create board"}</button></>}>
      <div className="field"><label>Board name</label><input className="input" autoFocus value={name} onChange={e => setName(e.target.value)} placeholder="e.g. Spring Menu Launch" onKeyDown={e => { if (e.key === "Enter" && name.trim()) onSave(name.trim()); }} /></div>
      {mode === "new" && <div className="kb-stage-hint" style={{ marginTop: 4 }}><Icon name="info" size={13} /> New boards start with the default stages. Customize them anytime from “Stages”.</div>}
    </Modal>
  );
}

// Searchable single-select used in the assignment task builder (sections & fields).
// Native <select> can't be searched, this combo can, and renders at 14px.
(function injectCaComboStyles() {
  if (document.getElementById("ca-combo-styles")) return;
  const css = `
  .ca-combo { position: relative; width: 100%; }
  .ca-combo-trigger { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; border: 1.5px solid var(--gray-200); border-radius: 8px; padding: 9px 11px; font-family: inherit; font-size: 14px; font-weight: 700; background: #fff; color: var(--text-primary); cursor: pointer; text-align: left; }
  .ca-combo-trigger:hover { border-color: var(--green-400); }
  .ca-combo-trigger.open { border-color: var(--green-600); box-shadow: 0 0 0 3px rgba(59,124,15,.1); }
  .ca-combo-val { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .ca-combo-trigger > svg { color: var(--gray-400); flex-shrink: 0; }
  .ca-combo-pop { position: absolute; top: calc(100% + 5px); left: 0; right: 0; z-index: 70; background: #fff; border: 1px solid var(--gray-200); border-radius: 10px; box-shadow: 0 16px 40px -12px rgba(14,22,18,.32); overflow: hidden; }
  .ca-combo-search { display: flex; align-items: center; gap: 8px; padding: 9px 11px; border-bottom: 1px solid var(--gray-100); }
  .ca-combo-search > svg { color: var(--gray-400); flex-shrink: 0; }
  .ca-combo-search input { flex: 1; min-width: 0; border: 0; outline: 0; font-family: inherit; font-size: 14px; background: transparent; color: var(--text-primary); }
  .ca-combo-list { max-height: 240px; overflow-y: auto; overflow-x: hidden; padding: 5px; }
  .ca-combo-opt { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; border: 0; background: transparent; border-radius: 7px; padding: 9px 10px; font-family: inherit; font-size: 14px; font-weight: 600; color: var(--text-primary); text-align: left; cursor: pointer; }
  .ca-combo-opt.hi { background: var(--gray-50); }
  .ca-combo-opt.sel { color: var(--green-700); font-weight: 700; }
  .ca-combo-opt > svg { color: var(--green-700); flex-shrink: 0; }
  .ca-combo-empty { padding: 12px; font-size: 13px; color: var(--gray-500); text-align: center; }
  /* Bump the rest of the task card to 14px to match */
  .cu-task-card .cu-sub-note { font-size: 14px; }
  .cu-task-card .cu-sub-cur { font-size: 13px; }
  .app[data-theme="dark"] .ca-combo-trigger, .app[data-theme="dark"] .ca-combo-pop, .app[data-theme="dark"] .ca-combo-search input, .app[data-theme="dark"] .ca-combo-opt { background: #11161B; color: #E5E9F0; }
  .app[data-theme="dark"] .ca-combo-pop { border-color: #1F242F; }
  `;
  const s = document.createElement("style"); s.id = "ca-combo-styles"; s.textContent = css; document.head.appendChild(s);
})();

function CaCombo({ options, value, onChange, placeholder = "Select…" }) {
  const [q, setQ] = React.useState("");
  const [open, setOpen] = React.useState(false);
  const [hi, setHi] = React.useState(0);
  const ref = React.useRef(null);
  React.useEffect(() => {
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) { setOpen(false); setQ(""); } };
    document.addEventListener("mousedown", onDoc); return () => document.removeEventListener("mousedown", onDoc);
  }, []);
  const filtered = (open && q.trim() ? options.filter((o) => o.toLowerCase().includes(q.toLowerCase())) : options);
  const choose = (o) => { onChange(o); setQ(""); setOpen(false); };
  return (
    <div className="ca-combo" ref={ref}>
      <button type="button" className={`ca-combo-trigger ${open ? "open" : ""}`} onClick={() => { setOpen((o) => !o); setQ(""); setHi(0); }}>
        <span className="ca-combo-val">{value || placeholder}</span>
        <Icon name={open ? "chevron-up" : "chevron-down"} size={16} />
      </button>
      {open && (
        <div className="ca-combo-pop">
          <div className="ca-combo-search">
            <Icon name="search" size={15} />
            <input autoFocus value={q} placeholder="Search…"
              onChange={(e) => { setQ(e.target.value); setHi(0); }}
              onKeyDown={(e) => {
                if (e.key === "ArrowDown") { e.preventDefault(); setHi((h) => Math.min(h + 1, filtered.length - 1)); }
                else if (e.key === "ArrowUp") { e.preventDefault(); setHi((h) => Math.max(h - 1, 0)); }
                else if (e.key === "Enter") { e.preventDefault(); if (filtered[hi]) choose(filtered[hi]); }
                else if (e.key === "Escape") { setOpen(false); }
              }} />
          </div>
          <div className="ca-combo-list">
            {filtered.length === 0 && <div className="ca-combo-empty">No matches for “{q}”</div>}
            {filtered.map((o, idx) => (
              <button type="button" key={o} className={`ca-combo-opt ${o === value ? "sel" : ""} ${idx === hi ? "hi" : ""}`}
                onMouseEnter={() => setHi(idx)} onClick={() => choose(o)}>
                <span style={{ minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{o}</span>
                {o === value && <Icon name="check" size={15} />}
              </button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

function KbCombo({ items, value, onSelect, getLabel, getSub, placeholder }) {
  const sel = items.find(i => i.id === value);
  const [q, setQ] = React.useState("");
  const [open, setOpen] = React.useState(false);
  const [hi, setHi] = React.useState(0);
  const ref = React.useRef(null);
  React.useEffect(() => {
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", onDoc); return () => document.removeEventListener("mousedown", onDoc);
  }, []);
  const display = open ? q : (sel ? getLabel(sel) : "");
  const filtered = (open && q.trim()
    ? items.filter(i => (getLabel(i) + " " + (getSub ? getSub(i) : "")).toLowerCase().includes(q.toLowerCase()))
    : items).slice(0, 60);
  const choose = (i) => { onSelect(i.id); setQ(""); setOpen(false); };
  return (
    <div className="kb-combo" ref={ref}>
      <div className={`kb-combo-input ${open ? "open" : ""}`}>
        <Icon name="search" size={15} />
        <input value={display} placeholder={placeholder}
          onChange={e => { setQ(e.target.value); setOpen(true); setHi(0); }}
          onFocus={() => { setQ(""); setOpen(true); }}
          onKeyDown={e => {
            if (e.key === "ArrowDown") { e.preventDefault(); setOpen(true); setHi(h => Math.min(h + 1, filtered.length - 1)); }
            else if (e.key === "ArrowUp") { e.preventDefault(); setHi(h => Math.max(h - 1, 0)); }
            else if (e.key === "Enter") { e.preventDefault(); if (filtered[hi]) choose(filtered[hi]); }
            else if (e.key === "Escape") { setOpen(false); }
          }} />
        <Icon name={open ? "chevron-up" : "chevron-down"} size={15} style={{ color: "var(--gray-400)" }} />
      </div>
      {open && (
        <div className="kb-combo-list">
          {filtered.length === 0 && <div className="kb-combo-empty">No matches for “{q}”</div>}
          {filtered.map((i, idx) => (
            <button type="button" key={i.id} className={`kb-combo-opt ${i.id === value ? "sel" : ""} ${idx === hi ? "hi" : ""}`}
              onMouseEnter={() => setHi(idx)} onClick={() => choose(i)}>
              <span style={{ minWidth: 0, flex: 1 }}>
                <span className="kb-combo-opt-l">{getLabel(i)}</span>
                {getSub && <span className="kb-combo-opt-s">{getSub(i)}</span>}
              </span>
              {i.id === value && <Icon name="check" size={15} style={{ color: "var(--green-700)", flexShrink: 0 }} />}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function AssignWorkModal({ col, currentRole, workload = {}, presetInitials, cols, loadCfg, editCard, assignees, onClose, onAssign }) {
  const editing = !!editCard;
  const cfg = loadCfg || DEFAULT_LOAD_CONFIG;
  const stageCols = cols || DEFAULT_COLS;
  const [kind, setKind] = React.useState(editCard ? editCard.kind : "recipe");
  const recipeOptions = RECIPES;
  const ingredientOptions = kbIngredients();
  const [recipeId, setRecipeId] = React.useState(editCard && editCard.kind === "recipe" ? editCard.rid : (RECIPES[9] ? RECIPES[9].id : RECIPES[0].id));
  const [ingredientId, setIngredientId] = React.useState(editCard && editCard.kind === "ingredient" ? editCard.rid : (ingredientOptions[0] && ingredientOptions[0].id));
  const [stage, setStage] = React.useState(col || (stageCols[0] && stageCols[0].id));
  const [priority, setPriority] = React.useState(editCard ? editCard.priority : "medium");
  const [due, setDue] = React.useState("");
  const [blocked, setBlocked] = React.useState(editCard ? editCard.blocked : false);
  const [task, setTask] = React.useState(editCard ? editCard.task : (KB_TASKS.recipe[0].label));
  const [note, setNote] = React.useState(editCard ? editCard.note : KB_TASKS.recipe[0].look);
  const [noteTouched, setNoteTouched] = React.useState(editing);
  // Card-based task builder (matches Create Assignment sidebar cards), catalog built
  // from the actual selected item so exact steps/ingredients/notes are targetable.
  const qaItem = kind === "recipe" ? (RECIPES.find(r => r.id === recipeId) || RECIPES[0]) : (ingredientOptions.find(i => i.id === ingredientId) || ingredientOptions[0]);
  const { catalog: qaCatalog, cur: qaCur } = React.useMemo(() => caCatalogFor(kind, qaItem), [kind, qaItem]);
  const qaSections = Object.keys(qaCatalog);
  const [rows, setRows] = React.useState(() => { const sec = Object.keys(CA_RECIPE_FIELDS)[0]; const [field, type] = CA_RECIPE_FIELDS[sec][0]; return [{ id: "q0", sec, field, type, note: "" }]; });
  const qaAddRow = () => { const sec = qaSections[0]; const [field, type] = qaCatalog[sec][0]; setRows(r => [...r, { id: "q" + Date.now() + r.length, sec, field, type, note: "" }]); };
  const qaPatch = (i, p) => setRows(rs => rs.map((r, j) => { if (j !== i) return r; const next = { ...r, ...p }; if (p.sec) { const [field, type] = qaCatalog[p.sec][0]; next.field = field; next.type = type; } if (p.field) { const ft = qaCatalog[next.sec].find(([f]) => f === p.field); if (ft) next.type = ft[1]; } return next; }));
  const qaRemove = (i) => setRows(rs => rs.filter((_, j) => j !== i));
  React.useEffect(() => { const sec = qaSections[0]; const [field, type] = qaCatalog[sec][0]; setRows([{ id: "q0", sec, field, type, note: "" }]); }, [kind]);

  // Role-scoped assignees
  const allowedRoles = KB_ASSIGN_MATRIX[currentRole] || KB_ALL_ROLES;
  const ME_BY_ROLE = { "media-contributor": "SC", "dietitian": "EN", "compliance": "DL", "manager": "AB", "admin": "AB", "super-admin": "AB" };
  const meUser = assignees.find(a => a.initials === ME_BY_ROLE[currentRole]) || assignees[0];
  const allowedBase = assignees.filter(a => allowedRoles.includes(a.role));
  // Everyone can assign to themselves, even if their role scope wouldn't otherwise include them
  const allowed = meUser && !allowedBase.some(a => a.id === meUser.id) ? [meUser, ...allowedBase] : allowedBase;
  const wl = (a) => workload[a.initials] || 0;
  const maxWl = Math.max(1, cfg.overMin, ...allowed.map(wl));
  // Load-balancing suggestion: eligible person with the lightest current workload
  const suggestion = (cfg.autoSuggest && allowed.length) ? allowed.reduce((min, a) => wl(a) < wl(min) ? a : min, allowed[0]) : null;
  const [assigneeId, setAssigneeId] = React.useState(() => {
    if (editCard && editCard.assignee) { const m = allowed.find(a => a.initials === editCard.assignee.initials); if (m) return m.id; }
    if (presetInitials) { const m = allowed.find(a => a.initials === presetInitials); if (m) return m.id; }
    return suggestion ? suggestion.id : (allowed[0] && allowed[0].id);
  });
  const selected = allowed.find(a => a.id === assigneeId);
  // Capacity check against hard cap (excludes the card we're editing if it already counts)
  const selLoad = selected ? wl(selected) : 0;
  const wouldExceed = selected && !editing && cfg.enforce !== "off" && (selLoad + 1) > cfg.hardCap;
  const atCapNow = selected && cfg.enforce !== "off" && selLoad >= cfg.hardCap;
  const capHit = wouldExceed || atCapNow;

  const tasksFor = KB_TASKS[kind] || KB_TASKS.recipe;
  const item = kind === "recipe" ? (RECIPES.find(r => r.id === recipeId) || RECIPES[0]) : (ingredientOptions.find(i => i.id === ingredientId) || ingredientOptions[0]);

  const switchKind = (k) => {
    setKind(k);
    const t = (KB_TASKS[k] || [])[0];
    if (t) { setTask(t.label); if (!noteTouched) setNote(t.look); }
  };
  const pickTask = (label) => {
    setTask(label);
    const def = tasksFor.find(t => t.label === label);
    if (def && !noteTouched) setNote(def.look);
  };

  return (
    <Modal open onClose={onClose} width={560}
      title={editing ? "Reassign work" : "Quick assign work"}
      subtitle={editing ? "Update owner, task, priority, and what to look into." : "Pick a recipe or ingredient, build the task, and assign an owner."}
      footer={<>
        <button className="btn ghost" onClick={onClose}>Cancel</button>
        <button className="btn primary" disabled={!assigneeId || !item || (capHit && cfg.enforce === "block")} onClick={() => {
          const checklist = rows.map(r => ({ t: `Fix ${r.field}`, done: false, loc: { sec: r.sec, field: r.field, type: r.type, cur: qaCur[r.field] || "(see item)", hint: r.note || `Correct the ${r.field.toLowerCase()} in ${r.sec}.` } }));
          const derivedTask = rows.length > 1 ? `${rows.length} fixes` : (rows[0] ? `Fix ${rows[0].field}` : task);
          const derivedNote = rows[0] ? (rows[0].note || `${rows[0].sec} › ${rows[0].field}`) : note;
          onAssign({ col: stage, kind, item, assigneeId, priority, blocked, due, task: derivedTask, note: derivedNote, checklist });
        }}>
          <Icon name={editing ? "check" : "user-plus"} size={15} /> {editing ? "Save changes" : "Assign work"}
        </button>
      </>}>

      {/* Item type */}
      {!editing && (
        <div className="field">
          <label>What are you assigning?</label>
          <div className="kb-typeseg">
            <button type="button" className={kind === "recipe" ? "on" : ""} onClick={() => switchKind("recipe")}><Icon name="utensils-crossed" size={15} /> Recipe</button>
            <button type="button" className={kind === "ingredient" ? "on" : ""} onClick={() => switchKind("ingredient")}><Icon name="leaf" size={15} /> Ingredient</button>
          </div>
        </div>
      )}

      {/* Item picker, type to search */}
      {!editing ? (
        <div className="field">
          <label>{kind === "recipe" ? "Recipe" : "Ingredient"}</label>
          {kind === "recipe" ? (
            <KbCombo items={recipeOptions} value={recipeId} onSelect={setRecipeId}
              getLabel={r => r.name} getSub={r => `${r.cuisine} · ${r.category}`} placeholder="Search recipes by name or cuisine…" />
          ) : (
            <KbCombo items={ingredientOptions} value={ingredientId} onSelect={setIngredientId}
              getLabel={i => i.name} getSub={i => `${i.canonical} · ${i.category}`} placeholder="Search ingredients by name or category…" />
          )}
        </div>
      ) : (
        <div className="field">
          <label>{editCard.kind === "ingredient" ? "Ingredient" : "Recipe"}</label>
          <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 0" }}>
            {editCard.kind === "ingredient"
              ? <div className="kb-thumb kb-thumb-ing"><Icon name="leaf" size={18} /></div>
              : <div className="kb-thumb" style={{ backgroundImage: `url("${editCard.cover}")` }} />}
            <div><div style={{ fontWeight: 700 }}>{editCard.name}</div><div className="muted" style={{ fontSize: 12 }}>{editCard.sub}</div></div>
          </div>
        </div>
      )}

      {/* Task, card builder (matches Create Assignment) */}
      <div className="field">
        <label>Task <span className="muted" style={{ fontWeight: 500 }}>· target the exact step &amp; field to fix</span></label>
        <div className="qa-tasks">
          {rows.map((r, i) => {
            const secKey = qaCatalog[r.sec] ? r.sec : qaSections[0];
            const fieldOpts = qaCatalog[secKey] || [];
            const fieldVal = fieldOpts.some(([f]) => f === r.field) ? r.field : (fieldOpts[0] && fieldOpts[0][0]);
            return (
            <div key={r.id} className="cu-task-card">
              <div className="cu-task-top">
                <span className="cu-sub-ic"><Icon name="circle" size={14} /></span>
                <span className={`ca-type-badge t-${r.type}`}>{r.type}</span>
                {rows.length > 1 && <button type="button" className="cu-sub-del" onClick={() => qaRemove(i)} title="Remove" style={{ marginLeft: "auto" }}><Icon name="trash-2" size={13} /></button>}
              </div>
              <TaskFieldDrill catalog={qaCatalog} cur={qaCur} sec={secKey} field={fieldVal}
                onPick={(s, f, t) => setRows(rs => rs.map((row, j) => j === i ? { ...row, sec: s, field: f, type: t } : row))} />
              <input className="cu-sub-note" value={r.note} onChange={e => qaPatch(i, { note: e.target.value })} placeholder="What to look into…" />
            </div>
          );})}
          <button type="button" className="cu-add-task" onClick={qaAddRow}><Icon name="plus" size={15} /> Add task</button>
        </div>
      </div>

      {/* Assignee (role-scoped + workload-balanced) */}
      <div className="field">
        <label>Assign to <span className="muted" style={{ fontWeight: 500 }}>· {ROLE_LABEL[currentRole] || "You"} can assign to {allowedRoles.map(r => ROLE_LABEL[r]).join(", ")}</span></label>
        {suggestion && (
          <button type="button" className="kb-suggest" onClick={() => setAssigneeId(suggestion.id)}>
            <Icon name="wand-2" size={14} />
            <span><strong>{suggestion.name}</strong> has the lightest load ({wl(suggestion)} active), suggested to balance the team.</span>
            {assigneeId === suggestion.id ? <span className="kb-suggest-on"><Icon name="check" size={13} /> Selected</span> : <span className="kb-suggest-cta">Use</span>}
          </button>
        )}
        {meUser && (
          <button type="button" className="kb-self-assign" onClick={() => setAssigneeId(meUser.id)}>
            <span className="kb-av" style={{ background: meUser.color, width: 24, height: 24, fontSize: 10 }}>{meUser.initials}</span>
            <span>Assign to myself <strong>({meUser.name})</strong></span>
            {assigneeId === meUser.id ? <span className="kb-suggest-on"><Icon name="check" size={13} /> Selected</span> : <span className="kb-suggest-cta">Use</span>}
          </button>
        )}
        <div className="kb-assignee-grid card" style={{ padding: 5 }}>
          {allowed.length === 0 && <div className="muted" style={{ padding: 12, fontSize: 13 }}>No eligible assignees for your role.</div>}
          {[...allowed].sort((a, b) => wl(a) - wl(b)).map(a => {
            const n = wl(a);
            const loadClass = n === 0 ? "idle" : n >= maxWl && maxWl > 1 ? "high" : "ok";
            return (
              <button key={a.id} type="button" className={`kb-assignee-opt ${assigneeId === a.id ? "on" : ""}`} onClick={() => setAssigneeId(a.id)}>
                <span className="kb-av" style={{ background: a.color, width: 28, height: 28 }}>{a.initials}</span>
                <span style={{ flex: 1, textAlign: "left", minWidth: 0 }}>
                  <span style={{ display: "block", fontSize: 13, fontWeight: 700 }}>{a.name} {a.id === meUser?.id && <span className="kb-suggest-tag" style={{ background: "var(--green-100)", color: "var(--green-800)" }}>You</span>} {a.id === suggestion?.id && <span className="kb-suggest-tag">Suggested</span>}</span>
                  <span className="muted" style={{ fontSize: 11 }}>{ROLE_LABEL[a.role] || a.role}</span>
                </span>
                <span className="kb-load-mini">
                  <span className={`kb-load-count ${loadClass}`} style={{ fontSize: 10.5 }}>{n === 0 ? "free" : n + " active"}</span>
                  <span className="kb-load-bar sm"><i className={loadClass} style={{ width: `${(n / maxWl) * 100}%` }} /></span>
                </span>
                {assigneeId === a.id && <Icon name="check" size={16} style={{ color: "var(--green-700)", flexShrink: 0 }} />}
              </button>
            );
          })}
        </div>
        {capHit && (
          <div className={`kb-cap-warn ${cfg.enforce === "block" ? "block" : ""}`}>
            <Icon name={cfg.enforce === "block" ? "shield-alert" : "alert-triangle"} size={15} />
            <span>
              <strong>{selected ? selected.name : "This person"}</strong> is at the workload cap ({cfg.hardCap}{cfg.metric === "weighted" ? " pts" : " items"}).
              {cfg.enforce === "block" ? " Assignment is blocked by company policy, pick someone with capacity." : " You can still assign, but consider re-balancing."}
            </span>
          </div>
        )}
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
        <div className="field">
          <label>Stage</label>
          <select className="select" value={stage} onChange={e => setStage(e.target.value)} disabled={editing}>
            {stageCols.map(c => <option key={c.id} value={c.id}>{c.title}</option>)}
          </select>
        </div>
        <div className="field">
          <label>Priority</label>
          <select className="select" value={priority} onChange={e => setPriority(e.target.value)}>
            <option value="high">High</option><option value="medium">Medium</option><option value="low">Low</option>
          </select>
        </div>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, alignItems: "end" }}>
        <div className="field" style={{ margin: 0 }}>
          <label>Due date</label>
          <input type="date" className="input" value={due} onChange={e => setDue(e.target.value)} />
        </div>
        <label style={{ display: "flex", alignItems: "center", gap: 9, fontSize: 13, fontWeight: 600, padding: "10px 0", cursor: "pointer" }}>
          <input type="checkbox" checked={blocked} onChange={e => setBlocked(e.target.checked)} /> Mark as blocked
        </label>
      </div>
    </Modal>
  );
}

// ───────────────── URGENT ITEMS (Compliance) ─────────────────
(function injectRpHoverStyles() {
  if (document.getElementById("rp-hover-styles")) return;
  const css = `
  .rp-hover { transition: transform .16s cubic-bezier(.34,1.56,.64,1), box-shadow .16s ease, border-color .12s ease; will-change: transform; }
  .rp-hover:hover { transform: translateY(-3px); box-shadow: 0 14px 28px -12px rgba(14,22,18,.36); position: relative; z-index: 2; }
  @media (prefers-reduced-motion: reduce) { .rp-hover { transition: box-shadow .14s ease; } .rp-hover:hover { transform: none; } }
  `;
  const s = document.createElement("style"); s.id = "rp-hover-styles"; s.textContent = css; document.head.appendChild(s);
})();
/* Reassign drawer — slides in from the right, lists reviewers a role is allowed
   to assign to, shows each one's live workload, and auto-suggests the lightest. */
function UrgReassignDrawer({ item, role, onClose, onAssign }) {
  const REVIEW_ROLES = ["dietitian", "compliance", "manager", "admin", "super-admin"];
  const load = (ini) => { try { return window.LoraaCommands ? window.LoraaCommands.workloadOf(ini).total : 0; } catch (e) { return 0; } };
  const people = React.useMemo(() => {
    let us = [];
    try { us = (typeof USERS !== "undefined" ? USERS : (window.USERS || [])).filter((u) => REVIEW_ROLES.indexOf(u.role) >= 0); } catch (e) {}
    return us.map((u) => ({ ...u, load: load(u.initials) })).sort((a, b) => a.load - b.load);
  }, []);
  const suggested = people[0];
  const RLABEL = { dietitian: "Dietitian", compliance: "Compliance Officer", manager: "Manager", admin: "Admin", "super-admin": "Super Admin" };
  const cap = (l) => l <= 2 ? { t: "Light", c: "ok" } : l <= 4 ? { t: "Moderate", c: "warn" } : { t: "Heavy", c: "block" };
  return (
    <div className="urg-rd-scrim" onClick={(e) => e.target === e.currentTarget && onClose()}>
      <div className="urg-rd">
        <div className="urg-rd-head">
          <div><div className="urg-rd-title">Reassign</div><div className="urg-rd-sub">{item && item.name}</div></div>
          <button className="urg-rd-x" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="urg-rd-body">
          {suggested && (
            <button className="urg-rd-sug" onClick={() => onAssign(suggested)}>
              <span className="urg-rd-sug-badge"><Icon name="lightbulb" size={12} stroke={2.4} /> Loraa suggests</span>
              <span className="urg-rd-person">
                <span className="avatar sm urg-av">{suggested.initials}</span>
                <span className="urg-rd-nm"><strong>{suggested.name}</strong><small>{RLABEL[suggested.role] || suggested.role} · lightest load ({suggested.load} open)</small></span>
              </span>
              <span className="urg-rd-go">Assign <Icon name="arrow-right" size={13} stroke={2.4} /></span>
            </button>
          )}
          <div className="urg-rd-lbl">All eligible reviewers</div>
          <div className="urg-rd-list">
            {people.map((p) => { const c = cap(p.load); return (
              <button key={p.initials} className="urg-rd-row" onClick={() => onAssign(p)}>
                <span className="avatar sm urg-av">{p.initials}</span>
                <span className="urg-rd-nm"><strong>{p.name}</strong><small>{RLABEL[p.role] || p.role}</small></span>
                <span className="urg-rd-load"><span className={"urg-rd-cap " + c.c}>{c.t}</span><small>{p.load} open</small></span>
              </button>
            ); })}
            {people.length === 0 && <div className="urg-rd-empty">No reviewers available to assign.</div>}
          </div>
        </div>
      </div>
    </div>
  );
}

function UrgentItems() {
  const { openRecipe, openIngredient, role, toast } = useApp();
  const me = currentUser(role);
  const [reassign, setReassign] = React.useState(null);
  const [urgTab, setUrgTab] = React.useState("escalated");
  const doAssign = (person) => {
    const it = reassign; setReassign(null);
    if (!it || !person) return;
    try {
      const r = RECIPES.find((x) => x.id === it.rid) || (it.recipe);
      if (r) r.reviewer = { initials: person.initials, name: person.name, role: person.role };
      if (typeof auditPush === "function") auditPush({ who: me ? me.name : "You", action: "Reassigned urgent item “" + (it.name || (it.recipe && it.recipe.name)) + "” to " + person.name, target: "Urgent review", severity: "medium" });
    } catch (e) {}
    toast("Reassigned to " + person.name.split(" ")[0]);
  };
  const seeRole = role === "admin" || role === "super-admin";
  const escalations = React.useMemo(() => {
    const all = urgentLoad();
    return seeRole ? all : all.filter((e) => e.toInitials === me.initials);
  }, [role]);

  const openItem = (e) => {
    if (e.kind === "ingredient") { const g = kbIngredients().find((x) => x.id === e.rid); if (g) openIngredient(g); }
    else { const r = RECIPES.find((x) => x.id === e.rid); if (r) openRecipe(r); }
  };

  // Baseline system-flagged urgent items (demo seed)
  const seed = [
    { recipe: RECIPES[0], age: "2 days",  reason: "SLA breach, pending > 48h",      severity: "high" },
    { recipe: RECIPES[5], age: "16 hours", reason: "Allergen flag missing, sesame", severity: "high" },
    { recipe: RECIPES[2], age: "1 day",   reason: "Contributor escalation requested", severity: "medium" },
  ];

  // Render one escalation as a priority row (red-accented, SLA rail + timer).
  const EscRow = ({ e }) => (
    <div className="urg-item sev-high rp-hover">
      <span className="urg-rail" />
      {e.kind === "ingredient"
        ? <div className="urg-thumb" style={{ background: "linear-gradient(135deg, #DCEEC1, #C9E2A4)", color: "var(--green-700)" }}><Icon name="leaf" size={24} stroke={1.8} /></div>
        : e.cover
          ? <div className="urg-thumb" style={{ backgroundImage: `url("${e.cover}")` }} />
          : <div className="urg-thumb" style={{ background: "linear-gradient(135deg, #FEF0C7, #FEDF89)", color: "#b54708" }}><Icon name="utensils-crossed" size={22} stroke={1.8} /></div>}
      <div className="urg-main">
        <div className="urg-line1">
          <h3 className="urg-name">{e.name}</h3>
          <span className="urg-reason"><Icon name="alert-triangle" size={11} stroke={2.6} /> {e.reason}</span>
        </div>
        <div className="urg-sub">{e.sub}</div>
        {e.escalatedSteps && (
          <div className="urg-steps"><Icon name="list-checks" size={13} stroke={2.2} />
            {e.escalatedSteps === "all" ? "Escalated review: every step" : `Escalated steps: ${(Array.isArray(e.escalatedSteps) ? e.escalatedSteps : []).join(", ")}`}
          </div>
        )}
        <div className="urg-chain">
          <span className="urg-prio">High</span>
          <span className="urg-chain-people">
            <span className="avatar sm urg-av">{e.byInitials}</span>{e.byName}
            <Icon name="arrow-right" size={13} stroke={2.4} className="urg-arrow" />
            <span className="avatar sm urg-av to">{(e.toName || "?").split(" ").map((w) => w[0]).join("").slice(0, 2)}</span>{e.toName}
          </span>
          <span className="urg-when">{formatDate(e.when)}</span>
        </div>
      </div>
      <div className="urg-actions">
        <span className="urg-timer over"><Icon name="clock" size={12} stroke={2.6} /> Due today</span>
        <button className="btn primary urg-review" onClick={() => openItem(e)}><Icon name="arrow-right" size={15} stroke={2.6} /> Review now</button>
        <button className="btn secondary sm urg-reassign" onClick={() => setReassign(e)}><Icon name="user-plus" size={14} stroke={2.4} /> Reassign</button>
      </div>
    </div>
  );

  return (
    <div>
      <Crumbs path={[{ label: "Urgent review" }]} />
      <div className="page-head">
        <div>
          <h1 className="page-title">Urgent review</h1>
          <p className="page-sub">High-priority items that need a decision today.</p>
        </div>
        <button className="btn danger"><Icon name="alert-triangle" size={16} /> Alert Super Admin</button>
      </div>

      <QAKpiRow items={[
        { icon: "alert-triangle", value: escalations.length, label: seeRole ? "Escalated to the team" : "Escalated to you", tone: "crit" },
        { icon: "clock", value: seed.filter((s) => s.severity === "high").length, label: "SLA at risk", tone: "warn" },
        { icon: "flag", value: seed.length, label: "System-flagged", tone: "warn" },
        { icon: "check-circle-2", value: "3.1h", label: "Avg. resolution", tone: "info" },
      ]} />

      {/* Left-side horizontal tab menu */}
      <div className="urg-tabs">
        <button className={"urg-tab" + (urgTab === "escalated" ? " on" : "")} onClick={() => setUrgTab("escalated")}>
          <span className="urg-group-dot crit" />
          {seeRole ? "Escalated for urgent review" : "Escalated to you"}
          <span className="pill error">{escalations.length}</span>
        </button>
        <button className={"urg-tab" + (urgTab === "flagged" ? " on" : "")} onClick={() => setUrgTab("flagged")}>
          <span className="urg-group-dot warn" />
          System-flagged
          <span className="pill warning">{seed.length}</span>
        </button>
      </div>

      {urgTab === "escalated" && (
        escalations.length === 0 ? (
          <div className="card pad" style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 24, color: "var(--gray-600)" }}>
            <Icon name="inbox" size={18} /> <span style={{ fontSize: 13.5 }}>Nothing escalated to you right now. Items marked “Urgent review” on a recipe or ingredient land here.</span>
          </div>
        ) : (
          <div className="urg-board">
            {escalations.map((e) => <EscRow key={e.key} e={e} />)}
          </div>
        )
      )}

      {urgTab === "flagged" && (
      <div className="urg-board">
        {seed.map((u) => (
          <div key={u.recipe.id} className={`urg-item sev-${u.severity} rp-hover`}>
            <span className="urg-rail" />
            <div className="urg-thumb" style={{ backgroundImage: `url("${u.recipe.cover}")` }} />
            <div className="urg-main">
              <div className="urg-line1">
                <h3 className="urg-name">{u.recipe.name}</h3>
                <span className={u.severity === "high" ? "urg-reason" : "urg-reason warn"}><Icon name="alert-triangle" size={11} stroke={2.6} /> {u.reason}</span>
              </div>
              <div className="urg-sub">Awaiting <strong>{u.age}</strong> · contributor {u.recipe.contributor.name} · {u.recipe.cuisine}</div>
              <div className="urg-chain">
                <StatusPill status={u.recipe.status} item={u.recipe} kind="recipe" />
                <PriorityPill priority={u.recipe.priority} />
              </div>
            </div>
            <div className="urg-actions">
              <span className={`urg-timer ${u.severity === "high" ? "over" : "risk"}`}><Icon name="clock" size={12} stroke={2.6} /> {u.severity === "high" ? "Due today" : "Due soon"}</span>
              <button className="btn primary urg-review" onClick={() => openRecipe(u.recipe)}><Icon name="arrow-right" size={15} stroke={2.6} /> Review now</button>
              <button className="btn secondary sm urg-reassign" onClick={() => setReassign({ name: u.recipe.name, rid: u.recipe.id, recipe: u.recipe })}><Icon name="user-plus" size={14} stroke={2.4} /> Reassign</button>
            </div>
          </div>
        ))}
      </div>
      )}
      {reassign && <UrgReassignDrawer item={reassign} role={role} onClose={() => setReassign(null)} onAssign={doAssign} />}
    </div>
  );
}

// ───────────────── PUBLISHED RECIPES (Compliance) ─────────────────
function PublishedRecipes() {
  const { openRecipe } = useApp();
  const published = RECIPES.filter((r) => r.status === "published");
  // Add synthetic published rows
  const allPublished = [
    ...published,
    { ...RECIPES[1], status: "published", id: "r-002p" },
    { ...RECIPES[6], status: "published", id: "r-007p" },
  ];
  return (
    <div>
      <Crumbs path={[{ label: "Published Recipes" }]} />
      <div className="page-head">
        <div>
          <h1 className="page-title">Published Recipes</h1>
          <p className="page-sub">{allPublished.length} live recipes, monitor for post-publish issues.</p>
        </div>
        <button className="btn secondary"><Icon name="globe" size={16} /> Open public site</button>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))", gap: 18 }}>
        {allPublished.map((r) => (
          <RecipeCard key={r.id} recipe={r} onOpen={openRecipe} />
        ))}
      </div>
    </div>
  );
}

// ───────────────── AUDIT LOG (Super Admin) ─────────────────
function LegacyAuditLogUnavailable() {
  const [, bump] = React.useState(0);
  React.useEffect(() => {
    const h = () => bump((n) => n + 1);
    window.addEventListener("nutridms-audit", h);
    return () => window.removeEventListener("nutridms-audit", h);
  }, []);
  const events = [];
  const sevPill = { high: "error", medium: "warning", low: "neutral" };
  const actionIcon = {
    "permission.updated": "shield",
    "recipe.published": "globe",
    "recipe.changes_requested": "message-square",
    "user.invited": "user-plus",
    "recipe.submitted": "upload-cloud",
    "csv.imported": "file-spreadsheet",
    "recipe.rejected": "x-circle",
    "auth.failed": "shield-alert",
    "recipe.edited": "pencil",
    "role.assigned": "users",
    "item.deleted": "trash-2",
    "item.restored": "rotate-ccw",
    "item.purged": "trash",
    "bin.auto_purged": "clock",
    "bin.emptied": "trash",
  };
  return (
    <div>
      <Crumbs path={[{ label: "Audit Log" }]} />
      <div className="page-head">
        <div>
          <h1 className="page-title">Audit Log</h1>
          <p className="page-sub">Every action by every user, queryable, exportable, immutable.</p>
        </div>
        <div style={{ display: "flex", gap: 8 }}>
          <button className="btn secondary"><Icon name="filter" size={16} /> Filter</button>
          <button className="btn secondary"><Icon name="download" size={16} /> Export CSV</button>
        </div>
      </div>

      <div className="card" style={{ overflow: "hidden" }}>
        <table className="table">
          <thead><tr>
            <th>Timestamp</th>
            <th>Actor</th>
            <th>Action</th>
            <th>Target</th>
            <th>IP</th>
            <th>Severity</th>
          </tr></thead>
          <tbody>
            {events.map((e, i) => (
              <tr key={i}>
                <td style={{ fontFamily: "monospace", fontSize: 12, color: "var(--gray-600)" }}>{e.when}</td>
                <td>
                  <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                    {e.who === "system"
                      ? <div className="avatar sm" style={{ background: "var(--gray-900)", color: "#fff" }}><Icon name="cpu" size={12} /></div>
                      : <div className="avatar sm" style={{ background: "var(--gray-100)", color: "var(--gray-700)" }}>{initialsOf(e.who)}</div>}
                    <span>{e.who}</span>
                  </div>
                </td>
                <td>
                  <span style={{ display: "inline-flex", alignItems: "center", gap: 6, fontFamily: "monospace", fontSize: 12, fontWeight: 600, padding: "3px 8px", background: "var(--gray-50)", borderRadius: 6, color: "var(--text-primary)" }}>
                    <Icon name={actionIcon[e.action] || "circle"} size={12} stroke={2.2} />{e.action}
                  </span>
                </td>
                <td style={{ color: "var(--gray-700)" }}>{e.target}</td>
                <td style={{ fontFamily: "monospace", fontSize: 12, color: "var(--gray-600)" }}>{e.ip}</td>
                <td><span className={`pill ${sevPill[e.severity]}`} style={{ textTransform: "capitalize" }}>{e.severity}</span></td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      <div className="row" style={{ marginTop: 16, justifyContent: "space-between" }}>
        <span className="muted" style={{ fontSize: 13 }}>Showing 10 of 1,247 events · last 24h</span>
        <button className="btn secondary sm">Load more <Icon name="arrow-down" size={14} /></button>
      </div>
    </div>
  );
}
function initialsOf(name) { return name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase(); }

// IntegrationsScreen lives in screens/integrations.jsx now.

// ───────────────── NUTRITION REVIEW (Dietitian-specific) ─────────────────
// Two QA-review queues, Recipes and Ingredients, both fed by what
// contributors submit (Upload Recipe / Add Ingredient). Items assigned to a
// specific reviewer only surface on that reviewer's account ("Assigned to me");
// unassigned items sit in a shared pool anyone can claim.

const NR_REVIEW_STATUSES = ["pending-review", "compliance-review", "changes-requested"];
// Deterministic reviewer assignment for the recipe queue (no reviewer field on recipes).
const NR_RECIPE_REVIEWER = {
  "r-001": { name: "Eve Nakamura", initials: "EN", role: "dietitian" },
  "r-003": { name: "Arjun Patel",  initials: "AP", role: "dietitian" },
  "r-006": null,                                          // unassigned → pool
  "r-007": { name: "Eve Nakamura", initials: "EN", role: "dietitian" },
};

function NutritionReview({ initialScope }) {
  const { openRecipe, openIngredient, role, toast } = useApp();
  const me = currentUser(role);
  const [tab, setTab] = React.useState("recipes");          // recipes | ingredients
  const [scope, setScope] = React.useState(initialScope || "mine");   // mine | pool | all | urgent
  const [qaSearch, setQaSearch] = React.useState("");
  const [revFilter, setRevFilter] = React.useState("all");   // all | <initials> | pool
  const [claimed, setClaimed] = React.useState(() => new Set());
  const [claiming, setClaiming] = React.useState(() => new Set());
  const [loraaOpen, setLoraaOpen] = React.useState(false);
  const [confidenceInsight, setConfidenceInsight] = React.useState(null);
  // Re-read submitted items when contributors submit (fires nutridms-submitted).
  const [submittedTick, setSubmittedTick] = React.useState(0);
  React.useEffect(() => {
    const bump = () => setSubmittedTick((t) => t + 1);
    window.addEventListener("nutridms-submitted", bump);
    window.addEventListener("nutridms-recipes", bump);
    window.addEventListener("nutridms-ingredients", bump);
    window.addEventListener("storage", bump);
    return () => { window.removeEventListener("nutridms-submitted", bump); window.removeEventListener("nutridms-recipes", bump); window.removeEventListener("nutridms-ingredients", bump); window.removeEventListener("storage", bump); };
  }, []);

  // Keep macros and Loraa confidence current while the review queue is open.
  React.useEffect(() => {
    let active = true;
    const refresh = async () => {
      if (!active || document.visibilityState === "hidden") return;
      const jobs = [];
      if (window.RecipeSync && typeof window.RecipeSync.refresh === "function") jobs.push(window.RecipeSync.refresh());
      if (window.IngredientSync && typeof window.IngredientSync.refresh === "function") jobs.push(window.IngredientSync.refresh());
      await Promise.allSettled(jobs);
    };
    refresh();
    return () => { active = false; };
  }, []);

  const mergeReviewRows = (submitted, remote) => {
    const byId = new Map();
    submitted.forEach((row) => { if (row && row.raw && row.raw.id) byId.set(String(row.raw.id), row); });
    remote.forEach((row) => { if (row && row.raw && row.raw.id) byId.set(String(row.raw.id), row); });
    return Array.from(byId.values());
  };

  // Build both queues with a normalized reviewer field, merging contributor-
  // submitted items (routed to a reviewer, or null = shared claim pool).
  const recipeQueue = React.useMemo(() => {
    const subs = (typeof window.loadSubmittedItems === "function" ? window.loadSubmittedItems("recipe") : [])
      .map((r) => ({ reviewer: r.reviewer || null, raw: r }));
    const base = RECIPES
      .filter((r) => NR_REVIEW_STATUSES.includes(r.status))
      .map((r) => ({ reviewer: r.reviewer || null, raw: r }));
    return mergeReviewRows(subs, base);
  }, [role, submittedTick]);
  const ingredientQueue = React.useMemo(() => {
    const subs = (typeof window.loadSubmittedItems === "function" ? window.loadSubmittedItems("ingredient") : [])
      .map((g) => ({ reviewer: g.reviewer || null, raw: g }));
    const base = kbIngredients()
      .filter((g) => NR_REVIEW_STATUSES.includes(g.status))
      .map((g) => ({ reviewer: g.reviewer || null, raw: g }));
    return mergeReviewRows(subs, base);
  }, [submittedTick]);

  const claimReviewer = (rev, id) => (claimed.has(id) ? me : rev);
  const isOwnSubmission = (raw) => {
    const auth = window.__nutridmsAuthenticatedUser || {};
    const authId = auth.id || auth.userId || null;
    return !!(raw && raw.createdById && authId && String(raw.createdById) === String(authId));
  };
  const isSelfClaimBlocked = (raw) => isOwnSubmission(raw) && raw.submitRoute !== "pool";
  const serverAction = (raw, action) => !!(
    raw && raw.__remote && raw.workflow && raw.workflow.actions && raw.workflow.actions[action]
  );
  const searchMatch = (raw) => {
    const q = qaSearch.trim().toLowerCase();
    if (!q) return true;
    return [raw.name, raw.ref, raw.canonical, raw.cuisine, raw.category, raw.contributor && raw.contributor.name]
      .filter(Boolean).some((s) => String(s).toLowerCase().includes(q));
  };
  const applyScope = (rows) => rows.filter((row) => {
    const rev = claimReviewer(row.reviewer, row.raw.id);
    if (!searchMatch(row.raw)) return false;
    if (revFilter === "pool" && rev) return false;
    if (revFilter !== "all" && revFilter !== "pool" && (!rev || rev.initials !== revFilter)) return false;
    if (scope === "mine") return rev && rev.initials === me.initials;
    if (scope === "pool") return !rev;
    if (scope === "urgent") return isUrgent(row.raw);
    return true;
  });
  const isUrgent = (raw) => !!(raw && (raw.urgent === true || raw.escalated === true || String(raw.priority) === "high" || String(raw.priority) === "critical"));

  const allLiveItems = (window.RECIPES || []).concat(window.INGREDIENT_ITEMS || []);
  const weekAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);
  const reviewedThisWeek = allLiveItems.filter((item) => {
    if (!["approved", "published"].includes(item.status)) return false;
    const stamp = new Date(item.updated || item.submitted || 0).getTime();
    return Number.isFinite(stamp) && stamp >= weekAgo;
  }).length;
  const allQueued = recipeQueue.concat(ingredientQueue).map((row) => row.raw);
  const flaggedMacros = allQueued.reduce((total, item) => total + (Array.isArray(item.macroFlags) ? item.macroFlags.length : Array.isArray(item.flags) ? item.flags.filter((flag) => flag.type === "nutrient" || flag.type === "macro").length : 0), 0);
  const turnaroundHours = allLiveItems.map((item) => Number(item.turnaroundHours)).filter(Number.isFinite);
  const averageTurnaround = turnaroundHours.length ? ((turnaroundHours.reduce((sum, value) => sum + value, 0) / turnaroundHours.length).toFixed(1) + "h") : "—";
  const confidenceFor = (item) => {
    const value = item && item.confidence;
    if (value == null || value === "") return null;
    const numeric = Number(typeof value === "object" ? (value.score != null ? value.score : value.confidence) : value);
    if (!Number.isFinite(numeric)) return null;
    return Math.max(0, Math.min(100, numeric <= 1 ? Math.round(numeric * 100) : Math.round(numeric)));
  };
  const macroText = (value, unit) => value == null || value === "" ? "—" : value + (unit || "");

  const recRows = applyScope(recipeQueue);
  const ingRows = applyScope(ingredientQueue);
  const rows = tab === "recipes" ? recRows : ingRows;

  const mineCount = (q) => q.filter((row) => { const rev = claimReviewer(row.reviewer, row.raw.id); return rev && rev.initials === me.initials; }).length;
  const poolCount = (q) => q.filter((row) => !claimReviewer(row.reviewer, row.raw.id)).length;
  const urgentCount = (q) => q.filter((row) => isUrgent(row.raw)).length;

  const claim = async (row, kind) => {
    const id = row.raw.id;
    if (row.raw.__remote && !serverAction(row.raw, "can_claim")) {
      toast("You are not eligible to claim this workflow stage.");
      return;
    }
    if (isSelfClaimBlocked(row.raw)) {
      toast("A different reviewer must claim items you submitted.");
      return;
    }
    if (claiming.has(id)) return;
    setClaiming((set) => new Set(set).add(id));
    try {
      if (row.raw.__remote) {
        try {
          if (!window.NutriRecipes || !window.NutriIngredients) throw new Error("The review service is still loading.");
          const saved = kind === "recipe"
            ? await window.NutriRecipes.claim(id)
            : await window.NutriIngredients.claim(id);
          const auth = window.__nutridmsAuthenticatedUser || {};
          const claimedBy = {
            id: auth.id || auth.userId || (saved && saved.reviewer) || null,
            name: auth.name || me.name || "You",
            initials: auth.initials || me.initials || "You",
            role: auth.role || me.role || "reviewer"
          };
          row.reviewer = claimedBy;
          row.raw.reviewer = claimedBy;
          setClaimed((set) => new Set(set).add(id));
          setRevFilter("all");
          setScope("mine");
          setSubmittedTick((tick) => tick + 1);
          if (kind === "recipe" && window.RecipeSync) await window.RecipeSync.sync();
          if (kind === "ingredient" && window.IngredientSync) await window.IngredientSync.sync();
          toast("Claimed — now shown in Assigned to me");
          return;
        } catch (error) {
          const data = error && error.data;
          const detail = data && (data.detail || (data.error && data.error.message));
          toast(detail || (error && (error.detail || error.message)) || "This item could not be claimed.");
          return;
        }
      }
      // Persist the claim so the item leaves the shared pool for EVERYONE and lands
      // in this reviewer's queue (survives reload + other accounts).
      setClaimed((s) => new Set(s).add(id));
      try {
        const meObj = { initials: me.initials, name: me.name, role: me.role };
        let persisted = false;
        // Contributor-submitted items live in the submitted store.
        ["recipe", "ingredient"].forEach((k) => {
          if (typeof window.loadSubmittedItems !== "function") return;
          const hit = window.loadSubmittedItems(k).find((x) => x.id === id);
          if (hit && typeof window.saveSubmittedItem === "function") { window.saveSubmittedItem(k, { ...hit, reviewer: meObj }); persisted = true; }
        });
        // Seeded recipes / ingredients carry the reviewer inline.
        if (!persisted) {
          const r = (window.RECIPES || []).find((x) => x.id === id); if (r) r.reviewer = meObj;
          const g = (typeof kbIngredients === "function" ? kbIngredients() : []).find((x) => x.id === id); if (g) g.reviewer = meObj;
        }
        if (typeof auditPush === "function") auditPush({ who: me.name, action: "Claimed “" + id + "” for nutrition review", target: "Nutrition Review", severity: "low" });
        window.dispatchEvent(new CustomEvent("nutridms-submitted"));
      } catch (e) {}
      toast("Claimed — moved to your queue");
    } finally {
      setClaiming((set) => { const next = new Set(set); next.delete(id); return next; });
    }
  };

  const reviewerCell = (row, kind) => {
    const rev = claimReviewer(row.reviewer, row.raw.id);
    if (!rev) {
      const own = isSelfClaimBlocked(row.raw);
      const busy = claiming.has(row.raw.id);
      if (own) return <span className="muted" style={{ fontSize: 12 }} title="Directly assigned submissions require another reviewer">Needs another reviewer</span>;
      if (row.raw.__remote && !serverAction(row.raw, "can_claim")) {
        return <span className="muted" style={{ fontSize: 12 }}>Not eligible for this stage</span>;
      }
      return (
        <button className="btn secondary sm" disabled={busy} onClick={(e) => { e.stopPropagation(); claim(row, kind); }}>
          <Icon name="hand" size={13} /> {busy ? "Claiming…" : "Claim"}
        </button>
      );
    }
    const isMe = rev.initials === me.initials;
    return (
      <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
        <div className="avatar sm" style={{ background: isMe ? "var(--green-100)" : "var(--gray-100)", color: isMe ? "var(--green-700)" : "var(--gray-700)" }}>{rev.initials}</div>
        <span style={{ fontSize: 13 }}>{isMe ? "You" : rev.name}</span>
      </div>
    );
  };

  return (
    <div>
      <Crumbs path={[{ label: "Nutrition Review" }]} />
      <div className="page-head">
        <div>
          <h1 className="page-title">Nutrition Review</h1>
          <p className="page-sub">Verify macros, allergen flags, and diet tags, sorted by SLA risk.</p>
        </div>
        {!loraaOpen && (
          <button type="button" className="cu-loraa-pill cu-loraa-animated" style={{ margin: 0 }} onClick={() => setLoraaOpen(true)}>
            <span className="cu-loraa-logo sm"><img src="assets/loraa-logo.png" alt="Loraa" /></span>
            <span>Pre-screen with Loraa</span>
            <Icon name="chevron-down" size={14} />
          </button>
        )}
      </div>

      {loraaOpen && <LoraaQAPanel onClose={() => setLoraaOpen(false)} />}
      {confidenceInsight && <ConfidenceInsightDrawer entry={confidenceInsight} onClose={() => setConfidenceInsight(null)} />}

      <div className="stats" style={{ marginBottom: 22 }}>
        <Stat label="In your queue" value={mineCount(tab === "recipes" ? recipeQueue : ingredientQueue)} icon="clipboard-check" tone="warn" />
        <Stat label="Reviewed this week" value={reviewedThisWeek} icon="check-circle-2" tone="success" />
        <Stat label="Flagged macros" value={flaggedMacros} icon="flag" tone="error" />
        <Stat label="Avg. turnaround" value={averageTurnaround} icon="timer" tone="info" />
      </div>

      {/* Queue toggle (right, bold) + reviewer scope (left) on one row */}
      <div className="nr-controls">
        <div className="nr-scope">
          {[
            { id: "mine", label: "Assigned to me", count: mineCount(tab === "recipes" ? recipeQueue : ingredientQueue) },
            { id: "pool", label: "Unassigned pool", count: poolCount(tab === "recipes" ? recipeQueue : ingredientQueue) },
            { id: "urgent", label: "Urgent review", count: urgentCount(tab === "recipes" ? recipeQueue : ingredientQueue) },
            { id: "all", label: "All", count: (tab === "recipes" ? recipeQueue : ingredientQueue).length },
          ].map((s) => (
            <button key={s.id} className={`chip ${scope === s.id ? "on" : ""} ${s.id === "urgent" ? "nr-chip-urgent" : ""}`} onClick={() => setScope(s.id)}>
              {s.label} <span style={{ fontWeight: 800, opacity: .7 }}>· {s.count}</span>
            </button>
          ))}
        </div>
        <div className="nr-queue-seg" role="tablist" aria-label="Queue">
          {[
            { id: "recipes", label: "Recipes", icon: "utensils-crossed", count: recipeQueue.length },
            { id: "ingredients", label: "Ingredients", icon: "leaf", count: ingredientQueue.length },
          ].map((t) => (
            <button key={t.id} role="tab" aria-selected={tab === t.id} className={`nr-queue-btn ${tab === t.id ? "on" : ""}`} onClick={() => setTab(t.id)}>
              <Icon name={t.icon} size={15} stroke={2.4} /> {t.label}
              <span className="nr-queue-count">{t.count}</span>
            </button>
          ))}
        </div>
      </div>

      <QAToolbar
        search={qaSearch} setSearch={setQaSearch}
        selects={[{
          value: revFilter, onChange: setRevFilter,
          options: [{ value: "all", label: "All reviewers" }, { value: "pool", label: "Unassigned pool" }].concat(qaReviewerWorkload().map((w) => ({ value: w.initials, label: w.name })))
        }]}
        count={rows.length}
      />

      <div className="qa-split">
        <div className="qa-main">
      <div className="lib-tablewrap">
        <table className="table lib-table">
          <thead><tr>
            <th>{tab === "recipes" ? "Recipe" : "Ingredient"}</th>
            <th>Contributor</th>
            <th>{tab === "recipes" ? "Macros (per serving)" : "Macros (per 100g)"}</th>
            <th>{tab === "recipes" ? "Allergens declared" : "Allergens"}</th>
            <th>Reviewer</th>
            <th>
              <span className="nr-conf-h">Confidence
                <span className="nr-conf-info" tabIndex={0}><Icon name="info" size={12} stroke={2.4} />
                  <span className="nr-conf-pop">
                    <strong>How confidence is measured</strong>
                    Loraa scores each item 0–100% from three live signals:
                    <span className="nr-conf-row"><b>Nutrition completeness</b> — macros + micros present and within USDA/FDC range</span>
                    <span className="nr-conf-row"><b>Source match</b> — ingredients mapped to a verified reference with a citation</span>
                    <span className="nr-conf-row"><b>Allergen &amp; claim coverage</b> — declared allergens reconcile with the ingredient list</span>
                    <em>Higher means less manual checking; below 65% flags a likely gap to review first.</em>
                  </span>
                </span>
              </span>
            </th>
            <th style={{ textAlign: "right" }}>Action</th>
          </tr></thead>
          <tbody>
            {tab === "recipes" && recRows.map((row, i) => {
              const r = row.raw;
              const rc = r.contributor || { name: "—", initials: "—" };
              const ral = r.allergens || [];
              const confidence = confidenceFor(r);
              const conf_color = confidence == null ? "var(--gray-300)" : confidence > 85 ? "var(--success-500)" : confidence > 65 ? "var(--warning-500)" : "var(--error-500)";
              return (
                <tr key={r.id} onClick={() => openRecipe(r)} style={{ cursor: "pointer" }}>
                  <td>
                    <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
                      {(r.cover || r.image)
                        ? <div className="thumb sm" style={{ backgroundImage: `url("${r.cover || r.image}")` }} />
                        : <div className="thumb sm" style={{ background: "linear-gradient(135deg, #DCEEC1, #C9E2A4)", display: "grid", placeItems: "center", color: "var(--green-700)" }}><Icon name="utensils-crossed" size={16} stroke={1.8} /></div>}
                      <div>
                        <div style={{ fontWeight: 600, color: "var(--text-primary)" }}>{r.name}</div>
                        <div className="muted" style={{ fontSize: 12 }}>{[r.cuisine, r.category].filter(Boolean).join(" · ")}</div>
                      </div>
                    </div>
                  </td>
                  <td>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <div className="avatar sm" style={{ background: "var(--gray-100)", color: "var(--gray-700)" }}>{rc.initials}</div>
                      <span style={{ fontSize: 13 }}>{rc.name}</span>
                    </div>
                  </td>
                  <td>
                    <div style={{ display: "flex", gap: 8, fontSize: 12 }}>
                      <span><strong style={{ color: "var(--text-primary)" }}>{macroText(r.calories)}</strong> <span className="muted">kcal</span></span>
                      <span><strong>{macroText(r.protein, "g")}</strong> <span className="muted">P</span></span>
                      <span><strong>{macroText(r.carbs, "g")}</strong> <span className="muted">C</span></span>
                      <span><strong>{macroText(r.fat, "g")}</strong> <span className="muted">F</span></span>
                    </div>
                  </td>
                  <td>
                    {ral.length === 0
                      ? <span className="pill warning"><Icon name="alert-triangle" size={11} stroke={2.4} /> None declared</span>
                      : <div style={{ display: "flex", gap: 4, flexWrap: "wrap" }}>{ral.slice(0, 2).map((a) => <span key={a} className="pill neutral" style={{ fontSize: 11 }}>{a}</span>)}{ral.length > 2 && <span className="pill neutral" style={{ fontSize: 11 }}>+{ral.length - 2}</span>}</div>}
                  </td>
                  <td onClick={(e) => e.stopPropagation()}>{reviewerCell(row, "recipe")}</td>
                  <td>
                    <button type="button" onClick={(e) => { e.stopPropagation(); setConfidenceInsight({ kind: "Recipe", item: r, score: confidence }); }} style={{ display: "flex", alignItems: "center", gap: 8, minWidth: 110, width: "100%", padding: 0, border: 0, background: "transparent", cursor: "pointer", fontFamily: "inherit", textAlign: "left" }} title="Open confidence insight">
                      <div style={{ flex: 1, height: 6, background: "var(--gray-100)", borderRadius: 999 }}>
                        <div style={{ width: `${confidence == null ? 0 : confidence}%`, height: "100%", background: conf_color, borderRadius: 999 }} />
                      </div>
                      <strong style={{ fontSize: 12 }}>{confidence == null ? "—" : confidence + "%"}</strong>
                    </button>
                  </td>
                  <td style={{ textAlign: "right", whiteSpace: "nowrap" }} onClick={(e) => e.stopPropagation()}>
                    <div className="nr-actions">
                      <button className="btn sm primary nr-verify" onClick={() => openRecipe(r)}>View <Icon name="arrow-right" size={14} /></button>
                    </div>
                  </td>
                </tr>
              );
            })}

            {tab === "ingredients" && ingRows.map((row, i) => {
              const g = row.raw;
              const gn = g.nutr || {};
              const gc = g.contributor || { name: "—", initials: "—" };
              const confidence = confidenceFor(g);
              const conf_color = confidence == null ? "var(--gray-300)" : confidence > 85 ? "var(--success-500)" : confidence > 65 ? "var(--warning-500)" : "var(--error-500)";
              return (
                <tr key={g.id} onClick={() => openIngredient(g)} style={{ cursor: "pointer" }}>
                  <td>
                    <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
                      {g.image || g.cover
                        ? <div className="thumb sm" style={{ backgroundImage: `url("${g.image || g.cover}")`, backgroundSize: "cover", backgroundPosition: g.imagePos ? `${g.imagePos.x}% ${g.imagePos.y}%` : "center" }} />
                        : <div className="thumb sm" style={{ background: "linear-gradient(135deg, #DCEEC1, #C9E2A4)", display: "grid", placeItems: "center", color: "var(--green-700)" }}><Icon name="leaf" size={18} stroke={1.8} /></div>}
                      <div>
                        <div style={{ fontWeight: 600, color: "var(--text-primary)" }}>{g.name}</div>
                        <div className="muted" style={{ fontSize: 12, fontStyle: "italic" }}>{[g.canonical, g.category].filter(Boolean).join(" · ")}</div>
                      </div>
                    </div>
                  </td>
                  <td>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <div className="avatar sm" style={{ background: "var(--gray-100)", color: "var(--gray-700)" }}>{gc.initials}</div>
                      <span style={{ fontSize: 13 }}>{gc.name}</span>
                    </div>
                  </td>
                  <td>
                    <div style={{ display: "flex", gap: 8, fontSize: 12 }}>
                      <span><strong style={{ color: "var(--text-primary)" }}>{macroText(gn.calories)}</strong> <span className="muted">kcal</span></span>
                      <span><strong>{macroText(gn.p, "g")}</strong> <span className="muted">P</span></span>
                      <span><strong>{macroText(gn.c, "g")}</strong> <span className="muted">C</span></span>
                      <span><strong>{macroText(gn.f, "g")}</strong> <span className="muted">F</span></span>
                    </div>
                  </td>
                  <td>
                    {(g.allergens && g.allergens.length)
                      ? <div style={{ display: "flex", gap: 4, flexWrap: "wrap" }}>{g.allergens.slice(0, 2).map((a) => <span key={a} className="pill neutral" style={{ fontSize: 11 }}>{a}</span>)}</div>
                      : <span className="pill success" style={{ fontSize: 11 }}><Icon name="check" size={11} stroke={2.4} /> None</span>}
                  </td>
                  <td onClick={(e) => e.stopPropagation()}>{reviewerCell(row, "ingredient")}</td>
                  <td>
                    <button type="button" onClick={(e) => { e.stopPropagation(); setConfidenceInsight({ kind: "Ingredient", item: g, score: confidence }); }} style={{ display: "flex", alignItems: "center", gap: 8, minWidth: 110, width: "100%", padding: 0, border: 0, background: "transparent", cursor: "pointer", fontFamily: "inherit", textAlign: "left" }} title="Open confidence insight">
                      <div style={{ flex: 1, height: 6, background: "var(--gray-100)", borderRadius: 999 }}>
                        <div style={{ width: `${confidence == null ? 0 : confidence}%`, height: "100%", background: conf_color, borderRadius: 999 }} />
                      </div>
                      <strong style={{ fontSize: 12 }}>{confidence == null ? "—" : confidence + "%"}</strong>
                    </button>
                  </td>
                  <td style={{ textAlign: "right", whiteSpace: "nowrap" }} onClick={(e) => e.stopPropagation()}>
                    <div className="nr-actions">
                      <button className="btn sm primary nr-verify" onClick={() => openIngredient(g)}>View <Icon name="arrow-right" size={14} /></button>
                    </div>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
      {rows.length === 0 && (
        <QAEmpty
          icon={scope === "pool" ? "hand" : "check-check"}
          title={scope === "mine" ? "Nothing assigned to you" : scope === "pool" ? "The claim pool is empty" : (qaSearch ? "No matches" : "All caught up")}
          sub={scope === "mine" ? "Items routed to you for nutrition review land here. Check the pool for unclaimed work." : scope === "pool" ? "No unclaimed items are waiting. New submissions sent to the shared pool will appear here." : (qaSearch ? "Try a different name, reference, or contributor." : `No ${tab === "recipes" ? "recipes" : "ingredients"} are pending nutrition review right now.`)}
          actions={scope !== "pool" ? [{ label: "Check the pool", icon: "hand", onClick: () => setScope("pool") }] : [{ label: "View all", icon: "list", onClick: () => setScope("all") }]}
        />
      )}
        </div>
        <QAInsightPanel
          me={me}
          metrics={[
            { label: "In your queue", value: mineCount(tab === "recipes" ? recipeQueue : ingredientQueue) },
            { label: "Unclaimed pool", value: poolCount(tab === "recipes" ? recipeQueue : ingredientQueue) },
            { label: "Reviewed this week", value: reviewedThisWeek },
            { label: "Avg. turnaround", value: averageTurnaround },
          ]}
          workload={qaReviewerWorkload()}
          footer={null}
        />
      </div>
    </div>
  );
}

function ConfidenceInsightDrawer({ entry, onClose }) {
  const item = entry && entry.item;
  const details = item && item.confidenceDetails && typeof item.confidenceDetails === "object" ? item.confidenceDetails : {};
  const components = Array.isArray(details.components) ? details.components : [];
  const score = entry && entry.score != null ? entry.score : null;
  const generatedAt = details.generated_at ? new Date(details.generated_at) : null;
  const generatedLabel = generatedAt && !Number.isNaN(generatedAt.getTime()) ? generatedAt.toLocaleString() : "when the record is saved";
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [onClose]);
  return ReactDOM.createPortal(
    <div className="qad-bg" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <aside className="qad" role="dialog" aria-label="Confidence insight">
        <div className="qad-head">
          <div>
            <span className="pill" style={{ background: "#EEF4FF", color: "#3538CD", border: "1px solid #C7D7FE", marginBottom: 8 }}><Icon name="gauge" size={12} /> Evidence confidence</span>
            <h3 style={{ margin: 0, fontFamily: "var(--serif)", fontSize: 22 }}>{item ? item.name : "Confidence insight"}</h3>
            <p style={{ margin: "4px 0 0", color: "var(--gray-600)", fontSize: 13 }}>{entry && entry.kind} · evidence updated {generatedLabel}</p>
          </div>
          <button className="icon-btn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="qad-body">
          <div className="card" style={{ padding: 18, marginBottom: 16 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
              <div style={{ width: 74, height: 74, flex: "0 0 74px", borderRadius: "50%", display: "grid", placeItems: "center", background: score == null ? "var(--gray-100)" : score >= 75 ? "var(--green-100)" : score >= 50 ? "#FFF7E8" : "#FEECEC", color: score == null ? "var(--gray-500)" : score >= 75 ? "var(--green-800)" : score >= 50 ? "#9A5508" : "#B42318", fontSize: 22, fontWeight: 800 }}>{score == null ? "—" : score + "%"}</div>
              <div>
                <strong style={{ display: "block", fontSize: 15 }}>Review evidence completeness</strong>
                <span className="muted" style={{ fontSize: 12.5, lineHeight: 1.45 }}>This score shows how complete and traceable the saved record is. It does not judge food quality, and it never invents missing nutrition.</span>
              </div>
            </div>
            {details.summary && <div style={{ marginTop: 12, padding: "9px 11px", borderRadius: 9, background: "var(--gray-50)", border: "1px solid var(--gray-100)", fontSize: 12.5 }}>{details.summary}</div>}
          </div>
          {components.length ? components.map((component) => {
            const max = Number(component.max) || 0;
            const got = Number(component.got) || 0;
            const pct = max ? Math.max(0, Math.min(100, Math.round(got / max * 100))) : 0;
            const criteria = Array.isArray(component.criteria) ? component.criteria : [];
            const issues = Array.isArray(component.issues) ? component.issues : [];
            return <section key={component.key || component.label} style={{ padding: "15px 0", borderBottom: "1px solid var(--gray-100)" }}>
              <div style={{ display: "flex", justifyContent: "space-between", gap: 12, fontSize: 13 }}><strong>{component.label}</strong><b>{got}/{max}</b></div>
              <div style={{ height: 6, background: "var(--gray-100)", borderRadius: 999, margin: "8px 0" }}><div style={{ width: pct + "%", height: "100%", borderRadius: 999, background: pct >= 75 ? "var(--success-500)" : pct >= 50 ? "var(--warning-500)" : "var(--error-500)" }} /></div>
              <div className="muted" style={{ fontSize: 12.5, lineHeight: 1.45 }}>{component.note || "No explanation was supplied."}</div>
              {criteria.length > 0 && <div style={{ marginTop: 10 }}>
                <strong style={{ display: "block", fontSize: 11.5, color: "var(--gray-700)", textTransform: "uppercase", letterSpacing: ".04em", marginBottom: 5 }}>Evidence checked</strong>
                {criteria.map((criterion, index) => <div key={index} style={{ display: "grid", gridTemplateColumns: "minmax(0,1fr) auto", gap: "3px 10px", padding: "7px 0", borderTop: index ? "1px solid var(--gray-100)" : "none", fontSize: 12 }}>
                  <span style={{ fontWeight: 650 }}>{criterion.label}</span>
                  <b style={{ color: Number(criterion.got) === Number(criterion.max) ? "var(--success-600)" : "var(--error-600)" }}>{criterion.got}/{criterion.max}</b>
                  <span className="muted" style={{ gridColumn: "1 / -1", lineHeight: 1.4 }}>{criterion.evidence}</span>
                </div>)}
              </div>}
              <div style={{ marginTop: 10 }}>
                <strong style={{ display: "block", fontSize: 11.5, color: "var(--gray-700)", textTransform: "uppercase", letterSpacing: ".04em", marginBottom: 6 }}>{issues.length ? "Needs attention" : "Evidence status"}</strong>
                {issues.length ? issues.map((issue, index) => <div key={index} style={{ padding: 10, borderRadius: 9, background: "#FFF7F6", border: "1px solid #FECDCA", marginBottom: 7, fontSize: 12, lineHeight: 1.45 }}>
                  <div style={{ display: "flex", justifyContent: "space-between", gap: 10 }}><strong style={{ color: "#B42318" }}>Check {index + 1}</strong></div>
                  <div style={{ marginTop: 3 }}>{issue.issue}</div>
                  <div style={{ marginTop: 5 }}><strong>Fix:</strong> {issue.fix}</div>
                </div>) : <div style={{ padding: 9, borderRadius: 9, background: "var(--green-50)", border: "1px solid var(--green-200)", color: "var(--green-800)", fontSize: 12 }}>Evidence is complete for this area.</div>}
              </div>
            </section>;
          }) : <QAEmpty icon="database" title="No confidence evidence yet" sub="Add nutrition, source mapping, allergen, and compliance data. The score updates automatically when the live record changes." />}
        </div>
        <div className="qad-foot"><button className="btn primary" onClick={onClose}>Done</button></div>
      </aside>
    </div>, document.body
  );
}

// Loraa QA pre-screen, full-width supportive banner shown when the reviewer
// expands the "Pre-screen with Loraa" pill. Replaces the old "Auto-verify (USDA)"
// button. House rule: collapsed by default, subscribed-only, supportive-only
// (never overrides the reviewer's final call).
function LoraaQAPanel({ onClose }) {
  const { toast } = useApp();
  const [running, setRunning] = React.useState(false);
  const [result, setResult] = React.useState(null);

  const run = async () => {
    setRunning(true);
    try {
      if (!window.NutriLoraa || typeof window.NutriLoraa.preScreen !== "function") throw new Error("Loraa is still loading.");
      const live = await window.NutriLoraa.preScreen();
      const items = Array.isArray(live && live.items) ? live.items : [];
      const scanned = Number(live && live.scanned) || 0;
      const macro = Number(live && live.macroFlags) || 0;
      const allergen = Number(live && live.allergenRisks) || 0;
      const critical = Number(live && live.critical) || 0;
      const dups = Number(live && live.duplicates) || 0;
      setRunning(false);
      setResult({ scanned, macro, allergen, critical, dups, items: items.slice(0, 8), at: live.generatedAt ? new Date(live.generatedAt) : new Date(), taskId: live.taskId });
      toast("Loraa pre-screened " + scanned + " items · " + macro + " macro flags, " + allergen + " allergen risks");
    } catch (error) {
      setRunning(false);
      const data = error && error.data;
      const detail = data && (data.detail || (data.error && data.error.message));
      toast(detail || (error && error.message) || "Loraa could not run the live pre-screen.");
    }
  };

  const duties = [
    { ic: "calculator",    t: "Reads the live nutrition profiles and flags incomplete macros." },
    { ic: "alert-triangle", t: "Checks whether each queued record has allergen coverage." },
    { ic: "shield-check",  t: "Includes the latest tenant compliance-check outcomes." },
    { ic: "copy-check",    t: "Surfaces exact duplicate names in the current queue." },
    { ic: "gauge",         t: "Recalculates auditable confidence and sorts the riskiest records first." },
    { ic: "history",       t: "Writes the pre-screen result to Loraa's governed task and audit history." },
  ];

  return (
    <div className="cu-loraa cu-loraa-animated nr-loraa" style={{ margin: "0 0 18px", alignItems: "flex-start", flexDirection: "column", gap: 14, padding: "16px 18px" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 11, width: "100%" }}>
        <span className="cu-loraa-logo"><img src="assets/loraa-logo.png" alt="Loraa" /></span>
        <div style={{ flex: 1, minWidth: 0 }}>
          <strong style={{ fontSize: 14.5, color: "#4318a8", display: "flex", alignItems: "center", gap: 7, whiteSpace: "nowrap" }}>Loraa · QA pre-screen <span style={{ fontSize: 9.5, fontWeight: 800, letterSpacing: ".05em", textTransform: "uppercase", padding: "2px 7px", borderRadius: 999, background: "#ECE9FE", color: "#5925DC" }}>Add-on</span></strong>
          <span style={{ fontSize: 12.5, color: "var(--gray-600)" }}>{result ? "Pre-screen complete. Review Loraa's findings below, then make the final call." : "Runs the heavy checks first so your review is faster, these are the jobs Loraa does for this queue:"}</span>
        </div>
        <button className="cu-loraa-collapse" onClick={onClose} title="Collapse"><Icon name="chevron-up" size={16} /></button>
      </div>

      {!result ? (
        <div style={{ width: "100%", display: "grid", gridTemplateColumns: "1fr 1fr", gap: "11px 22px" }}>
          {duties.map((d, i) => (
            <div key={i} style={{ display: "flex", gap: 9, alignItems: "flex-start", fontSize: 12.5, color: "var(--gray-700)", lineHeight: 1.45 }}>
              <Icon name={d.ic} size={15} style={{ color: "#6938EF", flexShrink: 0, marginTop: 1 }} />
              <span>{d.t}</span>
            </div>
          ))}
        </div>
      ) : (
        <div style={{ width: "100%" }}>
          <div className="nr-loraa-stats">
            {[["Scanned", result.scanned, "#6938EF"], ["Macro flags", result.macro, "#C2680C"], ["Allergen risks", result.allergen, "#B91C1C"], ["Duplicates", result.dups, "#0E7490"]].map(([l, v, c]) => (
              <div key={l} className="nr-loraa-stat"><b style={{ color: c }}>{v}</b><span>{l}</span></div>
            ))}
          </div>
          {result.items.length > 0 && (
            <div className="nr-loraa-findings">
              <div className="nr-loraa-findings-h">Needs attention first</div>
              {result.items.map((it, i) => (
                <div key={it.id || i} className="nr-loraa-finding">
                  <Icon name={it.critical ? "alert-octagon" : "alert-triangle"} size={14} style={{ color: it.critical ? "#B91C1C" : "#C2680C", flexShrink: 0 }} />
                  <span>{it.name}</span>
                  <em>{it.critical ? "critical · " : ""}{Array.isArray(it.flags) ? it.flags.length : 0} to review · {it.confidence}% confidence</em>
                </div>
              ))}
            </div>
          )}
        </div>
      )}

      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, width: "100%", flexWrap: "wrap", paddingTop: 12, borderTop: "1px dashed #E0DBFB" }}>
        <span className="ci-loraa-disclaimer" style={{ display: "flex", gap: 7, alignItems: "flex-start", maxWidth: 620, fontSize: 11, color: "#6b7280", lineHeight: 1.5, fontStyle: "italic", margin: 0, padding: 0, border: 0 }}>
          <Icon name="info" size={12} stroke={2.4} style={{ color: "#9b8afb", flexShrink: 0, marginTop: 1 }} />
          <span>Loraa is a supportive guide, insights, quick QA, and compliance checks only. Her suggestions never override a professional. You complete the final review before approving.</span>
        </span>
        <button className="ci-loraa-sub" style={{ display: "inline-flex", alignItems: "center", gap: 6, padding: "8px 15px", borderRadius: 9, background: "#6938EF", color: "#fff", border: 0, fontFamily: "inherit", fontSize: 12.5, fontWeight: 700, cursor: "pointer", flexShrink: 0 }} disabled={running} onClick={run}>
          {running ? <><Icon name="loader-2" size={14} className="spin" /> Scanning…</> : <><Icon name="lightbulb" size={14} /> {result ? "Re-run pre-screen" : "Run Loraa pre-screen"}</>}
        </button>
      </div>
    </div>
  );
}

// ───────────────── QUICK ASSIGN DRAWER (right slide-in) ─────────────────
// Triggered by "Request changes" across review surfaces. Lets a reviewer build a
// targeted fix task and assign an owner without leaving the page, the task lands
// in My Assignments. Slides in from the right so tasks get created quickly.
(function injectQADrawerStyles() {
  if (document.getElementById("qa-drawer-styles")) return;
  const css = `
  .qad-bg { position: fixed; inset: 0; background: rgba(10,13,18,.45); z-index: 95; animation: qad-fade .18s ease; }
  @keyframes qad-fade { from { opacity: 0; } to { opacity: 1; } }
  .qad { position: fixed; top: 0; right: 0; width: 480px; max-width: 100vw; height: 100dvh; background: #fff; box-shadow: -12px 0 40px rgba(14,22,18,.18); display: flex; flex-direction: column; animation: qad-in .28s cubic-bezier(.32,.72,.26,1); }
  @keyframes qad-in { from { transform: translateX(100%); } to { transform: translateX(0); } }
  .qad-head { padding: 18px 20px; border-bottom: 1px solid var(--gray-100); display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
  .qad-body { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 18px 20px; }
  .qad-foot { padding: 14px 20px; border-top: 1px solid var(--gray-100); display: flex; gap: 10px; justify-content: flex-end; }
  .qad-item { display: flex; align-items: center; gap: 11px; padding: 11px 12px; border: 1px solid var(--gray-200); border-radius: 11px; background: var(--gray-50); margin-bottom: 16px; }
  @media (max-width: 520px) { .qad { width: 100vw; } }
  .app[data-theme="dark"] .qad, .app[data-theme="dark"] .qad-head, .app[data-theme="dark"] .qad-foot { background: #11161B; border-color: #1F242F; }
  `;
  const s = document.createElement("style"); s.id = "qa-drawer-styles"; s.textContent = css; document.head.appendChild(s);
})();

function QuickAssignDrawer({ prefill, onClose }) {
  const { role, toast } = useApp();
  const p = prefill || {};
  const ASSIGNEES = React.useMemo(kbAssignees, []);
  const kind = p.kind === "ingredient" ? "ingredient" : "recipe";
  const item = p.item || null;
  const isRec = kind === "recipe";

  // Esc to close
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose && onClose(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [onClose]);

  // Task card builder, catalog built from the ACTUAL item: exact cooking steps,
  // ingredient lines, description, and cultural notes are all targetable.
  const { catalog, cur } = React.useMemo(() => caCatalogFor(kind, item), [kind, item]);
  const sections = Object.keys(catalog);
  const firstRow = () => { const sec = sections[0]; const [field, type] = catalog[sec][0]; return { id: "q0", sec, field, type, note: "" }; };
  const [rows, setRows] = React.useState(() => {
    if (p.field) { for (const sec of sections) { const ft = catalog[sec].find(([f]) => f === p.field); if (ft) return [{ id: "q0", sec, field: ft[0], type: ft[1], note: p.note || "" }]; } }
    return [firstRow()];
  });
  const addRow = () => { const sec = sections[0]; const [field, type] = catalog[sec][0]; setRows((r) => [...r, { id: "q" + Date.now() + r.length, sec, field, type, note: "" }]); };
  const patch = (i, patchObj) => setRows((rs) => rs.map((r, j) => { if (j !== i) return r; const next = { ...r, ...patchObj }; if (patchObj.sec) { const [field, type] = catalog[patchObj.sec][0]; next.field = field; next.type = type; } if (patchObj.field) { const ft = catalog[next.sec].find(([f]) => f === patchObj.field); if (ft) next.type = ft[1]; } return next; }));
  const removeRow = (i) => setRows((rs) => rs.filter((_, j) => j !== i));

  // Assignees, role-scoped + workload
  const allowedRoles = KB_ASSIGN_MATRIX[role] || KB_ALL_ROLES;
  const ME_BY_ROLE = { "media-contributor": "SC", "dietitian": "EN", "compliance": "DL", "manager": "AB", "admin": "AB", "super-admin": "AB" };
  const meUser = ASSIGNEES.find((a) => a.initials === ME_BY_ROLE[role]) || ASSIGNEES[0];
  const allowedBase = ASSIGNEES.filter((a) => allowedRoles.includes(a.role));
  const withMe = meUser && !allowedBase.some((a) => a.id === meUser.id) ? [meUser, ...allowedBase] : allowedBase;
  const liveContributor = item && item.contributor && item.contributor.id ? { ...item.contributor, role: item.contributor.role || "media-contributor", color: KB_AV_COLORS[0] } : null;
  const allowed = liveContributor && !withMe.some((a) => String(a.id) === String(liveContributor.id)) ? [liveContributor, ...withMe] : withMe;
  const wl = () => 0;
  const maxWl = Math.max(1, ...allowed.map(wl));
  const suggestion = allowed.length ? allowed.reduce((m, a) => (wl(a) < wl(m) ? a : m), allowed[0]) : null;
  // Default: route the change request back to the item's contributor when possible
  const contributorMatch = item && item.contributor ? allowed.find((a) => a.initials === item.contributor.initials) : null;
  const [assigneeId, setAssigneeId] = React.useState(() => (contributorMatch || suggestion || allowed[0] || {}).id);

  const [priority, setPriority] = React.useState((item && item.priority) || "high");
  const [due, setDue] = React.useState("");
  const [message, setMessage] = React.useState("");
  const [submitting, setSubmitting] = React.useState(false);

  const submit = async () => {
    if (!item || submitting) return;
    setSubmitting(true);
    const a = allowed.find((x) => x.id === assigneeId);
    const id = "ca-" + Date.now();
    const checklist = rows.map((r) => ({ t: `Fix ${r.field}`, done: false, loc: { sec: r.sec, field: r.field, type: r.type, cur: cur[r.field] || "(see item)", hint: r.note || `Correct the ${r.field.toLowerCase()} in ${r.sec}.` } }));
    const card = {
      id, item: item ? item.name : "Item", kind,
      task: rows.length > 1 ? `${rows.length} fixes` : (rows[0] ? `Fix ${rows[0].field}` : "Review"),
      status: "not-started", board: "Quick assign", stage: "Requested",
      priority, due: due ? "Jun " + new Date(due).getDate() : "Jun 20", overdue: false,
      by: meUser ? meUser.name : "You", checklist, comments: message.trim() ? 1 : 0, attachments: 0,
      assigneeInitials: a ? a.initials : undefined, note: message,
    };
    try {
      if (item.__remote && p.reason === "changes") {
        const payload = { message: message.trim(), priority, due_date: due || null, assignee: a ? a.id : null, tasks: checklist };
        if (kind === "recipe") {
          if (!window.NutriRecipes) throw new Error("The recipe review service is unavailable.");
          await window.NutriRecipes.requestChanges(item.id, payload);
          if (window.RecipeSync) await window.RecipeSync.sync();
        } else {
          if (!window.NutriIngredients) throw new Error("The ingredient review service is unavailable.");
          await window.NutriIngredients.requestChanges(item.id, payload);
          if (window.IngredientSync) await window.IngredientSync.sync();
        }
      }
      toast(`Changes requested${a ? " · assigned to " + a.name : ""}`);
      onClose && onClose();
    } catch (error) {
      const data = error && error.data;
      const detail = data && (data.detail || (data.error && data.error.message));
      toast(detail || (error && error.message) || "Changes could not be requested.");
    } finally {
      setSubmitting(false);
    }
  };

  return ReactDOM.createPortal(
    <div className="qad-bg" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose && onClose(); }}>
      <div className="qad" role="dialog" aria-label="Request changes">
        <div className="qad-head">
          <div style={{ minWidth: 0 }}>
            <span className="pill" style={{ background: "#F4F3FF", color: "#5925DC", border: "1px solid #E0DBFB", marginBottom: 8 }}><Icon name="message-square" size={11} stroke={2.4} /> Request changes</span>
            <h3 style={{ margin: 0, fontFamily: "var(--serif)", fontSize: 22, letterSpacing: "-.01em" }}>Quick assign</h3>
            <p style={{ margin: "4px 0 0", color: "var(--gray-600)", fontSize: 13 }}>Target the exact fields to fix and assign an owner, the task lands in their queue.</p>
          </div>
          <button className="icon-btn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>

        <div className="qad-body">
          {/* Item being reviewed */}
          {item && (
            <div className="qad-item">
              {isRec
                ? <div className="thumb sm" style={{ backgroundImage: `url("${item.cover || item.image || ''}")` }} />
                : (item.image || item.cover)
                  ? <div className="thumb sm" style={{ backgroundImage: `url("${item.image || item.cover}")`, backgroundSize: "cover", backgroundPosition: item.imagePos ? `${item.imagePos.x}% ${item.imagePos.y}%` : "center" }} />
                  : <div className="thumb sm" style={{ background: "linear-gradient(135deg, #DCEEC1, #C9E2A4)", display: "grid", placeItems: "center", color: "var(--green-700)" }}><Icon name="leaf" size={18} stroke={1.8} /></div>}
              <div style={{ minWidth: 0 }}>
                <div style={{ fontWeight: 700, fontSize: 14 }}>{item.name}</div>
                <div className="muted" style={{ fontSize: 12 }}>{isRec ? `${item.cuisine} · ${item.category}` : `${item.canonical} · ${item.category}`}</div>
              </div>
              <span className="pill" style={{ marginLeft: "auto", ...(isRec ? { background: "var(--green-50)", color: "var(--green-700)", border: "1px solid var(--green-200)" } : { background: "#EEF4FF", color: "#3538CD", border: "1px solid #C7D7FE" }) }}>
                <Icon name={isRec ? "utensils-crossed" : "leaf"} size={11} stroke={2.4} /> {isRec ? "Recipe" : "Ingredient"}
              </span>
            </div>
          )}

          {/* Task builder */}
          <div className="field">
            <label>What needs fixing? <span className="muted" style={{ fontWeight: 500 }}>· target the step &amp; field</span></label>
            <div className="qa-tasks">
              {rows.map((r, i) => {
                const secKey = catalog[r.sec] ? r.sec : sections[0];
                const fieldOpts = catalog[secKey] || [];
                const fieldVal = fieldOpts.some(([f]) => f === r.field) ? r.field : (fieldOpts[0] && fieldOpts[0][0]);
                return (
                  <div key={r.id} className="cu-task-card">
                    <div className="cu-task-top">
                      <span className="cu-sub-ic"><Icon name="circle" size={14} /></span>
                      <span className={`ca-type-badge t-${r.type}`}>{r.type}</span>
                      {rows.length > 1 && <button type="button" className="cu-sub-del" onClick={() => removeRow(i)} title="Remove" style={{ marginLeft: "auto" }}><Icon name="trash-2" size={13} /></button>}
                    </div>
                    <TaskFieldDrill catalog={catalog} cur={cur} sec={secKey} field={fieldVal}
                      onPick={(s, f, t) => setRows(rs => rs.map((row, j) => j === i ? { ...row, sec: s, field: f, type: t } : row))} />
                    <input className="cu-sub-note" value={r.note} onChange={(e) => patch(i, { note: e.target.value })} placeholder="What to look into…" />
                  </div>
                );
              })}
              <button type="button" className="cu-add-task" onClick={addRow}><Icon name="plus" size={15} /> Add task</button>
            </div>
          </div>

          {/* Assignee */}
          <div className="field">
            <label>Assign to <span className="muted" style={{ fontWeight: 500 }}>· {ROLE_LABEL[role] || "You"} can assign to {allowedRoles.map((r) => ROLE_LABEL[r]).join(", ")}</span></label>
            {suggestion && (
              <button type="button" className="kb-suggest" onClick={() => setAssigneeId(suggestion.id)}>
                <Icon name="wand-2" size={14} />
                <span><strong>{suggestion.name}</strong> has the lightest load ({wl(suggestion)} active), suggested to balance the team.</span>
                {assigneeId === suggestion.id ? <span className="kb-suggest-on"><Icon name="check" size={13} /> Selected</span> : <span className="kb-suggest-cta">Use</span>}
              </button>
            )}
            <div className="kb-assignee-grid card" style={{ padding: 5 }}>
              {allowed.length === 0 && <div className="muted" style={{ padding: 12, fontSize: 13 }}>No eligible assignees for your role.</div>}
              {[...allowed].sort((a, b) => wl(a) - wl(b)).map((a) => {
                const n = wl(a);
                const loadClass = n === 0 ? "idle" : n >= maxWl && maxWl > 1 ? "high" : "ok";
                return (
                  <button key={a.id} type="button" className={`kb-assignee-opt ${assigneeId === a.id ? "on" : ""}`} onClick={() => setAssigneeId(a.id)}>
                    <span className="kb-av" style={{ background: a.color, width: 28, height: 28 }}>{a.initials}</span>
                    <span style={{ flex: 1, textAlign: "left", minWidth: 0 }}>
                      <span style={{ display: "block", fontSize: 13, fontWeight: 700 }}>{a.name} {contributorMatch && a.id === contributorMatch.id && <span className="kb-suggest-tag" style={{ background: "var(--green-100)", color: "var(--green-800)" }}>Contributor</span>}</span>
                      <span className="muted" style={{ fontSize: 11 }}>{ROLE_LABEL[a.role] || a.role}</span>
                    </span>
                    <span className="kb-load-mini">
                      <span className={`kb-load-count ${loadClass}`} style={{ fontSize: 10.5 }}>{n === 0 ? "free" : n + " active"}</span>
                      <span className="kb-load-bar sm"><i className={loadClass} style={{ width: `${(n / maxWl) * 100}%` }} /></span>
                    </span>
                    {assigneeId === a.id && <Icon name="check" size={16} style={{ color: "var(--green-700)", flexShrink: 0 }} />}
                  </button>
                );
              })}
            </div>
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
            <div className="field">
              <label>Priority</label>
              <select className="select" value={priority} onChange={(e) => setPriority(e.target.value)}>
                <option value="high">High</option><option value="medium">Medium</option><option value="low">Low</option>
              </select>
            </div>
            <div className="field">
              <label>Due date</label>
              <input type="date" className="input" value={due} onChange={(e) => setDue(e.target.value)} />
            </div>
          </div>

          <div className="field" style={{ marginBottom: 0 }}>
            <label>Message to contributor <span className="muted" style={{ fontWeight: 500 }}>· optional</span></label>
            <textarea className="textarea" rows="3" value={message} onChange={(e) => setMessage(e.target.value)} placeholder="Explain what needs to change…" />
          </div>
        </div>

        <div className="qad-foot">
          <button className="btn ghost" onClick={onClose}>Cancel</button>
          <button className="btn primary" disabled={!item || submitting} onClick={submit}>{submitting ? <Icon name="loader-2" size={15} className="spin" /> : <Icon name="user-plus" size={15} />} {submitting ? "Requesting…" : "Request & assign"}</button>
        </div>
      </div>
    </div>, document.body);
}

Object.assign(window, { AnalyticsScreen, PublishingCalendar, AssignmentsScreen: LegacyAssignmentsScreen, UrgentItems, PublishedRecipes, NutritionReview, QuickAssignDrawer });
