// Main portfolio components — Cédrick Picard / Nyxoril
const { useState, useEffect, useRef, useMemo } = React;

// ---------- Smooth scroll ----------
function smoothScrollTo(targetY, duration = 900) {
  const startY = window.scrollY;
  const distance = targetY - startY;
  if (Math.abs(distance) < 4) return;
  const startT = performance.now();
  const ease = (p) => p < 0.5 ? 4 * p * p * p : 1 - Math.pow(-2 * p + 2, 3) / 2;
  const step = (t) => {
    const p = Math.min(1, (t - startT) / duration);
    window.scrollTo(0, startY + distance * ease(p));
    if (p < 1) requestAnimationFrame(step);
  };
  requestAnimationFrame(step);
}
function scrollToId(id) {
  if (id === "top") return smoothScrollTo(0);
  const el = document.getElementById(id);
  if (!el) return;
  const top = el.getBoundingClientRect().top + window.scrollY - 70;
  smoothScrollTo(top);
}

// ---------- Background grid + parallax glow ----------
function GridBackground({ accentHue }) {
  const ref = useRef(null);
  useEffect(() => {
    const onMove = (e) => {
      const x = (e.clientX / window.innerWidth - 0.5) * 30;
      const y = (e.clientY / window.innerHeight - 0.5) * 30;
      if (ref.current) ref.current.style.transform = `translate3d(${x}px, ${y}px, 0)`;
    };
    window.addEventListener("mousemove", onMove);
    return () => window.removeEventListener("mousemove", onMove);
  }, []);
  return (
    <div className="bg-grid" aria-hidden="true">
      <div className="bg-grid__dots" />
      <div ref={ref} className="bg-grid__glow-wrap">
        <div className="bg-grid__glow" style={{ background: `radial-gradient(60% 50% at 50% 0%, oklch(0.55 0.2 ${accentHue} / 0.22), transparent 70%)` }} />
        <div className="bg-grid__glow bg-grid__glow--bottom" style={{ background: `radial-gradient(40% 30% at 80% 90%, oklch(0.5 0.18 ${accentHue} / 0.14), transparent 70%)` }} />
      </div>
      <div className="bg-grid__noise" />
    </div>
  );
}

