/* gigi-ui.jsx — shared primitives + web layout components (v2). */

const GIGI_THEME_CSS = `
@import url('https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600;700;800&family=Space+Grotesk:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap');

:root{
  --sand:#EFECE4; --sand-2:#E7E3D8;
  --surface:#FFFFFF; --surface-2:#F5F3EE; --surface-3:#EDEAE2;
  --ink:#16180F; --ink-2:#55584A; --ink-3:#8C8E80;
  --line:#E7E3D8; --line-2:#EDEBE3;
  --accent:#C8DA3F; --accent-press:#B7C92E; --accent-ink:#1B1E12;
  --pill:#16180F; --pill-ink:#EAF09C;
  --go:#3C7D54; --go-bg:#E4EFE3;
  --info:#3A5C8C; --info-bg:#E3E9F2;
  --warn:#A6552A; --warn-bg:#F5EBE0;
  --neutral:#6B6E5E; --neutral-bg:#ECEAE0;
  --sidebar-w:240px; --topbar-h:60px;
  --r-lg:12px; --r-md:10px; --r-sm:8px;
  --pad:20px; --gap:14px;
  --shadow:0 1px 2px rgba(20,22,12,.04),0 4px 16px rgba(20,22,12,.06);
  --shadow-md:0 2px 8px rgba(20,22,12,.07),0 12px 32px rgba(20,22,12,.1);
  --ff:"Hanken Grotesk",system-ui,sans-serif;
  --fd:"Space Grotesk",system-ui,sans-serif;
  --fm:"Space Mono",ui-monospace,monospace;
}
.gigi-dark{
  --sand:#0F110B; --sand-2:#151710;
  --surface:#1B1E14; --surface-2:#23271A; --surface-3:#2A2E20;
  --ink:#F2F1E4; --ink-2:#B0B2A0; --ink-3:#7C7E6E;
  --line:#2C3022; --line-2:#262A1C;
  --go:#8FC79F; --go-bg:#1E2E1F;
  --info:#9FBDE6; --info-bg:#1A2438;
  --warn:#E0A877; --warn-bg:#2E2016;
  --neutral-bg:#262A1C;
  --shadow:0 1px 2px rgba(0,0,0,.3),0 4px 16px rgba(0,0,0,.4);
  --shadow-md:0 2px 8px rgba(0,0,0,.4),0 12px 32px rgba(0,0,0,.55);
}
.gigi-soft{ --r-lg:18px; --r-md:14px; --r-sm:10px; }
.gigi-compact{ --pad:14px; --gap:10px; }
.g-wiz-wrap{ width:100%; max-width:720px; margin:0 auto; }
.g-root *{ box-sizing:border-box; }
.g-root{ font-family:var(--ff); color:var(--ink); -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility; }
.g-num{ font-family:var(--fd); font-feature-settings:"tnum"; letter-spacing:-.01em; }
.g-mono{ font-family:var(--fm); }
.g-scroll::-webkit-scrollbar{ width:0; height:0; }
.g-tap{ cursor:pointer; -webkit-tap-highlight-color:transparent; transition:transform .1s,opacity .15s; user-select:none; }
.g-tap:active{ transform:scale(.98); opacity:.85; }
@keyframes gUp{  from{opacity:0;transform:translateY(8px);}  to{opacity:1;transform:none;} }
@keyframes gIn{  from{opacity:0;}                             to{opacity:1;} }
@keyframes gSheet{ from{transform:translateY(100%);} to{transform:translateY(0);} }
@keyframes gSlide{ from{opacity:0;transform:translateX(-8px);} to{opacity:1;transform:none;} }
@keyframes gPulse{ 0%,100%{opacity:1;} 50%{opacity:.35;} }
.g-enter{ animation:gUp .35s cubic-bezier(.2,.8,.2,1) both; }
.g-fade{ animation:gIn .25s ease both; }

/* ── Web layout ── */
.g-shell{
  display:flex; height:100dvh; overflow:hidden; background:var(--sand);
}
.g-sidebar{
  width:var(--sidebar-w); flex-shrink:0; height:100%;
  background:var(--surface); border-right:1px solid var(--line);
  display:flex; flex-direction:column; overflow:hidden;
  transition:width .22s cubic-bezier(.4,0,.2,1);
}
.g-sidebar.narrow{ width:68px; }
.g-main{
  flex:1; min-width:0; height:100%; display:flex; flex-direction:column; overflow:hidden;
}
.g-topbar{
  height:var(--topbar-h); flex-shrink:0; border-bottom:1px solid var(--line);
  display:flex; align-items:center; padding:0 24px; gap:14px;
  background:var(--surface);
}
.g-page{
  flex:1; overflow-y:auto; overflow-x:hidden;
  padding:32px 36px; background:var(--sand);
}
.g-page::-webkit-scrollbar{ width:6px; }
.g-page::-webkit-scrollbar-track{ background:transparent; }
.g-page::-webkit-scrollbar-thumb{ background:rgba(0,0,0,.12); border-radius:3px; }
.g-mobile-nav{
  display:none; flex-shrink:0; border-top:1px solid var(--line);
  background:var(--surface); padding:4px 0 max(env(safe-area-inset-bottom),8px);
}
@media(max-width:768px){
  .g-sidebar{ display:none!important; }
  .g-mobile-nav{ display:flex!important; }
  .g-topbar{ padding:0 16px; }
  .g-page{ padding:20px 16px 100px; }
}
@media(max-width:1024px) and (min-width:769px){
  .g-sidebar:not(.narrow){ width:var(--sidebar-w); }
  .g-sidebar.narrow{ width:68px; }
}
.g-card-grid{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(300px,1fr));
  gap:var(--gap);
}
@media(max-width:768px){ .g-card-grid{ grid-template-columns:1fr; } }
.g-2col{ display:grid; grid-template-columns:1fr 1fr; gap:var(--gap); }
@media(max-width:640px){ .g-2col{ grid-template-columns:1fr; } }
.g-3col{ display:grid; grid-template-columns:repeat(3,1fr); gap:var(--gap); }
@media(max-width:900px){ .g-3col{ grid-template-columns:repeat(2,1fr); } }
@media(max-width:600px){ .g-3col{ grid-template-columns:1fr; } }
@media(max-width:720px){ .g-fin-grid{ grid-template-columns:1fr!important; } }
`;

