/* =============================================================
   UPPAL — Interaction engine
   Lenis smooth scroll · GSAP ScrollTrigger · cursor · magnetics
   ============================================================= */
import gsap from "gsap";
import ScrollTrigger from "gsap/ScrollTrigger";
import Lenis from "lenis";

gsap.registerPlugin(ScrollTrigger);

const prefersReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const isTouch = window.matchMedia("(hover: none)").matches || window.innerWidth < 861;

/* -------------------------------------------------------------
   LENIS SMOOTH SCROLL  (synced with GSAP ScrollTrigger)
------------------------------------------------------------- */
let lenis;
function initLenis() {
  if (prefersReduced) return;
  lenis = new Lenis({
    duration: 1.15,
    easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
    smoothWheel: true,
    touchMultiplier: 1.6,
  });
  lenis.on("scroll", ScrollTrigger.update);
  gsap.ticker.add((time) => lenis.raf(time * 1000));
  gsap.ticker.lagSmoothing(0);
  window.__lenis = lenis;
}

/* -------------------------------------------------------------
   PRELOADER
------------------------------------------------------------- */
function runPreloader(onDone) {
  const pre = document.getElementById("preloader");
  const curtain = document.getElementById("curtain");
  const bar = document.getElementById("preloaderBar");
  const countEl = document.getElementById("preloaderCount");
  const letters = document.querySelectorAll("#preloaderLogo span");
  const sub = document.getElementById("preloaderSub");

  if (prefersReduced) {
    pre.style.display = "none";
    curtain.style.display = "none";
    onDone && onDone();
    return;
  }

  document.documentElement.style.overflow = "hidden";
  const counter = { v: 0 };
  const tl = gsap.timeline({ defaults: { ease: "power3.out" } });

  tl.to(letters, { y: 0, opacity: 1, duration: 0.9, stagger: 0.07, ease: "power4.out" })
    .to(sub, { opacity: 1, duration: 0.6 }, "-=0.4")
    .to(counter, {
      v: 100, duration: 2.0, ease: "power2.inOut",
      onUpdate: () => {
        const val = Math.round(counter.v);
        countEl.textContent = val;
        bar.style.width = val + "%";
      },
    }, 0.2)
    .to("#preloaderLogo span", { y: -14, opacity: 0, duration: 0.6, stagger: 0.04, ease: "power3.in" }, "+=0.15")
    .to([sub, countEl], { opacity: 0, duration: 0.4 }, "<")
    .to(pre, { yPercent: -100, duration: 0.9, ease: "power4.inOut" }, "-=0.1")
    .to(curtain, { yPercent: -100, duration: 1.0, ease: "power4.inOut", onComplete: () => {
        pre.style.display = "none";
        curtain.style.display = "none";
        document.documentElement.style.overflow = "";
        onDone && onDone();
      } }, "-=0.75");
}

/* -------------------------------------------------------------
   CUSTOM CURSOR + GLOW
------------------------------------------------------------- */
function initCursor() {
  if (isTouch) return;
  const dot = document.querySelector(".cursor-dot");
  const ring = document.querySelector(".cursor-ring");
  const glow = document.querySelector(".cursor-glow");
  let mx = window.innerWidth / 2, my = window.innerHeight / 2;
  let rx = mx, ry = my, gx = mx, gy = my;

  window.addEventListener("mousemove", (e) => {
    mx = e.clientX; my = e.clientY;
    dot.style.transform = `translate(${mx}px, ${my}px) translate(-50%,-50%)`;
    if (glow.style.opacity !== "1") glow.style.opacity = "1";
  });

  gsap.ticker.add(() => {
    rx += (mx - rx) * 0.18; ry += (my - ry) * 0.18;
    gx += (mx - gx) * 0.08; gy += (my - gy) * 0.08;
    ring.style.transform = `translate(${rx}px, ${ry}px) translate(-50%,-50%)`;
    glow.style.transform = `translate(${gx}px, ${gy}px) translate(-50%,-50%)`;
  });

  document.querySelectorAll('[data-cursor="hover"], a, button').forEach((el) => {
    el.addEventListener("mouseenter", () => document.body.classList.add("cursor-hover"));
    el.addEventListener("mouseleave", () => document.body.classList.remove("cursor-hover"));
  });
  window.addEventListener("mouseleave", () => { glow.style.opacity = "0"; });
}

