/* eslint @next/next/no-html-link-for-pages: off, react/jsx-no-undef: ["error", { "allowGlobals": true }] */
/* global React, ReactDOM, TweaksPanel, useTweaks, TweakSection, TweakRadio, TweakColor, Splash */
const { useState, useEffect, useRef } = React;

const _hs = (typeof window !== "undefined" && window.HOLT_STATS) || {};
const MEMBER_COUNT = (_hs.memberCount || 4258143).toLocaleString();
const FILE_COUNT = (_hs.fileCount || 322075).toLocaleString();
const MEMBER_COUNT_RAW = _hs.memberCount || 4258143;
const FILE_COUNT_RAW = _hs.fileCount || 322075;

const ACCENTS = {
  transfer: { name: "Transfer blue", hex: "#3B82F6", soft: "rgba(59,130,246,0.12)" },
  music:    { name: "Music orange",  hex: "#F97316", soft: "rgba(249,115,22,0.12)" },
  premium:  { name: "Premium amber", hex: "#F59E0B", soft: "rgba(245,158,11,0.12)" },
  tracker:  { name: "Tracker green", hex: "#10B981", soft: "rgba(16,185,129,0.12)" },
  groups:   { name: "Groups violet", hex: "#8B5CF6", soft: "rgba(139,92,246,0.12)" },
  stream:   { name: "Stream pink",   hex: "#EC4899", soft: "rgba(236,72,153,0.12)" },
  red:      { name: "Brand red",     hex: "#E5474A", soft: "rgba(229,71,74,0.12)" }
};

const TABS = [
  { id: "about",   label: "Holt, who?", href: "/holt/" },
  { id: "work",    label: "Recent work" },
  { id: "hire",    label: "Why hire me" },
  { id: "stats",   label: "By the numbers" },
  { id: "contact", label: "Say hello" }
];

const SUBTABS = [
  { id: "overview",  label: "Overview" },
  { id: "migration", label: "PHP to Next.js + Convex" },
  { id: "transfer",  label: "Transfer service" },
  { id: "helpdesk",  label: "Help desk" },
  { id: "stack",     label: "Stack and size" }
];

/* ---------- Typing hook ---------- */
function useTyped(text, speed = 18, start = true) {
  const [out, setOut] = useState("");
  useEffect(() => {
    if (!start) return;
    setOut("");
    let i = 0;
    const id = setInterval(() => {
      i++;
      setOut(text.slice(0, i));
      if (i >= text.length) clearInterval(id);
    }, speed);
    return () => clearInterval(id);
  }, [text, speed, start]);
  return out;
}

/* ---------- Mobile hook ---------- */
function useIsMobile() {
  const [mobile, setMobile] = useState(() => typeof window !== "undefined" && window.innerWidth <= 820);
  useEffect(() => {
    const check = () => setMobile(window.innerWidth <= 820);
    window.addEventListener("resize", check);
    return () => window.removeEventListener("resize", check);
  }, []);
  return mobile;
}

/* ---------- Konami ---------- */
function useKonami(onFire) {
  useEffect(() => {
    const seq = ["ArrowUp","ArrowUp","ArrowDown","ArrowDown","ArrowLeft","ArrowRight","ArrowLeft","ArrowRight"];
    let i = 0;
    const h = (e) => {
      const k = e.key.length === 1 ? e.key.toLowerCase() : e.key;
      if (k === seq[i]) {
        i++;
        if (i === seq.length) { onFire(); i = 0; }
      } else {
        i = (k === seq[0]) ? 1 : 0;
      }
    };
    window.addEventListener("keydown", h);
    return () => window.removeEventListener("keydown", h);
  }, [onFire]);
}

/* ---------- Pill tabs ---------- */
function Pills({ tabs, value, onChange, accent }) {
  return (
    <div className="pills">
      {tabs.map(t => t.href ? (
        <a
          key={t.id}
          href={t.href}
          className={"pill " + (t.id === value ? "is-active" : "")}
          style={t.id === value ? { background: accent, color: "#0a0a0d" } : null}
        >
          {t.label}
        </a>
      ) : (
        <button
          key={t.id}
          className={"pill " + (t.id === value ? "is-active" : "")}
          onClick={() => onChange(t.id)}
          style={t.id === value ? { background: accent, color: "#0a0a0d" } : null}
        >
          {t.label}
        </button>
      ))}
    </div>
  );
}

/* ---------- Mascot ---------- */
const WAVE_CYCLE = [
  "Let's build something.",
  "Still shipping.",
  "AI-first, always.",
];

function MascotBubble({ text }) {
  const typed = useTyped(text, 40);
  const done = typed.length === text.length;
  return (
    <div className="mascot-bubble">
      {typed}
      {!done && <span className="bubble-caret" />}
    </div>
  );
}

function Mascot({ mood, accent }) {
  const [waveIdx, setWaveIdx] = useState(0);
  useEffect(() => {
    if (mood !== "wave") return;
    const id = setInterval(() => setWaveIdx(i => (i + 1) % WAVE_CYCLE.length), 4500);
    return () => clearInterval(id);
  }, [mood]);

  const moods = {
    wave:    WAVE_CYCLE[waveIdx],
    work:    "Here's what I've been cooking.",
    hire:    "Pick me. Pick me.",
    stats:   "All numbers are real. Promise.",
    contact: "Slide into my DMs."
  };
  return (
    <div className="mascot-stage">
      <div className="mascot-glow" style={{ background: `radial-gradient(circle at 50% 50%, ${accent}55, transparent 60%)` }} />
      <img src="/holt/assets/mascot_peace.png" className="mascot-img" alt="Holt mascot" />
      <div className="mascot-bubble-wrap">
        <MascotBubble text={moods[mood] || moods.wave} key={mood === "wave" ? waveIdx : mood} />
      </div>
    </div>
  );
}

