// Section components for WaitWhatWeb
const { useState, useEffect, useRef, useMemo } = React;

// ---------- Reveal helper ----------
function Reveal({ children, mask = false, delay = 0, className = "", as: As = "div", ...rest }) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            setSeen(true);
            io.disconnect();
          }
        });
      },
      { threshold: 0.12, rootMargin: "0px 0px -10% 0px" }
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);
  const cls = (mask ? "reveal-mask " : "reveal ") + (seen ? "in " : "") + className;
  return (
    <As ref={ref} className={cls} style={delay ? { transitionDelay: `${delay}ms` } : undefined} {...rest}>
      {children}
    </As>);

}

// ---------- Magnetic button ----------
function MagneticButton({ children, className = "", strength = 0.35, ...rest }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const x = e.clientX - (r.left + r.width / 2);
      const y = e.clientY - (r.top + r.height / 2);
      el.style.transform = `translate(${x * strength}px, ${y * strength}px)`;
    };
    const onLeave = () => {el.style.transform = "translate(0,0)";};
    el.addEventListener("mousemove", onMove);
    el.addEventListener("mouseleave", onLeave);
    return () => {
      el.removeEventListener("mousemove", onMove);
      el.removeEventListener("mouseleave", onLeave);
    };
  }, [strength]);
  return (
    <button ref={ref} className={`btn ${className}`} data-cursor="hover" {...rest}>
      {children}
    </button>);

}

const Arrow = ({ size = 16 }) =>
<svg className="arrow" width={size} height={size} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5">
    <path d="M3 13 L13 3 M6 3 L13 3 L13 10" strokeLinecap="square" />
  </svg>;


// ---------- Smooth scroll helper ----------
function smoothScrollTo(id) {
  const el = document.getElementById(id);
  if (!el) return;
  const top = el.getBoundingClientRect().top + window.pageYOffset - 72;
  window.scrollTo({ top, behavior: "smooth" });
}

function handleHashClick(e) {
  const a = e.target.closest('a[href^="#"]');
  if (!a) return;
  const href = a.getAttribute("href");
  if (!href || href === "#" || href.length < 2) return;
  const id = href.slice(1);
  if (!document.getElementById(id)) return;
  e.preventDefault();
  smoothScrollTo(id);
}

// ---------- Nav ----------
function Nav() {
  const [open, setOpen] = useState(false);
  const onIndex = typeof window !== "undefined" && (
  window.location.pathname.endsWith("/index.html") ||
  window.location.pathname.endsWith("/WaitWhatWeb.html") ||
  window.location.pathname === "/" ||
  window.location.pathname.endsWith("/"));
  useEffect(() => {
    document.addEventListener("click", handleHashClick);
    return () => document.removeEventListener("click", handleHashClick);
  }, []);
  const link = (hash) => onIndex ? hash : `index.html${hash}`;

  return (
    <nav className="nav-v2">
      <a href={link("#about")} className="nav-side nav-side-l" data-cursor="hover">About</a>

      <div className="nav-pill" data-cursor="hover">
        <span className="nav-pill-gloss" aria-hidden="true"></span>
        <button
          className="nav-burger"
          aria-label="Menu"
          onClick={() => setOpen((v) => !v)}>
          <span></span>
          <span></span>
          <span></span>
        </button>
        <a href={onIndex ? "#top" : "index.html"} className="nav-mark">
          <img src="assets/logo.png" alt="WaitWhatWeb" />
        </a>
        <span className="nav-dot" aria-hidden="true"></span>
      </div>

      <a href={link("#services")} className="nav-side nav-side-r" data-cursor="hover">Services</a>

      <div className={`nav-sheet ${open ? "open" : ""}`} aria-hidden={!open}>
        <div className="nav-sheet-backdrop" onClick={() => setOpen(false)}></div>
        <div className="nav-sheet-panel">
          <div className="nav-sheet-handle" aria-hidden="true"></div>
          <div className="nav-sheet-grid">
            <a className="nav-sheet-link" href={link("#clients")} onClick={() => setOpen(false)}>Clients<span className="i">01</span></a>
            <a className="nav-sheet-link" href={link("#about")} onClick={() => setOpen(false)}>About<span className="i">02</span></a>
            <a className="nav-sheet-link" href={link("#services")} onClick={() => setOpen(false)}>Services<span className="i">03</span></a>
            <a className="nav-sheet-link" href={link("#faq")} onClick={() => setOpen(false)}>FAQ<span className="i">07</span></a>
            <a className="nav-sheet-link nav-sheet-cta" href={link("#contact")} onClick={() => setOpen(false)}>Start a Project<span className="i">→</span></a>
          </div>
          <div className="nav-sheet-foot">
            <span>Bandung · ID</span>
            <span>hello@waitwhatweb.co.id</span>
          </div>
        </div>
      </div>
    </nav>);

}

