/* UI primitives ===================================================== */
const { useState, useEffect, useRef, useMemo, useCallback, Fragment } = React;

/* ---------- Icons (inline SVG; no external icon lib needed) -------- */
function Icon({ name, size=16, className='', style }) {
  const props = { width:size, height:size, viewBox:'0 0 24 24', fill:'none', stroke:'currentColor', strokeWidth:1.6, strokeLinecap:'round', strokeLinejoin:'round', className, style };
  switch (name) {
    case 'chev-down':  return <svg {...props}><polyline points="6 9 12 15 18 9"/></svg>;
    case 'chev-right': return <svg {...props}><polyline points="9 6 15 12 9 18"/></svg>;
    case 'chev-left':  return <svg {...props}><polyline points="15 6 9 12 15 18"/></svg>;
    case 'plus':       return <svg {...props}><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>;
    case 'x':          return <svg {...props}><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>;
    case 'search':     return <svg {...props}><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>;
    case 'calendar':   return <svg {...props}><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>;
    case 'bell':       return <svg {...props}><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>;
    case 'sun':        return <svg {...props}><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>;
    case 'moon':       return <svg {...props}><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>;
    case 'settings':   return <svg {...props}><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06A2 2 0 1 1 4.27 16.95l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06A2 2 0 1 1 7.05 4.27l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>;
    case 'sliders':    return <svg {...props}><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></svg>;
    case 'play':       return <svg {...props}><polygon points="5 3 19 12 5 21 5 3"/></svg>;
    case 'pause':      return <svg {...props}><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>;
    case 'trend-up':   return <svg {...props}><polyline points="3 17 9 11 13 15 21 7"/><polyline points="14 7 21 7 21 14"/></svg>;
    case 'trend-down': return <svg {...props}><polyline points="3 7 9 13 13 9 21 17"/><polyline points="14 17 21 17 21 10"/></svg>;
    case 'alert':      return <svg {...props}><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>;
    case 'info':       return <svg {...props}><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>;
    case 'check':      return <svg {...props}><polyline points="20 6 9 17 4 12"/></svg>;
    case 'zap':        return <svg {...props}><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>;
    case 'columns':    return <svg {...props}><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/></svg>;
    case 'mediaplan':  return <svg {...props}><rect x="4" y="3" width="16" height="18" rx="2"/><line x1="8" y1="8" x2="16" y2="8"/><line x1="8" y1="12" x2="16" y2="12"/><line x1="8" y1="16" x2="13" y2="16"/></svg>;
    case 'menu':       return <svg {...props}><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>;
    case 'mobile':     return <svg {...props}><rect x="7" y="2" width="10" height="20" rx="2"/><line x1="11" y1="18" x2="13" y2="18"/></svg>;
    case 'monitor':    return <svg {...props}><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>;
    case 'tablet':     return <svg {...props}><rect x="4" y="2" width="16" height="20" rx="2"/><line x1="11" y1="18" x2="13" y2="18"/></svg>;
    case 'rocket':     return <svg {...props}><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="M12 15l-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/></svg>;
    case 'arrow-up':   return <svg {...props}><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg>;
    case 'arrow-down': return <svg {...props}><line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/></svg>;
    case 'arrow-right':return <svg {...props}><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>;
    case 'filter':     return <svg {...props}><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>;
    case 'dot':        return <svg {...props} width={size} height={size} viewBox="0 0 24 24"><circle cx="12" cy="12" r="5" fill="currentColor"/></svg>;
    case 'link-break': return <svg {...props}><path d="M9 17H7A5 5 0 0 1 7 7"/><path d="M15 7h2a5 5 0 0 1 4.54 7.1"/><line x1="2" y1="2" x2="22" y2="22"/></svg>;
    case 'menu-dots':  return <svg {...props}><circle cx="5" cy="12" r="1.2" fill="currentColor"/><circle cx="12" cy="12" r="1.2" fill="currentColor"/><circle cx="19" cy="12" r="1.2" fill="currentColor"/></svg>;
    case 'eye':        return <svg {...props}><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z"/><circle cx="12" cy="12" r="3"/></svg>;
    case 'eye-off':    return <svg {...props}><path d="M17.94 17.94A10.07 10.07 0 0 1 12 19c-7 0-11-7-11-7a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 7 11 7a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>;
    case 'sparkles':   return <svg {...props}><path d="M12 3l1.6 4.4L18 9l-4.4 1.6L12 15l-1.6-4.4L6 9l4.4-1.6L12 3z"/><path d="M5 18l.8 2.2L8 21l-2.2.8L5 24l-.8-2.2L2 21l2.2-.8z"/></svg>;
    case 'image':      return <svg {...props}><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>;
    case 'link':       return <svg {...props}><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>;
    case 'refresh':    return <svg {...props}><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>;
    case 'upload':     return <svg {...props}><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>;
    case 'download':   return <svg {...props}><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>;
    case 'trash':      return <svg {...props}><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>;
    case 'lock':       return <svg {...props}><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>;
    case 'palette':    return <svg {...props}><circle cx="13.5" cy="6.5" r=".9" fill="currentColor"/><circle cx="17.5" cy="10.5" r=".9" fill="currentColor"/><circle cx="8.5" cy="7.5" r=".9" fill="currentColor"/><circle cx="6.5" cy="12.5" r=".9" fill="currentColor"/><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.93 0 1.68-.75 1.68-1.68 0-.44-.16-.84-.44-1.14-.27-.3-.43-.7-.43-1.13 0-.93.75-1.68 1.68-1.68H16.5c3.04 0 5.5-2.46 5.5-5.5C22 6 17.5 2 12 2z"/></svg>;
    case 'users':      return <svg {...props}><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>;
    case 'star':       return <svg {...props}><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>;
    case 'file':       return <svg {...props}><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="13" y2="17"/></svg>;
    default: return null;
  }
}

