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

// ===== useInView hook (used for scroll-reveal) =====
const useInView = (ref, options = {}) => {
  const [inView, setInView] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const obs = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) { setInView(true); obs.disconnect(); }
    }, { threshold: 0.12, ...options });
    obs.observe(el);
    return () => obs.disconnect();
  }, []);
  return inView;
};

// Wraps a section so its contents fade + slide up when scrolled into view.
const RevealSection = ({ children, className = '', id, soft = false }) => {
  const ref = useRef(null);
  const inView = useInView(ref);
  return (
    <section
      ref={ref}
      id={id}
      className={`section ${soft ? 'section-soft' : ''} reveal-section ${inView ? 'is-visible' : ''} ${className}`}
    >
      {children}
    </section>
  );
};

// ===== Brand wordmark =====
// Inlined so the "THIERRY" portion can pick up currentColor for theming;
// "LAFEBER" stays at the brand blue.
const BrandLogo = () => (
  <svg viewBox="0 0 285 22" className="nav-brand-logo" role="img" aria-label="Thierry Lafeber" xmlns="http://www.w3.org/2000/svg">
    <g fill="currentColor">
      <path d="M15.9,0.7C16,0.8,16.1,1,16.1,1.2v0.4c0,0.2-0.1,0.4-0.2,0.5c-0.1,0.1-0.3,0.2-0.5,0.2H9.5v18.5 c0,0.2-0.1,0.4-0.2,0.5S9,21.5,8.8,21.5H8.3c-0.2,0-0.4-0.1-0.5-0.2S7.6,21,7.6,20.8V2.3H1.7c-0.2,0-0.4-0.1-0.5-0.2 C1.1,2,1,1.8,1,1.6V1.2C1,1,1.1,0.8,1.2,0.7c0.1-0.1,0.3-0.2,0.5-0.2h13.7C15.6,0.5,15.8,0.5,15.9,0.7z"/>
      <path d="M37.3,0.7c0.1,0.1,0.2,0.3,0.2,0.5v19.6c0,0.2-0.1,0.4-0.2,0.5c-0.1,0.1-0.3,0.2-0.5,0.2h-0.5 c-0.2,0-0.4-0.1-0.5-0.2c-0.1-0.1-0.2-0.3-0.2-0.5v-9.1H24v9.1c0,0.2-0.1,0.4-0.2,0.5c-0.1,0.1-0.3,0.2-0.5,0.2h-0.5 c-0.2,0-0.4-0.1-0.5-0.2c-0.1-0.1-0.2-0.3-0.2-0.5V1.2c0-0.2,0.1-0.4,0.2-0.5c0.1-0.1,0.3-0.2,0.5-0.2h0.5c0.2,0,0.4,0.1,0.5,0.2 S24,1,24,1.2v8.8h11.6V1.2c0-0.2,0.1-0.4,0.2-0.5c0.1-0.1,0.3-0.2,0.5-0.2h0.5C37,0.5,37.2,0.5,37.3,0.7z"/>
      <path d="M47.2,21.3c-0.1,0.1-0.3,0.2-0.5,0.2h-0.6c-0.2,0-0.4-0.1-0.5-0.2c-0.1-0.1-0.2-0.3-0.2-0.5V1.1 c0-0.2,0.1-0.4,0.2-0.5c0.1-0.1,0.3-0.2,0.5-0.2h0.6c0.2,0,0.4,0.1,0.5,0.2s0.2,0.3,0.2,0.5v19.7C47.4,21,47.3,21.2,47.2,21.3z"/>
      <path d="M68.5,19.8c0.1,0.1,0.2,0.3,0.2,0.5v0.4c0,0.2-0.1,0.4-0.2,0.5s-0.3,0.2-0.5,0.2H56.1c-0.2,0-0.4-0.1-0.5-0.2 s-0.2-0.3-0.2-0.5V1.2c0-0.2,0.1-0.4,0.2-0.5c0.1-0.1,0.3-0.2,0.5-0.2h11.7c0.2,0,0.4,0.1,0.5,0.2s0.2,0.3,0.2,0.5v0.4 c0,0.2-0.1,0.4-0.2,0.5c-0.1,0.1-0.3,0.2-0.5,0.2H57.3v7.6h9.8c0.2,0,0.4,0.1,0.5,0.2c0.1,0.1,0.2,0.3,0.2,0.5v0.4 c0,0.2-0.1,0.4-0.2,0.5s-0.3,0.2-0.5,0.2h-9.8v7.9H68C68.2,19.7,68.4,19.7,68.5,19.8z"/>
      <path d="M88.3,2c1.2,1,1.8,2.5,1.8,4.5c0,1.6-0.4,2.9-1.2,3.8s-1.9,1.6-3.4,2l4.8,8.2c0.1,0.1,0.1,0.2,0.1,0.3 c0,0.2-0.1,0.3-0.2,0.4c-0.1,0.1-0.3,0.2-0.4,0.2h-0.4c-0.3,0-0.5-0.1-0.7-0.2c-0.2-0.1-0.3-0.3-0.5-0.6l-4.8-8.1h-6.2v8.2 c0,0.2-0.1,0.4-0.2,0.5c-0.1,0.1-0.3,0.2-0.5,0.2h-0.5c-0.2,0-0.4-0.1-0.5-0.2c-0.1-0.1-0.2-0.3-0.2-0.5V1.2c0-0.2,0.1-0.4,0.2-0.5 c0.1-0.1,0.3-0.2,0.5-0.2h7C85.4,0.5,87,1,88.3,2z M83,10.8c1.7,0,3-0.4,3.9-1.1C87.8,9,88.2,8,88.2,6.5s-0.4-2.5-1.3-3.2 c-0.8-0.7-2.1-1.1-3.9-1.1h-5.6v8.5L83,10.8L83,10.8z"/>
      <path d="M109.8,2c1.2,1,1.8,2.5,1.8,4.5c0,1.6-0.4,2.9-1.2,3.8s-1.9,1.6-3.4,2l4.8,8.2c0.1,0.1,0.1,0.2,0.1,0.3 c0,0.2-0.1,0.3-0.2,0.4c-0.1,0.1-0.3,0.2-0.4,0.2h-0.4c-0.3,0-0.5-0.1-0.7-0.2c-0.2-0.1-0.3-0.3-0.5-0.6l-4.8-8.1H99v8.2 c0,0.2-0.1,0.4-0.2,0.5c-0.1,0.1-0.3,0.2-0.5,0.2h-0.5c-0.2,0-0.4-0.1-0.5-0.2c-0.1-0.1-0.2-0.3-0.2-0.5V1.2c0-0.2,0.1-0.4,0.2-0.5 c0.1-0.1,0.3-0.2,0.5-0.2h7C106.9,0.5,108.6,1,109.8,2z M104.6,10.8c1.7,0,3-0.4,3.9-1.1c0.8-0.7,1.3-1.8,1.3-3.2s-0.4-2.5-1.3-3.2 c-0.8-0.7-2.1-1.1-3.9-1.1H99v8.5L104.6,10.8L104.6,10.8z"/>
      <path d="M131.2,0.9c0.2-0.3,0.4-0.4,0.7-0.4h0.6c0.2,0,0.3,0.1,0.4,0.2s0.2,0.3,0.2,0.4c0,0.1,0,0.2-0.1,0.4l-7.3,12 v7.3c0,0.2-0.1,0.4-0.2,0.5s-0.3,0.2-0.5,0.2h-0.6c-0.2,0-0.4-0.1-0.5-0.2s-0.2-0.3-0.2-0.5v-7.3l-7.3-12c-0.1-0.2-0.1-0.3-0.1-0.4 c0-0.2,0.1-0.3,0.2-0.4s0.3-0.2,0.4-0.2h0.6c0.3,0,0.5,0.1,0.7,0.4l6.5,10.6L131.2,0.9z"/>
    </g>
    <g fill="#3FB3DC">
      <path d="M153.4,18c0.2,0.2,0.2,0.4,0.2,0.6v2.1c0,0.2-0.1,0.4-0.2,0.6c-0.2,0.2-0.4,0.2-0.6,0.2h-13.1 c-0.2,0-0.4-0.1-0.6-0.2c-0.2-0.2-0.2-0.3-0.2-0.6V1.3c0-0.2,0.1-0.4,0.2-0.6c0.2-0.2,0.4-0.2,0.6-0.2h2.4c0.2,0,0.4,0.1,0.6,0.2 c0.2,0.2,0.2,0.4,0.2,0.6v16.4h9.9C153.1,17.7,153.2,17.8,153.4,18z"/>
      <path d="M169.5,0.7c0.2,0.2,0.3,0.4,0.4,0.8l7.3,18.9c0,0.1,0,0.1,0,0.2c0,0.2-0.1,0.4-0.2,0.6 c-0.2,0.2-0.4,0.2-0.6,0.2h-2.2c-0.6,0-1-0.3-1.2-0.7l-1.3-3.3h-9.5l-1.3,3.3c-0.2,0.5-0.6,0.7-1.2,0.7h-2.2 c-0.2,0-0.4-0.1-0.6-0.2c-0.2-0.2-0.2-0.3-0.2-0.6c0-0.1,0-0.1,0-0.2l7.3-18.9c0.1-0.4,0.3-0.6,0.4-0.8c0.2-0.2,0.4-0.2,0.7-0.2 h3.3C169.1,0.5,169.3,0.6,169.5,0.7z M167.1,5.1l-3.3,8.5h6.6L167.1,5.1z"/>
      <path d="M196.6,0.7c0.2,0.2,0.2,0.4,0.2,0.6v2.1c0,0.2-0.1,0.4-0.2,0.6c-0.2,0.2-0.4,0.2-0.6,0.2h-9.6V10h9 c0.2,0,0.4,0.1,0.6,0.2s0.2,0.3,0.2,0.6V13c0,0.2-0.1,0.4-0.2,0.6s-0.4,0.2-0.6,0.2h-9v6.9c0,0.2-0.1,0.4-0.2,0.6 c-0.2,0.2-0.4,0.2-0.6,0.2h-2.4c-0.2,0-0.4-0.1-0.6-0.2c-0.2-0.2-0.2-0.3-0.2-0.6V1.3c0-0.2,0.1-0.4,0.2-0.6 c0.2-0.2,0.4-0.2,0.6-0.2H196C196.3,0.5,196.4,0.6,196.6,0.7z"/>
      <path d="M206.8,17.7h9.9c0.2,0,0.4,0.1,0.6,0.2c0.2,0.2,0.2,0.4,0.2,0.6v2.1c0,0.2-0.1,0.4-0.2,0.6 c-0.2,0.2-0.4,0.2-0.6,0.2h-13.2c-0.2,0-0.4-0.1-0.6-0.2c-0.2-0.2-0.2-0.3-0.2-0.6V1.3c0-0.2,0.1-0.4,0.2-0.6 c0.2-0.2,0.4-0.2,0.6-0.2h12.9c0.2,0,0.4,0.1,0.6,0.2c0.2,0.2,0.2,0.4,0.2,0.6v2.1c0,0.2-0.1,0.4-0.2,0.6c-0.2,0.2-0.4,0.2-0.6,0.2 h-9.7V9h9c0.2,0,0.4,0.1,0.6,0.2c0.2,0.2,0.2,0.4,0.2,0.6v2.1c0,0.2-0.1,0.4-0.2,0.6c-0.2,0.2-0.4,0.2-0.6,0.2h-9V17.7z"/>
      <path d="M223.9,21.2c-0.2-0.2-0.2-0.3-0.2-0.6V1.3c0-0.2,0.1-0.4,0.2-0.6c0.2-0.2,0.4-0.2,0.6-0.2h8.8 c1.6,0,2.9,0.3,3.9,0.9c1,0.6,1.7,1.4,2.2,2.3c0.4,0.9,0.7,1.8,0.7,2.7c0,1-0.2,1.9-0.7,2.6c-0.5,0.7-0.9,1.2-1.4,1.5 c0.7,0.5,1.3,1.1,1.8,1.9c0.5,0.8,0.7,1.7,0.7,2.8c0,1-0.3,2-0.8,3s-1.3,1.8-2.2,2.4c-1,0.6-2.1,0.9-3.5,0.9h-9.5 C224.2,21.5,224,21.4,223.9,21.2z M227.7,4.2v4.6h5.3c1,0,1.8-0.2,2.3-0.6c0.5-0.4,0.7-1,0.7-1.7s-0.2-1.4-0.7-1.7 c-0.5-0.4-1.2-0.6-2.3-0.6L227.7,4.2L227.7,4.2z M233.2,17.7c1.1,0,1.9-0.2,2.4-0.7s0.8-1.1,0.8-1.9c0-0.8-0.3-1.4-0.8-1.9 s-1.3-0.7-2.4-0.7h-5.5v5.1L233.2,17.7L233.2,17.7z"/>
      <path d="M250.6,17.7h9.9c0.2,0,0.4,0.1,0.6,0.2c0.2,0.2,0.2,0.4,0.2,0.6v2.1c0,0.2-0.1,0.4-0.2,0.6 c-0.2,0.2-0.4,0.2-0.6,0.2h-13.2c-0.2,0-0.4-0.1-0.6-0.2c-0.2-0.2-0.2-0.3-0.2-0.6V1.3c0-0.2,0.1-0.4,0.2-0.6 c0.2-0.2,0.4-0.2,0.6-0.2h12.9c0.2,0,0.4,0.1,0.6,0.2c0.2,0.2,0.2,0.4,0.2,0.6v2.1c0,0.2-0.1,0.4-0.2,0.6c-0.2,0.2-0.4,0.2-0.6,0.2 h-9.7V9h9c0.2,0,0.4,0.1,0.6,0.2c0.2,0.2,0.2,0.4,0.2,0.6v2.1c0,0.2-0.1,0.4-0.2,0.6c-0.2,0.2-0.4,0.2-0.6,0.2h-9L250.6,17.7 L250.6,17.7z"/>
      <path d="M281.7,2.2c1.3,1.2,2,2.8,2,4.8c0,1.5-0.3,2.7-1,3.7c-0.7,1-1.7,1.8-2.9,2.3l4.1,7.4c0.1,0.1,0.1,0.2,0.1,0.3 c0,0.2-0.1,0.3-0.2,0.5c-0.1,0.1-0.3,0.2-0.5,0.2h-2.5c-0.6,0-1-0.3-1.3-0.8l-3.9-7h-4v7c0,0.2-0.1,0.4-0.2,0.6 c-0.2,0.2-0.4,0.2-0.6,0.2h-2.4c-0.2,0-0.4-0.1-0.6-0.2c-0.2-0.2-0.2-0.3-0.2-0.6V1.3c0-0.2,0.1-0.4,0.2-0.6 c0.2-0.2,0.4-0.2,0.6-0.2h7.7C278.5,0.5,280.4,1.1,281.7,2.2z M275.9,9.9c1.2,0,2.1-0.2,2.8-0.6c0.6-0.4,1-1.2,1-2.2 s-0.3-1.8-0.9-2.2c-0.6-0.4-1.6-0.6-2.8-0.6h-4.3v5.7H275.9z"/>
    </g>
  </svg>
);

