// Full UI kit — 18 primitives. Dark-first, token-backed, no hardcoded hex.
// Supersedes 00-host-primitives.jsx for v2 bundle.

const { useState, useEffect, useRef, useMemo, useCallback, createContext, useContext } = React;

// ============================================================================
// Formatters
// ============================================================================
const fmt = {
  sek: (n) => new Intl.NumberFormat('sv-SE').format(Math.round(n)) + ' kr',
  num: (n) => new Intl.NumberFormat('sv-SE').format(n),
  pct: (n, d=1) => `${n.toFixed(d)}%`,
  time: (t) => t, // already in HH:MM
  shortDate: (d) => d,
};

// ============================================================================
// Icon glyph set — 36 icons, all 1.4 stroke
// ============================================================================
const I = (path, viewBox='0 0 16 16') => ({size=14, color='currentColor', fill='none', stroke=1.4}={}) =>
  React.createElement('svg', { width: size, height: size, viewBox, fill, stroke: color, strokeWidth: stroke, strokeLinecap: 'round', strokeLinejoin: 'round' }, path);

const Icon = {
  home:     I(<path d="M2 7l6-5 6 5v7H2z M6 14v-4h4v4"/>),
  sparkle:  I(<g><path d="M8 2l1.5 4L13 7.5 9.5 9 8 13 6.5 9 3 7.5 6.5 6z"/><path d="M13 2.5v2M12 3.5h2"/></g>),
  book:     I(<path d="M3 2h8a2 2 0 012 2v10H5a2 2 0 01-2-2V2z M3 12h10"/>),
  check:    I(<path d="M3 8l3 3 7-7"/>),
  bell:     I(<path d="M4 7a4 4 0 118 0v4l1 2H3l1-2V7z M6.5 13.5a1.5 1.5 0 003 0"/>),
  newspaper:I(<g><path d="M2 3h11v10H2z"/><path d="M4 5h7M4 7h7M4 9h4"/></g>),
  calendar: I(<g><rect x="2" y="3" width="12" height="11" rx="1"/><path d="M2 6h12M5 2v2M11 2v2"/></g>),
  activity: I(<path d="M1 8h3l2-5 3 10 2-5h4"/>),
  door:     I(<g><rect x="4" y="2" width="8" height="12"/><circle cx="10" cy="8" r="0.4" fill="currentColor"/></g>),
  grid:     I(<g><rect x="2" y="2" width="5" height="5"/><rect x="9" y="2" width="5" height="5"/><rect x="2" y="9" width="5" height="5"/><rect x="9" y="9" width="5" height="5"/></g>),
  flame:    I(<path d="M8 14c-3 0-5-2-5-5 0-2 1-3 2-4 0 2 1 3 2 3-1-3 1-5 3-6-1 3 0 4 2 6 1 1 1 2 1 3 0 3-2 3-5 3z"/>),
  plate:    I(<g><circle cx="8" cy="8" r="6"/><circle cx="8" cy="8" r="3"/></g>),
  notebook: I(<g><rect x="3" y="2" width="10" height="12"/><path d="M3 5h10M6 2v12"/></g>),
  link:     I(<g><path d="M6 10a3 3 0 01-3-3V5a3 3 0 013-3h1"/><path d="M10 6a3 3 0 013 3v2a3 3 0 01-3 3H9"/><path d="M6 8h4"/></g>),
  wine:     I(<path d="M5 2h6l-1 5a3 3 0 01-2 2 3 3 0 01-2-2L5 2z M8 9v4M6 14h4"/>),
  list:     I(<path d="M3 4h10M3 8h10M3 12h10"/>),
  cellar:   I(<g><path d="M2 3h12v11H2z"/><circle cx="5" cy="7" r="1.2"/><circle cx="11" cy="7" r="1.2"/><circle cx="8" cy="11" r="1.2"/></g>),
  box:      I(<g><path d="M2 5l6-3 6 3v7l-6 3-6-3z"/><path d="M2 5l6 3 6-3M8 8v7"/></g>),
  cart:     I(<path d="M2 3h2l2 8h7l1-5H5 M7 14a1 1 0 100-2 1 1 0 000 2z M12 14a1 1 0 100-2 1 1 0 000 2z"/>),
  plus:     I(<path d="M8 3v10M3 8h10"/>),
  receipt:  I(<path d="M3 2h10v12l-2-1-1.5 1L8 13l-1.5 1L5 13l-2 1V2z M5 5h6M5 8h6M5 11h3"/>),
  users:    I(<g><circle cx="6" cy="6" r="2"/><circle cx="11" cy="6" r="1.5"/><path d="M2 14c0-2 2-3 4-3s4 1 4 3M10 11c2 0 4 1 4 3"/></g>),
  user:     I(<g><circle cx="8" cy="5" r="2.5"/><path d="M3 14c0-3 2-5 5-5s5 2 5 5"/></g>),
  star:     I(<path d="M8 2l1.8 4L14 6.5l-3 3 1 4-4-2-4 2 1-4-3-3 4.2-.5z"/>),
  chart:    I(<path d="M2 14V3 M2 14h12 M5 11V7 M8 11V5 M11 11V8"/>),
  ledger:   I(<g><rect x="3" y="2" width="10" height="12"/><path d="M6 5h5M6 8h5M6 11h3"/></g>),
  percent:  I(<g><circle cx="4.5" cy="4.5" r="1.5"/><circle cx="11.5" cy="11.5" r="1.5"/><path d="M13 3l-10 10"/></g>),
  alert:    I(<path d="M8 2l7 12H1L8 2z M8 6v4 M8 12v.5"/>),
  matrix:   I(<g><rect x="2" y="2" width="5" height="5"/><rect x="9" y="2" width="5" height="5"/><rect x="2" y="9" width="5" height="5"/><rect x="9" y="9" width="5" height="5"/><path d="M7 4.5h2M4.5 7v2"/></g>),
  delta:    I(<path d="M8 2l6 11H2L8 2z"/>),
  target:   I(<g><circle cx="8" cy="8" r="6"/><circle cx="8" cy="8" r="3"/><circle cx="8" cy="8" r="0.5" fill="currentColor"/></g>),
  team:     I(<g><circle cx="5" cy="6" r="2"/><circle cx="11" cy="6" r="2"/><path d="M1 13c0-2 2-3 4-3s4 1 4 3M7 13c0-2 2-3 4-3s4 1 4 3"/></g>),
  clock:    I(<g><circle cx="8" cy="8" r="6"/><path d="M8 4v4l3 2"/></g>),
  edit:     I(<path d="M2 14h12 M11 3l2 2-7 7-3 1 1-3 7-7z"/>),
  away:     I(<g><circle cx="8" cy="8" r="6"/><path d="M5 8l2 2 4-4"/></g>),
  money:    I(<g><rect x="2" y="4" width="12" height="8"/><circle cx="8" cy="8" r="2"/></g>),
  coin:     I(<g><circle cx="8" cy="8" r="6"/><path d="M10 6.5a2 2 0 00-2-1.5c-1 0-2 .5-2 1.5s1 1.4 2 1.5c1 .1 2 .5 2 1.5S9 11 8 11a2 2 0 01-2-1.5 M8 4.5v1 M8 11v1"/></g>),
  forecast: I(<path d="M2 12l3-3 3 2 5-5"/>),
  plug:     I(<path d="M6 2v4 M10 2v4 M5 6h6v3a3 3 0 01-3 3 3 3 0 01-3-3V6z M8 12v3"/>),
  pulse:    I(<path d="M2 8h3l2-4 3 8 1-2h3"/>),
  scale:    I(<path d="M8 2v12 M3 2h10 M3 2l-2 5h4l-2-5 M13 2l2 5h-4l2-5"/>),
  envelope: I(<g><rect x="2" y="4" width="12" height="9"/><path d="M2 5l6 5 6-5"/></g>),
  shield:   I(<path d="M8 2l5 2v4c0 3-2 5-5 6-3-1-5-3-5-6V4l5-2z"/>),
  wand:     I(<path d="M3 13l8-8 M10 2l1 3 3 1-3 1-1 3-1-3-3-1 3-1z"/>),
  gear:     I(<g><circle cx="8" cy="8" r="2.5"/><path d="M8 1v2 M8 13v2 M3 3l1.5 1.5 M11.5 11.5L13 13 M1 8h2 M13 8h2 M3 13l1.5-1.5 M11.5 4.5L13 3"/></g>),
  lock:     I(<g><rect x="3" y="7" width="10" height="7"/><path d="M5 7V4a3 3 0 016 0v3"/></g>),
  truck:    I(<g><rect x="1" y="5" width="9" height="6"/><path d="M10 7h3l2 2v2h-5z M4 13a1 1 0 100-2 1 1 0 000 2z M12 13a1 1 0 100-2 1 1 0 000 2z"/></g>),
  leaf:     I(<path d="M2 14c0-7 5-12 12-12 0 7-5 12-12 12 M2 14c3-3 6-5 9-6"/>),
  card:     I(<g><rect x="2" y="4" width="12" height="9"/><path d="M2 7h12"/></g>),
  download: I(<path d="M8 2v10 M4 8l4 4 4-4 M2 14h12"/>),
  key:      I(<g><circle cx="5" cy="10" r="3"/><path d="M7 9l6-6 2 2-2 2M11 5l2 2"/></g>),
  webhook:  I(<g><circle cx="5" cy="5" r="2"/><circle cx="11" cy="5" r="2"/><circle cx="8" cy="12" r="2"/><path d="M6 6l-2 4 M10 6l2 4 M8 10v-3"/></g>),
  message:  I(<path d="M2 3h12v9h-5l-3 2v-2H2z"/>),
  archive:  I(<g><rect x="2" y="3" width="12" height="3"/><rect x="3" y="6" width="10" height="8"/><path d="M6 9h4"/></g>),
  search:   I(<g><circle cx="7" cy="7" r="4"/><path d="M10 10l4 4"/></g>),
  undo:     I(<path d="M3 7h7a3 3 0 010 6H8 M3 7l3-3 M3 7l3 3"/>),
  close:    I(<path d="M4 4l8 8M12 4l-8 8"/>),
  chevronR: I(<path d="M6 3l5 5-5 5"/>),
  chevronD: I(<path d="M3 6l5 5 5-5"/>),
  chevronUp:I(<path d="M3 10l5-5 5 5"/>),
  refresh:  I(<path d="M13 7a5 5 0 11-2-4 M13 2v4h-4"/>),
  menu:     I(<path d="M2 4h12M2 8h12M2 12h12"/>),
  wifiOff:  I(<path d="M1 5a10 10 0 0114 0 M3 8a7 7 0 019-1 M1 1l14 14"/>),
  filter:   I(<path d="M2 3h12l-4 5v5l-4-2V8z"/>),
  external: I(<path d="M6 3H3v10h10v-3 M10 3h3v3 M8 8l5-5"/>),
  more:     I(<g><circle cx="4" cy="8" r="1" fill="currentColor" stroke="none"/><circle cx="8" cy="8" r="1" fill="currentColor" stroke="none"/><circle cx="12" cy="8" r="1" fill="currentColor" stroke="none"/></g>),
  warning:  I(<path d="M8 2l7 12H1L8 2z M8 7v3 M8 12v.5"/>),
  trending: I(<path d="M1 12l5-5 3 3 6-6 M11 4h4v4"/>),
  trendingDown:I(<path d="M1 4l5 5 3-3 6 6 M11 12h4v-4"/>),
  eye:      I(<g><path d="M1 8s3-5 7-5 7 5 7 5-3 5-7 5-7-5-7-5z"/><circle cx="8" cy="8" r="2"/></g>),
};

