/* Wh4 main app - React + Babel inline */

const { useState, useEffect, useRef, useCallback } = React;

/* ---- Scroll progress hook for a section ---- */
function useScrollProgress(ref) {
  const [progress, setProgress] = useState(0);
  useEffect(() => {
    if (!ref.current) return;
    const onScroll = () => {
      const el = ref.current;
      if (!el) return;
      const rect = el.getBoundingClientRect();
      const total = rect.height - window.innerHeight;
      const p = Math.max(0, Math.min(1, -rect.top / total));
      setProgress(p);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, [ref]);
  return progress;
}

/* ---- Map a value between two ranges ---- */
function mapRange(t, [a, b], [c, d]) {
  if (t <= a) return c;
  if (t >= b) return d;
  const ratio = (t - a) / (b - a);
  return c + (d - c) * ratio;
}

/* ---- Is viewport mobile-width ---- */
function useIsMobile(bp = 768) {
  const [m, setM] = useState(typeof window !== 'undefined' && window.innerWidth < bp);
  useEffect(() => {
    const on = () => setM(window.innerWidth < bp);
    on();
    window.addEventListener('resize', on);
    return () => window.removeEventListener('resize', on);
  }, [bp]);
  return m;
}

/* ---- Reveal on scroll ---- */
function Reveal({ children, delay = 0, as: Tag = 'div', className = '', style }) {
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          setShown(true);
          io.disconnect();
        }
      });
    }, { rootMargin: '-60px' });
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  const cls = 'reveal ' + (shown ? 'in ' : '') + (delay ? `reveal-delay-${delay} ` : '') + className;
  return <Tag ref={ref} className={cls} style={style}>{children}</Tag>;
}

