// Studio body sections — project marquee, selected work, journal, and FAQ
const { useEffect, useRef, useState } = React;

// ----- hooks -----
function useInViewAnimation(threshold = 0.1) {
  const ref = useRef(null);
  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 }
    );
    obs.observe(el);
    return () => obs.disconnect();
  }, [threshold]);
  return [ref, inView];
}

function FadeIn({ children, delay = 0, className = "", as: Tag = "div" }) {
  const [ref, inView] = useInViewAnimation();
  return (
    <Tag ref={ref} className={`${className} ${inView ? "animate-fade-in-up" : "opacity-0"}`}
         style={{ animationDelay: `${delay}s` }}>
      {children}
    </Tag>
  );
}

// small icons
const IconArrowUpRight = ({ className = "w-4 h-4" }) => (
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
    <path d="M7 17 17 7"/><path d="M7 7h10v10"/>
  </svg>
);
const IconClose = ({ className = "w-4 h-4" }) => (
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" className={className}>
    <path d="M18 6 6 18M6 6l12 12"/>
  </svg>
);

// ----- Section header helper -----
function SectionHeader({ eyebrow, title, subtext, action }) {
  return (
    <div className="flex flex-col md:flex-row md:items-end md:justify-between gap-5 md:gap-6 mb-8 md:mb-14">
      <div className="max-w-xl">
        <FadeIn className="mb-5">
          <span className="text-[10px] sm:text-[11px] font-semibold text-slate-500 uppercase tracking-[0.24em] sm:tracking-[0.3em]">{eyebrow}</span>
        </FadeIn>
        <FadeIn delay={0.1}>
          <h2 className="font-display text-[34px] sm:text-[40px] md:text-[56px] leading-[1.05] tracking-tight font-medium" style={{ color: "#0a1b33" }}>
            {title}
          </h2>
        </FadeIn>
        {subtext && (
          <FadeIn delay={0.2}>
            <p className="mt-4 text-[14px] md:text-[15px] leading-relaxed text-slate-500">{subtext}</p>
          </FadeIn>
        )}
      </div>
      {action && (
        <FadeIn delay={0.25} className="shrink-0">
          {action}
        </FadeIn>
      )}
    </div>
  );
}

// ----- shared data -----
const PREVIEW_LIMIT_SECONDS = 10;
const PREVIEW_LOAD_MARGIN = "1200px 420px";
const projectFrameClass = (project) => project?.frame === "wide" ? "aspect-[1270/548]" : "aspect-video";
const projectMediaClass = (project) => project?.frame === "wide"
  ? "block w-full h-full object-contain bg-black"
  : "block w-full h-full object-cover";
const projectPageUrl = (id) => `pages/project.html?id=${id}`;
const JOURNAL_PROJECT_MATCHES = [
  ["defend", "defend-discover"],
  ["cartridge", "cartridge"],
  ["omnisect", "omnisect"],
  ["taskrypt", "taskrypt"],
  ["vaultx", "vaultx"],
  ["rv scan", "rvscan"],
  ["rvscan", "rvscan"],
];

function journalEntryHref(entry) {
  if (entry.href) return entry.href;
  if (entry.projectId) return projectPageUrl(entry.projectId);
  const text = `${entry.title || ""} ${entry.summary || ""}`.toLowerCase();
  const match = JOURNAL_PROJECT_MATCHES.find(([needle]) => text.includes(needle));
  return match ? projectPageUrl(match[1]) : "pages/work-archive.html";
}

function limitPreview(event) {
  const video = event.currentTarget;
  if (video.currentTime >= PREVIEW_LIMIT_SECONDS) {
    video.currentTime = 0;
    const play = video.play();
    if (play && play.catch) play.catch(() => {});
  }
}