// ============================================================================
// 1. Card
// ============================================================================
function Card({ children, elevated, selected, onClick, style, className='', padded=false }) {
  const base = {
    background: elevated ? 'var(--bg-elevated)' : 'var(--bg-secondary)',
    border: `1px solid ${selected ? 'var(--accent-gold-ring)' : 'var(--border-subtle)'}`,
    borderRadius: 'var(--r-lg)',
    boxShadow: selected ? 'var(--shadow-gold)' : 'var(--shadow-1)',
    transition: 'background 160ms ease, border-color 160ms ease, box-shadow 220ms ease',
    padding: padded ? 'var(--s-5)' : 0,
    ...style,
  };
  if (onClick) {
    return <div role="button" tabIndex={0} onClick={onClick} onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') onClick(e); }} className={className} style={{ ...base, cursor:'pointer' }}>{children}</div>;
  }
  return <div className={className} style={base}>{children}</div>;
}

// ============================================================================
// 2. Badge
// ============================================================================
const BADGE = {
  neutral:  { bg:'var(--bg-elevated)',    fg:'var(--text-secondary)', ring:'var(--border-default)' },
  success:  { bg:'var(--success-soft)',   fg:'var(--success)',         ring:'var(--success-ring)' },
  warning:  { bg:'var(--warning-soft)',   fg:'var(--warning)',         ring:'var(--warning-ring)' },
  critical: { bg:'var(--critical-soft)',  fg:'var(--critical)',        ring:'var(--critical-ring)' },
  gold:     { bg:'var(--accent-gold-soft)', fg:'var(--accent-gold)',   ring:'var(--accent-gold-ring)' },
  info:     { bg:'var(--info-soft)',      fg:'var(--info)',            ring:'var(--info-soft)' },
};
function Badge({ variant='neutral', children, icon, size='md', style }) {
  const v = BADGE[variant];
  const pad = size==='sm' ? '2px 6px' : '3px 8px';
  const fs = size==='sm' ? 10 : 11;
  return <span style={{ display:'inline-flex', alignItems:'center', gap:4, padding:pad, fontSize:fs, fontWeight:600, letterSpacing:'0.03em', textTransform:'uppercase', color:v.fg, background:v.bg, border:`1px solid ${v.ring}`, borderRadius:'var(--r-sm)', lineHeight:1.2, whiteSpace:'nowrap', ...style }}>{icon}{children}</span>;
}

