// Afford-X · ALM agent — unique brand mark, state machine, floating chat.
const { useState, useRef, useEffect, useCallback, createContext, useContext } = React;

/* ============================================================
   The ALM mark — an Afford-X spark.
   A 4-point star rotated to read as the brand "X", with a green
   gradient core and an orbiting micro-spark (red accent nod to
   the Afford-X "X"). Distinct from any third-party logo.
   ============================================================ */
const STAR = 'M12 1.6C12.7 6.9 17.1 11.3 22.4 12C17.1 12.7 12.7 17.1 12 22.4C11.3 17.1 6.9 12.7 1.6 12C6.9 11.3 11.3 6.9 12 1.6Z';

function AlmMark({ size = 26, state = 'idle', tone = 'green', id = 'm' }) {
  const gid = `almg-${id}`;
  const core = tone === 'white' ? '#fff' : `url(#${gid})`;
  const sat = tone === 'white' ? 'rgba(255,255,255,0.9)' : 'var(--afx-red-bright)';
  return (
    <span className={`alm-mark alm-state-${state}`} aria-hidden="true">
      <svg width={size} height={size} viewBox="0 0 24 24">
        <defs>
          <linearGradient id={gid} x1="0" y1="0" x2="1" y2="1">
            <stop offset="0" stopColor="#74C07F" />
            <stop offset="0.55" stopColor="var(--afx-green-500)" />
            <stop offset="1" stopColor="var(--afx-green-700)" />
          </linearGradient>
        </defs>
        <g className="alm-core" style={{ transform: 'rotate(45deg)', transformOrigin: '12px 12px' }}>
          <path d={STAR} fill={core} />
        </g>
        <g className="alm-orbit">
          <path d={STAR} fill={sat} transform="translate(20.5 4) scale(0.26)" />
        </g>
      </svg>
    </span>
  );
}

/* ============================================================
   Agent state machine (shared via context)
   phase: idle | engaged | listening | thinking | answering
   ============================================================ */
const GREETING = "Hi — I'm ALM, the Affordability Language Model behind Afford-X. Ask me anything about exclusion filters, segments, or how we make intent better.";
const SUGGESTIONS = [
  'What is the Exclusion Filter?',
  'How does ALM score affordability?',
  'Which industries do you serve?',
];

const SYS_PROMPT = `You are ALM, the Affordability Language Model — the AI agent that powers Afford-X, a martech platform.
Afford-X scores audiences on real purchasing power (not just intent) and offers a first-in-market one-click exclusion filter that removes window-shoppers before a campaign launches.
Key facts you can draw on: nearly 1 billion U.S. MAIDs and HEMs across 100+ data segments (income, credit, purchase behavior, intent); a Dollar Scoring Framework; GDPR/CCPA/LGPD compliant, consented/pseudonymized signals; verticals served include auto, luxury, financial services and e-commerce; tagline "We Make Intent Better."
Voice: confident, modern, benefit-led B2B. Speak to the visitor as "you", refer to Afford-X as "we". Keep replies under 70 words, no emoji, no markdown headers. Be concrete and helpful.`;

const AgentCtx = createContext(null);
const useAgent = () => useContext(AgentCtx);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

function AgentProvider({ children }) {
  const [open, setOpen] = useState(false);
  const [phase, setPhase] = useState('idle');
  const [messages, setMessages] = useState([{ role: 'assistant', content: GREETING }]);
  const [draft, setDraft] = useState('');
  const busyRef = useRef(false);

  const openWidget = useCallback(() => {
    setOpen(true);
    setPhase((p) => (p === 'idle' ? 'engaged' : p));
  }, []);
  const closeWidget = useCallback(() => {
    setOpen(false);
    if (!busyRef.current) setPhase('idle');
  }, []);

  // listening reacts to visitor typing
  const onDraft = useCallback((v) => {
    setDraft(v);
    if (busyRef.current) return;
    setPhase(v.trim() ? 'listening' : 'engaged');
  }, []);

  const ask = useCallback(async (qRaw) => {
    const q = (qRaw == null ? draft : qRaw).trim();
    if (!q || busyRef.current) return;
    busyRef.current = true;
    setDraft('');
    setMessages((m) => [...m, { role: 'user', content: q }]);
    setPhase('thinking');

    // build short conversation context
    const history = messages.filter((m) => m.role === 'user' || m.role === 'assistant').slice(-6)
      .map((m) => `${m.role === 'user' ? 'Visitor' : 'ALM'}: ${m.content}`).join('\n');
    const prompt = `${SYS_PROMPT}\n\n${history ? 'Conversation so far:\n' + history + '\n\n' : ''}Visitor: ${q}\nALM:`;

    let reply = '';
    try {
      if (window.claude && window.claude.complete) {
        reply = await window.claude.complete(prompt);
      } else {
        throw new Error('no-api');
      }
    } catch (e) {
      reply = "We narrow your intent audience down to people with real purchasing power — our one-click Exclusion Filter removes window-shoppers before launch, so every ad dollar works harder. (Live answers activate when this page is shared.)";
      await sleep(900);
    }
    reply = (reply || '').trim();

    // stream the reply word-by-word for the "answering" state
    setPhase('answering');
    setMessages((m) => [...m, { role: 'assistant', content: '' }]);
    const parts = reply.split(/(\s+)/);
    let acc = '';
    for (const w of parts) {
      acc += w;
      setMessages((m) => { const c = m.slice(); c[c.length - 1] = { role: 'assistant', content: acc }; return c; });
      await sleep(24);
    }
    busyRef.current = false;
    setPhase(open ? 'engaged' : 'idle');
  }, [draft, messages, open]);

  const value = { open, phase, messages, draft, openWidget, closeWidget, onDraft, ask, busy: () => busyRef.current };
  return <AgentCtx.Provider value={value}>{children}</AgentCtx.Provider>;
}

