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
+23 -28
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",
"@graph": [
{
"@type": "SoftwareApplication", "@type": "SoftwareApplication",
"name": "PodMan", "name": "PodMan",
"url": "https://podman.live", "url": "https://podman.live/",
"image": "https://podman.live/og.png",
"applicationCategory": "DeveloperApplication", "applicationCategory": "DeveloperApplication",
"operatingSystem": "Web", "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.", "description": "A real-time AI voice teammate that sees engineering work before git does and learns which coordination nudges help.",
"award": "Top 3 — AI Engineer World's Fair 2026 Hackathon (2nd overall of 95+ teams, LiveKit track winner, DigitalOcean Best Use)", "award": ["2nd Overall — AI Engineer World's Fair 2026", "LiveKit Track Winner", "DigitalOcean Best Use"],
"author": [ "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>
);
}
+186 -265
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>
>
<TrophyIcon className="size-4" />
Top 3 AI Engineer World&apos;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> </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"> <p className="pod-deck">
The teammate that sees what git can&apos;t. A real-time voice teammate that sees work before git doesthen learns which nudges
actually help.
</p> </p>
<p className="mx-auto mt-6 max-w-xl text-base leading-relaxed text-white/60 sm:text-lg"> <div className="pod-proof" aria-label="Awards">
An ambient AI teammate that watches your pod&apos;s screens in realtime catching merge <strong>$35K+ in prizes</strong>
collisions, duplicated work, and missed handoffs before anyone pushes. <span>2nd overall</span>
</p> <span>LiveKit track winner</span>
{/* 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> </div>
<a className="pod-scroll" href="#story">
{/* CTAs */} Read the story <ArrowDownIcon />
<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> </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>
{/* final CTA — beta coming soon */} <section id="story" className="pod-story pod-section">
<section className="relative z-10 mx-auto max-w-3xl px-6 py-24 text-center"> <div className="pod-section-index" data-reveal>01 THE PROBLEM</div>
<div className="rounded-3xl border border-white/10 bg-gradient-to-b from-white/[0.06] to-transparent px-8 py-14"> <div className="pod-story-grid">
<MicIcon className="mx-auto size-8 text-cyan-300" /> <div data-reveal>
<h2 className="mt-5 text-3xl font-semibold tracking-tight text-white sm:text-4xl"> <h2>Git sees the commit. PodMan sees the <em>collision coming.</em></h2>
The private beta is coming soon. <p>
</h2> AI-powered teams can ship in parallel faster than they can stay aligned. PodMan
<p className="mx-auto mt-4 max-w-md text-[15px] leading-relaxed text-white/60"> joins the LiveKit room, understands what each engineer is doing from shared
We&apos;re building PodMan out from the hackathon prototype into something a team runs screens, and speaks before two workstreams quietly become the same workstream.
every day. The private beta opens soon.
</p> </p>
<div className="mt-8 flex justify-center">
<BetaPill className="px-6 py-3 text-[15px]" />
</div> </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>
</section> </section>
{/* footer */} <section id="loop" className="pod-loop pod-section">
<footer className="relative z-10 border-t border-white/10"> <div className="pod-section-index" data-reveal>02 THE RECURSIVE LOOP</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-loop-heading" data-reveal>
<div className="flex items-center gap-2"> <h2>A teammate that gets quieterand smarterwith use.</h2>
<img src="/podman-logo.svg" alt="" className="h-5 w-5 opacity-70" /> <p>
<span>PodMan · podman.live</span> Every observation, intervention, accept, and dismiss becomes memory. The system
improves its next decision instead of repeating the same interruption.
</p>
</div> </div>
<span>Built at AI Engineer World&apos;s Fair 2026 · 2nd Overall</span> <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>,