/* ========================================================= JETX LLC — Shared components (multi-page) ========================================================= */ const { useState, useEffect, useRef } = React; /* ---------- counter that animates when triggered ---------- */ function useCountUp(target, options = {}) { const { duration = 1600, decimals = 0, trigger = true } = options; const [value, setValue] = useState(0); const startRef = useRef(null); const rafRef = useRef(null); useEffect(() => { if (!trigger) return; const step = (ts) => { if (!startRef.current) startRef.current = ts; const p = Math.min((ts - startRef.current) / duration, 1); const eased = 1 - Math.pow(1 - p, 5); setValue(target * eased); if (p < 1) rafRef.current = requestAnimationFrame(step); }; rafRef.current = requestAnimationFrame(step); return () => rafRef.current && cancelAnimationFrame(rafRef.current); }, [target, duration, trigger]); return decimals === 0 ? Math.round(value) : value.toFixed(decimals); } /* ---------- Reveal on scroll ---------- */ function Reveal({ children, className = "", delay = 0, as = "div", ...rest }) { const ref = useRef(null); const [shown, setShown] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver( (entries) => entries.forEach((e) => { if (e.isIntersecting) { setTimeout(() => setShown(true), delay); io.unobserve(el); } }), { threshold: 0.12, rootMargin: "0px 0px -60px 0px" } ); io.observe(el); return () => io.disconnect(); }, [delay]); const Tag = as; return {children}; } /* ---------- NAV ---------- */ const NAV_ITEMS = [ { label: "Home", href: "index.html" }, { label: "About", href: "about.html" }, { label: "Technology", href: "projects.html" }, { label: "Portfolio", href: "gallery.html" }, { label: "News", href: "news.html" }, { label: "Contact", href: "contactus.html" }, ]; function Nav({ current }) { const [scrolled, setScrolled] = useState(false); const [open, setOpen] = useState(false); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 40); onScroll(); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); const here = current || (location.pathname.split("/").pop() || "index.html"); return (
JETX
Get in touch
{open && (
{NAV_ITEMS.map((it) => ( {it.label} ))}
)}
); } /* ---------- SUBHERO (interior pages) ---------- */ function SubHero({ crumb, title, lede, image, flip, mirror }) { const [entered, setEntered] = useState(false); useEffect(() => { const id = setTimeout(() => setEntered(true), 60); return () => clearTimeout(id); }, []); return (
{image ?
: }
{crumb}

{title}

{lede &&

{lede}

}
); } /* ---------- PARTNERS / PROGRAMS LOGO STRIP ---------- */ function PartnersStrip({ heading = "Validated with", note }) { const logos = [ { src: "assets/logos/army-white.png", label: "U.S. Army" }, { src: "assets/logos/usaf-white.png", label: "U.S. Air Force" }, { src: "assets/logos/sbir-white.png", label: "SBIR · STTR" }, { src: "assets/logos/xtech-white.png", label: "Army xTech" }, { src: "assets/logos/ucf-white.png", label: "University of Central Florida" }, { src: "assets/logos/ttu-white.png", label: "Tennessee Tech" }, { src: "assets/logos/cymstar-white.png", label: "CymSTAR" }, ]; return (
{heading} {note && {note}}
{logos.map((l) => (
{l.label}
))}
); } /* ---------- SOCIAL ICONS ---------- */ function SocialIcon({ name }) { const p = { width: 18, height: 18, viewBox: "0 0 24 24", fill: "currentColor" }; if (name === "X") return ( ); if (name === "LinkedIn") return ( ); if (name === "YouTube") return ( ); if (name === "Instagram") return ( ); return null; } /* ---------- FOOTER ---------- */ function Footer() { const socials = [ { l: "X", href: "https://twitter.com/JETXAERO" }, { l: "LinkedIn", href: "https://www.linkedin.com/company/jetxus/" }, { l: "YouTube", href: "https://www.youtube.com/channel/UC3EqOdS30VVZnpXG4Lb_88Q" }, { l: "Instagram", href: "https://www.instagram.com/jetx_us/" }, ]; return ( ); } /* ---------- Investor CTA strip ---------- */ function InvestorCTA() { return (
Partnership · Collaboration

Partner
with JETX.

We build the propulsion and platform technology behind the next generation of eVTOL aircraft — for civil and military use. Let's collaborate on defense programs, AAM platforms and concept development.

Start a conversation
); } /* ---------- Propulsion diagram (spinning EDF) ---------- */ function PropulsionDiagram() { return (
{[60, 120, 180].map((r) => ( ))} {[0, 60, 120, 180, 240, 300].map((deg) => ( ))} VECTOR VENTRAL VECTOR EDF · NON-TILTING FLUIDIC + FLAP VECTORING {[[20,20],[380,20],[20,380],[380,380]].map(([x,y]) => ( ))}
); } /* ---------- Propulsion hardware slider (auto-advancing) ---------- */ function PropulsionSlider() { const slides = [ { src: "assets/concepts/prop-fan-cascade.png", k: "01", l: "Electric Ducted Fan + Cascade" }, { src: "assets/concepts/prop-edf-cone.png", k: "02", l: "Embedded EDF Module" }, { src: "assets/concepts/prop-twin-nozzle.png", k: "03", l: "Twin Fluidic Nozzle Unit" }, { src: "assets/concepts/prop-twin-pods.png", k: "04", l: "Vectoring Thruster Pods" }, ]; const [i, setI] = useState(0); const [paused, setPaused] = useState(false); useEffect(() => { if (paused) return; const id = setInterval(() => setI((p) => (p + 1) % slides.length), 4200); return () => clearInterval(id); }, [paused, slides.length]); return (
setPaused(true)} onMouseLeave={() => setPaused(false)} aria-label="JETX propulsion hardware" >
{slides.map((s, idx) => ( {s.l} ))}
{slides[i].k} {slides[i].l}
{slides.map((s, idx) => (
); } /* ---------- Service-card image slider (auto-advancing) ---------- */ function ServiceSlider({ slides, fit = "cover" }) { const [i, setI] = useState(0); const [paused, setPaused] = useState(false); useEffect(() => { if (paused) return; const id = setInterval(() => setI((p) => (p + 1) % slides.length), 4000); return () => clearInterval(id); }, [paused, slides.length]); return (
setPaused(true)} onMouseLeave={() => setPaused(false)} > {slides.map((src, idx) => ( ))}
{slides.map((src, idx) => (
); } /* ---------- Page shell: Nav + content + Footer + Tweaks ---------- */ const JETX_TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "accent": "#FFFFFF", "density": "comfort" }/*EDITMODE-END*/; function PageShell({ current, children }) { const [t, setTweak] = window.useTweaks(JETX_TWEAK_DEFAULTS); useEffect(() => { const map = { "#FFFFFF": "white", "#E40521": "red", "#1A66D6": "blue" }; document.body.dataset.accent = map[t.accent] || "white"; document.body.dataset.density = t.density === "tight" ? "tight" : "comfortable"; }, [t]); return (