/* Direction A — "The Operator"
   Live terminal/REPL portfolio. Phosphor green on graphite.
   Sections: Boot/Hero → whoami → ai-stack → ls work → timeline → contact
*/

const { useState, useEffect, useRef, useMemo, useCallback } = React;

const opStyles = {
  root: {
    fontFamily: "'JetBrains Mono', ui-monospace, monospace",
    background: "#0a0d0c",
    color: "#cfe8d6",
    width: "100%",
    minHeight: "100%",
    position: "relative",
    overflow: "clip",
    backgroundImage: `
      radial-gradient(ellipse at 20% 0%, rgba(123,255,170,0.08), transparent 50%),
      radial-gradient(ellipse at 100% 80%, rgba(80,200,255,0.04), transparent 60%),
      linear-gradient(180deg, #080a09 0%, #0a0d0c 100%)
    `,
  },
  scanlines: {
    position: "absolute", inset: 0, pointerEvents: "none",
    backgroundImage: "repeating-linear-gradient(0deg, rgba(123,255,170,0.025) 0 1px, transparent 1px 3px)",
    mixBlendMode: "overlay",
    zIndex: 1,
  },
  grain: {
    position: "absolute", inset: 0, pointerEvents: "none", opacity: 0.5,
    backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.7  0 0 0 0 1  0 0 0 0 0.8  0 0 0 0.06 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>")`,
    zIndex: 1,
  },
};

const PHOS = "#7bffaa";
const PHOS_DIM = "#3aa572";
const AMBER = "#ffc46a";
const RED = "#ff6b6b";
const BLUE = "#7ad0ff";

/* ---------- i18n ---------- */

const LangCtx = React.createContext({ lang: "no", setLang: () => {} });
const useLang = () => React.useContext(LangCtx).lang;

const I18N = {
  plainHero: {
    name: "Oleksandr Altukhov",
    tag: {
      no: "Lege (cand.med., jevngod med norsk legeutdanning) · bygger KI- og agent-systemer · Kristiansund",
      en: "Physician (M.D., assessed equivalent to a Norwegian medical degree) · builds AI & agent systems · Kristiansund",
    },
    oneLiner: {
      no: "Jeg er medisinsk utdannet, bruker Helseplattformen daglig fra brukersiden, og bygger egne dataverktøy og KI-systemer i Python — fra behov til løsning som virker. Broen mellom klinikk og teknologi.",
      en: "I'm medically trained, use Helseplattformen daily from the user side, and build my own data tools and AI systems in Python — from need to working solution. The bridge between clinic and technology.",
    },
    offers: [
      {
        no: "Klinisk forståelse + systembygging — jeg har jobbet i begge verdener",
        en: "Clinical understanding + systems building — I've worked in both worlds",
      },
      {
        no: "KI- og agent-systemer fra behov til løsning i drift — ~8 900 tester på tvers av prosjektene",
        en: "AI and agent systems from need to running solution — ~8,900 tests across projects",
      },
      {
        no: "Daglig Helseplattformen-bruker — kjenner klinikerens hverdag fra innsiden",
        en: "Daily Helseplattformen user — I know the clinician's workday from the inside",
      },
    ],
    cvBtn: { no: "Last ned CV (PDF)", en: "Download CV (PDF)" },
    cvHref: { no: "/cv/oleksandr-altukhov-no.pdf", en: "/cv/oleksandr-altukhov-en.pdf" },
    scrollHint: { no: "↓ teknisk dypdykk i terminalen nedenfor", en: "↓ technical deep-dive in the terminal below" },
  },
  boot: {
    no: [
      { kind: "sys",  text: "init oleksandr.altukhov ── oppstart v2026.06" },
      { kind: "ok",   text: "[ ok ] monterer / · lege + bygger · Kristiansund" },
      { kind: "ok",   text: "[ ok ] monterer /opt · medisin + markedsføring, parallelt" },
      { kind: "ok",   text: "[ ok ] starter tjeneste: claude-agent-sdk (kapabilitet-styrt)" },
      { kind: "ok",   text: "[ ok ] starter tjeneste: blast-radius + tri-review-panel" },
      { kind: "ok",   text: "[ ok ] starter tjeneste: tenant-scoped-lint (multi-tenant guard)" },
      { kind: "warn", text: "[ .. ] laster ledger · ~8 900 tester grønne på tvers av flåten" },
      { kind: "ok",   text: "[ ok ] første kunde live. klar til arbeid." },
    ],
    en: [
      { kind: "sys",  text: "init oleksandr.altukhov ── boot v2026.06" },
      { kind: "ok",   text: "[ ok ] mounting / · physician + builder · Kristiansund" },
      { kind: "ok",   text: "[ ok ] mounting /opt · medicine + marketing, in parallel" },
      { kind: "ok",   text: "[ ok ] starting service: claude-agent-sdk (capability-gated)" },
      { kind: "ok",   text: "[ ok ] starting service: blast-radius + tri-review-panel" },
      { kind: "ok",   text: "[ ok ] starting service: tenant-scoped-lint (multi-tenant guard)" },
      { kind: "warn", text: "[ .. ] loading ledger · ~8,900 tests green across the fleet" },
      { kind: "ok",   text: "[ ok ] first client live. ready for work." },
    ],
  },
  hero: {
    badge: {
      no: "KLINIKK × KI · E-HELSE · NORGE",
      en: "CLINIC × AI · E-HEALTH · NORWAY",
    },
    sub: {
      no: "→ lege som bygger KI/agent-systemer. multi-tenant, revisjonsspor, ~8 900 tester. første kunde live.",
      en: "→ physician who builds AI/agent systems. multi-tenant, audit trails, ~8,900 tests. first client live.",
    },
  },
  whoami: {
    cmd: "whoami --verbose",
    role:        { label: { no: "rolle",      en: "role" },          value: { no: "bygger · klinikk × KI", en: "builder · clinic × AI" } },
    background:  { label: { no: "bakgrunn",   en: "background" } },
    based:       { label: { no: "basert",     en: "based" },         value: { no: "Kristiansund, Norge", en: "Kristiansund, Norway" } },
    health:      { label: { no: "helse",      en: "health" },        value: { no: "jobber i helsetjenesten i Kristiansund · medikamentansvar · Helseplattformen daglig", en: "working in healthcare in Kristiansund · medication responsibility · Helseplattformen daily" } },
    languages:   { label: { no: "språk",      en: "languages" } },
    training:    { label: { no: "utdanning",  en: "training" },      value: { no: "cand.med. — jevngod med norsk legeutdanning (Helsedirektoratet + NOKUT)", en: "M.D. — assessed equivalent to a Norwegian medical degree (Helsedirektoratet + NOKUT)" } },
    operating_mode: { label: { no: "arbeidsmåte", en: "operating_mode" } },
    currently:   { label: { no: "nå",         en: "currently" } },
    bgLead:      { no: "medisin",             en: "medicine" },
    bgTail:      { no: " markedsføring — studerte og jobbet", en: " marketing — studied & worked" },
    bgHi:        { no: " parallelt",          en: " in parallel" },
    bgEnd:       { no: ", ikke i sekvens.",   en: ", not in sequence." },
    omPre:       { no: "parallell-bygger med fryst kontrakt.", en: "parallel-worker refactor with frozen contracts." },
    omHi:        { no: " 3–6 reviewere per merge",      en: " 3–6 reviewers per merge" },
    omPost:      { no: ", separate feilklasser, triangulering fanger det én reviewer mister. commit-på-grønt per fase — bisecterbar historikk, ikke vibe-merge. blast-radius-kart før hver review.", en: ", disjoint failure classes, triangulation catches what one reviewer misses. commit-on-green per phase — bisectable history, not vibe-merges. blast-radius map before every review." },
    curPre:      { no: "leverer ",          en: "shipping " },
    curHi:       { no: "Meria — Team OS for PM International (UA)", en: "Meria — Team OS for PM International (UA)" },
    curPost:     { no: ". alfa live · 5 871 tester · 277 audit-funn lukket over 16 parallelle Opus-spor · 3-språks paritet (UA/EN/RU) · tenant-isolasjon håndhevet på skjema- og commit-tid.", en: ". alpha live · 5,871 tests · 277 audit findings closed across 16 parallel Opus lanes · 3-language parity (UA/EN/RU) · tenant isolation enforced at schema + commit-time." },
  },
  shiplog: {
    cmd: "tail -f ship.log",
    sumProjects: { no: "systemer", en: "systems" },
    sumLive:     { no: "første kunde live", en: "first client live" },
    governance:  { no: "styring:", en: "governance:" },
    govV:        { no: "tri-review · blast-radius · commit-på-grønt", en: "tri-review · blast-radius · commit-on-green" },
    period:      { no: "mai–juni 2026", en: "may–june 2026" },
    footer:      { no: "ikke en demo. dette er det jeg leverte i mai–juni.", en: "not a demo. this is what i shipped in may–june." },
    metaLine:    { no: "henter fra wiki-loggen — hver linje er falsifiserbar.", en: "pulled from the wiki log — every line is falsifiable." },
  },
  manifesto: {
    cmd: { no: "cat ./slik_jobber_jeg.md", en: "cat ./how_i_work.md" },
    title: { no: "slik jobber jeg", en: "how i work" },
    rules: {
      no: [
        "hver diff har et blast-radius-kart før review",
        "hver fase commiter på grønt — ellers commiter den ikke",
        "3–6 Opus-reviewere per merge, separate feilklasser",
        "multi-tenant-isolasjon er en lint-regel, ikke et håp",
        "krise-spor kjører før LLM, ikke etter",
        "hvis systemet ikke kan revideres, leveres det ikke",
      ],
      en: [
        "every diff has a blast-radius map before review",
        "every phase commits on green — or it does not commit",
        "3–6 Opus reviewers per merge, disjoint failure classes",
        "multi-tenant isolation is a lint rule, not a hope",
        "crisis paths run before the LLM, not after",
        "if the system cannot be audited, it does not ship",
      ],
    },
  },
  patterns: {
    cmd: "ls ./patterns/",
    sub: { no: "egne mønstre, ikke verktøy. forsvarbart på samtale.", en: "patterns i invented, not tools i use. defensible on a call." },
  },
  stack: { cmd: "cat tools.yaml" },
  workCmd: "ls -la ./selected_work",
  timelineCmd: "git log --oneline experience",
  contact: {
    cmd: "./contact --open",
    sub: {
      no: "→ tilgjengelig for stillinger innen e-helse, helsedata og forskning · Kristiansund · CET. e-post eller telefon er raskest ↓",
      en: "→ available for roles in e-health, health data and research · Kristiansund · CET. email or phone is fastest ↓",
    },
  },
  chrome: {
    online: { no: "● tilkoblet", en: "● online" },
  },
};