// ===== Contact obfuscation =====
// Values are stored base64-encoded so they don't appear as plain text in the DOM.
// Decoded client-side only — keeps bots/scrapers from harvesting them.
const b64d = s => atob(s);
const ObfuscatedLink = ({ b64href, b64label, className, children }) => {
  const [revealed, setRevealed] = React.useState(false);
  const href = b64d(b64href);
  const label = b64label ? b64d(b64label) : null;
  return (
    <a href={href} className={className} onClick={() => setRevealed(true)}>
      {children}{label && <span>{label}</span>}
    </a>
  );
};
// Pre-encoded values (base64):
// mailto:thierry@lafeber.me  → "bWFpbHRvOnRoaWVycnlAbGFmZWJlci5tZQ=="
// tel:+31620027236           → "dGVsOiszMTYyMDAyNzIzNg=="
// thierry@lafeber.me         → "dGhpZXJyeUBsYWZlYmVyLm1l"
// +31 6 2002 7236            → "KzMxIDYgMjAwMiA3MjM2"

// ===== Icon set (Material-style outlined, hand-tuned) =====
const Icon = ({ name, size = 20 }) => {
  const s = { width: size, height: size, fill: "none", stroke: "currentColor", strokeWidth: 1.7, strokeLinecap: "round", strokeLinejoin: "round" };
  switch (name) {
    case "mail":   return <svg viewBox="0 0 24 24" {...s}><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 7l9 7 9-7"/></svg>;
    case "phone":  return <svg viewBox="0 0 24 24" {...s}><path d="M5 4h4l2 5-2.5 1.5a11 11 0 005 5L15 13l5 2v4a2 2 0 01-2 2A16 16 0 013 6a2 2 0 012-2z"/></svg>;
    case "pin":    return <svg viewBox="0 0 24 24" {...s}><path d="M12 21s-7-7.2-7-12a7 7 0 0114 0c0 4.8-7 12-7 12z"/><circle cx="12" cy="9" r="2.5"/></svg>;
    case "cal":    return <svg viewBox="0 0 24 24" {...s}><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M3 9h18M8 3v4M16 3v4"/></svg>;
    case "down":   return <svg viewBox="0 0 24 24" {...s}><path d="M12 4v12M6 14l6 6 6-6M4 21h16"/></svg>;
    case "chev":   return <svg viewBox="0 0 24 24" {...s}><path d="M6 9l6 6 6-6"/></svg>;
    case "arrow":  return <svg viewBox="0 0 24 24" {...s}><path d="M5 12h14M13 5l7 7-7 7"/></svg>;
    case "os":     return <svg viewBox="0 0 24 24" {...s}><rect x="3" y="4" width="18" height="13" rx="2"/><path d="M8 21h8M12 17v4"/></svg>;
    case "infra":  return <svg viewBox="0 0 24 24" {...s}><rect x="3" y="4" width="18" height="6" rx="1"/><rect x="3" y="14" width="18" height="6" rx="1"/><circle cx="7" cy="7" r=".7" fill="currentColor"/><circle cx="7" cy="17" r=".7" fill="currentColor"/></svg>;
    case "net":    return <svg viewBox="0 0 24 24" {...s}><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 010 18M12 3a14 14 0 000 18"/></svg>;
    case "code":   return <svg viewBox="0 0 24 24" {...s}><path d="M8 6l-5 6 5 6M16 6l5 6-5 6M14 4l-4 16"/></svg>;
    case "server": return <svg viewBox="0 0 24 24" {...s}><rect x="2" y="3" width="20" height="5" rx="1"/><rect x="2" y="10" width="20" height="5" rx="1"/><rect x="2" y="17" width="20" height="4" rx="1"/><circle cx="18" cy="5.5" r="1" fill="currentColor" stroke="none"/><circle cx="18" cy="12.5" r="1" fill="currentColor" stroke="none"/><path d="M5 5.5h6M5 12.5h6"/></svg>;
    case "cloud":  return <svg viewBox="0 0 24 24" {...s}><path d="M7 18a4 4 0 010-8 6 6 0 0111.7 1.5A4 4 0 0117 18H7z"/></svg>;
    case "shield": return <svg viewBox="0 0 24 24" {...s}><path d="M12 3l8 3v6c0 5-3.5 8.5-8 9-4.5-.5-8-4-8-9V6l8-3z"/></svg>;
    case "backup": return <svg viewBox="0 0 24 24" {...s}><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>;
    case "media":  return <svg viewBox="0 0 24 24" {...s}><rect x="2" y="4" width="20" height="16" rx="2"/><polygon points="10,9 10,15 15,12"/></svg>;
    case "activity": return <svg viewBox="0 0 24 24" {...s}><polyline points="22,12 18,12 15,21 9,3 6,12 2,12"/></svg>;
    case "db":     return <svg viewBox="0 0 24 24" {...s}><ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v7c0 1.7 3.6 3 8 3s8-1.3 8-3V5M4 12v7c0 1.7 3.6 3 8 3s8-1.3 8-3v-7"/></svg>;
    case "drone":  return <svg viewBox="0 0 24 24" {...s}><circle cx="6" cy="6" r="3"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="18" r="3"/><rect x="9" y="9" width="6" height="6" rx="1"/></svg>;
    case "cap":    return <svg viewBox="0 0 24 24" {...s}><path d="M12 4l10 5-10 5L2 9l10-5z"/><path d="M6 11v5c0 1.5 3 3 6 3s6-1.5 6-3v-5"/></svg>;
    case "globe":  return <svg viewBox="0 0 24 24" {...s}><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 010 18"/></svg>;
    case "badge":  return <svg viewBox="0 0 24 24" {...s}><circle cx="12" cy="9" r="5"/><path d="M8 13l-2 8 6-3 6 3-2-8"/></svg>;
    case "ext":    return <svg viewBox="0 0 24 24" {...s}><path d="M14 4h6v6M20 4l-9 9M19 14v5a1 1 0 01-1 1H5a1 1 0 01-1-1V6a1 1 0 011-1h5"/></svg>;
    case "linkedin": return <svg viewBox="0 0 24 24" {...s}><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M8 10v8M8 7v.01M12 18v-5a3 3 0 016 0v5"/></svg>;
    case "home":   return <svg viewBox="0 0 24 24" {...s}><path d="M3 11l9-7 9 7v10a1 1 0 01-1 1H4a1 1 0 01-1-1z"/><path d="M9 22V12h6v10"/></svg>;
    case "wrench": return <svg viewBox="0 0 24 24" {...s}><rect x="7" y="7" width="10" height="10" rx="1"/><path d="M7 9H4M7 12H4M7 15H4M17 9h3M17 12h3M17 15h3M9 7V4M12 7V4M15 7V4M9 17v3M12 17v3M15 17v3"/></svg>;
    case "hammer": return <svg viewBox="0 0 24 24" {...s}><path d="M3 21l7-7"/><path d="M12.5 3.5l8 8-3 3-8-8 3-3z"/><path d="M5 16l3 3"/></svg>;
    default: return null;
  }
};

