// Orbix Core site components
// React hooks are exposed as globals from index.html.

// ============================================================
// ROUTER (path-based, History API)
// ============================================================
const ROUTES = ['home', 'vision', 'products', 'pricing', 'about', 'contact', 'cookies', 'data', 'roadmap'];
function pathToRoute(p) {
  const slug = (p || '/').replace(/^\/+/, '').replace(/\/+$/, '').split('/')[0] || 'home';
  return ROUTES.includes(slug) ? slug : 'home';
}
function routeToPath(r) {
  return r === 'home' ? '/' : '/' + r;
}
function useRoute() {
  const [route, setRoute] = useState(() => pathToRoute(window.location.pathname));
  useEffect(() => {
    const onPop = () => {
      setRoute(pathToRoute(window.location.pathname));
      window.scrollTo({ top: 0, behavior: 'instant' });
    };
    window.addEventListener('popstate', onPop);
    window.addEventListener('orbix:nav', onPop);
    return () => {
      window.removeEventListener('popstate', onPop);
      window.removeEventListener('orbix:nav', onPop);
    };
  }, []);
  return route;
}
const go = (r) => {
  const path = routeToPath(r);
  if (window.location.pathname !== path) {
    window.history.pushState({}, '', path);
    window.dispatchEvent(new Event('orbix:nav'));
  }
};

// ============================================================
// NAV
// ============================================================
function Nav({ theme, onToggleTheme, route, user, onSignIn, onSignUp, onSignOut }) {
  const [open, setOpen] = useState(false);
  const links = [
    { id: 'vision', label: 'Vision' },
    { id: 'products', label: 'Products' },
    { id: 'pricing', label: 'Pricing' },
    { id: 'about', label: 'About' },
    { id: 'contact', label: 'Contact' },
  ];

  return (
    <nav className="nav">
      <div className="nav-inner">
        <a className="brand" href="/" onClick={(e) => { e.preventDefault(); go('home'); }}>
          <img className="brand-mark" src="/images/orbix-logo.png?v=2026-05-16a" width="28" height="28" alt="" aria-hidden="true" />
          <span className="brand-name">Orbix Core</span>
        </a>

        <div className="nav-links">
          {links.map((l) => (
            <a
              key={l.id}
              href={`/${l.id}`}
              onClick={(e) => { e.preventDefault(); go(l.id); }}
              className={`nav-link ${route === l.id ? 'is-active' : ''}`}
            >
              {l.label}
            </a>
          ))}
        </div>

        <div className="nav-actions">
          <button
            className="theme-toggle mono"
            onClick={onToggleTheme}
            aria-label="Toggle theme"
            title="Toggle theme"
          >
            <span className={`theme-dot ${theme === 'dark' ? 'on' : ''}`} />
            {theme === 'dark' ? 'DARK' : 'LIGHT'}
          </button>
          {user ? (
            <window.UserMenu user={user} onSignOut={onSignOut} />
          ) : (
            <>
              <button className="btn-text mono" onClick={onSignIn}>Sign in</button>
              <button
                className="btn-primary"
                onClick={onSignUp}
              >
                Get Early Access
                <span className="btn-arrow" aria-hidden="true">→</span>
              </button>
            </>
          )}
          <button
            className="menu-btn"
            aria-label="Menu"
            onClick={() => setOpen((v) => !v)}
          >
            <span /><span /><span />
          </button>
        </div>
      </div>
      {open && (
        <div className="nav-mobile">
          {links.map((l) => (
            <a
              key={l.id}
              href={`/${l.id}`}
              onClick={(e) => { e.preventDefault(); setOpen(false); go(l.id); }}
            >
              {l.label}
            </a>
          ))}
          {!user && <button className="nav-mobile-btn" onClick={() => { setOpen(false); onSignIn(); }}>Sign in</button>}
          {!user && <button className="nav-mobile-btn" onClick={() => { setOpen(false); onSignUp(); }}>Get Early Access →</button>}
          {user && <button className="nav-mobile-btn" onClick={() => { setOpen(false); onSignOut(); }}>Sign out</button>}
        </div>
      )}
    </nav>
  );
}

// ============================================================
// HERO with live Bob terminal
// ============================================================
const TERMINAL_SCRIPT = [
  { who: 'you', text: 'read this invoice and log it' },
  { who: 'sys', text: 'attached invoice.pdf · read on-device' },
  { who: 'bob', text: 'Reading the PDF on your machine.' },
  { who: 'bob', text: 'Got the vendor, date, total, and line items.' },
  { who: 'bob', text: 'Want me to log every invoice like this automatically?' },
];

