/* ============== App ============== */
const { useState, useEffect, useRef } = React;

const projects = JSON.parse(document.getElementById("projects-data").textContent);

/* ----- Interactive hero carousel (drag + auto-roll + focus scaling) ----- */
function HeroCarousel() {
  const trackRef = React.useRef(null);
  const viewportRef = React.useRef(null);
  // duplicate list for seamless looping
  const items = [...projects, ...projects, ...projects];
  const baseLen = projects.length;

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

    let x = 0;             // current translate
    let target = 0;        // eased target (for drag)
    let dragging = false;
    let startX = 0;
    let startTarget = 0;
    let velocity = 0.45;   // auto-scroll px per frame
    let lastPointerX = 0;
    let paused = false;
    let raf;

    // start one-third in so we can scroll both directions
    function unit() { return track.scrollWidth / 3; }
    x = -unit();
    target = x;

    function applyFocus() {
      const vpRect = vp.getBoundingClientRect();
      const center = vpRect.left + vpRect.width / 2;
      track.querySelectorAll(".hc-card").forEach((card) => {
        const r = card.getBoundingClientRect();
        const cardCenter = r.left + r.width / 2;
        const dist = Math.abs(center - cardCenter) / vpRect.width;
        const k = Math.max(0, 1 - dist * 1.6);
        const scale = 0.86 + k * 0.14;
        const op = 0.45 + k * 0.55;
        card.style.transform = `scale(${scale.toFixed(3)})`;
        card.style.opacity = op.toFixed(3);
        card.style.setProperty("--focus", k.toFixed(3));
      });
    }

    function frame() {
      if (!dragging && !paused) target -= velocity;
      // wrap
      const u = unit();
      if (target <= -u * 2) target += u;
      if (target >= 0) target -= u;
      x += (target - x) * (dragging ? 0.35 : 0.12);
      track.style.transform = `translate3d(${x.toFixed(2)}px,0,0)`;
      applyFocus();
      raf = requestAnimationFrame(frame);
    }
    raf = requestAnimationFrame(frame);

    // pointer drag
    function down(e) {
      dragging = true;
      vp.dataset.moved = "0";
      vp.classList.add("is-dragging");
      startX = e.clientX ?? (e.touches && e.touches[0].clientX) ?? 0;
      lastPointerX = startX;
      startTarget = target;
      window.addEventListener("pointermove", move);
      window.addEventListener("pointerup", up);
    }
    function move(e) {
      const cx = e.clientX ?? 0;
      if (Math.abs(cx - startX) > 6) vp.dataset.moved = "1";
      target = startTarget + (cx - startX);
      velocity = Math.max(0.2, Math.min(2.5, (lastPointerX - cx) * 0.3)) || 0.45;
      lastPointerX = cx;
    }
    function up() {
      dragging = false;
      vp.classList.remove("is-dragging");
      if (velocity < 0.2 || isNaN(velocity)) velocity = 0.45;
      window.removeEventListener("pointermove", move);
      window.removeEventListener("pointerup", up);
      // clear the moved flag shortly after so the click handler can read it first
      setTimeout(() => { vp.dataset.moved = "0"; }, 50);
    }
    vp.addEventListener("pointerdown", down);

    // pause on hover
    vp.addEventListener("mouseenter", () => { paused = true; });
    vp.addEventListener("mouseleave", () => { paused = false; });

    // arrow buttons
    const prevBtn = document.getElementById("hc-prev");
    const nextBtn = document.getElementById("hc-next");
    const step = () => (track.querySelector(".hc-card")?.getBoundingClientRect().width || 320) + 20;
    const onPrev = () => { target += step(); };
    const onNext = () => { target -= step(); };
    prevBtn && prevBtn.addEventListener("click", onPrev);
    nextBtn && nextBtn.addEventListener("click", onNext);

    return () => {
      cancelAnimationFrame(raf);
      vp.removeEventListener("pointerdown", down);
      prevBtn && prevBtn.removeEventListener("click", onPrev);
      nextBtn && nextBtn.removeEventListener("click", onNext);
    };
  }, []);

  return (
    <div className="hc">
      <div className="hc-viewport" ref={viewportRef} data-cursor="hide">
        <div className="hc-track" ref={trackRef}>
          {items.map((p, i) => (
            <a className="hc-card" href={p.href} key={i} draggable="false"
               onClick={(e) => { if (viewportRef.current?.dataset.moved === "1") e.preventDefault(); }}>
              <div className="hc-media">
                <img src={p.image} alt={p.client} loading={i < 6 ? "eager" : "lazy"} referrerPolicy="no-referrer" draggable="false" />
                <span className="hc-index">{String((i % baseLen) + 1).padStart(2, "0")}</span>
              </div>
              <div className="hc-info">
                <span className="hc-title">{p.subtitle.replace(/<[^>]+>/g, "")}</span>
                <span className="hc-meta">{p.tags.slice(0, 2).join(" · ")}</span>
              </div>
              <span className="hc-open">View case <svg width="13" height="13" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M3 11L11 3M11 3H5M11 3v6"/></svg></span>
            </a>
          ))}
        </div>
      </div>
      <div className="hc-controls">
        <button className="hc-arrow" id="hc-prev" aria-label="Previous" data-cursor="hide">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M15 6l-6 6 6 6"/></svg>
        </button>
        <button className="hc-arrow" id="hc-next" aria-label="Next" data-cursor="hide">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M9 6l6 6-6 6"/></svg>
        </button>
      </div>
    </div>
  );
}