// ============================================================================
// 3. Pill
// ============================================================================
function Pill({ variant='neutral', children, icon, onClick, style }) {
  const v = BADGE[variant];
  const Comp = onClick ? 'button' : 'span';
  return <Comp onClick={onClick} style={{ display:'inline-flex', alignItems:'center', gap:6, padding:'5px 10px', fontSize:12, fontWeight:600, color:v.fg, background:v.bg, border:`1px solid ${v.ring}`, borderRadius:'var(--r-pill)', lineHeight:1, cursor: onClick ? 'pointer':'default', whiteSpace:'nowrap', ...style }}>{icon}{children}</Comp>;
}

// ============================================================================
// 4. Button
// ============================================================================
function Button({ variant='ghost', size='md', children, onClick, icon, iconRight, disabled, style, className='', type='button' }) {
  const sizes = {
    sm: { padding:'7px 12px', fontSize:12.5, minHeight:32 },
    md: { padding:'9px 14px', fontSize:13.5, minHeight:40 },
    lg: { padding:'13px 20px', fontSize:14.5, minHeight:48 },
  };
  const variants = {
    primary:  { background:'var(--accent-gold)', color:'var(--text-on-gold)', border:'1px solid var(--accent-gold)' },
    secondary:{ background:'var(--bg-elevated)', color:'var(--text-primary)', border:'1px solid var(--border-default)' },
    ghost:    { background:'transparent',        color:'var(--text-secondary)', border:'1px solid transparent' },
    outline:  { background:'transparent',        color:'var(--text-primary)',   border:'1px solid var(--border-default)' },
    danger:   { background:'var(--critical-soft)', color:'var(--critical)',     border:'1px solid var(--critical-ring)' },
    success:  { background:'var(--success-soft)', color:'var(--success)',       border:'1px solid var(--success-ring)' },
  };
  return <button type={type} disabled={disabled} onClick={onClick} className={className}
    style={{ display:'inline-flex', alignItems:'center', justifyContent:'center', gap:7, fontWeight:600, lineHeight:1, borderRadius:'var(--r-md)', transition:'background 140ms ease, border-color 140ms ease', whiteSpace:'nowrap', cursor: disabled?'not-allowed':'pointer', opacity: disabled?0.4:1, ...sizes[size], ...variants[variant], ...style }}>
    {icon}{children}{iconRight}
  </button>;
}

