// Direction 3 — "ARCHITECTURAL INDEX"
// Swiss grid, oversized sans numerals, archive feel.
// Off-white + ink + cobalt accent. Dense but precise.

const D3 = {
  bg: '#eeeae3',
  bg2: '#e3ddd0',
  ink: '#0e0d0a',
  ink2: '#3d3a32',
  rule: '#cdc6b4',
  accent: '#1c3fa8', // cobalt
  warm: '#c8521a',
  sans: '"Space Grotesk", "Helvetica Neue", Helvetica, Arial, sans-serif',
  display: '"Space Grotesk", "Helvetica Neue", sans-serif',
  mono: '"JetBrains Mono", ui-monospace, monospace',
};

function D3Frame({ children }) {
  return (
    <div style={{
      width: '100%', height: '100%', background: D3.bg, color: D3.ink,
      fontFamily: D3.sans, position: 'relative', overflow: 'hidden',
    }}>
      <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" />
      {children}
    </div>
  );
}

function D3Sidebar({ active, onNav, data }) {
  const mobile = useIsMobile();
  const items = [
    { k: 'HOME', n: '00', label: 'Index' },
    { k: 'INTRO', n: '01', label: 'Intro' },
    { k: 'HISTORY', n: '02', label: 'History' },
    { k: 'PORTFOLIO', n: '03', label: 'Portfolio' },
    { k: 'CONTACT', n: '04', label: 'Contact' },
  ];
  if (mobile) {
    return (
      <div style={{
        position: 'absolute', left: 0, right: 0, top: 0, height: 86,
        borderBottom: `1px solid ${D3.rule}`, background: D3.bg2,
        display: 'flex', flexDirection: 'column', padding: '10px 16px', zIndex: 5,
      }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 12 }}>
          <div style={{ fontFamily: D3.display, fontSize: 16, fontWeight: 600, letterSpacing: -0.3 }}>Nicolas Finsterbusch</div>
          <div style={{ fontFamily: D3.mono, fontSize: 9, color: D3.ink2, marginLeft: 'auto' }}>NJF / 2026</div>
        </div>
        <div style={{ marginTop: 8, display: 'flex', gap: 14, overflowX: 'auto' }}>
          {items.map(it => (
            <div key={it.k} onClick={() => onNav(it.k)} style={{
              display: 'flex', alignItems: 'baseline', gap: 5, cursor: 'pointer',
              flexShrink: 0, paddingBottom: 4,
              color: active === it.k ? D3.accent : D3.ink,
              borderBottom: `1px solid ${active === it.k ? D3.accent : 'transparent'}`,
            }}>
              <span style={{ fontFamily: D3.mono, fontSize: 9, color: active===it.k ? D3.accent : D3.ink2 }}>{it.n}</span>
              <span style={{ fontSize: 13, fontWeight: 500 }}>{it.label}</span>
            </div>
          ))}
        </div>
      </div>
    );
  }
  return (
    <div style={{
      position: 'absolute', left: 0, top: 0, bottom: 0, width: 220,
      borderRight: `1px solid ${D3.rule}`, padding: '32px 24px',
      background: D3.bg2, display: 'flex', flexDirection: 'column',
    }}>
      <div style={{ fontFamily: D3.mono, fontSize: 10, letterSpacing: 2, color: D3.ink2 }}>NJF / 2026</div>
      <div style={{
        marginTop: 14, fontFamily: D3.display, fontSize: 22, fontWeight: 600, lineHeight: 1.05,
        letterSpacing: -0.4,
      }}>
        Nicolas<br/>Finsterbusch
      </div>
      <div style={{ marginTop: 6, fontSize: 11, color: D3.ink2 }}>AI &amp; Delivery, Gothenburg</div>

      <div style={{ marginTop: 36 }}>
        {items.map(it => (
          <div key={it.k} onClick={() => onNav(it.k)} style={{
            display: 'flex', alignItems: 'baseline', gap: 12, padding: '10px 0',
            cursor: 'pointer', borderTop: `1px solid ${D3.rule}`,
            color: active === it.k ? D3.accent : D3.ink,
          }}>
            <span style={{ fontFamily: D3.mono, fontSize: 10, color: active===it.k ? D3.accent : D3.ink2 }}>{it.n}</span>
            <span style={{ fontSize: 15, fontWeight: 500 }}>{it.label}</span>
            {active===it.k && <span style={{ marginLeft: 'auto', color: D3.accent }}>●</span>}
          </div>
        ))}
      </div>

      <div style={{ flex: 1 }} />
      <div style={{ fontFamily: D3.mono, fontSize: 9, color: D3.ink2, lineHeight: 1.7 }}>
        <div>57°42′N 11°58′E</div>
        <div>EST. 1996 · BSAS</div>
        <div style={{ marginTop: 8, color: D3.accent }}>● AVAILABLE Q2 2026</div>
      </div>
    </div>
  );
}