/* ============================================================
   Header indicator — "Ask ALM" pill that reflects live state
   ============================================================ */
const LABELS = {
  idle: 'Ask ALM',
  engaged: 'ALM is here',
  listening: 'Listening…',
  thinking: 'Thinking…',
  answering: 'Answering…',
};
const DOT_COLOR = {
  idle: 'var(--afx-gray-400)',
  engaged: 'var(--afx-green-500)',
  listening: 'var(--afx-green-500)',
  thinking: 'var(--afx-yellow)',
  answering: 'var(--afx-green-500)',
};

function HeaderAgent() {
  const { phase, open, openWidget, closeWidget } = useAgent();
  const active = phase !== 'idle';
  return (
    <button
      onClick={open ? closeWidget : openWidget}
      style={{
        display: 'inline-flex', alignItems: 'center', gap: 9, cursor: 'pointer',
        height: 40, padding: '0 14px 0 10px', borderRadius: 999,
        border: `1.5px solid ${active ? 'var(--afx-green-500)' : 'var(--afx-gray-200)'}`,
        background: active ? 'var(--afx-green-50)' : '#fff',
        fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 14,
        color: 'var(--afx-ink)', transition: 'border-color 0.18s ease, background 0.18s ease',
      }}
    >
      <AlmMark size={22} state={phase} id="hdr" />
      <span style={{ minWidth: 78, textAlign: 'left' }}>{LABELS[phase]}</span>
      <span className={active ? 'alm-dot' : ''} style={{ width: 8, height: 8, borderRadius: '50%', background: DOT_COLOR[phase], color: DOT_COLOR[phase], display: 'inline-block' }} />
    </button>
  );
}

/* ============================================================
   Floating widget — bottom-right bubble + chat panel
   ============================================================ */
function Bubble() {
  const { open, phase, openWidget } = useAgent();
  if (open) return null;
  const busy = phase === 'thinking' || phase === 'answering';
  return (
    <button
      onClick={openWidget}
      aria-label="Open ALM assistant"
      style={{
        position: 'fixed', right: 26, bottom: 26, zIndex: 60,
        width: 62, height: 62, borderRadius: '50%', cursor: 'pointer', border: 'none',
        background: 'linear-gradient(135deg, var(--afx-green-400), var(--afx-green-600))',
        boxShadow: '0 10px 28px rgba(42,87,52,0.34)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}
    >
      {!busy && <span className="alm-bubble-ring" />}
      <AlmMark size={32} state={phase} tone="white" id="bub" />
    </button>
  );
}

function SubLine({ phase }) {
  if (phase === 'thinking') return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
      Thinking <span className="alm-think-dots"><span></span><span></span><span></span></span>
    </span>
  );
  if (phase === 'answering') return <span>Answering…</span>;
  if (phase === 'listening') return <span>Listening…</span>;
  return <span>Online · replies in seconds</span>;
}

function Panel() {
  const { open, phase, messages, draft, closeWidget, onDraft, ask, busy } = useAgent();
  const scrollRef = useRef(null);
  const inputRef = useRef(null);
  useEffect(() => {
    if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
  }, [messages, phase, open]);
  useEffect(() => { if (open && inputRef.current) inputRef.current.focus(); }, [open]);
  if (!open) return null;
  const isBusy = busy();

  return (
    <div className="alm-panel" style={{
      position: 'fixed', right: 26, bottom: 26, zIndex: 60,
      width: 380, maxWidth: 'calc(100vw - 32px)', height: 560, maxHeight: 'calc(100vh - 52px)',
      background: '#fff', borderRadius: 18, overflow: 'hidden',
      border: '1px solid var(--afx-gray-200)', boxShadow: '0 24px 60px rgba(5,7,8,0.22)',
      display: 'flex', flexDirection: 'column',
    }}>
      {/* header */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '14px 16px', background: 'linear-gradient(135deg, #0c2414, #061a0e)', color: '#fff' }}>
        <div style={{ width: 42, height: 42, borderRadius: 12, background: 'rgba(255,255,255,0.08)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <AlmMark size={26} state={phase} tone="white" id="pnl" />
        </div>
        <div style={{ flex: 1, lineHeight: 1.2 }}>
          <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 16 }}>ALM</div>
          <div style={{ fontSize: 12, opacity: 0.85 }}><SubLine phase={phase} /></div>
        </div>
        <button onClick={closeWidget} aria-label="Close" style={{ background: 'rgba(255,255,255,0.1)', border: 'none', color: '#fff', width: 30, height: 30, borderRadius: 8, cursor: 'pointer', fontSize: 18, lineHeight: 1 }}>×</button>
      </div>

      {/* messages */}
      <div ref={scrollRef} className="alm-scroll" style={{ flex: 1, overflowY: 'auto', padding: '18px 16px', background: 'var(--afx-gray-50)', display: 'flex', flexDirection: 'column', gap: 12 }}>
        {messages.map((m, i) => (
          <div key={i} className="alm-msg" style={{ display: 'flex', justifyContent: m.role === 'user' ? 'flex-end' : 'flex-start' }}>
            <div style={{
              maxWidth: '82%', padding: '10px 13px', fontSize: 14, lineHeight: 1.5,
              fontFamily: 'var(--font-body)',
              borderRadius: m.role === 'user' ? '14px 14px 4px 14px' : '14px 14px 14px 4px',
              background: m.role === 'user' ? 'var(--afx-green-500)' : '#fff',
              color: m.role === 'user' ? '#fff' : 'var(--afx-gray-700)',
              border: m.role === 'user' ? 'none' : '1px solid var(--afx-gray-200)',
              boxShadow: m.role === 'user' ? 'none' : '0 1px 2px rgba(5,7,8,0.04)',
            }}>{m.content || '\u200b'}</div>
          </div>
        ))}
        {phase === 'thinking' && (
          <div className="alm-msg" style={{ display: 'flex', justifyContent: 'flex-start' }}>
            <div style={{ padding: '12px 14px', background: '#fff', border: '1px solid var(--afx-gray-200)', borderRadius: '14px 14px 14px 4px', display: 'flex', alignItems: 'center', gap: 4 }}>
              <span className="alm-think-dots"><span></span><span></span><span></span></span>
            </div>
          </div>
        )}
        {messages.length <= 1 && (
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 4 }}>
            {SUGGESTIONS.map((s) => (
              <button key={s} className="alm-chip" onClick={() => ask(s)} style={{
                padding: '7px 12px', borderRadius: 999, fontSize: 13, fontFamily: 'var(--font-body)',
                background: '#fff', border: '1px solid var(--afx-gray-300)', color: 'var(--afx-green-700)', cursor: 'pointer',
              }}>{s}</button>
            ))}
          </div>
        )}
      </div>

      {/* input */}
      <form onSubmit={(e) => { e.preventDefault(); ask(); }} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: 12, borderTop: '1px solid var(--afx-gray-200)', background: '#fff' }}>
        <input
          ref={inputRef}
          value={draft}
          onChange={(e) => onDraft(e.target.value)}
          placeholder={isBusy ? 'ALM is responding…' : 'Ask about Afford-X…'}
          disabled={isBusy}
          style={{
            flex: 1, height: 42, padding: '0 14px', borderRadius: 999, fontSize: 14, fontFamily: 'var(--font-body)',
            border: '1.5px solid var(--afx-gray-200)', outline: 'none', color: 'var(--afx-ink)', background: 'var(--afx-gray-50)',
          }}
          onFocus={(e) => (e.target.style.borderColor = 'var(--afx-green-500)')}
          onBlur={(e) => (e.target.style.borderColor = 'var(--afx-gray-200)')}
        />
        <button type="submit" disabled={isBusy || !draft.trim()} aria-label="Send" style={{
          width: 42, height: 42, borderRadius: '50%', border: 'none', flexShrink: 0,
          cursor: isBusy || !draft.trim() ? 'default' : 'pointer',
          background: !draft.trim() || isBusy ? 'var(--afx-gray-200)' : 'var(--afx-green-500)',
          color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center',
          transition: 'background 0.15s ease',
        }}>
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
        </button>
      </form>
    </div>
  );
}