const DRY_PASS_SCRIPT = [
  { who: 'sys', text: 'workflow preview · runs on your machine' },
  { who: 'bob', text: 'Trigger · a new PDF in your invoices folder' },
  { who: 'bob', text: 'Step · read it with a local model' },
  { who: 'bob', text: 'Output · a row in your database, a webhook, or email' },
  { who: 'sys', text: 'nothing leaves your computer' },
  { who: 'bob', text: 'Save this as a reusable workflow?' },
];

function Terminal() {
  const [lines, setLines] = useState([]);
  const [typing, setTyping] = useState('');
  const [step, setStep] = useState(0);
  const [done, setDone] = useState(false);
  const [phase, setPhase] = useState('plan');
  const [committed, setCommitted] = useState(false);

  const script = phase === 'plan' ? TERMINAL_SCRIPT : DRY_PASS_SCRIPT;

  useEffect(() => {
    if (step >= script.length) { setDone(true); return; }
    const cur = script[step];
    let i = 0;
    setTyping('');
    const interval = setInterval(() => {
      i++;
      setTyping(cur.text.slice(0, i));
      if (i >= cur.text.length) {
        clearInterval(interval);
        setTimeout(() => {
          setLines((ls) => [...ls, cur]);
          setTyping('');
          setStep((s) => s + 1);
        }, cur.who === 'sys' ? 240 : 480);
      }
    }, cur.who === 'sys' ? 12 : 24);
    return () => clearInterval(interval);
  }, [step, phase]);

  const reset = () => {
    setLines([]); setStep(0); setTyping(''); setDone(false);
    setPhase('plan'); setCommitted(false);
  };
  const runDryPass = () => {
    setLines((ls) => [...ls, { who: 'you', text: 'yes, show me the workflow' }]);
    setStep(0); setDone(false); setPhase('dry');
  };
  const commit = () => {
    setLines((ls) => [...ls, { who: 'sys', text: 'saved · runs on every new invoice · all local' }]);
    setCommitted(true);
  };

  const prefix = (who) => {
    if (who === 'you') return <span className="t-prefix t-you">you ›</span>;
    if (who === 'bob') return <span className="t-prefix t-bob">bob ›</span>;
    return <span className="t-prefix t-sys">sys ·</span>;
  };

  const cur = TERMINAL_SCRIPT[step];

  return (
    <div className="terminal" role="region" aria-label="Bob terminal demo">
      <div className="terminal-bar">
        <div className="terminal-dots">
          <span /><span /><span />
        </div>
        <div className="terminal-title mono">bob · orchestrator · ~/orbix</div>
        <div className="terminal-meta mono">
          <span className="status-dot" /> LOCAL
        </div>
      </div>
      <div className="terminal-body mono">
        <div className="t-line t-meta">
          <span className="t-prefix t-sys">sys ·</span>
          <span>orbix core 0.9.2 · ready</span>
        </div>
        {lines.map((l, i) => (
          <div key={i} className={`t-line ${l.who === 'sys' ? 't-meta' : ''}`}>
            {prefix(l.who)}
            <span>{l.text}</span>
          </div>
        ))}
        {!done && cur && (
          <div className={`t-line ${cur.who === 'sys' ? 't-meta' : ''}`}>
            {prefix(cur.who)}
            <span>{typing}<span className="caret" /></span>
          </div>
        )}
        {done && phase === 'plan' && (
          <div className="t-line t-actions">
            <button className="t-btn" onClick={reset}>↻ replay</button>
            <button className="t-btn t-btn-primary" onClick={runDryPass}>▶ preview workflow</button>
          </div>
        )}
        {done && phase === 'dry' && !committed && (
          <div className="t-line t-actions">
            <button className="t-btn" onClick={reset}>discard</button>
            <button className="t-btn t-btn-primary" onClick={commit}>save workflow</button>
          </div>
        )}
        {committed && (
          <div className="t-line t-actions">
            <button className="t-btn" onClick={reset}>↻ replay</button>
          </div>
        )}
      </div>
    </div>
  );
}

