// GUDID match approval view - Phase B.2.6 UI surface.

const { useEffect: useEffectGudid, useMemo: useMemoGudid, useState: useStateGudid, useRef: useRefGudid } = React;

const ADD_TO_LIBRARY_TOOLTIP = "GUDID covers about 30% of OR supplies. Items here are normal - flag them to expand the catalog.";
const GUDID_EMBEDDED_APP_SHELL = new URLSearchParams(window.location.search).get("embed") === "shell";

// Live card header helpers:
function firstLiveCardText(...values) {
  for (const value of values) {
    const text = String(value || "").replace(/\s+/g, " ").trim();
    if (text) return text;
  }
  return "";
}

function cardWithLiveHeader(card = {}) {
  const live = window.PREFCARD_LIVE_CARD || {};
  return {
    ...card,
    name: firstLiveCardText(live.procedure_name, card.name),
    maintainer: firstLiveCardText(live.surgeon_name, card.maintainer, "Maintainer unknown"),
    maintainerRole: firstLiveCardText(live.surgeon_specialty, live.surgeon_title, card.maintainerRole),
    maintainerInst: firstLiveCardText(live.facility_name, card.maintainerInst),
    lastEdited: firstLiveCardText(String(live.last_updated || "").slice(0, 10), card.lastEdited),
  };
}
// Live card header helpers end.

const MATCH_COPY = {
  block: {
    label: "Add to library",
    detail: "Catalog expansion needed",
    fg: "var(--ss-match-reject)",
    bg: "var(--ss-match-reject-bg)",
    bd: "#DAB5AC",
    icon: "alert",
  },
  reject: {
    label: "Reject",
    detail: "Manual catalog entry required",
    fg: "var(--ss-match-reject)",
    bg: "var(--ss-match-reject-bg)",
    bd: "#DAB5AC",
    icon: "alert",
  },
  no_match: {
    label: "No match",
    detail: "Add to library",
    fg: "var(--ss-match-nomatch)",
    bg: "var(--ss-match-nomatch-bg)",
    bd: "#D6D0C5",
    icon: "x",
  },
  review: {
    label: "Amber review",
    detail: "Verify before save",
    fg: "var(--ss-match-review)",
    bg: "var(--ss-match-review-bg)",
    bd: "#DEC2B2",
    icon: "review",
  },
  auto: {
    label: "Confirm badge",
    detail: "Auto-accept",
    fg: "var(--ss-match-auto)",
    bg: "var(--ss-match-auto-bg)",
    bd: "#B9D0BE",
    icon: "check",
  },
  auto_pending: {
    label: "Confirm badge",
    detail: "Auto-pending UI confirm",
    fg: "var(--ss-match-auto)",
    bg: "var(--ss-match-auto-bg)",
    bd: "#B9D0BE",
    icon: "check",
  },
  verified: {
    label: "GUDID verified",
    detail: "High confidence match",
    fg: "var(--ss-match-verified)",
    bg: "var(--ss-match-verified-bg)",
    bd: "#B9D0BE",
    icon: "check",
  },
};

const GUDID_ROOM_DEFAULT_RULES = [
  {
    id: "esu",
    label: "ESU",
    defaultLabel: "ESU assumed in room",
    accessoryPattern: /\b(bovie|cautery|electrosurgical)\b.*\b(pencil|tip|cord|pad|blade)\b|\b(pencil|tip)\b.*\b(bovie|cautery|electrosurgical)\b/i,
    equipmentPattern: /\b(esu|electrosurgical unit|cautery generator|bovie generator|valleylab|force triad)\b/i,
    targetLabels: ["boom", "equipment cart", "Bovie tower"],
    bodyTemplate: (sourceName) =>
      `This item (${sourceName}) is an ESU accessory, but no electrosurgical unit is listed on the card. The ESU is assumed to be a room default.`,
  },
  {
    id: "suction",
    label: "Suction",
    defaultLabel: "Suction assumed in room",
    accessoryPattern: /\b(yankauer|poole|suction tubing|suction tip|suction canister|suction liner)\b/i,
    equipmentPattern: /\b(suction machine|suction pump|suction tower|suction regulator|wall suction)\b/i,
    targetLabels: ["boom", "equipment cart", "suction tower"],
    bodyTemplate: (sourceName) =>
      `This item (${sourceName}) is a suction accessory, but no suction equipment is listed on the card. Suction is assumed to be a room default.`,
  },
];

function itemSearchText(item) {
  return [item?.item_name, item?.name, item?.raw_text].filter(Boolean).join(" ").toLowerCase();
}

function itemDisplayName(item) {
  return item?.item_name || item?.name || item?.raw_text || "this item";
}

function itemRoomDefault(item, cardItems) {
  if (!item || !Array.isArray(cardItems)) return null;
  const itemText = itemSearchText(item);
  for (const rule of GUDID_ROOM_DEFAULT_RULES) {
    if (!rule.accessoryPattern.test(itemText)) continue;
    const hasExplicitEquipment = cardItems.some((other) =>
      other !== item && rule.equipmentPattern.test(itemSearchText(other))
    );
    if (hasExplicitEquipment) continue;
    const sourceName = itemDisplayName(item);
    return { rule, sourceName, body: rule.bodyTemplate(sourceName) };
  }
  return null;
}

function getGudidBand(item) {
  const match = item.gudid_match;
  if (!match) return "no_match";
  // Scanner-bound items have gudid_match populated from FDA GUDID lookup but
  // never ran through the text-matcher, so confidence is null. Trust the
  // barcode binding (user explicitly scanned) and treat as verified. Anchored
  // in Ada's PR #1164 edge-case 2026-05-22T~17:35Z.
  if (match.match_confidence == null) return "verified";
  if (match.band) return match.band;
  if (match.match_confidence < 0.5) return "block";
  if (match.match_confidence < 0.85) return "review";
  if (match.match_confidence < 0.9) return "auto_pending";
  return "verified";
}

function percent(value) {
  if (typeof value !== "number") return "0%";
  return `${Math.round(value * 100)}%`;
}

function prettifyPath(path) {
  return (path || "no_match").replaceAll("_", " ");
}

// CAT grouping helpers: keep this block JSX-free so node tests can eval it.
const PREFCARD_SECTION_ORDER = [
  "prep",
  "position",
  "drapes",
  "sterile_supplies",
  "instruments",
  "rep_trays",
  "equipment",
  "meds",
  "suture",
  "dressing",
  "hold",
  "notes",
];
const LEGACY_PHASE_SECTION = { setup: "sterile_supplies", incision: "instruments", close: "dressing" };
const CAT_COLLAPSE_THRESHOLD = 5;

function labelForCardSection(section) {
  return (section || "notes").replaceAll("_", " ").replace(/\b\w/g, (char) => char.toUpperCase());
}

function quantityValue(value) {
  const qty = Number(value);
  return Number.isFinite(qty) ? qty : 0;
}

function sectionMapFromCardPayload(payload) {
  const byId = new Map();
  for (const [section, sectionItems] of Object.entries(payload?.sections || {})) {
    if (!Array.isArray(sectionItems)) continue;
    for (const item of sectionItems) {
      if (item?.id == null) continue;
      byId.set(String(item.id), section);
      byId.set(`card-item-${item.id}`, section);
    }
  }
  return byId;
}

function sectionKeyForItem(item, sectionMap = new Map()) {
  const mapped = sectionMap.get(String(item?.card_item_id)) || sectionMap.get(String(item?.id));
  const raw = mapped || item?.section_key || item?.section_enum || item?.card_section || item?.section || "notes";
  if (PREFCARD_SECTION_ORDER.includes(raw)) return raw;
  return LEGACY_PHASE_SECTION[raw] || "notes";
}

function itemAppearsOpen(item) {
  const open = quantityValue(item?.qty_open);
  const hold = quantityValue(item?.qty_hold);
  return open > 0 || open + hold === 0;
}

function itemAppearsHold(item) {
  return quantityValue(item?.qty_hold) > 0;
}

function sortSectionKeys(a, b) {
  const ai = PREFCARD_SECTION_ORDER.indexOf(a);
  const bi = PREFCARD_SECTION_ORDER.indexOf(b);
  return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi) || a.localeCompare(b);
}

function buildCategoryGroups(items, cardPayload) {
  const sectionMap = sectionMapFromCardPayload(cardPayload);
  const buckets = { open: new Map(), hold: new Map() };
  for (const item of items || []) {
    const section = sectionKeyForItem(item, sectionMap);
    const targets = [];
    if (itemAppearsOpen(item)) targets.push("open");
    if (itemAppearsHold(item)) targets.push("hold");
    for (const target of targets) {
      if (!buckets[target].has(section)) buckets[target].set(section, []);
      buckets[target].get(section).push(item);
    }
  }
  const finalize = (map) => ({
    total: Array.from(map.values()).reduce((sum, sectionItems) => sum + sectionItems.length, 0),
    sections: Array.from(map.keys()).sort(sortSectionKeys).map((section) => ({
      key: section,
      label: labelForCardSection(section),
      items: map.get(section),
      defaultCollapsed: map.get(section).length > CAT_COLLAPSE_THRESHOLD,
    })),
  });
  return { open: finalize(buckets.open), hold: finalize(buckets.hold) };
}
// CAT grouping helpers end.