function AgentWidget() {
  return <><Bubble /><Panel /></>;
}

Object.assign(window, { AgentProvider, useAgent, HeaderAgent, AgentWidget, AlmMark });
// Afford-X · generic ad-platform mock + live simulation dashboard (for the extension demo).
// NOTE: deliberately generic adtech UI — not a recreation of any real platform's interface.
const { Badge } = window.AffordXDesignSystem_998110;
const { useState, useEffect, useRef } = React;

const navIcon = {
  overview: '<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>',
  campaigns: '<path d="m3 11 18-5v12L3 14v-3z"/><path d="M11.6 16.8a3 3 0 1 1-5.8-1.6"/>',
  audiences: '<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"/>',
  creative: '<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.5-3.5L9 20"/>',
  reports: '<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>',
  billing: '<rect x="2" y="5" width="20" height="14" rx="2"/><line x1="2" y1="10" x2="22" y2="10"/>',
};
function NIcon({ d }) {
  return <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round" dangerouslySetInnerHTML={{ __html: d }} />;
}

const ROWS = [
  ['Q3 SUV — Prospecting', 'Active', '$12,480', '$58', '215'],
  ['Luxury Sedan — Retargeting', 'Active', '$8,140', '$72', '113'],
  ['EV Lineup — Lookalike', 'Active', '$15,920', '$84', '189'],
  ['Certified Pre-Owned', 'Paused', '$3,210', '$41', '78'],
  ['Service & Parts', 'Active', '$2,760', '$33', '84'],
];
const TILES = [['Spend', '$42.5K'], ['Impressions', '4.8M'], ['Clicks', '38.2K'], ['Conversions', '679']];

