const { useState, useEffect, useRef, useMemo, useCallback } = React;
const DATA = window.PZW_DATA;

// =============== HOOKS ===============
function useInView(opts = {}) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    // Immediate check: if already intersecting viewport on mount, reveal right away
    const r = ref.current.getBoundingClientRect();
    const vh = window.innerHeight || document.documentElement.clientHeight;
    if (r.top < vh && r.bottom > 0) { setInView(true); return; }
    const io = new IntersectionObserver(
      (entries) => entries.forEach((e) => e.isIntersecting && setInView(true)),
      { threshold: 0, rootMargin: "0px 0px -10% 0px", ...opts }
    );
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  return [ref, inView];
}

function useClock() {
  const [now, setNow] = useState(() => new Date());
  useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  return now;
}

function Reveal({ children, delay = 0, as: Tag = "div", className = "", ...rest }) {
  const [ref, inView] = useInView();
  return (
    <Tag
      ref={ref}
      className={className}
      style={{
        opacity: inView ? 1 : 0,
        transform: inView ? "translateY(0)" : "translateY(16px)",
        transition: `opacity 800ms cubic-bezier(.2,.7,.2,1) ${delay}ms, transform 800ms cubic-bezier(.2,.7,.2,1) ${delay}ms`,
      }}
      {...rest}
    >
      {children}
    </Tag>
  );
}

// =============== CURSOR RING ===============
function CursorRing() {
  const ref = useRef(null);
  const label = useRef(null);
  useEffect(() => {
    let x = 0, y = 0, tx = 0, ty = 0, raf;
    const onMove = (e) => { tx = e.clientX; ty = e.clientY; };
    const onOver = (e) => {
      const t = e.target.closest("[data-cursor]");
      if (label.current) label.current.textContent = t ? t.getAttribute("data-cursor") : "";
      if (ref.current) ref.current.classList.toggle("cursor-ring--hot", !!t);
    };
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseover", onOver);
    const loop = () => {
      x += (tx - x) * 0.2; y += (ty - y) * 0.2;
      if (ref.current) ref.current.style.transform = `translate(${x - 16}px, ${y - 16}px)`;
      raf = requestAnimationFrame(loop);
    };
    loop();
    return () => { window.removeEventListener("mousemove", onMove); window.removeEventListener("mouseover", onOver); cancelAnimationFrame(raf); };
  }, []);
  return <div ref={ref} className="cursor-ring" aria-hidden="true"><span ref={label} className="cursor-ring__label" /></div>;
}

// =============== TOP NAV ===============
const NAV = [
  ["01", "自述",     "#intro"],
  ["02", "方法",     "#method"],
  ["03", "实习",     "#internship"],
  ["04", "项目",     "#project"],
  ["05", "黑客松",   "#hackathon"],
  ["06", "获奖",     "#awards"],
  ["07", "媒体",     "#press"],
  ["08", "资产",     "#opensource"],
  ["09", "技能",     "#skills"],
  ["10", "在听",     "#inputs"],
  ["11", "AI 助理",  "#agent"],
  ["12", "联系",     "#contact"],
];

function TopNav({ direction }) {
  return (
    <header className="topnav">
      <div className="topnav__left" />

      <nav className="topnav__nav mono small">
        {NAV.map(([n, t, h]) => (
          <a key={h} href={h} data-cursor={"→ " + t} data-track={`topnav_${h.replace('#','')}`}>
            <span className="dim">{n}</span>&nbsp;{t}
          </a>
        ))}
      </nav>
      <div className="topnav__right">
        <a className="topnav__cta mono small" href="#contact" data-cursor="→ contact" data-track="cta_get_in_touch">
          GET IN TOUCH <span className="dim">↗</span>
        </a>
      </div>
    </header>
  );
}

function MobileNav() {
  const [open, setOpen] = useState(false);

  useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('keydown', onKey);
    document.body.style.overflow = 'hidden';
    return () => {
      document.removeEventListener('keydown', onKey);
      document.body.style.overflow = '';
    };
  }, [open]);

  const jump = (e, href) => {
    e.preventDefault();
    setOpen(false);
    setTimeout(() => {
      const t = document.querySelector(href);
      if (t) t.scrollIntoView({ behavior: 'smooth', block: 'start' });
    }, 200);
  };

  return (
    <>
      <button
        type="button"
        className="navfab"
        onClick={() => setOpen(true)}
        aria-label="打开导航"
        data-track="mobilenav_open"
      >
        <span /><span /><span />
      </button>
      <nav className={`navsheet mono ${open ? 'is-open' : ''}`} aria-hidden={!open}>
        <button
          type="button"
          className="navsheet__close"
          onClick={() => setOpen(false)}
          aria-label="关闭导航"
          data-track="mobilenav_close"
        >×</button>
        <div className="navsheet__head">
          <span className="dim">index ·</span>
          <span>navigate</span>
        </div>
        <ul className="navsheet__list">
          {NAV.map(([n, t, h]) => (
            <li key={h}>
              <a href={h} onClick={(e) => jump(e, h)} data-track={`mobilenav_${h.replace('#','')}`}>
                <span className="navsheet__num dim">{n}</span>
                <span className="navsheet__zh serif">{t}</span>
              </a>
            </li>
          ))}
        </ul>
      </nav>
    </>
  );
}