function Hero() {
  return (
    <section className="hero" id="top">
      <div className="hero-grid">
        <div className="hero-left">
          <div className="eyebrow mono">
            <span className="eyebrow-dot" />
            LOCAL-FIRST AI &nbsp;//&nbsp; RUNS ON YOUR HARDWARE
          </div>
          <h1 className="hero-title">
            Run AI on your own computer.<br />
            <span className="hero-title-soft">Free. No cloud account, no token bill.</span>
          </h1>
          <p className="hero-sub">
            Orbix Core is a free desktop app that runs open AI models locally. Chat
            with Bob, build automations you can reuse, and connect your tools. Small
            models on your own hardware mean less energy and data that never leaves
            your machine unless you choose to send it.
          </p>
          <div className="hero-cta">
            <a
              href="/contact"
              className="btn-primary btn-lg"
              onClick={(e) => { e.preventDefault(); go('contact'); }}
            >
              Get Early Access
              <span className="btn-arrow">→</span>
            </a>
            <a href="#how" className="btn-ghost mono">
              <span className="play-mark">▸</span> See how it works
            </a>
          </div>

          <div className="hero-stats mono">
            <div><span className="stat-n">0</span><span className="stat-l">per-token fees</span></div>
            <div><span className="stat-n">100%</span><span className="stat-l">on-device by default</span></div>
            <div><span className="stat-n">green</span><span className="stat-l">small models, less power</span></div>
          </div>
        </div>

        <div className="hero-right">
          <Terminal />
          <div className="hero-right-meta mono">
            <span><span className="status-dot" /> live demo · runs locally</span>
            <span>your model · metal gpu</span>
          </div>
        </div>
      </div>
    </section>
  );
}

// ============================================================
// SCROLL-DRIVEN "HOW IT WORKS"  -  product-wide, four stages
// ============================================================
const STAGES = [
  {
    id: 'ask',
    tag: '01 · ASK',
    title: 'Ask Bob in plain language.',
    body: 'Type or talk. Drop in a file, an image, or a PDF. Bob runs on a local model of your choice and answers on your machine, not in someone else’s datacenter.',
    metrics: [
      ['models', 'built-in or bring your own'],
      ['reads', 'text · images · pdf'],
      ['runs on', 'your own gpu'],
      ['fees', 'no per-token bill'],
    ],
  },
  {
    id: 'build',
    tag: '02 · BUILD',
    title: 'Turn it into a workflow.',
    body: 'Anything Bob does once can become a saved workflow: a trigger, an AI step, and an output. Build it once, then reuse it on a schedule or an event.',
    metrics: [
      ['trigger', 'email · webhook · file'],
      ['process', 'ai + your tools'],
      ['reuse', 'save & schedule'],
      ['record', 'kept on your machine'],
    ],
  },
  {
    id: 'connect',
    tag: '03 · CONNECT',
    title: 'Send results where you work.',
    body: 'Write straight into a database, Dataverse, SQL Server, a webhook, or email. Reach Orbix from your phone or another laptop over your own network.',
    metrics: [
      ['databases', 'sqlite · sql server'],
      ['business', 'dataverse · d365'],
      ['generic', 'rest · webhook · email'],
      ['remote', 'cli · lan endpoint'],
    ],
  },
  {
    id: 'control',
    tag: '04 · CONTROL',
    title: 'Local by default. Cloud on your terms.',
    body: 'Everything runs on your hardware. When a step needs a cloud model, Secure Relay strips personal data first, so the cloud sees tokens, never you.',
    metrics: [
      ['default', 'fully local'],
      ['cloud', 'opt-in per step'],
      ['relay', 'strips personal data'],
      ['record', 'local audit log'],
    ],
  },
];

function HowItWorks() {
  const [active, setActive] = useState(0);
  const ref = useRef(null);
  const stageRefs = useRef([]);

  useEffect(() => {
    const obs = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            const idx = Number(e.target.dataset.idx);
            setActive(idx);
          }
        });
      },
      { rootMargin: '-45% 0px -45% 0px', threshold: 0 }
    );
    stageRefs.current.forEach((el) => el && obs.observe(el));
    return () => obs.disconnect();
  }, []);

  return (
    <section className="how" id="how" ref={ref}>
      <div className="how-header">
        <div className="section-eyebrow mono">
          <span className="eyebrow-dot" /> HOW IT WORKS
        </div>
        <h2 className="section-title">
          What you can do, <span className="section-title-soft">start to finish.</span>
        </h2>
      </div>

      <div className="how-layout">
        <div className="how-sticky">
          <Diagram active={active} />
        </div>

        <div className="how-stages">
          {STAGES.map((s, i) => (
            <article
              key={s.id}
              ref={(el) => (stageRefs.current[i] = el)}
              data-idx={i}
              className={`stage ${active === i ? 'active' : ''}`}
            >
              <div className="stage-tag mono">{s.tag}</div>
              <h3 className="stage-title">{s.title}</h3>
              <p className="stage-body">{s.body}</p>
              <dl className="stage-metrics mono">
                {s.metrics.map(([k, v]) => (
                  <div key={k} className="metric-row">
                    <dt>{k}</dt>
                    <span className="metric-dots" aria-hidden="true" />
                    <dd>{v}</dd>
                  </div>
                ))}
              </dl>
            </article>
          ))}
        </div>
      </div>
    </section>
  );
}