/* ---------- low-level building blocks ---------- */

function Prompt({ user = "oleksandr", host = "altukhov.dev", path = "~", children, sym = "$" }) {
  return (
    <div style={{ display: "flex", gap: 8, alignItems: "baseline", flexWrap: "wrap" }}>
      <span style={{ color: PHOS }}>{user}@{host}</span>
      <span style={{ color: "#5e6f66" }}>:</span>
      <span style={{ color: BLUE }}>{path}</span>
      <span style={{ color: "#5e6f66" }}>{sym}</span>
      <span style={{ color: "#e8f5ec" }}>{children}</span>
    </div>
  );
}

function Caret({ blink = true, char = "█" }) {
  return (
    <span style={{
      display: "inline-block", color: PHOS,
      animation: blink ? "op-blink 1s steps(2,start) infinite" : "none",
      transform: "translateY(1px)",
      marginLeft: 2,
    }}>{char}</span>
  );
}

/* Typewriter that types lines sequentially. onDone fires once. */
function Typewriter({ lines, speed = 14, startDelay = 0, chunk = 3, onDone, render }) {
  const [out, setOut] = useState([]);
  const [lineIdx, setLineIdx] = useState(0);
  const [charIdx, setCharIdx] = useState(0);
  const doneRef = useRef(false);

  useEffect(() => {
    if (lineIdx >= lines.length) {
      if (!doneRef.current) { doneRef.current = true; onDone && onDone(); }
      return;
    }
    const line = lines[lineIdx];
    const text = typeof line === "string" ? line : line.text;
    const delay = lineIdx === 0 && charIdx === 0 ? startDelay : (typeof line === "object" && line.speed) || speed;
    const step = (typeof line === "object" && line.chunk) || chunk;

    if (charIdx <= text.length) {
      const t = setTimeout(() => setCharIdx(Math.min(text.length, charIdx + step)), delay);
      return () => clearTimeout(t);
    } else {
      const t = setTimeout(() => {
        setOut((o) => [...o, line]);
        setLineIdx(lineIdx + 1);
        setCharIdx(0);
      }, (typeof line === "object" && line.pause) || 40);
      return () => clearTimeout(t);
    }
  }, [lineIdx, charIdx, lines, speed, startDelay, chunk, onDone]);

  const cur = lines[lineIdx];
  const curText = typeof cur === "string" ? cur : cur?.text || "";
  const shown = curText.slice(0, charIdx);

  return (
    <>
      {out.map((l, i) => <React.Fragment key={i}>{render(l, i, true)}</React.Fragment>)}
      {lineIdx < lines.length && (
        <React.Fragment>
          {render({ ...(typeof cur === "object" ? cur : { text: cur }), text: shown, _typing: true }, lineIdx, false)}
        </React.Fragment>
      )}
    </>
  );
}