function FloatingActions() {
  const [showTop, setShowTop] = useState(false);
  const [hideMobile, setHideMobile] = useState(false);
  const [toast, setToast] = useState('');

  useEffect(() => {
    const onScroll = () => setShowTop(window.scrollY > 800);
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);

  useEffect(() => {
    const footer = document.querySelector('.footer');
    if (!footer) return;
    const io = new IntersectionObserver(
      ([e]) => setHideMobile(e.isIntersecting),
      { rootMargin: '0px 0px 0px 0px' }
    );
    io.observe(footer);
    return () => io.disconnect();
  }, []);

  const toTop = (e) => { e.preventDefault(); window.scrollTo({ top: 0, behavior: 'smooth' }); };

  const copyWechat = async (e) => {
    e.preventDefault();
    const id = '13238367571';
    try {
      await navigator.clipboard.writeText(id);
      setToast('微信号已复制：' + id);
    } catch {
      setToast('微信号：' + id);
    }
    setTimeout(() => setToast(''), 2400);
  };

  return (
    <>
      <button
        className={`fab ${showTop ? 'is-shown' : ''}`}
        onClick={toTop}
        aria-label="回到顶部"
        type="button"
        data-track="back_to_top"
      >↑</button>
      <nav className={`mobilebar mono ${hideMobile ? 'is-hidden' : ''}`} aria-label="联系方式">
        <button type="button" onClick={copyWechat} className="mobilebar__btn" data-track="mobilebar_wechat">
          <span className="mobilebar__icon">微</span>
          <span className="mobilebar__lbl">WeChat</span>
        </button>
        <a href="mailto:3098848445@qq.com" className="mobilebar__btn" data-track="mobilebar_email">
          <span className="mobilebar__icon">@</span>
          <span className="mobilebar__lbl">Email</span>
        </a>
        <a href="uploads/resume.pdf" target="_blank" rel="noopener" className="mobilebar__btn" data-track="mobilebar_resume">
          <span className="mobilebar__icon">PDF</span>
          <span className="mobilebar__lbl">Resume</span>
        </a>
      </nav>
      {toast ? <div className="toast mono small">{toast}</div> : null}
    </>
  );
}

// =============== FEATURE BANNER (wild) ===============
const FEATURES = [
  { en: "Destructive Creativity", zh: "破坏性创造力", color: "lime" },
  { en: "Leadership",             zh: "领导力",       color: "magenta" },
  { en: "Relentless Execution",   zh: "极致执行力",   color: "amber" },
  { en: "Infinite Curiosity",     zh: "无限好奇心",   color: "cyan" },
  { en: "AI Whispering",          zh: "擅长说服 AI",  color: "violet" },
];

function FeatureBanner() {
  const [shown, setShown] = useState(false);
  useEffect(() => {
    const id = setTimeout(() => setShown(true), 80);
    return () => clearTimeout(id);
  }, []);
  return (
    <div className="features">
      {FEATURES.map((f, i) => (
        <div
          key={f.en}
          className={"feature feature--" + f.color + (shown ? " feature--in" : "")}
          style={{ transitionDelay: (i * 120) + "ms" }}
        >
          <span className="feature__zh serif">{f.zh}</span>
          <span className="feature__en mono small">— {f.en}</span>
        </div>
      ))}
    </div>
  );
}

// =============== HERO ===============
function Typewriter({ text, speed = 40, className = "" }) {
  const [shown, setShown] = useState("");
  useEffect(() => {
    let i = 0, id;
    const tick = () => {
      setShown(text.slice(0, i));
      if (i < text.length) { i++; id = setTimeout(tick, speed); }
    };
    tick();
    return () => clearTimeout(id);
  }, [text, speed]);
  return <span className={className}>{shown}<span className="caret">▊</span></span>;
}

function Hero({ photo }) {
  const p = DATA.profile;
  const [bootLines, setBootLines] = useState([]);
  const [bootDone, setBootDone] = useState(false);
  const [history, setHistory] = useState([]); // [{ type: 'in' | 'out', text }]
  const [input, setInput] = useState("");
  const [busy, setBusy] = useState(false);
  const inputRef = useRef(null);
  const bodyRef = useRef(null);

  useEffect(() => {
    const seq = [
      "$ pzw-agent --boot",
      "ready · ask 实习 / 项目 / 方法论 / 技术栈 / 联系方式",
    ];
    let i = 0, id;
    const tick = () => {
      setBootLines(seq.slice(0, i));
      if (i < seq.length) { i++; id = setTimeout(tick, 220); }
      else setBootDone(true);
    };
    tick();
    return () => clearTimeout(id);
  }, []);

  const SUGGESTIONS = [
    "你最有代表性的项目？",
    "现在在哪里实习？",
    "怎么联系你？",
  ];

  useEffect(() => {
    if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
  }, [bootLines, history, busy]);

  const ask = useCallback(async (q) => {
    const query = (q || "").trim();
    if (!query || busy) return;
    setBusy(true);
    setHistory((h) => [...h, { type: 'in', text: query }]);
    setInput("");
    if (window.posthog) window.posthog.capture('hero_terminal_ask', { q: query.slice(0, 120) });
    let answer = "";
    try {
      const res = await fetch('/api/chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ q: query }),
      });
      if (!res.ok) throw new Error(`status ${res.status}`);
      const data = await res.json();
      answer = data.answer || data.text || data.message || JSON.stringify(data);
    } catch {
      answer = "（pzw-agent 后端尚未上线 · 暂时请直接联系：邮箱 3098848445@qq.com / 微信 13238367571）";
    }
    setHistory((h) => [...h, { type: 'out', text: answer }]);
    setBusy(false);
    setTimeout(() => inputRef.current?.focus(), 0);
  }, [busy]);

  const onSubmit = (e) => {
    e.preventDefault();
    ask(input);
  };

  return (
    <section className="hero" id="intro">
      <div className="hero__grid">
        <div className="hero__left">
          <div className="hero__badge mono small">
            <i className="led" /> <span>SUMMER 2026 · 开放合作</span>
          </div>

          <h1 className="hero__name">
            <span className="serif hero__name-zh">{p.nameZh}</span>
            <span className="hero__name-en mono small">
              <span className="dim">/</span> {p.nameEn}
            </span>
          </h1>

          <div className="hero__role serif">
            <em className="accent">Technical</em> AI<br />
            <span>Product Engineer</span>
          </div>

          <FeatureBanner />

          <p className="hero__statement">
            不想做只会写代码的工程师，也不想做只会画 PRD 的产品经理。<br />
            在 <em>AI 产品形态与组织范式被重写的窗口期</em>，<br />
            用 <em>想象力 × 工程直觉</em> 撬动团队，ship 出新形态的产品。<br />
            <br />
            现在用 <em>git</em> 管理个人 memory，同时研究怎么用 git 更好地沉淀 <em>企业级 AI coding memory</em>。
          </p>

          <div className="hero__figures">
            {[["大二", "in school"], ["@小红书", "ai pe · 1.2k+ followers"], ["10+", "independent builds · shipped"], ["#01", "adventurex 2025 · first prize"]].map(([n, l]) => (
              <div className="fig" key={l}>
                <div className="fig__n serif">{n}</div>
                <div className="fig__lbl mono small dim">{l}</div>
              </div>
            ))}
          </div>

          <FieldStack />
        </div>

        <div className="hero__right">
          <div className="portrait">
            <img src={photo} alt="portrait" onError={(e) => { e.target.style.display = "none"; }} />
          </div>

          <div className="terminal">
            <div className="terminal__bar mono small dim">
              <span className="tdot r" /><span className="tdot y" /><span className="tdot g" />
              <span>~/musketeer</span>
            </div>
            <div className="terminal__body mono" ref={bodyRef}>
              {bootLines.map((l, i) => (
                <div key={i} className={"tline " + (l.startsWith("$") ? "tline--prompt" : "tline--out")}>
                  {l}
                </div>
              ))}
              {history.map((h, i) => (
                <div key={"h" + i} className={"tline " + (h.type === 'in' ? "tline--prompt" : "tline--out")}>
                  {h.type === 'in' ? "$ " + h.text : h.text}
                </div>
              ))}
              {busy ? (
                <div className="tline tline--out"><span className="caret">▊</span> thinking…</div>
              ) : null}
              {bootDone && !busy && history.length === 0 ? (
                <div className="term-chips">
                  {SUGGESTIONS.map((s) => (
                    <button
                      key={s}
                      type="button"
                      className="term-chip mono"
                      onClick={() => ask(s)}
                      data-track={`hero_terminal_chip_${s.slice(0, 8)}`}
                    >{s}</button>
                  ))}
                </div>
              ) : null}
              {bootDone && !busy ? (
                <form className="tline tline--prompt term-form" onSubmit={onSubmit}>
                  <span>$&nbsp;</span>
                  <input
                    ref={inputRef}
                    className="term-input mono"
                    value={input}
                    onChange={(e) => setInput(e.target.value)}
                    placeholder="问点关于 pzw 的事…"
                    aria-label="ask pzw-agent"
                    data-track="hero_terminal_input"
                  />
                </form>
              ) : !bootDone ? (
                <div className="tline tline--prompt"><span className="caret-lg">▊</span></div>
              ) : null}
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// =============== FIELD STACK (hover to focus) ===============
const FIELD_SHOTS = [
  { src: "assets/field/aws-reinvent.jpg",       tag: "re:Invent 中国行",         caption: "亚马逊云科技 · DeepMe 路演" },
  { src: "assets/field/gdg-shanghai.jpg",       tag: "GDG Shanghai",             caption: "Google Developer Group · Speaker" },
  { src: "assets/field/waic-booth.jpg",         tag: "DeepMe @ WAIC",            caption: "H4-FT018 · 深度自我智能科技" },
  { src: "assets/field/innospark.jpg",          tag: "InnoSpark 路演",           caption: "心灵纽带 NeuraLink · 科创之光" },
  { src: "assets/field/waic-badge.jpg",         tag: "WAIC 2025",                caption: "World AI Conference · 大一独立开发者展商" },
  { src: "assets/field/xhs-hackathon.jpg",      tag: "小红书黑客松",              caption: "AI 暴富锦鱼 3.0 · ClawCorp 一人公司" },
  { src: "assets/field/intel-final.jpg",        tag: "Intel 总决赛",             caption: "2025 英特尔 AI 创新应用大赛 · 中国第一" },
  { src: "assets/field/intel-team-judging.jpg", tag: "Intel 评委 demo",          caption: "与评委 demo 心灵纽带 NeuraLink · 总决赛现场" },
  { src: "assets/field/intuitionx-stage.jpg",   tag: "抖音 × IntuitionX",        caption: "AI 创变者计划 · 优秀产品奖颁奖" },
  { src: "assets/field/adventurex-cert.png",    tag: "AdventureX 2025",          caption: "First Prize · 果霸 GoBuster · Paraflow 赛道" },
  { src: "assets/field/neuralink-talk.png",     tag: "字节 AI for Code 总决赛",   caption: "心灵纽带 NeuraLink · 稀土掘金 × Trae" },
  { src: "assets/field/deepme-interview.png",   tag: "Agilemind / DeepMe",       caption: "独立开发者 / WAIC 特邀展商 · 专访" },
];

function FieldStack() {
  const [hover, setHover] = useState(null);
  const n = FIELD_SHOTS.length;
  // deterministic pseudo-random tilt per index
  const tilts = useMemo(() => FIELD_SHOTS.map((_, i) => {
    const a = Math.sin(i * 1234.5) * 2.4;
    return Number(a.toFixed(2));
  }), []);
  const active = hover !== null ? FIELD_SHOTS[hover] : null;
  useEffect(() => {
    if (active === null) return;
    const onKey = (e) => { if (e.key === "Escape") setHover(null); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [active]);
  return (
    <div className="fstack">
      <div className="fstack__grid">
        {FIELD_SHOTS.map((s, i) => {
          const isHover = hover === i;
          const anyHover = hover !== null;
          const tilt = tilts[i];
          const style = anyHover && !isHover
            ? { transform: `rotate(${tilt}deg) scale(0.94)`, opacity: 0.4, filter: "blur(1.5px)" }
            : { transform: `rotate(${tilt}deg)` };
          return (
            <figure
              key={i}
              className={"fstack__card" + (isHover ? " is-hover" : "")}
              style={style}
              onMouseEnter={() => setHover(i)}
            >
              <img src={s.src} alt={s.caption} loading="lazy" />
              <span className="fstack__idx mono small">{String(i + 1).padStart(2, "0")} / {String(n).padStart(2, "0")}</span>
            </figure>
          );
        })}
      </div>
      {active ? (
        <div className="fstack__lightbox">
          <div className="fstack__lightbox-overlay" onClick={() => setHover(null)} />
          <button className="fstack__lightbox-close mono small" onClick={() => setHover(null)} aria-label="关闭">
            ESC ×
          </button>
          <figure className="fstack__lightbox-card">
            <img src={active.src} alt={active.caption} />
            <figcaption className="fstack__lightbox-cap">
              <span className="fstack__tag mono small">{active.tag}</span>
              <span className="fstack__caption">{active.caption}</span>
            </figcaption>
          </figure>
        </div>
      ) : null}
    </div>
  );
}

// =============== TIMELINE ===============
function TimelineItem({ item, idx }) {
  const isLarge = item.kind === "internship" || item.kind === "project";
  return (
    <Reveal className={"tl-item tl-item--" + item.kind} delay={idx * 40}>
      <div className="tl-item__spine" aria-hidden="true">
        <span className="tl-item__marker" />
      </div>
      <div className="tl-item__meta mono small">
        <span className="dim">{item.date}</span>
        {item.kind === "internship" ? null : <span className="tag-chip">{item.kind}</span>}
      </div>
      <div className="tl-item__body">
        <div className="tl-item__org mono small dim">
          {item.org}
          {item.venue ? (
            <>
              {" · "}
              {/\.[a-z]{2,}/i.test(item.venue) ? (
                <a
                  className="tl-item__venue"
                  href={item.venue.startsWith("http") ? item.venue : "https://" + item.venue}
                  target="_blank"
                  rel="noopener noreferrer"
                  data-cursor="↗ open"
                >
                  {item.venue} <span className="tl-item__venue-arrow">↗</span>
                </a>
              ) : (
                item.venue
              )}
            </>
          ) : null}
          {item.repo ? (
            <>
              {" · "}
              <a
                className="tl-item__venue"
                href={item.repo.startsWith("http") ? item.repo : "https://" + item.repo}
                target="_blank"
                rel="noopener noreferrer"
                data-cursor="↗ github"
              >
                GitHub <span className="tl-item__venue-arrow">↗</span>
              </a>
            </>
          ) : null}
        </div>
        <h3 className="tl-item__title serif">{item.title}</h3>
        {item.subtitle ? <div className="tl-item__subtitle serif">{item.subtitle}</div> : null}
        {item.role && item.kind !== "internship" ? <div className="tl-item__role mono small">{item.role}</div> : null}
        {item.summary && item.kind !== "internship" ? <p className="tl-item__summary">{item.summary}</p> : null}
        {item.bullets && isLarge ? (
          <ul className="tl-item__bullets">
            {item.bullets.map((b, i) => (
              <li key={i}><span className="accent">—</span><span>{b}</span></li>
            ))}
          </ul>
        ) : null}
        {item.metrics ? (
          <div className="tl-item__metrics">
            {item.metrics.map(([n, l]) => (
              <div className="metric" key={l}>
                <span className="metric__n serif">{n}</span>
                <span className="metric__l mono small dim">{l}</span>
              </div>
            ))}
          </div>
        ) : null}
        {item.tags ? (
          <div className="tl-item__tags">
            {item.tags.map((t) => <span key={t} className="tag-chip">{t}</span>)}
          </div>
        ) : null}
      </div>
    </Reveal>
  );
}

function Timeline() {
  const items = DATA.timeline;
  const groups = [
    { key: "internship", label: "实习经历",   kicker: "internships",  match: (it) => it.kind === "internship" },
    { key: "project",    label: "项目经历",   kicker: "projects",     match: (it) => it.kind === "project" },
    { key: "hackathon",  label: "黑客松经历", kicker: "hackathons",   match: (it) => it.kind === "hackathon" || it.kind === "showcase" },
  ];
  return (
    <div className="timeline">
      {groups.map((g) => {
        const list = items.filter(g.match);
        if (!list.length) return null;
        return (
          <div className="tl-group" key={g.key}>
            <div className="tl-group__head mono small">
              <span className="accent">{g.label}</span>
              <span className="dim">· {g.kicker}</span>
              <span className="tl-group__line" />
              <span className="tl-group__count dim">{String(list.length).padStart(2, "0")}</span>
            </div>
            {list.map((it, i) => <TimelineItem key={g.key + i} item={it} idx={i} />)}
          </div>
        );
      })}
      <AwardsBlock />
    </div>
  );
}

// Single-kind timeline group for split sections
function TimelineGroup({ kind }) {
  const list = DATA.timeline.filter((it) => it.kind === kind || (kind === "hackathon" && it.kind === "showcase"));
  return (
    <div className="timeline">
      <div className="tl-group">
        {list.map((it, i) => <TimelineItem key={kind + i} item={it} idx={i} />)}
      </div>
    </div>
  );
}

// Section head: number + EN kicker + 大字 ZH
function SectionHead({ num, en, zh, sub }) {
  return (
    <div className="sec-head">
      <div className="sec-head__top mono small">
        <span className="dim">{num}</span>
        <span className="sec-head__rule" />
      </div>
      <h2 className="sec-head__zh serif">{zh}</h2>
    </div>
  );
}

function AwardsBlock() {
  return (
    <div className="tl-group tl-group--awards">
      <div className="tl-group__head mono small">
        <span className="accent">获奖荣誉</span>
        <span className="dim">· awards</span>
        <span className="tl-group__line" />
        <span className="tl-group__count dim">{String(DATA.awards.length).padStart(2, "0")}</span>
      </div>
      <div className="awards-list">
        {DATA.awards.map(([y, n, p], i) => (
          <Reveal key={n + i} delay={i * 18} className="award-row">
            <span className="award-row__year mono small dim">{y}</span>
            <span className="award-row__name serif">{n}</span>
            <span className="award-row__prize mono small">{p}</span>
          </Reveal>
        ))}
      </div>
    </div>
  );
}

// =============== PRESS ===============
function PressBlock() {
  return (
    <div className="tl-group tl-group--press">
      <div className="tl-group__head mono small">
        <span className="accent">媒体报道</span>
        <span className="dim">· press · interviews</span>
        <span className="tl-group__line" />
        <span className="tl-group__count dim">{String(DATA.press.length).padStart(2, "0")}</span>
      </div>
      <div className="press">
        {DATA.press.map((p, i) => (
          <Reveal key={p.href} delay={i * 60} className="press__row">
            <a href={p.href} target="_blank" rel="noopener noreferrer" className="press__link" data-cursor="↗ open">
              <div className="press__outlet mono small">
                <span className="press__kind">{p.kind === "video" ? "[ video ]" : "[ article ]"}</span>
                <span className="press__outlet-name accent">{p.outletLabel}</span>
              </div>
              <h3 className="press__title serif">{p.title}</h3>
              <p className="press__sub">{p.sub}</p>
              <div className="press__cta mono small">
                <span>read interview</span>
                <span className="press__arrow">↗</span>
              </div>
            </a>
          </Reveal>
        ))}
      </div>
    </div>
  );
}

// =============== OPEN SOURCE SKILLS ===============
function OpenSourceBlock() {
  return (
    <div className="oss">
      {DATA.contextAssets.map((s, i) => (
        <Reveal key={s.repo} delay={i * 80} className={`oss__row oss__row--${s.kind || 'skill'}`}>
          <a href={s.href} target="_blank" rel="noopener noreferrer" className="oss__card" data-cursor="↗ open" data-track={`oss_${s.repo.replace(/[^a-z0-9]+/gi, '_').toLowerCase()}`}>
            <div className="oss__head mono small">
              <span className={`oss__kind oss__kind--${s.kind || 'skill'}`}>[ {s.kind || 'skill'} ]</span>
              <span className="oss__repo">{s.repo}</span>
            </div>
            <h3 className="oss__name serif">{s.name}</h3>
            <p className="oss__blurb">{s.blurb}</p>
            <div className="oss__foot">
              <div className="oss__tags mono small">
                {s.tags.map((t) => <span key={t} className="oss__tag">{t}</span>)}
              </div>
              <span className="oss__cta mono small">view on github <span className="oss__arrow">↗</span></span>
            </div>
          </a>
        </Reveal>
      ))}
    </div>
  );
}

// =============== METHODOLOGY ===============
function Methodology() {
  return (
    <div className="tl-group tl-group--method">
      <div className="tl-group__head mono small">
        <span className="accent">方法论</span>
        <span className="dim">· how I work</span>
        <span className="tl-group__line" />
        <span className="tl-group__count dim">{String(DATA.methodology.length).padStart(2, "0")}</span>
      </div>
      <div className="method">
        {DATA.methodology.map((m, i) => (
          <Reveal key={m.step} delay={i * 50} className="method__row">
            <div className="method__step mono small">{m.step}</div>
            <div className="method__main">
              <h3 className="method__title serif">{m.title}</h3>
              <p className="method__body">{m.body}</p>
              <div className="method__sample mono small">
                <span className="dim">&gt;&nbsp;case</span>&nbsp;&nbsp;{m.sample}
              </div>
            </div>
          </Reveal>
        ))}
      </div>
    </div>
  );
}

// =============== SKILLS ===============
function Skills() {
  return (
    <div className="skills">
      {DATA.skills.map((s) => (
        <Reveal key={s.group} className="skills__row">
          <div className="skills__group mono small accent">{s.group}</div>
          <div className="skills__items">
            {s.items.map((it) => <span key={it} className="skill-chip" data-cursor="/skill">{it}</span>)}
          </div>
        </Reveal>
      ))}
    </div>
  );
}

// =============== INPUTS (now listening / reading / watching / building) ===============
function Inputs() {
  const D = DATA.inputs;
  return (
    <div className="inputs">
      <div className="inputs__meta mono small">
        <span className="dim">updated</span>
        <span className="accent">{D.updated}</span>
        {D.note ? (
          <>
            <span className="inputs__rule" />
            <span className="dim">{D.note}</span>
          </>
        ) : null}
      </div>
      <div className="inputs__grid">
        {D.groups.map((g, gi) => (
          <Reveal key={g.kind} delay={gi * 40} className="inputs__col">
            <div className="inputs__colhead">
              <span className="mono small accent">{g.kind}</span>
              <span className="serif inputs__zh">{g.zh}</span>
              <span className="inputs__colrule" />
              <span className="mono small dim">{String(g.items.length).padStart(2, "0")}</span>
            </div>
            <ul className="inputs__list">
              {g.items.map((it, ii) => (
                <li key={it.title + ii} className="inputs__item">
                  <a href={it.href} target="_blank" rel="noreferrer" className="inputs__link" data-cursor="↗">
                    <div className="inputs__row">
                      <span className="serif inputs__title">{it.title}</span>
                      <span className="inputs__arrow mono">↗</span>
                    </div>
                    <div className="mono small dim inputs__src">{it.meta}</div>
                    <p className="inputs__note">{it.note}</p>
                  </a>
                </li>
              ))}
            </ul>
          </Reveal>
        ))}
      </div>
    </div>
  );
}

// =============== AGENT CHAT ===============
const SUGGESTED = [
  "你最擅长什么？",
  "介绍一下 ClawCorp。",
  "你做 AI PE 的方法论是什么？",
  "你有哪些代表性奖项？",
];

function AgentChat() {
  const [log, setLog] = useState([
    { role: "system", text: "pzw-agent 已就绪 · 基于简历信息回答 · 由 Claude 驱动" },
  ]);
  const [input, setInput] = useState("");
  const [busy, setBusy] = useState(false);
  const scrollerRef = useRef(null);

  useEffect(() => {
    if (scrollerRef.current) scrollerRef.current.scrollTop = scrollerRef.current.scrollHeight;
  }, [log, busy]);

  const send = useCallback(async (q) => {
    if (!q || busy) return;
    setBusy(true);
    setLog((l) => [...l, { role: "user", text: q }]);
    setInput("");

    const context = `你是彭智炜（Peng Zhiwei）个人网站上的 AI 助理，代表彭智炜本人回答访客问题。回答要求：简短（2-4 句话）、中文、第一人称（"我"），语气自然、克制、略带工程师的冷幽默。不要编造信息。信息缺失就说"可以直接联系我 · 微信 13238367571"。智炜，风格克制、偏极客/编辑口吻，允许短句打点。不要编造未列出的内容。

# 关于彭智炜
- 技术型 AI 产品工程师（Product Engineer），擅长 Agent 产品、复杂工作流、多 Agent 协作系统
- 实习：小红书 AI PE · 商业技术 PE Infra（2026.04—now，参与 Muse vibe coding + CodeWiz CLI 两条 AI 编程主线）、北京中关村学院 AI 研究院「情报引擎」AI PE（2026.01-04）、脉脉 AI 品类运营实习生（2025.10-12）
- 代表项目：ClawCorp（一人公司 AI 业务合伙人，小红书黑客松）、Mora、Neuralink、DeepMe（WAIC / re:Invent 展示）、果霸 Gobuster（AdventureX 第一名）
- 方法论 6 步：识别 → 抽象 → 原型 → 评估 → 交付 → 沉淀；ship 完把经验 commit 进 git，下次 AI 协同直接复用
- 奖项：AdventureX 第一、豆包算法亚军、英特尔中国第一、WAIC Amazon 邀请展商、Amazon 1000A Idea 总决赛、字节 AI for Code 优胜、百度 AI for Science 一等奖、华为 ICT 省一等奖
- 技术栈：Claude Code / Codex / Cursor / Manus / Gemini CLI / Kimi Code；React / TypeScript / Python / FastAPI / Supabase / PostgreSQL
- 联系：GitHub wilson534 · Email 3098848445@qq.com · WeChat 13238367571

访客问题：${q}`;

    try {
      const resp = await window.claude.complete(context);
      setLog((l) => [...l, { role: "assistant", text: resp }]);
    } catch (e) {
      setLog((l) => [...l, { role: "assistant", text: "（本地演示失败，请直接通过邮件或微信联系：3098848445@qq.com / 13238367571）" }]);
    }
    setBusy(false);
  }, [busy]);

  return (
    <div className="chat">
      <div className="chat__head mono small">
        <span className="tdot r" /><span className="tdot y" /><span className="tdot g" />
        <span className="chat__title">pzw-agent @ ~/portfolio</span>
        <span className="chat__model dim">model: claude-haiku</span>
      </div>
      <div className="chat__body mono" ref={scrollerRef}>
        {log.map((m, i) => (
          <div key={i} className={"cline cline--" + m.role}>
            <span className="cline__who">
              {m.role === "user" ? "visitor" : m.role === "assistant" ? "pzw-agent" : "system"}
            </span>
            <span className="cline__sep dim">&nbsp;»&nbsp;</span>
            <span className="cline__text">{m.text}</span>
          </div>
        ))}
        {busy ? (
          <div className="cline cline--assistant">
            <span className="cline__who">pzw-agent</span>
            <span className="cline__sep dim">&nbsp;»&nbsp;</span>
            <span className="cline__text"><span className="caret">▊</span> thinking…</span>
          </div>
        ) : null}
      </div>
      <div className="chat__suggest">
        {SUGGESTED.map((s) => (
          <button key={s} className="chat__chip mono small" onClick={() => send(s)} disabled={busy} data-cursor="/ask">
            {s}
          </button>
        ))}
      </div>
      <form className="chat__form mono" onSubmit={(e) => { e.preventDefault(); send(input); }}>
        <span className="chat__prompt">visitor »</span>
        <input
          className="chat__input"
          placeholder="问我任何关于 pzw 的事…"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          disabled={busy}
        />
        <button type="submit" className="chat__send mono small" disabled={busy || !input.trim()} data-cursor="↵">
          send ↵
        </button>
      </form>
    </div>
  );
}

// =============== CONTACT ===============
function Contact() {
  const p = DATA.profile;
  return (
    <div className="contact">
      <Reveal className="contact__headline serif">
        下一个 <em className="accent">Agent</em>，由你和我一起 ship。
      </Reveal>
      <div className="contact__grid">
        {p.links.map((l) => (
          <a key={l.label} href={l.href} className="contact__row" data-cursor={l.label} data-track={`contact_${l.label.replace(/[^a-z0-9]+/gi, '_').toLowerCase()}`} target={l.newTab ? "_blank" : undefined} rel={l.newTab ? "noopener noreferrer" : undefined}>
            <span className="mono small dim">{l.label}</span>
            <span className="contact__handle">{l.handle}</span>
            <span className="contact__arrow mono">↗</span>
          </a>
        ))}
      </div>
    </div>
  );
}

// =============== APP ===============
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "direction": "editorial",
  "accentHue": 118,
  "grain": true,
  "cursorRing": true,
  "showHelp": false
}/*EDITMODE-END*/;

function HelpSheet({ onClose }) {
  return (
    <div className="helpsheet" onClick={onClose}>
      <div className="helpsheet__card mono" onClick={(e) => e.stopPropagation()}>
        <div className="helpsheet__head">
          <span className="small dim">keyboard · shortcuts</span>
          <button className="helpsheet__x" onClick={onClose}>×</button>
        </div>
        <table>
          <tbody>
            <tr><td><kbd>j</kbd> / <kbd>↓</kbd></td><td>next section</td></tr>
            <tr><td><kbd>k</kbd> / <kbd>↑</kbd></td><td>prev section</td></tr>
            <tr><td><kbd>1</kbd>—<kbd>7</kbd></td><td>jump to section</td></tr>
            <tr><td><kbd>t</kbd></td><td>toggle direction</td></tr>
            <tr><td><kbd>g g</kbd></td><td>go to top</td></tr>
            <tr><td><kbd>G</kbd></td><td>go to bottom</td></tr>
            <tr><td><kbd>?</kbd></td><td>this help</td></tr>
          </tbody>
        </table>
      </div>
    </div>
  );
}

// =============== ANALYTICS ===============
function useTracking() {
  useEffect(() => {
    const ph = window.posthog;
    if (!ph || typeof ph.capture !== "function") return;

    // 1) Delegated click tracking — every <a> and <button> in the app
    const onClick = (e) => {
      const el = e.target.closest("a, button");
      if (!el) return;
      const tag = el.tagName.toLowerCase();
      const track = el.getAttribute("data-track") || null;
      const text = (el.innerText || el.textContent || "").trim().slice(0, 80);
      const href = tag === "a" ? el.getAttribute("href") : null;
      const section = el.closest("section.sec")?.id || null;
      const eventName = track ? `click_${track}` : (tag === "a" ? "link_click" : "button_click");
      ph.capture(eventName, {
        tag,
        text,
        href,
        section,
        external: href ? /^https?:\/\//.test(href) : false,
      });
    };
    document.addEventListener("click", onClick, true);

    // 2) Section visibility + dwell time
    const sections = Array.from(document.querySelectorAll("section.sec"));
    const dwell = new Map(); // id -> { enteredAt, total }
    sections.forEach((s) => dwell.set(s.id, { enteredAt: 0, total: 0 }));

    const flush = (id, reason) => {
      const d = dwell.get(id);
      if (!d || !d.enteredAt) return;
      const ms = Date.now() - d.enteredAt;
      d.total += ms;
      d.enteredAt = 0;
      ph.capture("section_view", { section: id, dwell_ms: ms, total_ms: d.total, reason });
    };

    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          const id = entry.target.id;
          const d = dwell.get(id);
          if (!d) return;
          if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
            if (!d.enteredAt) d.enteredAt = Date.now();
          } else {
            if (d.enteredAt) flush(id, "leave");
          }
        });
      },
      { threshold: [0, 0.5, 1] }
    );
    sections.forEach((s) => io.observe(s));

    // 3) Flush on hide / unload — page-level dwell capture
    const onHide = () => sections.forEach((s) => flush(s.id, "hide"));
    document.addEventListener("visibilitychange", () => { if (document.visibilityState === "hidden") onHide(); });
    window.addEventListener("pagehide", onHide);
    window.addEventListener("beforeunload", onHide);

    return () => {
      document.removeEventListener("click", onClick, true);
      io.disconnect();
    };
  }, []);
}

