// Header, pinned horizontal hero, floating quote button const NAV_ITEMS = [ ['Solutions', '#solutions'], ['Work', '#work'], ['Process', '#process'], ['Why us', '#why'], ['FAQ', '#faq'], ]; function Header() { const [open, setOpen] = React.useState(false); const headerRef = React.useRef(null); const lastY = React.useRef(0); const menuBtnRef = React.useRef(null); useScrollFrame(() => { const el = headerRef.current; if (!el) return; const y = window.scrollY; el.classList.toggle('is-solid', y > 24); const goingDown = y > lastY.current + 4; const goingUp = y < lastY.current - 4; if (goingDown && y > 240) el.classList.add('is-hidden'); if (goingUp || y < 240) el.classList.remove('is-hidden'); lastY.current = y; }); // is-solid / is-hidden are toggled imperatively above, so is-open is too: // a React-managed className would wipe them on every menu toggle. React.useLayoutEffect(() => { const el = headerRef.current; if (!el) return; el.classList.toggle('is-open', open); if (open) el.classList.remove('is-hidden'); }, [open]); React.useEffect(() => { document.body.classList.toggle('menu-open', open); if (!open) return undefined; const onKey = (e) => { if (e.key === 'Escape') { setOpen(false); menuBtnRef.current && menuBtnRef.current.focus(); } }; const onResize = () => window.innerWidth > 1023 && setOpen(false); window.addEventListener('keydown', onKey); window.addEventListener('resize', onResize); return () => { window.removeEventListener('keydown', onKey); window.removeEventListener('resize', onResize); }; }, [open]); const close = () => setOpen(false); return (
Next Horizon Get instant quote Quote
); } // Headline words. The pill (a switch whose rising-sun knob slides from off to on) is inserted after PILL_AFTER words. const HERO_WORDS = ['Idea', 'into', 'reality']; const PILL_AFTER = 1; function Hero() { const reduced = useReducedMotion(); const sectionRef = React.useRef(null); const trackRef = React.useRef(null); const pillRef = React.useRef(null); const wordRefs = React.useRef([]); useScrollFrame(() => { const section = sectionRef.current; const track = trackRef.current; if (!section || !track) return; if (reduced) { track.style.transform = ''; if (pillRef.current) pillRef.current.style.removeProperty('--roll'); return; } const p = pinProgress(section); const vw = window.innerWidth; const distance = Math.max(0, track.scrollWidth - vw); // Hold briefly at the start, then slide the whole line through. const t = clamp01((p - 0.04) / 0.86); track.style.transform = `translate3d(${(-distance * t).toFixed(1)}px,0,0)`; if (pillRef.current) pillRef.current.style.setProperty('--roll', clamp01((p - 0.02) / 0.26).toFixed(3)); const threshold = vw * 0.78; wordRefs.current.forEach((w) => { if (!w) return; w.classList.toggle('is-on', w.getBoundingClientRect().left < threshold); }); }, [reduced]); const parts = []; HERO_WORDS.forEach((w, i) => { parts.push( (wordRefs.current[i] = el)} className="hw">{w}); if (i === PILL_AFTER - 1) { parts.push( , ); } }); return (

{HERO_WORDS.join(' ')}

See what we build

AI and custom software development in Malaysia. Internal systems, dashboards, customer portals, automation and AI tools, live in 4-8 weeks.

); } // Pill that follows the visitor after the hero and gets out of the way near the quote/CTA/footer. function FloatingQuote() { const ref = React.useRef(null); useScrollFrame(() => { const el = ref.current; if (!el) return; const vh = window.innerHeight; const inView = (id) => { const node = document.getElementById(id); if (!node) return false; const r = node.getBoundingClientRect(); return r.top < vh && r.bottom > 0; }; const about = document.getElementById('about'); const pastIntro = about ? about.getBoundingClientRect().bottom < vh * 0.6 : false; const blocked = ['quote', 'contact', 'footer'].some(inView); const menuOpen = document.body.classList.contains('menu-open'); el.classList.toggle('is-visible', pastIntro && !blocked && !menuOpen); // Swap to ink while the button sits over the accent band. const sol = document.getElementById('solutions'); const r = sol ? sol.getBoundingClientRect() : null; el.classList.toggle('on-accent', !!r && r.top < vh - 40 && r.bottom > vh - 40); }); return (
Get instant quote
); } Object.assign(window, { Header, Hero, FloatingQuote });