const ThemeToggle = ({ dark, onToggle }) => (
  <button className="theme-toggle" onClick={onToggle} aria-label="Toggle dark mode" title={dark ? 'Light mode' : 'Dark mode'}>
    {dark
      ? <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>
      : <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M21 12.8A9 9 0 1111.2 3a7 7 0 009.8 9.8z"/></svg>}
  </button>
);

// Tiny inline SVG flags, for crisp rendering on every OS (Windows shows
// "NL"/"GB" placeholder text instead of the emoji flag glyphs).
// Both flags rendered in a 3:2 box so they share a consistent footprint.
const FlagNL = ({ size = 20 }) => (
  <svg width={size} height={size * 2 / 3} viewBox="0 0 9 6" aria-hidden="true" style={{ display: 'block', borderRadius: 2 }}>
    <rect width="9" height="2" fill="#AE1C28"/>
    <rect y="2" width="9" height="2" fill="#FFFFFF"/>
    <rect y="4" width="9" height="2" fill="#21468B"/>
  </svg>
);
const FlagGB = ({ size = 20 }) => (
  <svg width={size} height={size * 2 / 3} viewBox="0 0 9 6" aria-hidden="true" style={{ display: 'block', borderRadius: 2, overflow: 'hidden' }}>
    <clipPath id="gb-frame"><rect width="9" height="6"/></clipPath>
    <clipPath id="gb-stripes"><path d="M4.5,3 L9,6 L9,5 L5.5,3 z M0,0 L0,1 L3.5,3 L4.5,3 z M9,0 L9,1 L5.5,3 L4.5,3 z M0,6 L0,5 L3.5,3 L4.5,3 z"/></clipPath>
    <g clipPath="url(#gb-frame)">
      <rect width="9" height="6" fill="#012169"/>
      <path d="M0,0 L9,6 M9,0 L0,6" stroke="#fff" strokeWidth="1.2"/>
      <path d="M0,0 L9,6 M9,0 L0,6" stroke="#C8102E" strokeWidth="0.7" clipPath="url(#gb-stripes)"/>
      <path d="M4.5,0 V6 M0,3 H9" stroke="#fff" strokeWidth="1.6"/>
      <path d="M4.5,0 V6 M0,3 H9" stroke="#C8102E" strokeWidth="0.9"/>
    </g>
  </svg>
);

const LangToggle = ({ lang, onToggle }) => (
  <button className="theme-toggle lang-toggle" onClick={onToggle} aria-label="Toggle language" title={lang === 'en' ? 'Schakel naar Nederlands' : 'Switch to English'}>
    {lang === 'en' ? <FlagNL/> : <FlagGB/>}
  </button>
);

// ===== Nav =====
const Nav = ({ active, dark, onToggleDark, lang, onToggleLang, navCopy }) => {
  const [scrolled, setScrolled] = useState(false);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 8);
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  const items = [
    { id: "about",      label: navCopy.about },
    { id: "skills",     label: navCopy.skills },
    { id: "experience", label: navCopy.experience },
    { id: "lab",        label: navCopy.lab },
    { id: "education",  label: navCopy.education }
  ];
  return (
    <nav className={`nav ${scrolled ? 'scrolled' : ''}`}>
      <div className="wrap nav-inner">
        <a href="#top" className="nav-brand" aria-label="Thierry Lafeber">
          <BrandLogo />
        </a>
        <div className="nav-links">
          {items.map(i => (
            <a key={i.id} href={`#${i.id}`} className={`nav-link ${active === i.id ? 'active' : ''}`}>{i.label}</a>
          ))}
          <LangToggle lang={lang} onToggle={onToggleLang} />
          <ThemeToggle dark={dark} onToggle={onToggleDark} />
          <a href="#contact" className="nav-cta">{navCopy.contact}</a>
        </div>
      </div>
    </nav>
  );
};