function D3Home({ data }) {
  const mobile = useIsMobile();
  return (
    <div style={{ position: 'absolute', inset: 0, padding: mobile ? '20px 16px' : '32px 40px 32px 40px', overflow: 'auto' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
        <div style={{ fontFamily: D3.mono, fontSize: 10, letterSpacing: 2, color: D3.ink2 }}>00 / INDEX</div>
        <div style={{ fontFamily: D3.mono, fontSize: 10, color: D3.ink2 }}>v.2026.05</div>
      </div>

      <div style={{
        marginTop: mobile ? 16 : 24,
        fontFamily: D3.display, fontSize: mobile ? 48 : 88, fontWeight: 700,
        letterSpacing: mobile ? -1.5 : -3, lineHeight: 0.9,
      }}>
        AI<br/>
        ARCHITECTURE.<br/>
        <span style={{ color: D3.accent }}>DELIVERY</span><br/>
        AT SCALE.
      </div>

      <div style={{
        marginTop: mobile ? 24 : 32, display: 'grid', gridTemplateColumns: mobile ? '1fr' : '1fr 1fr 1fr',
        gap: mobile ? 16 : 24, paddingTop: 24, borderTop: `1px solid ${D3.ink}`,
      }}>
        <div style={{ fontSize: 14, lineHeight: 1.55, color: D3.ink2, maxWidth: 380 }}>
          AI consultant and delivery leader based in Gothenburg. Thirty years building governance-aware systems for regulated industries: automotive, energy, healthcare, retail, e-commerce.
        </div>
        <div style={{ fontSize: 14, lineHeight: 1.55, color: D3.ink2, maxWidth: 380 }}>
          Hands-on with applied AI since late 2024: RAG, ChromaDB, semantic memory, deterministic LLM workflows. Strongest at the intersection of business strategy and technical architecture.
        </div>
        <div style={{ fontSize: 14, lineHeight: 1.55, color: D3.ink2, maxWidth: 380 }}>
          Native Spanish &amp; German, fluent English. Available for fractional and project engagements from Q2 2026.
        </div>
      </div>

      <div style={{
        marginTop: mobile ? 24 : 36, paddingTop: 24, borderTop: `1px solid ${D3.rule}`,
        display: 'grid',
        gridTemplateColumns: mobile ? 'repeat(3, 1fr)' : 'repeat(6, 1fr)',
        gap: mobile ? 12 : 16,
      }}>
        {[
          ['30', 'YRS'], ['10+', 'AGENCIES'], ['15', 'MARKETS'],
          ['3', 'CONTINENTS'], ['~26K', 'LOC PYTHON'], ['84%', 'DIAG ACC.'],
        ].map(([v, k]) => (
          <div key={k} style={{ borderLeft: `1px solid ${D3.rule}`, paddingLeft: 12 }}>
            <div style={{ fontFamily: D3.display, fontSize: 36, fontWeight: 600, lineHeight: 1, letterSpacing: -1 }}>{v}</div>
            <div style={{ fontFamily: D3.mono, fontSize: 9, letterSpacing: 1.5, color: D3.ink2, marginTop: 4 }}>{k}</div>
          </div>
        ))}
      </div>

      <div style={{ marginTop: mobile ? 24 : 32, display: 'grid', gridTemplateColumns: mobile ? '1fr' : '2fr 1fr', gap: mobile ? 24 : 32 }}>
        <D3FeaturedRotator projects={data.projects || []} />
        <div>
          <div style={{ fontFamily: D3.mono, fontSize: 10, color: D3.ink2, letterSpacing: 2 }}>NOW</div>
          <div style={{ marginTop: 12, fontSize: 13, lineHeight: 1.7, color: D3.ink }}>
            <div>Consulting on applied AI →</div>
            <div>Co-founding Luna Fusion Bites →</div>
            <div>Building digital twin systems →</div>
            <div>Sailing in Bohuslän archipelago →</div>
          </div>
          <div style={{ marginTop: 24, padding: 16, background: D3.ink, color: D3.bg }}>
            <div style={{ fontFamily: D3.mono, fontSize: 9, letterSpacing: 2, opacity: 0.6 }}>NANOBOT</div>
            <div style={{ fontSize: 13, marginTop: 6, lineHeight: 1.5 }}>
              I'm in the bottom-right. Ask me anything about the work.
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

function D3FeaturedRotator({ projects }) {
  const [idx, setIdx] = React.useState(0);
  const [opacity, setOpacity] = React.useState(1);
  const [paused, setPaused] = React.useState(false);
  const ROTATE_MS = 7000;
  const FADE_MS = 350;

  React.useEffect(() => {
    if (!projects || projects.length <= 1 || paused) return;
    const id = setInterval(() => {
      setOpacity(0);
      setTimeout(() => {
        setIdx(i => (i + 1) % projects.length);
        setOpacity(1);
      }, FADE_MS);
    }, ROTATE_MS);
    return () => clearInterval(id);
  }, [projects && projects.length, paused]);

  const featured = projects && projects[idx];
  if (!featured) return <div />;
  const safeIdx = Math.min(idx, projects.length - 1);

  return (
    <div onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)}
      style={{ transition: `opacity ${FADE_MS}ms ease`, opacity }}>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 12 }}>
        <div style={{ fontFamily: D3.mono, fontSize: 10, color: D3.accent, letterSpacing: 2 }}>FEATURED · NO. {featured.n}</div>
        <div style={{ flex: 1 }} />
        <div style={{ fontFamily: D3.mono, fontSize: 9, letterSpacing: 1.5, color: D3.ink2 }}>
          {String(safeIdx + 1).padStart(2, '0')} / {String(projects.length).padStart(2, '0')}
          {paused && <span style={{ marginLeft: 8, color: D3.accent }}>·  PAUSED</span>}
        </div>
      </div>
      <div style={{ fontFamily: D3.display, fontSize: 36, fontWeight: 600, marginTop: 6, letterSpacing: -1, lineHeight: 1.05 }}>
        {featured.title}
      </div>
      <div style={{ fontSize: 14, color: D3.ink2, marginTop: 10, lineHeight: 1.55 }}>
        {featured.summary}
      </div>
      <div style={{
        marginTop: 18, aspectRatio: featured.legacy ? '4 / 3' : '16 / 9',
        background: featured.cover_url ? D3.bg2 : `repeating-linear-gradient(45deg, ${D3.bg2} 0 8px, ${D3.bg} 8px 16px)`,
        border: `1px solid ${D3.rule}`, position: 'relative', overflow: 'hidden',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontFamily: D3.mono, fontSize: 11, color: D3.ink2,
        cursor: featured.cover_url ? 'zoom-in' : 'default',
      }}
        onClick={featured.cover_url ? () => window.NJF_LIGHTBOX.openProject(featured, 0) : undefined}>
        {featured.cover_url ? (
          <img src={featured.cover_url} alt={featured.title} style={{
            width: '100%', height: '100%',
            objectFit: featured.legacy ? 'contain' : 'cover', display: 'block',
          }} />
        ) : (
          <>
            <div style={{
              position: 'absolute', top: 12, left: 12, fontFamily: D3.mono, fontSize: 9,
              letterSpacing: 2, color: D3.ink2,
            }}>FIG. {featured.n}.A</div>
            [ {featured.title.toUpperCase()} ]
          </>
        )}
      </div>
      {projects.length > 1 && (
        <div style={{ marginTop: 10, display: 'flex', gap: 4 }}>
          {projects.map((_, i) => (
            <div key={i} onClick={() => {
              setOpacity(0);
              setTimeout(() => { setIdx(i); setOpacity(1); }, FADE_MS / 2);
            }} style={{
              flex: 1, height: 2, cursor: 'pointer',
              background: i === safeIdx ? D3.accent : D3.rule,
            }} />
          ))}
        </div>
      )}
    </div>
  );
}

