// Shared UI primitives — exports to window

// Viewport breakpoint hook. Drives the mobile layout branches in app.jsx —
// the list/detail panes must swap rather than sit side by side, which CSS
// alone can't express against these inline styles.
const MOBILE_QUERY = '(max-width: 768px)';
function useIsMobile() {
  const [isMobile, setIsMobile] = React.useState(
    () => typeof window !== 'undefined' && window.matchMedia
      ? window.matchMedia(MOBILE_QUERY).matches
      : false
  );
  React.useEffect(() => {
    if (!window.matchMedia) return;
    const mq = window.matchMedia(MOBILE_QUERY);
    const onChange = e => setIsMobile(e.matches);
    setIsMobile(mq.matches);
    // Safari < 14 only has the deprecated listener API
    if (mq.addEventListener) mq.addEventListener('change', onChange);
    else mq.addListener(onChange);
    return () => {
      if (mq.removeEventListener) mq.removeEventListener('change', onChange);
      else mq.removeListener(onChange);
    };
  }, []);
  return isMobile;
}

// ── Themes ──────────────────────────────────────────────────────────────────
// Two palettes with identical key shapes. In both, the `slate` ramp runs
// surface → text: keys 50–300 are backgrounds, 400–900 is type. Components read
// only through these keys, so swapping the object recolours the entire app.

// True-neutral dark: R≈G≈B on every surface, so there is no blue cast. (The
// site shell's own #0d1117/#131a26 are blue-leaning; these deliberately are not.)
const THEME_DARK = {
  indigo: { 50:'#161616',100:'#1c1c1c',200:'#2a2a2a',300:'#3d3d3d',400:'#6366F1',500:'#6d8fff',600:'#3b82f6',700:'#4d91f7',800:'#93b4fb',900:'#c7dbfd' },
  blue:   { 50:'#161616',100:'#1c1c1c',500:'#3B82F6',600:'#60a5fa',700:'#93b4fb' },
  slate:  { 50:'#0a0a0a',100:'#141414',200:'#262626',300:'#383838',400:'#8a8a8a',500:'#9e9e9e',600:'#b5b5b5',700:'#d4d4d4',800:'#ededed',900:'#fafafa' },
  green:  { 500:'#22c55e',600:'#4ade80' },
  amber:  { 500:'#f59e0b',600:'#fbbf24' },
  rose:   { 200:'#4c2230',500:'#f43f5e',600:'#fb7185' },
  teal:   { 500:'#0ea5e9',600:'#38bdf8' },
  purple: { 500:'#8b5cf6',600:'#a78bfa' },
  pink:   { 500:'#EC4899',600:'#f472b6' },
  orange: { 500:'#F97316',600:'#fb923c' },
};

// Original light palette (Tailwind-ish), restored for the light setting.
const THEME_LIGHT = {
  indigo: { 50:'#EEF2FF',100:'#E0E7FF',200:'#C7D2FE',300:'#A5B4FC',400:'#818CF8',500:'#6366F1',600:'#4F46E5',700:'#4338CA',800:'#3730A3',900:'#312E81' },
  blue:   { 50:'#EFF6FF',100:'#DBEAFE',500:'#3B82F6',600:'#2563EB',700:'#1D4ED8' },
  slate:  { 50:'#F8FAFC',100:'#F1F5F9',200:'#E2E8F0',300:'#CBD5E1',400:'#94A3B8',500:'#64748B',600:'#475569',700:'#334155',800:'#1E293B',900:'#0F172A' },
  green:  { 500:'#10B981',600:'#059669' },
  amber:  { 500:'#F59E0B',600:'#D97706' },
  rose:   { 200:'#FECDD3',500:'#F43F5E',600:'#E11D48' },
  teal:   { 500:'#14B8A6',600:'#0D9488' },
  purple: { 500:'#A855F7',600:'#9333EA' },
  pink:   { 500:'#EC4899',600:'#DB2777' },
  orange: { 500:'#F97316',600:'#EA580C' },
};

