// app-ui.jsx — shared UI atoms for the PulseChart Claims working app.
// Independent of the design-canvas components.jsx (different concerns).

const PCAT = window.PCT;
const PCAD = window.PCA;

// ── Icons ─────────────────────────────────────────────────
function AppIcon({ name, size = 16, color = 'currentColor', stroke = 1.7 }) {
  const p = {
    width: size, height: size, viewBox: '0 0 24 24', fill: 'none',
    stroke: color, strokeWidth: stroke, strokeLinecap: 'round', strokeLinejoin: 'round',
  };
  switch (name) {
    case 'home':       return <svg {...p}><path d="M3 11l9-8 9 8M5 9v12h14V9"/></svg>;
    case 'inbox':      return <svg {...p}><path d="M3 13h5l2 3h4l2-3h5M3 13l3-9h12l3 9v7H3z"/></svg>;
    case 'list':       return <svg {...p}><path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01"/></svg>;
    case 'claims':     return <svg {...p}><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><path d="M14 2v6h6M9 14h6M9 18h4"/></svg>;
    case 'docs':       return <svg {...p}><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><path d="M14 2v6h6"/></svg>;
    case 'denial':     return <svg {...p}><circle cx="12" cy="12" r="10"/><path d="M4.93 4.93l14.14 14.14"/></svg>;
    case 'money':      return <svg {...p}><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 000 7h5a3.5 3.5 0 010 7H6"/></svg>;
    case 'ar':         return <svg {...p}><path d="M3 3v18h18M7 14l4-4 4 4 6-6"/></svg>;
    case 'chart':      return <svg {...p}><path d="M3 3v18h18M7 16V8M12 16V4M17 16v-6"/></svg>;
    case 'gear':       return <svg {...p}><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>;
    case 'search':     return <svg {...p}><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.35-4.35"/></svg>;
    case 'plus':       return <svg {...p}><path d="M12 5v14M5 12h14"/></svg>;
    case 'down':       return <svg {...p}><path d="M6 9l6 6 6-6"/></svg>;
    case 'up':         return <svg {...p}><path d="M18 15l-6-6-6 6"/></svg>;
    case 'right':      return <svg {...p}><path d="M9 18l6-6-6-6"/></svg>;
    case 'left':       return <svg {...p}><path d="M15 18l-6-6 6-6"/></svg>;
    case 'check':      return <svg {...p}><path d="M5 13l4 4L19 7"/></svg>;
    case 'x':          return <svg {...p}><path d="M18 6L6 18M6 6l12 12"/></svg>;
    case 'clock':      return <svg {...p}><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>;
    case 'alert':      return <svg {...p}><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><path d="M12 9v4M12 17h.01"/></svg>;
    case 'info':       return <svg {...p}><circle cx="12" cy="12" r="10"/><path d="M12 8v4M12 16h.01"/></svg>;
    case 'send':       return <svg {...p}><path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/></svg>;
    case 'upload':     return <svg {...p}><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/></svg>;
    case 'download':   return <svg {...p}><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>;
    case 'paperclip':  return <svg {...p}><path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/></svg>;
    case 'flag':       return <svg {...p}><path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1zM4 22V15"/></svg>;
    case 'user':       return <svg {...p}><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>;
    case 'users':      return <svg {...p}><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75"/></svg>;
    case 'sparkle':    return <svg {...p}><path d="M12 3l1.5 6.5L20 11l-6.5 1.5L12 19l-1.5-6.5L4 11l6.5-1.5z"/></svg>;
    case 'shield':     return <svg {...p}><path d="M12 2l9 4v6c0 5-3.5 9.5-9 10-5.5-.5-9-5-9-10V6z"/></svg>;
    case 'bed':        return <svg {...p}><path d="M3 18v-6a2 2 0 012-2h14a2 2 0 012 2v6M3 18h18M3 18v3M21 18v3M7 10V7a2 2 0 012-2h2v5"/></svg>;
    case 'phone':      return <svg {...p}><path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.67A2 2 0 014.11 2h3a2 2 0 012 1.72c.13.96.37 1.9.72 2.81a2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.91.35 1.85.59 2.81.72A2 2 0 0122 16.92z"/></svg>;
    case 'filter':     return <svg {...p}><path d="M22 3H2l8 9.46V19l4 2v-8.54z"/></svg>;
    case 'columns':    return <svg {...p}><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="12" y1="3" x2="12" y2="21"/></svg>;
    case 'kbd':        return <svg {...p}><rect x="2" y="6" width="20" height="14" rx="2"/><path d="M6 11h.01M10 11h.01M14 11h.01M18 11h.01M6 15h12"/></svg>;
    case 'bell':       return <svg {...p}><path d="M18 8a6 6 0 00-12 0c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 01-3.46 0"/></svg>;
    case 'pill':       return <svg {...p}><path d="M10.5 20.5a7 7 0 010-10l4-4a7 7 0 0110 10l-4 4a7 7 0 01-10 0z"/><path d="M8.5 8.5l8 8"/></svg>;
    case 'scalpel':    return <svg {...p}><path d="M14 4l6 6-9 9-3 1 1-3z"/></svg>;
    case 'lab':        return <svg {...p}><path d="M9 2v6L4 18a2 2 0 002 3h12a2 2 0 002-3L15 8V2"/><path d="M7 2h10M8 14h8"/></svg>;
    case 'box':        return <svg {...p}><path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z"/><path d="M3.27 6.96L12 12l8.73-5.04M12 22V12"/></svg>;
    case 'bar':        return <svg {...p}><path d="M3 4h18v16H3z"/><path d="M6 8v8M9 8v8M12 8v8M15 8v8M18 8v8" strokeWidth={1}/></svg>;
    case 'id':         return <svg {...p}><rect x="2" y="5" width="20" height="14" rx="2"/><circle cx="9" cy="12" r="2.5"/><path d="M14 10h5M14 14h3"/></svg>;
    case 'card':       return <svg {...p}><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 10h18"/></svg>;
    case 'whatsapp':   return <svg width={size} height={size} viewBox="0 0 24 24" fill="#25D366"><path d="M20.5 3.5a10.5 10.5 0 00-16.5 13L3 21l4.6-1a10.5 10.5 0 0012.9-16.5z"/></svg>;
    case 'menu':       return <svg {...p}><path d="M3 6h18M3 12h18M3 18h18"/></svg>;
    case 'refresh':    return <svg {...p}><path d="M23 4v6h-6M1 20v-6h6M3.5 9a9 9 0 0114.85-3.36L23 10M1 14l4.65 4.36A9 9 0 0020.5 15"/></svg>;
    default: return null;
  }
}

