launch PodMan award-winning one-page site

This commit is contained in:
karti-ai
2026-08-13 04:44:06 -07:00
parent 82e5378658
commit f22a17b9b0
16 changed files with 503 additions and 358 deletions
+28 -33
View File
@@ -3,67 +3,62 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <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" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>PodMan — the teammate that sees what git cant</title> <title>PodMan by Karti Tripathi & team — AI teammate for fast-moving engineers</title>
<meta <meta
name="description" 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" /> <meta name="author" content="Karti Tripathi, Ramis Hasanli, Yahya Alhinai, Shakthi Bachala" />
<link rel="canonical" href="https://podman.live/" /> <link rel="canonical" href="https://podman.live/" />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:site_name" content="PodMan" /> <meta property="og:site_name" content="PodMan" />
<meta property="og:url" content="https://www.podman.live/" /> <meta property="og:url" content="https://podman.live/" />
<meta property="og:title" content="PodMan — the teammate that sees what git cant" /> <meta property="og:title" content="PodMan — Your team is moving fast. Stay in sync." />
<meta <meta
property="og:description" 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" content="https://www.podman.live/og.png" />
<meta property="og:image:width" content="1200" /> <meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" /> <meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="PodMan — the teammate that sees what git cant" /> <meta name="twitter:title" content="PodMan — See the collision before the commit" />
<meta <meta
name="twitter:description" 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" /> <meta name="twitter:image" content="https://www.podman.live/og.png" />
<script type="application/ld+json"> <script type="application/ld+json">
{ {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "SoftwareApplication", "@graph": [
"name": "PodMan", {
"url": "https://podman.live", "@type": "SoftwareApplication",
"applicationCategory": "DeveloperApplication", "name": "PodMan",
"operatingSystem": "Web", "url": "https://podman.live/",
"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.", "image": "https://podman.live/og.png",
"award": "Top 3 — AI Engineer World's Fair 2026 Hackathon (2nd overall of 95+ teams, LiveKit track winner, DigitalOcean Best Use)", "applicationCategory": "DeveloperApplication",
"author": [ "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", "@type": "Person",
"@id": "https://karti.ai/#karti-tripathi",
"name": "Karti Tripathi", "name": "Karti Tripathi",
"url": "https://karti.ai", "url": "https://karti.ai/",
"sameAs": "https://www.linkedin.com/in/kartitripathi/" "sameAs": ["https://www.linkedin.com/in/kartitripathi/", "https://podman.live/"]
},
{
"@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/"
} }
] ]
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 535 KiB

+16 -14
View File
@@ -1,16 +1,18 @@
<svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg"> <svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
<rect width="1200" height="630" fill="#0b0f17" /> <defs>
<g fill="none" stroke="#34d399" opacity="0.12"> <clipPath id="photo"><rect x="650" y="0" width="550" height="630"/></clipPath>
<circle cx="1050" cy="140" r="70" stroke-width="6" /> </defs>
<circle cx="1050" cy="140" r="140" stroke-width="5" /> <rect width="1200" height="630" fill="#fbfaf7"/>
<circle cx="1050" cy="140" r="215" stroke-width="4" /> <image href="/media/team-podman.jpeg" x="650" y="0" width="946" height="630" preserveAspectRatio="xMidYMid slice" clip-path="url(#photo)"/>
</g> <rect x="642" y="0" width="8" height="630" fill="#174de8"/>
<rect x="90" y="150" width="104" height="104" rx="24" fill="#0f1629" stroke="#1f2a3a" stroke-width="2" /> <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"/>
<circle cx="176" cy="170" r="8.5" fill="#34d399" /> <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="142" y="228" text-anchor="middle" font-family="Helvetica, Arial, sans-serif" font-weight="bold" font-size="66" fill="#34d399">P</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="222" y="232" font-family="Helvetica, Arial, sans-serif" font-weight="bold" font-size="68" fill="#e7eaf0">PodMan</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="92" y="356" font-family="Helvetica, Arial, sans-serif" font-weight="bold" font-size="46" fill="#e7eaf0">The teammate that sees what git can&#8217;t.</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="92" y="416" font-family="Helvetica, Arial, sans-serif" font-size="34" fill="#9aa4b2">Catches merge collisions before anyone pushes.</text> <text x="50" y="410" font-family="Courier New, monospace" font-size="24" fill="#111111">A real-time AI teammate that sees</text>
<rect x="92" y="500" width="13" height="13" rx="3" fill="#34d399" /> <text x="50" y="443" font-family="Courier New, monospace" font-size="24" fill="#111111">work before git does.</text>
<text x="118" y="512" font-family="Helvetica, Arial, sans-serif" font-weight="bold" font-size="30" fill="#34d399">podman.live</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> </svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

+4
View File
@@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://podman.live/sitemap.xml
+9
View File
@@ -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>
+37
View File
@@ -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>
);
}
+194 -273
View File
@@ -1,23 +1,10 @@
import { useCallback } from 'react'; import { useEffect, useRef } from 'react';
import { import { ArrowDownIcon, ArrowRightIcon, PlayIcon } from 'lucide-react';
TrophyIcon, import { Button } from '@/components/ui/button';
ChevronDownIcon,
EyeIcon,
GitMergeIcon,
UsersIcon,
BellIcon,
RadioIcon,
DatabaseIcon,
SparklesIcon,
PlayIcon,
MicIcon,
} from 'lucide-react';
/** const DEMO_URL = 'https://www.youtube.com/watch?v=bWJIsIWTgr0';
* Public marketing landing page for podman.live. const LIVEKIT_POST_URL =
* Rendered at "/" without ClerkProvider — needs no secrets. 'https://www.linkedin.com/posts/codestackr_we-spent-the-weekend-at-the-ai-engineer-worlds-activity-7477213698709745664-sN3Q';
* The app (private beta) lives at "/app".
*/
const TEAM = [ const TEAM = [
{ name: 'Karti Tripathi', url: 'https://www.linkedin.com/in/kartitripathi/' }, { 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/' }, { 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() { export function Landing() {
const scrollToStory = useCallback(() => { const rootRef = useRef<HTMLDivElement>(null);
document.getElementById('story')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
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 ( return (
<div className="min-h-screen scroll-smooth bg-[#0b0f17] text-white/90 antialiased"> <div ref={rootRef} className="pod-site">
{/* ambient glows */} <a className="skip-link" href="#main">Skip to content</a>
<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>
{/* top nav */} <header className="pod-nav">
<header className="relative z-20 mx-auto flex max-w-6xl items-center justify-between px-6 py-5"> <a className="pod-brand" href="#top" aria-label="PodMan home">
<a href="#top" className="flex items-center gap-2.5"> <SignalMark />
<img src="/podman-logo.svg" alt="PodMan" className="h-7 w-7" /> <span>PodMan</span>
<span className="text-[15px] font-semibold tracking-tight text-white">PodMan</span>
</a> </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"> <nav className="pod-nav-links" aria-label="Main navigation">
<LiveDot /> <a href="#story">Story</a>
Beta soon <a href="#loop">How it learns</a>
</span> <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> </header>
{/* hero */} <main id="main">
<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"> <section id="top" className="pod-hero">
<button <div className="pod-hero-copy" data-reveal>
onClick={scrollToStory} <h1>
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" Your team is moving fast. <span>PodMan keeps everyone in sync.</span>
> </h1>
<TrophyIcon className="size-4" /> <p className="pod-deck">
Top 3 AI Engineer World&apos;s Fair Hackathon 2026 A real-time voice teammate that sees work before git doesthen learns which nudges
<ChevronDownIcon className="size-4 transition-transform group-hover:translate-y-0.5" /> actually help.
</button> </p>
<div className="pod-proof" aria-label="Awards">
<h1 className="text-5xl font-semibold leading-[1.05] tracking-tight text-white sm:text-7xl"> <strong>$35K+ in prizes</strong>
PodMan <span>2nd overall</span>
</h1> <span>LiveKit track winner</span>
<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"> </div>
The teammate that sees what git can&apos;t. <a className="pod-scroll" href="#story">
</p> Read the story <ArrowDownIcon />
<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&apos;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&apos;s Fair 2026 Hackathon</span> (June
2728) 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&apos;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&apos;t time on the clock to give it a proper home.
</p>
<p className="text-white/90">
So this is that home and we&apos;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}
</a> </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&apos;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>
</div> <figure className="pod-hero-media" data-reveal>
</section> <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 */} <section id="story" className="pod-story pod-section">
<footer className="relative z-10 border-t border-white/10"> <div className="pod-section-index" data-reveal>01 THE PROBLEM</div>
<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="pod-story-grid">
<div className="flex items-center gap-2"> <div data-reveal>
<img src="/podman-logo.svg" alt="" className="h-5 w-5 opacity-70" /> <h2>Git sees the commit. PodMan sees the <em>collision coming.</em></h2>
<span>PodMan · podman.live</span> <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> </div>
<span>Built at AI Engineer World&apos;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 quieterand smarterwith 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&apos;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 hoursand 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> </div>
</footer> </footer>
</div> </div>
); );
} }
function LiveDot() { function SignalMark() {
return ( return (
<span className="relative flex size-1.5"> <svg viewBox="0 0 62 28" aria-hidden="true">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-cyan-400/70" /> <path d="M1 14h8l4-10 6 20 6-18 6 18 5-20 5 20 5-10h15" />
<span className="relative inline-flex size-1.5 rounded-full bg-cyan-400" /> </svg>
</span>
); );
} }
function BetaPill({ className = '' }: { className?: string }) { function SignalThread() {
return ( return (
<span <svg className="pod-signal-thread" viewBox="0 0 700 160" aria-hidden="true">
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}`} <path d="M2 82c82 0 92-70 170-70s97 136 189 136 116-101 196-101 85 35 141 35" />
> </svg>
<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>
); );
} }
+4
View File
@@ -19,6 +19,10 @@ const buttonVariants = cva(
destructive: 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', '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', 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: { size: {
default: default:
+201
View File
@@ -94,6 +94,207 @@ input:focus-visible {
background-clip: padding-box; 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 { @theme inline {
--font-heading: var(--font-sans); --font-heading: var(--font-sans);
--font-sans: 'Geist Variable', sans-serif; --font-sans: 'Geist Variable', sans-serif;
+10 -38
View File
@@ -1,52 +1,24 @@
import { StrictMode } from 'react'; import { lazy, StrictMode, Suspense } from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
import { BrowserRouter, Routes, Route } from 'react-router-dom'; 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 { Landing } from './Landing.js';
import './index.css'; 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 AppRoute = lazy(() => import('./AppRoute.js'));
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>
);
}
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
<StrictMode> <StrictMode>
<BrowserRouter> <BrowserRouter>
<Routes> <Routes>
<Route path="/" element={<Landing />} /> <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> </Routes>
</BrowserRouter> </BrowserRouter>
</StrictMode>, </StrictMode>,