updated pages
This commit is contained in:
965
src/components/site/pages.tsx
Normal file
965
src/components/site/pages.tsx
Normal 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 }))}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user