// ---------- Nav ----------
function Nav({ onNavigate }) {
  const [scrolled, setScrolled] = useState(false);
  const [menuOpen, setMenuOpen] = useState(false);

  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 20);
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  useEffect(() => {
    if (!menuOpen) return;
    const orig = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    const onKey = (e) => { if (e.key === "Escape") setMenuOpen(false); };
    window.addEventListener("keydown", onKey);
    return () => {
      document.body.style.overflow = orig;
      window.removeEventListener("keydown", onKey);
    };
  }, [menuOpen]);

  const go = (id) => {
    setMenuOpen(false);
    setTimeout(() => onNavigate(id), 150);
  };

  return (
    <React.Fragment>
      <nav className={`nav ${scrolled ? "nav--scrolled" : ""} ${menuOpen ? "nav--menu-open" : ""}`}>
        <div className="nav__inner">
          <a href="#top" className="nav__logo" onClick={(e) => { e.preventDefault(); onNavigate("top"); }}>
            <span className="nav__logo-pfp">
              <img src="assets/pfp.png" alt="" />
              <span className="nav__logo-pfp-ring" />
            </span>
            <span className="nav__logo-text">nyxoril<span className="nav__logo-accent">.dev</span></span>
          </a>
          <div className="nav__links">
            <a href="#work" onClick={(e) => { e.preventDefault(); onNavigate("work"); }}>Work</a>
            <a href="#about" onClick={(e) => { e.preventDefault(); onNavigate("about"); }}>About</a>
            <a href="#stack" onClick={(e) => { e.preventDefault(); onNavigate("stack"); }}>Stack</a>
            <a href="#contact" onClick={(e) => { e.preventDefault(); onNavigate("contact"); }}>Contact</a>
          </div>
          <div className="nav__cta">
            <a href="mailto:cedrick.picard04@gmail.com" className="btn btn--ghost btn--sm">
              <span className="status-dot status-dot--green" />
              Open to work
            </a>
            <a href="mailto:cedrick.picard04@gmail.com" className="btn btn--solid btn--sm">
              Get in touch
              <svg viewBox="0 0 16 16" width="13" height="13"><path d="M6 4l4 4-4 4" stroke="currentColor" strokeWidth="1.5" fill="none" /></svg>
            </a>
          </div>
          <button
            className={`nav__hamburger ${menuOpen ? "nav__hamburger--open" : ""}`}
            onClick={() => setMenuOpen((o) => !o)}
            aria-label={menuOpen ? "Close menu" : "Open menu"}
            aria-expanded={menuOpen}
          >
            <span /><span /><span />
          </button>
        </div>
      </nav>

      {/* Drawer: rendered OUTSIDE <nav> so the nav's transform doesn't trap
          its position:fixed inside the nav's 58px-tall bounding box. */}
      <div className={`nav__drawer ${menuOpen ? "nav__drawer--open" : ""}`} aria-hidden={!menuOpen}>
        <div className="nav__drawer-backdrop" onClick={() => setMenuOpen(false)} />
        <aside className="nav__drawer-panel">
          <div className="nav__drawer-head">
            <span className="mono nav__drawer-label">navigation</span>
          </div>
          <nav className="nav__drawer-links">
            {[
              { id: "work", label: "Work", num: "01" },
              { id: "about", label: "About", num: "02" },
              { id: "stack", label: "Stack", num: "03" },
              { id: "contact", label: "Contact", num: "04" }
            ].map((item) => (
              <a key={item.id} href={`#${item.id}`}
                onClick={(e) => { e.preventDefault(); go(item.id); }}
                className="nav__drawer-link">
                <span className="nav__drawer-link-num mono">{item.num}</span>
                <span className="nav__drawer-link-label">{item.label}</span>
                <svg viewBox="0 0 16 16" width="14" height="14" className="nav__drawer-link-arrow">
                  <path d="M5 11L11 5M11 5H6.5M11 5V9.5" stroke="currentColor" strokeWidth="1.5" fill="none" />
                </svg>
              </a>
            ))}
          </nav>
          <div className="nav__drawer-foot">
            <a href="mailto:cedrick.picard04@gmail.com" className="btn btn--solid nav__drawer-cta">
              <span className="status-dot status-dot--green" />
              cedrick.picard04@gmail.com
            </a>
            <div className="nav__drawer-socials mono">
              <a href="https://github.com/C3ddy" target="_blank" rel="noopener">github</a>
              <a href="https://nyxoril.itch.io/" target="_blank" rel="noopener">itch.io</a>
              <a href="uploads/resume.pdf" target="_blank" rel="noopener">resume</a>
            </div>
          </div>
        </aside>
      </div>
    </React.Fragment>
  );
}

