// ScrubStack — shared primitives
// All exported to window at the bottom so other <script type="text/babel">
// files can reach them.

const { useState, useRef, useEffect, useMemo } = React;

// ───────────────────────────────────────────────────────────────────
// TokenChip — monospace mini-pill for hashes, GTINs, versions
// ───────────────────────────────────────────────────────────────────
function TokenChip({ value, label, tone = "neutral", title, copy = false, size = "md" }) {
  const [copied, setCopied] = useState(false);
  const display = label ?? truncateHash(value);
  const toneMap = {
    neutral: { bg: "#EEE7DA", fg: "#3A3833", bd: "#D6D0C5" },
    sage:    { bg: "#E3E8DE", fg: "#3F523F", bd: "#C3D0BF" },
    terra:   { bg: "#F1DDD2", fg: "#7A3F2A", bd: "#DEC2B2" },
    amber:   { bg: "#F4E6CC", fg: "#7A5615", bd: "#E1CC9E" },
    ghost:   { bg: "transparent", fg: "#6E6962", bd: "#D6D0C5" },
  };
  const t = toneMap[tone] || toneMap.neutral;
  const sz = size === "sm" ? { py: 1, px: 5, fs: 10.5 } : size === "lg" ? { py: 3, px: 8, fs: 13 } : { py: 2, px: 6, fs: 11.5 };
  return (
    <span
      className="ss-mono"
      title={title ?? value}
      onClick={(e) => {
        e.stopPropagation();
        if (!copy) return;
        navigator.clipboard?.writeText(value || "");
        setCopied(true);
        setTimeout(() => setCopied(false), 1200);
      }}
      style={{
        display: "inline-flex", alignItems: "center", gap: 4,
        padding: `${sz.py}px ${sz.px}px`,
        fontSize: sz.fs, lineHeight: 1.1,
        background: t.bg, color: t.fg,
        border: `1px solid ${t.bd}`, borderRadius: 4,
        cursor: copy ? "pointer" : "default",
        userSelect: "all", whiteSpace: "nowrap",
        letterSpacing: "0.01em",
      }}
    >
      {copied ? "copied" : display}
    </span>
  );
}

function truncateHash(s) {
  if (!s) return "";
  if (s.length <= 12) return s;
  return s.slice(0, 6) + "…" + s.slice(-4);
}

// ───────────────────────────────────────────────────────────────────
// StatusBadge — rounded pill, color-coded per reconciliation tier
// ───────────────────────────────────────────────────────────────────
const STATUS_META = {
  confirmed:    { label: "Confirmed",   fg: "#3D6149", bg: "#DCE7DE", bd: "#B9D0BE", glyph: "check" },
  open:         { label: "Open",        fg: "#7A5615", bg: "#F4E6CC", bd: "#E1CC9E", glyph: "dot" },
  outstanding:  { label: "Outstanding", fg: "#7A3F2A", bg: "#F1DDD2", bd: "#DEC2B2", glyph: "minus" },
  overcount:    { label: "Overcount",   fg: "#7A3F2A", bg: "#F1DDD2", bd: "#DEC2B2", glyph: "plus" },
  unmatched:    { label: "Unmatched",   fg: "#7A2A23", bg: "#F0D8D2", bd: "#DAB5AC", glyph: "x" },
  fork:         { label: "Confirmed · fork", fg: "#3D6149", bg: "#DCE7DE", bd: "#B9D0BE", glyph: "star" },
};

function StatusBadge({ status, size = "md", showLabel = true }) {
  const m = STATUS_META[status] || STATUS_META.open;
  const sz = size === "sm" ? { py: 1, px: 6, fs: 10, gap: 4 } : { py: 2, px: 8, fs: 11, gap: 5 };
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: sz.gap,
      padding: `${sz.py}px ${sz.px}px`, borderRadius: 99,
      background: m.bg, color: m.fg, border: `1px solid ${m.bd}`,
      fontFamily: "var(--ss-sans)",
      fontSize: sz.fs, lineHeight: 1, fontWeight: 500,
      letterSpacing: "0.04em", textTransform: "uppercase",
      whiteSpace: "nowrap",
    }}>
      <Glyph kind={m.glyph} size={sz.fs} color={m.fg} />
      {showLabel && m.label}
    </span>
  );
}

