launch PodMan award-winning one-page site
@@ -3,67 +3,62 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="theme-color" content="#0b0f17" />
|
||||
<meta name="theme-color" content="#fbfaf7" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<title>PodMan — the teammate that sees what git can’t</title>
|
||||
<title>PodMan by Karti Tripathi & team — AI teammate for fast-moving engineers</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="PodMan is an ambient AI teammate that watches your pod's screens in realtime and catches merge collisions before anyone pushes. Top 3 at the AI Engineer World's Fair 2026 Hackathon — built by Karti Tripathi, Ramis Hasanli, Yahya Alhinai, and Shakthi Bachala."
|
||||
content="PodMan is the award-winning real-time AI teammate built by Karti Tripathi, Ramis Hasanli, Yahya Alhinai, and Shakthi Bachala. It sees work before git does and learns which team nudges help."
|
||||
/>
|
||||
<meta name="author" content="Karti Tripathi, Ramis Hasanli, Yahya Alhinai, Shakthi Bachala" />
|
||||
<link rel="canonical" href="https://podman.live/" />
|
||||
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="PodMan" />
|
||||
<meta property="og:url" content="https://www.podman.live/" />
|
||||
<meta property="og:title" content="PodMan — the teammate that sees what git can’t" />
|
||||
<meta property="og:url" content="https://podman.live/" />
|
||||
<meta property="og:title" content="PodMan — Your team is moving fast. Stay in sync." />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Ambient AI teammate that watches your pod's screens in realtime and catches merge collisions before anyone pushes."
|
||||
content="The $35K+ award-winning AI teammate by Karti Tripathi and team. 2nd overall and LiveKit track winner at AI Engineer World's Fair 2026."
|
||||
/>
|
||||
<meta property="og:image" content="https://www.podman.live/og.png" />
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="PodMan — the teammate that sees what git can’t" />
|
||||
<meta name="twitter:title" content="PodMan — See the collision before the commit" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="Ambient AI teammate that catches merge collisions before anyone pushes."
|
||||
content="Built by Karti Tripathi and team. $35K+ in prizes, 2nd overall, and LiveKit track winner."
|
||||
/>
|
||||
<meta name="twitter:image" content="https://www.podman.live/og.png" />
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "PodMan",
|
||||
"url": "https://podman.live",
|
||||
"applicationCategory": "DeveloperApplication",
|
||||
"operatingSystem": "Web",
|
||||
"description": "An ambient AI teammate that watches your pod's screens in realtime and catches merge collisions, duplicated work, and missed handoffs before anyone pushes.",
|
||||
"award": "Top 3 — AI Engineer World's Fair 2026 Hackathon (2nd overall of 95+ teams, LiveKit track winner, DigitalOcean Best Use)",
|
||||
"author": [
|
||||
"@graph": [
|
||||
{
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "PodMan",
|
||||
"url": "https://podman.live/",
|
||||
"image": "https://podman.live/og.png",
|
||||
"applicationCategory": "DeveloperApplication",
|
||||
"operatingSystem": "Web",
|
||||
"description": "A real-time AI voice teammate that sees engineering work before git does and learns which coordination nudges help.",
|
||||
"award": ["2nd Overall — AI Engineer World's Fair 2026", "LiveKit Track Winner", "DigitalOcean Best Use"],
|
||||
"author": [
|
||||
{ "@id": "https://karti.ai/#karti-tripathi" },
|
||||
{ "@type": "Person", "name": "Ramis Hasanli", "sameAs": "https://www.linkedin.com/in/hasanliramis/" },
|
||||
{ "@type": "Person", "name": "Yahya Alhinai", "sameAs": "https://www.linkedin.com/in/yhinai/" },
|
||||
{ "@type": "Person", "name": "Shakthi Bachala", "sameAs": "https://www.linkedin.com/in/shakthi-bachala-09401510a/" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"@type": "Person",
|
||||
"@id": "https://karti.ai/#karti-tripathi",
|
||||
"name": "Karti Tripathi",
|
||||
"url": "https://karti.ai",
|
||||
"sameAs": "https://www.linkedin.com/in/kartitripathi/"
|
||||
},
|
||||
{
|
||||
"@type": "Person",
|
||||
"name": "Ramis Hasanli",
|
||||
"sameAs": "https://www.linkedin.com/in/hasanliramis/"
|
||||
},
|
||||
{
|
||||
"@type": "Person",
|
||||
"name": "Yahya Alhinai",
|
||||
"sameAs": "https://www.linkedin.com/in/yhinai/"
|
||||
},
|
||||
{
|
||||
"@type": "Person",
|
||||
"name": "Shakthi Bachala",
|
||||
"sameAs": "https://www.linkedin.com/in/shakthi-bachala-09401510a/"
|
||||
"url": "https://karti.ai/",
|
||||
"sameAs": ["https://www.linkedin.com/in/kartitripathi/", "https://podman.live/"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 203 KiB |
|
After Width: | Height: | Size: 199 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 535 KiB |
@@ -1,16 +1,18 @@
|
||||
<svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="1200" height="630" fill="#0b0f17" />
|
||||
<g fill="none" stroke="#34d399" opacity="0.12">
|
||||
<circle cx="1050" cy="140" r="70" stroke-width="6" />
|
||||
<circle cx="1050" cy="140" r="140" stroke-width="5" />
|
||||
<circle cx="1050" cy="140" r="215" stroke-width="4" />
|
||||
</g>
|
||||
<rect x="90" y="150" width="104" height="104" rx="24" fill="#0f1629" stroke="#1f2a3a" stroke-width="2" />
|
||||
<circle cx="176" cy="170" r="8.5" fill="#34d399" />
|
||||
<text x="142" y="228" text-anchor="middle" font-family="Helvetica, Arial, sans-serif" font-weight="bold" font-size="66" fill="#34d399">P</text>
|
||||
<text x="222" y="232" font-family="Helvetica, Arial, sans-serif" font-weight="bold" font-size="68" fill="#e7eaf0">PodMan</text>
|
||||
<text x="92" y="356" font-family="Helvetica, Arial, sans-serif" font-weight="bold" font-size="46" fill="#e7eaf0">The teammate that sees what git can’t.</text>
|
||||
<text x="92" y="416" font-family="Helvetica, Arial, sans-serif" font-size="34" fill="#9aa4b2">Catches merge collisions before anyone pushes.</text>
|
||||
<rect x="92" y="500" width="13" height="13" rx="3" fill="#34d399" />
|
||||
<text x="118" y="512" font-family="Helvetica, Arial, sans-serif" font-weight="bold" font-size="30" fill="#34d399">podman.live</text>
|
||||
<defs>
|
||||
<clipPath id="photo"><rect x="650" y="0" width="550" height="630"/></clipPath>
|
||||
</defs>
|
||||
<rect width="1200" height="630" fill="#fbfaf7"/>
|
||||
<image href="/media/team-podman.jpeg" x="650" y="0" width="946" height="630" preserveAspectRatio="xMidYMid slice" clip-path="url(#photo)"/>
|
||||
<rect x="642" y="0" width="8" height="630" fill="#174de8"/>
|
||||
<path d="M45 72h14l8-26 11 52 11-46 11 46 10-52 10 52 10-26h30" fill="none" stroke="#f13a19" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<text x="185" y="89" font-family="Arial, Helvetica, sans-serif" font-weight="800" font-size="58" letter-spacing="-3" fill="#f13a19">PodMan</text>
|
||||
<text x="48" y="205" font-family="Arial, Helvetica, sans-serif" font-weight="800" font-size="66" letter-spacing="-4" fill="#111111">Your team is</text>
|
||||
<text x="48" y="275" font-family="Arial, Helvetica, sans-serif" font-weight="800" font-size="66" letter-spacing="-4" fill="#111111">moving fast.</text>
|
||||
<text x="48" y="345" font-family="Arial, Helvetica, sans-serif" font-weight="800" font-size="66" letter-spacing="-4" fill="#f13a19">Stay in sync.</text>
|
||||
<text x="50" y="410" font-family="Courier New, monospace" font-size="24" fill="#111111">A real-time AI teammate that sees</text>
|
||||
<text x="50" y="443" font-family="Courier New, monospace" font-size="24" fill="#111111">work before git does.</text>
|
||||
<rect x="0" y="520" width="650" height="110" fill="#f13a19"/>
|
||||
<text x="48" y="567" font-family="Arial, Helvetica, sans-serif" font-weight="800" font-size="30" fill="#ffffff">$35K+ IN PRIZES · 2ND OVERALL</text>
|
||||
<text x="48" y="603" font-family="Arial, Helvetica, sans-serif" font-weight="700" font-size="23" fill="#111111">LIVEKIT TRACK WINNER · podman.live</text>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,4 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://podman.live/sitemap.xml
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://podman.live/</loc>
|
||||
<lastmod>2026-08-13</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
@@ -0,0 +1,37 @@
|
||||
import { ClerkProvider } from '@clerk/react';
|
||||
import { shadcn } from '@clerk/ui/themes';
|
||||
import App from './App.js';
|
||||
import { TooltipProvider } from '@/components/ui/tooltip';
|
||||
import '@clerk/ui/themes/shadcn.css';
|
||||
|
||||
const clerkPublishableKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
|
||||
const hasClerk = typeof clerkPublishableKey === 'string' && clerkPublishableKey.startsWith('pk_');
|
||||
|
||||
export default function AppRoute() {
|
||||
if (!hasClerk) {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center gap-3 bg-[#0b0f17] px-6 text-center text-white/80">
|
||||
<img src="/podman-logo.svg" alt="PodMan" className="size-10" />
|
||||
<h1 className="text-xl font-semibold text-white">PodMan is deploying</h1>
|
||||
<p className="max-w-sm text-sm text-white/50">
|
||||
The live app is being provisioned. Check back shortly.
|
||||
</p>
|
||||
<a href="/" className="mt-2 text-sm text-cyan-300 hover:underline">
|
||||
← Back to home
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ClerkProvider
|
||||
publishableKey={clerkPublishableKey}
|
||||
appearance={{ theme: shadcn }}
|
||||
afterSignOutUrl="/"
|
||||
>
|
||||
<TooltipProvider>
|
||||
<App />
|
||||
</TooltipProvider>
|
||||
</ClerkProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,23 +1,10 @@
|
||||
import { useCallback } from 'react';
|
||||
import {
|
||||
TrophyIcon,
|
||||
ChevronDownIcon,
|
||||
EyeIcon,
|
||||
GitMergeIcon,
|
||||
UsersIcon,
|
||||
BellIcon,
|
||||
RadioIcon,
|
||||
DatabaseIcon,
|
||||
SparklesIcon,
|
||||
PlayIcon,
|
||||
MicIcon,
|
||||
} from 'lucide-react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { ArrowDownIcon, ArrowRightIcon, PlayIcon } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
/**
|
||||
* Public marketing landing page for podman.live.
|
||||
* Rendered at "/" without ClerkProvider — needs no secrets.
|
||||
* The app (private beta) lives at "/app".
|
||||
*/
|
||||
const DEMO_URL = 'https://www.youtube.com/watch?v=bWJIsIWTgr0';
|
||||
const LIVEKIT_POST_URL =
|
||||
'https://www.linkedin.com/posts/codestackr_we-spent-the-weekend-at-the-ai-engineer-worlds-activity-7477213698709745664-sN3Q';
|
||||
|
||||
const TEAM = [
|
||||
{ name: 'Karti Tripathi', url: 'https://www.linkedin.com/in/kartitripathi/' },
|
||||
@@ -26,282 +13,216 @@ const TEAM = [
|
||||
{ name: 'Shakthi Bachala', url: 'https://www.linkedin.com/in/shakthi-bachala-09401510a/' },
|
||||
];
|
||||
|
||||
const LOOP = [
|
||||
['01', 'See', 'Shared screens and room activity, in real time.'],
|
||||
['02', 'Understand', 'Intent, context, ownership, and potential overlap.'],
|
||||
['03', 'Nudge', 'A useful voice prompt before the work collides.'],
|
||||
['04', 'Learn', 'Accepted and dismissed nudges sharpen the next one.'],
|
||||
];
|
||||
|
||||
export function Landing() {
|
||||
const scrollToStory = useCallback(() => {
|
||||
document.getElementById('story')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const root = rootRef.current;
|
||||
if (!root || !('IntersectionObserver' in window)) return;
|
||||
const items = root.querySelectorAll<HTMLElement>('[data-reveal]');
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
(entry.target as HTMLElement).dataset.visible = 'true';
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ rootMargin: '0px 0px -10% 0px', threshold: 0.12 },
|
||||
);
|
||||
items.forEach((item) => observer.observe(item));
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen scroll-smooth bg-[#0b0f17] text-white/90 antialiased">
|
||||
{/* ambient glows */}
|
||||
<div className="pointer-events-none fixed inset-0 overflow-hidden">
|
||||
<div className="absolute -top-40 left-1/2 h-[38rem] w-[38rem] -translate-x-1/2 rounded-full bg-indigo-600/20 blur-[120px]" />
|
||||
<div className="absolute top-1/3 -right-32 h-[26rem] w-[26rem] rounded-full bg-cyan-500/10 blur-[110px]" />
|
||||
</div>
|
||||
<div ref={rootRef} className="pod-site">
|
||||
<a className="skip-link" href="#main">Skip to content</a>
|
||||
|
||||
{/* top nav */}
|
||||
<header className="relative z-20 mx-auto flex max-w-6xl items-center justify-between px-6 py-5">
|
||||
<a href="#top" className="flex items-center gap-2.5">
|
||||
<img src="/podman-logo.svg" alt="PodMan" className="h-7 w-7" />
|
||||
<span className="text-[15px] font-semibold tracking-tight text-white">PodMan</span>
|
||||
<header className="pod-nav">
|
||||
<a className="pod-brand" href="#top" aria-label="PodMan home">
|
||||
<SignalMark />
|
||||
<span>PodMan</span>
|
||||
</a>
|
||||
<span className="inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/5 px-4 py-1.5 text-sm font-medium text-white/70">
|
||||
<LiveDot />
|
||||
Beta soon
|
||||
</span>
|
||||
<nav className="pod-nav-links" aria-label="Main navigation">
|
||||
<a href="#story">Story</a>
|
||||
<a href="#loop">How it learns</a>
|
||||
<a href="#team">Team</a>
|
||||
</nav>
|
||||
<Button asChild variant="brand" size="lg">
|
||||
<a href={DEMO_URL} target="_blank" rel="noreferrer">
|
||||
Watch the demo <ArrowRightIcon data-icon="inline-end" />
|
||||
</a>
|
||||
</Button>
|
||||
</header>
|
||||
|
||||
{/* hero */}
|
||||
<section id="top" className="relative z-10 mx-auto flex max-w-3xl flex-col items-center px-6 pt-16 pb-24 text-center sm:pt-24">
|
||||
<button
|
||||
onClick={scrollToStory}
|
||||
className="group mb-8 inline-flex items-center gap-2 rounded-full border border-amber-300/30 bg-gradient-to-r from-amber-400/15 to-amber-200/5 px-4 py-1.5 text-sm font-medium text-amber-200 transition-colors hover:border-amber-300/50"
|
||||
>
|
||||
<TrophyIcon className="size-4" />
|
||||
Top 3 — AI Engineer World's Fair Hackathon 2026
|
||||
<ChevronDownIcon className="size-4 transition-transform group-hover:translate-y-0.5" />
|
||||
</button>
|
||||
|
||||
<h1 className="text-5xl font-semibold leading-[1.05] tracking-tight text-white sm:text-7xl">
|
||||
PodMan
|
||||
</h1>
|
||||
<p className="mt-5 bg-gradient-to-r from-indigo-300 via-sky-300 to-cyan-200 bg-clip-text text-2xl font-medium text-transparent sm:text-3xl">
|
||||
The teammate that sees what git can't.
|
||||
</p>
|
||||
<p className="mx-auto mt-6 max-w-xl text-base leading-relaxed text-white/60 sm:text-lg">
|
||||
An ambient AI teammate that watches your pod's screens in realtime — catching merge
|
||||
collisions, duplicated work, and missed handoffs before anyone pushes.
|
||||
</p>
|
||||
|
||||
{/* award chips */}
|
||||
<div className="mt-9 flex flex-wrap items-center justify-center gap-2.5">
|
||||
{[
|
||||
{ label: '2nd Overall · 95+ teams', accent: 'text-amber-200 border-amber-300/25 bg-amber-400/10' },
|
||||
{ label: 'LiveKit Track — Winner', accent: 'text-sky-200 border-sky-300/25 bg-sky-400/10' },
|
||||
{ label: 'DigitalOcean — Best Use', accent: 'text-cyan-200 border-cyan-300/25 bg-cyan-400/10' },
|
||||
].map((c) => (
|
||||
<span
|
||||
key={c.label}
|
||||
className={`rounded-full border px-3.5 py-1.5 text-[13px] font-medium ${c.accent}`}
|
||||
>
|
||||
{c.label}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* CTAs */}
|
||||
<div className="mt-10 flex flex-col items-center gap-3 sm:flex-row">
|
||||
<button
|
||||
onClick={scrollToStory}
|
||||
className="inline-flex h-11 items-center justify-center gap-2 rounded-full bg-gradient-to-r from-indigo-500 to-cyan-400 px-7 text-sm font-semibold text-[#0b0f17] shadow-lg shadow-indigo-500/20 transition-transform hover:scale-[1.02]"
|
||||
>
|
||||
Read the story <ChevronDownIcon className="size-4" />
|
||||
</button>
|
||||
<BetaPill />
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={scrollToStory}
|
||||
aria-label="Scroll to read"
|
||||
className="mt-20 animate-bounce text-white/40 transition-colors hover:text-white/70"
|
||||
>
|
||||
<ChevronDownIcon className="size-7" />
|
||||
</button>
|
||||
</section>
|
||||
|
||||
{/* story */}
|
||||
<Section id="story" eyebrow="How it happened" title="Built on stage, in about 24 hours.">
|
||||
<div className="space-y-5 text-[15px] leading-relaxed text-white/70">
|
||||
<p>
|
||||
PodMan was built at the{' '}
|
||||
<span className="text-white">AI Engineer World's Fair 2026 Hackathon</span> (June
|
||||
27–28) against 95+ teams. It took{' '}
|
||||
<span className="text-amber-200">2nd place overall</span>, and won both the{' '}
|
||||
<span className="text-sky-200">LiveKit</span> track and{' '}
|
||||
<span className="text-cyan-200">DigitalOcean's Best Use</span> award.
|
||||
</p>
|
||||
<p>
|
||||
We shipped the live demo in a single sprint — a LiveKit room, Gemini watching the
|
||||
screen-share, and a voice that speaks up the moment two engineers drift toward the same
|
||||
code. There wasn't time on the clock to give it a proper home.
|
||||
</p>
|
||||
<p className="text-white/90">
|
||||
So this is that home — and we're actively building PodMan out from the hackathon
|
||||
prototype into something a team can actually run every day.
|
||||
</p>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* what it does */}
|
||||
<Section id="what" eyebrow="What it does" title="A teammate in the room, not another dashboard.">
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<Feature
|
||||
icon={<EyeIcon className="size-5" />}
|
||||
title="Ambient awareness"
|
||||
body="Joins your pod's LiveKit room and samples every screen-share in realtime — no bots to invoke, no commands to remember."
|
||||
/>
|
||||
<Feature
|
||||
icon={<GitMergeIcon className="size-5" />}
|
||||
title="Merge-collision radar"
|
||||
body="Recognizes when two people are converging on the same files or feature and warns them before the conflict ever reaches a push."
|
||||
/>
|
||||
<Feature
|
||||
icon={<UsersIcon className="size-5" />}
|
||||
title="Duplicated-work detection"
|
||||
body="Notices when effort overlaps across the team and surfaces it early, so two engineers don't quietly build the same thing."
|
||||
/>
|
||||
<Feature
|
||||
icon={<BellIcon className="size-5" />}
|
||||
title="Handoff nudges"
|
||||
body="Speaks up about missed handoffs and stalled threads — and learns which nudges actually helped, so it stays useful, not noisy."
|
||||
/>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* how it works */}
|
||||
<Section id="how" eyebrow="Under the hood" title="Realtime vision, memory, and a voice.">
|
||||
<div className="grid gap-4 sm:grid-cols-3">
|
||||
<Stack icon={<RadioIcon className="size-5" />} name="LiveKit" note="Realtime transport for the shared room and voice delivery." />
|
||||
<Stack icon={<SparklesIcon className="size-5" />} name="Gemini" note="Vision + event detection + the live voice that nudges the team." />
|
||||
<Stack icon={<DatabaseIcon className="size-5" />} name="MongoDB" note="Engineer state, ownership map, events, and which nudges landed." />
|
||||
</div>
|
||||
<div className="mt-6 flex flex-wrap gap-2 text-[13px] text-white/50">
|
||||
{['LiveKit', 'Gemini Live', 'MongoDB Atlas', 'Voyage embeddings', 'Modular MAX', 'Hermes ops'].map((t) => (
|
||||
<span key={t} className="rounded-md border border-white/10 bg-white/[0.03] px-2.5 py-1">
|
||||
{t}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* demo */}
|
||||
<Section id="demo" eyebrow="See it" title="The 60-second demo.">
|
||||
<div className="overflow-hidden rounded-2xl border border-white/10 bg-black/40 shadow-2xl">
|
||||
<div className="relative w-full" style={{ paddingBottom: '56.25%' }}>
|
||||
<iframe
|
||||
className="absolute inset-0 h-full w-full"
|
||||
src="https://www.youtube-nocookie.com/embed/bWJIsIWTgr0"
|
||||
title="PodMan demo"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 flex items-center gap-2 text-sm text-white/40">
|
||||
<PlayIcon className="size-4" /> Watch the pod get its warning before the collision.
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* built by / team */}
|
||||
<Section id="team" eyebrow="Built by" title="The team behind PodMan.">
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{TEAM.map((p) => (
|
||||
<a
|
||||
key={p.name}
|
||||
href={p.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="rounded-full border border-white/10 bg-white/[0.03] px-4 py-2 text-[15px] font-medium text-white/80 transition-colors hover:border-cyan-300/40 hover:text-white"
|
||||
>
|
||||
{p.name}
|
||||
<main id="main">
|
||||
<section id="top" className="pod-hero">
|
||||
<div className="pod-hero-copy" data-reveal>
|
||||
<h1>
|
||||
Your team is moving fast. <span>PodMan keeps everyone in sync.</span>
|
||||
</h1>
|
||||
<p className="pod-deck">
|
||||
A real-time voice teammate that sees work before git does—then learns which nudges
|
||||
actually help.
|
||||
</p>
|
||||
<div className="pod-proof" aria-label="Awards">
|
||||
<strong>$35K+ in prizes</strong>
|
||||
<span>2nd overall</span>
|
||||
<span>LiveKit track winner</span>
|
||||
</div>
|
||||
<a className="pod-scroll" href="#story">
|
||||
Read the story <ArrowDownIcon />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* final CTA — beta coming soon */}
|
||||
<section className="relative z-10 mx-auto max-w-3xl px-6 py-24 text-center">
|
||||
<div className="rounded-3xl border border-white/10 bg-gradient-to-b from-white/[0.06] to-transparent px-8 py-14">
|
||||
<MicIcon className="mx-auto size-8 text-cyan-300" />
|
||||
<h2 className="mt-5 text-3xl font-semibold tracking-tight text-white sm:text-4xl">
|
||||
The private beta is coming soon.
|
||||
</h2>
|
||||
<p className="mx-auto mt-4 max-w-md text-[15px] leading-relaxed text-white/60">
|
||||
We're building PodMan out from the hackathon prototype into something a team runs
|
||||
every day. The private beta opens soon.
|
||||
</p>
|
||||
<div className="mt-8 flex justify-center">
|
||||
<BetaPill className="px-6 py-3 text-[15px]" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<figure className="pod-hero-media" data-reveal>
|
||||
<img
|
||||
src="/media/team-podman.jpeg"
|
||||
alt="PodMan builders Shakthi Bachala, Karti Tripathi, Yahya Alhinai, and Ramis Hasanli at the AI Engineer World's Fair"
|
||||
width="1536"
|
||||
height="1024"
|
||||
fetchPriority="high"
|
||||
/>
|
||||
<figcaption>San Francisco · June 2026</figcaption>
|
||||
<SignalThread />
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
{/* footer */}
|
||||
<footer className="relative z-10 border-t border-white/10">
|
||||
<div className="mx-auto flex max-w-6xl flex-col items-center justify-between gap-3 px-6 py-8 text-sm text-white/40 sm:flex-row">
|
||||
<div className="flex items-center gap-2">
|
||||
<img src="/podman-logo.svg" alt="" className="h-5 w-5 opacity-70" />
|
||||
<span>PodMan · podman.live</span>
|
||||
<section id="story" className="pod-story pod-section">
|
||||
<div className="pod-section-index" data-reveal>01 — THE PROBLEM</div>
|
||||
<div className="pod-story-grid">
|
||||
<div data-reveal>
|
||||
<h2>Git sees the commit. PodMan sees the <em>collision coming.</em></h2>
|
||||
<p>
|
||||
AI-powered teams can ship in parallel faster than they can stay aligned. PodMan
|
||||
joins the LiveKit room, understands what each engineer is doing from shared
|
||||
screens, and speaks before two workstreams quietly become the same workstream.
|
||||
</p>
|
||||
</div>
|
||||
<figure className="pod-product-frame" data-reveal>
|
||||
<img
|
||||
src="/media/collision-nudge.jpeg"
|
||||
alt="PodMan collision alert shown during the live demo"
|
||||
loading="lazy"
|
||||
/>
|
||||
<figcaption>Live demo: a collision caught before the commit.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<span>Built at AI Engineer World's Fair 2026 · 2nd Overall</span>
|
||||
</section>
|
||||
|
||||
<section id="loop" className="pod-loop pod-section">
|
||||
<div className="pod-section-index" data-reveal>02 — THE RECURSIVE LOOP</div>
|
||||
<div className="pod-loop-heading" data-reveal>
|
||||
<h2>A teammate that gets quieter—and smarter—with use.</h2>
|
||||
<p>
|
||||
Every observation, intervention, accept, and dismiss becomes memory. The system
|
||||
improves its next decision instead of repeating the same interruption.
|
||||
</p>
|
||||
</div>
|
||||
<ol className="pod-loop-rail" data-reveal>
|
||||
{LOOP.map(([number, title, body]) => (
|
||||
<li key={number}>
|
||||
<span>{number}</span>
|
||||
<h3>{title}</h3>
|
||||
<p>{body}</p>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
<div className="pod-return-line" aria-hidden="true">
|
||||
<span>outcome becomes memory</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="pod-win" aria-labelledby="win-title">
|
||||
<div className="pod-win-copy" data-reveal>
|
||||
<p>AI Engineer World's Fair 2026 · San Francisco</p>
|
||||
<h2 id="win-title">Built in one weekend. Awarded on three stages.</h2>
|
||||
</div>
|
||||
<div className="pod-prize" data-reveal>
|
||||
<strong>$35K+</strong>
|
||||
<span>in prizes and partner awards</span>
|
||||
</div>
|
||||
<dl className="pod-stats" data-reveal>
|
||||
<div><dt>2nd</dt><dd>overall</dd></div>
|
||||
<div><dt>95+</dt><dd>teams</dd></div>
|
||||
<div><dt>LiveKit</dt><dd>track winner</dd></div>
|
||||
<div><dt>DigitalOcean</dt><dd>Best Use</dd></div>
|
||||
</dl>
|
||||
<div className="pod-win-photos" data-reveal>
|
||||
<img src="/media/livekit-winners.jpeg" alt="PodMan team with the LiveKit hackathon track finalists" loading="lazy" />
|
||||
<img src="/media/demo-stage.jpeg" alt="The PodMan team presenting the live recursive learning system" loading="lazy" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="team" className="pod-team pod-section">
|
||||
<div className="pod-team-copy" data-reveal>
|
||||
<div className="pod-section-index">03 — BUILT BY</div>
|
||||
<h2>Four builders. One very awake teammate.</h2>
|
||||
<p>
|
||||
PodMan was created by Karti Tripathi, Ramis Hasanli, Yahya Alhinai, and Shakthi
|
||||
Bachala in roughly 24 hours—and presented live in San Francisco.
|
||||
</p>
|
||||
<figure>
|
||||
<img src="/media/karti-presenting.jpeg" alt="Karti Tripathi presenting PodMan on stage" loading="lazy" />
|
||||
<figcaption>Karti Tripathi presenting PodMan</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<ul className="pod-team-list" data-reveal>
|
||||
{TEAM.map((member, index) => (
|
||||
<li key={member.name}>
|
||||
<span>0{index + 1}</span>
|
||||
<a href={member.url} target="_blank" rel="noreferrer">
|
||||
<strong>{member.name}</strong>
|
||||
<span>LinkedIn ↗</span>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className="pod-cta" data-reveal>
|
||||
<h2>See PodMan catch the collision before the commit.</h2>
|
||||
<Button asChild variant="inverse" size="lg">
|
||||
<a href={DEMO_URL} target="_blank" rel="noreferrer">
|
||||
<PlayIcon data-icon="inline-start" /> Watch the 60-second demo
|
||||
</a>
|
||||
</Button>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer className="pod-footer">
|
||||
<p>PodMan · San Francisco · 2026</p>
|
||||
<div>
|
||||
<a href="https://github.com/karti-ai" target="_blank" rel="noreferrer">
|
||||
GitHub ↗
|
||||
</a>
|
||||
<a href={LIVEKIT_POST_URL} target="_blank" rel="noreferrer">LiveKit post ↗</a>
|
||||
<a href="https://www.linkedin.com/in/kartitripathi/" target="_blank" rel="noreferrer">Karti Tripathi ↗</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LiveDot() {
|
||||
function SignalMark() {
|
||||
return (
|
||||
<span className="relative flex size-1.5">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-cyan-400/70" />
|
||||
<span className="relative inline-flex size-1.5 rounded-full bg-cyan-400" />
|
||||
</span>
|
||||
<svg viewBox="0 0 62 28" aria-hidden="true">
|
||||
<path d="M1 14h8l4-10 6 20 6-18 6 18 5-20 5 20 5-10h15" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function BetaPill({ className = '' }: { className?: string }) {
|
||||
function SignalThread() {
|
||||
return (
|
||||
<span
|
||||
className={`inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/5 px-5 py-2.5 text-sm font-medium text-white/70 ${className}`}
|
||||
>
|
||||
<LiveDot />
|
||||
Private beta — coming soon
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function Section({
|
||||
id,
|
||||
eyebrow,
|
||||
title,
|
||||
children,
|
||||
}: {
|
||||
id: string;
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section id={id} className="relative z-10 border-t border-white/[0.06] py-20">
|
||||
<div className="mx-auto max-w-3xl px-6">
|
||||
<p className="text-sm font-medium tracking-wide text-cyan-300/80 uppercase">{eyebrow}</p>
|
||||
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-white sm:text-4xl">{title}</h2>
|
||||
<div className="mt-8">{children}</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function Feature({ icon, title, body }: { icon: React.ReactNode; title: string; body: string }) {
|
||||
return (
|
||||
<div className="rounded-2xl border border-white/10 bg-white/[0.03] p-6 transition-colors hover:border-white/20">
|
||||
<div className="flex size-10 items-center justify-center rounded-xl bg-gradient-to-br from-indigo-500/20 to-cyan-400/20 text-cyan-200">
|
||||
{icon}
|
||||
</div>
|
||||
<h3 className="mt-4 text-lg font-semibold text-white">{title}</h3>
|
||||
<p className="mt-2 text-[14px] leading-relaxed text-white/60">{body}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Stack({ icon, name, note }: { icon: React.ReactNode; name: string; note: string }) {
|
||||
return (
|
||||
<div className="rounded-2xl border border-white/10 bg-white/[0.03] p-6">
|
||||
<div className="flex items-center gap-2.5 text-white">
|
||||
<span className="text-cyan-200">{icon}</span>
|
||||
<span className="text-base font-semibold">{name}</span>
|
||||
</div>
|
||||
<p className="mt-3 text-[14px] leading-relaxed text-white/60">{note}</p>
|
||||
</div>
|
||||
<svg className="pod-signal-thread" viewBox="0 0 700 160" aria-hidden="true">
|
||||
<path d="M2 82c82 0 92-70 170-70s97 136 189 136 116-101 196-101 85 35 141 35" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ const buttonVariants = cva(
|
||||
destructive:
|
||||
'bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
brand:
|
||||
'rounded-none border-[var(--pod-orange)] bg-[var(--pod-orange)] px-5 text-white hover:bg-[var(--pod-orange-dark)]',
|
||||
inverse:
|
||||
'rounded-none border-white bg-white px-5 text-[var(--pod-ink)] hover:bg-white/90',
|
||||
},
|
||||
size: {
|
||||
default:
|
||||
|
||||
@@ -94,6 +94,207 @@ input:focus-visible {
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
/* PodMan editorial landing page */
|
||||
.pod-site {
|
||||
--pod-paper: #fbfaf7;
|
||||
--pod-ink: #111111;
|
||||
--pod-orange: #f13a19;
|
||||
--pod-orange-dark: #cc2d11;
|
||||
--pod-blue: #174de8;
|
||||
min-height: 100vh;
|
||||
overflow: clip;
|
||||
background: var(--pod-paper);
|
||||
color: var(--pod-ink);
|
||||
}
|
||||
|
||||
.pod-site * { box-sizing: border-box; }
|
||||
.pod-site a { color: inherit; text-decoration: none; }
|
||||
.pod-site img { display: block; max-width: 100%; }
|
||||
.pod-site svg { display: block; }
|
||||
|
||||
.skip-link {
|
||||
position: fixed;
|
||||
top: 0.75rem;
|
||||
left: 0.75rem;
|
||||
z-index: 100;
|
||||
transform: translateY(-180%);
|
||||
background: var(--pod-ink);
|
||||
color: white !important;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
.skip-link:focus { transform: translateY(0); }
|
||||
|
||||
.pod-nav {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
padding: 1.5rem clamp(1.25rem, 4vw, 4.5rem);
|
||||
}
|
||||
.pod-brand { display: inline-flex; align-items: center; gap: 0.7rem; width: max-content; font-size: 1.65rem; font-weight: 780; letter-spacing: -0.05em; color: var(--pod-orange) !important; }
|
||||
.pod-brand svg { width: 2.8rem; fill: none; stroke: currentColor; stroke-width: 2.7; stroke-linecap: round; stroke-linejoin: round; }
|
||||
.pod-nav-links { display: flex; align-items: center; gap: clamp(1.5rem, 4vw, 4rem); font-size: 0.9rem; font-weight: 650; }
|
||||
.pod-nav-links a { border-bottom: 1px solid transparent; }
|
||||
.pod-nav-links a:hover { border-color: currentColor; }
|
||||
.pod-nav > [data-slot='button'] { justify-self: end; height: 3rem; }
|
||||
|
||||
.pod-hero {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.25fr);
|
||||
gap: clamp(2rem, 5vw, 6rem);
|
||||
max-width: 1600px;
|
||||
min-height: min(820px, calc(100svh - 96px));
|
||||
margin: 0 auto;
|
||||
padding: clamp(3.5rem, 8vh, 7rem) clamp(1.25rem, 4vw, 4.5rem) 5rem;
|
||||
align-items: center;
|
||||
}
|
||||
.pod-hero-copy { position: relative; z-index: 2; }
|
||||
.pod-hero h1, .pod-section h2, .pod-win h2, .pod-cta h2 { margin: 0; font-weight: 780; letter-spacing: -0.065em; text-wrap: balance; }
|
||||
.pod-hero h1 { max-width: 13ch; font-size: clamp(3.6rem, 6.3vw, 7.5rem); line-height: 0.9; }
|
||||
.pod-hero h1 span { color: var(--pod-orange); }
|
||||
.pod-deck { max-width: 34rem; margin: 2rem 0 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: clamp(1rem, 1.3vw, 1.2rem); line-height: 1.55; }
|
||||
.pod-proof { display: flex; flex-wrap: wrap; gap: 0.55rem 1.1rem; align-items: baseline; margin-top: 2rem; color: var(--pod-orange); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.92rem; }
|
||||
.pod-proof strong { font-size: 1.05rem; }
|
||||
.pod-proof span::before { content: '·'; margin-right: 1rem; }
|
||||
.pod-scroll { display: inline-flex; align-items: center; gap: 0.55rem; margin-top: 3rem; border-bottom: 1px solid currentColor; padding-bottom: 0.25rem; font-size: 0.85rem; font-weight: 700; }
|
||||
.pod-scroll svg { width: 1rem; }
|
||||
.pod-hero-media { position: relative; margin: 0; align-self: stretch; min-height: 520px; }
|
||||
.pod-hero-media::before { content: ''; position: absolute; inset: -1.25rem 1.25rem 1.25rem -1.25rem; border-top: 2px solid var(--pod-orange); border-left: 2px solid var(--pod-orange); pointer-events: none; }
|
||||
.pod-hero-media img { width: 100%; height: 100%; object-fit: cover; object-position: center; }
|
||||
.pod-hero-media figcaption { position: absolute; right: 0; bottom: 0; background: var(--pod-blue); color: white; padding: 0.7rem 1rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.72rem; }
|
||||
.pod-signal-thread { position: absolute; left: -34%; bottom: -5.4rem; width: 83%; overflow: visible; fill: none; stroke: var(--pod-blue); stroke-width: 2; }
|
||||
.pod-signal-thread path { stroke-dasharray: 1200; stroke-dashoffset: 1200; animation: pod-draw 2.2s 0.5s cubic-bezier(.2,.7,.2,1) forwards; }
|
||||
|
||||
.pod-section { max-width: 1600px; margin: 0 auto; padding: clamp(5rem, 10vw, 10rem) clamp(1.25rem, 6vw, 7rem); }
|
||||
.pod-section-index { margin-bottom: 2.5rem; color: var(--pod-orange); font-size: 0.78rem; font-weight: 760; letter-spacing: 0.08em; }
|
||||
.pod-story { border-top: 1px solid #d8d6d0; }
|
||||
.pod-story-grid { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(420px, .7fr); gap: clamp(3rem, 8vw, 9rem); align-items: end; }
|
||||
.pod-story h2 { max-width: 12ch; font-size: clamp(3.3rem, 6.5vw, 7.8rem); line-height: 0.92; }
|
||||
.pod-story h2 em { color: var(--pod-orange); font-style: normal; }
|
||||
.pod-story-grid > div > p, .pod-loop-heading > p, .pod-team-copy > p { max-width: 42rem; margin: 2.4rem 0 0; font-size: clamp(1rem, 1.3vw, 1.2rem); line-height: 1.65; }
|
||||
.pod-product-frame { margin: 0; border: 1px solid var(--pod-ink); background: var(--pod-ink); }
|
||||
.pod-product-frame img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
|
||||
.pod-product-frame figcaption { color: white; padding: 0.85rem 1rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.72rem; }
|
||||
|
||||
.pod-loop { border-top: 1px solid #d8d6d0; }
|
||||
.pod-loop-heading { display: grid; grid-template-columns: 1.3fr .7fr; gap: 3rem; align-items: end; }
|
||||
.pod-loop-heading h2 { max-width: 13ch; font-size: clamp(3rem, 5.5vw, 6.6rem); line-height: 0.94; }
|
||||
.pod-loop-heading > p { margin-bottom: 0.5rem; }
|
||||
.pod-loop-rail { position: relative; display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; margin: clamp(4rem, 8vw, 8rem) 0 0; padding: 0; list-style: none; }
|
||||
.pod-loop-rail::before { content: ''; position: absolute; top: 3.8rem; left: 1rem; right: 1rem; height: 1px; background: var(--pod-ink); }
|
||||
.pod-loop-rail li { position: relative; padding-right: clamp(1.5rem, 4vw, 5rem); }
|
||||
.pod-loop-rail li:not(:last-child)::after { content: '→'; position: absolute; top: 2.8rem; right: 1.5rem; background: var(--pod-paper); padding: 0 0.5rem; font-size: 1.8rem; }
|
||||
.pod-loop-rail li > span { color: var(--pod-blue); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.85rem; font-weight: 700; }
|
||||
.pod-loop-rail h3 { position: relative; width: max-content; margin: 1.2rem 0 3.3rem; background: var(--pod-paper); padding-right: 1rem; font-size: clamp(1.6rem, 2.6vw, 3rem); letter-spacing: -0.05em; }
|
||||
.pod-loop-rail p { max-width: 14rem; margin: 0; font-size: 0.9rem; line-height: 1.55; }
|
||||
.pod-return-line { height: 4rem; margin: 3rem 5% 0; border-right: 2px solid var(--pod-blue); border-bottom: 2px solid var(--pod-blue); border-left: 2px solid var(--pod-blue); border-radius: 0 0 1rem 1rem; color: var(--pod-blue); text-align: center; }
|
||||
.pod-return-line span { position: relative; top: 3rem; background: var(--pod-paper); padding: 0 1rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.72rem; }
|
||||
|
||||
.pod-win { display: grid; grid-template-columns: 1.12fr .88fr; background: var(--pod-paper); border-top: 1px solid var(--pod-ink); }
|
||||
.pod-win-copy { padding: clamp(4rem, 7vw, 8rem) clamp(1.25rem, 6vw, 7rem); }
|
||||
.pod-win-copy p { margin: 0 0 1.2rem; color: var(--pod-orange); font-weight: 720; }
|
||||
.pod-win h2 { max-width: 12ch; font-size: clamp(3.2rem, 6vw, 7rem); line-height: 0.94; }
|
||||
.pod-prize { display: flex; flex-direction: column; justify-content: center; background: var(--pod-orange); color: white; padding: 3rem clamp(1.5rem, 5vw, 6rem); }
|
||||
.pod-prize strong { font-size: clamp(6rem, 14vw, 14rem); line-height: 0.75; letter-spacing: -0.09em; }
|
||||
.pod-prize span { margin-top: 2rem; color: var(--pod-ink); font-size: clamp(1.15rem, 2vw, 2rem); font-weight: 720; }
|
||||
.pod-stats { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(4, 1fr); margin: 0; border-top: 1px solid var(--pod-ink); border-bottom: 1px solid var(--pod-ink); }
|
||||
.pod-stats div { padding: 2.3rem clamp(1.25rem, 4vw, 4rem); }
|
||||
.pod-stats div:not(:last-child) { border-right: 1px solid var(--pod-ink); }
|
||||
.pod-stats dt { color: var(--pod-blue); font-size: clamp(2.1rem, 4vw, 5rem); font-weight: 780; letter-spacing: -0.07em; }
|
||||
.pod-stats dd { margin: 0.3rem 0 0; font-weight: 700; }
|
||||
.pod-win-photos { grid-column: 1 / -1; display: grid; grid-template-columns: 1fr 1fr; background: var(--pod-blue); gap: 3px; padding: 3px; }
|
||||
.pod-win-photos img { width: 100%; height: min(44vw, 620px); object-fit: cover; }
|
||||
|
||||
.pod-team { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(360px, .8fr); gap: clamp(4rem, 10vw, 11rem); }
|
||||
.pod-team h2 { max-width: 10ch; font-size: clamp(3.8rem, 7vw, 8rem); line-height: 0.9; }
|
||||
.pod-team-copy figure { position: relative; margin: 4rem 0 0; }
|
||||
.pod-team-copy figure::before { content: ''; position: absolute; inset: -0.8rem auto auto -0.8rem; width: 36%; height: 36%; border-top: 2px solid var(--pod-blue); border-left: 2px solid var(--pod-blue); }
|
||||
.pod-team-copy figure img { width: 100%; max-height: 620px; object-fit: cover; object-position: center 30%; }
|
||||
.pod-team-copy figcaption { margin-top: 0.65rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.72rem; }
|
||||
.pod-team-list { align-self: center; margin: 0; padding: 0; list-style: none; }
|
||||
.pod-team-list li { display: grid; grid-template-columns: 2rem 1fr; gap: 1rem; border-top: 1px solid var(--pod-ink); padding: 1.8rem 0; }
|
||||
.pod-team-list li:last-child { border-bottom: 1px solid var(--pod-ink); }
|
||||
.pod-team-list li > span { color: var(--pod-orange); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.75rem; }
|
||||
.pod-team-list a { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; }
|
||||
.pod-team-list strong { font-size: clamp(1.7rem, 3vw, 3.6rem); line-height: 0.95; letter-spacing: -0.05em; }
|
||||
.pod-team-list a > span { display: inline-flex; align-items: center; gap: 0.35rem; color: var(--pod-blue); font-size: 0.78rem; font-weight: 700; }
|
||||
.pod-team-list svg { width: 0.9rem; }
|
||||
.pod-team-list a:hover strong { color: var(--pod-orange); }
|
||||
|
||||
.pod-cta { display: flex; align-items: center; justify-content: space-between; gap: 3rem; background: var(--pod-orange); color: white; padding: clamp(3.5rem, 7vw, 7rem) clamp(1.25rem, 6vw, 7rem); }
|
||||
.pod-cta h2 { max-width: 16ch; font-size: clamp(2.8rem, 5.8vw, 6.5rem); line-height: 0.93; }
|
||||
.pod-cta [data-slot='button'] { min-height: 3.5rem; flex: none; }
|
||||
.pod-cta [data-variant='inverse'] { color: var(--pod-ink); }
|
||||
.pod-footer { display: flex; justify-content: space-between; gap: 2rem; padding: 2rem clamp(1.25rem, 4vw, 4.5rem); font-size: 0.78rem; }
|
||||
.pod-footer p { margin: 0; }
|
||||
.pod-footer div { display: flex; flex-wrap: wrap; gap: 1.5rem; }
|
||||
.pod-footer a { display: inline-flex; align-items: center; gap: 0.35rem; }
|
||||
.pod-footer svg { width: 0.9rem; }
|
||||
|
||||
[data-reveal] { opacity: 0; transform: translateY(2.5rem); transition: opacity .8s ease, transform .8s cubic-bezier(.2,.75,.2,1); }
|
||||
[data-reveal][data-visible='true'] { opacity: 1; transform: translateY(0); }
|
||||
|
||||
@keyframes pod-draw { to { stroke-dashoffset: 0; } }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.pod-nav { grid-template-columns: 1fr auto; }
|
||||
.pod-nav-links { display: none; }
|
||||
.pod-hero { grid-template-columns: 1fr; min-height: 0; padding-top: 3.5rem; }
|
||||
.pod-hero h1 { max-width: 12ch; }
|
||||
.pod-hero-media { min-height: 60vw; }
|
||||
.pod-signal-thread { left: 8%; bottom: -4.5rem; width: 84%; }
|
||||
.pod-story-grid, .pod-loop-heading, .pod-team { grid-template-columns: 1fr; }
|
||||
.pod-story-grid { gap: 3rem; }
|
||||
.pod-story h2, .pod-loop-heading h2, .pod-team h2 { max-width: 13ch; }
|
||||
.pod-loop-rail { grid-template-columns: 1fr 1fr; gap: 3.5rem 2rem; }
|
||||
.pod-loop-rail::before, .pod-loop-rail li::after, .pod-return-line { display: none; }
|
||||
.pod-loop-rail h3 { margin-bottom: 0.8rem; }
|
||||
.pod-win { grid-template-columns: 1fr; }
|
||||
.pod-prize { min-height: 300px; }
|
||||
.pod-stats { grid-template-columns: 1fr 1fr; }
|
||||
.pod-stats div:nth-child(2) { border-right: 0; }
|
||||
.pod-stats div:nth-child(-n+2) { border-bottom: 1px solid var(--pod-ink); }
|
||||
.pod-team { gap: 4rem; }
|
||||
.pod-team-list { align-self: start; }
|
||||
.pod-cta { align-items: flex-start; flex-direction: column; }
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.pod-nav { padding-block: 1rem; }
|
||||
.pod-brand { font-size: 1.35rem; }
|
||||
.pod-brand svg { width: 2.2rem; }
|
||||
.pod-nav > [data-slot='button'] { height: 2.5rem; font-size: 0.76rem; }
|
||||
.pod-hero { padding-top: 2.6rem; padding-bottom: 4rem; gap: 3.2rem; }
|
||||
.pod-hero h1 { font-size: clamp(3.25rem, 15.5vw, 5rem); }
|
||||
.pod-hero-media { min-height: 78vw; }
|
||||
.pod-proof { align-items: flex-start; flex-direction: column; }
|
||||
.pod-proof span::before { content: ''; margin: 0; }
|
||||
.pod-section { padding-top: 5.5rem; padding-bottom: 5.5rem; }
|
||||
.pod-story h2, .pod-loop-heading h2, .pod-team h2 { font-size: clamp(3rem, 14vw, 4.7rem); }
|
||||
.pod-story-grid { grid-template-columns: minmax(0, 1fr); }
|
||||
.pod-loop-rail { grid-template-columns: 1fr; gap: 2.6rem; }
|
||||
.pod-prize strong { font-size: 29vw; }
|
||||
.pod-stats { grid-template-columns: 1fr 1fr; }
|
||||
.pod-stats div { padding: 1.5rem 1.25rem; }
|
||||
.pod-stats dt { font-size: 2rem; }
|
||||
.pod-win-photos { grid-template-columns: 1fr; }
|
||||
.pod-win-photos img { height: 70vw; }
|
||||
.pod-team-list strong { font-size: 1.75rem; }
|
||||
.pod-team-list a { align-items: flex-start; flex-direction: column; }
|
||||
.pod-cta { padding-block: 4rem; }
|
||||
.pod-footer { flex-direction: column; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html { scroll-behavior: auto !important; }
|
||||
[data-reveal] { opacity: 1; transform: none; transition: none; }
|
||||
.pod-signal-thread path { animation: none; stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--font-heading: var(--font-sans);
|
||||
--font-sans: 'Geist Variable', sans-serif;
|
||||
|
||||
@@ -1,52 +1,24 @@
|
||||
import { StrictMode } from 'react';
|
||||
import { lazy, StrictMode, Suspense } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
||||
import { ClerkProvider } from '@clerk/react';
|
||||
import { shadcn } from '@clerk/ui/themes';
|
||||
import App from './App.js';
|
||||
import { Landing } from './Landing.js';
|
||||
import './index.css';
|
||||
import '@clerk/ui/themes/shadcn.css';
|
||||
import { TooltipProvider } from '@/components/ui/tooltip';
|
||||
|
||||
const clerkPublishableKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
|
||||
const hasClerk = typeof clerkPublishableKey === 'string' && clerkPublishableKey.startsWith('pk_');
|
||||
|
||||
function AppRoute() {
|
||||
if (!hasClerk) {
|
||||
// Secrets not provisioned yet — show a graceful placeholder instead of crashing.
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center gap-3 bg-[#0b0f17] px-6 text-center text-white/80">
|
||||
<img src="/podman-logo.svg" alt="PodMan" className="h-10 w-10" />
|
||||
<h1 className="text-xl font-semibold text-white">PodMan is deploying</h1>
|
||||
<p className="max-w-sm text-sm text-white/50">
|
||||
The live app is being provisioned. Check back shortly.
|
||||
</p>
|
||||
<a href="/" className="mt-2 text-sm text-cyan-300 hover:underline">
|
||||
← Back to home
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<ClerkProvider
|
||||
publishableKey={clerkPublishableKey}
|
||||
appearance={{ theme: shadcn }}
|
||||
afterSignOutUrl="/"
|
||||
>
|
||||
<TooltipProvider>
|
||||
<App />
|
||||
</TooltipProvider>
|
||||
</ClerkProvider>
|
||||
);
|
||||
}
|
||||
const AppRoute = lazy(() => import('./AppRoute.js'));
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Landing />} />
|
||||
<Route path="/app/*" element={<AppRoute />} />
|
||||
<Route
|
||||
path="/app/*"
|
||||
element={
|
||||
<Suspense fallback={<div className="min-h-screen bg-[#0b0f17]" />}>
|
||||
<AppRoute />
|
||||
</Suspense>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
</StrictMode>,
|
||||
|
||||