// ---------- Hero ----------
function Hero({ accentHue }) {
  const [text, setText] = useState("");
  const phrases = ["// Unreal · Unity · Godot · Web", "// gameplay · cinematics · tools", "// bilingual FR/EN · Ottawa, ON"];
  const [phraseIdx, setPhraseIdx] = useState(0);

  useEffect(() => {
    let i = 0; let deleting = false;
    const full = phrases[phraseIdx];
    const tick = () => {
      if (!deleting) {
        i++;
        setText(full.slice(0, i));
        if (i >= full.length) { deleting = true; setTimeout(tick, 1800); return; }
      } else {
        i--;
        setText(full.slice(0, i));
        if (i <= 0) { setPhraseIdx((p) => (p + 1) % phrases.length); return; }
      }
      setTimeout(tick, deleting ? 28 : 42);
    };
    const t = setTimeout(tick, 200);
    return () => clearTimeout(t);
  }, [phraseIdx]);

  return (
    <section className="hero" id="top">
      <div className="hero__inner">
        <div className="hero__top">
          <div className="hero__avatar">
            <img src="assets/pfp.png" alt="Cédrick — samurai avatar" />
            <div className="hero__avatar-ring" style={{ borderColor: `oklch(0.7 0.18 ${accentHue} / 0.4)` }} />
            <div className="hero__avatar-ring hero__avatar-ring--2" style={{ borderColor: `oklch(0.7 0.18 ${accentHue} / 0.25)` }} />
          </div>
          <div className="hero__badge">
            <span className="status-dot status-dot--green" />
            <span className="mono">Available · Game Dev grad · Algonquin College</span>
          </div>
        </div>

        <h1 className="hero__title">
          Cédrick Picard —<br />
          making the <span className="hero__title-accent" style={{ color: `oklch(0.85 0.18 ${accentHue})` }}>half-second</span><br />
          between input and reaction count.
        </h1>

        <p className="hero__sub">
          Also known as <strong>Nyxoril</strong>. I build gameplay, tools and cinematics across
          <strong> Unreal, Unity and Godot</strong> — and I take <strong>web-dev</strong> side-quests when I need a
          break from C++. Advanced Diploma in Game Development from Algonquin College — Dean's List, bilingual FR/EN.
        </p>

        <div className="hero__type mono">{text}<span className="caret" /></div>

        <div className="hero__ctas">
          <a href="#work" className="btn btn--solid" onClick={(e) => { e.preventDefault(); scrollToId("work"); }}>
            See the work
            <svg viewBox="0 0 16 16" width="13" height="13"><path d="M6 4l4 4-4 4" stroke="currentColor" strokeWidth="1.5" fill="none" /></svg>
          </a>
          <a href="uploads/resume.pdf" target="_blank" rel="noopener" className="btn btn--ghost">
            <svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M8 2v9M8 11l-3-3M8 11l3-3M3 13h10" /></svg>
            Download CV
          </a>
          <a href="https://github.com/C3ddy" target="_blank" rel="noopener" className="hero__icon-link" aria-label="GitHub">
            <svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M12 .5a11.5 11.5 0 0 0-3.64 22.41c.58.11.79-.25.79-.56v-2c-3.2.7-3.88-1.54-3.88-1.54-.52-1.33-1.28-1.69-1.28-1.69-1.05-.71.08-.7.08-.7 1.16.08 1.77 1.19 1.77 1.19 1.03 1.76 2.7 1.25 3.36.96.1-.75.4-1.25.73-1.54-2.55-.29-5.23-1.28-5.23-5.7 0-1.26.45-2.29 1.18-3.1-.12-.29-.51-1.46.11-3.04 0 0 .97-.31 3.18 1.18a11.05 11.05 0 0 1 5.78 0c2.2-1.49 3.17-1.18 3.17-1.18.63 1.58.23 2.75.12 3.04.73.81 1.18 1.84 1.18 3.1 0 4.43-2.69 5.4-5.25 5.69.41.36.78 1.06.78 2.14v3.17c0 .31.21.68.8.56A11.5 11.5 0 0 0 12 .5z" /></svg>
          </a>
          <a href="https://nyxoril.itch.io/" target="_blank" rel="noopener" className="hero__icon-link" aria-label="itch.io">
            <svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M3 4l-2 2.5v2C1 9.7 2.3 11 3.5 11c.8 0 1.5-.5 1.5-1.2 0 .7.7 1.2 1.5 1.2S8 10.5 8 9.8c0 .7.7 1.2 1.5 1.2s1.5-.5 1.5-1.2c0 .7.7 1.2 1.5 1.2s1.5-.5 1.5-1.2c0 .7.7 1.2 1.5 1.2S17 10.5 17 9.8c0 .7.7 1.2 1.5 1.2s1.5-.5 1.5-1.2c0 .7.7 1.2 1.5 1.2 1.2 0 2.5-1.3 2.5-2.5v-2L21 4H3zm.5 8c-.5 0-1 .3-1 .8L2 19c0 .5.5 1 1 1h7l1-1 1 1h7c.5 0 1-.5 1-1l-.5-6.2c0-.5-.5-.8-1-.8-.4 0-.8.2-1.1.6-.3-.4-.7-.6-1.2-.6-.4 0-.8.2-1.1.6-.3-.4-.7-.6-1.2-.6h-6c-.5 0-.9.2-1.2.6-.3-.4-.7-.6-1.2-.6-.4 0-.8.2-1.1.6-.3-.4-.6-.6-1-.6zm7 2h3l.4 2.5h-3.8L10.5 14z" /></svg>
          </a>
        </div>

        <div className="hero__rail">
          <RailItem label="Engines shipped in" value="3" />
          <RailItem label="Year of grad" value="2025" />
          <RailItem label="Cumulative GPA" value="3.18" />
          <RailItem label="Certifications" value="6" />
        </div>
      </div>
    </section>
  );
}