// Surface-vs-paper tokens that can't come from the ramp (code blocks, tooltips,
// tinted callouts) — they need a different treatment per theme, not an inverted one.
const THEME_TOKENS = {
  dark:  { code:'#000000', codeText:'#d4d4d4', tooltip:'#262626', warnBg:'rgba(245,158,11,.08)',
           warnBorder:'rgba(245,158,11,.28)', okBg:'rgba(34,197,94,.09)', okBorder:'rgba(34,197,94,.28)',
           shadow:'0 1px 3px rgba(0,0,0,0.5)' },
  light: { code:'#0F172A', codeText:'#E2E8F0', tooltip:'#0F172A', warnBg:'#FFF7ED',
           warnBorder:'#FED7AA', okBg:'#F0FDF4', okBorder:'#BBF7D0',
           shadow:'0 1px 3px rgba(0,0,0,0.04)' },
};

const THEME_STORAGE_KEY = 'techlib-theme';
function getInitialTheme() {
  try {
    const saved = localStorage.getItem(THEME_STORAGE_KEY);
    if (saved === 'dark' || saved === 'light') return saved;
  } catch (e) { /* private mode / blocked storage — fall through */ }
  return 'light';
}

// Resolved once at load. Reading storage repeatedly risks the palette, the
// React state and the DOM attribute disagreeing on the very first paint.
const INITIAL_THEME = getInitialTheme();

// Live palette. Mutated in place on theme change so the ~300 existing
// `COLORS.slate[x]` reads across the app keep working untouched; the App then
// re-renders via state, and every component picks up the new values.
const COLORS = Object.assign({}, INITIAL_THEME === 'dark' ? THEME_DARK : THEME_LIGHT);
let TOKENS = Object.assign({}, THEME_TOKENS[INITIAL_THEME]);

function applyTheme(name) {
  const src = name === 'dark' ? THEME_DARK : THEME_LIGHT;
  Object.keys(src).forEach(family => { COLORS[family] = Object.assign({}, src[family]); });
  Object.assign(TOKENS, THEME_TOKENS[name]);
  refreshFamilyColors();
  const root = document.documentElement;
  root.setAttribute('data-theme', name);
  try { localStorage.setItem(THEME_STORAGE_KEY, name); } catch (e) { /* ignore */ }
}

// Which palette COLORS currently holds. Lets the render-time sync below skip
// the rewrite when nothing changed.
let CURRENT_THEME = INITIAL_THEME;

// Swap the palette WITHOUT touching the DOM or storage. This has to run during
// render: components read COLORS.* while rendering, so doing the swap in an
// effect (which fires after the render) leaves every inline style one theme
// behind — the UI would only catch up on the next reload.
function syncPalette(name) {
  if (name === CURRENT_THEME) return;
  const src = name === 'dark' ? THEME_DARK : THEME_LIGHT;
  Object.keys(src).forEach(family => { COLORS[family] = Object.assign({}, src[family]); });
  Object.assign(TOKENS, THEME_TOKENS[name]);
  refreshFamilyColors();
  CURRENT_THEME = name;
}

// Owns the theme for the app; returns [name, toggle].
function useTheme() {
  const [theme, setTheme] = React.useState(INITIAL_THEME);
  // Render-time: the palette must be correct before any child reads COLORS.
  syncPalette(theme);
  // Effect-time: DOM attribute + persistence are side effects, so they stay here.
  React.useEffect(() => {
    document.documentElement.setAttribute('data-theme', theme);
    try { localStorage.setItem(THEME_STORAGE_KEY, theme); } catch (e) { /* ignore */ }
  }, [theme]);
  return [theme, () => setTheme(t => (t === 'dark' ? 'light' : 'dark'))];
}

// Toggle control for the top bar
function ThemeToggle({ theme, onToggle, compact }) {
  const dark = theme === 'dark';
  return (
    <button onClick={onToggle}
      aria-label={dark ? 'Switch to light theme' : 'Switch to dark theme'}
      title={dark ? 'Switch to light theme' : 'Switch to dark theme'}
      style={{
        padding: compact ? '7px 11px' : '7px 12px', borderRadius:10,
        border:`1.5px solid ${COLORS.slate[200]}`, background:COLORS.slate[100],
        cursor:'pointer', fontSize:13, lineHeight:1, color:COLORS.slate[600],
        display:'flex', alignItems:'center', gap:6, flexShrink:0, transition:'all 0.12s'
      }}>
      {/* Shows the CURRENT theme, not the destination — labelling the target
          reads as a broken toggle that lags one click behind. */}
      {dark ? '☾' : '☀'}{compact ? '' : (dark ? ' Dark' : ' Light')}
    </button>
  );
}