// ============================================================================
// 5. StatCard — top-bar/overview numeric
// ============================================================================
function StatCard({ label, value, trailing, tone, delta, deltaTone, icon, style }) {
  const tones = { gold:'var(--accent-gold)', success:'var(--success)', warning:'var(--warning)', critical:'var(--critical)' };
  return <div style={{ display:'flex', flexDirection:'column', gap:6, padding:'16px 18px', background:'var(--bg-secondary)', border:'1px solid var(--border-subtle)', borderRadius:'var(--r-lg)', minWidth:140, ...style }}>
    <div style={{ display:'flex', alignItems:'center', gap:6, fontSize:11, fontWeight:600, letterSpacing:'0.08em', color:'var(--text-tertiary)', textTransform:'uppercase' }}>
      {icon}{label}
    </div>
    <div style={{ display:'flex', alignItems:'baseline', gap:8 }}>
      <span className="num" style={{ fontSize:26, fontWeight:600, color:tones[tone] || 'var(--text-primary)', lineHeight:1 }}>{value}</span>
      {trailing && <span style={{ fontSize:12, color:'var(--text-tertiary)' }}>{trailing}</span>}
    </div>
    {delta && <div style={{ fontSize:11, color: deltaTone==='up'?'var(--success)':deltaTone==='down'?'var(--critical)':'var(--text-tertiary)', fontWeight:600 }}>{delta}</div>}
  </div>;
}