function Glyph({ kind, size = 11, color = "currentColor", stroke = 1.6 }) {
  const s = size;
  const props = { width: s, height: s, viewBox: "0 0 12 12", fill: "none", stroke: color, strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round" };
  switch (kind) {
    case "check": return <svg {...props}><path d="M2.5 6.2L5 8.5L9.5 3.8" /></svg>;
    case "x":     return <svg {...props}><path d="M3 3L9 9M9 3L3 9" /></svg>;
    case "plus":  return <svg {...props}><path d="M6 2.5V9.5M2.5 6H9.5" /></svg>;
    case "minus": return <svg {...props}><path d="M2.5 6H9.5" /></svg>;
    case "star":  return <svg {...props} fill={color} stroke="none"><path d="M6 1.5L7.4 4.4L10.5 4.8L8.2 7L8.8 10L6 8.6L3.2 10L3.8 7L1.5 4.8L4.6 4.4Z" /></svg>;
    case "dot":   return <svg {...props} fill={color} stroke="none"><circle cx="6" cy="6" r="2.4" /></svg>;
    default: return null;
  }
}

// ───────────────────────────────────────────────────────────────────
// PhaseChip — colored marker for procedure phase
// ───────────────────────────────────────────────────────────────────
function PhaseChip({ phase, size = "md", filter = false, active = false }) {
  const p = window.PREFCARD.phases.find((x) => x.id === phase) || { name: phase, color: "#6E6962" };
  const sz = size === "sm" ? { py: 1, px: 6, fs: 10 } : { py: 2, px: 8, fs: 11 };
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 5,
      padding: `${sz.py}px ${sz.px}px`, borderRadius: 4,
      border: `1px solid ${active ? p.color : "rgba(0,0,0,0.06)"}`,
      background: active ? `${p.color}1F` : "rgba(0,0,0,0.025)",
      color: "var(--ss-gray-900)", fontSize: sz.fs, fontWeight: 500,
      letterSpacing: "0.03em", whiteSpace: "nowrap",
      cursor: filter ? "pointer" : "default",
    }}>
      <span style={{ width: 6, height: 6, borderRadius: 2, background: p.color }} />
      {p.name}
    </span>
  );
}

// ───────────────────────────────────────────────────────────────────
// CriticalityBadge — high/medium/low
// ───────────────────────────────────────────────────────────────────
function CriticalityBadge({ level }) {
  const m = level === "high"
    ? { label: "Critical", fg: "#7A3F2A", bg: "#F1DDD2", bd: "#DEC2B2" }
    : level === "medium"
    ? { label: "Medium",   fg: "#6E6962", bg: "#EEE7DA", bd: "#D6D0C5" }
    : { label: "Low",      fg: "#6E6962", bg: "transparent", bd: "#D6D0C5" };
  return (
    <span style={{
      padding: "1px 6px", borderRadius: 3,
      background: m.bg, color: m.fg, border: `1px solid ${m.bd}`,
      fontSize: 10, lineHeight: 1.5, fontWeight: 500,
      letterSpacing: "0.05em", textTransform: "uppercase",
      whiteSpace: "nowrap",
    }}>{m.label}</span>
  );
}

