import { useCallback, useEffect, useRef, useState, type CSSProperties, type FormEvent, type PointerEvent as ReactPointerEvent, type ReactNode, } from "react"; import { ArrowDown, ArrowRight, ArrowUp, ArrowUpRight, Bot, Brain, Building2, ChartLine, Check, Cloud, Cpu, Database, Eye, FlaskConical, GitBranch, Hammer, Handshake, HeartPulse, Landmark, Layers, Leaf, Mail, MapPin, Megaphone, Network, Palette, Rocket, Scale, Search, ShoppingBag, Sparkles, Sprout, Target, TrendingUp, Truck, Users, UtensilsCrossed, Wallet, Workflow, type LucideIcon, } from "lucide-react"; import { CONTACT_EMAIL, INTERESTS, sendContactEnquiry, type ContactInput } from "@/lib/contact"; // Brand assets cut from kapfoundry-logo-1.png (emblem, wordmark, full lockup). import logoMark from "@/assets/kapfoundry-mark.webp"; import logoWordmark from "@/assets/kapfoundry-wordmark.webp"; import logoFull from "@/assets/kapfoundry-logo-full.webp"; import heroBg from "@/assets/hero-bg.jpg"; import vLogistics from "@/assets/vertical-logistics.jpg"; import vRetail from "@/assets/vertical-retail.jpg"; import vFintech from "@/assets/vertical-fintech.jpg"; import vAgri from "@/assets/vertical-agri.jpg"; import vFoodtech from "@/assets/vertical-foodtech.jpg"; import vCarbon from "@/assets/vertical-carbon.jpg"; import vHealthtech from "@/assets/vertical-healthtech.jpg"; // Home-page sections are "/#id" so they work from any page; About is its own route. const NAV = [ { label: "Studio", href: "/#studio" }, { label: "Framework", href: "/#framework" }, { label: "Portfolio", href: "/#portfolio" }, { label: "AI", href: "/#ai" }, { label: "Investors", href: "/#investors" }, { label: "About", href: "/about" }, { label: "Contact", href: "/#contact" }, ]; export type SitePageName = "home" | "about"; /** Section id a nav link points at ("" for page links). */ function sectionOf(href: string) { return href.split("#")[1] ?? ""; } const VERTICALS = [ { n: "01", name: "Logistics Technology", short: "Logistics", icon: Truck, tag: "Fragmented, expensive, inefficient", img: vLogistics, problem: [ "Last-mile delivery", "Fleet optimization", "Air cargo coordination", "Route planning", "Warehouse visibility", "Freight pricing and supply-chain transparency", ], solution: [ "Route optimization", "Real-time fleet intelligence", "Air cargo platforms", "Warehouse automation and delivery intelligence", "Predictive logistics", "Carbon-aware route planning", ], example: "DoorMile Wings. An AI-powered domestic air cargo and express logistics network.", }, { n: "02", name: "Retail Technology", short: "Retail", icon: ShoppingBag, tag: "Help merchants compete", img: vRetail, problem: [ "Poor loyalty programs", "Lack of customer insights", "No digital engagement", "Ineffective promotions", "Limited analytics", ], solution: [ "Customer loyalty and rewards platforms", "AI marketing", "Consumer and merchant analytics", "Store performance analytics", "Digital commerce", ], example: "Loyaly.ai. An AI loyalty and engagement platform for independent merchants.", }, { n: "03", name: "Financial Technology", short: "Fintech", icon: Wallet, tag: "Millions underserved", img: vFintech, problem: [ "Lending access", "Payments", "Credit visibility", "Collections", "Merchant financing", ], solution: [ "Embedded finance", "Lending infrastructure and merchant credit", "Payment platforms", "Expense intelligence and financial automation", "Risk analytics", ], }, { n: "04", name: "Agriculture Technology", short: "Agritech", icon: Sprout, tag: "Connect farms to markets", img: vAgri, problem: [ "Low productivity", "Market inefficiencies", "Price uncertainty", "Limited technology adoption", "Climate risks", ], solution: [ "Precision farming", "AI crop monitoring", "Marketplace platforms", "Supply-chain digitization", "Yield prediction and farm analytics", ], }, { n: "05", name: "FoodTech", short: "FoodTech", icon: UtensilsCrossed, tag: "Powering the future of food", img: vFoodtech, problem: [ "Customer retention", "Food waste", "Inventory management", "Delivery inefficiencies", "Demand forecasting", ], solution: [ "Smart POS", "AI kitchen analytics", "Restaurant CRM and customer rewards", "Food-delivery optimization", "Inventory intelligence", ], }, { n: "06", name: "Carbon Technology", short: "Carbon", icon: Leaf, tag: "Sustainability, made measurable", img: vCarbon, problem: [ "Accurate emission measurement", "Meeting sustainability goals", "Regulatory reporting", "Green supply-chain visibility", "Reliable ESG tooling", ], solution: [ "Carbon accounting", "ESG platforms", "Emission monitoring", "Carbon credit marketplaces", "Sustainability dashboards", "Green supply-chain intelligence", ], }, { n: "07", name: "HealthTech", short: "HealthTech", icon: HeartPulse, tag: "Care that reaches everyone", img: vHealthtech, problem: [ "Access to quality care", "Fragmented patient records", "Rising treatment costs", "Clinic and hospital efficiency", "Preventive health at scale", ], solution: [ "Digital health platforms", "AI diagnostics and triage", "Clinic operations software", "Remote care and telemedicine", "Health data intelligence", "Patient engagement tools", ], }, ]; const FRAMEWORK = [ { n: "01", title: "Discover", icon: Search, body: "Identify billion-dollar industry problems.", }, { n: "02", title: "Validate", icon: FlaskConical, body: "Customer interviews, market research, prototype testing.", }, { n: "03", title: "Build", icon: Hammer, body: "Scalable products on AI-first architecture." }, { n: "04", title: "Launch", icon: Rocket, body: "Acquire customers and reach product-market fit.", }, { n: "05", title: "Scale", icon: TrendingUp, body: "Expand nationally and internationally." }, { n: "06", title: "Spin-Off", icon: GitBranch, body: "Grow into independent portfolio companies.", }, ]; const STACK: { label: string; icon: LucideIcon }[] = [ { label: "AI and Engineering", icon: Cpu }, { label: "Product Design", icon: Palette }, { label: "Cloud Infrastructure", icon: Cloud }, { label: "Legal and Compliance", icon: Scale }, { label: "Finance", icon: Wallet }, { label: "Marketing", icon: Megaphone }, { label: "Sales Enablement", icon: Handshake }, { label: "Growth Teams", icon: Users }, { label: "Data Science", icon: Database }, { label: "Corporate Governance", icon: Landmark }, ]; const AI_CAPS: { label: string; icon: LucideIcon }[] = [ { label: "Computer Vision", icon: Eye }, { label: "Agentic AI", icon: Bot }, { label: "Enterprise AI", icon: Building2 }, { label: "Predictive Analytics", icon: ChartLine }, { label: "LLM Applications", icon: Sparkles }, { label: "AI Automation", icon: Workflow }, { label: "Multi-Agent Systems", icon: Network }, { label: "Decision Intelligence", icon: Brain }, ]; const MISSION = [ "Identify large market inefficiencies.", "Build AI-first technology companies.", "Scale them with shared resources.", "Create long-term enterprise value.", "Deliver diversified returns for investors.", "Empower entrepreneurs with world-class execution.", ]; const INVESTOR_REASONS = [ "Diversified technology portfolio", "Reduced single-company risk", "AI-first businesses", "Experienced venture-building team", "Multiple growth opportunities", "Shared engineering and operations", "Long-term enterprise value", "Repeatable creation framework", ]; // Team portraits live in public/assets, in card order: 4.jpeg, 1.png, 2.png, 3.jpg. // Add each person's name when ready (cards hide the name while it is empty). // Every card is labelled with its role and numbered 1-4 in card order. const TEAM = [ { name: "", role: "Venture Builder", img: "/assets/4.jpeg", }, { name: "", role: "Venture Builder", img: "/assets/1.png", }, { name: "", role: "Venture Builder", img: "/assets/2.png", }, { name: "", role: "Venture Builder", img: "/assets/3.jpg", }, ]; const TICKER = [...VERTICALS.map((v) => v.short), "AI-First", "Build Bold"]; /** * Scroll-reveal styles (see styles.css): * - "up": fade and rise (default) * - "mask": text wipes up into view, for headlines * - "clip": image frame opens from an inset while the photo settles, for media * - "stagger": the element's direct children rise one after another */ type RevealVariant = "up" | "mask" | "clip" | "stagger"; /** Props that opt an element into the scroll-reveal animation. */ function reveal(variant: RevealVariant = "up", delay = 0) { return { "data-reveal": variant, style: { "--reveal-delay": `${delay}ms` } as CSSProperties, }; } const REVEAL_BATCH_STEP = 90; const REVEAL_SETTLE_MS = 1700; function useScrollReveal() { useEffect(() => { if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; const timers: number[] = []; const io = new IntersectionObserver( (entries) => { // Elements that arrive together (a grid row, a fast scroll) cascade in reading order. const arriving = entries .filter((e) => e.isIntersecting) .sort( (a, b) => a.boundingClientRect.top - b.boundingClientRect.top || a.boundingClientRect.left - b.boundingClientRect.left, ); arriving.forEach((entry, i) => { const el = entry.target as HTMLElement; const batchDelay = Math.min(i, 5) * REVEAL_BATCH_STEP; el.style.setProperty("--batch-delay", `${batchDelay}ms`); el.classList.add("is-visible"); io.unobserve(el); // Once settled, drop the reveal classes so the element's own hover transitions return. const own = parseFloat(el.style.getPropertyValue("--reveal-delay")) || 0; const stagger = el.dataset.reveal === "stagger" ? el.children.length * 80 : 0; timers.push( window.setTimeout( () => el.classList.remove("reveal-pending", "is-visible"), own + batchDelay + stagger + REVEAL_SETTLE_MS, ), ); }); }, { rootMargin: "0px 0px -10% 0px" }, ); for (const el of document.querySelectorAll("[data-reveal]")) { // Leave anything already on screen alone so it never flickers after hydration. if (el.getBoundingClientRect().top < window.innerHeight) continue; if (el.dataset.reveal === "stagger") { Array.from(el.children).forEach((child, i) => (child as HTMLElement).style.setProperty("--i", String(i)), ); } el.classList.add("reveal-pending"); io.observe(el); } return () => { io.disconnect(); timers.forEach(clearTimeout); }; }, []); } function useScrolledPast(offset: number) { const [past, setPast] = useState(false); useEffect(() => { const on = () => setPast(window.scrollY > offset); on(); window.addEventListener("scroll", on, { passive: true }); return () => window.removeEventListener("scroll", on); }, [offset]); return past; } /** * True while the visitor is scrolling down past the top of the page; false as soon as they * scroll back up. Small movements are ignored so the header does not flicker. */ function useHideOnScroll(topZone = 120, tolerance = 6) { const [hidden, setHidden] = useState(false); useEffect(() => { let last = window.scrollY; let frame = 0; const update = () => { frame = 0; const y = window.scrollY; const delta = y - last; if (y < topZone) { setHidden(false); last = y; } else if (Math.abs(delta) > tolerance) { setHidden(delta > 0); last = y; } }; const onScroll = () => { if (!frame) frame = requestAnimationFrame(update); }; window.addEventListener("scroll", onScroll, { passive: true }); return () => { window.removeEventListener("scroll", onScroll); cancelAnimationFrame(frame); }; }, [topZone, tolerance]); /** Bring the header back until the next downward scroll. */ const reveal = useCallback(() => setHidden(false), []); return [hidden, reveal] as const; } const SECTION_IDS = ["top", ...NAV.map((n) => sectionOf(n.href)).filter(Boolean)]; /** Id of the section crossing the middle of the viewport, for highlighting the nav. */ function useActiveSection() { const [active, setActive] = useState("top"); useEffect(() => { const io = new IntersectionObserver( (entries) => { for (const entry of entries) if (entry.isIntersecting) setActive(entry.target.id); }, { rootMargin: "-45% 0px -50% 0px" }, ); for (const id of SECTION_IDS) { const el = document.getElementById(id); if (el) io.observe(el); } return () => io.disconnect(); }, []); return active; } /** Staggered entrance for hero content (pairs with the `rise-in` class). */ function rise(delay: number) { return { style: { "--rise-delay": `${delay}ms` } as CSSProperties }; } function Accent({ children }: { children: ReactNode }) { return {children}; } function Eyebrow({ icon: Icon, children }: { icon: LucideIcon; children: ReactNode }) { return ( {children} ); } function IconTile({ icon: Icon, className = "" }: { icon: LucideIcon; className?: string }) { return ( ); } /** Section heading: headline with an optional body line stacked beneath it. Labels are rationed. */ function SectionHeader({ eyebrow, icon, title, children, compact = false, }: { eyebrow?: string; icon?: LucideIcon; title: ReactNode; children?: ReactNode; /** Smaller heading for long titles. */ compact?: boolean; }) { return (
{eyebrow && icon ? (
{eyebrow}
) : null}

