/* global React */ const { useState, useEffect, useRef } = React; // ========================================================= // Top Nav // ========================================================= function Nav({ onBook, bookLabel = "Book a discovery call", showAffiliates = true }) { const [condensed, setCondensed] = useState(false); // Condense the sticky header into a slim bar once the user scrolls off // the very top โ€” reclaims vertical space while reading. useEffect(() => { const onScroll = () => setCondensed(window.scrollY > 60); window.addEventListener("scroll", onScroll, { passive: true }); onScroll(); return () => window.removeEventListener("scroll", onScroll); }, []); return ( ); } function ArrowGlyph() { return ( ); } function StarGlyph() { return ( ); } function WhatsAppGlyph() { return ( ); } // ========================================================= // Sticky bottom CTA โ€” appears after scrolling past hero // ========================================================= function StickyCTA({ onBook, bookLabel = "Book a call" }) { const [on, setOn] = useState(false); useEffect(() => { const onScroll = () => { const y = window.scrollY; const h = window.innerHeight; const doc = document.documentElement.scrollHeight; // Wait until the actual hero is past, not just one viewport of scroll. // This matters on mobile, where the hero is taller than the viewport, // and the avail bar at the bottom of the hero would get covered by // the sticky CTA if we triggered on viewport-height alone. const hero = document.getElementById("top"); const heroBottom = hero ? hero.getBoundingClientRect().bottom + y : h * 0.9; setOn(y > heroBottom - 40 && y < doc - h * 1.4); }; window.addEventListener("scroll", onScroll, { passive: true }); onScroll(); return () => window.removeEventListener("scroll", onScroll); }, []); return (
Jay
Talk to Jay 30-min discovery ยท no pitch
{ /* Google Ads โ€” WhatsApp Click conversion (mobile floating button) */ if (typeof gtag === "function") { gtag('event', 'conversion', { 'send_to': 'AW-757764531/ijKzCMvGo70cELOjqukC' }); } }}> Message Jay on WhatsApp
); } // ========================================================= // Reveal-on-scroll wrapper // ========================================================= function Reveal({ children, as = "div", delay = 0, ...rest }) { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { setTimeout(() => el.classList.add("is-in"), delay); io.disconnect(); } }, { threshold: 0.15 } ); io.observe(el); return () => io.disconnect(); }, [delay]); const Tag = as; return ( {children} ); } Object.assign(window, { Nav, StickyCTA, Reveal, ArrowGlyph, StarGlyph, WhatsAppGlyph });