// ============================================================
// MODULE INDEX  -  quiet grid of every part of the product
// ============================================================
const MODULES = [
  {
    id: 'bob',
    tag: '01 · THE ASSISTANT',
    title: 'Bob.',
    body: 'One assistant you talk to in plain language. He reads your files, plans the steps, and does the work. The intelligence runs on your machine, so it works offline and stays private.',
    note: 'Local models · reads images & pdf · voice or text',
  },
  {
    id: 'workflows',
    tag: '02 · WORKFLOWS',
    title: 'Build once, reuse forever.',
    body: 'Save any task as a workflow: a trigger, AI processing, and an output. Schedule it, fire it on an event, or run it by hand. Same process, every time.',
    note: 'Triggers · reusable steps · scheduled runs',
  },
  {
    id: 'connect',
    tag: '03 · CONNECTORS & OUTPUT',
    title: 'Plug into what you already use.',
    body: 'Write results into SQLite, Dataverse, SQL Server, a REST endpoint, a webhook, or email. Bring your own database and Orbix adapts to it.',
    note: 'sqlite · dataverse · sql server · rest · email',
  },
  {
    id: 'relay',
    tag: '04 · LOCAL + CLOUD SECURITY',
    title: 'Private on-device, safe in the cloud.',
    body: 'Runs local by default. When you opt into a cloud model, Secure Relay tokenises every identifier first, so the cloud sees opaque tokens, never your data.',
    note: 'local first · detect · redact · reverse',
  },
  {
    id: 'green',
    tag: '05 · GREEN & SCALABLE',
    title: 'Small models, big reach.',
    body: 'Open models sized for your hardware use a fraction of the power of a datacenter and cost nothing per token. Add machines to scale out; each runs its own Orbix.',
    note: 'low energy · no per-token fees · scales per device',
  },
  {
    id: 'endpoint',
    tag: '06 · OPEN ENDPOINT',
    title: 'Reach Orbix across your network.',
    body: 'An OpenAI-compatible endpoint and a command-line tool let your phone or another laptop chat with the models on your machine. Your computer becomes the server.',
    note: 'orbix cli · /v1 endpoint · lan discovery',
  },
];

function ModuleIndex() {
  return (
    <section className="how modules-section" id="modules">
      <div className="how-header">
        <div className="section-eyebrow mono">
          <span className="eyebrow-dot" /> WHAT IT DOES
        </div>
        <h2 className="section-title">
          Six parts, <span className="section-title-soft">one free app.</span>
        </h2>
        <p className="how-intro">
          Orbix Core is one desktop app made of parts that fit together: the
          assistant, reusable workflows, connectors, security, and an open
          endpoint. Use one, or wire them into a whole process.
        </p>
      </div>

      <div className="modules-grid">
        {MODULES.map((m) => (
          <article key={m.id} className="module-card">
            <div className="module-tag mono">{m.tag}</div>
            <h3 className="module-title">{m.title}</h3>
            <p className="module-body">{m.body}</p>
            <div className="module-note mono">{m.note}</div>
          </article>
        ))}
      </div>
    </section>
  );
}