(function injectTheme() {
    if (typeof document === 'undefined' || document.getElementById('gigi-theme-v2')) return;
    const s = document.createElement('style');
    s.id = 'gigi-theme-v2';
    s.textContent = GIGI_THEME_CSS;
    document.head.appendChild(s);
})();

// ── Icons ─────────────────────────────────────────────────────────
function Icon({name, size = 20, stroke = 1.8, style, className}) {
    const p = {
        fill: 'none',
        stroke: 'currentColor',
        strokeWidth: stroke,
        strokeLinecap: 'round',
        strokeLinejoin: 'round'
    };
    const paths = {
        vault: <>
            <rect x="3" y="4" width="18" height="16" rx="2" {...p}/>
            <circle cx="12" cy="12" r="3" {...p}/>
            <path d="M12 6v1.5M12 16.5V18M6 12h1.5M16.5 12H18" {...p}/>
        </>,
        home: <>
            <path d="M4 11l8-6 8 6v8a1 1 0 0 1-1 1h-4v-5h-6v5H5a1 1 0 0 1-1-1z" {...p}/>
        </>,
        building: <>
            <rect x="5" y="3" width="14" height="18" rx="1.5" {...p}/>
            <path d="M9 7h2M13 7h2M9 11h2M13 11h2M9 15h2M13 15h2" {...p}/>
        </>,
        office: <>
            <rect x="2" y="7" width="20" height="14" rx="2" {...p}/>
            <path d="M8 7V5a2 2 0 0 1 4 0v2M12 7v-2a2 2 0 0 1 4 0v2" {...p}/>
            <path d="M9 12h6M9 16h4" {...p}/>
        </>,
        box: <>
            <path
                d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" {...p}/>
            <path d="M3.27 6.96L12 12.01l8.73-5.05M12 22.08V12" {...p}/>
        </>,
        users: <>
            <circle cx="9" cy="8" r="3" {...p}/>
            <path d="M3.5 19a5.5 5.5 0 0 1 11 0" {...p}/>
            <path d="M16 6a3 3 0 0 1 0 6M21 19a5.5 5.5 0 0 0-4-5.3" {...p}/>
        </>,
        sync: <>
            <path d="M4 12a8 8 0 0 1 13.7-5.6L20 9" {...p}/>
            <path d="M20 4v5h-5" {...p}/>
            <path d="M20 12a8 8 0 0 1-13.7 5.6L4 15" {...p}/>
            <path d="M4 20v-5h5" {...p}/>
        </>,
        settings: <>
            <circle cx="12" cy="12" r="3" {...p}/>
            <path
                d="M12 2v2M12 20v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M2 12h2M20 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" {...p}/>
        </>,
        chevR: <>
            <path d="M9 5l7 7-7 7" {...p}/>
        </>,
        chevL: <>
            <path d="M15 5l-7 7 7 7" {...p}/>
        </>,
        chevD: <>
            <path d="M5 9l7 7 7-7" {...p}/>
        </>,
        chevU: <>
            <path d="M19 15l-7-7-7 7" {...p}/>
        </>,
        plus: <>
            <path d="M12 5v14M5 12h14" {...p}/>
        </>,
        check: <>
            <path d="M5 12.5l4.5 4.5L19 6.5" {...p}/>
        </>,
        bolt: <>
            <path d="M13 2 4 14h7l-1 8 9-12h-7z" {...p}/>
        </>,
        shield: <>
            <path d="M12 3l7 3v5c0 4.5-3 8-7 10-4-2-7-5.5-7-10V6z" {...p}/>
        </>,
        doc: <>
            <path d="M7 3h7l4 4v14a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z" {...p}/>
            <path d="M13 3v5h5" {...p}/>
        </>,
        upload: <>
            <path d="M12 16V5M8 9l4-4 4 4" {...p}/>
            <path d="M5 19h14" {...p}/>
        </>,
        mpesa: <>
            <circle cx="12" cy="12" r="9" {...p}/>
            <path d="M8 14V9l4 3 4-3v5" {...p}/>
        </>,
        bank: <>
            <path d="M4 10h16M5 10l7-5 7 5M6 10v7M10 10v7M14 10v7M18 10v7M4 20h16" {...p}/>
        </>,
        lock: <>
            <rect x="5" y="11" width="14" height="9" rx="2" {...p}/>
            <path d="M8 11V8a4 4 0 0 1 8 0v3" {...p}/>
        </>,
        search: <>
            <circle cx="11" cy="11" r="7" {...p}/>
            <path d="M20 20l-4-4" {...p}/>
        </>,
        clock: <>
            <circle cx="12" cy="12" r="9" {...p}/>
            <path d="M12 7v5l3 2" {...p}/>
        </>,
        arrowUp: <>
            <path d="M12 19V5M6 11l6-6 6 6" {...p}/>
        </>,
        x: <>
            <path d="M6 6l12 12M18 6 6 18" {...p}/>
        </>,
        ardhi: <>
            <path d="M12 21s-7-5.5-7-11a7 7 0 0 1 14 0c0 5.5-7 11-7 11z" {...p}/>
            <circle cx="12" cy="10" r="2.5" {...p}/>
        </>,
        info: <>
            <circle cx="12" cy="12" r="9" {...p}/>
            <path d="M12 11v5M12 8h.01" {...p}/>
        </>,
        bell: <>
            <path d="M6 9a6 6 0 0 1 12 0c0 5 2 6 2 6H4s2-1 2-6z" {...p}/>
            <path d="M10 19a2 2 0 0 0 4 0" {...p}/>
        </>,
        menu: <>
            <path d="M4 6h16M4 12h16M4 18h16" {...p}/>
        </>,
        user: <>
            <circle cx="12" cy="8" r="4" {...p}/>
            <path d="M4 20a8 8 0 0 1 16 0" {...p}/>
        </>,
        switch: <>
            <path d="M7 8h11l-3-3M17 16H6l3 3" {...p}/>
        </>,
        eye: <>
            <path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z" {...p}/>
            <circle cx="12" cy="12" r="3" {...p}/>
        </>,
        id: <>
            <rect x="2" y="6" width="20" height="12" rx="2" {...p}/>
            <circle cx="8" cy="12" r="2" {...p}/>
            <path d="M14 10h4M14 14h2" {...p}/>
        </>,
        percent: <>
            <path d="M19 5 5 19" {...p}/>
            <circle cx="6.5" cy="6.5" r="2.5" {...p}/>
            <circle cx="17.5" cy="17.5" r="2.5" {...p}/>
        </>,
    };
    return (
        <svg width={size} height={size} viewBox="0 0 24 24" style={style} className={className}>
            {paths[name] || <circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" strokeWidth={stroke}/>}
        </svg>
    );
}

// ── Tone colors ────────────────────────────────────────────────
const toneColor = (tone) => ({
    go: {c: 'var(--go)', b: 'var(--go-bg)'},
    info: {c: 'var(--info)', b: 'var(--info-bg)'},
    warn: {c: 'var(--warn)', b: 'var(--warn-bg)'},
    neutral: {c: 'var(--neutral)', b: 'var(--neutral-bg)'},
    accent: {c: 'var(--accent-ink)', b: 'var(--accent)'},
}[tone] || {c: 'var(--ink-2)', b: 'var(--surface-2)'});

// ── Badge ──────────────────────────────────────────────────────
function Badge({tone = 'neutral', children, dot = false, style}) {
    const t = toneColor(tone);
    return (
        <span style={{
            display: 'inline-flex', alignItems: 'center', gap: 5, background: t.b, color: t.c,
            fontSize: 11.5, fontWeight: 600, padding: '3px 9px', borderRadius: 999, letterSpacing: '.01em',
            whiteSpace: 'nowrap', ...style
        }}>
      {dot && <span style={{width: 6, height: 6, borderRadius: 999, background: t.c}}/>}
            {children}
    </span>
    );
}

// ── Card ───────────────────────────────────────────────────────
function Card({children, pad = true, onClick, style, className = ''}) {
    return (
        <div onClick={onClick} className={`${onClick ? 'g-tap ' : ''}${className}`}
             style={{
                 background: 'var(--surface)', borderRadius: 'var(--r-lg)', boxShadow: 'var(--shadow)',
                 padding: pad ? 'var(--pad)' : 0, ...style
             }}>
            {children}
        </div>
    );
}

// ── Buttons ────────────────────────────────────────────────────
function Btn({children, kind = 'primary', icon, full = false, onClick, disabled, small = false, style}) {
    const base = {
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        width: full ? '100%' : 'auto', height: small ? 36 : 46, borderRadius: 999, border: 'none',
        fontFamily: 'var(--ff)', fontSize: small ? 13 : 15, fontWeight: 700, letterSpacing: '.01em',
        cursor: disabled ? 'not-allowed' : 'pointer', opacity: disabled ? 0.45 : 1,
        padding: full ? '0 20px' : (small ? '0 16px' : '0 24px'),
        transition: 'transform .1s, background .15s', WebkitTapHighlightColor: 'transparent',
        whiteSpace: 'nowrap',
    };
    const kinds = {
        primary: {background: 'var(--pill)', color: 'var(--pill-ink)'},
        accent: {background: 'var(--accent)', color: 'var(--accent-ink)'},
        ghost: {background: 'var(--surface-2)', color: 'var(--ink)'},
        line: {background: 'transparent', color: 'var(--ink)', border: '1.5px solid var(--line)'},
        danger: {background: 'var(--warn-bg)', color: 'var(--warn)'},
    };
    return (
        <button className="g-tap" onClick={disabled ? undefined : onClick}
                style={{...base, ...kinds[kind], ...style}}>
            {icon && <Icon name={icon} size={small ? 15 : 17}/>}{children}
        </button>
    );
}

// ── Input ──────────────────────────────────────────────────────
function Input({label, placeholder, value, onChange, hint, prefix, suffix, mono = false, type = 'text', required}) {
    return (
        <label style={{display: 'block'}}>
            {label && (
                <div style={{fontSize: 12.5, fontWeight: 600, color: 'var(--ink-2)', marginBottom: 6}}>
                    {label}{required && <span style={{color: 'var(--warn)', marginLeft: 3}}>*</span>}
                </div>
            )}
            <div style={{
                display: 'flex', alignItems: 'center', gap: 8, background: 'var(--surface)',
                border: '1.5px solid var(--line)', borderRadius: 'var(--r-sm)', padding: '0 14px', height: 46,
                transition: 'border-color .15s'
            }}>
                {prefix && <span style={{color: 'var(--ink-3)', fontSize: 13, whiteSpace: 'nowrap'}}>{prefix}</span>}
                <input value={value || ''} placeholder={placeholder} type={type}
                       onChange={(e) => onChange && onChange(e.target.value)}
                       className={mono ? 'g-mono' : ''}
                       style={{
                           flex: 1,
                           border: 'none',
                           background: 'transparent',
                           outline: 'none',
                           fontFamily: mono ? 'var(--fm)' : 'var(--ff)',
                           fontSize: 14.5,
                           color: 'var(--ink)',
                           minWidth: 0
                       }}/>
                {suffix && <span style={{color: 'var(--ink-3)', fontSize: 13, whiteSpace: 'nowrap'}}>{suffix}</span>}
            </div>
            {hint && <div style={{fontSize: 11.5, color: 'var(--ink-3)', marginTop: 5, lineHeight: 1.5}}>{hint}</div>}
        </label>
    );
}

// ── Select ─────────────────────────────────────────────────────
function Select({label, value, onChange, options, hint}) {
    return (
        <label style={{display: 'block'}}>
            {label &&
                <div style={{fontSize: 12.5, fontWeight: 600, color: 'var(--ink-2)', marginBottom: 6}}>{label}</div>}
            <div style={{position: 'relative'}}>
                <select value={value} onChange={(e) => onChange(e.target.value)}
                        style={{
                            width: '100%', height: 46, borderRadius: 'var(--r-sm)', border: '1.5px solid var(--line)',
                            background: 'var(--surface)', color: 'var(--ink)', fontFamily: 'var(--ff)', fontSize: 14.5,
                            padding: '0 38px 0 14px', appearance: 'none', outline: 'none', cursor: 'pointer'
                        }}>
                    {options.map(o => <option key={o.value || o} value={o.value || o}>{o.label || o}</option>)}
                </select>
                <div style={{
                    position: 'absolute',
                    right: 12,
                    top: '50%',
                    transform: 'translateY(-50%)',
                    pointerEvents: 'none',
                    color: 'var(--ink-3)'
                }}>
                    <Icon name="chevD" size={16}/>
                </div>
            </div>
            {hint && <div style={{fontSize: 11.5, color: 'var(--ink-3)', marginTop: 5}}>{hint}</div>}
        </label>
    );
}

// ── FileUpload ─────────────────────────────────────────────────
function FileUpload({label, hint, accept = '*', uploaded = false, filename, onUpload}) {
    const [dragging, setDragging] = React.useState(false);
    return (
        <div>
            {label &&
                <div style={{fontSize: 12.5, fontWeight: 600, color: 'var(--ink-2)', marginBottom: 8}}>{label}</div>}
            <div
                onDragOver={(e) => {
                    e.preventDefault();
                    setDragging(true);
                }}
                onDragLeave={() => setDragging(false)}
                onDrop={(e) => {
                    e.preventDefault();
                    setDragging(false);
                    onUpload && onUpload(e.dataTransfer.files[0]);
                }}
                onClick={() => onUpload && onUpload({name: filename || 'document.pdf', size: 1024})}
                className="g-tap"
                style={{
                    border: `1.5px dashed ${uploaded ? 'var(--go)' : dragging ? 'var(--accent-ink)' : 'var(--line)'}`,
                    borderRadius: 'var(--r-md)', padding: '18px 16px', textAlign: 'center',
                    background: uploaded ? 'var(--go-bg)' : dragging ? 'var(--surface-2)' : 'var(--surface)',
                    transition: 'border-color .15s, background .15s',
                }}>
                {uploaded ? (
                    <div style={{
                        display: 'flex',
                        alignItems: 'center',
                        justifyContent: 'center',
                        gap: 9,
                        color: 'var(--go)'
                    }}>
                        <Icon name="check" size={18} stroke={2.5}/>
                        <span style={{fontSize: 13.5, fontWeight: 600}}>{filename || 'Uploaded'}</span>
                    </div>
                ) : (
                    <>
                        <div style={{color: 'var(--ink-3)'}}><Icon name="upload" size={22}/></div>
                        <div style={{fontSize: 13.5, fontWeight: 600, marginTop: 8, color: 'var(--ink)'}}>Click or drag
                            to upload
                        </div>
                        {hint && <div style={{fontSize: 12, color: 'var(--ink-3)', marginTop: 4}}>{hint}</div>}
                    </>
                )}
            </div>
        </div>
    );
}

// ── Metric ─────────────────────────────────────────────────────
function Metric({label, value, sub, icon, tone}) {
    const t = tone ? toneColor(tone) : null;
    return (
        <div style={{display: 'flex', flexDirection: 'column', gap: 6}}>
            <div style={{
                display: 'flex',
                alignItems: 'center',
                gap: 6,
                color: 'var(--ink-3)',
                fontSize: 12.5,
                fontWeight: 600,
                letterSpacing: '.02em',
                textTransform: 'uppercase'
            }}>
                {icon && <Icon name={icon} size={14}/>}{label}
            </div>
            <div className="g-num" style={{
                fontSize: 28,
                fontWeight: 700,
                color: t ? t.c : 'var(--ink)',
                letterSpacing: '-.01em'
            }}>{value}</div>
            {sub && <div style={{fontSize: 12.5, color: 'var(--ink-3)'}}>{sub}</div>}
        </div>
    );
}

// ── KPI card ───────────────────────────────────────────────────
function KpiCard({label, value, sub, icon, dark = false}) {
    return (
        <Card style={{background: dark ? 'var(--accent-ink)' : 'var(--surface)'}}>
            <div style={{display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: 14}}>
                <div style={{
                    width: 38, height: 38, borderRadius: 10, display: 'grid', placeItems: 'center',
                    background: dark ? 'rgba(255,255,255,.1)' : 'var(--surface-2)',
                    color: dark ? 'var(--accent)' : 'var(--ink-2)'
                }}>
                    <Icon name={icon} size={20}/>
                </div>
            </div>
            <div className="g-num" style={{
                fontSize: 30,
                fontWeight: 700,
                color: dark ? '#fff' : 'var(--ink)',
                letterSpacing: '-.02em'
            }}>{value}</div>
            <div style={{
                fontSize: 13,
                color: dark ? 'rgba(255,255,255,.6)' : 'var(--ink-2)',
                marginTop: 4
            }}>{label}</div>
            {sub && <div style={{
                fontSize: 12,
                color: dark ? 'rgba(255,255,255,.45)' : 'var(--ink-3)',
                marginTop: 2
            }}>{sub}</div>}
        </Card>
    );
}

// ── Ring (occupancy) ───────────────────────────────────────────
function Ring({pct, size = 48, threshold = 85}) {
    const r = (size - 6) / 2, c = 2 * Math.PI * r;
    const good = pct >= threshold;
    return (
        <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
            <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke="var(--line)" strokeWidth="5"/>
            <circle cx={size / 2} cy={size / 2} r={r} fill="none"
                    stroke={good ? 'var(--go)' : 'var(--warn)'} strokeWidth="5" strokeLinecap="round"
                    strokeDasharray={`${c * pct / 100} ${c}`} transform={`rotate(-90 ${size / 2} ${size / 2})`}/>
            <text x="50%" y="52%" dominantBaseline="middle" textAnchor="middle"
                  fontFamily="var(--fd)" fontSize={size < 44 ? '11' : '13'} fontWeight="700" fill="var(--ink)">{pct}%
            </text>
        </svg>
    );
}

// ── PerfBars ───────────────────────────────────────────────────
function PerfBars({data, height = 32}) {
    if (!data || !data.length) return <div style={{fontSize: 12, color: 'var(--ink-3)'}}>No history yet</div>;
    const col = v => v === 1 ? 'var(--go)' : v === 0.5 ? 'var(--warn)' : 'var(--warn-bg)';
    return (
        <div style={{display: 'flex', alignItems: 'flex-end', gap: 3, height}}>
            {data.map((v, i) => (
                <div key={i} style={{
                    flex: 1, height: v === 0 ? '30%' : '100%', minHeight: 3,
                    background: col(v), borderRadius: 3, opacity: v === 0 ? 0.6 : 1
                }}/>
            ))}
        </div>
    );
}

// ── SegTabs ────────────────────────────────────────────────────
function SegTabs({tabs, active, onChange, scroll = false}) {
    return (
        <div className={scroll ? 'g-scroll' : ''} style={{
            display: 'flex', gap: 4, padding: 3,
            background: 'var(--surface-2)', borderRadius: 999, overflowX: scroll ? 'auto' : 'visible',
            flexShrink: 0
        }}>
            {tabs.map(t => {
                const on = t.id === active;
                return (
                    <button key={t.id} onClick={() => onChange(t.id)} className="g-tap" style={{
                        flex: scroll ? '0 0 auto' : 1, border: 'none', borderRadius: 999, padding: '8px 16px',
                        background: on ? 'var(--surface)' : 'transparent', color: on ? 'var(--ink)' : 'var(--ink-2)',
                        boxShadow: on ? 'var(--shadow)' : 'none', fontFamily: 'var(--ff)', fontSize: 13.5,
                        fontWeight: 600, whiteSpace: 'nowrap', cursor: 'pointer'
                    }}>{t.label}</button>
                );
            })}
        </div>
    );
}

// ── Field (read-only row) ──────────────────────────────────────
function Field({label, value, mono = false, last = false}) {
    return (
        <div style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
            padding: '11px 0', borderBottom: last ? 'none' : '1px solid var(--line-2)', gap: 16
        }}>
            <span style={{color: 'var(--ink-2)', fontSize: 13.5, flexShrink: 0}}>{label}</span>
            <span className={mono ? 'g-mono' : ''}
                  style={{color: 'var(--ink)', fontSize: 14, fontWeight: 600, textAlign: 'right'}}>{value}</span>
        </div>
    );
}