// ── Money ─────────────────────────────────────────────────
function AppMoney({ value, muted, strong, size = 13, neg }) {
  if (value == null) return <span style={{ color: PCAT.c.textDim, fontFamily: PCAT.f.mono, fontSize: size }}>—</span>;
  return (
    <span style={{
      fontFamily: PCAT.f.mono, fontSize: size,
      color: neg ? PCAT.c.error : muted ? PCAT.c.textMuted : PCAT.c.text,
      fontWeight: strong ? 600 : 500,
      fontVariantNumeric: 'tabular-nums',
      whiteSpace: 'nowrap',
    }}>{neg ? '−' : ''}{PCAD.inr(Math.abs(value))}</span>
  );
}

// ── StatusPill ───────────────────────────────────────────
function AppStatusPill({ status }) {
  const meta = PCAD.statusMeta[status] || { label: status, tone:'neutral' };
  const tones = {
    success: { bg: PCAT.c.successTint, fg: '#15803D', dot: PCAT.c.success },
    warning: { bg: PCAT.c.warningTint, fg: '#92400E', dot: PCAT.c.warning },
    error:   { bg: PCAT.c.errorTint,   fg: '#991B1B', dot: PCAT.c.error   },
    info:    { bg: PCAT.c.accentTint,  fg: '#075985', dot: PCAT.c.accent  },
    neutral: { bg: PCAT.c.surface2,    fg: '#44403C', dot: PCAT.c.textDim },
  };
  const t = tones[meta.tone];
  return (
    <span style={{
      display:'inline-flex', alignItems:'center', gap:6,
      padding:'3px 9px', borderRadius:999,
      background: t.bg, color: t.fg,
      fontSize:11, fontWeight:600, letterSpacing:'.01em',
      whiteSpace:'nowrap',
    }}>
      <span style={{ width:5, height:5, borderRadius:'50%', background: t.dot }}/>
      {meta.label}
    </span>
  );
}