function RailItem({ label, value }) {
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  useEffect(() => {
    const node = ref.current; if (!node) return;
    const io = new IntersectionObserver((entries) => {
      if (entries[0].isIntersecting) { setShown(true); io.disconnect(); }
    }, { threshold: 0.5 });
    io.observe(node);
    return () => io.disconnect();
  }, []);
  const display = useAnimatedValue(value, shown);
  return (
    <div className="rail-item" ref={ref}>
      <div className="rail-item__value">{display}</div>
      <div className="rail-item__label mono">{label}</div>
    </div>
  );
}

function useAnimatedValue(target, start) {
  const [v, setV] = useState(target);
  useEffect(() => {
    if (!start) { setV(target); return; }
    // try to parse numeric prefix; if non-numeric, just set directly
    const match = /^(\d+(?:\.\d+)?)(.*)$/.exec(String(target));
    if (!match) { setV(target); return; }
    const end = parseFloat(match[1]); const suffix = match[2];
    const dur = 900; const t0 = performance.now();
    let raf;
    const tick = (t) => {
      const p = Math.min(1, (t - t0) / dur);
      const eased = 1 - Math.pow(1 - p, 3);
      const cur = end * eased;
      const decimals = match[1].includes(".") ? match[1].split(".")[1].length : 0;
      setV(cur.toFixed(decimals) + suffix);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [start, target]);
  return v;
}

// ---------- Tech marquee ----------
function TechMarquee() {
  const techs = ["C++", "C#", "Unreal Engine 5", "Unity", "Godot", "Node.js", "Three.js", "React", "OpenGL", "GLSL", "Lua", "Python", "Java", "SQLite", "Sequencer", "Blueprints", "Tailwind", "JavaScript"];
  return (
    <div className="marquee" aria-hidden="true">
      <div className="marquee__track">
        {[...techs, ...techs].map((t, i) => (
          <span key={i} className="marquee__item mono">
            <span className="marquee__dot" />{t}
          </span>
        ))}
      </div>
    </div>
  );
}

// ---------- Project card ----------
function ProjectCard({ project, onOpen, index }) {
  const Cover = window.Covers[project.cover];
  const [hover, setHover] = useState(false);
  const ref = useRef(null);
  const onMove = (e) => {
    if (!ref.current) return;
    const r = ref.current.getBoundingClientRect();
    ref.current.style.setProperty("--mx", `${e.clientX - r.left}px`);
    ref.current.style.setProperty("--my", `${e.clientY - r.top}px`);
  };
  return (
    <article ref={ref} className={`card ${hover ? "card--hover" : ""}`}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      onMouseMove={onMove}
      onClick={() => onOpen(project.id)}
      tabIndex="0"
      onKeyDown={(e) => { if (e.key === "Enter") onOpen(project.id); }}
      style={{ "--card-hue": project.accentHue, animationDelay: `${index * 80}ms` }}>
      <div className="card__spotlight" />
      <div className="card__border-shine" />
      <div className="card__cover">
        {Cover && <Cover hue={project.accentHue} />}
        <div className="card__cover-fade" />
        <div className="card__scan" />
        <span className={`card__status status-pill status-pill--${project.statusColor}`}>
          <span className="status-dot" />
          {project.status}
        </span>
        <span className="card__year mono">{project.year}</span>
        {project.youtube && (
          <span className="card__media-badge mono">
            <svg viewBox="0 0 16 16" width="10" height="10"><polygon points="4,3 4,13 13,8" fill="currentColor" /></svg>
            video
          </span>
        )}
      </div>
      <div className="card__body">
        <div className="card__head">
          <h3 className="card__title">{project.title}</h3>
          <span className="card__arrow">
            <svg viewBox="0 0 16 16" width="14" height="14"><path d="M5 11L11 5M11 5H6.5M11 5V9.5" stroke="currentColor" strokeWidth="1.5" fill="none" /></svg>
          </span>
        </div>
        <p className="card__tag">{project.tagline}</p>
        <div className="card__meta mono">
          <span>{project.role}</span>
          <span className="card__meta-dot">·</span>
          <span>{project.engine}</span>
        </div>
      </div>
    </article>
  );
}

// ---------- Project modal ----------
function ProjectModal({ project, onClose }) {
  const Cover = window.Covers[project.cover];
  const [tab, setTab] = useState("overview");
  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";
    return () => {
      window.removeEventListener("keydown", onKey);
      document.body.style.overflow = "";
    };
  }, [onClose]);

  const tabs = ["overview", "highlights", "stack"];
  if (project.log && project.log.length) tabs.push("timeline");

  return (
    <div className="modal" onClick={onClose}>
      <div className="modal__panel" onClick={(e) => e.stopPropagation()} style={{ "--card-hue": project.accentHue }}>
        <button className="modal__close" onClick={onClose} aria-label="Close">
          <svg viewBox="0 0 16 16" width="16" height="16"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" strokeWidth="1.6" fill="none" /></svg>
        </button>

        <div className="modal__hero">
          {Cover && <Cover hue={project.accentHue} />}
          <div className="modal__hero-fade" />
          <div className="modal__hero-content">
            <div className="modal__crumb mono">
              <a href="#work" onClick={(e) => { e.preventDefault(); onClose(); }}>Work</a>
              <span>/</span>
              <span>{project.id}</span>
            </div>
            <h2 className="modal__title">{project.title}</h2>
            <p className="modal__tag">{project.tagline}</p>
            <div className="modal__chips">
              <span className={`status-pill status-pill--${project.statusColor}`}>
                <span className="status-dot" />
                {project.status}
              </span>
              <span className="chip mono">{project.year}</span>
              <span className="chip mono">{project.role}</span>
              <span className="chip mono">{project.engine}</span>
            </div>
          </div>
        </div>
        <div className="modal__tabs">
          {tabs.map((t) => (
            <button key={t} className={`modal__tab ${tab === t ? "modal__tab--active" : ""}`} onClick={() => setTab(t)}>
              {t}
            </button>
          ))}
        </div>

        <div className="modal__body">
          {tab === "overview" && (
            <div className="modal__overview">
              {project.note && (
                <div className="modal__note mono">
                  <svg viewBox="0 0 16 16" width="13" height="13" style={{ flexShrink: 0, marginTop: 1 }}><circle cx="8" cy="8" r="6.5" fill="none" stroke="currentColor" strokeWidth="1.4" /><path d="M8 5v3.5M8 11v.1" stroke="currentColor" strokeWidth="1.6" /></svg>
                  <span>{project.note}</span>
                </div>
              )}
              <p className="modal__lede">{project.description}</p>
              <div className="modal__stats">
                {project.stats.map((s) => (
                  <div className="modal__stat" key={s.label}>
                    <div className="modal__stat-value">{s.value}</div>
                    <div className="modal__stat-label mono">{s.label}</div>
                  </div>
                ))}
              </div>
              {project.youtube && (
                <div className="modal__video">
                  <div className="modal__video-label mono">▶ embedded video</div>
                  <div className="modal__video-frame">
                    <iframe
                      src={`https://www.youtube-nocookie.com/embed/${project.youtube}?rel=0&modestbranding=1`}
                      title={`${project.title} video`}
                      frameBorder="0"
                      allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
                      allowFullScreen
                    />
                  </div>
                </div>
              )}
              {project.download && (() => {
                const downloads = Array.isArray(project.download) ? project.download : [project.download];
                return (
                  <div className="modal__embed">
                    <div className="modal__video-label mono">▼ downloads</div>
                    <div className="modal__downloads">
                      {downloads.map((d, i) => (
                        <a key={i} href={d.file} download
                          className="modal__download">
                          <div className="modal__download-icon">
                            <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" strokeWidth="1.7">
                              <path d="M12 4v12M12 16l-5-5M12 16l5-5M5 20h14" />
                            </svg>
                          </div>
                          <div className="modal__download-body">
                            <div className="modal__download-label">{d.label || "Download"}</div>
                            <div className="modal__download-meta mono">
                              <span>{d.file.split("/").pop()}</span>
                              {d.size && <span className="modal__download-size">· {d.size}</span>}
                            </div>
                          </div>
                          <span className="modal__download-arrow mono">.zip ↓</span>
                        </a>
                      ))}
                    </div>
                  </div>
                );
              })()}
              {project.itch && (
                <div className="modal__embed">
                  <div className="modal__video-label mono">▶ playable on itch.io</div>
                  <a href={project.itch} target="_blank" rel="noopener" className="modal__itch">
                    <div className="modal__itch-icon">
                      <svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor"><path d="M3 4l-2 2.5v2C1 9.7 2.3 11 3.5 11c.8 0 1.5-.5 1.5-1.2 0 .7.7 1.2 1.5 1.2S8 10.5 8 9.8c0 .7.7 1.2 1.5 1.2s1.5-.5 1.5-1.2c0 .7.7 1.2 1.5 1.2s1.5-.5 1.5-1.2c0 .7.7 1.2 1.5 1.2S17 10.5 17 9.8c0 .7.7 1.2 1.5 1.2s1.5-.5 1.5-1.2c0 .7.7 1.2 1.5 1.2 1.2 0 2.5-1.3 2.5-2.5v-2L21 4H3zm.5 8c-.5 0-1 .3-1 .8L2 19c0 .5.5 1 1 1h7l1-1 1 1h7c.5 0 1-.5 1-1l-.5-6.2c0-.5-.5-.8-1-.8-.4 0-.8.2-1.1.6-.3-.4-.7-.6-1.2-.6-.4 0-.8.2-1.1.6-.3-.4-.7-.6-1.2-.6h-6c-.5 0-.9.2-1.2.6-.3-.4-.7-.6-1.2-.6-.4 0-.8.2-1.1.6-.3-.4-.6-.6-1-.6zm7 2h3l.4 2.5h-3.8L10.5 14z" /></svg>
                    </div>
                    <div>
                      <div className="modal__itch-title">Play in your browser →</div>
                      <div className="modal__itch-url mono">{project.itch.replace("https://", "")}</div>
                    </div>
                  </a>
                </div>
              )}
            </div>
          )}
          {tab === "highlights" && (
            <ul className="modal__highlights">
              {project.highlights.map((h, i) => (
                <li key={i}>
                  <span className="modal__highlight-num mono">{String(i + 1).padStart(2, "0")}</span>
                  <span>{h}</span>
                </li>
              ))}
            </ul>
          )}
          {tab === "stack" && (
            <div className="modal__stack">
              <div className="modal__stack-grid">
                {project.tech.map((t) => (
                  <span key={t} className="tech-tag mono">{t}</span>
                ))}
              </div>
              <div className="modal__links">
                {project.links.map((l) => (
                  <a key={l.label} href={l.href} target="_blank" rel="noopener" className="modal__link">
                    <span>{l.label}</span>
                    <svg viewBox="0 0 16 16" width="13" height="13"><path d="M5 11L11 5M11 5H6.5M11 5V9.5" stroke="currentColor" strokeWidth="1.5" fill="none" /></svg>
                  </a>
                ))}
              </div>
            </div>
          )}
          {tab === "timeline" && (
            <ol className="modal__timeline">
              {project.log.map((l, i) => (
                <li key={i} className="modal__timeline-item">
                  <span className="modal__timeline-date mono">{l.date}</span>
                  <span className="modal__timeline-dot" />
                  <span className="modal__timeline-note">{l.note}</span>
                </li>
              ))}
            </ol>
          )}
        </div>
      </div>
    </div>
  );
}