/* -------------------------------------------------------------
   MAGNETIC BUTTONS  + local spotlight for gold fill
------------------------------------------------------------- */
function initMagnetic() {
  if (isTouch) return;
  document.querySelectorAll("[data-magnetic]").forEach((el) => {
    const strength = el.classList.contains("nav__logo") ? 0.25 : 0.4;
    el.addEventListener("mousemove", (e) => {
      const r = el.getBoundingClientRect();
      const x = e.clientX - (r.left + r.width / 2);
      const y = e.clientY - (r.top + r.height / 2);
      gsap.to(el, { x: x * strength, y: y * strength, duration: 0.5, ease: "power3.out" });
      el.style.setProperty("--mx", `${e.clientX - r.left}px`);
      el.style.setProperty("--my", `${e.clientY - r.top}px`);
    });
    el.addEventListener("mouseleave", () => {
      gsap.to(el, { x: 0, y: 0, duration: 0.7, ease: "elastic.out(1, 0.35)" });
    });
  });
}

/* -------------------------------------------------------------
   RIPPLE
------------------------------------------------------------- */
function initRipple() {
  document.querySelectorAll("[data-ripple]").forEach((el) => {
    el.addEventListener("pointerdown", (e) => {
      const r = el.getBoundingClientRect();
      const span = document.createElement("span");
      span.className = "ripple";
      const size = Math.max(r.width, r.height) * 2.2;
      span.style.width = span.style.height = size + "px";
      span.style.left = e.clientX - r.left + "px";
      span.style.top = e.clientY - r.top + "px";
      el.appendChild(span);
      gsap.fromTo(span, { scale: 0, opacity: 0.5 },
        { scale: 1, opacity: 0, duration: 0.8, ease: "power2.out", onComplete: () => span.remove() });
    });
  });
}

/* -------------------------------------------------------------
   TEXT SPLITTING  (lines -> words in masked spans)
------------------------------------------------------------- */
function splitToWords(el) {
  const text = el.textContent.trim();
  const nodes = Array.from(el.childNodes);
  // Preserve existing inner HTML structure for simple cases: rebuild from words
  el.innerHTML = "";
  // Support <br> as hard breaks
  const parts = text.split(/(\s+)/);
  parts.forEach((p) => {
    if (/^\s+$/.test(p)) { el.appendChild(document.createTextNode(" ")); return; }
    if (!p) return;
    const w = document.createElement("span");
    w.className = "reveal-word";
    const inner = document.createElement("span");
    inner.textContent = p;
    w.appendChild(inner);
    el.appendChild(w);
  });
  return el.querySelectorAll(".reveal-word > span");
}

/* -------------------------------------------------------------
   HERO INTRO
------------------------------------------------------------- */
function initHero() {
  const title = document.querySelector('[data-hero="title"]');
  // Keep the styled inner span; wrap plain text words while preserving the gold span
  wrapHeroTitle(title);
  const heroImg = document.querySelector("[data-hero-img] img");

  const tl = gsap.timeline({ delay: 0.1, defaults: { ease: "power4.out" } });
  tl.from('[data-hero="eyebrow"]', { opacity: 0, y: 20, duration: 0.8 }, 0)
    .set('[data-hero="eyebrow"]', { opacity: 1 })
    .to(".hero__title .reveal-word > span", { y: 0, duration: 1.1, stagger: 0.09 }, 0.15)
    .to('[data-hero="lead"]', { opacity: 1, y: 0, duration: 1, }, "-=0.6")
    .from('[data-hero="lead"]', { y: 24, duration: 1 }, "<")
    .to('[data-hero="actions"]', { opacity: 1, y: 0, duration: 0.9 }, "-=0.7")
    .from('[data-hero="actions"]', { y: 20, duration: 0.9 }, "<")
    .to('[data-hero="scroll"]', { opacity: 1, duration: 0.8 }, "-=0.5")
    .to('[data-hero="side"]', { opacity: 1, duration: 0.8 }, "<");

  // Slow cinematic zoom on hero image
  if (heroImg && !prefersReduced) {
    gsap.to(heroImg, { scale: 1.02, duration: 14, ease: "none", repeat: -1, yoyo: true });
    gsap.to(heroImg, {
      yPercent: 12, ease: "none",
      scrollTrigger: { trigger: ".hero", start: "top top", end: "bottom top", scrub: true },
    });
  }
  // Parallax hero text out
  gsap.to(".hero__inner", {
    yPercent: -18, opacity: 0.2, ease: "none",
    scrollTrigger: { trigger: ".hero", start: "top top", end: "bottom top", scrub: true },
  });
}