// ── SLA Timer (live) ─────────────────────────────────────
function AppSLATimer({ deadline, compact }) {
  const [, force] = React.useReducer(x => x + 1, 0);
  React.useEffect(() => {
    if (!deadline) return;
    const t = setInterval(force, 1000);
    return () => clearInterval(t);
  }, [deadline]);
  if (!deadline) return null;
  const ms = deadline - Date.now();
  const breach = ms < 0;
  const abs = Math.abs(ms);
  const mins = Math.floor(abs / 60000);
  const secs = Math.floor((abs % 60000) / 1000);
  const text = mins >= 60
    ? `${Math.floor(mins/60)}h ${mins%60}m`
    : `${mins}:${String(secs).padStart(2,'0')}`;
  const tone = breach ? PCAT.c.error
              : ms < 15 * 60 * 1000 ? PCAT.c.warning
              : PCAT.c.success;
  if (compact) {
    return (
      <span style={{
        fontFamily: PCAT.f.mono, fontSize: 11, fontWeight:600,
        color: tone, fontVariantNumeric:'tabular-nums',
      }}>{breach ? '−' : ''}{text}</span>
    );
  }
  return (
    <div style={{
      display:'inline-flex', alignItems:'center', gap:6,
      padding:'3px 8px', borderRadius:6,
      background: breach ? PCAT.c.errorTint : ms < 15*60*1000 ? PCAT.c.warningTint : PCAT.c.successTint,
      fontFamily: PCAT.f.mono, fontSize:11.5, fontWeight:600,
      color: tone, fontVariantNumeric:'tabular-nums', letterSpacing:'.02em',
    }}>
      <span style={{ width:5, height:5, borderRadius:'50%', background: tone }}/>
      {breach ? 'BREACH · ' : ''}{text}
    </div>
  );
}

