
/* ===== shell.jsx ===== */
/* global React */
// Shared shell + primitives used across all frames.

const Icon = ({ name, size = 14, color = 'currentColor', style }) => {
  const paths = {
    check: <path d="M20 6L9 17l-5-5" />,
    chevronRight: <path d="M9 18l6-6-6-6" />,
    chevronDown: <path d="M6 9l6 6 6-6" />,
    chevronUp: <path d="M18 15l-6-6-6 6" />,
    chevronLeft: <path d="M15 18l-6-6 6-6" />,
    arrowRight: <path d="M5 12h14M13 5l7 7-7 7" />,
    sparkle: <path d="M12 3l1.9 5.8L20 11l-6.1 2.2L12 19l-1.9-5.8L4 11l6.1-2.2L12 3z" />,
    sparkles: <g><path d="M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z" /><path d="M19 14l.7 2.1L22 17l-2.3.9L19 20l-.7-2.1L16 17l2.3-.9L19 14z" /></g>,
    user: <g><circle cx="12" cy="8" r="4" /><path d="M4 21c0-4.4 3.6-8 8-8s8 3.6 8 8" /></g>,
    users: <g><circle cx="9" cy="8" r="3.5" /><path d="M3 20c0-3.3 2.7-6 6-6s6 2.7 6 6" /><circle cx="17" cy="9" r="2.5" /><path d="M16 14c2.8 0 5 2.2 5 5" /></g>,
    bolt: <path d="M13 2L4 14h7l-1 8 9-12h-7l1-8z" />,
    shield: <path d="M12 2l8 3v7c0 5-3.5 8.5-8 10-4.5-1.5-8-5-8-10V5l8-3z" />,
    heart: <path d="M12 20s-7-4.5-9.5-9C1 8 2.5 4.5 6 4.5c2 0 3.2 1.2 4 2.3.8-1.1 2-2.3 4-2.3 3.5 0 5 3.5 3.5 6.5C19 15.5 12 20 12 20z" />,
    star: <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />,
    book: <path d="M4 4h12a3 3 0 013 3v13H7a3 3 0 01-3-3V4zM4 17a3 3 0 013-3h12" />,
    file: <path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6zM14 2v6h6" />,
    fileCheck: <g><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6zM14 2v6h6" /><path d="M9 14l2 2 4-4" /></g>,
    folder: <path d="M3 6a2 2 0 012-2h4l2 3h8a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V6z" />,
    flow: <g><circle cx="6" cy="6" r="2.5" /><circle cx="18" cy="6" r="2.5" /><circle cx="6" cy="18" r="2.5" /><circle cx="18" cy="18" r="2.5" /><path d="M8.5 6h7M6 8.5v7M18 8.5v7M8.5 18h7" /></g>,
    sitemap: <g><rect x="9" y="3" width="6" height="4" rx="1" /><rect x="3" y="15" width="6" height="4" rx="1" /><rect x="15" y="15" width="6" height="4" rx="1" /><path d="M12 7v4M6 15v-2h12v2" /></g>,
    play: <path d="M6 4l14 8-14 8V4z" />,
    plus: <path d="M12 5v14M5 12h14" />,
    settings: <g><circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.7 1.7 0 00.3 1.8l.1.1a2 2 0 01-2.8 2.8l-.1-.1a1.7 1.7 0 00-1.8-.3 1.7 1.7 0 00-1 1.5V21a2 2 0 01-4 0v-.1a1.7 1.7 0 00-1.1-1.5 1.7 1.7 0 00-1.8.3l-.1.1a2 2 0 01-2.8-2.8l.1-.1a1.7 1.7 0 00.3-1.8 1.7 1.7 0 00-1.5-1H3a2 2 0 010-4h.1a1.7 1.7 0 001.5-1.1 1.7 1.7 0 00-.3-1.8l-.1-.1a2 2 0 012.8-2.8l.1.1a1.7 1.7 0 001.8.3H9a1.7 1.7 0 001-1.5V3a2 2 0 014 0v.1a1.7 1.7 0 001 1.5 1.7 1.7 0 001.8-.3l.1-.1a2 2 0 012.8 2.8l-.1.1a1.7 1.7 0 00-.3 1.8V9a1.7 1.7 0 001.5 1H21a2 2 0 010 4h-.1a1.7 1.7 0 00-1.5 1z" /></g>,
    lightbulb: <g><path d="M9 18h6M10 22h4M12 2a7 7 0 00-4 12.7c1 .8 1.5 1.5 1.5 2.3v1h5v-1c0-.8.5-1.5 1.5-2.3A7 7 0 0012 2z" /></g>,
    target: <g><circle cx="12" cy="12" r="9" /><circle cx="12" cy="12" r="5" /><circle cx="12" cy="12" r="1.5" fill="currentColor" /></g>,
    clock: <g><circle cx="12" cy="12" r="9" /><path d="M12 7v5l3 2" /></g>,
    info: <g><circle cx="12" cy="12" r="9" /><path d="M12 16v-5M12 8v.01" /></g>,
    compass: <g><circle cx="12" cy="12" r="9" /><path d="M16 8l-2 6-6 2 2-6 6-2z" /></g>,
    aperture: <g><circle cx="12" cy="12" r="9" /><path d="M13.4 3.2l5.2 9M9.9 3.6h10.3M7.5 11l5.1-8.9M9 15l-5.2-9M13.4 15H3.1M10.6 21l5.1-8.9" /></g>,
    constellation: <g><path d="M7.7 7.6 16.3 7.1M6.8 9.8 10.9 16.2M17.1 8.9 13.1 16.2" /><circle cx="6" cy="8" r="2.1" /><circle cx="18" cy="7" r="2.1" /><circle cx="12" cy="18" r="2.1" /></g>,
    brain: <g><path d="M12 5.5V19" /><path d="M12 6.5A3 3 0 0 0 6.5 8 2.6 2.6 0 0 0 5 12.5 2.6 2.6 0 0 0 6.5 16a2.8 2.8 0 0 0 2.8 2.5c1.2 0 2.2-.8 2.7-1.8" /><path d="M12 6.5A3 3 0 0 1 17.5 8 2.6 2.6 0 0 1 19 12.5 2.6 2.6 0 0 1 17.5 16a2.8 2.8 0 0 1-2.8 2.5c-1.2 0-2.2-.8-2.7-1.8" /><path d="M8.5 10.5c1 0 1.5.6 1.5 1.5M15.5 10.5c-1 0-1.5.6-1.5 1.5" /></g>,
    layers: <g><path d="M12 2L2 7l10 5 10-5-10-5z" /><path d="M2 17l10 5 10-5M2 12l10 5 10-5" /></g>,
    grid: <g><rect x="3" y="3" width="7" height="7" rx="1" /><rect x="14" y="3" width="7" height="7" rx="1" /><rect x="3" y="14" width="7" height="7" rx="1" /><rect x="14" y="14" width="7" height="7" rx="1" /></g>,
    list: <g><path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" /></g>,
    map: <g><path d="M9 4l-6 2v14l6-2 6 2 6-2V4l-6 2-6-2z" /><path d="M9 4v14M15 6v14" /></g>,
    eye: <g><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7z" /><circle cx="12" cy="12" r="3" /></g>,
    pencil: <path d="M12 20h9M16.5 3.5a2.1 2.1 0 113 3L7 19l-4 1 1-4L16.5 3.5z" />,
    close: <path d="M18 6L6 18M6 6l12 12" />,
    download: <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3" />,
    pause: <g><rect x="6" y="4" width="4" height="16" rx="1" /><rect x="14" y="4" width="4" height="16" rx="1" /></g>,
    cog: <g><circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.7 1.7 0 00.3 1.8l.1.1a2 2 0 01-2.8 2.8l-.1-.1a1.7 1.7 0 00-1.8-.3 1.7 1.7 0 00-1 1.5V21a2 2 0 01-4 0v-.1a1.7 1.7 0 00-1.1-1.5 1.7 1.7 0 00-1.8.3l-.1.1a2 2 0 01-2.8-2.8l.1-.1a1.7 1.7 0 00.3-1.8 1.7 1.7 0 00-1.5-1H3a2 2 0 010-4h.1a1.7 1.7 0 001.5-1.1 1.7 1.7 0 00-.3-1.8l-.1-.1a2 2 0 012.8-2.8l.1.1a1.7 1.7 0 001.8.3H9a1.7 1.7 0 001-1.5V3a2 2 0 014 0v.1a1.7 1.7 0 001 1.5 1.7 1.7 0 001.8-.3l.1-.1a2 2 0 012.8 2.8l-.1.1a1.7 1.7 0 00-.3 1.8V9a1.7 1.7 0 001.5 1H21a2 2 0 010 4h-.1a1.7 1.7 0 00-1.5 1z" /></g>,
    rocket: <g><path d="M5 14l5 5M12 19l-7-7 7-7c4-2 8-1 9 0s2 5 0 9l-9 5z" /><circle cx="14" cy="10" r="1.5" /></g>,
    circleDashed: <circle cx="12" cy="12" r="9" strokeDasharray="3 3" />,
    circle: <circle cx="12" cy="12" r="9" />,
    circleCheck: <g><circle cx="12" cy="12" r="9" /><path d="M8 12l3 3 5-6" /></g>,
    lock: <g><rect x="4" y="11" width="16" height="10" rx="2" /><path d="M8 11V7a4 4 0 118 0v4" /></g>,
    branch: <g><circle cx="6" cy="6" r="2.5" /><circle cx="18" cy="18" r="2.5" /><circle cx="6" cy="18" r="2.5" /><path d="M6 8.5v7M8.5 6c5 0 9.5 3 9.5 9.5" /></g>,
    verified: <g><path d="M9 12l2 2 4-4" /><path d="M12 2l2.4 1.8L17.5 4l1.4 3 2.6 1.5-1 3.3 1 3.3-2.6 1.5L17.5 20l-3.1.2L12 22l-2.4-1.8L6.5 20 5 17l-2.6-1.5 1-3.3-1-3.3L5 7.6 6.5 4l3.1-.2L12 2z" /></g>,
    tag: <g><path d="M20 12V5a1 1 0 00-1-1h-7L3 13l8 8 9-9z" /><circle cx="9" cy="9" r="1" fill="currentColor" /></g>,
    chat: <path d="M21 12a8 8 0 01-12 7l-5 1 1-5a8 8 0 1116-3z" />,
    refresh: <g><path d="M3 12a9 9 0 0115-7l3 3M3 12l3-3M21 12a9 9 0 01-15 7l-3-3M21 12l-3 3" /></g>,
    activity: <path d="M22 12h-4l-3 9L9 3l-3 9H2" />,
    flask: <g><path d="M9 3h6M10 3v6.5L5 18a2 2 0 001.8 3h10.4A2 2 0 0019 18l-5-8.5V3" /><path d="M7.5 14h9" /></g>,
    building: <g><rect x="5" y="3" width="14" height="18" rx="1" /><path d="M9 7h2M13 7h2M9 11h2M13 11h2M9 15h2M13 15h2" /></g>,
    mail: <g><rect x="3" y="5" width="18" height="14" rx="2" /><path d="M3 7l9 6 9-6" /></g>,
    upload: <g><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12" /></g>,
    scan: <g><path d="M3 7V5a2 2 0 012-2h2M17 3h2a2 2 0 012 2v2M21 17v2a2 2 0 01-2 2h-2M7 21H5a2 2 0 01-2-2v-2" /><path d="M3 12h18" /></g>,
    warning: <g><path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z" /><path d="M12 9v4M12 17h.01" /></g>,
    trend: <path d="M3 17l6-6 4 4 8-8M21 7v5h-5" />,
    dollar: <g><path d="M12 2v20M17 5H9.5a3.5 3.5 0 000 7h5a3.5 3.5 0 010 7H6" /></g>,
    external: <g><path d="M15 3h6v6M10 14L21 3M21 14v5a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h5" /></g>,
    starFill: <path d="M12 2l2.9 6.3 6.9.8-5.1 4.7 1.4 6.8L12 17.8 5.9 20.6l1.4-6.8L2.2 9.1l6.9-.8L12 2z" fill="currentColor" stroke="none" />,
    sliders: <g><path d="M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6" /></g>,
    logout: <g><path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4" /><path d="M16 17l5-5-5-5M21 12H9" /></g>,
    calendar: <g><rect x="3" y="4" width="18" height="18" rx="2" /><path d="M3 10h18M8 2v4M16 2v4" /></g>,
    flag: <g><path d="M4 22V4M4 4h12l-2 4 2 4H4" /></g>,
  };
  const p = paths[name];
  if (!p) return null;
  return (
    <svg className="ico" width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={style}>
      {p}
    </svg>
  );
};

const TopBar = ({ activeTab = 'studio', onNav, user }) => {
  const u = user || (window.DATA && window.DATA.USER) || { initials: 'MC', org: '' };
  const tab = (id, label) => (
    <button className={activeTab === id ? 'active' : ''} onClick={() => onNav && onNav(id)}>{label}</button>
  );
  return (
    <div className="app-topbar">
      <div className="logo">
        <img src="assets/asher-lockup-color.png" alt="Asher Informatics" />
        <span style={{ marginLeft: 2, paddingLeft: 12, borderLeft: '1px solid #e7e3d8', fontWeight: 500, color: 'var(--asher-grey-medium)', letterSpacing: 0 }}>Forge</span>
      </div>
      <div className="nav">
        {tab('home', 'Home')}
        {tab('studio', 'Governance Studio')}
        {tab('explore', 'Frameworks')}
        {tab('admin', 'Admin')}
      </div>
      <div className="right">
        <Icon name="chat" size={17} color="#9aa39d" />
        <Icon name="info" size={17} color="#9aa39d" />
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <div className="avatar">{u.initials}</div>
        </div>
      </div>
    </div>
  );
};

// Breadcrumb labels that map to a real, navigable screen. Section-header crumbs
// ("Frameworks", "Organizational Profile") are categories, not pages, so they
// stay as plain labels. "Home" is the Triage front door (same place the sidebar
// "Revisit welcome" button goes); "Governance Studio" is the Studio landing.
const CRUMB_ROUTES = { 'Home': 'triage', 'Governance Studio': 'studio' };

const PolicyBreadcrumb = ({ items, onCrumb }) => (
  <div className="breadcrumb">
    {items.map((it, i) => {
      const isLast = i === items.length - 1;
      // A crumb is a live link only when it maps to a route, a handler exists,
      // and it isn't the current (last) page.
      const route = (!isLast && onCrumb) ? CRUMB_ROUTES[it] : null;
      return (
        <React.Fragment key={i}>
          {i > 0 && <Icon name="chevronRight" size={11} color="#9ca5b0" />}
          {route ? (
            <button type="button" className="breadcrumb-link" onClick={() => onCrumb(route)}>
              {it}
            </button>
          ) : (
            <span style={{ color: isLast ? 'var(--asher-grey-medium)' : 'var(--asher-grey-light)', fontWeight: isLast ? 600 : 400 }}>
              {it}
            </span>
          )}
        </React.Fragment>
      );
    })}
  </div>
);

// Capability area definitions used in many frames
const CAPABILITY_AREAS = [
  { id: 'strategy', label: 'Strategy & Leadership', short: 'Strategy', swatch: 'cap-strategy', color: '#f4d35e', focus: 'Vision · governance posture · risk appetite' },
  { id: 'risk', label: 'Risk, Ethics & Compliance', short: 'Risk & Ethics', swatch: 'cap-risk', color: '#f4a4a4', focus: 'Risk classification · ethics review · regulatory mapping' },
  { id: 'data', label: 'Data, Model & System Lifecycle', short: 'Data & Model', swatch: 'cap-data', color: '#a4d4d4', focus: 'Data governance · model dev · system deployment' },
  { id: 'ops', label: 'Operations & Workflow', short: 'Operations', swatch: 'cap-ops', color: '#d4d4a4', focus: 'Monitoring · incident response · clinical workflow integration' },
  { id: 'vendor', label: 'Vendor & Ecosystem', short: 'Vendor', swatch: 'cap-vendor', color: '#d4b4f4', focus: 'Procurement · third-party AI · ecosystem partnerships' },
];

// ── The AIMS object model (shared by the Studio landing + Configuration Hub) ──
// An AI Management System (AIMS / GovernanceProgram) is the owning object: it IS the
// aggregation of its Workplans. One Workplan per Capability Area (the 5-area spine
// above). A versioned Blueprint maps 1:1 to an AIMS version and 1:many to its
// Workplans. A customer can hold MORE THAN ONE AIMS/Blueprint, per license + org
// scope — so this is a list. Demo seeds one (PTMC Radiology).
//
// Shape ported from coreapp (ImplementationWorkplan / GovernanceProgram). A workplan
// carries: `phase` (now/next/later — assigned from the Blueprint's phase_assignments,
// which are computed from the Stage A answers), `rank` (1-based for the NOW-band
// "next 3 actions", null otherwise), `mix` (the artifact-type focus from wizard.ts —
// NOT the capability-area focus), `status`, `setupDone`/`setupTotal` (the 5 wizard
// steps), and `docs`/`flows` counts. This models the freshly-hydrated state the
// product B1 shows: everything `planned`, 0/5, AI recommendations pending.
// docs = doc_plan_count (pre-populated from the blueprint). flows = workflow_count
// (built during configuration, so 0 at the freshly-hydrated state → render "—").
// `free: true` marks the workplan ENTITLED on the free tier — Strategy & Leadership,
// the foundational area that carries the Scope Applicability and AI Governance
// policies everything else builds on. The Config Hub reads this flag to decide which
// single workplan is configurable; the rest are locked-but-visible.
const WORKPLAN_MODEL = {
  strategy: { mix: 'People + Policy',                 phase: 'now',  rank: null, docs: 2, flows: 0, free: true },
  risk:     { mix: 'Policy/Process + Operations',     phase: 'now',  rank: 3,    docs: 3, flows: 0 },
  data:     { mix: 'Technology + Process',            phase: 'next', rank: null, docs: 3, flows: 0 },
  ops:      { mix: 'Technology + Operations',         phase: 'now',  rank: 2,    docs: 3, flows: 0 },
  vendor:   { mix: 'Policy + Process (crosscutting)', phase: 'now',  rank: 1,    docs: 2, flows: 0 },
};
const makeWorkplans = () => CAPABILITY_AREAS.map((a) => ({
  id: a.id, name: a.label, short: a.short, color: a.color,
  status: 'planned', setupDone: 0, setupTotal: 5,
  ...(WORKPLAN_MODEL[a.id] || { mix: '', phase: 'later', rank: null, docs: 0, flows: 0 }),
}));
// The customer's AIMS programs. One entry for the demo; the shape supports N.
const AIMS_PROGRAMS = [
  {
    id: 'ptmc-rad',
    name: 'PTMC Radiology, ARCH-AI',
    blueprintName: 'PTMC Radiology — ARCH-AI',
    version: 1,
    prm: 'PRM v0.9.0',
    blueprintState: 'approved',
    recommendations: 'pending',           // 'pending' | 'applied'
    capabilityAreas: 5,
    lastUpdated: 'Apr 29, 2026',
    priorityFocus: ['ACR ARCH-AI', 'HIPAA'],   // from Stage A governance context
    compliance: [{ framework: 'ACR ARCH-AI', pct: 0 }],   // 0% until the workplan processes are configured
    workplans: makeWorkplans(),
  },
];
window.AIMS_PROGRAMS = AIMS_PROGRAMS;

// ── ExternalAppFrame: embed a live Discover app inside the wireframes ────────
// A house-styled wrapper around an <iframe> that hosts one of the standalone
// demos on discover.ashmatics.com (URLs centralized in window.DATA.EMBEDS).
// Used for the ARCH-AI assessment preview (Maturity step) and the Policy
// Ingestionator gap report (Step 6). The chrome carries the app name, an
// optional badge, and an "Open in new tab" link — which doubles as the fallback
// if the booth wifi drops, since these are the one place the offline demo
// reaches the network (see README rule 6). `note` adds an honest one-liner that
// this is a live, networked preview.
const ExternalAppFrame = ({ url, title, badge, note, height = 640, icon = 'external' }) => {
  if (!url) return null;
  return (
    <div className="asher-card" style={{ padding: 0, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '11px 14px', borderBottom: '1px solid #eef0eb', background: 'var(--asher-cream)' }}>
        <Icon name={icon} size={15} color="var(--asher-gold-dark)" />
        <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>{title}</span>
        {badge && <span className="chip" style={{ fontSize: 9.5 }}>{badge}</span>}
        <a className="btn btn-secondary btn-sm" style={{ marginLeft: 'auto', textDecoration: 'none' }} href={url} target="_blank" rel="noopener noreferrer">
          Open in new tab <Icon name="external" size={11} />
        </a>
      </div>
      <iframe
        src={url}
        title={title}
        loading="lazy"
        style={{ width: '100%', height, border: 'none', display: 'block', background: '#fff' }}
      />
      {note && (
        <div style={{ display: 'flex', gap: 7, padding: '9px 14px', borderTop: '1px solid #f1efe8', background: '#fdfcf8', fontSize: 11, color: 'var(--asher-grey-light)', lineHeight: 1.5 }}>
          <Icon name="info" size={12} color="var(--asher-gold-dark)" style={{ flexShrink: 0, marginTop: 1 }} />
          <span>{note}</span>
        </div>
      )}
    </div>
  );
};

// ── App shell: persistent left sidebar + thin breadcrumb top bar ─────────────
// The production-style frame the funnel sits inside once a viewer is "in" the app
// (Triage onward). Mirrors the AshMatics app chrome in ../../aigov-wizard-legacy:
// a fixed dark sidebar (brand + nav + user footer) beside a main column whose top
// bar carries only the breadcrumb. Replaces the per-screen horizontal TopBar.
// "Explore Asher FORGE" leads as a standalone, section-less item: it's the learn /
// orientation surface (platform, CHAR, and how the Studio works), broader than any
// one operational area, and the recommended first stop. Below it the operational
// tools and the revisitable organizational-profile surfaces (Organization Context,
// AI System Registry) read as first-class destinations, not one-time onboarding.
// The AI-Select Studio is the forthcoming AI Strategy / selection surface (the
// HTA Lab and use-case selector live behind it). It ships as an Early Preview
// (`badge`), and sits BEFORE governance because it is pre-acquisition: choose and
// value the AI here, then govern what you run in the Governance Studio. Disabled
// items show a `title` tooltip with their plain-language descriptor (e.g. Asher
// Workspace™ → "AI Committee Hub").
const FORGE_NAV = [
  {
    section: null,
    items: [
      { id: 'explore', label: 'Explore Asher FORGE™', icon: 'compass' },
    ],
  },
  {
    section: 'AI Strategy & Selection',
    items: [
      { id: 'aiselect', label: 'AI-Select Studio', icon: 'aperture', badge: 'Early Preview' },
    ],
  },
  {
    section: 'AI Governance & Oversight',
    items: [
      { id: 'studio', label: 'Governance Studio', icon: 'sitemap' },
      { id: 'artifacts', label: 'Program Artifacts', icon: 'book', disabled: true },
      { id: 'committee', label: 'Asher Workspace™', icon: 'users', disabled: true, title: 'AI Committee Hub' },
    ],
  },
  {
    section: 'Organizational Profile',
    items: [
      { id: 'orgcontext', label: 'Organization Context', icon: 'building' },
      { id: 'registry', label: 'AI System Registry', icon: 'grid' },
    ],
  },
];

const AppShell = ({ activeNav = 'studio', breadcrumb = [], user, onNav, onCrumb, onLogout, onWelcome, fill = false, children }) => {
  const u = user || (window.DATA && window.DATA.USER) || { initials: 'MC', firstName: 'Maya', lastName: 'Chandra', email: '', org: '' };
  const fullName = u.firstName ? `${u.firstName} ${u.lastName}` : (u.name || 'User');
  return (
    <div className="forge-layout">
      <nav className="forge-sidebar">
        <div className="forge-sidebar__brand">
          <img src="assets/asher-lockup-white.png" alt="Asher Informatics" />
          <span className="forge-sidebar__product">Asher FORGE™</span>
        </div>
        <div className="forge-sidebar__nav">
          {FORGE_NAV.map((group, gi) => (
            <React.Fragment key={group.section || `g${gi}`}>
              {group.section && <div className="forge-sidebar__section">{group.section}</div>}
              {group.items.map((it) => (
                <button
                  key={it.id}
                  type="button"
                  className={`forge-nav-item${it.id === activeNav ? ' is-active' : ''}${it.disabled ? ' is-disabled' : ''}`}
                  // class-based disable (not the `disabled` attr) so the hover title
                  // tooltip still shows; the click is guarded below.
                  aria-disabled={it.disabled || undefined}
                  title={it.title || undefined}
                  onClick={() => { if (!it.disabled && onNav) onNav(it.id); }}
                >
                  <Icon name={it.icon} size={16} />
                  <span>{it.label}</span>
                  {it.badge && (
                    <span className="forge-nav-badge" title={it.badge}>{it.badge}</span>
                  )}
                </button>
              ))}
              {!group.section && <div className="forge-sidebar__divider" />}
            </React.Fragment>
          ))}
        </div>
        <div className="forge-sidebar__footer">
          <div className="forge-user">
            <div className="forge-user__avatar">{u.initials}</div>
            <div className="forge-user__info">
              <div className="forge-user__name">{fullName}</div>
              <div className="forge-user__email">{u.email}</div>
            </div>
          </div>
          {onWelcome && (
            <button type="button" className="forge-logout" onClick={() => onWelcome()}>
              <Icon name="compass" size={13} /> Revisit welcome
            </button>
          )}
          <button type="button" className="forge-logout" onClick={() => onLogout && onLogout()}>
            <Icon name="logout" size={13} /> Log out
          </button>
        </div>
      </nav>
      <div className="forge-main">
        <div className="forge-topbar">
          <PolicyBreadcrumb items={breadcrumb.length ? breadcrumb : ['Home']} onCrumb={onCrumb} />
        </div>
        <div className={`forge-main-scroll${fill ? ' forge-main-scroll--fill' : ''}`}>
          {children}
        </div>
      </div>
    </div>
  );
};

// Make available globally for other Babel scripts
Object.assign(window, { Icon, TopBar, PolicyBreadcrumb, AppShell, ExternalAppFrame, CAPABILITY_AREAS });


/* ===== funnel.jsx ===== */
/* global React, ReactDOM, Icon */
// Shared funnel furniture: the Ask Asher assist panel, the risk + mode badges,
// and a few small primitives used across funnel screens.
// (The dev-only AccessLadder spine was removed in 2.0.0; it never shipped.)

// ── Risk-boundary badge ──────────────────────────────────────────────────────
const RiskBadge = ({ risk, size = 'md' }) => {
  const map = {
    high: { label: 'High-Assurance', bg: 'var(--risk-high-bg)', fg: 'var(--risk-high)' },
    moderate: { label: 'Operational', bg: 'var(--risk-moderate-bg)', fg: '#8a6a16' },
    sandbox: { label: 'Sandboxed', bg: 'var(--risk-sandbox-bg)', fg: 'var(--risk-sandbox)' },
  };
  const c = map[risk] || map.moderate;
  return (
    <span className="chip" style={{ background: c.bg, color: c.fg, fontSize: size === 'sm' ? 10 : 11 }}>
      <span className="dot" style={{ background: c.fg, width: 6, height: 6 }} /> {c.label}
    </span>
  );
};

// ── Delivery-mode badge (Generate / Augment / Align / Reference) ──────────────
const ModeBadge = ({ mode }) => {
  const map = {
    Generate: { bg: 'rgba(228,170,35,0.14)', fg: 'var(--asher-gold-dark)' },
    Augment: { bg: '#faf1d8', fg: '#8a6a16' },
    Align: { bg: 'var(--asher-success-bg)', fg: 'var(--asher-success-fg)' },
    Reference: { bg: '#eceae3', fg: 'var(--asher-grey-medium)' },
  };
  const c = map[mode] || map.Reference;
  return <span className="chip" style={{ background: c.bg, color: c.fg, fontSize: 10 }}>{mode}</span>;
};

// ── Ask Asher — slide-out reasoning drawer (mirrors AskAsherDrawer.vue) ───────
// Per the production design language, Asher's assist is an inline launcher +
// right slide-out reasoning panel, NEVER a chat-bot UI. The panel renders a
// "Asking about" context card, structured reasoning sections, suggested next
// steps, preset prompts, and a footer disclosure.
const ASHER_FOOTER = "Asher's reasoning here is built from your program context. Live back-and-forth lands alongside future agentic surfaces.";
// Brand mark for Asher's assist. Candidates: 'constellation' (orchestration),
// 'compass' (guidance), 'aperture' (focus/insight), 'brain' (cognition).
const ASHER_ICON = 'constellation';

const AskAsherDrawer = ({ open, panel, onClose }) => {
  // Portal to <body> so position:fixed resolves against the viewport, not a
  // transformed ancestor (entrance-animation wrappers create a containing block).
  const node = (
    <div className={`ask-asher-drawer ${open ? 'is-open' : ''}`} role="dialog" aria-label="Ask Asher" aria-hidden={!open}
      style={{ transform: open ? 'translateX(0)' : 'translateX(100%)', pointerEvents: open ? 'auto' : 'none' }}>
      <div className="ask-asher-drawer__head">
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
          <Icon name={ASHER_ICON} size={17} color="var(--asher-gold)" />
          <span style={{ fontFamily: 'Montserrat', fontSize: 15, fontWeight: 700, color: 'var(--asher-grey-dark)' }}>Ask Asher</span>
        </span>
        <button className="btn btn-ghost btn-sm" onClick={onClose} aria-label="Close"><Icon name="close" size={15} /></button>
      </div>

      <div className="ask-asher-drawer__body">
        {/* Asking-about context card */}
        <div className="ask-asher-context">
          <div className="ask-asher-context__label">Asking about</div>
          <div className="ask-asher-context__subject">{panel.subject}</div>
          {panel.meta && <div className="ask-asher-context__meta">{panel.meta}</div>}
        </div>

        {/* Reasoning sections */}
        {panel.sections.map((s, i) => (
          <section key={i} className="ask-asher-section">
            <h3 className="ask-asher-section__head">
              <Icon name={s.icon} size={12} color={s.gold ? 'var(--asher-gold)' : 'var(--asher-grey-medium)'} />
              {s.title}
            </h3>
            {s.body && <p className="ask-asher-section__body">{s.body}</p>}
            {s.list && (
              <ul className="ask-asher-list">
                {s.list.map((li, j) => <li key={j}>{li}</li>)}
              </ul>
            )}
          </section>
        ))}

        {/* Suggested next steps */}
        {panel.steps && panel.steps.length > 0 && (
          <section className="ask-asher-section">
            <h3 className="ask-asher-section__head">
              <Icon name="circleCheck" size={12} color="var(--asher-grey-medium)" />
              Suggested next steps
            </h3>
            <ol className="ask-asher-steps">
              {panel.steps.map((st, i) => <li key={i}>{st}</li>)}
            </ol>
          </section>
        )}

        {/* Preset prompts — dig deeper (no live chat in this prototype) */}
        {panel.presets && panel.presets.length > 0 && (
          <section className="ask-asher-section">
            <h3 className="ask-asher-section__head">
              <Icon name="compass" size={12} color="var(--asher-grey-medium)" />
              Dig deeper
            </h3>
            <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 7 }}>
              {panel.presets.map((p) => (
                <button key={p} type="button" className="ask-asher-preset" disabled title="Live back-and-forth arrives with agentic surfaces">
                  <Icon name="arrowRight" size={10} /> {p}
                </button>
              ))}
            </div>
          </section>
        )}

        {/* Footer disclosure */}
        <div className="ask-asher-disclosure">
          <Icon name="info" size={12} color="var(--asher-grey-medium)" />
          <span>{ASHER_FOOTER}</span>
        </div>
      </div>
    </div>
  );
  return ReactDOM.createPortal(node, document.body);
};

const AskAsher = ({ surface, title = 'Ask Asher', style }) => {
  const panel = window.DATA.ASHER[surface];
  const [open, setOpen] = React.useState(false);

  // Other surfaces (e.g. Gate scope tooltips) can open the drawer.
  React.useEffect(() => {
    const handler = (e) => {
      const d = e.detail || {};
      if (d.surface && d.surface !== surface) return;
      setOpen(true);
    };
    window.addEventListener('asher:ask', handler);
    return () => window.removeEventListener('asher:ask', handler);
  }, [surface]);

  if (!panel) return null;
  const launchStyle = { ...style };
  delete launchStyle.height; // launcher sizes to content; height was for the old chat card

  return (
    <React.Fragment>
      {/* In-rail launcher */}
      <div className="asher-card ask-asher-launch" style={launchStyle}>
        <div className="ask-asher-launch__head">
          <span style={{ width: 30, height: 30, borderRadius: 8, background: 'var(--asher-gold)', display: 'grid', placeItems: 'center', color: '#fff', flexShrink: 0 }}>
            <Icon name={ASHER_ICON} size={16} />
          </span>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: 'Montserrat', fontSize: 14, fontWeight: 700, color: 'var(--asher-grey-dark)' }}>{title}</div>
            <div style={{ fontSize: 11, color: 'var(--asher-grey-light)' }}>Reasoning grounded in your blueprint &amp; registry</div>
          </div>
        </div>
        <p className="ask-asher-launch__subject">{panel.subject}</p>
        <button className="btn btn-secondary btn-sm" style={{ width: '100%', justifyContent: 'space-between' }} onClick={() => setOpen(true)}>
          See Asher's reasoning <Icon name="arrowRight" size={13} />
        </button>
      </div>

      <AskAsherDrawer open={open} panel={panel} onClose={() => setOpen(false)} />
    </React.Fragment>
  );
};

Object.assign(window, { RiskBadge, ModeBadge, AskAsher });


/* ===== screens-entry.jsx ===== */
/* global React, Icon */
// Entry surfaces, both genuinely new per the URS:
//   (a) Register, institutional-email sign-up with domain verification
//   (b) Triage  , the three-route front door

// Consumer domains blocked from the self-service flow.
const CONSUMER_DOMAINS = ['gmail.com', 'yahoo.com', 'outlook.com', 'hotmail.com', 'icloud.com', 'aol.com', 'proton.me', 'gmx.com'];
const KNOWN_ORGS = { 'ptmc.org': 'Pittsburgh Trauma Medical Center' };

function classifyEmail(email) {
  const m = /^[^@\s]+@([^@\s]+\.[^@\s]+)$/.exec(email.trim().toLowerCase());
  if (!m) return { state: email.length ? 'invalid' : 'empty', domain: '' };
  const domain = m[1];
  if (CONSUMER_DOMAINS.includes(domain)) return { state: 'consumer', domain };
  return { state: 'ok', domain, org: KNOWN_ORGS[domain] || titleCaseDomain(domain) };
}
function titleCaseDomain(d) {
  const base = d.split('.')[0].replace(/[-_]/g, ' ');
  return base.replace(/\b\w/g, (c) => c.toUpperCase());
}

// ── Register ────────────────────────────────────────────────────────────────
const Register = ({ onDone }) => {
  const U = window.DATA.USER;
  const [step, setStep] = React.useState('form'); // form | verify
  // Pre-fill Maya's credentials so a demo (prospect or customer path) can click
  // straight through without typing. Still fully editable. Placeholders remain as
  // the fallback if a field is cleared.
  const [email, setEmail] = React.useState(U.email);
  const [name, setName] = React.useState(`${U.firstName} ${U.lastName}`);
  const [role, setRole] = React.useState(U.title);
  const cls = classifyEmail(email);
  const canSubmit = cls.state === 'ok' && name.trim().length > 1;

  return (
    <div className="canvas-cream" style={{ alignItems: 'center', justifyContent: 'center', padding: '40px 20px' }}>
      <div style={{ width: '100%', maxWidth: 880, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 0, borderRadius: 18, overflow: 'hidden', boxShadow: 'var(--shadow-asher-lg)', background: '#fff' }} className="fade-up">

        {/* Brand rail */}
        <div style={{ background: 'var(--asher-grey-dark)', color: '#fff', padding: '40px 36px', display: 'flex', flexDirection: 'column' }}>
          <img src="assets/asher-lockup-white.png" alt="Asher Informatics" style={{ height: 44, width: 'auto', alignSelf: 'flex-start' }} />
          <div style={{ marginTop: 'auto' }}>
            <div className="eyebrow" style={{ color: 'var(--asher-gold)', marginBottom: 14 }}>Forge · AI Governance Studio</div>
            <h1 style={{ fontSize: 31, lineHeight: 1.16, margin: '0 0 12px', color: '#fff', letterSpacing: '-0.01em' }}>
              Build your AI Management System for clinical AI.
            </h1>
            <div style={{ display: 'inline-flex', alignItems: 'center', gap: 7, fontSize: 12.5, fontWeight: 600, color: 'var(--asher-gold)', marginBottom: 14 }}>
              <Icon name="sparkles" size={13} /> Free to get started. No commitment to explore.
            </div>
            <p style={{ fontSize: 14, lineHeight: 1.65, color: '#c8cdd2', margin: 0 }}>
              An AI-native platform that helps you author, configure, and run the governance and controls behind your clinical AI, so you can put AI to work for your patients safely, responsibly, and ethically.
            </p>
            <div style={{ display: 'flex', gap: 18, marginTop: 28 }}>
              {[['shield', 'ISO 42001-aligned'], ['verified', 'CHAR framework'], ['eye', 'HIPAA-aware']].map(([ic, t]) => (
                <div key={t} style={{ display: 'flex', alignItems: 'center', gap: 7, fontSize: 11.5, color: '#aeb4ba' }}>
                  <Icon name={ic} size={14} color="var(--asher-gold)" /> {t}
                </div>
              ))}
            </div>
          </div>
        </div>

        {/* Form */}
        <div style={{ padding: '40px 36px' }}>
          {step === 'form' && (
            <div className="fade-up">
              <h2 style={{ fontSize: 21, margin: '0 0 4px' }}>Create your account</h2>
              <p className="subtitle" style={{ fontSize: 13, margin: '0 0 22px' }}>Self-service sign-up with your institutional email.</p>

              <label style={lblStyle}>Full name</label>
              <input value={name} onChange={(e) => setName(e.target.value)} placeholder="e.g. Maya Chandra" style={inpStyle} />

              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 16, marginBottom: 6 }}>
                <label style={{ ...lblStyle, margin: 0 }}>Role or title</label>
                <span style={{ fontSize: 10, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em', color: 'var(--asher-grey-light)', background: 'var(--asher-grey-bg)', padding: '2px 7px', borderRadius: 999 }}>Optional</span>
              </div>
              <input value={role} onChange={(e) => setRole(e.target.value)} placeholder="e.g. Director of Imaging Informatics" style={inpStyle} />
              <div style={hintMute}>Helps us tailor your setup, we'll match it to the closest governance role.</div>

              <label style={{ ...lblStyle, marginTop: 16 }}>Work email</label>
              <div style={{ position: 'relative' }}>
                <input
                  value={email} onChange={(e) => setEmail(e.target.value)}
                  placeholder="you@yourhospital.org"
                  style={{ ...inpStyle, borderColor: cls.state === 'consumer' || cls.state === 'invalid' ? 'var(--asher-danger)' : cls.state === 'ok' ? 'var(--asher-success)' : '#d9d4c6', paddingRight: 38 }}
                />
                {cls.state === 'ok' && <Icon name="circleCheck" size={17} color="var(--asher-success)" style={{ position: 'absolute', right: 12, top: 11 }} />}
                {(cls.state === 'consumer' || cls.state === 'invalid') && <Icon name="warning" size={16} color="var(--asher-danger)" style={{ position: 'absolute', right: 12, top: 11 }} />}
              </div>

              {cls.state === 'consumer' && (
                <div style={hintErr}><Icon name="warning" size={13} color="var(--asher-danger)" /> Please use your organization's email. Consumer domains aren't supported for self-service, your enterprise admin can invite you instead.</div>
              )}
              {cls.state === 'ok' && (
                <div style={hintOk}><Icon name="building" size={13} color="var(--asher-success)" /> Recognized <strong style={{ margin: '0 3px' }}>{cls.domain}</strong> → {cls.org}. We'll verify this domain.</div>
              )}
              {cls.state !== 'consumer' && cls.state !== 'ok' && (
                <div style={hintMute}>We'll just check that your email belongs to your organization, that's all you need to get started.</div>
              )}

              <button className="btn btn-primary btn-lg" style={{ width: '100%', marginTop: 22 }} disabled={!canSubmit} onClick={() => setStep('verify')}>
                Create account <Icon name="arrowRight" size={14} />
              </button>

              <div style={{ display: 'flex', alignItems: 'center', gap: 12, margin: '20px 0' }}>
                <div style={{ flex: 1, height: 1, background: '#eceae3' }} /><span style={{ fontSize: 11, color: 'var(--asher-grey-light)' }}>or use your work identity</span><div style={{ flex: 1, height: 1, background: '#eceae3' }} />
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                {[['google', 'Google Workspace'], ['microsoft', 'Microsoft Entra ID'], ['id-card', 'ORCID iD']].map(([ic, p]) => (
                  <button key={p} className="btn btn-secondary" style={{ width: '100%', justifyContent: 'flex-start', gap: 10 }} onClick={() => { setEmail(U.email); setName(`${U.firstName} ${U.lastName}`); setStep('verify'); }}>
                    <i className={`pi pi-${ic}`} style={{ fontSize: 14, color: 'var(--asher-grey-medium)' }} /> Continue with {p}
                  </button>
                ))}
              </div>
              <p style={{ fontSize: 10.5, color: 'var(--asher-grey-light)', marginTop: 12, lineHeight: 1.5 }}>
                Sign in with your organization's work account. Connecting your hospital's own login system is something we set up later, for contracted customers.
              </p>
              <p style={{ fontSize: 12, color: 'var(--asher-grey-medium)', marginTop: 16 }}>
                Already have an account? <a href="#" onClick={(e) => { e.preventDefault(); setEmail(U.email); setName(`${U.firstName} ${U.lastName}`); setStep('verify'); }} style={{ fontWeight: 600 }}>Sign in</a>
              </p>
            </div>
          )}

          {step === 'verify' && (
            <div className="fade-up" style={{ display: 'flex', flexDirection: 'column', height: '100%', minHeight: 420 }}>
              <div style={{ width: 52, height: 52, borderRadius: 14, background: 'var(--asher-gold-50)', display: 'grid', placeItems: 'center', marginBottom: 18 }}>
                <Icon name="mail" size={24} color="var(--asher-gold-dark)" />
              </div>
              <h2 style={{ fontSize: 21, margin: '0 0 6px' }}>Check your inbox</h2>
              <p className="subtitle" style={{ fontSize: 13, margin: 0, lineHeight: 1.55 }}>
                We sent a verification link to <strong style={{ color: 'var(--asher-grey-dark)' }}>{email || window.DATA.USER.email}</strong>. Confirm it to activate your Forge account.
              </p>
              <div style={{ marginTop: 20, padding: 16, background: 'var(--asher-grey-bg)', borderRadius: 10, fontSize: 12.5, color: 'var(--asher-grey-medium)', lineHeight: 1.6 }}>
                <strong style={{ color: 'var(--asher-grey-dark)' }}>While you wait, </strong> your domain is being verified against the organization directory. This keeps Forge limited to real healthcare institutions.
              </div>
              <div style={{ marginTop: 'auto', paddingTop: 24 }}>
                <button className="btn btn-primary btn-lg" style={{ width: '100%' }} onClick={onDone}>
                  I've verified, continue <Icon name="arrowRight" size={14} />
                </button>
                <button className="btn btn-ghost btn-sm" style={{ width: '100%', marginTop: 8 }} onClick={() => setStep('form')}>Use a different email</button>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

const lblStyle = { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--asher-grey-dark)', marginBottom: 6 };
const inpStyle = { width: '100%', fontSize: 14, padding: '11px 13px', borderRadius: 9, border: '1px solid #d9d4c6', outline: 'none' };
const hintErr = { display: 'flex', gap: 7, alignItems: 'flex-start', fontSize: 12, color: 'var(--asher-danger)', marginTop: 8, lineHeight: 1.5 };
const hintOk = { display: 'flex', gap: 7, alignItems: 'center', fontSize: 12, color: 'var(--asher-success-fg)', marginTop: 8, lineHeight: 1.5 };
const hintMute = { fontSize: 12, color: 'var(--asher-grey-light)', marginTop: 8, lineHeight: 1.5 };

// ── Intent triage (the front door) ───────────────────────────────────────────
// A first-time viewer (no org `setup` yet) sees an ordered on-ramp: Explore is
// strongly suggested FIRST so the rest makes sense, then setting up the
// organization is the required Step 2, and Building is locked until that's done.
// The org profile + AI System Registry are durable, cross-cutting objects (the
// Registry is a first-class CHAR tool) that persist across every blueprint, so
// capturing them is framed as setup, not part of any one build. Once set up (or a
// contracted customer arriving pre-filled), Build unlocks and becomes primary.
const PathNode = ({ icon, label, sub, state }) => {
  const fg = state === 'done' ? 'var(--asher-gold-dark)' : state === 'active' ? 'var(--asher-grey-dark)' : 'var(--asher-grey-light)';
  const bg = state === 'done' ? 'var(--asher-gold)' : state === 'active' ? 'var(--asher-grey-dark)' : '#ece9e1';
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 9, minWidth: 0 }}>
      <span style={{ width: 28, height: 28, borderRadius: '50%', flexShrink: 0, display: 'grid', placeItems: 'center', background: bg, color: state === 'locked' ? 'var(--asher-grey-light)' : '#fff' }}>
        <Icon name={icon} size={14} />
      </span>
      <div style={{ minWidth: 0 }}>
        <div style={{ fontSize: 12.5, fontWeight: 700, color: fg, whiteSpace: 'nowrap' }}>{label}</div>
        <div style={{ fontSize: 10.5, color: 'var(--asher-grey-light)', whiteSpace: 'nowrap' }}>{sub}</div>
      </div>
    </div>
  );
};

const Triage = ({ onRoute, firstRun = true, hasSetup = false }) => {
  const U = window.DATA.USER;
  const PACKAGES = window.DATA.PACKAGES || [];
  // The pricing scale lives behind a collapsible toggle on the free-tier bar, so it
  // is available on the front door without occupying eyeball attention by default.
  const [showPricing, setShowPricing] = React.useState(false);
  // A viewer who has set up their org is returning by definition, even if they
  // got there via the sidebar (which never trips firstRun).
  const returning = hasSetup || !firstRun;
  const routes = hasSetup ? [
    { id: 'explore', icon: 'compass', title: 'Explore & learn', desc: 'Browse CHAR, our clinical AI governance framework, any time. No setup needed.', cta: 'Open the explorer', tone: 'plain' },
    { id: 'build', icon: 'rocket', title: 'Build a blueprint', desc: 'Generate a governance plan sized to your registered AI systems and accreditation target. Build as many as you need.', cta: 'Build a blueprint', tone: 'gold', badge: 'Ready' },
    { id: 'orgprofile', icon: 'building', title: 'Organization profile', desc: 'Review or update your organization details and AI System Registry. This profile persists across every blueprint you build.', cta: 'Review profile', tone: 'plain', done: true },
  ] : [
    { id: 'explore', icon: 'compass', title: 'Explore & learn', desc: 'Browse CHAR, our clinical AI governance framework, and see what Forge can do. No setup, no commitment. Start here so setting up and building actually make sense.', cta: 'Start exploring', tone: 'gold', badge: 'Step 1 · Start here' },
    { id: 'setup', icon: 'building', title: 'Set up your organization', desc: 'Tell us about you, your organization, and your AI systems. This is your organization profile and AI System Registry, it stays with you across everything you do in Forge and evolves on its own, independent of any single blueprint.', cta: 'Set up your organization', tone: 'step', badge: 'Step 2' },
    { id: 'contracted', icon: 'verified', title: "I'm a contracted customer", desc: 'Your organization, target, and systems are already pre-filled from your sales conversation, so you skip the setup questions. Review and confirm to get started fast.', cta: 'Resume my setup', tone: 'plain' },
  ];
  return (
    <div style={{ minHeight: '100%', background: '#fcfcfa', display: 'flex', flexDirection: 'column' }}>
      <div className="canvas-cream" style={{ flex: 1, padding: '56px 24px' }}>
        <div style={{ maxWidth: 1080, margin: '0 auto', width: '100%' }} className="fade-up">
          <div className="eyebrow" style={{ color: 'var(--asher-gold-dark)', marginBottom: 10 }}>{returning ? 'Forge front door' : 'Welcome to Forge'}</div>
          <h1 style={{ fontSize: 34, margin: '0 0 8px', letterSpacing: '-0.02em' }}>{returning ? `Welcome back, ${U.firstName} 👋` : `Welcome, ${U.firstName} 👋`}</h1>
          <p className="subtitle" style={{ fontSize: 16, maxWidth: 680 }}>
            {U.title} · {U.org}. {hasSetup
              ? <>Build a blueprint, revisit the framework, or update your organization profile. You can switch any time.</>
              : <>New to Forge? We strongly suggest <strong style={{ color: 'var(--asher-grey-dark)' }}>Explore &amp; learn</strong> first, so setting up and building actually make sense. Then set up your organization, your profile and AI System Registry persist across everything you do here, beyond any single blueprint. Building unlocks once you're set up.</>}
          </p>

          {/* Suggested path — first run only. Explore (recommended) → Set up (required) → Build (locked). */}
          {!hasSetup && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 14, flexWrap: 'wrap', marginTop: 22, padding: '14px 20px', borderRadius: 12, background: '#fff', border: '1px solid #e9e5da' }}>
              <PathNode icon="compass" label="Explore & learn" sub="Recommended first" state="done" />
              <Icon name="chevronRight" size={14} color="#cfcabb" />
              <PathNode icon="building" label="Set up your organization" sub="Step 2 · required to build" state="active" />
              <Icon name="chevronRight" size={14} color="#cfcabb" />
              <PathNode icon="lock" label="Build a blueprint" sub="Unlocks after setup" state="locked" />
            </div>
          )}

          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 18, marginTop: 22 }}>
            {routes.map((r) => {
              const gold = r.tone === 'gold';
              const step = r.tone === 'step';
              const iconBg = gold ? 'var(--asher-gold)' : step ? 'var(--asher-grey-dark)' : r.done ? 'rgba(74,143,94,0.14)' : 'var(--asher-cream)';
              const iconFg = gold || step ? '#fff' : r.done ? 'var(--asher-success-fg)' : 'var(--asher-gold-dark)';
              return (
                <button key={r.id} className="asher-card asher-card--hover" onClick={() => onRoute(r.id)}
                  style={{
                    textAlign: 'left', cursor: 'pointer', padding: 24, display: 'flex', flexDirection: 'column',
                    border: gold ? '2px solid var(--asher-gold)' : step ? '1.5px solid #c4bdac' : '1px solid #e9e5da',
                    background: gold ? 'linear-gradient(160deg,#fffdf6,#fbf4e2)' : '#fff', minHeight: 230,
                  }}>
                  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                    <span style={{ width: 46, height: 46, borderRadius: 12, background: iconBg, display: 'grid', placeItems: 'center', color: iconFg }}>
                      <Icon name={r.done ? 'check' : r.icon} size={22} />
                    </span>
                    {r.done
                      ? <span className="chip" style={{ fontSize: 10, background: 'rgba(74,143,94,0.14)', color: 'var(--asher-success-fg)' }}><Icon name="check" size={11} /> Set up</span>
                      : gold && r.badge ? <span className="chip chip-gold" style={{ fontSize: 10 }}>{r.badge}</span>
                      : r.badge ? <span className="chip" style={{ fontSize: 10, background: 'var(--asher-grey-dark)', color: '#fff' }}>{r.badge}</span>
                      : null}
                  </div>
                  <h3 style={{ fontSize: 18, margin: '18px 0 8px' }}>{r.title}</h3>
                  <p style={{ fontSize: 13, color: 'var(--asher-grey-medium)', lineHeight: 1.55, margin: 0, flex: 1 }}>{r.desc}</p>
                  <span style={{ display: 'inline-flex', alignItems: 'center', gap: 7, marginTop: 18, fontSize: 13, fontWeight: 600, color: gold ? 'var(--asher-gold-dark)' : 'var(--asher-grey-dark)' }}>
                    {r.cta} <Icon name="arrowRight" size={14} />
                  </span>
                </button>
              );
            })}
          </div>

          {/* Build is locked until the organization is set up — shown so the viewer
              knows it's coming and why, without it being a dead/clickable card. */}
          {!hasSetup && (
            <div style={{ marginTop: 18, display: 'flex', alignItems: 'center', gap: 14, padding: '16px 20px', borderRadius: 12, background: '#f6f5f1', border: '1px dashed #d3cdbd' }}>
              <span style={{ width: 38, height: 38, borderRadius: 10, flexShrink: 0, background: '#ece9e1', display: 'grid', placeItems: 'center', color: 'var(--asher-grey-light)' }}><Icon name="lock" size={17} /></span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--asher-grey-medium)' }}>Step 3 · Build a blueprint</div>
                <div style={{ fontSize: 12.5, color: 'var(--asher-grey-light)', lineHeight: 1.5 }}>Unlocks once your organization is set up. Blueprints are generated from the profile and AI systems you register in Step 2, so there's nothing to build until that's in place.</div>
              </div>
              <span className="chip" style={{ fontSize: 10, flexShrink: 0 }}><Icon name="lock" size={11} /> Locked</span>
            </div>
          )}

          {/* Free-tier reassurance — CRO asked for a standing reminder of what's free
              before the visitor commits, to lower the friction of choosing Build. The
              full pricing scale collapses behind a toggle here so it's available on the
              front door without dominating it. */}
          <div style={{ marginTop: 22, borderRadius: 12, background: 'linear-gradient(135deg,#fffdf6,#fbf4e2)', border: '1px solid rgba(228,170,35,0.28)', overflow: 'hidden' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 20, flexWrap: 'wrap', padding: '14px 20px' }}>
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontSize: 13, fontWeight: 700, color: 'var(--asher-gold-dark)', whiteSpace: 'nowrap' }}>
                <Icon name="sparkles" size={15} /> Free to start, no credit card
              </span>
              <div style={{ display: 'flex', gap: 20, flexWrap: 'wrap', flex: 1 }}>
                {[
                  'Your full governance blueprint + policy artifacts',
                  'Build 3 process domains (SOPs & controls)',
                  '1 operational example to try',
                ].map((t) => (
                  <span key={t} style={{ display: 'inline-flex', alignItems: 'center', gap: 7, fontSize: 12.5, color: 'var(--asher-grey-medium)' }}>
                    <Icon name="check" size={13} color="var(--asher-gold-dark)" /> {t}
                  </span>
                ))}
              </div>
              <button onClick={() => setShowPricing((s) => !s)} className="btn btn-ghost btn-sm" style={{ marginLeft: 'auto', whiteSpace: 'nowrap', color: 'var(--asher-gold-dark)' }} aria-expanded={showPricing}>
                <Icon name="tag" size={13} /> {showPricing ? 'Hide plans' : 'See plans & pricing'} <Icon name={showPricing ? 'chevronUp' : 'chevronDown'} size={12} />
              </button>
            </div>

            {/* Collapsible pricing scale (the named packages from the paygate) */}
            {showPricing && (
              <div className="fade-up" style={{ borderTop: '1px solid rgba(228,170,35,0.25)', background: 'rgba(255,255,255,0.6)', padding: '18px 20px' }}>
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 12 }}>
                  {PACKAGES.map((p) => (
                    <div key={p.id} className="asher-card" style={{ padding: 14, border: p.current ? '1.5px solid var(--asher-gold)' : '1px solid #e9e5da', display: 'flex', flexDirection: 'column', gap: 7 }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                        <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--asher-grey-dark)' }}>{p.name}</span>
                        {p.current && <span className="chip chip-gold" style={{ fontSize: 8.5, marginLeft: 'auto' }}>You're here</span>}
                      </div>
                      <div>
                        {p.price ? (
                          <>
                            {p.startingAt && <span style={{ fontSize: 9.5, color: 'var(--asher-grey-light)' }}>from </span>}
                            <span style={{ fontSize: 19, fontWeight: 800, fontFamily: 'Montserrat', color: 'var(--asher-grey-dark)' }}>{p.price}</span>
                            <span style={{ fontSize: 10.5, color: 'var(--asher-grey-light)' }}> /mo</span>
                          </>
                        ) : (
                          <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--asher-grey-medium)' }}>{p.priceNote}</span>
                        )}
                      </div>
                      <div style={{ fontSize: 11, color: 'var(--asher-grey-light)', lineHeight: 1.45 }}>{p.tagline}</div>
                    </div>
                  ))}
                </div>
                <p style={{ fontSize: 10.5, color: 'var(--asher-grey-light)', margin: '12px 0 0', lineHeight: 1.5 }}>
                  Billed monthly or annually. Department &amp; Service Line are "starting at" — final pricing scales with your scope. <strong style={{ color: 'var(--asher-grey-medium)' }}>Do everything in Free first</strong>, upgrade only when you're ready to build it all.
                </p>
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
};

Object.assign(window, { Register, Triage });


/* ===== screen-onboarding.jsx ===== */
/* global React, Icon, RiskBadge */
// Onboarding, the de-novo "who are you + your organization" capture that runs
// right after registration. Implements ADR-032's three-domain model in the UX:
//   · D2 (real-world structure): captured with PICKERS, and the ENCLOSING facility
//     is pulled by reference from the knowledge base, never the whole org tree.
//   · D1/D3 (licensed + governance scope): the governable slice is bounded and
//     visualized, so a free/department user can't scope an entire health system.
// Top-of-system, granularity, and autonomy are DERIVED, not free-typed.

// Radiology SERVICES / OFFERINGS, what the group provides (a single group may
// offer several). Separate from administrative structure below.
const RAD_OFFERINGS = [
  'In-patient Diagnostic Imaging',
  'Acute & Trauma Imaging',
  'Outpatient & Specialty Imaging',
  'Interventional Radiology',
  'Imaging Research & Translation Lab',
  'Affiliated (owned) Free-Standing Imaging Centers',
];
const GENERIC_OFFERINGS = ['In-patient services', 'Outpatient clinics', 'Affiliated / owned sites', 'Research & innovation'];
// ADMINISTRATIVE sub-units, sections led by a chief (sub-specialty divisions).
const RAD_DIVISIONS = ['Abdominal Imaging', 'Neuroradiology', 'Musculoskeletal (MSK)', 'Cardiothoracic / Chest', 'Breast Imaging', 'Interventional Radiology', 'Nuclear Medicine', 'Pediatric Radiology', 'Emergency / Trauma Radiology'];
const GENERIC_DIVISIONS = [];

// `embedded` renders inside the app shell (no full-bleed brand bar): used both as
// the just-in-time capture when someone first chooses Build, and as the editable
// "Organization Context" destination in the sidebar. Same data, two entry points.
const Onboarding = ({ onDone, onBack, embedded = false, building = false, setup = null }) => {
  const D = window.DATA;
  // Hydrate from a previously-captured setup so revisiting Organization Context
  // shows what was saved (role, relationship, facility, node) rather than reverting
  // to defaults. (Deeper StepOrg-local fields — offers/divisions/AI-mix — are a
  // known wireframe seam and still re-default on re-edit.)
  const [step, setStep] = React.useState('you');     // you | org
  const [role, setRole] = React.useState((setup && setup.role) || 'owner');
  const [rel, setRel] = React.useState((setup && setup.rel) || 'partOf');    // independent | partOf
  const [enclosing, setEnclosing] = React.useState((setup && (setup.rel === 'independent' ? setup.indepType : setup.facilityId)) || 'ptmc');
  const [node, setNode] = React.useState((setup && setup.nodeId) || 'rad');
  const [query, setQuery] = React.useState('');

  const fac = D.DIRECTORY.find((f) => f.id === enclosing);
  const selectedLine = D.ORG.serviceLines.find((s) => s.id === node);
  const tooBig = rel === 'independent' && false; // independent handled separately
  const orgReady = rel === 'independent' ? true : !!enclosing && !!node;

  return (
    <div className={embedded ? '' : 'canvas-cream'} style={embedded ? {} : { minHeight: '100vh' }}>
      {/* Header: brand bar full-bleed; in-shell just the step indicator */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: embedded ? '14px 28px' : '16px 28px', borderBottom: '1px solid #ece8dd', background: embedded ? '#fff' : 'rgba(255,255,255,0.6)' }}>
        {embedded ? (
          <span style={{ fontFamily: 'Montserrat', fontWeight: 600, fontSize: 13, color: 'var(--asher-grey-medium)' }}>Organization context</span>
        ) : (
          <>
            <img src="assets/asher-lockup-color.png" alt="Asher Informatics" style={{ height: 24 }} />
            <span style={{ fontFamily: 'Montserrat', fontWeight: 600, fontSize: 13, color: 'var(--asher-grey-medium)', paddingLeft: 12, borderLeft: '1px solid #ddd8cb' }}>Forge · Set up your workspace</span>
          </>
        )}
        <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 8 }}>
          <StepDot active={step === 'you'} done={step === 'org'} n={1} label="You" />
          <div style={{ width: 22, height: 1, background: '#ddd8cb' }} />
          <StepDot active={step === 'org'} n={2} label="Organization" />
          {building && (
            <>
              <div style={{ width: 22, height: 1, background: '#ddd8cb' }} />
              <StepDot n={3} label="AI systems" />
            </>
          )}
        </div>
      </div>

      <div style={{ flex: 1, padding: '36px 28px 60px', maxWidth: 1140, margin: '0 auto', width: '100%' }}>
        {step === 'you' ? (
          <StepYou role={role} setRole={setRole} onNext={() => setStep('org')} onBack={onBack} />
        ) : (
          <StepOrg {...{ rel, setRel, enclosing, setEnclosing, node, setNode, query, setQuery, fac, selectedLine, orgReady, role, setup }} onBack={() => setStep('you')} onDone={onDone} />
        )}
      </div>
    </div>
  );
};

const StepDot = ({ active, done, n, label }) => (
  <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
    <span style={{ width: 22, height: 22, borderRadius: '50%', display: 'grid', placeItems: 'center', fontSize: 11, fontWeight: 700,
      background: done ? 'var(--asher-gold)' : active ? '#fff' : '#ecebe6', border: active ? '2px solid var(--asher-gold)' : 'none',
      color: done ? '#fff' : active ? 'var(--asher-gold-dark)' : 'var(--asher-grey-light)' }}>{done ? <Icon name="check" size={11} /> : n}</span>
    <span style={{ fontSize: 12, fontWeight: active ? 700 : 500, color: active || done ? 'var(--asher-grey-dark)' : 'var(--asher-grey-light)' }}>{label}</span>
  </div>
);

// ── Step 1 · Who is Maya ─────────────────────────────────────────────────────
const StepYou = ({ role, setRole, onNext, onBack }) => {
  const D = window.DATA;
  const seat = D.ROLES.find((r) => r.id === role).seat;
  const seatInfo = D.SEAT_LABEL[seat];
  return (
    <div className="fade-up" style={{ maxWidth: 760, margin: '0 auto' }}>
      <div className="eyebrow" style={{ color: 'var(--asher-gold-dark)', marginBottom: 8 }}>Step 1 · About you</div>
      <h1 style={{ fontSize: 30, margin: '0 0 6px', letterSpacing: '-0.01em' }}>A bit about you, {D.USER.firstName}</h1>
      <p className="subtitle" style={{ fontSize: 15, marginBottom: 26 }}>What's your role? This sets your seat and what you can do in the workspace.</p>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
        {D.ROLES.map((r) => {
          const on = role === r.id;
          return (
            <button key={r.id} onClick={() => setRole(r.id)} style={{
              display: 'flex', alignItems: 'center', gap: 14, textAlign: 'left', fontFamily: 'inherit', cursor: 'pointer', padding: 16,
              border: on ? '2px solid var(--asher-gold)' : '1px solid #e3e0d8', borderRadius: 12, background: on ? 'linear-gradient(160deg,#fffdf6,#fbf4e2)' : '#fff',
            }}>
              <span style={{ width: 42, height: 42, borderRadius: 11, background: on ? 'var(--asher-gold)' : 'var(--asher-cream)', display: 'grid', placeItems: 'center', color: on ? '#fff' : 'var(--asher-gold-dark)', flexShrink: 0 }}><Icon name={r.icon} size={19} /></span>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 14.5, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>{r.title}</div>
                <div style={{ fontSize: 12.5, color: 'var(--asher-grey-medium)' }}>{r.desc}</div>
              </div>
              <SeatBadge seat={r.seat} highlight={on} />
              <Icon name={on ? 'circleCheck' : 'circle'} size={18} color={on ? 'var(--asher-gold)' : '#cfcabd'} />
            </button>
          );
        })}
      </div>

      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 9, marginTop: 14, fontSize: 12, color: 'var(--asher-grey-medium)', padding: '12px 14px', background: 'var(--asher-grey-bg)', borderRadius: 10, lineHeight: 1.55 }}>
        <Icon name="info" size={13} color="var(--asher-gold-dark)" style={{ flexShrink: 0, marginTop: 1 }} />
        <span><strong style={{ color: 'var(--asher-grey-dark)' }}>{seatInfo.label}:</strong> {seatInfo.def} {seatInfo.note}</span>
      </div>

      <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 24 }}>
        <button className="btn btn-secondary" onClick={onBack}><Icon name="chevronLeft" size={13} /> Back</button>
        <button className="btn btn-primary btn-lg" onClick={onNext}>Continue <Icon name="arrowRight" size={14} /></button>
      </div>
    </div>
  );
};

// ── Step 2 · The organization hierarchy (D2 capture via pickers) ─────────────
const StepOrg = ({ rel, setRel, enclosing, setEnclosing, node, setNode, query, setQuery, fac, selectedLine, orgReady, role, setup, onBack, onDone }) => {
  const D = window.DATA;
  // OFFERINGS / DIVISIONS depend only on the (already-hydrated) node, so derive them
  // before state — the initializers below reverse-map saved offer/division NAMES back
  // to checklist indices so a re-edit opens with everything that was committed.
  const isRad = node === 'rad';
  const OFFERINGS = isRad ? RAD_OFFERINGS : GENERIC_OFFERINGS;
  const DIVISIONS = isRad ? RAD_DIVISIONS : GENERIC_DIVISIONS;
  const S = setup || {};
  const idxOf = (names, list) => (names || []).map((n) => list.indexOf(n)).filter((i) => i >= 0);
  const [granularity, setGranularity] = React.useState(S.granularity || null);
  const [offers, setOffers] = React.useState(idxOf(S.offers, OFFERINGS));
  const [divided, setDivided] = React.useState(S.divided || null);
  const [divs, setDivs] = React.useState(idxOf(S.divs, DIVISIONS));
  const [moreOpen, setMoreOpen] = React.useState(false);
  // Re-seed a custom service line (id like "cust…") so its name resolves on re-edit.
  const [customLines, setCustomLines] = React.useState(S.nodeId && /^cust/.test(S.nodeId) && S.nodeName ? [{ id: S.nodeId, name: S.nodeName }] : []);
  const [customName, setCustomName] = React.useState('');
  const [aiMix, setAiMix] = React.useState(S.aiMix || null); // vended | mixed | inhouse — shapes the suggested committee
  const MORE_LINES = [{ id: 'cc', name: 'Critical Care' }, { id: 'pulm', name: 'Pulmonology' }, { id: 'neuro', name: 'Neurology' }, { id: 'surg', name: 'Surgery' }, { id: 'obgyn', name: 'Obstetrics & Gynecology' }, { id: 'ortho', name: 'Orthopedics' }];
  const addCustom = () => { if (!customName.trim()) return; const id = 'cust' + Date.now(); setCustomLines([...customLines, { id, name: customName.trim() }]); setNode(id); setCustomName(''); };
  const nodeName = (D.ORG.serviceLines.find((s) => s.id === node) || MORE_LINES.find((s) => s.id === node) || customLines.find((s) => s.id === node) || {}).name || null;
  const scopeNote = offers.length ? `${offers.length} of ${OFFERINGS.length} services` : null;
  const indepType = rel === 'independent' ? enclosing : null;
  const indepSmall = indepType === 'practice' || indepType === 'institute';
  const tooBroad = (rel === 'independent' && (indepType === 'enterprise' || indepType === 'trhs')) || (rel === 'partOf' && fac && fac.id === 'trhs');
  const partOfNode = rel === 'partOf' && fac && fac.id !== 'trhs' && !!nodeName;
  const govName = partOfNode ? nodeName : indepSmall ? (indepType === 'practice' ? 'Your practice' : 'Your institute') : null;
  const canFinish = !tooBroad && !!govName;
  const results = D.DIRECTORY.filter((f) => !query || f.name.toLowerCase().includes(query.toLowerCase()));
  const wholeSystem = rel === 'independent' && enclosing === 'trhs';

  // Snapshot of the choices Stage A needs: what you govern (node + offerings) / where (facility).
  const buildSetup = () => ({
    role,
    rel,
    isTop: rel === 'independent',
    facilityId: rel === 'partOf' ? enclosing : null,
    facilityName: rel === 'partOf' ? (fac && fac.name) : null,
    facilityKind: rel === 'partOf' ? (fac && fac.kind) : null,
    indepType: rel === 'independent' ? enclosing : null,
    nodeId: rel === 'partOf' ? node : null,
    nodeName: rel === 'partOf' ? nodeName : null,
    govName,
    granularity,
    granularityLabel: granularity ? GRAN_LABELS[granularity] : null,
    offers: offers.map((i) => OFFERINGS[i]).filter(Boolean),
    divided,
    divs: divs.map((i) => DIVISIONS[i]).filter(Boolean),
    isRad,
    aiMix,
    committee: aiMix ? deriveCommittee(govName, aiMix) : null,
  });

  return (
    <div className="fade-up">
      <div style={{ maxWidth: 760, marginBottom: 22 }}>
        <div className="eyebrow" style={{ color: 'var(--asher-gold-dark)', marginBottom: 8 }}>Step 2 · Your organization</div>
        <h1 style={{ fontSize: 30, margin: '0 0 6px', letterSpacing: '-0.01em' }}>Where does your governance live?</h1>
        <p className="subtitle" style={{ fontSize: 15 }}>Tell us about your organization's structure. We use it to frame the recommendations our agentic wizards make later, and to set the boundaries of your governance scope. Where possible, we pull what we need from the Asher Knowledge Graph, so you don't re-enter it.</p>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 360px', gap: 26, alignItems: 'flex-start' }}>
        {/* LEFT, the pickers */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 22 }}>
          {/* Q1 relationship */}
          <Field n="1" label="Is your organization independent, or part of something larger?">
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
              <PickCard on={rel === 'partOf'} onClick={() => setRel('partOf')} icon="sitemap" title="Part of a larger organization" sub="A department, service line, or facility inside a hospital or system" />
              <PickCard on={rel === 'independent'} onClick={() => setRel('independent')} icon="building" title="Independent / top of our system" sub="A standalone practice, or the head of a health system" />
            </div>
          </Field>

          {rel === 'partOf' && (
            <>
              {/* Q2 enclosing facility, KB reference */}
              <Field n="2" label="Find the hospital or health system you belong to" hint="We reference it from the Asher Knowledge Graph, so demographics, CCN, and quality come along automatically.">
                <div style={{ position: 'relative', marginBottom: 10 }}>
                  <Icon name="building" size={14} color="#a9b0a6" style={{ position: 'absolute', left: 12, top: 11 }} />
                  <input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search e.g. “Pittsburgh”…" style={{ width: '100%', fontSize: 13.5, padding: '10px 12px 10px 34px', borderRadius: 9, border: '1px solid #d9d4c6', outline: 'none' }} />
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                  {results.map((f) => {
                    const on = enclosing === f.id;
                    return (
                      <button key={f.id} onClick={() => setEnclosing(f.id)} style={{
                        textAlign: 'left', fontFamily: 'inherit', cursor: 'pointer', padding: '11px 13px', display: 'flex', gap: 12, alignItems: 'center',
                        border: on ? '2px solid var(--asher-gold)' : '1px solid #e3e0d8', borderRadius: 10, background: on ? '#fffdf6' : '#fff',
                      }}>
                        <Icon name={on ? 'circleCheck' : 'circle'} size={17} color={on ? 'var(--asher-gold)' : '#cfcabd'} style={{ flexShrink: 0 }} />
                        <div style={{ flex: 1 }}>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                            <span style={{ fontSize: 13.5, fontWeight: 600 }}>{f.name}</span>
                            {f.primary && <span className="chip chip-gold" style={{ fontSize: 9, gap: 3 }} title="Matched from your email domain via the Asher Knowledge Graph"><Icon name="star" size={9} /> Best guess from your domain</span>}
                          </div>
                          <div style={{ fontSize: 11.5, color: 'var(--asher-grey-light)' }}>{f.kind} · {f.city} · {f.level}</div>
                        </div>
                        {f.id === 'trhs' && <span className="chip" style={{ fontSize: 9.5 }}>Health system</span>}
                      </button>
                    );
                  })}
                </div>
                {fac && (
                  <div className="fade-up" style={{ marginTop: 12, padding: 14, background: 'var(--asher-grey-bg)', borderRadius: 11, border: '1px solid #e9e5da' }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
                      <Icon name="book" size={13} color="var(--asher-gold-dark)" />
                      <span className="eyebrow" style={{ fontSize: 9.5 }}>From the Asher Knowledge Graph · read-only</span>
                      <span className="chip chip-success" style={{ fontSize: 9, marginLeft: 'auto' }}><Icon name="check" size={9} /> Linked</span>
                    </div>
                    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7 }}>
                      {[['Type', fac.kind], ['Beds', fac.beds], ['CCN', fac.ccn], ['Quality', fac.quality], ['Region', fac.region]].map(([k, v]) => (
                        <span key={k} style={{ fontSize: 11, color: 'var(--asher-grey-medium)', background: '#fff', border: '1px solid #e9e5da', padding: '4px 9px', borderRadius: 7 }}>
                          <span style={{ color: 'var(--asher-grey-light)' }}>{k}:</span> <strong style={{ color: 'var(--asher-grey-dark)' }}>{v}</strong>
                        </span>
                      ))}
                    </div>
                    <div style={{ fontSize: 11, color: 'var(--asher-grey-light)', marginTop: 9 }}>You don't re-enter these. They ground your governance in the real setting.</div>
                  </div>
                )}
              </Field>

              {/* Q3 your node */}
              {fac && fac.id !== 'trhs' && (
                <Field n="3" label={`Which part of ${fac.name} are you setting up governance for?`} hint="This becomes your governance scope. You author here; enterprise policy flows down to you.">
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                    {D.ORG.serviceLines.map((s) => {
                      const on = node === s.id;
                      return (
                        <button key={s.id} onClick={() => setNode(s.id)} style={{
                          textAlign: 'left', fontFamily: 'inherit', cursor: 'pointer', padding: '11px 13px', display: 'flex', gap: 11, alignItems: 'center',
                          border: on ? '2px solid var(--asher-gold)' : '1px solid #e3e0d8', borderRadius: 10, background: on ? '#fffdf6' : '#fff',
                        }}>
                          <Icon name={on ? 'circleCheck' : 'circle'} size={16} color={on ? 'var(--asher-gold)' : '#cfcabd'} style={{ flexShrink: 0 }} />
                          <span style={{ flex: 1, fontSize: 13, fontWeight: on ? 600 : 500, color: 'var(--asher-grey-dark)' }}>{s.name}</span>
                          {node === s.id && granularity && <span className="chip chip-gold" style={{ fontSize: 9.5 }}>{({ service_line: 'Service line', department: 'Department', division: 'Division / section', institute: 'Institute / center' })[granularity]}</span>}
                        </button>
                      );
                    })}
                  </div>
                  <button onClick={() => setMoreOpen((o) => !o)} style={{ marginTop: 8, display: 'flex', alignItems: 'center', gap: 6, fontFamily: 'inherit', fontSize: 12, fontWeight: 600, color: 'var(--asher-gold-dark)', background: 'transparent', border: 'none', cursor: 'pointer' }}>
                    <Icon name={moreOpen ? 'chevronDown' : 'chevronRight'} size={12} /> More service lines
                  </button>
                  {moreOpen && (
                    <div className="fade-up" style={{ display: 'flex', flexDirection: 'column', gap: 8, marginTop: 8 }}>
                      {[...MORE_LINES, ...customLines].map((s) => {
                        const on = node === s.id;
                        return (
                          <button key={s.id} onClick={() => setNode(s.id)} style={{ textAlign: 'left', fontFamily: 'inherit', cursor: 'pointer', padding: '11px 13px', display: 'flex', gap: 11, alignItems: 'center', border: on ? '2px solid var(--asher-gold)' : '1px solid #e3e0d8', borderRadius: 10, background: on ? '#fffdf6' : '#fff' }}>
                            <Icon name={on ? 'circleCheck' : 'circle'} size={16} color={on ? 'var(--asher-gold)' : '#cfcabd'} style={{ flexShrink: 0 }} />
                            <span style={{ flex: 1, fontSize: 13, fontWeight: on ? 600 : 500, color: 'var(--asher-grey-dark)' }}>{s.name}</span>
                          </button>
                        );
                      })}
                      <div style={{ display: 'flex', gap: 8 }}>
                        <input value={customName} onChange={(e) => setCustomName(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') addCustom(); }} placeholder="Add your own, e.g. Nephrology" style={{ flex: 1, fontSize: 12.5, padding: '9px 11px', borderRadius: 8, border: '1px dashed #d9d4c6', outline: 'none' }} />
                        <button className="btn btn-secondary btn-sm" onClick={addCustom}>Add</button>
                      </div>
                    </div>
                  )}
                  {nodeName && (
                    <div style={{ marginTop: 12, padding: 12, background: 'var(--asher-grey-bg)', borderRadius: 10 }}>
                      <div className="eyebrow" style={{ fontSize: 9.5, marginBottom: 8 }}>How is {nodeName} organized at {fac.name}?</div>
                      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7 }}>
                        {[['service_line', 'Service line'], ['department', 'Department'], ['division', 'Division / section'], ['institute', 'Institute / center']].map(([id, label]) => (
                          <GranOption key={id} id={id} label={label} sel={granularity === id} onClick={() => setGranularity(id)} />
                        ))}
                      </div>
                      <p style={{ fontSize: 11, color: 'var(--asher-grey-light)', margin: '9px 0 0', lineHeight: 1.5 }}>How you run this area sets your governance granularity. Not sure? That is okay. We will refine it together later, and you can Ask Asher for help any time.</p>
                    </div>
                  )}
                </Field>
              )}
            </>
          )}

          {rel === 'independent' && (
            <Field n="2" label="What best describes your organization?" hint="This sets your governance granularity.">
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                {[['practice', 'Independent practice / IDTF', 'building'], ['enterprise', 'A hospital / medical center', 'building'], ['institute', 'An institute or center', 'sitemap'], ['trhs', 'A whole health system', 'sitemap']].map(([id, label, ic]) => (
                  <PickCard key={id} on={enclosing === id} onClick={() => setEnclosing(id)} icon={ic} title={label} sub="" compact />
                ))}
              </div>
            </Field>
          )}

          {/* Shared, services/offerings + administrative structure (both paths) */}
          {govName && (
            <div className="asher-card" style={{ padding: 18 }}>
              <h3 style={{ fontSize: 15, margin: '0 0 14px' }}>What does {govName} look like?</h3>
              <div className="eyebrow" style={{ fontSize: 9.5, marginBottom: 2 }}>Which of these does {govName} offer?</div>
              <CheckList title="Services & offerings" items={OFFERINGS} selected={offers} allowOther
                onToggle={(i) => setOffers(offers.includes(i) ? offers.filter((x) => x !== i) : [...offers, i])}
                onAll={() => setOffers(offers.length === OFFERINGS.length ? [] : OFFERINGS.map((_, i) => i))}
                footer="These set how far this program's policies, SOPs, and controls reach. You can carve one into its own program later, for example keeping Research separate." />
              <div style={{ marginTop: 16 }}>
                <div className="eyebrow" style={{ fontSize: 9.5, marginBottom: 3 }}>Administratively, is {govName} one group or divided into sub-units?</div>
                <p style={{ fontSize: 11, color: 'var(--asher-grey-light)', margin: '0 0 9px', lineHeight: 1.5 }}>{isRad ? 'Sub-units are sections led by a chief. For example, Abdominal Imaging or Neuroradiology, where those radiologists sit.' : 'Sub-units are sections, each led by a lead or chief.'}</p>
                <div style={{ display: 'flex', gap: 8 }}>
                  {[['one', 'One group'], ['subunits', 'Divided into sub-units']].map(([id, label]) => {
                    const sel = divided === id;
                    return <button key={id} onClick={() => setDivided(id)} style={{ fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600, cursor: 'pointer', padding: '6px 13px', borderRadius: 999, border: sel ? '1px solid var(--asher-gold)' : '1px solid #e0dccf', background: sel ? 'var(--asher-cream)' : '#fff', color: sel ? 'var(--asher-gold-dark)' : 'var(--asher-grey-medium)' }}>{label}</button>;
                  })}
                </div>
                {divided === 'subunits' && DIVISIONS.length > 0 && (
                  <CheckList title="Sections / divisions (each led by a chief)" items={DIVISIONS} selected={divs} allowOther
                    onToggle={(i) => setDivs(divs.includes(i) ? divs.filter((x) => x !== i) : [...divs, i])}
                    onAll={() => setDivs(divs.length === DIVISIONS.length ? [] : DIVISIONS.map((_, i) => i))} />
                )}
                {divided === 'subunits' && DIVISIONS.length === 0 && (
                  <p style={{ fontSize: 11, color: 'var(--asher-grey-light)', marginTop: 8 }}>List your sections in the next step, and we'll set them up with you.</p>
                )}
              </div>
            </div>
          )}

          {govName && (
            <GovernanceCard govName={govName} aiMix={aiMix} setAiMix={setAiMix}
              divNames={divided === 'subunits' ? divs.map((i) => DIVISIONS[i]).filter(Boolean) : []} />
          )}
        </div>

        {/* RIGHT, live scope guardrail */}
        <div style={{ position: 'sticky', top: 18, display: 'flex', flexDirection: 'column', gap: 14 }}>
          <ScopeGuardrail tooBroad={tooBroad} outerName={rel === 'partOf' ? (fac && fac.name) : govName} govName={govName} scopeNote={scopeNote} flowDown={rel === 'partOf'} others={partOfNode ? D.ORG.serviceLines.filter((s) => s.id !== node) : []} />
          <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8 }}>
            <button className="btn btn-secondary" onClick={onBack}><Icon name="chevronLeft" size={13} /> Back</button>
            <button className="btn btn-primary" style={{ flex: 1 }} onClick={() => onDone(buildSetup())} disabled={!canFinish}>
              {tooBroad ? 'Scope too broad for self-service' : <>Finish setup <Icon name="arrowRight" size={13} /></>}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

// Live scope visualization, plain-language framing of D2 / governable slice / expansion.
const ScopeGuardrail = ({ tooBroad, outerName, govName, scopeNote, flowDown, others = [] }) => (
  <div className="asher-card" style={{ padding: 16 }}>
    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
      <Icon name="layers" size={15} color="var(--asher-gold-dark)" />
      <span style={{ fontFamily: 'Montserrat', fontSize: 13.5, fontWeight: 700 }}>Your AI Governance Scope</span>
    </div>
    <p style={{ fontSize: 11.5, color: 'var(--asher-grey-light)', margin: '0 0 14px', lineHeight: 1.5 }}>This is the functional scope Asher will work within for this session. Our agentic wizards step you through your detailed processes here, and for now your account stays focused on this area.</p>

    {tooBroad ? (
      <div style={{ padding: 14, background: 'rgba(192,73,60,0.06)', border: '1px solid rgba(192,73,60,0.25)', borderRadius: 10 }}>
        <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
          <Icon name="warning" size={15} color="var(--asher-danger)" style={{ flexShrink: 0, marginTop: 1 }} />
          <div>
            <div style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--asher-grey-dark)', marginBottom: 4 }}>That's an enterprise engagement</div>
            <p style={{ fontSize: 11.5, color: 'var(--asher-grey-medium)', lineHeight: 1.55, margin: 0 }}>
              Governing an entire hospital or health system isn't a self-service setup. Start with a single department or service line, or talk to our team about an enterprise program.
            </p>
            <button className="btn btn-secondary btn-sm" style={{ marginTop: 10 }}><Icon name="mail" size={11} /> Contact our team</button>
          </div>
        </div>
      </div>
    ) : govName ? (
      <>
        <div style={{ border: '1px dashed #d4cfbf', borderRadius: 11, padding: 11, background: '#fbfaf6' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 9 }}>
            <Icon name="building" size={12} color="var(--asher-grey-light)" />
            <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--asher-grey-medium)' }}>{outerName || 'Your organization'}</span>
            <span style={{ marginLeft: 'auto', fontSize: 9.5, color: 'var(--asher-grey-light)' }}>{flowDown ? 'full picture' : 'your organization'}</span>
          </div>
          <div style={{ border: '2px solid var(--asher-gold)', borderRadius: 9, padding: 10, background: 'linear-gradient(160deg,#fffdf6,#fbf4e2)', marginBottom: flowDown && others.length ? 8 : 0 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
              <Icon name="shield" size={13} color="var(--asher-gold-dark)" />
              <span style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--asher-grey-dark)' }}>{govName}</span>
            </div>
            <div style={{ fontSize: 10.5, color: 'var(--asher-gold-dark)', fontWeight: 600, marginTop: 3 }}>You govern this{scopeNote ? ` · ${scopeNote}` : ''}</div>
          </div>
          {flowDown && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
              {others.slice(0, 4).map((s) => (
                <div key={s.id} style={{ display: 'flex', alignItems: 'center', gap: 7, padding: '6px 9px', borderRadius: 7, background: '#fff', border: '1px solid #efedE7', opacity: 0.75 }}>
                  <Icon name="lock" size={10} color="#c4c0b6" />
                  <span style={{ fontSize: 11, color: 'var(--asher-grey-light)' }}>{s.name}</span>
                </div>
              ))}
            </div>
          )}
        </div>
        <div style={{ fontSize: 11, color: 'var(--asher-grey-medium)', lineHeight: 1.55, marginTop: 12, display: 'flex', gap: 8 }}>
          <Icon name="info" size={12} color="var(--asher-gold-dark)" style={{ flexShrink: 0, marginTop: 1 }} />
          <span>{flowDown
            ? <>If {outerName} has its own AI policies or guidelines, we will fold them in once you point us to them. The greyed areas sit outside your scope for now, and are where you could expand later.</>
            : <>You are the top of your own organization, so you set policy for all of it. Nothing flows down from above.</>}</span>
        </div>
        {flowDown && (
          <div style={{ fontSize: 10.5, color: 'var(--asher-grey-light)', lineHeight: 1.5, marginTop: 8, paddingLeft: 20 }}>
            No AI governance above you yet? You can <a href="#" onClick={(e) => e.preventDefault()} style={{ fontWeight: 600 }}>tell your leadership about Asher Forge</a>.
          </div>
        )}
      </>
    ) : (
      <div style={{ padding: '18px 14px', textAlign: 'center', fontSize: 12, color: 'var(--asher-grey-light)' }}>Pick your organization to see your scope.</div>
    )}
  </div>
);

const Field = ({ n, label, hint, children }) => (
  <div className="asher-card" style={{ padding: 18 }}>
    <div style={{ display: 'flex', gap: 10, marginBottom: hint ? 4 : 12 }}>
      <span style={{ width: 22, height: 22, borderRadius: '50%', background: 'var(--asher-cream)', color: 'var(--asher-gold-dark)', display: 'grid', placeItems: 'center', fontSize: 11, fontWeight: 700, flexShrink: 0 }}>{n}</span>
      <h3 style={{ fontSize: 15, margin: '1px 0 0', lineHeight: 1.3 }}>{label}</h3>
    </div>
    {hint && <p style={{ fontSize: 12, color: 'var(--asher-grey-light)', margin: '0 0 12px 32px', lineHeight: 1.5 }}>{hint}</p>}
    <div style={{ marginLeft: 0 }}>{children}</div>
  </div>
);

const PickCard = ({ on, onClick, icon, title, sub, compact }) => (
  <button onClick={onClick} style={{
    textAlign: 'left', fontFamily: 'inherit', cursor: 'pointer', padding: compact ? 13 : 15, display: 'flex', gap: 11, alignItems: 'flex-start',
    border: on ? '2px solid var(--asher-gold)' : '1px solid #e3e0d8', borderRadius: 11, background: on ? 'linear-gradient(160deg,#fffdf6,#fbf4e2)' : '#fff',
  }}>
    <span style={{ width: 34, height: 34, borderRadius: 9, background: on ? 'var(--asher-gold)' : 'var(--asher-cream)', display: 'grid', placeItems: 'center', color: on ? '#fff' : 'var(--asher-gold-dark)', flexShrink: 0 }}><Icon name={icon} size={16} /></span>
    <div style={{ flex: 1 }}>
      <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--asher-grey-dark)', lineHeight: 1.3 }}>{title}</div>
      {sub && <div style={{ fontSize: 11.5, color: 'var(--asher-grey-light)', marginTop: 3, lineHeight: 1.4 }}>{sub}</div>}
    </div>
    <Icon name={on ? 'circleCheck' : 'circle'} size={16} color={on ? 'var(--asher-gold)' : '#cfcabd'} style={{ flexShrink: 0 }} />
  </button>
);

const GRAN_LABELS = { service_line: 'Service line', department: 'Department', division: 'Division / section', institute: 'Institute / center' };

// Canonical granularity definitions (Asher Knowledge Graph framing), shown on hover.
const GRAN_DEFS = {
  service_line: 'A clinically led grouping of related services that can span facilities (for example, all imaging across the hospital). Led by a service line leader.',
  department: 'An operational unit within a facility, with its own budget and a chair or director (for example, the Radiology department at one hospital).',
  division: 'A sub-unit inside a department, usually a sub-specialty led by a section chief (for example, Neuroradiology).',
  institute: 'A named, often multi-disciplinary center that may operate semi-autonomously (for example, a Cancer Institute).',
};

const GranOption = ({ id, label, sel, onClick }) => {
  const [hov, setHov] = React.useState(false);
  return (
    <span style={{ position: 'relative', display: 'inline-flex' }} onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)}>
      <button onClick={onClick} aria-label={`${label}: ${GRAN_DEFS[id]}`} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600, cursor: 'pointer', padding: '6px 12px', borderRadius: 999, border: sel ? '1px solid var(--asher-gold)' : '1px solid #e0dccf', background: sel ? 'var(--asher-cream)' : '#fff', color: sel ? 'var(--asher-gold-dark)' : 'var(--asher-grey-medium)' }}>{label}<Icon name="info" size={10} style={{ opacity: 0.55 }} /></button>
      {hov && (
        <span role="tooltip" style={{ position: 'absolute', bottom: 'calc(100% + 8px)', left: 0, width: 232, background: 'var(--asher-grey-dark)', color: '#fff', fontSize: 11, lineHeight: 1.5, padding: '9px 11px', borderRadius: 9, zIndex: 30, boxShadow: '0 8px 22px rgba(0,0,0,0.22)', fontWeight: 400 }}>
          {GRAN_DEFS[id]}
          <span style={{ position: 'absolute', top: '100%', left: 18, border: '5px solid transparent', borderTopColor: 'var(--asher-grey-dark)' }} />
        </span>
      )}
    </span>
  );
};

const CheckList = ({ title, items, selected, onToggle, onAll, footer, allowOther }) => {
  const [otherOn, setOtherOn] = React.useState(false);
  const [otherText, setOtherText] = React.useState('');
  const rowStyle = (on) => ({ display: 'flex', alignItems: 'center', gap: 11, textAlign: 'left', fontFamily: 'inherit', cursor: 'pointer', padding: '9px 11px', borderRadius: 8, border: 'none', background: on ? 'var(--asher-cream)' : 'transparent', width: '100%' });
  const box = (on) => (
    <span style={{ width: 17, height: 17, borderRadius: 5, border: on ? 'none' : '1.5px solid #cfcabd', background: on ? 'var(--asher-gold)' : '#fff', display: 'grid', placeItems: 'center', flexShrink: 0 }}>{on && <Icon name="check" size={11} color="#fff" />}</span>
  );
  return (
    <div className="fade-up" style={{ marginTop: 8, border: '1px solid #e9e5da', borderRadius: 10, overflow: 'hidden' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '9px 12px', background: 'var(--asher-grey-bg)', borderBottom: '1px solid #eef0eb' }}>
        <span style={{ fontSize: 11.5, fontWeight: 600, color: 'var(--asher-grey-medium)' }}>{title}</span>
        <button onClick={onAll} style={{ marginLeft: 'auto', fontFamily: 'inherit', fontSize: 11, fontWeight: 600, cursor: 'pointer', border: 'none', background: 'transparent', color: 'var(--asher-gold-dark)' }}>
          {selected.length === items.length ? 'Clear all' : 'Select all'}
        </button>
      </div>
      <div style={{ padding: 7, display: 'flex', flexDirection: 'column', gap: 3 }}>
        {items.map((u, i) => {
          const on = selected.includes(i);
          return (
            <button key={i} onClick={() => onToggle(i)} style={rowStyle(on)}>
              {box(on)}
              <span style={{ fontSize: 12.5, color: 'var(--asher-grey-dark)', fontWeight: on ? 600 : 400 }}>{u}</span>
            </button>
          );
        })}
        {allowOther && (
          <>
            <button onClick={() => setOtherOn((o) => !o)} style={rowStyle(otherOn)}>
              {box(otherOn)}
              <span style={{ fontSize: 12.5, color: 'var(--asher-grey-dark)', fontWeight: otherOn ? 600 : 400, fontStyle: 'italic' }}>Other (tell us)</span>
            </button>
            {otherOn && (
              <input value={otherText} onChange={(e) => setOtherText(e.target.value)} placeholder="Tell us what we're missing, and we'll add it" style={{ margin: '2px 9px 6px', width: 'calc(100% - 18px)', fontSize: 12, padding: '8px 10px', borderRadius: 7, border: '1px solid #d9d4c6', outline: 'none' }} />
            )}
          </>
        )}
      </div>
      {footer && (
        <div style={{ padding: '10px 12px', borderTop: '1px solid #eef0eb', fontSize: 11, color: 'var(--asher-grey-light)', lineHeight: 1.55, display: 'flex', gap: 7 }}>
          <Icon name="info" size={12} color="var(--asher-gold-dark)" style={{ flexShrink: 0, marginTop: 1 }} />
          <span>{footer}</span>
        </div>
      )}
    </div>
  );
};

const SeatBadge = ({ seat, highlight }) => {
  const [hov, setHov] = React.useState(false);
  const info = window.DATA.SEAT_LABEL[seat];
  return (
    <span style={{ position: 'relative', display: 'inline-flex' }} onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)}>
      <span className={`chip ${highlight && seat === 'builder' ? 'chip-gold' : ''}`} style={{ fontSize: 10, cursor: 'help', gap: 3 }} tabIndex={0} aria-label={`${info.label}: ${info.def}`}>
        {info.label}<Icon name="info" size={9} style={{ opacity: 0.6 }} />
      </span>
      {hov && (
        <span role="tooltip" style={{ position: 'absolute', bottom: 'calc(100% + 9px)', right: 0, width: 210, background: 'var(--asher-grey-dark)', color: '#fff', fontSize: 11, lineHeight: 1.5, padding: '9px 11px', borderRadius: 9, zIndex: 30, boxShadow: '0 8px 22px rgba(0,0,0,0.22)', fontWeight: 400 }}>
          {info.def}
          <span style={{ position: 'absolute', top: '100%', right: 16, border: '5px solid transparent', borderTopColor: 'var(--asher-grey-dark)' }} />
        </span>
      )}
    </span>
  );
};

// ── Governance context · AI mix + a department-scale committee suggestion ────
// The lift-and-shift of coreapp Step-1 Q3 (AI sourcing) + the committee
// recommender, deliberately SHRUNK: no enterprise archetypes (hub-and-spoke /
// federated). The scope already comes from the About-us capture, so this suggests
// ONE working committee sized to a department or service line, and tunes its seats
// to how the org sources its AI.
const AI_MIX_OPTIONS = [
  { id: 'vended', icon: 'building', title: 'Mostly vendor', sub: 'You buy AI from suppliers and turn it on.' },
  { id: 'mixed', icon: 'layers', title: 'A mix', sub: 'Some bought, some built or tuned by your team.' },
  { id: 'inhouse', icon: 'brain', title: 'Mostly in-house', sub: 'Your team develops or fine-tunes models.' },
];

const COMMITTEE_BASE = [
  { role: 'Medical / service-line director', tag: 'Chair', why: 'Owns clinical accountability and signs off on the program.' },
  { role: 'Clinical champion', why: 'A practicing physician who speaks for the people using the AI day to day.' },
  { role: 'Informatics / IT partner', why: 'Owns integration, access, and the technical side of deployment.' },
  { role: 'Quality & safety', why: 'Connects AI oversight to your existing quality and safety governance.' },
  { role: 'Privacy / compliance', why: 'Keeps regulatory, security, and privacy obligations covered.' },
];

function deriveCommittee(govName, aiMix) {
  const seats = COMMITTEE_BASE.slice();
  if (aiMix === 'vended' || aiMix === 'mixed') seats.push({ role: 'Procurement / vendor lead', why: 'Runs vendor due-diligence and holds AI suppliers to contract SLAs.' });
  if (aiMix === 'inhouse' || aiMix === 'mixed') seats.push({ role: 'Data science / model validation lead', why: 'Owns validation and lifecycle for models you build or tune.' });
  return { name: (govName ? govName : 'Your') + ' AI Governance Committee', seats, aiMix };
}
window.deriveCommittee = deriveCommittee;

const MIX_NOTE = {
  vended: 'Because your AI is mostly vendor-sourced, vendor due-diligence and contract SLAs carry the most weight — so a procurement lead earns a seat.',
  mixed: "A mix means you cover both sides: vendor oversight for what you buy, and model validation for what you build. We've added a seat for each.",
  inhouse: 'Since you build or tune models yourself, validation and lifecycle ownership sit inside your committee — so a data-science lead earns a seat.',
};

const GovernanceCard = ({ govName, aiMix, setAiMix, divNames }) => {
  const committee = aiMix ? deriveCommittee(govName, aiMix) : null;
  return (
    <div className="asher-card" style={{ padding: 18 }}>
      <div className="eyebrow" style={{ color: 'var(--asher-gold-dark)', marginBottom: 6 }}>Governance context</div>
      <h3 style={{ fontSize: 15, margin: '0 0 4px' }}>How will you oversee AI in {govName}?</h3>
      <p style={{ fontSize: 12, color: 'var(--asher-grey-light)', margin: '0 0 14px', lineHeight: 1.5 }}>Where your AI comes from shapes who needs a seat. We size this to {govName} — one working committee, not an enterprise hierarchy.</p>

      <div className="eyebrow" style={{ fontSize: 9.5, marginBottom: 8 }}>What's the mix of AI you run?</div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10 }}>
        {AI_MIX_OPTIONS.map((o) => (
          <PickCard key={o.id} on={aiMix === o.id} onClick={() => setAiMix(o.id)} icon={o.icon} title={o.title} sub={o.sub} compact />
        ))}
      </div>

      {committee ? (
        <div className="fade-up" style={{ marginTop: 16, border: '1px solid #e9e5da', borderRadius: 11, overflow: 'hidden' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '11px 14px', background: 'var(--asher-grey-bg)', borderBottom: '1px solid #eef0eb' }}>
            <Icon name="users" size={14} color="var(--asher-gold-dark)" />
            <span style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--asher-grey-dark)' }}>{committee.name}</span>
            <span className="chip chip-gold" style={{ fontSize: 9, marginLeft: 'auto' }}>Suggested</span>
          </div>
          <div style={{ padding: 8, display: 'flex', flexDirection: 'column', gap: 2 }}>
            {committee.seats.map((s) => (
              <div key={s.role} style={{ display: 'flex', gap: 10, alignItems: 'flex-start', padding: '8px 9px', borderRadius: 8 }}>
                <Icon name="user" size={14} color="var(--asher-gold-dark)" style={{ flexShrink: 0, marginTop: 2 }} />
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>{s.role}{s.tag && <span className="chip" style={{ fontSize: 9, marginLeft: 7 }}>{s.tag}</span>}</div>
                  <div style={{ fontSize: 11, color: 'var(--asher-grey-light)', marginTop: 1, lineHeight: 1.4 }}>{s.why}</div>
                </div>
              </div>
            ))}
          </div>
          <div style={{ padding: '10px 14px', borderTop: '1px solid #eef0eb', fontSize: 11, color: 'var(--asher-grey-medium)', lineHeight: 1.55, display: 'flex', gap: 7 }}>
            <Icon name="info" size={12} color="var(--asher-gold-dark)" style={{ flexShrink: 0, marginTop: 1 }} />
            <span>{MIX_NOTE[aiMix]}{divNames && divNames.length ? ` With sections like ${divNames.slice(0, 2).join(' and ')}, name a delegate from each so oversight reaches the bench.` : ''}</span>
          </div>
        </div>
      ) : (
        <div style={{ marginTop: 14, padding: 14, borderRadius: 10, border: '1px dashed #d4cfbf', background: '#fbfaf6', fontSize: 12, color: 'var(--asher-grey-light)', lineHeight: 1.5, display: 'flex', gap: 8 }}>
          <Icon name="info" size={13} color="var(--asher-gold-dark)" style={{ flexShrink: 0, marginTop: 1 }} />
          <span>Pick your AI mix above and we'll suggest a committee and the seats it should have. You can refine it any time.</span>
        </div>
      )}
    </div>
  );
};

window.Onboarding = Onboarding;


/* ===== char-graph.jsx (CharNetworkGraph) ===== */
/* global React */
/* ============================================================================
 * CharNetworkGraph — interactive policy → process → SOP network graph for CHAR.
 *
 * Plain-JSX port of packages/char-network-graph/src/CharNetworkGraph.tsx, so the
 * in-browser Babel loader (JSX only, no TypeScript) can run it. All TS type
 * annotations, interfaces and generics are stripped; behaviour is unchanged.
 * Exposes window.CharNetworkGraph (component) and window.CHAR_NETWORK (data).
 *
 * If you edit the source-of-truth .tsx, re-port the changes here by hand.
 * ========================================================================== */
const { useState, useMemo, useRef, useLayoutEffect } = React;

const GOLD = '#e4aa23';
const GOLD_600 = '#c99420';

/* ------------------------------------------------------------------ data -- */
const CHAR_NETWORK = {
  clusters: {
    c1: { n: '01', name: 'Strategy & Leadership',          short: 'Strategy & Leadership',     color: '#274d52' },
    c2: { n: '02', name: 'Risk, Ethics & Compliance',      short: 'Risk, Ethics & Compliance', color: '#796880' },
    c3: { n: '03', name: 'Data, Model & System Lifecycle', short: 'Data & System Lifecycle',   color: '#5b7790' },
    c4: { n: '04', name: 'Operations & Workflow',          short: 'Operations & Workflow',     color: '#6f7a5e' },
    c5: { n: '05', name: 'Vendor, Partners & Ecosystem',   short: 'Vendor & Ecosystem',        color: '#604e3c' },
  },
  clusterOrder: ['c1', 'c2', 'c3', 'c4', 'c5'],
  policies: [
    { code: 'SAP', name: 'Scope & Applicability',  cluster: 'c1' },
    { code: 'AGP', name: 'AI Governance',          cluster: 'c1' },
    { code: 'SEP', name: 'Stakeholder Engagement', cluster: 'c1' },
    { code: 'RMP', name: 'Risk Management',         cluster: 'c2' },
    { code: 'EFP', name: 'AI Ethics & Fairness',    cluster: 'c2' },
    { code: 'HOP', name: 'Human Oversight',         cluster: 'c2' },
    { code: 'DGP', name: 'Data Governance',         cluster: 'c3' },
    { code: 'LTP', name: 'Lifecycle Traceability',  cluster: 'c3' },
    { code: 'VAL', name: 'Validation',              cluster: 'c3' },
    { code: 'MMP', name: 'Model Monitoring',        cluster: 'c4' },
    { code: 'CMP', name: 'Change Management',        cluster: 'c4' },
    { code: 'TPP', name: 'Third-Party AI Mgmt',     cluster: 'c5' },
  ],
  processes: [
    { code: 'OVR', name: 'Oversight & Steering',          cluster: 'c1', sops: 3 },
    { code: 'ORG', name: 'Organization & Strategy',       cluster: 'c1', sops: 4 },
    { code: 'SE',  name: 'Stakeholder Engagement',        cluster: 'c1', sops: 0 },
    { code: 'RM',  name: 'Risk Management',               cluster: 'c2', sops: 3 },
    { code: 'EF',  name: 'Ethics & Fairness',             cluster: 'c2', sops: 4 },
    { code: 'HO',  name: 'Human Oversight',               cluster: 'c2', sops: 4 },
    { code: 'TR',  name: 'Transparency & Explainability', cluster: 'c3', sops: 3 },
    { code: 'PV',  name: 'Problem & Value',               cluster: 'c3', sops: 4 },
    { code: 'SA',  name: 'Solution Architecture',         cluster: 'c3', sops: 4 },
    { code: 'MON', name: 'Monitoring & Lifecycle',        cluster: 'c4', sops: 3 },
    { code: 'IT',  name: 'IT Systems & Data',             cluster: 'c4', sops: 4 },
    { code: 'UG',  name: 'Usage Governance',              cluster: 'c4', sops: 3 },
  ],
  configures: {
    SAP: ['SA', 'OVR', 'ORG'],
    AGP: ['OVR', 'ORG', 'RM', 'MON', 'HO', 'EF', 'TR', 'UG'],
    RMP: ['RM', 'SA', 'MON', 'PV'],
    HOP: ['HO', 'SA', 'MON', 'UG'],
    EFP: ['EF', 'RM', 'SA', 'MON', 'OVR'],
    MMP: ['MON', 'TR', 'UG'],
    LTP: ['TR', 'IT', 'MON'],
    CMP: ['IT', 'MON', 'SA', 'RM'],
    DGP: ['IT', 'SA', 'MON', 'PV'],
    SEP: ['SE', 'OVR', 'TR', 'HO'],
    VAL: ['SA', 'MON', 'RM', 'PV'],
    TPP: ['PV', 'RM', 'SA', 'MON', 'IT'],
  },
  feeds: {
    ORG: ['OVR', 'PV', 'IT'],
    OVR: ['PV', 'SA', 'MON', 'RM'],
    PV:  ['SA', 'RM'],
    SA:  ['MON', 'TR'],
    IT:  ['SA', 'MON', 'TR'],
    RM:  ['SA', 'MON'],
    MON: ['TR', 'UG'],
    HO:  ['MON', 'UG'],
    EF:  ['MON', 'RM', 'OVR'],
    TR:  ['SE', 'OVR'],
    UG:  ['OVR'],
    SE:  ['OVR'],
  },
};

/* --------------------------------------------------------------- helpers -- */
function polar(cx, cy, r, deg) {
  const a = (deg * Math.PI) / 180;
  return [cx + r * Math.cos(a), cy + r * Math.sin(a)];
}
function qpath(p1, p2, cx, cy, t) {
  const mx = (p1[0] + p2[0]) / 2, my = (p1[1] + p2[1]) / 2;
  const cpx = mx + (cx - mx) * t, cpy = my + (cy - my) * t;
  return `M${p1[0].toFixed(1)},${p1[1].toFixed(1)} Q${cpx.toFixed(1)},${cpy.toFixed(1)} ${p2[0].toFixed(1)},${p2[1].toFixed(1)}`;
}
function arcPath(cx, cy, r, a0, a1) {
  const [x0, y0] = polar(cx, cy, r, a0);
  const [x1, y1] = polar(cx, cy, r, a1);
  const large = a1 - a0 > 180 ? 1 : 0;
  return `M${x0.toFixed(1)},${y0.toFixed(1)} A${r},${r} 0 ${large} 1 ${x1.toFixed(1)},${y1.toFixed(1)}`;
}
// deterministic 2–4 "controls" per SOP so the bloom is organic but stable
function ctrlCount(key) {
  let h = 0; for (let i = 0; i < key.length; i++) h = (h * 31 + key.charCodeAt(i)) & 255;
  return 2 + (h % 3);
}

/* --------------------------------------------------------- brand bar mark - */
function BarMark({ size = 44, gold = GOLD, dark = '#333f49' }) {
  const bars = [
    { x: 4, y: 5, h: 46, c: dark }, { x: 22, y: 16, h: 44, c: gold },
    { x: 40, y: 9, h: 50, c: gold }, { x: 58, y: 5, h: 42, c: dark },
    { x: 76, y: 19, h: 36, c: gold },
  ];
  return (
    <svg width={size} height={size * 0.66} viewBox="0 0 92 64" aria-hidden="true">
      <g transform="skewX(-11)">
        {bars.map((b, i) => <rect key={i} x={b.x} y={b.y} width={12} height={b.h} rx={1.2} fill={b.c} />)}
      </g>
    </svg>
  );
}

/* ------------------------------------------------------------- component -- */
function CharNetworkGraph({
  data = CHAR_NETWORK,
  header = true,
  eyebrow = 'Clinical Health AI · Connected Framework',
  title = 'CHAR as a Connected Graph',
  subtitle = 'Every policy domain binds the process domains that carry it out, and processes hand off to one another. The whole policy → process → SOP fabric, in one view.',
  brand = 'ASHER',
  brandSub = 'INFORMATICS',
  coreLabel = 'CHAR',
  coreSub = 'Operating\nModel',
  logoSrc = '',
  markSrc = '',
  controlsNote = 'sit under each SOP — crosswalked to ISO 42001, NIST AI RMF & CHAI, then configured to your needs as the management system is built.',
  controlsRevealHint = 'Hover a process or SOP to reveal them.',
  clustersTitle = 'Governance clusters',
  clustersAka = 'Capability Domains',
  legends = true,
  maxWidth = 1600,
  className = '',
  style = {},
}) {
  injectStyles();
  const [hover, setHover] = useState(null);
  const [pin, setPin] = useState(null);
  const [hoverSop, setHoverSop] = useState(null);
  const active = pin || hover;

  const cx = 800, cy = 486;
  const rPol = 150, rProc = 270, rSop = 332;
  const rTickIn = 345, rTickOut = 357, rArc = 351;

  const model = useMemo(() => {
    const order = data.clusterOrder || Object.keys(data.clusters);
    const W = {}; let totW = 0;
    order.forEach((c) => {
      const np = data.policies.filter((p) => p.cluster === c).length;
      const nq = data.processes.filter((p) => p.cluster === c).length;
      W[c] = Math.max(1, np + nq); totW += W[c];
    });
    const gap = 6, span = 360 - order.length * gap;
    let cur = -90; const sect = {};
    order.forEach((c) => { const arc = (W[c] / totW) * span; sect[c] = { a0: cur, a1: cur + arc }; cur += arc + gap; });

    const spread = (items, c, r, pad, tier) => {
      const { a0, a1 } = sect[c]; const n = items.length;
      return items.map((it, i) => {
        const ang = n === 1 ? (a0 + a1) / 2 : a0 + pad + (i * (a1 - a0 - 2 * pad)) / (n - 1);
        const [x, y] = polar(cx, cy, r, ang);
        return { ...it, sops: it.sops, tier, ang, x, y };
      });
    };

    const pos = {};
    order.forEach((c) => {
      spread(data.policies.filter((p) => p.cluster === c), c, rPol, 7, 'policy').forEach((p) => { pos[p.code] = p; });
      spread(data.processes.filter((p) => p.cluster === c), c, rProc, 8, 'process').forEach((p) => { pos[p.code] = p; });
    });

    const sops = [];
    Object.values(pos).forEach((p) => {
      if (p.tier !== 'process' || !p.sops) return;
      const step = 3.0;
      for (let i = 0; i < p.sops; i++) {
        const ang = p.ang + (i - (p.sops - 1) / 2) * step;
        const [x, y] = polar(cx, cy, rSop, ang);
        const key = p.code + i;
        const nc = ctrlCount(key);
        const ticks = [];
        for (let j = 0; j < nc; j++) {
          const ta = ang + (j - (nc - 1) / 2) * 0.82;
          const [ix, iy] = polar(cx, cy, rTickIn, ta);
          const [ox, oy] = polar(cx, cy, rTickOut, ta);
          ticks.push({ ix, iy, ox, oy });
        }
        sops.push({ parent: p.code, cluster: p.cluster, x, y, key, ang, nc, ticks });
      }
    });

    const edges = [];
    Object.entries(data.configures || {}).forEach(([pol, procs]) => {
      procs.forEach((pr) => {
        const a = pos[pol], b = pos[pr]; if (!a || !b) return;
        const cross = a.cluster !== b.cluster;
        edges.push({ id: 'c' + pol + pr, a: pol, b: pr, kind: 'cfg', cross,
          d: qpath([a.x, a.y], [b.x, b.y], cx, cy, 0.16),
          color: cross ? GOLD : data.clusters[a.cluster].color });
      });
    });
    Object.entries(data.feeds || {}).forEach(([from, tos]) => {
      tos.forEach((to) => {
        const a = pos[from], b = pos[to]; if (!a || !b) return;
        edges.push({ id: 'f' + from + to, a: from, b: to, kind: 'feed',
          d: qpath([a.x, a.y], [b.x, b.y], cx, cy, 0.42), color: '#3c4954' });
      });
    });

    const adj = {};
    edges.forEach((e) => {
      (adj[e.a] = adj[e.a] || new Set()).add(e.b);
      (adj[e.b] = adj[e.b] || new Set()).add(e.a);
    });

    const nSops = data.processes.reduce((s, p) => s + (p.sops || 0), 0);
    const totals = [
      { v: data.policies.length, l: 'Policy domains', gold: true },
      { v: data.processes.length, l: 'Process domains', gold: false },
      { v: nSops, l: 'SOPs', gold: false },
      { v: edges.length, l: 'Domain links', gold: false },
    ];
    return { pos, sops, edges, adj, totals };
  }, [data]);

  const { pos, sops, edges, adj, totals } = model;
  const nbr = active ? adj[active] || new Set() : null;
  const isLit = (id) => !active || id === active || (!!nbr && nbr.has(id));
  const edgeLit = (e) => !active || e.a === active || e.b === active;
  const tip = active ? pos[active] : null;
  const clusters = data.clusters;

  // control-tick reveal: a hovered SOP (just it), else all SOPs of the active process
  const sopByKey = useMemo(() => Object.fromEntries(sops.map((s) => [s.key, s])), [sops]);
  const ctrlKeys = useMemo(() => {
    if (hoverSop) return new Set([hoverSop]);
    if (active && pos[active] && pos[active].tier === 'process') {
      return new Set(sops.filter((s) => s.parent === active).map((s) => s.key));
    }
    return new Set();
  }, [hoverSop, active, sops, pos]);
  const ctrlArcs = useMemo(() => {
    const byProc = {};
    ctrlKeys.forEach((k) => { const s = sopByKey[k]; (byProc[s.parent] = byProc[s.parent] || []).push(s.ang); });
    return Object.entries(byProc).map(([proc, angs]) => {
      const a0 = Math.min(...angs) - 1.6, a1 = Math.max(...angs) + 1.6;
      return { proc, d: arcPath(cx, cy, rArc, a0, a1) };
    });
  }, [ctrlKeys, sopByKey]);
  const ctrlTotal = [...ctrlKeys].reduce((a, k) => a + sopByKey[k].nc, 0);

  /* fit-to-parent scaling */
  const outer = useRef(null);
  const [scale, setScale] = useState(1);
  useLayoutEffect(() => {
    const el = outer.current; if (!el) return;
    const apply = () => {
      const w = el.clientWidth || 1600;
      setScale(Math.min(w / 1600, maxWidth / 1600));
    };
    apply();
    const ro = new ResizeObserver(apply); ro.observe(el);
    return () => ro.disconnect();
  }, [maxWidth]);

  return (
    <div ref={outer} className={'cng-root ' + className} style={style}>
      <div className="cng-stage" style={{ height: 900 * scale }}>
      <div className="cng-frame" style={{ transform: `scale(${scale})` }}>
        {header && (
          <>
            <div className="cng-head" style={{ left: 70, top: 50, width: 760 }}>
              <div className="cng-eyebrow">{eyebrow}</div>
              <div className="cng-title">{title}</div>
              {subtitle && <div className="cng-sub">{subtitle}</div>}
            </div>
            <div className="cng-lockup" style={{ right: 70, top: 54 }}>
              {logoSrc
                ? <img src={logoSrc} alt={brand + ' ' + brandSub} style={{ height: 40, width: 'auto', display: 'block' }} />
                : <><BarMark size={34} /><div className="cng-wm"><b>{brand}</b><span>{brandSub}</span></div></>}
            </div>
          </>
        )}

        <svg className="cng-svg" width="1600" height="900">
          <defs>
            <marker id="cngAh" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
              <path d="M0,1 L9,5 L0,9 Z" fill="#3c4954" />
            </marker>
            <marker id="cngAhLit" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
              <path d="M0,1 L9,5 L0,9 Z" fill="#2a343c" />
            </marker>
          </defs>

          {!active && data.policies.map((p) => {
            const n = pos[p.code];
            return <line key={'sp' + p.code} x1={cx} y1={cy} x2={n.x} y2={n.y} stroke={GOLD} strokeWidth="1" opacity="0.10" />;
          })}

          {edges.filter((e) => e.kind === 'cfg').map((e) => {
            const lit = edgeLit(e);
            return <path key={e.id} d={e.d} fill="none" stroke={e.color}
              strokeWidth={lit && active ? 2 : e.cross ? 1.1 : 1}
              opacity={!active ? (e.cross ? 0.34 : 0.22) : lit ? 0.95 : 0.05}
              strokeLinecap="round" />;
          })}

          {edges.filter((e) => e.kind === 'feed').map((e) => {
            const lit = edgeLit(e);
            return <path key={e.id} d={e.d} fill="none" stroke={e.color}
              strokeWidth={lit && active ? 2 : 1.2}
              opacity={!active ? 0.24 : lit ? 0.92 : 0.04}
              strokeDasharray="1 5" strokeLinecap="round"
              markerEnd={`url(#${lit && active ? 'cngAhLit' : 'cngAh'})`} />;
          })}

          {sops.map((s) => {
            const p = pos[s.parent]; const lit = isLit(s.parent);
            return <line key={'st' + s.key} x1={p.x} y1={p.y} x2={s.x} y2={s.y} stroke={GOLD}
              strokeWidth="1" opacity={!active ? 0.2 : lit ? 0.55 : 0.03} />;
          })}
          {ctrlArcs.map((a) => (
            <path key={'ca' + a.proc} className="cng-ctrl-arc" d={a.d} fill="none"
              stroke={GOLD} strokeWidth="1" opacity="0.4" strokeLinecap="round" />
          ))}
          {sops.map((s) => ctrlKeys.has(s.key) && (
            <g key={'ct' + s.key} className="cng-ctrl-g">
              {s.ticks.map((t, j) => (
                <line key={j} x1={t.ix} y1={t.iy} x2={t.ox} y2={t.oy}
                  stroke={GOLD} strokeWidth="1.4" strokeLinecap="round"
                  style={{ animationDelay: (j * 35) + 'ms' }} />
              ))}
            </g>
          ))}
          {sops.map((s) => {
            const lit = isLit(s.parent);
            const on = hoverSop === s.key || ctrlKeys.has(s.key);
            return <circle key={s.key} cx={s.x} cy={s.y} r={on ? 4.2 : 3.4} fill={GOLD} stroke="#fff" strokeWidth="1"
              opacity={!active ? 0.95 : lit ? 1 : 0.1} style={{ transition: 'r .15s ease' }} />;
          })}
        </svg>

        <div className="cng-core" style={{ left: cx - 54, top: cy - 54 }}>
          <div className="cng-core-ring" />
          {markSrc ? <img src={markSrc} alt="" style={{ width: 43, height: 'auto', display: 'block' }} /> : <BarMark size={43} />}
          <div className="cng-core-label">{coreLabel}</div>
          <div className="cng-core-sub">{coreSub.split('\n').map((t, i) => <div key={i}>{t}</div>)}</div>
        </div>

        <div className="cng-nodes">
          {data.policies.map((p) => {
            const n = pos[p.code]; const col = clusters[p.cluster].color; const lit = isLit(p.code);
            return (
              <div key={p.code} className={'cng-node pol' + (active === p.code ? ' on' : '')}
                style={{ left: n.x, top: n.y, opacity: lit ? 1 : 0.2 }}
                onMouseEnter={() => { setHover(p.code); setHoverSop(null); }} onMouseLeave={() => setHover(null)}
                onClick={() => setPin(pin === p.code ? null : p.code)}>
                <span className="dia" style={{ borderColor: col }} />
                <span className="code" style={{ color: col }}>{p.code}</span>
              </div>
            );
          })}
          {data.processes.map((p) => {
            const n = pos[p.code]; const col = clusters[p.cluster].color; const lit = isLit(p.code);
            return (
              <div key={p.code} className={'cng-node proc' + (active === p.code ? ' on' : '')}
                style={{ left: n.x, top: n.y, opacity: lit ? 1 : 0.2 }}
                onMouseEnter={() => { setHover(p.code); setHoverSop(null); }} onMouseLeave={() => setHover(null)}
                onClick={() => setPin(pin === p.code ? null : p.code)}>
                <span className="pill" style={{ borderColor: col }}>
                  <span className="pcode" style={{ color: col }}>{p.code}</span>
                  {p.sops > 0 && <span className="psop"><span className="d" />{p.sops}</span>}
                </span>
              </div>
            );
          })}
        </div>

        {/* SOP hit-targets + control tooltip */}
        <div className="cng-sophits">
          {sops.map((s) => {
            const lit = isLit(s.parent);
            if (active && !lit) return null;
            return <div key={'hit' + s.key} className="cng-sophit" style={{ left: s.x, top: s.y }}
              onMouseEnter={() => { setHoverSop(s.key); setHover(null); }}
              onMouseLeave={() => setHoverSop(null)} />;
          })}
        </div>
        {hoverSop && sopByKey[hoverSop] && (() => {
          const s = sopByKey[hoverSop]; const below = s.y < cy;
          return (
            <div className="cng-ctip" style={{
              left: Math.min(Math.max(s.x, 120), 1480),
              top: below ? s.y + 16 : s.y - 16,
              transform: below ? 'translate(-50%,0)' : 'translate(-50%,-100%)',
            }}>
              <div className="ct-head"><span className="ct-dot" />{pos[s.parent].name} · SOP</div>
              <div className="ct-body"><b>{s.nc} controls</b> mapped here</div>
              <div className="ct-foot">Configured to your org at build time</div>
            </div>
          );
        })()}

        {tip && (() => {
          const below = tip.y < cy; const off = tip.tier === 'process' ? 22 : 16;
          return (
            <div className="cng-tip" style={{
              left: Math.min(Math.max(tip.x, 150), 1450),
              top: below ? tip.y + off : tip.y - off,
              transform: below ? 'translate(-50%,0)' : 'translate(-50%,-100%)',
              borderColor: clusters[tip.cluster].color,
              borderTopWidth: below ? 1 : 3, borderBottomWidth: below ? 3 : 1,
            }}>
              <div className="t-head">
                <span className="t-code" style={{ color: clusters[tip.cluster].color }}>{tip.code}</span>
                <span className="t-kind">{tip.tier === 'policy' ? 'Policy domain' : 'Process domain'}</span>
              </div>
              <div className="t-name">{tip.name}</div>
              <div className="t-meta">{tip.tier === 'policy'
                ? `Configures ${(data.configures[tip.code] || []).length} process domains`
                : `${tip.sops} SOPs · ${nbr ? nbr.size : 0} linked domains`}</div>
            </div>
          );
        })()}

        {legends && (
          <>
            <div className="cng-key" style={{ left: 64, top: 256 }}>
              <div className="cng-ktitle">How to read</div>
              <div className="cng-krow"><span className="cng-kgl"><span className="kg-dia" /></span><span><b>Policy domain</b> &nbsp;governs intent</span></div>
              <div className="cng-krow"><span className="cng-kgl"><span className="kg-pill" /></span><span><b>Process domain</b> &nbsp;does the work</span></div>
              <div className="cng-krow"><span className="cng-kgl"><span className="kg-dot" /></span><span><b>SOP</b> &nbsp;standard procedure</span></div>
              {controlsNote && <div className="cng-kctrl"><span className="cng-kgl"><span className="kg-ticks"><i /><i /><i /></span></span><span><b>Controls</b> {controlsNote}{controlsRevealHint && <em> {controlsRevealHint}</em>}</span></div>}
              <div className="cng-kdiv" />
              <div className="cng-krow"><span className="cng-kgl"><svg width="30" height="12"><path d="M1,10 Q15,-2 29,10" stroke={GOLD} strokeWidth="2" fill="none" /></svg></span><span><b style={{ color: '#b78310' }}>Configures</b> &nbsp;policy &rarr; process</span></div>
              <div className="cng-krow"><span className="cng-kgl"><svg width="30" height="12"><path d="M1,9 Q15,1 27,9" stroke="#3c4954" strokeWidth="2" strokeDasharray="1 4" fill="none" markerEnd="url(#cngAh)" /></svg></span><span><b>Feeds into</b> &nbsp;process &rarr; process</span></div>
              <div className="cng-khint">Gold links cross cluster lines &mdash; that reach is the connectedness CHAR designs in.</div>
            </div>

            <div className="cng-clusters" style={{ right: 64, top: 256 }}>
              <div className="cng-ktitle">{clustersTitle}{clustersAka && <span className="cng-aka"> ({clustersAka})</span>}</div>
              {(data.clusterOrder || Object.keys(clusters)).map((k) => {
                const c = clusters[k];
                const np = data.policies.filter((p) => p.cluster === k).length;
                const nq = data.processes.filter((p) => p.cluster === k).length;
                return (
                  <div key={k} className={'cng-clrow' + (active && pos[active].cluster === k ? ' on' : '')}>
                    <span className="cng-clbadge" style={{ background: c.color }}>{c.n}</span>
                    <span className="cng-cltext"><b>{c.short || c.name}</b><span className="sub">{np} policy &middot; {nq} process</span></span>
                  </div>
                );
              })}
            </div>

            <div className="cng-foot" style={{ left: 70, bottom: 30 }}>
              {hoverSop
                ? <span><b style={{ color: '#b78310' }}>{ctrlTotal} controls</b> on this SOP · configured at build time</span>
                : active
                ? <span><b style={{ color: clusters[pos[active].cluster].color }}>{active}</b> highlighted{ctrlTotal ? <span> · <b style={{ color: '#b78310' }}>{ctrlTotal} controls</b> revealed</span> : null} — {pin ? 'click again to release' : 'move away to reset'}</span>
                : <span>Hover any domain to trace its links · hover a process to bloom its controls · click to pin</span>}
            </div>
            <div className="cng-totals" style={{ right: 64, bottom: 24 }}>
              {totals.map((t, i) => (
                <div key={i} className={'t' + (t.gold ? ' gold' : '')}>
                  <div className="v">{t.v}</div><div className="l">{t.l}</div>
                </div>
              ))}
            </div>
          </>
        )}
      </div>
      </div>
    </div>
  );
}

/* ----------------------------------------------------------- styles (once) - */
let cngInjected = false;
function injectStyles() {
  if (cngInjected || typeof document === 'undefined') return;
  cngInjected = true;
  const css = `
.cng-root{--cng-dark:#333f49;--cng-cream:#eee8d9;--cng-sage:#b4bdaf;--cng-brown:#8d7240;
  --cng-fg1:#333f49;--cng-fg2:#5b5446;--cng-fg3:#7f928c;--cng-gold:${GOLD};--cng-gold6:${GOLD_600};
  --cng-mont:'Montserrat',system-ui,sans-serif;--cng-robo:'Roboto',system-ui,sans-serif;
  width:100%;}
.cng-stage{position:relative;width:100%;overflow:hidden;margin:0 auto;}
.cng-frame{position:relative;width:1600px;height:900px;transform-origin:top left;
  background:radial-gradient(125% 100% at 50% 42%,#fff 0%,#fff 52%,#f6f2e8 100%);
  font-family:var(--cng-robo);color:var(--cng-fg1);-webkit-font-smoothing:antialiased;}
.cng-frame *{box-sizing:border-box;}
.cng-head{position:absolute;z-index:6;}
.cng-eyebrow{font-family:var(--cng-mont);font-weight:700;font-size:15px;letter-spacing:3px;
  text-transform:uppercase;color:var(--cng-gold6);}
.cng-title{font-family:var(--cng-mont);font-weight:800;font-size:38px;color:var(--cng-dark);
  letter-spacing:-1px;line-height:1.02;margin:6px 0 0;}
.cng-sub{font-family:var(--cng-robo);font-size:16px;color:var(--cng-fg3);line-height:1.45;
  max-width:600px;margin-top:10px;}
.cng-lockup{position:absolute;z-index:6;display:flex;align-items:center;gap:12px;}
.cng-wm{font-family:var(--cng-mont);line-height:1;}
.cng-wm b{display:block;font-weight:800;font-size:16px;letter-spacing:1px;color:var(--cng-dark);}
.cng-wm span{display:block;font-weight:500;font-size:9px;letter-spacing:3.5px;color:var(--cng-brown);}
.cng-svg{position:absolute;inset:0;pointer-events:none;z-index:2;}
.cng-core{position:absolute;z-index:4;width:108px;height:108px;border-radius:50%;background:#fff;
  border:1.5px solid rgba(180,189,175,.7);box-shadow:0 6px 22px rgba(51,63,73,.10),0 1px 3px rgba(0,0,0,.06);
  display:flex;flex-direction:column;align-items:center;justify-content:center;}
.cng-core-ring{position:absolute;inset:8px;border-radius:50%;border:1.5px solid rgba(228,170,35,.45);}
.cng-core-label{font-family:var(--cng-mont);font-weight:800;font-size:22px;color:var(--cng-dark);
  letter-spacing:1px;line-height:1;margin-top:5px;}
.cng-core-sub{font-family:var(--cng-mont);font-weight:600;font-size:8.4px;text-transform:uppercase;
  letter-spacing:1.5px;color:var(--cng-fg3);text-align:center;line-height:1.25;margin-top:4px;}
.cng-nodes{position:absolute;inset:0;z-index:5;}
.cng-node{position:absolute;transform:translate(-50%,-50%);display:flex;flex-direction:column;
  align-items:center;cursor:pointer;transition:opacity .18s ease;}
.cng-node .dia{width:15px;height:15px;transform:rotate(45deg);background:#fff;border:2px solid;border-radius:2px;
  box-shadow:0 1px 3px rgba(51,63,73,.18);transition:transform .15s ease,box-shadow .15s ease;}
.cng-node.pol .code{font-family:var(--cng-robo);font-weight:700;font-size:11px;letter-spacing:.2px;
  margin-top:4px;line-height:1;text-shadow:0 0 3px #fff,0 0 3px #fff,0 0 3px #fff;}
.cng-node .pill{display:inline-flex;align-items:center;gap:5px;background:#fff;border:1.7px solid;
  border-radius:8px;padding:4px 9px;box-shadow:0 1px 4px rgba(51,63,73,.14);transition:transform .15s ease,box-shadow .15s ease;}
.cng-node .pill .pcode{font-family:var(--cng-robo);font-weight:700;font-size:12.5px;letter-spacing:.2px;}
.cng-node .pill .psop{display:inline-flex;align-items:center;gap:3px;font-family:var(--cng-robo);
  font-weight:600;font-size:10.5px;color:var(--cng-gold6);}
.cng-node .pill .psop .d{width:5px;height:5px;border-radius:50%;background:var(--cng-gold);}
.cng-node.on .dia{transform:rotate(45deg) scale(1.25);box-shadow:0 3px 10px rgba(51,63,73,.28);}
.cng-node.on .pill{transform:scale(1.08);box-shadow:0 4px 14px rgba(228,170,35,.28);}
.cng-node:hover .pill{transform:scale(1.08);}
.cng-node:hover .dia{transform:rotate(45deg) scale(1.2);}
.cng-tip{position:absolute;z-index:8;background:#fff;border:1px solid;border-radius:10px;
  padding:10px 13px 11px;min-width:188px;max-width:250px;box-shadow:0 10px 26px rgba(51,63,73,.18);pointer-events:none;}
.cng-tip .t-head{display:flex;align-items:baseline;gap:8px;}
.cng-tip .t-code{font-family:var(--cng-robo);font-weight:700;font-size:15px;}
.cng-tip .t-kind{font-family:var(--cng-mont);font-weight:600;font-size:9.5px;letter-spacing:1px;
  text-transform:uppercase;color:var(--cng-fg3);}
.cng-tip .t-name{font-family:var(--cng-mont);font-weight:600;font-size:14px;color:var(--cng-dark);margin-top:3px;line-height:1.2;}
.cng-tip .t-meta{font-family:var(--cng-robo);font-size:12px;color:var(--cng-fg2);margin-top:5px;}
.cng-key{position:absolute;z-index:6;width:340px;}
.cng-clusters{position:absolute;z-index:6;width:300px;}
.cng-ktitle{font-family:var(--cng-mont);font-weight:700;font-size:11px;letter-spacing:1.4px;
  text-transform:uppercase;color:var(--cng-fg3);margin-bottom:14px;}
.cng-krow{display:flex;align-items:center;gap:11px;margin-bottom:12px;font-family:var(--cng-robo);
  font-size:13.5px;color:var(--cng-fg2);line-height:1.25;white-space:nowrap;}
.cng-krow b{color:var(--cng-fg1);font-weight:600;}
.cng-kgl{flex:0 0 30px;display:flex;justify-content:center;align-items:center;}
.kg-dia{width:13px;height:13px;transform:rotate(45deg);background:#fff;border:2px solid var(--cng-dark);border-radius:2px;}
.kg-pill{width:24px;height:14px;border-radius:5px;background:#fff;border:1.7px solid var(--cng-dark);}
.kg-dot{width:9px;height:9px;border-radius:50%;background:var(--cng-gold);}
.kg-sq{width:11px;height:11px;border-radius:2px;background:var(--cng-dark);}
.kg-ticks{display:inline-flex;align-items:center;gap:2px;}
.kg-ticks i{width:1.6px;height:11px;border-radius:1px;background:var(--cng-gold);display:block;}
.cng-kdiv{height:1px;background:rgba(180,189,175,.45);margin:13px 0 13px 41px;}
.cng-kctrl{display:flex;align-items:flex-start;gap:11px;margin:14px 0 2px;font-family:var(--cng-robo);
  font-size:12px;color:var(--cng-fg3);line-height:1.45;max-width:332px;}
.cng-kctrl b{color:var(--cng-fg1);font-weight:600;}
.cng-kctrl em{font-style:italic;color:var(--cng-gold6);}
.cng-kctrl .cng-kgl{margin-top:1px;}
@keyframes cngCtrlTickIn{from{opacity:0;transform:scale(.4);}to{opacity:.92;transform:scale(1);}}
@keyframes cngCtrlArcIn{from{opacity:0;}to{opacity:.4;}}
.cng-ctrl-g line{transform-box:fill-box;transform-origin:center;animation:cngCtrlTickIn .26s ease both;}
.cng-ctrl-arc{animation:cngCtrlArcIn .3s ease both;}
.cng-sophits{position:absolute;inset:0;z-index:6;pointer-events:none;}
.cng-sophit{position:absolute;width:18px;height:18px;transform:translate(-50%,-50%);border-radius:50%;cursor:pointer;pointer-events:auto;}
.cng-ctip{position:absolute;z-index:9;background:var(--cng-dark);color:#fff;border-radius:9px;
  padding:8px 12px 9px;min-width:158px;box-shadow:0 10px 24px rgba(51,63,73,.28);pointer-events:none;border-top:3px solid var(--cng-gold);}
.cng-ctip .ct-head{display:flex;align-items:center;gap:7px;font-family:var(--cng-mont);font-weight:600;font-size:12px;color:#fff;}
.cng-ctip .ct-head .ct-dot{width:7px;height:7px;border-radius:50%;background:var(--cng-gold);box-shadow:0 0 0 1.5px rgba(255,255,255,.25);flex:0 0 auto;}
.cng-ctip .ct-body{font-family:var(--cng-robo);font-size:13px;color:#f4efe2;margin-top:5px;}
.cng-ctip .ct-body b{color:var(--cng-gold);font-weight:700;}
.cng-ctip .ct-foot{font-family:var(--cng-robo);font-size:11px;font-style:italic;color:rgba(255,255,255,.6);margin-top:4px;}
.cng-aka{text-transform:none;letter-spacing:.2px;font-weight:500;color:var(--cng-fg3);}
.cng-khint{font-family:var(--cng-robo);font-size:12px;font-style:italic;color:var(--cng-fg3);
  line-height:1.4;margin-top:14px;padding-left:41px;max-width:300px;}
.cng-clrow{display:flex;align-items:center;gap:11px;margin-bottom:13px;padding:3px 6px 3px 0;
  border-radius:8px;transition:background .15s ease;}
.cng-clrow.on{background:rgba(228,170,35,.10);}
.cng-clbadge{flex:0 0 auto;width:30px;height:30px;border-radius:7px;color:#fff;font-family:var(--cng-mont);
  font-weight:800;font-size:13px;display:flex;align-items:center;justify-content:center;letter-spacing:.5px;}
.cng-cltext{display:flex;flex-direction:column;}
.cng-cltext b{font-family:var(--cng-mont);font-weight:600;font-size:13px;color:var(--cng-fg1);line-height:1.2;white-space:nowrap;}
.cng-cltext .sub{font-family:var(--cng-robo);font-size:11.5px;color:var(--cng-fg3);margin-top:3px;line-height:1;white-space:nowrap;}
.cng-foot{position:absolute;z-index:6;font-family:var(--cng-robo);font-size:13px;color:var(--cng-fg3);}
.cng-foot b{font-weight:700;}
.cng-totals{position:absolute;z-index:6;display:flex;align-items:stretch;}
.cng-totals .t{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0 22px;
  border-right:1px solid rgba(180,189,175,.5);}
.cng-totals .t:last-child{border-right:none;padding-right:0;}
.cng-totals .t .v{font-family:var(--cng-robo);font-weight:700;font-size:34px;color:var(--cng-dark);
  line-height:1;font-feature-settings:"tnum";}
.cng-totals .t.gold .v{color:var(--cng-gold6);}
.cng-totals .t .l{font-family:var(--cng-mont);font-weight:600;font-size:11px;letter-spacing:1px;
  text-transform:uppercase;color:var(--cng-fg3);margin-top:8px;text-align:center;}
`;
  const tag = document.createElement('style');
  tag.id = 'cng-styles';
  tag.textContent = css;
  document.head.appendChild(tag);
}

window.CharNetworkGraph = CharNetworkGraph;
window.CHAR_NETWORK = CHAR_NETWORK;


/* ===== explore.jsx ===== */
/* global React, Icon, TopBar, PolicyBreadcrumb, CharNetworkGraph */
// Explore Asher Forge — the read-only learn surface. Top-level views in the dark
// domain bar:
//   • The big picture — FORGE | CHAR sub-tabs (platform story vs. the reference model)
//   • Framework / Policy / Process — the three-domain reference browser, each domain
//     landing on its layer slides (policy: 12 domains + compliance; process: 11
//     domains + controls + tools)
//   • AI Gov Studio — theory of operation (system interaction, the two modes, a DAG)
// Mirrors coreapp ExplorerMode/ExplorerLayout. Limited assistant + a strong build CTA.

// Slide imagery (booth-deck exports living in assets/). The decks carry their own
// titles, so we frame each with a light "kicker" caption rather than a duplicate heading.
const SLIDE = {
  forgePlatform: 'assets/forge-platform.png',
  forgeApproach: 'assets/forge-approach.png',
  forgeMaturity: 'assets/forge-maturity.png',
  charTree:      'assets/char-operating-model.png',
  policyDomains: 'assets/char-policy-domains.png',
  compliance:    'assets/char-compliance.png',
  processDomains:'assets/char-process-domains.png',
  controls:      'assets/char-controls.png',
  tools:         'assets/char-tools.png',
  studioFlow:    'assets/studio-interaction.png',
  studioBuild:   'assets/studio-build.png',
  studioOperate: 'assets/studio-operate.png',
  studioDag:     'assets/studio-workflow-dag.png',
};

const CHAR = {
  framework: {
    label: 'Framework', icon: 'book',
    sections: [
      { id: '1.1', name: 'Framework Overview' },
      { id: '1.2', name: 'Core Components' },
      { id: '2.1', name: 'The Documentation Pyramid' },
      { id: '3.1', name: 'Conditional Methods' },
      { id: '4.1', name: 'Accreditation Targets' },
    ],
  },
  policy: {
    label: 'Policy Domain', icon: 'shield',
    sections: [
      { id: 'policy-overview', name: 'Domain overview', overview: true },
      { id: 'AGP', name: 'AI Governance Policy' },
      { id: 'RMP', name: 'Risk Management Policy' },
      { id: 'HOP', name: 'Human Oversight Policy' },
      { id: 'MMP', name: 'Monitoring & Maintenance Policy' },
      { id: 'VAL', name: 'Validation & Verification Policy' },
      { id: 'DGP', name: 'Data Governance Policy' },
      { id: 'TPP', name: 'Third-Party & Partner Policy' },
      { id: 'SAP', name: 'Strategy & Alignment Policy' },
      { id: 'EFP', name: 'Ethics & Fairness Policy' },
      { id: 'LTP', name: 'Lifecycle & Transparency Policy' },
      { id: 'CMP', name: 'Change Management Policy' },
      { id: 'SEP', name: 'Stakeholder Engagement Policy' },
    ],
  },
  process: {
    label: 'Process Domain', icon: 'sitemap',
    sections: [
      { id: 'process-overview', name: 'Domain overview', overview: true },
      { id: 'OVR', name: 'Oversight & Steering' },
      { id: 'RM', name: 'Risk Management' },
      { id: 'SA', name: 'Solution Architecture' },
      { id: 'MON', name: 'Monitoring & Lifecycle' },
      { id: 'HO', name: 'Human Oversight' },
      { id: 'TR', name: 'Transparency & Explainability' },
      { id: 'PV', name: 'Problem & Value' },
      { id: 'IT', name: 'IT & Data Management' },
      { id: 'EF', name: 'Ethics & Fairness' },
      { id: 'UG', name: 'Usage Governance' },
      { id: 'ORG', name: 'Organization Strategy' },
    ],
  },
};

const CONTENT = {
  '1.1': { lede: 'CHAR is the Asher reference process model for clinical AI governance, the body of reference policies, processes, and controls your program is generated from.', body: ['It separates two independent hierarchies: an organizational hierarchy (enterprise → service line → department) and a documentation hierarchy (policy → procedure → SOP). Policies flow down the organization; processes attach to the AI systems that trigger them.', 'A blueprint is generated by mapping your chosen accreditation target onto CHAR, then routing your registered AI systems through the conditional-method engine.'], facts: [['Policy domains', 12], ['Process domains', 11], ['SOP templates', 124]] },
  default: { lede: 'This domain is part of the CHAR reference process model. Browse its base practices, SOPs, and the work products it produces.', body: ['Each domain defines reference base practices that a customer program instantiates. The conditional-method engine decides which apply, based on the attributes of your registered AI systems.', 'In the Governance Studio, building this domain generates the SOPs and work products tailored to your organization and accreditation target.'], facts: [['Base practices', 4], ['SOP templates', 6], ['Frameworks mapped', 8]] },
};

const Explore = ({ onBuild, onNav, onBack }) => {
  // view: 'big' (FORGE|CHAR sub-tabs) · 'browse' (domain reference browser) · 'studio'
  const [view, setView] = React.useState('big');
  const [bigTab, setBigTab] = React.useState('forge');
  const [domain, setDomain] = React.useState('policy');
  const [section, setSection] = React.useState('policy-overview');
  const dom = CHAR[domain];
  const content = CONTENT[section] || CONTENT.default;
  const secName = dom.sections.find((s) => s.id === section)?.name || section;
  const isOverview = !!dom.sections.find((s) => s.id === section)?.overview;
  const navCount = dom.sections.filter((s) => !s.overview).length;

  const switchDomain = (d) => {
    setView('browse'); setDomain(d);
    const first = CHAR[d].sections[0];
    setSection(first.id);
  };

  // Top-level pages in the domain bar (left of the framework tabs)
  const PAGES = [
    { id: 'big', label: 'The big picture', icon: 'map' },
    { id: 'studio', label: 'AI Gov Studio', icon: 'brain' },
  ];
  const pageOn = (id) => view === id;
  const pageBtn = (active) => ({
    display: 'inline-flex', alignItems: 'center', gap: 7, fontFamily: 'inherit', fontSize: 12.5, fontWeight: 600, cursor: 'pointer',
    padding: '8px 14px', borderRadius: 8, border: 'none',
    background: active ? 'var(--asher-gold)' : 'rgba(255,255,255,0.08)', color: active ? '#fff' : '#d6dade',
  });

  return (
    <div className="app-shell" style={{ height: '100%', overflow: 'hidden' }}>
      <div className="app-body" style={{ overflow: 'hidden' }}>
        {/* Dark domain bar */}
        <div style={{ background: 'var(--asher-grey-dark)', color: '#fff', padding: '14px 24px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16, flexWrap: 'wrap' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <span style={{ width: 40, height: 40, borderRadius: 10, background: 'linear-gradient(135deg,var(--asher-gold),var(--asher-brown))', display: 'grid', placeItems: 'center', color: '#fff' }}><Icon name="compass" size={19} /></span>
            <div>
              <div style={{ fontFamily: 'Montserrat', fontSize: 17, fontWeight: 700 }}>Explore Asher Forge</div>
              <div style={{ fontSize: 11.5, color: 'var(--asher-sage)' }}>Asher Informatics · clinical AI governance · read-only</div>
            </div>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
            {PAGES.map((p) => (
              <button key={p.id} onClick={() => setView(p.id)} style={pageBtn(pageOn(p.id))}>
                <Icon name={p.icon} size={13} /> {p.label}
              </button>
            ))}
            <span style={{ width: 1, height: 22, background: 'rgba(255,255,255,0.14)', margin: '0 2px' }} />
            {Object.entries(CHAR).map(([id, d]) => (
              <button key={id} onClick={() => switchDomain(id)} style={pageBtn(view === 'browse' && domain === id)}>
                <Icon name={d.icon} size={13} /> {d.label}
              </button>
            ))}
            <a href="https://aigov-explorer.ashmatics.com/" target="_blank" rel="noopener noreferrer" title="Open the full CHAR Explorer in a new tab" style={{
              display: 'inline-flex', alignItems: 'center', gap: 7, fontSize: 12, fontWeight: 600, textDecoration: 'none',
              padding: '8px 13px', borderRadius: 8, marginLeft: 6, border: '1px solid rgba(255,255,255,0.18)',
              background: 'transparent', color: 'var(--asher-sage)',
            }}>See full explorer <Icon name="external" size={12} /></a>
          </div>
        </div>

        {/* Body — one of: big picture (FORGE|CHAR) · Studio · the domain browser */}
        {view === 'big' ? <BigPicture onBuild={onBuild} tab={bigTab} setTab={setBigTab} />
         : view === 'studio' ? <StudioStory onBuild={onBuild} />
         : (
        <div style={{ flex: 1, display: 'flex', overflow: 'hidden' }}>
          {/* Nav tree */}
          <aside style={{ width: 280, flexShrink: 0, borderRight: '1px solid #e9e5da', background: '#fbfaf6', overflowY: 'auto', padding: '14px 10px' }}>
            <div className="eyebrow" style={{ fontSize: 9.5, padding: '4px 10px 10px' }}>{dom.label} · {navCount}</div>
            {dom.sections.map((s) => {
              const on = section === s.id;
              return (
                <button key={s.id} onClick={() => setSection(s.id)} style={{
                  display: 'flex', alignItems: 'center', gap: 10, width: '100%', textAlign: 'left', fontFamily: 'inherit',
                  padding: '9px 11px', borderRadius: 8, border: 'none', cursor: 'pointer', marginBottom: 2,
                  background: on ? 'var(--asher-cream)' : 'transparent',
                  color: on ? 'var(--asher-grey-dark)' : 'var(--asher-grey-medium)', fontWeight: on || s.overview ? 600 : 500, fontSize: 12.5,
                }}>
                  <span style={{ fontSize: 9.5, fontFamily: 'ui-monospace, monospace', color: on ? 'var(--asher-gold-dark)' : 'var(--asher-grey-light)', minWidth: 26, display: 'inline-flex', justifyContent: 'center' }}>
                    {s.overview ? <Icon name="map" size={12} /> : s.id}
                  </span>
                  {s.name}
                </button>
              );
            })}
          </aside>

          {isOverview ? (
            <DomainOverview domain={domain} onBuild={onBuild} />
          ) : (
            <>
              {/* Content viewer */}
              <main style={{ flex: 1, overflowY: 'auto', padding: '32px 40px' }}>
                <div style={{ maxWidth: 720 }}>
                  <div className="eyebrow" style={{ color: 'var(--asher-gold-dark)', marginBottom: 8 }}>{dom.label} · {section}</div>
                  <h1 style={{ fontSize: 30, margin: '0 0 14px', letterSpacing: '-0.01em' }}>{secName}</h1>
                  <p style={{ fontSize: 16, lineHeight: 1.6, color: 'var(--asher-grey-dark)', marginBottom: 18 }}>{content.lede}</p>
                  {content.body.map((p, i) => <p key={i} style={{ fontSize: 14, lineHeight: 1.7, color: 'var(--asher-grey-medium)', marginBottom: 14 }}>{p}</p>)}
                  <div style={{ display: 'flex', gap: 10, margin: '22px 0' }}>
                    {content.facts.map(([l, n]) => (
                      <div key={l} style={{ flex: 1, background: 'var(--asher-grey-bg)', borderRadius: 10, padding: '14px 16px', textAlign: 'center' }}>
                        <div style={{ fontSize: 26, fontWeight: 700, fontFamily: 'Montserrat', color: 'var(--asher-gold-dark)' }}>{n}</div>
                        <div style={{ fontSize: 10.5, color: 'var(--asher-grey-light)', textTransform: 'uppercase', letterSpacing: '0.04em', marginTop: 4 }}>{l}</div>
                      </div>
                    ))}
                  </div>
                  <BuildCTA onBuild={onBuild} />
                </div>
              </main>

              {/* Context panel + limited assistant */}
              <aside style={{ width: 300, flexShrink: 0, borderLeft: '1px solid #e9e5da', background: '#fbfaf6', overflowY: 'auto', padding: 18 }}>
                <div className="eyebrow" style={{ fontSize: 9.5, marginBottom: 10 }}>On this page</div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginBottom: 22 }}>
                  {[secName, 'Base practices', 'Related SOP templates', 'Mapped frameworks'].map((t, i) => (
                    <a key={t} href="#" onClick={(e) => e.preventDefault()} style={{ fontSize: 12.5, color: i === 0 ? 'var(--asher-gold-dark)' : 'var(--asher-grey-medium)', fontWeight: i === 0 ? 600 : 400, padding: '3px 0', borderLeft: i === 0 ? '2px solid var(--asher-gold)' : '2px solid transparent', paddingLeft: 10 }}>{t}</a>
                  ))}
                </div>
                <MiniAssistant />
              </aside>
            </>
          )}
        </div>
        )}
      </div>
    </div>
  );
};

// Limited assistant (the public-Discover-style helper)
const MiniAssistant = () => {
  const [q, setQ] = React.useState('');
  const [msgs, setMsgs] = React.useState([{ role: 'asher', text: 'Ask me anything about Forge or CHAR, I can explain a domain, a policy, or how a target maps in. (Limited preview.)' }]);
  const send = () => {
    const t = q.trim(); if (!t) return;
    setMsgs((m) => [...m, { role: 'user', text: t }, { role: 'asher', text: 'In this read-only preview I can give a short answer. For governance grounded in your own systems, build a blueprint, the in-product assistant draws on your registry and target.' }]);
    setQ('');
  };
  return (
    <div className="asher-card" style={{ padding: 0, overflow: 'hidden' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '11px 13px', borderBottom: '1px solid #eef0eb' }}>
        <span style={{ width: 24, height: 24, borderRadius: 7, background: 'var(--asher-gold)', display: 'grid', placeItems: 'center', color: '#fff' }}><Icon name="constellation" size={13} /></span>
        <span style={{ fontFamily: 'Montserrat', fontSize: 12.5, fontWeight: 700 }}>Ask Asher</span>
        <span className="chip" style={{ fontSize: 8.5, marginLeft: 'auto' }}>Limited</span>
      </div>
      <div style={{ padding: 12, display: 'flex', flexDirection: 'column', gap: 8, maxHeight: 200, overflowY: 'auto' }}>
        {msgs.map((m, i) => (
          <div key={i} style={{ fontSize: 11.5, lineHeight: 1.5, padding: '8px 10px', borderRadius: 8, alignSelf: m.role === 'user' ? 'flex-end' : 'flex-start', maxWidth: '92%', background: m.role === 'user' ? 'var(--asher-grey-dark)' : 'var(--asher-cream)', color: m.role === 'user' ? '#fff' : 'var(--asher-grey-dark)' }}>{m.text}</div>
        ))}
      </div>
      <div style={{ display: 'flex', gap: 6, padding: 10, borderTop: '1px solid #eef0eb' }}>
        <input value={q} onChange={(e) => setQ(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && send()} placeholder="Ask about Forge…" style={{ flex: 1, fontSize: 11.5, padding: '7px 10px', borderRadius: 7, border: '1px solid #d9d4c6', outline: 'none' }} />
        <button className="btn btn-primary btn-sm" onClick={send} aria-label="Send"><Icon name="arrowRight" size={12} /></button>
      </div>
    </div>
  );
};

/* ─────────────────────────── shared learn-surface primitives ─────────────── */

// Reusable conversion CTA (gold gradient card).
const BuildCTA = ({ onBuild, heading = 'Ready to see what this means for your organization?', body = 'Build a free blueprint sized to your real AI systems and your accreditation target.' }) => (
  <div className="asher-card" style={{ padding: 20, marginTop: 8, display: 'flex', alignItems: 'center', gap: 16, background: 'linear-gradient(135deg,#fffdf6,#fbf4e2)', border: '1px solid rgba(228,170,35,0.3)' }}>
    <div style={{ flex: 1 }}>
      <h3 style={{ fontSize: 16, margin: '0 0 4px' }}>{heading}</h3>
      <p style={{ fontSize: 13, color: 'var(--asher-grey-medium)', margin: 0, lineHeight: 1.5 }}>{body}</p>
    </div>
    <button className="btn btn-primary" onClick={onBuild}>Build a blueprint <Icon name="arrowRight" size={13} /></button>
  </div>
);

// Framed slide image (16:9 booth-deck exports). Scales to its container.
const SlideFrame = ({ src, alt }) => (
  <figure style={{ margin: 0, border: '1px solid #e4ddcb', borderRadius: 14, overflow: 'hidden', background: '#fff', boxShadow: '0 8px 26px rgba(51,63,73,0.07)' }}>
    <img src={src} alt={alt} style={{ width: '100%', height: 'auto', display: 'block' }} />
  </figure>
);

// A slide with a light "kicker" caption above it (no big heading — the deck slides
// carry their own titles, so this avoids doubling them).
const CaptionedSlide = ({ kicker, note, src, alt }) => (
  <section style={{ marginBottom: 30 }}>
    {kicker && (
      <div style={{ display: 'flex', alignItems: 'center', gap: 9, marginBottom: 10 }}>
        <span style={{ width: 6, height: 18, borderRadius: 2, background: 'var(--asher-gold)' }} />
        <span className="eyebrow" style={{ color: 'var(--asher-gold-dark)' }}>{kicker}</span>
      </div>
    )}
    <SlideFrame src={src} alt={alt} />
    {note && <p style={{ fontSize: 12.5, lineHeight: 1.5, color: 'var(--asher-grey-light)', margin: '9px 2px 0' }}>{note}</p>}
  </section>
);

// Scrolling page shell with a heading + intro, shared by the big-picture and Studio pages.
const ScrollPage = ({ eyebrow, title, intro, children }) => (
  <main style={{ flex: 1, overflowY: 'auto', background: '#fbfaf6' }}>
    <div style={{ maxWidth: 1180, margin: '0 auto', padding: '30px 40px 60px' }}>
      {eyebrow && <div className="eyebrow" style={{ color: 'var(--asher-gold-dark)', marginBottom: 8 }}>{eyebrow}</div>}
      {title && <h1 style={{ fontSize: 30, margin: '0 0 12px', letterSpacing: '-0.01em' }}>{title}</h1>}
      {intro && <p style={{ fontSize: 16, lineHeight: 1.6, color: 'var(--asher-grey-dark)', maxWidth: 840, marginBottom: 30 }}>{intro}</p>}
      {children}
    </div>
  </main>
);

/* ───────────────────────────────── The big picture ───────────────────────── */

const BigPicture = ({ onBuild, tab, setTab }) => {
  const tabs = [
    { id: 'forge', label: 'Forge platform', icon: 'brain' },
    { id: 'char', label: 'CHAR model', icon: 'constellation' },
    { id: 'ontology', label: 'Asher Ontology', icon: 'sitemap' },
  ];
  return (
    <main style={{ flex: 1, overflowY: 'auto', background: '#fbfaf6' }}>
      <div style={{ maxWidth: 1180, margin: '0 auto', padding: '24px 40px 60px' }}>
        {/* FORGE | CHAR sub-tabs */}
        <div style={{ display: 'inline-flex', gap: 4, padding: 4, borderRadius: 11, background: '#efeadd', marginBottom: 26 }}>
          {tabs.map((t) => {
            const on = tab === t.id;
            return (
              <button key={t.id} onClick={() => setTab(t.id)} style={{
                display: 'inline-flex', alignItems: 'center', gap: 7, fontFamily: 'inherit', fontSize: 13, fontWeight: 600, cursor: 'pointer',
                padding: '8px 18px', borderRadius: 8, border: 'none',
                background: on ? '#fff' : 'transparent', color: on ? 'var(--asher-grey-dark)' : 'var(--asher-grey-medium)',
                boxShadow: on ? '0 1px 4px rgba(51,63,73,0.12)' : 'none',
              }}><Icon name={t.icon} size={14} color={on ? 'var(--asher-gold-dark)' : 'currentColor'} /> {t.label}</button>
            );
          })}
        </div>

        {tab === 'forge' ? <ForgeGlance onBuild={onBuild} />
         : tab === 'char' ? <CharGlance onBuild={onBuild} />
         : <OntologyGlance />}
      </div>
    </main>
  );
};

// FORGE platform big picture: the platform schematic, the approach, the maturity ladder.
const ForgeGlance = ({ onBuild }) => (
  <>
    <div className="eyebrow" style={{ color: 'var(--asher-gold-dark)', marginBottom: 8 }}>The platform · big picture</div>
    <h1 style={{ fontSize: 30, margin: '0 0 12px', letterSpacing: '-0.01em' }}>Asher Forge, end to end</h1>
    <p style={{ fontSize: 16, lineHeight: 1.6, color: 'var(--asher-grey-dark)', maxWidth: 840, marginBottom: 30 }}>
      Forge is the platform; CHAR is the reference model it runs on. Here is the whole platform at a glance,
      the approach behind it, and how a program matures from a defensible baseline to full certification.
    </p>
    <CaptionedSlide kicker="FORGE platform · workspace, validation, governance" src={SLIDE.forgePlatform}
      alt="Asher Informatics Forge platform schematic: Forge Workspace, the Clinical AI Validation add-on (AI-Select, AI-Watch), and the AI Governance Studio across the full lifecycle."
      note="The full platform: a shared workspace, the optional Clinical AI Validation studios, and the AI Governance Studio that runs across the whole lifecycle." />
    <CaptionedSlide kicker="The Asher approach · why it's different" src={SLIDE.forgeApproach}
      alt="The Asher Informatics approach: common patterns not another playbook, a process reference model, built for agentic and predictive AI, and evidence by design."
      note="Governance-first, outcomes-focused, risk-based. Compliance is a byproduct of good operations, not the goal." />
    <CaptionedSlide kicker="Maturity · five tiers, defensible to certified" src={SLIDE.forgeMaturity}
      alt="Scalable governance maturity: five tiers from Essential (4 policies, days) to Excellence (12+ policies, all 39 SOPs, 6+ months)."
      note="A program can start small and grow. Each tier adds policies and SOPs on the path toward ISO 42001 certification." />
    <BuildCTA onBuild={onBuild} />
  </>
);

// CHAR reference model big picture: the structured tree, then the connected graph.
const CharGlance = ({ onBuild }) => (
  <>
    <div className="eyebrow" style={{ color: 'var(--asher-gold-dark)', marginBottom: 8 }}>The reference model · big picture</div>
    <h1 style={{ fontSize: 30, margin: '0 0 12px', letterSpacing: '-0.01em' }}>CHAR at a glance</h1>
    <p style={{ fontSize: 16, lineHeight: 1.6, color: 'var(--asher-grey-dark)', maxWidth: 840, marginBottom: 30 }}>
      CHAR, the Asher reference model for clinical AI governance, organizes everything into five governance clusters,
      twelve policy domains, twelve process domains, and the SOPs and controls beneath them. See it two ways:
      the structured tree first, then the same model as a living, connected graph.
    </p>
    <CaptionedSlide kicker="Structured tree · the CHAR Operating Model" src={SLIDE.charTree}
      alt="The CHAR Operating Model — five governance clusters, each listing its policies, processes and SOPs, with totals across the bottom."
      note="One reference core, spread to five clusters. Each holds the policies, processes and SOPs it owns. This is the map your blueprint is generated from." />
    <section style={{ marginBottom: 30 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 9, marginBottom: 10 }}>
        <span style={{ width: 6, height: 18, borderRadius: 2, background: 'var(--asher-gold)' }} />
        <span className="eyebrow" style={{ color: 'var(--asher-gold-dark)' }}>Interactive · CHAR as a connected graph</span>
      </div>
      <div style={{ border: '1px solid #e4ddcb', borderRadius: 14, overflow: 'hidden', background: '#fff', boxShadow: '0 8px 26px rgba(51,63,73,0.07)', padding: '8px 8px 0' }}>
        {typeof CharNetworkGraph !== 'undefined'
          ? <CharNetworkGraph />
          : <div style={{ padding: 40, textAlign: 'center', color: 'var(--asher-grey-light)' }}>Interactive graph unavailable.</div>}
      </div>
      <p style={{ fontSize: 12.5, lineHeight: 1.5, color: 'var(--asher-grey-light)', margin: '9px 2px 0' }}>
        Hover any domain to trace what it binds and feeds; hover a process to bloom the controls beneath its SOPs; click to pin.
        The gold links that cross cluster lines are the connectedness CHAR designs in.
      </p>
    </section>
    <BuildCTA onBuild={onBuild} />
  </>
);

/* ─────────────────────────── Domain landings (Policy / Process) ───────────── */

const DOMAIN_OVERVIEW = {
  policy: {
    eyebrow: 'Policy Domain · the policy layer',
    title: 'The 12 policy domains',
    intro: 'Policies govern intent. Each CHAR policy domain is a tokenized template, instantiated to your organization at build time, and every one maps to the regulatory frameworks you target.',
    slides: [
      { kicker: 'Policy layer · 12 tokenized templates', src: SLIDE.policyDomains,
        alt: '12 policy domains with token counts: AGP 204, SAP 91, RMP 82, and more.',
        note: 'Token counts show how much reference material seeds each domain before it is tailored to you.' },
      { kicker: 'Compliance · one model, many frameworks', src: SLIDE.compliance,
        alt: 'Multi-framework compliance: ISO 42001, NIST AI RMF, Joint Commission, ACR ARCH-AI, ONC HTI-1, URAC HAI with coverage percentages.',
        note: 'The same reference model maps to ISO 42001, NIST AI RMF, Joint Commission, ACR ARCH-AI, ONC HTI-1 and URAC HAI at once.' },
    ],
  },
  process: {
    eyebrow: 'Process Domain · the process layer',
    title: 'The 11 process domains',
    intro: 'Processes do the work. CHAR sequences them foundation-first across four implementation layers, and beneath them sit the reusable control patterns and the tools that carry them out.',
    slides: [
      { kicker: 'Process layer · 11 domains, 4 layers', src: SLIDE.processDomains,
        alt: '11 process domains across four layers: Foundation, Risk, Operational, Advanced, with base-practice and SOP counts.',
        note: 'Foundation-first sequencing: 4 implementation layers, 40 base practices, 39 SOPs.' },
      { kicker: 'Controls · 55 healthcare-specific patterns', src: SLIDE.controls,
        alt: 'Controls layer: 55 healthcare-specific control patterns, the base-class pattern, and ISO 42001 Annex A coverage.',
        note: 'Reusable base-class controls, crosswalked to ISO 42001 Annex A and NIST AI RMF, instantiated as your SOPs are built.' },
      { kicker: 'Tools & enablement · platform + integrations', src: SLIDE.tools,
        alt: 'Tools and enablement layer: AshMatics Platform tools, external integrations, and the reference pattern for tool binding.',
        note: 'Each SOP step maps to a tool, whether an AshMatics Platform capability or an external system you already run.' },
    ],
  },
};

const DomainOverview = ({ domain, onBuild }) => {
  const o = DOMAIN_OVERVIEW[domain];
  if (!o) return null;
  return (
    <main style={{ flex: 1, overflowY: 'auto', background: '#fbfaf6' }}>
      <div style={{ maxWidth: 980, margin: '0 auto', padding: '30px 40px 60px' }}>
        <div className="eyebrow" style={{ color: 'var(--asher-gold-dark)', marginBottom: 8 }}>{o.eyebrow}</div>
        <h1 style={{ fontSize: 28, margin: '0 0 12px', letterSpacing: '-0.01em' }}>{o.title}</h1>
        <p style={{ fontSize: 15.5, lineHeight: 1.6, color: 'var(--asher-grey-dark)', maxWidth: 820, marginBottom: 28 }}>{o.intro}</p>
        {o.slides.map((s) => <CaptionedSlide key={s.src} {...s} />)}
        <p style={{ fontSize: 13, color: 'var(--asher-grey-light)', margin: '4px 0 18px' }}>
          Pick a domain on the left to read its base practices, SOP templates and work products.
        </p>
        <BuildCTA onBuild={onBuild} />
      </div>
    </main>
  );
};

/* ───────────────────────────── AI Governance Studio ──────────────────────── */

// Theory of operation: how the reference model becomes a running management system.
const StudioStory = ({ onBuild }) => (
  <ScrollPage eyebrow="How it works · theory of operation" title="The AI Governance Studio"
    intro="Two modes turn the CHAR reference model into a running AI management system. Mode 1 builds and configures your program; Mode 2 operationalizes it, with the agentic wizard guiding throughout.">
    <CaptionedSlide kicker="System interaction · user → studio → outputs" src={SLIDE.studioFlow}
      alt="System interaction overview: a user (client, consultant or regulator), AI-guided through Stage A and Stage B of the Governance Studio, producing a blueprint, policy documents, process workflows and configured tools."
      note="Whether you are a health system, a consultant, or a regulator, the Studio guides you from org profile to an activated program." />
    <CaptionedSlide kicker="Mode 1 · building & configuring (Stage A + B)" src={SLIDE.studioBuild}
      alt="Mode 1 Building and Configuring: Stage A blueprint creation and Stage B capability configuration, producing a governance program ready to operationalize."
      note="Stage A captures your context and recommends a prioritized strategy; Stage B configures the workplans, documentation and workflows." />
    <CaptionedSlide kicker="Mode 2 · operationalizing" src={SLIDE.studioOperate}
      alt="Mode 2 Operationalizing: documentation activities, workflow execution, and ongoing governance for a running AI management system."
      note="Once built, the program runs: documents are generated, workflows execute at each governance touchpoint, and dashboards track compliance." />
    <CaptionedSlide kicker="Worked example · a continuous-monitoring workflow" src={SLIDE.studioDag}
      alt="Workflow process example: a continuous monitoring and maintenance DAG, with base practices rendered as a deployable workflow of conditional, human, and automated steps."
      note="Each base practice becomes a deployable workflow graph, with human-in-the-loop and automated steps, producing audit-ready work products." />
    <BuildCTA onBuild={onBuild} heading="See the Studio build your program" body="Start a free blueprint and watch Stage A and Stage B generate a management system sized to your AI systems." />
  </ScrollPage>
);

// Asher Ontology deep-dive — the self-contained connected-ontology HTML embedded as an iframe.
const OntologyGlance = () => (
  <main style={{ flex: 1, overflowY: 'auto', background: '#fbfaf6' }}>
    <iframe
      src="/packages/asher-ontology/asher-forge-connected-ontology_20260607.html"
      title="Asher Connected Ontology"
      style={{ display: 'block', width: '100%', height: 'calc(100vh - 130px)', border: 'none' }}
    />
  </main>
);

window.Explore = Explore;


/* ===== studio-landing.jsx ===== */
/* global React, Icon, AIMS_PROGRAMS */
// Governance Studio landing, adapted from the Stage B "0B · Modes-led" screen.
// Mode 1 (Configure) is the active path; Mode 2 (Operate) is the paid future
// step (Op-1 / Op-2 are out of scope for June, modeled as a quiet-lock pay gate).

// The Plan & Configure card lists the customer's AI Management Systems (AIMS — the
// owning object; an AIMS IS the aggregation of its Workplans). A customer can hold
// more than one, per license + org scope. Per AIMS the card stays COMPACT: a program
// row + its versioned Blueprint (1:1) + a Workplans summary (the 5-Capability-Area
// spine, N of M domains built, count ready to activate) + a link into the all-5
// Configuration Hub (B1). The per-workplan detail lives in the Hub, not here. The
// AIMS/Workplan model is defined once in shell.jsx (window.AIMS_PROGRAMS).
const StudioLanding = ({ progress, onStart, onOpenBlueprint, onCreateAims, onHub, onExplore, onNav, onResetBlueprint, onSeedBlueprint }) => {
  const hasBlueprint = progress.blueprint;   // a blueprint has been generated & approved
  const hasAims = progress.aims;              // an AIMS has been CREATED from that blueprint
  const [admin, setAdmin] = React.useState(false);
  const programs = (typeof AIMS_PROGRAMS !== 'undefined' && AIMS_PROGRAMS) || [];
  const aimsLabel = programs.length === 1 ? 'Your AI Management System' : `Your AI Management Systems · ${programs.length}`;

  return (
    <div className="app-shell">
      <div className="app-body">
        <div className="section-title-bar">
          <div style={{ display: 'flex', alignItems: 'flex-start', gap: 16 }}>
            <div style={{ flex: 1 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 5 }}>
                <h1 style={{ margin: 0 }}>The Forge AI Governance Studio</h1>
                <span className="chip" style={{ background: '#f1f0ea', border: '1px solid #e0dccf', color: '#5b6358', fontFamily: 'ui-monospace, monospace', fontWeight: 700, fontSize: 10 }}>CHAR v2.1</span>
                <span className="chip chip-success" style={{ fontSize: 10 }}><span className="dot dot-success" /> Knowledge base connected</span>
              </div>
              <p className="subtitle">Plan, then configure, then operate · anchored to the CHAR reference process model</p>
            </div>
            <div style={{ display: 'flex', gap: 8, position: 'relative' }}>
              <button className="btn btn-ghost" aria-label="Demo controls" onClick={() => setAdmin((a) => !a)}><Icon name="settings" size={15} /></button>
              {admin && (
                <>
                  <div onClick={() => setAdmin(false)} style={{ position: 'fixed', inset: 0, zIndex: 39 }} />
                  <div className="asher-card fade-up" style={{ position: 'absolute', top: 'calc(100% + 6px)', right: 0, width: 268, padding: 14, zIndex: 40, boxShadow: 'var(--shadow-asher-md)' }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 6 }}>
                      <Icon name="settings" size={12} color="var(--asher-grey-medium)" />
                      <span className="eyebrow" style={{ fontSize: 9.5 }}>Demo controls · admin</span>
                    </div>
                    <p style={{ fontSize: 11, color: 'var(--asher-grey-light)', margin: '0 0 11px', lineHeight: 1.5 }}>Reset the program state for a clean walk-through. Demo only — not shown in production.</p>
                    <button className="btn btn-secondary btn-sm" style={{ width: '100%', marginBottom: 8, justifyContent: 'center' }} onClick={() => { onSeedBlueprint && onSeedBlueprint(); setAdmin(false); }}>
                      <Icon name="sparkles" size={12} /> Hydrate a seeded blueprint
                    </button>
                    <button className="btn btn-ghost btn-sm" style={{ width: '100%', justifyContent: 'center', color: 'var(--asher-danger)' }} onClick={() => { onResetBlueprint && onResetBlueprint(); setAdmin(false); }}>
                      <Icon name="close" size={12} /> Delete blueprint &amp; reset
                    </button>
                  </div>
                </>
              )}
            </div>
          </div>
        </div>

        <div style={{ padding: '24px 36px 40px', maxWidth: 1240, margin: '0 auto', width: '100%' }}>
          {/* Two mode surfaces ──────────────────────────────────────────── */}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 22 }}>

            {/* MODE 1 · CONFIGURE (active) */}
            <div style={{ background: 'linear-gradient(135deg,#fffdf6 0%,#faf4e3 100%)', border: '2px solid var(--asher-gold)', borderRadius: 14, padding: 24, position: 'relative', overflow: 'hidden' }}>
              <div style={{ position: 'absolute', top: -40, right: -40, width: 150, height: 150, background: 'rgba(228,170,35,0.08)', borderRadius: '50%' }} />
              <div style={{ position: 'relative' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
                  <Icon name="cog" size={20} color="var(--asher-gold-dark)" />
                  <span className="eyebrow" style={{ color: 'var(--asher-gold-dark)' }}>Plan &amp; configure</span>
                </div>
                <h2 style={{ margin: '0 0 6px', fontSize: 22, fontWeight: 700 }}>Build your AI Management System</h2>
                <p style={{ margin: '0 0 16px', fontSize: 13, color: 'var(--asher-grey-medium)', lineHeight: 1.55 }}>
                  Generate a strategic blueprint (Stage A, free), then configure it into policies, SOPs, methods and controls (Stage B).
                </p>

                <div style={{ background: 'rgba(255,255,255,0.72)', border: '1px solid rgba(228,170,35,0.22)', borderRadius: 9, padding: 13, marginBottom: 14 }}>
                  {/* State 1 — nothing yet */}
                  {!hasBlueprint && (
                    <div style={{ display: 'flex', alignItems: 'center', gap: 11, padding: '4px 2px' }}>
                      <span style={{ width: 34, height: 34, borderRadius: 9, background: 'var(--asher-cream)', display: 'grid', placeItems: 'center', color: 'var(--asher-gold-dark)' }}><Icon name="rocket" size={16} /></span>
                      <div style={{ flex: 1 }}>
                        <div style={{ fontSize: 13, fontWeight: 600 }}>No programs yet</div>
                        <div style={{ fontSize: 11.5, color: 'var(--asher-grey-light)' }}>Start with a free blueprint for PTMC Radiology.</div>
                      </div>
                    </div>
                  )}

                  {/* State 2 — blueprint approved, AIMS not yet created. Show the
                      BLUEPRINT, not a named AIMS. The next step is to create the AIMS. */}
                  {hasBlueprint && !hasAims && (
                    <>
                      <div className="eyebrow" style={{ fontSize: 9.5, marginBottom: 7 }}>Your approved blueprint</div>
                      <button onClick={onOpenBlueprint} style={progRow}>
                        <span style={{ width: 7, height: 7, borderRadius: '50%', background: 'var(--asher-gold)', flexShrink: 0 }} />
                        <div style={{ flex: 1, minWidth: 0, textAlign: 'left' }}>
                          <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>{(programs[0] && programs[0].blueprintName) || 'PTMC Radiology — ARCH-AI'}</div>
                          <div style={{ fontSize: 10.5, color: 'var(--asher-grey-light)' }}>Approved · free &amp; complete{programs[0] && programs[0].prm ? ` · ${programs[0].prm}` : ''}</div>
                        </div>
                        <span className="chip chip-success" style={{ fontSize: 9 }}><Icon name="check" size={9} /> Approved</span>
                      </button>
                      <p style={{ margin: '10px 4px 2px', fontSize: 11.5, color: 'var(--asher-grey-medium)', lineHeight: 1.5 }}>
                        Next, create your AI Management System from this blueprint. We seed its workplans so you can start configuring; your free allowance covers one.
                      </p>
                    </>
                  )}

                  {/* State 3 — AIMS created. Show the AIMS, its blueprint, and the Hub. */}
                  {hasAims && (
                    <>
                      <div className="eyebrow" style={{ fontSize: 9.5, marginBottom: 7 }}>{aimsLabel}</div>
                      {programs.map((p) => {
                        const now = p.workplans.filter((w) => w.phase === 'now').length;
                        const next = p.workplans.filter((w) => w.phase === 'next').length;
                        const stepsDone = p.workplans.reduce((n, w) => n + (w.setupDone || 0), 0);
                        const stepsTotal = p.workplans.reduce((n, w) => n + (w.setupTotal || 0), 0);
                        const sub = `AIMS v${p.version} · blueprint ${p.blueprintState} · ${now} NOW / ${next} NEXT`;
                        return (
                          <div key={p.id} style={{ paddingBottom: 4 }}>
                            {/* The AIMS — the owning object (opens its Configuration Hub) */}
                            <button onClick={onHub} style={progRow}>
                              <span style={{ width: 7, height: 7, borderRadius: '50%', background: 'var(--asher-gold)', flexShrink: 0 }} />
                              <div style={{ flex: 1, minWidth: 0, textAlign: 'left' }}>
                                <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>{p.name}</div>
                                <div style={{ fontSize: 10.5, color: 'var(--asher-grey-light)' }}>{sub}</div>
                              </div>
                              <Icon name="arrowRight" size={12} color="var(--asher-grey-light)" />
                            </button>
                            {/* Its versioned Blueprint (1:1) + Workplans summary → Configuration Hub */}
                            <div style={{ paddingLeft: 17, marginTop: 2, display: 'flex', flexDirection: 'column', gap: 4 }}>
                              <button onClick={onOpenBlueprint} style={subLink}>
                                <Icon name="verified" size={11} color="var(--asher-grey-light)" />
                                Blueprint v{p.version} · {p.blueprintState}{p.prm ? ` · ${p.prm}` : ''}
                                <Icon name="arrowRight" size={10} color="var(--asher-grey-light)" style={{ marginLeft: 'auto' }} />
                              </button>
                              <button onClick={onHub} style={subLink}>
                                <Icon name="layers" size={11} color="var(--asher-grey-light)" />
                                Workplans · {p.workplans.length} areas · {stepsDone} of {stepsTotal} steps — open the Configuration Hub
                                <Icon name="arrowRight" size={10} color="var(--asher-grey-light)" style={{ marginLeft: 'auto' }} />
                              </button>
                            </div>
                          </div>
                        );
                      })}
                    </>
                  )}
                </div>

                <div style={{ display: 'flex', gap: 8 }}>
                  {!hasBlueprint && (
                    <>
                      <button className="btn btn-primary" style={{ flex: 1 }} onClick={onStart}>
                        <Icon name="cog" size={14} /> Enter Configure mode <Icon name="arrowRight" size={12} />
                      </button>
                      <button className="btn btn-secondary" onClick={onStart}><Icon name="plus" size={12} /> New</button>
                    </>
                  )}
                  {hasBlueprint && !hasAims && (
                    <>
                      <button className="btn btn-primary" style={{ flex: 1 }} onClick={onCreateAims}>
                        <Icon name="sparkles" size={14} /> Create AIMS from blueprint <Icon name="arrowRight" size={12} />
                      </button>
                      <button className="btn btn-secondary" onClick={onOpenBlueprint}><Icon name="eye" size={12} /> Blueprint</button>
                    </>
                  )}
                  {hasAims && (
                    <button className="btn btn-primary" style={{ flex: 1 }} onClick={onHub}>
                      <Icon name="cog" size={14} /> Resume in the Configuration Hub <Icon name="arrowRight" size={12} />
                    </button>
                  )}
                </div>
              </div>
            </div>

            {/* MODE 2 · OPERATE (paid gate, future step) */}
            <ModeTwoLocked />
          </div>

          {/* Three secondary surfaces ─────────────────────────────────────── */}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 14 }}>
            <SecondarySurface icon="brain" eyebrow="Start something new" title="Begin a governance program"
              body="Two ways in, both produce a working AI Management System." twoPath onStart={onStart} />
            <SecondarySurface icon="map" eyebrow="Platform & reference model" title="Explore Asher Forge"
              body="The big picture, the CHAR framework's policy and process domains, and how the AI Governance Studio works. With limited AI Q&A."
              counts={[['Policy domains', 12], ['Process domains', 11], ['SOP templates', 124]]} cta="Open the Explorer" onCta={onExplore} />
            <SecondarySurface icon="book" eyebrow="Artifact library" title="Your program artifacts"
              body="Policies, procedures, workflows, and controls you generate appear here once you start building."
              locked lockNote="Unlocks in Stage B" />
          </div>
        </div>
      </div>
    </div>
  );
};

const progRow = { display: 'flex', alignItems: 'center', gap: 10, padding: '9px 4px', width: '100%', background: 'transparent', border: 'none', borderTop: '1px solid rgba(0,0,0,0.05)', cursor: 'pointer', fontFamily: 'inherit' };
// Secondary line under an AIMS row: its Blueprint and the Workplans → Hub link.
const subLink = { display: 'flex', alignItems: 'center', gap: 6, width: '100%', textAlign: 'left', background: 'transparent', border: 'none', padding: '3px 2px', cursor: 'pointer', fontFamily: 'inherit', fontSize: 10.5, color: 'var(--asher-grey-medium)' };

// ── Mode 2 · Operate — green-tinted, currently EMPTY (no program operating) ──
// Green (#10b981) is the one high-contrast accent against the otherwise gold/cream
// Studio. Nothing operates yet: a program becomes operational only after a workplan
// is configured (Mode 1) and a workflow is operationalized from it. The free tier
// includes operationalizing one workflow — the hook for the next build step, where a
// real operationalized workflow will populate this surface.
const ModeTwoLocked = () => {
  const FREE = (window.DATA && window.DATA.FREE_TIER) || { operationalizedWorkflows: 1 };
  const n = FREE.operationalizedWorkflows;
  return (
    <div style={{ background: 'linear-gradient(160deg,#ffffff 0%,var(--operate-green-bg) 100%)', border: '2px solid var(--operate-green)', borderRadius: 14, padding: 24, position: 'relative', overflow: 'hidden' }}>
      <div style={{ position: 'absolute', top: -40, right: -40, width: 150, height: 150, background: 'rgba(16,185,129,0.10)', borderRadius: '50%' }} />
      <div style={{ position: 'relative' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
          <Icon name="activity" size={18} color="var(--operate-green-dark)" />
          <span className="eyebrow" style={{ color: 'var(--operate-green-dark)' }}>Operationalize</span>
        </div>
        <h2 style={{ margin: '0 0 6px', fontSize: 22, fontWeight: 700, color: 'var(--asher-grey-dark)' }}>Run your AI Management System</h2>
        <p style={{ margin: '0 0 16px', fontSize: 13, color: 'var(--asher-grey-medium)', lineHeight: 1.55 }}>
          Execute workflows, capture Records and Signals, manage incidents. Guided or Automated execution.
        </p>

        <div style={{ background: 'rgba(255,255,255,0.78)', border: '1px solid var(--operate-green-border)', borderRadius: 9, padding: 13, marginBottom: 14 }}>
          <div className="eyebrow" style={{ fontSize: 9.5, marginBottom: 9, color: 'var(--operate-green-deep)' }}>No programs operating yet</div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 11, padding: '4px 2px' }}>
            <span style={{ width: 34, height: 34, borderRadius: 9, background: 'var(--operate-green-bg)', display: 'grid', placeItems: 'center', color: 'var(--operate-green-dark)', flexShrink: 0 }}><Icon name="activity" size={16} /></span>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>Nothing to operate yet</div>
              <div style={{ fontSize: 11.5, color: 'var(--asher-grey-light)' }}>Configure a workplan first, then operationalize a workflow from it.</div>
            </div>
          </div>
          <p style={{ margin: '10px 4px 2px', fontSize: 11, fontStyle: 'italic', color: 'var(--asher-grey-light)', lineHeight: 1.5, textAlign: 'center' }}>
            Your free allowance includes operationalizing {n} workflow. Programs become operational once a configured workplan is operationalized.
          </p>
        </div>

        <div style={{ display: 'flex', gap: 8 }}>
          <button className="btn btn-operate" style={{ flex: 1, opacity: 0.5, cursor: 'not-allowed' }} disabled>
            <Icon name="lock" size={13} /> Enter Operate mode <Icon name="arrowRight" size={12} />
          </button>
        </div>
      </div>
    </div>
  );
};

const SecondarySurface = ({ icon, eyebrow, title, body, counts, cta, onCta, twoPath, onStart, locked, lockNote }) => (
  <div className="asher-card" style={{ padding: 18, position: 'relative', opacity: locked ? 0.85 : 1 }}>
    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
      <Icon name={icon} size={16} color="var(--asher-grey-medium)" />
      <span className="eyebrow" style={{ fontSize: 10 }}>{eyebrow}</span>
      {locked && <span className="chip chip-locked" style={{ fontSize: 9.5, marginLeft: 'auto' }}><Icon name="lock" size={9} /> {lockNote}</span>}
    </div>
    <h3 style={{ margin: '0 0 6px', fontSize: 15 }}>{title}</h3>
    <p style={{ margin: '0 0 14px', fontSize: 12, color: 'var(--asher-grey-medium)', lineHeight: 1.5 }}>{body}</p>
    {counts && (
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 14 }}>
        {counts.map(([label, n]) => (
          <span key={label} style={{ fontSize: 11, color: 'var(--asher-grey-medium)', background: 'var(--asher-grey-bg)', padding: '3px 8px', borderRadius: 5 }}>
            <span style={{ fontWeight: 700, color: 'var(--asher-grey-dark)' }}>{n}</span> {label}
          </span>
        ))}
      </div>
    )}
    {twoPath && (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        <button className="btn btn-primary btn-sm" style={{ justifyContent: 'space-between' }} onClick={onStart}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><Icon name="constellation" size={12} /> Guided assessment</span>
          <span style={{ fontSize: 10, opacity: 0.85 }}>~15 min</span>
        </button>
        <button className="btn btn-secondary btn-sm" style={{ justifyContent: 'space-between' }} onClick={onStart}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><Icon name="settings" size={12} /> Build manually</span>
          <span style={{ fontSize: 10, opacity: 0.7 }}>Advanced</span>
        </button>
      </div>
    )}
    {cta && <button className="btn btn-secondary btn-sm" style={{ width: '100%' }} onClick={onCta}>{cta} <Icon name="arrowRight" size={11} /></button>}
    {locked && !cta && !twoPath && <button className="btn btn-secondary btn-sm" style={{ width: '100%' }} disabled>Locked</button>}
  </div>
);

// ── AI System Registry (read-only destination) ──────────────────────────────
// A standing, revisitable surface (sidebar → Organizational Profile). Read-only stub
// for the demo: the guided capture and editing live in the Stage A flow. Built
// straight from the PTMC scenario data so it stays in sync with the blueprint.
const RISK_TONE = {
  high:     { label: 'High-assurance', bg: 'rgba(192,73,60,0.10)', fg: 'var(--asher-danger)' },
  moderate: { label: 'Operational',    bg: 'var(--asher-cream)',   fg: 'var(--asher-gold-dark)' },
  sandbox:  { label: 'Sandboxed',      bg: 'var(--asher-grey-bg)', fg: 'var(--asher-grey-medium)' },
};

// Read-only registry body (count strip + systems grouped by governance boundary).
// DERIVED from the single source of truth — the captured `registry` on App — so
// what a viewer enters in the guided capture (StepRegistryFull) shows up here and
// in the Stage A review. OFFERING ids == boundary ids, so the captured `included`
// set groups straight onto ORG.boundaries; the canned DATA.SYSTEMS is no longer
// read. Shows an empty state when nothing has been registered yet.
const RegistryReviewBody = ({ registry }) => {
  const { ORG } = window.DATA;
  const REG = window.REG;
  const reg = registry || REG.emptyRegistry();
  const included = reg.included || {};
  const byId = {};
  [...REG.CATALOG, ...(reg.customs || [])].forEach((s) => { byId[s.id] = s; });

  const row = (id) => {
    const sys = byId[id]; if (!sys) return null;
    const e = included[id] || {};
    const regulated = e.source ? !e.source.inhouse : !sys.builtInHouse;
    return { id, name: sys.name, use: sys.task || sys.use || '', modality: sys.modality, regulated, autonomous: sys.autonomous, offering: sys.offering };
  };
  const ids = Object.keys(included);
  const today = ids.length;
  const groups = ORG.boundaries
    .map((b) => ({ b, systems: ids.map(row).filter((r) => r && r.offering === b.id) }))
    .filter((g) => g.systems.length);
  const orphans = ids.map(row).filter((r) => r && !ORG.boundaries.some((b) => b.id === r.offering));

  if (today === 0) {
    return (
      <div className="asher-card" style={{ padding: '40px 24px', textAlign: 'center', color: 'var(--asher-grey-light)' }}>
        <Icon name="scan" size={26} color="#cfcabd" />
        <div style={{ marginTop: 10, fontSize: 13 }}>No AI systems registered yet. Open the guided setup to add the systems you run.</div>
      </div>
    );
  }

  const SystemRow = ({ s }) => (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 11px', borderRadius: 8 }}>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>{s.name}</div>
        <div style={{ fontSize: 11.5, color: 'var(--asher-grey-medium)', lineHeight: 1.45 }}>{s.use}</div>
      </div>
      <span style={{ fontSize: 11, color: 'var(--asher-grey-light)', flexShrink: 0, width: 96, textAlign: 'right' }}>{s.modality}</span>
      <div style={{ display: 'flex', gap: 5, flexShrink: 0, width: 168, justifyContent: 'flex-end' }}>
        <span className="chip" style={{ fontSize: 9.5 }}>{s.regulated ? 'FDA-cleared' : 'In-house'}</span>
        {s.autonomous && <span className="chip chip-gold" style={{ fontSize: 9.5 }}>Autonomous</span>}
      </div>
    </div>
  );

  return (
    <>
      {/* Count strip */}
      <div style={{ display: 'flex', gap: 12, marginBottom: 22, flexWrap: 'wrap' }}>
        {[['Registered today', today], ['Projected (12 mo)', reg.projected || today], ['Governance boundaries', groups.length]].map(([l, n]) => (
          <div key={l} style={{ flex: '1 1 180px', background: 'var(--asher-grey-bg)', borderRadius: 10, padding: '14px 16px' }}>
            <div style={{ fontSize: 26, fontWeight: 700, fontFamily: 'Montserrat', color: 'var(--asher-gold-dark)' }}>{n}</div>
            <div style={{ fontSize: 10.5, color: 'var(--asher-grey-light)', textTransform: 'uppercase', letterSpacing: '0.04em', marginTop: 4 }}>{l}</div>
          </div>
        ))}
      </div>

      {/* Systems grouped by governance boundary */}
      {groups.map(({ b, systems }) => {
        const tone = RISK_TONE[b.risk] || RISK_TONE.moderate;
        return (
          <div key={b.id} className="asher-card" style={{ padding: 0, marginBottom: 16, overflow: 'hidden' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 11, padding: '13px 18px', borderBottom: '1px solid #eef0eb', background: '#fbfaf6' }}>
              <span style={{ width: 32, height: 32, borderRadius: 9, background: 'var(--asher-cream)', display: 'grid', placeItems: 'center', color: 'var(--asher-gold-dark)', flexShrink: 0 }}><Icon name={b.icon} size={16} /></span>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--asher-grey-dark)' }}>{b.name}</div>
                <div style={{ fontSize: 11.5, color: 'var(--asher-grey-light)' }}>{b.deployment}</div>
              </div>
              <span style={{ fontSize: 10.5, fontWeight: 600, padding: '4px 10px', borderRadius: 999, background: tone.bg, color: tone.fg }}>{b.riskLabel}</span>
            </div>
            <div style={{ padding: 7 }}>{systems.map((s) => <SystemRow key={s.id} s={s} />)}</div>
          </div>
        );
      })}

      {/* Systems added manually that don't map to a known boundary */}
      {orphans.length > 0 && (
        <div className="asher-card" style={{ padding: 0, marginBottom: 16, overflow: 'hidden' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 11, padding: '13px 18px', borderBottom: '1px solid #eef0eb', background: '#fbfaf6' }}>
            <span style={{ width: 32, height: 32, borderRadius: 9, background: 'var(--asher-cream)', display: 'grid', placeItems: 'center', color: 'var(--asher-gold-dark)', flexShrink: 0 }}><Icon name="plus" size={16} /></span>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--asher-grey-dark)' }}>Added manually</div>
              <div style={{ fontSize: 11.5, color: 'var(--asher-grey-light)' }}>Systems you entered that aren't in the catalog yet</div>
            </div>
          </div>
          <div style={{ padding: 7 }}>{orphans.map((s) => <SystemRow key={s.id} s={s} />)}</div>
        </div>
      )}
    </>
  );
};

// The standing AI System Registry surface (sidebar → Organizational Profile).
// Read-only review of the configured registry. "Open guided setup" (onStart) opens
// JUST the detailed registry capture (the regsetup screen), not the Stage A wizard.
const SystemRegistry = ({ onStart, setup, registry }) => {
  const govName = (setup && setup.govName) || 'Radiology & Imaging Services';
  return (
    <div className="app-shell">
      <div className="app-body">
        <div className="section-title-bar">
          <div style={{ display: 'flex', alignItems: 'flex-start', gap: 16 }}>
            <div style={{ flex: 1 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 5 }}>
                <h1 style={{ margin: 0 }}>AI System Registry</h1>
                <span className="chip" style={{ fontSize: 10 }}><Icon name="eye" size={11} /> Read-only preview</span>
              </div>
              <p className="subtitle">The inventory of AI systems in scope for {govName}. Each system's attributes drive which governance methods your blueprint pulls in. This registry is a standing record, it evolves on its own, independent of any single blueprint.</p>
            </div>
          </div>
        </div>

        <div style={{ padding: '24px 36px 40px', maxWidth: 1100, margin: '0 auto', width: '100%' }}>
          <RegistryReviewBody registry={registry} />

          {/* Guided edit opens the detailed capture (regsetup), the same screen
              used in first-run setup — not the Stage A blueprint wizard. */}
          <div className="asher-card" style={{ padding: 18, display: 'flex', alignItems: 'center', gap: 16, background: 'linear-gradient(135deg,#fffdf6,#fbf4e2)', border: '1px solid rgba(228,170,35,0.3)', marginTop: 4 }}>
            <div style={{ flex: 1 }}>
              <h3 style={{ fontSize: 15, margin: '0 0 4px' }}>Add or edit systems in the guided flow</h3>
              <p style={{ fontSize: 12.5, color: 'var(--asher-grey-medium)', margin: 0, lineHeight: 1.5 }}>Open the detailed registry setup to add systems, set deployment, and refine attributes. Changes re-route which governance methods your blueprints pull in.</p>
            </div>
            {onStart && <button className="btn btn-primary" onClick={onStart}><Icon name="sliders" size={14} /> Open guided setup <Icon name="arrowRight" size={12} /></button>}
          </div>
        </div>
      </div>
    </div>
  );
};

Object.assign(window, { StudioLanding, SystemRegistry, RegistryReviewBody });


/* ===== screen-assessment.jsx (StepMaturity/StepPriorities + window.ASSESS) ===== */
/* global React, Icon, ExternalAppFrame */
// Stage A · Capability Maturity + Priorities & Compliance.
// Lifted-and-shifted from the coreapp Blueprint Wizard (Vue): the 11-question
// maturity assessment across 5 capability clusters, and the priorities ranking +
// compliance journey. Content and scoring are ported FAITHFULLY so the numbers
// match the real product if anyone cross-checks; the backend tier-rules / archetype
// machinery is NOT ported (the wireframe "Generate" is a staged fake).
//
//   · Maturity questions/options → coreapp src/data/maturityQuestions.ts
//   · Scoring (per-cluster average → mean of cluster scores → 5 bands)
//        → coreapp src/services/MaturityScoringService.ts
//   · Priorities + journey stages → coreapp src/config/governancePrioritiesConfig.ts
//
// window.ASSESS exposes the data + helpers (empty / seed / scoreMaturity) so App can
// lift assessment state alongside the registry and seed it for the #customer scenario.

// ── Maturity levels (A–E → 1–5) and the score→label bands ────────────────────
const MAT_LEVELS = [
  { n: 1, key: 'AWARENESS', label: 'Awareness' },
  { n: 2, key: 'ACTIVE', label: 'Active' },
  { n: 3, key: 'OPERATIONAL', label: 'Operational' },
  { n: 4, key: 'SYSTEMIC', label: 'Systemic' },
  { n: 5, key: 'TRANSFORMATIONAL', label: 'Transformational' },
];
// Score → maturity label (MaturityScoringService thresholds).
function levelLabelFor(score) {
  if (score <= 0) return '—';
  if (score < 1.5) return 'Awareness';
  if (score < 2.5) return 'Active';
  if (score < 3.5) return 'Operational';
  if (score < 4.5) return 'Systemic';
  return 'Transformational';
}

// ── The 5 capability clusters (radar axes, in display order) ─────────────────
const MAT_CLUSTERS = [
  { id: 'strategy_leadership', label: 'Strategy & Leadership', axis: 'Strategy &\nLeadership', dot: '#e4aa23' },
  { id: 'risk_ethics_compliance', label: 'Risk, Ethics & Compliance', axis: 'Risk, Ethics\n& Compliance', dot: '#c0493c' },
  { id: 'data_model_lifecycle', label: 'Data, Model & System Lifecycle', axis: 'Data &\nLifecycle', dot: '#3b6ea5' },
  { id: 'operations_workflow', label: 'Operations & Workflow Integration', axis: 'Operations\n& Workflow', dot: '#3f9b6e' },
  { id: 'vendor_partners', label: 'Vendor, Partners & Ecosystem', axis: 'Vendor &\nPartners', dot: '#8161b0' },
];

// ── The 11 questions (verbatim from maturityQuestions.ts) ────────────────────
const MAT_QUESTIONS = [
  { id: 'q1_1', cluster: 'strategy_leadership', number: '1.1', title: 'AI Strategic Alignment',
    subtitle: "Which best describes how AI is connected to your organization's strategy and clinical priorities?",
    options: [
      'No formal AI strategy; AI discussed only informally.',
      'Some AI initiatives mentioned in strategy documents, but not clearly prioritized.',
      'A defined AI strategy exists for selected domains or service lines.',
      'A board- or executive-approved AI strategy explicitly linked to organizational and clinical priorities.',
      'AI is a core strategic capability with dynamic roadmaps, portfolio management, and regular executive review of AI outcomes.',
    ] },
  { id: 'q1_2', cluster: 'strategy_leadership', number: '1.2', title: 'Governance Leadership & Roles',
    subtitle: 'How is leadership for clinical AI governance structured?',
    options: [
      'No designated leaders or committee for AI.',
      'Informal group or single champion occasionally reviews AI efforts.',
      'Named AI governance committee with defined (but limited) scope and membership.',
      'Multi-disciplinary AI governance board with clear charter, decision rights, and executive sponsorship (e.g., CMIO/CDO/CHAIO).',
      'Embedded leadership model with defined AI roles (e.g., algorithmic consultants, domain champions) and governance integrated into enterprise decision-making.',
    ] },
  { id: 'q2_1', cluster: 'risk_ethics_compliance', number: '2.1', title: 'Risk & Ethics Management',
    subtitle: 'How are ethical, safety, and equity risks of AI handled?',
    options: [
      'Not addressed formally; handled ad-hoc if issues arise.',
      'Basic ethical/risk questions considered for some projects, without standard methods.',
      'Structured risk/ethics reviews for selected or high-risk AI tools.',
      'Standardized risk and ethics review integrated into the AI lifecycle for all clinical AI tools.',
      'Proactive, data-driven risk and equity program with ongoing monitoring, clear escalation paths, and regular reporting into quality/safety governance.',
    ] },
  { id: 'q2_2', cluster: 'risk_ethics_compliance', number: '2.2', title: 'Regulatory, Security & Privacy Compliance',
    subtitle: 'How do you ensure that AI solutions meet regulatory, security, and privacy requirements?',
    options: [
      'No AI-specific processes beyond general IT/security policies.',
      'Ad-hoc checks of regulatory status and security/privacy for some AI tools.',
      'Defined review for higher-risk AI (e.g., those considered medical devices or handling sensitive data).',
      'Standard AI-specific compliance and security review for all relevant tools, with documented decisions and responsibilities.',
      'Integrated, continuously updated compliance framework for AI, with monitoring dashboards and clear ownership across legal, risk, and security teams.',
    ] },
  { id: 'q2_3', cluster: 'risk_ethics_compliance', number: '2.3', title: 'Human Oversight of AI Clinical Decisions',
    subtitle: 'How is human oversight of AI-assisted clinical decisions structured?',
    options: [
      'No formal oversight model; clinicians use AI outputs at their own discretion.',
      'General expectations that clinicians review AI outputs, but no defined oversight levels or protocols.',
      'Defined oversight requirements for high-risk AI tools (e.g., mandatory clinician review before acting on AI recommendations).',
      'Structured oversight models (e.g., human-in-the-loop, human-on-the-loop) assigned per AI system based on risk level, with documented override authority and escalation paths.',
      'Comprehensive oversight program with risk-tiered models for all clinical AI, tracked override/intervention rates, competency validation, and oversight patterns feeding back into system improvement.',
    ] },
  { id: 'q3_1', cluster: 'data_model_lifecycle', number: '3.1', title: 'Data Governance & Readiness for AI',
    subtitle: 'How ready is your data environment to support clinical AI?',
    options: [
      'Data is fragmented and hard to access; no AI-specific data governance.',
      'Some curated datasets or exports exist for individual AI projects.',
      'Governed datasets exist for key domains, with basic standards and documentation.',
      'Enterprise data governance for AI with defined standards (e.g., CDEs), quality controls, and documented lineage for most prioritized data.',
      'Comprehensive, enterprise-wide data fabric for AI, with strong governance, near real-time integration for critical use cases, and systematic bias/shift monitoring.',
    ] },
  { id: 'q3_2', cluster: 'data_model_lifecycle', number: '3.2', title: 'AI System Validation & Lifecycle Management',
    subtitle: 'How are AI models and their hosting systems evaluated and managed over time?',
    options: [
      'Little or no formal validation; once deployed, AI systems are rarely revisited.',
      'Basic pre-deployment testing for some models; limited post-deployment checks of model or system performance.',
      'Defined validation for most AI models with occasional performance reviews, including the applications and infrastructure that host them.',
      'Standard lifecycle process for all AI models and their hosting systems — including intake, risk assessment, validation, approval, monitoring, change control, and retirement.',
      'Mature lifecycle management with risk-tiering for models and systems, continuous monitoring (including outcomes, equity, and integration health), and clear triggers for retraining, patching, or decommissioning.',
    ] },
  { id: 'q4_1', cluster: 'operations_workflow', number: '4.1', title: 'Clinical Workflow Integration & Adoption',
    subtitle: 'How are AI tools integrated into clinical workflows and adopted by users?',
    options: [
      'Tools are mostly used outside core workflows (e.g., separate portals or ad-hoc use).',
      'Some AI tools are integrated into workflows, but inconsistently and with limited training.',
      'Standard patterns exist for integrating AI into EHR/CDS for selected use cases, with structured training.',
      'AI is routinely integrated into clinical workflows with defined UX patterns, training plans, and support structures.',
      'AI is seamlessly embedded into pathways and decision-making across the enterprise, with co-designed workflows and feedback from clinicians continuously shaping improvements.',
    ] },
  { id: 'q4_2', cluster: 'operations_workflow', number: '4.2', title: 'Impact Measurement & Value Realization',
    subtitle: "How do you measure AI's impact on care and operations?",
    options: [
      'AI impact is not measured separately from general operations.',
      'Some anecdotal evidence or basic metrics are tracked for individual projects.',
      'Defined success metrics for most significant AI initiatives, with periodic reporting.',
      'Systematic measurement framework for AI tools linked to quality, safety, and operational KPIs, with regular governance reporting.',
      'Continuous measurement and learning loops where AI performance, outcomes, and equity metrics drive ongoing improvement of both AI systems and clinical processes.',
    ] },
  { id: 'q5_1', cluster: 'vendor_partners', number: '5.1', title: 'Vendor & Partner Management for AI',
    subtitle: 'How do you manage external vendors and partners providing AI solutions?',
    options: [
      'AI vendors are handled via standard IT procurement with no AI-specific criteria.',
      'Some AI-specific due diligence for selected vendors (e.g., security or regulatory checks).',
      'Defined AI vendor evaluation process for higher-risk or enterprise-wide solutions.',
      'Standardized AI vendor and partner management process, including security, compliance, performance, and contractual SLAs for safety and support.',
      'Proactive AI ecosystem strategy, with portfolio-level vendor management, joint roadmapping, and shared accountability for outcomes and risk.',
    ] },
  { id: 'q5_2', cluster: 'vendor_partners', number: '5.2', title: 'Collaboration & External Engagement',
    subtitle: 'How does your organization engage with external networks around clinical AI?',
    options: [
      'No structured external engagement around AI.',
      'Occasional participation in external events or pilots, driven by individuals.',
      'Some formal collaborations or memberships relevant to AI governance or implementation.',
      'Active participation in external initiatives that inform internal AI governance (e.g., consortia, standards bodies, multi-site studies, health system network AI working groups).',
      'Recognized, active role shaping external best practices and policy on clinical AI, with bi-directional learning feeding directly into internal governance and operations.',
    ] },
];

// ── The 10 governance priorities (governancePrioritiesConfig.ts) ─────────────
const GOV_PRIORITIES = [
  { id: 'risk_patient_safety', label: 'Risk & Patient Safety', icon: 'shield', desc: 'Ensure AI tools do not harm patients and risks are systematically managed.' },
  { id: 'regulatory_compliance', label: 'Regulatory Compliance', icon: 'fileCheck', desc: 'Meet current and emerging regulatory requirements for AI in healthcare.' },
  { id: 'ethics_equity', label: 'Ethics & Equity', icon: 'heart', desc: 'Address bias, fairness, and ethical concerns in AI-driven clinical decisions.' },
  { id: 'strategic_alignment', label: 'Strategic Alignment', icon: 'compass', desc: 'Ensure AI investments align with organizational strategy and clinical priorities.' },
  { id: 'operational_efficiency', label: 'Operational Efficiency', icon: 'bolt', desc: 'Improve workflows, reduce burden, and realize measurable value from AI.' },
  { id: 'data_readiness', label: 'Data Readiness & Quality', icon: 'layers', desc: 'Build a reliable data foundation to support AI training, validation, and monitoring.' },
  { id: 'vendor_ecosystem', label: 'Vendor & Ecosystem Management', icon: 'users', desc: 'Evaluate, onboard, and govern external AI vendors and partners effectively.' },
  { id: 'monitoring_accountability', label: 'Monitoring & Accountability', icon: 'eye', desc: 'Track AI performance, outcomes, and equity metrics with clear ownership.' },
  { id: 'workforce_adoption', label: 'Workforce Readiness & Adoption', icon: 'users', desc: 'Prepare clinicians and staff for effective AI use through training and change management.' },
  { id: 'accreditation_certification', label: 'Accreditation & Certification', icon: 'target', desc: 'Meet or exceed standards for accreditation bodies and certification frameworks.' },
];

// Journey-stage options for the compliance question (temporal enrichment).
const JOURNEY_STAGES = [
  { value: 'achieved', label: 'Achieved', desc: 'Already certified or in compliance' },
  { value: 'well_underway', label: 'Well underway', desc: 'Active program with measurable progress' },
  { value: 'early_stages', label: 'Early stages', desc: 'Started but still building foundation' },
  { value: 'planning_12mo', label: 'Planning (12 mo)', desc: 'Committed plan with a 12-month target' },
  { value: 'horizon', label: 'Horizon', desc: 'Aspirational; no active plan yet' },
];

// The wizard's Step-1 target options map to a framework label for the compliance
// journey question. 'baseline' has no external framework, so no journey is asked.
const TARGET_FRAMEWORK = {
  'arch-ai': 'ACR ARCH-AI Accreditation',
  nist: 'NIST AI RMF',
  jc: 'Responsible Use of AI in Healthcare (Joint Commission)',
  iso: 'ISO/IEC 42001',
};

// ── Maturity assessment providers (the Step-4 "how do you want to assess?" pick) ──
// An extensible registry of maturity models. The DEFAULT, `asher`, is the native
// in-wizard capture (the 11-question Asher Enterprise model) and is the ONLY
// provider that feeds the blueprint — its answers populate the radar, the
// Now/Next/Later phasing, and the AIMS-creation ceremony score. Other providers
// are embedded, illustrative PREVIEWS shown alongside (they don't write back; see
// the chooser-shape decision). To add a future accreditation surface (NIST, ISO
// 42001, Joint Commission), append an entry here: `kind: 'embed'` + an `embedKey`
// into window.DATA.EMBEDS, or `kind: 'native'` with its own capture component.
// `target` is the matching Step-1 target id, used to flag "matches your target".
const MATURITY_PROVIDERS = [
  { id: 'asher', label: 'Asher Enterprise Maturity', kind: 'native', icon: 'brain', available: true,
    blurb: 'Cross-setting capability maturity, 11 questions across 5 governance areas. This is the assessment your blueprint is built from.' },
  { id: 'arch-ai', label: 'ARCH-AI Readiness & Maturity', kind: 'embed', icon: 'scan', available: true, badge: 'Preview',
    embedKey: 'archAiAssessment', target: 'arch-ai',
    blurb: 'ACR ARCH-AI accreditation readiness across the 10 attestation gates, plus imaging-AI maturity (ISO 33072). Radiology-specific.' },
  { id: 'nist', label: 'NIST AI RMF Profile', kind: 'embed', icon: 'shield', available: false, target: 'nist',
    blurb: 'A GOVERN / MAP / MEASURE / MANAGE readiness profile against the NIST AI Risk Management Framework.' },
  { id: 'iso', label: 'ISO/IEC 42001 Readiness', kind: 'embed', icon: 'verified', available: false, target: 'iso',
    blurb: 'AI Management System conformity readiness against ISO/IEC 42001.' },
];

// ── Scoring (MaturityScoringService) ─────────────────────────────────────────
// answers: { [questionId]: 1..5 }. Per-cluster score = mean of answered questions
// in that cluster (0 if none answered). Overall = mean of the cluster scores over
// clusters that have at least one answer (equal weight per cluster).
function scoreMaturity(answers) {
  answers = answers || {};
  const clusterScores = MAT_CLUSTERS.map((c) => {
    const qs = MAT_QUESTIONS.filter((q) => q.cluster === c.id);
    const vals = qs.map((q) => answers[q.id]).filter((v) => typeof v === 'number');
    const avg = vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : 0;
    return { id: c.id, label: c.label, axis: c.axis, dot: c.dot, avg, answered: vals.length, total: qs.length };
  });
  const scored = clusterScores.filter((c) => c.answered > 0);
  const overall = scored.length ? scored.reduce((a, c) => a + c.avg, 0) / scored.length : 0;
  const answeredCount = Object.values(answers).filter((v) => typeof v === 'number').length;
  return { clusterScores, overall, overallLabel: levelLabelFor(overall), answeredCount, total: MAT_QUESTIONS.length };
}

// ── window.ASSESS — empty / seed, mirrors window.REG's role for the registry ──
const emptyAssessment = () => ({ maturity: {}, priorities: [], journey: {} });
// A realistic, mid-maturity answer set for the #customer (contracted) scenario, so
// a booth visitor lands on a rich radar instead of 11 blank questions.
const seedAssessment = () => ({
  maturity: {
    q1_1: 3, q1_2: 3,
    q2_1: 3, q2_2: 4, q2_3: 3,
    q3_1: 3, q3_2: 2,
    q4_1: 2, q4_2: 2,
    q5_1: 3, q5_2: 2,
  },
  priorities: ['risk_patient_safety', 'regulatory_compliance', 'monitoring_accountability'],
  journey: { 'arch-ai': 'well_underway' },
});

window.ASSESS = {
  MAT_CLUSTERS, MAT_QUESTIONS, MAT_LEVELS, GOV_PRIORITIES, JOURNEY_STAGES, TARGET_FRAMEWORK, MATURITY_PROVIDERS,
  scoreMaturity, levelLabelFor, empty: emptyAssessment, seed: seedAssessment,
};

// ── Maturity radar (hand-rolled SVG pentagon, no chart library) ──────────────
const MaturityRadar = ({ clusterScores, size = 230, labels = true }) => {
  const cx = size / 2;
  const cy = size / 2 - 2;
  const R = size * 0.32;
  const n = clusterScores.length;
  const angle = (i) => -Math.PI / 2 + (i * 2 * Math.PI) / n;
  const pt = (i, frac) => [cx + Math.cos(angle(i)) * R * frac, cy + Math.sin(angle(i)) * R * frac];
  const poly = (frac, i0) => clusterScores.map((c, i) => pt(i, i0 == null ? frac : c.avg / 5).join(',')).join(' ');
  const ringPoly = (ring) => clusterScores.map((_, i) => pt(i, ring / 5).join(',')).join(' ');
  const dataPoly = clusterScores.map((c, i) => pt(i, Math.max(c.avg, 0.001) / 5).join(',')).join(' ');
  const hasData = clusterScores.some((c) => c.avg > 0);

  return (
    <svg viewBox={`0 0 ${size} ${size}`} width="100%" style={{ display: 'block', overflow: labels ? 'visible' : 'hidden' }} role="img" aria-label="Maturity profile radar">
      {/* grid rings 1..5 */}
      {[1, 2, 3, 4, 5].map((ring) => (
        <polygon key={ring} points={ringPoly(ring)} fill="none" stroke="#e7e3d8" strokeWidth={ring === 5 ? 1.2 : 0.8} />
      ))}
      {/* axes */}
      {clusterScores.map((c, i) => {
        const [x, y] = pt(i, 1);
        return <line key={c.id} x1={cx} y1={cy} x2={x} y2={y} stroke="#e7e3d8" strokeWidth={0.8} />;
      })}
      {/* data polygon */}
      {hasData && <polygon points={dataPoly} fill="rgba(228,170,35,0.22)" stroke="var(--asher-gold)" strokeWidth={1.6} strokeLinejoin="round" />}
      {/* vertices */}
      {hasData && clusterScores.map((c, i) => {
        if (c.avg <= 0) return null;
        const [x, y] = pt(i, c.avg / 5);
        return <circle key={c.id} cx={x} cy={y} r={2.6} fill="var(--asher-gold-dark)" />;
      })}
      {/* axis labels (two-line) — omitted on small thumbnails where they'd overlap */}
      {labels && clusterScores.map((c, i) => {
        const [x, y] = pt(i, 1.16);
        const a = angle(i);
        const anchor = Math.abs(Math.cos(a)) < 0.34 ? 'middle' : Math.cos(a) > 0 ? 'start' : 'end';
        const lines = (c.axis || c.label).split('\n');
        return (
          <text key={c.id} x={x} y={y} textAnchor={anchor} fontSize="8.2" fill="var(--asher-grey-medium)" fontFamily="Roboto, sans-serif">
            {lines.map((ln, k) => <tspan key={k} x={x} dy={k === 0 ? (lines.length > 1 ? -3 : 3) : 9.5}>{ln}</tspan>)}
          </text>
        );
      })}
    </svg>
  );
};

// ── Maturity profile card (radar + overall + per-cluster legend) ─────────────
const MaturityProfileCard = ({ profile }) => (
  <div className="asher-card" style={{ padding: 16, position: 'sticky', top: 16 }}>
    <div className="eyebrow" style={{ fontSize: 10, marginBottom: 6 }}>Maturity profile</div>
    <MaturityRadar clusterScores={profile.clusterScores} />
    <div style={{ textAlign: 'center', margin: '6px 0 12px' }}>
      <div style={{ fontSize: 30, fontWeight: 700, fontFamily: 'Montserrat', color: 'var(--asher-gold-dark)', lineHeight: 1, fontFeatureSettings: '"tnum"' }}>
        {profile.overall > 0 ? profile.overall.toFixed(1) : '—'}<span style={{ fontSize: 14, color: 'var(--asher-grey-light)', fontWeight: 600 }}> / 5.0</span>
      </div>
      <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--asher-gold-dark)', marginTop: 4 }}>{profile.overall > 0 ? profile.overallLabel : 'Not started'}</div>
    </div>
    <div style={{ display: 'flex', flexDirection: 'column', gap: 6, paddingTop: 12, borderTop: '1px solid #f1efe8' }}>
      {profile.clusterScores.map((c) => (
        <div key={c.id} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 11.5 }}>
          <span style={{ width: 8, height: 8, borderRadius: '50%', background: c.dot, flexShrink: 0 }} />
          <span style={{ flex: 1, color: 'var(--asher-grey-dark)', lineHeight: 1.25 }}>{c.label}</span>
          <span style={{ fontWeight: 700, fontFamily: 'Montserrat', color: c.avg > 0 ? 'var(--asher-grey-dark)' : 'var(--asher-grey-light)', fontFeatureSettings: '"tnum"' }}>{c.avg.toFixed(1)}</span>
        </div>
      ))}
    </div>
  </div>
);

// ── The provider picker (which maturity model do you want to run?) ───────────
// Cards for each entry in MATURITY_PROVIDERS. The native Asher Enterprise model
// is the default and source of truth; embedded providers are previews. An
// unavailable provider renders as a "Coming soon" placeholder. When a provider
// matches the Step-1 target, it carries a subtle "Matches your target" hint.
const MaturityProviderPicker = ({ providers, active, onPick, target }) => (
  <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 10, marginBottom: 18, maxWidth: 1100 }}>
    {providers.map((p) => {
      const on = p.id === active;
      const matches = target && p.target === target;
      return (
        <button key={p.id} disabled={!p.available} onClick={() => p.available && onPick(p.id)} style={{
          textAlign: 'left', fontFamily: 'inherit', cursor: p.available ? 'pointer' : 'not-allowed',
          display: 'flex', gap: 11, alignItems: 'flex-start', padding: 14,
          border: on ? '2px solid var(--asher-gold)' : '1px solid #e3e0d8', borderRadius: 11,
          background: on ? 'linear-gradient(160deg,#fffdf6,#fbf4e2)' : '#fff', opacity: p.available ? 1 : 0.6,
        }}>
          <span style={{ width: 32, height: 32, borderRadius: 9, flexShrink: 0, display: 'grid', placeItems: 'center',
            background: on ? 'var(--asher-gold)' : 'var(--asher-cream)', color: on ? '#fff' : 'var(--asher-gold-dark)' }}>
            <Icon name={p.icon} size={16} />
          </span>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 7, flexWrap: 'wrap' }}>
              <span style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>{p.label}</span>
              {p.badge && p.available && <span className="chip" style={{ fontSize: 9 }}>{p.badge}</span>}
              {!p.available && <span className="chip" style={{ fontSize: 9 }}>Coming soon</span>}
              {p.kind === 'native' && <span className="chip chip-success" style={{ fontSize: 9 }}>Feeds your blueprint</span>}
              {matches && p.available && <span className="chip chip-info" style={{ fontSize: 9 }}>Matches your target</span>}
            </div>
            <div style={{ fontSize: 11.5, color: 'var(--asher-grey-medium)', marginTop: 4, lineHeight: 1.45 }}>{p.blurb}</div>
          </div>
        </button>
      );
    })}
  </div>
);

// ── Step · Capability Maturity ───────────────────────────────────────────────
const StepMaturity = ({ assessment, onChange, target }) => {
  const answers = assessment.maturity || {};
  const setAnswer = (qid, level) => onChange({ ...assessment, maturity: { ...answers, [qid]: level } });
  const profile = window.ASSESS.scoreMaturity(answers);
  const pct = Math.round((profile.answeredCount / profile.total) * 100);

  // Which maturity model is on screen. Always defaults to the native Asher
  // Enterprise capture — the source of truth — even when targeting ARCH-AI; the
  // ARCH-AI card just carries a "Matches your target" hint to invite the preview.
  const providers = window.ASSESS.MATURITY_PROVIDERS;
  const [provider, setProvider] = React.useState('asher');
  const active = providers.find((p) => p.id === provider) || providers[0];
  const embedUrl = active.embedKey ? ((window.DATA.EMBEDS || {})[active.embedKey] || null) : null;

  return (
    <div className="fade-up">
      <StepHead n={4} eyebrow="Capability Maturity Assessment" title="Where is your AI Governance focus today?"
        sub="Choose how you want to assess maturity, then answer across 5 governance capability areas. Your responses shape the blueprint's recommendations and phasing — they don't gate it, so answer what you can." />

      <MaturityProviderPicker providers={providers} active={provider} onPick={setProvider} target={target} />

      {provider === 'asher' && (
        /* progress — only meaningful for the native capture */
        <div className="asher-card" style={{ padding: '12px 16px', marginBottom: 18, maxWidth: 1100 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
            <div style={{ flex: 1, height: 7, background: '#efebe0', borderRadius: 999, overflow: 'hidden' }}>
              <div style={{ width: `${pct}%`, height: '100%', background: 'var(--asher-gold)', borderRadius: 999, transition: 'width 0.3s ease' }} />
            </div>
            <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--asher-grey-medium)', whiteSpace: 'nowrap', fontFeatureSettings: '"tnum"' }}>{profile.answeredCount} of {profile.total}</span>
          </div>
        </div>
      )}

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 300px', gap: 24, alignItems: 'flex-start' }}>
        {provider === 'asher' ? (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 26 }}>
            {window.ASSESS.MAT_CLUSTERS.map((cl) => {
              const qs = window.ASSESS.MAT_QUESTIONS.filter((q) => q.cluster === cl.id);
              return (
                <div key={cl.id}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 9, marginBottom: 12 }}>
                    <span style={{ width: 9, height: 9, borderRadius: '50%', background: cl.dot }} />
                    <h3 style={{ fontSize: 15, margin: 0 }}>{cl.label}</h3>
                    <span className="chip" style={{ fontSize: 9.5 }}>{qs.length} question{qs.length > 1 ? 's' : ''}</span>
                  </div>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
                    {qs.map((q) => <MaturityQuestion key={q.id} q={q} value={answers[q.id]} onPick={(lvl) => setAnswer(q.id, lvl)} />)}
                  </div>
                </div>
              );
            })}
          </div>
        ) : (
          /* An embedded provider preview. The native capture stays the source of
             truth — the radar on the right reflects it — so this never blanks
             the blueprint; it's a "here's how you'd really score it" showcase. */
          <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
            <div className="asher-card" style={{ display: 'flex', gap: 9, padding: '11px 14px', alignItems: 'flex-start', background: 'rgba(228,170,35,0.07)', borderColor: 'rgba(228,170,35,0.25)' }}>
              <Icon name="info" size={14} color="var(--asher-gold-dark)" style={{ flexShrink: 0, marginTop: 1 }} />
              <span style={{ fontSize: 12, color: 'var(--asher-grey-dark)', lineHeight: 1.5 }}>
                This is a live preview of <strong>{active.label}</strong>. Your blueprint is built from the <strong>Asher Enterprise Maturity</strong> capture, the radar on the right, so switch back to it any time to edit your answers.
              </span>
            </div>
            <ExternalAppFrame
              url={embedUrl}
              title={active.label}
              badge={active.badge}
              icon={active.icon}
              height={680}
              note="Live, networked preview of a standalone Asher FORGE demo. If it does not load, use Open in new tab." />
          </div>
        )}
        <MaturityProfileCard profile={profile} />
      </div>
    </div>
  );
};

const MaturityQuestion = ({ q, value, onPick }) => (
  <div className="asher-card" style={{ padding: 16 }}>
    <div style={{ display: 'flex', gap: 11, marginBottom: 12 }}>
      <span style={{ width: 26, height: 26, borderRadius: '50%', background: 'var(--asher-cream)', color: 'var(--asher-gold-dark)', display: 'grid', placeItems: 'center', fontSize: 10.5, fontWeight: 700, flexShrink: 0, fontFeatureSettings: '"tnum"' }}>{q.number}</span>
      <div>
        <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>{q.title}</div>
        <div style={{ fontSize: 12.5, color: 'var(--asher-grey-medium)', marginTop: 2, lineHeight: 1.45 }}>{q.subtitle}</div>
      </div>
    </div>
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
      {q.options.map((text, i) => {
        const lvl = i + 1;
        const on = value === lvl;
        const tag = MAT_LEVELS[i];
        const span = i === 4 ? { gridColumn: '1 / -1' } : {};
        return (
          <button key={i} onClick={() => onPick(lvl)} style={{
            textAlign: 'left', fontFamily: 'inherit', cursor: 'pointer', display: 'flex', gap: 10, alignItems: 'flex-start', padding: '11px 12px',
            border: on ? '2px solid var(--asher-gold)' : '1px solid #e3e0d8', borderRadius: 10, background: on ? 'linear-gradient(160deg,#fffdf6,#fbf4e2)' : '#fff', ...span,
          }}>
            <Icon name={on ? 'circleCheck' : 'circle'} size={16} color={on ? 'var(--asher-gold)' : '#cfcabd'} style={{ flexShrink: 0, marginTop: 1 }} />
            <span style={{ flex: 1 }}>
              <span style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 3 }}>
                <span style={{ fontSize: 10.5, fontWeight: 700, color: on ? 'var(--asher-gold-dark)' : 'var(--asher-grey-light)' }}>{String.fromCharCode(65 + i)}</span>
                <span style={{ fontSize: 10, color: 'var(--asher-grey-light)' }}>{tag.label}</span>
              </span>
              <span style={{ fontSize: 12, color: 'var(--asher-grey-dark)', lineHeight: 1.4 }}>{text}</span>
            </span>
          </button>
        );
      })}
    </div>
  </div>
);

// ── Step · Priorities & Compliance ───────────────────────────────────────────
const StepPriorities = ({ assessment, onChange, target }) => {
  const ranked = assessment.priorities || [];
  const journey = assessment.journey || {};
  const framework = window.ASSESS.TARGET_FRAMEWORK[target];

  const toggle = (id) => {
    if (ranked.includes(id)) onChange({ ...assessment, priorities: ranked.filter((x) => x !== id) });
    else if (ranked.length < 3) onChange({ ...assessment, priorities: [...ranked, id] });
  };
  const setJourney = (stage) => onChange({ ...assessment, journey: { ...journey, [target]: stage } });

  return (
    <div className="fade-up">
      <StepHead n={5} eyebrow="Priorities & Compliance" title="What matters most right now?"
        sub="Pick and rank your top three governance priorities. These weight how the blueprint phases its work — your top priority pulls related artifacts into the NOW lane." />

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 300px', gap: 24, alignItems: 'flex-start' }}>
        <div>
          <div className="eyebrow" style={{ fontSize: 10, marginBottom: 10 }}>Rank your top 3 · {ranked.length}/3 chosen</div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
            {window.ASSESS.GOV_PRIORITIES.map((p) => {
              const rank = ranked.indexOf(p.id);
              const on = rank >= 0;
              const full = !on && ranked.length >= 3;
              return (
                <button key={p.id} onClick={() => toggle(p.id)} disabled={full} style={{
                  textAlign: 'left', fontFamily: 'inherit', cursor: full ? 'not-allowed' : 'pointer', display: 'flex', gap: 11, alignItems: 'flex-start', padding: 14,
                  border: on ? '2px solid var(--asher-gold)' : '1px solid #e3e0d8', borderRadius: 11, background: on ? 'linear-gradient(160deg,#fffdf6,#fbf4e2)' : '#fff', opacity: full ? 0.55 : 1,
                }}>
                  <span style={{ width: 30, height: 30, borderRadius: 9, flexShrink: 0, display: 'grid', placeItems: 'center',
                    background: on ? 'var(--asher-gold)' : 'var(--asher-cream)', color: on ? '#fff' : 'var(--asher-gold-dark)', fontWeight: 700, fontSize: 13 }}>
                    {on ? rank + 1 : <Icon name={p.icon} size={15} />}
                  </span>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--asher-grey-dark)', lineHeight: 1.3 }}>{p.label}</div>
                    <div style={{ fontSize: 11.5, color: 'var(--asher-grey-medium)', marginTop: 3, lineHeight: 1.45 }}>{p.desc}</div>
                  </div>
                </button>
              );
            })}
          </div>

          {/* Compliance journey — keyed off the Step-1 target framework */}
          {framework && (
            <div className="asher-card" style={{ padding: 18, marginTop: 22 }}>
              <div className="eyebrow" style={{ fontSize: 10, marginBottom: 4 }}>Compliance</div>
              <h3 style={{ fontSize: 15, margin: '0 0 4px' }}>Where are you on the path to {framework}?</h3>
              <p style={{ fontSize: 12, color: 'var(--asher-grey-light)', margin: '0 0 14px', lineHeight: 1.5 }}>This is the target you chose in Step 1. Telling us where you are tunes how aggressively the blueprint phases the work.</p>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
                {window.ASSESS.JOURNEY_STAGES.map((s) => {
                  const on = journey[target] === s.value;
                  return (
                    <button key={s.value} onClick={() => setJourney(s.value)} title={s.desc} style={{
                      fontFamily: 'inherit', cursor: 'pointer', textAlign: 'left', padding: '9px 13px', borderRadius: 10,
                      border: on ? '2px solid var(--asher-gold)' : '1px solid #e0dccf', background: on ? 'var(--asher-cream)' : '#fff',
                    }}>
                      <div style={{ fontSize: 12.5, fontWeight: 600, color: on ? 'var(--asher-gold-dark)' : 'var(--asher-grey-dark)' }}>{s.label}</div>
                      <div style={{ fontSize: 10.5, color: 'var(--asher-grey-light)', marginTop: 1 }}>{s.desc}</div>
                    </button>
                  );
                })}
              </div>
            </div>
          )}
        </div>

        {/* right rail — ranked recap */}
        <div className="asher-card" style={{ padding: 16, position: 'sticky', top: 16 }}>
          <div className="eyebrow" style={{ fontSize: 10, marginBottom: 10 }}>Your priorities</div>
          {ranked.length === 0 ? (
            <div style={{ fontSize: 12, color: 'var(--asher-grey-light)', lineHeight: 1.5, padding: '8px 0' }}>Pick up to three. The order matters — #1 carries the most weight.</div>
          ) : (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
              {ranked.map((id, i) => {
                const p = window.ASSESS.GOV_PRIORITIES.find((x) => x.id === id);
                return (
                  <div key={id} style={{ display: 'flex', alignItems: 'center', gap: 9 }}>
                    <span style={{ width: 22, height: 22, borderRadius: '50%', background: 'var(--asher-gold)', color: '#fff', display: 'grid', placeItems: 'center', fontSize: 11, fontWeight: 700, flexShrink: 0 }}>{i + 1}</span>
                    <span style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--asher-grey-dark)', lineHeight: 1.25 }}>{p.label}</span>
                  </div>
                );
              })}
            </div>
          )}
          <div style={{ marginTop: 14, paddingTop: 13, borderTop: '1px solid #f1efe8', fontSize: 11, color: 'var(--asher-grey-medium)', lineHeight: 1.55, display: 'flex', gap: 7 }}>
            <Icon name="info" size={12} color="var(--asher-gold-dark)" style={{ flexShrink: 0, marginTop: 1 }} />
            <span>Priorities reshape the Now / Next / Later phasing of your blueprint — they never remove anything from the plan.</span>
          </div>
        </div>
      </div>
    </div>
  );
};

Object.assign(window, { MaturityRadar, MaturityProfileCard, MaturityProviderPicker, StepMaturity, StepPriorities });


/* ===== screen-wizard.jsx ===== */
/* global React, Icon, TopBar, PolicyBreadcrumb, RiskBadge, AskAsher, ExternalAppFrame */
// Stage A, the blueprint wizard. Reuses the WizardMode pattern from coreapp:
// target → scope/org levels → lightweight registry → brownfield gap → generate.
// Composes existing components conceptually (AssessmentWizard, GoalSelectionModal).

const WIZ_STEPS = [
  { id: 'target', label: 'Target', icon: 'target' },
  { id: 'scope', label: 'Scope', icon: 'sitemap' },
  { id: 'registry', label: 'AI Systems', icon: 'scan' },
  { id: 'maturity', label: 'Maturity', icon: 'brain' },
  { id: 'priorities', label: 'Priorities', icon: 'list' },
  { id: 'gap', label: 'Existing policies', icon: 'fileCheck' },
];

// ── Deriving Stage A content from the onboarding capture ──────────────────────
// Each offering the customer picked in onboarding becomes a governance area (an
// L3 boundary). Three radiology offerings map onto the rich scenario boundaries
// (so their named systems carry through); the rest get a sensible default.
const RISK_LABELS = { high: 'High-Assurance', moderate: 'Operational', sandbox: 'Sandboxed / Innovation' };
const OFFERING_META = {
  'In-patient Diagnostic Imaging': { risk: 'moderate', icon: 'sitemap', profile: 'Inpatient reads across wards and the ICU, integrated into the care-team workflow.' },
  'Interventional Radiology': { risk: 'high', icon: 'bolt', profile: 'Image-guided procedures with intra-procedural AI guidance and dose tracking.' },
  'Affiliated (owned) Free-Standing Imaging Centers': { risk: 'moderate', icon: 'building', profile: 'Owned outpatient sites; your policies and SOPs reach across every location.' },
  'In-patient services': { risk: 'moderate', icon: 'sitemap', profile: 'Inpatient care areas inheriting your program policies and SOPs.' },
  'Outpatient clinics': { risk: 'moderate', icon: 'sitemap', profile: 'Scheduled outpatient encounters with background efficiency and quality tools.' },
  'Affiliated / owned sites': { risk: 'moderate', icon: 'building', profile: 'Owned satellite sites; the same governance reaches across locations.' },
  'Research & innovation': { risk: 'sandbox', icon: 'flask', profile: 'De-identified experimentation with strict data-egress controls.' },
};
const wizSlug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
function nameToBoundary(name, D) {
  const known = D.ORG.boundaries.find((b) => b.name === name);
  if (known) return known;
  const meta = OFFERING_META[name] || { risk: 'moderate', icon: 'sitemap', profile: 'Governed AI in this area inherits your program policies and SOPs.' };
  return { id: 'g-' + wizSlug(name), name, risk: meta.risk, riskLabel: RISK_LABELS[meta.risk], profile: meta.profile, icon: meta.icon };
}
function boundariesFromSetup(setup, D) {
  const offers = (setup && setup.offers) || [];
  if (offers.length) return offers.map((n) => nameToBoundary(n, D));
  return D.ORG.boundaries; // default radiology trio when nothing was picked
}

const StageAWizard = ({ onComplete, onExit, setup, registry, onRegistryChange, assessment, onAssessmentChange }) => {
  const D = window.DATA;
  const S = setup || {};
  const boundaries = React.useMemo(() => boundariesFromSetup(S, D), []);
  const facilityName = S.facilityName || D.ORG.enterprise.name;
  const facilityKind = S.facilityKind || D.ORG.enterprise.kind;
  const nodeName = S.nodeName || D.ORG.serviceLines[0].name;
  const govName = S.govName || nodeName;
  const isTop = !!S.isTop;

  const [step, setStep] = React.useState(0);
  const [target, setTarget] = React.useState('arch-ai');
  const [areas, setAreas] = React.useState(boundaries.map((b) => b.id)); // all governance areas
  const [gapDone, setGapDone] = React.useState(false);
  const [generating, setGenerating] = React.useState(false);

  const canNext = step === 0 ? !!target : step === 1 ? areas.length > 0 : true;
  const last = step === WIZ_STEPS.length - 1;

  const next = () => {
    if (last) { setGenerating(true); setTimeout(onComplete, 1700); return; }
    setStep((s) => s + 1);
  };

  return (
    <div className="app-shell">
      <div className="app-body">
        <div className="section-title-bar" style={{ paddingBottom: 0 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
            <div>
              <h1 style={{ fontSize: 23 }}>Design your governance blueprint</h1>
            </div>
            <button className="btn btn-ghost btn-sm" onClick={onExit}><Icon name="close" size={13} /> Leave</button>
          </div>
          {/* Step indicator */}
          <div style={{ display: 'flex', gap: 4, marginTop: 16, borderTop: '1px solid #eef0eb', paddingTop: 12 }}>
            {WIZ_STEPS.map((s, i) => {
              const state = i < step ? 'done' : i === step ? 'current' : 'todo';
              return (
                <button key={s.id} onClick={() => i <= step && setStep(i)} style={{
                  display: 'flex', alignItems: 'center', gap: 8, padding: '8px 14px', borderRadius: 8, border: 'none',
                  background: state === 'current' ? 'var(--asher-cream)' : 'transparent', cursor: i <= step ? 'pointer' : 'default', fontFamily: 'inherit',
                }}>
                  <span style={{ width: 24, height: 24, borderRadius: '50%', display: 'grid', placeItems: 'center', flexShrink: 0,
                    background: state === 'done' ? 'var(--asher-gold)' : state === 'current' ? '#fff' : '#ecebe6',
                    border: state === 'current' ? '2px solid var(--asher-gold)' : 'none',
                    color: state === 'done' ? '#fff' : state === 'current' ? 'var(--asher-gold-dark)' : 'var(--asher-grey-light)', fontSize: 11, fontWeight: 700 }}>
                    {state === 'done' ? <Icon name="check" size={12} /> : i + 1}
                  </span>
                  <span style={{ fontSize: 12.5, fontWeight: state === 'current' ? 700 : 500, color: state === 'todo' ? 'var(--asher-grey-light)' : 'var(--asher-grey-dark)' }}>{s.label}</span>
                </button>
              );
            })}
          </div>
        </div>

        {/* Context banner */}
        <div style={{ background: 'rgba(228,170,35,0.07)', borderBottom: '1px solid rgba(228,170,35,0.18)', padding: '10px 36px' }}>
          <div style={{ maxWidth: 1100, margin: '0 auto', display: 'flex', alignItems: 'center', gap: 9, fontSize: 13, color: 'var(--asher-grey-dark)' }}>
            <Icon name="compass" size={15} color="var(--asher-gold-dark)" />
            <span><strong>You're designing the {govName} blueprint{isTop ? '' : ` for ${facilityName}`}.</strong> This stays free, the plan you see at the end is yours to keep.</span>
          </div>
        </div>

        {generating ? <Generating /> : (
          <div style={{ maxWidth: 1100, margin: '0 auto', width: '100%', padding: '28px 36px 24px' }}>
            {step === 0 && <StepTarget target={target} setTarget={setTarget} />}
            {step === 1 && <StepScope areas={areas} setAreas={setAreas} boundaries={boundaries} facilityName={facilityName} facilityKind={facilityKind} nodeName={nodeName} govName={govName} isTop={isTop} />}
            {step === 2 && <StepRegistryReview registry={registry} onChange={onRegistryChange} />}
            {step === 3 && <StepMaturity assessment={assessment} onChange={onAssessmentChange} target={target} />}
            {step === 4 && <StepPriorities assessment={assessment} onChange={onAssessmentChange} target={target} />}
            {step === 5 && <StepGap done={gapDone} setDone={setGapDone} />}
          </div>
        )}

        {/* Footer */}
        {!generating && (
          <div style={{ position: 'sticky', bottom: 0, background: '#fff', borderTop: '1px solid #eef0eb', padding: '14px 36px', display: 'flex', justifyContent: 'center' }}>
            <div style={{ maxWidth: 1100, width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <button className="btn btn-secondary" onClick={() => (step === 0 ? onExit() : setStep((s) => s - 1))}>
                <Icon name="chevronLeft" size={13} /> Back
              </button>
              <span style={{ fontSize: 12, color: 'var(--asher-grey-light)' }}>Step {step + 1} of {WIZ_STEPS.length}{last ? ' · optional' : ''}</span>
              <button className="btn btn-primary" onClick={next} disabled={!canNext}>
                {last ? <>Generate blueprint <Icon name="sparkles" size={14} /></> : <>Continue <Icon name="arrowRight" size={13} /></>}
              </button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

// ── Step 1 · Target (the leading question) ───────────────────────────────────
const StepTarget = ({ target, setTarget }) => {
  const T = window.DATA.TARGET;
  const options = [
    { id: 'arch-ai', name: 'ACR ARCH-AI Accreditation', body: 'American College of Radiology', meta: '3–6 months · 10 requirements · imaging', available: true },
    { id: 'nist', name: 'NIST AI RMF, GOVERN function', body: 'NIST AI Risk Management Framework', meta: 'Choose functions', available: false },
    { id: 'jc', name: 'Responsible Use of AI in Healthcare', body: 'The Joint Commission', meta: 'Enterprise standard', available: false },
    { id: 'iso', name: 'ISO/IEC 42001, full', body: 'AI Management System', meta: '6–12 months', available: false },
    { id: 'baseline', name: 'Defensible baseline', body: 'No specific accreditation, sensible default', meta: 'Lightweight', available: true },
  ];
  return (
    <div className="fade-up">
      <StepHead n={1} eyebrow="The leading question" title="What are you trying to become?"
        sub="Your target orients the whole blueprint and drives which standard-specific methods Stage B pulls in. Set per blueprint, you can make a lighter one later." />
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, maxWidth: 760 }}>
        {options.map((o) => {
          const sel = target === o.id;
          return (
            <button key={o.id} disabled={!o.available} onClick={() => setTarget(o.id)} style={{
              textAlign: 'left', cursor: o.available ? 'pointer' : 'not-allowed', fontFamily: 'inherit',
              border: sel ? '2px solid var(--asher-gold)' : '1px solid #e3e0d8', borderRadius: 11, padding: 16,
              background: sel ? 'linear-gradient(160deg,#fffdf6,#fbf4e2)' : '#fff', opacity: o.available ? 1 : 0.6,
              display: 'flex', gap: 12, alignItems: 'flex-start',
            }}>
              <Icon name={sel ? 'circleCheck' : 'circle'} size={18} color={sel ? 'var(--asher-gold)' : '#cfcabd'} style={{ marginTop: 1, flexShrink: 0 }} />
              <div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                  <span style={{ fontSize: 14, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>{o.name}</span>
                  <span className={o.available ? 'chip chip-success' : 'chip'} style={{ fontSize: 9.5 }}>{o.available ? 'Available' : 'Coming soon'}</span>
                </div>
                <div style={{ fontSize: 12, color: 'var(--asher-grey-medium)', margin: '4px 0' }}>{o.body}</div>
                <div style={{ fontSize: 11, color: 'var(--asher-grey-light)' }}>{o.meta}</div>
              </div>
            </button>
          );
        })}
      </div>
    </div>
  );
};

// ── Step 2 · Scope & organizational levels ───────────────────────────────────
const StepScope = ({ areas, setAreas, boundaries, facilityName, facilityKind, nodeName, govName, isTop }) => {
  const toggle = (id) => setAreas((a) => (a.includes(id) ? a.filter((x) => x !== id) : [...a, id]));
  return (
    <div className="fade-up">
      <StepHead n={2} eyebrow="Scope & organizational levels" title="Which parts of your organization will you govern?"
        sub={isTop
          ? `Pick the governance areas you'll author over for ${govName}. You're the top of your own organization, so you set policy for all of it — nothing flows down from above.`
          : `Pick the governance areas you'll author over. ${facilityName} enterprise policy flows down to each; you go to section-level depth where the AI and the risk justify it.`} />
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 22, alignItems: 'flex-start' }}>
        <div>
          {/* L1 enterprise / top of org */}
          <div className="asher-card" style={{ padding: 16, marginBottom: 12, display: 'flex', alignItems: 'center', gap: 12 }}>
            <span style={{ width: 38, height: 38, borderRadius: 10, background: 'var(--asher-grey-dark)', display: 'grid', placeItems: 'center', color: 'var(--asher-gold)' }}><Icon name="building" size={18} /></span>
            <div style={{ flex: 1 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <span style={{ fontSize: 14, fontWeight: 700 }}>{isTop ? govName : facilityName}</span>
                <span className="chip" style={{ fontSize: 9.5 }}>{isTop ? 'Top of your organization' : 'Level 1 · Enterprise'}</span>
              </div>
              <div style={{ fontSize: 11.5, color: 'var(--asher-grey-light)' }}>{isTop ? 'You set policy for all of it · nothing flows down from above' : `${facilityKind} · enterprise AI policy flows down`}</div>
            </div>
          </div>
          {/* L2 service line / node (part-of only) */}
          {!isTop && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '4px 0 10px 18px', fontSize: 12, color: 'var(--asher-grey-medium)' }}>
              <Icon name="sitemap" size={13} color="var(--asher-gold-dark)" /> <strong>{nodeName}</strong> · Level 2 · the area you're scoping
            </div>
          )}
          {/* L3 boundaries (governance areas) — derived from onboarding offerings */}
          <div style={{ paddingLeft: isTop ? 0 : 18, display: 'flex', flexDirection: 'column', gap: 10 }}>
            {boundaries.map((b) => {
              const on = areas.includes(b.id);
              return (
                <button key={b.id} onClick={() => toggle(b.id)} style={{
                  textAlign: 'left', cursor: 'pointer', fontFamily: 'inherit', display: 'flex', gap: 13, alignItems: 'flex-start',
                  border: on ? '2px solid var(--asher-gold)' : '1px solid #e3e0d8', borderRadius: 11, padding: 15, background: on ? '#fffdf6' : '#fff',
                }}>
                  <Icon name={on ? 'circleCheck' : 'circle'} size={18} color={on ? 'var(--asher-gold)' : '#cfcabd'} style={{ marginTop: 1, flexShrink: 0 }} />
                  <span style={{ width: 34, height: 34, borderRadius: 9, background: 'var(--asher-cream)', display: 'grid', placeItems: 'center', color: 'var(--asher-gold-dark)', flexShrink: 0 }}><Icon name={b.icon} size={16} /></span>
                  <div style={{ flex: 1 }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                      <span style={{ fontSize: 13.5, fontWeight: 600 }}>{b.name}</span>
                      <span className="chip" style={{ fontSize: 9.5 }}>Level 3 · governance area</span>
                      <RiskBadge risk={b.risk} size="sm" />
                    </div>
                    <div style={{ fontSize: 11.5, color: 'var(--asher-grey-medium)', marginTop: 3 }}>{b.profile}</div>
                  </div>
                </button>
              );
            })}
          </div>
        </div>
        <div className="asher-card" style={{ padding: 16, position: 'sticky', top: 20 }}>
          <div className="eyebrow" style={{ fontSize: 10, marginBottom: 10 }}>Your selection</div>
          <div style={{ fontSize: 34, fontWeight: 700, fontFamily: 'Montserrat', color: 'var(--asher-gold-dark)', lineHeight: 1 }}>{areas.length}</div>
          <div style={{ fontSize: 12, color: 'var(--asher-grey-medium)', marginBottom: 14 }}>of {boundaries.length} governance areas</div>
          <div style={{ fontSize: 11.5, color: 'var(--asher-grey-medium)', lineHeight: 1.6, padding: 12, background: 'var(--asher-grey-bg)', borderRadius: 9 }}>
            <Icon name="info" size={12} color="var(--asher-gold-dark)" /> Each counts as one toward your licensed scope. Your governance can never exceed what you license{isTop ? '.' : `, enterprise policy still flows down to all of ${nodeName} regardless.`}
          </div>
        </div>
      </div>
    </div>
  );
};

// ── Step 3 · AI System Registry (the full confirm-the-list experience) ───────
// Replaces the old lightweight "count + chips" placeholder. Reuses the standalone
// registry's parts (window.RegistryParts) and catalog (window.REG), embedded inside
// the wizard chrome — no separate TopBar / banner / footer / Tweaks. The capture
// reads/writes the shared registry on App via {registry, onChange}, so what you
// enter here flows to the review and the standing registry. Seeding helpers and
// the entry shape live in window.REG (registry/data.js).

const RegSegmented = ({ label, value, onChange, options }) => (
  <div style={{ display: 'inline-flex', alignItems: 'center', gap: 7 }}>
    <span style={{ fontSize: 11, color: 'var(--asher-grey-light)' }}>{label}</span>
    <div style={{ display: 'inline-flex', background: '#f1efe8', borderRadius: 8, padding: 3 }}>
      {options.map(([id, lbl]) => (
        <button key={id} onClick={(e) => { e.stopPropagation(); onChange(id); }} style={{
          fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600, cursor: 'pointer', border: 'none', borderRadius: 6, padding: '5px 11px',
          background: value === id ? '#fff' : 'transparent', color: value === id ? 'var(--asher-grey-dark)' : 'var(--asher-grey-light)',
          boxShadow: value === id ? '0 1px 2px rgba(0,0,0,0.08)' : 'none',
        }}>{lbl}</button>
      ))}
    </div>
  </div>
);

const REG_RUNGS = [
  { icon: 'layers', title: 'Deepen this registry', body: 'The full record per system — model & version, FDA clearance, intended use, validation evidence, and deployment details.', tag: 'Subscription' },
  { icon: 'upload', title: 'Import a list', body: 'Bulk-populate from a CSV, JSON, or Excel export of your AI inventory.', tag: 'Subscription' },
  { icon: 'scan', title: 'Run discovery', body: 'Auto-detect systems from PACS / DICOM and your network.', tag: 'Roadmap' },
];

const StepRegistryFull = ({ registry, onChange }) => {
  const REG = window.REG;
  const { OFFERINGS, SUBSPECIALTIES, CATEGORIES, CATEGORY_META, CATALOG, newEntry } = REG;
  const { GroupSection } = window.RegistryParts;

  // The registry (in-scope services, included systems, customs, projection) lives
  // on App and arrives via props; this component only owns ephemeral UI state.
  const { inScope, included, customs, projected } = registry;
  const update = (partial) => onChange({ ...registry, ...partial });

  const [lens, setLens] = React.useState('offering');
  const [openId, setOpenId] = React.useState(null);
  const [adding, setAdding] = React.useState(false);
  const [draftName, setDraftName] = React.useState('');
  const allSystems = [...CATALOG, ...customs];

  const toggleOffering = (id) => {
    const on = inScope.includes(id);
    const nextScope = on ? inScope.filter((x) => x !== id) : [...inScope, id];
    const nextIncluded = { ...included };
    if (on) CATALOG.filter((s) => s.offering === id).forEach((s) => delete nextIncluded[s.id]);
    else CATALOG.filter((s) => s.offering === id && s.common).forEach((s) => { if (!nextIncluded[s.id]) nextIncluded[s.id] = newEntry(s, true); });
    update({ inScope: nextScope, included: nextIncluded });
  };
  const addSystem = (s) => update({ included: { ...included, [s.id]: newEntry(s, true) } });
  const removeSystem = (id) => { const n = { ...included }; delete n[id]; update({ included: n }); };
  const setEntry = (id, e) => update({ included: { ...included, [id]: e } });
  const setProjected = (v) => update({ projected: v });
  const addCustom = () => {
    const name = draftName.trim(); if (!name) return;
    const id = 'custom-' + (customs.length + 1) + '-' + name.replace(/\W+/g, '').slice(0, 10).toLowerCase();
    const sys = { id, name, modality: '—', task: '', offering: 'custom', sub: 'custom', vendors: [], tier: 'moderate', autonomous: false, effort: 8, category: 'clinical', aitype: 'predictive', custom: true };
    update({ customs: [...customs, sys], included: { ...included, [id]: newEntry(sys, false) } });
    setDraftName(''); setAdding(false);
  };

  const includedSystems = allSystems.filter((s) => included[s.id]);
  const count = includedSystems.length;
  const effort = includedSystems.reduce((tot, s) => tot + (s.effort || 0), 0);
  const byCat = { clinical: 0, operational: 0, administrative: 0 };
  includedSystems.forEach((s) => { const c = included[s.id].category; byCat[c] = (byCat[c] || 0) + 1; });
  const needDeploy = includedSystems.filter((s) => !included[s.id].deploy).length;

  let groups;
  if (lens === 'offering') {
    groups = OFFERINGS.filter((o) => inScope.includes(o.id)).map((o) => ({ key: o.id, label: o.label, systems: CATALOG.filter((s) => s.offering === o.id) }));
  } else {
    groups = SUBSPECIALTIES.map((sp) => ({ key: sp.id, label: sp.label, systems: CATALOG.filter((s) => inScope.includes(s.offering) && s.sub === sp.id) })).filter((g) => g.systems.length);
  }
  if (customs.length) groups.push({ key: 'custom', label: 'Added manually', systems: customs });

  return (
    <div className="fade-up" onClick={() => setOpenId(null)}>
      <StepHead n={3} eyebrow="AI System Registry" title="What AI are you running?"
        sub="Suggestions come from Asher's clinical-AI ontology for the services you offer — add the ones you actually run. Only deployment is needed now; vendor and the deeper record come as you operationalize." />
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 24, alignItems: 'flex-start' }}>
        {/* LEFT — scope bar + grouped systems */}
        <div>
          <div className="asher-card" style={{ padding: 14, marginBottom: 18 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 10, flexWrap: 'wrap' }}>
              <span className="eyebrow" style={{ fontSize: 10 }}>Your services</span>
              <span style={{ flex: 1 }} />
              <RegSegmented label="Group by" value={lens} onChange={setLens} options={[['offering', 'By service'], ['subspecialty', 'By subspecialty']]} />
            </div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7 }}>
              {OFFERINGS.map((o) => {
                const on = inScope.includes(o.id);
                return (
                  <button key={o.id} onClick={(e) => { e.stopPropagation(); toggleOffering(o.id); }} style={{
                    display: 'inline-flex', alignItems: 'center', gap: 7, fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600, cursor: 'pointer',
                    padding: '6px 12px', borderRadius: 999, border: on ? '1px solid var(--asher-gold)' : '1px dashed #d8d2c2',
                    background: on ? 'var(--asher-cream)' : '#fff', color: on ? 'var(--asher-gold-dark)' : 'var(--asher-grey-light)',
                  }}>
                    <Icon name={on ? 'circleCheck' : 'plus'} size={13} color={on ? 'var(--asher-gold-dark)' : '#bcb6a8'} />
                    {o.label}
                  </button>
                );
              })}
            </div>
            <div style={{ fontSize: 11, color: 'var(--asher-grey-light)', marginTop: 9, display: 'flex', gap: 7, alignItems: 'flex-start', lineHeight: 1.5 }}>
              <Icon name="info" size={12} color="var(--asher-gold-dark)" style={{ flexShrink: 0, marginTop: 1 }} />
              <span>These came from your setup — they organize the suggestions below. Add or remove a service to refocus the registry.</span>
            </div>
          </div>

          {groups.length === 0 ? (
            <div style={{ padding: '50px 20px', textAlign: 'center', color: 'var(--asher-grey-light)', fontSize: 13 }}>
              <Icon name="scan" size={26} color="#cfcabd" /><div style={{ marginTop: 10 }}>Turn on a service above, or add a system, to start your registry.</div>
            </div>
          ) : groups.map((g) => (
            <GroupSection key={g.key} label={g.label} systems={g.systems}
              included={included} openId={openId} setOpenId={setOpenId} compact={false}
              guided={false} requireDeploy={true} vendorNudge={true}
              onAdd={addSystem} onRemoveCard={removeSystem} onSetEntry={setEntry} />
          ))}

          <div style={{ marginTop: 4 }}>
            {!adding ? (
              <button onClick={(e) => { e.stopPropagation(); setAdding(true); }} style={{
                width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 9, fontFamily: 'inherit', cursor: 'pointer',
                padding: '14px', borderRadius: 11, border: '1px dashed #d4cdba', background: '#fbfaf6', color: 'var(--asher-grey-medium)', fontSize: 12.5, fontWeight: 600,
              }}>
                <Icon name="plus" size={15} color="var(--asher-gold-dark)" /> Add a system that isn't listed
              </button>
            ) : (
              <div className="asher-card fade-up" style={{ padding: 14, display: 'flex', gap: 9, alignItems: 'center' }} onClick={(e) => e.stopPropagation()}>
                <Icon name="scan" size={16} color="var(--asher-gold-dark)" />
                <input autoFocus value={draftName} onChange={(e) => setDraftName(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && addCustom()}
                  placeholder="Name it, e.g. “Rib fracture detection”" style={{ flex: 1, fontSize: 13, padding: '9px 12px', borderRadius: 8, border: '1px solid #d9d4c6', outline: 'none' }} />
                <button className="btn btn-secondary btn-sm" onClick={() => { setAdding(false); setDraftName(''); }}>Cancel</button>
                <button className="btn btn-primary btn-sm" onClick={addCustom}>Add</button>
              </div>
            )}
          </div>
        </div>

        {/* RIGHT — live summary + upsell */}
        <div style={{ position: 'sticky', top: 16, display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div className="asher-card" style={{ padding: 16 }} onClick={(e) => e.stopPropagation()}>
            <div className="eyebrow" style={{ fontSize: 10, marginBottom: 8 }}>Your registry</div>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
              <span style={{ fontSize: 40, fontWeight: 700, fontFamily: 'Montserrat', color: 'var(--asher-gold-dark)', lineHeight: 1, fontFeatureSettings: '"tnum"' }}>{count}</span>
              <span style={{ fontSize: 13, color: 'var(--asher-grey-medium)' }}>systems today</span>
            </div>
            <div style={{ display: 'flex', gap: 6, marginTop: 12, flexWrap: 'wrap' }}>
              {CATEGORIES.map((c) => byCat[c.id] ? (
                <span key={c.id} className="chip" style={{ fontSize: 10.5, background: CATEGORY_META[c.id].bg, color: CATEGORY_META[c.id].fg }}>{byCat[c.id]} {c.label}</span>
              ) : null)}
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 14, paddingTop: 13, borderTop: '1px solid #f1efe8' }}>
              <span style={{ fontSize: 12, color: 'var(--asher-grey-medium)' }}>Projected in 3 yrs</span>
              <input value={projected} onChange={(e) => setProjected(e.target.value.replace(/\D/g, '') || 0)}
                style={{ width: 56, textAlign: 'right', fontSize: 18, fontWeight: 700, fontFamily: 'Montserrat', color: 'var(--asher-grey-dark)', border: 'none', borderBottom: '2px solid var(--asher-cream)', outline: 'none' }} />
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 9 }}>
              <span style={{ fontSize: 12, color: 'var(--asher-grey-medium)' }}>Work products seeded</span>
              <span style={{ fontSize: 14, fontWeight: 700, fontFamily: 'Montserrat', color: 'var(--asher-grey-dark)', fontFeatureSettings: '"tnum"' }}>{effort}</span>
            </div>
            <div style={{ marginTop: 13, padding: '10px 12px', borderRadius: 9, fontSize: 11.5, lineHeight: 1.5, display: 'flex', gap: 8, alignItems: 'flex-start',
              background: needDeploy ? 'rgba(228,170,35,0.10)' : 'var(--asher-success-bg)', color: needDeploy ? 'var(--asher-gold-dark)' : 'var(--asher-success-fg)' }}>
              <Icon name={needDeploy ? 'warning' : 'circleCheck'} size={13} style={{ flexShrink: 0, marginTop: 1 }} />
              <span>{needDeploy ? <><strong>{needDeploy} {needDeploy === 1 ? 'system needs' : 'systems need'} a deployment.</strong> It's the one thing we need now.</> : <>Every system has a deployment. Vendor &amp; the deeper record can come later.</>}</span>
            </div>
          </div>

          <div className="asher-card" style={{ padding: 15 }} onClick={(e) => e.stopPropagation()}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 11 }}>
              <Icon name="rocket" size={15} color="var(--asher-gold-dark)" />
              <span style={{ fontFamily: 'Montserrat', fontSize: 13, fontWeight: 700, color: 'var(--asher-grey-dark)' }}>Grow this registry</span>
              <span className="chip chip-locked" style={{ fontSize: 9, marginLeft: 'auto' }}><Icon name="lock" size={8} /> Later</span>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              {REG_RUNGS.map((r) => (
                <div key={r.title} style={{ display: 'flex', gap: 10 }}>
                  <span style={{ width: 30, height: 30, borderRadius: 8, background: 'var(--asher-cream)', display: 'grid', placeItems: 'center', color: 'var(--asher-gold-dark)', flexShrink: 0 }}><Icon name={r.icon} size={15} /></span>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--asher-grey-dark)', lineHeight: 1.25 }}>{r.title}
                      <span className="chip" style={{ fontSize: 8.5, marginLeft: 6, verticalAlign: 'middle', background: r.tag === 'Roadmap' ? '#e8eded' : '#ece9e1', color: 'var(--asher-grey-medium)' }}>{r.tag}</span>
                    </div>
                    <p style={{ fontSize: 11, color: 'var(--asher-grey-light)', margin: '4px 0 0', lineHeight: 1.5 }}>{r.body}</p>
                  </div>
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

// ── Guided registry setup (capture) used OUTSIDE the wizard ──────────────────
// Wraps the StepRegistryFull capture in its own page chrome + a Done CTA, so the
// "what AI are you running?" capture can run as onboarding Step 3 (build flow) and
// as the standing registry's "Open guided setup" re-edit. buildFlow adds the slim
// You ✓ · Organization ✓ · AI systems rail so the setup sequence reads continuous.
const GuidedRegistrySetup = ({ registry, onChange, onDone, onBack, doneLabel = 'Continue', note, buildFlow = false }) => {
  const dot = (done, label, n) => (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontWeight: 700, color: done ? 'var(--asher-gold-dark)' : 'var(--asher-grey-dark)' }}>
      <span style={{ width: 20, height: 20, borderRadius: '50%', background: done ? 'var(--asher-gold)' : 'var(--asher-grey-dark)', color: '#fff', display: 'grid', placeItems: 'center' }}>
        {done ? <Icon name="check" size={11} /> : n}
      </span>
      {label}
    </span>
  );
  return (
    <div className="app-shell">
      <div className="app-body">
        <div style={{ padding: '24px 36px 40px', maxWidth: 1180, margin: '0 auto', width: '100%' }}>
          {buildFlow && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap', marginBottom: 18, fontSize: 12.5 }}>
              {dot(true, 'You')}
              <Icon name="chevronRight" size={12} color="#cfcabb" />
              {dot(true, 'Organization')}
              <Icon name="chevronRight" size={12} color="#cfcabb" />
              {dot(false, 'AI systems', 3)}
            </div>
          )}
          <StepRegistryFull registry={registry} onChange={onChange} />
          <div className="asher-card" style={{ marginTop: 8, padding: 18, display: 'flex', alignItems: 'center', gap: 16, background: 'linear-gradient(135deg,#fffdf6,#fbf4e2)', border: '1px solid rgba(228,170,35,0.3)' }}>
            <div style={{ flex: 1 }}>
              <h3 style={{ fontSize: 15, margin: '0 0 4px' }}>{buildFlow ? "That's enough to get started" : 'Done editing your registry'}</h3>
              <p style={{ fontSize: 12.5, color: 'var(--asher-grey-medium)', margin: 0, lineHeight: 1.5 }}>{note}</p>
            </div>
            {onBack && <button className="btn btn-ghost" onClick={onBack}><Icon name="chevronLeft" size={13} /> Back</button>}
            <button className="btn btn-primary" onClick={onDone}>{doneLabel} <Icon name="arrowRight" size={12} /></button>
          </div>
        </div>
      </div>
    </div>
  );
};

// ── Stage A · Step 3 · Review (read-only) ────────────────────────────────────
// The registry was captured during setup, so in the blueprint wizard this step is
// a review/confirm, not a create. Reuses the standing registry's read-only body.
const StepRegistryReview = ({ registry, onChange }) => {
  const RegistryReviewBody = window.RegistryReviewBody;
  const [editing, setEditing] = React.useState(false);

  // Edit-in-place: the same guided capture (StepRegistryFull) the sidebar registry
  // opens, embedded here so you don't leave the wizard. Writes to the lifted registry.
  if (editing && onChange) {
    return (
      <div className="fade-up">
        <StepRegistryFull registry={registry} onChange={onChange} />
        <div className="asher-card" style={{ marginTop: 8, padding: 16, display: 'flex', alignItems: 'center', gap: 16, background: 'linear-gradient(135deg,#fffdf6,#fbf4e2)', border: '1px solid rgba(228,170,35,0.3)' }}>
          <div style={{ flex: 1 }}>
            <h3 style={{ fontSize: 14.5, margin: '0 0 3px' }}>Done editing your systems</h3>
            <p style={{ fontSize: 12, color: 'var(--asher-grey-medium)', margin: 0, lineHeight: 1.5 }}>Changes save to your AI System Registry and re-tune any blueprint that draws on it.</p>
          </div>
          <button className="btn btn-primary" onClick={() => setEditing(false)}><Icon name="check" size={13} /> Back to review</button>
        </div>
      </div>
    );
  }

  return (
    <div className="fade-up">
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16, maxWidth: 980 }}>
        <StepHead n={3} eyebrow="AI System Registry" title="Confirm your AI systems for this blueprint"
          sub="These are the systems in scope for this blueprint. Review them, and edit if anything's changed — you don't re-enter anything. This is the same registry you manage from Organizational Profile." />
        {onChange && (
          <button className="btn btn-secondary btn-sm" style={{ flexShrink: 0, marginTop: 4 }} onClick={() => setEditing(true)}>
            <Icon name="sliders" size={13} /> Edit systems
          </button>
        )}
      </div>
      <div style={{ maxWidth: 980 }}>
        {RegistryReviewBody ? <RegistryReviewBody registry={registry} /> : null}
      </div>
    </div>
  );
};

// ── Step 4 · Brownfield gap (optional taste) ─────────────────────────────────
const StepGap = ({ done, setDone }) => {
  // Reveals the embedded Policy Ingestionator — the live gap-analysis report
  // (PTMC's draft policy vs. the CHAR reference model, with the "view uploaded
  // artifact" source-doc viewer). The findings card above is the in-context
  // teaser; this is the full standalone report.
  const [showReport, setShowReport] = React.useState(false);
  const ingestUrl = (window.DATA.EMBEDS || {}).policyIngestionator;
  const rows = [
    { name: 'AI Governance Policy', rec: 'Augment', note: 'Your draft covers structure but not autonomous-tool oversight.' },
    { name: 'AI Risk Management Policy', rec: 'Augment', note: 'Strong base, add ARCH-AI hazard-analysis requirements.' },
    { name: 'Model Monitoring Policy', rec: 'Write new', note: 'No equivalent found in your document.' },
    { name: 'Human Oversight Policy', rec: 'Write new', note: 'Escalation authority for autonomous tools is missing.' },
    { name: 'Data Governance Policy', rec: 'Align', note: 'Existing HIPAA policy maps cleanly, minor additions.' },
  ];
  return (
    <div className="fade-up">
      <StepHead n={6} eyebrow="Brownfield gap assessment · optional" title="Already have policies in place?"
        sub="Upload one document and we'll show the gaps against CHAR and ARCH-AI, with augment / replace / write-from-scratch recommendations. One document is free." />
      {!done ? (
        <button onClick={() => setDone(true)} style={{
          width: '100%', maxWidth: 720, border: '2px dashed #d4cfbf', borderRadius: 14, padding: '40px 24px', background: '#fdfcf8',
          cursor: 'pointer', fontFamily: 'inherit', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 10,
        }}>
          <span style={{ width: 52, height: 52, borderRadius: 13, background: 'var(--asher-cream)', display: 'grid', placeItems: 'center', color: 'var(--asher-gold-dark)' }}><Icon name="upload" size={24} /></span>
          <div style={{ fontSize: 15, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>Drop a policy document, or click to browse</div>
          <div style={{ fontSize: 12, color: 'var(--asher-grey-light)' }}>PDF or DOCX · one document free · click to simulate</div>
        </button>
      ) : (
        <div className="fade-up" style={{ maxWidth: 760 }}>
          <div className="asher-card" style={{ padding: 0, overflow: 'hidden' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '13px 16px', borderBottom: '1px solid #eef0eb', background: 'var(--asher-success-bg)' }}>
              <Icon name="fileCheck" size={16} color="var(--asher-success-fg)" />
              <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--asher-success-fg)' }}>Assessed: PTMC-AI-Governance-Draft-v3.docx</span>
              <span className="chip chip-success" style={{ fontSize: 10, marginLeft: 'auto' }}>5 findings</span>
            </div>
            {rows.map((r, i) => (
              <div key={r.name} style={{ display: 'flex', gap: 12, padding: '12px 16px', borderTop: i ? '1px solid #f3f1ea' : 'none', alignItems: 'flex-start' }}>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 13, fontWeight: 600 }}>{r.name}</div>
                  <div style={{ fontSize: 11.5, color: 'var(--asher-grey-light)', marginTop: 2 }}>{r.note}</div>
                </div>
                <RecChip rec={r.rec} />
              </div>
            ))}
          </div>
          <div style={{ marginTop: 10, fontSize: 11.5, color: 'var(--asher-grey-light)' }}>These recommendations shape how each blueprint item is delivered. The full multi-document assessment is part of a subscription.</div>

          {/* Reveal the full standalone gap-analysis report (Policy Ingestionator). */}
          {ingestUrl && (
            <div style={{ marginTop: 16 }}>
              {!showReport ? (
                <button className="btn btn-secondary" onClick={() => setShowReport(true)}>
                  <Icon name="fileCheck" size={14} /> Open the full gap analysis report
                </button>
              ) : (
                <ExternalAppFrame
                  url={ingestUrl}
                  title="Policy gap analysis · PTMC-AI-Governance-Draft-v3.docx"
                  icon="fileCheck"
                  height={720}
                  note="Live, networked preview of the standalone Policy Ingestionator. Includes the parsed source document under 'view uploaded artifact'. If it does not load, use Open in new tab." />
              )}
            </div>
          )}
        </div>
      )}
    </div>
  );
};

const RecChip = ({ rec }) => {
  const map = { Augment: 'chip-warn', 'Write new': 'chip-info', Align: 'chip-success', Replace: 'chip' };
  return <span className={`chip ${map[rec] || 'chip'}`} style={{ fontSize: 10, flexShrink: 0 }}>{rec}</span>;
};

const NumField = ({ label, value, hint }) => (
  <div className="asher-card" style={{ padding: 16, textAlign: 'center' }}>
    <div className="eyebrow" style={{ fontSize: 10, marginBottom: 10 }}>{label}</div>
    <input defaultValue={value} style={{ width: '100%', textAlign: 'center', fontSize: 30, fontWeight: 700, fontFamily: 'Montserrat', color: 'var(--asher-grey-dark)', border: 'none', borderBottom: '2px solid var(--asher-cream)', outline: 'none', padding: '2px 0' }} />
    <div style={{ fontSize: 11, color: 'var(--asher-grey-light)', marginTop: 8 }}>{hint}</div>
  </div>
);

const StepHead = ({ n, eyebrow, title, sub }) => (
  <div style={{ marginBottom: 22, maxWidth: 720 }}>
    <div className="eyebrow" style={{ color: 'var(--asher-gold-dark)', marginBottom: 8 }}>{eyebrow}</div>
    <h2 style={{ fontSize: 24, margin: '0 0 8px', letterSpacing: '-0.01em' }}>{title}</h2>
    <p style={{ fontSize: 14, color: 'var(--asher-grey-medium)', lineHeight: 1.55, margin: 0 }}>{sub}</p>
  </div>
);

const Generating = () => (
  <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '80px 20px', minHeight: 400 }}>
    <div style={{ width: 56, height: 56, borderRadius: 15, background: 'var(--asher-gold)', display: 'grid', placeItems: 'center', color: '#fff', marginBottom: 20, animation: 'blink 1.2s infinite' }}>
      <Icon name="sparkles" size={26} />
    </div>
    <h2 style={{ fontSize: 22, margin: '0 0 8px' }}>Generating your blueprint…</h2>
    <p style={{ fontSize: 14, color: 'var(--asher-grey-medium)', textAlign: 'center', maxWidth: 440, lineHeight: 1.55 }}>
      Routing your 11 systems through the conditional-method engine and mapping to ARCH-AI's 10 requirements.
    </p>
  </div>
);

Object.assign(window, { StageAWizard, GuidedRegistrySetup, StepRegistryReview });


/* ===== screen-blueprint.jsx ===== */
/* global React, Icon, TopBar, PolicyBreadcrumb, RiskBadge, ModeBadge, AskAsher */
// HERO 1, the complete, free, never-gated blueprint preview.
// Matches the production BlueprintKanban mechanic: NOW / NEXT / LATER as tabs with
// count badges; each phase splits into Policies | Processes columns (KanbanPhase),
// cards expand one-at-a-time into a detail accordion; plus the ARCH-AI coverage
// ledger and the Ask Asher reasoning rail.

const PHASES = [
  { id: 'now', label: 'NOW', window: '0–30 days', tone: 'now' },
  { id: 'next', label: 'NEXT', window: '30–90 days', tone: 'next' },
  { id: 'later', label: 'LATER', window: '90+ days', tone: 'later' },
];

const BlueprintPreview = ({ assessment, onGate, onBack, onNav }) => {
  const { BLUEPRINT, TARGET, ARCH_REQS } = window.DATA;
  // The Stage A assessment (maturity + priorities) informs how the plan is phased.
  // Surfaced here as a read-only recap; shown only once there are answers to show.
  const A = assessment || {};
  const profile = window.ASSESS.scoreMaturity(A.maturity);
  const hasAssessment = profile.answeredCount > 0;
  const topPriorities = (A.priorities || []).map((id) => window.ASSESS.GOV_PRIORITIES.find((p) => p.id === id)).filter(Boolean);
  const [activeTab, setActiveTab] = React.useState('now'); // now | next | later (BlueprintKanban tabs)
  const [expanded, setExpanded] = React.useState(null);     // expandedCard (single open)

  const items = BLUEPRINT.items;
  const effort = items.reduce((t, i) => t + i.effort, 0);
  const policies = items.filter((i) => i.type === 'policy').length;
  const processes = items.length - policies;
  const freeCount = items.filter((i) => i.free).length;

  return (
    <div className="app-shell">
      <div className="app-body">
        <div className="section-title-bar" style={{ borderTop: '4px solid var(--asher-gold)' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16 }}>
            <div style={{ flex: 1 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
                <h1 style={{ margin: 0 }}>Your governance blueprint</h1>
                <span className="chip chip-gold" style={{ fontSize: 11 }}><Icon name="target" size={11} /> {TARGET.name}</span>
                <span className="chip chip-success" style={{ fontSize: 11 }}><Icon name="check" size={11} /> Free &amp; complete</span>
              </div>
              <p className="subtitle">The whole plan for PTMC Radiology, organized Now / Next / Later, sized to your real systems. Yours to keep.</p>
            </div>
            <div style={{ display: 'flex', gap: 8 }}>
              <button className="btn btn-secondary"><Icon name="download" size={13} /> Export</button>
              <button className="btn btn-primary" onClick={onGate}><Icon name="verified" size={14} /> Approve blueprint</button>
            </div>
          </div>
        </div>

        {/* The size of the plan, the motivation to subscribe. Shown in full. */}
        <div style={{ background: 'var(--asher-grey-dark)', color: '#fff', padding: '18px 36px' }}>
          <div style={{ maxWidth: 1280, margin: '0 auto', display: 'flex', alignItems: 'center', gap: 36, flexWrap: 'wrap' }}>
            <div style={{ marginRight: 'auto' }}>
              <div className="eyebrow" style={{ color: 'var(--asher-gold)', marginBottom: 4 }}>Your full ARCH-AI plan</div>
              <div style={{ fontSize: 15, color: '#d6dade' }}>Everything below is free to view. Building it is where a subscription comes in.</div>
            </div>
            {[[items.length, 'artifacts'], [effort, 'work products'], [`${policies}/${processes}`, 'policies / processes'], [3, 'governance areas'], ['10/10', 'ARCH-AI reqs covered']].map(([v, l]) => (
              <div key={l} style={{ textAlign: 'center' }}>
                <div style={{ fontSize: 30, fontWeight: 700, fontFamily: 'Montserrat', color: 'var(--asher-gold)', lineHeight: 1, fontFeatureSettings: '"tnum"' }}>{v}</div>
                <div style={{ fontSize: 10.5, color: '#aeb4ba', textTransform: 'uppercase', letterSpacing: '0.05em', marginTop: 5 }}>{l}</div>
              </div>
            ))}
          </div>
        </div>

        <div style={{ maxWidth: 1280, margin: '0 auto', width: '100%', padding: '22px 36px 40px', display: 'grid', gridTemplateColumns: '1fr 340px', gap: 22, alignItems: 'flex-start' }}>
          {/* LEFT — phase tabs (BlueprintKanban mechanic) */}
          <div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12.5, color: 'var(--asher-grey-medium)', marginBottom: 14 }}>
              <Icon name="info" size={13} color="var(--asher-gold-dark)" />
              Policies flow down the org (cheap); processes &amp; SOPs attach to your AI systems.
            </div>

            {/* TabList */}
            <div role="tablist" style={{ display: 'flex', gap: 2, borderBottom: '1px solid rgba(180,189,175,0.55)', marginBottom: 16 }}>
              {PHASES.map((ph) => {
                const count = items.filter((i) => i.phase === ph.id).length;
                const on = activeTab === ph.id;
                return (
                  <button key={ph.id} role="tab" aria-selected={on} onClick={() => { setActiveTab(ph.id); setExpanded(null); }} style={{
                    display: 'inline-flex', alignItems: 'center', gap: 9, fontFamily: 'inherit', cursor: 'pointer',
                    background: 'transparent', border: 'none', padding: '10px 16px', marginBottom: -1,
                    borderBottom: on ? '2px solid var(--asher-gold)' : '2px solid transparent',
                    color: on ? 'var(--asher-gold-dark)' : 'var(--asher-grey-medium)',
                    fontSize: 13, fontWeight: on ? 700 : 600, transition: 'color 0.2s ease',
                  }}>
                    <span>{ph.label} <span style={{ fontWeight: 500, fontSize: 11, color: on ? 'var(--asher-gold-dark)' : 'var(--asher-grey-light)' }}>{ph.window}</span></span>
                    <PhaseBadge tone={ph.tone} count={count} />
                  </button>
                );
              })}
            </div>

            {/* TabPanel — Policies | Processes columns (KanbanPhase mechanic), expand-on-click */}
            <div className="fade-up" key={activeTab} style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20, alignItems: 'flex-start' }}>
              <PhaseColumn kind="policy" items={items.filter((i) => i.phase === activeTab && i.type === 'policy')} expanded={expanded} onToggle={setExpanded} />
              <PhaseColumn kind="process" items={items.filter((i) => i.phase === activeTab && i.type === 'process')} expanded={expanded} onToggle={setExpanded} />
            </div>
          </div>

          {/* RIGHT, coverage ledger + Ask Asher */}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16, position: 'sticky', top: 16 }}>
            <div className="asher-card" style={{ padding: 16 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
                <Icon name="target" size={15} color="var(--asher-gold-dark)" />
                <span style={{ fontFamily: 'Montserrat', fontSize: 13.5, fontWeight: 700 }}>Path to ARCH-AI</span>
                <span style={{ marginLeft: 'auto', fontSize: 11, color: 'var(--asher-grey-light)' }}>10 requirements</span>
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
                {ARCH_REQS.map((r) => (
                  <div key={r.n} style={{ display: 'flex', alignItems: 'center', gap: 9, fontSize: 11.5 }}>
                    <span style={{ width: 18, height: 18, borderRadius: '50%', background: 'var(--asher-cream)', color: 'var(--asher-gold-dark)', display: 'grid', placeItems: 'center', fontSize: 9.5, fontWeight: 700, flexShrink: 0 }}>{r.n}</span>
                    <span style={{ flex: 1, color: 'var(--asher-grey-dark)' }}>{r.name}</span>
                    <span className={`chip chip-${r.status}`} style={{ fontSize: 9 }}>{r.status.toUpperCase()}</span>
                  </div>
                ))}
              </div>
              <div style={{ marginTop: 12, paddingTop: 12, borderTop: '1px solid #f1efe8', fontSize: 11.5, color: 'var(--asher-grey-medium)', lineHeight: 1.5 }}>
                All 10 requirements are addressed across your plan. <strong>4 land in your NOW lane.</strong>
              </div>
            </div>

            {hasAssessment && (
              <div className="asher-card" style={{ padding: 16 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
                  <Icon name="brain" size={15} color="var(--asher-gold-dark)" />
                  <span style={{ fontFamily: 'Montserrat', fontSize: 13.5, fontWeight: 700 }}>Informed by your assessment</span>
                </div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                  <div style={{ width: 104, flexShrink: 0 }}><window.MaturityRadar clusterScores={profile.clusterScores} size={104} labels={false} /></div>
                  <div>
                    <div style={{ fontSize: 26, fontWeight: 700, fontFamily: 'Montserrat', color: 'var(--asher-gold-dark)', lineHeight: 1, fontFeatureSettings: '"tnum"' }}>{profile.overall.toFixed(1)}<span style={{ fontSize: 12, color: 'var(--asher-grey-light)', fontWeight: 600 }}> / 5</span></div>
                    <div style={{ fontSize: 11.5, fontWeight: 700, color: 'var(--asher-gold-dark)', marginTop: 3 }}>{profile.overallLabel}</div>
                    <div style={{ fontSize: 10.5, color: 'var(--asher-grey-light)', marginTop: 2 }}>maturity today</div>
                  </div>
                </div>
                {topPriorities.length > 0 && (
                  <div style={{ marginTop: 12, paddingTop: 12, borderTop: '1px solid #f1efe8' }}>
                    <span className="eyebrow" style={{ fontSize: 9, color: 'var(--asher-grey-light)' }}>Your top priorities</span>
                    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5, marginTop: 7 }}>
                      {topPriorities.map((p, i) => (
                        <span key={p.id} className="chip" style={{ fontSize: 10, gap: 4 }}>
                          <span style={{ fontWeight: 700, color: 'var(--asher-gold-dark)' }}>{i + 1}</span>{p.label}
                        </span>
                      ))}
                    </div>
                    <p style={{ fontSize: 11, color: 'var(--asher-grey-medium)', lineHeight: 1.5, margin: '10px 0 0' }}>
                      Your NOW lane leans into <strong>{topPriorities[0].label}</strong>, and the plan closes your lowest-scoring capability areas first.
                    </p>
                  </div>
                )}
              </div>
            )}

            <AskAsher surface="blueprint" style={{ height: 420 }} />
          </div>
        </div>

        {/* Sticky build CTA */}
        <div style={{ position: 'sticky', bottom: 0, background: '#fff', borderTop: '1px solid #eef0eb', padding: '14px 36px' }}>
          <div style={{ maxWidth: 1280, margin: '0 auto', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 13, color: 'var(--asher-grey-medium)' }}>
              <button className="btn btn-secondary btn-sm" onClick={onBack}><Icon name="chevronLeft" size={12} /> Studio</button>
              <span><strong style={{ color: 'var(--asher-grey-dark)' }}>{freeCount} artifacts</strong> are free to build right now, the rest unlock when you're ready.</span>
            </div>
            <button className="btn btn-primary btn-lg" onClick={onGate}><Icon name="verified" size={15} /> Approve blueprint <Icon name="arrowRight" size={14} /></button>
          </div>
        </div>
      </div>
    </div>
  );
};

// ── Phase count badge (PrimeVue Badge — NOW=gold, NEXT=info, LATER=secondary) ──
const PhaseBadge = ({ tone, count }) => {
  const palette = {
    now: { background: 'var(--asher-gold)', color: '#fff' },
    next: { background: 'rgba(84,104,107,0.16)', color: '#4a5d60' },
    later: { background: '#ece9e1', color: 'var(--asher-grey-light)' },
  };
  return (
    <span style={{ minWidth: 19, height: 19, padding: '0 6px', borderRadius: 999, display: 'inline-grid', placeItems: 'center', fontSize: 10.5, fontWeight: 700, lineHeight: 1, fontFeatureSettings: '"tnum"', ...palette[tone] }}>{count}</span>
  );
};

// ── Phase column (KanbanPhase mechanic: split Policies | Processes, each with a
//    gold-icon header + count tag, cards expand one-at-a-time into a detail accordion) ──
const PhaseColumn = ({ kind, items, expanded, onToggle }) => {
  const isPolicy = kind === 'policy';
  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12, paddingBottom: 8, borderBottom: '1px solid rgba(228,170,35,0.28)' }}>
        <Icon name={isPolicy ? 'fileCheck' : 'sitemap'} size={15} color="var(--asher-gold)" />
        <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>{isPolicy ? 'Policies' : 'Processes'}</span>
        <span className="tag-secondary tag-count">{items.length}</span>
      </div>
      {items.length === 0 ? (
        <div style={{ fontSize: 12, color: 'var(--asher-grey-light)', fontStyle: 'italic', textAlign: 'center', padding: '18px 0' }}>
          No {isPolicy ? 'policies' : 'processes'} in this phase
        </div>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {items.map((it) => (
            <ItemCard key={it.id} it={it} expanded={expanded === it.id} onToggle={() => onToggle(expanded === it.id ? null : it.id)} />
          ))}
        </div>
      )}
    </div>
  );
};

// ── Detail key/value row (KanbanPhase accordion) ──
const KV = ({ label, value }) => (
  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 12, fontSize: 11.5, lineHeight: 1.5 }}>
    <span style={{ color: 'var(--asher-grey-medium)', whiteSpace: 'nowrap' }}>{label}</span>
    <span style={{ color: 'var(--asher-grey-dark)', fontWeight: 500, textAlign: 'right', flexShrink: 0 }}>{value}</span>
  </div>
);

// ── Blueprint item card (KanbanPhase card: icon + name + code tag, gold ring when
//    expanded, accordion of key/value detail rows) ──
const ItemCard = ({ it, expanded, onToggle }) => (
  <div className="asher-card asher-card--hover" onClick={onToggle} style={{
    cursor: 'pointer', fontFamily: 'inherit', padding: 0, overflow: 'hidden',
    boxShadow: expanded ? '0 0 0 1px rgba(228,170,35,0.4), var(--shadow-asher-md)' : 'var(--shadow-asher-sm)',
    border: '1px solid #e9e5da',
  }}>
    <div style={{ padding: '12px 14px' }}>
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 11 }}>
        <Icon name={it.type === 'policy' ? 'fileCheck' : 'sitemap'} size={17} color="var(--asher-gold)" style={{ flexShrink: 0, marginTop: 1 }} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--asher-grey-dark)', lineHeight: 1.3 }}>{it.name}</div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginTop: 6, flexWrap: 'wrap' }}>
            <span className="tag-secondary">{it.code}</span>
            {it.free
              ? <span className="chip chip-gold" style={{ fontSize: 9 }}>Free to build</span>
              : <span className="chip chip-locked" style={{ fontSize: 9 }}><Icon name="lock" size={8} /> Subscription</span>}
          </div>
        </div>
        <Icon name={expanded ? 'chevronDown' : 'chevronRight'} size={14} color="var(--asher-grey-light)" style={{ flexShrink: 0, marginTop: 2 }} />
      </div>
    </div>
    {expanded && (
      <div className="fade-up" style={{ borderTop: '1px solid rgba(228,170,35,0.22)', padding: '12px 14px', background: 'rgba(238,232,217,0.22)' }}>
        {it.note && <p style={{ fontSize: 12, color: 'var(--asher-grey-dark)', lineHeight: 1.55, margin: '0 0 11px' }}>{it.note}</p>}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
          <KV label="Output mode" value={<ModeBadge mode={it.mode} />} />
          <KV label="ARCH-AI req" value={`Req ${it.archReq}`} />
          <KV label="Work products" value={`${it.effort}`} />
          <KV label="Build stage" value={it.free ? 'Free now' : 'Stage B'} />
        </div>
        <div style={{ marginTop: 11, paddingTop: 10, borderTop: '1px solid rgba(228,170,35,0.16)' }}>
          <span className="eyebrow" style={{ fontSize: 9, color: 'var(--asher-grey-light)' }}>Why this is here</span>
          <p style={{ fontSize: 11.5, color: 'var(--asher-grey-medium)', lineHeight: 1.55, margin: '4px 0 0' }}>
            {it.type === 'policy'
              ? 'A documentation policy authored once and applied across organizational levels — it flows down to all of Radiology.'
              : 'A conditional process that attaches to the AI systems whose attributes trigger it, not copied per department.'}
          </p>
        </div>
      </div>
    )}
  </div>
);

window.BlueprintPreview = BlueprintPreview;


/* ===== screen-gate.jsx ===== */
/* global React, Icon, AskAsher */
// HERO 2, Gate 1: the moment the customer is asked to subscribe (inside Mode 1).
// The plan is never gated; BUILDING the full set is. Locked items stay visible,
// copy is positive & plain, named packages only (no internal codes/dials).

const GateOne = ({ onProceed, onBack }) => {
  const { PACKAGES, BLUEPRINT, FREE_TIER } = window.DATA;
  const FREE = FREE_TIER || { processDomains: 3, workplans: 1, operationalizedWorkflows: 1 };
  const [pick, setPick] = React.useState('free');
  const lockedItems = BLUEPRINT.items.filter((i) => !i.free);
  const lockedEffort = lockedItems.reduce((t, i) => t + i.effort, 0);

  return (
    <div className="app-shell">
      <div className="app-body">
        <div className="section-title-bar">
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <h1 style={{ margin: 0 }}>Your free allowance</h1>
            <span className="chip chip-gold" style={{ fontSize: 11 }}>Plan &amp; configure</span>
          </div>
          <p className="subtitle">You have the whole plan, free, and you can build with it right now. No subscription needed to get started.</p>
        </div>

        {/* Positive framing band — free runs end to end */}
        <div style={{ background: 'linear-gradient(135deg,#fffdf6,#faf3e1)', borderBottom: '1px solid rgba(228,170,35,0.2)', padding: '20px 36px' }}>
          <div style={{ maxWidth: 1240, margin: '0 auto', display: 'flex', alignItems: 'center', gap: 26, flexWrap: 'wrap' }}>
            <div style={{ width: 48, height: 48, borderRadius: 13, background: 'var(--asher-gold)', display: 'grid', placeItems: 'center', color: '#fff', flexShrink: 0 }}><Icon name="rocket" size={22} /></div>
            <div style={{ flex: 1, minWidth: 280 }}>
              <h2 style={{ margin: '0 0 4px', fontSize: 19 }}>Build {FREE.processDomains} process domains free, all the way through</h2>
              <p style={{ margin: 0, fontSize: 13.5, color: 'var(--asher-grey-medium)', lineHeight: 1.55 }}>
                Your free allowance covers {FREE.processDomains} process domains and their SOPs, one configured workplan, and operationalizing one workflow, end to end. Going broader across all of Radiology is a paid plan, when you're ready, with no rework.
              </p>
            </div>
            <div style={{ display: 'flex', gap: 18 }}>
              <Stat v={FREE.processDomains} l="process domains free" tone="gold" />
              <Stat v={FREE.workplans} l="workplan to configure" tone="gold" />
              <Stat v={FREE.operationalizedWorkflows} l="workflow to operate" tone="gold" />
            </div>
          </div>
        </div>

        <div style={{ maxWidth: 1240, margin: '0 auto', width: '100%', padding: '24px 36px 40px', display: 'grid', gridTemplateColumns: '1fr 340px', gap: 24, alignItems: 'flex-start' }}>
          {/* LEFT, packages */}
          <div>
            <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 14 }}>
              <h3 style={{ margin: 0, fontSize: 16 }}>Plans, for when you outgrow the free tier</h3>
              <span style={{ fontSize: 12, color: 'var(--asher-grey-light)' }}>Reference only · prices illustrative</span>
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 14 }}>
              {PACKAGES.map((p) => <PackageCard key={p.id} p={p} selected={pick === p.id} onSelect={() => setPick(p.id)} />)}
            </div>

            {/* Licensed-scope ceiling explainer, plain language, no codes */}
            <div className="asher-card" style={{ padding: 18, marginTop: 16, display: 'flex', gap: 14, alignItems: 'flex-start' }}>
              <span style={{ width: 38, height: 38, borderRadius: 10, background: 'var(--asher-cream)', display: 'grid', placeItems: 'center', color: 'var(--asher-gold-dark)', flexShrink: 0 }}><Icon name="layers" size={18} /></span>
              <div>
                <div style={{ fontSize: 13.5, fontWeight: 700, marginBottom: 4 }}>Your scope: 3 governance areas in Radiology</div>
                <p style={{ margin: 0, fontSize: 12.5, color: 'var(--asher-grey-medium)', lineHeight: 1.6 }}>
                  You're governing your three Radiology sections, Acute &amp; Trauma, Outpatient, and Research. A plan covers up to that many areas. Enterprise-wide policy from PTMC still flows down to you automatically; you only author governance for what you license. Need more areas later? That's a larger package, no rework.
                </p>
              </div>
            </div>

            {/* Locked items stay visible (motivation) */}
            <div style={{ marginTop: 16 }}>
              <div className="eyebrow" style={{ fontSize: 10, marginBottom: 10 }}>Still locked, visible so you know what's ahead</div>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 8 }}>
                {lockedItems.slice(0, 6).map((it) => (
                  <div key={it.id} style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '9px 11px', background: '#faf9f6', border: '1px solid #ecebe6', borderRadius: 8 }}>
                    <Icon name="lock" size={12} color="#c4c0b6" />
                    <span style={{ flex: 1, fontSize: 12, color: 'var(--asher-grey-medium)' }}>{it.name}</span>
                    <span style={{ fontSize: 10, color: 'var(--asher-grey-light)' }}>{it.effort} work products</span>
                  </div>
                ))}
              </div>
              <div style={{ fontSize: 11.5, color: 'var(--asher-grey-light)', marginTop: 8 }}>+ {lockedItems.length - 6} more across NEXT and LATER</div>
            </div>
          </div>

          {/* RIGHT, Ask Asher */}
          <div style={{ position: 'sticky', top: 16, display: 'flex', flexDirection: 'column', gap: 16 }}>
            <AskAsher surface="gate" style={{ height: 420 }} />
            <div className="asher-card" style={{ padding: 16, fontSize: 12, color: 'var(--asher-grey-medium)', lineHeight: 1.6 }}>
              <Icon name="info" size={13} color="var(--asher-gold-dark)" /> Nothing to decide today. Proceed with your free allowance, build {FREE.processDomains} process domains and a workplan now, your plan is saved and nothing expires.
            </div>
          </div>
        </div>

        {/* Footer actions — the ONLY way forward is the free allowance. Paid plans
            above are informational, for when you outgrow it; they aren't the path. */}
        <div style={{ position: 'sticky', bottom: 0, background: '#fff', borderTop: '1px solid #eef0eb', padding: '14px 36px' }}>
          <div style={{ maxWidth: 1240, margin: '0 auto', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16 }}>
            <button className="btn btn-secondary" onClick={onBack}><Icon name="chevronLeft" size={12} /> Back to plan</button>
            <button className="btn btn-primary btn-lg" onClick={onProceed}>Proceed with free allowance <Icon name="arrowRight" size={14} /></button>
          </div>
        </div>
      </div>
    </div>
  );
};

const Stat = ({ v, l, tone }) => (
  <div style={{ textAlign: 'center' }}>
    <div style={{ fontSize: 26, fontWeight: 700, fontFamily: 'Montserrat', lineHeight: 1, color: tone === 'gold' ? 'var(--asher-gold-dark)' : 'var(--asher-grey-dark)' }}>{v}</div>
    <div style={{ fontSize: 10, color: 'var(--asher-grey-light)', textTransform: 'uppercase', letterSpacing: '0.04em', marginTop: 5 }}>{l}</div>
  </div>
);

// Licensing-scope (D3) definitions for the named packages. This grouping is about
// how far one plan can govern, not the formal ontology namespace, so the framing
// is plain-language with a concrete imaging example.
const SCOPE_DEFS = {
  dept: {
    label: 'Department',
    body: 'A formal hospital or health system unit with defined staffing, reporting, and operational ownership.',
    eg: 'Examples: Radiology, Pathology.',
    ask: "What's the difference between the Department and Service Line packages?",
  },
  serviceline: {
    label: 'Service Line',
    body: 'A clinical grouping that cuts across several departments \u2014 broader than any single one.',
    eg: 'Example: Enterprise Imaging spans Radiology, Cardiology, Surgery, and Dermatology.',
    ask: "What's the difference between the Department and Service Line packages?",
  },
  enterprise: {
    label: 'Enterprise',
    body: 'Governance across an entire hospital or health system \u2014 every department and service line, with managed-service options.',
    eg: 'Example: a whole IDN standardizing AI oversight across all of its facilities.',
    ask: 'When does a program need the Enterprise package instead of Service Line?',
  },
};

const ScopeInfo = ({ id }) => {
  const [hov, setHov] = React.useState(false);
  const d = SCOPE_DEFS[id];
  if (!d) return null;
  return (
    <span style={{ position: 'relative', display: 'inline-flex' }}
      onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)}
      onClick={(e) => e.stopPropagation()}>
      <span tabIndex={0} role="img" aria-label={`${d.label}: ${d.body} ${d.eg}`}
        onFocus={() => setHov(true)} onBlur={() => setHov(false)}
        style={{ display: 'inline-grid', placeItems: 'center', cursor: 'help', color: 'var(--asher-gold-dark)' }}>
        <Icon name="info" size={13} style={{ opacity: 0.7 }} />
      </span>
      {hov && (
        <span role="tooltip" style={{ position: 'absolute', top: 'calc(100% + 9px)', left: '50%', transform: 'translateX(-50%)', width: 252, background: 'var(--asher-grey-dark)', color: '#fff', fontSize: 11.5, lineHeight: 1.55, padding: '11px 13px', borderRadius: 10, zIndex: 40, boxShadow: '0 10px 26px rgba(0,0,0,0.26)', fontWeight: 400, textAlign: 'left', fontFamily: 'Roboto, sans-serif' }}>
          <span style={{ display: 'block', fontWeight: 700, marginBottom: 4 }}>{d.label}</span>
          {d.body}
          <span style={{ display: 'block', marginTop: 7, paddingTop: 7, borderTop: '1px solid rgba(255,255,255,0.16)', color: 'var(--asher-gold)', fontWeight: 500 }}>{d.eg}</span>
          <button
            onMouseDown={(e) => { e.preventDefault(); e.stopPropagation(); }}
            onClick={(e) => { e.stopPropagation(); window.dispatchEvent(new CustomEvent('asher:ask', { detail: { surface: 'gate', text: d.ask } })); }}
            style={{ marginTop: 9, display: 'inline-flex', alignItems: 'center', gap: 5, fontFamily: 'inherit', fontSize: 11, fontWeight: 600, cursor: 'pointer', color: 'var(--asher-grey-dark)', background: 'var(--asher-gold)', border: 'none', borderRadius: 7, padding: '5px 9px' }}>
            <Icon name="sparkles" size={11} /> Ask Asher{id === 'enterprise' ? '' : ' to compare'}
          </button>
          <span style={{ position: 'absolute', bottom: '100%', left: '50%', transform: 'translateX(-50%)', border: '5px solid transparent', borderBottomColor: 'var(--asher-grey-dark)' }} />
        </span>
      )}
    </span>
  );
};

const PackageCard = ({ p, selected, onSelect }) => {
  const cur = p.current;
  return (
    <button onClick={onSelect} className="asher-card" style={{
      textAlign: 'left', cursor: 'pointer', fontFamily: 'inherit', padding: 18, position: 'relative',
      border: selected ? '2px solid var(--asher-gold)' : cur ? '2px solid rgba(228,170,35,0.4)' : '1px solid #e9e5da',
      background: selected || cur ? 'linear-gradient(165deg,#fffdf7,#fdf8ec)' : '#fff',
    }}>
      {cur && <span className="chip chip-gold" style={{ fontSize: 9.5, position: 'absolute', top: 14, right: 14, display: 'inline-flex', alignItems: 'center', gap: 4, whiteSpace: 'nowrap' }}><Icon name="target" size={9} /> You are here</span>}
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontFamily: 'Montserrat', fontSize: 16, fontWeight: 700, color: 'var(--asher-grey-dark)' }}>{p.name}</span>
        <ScopeInfo id={p.id} />
      </div>
      <div style={{ fontSize: 12, color: 'var(--asher-grey-medium)', margin: '3px 0 12px', minHeight: 32 }}>{p.tagline}</div>
      <div style={{ marginBottom: 14 }}>
        {p.price ? <>
          {p.startingAt && <div style={{ fontSize: 10.5, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em', color: 'var(--asher-grey-light)', marginBottom: 1 }}>Starting at</div>}
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
            <span style={{ fontSize: 26, fontWeight: 700, fontFamily: 'Montserrat', color: 'var(--asher-grey-dark)' }}>{p.price}</span>
            <span style={{ fontSize: 11, color: 'var(--asher-grey-light)' }}>{p.priceNote}</span>
          </div>
        </> : <span style={{ fontSize: 15, fontWeight: 600, color: 'var(--asher-grey-medium)' }}>{p.priceNote}</span>}
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
        {p.includes.map((f) => (
          <div key={f} style={{ display: 'flex', gap: 8, fontSize: 12, color: 'var(--asher-grey-medium)', lineHeight: 1.4 }}>
            <Icon name="check" size={13} color="var(--asher-gold-dark)" style={{ flexShrink: 0, marginTop: 1 }} /> {f}
          </div>
        ))}
      </div>
      <div style={{ marginTop: 14, display: 'flex', alignItems: 'center', gap: 8, fontSize: 12.5, fontWeight: 600, color: selected ? 'var(--asher-gold-dark)' : 'var(--asher-grey-medium)' }}>
        <Icon name={selected ? 'circleCheck' : 'circle'} size={16} color={selected ? 'var(--asher-gold)' : '#cfcabd'} />
        {selected ? 'Selected' : p.cta}
      </div>
    </button>
  );
};

window.GateOne = GateOne;


/* ===== screen-stageb.jsx ===== */
/* global React, Icon, TopBar, PolicyBreadcrumb, RiskBadge, AskAsher */
// HERO 3, Stage B capability configuration, centered on the conditional-methods
// contrast. Reuses the MethodCandidatePanel / MethodCandidateCard idea: each AI
// system instantiates the subset of methods its attributes trigger. A high-risk
// regulated autonomous tool pulls in far more than a sandboxed in-house model.

const StageB = ({ onBack, onUpgrade, onNav }) => {
  const upgrade = onUpgrade || onBack; // surfaces the (informational) plans/paygate
  const { SYSTEMS, METHODS, BLUEPRINT, FREE_TIER } = window.DATA;
  const FREE = FREE_TIER || { processDomains: 3 };
  const [a, setA] = React.useState('ich');
  const [b, setB] = React.useState('denoise');
  const sysA = SYSTEMS.find((s) => s.id === a);
  const sysB = SYSTEMS.find((s) => s.id === b);

  const freeBuilt = BLUEPRINT.items.filter((i) => i.type === 'process' && i.free);
  const lockedProc = BLUEPRINT.items.filter((i) => i.type === 'process' && !i.free);

  return (
    <div className="app-shell">
      <div className="app-body">
        <div className="section-title-bar" style={{ borderTop: '4px solid var(--cap-risk)' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16 }}>
            <div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <h1 style={{ margin: 0 }}>Build artifacts</h1>
                <span className="chip chip-gold" style={{ fontSize: 11 }}>Plan &amp; configure</span>
              </div>
              <p className="subtitle">Inside this workplan: process domains, SOPs, methods and controls, conditional on your registry and your ARCH-AI target.</p>
            </div>
            <button className="btn btn-secondary btn-sm" onClick={onBack}><Icon name="chevronLeft" size={12} /> Configuration Hub</button>
          </div>
        </div>

        {/* Free-allowance band — what the free tier lets you build here */}
        <div style={{ background: 'rgba(228,170,35,0.07)', borderBottom: '1px solid rgba(228,170,35,0.18)', padding: '12px 36px' }}>
          <div style={{ maxWidth: 1280, margin: '0 auto', display: 'flex', alignItems: 'center', gap: 14, flexWrap: 'wrap' }}>
            <Icon name="bolt" size={15} color="var(--asher-gold-dark)" />
            <span style={{ fontSize: 13, color: 'var(--asher-grey-dark)' }}><strong>Free allowance:</strong> {FREE.processDomains} of 11 process domains</span>
            <div style={{ flex: 1, maxWidth: 220, height: 7, background: '#eceae3', borderRadius: 4, overflow: 'hidden' }}>
              <div style={{ width: `${Math.round((FREE.processDomains / 11) * 100)}%`, height: '100%', background: 'var(--asher-gold)' }} />
            </div>
            <span style={{ fontSize: 12, color: 'var(--asher-grey-light)' }}>The rest are part of the full program</span>
            <button className="btn btn-ghost btn-sm" style={{ marginLeft: 'auto' }} onClick={upgrade}>See plans <Icon name="arrowRight" size={12} /></button>
          </div>
        </div>

        <div style={{ maxWidth: 1280, margin: '0 auto', width: '100%', padding: '24px 36px 40px', display: 'grid', gridTemplateColumns: '1fr 340px', gap: 24, alignItems: 'flex-start' }}>
          <div>
            {/* THE CONTRAST */}
            <div style={{ marginBottom: 10 }}>
              <div className="eyebrow" style={{ color: 'var(--asher-gold-dark)', marginBottom: 6 }}>The conditional-method engine</div>
              <h2 style={{ fontSize: 22, margin: '0 0 6px', letterSpacing: '-0.01em' }}>Different systems, different governance</h2>
              <p style={{ fontSize: 13.5, color: 'var(--asher-grey-medium)', lineHeight: 1.55, margin: 0, maxWidth: 760 }}>
                Each AI system instantiates only the methods its attributes trigger, modality, whether the manufacturer is regulated, risk, and how it's deployed. This is grounded in your registry, not a generic template. Compare any two:
              </p>
            </div>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
              <SystemColumn sys={sysA} other={sysB} side="A" onPick={setA} METHODS={METHODS} SYSTEMS={SYSTEMS} accent="var(--risk-high)" />
              <SystemColumn sys={sysB} other={sysA} side="B" onPick={setB} METHODS={METHODS} SYSTEMS={SYSTEMS} accent="var(--risk-sandbox)" />
            </div>

            {/* Contrast callout */}
            <div className="asher-card" style={{ marginTop: 14, padding: 16, display: 'flex', gap: 14, alignItems: 'center', background: 'linear-gradient(135deg,#fffdf6,#fbf4e2)', border: '1px solid rgba(228,170,35,0.3)' }}>
              <Icon name="lightbulb" size={20} color="var(--asher-gold-dark)" style={{ flexShrink: 0 }} />
              <div style={{ fontSize: 13, color: 'var(--asher-grey-dark)', lineHeight: 1.55 }}>
                <strong>{sysA.name}</strong> triggers <strong>{sysA.methods.length} methods</strong> ({sysA.effort} work products); <strong>{sysB.name}</strong> triggers <strong>{sysB.methods.length}</strong> ({sysB.effort} work products).
                {' '}The difference isn't arbitrary, it's the proof your governance is sized to your real systems. Fewer high-risk tools would mean a smaller program.
              </div>
            </div>

            {/* Free/locked build queue */}
            <div style={{ marginTop: 26 }}>
              <h3 style={{ fontSize: 16, margin: '0 0 4px' }}>Your build queue · NOW lane</h3>
              <p style={{ fontSize: 12.5, color: 'var(--asher-grey-light)', margin: '0 0 14px' }}>Process domains staged for the NOW phase. Three are free; the rest unlock with a subscription.</p>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
                {freeBuilt.map((it) => <BuildRow key={it.id} it={it} state="building" />)}
                {lockedProc.slice(0, 5).map((it) => <BuildRow key={it.id} it={it} state="locked" onUpgrade={upgrade} />)}
              </div>
            </div>

            {/* Locked operationalize future step */}
            <div style={{ marginTop: 26 }}>
              <div className="eyebrow" style={{ fontSize: 10, marginBottom: 10 }}>What comes after building</div>
              <OperationalizeLocked />
            </div>
          </div>

          {/* Ask Asher rail */}
          <div style={{ position: 'sticky', top: 16 }}>
            <AskAsher surface="stageB" style={{ height: 480 }} />
          </div>
        </div>
      </div>
    </div>
  );
};

// ── System comparison column ─────────────────────────────────────────────────
const SystemColumn = ({ sys, other, side, onPick, METHODS, SYSTEMS, accent }) => {
  const shared = (k) => other.methods.includes(k);
  return (
    <div className="asher-card" style={{ padding: 0, overflow: 'hidden', borderTop: `3px solid ${accent}` }}>
      <div style={{ padding: '14px 16px', borderBottom: '1px solid #eef0eb' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
          <span className="eyebrow" style={{ fontSize: 9.5 }}>System {side}</span>
          <select value={sys.id} onChange={(e) => onPick(e.target.value)} style={{ fontSize: 11.5, fontFamily: 'inherit', border: '1px solid #e3e0d8', borderRadius: 7, padding: '4px 8px', color: 'var(--asher-grey-medium)', background: '#fff', cursor: 'pointer', maxWidth: 150 }}>
            {SYSTEMS.map((s) => <option key={s.id} value={s.id}>{s.name}</option>)}
          </select>
        </div>
        <div style={{ fontSize: 15, fontWeight: 700, fontFamily: 'Montserrat', color: 'var(--asher-grey-dark)' }}>{sys.name}</div>
        <div style={{ fontSize: 11.5, color: 'var(--asher-grey-light)', marginTop: 2 }}>{sys.use}</div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 10 }}>
          <RiskBadge risk={sys.risk} size="sm" />
          <span className="chip" style={{ fontSize: 10 }}>{sys.modality}</span>
          <span className="chip" style={{ fontSize: 10 }}>{sys.regulated ? 'FDA-cleared' : 'In-house'}</span>
          {sys.autonomous && <span className="chip chip-warn" style={{ fontSize: 10 }}>Autonomous</span>}
        </div>
      </div>

      {/* Triggered methods */}
      <div style={{ padding: '12px 16px' }}>
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 10 }}>
          <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--asher-grey-medium)' }}>Triggered methods</span>
          <span style={{ fontSize: 11, color: 'var(--asher-grey-light)' }}><strong style={{ color: accent, fontSize: 16, fontFamily: 'Montserrat' }}>{sys.methods.length}</strong> methods · {sys.effort} work products</span>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          {sys.methods.map((k) => {
            const m = METHODS[k];
            const isShared = shared(k);
            return (
              <div key={k} style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '8px 10px', borderRadius: 8, background: isShared ? '#faf9f6' : 'rgba(228,170,35,0.06)', border: `1px solid ${isShared ? '#eceae3' : 'rgba(228,170,35,0.22)'}` }}>
                <span style={{ width: 6, height: 6, borderRadius: '50%', background: isShared ? '#cfcabd' : accent, flexShrink: 0 }} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--asher-grey-dark)', lineHeight: 1.25 }}>{m.name}</div>
                  <div style={{ fontSize: 10, color: 'var(--asher-grey-light)' }}>{m.domain} · {m.id}</div>
                </div>
                {!isShared && <span className="chip chip-gold" style={{ fontSize: 8.5 }}>risk-driven</span>}
              </div>
            );
          })}
        </div>
        <div style={{ marginTop: 10, fontSize: 10.5, color: 'var(--asher-grey-light)', display: 'flex', alignItems: 'center', gap: 6 }}>
          <span style={{ width: 6, height: 6, borderRadius: '50%', background: '#cfcabd' }} /> shared baseline
          <span style={{ width: 6, height: 6, borderRadius: '50%', background: accent, marginLeft: 8 }} /> triggered by this system
        </div>
      </div>
    </div>
  );
};

// ── Build-queue row ──────────────────────────────────────────────────────────
const BuildRow = ({ it, state, onUpgrade }) => {
  const building = state === 'building';
  return (
    <div className="asher-card" style={{ padding: '12px 15px', display: 'flex', alignItems: 'center', gap: 13, opacity: building ? 1 : 0.92 }}>
      <span style={{ width: 32, height: 32, borderRadius: 9, display: 'grid', placeItems: 'center', flexShrink: 0,
        background: building ? 'var(--asher-success-bg)' : '#efedE7', color: building ? 'var(--asher-success-fg)' : 'var(--asher-grey-light)' }}>
        <Icon name={building ? 'flow' : 'lock'} size={15} />
      </span>
      <div style={{ flex: 1 }}>
        <div style={{ fontSize: 13, fontWeight: 600 }}>{it.name}</div>
        <div style={{ fontSize: 11, color: 'var(--asher-grey-light)' }}>{it.code} · {it.effort} work products{it.note ? ` · ${it.note}` : ''}</div>
      </div>
      {building
        ? <span className="chip chip-success" style={{ fontSize: 10 }}><Icon name="check" size={10} /> Building · free</span>
        : <button className="btn btn-ghost btn-sm" onClick={onUpgrade}><Icon name="lock" size={11} /> See plans</button>}
    </div>
  );
};

// ── Operationalize (Op-1 / Op-2), locked future step, two treatments ────────
// ── Operationalize (Op-1 / Op-2), quiet-lock paid gate ──────────────────────
const OperationalizeLocked = () => {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
      {[['users', 'Run manually', 'Controls wired to tools; your team runs the steps by hand.', true], ['cog', 'Automate', 'Asher runs steps & controls as scheduled workflows.', false]].map(([ic, t, d, lite]) => (
        <div key={t} style={{ background: '#faf9f6', border: '1px solid #e9e5da', borderRadius: 11, padding: 16, display: 'flex', gap: 12, alignItems: 'flex-start' }}>
          <span style={{ width: 34, height: 34, borderRadius: 9, background: '#efedE7', display: 'grid', placeItems: 'center', color: 'var(--asher-grey-light)', flexShrink: 0 }}><Icon name={ic} size={16} /></span>
          <div style={{ flex: 1 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--asher-grey-medium)' }}>{t}</span>
              {lite
                ? <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, marginLeft: 'auto', fontSize: 9, fontWeight: 700, color: '#5d6b54', background: 'rgba(180,189,175,0.28)', padding: '2px 7px', borderRadius: 999, textTransform: 'uppercase', letterSpacing: '0.04em' }}><Icon name="check" size={8} /> Lite, free</span>
                : <span className="chip chip-gold" style={{ fontSize: 9, marginLeft: 'auto' }}><Icon name="lock" size={9} /> Paid</span>}
            </div>
            <div style={{ fontSize: 11.5, color: 'var(--asher-grey-light)', marginTop: 3, lineHeight: 1.5 }}>{d}</div>
          </div>
        </div>
      ))}
    </div>
  );
};

window.StageB = StageB;


/* ===== screen-activation.jsx (ScreenActivation / 2B threshold ceremony) ===== */
/* global React, Icon */
// ───────────────────────────────────────────────────────────────────────────
// ScreenActivation  ·  Stage B / Frame 2B  ·  the "threshold ceremony"
//
// Shown after the user clicks "Start Detailed Configuration" on an approved
// blueprint. Narrates the hydration of their AI Management System (workplans
// spawned, frameworks mapped, tokens seeded, assistants prepping), then enables
// the opinionated "start here" CTA and fires onComplete.
//
// DROP-IN CONTRACT (matches the rest of the sim):
//   • Browser-Babel + window globals. No import/export.
//   • React + ReactDOM assumed global (uses React.useState / useEffect / useRef).
//   • <Icon> assumed global (provide via your concat order — shell.jsx).
//   • Styling via styles.css classes (.btn, .chip, .app-shell …) + CSS var tokens
//     (--asher-gold, --asher-success …). No external stylesheet authored here.
//   • Attaches window.ScreenActivation. Sub-components stay private.
//
// DATA MODEL
//   PROPS (high-variance — lift from window.REG / window.ASSESS / window.DATA,
//          vary by #prospect / #customer scenario). PTMC baked as defaults so the
//          component self-demos standalone; override all of them on integration.
//   CONSTANTS (in-file, below) — frameworks + every string. One value today;
//          parameterize later only when a real second value appears.
//
//   <ScreenActivation
//      setup={REG}              // org profile — REAL lifted key, may be null
//      assessment={ASSESS}      // maturity profile — REAL lifted key, opaque/partial
//      hydrationSteps={DATA.hydration}
//      recommendedArea={DATA.recommendedArea}
//      activeIndex={n}          // OPTIONAL controlled override — omit to self-pace
//      onComplete={() => …}     // ceremony finished
//      onPrimary={() => …}      // "Take me to {area}" (enabled at completion)
//      onSeeAll={() => …}       // "see all workplans"
//   />
//
// PACING — controlled-if-prop-present (React controlled/uncontrolled pattern):
//   • omit activeIndex → component SELF-PACES off each step's `duration` (booth
//     default; matches the wizard's setTimeout(onComplete, 1700) precedent).
//   • pass a numeric activeIndex → component is CONTROLLED; no internal timer,
//     the parent feeds the index (drop-in for real backend hydration signals).
//   setup may be null and assessment is treated as opaque — both degrade to
//   em-dash rather than crashing. PTMC defaults apply only when undefined.
// ───────────────────────────────────────────────────────────────────────────

// ── CONSTANTS (stable scaffolding — copy + frameworks) ──────────────────────
const ACTIVATION_COPY = {
  topbarBrand: 'Asher Informatics',
  topbarRunning: 'Activating · You can step away',
  topbarDone: 'Setup complete',
  badge: 'Activating your system',
  headlineLead: 'Your blueprint is becoming',
  headlineEmphasis: 'your AI Management System.',
  lede:
    "We're seeding five implementation workplans with what you told us in the assessment. " +
    'Each capability area gets its own focused workspace where you’ll define the policies, ' +
    'workflows, and SOPs that will run your governance.',
  knowLabel: 'From your blueprint, we know:',
  fieldOrg: 'Organization',
  fieldFrameworks: 'Frameworks in scope',
  fieldMaturity: 'Maturity profile',
  fieldArchetype: 'Governance archetype',
  settingUp: 'Setting up',
  stepsComplete: 'steps complete',
  recommendedLabel: 'Recommended starting point',
  primaryPrefix: 'Take me to',
  pendingFooter: 'Available when setup completes',
  readyFooter: 'Ready when you are',
  seeAll: 'see all workplans',
};

// Frameworks in scope — ACR ARCH-AI is the demo's through line (the blueprint TARGET),
// with HIPAA as the standing requirement. Matches the AIMS `priorityFocus` in shell.jsx.
const ACTIVATION_FRAMEWORKS = ['ACR ARCH-AI', 'HIPAA'];

const ACTIVATION_DEFAULT_STEP_MS = 1700;

// ── DEFAULT PROP VALUES (PTMC — self-demo; override on integration) ─────────
const ACTIVATION_DEFAULT_SETUP = {
  org: 'PTMC Radiology',
  orgDetail: '240-bed health system',
  archetype: 'Federated · 3-tier committee structure',
};

const ACTIVATION_DEFAULT_ASSESSMENT = {
  maturityScore: '2.4 / 5.0',
  maturityLabel: 'Establishing',
};

// Per-step durations halved from the original hand-off (~17.6s → ~9s) so the
// ceremony reads as a beat, not a wait. Tune here; the sum is the total run time.
const ACTIVATION_DEFAULT_HYDRATION_STEPS = [
  {
    label: 'Workplans created',
    detail: '5 capability areas · seeded with priority domains from your blueprint',
    duration: 1600,
  },
  {
    label: 'Compliance frameworks mapped',
    detail:
      '178 control requirements aligned to ACR ARCH-AI, cross-mapped to ISO 42001, NIST AI RMF, TJC AI, HIPAA',
    duration: 2000,
  },
  {
    label: 'Organizational tokens seeded',
    detail: 'Committee structure, service lines, and risk appetite from your assessment',
    duration: 1600,
  },
  {
    label: 'Policy templates pre-selected',
    detail: '38 candidate policies — 12 marked NOW, 14 NEXT, 12 LATER',
    duration: 1800,
  },
  {
    label: 'Domain assistants preparing recommendations',
    detail: 'Drafting opinionated workflow proposals for your priority domains',
    duration: 2000,
  },
];

const ACTIVATION_DEFAULT_RECOMMENDED_AREA = {
  name: 'Strategy & Leadership',
  domainsNow: null,
  rationale:
    'It carries the foundational artifacts the rest of your program builds on — your Scope Applicability and AI Governance policies. It is the workplan included in your free allowance, so we suggest starting here.',
};

// Opaque-safe read of the maturity profile — assessment is a real lifted key
// treated as a black box; tolerate several shapes + partial/empty objects.
function readActivationMaturity(a) {
  if (!a || typeof a !== 'object') return null;
  if (a.maturityScore != null || a.maturityLabel != null) {
    return [a.maturityScore, a.maturityLabel].filter(Boolean).join(' — ');
  }
  if (typeof a.maturityDisplay === 'string') return a.maturityDisplay;
  if (typeof a.maturity === 'string') return a.maturity;
  if (a.score != null) return [a.score, a.label].filter(Boolean).join(' — ');
  return null;
}

// ── ScreenActivation ────────────────────────────────────────────────────────
function ScreenActivation({
  setup = ACTIVATION_DEFAULT_SETUP,
  assessment = ACTIVATION_DEFAULT_ASSESSMENT,
  hydrationSteps = ACTIVATION_DEFAULT_HYDRATION_STEPS,
  recommendedArea = ACTIVATION_DEFAULT_RECOMMENDED_AREA,
  activeIndex,            // CONTROLLED override (number). Omit → self-pace.
  autoRun = true,
  onComplete,
  onPrimary,
  onSeeAll,
}) {
  const steps = hydrationSteps && hydrationSteps.length ? hydrationSteps : ACTIVATION_DEFAULT_HYDRATION_STEPS;

  // Controlled-if-prop-present: a numeric activeIndex drives the ceremony from
  // outside (real backend hydration); omit it and we self-pace off per-step
  // duration. The pacing timer is transient local UI state, intentionally not
  // lifted — mirrors the wizard's setGenerating/setTimeout beat.
  const controlled = typeof activeIndex === 'number';

  // Honor reduced-motion: skip the ceremony, present the ready state.
  const reduceMotion = React.useMemo(
    () =>
      typeof window !== 'undefined' &&
      window.matchMedia &&
      window.matchMedia('(prefers-reduced-motion: reduce)').matches,
    []
  );

  const [internalIndex, setInternalIndex] = React.useState(
    autoRun && !reduceMotion ? 0 : steps.length
  );
  const idx = controlled ? activeIndex : internalIndex;
  const firedComplete = React.useRef(false);

  React.useEffect(() => {
    if (controlled) return undefined;            // parent drives the index
    if (!autoRun || reduceMotion) return undefined;
    if (internalIndex >= steps.length) return undefined;
    const dur =
      steps[internalIndex] && steps[internalIndex].duration != null
        ? steps[internalIndex].duration
        : ACTIVATION_DEFAULT_STEP_MS;
    const t = setTimeout(() => setInternalIndex((i) => i + 1), dur);
    return () => clearTimeout(t);
  }, [controlled, internalIndex, autoRun, reduceMotion, steps]);

  const complete = idx >= steps.length;
  const doneCount = Math.min(Math.max(idx, 0), steps.length);

  React.useEffect(() => {
    if (complete && !firedComplete.current) {
      firedComplete.current = true;
      onComplete && onComplete();
    }
  }, [complete, onComplete]);

  const statusLabel = complete
    ? `${ACTIVATION_COPY.topbarDone} · ${steps.length} of ${steps.length} ${ACTIVATION_COPY.stepsComplete}`
    : `${ACTIVATION_COPY.settingUp} · ${doneCount} of ${steps.length} ${ACTIVATION_COPY.stepsComplete}`;

  // Null-safe setup + opaque-safe assessment (real lifted keys). Defaults above
  // apply only when undefined (standalone demo); explicit null → em-dash.
  const s = setup || {};
  const orgText = [s.org, s.orgDetail].filter(Boolean).join(' · ') || '—';
  const archetypeText = s.archetype || '—';
  const maturityText = readActivationMaturity(assessment) || '—';

  const rec = recommendedArea || {};
  const recName = rec.name || '—';
  const recSentence =
    (rec.domainsNow != null ? `You marked ${rec.domainsNow} domains here as NOW priority. ` : '') +
    (rec.rationale || '');

  return (
    <div className="app-shell" style={{ background: '#1a1f2c' }}>
      {/* Dark, muted top bar */}
      <div
        style={{
          height: 52,
          background: '#11151e',
          borderBottom: '1px solid #2a3441',
          display: 'flex',
          alignItems: 'center',
          padding: '0 18px',
          gap: 12,
          flexShrink: 0,
        }}
      >
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <div
            style={{
              width: 22,
              height: 22,
              background: 'var(--asher-gold)',
              color: '#11151e',
              borderRadius: 4,
              display: 'grid',
              placeItems: 'center',
              fontWeight: 800,
              fontSize: 11,
            }}
          >
            A
          </div>
          <span style={{ fontWeight: 700, fontSize: 13, color: '#fff' }}>{ACTIVATION_COPY.topbarBrand}</span>
        </div>
        <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 10 }}>
          {complete && <Icon name="circleCheck" size={14} color="#34d399" />}
          <span style={{ fontSize: 11, color: complete ? '#34d399' : '#9ca5b0' }}>
            {complete ? ACTIVATION_COPY.topbarDone : ACTIVATION_COPY.topbarRunning}
          </span>
        </div>
      </div>

      <div style={{ flex: 1, display: 'grid', gridTemplateColumns: '1fr 1fr', overflow: 'hidden' }}>
        {/* LEFT — narrative */}
        <div
          style={{
            padding: '60px 56px',
            display: 'flex',
            flexDirection: 'column',
            justifyContent: 'center',
          }}
        >
          <div
            className="chip"
            style={{
              background: 'rgba(212,160,23,0.18)',
              color: 'var(--asher-gold-light)',
              alignSelf: 'flex-start',
              marginBottom: 18,
            }}
          >
            <Icon name="bolt" size={11} /> {ACTIVATION_COPY.badge}
          </div>
          <h1
            style={{
              margin: '0 0 14px',
              fontSize: 42,
              lineHeight: 1.1,
              color: '#fff',
              letterSpacing: '-0.02em',
              fontWeight: 700,
            }}
          >
            {ACTIVATION_COPY.headlineLead}
            <br />
            <span style={{ color: 'var(--asher-gold-light)' }}>{ACTIVATION_COPY.headlineEmphasis}</span>
          </h1>
          <p style={{ margin: '0 0 26px', fontSize: 15, color: '#a8b3c1', lineHeight: 1.6, maxWidth: 480 }}>
            {ACTIVATION_COPY.lede}
          </p>

          <div
            style={{
              background: 'rgba(255,255,255,0.04)',
              border: '1px solid rgba(255,255,255,0.08)',
              borderRadius: 10,
              padding: 18,
            }}
          >
            <div
              style={{
                fontSize: 11,
                fontWeight: 700,
                letterSpacing: '0.1em',
                color: 'var(--asher-gold-light)',
                textTransform: 'uppercase',
                marginBottom: 14,
              }}
            >
              {ACTIVATION_COPY.knowLabel}
            </div>
            <div
              style={{
                display: 'grid',
                gridTemplateColumns: '1fr 1fr',
                gap: 12,
                fontSize: 12.5,
                color: '#cbd5e1',
                lineHeight: 1.6,
              }}
            >
              <ActivationFact label={ACTIVATION_COPY.fieldOrg} value={orgText} />
              <ActivationFact
                label={ACTIVATION_COPY.fieldFrameworks}
                value={ACTIVATION_FRAMEWORKS.join(' · ')}
              />
              <ActivationFact label={ACTIVATION_COPY.fieldMaturity} value={maturityText} />
              <ActivationFact label={ACTIVATION_COPY.fieldArchetype} value={archetypeText} />
            </div>
          </div>
        </div>

        {/* RIGHT — hydration progress */}
        <div style={{ padding: '60px 56px', borderLeft: '1px solid #232a37', overflow: 'auto' }}>
          <div
            style={{
              fontSize: 11,
              fontWeight: 700,
              letterSpacing: '0.1em',
              color: complete ? '#34d399' : '#7d8694',
              textTransform: 'uppercase',
              marginBottom: 18,
            }}
          >
            {statusLabel}
          </div>

          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            {steps.map((step, i) => (
              <ActivationStep
                key={i}
                status={i < idx ? 'done' : i === idx ? 'active' : 'pending'}
                label={step.label}
                detail={step.detail}
                duration={step.duration != null ? step.duration : ACTIVATION_DEFAULT_STEP_MS}
              />
            ))}
          </div>

          {/* Recommended starting point */}
          <div
            style={{
              marginTop: 28,
              padding: 18,
              background: 'rgba(212,160,23,0.08)',
              border: '1px solid rgba(212,160,23,0.25)',
              borderRadius: 10,
              transition: 'border-color 0.4s, background 0.4s',
            }}
          >
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
              <Icon name="lightbulb" size={14} color="var(--asher-gold-light)" />
              <span
                style={{
                  fontSize: 11,
                  fontWeight: 700,
                  letterSpacing: '0.08em',
                  color: 'var(--asher-gold-light)',
                  textTransform: 'uppercase',
                }}
              >
                {ACTIVATION_COPY.recommendedLabel}
              </span>
            </div>
            <div style={{ fontSize: 14, fontWeight: 600, color: '#fff', marginBottom: 4 }}>{recName}</div>
            <div style={{ fontSize: 12, color: '#a8b3c1', lineHeight: 1.55, marginBottom: 14 }}>
              {recSentence}
            </div>
            <div style={{ display: 'flex', gap: 8 }}>
              <button
                className="btn btn-primary"
                style={{ flex: 1, opacity: complete ? 1 : 0.55, transition: 'opacity 0.4s' }}
                disabled={!complete}
                onClick={() => complete && onPrimary && onPrimary(rec)}
              >
                {ACTIVATION_COPY.primaryPrefix} {recName} <Icon name="arrowRight" size={13} />
              </button>
            </div>
            <div style={{ fontSize: 11, color: '#7d8694', marginTop: 10, textAlign: 'center' }}>
              {complete ? ACTIVATION_COPY.readyFooter : ACTIVATION_COPY.pendingFooter} ·{' '}
              <a
                href="#"
                style={{ color: '#a8b3c1' }}
                onClick={(e) => {
                  e.preventDefault();
                  onSeeAll && onSeeAll();
                }}
              >
                {ACTIVATION_COPY.seeAll}
              </a>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ── private sub-components ───────────────────────────────────────────────────
function ActivationFact({ label, value }) {
  return (
    <div>
      <div style={{ color: '#7d8694', fontSize: 11, marginBottom: 2 }}>{label}</div>
      <div>{value}</div>
    </div>
  );
}

function ActivationStep({ status, label, detail, duration }) {
  const done = status === 'done';
  const active = status === 'active';
  return (
    <div style={{ display: 'flex', gap: 14, alignItems: 'flex-start' }}>
      <div
        style={{
          width: 26,
          height: 26,
          borderRadius: '50%',
          flexShrink: 0,
          background: done
            ? 'rgba(16,185,129,0.18)'
            : active
            ? 'rgba(212,160,23,0.2)'
            : 'rgba(255,255,255,0.05)',
          border: done
            ? '1px solid rgba(16,185,129,0.4)'
            : active
            ? '1px solid var(--asher-gold)'
            : '1px solid rgba(255,255,255,0.1)',
          display: 'grid',
          placeItems: 'center',
          marginTop: 2,
          color: done ? '#34d399' : active ? 'var(--asher-gold-light)' : '#7d8694',
          transition: 'background 0.3s, border-color 0.3s, color 0.3s',
        }}
      >
        {done ? (
          <Icon name="check" size={13} />
        ) : active ? (
          <span
            style={{
              width: 8,
              height: 8,
              borderRadius: '50%',
              background: 'var(--asher-gold-light)',
              animation: 'none',
            }}
          />
        ) : (
          <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'currentColor' }} />
        )}
      </div>
      <div style={{ flex: 1 }}>
        <div
          style={{
            fontSize: 13.5,
            fontWeight: 600,
            color: done || active ? '#fff' : '#7d8694',
            marginBottom: 3,
            transition: 'color 0.3s',
          }}
        >
          {label}
        </div>
        <div style={{ fontSize: 11.5, color: '#7d8694', lineHeight: 1.5 }}>{detail}</div>
        {active && <ActivationFillBar duration={duration} />}
      </div>
    </div>
  );
}

// CSS-motion progress bar: animates 0 → 100% over the step's duration on mount.
function ActivationFillBar({ duration }) {
  const [w, setW] = React.useState(0);
  React.useEffect(() => {
    // double-rAF so the transition fires from the 0 baseline
    const r1 = requestAnimationFrame(() => {
      const r2 = requestAnimationFrame(() => setW(100));
      return r2;
    });
    return () => cancelAnimationFrame(r1);
  }, []);
  return (
    <div
      style={{
        marginTop: 8,
        height: 4,
        background: 'rgba(255,255,255,0.06)',
        borderRadius: 2,
        overflow: 'hidden',
      }}
    >
      <div
        style={{
          width: `${w}%`,
          height: '100%',
          background: 'var(--asher-gold)',
          transition: `width ${duration}ms linear`,
        }}
      />
    </div>
  );
}

window.ScreenActivation = ScreenActivation;


/* ===== screen-confighub.jsx (ConfigurationHub / B1 — post-ceremony, all 5 workplans) ===== */
/* global React, Icon, AIMS_PROGRAMS */
// ───────────────────────────────────────────────────────────────────────────
// ConfigurationHub  ·  B1  ·  the screen where you see all the workplans together
//
// Wireframe port of the product's Blueprint Workplans view (coreapp
// ProgramDetail.vue + NextActionsCockpit.vue + CapabilityWorkplanCard.vue). An AIMS
// IS the aggregation of its Workplans — one per Capability Area (the 5-area spine).
// Reached post-ceremony and from the Studio landing's AIMS row / Hub link.
//
// Faithful to the product model: priority + phasing come from the Blueprint (which is
// computed from the Stage A wizard answers), surfaced here as each workplan's `phase`
// (NOW/NEXT/LATER) and the NOW-band `rank` that drives the "next 3 actions" cockpit.
// This renders the freshly-hydrated state: everything `planned`, 0/5 steps, AI
// recommendations pending — matching what the product shows right after hydration.
//
// DROP-IN CONTRACT (house style): Browser-Babel + window globals, no import/export;
// React + Icon + AIMS_PROGRAMS assumed global (shell.jsx, earlier in concat order);
// styling via styles.css classes + CSS-var tokens; attaches window.ConfigurationHub.
// Tabs other than Workplans are light stubs (the product has Phase / Priority /
// Process Map views; those are a later port).
//
//   <ConfigurationHub setup={…} aims={program} onOpenArea={(w)=>…} onBack={()=>…} />
// ───────────────────────────────────────────────────────────────────────────

const PHASE_BADGE = {
  now:   { label: 'NOW',   fg: 'var(--asher-gold-dark)',   bg: 'var(--asher-cream)' },
  next:  { label: 'NEXT',  fg: 'var(--asher-grey-medium)', bg: 'var(--asher-grey-bg)' },
  later: { label: 'LATER', fg: 'var(--asher-grey-light)',  bg: '#f4f5f7' },
};

const CONFIG_TABS = [
  { id: 'workplans', label: 'Workplans', icon: 'grid' },
  { id: 'phase', label: 'Phase', icon: 'calendar' },
  { id: 'priority', label: 'Priority', icon: 'flag' },
  { id: 'processmap', label: 'Process Map', icon: 'sitemap', tag: 'NEW' },
];

function ConfigurationHub({ setup, aims, onOpenArea, onBack }) {
  const program = aims || (typeof AIMS_PROGRAMS !== 'undefined' && AIMS_PROGRAMS[0]) || { name: 'Your AI Management System', version: 1, workplans: [] };
  const workplans = program.workplans || [];
  const [tab, setTab] = React.useState('workplans');

  // Free tier: the whole AIMS is shown (the plan is free & complete), but only the
  // top-ranked NOW workplan(s) are configurable — the rest are locked-but-visible.
  // `FREE_TIER.workplans` is the single source of truth (1 on the free tier).
  const FREE = (window.DATA && window.DATA.FREE_TIER) || { workplans: 1 };
  // Entitled (configurable) workplan on the free tier: the one FLAGGED `free` in the
  // AIMS model — Strategy & Leadership, the foundational area carrying the Scope
  // Applicability and AI Governance policies the rest build on — capped at
  // FREE_TIER.workplans. Fallback (no flag): the top-ranked NOW workplan(s).
  const flaggedFree = workplans.filter((w) => w.free);
  const freeList = (flaggedFree.length
    ? flaggedFree
    : workplans.filter((w) => w.phase === 'now' && w.rank).sort((a, b) => a.rank - b.rank)
  ).slice(0, FREE.workplans);
  const freeIds = new Set(freeList.map((w) => w.id));
  const isFree = (w) => freeIds.has(w.id);
  const lockedCount = workplans.length - freeIds.size;

  // Next actions = the configurable (free) workplans — what you can act on right now.
  const nextActions = freeList;

  return (
    <div className="app-shell">
      <div className="app-body" style={{ overflow: 'auto' }}>
        {/* ── AIMS-anchored blueprint header ───────────────────────────────── */}
        <div className="section-title-bar">
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 24 }}>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 6, flexWrap: 'wrap' }}>
                <h1 style={{ margin: 0 }}>Blueprint: {program.blueprintName || program.name}</h1>
                <span className="chip chip-gold" style={{ fontSize: 10 }}>AIMS</span>
                {program.prm && <span className="chip" style={{ fontSize: 10 }}><Icon name="verified" size={10} /> Anchored to {program.prm}</span>}
                {program.recommendations === 'pending' && (
                  <span className="chip" style={{ fontSize: 10, color: 'var(--asher-grey-medium)' }}><Icon name="clock" size={10} /> AI Recommendations Pending</span>
                )}
              </div>
              <p className="subtitle">From · {program.capabilityAreas || workplans.length} capability areas · AIMS v{program.version}{program.lastUpdated ? ` · Last updated ${program.lastUpdated}` : ''}</p>
            </div>
            <div style={{ textAlign: 'right', flexShrink: 0, maxWidth: 280 }}>
              <button className="btn btn-secondary" style={{ marginBottom: 8 }}><Icon name="sparkles" size={13} /> Get AI Recommendations</button>
              {onBack && <button className="btn btn-ghost btn-sm" style={{ marginLeft: 8 }} onClick={onBack}><Icon name="chevronLeft" size={11} /> Studio</button>}
              <p style={{ fontSize: 10.5, color: 'var(--asher-grey-light)', margin: '4px 0 0', lineHeight: 1.45 }}>
                Populates suggested governance settings, compliance coverage estimates, and method options across all workplans. Your accepted choices are preserved.
              </p>
            </div>
          </div>

          {/* Tabs */}
          <div style={{ display: 'flex', gap: 4, marginTop: 16, borderBottom: '1px solid #e7e3d8' }}>
            {CONFIG_TABS.map((t) => {
              const on = tab === t.id;
              return (
                <button key={t.id} onClick={() => setTab(t.id)} style={{
                  display: 'flex', alignItems: 'center', gap: 6, padding: '9px 14px', background: 'transparent', border: 'none', borderBottom: `2px solid ${on ? 'var(--asher-gold)' : 'transparent'}`, cursor: 'pointer', fontFamily: 'inherit', fontSize: 13, fontWeight: 600, color: on ? 'var(--asher-grey-dark)' : 'var(--asher-grey-light)', marginBottom: -1 }}>
                  <Icon name={t.icon} size={13} /> {t.label}
                  {t.tag && <span style={{ fontSize: 8, fontWeight: 800, color: 'var(--operate-green-dark)', background: 'var(--operate-green-bg)', padding: '1px 5px', borderRadius: 4, letterSpacing: '0.04em' }}>{t.tag}</span>}
                </button>
              );
            })}
          </div>
        </div>

        <div style={{ padding: '24px 36px 40px', maxWidth: 1240, margin: '0 auto', width: '100%' }}>
          {tab === 'workplans' ? (
            <>
              {/* ── Free-allowance banner ────────────────────────────────────── */}
              <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 16px', marginBottom: 22, background: 'rgba(228,170,35,0.07)', border: '1px solid rgba(228,170,35,0.22)', borderRadius: 10 }}>
                <Icon name="bolt" size={16} color="var(--asher-gold-dark)" />
                <div style={{ flex: 1, fontSize: 12.5, color: 'var(--asher-grey-dark)', lineHeight: 1.5 }}>
                  <strong>Free allowance:</strong> configure {FREE.workplans} of {workplans.length} workplans. Your full AIMS spans all {workplans.length} capability areas — planned and visible below; the other {lockedCount} unlock with a plan.
                </div>
              </div>

              {/* ── Your next action(s) (START HERE) ─────────────────────────── */}
              {nextActions.length > 0 && (
                <div style={{ border: '1px solid rgba(228,170,35,0.4)', background: 'linear-gradient(135deg,#fffdf6,#faf4e3)', borderRadius: 12, padding: 20, marginBottom: 26 }}>
                  <div className="eyebrow" style={{ color: 'var(--asher-gold-dark)', marginBottom: 4 }}><Icon name="compass" size={12} /> Start here</div>
                  <h2 style={{ margin: '0 0 4px', fontSize: 19 }}>Your next {nextActions.length === 1 ? 'action' : `${nextActions.length} actions`}</h2>
                  <p style={{ margin: '0 0 16px', fontSize: 12.5, color: 'var(--asher-grey-medium)' }}>{nextActions.length === 1 ? 'This workplan is ready for your attention now — open it to begin configuring.' : 'These workplans are ready for your attention now — click any one to begin configuring.'}</p>
                  <div style={{ display: 'grid', gridTemplateColumns: `repeat(${nextActions.length}, 1fr)`, gap: 12 }}>
                    {nextActions.map((w, i) => (
                      <button key={w.id} onClick={() => onOpenArea && onOpenArea(w)} className="asher-card" style={{ textAlign: 'left', cursor: 'pointer', padding: 15, borderLeft: `4px solid ${w.color}`, display: 'flex', flexDirection: 'column', gap: 6 }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                          <span style={{ width: 20, height: 20, borderRadius: '50%', background: 'var(--asher-gold)', color: '#fff', fontSize: 11, fontWeight: 800, display: 'grid', placeItems: 'center', flexShrink: 0 }}>{w.rank || (i + 1)}</span>
                          <span className="eyebrow" style={{ fontSize: 9, color: 'var(--asher-gold-dark)' }}>Begin configuration</span>
                          <Icon name="arrowRight" size={13} color="var(--asher-grey-light)" style={{ marginLeft: 'auto' }} />
                        </div>
                        <div style={{ fontSize: 14, fontWeight: 700, color: 'var(--asher-grey-dark)' }}>{w.short}</div>
                        <div style={{ fontSize: 11, color: 'var(--asher-grey-light)' }}>Status: {w.status} · {w.setupDone}/{w.setupTotal} steps</div>
                      </button>
                    ))}
                  </div>
                </div>
              )}

              {/* ── Implementation Workplans grid (the 5-area spine) ─────────── */}
              <h3 style={{ fontSize: 16, margin: '0 0 4px' }}>Implementation Workplans</h3>
              <p style={{ fontSize: 12, color: 'var(--asher-grey-light)', margin: '0 0 16px' }}>The numbered card is configurable on your free allowance. The rest are part of your plan and kept visible for context — they unlock with a paid plan, no rework.</p>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: 16 }}>
                {workplans.map((w) => {
                  const ph = PHASE_BADGE[w.phase] || PHASE_BADGE.later;
                  const free = isFree(w);
                  const Tag = free ? 'button' : 'div';
                  return (
                    <Tag key={w.id} onClick={free ? () => onOpenArea && onOpenArea(w) : undefined} className="asher-card" style={{ textAlign: 'left', cursor: free ? 'pointer' : 'default', padding: 0, overflow: 'hidden', borderTop: `4px solid ${w.color}`, opacity: free ? 1 : 0.66 }}>
                      <div style={{ padding: '14px 16px 12px' }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
                          {free && w.rank && <span style={{ width: 18, height: 18, borderRadius: '50%', background: 'var(--asher-gold)', color: '#fff', fontSize: 10, fontWeight: 800, display: 'grid', placeItems: 'center' }}>{w.rank}</span>}
                          <span className="eyebrow" style={{ fontSize: 9 }}>Workplan</span>
                          {free
                            ? <span style={{ marginLeft: 'auto', fontSize: 9.5, fontWeight: 800, letterSpacing: '0.05em', color: ph.fg, background: ph.bg, padding: '2px 8px', borderRadius: 999 }}>{ph.label}</span>
                            : <span className="chip chip-locked" style={{ marginLeft: 'auto', fontSize: 9 }}><Icon name="lock" size={9} /> Plan</span>}
                        </div>
                        <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--asher-grey-dark)', lineHeight: 1.25 }}>{w.name}</div>
                        <div style={{ fontSize: 11.5, color: 'var(--asher-grey-light)', marginTop: 2 }}>{w.mix}</div>
                      </div>
                      <div style={{ padding: '10px 16px', borderTop: '1px solid #f1efe8', display: 'flex', alignItems: 'center', gap: 8 }}>
                        <span style={{ fontSize: 11.5, color: 'var(--asher-grey-medium)' }}>Setup</span>
                        <span style={{ marginLeft: 'auto', fontSize: 11.5, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>{w.setupDone} of {w.setupTotal} steps</span>
                      </div>
                      <div style={{ padding: '9px 16px', borderTop: '1px solid #f1efe8', display: 'flex', alignItems: 'center', gap: 14, fontSize: 11, color: 'var(--asher-grey-light)' }}>
                        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}><Icon name="file" size={11} /> Docs <strong style={{ color: 'var(--asher-grey-dark)' }}>{w.docs}</strong></span>
                        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}><Icon name="flow" size={11} /> Flows <strong style={{ color: 'var(--asher-grey-dark)' }}>{w.flows > 0 ? w.flows : '—'}</strong></span>
                        <Icon name={free ? 'arrowRight' : 'lock'} size={12} style={{ marginLeft: 'auto' }} />
                      </div>
                      <div style={{ padding: '0 16px 12px' }}>
                        <span className="chip" style={{ fontSize: 10, background: '#eef1f6', color: 'var(--asher-grey-medium)' }}>{free ? w.status : 'Included with full program'}</span>
                      </div>
                    </Tag>
                  );
                })}
              </div>

              {/* ── Compliance Coverage ───────────────────────────────────────── */}
              {program.compliance && program.compliance.length > 0 && (
                <div style={{ marginTop: 30 }}>
                  <h3 style={{ fontSize: 15, margin: '0 0 12px' }}>Compliance Coverage</h3>
                  {program.compliance.map((c) => (
                    <div key={c.framework} style={{ marginBottom: 12 }}>
                      <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, color: 'var(--asher-grey-medium)', marginBottom: 5 }}>
                        <span>{c.framework}</span>
                        <span style={{ fontWeight: 700, color: 'var(--asher-grey-dark)' }}>{c.pct}%</span>
                      </div>
                      <div style={{ height: 8, background: 'var(--asher-grey-bg)', borderRadius: 4, overflow: 'hidden' }}>
                        <div style={{ width: `${c.pct}%`, height: '100%', background: 'var(--operate-green)' }} />
                      </div>
                    </div>
                  ))}
                </div>
              )}
            </>
          ) : tab === 'processmap' ? (
            <ProcessMapView />
          ) : (
            <div className="asher-card" style={{ padding: '48px 24px', textAlign: 'center', color: 'var(--asher-grey-light)' }}>
              <Icon name={(CONFIG_TABS.find((t) => t.id === tab) || {}).icon || 'grid'} size={26} color="#cfcabd" />
              <div style={{ marginTop: 10, fontSize: 13 }}>The {(CONFIG_TABS.find((t) => t.id === tab) || {}).label} view is part of the hi-fi B1 port — not wired in this wireframe yet.</div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ───────────────────────────────────────────────────────────────────────────
// ProcessMapView  ·  the "Process Map" tab — a STATIC PRM instantiation diagram
// for the Strategy & Leadership capability area. A faithful wireframe of the
// product's process-map render: the capability node governs its policy/process
// domains, each carries its blueprint counts, and the right-rail "instantiation
// overlay" reports how many PRM nodes are live in this AIMS. Static for now (the
// other capability pills are inert); the live version derives from the blueprint.
// ───────────────────────────────────────────────────────────────────────────
const PM_W = 912, PM_H = 560;
const PM_GOLD = 'linear-gradient(160deg,#ecdc8f,#e3cf76)';
const PM_LINE = '#aab59c';

// Nodes laid out on a fixed PM_W×PM_H canvas (gold = capability/process domain,
// white = policy domain; green check = instantiated in this AIMS).
const PM_NODES = [
  { id: 'cap', kind: 'capability', kindLabel: 'CAPABILITY AREA', tag: '', name: 'Strategy & Leadership', meta: '2 process · 4 policy domains', x: 64, y: 20, w: 320, h: 72, check: true },
  { id: 'agp', kind: 'policy', kindLabel: 'POLICY', tag: 'AGP', name: 'AI Governance Policy', meta: '2 domains in cluster', x: 120, y: 166, w: 206, h: 72, check: true },
  { id: 'sap', kind: 'policy', kindLabel: 'POLICY', tag: 'SAP', name: 'Strategy & Alignment Policy', meta: '2 domains in cluster', x: 352, y: 166, w: 214, h: 72, check: true },
  { id: 'org', kind: 'process', kindLabel: 'PROCESS', tag: 'ORG', name: 'Organization Strategy', meta: 'BP 4 · SOP 4', x: 244, y: 296, w: 192, h: 82, check: true },
  { id: 'efp', kind: 'policy', kindLabel: 'POLICY', tag: 'EFP', name: 'Ethics & Fairness Policy', meta: '1 domain in cluster', x: 462, y: 296, w: 200, h: 72, check: true },
  { id: 'sep', kind: 'policy', kindLabel: 'POLICY', tag: 'SEP', name: 'Stakeholder Engagement Policy', meta: '1 domain in cluster', x: 684, y: 296, w: 204, h: 72, check: true },
  { id: 'ovr', kind: 'process', kindLabel: 'PROCESS', tag: 'OVR', name: 'Oversight & Steering', meta: 'BP 2 · SOP 3', x: 360, y: 464, w: 192, h: 82, check: true },
];
// Orthogonal connectors. Solid = "governs", dashed = "feeds into".
const PM_CONN = [
  { pts: [[224, 92], [224, 134]] },
  { pts: [[223, 134], [459, 134]] },
  { pts: [[223, 134], [223, 166]] },
  { pts: [[459, 134], [459, 166]] },
  { pts: [[326, 202], [352, 202]] },
  { pts: [[410, 238], [410, 268], [340, 268], [340, 296]] },
  { pts: [[500, 238], [500, 272], [562, 272], [562, 296]] },
  { pts: [[540, 238], [540, 280], [786, 280], [786, 296]] },
  { pts: [[340, 378], [340, 426], [456, 426], [456, 464]], dashed: true },
];
const PM_CAPS = [
  { label: 'Strategy & Leadership', color: '#f4d35e', active: true },
  { label: 'Risk, Ethics & Compliance', color: '#f4a4a4' },
  { label: 'Data, Model & System Lifecycle', color: '#a4d4d4' },
  { label: 'Operations & Workflow Integration', color: '#d4d4a4' },
  { label: 'Vendor, Partners & Ecosystem', color: '#d4b4f4' },
];

function PMNode({ n }) {
  const filled = n.kind === 'capability' || n.kind === 'process';
  return (
    <div style={{ position: 'absolute', left: n.x, top: n.y, width: n.w, height: n.h, boxSizing: 'border-box', padding: '10px 13px', borderRadius: 10, background: filled ? PM_GOLD : '#fff', border: filled ? '1px solid #d4bd5b' : '1px solid #e3e6df', boxShadow: '0 1px 2px rgba(0,0,0,0.04)' }}>
      <div style={{ fontSize: 9, fontWeight: 800, letterSpacing: '0.06em', textTransform: 'uppercase', color: filled ? '#7a6a2a' : 'var(--asher-grey-light)' }}>
        {n.kindLabel}{n.tag ? <span style={{ marginLeft: 6, color: filled ? '#5d5120' : 'var(--asher-grey-medium)' }}>{n.tag}</span> : null}
      </div>
      <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--asher-grey-dark)', lineHeight: 1.2, marginTop: 2 }}>{n.name}</div>
      <div style={{ fontSize: 10.5, color: filled ? '#8a7a3a' : 'var(--asher-grey-light)', marginTop: 3 }}>{n.meta}</div>
      {n.check && (
        <span style={{ position: 'absolute', right: -7, bottom: -7, width: 18, height: 18, borderRadius: '50%', background: '#fff', border: '1px solid #cfe3d3', display: 'grid', placeItems: 'center' }}>
          <Icon name="check" size={11} color="#4ea36e" />
        </span>
      )}
    </div>
  );
}

const PMLegendSwatch = ({ bg, border }) => (
  <span style={{ width: 13, height: 13, borderRadius: 3, background: bg, border: border || 'none', display: 'inline-block', flexShrink: 0 }} />
);

function ProcessMapView() {
  return (
    <>
      {/* Capability-area selector (Strategy active; others static) */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 16, flexWrap: 'wrap' }}>
        <span className="eyebrow" style={{ fontSize: 9.5, color: 'var(--asher-grey-light)' }}>Capability area</span>
        {PM_CAPS.map((c) => (
          <span key={c.label} style={{ display: 'inline-flex', alignItems: 'center', gap: 7, padding: '6px 12px', borderRadius: 999, fontSize: 12, fontWeight: 600, cursor: 'default',
            background: c.active ? '#262b38' : '#fff', color: c.active ? '#fff' : 'var(--asher-grey-medium)', border: c.active ? '1px solid #262b38' : '1px solid #e7e3d8' }}>
            <PMLegendSwatch bg={c.color} />
            {c.label}
          </span>
        ))}
      </div>

      <div style={{ display: 'flex', gap: 18, alignItems: 'flex-start' }}>
        {/* Diagram canvas */}
        <div className="asher-card" style={{ flex: 1, minWidth: 0, padding: 18, overflowX: 'auto' }}>
          <div style={{ position: 'relative', width: PM_W, height: PM_H, margin: '0 auto' }}>
            <svg width={PM_W} height={PM_H} style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
              {PM_CONN.map((c, i) => (
                <polyline key={i} points={c.pts.map((p) => p.join(',')).join(' ')} fill="none" stroke={PM_LINE} strokeWidth="1.5" strokeDasharray={c.dashed ? '5 4' : undefined} />
              ))}
            </svg>
            {PM_NODES.map((n) => <PMNode key={n.id} n={n} />)}
          </div>

          {/* Bottom legend */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 18, flexWrap: 'wrap', marginTop: 14, paddingTop: 12, borderTop: '1px solid #f1efe8', fontSize: 11, color: 'var(--asher-grey-medium)' }}>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><PMLegendSwatch bg="#ecdc8f" border="1px solid #d4bd5b" /> Capability area</span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><PMLegendSwatch bg="#fff" border="1px solid #e3e6df" /> Policy domain</span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><PMLegendSwatch bg="#ecdc8f" border="1px solid #d4bd5b" /> Process domain</span>
            <span style={{ marginLeft: 'auto', display: 'inline-flex', alignItems: 'center', gap: 7 }}><svg width="26" height="8"><line x1="0" y1="4" x2="26" y2="4" stroke={PM_LINE} strokeWidth="1.5" /></svg> governs</span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 7 }}><svg width="26" height="8"><line x1="0" y1="4" x2="26" y2="4" stroke={PM_LINE} strokeWidth="1.5" strokeDasharray="5 4" /></svg> feeds into</span>
          </div>
        </div>

        {/* Instantiation overlay rail */}
        <div className="asher-card" style={{ width: 270, flexShrink: 0, padding: 18 }}>
          <div className="eyebrow" style={{ fontSize: 9.5, color: 'var(--asher-grey-light)', marginBottom: 6 }}>Instantiation overlay</div>
          <h3 style={{ fontSize: 16, margin: '0 0 3px' }}>Strategy &amp; Leadership</h3>
          <p style={{ fontSize: 12, color: 'var(--asher-grey-medium)', margin: '0 0 14px' }}><strong style={{ color: 'var(--asher-grey-dark)' }}>7 of 7</strong> nodes instantiated</p>

          <div style={{ display: 'flex', flexDirection: 'column', gap: 9, marginBottom: 14 }}>
            {[['Instantiated', 7, '#ecdc8f', '1px solid #d4bd5b'], ['Canonical, not yet', 0, '#fff', '1px solid #d8dcd3'], ['Deferred', 0, '#e7e7e2', '1px solid #d8dcd3']].map(([label, n, bg, border]) => (
              <div key={label} style={{ display: 'flex', alignItems: 'center', gap: 9, fontSize: 12 }}>
                <PMLegendSwatch bg={bg} border={border} />
                <span style={{ flex: 1, color: 'var(--asher-grey-dark)', fontWeight: 600 }}>{label}</span>
                <span style={{ color: 'var(--asher-grey-medium)' }}>{n}</span>
              </div>
            ))}
          </div>

          <p style={{ fontSize: 11.5, color: 'var(--asher-grey-light)', lineHeight: 1.55, margin: '0 0 14px' }}>
            Click any process node to drill in for Asher's per-node guidance. Solid swatch + ✓ = part of your AIMS now. Dashed white = available in the PRM but not yet configured for you.
          </p>
          <div style={{ paddingTop: 12, borderTop: '1px solid #f1efe8' }}>
            <button className="btn btn-ghost btn-sm" style={{ padding: 0, color: 'var(--asher-gold-dark)' }}>Open in PRM Explorer <Icon name="external" size={12} /></button>
          </div>
        </div>
      </div>
    </>
  );
}

window.ConfigurationHub = ConfigurationHub;


/* ===== screen-aiselect.jsx (ScreenAISelect / AI-Select Studio landing — Early Preview, links out to HTA Lab) ===== */
/* global React, Icon */
/* =============================================================================
 * ScreenAISelect — Forge "AI-Select Studio" landing (Early Preview)
 * -----------------------------------------------------------------------------
 * FRAMING (confirm before integrating):
 *   AI-Select Studio is the PRE-ACQUISITION AI strategy & selection surface:
 *   which clinical AI to adopt, health-technology assessment (HTA), and use-case
 *   selection. It is distinct from the Governance Studio, which governs AI you
 *   already run. This page does not govern; it helps choose and value.
 *
 * WHAT THE "SIMULATION" SIMULATES:
 *   Health-technology assessment of a candidate use case — cost-effectiveness
 *   (Markov cohort: lifetime costs, QALYs) and operational value (discrete-event
 *   simulation: queueing, wait-time, throughput). This landing is presentational
 *   only; the actual runs happen in the external HTA Lab. First pass is static +
 *   props-driven (no lifted app state required). Component-local UI state only.
 *
 * HOUSE STYLE COMPLIANCE:
 *   · No module syntax. React/hooks are globals. Exposed on window (bottom).
 *   · One self-contained component family in this file. No forward references.
 *   · Reads no globals mandatorily; uses `Icon` global from app/shell.jsx.
 *   · Styling via app.css classes + --asher-* CSS-var tokens. Inline style is
 *     layout-only (matches app/studio-landing.jsx convention).
 *   · No new deps / CDN / fetch. Offline-safe. Outbound targets are plain
 *     target="_blank" anchors that never load on mount — the page renders fully
 *     even if both destinations are unreachable (booth wifi / R&D down).
 *
 * PROPS CONTRACT (all overridable; PTMC defaults so it self-demos):
 *   scenario      'customer' | 'prospect'   seeded demo state (default 'customer')
 *   org           string                    organization name
 *   user          { name, initials }        current user
 *   library       { useCases, domains, modalities }  curated-library counts
 *   shortlist     { name, org, count, items[] }      Stage-1 selection summary
 *   models        Model[]                   bounded validated-model set (see shape)
 *   methods       { id: Method }            method dictionary
 *   comingSoon    string[]                  expansion-path use-case names
 *   methodRec     boolean                   show per-model method + rationale (default true)
 *   futureStages  'ghosted' | 'solid'       Source/Validate dimming (default 'ghosted')
 *   preview       boolean                   render the Early Preview badge (default true)
 *   htaLabUrl     string  "Open the HTA Lab preview" target (hero + portfolio header)
 *                         default 'https://htalab.ashmatics.com'
 *   simUrl        string  "Launch Simulation" base (model cards; gets ?model=<id>)
 *                         default 'https://htalab.ashmatics.com'. Decoupled from
 *                         htaLabUrl so the sims can point at different instances if
 *                         needed.
 *   htaLabDevUrl  string  outbound "R&D dev instance" link. Default null = the link
 *                         is HIDDEN; pass a URL to show it (no stable instance yet).
 *   onNav         (navId: string) => void   optional; fired by in-page CTAs
 *
 * INTEGRATION METADATA (for app.jsx / shell.jsx wiring — not my job to wire):
 *   · Sidebar placement (recommended): new group "AI Strategy & Selection".
 *       nav id assumed:  'ai-select'    (active-tab key)
 *       in-page CTA nav ids fired via onNav: 'ai-select-browse'
 *   · Globals read: `Icon` (required, from shell). No window.DATA/ASSESS/REG needed.
 *   · Free-tier metering: none introduced. If desired, gate "Launch Simulation"
 *       against window.DATA.FREE_TIER and pass a `metered` prop (not built here).
 *   · Net-new state keys: NONE lifted. All state is component-local UI
 *       (gallery scroll). methodRec/futureStages arrive as props.
 *   · Seeded scenarios: `scenario` prop. 'prospect' vs 'customer' may swap
 *       shortlist/org copy upstream; defaults shown are the 'customer' (PTMC) seed.
 *   · Renders the ROUTE BODY only (section title bar + content). The shell owns
 *       the top bar / sidebar chrome.
 * ============================================================================ */

// ── PTMC default data (baked so the page self-demos standalone) ──────────────
const AISELECT_METHODS = {
  'pure-des': {
    id: 'pure-des', label: 'Pure DES', full: 'Discrete-Event Simulation', tone: 'info', icon: 'flow',
    blurb: 'Models patient flow, queues and resources. Best for turnaround time, throughput and staffing impact.',
  },
  'des-stateful': {
    id: 'des-stateful', label: 'DES-Stateful', full: 'Stateful Discrete-Event simulation', tone: 'gold', icon: 'sitemap',
    blurb: 'Per-patient state machine with decision branches. Best for AI-in-the-loop pathways that fork on a model output.',
  },
  'markov': {
    id: 'markov', label: 'Markov HTA', full: 'Markov Cost-Effectiveness', tone: 'success', icon: 'trend',
    blurb: 'Health states, transition probabilities and QALYs. Best for long-run clinical and economic value.',
  },
};

const AISELECT_MODELS = [
  {
    id: 'stroke-lvo', title: 'Ischemic Stroke LVO Detection', domain: 'Neuro · Acute stroke',
    modality: ['CT', 'CTA'], method: 'markov', tag: 'Validated',
    why: 'Lifetime costs and QALYs call for a Markov cohort, not a single-visit model.',
    fields: [
      { label: 'Evaluates', text: 'AI-aided detection of large-vessel occlusions (LVOs) in acute stroke cases.' },
      { label: 'Based on', text: 'Leeuwen et al. (2021), an early health-technology assessment of AI-aided vessel-occlusion detection.' },
      { label: 'Key findings', text: 'AI-aided LVO detection can be cost-saving and improve outcomes if it reduces missed LVOs eligible for intra-arterial thrombectomy.' },
    ],
  },
  {
    id: 'opportunistic-ct', title: 'Opportunistic CT Screening', domain: 'Preventive · Body CT',
    modality: ['CT'], method: 'markov', tag: 'Validated',
    why: 'Long-run cost-effectiveness across conditions, so a Markov cohort fits.',
    fields: [
      { label: 'Evaluates', text: 'AI-based opportunistic screening on routine abdominal CT for:' },
    ],
    bullets: [
      'Cardiovascular disease (abdominal aortic calcification)',
      'Osteoporosis (L1 attenuation measurement)',
      'Sarcopenia (L3 skeletal-muscle index)',
      'Combined multi-condition screening',
    ],
    fieldsAfter: [
      { label: 'Based on', text: 'Pickhardt et al. (2023), cost-effectiveness of AI-based opportunistic CT screening.' },
      { label: 'Key findings', text: 'Opportunistic screening can be cost-effective by surfacing incidental disease from scans already acquired.' },
    ],
  },
  {
    id: 'qucad', title: 'QuCAD: Radiology Workflow Simulation', domain: 'Operations · Worklist',
    modality: ['Workflow'], method: 'pure-des', tag: 'Validated',
    why: 'Queue dynamics and wait-time savings call for a discrete-event flow model.',
    fields: [
      { label: 'Evaluates', text: 'Wait-time-saving benefits of computer-aided triage and notification (CADt) devices in radiology, via discrete-event simulation.' },
      { label: 'Application', text: 'Quantifies workflow improvements from AI triage that prioritizes urgent cases based on binary algorithm outputs.' },
      { label: 'Methodology', text: 'Python-based discrete-event simulation modeling patient flow, queue dynamics, and radiologist workflow with and without CADt devices.' },
    ],
  },
];

const AISELECT_COMING = ['Mammography AI assistant', 'Lung nodule screening', 'ICU deterioration monitoring', 'Chest X-ray triage'];

const AISELECT_LIBRARY = { useCases: 40, domains: 12, modalities: 6 };

const AISELECT_SHORTLIST = {
  name: 'PTMC Radiology shortlist', org: 'Validated in HTA Lab', count: 3,
  items: ['Ischemic stroke LVO detection', 'Opportunistic CT screening', 'CADt worklist prioritization'],
};

// ── Small primitives ────────────────────────────────────────────────────────
const AISELECT_CHIPCLASS = { info: 'chip-info', gold: 'chip-gold', success: 'chip-success' };

const MethodChip = ({ method, size }) => (
  <span className={'chip ' + (AISELECT_CHIPCLASS[method.tone] || 'chip')} style={{ fontSize: size || 10.5 }}>
    <Icon name={method.icon} size={size ? 11 : 10} /> {method.label}
  </span>
);

const NewTabCue = ({ light }) => (
  <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 11, color: light ? 'rgba(255,255,255,0.6)' : 'var(--asher-grey-light)' }}>
    <Icon name="external" size={11} /> Opens in a new tab
  </span>
);

const AISelectField = ({ label, text }) => (
  <p style={{ margin: '0 0 9px', fontSize: 12.5, color: 'var(--asher-grey-medium)', lineHeight: 1.5 }}>
    <span style={{ fontWeight: 700, color: 'var(--asher-grey-dark)' }}>{label}: </span>{text}
  </p>
);

// NOTE (integration): the breadcrumb is supplied by AppShell's topbar
// (PolicyBreadcrumb, driven by SHELL_META), exactly as every other screen does it —
// studio-landing renders none of its own. So the artifact's in-file Breadcrumb is
// dropped here to avoid a doubled crumb, and the outer wrapper is the house
// `.app-shell > .app-body` (which gives the screen its min-height + background),
// not the artifact's standalone `.ai-select-screen`.
const aiselectProgRow = { display: 'flex', alignItems: 'center', gap: 10, padding: '9px 4px', width: '100%', background: 'transparent', border: 'none', borderTop: '1px solid rgba(0,0,0,0.05)', cursor: 'pointer', fontFamily: 'inherit' };

// ── HERO 1 · SELECT (active, gold) ──────────────────────────────────────────
const SelectSurface = ({ library, shortlist, onBrowse }) => (
  <div style={{ background: 'linear-gradient(135deg,#fffdf6 0%,#faf4e3 100%)', border: '2px solid var(--asher-gold)', borderRadius: 14, padding: 24, position: 'relative', overflow: 'hidden' }}>
    <div style={{ position: 'absolute', top: -40, right: -40, width: 150, height: 150, background: 'rgba(228,170,35,0.08)', borderRadius: '50%' }} />
    <div style={{ position: 'relative' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
        <Icon name="aperture" size={20} color="var(--asher-gold-dark)" />
        <span className="eyebrow" style={{ color: 'var(--asher-gold-dark)' }}>Stage 1 · Select</span>
      </div>
      <h2 style={{ margin: '0 0 6px', fontSize: 22, fontWeight: 700 }}>Choose the right use cases</h2>
      <p style={{ margin: '0 0 14px', fontSize: 13, color: 'var(--asher-grey-medium)', lineHeight: 1.55 }}>
        Browse Asher's curated, evidence-based library of clinical AI use cases. Filter by specialty, data modality and value, then shortlist the ones worth modeling.
      </p>

      <div style={{ display: 'flex', gap: 14, marginBottom: 14 }}>
        {[[library.useCases + '+', 'Use cases'], [library.domains, 'Clinical domains'], [library.modalities, 'Modalities']].map(([n, l]) => (
          <div key={l} style={{ display: 'flex', flexDirection: 'column' }}>
            <span style={{ fontSize: 22, fontWeight: 700, color: 'var(--asher-gold-dark)', fontFeatureSettings: '"tnum"' }}>{n}</span>
            <span style={{ fontSize: 11, color: 'var(--asher-grey-light)' }}>{l}</span>
          </div>
        ))}
      </div>

      <div style={{ background: 'rgba(255,255,255,0.72)', border: '1px solid rgba(228,170,35,0.22)', borderRadius: 9, padding: 13, marginBottom: 14 }}>
        <div className="eyebrow" style={{ fontSize: 9.5, marginBottom: 9 }}>1 shortlist · {shortlist.count} use cases</div>
        <button onClick={onBrowse} style={aiselectProgRow}>
          <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--asher-gold)' }} />
          <div style={{ flex: 1, minWidth: 0, textAlign: 'left' }}>
            <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>{shortlist.name} · {shortlist.org}</div>
            <div style={{ fontSize: 10.5, color: 'var(--asher-grey-light)' }}>{shortlist.items.join(' · ')}</div>
          </div>
          <Icon name="arrowRight" size={12} color="var(--asher-grey-light)" />
        </button>
      </div>

      <div style={{ display: 'flex', gap: 8 }}>
        <button className="btn btn-primary" style={{ flex: 1 }} onClick={onBrowse}>
          <Icon name="aperture" size={14} /> Browse use cases <Icon name="arrowRight" size={12} />
        </button>
        <button className="btn btn-secondary" onClick={onBrowse}><Icon name="plus" size={12} /> New</button>
      </div>
    </div>
  </div>
);

// ── HERO 2 · SIMULATE (active, dark — the engine / HTA Lab) ──────────────────
const SimulateSurface = ({ methods, htaLabUrl, htaLabDevUrl, onBrowse }) => (
  <div style={{ background: 'linear-gradient(160deg,#3a4650 0%,#2a333c 100%)', border: '2px solid var(--asher-grey-dark)', borderRadius: 14, padding: 24, position: 'relative', overflow: 'hidden' }}>
    <div style={{ position: 'absolute', top: -50, right: -50, width: 170, height: 170, background: 'rgba(228,170,35,0.10)', borderRadius: '50%' }} />
    <div style={{ position: 'relative' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
        <Icon name="activity" size={19} color="var(--asher-gold-light)" />
        <span className="eyebrow" style={{ color: 'var(--asher-gold-light)' }}>Stage 2 · Simulate</span>
        <span className="chip" style={{ marginLeft: 'auto', background: 'rgba(228,170,35,0.18)', color: 'var(--asher-gold-light)', fontSize: 10 }}><span className="dot" style={{ background: 'var(--asher-gold-light)' }} /> HTA Lab preview</span>
      </div>
      <h2 style={{ margin: '0 0 6px', fontSize: 22, fontWeight: 700, color: '#fff' }}>Estimate the value</h2>
      <p style={{ margin: '0 0 16px', fontSize: 13, color: 'rgba(255,255,255,0.74)', lineHeight: 1.55 }}>
        Run your shortlist through the simulation engine to size real, institution-specific value (throughput, turnaround, and cost-effectiveness) before you buy or build.
      </p>

      <div style={{ background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.12)', borderRadius: 9, padding: 13, marginBottom: 14 }}>
        <div className="eyebrow" style={{ fontSize: 9.5, marginBottom: 10, color: 'rgba(255,255,255,0.5)' }}>Three methods, one engine</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
          {Object.keys(methods).map((id) => {
            const m = methods[id];
            return (
              <div key={id} style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
                <span style={{ width: 28, height: 28, borderRadius: 7, background: 'rgba(228,170,35,0.16)', display: 'grid', placeItems: 'center', color: 'var(--asher-gold-light)', flexShrink: 0 }}><Icon name={m.icon} size={14} /></span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 600, color: '#fff' }}>{m.label} <span style={{ fontWeight: 400, color: 'rgba(255,255,255,0.5)', fontSize: 11 }}>· {m.full}</span></div>
                  <div style={{ fontSize: 11, color: 'rgba(255,255,255,0.6)', lineHeight: 1.45 }}>{m.blurb}</div>
                </div>
              </div>
            );
          })}
        </div>
      </div>

      <div style={{ display: 'flex', gap: 8, marginBottom: 10 }}>
        <a className="btn btn-primary" style={{ flex: 1, textDecoration: 'none' }} href={htaLabUrl} target="_blank" rel="noopener noreferrer">
          <Icon name="external" size={14} /> Open the HTA Lab preview
        </a>
        <button className="btn" style={{ background: 'transparent', color: '#fff', border: '1px solid rgba(255,255,255,0.28)' }} onClick={onBrowse}>View models</button>
      </div>
      {/* "R&D dev instance" link renders only when a htaLabDevUrl is supplied. It is
          hidden by default (no URL passed) until there's a real, stable instance to
          point at. To bring it back, pass htaLabDevUrl from the app. */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
        <NewTabCue light />
        {htaLabDevUrl && (
          <a href={htaLabDevUrl} target="_blank" rel="noopener noreferrer" style={{ fontSize: 11, color: 'rgba(255,255,255,0.55)', textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 5 }}>
            <Icon name="flask" size={11} /> R&amp;D dev instance
          </a>
        )}
      </div>
    </div>
  </div>
);

// ── Secondary surfaces ──────────────────────────────────────────────────────
const AISelectSecondarySurface = ({ icon, eyebrow, title, body, counts, cta, onCta, locked, lockNote, ghosted }) => (
  <div className="asher-card" style={{ padding: 18, position: 'relative', opacity: locked ? (ghosted ? 0.6 : 0.9) : 1 }}>
    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
      <Icon name={icon} size={16} color="var(--asher-grey-medium)" />
      <span className="eyebrow" style={{ fontSize: 10 }}>{eyebrow}</span>
      {locked && <span className="chip chip-locked" style={{ fontSize: 9.5, marginLeft: 'auto' }}><Icon name="lock" size={9} /> {lockNote}</span>}
    </div>
    <h3 style={{ margin: '0 0 6px', fontSize: 15 }}>{title}</h3>
    <p style={{ margin: '0 0 14px', fontSize: 12, color: 'var(--asher-grey-medium)', lineHeight: 1.5 }}>{body}</p>
    {counts && (
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 14 }}>
        {counts.map(([label, n]) => (
          <span key={label} style={{ fontSize: 11, color: 'var(--asher-grey-medium)', background: 'var(--asher-grey-bg)', padding: '3px 8px', borderRadius: 5 }}>
            <span style={{ fontWeight: 700, color: 'var(--asher-grey-dark)' }}>{n}</span> {label}
          </span>
        ))}
      </div>
    )}
    {cta && !locked && <button className="btn btn-secondary btn-sm" style={{ width: '100%' }} onClick={onCta}>{cta} <Icon name="arrowRight" size={11} /></button>}
    {locked && <button className="btn btn-secondary btn-sm" style={{ width: '100%' }} disabled>Locked</button>}
  </div>
);

// ── Validated model card (mirrors the HTA Lab "Model Portfolio") ─────────────
const ModelCard = ({ model, methods, methodRec, simHref }) => (
  <div className="asher-card asher-card--hover" style={{ padding: 18, display: 'flex', flexDirection: 'column' }}>
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, marginBottom: 9 }}>
      <span className="eyebrow" style={{ fontSize: 9.5 }}>{model.domain}</span>
      <span className="chip chip-success" style={{ fontSize: 9.5 }}><span className="dot dot-success" /> {model.tag}</span>
    </div>
    <h3 style={{ margin: '0 0 12px', fontSize: 17, color: 'var(--asher-gold-dark)' }}>{model.title}</h3>

    {model.fields.map((f) => <AISelectField key={f.label} {...f} />)}
    {model.bullets && (
      <ul style={{ margin: '-3px 0 11px', paddingLeft: 18, display: 'flex', flexDirection: 'column', gap: 4 }}>
        {model.bullets.map((b) => <li key={b} style={{ fontSize: 12, color: 'var(--asher-grey-medium)', lineHeight: 1.4 }}>{b}</li>)}
      </ul>
    )}
    {model.fieldsAfter && model.fieldsAfter.map((f) => <AISelectField key={f.label} {...f} />)}

    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5, margin: '3px 0 12px' }}>
      {model.modality.map((mod) => <span key={mod} className="tag-secondary">{mod}</span>)}
    </div>

    {methodRec ? (
      <div style={{ border: '1px solid rgba(228,170,35,0.3)', background: 'rgba(228,170,35,0.06)', borderRadius: 9, padding: '9px 11px', marginBottom: 12 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
          <Icon name="sparkle" size={12} color="var(--asher-gold-dark)" />
          <span style={{ fontSize: 10.5, fontWeight: 700, color: 'var(--asher-gold-dark)', textTransform: 'uppercase', letterSpacing: '0.04em' }}>Method</span>
          <span style={{ marginLeft: 'auto' }}><MethodChip method={methods[model.method]} /></span>
        </div>
        <div style={{ fontSize: 11, color: 'var(--asher-grey-medium)', lineHeight: 1.45 }}>{model.why}</div>
      </div>
    ) : (
      <div style={{ marginBottom: 12 }}><MethodChip method={methods[model.method]} /></div>
    )}

    <a className="btn btn-primary btn-sm" style={{ width: '100%', marginTop: 'auto', textDecoration: 'none' }} href={simHref} target="_blank" rel="noopener noreferrer">
      <Icon name="play" size={12} /> Launch Simulation
    </a>
    <div style={{ marginTop: 8, textAlign: 'center' }}><NewTabCue /></div>
  </div>
);

// ── Main screen (route body) ────────────────────────────────────────────────
const ScreenAISelect = ({
  scenario = 'customer',
  org = 'Pittsburgh Trauma Medical Center',
  user = { name: 'Maya Chandra', initials: 'MC' },
  library = AISELECT_LIBRARY,
  shortlist = AISELECT_SHORTLIST,
  models = AISELECT_MODELS,
  methods = AISELECT_METHODS,
  comingSoon = AISELECT_COMING,
  methodRec = true,
  futureStages = 'ghosted',
  preview = true,
  htaLabUrl = 'https://htalab.ashmatics.com',
  simUrl = 'https://htalab.ashmatics.com',
  htaLabDevUrl = null,
  onNav,
} = {}) => {
  const galleryRef = React.useRef(null);
  const scrollToGallery = () => {
    const el = galleryRef.current;
    if (el) window.scrollTo({ top: el.offsetTop - 70, behavior: 'smooth' });
  };
  const onBrowse = () => { if (onNav) onNav('aiselect-browse'); scrollToGallery(); };
  const ghosted = futureStages === 'ghosted';
  // "Launch Simulation" (model cards) targets simUrl, which is INTENTIONALLY decoupled
  // from htaLabUrl: the pre-cooked sims live on staging, while "Open the HTA Lab
  // preview" can point at a local/dev instance. Defaults make them equal; the app
  // splits them.
  const simHref = (id) => simUrl + (id ? (simUrl.indexOf('?') >= 0 ? '&' : '?') + 'model=' + encodeURIComponent(id) : '');

  return (
    <div className="app-shell">
      <div className="app-body">
      <div className="section-title-bar">
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 16 }}>
          <div style={{ flex: 1 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 5, flexWrap: 'wrap' }}>
              <h1 style={{ margin: 0 }}>The Forge AI-Select Studio</h1>
              {preview && <span className="chip chip-gold" style={{ fontSize: 10 }}><Icon name="sparkle" size={10} /> Early Preview</span>}
              <span className="chip chip-success" style={{ fontSize: 10 }}><span className="dot dot-success" /> HTA Lab connected</span>
            </div>
            <p className="subtitle">Choose the right use cases, estimate their value, then source and validate the AI. Evidence-based and simulation-backed.</p>
            <p className="subtitle" style={{ marginTop: 3, fontSize: 12.5, color: 'var(--asher-grey-light)' }}>
              Pre-acquisition AI strategy and selection. Governance Studio governs the AI you already run.
            </p>
          </div>
          <button className="btn btn-ghost" aria-label="Settings"><Icon name="settings" size={15} /></button>
        </div>
      </div>

      <div style={{ padding: '24px 36px 56px', maxWidth: 1240, margin: '0 auto', width: '100%' }}>
        {/* Two active hero surfaces */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 22 }}>
          <SelectSurface library={library} shortlist={shortlist} onBrowse={onBrowse} />
          <SimulateSurface methods={methods} htaLabUrl={htaLabUrl} htaLabDevUrl={htaLabDevUrl} onBrowse={scrollToGallery} />
        </div>

        {/* Secondary surfaces */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 14, marginBottom: 40 }}>
          <AISelectSecondarySurface icon="grid" eyebrow="Model portfolio" title="Validated models"
            body="Three models run today: stroke LVO detection, opportunistic CT screening, and CADt worklist simulation. More build out in HTA Lab."
            counts={[['Validated', models.length], ['Methods', 3]]} cta="View model portfolio" onCta={scrollToGallery} />
          <AISelectSecondarySurface icon="layers" eyebrow="Stage 3 · Source" title="Source the AI systems"
            body="Bring in candidate systems (vendor, open-source, or agentic) for your selected, value-ranked use cases."
            locked lockNote="Arrives next" ghosted={ghosted} />
          <AISelectSecondarySurface icon="verified" eyebrow="Stage 4 · Validate" title="Validate before deployment"
            body="Independently validate chosen systems against your own data before they reach the governance program."
            locked lockNote="On the roadmap" ghosted={ghosted} />
        </div>

        {/* Validated model portfolio */}
        <div ref={galleryRef}>
          <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 16, marginBottom: 16 }}>
            <div>
              <span className="eyebrow" style={{ color: 'var(--asher-gold-dark)' }}>Validated models</span>
              <h2 style={{ margin: '4px 0 4px', fontSize: 22, fontWeight: 700 }}>Model portfolio</h2>
              <p className="subtitle" style={{ maxWidth: 700 }}>Three models are validated and pre-cooked, so you can launch each as a ready-to-run simulation. HTA Lab is the full build experience, where new use cases get modeled and validated before they land here.</p>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 6 }}>
              <a className="btn btn-secondary btn-sm" style={{ textDecoration: 'none' }} href={htaLabUrl} target="_blank" rel="noopener noreferrer">Open the HTA Lab preview <Icon name="external" size={11} /></a>
              <NewTabCue />
            </div>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14, alignItems: 'stretch', marginBottom: 14 }}>
            {models.map((m) => <ModelCard key={m.id} model={m} methods={methods} methodRec={methodRec} simHref={simHref(m.id)} />)}
          </div>

          {/* Expansion path */}
          <div className="asher-card" style={{ padding: 18, borderStyle: 'dashed', display: 'flex', alignItems: 'center', gap: 18, flexWrap: 'wrap' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 11, flex: 1, minWidth: 260 }}>
              <span style={{ width: 38, height: 38, borderRadius: 9, background: 'var(--asher-cream)', display: 'grid', placeItems: 'center', color: 'var(--asher-gold-dark)', flexShrink: 0 }}><Icon name="plus" size={18} /></span>
              <div>
                <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--asher-grey-dark)' }}>More models build out in HTA Lab</div>
                <div style={{ fontSize: 12, color: 'var(--asher-grey-light)' }}>Use cases populate here as they are modeled and validated.</div>
              </div>
            </div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
              {comingSoon.map((c) => <span key={c} className="chip" style={{ fontSize: 11 }}><Icon name="clock" size={10} color="var(--asher-grey-light)" /> {c}</span>)}
            </div>
          </div>
        </div>
      </div>
      </div>
    </div>
  );
};

// House style: expose on window (both names — canonical + the one Claude Code used).
window.ScreenAISelect = ScreenAISelect;
window.AISelectLanding = ScreenAISelect;


/* ===== registry.jsx (RegistryParts) ===== */
/* global React, Icon, TopBar, PolicyBreadcrumb */
// AI System Registry — Stage A, step 3 (standalone exploration).
// The mechanic: the user CONFIRMS a scope-seeded list instead of authoring one.
//   1. Scope (offerings from onboarding) seeds candidate systems.
//   2. A group-by lens flips between "By service" and "By subspecialty".
//   3. Common systems are pre-checked; the user prunes.
//   4. Each included system asks TWO pickers only: deployment (required) + source.
//   5. Locked upsell rungs (Deepen · Import · Discovery) show the upgrade path.

const { OFFERINGS, SUBSPECIALTIES, CATEGORIES, CATEGORY_META, AI_TYPES, DEPLOYMENTS, VENDORS, CATALOG, USER } = window.REG;
const depMeta = (id) => DEPLOYMENTS.find((d) => d.id === id);
const catMeta = (id) => CATEGORIES.find((c) => c.id === id) || {};
const aiMeta = (id) => AI_TYPES.find((a) => a.id === id) || {};

// ── A lightweight dropdown menu (one open at a time, closed by a backdrop) ────
const Menu = ({ id, openId, setOpenId, trigger, width = 230, align = 'left', children }) => {
  const open = openId === id;
  return (
    <span style={{ position: 'relative', display: 'inline-flex' }}>
      <span onClick={(e) => { e.stopPropagation(); setOpenId(open ? null : id); }}>{trigger}</span>
      {open && (
        <div onClick={(e) => e.stopPropagation()} className="fade-up" style={{
          position: 'absolute', top: 'calc(100% + 6px)', [align]: 0, width, zIndex: 60,
          background: '#fff', border: '1px solid #e3ddcd', borderRadius: 10, boxShadow: 'var(--shadow-asher-lg)',
          padding: 6, maxHeight: 286, overflowY: 'auto',
        }}>{children}</div>
      )}
    </span>
  );
};

const MenuItem = ({ on, onClick, icon, title, sub }) => (
  <button onClick={onClick} style={{
    width: '100%', display: 'flex', gap: 10, alignItems: 'flex-start', textAlign: 'left', fontFamily: 'inherit',
    cursor: 'pointer', border: 'none', borderRadius: 7, padding: '8px 9px', background: on ? 'var(--asher-cream)' : 'transparent',
  }}>
    {icon && <Icon name={icon} size={15} color={on ? 'var(--asher-gold-dark)' : 'var(--asher-grey-light)'} style={{ flexShrink: 0, marginTop: 1 }} />}
    <span style={{ flex: 1 }}>
      <span style={{ display: 'block', fontSize: 12.5, fontWeight: on ? 700 : 500, color: 'var(--asher-grey-dark)' }}>{title}</span>
      {sub && <span style={{ display: 'block', fontSize: 11, color: 'var(--asher-grey-light)', marginTop: 1, lineHeight: 1.4 }}>{sub}</span>}
    </span>
    {on && <Icon name="check" size={13} color="var(--asher-gold-dark)" style={{ flexShrink: 0, marginTop: 2 }} />}
  </button>
);

const CategoryBadge = ({ category }) => {
  const m = CATEGORY_META[category] || {};
  return <span className="chip" style={{ fontSize: 9.5, padding: '2px 8px', whiteSpace: 'nowrap', flexShrink: 0, background: m.bg, color: m.fg }}>{catMeta(category).label || category}</span>;
};

// ── Suggested (not-yet-added) tile — the lightweight "confirm me" affordance ──
const SuggestTile = ({ sys, onAdd, compact, guided }) => (
  <button onClick={onAdd} className="asher-card asher-card--hover" style={{
    textAlign: 'left', fontFamily: 'inherit', cursor: 'pointer', display: 'flex', gap: 11, alignItems: 'flex-start',
    padding: compact ? '11px 12px' : '13px 14px', border: '1px dashed #d6cfbd', background: '#fcfbf7', width: '100%',
  }}>
    <span style={{ width: 30, height: 30, borderRadius: 8, background: '#fff', border: '1px solid #e9e2d0', display: 'grid', placeItems: 'center', color: 'var(--asher-grey-light)', flexShrink: 0 }}>
      <Icon name="plus" size={15} />
    </span>
    <span style={{ flex: 1, minWidth: 0 }}>
      <span style={{ display: 'block', fontSize: 13, fontWeight: 600, color: 'var(--asher-grey-dark)', lineHeight: 1.25 }}>{sys.name}</span>
      <span style={{ display: 'block', fontSize: 11, color: 'var(--asher-grey-light)', marginTop: 2, lineHeight: 1.45 }}>{sys.modality}{!compact ? ` · ${sys.task}` : ''}</span>
      <span style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 7 }}>
        <CategoryBadge category={sys.category} />
        <span style={{ fontSize: 11.5, fontWeight: 600, color: 'var(--asher-gold-dark)', whiteSpace: 'nowrap' }}>Add to registry</span>
        {sys.vendors.length > 0 && <span style={{ fontSize: 10.5, color: 'var(--asher-grey-light)' }}>{sys.vendors.length} known vendor{sys.vendors.length > 1 ? 's' : ''}</span>}
        {sys.builtInHouse && <span style={{ fontSize: 10.5, color: 'var(--asher-grey-light)' }}>· often in-house</span>}
      </span>
    </span>
  </button>
);

// ── Included system card — the gold-checked, attribute-bearing state ──────────
const SystemCard = ({ sys, entry, openId, setOpenId, onRemove, setEntry, compact, requireDeploy, vendorNudge }) => {
  const dep = entry.deploy ? depMeta(entry.deploy) : null;
  const needsDeploy = requireDeploy && !entry.deploy;
  const sourceLabel = entry.source?.inhouse ? 'Built in-house' : entry.source?.vendor || null;

  return (
    <div className="asher-card" style={{ padding: compact ? '11px 13px' : '14px 15px', border: '1px solid #e6dfcd', borderLeft: '3px solid var(--asher-gold)', background: '#fff' }}>
      <div style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
        <span style={{ width: 30, height: 30, borderRadius: 8, background: 'var(--asher-gold)', display: 'grid', placeItems: 'center', color: '#fff', flexShrink: 0 }}>
          <Icon name="check" size={16} />
        </span>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--asher-grey-dark)', lineHeight: 1.25 }}>{sys.name}</div>
          <div style={{ fontSize: 11, color: 'var(--asher-grey-light)', marginTop: 4, lineHeight: 1.45 }}>{sys.modality}{!compact ? ` · ${sys.task}` : ''}</div>
        </div>
        <button onClick={onRemove} title="Remove from registry" style={{ background: 'transparent', border: 'none', cursor: 'pointer', color: '#bcb6a8', padding: 2, flexShrink: 0 }}>
          <Icon name="close" size={14} />
        </button>
      </div>

      {/* Pickers — deployment (required) · classification · AI type · source */}
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 11, paddingLeft: compact ? 0 : 40 }}>
        {/* Deployment — REQUIRED */}
        <Menu id={`${sys.id}-dep`} openId={openId} setOpenId={setOpenId} width={252}
          trigger={
            <button style={{
              display: 'inline-flex', alignItems: 'center', gap: 7, fontFamily: 'inherit', cursor: 'pointer', fontSize: 11.5, fontWeight: 500, whiteSpace: 'nowrap',
              padding: '6px 10px', borderRadius: 7, background: needsDeploy ? 'rgba(228,170,35,0.10)' : '#fff',
              border: needsDeploy ? '1px solid var(--asher-gold)' : '1px solid #e0dac9', color: needsDeploy ? 'var(--asher-gold-dark)' : 'var(--asher-grey-dark)',
            }}>
              <Icon name={dep ? dep.icon : 'warning'} size={13} color={needsDeploy ? 'var(--asher-gold-dark)' : 'var(--asher-grey-light)'} />
              {dep ? dep.label : 'Set deployment'}
              {needsDeploy && <span style={{ fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.04em' }}>· required</span>}
              <Icon name="chevronDown" size={11} color="var(--asher-grey-light)" />
            </button>
          }>
          <div style={{ padding: '4px 8px 7px', fontSize: 10, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--asher-grey-light)' }}>Where does it run?</div>
          {DEPLOYMENTS.map((d) => (
            <MenuItem key={d.id} on={entry.deploy === d.id} icon={d.icon} title={d.label} sub={d.hint}
              onClick={() => { setEntry({ ...entry, deploy: d.id }); setOpenId(null); }} />
          ))}
        </Menu>

        {/* Classification — USER-SET */}
        <Menu id={`${sys.id}-cat`} openId={openId} setOpenId={setOpenId} width={236}
          trigger={
            <button style={{
              display: 'inline-flex', alignItems: 'center', gap: 7, fontFamily: 'inherit', cursor: 'pointer', fontSize: 11.5, fontWeight: 600, whiteSpace: 'nowrap',
              padding: '6px 10px', borderRadius: 7, background: (CATEGORY_META[entry.category] || {}).bg, color: (CATEGORY_META[entry.category] || {}).fg, border: '1px solid transparent',
            }}>
              {catMeta(entry.category).label || 'Classify'}
              <Icon name="chevronDown" size={11} color="currentColor" style={{ opacity: 0.7 }} />
            </button>
          }>
          <div style={{ padding: '4px 8px 7px', fontSize: 10, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--asher-grey-light)' }}>Classification · your call</div>
          {CATEGORIES.map((c) => (
            <MenuItem key={c.id} on={entry.category === c.id} icon="tag" title={c.label} sub={c.desc}
              onClick={() => { setEntry({ ...entry, category: c.id }); setOpenId(null); }} />
          ))}
        </Menu>

        {/* AI technology type — USER-SET */}
        <Menu id={`${sys.id}-ai`} openId={openId} setOpenId={setOpenId} width={210}
          trigger={
            <button style={{
              display: 'inline-flex', alignItems: 'center', gap: 7, fontFamily: 'inherit', cursor: 'pointer', fontSize: 11.5, fontWeight: 500, whiteSpace: 'nowrap',
              padding: '6px 10px', borderRadius: 7, background: '#fff', border: '1px solid #e0dac9', color: entry.aitype ? 'var(--asher-grey-dark)' : 'var(--asher-grey-light)',
            }}>
              <Icon name={aiMeta(entry.aitype).icon || 'tag'} size={13} color="var(--asher-grey-light)" />
              {aiMeta(entry.aitype).label || 'AI type'}
              <Icon name="chevronDown" size={11} color="var(--asher-grey-light)" />
            </button>
          }>
          <div style={{ padding: '4px 8px 7px', fontSize: 10, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--asher-grey-light)' }}>Type of AI technology</div>
          {AI_TYPES.map((a) => (
            <MenuItem key={a.id} on={entry.aitype === a.id} icon={a.icon} title={a.label} sub={a.desc}
              onClick={() => { setEntry({ ...entry, aitype: a.id }); setOpenId(null); }} />
          ))}
        </Menu>

        {/* Source — OPTIONAL (vendor / in-house) */}
        <Menu id={`${sys.id}-src`} openId={openId} setOpenId={setOpenId} width={210}
          trigger={
            <button style={{
              display: 'inline-flex', alignItems: 'center', gap: 7, fontFamily: 'inherit', cursor: 'pointer', fontSize: 11.5, fontWeight: 500, whiteSpace: 'nowrap',
              padding: '6px 10px', borderRadius: 7, background: '#fff', border: '1px dashed #d8d2c2',
              color: sourceLabel ? 'var(--asher-grey-dark)' : 'var(--asher-grey-light)',
            }}>
              <Icon name={entry.source?.inhouse ? 'building' : 'tag'} size={13} color="var(--asher-grey-light)" />
              {sourceLabel || (vendorNudge ? 'Vendor — needed later' : 'Add vendor / source')}
              <Icon name="chevronDown" size={11} color="var(--asher-grey-light)" />
            </button>
          }>
          <div style={{ padding: '4px 8px 7px', fontSize: 10, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--asher-grey-light)' }}>Vendor or built in-house?</div>
          <MenuItem on={entry.source?.inhouse} icon="building" title="Built in-house" onClick={() => { setEntry({ ...entry, source: { inhouse: true } }); setOpenId(null); }} />
          <div style={{ height: 1, background: '#f0ece1', margin: '5px 4px' }} />
          {(sys.vendors.length ? sys.vendors : VENDORS).map((v) => (
            <MenuItem key={v} on={entry.source?.vendor === v} icon="tag" title={v} onClick={() => { setEntry({ ...entry, source: { vendor: v } }); setOpenId(null); }} />
          ))}
          {sys.vendors.length > 0 && (
            <div style={{ padding: '6px 9px 3px', fontSize: 10.5, color: 'var(--asher-grey-light)', borderTop: '1px solid #f0ece1', marginTop: 4 }}>Suggested from the knowledge graph</div>
          )}
        </Menu>
      </div>
    </div>
  );
};

// ── Group section (one offering or one subspecialty) ──────────────────────────
const GroupSection = ({ label, systems, included, openId, setOpenId, compact, guided, requireDeploy, vendorNudge, onAdd, onRemoveCard, onSetEntry }) => {
  const addedCount = systems.filter((s) => included[s.id]).length;
  return (
    <div style={{ marginBottom: 22 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 9, marginBottom: 11, paddingBottom: 7, borderBottom: '1px solid rgba(180,189,175,0.4)' }}>
        <span style={{ fontFamily: 'Montserrat', fontSize: 13.5, fontWeight: 700, color: 'var(--asher-grey-dark)' }}>{label}</span>
        <span className="tag-secondary tag-count" style={{ background: addedCount ? 'rgba(228,170,35,0.16)' : '#ece9e1', color: addedCount ? 'var(--asher-gold-dark)' : 'var(--asher-grey-medium)' }}>{addedCount} added</span>
        <span style={{ fontSize: 11, color: 'var(--asher-grey-light)', marginLeft: 'auto' }}>{systems.length} suggested</span>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(auto-fill, minmax(${compact ? 252 : 290}px, 1fr))`, gap: 10 }}>
        {systems.map((s) => included[s.id]
          ? <SystemCard key={s.id} sys={s} entry={included[s.id]} openId={openId} setOpenId={setOpenId} compact={compact}
              requireDeploy={requireDeploy} vendorNudge={vendorNudge}
              onRemove={() => onRemoveCard(s.id)} setEntry={(e) => onSetEntry(s.id, e)} />
          : <SuggestTile key={s.id} sys={s} compact={compact} guided={guided} onAdd={() => onAdd(s)} />)}
      </div>
    </div>
  );
};

window.RegistryParts = { Menu, MenuItem, CategoryBadge, SuggestTile, SystemCard, GroupSection, depMeta };


/* ===== app.jsx ===== */
/* global React, ReactDOM, AppShell, Register, Onboarding, Triage, Explore, StudioLanding, SystemRegistry, GuidedRegistrySetup, StageAWizard, BlueprintPreview, GateOne, StageB, ScreenActivation, ConfigurationHub, ScreenAISelect */
// App router + funnel state. A linear, on-screen-controls-only flow: each screen
// advances via its own CTA. No design-tool navigator. Only Register (+ email verify)
// renders full-bleed; from Triage on, screens render inside AppShell.
//
// Flow: Register → verify → Triage (the front door). Triage nudges Explore first.
// The Build path captures organization context just-in-time (only if not already
// set), then lands in the Studio. That captured `setup` object has TWO entry
// points: the Build path produces it, and the sidebar "Organization Context"
// surface edits the same object. Contracted customers arrive with it pre-filled.

const STORE_KEY = 'forge_lcr_v1';


// Pre-filled scope for the "I'm a contracted customer" persona, so they skip the
// capture questions a cold prospect answers. Same shape Onboarding's buildSetup()
// produces. (In real product code this is hydrated from the signed engagement.)
const CONTRACTED_SETUP = {
  role: 'imaging-ai',
  rel: 'partOf', isTop: false,
  facilityId: 'ptmc', facilityName: 'Pittsburgh Trauma Medical Center', facilityKind: 'Academic Medical Center',
  indepType: null, nodeId: 'rad', nodeName: 'Radiology & Imaging Services',
  govName: 'Radiology & Imaging Services', granularity: 'department', granularityLabel: 'Department',
  offers: ['In-patient Diagnostic Imaging', 'Acute & Trauma Imaging', 'Outpatient & Specialty Imaging', 'Interventional Radiology', 'Imaging Research & Translation Lab'],
  divided: 'subunits', divs: ['Neuroradiology', 'Musculoskeletal (MSK)', 'Emergency / Trauma Radiology'],
  isRad: true, contracted: true,
  aiMix: 'mixed', committee: window.deriveCommittee('Radiology & Imaging Services', 'mixed'),
};

// A registry pre-filled with the canonical PTMC system set, so a contracted
// customer (and the `customer` demo scenario) lands with a populated registry to
// review rather than an empty capture. Reads the shared catalog/seeding helpers.
const fullRegistry = () => {
  const REG = window.REG, DATA = window.DATA;
  return { inScope: REG.seededScope(CONTRACTED_SETUP), included: REG.includedFromIds(DATA.SYSTEMS.map((s) => s.id)), customs: [], projected: 30 };
};

// Named demo scenarios, applied on load from the URL hash so the booth and demo
// videos can deep-link a known state. `prospect` is the cold free-tier path;
// `customer` is an already-set-up customer dropped into the Studio (Stage A → B →
// operate). Build state on demand (reads window.REG/DATA at call time).
function buildScenario(key) {
  if (key === 'customer') {
    // Org context configured (setup + registry + assessment) and the blueprint
    // already approved, but the AIMS is NOT yet created — so #customer lands in the
    // Studio ready to "Create AIMS from blueprint". A free-tier, try-and-buy pilot.
    return { setup: CONTRACTED_SETUP, registry: fullRegistry(), assessment: window.ASSESS.seed(), progress: { blueprint: true, aims: false }, firstRun: false, screen: 'studio' };
  }
  if (key === 'prospect') {
    return { setup: null, registry: window.REG.emptyRegistry(), assessment: window.ASSESS.empty(), progress: { blueprint: false, aims: false }, firstRun: true, screen: 'triage' };
  }
  return null;
}

// Adapters: the 2B activation ceremony (ScreenActivation) is presentational and
// reads a SHAPE-SPECIFIC slice — setup as { org, orgDetail, archetype } and the
// maturity profile as { maturityScore, maturityLabel }. The app's real lifted
// objects are richer and differently shaped (setup carries govName/facilityName/
// granularityLabel/committee; assessment carries the raw maturity ANSWERS, with the
// score computed by window.ASSESS.scoreMaturity). The mapping is integration glue,
// so it lives here in the router, not in the component. Both return null when there
// is nothing real to show, so the screen degrades to an em-dash rather than
// resurrecting the component's baked PTMC defaults (those apply only to `undefined`).
function activationSetupFrom(setup) {
  if (!setup) return null;
  const org = setup.govName || setup.facilityName || null;
  const detail = [];
  if (setup.facilityName && setup.facilityName !== org) detail.push(setup.facilityName);
  if (setup.granularityLabel) detail.push(setup.granularityLabel);
  const archetype = setup.committee
    ? [setup.granularityLabel, `${setup.committee.seats.length}-seat governance committee`].filter(Boolean).join(' · ')
    : (setup.granularityLabel || null);
  return { org, orgDetail: detail.join(' · ') || null, archetype };
}
function activationAssessmentFrom(assessment) {
  const profile = window.ASSESS.scoreMaturity((assessment && assessment.maturity) || {});
  if (!profile || !(profile.overall > 0)) return null;
  return { maturityScore: `${profile.overall.toFixed(1)} / 5.0`, maturityLabel: profile.overallLabel };
}

// Per-screen shell metadata. Screens absent from this map render full-bleed
// (Register, email verify). Listed screens render inside AppShell, which supplies
// the sidebar + breadcrumb (the per-screen TopBar is retired).
const SHELL_META = {
  triage:     { activeNav: 'studio',     breadcrumb: ['Home'] },
  explore:    { activeNav: 'explore',    breadcrumb: ['Home', 'Explore Asher Forge'] },
  aiselect:   { activeNav: 'aiselect',   breadcrumb: ['Home', 'AI-Select Studio'] },
  orgcontext: { activeNav: 'orgcontext', breadcrumb: ['Home', 'Organizational Profile', 'Organization context'] },
  registry:   { activeNav: 'registry',   breadcrumb: ['Home', 'Organizational Profile', 'AI system registry'] },
  regsetup:   { activeNav: 'registry',   breadcrumb: ['Home', 'Organizational Profile', 'AI system registry', 'Guided setup'] },
  studio:     { activeNav: 'studio',     breadcrumb: ['Home', 'Governance Studio'] },
  wizard:     { activeNav: 'studio',     breadcrumb: ['Governance Studio', 'New blueprint', 'Stage A'] },
  blueprint:  { activeNav: 'studio',     breadcrumb: ['Governance Studio', 'PTMC Radiology', 'Blueprint preview'] },
  gate:       { activeNav: 'studio',     breadcrumb: ['Governance Studio', 'PTMC Radiology', 'Free allowance'] },
  stageB:     { activeNav: 'studio',     breadcrumb: ['Governance Studio', 'PTMC Radiology, AI Management System', 'Configuration Hub', 'Build artifacts'] },
  confighub:  { activeNav: 'studio',     breadcrumb: ['Governance Studio', 'PTMC Radiology, AI Management System', 'Configuration Hub'] },
};
// Screens that manage their own internal scroll (locked viewport, no page scroll).
const FILL_SCREENS = new Set(['explore']);

function App() {
  // A fresh load always starts at Register with clean state: never drop a viewer
  // mid-funnel, and never inherit a previous walkthrough's progress on reload (the
  // conference reset path, where someone hits refresh instead of Log out). State is
  // still written to localStorage below, so a real "resume your setup" feature can
  // read it later; the sidebar "Log out" clears it.
  const [screen, setScreen] = React.useState('register');
  // progress.blueprint — a blueprint has been generated & approved (free, kept).
  // progress.aims      — an AI Management System has been CREATED from that blueprint
  //                      (the one-time creation ceremony has run). Until then the
  //                      Studio shows the approved blueprint, not a named AIMS.
  const [progress, setProgress] = React.useState({ blueprint: false, aims: false });
  const [setup, setSetup] = React.useState(null);
  // "First time in app" flag. True until the viewer leaves the Triage front door
  // for the first time. Persisted so a real "resume" path can read it; for now it
  // just softens the Triage greeting on a return visit (Welcome → Welcome back)
  // and is the hook for the deferred returning-user bypass.
  const [firstRun, setFirstRun] = React.useState(true);
  // True while the viewer is in the guided first-build setup sequence
  // (Organization context → AI System Registry → Studio). It flips the org-context
  // and registry screens out of their standing "edit from the sidebar" mode and
  // into the build journey, and is cleared the moment they reach the Studio. The
  // sidebar always enters those surfaces in standing mode (building=false).
  const [building, setBuilding] = React.useState(false);
  // The captured AI System Registry — the single source of truth shared by the
  // guided capture and the read-only review. Starts empty (a brand-new viewer
  // hasn't told us anything); seeded when org context is established.
  const [registry, setRegistry] = React.useState(() => window.REG.emptyRegistry());
  // Stage A assessment (capability maturity + priorities/compliance), lifted here
  // for the same reasons as the registry: it seeds for the #customer scenario, it
  // surfaces in the blueprint preview, and it persists. Starts empty for a cold
  // prospect; seeded for a contracted customer.
  const [assessment, setAssessment] = React.useState(() => window.ASSESS.empty());

  // On first load, apply a demo scenario from the URL hash (#customer, #prospect,
  // #customer-stageb) so the booth and videos can deep-link a known state.
  React.useEffect(() => {
    const h = (window.location.hash || '').replace(/^#\/?/, '').toLowerCase();
    if (!h) return;
    let key = h, target = null;
    if (h === 'customer-stageb' || h === 'customer/stageb') { key = 'customer'; target = 'stageB'; }
    if (h === 'customer-activation' || h === 'customer/activation') { key = 'customer'; target = 'activation'; }
    if (h === 'customer-confighub' || h === 'customer/confighub') { key = 'customer'; target = 'confighub'; }
    const sc = buildScenario(key);
    if (!sc) return;
    setSetup(sc.setup);
    setRegistry(sc.registry);
    setAssessment(sc.assessment || window.ASSESS.empty());
    // The confighub and stageB deep-links land on a customer whose AIMS already
    // exists (Config Hub + its build child both live post-creation). The activation
    // deep-link is the creation ceremony itself, so the AIMS is NOT yet created there.
    const aimsExists = target === 'stageB' || target === 'confighub';
    setProgress({ ...sc.progress, aims: aimsExists ? true : sc.progress.aims });
    setFirstRun(sc.firstRun);
    setBuilding(false);
    setScreen(target || sc.screen);
  }, []);

  React.useEffect(() => {
    localStorage.setItem(STORE_KEY, JSON.stringify({ screen, progress, setup, firstRun, registry, assessment }));
  }, [screen, progress, setup, firstRun, registry, assessment]);

  // Reset scroll on SCREEN change only — not on every state write. (Keying this on
  // all state, as it once did, re-anchored the page to the top on every maturity
  // answer / registry edit; the sticky profile rail handles in-page scroll.)
  React.useEffect(() => { window.scrollTo(0, 0); }, [screen]);

  const go = (s) => setScreen(s);
  const reset = () => {
    try { localStorage.removeItem(STORE_KEY); } catch (e) { /* ignore */ }
    setProgress({ blueprint: false, aims: false });
    setSetup(null);
    setFirstRun(true);
    setBuilding(false);
    setRegistry(window.REG.emptyRegistry());
    setAssessment(window.ASSESS.empty());
    setScreen('register');
  };

  // Sidebar nav: the live destinations. Disabled items never call onNav. The
  // Studio is gated like every other path to it: a viewer who hasn't set up their
  // organization is routed through setup first (enterBuild), so the sidebar can't
  // be used to jump past the gate. Every other surface is standing mode.
  const onNav = (id) => {
    if (id === 'studio') { enterBuild(); return; }
    setBuilding(false);
    if (id === 'explore') go('explore');
    else if (id === 'aiselect') go('aiselect');
    else if (id === 'orgcontext') go('orgcontext');
    else if (id === 'registry') go('registry');
    // 'aiselect-browse' (the landing's in-page "Browse use cases" CTA) intentionally
    // falls through: there is no separate browse screen, the page scrolls locally.
  };

  // Build path: the AI registry is part of setting up the organization, so the
  // guided sequence is Organization context → AI System Registry → Studio. The
  // registry is intentionally not a hard gate; the viewer can leave it partial and
  // refine later. Once setup exists (returning builder or contracted), skip
  // straight to the Studio.
  const enterBuild = () => {
    if (setup) { setBuilding(false); go('studio'); }
    else { setBuilding(true); go('orgcontext'); }
  };

  const studioProps = {
    progress,
    onStart: () => go('wizard'),
    onOpenBlueprint: () => go('blueprint'),
    // The one-time "Create AIMS from blueprint" — runs the creation ceremony, which
    // lands in the Configuration Hub once the AIMS exists.
    onCreateAims: () => go('activation'),
    onHub: () => go('confighub'),
    onExplore: () => go('explore'),
    onNav,
    // Demo/admin controls on the Studio landing — reset to a clean run-through, or
    // instantly hydrate a seeded blueprint (approved, AIMS not yet created), so a
    // booth demo can re-run the create-AIMS ceremony on the fly.
    onResetBlueprint: () => { setProgress({ blueprint: false, aims: false }); setAssessment(window.ASSESS.empty()); },
    onSeedBlueprint: () => { setSetup((s) => s || CONTRACTED_SETUP); setRegistry(fullRegistry()); setAssessment(window.ASSESS.seed()); setProgress({ blueprint: true, aims: false }); },
  };

  let view;
  switch (screen) {
    case 'register':
      view = <Register onDone={() => go('triage')} />;
      break;
    case 'triage':
      view = <Triage firstRun={firstRun} hasSetup={!!setup} onRoute={(id) => {
        setFirstRun(false); // they've now left the front door at least once
        if (id === 'explore') go('explore');
        else if (id === 'contracted') { setSetup(CONTRACTED_SETUP); setRegistry(fullRegistry()); setAssessment(window.ASSESS.seed()); go('studio'); }
        else if (id === 'setup') { setBuilding(true); go('orgcontext'); } // Step 2: org + AI registry
        else if (id === 'orgprofile') { setBuilding(false); go('orgcontext'); } // review the standing profile
        else if (id === 'build') enterBuild(); // only offered once setup exists
      }} />;
      break;
    case 'orgcontext':
      // Just-in-time capture (first Build) and the editable Organization Context
      // surface are the same screen. In the build journey, finishing here advances
      // to the AI System Registry (the next setup step); editing from the sidebar
      // returns to the Studio. onBack returns to the front door during first
      // capture (and leaves the build journey), the Studio when editing later.
      view = <Onboarding embedded setup={setup} building={building}
        onDone={(s) => {
          if (s) setSetup(s);
          // Seed the registry from the just-captured scope the first time only, so
          // the next step (capture) opens with suggested systems; don't clobber edits.
          if (s) setRegistry((r) => (Object.keys(r.included || {}).length === 0 ? window.REG.seedRegistry(s) : r));
          go(building ? 'registry' : 'studio');
        }}
        onBack={() => { if (setup) { go('studio'); } else { setBuilding(false); go('triage'); } }} />;
      break;
    case 'registry':
      // Build journey (building): the third setup step is the guided CAPTURE — tell
      // us what AI you run — since onboarding can't know it yet. Deliberately
      // skippable/partial; then continue to the Studio. Standing (from the sidebar):
      // the read-only review whose "Open guided setup" opens the capture (regsetup).
      view = building
        ? <GuidedRegistrySetup registry={registry} onChange={setRegistry} buildFlow
            doneLabel="Continue to the Studio"
            note="Nothing here is locked in. You can register more systems and refine attributes any time from the AI System Registry in the sidebar, your blueprint re-tunes as it changes."
            onDone={() => { setBuilding(false); go('studio'); }}
            onBack={() => go('orgcontext')} />
        : <SystemRegistry setup={setup} registry={registry} onStart={() => go('regsetup')} />;
      break;
    case 'regsetup':
      // The standing registry's "Open guided setup": JUST the detailed capture, not
      // the blueprint wizard. Save returns to the standing registry review.
      view = <GuidedRegistrySetup registry={registry} onChange={setRegistry}
        doneLabel="Save & close"
        note="Changes save to your AI System Registry and re-tune any blueprint that draws on it."
        onDone={() => go('registry')}
        onBack={() => go('registry')} />;
      break;
    case 'explore':
      // Back to the front door is the breadcrumb "Home" (and the sidebar
      // "Revisit welcome"); Explore no longer needs its own back handler.
      view = <Explore onBuild={enterBuild} onNav={onNav} />;
      break;
    case 'studio':
      view = <StudioLanding {...studioProps} />;
      break;
    case 'aiselect':
      // AI-Select Studio landing (Early Preview). Pre-acquisition AI strategy &
      // selection: it presents the curated use-case library and the validated-model
      // portfolio, then links OUT to the external HTA Lab for the actual simulations
      // (staging + the R&D dev instance). Presentational and props-driven — owns no
      // lifted state, so the baked PTMC defaults carry the demo. Freely reachable
      // like Explore (not gated like the Governance Studio).
      view = <ScreenAISelect onNav={onNav} />;
      break;
    case 'wizard':
      view = <StageAWizard setup={setup} registry={registry} onRegistryChange={setRegistry} assessment={assessment} onAssessmentChange={setAssessment} onComplete={() => { setProgress((p) => ({ ...p, blueprint: true })); go('blueprint'); }} onExit={() => go('studio')} />;
      break;
    case 'blueprint':
      // "Approve blueprint" hands off to the free-allowance paygate — the single
      // try-and-buy decision point — which then returns to the Studio with the
      // blueprint marked approved. (onGate is the component's approve callback.)
      view = <BlueprintPreview assessment={assessment} onGate={() => go('gate')} onBack={() => go('studio')} onNav={onNav} />;
      break;
    case 'gate':
      // The free-allowance paygate. The ONLY action is "Proceed with free allowance":
      // the blueprint is now approved (kept, free), and we return to the Studio, where
      // the next step is "Create AIMS from blueprint". Paid plans are shown as
      // informational, for when you outgrow the free tier — never as the path forward.
      view = <GateOne onProceed={() => { setProgress((p) => ({ ...p, blueprint: true })); go('studio'); }} onBack={() => go('blueprint')} />;
      break;
    case 'activation':
      // The one-time AIMS creation ceremony. Reached from the Studio's "Create AIMS
      // from blueprint" (and the #customer-activation deep-link) — gated on the AIMS
      // not already existing, so it never replays once created. Marks the AIMS created
      // on the way out and lands in the Configuration Hub. onComplete is intentionally
      // omitted so the ceremony settles on its completed state and the payoff CTA
      // shows; the user advances by choice.
      view = <ScreenActivation
        setup={activationSetupFrom(setup)}
        assessment={activationAssessmentFrom(assessment)}
        onPrimary={() => { setProgress((p) => ({ ...p, aims: true })); go('confighub'); }}
        onSeeAll={() => { setProgress((p) => ({ ...p, aims: true })); go('confighub'); }} />;
      break;
    case 'stageB':
      // "Build artifacts" — the conditional-method engine. This is now a CHILD of the
      // Configuration Hub (you reach it by opening a workplan), not a destination from
      // the blueprint. Back returns to the Hub. The free tier meters what you can build
      // here (process domains); "See plans" surfaces the paygate as info only.
      view = <StageB onBack={() => go('confighub')} onUpgrade={() => go('gate')} onNav={onNav} />;
      break;
    case 'confighub':
      // Configuration Hub (B1) — post-creation, shows all 5 workplans of the AIMS.
      // Free tier: 1 workplan is configurable, the rest are locked-but-visible.
      // PLACEHOLDER aligned to the product B1 model; the hi-fi port (coreapp Vue) is
      // the next step. Opening a free workplan drops into the Build artifacts child.
      view = <ConfigurationHub setup={setup} onOpenArea={() => go('stageB')} onBack={() => go('studio')} />;
      break;
    default:
      view = <Register onDone={() => go('triage')} />;
  }

  let meta = SHELL_META[screen];
  // The orgcontext screen is reached two ways. First-run capture (from Build, no
  // setup yet) reads as part of the build journey; revisiting it from the sidebar
  // is the standing "Organization Context" edit surface. Distinguish the chrome.
  if (screen === 'orgcontext' && !setup) {
    meta = { activeNav: 'studio', breadcrumb: ['Home', 'Build a blueprint', 'A bit about you'] };
  }
  // The registry is likewise two screens in one. In the build journey it is the
  // "Your AI systems" setup step; from the sidebar it is the standing registry.
  if (screen === 'registry' && building) {
    meta = { activeNav: 'studio', breadcrumb: ['Home', 'Build a blueprint', 'Your AI systems'] };
  }
  if (meta) {
    return (
      <AppShell
        activeNav={meta.activeNav}
        breadcrumb={meta.breadcrumb}
        onNav={onNav}
        onCrumb={(route) => go(route)}
        onLogout={reset}
        onWelcome={screen === 'triage' ? null : () => go('triage')}
        fill={FILL_SCREENS.has(screen)}
      >
        {view}
      </AppShell>
    );
  }
  // The activation ceremony renders full-bleed (its own dark chrome, not AppShell).
  // Its root is `.app-shell` (min-height: 100%), which only fills the screen against
  // a definite parent height — so give it a 100vh wrapper. (minHeight would leave the
  // parent indefinite and the percentage collapses back to content height.)
  if (screen === 'activation') {
    return <div style={{ height: '100vh' }}>{view}</div>;
  }
  return view;
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);