function wrapHeroTitle(title) {
  if (!title) return;
  const frag = document.createDocumentFragment();
  title.childNodes.forEach(() => {});
  const children = Array.from(title.childNodes);
  title.innerHTML = "";
  children.forEach((node) => {
    if (node.nodeType === Node.TEXT_NODE) {
      const words = node.textContent.split(/(\s+)/);
      words.forEach((wtext) => {
        if (/^\s+$/.test(wtext)) { frag.appendChild(document.createTextNode(" ")); return; }
        if (!wtext) return;
        const w = document.createElement("span");
        w.className = "reveal-word";
        const s = document.createElement("span");
        s.textContent = wtext;
        w.appendChild(s);
        frag.appendChild(w);
      });
    } else {
      // element (the gold span) — wrap whole as one reveal word
      const w = document.createElement("span");
      w.className = "reveal-word";
      node.style.display = "inline-block";
      w.appendChild(node);
      frag.appendChild(w);
      frag.appendChild(document.createTextNode(" "));
    }
  });
  title.appendChild(frag);
}

/* -------------------------------------------------------------
   SCROLL REVEALS
------------------------------------------------------------- */
function initReveals() {
  // Split-line headings -> word reveal on scroll
  document.querySelectorAll("[data-splitlines]").forEach((el) => {
    const spans = splitHeadingKeepBreaks(el);
    gsap.set(spans, { yPercent: 110 });
    ScrollTrigger.create({
      trigger: el, start: "top 85%",
      onEnter: () => gsap.to(spans, { yPercent: 0, duration: 1.1, stagger: 0.06, ease: "power4.out" }),
    });
  });

  // Simple eyebrow reveal
  document.querySelectorAll("[data-reveal]").forEach((el) => {
    gsap.set(el, { opacity: 0, y: 20 });
    ScrollTrigger.create({
      trigger: el, start: "top 90%",
      onEnter: () => gsap.to(el, { opacity: 1, y: 0, duration: 0.9, ease: "power3.out" }),
    });
  });

  // Generic .fx / .fx-blur
  document.querySelectorAll(".fx, .fx-blur").forEach((el) => {
    ScrollTrigger.create({
      trigger: el, start: "top 88%",
      onEnter: () => el.classList.add("in"),
    });
  });

  // Gold dividers
  document.querySelectorAll("[data-divider]").forEach((el) => {
    ScrollTrigger.create({
      trigger: el, start: "top 90%",
      onEnter: () => gsap.to(el, { scaleX: 1, duration: 1.2, ease: "power3.out" }),
    });
  });

  // Cards stagger
  const cards = document.querySelectorAll("[data-reveal-card]");
  if (cards.length) {
    gsap.set(cards, { opacity: 0, y: 60 });
    ScrollTrigger.create({
      trigger: ".cards", start: "top 80%",
      onEnter: () => gsap.to(cards, { opacity: 1, y: 0, duration: 1, stagger: 0.12, ease: "power3.out" }),
    });
  }
}

function splitHeadingKeepBreaks(el) {
  const html = el.innerHTML;
  const lines = html.split(/<br\s*\/?>/i);
  el.innerHTML = "";
  const allSpans = [];
  lines.forEach((line, li) => {
    const lineWrap = document.createElement("span");
    lineWrap.className = "reveal-line";
    // strip tags for words but keep gold spans: use a temp element
    const temp = document.createElement("span");
    temp.innerHTML = line;
    Array.from(temp.childNodes).forEach((node) => {
      if (node.nodeType === Node.TEXT_NODE) {
        node.textContent.split(/(\s+)/).forEach((wtext) => {
          if (/^\s+$/.test(wtext)) { lineWrap.appendChild(document.createTextNode(" ")); return; }
          if (!wtext) return;
          const w = document.createElement("span"); w.className = "reveal-word";
          const s = document.createElement("span"); s.textContent = wtext;
          w.appendChild(s); lineWrap.appendChild(w); allSpans.push(s);
        });
      } else {
        const w = document.createElement("span"); w.className = "reveal-word";
        node.style.display = "inline-block";
        w.appendChild(node.cloneNode(true));
        lineWrap.appendChild(w); allSpans.push(w.firstChild);
      }
    });
    el.appendChild(lineWrap);
    if (li < lines.length - 1) el.appendChild(document.createElement("br"));
  });
  return allSpans;
}