// ============================================================================
// 6. Avatar
// ============================================================================
function Avatar({ name, vip, size=32, role }) {
  const initials = useMemo(() => {
    const parts = name.replace(/·.*/,'').trim().split(/\s+/);
    return (parts[0]?.[0] || '') + (parts[parts.length-1]?.[0] || '');
  }, [name]);
  const goldTier = vip === 'VVIP' || vip === 'VIP';
  return <div title={name} style={{
    width:size, height:size, minWidth:size,
    borderRadius:'var(--r-pill)',
    background: goldTier ? 'var(--accent-gold-soft)' : 'var(--bg-elevated)',
    color: goldTier ? 'var(--accent-gold)' : 'var(--text-secondary)',
    border: `1px solid ${goldTier ? 'var(--accent-gold-ring)' : 'var(--border-default)'}`,
    display:'flex', alignItems:'center', justifyContent:'center',
    fontSize: size * 0.36, fontWeight: 700, letterSpacing:'0.02em',
    flexShrink:0,
  }}>{initials.toUpperCase()}</div>;
}

// ============================================================================
// 7. Money — tabular, currency-aware
// ============================================================================
function Money({ value, currency='kr', sign, size='md', tone, style }) {
  const sizes = { sm:12, md:14, lg:18, xl:24, '2xl':32 };
  const tones = { gold:'var(--accent-gold)', success:'var(--success)', critical:'var(--critical)', muted:'var(--text-tertiary)' };
  const formatted = new Intl.NumberFormat('sv-SE').format(Math.abs(Math.round(value)));
  const prefix = sign && value > 0 ? '+' : value < 0 ? '−' : '';
  return <span className="num" style={{ fontSize:sizes[size], fontWeight:600, color: tones[tone]||'var(--text-primary)', whiteSpace:'nowrap', letterSpacing:'-0.01em', ...style }}>
    {prefix}{formatted} <span style={{ color:'var(--text-tertiary)', fontWeight:500, fontSize:'0.78em', marginLeft:2 }}>{currency}</span>
  </span>;
}

// ============================================================================
// 8. NumberInput / Input
// ============================================================================
function Input({ value, onChange, placeholder, icon, size='md', style, type='text', suffix, ariaLabel }) {
  const sizes = { sm:{h:32, fs:12.5, pad:'6px 10px'}, md:{h:40, fs:13.5, pad:'10px 12px'}, lg:{h:48, fs:14.5, pad:'12px 14px'} };
  const s = sizes[size];
  return <div style={{ display:'inline-flex', alignItems:'center', height:s.h, background:'var(--bg-secondary)', border:'1px solid var(--border-default)', borderRadius:'var(--r-md)', padding:s.pad, transition:'border-color 140ms ease', minWidth:0, ...style }}>
    {icon && <span style={{ color:'var(--text-tertiary)', marginRight:8, display:'flex' }}>{icon}</span>}
    <input type={type} value={value||''} onChange={e => onChange?.(e.target.value)} placeholder={placeholder} aria-label={ariaLabel}
      style={{ background:'transparent', border:0, outline:0, color:'var(--text-primary)', fontFamily:'inherit', fontSize:s.fs, flex:1, minWidth:0, padding:0 }}/>
    {suffix && <span style={{ color:'var(--text-tertiary)', fontSize:s.fs, marginLeft:8 }}>{suffix}</span>}
  </div>;
}