/* ---------- boot sequence ---------- */

function BootSequence({ onDone }) {
  const lang = useLang();
  const lines = useMemo(() => I18N.boot[lang], [lang]);

  const PER_LINE_MS = 160;
  const TOTAL_MS = lines.length * PER_LINE_MS + 200;

  useEffect(() => {
    const t = setTimeout(() => onDone && onDone(), TOTAL_MS);
    return () => clearTimeout(t);
  }, [onDone, TOTAL_MS]);

  return (
    <div style={{ fontSize: 13, color: "#9fb5a8", minHeight: 180 }}>
      {lines.map((line, i) => {
        const c = line.kind === "ok" ? PHOS
                : line.kind === "warn" ? AMBER
                : line.kind === "sys" ? BLUE
                : "#cfe8d6";
        return (
          <div key={i}
            className="op-boot-line"
            style={{
              color: c, lineHeight: 1.7, fontSize: 13,
              opacity: 0,
              animation: `op-bootline 220ms ease-out ${i * PER_LINE_MS}ms forwards`,
            }}>
            {line.text}
          </div>
        );
      })}
    </div>
  );
}

/* ---------- hero name banner ---------- */

function NameBanner({ visible }) {
  const lang = useLang();
  return (
    <div style={{
      marginTop: 18,
      opacity: visible ? 1 : 0,
      transform: visible ? "translateY(0)" : "translateY(8px)",
      transition: "opacity 600ms ease, transform 600ms ease",
    }}>
      <div className="op-name" style={{
        fontFamily: "'JetBrains Mono', monospace",
        fontWeight: 700,
        fontSize: 64,
        lineHeight: 0.95,
        letterSpacing: "-0.04em",
        color: "#eafff1",
        textShadow: `0 0 24px rgba(123,255,170,0.25), 0 0 2px rgba(123,255,170,0.4)`,
      }}>
        OLEKSANDR
        <br />
        ALTUKHOV<span style={{ color: PHOS }}>_</span>
      </div>
      <div style={{
        marginTop: 14,
        display: "flex", gap: 12, alignItems: "center", flexWrap: "wrap",
        fontSize: 12, color: "#9fb5a8",
      }}>
        <span style={{
          padding: "3px 9px",
          border: `1px solid ${PHOS_DIM}`,
          color: PHOS,
          borderRadius: 2,
          fontSize: 10,
          letterSpacing: "0.08em",
        }}>{I18N.hero.badge[lang]}</span>
        <span>{I18N.hero.sub[lang]}</span>
      </div>
    </div>
  );
}

/* ---------- section: whoami ---------- */

function WhoamiSection() {
  const lang = useLang();
  const [open, setOpen] = useState(false);
  useEffect(() => { const t = setTimeout(() => setOpen(true), 200); return () => clearTimeout(t); }, []);
  const W = I18N.whoami;

  return (
    <Section command={W.cmd}>
      <div className="op-whoami" style={{
        opacity: open ? 1 : 0, transition: "opacity 600ms ease",
        display: "grid", gridTemplateColumns: "160px 1fr", gap: "8px 16px", fontSize: 13, lineHeight: 1.55,
      }}>
        <div style={{ color: PHOS_DIM }}>{W.role.label[lang]}</div>
        <div>{W.role.value[lang]}</div>

        <div style={{ color: PHOS_DIM }}>{W.background.label[lang]}</div>
        <div>
          {W.bgLead[lang]} <span style={{ color: PHOS_DIM }}>+</span>{W.bgTail[lang]}
          <span style={{ color: BLUE }}>{W.bgHi[lang]}</span>{W.bgEnd[lang]}
        </div>

        <div style={{ color: PHOS_DIM }}>{W.based.label[lang]}</div>
        <div>{W.based.value[lang]}</div>

        <div style={{ color: PHOS_DIM }}>{W.health.label[lang]}</div>
        <div>{W.health.value[lang]}</div>

        <div style={{ color: PHOS_DIM }}>{W.languages.label[lang]}</div>
        <div>
          <span style={{ color: BLUE }}>norsk</span> <span style={{ color: "#9fb5a8" }}>B2</span> ·
          <span style={{ color: BLUE }}> english</span> <span style={{ color: "#9fb5a8" }}>C1</span> ·
          <span style={{ color: BLUE }}> українська</span> · <span style={{ color: BLUE }}>русский</span> <span style={{ color: "#9fb5a8" }}>{lang === "no" ? "morsmål" : "native"}</span>
        </div>

        <div style={{ color: PHOS_DIM }}>{W.training.label[lang]}</div>
        <div>{W.training.value[lang]}</div>

        <div style={{ color: PHOS_DIM }}>{W.operating_mode.label[lang]}</div>
        <div>
          {W.omPre[lang]}
          <span style={{ color: BLUE }}>{W.omHi[lang]}</span>{W.omPost[lang]}
        </div>

        <div style={{ color: PHOS_DIM }}>{W.currently.label[lang]}</div>
        <div>
          {W.curPre[lang]}
          <span style={{ color: PHOS }}>{W.curHi[lang]}</span>{W.curPost[lang]}
        </div>
      </div>
    </Section>
  );
}

/* ---------- Section wrapper ---------- */

function Section({ command, children, delay = 0 }) {
  return (
    <div style={{ marginTop: 36, position: "relative" }}>
      <div style={{
        position: "absolute", left: -22, top: 0, bottom: 0, width: 2,
        background: `linear-gradient(180deg, ${PHOS} 0%, transparent 100%)`,
        opacity: 0.4,
      }} />
      <Prompt>{command}<Caret /></Prompt>
      <div style={{ marginTop: 14, paddingLeft: 4 }}>{children}</div>
    </div>
  );
}

/* ---------- AI stack section ---------- */