/* -------------------------------------------------------------
   COUNTERS
------------------------------------------------------------- */
function initCounters() {
  document.querySelectorAll("[data-count]").forEach((el) => {
    const target = parseFloat(el.getAttribute("data-count"));
    const suffix = el.getAttribute("data-suffix") || "";
    const isYear = target > 999;
    const obj = { v: isYear ? target - 46 : 0 };
    ScrollTrigger.create({
      trigger: el, start: "top 90%", once: true,
      onEnter: () => gsap.to(obj, {
        v: target, duration: 2, ease: "power2.out",
        onUpdate: () => { el.textContent = Math.round(obj.v) + suffix; },
      }),
    });
  });
}

/* -------------------------------------------------------------
   PARALLAX IMAGES & FLOATS
------------------------------------------------------------- */
function initParallax() {
  if (prefersReduced) return;
  document.querySelectorAll("[data-parallax-img] img").forEach((img) => {
    gsap.to(img, { yPercent: -10, ease: "none",
      scrollTrigger: { trigger: img.closest("[data-parallax-img]"), start: "top bottom", end: "bottom top", scrub: true } });
  });
  document.querySelectorAll("[data-panel-img] img").forEach((img) => {
    gsap.fromTo(img, { scale: 1.2 }, { scale: 1.05, ease: "none",
      scrollTrigger: { trigger: img.closest(".panel"), start: "left right", end: "right left",
        containerAnimation: window.__projectsScroll, scrub: true } });
  });
  document.querySelectorAll("[data-float]").forEach((el) => {
    gsap.to(el, { y: -26, ease: "none",
      scrollTrigger: { trigger: el, start: "top bottom", end: "bottom top", scrub: true } });
  });
  const vbg = document.querySelector("[data-vision-bg] img");
  if (vbg) gsap.to(vbg, { yPercent: -14, ease: "none",
    scrollTrigger: { trigger: ".vision", start: "top bottom", end: "bottom top", scrub: true } });
}

/* -------------------------------------------------------------
   3D TILT + SPOTLIGHT (cards, glass)
------------------------------------------------------------- */
function initTilt() {
  if (isTouch) return;
  document.querySelectorAll("[data-tilt]").forEach((el) => {
    const max = el.hasAttribute("data-glass") ? 6 : 9;
    el.addEventListener("mousemove", (e) => {
      const r = el.getBoundingClientRect();
      const px = (e.clientX - r.left) / r.width;
      const py = (e.clientY - r.top) / r.height;
      gsap.to(el, {
        rotateY: (px - 0.5) * max * 2,
        rotateX: -(py - 0.5) * max * 2,
        duration: 0.5, ease: "power2.out", transformPerspective: 900, transformOrigin: "center",
      });
      if (el.hasAttribute("data-spotlight") || el.hasAttribute("data-glass")) {
        el.style.setProperty("--mx", px * 100 + "%");
        el.style.setProperty("--my", py * 100 + "%");
        const glow = el.querySelector(".card__glow");
        if (glow) glow.style.setProperty("--mx", px * 100 + "%") , glow.style.setProperty("--my", py * 100 + "%");
      }
    });
    el.addEventListener("mouseleave", () => {
      gsap.to(el, { rotateY: 0, rotateX: 0, duration: 0.8, ease: "elastic.out(1,0.5)" });
    });
  });
  // glass top-line (::before) + bottom-line reveal
  document.querySelectorAll("[data-glass]").forEach((el) => {
    ScrollTrigger.create({ trigger: el, start: "top 82%", onEnter: () => el.classList.add("in") });
  });
  document.querySelectorAll("[data-glass-line]").forEach((line) => {
    ScrollTrigger.create({ trigger: line.closest("[data-glass]"), start: "top 80%",
      onEnter: () => gsap.to(line, { scaleX: 1, duration: 1.2, ease: "power3.out", delay: 0.3 }) });
  });
}

/* -------------------------------------------------------------
   LEGACY TIMELINE progress + steps
------------------------------------------------------------- */
function initLegacy() {
  const spine = document.querySelector("[data-spine]");
  const steps = document.querySelectorAll("[data-step]");
  if (spine) {
    ScrollTrigger.create({
      trigger: "[data-timeline]", start: "top 70%", end: "bottom 70%",
      onUpdate: (self) => spine.style.setProperty("--progress", (self.progress * 100).toFixed(1) + "%"),
    });
  }
  steps.forEach((step) => {
    gsap.set(step, { opacity: 0, x: -30 });
    ScrollTrigger.create({
      trigger: step, start: "top 82%",
      onEnter: () => { step.classList.add("in"); gsap.to(step, { opacity: 1, x: 0, duration: 0.9, ease: "power3.out" }); },
    });
  });
}