function GudidMatchApprovalView({ mobile = false, viewTabs = null }) {
  // Live source built from real /api/cards/{id} payload (PR #1164 + post-PR
  // projection in data.js::mapApiToGudidMatchLive). The companion
  // PREFCARD_GUDID_MATCH_RESPONSE is retained for view-primitive.jsx's
  // design-system band illustration only.
  const [cardSnapshot, setCardSnapshot] = useStateGudid(window.PREFCARD);
  const [liveResponse, setLiveResponse] = useStateGudid(window.PREFCARD_GUDID_MATCH_LIVE || window.PREFCARD_GUDID_MATCH_RESPONSE);
  const [selectedFiles, setSelectedFiles] = useStateGudid([]);
  const [uploadResult, setUploadResult] = useStateGudid(window.PREFCARD_UPLOAD_RESULT || null);
  const [uploadState, setUploadState] = useStateGudid({ phase: "idle", message: "" });
  const [approveState, setApproveState] = useStateGudid({ phase: "idle", message: "" });
  const response = liveResponse || window.PREFCARD_GUDID_MATCH_RESPONSE;
  const items = response.matched_items || [];
  const [filter, setFilter] = useStateGudid("all");
  const [detailItem, setDetailItem] = useStateGudid(null);
  const [verifiedIds, setVerifiedIds] = useStateGudid(new Set());
  const [manualNames, setManualNames] = useStateGudid({});
  const [driftChoice, setDriftChoice] = useStateGudid({});
  const [activeQtyTab, setActiveQtyTab] = useStateGudid("open");

  const stats = useMemoGudid(() => summarizeGudidItems(items), [items]);
  const visibleItems = useMemoGudid(() => items.filter((item) => {
    const band = getGudidBand(item);
    if (filter === "manual") return band === "block" || band === "reject" || band === "no_match";
    if (filter === "review") return band === "review";
    if (filter === "auto") return band === "auto" || band === "auto_pending" || band === "verified";
    return true;
  }), [items, filter]);
  const categoryGroups = useMemoGudid(
    () => buildCategoryGroups(visibleItems, window.PREFCARD_LAST_PAYLOAD),
    [visibleItems, cardSnapshot]
  );
  const headerCard = cardWithLiveHeader(cardSnapshot);

  function verifyItem(id) {
    setVerifiedIds((prev) => {
      const next = new Set(prev);
      next.add(id);
      return next;
    });
  }

  function setManualName(id, value) {
    setManualNames((prev) => ({ ...prev, [id]: value }));
  }

  async function matchUploadedItems(result) {
    const extractionItems = result?.extraction?.items || [];
    const supplyItems = extractionItems
      .filter((item) => item.section !== "notes" && (item.item_name || item.raw_text))
      .slice(0, 200);
    if (!supplyItems.length) return null;
    const response = await fetch(window.prefcardApiPath("/api/match-items"), {
      method: "POST",
      credentials: "same-origin",
      headers: { "Content-Type": "application/json", "Accept": "application/json" },
      body: JSON.stringify({
        items: supplyItems.map((item) => ({
          item_name: item.item_name || item.raw_text,
          catalog_num: item.catalog_num || null,
          vendor: item.vendor || null,
        })),
        require_active_distribution: true,
      }),
    });
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return response.json();
  }

  async function uploadSelectedFiles() {
    if (!selectedFiles.length) return;
    setUploadState({
      phase: "uploading",
      message: `Combining ${selectedFiles.length} page${selectedFiles.length === 1 ? "" : "s"} into one preference card.`,
    });
    setApproveState({ phase: "idle", message: "" });
    const form = new FormData();
    selectedFiles.forEach((file) => form.append("files", file));
    form.append("source_type", "prefcard_regen_canvas");
    try {
      const response = await fetch(window.prefcardApiPath("/api/upload/card"), {
        method: "POST",
        credentials: "same-origin",
        body: form,
      });
      const result = await response.json();
      if (!response.ok) throw new Error(result.detail || `HTTP ${response.status}`);
      let matchResponse = null;
      try {
        matchResponse = await matchUploadedItems(result);
      } catch (matchErr) {
        console.warn("[prefcard-multi-view] match-items probe failed; showing upload extraction only:", matchErr);
      }
      window.applyUploadResult(result, matchResponse);
      setCardSnapshot(window.PREFCARD);
      setLiveResponse(window.PREFCARD_GUDID_MATCH_LIVE);
      setUploadResult(result);
      setFilter("all");
      setUploadState({
        phase: "done",
        message: `Merged ${result.page_count || result.extraction?.page_count || selectedFiles.length} page${selectedFiles.length === 1 ? "" : "s"}; ${result.items_count || result.extraction?.items?.length || 0} extracted rows ready for review.`,
      });
    } catch (err) {
      setUploadState({ phase: "error", message: err.message || "Upload failed" });
    }
  }

  async function approveUpload() {
    const uploadId = uploadResult?.upload_id || cardSnapshot.uploadId;
    if (!uploadId) return;
    setApproveState({ phase: "saving", message: "Approving merged extraction." });
    try {
      const response = await fetch(window.prefcardApiPath(`/api/upload/card/${uploadId}/approve`), {
        method: "POST",
        credentials: "same-origin",
        headers: { "Content-Type": "application/json", "Accept": "application/json" },
        body: JSON.stringify({
          items: uploadResult?.extraction?.items || [],
        }),
      });
      const result = await response.json();
      if (!response.ok) throw new Error(result.detail || `HTTP ${response.status}`);
      if (result.card_id) {
        const cardResponse = await fetch(window.prefcardApiPath(`/api/cards/${result.card_id}`), {
          credentials: "same-origin",
          headers: { "Accept": "application/json" },
        });
        if (cardResponse.ok) {
          window.applyPrefcardPayload(await cardResponse.json());
          setCardSnapshot(window.PREFCARD);
          setLiveResponse(window.PREFCARD_GUDID_MATCH_LIVE);
        }
      }
      setApproveState({
        phase: "done",
        message: `${result.items_added || 0} rows approved; ${result.items_needs_review || 0} need human review.`,
      });
    } catch (err) {
      setApproveState({ phase: "error", message: err.message || "Approve failed" });
    }
  }

  function applyScanResolved(item, result) {
    const nextMatch = {
      primary_di: result.gudid_di,
      // Precedence: most-specific first. gudid_description carries the full canonical
      // product (e.g. "Stryker Performance Series Sagittal Blade") while gudid_brand
       // is a generic family label (e.g. "Stryker Performance Series"). Surgeon's
      // item_name preserves their familiar text + size + catalog# context — second
      // choice when GUDID description is missing. gudid_brand is last-resort fallback.
      canonical_name: result.gudid_description || item.item_name || result.gudid_brand || "-",
      brand_name: result.gudid_brand || "-",
      company_name: result.gudid_company || "-",
      device_description: result.gudid_description || "",
      match_confidence: result.match_confidence ?? 1,
      match_path: result.match_path || "scan",
      match_pin_source: "scan_barcode",
      band: "verified",
    };
    setLiveResponse((prev) => ({
      ...prev,
      matched_items: (prev?.matched_items || []).map((row) => (
        row.id === item.id || row.card_item_id === item.card_item_id
          ? { ...row, needs_human_review: 0, gudid_match: nextMatch }
          : row
      )),
    }));
    setDetailItem((prev) => (prev ? { ...prev, needs_human_review: 0, gudid_match: nextMatch } : prev));
  }

  return (
    <div className="ss-app" style={{ background: "var(--ss-cream)", minHeight: "100vh", position: "relative" }}>
      {!GUDID_EMBEDDED_APP_SHELL && (mobile ? <GudidMobileHeader card={headerCard} /> : <window.AppHeader />)}
      <GudidPageHeader
        mobile={mobile}
        card={headerCard}
        stats={stats}
        pendingUploadId={uploadResult?.upload_id || cardSnapshot.uploadId}
        approveState={approveState}
        onApprove={approveUpload}
      />
      {viewTabs}
      <div style={{ padding: mobile ? "12px 16px 28px" : "18px 32px 40px" }}>
        <GudidUploadPanel
          mobile={mobile}
          files={selectedFiles}
          uploadState={uploadState}
          approveState={approveState}
          uploadResult={uploadResult}
          onFiles={setSelectedFiles}
          onUpload={uploadSelectedFiles}
          onApprove={approveUpload}
        />
        <GudidNotesPanel card={cardSnapshot} mobile={mobile} />
        <GudidStatsBar stats={stats} filter={filter} onFilter={setFilter} mobile={mobile} />
        <div style={{
          marginTop: mobile ? 12 : 18,
          display: "grid",
          gridTemplateColumns: mobile ? "1fr" : "minmax(0, 1fr) 340px",
          gap: 18,
          alignItems: "start",
        }}>
          <GudidCategorizedRows
            groups={categoryGroups}
            activeTab={activeQtyTab}
            cardId={window.PREFCARD_CONFIG.cardId}
            mobile={mobile}
            verifiedIds={verifiedIds}
            manualNames={manualNames}
            driftChoice={driftChoice}
            onTab={setActiveQtyTab}
            onManualName={setManualName}
            onVerify={verifyItem}
            onDetail={setDetailItem}
            onDriftChoice={(id, value) => setDriftChoice((prev) => ({ ...prev, [id]: value }))}
          />
          {!mobile && (
            <GudidSidePanel
              stats={stats}
              selected={detailItem || items.find((item) => getGudidBand(item) === "review")}
              onDetail={setDetailItem}
            />
          )}
        </div>
      </div>
      {detailItem && (
        <GudidDetailModal
          item={detailItem}
          cardItems={items}
          onClose={() => setDetailItem(null)}
          onScanResolved={applyScanResolved}
        />
      )}
    </div>
  );
}

function GudidMobileHeader({ card }) {
  const maintainer = card?.maintainer || "Maintainer";
  return (
    <header style={{
      background: "var(--ss-cream)",
      borderBottom: "1px solid var(--ss-line)",
      padding: "10px 16px",
      display: "flex",
      alignItems: "center",
      gap: 10,
    }}>
      <a href="/cards" style={{ display: "inline-flex", alignItems: "center", gap: 8, textDecoration: "none", minWidth: 0 }}>
        <svg width="20" height="20" viewBox="0 0 24 24" style={{ flexShrink: 0 }}>
          <path d="M4 4 L20 4 L20 12 L12 20 L4 12 Z" fill="none" stroke="#1B1A18" strokeWidth="1.6" strokeLinejoin="round" />
          <path d="M9 9 L15 9 L15 13 L12 16 L9 13 Z" fill="#C7765A" />
        </svg>
        <span className="ss-serif" style={{ fontSize: 17, fontWeight: 600, color: "var(--ss-ink)", whiteSpace: "nowrap" }}>ScrubStack</span>
      </a>
      <div style={{ flex: 1 }} />
      <a href="/cards" style={{
        padding: "5px 9px",
        borderRadius: 4,
        background: "rgba(0,0,0,0.04)",
        color: "var(--ss-ink)",
        fontSize: 12.5,
        fontWeight: 500,
        textDecoration: "none",
        whiteSpace: "nowrap",
      }}>
        Prefcards
      </a>
      <window.Avatar name={maintainer} size={26} />
    </header>
  );
}

