/* global React, ReactDOM */
const { useState, useEffect, useRef, useCallback } = React;

/* ------------------------------------------------------------------
 *  sendMessage — calls the Vercel API endpoint.
 *  Returns Promise<{ lines, followUps, digressions, preview? }>
 * ------------------------------------------------------------------ */
async function sendMessage(text, history = []) {
  const res = await fetch('/api/chat', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ message: text, history }),
  });
  if (!res.ok) {
    return {
      lines: [{ kind: 'aside', text: 'something went wrong on my end — try again in a moment.' }],
      followUps: [],
      digressions: [],
    };
  }
  return res.json();
}

/* ============================================================
 *  Static "filesystem" — what `ls`, `cat`, `find` operate on
 * ============================================================ */
const PROJECTS = [
  { name: 'beam-ai.md',          status: '●', date: '2023–2024', size: '12K', blurb: 'AI-native estimating — 88% adoption, $2.6B+ GMV, $52M acquisition' },
  { name: 'beam-foundations.md', status: '●', date: '2023–2024', size: '8K',  blurb: 'Design systems & core platform thinking across Beam\'s financial suite' },
  { name: 'prism.md',            status: '●', date: '2019–2021', size: '10K', blurb: 'Modular analytics for traders at D.E. Shaw — 60% faster insights' },
  { name: 'vgs-onboarding.md',   status: '●', date: '2022',      size: '6K',  blurb: 'Developer onboarding redesign — 4.5% → 95% completion rate' },
  { name: 'research.md',         status: '●', date: '2019, 2023', size: '4K', blurb: '2× published at ACM CHI — haptic interfaces & VR collaboration' },
];

const ESSAYS = [
  { name: 'vrgit-chi23.md',       date: '2023-04-23', size: '6.4K' },
  { name: 'haptipedia-chi19.md',  date: '2019-05-04', size: '8.2K' },
];

const CONTACT = {
  email:    'agarashu@umich.edu',
  linkedin: 'in/ashutoshagrawal',
  portfolio: 'ask and I\'ll send the link',
};

const WHOAMI = [
  'name           Ashutosh Agrawal',
  'alias          ash',
  'role           product designer',
  'focus          B2B SaaS · AI surfaces · developer tools',
  'current        Founding Designer at Beam → acquired by CompanyCam',
  'education      M.S. HCI, University of Michigan · B.Des, IIT Guwahati',
  'research       2× ACM CHI — cited 43+ and 91+ times',
  'code           40+ React / TypeScript PRs shipped',
  'based          San Francisco',
  'status         open to new opportunities',
];

const MAN = [
  'NAME',
  '    ashutosh — product designer',
  '',
  'SYNOPSIS',
  '    talk to ashutosh [question]',
  '    ls | cat <file> | cd <project> | find <term>',
  '    whoami | man ashutosh | top | contact | help | clear',
  '',
  'DESCRIPTION',
  '    Founding product designer at Beam (Accel Series A → $52M acquisition).',
  '    6+ years shipping B2B SaaS across fintech, developer tools, and',
  '    enterprise workflows. Deep background in AI-native product design.',
  '',
  '    Bridges design, strategy, and code — 40+ React/TypeScript PRs alongside',
  '    the design work. M.S. HCI from Michigan, B.Des from IIT Guwahati.',
  '    Published at ACM CHI twice.',
  '',
  'WORK',
  '    Beam Finance (2023–present)    Founding Designer · 0→1→acquisition',
  '    D.E. Shaw & Co (2019–2021)     Senior UX Designer · enterprise tooling',
  '    Very Good Security (2022)      Product Design Intern',
  '',
  'TYPICAL ASKS',
  '    "tell me about beam"           → cat beam-ai.md',
  '    "how do you approach AI?"      → just ask',
  '    "what are you looking for?"    → top',
  '',
  'SEE ALSO',
  '    ls, cat beam-ai.md, top, contact',
];

const TOP = [
  'currently thinking about:',
  '  · prompt design as a product surface — not an implementation detail',
  '  · what AI co-creation actually means for accuracy-sensitive workflows',
  '  · design systems that enable velocity without a dedicated DS team',
  '',
  'open to conversations about:',
  '  · senior IC / founding designer roles at AI-focused companies',
  '  · B2B SaaS product teams where design has real strategic weight',
  '  · anything at the intersection of AI, trust, and complex work',
];