const PATTERNS = [
  { name: "parallel-worker-refactor",   meta: { no: "frosne type/fil-kontrakter → N samtidige byggere", en: "frozen type/file contracts → N concurrent builders" }, accent: PHOS },
  { name: "ship-then-review-loop",      meta: { no: "bygg til grønt, deretter 3–6 Opus-reviewere per feilklasse", en: "build to green, then 3–6 Opus reviewers by failure class" }, accent: PHOS },
  { name: "blast-radius-mapper",        meta: { no: "kartlegger callers/dependents før hver review", en: "maps callers/dependents before every review" }, accent: BLUE },
  { name: "tenant-scoped-lint",         meta: { no: "håndhever multi-tenant-isolasjon på commit-tid", en: "enforces multi-tenant isolation at commit-time" }, accent: BLUE },
  { name: "crisis-lexicon-shortcircuit",meta: { no: "strukturell sikkerhets-prefilter før LLM (ikke etter)", en: "structural safety pre-filter before LLM (not after)" }, accent: AMBER },
  { name: "capability-gated-perms",     meta: { no: "per-verktøy tillatelseskontrakter på Claude Agent SDK", en: "per-tool permission contracts on Claude Agent SDK" }, accent: AMBER },
];

const STACK = [
  { name: "claude agent sdk", kind: "agent",    meta: "permissions · hooks · custom tools", accent: PHOS },
  { name: "claude code · codex", kind: "agent", meta: "daily orchestration",               accent: PHOS },
  { name: "MCP",              kind: "protocol", meta: "custom servers · connectors",       accent: BLUE },
  { name: "python · uv",      kind: "lang",     meta: "asyncio · pandas · scikit",         accent: BLUE },
  { name: "postgres + pgvector", kind: "db",    meta: "RAG · multi-tenant schemas",        accent: BLUE },
  { name: "fastapi · telegram-bot", kind: "api", meta: "async server · bot framework",     accent: BLUE },
  { name: "react · vite · preact", kind: "ui",  meta: "mini-apps · PWAs · dashboards",     accent: AMBER },
  { name: "fly.io · cloudflare workers", kind: "infra", meta: "deploys · postgis · edge",  accent: AMBER },
  { name: "docker",           kind: "infra",    meta: "agent sandboxes",                   accent: AMBER },
  { name: "notion + obsidian",kind: "knowledge",meta: "context · memory · wiki",           accent: AMBER },
];

function PatternsSection() {
  const lang = useLang();
  const [hover, setHover] = useState(null);
  return (
    <Section command={I18N.patterns.cmd}>
      <div style={{ fontSize: 11, color: PHOS_DIM, marginBottom: 10 }}>
        → {I18N.patterns.sub[lang]}
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
        {PATTERNS.map((p, i) => (
          <div key={p.name}
            onMouseEnter={() => setHover(i)}
            onMouseLeave={() => setHover(null)}
            style={{
              display: "grid",
              gridTemplateColumns: "260px 1fr",
              gap: 14,
              padding: "8px 14px",
              border: `1px solid ${hover === i ? p.accent : "rgba(123,255,170,0.1)"}`,
              background: hover === i ? "rgba(123,255,170,0.04)" : "rgba(255,255,255,0.015)",
              borderRadius: 2,
              transition: "all 180ms ease",
              fontFamily: "'JetBrains Mono', monospace",
            }}>
            <div style={{ color: p.accent, fontSize: 13, fontWeight: 500 }}>{p.name}</div>
            <div style={{ color: "#9fb5a8", fontSize: 12 }}>{p.meta[lang]}</div>
          </div>
        ))}
      </div>
    </Section>
  );
}

function StackSection() {
  return (
    <Section command={I18N.stack.cmd}>
      <div style={{
        display: "grid",
        gridTemplateColumns: "repeat(2, 1fr)",
        gap: 8,
      }}>
        {STACK.map((s) => (
          <div key={s.name}
            style={{
              position: "relative",
              padding: "8px 12px",
              border: "1px solid rgba(123,255,170,0.1)",
              background: "rgba(255,255,255,0.012)",
              borderRadius: 2,
              overflow: "hidden",
            }}>
            <div style={{
              position: "absolute", left: 0, top: 0, bottom: 0, width: 2,
              background: s.accent, opacity: 0.5,
            }} />
            <div style={{ fontSize: 8, color: "#5e6f66", letterSpacing: "0.12em", textTransform: "uppercase" }}>{s.kind}</div>
            <div style={{ marginTop: 1, fontSize: 13, fontWeight: 500, color: "#eafff1", lineHeight: 1.15 }}>{s.name}</div>
            <div style={{ marginTop: 2, fontSize: 10, color: "#9fb5a8" }}>{s.meta}</div>
          </div>
        ))}
      </div>
    </Section>
  );
}

function ManifestoSection() {
  const lang = useLang();
  const M = I18N.manifesto;
  return (
    <Section command={M.cmd[lang]}>
      <div style={{
        padding: "16px 18px",
        border: `1px solid ${PHOS_DIM}`,
        borderRadius: 2,
        background: "linear-gradient(180deg, rgba(123,255,170,0.04), transparent)",
      }}>
        <div style={{ fontSize: 10, color: PHOS, letterSpacing: "0.12em", textTransform: "uppercase", marginBottom: 12 }}>
          # {M.title[lang]}
        </div>
        <ul style={{ listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 8 }}>
          {M.rules[lang].map((r, i) => (
            <li key={i} style={{
              display: "grid",
              gridTemplateColumns: "16px 1fr",
              gap: 10,
              fontSize: 13,
              color: "#cfe8d6",
              lineHeight: 1.5,
            }}>
              <span style={{ color: PHOS }}>—</span>
              <span>{r}</span>
            </li>
          ))}
        </ul>
      </div>
    </Section>
  );
}

/* ---------- Work section ---------- */