// Rebuilt on theme change (see refreshFamilyColors below) so it never holds
// stale values from the palette that was active at load time.
const FAMILY_COLORS = {};
function refreshFamilyColors() {
  Object.assign(FAMILY_COLORS, {
    'Preference Research': COLORS.indigo[600],
    'Portfolio & Coverage Research': COLORS.teal[600],
    'Pricing Research': COLORS.purple[600],
    'Driver & Correlation Research': COLORS.green[600],
    'Segmentation Research': COLORS.amber[600],
    'Predictive & Explainable Modelling': COLORS.rose[600],
  });
}
refreshFamilyColors();

// Site nav — mirrors the .nav block in index.php so the library reads as part
// of DataIngrid. Links are relative to this file's location under the web root.
// Deliberately stays dark in both themes: it reproduces the site chrome, which
// is dark site-wide, so a light-themed page still sits under the same masthead.
const SITE_ROOT = '../../';
function SiteNav() {
  const links = [
    { label:'Features',    href: SITE_ROOT + 'index.php#features' },
    { label:'How it works',href: SITE_ROOT + 'index.php#how-it-works' },
    { label:'Data QC',     href: SITE_ROOT + 'index.php#data-qc' },
    { label:'PRF Stacking',href: SITE_ROOT + 'index.php#prf' },
  ];
  return (
    <div style={{
      display:'flex', alignItems:'center', justifyContent:'space-between',
      padding:'.6rem 2.5rem', background:'rgba(13,17,23,.85)',
      backdropFilter:'blur(18px)', WebkitBackdropFilter:'blur(18px)',
      borderBottom:'1px solid rgba(59,130,246,.1)', flexShrink:0
    }}>
      <a href={SITE_ROOT + 'index.php'} style={{
        display:'flex', alignItems:'center', gap:'.6rem', fontSize:'1.2rem',
        fontWeight:800, letterSpacing:'-.025em', textDecoration:'none', color:'#e8edf8'
      }}>
        <div style={{
          width:34, height:34, background:'linear-gradient(135deg, #3b82f6, #8b5cf6)',
          borderRadius:10, display:'grid', placeItems:'center'
        }}>
          <svg viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.2"
            strokeLinecap="round" strokeLinejoin="round" style={{ width:18, height:18 }}>
            <path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/>
          </svg>
        </div>
        Data<span style={{ color:'#3b82f6' }}>Ingrid</span>
      </a>
      <div style={{ display:'flex', alignItems:'center', gap:'2rem' }}>
        {links.map(l => (
          <a key={l.label} href={l.href} style={{
            color:'#7b90b0', textDecoration:'none', fontSize:'.9rem', fontWeight:500
          }}>{l.label}</a>
        ))}
        <span style={{ color:'#e8edf8', fontSize:'.9rem', fontWeight:600 }}>Analytics</span>
      </div>
    </div>
  );
}

// Badge
function Badge({ label, color, small }) {
  return (
    <span style={{
      display:'inline-flex', alignItems:'center', padding: small ? '2px 7px' : '3px 10px',
      borderRadius:999, fontSize: small ? 10 : 11, fontWeight:600, letterSpacing:'0.02em',
      background: color ? color + '18' : COLORS.slate[100],
      color: color || COLORS.slate[600], border:`1px solid ${color ? color + '30' : COLORS.slate[200]}`,
      whiteSpace:'nowrap'
    }}>{label}</span>
  );
}

// Icon button
function IconBtn({ icon, onClick, title, active, size=32 }) {
  const [hov, setHov] = React.useState(false);
  return (
    <button onClick={onClick} title={title}
      onMouseEnter={()=>setHov(true)} onMouseLeave={()=>setHov(false)}
      style={{
        width:size, height:size, borderRadius:8, border:'none', cursor:'pointer',
        display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0,
        background: active ? COLORS.indigo[100] : hov ? COLORS.slate[100] : 'transparent',
        color: active ? COLORS.indigo[700] : COLORS.slate[500],
        transition:'background 0.12s, color 0.12s', fontSize:14
      }}>
      {icon}
    </button>
  );
}

// Section heading inside detail
function SectionLabel({ children }) {
  return (
    <div style={{ fontSize:10, fontWeight:700, letterSpacing:'0.1em', textTransform:'uppercase',
      color:COLORS.slate[400], marginBottom:8 }}>
      {children}
    </div>
  );
}