// ============================================================================
// 9. Tabs
// ============================================================================
function Tabs({ tabs, value, onChange, style }) {
  return <div style={{ display:'flex', gap:2, borderBottom:'1px solid var(--border-subtle)', ...style }} role="tablist">
    {tabs.map(t => {
      const active = value === t.id;
      return <button key={t.id} role="tab" aria-selected={active} onClick={() => onChange(t.id)} style={{
        padding:'12px 16px', fontSize:13, fontWeight:600,
        color: active ? 'var(--text-primary)' : 'var(--text-tertiary)',
        borderBottom: `2px solid ${active ? 'var(--accent-gold)' : 'transparent'}`,
        background:'transparent',
        marginBottom:-1,
        cursor:'pointer',
        transition:'color 140ms ease, border-color 140ms ease',
      }}>{t.label}{t.count != null && <span className="num" style={{marginLeft:6, fontSize:11, color:'var(--text-tertiary)'}}>{t.count}</span>}</button>;
    })}
  </div>;
}

// ============================================================================
// 10. DataTable
// ============================================================================
function DataTable({ columns, rows, getKey, onRowClick, selectedKey, compact, emptyLabel='No results', style }) {
  return <div style={{ width:'100%', background:'var(--bg-secondary)', border:'1px solid var(--border-subtle)', borderRadius:'var(--r-lg)', overflow:'hidden', ...style }}>
    <div style={{
      display:'grid',
      gridTemplateColumns: columns.map(c => c.width || '1fr').join(' '),
      padding: compact ? '8px 14px' : '10px 16px',
      borderBottom:'1px solid var(--border-subtle)',
      background:'var(--bg-secondary)',
      fontSize:10, fontWeight:700, letterSpacing:'0.08em', color:'var(--text-tertiary)', textTransform:'uppercase',
    }}>
      {columns.map(c => <div key={c.key} style={{ textAlign: c.align || 'left' }}>{c.label}</div>)}
    </div>
    {rows.length === 0 ? (
      <div style={{ padding:32, textAlign:'center', color:'var(--text-tertiary)', fontSize:13 }}>{emptyLabel}</div>
    ) : rows.map((r, i) => {
      const k = getKey(r);
      const active = selectedKey === k;
      return <div key={k} role={onRowClick?'button':undefined} tabIndex={onRowClick?0:undefined}
        onClick={() => onRowClick?.(r)}
        onKeyDown={e => { if (onRowClick && (e.key==='Enter'||e.key===' ')) onRowClick(r); }}
        style={{
          display:'grid',
          gridTemplateColumns: columns.map(c => c.width || '1fr').join(' '),
          padding: compact ? '8px 14px' : '12px 16px',
          borderBottom: i < rows.length-1 ? '1px solid var(--border-subtle)' : 'none',
          background: active ? 'var(--row-selected)' : (i%2 ? 'var(--row-alt)' : 'transparent'),
          fontSize:13, color:'var(--text-primary)',
          cursor: onRowClick ? 'pointer' : 'default',
          alignItems:'center',
          transition:'background 100ms ease',
        }}
        onMouseEnter={e => { if (!active) e.currentTarget.style.background = 'var(--row-hover)'; }}
        onMouseLeave={e => { if (!active) e.currentTarget.style.background = i%2 ? 'var(--row-alt)' : 'transparent'; }}
      >
        {columns.map(c => <div key={c.key} style={{ textAlign: c.align || 'left', minWidth:0, overflow:'hidden', textOverflow:'ellipsis', whiteSpace: c.wrap?'normal':'nowrap' }}>{c.render ? c.render(r) : r[c.key]}</div>)}
      </div>;
    })}
  </div>;
}

// ============================================================================
// 11. ProgressBar
// ============================================================================
function ProgressBar({ value, max=100, tone='gold', label, size='md', style }) {
  const pct = Math.min(100, Math.max(0, (value/max)*100));
  const tones = { gold:'var(--accent-gold)', success:'var(--success)', warning:'var(--warning)', critical:'var(--critical)', info:'var(--info)' };
  const h = size==='sm' ? 4 : size==='lg' ? 10 : 6;
  return <div style={{ display:'flex', flexDirection:'column', gap:6, ...style }}>
    {label && <div style={{ display:'flex', justifyContent:'space-between', fontSize:11, color:'var(--text-secondary)' }}>
      <span>{label}</span>
      <span className="num">{Math.round(pct)}%</span>
    </div>}
    <div style={{ height:h, background:'var(--bg-elevated)', borderRadius:'var(--r-pill)', overflow:'hidden' }}>
      <div style={{ width:`${pct}%`, height:'100%', background:tones[tone], borderRadius:'var(--r-pill)', transition:'width 400ms ease' }}/>
    </div>
  </div>;
}