function GudidUploadPanel({
  mobile,
  files,
  uploadState,
  approveState,
  uploadResult,
  onFiles,
  onUpload,
  onApprove,
}) {
  const pageCount = uploadResult?.page_count || uploadResult?.extraction?.page_count;
  const itemCount = uploadResult?.items_count || uploadResult?.extraction?.items?.length;
  const busy = uploadState.phase === "uploading" || approveState.phase === "saving";

  return (
    <section style={{
      marginBottom: 12,
      background: "var(--ss-warm-white)",
      border: "1px solid var(--ss-line)",
      borderRadius: 6,
      boxShadow: "var(--ss-shadow-card)",
      padding: mobile ? 12 : 14,
      display: "grid",
      gridTemplateColumns: mobile ? "1fr" : "minmax(0, 1fr) auto",
      gap: mobile ? 12 : 16,
      alignItems: "start",
    }}>
      <div style={{ minWidth: 0 }}>
        <div className="ss-caps" style={{ color: "var(--ss-terra-deep)", marginBottom: 6 }}>Multi-page upload</div>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center" }}>
          <input
            aria-label="Upload preference card pages"
            type="file"
            multiple
            accept="image/*,.pdf"
            onChange={(event) => onFiles(Array.from(event.target.files || []))}
            style={{
              maxWidth: mobile ? "100%" : 260,
              fontFamily: "var(--ss-sans)",
              fontSize: 12.5,
              color: "var(--ss-gray-900)",
            }}
          />
          <button disabled={!files.length || busy} onClick={onUpload} style={{
            ...gudidPrimaryButton,
            opacity: !files.length || busy ? 0.55 : 1,
            cursor: !files.length || busy ? "not-allowed" : "pointer",
          }}>
            Combine pages
          </button>
          {(uploadResult || uploadState.phase === "done") && (
            <button disabled={!uploadResult || busy} onClick={onApprove} style={{
              ...gudidGhostButton,
              opacity: !uploadResult || busy ? 0.55 : 1,
              cursor: !uploadResult || busy ? "not-allowed" : "pointer",
            }}>
              Approve upload
            </button>
          )}
        </div>
        {files.length > 0 && (
          <div style={{
            marginTop: 10,
            display: "grid",
            gridTemplateColumns: mobile ? "1fr" : "repeat(3, minmax(0, 1fr))",
            gap: 6,
          }}>
            {files.map((file, index) => (
              <div key={`${file.name}-${index}`} style={{
                border: "1px solid var(--ss-line-soft)",
                borderRadius: 5,
                background: "var(--ss-cream)",
                padding: "7px 9px",
                minWidth: 0,
              }}>
                <div className="ss-caps" style={{ color: "var(--ss-gray-500)", marginBottom: 2 }}>Page {index + 1}</div>
                <div style={{ fontSize: 12.5, color: "var(--ss-ink)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{file.name}</div>
              </div>
            ))}
          </div>
        )}
      </div>
      <div style={{
        borderLeft: mobile ? "none" : "1px solid var(--ss-line)",
        paddingLeft: mobile ? 0 : 14,
        minWidth: mobile ? 0 : 190,
      }}>
        <HeaderMetric label="Pages" value={pageCount || files.length || "-"} />
        <div style={{ height: 8 }} />
        <HeaderMetric label="Rows" value={itemCount || "-"} />
        {(uploadState.message || approveState.message) && (
          <div style={{
            marginTop: 10,
            fontSize: 12,
            lineHeight: 1.35,
            color: uploadState.phase === "error" || approveState.phase === "error" ? "var(--ss-match-reject)" : "var(--ss-gray-700)",
          }}>
            {approveState.message || uploadState.message}
          </div>
        )}
      </div>
    </section>
  );
}

function GudidNotesPanel({ card, mobile }) {
  const historyNotes = card.historyNotes || [];
  const clinicalNotes = card.clinicalNotes || [];
  if (!historyNotes.length && !clinicalNotes.length) return null;

  return (
    <section style={{
      marginBottom: 12,
      display: "grid",
      gridTemplateColumns: mobile ? "1fr" : "1fr 1fr",
      gap: 12,
    }}>
      {historyNotes.length > 0 && (
        <NoteBucket title="Card History / EHR Notes" count={historyNotes.length} mobile={mobile}>
          {historyNotes.map((note) => (
            <li key={note.id} style={{ marginBottom: 5 }}>
              <span style={{ color: note.is_warning ? "var(--ss-match-reject)" : "var(--ss-gray-900)" }}>{note.note_text}</span>
            </li>
          ))}
        </NoteBucket>
      )}
      {clinicalNotes.length > 0 && (
        <NoteBucket title="Clinical instructions" count={clinicalNotes.length} mobile={mobile}>
          {clinicalNotes.map((note) => (
            <li key={note.id} style={{ marginBottom: 8 }}>
              <div style={{ fontWeight: 600, color: "var(--ss-ink)", marginBottom: 2 }}>{note.item_name}</div>
              <div style={{ color: note.raw_text ? "var(--ss-gray-700)" : "var(--ss-gray-500)" }}>
                {note.raw_text || "(none captured)"}
              </div>
            </li>
          ))}
        </NoteBucket>
      )}
    </section>
  );
}

function NoteBucket({ title, count, children, mobile }) {
  return (
    <details open={!mobile} style={{
      background: "var(--ss-warm-white)",
      border: "1px solid var(--ss-line)",
      borderRadius: 6,
      boxShadow: "var(--ss-shadow-card)",
      padding: mobile ? 10 : 12,
      minWidth: 0,
    }}>
      <summary style={{
        cursor: "pointer",
        listStyle: "none",
        display: "flex",
        alignItems: "center",
        justifyContent: "space-between",
        gap: 8,
      }}>
        <span className="ss-caps" style={{ color: "var(--ss-terra-deep)" }}>{title}</span>
        <span className="ss-mono" style={{ fontSize: 12, color: "var(--ss-gray-700)" }}>{count}</span>
      </summary>
      <ul style={{
        margin: "10px 0 0",
        paddingLeft: 18,
        maxHeight: mobile ? 150 : 190,
        overflow: "auto",
        fontSize: 12.5,
        lineHeight: 1.35,
      }}>
        {children}
      </ul>
    </details>
  );
}

function summarizeGudidItems(items) {
  return items.reduce((acc, item) => {
    const band = getGudidBand(item);
    acc.total += 1;
    if (band === "block" || band === "reject" || band === "no_match") acc.manual += 1;
    if (band === "review") acc.review += 1;
    if (band === "auto" || band === "auto_pending" || band === "verified") acc.auto += 1;
    if (item.gudid_match) acc.matched += 1;
    return acc;
  }, { total: 0, matched: 0, auto: 0, review: 0, manual: 0, noMatch: 0 });
}

function GudidPageHeader({ mobile, card, stats, pendingUploadId, approveState, onApprove }) {
  const canApprove = Boolean(pendingUploadId) && approveState.phase !== "saving";
  return (
    <div style={{ padding: mobile ? "16px 16px 14px" : "28px 32px 18px", borderBottom: "1px solid var(--ss-line)" }}>
      <div style={{ display: "flex", alignItems: mobile ? "stretch" : "flex-start", flexDirection: mobile ? "column" : "row", gap: mobile ? 12 : 28 }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="ss-caps" style={{ color: "var(--ss-terra-deep)", marginBottom: 8 }}>
            Prefcard regen approval
          </div>
          <h1 className="ss-serif" style={{
            margin: 0,
            fontSize: mobile ? 24 : 34,
            fontWeight: 500,
            lineHeight: 1.1,
            letterSpacing: "-0.015em",
            color: "var(--ss-ink)",
          }}>
            {card.name} <span style={{ color: "var(--ss-gray-500)" }}>with GUDID match review</span>
          </h1>
          <div style={{ display: "flex", alignItems: "center", gap: 12, marginTop: 12, flexWrap: "wrap" }}>
            <window.Avatar name={card.maintainer} size={28} />
            <div style={{ fontSize: 13, color: "var(--ss-gray-900)" }}>
              {card.maintainer}
              <div style={{ fontSize: 12, color: "var(--ss-gray-700)" }}>{card.maintainerInst}</div>
            </div>
            <div style={{ width: 1, height: 28, background: "var(--ss-line)" }} />
            <HeaderMetric label="Vision items" value={stats.total} />
            <HeaderMetric label="Matcher" value="B.2.3" />
          </div>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
          <span className="ss-mono" style={{
            border: "1px solid var(--ss-line)",
            background: "var(--ss-warm-white)",
            borderRadius: 5,
            padding: "7px 10px",
            fontSize: 12,
            color: "var(--ss-gray-700)",
          }}>
            {pendingUploadId ? `upload ${pendingUploadId}` : `card ${window.PREFCARD_CONFIG.cardId}`}
          </span>
          <button disabled={!canApprove} onClick={onApprove} style={{
            ...gudidPrimaryButton,
            opacity: canApprove ? 1 : 0.55,
            cursor: canApprove ? "pointer" : "not-allowed",
          }}>
            Approve upload
          </button>
        </div>
      </div>
      <window.ProvenanceFooter
        source="gudid-match"
        freshness={card.freshness}
        sha={card.sha}
        version="B.2.6"
        style={{ marginTop: 14 }}
      />
    </div>
  );
}

function HeaderMetric({ label, value }) {
  return (
    <div style={{ fontSize: 12, color: "var(--ss-gray-700)" }}>
      <div className="ss-caps" style={{ fontSize: 10, marginBottom: 2, color: "var(--ss-gray-500)" }}>{label}</div>
      <span className="ss-mono" style={{ fontSize: 13, color: "var(--ss-ink)", fontWeight: 600 }}>{value}</span>
    </div>
  );
}

function GudidStatsBar({ stats, filter, onFilter, mobile }) {
  const segments = [
    { id: "all", label: "Total", value: stats.total, tone: "neutral" },
    { id: "auto", label: "Auto-accept", value: stats.auto, tone: "verified" },
    { id: "review", label: "Amber review", value: stats.review, tone: "review" },
    { id: "manual", label: "Add to library", value: stats.manual, tone: "reject", title: ADD_TO_LIBRARY_TOOLTIP },
  ];
  return (
    <div style={{
      background: "var(--ss-warm-white)",
      border: "1px solid var(--ss-line)",
      borderRadius: 6,
      padding: mobile ? 10 : 12,
      display: "grid",
      gridTemplateColumns: mobile ? "1fr 1fr" : "repeat(4, minmax(0, 1fr))",
      gap: 8,
    }}>
      {segments.map((segment) => (
        <button
          key={segment.id}
          onClick={() => onFilter(segment.id)}
          style={{
            ...statButtonTone(segment.tone),
            borderRadius: 5,
            border: filter === segment.id ? "1px solid currentColor" : "1px solid var(--ss-line-soft)",
            padding: mobile ? "9px 10px" : "10px 12px",
            textAlign: "left",
            cursor: "pointer",
            fontFamily: "var(--ss-sans)",
          }}
          title={segment.title}
        >
          <span className="ss-mono" style={{ display: "block", fontSize: 20, fontWeight: 600, lineHeight: 1 }}>{segment.value}</span>
          <span style={{ display: "block", marginTop: 3, fontSize: 11.5, fontWeight: 500 }}>{segment.label}</span>
        </button>
      ))}
    </div>
  );
}

function statButtonTone(tone) {
  if (tone === "verified") return { color: "var(--ss-match-verified)", background: "var(--ss-match-verified-bg)" };
  if (tone === "review") return { color: "var(--ss-match-review)", background: "var(--ss-match-review-bg)" };
  if (tone === "reject") return { color: "var(--ss-match-reject)", background: "var(--ss-match-reject-bg)" };
  return { color: "var(--ss-gray-900)", background: "var(--ss-cream)" };
}

function GudidCategorizedRows({
  groups,
  activeTab,
  cardId,
  mobile,
  verifiedIds,
  manualNames,
  driftChoice,
  onTab,
  onManualName,
  onVerify,
  onDetail,
  onDriftChoice,
}) {
  const activeGroup = groups[activeTab] || groups.open;
  const tabs = [
    { id: "open", label: "Open", group: groups.open },
    { id: "hold", label: "Hold", group: groups.hold },
  ];
  return (
    <div className="prefcard-cat-panel" data-prefcard-cat-ui="open-hold-section-accordion">
      <div role="tablist" aria-label="Open and hold items" className="prefcard-cat-tabs">
        {tabs.map((tab) => (
          <button
            key={tab.id}
            type="button"
            role="tab"
            aria-selected={activeTab === tab.id}
            data-cat-tab={tab.id}
            onClick={() => onTab(tab.id)}
            className={`prefcard-cat-tab ${activeTab === tab.id ? "is-active" : ""}`}
          >
            {tab.label} <span className="ss-mono">({tab.group.total})</span>
          </button>
        ))}
      </div>
      <div className="prefcard-cat-sections">
        {activeGroup.sections.length ? activeGroup.sections.map((section) => (
          <GudidSectionAccordion
            key={section.key}
            section={section}
            cardId={cardId}
            mobile={mobile}
            verifiedIds={verifiedIds}
            manualNames={manualNames}
            driftChoice={driftChoice}
            onManualName={onManualName}
            onVerify={onVerify}
            onDetail={onDetail}
            onDriftChoice={onDriftChoice}
          />
        )) : (
          <div className="prefcard-cat-empty">No {activeTab} items match the current GUDID filter.</div>
        )}
      </div>
    </div>
  );
}

function GudidSectionAccordion({
  section,
  cardId,
  mobile,
  verifiedIds,
  manualNames,
  driftChoice,
  onManualName,
  onVerify,
  onDetail,
  onDriftChoice,
}) {
  const storageKey = `prefcard-cat:${cardId}:${section.key}`;
  const [collapsed, setCollapsed] = useStateGudid(() => {
    try {
      const saved = window.sessionStorage.getItem(storageKey);
      if (saved != null) return saved === "1";
    } catch (_) {}
    return section.defaultCollapsed;
  });

  useEffectGudid(() => {
    try {
      const saved = window.sessionStorage.getItem(storageKey);
      setCollapsed(saved != null ? saved === "1" : section.defaultCollapsed);
    } catch (_) {
      setCollapsed(section.defaultCollapsed);
    }
  }, [storageKey, section.defaultCollapsed]);

  function toggle() {
    setCollapsed((prev) => {
      const next = !prev;
      try { window.sessionStorage.setItem(storageKey, next ? "1" : "0"); } catch (_) {}
      return next;
    });
  }

  return (
    <section className="prefcard-cat-section" data-cat-section={section.key}>
      <button
        type="button"
        className="prefcard-cat-section-head"
        aria-expanded={!collapsed}
        onClick={toggle}
      >
        <span aria-hidden="true" className="prefcard-cat-arrow">{collapsed ? "▸" : "▾"}</span>
        <span>{section.label}</span>
        <span className="prefcard-cat-count ss-mono">{section.items.length}</span>
      </button>
      {!collapsed && (
        <div className="prefcard-cat-section-items">
          {section.items.map((item) => (
            <GudidMatchRow
              key={item.id}
              item={item}
              sectionKey={section.key}
              mobile={mobile}
              verified={verifiedIds.has(item.id)}
              manualName={manualNames[item.id] || ""}
              driftChoice={driftChoice[item.id]}
              onManualName={onManualName}
              onVerify={onVerify}
              onDetail={onDetail}
              onDriftChoice={onDriftChoice}
            />
          ))}
        </div>
      )}
    </section>
  );
}

function GudidMatchRow({
  item,
  sectionKey,
  mobile,
  verified,
  manualName,
  driftChoice,
  onManualName,
  onVerify,
  onDetail,
  onDriftChoice,
}) {
  const band = getGudidBand(item);
  const match = item.gudid_match;
  const meta = MATCH_COPY[band];
  const needsManual = band === "block" || band === "reject" || band === "no_match";
  const needsVerify = band === "review" && !verified;

  return (
    <article style={{
      background: "var(--ss-warm-white)",
      border: `1px solid ${band === "verified" ? "var(--ss-line)" : meta.bd}`,
      borderLeft: `4px solid ${meta.fg}`,
      borderRadius: 6,
      boxShadow: "var(--ss-shadow-card)",
      padding: mobile ? 12 : 14,
      display: "grid",
      gridTemplateColumns: mobile ? "1fr" : "64px minmax(0, 1fr) auto",
      gap: mobile ? 12 : 14,
      alignItems: "start",
    }}>
      <window.ProductGlyph supply={supplyGlyphSeed(item)} size={mobile ? 52 : 64} />
      <div style={{ minWidth: 0 }}>
        <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 10 }}>
          <CanonicalNameBlock item={item} manualName={manualName} />
          {mobile && <MatchConfidenceChip item={item} verified={verified} onOpen={onDetail} compact />}
        </div>
        <div style={{ marginTop: 8, display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center" }}>
          <SectionChip section={sectionKey || item.section} />
          <window.TokenChip value={item.catalog_num || "catalog blank"} label={item.catalog_num || "no catalog"} size="sm" tone={needsManual ? "terra" : "ghost"} />
          {match && <window.TokenChip value={match.primary_di} label={`DI ${match.primary_di.slice(-6)}`} size="sm" tone="sage" copy />}
          <span style={{ fontSize: 11.5, color: "var(--ss-gray-700)" }}>
            qty {item.qty_open} open / {item.qty_hold} hold
          </span>
        </div>
        {item.notes && (
          <div style={{
            marginTop: 7,
            fontSize: 12.5,
            lineHeight: 1.35,
            color: "var(--ss-gray-700)",
          }}>
            {item.notes}
          </div>
        )}
        {band === "review" && (
          <DiffPanel item={item} verified={verified} />
        )}
        {needsManual && (
          <ManualEntryPanel item={item} value={manualName} onChange={onManualName} />
        )}
        {match?.drift_detected && (
          <DriftPanel item={item} choice={driftChoice} onChoice={onDriftChoice} />
        )}
      </div>
      <div style={{
        display: "flex",
        flexDirection: mobile ? "row" : "column",
        gap: 8,
        alignItems: mobile ? "center" : "flex-end",
        justifyContent: mobile ? "space-between" : "flex-start",
      }}>
        {!mobile && <MatchConfidenceChip item={item} verified={verified} onOpen={onDetail} />}
        <button onClick={() => onDetail(item)} style={gudidGhostButton}>Details</button>
        {cardItemIdForProductInfo(item) && <button onClick={() => onDetail(item)} style={gudidGhostButton}>Scan</button>}
        {needsVerify && <button onClick={() => onVerify(item.id)} style={gudidPrimaryButton}>Verify</button>}
        {verified && <span className="ss-caps" style={{ color: "var(--ss-match-verified)" }}>verified</span>}
      </div>
    </article>
  );
}

function SectionChip({ section }) {
  return (
    <span style={{
      display: "inline-flex",
      alignItems: "center",
      gap: 5,
      padding: "1px 6px",
      borderRadius: 4,
      border: "1px solid rgba(0,0,0,0.06)",
      background: "rgba(0,0,0,0.025)",
      color: "var(--ss-gray-900)",
      fontSize: 10,
      fontWeight: 500,
      letterSpacing: "0.03em",
      whiteSpace: "nowrap",
    }}>
      <span style={{ width: 6, height: 6, borderRadius: 2, background: "var(--ss-sage)" }} />
      {labelForCardSection(section)}
    </span>
  );
}

function supplyGlyphSeed(item) {
  const kind = item.section === "incision" ? "implant" : item.section === "prep" ? "equipment" : "consumable";
  return { id: item.id, kind };
}

function MatchConfidenceChip({ item, verified = false, onOpen, compact = false }) {
  const rawBand = getGudidBand(item);
  const band = verified ? "verified" : rawBand;
  const match = item.gudid_match;
  const meta = MATCH_COPY[band];
  const silentVerified = rawBand === "verified" && !verified;
  const label = band === "block" || band === "reject"
    ? `Reject ${percent(match.match_confidence)}`
    : band === "no_match"
    ? "No match"
    : band === "verified"
    ? (match && match.match_confidence == null ? "Scanner" : "Verified")
    : `${band === "review" ? "Amber" : "Confirm"} ${percent(match.match_confidence)}`;

  return (
    <button
      onClick={(event) => {
        event.stopPropagation();
        onOpen && onOpen(item);
      }}
      aria-label={silentVerified ? "GUDID verified" : label}
      title={
        !match
          ? ADD_TO_LIBRARY_TOOLTIP
          : match.match_confidence == null
          ? `Scanner-bound via FDA GUDID lookup; pin source ${match.match_pin_source || "gudid"}`
          : `Match confidence ${percent(match.match_confidence)} via ${prettifyPath(match.match_path)}; pin source ${match.match_pin_source || "gudid"}`
      }
      style={{
        display: "inline-flex",
        alignItems: "center",
        gap: silentVerified ? 0 : compact ? 4 : 6,
        border: silentVerified ? "none" : `1px solid ${meta.bd}`,
        background: silentVerified ? "transparent" : meta.bg,
        color: meta.fg,
        borderRadius: 99,
        padding: silentVerified ? "4px" : compact ? "3px 7px" : "5px 9px",
        fontSize: compact ? 10.5 : 11.5,
        fontWeight: 600,
        fontFamily: "var(--ss-sans)",
        letterSpacing: "0.03em",
        textTransform: "uppercase",
        whiteSpace: "nowrap",
        cursor: "pointer",
      }}
    >
      <MatchGlyph kind={meta.icon} size={compact ? 10 : 12} />
      {!silentVerified && label}
    </button>
  );
}

function CanonicalNameBlock({ item, manualName }) {
  const band = getGudidBand(item);
  const match = item.gudid_match;
  const showDiff = band === "review";
  const title = band === "block" || band === "reject" || band === "no_match"
    ? (manualName || item.item_name)
    : match.canonical_name;

  return (
    <div style={{ minWidth: 0 }}>
      {showDiff && (
        <div style={{
          fontSize: 12.5,
          color: "var(--ss-gray-500)",
          textDecoration: "line-through",
          marginBottom: 2,
          overflow: "hidden",
          textOverflow: "ellipsis",
          whiteSpace: "nowrap",
        }}>
          {item.item_name}
        </div>
      )}
      <h3 style={{
        margin: 0,
        fontSize: 15,
        fontWeight: 600,
        lineHeight: 1.25,
        color: (band === "block" || band === "reject" || band === "no_match") && !manualName ? "var(--ss-match-reject)" : "var(--ss-ink)",
        letterSpacing: "-0.005em",
      }}>
        {title}
      </h3>
      <div style={{ marginTop: 3, fontSize: 12.5, color: "var(--ss-gray-700)" }}>
        {match ? `${match.brand_name} / ${match.company_name}` : `${item.vendor || "Unknown vendor"} / no GUDID match`}
      </div>
    </div>
  );
}

function DiffPanel({ item, verified }) {
  const match = item.gudid_match;
  return (
    <div style={{
      marginTop: 10,
      background: verified ? "var(--ss-match-verified-bg)" : "var(--ss-match-review-bg)",
      border: `1px solid ${verified ? "#B9D0BE" : "#DEC2B2"}`,
      borderRadius: 5,
      padding: "8px 10px",
      display: "grid",
      gap: 4,
    }}>
      <div style={{ display: "flex", alignItems: "baseline", gap: 8 }}>
        <span className="ss-caps" style={{ color: "var(--ss-gray-500)", width: 70 }}>Vision</span>
        <span style={{ fontSize: 12.5, color: "var(--ss-gray-700)", textDecoration: "line-through" }}>{item.item_name}</span>
      </div>
      <div style={{ display: "flex", alignItems: "baseline", gap: 8 }}>
        <span className="ss-caps" style={{ color: verified ? "var(--ss-match-verified)" : "var(--ss-match-review)", width: 70 }}>GUDID</span>
        <span style={{ fontSize: 12.5, color: "var(--ss-ink)", fontWeight: 500 }}>{match.canonical_name}</span>
      </div>
      <div style={{ fontSize: 11.5, color: "var(--ss-gray-700)", marginTop: 2 }}>
        {percent(match.match_confidence)} through {prettifyPath(match.match_path)}
      </div>
    </div>
  );
}

function ManualEntryPanel({ item, value, onChange }) {
  return (
    <div style={{
      marginTop: 10,
      background: "var(--ss-match-reject-bg)",
      border: "1px solid #DAB5AC",
      borderRadius: 5,
      padding: 10,
      display: "grid",
      gridTemplateColumns: "minmax(0, 1fr) auto",
      gap: 8,
    }}>
      <input
        aria-label="Manual canonical name"
        value={value}
        onChange={(event) => onChange(item.id, event.target.value)}
        placeholder="Manual canonical name"
        style={{
          minWidth: 0,
          border: "1px solid #DAB5AC",
          borderRadius: 4,
          padding: "7px 9px",
          fontFamily: "var(--ss-sans)",
          fontSize: 12.5,
          background: "var(--ss-warm-white)",
          color: "var(--ss-ink)",
        }}
      />
      <button style={{ ...gudidGhostButton, background: "var(--ss-warm-white)" }}>Search GUDID</button>
      <div style={{ gridColumn: "1 / -1", fontSize: 11.5, color: "var(--ss-match-reject)" }}>
        {item.gudid_match
          ? "Rejected below confidence threshold; manual catalog entry required."
          : "No GUDID match returned; add manually before approval."}
      </div>
    </div>
  );
}

function DriftPanel({ item, choice, onChoice }) {
  const match = item.gudid_match;
  return (
    <div style={{
      marginTop: 10,
      background: "var(--ss-match-auto-bg)",
      border: "1px solid #E1CC9E",
      borderLeft: "3px solid var(--ss-match-auto)",
      borderRadius: 5,
      padding: 10,
      color: "#7A5615",
    }}>
      <div style={{ fontSize: 12.5, lineHeight: 1.45 }}>
        Cerner card has {match.canonical_name}, discontinued {match.distribution_end_date}. Active successor: <strong>{match.successor_pin}</strong>.
      </div>
      <div style={{ display: "flex", gap: 8, marginTop: 8, flexWrap: "wrap" }}>
        <button onClick={() => onChoice(item.id, "successor")} style={choice === "successor" ? gudidPrimaryButton : gudidGhostButton}>
          Use successor
        </button>
        <button onClick={() => onChoice(item.id, "original")} style={choice === "original" ? gudidPrimaryButton : gudidGhostButton}>
          Keep original
        </button>
      </div>
    </div>
  );
}

function GudidSidePanel({ stats, selected, onDetail }) {
  return (
    <aside style={{
      position: "sticky",
      top: 72,
      background: "var(--ss-warm-white)",
      border: "1px solid var(--ss-line)",
      borderRadius: 6,
      padding: 16,
      boxShadow: "var(--ss-shadow-card)",
    }}>
      <div className="ss-caps" style={{ color: "var(--ss-terra-deep)", marginBottom: 8 }}>Review queue</div>
      <h2 className="ss-serif" style={{ margin: 0, fontSize: 22, fontWeight: 500, color: "var(--ss-ink)" }}>
        {stats.review + stats.manual} decisions before final save
      </h2>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginTop: 14 }}>
        <MiniStat label="Matched" value={stats.matched} />
        <MiniStat label="Library adds" value={stats.manual} />
        <MiniStat label="Review" value={stats.review} />
        <MiniStat label="No match" value={stats.total - stats.matched} />
      </div>
      {selected && (
        <div style={{ marginTop: 16, paddingTop: 14, borderTop: "1px solid var(--ss-line-soft)" }}>
          <div className="ss-caps" style={{ color: "var(--ss-gray-500)", marginBottom: 8 }}>Selected detail</div>
          <CanonicalNameBlock item={selected} manualName="" />
          <div style={{ marginTop: 10 }}>
            <MatchConfidenceChip item={selected} onOpen={onDetail} />
          </div>
          {selected.gudid_match && (
            <dl style={{ margin: "12px 0 0", display: "grid", gap: 7, fontSize: 12.5 }}>
              <DetailPair label="Primary DI" value={selected.gudid_match.primary_di} mono />
              <DetailPair label="Path" value={prettifyPath(selected.gudid_match.match_path)} />
              <DetailPair label="Source" value={selected.gudid_match.match_pin_source || "gudid"} />
            </dl>
          )}
        </div>
      )}
    </aside>
  );
}

function MiniStat({ label, value }) {
  return (
    <div style={{ background: "var(--ss-cream)", border: "1px solid var(--ss-line-soft)", borderRadius: 5, padding: 10 }}>
      <div className="ss-mono" style={{ fontSize: 18, fontWeight: 600, color: "var(--ss-ink)", lineHeight: 1 }}>{value}</div>
      <div style={{ marginTop: 4, fontSize: 11.5, color: "var(--ss-gray-700)" }}>{label}</div>
    </div>
  );
}

function GudidDetailModal({ item, cardItems = [], onClose, onScanResolved }) {
  const [productInfo, setProductInfo] = useStateGudid({ phase: "idle", enrichment: null, imageUrl: null, error: "" });
  const [scanOpen, setScanOpen] = useStateGudid(false);
  const cardItemId = cardItemIdForProductInfo(item);

  useEffectGudid(() => {
    let cancelled = false;
    if (!cardItemId) {
      setProductInfo({ phase: "unavailable", enrichment: null, imageUrl: null, error: "" });
      return () => { cancelled = true; };
    }
    setProductInfo({ phase: "loading", enrichment: null, imageUrl: null, error: "" });
    Promise.all([
      fetch(window.prefcardApiPath(`/api/items/${cardItemId}/enrich`), {
        method: "POST",
        credentials: "same-origin",
        headers: { "Accept": "application/json" },
      }).then(async (response) => {
        const payload = await response.json().catch(() => ({}));
        if (!response.ok) throw new Error(payload.detail || `HTTP ${response.status}`);
        return payload;
      }),
      fetch(window.prefcardApiPath(`/api/product-image/${cardItemId}`), {
        credentials: "same-origin",
        headers: { "Accept": "application/json" },
      }).then((response) => response.ok ? response.json() : { image_url: null }).catch(() => ({ image_url: null })),
    ]).then(([enrichment, image]) => {
      if (cancelled) return;
      setProductInfo({ phase: "ready", enrichment, imageUrl: image?.image_url || null, error: "" });
    }).catch((err) => {
      if (cancelled) return;
      setProductInfo({ phase: "error", enrichment: null, imageUrl: null, error: err.message || "Lookup failed" });
    });
    return () => { cancelled = true; };
  }, [cardItemId]);

  return (
    <div
      onClick={(event) => {
        if (event.target === event.currentTarget) onClose();
      }}
      style={{
        position: "fixed",
        inset: 0,
        zIndex: 20,
        background: "rgba(27,26,24,0.28)",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        padding: 24,
      }}
    >
      <div style={{
        width: "min(760px, 100%)",
        maxHeight: "calc(100vh - 64px)",
        overflow: "auto",
        background: "var(--ss-warm-white)",
        border: "1px solid var(--ss-line)",
        borderRadius: 6,
        boxShadow: "var(--ss-shadow-pop)",
        padding: 18,
      }}>
        <div style={{ display: "flex", alignItems: "flex-start", gap: 14 }}>
          <window.ProductGlyph supply={supplyGlyphSeed(item)} size={58} />
          <div style={{ flex: 1, minWidth: 0 }}>
            <CanonicalNameBlock item={item} manualName="" />
            <div style={{ marginTop: 8, display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center" }}>
              <MatchConfidenceChip item={item} onOpen={() => {}} />
              {cardItemId && <window.TokenChip value={String(cardItemId)} label={`item ${cardItemId}`} size="sm" tone="ghost" />}
            </div>
          </div>
          <button aria-label="Close" onClick={onClose} style={{
            ...gudidGhostButton,
            width: 30,
            height: 30,
            padding: 0,
            display: "inline-flex",
            alignItems: "center",
            justifyContent: "center",
          }}>
            <MatchGlyph kind="x" size={13} />
          </button>
        </div>

        <ProductInfoLookupPanel item={item} cardItems={cardItems} lookup={productInfo} />
        {scanOpen && (
          <ScanBarcodeModal
            item={item}
            cardItemId={cardItemId}
            onClose={() => setScanOpen(false)}
            onResolved={(result) => {
              onScanResolved?.(item, result);
            }}
          />
        )}

        <div style={{ marginTop: 16, display: "flex", justifyContent: "space-between", gap: 10, flexWrap: "wrap" }}>
          <button style={gudidGhostButton}>Flag for library add</button>
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
            {cardItemId && <button className="scan-barcode-open" onClick={() => setScanOpen(true)} style={gudidGhostButton}>Scan barcode</button>}
            <button onClick={onClose} style={gudidPrimaryButton}>Done</button>
          </div>
        </div>
      </div>
    </div>
  );
}

function ScanBarcodeModal({ item, cardItemId, onClose, onResolved }) {
  const [barcode, setBarcode] = useStateGudid("");
  const [barcodeType, setBarcodeType] = useStateGudid("unknown");
  const [phase, setPhase] = useStateGudid("idle");
  const [message, setMessage] = useStateGudid("");
  const [fileName, setFileName] = useStateGudid("");
  const [cameraOn, setCameraOn] = useStateGudid(false);
  const videoRef = useRefGudid(null);
  const streamRef = useRefGudid(null);
  const detectorSupported = typeof window !== "undefined" && "BarcodeDetector" in window;
  const cardId = window.PREFCARD_CONFIG?.cardId;

  async function resolveBarcodeValue(value, type) {
    const clean = (value || "").trim();
    if (!clean) return;
    try {
      setPhase("resolving");
      const response = await fetch(window.prefcardApiPath(`/api/cards/${cardId}/items/${cardItemId}/scan-barcode`), {
        method: "POST",
        credentials: "same-origin",
        headers: { "Content-Type": "application/json", "Accept": "application/json" },
        body: JSON.stringify({
          barcode_value: clean,
          barcode_type: type || "unknown",
          raw_item_name: item.item_name || item.raw_text || "",
        }),
      });
      const result = await response.json().catch(() => ({}));
      if (!response.ok) throw new Error(result.detail || `HTTP ${response.status}`);
      if (!result.resolved) {
        setPhase("not-found");
        setMessage(result.reason || "No GUDID record found for that barcode.");
        return;
      }
      setPhase("success");
      setMessage(`Linked ${result.gudid_brand || "GUDID device"} and saved this facility mapping.`);
      onResolved?.(result);
    } catch (err) {
      setPhase("error");
      setMessage(err.message || "Barcode scan failed.");
    }
  }

  useEffectGudid(() => {
    if (!cameraOn || !detectorSupported) return undefined;
    let cancelled = false;
    let rafId = null;
    let detector = null;
    (async () => {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } });
        if (cancelled) { stream.getTracks().forEach((t) => t.stop()); return; }
        streamRef.current = stream;
        if (videoRef.current) {
          videoRef.current.srcObject = stream;
          await videoRef.current.play().catch(() => {});
        }
        detector = new window.BarcodeDetector({ formats: ["data_matrix", "qr_code", "ean_13", "code_128", "upc_a"] });
        setPhase("scanning");
        setMessage("Point camera at the barcode. Auto-detects once visible.");
        const scanFrame = async () => {
          if (cancelled || !videoRef.current || videoRef.current.readyState < 2) {
            if (!cancelled) rafId = requestAnimationFrame(scanFrame);
            return;
          }
          try {
            const codes = await detector.detect(videoRef.current);
            // PREFER DATA MATRIX over 1D when both are visible: GS1 DataMatrix carries
            // primary DI + lot + expiry that matches GUDID DB; 1D Code 128 often only
            // carries the GTIN with AIM identifier prefix that needs server-side parsing.
            const dataMatrix = codes && codes.find && codes.find((c) => c.format === "data_matrix");
            const first = dataMatrix || (codes && codes[0]);
            if (first && first.rawValue) {
              setBarcode(first.rawValue);
              setBarcodeType(first.format || "live");
              setMessage(`Detected ${first.format || "barcode"} — resolving...`);
              setCameraOn(false);
              await resolveBarcodeValue(first.rawValue, first.format || "live");
              return;
            }
          } catch (e) {
            // transient frame error — keep scanning
          }
          if (!cancelled) rafId = requestAnimationFrame(scanFrame);
        };
        scanFrame();
      } catch (err) {
        setPhase("error");
        setMessage(err.message || "Camera unavailable. Use file upload or type the code.");
        setCameraOn(false);
      }
    })();
    return () => {
      cancelled = true;
      if (rafId) cancelAnimationFrame(rafId);
      if (streamRef.current) {
        streamRef.current.getTracks().forEach((t) => t.stop());
        streamRef.current = null;
      }
    };
  }, [cameraOn, detectorSupported]);

  async function detectFromFile(event) {
    const file = event.target.files?.[0];
    if (!file) return;
    setFileName(file.name);
    setMessage("");
    if (!detectorSupported) {
      setPhase("fallback");
      setMessage("This browser cannot decode uploaded barcode images; enter the barcode printed on the package.");
      return;
    }
    try {
      setPhase("scanning");
      const bitmap = await createImageBitmap(file);
      const detector = new window.BarcodeDetector({ formats: ["data_matrix", "qr_code", "ean_13", "code_128", "upc_a"] });
      const codes = await detector.detect(bitmap);
      const first = codes?.[0];
      if (!first) {
        setPhase("idle");
        setMessage("No barcode detected in that image.");
        return;
      }
      setBarcode(first.rawValue || "");
      setBarcodeType(first.format || "image");
      setPhase("ready");
      setMessage(`Detected ${first.format || "barcode"} from ${file.name}.`);
    } catch (err) {
      setPhase("error");
      setMessage(err.message || "Could not decode that image.");
    }
  }

  async function resolveBarcode() {
    const clean = barcode.trim();
    if (!clean) {
      setPhase("error");
      setMessage("Enter or upload a barcode first.");
      return;
    }
    try {
      setPhase("resolving");
      const response = await fetch(window.prefcardApiPath(`/api/cards/${cardId}/items/${cardItemId}/scan-barcode`), {
        method: "POST",
        credentials: "same-origin",
        headers: { "Content-Type": "application/json", "Accept": "application/json" },
        body: JSON.stringify({
          barcode_value: clean,
          barcode_type: barcodeType,
          raw_item_name: item.item_name || item.raw_text || "",
        }),
      });
      const result = await response.json().catch(() => ({}));
      if (!response.ok) throw new Error(result.detail || `HTTP ${response.status}`);
      if (!result.resolved) {
        setPhase("not-found");
        setMessage(result.reason || "No GUDID record found for that barcode.");
        return;
      }
      setPhase("success");
      setMessage(`Linked ${result.gudid_brand || "GUDID device"} and saved this facility mapping.`);
      onResolved?.(result);
    } catch (err) {
      setPhase("error");
      setMessage(err.message || "Barcode scan failed.");
    }
  }

  return (
    <div className="scan-barcode-modal" style={{ marginTop: 16, border: "1px solid var(--ss-line)", borderRadius: 6, padding: 14, background: "var(--ss-cream)", display: "grid", gap: 12 }}>
      <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 12 }}>
        <div>
          <div className="ss-caps" style={{ color: "var(--ss-terra-deep)" }}>Scan to learn</div>
          <h3 style={{ margin: "4px 0 0", fontSize: 16, color: "var(--ss-ink)" }}>Bind barcode to this item</h3>
        </div>
        <button onClick={onClose} style={gudidGhostButton}>Close</button>
      </div>
      <div style={{ fontSize: 12.5, color: "var(--ss-gray-700)" }}>
        Future cards at this facility will try this scanned GUDID match before generic text matching.
      </div>
      {detectorSupported && (
        <div style={{ display: "grid", gap: 8 }}>
          <button onClick={() => setCameraOn((v) => !v)} style={cameraOn ? gudidGhostButton : gudidPrimaryButton} disabled={phase === "resolving"}>
            {cameraOn ? "Stop camera" : "Scan with camera"}
          </button>
          {cameraOn && (
            <video ref={videoRef} playsInline muted autoPlay style={{ width: "100%", maxHeight: 320, background: "#000", borderRadius: 5 }} />
          )}
        </div>
      )}
      {!detectorSupported && (
        <div style={{ fontSize: 12, color: "var(--ss-gray-600)", fontStyle: "italic" }}>
          Live camera barcode scan requires BarcodeDetector API (Chrome/Edge on Android). On unsupported browsers, use file upload or type the code below.
        </div>
      )}
      <label style={{ display: "grid", gap: 6, fontSize: 12.5, color: "var(--ss-gray-700)" }}>
        Barcode value
        <input
          value={barcode}
          onChange={(event) => { setBarcode(event.target.value); setPhase("ready"); }}
          placeholder="Scan or type DI / GS1 (01)..."
          style={{ border: "1px solid var(--ss-line)", borderRadius: 5, padding: "9px 10px", background: "var(--ss-warm-white)", color: "var(--ss-ink)" }}
        />
      </label>
      <label style={{ display: "grid", gap: 6, fontSize: 12.5, color: "var(--ss-gray-700)" }}>
        Upload barcode image
        <input className="scan-barcode-file" type="file" accept="image/*" onChange={detectFromFile} />
      </label>
      {fileName && <div className="ss-mono" style={{ fontSize: 11.5, color: "var(--ss-gray-600)" }}>{fileName}</div>}
      {message && (
        <div role="status" className={`scan-barcode-status scan-barcode-${phase}`} style={{
          border: "1px solid var(--ss-line-soft)",
          borderRadius: 5,
          padding: 10,
          background: phase === "success" ? "var(--ss-match-verified-bg)" : phase === "not-found" || phase === "error" ? "var(--ss-match-reject-bg)" : "var(--ss-warm-white)",
          color: phase === "success" ? "var(--ss-match-verified)" : phase === "not-found" || phase === "error" ? "var(--ss-match-reject)" : "var(--ss-gray-700)",
        }}>
          {message}
        </div>
      )}
      <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, flexWrap: "wrap" }}>
        <button onClick={resolveBarcode} disabled={phase === "resolving" || !cardId || !cardItemId} style={gudidPrimaryButton}>
          {phase === "resolving" ? "Resolving..." : "Resolve barcode"}
        </button>
      </div>
    </div>
  );
}