// ── Section label ──────────────────────────────────────────────
function SectionLabel({children, action}) {
    return (
        <div style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between', margin: '0 2px 10px'}}>
            <span style={{
                fontSize: 11.5,
                fontWeight: 700,
                letterSpacing: '.07em',
                textTransform: 'uppercase',
                color: 'var(--ink-3)'
            }}>{children}</span>
            {action}
        </div>
    );
}

// ── Sheet (mobile / web modal) ─────────────────────────────────
function Sheet({open, onClose, title, children, width = 480}) {
    if (!open) return null;
    const isMobile = typeof window !== 'undefined' && window.innerWidth < 640;
    if (isMobile) {
        return (
            <div onClick={onClose} style={{
                position: 'fixed', inset: 0, zIndex: 60,
                background: 'rgba(20,22,12,.45)', backdropFilter: 'blur(3px)',
                display: 'flex', alignItems: 'flex-end', animation: 'gIn .2s both'
            }}>
                <div onClick={e => e.stopPropagation()} style={{
                    width: '100%', background: 'var(--surface)',
                    borderRadius: '22px 22px 0 0', padding: '10px 20px calc(24px + env(safe-area-inset-bottom))',
                    maxHeight: '85dvh', overflowY: 'auto', animation: 'gSheet .3s cubic-bezier(.2,.8,.2,1) both'
                }} className="g-scroll">
                    <div style={{
                        width: 36,
                        height: 4,
                        borderRadius: 999,
                        background: 'var(--line)',
                        margin: '4px auto 14px'
                    }}/>
                    {title && <SheetHeader title={title} onClose={onClose}/>}
                    {children}
                </div>
            </div>
        );
    }
    return (
        <div onClick={onClose} style={{
            position: 'fixed', inset: 0, zIndex: 60,
            background: 'rgba(20,22,12,.35)', backdropFilter: 'blur(3px)',
            display: 'flex', alignItems: 'center', justifyContent: 'center', animation: 'gIn .2s both'
        }}>
            <div onClick={e => e.stopPropagation()} style={{
                width: '100%', maxWidth: width,
                background: 'var(--surface)', borderRadius: 24, padding: '24px 28px',
                maxHeight: '88vh', overflowY: 'auto', boxShadow: 'var(--shadow-md)', animation: 'gUp .28s both'
            }} className="g-scroll">
                {title && <SheetHeader title={title} onClose={onClose}/>}
                {children}
            </div>
        </div>
    );
}