function ProjectMedia({ src, label, className = "block w-full h-full object-cover", style, staticAt = null }) {
  const isVideo = /\.(mp4|webm|mov)$/i.test(src);
  const isStaticFrame = typeof staticAt === "number";
  const mediaRef = useRef(null);
  const retryRef = useRef(0);
  const [shouldLoad, setShouldLoad] = useState(!isVideo);

  useEffect(() => {
    if (!isVideo || shouldLoad) return;
    const el = mediaRef.current;
    if (!el) return;

    if (!("IntersectionObserver" in window)) {
      setShouldLoad(true);
      return;
    }

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setShouldLoad(true);
          observer.disconnect();
        }
      },
      { rootMargin: PREVIEW_LOAD_MARGIN }
    );

    observer.observe(el);
    return () => observer.disconnect();
  }, [isVideo, shouldLoad]);

  const showStaticFrame = (event) => {
    const video = event.currentTarget;
    const targetTime = Math.max(0, staticAt);
    try {
      if (Math.abs(video.currentTime - targetTime) > 0.05) video.currentTime = targetTime;
      video.pause();
    } catch (error) {
      video.pause();
    }
  };
  const retryLoad = (event) => {
    if (retryRef.current >= 2) return;
    retryRef.current += 1;
    const video = event.currentTarget;
    window.setTimeout(() => {
      video.load();
      if (!isStaticFrame) {
        const play = video.play();
        if (play && play.catch) play.catch(() => {});
      }
    }, 350 * retryRef.current);
  };
  if (isVideo) {
    return (
      <video
        ref={mediaRef}
        src={shouldLoad ? src : undefined}
        aria-label={`${label} preview`}
        className={className}
        style={style}
        autoPlay={!isStaticFrame}
        muted
        loop={!isStaticFrame}
        playsInline
        preload="metadata"
        onLoadedMetadata={isStaticFrame ? showStaticFrame : undefined}
        onLoadedData={isStaticFrame ? showStaticFrame : undefined}
        onSeeked={isStaticFrame ? showStaticFrame : undefined}
        onTimeUpdate={isStaticFrame ? undefined : limitPreview}
        onError={retryLoad}
        data-critical-media={!isStaticFrame ? "true" : undefined}
      />
    );
  }
  return <img src={src} alt={`${label} preview`} className={className} style={style} />;
}

const PROJECT_PREVIEWS = [
  { label: "Defend and Discover", cover: "previews/videos/defend-discover.mp4" },
  { label: "Cartridge",           cover: "previews/videos/cartridge-10s.mp4" },
  { label: "VaultX",              cover: "previews/videos/vaultx-10s.mp4" },
  { label: "RV Scan",             cover: "previews/videos/rvscan-10s.mp4" },
  { label: "Omnisect",            cover: "previews/videos/omnisect-10s.mp4", frame: "wide" },
  { label: "Taskrypt",            cover: "previews/videos/taskrypt2-13s.mp4", frame: "wide" },
  { label: "Evince",              cover: "previews/videos/evince.mp4",        frame: "wide" },
  { label: "Lyra",                cover: "previews/videos/lyra.mp4",          frame: "wide" },
];

const CERTIFICATES = [
  { title: "Web Application Pentesting", issuer: "TryHackMe", date: "21 May 2026", area: "Web AppSec", summary: "Hands-on web application testing path focused on finding, validating, and explaining real security issues.", credentialId: "THM-AWMAF3PTMS", pdf: "certificates/THM-AWMAF3PTMS.pdf", preview: "certificates/previews/web-application-pentesting.png", previewRatio: "1560 / 1105" },
  { title: "Jr Penetration Tester", issuer: "TryHackMe", date: "14 Nov 2025", area: "Pentesting", summary: "Practical penetration testing workflow covering enumeration, exploitation, privilege escalation, and reporting.", credentialId: "THM-KVFLDN3LGW", pdf: "certificates/THM-Jr Penetration Tester.pdf", preview: "certificates/previews/jr-penetration-tester.png", previewRatio: "1560 / 1105" },
  { title: "Cyber Security 101", issuer: "TryHackMe", date: "14 Aug 2025", area: "Cybersecurity", summary: "Core cybersecurity training across defensive concepts, common attack paths, and hands-on security fundamentals.", credentialId: "THM-QFB0XXRQCM", pdf: "certificates/THM-Cysec101.pdf", preview: "certificates/previews/cyber-security-101.png", previewRatio: "1560 / 1105" },
  { title: "Web Fundamentals", issuer: "TryHackMe", date: "29 Aug 2025", area: "Web Basics", summary: "Foundation work in HTTP, web technologies, and browser-server behavior that supports web security testing.", credentialId: "THM-JYTSCXPRDB", pdf: "certificates/THM-Web Fundamentals.pdf", preview: "certificates/previews/web-fundamentals.png", previewRatio: "1560 / 1105" },
  { title: "Pre Security", issuer: "TryHackMe", date: "22 Feb 2025", area: "Security Basics", summary: "Introductory security path covering networking, Linux, web basics, and the baseline concepts behind security work.", credentialId: "THM-AAHAG5M5VZ", pdf: "certificates/THM Pre Security Certificate.pdf", preview: "certificates/previews/pre-security.png", previewRatio: "1560 / 1105" },
  { title: "COSMOS 2024 Capture The Flag", issuer: "COSMOS 2024", date: "2024", area: "CTF Award", summary: "Second place award for Capture The Flag competition performance.", credentialId: "", pdf: "certificates/SERTIFIKAT COSMOS 2024 - Aldin Izyan Noor.pdf", preview: "certificates/previews/cosmos-2024-ctf.png", previewRatio: "2021 / 1430" },
];