// ---------- Hero animated background ----------
function HeroBackground() {
  const canvasRef = useRef(null);
  useEffect(() => {
    const c = canvasRef.current;
    if (!c) return;
    const ctx = c.getContext("2d");
    let raf, w, h, dpr;
    const resize = () => {
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      w = c.clientWidth;h = c.clientHeight;
      c.width = w * dpr;c.height = h * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    window.addEventListener("resize", resize);

    // Gentle radial blob field
    const blobs = Array.from({ length: 5 }).map((_, i) => ({
      x: Math.random(), y: Math.random(),
      r: 0.32 + Math.random() * 0.28,
      vx: (Math.random() - 0.5) * 0.00008,
      vy: (Math.random() - 0.5) * 0.00008,
      hue: i % 2 === 0 ? "lime" : "white"
    }));

    const draw = (t) => {
      ctx.clearRect(0, 0, w, h);
      // Base subtle wash
      const g = ctx.createLinearGradient(0, 0, 0, h);
      g.addColorStop(0, "rgba(20,20,20,1)");
      g.addColorStop(1, "rgba(10,10,10,1)");
      ctx.fillStyle = g;
      ctx.fillRect(0, 0, w, h);

      blobs.forEach((b, i) => {
        b.x += b.vx * 16;b.y += b.vy * 16;
        b.x += Math.sin(t * 0.0002 + i) * 0.0006;
        b.y += Math.cos(t * 0.00018 + i * 1.4) * 0.0006;
        if (b.x < 0 || b.x > 1) b.vx *= -1;
        if (b.y < 0 || b.y > 1) b.vy *= -1;
        const cx = b.x * w,cy = b.y * h;
        const rad = b.r * Math.max(w, h);
        const grd = ctx.createRadialGradient(cx, cy, 0, cx, cy, rad);
        if (b.hue === "lime") {
          grd.addColorStop(0, "rgba(200,255,61,0.22)");
          grd.addColorStop(0.4, "rgba(200,255,61,0.05)");
          grd.addColorStop(1, "rgba(200,255,61,0)");
        } else {
          grd.addColorStop(0, "rgba(255,255,255,0.10)");
          grd.addColorStop(0.5, "rgba(255,255,255,0.02)");
          grd.addColorStop(1, "rgba(255,255,255,0)");
        }
        ctx.globalCompositeOperation = "lighter";
        ctx.fillStyle = grd;
        ctx.fillRect(0, 0, w, h);
      });
      ctx.globalCompositeOperation = "source-over";

      // Thin scanning line
      const ly = (Math.sin(t * 0.0003) + 1) / 2 * h;
      const ling = ctx.createLinearGradient(0, ly - 1, 0, ly + 1);
      ling.addColorStop(0, "rgba(200,255,61,0)");
      ling.addColorStop(0.5, "rgba(200,255,61,0.18)");
      ling.addColorStop(1, "rgba(200,255,61,0)");
      ctx.fillStyle = ling;
      ctx.fillRect(0, ly - 1, w, 2);

      raf = requestAnimationFrame(draw);
    };
    raf = requestAnimationFrame(draw);
    return () => {cancelAnimationFrame(raf);window.removeEventListener("resize", resize);};
  }, []);
  return (
    <div className="hero-bg">
      <canvas ref={canvasRef} className="hero-canvas" />
      <div className="hero-noise" />
      <div className="hero-vignette" />
    </div>);

}

// ---------- Pixelated tagline (per-character glitch reveal, loops every 15s) ----------
function PixelTagline({ lines, interval = 15000 }) {
  const ref = useRef(null);
  const [done, setDone] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const chars = el.querySelectorAll("[data-c]");
    const GLYPHS = "!@#$%&*?<>/\\=+-01ABCDEFGHJKLMNPQRSTVWXYZ";
    let raf, loopTimer, started = false, visible = false;
    const stagger = 22;
    const localDur = 380;

    const runOnce = () => {
      cancelAnimationFrame(raf);
      setDone(false);
      chars.forEach((c) => c.classList.remove("settled"));
      const start = performance.now();
      const tick = (t) => {
        const elapsed = t - start;
        let allDone = true;
        chars.forEach((c, i) => {
          const target = c.dataset.c;
          const localStart = i * stagger;
          if (elapsed < localStart) { c.textContent = ""; allDone = false; return; }
          const localT = elapsed - localStart;
          if (localT >= localDur) {
            c.textContent = target === " " ? "\u00A0" : target;
            c.classList.add("settled");
          } else {
            allDone = false;
            if (target === " ") { c.textContent = "\u00A0"; return; }
            c.textContent = GLYPHS[Math.floor(Math.random() * GLYPHS.length)];
          }
        });
        if (!allDone) {
          raf = requestAnimationFrame(tick);
        } else {
          setDone(true);
        }
      };
      raf = requestAnimationFrame(tick);
    };

    const scheduleLoop = () => {
      clearInterval(loopTimer);
      loopTimer = setInterval(() => { if (visible) runOnce(); }, interval);
    };

    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        visible = e.isIntersecting;
        if (e.isIntersecting && !started) {
          started = true;
          runOnce();
          scheduleLoop();
        }
      });
    }, { threshold: 0.3 });
    io.observe(el);
    return () => { cancelAnimationFrame(raf); clearInterval(loopTimer); io.disconnect(); };
  }, [interval]);
  return (
    <h1 ref={ref} className={`hero-pixel-tag ${done ? "done" : ""}`}>
      {lines.map((line, li) => (
        <div className="hero-pixel-line" key={li}>
          {line.split("").map((ch, i) => (
            <span data-c={ch} key={`${li}-${i}`}>{ch === " " ? "\u00A0" : ""}</span>
          ))}
        </div>
      ))}
    </h1>
  );
}

// ---------- WAITWHAT as fluid glass blobs (SVG lighting + displacement) ----------
function GooWordmark() {
  return (
    <div className="goo-wrap fg-wrap">
      <svg width="0" height="0" style={{ position: "absolute" }} aria-hidden="true">
        <defs>
          {/* Liquid glass blob filter — blurs text into a height map,
              adds turbulence wetness, then renders with specular + diffuse lighting */}
          <filter id="fg-glass-blob" x="-25%" y="-25%" width="150%" height="150%" colorInterpolationFilters="sRGB">
            {/* 1. Height map from text alpha (blurry blob silhouette) */}
            <feGaussianBlur in="SourceAlpha" stdDeviation="10" result="blur" />

            {/* 2. Animated noise to make the surface lumpy / wet */}
            <feTurbulence type="fractalNoise" baseFrequency="0.014" numOctaves="2" seed="4" result="turb">
              <animate attributeName="baseFrequency" dur="22s"
                values="0.012; 0.020; 0.010; 0.014" repeatCount="indefinite" />
              <animate attributeName="seed" dur="18s" values="4; 19; 4" repeatCount="indefinite" />
            </feTurbulence>

            {/* 3. Use turbulence to wobble the height map */}
            <feDisplacementMap in="blur" in2="turb" xChannelSelector="R" yChannelSelector="G" result="wet">
              <animate attributeName="scale" dur="9s" values="14; 26; 14" repeatCount="indefinite" />
            </feDisplacementMap>

            {/* 4. Sharp white specular highlights (rim lighting from top-left) */}
            <feSpecularLighting in="wet" surfaceScale="9" specularConstant="2.0" specularExponent="55"
              lightingColor="#ffffff" result="spec">
              <feDistantLight azimuth="125" elevation="62" />
            </feSpecularLighting>

            {/* 5. Lime/accent specular from the opposite side (color hint) */}
            <feSpecularLighting in="wet" surfaceScale="6" specularConstant="1.2" specularExponent="35"
              lightingColor="#C8FF3D" result="spec2">
              <feDistantLight azimuth="305" elevation="42" />
            </feSpecularLighting>

            {/* 6. Soft diffuse body (mid grey so the glass looks dimensional, not flat) */}
            <feDiffuseLighting in="wet" surfaceScale="3" diffuseConstant="1.0"
              lightingColor="#3a3a3a" result="diff">
              <feDistantLight azimuth="125" elevation="50" />
            </feDiffuseLighting>

            {/* 7. Mask all layers to the wet blob silhouette so they stay inside the glass */}
            <feComposite in="diff" in2="wet" operator="in" result="diffM" />
            <feComposite in="spec" in2="wet" operator="in" result="specM" />
            <feComposite in="spec2" in2="wet" operator="in" result="spec2M" />

            {/* 8. Layer: diffuse body → accent specular → bright specular on top */}
            <feMerge>
              <feMergeNode in="diffM" />
              <feMergeNode in="spec2M" />
              <feMergeNode in="specM" />
            </feMerge>
          </filter>
        </defs>
      </svg>

      <div className="fg-stage fg-liquid-stage">
        <div className="fg-liquid-text">
          <span className="goo-line goo-line-desktop">WAITWHAT</span>
          <span className="goo-line goo-line-mobile">WAIT</span>
          <span className="goo-line goo-line-mobile">WHAT</span>
        </div>
      </div>
    </div>
  );
}