// ───────────────────────────────────────────────────────────────────
// Sparkline — pure curve, no axes
// ───────────────────────────────────────────────────────────────────
function Sparkline({ data = [], width = 64, height = 16, color = "#7A8E7B", strokeWidth = 1.2 }) {
  if (!data.length) return null;
  const min = Math.min(...data), max = Math.max(...data);
  const range = max - min || 1;
  const stepX = data.length > 1 ? width / (data.length - 1) : 0;
  const pts = data.map((v, i) => [i * stepX, height - ((v - min) / range) * height]);
  const d = pts.map((p, i) => (i === 0 ? "M" : "L") + p[0].toFixed(1) + " " + p[1].toFixed(1)).join(" ");
  return (
    <svg width={width} height={height} style={{ display: "block" }}>
      <path d={d} stroke={color} strokeWidth={strokeWidth} fill="none" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// ───────────────────────────────────────────────────────────────────
// ProvenanceFooter — `<source> · <freshness> · <sha-chip> · <version-chip>`
// ───────────────────────────────────────────────────────────────────
function ProvenanceFooter({ source = "surgx", freshness = "2h fresh", sha = "9c2fb4d7e8af1c0bcd2731f366c5", version = "v3.0.0", maintainer = null, phi = null, expanded = false, style = {} }) {
  return (
    <div className="ss-mono" style={{
      display: "flex", alignItems: "center", flexWrap: "wrap", gap: 6,
      fontSize: 10.5, color: "var(--ss-gray-700)", letterSpacing: "0.01em",
      ...style,
    }}>
      <span style={{ color: "var(--ss-terra-deep)", fontWeight: 600 }}>{source}</span>
      <Dot />
      <span>{freshness}</span>
      <Dot />
      <TokenChip value={sha} size="sm" tone="ghost" />
      <Dot />
      <TokenChip value={version} label={version} size="sm" tone="ghost" />
      {phi !== null && (<><Dot /><span>phi:{String(phi)}</span></>)}
      {expanded && maintainer && (<><Dot /><span style={{ color: "var(--ss-gray-500)" }}>maintained by {maintainer}</span></>)}
    </div>
  );
}
function Dot() { return <span style={{ color: "var(--ss-gray-500)" }}>·</span>; }

// ───────────────────────────────────────────────────────────────────
// ProductGlyph — diagrammatic placeholder image (no stock photos)
// Generates a tinted, geometric tile keyed off the supply kind + id
// ───────────────────────────────────────────────────────────────────
function ProductGlyph({ supply, size = 72, radius = 6 }) {
  const palette = supply.kind === "implant"
    ? ["#E8DCC8", "#C7765A", "#7A3F2A"]
    : supply.kind === "instrument"
    ? ["#E2E2DA", "#6E6962", "#3A3833"]
    : supply.kind === "equipment"
    ? ["#DCE2E6", "#6B7A85", "#3A4651"]
    : ["#E3E8DE", "#7A8E7B", "#3F523F"];

  // deterministic seed
  const seed = supply.id.charCodeAt(supply.id.length - 1);
  const variant = seed % 4;

  return (
    <div style={{
      width: size, height: size, borderRadius: radius,
      background: palette[0], position: "relative", overflow: "hidden",
      border: "1px solid rgba(27,26,24,0.06)", flexShrink: 0,
    }}>
      <svg width={size} height={size} viewBox="0 0 72 72" style={{ position: "absolute", inset: 0 }}>
        {variant === 0 && (<>
          <rect x="14" y="22" width="44" height="28" rx="3" fill={palette[1]} opacity="0.55" />
          <rect x="22" y="30" width="28" height="12" rx="2" fill={palette[2]} opacity="0.5" />
        </>)}
        {variant === 1 && (<>
          <circle cx="36" cy="36" r="20" fill={palette[1]} opacity="0.45" />
          <circle cx="36" cy="36" r="10" fill={palette[2]} opacity="0.55" />
        </>)}
        {variant === 2 && (<>
          <path d="M14 50 L36 18 L58 50 Z" fill={palette[1]} opacity="0.55" />
          <path d="M28 50 L36 38 L44 50 Z" fill={palette[2]} opacity="0.6" />
        </>)}
        {variant === 3 && (<>
          <rect x="12" y="32" width="48" height="8" rx="2" fill={palette[1]} opacity="0.6" />
          <rect x="12" y="44" width="32" height="6" rx="2" fill={palette[1]} opacity="0.4" />
          <rect x="12" y="22" width="40" height="6" rx="2" fill={palette[2]} opacity="0.5" />
        </>)}
        {/* subtle hatch overlay for "placeholder" feel */}
        <defs>
          <pattern id={`hatch-${supply.id}`} width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(35)">
            <line x1="0" y1="0" x2="0" y2="6" stroke={palette[2]} strokeWidth="0.6" opacity="0.18" />
          </pattern>
        </defs>
        <rect x="0" y="0" width="72" height="72" fill={`url(#hatch-${supply.id})`} />
      </svg>
      <span className="ss-mono" style={{
        position: "absolute", left: 4, bottom: 3, fontSize: 8, color: palette[2],
        letterSpacing: "0.05em", textTransform: "uppercase", opacity: 0.75,
      }}>{supply.kind.slice(0, 3)}</span>
    </div>
  );
}

// ───────────────────────────────────────────────────────────────────
// CountReadout — expected (and observed) count, formatted
// ───────────────────────────────────────────────────────────────────
function CountReadout({ expected, observed = null, unit = "ea", size = "md" }) {
  const sz = size === "lg" ? { num: 28, lbl: 12 } : size === "sm" ? { num: 16, lbl: 10 } : { num: 20, lbl: 11 };
  if (observed === null) {
    return (
      <div style={{ display: "flex", alignItems: "baseline", gap: 4 }}>
        <span className="ss-mono" style={{ fontSize: sz.num, fontWeight: 600, lineHeight: 1, color: "var(--ss-ink)" }}>{expected}</span>
        <span style={{ fontSize: sz.lbl, color: "var(--ss-gray-700)" }}>{unit}</span>
      </div>
    );
  }
  const matched = observed === expected;
  const color = matched ? "var(--ss-confirmed)" : observed > expected ? "var(--ss-outstanding)" : "var(--ss-outstanding)";
  return (
    <div style={{ display: "flex", alignItems: "baseline", gap: 4 }}>
      <span className="ss-mono" style={{ fontSize: sz.num, fontWeight: 600, lineHeight: 1, color }}>{observed}</span>
      <span className="ss-mono" style={{ fontSize: sz.lbl, color: "var(--ss-gray-500)" }}>/ {expected}</span>
      <span style={{ fontSize: sz.lbl, color: "var(--ss-gray-700)", marginLeft: 2 }}>{unit}</span>
    </div>
  );
}

// ───────────────────────────────────────────────────────────────────
// ProductCard — the primary primitive
// variant: "authoring" | "preview" | "live" | "report" | "compact"
// ───────────────────────────────────────────────────────────────────
function ProductCard({ supply, variant = "preview", onClick, dragHandle = false, sha }) {
  const showLive = variant === "live" || variant === "report";
  const showAuthor = variant === "authoring";
  const hashFor = sha || (window.PREFCARD.sha + supply.id);
  return (
    <article
      onClick={onClick}
      style={{
        background: "var(--ss-warm-white)",
        border: "1px solid var(--ss-line)",
        borderRadius: 6,
        padding: 14,
        boxShadow: "var(--ss-shadow-card)",
        display: "flex", flexDirection: "column", gap: 12,
        cursor: onClick ? "pointer" : "default",
        transition: "box-shadow .15s, transform .15s, border-color .15s",
        position: "relative",
        minHeight: 168,
      }}
      onMouseEnter={(e) => {
        if (!onClick) return;
        e.currentTarget.style.boxShadow = "var(--ss-shadow-lift)";
        e.currentTarget.style.borderColor = "#BBB4A6";
      }}
      onMouseLeave={(e) => {
        if (!onClick) return;
        e.currentTarget.style.boxShadow = "var(--ss-shadow-card)";
        e.currentTarget.style.borderColor = "var(--ss-line)";
      }}
    >
      {/* Top row: image + body */}
      <div style={{ display: "flex", gap: 12, alignItems: "flex-start" }}>
        <ProductGlyph supply={supply} size={64} />
        <div style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 4 }}>
          <div style={{ display: "flex", alignItems: "flex-start", gap: 6, justifyContent: "space-between" }}>
            <h3 style={{
              margin: 0, fontSize: 14.5, fontWeight: 600, lineHeight: 1.25,
              color: "var(--ss-ink)", letterSpacing: "-0.005em",
              textWrap: "balance",
            }}>{supply.name}</h3>
            {showAuthor && <button title="Edit" style={iconBtn}><EditGlyph /></button>}
            {dragHandle && <button title="Drag" style={iconBtn}><GripGlyph /></button>}
          </div>
          <div style={{ fontSize: 12.5, color: "var(--ss-gray-700)" }}>{supply.mfg}</div>
          <div style={{ display: "flex", gap: 5, flexWrap: "wrap", marginTop: 2 }}>
            <TokenChip value={supply.gtin} label={supply.gtin} size="sm" copy />
            {supply.crit === "high" && <CriticalityBadge level="high" />}
          </div>
        </div>
      </div>

      {/* Mid row: count + phase + status */}
      <div style={{
        display: "flex", alignItems: "center", justifyContent: "space-between",
        gap: 8, paddingTop: 10, borderTop: "1px dashed var(--ss-line-soft)",
      }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
          <CountReadout
            expected={supply.expected}
            observed={showLive ? supply.observed : null}
            unit={supply.unit}
            size="md"
          />
          <PhaseChip phase={supply.phase} size="sm" />
        </div>
        <StatusBadge status={showLive ? supply.status : (showAuthor ? "open" : "open")} size="sm" />
      </div>

      {/* Note for unmatched */}
      {showLive && supply.note && (
        <div style={{
          background: "var(--ss-brick-bg)", borderLeft: "2px solid var(--ss-brick)",
          padding: "5px 9px", fontSize: 11.5, color: "#7A2A23",
          borderRadius: 2,
        }}>
          <span className="ss-caps" style={{ marginRight: 6 }}>note</span>
          {supply.note}
        </div>
      )}

      {/* Footer */}
      <ProvenanceFooter
        source={window.PREFCARD.source}
        freshness={supply.lastRecon && showLive ? `recon ${supply.lastRecon}` : window.PREFCARD.freshness}
        sha={hashFor}
        version={window.PREFCARD.version}
        style={{ marginTop: "auto" }}
      />
    </article>
  );
}

const iconBtn = {
  width: 22, height: 22, padding: 0, border: "none", background: "transparent",
  color: "var(--ss-gray-700)", borderRadius: 3, cursor: "pointer",
  display: "inline-flex", alignItems: "center", justifyContent: "center",
};

function EditGlyph() {
  return <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"><path d="M2 10L4 9.5L10 3.5L8.5 2L2.5 8L2 10Z"/></svg>;
}
function GripGlyph() {
  return <svg width="10" height="14" viewBox="0 0 10 14" fill="currentColor"><circle cx="2.5" cy="2.5" r="1.1"/><circle cx="7.5" cy="2.5" r="1.1"/><circle cx="2.5" cy="7" r="1.1"/><circle cx="7.5" cy="7" r="1.1"/><circle cx="2.5" cy="11.5" r="1.1"/><circle cx="7.5" cy="11.5" r="1.1"/></svg>;
}

// ───────────────────────────────────────────────────────────────────
// Avatar — circular monogram
// ───────────────────────────────────────────────────────────────────
function Avatar({ name = "SC", size = 28, tier = null }) {
  const initials = name.split(" ").map((w) => w[0]).join("").slice(0, 2).toUpperCase();
  return (
    <div style={{
      width: size, height: size, borderRadius: "50%",
      background: "linear-gradient(135deg, #C7765A 0%, #7A8E7B 100%)",
      color: "#FAF7F2", display: "inline-flex", alignItems: "center", justifyContent: "center",
      fontSize: size * 0.4, fontWeight: 600, letterSpacing: "0.02em",
      boxShadow: tier ? `0 0 0 2px var(--ss-cream), 0 0 0 ${2 + 2}px ${tier}` : "none",
      flexShrink: 0,
    }}>{initials}</div>
  );
}

// ───────────────────────────────────────────────────────────────────
// AppHeader — global navigation skeleton + page action row
// ───────────────────────────────────────────────────────────────────
function AppHeader({ activeView, onView, compact = false }) {
  const card = window.PREFCARD || {};
  const navItems = [{ label: "Prefcards", href: "/cards" }];
  return (
    <header style={{
      background: "var(--ss-cream)",
      borderBottom: "1px solid var(--ss-line)",
      padding: compact ? "10px 20px" : "14px 32px",
      display: "flex", alignItems: "center", gap: 24,
      position: "sticky", top: 0, zIndex: 5,
    }}>
      <Logo />
      <nav style={{ display: "flex", gap: 4 }}>
        {navItems.map((item) => (
          <a key={item.label} href={item.href} style={{
            padding: "6px 10px", borderRadius: 4,
            fontSize: 13.5, fontWeight: 500, color: "var(--ss-ink)",
            textDecoration: "none",
            background: "rgba(0,0,0,0.04)",
          }}>{item.label}</a>
        ))}
      </nav>
      <div style={{ flex: 1 }} />
      <div style={{
        display: "flex", alignItems: "center", gap: 8,
        padding: "6px 10px", background: "var(--ss-warm-white)",
        border: "1px solid var(--ss-line)", borderRadius: 5,
        width: 280, color: "var(--ss-gray-500)", fontSize: 13,
      }}>
        <SearchGlyph />
        <span style={{ flex: 1 }}>Search prefcards, supplies, hashes…</span>
        <TokenChip value="⌘K" label="⌘K" size="sm" tone="ghost" />
      </div>
      <Avatar name={card.maintainer || "Maintainer"} size={30} />
    </header>
  );
}

function scrubStackLogoSrc(version) {
  return `/static/og/scrubstack-logo-transparent.png?v=${version}`;
}

function Logo() {
  const [failed, setFailed] = useState(false);
  const logoSrc = scrubStackLogoSrc(3);
  if (failed || !logoSrc) {
    return (
      <a href="/cards" aria-label="ScrubStack home" style={{ display: "inline-flex", alignItems: "center", gap: 8, textDecoration: "none" }}>
        <svg width="22" height="22" 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: 18, fontWeight: 600, color: "var(--ss-ink)", whiteSpace: "nowrap" }}>ScrubStack</span>
      </a>
    );
  }
  return (
    <a href="/cards" aria-label="ScrubStack home" style={{ display: "inline-flex", alignItems: "center", width: 154, height: 20, overflow: "hidden", position: "relative", textDecoration: "none", flexShrink: 0 }}>
      <img
        src={logoSrc}
        alt="ScrubStack"
        onError={() => setFailed(true)}
        style={{ position: "absolute", width: 185, height: "auto", left: -15, top: -35, display: "block", userSelect: "none" }}
      />
    </a>
  );
}