// ---------- Section header ----------
function SectionHeader({ tag, title, sub }) {
  return (
    <header className="section__header">
      <div className="section__tag mono">[ {tag} ]</div>
      <h2 className="section__title">{title}</h2>
      {sub && <p className="section__sub">{sub}</p>}
    </header>
  );
}

// ---------- Work section ----------
function WorkSection({ projects, onOpen }) {
  const [filter, setFilter] = useState("all");
  const statuses = Array.from(new Set(projects.map((p) => p.status.toLowerCase())));
  const filters = ["all", ...statuses];
  const filtered = projects.filter((p) => filter === "all" || p.status.toLowerCase() === filter);
  return (
    <section className="section" id="work">
      <SectionHeader tag="01 · selected work" title="Things I've built and shipped" sub="Six projects across Unreal, Unity, Three.js, Node.js and a video edit suite. Click any card to read the case study and watch the embedded video." />
      <div className="filters mono">
        {filters.map((f) => (
          <button key={f} className={`filter ${filter === f ? "filter--active" : ""}`} onClick={() => setFilter(f)}>
            {f}
            <span className="filter__count">
              {f === "all" ? projects.length : projects.filter(p => p.status.toLowerCase() === f).length}
            </span>
          </button>
        ))}
      </div>
      <div className="card-grid">
        {filtered.map((p, i) => (
          <ProjectCard key={p.id} project={p} onOpen={onOpen} index={i} />
        ))}
      </div>
    </section>
  );
}