function App() {
  const [tw, setTw] = window.useTweaks ? window.useTweaks(TWEAK_DEFAULTS) : [TWEAK_DEFAULTS, () => {}];
  const [help, setHelp] = useState(false);
  const lastG = useRef(0);

  useTracking();

  // Apply direction + theme vars
  useEffect(() => {
    const root = document.documentElement;
    root.setAttribute("data-direction", tw.direction);
    root.style.setProperty("--accent-hue", tw.accentHue);
    root.classList.toggle("grain", tw.grain);
  }, [tw.direction, tw.accentHue, tw.grain]);

  // Keyboard shortcuts
  useEffect(() => {
    const sections = () => NAV.map(([, , h]) => document.querySelector(h)).filter(Boolean);
    const curIdx = () => {
      const ss = sections();
      const y = window.scrollY + 120;
      let idx = 0;
      ss.forEach((s, i) => { if (s.offsetTop <= y) idx = i; });
      return idx;
    };
    const goto = (i) => {
      const ss = sections();
      if (ss[i]) ss[i].scrollIntoView({ behavior: "smooth", block: "start" });
    };
    const onKey = (e) => {
      if (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") return;
      if (e.key === "j" || e.key === "ArrowDown" && !e.shiftKey) { e.preventDefault(); goto(Math.min(NAV.length - 1, curIdx() + 1)); }
      else if (e.key === "k" || e.key === "ArrowUp" && !e.shiftKey) { e.preventDefault(); goto(Math.max(0, curIdx() - 1)); }
      else if (/^[1-9]$/.test(e.key)) goto(parseInt(e.key, 10) - 1);
      else if (e.key === "?") setHelp(true);
      else if (e.key === "Escape") setHelp(false);
      else if (e.key === "t") setTw({ direction: tw.direction === "editorial" ? "paper" : tw.direction === "paper" ? "terminal" : "editorial" });
      else if (e.key === "g") {
        const now = Date.now();
        if (now - lastG.current < 400) window.scrollTo({ top: 0, behavior: "smooth" });
        lastG.current = now;
      }
      else if (e.key === "G") window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" });
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [tw.direction, setTw]);

  const directionLabel = { editorial: "editorial/paper", paper: "paper/editorial", terminal: "terminal" }[tw.direction] || tw.direction;

  return (
    <div className="site">
      {tw.cursorRing ? <CursorRing /> : null}
      <TopNav direction={directionLabel} />
      <main>
        <Hero photo={DATA.profile.photo} />

        <section className="sec sec--alt" id="method">
          <SectionHead num="02" en="Methodology" zh="方法论" sub="how I work" />
          <Methodology />
        </section>

        <section className="sec" id="internship">
          <SectionHead num="03" en="Internship" zh="实习经历" sub="full-time-style stints" />
          <TimelineGroup kind="internship" />
        </section>

        <section className="sec sec--alt" id="project">
          <SectionHead num="04" en="Projects" zh="项目经历" sub="independent builds, shipped" />
          <TimelineGroup kind="project" />
        </section>

        <section className="sec" id="hackathon">
          <SectionHead num="05" en="Hackathons" zh="黑客松 / 比赛" sub="48-hour ships, on-site demos" />
          <TimelineGroup kind="hackathon" />
        </section>

        <section className="sec sec--alt" id="awards">
          <SectionHead num="06" en="Awards" zh="获奖" sub="第一名、提名、入选" />
          <AwardsBlock />
        </section>

        <section className="sec" id="press">
          <SectionHead num="07" en="Press" zh="媒体报道" sub="采访 · 视频 · 报道" />
          <PressBlock />
        </section>

        <section className="sec sec--alt" id="opensource">
          <SectionHead num="08" en="Context Assets" zh="个人 context 资产" sub="skill · memory · knowledge · install once, remember always" />
          <OpenSourceBlock />
        </section>

        <section className="sec" id="skills">
          <Skills />
        </section>

        <section className="sec sec--alt" id="inputs">
          <SectionHead num="10" en="Now / Inputs" zh="现在在听 · 在读 · 在用" sub="this is what's feeding my brain right now" />
          <Inputs />
        </section>

        <section className="sec sec--agent" id="agent">
          <AgentChat />
        </section>

        <section className="sec" id="contact">
          <Contact />
        </section>
      </main>

      <footer className="footer mono small">
        <div>© 2026 彭智炜 · designed & built solo</div>
        <div className="dim">last updated · 2026.04.24</div>
      </footer>

      <MobileNav />
      <FloatingActions />

      {help ? <HelpSheet onClose={() => setHelp(false)} /> : null}

      {window.TweaksPanel ? (
        <window.TweaksPanel title="Tweaks">
          <window.TweakSection title="direction">
            <window.TweakRadio
              label="visual system"
              value={tw.direction}
              onChange={(v) => setTw({ direction: v })}
              options={[
                { value: "editorial", label: "Editorial · 深色套印" },
                { value: "paper",     label: "Paper · 纸本套印" },
                { value: "terminal",  label: "Terminal · 终端黑客" },
              ]}
            />
            <window.TweakSlider label="accent hue" min={0} max={360} step={1} value={tw.accentHue} onChange={(v) => setTw({ accentHue: v })} />
            <window.TweakToggle label="grain texture" value={tw.grain} onChange={(v) => setTw({ grain: v })} />
            <window.TweakToggle label="cursor ring"   value={tw.cursorRing} onChange={(v) => setTw({ cursorRing: v })} />
          </window.TweakSection>
          <window.TweakSection title="shortcuts">
            <div className="mono" style={{ fontSize: 12, lineHeight: 1.8, opacity: 0.7 }}>
              <div><kbd>j/k</kbd> next / prev</div>
              <div><kbd>1-7</kbd> jump to section</div>
              <div><kbd>t</kbd> toggle direction</div>
              <div><kbd>?</kbd> help sheet</div>
            </div>
          </window.TweakSection>
        </window.TweaksPanel>
      ) : null}
    </div>
  );
}

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