function D3Intro({ data }) {
  const mobile = useIsMobile();
  return (
    <div style={{ position: 'absolute', inset: 0, padding: mobile ? '20px 16px' : '32px 40px', overflow: 'auto' }}>
      <div style={{ fontFamily: D3.mono, fontSize: 10, letterSpacing: 2, color: D3.ink2 }}>01 / INTRO</div>
      <div style={{
        marginTop: mobile ? 12 : 16, fontFamily: D3.display, fontSize: mobile ? 32 : 64, fontWeight: 600,
        letterSpacing: mobile ? -1 : -2, lineHeight: 1, maxWidth: 900,
      }}>
        Systems thinker. Delivery leader. <span style={{ color: D3.accent }}>Application architect.</span>
      </div>
      <div style={{ marginTop: 24, paddingTop: 24, borderTop: `1px solid ${D3.ink}`,
        display: 'grid',
        gridTemplateColumns: mobile ? '1fr' : (data.portraitUrl ? '60px 240px 1fr 1fr 1fr' : '60px 1fr 1fr 1fr'),
        gap: mobile ? 16 : 28, alignItems: 'start' }}>
        <div style={{ fontFamily: D3.mono, fontSize: 10, color: D3.ink2 }}>§ 1</div>
        {data.portraitUrl && (
          <div onClick={() => window.NJF_LIGHTBOX.openSingle(data.portraitUrl, 'Nicolas Finsterbusch · Gothenburg')}
            style={{ cursor: 'zoom-in' }}>
            <div style={{ aspectRatio: '4 / 5', overflow: 'hidden', background: D3.bg2, border: `1px solid ${D3.rule}` }}>
              <img src={data.portraitUrl} alt="Nicolas Finsterbusch"
                style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
            </div>
            <div style={{ marginTop: 8, fontFamily: D3.mono, fontSize: 9, letterSpacing: 1.5, color: D3.ink2 }}>
              FIG. 01 · NICOLAS, GOTHENBURG
            </div>
          </div>
        )}
        {data.intro.map((p, i) => (
          <div key={i} style={{ fontSize: 14, lineHeight: 1.6, color: D3.ink2 }}>{p}</div>
        ))}
      </div>

      <div style={{ marginTop: mobile ? 32 : 48, paddingTop: 24, borderTop: `1px solid ${D3.ink}` }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 18 }}>
          <div style={{ fontFamily: D3.mono, fontSize: 10, color: D3.ink2 }}>§ 2 — PRACTICES</div>
          <div style={{ fontFamily: D3.mono, fontSize: 10, color: D3.ink2 }}>4 of 4</div>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: mobile ? '1fr 1fr' : 'repeat(4,1fr)', gap: 0 }}>
          {data.pillars.map((p, i) => (
            <div key={p.tag} style={{
              padding: 20, borderLeft: i===0 ? `1px solid ${D3.rule}` : 'none',
              borderRight: `1px solid ${D3.rule}`, minHeight: 220,
              display: 'flex', flexDirection: 'column',
            }}>
              <div style={{ fontFamily: D3.display, fontSize: 56, fontWeight: 700, color: D3.accent, lineHeight: 1, letterSpacing: -2 }}>{p.tag}</div>
              <div style={{ fontFamily: D3.display, fontSize: 18, fontWeight: 600, marginTop: 14, lineHeight: 1.15 }}>{p.title}</div>
              <div style={{ fontSize: 12, color: D3.ink2, marginTop: 10, lineHeight: 1.55 }}>{p.body}</div>
            </div>
          ))}
        </div>
      </div>

      <div style={{ marginTop: mobile ? 32 : 48, paddingTop: 24, borderTop: `1px solid ${D3.ink}`, display: 'grid', gridTemplateColumns: mobile ? '1fr' : '1fr 1fr 1fr', gap: mobile ? 20 : 32 }}>
        <div>
          <div style={{ fontFamily: D3.mono, fontSize: 10, color: D3.ink2, marginBottom: 10 }}>LANGUAGES</div>
          {data.languages.map(l => (
            <div key={l} style={{ fontSize: 14, padding: '6px 0', borderTop: `1px solid ${D3.rule}` }}>{l}</div>
          ))}
        </div>
        <div>
          <div style={{ fontFamily: D3.mono, fontSize: 10, color: D3.ink2, marginBottom: 10 }}>EDUCATION</div>
          <div style={{ fontSize: 14, padding: '6px 0', borderTop: `1px solid ${D3.rule}` }}>{data.education.degree}</div>
          <div style={{ fontSize: 13, color: D3.ink2, padding: '6px 0' }}>{data.education.school}, {data.education.place}</div>
        </div>
        <div>
          <div style={{ fontFamily: D3.mono, fontSize: 10, color: D3.ink2, marginBottom: 10 }}>OFF-CLOCK</div>
          {data.interests.map(i => (
            <div key={i} style={{ fontSize: 14, padding: '6px 0', borderTop: `1px solid ${D3.rule}` }}>{i}</div>
          ))}
        </div>
      </div>
    </div>
  );
}