// ---------- Hero interactive blobs background (cursor-magnetic, wobbly) ----------
function HeroBlobsBg() {
  const ref = useRef(null);
  useEffect(() => {
    const root = ref.current;
    if (!root) return;
    const stage = root.querySelector(".hero-blobs-stage");
    const blobs = Array.from(root.querySelectorAll(".hero-blob"));
    const ripples = root.querySelector(".hero-ripples");
    const W = () => stage.clientWidth;
    const H = () => stage.clientHeight;
    const state = blobs.map((el) => ({
      el,
      baseR: parseFloat(el.dataset.r),
      x: parseFloat(el.dataset.x) * W(),
      y: parseFloat(el.dataset.y) * H(),
      vx: (Math.random() - 0.5) * 0.18,
      vy: (Math.random() - 0.5) * 0.18,
      seed: Math.random() * Math.PI * 2,
    }));
    let mx = -9999, my = -9999, raf;
    const onMove = (e) => {
      const r = stage.getBoundingClientRect();
      mx = e.clientX - r.left; my = e.clientY - r.top;
    };
    const onLeave = () => { mx = -9999; my = -9999; };
    const onClick = (e) => {
      const r = stage.getBoundingClientRect();
      const x = e.clientX - r.left, y = e.clientY - r.top;
      const dot = document.createElement("span");
      dot.className = "hero-ripple";
      dot.style.left = x + "px";
      dot.style.top = y + "px";
      ripples.appendChild(dot);
      setTimeout(() => dot.remove(), 1100);
    };
    stage.addEventListener("mousemove", onMove);
    stage.addEventListener("mouseleave", onLeave);
    stage.addEventListener("click", onClick);
    const tick = (t) => {
      const w = W(), h = H();
      state.forEach((s) => {
        s.x += s.vx; s.y += s.vy;
        const breathe = Math.sin(t * 0.0006 + s.seed) * 0.10 + 1;
        let pullX = 0, pullY = 0, sx = 1, sy = 1;
        if (mx > -1000) {
          const dx = mx - s.x, dy = my - s.y;
          const dist = Math.hypot(dx, dy);
          const reach = 320;
          if (dist < reach) {
            const k = 1 - dist / reach;
            const mag = k * k * 60;
            pullX = (dx / (dist || 1)) * mag;
            pullY = (dy / (dist || 1)) * mag;
            sx = 1 + k * 0.45;
            sy = 1 - k * 0.18;
            const ang = Math.atan2(dy, dx);
            s.el.style.setProperty("--rot", ang + "rad");
          } else {
            s.el.style.setProperty("--rot", "0rad");
          }
        }
        const m = -40;
        if (s.x < m) s.vx = Math.abs(s.vx);
        if (s.x > w - m) s.vx = -Math.abs(s.vx);
        if (s.y < m) s.vy = Math.abs(s.vy);
        if (s.y > h - m) s.vy = -Math.abs(s.vy);
        s.el.style.transform = `translate(${s.x + pullX}px, ${s.y + pullY}px) translate(-50%, -50%) rotate(var(--rot, 0rad)) scale(${sx * breathe}, ${sy * breathe})`;
      });
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    const onResize = () => {
      state.forEach((s, i) => {
        s.x = parseFloat(blobs[i].dataset.x) * W();
        s.y = parseFloat(blobs[i].dataset.y) * H();
      });
    };
    window.addEventListener("resize", onResize);
    return () => {
      cancelAnimationFrame(raf);
      stage.removeEventListener("mousemove", onMove);
      stage.removeEventListener("mouseleave", onLeave);
      stage.removeEventListener("click", onClick);
      window.removeEventListener("resize", onResize);
    };
  }, []);
  const BLOBS = [
    { x: 0.12, y: 0.28, r: 110, c: "lime" },
    { x: 0.82, y: 0.22, r: 90,  c: "white" },
    { x: 0.25, y: 0.78, r: 130, c: "white" },
    { x: 0.72, y: 0.72, r: 100, c: "lime" },
    { x: 0.50, y: 0.45, r: 70,  c: "white" },
    { x: 0.92, y: 0.55, r: 80,  c: "lime" },
  ];
  return (
    <div ref={ref} className="hero-bg-blobs" aria-hidden="true">
      <svg width="0" height="0" style={{ position: "absolute" }}>
        <defs>
          <filter id="hero-goo">
            <feGaussianBlur in="SourceGraphic" stdDeviation="22" result="b" />
            <feColorMatrix in="b" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 18 -7" />
          </filter>
        </defs>
      </svg>
      <div className="hero-blobs-stage">
        <div className="hero-blobs-grid"></div>
        <div className="hero-blobs-goo">
          {BLOBS.map((b, i) => (
            <span
              key={i}
              className={`hero-blob hero-blob-${b.c}`}
              data-x={b.x} data-y={b.y} data-r={b.r}
              style={{ width: b.r * 2, height: b.r * 2 }}
            />
          ))}
        </div>
        <div className="hero-ripples"></div>
        <div className="hero-blobs-noise"></div>
      </div>
    </div>
  );
}

// ---------- Hero ----------
function Hero() {
  return (
    <header className="hero-v2" id="top">
      <HeroBlobsBg />

      <PixelTagline lines={[
        "WE BUILD DIGITAL MACHINES",
        "THAT SELL WHILE YOU SLEEP."
      ]} />

      <GooWordmark />

      <a href="#clients" className="hero-down" aria-label="Scroll down" data-cursor="hover">
        <svg width="28" height="28" viewBox="0 0 28 28" fill="none" stroke="currentColor" strokeWidth="1.6">
          <path d="M6 11 L14 19 L22 11" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
      </a>
    </header>
  );
}

// ---------- Marquee ----------
function Marquee({ items }) {
  const content = [...items, ...items, ...items];
  return (
    <div className="marquee" aria-hidden="true">
      <div className="marquee-track">
        {content.map((it, i) =>
        <span className="marquee-item" key={i}>
            <span>{it}</span>
            <span className="star">✶</span>
          </span>
        )}
      </div>
    </div>);

}

// ---------- Trusted Clients (4-row infinite carousel) ----------
const CLIENTS = [
"zoya", "wotbatu", "wingman", "wakaigo", "vearst", "vanwalk",
"trendtech", "trademarkmarket", "tiento", "thesigit", "supergrain",
"sneakypair", "season", "sairanisar", "rubylicious", "polyesterembassy",
"nokha", "nadjani", "millersrecords", "meramu", "meandlia",
"mantaliberta", "makmurjayacoffee", "lozy", "lippielust", "lazysusan",
"kukeystudio", "kitc", "kamargadget", "junkard", "jagoankacamata",
"ikatindonesia", "hijacksandals", "heykama", "grimloc", "graaaund",
"geoffmax", "escort", "erigo", "elitearena", "dripsndrops",
"crashbaggage", "cartenz", "capslock", "buttonscarves", "brasov",
"bigmo", "beatboys", "bcstudio", "arei", "amble",
"alanbiyaa", "akross", "adhi", "1489", "910"];


function shuffle(arr, seed) {
  const a = [...arr];
  let s = seed;
  for (let i = a.length - 1; i > 0; i--) {
    s = (s * 9301 + 49297) % 233280;
    const j = Math.floor(s / 233280 * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}

function ClientsRow({ logos, duration = 60, reverse = false }) {
  // Triple the items so the loop crosses one set width seamlessly
  const items = [...logos, ...logos, ...logos];
  return (
    <div className={`tc-row ${reverse ? "tc-row-rev" : ""}`}>
      <div className="tc-track" style={{ animationDuration: `${duration}s` }}>
        {items.map((slug, i) =>
        <div className="tc-cell" key={`${slug}-${i}`} style={{ padding: "2px" }}>
            <img src={`clients/${slug}.png`} alt={slug} loading="lazy" style={{ width: "225px", height: "150px" }} />
          </div>
        )}
      </div>
    </div>);

}

function TrustedClients() {
  const rows = useMemo(() => {
    // Disjoint partition: shuffle once, split into 4 non-overlapping chunks
    const shuffled = shuffle(CLIENTS, 11);
    const per = Math.ceil(shuffled.length / 4);
    return [
      shuffled.slice(0 * per, 1 * per),
      shuffled.slice(1 * per, 2 * per),
      shuffled.slice(2 * per, 3 * per),
      shuffled.slice(3 * per, 4 * per),
    ];
  }, []);
  return (
    <section className="section tc-section" id="clients">
      <div className="shell">
        <div className="section-hd">
          <Reveal><span className="section-eyebrow">Trusted by / 01</span></Reveal>
          <Reveal mask>
            <h2 className="section-title display">
              Brands we've helped<br />step beyond marketplaces.
            </h2>
          </Reveal>
          <Reveal delay={200}>
            <p className="tc-sub">
              From boutique labels to household names — independent brands building direct relationships with their customers, on their own terms.
            </p>
          </Reveal>
        </div>
      </div>

      <div className="tc-rows">
        <ClientsRow logos={rows[0]} duration={70} />
        <ClientsRow logos={rows[1]} duration={85} reverse />
        <ClientsRow logos={rows[2]} duration={60} />
        <ClientsRow logos={rows[3]} duration={95} reverse />
      </div>

      <div className="shell">
        <Reveal delay={150}>
          <div className="tc-many display">— and many others.</div>
        </Reveal>
        <Reveal delay={300}>
          <div className="tc-foot">
            <span className="mono">— 100++ brands · est. 2016 · Bandung, ID</span>
            <a href="#contact" className="tc-foot-link" data-cursor="hover">
              Be the next one <Arrow size={14} />
            </a>
          </div>
        </Reveal>
      </div>
    </section>);

}

// ---------- About ----------
function About() {
  return (
    <section className="section" id="about">
      <div className="shell">
        <div className="about-grid">
          <Reveal>
            <span className="about-eyebrow">— About / 02</span>
          </Reveal>
          <div>
            <Reveal mask>
              <h2 className="about-statement">
                We focus on your users.<br />
                By combining <em>technology</em> and <em>design</em>, we build simple, powerful systems that help your business grow.
              </h2>
            </Reveal>
            <Reveal delay={200}>
              <div className="about-pills">
                <span className="pill">Customer-first</span>
                <span className="pill">Engineered to scale</span>
                <span className="pill">No templates</span>
                <span className="pill">Performance over polish</span>
                <span className="pill">Built to last</span>
              </div>
            </Reveal>
          </div>
        </div>
      </div>
    </section>);

}

// ---------- Services ----------
const SERVICES = [
{ num: "01", name: "E-commerce Engines", desc: "Shopify & WooCommerce built to run fast and scale harder." },
{ num: "02", name: "Custom Systems", desc: "Dashboards and tools shaped by your business, not templates." },
{ num: "03", name: "Automation Flows", desc: "Registration, approval, and workflow systems that run themselves." },
{ num: "04", name: "IT Team on Demand", desc: "A dedicated tech squad to build, maintain, and push your systems further." },
{ num: "05", name: "Connected Experiences", desc: "Fast, integrated, and engineered for real-world usage." }];


function Services() {
  return (
    <section className="section" id="services">
      <div className="shell">
        <div className="section-hd">
          <Reveal><span className="section-eyebrow">Services / 03</span></Reveal>
          <Reveal mask><h2 className="section-title display">Built around what<br />you actually need.</h2></Reveal>
        </div>
        <div className="services-list">
          {SERVICES.map((s, i) =>
          <Reveal key={s.num} delay={i * 60}>
              <div className="service" data-cursor="hover">
                <span className="service-num mono">{s.num} /</span>
                <h3 className="service-name display">{s.name}</h3>
                <p className="service-desc">{s.desc}</p>
                <div className="service-arrow">
                  <svg width="16" height="16" viewBox="0 0 16 16" fill="none" strokeWidth="1.5">
                    <path d="M3 13 L13 3 M6 3 L13 3 L13 10" strokeLinecap="square" />
                  </svg>
                </div>
              </div>
            </Reveal>
          )}
        </div>
      </div>
    </section>);

}

// ---------- Principles ----------
const PRINCIPLES = [
{
  num: "P / 01",
  name: "Customer-first",
  desc: "Every decision starts and ends with your user.",
  icon:
  <svg viewBox="0 0 64 64" fill="none" stroke="currentColor" strokeWidth="1.5">
        <circle cx="32" cy="32" r="20" />
        <circle cx="32" cy="32" r="6" fill="currentColor" />
      </svg>

},
{
  num: "P / 02",
  name: "Simplicity",
  desc: "Strip everything that doesn't serve the outcome.",
  icon:
  <svg viewBox="0 0 64 64" fill="none" stroke="currentColor" strokeWidth="1.5">
        <path d="M12 22 H52 M12 32 H40 M12 42 H28" strokeLinecap="square" />
      </svg>

},
{
  num: "P / 03",
  name: "Performance",
  desc: "Speed isn't a feature. It's the foundation.",
  icon:
  <svg viewBox="0 0 64 64" fill="none" stroke="currentColor" strokeWidth="1.5">
        <path d="M32 12 L18 36 H32 L26 52 L46 28 H32 L38 12 Z" strokeLinejoin="miter" />
      </svg>

},
{
  num: "P / 04",
  name: "Custom-built",
  desc: "No templates. No shortcuts. Built for you, only.",
  icon:
  <svg viewBox="0 0 64 64" fill="none" stroke="currentColor" strokeWidth="1.5">
        <rect x="12" y="12" width="40" height="40" />
        <path d="M22 22 L42 42 M42 22 L22 42" />
      </svg>

}];


function Principles() {
  return (
    <section className="section" id="principles">
      <div className="shell">
        <div className="section-hd">
          <Reveal><span className="section-eyebrow">Principles / 04</span></Reveal>
          <Reveal mask><h2 className="section-title display">Four rules.<br />Zero exceptions.</h2></Reveal>
        </div>
        <div className="principles-grid">
          {PRINCIPLES.map((p, i) =>
          <Reveal key={p.num} delay={i * 80} className="">
              <div className="principle" data-cursor="hover">
                <span className="principle-num mono">{p.num}</span>
                <div className="principle-icon">{p.icon}</div>
                <div>
                  <h3 className="principle-name display">{p.name}</h3>
                  <p className="principle-desc">{p.desc}</p>
                </div>
              </div>
            </Reveal>
          )}
        </div>
      </div>
    </section>);

}

{/*

// ---------- Selected Work ----------
const WORK = [
{
  id: "halo",
  title: "Halo Capital",
  cat: "Brand · Web · System",
  year: "2025",
  span: "span-7",
  visual: ({ t }) =>
  <div style={{ position: "absolute", inset: 0, background: "linear-gradient(135deg, #1a1a1a, #0a0a0a)", display: "flex", alignItems: "center", justifyContent: "center" }}>
        <svg viewBox="0 0 600 450" width="100%" height="100%" preserveAspectRatio="xMidYMid slice">
          <defs>
            <radialGradient id="halo-g" cx="50%" cy="50%">
              <stop offset="0%" stopColor="#C8FF3D" stopOpacity="0.4" />
              <stop offset="100%" stopColor="#C8FF3D" stopOpacity="0" />
            </radialGradient>
          </defs>
          <circle cx="300" cy="225" r="140" fill="url(#halo-g)" />
          {[...Array(8)].map((_, i) =>
      <circle key={i} cx="300" cy="225" r={40 + i * 18} fill="none" stroke="#C8FF3D" strokeOpacity={0.18 - i * 0.018} strokeWidth="1" />
      )}
          <text x="40" y="420" fill="#8A8A8A" fontFamily="Geist Mono, monospace" fontSize="13" letterSpacing="2">HALO ⌐ CAPITAL</text>
          <text x="490" y="420" fill="#8A8A8A" fontFamily="Geist Mono, monospace" fontSize="13" letterSpacing="2">/2025</text>
        </svg>
      </div>

},
{
  id: "obverse",
  title: "Obverse Studio",
  cat: "UI/UX · Web",
  year: "2024",
  span: "span-5",
  visual: () =>
  <div style={{ position: "absolute", inset: 0, background: "#EDEDED", color: "#0a0a0a", display: "flex", alignItems: "flex-end", padding: "32px" }}>
        <div style={{ fontFamily: "Geist", fontSize: 64, fontWeight: 500, letterSpacing: "-0.04em", lineHeight: 0.9 }}>
          ob<br />ver<br />se.
        </div>
        <div style={{ position: "absolute", top: 24, right: 24, fontFamily: "Geist Mono, monospace", fontSize: 11, color: "#545454" }}>
          STUDIO / 2024
        </div>
        <svg style={{ position: "absolute", top: 24, left: 24 }} width="32" height="32" viewBox="0 0 32 32"><circle cx="16" cy="16" r="14" fill="none" stroke="#0a0a0a" strokeWidth="1.5" /><circle cx="16" cy="16" r="6" fill="#0a0a0a" /></svg>
      </div>

},
{
  id: "northbound",
  title: "Northbound",
  cat: "E-commerce · Brand",
  year: "2025",
  span: "span-5",
  visual: () =>
  <div style={{ position: "absolute", inset: 0, background: "#1a2519", display: "flex", alignItems: "center", justifyContent: "center" }}>
        <svg viewBox="0 0 600 450" width="100%" height="100%" preserveAspectRatio="xMidYMid slice">
          {[...Array(40)].map((_, i) =>
      <line key={i} x1={i * 16} y1={450} x2={i * 16 + 100} y2={0} stroke="#C8FF3D" strokeOpacity={0.06 + i % 5 * 0.02} strokeWidth="1" />
      )}
          <text x="40" y="60" fill="#EDEDED" fontFamily="Instrument Serif, serif" fontSize="48" fontStyle="italic">Northbound</text>
          <text x="40" y="420" fill="#8A8A8A" fontFamily="Geist Mono, monospace" fontSize="12">→ EXPEDITION GEAR</text>
        </svg>
      </div>

},
{
  id: "kernel",
  title: "Kernel OS",
  cat: "System · Tools",
  year: "2024",
  span: "span-7",
  visual: () =>
  <div style={{ position: "absolute", inset: 0, background: "#0f0f0f", color: "#EDEDED", padding: "28px", fontFamily: "Geist Mono, monospace", fontSize: 12, lineHeight: 1.6 }}>
        <div style={{ color: "#545454", marginBottom: 20 }}>// kernel.os — runtime</div>
        <div><span style={{ color: "#C8FF3D" }}>$</span> kernel boot --mode=production</div>
        <div style={{ color: "#8A8A8A" }}>✓ services online · 47ms</div>
        <div style={{ color: "#8A8A8A" }}>✓ workers ready · 12 active</div>
        <div style={{ color: "#8A8A8A" }}>✓ db connected · 2 replicas</div>
        <div style={{ marginTop: 20 }}><span style={{ color: "#C8FF3D" }}>$</span> kernel deploy halo --canary</div>
        <div style={{ color: "#8A8A8A" }}>→ shipping in 3.2s ...</div>
        <div style={{ position: "absolute", top: 28, right: 28, padding: "4px 10px", border: "1px solid #2a2a2a", borderRadius: 4, fontSize: 10, letterSpacing: 1, color: "#C8FF3D" }}>● LIVE</div>
        <div style={{ position: "absolute", bottom: 28, left: 28, fontSize: 32, fontFamily: "Geist", letterSpacing: "-0.03em", color: "#EDEDED", fontWeight: 500 }}>Kernel OS</div>
        <div style={{ position: "absolute", bottom: 28, right: 28, fontSize: 11, color: "#545454" }}>INTERNAL TOOLING /2024</div>
      </div>

},
{
  id: "fourpoint",
  title: "Fourpoint Health",
  cat: "Web · UI/UX",
  year: "2025",
  span: "span-12",
  visual: () =>
  <div style={{ position: "absolute", inset: 0, background: "linear-gradient(180deg, #f4f1ea 0%, #e8e3d8 100%)", color: "#0a0a0a", padding: "48px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <div>
          <div style={{ fontFamily: "Geist Mono, monospace", fontSize: 11, color: "#545454", marginBottom: 24 }}>FOURPOINT / HEALTHCARE PLATFORM</div>
          <div style={{ fontFamily: "Instrument Serif, serif", fontStyle: "italic", fontSize: 96, lineHeight: 0.9, letterSpacing: "-0.02em" }}>Care,<br />engineered.</div>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 12, width: "44%" }}>
          {[...Array(9)].map((_, i) =>
      <div key={i} style={{ aspectRatio: "1", background: i === 4 ? "#C8FF3D" : "rgba(10,10,10,0.06)", borderRadius: 4, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: "Geist Mono, monospace", fontSize: 11, color: i === 4 ? "#0a0a0a" : "#545454" }}>
              {String(i + 1).padStart(2, "0")}
            </div>
      )}
        </div>
      </div>

}];


function Work() {
  return (
    <section className="section" id="work">
      <div className="shell">
        <div className="section-hd">
          <Reveal><span className="section-eyebrow">Selected Work / 04</span></Reveal>
          <Reveal mask><h2 className="section-title display">Recent fieldwork.</h2></Reveal>
        </div>
        <div className="work-grid">
          {WORK.map((w, i) => {
            const V = w.visual;
            return (
              <Reveal key={w.id} delay={i * 80} className={`work-card ${w.span}`}>
                <div data-cursor="hover">
                  <div className="visual">
                    <div className="visual-inner"><V /></div>
                    <div className="visual-overlay">
                      <span className="view-btn">View Case <Arrow /></span>
                    </div>
                  </div>
                  <div className="work-meta">
                    <div>
                      <div className="work-title display">{w.title}</div>
                      <div className="work-cat">{w.cat}</div>
                    </div>
                    <div className="work-year">/ {w.year}</div>
                  </div>
                </div>
              </Reveal>);

          })}
        </div>
      </div>
    </section>);

}

*/}

// ---------- Team (6 portraits, 3D tilt + reveal) ----------
const TEAM = [
  { slug: "ray",    name: "Ray",     role: "Director", 				years: "Est. BC", 	tag: "RAY-01" },
  { slug: "vina", 	name: "Vina",  	 role: "Finance Manager",       years: "8 yrs",     tag: "VIN-02" },
  { slug: "rizky",	name: "Rizky",   role: "Project Manager",     	years: "6 yrs",     tag: "RZK-03" },
  { slug: "yasyfi", name: "Yasyfi",  role: "Web Developer",         years: "3 yrs",     tag: "YSF-04" },
  { slug: "awan", 	name: "Awan",  	 role: "Web Developer",     	years: "3 yrs",     tag: "AWN-05" },
  { slug: "lukman", name: "Lukman",  role: "Account Executive",     years: "2 yrs",     tag: "LKM-06" },
];

function TeamCard({ p, i }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const x = (e.clientX - r.left) / r.width - 0.5;
      const y = (e.clientY - r.top) / r.height - 0.5;
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        el.style.setProperty("--rx", `${-y * 10}deg`);
        el.style.setProperty("--ry", `${x * 14}deg`);
        el.style.setProperty("--mx", `${(x + 0.5) * 100}%`);
        el.style.setProperty("--my", `${(y + 0.5) * 100}%`);
      });
    };
    const onLeave = () => {
      el.style.setProperty("--rx", "0deg");
      el.style.setProperty("--ry", "0deg");
      el.style.setProperty("--mx", "50%");
      el.style.setProperty("--my", "50%");
    };
    el.addEventListener("mousemove", onMove);
    el.addEventListener("mouseleave", onLeave);
    return () => {
      el.removeEventListener("mousemove", onMove);
      el.removeEventListener("mouseleave", onLeave);
      cancelAnimationFrame(raf);
    };
  }, []);
  return (
    <Reveal delay={i * 80}>
      <article className="tm-card" ref={ref} data-cursor="hover">
        <div className="tm-card-inner">
          <div className="tm-photo">
            <img src={`team/${p.slug}.png`} alt={p.name} loading="lazy" />
            <div className="tm-photo-glow" aria-hidden="true"></div>
            <div className="tm-photo-grain" aria-hidden="true"></div>
          </div>
          <div className="tm-meta">
            <div className="tm-meta-row">
              <span className="tm-tag mono">{p.tag}</span>
              <span className="tm-years mono">{p.years}</span>
            </div>
            <h3 className="tm-name display">{p.name}</h3>
            <p className="tm-role">{p.role}</p>
          </div>
          <span className="tm-corner mono">+</span>
        </div>
      </article>
    </Reveal>
  );
}