// ----- 1. Project marquee -----
function ProjectMarquee({ projects = PROJECT_PREVIEWS }) {
  const previews = projects.map((p) => ({ ...p, label: p.label || p.title, cover: p.cover }));
  const items = [...previews, ...previews];

  const trackRef = React.useRef(null);
  const posRef = React.useRef(0);
  const baseSpeed = 1.2; // px per frame
  const speedRef = React.useRef(baseSpeed);
  const pausedRef = React.useRef(false);
  const rafRef = React.useRef(null);
  const halfWidthRef = React.useRef(0);
  const scrollVelRef = React.useRef(0);
  const scrollDecayRef = React.useRef(null);

  React.useEffect(() => {
    const track = trackRef.current;
    if (!track) return;

    // Measure half-width after mount (one copy of items)
    const measureHalf = () => {
      halfWidthRef.current = track.scrollWidth / 2;
    };
    measureHalf();
    window.addEventListener("resize", measureHalf);

    // Animation loop
    const tick = () => {
      if (!pausedRef.current) {
        const half = halfWidthRef.current || track.scrollWidth / 2;
        posRef.current += speedRef.current;
        if (speedRef.current >= 0 && posRef.current >= half) posRef.current -= half;
        if (speedRef.current < 0 && posRef.current <= 0) posRef.current += half;
        track.style.transform = `translateX(-${posRef.current}px)`;
      }
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);

    // Scroll: only active while hovering the marquee section
    const section = track.closest("section") || track.parentElement;
    const onScroll = (e) => {
      if (Math.abs(e.deltaX) <= Math.abs(e.deltaY)) return; // ignore vertical scroll
      const delta = e.deltaX;
      scrollVelRef.current = delta * 0.18;
      speedRef.current = baseSpeed + scrollVelRef.current;
      clearTimeout(scrollDecayRef.current);
      scrollDecayRef.current = setTimeout(() => {
        speedRef.current = baseSpeed;
      }, 600);
    };
    section.addEventListener("wheel", onScroll, { passive: true });

    // Touch: pause on hold, swipe left/right to steer
    let touchStartX = 0;
    let touchStartY = 0;
    let swipeSteering = false;
    const onTouchStart = (e) => {
      touchStartX = e.touches[0].clientX;
      touchStartY = e.touches[0].clientY;
      swipeSteering = false;
      pausedRef.current = true;
    };
    const onTouchMove = (e) => {
      const dx = e.touches[0].clientX - touchStartX;
      const dy = e.touches[0].clientY - touchStartY;
      if (!swipeSteering && Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 6) {
        swipeSteering = true;
      }
      if (swipeSteering) {
        // Horizontal swipe steers the marquee: right = reverse, left = forward
        speedRef.current = baseSpeed - dx * 0.08;
        pausedRef.current = false;
      }
    };
    const onTouchEnd = () => {
      pausedRef.current = false;
      swipeSteering = false;
      speedRef.current = baseSpeed;
    };
    track.addEventListener("touchstart",  onTouchStart,  { passive: true });
    track.addEventListener("touchmove",   onTouchMove,   { passive: true });
    track.addEventListener("touchend",    onTouchEnd,    { passive: true });
    track.addEventListener("touchcancel", onTouchEnd,    { passive: true });

    // Mouse: pause on hold (PC)
    const onMouseDown = () => { pausedRef.current = true; };
    const onMouseUp   = () => { pausedRef.current = false; };
    track.addEventListener("mousedown", onMouseDown);
    window.addEventListener("mouseup",  onMouseUp);

    return () => {
      cancelAnimationFrame(rafRef.current);
      clearTimeout(scrollDecayRef.current);
      window.removeEventListener("resize", measureHalf);
      section.removeEventListener("wheel", onScroll);
      window.removeEventListener("mouseup", onMouseUp);
      track.removeEventListener("touchstart",  onTouchStart);
      track.removeEventListener("touchmove",   onTouchMove);
      track.removeEventListener("touchend",    onTouchEnd);
      track.removeEventListener("touchcancel", onTouchEnd);
    };
  }, []);

  return (
    <section className="mt-12 md:mt-20 mb-16 md:mb-20">
      <div className="max-w-[1400px] mx-auto px-1 sm:px-2 mb-4 md:mb-5">
        <span className="text-[10px] sm:text-[11px] font-semibold text-slate-500 uppercase tracking-[0.24em] sm:tracking-[0.3em]">Projects I’ve built</span>
      </div>
      <div className="overflow-hidden" style={{ cursor: "grab" }}>
        <div ref={trackRef} className="flex will-change-transform" style={{ width: "max-content" }}>
          {items.map((p, i) => (
            <div key={i} className={`${p.frame === "wide" ? "h-[210px] w-[490px] sm:h-[300px] sm:w-[700px] md:h-[360px] md:w-[840px]" : "h-[210px] w-[360px] sm:h-[300px] sm:w-[540px] md:h-[360px] md:w-[640px]"} mx-2 sm:mx-3 rounded-2xl shadow-lg shrink-0 overflow-hidden border border-slate-200/10`}>
              <ProjectMedia src={p.cover} label={p.label} className={projectMediaClass(p)} />
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

function CertificatePreview({ certificate }) {
  const title = certificate.title || "Certificate";
  const pdf = certificate.pdf || "";
  const preview = certificate.preview || "";
  const previewRatio = certificate.previewRatio || "1560 / 1105";
  const canOpen = Boolean(pdf);

  const visual = preview ? (
    <img src={preview} alt={`${title} preview`} className="block w-full h-full object-contain pointer-events-none select-none" loading="lazy" draggable="false" />
  ) : pdf ? (
    <object
      data={`${pdf}#page=1&view=FitH&toolbar=0&navpanes=0&scrollbar=0`}
      type="application/pdf"
      className="block w-full h-full bg-white"
      aria-label={`${title} PDF preview`}
    >
      <div className="w-full h-full bg-white flex items-center justify-center px-8 text-center">
        <span className="text-[11px] font-semibold uppercase tracking-[0.22em] text-slate-400">PDF preview</span>
      </div>
    </object>
  ) : (
    <div className="w-full h-full bg-white px-8 py-10 flex flex-col justify-between">
      <div>
        <div className="w-14 h-14 rounded-2xl bg-slate-100 border border-slate-200 flex items-center justify-center text-[#0a1b33] mb-8">
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
            <path d="M14 2v6h6" />
            <path d="M8 13h8" />
            <path d="M8 17h5" />
          </svg>
        </div>
        <div className="text-[10px] font-semibold uppercase tracking-[0.24em] text-slate-400 mb-3">PDF certificate</div>
        <div className="font-display text-[28px] leading-[1.05] text-[#0a1b33]">Drop a PDF here</div>
      </div>
      <div className="h-2 rounded-full bg-slate-100 overflow-hidden">
        <div className="h-full w-2/3 bg-[#0a1b33]"></div>
      </div>
    </div>
  );

  const card = (
    <div className="h-full rounded-2xl bg-white border border-slate-200/70 shadow-sm overflow-hidden">
      <div className="bg-white border-b border-slate-200/70 overflow-hidden" style={{ aspectRatio: previewRatio }}>{visual}</div>
      <div className="p-5">
        <div className="flex flex-wrap items-center gap-1.5 mb-3">
          <span className="text-[10px] font-semibold uppercase tracking-[0.18em] px-2 py-0.5 rounded-full bg-slate-100 text-slate-600 border border-slate-200">{certificate.area || "Credential"}</span>
          <span className="text-[10px] font-semibold uppercase tracking-[0.18em] px-2 py-0.5 rounded-full bg-white text-slate-500 border border-slate-200">{certificate.date || "Completed"}</span>
        </div>
        <h3 className="font-display text-[21px] leading-snug font-medium text-[#0a1b33] truncate">{title}</h3>
        <p className="text-[12px] text-slate-500 mt-1 truncate">{certificate.issuer || "Issuer"}</p>
        {certificate.summary ? (
          <p
            className="text-[12px] leading-relaxed text-slate-500 mt-3"
            style={{ display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden" }}
          >
            {certificate.summary}
          </p>
        ) : null}
      </div>
    </div>
  );

  return canOpen ? (
    <a href={pdf} target="_blank" rel="noreferrer" className="block h-full" aria-label={`Open ${title} certificate`}>
      {card}
    </a>
  ) : card;
}

function CertificateMarquee({ certificates = CERTIFICATES }) {
  const items = [...certificates, ...certificates];
  const trackRef = React.useRef(null);
  const posRef = React.useRef(0);
  const baseSpeed = 0.8;
  const speedRef = React.useRef(baseSpeed);
  const pausedRef = React.useRef(false);
  const rafRef = React.useRef(null);
  const halfWidthRef = React.useRef(0);
  const scrollDecayRef = React.useRef(null);

  React.useEffect(() => {
    const track = trackRef.current;
    if (!track) return;
    const section = track.closest("section") || track.parentElement;

    const measureHalf = () => { halfWidthRef.current = track.scrollWidth / 2; };
    measureHalf();
    window.addEventListener("resize", measureHalf);

    const tick = () => {
      if (!pausedRef.current) {
        const half = halfWidthRef.current || track.scrollWidth / 2;
        posRef.current += speedRef.current;
        if (speedRef.current >= 0 && posRef.current >= half) posRef.current -= half;
        if (speedRef.current < 0 && posRef.current <= 0) posRef.current += half;
        track.style.transform = `translateX(-${posRef.current}px)`;
      }
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);

    const onWheel = (e) => {
      if (Math.abs(e.deltaX) <= Math.abs(e.deltaY)) return;
      speedRef.current = baseSpeed + e.deltaX * 0.18;
      clearTimeout(scrollDecayRef.current);
      scrollDecayRef.current = setTimeout(() => { speedRef.current = baseSpeed; }, 600);
    };
    section.addEventListener("wheel", onWheel, { passive: true });

    let touchStartX = 0, touchStartY = 0, swipeSteering = false;
    const onTouchStart = (e) => { touchStartX = e.touches[0].clientX; touchStartY = e.touches[0].clientY; swipeSteering = false; pausedRef.current = true; };
    const onTouchMove = (e) => {
      const dx = e.touches[0].clientX - touchStartX;
      const dy = e.touches[0].clientY - touchStartY;
      if (!swipeSteering && Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 6) swipeSteering = true;
      if (swipeSteering) { speedRef.current = baseSpeed - dx * 0.08; pausedRef.current = false; }
    };
    const onTouchEnd = () => { pausedRef.current = false; swipeSteering = false; speedRef.current = baseSpeed; };
    track.addEventListener("touchstart",  onTouchStart,  { passive: true });
    track.addEventListener("touchmove",   onTouchMove,   { passive: true });
    track.addEventListener("touchend",    onTouchEnd,    { passive: true });
    track.addEventListener("touchcancel", onTouchEnd,    { passive: true });

    const onMouseDown = () => { pausedRef.current = true; };
    const onMouseUp   = () => { pausedRef.current = false; };
    track.addEventListener("mousedown", onMouseDown);
    window.addEventListener("mouseup",  onMouseUp);

    return () => {
      cancelAnimationFrame(rafRef.current);
      clearTimeout(scrollDecayRef.current);
      window.removeEventListener("resize", measureHalf);
      window.removeEventListener("mouseup", onMouseUp);
      section.removeEventListener("wheel", onWheel);
      track.removeEventListener("touchstart",  onTouchStart);
      track.removeEventListener("touchmove",   onTouchMove);
      track.removeEventListener("touchend",    onTouchEnd);
      track.removeEventListener("touchcancel", onTouchEnd);
    };
  }, []);

  return (
    <section id="certificates" className="mt-4 md:mt-10 mb-16 md:mb-24">
      <div className="max-w-[1400px] mx-auto px-1 sm:px-2 mb-5 flex flex-col md:flex-row md:items-end md:justify-between gap-4">
        <div>
          <span className="text-[10px] sm:text-[11px] font-semibold text-slate-500 uppercase tracking-[0.24em] sm:tracking-[0.3em]">Certificates I&apos;ve completed</span>
          <p className="mt-3 text-[14px] md:text-[15px] leading-relaxed text-slate-500 max-w-xl">TryHackMe learning paths and CTF recognition that support the security projects in this portfolio.</p>
        </div>
      </div>
      <div className="overflow-hidden" style={{ cursor: "grab" }}>
        <div ref={trackRef} className="flex will-change-transform" style={{ width: "max-content" }}>
          {items.map((certificate, i) => (
            <div key={`${certificate.title}-${i}`} className="w-[300px] sm:w-[360px] mx-2 sm:mx-3 shrink-0">
              <CertificatePreview certificate={certificate} />
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

// ----- 2. Selected work (case studies, clickable → lightbox) -----
const SELECTED_WORK = [
  {
    id: "defend-discover", title: "Defend and Discover", role: "Electron desktop launcher", year: "2026",
    tags: ["Electron", "Desktop", "Tools"],
    cover: "previews/videos/defend-discover.mp4",
    summary: "A desktop control hub that launches the local defense and discovery tools from one polished Electron interface, tying VaultX, RV Scan, and Cartridge into a single workflow.",
  },
  {
    id: "cartridge", title: "Cartridge", role: "D&D companion app", year: "2026",
    tags: ["Desktop", "Tools", "D&D"],
    cover: "previews/videos/cartridge-10s.mp4",
    summary: "A companion desktop app bundled into Defend and Discover as another focused utility inside the local toolkit.",
  },
  {
    id: "vaultx", title: "VaultX", role: "Encrypted password manager", year: "2026",
    tags: ["Python", "Security", "Desktop"],
    cover: "previews/videos/vaultx-10s.mp4",
    summary: "A local password vault with an encrypted core, password health views, generator tooling, audit surfaces, and a desktop UI built for practical day-to-day security.",
  },
  {
    id: "rvscan", title: "RV Scan", role: "Rust malware scanner", year: "2026",
    tags: ["Rust", "YARA-X", "Iced"],
    cover: "previews/videos/rvscan-10s.mp4",
    summary: "A Rust-powered desktop scanner with signature checks, YARA-X rules, scan history, and quarantine workflow. It is packaged inside Defend and Discover as the defensive utility.",
  },
  {
    id: "omnisect", title: "Omnisect", role: "Recon automation platform", year: "2026",
    tags: ["React", "Recon", "Security"],
    frame: "wide",
    cover: "previews/videos/omnisect-10s.mp4",
    summary: "An authorized reconnaissance dashboard that turns a domain or URL into live hosts, endpoints, technologies, prioritized findings, and exportable evidence.",
  },
  {
    id: "taskrypt", title: "Taskrypt", role: "Escrow bounty platform", year: "2026",
    tags: ["Next.js", "Solidity", "Web3"],
    frame: "wide",
    cover: "previews/videos/taskrypt2-13s.mp4",
    summary: "A bounty and task marketplace where teams can lock rewards in escrow, review submissions, and move payouts through a clear smart-contract-backed workflow.",
  },
  {
    id: "evince", title: "Evince", role: "CTF cryptography workspace", year: "2026",
    tags: ["JavaScript", "CTF", "Security"],
    frame: "wide",
    cover: "previews/videos/evince.mp4",
    summary: "A local-first browser tool for CTF and crypto work — auto-identifies 300+ formats, cracks hashes, and solves classical ciphers and encodings.",
  },
  {
    id: "lyra", title: "Lyra", role: "Local AI chatbot", year: "2026",
    tags: ["Python", "AI", "FastAPI"],
    frame: "wide",
    cover: "previews/videos/lyra.mp4",
    summary: "A fully local AI chatbot — no cloud, no API keys, no data leaving your machine. Runs LLMs on your own hardware via Ollama.",
  },
];

function SelectedWork({ projects = SELECTED_WORK }) {
  // Cards navigate to the project detail page (pages/project.html?id=...)

  const openProject = (id) => { window.location.href = `pages/project.html?id=${id}`; };

  return (
    <section id="work" className="max-w-[1400px] mx-auto px-1 sm:px-2 py-14 md:py-28 scroll-mt-24">
      <SectionHeader
        eyebrow="Selected work"
        title="Recent projects"
        subtext="Real builds from my local workspace - desktop launchers, security tools, recon automation, and escrow-backed bounty workflows."
        action={
          <a href="pages/work-archive.html" className="hidden md:inline-flex items-center gap-2 rounded-full px-5 py-2.5 bg-white border border-slate-200/70 text-[13px] font-semibold text-[#0a1b33] shadow-sm hover:border-slate-300 transition-all">
            View archive
            <IconArrowUpRight className="w-3.5 h-3.5" />
          </a>
        }
      />

      <div className="grid grid-cols-1 md:grid-cols-2 gap-6 md:gap-8">
        {projects.map((p, i) => (
          <FadeIn key={p.id} delay={0.05 + i * 0.05}>
            <button onClick={() => openProject(p.id)} className="group block w-full text-left">
              <div className={`kenburns overflow-hidden rounded-2xl md:rounded-3xl bg-slate-100 ${projectFrameClass(p)} border border-slate-200/60`}>
                <ProjectMedia src={p.cover} label={p.title}
                     style={{ viewTransitionName: `cover-${p.id}` }}
                     className={projectMediaClass(p)} />
              </div>
              <div className="mt-5 flex items-end justify-between gap-4">
                <div className="min-w-0">
                  <div className="flex flex-wrap items-center gap-1.5 mb-1.5">
                    {p.tags.map(t => (
                      <span key={t} className="text-[10px] font-semibold uppercase tracking-[0.18em] px-2 py-0.5 rounded-full bg-slate-100 text-slate-600 border border-slate-200">{t}</span>
                    ))}
                  </div>
                  <h3 className="font-display text-[22px] md:text-[26px] font-medium text-[#0a1b33]">{p.title}</h3>
                  <p className="text-[13px] text-slate-500 mt-0.5">{p.role} · {p.year}</p>
                </div>
                <span className="shrink-0 inline-flex items-center gap-1 text-[12px] font-semibold text-[#0a1b33] group-hover:gap-2 transition-all">
                  Read
                  <IconArrowUpRight className="w-3.5 h-3.5" />
                </span>
              </div>
            </button>
          </FadeIn>
        ))}
      </div>
    </section>
  );
}

const JOURNAL = [
  { title: "Defend and Discover ties the desktop tools together", status: "Shipped",     summary: "Electron launcher with VaultX, RV Scan, and Cartridge surfaced from one control panel.", date: "May 2026", img: "previews/videos/defend-discover.mp4", projectId: "defend-discover" },
  { title: "Cartridge joins the D&D toolkit",                     status: "Shipped",     summary: "A companion desktop app now shown as its own portfolio project and bundled utility.",    date: "May 2026", img: "previews/videos/cartridge-10s.mp4", projectId: "cartridge" },
  { title: "Omnisect turns recon into a dashboard",                status: "Shipped",     summary: "Authorized attack-surface mapping with targets, findings, history, and reports.", date: "May 2026",     img: "previews/videos/omnisect-10s.mp4", projectId: "omnisect" },
  { title: "Taskrypt packages bounties around escrow",             status: "In progress", summary: "Next.js and Solidity flow for funded tasks, submissions, review, and payout states.", date: "Now building", img: "previews/videos/taskrypt-10s.mp4", projectId: "taskrypt" },
  { title: "VaultX keeps the password workflow local",             status: "Shipped",     summary: "Encrypted vault, generator, health checks, audit screens, and desktop packaging.", date: "May 2026",     img: "previews/videos/vaultx-10s.mp4", projectId: "vaultx" },
];

function JournalSection({ journal = JOURNAL }) {
  return (
    <section id="journal" className="max-w-[1200px] mx-auto px-1 sm:px-2 md:px-6 lg:px-10 py-14 md:py-24 scroll-mt-24">
      <SectionHeader
        eyebrow="Ideas & in-flight"
        title="Build notes and current work."
        subtext="A live shortlist of the tools I am shipping, packaging, and turning into portfolio-ready case studies."
        action={
          <a href="pages/work-archive.html" className="hidden md:inline-flex items-center gap-2 rounded-full px-5 py-2.5 bg-white border border-slate-200/70 text-[13px] font-semibold text-[#0a1b33] shadow-sm hover:border-slate-300 transition-all">
            View all
            <IconArrowUpRight className="w-3.5 h-3.5" />
          </a>
        }
      />

      <div className="flex flex-col gap-4">
        {journal.map((entry, i) => (
          <FadeIn key={entry.title} delay={0.05 + i * 0.05}>
            <a href={journalEntryHref(entry)} className="group flex items-center gap-3 md:gap-6 p-3 md:p-4 pr-4 md:pr-8 rounded-2xl sm:rounded-full bg-white border border-slate-200/60 hover:border-slate-300 hover:shadow-sm transition-all">
              <ProjectMedia src={entry.img} label={entry.title} staticAt={1} className="block w-16 h-16 md:w-20 md:h-20 rounded-2xl sm:rounded-full object-cover shrink-0" />
              <div className="min-w-0 flex-1">
                <div className="flex items-center gap-2 mb-1">
                  <span className={"text-[10px] font-semibold uppercase tracking-[0.18em] px-2 py-0.5 rounded-full " + (
                    entry.status === "Shipped"     ? "bg-emerald-50 text-emerald-700 border border-emerald-100" :
                    entry.status === "In progress" ? "bg-amber-50 text-amber-700 border border-amber-100" :
                                                     "bg-slate-100 text-slate-600 border border-slate-200"
                  )}>
                    {entry.status}
                  </span>
                  <span className="text-[10px] uppercase tracking-[0.18em] text-slate-400">{entry.date}</span>
                </div>
                <h3 className="font-display text-[16px] md:text-[22px] leading-snug font-medium text-[#0a1b33] truncate">
                  {entry.title}
                </h3>
                <p className="hidden md:block text-[13px] text-slate-500 mt-1 truncate">{entry.summary}</p>
              </div>
              <span className="w-9 h-9 rounded-full bg-slate-100 group-hover:bg-[#0a1b33] group-hover:text-white text-[#0a1b33] flex items-center justify-center transition-colors shrink-0">
                <IconArrowUpRight className="w-3.5 h-3.5" />
              </span>
            </a>
          </FadeIn>
        ))}
      </div>
    </section>
  );
}

// ----- exported wrapper -----
const PORTFOLIO_STATS = [
  { num: "6",       label: "Projects featured in this portfolio" },
  { num: "3",       label: "Tools bundled inside Defend and Discover" },
  { num: "5",       label: "Main languages across the builds" },
  { num: "100%",    label: "Recorded from real project assets" },
];

function StatsSection({ stats = PORTFOLIO_STATS }) {
  return (
    <section className="max-w-[1400px] mx-auto px-1 sm:px-2 py-14 md:py-24 scroll-mt-24">
      <FadeIn className="mb-10 md:mb-14">
        <span className="text-[11px] font-semibold text-slate-500 uppercase tracking-[0.3em]">By the numbers</span>
      </FadeIn>
      <div className="grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-10 py-10 md:py-12 border-y border-slate-200/70">
        {stats.map((s, i) => (
          <FadeIn key={s.label} delay={0.05 + i * 0.06}>
            <div className="font-display text-[36px] md:text-[64px] lg:text-[72px] leading-[0.95] tracking-tight font-medium text-[#0a1b33]">{s.num}</div>
            <div className="text-[12px] md:text-[13px] text-slate-500 mt-3 leading-relaxed max-w-[12rem]">{s.label}</div>
          </FadeIn>
        ))}
      </div>
    </section>
  );
}

const FAQS = [
  { q: "What is Defend and Discover?",
    a: "Defend and Discover is an Electron desktop hub for the local tools VaultX, RV Scan, and Cartridge. It gives them one clean launch surface." },
  { q: "What is Cartridge?",
    a: "Cartridge is another app inside the Defend and Discover toolkit. The portfolio now gives it its own project entry and preview recording." },
  { q: "What is Omnisect for?",
    a: "Omnisect is for authorized reconnaissance. It organizes target setup, scan phases, findings, history, and reporting into one dashboard." },
  { q: "What is Taskrypt?",
    a: "Taskrypt is the bounty project. The portfolio names it Taskrypt and frames it as an escrow-backed task and bounty workflow built with Next.js and Solidity." },
  { q: "Are these previews real?",
    a: "Yes. The previews use the real project recordings, and playback loops within the first 10 seconds." },
];

function FAQSection() {
  return (
    <section className="max-w-[1200px] mx-auto px-1 sm:px-2 md:px-6 py-14 md:py-24 scroll-mt-24">
      <SectionHeader
        eyebrow="FAQ"
        title="Project notes."
        subtext="Short answers about the tools shown above."
      />
      <div className="divide-y divide-slate-200/70 border-y border-slate-200/70">
        {FAQS.map((f, i) => (
          <FadeIn key={f.q} delay={0.03 + i * 0.04}>
            <details className="group py-5 md:py-6">
              <summary className="flex items-start justify-between gap-6 cursor-pointer list-none">
                <h3 className="font-display text-[18px] md:text-[22px] font-medium text-[#0a1b33] leading-snug">{f.q}</h3>
                <span className="shrink-0 w-8 h-8 rounded-full border border-slate-200 flex items-center justify-center text-[#0a1b33] transition-transform group-open:rotate-45">
                  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M12 5v14M5 12h14"/></svg>
                </span>
              </summary>
              <p className="mt-4 pr-12 text-[14px] md:text-[15px] leading-relaxed text-slate-600 max-w-2xl">{f.a}</p>
            </details>
          </FadeIn>
        ))}
      </div>
    </section>
  );
}

function usePortfolioData() {
  const [portfolio, setPortfolio] = useState({
    projects: SELECTED_WORK,
    journal: JOURNAL,
    stats: PORTFOLIO_STATS,
    certificates: CERTIFICATES,
  });

  useEffect(() => {
    let cancelled = false;
    const markReady = () => {
      window.__portfolioReady = true;
      window.dispatchEvent(new Event("portfolio:ready"));
    };
    Promise.all([
      fetch("api/projects.php", { headers: { Accept: "application/json" } })
        .then((response) => {
          if (!response.ok) throw new Error("Project API unavailable");
          return response.json();
        }),
      fetch("api/certificates.php", { headers: { Accept: "application/json" } })
        .then((response) => {
          if (!response.ok) throw new Error("Certificate API unavailable");
          return response.json();
        })
        .catch(() => ({ certificates: CERTIFICATES })),
    ])
      .then(([data, certificateData]) => {
        if (cancelled) return;
        setPortfolio({
          projects: data.projects || SELECTED_WORK,
          journal: data.journal || JOURNAL,
          stats: data.stats || PORTFOLIO_STATS,
          certificates: certificateData.certificates || CERTIFICATES,
        });
        requestAnimationFrame(markReady);
      })
      .catch(() => {
        if (!cancelled) requestAnimationFrame(markReady);
      });

    return () => { cancelled = true; };
  }, []);

  return portfolio;
}

function StudioBody() {
  const portfolio = usePortfolioData();
  return (
    <div className="font-sans" style={{ color: "#051A24" }}>
      <ProjectMarquee projects={portfolio.projects} />
      <CertificateMarquee certificates={portfolio.certificates} />
      <SelectedWork projects={portfolio.projects} />
      <StatsSection stats={portfolio.stats} />
      <JournalSection journal={portfolio.journal} />
      <FAQSection />
    </div>
  );
}

window.StudioBody = StudioBody;