function D3History({ data }) {
  const mobile = useIsMobile();
  return (
    <div style={{ position: 'absolute', inset: 0, padding: mobile ? '20px 16px' : '32px 40px', overflow: 'auto' }}>
      <div style={{ fontFamily: D3.mono, fontSize: 10, letterSpacing: 2, color: D3.ink2 }}>02 / HISTORY</div>
      <div style={{
        display: mobile ? 'block' : 'flex',
        justifyContent: 'space-between', alignItems: 'flex-end',
        marginTop: 12, paddingBottom: 18, borderBottom: `1px solid ${D3.ink}`, gap: 32 }}>
        <div style={{ fontFamily: D3.display, fontSize: mobile ? 36 : 64, fontWeight: 700, letterSpacing: mobile ? -1 : -2, lineHeight: 1 }}>
          A career, indexed.
        </div>
        <div style={{ display: 'flex', flexDirection: mobile ? 'row' : 'column',
          alignItems: mobile ? 'center' : 'flex-end', gap: mobile ? 12 : 8,
          marginTop: mobile ? 14 : 0, justifyContent: mobile ? 'space-between' : 'flex-end' }}>
          <a href={'https://' + data.linkedin} target="_blank" rel="noopener noreferrer" style={{
            fontFamily: D3.mono, fontSize: 10, letterSpacing: 1.5, color: D3.accent,
            textDecoration: 'none', textTransform: 'uppercase', whiteSpace: 'nowrap',
            border: `1px solid ${D3.accent}`, padding: '6px 10px',
          }}>LINKEDIN ↗</a>
          <div style={{ fontFamily: D3.mono, fontSize: 11, color: D3.ink2 }}>{data.history.length} entries · 1996 — 2026</div>
        </div>
      </div>

      {data.history.map((h, i) => (
        <div key={i} style={{
          display: 'grid',
          gridTemplateColumns: mobile ? '40px 1fr' : '60px 130px 1.2fr 1fr 1.6fr',
          gap: mobile ? 12 : 16, padding: mobile ? '14px 0' : '20px 0',
          borderBottom: `1px solid ${D3.rule}`, alignItems: 'baseline',
        }}>
          <div style={{ fontFamily: D3.mono, fontSize: 11, color: D3.accent }}>№ {String(data.history.length - i).padStart(2,'0')}</div>
          {mobile ? (
            <div>
              <div style={{ fontFamily: D3.mono, fontSize: 11, color: D3.ink2 }}>{h.year}</div>
              <div style={{ fontFamily: D3.display, fontSize: 17, fontWeight: 600, letterSpacing: -0.3, marginTop: 2 }}>{h.role}</div>
              <div style={{ fontSize: 12, color: D3.ink2, marginTop: 2 }}>{h.org} · <span style={{ fontFamily: D3.mono, fontSize: 10 }}>{h.place.toUpperCase()}</span></div>
              <div style={{ fontSize: 12, color: D3.ink2, lineHeight: 1.55, marginTop: 6 }}>{h.note}</div>
            </div>
          ) : (
            <>
              <div style={{ fontFamily: D3.mono, fontSize: 12, color: D3.ink2 }}>{h.year}</div>
              <div style={{ fontFamily: D3.display, fontSize: 20, fontWeight: 600, letterSpacing: -0.4 }}>{h.role}</div>
              <div style={{ fontSize: 13, color: D3.ink2 }}>{h.org}<br/><span style={{ fontFamily: D3.mono, fontSize: 10 }}>{h.place.toUpperCase()}</span></div>
              <div style={{ fontSize: 13, color: D3.ink2, lineHeight: 1.55 }}>{h.note}</div>
            </>
          )}
        </div>
      ))}
    </div>
  );
}