// ── PayerChip ────────────────────────────────────────────
function AppPayerChip({ insurerId, size = 28 }) {
  const insurer = PCAD.findInsurer(insurerId);
  if (!insurer) return null;
  return (
    <div style={{ display:'flex', alignItems:'center', gap:8, minWidth:0 }}>
      <div style={{
        width: size, height: size, borderRadius: 6,
        background: insurer.color + '18',
        color: insurer.color,
        display:'grid', placeItems:'center',
        fontSize: Math.round(size * 0.4), fontWeight: 700,
        fontFamily: PCAT.f.mono, letterSpacing: '-.02em',
        border: `1px solid ${insurer.color}30`,
        flexShrink: 0,
      }}>{insurer.logo}</div>
      <div style={{ display:'flex', flexDirection:'column', lineHeight: 1.2, minWidth:0 }}>
        <div style={{ fontSize:13, fontWeight:500, color: PCAT.c.text, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{insurer.name}</div>
        <div style={{ fontSize:10, color: PCAT.c.textDim, fontFamily: PCAT.f.mono, letterSpacing:'.04em', textTransform:'uppercase' }}>{insurer.type}</div>
      </div>
    </div>
  );
}

// ── Button ───────────────────────────────────────────────
function AppBtn({ children, kind = 'ghost', size = 'md', onClick, icon, iconRight, full, disabled, type = 'button' }) {
  const sizes = {
    sm: { h:28, px:10, fs:12 },
    md: { h:34, px:14, fs:13 },
    lg: { h:40, px:18, fs:14 },
  }[size];
  const kinds = {
    primary: { bg: PCAT.c.accent,    fg:'#fff',         bd: PCAT.c.accent },
    dark:    { bg: PCAT.c.text,      fg:'#fff',         bd: PCAT.c.text },
    ghost:   { bg: PCAT.c.surface,   fg: PCAT.c.text,   bd: PCAT.c.border },
    soft:    { bg: PCAT.c.surface2,  fg: PCAT.c.text,   bd: 'transparent' },
    danger:  { bg: PCAT.c.error,     fg:'#fff',         bd: PCAT.c.error },
    success: { bg: PCAT.c.success,   fg:'#fff',         bd: PCAT.c.success },
    brand:   { bg: PCAT.c.brand,     fg:'#fff',         bd: PCAT.c.brand },
  }[kind];
  return (
    <button type={type} onClick={onClick} disabled={disabled} style={{
      display:'inline-flex', alignItems:'center', justifyContent:'center', gap:7,
      height: sizes.h, padding: `0 ${sizes.px}px`,
      background: disabled ? PCAT.c.surface2 : kinds.bg,
      color: disabled ? PCAT.c.textDim : kinds.fg,
      border: `1px solid ${disabled ? PCAT.c.border : kinds.bd}`,
      borderRadius:7,
      fontFamily: PCAT.f.sans, fontSize: sizes.fs, fontWeight:500,
      cursor: disabled ? 'not-allowed' : 'pointer',
      whiteSpace:'nowrap',
      width: full ? '100%' : 'auto',
      transition:'background .12s',
    }}>
      {icon && <AppIcon name={icon} size={14}/>}
      {children}
      {iconRight && <AppIcon name={iconRight} size={14}/>}
    </button>
  );
}

// ── Card / Section Label / KBD ────────────────────────────
function AppCard({ children, pad = 20, style = {}, onClick }) {
  return (
    <div onClick={onClick} style={{
      background: PCAT.c.surface,
      border: `1px solid ${PCAT.c.border}`,
      borderRadius: 10,
      padding: pad,
      ...style,
    }}>{children}</div>
  );
}

function AppSectionLabel({ children, right }) {
  return (
    <div style={{ display:'flex', alignItems:'baseline', justifyContent:'space-between', gap:12 }}>
      <div style={{
        fontFamily: PCAT.f.mono, fontSize:10, fontWeight:600,
        letterSpacing:'.1em', textTransform:'uppercase', color: PCAT.c.textMuted,
      }}>{children}</div>
      {right}
    </div>
  );
}

function AppKBD({ children }) {
  return <span style={{
    display:'inline-block', padding:'1px 6px', margin:'0 3px',
    fontFamily: PCAT.f.mono, fontSize:10, fontWeight:600,
    background:'#fff', border: `1px solid ${PCAT.c.border}`,
    borderRadius:4, color: PCAT.c.text, lineHeight:1.4,
  }}>{children}</span>;
}

function AppAvatar({ name, size = 28, bg }) {
  const initials = name.split(' ').map(s => s[0]).slice(0,2).join('').toUpperCase();
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%',
      background: bg || '#E4E2DD',
      color: PCAT.c.text,
      display:'grid', placeItems:'center',
      fontSize: Math.round(size * 0.38), fontWeight: 600,
      flexShrink: 0,
    }}>{initials}</div>
  );
}

function AppFlagChip({ kind }) {
  const map = {
    'sla-breach':     { label:'SLA BREACH',     bg: PCAT.c.errorTint,   fg: PCAT.c.error },
    'escalate':       { label:'Escalate',       bg: PCAT.c.errorTint,   fg: PCAT.c.error },
    'urgent':         { label:'Urgent',         bg: PCAT.c.warningTint, fg: PCAT.c.warning },
    'gap':            { label:'Patient gap',    bg: PCAT.c.warningTint, fg: PCAT.c.warning },
    'implant-pending':{ label:'Implant gate',   bg: PCAT.c.warningTint, fg: PCAT.c.warning },
    'query':          { label:'Query',          bg: PCAT.c.warningTint, fg: PCAT.c.warning },
    'docs':           { label:'Docs pending',   bg: PCAT.c.surface2,    fg: PCAT.c.textMuted },
    'denied':         { label:'Denied',         bg: PCAT.c.errorTint,   fg: PCAT.c.error },
    'self-pay-conv':  { label:'→ Self-pay',     bg: PCAT.c.surface2,    fg: PCAT.c.textMuted },
  }[kind];
  if (!map) return null;
  return (
    <span style={{
      display:'inline-flex', padding:'2px 7px', borderRadius:4,
      background: map.bg, color: map.fg,
      fontSize:10, fontWeight:600, letterSpacing:'.04em', textTransform:'uppercase',
    }}>{map.label}</span>
  );
}