/* ---- Window scroll Y ---- */
function useScrollY() {
  const [y, setY] = useState(0);
  useEffect(() => {
    const onScroll = () => setY(window.scrollY);
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  return y;
}

/* ---------- Announcement Bar ---------- */
function AnnouncementBar({ onClose }) {
  return (
    <div className="announce">
      <div className="container">
        <div className="left">
          <button className="close" aria-label="Close" onClick={onClose}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <path d="M18 6L6 18M6 6l12 12" />
            </svg>
          </button>
          <p>A family-owned group · M&A · Growth Partnerships · Land · Capital</p>
        </div>
        <div className="right">
          <a href="partners.html" className="pillbtn">Join the Private Founders Collective</a>
          <a href="partners.html" className="arrow" aria-label="Join">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <path d="M5 12h14M13 5l7 7-7 7" />
            </svg>
          </a>
        </div>
      </div>
    </div>
  );
}

/* ---------- Site Header ---------- */
const NAV_LINKS = [
  ['advisory.html', 'M&A'],
  ['partners.html', 'Growth Partnerships'],
  ['property.html', 'Land'],
  ['capital.html', 'Capital'],
];
function SiteHeader({ onContact }) {
  const y = useScrollY();
  const [menuOpen, setMenuOpen] = useState(false);
  // Stay transparent over the (one-viewport) hero; solidify as it scrolls out
  const ratio = Math.min(1, Math.max(0, (y - (window.innerHeight - 320)) / 240));
  const dark = ratio < 0.4 && !menuOpen;
  const headerStyle = {
    backgroundColor: menuOpen ? '#fff' : `rgba(255, 255, 255, ${ratio * 0.9})`,
    color: dark ? '#fff' : '#191c1f',
    borderBottom: (ratio > 0.6 || menuOpen) ? '1px solid var(--line)' : '1px solid transparent',
  };
  return (
    <header className={'header ' + (dark ? 'dark' : 'light')} style={headerStyle}>
      <div className="container row">
        <a href="index.html" className="logo" aria-label="Wh4">
          <img src={dark ? (window.__resources && window.__resources.logoWhite || 'assets/wh4-logo.png') : (window.__resources && window.__resources.logoNavy || 'assets/wh4-logo-navy.png')} alt="Wh4" className="logo-mark" />
        </a>
        <nav>
          {NAV_LINKS.map(([href, label]) => <a key={href} href={href}>{label}</a>)}
        </nav>
        <div className="actions">
          <a href="contact.html#book" className="apply apply-white">Book a consultation</a>
          <button className="nav-toggle" aria-label="Menu" aria-expanded={menuOpen} onClick={() => setMenuOpen((o) => !o)}>
            {menuOpen ? (
              <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M18 6L6 18M6 6l12 12" /></svg>
            ) : (
              <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M3 6h18M3 12h18M3 18h18" /></svg>
            )}
          </button>
        </div>
      </div>
      {menuOpen && (
        <div className="mobile-menu">
          {NAV_LINKS.map(([href, label]) => <a key={href} href={href} onClick={() => setMenuOpen(false)}>{label}</a>)}
          <div className="mobile-menu-actions">
            <a href="contact.html#book" className="mm-apply">Book a consultation</a>
          </div>
        </div>
      )}
    </header>
  );
}

/* ---------- Client globe — WebGL (globe.gl, pinned 2.46.1, self-hosted
   NASA night-lights texture). Markers + animated arcs from London to each
   current client country: USA, Canada, India, UK, France, Dubai. ---------- */
function ClientGlobe() {
  const wrapRef = useRef(null);
  useEffect(() => {
    const el = wrapRef.current;
    if (!el) return;
    let globe = null, dead = false;
    const CITIES = [
      { name: 'UK',     la: 51.51, lo:  -0.13, home: true },
      { name: 'France', la: 48.86, lo:   2.35 },
      { name: 'USA',    la: 40.71, lo: -74.01 },
      { name: 'Canada', la: 43.65, lo: -79.38 },
      { name: 'Dubai',  la: 25.20, lo:  55.27 },
      { name: 'India',  la: 19.08, lo:  72.88 },
    ];
    const load = () => new Promise((res, rej) => {
      if (window.Globe) return res();
      const s = document.createElement('script');
      s.src = 'https://unpkg.com/globe.gl@2.46.1/dist/globe.gl.min.js';
      s.onload = res; s.onerror = rej;
      document.head.appendChild(s);
    });
    load().then(() => {
      if (dead || !window.Globe) return;
      const size = Math.round(el.clientWidth || 540);
      const ARCS = CITIES.filter((c) => !c.home).map((c) => ({
        startLat: 51.51, startLng: -0.13, endLat: c.la, endLng: c.lo,
      }));
      globe = Globe({ animateIn: true })(el)
        .width(size).height(size)
        .backgroundColor('rgba(0,0,0,0)')
        .globeImageUrl('assets/earth-night.jpg')
        .bumpImageUrl('assets/earth-topology.png')
        .showAtmosphere(true)
        .atmosphereColor('#3f8bd9')
        .atmosphereAltitude(0.18)
        .pointsData(CITIES)
        .pointLat('la').pointLng('lo')
        .pointColor((c) => (c.home ? '#ffffff' : '#0cc0df'))
        .pointAltitude(0.016)
        .pointRadius(0.5)
        .labelsData(CITIES)
        .labelLat('la').labelLng('lo')
        .labelText('name')
        .labelSize(1.4)
        .labelDotRadius(0.55)
        .labelColor(() => 'rgba(255,255,255,0.95)')
        .labelResolution(2)
        .labelAltitude(0.02)
        .arcsData(ARCS)
        .arcColor(() => ['rgba(12,192,223,0.08)', 'rgba(12,192,223,0.95)'])
        .arcAltitude(0.22)
        .arcStroke(0.45)
        .arcDashLength(0.55)
        .arcDashGap(1.1)
        .arcDashAnimateTime(3400);
      globe.pointOfView({ lat: 30, lng: -18, altitude: 2.0 });
      const ctr = globe.controls();
      const reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
      ctr.autoRotate = !reduced;
      ctr.autoRotateSpeed = 0.55;
      ctr.enableZoom = false;
      ctr.enablePan = false;
      ctr.enableRotate = true;       // grab with the mouse and spin to any angle
      ctr.enableDamping = true;      // smooth inertia after release
      ctr.dampingFactor = 0.08;
      ctr.rotateSpeed = 0.9;
    }).catch(() => { /* WebGL/script unavailable — hero remains clean */ });
    return () => { dead = true; try { if (globe && globe._destructor) globe._destructor(); } catch (e) {} };
  }, []);
  return (
    <div className="client-globe client-globe--gl" aria-label="Current clients: USA, Canada, India, UK, France, Dubai">
      <div ref={wrapRef} className="cg-canvas" />
      <p className="cg-title">We operate globally.</p>
      <p className="cg-cap">Current clients · USA · Canada · India · UK · France · Dubai</p>
    </div>
  );
}

/* ---------- HERO — static full-bleed office photo.
   Rob (WhatsApp, June 11): no right-hand image, no morph squares, no
   "Building valuable companies" section; background = OUR OFFICE. ---------- */
function HeroMorph() {
  return (
    <section className="hero-section hero-static">
      <div className="hero-sticky">
        <div className="hero-sky" />
        <ClientGlobe />

        <div className="hero-text">
          {/* Jacob's spec (PDF, June 12): Helios Extended, letter-spacing -60, line-height 1.03 */}
          <h1 className="hero-title-spec">
            <span className="hts-mark">Wh<sup>4</sup></span>
            <span className="hts-line">Private Office for Founders &amp; Entrepreneurs</span>
          </h1>
          <p className="hero-sub-spec">London-based, operating globally. Sell-side M&amp;A or strategic growth partnership — depending on where you are in your journey.</p>
          <div className="cta-row">
            <a href="contact.html" className="btn-primary btn-hero-white">Speak with us confidentially →</a>
          </div>
        </div>
      </div>
    </section>
  );
}

Object.assign(window, { useScrollY, useScrollProgress, mapRange, Reveal, AnnouncementBar, SiteHeader, HeroMorph });