const WORK = [
  {
    id: "01", name: "Meria — Team OS",
    role: { no: "privat · alfa · første kunde live", en: "private · alpha · first client live" },
    summary: {
      no: "Multi-tenant team-onboarding + leder-coaching på Telegram. 30-dagers løp, auto-graduering til leder, /coach med samtaleliste sortert på genealogi-dybde. Første kunde: PM International (UA). Tenant-isolasjon håndhevet på skjema- og commit-tid · krise-klassifisering med strukturell short-circuit før LLM.",
      en: "Multi-tenant team onboarding + leader coaching on Telegram. 30-day arc, auto-graduation to leader, /coach call-list sorted by genealogy depth. First client: PM International (UA). Tenant isolation enforced at schema + commit-time · crisis classifier with structural short-circuit before the LLM.",
    },
    tags: ["claude agent sdk", "multi-tenant", "telegram", "rag", "alpha"],
    metric: "5 871",
    metricLabel: { no: "tester grønne · 3 språk", en: "tests green · 3 languages" },
  },
  {
    id: "02", name: "Job-Search OS",
    role: { no: "privat · styrt agentisk system · dogfooding", en: "private · governed agentic system · dogfooding" },
    summary: {
      no: "Kjørte mitt eget norske jobbsøk som et styrt agentisk system. Ingester NAVs offentlige stillingsfeed med trinnvise filtre + fuzzy dedup · Brreg/NACE-oppdagelse med flerfaktor-scoring · skill-orkestrert, skreddersydd CV- og søknadsgenerering med ærlighets-gate (Opus) · menneske-godkjente utsendelser, ingen auto-send · SQLite + Notion dobbel-skriving for sporing.",
      en: "Ran my own Norwegian job search as a governed agentic system. Ingests NAV's public job feed with staged filters + fuzzy dedup · Brreg/NACE discovery with multi-factor scoring · skill-orchestrated, tailored CV/letter generation with an honesty gate (Opus) · human-approved sends, no auto-send · SQLite + Notion dual-write tracking.",
    },
    tags: ["NAV-feed", "brreg/NACE", "honesty-gate", "human-in-the-loop", "sqlite+notion"],
    metric: "~50",
    metricLabel: { no: "søknader + 43 outreach · 1 møte", en: "applications + 43 outreach · 1 meeting" },
  },
  {
    id: "03", name: "Hikari Agent",
    role: { no: "offentlig · agent-infrastruktur", en: "public · agent infrastructure" },
    summary: {
      no: "Personlig agent på Claude Agent SDK. Bi-temporal faktaminne (Park et al. retrieval), kapabilitet-styrt tillatelsessystem, 15+ MCP-servere (7 in-process + 8 eksterne), ~55 verktøy, planlagte jobber.",
      en: "Personal agent on the Claude Agent SDK. Bi-temporal fact memory (Park et al. retrieval), capability-gated permission system, 15+ MCP servers (7 in-process + 8 external), ~55 tools, scheduled jobs.",
    },
    tags: ["claude agent sdk", "MCP", "memory", "permissions"],
    metric: "2 882",
    metricLabel: { no: "tester · 15+ MCP · ~55 verktøy", en: "tests · 15+ MCP · ~55 tools" },
  },
  {
    id: "04", name: "Nærtur",
    role: { no: "offentlig · live", en: "public · live" },
    summary: {
      no: "Mobile-first tursøker for Norge — live på naertur.alksalt.com. PWA på Cloudflare Workers + FastAPI på Fly.io. Prosedyrale per-tur SVG-topokart via djb2-frø (null asset-kostnad). Selv-hostet PostGIS ~$0.15/mnd idle vs $38/mnd managed. 126 tester.",
      en: "Mobile-first hike picker for Norway — live at naertur.alksalt.com. PWA on Cloudflare Workers + FastAPI on Fly.io. Procedural per-hike SVG topo-maps via djb2 seeding (zero asset cost). Self-hosted PostGIS ~$0.15/mo idle vs $38/mo managed. 126 tests.",
    },
    tags: ["public", "PWA", "cloudflare workers", "postgis", "fly.io"],
    metric: "$0.15",
    metricLabel: { no: "/mnd idle-infra", en: "/mo idle infra" },
  },
];

function WorkSection() {
  const lang = useLang();
  return (
    <Section command={I18N.workCmd}>
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {WORK.map((w, i) => (
          <div key={w.id} className="op-work-row" style={{
            display: "grid",
            gridTemplateColumns: "40px 1fr 110px",
            gap: 14,
            padding: "12px 14px",
            border: "1px solid rgba(123,255,170,0.1)",
            borderRadius: 2,
            background: "rgba(255,255,255,0.015)",
            position: "relative",
            overflow: "hidden",
          }}>
            <div style={{ color: PHOS_DIM, fontSize: 11, paddingTop: 3 }}>{w.id}/</div>
            <div>
              <div style={{ display: "flex", alignItems: "baseline", gap: 10, flexWrap: "wrap" }}>
                <div style={{ fontSize: 15, color: "#eafff1", fontWeight: 500 }}>{w.name}</div>
                <div style={{ fontSize: 10, color: PHOS, letterSpacing: "0.08em", textTransform: "uppercase" }}>{w.role[lang]}</div>
              </div>
              <div style={{ marginTop: 6, fontSize: 12, color: "#9fb5a8", lineHeight: 1.55, maxWidth: 560 }}>{w.summary[lang]}</div>
              <div style={{ marginTop: 8, display: "flex", gap: 5, flexWrap: "wrap" }}>
                {w.tags.map(t => (
                  <span key={t} style={{
                    fontSize: 9, padding: "2px 6px", border: "1px solid rgba(123,255,170,0.2)",
                    color: "#9fb5a8", borderRadius: 2, letterSpacing: "0.04em",
                  }}>{t}</span>
                ))}
              </div>
            </div>
            <div style={{ textAlign: "right" }}>
              <div style={{ fontSize: 26, color: PHOS, fontWeight: 600, lineHeight: 1, letterSpacing: "-0.03em" }}>{w.metric}</div>
              <div style={{ fontSize: 9, color: "#5e6f66", marginTop: 4, letterSpacing: "0.08em", textTransform: "uppercase" }}>{w.metricLabel[lang]}</div>
            </div>
          </div>
        ))}
      </div>
    </Section>
  );
}

/* ---------- Timeline ---------- */

const TIMELINE = [
  { year: { no: "siste 30d", en: "last 30d" }, what: { no: "Job-Search OS · Meria alfa · Nærtur live · Hikari 2 882 tester", en: "Job-Search OS · Meria alpha · Nærtur live · Hikari 2,882 tests" } },
  { year: { no: "nå", en: "now" },           what: { no: "Kristiansund · helsetjeneste + bygger KI/agent-systemer", en: "Kristiansund · healthcare + building AI/agent systems" } },
  { year: { no: "2024–nå", en: "2024–now" }, what: { no: "helsetjeneste i Kristiansund (sykehjem) · medikamentansvar · Helseplattformen daglig", en: "healthcare in Kristiansund (nursing home) · medication responsibility · Helseplattformen daily" } },
  { year: { no: "2025–26", en: "2025–26" },  what: { no: "agenter — dypdykk: orkestrering, kontekst, minne, tillatelser", en: "agents — deep dive: orchestration, context, memory, permissions" } },
  { year: { no: "2024–25", en: "2024–25" },  what: { no: "LLM-er · chatbots · første lanserte prosjekter", en: "LLMs · chatbots · first shipped projects" } },
  { year: { no: "2023–24", en: "2023–24" },  what: { no: "data science selvstudium — kode, matte, ML, DL, analyse", en: "data science self-study — code, math, ML, DL, analytics" } },
  { year: { no: "2018–22", en: "2018–22" },  what: { no: "frilans markedsføring (UA) · parallelt: medisinsk grad (cand.med., jevngod med norsk legeutdanning)", en: "freelance marketing (UA) · in parallel: medical degree (M.D., assessed equivalent to a Norwegian degree)" } },
];