// The mock ad platform page. `cueRef` is attached to the Campaigns nav item for the coachmark.
function AdPlatform({ platform, cueOn, onCue, cueRef, dimmed }) {
  const nav = [['overview', 'Overview'], ['campaigns', 'Campaigns'], ['audiences', 'Audiences'], ['creative', 'Creative'], ['reports', 'Reports'], ['billing', 'Billing']];
  return (
    <div style={{ position: 'absolute', inset: 0, display: 'grid', gridTemplateColumns: '210px 1fr', background: '#fff', filter: dimmed ? 'saturate(0.9)' : 'none' }}>
      {/* left rail */}
      <div style={{ background: 'var(--afx-black)', color: 'var(--afx-gray-400)', padding: '18px 12px', display: 'flex', flexDirection: 'column', gap: 4 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '4px 8px 16px' }}>
          <span style={{ width: 26, height: 26, borderRadius: 7, background: platform.color, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontWeight: 800, fontFamily: 'var(--font-display)', fontSize: 14 }}>{platform.glyph}</span>
          <span style={{ color: '#fff', fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 14 }}>{platform.short}</span>
        </div>
        {nav.map(([k, label]) => {
          const active = k === 'campaigns' && cueOn;
          const isCampaigns = k === 'campaigns';
          return (
            <button key={k} ref={isCampaigns ? cueRef : null} onClick={isCampaigns && cueOn ? onCue : undefined}
              style={{
                position: 'relative', display: 'flex', alignItems: 'center', gap: 11, padding: '9px 10px', borderRadius: 8,
                border: active ? '2px solid var(--afx-green-400)' : '2px solid transparent', background: active ? 'rgba(84,169,102,0.18)' : 'transparent',
                color: active ? '#fff' : (k === 'overview' ? '#fff' : 'var(--afx-gray-400)'),
                fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 13.5, cursor: isCampaigns && cueOn ? 'pointer' : 'default', textAlign: 'left',
                boxShadow: active ? '0 0 0 4px rgba(84,169,102,0.25)' : 'none',
              }}>
              <NIcon d={navIcon[k]} /> {label}
            </button>
          );
        })}
      </div>
      {/* content */}
      <div style={{ display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
        <div style={{ height: 56, borderBottom: '1px solid var(--afx-gray-200)', display: 'flex', alignItems: 'center', gap: 12, padding: '0 22px', flexShrink: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 13.5, color: 'var(--afx-ink)' }}>
            <span style={{ width: 22, height: 22, borderRadius: '50%', background: 'var(--afx-green-100)', color: 'var(--afx-green-700)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, fontWeight: 700 }}>AA</span>
            Acme Auto · #4471
          </div>
          <span style={{ marginLeft: 'auto', fontSize: 12.5, color: 'var(--afx-gray-500)', border: '1px solid var(--afx-gray-200)', borderRadius: 7, padding: '6px 10px' }}>Last 30 days</span>
          <span style={{ fontSize: 12.5, color: 'var(--afx-gray-500)', border: '1px solid var(--afx-gray-200)', borderRadius: 7, padding: '6px 10px' }}>Search</span>
        </div>
        <div style={{ padding: '22px', overflow: 'auto' }}>
          <h1 style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 24, letterSpacing: '-0.02em', color: 'var(--afx-ink)', margin: '0 0 2px' }}>Campaigns</h1>
          <p style={{ fontSize: 13, color: 'var(--afx-gray-500)', margin: '0 0 18px' }}>{platform.name} · 5 active campaigns</p>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 14, marginBottom: 20 }}>
            {TILES.map(([l, v]) => (
              <div key={l} style={{ border: '1px solid var(--afx-gray-200)', borderRadius: 12, padding: '14px 16px' }}>
                <div style={{ fontSize: 12.5, color: 'var(--afx-gray-500)', fontWeight: 600 }}>{l}</div>
                <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 26, color: 'var(--afx-ink)', letterSpacing: '-0.02em' }}>{v}</div>
              </div>
            ))}
          </div>
          <div style={{ border: '1px solid var(--afx-gray-200)', borderRadius: 12, overflow: 'hidden' }}>
            <div style={{ display: 'grid', gridTemplateColumns: '2.4fr 1fr 1fr 1fr 1fr', padding: '11px 16px', background: 'var(--afx-gray-50)', fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 12, color: 'var(--afx-gray-500)', textTransform: 'uppercase', letterSpacing: '0.04em' }}>
              <span>Campaign</span><span>Status</span><span>Spend</span><span>CPA</span><span>Conv.</span>
            </div>
            {ROWS.map((r, i) => (
              <div key={i} style={{ display: 'grid', gridTemplateColumns: '2.4fr 1fr 1fr 1fr 1fr', padding: '13px 16px', borderTop: '1px solid var(--afx-gray-100)', fontSize: 13.5, color: 'var(--afx-gray-700)', alignItems: 'center' }}>
                <span style={{ fontWeight: 600, color: 'var(--afx-ink)' }}>{r[0]}</span>
                <span><Badge tone={r[1] === 'Active' ? 'green' : 'neutral'}>{r[1]}</Badge></span>
                <span>{r[2]}</span><span>{r[3]}</span><span>{r[4]}</span>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

// animated counter
function useCountTo(target, on, dur = 1100) {
  // initialise to target so the value is always correct even if rAF is throttled (background tab / capture)
  const [v, setV] = useState(target);
  useEffect(() => {
    if (!on) { setV(target); return; }
    let raf, start, done = false;
    const from = 0;
    const tick = (t) => {
      if (!start) start = t;
      const p = Math.min(1, (t - start) / dur);
      const e = 1 - Math.pow(1 - p, 3);
      setV(from + (target - from) * e);
      if (p < 1) raf = requestAnimationFrame(tick); else done = true;
    };
    raf = requestAnimationFrame(tick);
    // safety: guarantee final value lands even if frames are dropped
    const safety = setTimeout(() => { if (!done) setV(target); }, dur + 400);
    return () => { cancelAnimationFrame(raf); clearTimeout(safety); };
  }, [target, on, dur]);
  return v;
}

function SimDashboard({ platform, onClose }) {
  const [filterOn, setFilterOn] = useState(true);
  const [live, setLive] = useState(0);
  useEffect(() => { const id = setInterval(() => setLive((n) => n + 1), 1400); return () => clearInterval(id); }, []);

  // metric model: current vs with-Afford-X
  const cpaCurrent = 63;
  const cpaAX = 41;
  const cpa = useCountTo(filterOn ? cpaAX : cpaCurrent, filterOn);
  const excluded = useCountTo(filterOn ? 38 : 0, filterOn);
  const lift = useCountTo(filterOn ? 28 : 0, filterOn);
  const convShare = filterOn ? 100 : 62;

  const jitter = filterOn ? (live % 2 === 0 ? 0 : 1) : 0;

  return (
    <div style={{ position: 'absolute', inset: 0, zIndex: 40, display: 'flex', justifyContent: 'flex-end', background: 'rgba(5,7,8,0.34)' }}>
      <div className="alm-panel" style={{ width: 430, maxWidth: '92%', height: '100%', background: '#fff', boxShadow: '-18px 0 50px rgba(5,7,8,0.25)', display: 'flex', flexDirection: 'column' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '16px 18px', background: 'linear-gradient(135deg,#0c2414,#061a0e)', color: '#fff' }}>
          <window.AlmMark size={26} state="answering" tone="white" id="sim" />
          <div style={{ flex: 1, lineHeight: 1.2 }}>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 16 }}>Live Simulation</div>
            <div style={{ fontSize: 12, opacity: 0.85, display: 'flex', alignItems: 'center', gap: 6 }}>
              <span className="alm-dot" style={{ width: 7, height: 7, borderRadius: '50%', background: '#74C07F', color: '#74C07F' }} /> Reading {platform.name} in real time
            </div>
          </div>
          <button onClick={onClose} aria-label="Close" style={{ background: 'rgba(255,255,255,0.12)', border: 'none', color: '#fff', width: 28, height: 28, borderRadius: 8, cursor: 'pointer', fontSize: 17 }}>×</button>
        </div>

        <div className="alm-scroll" style={{ flex: 1, overflowY: 'auto', padding: 18 }}>
          {/* exclusion toggle */}
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', background: 'var(--afx-gray-50)', border: '1px solid var(--afx-gray-200)', borderRadius: 12, padding: '12px 14px', marginBottom: 16 }}>
            <div>
              <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 14, color: 'var(--afx-ink)' }}>Exclusion Filter</div>
              <div style={{ fontSize: 12, color: 'var(--afx-gray-500)' }}>Remove window-shoppers from this audience</div>
            </div>
            <div style={{ display: 'inline-flex', background: '#fff', border: '1px solid var(--afx-gray-200)', borderRadius: 999, padding: 3 }}>
              {[['Off', false], ['On', true]].map(([l, val]) => (
                <button key={l} onClick={() => setFilterOn(val)} style={{ border: 'none', cursor: 'pointer', height: 28, padding: '0 14px', borderRadius: 999, fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 12.5, background: filterOn === val ? 'var(--afx-green-500)' : 'transparent', color: filterOn === val ? '#fff' : 'var(--afx-gray-500)', transition: 'all .15s ease' }}>{l}</button>
              ))}
            </div>
          </div>

          {/* headline CPA comparison */}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 16 }}>
            <div style={{ border: '1px solid var(--afx-gray-200)', borderRadius: 12, padding: '14px 16px' }}>
              <div style={{ fontSize: 12, color: 'var(--afx-gray-500)', fontWeight: 600 }}>Current CPA</div>
              <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 30, color: 'var(--afx-gray-400)', letterSpacing: '-0.02em', textDecoration: filterOn ? 'line-through' : 'none' }}>${cpaCurrent}</div>
            </div>
            <div style={{ border: '2px solid var(--afx-green-500)', borderRadius: 12, padding: '14px 16px', background: 'var(--afx-green-50)' }}>
              <div style={{ fontSize: 12, color: 'var(--afx-green-700)', fontWeight: 600 }}>With Afford-X</div>
              <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 30, color: 'var(--afx-green-700)', letterSpacing: '-0.02em' }}>${Math.round(cpa)}</div>
            </div>
          </div>

          {/* metric rows */}
          {[
            ['Window-shoppers excluded', `${Math.round(excluded)}%`],
            ['Projected conversion lift', `+${Math.round(lift)}%`],
            ['Convertible audience share', `${convShare}%`],
            ['Wasted spend recovered', `$${(16.2 * (filterOn ? 1 : 0)).toFixed(1)}K`],
          ].map(([l, v]) => (
            <div key={l} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '11px 2px', borderBottom: '1px solid var(--afx-gray-100)' }}>
              <span style={{ fontSize: 13.5, color: 'var(--afx-gray-600)' }}>{l}</span>
              <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 15, color: filterOn ? 'var(--afx-green-700)' : 'var(--afx-gray-400)' }}>{v}</span>
            </div>
          ))}

          {/* convertible bar */}
          <div style={{ marginTop: 18 }}>
            <div style={{ fontSize: 12, color: 'var(--afx-gray-500)', fontWeight: 600, marginBottom: 8 }}>Audience composition {filterOn && <span style={{ color: 'var(--afx-green-600)' }}>· live{'.'.repeat(jitter + 1)}</span>}</div>
            <div style={{ height: 28, borderRadius: 8, overflow: 'hidden', display: 'flex', border: '1px solid var(--afx-gray-200)' }}>
              <div style={{ width: `${convShare}%`, background: 'var(--afx-green-500)', transition: 'width .9s cubic-bezier(0.16,1,0.3,1)' }} />
              <div style={{ flex: 1, background: 'repeating-linear-gradient(45deg, var(--afx-gray-100), var(--afx-gray-100) 6px, var(--afx-gray-200) 6px, var(--afx-gray-200) 12px)' }} />
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 6, fontSize: 11.5, color: 'var(--afx-gray-500)' }}>
              <span>● Real purchasing power</span><span>Window-shoppers</span>
            </div>
          </div>
        </div>

        <div style={{ padding: 16, borderTop: '1px solid var(--afx-gray-200)' }}>
          <window.AfxApplyButton />
          <p style={{ fontSize: 11.5, color: 'var(--afx-gray-500)', textAlign: 'center', margin: '10px 0 0' }}>Simulated on your live campaign data — nothing is changed until you apply.</p>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { AdPlatform, SimDashboard });