function cardItemIdForProductInfo(item) {
  const raw = item?.card_item_id || item?.backend_id;
  if (raw && Number.isFinite(Number(raw))) return Number(raw);
  // Fallback: items from /api/cards/{id} responses have item.id = card_items.id (numeric).
  // The /api/match-items preview path uses synthesized string ids — caller falls through to the regex below.
  if (item?.id && Number.isFinite(Number(item.id))) return Number(item.id);
  const match = String(item?.id || "").match(/card-item-(\d+)$/);
  return match ? Number(match[1]) : null;
}

function ProductInfoLookupPanel({ item, cardItems = [], lookup }) {
  if (lookup.phase === "loading" || lookup.phase === "idle") {
    return (
      <div style={productInfoNotice("var(--ss-cream)")}>
        <MiniSpinner />
        <span>Looking up product...</span>
      </div>
    );
  }
  if (lookup.phase === "unavailable") {
    return (
      <div style={productInfoNotice("var(--ss-match-nomatch-bg)")}>
        <span className="ss-caps" style={{ color: "var(--ss-match-nomatch)" }}>Saved item needed</span>
        <span>This row is not persisted yet, so live openFDA and product-image lookup are unavailable.</span>
      </div>
    );
  }
  if (lookup.phase === "error") {
    return (
      <div style={{ ...productInfoNotice("var(--ss-match-reject-bg)"), display: "grid", alignItems: "stretch" }}>
        <span className="ss-caps" style={{ color: "var(--ss-match-reject)" }}>Lookup unavailable</span>
        <span>{lookup.error}</span>
        <FallbackDetailGrid item={item} />
        <DisruptionBadge item={item} />
      </div>
    );
  }

  const enrichment = lookup.enrichment || {};
  const match = item.gudid_match;
  const marketplace = enrichment.marketplace_match || {};
  const safety = enrichment.safety || {};
  const rep = enrichment.surgx_rep;
  const manufacturer = enrichment.company || marketplace.manufacturer_name || match?.company_name || item.vendor || "";
  const imageUrl = lookup.imageUrl;
  const roomDefault = itemRoomDefault(item, cardItems);

  return (
    <div style={{ marginTop: 16, display: "grid", gap: 14 }}>
      <ProductInfoBand item={item} enrichment={enrichment} />
      <div style={{
        display: "grid",
        gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
        gap: 14,
      }}>
        <ProductImageFrame imageUrl={imageUrl} item={item} />
        <div style={{
          display: "grid",
          gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))",
          gap: 10,
        }}>
          <DetailTile label="Brand" value={enrichment.brand || marketplace.brand_name || match?.brand_name || item.item_name} />
          <DetailTile label="Manufacturer" value={manufacturer} />
          <DetailTile label="Catalog #" value={enrichment.catalog_num || marketplace.catalog_num || item.catalog_num || "Not listed"} mono />
          <DetailTile label="Primary DI" value={enrichment.di || match?.primary_di || "Not matched"} mono />
          <DetailTile label="GUDID class" value={marketplace.fda_class ? `Class ${marketplace.fda_class}` : "Not returned"} />
          <DetailTile label="Risk" value={riskTextForClass(marketplace.fda_class)} />
          <DetailTile label="Product code" value={marketplace.product_code || match?.fda_product_code || "Not returned"} mono />
          <DetailTile label="Pin source" value={enrichment._match_pin_source || match?.match_pin_source || "gudid"} />
        </div>
      </div>
      {(enrichment.description || marketplace.device_description || match?.device_description) && (
        <div style={{
          background: "var(--ss-cream)",
          border: "1px solid var(--ss-line-soft)",
          borderRadius: 5,
          padding: 12,
          color: "var(--ss-gray-900)",
          fontSize: 12.5,
          lineHeight: 1.45,
        }}>
          <div className="ss-caps" style={{ color: "var(--ss-gray-500)", marginBottom: 5 }}>Description</div>
          {enrichment.description || marketplace.device_description || match?.device_description}
        </div>
      )}
      <RoomDefaultEducationChip item={item} roomDefault={roomDefault} />
      <DisruptionBadge item={item} />
      <SafetySummary safety={safety} />
      <AllergenEducationBlock allergens={enrichment.allergens} />
      <VendorRepBlock manufacturer={manufacturer} rep={rep} repCount={enrichment.surgx_rep_count || 0} />
      {!item.gudid_match && <NoMatchEducation />}
    </div>
  );
}