function TimelineSection() {
  const lang = useLang();
  return (
    <Section command={I18N.timelineCmd}>
      <div style={{ display: "flex", flexDirection: "column" }}>
        {TIMELINE.map((t, i) => (
          <div key={i} style={{
            display: "grid", gridTemplateColumns: "90px 12px 1fr",
            gap: 12, padding: "8px 0", alignItems: "center",
            borderBottom: i < TIMELINE.length - 1 ? "1px dashed rgba(123,255,170,0.1)" : "none",
          }}>
            <div style={{ fontSize: 12, color: i === 0 ? PHOS : "#5e6f66", fontWeight: i === 0 ? 600 : 400 }}>{t.year[lang]}</div>
            <div style={{ display: "flex", justifyContent: "center" }}>
              <div style={{
                width: 7, height: 7, borderRadius: "50%",
                background: i === 0 ? PHOS : "transparent",
                border: `1px solid ${i === 0 ? PHOS : PHOS_DIM}`,
                boxShadow: i === 0 ? `0 0 10px ${PHOS}` : "none",
              }} />
            </div>
            <div style={{ fontSize: 13, color: i === 0 ? "#eafff1" : "#9fb5a8" }}>{t.what[lang]}</div>
          </div>
        ))}
      </div>
    </Section>
  );
}

/* ---------- Fleet (ps aux | grep agent) ---------- */

const SHIP_LOG = [
  { date: "2026-06-09", project: "job-search-os",  tag: "ship",    text: { no: "søknad ~#50 sendt · skill-orkestrert CV/søknad-pipeline · ærlighets-gate", en: "application ~#50 sent · skill-orchestrated CV/letter pipeline · honesty gate" } },
  { date: "2026-06-05", project: "job-search-os",  tag: "ship",    text: { no: "outreach-pipeline live · Brreg/NACE flerfaktor-scoring · 43 pakker · 1 møte booket", en: "outreach pipeline live · Brreg/NACE multi-factor scoring · 43 packages · 1 meeting booked" } },
  { date: "2026-06-03", project: "hikari-agent",   tag: "phase",   text: { no: "2 882 tester · 15+ MCP-servere · ~55 verktøy · kapabilitet-styrte tillatelser", en: "2,882 tests · 15+ MCP servers · ~55 tools · capability-gated permissions" } },
  { date: "2026-06-01", project: "job-search-os",  tag: "infra",   text: { no: "NAV-feed-ingester · trinnvise filtre + fuzzy dedup · SQLite + Notion-sporing", en: "NAV feed ingester · staged filters + fuzzy dedup · SQLite + Notion tracking" } },
  { date: "2026-05-21", project: "meria",          tag: "sprint",  text: { no: "sprint 30 lukket · 7 P0 + 10 P1 alfa-blockere", en: "sprint 30 closed · 7 P0 + 10 P1 alpha blockers" } },
  { date: "2026-05-21", project: "naertur",        tag: "deploy",  text: { no: "full-stack deploy · Cloudflare Workers + Fly PostGIS · 126 tester", en: "full-stack deploy · Cloudflare Workers + Fly PostGIS · 126 tests" } },
  { date: "2026-05-16", project: "meria",          tag: "ship",    text: { no: "i18n-ruting for dashboard/report/manage · 5 871 tester grønne", en: "i18n routing for dashboard/report/manage · 5,871 tests green" } },
  { date: "2026-05-11", project: "meria",          tag: "audit",   text: { no: "alfa-klar · 277 audit-funn lukket · tenant-lint 100→0", en: "alpha-ready · 277 audit findings closed · tenant lint 100→0" } },
  { date: "2026-05-08", project: "meria",          tag: "review",  text: { no: "deep review · 16 parallelle Opus-spor · 84 commits", en: "deep review · 16 parallel Opus lanes · 84 commits" } },
  { date: "2026-05-07", project: "meria",          tag: "ship",    text: { no: "/coach + /call_list · genealogi-sortert · 5-stegs leder-invite", en: "/coach + /call_list · genealogy-sorted · 5-step leader invite" } },
  { date: "2026-05-07", project: "meria",          tag: "ship",    text: { no: "krise-klassifiserer default-on · krise-leksikon før LLM", en: "crisis classifier default-on · crisis lexicon before LLM" } },
  { date: "2026-05-05", project: "meria",          tag: "ship",    text: { no: "dag 1–25 onboarding rewrite fra operator-spec (UA)", en: "day 1–25 onboarding rewrite from operator spec (UA)" } },
  { date: "2026-05-04", project: "meria",          tag: "infra",   text: { no: "Mini-App mobil-dashboard · 5 faser · React + Vite multi-entry", en: "Mini-App mobile dashboard · 5 phases · React + Vite multi-entry" } },
];