function Team() {
  return (
    <section className="section tm-section" id="team">
      <div className="shell">
        <div className="section-hd">
          <Reveal><span className="section-eyebrow">Team / 05</span></Reveal>
          <Reveal mask>
            <h2 className="section-title display">
              Small studio.<br />
              Sharper for it.
            </h2>
          </Reveal>
          <Reveal delay={200}>
            <p className="tm-sub">
              Six people. Same room. No agency layers between you and the person actually doing the work.
            </p>
          </Reveal>
        </div>

        <div className="tm-grid">
          {TEAM.map((p, i) => (
            <TeamCard key={p.slug} p={p} i={i} />
          ))}
        </div>

        <Reveal delay={300}>
          <div className="tm-foot mono">
            <span>— Based in Bandung, ID · Working with brands across SEA</span>
            <a href="#contact" className="tm-foot-link" data-cursor="hover">
              Work with us <Arrow size={14} />
            </a>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

// ---------- Process (interactive stepper) ----------
function Process() {
  const STEPS = [
    {
      num: "01", title: "Discover",
      desc: "Audit, interview, define. We sharpen the problem before touching pixels.",
      bullets: ["Stakeholder & user interviews", "Tech & content audit", "Scope and KPI alignment"],
      duration: "1–2 weeks"
    },
    {
      num: "02", title: "Design",
      desc: "Pixel-level decisions. We prototype until friction disappears.",
      bullets: ["Information architecture", "Hi-fi prototype", "Design system tokens"],
      duration: "2–3 weeks"
    },
    {
      num: "03", title: "Build",
      desc: "Engineered in production from day one. No throwaway code.",
      bullets: ["Component-driven build", "CMS & integrations", "Performance budget"],
      duration: "2–4 weeks"
    },
    {
      num: "04", title: "Launch",
      desc: "Ship, measure, iterate. We hand off documentation that survives.",
      bullets: ["QA & accessibility", "Analytics & SEO", "Team handover"],
      duration: "1 week"
    }
  ];
  const [active, setActive] = useState(0);
  const [paused, setPaused] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    if (paused) return;
    const t = setInterval(() => setActive((a) => (a + 1) % STEPS.length), 5000);
    return () => clearInterval(t);
  }, [paused, STEPS.length]);

  return (
    <section className="section process-v2" id="process"
      onMouseEnter={() => setPaused(true)}
      onMouseLeave={() => setPaused(false)}
      ref={ref}>
      <div className="shell">
        <div className="section-hd">
          <Reveal><span className="section-eyebrow">Process / 06</span></Reveal>
          <Reveal mask><h2 className="section-title display">Four moves.<br />No theatre.</h2></Reveal>
        </div>

        {/* Stepper rail */}
        <div className="pv-rail" role="tablist">
          <div className="pv-progress" style={{ width: `${(active / (STEPS.length - 1)) * 100}%` }}></div>
          {STEPS.map((s, i) => (
            <button
              key={s.num}
              role="tab"
              aria-selected={active === i}
              className={`pv-node ${active === i ? "is-active" : ""} ${i < active ? "is-done" : ""}`}
              onClick={() => setActive(i)}
              data-cursor="hover">
              <span className="pv-node-dot" aria-hidden="true"></span>
              <span className="pv-node-num mono">{s.num}</span>
              <span className="pv-node-title">{s.title}</span>
            </button>
          ))}
        </div>

        {/* Active panel */}
        <div className="pv-panel">
          <div className="pv-panel-l">
            <span className="pv-panel-num mono">— Step {STEPS[active].num} / {String(STEPS.length).padStart(2, "0")}</span>
            <h3 className="pv-panel-title display" key={`t-${active}`}>{STEPS[active].title}</h3>
            <p className="pv-panel-desc" key={`d-${active}`}>{STEPS[active].desc}</p>
            <span className="pv-panel-meta mono">Duration · {STEPS[active].duration}</span>
          </div>
          <ul className="pv-panel-r" key={`b-${active}`}>
            {STEPS[active].bullets.map((b, i) => (
              <li key={i} style={{ animationDelay: `${i * 80}ms` }}>
                <span className="pv-bul-dot" aria-hidden="true"></span>
                <span>{b}</span>
              </li>
            ))}
          </ul>
        </div>

        <div className="pv-foot mono">
          <span>{paused ? "Paused" : "Auto-advancing"}</span>
          <span>{String(active + 1).padStart(2, "0")} / {String(STEPS.length).padStart(2, "0")}</span>
        </div>
      </div>
    </section>);
}