// Divider
function Divider({ margin='16px 0' }) {
  return <div style={{ height:1, background:COLORS.slate[100], margin }} />;
}

// Chip row
function ChipRow({ items, color }) {
  if (!items || !items.length) return null;
  return (
    <div style={{ display:'flex', flexWrap:'wrap', gap:5 }}>
      {items.map((item,i) => <Badge key={i} label={item} color={color} small />)}
    </div>
  );
}

// Copy button
function CopyBtn({ text, label='Copy' }) {
  const [copied, setCopied] = React.useState(false);
  const copy = () => {
    navigator.clipboard.writeText(text).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 1800);
    });
  };
  return (
    <button onClick={copy} style={{
      padding:'5px 12px', borderRadius:6, border:`1px solid ${COLORS.slate[200]}`,
      background: copied ? COLORS.green[500] : COLORS.slate[100], color: copied ? 'white' : COLORS.slate[600],
      fontSize:12, fontWeight:600, cursor:'pointer', transition:'all 0.15s', display:'flex',
      alignItems:'center', gap:5
    }}>
      {copied ? '✓ Copied' : `⎘ ${label}`}
    </button>
  );
}

// Tab bar
function TabBar({ tabs, active, onChange }) {
  // overflowY must be explicit: with only overflowX set, CSS promotes the other
  // axis from visible to auto, and the buttons' -1px marginBottom is enough to
  // raise a stray 1px vertical scrollbar.
  return (
    <div data-tabstrip style={{ display:'flex', gap:2, padding:'0 0 0 0',
      overflowX:'auto', overflowY:'hidden',
      borderBottom:`1px solid ${COLORS.slate[200]}`, flexShrink:0,
      scrollbarWidth:'none' }}>
      {tabs.map(t => {
        const isActive = t.id === active;
        return (
          <button key={t.id} onClick={() => onChange(t.id)}
            style={{
              padding:'10px 16px', border:'none', background:'transparent', cursor:'pointer',
              fontSize:12, fontWeight: isActive ? 700 : 500,
              color: isActive ? COLORS.indigo[700] : COLORS.slate[500],
              borderBottom: isActive ? `2px solid ${COLORS.indigo[600]}` : '2px solid transparent',
              marginBottom:-1, whiteSpace:'nowrap', transition:'color 0.12s',
              letterSpacing:'0.01em'
            }}>{t.label}
          </button>
        );
      })}
    </div>
  );
}

// Collapsible
function Collapse({ title, children, defaultOpen=false }) {
  const [open, setOpen] = React.useState(defaultOpen);
  return (
    <div style={{ border:`1px solid ${COLORS.slate[200]}`, borderRadius:10, overflow:'hidden', marginBottom:8 }}>
      <button onClick={() => setOpen(o => !o)} style={{
        width:'100%', padding:'12px 16px', background: open ? COLORS.indigo[50] : COLORS.slate[100],
        border:'none', cursor:'pointer', display:'flex', justifyContent:'space-between',
        alignItems:'center', fontSize:13, fontWeight:600, color: open ? COLORS.indigo[800] : COLORS.slate[700],
        textAlign:'left', transition:'background 0.12s'
      }}>
        {title}
        <span style={{ fontSize:10, transform: open ? 'rotate(180deg)' : 'none', transition:'transform 0.18s', color:COLORS.slate[400] }}>▼</span>
      </button>
      {open && (
        <div style={{ padding:'14px 16px', borderTop:`1px solid ${COLORS.slate[100]}`, background:COLORS.slate[100] }}>
          {children}
        </div>
      )}
    </div>
  );
}

// Stat card
function StatCard({ label, value, sub, color }) {
  return (
    <div style={{
      background:COLORS.slate[100], border:`1px solid ${COLORS.slate[200]}`, borderRadius:10,
      padding:'16px 20px', flex:1, minWidth:120
    }}>
      <div style={{ fontSize:11, color:COLORS.slate[400], fontWeight:600, letterSpacing:'0.06em', textTransform:'uppercase', marginBottom:6 }}>{label}</div>
      <div style={{ fontSize:24, fontWeight:800, color: color || COLORS.indigo[700], lineHeight:1 }}>{value}</div>
      {sub && <div style={{ fontSize:11, color:COLORS.slate[500], marginTop:4 }}>{sub}</div>}
    </div>
  );
}