// ============================================================================
// 12. Toggle
// ============================================================================
function Toggle({ checked, onChange, label }) {
  return <label style={{ display:'inline-flex', alignItems:'center', gap:10, cursor:'pointer' }}>
    <span style={{
      position:'relative', width:36, height:20, borderRadius:'var(--r-pill)',
      background: checked ? 'var(--accent-gold)' : 'var(--bg-elevated)',
      border: `1px solid ${checked ? 'var(--accent-gold)' : 'var(--border-default)'}`,
      transition:'background 180ms ease',
    }}>
      <span style={{ position:'absolute', top:1, left: checked ? 16 : 1, width:16, height:16, background:checked?'var(--text-on-gold)':'var(--text-secondary)', borderRadius:'var(--r-pill)', transition:'left 180ms ease' }}/>
    </span>
    {label && <span style={{ fontSize:13, color:'var(--text-primary)' }}>{label}</span>}
    <input type="checkbox" checked={!!checked} onChange={e => onChange?.(e.target.checked)} style={{ position:'absolute', opacity:0, pointerEvents:'none' }}/>
  </label>;
}

// ============================================================================
// 13. ToggleGroup (segmented)
// ============================================================================
function ToggleGroup({ options, value, onChange, size='md', style }) {
  const pads = { sm:'6px 10px', md:'8px 12px', lg:'10px 14px' };
  const fs = { sm:12, md:13, lg:14 };
  return <div style={{ display:'inline-flex', background:'var(--bg-secondary)', border:'1px solid var(--border-default)', borderRadius:'var(--r-md)', padding:2, gap:2, ...style }}>
    {options.map(o => {
      const active = value === o.value;
      return <button key={o.value} onClick={() => onChange(o.value)} style={{
        padding: pads[size], fontSize:fs[size], fontWeight:600,
        color: active ? 'var(--text-primary)' : 'var(--text-tertiary)',
        background: active ? 'var(--bg-elevated)' : 'transparent',
        borderRadius:'var(--r-sm)', transition:'background 140ms ease',
      }}>{o.label}</button>;
    })}
  </div>;
}

// ============================================================================
// 14. SectionHeader
// ============================================================================
function SectionHeader({ title, subtitle, actions, size='lg', style }) {
  const sizes = { sm:{title:14, sub:11}, md:{title:17, sub:12}, lg:{title:22, sub:13}, xl:{title:28, sub:14} };
  const s = sizes[size];
  return <div style={{ display:'flex', alignItems:'flex-end', justifyContent:'space-between', gap:16, marginBottom:16, ...style }}>
    <div>
      <div style={{ fontSize:s.title, fontWeight:700, letterSpacing:'-0.015em', color:'var(--text-primary)', lineHeight:1.2 }}>{title}</div>
      {subtitle && <div style={{ fontSize:s.sub, color:'var(--text-tertiary)', marginTop:4, lineHeight:1.4 }}>{subtitle}</div>}
    </div>
    {actions && <div style={{ display:'flex', gap:8 }}>{actions}</div>}
  </div>;
}

// ============================================================================
// 15. KeyValue
// ============================================================================
function KeyValue({ k, v, align='row', size='md' }) {
  const fs = size==='sm' ? {l:10, v:12} : {l:11, v:14};
  if (align === 'col') {
    return <div style={{display:'flex', flexDirection:'column', gap:3}}>
      <div style={{ fontSize:fs.l, fontWeight:600, letterSpacing:'0.06em', color:'var(--text-tertiary)', textTransform:'uppercase' }}>{k}</div>
      <div style={{ fontSize:fs.v, color:'var(--text-primary)', fontWeight:500 }}>{v}</div>
    </div>;
  }
  return <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', padding:'8px 0', borderBottom:'1px solid var(--border-subtle)', gap:16 }}>
    <span style={{ fontSize:12, color:'var(--text-tertiary)' }}>{k}</span>
    <span style={{ fontSize:13, color:'var(--text-primary)', fontWeight:500, textAlign:'right' }}>{v}</span>
  </div>;
}