// ── Form atoms ────────────────────────────────────────────
function AppField({ label, hint, children, required }) {
  return (
    <div style={{ display:'flex', flexDirection:'column', gap:6 }}>
      <label style={{
        fontFamily: PCAT.f.mono, fontSize:10, fontWeight:600,
        letterSpacing:'.08em', textTransform:'uppercase', color: PCAT.c.textMuted,
      }}>
        {label}
        {required && <span style={{ color: PCAT.c.error, marginLeft:4 }}>*</span>}
      </label>
      {children}
      {hint && <div style={{ fontSize:11, color: PCAT.c.textDim, lineHeight:1.4 }}>{hint}</div>}
    </div>
  );
}

function AppInput({ value, onChange, placeholder, mono, type = 'text' }) {
  return (
    <input
      type={type}
      value={value || ''}
      onChange={e => onChange && onChange(e.target.value)}
      placeholder={placeholder}
      style={{
        height: 36, padding: '0 12px',
        background: PCAT.c.surface,
        border: `1px solid ${PCAT.c.border}`,
        borderRadius: 7,
        fontFamily: mono ? PCAT.f.mono : PCAT.f.sans, fontSize: 13.5,
        color: PCAT.c.text,
        outline: 'none',
      }}
      onFocus={e => e.target.style.borderColor = PCAT.c.accent}
      onBlur={e => e.target.style.borderColor = PCAT.c.border}
    />
  );
}

function AppTextarea({ value, onChange, placeholder, rows = 3 }) {
  return (
    <textarea
      value={value || ''}
      onChange={e => onChange && onChange(e.target.value)}
      placeholder={placeholder}
      rows={rows}
      style={{
        padding: '10px 12px',
        background: PCAT.c.surface,
        border: `1px solid ${PCAT.c.border}`,
        borderRadius: 7,
        fontFamily: PCAT.f.sans, fontSize: 13.5,
        color: PCAT.c.text,
        outline: 'none', resize: 'vertical',
        minHeight: 80, width: '100%', boxSizing: 'border-box',
      }}
    />
  );
}

function AppSelect({ value, onChange, options, placeholder = 'Select…' }) {
  return (
    <div style={{ position:'relative' }}>
      <select value={value || ''} onChange={e => onChange && onChange(e.target.value)} style={{
        appearance:'none', WebkitAppearance:'none',
        width:'100%', height:36, padding:'0 32px 0 12px',
        background: PCAT.c.surface,
        border: `1px solid ${PCAT.c.border}`,
        borderRadius:7,
        fontFamily: PCAT.f.sans, fontSize:13.5, color: PCAT.c.text,
        outline:'none', cursor:'pointer',
      }}>
        {!value && <option value="">{placeholder}</option>}
        {options.map(o => (
          <option key={typeof o === 'object' ? o.value : o} value={typeof o === 'object' ? o.value : o}>
            {typeof o === 'object' ? o.label : o}
          </option>
        ))}
      </select>
      <div style={{ position:'absolute', right:10, top:'50%', transform:'translateY(-50%)', pointerEvents:'none' }}>
        <AppIcon name="down" size={12} color={PCAT.c.textMuted}/>
      </div>
    </div>
  );
}

// ── Modal ─────────────────────────────────────────────────
function AppModal({ open, onClose, title, subtitle, width = 600, children, footer }) {
  if (!open) return null;
  return ReactDOM.createPortal(
    <div onClick={onClose} style={{
      position:'fixed', inset:0, zIndex:1000,
      background:'rgba(24,20,16,.42)',
      backdropFilter:'blur(6px)',
      display:'flex', alignItems:'center', justifyContent:'center', padding:24,
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        background: PCAT.c.surface,
        borderRadius: 12,
        width: '100%', maxWidth: width,
        maxHeight: '85vh',
        display:'flex', flexDirection:'column',
        boxShadow:'0 25px 60px -12px rgba(0,0,0,0.25)',
        overflow:'hidden',
      }}>
        <div style={{
          padding:'18px 22px', borderBottom: `1px solid ${PCAT.c.border}`,
          display:'flex', alignItems:'flex-start', justifyContent:'space-between', gap:14,
        }}>
          <div>
            <div style={{ fontSize:16, fontWeight:600, letterSpacing:'-.005em' }}>{title}</div>
            {subtitle && <div style={{ fontSize:12.5, color: PCAT.c.textMuted, marginTop:2 }}>{subtitle}</div>}
          </div>
          <button onClick={onClose} style={{
            border:'none', background:'transparent', cursor:'pointer',
            width:28, height:28, borderRadius:6, display:'grid', placeItems:'center',
            color: PCAT.c.textMuted,
          }}><AppIcon name="x" size={16}/></button>
        </div>
        <div style={{ padding:22, overflow:'auto', flex:1 }}>{children}</div>
        {footer && (
          <div style={{
            padding:'14px 22px', borderTop: `1px solid ${PCAT.c.border}`,
            display:'flex', justifyContent:'flex-end', gap:8,
            background: PCAT.c.bg,
          }}>{footer}</div>
        )}
      </div>
    </div>,
    document.body,
  );
}