function SheetHeader({title, onClose}) {
    return (
        <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20}}>
            <h3 className="g-num" style={{margin: 0, fontSize: 20, fontWeight: 700}}>{title}</h3>
            <button onClick={onClose} className="g-tap" style={{
                border: 'none',
                background: 'var(--surface-2)',
                width: 32,
                height: 32,
                borderRadius: 999,
                display: 'grid',
                placeItems: 'center',
                color: 'var(--ink-2)',
                cursor: 'pointer'
            }}>
                <Icon name="x" size={16}/>
            </button>
        </div>
    );
}

// ── Toggle ─────────────────────────────────────────────────────
function Toggle({on, onClick}) {
    return (
        <div onClick={onClick} className="g-tap" style={{
            width: 44, height: 26, borderRadius: 999,
            background: on ? 'var(--accent-ink)' : 'var(--line)', padding: 3,
            transition: 'background .2s', flexShrink: 0
        }}>
            <div style={{
                width: 20, height: 20, borderRadius: 999, background: '#fff',
                transform: on ? 'translateX(18px)' : 'none', transition: 'transform .2s',
                boxShadow: '0 1px 3px rgba(0,0,0,.2)'
            }}/>
        </div>
    );
}

// ── Wizard shell ───────────────────────────────────────────────
function WizardShell({
                         steps,
                         currentStep,
                         title,
                         subtitle,
                         children,
                         onBack,
                         onNext,
                         nextLabel,
                         nextDisabled,
                         nextKind = 'primary',
                         isLast = false
                     }) {
    return (
        <div className="g-root" style={{
            display: 'flex',
            flexDirection: 'column',
            height: '100%',
            background: 'var(--sand)',
            overflow: 'hidden'
        }}>
            {/* Progress bar */}
            <div style={{flexShrink: 0, padding: '0 0 0'}}>
                <div style={{display: 'flex', height: 3}}>
                    {steps.map((s, i) => (
                        <div key={i} style={{
                            flex: 1,
                            background: i < currentStep ? 'var(--accent)' : i === currentStep ? 'var(--accent)' : 'var(--line)',
                            opacity: i === currentStep ? 1 : i < currentStep ? 0.6 : 0.25,
                            transition: 'background .3s'
                        }}/>
                    ))}
                </div>
            </div>
            {/* Header */}
            <div style={{flexShrink: 0, padding: '20px 28px 0', display: 'flex', alignItems: 'center', gap: 14}}>
                {onBack && (
                    <button onClick={onBack} className="g-tap" style={{
                        width: 36,
                        height: 36,
                        borderRadius: 999,
                        border: 'none',
                        background: 'var(--surface)',
                        display: 'grid',
                        placeItems: 'center',
                        color: 'var(--ink-2)',
                        cursor: 'pointer',
                        boxShadow: 'var(--shadow)',
                        flexShrink: 0
                    }}>
                        <Icon name="chevL" size={18}/>
                    </button>
                )}
                <div>
                    <div style={{
                        fontSize: 11.5,
                        fontWeight: 700,
                        color: 'var(--ink-3)',
                        textTransform: 'uppercase',
                        letterSpacing: '.07em',
                        marginBottom: 4
                    }}>
                        Step {currentStep + 1} of {steps.length} · {steps[currentStep]}
                    </div>
                    <h1 className="g-num"
                        style={{fontSize: 26, fontWeight: 700, margin: 0, lineHeight: 1.1}}>{title}</h1>
                </div>
            </div>
            {/* Subtitle */}
            {subtitle && <p style={{
                flexShrink: 0,
                margin: '10px 28px 0',
                fontSize: 14,
                color: 'var(--ink-2)',
                lineHeight: 1.55
            }}>{subtitle}</p>}
            {/* Body */}
            <div className="g-scroll" style={{flex: 1, overflowY: 'auto', padding: '20px 28px 16px'}}>
                {children}
            </div>
            {/* Footer */}
            <div style={{
                flexShrink: 0, padding: '12px 28px calc(16px + env(safe-area-inset-bottom,0px))',
                background: 'linear-gradient(transparent, var(--sand) 30%)', display: 'flex', gap: 10
            }}>
                <Btn kind={nextKind} onClick={onNext} disabled={nextDisabled} full icon={isLast ? 'check' : undefined}>
                    {nextLabel || (isLast ? 'Submit' : 'Continue')}
                </Btn>
            </div>
        </div>
    );
}