function ShipLogSection() {
  const lang = useLang();
  const L = I18N.shiplog;
  const tagColor = (t) =>
    t === "ship" ? PHOS :
    t === "deploy" ? PHOS :
    t === "audit" ? AMBER :
    t === "review" ? AMBER :
    t === "refactor" ? BLUE :
    t === "phase" ? BLUE :
    t === "sprint" ? PHOS :
    t === "infra" ? BLUE :
    t === "research" ? "#9fb5a8" :
    t === "rename" ? AMBER :
    PHOS_DIM;
  const totalProjects = new Set(SHIP_LOG.map(e => e.project)).size;
  return (
    <Section command={L.cmd}>
      <div style={{
        border: "1px solid rgba(123,255,170,0.12)",
        borderRadius: 2,
        background: "rgba(0,0,0,0.25)",
        overflow: "hidden",
      }}>
        <div style={{
          display: "flex", gap: 18, alignItems: "center", flexWrap: "wrap",
          padding: "8px 12px",
          borderBottom: "1px solid rgba(123,255,170,0.1)",
          fontSize: 10, color: "#5e6f66",
          background: "rgba(123,255,170,0.04)",
        }}>
          <span style={{ color: PHOS, fontWeight: 600 }}>{totalProjects} {L.sumProjects[lang]} · {L.sumLive[lang]}</span>
          <span>{L.governance[lang]} <span style={{ color: BLUE }}>{L.govV[lang]}</span></span>
          <span style={{ marginLeft: "auto", color: PHOS_DIM }}>{L.period[lang]}</span>
        </div>

        <div style={{
          display: "grid",
          gridTemplateColumns: "92px 110px 70px 1fr",
          gap: 10,
          padding: "6px 12px",
          fontSize: 9, color: "#5e6f66", letterSpacing: "0.1em", textTransform: "uppercase",
          borderBottom: "1px dashed rgba(123,255,170,0.1)",
        }}>
          <div>date</div>
          <div>project</div>
          <div>kind</div>
          <div>shipped</div>
        </div>

        <div>
          {SHIP_LOG.map((e, i) => (
            <div key={`${e.date}-${i}`} style={{
              display: "grid",
              gridTemplateColumns: "92px 110px 70px 1fr",
              gap: 10,
              padding: "5px 12px",
              fontSize: 11,
              fontFamily: "'JetBrains Mono', monospace",
              borderBottom: i < SHIP_LOG.length - 1 ? "1px solid rgba(123,255,170,0.04)" : "none",
              color: "#9fb5a8",
            }}>
              <div style={{ color: "#5e6f66" }}>{e.date}</div>
              <div style={{ color: "#cfe8d6" }}>{e.project}</div>
              <div style={{ color: tagColor(e.tag) }}>{e.tag}</div>
              <div style={{ color: "#cfe8d6", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{e.text[lang]}</div>
            </div>
          ))}
        </div>

        <div style={{
          padding: "8px 12px",
          borderTop: "1px solid rgba(123,255,170,0.1)",
          fontSize: 10, color: "#5e6f66",
          background: "rgba(0,0,0,0.25)",
          display: "flex", justifyContent: "space-between", flexWrap: "wrap", gap: 10,
        }}>
          <span>{L.footer[lang]}</span>
          <span style={{ color: PHOS_DIM }}>{L.metaLine[lang]}</span>
        </div>
      </div>
    </Section>
  );
}

/* ---------- Contact ---------- */

function ContactSection() {
  const lang = useLang();
  const channels = [
    { k: "mail",     v: "olealt25@gmail.com",                   href: "mailto:olealt25@gmail.com" },
    { k: "tlf",      v: "+47 939 84 596",                       href: "tel:+4793984596" },
    { k: "linkedin", v: "/in/alksalt",                          href: "https://www.linkedin.com/in/alksalt/" },
    { k: "github",   v: "@Alksalt",                             href: "https://github.com/Alksalt/" },
  ];
  return (
    <Section command={I18N.contact.cmd}>
      <div style={{
        padding: "16px 18px",
        border: `1px solid ${PHOS_DIM}`,
        borderRadius: 2,
        background: "linear-gradient(180deg, rgba(123,255,170,0.03), transparent)",
      }}>
        <div style={{ fontSize: 12, color: PHOS_DIM, marginBottom: 12 }}>
          {I18N.contact.sub[lang]}
        </div>
        <div className="op-contact-grid" style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 8 }}>
          {channels.map(c => (
            <a key={c.k} href={c.href} target="_blank" rel="noopener noreferrer" style={{
              display: "flex", justifyContent: "space-between", alignItems: "center",
              padding: "8px 12px", textDecoration: "none",
              border: "1px solid rgba(123,255,170,0.15)", borderRadius: 2,
              background: "rgba(255,255,255,0.02)",
              fontSize: 12, color: "#cfe8d6",
            }}>
              <span style={{ color: PHOS_DIM }}>{c.k}</span>
              <span>{c.v} <span style={{ color: PHOS, marginLeft: 4 }}>↗</span></span>
            </a>
          ))}
        </div>
      </div>
    </Section>
  );
}

/* ---------- Language switcher ---------- */

function LangSwitcher() {
  const { lang, setLang } = React.useContext(LangCtx);
  const btn = (code, label) => {
    const active = lang === code;
    return (
      <button
        key={code}
        onClick={() => setLang(code)}
        aria-pressed={active}
        style={{
          background: active ? "rgba(123,255,170,0.12)" : "transparent",
          color: active ? PHOS : "#5e6f66",
          border: `1px solid ${active ? PHOS_DIM : "rgba(123,255,170,0.15)"}`,
          borderRadius: 2,
          padding: "2px 8px",
          fontFamily: "'JetBrains Mono', monospace",
          fontSize: 10,
          letterSpacing: "0.08em",
          cursor: "pointer",
          textTransform: "uppercase",
        }}
      >{label}</button>
    );
  };
  return (
    <div style={{ display: "flex", gap: 4, alignItems: "center" }}>
      {btn("no", "no")}
      {btn("en", "en")}
    </div>
  );
}

/* ---------- Plain hero (non-terminal first screen) ---------- */