// ---------- About + timeline ----------
function AboutSection({ timeline, certifications }) {
  return (
    <section className="section section--about" id="about">
      <SectionHeader tag="02 · about" title="Bilingual game-dev grad from Ottawa. Currently looking for a studio to build with." />
      <div className="about__grid">
        <div className="about__copy">
          <p>
            I'm Cédrick — a recent Game Development grad from <strong>Algonquin College</strong> (Dean's List, GPA 3.18).
            My capstone year was spent inside the college's <strong>Capstone Studio</strong> as part of team CtrlAltElite,
            shipping a game we showed publicly every week at the Canadian Aviation and Space Museum.
          </p>
          <p>
            My focus is the things players <em>feel</em> but never name — character movement, UI responsiveness,
            the half-second between input and reaction. I work across <strong>Unreal C++ &amp; Blueprints, Unity, Godot</strong>,
            and I take <strong>web-dev</strong> side-quests in Node.js and Three.js to keep the other half of my brain sharp.
          </p>
          <p>
            Bilingual (FR/EN). Based in Ottawa / North Bay, ON. I worked as a McDonald's crew trainer for nearly three years and a mover for a season — neither was glamorous, but both were good schools for showing up on time and finishing what you start.
          </p>
          <div className="about__certs">
            <div className="about__certs-title mono">Certifications</div>
            <ul className="about__certs-list">
              {certifications.map((c, i) => (
                <li key={i}>
                  <span className="mono about__certs-year">{c.year}</span>
                  <span className="about__certs-issuer">{c.issuer}</span>
                  <span className="about__certs-name">{c.name}</span>
                </li>
              ))}
            </ul>
          </div>
        </div>
        <ol className="timeline">
          {timeline.map((t, i) => (
            <li key={i} className="timeline__item">
              <div className="timeline__year mono">{t.year}</div>
              <div className="timeline__body">
                <div className="timeline__role">{t.role}</div>
                <div className="timeline__where mono">{t.where}</div>
                <div className="timeline__note">{t.note}</div>
              </div>
            </li>
          ))}
        </ol>
      </div>
    </section>
  );
}