/* -------------------------------------------------------------
   HORIZONTAL PROJECTS
------------------------------------------------------------- */
function initProjects() {
  const track = document.getElementById("projectsTrack");
  const pin = document.getElementById("projectsPin");
  const panels = track ? track.querySelectorAll(".panel") : [];
  if (!track || panels.length < 2 || isTouch) return;

  const getScrollAmount = () => -(track.scrollWidth - window.innerWidth);

  const tween = gsap.to(track, {
    x: getScrollAmount, ease: "none",
    scrollTrigger: {
      trigger: ".projects__pin",
      start: "top top",
      end: () => "+=" + (track.scrollWidth - window.innerWidth),
      pin: true, scrub: 1, invalidateOnRefresh: true,
      onUpdate: (self) => {
        const p = self.progress;
        const bar = document.getElementById("projBar");
        const cur = document.getElementById("projCur");
        if (bar) bar.style.setProperty("--p", (p * 100).toFixed(1) + "%");
        if (cur) cur.textContent = String(Math.min(panels.length, Math.floor(p * panels.length) + 1)).padStart(2, "0");
      },
    },
  });
  window.__projectsScroll = tween;

  // parallax within panels tied to container animation
  panels.forEach((panel) => {
    const img = panel.querySelector("[data-panel-img] img");
    const body = panel.querySelector(".panel__body");
    if (img) gsap.fromTo(img, { xPercent: -6 }, { xPercent: 6, ease: "none",
      scrollTrigger: { trigger: panel, containerAnimation: tween, start: "left right", end: "right left", scrub: true } });
    if (body) gsap.fromTo(body, { opacity: 0.2, y: 40 }, { opacity: 1, y: 0, ease: "power2.out",
      scrollTrigger: { trigger: panel, containerAnimation: tween, start: "left center", end: "center center", scrub: true } });
  });
}

/* -------------------------------------------------------------
   MARQUEE + RENDERS drift
------------------------------------------------------------- */
function initMarquee() {
  const m = document.getElementById("marquee1");
  if (m) {
    // duplicate content already ~2x; animate half
    gsap.to(m, { xPercent: -50, ease: "none", duration: 24, repeat: -1 });
  }
  const rt = document.getElementById("rendersTrack");
  if (rt && !isTouch) {
    gsap.to(rt, { x: () => -(rt.scrollWidth - window.innerWidth) * 0.5, ease: "none",
      scrollTrigger: { trigger: ".renders", start: "top bottom", end: "bottom top", scrub: 1 } });
  }
}

/* -------------------------------------------------------------
   NAV: solid on scroll + mobile menu + active link
------------------------------------------------------------- */
function initNav() {
  const nav = document.getElementById("nav");
  ScrollTrigger.create({
    start: "top -80",
    onUpdate: (self) => {
      if (window.scrollY > 80) nav.classList.add("solid");
      else nav.classList.remove("solid");
    },
  });
  window.addEventListener("scroll", () => {
    if (window.scrollY > 80) nav.classList.add("solid"); else nav.classList.remove("solid");
  }, { passive: true });

  const toggle = document.getElementById("navToggle");
  const mobile = document.getElementById("navMobile");
  toggle && toggle.addEventListener("click", () => {
    document.body.classList.toggle("menu-open");
    if (lenis) document.body.classList.contains("menu-open") ? lenis.stop() : lenis.start();
  });
  mobile && mobile.querySelectorAll("a").forEach((a) => a.addEventListener("click", () => {
    document.body.classList.remove("menu-open");
    if (lenis) lenis.start();
  }));

  // Smooth anchor scrolling via Lenis
  document.querySelectorAll('a[href^="#"]').forEach((a) => {
    a.addEventListener("click", (e) => {
      const id = a.getAttribute("href");
      if (id.length < 2) return;
      const target = document.querySelector(id);
      if (!target) return;
      e.preventDefault();
      if (lenis) lenis.scrollTo(target, { offset: 0, duration: 1.4 });
      else target.scrollIntoView({ behavior: "smooth" });
    });
  });
}