/* ---------- Live dot ----------------------------------------------- */
function LiveDot({ tone='#22c55e', size=8 }){
  return (
    <span className="relative inline-flex items-center justify-center" style={{width:size, height:size}}>
      <span className="absolute inset-0 rounded-full animate-pulse2" style={{background:tone}}/>
      <span className="relative rounded-full" style={{width:size, height:size, background:tone}}/>
    </span>
  );
}

/* ---------- Card --------------------------------------------------- */
function Card({ className='', children, ...rest }){
  return (
    <div className={"rounded-xl border border-ink-200/80 dark:border-white/10 bg-white dark:bg-ink-900/60 shadow-card "+className} {...rest}>
      {children}
    </div>
  );
}

/* ---------- Pill / Tag -------------------------------------------- */
function Pill({ children, tone='neutral', className='' }){
  const tones = {
    neutral: 'bg-ink-100 text-ink-700 dark:bg-white/5 dark:text-ink-200',
    accent:  'bg-accent-100 text-accent-700 dark:bg-accent-500/15 dark:text-accent-200',
    good:    'bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300',
    bad:     'bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-300',
    warn:    'bg-amber-100 text-amber-800 dark:bg-amber-500/15 dark:text-amber-300',
    info:    'bg-sky-100 text-sky-700 dark:bg-sky-500/15 dark:text-sky-300',
  };
  return <span className={`inline-flex items-center gap-1 text-[11px] font-medium px-2 py-0.5 rounded-full ${tones[tone]||tones.neutral} ${className}`}>{children}</span>;
}

/* ---------- Toggle ------------------------------------------------- */
function Toggle({ checked, onChange, size='md', label, tone='#10b981' }){
  const W = size==='sm' ? 28 : 38, H = size==='sm' ? 16 : 22, K = size==='sm' ? 12 : 18;
  const off = (H-K)/2;
  return (
    <button type="button"
      role="switch"
      aria-checked={checked}
      onClick={()=>onChange(!checked)}
      style={{ width:W, height:H, background: checked ? tone : undefined }}
      className={`relative rounded-full transition-colors duration-200 flex-shrink-0 ${checked?'':'bg-ink-300 dark:bg-white/20'}`}
      aria-label={label||'toggle'}>
      <span style={{ width:K, height:K, top:off, left: checked ? W-K-off : off }}
        className="absolute rounded-full bg-white shadow-[0_1px_2px_rgba(0,0,0,0.3)] transition-all duration-200"/>
    </button>
  );
}

/* ---------- IconButton --------------------------------------------- */
function IconButton({ children, onClick, active=false, className='', title }){
  return (
    <button onClick={onClick} title={title}
      className={`inline-flex items-center justify-center h-9 w-9 rounded-lg border transition
                  ${active?'border-accent-300 bg-accent-50 text-accent-700 dark:bg-accent-500/15 dark:border-accent-500/40 dark:text-accent-200'
                          :'border-ink-200 hover:bg-ink-100 dark:border-white/10 dark:hover:bg-white/5'} ${className}`}>
      {children}
    </button>
  );
}