function PlainHero() {
  const { lang, setLang } = React.useContext(LangCtx);
  const H = I18N.plainHero;
  const sans = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, Roboto, sans-serif";
  const contacts = [
    { v: "olealt25@gmail.com",       href: "mailto:olealt25@gmail.com" },
    { v: "+47 939 84 596",           href: "tel:+4793984596" },
    { v: "linkedin.com/in/alksalt",  href: "https://www.linkedin.com/in/alksalt/" },
    { v: "github.com/Alksalt",       href: "https://github.com/Alksalt/" },
  ];
  return (
    <section style={{
      position: "relative", zIndex: 2,
      background: "#f5f8f6",
      color: "#15241c",
      fontFamily: sans,
      padding: "36px 36px 44px",
    }}>
      <div style={{ maxWidth: 880, margin: "0 auto" }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
          <div style={{ fontSize: 12, letterSpacing: "0.08em", color: "#56705f", textTransform: "uppercase" }}>alksalt.com</div>
          <div style={{ display: "flex", gap: 6 }}>
            {["no", "en"].map(code => (
              <button key={code} onClick={() => setLang(code)} aria-pressed={lang === code} style={{
                background: lang === code ? "#15241c" : "transparent",
                color: lang === code ? "#f5f8f6" : "#56705f",
                border: "1px solid #b9cabf", borderRadius: 4, padding: "3px 10px",
                fontFamily: sans, fontSize: 12, cursor: "pointer", textTransform: "uppercase", letterSpacing: "0.06em",
              }}>{code}</button>
            ))}
          </div>
        </div>
        <h1 style={{ margin: "26px 0 0", fontSize: 44, lineHeight: 1.05, letterSpacing: "-0.02em", fontWeight: 700 }}>{H.name}</h1>
        <div style={{ marginTop: 10, fontSize: 15, color: "#3c5547", fontWeight: 500 }}>{H.tag[lang]}</div>
        <p style={{ margin: "18px 0 0", fontSize: 18, lineHeight: 1.55, maxWidth: 720, color: "#15241c" }}>{H.oneLiner[lang]}</p>
        <ul style={{ margin: "18px 0 0", padding: 0, listStyle: "none", display: "flex", flexDirection: "column", gap: 8 }}>
          {H.offers.map((o, i) => (
            <li key={i} style={{ display: "grid", gridTemplateColumns: "18px 1fr", gap: 8, fontSize: 15, lineHeight: 1.5, color: "#2c4437" }}>
              <span style={{ color: "#0c8a4d", fontWeight: 700 }}>✓</span><span>{o[lang]}</span>
            </li>
          ))}
        </ul>
        <div style={{ marginTop: 26, display: "flex", gap: 16, alignItems: "center", flexWrap: "wrap" }}>
          <a href={H.cvHref[lang]} style={{
            background: "#0c8a4d", color: "#ffffff", textDecoration: "none",
            padding: "12px 22px", borderRadius: 6, fontSize: 15, fontWeight: 600,
            boxShadow: "0 1px 2px rgba(12,138,77,0.4)", display: "inline-block",
          }}>{H.cvBtn[lang]} ↓</a>
          <div style={{ display: "flex", gap: 16, flexWrap: "wrap", fontSize: 14 }}>
            {contacts.map(c => (
              <a key={c.href} href={c.href}
                 target={c.href.startsWith("http") ? "_blank" : undefined}
                 rel={c.href.startsWith("http") ? "noopener noreferrer" : undefined}
                 style={{ color: "#0c5132", textDecoration: "none", fontWeight: 500 }}>{c.v}</a>
            ))}
          </div>
        </div>
        <div style={{ marginTop: 28, fontSize: 12, color: "#7b9486" }}>{H.scrollHint[lang]}</div>
      </div>
    </section>
  );
}

/* ---------- Top bar (faux terminal chrome) ---------- */

function TerminalChrome() {
  const lang = useLang();
  const [time, setTime] = useState("");
  useEffect(() => {
    const tick = () => {
      const d = new Date();
      const hh = String(d.getHours()).padStart(2,"0");
      const mm = String(d.getMinutes()).padStart(2,"0");
      const ss = String(d.getSeconds()).padStart(2,"0");
      setTime(`${hh}:${mm}:${ss}`);
    };
    tick(); const id = setInterval(tick, 1000); return () => clearInterval(id);
  }, []);
  return (
    <div style={{
      display: "flex", alignItems: "center", justifyContent: "space-between",
      padding: "10px 16px",
      borderBottom: "1px solid rgba(123,255,170,0.1)",
      fontSize: 11, color: "#5e6f66",
      background: "rgba(0,0,0,0.3)",
      position: "relative", zIndex: 2,
    }}>
      <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
        <div style={{ width: 10, height: 10, borderRadius: "50%", background: "#3a4a42" }} />
        <div style={{ width: 10, height: 10, borderRadius: "50%", background: "#3a4a42" }} />
        <div style={{ width: 10, height: 10, borderRadius: "50%", background: PHOS, boxShadow: `0 0 6px ${PHOS}` }} />
        <span style={{ marginLeft: 12, letterSpacing: "0.1em" }}>oleksandr@altukhov — zsh — kristiansund.no</span>
      </div>
      <div style={{ display: "flex", gap: 14, alignItems: "center" }}>
        <LangSwitcher />
        <span>{I18N.chrome.online[lang]}</span>
        <span>tz: cet</span>
        <span style={{ color: PHOS }}>{time}</span>
      </div>
    </div>
  );
}

/* ---------- main composition ---------- */

function OperatorVariant() {
  const [phase, setPhase] = useState(0); // 0 booting, 1 hero, 2 content
  const [lang, setLangState] = useState(() => {
    try {
      const v = (typeof localStorage !== "undefined") && localStorage.getItem("oa.lang");
      return v === "en" ? "en" : "no";
    } catch (e) { return "no"; }
  });
  const setLang = useCallback((v) => {
    setLangState(v);
    try { localStorage.setItem("oa.lang", v); } catch (e) {}
    try { document.documentElement.lang = v; } catch (e) {}
  }, []);
  useEffect(() => { try { document.documentElement.lang = lang; } catch (e) {} }, [lang]);

  useEffect(() => {
    if (phase === 1) {
      const t = setTimeout(() => setPhase(2), 700);
      return () => clearTimeout(t);
    }
  }, [phase]);

  const ctxValue = useMemo(() => ({ lang, setLang }), [lang, setLang]);

  return (
    <LangCtx.Provider value={ctxValue}>
    <div className="variant-root op-root" style={opStyles.root}>
      <style>{`
        @keyframes op-blink { 50% { opacity: 0; } }
        @keyframes op-fadein { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
        @keyframes op-bootline { from { opacity: 0; transform: translateX(-6px); } to { opacity: 0.85; transform: translateX(0); } }
        .op-fadein { animation: op-fadein 600ms ease forwards; }
        .op-work-row:hover { border-color: ${PHOS_DIM} !important; background: rgba(123,255,170,0.03) !important; }
      `}</style>

      <div style={opStyles.scanlines} />
      <div style={opStyles.grain} />

      <PlainHero />

      <TerminalChrome />

      <div className="op-content" style={{ position: "relative", zIndex: 2, padding: "22px 36px 56px" }}>
        {phase === 0 && <BootSequence onDone={() => setPhase(1)} />}

        {phase >= 1 && <NameBanner visible={phase >= 1} />}

        {phase >= 2 && (
          <div className="op-fadein" style={{ marginTop: 26, display: "flex", gap: 14, fontSize: 11, color: "#5e6f66", flexWrap: "wrap" }}>
            <span style={{ color: PHOS }}>--help</span>
            <span>whoami</span>
            <span>how_i_work</span>
            <span>ship_log</span>
            <span>patterns</span>
            <span>tools</span>
            <span>selected_work</span>
            <span>experience</span>
            <span>contact</span>
          </div>
        )}

        {phase >= 2 && (
          <div className="op-fadein" style={{ animationDelay: "120ms" }}>
            <WhoamiSection />
            <ManifestoSection />
            <ShipLogSection />
            <PatternsSection />
            <StackSection />
            <WorkSection />
            <TimelineSection />
            <ContactSection />

            <div style={{ marginTop: 40, color: "#3a4a42", fontSize: 11 }}>
              <Prompt>logout<Caret /></Prompt>
            </div>
          </div>
        )}
      </div>
    </div>
    </LangCtx.Provider>
  );
}

window.OperatorVariant = OperatorVariant;