// ---------- FAQ ----------
const FAQS = 
[
  {
    q: "How long does a project take?",
    a: "Most projects ship within 2–4 weeks depending on scope, complexity, and feedback cycles. Smaller sprint-based builds can move much faster. We keep our pipeline intentionally lean so every project gets full focus."
  },
  {
    q: "How much does a project cost?",
    a: "Pricing depends on scope, complexity, integrations, and production requirements. After a discovery call, we send a fixed proposal with clear deliverables, timelines, and no hidden surprises."
  },
  {
    q: "Can you work with an existing website or codebase?",
    a: "Yes. We can improve, rebuild, optimize, or expand existing systems. We usually start with a technical audit to understand what’s worth keeping and what’s slowing the business down."
  },
  {
    q: "What technologies do you work with?",
    a: "We work across Shopify, WooCommerce, WordPress, React, Next.js, Node.js, Firebase, custom dashboards, API integrations, interactive systems, and experimental web technologies."
  },
  {
    q: "Do you handle both design and development?",
    a: "Yes. From strategy, UI/UX, motion, and interaction design to frontend, backend, deployment, and optimization — everything can be handled end-to-end."
  },
  {
    q: "Can you build custom dashboards or internal tools?",
    a: "Absolutely. We build operational dashboards, KPI systems, registration systems, booking platforms, admin panels, and custom business tools tailored to specific workflows."
  },
  {
    q: "Do you work internationally?",
    a: "Yes. Most collaborations happen remotely with clients across different time zones. Communication stays structured, fast, and async-friendly."
  },
  {
    q: "What happens after launch?",
    a: "We can continue through ongoing maintenance, optimization, new feature development, and scaling support — or hand off a clean, documented system your internal team can fully manage."
  },
  {
    q: "How do we get started?",
    a: "Send us your project brief, goals, references, timeline, and estimated budget range. From there, we’ll map out the scope, workflow, and next steps."
  }
];


