/* App shell — smooth scroll, scroll progress rail, magnetic cursor,
   theme wipe, section composition. */

/* Inline BODE wordmark — uses currentColor so it themes with surrounding text. */
function BodeLogo({ className, ariaLabel = "BODE" }) {
  return (
    <svg
      className={className}
      viewBox="0 0 686 551"
      fill="currentColor"
      xmlns="http://www.w3.org/2000/svg"
      role="img"
      aria-label={ariaLabel}
    >
      <path d="M349.271 62.915C361.8 63.0225 374.332 63.0491 386.861 62.9932C411.732 62.998 434.537 62.7225 453.735 81.5527C467.294 94.8528 471.626 111.805 471.778 130.274V324.741L471.799 379.442C471.82 390.54 472.373 407.313 470.479 417.8C468.306 430.116 462.406 441.467 453.573 450.319L453.31 450.586C446.33 457.507 438.341 461.692 429.063 464.767C414.77 469.506 400.798 468.587 385.973 468.599L349.208 468.575L349.271 62.915ZM421.944 98.7803C411.492 90.2338 397.134 92.1623 384.374 92.2393V324.577L384.387 400.59C384.384 413.211 384.159 426.35 384.365 438.902C399.855 438.98 413.612 441.336 425.297 429.718C425.423 429.595 425.545 429.466 425.668 429.337C435.905 416.689 435.237 406.092 435.207 390.83L435.187 363.678L435.217 271.135V172.225L435.214 142.198C435.22 126.158 435.896 110.188 421.944 98.7803Z" />
      <path d="M271.474 195.347C286.882 193.863 303.593 198.039 315.548 208.091C327.637 218.205 335.155 232.754 336.412 248.468C337.22 257.896 336.757 272.281 336.754 282.197L336.751 343.193L336.756 392.509C336.765 414.033 338.672 433.255 323.298 450.945C311.277 464.772 297.792 469.817 279.819 471.356C279.551 471.389 279.282 471.416 279.013 471.437C263.305 472.53 246.409 467.629 234.332 457.319C222.859 447.407 215.734 433.391 214.49 418.279C213.669 407.547 214.1 393.447 214.103 382.371L214.108 320.3L214.098 274.787C214.09 260.408 212.879 248.109 217.985 234.414C226.754 210.896 246.829 197.189 271.474 195.347ZM299.705 276.799C299.631 257.069 303.928 224.088 274.418 226.153C261.811 227.576 254.991 237.539 253.589 249.635C252.431 259.621 252.842 270.4 252.839 280.496L252.859 330.853L252.864 385.846C252.87 396.581 252.444 407.777 253.512 418.425C254.958 432.846 263.805 441.557 278.471 439.953C290.78 438.644 296.411 430.495 297.585 418.53C298.516 409.031 298.192 399.209 298.3 389.648L298.856 335.247L299.645 281.235C299.665 279.759 299.71 278.274 299.705 276.799Z" />
      <path d="M135.502 198.483C145.782 198.477 158.249 197.813 168.287 199.283C199.827 203.901 206.901 245.775 181.43 263.435C191.05 268.164 198.675 274.114 201.795 284.833C204.461 293.991 203.24 304.995 198.59 313.307C196.691 316.599 195.298 318.648 192.561 321.355C187.856 325.95 182.066 329.281 175.73 331.039C164.81 334.011 159.364 333.604 148.24 333.616L128.687 333.634L79.003 333.61L79.0537 198.475L135.502 198.483ZM166.771 290.453C167.09 275.098 151.166 277.202 140.591 277.214L114.349 277.26L114.355 303.859L140.531 303.883C149.413 303.891 155.651 305.378 162.964 299.624C165.868 296.838 166.687 294.467 166.771 290.453ZM161.133 231.184C156.412 226.23 144.848 227.23 138.742 227.236L114.329 227.265L114.341 252.656L139.299 252.705C147.165 252.714 154.189 254.015 160.643 248.836C165.035 244.287 165.322 235.581 161.133 231.184Z" />
      <path d="M484.113 333.559L606.997 333.577L606.967 362.403L551.426 362.421C541.109 362.415 529.8 362.172 519.558 362.558L519.63 386.912C528.804 387.121 538.768 386.947 547.993 386.947L598.478 386.891L598.475 414.752C581.028 415.07 562.623 414.848 545.128 414.836L519.552 414.8L519.603 438.932L606.985 438.95L606.991 468.593H484.101L484.113 333.559Z" />
    </svg>
  );
}
window.BodeLogo = BodeLogo;

const SECTION_MARKERS = [
  { id: 'hero',         label: '01' },
  { id: 'blueprint',    label: '02' },
  { id: 'problem',      label: '03' },
  { id: 'capabilities', label: '04' },
  { id: 'position',     label: '05' },
  { id: 'contact',      label: '06' },
];