function disruptionPrimaryDi(item) {
  return item?.gudid_di || item?.gudid_match?.primary_di || item?.gudid_match?.di || "";
}

function disruptionTierStyle(severity) {
  const tier = String(severity || "low").toLowerCase();
  if (tier === "critical") {
    return { background: "var(--ss-match-reject-bg)", border: "#DAB5AC", color: "var(--ss-match-reject)" };
  }
  if (tier === "moderate") {
    return { background: "var(--ss-open-bg)", border: "#E1CC9E", color: "#7A5615" };
  }
  return { background: "var(--ss-cream)", border: "var(--ss-line-soft)", color: "var(--ss-gray-700)" };
}

function disruptionTopAlert(alerts) {
  const rank = { critical: 3, moderate: 2, low: 1 };
  return [...alerts].sort((left, right) => {
    const rightRank = rank[String(right.severity || "low").toLowerCase()] || 0;
    const leftRank = rank[String(left.severity || "low").toLowerCase()] || 0;
    return rightRank - leftRank;
  })[0];
}

function disruptionFirmLabel(alerts, topAlert) {
  const firms = [...new Set(alerts.map((alert) => alert.recalling_firm).filter(Boolean))];
  if (firms.length > 1) return "Multiple manufacturers";
  return firms[0] || topAlert?.recalling_firm || "Manufacturer not listed";
}