function FAQ() {
  const [open, setOpen] = useState(0);
  return (
    <section className="section" id="faq">
      <div className="shell">
        <div className="section-hd">
          <Reveal><span className="section-eyebrow">FAQ / 07</span></Reveal>
          <Reveal mask><h2 className="section-title display">Things people ask<br />before they sign.</h2></Reveal>
        </div>
        <div className="faq-list">
          {FAQS.map((f, i) =>
          <Reveal key={i} delay={i * 40}>
              <div className={`faq-item ${open === i ? "open" : ""}`} onClick={() => setOpen(open === i ? -1 : i)} data-cursor="hover">
                <div className="faq-q display">
                  <span>{f.q}</span>
                  <span className="faq-toggle"></span>
                </div>
                <div className="faq-a">{f.a}</div>
              </div>
            </Reveal>
          )}
        </div>
      </div>
    </section>);

}

// ---------- CTA ----------
function CTA() {
  return (
    <section className="cta" id="contact">
      <div className="shell">
        <Reveal><span className="cta-eyebrow">Available for new work</span></Reveal>
        <Reveal mask>
          <h2 className="cta-headline display">
            Let's build<br /><em>something great.</em>
          </h2>
        </Reveal>
        <Reveal delay={200}><p className="cta-sub">No fluff. Just results.</p></Reveal>
        <Reveal delay={300}>
          <div className="cta-ctas">
            <MagneticButton className="btn-primary"><a href="https://wa.me/+6285183166613" target="_blank">Start a Project</a> <Arrow /></MagneticButton>
            <MagneticButton className="btn-ghost">hello@waitwhatweb.co.id</MagneticButton>
          </div>
        </Reveal>
      </div>
    </section>);

}