// ── Page header (web) ──────────────────────────────────────────
function PageHeader({title, subtitle, actions, back, onBack}) {
    return (
        <div style={{
            display: 'flex',
            alignItems: 'flex-start',
            justifyContent: 'space-between',
            gap: 16,
            marginBottom: 28,
            flexWrap: 'wrap'
        }}>
            <div style={{display: 'flex', alignItems: 'center', gap: 14}}>
                {back && (
                    <button onClick={onBack} className="g-tap" style={{
                        width: 38,
                        height: 38,
                        borderRadius: 12,
                        border: 'none',
                        background: 'var(--surface)',
                        display: 'grid',
                        placeItems: 'center',
                        color: 'var(--ink-2)',
                        cursor: 'pointer',
                        boxShadow: 'var(--shadow)',
                        flexShrink: 0
                    }}>
                        <Icon name="chevL" size={18}/>
                    </button>
                )}
                <div>
                    {subtitle && <div style={{
                        fontSize: 12.5,
                        color: 'var(--ink-3)',
                        fontWeight: 600,
                        marginBottom: 3
                    }}>{subtitle}</div>}
                    <h1 className="g-num"
                        style={{margin: 0, fontSize: 28, fontWeight: 700, lineHeight: 1.1}}>{title}</h1>
                </div>
            </div>
            {actions && <div style={{
                display: 'flex',
                gap: 10,
                alignItems: 'center',
                flexShrink: 0,
                flexWrap: 'wrap'
            }}>{actions}</div>}
        </div>
    );
}