// ===== Hero =====
const Hero = ({ data, showPhoto }) => (
  <header className="hero" id="top">
    <div className="wrap hero-grid">
      <div>
        <h1>
          {data.name}.<br/>
          <span className="accent">{data.title}.</span>
        </h1>
        <p className="hero-sub">{data.tagline}</p>
        <div className="hero-meta">
          <span className="hero-meta-item"><span className="hero-meta-icon"><Icon name="pin" size={16}/></span>Zaanstad, NL</span>
          <span className="hero-meta-item"><span className="hero-meta-icon"><Icon name="cal" size={16}/></span>{data.copy.hero.years}</span>
          <span className="hero-meta-item"><span className="hero-meta-icon"><Icon name="globe" size={16}/></span>{data.copy.hero.langs}</span>
          <span className="hero-meta-item"><span className="hero-meta-icon"><Icon name="cal" size={16}/></span>{data.copy.hero.updated}</span>
        </div>
        <div className="hero-actions">
          <a href="#contact" className="btn btn-primary">{data.copy.hero.cta} <Icon name="arrow" size={16}/></a>
          <ObfuscatedLink b64href="bWFpbHRvOnRoaWVycnlAbGFmZWJlci5tZQ==" className="btn btn-ghost"><Icon name="mail" size={16}/> {data.copy.hero.email}</ObfuscatedLink>
        </div>
      </div>
      {showPhoto && (
        <div className="hero-photo">
          <img src="assets/thierry.jpg" alt={data.name} className="hero-photo-img" />
        </div>
      )}
      {/* References section removed per user request */}
    </div>
  </header>
);