{title}

{children ? (
{children}
) : null}
); } function Nav({ page }: { page: SitePageName }) { const scrolled = useScrolledPast(20); const [scrollingDown, revealHeader] = useHideOnScroll(); const active = useActiveSection(); const [open, setOpen] = useState(false); const toggleRef = useRef(null); const overlayRef = useRef(null); // The overlay grows out of (and shrinks back into) the menu button. const setOrigin = () => { const btn = toggleRef.current; const overlay = overlayRef.current; if (!btn || !overlay) return; const r = btn.getBoundingClientRect(); overlay.style.setProperty("--menu-x", `${r.left + r.width / 2}px`); overlay.style.setProperty("--menu-y", `${r.top + r.height / 2}px`); }; const toggle = () => { setOrigin(); setOpen((o) => !o); }; useEffect(() => { if (!open) return; document.documentElement.classList.add("menu-open"); const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") { setOpen(false); toggleRef.current?.focus(); return; } if (e.key !== "Tab" || !overlayRef.current || !toggleRef.current) return; // Keep keyboard focus inside the open menu (the toggle button plus the menu's links). const items = [ toggleRef.current, ...overlayRef.current.querySelectorAll("a[href], button"), ]; const first = items[0]; const last = items[items.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } }; window.addEventListener("keydown", onKey); // Close if the viewport grows to desktop, where the menu does not exist. const desktop = window.matchMedia("(min-width: 1024px)"); const onDesktop = () => desktop.matches && setOpen(false); desktop.addEventListener("change", onDesktop); return () => { document.documentElement.classList.remove("menu-open"); window.removeEventListener("keydown", onKey); desktop.removeEventListener("change", onDesktop); }; }, [open]); // After the menu closes, keep the header (and its menu button) in view. useEffect(() => { if (!open) revealHeader(); }, [open, revealHeader]); const solid = scrolled && !open; const hidden = scrollingDown && !open; // Home highlights the section in view; other pages highlight their own link. const isCurrent = (href: string) => page === "home" ? active === sectionOf(href) : href === `/${page}`; return ( <> {/* Full-screen menu for phones and tablets: a circle that opens from the menu button. */}