const PROJECT_BODIES = {
  'beam-ai.md': [
    { kind: 'h',    text: 'beam ai — ai-native estimating' },
    { kind: 'raw',  text: 'role        Founding Designer · 0→1→scale' },
    { kind: 'raw',  text: 'year        2023–2024' },
    { kind: 'raw',  text: 'outcome     $1M+ ARR · 88% adoption · $52M acquisition' },
    { kind: 'blank' },
    { kind: 'p',    text: 'Contractors spend hours manually translating project scope into estimate line items. I identified that bottleneck, prototyped an AI solution with Cursor before any eng investment, and designed an end-to-end experience that generates structured estimates from unstructured inputs — notes, photos, plans — modeled on each contractor\'s past work.' },
    { kind: 'p',    text: 'The hard part wasn\'t the AI. It was making the output trustworthy. I built inline diff views, explicit assumption surfacing, and a co-creation layout so users stayed in control while the AI did the heavy lifting.' },
    { kind: 'blank' },
    { kind: 'h2',   text: 'outcomes' },
    { kind: 'li',   text: '88% of SaaS customers adopted AI estimating' },
    { kind: 'li',   text: '$2.6B+ in estimates generated via AI' },
    { kind: 'li',   text: '3× faster estimate creation (user-reported)' },
    { kind: 'li',   text: 'Monthly payment volume: $10M → $20M' },
    { kind: 'li',   text: '$1M+ ARR in 9 months → contributed to $52M acquisition' },
    { kind: 'blank' },
    { kind: 'h2',   text: 'takeaways' },
    { kind: 'li',   text: 'Start with the right problem, not the technology' },
    { kind: 'li',   text: 'Prompt design is a product surface — treat it with the same rigor as UI' },
    { kind: 'li',   text: 'Design for trust from day one, especially in accuracy-sensitive workflows' },
  ],

  'beam-foundations.md': [
    { kind: 'h',    text: 'beam foundations — designing for scale' },
    { kind: 'raw',  text: 'role        Founding Designer' },
    { kind: 'raw',  text: 'year        2023–2024' },
    { kind: 'blank' },
    { kind: 'p',    text: 'As Beam expanded from AI estimating into invoicing, expenses, contracts, and budgeting, the challenge shifted from building features to maintaining coherence across a growing product surface — without slowing velocity.' },
    { kind: 'p',    text: 'Three principles guided the work: standardize what repeats, connect what belongs together, find small wins with big impact.' },
    { kind: 'blank' },
    { kind: 'h2',   text: 'what shipped' },
    { kind: 'li',   text: 'detail page pattern that scaled across all product areas — predictable nav, consistent actions' },
    { kind: 'li',   text: 'unified spend management IA across contracts, billing, expenses, and budgets' },
    { kind: 'li',   text: 'auto-contract creation to remove friction from the invoicing path — no costly backend rewrite' },
  ],

  'prism.md': [
    { kind: 'h',    text: 'prism — modular analytics for traders' },
    { kind: 'raw',  text: 'role        Lead Product Designer' },
    { kind: 'raw',  text: 'year        2019–2021 · D.E. Shaw & Co' },
    { kind: 'raw',  text: 'firm        $65B AUM global investment and technology firm' },
    { kind: 'blank' },
    { kind: 'p',    text: 'Traders needed to configure dashboards across multiple datasets in high-pressure trading hours, but the existing pivot component was unscalable and painful to use. I designed Prism: a modular analytics system that could be embedded into existing internal tools or extended into full applications.' },
    { kind: 'p',    text: 'Core design principle: flexibility. Every trader has unique needs and multiple contexts of use, so every component offered high customizability and multiple ways to approach a task.' },
    { kind: 'blank' },
    { kind: 'h2',   text: 'outcomes' },
    { kind: 'li',   text: '8 of 13 business units adopted Prism at beta launch' },
    { kind: 'li',   text: '60% faster insight generation' },
    { kind: 'li',   text: '95% ease-of-use score (SEQ)' },
    { kind: 'blank' },
    { kind: 'h2',   text: 'takeaways' },
    { kind: 'li',   text: 'Ask the right questions of stakeholders before designing' },
    { kind: 'li',   text: 'Design against the business backdrop, not just user needs' },
    { kind: 'li',   text: 'Optimal UX, not necessarily the best — pragmatic decisions that work within real constraints' },
  ],

  'vgs-onboarding.md': [
    { kind: 'h',    text: 'vgs — developer onboarding redesign' },
    { kind: 'raw',  text: 'role        Product Design Intern' },
    { kind: 'raw',  text: 'year        2022 · Very Good Security (Series C)' },
    { kind: 'blank' },
    { kind: 'p',    text: 'VGS had a < 1% web conversion rate. The old onboarding had 13 tutorial steps plus 12 config steps before a developer could test the API — the actual moment of value. I reframed the whole problem: why do users have to graduate from VGS university?' },
    { kind: 'p',    text: 'The redesign collapsed it into a 5-step guided flow built around the Aha! moment. Progressive disclosure of jargon, status quo bias baked into defaults, and "Test API" as the primary action.' },
    { kind: 'blank' },
    { kind: 'h2',   text: 'outcomes' },
    { kind: 'li',   text: 'Completion rate: 4.5% → 95%' },
    { kind: 'li',   text: '50% faster time to Aha! moment' },
    { kind: 'blank' },
    { kind: 'h2',   text: 'takeaways' },
    { kind: 'li',   text: 'First-time UX is the highest-leverage surface for activation' },
    { kind: 'li',   text: 'Concise, actionable UX copy is a design asset in developer tools' },
    { kind: 'li',   text: 'Ship in a fast-paced env means getting alignment before building, not after' },
  ],

  'research.md': [
    { kind: 'h',    text: 'published research · ACM CHI' },
    { kind: 'blank' },
    { kind: 'h2',   text: 'vrgit — version control for vr collaboration (CHI \'23)' },
    { kind: 'raw',  text: 'citations   43+' },
    { kind: 'p',    text: 'Designed and built a version control system for collaborative content creation in virtual reality. Explored how branching and merging mental models transfer to spatial, embodied interactions.' },
    { kind: 'blank' },
    { kind: 'h2',   text: 'haptipedia — haptic device discovery (CHI \'19)' },
    { kind: 'raw',  text: 'citations   91+' },
    { kind: 'p',    text: 'A tool for accelerating haptic device discovery to support interaction and engineering design. Built a structured database of 105 devices with computational tools for designers and engineers to navigate the design space.' },
  ],

  'vrgit-chi23.md': [
    { kind: 'h',   text: 'vrgit — vr collaboration (CHI \'23)' },
    { kind: 'raw', text: 'venue       ACM CHI 2023' },
    { kind: 'raw', text: 'citations   43+' },
    { kind: 'blank' },
    { kind: 'p',   text: 'A version control system for collaborative content creation in virtual reality. Explores how git-style branching and merging mental models translate to spatial, embodied interactions.' },
  ],

  'haptipedia-chi19.md': [
    { kind: 'h',   text: 'haptipedia — haptic device discovery (CHI \'19)' },
    { kind: 'raw', text: 'venue       ACM CHI 2019' },
    { kind: 'raw', text: 'citations   91+' },
    { kind: 'blank' },
    { kind: 'p',   text: 'Accelerating haptic device discovery to support interaction and engineering design. Structured database of 105 grounded force-feedback devices with computational tools to navigate the design space.' },
  ],
};