/* ----------------------------- Lenis smooth scroll ----------------------- */
function useLenis() {
  React.useEffect(() => {
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    // Slightly snappier easing + shorter duration than the default — keeps the
    // page from feeling rubbery and reduces double-lerp jitter against any
    // ScrollTrigger scrub: true triggers below.
    const lenis = new window.Lenis({
      duration: 0.9,
      easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
      smoothWheel: true,
      wheelMultiplier: 1,
      touchMultiplier: 1.6,
    });
    const tickerCb = (time) => lenis.raf(time * 1000);
    gsap.ticker.add(tickerCb);
    gsap.ticker.lagSmoothing(0);
    lenis.on('scroll', window.ScrollTrigger.update);
    window.__bodeLenis = lenis;
    // Pinned ScrollTriggers measure layout at refresh time. Wait one frame so
    // initial layout (logo image load, hero ambient masks) has settled.
    requestAnimationFrame(() => window.ScrollTrigger.refresh());
    return () => {
      gsap.ticker.remove(tickerCb);
      lenis.destroy();
      window.__bodeLenis = null;
    };
  }, []);
}

/* ----------------------------- Magnetic cursor brackets ----------------- */
function CursorBrackets() {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (window.matchMedia('(pointer: coarse)').matches) return;

    let raf = null;
    let target = { x: -100, y: -100 };
    let pos = { x: -100, y: -100 };
    let scale = 1;
    let scaleTarget = 1;
    let active = false;

    function onMove(e) {
      const t = e.target;
      const interactive = t && t.closest && t.closest('a, button, input, textarea, .sector, .cap, .opt, .submit-btn, .founder, [data-magnetic]');
      const magnet = t && t.closest && t.closest('[data-magnetic]');

      if (interactive) {
        const rect = interactive.getBoundingClientRect();
        const cx = rect.left + rect.width / 2;
        const cy = rect.top + rect.height / 2;
        const pull = magnet ? 0.55 : 0.18;
        target.x = e.clientX + (cx - e.clientX) * pull;
        target.y = e.clientY + (cy - e.clientY) * pull;
        scaleTarget = magnet ? 2.4 : 1.4;
        el.classList.add('show');
        el.classList.toggle('lock', !!magnet);
      } else {
        target.x = e.clientX;
        target.y = e.clientY;
        scaleTarget = 1;
        el.classList.add('show');
        el.classList.remove('lock');
      }
      if (!active) { active = true; tick(); }
    }
    function tick() {
      pos.x += (target.x - pos.x) * 0.28;
      pos.y += (target.y - pos.y) * 0.28;
      scale += (scaleTarget - scale) * 0.18;
      el.style.transform = `translate(${pos.x}px, ${pos.y}px) translate(-50%, -50%) scale(${scale})`;
      raf = requestAnimationFrame(tick);
    }
    function onLeave() { el.classList.remove('show'); }
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseleave', onLeave);
    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseleave', onLeave);
      cancelAnimationFrame(raf);
    };
  }, []);
  return (
    <div className="cursor-brackets" ref={ref}>
      <span className="tl"></span><span className="tr"></span>
      <span className="bl"></span><span className="br"></span>
    </div>
  );
}