// ============================================================================
// 16. EmptyState
// ============================================================================
function EmptyState({ icon, title, body, cta, style }) {
  return <div style={{ display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', padding:'48px 32px', textAlign:'center', ...style }}>
    {icon && <div style={{ width:56, height:56, borderRadius:'var(--r-xl)', background:'var(--bg-elevated)', border:'1px solid var(--border-default)', display:'flex', alignItems:'center', justifyContent:'center', marginBottom:16, color:'var(--text-tertiary)' }}>{icon}</div>}
    <div style={{ fontSize:17, fontWeight:700, color:'var(--text-primary)' }}>{title}</div>
    {body && <div style={{ fontSize:13, color:'var(--text-secondary)', marginTop:8, maxWidth:320, lineHeight:1.5 }}>{body}</div>}
    {cta && <div style={{ marginTop:20, display:'flex', gap:10 }}>{cta}</div>}
  </div>;
}

// ============================================================================
// 17. Skeleton
// ============================================================================
function Skeleton({ w, h=14, style }) {
  return <div className="shimmer" style={{ width:w, height:h, borderRadius:'var(--r-sm)', ...style }}/>;
}

// ============================================================================
// 18. Sparkline (inline SVG)
// ============================================================================
function Sparkline({ data, width=120, height=32, tone='gold', fill=true }) {
  const tones = { gold:'var(--accent-gold)', success:'var(--success)', critical:'var(--critical)', info:'var(--info)', muted:'var(--text-tertiary)' };
  const min = Math.min(...data), max = Math.max(...data);
  const range = max - min || 1;
  const step = width / (data.length-1);
  const pts = data.map((d,i) => [i*step, height - ((d-min)/range)*height]);
  const path = pts.map((p,i) => (i===0?'M':'L') + p[0] + ',' + p[1]).join(' ');
  const area = path + ` L ${width},${height} L 0,${height} Z`;
  return <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}>
    {fill && <path d={area} fill={tones[tone]} opacity="0.1"/>}
    <path d={path} fill="none" stroke={tones[tone]} strokeWidth="1.5" strokeLinejoin="round" strokeLinecap="round"/>
  </svg>;
}

// ============================================================================
// 19. DeltaArrow
// ============================================================================
function DeltaArrow({ value, inverse=false, size=12 }) {
  const up = value > 0;
  const tone = (up && !inverse) || (!up && inverse) ? 'var(--success)' : 'var(--critical)';
  return <span style={{ display:'inline-flex', alignItems:'center', gap:2, color:tone, fontSize:size, fontWeight:600 }} className="num">
    <svg width={10} height={10} viewBox="0 0 10 10" fill="none" stroke={tone} strokeWidth="1.5" strokeLinecap="round"><path d={up ? 'M2 7l3-3 3 3' : 'M2 3l3 3 3-3'}/></svg>
    {up?'+':''}{value}%
  </span>;
}

// ============================================================================
// 20. Modal / Sheet
// ============================================================================
function Modal({ open, onClose, title, children, footer, size='md' }) {
  if (!open) return null;
  const widths = { sm:420, md:560, lg:720, xl:920 };
  return <div onClick={onClose} style={{ position:'fixed', inset:0, background:'var(--bg-scrim)', display:'flex', alignItems:'center', justifyContent:'center', zIndex:100, backdropFilter:'blur(4px)' }}>
    <div onClick={e=>e.stopPropagation()} className="fade-up" style={{ width:widths[size], maxWidth:'92vw', maxHeight:'86vh', background:'var(--bg-secondary)', border:'1px solid var(--border-default)', borderRadius:'var(--r-xl)', boxShadow:'var(--shadow-2)', display:'flex', flexDirection:'column', overflow:'hidden' }}>
      <div style={{ padding:'18px 22px', borderBottom:'1px solid var(--border-subtle)', display:'flex', alignItems:'center', justifyContent:'space-between' }}>
        <div style={{ fontSize:16, fontWeight:700 }}>{title}</div>
        <Button variant="ghost" size="sm" icon={<Icon.close/>} onClick={onClose}/>
      </div>
      <div className="scroll" style={{ padding:22, overflowY:'auto', flex:1 }}>{children}</div>
      {footer && <div style={{ padding:'14px 22px', borderTop:'1px solid var(--border-subtle)', display:'flex', justifyContent:'flex-end', gap:10 }}>{footer}</div>}
    </div>
  </div>;
}

Object.assign(window, {
  Icon, fmt,
  Card, Badge, Pill, Button, StatCard, Avatar, Money, Input,
  Tabs, DataTable, ProgressBar, Toggle, ToggleGroup, SectionHeader,
  KeyValue, EmptyState, Skeleton, Sparkline, DeltaArrow, Modal,
});