// ── Empty state ───────────────────────────────────────────
function AppEmpty({ icon, title, body, action }) {
  return (
    <div style={{
      padding: '48px 24px', textAlign:'center',
      display:'flex', flexDirection:'column', alignItems:'center', gap:12,
    }}>
      {icon && (
        <div style={{
          width:44, height:44, borderRadius:'50%',
          background: PCAT.c.surface2, color: PCAT.c.textMuted,
          display:'grid', placeItems:'center',
        }}><AppIcon name={icon} size={20}/></div>
      )}
      <div style={{ fontSize:14, fontWeight:600 }}>{title}</div>
      {body && <div style={{ fontSize:12.5, color: PCAT.c.textMuted, maxWidth: 360, lineHeight:1.5 }}>{body}</div>}
      {action}
    </div>
  );
}

// ── HMS badge ─────────────────────────────────────────────
// Used everywhere to mark a data point as coming from PulseChart HMS.
// Variants: 'pill' (default), 'chip' (smaller, fits inside lists), 'stamp'
// (for completeness items).
function HmsBadge({ variant = 'pill', source, children }) {
  if (variant === 'chip') {
    return (
      <span style={{
        display:'inline-flex', alignItems:'center', gap:4,
        padding:'1px 6px', borderRadius:4,
        background: PCAT.c.brandSoft, color: PCAT.c.brand,
        fontSize:9.5, fontWeight:600, fontFamily: PCAT.f.mono,
        letterSpacing:'.04em', textTransform:'uppercase',
        whiteSpace:'nowrap', verticalAlign: 'baseline',
      }}>
        <svg width="8" height="8" viewBox="0 0 8 8" fill="currentColor">
          <circle cx="4" cy="4" r="3"/>
        </svg>
        {children || 'HMS'}
      </span>
    );
  }
  if (variant === 'stamp') {
    return (
      <span style={{
        display:'inline-flex', alignItems:'center', gap:5,
        padding:'2px 7px', borderRadius:4,
        background: PCAT.c.brandSoft, color: PCAT.c.brand,
        fontSize:10, fontWeight:600, fontFamily: PCAT.f.mono,
        letterSpacing:'.04em', textTransform:'uppercase',
      }}>
        <svg width="10" height="10" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
          <path d="M3 6l2 2 4-4"/>
        </svg>
        EHR auto
      </span>
    );
  }
  // default 'pill'
  return (
    <span style={{
      display:'inline-flex', alignItems:'center', gap:6,
      padding:'3px 9px', borderRadius:999,
      background: PCAT.c.brandSoft, color: PCAT.c.brand,
      fontSize:11, fontWeight:600, letterSpacing:'.01em', whiteSpace:'nowrap',
    }}>
      <svg width="11" height="11" viewBox="0 0 12 12" fill="currentColor"><rect x="1" y="2" width="10" height="8" rx="1.5"/></svg>
      {children || 'HMS-linked'}{source && <span style={{ opacity:.6, fontFamily: PCAT.f.mono, fontSize:9.5, marginLeft:2 }}>· {source}</span>}
    </span>
  );
}

// Helper: is this mode integrated?
function useIntegrated() {
  const store = useStore();
  return store.state.mode === 'integrated';
}

Object.assign(window, {
  AppIcon, AppMoney, AppStatusPill, AppSLATimer, AppPayerChip,
  AppBtn, AppCard, AppSectionLabel, AppKBD, AppAvatar, AppFlagChip,
  AppField, AppInput, AppTextarea, AppSelect, AppModal, AppEmpty,
  HmsBadge, useIntegrated,
});