function D3Portfolio({ data }) {
  const mobile = useIsMobile();
  const sectors = React.useMemo(
    () => ['All', ...Array.from(new Set(data.projects.map(p => p.sector).filter(Boolean)))],
    [data.projects]
  );
  const [filter, setFilter] = React.useState('All');
  const filtered = React.useMemo(
    () => filter === 'All' ? data.projects : data.projects.filter(p => p.sector === filter),
    [data.projects, filter]
  );
  const [selId, setSelId] = React.useState(filtered[0] && filtered[0].id);
  React.useEffect(() => {
    if (!filtered.find(p => p.id === selId)) setSelId(filtered[0] && filtered[0].id);
  }, [filtered, selId]);
  const p = filtered.find(x => x.id === selId) || filtered[0];

  return (
    <div style={{ position: 'absolute', inset: 0, padding: mobile ? '20px 16px' : '32px 40px', overflow: 'auto' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
        <div style={{ fontFamily: D3.mono, fontSize: 10, letterSpacing: 2, color: D3.ink2 }}>03 / PORTFOLIO</div>
        <div style={{ fontFamily: D3.mono, fontSize: 10, color: D3.ink2 }}>
          SHOWING {filtered.length}{filter !== 'All' && ` of ${data.projects.length}`}
        </div>
      </div>

      {/* Filter chips */}
      <div style={{ marginTop: 16, display: 'flex', gap: 6, flexWrap: 'wrap' }}>
        {sectors.map(s => (
          <div key={s} onClick={() => setFilter(s)} style={{
            fontFamily: D3.mono, fontSize: 10, letterSpacing: 1.5, padding: '6px 12px',
            cursor: 'pointer', textTransform: 'uppercase',
            background: filter === s ? D3.accent : 'transparent',
            color: filter === s ? '#fff' : D3.ink2,
            border: `1px solid ${filter === s ? D3.accent : D3.rule}`,
          }}>{s}</div>
        ))}
      </div>

      {/* Specimen index — wraps automatically with auto-fill */}
      <div style={{ marginTop: 20, display: 'grid', gridTemplateColumns: mobile ? 'repeat(2, 1fr)' : 'repeat(auto-fill, minmax(150px, 1fr))', gap: 8, paddingBottom: 20, borderBottom: `1px solid ${D3.ink}` }}>
        {filtered.map((pr) => (
          <div key={pr.id} onClick={() => setSelId(pr.id)} style={{
            padding: '14px 12px', cursor: 'pointer',
            background: pr.id === selId ? D3.ink : 'transparent',
            color: pr.id === selId ? D3.bg : D3.ink,
            border: `1px solid ${pr.id === selId ? D3.ink : D3.rule}`,
          }}>
            <div style={{ fontFamily: D3.mono, fontSize: 9, letterSpacing: 1.5, opacity: 0.7 }}>SPEC. {pr.n}</div>
            <div style={{ fontFamily: D3.display, fontSize: 14, fontWeight: 600, marginTop: 6, lineHeight: 1.2 }}>{pr.title}</div>
            <div style={{ fontFamily: D3.mono, fontSize: 9, marginTop: 8, opacity: 0.7 }}>{pr.year}</div>
          </div>
        ))}
      </div>

      {!p && (
        <div style={{ padding: '40px 0', color: D3.ink2, fontSize: 14 }}>No specimens in this filter.</div>
      )}
      {p && <>


      {/* Detail */}
      <div style={{ marginTop: mobile ? 22 : 28, display: 'grid', gridTemplateColumns: mobile ? '1fr' : '1.4fr 1fr', gap: mobile ? 24 : 40 }}>
        <div>
          <div style={{ fontFamily: D3.mono, fontSize: 10, color: D3.accent, letterSpacing: 2 }}>
            SPECIMEN № {p.n} · {p.sector.toUpperCase()} · {p.year}
          </div>
          <div style={{ fontFamily: D3.display, fontSize: mobile ? 32 : 56, fontWeight: 700, letterSpacing: mobile ? -1 : -1.8, lineHeight: 1, marginTop: 10 }}>
            {p.title}
          </div>
          <div style={{ fontSize: mobile ? 15 : 18, color: D3.accent, marginTop: 8 }}>{p.kicker}</div>

          <div style={{
            marginTop: 24, aspectRatio: p.legacy ? '4 / 3' : '16 / 9',
            background: p.cover_url ? D3.bg2 : `repeating-linear-gradient(135deg, ${D3.bg2} 0 8px, ${D3.bg} 8px 16px)`,
            border: `1px solid ${D3.rule}`, position: 'relative', overflow: 'hidden',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontFamily: D3.mono, fontSize: 10, color: D3.ink2, letterSpacing: 2,
            cursor: p.cover_url ? 'zoom-in' : 'default',
          }}
            onClick={p.cover_url ? () => window.NJF_LIGHTBOX.openProject(p, 0) : undefined}>
            <div style={{ position: 'absolute', top: 10, left: 12, fontFamily: D3.mono, fontSize: 9, color: D3.ink2, zIndex: 1, mixBlendMode: 'difference', color: '#fff' }}>FIG. {p.n}.A</div>
            {p.cover_url ? (
              <img src={p.cover_url} alt={p.title} style={{
                width: '100%', height: '100%',
                objectFit: p.legacy ? 'contain' : 'cover',
                display: 'block',
              }} />
            ) : (
              <span>[ KEY VISUAL · {p.title.toUpperCase()} ]</span>
            )}
          </div>

          <div style={{ marginTop: 22, fontSize: 14, lineHeight: 1.6, color: D3.ink2 }}>{p.summary}</div>
          <div style={{ marginTop: 12, fontSize: 13, lineHeight: 1.6, color: D3.ink2 }}>{p.body}</div>

          {Array.isArray(p.gallery) && p.gallery.length > 0 && (
            <div style={{ marginTop: 28, paddingTop: 18, borderTop: `1px solid ${D3.rule}` }}>
              <div style={{ fontFamily: D3.mono, fontSize: 10, letterSpacing: 2, color: D3.ink2, marginBottom: 10 }}>
                FIG. {p.n}.B — GALLERY ({p.gallery.length})
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10 }}>
                {p.gallery.map((g, i) => {
                  const url = typeof g === 'string' ? g : g.url;
                  const cap = typeof g === 'string' ? null : g.caption;
                  const startIdx = (p.cover_url ? 1 : 0) + i;
                  return (
                    <figure key={i} style={{ margin: 0, cursor: 'zoom-in' }}
                      onClick={() => window.NJF_LIGHTBOX.openProject(p, startIdx)}>
                      <div style={{ aspectRatio: p.legacy ? '4 / 3' : '4 / 3', overflow: 'hidden', background: D3.bg2, border: `1px solid ${D3.rule}` }}>
                        <img src={url} alt={cap || ''} style={{ width: '100%', height: '100%', objectFit: p.legacy ? 'contain' : 'cover', display: 'block' }} />
                      </div>
                      {cap && <figcaption style={{ marginTop: 6, fontFamily: D3.mono, fontSize: 9, color: D3.ink2, letterSpacing: 1 }}>{cap}</figcaption>}
                    </figure>
                  );
                })}
              </div>
            </div>
          )}
        </div>

        <div>
          <div style={{ fontFamily: D3.mono, fontSize: 10, letterSpacing: 2, color: D3.ink2, marginBottom: 12 }}>METRICS</div>
          {p.metrics.map((m, i) => (
            <div key={i} style={{ padding: '14px 0', borderTop: `1px solid ${D3.rule}` }}>
              <div style={{ fontFamily: D3.display, fontSize: 36, fontWeight: 700, letterSpacing: -1, color: D3.ink, lineHeight: 1 }}>{m.v}</div>
              <div style={{ fontFamily: D3.mono, fontSize: 10, letterSpacing: 1.5, color: D3.ink2, marginTop: 4, textTransform: 'uppercase' }}>{m.k}</div>
            </div>
          ))}

          <div style={{ marginTop: 18, fontFamily: D3.mono, fontSize: 10, letterSpacing: 2, color: D3.ink2, marginBottom: 8 }}>STACK</div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {p.tags.map(t => (
              <span key={t} style={{
                fontFamily: D3.mono, fontSize: 10, padding: '4px 10px',
                background: D3.bg2, color: D3.ink,
              }}>{t}</span>
            ))}
          </div>

          {(p.live_url || p.liveUrl) && (
            <a href={p.live_url || p.liveUrl} target="_blank" rel="noopener noreferrer" style={{
              display: 'block', marginTop: 22, padding: '14px 16px',
              background: D3.accent, color: '#fff', textDecoration: 'none',
              fontFamily: D3.mono, fontSize: 11, letterSpacing: 1.5, textAlign: 'center',
            }}>↗ VIEW LIVE · {(p.live_url || p.liveUrl).replace(/^https?:\/\//, '').replace(/\/$/, '').toUpperCase()}</a>
          )}
        </div>
      </div>
      </>}
    </div>
  );
}

function D3Contact({ data }) {
  const mobile = useIsMobile();
  const [name, setName] = React.useState('');
  const [email, setEmail] = React.useState('');
  const [message, setMessage] = React.useState('');
  const [status, setStatus] = React.useState('idle');

  const submit = async () => {
    if (!name.trim() || !email.trim() || !message.trim() || status === 'sending') return;
    setStatus('sending');
    const ok = window.NJF_NANOBOT && await window.NJF_NANOBOT.submitContact({
      name: name.trim(), email: email.trim(), message: message.trim(),
      variant: window.NJF_AB && window.NJF_AB.getVariant(),
      referrer: document.referrer || null,
    });
    if (ok) {
      window.NJF_AB && window.NJF_AB.logEvent('contact', { source: 'form' });
      setStatus('sent'); setName(''); setEmail(''); setMessage('');
    } else {
      setStatus('error');
    }
  };

  const inp = {
    width: '100%', padding: '11px 12px', background: D3.bg,
    border: `1px solid ${D3.rule}`, borderRadius: 4,
    fontFamily: D3.sans, fontSize: 13, color: D3.ink, outline: 'none',
    marginBottom: 10, display: 'block',
  };

  return (
    <div style={{ position: 'absolute', inset: 0, padding: mobile ? '20px 16px' : '32px 40px', overflow: 'auto' }}>
      <div style={{ fontFamily: D3.mono, fontSize: 10, letterSpacing: 2, color: D3.ink2 }}>04 / CONTACT</div>
      <div style={{
        marginTop: mobile ? 12 : 16, fontFamily: D3.display, fontSize: mobile ? 44 : 96, fontWeight: 700,
        letterSpacing: mobile ? -1.5 : -3, lineHeight: 0.95,
      }}>
        Open to brief.<br/>
        <span style={{ color: D3.accent }}>Q2 2026 →</span>
      </div>

      <div style={{ marginTop: mobile ? 24 : 36, paddingTop: 24, borderTop: `1px solid ${D3.ink}`, display: 'grid', gridTemplateColumns: mobile ? '1fr' : '1.2fr 1fr', gap: mobile ? 24 : 50 }}>
        <div>
          <div style={{ fontFamily: D3.mono, fontSize: 10, letterSpacing: 2, color: D3.ink2, marginBottom: 18 }}>BEST FITS</div>
          {[
            ['A', 'Applied AI architecture', 'RAG, vector DBs, deterministic LLM workflows, governance.'],
            ['B', 'Fractional delivery leadership', 'Regulated programmes, multi-team coordination, hiring.'],
            ['C', 'AI product strategy', 'Discovery, PRDs, roadmaps, go-to-market.'],
          ].map(([k, t, d]) => (
            <div key={k} style={{
              display: 'grid', gridTemplateColumns: '40px 1fr', gap: 16,
              padding: '18px 0', borderTop: `1px solid ${D3.rule}`,
            }}>
              <div style={{ fontFamily: D3.display, fontSize: 28, fontWeight: 700, color: D3.accent }}>{k}</div>
              <div>
                <div style={{ fontFamily: D3.display, fontSize: 22, fontWeight: 600, letterSpacing: -0.5 }}>{t}</div>
                <div style={{ fontSize: 13, color: D3.ink2, marginTop: 4 }}>{d}</div>
              </div>
            </div>
          ))}
        </div>
        <div>
          <div style={{ fontFamily: D3.mono, fontSize: 10, letterSpacing: 2, color: D3.ink2, marginBottom: 12 }}>CHANNELS</div>
          {[
            ['EMAIL', data.email, 'Best for first contact'],
            ['LINKEDIN', '/in/nicolasfinsterbusch', 'Connection requests welcome'],
            ['STUDIO', 'Gothenburg, Sweden', 'In-person on request'],
          ].map(([k, v, n]) => (
            <div key={k} style={{ padding: '14px 0', borderTop: `1px solid ${D3.rule}` }}>
              <div style={{ fontFamily: D3.mono, fontSize: 9, letterSpacing: 2, color: D3.ink2 }}>{k}</div>
              <div style={{ fontFamily: D3.display, fontSize: 16, fontWeight: 600, marginTop: 4 }}>{v}</div>
              <div style={{ fontSize: 11, color: D3.ink2, marginTop: 2 }}>{n}</div>
            </div>
          ))}

          <div style={{ marginTop: 24, paddingTop: 18, borderTop: `1px solid ${D3.ink}` }}>
            <div style={{ fontFamily: D3.mono, fontSize: 10, letterSpacing: 2, color: D3.ink2, marginBottom: 10 }}>SEND A BRIEF</div>
            <input value={name} onChange={(e) => setName(e.target.value)} placeholder="Your name" style={inp} />
            <input value={email} onChange={(e) => setEmail(e.target.value)} type="email" placeholder="Your email" style={inp} />
            <textarea value={message} onChange={(e) => setMessage(e.target.value)} placeholder="What are you trying to ship?"
              style={{ ...inp, height: 100, resize: 'none', marginBottom: 12 }} />
            <button onClick={submit} disabled={status === 'sending'} style={{
              padding: '14px 22px', background: status === 'sent' ? '#2f8a4a' : D3.accent, color: '#fff',
              border: 'none', fontFamily: D3.sans, fontSize: 12, fontWeight: 600,
              letterSpacing: 1.5, cursor: status === 'sending' ? 'wait' : 'pointer',
              opacity: status === 'sending' ? 0.6 : 1,
            }}>{
              status === 'sending' ? 'SENDING…' :
              status === 'sent'    ? '✓ BRIEF SENT' :
              status === 'error'   ? 'RETRY (FAILED)' :
                                     '↗ SEND BRIEF'
            }</button>
            {status === 'error' && (
              <div style={{ marginTop: 10, fontSize: 11, color: D3.ink2 }}>
                Submission failed. Email instead: {data.email}
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

function D3Thinking({ color }) {
  const [n, setN] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => setN(x => (x + 1) % 4), 350);
    return () => clearInterval(id);
  }, []);
  return <span style={{ color, opacity: 0.7, fontFamily: 'inherit' }}>{'·'.repeat(n) + ' '.repeat(3 - n)}</span>;
}

function D3Nanobot({ data, onNav }) {
  const mobile = useIsMobile();
  const [open, setOpen] = React.useState(!mobile);
  const [messages, setMessages] = React.useState([
    { from: 'bot', text: "Hi. I'm a small Nicolas. Index of common questions below. Or type your own." },
  ]);
  const [input, setInput] = React.useState('');
  const ref = React.useRef(null);
  React.useEffect(() => { if (ref.current) ref.current.scrollTop = ref.current.scrollHeight; }, [messages]);

  const localRespond = (q) => {
    const lower = q.toLowerCase();
    let reply = "Nanobot offline. Email nfinsterbusch@gmail.com.";
    if (/xc40/.test(lower)) reply = "XC40: 4.77M sessions in 7 days, 189,958 configurations. Ran it at AKQA.";
    else if (/clinical/.test(lower)) reply = "Clinical screener. 47%→84% accuracy. 105 override patterns, 11 abstention paths.";
    else if (/twin|legacy/.test(lower)) reply = "Digital twin: local-only, 26.6k LOC, 6-level consent.";
    else if (/now|current/.test(lower)) reply = "Consulting + Luna Fusion Bites + clinical AI work.";
    else if (/history|career/.test(lower)) { reply = "Opening §02."; setTimeout(() => onNav('HISTORY'), 500); }
    else if (/contact|email|hire/.test(lower)) { reply = "Opening §04."; setTimeout(() => onNav('CONTACT'), 500); }
    else if (/portfolio|work|project/.test(lower)) { reply = "Opening §03."; setTimeout(() => onNav('PORTFOLIO'), 500); }
    return reply;
  };

  const send = async (text) => {
    const q = (text || input).trim();
    if (!q) return;
    const historyAtSend = messages;
    const pendingId = 'p_' + Date.now();
    setMessages(m => [...m, { from: 'me', text: q }, { from: 'bot', text: '', pending: true, id: pendingId }]);
    setInput('');
    let reply = null;
    if (window.NJF_NANOBOT) {
      reply = await window.NJF_NANOBOT.ask({
        message: q, history: historyAtSend, onNav,
        onChunk: (_chunk, full) => {
          setMessages(m => m.map(x => x.id === pendingId ? { ...x, text: full, pending: false } : x));
        },
      });
    }
    if (!reply) {
      reply = localRespond(q);
      setMessages(m => m.map(x => x.id === pendingId ? { from: 'bot', text: reply } : x));
    } else {
      setMessages(m => m.map(x => x.id === pendingId ? { from: 'bot', text: reply } : x));
    }
  };

  if (!open) {
    return (
      <div onClick={() => setOpen(true)} style={{
        position: 'absolute', right: 22, bottom: 22, width: 48, height: 48,
        background: D3.accent, color: '#fff', cursor: 'pointer',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontFamily: D3.display, fontSize: 16, fontWeight: 700,
        boxShadow: '0 12px 30px rgba(28,63,168,0.35)',
      }}>NB</div>
    );
  }

  return (
    <div style={mobile ? {
      position: 'fixed', left: 0, right: 0, bottom: 0, height: '70vh',
      background: D3.bg, borderTop: `1px solid ${D3.ink}`,
      display: 'flex', flexDirection: 'column', zIndex: 50,
      boxShadow: '0 -10px 40px rgba(0,0,0,0.2)',
    } : {
      position: 'absolute', right: 22, bottom: 22, width: 320, height: 380,
      background: D3.bg, border: `1px solid ${D3.ink}`,
      display: 'flex', flexDirection: 'column',
      boxShadow: '0 16px 50px rgba(0,0,0,0.18)',
    }}>
      <div style={{
        padding: '10px 14px', background: D3.ink, color: D3.bg,
        display: 'flex', alignItems: 'center',
      }}>
        <div style={{ fontFamily: D3.display, fontSize: 14, fontWeight: 700 }}>NB</div>
        <div style={{ fontFamily: D3.mono, fontSize: 9, letterSpacing: 1.5, marginLeft: 10, opacity: 0.7 }}>NANOBOT · NJF</div>
        <div style={{ flex: 1 }} />
        <span onClick={() => setOpen(false)} style={{ cursor: 'pointer', fontSize: 14 }}>×</span>
      </div>

      <div ref={ref} style={{ flex: 1, overflow: 'auto', padding: 14, fontSize: 12, lineHeight: 1.55 }}>
        {messages.map((m, i) => (
          <div key={m.id || i} style={{ marginBottom: 10 }}>
            <span style={{ fontFamily: D3.mono, fontSize: 9, letterSpacing: 1.5, color: m.from==='bot' ? D3.accent : D3.warm }}>
              {m.from === 'bot' ? 'NJF →' : 'YOU →'}
            </span>
            <div style={{ color: D3.ink, marginTop: 2, whiteSpace: 'pre-wrap' }}>
              {m.pending ? <D3Thinking color={D3.accent} /> : m.text}
            </div>
          </div>
        ))}
      </div>

      {messages.length <= 1 && (
        <div style={{ padding: '0 14px 8px', display: 'flex', gap: 4, flexWrap: 'wrap' }}>
          {data.chatbotPrompts.slice(0, 3).map(p => (
            <span key={p} onClick={() => send(p)} style={{
              fontSize: 10, padding: '4px 8px', background: D3.bg2,
              cursor: 'pointer', border: `1px solid ${D3.rule}`,
            }}>{p}</span>
          ))}
        </div>
      )}

      <div style={{ borderTop: `1px solid ${D3.rule}`, padding: 10, display: 'flex' }}>
        <input
          value={input} onChange={e => setInput(e.target.value)}
          onKeyDown={e => e.key === 'Enter' && send()}
          placeholder="Ask…"
          style={{ flex: 1, background: 'transparent', border: 'none', outline: 'none',
            fontFamily: D3.sans, fontSize: 13, color: D3.ink }}
        />
        <span onClick={() => send()} style={{ cursor: 'pointer', color: D3.accent, fontSize: 14 }}>→</span>
      </div>
    </div>
  );
}

function Direction3({ data }) {
  const [section, setSection] = React.useState('HOME');
  const mobile = useIsMobile();
  const sec = {
    HOME: <D3Home data={data} />, INTRO: <D3Intro data={data} />,
    HISTORY: <D3History data={data} />, PORTFOLIO: <D3Portfolio data={data} />,
    CONTACT: <D3Contact data={data} />,
  }[section];
  return (
    <D3Frame>
      <D3Sidebar active={section} onNav={setSection} data={data} />
      <div style={{
        position: 'absolute',
        left: mobile ? 0 : 220, right: 0,
        top: mobile ? 86 : 0,
        bottom: 0,
      }}>
        {sec}
      </div>
      <D3Nanobot data={data} onNav={setSection} />
    </D3Frame>
  );
}

window.Direction3 = Direction3;