/* ----------------------------- Scroll progress rail --------------------- */
function ScrollProgress() {
  const fillRef = React.useRef(null);
  const [active, setActive] = React.useState('hero');

  React.useEffect(() => {
    const fill = fillRef.current;
    let raf = null;
    function tick() {
      const max = document.documentElement.scrollHeight - window.innerHeight;
      const p = max > 0 ? window.scrollY / max : 0;
      if (fill) fill.style.transform = `scaleY(${p})`;
      let bestId = 'hero';
      let bestDist = Infinity;
      for (const m of SECTION_MARKERS) {
        const node = document.getElementById(m.id);
        if (!node) continue;
        const r = node.getBoundingClientRect();
        const d = Math.abs(r.top - 80);
        if (r.top < window.innerHeight * 0.6 && d < bestDist) {
          bestDist = d; bestId = m.id;
        }
      }
      setActive(bestId);
      raf = requestAnimationFrame(tick);
    }
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);

  function jumpTo(id) {
    const node = document.getElementById(id);
    if (!node) return;
    if (window.__bodeLenis) {
      window.__bodeLenis.scrollTo(node, { offset: -40, duration: 1.4 });
    } else {
      node.scrollIntoView({ behavior: 'smooth', block: 'start' });
    }
  }

  return (
    <nav className="scroll-rail" aria-label="Section navigation">
      <div className="scroll-rail__track" aria-hidden="true"><div className="scroll-rail__fill" ref={fillRef}></div></div>
      <div className="scroll-rail__markers">
        {SECTION_MARKERS.map((m) => (
          <button
            key={m.id}
            type="button"
            className={`scroll-rail__mark ${active === m.id ? 'is-active' : ''}`}
            onClick={() => jumpTo(m.id)}
            aria-label={`Jump to ${m.id}`}
            aria-current={active === m.id ? 'true' : undefined}
          >
            <span className="dot" aria-hidden="true"></span>
            <span className="num" aria-hidden="true">{m.label}</span>
          </button>
        ))}
      </div>
    </nav>
  );
}

/* ----------------------------- Theme toggle (with coral wipe) ----------- */
function ThemeToggle() {
  const [dark, setDark] = React.useState(() => {
    try {
      const v = localStorage.getItem('bode-theme-v3');
      if (v === 'dark') return true;
      if (v === 'light') return false;
      return false; // light-led by default; dark is opt-in
    } catch (e) { return false; }
  });
  function toggle() {
    const next = !dark;
    const wipe = document.getElementById('themeWipe');
    if (wipe && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
      gsap.fromTo(wipe,
        { scaleX: 0, transformOrigin: 'left center', opacity: 1 },
        { scaleX: 1, duration: 0.45, ease: 'expo.in',
          onComplete: () => {
            setDark(next);
            gsap.to(wipe, { scaleX: 0, transformOrigin: 'right center', duration: 0.45, ease: 'expo.out', delay: 0.05 });
          }
        }
      );
    } else {
      setDark(next);
    }
  }
  React.useEffect(() => {
    document.body.classList.toggle('dark', dark);
    try { localStorage.setItem('bode-theme-v3', dark ? 'dark' : 'light'); } catch (e) {}
  }, [dark]);
  return (
    <button
      type="button"
      className={`theme-toggle ${dark ? 'is-dark' : ''}`}
      aria-label={`Switch to ${dark ? 'light' : 'dark'} mode`}
      onClick={toggle}
    >
      <span className="theme-toggle__track">
        <span className="theme-toggle__thumb"></span>
        <span className="theme-toggle__icon theme-toggle__icon--sun ms">light_mode</span>
        <span className="theme-toggle__icon theme-toggle__icon--moon ms">dark_mode</span>
      </span>
    </button>
  );
}

/* ----------------------------- Nav -------------------------------------- */
function Nav() {
  return (
    <nav className="nav">
      <a href="#hero" className="nav__brand" aria-label="BODE, home">
        <BodeLogo className="nav__brand-logo" />
      </a>
      <div className="nav__right">
        <span className="nav__status">
          <span className="nav__dot"></span>
          <span>Coming Soon · 2026</span>
        </span>
        <ThemeToggle />
        <a href="#contact" className="nav__cta" data-magnetic>
          <span>Request Access</span>
          <span className="ms" style={{ fontSize: 16 }}>arrow_downward</span>
        </a>
      </div>
    </nav>
  );
}

/* ----------------------------- Section transitions --------------------- */
function useSectionCuts() {
  React.useEffect(() => {
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    const sweep = document.getElementById('cutSweep');
    if (!sweep) return;

    const ids = ['blueprint', 'problem', 'capabilities', 'position', 'contact'];
    const triggers = ids.map((id) => {
      const el = document.getElementById(id);
      if (!el) return null;
      return ScrollTrigger.create({
        trigger: el,
        start: 'top 88%',
        onEnter: () => {
          gsap.fromTo(sweep,
            { scaleX: 0, transformOrigin: 'left center', opacity: 0.85 },
            { scaleX: 1, duration: 0.32, ease: 'expo.in',
              onComplete: () => {
                gsap.to(sweep, { scaleX: 0, transformOrigin: 'right center', duration: 0.32, ease: 'expo.out' });
              }
            }
          );
        },
        once: true,
      });
    });
    return () => triggers.forEach((t) => t && t.kill());
  }, []);
}

/* ----------------------------- Reveal observer ------------------------- */
function useReveal() {
  React.useEffect(() => {
    const els = document.querySelectorAll('.reveal');
    const obs = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add('in');
          obs.unobserve(e.target);
        }
      });
    }, { threshold: 0.12, rootMargin: '0px 0px -60px 0px' });
    els.forEach((el) => obs.observe(el));
    return () => obs.disconnect();
  });
}

/* ----------------------------- App -------------------------------------- */
function App() {
  useLenis();
  useReveal();
  useSectionCuts();

  return (
    <React.Fragment>
      <NetworkBackground density="balanced" />
      <CursorBrackets />
      <ScrollProgress />
      <Nav />
      <Hero />
      <Blueprint />
      <Problem />
      <WhatBodeDoes />
      <Position />
      <Contact />
      <Footer />
      <BodeTweaks />
    </React.Fragment>
  );
}

window.App = App;