function SearchGlyph() {
  return <svg width="13" height="13" viewBox="0 0 13 13" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"><circle cx="5.5" cy="5.5" r="3.5" /><path d="M8.5 8.5L11 11" /></svg>;
}

// ───────────────────────────────────────────────────────────────────
// ViewSwitcher — Cards · Flow · List · Spec · Diagram
// ───────────────────────────────────────────────────────────────────
const VIEWS = [
  { id: "cards",   label: "Cards",   icon: "grid" },
  { id: "flow",    label: "Flow",    icon: "flow" },
  { id: "list",    label: "List",    icon: "list" },
  { id: "spec",    label: "Spec",    icon: "doc" },
  { id: "diagram", label: "Diagram", icon: "diag" },
];

function ViewSwitcher({ active, onChange }) {
  return (
    <div style={{
      display: "inline-flex", alignItems: "center",
      background: "var(--ss-warm-white)",
      border: "1px solid var(--ss-line)",
      borderRadius: 6, padding: 3, gap: 2,
    }}>
      {VIEWS.map((v) => (
        <button key={v.id}
          onClick={() => onChange && onChange(v.id)}
          style={{
            display: "inline-flex", alignItems: "center", gap: 6,
            padding: "6px 12px", border: "none", borderRadius: 4,
            background: active === v.id ? "var(--ss-cream-tint)" : "transparent",
            color: active === v.id ? "var(--ss-ink)" : "var(--ss-gray-700)",
            fontFamily: "var(--ss-sans)", fontSize: 13, fontWeight: 500,
            cursor: "pointer", letterSpacing: "0.01em",
          }}
        >
          <ViewIcon kind={v.icon} active={active === v.id} />
          {v.label}
        </button>
      ))}
    </div>
  );
}