// Afford-X · ALM browser-extension experience (guided prototype).
const { Button, Badge, Input } = window.AffordXDesignSystem_998110;
const { AgentProvider, AgentWidget, AlmMark } = window;
const { AdPlatform, SimDashboard } = window;
const RX = React;

const PLATFORMS = {
  meta:  { id: 'meta',  name: 'Meta Ads Manager',         short: 'Meta',  glyph: 'M', color: '#1877F2', host: 'adsmanager.facebook.com' },
  gdn:   { id: 'gdn',   name: 'Google Display Network',   short: 'GDN',   glyph: 'G', color: '#4285F4', host: 'ads.google.com' },
  dv360: { id: 'dv360', name: 'Display & Video 360',      short: 'DV360', glyph: 'D', color: '#34A853', host: 'displayvideo.google.com' },
};
const ORDER = ['install', 'email', 'platform', 'navigate', 'scanning', 'dashboard'];
const STEP_LABEL = { install: 'Installed', email: 'Verify email', platform: 'Choose platform', navigate: 'Follow the cue', scanning: 'Reading campaign', dashboard: 'Live simulation' };

// ---- Apply button used inside SimDashboard (exported to window) ----
function AfxApplyButton() {
  const [done, setDone] = RX.useState(false);
  return (
    <button onClick={() => setDone(true)} disabled={done} style={{
      width: '100%', height: 46, borderRadius: 10, border: 'none', cursor: done ? 'default' : 'pointer',
      background: done ? 'var(--afx-green-600)' : 'var(--afx-green-500)', color: '#fff',
      fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 15, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
    }}>
      {done ? <>✓ Exclusion Filter applied</> : 'Apply Exclusion Filter to campaign'}
    </button>
  );
}
Object.assign(window, { AfxApplyButton });