function substitutesForItem(substitutesList, itemName) {
  if (!Array.isArray(substitutesList) || !substitutesList.length) return { alternatives: [], label: "" };
  const target = String(itemName || "").trim().toLowerCase();
  const exact = target
    ? substitutesList.find((group) => String(group.for_item || "").trim().toLowerCase() === target)
    : null;
  const group = exact || substitutesList[0] || {};
  return {
    alternatives: Array.isArray(group.alternatives) ? group.alternatives : [],
    label: group.for_item || itemName || "",
  };
}

function SubstitutesPanel({ state, itemName, onRetry, onMarkSubstitute, substitutedProductId, disabled = false }) {
  if (state.phase === "loading") {
    return (
      <div className="ss-substitutes-panel" style={substitutesPanelStyle}>
        <div style={{ fontWeight: 700 }}>Looking up substitutes...</div>
      </div>
    );
  }
  if (state.phase === "error") {
    return (
      <div className="ss-substitutes-panel" style={substitutesPanelStyle}>
        <div style={{ fontWeight: 700 }}>Substitute lookup failed. Try again.</div>
        <button type="button" onClick={onRetry} style={{ ...gudidGhostButton, justifySelf: "start", marginTop: 6 }}>Retry</button>
      </div>
    );
  }

  const { alternatives, label } = substitutesForItem(state.detail?.substitutes, itemName);
  if (!alternatives.length) {
    return (
      <div className="ss-substitutes-panel" style={substitutesPanelStyle}>
        <div style={{ fontWeight: 700 }}>No substitute matches in catalog.</div>
        <div style={{ marginTop: 3 }}>Contact materials team for sourcing options.</div>
      </div>
    );
  }

  return (
    <div className="ss-substitutes-panel" style={substitutesPanelStyle}>
      <div className="ss-caps" style={{ color: "var(--ss-gray-600)" }}>Alternatives for {label || itemName}</div>
      <div style={{ display: "grid", gap: 7, marginTop: 7 }}>
        {alternatives.slice(0, 3).map((alt, idx) => {
          const isSubstituted = substitutedProductId != null && String(substitutedProductId) === String(alt.id);
          return (
            <div key={alt.id || `${alt.name || "alt"}-${idx}`} style={{ padding: 9, border: "1px solid var(--ss-line-soft)", borderRadius: 5, background: "var(--ss-paper)" }}>
              <div style={{ display: "flex", gap: 8, justifyContent: "space-between", alignItems: "start", flexWrap: "wrap" }}>
                <div>
                  <div style={{ fontWeight: 700, color: "var(--ss-gray-900)" }}>{alt.name || "Unnamed substitute"}</div>
                  <div style={{ fontSize: 12, lineHeight: 1.35, color: "var(--ss-gray-600)", marginTop: 3 }}>
                    {[alt.manufacturer, alt.vendor_name, alt.price != null && alt.price !== "" ? String(alt.price) : ""].filter(Boolean).join(" / ") || "Vendor details not listed"}
                  </div>
                </div>
                {isSubstituted ? (
                  <span style={substitutedBadgeStyle}>Substituted</span>
                ) : (
                  <button type="button" disabled={disabled} onClick={() => onMarkSubstitute(alt)} style={{ ...gudidGhostButton, borderRadius: 5 }}>
                    Mark as substituted
                  </button>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function SubstituteConfirmDialog({ itemName, substitute, onCancel, onConfirm }) {
  const [reason, setReason] = useStateGudid("");
  const [phase, setPhase] = useStateGudid("idle");
  const [error, setError] = useStateGudid("");
  const dialogRef = React.useRef(null);
  const textareaRef = React.useRef(null);
  const cleanReason = reason.trim();
  const canSubmit = cleanReason.length >= 50 && phase !== "saving";

  useEffectGudid(() => {
    textareaRef.current?.focus();
  }, []);

  const close = () => {
    if (phase !== "saving") onCancel();
  };
  const submit = async () => {
    if (!canSubmit) return;
    setPhase("saving");
    setError("");
    try {
      await onConfirm(cleanReason);
      setPhase("done");
    } catch (err) {
      setPhase("idle");
      setError(err.message || "Could not mark substitute. Try again.");
    }
  };
  const onKeyDown = (event) => {
    if (event.key === "Escape") {
      event.preventDefault();
      close();
      return;
    }
    if (event.key !== "Tab" || !dialogRef.current) return;
    const focusable = Array.from(dialogRef.current.querySelectorAll("button, textarea")).filter((node) => !node.disabled);
    if (!focusable.length) return;
    const first = focusable[0];
    const last = focusable[focusable.length - 1];
    if (event.shiftKey && document.activeElement === first) {
      event.preventDefault();
      last.focus();
    } else if (!event.shiftKey && document.activeElement === last) {
      event.preventDefault();
      first.focus();
    }
  };

  return (
    <div role="presentation" onMouseDown={(event) => { if (event.target === event.currentTarget) close(); }} style={substituteDialogOverlayStyle}>
      <div ref={dialogRef} role="dialog" aria-modal="true" aria-labelledby="substitute-dialog-title" onKeyDown={onKeyDown} style={substituteDialogStyle}>
        <div id="substitute-dialog-title" style={{ fontSize: 18, fontWeight: 800, color: "var(--ss-gray-900)" }}>Mark as substituted</div>
        <div style={{ fontSize: 12.5, color: "var(--ss-gray-600)", lineHeight: 1.4 }}>
          This records that <strong>{substitute?.name || "Unnamed substitute"}</strong> is replacing <span style={{ textDecoration: "line-through" }}>{itemName || "this item"}</span>.
        </div>
        <div style={{ padding: 10, border: "1px solid var(--ss-line-soft)", borderRadius: 5, background: "var(--ss-bg)" }}>
          <div style={{ fontWeight: 700, color: "var(--ss-gray-900)" }}>{substitute?.name || "Unnamed substitute"}</div>
          <div style={{ fontSize: 12, color: "var(--ss-gray-600)", marginTop: 3 }}>
            {[substitute?.manufacturer, substitute?.vendor_name || substitute?.vendor].filter(Boolean).join(" / ") || "Vendor details not listed"}
          </div>
        </div>
        <label style={{ display: "grid", gap: 5, fontSize: 12, fontWeight: 700, color: "var(--ss-gray-800)" }}>
          Why substituting?
          <textarea ref={textareaRef} value={reason} onChange={(event) => setReason(event.target.value)} rows={4} style={substituteTextareaStyle} placeholder="Describe the recall/disruption and why this substitute is appropriate..." />
        </label>
        <div style={{ display: "flex", justifyContent: "space-between", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
          <span style={{ color: cleanReason.length >= 50 ? "var(--ss-match-verified)" : "var(--ss-match-reject)", fontSize: 12 }}>{cleanReason.length}/50 characters</span>
          {error && <span role="alert" style={{ color: "var(--ss-match-reject)", fontSize: 12 }}>{error}</span>}
        </div>
        <div style={{ display: "flex", justifyContent: "end", gap: 8 }}>
          <button type="button" onClick={close} disabled={phase === "saving"} style={gudidGhostButton}>Cancel</button>
          <button type="button" onClick={submit} disabled={!canSubmit} style={{ ...gudidPrimaryButton, opacity: canSubmit ? 1 : 0.55 }}>
            {phase === "saving" ? "Saving..." : "Confirm substitute"}
          </button>
        </div>
      </div>
    </div>
  );
}

const substitutesPanelStyle = {
  marginTop: 4,
  padding: 10,
  border: "1px solid var(--ss-line-soft)",
  borderRadius: 5,
  background: "rgba(255,255,255,0.58)",
  color: "var(--ss-gray-800)",
  fontSize: 12.5,
  lineHeight: 1.35,
};

const substitutedBadgeStyle = {
  alignSelf: "start",
  border: "1px solid var(--ss-match-verified)",
  borderRadius: 999,
  color: "var(--ss-match-verified)",
  background: "rgba(28, 95, 72, 0.08)",
  fontSize: 11,
  fontWeight: 800,
  padding: "4px 8px",
};

const substituteDialogOverlayStyle = {
  position: "fixed",
  inset: 0,
  zIndex: 90,
  background: "rgba(17, 24, 39, 0.36)",
  display: "grid",
  placeItems: "center",
  padding: 18,
};

const substituteDialogStyle = {
  width: "min(520px, 100%)",
  background: "var(--ss-paper)",
  border: "1px solid var(--ss-line)",
  borderRadius: 8,
  boxShadow: "0 18px 60px rgba(17,24,39,0.24)",
  padding: 18,
  display: "grid",
  gap: 12,
};

const substituteTextareaStyle = {
  width: "100%",
  border: "1px solid var(--ss-line)",
  borderRadius: 5,
  background: "var(--ss-paper)",
  padding: 10,
  minHeight: 92,
  resize: "vertical",
  font: "inherit",
};

function DisruptionBadge({ item }) {
  const gudidDi = disruptionPrimaryDi(item);
  const [status, setStatus] = useStateGudid({ phase: gudidDi ? "loading" : "none", alerts: [], error: "" });
  const [substitutes, setSubstitutes] = useStateGudid({ expanded: false, phase: "idle", alertId: null, detail: null, error: "" });
  const [pendingSubstitute, setPendingSubstitute] = useStateGudid(null);
  const [substitution, setSubstitution] = useStateGudid(item.intent_status === "substituted" ? { productId: item.marketplace_product_id || null, name: item.substituted_product_name || "" } : null);
  const substitutesRequestId = React.useRef(0);

  useEffectGudid(() => {
    let cancelled = false;
    if (!gudidDi) {
      setStatus({ phase: "none", alerts: [], error: "" });
      return () => { cancelled = true; };
    }
    setStatus({ phase: "loading", alerts: [], error: "" });
    fetch(window.prefcardApiPath(`/api/disruptions/check/${encodeURIComponent(gudidDi)}`), {
      credentials: "same-origin",
      headers: { "Accept": "application/json" },
    }).then(async (response) => {
      const payload = await response.json().catch(() => ({}));
      if (!response.ok) throw new Error(payload.detail || `HTTP ${response.status}`);
      return payload;
    }).then((payload) => {
      if (cancelled) return;
      const alerts = Array.isArray(payload.alerts) ? payload.alerts : [];
      setStatus({ phase: payload.affected && alerts.length ? "affected" : "clean", alerts, error: "" });
    }).catch((err) => {
      if (cancelled) return;
      setStatus({ phase: "error", alerts: [], error: err.message || "Disruption check failed" });
    });
    return () => { cancelled = true; };
  }, [gudidDi]);

  if (status.phase !== "affected") return null;

  const alerts = status.alerts;
  const topAlert = disruptionTopAlert(alerts);
  const style = disruptionTierStyle(topAlert?.severity);
  const count = alerts.length;
  const itemName = item.item_name || item.name || item.description || "";
  const cardItemId = cardItemIdForProductInfo(item);
  const cardId = window.PREFCARD_CONFIG?.cardId;
  const submitMarketplaceSubstitute = async (substitute, reason) => {
    if (!cardId || !cardItemId) throw new Error("Card item id missing for substitute update.");
    const response = await fetch(window.prefcardApiPath(`/api/cards/${cardId}/items/${cardItemId}/substitute`), {
      method: "POST",
      credentials: "same-origin",
      headers: { "Content-Type": "application/json", "Accept": "application/json" },
      body: JSON.stringify({
        marketplace_product_id: substitute.id,
        marketplace_product_name: substitute.name || "Unnamed substitute",
        marketplace_manufacturer: substitute.manufacturer || null,
        marketplace_vendor: substitute.vendor_name || substitute.vendor || null,
        intent_reason: reason,
      }),
    });
    const payload = await response.json().catch(() => ({}));
    if (!response.ok) {
      const authMessage = response.status === 401 || response.status === 403 ? "Sign-in required to substitute." : "";
      throw new Error(authMessage || payload.detail || `HTTP ${response.status}`);
    }
    setSubstitution({ productId: substitute.id, name: substitute.name || "Unnamed substitute", reason });
    setPendingSubstitute(null);
    return payload;
  };
  const loadSubstitutes = (force = false) => {
    if (!topAlert?.id) return;
    if (substitutes.expanded && !force) {
      substitutesRequestId.current += 1;
      setSubstitutes((prev) => ({ ...prev, expanded: false }));
      return;
    }
    if (!force && substitutes.detail && substitutes.alertId === topAlert.id) {
      setSubstitutes((prev) => ({ ...prev, expanded: true }));
      return;
    }
    const requestId = substitutesRequestId.current + 1;
    substitutesRequestId.current = requestId;
    setSubstitutes({ expanded: true, phase: "loading", alertId: topAlert.id, detail: null, error: "" });
    fetch(window.prefcardApiPath(`/api/disruptions/${encodeURIComponent(topAlert.id)}`), {
      credentials: "same-origin",
      headers: { "Accept": "application/json" },
    }).then(async (response) => {
      const payload = await response.json().catch(() => ({}));
      if (!response.ok) throw new Error(payload.detail || `HTTP ${response.status}`);
      return payload;
    }).then((payload) => {
      if (substitutesRequestId.current !== requestId) return;
      setSubstitutes({ expanded: true, phase: "loaded", alertId: topAlert.id, detail: payload, error: "" });
    }).catch((err) => {
      if (substitutesRequestId.current !== requestId) return;
      setSubstitutes({ expanded: true, phase: "error", alertId: topAlert.id, detail: null, error: err.message || "Substitute lookup failed" });
    });
  };

  return (
    <section style={{
      background: style.background,
      border: `1px solid ${style.border}`,
      borderLeft: `3px solid ${style.border}`,
      borderRadius: 5,
      padding: 12,
      color: style.color,
      display: "grid",
      gap: 7,
    }}>
      <div className="ss-caps" style={{ color: style.color }}>Supply disruption</div>
      <div style={{ fontWeight: 700, color: style.color }}>
        ! {count} active FDA disruption{count === 1 ? "" : "s"}
      </div>
      <div style={{ fontSize: 12.5, lineHeight: 1.42, color: "var(--ss-gray-900)" }}>
        {topAlert?.reason || "Active FDA disruption is linked to this device."}
      </div>
      <div style={{ fontSize: 12, lineHeight: 1.35 }}>
        {disruptionFirmLabel(alerts, topAlert)}
        {topAlert?.severity ? ` / ${String(topAlert.severity).toUpperCase()} severity` : ""}
      </div>
      {substitution && (
        <div data-substitution-success style={{ padding: 9, border: "1px solid var(--ss-match-verified)", borderRadius: 5, background: "rgba(28, 95, 72, 0.08)", color: "var(--ss-gray-800)", display: "grid", gap: 3 }}>
          <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
            <span style={substitutedBadgeStyle}>Substituted - see notes</span>
            <span style={{ fontWeight: 700 }}>{substitution.name}</span>
          </div>
          <div style={{ textDecoration: "line-through", color: "var(--ss-gray-600)", fontSize: 12 }}>{itemName || "Original item"}</div>
        </div>
      )}
      {topAlert?.id && (
        <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap", marginTop: 2 }}>
          <button type="button" onClick={() => loadSubstitutes()} aria-expanded={substitutes.expanded ? "true" : "false"} style={{ ...gudidGhostButton, justifySelf: "start" }}>
            {substitutes.expanded ? "Hide substitutes" : "Show substitutes"}
          </button>
          <a href={window.prefcardApiPath(`/api/disruptions/${encodeURIComponent(topAlert.id)}`)} target="_blank" rel="noopener" style={{ color: "var(--ss-gray-600)", fontSize: 12, textDecoration: "underline" }}>
            Open raw alert -&gt;
          </a>
        </div>
      )}
      {substitutes.expanded && (
        <SubstitutesPanel
          state={substitutes}
          itemName={itemName}
          onRetry={() => loadSubstitutes(true)}
          onMarkSubstitute={setPendingSubstitute}
          substitutedProductId={substitution?.productId}
          disabled={!cardId || !cardItemId}
        />
      )}
      {pendingSubstitute && (
        <SubstituteConfirmDialog
          itemName={itemName}
          substitute={pendingSubstitute}
          onCancel={() => setPendingSubstitute(null)}
          onConfirm={(reason) => submitMarketplaceSubstitute(pendingSubstitute, reason)}
        />
      )}
    </section>
  );
}

function FallbackDetailGrid({ item }) {
  const match = item.gudid_match;
  return (
    <div style={{ marginTop: 12, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
      {match ? (
        <>
          <DetailTile label="Primary DI" value={match.primary_di} mono />
          <DetailTile label="Brand" value={match.brand_name} />
          <DetailTile label="Company" value={match.company_name} />
          <DetailTile label="FDA product code" value={match.fda_product_code} mono />
        </>
      ) : (
        <NoMatchEducation />
      )}
    </div>
  );
}

function RoomDefaultEducationChip({ item, roomDefault }) {
  if (!roomDefault) return null;
  const itemId = cardItemIdForProductInfo(item);
  const openLayout = () => {
    const url = new URL(window.location.href);
    url.searchParams.set("view", "layout");
    if (itemId) url.searchParams.set("highlight_item", String(itemId));
    window.location.href = url.toString();
  };
  return (
    <section style={{
      background: "var(--ss-open-bg)",
      border: "1px solid #E1CC9E",
      borderLeft: "3px solid #E1CC9E",
      borderRadius: 5,
      padding: 12,
      color: "#7A5615",
      display: "grid",
      gap: 7,
    }}>
      <div className="ss-caps" style={{ color: "#7A5615" }}>Room-default assumption</div>
      <div style={{ fontWeight: 700, color: "#5F3F0E" }}>{roomDefault.rule.defaultLabel}</div>
      <div style={{ fontSize: 12.5, lineHeight: 1.42 }}>{roomDefault.body}</div>
      <div style={{ fontSize: 12.5, lineHeight: 1.42 }}>
        Suggested placement: {roomDefault.rule.targetLabels.join(", ")}.
      </div>
      <button type="button" onClick={openLayout} style={{
        ...gudidGhostButton,
        justifySelf: "start",
        marginTop: 2,
        borderColor: "#E1CC9E",
        color: "#7A5615",
      }}>
        Open on Layout tab ->
      </button>
    </section>
  );
}

function ProductInfoBand({ item, enrichment }) {
  const match = item.gudid_match;
  const conf = enrichment._match_confidence ?? match?.match_confidence;
  const band = getGudidBand(item);
  const meta = MATCH_COPY[band];
  const path = prettifyPath(enrichment._match_path || match?.match_path || "no_match");
  const title = band === "no_match"
    ? "No GUDID match - add to library"
    : band === "review"
    ? "Amber review - verify before save"
    : band === "block" || band === "reject"
    ? "Low confidence - add to library"
    : "GUDID verified";
  return (
    <div style={{
      border: `1px solid ${meta.bd}`,
      borderLeft: `4px solid ${meta.fg}`,
      background: meta.bg,
      borderRadius: 6,
      padding: "10px 12px",
      display: "flex",
      justifyContent: "space-between",
      alignItems: "flex-start",
      gap: 12,
      color: meta.fg,
    }}>
      <div>
        <div className="ss-caps" style={{ color: meta.fg }}>{title}</div>
        <div style={{ fontSize: 12, marginTop: 3 }}>
          {conf == null ? "Scanner or existing GUDID binding; no text-confidence score needed." : `${percent(conf)} through ${path}.`}
        </div>
        {(enrichment._match_pin_source || match?.match_pin_source) && (
          <div className="ss-mono" style={{ marginTop: 4, fontSize: 10.5 }}>
            pin {enrichment._match_pin_source || match.match_pin_source}
          </div>
        )}
      </div>
      <div className="ss-mono" style={{ fontSize: 24, fontWeight: 700, lineHeight: 1 }}>
        {conf == null ? "GUDID" : percent(conf)}
      </div>
    </div>
  );
}

function ProductImageFrame({ imageUrl, item }) {
  return (
    <div style={{
      minHeight: 196,
      background: "var(--ss-cream)",
      border: "1px solid var(--ss-line-soft)",
      borderRadius: 6,
      display: "grid",
      placeItems: "center",
      overflow: "hidden",
      padding: 10,
    }}>
      {imageUrl ? (
        <img
          src={window.prefcardApiPath(`/api/product-image-proxy?url=${encodeURIComponent(imageUrl)}`)}
          alt=""
          style={{ maxWidth: "100%", maxHeight: 190, objectFit: "contain", display: "block" }}
          onError={(event) => { event.currentTarget.style.display = "none"; }}
        />
      ) : (
        <div style={{ display: "grid", placeItems: "center", gap: 8, color: "var(--ss-gray-700)", textAlign: "center" }}>
          <window.ProductGlyph supply={supplyGlyphSeed(item)} size={88} />
          <span style={{ fontSize: 12 }}>No product image returned</span>
        </div>
      )}
    </div>
  );
}

const SAFETY_EDUCATION = {
  open_recalls: {
    label: "Open recalls",
    zero: {
      tier: "sage",
      headline: "No active FDA recalls",
      body: "No open recall actions on this device or its product family. Continue normal verification.",
    },
    nonzero: (n) => ({
      tier: "terra",
      headline: `${n} open FDA recall${n === 1 ? "" : "s"}`,
      body: "FDA has an active recall on this device or product family. Verify lot/serial against the recall notice with the manufacturer or your supply-chain lead before using.",
    }),
  },
  total_recalls: {
    label: "Total recalls",
    zero: {
      tier: "sage",
      headline: "No recall history",
      body: "No historical recalls for this product family.",
    },
    nonzero: (n) => ({
      tier: "neutral",
      headline: `${n} historical recall${n === 1 ? "" : "s"}`,
      body: "Past recall actions for this product family. Not all are active - open count above is what matters today.",
    }),
  },
  clearance_count: {
    label: "510(k)",
    zero: {
      tier: "amber",
      headline: "No 510(k) clearances found",
      body: "No 510(k) premarket notifications for this manufacturer's device family in our records. The device may be cleared under PMA, exempt, or our coverage is incomplete. Verify FDA pathway separately if needed.",
    },
    nonzero: (n) => ({
      tier: "sage",
      headline: `Cleared ${n} time${n === 1 ? "" : "s"} via 510(k)`,
      body: "510(k) is the FDA pathway for devices that are substantially equivalent to a previously cleared device. Multiple clearances suggest an established product line.",
    }),
  },
  adverse_event_count: {
    label: "MAUDE",
    zero: {
      tier: "sage",
      headline: "No MAUDE adverse-event reports",
      body: "No adverse event reports filed with FDA against this manufacturer. Continue normal verification.",
    },
    low: (n) => ({
      tier: "amber",
      headline: `${n} MAUDE report${n === 1 ? "" : "s"}`,
      body: "Manufacturer and User Facility Device Experience - adverse event reports filed with FDA. Review device labeling and monitor patient closely.",
    }),
    high: (n) => ({
      tier: "terra",
      headline: `${n} MAUDE reports - elevated`,
      body: "Manufacturer and User Facility Device Experience - elevated count of FDA-filed adverse event reports for this manufacturer. Surface to the surgeon if substitute is available.",
    }),
  },
};

function safetyEducationFor(metric, value) {
  const n = Number(value || 0);
  const config = SAFETY_EDUCATION[metric];
  if (!config) return null;
  if (metric === "adverse_event_count") {
    if (n <= 0) return config.zero;
    return n >= 10 ? config.high(n) : config.low(n);
  }
  return n > 0 ? config.nonzero(n) : config.zero;
}

function SafetySummary({ safety }) {
  const [openMetric, setOpenMetric] = useStateGudid(null);
  const recallCount = Number(safety.recall_count || 0);
  const openRecalls = Number(safety.open_recalls || 0);
  const clearances = Number(safety.clearance_count || 0);
  const adverse = Number(safety.adverse_event_count || 0);
  const metrics = [
    { id: "open_recalls", label: "Open recalls", value: openRecalls, tone: openRecalls > 0 ? "terra" : "sage" },
    { id: "total_recalls", label: "Total recalls", value: recallCount, tone: "neutral" },
    { id: "clearance_count", label: "510(k)", value: clearances, tone: clearances > 0 ? "sage" : "amber" },
    { id: "adverse_event_count", label: "MAUDE", value: adverse, tone: adverse >= 10 ? "terra" : adverse > 0 ? "amber" : "sage" },
  ];
  const activeMetric = metrics.find((metric) => metric.id === openMetric);
  const activeEducation = activeMetric ? safetyEducationFor(activeMetric.id, activeMetric.value) : null;
  const toggleMetric = (id) => setOpenMetric((current) => current === id ? null : id);
  return (
    <div style={{ display: "grid", gap: 8 }}>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(120px, 1fr))", gap: 8 }}>
        {metrics.map((metric) => (
          <SafetyMetric
            key={metric.id}
            label={metric.label}
            value={metric.value}
            tone={metric.tone}
            expanded={openMetric === metric.id}
            onToggle={() => toggleMetric(metric.id)}
          />
        ))}
      </div>
      {activeEducation && (
        <SafetyEducationPanel education={activeEducation} />
      )}
    </div>
  );
}

function SafetyEducationPanel({ education }) {
  const toneMap = {
    sage: { bg: "var(--ss-match-verified-bg)", fg: "var(--ss-match-verified)", bd: "#B9D0BE" },
    terra: { bg: "var(--ss-match-reject-bg)", fg: "var(--ss-match-reject)", bd: "#DAB5AC" },
    amber: { bg: "var(--ss-open-bg)", fg: "#7A5615", bd: "#E1CC9E" },
    neutral: { bg: "var(--ss-cream)", fg: "var(--ss-gray-900)", bd: "var(--ss-line-soft)" },
  };
  const t = toneMap[education.tier] || toneMap.neutral;
  return (
    <div style={{ background: t.bg, color: t.fg, border: `1px solid ${t.bd}`, borderLeft: `3px solid ${t.bd}`, borderRadius: 5, padding: 10 }}>
      <div className="ss-caps" style={{ color: t.fg }}>{education.headline}</div>
      <div style={{ marginTop: 5, fontSize: 12.5, lineHeight: 1.4 }}>{education.body}</div>
    </div>
  );
}

function SafetyMetric({ label, value, tone = "neutral", expanded = false, onToggle }) {
  const toneMap = {
    sage: { bg: "var(--ss-match-verified-bg)", fg: "var(--ss-match-verified)", bd: "#B9D0BE" },
    terra: { bg: "var(--ss-match-reject-bg)", fg: "var(--ss-match-reject)", bd: "#DAB5AC" },
    amber: { bg: "var(--ss-open-bg)", fg: "#7A5615", bd: "#E1CC9E" },
    neutral: { bg: "var(--ss-cream)", fg: "var(--ss-gray-900)", bd: "var(--ss-line-soft)" },
  };
  const t = toneMap[tone] || toneMap.neutral;
  const handleKeyDown = (event) => {
    if (event.key === "Enter" || event.key === " ") {
      event.preventDefault();
      onToggle?.();
    }
  };
  return (
    <div
      role="button"
      tabIndex={0}
      aria-expanded={expanded}
      onClick={onToggle}
      onKeyDown={handleKeyDown}
      style={{ position: "relative", background: t.bg, color: t.fg, border: `1px solid ${t.bd}`, borderRadius: 5, padding: 10, cursor: "pointer", minHeight: 60 }}
    >
      <span aria-hidden="true" style={{ position: "absolute", top: 7, right: 8, fontSize: 13, fontWeight: 700 }}>{expanded ? "^" : "?"}</span>
      <div className="ss-mono" style={{ fontSize: 20, fontWeight: 700, lineHeight: 1 }}>{value}</div>
      <div className="ss-caps" style={{ marginTop: 5, color: t.fg }}>{label}</div>
    </div>
  );
}

const ALLERGEN_SIGNAL_COPY = {
  latex: {
    tier: "high",
    label: "LATEX",
    text: "Verify patient latex allergy before opening package.",
  },
  mri_unsafe: {
    tier: "high",
    label: "MRI UNSAFE",
    text: "Do not use in MRI suite under any field strength.",
  },
  nickel: {
    tier: "medium",
    label: "NICKEL",
    text: "Check pre-op chart for nickel sensitivity.",
  },
  iodine: {
    tier: "medium",
    label: "IODINE",
    text: "Verify iodine allergy before use.",
  },
  mri_conditional: {
    tier: "medium",
    label: "MRI CONDITIONAL",
    text: "Verify scanner field strength and conditions before use.",
  },
  formaldehyde: {
    tier: "medium",
    label: "FORMALDEHYDE",
    text: "Wear appropriate PPE and ensure ventilation.",
  },
  ethylene_oxide: {
    tier: "medium",
    label: "ETHYLENE-OXIDE STERILIZED",
    text: "Verify aeration time before opening.",
  },
  silicone: { tier: "low", label: "silicone", text: "Contains silicone" },
  pvc: { tier: "low", label: "PVC", text: "Contains PVC" },
  dehp: { tier: "low", label: "DEHP", text: "Contains DEHP; consider neonatal exposure." },
  bpa: { tier: "low", label: "BPA", text: "Contains BPA" },
  adhesive_acrylic: {
    tier: "low",
    label: "acrylic adhesive",
    text: "Acrylic adhesive; possible contact-allergen.",
  },
};

function activeAllergenSignals(allergens) {
  const flags = allergens?.flags;
  if (!flags || typeof flags !== "object") return [];
  return Object.entries(flags).reduce((signals, [key, value]) => {
    if (!value) return signals;
    const signal = ALLERGEN_SIGNAL_COPY[key];
    if (!signal) {
      console.warn("Unknown allergen flag skipped", key);
      return signals;
    }
    signals.push({ key, ...signal });
    return signals;
  }, []);
}

function AllergenEducationBlock({ allergens }) {
  const signals = activeAllergenSignals(allergens);
  if (!signals.length) return null;

  const high = signals.filter((signal) => signal.tier === "high");
  const medium = signals.filter((signal) => signal.tier === "medium");
  const low = signals.filter((signal) => signal.tier === "low");
  const confidence = Number(allergens?.confidence);
  const hasConfidence = Number.isFinite(confidence);
  const lowConfidence = hasConfidence && confidence < 0.7;

  return (
    <div style={{
      display: "grid",
      gap: 8,
      border: "1px solid var(--ss-line-soft)",
      borderRadius: 6,
      background: "var(--ss-cream)",
      padding: 12,
    }}>
      <div className="ss-caps" style={{ color: "var(--ss-gray-500)" }}>
        Allergen / Material Safety
      </div>
      {high.map((signal) => (
        <AllergenSignalRow key={signal.key} signal={signal} tier="high" />
      ))}
      {medium.map((signal) => (
        <AllergenSignalRow key={signal.key} signal={signal} tier="medium" />
      ))}
      {low.length > 0 && (
        <div style={{
          padding: "9px 11px",
          borderRadius: 5,
          background: "var(--ss-match-verified-bg)",
          border: "1px solid #B9D0BE",
          color: "var(--ss-match-verified)",
          fontSize: 12.5,
          lineHeight: 1.35,
        }}>
          {low.map((signal) => signal.text).join("; ")}
        </div>
      )}
      {(hasConfidence || lowConfidence) && (
        <div style={{ fontSize: 10.5, color: "var(--ss-gray-700)", lineHeight: 1.35 }}>
          {hasConfidence && <span>{Math.round(confidence * 100)}% confidence</span>}
          {lowConfidence && (
            <span>{hasConfidence ? " - " : ""}Low confidence - verify against package labeling.</span>
          )}
        </div>
      )}
    </div>
  );
}

function AllergenSignalRow({ signal, tier }) {
  const tone = tier === "high"
    ? { bg: "var(--ss-match-reject-bg)", fg: "var(--ss-match-reject)", bd: "#DAB5AC" }
    : { bg: "var(--ss-open-bg)", fg: "#7A5615", bd: "#E1CC9E" };
  return (
    <div style={{
      padding: "10px 12px",
      borderRadius: 5,
      background: tone.bg,
      border: `1px solid ${tone.bd}`,
      color: tone.fg,
    }}>
      <div className="ss-caps" style={{ color: tone.fg }}>{signal.label}</div>
      <div style={{ fontSize: 12.5, lineHeight: 1.35, marginTop: 3 }}>{signal.text}</div>
    </div>
  );
}

function VendorRepBlock({ manufacturer, rep, repCount }) {
  if (isDisplayableCanvasRep(rep)) {
    const location = [rep.home_base_city, rep.home_base_state].filter(Boolean).join(", ");
    return (
      <div style={{
        background: "var(--ss-cream)",
        border: "1px solid var(--ss-line)",
        borderLeft: "4px solid var(--ss-sage)",
        borderRadius: 6,
        padding: 12,
      }}>
        <div style={{ display: "flex", justifyContent: "space-between", gap: 10 }}>
          <div className="ss-caps" style={{ color: "var(--ss-sage-deep)" }}>
            Vendor rep {repCount > 1 ? `(${repCount} on file)` : ""}
          </div>
          <span className="ss-caps" style={{ color: "var(--ss-sage-deep)" }}>verified {rep.verified}</span>
        </div>
        <div style={{ marginTop: 7, fontWeight: 700, color: "var(--ss-ink)" }}>{rep.full_name || "Unnamed rep"}</div>
        <div style={{ marginTop: 2, fontSize: 12.5, color: "var(--ss-gray-700)" }}>
          {[rep.title, rep.company_name, rep.rep_type, location].filter(Boolean).join(" / ")}
        </div>
        <div style={{ marginTop: 8, display: "flex", gap: 8, flexWrap: "wrap" }}>
          {rep.email && <a href={`mailto:${rep.email}`} style={repChipStyle}>Email</a>}
          {rep.phone && <a href={`tel:${rep.phone}`} style={repChipStyle}>{rep.phone}</a>}
          {rep.linkedin_url && <a href={rep.linkedin_url} target="_blank" rel="noopener" style={repChipStyle}>LinkedIn</a>}
        </div>
      </div>
    );
  }
  if (!manufacturer) return null;
  return (
    <div style={{
      background: "var(--ss-cream)",
      border: "1px dashed var(--ss-line)",
      borderRadius: 6,
      padding: 12,
      textAlign: "center",
      color: "var(--ss-gray-700)",
      fontSize: 12.5,
    }}>
      <div className="ss-caps" style={{ color: "var(--ss-gray-500)", marginBottom: 4 }}>Vendor contact</div>
      No verified rep is on file for {manufacturer}. Use the manufacturer contact path for now.
    </div>
  );
}

function NoMatchEducation() {
  return (
    <div
      title={ADD_TO_LIBRARY_TOOLTIP}
      style={{
        gridColumn: "1 / -1",
        color: "var(--ss-match-nomatch)",
        background: "var(--ss-match-nomatch-bg)",
        border: "1px solid var(--ss-line)",
        borderRadius: 5,
        padding: 10,
        fontSize: 12.5,
      }}
    >
      <span className="ss-caps" style={{ color: "var(--ss-match-nomatch)", marginRight: 6 }}>Why no match?</span>
      GUDID covers about 30% of OR supplies. This is normal for commodity supplies; add it to the library when the item matters.
    </div>
  );
}

function MiniSpinner() {
  return (
    <span style={{
      width: 14,
      height: 14,
      borderRadius: 99,
      border: "2px solid var(--ss-line)",
      borderTopColor: "var(--ss-terra)",
      display: "inline-block",
    }} />
  );
}

function productInfoNotice(background) {
  return {
    marginTop: 16,
    background,
    border: "1px solid var(--ss-line)",
    borderRadius: 6,
    padding: 14,
    display: "flex",
    alignItems: "center",
    gap: 10,
    color: "var(--ss-gray-900)",
    fontSize: 13,
  };
}

function riskTextForClass(fdaClass) {
  const key = String(fdaClass || "").trim().toUpperCase();
  if (key === "I") return "Low risk / general controls";
  if (key === "II") return "Moderate risk / special controls";
  if (key === "III") return "High risk / PMA pathway";
  return "Not classified";
}

function isDisplayableCanvasRep(rep) {
  if (!rep || Number(rep.verified || 0) <= 0) return false;
  const email = String(rep.email || "").toLowerCase();
  const name = String(rep.full_name || "").toLowerCase();
  const company = String(rep.company_name || "").toLowerCase();
  const localPart = email.split("@")[0] || "";
  const label = `${rep.full_name || ""} ${rep.title || ""}`.toLowerCase();
  if (email.includes(".demo@") || email.includes("@demo.") || /\bdemo\b/.test(name) || /\bdemo\b/.test(company)) return false;
  if (localPart.includes("customerservice") || localPart.includes("customer.service") || localPart.includes("orders")) return false;
  if (["info", "support", "service", "sales"].includes(localPart)) return false;
  return !(/\bcustomer service\b|\border(s|ing)?\b|\bsupport\b|\bservice desk\b/.test(label));
}

const repChipStyle = {
  display: "inline-flex",
  alignItems: "center",
  padding: "4px 8px",
  borderRadius: 99,
  background: "var(--ss-warm-white)",
  border: "1px solid var(--ss-line)",
  color: "var(--ss-ink)",
  fontSize: 12,
  fontWeight: 600,
  textDecoration: "none",
};

function DetailTile({ label, value, mono = false }) {
  return (
    <div style={{ background: "var(--ss-cream)", border: "1px solid var(--ss-line-soft)", borderRadius: 5, padding: 10 }}>
      <div className="ss-caps" style={{ color: "var(--ss-gray-500)", marginBottom: 5 }}>{label}</div>
      <div className={mono ? "ss-mono" : ""} style={{ fontSize: 12.5, color: "var(--ss-ink)", overflowWrap: "anywhere" }}>{value || "-"}</div>
    </div>
  );
}

function DetailPair({ label, value, mono = false }) {
  return (
    <div style={{ display: "grid", gridTemplateColumns: "88px minmax(0, 1fr)", gap: 8 }}>
      <dt className="ss-caps" style={{ color: "var(--ss-gray-500)" }}>{label}</dt>
      <dd className={mono ? "ss-mono" : ""} style={{ margin: 0, color: "var(--ss-gray-900)", overflowWrap: "anywhere" }}>{value}</dd>
    </div>
  );
}

function MatchGlyph({ kind, size = 12 }) {
  const props = { width: size, height: size, viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.7, strokeLinecap: "round", strokeLinejoin: "round" };
  if (kind === "check") return <svg {...props}><path d="M2.2 6.2L4.8 8.7L9.8 3.4" /></svg>;
  if (kind === "x") return <svg {...props}><path d="M3 3L9 9M9 3L3 9" /></svg>;
  if (kind === "alert") return <svg {...props}><path d="M6 1.5L11 10H1L6 1.5Z" /><path d="M6 4.5V6.7M6 8.5H6.1" /></svg>;
  if (kind === "review") return <svg {...props}><circle cx="5.5" cy="5.5" r="3.5" /><path d="M8.4 8.4L10.5 10.5" /></svg>;
  return <svg {...props} fill="currentColor" stroke="none"><circle cx="6" cy="6" r="2.4" /></svg>;
}

const gudidGhostButton = {
  padding: "6px 10px",
  borderRadius: 5,
  background: "transparent",
  border: "1px solid var(--ss-line)",
  color: "var(--ss-gray-900)",
  fontSize: 12,
  fontWeight: 500,
  cursor: "pointer",
  fontFamily: "var(--ss-sans)",
};

const gudidPrimaryButton = {
  padding: "6px 10px",
  borderRadius: 5,
  background: "var(--ss-ink)",
  border: "1px solid var(--ss-ink)",
  color: "var(--ss-cream)",
  fontSize: 12,
  fontWeight: 500,
  cursor: "pointer",
  fontFamily: "var(--ss-sans)",
};

Object.assign(window, {
  GudidMatchApprovalView,
  MatchConfidenceChip,
  getGudidBand,
  buildCategoryGroups,
  sectionKeyForItem,
});