const heroCarouselEl = document.getElementById("hero-carousel");
if (heroCarouselEl) ReactDOM.createRoot(heroCarouselEl).render(<HeroCarousel />);

/* ----- Hero word rotator ----- */
(function rotator() {
  const words = ["next tap.", "next click.", "next return.", "next yes.", "long haul."];
  let i = 0;
  const slot = document.querySelector("#rotator .slot");
  if (!slot) return;
  setInterval(() => {
    i = (i + 1) % words.length;
    slot.style.transition = "transform .55s cubic-bezier(.6,.0,.3,1), opacity .35s ease";
    slot.style.transform = "translateY(-110%)";
    slot.style.opacity = "0";
    setTimeout(() => {
      slot.textContent = words[i];
      slot.style.transition = "none";
      slot.style.transform = "translateY(110%)";
      slot.style.opacity = "0";
      // next frame
      requestAnimationFrame(() => {
        slot.style.transition = "transform .55s cubic-bezier(.2,.7,.2,1), opacity .35s ease";
        slot.style.transform = "translateY(0)";
        slot.style.opacity = "1";
      });
    }, 550);
  }, 2800);
})();

/* ----- Featured sticky stack ----- */
function FeaturedStack() {
  return (
    <>
      {projects.map((p, idx) => {
        // Each card is sticky and stacks. We translate each subsequent card to its own offset for the "fan" effect.
        const stickyTop = 88 + idx * 18;
        return (
          <article
            key={p.slug}
            className="feat-card reveal"
            data-delay={Math.min(idx, 3)}
            style={{ top: stickyTop }}
          >
            <a href={p.href} className="feat-cover" data-cursor="view" aria-label={`Open ${p.client} case study`}>
              <img referrerPolicy="no-referrer" src={p.image} alt={p.client} loading={idx < 2 ? "eager" : "lazy"} />
            </a>
            <div className="feat-info">
              <div>
                <span className="feat-eyebrow">{p.subtitle.replace(/<[^>]+>/g, "")}</span>
                <h3 className="feat-title">{p.title.replace(/<[^>]+>/g, "")}</h3>
                <p className="feat-desc">{p.desc.replace(/<[^>]+>/g, "")}</p>
              </div>
              <div className="feat-bottom">
                <a className="feat-cta" href={p.href} data-cursor="view">
                  View case study
                  <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 11L11 3M11 3H5M11 3v6"/></svg>
                </a>
              </div>
            </div>
          </article>
        );
      })}
    </>
  );
}

const stackEl = document.getElementById("feat-stack");
if (stackEl) ReactDOM.createRoot(stackEl).render(<FeaturedStack />);

/* ============== Custom dot cursor ============== */
(function dotCursor() {
  const dot = document.getElementById("dot-cursor");
  if (!dot) return;
  let tx = -100, ty = -100, cx = -100, cy = -100;
  let rafId;
  function tick() {
    cx += (tx - cx) * 0.22;
    cy += (ty - cy) * 0.22;
    dot.style.transform = `translate(${cx}px, ${cy}px) translate(-50%, -50%)`;
    rafId = requestAnimationFrame(tick);
  }
  tick();
  window.addEventListener("mousemove", (e) => { tx = e.clientX; ty = e.clientY; });

  // Hover detection: scale up when over project surfaces
  function bind() {
    document.querySelectorAll("[data-cursor='view']").forEach((el) => {
      if (el.__cursorBound) return; el.__cursorBound = true;
      el.addEventListener("mouseenter", () => dot.classList.add("is-hover"));
      el.addEventListener("mouseleave", () => dot.classList.remove("is-hover"));
    });
  }
  bind();
  setTimeout(bind, 300);
  setTimeout(bind, 1000);
  setTimeout(bind, 2200);
})();