/* ---------- Btn ---------------------------------------------------- */
function Btn({ children, onClick, variant='ghost', size='md', className='', icon }){
  const sizeCls = size==='sm' ? 'h-8 px-2.5 text-[12px]' : 'h-9 px-3 text-[13px]';
  const variants = {
    primary: 'bg-ink-900 hover:bg-ink-800 text-white dark:bg-white dark:hover:bg-ink-100 dark:text-ink-900',
    accent:  'bg-accent-500 hover:bg-accent-600 text-white',
    ghost:   'border border-ink-200 hover:bg-ink-100 text-ink-800 dark:border-white/10 dark:hover:bg-white/5 dark:text-ink-100',
    subtle:  'bg-ink-100 hover:bg-ink-200 text-ink-800 dark:bg-white/5 dark:hover:bg-white/10 dark:text-ink-100',
    danger:  'bg-rose-600 hover:bg-rose-700 text-white',
  };
  return (
    <button onClick={onClick} className={`inline-flex items-center gap-1.5 rounded-lg font-medium transition ${sizeCls} ${variants[variant]} ${className}`}>
      {icon && <Icon name={icon} size={14}/>}
      {children}
    </button>
  );
}

/* ---------- Dropdown ----------------------------------------------- */
function useOutsideClick(ref, onClose){
  useEffect(()=>{
    function handler(e){ if(!ref.current) return; if(!ref.current.contains(e.target)) onClose(); }
    document.addEventListener('mousedown', handler);
    return ()=>document.removeEventListener('mousedown', handler);
  }, [onClose]);
}

function Dropdown({ trigger, children, align='left', width=240 }){
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useOutsideClick(ref, ()=>setOpen(false));
  return (
    <div className="relative" ref={ref}>
      <div onClick={()=>setOpen(o=>!o)}>{trigger}</div>
      {open && (
        <div className={`absolute z-30 mt-2 ${align==='right'?'right-0 origin-top-right':'left-0 origin-top-left'} rounded-xl border border-ink-200 dark:border-white/10 bg-white dark:bg-ink-800 shadow-float p-1 animate-dropin`}
          style={{width}}>
          {typeof children === 'function' ? children(()=>setOpen(false)) : children}
        </div>
      )}
    </div>
  );
}

/* ---------- Sparkline (svg) ---------------------------------------- */
function Sparkline({ data, width, height=36, tone='#8351ff', area=true, strokeWidth=1.6 }){
  if (!data || !data.length) return null;
  const responsive = width === undefined;
  const W = width || 200;
  const H = height;
  const vals = data.map(d => d.v);
  const min = Math.min(...vals), max = Math.max(...vals);
  const pad = 2;
  const x = i => pad + (i*(W-2*pad)/(data.length-1));
  const y = v => H - pad - ((v-min)/((max-min)||1))*(H-2*pad);
  const path = data.map((d,i)=>(i===0?'M':'L')+x(i).toFixed(2)+','+y(d.v).toFixed(2)).join(' ');
  const areaPath = path + ` L ${x(data.length-1).toFixed(2)},${H-pad} L ${x(0).toFixed(2)},${H-pad} Z`;
  const id = useMemo(()=>'sg'+Math.random().toString(36).slice(2,8), []);
  const svgProps = responsive
    ? { viewBox: `0 0 ${W} ${H}`, preserveAspectRatio:'none', style:{width:'100%', height:H} }
    : { width: W, height: H };
  return (
    <svg {...svgProps} className="block">
      <defs>
        <linearGradient id={id} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={tone} stopOpacity="0.25"/>
          <stop offset="100%" stopColor={tone} stopOpacity="0"/>
        </linearGradient>
      </defs>
      {area && <path d={areaPath} fill={`url(#${id})`} stroke="none"/>}
      <path d={path} fill="none" stroke={tone} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round" vectorEffect="non-scaling-stroke"/>
      <circle cx={x(data.length-1)} cy={y(data[data.length-1].v)} r="2" fill={tone}/>
    </svg>
  );
}

/* ---------- Rolling ticker number ---------------------------------- */
function Ticker({ value, format='int', tone='neutral', className='' }){
  /* Subtle: don't fade in/out — gently translate the new value into place */
  const prev = useRef(value);
  const [bump, setBump] = useState(0);
  useEffect(()=>{
    if (value === prev.current) return;
    prev.current = value;
    setBump(b=>b+1);
  }, [value]);
  return (
    <span key={bump} className={`inline-block animate-ticker tabular ${className}`}>
      {fmt(value, format)}
    </span>
  );
}