// Process step
function ProcessStep({ step, title, detail, color }) {
  const [hov, setHov] = React.useState(false);
  return (
    <div onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)}
      style={{ display:'flex', gap:16, padding:'14px 16px', borderRadius:10, background: hov ? COLORS.slate[50] : 'transparent', transition:'background 0.12s' }}>
      <div style={{ width:32, height:32, borderRadius:999, background: color || COLORS.indigo[600], color:'white',
        display:'flex', alignItems:'center', justifyContent:'center', fontWeight:800, fontSize:13, flexShrink:0 }}>
        {step}
      </div>
      <div>
        <div style={{ fontWeight:700, fontSize:13, color:COLORS.slate[800], marginBottom:4 }}>{title}</div>
        <div style={{ fontSize:12.5, color:COLORS.slate[600], lineHeight:1.6 }}>{detail}</div>
      </div>
    </div>
  );
}

// Technique card (for the grid)
function TechniqueCard({ technique, onClick, isActive, compact }) {
  const [hov, setHov] = React.useState(false);
  const c = technique.color || COLORS.indigo[600];
  return (
    <div onClick={() => onClick(technique.id)}
      onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)}
      role="button" tabIndex={0}
      onKeyDown={e => e.key==='Enter' && onClick(technique.id)}
      style={{
        background:COLORS.slate[100], borderRadius:16,
        border: isActive ? `2px solid ${c}` : `1px solid ${COLORS.slate[200]}`,
        padding: compact ? '14px 16px' : '20px',
        cursor:'pointer', transition:'all 0.15s',
        boxShadow: hov ? '0 4px 20px rgba(0,0,0,0.08)' : isActive ? `0 0 0 4px ${c}20` : 'none',
        transform: hov && !isActive ? 'translateY(-1px)' : 'none',
        display:'flex', flexDirection:'column', gap:10
      }}>
      <div style={{ display:'flex', alignItems:'flex-start', gap:12 }}>
        <div style={{
          width:40, height:40, borderRadius:10, background:`${c}18`,
          display:'flex', alignItems:'center', justifyContent:'center',
          fontSize:20, flexShrink:0, color:c
        }}>{technique.icon}</div>
        <div style={{ flex:1, minWidth:0 }}>
          <div style={{ fontWeight:700, fontSize:14, color:COLORS.slate[800], marginBottom:2 }}>{technique.name}</div>
          <div style={{ fontSize:11, color: c, fontWeight:600, letterSpacing:'0.02em' }}>{technique.family}</div>
        </div>
      </div>
      {!compact && (
        <>
          <div style={{ fontSize:12, color:COLORS.slate[600], lineHeight:1.55 }}>{technique.tagline}</div>
          <div style={{ display:'flex', flexWrap:'wrap', gap:4 }}>
            {(technique.tags.businessProblems || []).slice(0,3).map((t,i) => (
              <Badge key={i} label={t} color={c} small />
            ))}
          </div>
        </>
      )}
    </div>
  );
}