// ---- extension popup ----
function PopupShell({ children, onMin }) {
  return (
    <div className="alm-panel" style={{ position: 'absolute', top: 52, right: 14, zIndex: 60, width: 348, background: '#fff', borderRadius: 14, boxShadow: '0 18px 50px rgba(5,7,8,0.28)', border: '1px solid var(--afx-gray-200)', overflow: 'hidden' }}>
      <div style={{ position: 'absolute', top: -7, right: 26, width: 14, height: 14, background: 'linear-gradient(135deg,#0c2414,#061a0e)', transform: 'rotate(45deg)' }} />
      <div style={{ position: 'relative', display: 'flex', alignItems: 'center', gap: 10, padding: '13px 14px', background: 'linear-gradient(135deg,#0c2414,#061a0e)', color: '#fff' }}>
        <AlmMark size={22} state="engaged" tone="white" id="pop" />
        <div style={{ flex: 1, fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 14 }}>Afford-X ALM</div>
        <button onClick={onMin} aria-label="Minimize" style={{ background: 'rgba(255,255,255,0.12)', border: 'none', color: '#fff', width: 24, height: 24, borderRadius: 6, cursor: 'pointer', fontSize: 14 }}>—</button>
      </div>
      <div style={{ padding: 16 }}>{children}</div>
    </div>
  );
}

function PopupBody({ stage, ctx }) {
  if (stage === 'install') {
    return (
      <div style={{ textAlign: 'center' }}>
        <div style={{ width: 52, height: 52, borderRadius: 14, background: 'var(--afx-green-50)', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '4px auto 12px' }}>
          <AlmMark size={30} state="engaged" id="ins" />
        </div>
        <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 17, color: 'var(--afx-ink)', margin: '0 0 6px' }}>Extension installed</h3>
        <p style={{ fontSize: 13.5, lineHeight: 1.55, color: 'var(--afx-gray-600)', margin: '0 0 16px' }}>Let’s evaluate ALM on the campaigns you’re already running — right inside your browser.</p>
        <Button variant="primary" fullWidth onClick={() => ctx.go('email')}>Get started</Button>
      </div>
    );
  }
  if (stage === 'email') {
    return (
      <div>
        <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 16, color: 'var(--afx-ink)', margin: '0 0 4px' }}>Verify it’s you</h3>
        <p style={{ fontSize: 13, lineHeight: 1.5, color: 'var(--afx-gray-600)', margin: '0 0 14px' }}>Use the <strong>same email</strong> you started your Free Evaluation with.</p>
        <form onSubmit={(e) => { e.preventDefault(); ctx.verify(); }}>
          <Input type="email" placeholder="you@company.com" value={ctx.email} error={ctx.err || undefined}
            onChange={(e) => ctx.setEmail(e.target.value)} autoFocus
            leading={<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="5" width="18" height="14" rx="2" /><path d="m3 7 9 6 9-6" /></svg>} />
          <div style={{ height: 12 }} />
          <Button variant="primary" fullWidth onClick={ctx.verify}>Verify & continue</Button>
        </form>
        {ctx.saved && <p style={{ fontSize: 11.5, color: 'var(--afx-gray-500)', margin: '10px 0 0' }}>Started with <strong>{ctx.saved}</strong></p>}
      </div>
    );
  }
  if (stage === 'platform') {
    return (
      <div>
        <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 16, color: 'var(--afx-ink)', margin: '0 0 4px' }}>Where do you advertise?</h3>
        <p style={{ fontSize: 13, lineHeight: 1.5, color: 'var(--afx-gray-600)', margin: '0 0 14px' }}>Pick the platform you want ALM to evaluate.</p>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
          {Object.values(PLATFORMS).map((p) => {
            const on = ctx.platform === p.id;
            return (
              <button key={p.id} onClick={() => ctx.setPlatform(p.id)} style={{
                display: 'flex', alignItems: 'center', gap: 12, padding: '11px 13px', borderRadius: 10, cursor: 'pointer', textAlign: 'left',
                border: on ? '2px solid var(--afx-green-500)' : '1.5px solid var(--afx-gray-200)', background: on ? 'var(--afx-green-50)' : '#fff',
              }}>
                <span style={{ width: 30, height: 30, borderRadius: 8, background: p.color, color: '#fff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 15, flexShrink: 0 }}>{p.glyph}</span>
                <span style={{ flex: 1, fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 14, color: 'var(--afx-ink)' }}>{p.name}</span>
                <span style={{ width: 18, height: 18, borderRadius: '50%', border: on ? '5px solid var(--afx-green-500)' : '2px solid var(--afx-gray-300)' }} />
              </button>
            );
          })}
        </div>
        <div style={{ height: 14 }} />
        <Button variant="primary" fullWidth disabled={!ctx.platform} onClick={() => ctx.go('navigate')}>Continue</Button>
      </div>
    );
  }
  if (stage === 'navigate') {
    return (
      <div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
          <span className="alm-dot" style={{ width: 9, height: 9, borderRadius: '50%', background: 'var(--afx-green-500)', color: 'var(--afx-green-500)' }} />
          <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 13, color: 'var(--afx-green-700)' }}>ALM is watching your screen</span>
        </div>
        <p style={{ fontSize: 13.5, lineHeight: 1.55, color: 'var(--afx-gray-600)', margin: 0 }}>Open your <strong>Campaigns</strong> tab — follow the highlighted cue on the left. ALM will read your live performance from there.</p>
      </div>
    );
  }
  if (stage === 'scanning') {
    return (
      <div style={{ textAlign: 'center', padding: '4px 0' }}>
        <AlmMark size={30} state="thinking" id="scn" />
        <p style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 14, color: 'var(--afx-ink)', margin: '10px 0 2px' }}>Reading campaign performance…</p>
        <p style={{ fontSize: 12.5, color: 'var(--afx-gray-500)', margin: 0 }}>Scoring audience on real purchasing power</p>
      </div>
    );
  }
  return null;
}