// ---------- Stack (periodic-table style tiles) ----------
function StackTile({ item, hue, index }) {
  const ref = useRef(null);
  const onMove = (e) => {
    if (!ref.current) return;
    const r = ref.current.getBoundingClientRect();
    const x = ((e.clientX - r.left) / r.width - 0.5) * 12;
    const y = ((e.clientY - r.top) / r.height - 0.5) * -12;
    ref.current.style.setProperty("--rx", `${y}deg`);
    ref.current.style.setProperty("--ry", `${x}deg`);
    ref.current.style.setProperty("--mx", `${e.clientX - r.left}px`);
    ref.current.style.setProperty("--my", `${e.clientY - r.top}px`);
  };
  const onLeave = () => {
    if (!ref.current) return;
    ref.current.style.setProperty("--rx", `0deg`);
    ref.current.style.setProperty("--ry", `0deg`);
  };
  return (
    <div ref={ref} className="tile"
      onMouseMove={onMove} onMouseLeave={onLeave}
      style={{ "--tile-hue": hue, animationDelay: `${index * 40}ms` }}>
      <div className="tile__spotlight" />
      <div className="tile__num mono">{String(index + 1).padStart(2, "0")}</div>
      <div className="tile__abbr">{item.abbr}</div>
      <div className="tile__name">{item.name}</div>
    </div>
  );
}