/* ---------- Tab strip ---------------------------------------------- */
function Tabs({ items, value, onChange }){
  const ref = useRef(null);
  const [pill, setPill] = useState({left:0, width:0});
  useEffect(()=>{
    if (!ref.current) return;
    const el = ref.current.querySelector(`[data-tab="${value}"]`);
    if (!el) return;
    const parentRect = ref.current.getBoundingClientRect();
    const r = el.getBoundingClientRect();
    setPill({ left: r.left - parentRect.left, width: r.width });
  }, [value, items.length]);
  return (
    <div ref={ref} className="relative inline-flex items-center gap-0 bg-ink-100/70 dark:bg-white/5 rounded-xl p-1">
      <span className="absolute top-1 bottom-1 rounded-lg bg-white dark:bg-ink-800 shadow-card transition-all duration-300"
        style={{ left: pill.left, width: pill.width }} />
      {items.map(it=>(
        <button key={it.id} data-tab={it.id} onClick={()=>onChange(it.id)}
          className={`relative z-[1] px-3.5 h-8 text-[13px] font-medium rounded-lg flex items-center gap-1.5 transition whitespace-nowrap
            ${value===it.id ? 'text-ink-900 dark:text-white' : 'text-ink-500 dark:text-ink-300 hover:text-ink-800 dark:hover:text-white'}`}>
          {it.icon
            ? <span className={`inline-flex items-center justify-center w-[18px] h-[18px] flex-shrink-0 transition ${value===it.id?'text-ink-900 dark:text-white':'text-ink-500 dark:text-ink-300'}`}><BrandIcon name={it.icon} size={18}/></span>
            : it.glyph && (
            <span className="inline-flex items-center justify-center w-4 h-4 rounded text-[10px] font-bold flex-shrink-0"
              style={{ background: value===it.id ? (it.tone+'22') : 'transparent', color: it.tone }}>
              {it.glyph}
            </span>
          )}
          {it.label}
        </button>
      ))}
    </div>
  );
}

/* ---------- Modal -------------------------------------------------- */
function Modal({ open, onClose, children, width=560 }){
  useEffect(()=>{
    if (!open) return;
    const onKey = e => e.key==='Escape' && onClose();
    document.addEventListener('keydown', onKey);
    return ()=>document.removeEventListener('keydown', onKey);
  }, [open, onClose]);
  if (!open) return null;
  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center">
      <div className="absolute inset-0 bg-ink-900/50 backdrop-blur-sm animate-fade" onClick={onClose}/>
      <div className="relative bg-white dark:bg-ink-800 rounded-2xl shadow-float border border-ink-200 dark:border-white/10 animate-popin max-h-[88vh] overflow-hidden flex flex-col" style={{width}}>
        {children}
      </div>
    </div>
  );
}

/* ---------- Drawer (right) ----------------------------------------- */
function Drawer({ open, onClose, children, width=480, title, footer }){
  if (!open) return null;
  return (
    <div className="fixed inset-0 z-50 flex justify-end">
      <div className="absolute inset-0 bg-ink-900/30 backdrop-blur-[2px] animate-fade" onClick={onClose}/>
      <div className="relative h-full bg-white dark:bg-ink-800 border-l border-ink-200 dark:border-white/10 shadow-float animate-slidein flex flex-col" style={{width}}>
        {title && (
          <div className="flex items-center justify-between px-5 h-14 border-b border-ink-200 dark:border-white/10">
            <div className="text-[14px] font-semibold flex items-center gap-2">{title}</div>
            <button onClick={onClose} className="h-8 w-8 inline-flex items-center justify-center rounded-lg hover:bg-ink-100 dark:hover:bg-white/5"><Icon name="x"/></button>
          </div>
        )}
        <div className="flex-1 overflow-auto scroll-thin">{children}</div>
        {footer && <div className="px-5 h-14 border-t border-ink-200 dark:border-white/10 flex items-center justify-end gap-2">{footer}</div>}
      </div>
    </div>
  );
}

/* ---------- Platform chip ------------------------------------------ */
function PlatformChip({ p, size=18 }){
  const plat = PLATFORMS.find(x=>x.id===p) || PLATFORMS[0];
  if (plat.icon){
    return (
      <span className="inline-flex items-center justify-center text-ink-900 dark:text-white" style={{ width:size, height:size }}>
        <BrandIcon name={plat.icon} size={size}/>
      </span>
    );
  }
  return (
    <span className="inline-flex items-center justify-center rounded font-bold"
      style={{ width:size, height:size, background: plat.tone+'1f', color: plat.tone, fontSize: size*0.55 }}>
      {plat.glyph}
    </span>
  );
}

/* ---------- Export -------------------------------------------------- */
Object.assign(window, {
  Icon, LiveDot, Card, Pill, Toggle, IconButton, Btn,
  Dropdown, useOutsideClick, Sparkline, Ticker, Tabs, Modal, Drawer,
  PlatformChip,
});
/* v2 — refresh icon added 2026-07-02 */