// ---- coachmark over the Campaigns nav ----
function Coachmark({ rect }) {
  if (!rect) return null;
  return (
    <div style={{ position: 'absolute', left: rect.left + rect.width + 14, top: rect.top + rect.height / 2 - 18, zIndex: 50, display: 'flex', alignItems: 'center', gap: 8, pointerEvents: 'none' }}>
      <svg width="44" height="36" viewBox="0 0 44 36" fill="none" style={{ transform: 'scaleX(-1)' }}>
        <path d="M40 18 C 26 18, 16 14, 6 18" stroke="var(--afx-green-500)" strokeWidth="3" strokeLinecap="round" fill="none" />
        <path d="M14 10 L4 18 L14 25" stroke="var(--afx-green-500)" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" fill="none" />
      </svg>
      <span className="alm-dot" style={{ background: 'var(--afx-green-600)', color: 'var(--afx-green-600)', fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 12.5, padding: '6px 12px', borderRadius: 999, boxShadow: '0 6px 18px rgba(42,87,52,0.35)', whiteSpace: 'nowrap' }}>
        <span style={{ color: '#fff' }}>Click here to let ALM scan</span>
      </span>
    </div>
  );
}

function ScanOverlay({ onDone }) {
  const [p, setP] = RX.useState(0);
  RX.useEffect(() => {
    let raf, start;
    const tick = (t) => { if (!start) start = t; const k = Math.min(1, (t - start) / 2100); setP(k); if (k < 1) raf = requestAnimationFrame(tick); else setTimeout(onDone, 250); };
    raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf);
  }, [onDone]);
  return (
    <div style={{ position: 'absolute', inset: 0, zIndex: 45, background: 'rgba(5,7,8,0.55)', backdropFilter: 'blur(2px)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
      <div style={{ width: 340, background: '#fff', borderRadius: 16, padding: 26, textAlign: 'center', boxShadow: 'var(--shadow-lg)' }}>
        <AlmMark size={40} state="thinking" id="ovl" />
        <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 17, color: 'var(--afx-ink)', margin: '14px 0 6px' }}>Reading your campaign performance</h3>
        <p style={{ fontSize: 13, color: 'var(--afx-gray-600)', margin: '0 0 18px' }}>ALM is scoring your audience on real purchasing power…</p>
        <div style={{ height: 8, borderRadius: 999, background: 'var(--afx-gray-100)', overflow: 'hidden' }}>
          <div style={{ width: `${p * 100}%`, height: '100%', background: 'var(--afx-green-500)', transition: 'width .1s linear' }} />
        </div>
        <div style={{ fontSize: 12, color: 'var(--afx-gray-500)', marginTop: 8 }}>{Math.round(p * 100)}%</div>
      </div>
    </div>
  );
}