function ViewIcon({ kind, active }) {
  const c = active ? "#C7765A" : "currentColor";
  const props = { width: 13, height: 13, viewBox: "0 0 13 13", fill: "none", stroke: c, strokeWidth: 1.4, strokeLinecap: "round", strokeLinejoin: "round" };
  switch (kind) {
    case "grid": return <svg {...props}><rect x="1.5" y="1.5" width="4" height="4" rx="0.5"/><rect x="7.5" y="1.5" width="4" height="4" rx="0.5"/><rect x="1.5" y="7.5" width="4" height="4" rx="0.5"/><rect x="7.5" y="7.5" width="4" height="4" rx="0.5"/></svg>;
    case "flow": return <svg {...props}><rect x="1.5" y="2" width="2.5" height="9" rx="0.5"/><rect x="5.5" y="2" width="2.5" height="6" rx="0.5"/><rect x="9.5" y="2" width="2" height="8" rx="0.5"/></svg>;
    case "list": return <svg {...props}><path d="M1.5 3H11.5M1.5 6.5H11.5M1.5 10H11.5"/></svg>;
    case "doc":  return <svg {...props}><path d="M3 1.5H8L10.5 4V11.5H3Z"/><path d="M5 5H8M5 7.5H8.5M5 10H7.5"/></svg>;
    case "diag": return <svg {...props}><rect x="1.5" y="1.5" width="10" height="10" rx="1"/><path d="M1.5 5H11.5M5 1.5V11.5"/></svg>;
    default: return null;
  }
}