// ===== About =====
const About = ({ data }) => (
  <RevealSection id="about">
    <div className="wrap">
      <div className="section-header">
        <div>
          <div className="section-eyebrow">{data.copy.sections.about.eyebrow}</div>
          <h2 className="section-title">{data.copy.sections.about.title}</h2>
        </div>
      </div>
      <div className="about-grid">
        <div className="about-prose">
          <p>{data.intro}</p>
          <p>{data.intro2}</p>
        </div>
        <div className="info-card">
          {data.basics.map(b => (
            <div className="info-row" key={b.label}>
              <div className="info-label">{b.label}</div>
              <div className="info-value">
                {b.obfuscated
                  ? <ObfuscatedLink b64href={b.b64href} b64label={b.b64value} />
                  : b.href ? <a href={b.href}>{b.value}</a> : b.value}
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  </RevealSection>
);

// ===== Skills =====
const Skills = ({ data }) => (
  <RevealSection id="skills" soft>
    <div className="wrap">
      <div className="section-header section-header-stack">
        <div>
          <div className="section-eyebrow">{data.copy.sections.skills.eyebrow}</div>
          <h2 className="section-title">{data.copy.sections.skills.title}</h2>
          {data.copy.sections.skills.lead && <p className="section-lead">{data.copy.sections.skills.lead}</p>}
        </div>
      </div>
      <div className="skills-grid">
        {data.skills.map(cat => (
          <div className="skill-cat" key={cat.title}>
            <div className="skill-cat-header">
              <div className="skill-cat-icon"><Icon name={cat.icon} size={20}/></div>
              <h3>{cat.title}</h3>
            </div>
            <div className="skill-tags">
              {cat.tags.map(t => <span className="skill-tag" key={t}>{t}</span>)}
            </div>
          </div>
        ))}
      </div>
    </div>
  </RevealSection>
);

// ===== Timeline =====
const TimelineItem = ({ item, defaultOpen, copy }) => {
  const [open, setOpen] = useState(!!defaultOpen);
  return (
    <div className="tl-item">
      <div className={`tl-period ${item.current ? 'tl-period-current' : ''}`}>{item.period}</div>
      <div className="tl-content">
        <h3 className="tl-role">{item.role}</h3>
        <div className="tl-org">
          <span>{item.org}</span>
          {item.location && <><span className="tl-org-sep">·</span><span>{item.location}</span></>}
        </div>
        <p className="tl-summary">{item.summary}</p>
        {item.details && item.details.length > 0 && (
          <>
            <button className="tl-toggle" aria-expanded={open} onClick={() => setOpen(o => !o)}>
              <span className="tl-toggle-label">{open ? copy.hideDetails : copy.showDetails}</span>
              <span className="tl-toggle-icon"><Icon name="chev" size={14}/></span>
            </button>
            <div className={`tl-details ${open ? 'is-open' : ''}`} aria-hidden={!open}>
              <div className="tl-details-inner">
                <ul>{item.details.map((d, i) => <li key={i} style={{ '--li-i': i }}>{d}</li>)}</ul>
              </div>
            </div>
          </>
        )}
      </div>
    </div>
  );
};

const Experience = ({ data }) => (
  <RevealSection id="experience">
    <div className="wrap">
      <div className="section-header section-header-stack">
        <div>
          <div className="section-eyebrow">{data.copy.sections.experience.eyebrow}</div>
          <h2 className="section-title">{data.copy.sections.experience.title}</h2>
          {data.copy.sections.experience.lead && <p className="section-lead">{data.copy.sections.experience.lead}</p>}
        </div>
      </div>
      <div className="timeline">
        {data.experience.map((item, i) => (
          <TimelineItem key={i} item={item} defaultOpen={i === 0} copy={data.copy.timeline} />
        ))}
      </div>
    </div>
  </RevealSection>
);

// ===== Lab =====
const Lab = ({ data }) => (
  <RevealSection id="lab" soft>
    <div className="wrap">
      <div className="section-header section-header-stack">
        <div>
          <div className="section-eyebrow">{data.copy.sections.lab.eyebrow}</div>
          <h2 className="section-title">{data.copy.sections.lab.title}</h2>
          {data.copy.sections.lab.lead && <p className="section-lead">{data.copy.sections.lab.lead}</p>}
        </div>
      </div>
      <div className="lab-grid">
        {data.lab.map(p => (
          <div className="lab-card" key={p.title}>
            <div className="lab-card-icon"><Icon name={p.icon} size={18}/></div>
            <h3>{p.title}</h3>
            <p>{p.desc}</p>
            <div className="lab-card-stack">
              {p.stack.map(s => <span key={s}>{s}</span>)}
            </div>
          </div>
        ))}
      </div>
    </div>
  </RevealSection>
);

// ===== Hobbies =====
const Hobbies = ({ data }) => (
  <RevealSection id="hobbies">
    <div className="wrap">
      <div className="section-header">
        <div>
          <div className="section-eyebrow">{data.copy.sections.hobbies.eyebrow}</div>
          <h2 className="section-title">{data.copy.sections.hobbies.title}</h2>
        </div>
      </div>
      <div className="hobby-grid">
        {data.hobbies.map(h => (
          <div className="hobby-card" key={h.title}>
            <div className="hobby-card-icon"><Icon name={h.icon} size={20}/></div>
            <h3>{h.title}</h3>
            <p>{h.desc}</p>
          </div>
        ))}
      </div>
    </div>
  </RevealSection>
);

// ===== References =====
const References = ({ data }) => (
  <RevealSection id="references" soft>
    <div className="wrap">
      <div className="section-header" style={{ justifyContent: 'center', textAlign: 'center' }}>
        <div>
          <div className="section-eyebrow">{data.copy.sections.references.eyebrow}</div>
          <h2 className="section-title">{data.copy.sections.references.title}</h2>
        </div>
      </div>
      {data.references.map(r => (
        <div className="ref-card" key={r.name}>
          <p className="ref-quote">“{r.quote}”</p>
          <div className="ref-attrib">
            <div className="ref-avatar">{r.name.split(' ').map(n => n[0]).slice(0,2).join('')}</div>
            <div>
              <div className="ref-name">{r.name}</div>
              <div className="ref-role">{r.role}</div>
            </div>
          </div>
        </div>
      ))}
    </div>
  </RevealSection>
);

// ===== Education + Certificates + Languages =====
const Education = ({ data }) => {
  const ref = useRef(null);
  const inView = useInView(ref);
  return (
  <section className={`section reveal-section ${inView ? 'is-visible' : ''}`} id="education" ref={ref}>
    <div className="wrap">
      <div className="section-header">
        <div>
          <div className="section-eyebrow">{data.copy.sections.education.eyebrow}</div>
          <h2 className="section-title">{data.copy.sections.education.title}</h2>
        </div>
      </div>
      <div className="three-grid">
        <div className={`tg-block edu-block ${inView ? 'is-visible' : ''}`} style={{ '--block-i': 0 }}>
          <h3><Icon name="cap" size={18}/> {data.copy.education.education}</h3>
          <div className="tg-list">
            {data.education.map((e, li) => (
              <div className={`tg-item edu-item ${inView ? 'is-visible' : ''}`} key={e.title} style={{ '--item-i': li, '--block-i': 0 }}>
                <div className="tg-item-title">{e.title}</div>
                <div className="tg-item-meta">{e.meta}</div>
              </div>
            ))}
          </div>
        </div>
        <div className={`tg-block edu-block ${inView ? 'is-visible' : ''}`} style={{ '--block-i': 1 }}>
          <h3><Icon name="badge" size={18}/> {data.copy.education.certificates}</h3>
          <div className="tg-list">
            {data.certificates.map((c, li) => (
              <div className={`tg-item edu-item ${inView ? 'is-visible' : ''}`} key={c.title} style={{ '--item-i': li, '--block-i': 1 }}>
                <div className="tg-item-title">{c.title}</div>
                <div className="tg-item-meta">{c.meta}</div>
              </div>
            ))}
          </div>
        </div>
        <div className={`tg-block edu-block ${inView ? 'is-visible' : ''}`} style={{ '--block-i': 2 }}>
          <h3><Icon name="globe" size={18}/> {data.copy.education.languages}</h3>
          <div className="tg-list">
            {data.languages.map((l, li) => (
              <div className={`lang-row edu-item ${inView ? 'is-visible' : ''}`} key={l.name} style={{ '--item-i': li, '--block-i': 2 }}>
                <div>
                  <span className="lang-name">{l.name}</span>
                  <span className="lang-dots">
                    {[1,2,3,4,5].map(n => <span key={n} className={`lang-dot ${n <= l.dots ? 'on' : ''}`}></span>)}
                  </span>
                </div>
                <span className="lang-level">{l.level}</span>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  </section>
  );
};

// ===== Contact =====
const Contact = ({ lang }) => (
  <RevealSection id="contact">
    <div className="wrap">
      <div className="contact-card">
        <h2>{lang === 'en' ? "Let's talk." : 'Neem contact op.'}</h2>
        <p>{lang === 'en'
          ? 'Got a question, an interesting infrastructure problem, or just want to chat? Reach out, I read every message.'
          : 'Een vraag, een interessant infrastructuurprobleem, of gewoon even praten? Stuur een bericht, ik lees alles.'}</p>
        <div className="contact-actions">
          <ObfuscatedLink b64href="bWFpbHRvOnRoaWVycnlAbGFmZWJlci5tZQ==" b64label="dGhpZXJyeUBsYWZlYmVyLm1l" className="btn btn-primary"><Icon name="mail" size={16}/> </ObfuscatedLink>
          <ObfuscatedLink b64href="dGVsOiszMTYyMDAyNzIzNg==" b64label="KzMxIDYgMjAwMiA3MjM2" className="btn btn-ghost"><Icon name="phone" size={16}/> </ObfuscatedLink>
          <a href="https://www.linkedin.com/in/thierry-lafeber" target="_blank" rel="noopener noreferrer" className="btn btn-ghost"><Icon name="linkedin" size={16}/> LinkedIn</a>
          <a href={lang === 'en'
            ? "/uploads/2026 Curriculum Vitae - Thierry Lafeber - English.pdf"
            : "/uploads/2026 Curriculum Vitae - Thierry Lafeber - Nederlands.pdf"
          } download className="btn btn-ghost"><Icon name="down" size={16}/> {lang === 'en' ? 'Download CV' : 'CV Downloaden'}</a>
        </div>
        <div className="contact-channels">
          <span className="contact-ch"><Icon name="pin" size={16}/> Zaanstad, NL</span>
          <span className="contact-ch"><Icon name="ext" size={16}/> <a href="https://thierrylafeber.nl">thierrylafeber.nl</a></span>
        </div>
      </div>
    </div>
  </RevealSection>
);

// ===== Footer =====
const Footer = () => (
  <footer className="footer">
    <div className="wrap footer-inner">
      <div>© {new Date().getFullYear()} Thierry Lafeber</div>
      <div>Last updated April 2026</div>
    </div>
  </footer>
);

// ===== Tweaks (removed, settings baked in via App) =====

const ACCENTS = {
  sky:   { accent: '#3FB3DC', hover: '#2A8FB5', soft: '#E6F4FA' },
  blue:  { accent: '#1a73e8', hover: '#1557b0', soft: '#e8f0fe' },
  green: { accent: '#188038', hover: '#0d652d', soft: '#e6f4ea' },
  red:   { accent: '#c5221f', hover: '#a50e0e', soft: '#fce8e6' },
  mono:  { accent: '#202124', hover: '#000000', soft: '#f1f3f4' }
};

// ===== Active section observer =====
const useActiveSection = (ids) => {
  const [active, setActive] = useState(ids[0]);
  useEffect(() => {
    const obs = new IntersectionObserver(
      entries => {
        entries.forEach(e => {
          if (e.isIntersecting) setActive(e.target.id);
        });
      },
      { rootMargin: '-30% 0px -55% 0px', threshold: 0 }
    );
    ids.forEach(id => {
      const el = document.getElementById(id);
      if (el) obs.observe(el);
    });
    return () => obs.disconnect();
  }, []);
  return active;
};

// ===== App =====
const SETTINGS = { accent: 'sky', density: 'compact', font: 'roboto', showPhoto: true };

const App = () => {
  const [lang, setLang] = useState(() => {
    try { return localStorage.getItem('tl-lang') || 'en'; } catch (e) { return 'en'; }
  });
  const data = lang === 'en' ? window.CV_DATA : window.CV_DATA_NL;

  const toggleLang = () => setLang(l => {
    const next = l === 'en' ? 'nl' : 'en';
    try { localStorage.setItem('tl-lang', next); } catch (e) {}
    return next;
  });

  const [dark, setDark] = useState(() => {
    try { return localStorage.getItem('tl-dark') === '1'; } catch (e) { return false; }
  });

  // Apply settings once on mount
  useEffect(() => {
    const a = ACCENTS[SETTINGS.accent] || ACCENTS.blue;
    document.documentElement.style.setProperty('--accent', a.accent);
    document.documentElement.style.setProperty('--accent-hover', a.hover);
    document.documentElement.style.setProperty('--accent-soft', a.soft);
    document.body.classList.toggle('density-compact', SETTINGS.density === 'compact');
    document.body.classList.add(`font-${SETTINGS.font}`);
  }, []);

  // Dark mode
  useEffect(() => {
    document.body.classList.toggle('dark', dark);
    try { localStorage.setItem('tl-dark', dark ? '1' : '0'); } catch (e) {}
    // Re-apply accent each time so dark/light themes pick the right token set.
    const a = ACCENTS[SETTINGS.accent] || ACCENTS.sky;
    if (dark && SETTINGS.accent === 'sky') {
      // Slightly brighter blue for dark backgrounds
      document.documentElement.style.setProperty('--accent', '#5DC4E5');
      document.documentElement.style.setProperty('--accent-hover', '#7DD2EC');
      document.documentElement.style.setProperty('--accent-soft', 'rgba(93,196,229,0.16)');
    } else if (SETTINGS.accent === 'mono' && dark) {
      document.documentElement.style.setProperty('--accent', '#e8eaed');
      document.documentElement.style.setProperty('--accent-hover', '#ffffff');
      document.documentElement.style.setProperty('--accent-soft', '#2a2a2a');
    } else {
      document.documentElement.style.setProperty('--accent', a.accent);
      document.documentElement.style.setProperty('--accent-hover', a.hover);
      document.documentElement.style.setProperty('--accent-soft', a.soft);
    }
  }, [dark]);

  const active = useActiveSection(['about', 'skills', 'experience', 'lab', 'education', 'hobbies', 'contact']);

  return (
    <>
      <Nav active={active} dark={dark} onToggleDark={() => setDark(d => !d)} lang={lang} onToggleLang={toggleLang} navCopy={data.copy.nav} />
      <Hero data={data} showPhoto={SETTINGS.showPhoto} />
      <About data={data} />
      <Skills data={data} />
      <Experience data={data} />
      <Lab data={data} />
      <Education data={data} />
      <Hobbies data={data} />
      <Contact lang={lang} />
      <Footer />
    </>
  );
};

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