function ExtApp() {
  const [stage, setStage] = RX.useState('install');
  const [minimized, setMin] = RX.useState(false);
  const [platform, setPlatform] = RX.useState('meta');
  const [email, setEmail] = RX.useState('');
  const [err, setErr] = RX.useState('');
  const [rect, setRect] = RX.useState(null);
  const saved = (typeof localStorage !== 'undefined' && localStorage.getItem('afx_eval_email')) || '';
  const cueRef = RX.useRef(null);
  const viewRef = RX.useRef(null);

  RX.useEffect(() => { setEmail(saved); }, [saved]);

  const go = (s) => { setErr(''); setMin(false); setStage(s); };
  const verify = () => {
    const v = email.trim();
    if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v)) { setErr('Enter a valid email.'); return; }
    if (saved && v.toLowerCase() !== saved.toLowerCase()) { setErr(`Use the email you started with (${saved}).`); return; }
    go('platform');
  };

  // measure the Campaigns nav item for the coachmark during the navigate stage
  RX.useLayoutEffect(() => {
    if (stage !== 'navigate') { setRect(null); return; }
    const measure = () => {
      if (cueRef.current && viewRef.current) {
        const a = cueRef.current.getBoundingClientRect();
        const b = viewRef.current.getBoundingClientRect();
        setRect({ left: a.left - b.left, top: a.top - b.top, width: a.width, height: a.height });
      }
    };
    measure();
    window.addEventListener('resize', measure);
    return () => window.removeEventListener('resize', measure);
  }, [stage]);

  const p = PLATFORMS[platform];
  const idx = ORDER.indexOf(stage);
  const ctx = { go, verify, email, setEmail, err, saved, platform, setPlatform };

  return (
    <div style={{ minHeight: '100vh', background: 'radial-gradient(circle at 50% 0, #20262a, #0d1012)', display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '26px 18px 96px', boxSizing: 'border-box' }}>
      {/* browser window */}
      <div style={{ position: 'relative', width: 'min(1200px, 96vw)', height: 'min(760px, 84vh)', background: '#fff', borderRadius: 14, overflow: 'hidden', boxShadow: '0 40px 100px rgba(0,0,0,0.5)', display: 'flex', flexDirection: 'column' }}>
        {/* chrome */}
        <div style={{ height: 46, flexShrink: 0, background: 'var(--afx-gray-100)', borderBottom: '1px solid var(--afx-gray-200)', display: 'flex', alignItems: 'center', gap: 12, padding: '0 14px' }}>
          <span style={{ display: 'flex', gap: 7 }}>{['#FF5F57', '#FEBC2E', '#28C840'].map((c) => <span key={c} style={{ width: 12, height: 12, borderRadius: '50%', background: c }} />)}</span>
          <span style={{ display: 'flex', gap: 4, color: 'var(--afx-gray-400)' }}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="m15 18-6-6 6-6" /></svg>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="m9 18 6-6-6-6" /></svg>
          </span>
          <div style={{ flex: 1, height: 30, borderRadius: 8, background: '#fff', border: '1px solid var(--afx-gray-200)', display: 'flex', alignItems: 'center', gap: 8, padding: '0 12px', fontFamily: 'var(--font-mono)', fontSize: 12.5, color: 'var(--afx-gray-600)' }}>
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="var(--afx-green-600)" strokeWidth="2"><rect x="3" y="11" width="18" height="11" rx="2" /><path d="M7 11V7a5 5 0 0 1 10 0v4" /></svg>
            {p.host}/campaigns
          </div>
          {/* extension toolbar icons */}
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--afx-gray-400)" strokeWidth="2"><path d="M10 3v4a2 2 0 0 1-2 2H4M3 10h4a2 2 0 0 1 2 2v8M14 3v4a2 2 0 0 0 2 2h4M21 14h-4a2 2 0 0 0-2 2v4" /></svg>
          <button onClick={() => setMin((m) => !m)} aria-label="Afford-X ALM" style={{ width: 30, height: 30, borderRadius: 8, border: minimized ? '2px solid var(--afx-green-500)' : '2px solid transparent', background: 'var(--afx-green-500)', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', position: 'relative' }}>
            <AlmMark size={18} state={stage === 'scanning' ? 'thinking' : 'engaged'} tone="white" id="bar" />
            {minimized && stage !== 'dashboard' && <span style={{ position: 'absolute', top: -3, right: -3, width: 10, height: 10, borderRadius: '50%', background: 'var(--afx-red-bright)', border: '2px solid #fff' }} />}
          </button>
        </div>

        {/* viewport */}
        <div ref={viewRef} style={{ position: 'relative', flex: 1, overflow: 'hidden' }}>
          <AdPlatform platform={p} cueOn={stage === 'navigate'} onCue={() => go('scanning')} cueRef={cueRef} dimmed={stage === 'dashboard'} />
          {stage === 'navigate' && <Coachmark rect={rect} />}
          {stage === 'scanning' && <ScanOverlay onDone={() => go('dashboard')} />}
          {stage === 'dashboard' && <SimDashboard platform={p} onClose={() => go('navigate')} />}
          {!minimized && stage !== 'scanning' && stage !== 'dashboard' && (
            <PopupShell onMin={() => setMin(true)}><PopupBody stage={stage} ctx={ctx} /></PopupShell>
          )}
          {/* ALM chat lives over the ad platform from the navigate step on */}
          {idx >= 3 && <AgentWidget />}
        </div>
      </div>

      {/* demo pager */}
      <div style={{ position: 'fixed', bottom: 20, left: '50%', transform: 'translateX(-50%)', zIndex: 80, display: 'flex', alignItems: 'center', gap: 14, background: 'rgba(255,255,255,0.96)', border: '1px solid var(--afx-gray-200)', borderRadius: 999, padding: '8px 10px 8px 16px', boxShadow: '0 10px 30px rgba(0,0,0,0.35)' }}>
        <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 12.5, color: 'var(--afx-gray-500)' }}>Demo</span>
        <div style={{ display: 'flex', gap: 5 }}>
          {ORDER.map((s, i) => <span key={s} title={STEP_LABEL[s]} onClick={() => go(s)} style={{ width: 9, height: 9, borderRadius: '50%', cursor: 'pointer', background: i === idx ? 'var(--afx-green-500)' : i < idx ? 'var(--afx-green-200)' : 'var(--afx-gray-200)' }} />)}
        </div>
        <span style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 13, color: 'var(--afx-ink)', minWidth: 118, textAlign: 'center' }}>{idx + 1}. {STEP_LABEL[stage]}</span>
        <div style={{ display: 'flex', gap: 6 }}>
          <button onClick={() => go(ORDER[Math.max(0, idx - 1)])} disabled={idx === 0} style={pagerBtn(idx === 0)}>Back</button>
          <button onClick={() => go(ORDER[Math.min(ORDER.length - 1, idx + 1)])} disabled={idx === ORDER.length - 1} style={pagerBtn(idx === ORDER.length - 1, true)}>Next</button>
        </div>
      </div>
    </div>
  );
}

function pagerBtn(disabled, primary) {
  return {
    height: 34, padding: '0 16px', borderRadius: 999, border: 'none', cursor: disabled ? 'default' : 'pointer',
    fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 13,
    background: disabled ? 'var(--afx-gray-100)' : primary ? 'var(--afx-green-500)' : 'var(--afx-gray-100)',
    color: disabled ? 'var(--afx-gray-400)' : primary ? '#fff' : 'var(--afx-gray-700)',
  };
}

function ExtensionApp() {
  return (
    <AgentProvider>
      <ExtApp />
    </AgentProvider>
  );
}

Object.assign(window, { ExtensionApp });

// auto-render
document.body.innerHTML = '<div id="afx-root"></div>';
ReactDOM.createRoot(document.getElementById('afx-root')).render(<window.ExtensionApp />);