// Data table
function DataTable({ headers, rows }) {
  return (
    <div style={{ overflowX:'auto', borderRadius:10, border:`1px solid ${COLORS.slate[200]}` }}>
      <table style={{ width:'100%', borderCollapse:'collapse', fontSize:12 }}>
        <thead>
          <tr style={{ background:COLORS.slate[50] }}>
            {headers.map((h,i) => (
              <th key={i} style={{ padding:'10px 14px', textAlign:'left', fontWeight:700,
                color:COLORS.slate[600], borderBottom:`1px solid ${COLORS.slate[200]}`,
                letterSpacing:'0.03em', fontSize:11 }}>{h}</th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.map((row,i) => (
            <tr key={i} style={{ background: i%2===0 ? 'white' : COLORS.slate[50] }}>
              {row.map((cell,j) => (
                <td key={j} style={{ padding:'9px 14px', color:COLORS.slate[700],
                  borderBottom: i < rows.length-1 ? `1px solid ${COLORS.slate[100]}` : 'none' }}>{cell}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

// Bar chart (horizontal)
function HorizBarChart({ items, valueKey, labelKey, colorKey, maxValue, unit='' }) {
  const max = maxValue || Math.max(...items.map(i => i[valueKey]));
  return (
    <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
      {items.map((item, i) => {
        const pct = (item[valueKey] / max) * 100;
        const color = item[colorKey] || COLORS.indigo[600];
        return (
          <div key={i}>
            <div style={{ display:'flex', justifyContent:'space-between', marginBottom:4 }}>
              <span style={{ fontSize:12, color:COLORS.slate[700], fontWeight:500 }}>{item[labelKey]}</span>
              <span style={{ fontSize:12, color:COLORS.slate[500], fontWeight:700 }}>{item[valueKey]}{unit}</span>
            </div>
            <div style={{ height:8, background:COLORS.slate[100], borderRadius:999, overflow:'hidden' }}>
              <div style={{ height:'100%', width:`${pct}%`, background:color,
                borderRadius:999, transition:'width 0.4s ease' }} />
            </div>
          </div>
        );
      })}
    </div>
  );
}

// Empty state
function EmptyState({ title, subtitle, action }) {
  return (
    <div style={{ display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center',
      padding:'60px 24px', textAlign:'center' }}>
      <div style={{ fontSize:48, marginBottom:16, opacity:0.3 }}>🔍</div>
      <div style={{ fontSize:16, fontWeight:700, color:COLORS.slate[700], marginBottom:8 }}>{title}</div>
      {subtitle && <div style={{ fontSize:13, color:COLORS.slate[500], maxWidth:320 }}>{subtitle}</div>}
      {action}
    </div>
  );
}

// Modal
function Modal({ open, onClose, title, children, width=680 }) {
  const isMobile = useIsMobile();
  if (!open) return null;
  return (
    <div onClick={onClose} style={{
      position:'fixed', inset:0, background:'rgba(15,23,42,0.5)', zIndex:1000,
      display:'flex', alignItems:'center', justifyContent:'center',
      padding: isMobile ? 0 : 20
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        background:COLORS.slate[100],
        borderRadius: isMobile ? 0 : 16,
        width:'100%', maxWidth: isMobile ? 'none' : width,
        height: isMobile ? '100%' : 'auto',
        maxHeight: isMobile ? 'none' : '90vh',
        overflow:'hidden', display:'flex', flexDirection:'column',
        boxShadow: isMobile ? 'none' : '0 25px 60px rgba(0,0,0,0.25)',
        paddingTop: isMobile ? 'env(safe-area-inset-top)' : 0,
        paddingBottom: isMobile ? 'env(safe-area-inset-bottom)' : 0
      }}>
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between',
          padding: isMobile ? '14px 16px' : '20px 24px',
          borderBottom:`1px solid ${COLORS.slate[200]}`, flexShrink:0 }}>
          <span style={{ fontWeight:700, fontSize: isMobile ? 15 : 16, color:COLORS.slate[800] }}>{title}</span>
          <button onClick={onClose} aria-label="Close" style={{ border:'none', background:'none', cursor:'pointer',
            fontSize:20, color:COLORS.slate[400],
            width: isMobile ? 40 : 32, height: isMobile ? 40 : 32, borderRadius:8,
            display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0 }}>×</button>
        </div>
        <div style={{ overflow:'auto', padding: isMobile ? '16px' : '24px', flex:1,
          WebkitOverflowScrolling:'touch' }}>{children}</div>
      </div>
    </div>
  );
}

// Tooltip wrapper
function Tooltip({ text, children }) {
  const [show, setShow] = React.useState(false);
  return (
    <span style={{ position:'relative', display:'inline-flex' }}
      onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)}>
      {children}
      {show && (
        <span style={{
          position:'absolute', bottom:'calc(100% + 6px)', left:'50%', transform:'translateX(-50%)',
          background:TOKENS.tooltip, color:COLORS.slate[800], border:`1px solid ${COLORS.slate[300]}`,
          fontSize:11, padding:'4px 8px',
          borderRadius:6, whiteSpace:'nowrap', pointerEvents:'none', zIndex:999
        }}>{text}</span>
      )}
    </span>
  );
}

Object.assign(window, {
  COLORS, FAMILY_COLORS, Badge, IconBtn, SectionLabel, Divider, ChipRow, CopyBtn,
  TabBar, Collapse, StatCard, ProcessStep, TechniqueCard, DataTable, HorizBarChart,
  EmptyState, Modal, Tooltip, useIsMobile, SiteNav,
  TOKENS, useTheme, ThemeToggle, applyTheme
});