/* ============================================================
 *  Suggestions — the 3 tab-cyclable starters on the empty state
 * ============================================================ */
const SUGGESTIONS = [
  'Tell me about your overall experience and background',
  'Walk me through a project you\'re proud of',
  'How are you using AI in your design process?',
  'How and why did you build this site?',
];

/* ============================================================
 *  Terminal gags — playful intercepts for shell commands
 * ============================================================ */
const TERMINAL_GAGS = {
  sudo:    "i'm not a real terminal. no root for you.",
  rm:      "absolutely not. this is a portfolio, not a disaster.",
  vim:     "i can't host your existential crisis. try asking a question instead.",
  emacs:   "vim is right there. ok kidding — i'm not a real terminal.",
  nano:    "nothing to edit here. i'm just ashutosh, in a box.",
  git:     "already cloned, see ↑. anything else i can show you?",
  npm:     "no node here, just thoughts. ask away.",
  yarn:    "not that kind of yarn. try asking a question.",
  pnpm:    "i'm not a real terminal.",
  python:  "no repl, just opinions. ask me one.",
  node:    "no runtime, only vibes. what do you want to know?",
  ssh:     "you're already in. this is the whole machine.",
  exit:    "you can close the tab whenever — but you'll miss me.",
  logout:  "you can close the tab whenever — but you'll miss me.",
  reboot:  "already booted, see ↑. ask me something instead.",
  kill:    "a little dark for a portfolio, no?",
  ps:      "one process running: me, thinking about your question.",
  curl:    "i don't fetch, i answer. try asking.",
  wget:    "i don't fetch, i answer. try asking.",
  pwd:     "~/somewhere-in-san-francisco",
  echo:    "echo back at you. now ask something real.",
  date:    new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }).toLowerCase(),
  uname:   "agarashu-clone (definitely not a real unix)",
  ping:    "pong. also not a real terminal.",
  brew:    "no homebrew here. but ashutosh does like good coffee.",
  code:    "you're already looking at the code. well, the person.",
  cursor:  "funny you should ask — ashutosh used cursor to prototype the AI estimating MVP.",
};