// ── Empty state ────────────────────────────────────────────────
function Empty({icon, title, body, action}) {
    return (
        <div style={{textAlign: 'center', padding: '48px 24px', color: 'var(--ink-3)'}}>
            <div style={{
                width: 56,
                height: 56,
                borderRadius: 16,
                background: 'var(--surface-2)',
                display: 'grid',
                placeItems: 'center',
                margin: '0 auto',
                color: 'var(--ink-3)'
            }}>
                <Icon name={icon || 'info'} size={26}/>
            </div>
            <div style={{fontWeight: 700, fontSize: 16, color: 'var(--ink)', marginTop: 16}}>{title}</div>
            {body && <div style={{
                fontSize: 13.5,
                marginTop: 8,
                lineHeight: 1.55,
                maxWidth: 320,
                margin: '8px auto 0'
            }}>{body}</div>}
            {action && <div style={{marginTop: 20}}>{action}</div>}
        </div>
    );
}

// ── Chip selector (property type, entity type etc) ─────────────
function ChipSelector({options, value, onChange}) {
    return (
        <div style={{display: 'grid', gridTemplateColumns: 'repeat(auto-fill,minmax(150px,1fr))', gap: 10}}>
            {options.map(o => {
                const on = o.id === value;
                return (
                    <div key={o.id} onClick={() => onChange(o.id)} className="g-tap" style={{
                        padding: '14px 12px',
                        borderRadius: 'var(--r-md)',
                        border: `1.5px solid ${on ? 'var(--accent-ink)' : 'var(--line)'}`,
                        background: on ? 'var(--accent-ink)' : 'var(--surface)',
                        color: on ? '#fff' : 'var(--ink)',
                        display: 'flex',
                        flexDirection: 'column',
                        gap: 6
                    }}>
                        <Icon name={o.icon} size={22} style={{color: on ? 'var(--accent)' : 'var(--ink-3)'}}/>
                        <div style={{fontWeight: 700, fontSize: 13.5}}>{o.label}</div>
                        {o.desc && <div style={{
                            fontSize: 11.5,
                            color: on ? 'rgba(255,255,255,.65)' : 'var(--ink-3)',
                            lineHeight: 1.4
                        }}>{o.desc}</div>}
                    </div>
                );
            })}
        </div>
    );
}

Object.assign(window, {
    Icon, Badge, Card, Btn, Input, Select, FileUpload, Metric, KpiCard,
    Ring, PerfBars, SegTabs, Field, SectionLabel, Sheet, Toggle,
    WizardShell, PageHeader, Empty, ChipSelector, toneColor,
});