/* -------------------------------------------------------------
   FOOTER glow follows cursor
------------------------------------------------------------- */
function initFooterGlow() {
  const footer = document.querySelector("[data-footer]");
  const glow = document.querySelector("[data-footer-glow]");
  const sep = document.querySelector("[data-footer-sep]");
  if (footer && glow) {
    footer.addEventListener("mousemove", (e) => {
      const r = footer.getBoundingClientRect();
      glow.style.setProperty("--mx", e.clientX - r.left + "px");
      glow.style.setProperty("--my", e.clientY - r.top + "px");
    });
  }
  if (sep) ScrollTrigger.create({ trigger: sep, start: "top 92%",
    onEnter: () => gsap.to(sep, { scaleX: 1, duration: 1.4, ease: "power3.out" }) });
}

/* -------------------------------------------------------------
   HERO PARTICLES (lightweight canvas)
------------------------------------------------------------- */
function initParticles() {
  const canvas = document.getElementById("heroParticles");
  if (!canvas || prefersReduced) return;
  const ctx = canvas.getContext("2d");
  let w, h, particles, raf;
  const DPR = Math.min(window.devicePixelRatio || 1, 2);
  const COUNT = isTouch ? 26 : 60;
  let mouse = { x: -9999, y: -9999 };

  function resize() {
    w = canvas.clientWidth; h = canvas.clientHeight;
    canvas.width = w * DPR; canvas.height = h * DPR;
    ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
  }
  function make() {
    particles = Array.from({ length: COUNT }, () => ({
      x: Math.random() * w, y: Math.random() * h,
      r: Math.random() * 1.6 + 0.3,
      vx: (Math.random() - 0.5) * 0.25, vy: (Math.random() - 0.5) * 0.25 - 0.08,
      a: Math.random() * 0.5 + 0.1,
    }));
  }
  function tick() {
    ctx.clearRect(0, 0, w, h);
    particles.forEach((p) => {
      p.x += p.vx; p.y += p.vy;
      const dx = p.x - mouse.x, dy = p.y - mouse.y;
      const dist = Math.hypot(dx, dy);
      if (dist < 120) { p.x += (dx / dist) * 0.6; p.y += (dy / dist) * 0.6; }
      if (p.x < 0) p.x = w; if (p.x > w) p.x = 0;
      if (p.y < 0) p.y = h; if (p.y > h) p.y = 0;
      ctx.beginPath();
      ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
      ctx.fillStyle = `rgba(212,175,55,${p.a})`;
      ctx.fill();
    });
    raf = requestAnimationFrame(tick);
  }
  const hero = document.querySelector(".hero");
  hero.addEventListener("mousemove", (e) => {
    const r = canvas.getBoundingClientRect();
    mouse.x = e.clientX - r.left; mouse.y = e.clientY - r.top;
  });
  hero.addEventListener("mouseleave", () => { mouse.x = mouse.y = -9999; });
  window.addEventListener("resize", () => { resize(); make(); });
  resize(); make(); tick();

  // pause when hero out of view
  ScrollTrigger.create({ trigger: ".hero", start: "bottom top",
    onEnter: () => cancelAnimationFrame(raf),
    onLeaveBack: () => { cancelAnimationFrame(raf); tick(); } });
}

/* -------------------------------------------------------------
   BOOT
------------------------------------------------------------- */
function boot() {
  document.getElementById("year").textContent = new Date().getFullYear();
  initLenis();
  initCursor();
  initMagnetic();
  initRipple();
  initTilt();
  initReveals();
  initCounters();
  initLegacy();
  initProjects();
  initParallax();
  initMarquee();
  initNav();
  initFooterGlow();
  initParticles();
  window.__booted = true;
  ScrollTrigger.refresh();

  // Refresh after fonts/images settle
  window.addEventListener("load", () => ScrollTrigger.refresh());
  document.fonts && document.fonts.ready.then(() => ScrollTrigger.refresh());
}

runPreloader(() => {
  initHero();
  boot();
});

// Safety: if preloader assets stall, boot anyway
setTimeout(() => {
  if (!window.__booted) {
    const pre = document.getElementById("preloader");
    if (pre && pre.style.display !== "none") {
      pre.style.display = "none";
      const c = document.getElementById("curtain"); if (c) c.style.display = "none";
      document.documentElement.style.overflow = "";
      initHero(); boot();
    }
  }
}, 6000);
window.__booted = false;