// ============================================================
// DIAGRAM, CSS-rendered, animates by `active`
// ============================================================
function Diagram({ active }) {
  return (
    <div className="diagram">
      <div className="diagram-frame mono">
        <span>orbix.dag</span>
        <span className="status-dot" /> running
      </div>
      <div className="diagram-canvas">
        {/* Device */}
        <div className={`d-node d-device ${active >= 0 ? 'on' : ''}`}>
          <div className="d-node-label mono">your mac</div>
          <div className="d-node-inner">
            <div className="d-chip">
              <div className="d-chip-grid">
                {Array.from({ length: 16 }).map((_, i) => (
                  <span key={i} style={{ animationDelay: `${i * 60}ms` }} />
                ))}
              </div>
              <div className="d-chip-label mono">M-series · metal</div>
            </div>
          </div>
        </div>

        {/* Bob orchestrator */}
        <div className={`d-node d-bob ${active >= 1 ? 'on' : ''}`}>
          <div className="d-node-label mono">bob</div>
          <div className="d-orb">
            <div className="d-orb-ring r1" />
            <div className="d-orb-ring r2" />
            <div className="d-orb-ring r3" />
            <div className="d-orb-core" />
          </div>
          <div className="d-agents mono">
            <span className={active >= 1 ? 'a-on' : ''}>· researcher</span>
            <span className={active >= 1 ? 'a-on' : ''} style={{ transitionDelay: '120ms' }}>· writer</span>
            <span className={active >= 1 ? 'a-on' : ''} style={{ transitionDelay: '240ms' }}>· reviewer</span>
          </div>
        </div>

        {/* Tools  -  apps + connectors Bob can reach */}
        <div className={`d-node d-relay ${active >= 2 ? 'on' : ''}`}>
          <div className="d-node-label mono">tools</div>
          <div className="d-relay-rows mono">
            <div className="rr"><span>app</span><span className="rr-arrow">·</span><span className="rr-token">gmail</span></div>
            <div className="rr"><span>app</span><span className="rr-arrow">·</span><span className="rr-token">slack</span></div>
            <div className="rr"><span>conn</span><span className="rr-arrow">·</span><span className="rr-token">github</span></div>
          </div>
        </div>

        {/* Models  -  local & cloud, routed by Bob */}
        <div className={`d-node d-cloud ${active >= 2 ? 'on' : ''}`}>
          <div className="d-node-label mono">models · routed</div>
          <div className="d-cloud-inner mono">
            <div className="d-cloud-pill">local</div>
            <div className="d-cloud-pill">custom</div>
            <div className="d-cloud-pill">cloud</div>
          </div>
        </div>

        {/* Ledger  -  every step recorded, replayable */}
        <div className={`d-node d-exec ${active >= 3 ? 'on' : ''}`}>
          <div className="d-node-label mono">ledger</div>
          <div className="d-exec-rows mono">
            <div className="er"><span className="er-check">✓</span><span>compose draft</span></div>
            <div className="er"><span className="er-check">✓</span><span>saved as workflow</span></div>
            <div className="er"><span className="er-pending">·</span><span>replay ready</span></div>
          </div>
        </div>

        {/* Wires */}
        <svg className="d-wires" viewBox="0 0 600 520" preserveAspectRatio="none" aria-hidden="true">
          <defs>
            <linearGradient id="wireGrad" x1="0" y1="0" x2="1" y2="0">
              <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.0" />
              <stop offset="50%" stopColor="var(--accent)" stopOpacity="0.9" />
              <stop offset="100%" stopColor="var(--accent)" stopOpacity="0.0" />
            </linearGradient>
          </defs>
          {/* device → bob */}
          <path d="M 130 130 C 200 130 220 260 300 260" className={`wire ${active >= 1 ? 'wire-on' : ''}`} />
          {/* bob → relay */}
          <path d="M 300 260 C 380 260 400 130 470 130" className={`wire ${active >= 2 ? 'wire-on' : ''}`} />
          {/* relay → cloud */}
          <path d="M 470 130 C 540 130 540 260 470 260" className={`wire ${active >= 2 ? 'wire-on' : ''}`} />
          {/* bob → exec */}
          <path d="M 300 260 C 380 260 400 400 470 400" className={`wire ${active >= 3 ? 'wire-on' : ''}`} />
          {/* device → exec direct */}
          <path d="M 130 130 C 130 400 300 400 470 400" className={`wire wire-faint ${active >= 3 ? 'wire-on' : ''}`} />
        </svg>
      </div>
      <div className="diagram-footer mono">
        <span>stage {String(active + 1).padStart(2, '0')}/04</span>
        <span className="diagram-progress">
          {STAGES.map((_, i) => (
            <span key={i} className={i <= active ? 'on' : ''} />
          ))}
        </span>
        <span>{STAGES[active]?.id}</span>
      </div>
    </div>
  );
}