// ---------- Footer ----------
function Footer() {
  return (
    <footer className="footer">
      <div className="shell">
        <div className="footer-top">
          <div className="footer-brand display">
            <img src="assets/waitwhat.png" alt="WaitWhatWeb" className="footer-logo-img" />
          </div>
          <div className="footer-col">
            <h4>Studio</h4>
            <ul>
              <li><a href="#work" data-cursor="hover">Work</a></li>
              <li><a href="#services" data-cursor="hover">Services</a></li>
              <li><a href="#process" data-cursor="hover">Process</a></li>
              <li><a href="#contact" data-cursor="hover">Contact</a></li>
            </ul>
          </div>
          <div className="footer-col">
            <h4>Connect</h4>
            <ul>
              <li><a href="https://www.instagram.com/waitwhatweb" data-cursor="hover">Instagram <span className="ext">↗</span></a></li>
              <li><a href="https://www.linkedin.com/company/waitwhatweb" data-cursor="hover">LinkedIn <span className="ext">↗</span></a></li>
            </ul>
          </div>
          <div className="footer-col">
            <h4>Contact</h4>
            <ul>
              <li><a href="#" data-cursor="hover">PT WWW KLIK AJA</a></li>
              <li><a href="mailto:hello@waitwhatweb.co.id" data-cursor="hover">hello@waitwhatweb.co.id</a></li>
              <li><a href="https://wa.me/+6285183166613" data-cursor="hover">+62-851-831-666-13 (Lukman)</a></li>
              <li><a href="#" data-cursor="hover">Kanayakan Dalam No.28, Bandung-Indonesia</a></li>
            </ul>
          </div>
        </div>
        <div className="footer-bottom">
          <span>© 2026 WAITWHAT - WEB development studio · All rights reserved</span>
          <span>Built in Bandung · v.05</span>
        </div>
      </div>
    </footer>);

}