function StackSection({ skills }) {
  // distinct hue per group
  const groupHues = [220, 168, 12, 285];
  return (
    <section className="section" id="stack">
      <SectionHeader tag="03 · stack" title="The tools I reach for"
        sub="Twenty-something tools, give or take. The ones in bold I'd be confident shipping in tomorrow." />
      <div className="stack-table">
        {skills.map((group, gi) => (
          <div className="stack-row" key={group.group}>
            <div className="stack-row__head">
              <div className="stack-row__title mono">
                <span className="stack-row__num" style={{ color: `oklch(0.85 0.16 ${groupHues[gi % groupHues.length]})` }}>0{gi + 1}</span>
                {group.group.toLowerCase()}
              </div>
              <div className="stack-row__rule" style={{ background: `linear-gradient(to right, oklch(0.85 0.16 ${groupHues[gi % groupHues.length]} / 0.6), transparent)` }} />
              <div className="stack-row__count mono">{group.items.length} items</div>
            </div>
            <div className="stack-row__tiles">
              {group.items.map((item, i) => (
                <StackTile key={item.name} item={item} hue={groupHues[gi % groupHues.length]} index={i} />
              ))}
            </div>
          </div>
        ))}
      </div>
    </section>
  );
}

// ---------- Contact ----------
function ContactSection({ accentHue }) {
  return (
    <section className="section section--contact" id="contact">
      <div className="contact__panel" style={{ "--card-hue": accentHue }}>
        <div className="contact__bg" />
        <div className="contact__inner">
          <div className="section__tag mono">[ 04 · contact ]</div>
          <h2 className="contact__title">Got a studio or project that needs a builder?</h2>
          <p className="contact__sub">
            Open to junior / contract gameplay work. Always happy to hear about game jams,
            weird side projects, or anything Unreal-shaped.
          </p>
          <div className="contact__ctas">
            <a href="mailto:cedrick.picard04@gmail.com" className="btn btn--solid">
              cedrick.picard04@gmail.com
              <svg viewBox="0 0 16 16" width="13" height="13"><path d="M6 4l4 4-4 4" stroke="currentColor" strokeWidth="1.5" fill="none" /></svg>
            </a>
            <a href="tel:+16137900204" className="btn btn--ghost">
              <svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 3h3l1.5 3-2 1a8 8 0 0 0 4 4l1-2 3 1.5V13l-1 1a11 11 0 0 1-10-10L3 3z" /></svg>
              (613) 790-0204
            </a>
            <div className="contact__socials mono">
              <a href="https://github.com/C3ddy" target="_blank" rel="noopener" className="contact__social">github</a>
              <a href="https://nyxoril.itch.io/" target="_blank" rel="noopener" className="contact__social">itch.io</a>
              <button
                type="button"
                className="contact__social contact__social--btn"
                onClick={() => { sessionStorage.removeItem("nyx_seen_boot"); location.reload(); }}
                title="Replay boot animation"
              >
                ↻ replay intro
              </button>
              <a href="uploads/resume.pdf" target="_blank" rel="noopener" className="contact__social">resume</a>
            </div>
          </div>
        </div>
      </div>
      <footer className="footer mono">
        <span>© 2026 Cédrick Picard · Nyxoril</span>
        <span>Ottawa / North Bay · FR / EN</span>
      </footer>
    </section>
  );
}

window.PortfolioParts = {
  GridBackground, Nav, Hero, ProjectCard, ProjectModal, TechMarquee,
  WorkSection, AboutSection, StackSection, ContactSection
};
window.scrollToId = scrollToId;
window.smoothScrollTo = smoothScrollTo;