// ============================================================
// MEET BOB  -  curated templates, each a real session
// ============================================================
const BOB_TEMPLATES = [
  {
    id: 'marketing',
    tag: 'GROWTH · MARKETING',
    prompt: 'draft our Q3 launch posts',
    plan: [
      'reading your brand voice from memory',
      'drafting the workflow: research, write, review',
      'ready to preview',
    ],
    dryPass: [
      { who: 'sys', text: 'runs in a sandbox, on a copy of your data' },
      { who: 'bob', text: 'Research · gathering recent, relevant sources' },
      { who: 'sys', text: 'nothing is stored outside your machine' },
      { who: 'bob', text: 'Write · drafting posts in your brand voice' },
      { who: 'bob', text: 'Review · checking tone, flagging anything unverified' },
      { who: 'sys', text: 'would publish or send only after you approve' },
      { who: 'bob', text: 'drafts are ready for your review. save as a workflow?' },
    ],
    committed: 'drafts saved · nothing sent · saved as a reusable workflow · audit log written',
  },
  {
    id: 'expenses',
    tag: 'FINANCE · EXPENSES',
    prompt: 'sort last quarter’s expenses',
    plan: [
      'connecting your accounts, read-only',
      'drafting the workflow: categorise, check, report',
      'ready to preview',
    ],
    dryPass: [
      { who: 'sys', text: 'runs in a sandbox, on a copy of your data' },
      { who: 'bob', text: 'Categorise · matching transactions to your categories' },
      { who: 'bob', text: 'Check · flagging anything unusual for your review' },
      { who: 'bob', text: 'Report · drafting a plain summary' },
      { who: 'sys', text: 'read-only · no payments · nothing changed' },
      { who: 'bob', text: 'a few need your call. save as a workflow?' },
    ],
    committed: 'summary drafted · flagged items queued for you · saved as a reusable workflow',
  },
  {
    id: 'ar',
    tag: 'FINANCE · COLLECTIONS',
    prompt: 'chase overdue invoices',
    plan: [
      'finding your open invoices',
      'drafting the workflow: look up, set tone, write',
      'ready to preview',
    ],
    dryPass: [
      { who: 'sys', text: 'runs in a sandbox, on a copy of your data' },
      { who: 'bob', text: 'Look up · finding overdue invoices' },
      { who: 'bob', text: 'Tone · matching the reminder to how overdue each is' },
      { who: 'bob', text: 'Write · drafting one email per customer' },
      { who: 'sys', text: 'would send only after you approve' },
      { who: 'bob', text: 'emails are staged for your sign-off. save as a workflow?' },
    ],
    committed: 'emails staged · awaiting your sign-off · saved as a reusable workflow',
  },
  {
    id: 'support',
    tag: 'OPS · SUPPORT',
    prompt: 'triage the support inbox',
    plan: [
      'connecting your inbox, read-only',
      'drafting the workflow: classify, draft, route',
      'ready to preview',
    ],
    dryPass: [
      { who: 'sys', text: 'runs in a sandbox, on a copy of your data' },
      { who: 'bob', text: 'Classify · sorting tickets by type and urgency' },
      { who: 'bob', text: 'Draft · writing replies from your knowledge base' },
      { who: 'bob', text: 'Route · sending anything sensitive to a human' },
      { who: 'sys', text: 'would reply only after you approve' },
      { who: 'bob', text: 'replies are drafted. save as a workflow?' },
    ],
    committed: 'replies drafted · escalations queued · saved as a reusable workflow',
  },
];

