updated pages

This commit is contained in:
R-Bharathraj
2026-09-29 15:24:35 +05:30
parent bb6386e34c
commit 7f49d2cd8a
55 changed files with 3308 additions and 350 deletions

View File

@@ -0,0 +1,965 @@
import { useEffect, useState, type CSSProperties, type ReactNode } from "react";
import { ArrowRight, ArrowUpRight, Check, Compass, Layers, Minus, Sparkles } from "lucide-react";
import { CONTACT_EMAIL } from "@/lib/contact";
import { SplitTitle, type TitlePart } from "@/components/site/motion";
import { IndustryIndex, MoltenMark } from "@/components/site/creative";
// Section photography (Unsplash License): see src/assets/sections/CREDITS.md.
import principleEvidence from "@/assets/sections/principle-evidence.webp";
import principleEngine from "@/assets/sections/principle-engine.webp";
import principleStandalone from "@/assets/sections/principle-standalone.webp";
import modelBuild from "@/assets/sections/model-build.webp";
import modelSpinoff from "@/assets/sections/model-spinoff.webp";
import modelCompound from "@/assets/sections/model-compound.webp";
import {
Accent,
DuoPhoto,
EASE_STAGE,
Eyebrow,
FRAMEWORK,
FillWords,
NAV,
STACK,
SectionHeader,
VERTICALS,
logoFull,
logoMark,
orbitPosition,
reveal,
rise,
type SitePageName,
} from "@/components/site/sections";
/** Position of a page in the nav, as "01".."07". */
function pageNumber(page: SitePageName) {
return String(NAV.findIndex((n) => n.href === `/${page}`) + 1).padStart(2, "0");
}
// ---------------------------------------------------------------------------------------------
// Page hero
// ---------------------------------------------------------------------------------------------
/**
* Full-height opener for inner pages: page number and label, a headline whose words rise in
* one by one, the lead paragraph and a page-specific visual. As it scrolls away the visual
* drifts and the copy lifts (hero-timeline in styles.css).
*/
function PageHero({
page,
label,
title,
intro,
visual,
children,
}: {
page: SitePageName;
label: string;
title: TitlePart[];
intro: ReactNode;
visual: ReactNode;
children?: ReactNode;
}) {
return (
<section
id="top"
className="hero-timeline relative flex min-h-[100svh] flex-col overflow-hidden bg-ink"
>
<div className="hero-media absolute inset-0">
<div className="mesh-gold absolute inset-0 opacity-25" aria-hidden />
<div className="dot-grid absolute inset-0" aria-hidden />
<div
aria-hidden
className="absolute inset-0 opacity-20 lg:left-auto lg:w-[48%] lg:opacity-100 xl:w-[46%]"
{...rise(500)}
>
<div className="rise-in h-full w-full">{visual}</div>
</div>
<div
aria-hidden
className="absolute inset-0 bg-gradient-to-r from-ink via-ink/75 to-transparent lg:via-ink/40"
/>
<div className="absolute inset-x-0 bottom-0 h-40 bg-gradient-to-t from-ink to-transparent" />
</div>
<div className="hero-copy container-x relative flex flex-1 flex-col justify-between gap-12 pb-10 pt-28 md:pb-12 md:pt-36">
<div
{...rise(100)}
className="rise-in flex items-center gap-4 font-display text-xs font-semibold uppercase tracking-[0.2em]"
>
<span data-scramble="in" className="tabular-nums text-primary">
({pageNumber(page)})
</span>
<span className="h-px w-12 bg-primary/50" aria-hidden />
<span data-scramble="in" className="text-muted-foreground">
{label}
</span>
</div>
<h1 className="max-w-[14ch] font-display text-[clamp(2.25rem,10vw,3.5rem)] md:text-[clamp(3.5rem,6.6vw,6.25rem)] font-extrabold leading-[0.93] tracking-tighter">
<SplitTitle parts={title} />
</h1>
<div className="grid items-end gap-8 md:grid-cols-12">
<div className="md:col-span-7 lg:col-span-6">
<p
{...rise(700)}
className="rise-in max-w-[52ch] text-lg leading-relaxed text-muted-foreground md:text-xl"
>
{intro}
</p>
{children ? (
<div {...rise(850)} className="rise-in mt-8">
{children}
</div>
) : null}
</div>
<a
href="#main-content"
{...rise(1000)}
className="rise-in hidden items-center gap-4 justify-self-end font-display text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground transition-colors hover:text-primary md:col-span-5 md:flex lg:col-span-6"
>
Scroll to explore
<span className="relative block h-12 w-px overflow-hidden bg-white/15" aria-hidden>
<span className="scroll-cue absolute inset-x-0 top-0 h-1/2 bg-primary" />
</span>
</a>
</div>
</div>
{/* Target for the scroll cue: the first section after the hero. */}
<span id="main-content" className="absolute bottom-0" aria-hidden />
</section>
);
}
// ---------------------------------------------------------------------------------------------
// Hero visuals (decorative)
// ---------------------------------------------------------------------------------------------
/** Place n items evenly on a circle of the given radius (% of the box), starting at 12 o'clock. */
function ring(i: number, n: number, radius: number): CSSProperties {
const a = ((-90 + (i * 360) / n) * Math.PI) / 180;
return {
left: `${(50 + radius * Math.cos(a)).toFixed(2)}%`,
top: `${(50 + radius * Math.sin(a)).toFixed(2)}%`,
};
}
/** Studio: the shared stack orbits the studio, the ventures circle closer in. */
function StudioVisual() {
return (
<div className="grid h-full place-items-center">
<div className="relative aspect-square w-[min(40rem,92%)]">
<div className="absolute inset-[3%] rounded-full border border-white/10" />
<div className="absolute inset-[23%] rounded-full border border-dashed border-primary/30" />
<div className="glow absolute inset-[20%]" />
<div className="spin-slow absolute inset-0">
{STACK.map((s, i) => (
<span
key={s.label}
style={ring(i, STACK.length, 47)}
className="absolute -translate-x-1/2 -translate-y-1/2"
>
<span className="upright block whitespace-nowrap rounded-full border border-white/10 bg-ink/80 px-3 py-1.5 font-display text-[11px] font-semibold uppercase tracking-wider text-muted-foreground backdrop-blur">
{s.label}
</span>
</span>
))}
</div>
<div className="spin-slower absolute inset-0">
{VERTICALS.map((v, i) => (
<span
key={v.n}
style={ring(i, VERTICALS.length, 27)}
className="absolute -translate-x-1/2 -translate-y-1/2"
>
<span className="upright flex items-center gap-2 whitespace-nowrap font-display text-[11px] font-bold uppercase tracking-wider text-primary">
<span className="h-2 w-2 rounded-full bg-primary shadow-[0_0_12px] shadow-primary" />
{v.short}
</span>
</span>
))}
</div>
<img
src={logoMark}
alt=""
width={201}
height={192}
className="absolute left-1/2 top-1/2 w-[18%] -translate-x-1/2 -translate-y-1/2 drop-shadow-[0_20px_40px_oklch(0.05_0.01_60/0.8)]"
/>
</div>
</div>
);
}
/** Framework: the six stages as a loop with a comet running round it. */
function FrameworkVisual() {
return (
<div className="grid h-full place-items-center">
<div className="relative aspect-square w-[min(38rem,90%)]">
<div className="absolute inset-[12.5%] rounded-full border border-white/10" />
<div
className="orbit-comet absolute inset-[12.5%] rounded-full"
style={{
background: "conic-gradient(from 0deg, transparent 0deg 250deg, var(--primary) 360deg)",
mask: "radial-gradient(farthest-side, transparent calc(100% - 3px), black calc(100% - 2px))",
}}
/>
<div className="glow absolute inset-[25%]" />
{FRAMEWORK.map((f, i) => {
const Icon = f.icon;
return (
<div
key={f.n}
style={orbitPosition(i, FRAMEWORK.length)}
className="absolute flex -translate-x-1/2 -translate-y-1/2 flex-col items-center gap-2"
>
<span className="grid h-14 w-14 place-items-center rounded-2xl border border-primary/30 bg-ink/90 text-primary shadow-lg shadow-black/40 backdrop-blur">
<Icon className="h-5 w-5" />
</span>
<span className="whitespace-nowrap font-display text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">
<span className="text-primary">{f.n}</span> {f.title}
</span>
</div>
);
})}
<div className="absolute inset-0 grid place-items-center text-center">
<div>
<div className="font-display text-7xl font-extrabold tracking-tighter text-foreground md:text-8xl">
06
</div>
<div className="mt-1 font-display text-xs font-semibold uppercase tracking-[0.2em] text-primary">
Stages, one loop
</div>
</div>
</div>
</div>
</div>
);
}
/** Portfolio: columns of vertical photos drifting past each other. */
function PortfolioVisual() {
const columns = [
{ items: VERTICALS, cls: "column-up" },
{ items: [...VERTICALS.slice(3), ...VERTICALS.slice(0, 3)], cls: "column-down" },
{ items: [...VERTICALS.slice(5), ...VERTICALS.slice(0, 5)], cls: "column-up" },
];
return (
<div className="h-full overflow-hidden">
<div className="grid h-[140%] -translate-y-[12%] rotate-[-8deg] scale-110 grid-cols-2 gap-4 px-4 sm:grid-cols-3">
{columns.map((col, c) => (
<div key={c} className={`overflow-hidden ${c === 2 ? "hidden sm:block" : ""}`}>
<div className={col.cls}>
{[...col.items, ...col.items].map((v, i) => (
<figure
key={i}
className="relative mb-4 aspect-[4/5] overflow-hidden rounded-2xl border border-white/10"
>
<img
src={v.img}
alt=""
decoding="async"
className="absolute inset-0 h-full w-full object-cover"
/>
<div className="absolute inset-x-0 bottom-0 h-1/3 bg-gradient-to-t from-ink/80 to-transparent" />
<figcaption className="absolute bottom-3 left-3 rounded-full bg-ink/70 px-3 py-1 font-display text-[11px] font-semibold uppercase tracking-wider text-primary backdrop-blur">
{v.short}
</figcaption>
</figure>
))}
</div>
</div>
))}
</div>
</div>
);
}
// Network layout for the AI visual, in a 400×400 viewBox.
const AI_LAYERS = [3, 5, 5, 3].map((count, l, all) =>
Array.from({ length: count }, (_, i) => ({
x: 50 + (l * 300) / (all.length - 1),
y: 200 + (i - (count - 1) / 2) * 62,
})),
);
const AI_EDGES = AI_LAYERS.slice(0, -1).flatMap((layer, l) =>
layer.flatMap((a) => AI_LAYERS[l + 1].map((b) => ({ a, b }))),
);
/** AI: a small neural network with signals flowing through it. */
function AIVisual() {
return (
<div className="grid h-full place-items-center">
<svg viewBox="0 0 400 400" className="w-[min(40rem,94%)] overflow-visible">
{AI_EDGES.map(({ a, b }, i) => (
<line
key={`e${i}`}
x1={a.x}
y1={a.y}
x2={b.x}
y2={b.y}
stroke="white"
strokeOpacity={0.07}
/>
))}
{AI_EDGES.map(({ a, b }, i) => (
<line
key={`s${i}`}
x1={a.x}
y1={a.y}
x2={b.x}
y2={b.y}
className="signal"
stroke="var(--primary)"
strokeWidth={1.5}
strokeLinecap="round"
style={{ "--d": `${-((i * 37) % 29) / 9}s` } as CSSProperties}
/>
))}
{AI_LAYERS.flat().map((n, i) => (
<g key={`n${i}`}>
<circle cx={n.x} cy={n.y} r={14} fill="var(--primary)" opacity={0.08} />
<circle
cx={n.x}
cy={n.y}
r={4}
fill="var(--primary)"
opacity={0.55}
className="node-pulse"
style={{ "--d": `${-((i * 13) % 11) / 4}s` } as CSSProperties}
/>
</g>
))}
</svg>
</div>
);
}
/** Investors: a field of companies, lighting up in a wave across the ecosystem. */
function InvestorsVisual() {
const cols = 11;
const rows = 11;
return (
<div className="grid h-full place-items-center">
<div
className="grid w-[min(34rem,88%)] gap-[3.2%]"
style={{ gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` }}
>
{Array.from({ length: cols * rows }, (_, i) => {
const r = Math.floor(i / cols);
const c = i % cols;
const hub = r === 5 && c === 5;
return (
<span
key={i}
className={`dot-wave aspect-square rounded-full ${
hub ? "bg-foreground" : (r * 7 + c * 3) % 5 === 0 ? "bg-primary" : "bg-primary/60"
}`}
style={{ "--d": `${((r + c) * 0.14).toFixed(2)}s` } as CSSProperties}
/>
);
})}
</div>
</div>
);
}
function AboutVisual() {
return (
<div className="grid h-full place-items-center">
<div className="relative">
<div className="glow absolute -inset-16" />
<div className="spin-slower absolute -inset-10 rounded-full border border-dashed border-primary/25" />
<MoltenMark src={logoMark} className="aspect-square w-[min(28rem,70vw)]" />
</div>
</div>
);
}
const IST = new Intl.DateTimeFormat("en-GB", {
timeZone: "Asia/Kolkata",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
});
/** Contact: the studio's local time inside a rotating ring of text. */
function ContactVisual() {
const [time, setTime] = useState<string | null>(null);
useEffect(() => {
const tick = () => setTime(IST.format(new Date()));
tick();
const id = window.setInterval(tick, 1000);
return () => window.clearInterval(id);
}, []);
return (
<div className="grid h-full place-items-center">
<div className="relative aspect-square w-[min(34rem,86%)]">
<svg viewBox="0 0 400 400" className="spin-slow absolute inset-0 overflow-visible">
<defs>
<path
id="contact-ring"
d="M200,200 m-170,0 a170,170 0 1,1 340,0 a170,170 0 1,1 -340,0"
/>
</defs>
<text
className="font-display"
fill="var(--primary)"
fontSize="17"
fontWeight="700"
letterSpacing="5"
>
<textPath href="#contact-ring" textLength="1060">
BUILD WITH US · INVEST WITH US · PARTNER WITH US ·
</textPath>
</text>
</svg>
<div className="absolute inset-[16%] rounded-full border border-white/10" />
<div className="glow absolute inset-[10%]" />
<div className="absolute inset-0 grid place-items-center text-center">
<div>
<div className="font-display text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">
Bangalore, IST
</div>
<div className="mt-3 font-display text-5xl font-extrabold tabular-nums tracking-tight md:text-7xl">
{time ?? "--:--:--"}
</div>
<div className="mt-3 inline-flex items-center gap-2 text-sm text-primary">
<span className="h-2 w-2 animate-pulse rounded-full bg-primary" />
Studio open to new partners
</div>
</div>
</div>
</div>
</div>
);
}
// ---------------------------------------------------------------------------------------------
// Heroes per page
// ---------------------------------------------------------------------------------------------
export function StudioHero() {
return (
<PageHero
page="studio"
label="The studio"
title={["One studio.", "Many companies.", { accent: "One engine." }]}
intro="We don't fund companies after they form. We find the problem, build the company and scale it on infrastructure every venture shares."
visual={<StudioVisual />}
/>
);
}
export function FrameworkHero() {
return (
<PageHero
page="framework"
label="The framework"
title={["Company building,", { accent: "made repeatable." }]}
intro="Every venture moves through the same six stages, so each new company starts with the lessons of the last."
visual={<FrameworkVisual />}
/>
);
}
export function PortfolioHero() {
return (
<PageHero
page="portfolio"
label="The portfolio"
title={["Big problems.", { accent: "Bold companies." }]}
intro="We build where problems are large, fragmented and overdue for change: seven industries, one AI engine underneath them all."
visual={<PortfolioVisual />}
/>
);
}
export function AIHero() {
return (
<PageHero
page="ai"
label="Artificial intelligence"
title={["Intelligence,", { accent: "built in from day one." }]}
intro="AI isn't bolted on later. It shapes how every KapFoundry company is designed, operated and scaled."
visual={<AIVisual />}
/>
);
}
export function InvestorsHero() {
return (
<PageHero
page="investors"
label="For investors"
title={["One investment.", { accent: "Many companies." }]}
intro="Exposure to a portfolio of AI-first ventures across industries: risk spread wide, upside shared across every company the studio builds."
visual={<InvestorsVisual />}
>
<a
href="/contact"
data-magnetic
className="press group inline-flex items-center gap-2 rounded-full bg-primary px-7 py-3.5 font-display text-sm font-semibold text-primary-foreground hover:brightness-110"
>
Speak with our team
<ArrowRight className="h-4 w-4 transition group-hover:translate-x-0.5" aria-hidden />
</a>
</PageHero>
);
}
export function AboutHero() {
return (
<PageHero
page="about"
label="About KapFoundry"
title={["We don't wait for the future.", { accent: "We build it." }]}
intro="A venture studio and technology holding company that engineers category-defining, AI-first companies, before they even exist."
visual={<AboutVisual />}
/>
);
}
export function ContactHero() {
return (
<PageHero
page="contact"
label="Contact"
title={["Let's build", { accent: "what comes next." }]}
intro="Whether you want to invest, partner or build with us, tell us a little about yourself and our team will be in touch."
visual={<ContactVisual />}
>
<a
href={`mailto:${CONTACT_EMAIL}`}
data-cursor="Write"
className="group inline-flex items-center gap-3 border-b border-primary/40 pb-2 font-display text-2xl font-bold transition-colors hover:border-primary hover:text-primary md:text-3xl"
>
{CONTACT_EMAIL}
<ArrowUpRight
className="h-6 w-6 text-primary transition-transform duration-300 group-hover:-translate-y-1 group-hover:translate-x-1"
aria-hidden
/>
</a>
</PageHero>
);
}
// ---------------------------------------------------------------------------------------------
// New content sections
// ---------------------------------------------------------------------------------------------
const VC_ROWS = [
{
topic: "When capital arrives",
vc: "After a company already exists",
kf: "Before the company exists, once research proves the problem",
},
{
topic: "Who builds",
vc: "Founders alone, starting from scratch",
kf: "Founders with a studio team behind them from day one",
},
{
topic: "Infrastructure",
vc: "Assembled again, company by company",
kf: "Engineering, design, legal, finance and growth, built once and shared",
},
{
topic: "Technology",
vc: "AI added later, if at all",
kf: "AI-first architecture from the first line of code",
},
{
topic: "Risk",
vc: "Concentrated in a single bet",
kf: "Spread across a portfolio of ventures",
},
];
/** Studio: how the studio model differs from traditional venture capital, row by row. */
export function StudioVsVC() {
return (
<section className="sheet-enter theme-light section-y bg-background text-foreground">
<div className="container-x">
<SectionHeader
eyebrow="A different model"
icon={Compass}
title={
<>
Venture capital waits. <Accent>We build.</Accent>
</>
}
>
<p>
Traditional venture capital invests in startups after they exist. KapFoundry creates
them, which changes almost everything about how a company begins.
</p>
</SectionHeader>
<div className="mt-16">
<div className="hidden grid-cols-12 gap-6 pb-4 font-display text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground lg:grid">
<span className="col-span-3" />
<span className="col-span-4">Traditional venture capital</span>
<span className="col-span-5 text-primary">KapFoundry venture studio</span>
</div>
<ol>
{VC_ROWS.map((r, i) => (
<li
key={r.topic}
{...reveal("up", i * 40)}
className="group relative grid gap-3 py-7 lg:grid-cols-12 lg:gap-6 lg:py-9"
>
<span aria-hidden className="absolute inset-x-0 top-0 h-px bg-border" />
<span
aria-hidden
className="line-draw absolute inset-x-0 top-0 h-px bg-primary/60"
/>
<h3 className="font-display text-xl font-bold lg:col-span-3 lg:text-2xl">
<span className="mr-3 text-sm tabular-nums text-primary">0{i + 1}</span>
{r.topic}
</h3>
<p className="flex gap-3 text-muted-foreground lg:col-span-4">
<Minus className="mt-1 h-4 w-4 shrink-0 opacity-60" aria-hidden />
<span>
<span className="sr-only">Traditional venture capital: </span>
{r.vc}
</span>
</p>
<p
className={`flex gap-3 font-display text-lg font-semibold transition-transform duration-500 group-hover:translate-x-1 lg:col-span-5 lg:text-xl ${EASE_STAGE}`}
>
<Check className="mt-1 h-5 w-5 shrink-0 text-primary" aria-hidden />
<span>
<span className="sr-only">KapFoundry: </span>
{r.kf}
</span>
</p>
</li>
))}
</ol>
</div>
</div>
</section>
);
}
const PRINCIPLES = [
{
t: "Evidence before capital",
img: principleEvidence,
d: "Every venture begins with market research, customer discovery and product validation, before a single rupee is deployed.",
},
{
t: "One engine, many companies",
img: principleEngine,
d: "Engineering, design, legal, finance and growth are built once and shared, so each venture launches with a mature company's stack.",
},
{
t: "Built to stand alone",
img: principleStandalone,
d: "Each venture is designed to spin off into an independent, high-growth company that strengthens the wider ecosystem.",
},
];
/** Framework: the statement and principles behind the six stages. */
export function FrameworkPrinciples() {
return (
<section className="section-y relative overflow-hidden bg-ink">
<div className="glow glow-drift absolute -left-48 top-10 h-[36rem] w-[36rem] opacity-40" />
<div className="container-x relative">
<p className="max-w-5xl font-display text-4xl font-bold leading-[1.06] tracking-tight md:text-6xl">
<FillWords text="Great ideas fail on avoidable mistakes." />
<FillWords
text="A repeatable system catches them before capital is spent."
className="text-primary"
/>
</p>
<ol className="mt-20 grid gap-6 md:grid-cols-3" {...reveal("stagger")}>
{PRINCIPLES.map((p, i) => (
<li
key={p.t}
tabIndex={0}
className="group relative overflow-hidden rounded-3xl border border-border bg-card/60 transition-colors duration-500 hover:border-primary/50"
>
<div className="relative h-56 overflow-hidden">
<DuoPhoto src={p.img} />
<div className="absolute inset-0 bg-gradient-to-t from-card via-card/20 to-transparent" />
<span className="absolute bottom-3 left-8 font-display text-6xl font-extrabold tabular-nums text-transparent transition-colors duration-500 [-webkit-text-stroke:1px_color-mix(in_oklab,var(--primary)_80%,transparent)] group-hover:text-primary">
0{i + 1}
</span>
</div>
<div className="p-8 pt-5">
<h3 className="font-display text-2xl font-bold">{p.t}</h3>
<p className="mt-3 leading-relaxed text-muted-foreground">{p.d}</p>
</div>
</li>
))}
</ol>
</div>
</section>
);
}
const VENTURES = VERTICALS.filter((v) => v.example).map((v) => {
const [name, ...rest] = (v.example ?? "").split(". ");
return { name, pitch: rest.join(". "), vertical: v.name, img: v.img, icon: v.icon };
});
/** Portfolio: ventures already taking shape in the studio. */
export function Ventures() {
return (
<section className="sheet-enter theme-light section-y bg-background text-foreground">
<div className="container-x">
<SectionHeader
eyebrow="In the studio"
icon={Sparkles}
title={
<>
Ventures <Accent>taking shape.</Accent>
</>
}
>
<p>A first look at companies being engineered on the KapFoundry engine.</p>
</SectionHeader>
<div className="mt-16 grid gap-6 md:grid-cols-2">
{VENTURES.map((v, i) => {
const Icon = v.icon;
return (
<article
key={v.name}
{...reveal("clip", i * 120)}
className="theme-dark group relative flex min-h-[30rem] flex-col justify-end overflow-hidden rounded-[2rem] bg-background p-8 text-foreground md:min-h-[36rem] md:p-10"
>
<img
src={v.img}
alt=""
loading="lazy"
decoding="async"
className={`absolute inset-0 h-full w-full object-cover transition-transform duration-[1.6s] group-hover:scale-105 ${EASE_STAGE}`}
/>
<div className="absolute inset-0 bg-gradient-to-t from-ink via-ink/70 to-ink/10" />
<div className="relative">
<span className="inline-flex items-center gap-2 rounded-full bg-primary px-3 py-1.5 font-display text-[11px] font-semibold uppercase tracking-wider text-primary-foreground">
<Icon className="h-3.5 w-3.5" aria-hidden />
{v.vertical}
</span>
<h3 className="mt-6 font-display text-4xl font-extrabold tracking-tight lg:text-6xl">
{v.name}
</h3>
<p className="mt-4 max-w-[40ch] text-lg leading-relaxed text-muted-foreground">
{v.pitch}
</p>
</div>
</article>
);
})}
</div>
<a
href="/contact"
data-cursor="Talk"
{...reveal("up", 150)}
className="group mt-6 flex flex-col items-start justify-between gap-6 rounded-[2rem] border border-dashed border-foreground/25 p-8 transition-colors duration-500 hover:border-primary hover:bg-primary/5 md:flex-row md:items-center md:p-10"
>
<span>
<span className="block font-display text-xs font-semibold uppercase tracking-[0.16em] text-primary">
Next in the studio
</span>
<span className="mt-3 block font-display text-3xl font-bold tracking-tight md:text-4xl">
Build the next one with us.
</span>
</span>
<span className="grid h-16 w-16 shrink-0 place-items-center rounded-full bg-primary text-primary-foreground transition-transform duration-500 group-hover:rotate-45">
<ArrowUpRight className="h-6 w-6" aria-hidden />
</span>
</a>
</div>
</section>
);
}
/** AI: what the foundation does inside each vertical; the row at the centre of the screen is lit. */
export function AIAcrossVerticals() {
return (
<section className="section-y relative bg-ink">
<div className="container-x grid gap-12 lg:grid-cols-12 lg:gap-16">
<div className="lg:col-span-5">
<div className="lg:sticky lg:top-32" {...reveal("stagger")}>
<Eyebrow icon={Layers}>AI in every vertical</Eyebrow>
<h2 className="mt-6 font-display text-4xl font-bold leading-[1.04] md:text-5xl">
One foundation. <Accent>Seven industries.</Accent>
</h2>
<p className="mt-6 max-w-[48ch] text-lg leading-relaxed text-muted-foreground">
The same AI capabilities show up differently in every market we enter. Here is where
they do the heavy lifting.
</p>
</div>
</div>
<ol className="lg:col-span-7">
{VERTICALS.map((v) => {
const Icon = v.icon;
return (
<li
key={v.n}
className="row-focus border-t border-white/10 py-8 last:border-b md:py-10"
>
<div className="flex items-center gap-4">
<span className="relative h-14 w-14 shrink-0 overflow-hidden rounded-2xl md:h-16 md:w-16">
<img
src={v.img}
alt=""
loading="lazy"
decoding="async"
className="h-full w-full object-cover"
/>
<span className="absolute bottom-1 right-1 grid h-6 w-6 place-items-center rounded-lg bg-ink/80 text-primary backdrop-blur">
<Icon className="h-3.5 w-3.5" aria-hidden />
</span>
</span>
<h3 className="font-display text-2xl font-bold md:text-4xl">{v.name}</h3>
<span className="ml-auto font-display text-sm tabular-nums text-primary">
{v.n}
</span>
</div>
<ul className="mt-5 flex flex-wrap gap-2 md:pl-20">
{v.solution.slice(0, 3).map((s) => (
<li
key={s}
className="rounded-full border border-white/10 bg-white/[0.04] px-4 py-2 text-sm text-foreground"
>
{s}
</li>
))}
</ul>
</li>
);
})}
</ol>
</div>
</section>
);
}
const MODEL = [
{
t: "The studio builds",
img: modelBuild,
d: "Ventures are researched, validated and built on KapFoundry's shared engine.",
},
{
t: "Ventures spin off",
img: modelSpinoff,
d: "Each company grows into an independent, high-growth business in its own market.",
},
{
t: "Value compounds",
img: modelCompound,
d: "Every success strengthens the ecosystem, and investors share in the portfolio, not a single bet.",
},
];
/** Investors: how value flows from the studio to the portfolio. */
export function InvestorModel() {
return (
<section className="sheet-enter theme-light section-y bg-background text-foreground">
<div className="container-x">
<SectionHeader
title={
<>
How value <Accent>compounds.</Accent>
</>
}
>
<p>A holding company at the centre, many independent companies around it.</p>
</SectionHeader>
<div className="relative mt-20">
<ol className="grid gap-12 md:grid-cols-3 md:gap-10" {...reveal("stagger")}>
{MODEL.map((m, i) => (
<li key={m.t} tabIndex={0} className="group relative">
<div className="theme-dark relative aspect-[4/3] overflow-hidden rounded-3xl bg-ink">
<DuoPhoto src={m.img} />
</div>
<span className="relative -mt-7 ml-6 grid h-14 w-14 place-items-center rounded-full bg-primary font-display text-lg font-bold tabular-nums text-primary-foreground shadow-lg shadow-primary/30 ring-4 ring-background">
0{i + 1}
</span>
<h3 className="mt-8 font-display text-3xl font-bold">{m.t}</h3>
<p className="mt-3 max-w-[36ch] text-lg leading-relaxed text-muted-foreground">
{m.d}
</p>
</li>
))}
</ol>
</div>
</div>
</section>
);
}
// ---------------------------------------------------------------------------------------------
// Next page handoff
// ---------------------------------------------------------------------------------------------
const NEXT_BLURB: Record<string, string> = {
"/studio": "How one shared engine builds many companies.",
"/framework": "Six stages from a billion-dollar problem to an independent company.",
"/portfolio": "The industries we're rebuilding, AI-first.",
"/ai": "The foundation under every venture we build.",
"/investors": "Exposure to a portfolio, not a single bet.",
"/about": "Our vision, our mission and the people behind them.",
"/contact": "Build with us, invest with us, or simply say hello.",
};
/** Giant link to the next page in nav order; grows in as it enters and fills gold on hover. */
export function NextPage({ page }: { page: SitePageName }) {
const i = NAV.findIndex((n) => n.href === `/${page}`);
const k = (i + 1) % NAV.length;
const next = NAV[k];
return (
<section
aria-label="Next page"
className="relative overflow-hidden border-t border-border bg-ink"
>
<div className="glow glow-drift absolute -right-40 top-0 h-[34rem] w-[34rem] opacity-30" />
<a href={next.href} data-cursor="Next" className="group relative block py-24 md:py-36">
<div className="container-x">
<div className="flex items-center justify-between font-display text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">
<span data-scramble>Next page</span>
<span className="tabular-nums">
<span className="text-primary">{String(k + 1).padStart(2, "0")}</span> /{" "}
{String(NAV.length).padStart(2, "0")}
</span>
</div>
<div className="next-grow mt-8 flex flex-col items-start gap-6 md:flex-row md:items-center md:justify-between">
<span className="next-fill font-display text-[clamp(2.5rem,15vw,12rem)] md:text-[min(12vw,12rem)] font-extrabold leading-[0.9] tracking-tighter">
{next.label}
</span>
<span
className={`grid h-16 w-16 shrink-0 place-items-center rounded-full border border-primary/40 text-primary transition-all duration-700 group-hover:bg-primary group-hover:text-primary-foreground md:h-28 md:w-28 ${EASE_STAGE}`}
>
<ArrowUpRight
className={`h-7 w-7 transition-transform duration-700 group-hover:rotate-45 md:h-10 md:w-10 ${EASE_STAGE}`}
aria-hidden
/>
</span>
</div>
<div className="mt-10 h-px bg-white/10">
<div className="line-draw h-full bg-primary/70" />
</div>
<p className="mt-6 max-w-md text-lg text-muted-foreground">{NEXT_BLURB[next.href]}</p>
</div>
</a>
</section>
);
}
/** Portfolio: the seven industries as a typographic index with floating photos. */
export function PortfolioIndex() {
return (
<IndustryIndex
items={VERTICALS.map((v) => ({ n: v.n, name: v.short, tag: v.tag, img: v.img }))}
/>
);
}