/* ============== Mobile menu ============== */
(function mobileMenu() {
  const btn = document.getElementById("mobile-toggle");
  const close = document.getElementById("mobile-close");
  const sheet = document.getElementById("mobile-sheet");
  if (!btn || !sheet) return;
  function open() { sheet.classList.add("is-open"); btn.setAttribute("aria-expanded", "true"); document.body.style.overflow = "hidden"; }
  function shut() { sheet.classList.remove("is-open"); btn.setAttribute("aria-expanded", "false"); document.body.style.overflow = ""; }
  btn.addEventListener("click", open);
  close && close.addEventListener("click", shut);
  sheet.querySelectorAll("a").forEach((a) => a.addEventListener("click", shut));
  window.addEventListener("keydown", (e) => { if (e.key === "Escape") shut(); });
})();

/* ============== Contact form: submit + CAPTCHA ============== */
(function contactForm() {
  const form = document.getElementById("contact-form");
  if (!form) return;
  const success = document.getElementById("form-success");
  const qEl = document.getElementById("cf-captcha-q");
  const capIn = document.getElementById("cf-captcha");

  // Simple, accessible math CAPTCHA — regenerated on load and after each submit.
  let answer = 0;
  function newCaptcha() {
    const a = Math.floor(Math.random() * 8) + 1;
    const b = Math.floor(Math.random() * 8) + 1;
    answer = a + b;
    if (qEl) qEl.textContent = "what is " + a + " + " + b + "?";
    if (capIn) capIn.value = "";
  }
  newCaptcha();

  form.addEventListener("submit", (e) => {
    e.preventDefault();
    const name = form.querySelector("#cf-name").value.trim();
    const email = form.querySelector("#cf-email").value.trim();
    const message = form.querySelector("#cf-project").value.trim();
    const cap = capIn ? capIn.value.trim() : "";

    if (!name || !email || !message) {
      success.textContent = "Please fill the required fields ✱";
      success.style.color = "var(--cream)";
      return;
    }
    if (parseInt(cap, 10) !== answer) {
      success.textContent = "That answer isn't right — give the sum another try.";
      success.style.color = "var(--cream)";
      newCaptcha();
      return;
    }
    success.textContent = "Thanks — your message is on its way. I'll reply within a working day. ✦";
    success.style.color = "var(--accent)";
    form.reset();
    newCaptcha();
  });
})();

/* ============== Scroll progress bar ============== */
(function scrollProgress() {
  const bar = document.createElement("div");
  bar.className = "scroll-progress";
  document.body.appendChild(bar);
  function update() {
    const h = document.documentElement;
    const max = h.scrollHeight - h.clientHeight;
    const p = max > 0 ? h.scrollTop / max : 0;
    bar.style.setProperty("--p", p.toFixed(4));
  }
  update();
  window.addEventListener("scroll", update, { passive: true });
  window.addEventListener("resize", update);
})();

/* ============== Topbar scroll state ============== */
const topbar = document.getElementById("topbar");
function onScroll() {
  if (!topbar) return;
  if (window.scrollY > 12) topbar.classList.add("is-scrolled");
  else topbar.classList.remove("is-scrolled");
}
onScroll();
window.addEventListener("scroll", onScroll, { passive: true });

/* ============== Reveal on scroll (JS-gated) ============== */
const prefersReduced = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (!prefersReduced && "IntersectionObserver" in window) {
  document.documentElement.classList.add("js-reveal");
}
const io = new IntersectionObserver(
  (entries) => {
    entries.forEach((e) => {
      if (e.isIntersecting) {
        e.target.classList.add("is-in");
        io.unobserve(e.target);
      }
    });
  },
  { threshold: 0.06, rootMargin: "0px 0px -10% 0px" }
);
function revealAboveFold(el) {
  const r = el.getBoundingClientRect();
  return r.top < (window.innerHeight || 800) * 0.95;
}
function attachReveal() {
  document.querySelectorAll(".reveal:not(.is-bound)").forEach((el) => {
    el.classList.add("is-bound");
    if (revealAboveFold(el)) el.classList.add("is-in");
    else io.observe(el);
  });
}
attachReveal();
requestAnimationFrame(attachReveal);
setTimeout(attachReveal, 200);
setTimeout(attachReveal, 800);
setTimeout(attachReveal, 1800);
// Hard failsafe
setTimeout(() => {
  document.querySelectorAll(".reveal:not(.is-in)").forEach((el) => el.classList.add("is-in"));
}, 2500);

/* ============== Nav active section ============== */
const sections = ["work", "about", "contact"];
const navAs = sections.map((id) => document.querySelector(`.nav-links a[href="#${id}"]`));
const sectionEls = sections.map((id) => document.getElementById(id));
window.addEventListener("scroll", () => {
  const mid = window.scrollY + window.innerHeight * 0.35;
  let activeIdx = -1;
  sectionEls.forEach((s, i) => {
    if (!s) return;
    if (s.offsetTop <= mid) activeIdx = i;
  });
  navAs.forEach((a, i) => {
    if (!a) return;
    if (i === activeIdx) a.classList.add("is-active");
    else a.classList.remove("is-active");
  });
}, { passive: true });