function MeetBob() {
  const [submitted, setSubmitted] = useState(null);

  const submit = (template) => setSubmitted(template);

  return (
    <section className="bob" id="bob">
      <div className="bob-grid">
        <div className="bob-left">
          <div className="section-eyebrow mono">
            <span className="eyebrow-dot" /> MEET BOB
          </div>
          <h2 className="section-title">
            Tell Bob what you need.<br />
            <span className="section-title-soft">He does the work.</span>
          </h2>
          <p className="section-body">
            Bob is the assistant at the centre of Orbix Core. Ask in plain
            language and he reads your files, drafts the work, and runs the steps.
            Turn any task into a workflow you can reuse. It all runs on your
            machine.
          </p>

          <ul className="bob-bullets">
            <li>
              <span className="bullet-num mono">01</span>
              <div>
                <strong>Plain language, no prompt tricks.</strong>
                <span>Say what you want. Bob handles files, images, and PDFs.</span>
              </div>
            </li>
            <li>
              <span className="bullet-num mono">02</span>
              <div>
                <strong>Runs on your own machine.</strong>
                <span>Local models. Your conversations never become training data.</span>
              </div>
            </li>
            <li>
              <span className="bullet-num mono">03</span>
              <div>
                <strong>You stay in control.</strong>
                <span>He shows the plan first. Approve it, save it, reuse it.</span>
              </div>
            </li>
          </ul>
        </div>

        <div className="bob-right">
          <div className="bob-card">
            <div className="bob-card-bar mono">
              <span className="status-dot" /> bob · listening
              <span className="bob-card-spacer" />
              <span>local · 0 tokens</span>
            </div>
            <div className="bob-card-body">
              {submitted ? (
                <BobResponse template={submitted} onReset={() => setSubmitted(null)} />
              ) : (
                <>
                  <div className="bob-prompt-label mono">pick a template</div>
                  <div className="bob-templates">
                    {BOB_TEMPLATES.map((t) => (
                      <button
                        key={t.id}
                        className="bob-template"
                        onClick={() => submit(t)}
                      >
                        <span className="bob-template-tag mono">{t.tag}</span>
                        <span className="bob-template-prompt">{t.prompt}</span>
                      </button>
                    ))}
                  </div>
                </>
              )}
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

function BobResponse({ template, onReset }) {
  const stages = template.plan;
  const dryStages = template.dryPass;
  const [stage, setStage] = useState(0);
  const [phase, setPhase] = useState('plan'); // plan | dry | committed
  const [dryStage, setDryStage] = useState(0);

  useEffect(() => {
    if (phase !== 'plan') return;
    if (stage >= stages.length - 1) return;
    const t = setTimeout(() => setStage((s) => s + 1), 600);
    return () => clearTimeout(t);
  }, [stage, phase, stages.length]);
  useEffect(() => {
    if (phase !== 'dry') return;
    if (dryStage >= dryStages.length) return;
    const t = setTimeout(() => setDryStage((s) => s + 1), 520);
    return () => clearTimeout(t);
  }, [dryStage, phase, dryStages.length]);

  const runDryPass = () => { setPhase('dry'); setDryStage(0); };
  const commit = () => setPhase('committed');

  const dryPrefix = (who) => {
    if (who === 'bob') return <span className="t-prefix t-bob">bob ›</span>;
    return <span className="t-prefix t-sys">sys ·</span>;
  };

  return (
    <div className="bob-response mono">
      <div className="br-line br-you">
        <span className="t-prefix t-you">you ›</span>
        <span>{template.prompt}</span>
      </div>
      <div className="br-line br-bob">
        <span className="t-prefix t-bob">bob ›</span>
        <span>understood. designing now.</span>
      </div>
      <div className="br-stages">
        {stages.map((s, i) => (
          <div key={s} className={`br-stage ${i <= stage ? 'on' : ''} ${i === stage && i < stages.length - 1 ? 'active' : ''}`}>
            <span className="br-stage-mark">
              {i < stage ? '✓' : i === stage ? '·' : '○'}
            </span>
            <span>{s}</span>
            {i === stage && i < stages.length - 1 && <span className="br-stage-dots"><span /><span /><span /></span>}
          </div>
        ))}
      </div>
      {phase === 'plan' && stage >= stages.length - 1 && (
        <div className="br-actions">
          <button className="t-btn t-btn-primary" onClick={runDryPass}>▶ run dry pass</button>
          <button className="t-btn" onClick={onReset}>↻ pick another</button>
        </div>
      )}

      {(phase === 'dry' || phase === 'committed') && (
        <div className="br-dry-log">
          {dryStages.slice(0, phase === 'committed' ? dryStages.length : dryStage).map((line, i) => (
            <div key={i} className={`br-dry-line ${line.who === 'bob' ? 'br-dry-bob' : 'br-dry-sys'}`}>
              {dryPrefix(line.who)}
              <span>{line.text}</span>
            </div>
          ))}
          {phase === 'dry' && dryStage < dryStages.length && (
            <div className="br-dry-line br-dry-sys br-dry-typing">
              <span className="t-prefix t-sys">sys ·</span>
              <span className="br-stage-dots"><span /><span /><span /></span>
            </div>
          )}
        </div>
      )}

      {phase === 'dry' && dryStage >= dryStages.length && (
        <div className="br-actions">
          <button className="t-btn t-btn-primary" onClick={commit}>commit</button>
          <button className="t-btn" onClick={onReset}>discard</button>
        </div>
      )}

      {phase === 'committed' && (
        <>
          <div className="br-line br-bob br-committed">
            <span className="t-prefix t-bob">bob ›</span>
            <span>{template.committed}</span>
          </div>
          <div className="br-actions">
            <button className="t-btn" onClick={onReset}>↻ pick another</button>
          </div>
        </>
      )}
    </div>
  );
}

// ============================================================
// CTA + FOOTER
// ============================================================
function CTA() {
  const user = window.__orbixUser ? window.__orbixUser() : null;
  const canDownload = !!(user && user.license_key);
  return (
    <section className="cta" id="access">
      <div className="cta-frame">
        <div className="cta-eyebrow mono">// EARLY ACCESS</div>
        <h2 className="cta-title">Run AI on your terms.</h2>
        <p className="cta-sub">Free to use. Runs on your own machine. Use the cloud only when you choose.</p>
        <div className="cta-actions">
          <a
            href="/contact"
            className="btn-primary btn-lg"
            onClick={(e) => { e.preventDefault(); go('contact'); }}
          >
            Get Early Access <span className="btn-arrow">→</span>
          </a>
          {canDownload ? (
            <a href="/api/download" className="btn-ghost mono">Download orbixcore.dmg</a>
          ) : (
            <a
              href="/contact"
              className="btn-ghost mono"
              onClick={(e) => { e.preventDefault(); go('contact'); }}
              title="Sign in and activate a license to download"
            >Download orbixcore.dmg</a>
          )}
        </div>
        <div className="cta-meta mono">
          <span>macos & windows · runs on your gpu</span>
          <span>·</span>
          <span>no telemetry</span>
          <span>·</span>
          <span>eu boundary ready</span>
        </div>
      </div>
    </section>
  );
}

function Footer() {
  const navLink = (id, label) => (
    <a href={`/${id}`} onClick={(e) => { e.preventDefault(); go(id); }}>{label}</a>
  );
  return (
    <footer className="footer">
      <div className="footer-inner">
        <div className="footer-brand">
          <a className="brand" href="/" onClick={(e) => { e.preventDefault(); go('home'); }}>
            <img className="brand-mark" src="/images/orbix-logo.png?v=2026-05-16a" width="28" height="28" alt="" aria-hidden="true" />
            <span className="brand-name">Orbix Core</span>
          </a>
          <span className="footer-tag mono">© 2026 · local-first by design</span>
          <span className="footer-tag mono">CVR 46295935</span>
        </div>
        <div className="footer-cols">
          <div>
            <div className="footer-col-title mono">PRODUCT</div>
            {navLink('vision', 'Vision')}
            {navLink('products', 'Products')}
            {navLink('pricing', 'Pricing')}
          </div>
          <div>
            <div className="footer-col-title mono">COMPANY</div>
            {navLink('about', 'About')}
            {navLink('contact', 'Contact')}
            {navLink('cookies', 'Cookies')}
            {navLink('data', 'Data policy')}
          </div>
          <div>
            <div className="footer-col-title mono">RESOURCES</div>
            <a href="/api/download">Download</a>
            {navLink('roadmap', 'Roadmap')}
            <a href="mailto:orbie@orbixcore.ai">Support</a>
          </div>
        </div>
      </div>
    </footer>
  );
}

// ============================================================
// APP
// ============================================================
const API_BASE = (window.__ORBIX && window.__ORBIX.API_BASE) || '';

function App() {
  const [theme, setTheme] = useState(() => {
    try { return localStorage.getItem('orbix-theme') || 'light'; } catch (e) { return 'light'; }
  });
  const [user, setUser] = useState(null);
  const [authMode, setAuthMode] = useState(null); // 'signin' | 'signup' | null

  useEffect(() => {
    document.documentElement.dataset.theme = theme;
    try { localStorage.setItem('orbix-theme', theme); } catch (e) {}
  }, [theme]);

  // Resume session from token
  useEffect(() => {
    const token = (() => { try { return localStorage.getItem('orbix-token'); } catch (e) { return null; } })();
    if (!token) return;
    let cancelled = false;
    fetch(API_BASE + '/api/auth/me', { headers: { 'Authorization': 'Bearer ' + token } })
      .then((r) => r.ok ? r.json() : null)
      .then((data) => {
        if (cancelled) return;
        if (data && data.email) setUser(data);
        else { try { localStorage.removeItem('orbix-token'); } catch (e) {} }
      })
      .catch(() => { try { localStorage.removeItem('orbix-token'); } catch (e) {} });
    return () => { cancelled = true; };
  }, []);

  // Expose user for non-React reads (CTA download check, etc.)
  useEffect(() => { window.__orbixUser = () => user; }, [user]);

  const signOut = () => {
    try { localStorage.removeItem('orbix-token'); } catch (e) {}
    setUser(null);
  };

  const toggleTheme = () => setTheme((t) => (t === 'light' ? 'dark' : 'light'));
  const route = useRoute();

  const renderPage = () => {
    switch (route) {
      case 'vision': return <window.VisionPage />;
      case 'products': return <window.ProductsPage />;
      case 'pricing': return <window.PricingPage user={user} />;
      case 'about': return <window.AboutPage />;
      case 'contact': return <window.ContactPage />;
      case 'cookies': return <window.CookiesPage />;
      case 'data': return <window.DataPolicyPage />;
      case 'roadmap': return <window.RoadmapPage />;
      default:
        return (
          <>
            <Hero />
            <HowItWorks />
            <ModuleIndex />
            <MeetBob />
            <CTA />
          </>
        );
    }
  };

  return (
    <>
      <Nav
        theme={theme}
        onToggleTheme={toggleTheme}
        route={route}
        user={user}
        onSignIn={() => setAuthMode('signin')}
        onSignUp={() => setAuthMode('signup')}
        onSignOut={signOut}
      />
      <main key={route}>
        {renderPage()}
      </main>
      <Footer />
      {authMode && window.AuthModal && (
        <window.AuthModal
          mode={authMode}
          apiBase={API_BASE}
          onClose={() => setAuthMode(null)}
          onSwitch={() => setAuthMode((m) => (m === 'signin' ? 'signup' : 'signin'))}
          onSuccess={(u) => { setUser(u); setAuthMode(null); }}
        />
      )}
    </>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