/* ---------- Lucide icons (inlined SVG) ---------- */
function LucideIcon({ size = 24, color = "currentColor", style, children }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" style={style} aria-hidden="true">
      {children}
    </svg>
  );
}
function BrainCircuit({ size, color, style }) {
  return (
    <LucideIcon size={size} color={color} style={style}>
      <path d="M12 4.5a2.5 2.5 0 0 0-4.96-.46 2.5 2.5 0 0 0-1.98 3 2.5 2.5 0 0 0-1.32 4.24 3 3 0 0 0 .34 5.58 2.5 2.5 0 0 0 2.96 3.08 2.5 2.5 0 0 0 4.91.05L12 20V4.5Z"/>
      <path d="M16 8V5c0-1.1.9-2 2-2"/><path d="M12 13h4"/><path d="M12 18h6a2 2 0 0 1 2 2v1"/>
      <path d="M12 8h8"/><path d="M20.5 8a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Z"/>
      <path d="M16.5 13a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Z"/>
      <path d="M20.5 21a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Z"/>
      <path d="M18.5 3a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Z"/>
    </LucideIcon>
  );
}
function IconBot({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/>
    <path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/>
  </LucideIcon>;
}
function IconZap({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
  </LucideIcon>;
}
function IconLayers({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <path d="m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"/>
    <path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65"/>
    <path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65"/>
  </LucideIcon>;
}
function IconUsers({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/>
    <circle cx="9" cy="7" r="4"/>
    <path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>
  </LucideIcon>;
}
function IconGitBranch({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <line x1="6" x2="6" y1="3" y2="15"/>
    <circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/>
    <path d="M18 9a9 9 0 0 1-9 9"/>
  </LucideIcon>;
}
function IconTrendingUp({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <polyline points="22 7 13.5 15.5 8.5 10.5 2 17"/>
    <polyline points="16 7 22 7 22 13"/>
  </LucideIcon>;
}
function IconShield({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/>
  </LucideIcon>;
}
function IconGitCommit({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <circle cx="12" cy="12" r="3"/><line x1="3" y1="12" x2="9" y2="12"/><line x1="15" y1="12" x2="21" y2="12"/>
  </LucideIcon>;
}
function IconFlame({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/>
  </LucideIcon>;
}
function IconCalendarDays({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <rect width="18" height="18" x="3" y="4" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/><path d="M8 14h.01M12 14h.01M16 14h.01M8 18h.01M12 18h.01M16 18h.01"/>
  </LucideIcon>;
}
function IconPuzzle({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <path d="M19.439 7.85c-.049.322.059.648.289.878l1.568 1.568c.47.47.706 1.087.706 1.704s-.235 1.233-.706 1.704l-1.611 1.611a.98.98 0 0 1-.837.276c-.47-.07-.802-.48-.968-.925a2.501 2.501 0 1 0-3.214 3.214c.446.166.855.497.925.968a.979.979 0 0 1-.276.837l-1.61 1.61a2.404 2.404 0 0 1-1.705.707 2.402 2.402 0 0 1-1.704-.706l-1.568-1.568a1.026 1.026 0 0 0-.877-.29c-.493.074-.84.504-1.02.968a2.5 2.5 0 1 1-3.237-3.237c.464-.18.894-.527.967-1.02a1.026 1.026 0 0 0-.289-.877l-1.568-1.568A2.402 2.402 0 0 1 1.998 12c0-.617.236-1.234.706-1.704L4.23 8.69c.24-.24.581-.353.917-.303.515.077.877.528 1.073 1.01a2.5 2.5 0 1 0 3.259-3.259c-.482-.196-.933-.558-1.01-1.073-.05-.336.062-.676.303-.917l1.525-1.525A2.402 2.402 0 0 1 12 2c.617 0 1.234.236 1.704.706l1.568 1.568c.23.23.556.338.877.29.493-.074.84-.504 1.02-.968a2.5 2.5 0 1 1 3.237 3.237c-.464.18-.894.527-.967 1.02z"/>
  </LucideIcon>;
}
function IconLink({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
  </LucideIcon>;
}
function IconDatabase({ size = 14, color }) {
  return <LucideIcon size={size} color={color}>
    <ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5V19A9 3 0 0 0 21 19V5"/><path d="M3 12A9 3 0 0 0 21 12"/>
  </LucideIcon>;
}

/* ---------- Terminal ---------- */
const TERMINAL_COMMANDS = {
  help: () => [
    { grid: [
      ["whoami", "who I am"],
      ["stack", "what I build with"],
      ["ai", "how I work with AI"],
      ["ship", "recent commit log"],
      ["brain", "how I think"],
      ["style", "what I'm strong at"],
      ["hire", "why hire me"],
      ["konami", "a hint"],
      ["clear", "wipe the terminal"]
    ]}
  ],
  whoami: () => [
    "Holt Meyers. I build software.",
    "First site: PC Gameworld, launched 1998. Thousands of hand-written HTML pages. Grew it until it needed a proper rebuild.",
    "Now: filefactory.com - 4 million members, over 1 billion pageviews."
  ],
  stack: () => [
    "Next.js 16 (App Router) on the front.",
    "Convex on the back. TypeScript everywhere.",
    "Tailwind for styles. Playwright and Jest for tests.",
    `${FILE_COUNT} user files stored on Cloudflare R2.`
  ],
  ai: () => [
    "I work AI-first, every day.",
    "Claude Code and Codex are my main tools.",
    "Loop: I give the direction. The agent writes the spec, code, and tests.",
    "I review the code. I verify the UI. It ships.",
    "Human in the loop, but the loop is fast."
  ],
  ship: () => [
    "Six month commit log:",
    "  Feb 2026 ........ 2,462 commits",
    "  Mar 2026 ........ 2,869 commits   (Convex migration)",
    "  Apr 2026 ........ 1,641 commits",
    "  May 2026 ........ 2,188 commits",
    "Average ~42 commits a day."
  ],
  hire: () => [
    "I ship daily.",
    "I give the direction. The agent writes the spec, code, and tests. I review everything.",
    "I lead by doing the work.",
    "I own the outcome, not just the code."
  ],
  brain: () => [
    "Most engineers think:",
    "  database → backend → API → UI",
    "I think:",
    "  product fantasy → visual identity → emotional appeal → feature system → implementation",
    "I'm a founder/designer/engineer hybrid.",
    "Visual-first. Product-driven. AI in the workflow, every day."
  ],
  style: () => [
    "What I'm good at:",
    "  product sense - I know what to build and how it should feel",
    "  working with AI - direct it, review it, ship it",
    "  staying unblocked - I find a way around most things",
    "  end-to-end ownership - I own the whole thing, top to bottom",
    "What I avoid: anything that gets in the way of shipping."
  ],
  konami: () => ["Up Up Down Down Left Right Left Right", "(go on, try it)"],
  clear: () => "__clear__"
};

const SUGGEST_ORDER = ["whoami", "ship", "ai", "stack", "brain", "hire", "style", "konami"];

function Terminal({ accent }) {
  const [lines, setLines] = useState(() => {
    const helpOut = TERMINAL_COMMANDS.help();
    return [
      { kind: "cmd", cmd: "help" },
      { kind: "dim", text: "commands you can try:" },
      ...helpOut.map(item => typeof item === "string" ? { kind: "dim", text: item } : { kind: "grid", grid: item.grid })
    ];
  });
  const [pending, setPending] = useState([]);
  const [used, setUsed] = useState(new Set());
  const [input, setInput] = useState("");
  const [focused, setFocused] = useState(false);
  const inpRef = useRef(null);
  const measureRef = useRef(null);
  const [caretX, setCaretX] = useState(0);
  const scrollRef = useRef(null);
  const streaming = pending.length > 0;

  const nextSuggestion = !streaming ? (SUGGEST_ORDER.find(c => !used.has(c)) || null) : null;

  // drain pending queue one line at a time
  useEffect(() => {
    if (pending.length === 0) return;
    const id = setTimeout(() => {
      setLines(prev => [...prev, pending[0]]);
      setPending(prev => prev.slice(1));
    }, 55);
    return () => clearTimeout(id);
  }, [pending]);

  useEffect(() => {
    if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
  }, [lines]);

  useEffect(() => {
    if (measureRef.current) {
      setCaretX(measureRef.current.offsetWidth);
    }
  }, [input]);

  function run(raw) {
    const cmd = raw.trim().toLowerCase();
    if (!cmd || streaming) return;
    setLines(prev => [...prev, { kind: "cmd", cmd: raw }]);
    if (cmd === "clear") {
      setPending([]);
      setLines([{ kind: "dim", text: "(cleared) - type help" }]);
      setUsed(new Set());
      return;
    }
    const fn = TERMINAL_COMMANDS[cmd];
    if (!fn) {
      setPending([{ kind: "err", text: `${cmd}: not a command. Try 'help'.` }]);
      return;
    }
    const out = fn();
    setPending(out.map(t => typeof t === "string" ? { kind: "dim", text: t } : { kind: "grid", grid: t.grid }));
    setUsed(prev => new Set([...prev, cmd]));
  }

  return (
    <div className="terminal" onClick={() => inpRef.current?.focus()}>
      <div className="terminal-bar">
        <span className="dot" style={{ background: "#ff5f57" }} />
        <span className="dot" style={{ background: "#febc2e" }} />
        <span className="dot" style={{ background: "#28c840" }} />
        <span className="terminal-title">~/filefactory - try it</span>
      </div>
      <div className="terminal-body" ref={scrollRef}>
        {lines.map((l, i) => (
          l.kind === "grid"
            ? <div key={i} className="tline tline-grid">
                {l.grid.map(([cmd, desc], j) => (
                  <div key={j} className="tcell">
                    <span className="tcmd" style={{ color: accent }}>{cmd}</span>
                    <span className="tdesc">{desc}</span>
                  </div>
                ))}
              </div>
            : l.kind === "cmd"
              ? <div key={i} className="tline tline-cmd">
                  <span className="tprompt-host" style={{ color: accent }}>filefactory@holt</span>
                  <span style={{ color: "#71748A" }}>:~$ </span>
                  <span>{l.cmd}</span>
                </div>
              : <div key={i} className={"tline tline-" + l.kind}>{l.text}</div>
        ))}
        {nextSuggestion && (
          <div className="tline tline-suggest">
            <span>→ try </span>
            <button
              className="tsuggest-btn"
              style={{ color: accent }}
              onClick={(e) => { e.stopPropagation(); run(nextSuggestion); }}
            >
              {nextSuggestion}
            </button>
          </div>
        )}
        <div className="tline tline-input">
          <span style={{ color: accent }}>filefactory@holt</span>
          <span style={{ color: "#71748A" }}>:~$ </span>
          <span className="terminal-input-wrap">
            <span ref={measureRef} className="terminal-measure">{input || "type 'help'…"}</span>
            <input
              ref={inpRef}
              value={input}
              onChange={(e) => setInput(e.target.value)}
              onKeyDown={(e) => { if (e.key === "Enter") { run(input); setInput(""); } }}
              placeholder="type 'help'…"
              className="terminal-input"
              spellCheck={false}
            />
            <span className="caret" style={{ background: accent, left: caretX }} />
          </span>
        </div>
      </div>
    </div>
  );
}

/* ---------- Stat tile ---------- */
function Stat({ value, label, sub, accent, icon: Icon }) {
  return (
    <div className="stat">
      {Icon && (
        <div className="stat-icon" style={{ color: accent }}>
          <Icon size={18} color={accent} />
        </div>
      )}
      <div className="stat-value" style={{ color: accent }}>{value}</div>
      <div className="stat-label">{label}</div>
      {sub && <div className="stat-sub">{sub}</div>}
    </div>
  );
}

/* ---------- Sections ---------- */
function AboutSection({ accent, serif, onOpenGame }) {
  const greeting = "Hi, I'm Holt.";
  const body1 = "I built FileFactory - a file hosting platform with over 4 million members and billions of pageviews.";
  const body2 = "These days I work entirely AI-first. Claude Code and Codex are my main tools for shipping fast.";
  const intro = greeting + " " + body1 + " " + body2;
  const typed = useTyped(intro, 14);
  const typedGreeting = typed.slice(0, Math.min(typed.length, greeting.length));
  const greetingDone = typed.length > greeting.length;
  const typedRest = greetingDone ? typed.slice(greeting.length + 1) : "";
  const typedBody1 = typedRest.slice(0, Math.min(typedRest.length, body1.length));
  const body1Done = typedRest.length > body1.length;

  const [showFF, setShowFF] = useState(true);
  const [cardCls, setCardCls] = useState("");
  useEffect(() => {
    function doSwap() {
      setCardCls("origin-card-out");
      setTimeout(() => {
        setShowFF(v => !v);
        setCardCls("origin-card-in origin-entering");
        setTimeout(() => {
          setCardCls("origin-entering");
          setTimeout(() => setCardCls(""), 800);
        }, 50);
      }, 400);
    }
    const id = setInterval(doSwap, 15000);
    return () => clearInterval(id);
  }, []);
  const typedBody2 = body1Done ? typedRest.slice(body1.length + 1) : "";
  return (
    <div>
      <div className="overline" style={{ color: accent }}>About me</div>
      <h1 className="display" style={{ fontFamily: serif }}>
        I build things.
        <br />
        <span style={{ color: accent }}>Now building AI-first.</span>
      </h1>
      <div className="grid about-grid">
        <div>
          <p className="lead lead-typed" style={{ marginBottom: 8 }}>
            <span className="lead-ghost" aria-hidden="true">{greeting}</span>
            <span className="lead-typed-text">
              {typedGreeting}
              {!greetingDone && <span className="caret-text" style={{ color: accent }}>_</span>}
            </span>
          </p>
          <p className="lead lead-typed" style={{ marginBottom: 8 }}>
            <span className="lead-ghost" aria-hidden="true">{body1}</span>
            <span className="lead-typed-text">
              {typedBody1}
              {greetingDone && !body1Done && <span className="caret-text" style={{ color: accent }}>_</span>}
            </span>
          </p>
          <p className="lead lead-typed">
            <span className="lead-ghost" aria-hidden="true">{body2}</span>
            <span className="lead-typed-text">
              {typedBody2}
              {body1Done && <span className="caret-text" style={{ color: accent }}>_</span>}
            </span>
          </p>

          <div className={"origin-card" + (cardCls ? " " + cardCls : "")}>
            <div className="origin-tape origin-tape-l" />
            <div className="origin-tape origin-tape-r" />
            <div className="origin-panels">
              <div className={"origin-panel" + (!showFF ? " is-active" : "")}>
                <div className="origin-meta">
                  <span className="origin-meta-label">FIRST SITE</span>
                  <span className="origin-meta-dot">·</span>
                  <span className="origin-meta-loc">archived</span>
                </div>
                <div className="origin-url">
                  <span className="origin-url-proto">http://www.</span>
                  <span className="origin-url-host">pcgameworld</span>
                  <span className="origin-url-tld">.com</span>
                  <span className="origin-url-cursor">_</span>
                </div>
                <div className="origin-body">
                  <p>It started as thousands of pages of plain HTML that I wrote and linked by hand. When it outgrew that approach, I brought in a developer to rebuild it in PHP.</p>
                  <p>Twenty-five years later the stack has changed completely, but the job feels the same: talk to the user, ship the thing, and fix what breaks.</p>
                </div>
                <a href="/holt/game" className="origin-game-link" onClick={onOpenGame} style={{ borderColor: accent + "66", color: accent }}>
                  <span className="origin-game-link-kicker">Play</span>
                  <span className="origin-game-link-title">HOLT ATTACK</span>
                  <span className="origin-game-link-arrow">→</span>
                </a>
              </div>
              <div className={"origin-panel" + (showFF ? " is-active" : "")}>
                <div className="origin-meta">
                  <span className="origin-meta-label">MAIN PROJECT</span>
                  <span className="origin-meta-dot">·</span>
                  <span className="origin-meta-loc">live</span>
                </div>
                <div className="origin-url">
                  <span className="origin-url-proto">https://www.</span>
                  <span className="origin-url-host">filefactory</span>
                  <span className="origin-url-tld">.com</span>
                  <span className="origin-url-cursor">_</span>
                </div>
                <div className="origin-body">
                  <p>FileFactory started in 2005 as a proof of concept - a few PHP files built in a week. Today it has over 4 million registered members, has served well over a billion pageviews, and runs on 678,000 lines of code.</p>
                  <p>The technology has evolved beyond recognition, but the mission hasn't: deliver fast, reliable file hosting run by someone who actually cares when things go wrong.</p>
                </div>
                <a href="https://www.filefactory.com" target="_blank" rel="noreferrer" className="origin-game-link" style={{ borderColor: accent + "66", color: accent }}>
                  <span className="origin-game-link-kicker">Visit</span>
                  <span className="origin-game-link-title">FILEFACTORY.COM</span>
                  <span className="origin-game-link-arrow">→</span>
                </a>
              </div>
            </div>
          </div>
        </div>

        <Mascot mood="wave" accent={accent} />
      </div>
    </div>
  );
}

function WorkSection({ accent, serif, sub, setSub }) {
  return (
    <div>
      <div className="overline" style={{ color: accent }}>Recent work</div>
      <h1 className="display" style={{ fontFamily: serif }}>
        FileFactory.{" "}
        <span style={{ color: accent }}>Top to bottom.</span>
      </h1>
      <p className="lead">
        I've run FileFactory since 2005. In 2025-2026 I rebuilt the entire platform from the ground up - moving five legacy PHP applications onto a modern Next.js + Convex stack.
      </p>

      <div className="subtabs">
        {SUBTABS.map(s => (
          <button
            key={s.id}
            className={"subtab " + (s.id === sub ? "is-active" : "")}
            style={s.id === sub ? { color: accent, borderColor: accent } : null}
            onClick={() => setSub(s.id)}
          >
            {s.label}
          </button>
        ))}
      </div>

      <div className="subpanel">
        {sub === "overview"  && <WorkOverview accent={accent} />}
        {sub === "migration" && <WorkMigration accent={accent} />}
        {sub === "transfer"  && <WorkTransfer accent={accent} />}
        {sub === "helpdesk"  && <WorkHelpdesk accent={accent} />}
        {sub === "stack"     && <WorkStack accent={accent} />}
      </div>
    </div>
  );
}

function WorkOverview({ accent }) {
  return <OverviewStats accent={accent} />;
}

function OverviewStats({ accent }) {
  return (
    <div className="ov">
      {/* Hero row: the headline numbers */}
      <div className="ov-hero">
        <div className="ov-hero-card ov-hero-users">
          <div className="ov-eyebrow" style={{ color: accent }}>Members</div>
          <div className="ov-hero-num" style={{ color: accent }}>4,000,000</div>
          <div className="ov-hero-sub">across two decades on FileFactory</div>
          <svg className="ov-hero-spark" viewBox="0 -8 300 60" preserveAspectRatio="none">
            <defs>
              <linearGradient id="ov-spark-fill" x1="0" x2="0" y1="0" y2="1">
                <stop offset="0%" stopColor={accent} stopOpacity="0.4" />
                <stop offset="100%" stopColor={accent} stopOpacity="0" />
              </linearGradient>
            </defs>
            <path d="M0,47 L20,44 L40,40 L60,34 L80,30 L100,22 L120,17 L140,14 L160,10 L180,7 L200,4 L220,2 L240,0 L260,-1 L280,-2 L300,-3 L300,52 L0,52 Z" fill="url(#ov-spark-fill)" />
            <path d="M0,47 L20,44 L40,40 L60,34 L80,30 L100,22 L120,17 L140,14 L160,10 L180,7 L200,4 L220,2 L240,0 L260,-1 L280,-2 L300,-3" stroke={accent} strokeWidth="1.5" fill="none" />
          </svg>
          <div className="ov-spark-dates">
            <span>2002</span>
            <span>2026</span>
          </div>
        </div>

        <div className="ov-hero-card ov-hero-pv">
          <div className="ov-eyebrow" style={{ color: accent }}>Billions of pageviews served</div>
          <div className="ov-hero-num" style={{ color: accent }}>1,000,000,000<span className="ov-plus">+</span></div>
          <div className="ov-hero-sub">built up over twenty years of real traffic</div>
          <div className="ov-pv-grid" aria-hidden="true">
            {Array.from({ length: 100 }).map((_, i) => {
              const filled = i < 73;
              return <span key={i} className="ov-pv-cell" style={{ background: filled ? accent : "rgba(255,255,255,0.06)", opacity: filled ? 0.35 + (i / 100) * 0.65 : 1 }} />;
            })}
          </div>
          <div className="ov-pv-legend">
            <span>each square = 10M views</span>
          </div>
        </div>
      </div>

      {/* Codebase ledger */}
      <div className="ov-ledger">
        <div className="ov-ledger-head">
          <span className="ov-eyebrow" style={{ color: accent }}>The codebase, by the numbers</span>
          <span className="ov-ledger-sub">apps/filefactory - counted yesterday</span>
        </div>
        <div className="ov-ledger-rows">
          <LedgerRow accent={accent} label="TypeScript" value="678,412" unit="lines" pct={100} note="primary language" />
          <LedgerRow accent={accent} label="Convex functions" value="2,703" unit="files" pct={42} note="queries · mutations · actions" />
          <LedgerRow accent={accent} label="Next.js pages" value="674" unit="routes" pct={28} note="+ 330 API routes" />
          <LedgerRow accent={accent} label="Test cases" value="3,875" unit="specs" pct={62} note="Jest + Playwright" />
          <LedgerRow accent={accent} label="Files on R2" value={FILE_COUNT} unit="objects" pct={48} note="user uploads" />
        </div>
      </div>

      {/* Team context callout */}
      <div className="card-note" style={{ marginTop: 28 }}>
        <div className="card-note-title">What this normally costs</div>
        <div className="card-note-body">
          A codebase this size would normally be a big team effort. I rebuilt it solo in 9 months while the site stayed live and paying customers kept using it - all thanks to working AI-first.
        </div>
      </div>

      {/* In short - pull quote */}
      <div className="notepad notepad-tilt-r" style={{ marginTop: 32 }}>
        <div className="notepad-tape notepad-tape-l" />
        <div className="notepad-tape notepad-tape-r" />
        <div className="notepad-eyebrow" style={{ color: accent }}>
          In short
          <span className="notepad-eyebrow-rule" />
          <span style={{ color: "var(--fg-muted)" }}>the whole pitch</span>
        </div>
        <p className="notepad-body">
          One person. <span className="text-emphasis">AI-first</span> workflow. A full platform rebuild while real users and real revenue kept flowing - all the way through.
        </p>
        <div className="notepad-sig">
          <span className="notepad-sig-mark" style={{ color: accent }}>Holt Meyers</span>
          <span className="notepad-sig-meta">
            <span>Founder &amp; Engineer</span>
            <span>FileFactory</span>
          </span>
        </div>
      </div>
    </div>
  );
}

function LedgerRow({ accent, label, value, unit, pct, note }) {
  return (
    <div className="ov-row">
      <div className="ov-row-label">
        <div className="ov-row-name">{label}</div>
        <div className="ov-row-note">{note}</div>
      </div>
      <div className="ov-row-bar">
        <div className="ov-row-bar-fill" style={{ width: pct + "%", background: accent }} />
      </div>
      <div className="ov-row-num">
        <span className="ov-row-val" style={{ color: accent }}>{value}</span>
        <span className="ov-row-unit">{unit}</span>
      </div>
    </div>
  );
}

function WorkMigration({ accent }) {
  return (
    <div>
      <h3 className="sub-h">PHP to Next.js + Convex</h3>
      <p className="lead" style={{ marginBottom: 20 }}>
        Five legacy PHP apps. Around 400,000 lines of real code. All moved onto a modern stack while real users kept uploading, downloading, and paying. February and March 2026 was a two-month sprint at five times my normal pace.
      </p>

      <div className="bars">
        {[
          ["prodigy",  102450, 466],
          ["www",       92332, 555],
          ["classic",   76593, 484],
          ["dl",        76357, 296],
          ["secure",    49860, 596]
        ].map(([app, lines, files]) => (
          <div key={app} className="bar">
            <div className="bar-label"><span>{app}</span><span className="bar-files">{files} files</span></div>
            <div className="bar-track">
              <div className="bar-fill" style={{ width: `${(lines / 102450) * 100}%`, background: accent }} />
            </div>
            <div className="bar-value">{lines.toLocaleString()} lines</div>
          </div>
        ))}
      </div>

      <div className="grid grid-3" style={{ marginTop: 32 }}>
        <Stat value="2,462"   label="Feb commits"  accent={accent} icon={IconGitCommit} />
        <Stat value="2,869"   label="Mar commits"  sub="Convex migration peak" accent={accent} icon={IconFlame} />
        <Stat value="1"        label="Engineer"     sub="Solo build, start to finish" accent={accent} icon={IconUsers} />
      </div>
    </div>
  );
}

function WorkTransfer({ accent }) {
  return (
    <div className="grid grid-2">
      <div>
        <h3 className="sub-h">Transfer service</h3>
        <p>
          Drag, drop, send. Read receipts on your file. The end of the "did you get my email?" follow-up. I built it end to end: chunked uploads, expiring share links, password gates, secure download flow, billing across five payment processors.
        </p>
        <ul className="ticks">
          <li>Chunked, resumable uploads. Multi-gigabyte files survive a flaky connection.</li>
          <li>Per-link expiry, password gates, download caps</li>
          <li>Playwright tests cover upload, share, download end to end</li>
        </ul>
      </div>
      <div className="mock">
        <div className="mock-header">
          <span className="dot" style={{ background: "#ff5f57" }} />
          <span className="dot" style={{ background: "#febc2e" }} />
          <span className="dot" style={{ background: "#28c840" }} />
          <span className="mock-title">FileFactory Transfer</span>
        </div>
        <div className="mock-body">
          <div className="drop" style={{ borderColor: accent + "55" }}>
            <div className="drop-icon" style={{ color: accent }}>↑</div>
            <div className="drop-headline">Drop files here</div>
            <div className="drop-sub">Up to 10 GB. We handle the heavy lifting.</div>
          </div>
          <div className="file-row">
            <div className="file-name">project_master_final_v7.zip</div>
            <div className="file-meta">7.2 GB · uploading</div>
            <div className="bar-track" style={{ marginTop: 8 }}>
              <div className="bar-fill" style={{ width: "62%", background: accent }} />
            </div>
          </div>
          <div className="file-row">
            <div className="file-name">shoot_raw_apr.mov</div>
            <div className="file-meta">3.8 GB · queued</div>
          </div>
        </div>
      </div>
    </div>
  );
}

function WorkHelpdesk({ accent }) {
  return (
    <div className="grid grid-2">
      <div>
        <h3 className="sub-h">Help desk, built in-house</h3>
        <p>
          We were spending a lot on Intercom, so I replaced it with our own internal help desk. It includes threads, macros, AI-assisted replies, customer profiles, internal notes, and SLA tracking. All deeply integrated with the user's account and history.
        </p>
        <ul className="ticks">
          <li>28 customer-support tables (<code>cs_*</code>)</li>
          <li>AI-suggested replies, grounded on the user's own files and history</li>
          <li>Macros, snippets, internal notes, assignment</li>
          <li>SLA timers and revenue-tier routing</li>
          <li>I still answer tickets in it myself every day</li>
        </ul>
      </div>

      <div className="mock">
        <div className="mock-header">
          <span className="dot" style={{ background: "#ff5f57" }} />
          <span className="dot" style={{ background: "#febc2e" }} />
          <span className="dot" style={{ background: "#28c840" }} />
          <span className="mock-title">Inbox · 12 open</span>
        </div>
        <div className="mock-body inbox">
          {[
            ["Anya",   "Can't download my zip",        "2m",  true],
            ["Marcus", "Lifetime upgrade question",    "14m", false],
            ["Tomás",  "Read receipt didn't fire",     "1h",  false],
            ["Yui",    "Refund for double charge",     "3h",  false]
          ].map(([name, subj, ago, unread], i) => (
            <div key={i} className={"thread " + (unread ? "unread" : "")}>
              <div className="thread-avatar" style={{ background: accent + "33", color: accent }}>{name[0]}</div>
              <div className="thread-body">
                <div className="thread-name">{name}</div>
                <div className="thread-subj">{subj}</div>
              </div>
              <div className="thread-meta">
                {unread && <span className="dot-unread" style={{ background: accent }} />}
                <span>{ago}</span>
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function WorkStack({ accent }) {
  const data = [
    ["app/ - pages, routes, providers", 332],
    ["convex/ - backend",               141],
    ["lib/ - utilities, hooks",         101],
    ["components/ - shared UI",          90],
    ["tests/",                           78],
    ["e2e/ - Playwright",                 6]
  ];
  return (
    <div>
      <h3 className="sub-h">Tech stack and scale</h3>
      <p>The platform sits on Next.js and Convex, with Cloudflare and R2 handling the heavy lifting. Everything is type-safe, heavily tested, and built with real users in mind. About 678,000 lines across <strong>apps/filefactory</strong> alone, with {FILE_COUNT} files on R2.</p>
      <div className="stack-grid">
        {data.map(([k,v]) => (
          <div key={k} className="stack-row">
            <div className="stack-key">{k}</div>
            <div className="stack-bar"><div className="stack-fill" style={{ width: (v / 332) * 100 + "%", background: accent }} /></div>
            <div className="stack-val" style={{ color: accent }}>{v}k</div>
          </div>
        ))}
      </div>

      <div className="grid grid-3" style={{ marginTop: 32 }}>
        <Stat value="1,818" label="React components" accent={accent} icon={IconPuzzle} />
        <Stat value="243"   label="Custom hooks"     accent={accent} icon={IconLink} />
        <Stat value="301"   label="Database tables"  sub="across 43 schema files" accent={accent} icon={IconDatabase} />
      </div>
    </div>
  );
}

function HireSection({ accent, serif }) {
  const isMobile = useIsMobile();
  return (
    <div>
      <div className="overline" style={{ color: accent }}>Why hire me</div>
      <h1 className="display" style={{ fontFamily: serif }}>
        I get things <span style={{ color: accent }}>done.</span>
      </h1>
      <p className="lead">
        I'm not just "AI-curious" - I'm AI-first. Claude Code and Codex are core to how I work every single day.
      </p>
      <p className="lead">
        In the last nine months I've delivered a full legacy platform migration (over 400,000 lines of PHP), written thousands of tests, and kept a live product running smoothly the whole time.
      </p>

      {/* How I think - side-by-side mindset comparison */}
      <div className="mindset">
        <div className="mindset-head" style={{ position: "relative" }}>
          <BrainCircuit
            size={160}
            color={accent}
            style={{ position: "absolute", top: -16, right: 0, opacity: 0.07, pointerEvents: "none" }}
          />
          <div className="mindset-eyebrow" style={{ color: accent, display: "inline-flex", alignItems: "center", gap: 8 }}>
            <BrainCircuit size={15} color={accent} />
            How I think
          </div>
          <h2 className="mindset-title" style={{ fontFamily: serif }}>
            Most engineers start at the code.<br /><span style={{ color: accent }}>I start with the customer.</span>
          </h2>
          <p className="mindset-sub">After years of running my own product, I start with the customer, not the ticket. I focus on outcomes first: What actually needs to change? What's the smallest useful thing we can ship? Did it move the needle?</p>
        </div>

        <div className="mindset-pull" style={{ borderLeftColor: accent }}>
          <span className="mindset-pull-mark" style={{ color: accent, fontFamily: serif }}>"</span>
          <span>I've spent my career deciding what to build, what to cut, and when to ship - not waiting for someone else to write the spec.</span>
        </div>
      </div>

      {/* Career timeline - zigzag on desktop, vertical list on mobile */}
      {(() => {
        const PINS = [
          { y: "1998", t: "PC Gameworld", s: "first site, plain HTML", role: "solo" },
          { y: "2005", t: "FileFactory v1", s: "PHP launch, indie shop", role: "solo" },
          { y: "2011", t: "Team of 3", s: "hired engineers, ran sprints", role: "lead" },
          { y: "2016", t: "Team of 5", s: "max headcount", role: "lead" },
          { y: "2022", t: "Owner-operator", s: "lean ops, full-stack again", role: "solo" },
          { y: "2025", t: "AI-first", s: "Claude Code, Codex, Cursor", role: "solo" },
          { y: "2026", t: "Convex rebuild", s: "678k LOC, solo sprint", role: "solo" }
        ];
        const dotStyle = (p) => ({
          background: p.role === "lead" ? accent : "var(--fg-muted)",
          boxShadow: p.role === "lead" ? `0 0 0 4px ${accent}22` : "none"
        });
        if (isMobile) {
          return (
            <div className="career-strip">
              <div className="career-strip-label" style={{ color: accent }}>Career timeline</div>
              <ol className="career-list">
                {PINS.map((p, i) => (
                  <li key={i} className="career-list-item">
                    <div className="career-list-dot" style={dotStyle(p)} />
                    <div className="career-list-body">
                      <span className="career-list-year" style={{ color: p.role === "lead" ? accent : "var(--fg-secondary)" }}>{p.y}</span>
                      <span className="career-list-title">{p.t}</span>
                      <span className="career-list-sub">{p.s}</span>
                      {p.role === "lead" && <span className="career-pin-tag" style={{ background: accent + "22", color: accent }}>led team</span>}
                    </div>
                  </li>
                ))}
              </ol>
            </div>
          );
        }
        return (
          <div className="career-strip">
            <div className="career-strip-label" style={{ color: accent }}>Career timeline</div>
            <div className="career-track">
              <div className="career-axis" />
              {PINS.map((p, i) => i % 2 === 0 && (
                <div key={"t" + i} className="career-cell career-cell-top" style={{ gridColumn: i + 1 }}>
                  <div className="career-pin-year" style={{ color: p.role === "lead" ? accent : "var(--fg-secondary)" }}>{p.y}</div>
                  <div className="career-pin-title">{p.t}</div>
                  <div className="career-pin-sub">{p.s}</div>
                  {p.role === "lead" && <div className="career-pin-tag" style={{ background: accent + "22", color: accent }}>led team</div>}
                </div>
              ))}
              {PINS.map((p, i) => (
                <div key={"d" + i} className="career-cell career-cell-dot" style={{ gridColumn: i + 1 }}>
                  <div className="career-pin-dot" style={dotStyle(p)} />
                </div>
              ))}
              {PINS.map((p, i) => i % 2 === 1 && (
                <div key={"b" + i} className="career-cell career-cell-bot" style={{ gridColumn: i + 1 }}>
                  <div className="career-pin-year" style={{ color: p.role === "lead" ? accent : "var(--fg-secondary)" }}>{p.y}</div>
                  <div className="career-pin-title">{p.t}</div>
                  <div className="career-pin-sub">{p.s}</div>
                  {p.role === "lead" && <div className="career-pin-tag" style={{ background: accent + "22", color: accent }}>led team</div>}
                </div>
              ))}
            </div>
          </div>
        );
      })()}

      <div className="grid grid-2">
        <div className="hire-card">
          <div className="hire-card-eyebrow" style={{ color: accent, display: "flex", alignItems: "center", gap: 6 }}><IconBot color={accent} />How I work with AI</div>
          <h3 className="hire-card-title">Direct, review, test, ship</h3>
          <p>I give the direction. The agent writes the spec, the code, and the tests. I read every line and verify the UI manually. It ships.</p>
          <p>Human in the loop, but the loop is fast. Six months on a live codebase will teach you quickly which parts of AI output need a second look - and which parts you can just let run.</p>
          <div className="kbd-row">
            <span className="kbd">Claude Code</span>
            <span className="kbd">Codex</span>
            <span className="kbd">Cursor</span>
            <span className="kbd">Playwright MCP</span>
          </div>
        </div>

        <div className="hire-card">
          <div className="hire-card-eyebrow" style={{ color: accent, display: "flex", alignItems: "center", gap: 6 }}><IconZap color={accent} />Pace</div>
          <h3 className="hire-card-title">I move fast</h3>
          <p>In the last nine months I've pushed over 12,000 commits. During the Convex migration I was averaging around 40-50 commits on active days.</p>
          <p>No endless debate. I cut, ship, and circle back with a test.</p>
          <div className="velocity-bars">
            {[
              ["Nov", 60], ["Dec", 60], ["Jan", 70],
              ["Feb", 85], ["Mar", 100], ["Apr", 57], ["May", 76]
            ].map(([m, h]) => (
              <div key={m} className="vbar">
                <div className="vbar-fill" style={{ height: h + "%", background: accent }} />
                <div className="vbar-lbl">{m}</div>
              </div>
            ))}
          </div>
        </div>

        <div className="hire-card">
          <div className="hire-card-eyebrow" style={{ color: accent, display: "flex", alignItems: "center", gap: 6 }}><IconLayers color={accent} />Range</div>
          <h3 className="hire-card-title">Front, back, mobile, ops, support</h3>
          <p>I've replaced entire legacy codebases without disrupting users, built internal tools that save real money, and shipped features while real customers kept using the product.</p>
          <p>Then I go and answer the support tickets. Every part of the stack I'd ask someone else to own, I've owned myself first.</p>
        </div>

        <div className="hire-card">
          <div className="hire-card-eyebrow" style={{ color: accent, display: "flex", alignItems: "center", gap: 6 }}><IconUsers color={accent} />Leadership</div>
          <h3 className="hire-card-title">I've led engineering teams.</h3>
          <p>Hiring, code review, sprint planning, mentoring juniors, unblocking seniors. But I'm equally comfortable going deep as a hands-on engineer who owns the full stack.</p>
          <div className="kbd-row">
            <span className="kbd">Hiring</span>
            <span className="kbd">Code review</span>
            <span className="kbd">Mentoring</span>
            <span className="kbd">Sprint planning</span>
          </div>
        </div>

        <div className="hire-card">
          <div className="hire-card-eyebrow" style={{ color: accent, display: "flex", alignItems: "center", gap: 6 }}><IconGitBranch color={accent} />Legacy stack replacement</div>
          <h3 className="hire-card-title">400,000 lines of PHP. Replaced entirely.</h3>
          <p>FileFactory ran on aging PHP and MySQL for nearly two decades. I rebuilt the entire platform on Next.js, Tailwind, and Convex. Modern stack, real-time backend, type-safe all the way down.</p>
        </div>

        <div className="hire-card">
          <div className="hire-card-eyebrow" style={{ color: accent, display: "flex", alignItems: "center", gap: 6 }}><IconTrendingUp color={accent} />Receipts</div>
          <h3 className="hire-card-title">4 million members. Over 1 billion pageviews.</h3>
          <p>FileFactory isn't a portfolio piece. Those numbers are live. If something breaks, my phone buzzes. I've made my peace with that.</p>
        </div>

        <div className="hire-card">
          <div className="hire-card-eyebrow" style={{ color: accent, display: "flex", alignItems: "center", gap: 6 }}><IconShield color={accent} />Security and ops</div>
          <h3 className="hire-card-title">Cloudflare, CI all the way</h3>
          <p>{FILE_COUNT} user files stored on Cloudflare R2.</p>
          <p>CI/CD with security checks in the pipeline, not bolted on at the end.</p>
        </div>
      </div>
    </div>
  );
}

function StatsSection(props) {
  return window.HoltStats.StatsSection(props);
}

function ContactRealHolt({ accent }) {
  return (
    <figure className="contact-real-holt" style={{ "--contact-accent": accent }}>
      <img
        src="/holt/assets/holt_pixelated.jpg"
        className="contact-real-holt-photo"
        alt="Pixel portrait of the real Holt"
      />
      <figcaption className="contact-real-holt-note">
        this is the real Holt
      </figcaption>
    </figure>
  );
}

function ContactSection({ accent, serif }) {
  return (
    <div>
      <div className="overline" style={{ color: accent }}>Contact</div>
      <h1 className="display" style={{ fontFamily: serif }}>
        Let's talk. <span style={{ color: accent }}>I reply fast.</span>
      </h1>
    <div className="grid about-grid">
      <div>
        <p className="lead">I'm currently open to interesting opportunities: contract, contract-to-hire, or full-time with the right team.</p>
        <p className="lead">AI-first companies preferred. Remote or hybrid is fine.</p>

        {/* Boarding-pass style card - custom UI */}
        <div className="bp" style={{ borderColor: "rgba(255,255,255,0.08)" }}>
          <div className="bp-main">
            <div className="bp-row">
              <div>
                <div className="bp-eyebrow" style={{ color: accent }}>Passenger</div>
                <div className="bp-value" style={{ fontFamily: serif }}>Holt Meyers</div>
              </div>
              <div>
                <div className="bp-eyebrow" style={{ color: accent }}>Class</div>
                <div className="bp-value">First Class</div>
              </div>
            </div>
            <div className="bp-route">
              <div className="bp-port">
                <div className="bp-port-code" style={{ color: accent }}>SOLO</div>
                <div className="bp-port-name">founder, builder</div>
              </div>
              <div className="bp-route-line">
                <span className="bp-route-dot" style={{ background: accent }} />
                <span className="bp-route-arrow">✈</span>
                <span className="bp-route-dot" style={{ background: accent }} />
              </div>
              <div className="bp-port" style={{ textAlign: "right" }}>
                <div className="bp-port-code" style={{ color: accent }}>YOU</div>
                <div className="bp-port-name">your next sprint</div>
              </div>
            </div>
            <div className="bp-row bp-row-meta">
              <div><div className="bp-eyebrow">Availability</div><div className="bp-meta">Now</div></div>
              <div><div className="bp-eyebrow">Notice</div><div className="bp-meta">2 weeks</div></div>
              <div><div className="bp-eyebrow">Mode</div><div className="bp-meta">Remote / hybrid</div></div>
              <div><div className="bp-eyebrow">Timezone</div><div className="bp-meta">AEDT (UTC+11)</div></div>
            </div>
          </div>
          <div className="bp-stub">
            <div className="bp-stub-eyebrow" style={{ color: accent }}>Boarding</div>
            <div className="bp-stub-num" style={{ fontFamily: serif }}>HM-2026</div>
            <div className="bp-stub-bars" aria-hidden="true">
              {Array.from({ length: 22 }).map((_, i) => (
                <span key={i} className="bp-stub-bar" style={{ width: (i % 4 === 0 ? 3 : i % 3 === 0 ? 2 : 1) + "px" }} />
              ))}
            </div>
          </div>
        </div>

        <a href="mailto:holt.meyers@gmail.com" className="big-link" style={{ borderColor: accent, color: "var(--fg-primary)" }}>
          <span className="big-link-icon" style={{ background: accent }}>@</span>
          <span>
            <span className="big-link-eyebrow">Email</span>
            <span className="big-link-handle">holt.meyers@gmail.com</span>
          </span>
          <CopyBtn value="holt.meyers@gmail.com" />
        </a>
        <a href="tel:+61488099405" className="big-link" style={{ borderColor: accent, color: "var(--fg-primary)" }}>
          <span className="big-link-icon" style={{ background: accent }}>☎</span>
          <span>
            <span className="big-link-eyebrow">Phone</span>
            <span className="big-link-handle">0488 099 405</span>
          </span>
          <CopyBtn value="0488099405" />
        </a>
        <a href="https://github.com/holtmeister" target="_blank" rel="noreferrer" className="big-link" style={{ borderColor: accent, color: "var(--fg-primary)" }}>
          <span className="big-link-icon" style={{ background: accent }}>↗</span>
          <span>
            <span className="big-link-eyebrow">GitHub</span>
            <span className="big-link-handle">github.com/holtmeister</span>
          </span>
          <CopyBtn value="github.com/holtmeister" />
        </a>
        <a href="https://filefactory.com" target="_blank" rel="noreferrer" className="big-link" style={{ borderColor: accent, color: "var(--fg-primary)" }}>
          <span className="big-link-icon" style={{ background: accent }}>↗</span>
          <span>
            <span className="big-link-eyebrow">Main project</span>
            <span className="big-link-handle">filefactory.com</span>
          </span>
          <CopyBtn value="filefactory.com" />
        </a>
      </div>
      <ContactRealHolt accent={accent} />
    </div>
    </div>
  );
}

/* ---------- Copy to clipboard ---------- */
function useCopy() {
  const [copied, setCopied] = useState(false);
  function copy(text) {
    navigator.clipboard.writeText(text).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  }
  return [copied, copy];
}

function CopyBtn({ value, accent }) {
  const [copied, copy] = useCopy();
  return (
    <button
      className={"copy-btn" + (copied ? " copied" : "")}
      onClick={(e) => { e.preventDefault(); e.stopPropagation(); copy(value); }}
      title={copied ? "Copied!" : "Copy"}
      aria-label={copied ? "Copied" : "Copy to clipboard"}
    >
      {copied
        ? <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
        : <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect width="14" height="14" x="8" y="8" rx="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
      }
    </button>
  );
}

/* ---------- HOLT ATTACK modal ---------- */
const GAME_MODAL_CANVAS = { width: 600, height: 800 };

function GameModal({ open, onClose }) {
  const topbarRef = useRef(null);
  const iframeRef = useRef(null);
  const [frameSize, setFrameSize] = useState(GAME_MODAL_CANVAS);

  useEffect(() => {
    if (!open) return;

    function fitGameFrame() {
      const viewport = window.visualViewport;
      const viewportWidth = viewport?.width || window.innerWidth;
      const viewportHeight = viewport?.height || window.innerHeight;
      const overlayPad = viewportWidth <= 760 ? 10 : Math.min(Math.max(viewportWidth * 0.03, 16), 36);
      const topbarHeight = topbarRef.current?.offsetHeight || (viewportWidth <= 760 ? 70 : 86);
      const maxWidth = Math.max(260, viewportWidth - overlayPad * 2 - 2);
      const maxHeight = Math.max(320, viewportHeight - overlayPad * 2 - topbarHeight - 2);
      const scale = Math.min(
        1,
        maxWidth / GAME_MODAL_CANVAS.width,
        maxHeight / GAME_MODAL_CANVAS.height,
      );
      const next = {
        width: Math.floor(GAME_MODAL_CANVAS.width * scale),
        height: Math.floor(GAME_MODAL_CANVAS.height * scale),
      };

      setFrameSize((current) => (
        current.width === next.width && current.height === next.height ? current : next
      ));
    }

    fitGameFrame();
    const frame = window.requestAnimationFrame(fitGameFrame);
    window.addEventListener("resize", fitGameFrame);
    window.visualViewport?.addEventListener("resize", fitGameFrame);
    return () => {
      window.cancelAnimationFrame(frame);
      window.removeEventListener("resize", fitGameFrame);
      window.visualViewport?.removeEventListener("resize", fitGameFrame);
    };
  }, [open]);

  useEffect(() => {
    if (!open) return;
    const previousOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    function onKeyDown(e) {
      if (e.key === "Escape") onClose();
    }
    window.addEventListener("keydown", onKeyDown);
    return () => {
      window.removeEventListener("keydown", onKeyDown);
      document.body.style.overflow = previousOverflow;
    };
  }, [open, onClose]);

  useEffect(() => {
    if (!open) return;

    function focusGameFrame() {
      iframeRef.current?.focus();
      iframeRef.current?.contentWindow?.focus();
    }

    const frame = window.requestAnimationFrame(focusGameFrame);
    iframeRef.current?.addEventListener("load", focusGameFrame);
    return () => {
      window.cancelAnimationFrame(frame);
      iframeRef.current?.removeEventListener("load", focusGameFrame);
    };
  }, [open]);

  if (!open) return null;

  const frameStyle = {
    "--game-frame-width": `${frameSize.width}px`,
    "--game-frame-height": `${frameSize.height}px`,
  };

  return (
    <div className="game-modal-overlay" role="dialog" aria-modal="true" aria-label="HOLT ATTACK" onClick={onClose}>
      <div className="game-modal-shell" style={frameStyle} onClick={(e) => e.stopPropagation()}>
        <div className="game-modal-topbar" ref={topbarRef}>
          <div>
            <div className="game-modal-kicker">Now playing</div>
            <div className="game-modal-title">HOLT ATTACK</div>
          </div>
          <button className="game-modal-close" type="button" aria-label="Close HOLT ATTACK" onClick={onClose}>
            <svg viewBox="0 0 24 24" aria-hidden="true">
              <path d="M18 6 6 18" />
              <path d="m6 6 12 12" />
            </svg>
          </button>
        </div>
        <div className="game-modal-stage">
          <iframe ref={iframeRef} className="game-modal-frame" src="/holt/game" title="HOLT ATTACK game" />
        </div>
      </div>
    </div>
  );
}

/* ---------- Mobile modal ---------- */
function MobileModal({ accent }) {
  const [dismissed, setDismissed] = useState(() => window.innerWidth >= 768);
  if (dismissed) return null;
  return (
    <div className="mobile-modal-overlay">
      <div className="mobile-modal">
        <div className="mobile-modal-emoji">💻</div>
        <p className="mobile-modal-text">Holt is best viewed on a desktop :)</p>
        <button
          className="mobile-modal-btn"
          style={{ background: accent }}
          onClick={() => setDismissed(true)}
        >
          OK
        </button>
      </div>
    </div>
  );
}

/* ---------- App ---------- */
function App() {
  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "accent": "music",
    "serif": "Merriweather"
  }/*EDITMODE-END*/;
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  const accentKey = ACCENTS[t.accent] ? t.accent : "transfer";
  const accent = ACCENTS[accentKey].hex;
  const accentSoft = ACCENTS[accentKey].soft;
  const serif = t.serif === "Inter"
    ? "var(--font-sans)"
    : "'Merriweather', Georgia, serif";

  // URL ↔ tab/sub mapping
  const TAB_TO_SLUG = { about: "who", work: "work", hire: "hire", stats: "stats", contact: "contact" };
  const SLUG_TO_TAB = Object.fromEntries(Object.entries(TAB_TO_SLUG).map(([k,v]) => [v, k]));
  const VALID_SUBS = ["overview", "migration", "transfer", "helpdesk", "stack"];

  function parseLocation() {
    const parts = window.location.pathname.replace(/^\/holt\/?/, "").split("/").filter(Boolean);
    const tabSlug = parts[0] || "who";
    const tab = SLUG_TO_TAB[tabSlug] || "about";
    const sub = (tab === "work" && parts[1] && VALID_SUBS.includes(parts[1])) ? parts[1] : "overview";
    return { tab, sub };
  }

  const initial = parseLocation();
  const [tab, setTabState] = useState(initial.tab);
  const [sub, setSubState] = useState(initial.sub);

  function setTab(nextTab) {
    const slug = TAB_TO_SLUG[nextTab] || nextTab;
    const url = nextTab === "work" ? `/holt/work/${sub}` : `/holt/${slug}`;
    history.pushState(null, "", url);
    setTabState(nextTab);
  }

  function setSub(nextSub) {
    history.pushState(null, "", `/holt/work/${nextSub}`);
    setSubState(nextSub);
  }

  useEffect(() => {
    function onPop() {
      const { tab, sub } = parseLocation();
      setTabState(tab);
      setSubState(sub);
    }
    window.addEventListener("popstate", onPop);
    return () => window.removeEventListener("popstate", onPop);
  }, []);

  const [menuOpen, setMenuOpen] = useState(false);

  const [konami, setKonami] = useState(false);
  const [gameOpen, setGameOpen] = useState(false);
  function openGameModal(e) {
    if (e) e.preventDefault();
    setGameOpen(true);
  }
  function closeGameModal() {
    setGameOpen(false);
  }

  useKonami(() => setKonami(true));
  useEffect(() => {
    if (!konami) return;
    const onKey = (e) => { if (e.key === "Escape") setKonami(false); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [konami]);

  const mood = tab === "work" ? "work" : tab === "hire" ? "hire" : tab === "stats" ? "stats" : tab === "contact" ? "contact" : "wave";

  // Map accent hex back to the key when the color picker changes
  const accentHexToKey = Object.fromEntries(Object.entries(ACCENTS).map(([k,v]) => [v.hex, k]));

  return (
    <div className="page" style={{ "--accent": accent, "--accent-soft": accentSoft }} data-screen-label={`Resume - ${TABS.find(x => x.id === tab)?.label || ""}`}>
      <MobileModal accent={accent} />
      <div className="ambient" style={{ background: `radial-gradient(60% 50% at 50% 0%, ${accent}22, transparent 70%)` }} />

      <header className="header">
        <a href="/holt/" className="brand" style={{ textDecoration: "none" }}>
          <div className="brand-mark" style={{ color: accent }}>HM</div>
          <div className="brand-text">
            <div className="brand-name">Holt Meyers</div>
            <div className="brand-role">
              <span>Founder · Full-Stack · AI-First</span>
              <span className="brand-info" tabIndex="0" aria-label="More about Holt">
                <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
                  <circle cx="6" cy="6" r="5.25" stroke="currentColor" strokeWidth="1" />
                  <circle cx="6" cy="3.4" r="0.7" fill="currentColor" />
                  <rect x="5.4" y="5.2" width="1.2" height="3.8" rx="0.4" fill="currentColor" />
                </svg>
                <span className="brand-tip" role="tooltip">
                  <span className="brand-tip-eyebrow" style={{ color: accent }}>About</span>
                  <span className="brand-tip-body">AI-native engineer. Founder, builder, full-stack. Currently available.</span>
                </span>
              </span>
            </div>
          </div>
        </a>
        <Pills tabs={TABS} value={tab} onChange={setTab} accent={accent} />
        <a href="https://github.com/holtmeister" target="_blank" rel="noreferrer" className="cta cta-outline header-github" style={{ borderColor: accent, color: accent }}>
          GitHub →
        </a>
        <button className="hamburger" aria-label="Menu" onClick={() => setMenuOpen(v => !v)}>
          <span className={"ham-icon" + (menuOpen ? " ham-open" : "")}>
            <span /><span /><span />
          </span>
        </button>
      </header>

      {menuOpen && (
        <div className="mobile-nav" onClick={() => setMenuOpen(false)}>
          {TABS.map(t => t.href ? (
            <a key={t.id} href={t.href} className={"mobile-nav-item" + (t.id === tab ? " is-active" : "")} style={t.id === tab ? { color: accent } : {}}>
              {t.label}
            </a>
          ) : (
            <button key={t.id} className={"mobile-nav-item" + (t.id === tab ? " is-active" : "")} style={t.id === tab ? { color: accent } : {}}
              onClick={() => { setTab(t.id); setMenuOpen(false); }}>
              {t.label}
            </button>
          ))}
          <a href="https://github.com/holtmeister" target="_blank" rel="noreferrer" className="mobile-nav-item mobile-nav-github" style={{ color: accent }}>
            GitHub →
          </a>
        </div>
      )}

      <main className="panel" key={tab}>
        {tab === "about"   && <AboutSection accent={accent} serif={serif} onOpenGame={openGameModal} />}
        {tab === "work"    && <WorkSection accent={accent} serif={serif} sub={sub} setSub={setSub} />}
        {tab === "hire"    && <HireSection accent={accent} serif={serif} />}
        {tab === "stats"   && <StatsSection accent={accent} serif={serif} />}
        {tab === "contact" && <ContactSection accent={accent} serif={serif} />}
        {(() => {
          const next = { about: ["work", "Now the proof."], work: ["hire", "Still weighing it up?"], hire: ["stats", "Let the numbers talk."], stats: ["contact", "Seen enough?"] }[tab];
          if (!next) return null;
          const [nextId, teaser] = next;
          const nextLabel = TABS.find(t => t.id === nextId)?.label;
          return (
            <div className="next-page-wrap">
              <button className="next-page-link" onClick={() => { setTab(nextId); window.scrollTo({ top: 0, behavior: "smooth" }); }} style={{ "--np-accent": accent }}>
                <span className="next-page-teaser">{teaser}</span>
                <span className="next-page-label">{nextLabel} <span className="next-page-arrow">→</span></span>
              </button>
            </div>
          );
        })()}
      </main>

      <section className="terminal-wrap">
        <div className="terminal-eyebrow">
          <span className="overline" style={{ color: accent }}>Poke around</span>
          <span className="terminal-hint">try <code>whoami</code>, <code>ship</code>, <code>ai</code>, <code>konami</code></span>
        </div>
        <Terminal accent={accent} />
      </section>

      <footer className="foot">
        <a href="/holt/game" className="foot-game-attract" aria-label="Play HOLT ATTACK" onClick={openGameModal}>
          <span className="foot-game-track" aria-hidden="true">
            <span className="foot-game-runner">
              <span className="foot-game-sprite foot-sprite-a" />
              <span className="foot-game-sprite foot-sprite-b" />
            </span>
          </span>
          <span className="foot-game-copy">SHALL WE... PLAY A GAME?</span>
        </a>
        <nav className="foot-nav">
          {TABS.map(t => t.href ? (
            <a key={t.id} href={t.href} className={"foot-nav-item" + (tab === t.id ? " is-active" : "")} style={tab === t.id ? { color: accent } : {}}>
              {t.label}
            </a>
          ) : (
            <button key={t.id} className={"foot-nav-item" + (tab === t.id ? " is-active" : "")} onClick={() => { setTab(t.id); window.scrollTo({ top: 0, behavior: "smooth" }); }} style={tab === t.id ? { color: accent } : {}}>
              {t.label}
            </button>
          ))}
        </nav>
        <div className="foot-meta">
          <span>© Holt Meyers</span>
          <span className="foot-sep">·</span>
          <span>Hand-rolled, AI-assisted</span>
          <span className="foot-sep">·</span>
          <span>Built on the FileFactory design system</span>
          <span className="foot-sep">·</span>
          <span className="foot-build-pill">
            <span className="foot-build-label">⚡ Built in 5.5 hrs</span>
            <span className="foot-build-tooltip">
              <span className="fbt-header">This app - by the numbers</span>
              <span className="fbt-divider" />
              <span className="fbt-row"><span className="fbt-key">Design</span><span className="fbt-val">2 hrs · Claude Design</span></span>
              <span className="fbt-row"><span className="fbt-key">Code</span><span className="fbt-val">3.5 hrs · Claude Code</span></span>
              <span className="fbt-row fbt-row-total"><span className="fbt-key">Total</span><span className="fbt-val fbt-val-accent">5.5 hrs</span></span>
              <span className="fbt-divider" />
              <span className="fbt-row"><span className="fbt-key">Commits</span><span className="fbt-val">3</span></span>
              <span className="fbt-row"><span className="fbt-key">Lines written</span><span className="fbt-val">~8,200</span></span>
              <span className="fbt-row"><span className="fbt-key">Files touched</span><span className="fbt-val">15</span></span>
              <span className="fbt-divider" />
              <span className="fbt-row fbt-row-note">This is what AI-first looks like in practice.</span>
            </span>
          </span>
        </div>
        <button
          className="foot-tweaks-btn"
          onClick={() => window.postMessage({ type: '__activate_edit_mode' }, '*')}
          style={{ "--tw-accent": accent }}
          aria-label="Open color tweaks"
        >
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 1 1-14.14 0"/></svg>
          Tweak colors
        </button>
      </footer>

      <GameModal open={gameOpen} onClose={closeGameModal} />

      {konami && (
        <div className="konami" onClick={() => setKonami(false)}>
          <div className="konami-card" onClick={(e) => e.stopPropagation()}>
            <div className="konami-scanlines" aria-hidden="true" />
            <div className="konami-grid">
              <div className="konami-mascot">

                <div className="konami-mascot-glow" style={{ background: `radial-gradient(circle at 50% 50%, ${accent}66, transparent 65%)` }} />
                <img src="/holt/assets/mascot_peace.png" alt="" />
              </div>
              <div className="konami-text">
                <h2 className="konami-title" style={{ fontFamily: serif }}>The Konami Code still works.</h2>
                <p className="konami-body">Not many people try that. You're already my kind of person.</p>
                <div className="konami-keys" aria-hidden="true">
                  {["↑","↑","↓","↓","←","→","←","→"].map((k, i) => (
                    <span key={i} className="konami-key" style={{ animationDelay: (i * 60) + "ms" }}>{k}</span>
                  ))}
                </div>
                <button className="konami-dismiss" onClick={() => setKonami(false)} style={{ borderColor: accent + "66", color: accent }}>
                  Dismiss <span className="konami-dismiss-kbd">esc</span>
                </button>
              </div>
            </div>
          </div>
        </div>
      )}

      <TweaksPanel title="Tweaks">
        <TweakSection label="Accent" />
        <TweakColor
          label="Color"
          value={ACCENTS[accentKey].hex}
          options={Object.values(ACCENTS).map(a => a.hex)}
          onChange={(hex) => setTweak("accent", accentHexToKey[hex] || "transfer")}
        />
        <TweakSection label="Display font" />
        <TweakRadio
          label="Serif"
          value={t.serif}
          options={["Merriweather", "Inter"]}
          onChange={(v) => setTweak("serif", v)}
        />
      </TweaksPanel>
    </div>
  );
}

function Root() {
  const forParam = new URLSearchParams(window.location.search).get("for");
  const [splashDone, setSplashDone] = useState(() => forParam !== "transportme");
  if (!splashDone) {
    return <Splash accent="#FF8A3D" onEnter={() => setSplashDone(true)} />;
  }
  return <App />;
}

ReactDOM.createRoot(document.getElementById("root")).render(<Root />);