/* ============================================================
 *  Boot sequence
 * ============================================================ */
const BOOT = [
  { kind: 'type', text: 'gh clone ashutosh-agrawal', cls: 'boot-cmd',
    preBlink: 900, typeMs: 75, jitter: 60, postEnter: 480 },
  { kind: 'sys', text: "Cloning into 'ashutosh-agrawal'...", cls: 'boot-sys', delay: 120 },
  { kind: 'sys', text: 'remote: Enumerating objects: 312, done.', cls: 'boot-sys', delay: 360 },
  { kind: 'sys', text: 'remote: Counting objects: 100% (312/312), done.', cls: 'boot-sys', delay: 280 },
  { kind: 'sys', text: 'Receiving objects: 100% (312/312), 4.18 MiB | 6.20 MiB/s, done.', cls: 'boot-sys', delay: 380 },
  { kind: 'sys', text: 'Resolving deltas: 100% (89/89), done.', cls: 'boot-sys', delay: 240 },
  { kind: 'type', text: 'cd ask-me && ./hello', cls: 'boot-cmd',
    preBlink: 1100, typeMs: 80, jitter: 70, postEnter: 520 },
];

/* ============================================================
 *  App
 * ============================================================ */
function App() {
  const [bootDone,      setBootDone]      = useState(false);
  const [bootCollapsed, setBootCollapsed] = useState(false);
  const [bootLines,     setBootLines]     = useState([]);
  const [blocks,        setBlocks]        = useState([]);
  const [input,         setInput]         = useState('');
  const [pending,       setPending]       = useState(false);
  const [history,       setHistory]       = useState([]);
  const [histIdx,       setHistIdx]       = useState(-1);
  const [draft,         setDraft]         = useState('');
  const [tabIdx,        setTabIdx]        = useState(-1);
  const [preview,       setPreview]       = useState(null);

  const inputRef     = useRef(null);
  const scrollerRef  = useRef(null);
  const promptRowRef = useRef(null);
  // Conversation history for API context — stable ref, no re-renders
  const historyRef   = useRef([]);

  /* boot animation */
  useEffect(() => {
    let cancelled = false;
    const timers = [];
    const wait = (ms) => new Promise((r) => timers.push(setTimeout(r, ms)));

    async function run() {
      for (let i = 0; i < BOOT.length; i++) {
        if (cancelled) return;
        const line = BOOT[i];
        if (line.delay) await wait(line.delay);
        if (cancelled) return;

        if (line.kind === 'type') {
          setBootLines((cur) => [...cur, { ...line, partial: '', typing: true }]);
          if (line.preBlink) await wait(line.preBlink);
          if (cancelled) return;

          for (let j = 1; j <= line.text.length; j++) {
            if (cancelled) return;
            await wait((line.typeMs || 75) + (line.jitter || 0) * Math.random());
            setBootLines((cur) => {
              const next = cur.slice();
              const last = next[next.length - 1];
              next[next.length - 1] = { ...last, partial: line.text.slice(0, j) };
              return next;
            });
          }
          if (cancelled) return;
          await wait(line.postEnter || 400);
          if (cancelled) return;
          setBootLines((cur) => {
            const next = cur.slice();
            const last = next[next.length - 1];
            next[next.length - 1] = { ...last, typing: false };
            return next;
          });
        } else {
          setBootLines((cur) => [...cur, { ...line, partial: line.text }]);
        }
      }
      if (!cancelled) {
        setBootDone(true);
        await wait(500);
        if (!cancelled) setBootCollapsed(true);
      }
    }
    run();
    return () => { cancelled = true; timers.forEach(clearTimeout); };
  }, []);

  /* pin scroll to bottom during boot */
  useEffect(() => {
    if (bootCollapsed) return;
    const el = scrollerRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [bootLines, bootCollapsed]);

  /* scroll past boot once collapsed */
  useEffect(() => {
    if (!bootCollapsed) return;
    const el = scrollerRef.current;
    if (!el) return;
    requestAnimationFrame(() => {
      const intro = el.querySelector('.intro-card');
      if (!intro) return;
      const elRect    = el.getBoundingClientRect();
      const introRect = intro.getBoundingClientRect();
      el.scrollTo({ top: el.scrollTop + (introRect.top - elRect.top) - 28, behavior: 'smooth' });
    });
  }, [bootCollapsed]);

  /* scroll new block into view */
  useEffect(() => {
    if (blocks.length === 0) return;
    const el = scrollerRef.current;
    if (!el) return;
    const isFirst = blocks.length === 1;
    const t = setTimeout(() => {
      const all  = el.querySelectorAll('.block');
      const last = all[all.length - 1];
      if (!last) return;
      const elRect   = el.getBoundingClientRect();
      const lastRect = last.getBoundingClientRect();
      el.scrollTo({ top: el.scrollTop + (lastRect.top - elRect.top) - 28, behavior: 'smooth' });
    }, isFirst ? 720 : 50);
    return () => clearTimeout(t);
  }, [blocks.length]);

  /* keep focus on hidden input */
  useEffect(() => {
    if (!bootCollapsed) return;
    inputRef.current?.focus();
    const handler = (e) => {
      if (e.target.closest('a, button, .preview-pane')) return;
      inputRef.current?.focus();
    };
    document.addEventListener('click', handler);
    return () => document.removeEventListener('click', handler);
  }, [bootCollapsed]);

  /* ── submit ── */
  const submit = useCallback(async (raw) => {
    const cmd = raw.trim();
    if (!cmd || pending) return;

    setHistory((h) => [...h, cmd]);
    setHistIdx(-1);
    setDraft('');
    setTabIdx(-1);
    setInput('');

    if (cmd === 'clear' || cmd === '/clear') {
      setBlocks([]);
      return;
    }

    const block = { id: crypto.randomUUID(), cmd, lines: [], status: 'running' };
    setBlocks((bs) => [...bs, block]);

    const out = await runCommand(cmd, blocks);
    if (!out) return; // e.g. clear handled above

    setBlocks((bs) => bs.map((b) =>
      b.id === block.id
        ? { ...b, lines: out.lines || [], preview: out.preview,
            followUps: out.followUps, digressions: out.digressions, status: 'done' }
        : b
    ));
    if (out.preview) setPreview(out.preview);

    // persist conversation turn for API context
    if (out.lines && out.lines.some((l) => l.kind === 'answer' || l.kind === 'aside')) {
      historyRef.current = [
        ...historyRef.current,
        { role: 'user', content: cmd },
        { role: 'assistant', content: (out.lines || []).filter((l) => l.text).map((l) => l.text).join('\n') },
      ];
    }
  }, [pending]); // eslint-disable-line react-hooks/exhaustive-deps

  /* ── runCommand ── */
  async function runCommand(cmd, currentBlocks = []) {
    const verb = cmd.trim().split(/\s+/)[0].toLowerCase();

    // any shell-shaped input (single token or known shell verbs) → friendly redirect
    const isShellShaped = /^[a-z][a-z0-9\-_]{0,19}$/i.test(cmd.trim());
    const knownVerbs = ['ls','cat','cd','find','whoami','man','top','contact','help','open','grep','curl','wget','ssh','git','npm','sudo','rm','vim','nano','pwd','echo','ps','kill','ping','brew'];
    if (isShellShaped || knownVerbs.includes(verb)) {
      // carry forward suggestions from the most recent answered block,
      // or fall back to the static starters if this is the first interaction
      const prev = [...currentBlocks].reverse().find(
        (b) => b.status === 'done' && (b.followUps?.length || b.digressions?.length)
      );
      return {
        lines: [
          { kind: 'aside', text: "not a real terminal — but ash is working on making it more realistic 🙂" },
          { kind: 'aside', text: 'for now, just ask me something in plain english.' },
        ],
        followUps:   prev?.followUps   || [],
        digressions: prev?.digressions?.length ? prev.digressions : SUGGESTIONS,
      };
    }

    // free-form → LLM
    setPending(true);
    try {
      return await sendMessage(cmd, historyRef.current);
    } finally {
      setPending(false);
    }
  }

  /* active suggestions for tab cycling */
  const lastBlock        = blocks[blocks.length - 1];
  const isAnswered       = lastBlock && lastBlock.status === 'done' && !pending;
  const activeSuggestions = isAnswered
    ? [...(lastBlock.followUps || []), ...(lastBlock.digressions || [])]
    : blocks.length === 0 && !pending ? SUGGESTIONS : [];

  /* keyboard handler */
  const onKey = (e) => {
    if (e.key === 'Enter') {
      e.preventDefault();
      submit(input);
    } else if (e.key === 'Tab') {
      e.preventDefault();
      if (activeSuggestions.length === 0) return;
      const next = (tabIdx + 1) % activeSuggestions.length;
      setTabIdx(next);
      setInput(activeSuggestions[next]);
    } else if (e.key === 'ArrowUp') {
      if (history.length === 0) return;
      e.preventDefault();
      if (histIdx === -1) setDraft(input);
      const ni = histIdx === -1 ? history.length - 1 : Math.max(0, histIdx - 1);
      setHistIdx(ni);
      setInput(history[ni]);
    } else if (e.key === 'ArrowDown') {
      if (histIdx === -1) return;
      e.preventDefault();
      const ni = histIdx + 1;
      if (ni >= history.length) { setHistIdx(-1); setInput(draft); }
      else { setHistIdx(ni); setInput(history[ni]); }
    } else if (e.key === 'l' && (e.ctrlKey || e.metaKey)) {
      e.preventDefault();
      setBlocks([]);
    }
  };

  return (
    <div className={`term ${preview ? 'has-preview' : ''} ${bootCollapsed ? 'boot-collapsed' : ''}`}>
      <div className={`term-header ${blocks.length === 0 ? 'th-empty' : 'th-revealed'}`}>
        <span className="th-left">
          <span className="th-dot" />
          <span className="th-fade">
            <span className="th-name">ashutosh</span>
            <span className="th-sep"> · </span>
            <span className="th-role">product designer</span>
          </span>
        </span>
        <span className="th-right" />
      </div>

      <div className="term-body">
        <div className="scroller" ref={scrollerRef}>

          {/* boot scrollback */}
          <div className={`boot ${bootCollapsed ? 'is-collapsed' : ''}`}>
            {bootLines.map((b, i) => {
              if (b.kind === 'type') {
                const parts = b.partial.split(/(\s+)/);
                return (
                  <div key={i} className={`boot-line ${b.cls}`}>
                    <span className="boot-prompt">$ </span>
                    <span>
                      {parts.map((p, j) =>
                        j === 0
                          ? <span key={j} className="tok-git-verb">{p}</span>
                          : <span key={j}>{p}</span>
                      )}
                    </span>
                    {b.typing && <span className="boot-cursor" />}
                  </div>
                );
              }
              const sysText = b.partial || b.text || ' ';
              const m = sysText.match(/^(remote: |Cloning into |Receiving objects: |Resolving deltas: )(.*)/);
              if (m) {
                return (
                  <div key={i} className={`boot-line ${b.cls}`}>
                    <span className="tok-git-prefix">{m[1]}</span>
                    <span dangerouslySetInnerHTML={{ __html: m[2]
                      .replace(/(\d+%)/g, '<span class="tok-git-pct">$1</span>')
                      .replace(/done\./g, '<span class="tok-git-done">done.</span>') }} />
                  </div>
                );
              }
              return <div key={i} className={`boot-line ${b.cls}`}>{sysText}</div>;
            })}
          </div>

          {/* intro card */}
          {bootCollapsed && (
            <div className="intro-card" role="region" aria-label="welcome">
              <div className="ic-mark" aria-hidden="true">
                <pre className="ic-glasses">{`  ╭───╮ ╭───╮\n  │ `}<span className="ic-eye">●</span>{` │─│ `}<span className="ic-eye">●</span>{` │\n  ╰───╯ ╰───╯`}</pre>
              </div>
              <div className="ic-id">
                <div className="ic-id-title">Ashutosh Agrawal v0.1</div>
                <div className="ic-id-line ic-id-dim">Product designer · San Francisco</div>
              </div>
            </div>
          )}

          {/* command blocks */}
          {blocks.map((b) => <Block key={b.id} block={b} />)}

          {/* live prompt */}
          {bootCollapsed && !pending && (
            <div className="prompt-row" ref={promptRowRef}>
              <Prompt />
              <div className="prompt-input-row">
                <span className="prompt-input">{input}<Cursor pending={pending} /></span>
                {input.trim().length > 0 && (
                  <button className="send-btn" onClick={() => submit(input)} aria-label="Send">
                    <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
                      <path d="M7 12V2M7 2L3 6M7 2L11 6" stroke="#100E0B" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
                    </svg>
                  </button>
                )}
              </div>
            </div>
          )}

          {/* follow-up / digress chips */}
          {(() => {
            const last = blocks[blocks.length - 1];
            if (!last || last.status !== 'done' || pending) return null;
            const fu = last.followUps   || [];
            const dg = last.digressions || [];
            if (fu.length === 0 && dg.length === 0) return null;
            return (
              <div className="next-up">
                {fu.length > 0 && (
                  <div className="nu-group">
                    <div className="nu-label">// follow up</div>
                    <div className="nu-chips">
                      {fu.map((q, i) => (
                        <button key={`fu-${i}`} className={`sugg ${tabIdx === i ? 'is-tab' : ''}`}
                          onClick={() => submit(q)} tabIndex={-1}>{q}</button>
                      ))}
                    </div>
                  </div>
                )}
                {dg.length > 0 && (
                  <div className="nu-group">
                    <div className="nu-label">// digress</div>
                    <div className="nu-chips">
                      {dg.map((q, i) => (
                        <button key={`dg-${i}`} className={`sugg ${tabIdx === fu.length + i ? 'is-tab' : ''}`}
                          onClick={() => submit(q)} tabIndex={-1}>{q}</button>
                      ))}
                    </div>
                  </div>
                )}
                <span className="sugg-hint"><kbd>tab</kbd> to cycle</span>
              </div>
            );
          })()}

          {/* welcome suggestions */}
          {bootCollapsed && blocks.length === 0 && !pending && (
            <div className="suggestions">
              {SUGGESTIONS.map((s, i) => (
                <button key={s} className={`sugg ${tabIdx === i ? 'is-tab' : ''}`}
                  onClick={() => { setInput(s); inputRef.current?.focus(); }}
                  tabIndex={-1}>{s}</button>
              ))}
              <span className="sugg-hint"><kbd>tab</kbd> to cycle</span>
            </div>
          )}

          {bootCollapsed && <div className="bottom-spacer" aria-hidden="true" />}
        </div>

        {/* preview pane */}
        {preview && (
          <aside className="preview-pane">
            <div className="pp-bar">
              <span className="pp-mono">preview</span>
              <button className="pp-close" onClick={() => setPreview(null)} aria-label="close preview">esc</button>
            </div>
            <div className="pp-frame">
              {preview.src
                ? <img src={preview.src} alt={preview.alt || ''} />
                : <div className="pp-placeholder" aria-hidden="true" />}
            </div>
            <div className="pp-cap">{preview.caption}</div>
          </aside>
        )}
      </div>

      <input
        ref={inputRef}
        className="hidden-input"
        value={input}
        onChange={(e) => { setInput(e.target.value); setTabIdx(-1); }}
        onKeyDown={onKey}
        autoFocus
        spellCheck={false}
        autoComplete="off"
        autoCorrect="off"
        autoCapitalize="off"
        aria-label="Ask Ashutosh anything"
      />
    </div>
  );
}

/* ============================================================
 *  Sub-components
 * ============================================================ */
function Prompt() {
  return (
    <span className="prompt">
      <span className="prompt-user">visitor</span>
      <span className="prompt-at">@</span>
      <span className="prompt-host">agarashu</span>
      <span className="prompt-path"> ask-me</span>
      <span className="prompt-sigil"> %</span>
      <span> </span>
    </span>
  );
}

function Cursor({ pending }) {
  if (pending) return <span className="cursor-pulse" aria-hidden="true" />;
  return <span className="cursor-block" aria-hidden="true" />;
}

function Block({ block }) {
  return (
    <div className="block">
      <div className="block-cmd-row">
        <Prompt />
        <span className="block-cmd">{block.cmd}</span>
      </div>
      <div className="block-out">
        {block.lines.map((l, i) => <Line key={i} line={l} />)}
        {block.status === 'running' && (
          <div className="out out-pending">Articulating…</div>
        )}
      </div>
    </div>
  );
}

function Line({ line }) {
  if (line.kind === 'blank')   return <div className="out out-blank">&nbsp;</div>;
  if (line.kind === 'h')       return <div className="out out-h">▎{line.text}</div>;
  if (line.kind === 'h2')      return <div className="out out-h2">{line.text}</div>;
  if (line.kind === 'li')      return <div className="out out-li">  · {line.text}</div>;
  return <div className={`out out-${line.kind || 'raw'}`}>{line.text}</div>;
}

/* ============================================================
 *  Command output builders
 * ============================================================ */
function lsLines() {
  const rows = [];
  rows.push({ kind: 'raw', text: `total ${PROJECTS.length + ESSAYS.length}` });
  rows.push({ kind: 'blank' });
  rows.push({ kind: 'raw', text: 'drwx──  projects/' });
  PROJECTS.forEach((p) => {
    rows.push({ kind: 'raw', text: `  ${p.status}  ${pad(p.size, 5)}  ${pad(p.date, 9)}  ${pad(p.name, 24)}  ${p.blurb}` });
  });
  rows.push({ kind: 'blank' });
  rows.push({ kind: 'raw', text: 'drwx──  research/' });
  ESSAYS.forEach((e) => {
    rows.push({ kind: 'raw', text: `  ●  ${pad(e.size, 5)}  ${e.date}  ${pad(e.name, 24)}` });
  });
  rows.push({ kind: 'blank' });
  rows.push({ kind: 'raw', text: '-rw──── contact.txt' });
  rows.push({ kind: 'raw', text: '-rw──── philosophy.md' });
  rows.push({ kind: 'blank' });
  rows.push({ kind: 'raw', text: '● shipped   ◐ in flight   ○ archived' });
  return rows;
}

function pad(s, n) {
  s = String(s);
  return s.length >= n ? s : s + ' '.repeat(n - s.length);
}

function catLines(name) {
  if (!name) return { lines: [{ kind: 'raw', text: "cat: missing operand. try `ls` to see what's here." }] };
  const body = PROJECT_BODIES[name];
  if (body) {
    const project = PROJECTS.find((p) => p.name === name);
    return {
      lines: body,
      preview: project ? { src: null, alt: project.name, caption: `// ${project.name} · ${project.date}` } : undefined,
    };
  }
  if (name === 'contact.txt') return { lines: contactLines() };
  if (name === 'philosophy.md') {
    return { lines: [
      { kind: 'h',     text: 'philosophy' },
      { kind: 'blank' },
      { kind: 'p',     text: 'The system is the product. The screen is a moment in a flow. The flow is the unit of design.' },
      { kind: 'p',     text: 'Ship the boring states. Empty, error, loading, edge — these are where an interface earns trust.' },
      { kind: 'p',     text: 'Prototype in code when the question is "how does it feel." Most questions worth asking are.' },
      { kind: 'blank' },
      { kind: 'p',     text: 'Start with the right problem, not the technology — even when the tech is exciting.' },
      { kind: 'p',     text: 'Prompt design is a product surface. Design for trust from day one.' },
    ]};
  }
  return { lines: [{ kind: 'raw', text: `cat: ${name}: no such file. try \`ls\`.` }] };
}

function cdLines(name) {
  if (!name) return [{ kind: 'raw', text: 'cd: missing operand.' }];
  const project = PROJECTS.find((p) => p.name === name || p.name.replace('.md', '') === name);
  if (project) return [
    { kind: 'raw', text: `entering ${project.name}…` },
    { kind: 'raw', text: `(tip: \`cat ${project.name}\` for the writeup)` },
  ];
  return [{ kind: 'raw', text: `cd: ${name}: no such project.` }];
}

function findLines(term) {
  if (!term) return [{ kind: 'raw', text: 'find: missing operand.' }];
  const hits = [];
  const t = term.toLowerCase();
  PROJECTS.forEach((p) => {
    if (p.name.toLowerCase().includes(t) || p.blurb.toLowerCase().includes(t)) {
      hits.push({ kind: 'raw', text: `projects/${p.name}    ${p.blurb}` });
    }
  });
  ESSAYS.forEach((e) => {
    if (e.name.toLowerCase().includes(t)) hits.push({ kind: 'raw', text: `research/${e.name}` });
  });
  if (hits.length === 0) return [{ kind: 'raw', text: `find: no matches for "${term}". try a broader term, or just ask.` }];
  return hits;
}

function helpLines() {
  return [
    { kind: 'raw',  text: 'ashutosh — talk to a clone of ashutosh' },
    { kind: 'blank' },
    { kind: 'raw',  text: '  ls               list projects and research' },
    { kind: 'raw',  text: '  cat <file>       open a project writeup' },
    { kind: 'raw',  text: '  cd <project>     focus on a project' },
    { kind: 'raw',  text: '  find <term>      search by keyword' },
    { kind: 'raw',  text: '  whoami           background & facts' },
    { kind: 'raw',  text: '  man ashutosh     long-form intro' },
    { kind: 'raw',  text: '  top              what\'s on his mind' },
    { kind: 'raw',  text: '  contact          how to reach him' },
    { kind: 'raw',  text: '  clear  /  ⌃L     clear the screen' },
    { kind: 'blank' },
    { kind: 'raw',  text: '  or just ask a question in plain english.' },
  ];
}

function contactLines() {
  return [
    { kind: 'h',     text: 'contact' },
    { kind: 'blank' },
    { kind: 'raw',   text: `email      ${CONTACT.email}` },
    { kind: 'raw',   text: `linkedin   ${CONTACT.linkedin}` },
    { kind: 'raw',   text: `portfolio  ${CONTACT.portfolio}` },
    { kind: 'blank' },
    { kind: 'aside', text: 'email is best. happy to chat.' },
  ];
}

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