// ---------- Custom Cursor ----------
function CustomCursor() {
  const dotRef = useRef(null);
  const ringRef = useRef(null);
  useEffect(() => {
    let mx = window.innerWidth / 2,my = window.innerHeight / 2;
    let dx = mx,dy = my,rx = mx,ry = my;
    const onMove = (e) => {mx = e.clientX;my = e.clientY;};
    window.addEventListener("mousemove", onMove);

    const onOver = (e) => {
      const t = e.target.closest("[data-cursor], a, button, .service, .faq-item, .work-card, .principle, input, textarea");
      document.body.classList.remove("cursor-hover", "cursor-text");
      if (!t) return;
      const v = t.getAttribute("data-cursor");
      if (v === "text" || t.tagName === "INPUT" || t.tagName === "TEXTAREA") document.body.classList.add("cursor-text");else
      document.body.classList.add("cursor-hover");
    };
    document.addEventListener("mouseover", onOver);

    let raf;
    const tick = () => {
      dx += (mx - dx) * 0.6;
      dy += (my - dy) * 0.6;
      rx += (mx - rx) * 0.18;
      ry += (my - ry) * 0.18;
      if (dotRef.current) dotRef.current.style.transform = `translate(${dx}px, ${dy}px) translate(-50%, -50%)`;
      if (ringRef.current) ringRef.current.style.transform = `translate(${rx}px, ${ry}px) translate(-50%, -50%)`;
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => {
      window.removeEventListener("mousemove", onMove);
      document.removeEventListener("mouseover", onOver);
      cancelAnimationFrame(raf);
    };
  }, []);
  return (
    <>
      <svg width="0" height="0" style={{ position: "fixed" }} aria-hidden="true">
        <defs>
          <filter id="cursor-refract" x="-20%" y="-20%" width="140%" height="140%">
            <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" seed="4" result="t">
              <animate attributeName="baseFrequency" dur="14s"
                values="0.010; 0.018; 0.012; 0.010" repeatCount="indefinite" />
            </feTurbulence>
            <feDisplacementMap in="SourceGraphic" in2="t" scale="14"
              xChannelSelector="R" yChannelSelector="G" />
          </filter>
        </defs>
      </svg>
      <div ref={dotRef} className="cursor-dot" />
      <div ref={ringRef} className="cursor-ring">
        <span className="cursor-ring-glass" aria-hidden="true"></span>
        <span className="cursor-ring-spec" aria-hidden="true"></span>
        <span className="cursor-ring-rim" aria-hidden="true"></span>
      </div>
    </>);

}

Object.assign(window, {
  Reveal, MagneticButton, Arrow,
  Nav, Hero, PixelTagline, GooWordmark, Marquee, TrustedClients, About, Services, Principles, Team, Process, FAQ, CTA, Footer, CustomCursor
});