// ───────────────────────────────────────────────────────────────────
// PageHeader — large editorial header for prefcard page
// ───────────────────────────────────────────────────────────────────
function PageHeader({ mode = "preview", onAction }) {
  const card = window.PREFCARD;
  const watermarkSrc = scrubStackLogoSrc(1);
  return (
    <div style={{ padding: "28px 32px 18px", borderBottom: "1px solid var(--ss-line)", position: "relative" }}>
      <div style={{ display: "flex", alignItems: "flex-start", gap: 32 }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="ss-caps" style={{ color: "var(--ss-terra-deep)", marginBottom: 10 }}>
            Preference Card · {card.fullName}
          </div>
          <h1 className="ss-serif" style={{
            margin: 0, fontSize: 34, fontWeight: 500, lineHeight: 1.1,
            letterSpacing: "-0.015em", color: "var(--ss-ink)",
            textWrap: "balance",
          }}>
            {card.name} <span style={{ color: "var(--ss-gray-500)" }}>—</span>{" "}
            <span style={{ color: "var(--ss-gray-700)", fontWeight: 400, fontStyle: "italic" }}>maintained by {card.maintainer}'s office</span>
          </h1>
          <div style={{ display: "flex", alignItems: "center", gap: 14, marginTop: 14, flexWrap: "wrap" }}>
            <Avatar name={card.maintainer} size={28} />
            <div style={{ fontSize: 13, color: "var(--ss-gray-900)" }}>
              {card.maintainer}, {card.maintainerRole}
              <div style={{ fontSize: 12, color: "var(--ss-gray-700)" }}>{card.maintainerInst}</div>
            </div>
            <div style={{ width: 1, height: 28, background: "var(--ss-line)" }} />
            <div style={{ fontSize: 12, color: "var(--ss-gray-700)" }}>
              <div className="ss-caps" style={{ fontSize: 10, marginBottom: 2, color: "var(--ss-gray-500)" }}>Duration</div>
              <span className="ss-mono" style={{ fontSize: 13, color: "var(--ss-ink)" }}>{card.duration}</span> min
            </div>
            <div style={{ width: 1, height: 28, background: "var(--ss-line)" }} />
            <div style={{ fontSize: 12, color: "var(--ss-gray-700)" }}>
              <div className="ss-caps" style={{ fontSize: 10, marginBottom: 2, color: "var(--ss-gray-500)" }}>Supplies</div>
              <span className="ss-mono" style={{ fontSize: 13, color: "var(--ss-ink)" }}>{(card.supplies || []).length}</span>
              {" · "}{(card.supplies || []).filter((s) => s.crit === "high").length} critical
            </div>
          </div>
        </div>
        <div style={{ display: "flex", alignItems: "flex-start", gap: 8, paddingTop: 4 }}>
          <ActionButton label="Edit" />
          <ActionButton label="Duplicate" />
          <ActionButton label="Version" />
          <ActionButton label="Share to Feed" primary />
          <ActionButton label="Export" />
        </div>
      </div>
      <ProvenanceFooter
        source={card.source} freshness={card.freshness} sha={card.sha} version={card.version}
        style={{ marginTop: 16 }}
      />
      {watermarkSrc && (
        <img
          src={watermarkSrc}
          alt=""
          aria-hidden="true"
          onError={(event) => { event.currentTarget.style.display = "none"; }}
          style={{
            position: "absolute",
            right: 24,
            bottom: 14,
            height: 16,
            width: "auto",
            opacity: 0.32,
            pointerEvents: "none",
            userSelect: "none",
          }}
        />
      )}
    </div>
  );
}

function ActionButton({ label, primary = false, onClick }) {
  return (
    <button onClick={onClick} style={{
      padding: "7px 12px", borderRadius: 5, cursor: "pointer",
      background: primary ? "var(--ss-ink)" : "var(--ss-warm-white)",
      color: primary ? "var(--ss-cream)" : "var(--ss-ink)",
      border: primary ? "1px solid var(--ss-ink)" : "1px solid var(--ss-line)",
      fontFamily: "var(--ss-sans)", fontSize: 13, fontWeight: 500,
      letterSpacing: "0.005em",
      boxShadow: primary ? "0 1px 0 rgba(255,255,255,0.06) inset" : "none",
    }}>{label}</button>
  );
}

// ───────────────────────────────────────────────────────────────────
// StatusBanner — reconciliation summary
// ───────────────────────────────────────────────────────────────────
function StatusBanner({ mode = "live" }) {
  const s = window.PREFCARD_SUMMARY;
  const isReport = mode === "report";
  return (
    <div style={{
      margin: "20px 32px 0",
      background: isReport ? "var(--ss-warm-white)" : "var(--ss-sage-tint)",
      border: `1px solid ${isReport ? "var(--ss-line)" : "#C3D0BF"}`,
      borderRadius: 6,
      padding: "14px 18px",
      display: "flex", alignItems: "center", gap: 22,
    }}>
      <div style={{
        width: 8, height: 8, borderRadius: 99,
        background: isReport ? "var(--ss-gray-700)" : "var(--ss-confirmed)",
        animation: isReport ? "none" : "pulse 1.6s ease-in-out infinite",
      }} />
      <div style={{ flex: 1, display: "flex", alignItems: "baseline", gap: 18, flexWrap: "wrap" }}>
        <span className="ss-serif" style={{ fontSize: 18, fontWeight: 500, color: "var(--ss-ink)" }}>
          {s.confirmed} of {s.total} supplies confirmed
        </span>
        <span style={{ fontSize: 13, color: "var(--ss-gray-700)", display: "inline-flex", gap: 14 }}>
          <span><span className="ss-mono" style={{ color: "var(--ss-outstanding)", fontWeight: 600 }}>{s.outstanding}</span> outstanding</span>
          <span><span className="ss-mono" style={{ color: "var(--ss-brick)", fontWeight: 600 }}>{s.unmatched}</span> unmatched</span>
          {isReport && <span style={{ color: "var(--ss-gray-500)" }}>· case completed 13:18 · 78 min</span>}
        </span>
      </div>
      {isReport ? (
        <div style={{ display: "flex", gap: 8 }}>
          <ActionButton label="Download report" />
          <ActionButton label="Share" primary />
        </div>
      ) : (
        <span className="ss-caps" style={{ color: "var(--ss-confirmed)" }}>● live</span>
      )}
      <style>{"@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}"}</style>
    </div>
  );
}

// ───────────────────────────────────────────────────────────────────
// Export
// ───────────────────────────────────────────────────────────────────
Object.assign(window, {
  TokenChip, StatusBadge, PhaseChip, CriticalityBadge, Sparkline,
  ProvenanceFooter, ProductGlyph, CountReadout, ProductCard,
  Avatar, AppHeader, ViewSwitcher, PageHeader, StatusBanner,
  Glyph, truncateHash, VIEWS, STATUS_META,
});
