Files
pocketpaw/docs/_landing/index.html
Rohit Kushwaha 4bb7313829 feat: move docs into monorepo, add deploy workflow
Consolidate documentation from the separate pocketpaw-web repo into the
main pocketpaw repo. This keeps docs and code in sync so PRs can update
both atomically.

- Remove docs/ from .gitignore
- Remove docs' own .git (was pocketpaw/pocketpaw-web)
- Add .github/workflows/deploy-docs.yml (builds from docs/ subdirectory)
- Track all 120+ MDX pages, config, landing page, and public assets

The separate pocketpaw-web repo can now be archived.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 13:12:04 +05:30

3879 lines
126 KiB
HTML

<!-- PocketPaw Landing Page — 2026-02-10
Design: "Denim Blue" — clean, editorial feel inspired by the mascot's denim pocket.
Fonts: Fraunces (wonky soft serif) + Plus Jakarta Sans + JetBrains Mono.
Changes: Added UnoCSS CDN runtime (config + shortcuts + Tailwind reset + FOUC prevention).
Existing <style> block preserved intact — UnoCSS is additive for utility classes.
Previous: channel icons, humanized copy, pip extras fix, Command Center cycling,
GSAP ScrollTrigger reveals, mascot parallax. -->
<!doctype html>
<html lang="en" data-grad="prism">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PocketPaw — Your AI Agent. Modular. Secure. Everywhere.</title>
<meta
name="description"
content="Self-hosted, open-source AI agent you control from Telegram, Discord, Slack, WhatsApp, or a web dashboard. Installs in 30 seconds. Python. MIT licensed."
/>
<meta
name="keywords"
content="AI agent, self-hosted, open source, Telegram bot, Discord bot, Slack bot, WhatsApp bot, Ollama, Claude, OpenAI, personal AI, encrypted, modular, Python, pip install"
/>
<meta name="robots" content="index, follow" />
<meta name="theme-color" content="#4a6fa5" />
<link rel="canonical" href="https://pocketpaw.xyz/" />
<link rel="apple-touch-icon" href="./paw.webp" />
<!-- Open Graph (Facebook, LinkedIn, Slack, iMessage) -->
<meta property="og:url" content="https://pocketpaw.netlify.app/" />
<meta property="og:type" content="website" />
<meta
property="og:title"
content="PocketPaw — Your AI Agent. Modular. Secure. Everywhere."
/>
<meta
property="og:description"
content="Self-hosted AI agent you control from Telegram, Discord, Slack, WhatsApp, or a web dashboard. One command install. Encrypted credentials. Open source."
/>
<meta
property="og:image"
content="https://pocketpaw.netlify.app/pocket-paw-og.webp"
/>
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@PocketPaw89242" />
<meta
name="twitter:title"
content="PocketPaw — Your AI Agent. Modular. Secure. Everywhere."
/>
<meta
name="twitter:description"
content="Self-hosted AI agent. One command install. Encrypted credentials. Telegram, Discord, Slack, WhatsApp, Web. Open source, MIT licensed."
/>
<meta
name="twitter:image"
content="https://pocketpaw.netlify.app/pocket-paw-og.webp"
/>
<link rel="icon" href="./paw.webp" type="image/png" />
<!-- Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "PocketPaw",
"description": "Self-hosted, open-source AI agent you control from Telegram, Discord, Slack, WhatsApp, or a web dashboard. Encrypted credentials, modular install, 3 LLM backends.",
"applicationCategory": "DeveloperApplication",
"operatingSystem": "Linux, macOS, Windows",
"url": "https://pocketpaw.xyz",
"downloadUrl": "https://pypi.org/project/pocketpaw/",
"softwareVersion": "0.1.0",
"license": "https://opensource.org/licenses/MIT",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"author": {
"@type": "Organization",
"name": "PocketPaw",
"url": "https://github.com/pocketpaw"
}
}
</script>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Plus+Jakarta+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
rel="stylesheet"
/>
<!-- CSS Reset (UnoCSS runtime has no preflight) -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@unocss/reset/tailwind.min.css"
/>
<!-- FOUC prevention -->
<style>
[un-cloak] {
display: none;
}
</style>
<!-- UnoCSS config -->
<script>
window.__unocss = {
theme: {
colors: {
bg: "#f9f8f6",
card: "#ffffff",
sand: "#f1eee9",
txt: { DEFAULT: "#1a1a2e", 2: "#5c5c7a", 3: "#9b9bb0" },
blue: { DEFAULT: "#4a6fa5", hover: "#3b5d8e", light: "#e8eef6" },
honey: { DEFAULT: "#d49a5c", light: "#f5ead8" },
coral: "#c46b5c",
},
fontFamily: {
serif: "Fraunces, Georgia, serif",
sans: "Plus Jakarta Sans, -apple-system, sans-serif",
mono: "JetBrains Mono, monospace",
},
animation: {
keyframes: {
bob: "{0%,100%{transform:translateY(0)}50%{transform:translateY(-12px)}}",
emerge:
"{from{opacity:0;filter:blur(12px);transform:translateY(10px) scale(0.98)}to{opacity:1;filter:blur(0);transform:translateY(0) scale(1)}}",
"pulse-dot": "{0%,100%{opacity:1}50%{opacity:.3}}",
"blink-cursor": "{0%,100%{opacity:1}50%{opacity:0}}",
},
durations: {
bob: "5s",
emerge: "1s",
"pulse-dot": "2s",
"blink-cursor": ".8s",
},
timingFns: {
bob: "ease-in-out",
emerge: "cubic-bezier(.16,1,.3,1)",
"pulse-dot": "ease-in-out",
"blink-cursor": "step-end",
},
counts: {
bob: "infinite",
"pulse-dot": "infinite",
"blink-cursor": "infinite",
},
},
},
shortcuts: [
{ wrap: "max-w-[1040px] mx-auto px-7" },
{ "wrap-sm": "max-w-[700px] mx-auto px-7" },
{
tag: "inline-block text-[0.7rem] font-semibold tracking-[0.18em] uppercase text-blue mb-2.5",
},
{
btn: "inline-flex items-center gap-2 px-[26px] py-3 font-sans text-[.88rem] font-medium no-underline rounded-full transition-all cursor-pointer active:scale-[.98]",
},
{
"btn-fill":
"btn bg-blue text-white border-2 border-blue hover:bg-blue-hover hover:border-blue-hover",
},
{
"btn-ghost":
"btn bg-transparent text-txt border-[1.5px] border-transparent hover:bg-black/4",
},
{
"f-card":
"bg-card border border-black/7 rounded-2xl p-[34px] transition-all hover:border-black/14 hover:shadow-lg hover:-translate-y-[3px]",
},
],
};
</script>
<!-- UnoCSS runtime -->
<script src="https://cdn.jsdelivr.net/npm/@unocss/runtime/uno.global.js"></script>
<style>
/* ════════════════════════════════════════
TOKENS — "Denim & Honey"
════════════════════════════════════════ */
:root {
--lp-bg: #f9f8f6;
--lp-card: #ffffff;
--lp-sand: #f1eee9;
--lp-text: #1a1a2e;
--lp-text-2: #5c5c7a;
--lp-text-3: #9b9bb0;
--lp-blue: #4a6fa5;
--lp-blue-hover: #3b5d8e;
--lp-blue-light: #e8eef6;
--lp-honey: #d49a5c;
--lp-honey-light: #f5ead8;
--lp-coral: #c46b5c;
--lp-border: rgba(26, 26, 46, 0.07);
--lp-border-hover: rgba(26, 26, 46, 0.14);
--lp-shadow-s:
0 1px 3px rgba(26, 26, 46, 0.04), 0 4px 12px rgba(26, 26, 46, 0.03);
--lp-shadow-m:
0 4px 16px rgba(26, 26, 46, 0.06), 0 12px 40px rgba(26, 26, 46, 0.04);
--lp-shadow-mascot: 0 20px 50px rgba(80, 60, 30, 0.18);
--lp-r-card: 16px;
--lp-r-code: 12px;
--lp-r-pill: 100px;
--lp-ease: cubic-bezier(0.22, 1, 0.36, 1);
}
/* ════════════════════════════════════════
RESET
════════════════════════════════════════ */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
.landing-custom {
background: var(--lp-bg);
color: var(--lp-text);
font-family:
"Plus Jakarta Sans",
-apple-system,
sans-serif;
font-weight: 400;
font-size: 1rem;
line-height: 1.7;
-webkit-font-smoothing: antialiased;
overflow-x: hidden;
}
.landing-custom img {
display: block;
max-width: 100%;
}
.landing-custom a {
color: inherit;
}
/* ════════════════════════════════════════
TEXTURE — subtle dot grid
════════════════════════════════════════ */
.landing-custom::before {
content: "";
position: fixed;
inset: 0;
background-image: radial-gradient(
rgba(26, 26, 46, 0.07) 1px,
transparent 1px
);
background-size: 24px 24px;
pointer-events: none;
z-index: 0;
}
section,
footer,
nav,
.section-break {
position: relative;
z-index: 1;
}
/* ════════════════════════════════════════
TYPOGRAPHY
════════════════════════════════════════ */
h1,
h2 {
font-family: "Fraunces", Georgia, serif;
font-weight: 400;
font-optical-sizing: auto;
}
/* ════════════════════════════════════════
LAYOUT
════════════════════════════════════════ */
.wrap {
max-width: 1040px;
margin: 0 auto;
padding: 0 28px;
}
.wrap--sm {
max-width: 700px;
margin: 0 auto;
padding: 0 28px;
}
/* ════════════════════════════════════════
SECTION LABEL
════════════════════════════════════════ */
.tag {
display: inline-block;
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--lp-blue);
margin-bottom: 10px;
}
/* ════════════════════════════════════════
THREE-DOT SECTION BREAK
════════════════════════════════════════ */
.section-break {
display: flex;
justify-content: center;
gap: 8px;
padding: 24px 0;
}
.section-break i {
width: 5px;
height: 5px;
border-radius: 50%;
background: var(--lp-blue);
opacity: 0.3;
font-style: normal;
}
/* ════════════════════════════════════════
NAV
════════════════════════════════════════ */
nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
padding: 8px 0;
background: rgba(255, 255, 255, 0.12);
backdrop-filter: blur(14px) saturate(140%);
-webkit-backdrop-filter: blur(14px) saturate(140%);
border-bottom: 1px solid rgba(255, 255, 255, 0.25);
transition:
background 0.3s,
padding 0.3s,
border-color 0.3s;
}
nav.scrolled {
background: rgba(255, 255, 255, 0.28);
border-bottom-color: rgba(255, 255, 255, 0.45);
padding: 7px 0;
}
.nav-bar {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.nav-brand {
display: grid;
}
.nav-brand > * {
grid-area: 1/1;
align-self: center;
transition:
opacity 0.35s var(--lp-ease),
transform 0.35s var(--lp-ease);
}
.nav-logo {
font-family: "Fraunces", serif;
font-size: 1.35rem;
letter-spacing: -0.04em;
color: var(--lp-text);
text-decoration: none;
opacity: 0;
transform: translateY(4px);
}
.nav-logo em {
font-style: italic;
color: var(--lp-blue);
}
.nav-tagline {
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--lp-blue);
white-space: nowrap;
opacity: 0;
transform: translateY(-4px);
pointer-events: none;
}
/* Default (no scroll): brand hidden, GitHub icon centered */
.nav-brand {
opacity: 0;
pointer-events: none;
transition: opacity 0.35s var(--lp-ease);
}
.nav-links {
display: flex;
align-items: center;
gap: 20px;
position: absolute;
left: 50%;
transform: translateX(-50%);
transition: all 0.35s var(--lp-ease);
}
.nav-a-text {
display: inline-block;
max-width: 0;
opacity: 0;
overflow: hidden;
transition:
max-width 0.35s var(--lp-ease),
opacity 0.35s var(--lp-ease);
}
/* Scrolled: brand visible, links move right, GitHub text appears */
nav.scrolled .nav-brand {
opacity: 1;
pointer-events: auto;
}
nav.scrolled .nav-logo {
opacity: 1;
transform: translateY(0);
}
nav.scrolled .nav-tagline {
opacity: 0;
transform: translateY(-4px);
pointer-events: none;
}
nav.scrolled .nav-links {
position: static;
transform: none;
}
nav.scrolled .nav-a-text {
max-width: 80px;
opacity: 1;
}
.nav-a {
font-size: 0.85rem;
font-weight: 500;
color: var(--lp-text-2);
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 5px;
transition: color 0.2s;
}
.nav-a:hover {
color: var(--lp-text);
}
/* ════════════════════════════════════════
BUTTONS
════════════════════════════════════════ */
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 12px 26px;
font-family: inherit;
font-size: 0.88rem;
font-weight: 500;
text-decoration: none;
border-radius: var(--lp-r-pill);
transition:
background 0.2s,
border-color 0.2s,
transform 0.15s;
cursor: pointer;
}
.btn:active {
transform: scale(0.98);
}
.btn--fill {
background: var(--lp-blue);
color: #fff;
border: 2px solid var(--lp-blue);
}
.btn--fill:hover {
background: var(--lp-blue-hover);
border-color: var(--lp-blue-hover);
}
.btn--ghost {
background: transparent;
color: var(--lp-text);
border: 1.5px solid transparent;
}
.btn--ghost:hover {
background: rgba(26, 26, 46, 0.04);
}
/* ════════════════════════════════════════
PAGE LOAD ANIMATION — blur-to-sharp reveal
════════════════════════════════════════ */
@keyframes emerge {
from {
opacity: 0;
filter: blur(12px);
transform: translateY(10px) scale(0.98);
}
to {
opacity: 1;
filter: blur(0);
transform: translateY(0) scale(1);
}
}
.hero .load-in {
opacity: 0;
animation: emerge 1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.hero .load-in:nth-child(1) {
animation-delay: 0.15s;
}
.hero .load-in:nth-child(2) {
animation-delay: 0.3s;
}
.hero .load-in:nth-child(3) {
animation-delay: 0.45s;
}
.hero .load-in:nth-child(4) {
animation-delay: 0.62s;
}
.hero .load-in:nth-child(5) {
animation-delay: 0.78s;
}
/* ════════════════════════════════════════
HERO
════════════════════════════════════════ */
.hero {
padding: 72px 0 40px;
text-align: center;
}
.hero-title {
font-size: clamp(3rem, 8vw, 5.8rem);
letter-spacing: -0.04em;
line-height: 1.02;
margin-bottom: 6px;
}
.hero-title em {
font-style: italic;
color: var(--lp-blue);
}
.hero-sub {
font-size: clamp(1rem, 2.2vw, 1.2rem);
color: var(--lp-text-2);
font-weight: 300;
line-height: 1.55;
margin-bottom: 20px;
}
/* mascot */
.mascot-area {
position: relative;
display: inline-block;
margin-top: 10px;
margin-bottom: 14px;
}
.mascot-glow {
position: absolute;
width: 400px;
height: 400px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: radial-gradient(
ellipse,
rgba(74, 111, 165, 0.09) 0%,
rgba(74, 111, 165, 0.04) 45%,
transparent 72%
);
pointer-events: none;
}
.mascot-img {
position: relative;
width: 210px;
height: auto;
filter: drop-shadow(var(--lp-shadow-mascot));
animation: bob 5s ease-in-out infinite;
}
.mascot-img:hover {
animation-duration: 2.5s;
}
@keyframes bob {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-12px);
}
}
/* terminal install block */
.terminal {
display: inline-block;
text-align: left;
background: var(--lp-text);
border-radius: var(--lp-r-code);
padding: 0;
margin-top: 0;
box-shadow: var(--lp-shadow-m);
overflow: hidden;
max-width: 100%;
}
/* Animated gradient text on hero terminal */
.terminal--glow .terminal-body code {
background: var(--grad-text);
background-size: 200% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
animation: glow-text 4s linear infinite;
}
@keyframes glow-text {
0% {
background-position: 0% 50%;
}
100% {
background-position: 200% 50%;
}
}
.terminal--glow .terminal-body .prompt {
-webkit-text-fill-color: #7cb3d9;
}
/* ── Gradient text color variations (toggle via picker) ── */
/* 1. Prism (current) — blue, honey, orange, coral, purple */
[data-grad="prism"] {
--grad-text: linear-gradient(
90deg,
#7cb3d9 0%,
#d49a5c 20%,
#e8834a 40%,
#c46b5c 55%,
#a855f7 70%,
#7cb3d9 85%,
#4a6fa5 100%
);
}
/* 2. Denim & Honey — stays on brand, warm */
[data-grad="denim"] {
--grad-text: linear-gradient(
90deg,
#4a6fa5 0%,
#7cb3d9 25%,
#d49a5c 50%,
#7cb3d9 75%,
#4a6fa5 100%
);
}
/* 3. Aurora — cool greens and teals */
[data-grad="aurora"] {
--grad-text: linear-gradient(
90deg,
#4ade80 0%,
#22d3ee 25%,
#818cf8 50%,
#22d3ee 75%,
#4ade80 100%
);
}
/* 4. Sunset — warm oranges and pinks */
[data-grad="sunset"] {
--grad-text: linear-gradient(
90deg,
#fb923c 0%,
#f472b6 25%,
#c084fc 50%,
#f472b6 75%,
#fb923c 100%
);
}
/* 5. Ice — cool blues and whites */
[data-grad="ice"] {
--grad-text: linear-gradient(
90deg,
#bfdbfe 0%,
#93c5fd 20%,
#60a5fa 40%,
#a5b4fc 60%,
#93c5fd 80%,
#bfdbfe 100%
);
}
/* 6. Fire — reds and golds */
[data-grad="fire"] {
--grad-text: linear-gradient(
90deg,
#fbbf24 0%,
#f97316 25%,
#ef4444 50%,
#f97316 75%,
#fbbf24 100%
);
}
/* 7. Mono — subtle silver shimmer */
[data-grad="mono"] {
--grad-text: linear-gradient(
90deg,
#94a3b8 0%,
#e2e8f0 30%,
#cbd5e1 50%,
#e2e8f0 70%,
#94a3b8 100%
);
}
/* Color picker strip (easter egg — Shift+C to toggle) */
.grad-picker {
position: fixed;
top: 12px;
left: 50%;
transform: translateX(-50%) translateY(-60px);
z-index: 9999;
display: flex;
gap: 6px;
background: rgba(26, 26, 46, 0.9);
padding: 8px 14px;
border-radius: 100px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
opacity: 0;
pointer-events: none;
transition:
transform 0.35s cubic-bezier(0.22, 1, 0.36, 1),
opacity 0.35s ease;
}
.grad-picker.visible {
transform: translateX(-50%) translateY(0);
opacity: 1;
pointer-events: auto;
}
.grad-picker button {
width: 28px;
height: 28px;
border-radius: 50%;
border: 2px solid transparent;
cursor: pointer;
transition:
border-color 0.2s,
transform 0.2s;
font-size: 0;
}
.grad-picker button:hover {
transform: scale(1.15);
}
.grad-picker button.active {
border-color: #fff;
}
.grad-picker button[data-v="prism"] {
background: conic-gradient(#7cb3d9, #d49a5c, #c46b5c, #a855f7, #7cb3d9);
}
.grad-picker button[data-v="denim"] {
background: conic-gradient(#4a6fa5, #7cb3d9, #d49a5c, #7cb3d9, #4a6fa5);
}
.grad-picker button[data-v="aurora"] {
background: conic-gradient(#4ade80, #22d3ee, #818cf8, #4ade80);
}
.grad-picker button[data-v="sunset"] {
background: conic-gradient(#fb923c, #f472b6, #c084fc, #fb923c);
}
.grad-picker button[data-v="ice"] {
background: conic-gradient(#bfdbfe, #60a5fa, #a5b4fc, #bfdbfe);
}
.grad-picker button[data-v="fire"] {
background: conic-gradient(#fbbf24, #f97316, #ef4444, #fbbf24);
}
.grad-picker button[data-v="mono"] {
background: conic-gradient(#94a3b8, #e2e8f0, #cbd5e1, #94a3b8);
}
/* Auto-rotate indicator */
.grad-picker .auto-btn {
width: 28px;
height: 28px;
border-radius: 50%;
border: 2px solid transparent;
cursor: pointer;
background: rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
line-height: 1;
color: #fff;
transition:
border-color 0.2s,
transform 0.2s;
margin-left: 4px;
}
.grad-picker .auto-btn:hover {
transform: scale(1.15);
}
.grad-picker .auto-btn.active {
border-color: #fff;
}
.terminal-bar {
display: flex;
align-items: center;
gap: 7px;
padding: 12px 18px 0;
}
.terminal-bar i {
width: 11px;
height: 11px;
border-radius: 50%;
font-style: normal;
}
.terminal-bar i:nth-child(1) {
background: #ff5f57;
}
.terminal-bar i:nth-child(2) {
background: #febc2e;
}
.terminal-bar i:nth-child(3) {
background: #28c840;
}
.terminal-body {
padding: 14px 22px 18px;
display: flex;
align-items: center;
gap: 14px;
}
.terminal-body code {
font-family: "JetBrains Mono", monospace;
font-size: 0.88rem;
color: #d1d5e8;
white-space: nowrap;
}
.terminal-body .prompt {
color: #7cb3d9;
user-select: none;
}
.copy-btn {
font-family: inherit;
font-size: 0.65rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #7a7a9a;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 6px;
padding: 5px 10px;
cursor: pointer;
transition:
color 0.2s,
border-color 0.2s;
white-space: nowrap;
display: inline-flex;
align-items: center;
gap: 4px;
}
.copy-btn:hover {
color: #d1d5e8;
border-color: rgba(255, 255, 255, 0.25);
}
.copy-btn.copied {
color: #7cb3d9;
border-color: #7cb3d9;
}
.copy-icon {
display: none;
flex-shrink: 0;
}
.install-note {
font-size: 0.82rem;
color: var(--lp-text-3);
margin-top: 14px;
}
/* channel icons row */
.channel-icons {
display: flex;
align-items: center;
justify-content: center;
gap: 14px;
margin-top: 18px;
}
.channel-icon {
position: relative;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
background: var(--lp-card);
border: 1px solid var(--lp-border);
box-shadow: var(--lp-shadow-s);
cursor: default;
transition:
border-color 0.2s var(--lp-ease),
box-shadow 0.2s var(--lp-ease),
transform 0.2s var(--lp-ease);
}
.channel-icon:hover {
border-color: var(--lp-border-hover);
box-shadow: var(--lp-shadow-m);
transform: translateY(-2px);
}
.channel-icon svg {
width: 18px;
height: 18px;
flex-shrink: 0;
fill: var(--lp-text-3);
transition: fill 0.2s var(--lp-ease);
}
.channel-icon:hover svg {
fill: var(--lp-blue);
}
/* stroke-based icons (web) */
.channel-icon svg.stroke-icon {
fill: none;
stroke: var(--lp-text-3);
stroke-width: 1.8;
}
.channel-icon:hover svg.stroke-icon {
stroke: var(--lp-blue);
}
/* tooltip */
.channel-icon::after {
content: attr(data-tip);
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%) translateY(4px);
padding: 5px 10px;
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.02em;
color: var(--lp-card);
background: var(--lp-text);
border-radius: 6px;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition:
opacity 0.18s var(--lp-ease),
transform 0.18s var(--lp-ease);
}
.channel-icon:hover::after {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* gradient time badge */
.time-badge {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
margin-top: 14px;
}
.time-badge svg {
flex-shrink: 0;
color: var(--lp-blue);
opacity: 0.7;
}
.time-badge span {
font-size: 0.88rem;
font-weight: 600;
letter-spacing: -0.01em;
background: linear-gradient(
135deg,
var(--lp-blue) 0%,
#7cb3d9 50%,
var(--lp-blue) 100%
);
background-size: 200% 200%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: gradient-shift 4s ease-in-out infinite;
}
@keyframes gradient-shift {
0%,
100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
/* OS install buttons */
.install-options {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
margin-top: 18px;
flex-wrap: wrap;
}
.install-label {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--lp-text-3);
margin-bottom: 8px;
}
.install-or {
display: flex;
align-items: center;
gap: 14px;
margin: 10px auto;
width: 280px;
}
.install-or::before,
.install-or::after {
content: "";
flex: 1;
height: 1px;
background: var(--lp-border);
}
.install-or span {
font-size: 0.68rem;
font-weight: 500;
color: var(--lp-text-3);
text-transform: uppercase;
letter-spacing: 0.12em;
white-space: nowrap;
}
/* animated cursor-click icon on OS buttons */
.btn--os .click-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
opacity: 0.45;
}
.btn--os:hover .click-icon {
opacity: 0.7;
}
.click-icon .cursor-arrow {
animation: cursor-nudge 3s ease-in-out infinite;
transform-origin: 9px 10px;
}
.click-icon .click-line {
opacity: 0;
animation: click-spread 3s ease-out infinite;
}
.click-icon .click-line:nth-child(2) {
animation-delay: 0.05s;
}
.click-icon .click-line:nth-child(3) {
animation-delay: 0.1s;
}
.click-icon .click-line:nth-child(4) {
animation-delay: 0.15s;
}
.click-icon .click-line:nth-child(5) {
animation-delay: 0.2s;
}
@keyframes cursor-nudge {
0%,
100% {
transform: translate(0, 0);
}
15% {
transform: translate(-1px, -2px);
}
30% {
transform: translate(0, 0);
}
}
@keyframes click-spread {
0%,
10% {
opacity: 0;
transform: translate(0, 0);
}
20% {
opacity: 1;
transform: translate(0, 0);
}
40% {
opacity: 0;
transform: var(--spread);
}
100% {
opacity: 0;
}
}
.btn--os {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 10px 20px;
font-family: inherit;
font-size: 0.82rem;
font-weight: 500;
text-decoration: none;
color: var(--lp-text);
background: var(--lp-card);
border: 1.5px solid var(--lp-border);
border-radius: var(--lp-r-pill);
transition:
border-color 0.2s,
box-shadow 0.2s,
transform 0.15s;
cursor: pointer;
}
.btn--os:hover {
border-color: var(--lp-border-hover);
box-shadow: var(--lp-shadow-s);
transform: translateY(-1px);
}
.btn--os.disabled {
opacity: 0.45;
pointer-events: none;
cursor: default;
}
.btn--os svg {
width: 16px;
height: 16px;
flex-shrink: 0;
}
.btn--os .os-sub {
font-size: 0.62rem;
color: var(--lp-text-3);
font-weight: 400;
margin-left: -2px;
}
.hero-ctas {
display: flex;
gap: 12px;
justify-content: center;
flex-wrap: wrap;
margin-top: 18px;
}
/* ════════════════════════════════════════
MULTI-LINE TERMINAL
════════════════════════════════════════ */
.terminal--multi .terminal-body {
display: block;
padding: 14px 22px 20px;
}
.terminal--multi code {
font-family: "JetBrains Mono", monospace;
font-size: 0.82rem;
line-height: 1.9;
color: #d1d5e8;
white-space: pre;
display: block;
}
.terminal--multi .prompt {
color: #7cb3d9;
}
.terminal--multi .comment {
color: #6a6a8a;
}
.terminal--multi .hl {
color: #8bb8e8;
}
/* ════════════════════════════════════════
PROBLEM
════════════════════════════════════════ */
.problem-lead {
font-family: "Fraunces", serif;
font-size: clamp(1.25rem, 3vw, 1.8rem);
line-height: 1.45;
letter-spacing: -0.02em;
text-align: center;
}
.problem-body {
font-size: 1rem;
color: var(--lp-text-2);
line-height: 1.75;
margin-top: 16px;
font-weight: 300;
text-align: center;
}
/* ════════════════════════════════════════
COMPARISON — two-card editorial breakdown
════════════════════════════════════════ */
.compare-panel {
margin-top: 48px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
align-items: stretch;
}
/* shared card base */
.compare-left,
.compare-right {
border-radius: var(--lp-r-card);
padding: 32px 28px;
position: relative;
overflow: hidden;
}
/* Left — warm coral tint */
.compare-left {
background: var(--lp-card);
border: 1px solid var(--lp-border);
}
.compare-heading {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 20px;
}
.compare-heading-icon {
width: 18px;
height: 18px;
flex-shrink: 0;
}
.compare-heading span {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
}
.compare-left .compare-heading span {
color: var(--lp-coral);
}
.compare-left .compare-heading-icon {
color: var(--lp-coral);
opacity: 0.6;
}
.compare-steps {
list-style: none;
padding: 0;
margin: 0;
}
.compare-step {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid var(--lp-border);
font-size: 0.82rem;
color: var(--lp-text-3);
}
.compare-step:last-child {
border-bottom: none;
}
.compare-step-name {
display: flex;
align-items: center;
gap: 8px;
}
.compare-step-num {
font-family: "JetBrains Mono", monospace;
font-size: 0.6rem;
color: var(--lp-text-3);
opacity: 0.4;
width: 14px;
text-align: right;
flex-shrink: 0;
}
.compare-step-time {
font-family: "JetBrains Mono", monospace;
font-size: 0.7rem;
color: var(--lp-coral);
opacity: 0.7;
white-space: nowrap;
}
.compare-total {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 14px;
padding-top: 14px;
border-top: 1.5px solid rgba(196, 107, 92, 0.15);
}
.compare-total-label {
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--lp-text-3);
}
.compare-total-time {
font-family: "Fraunces", serif;
font-size: 1.5rem;
letter-spacing: -0.03em;
color: var(--lp-coral);
}
/* Right — soft blue accent card */
.compare-right {
background: var(--lp-blue-light);
border: 1px solid rgba(74, 111, 165, 0.12);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.compare-right .compare-heading span {
color: var(--lp-blue);
}
.compare-right .compare-heading-icon {
color: var(--lp-blue);
opacity: 0.6;
}
.compare-hero-time {
font-family: "Fraunces", serif;
font-size: clamp(3.4rem, 7vw, 5.4rem);
letter-spacing: -0.04em;
line-height: 1;
color: var(--lp-text);
margin: 8px 0 2px;
}
.compare-hero-time small {
font-size: 0.38em;
color: var(--lp-text-3);
}
.compare-hero-unit {
font-size: 0.82rem;
font-weight: 300;
color: var(--lp-text-2);
margin-bottom: 22px;
}
.compare-right-cmd {
font-family: "JetBrains Mono", monospace;
font-size: 0.72rem;
color: var(--lp-text-2);
background: rgba(74, 111, 165, 0.08);
padding: 9px 16px;
border-radius: 8px;
margin-bottom: 18px;
}
.compare-right-cmd .prompt {
color: var(--lp-blue);
}
.compare-checks {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 5px;
}
.compare-checks li {
font-size: 0.78rem;
color: var(--lp-text-2);
display: flex;
align-items: center;
gap: 6px;
}
.compare-checks .check {
color: #3fae6f;
font-weight: 600;
}
.compare-badges {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 6px;
margin-top: 16px;
}
.compare-badge {
font-size: 0.62rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
padding: 4px 10px;
border-radius: 100px;
background: var(--lp-card);
border: 1px solid rgba(74, 111, 165, 0.15);
color: var(--lp-blue);
box-shadow: 0 1px 2px rgba(26, 26, 46, 0.03);
}
.compare-badge--green {
color: #3fae6f;
border-color: rgba(63, 174, 111, 0.2);
background: rgba(63, 174, 111, 0.06);
}
.compare-badge--honey {
color: #b07d3a;
border-color: rgba(212, 154, 92, 0.25);
background: rgba(212, 154, 92, 0.08);
}
.compare-footer {
text-align: center;
margin-top: 20px;
font-size: 0.85rem;
color: var(--lp-text-2);
font-weight: 300;
}
.compare-footer strong {
font-weight: 600;
color: var(--lp-text);
}
@media (max-width: 700px) {
.compare-panel {
grid-template-columns: 1fr;
}
}
/* ════════════════════════════════════════
SECTION HEADINGS
════════════════════════════════════════ */
.heading {
font-size: clamp(1.9rem, 5vw, 3.1rem);
letter-spacing: -0.035em;
line-height: 1.1;
margin-bottom: 14px;
}
.heading .honey {
color: var(--lp-blue);
}
.heading .dim {
color: var(--lp-text-2);
}
.heading .blue {
color: var(--lp-blue);
}
.s-desc {
font-size: 1.02rem;
color: var(--lp-text-2);
line-height: 1.7;
max-width: 520px;
font-weight: 300;
}
/* ════════════════════════════════════════
INLINE STATS (solution section)
════════════════════════════════════════ */
.quick-stats {
display: flex;
gap: 48px;
flex-wrap: wrap;
margin-top: 48px;
}
.qs-val {
font-family: "Fraunces", serif;
font-size: clamp(2rem, 4vw, 3.2rem);
letter-spacing: -0.03em;
line-height: 1;
}
.qs-val small {
font-size: 0.5em;
color: var(--lp-text-3);
}
.qs-lbl {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--lp-text-3);
margin-top: 5px;
}
/* ════════════════════════════════════════
CORE INCLUDES (pills)
════════════════════════════════════════ */
.core-includes {
margin-top: 36px;
}
.core-label {
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--lp-text-3);
margin-bottom: 12px;
}
.core-pills {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.core-pills span {
font-size: 0.8rem;
font-weight: 500;
color: var(--lp-blue);
background: var(--lp-blue-light);
padding: 6px 14px;
border-radius: var(--lp-r-pill);
white-space: nowrap;
}
/* ════════════════════════════════════════
FEATURE CARDS
════════════════════════════════════════ */
.features {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 14px;
margin-top: 52px;
}
.f-card {
background: var(--lp-card);
border: 1px solid var(--lp-border);
border-radius: var(--lp-r-card);
padding: 34px;
transition:
border-color 0.25s,
box-shadow 0.25s,
transform 0.25s;
}
.f-card:hover {
border-color: var(--lp-border-hover);
box-shadow: var(--lp-shadow-m);
transform: translateY(-3px);
}
/* feature icon */
.f-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 11px;
background: var(--lp-blue-light);
color: var(--lp-blue);
margin-bottom: 16px;
}
.f-icon svg {
width: 20px;
height: 20px;
}
.f-card h3 {
font-family: "Plus Jakarta Sans", sans-serif;
font-size: 1.05rem;
font-weight: 600;
letter-spacing: -0.01em;
margin-bottom: 7px;
}
.f-card p {
font-size: 0.9rem;
color: var(--lp-text-2);
line-height: 1.6;
font-weight: 300;
}
/* ════════════════════════════════════════
COMMAND CENTER — animated dashboard
════════════════════════════════════════ */
.cc-desc {
font-size: 1rem;
color: var(--lp-text-2);
line-height: 1.7;
font-weight: 300;
margin-top: 14px;
max-width: 560px;
}
/* example selector pills */
.cc-examples {
display: flex;
justify-content: center;
gap: 8px;
margin-top: 36px;
flex-wrap: wrap;
}
.cc-ex-btn {
font-family: inherit;
font-size: 0.78rem;
font-weight: 500;
color: var(--lp-text-3);
background: transparent;
border: 1.5px solid var(--lp-border);
border-radius: var(--lp-r-pill);
padding: 8px 18px;
cursor: pointer;
transition: all 0.25s;
}
.cc-ex-btn:hover {
border-color: var(--lp-text-2);
color: var(--lp-text-2);
}
.cc-ex-btn.active {
background: var(--lp-blue);
color: #fff;
border-color: var(--lp-blue);
}
/* dark panel */
.cc-panel {
margin-top: 20px;
background: #13131f;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.18);
}
.cc-topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 24px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.cc-topbar-left {
display: flex;
align-items: center;
gap: 10px;
}
.cc-topbar-dots {
display: flex;
gap: 6px;
}
.cc-topbar-dots i {
width: 10px;
height: 10px;
border-radius: 50%;
font-style: normal;
}
.cc-topbar-dots i:nth-child(1) {
background: #ff5f57;
}
.cc-topbar-dots i:nth-child(2) {
background: #febc2e;
}
.cc-topbar-dots i:nth-child(3) {
background: #28c840;
}
.cc-topbar-title {
font-family: "JetBrains Mono", monospace;
font-size: 0.72rem;
color: rgba(255, 255, 255, 0.55);
letter-spacing: 0.04em;
}
.cc-topbar-status {
display: flex;
align-items: center;
gap: 6px;
font-size: 0.68rem;
font-weight: 600;
color: #3fae6f;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.cc-topbar-status::before {
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
background: #3fae6f;
animation: pulse-dot 2s ease-in-out infinite;
}
@keyframes pulse-dot {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.3;
}
}
/* agent avatar stack */
.cc-agents {
display: flex;
align-items: center;
}
.cc-agent-av {
width: 26px;
height: 26px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.52rem;
font-weight: 700;
color: #fff;
border: 2px solid #13131f;
margin-left: -7px;
opacity: 0;
transform: scale(0.4);
transition:
opacity 0.35s ease,
transform 0.35s ease;
position: relative;
}
.cc-agent-av:first-child {
margin-left: 0;
}
.cc-agent-av.visible {
opacity: 1;
transform: scale(1);
}
.cc-agent-av .cc-av-tip {
position: absolute;
bottom: calc(100% + 6px);
left: 50%;
transform: translateX(-50%);
background: #1e1e30;
color: rgba(255, 255, 255, 0.75);
font-size: 0.58rem;
font-weight: 500;
letter-spacing: 0.01em;
padding: 4px 8px;
border-radius: 5px;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s;
}
.cc-agent-av:hover .cc-av-tip {
opacity: 1;
}
.cc-agent-count {
width: 26px;
height: 26px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.52rem;
font-weight: 700;
color: rgba(255, 255, 255, 0.5);
background: rgba(255, 255, 255, 0.08);
border: 2px solid #13131f;
margin-left: -7px;
opacity: 0;
transform: scale(0.4);
transition:
opacity 0.35s ease,
transform 0.35s ease;
}
.cc-agent-count.visible {
opacity: 1;
transform: scale(1);
}
/* prompt bubble */
.cc-prompt {
padding: 20px 24px 0;
display: flex;
gap: 10px;
align-items: flex-start;
}
.cc-prompt-avatar {
width: 28px;
height: 28px;
border-radius: 8px;
background: var(--lp-blue);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 0.65rem;
font-weight: 700;
color: #fff;
}
.cc-prompt-text {
font-family: "JetBrains Mono", monospace;
font-size: 0.82rem;
color: rgba(255, 255, 255, 0.7);
line-height: 1.5;
min-height: 1.5em;
}
.cc-prompt-cursor {
display: inline-block;
width: 2px;
height: 1em;
background: var(--lp-blue);
margin-left: 2px;
animation: blink-cursor 0.8s step-end infinite;
vertical-align: text-bottom;
}
@keyframes blink-cursor {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
}
/* flow header + progress */
.cc-header {
padding: 20px 24px 0;
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
gap: 12px;
opacity: 0;
transition: opacity 0.4s;
}
.cc-header.visible {
opacity: 1;
}
.cc-flow-name {
color: #fff;
font-size: 0.88rem;
font-weight: 500;
}
.cc-flow-meta {
font-size: 0.68rem;
color: rgba(255, 255, 255, 0.5);
margin-top: 3px;
}
.cc-progress-wrap {
text-align: right;
}
.cc-progress-pct {
font-family: "JetBrains Mono", monospace;
font-size: 1.5rem;
color: #fff;
font-weight: 500;
transition: all 0.3s;
}
.cc-progress-lbl {
font-size: 0.6rem;
color: rgba(255, 255, 255, 0.5);
text-transform: uppercase;
letter-spacing: 0.1em;
}
/* progress bar */
.cc-bar {
margin: 16px 24px 0;
height: 3px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
overflow: hidden;
}
.cc-bar-fill {
height: 100%;
width: 0;
border-radius: 2px;
background: linear-gradient(90deg, var(--lp-blue), #7cb3d9);
transition: width 1.2s cubic-bezier(0.22, 1, 0.36, 1);
}
/* node cards */
.cc-nodes {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
padding: 20px 24px 24px;
}
.cc-node {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 10px;
padding: 14px;
position: relative;
overflow: hidden;
transition:
border-color 0.5s,
opacity 0.5s;
opacity: 0.3;
}
.cc-node.st-done {
border-color: rgba(63, 174, 111, 0.3);
opacity: 1;
}
.cc-node.st-active {
border-color: rgba(74, 111, 165, 0.5);
opacity: 1;
}
.cc-node.st-active::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: var(--lp-blue);
}
.cc-node.st-review {
border-color: rgba(254, 188, 46, 0.3);
opacity: 0.7;
}
.cc-node.st-pending {
opacity: 0.3;
}
.cc-node-badge {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.58rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
margin-bottom: 8px;
padding: 3px 8px;
border-radius: 4px;
transition: all 0.4s;
}
.badge-done {
color: #3fae6f;
background: rgba(63, 174, 111, 0.1);
}
.badge-running {
color: #7cb3d9;
background: rgba(74, 111, 165, 0.15);
}
.badge-pending {
color: rgba(255, 255, 255, 0.45);
background: rgba(255, 255, 255, 0.06);
}
.badge-review {
color: #febc2e;
background: rgba(254, 188, 46, 0.1);
}
.badge-approved {
color: #3fae6f;
background: rgba(63, 174, 111, 0.1);
}
.cc-node-name {
color: #fff;
font-size: 0.8rem;
font-weight: 500;
margin-bottom: 3px;
}
.cc-node-agent {
font-size: 0.68rem;
color: rgba(255, 255, 255, 0.45);
}
/* connector dots between nodes */
.cc-node + .cc-node::before {
content: "";
position: absolute;
top: 50%;
left: -7px;
width: 4px;
height: 4px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.12);
transform: translateY(-50%);
}
@media (max-width: 900px) {
.cc-nodes {
grid-template-columns: repeat(3, 1fr);
}
.cc-node + .cc-node::before {
display: none;
}
}
@media (max-width: 560px) {
.cc-nodes {
grid-template-columns: 1fr;
}
.cc-header {
flex-direction: column;
}
.cc-examples {
gap: 6px;
}
.cc-ex-btn {
font-size: 0.72rem;
padding: 6px 14px;
}
}
/* ════════════════════════════════════════
SECURITY
════════════════════════════════════════ */
.sec-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 72px;
align-items: start;
}
.sec-intro {
font-size: 1rem;
color: var(--lp-text-2);
line-height: 1.7;
margin-top: 14px;
font-weight: 300;
}
.sec-item {
display: flex;
gap: 14px;
padding: 16px 0;
border-bottom: 1px solid var(--lp-border);
}
.sec-item:last-child {
border-bottom: none;
}
.sec-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--lp-blue);
flex-shrink: 0;
margin-top: 7px;
}
.sec-name {
font-size: 0.93rem;
font-weight: 500;
margin-bottom: 2px;
}
.sec-detail {
font-size: 0.83rem;
color: var(--lp-text-3);
font-weight: 300;
}
/* ════════════════════════════════════════
NUMBERS
════════════════════════════════════════ */
.nums {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 48px 28px;
margin-top: 44px;
text-align: center;
}
.n-val {
font-family: "Fraunces", serif;
font-size: clamp(2.4rem, 4.5vw, 3.6rem);
letter-spacing: -0.035em;
line-height: 1;
}
.n-val small {
font-size: 0.48em;
color: var(--lp-text-3);
vertical-align: baseline;
}
.n-lbl {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--lp-text-3);
margin-top: 7px;
}
/* ════════════════════════════════════════
CTA
════════════════════════════════════════ */
.cta {
padding: 80px 0 110px;
text-align: center;
}
.cta-meta {
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--lp-text-3);
margin-bottom: 28px;
}
/* ════════════════════════════════════════
FOOTER
════════════════════════════════════════ */
footer {
border-top: 1px solid var(--lp-border);
padding: 28px 0;
}
.foot {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 14px;
}
.foot-left {
display: flex;
align-items: center;
gap: 10px;
}
.foot-logo {
font-family: "Fraunces", serif;
font-size: 1.08rem;
letter-spacing: -0.02em;
}
.foot-sep {
color: var(--lp-border-hover);
}
.foot-sm {
font-size: 0.8rem;
color: var(--lp-text-3);
}
.foot-links {
display: flex;
align-items: center;
gap: 20px;
}
.foot-a {
font-size: 0.8rem;
color: var(--lp-text-3);
text-decoration: none;
transition: color 0.2s;
}
.foot-a:hover {
color: var(--lp-text);
}
/* ════════════════════════════════════════
TEXT REVEAL ON SCROLL (word by word, GSAP-driven)
════════════════════════════════════════ */
.scroll-text .word {
display: inline;
opacity: 0.12;
}
/* ════════════════════════════════════════
RESPONSIVE
════════════════════════════════════════ */
@media (max-width: 900px) {
.hero {
padding: 64px 0 32px;
}
.mascot-img {
width: 170px;
}
.features {
grid-template-columns: 1fr;
}
.sec-grid {
grid-template-columns: 1fr;
gap: 44px;
}
.nums {
grid-template-columns: repeat(2, 1fr);
}
.hero-ctas {
flex-direction: column;
align-items: center;
}
.btn {
width: 260px;
justify-content: center;
}
}
@media (max-width: 560px) {
.nav-bar {
overflow: hidden;
}
nav.scrolled .nav-a-text {
max-width: 0;
opacity: 0;
}
.nav-logo {
font-size: 1.15rem;
}
.hero {
padding: 56px 0 24px;
}
.tag {
font-size: 0.58rem;
letter-spacing: 0.1em;
}
.mascot-img {
width: 150px;
}
.mascot-area {
margin-bottom: 22px;
}
.install-options {
gap: 8px;
}
.btn--os {
padding: 8px 16px;
font-size: 0.78rem;
}
.btn--os .os-sub {
display: none;
}
.terminal-body {
padding: 12px 16px 14px;
}
.terminal-body code {
font-size: 0.78rem;
}
.copy-icon {
display: block;
}
.copy-label {
display: none;
}
.terminal--multi .terminal-body {
padding: 12px 16px 16px;
overflow-x: auto;
}
.terminal--multi code {
font-size: 0.65rem;
line-height: 1.7;
}
.terminal--multi .comment {
display: none;
}
.quick-stats {
gap: 28px;
}
.core-pills {
gap: 6px;
}
.core-pills span {
padding: 5px 12px;
font-size: 0.75rem;
}
.f-card {
padding: 26px;
}
.nums {
gap: 32px 18px;
}
.wrap,
.wrap--sm {
padding: 0 18px;
}
.btn {
width: 100%;
}
section.padded {
padding: 64px 0 52px;
}
}
section.padded {
padding: 88px 0 72px;
}
</style>
<!-- GSAP + ScrollTrigger loaded dynamically to avoid Vite strict-mode wrapper -->
</head>
<body class="landing-custom" un-cloak>
<!-- ───────────── NAV ───────────── -->
<nav
id="nav"
style="
opacity: 0;
animation: emerge 0.8s cubic-bezier(0.16, 1, 0.3, 1) 0.08s forwards;
"
>
<div class="wrap nav-bar">
<div class="nav-brand">
<a href="#top" class="nav-logo">Pocket<em>Paw</em></a>
<span class="nav-tagline"
>Open Source &middot; MIT Licensed &middot; Python 3.11+</span
>
</div>
<div class="nav-links">
<a href="/introduction" class="nav-a">
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
</svg>
<span class="nav-a-text">Docs</span>
</a>
<a
href="https://github.com/pocketpaw/pocketpaw"
target="_blank"
rel="noopener"
class="nav-a"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
<path
d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"
/>
</svg>
<span class="nav-a-text">GitHub</span>
</a>
</div>
</div>
</nav>
<!-- ───────────── HERO ───────────── -->
<section class="hero">
<div class="wrap">
<div class="load-in">
<span class="tag"
>Open Source &middot; MIT Licensed &middot; Python 3.11+</span
>
</div>
<div class="load-in mascot-area">
<div class="mascot-glow"></div>
<img
src="./paw.webp"
alt="PocketPaw mascot — a friendly puppy waving from a denim pocket"
class="mascot-img"
/>
</div>
<div class="load-in">
<h1 class="hero-title">Pocket<em>Paw</em></h1>
<p class="hero-sub">Your AI agent. Modular. Secure. Everywhere.</p>
</div>
<div class="load-in">
<p class="install-label">One-click install</p>
<div class="install-options" style="margin-top: 0; margin-bottom: 0">
<span class="btn--os disabled">
<svg
viewBox="0 0 24 24"
fill="currentColor"
width="16"
height="16"
>
<path
d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"
/>
</svg>
macOS
<span class="os-sub">Coming soon</span>
</span>
<span class="btn--os disabled">
<svg
viewBox="0 0 24 24"
fill="currentColor"
width="16"
height="16"
>
<path
d="M3 12V6.75l6-1.32v6.48L3 12zm6.98.01l.02 6.47-6.98-1.01V13l6.96-.99zM10 5.12L19.99 3v8.87H10V5.12zm10.01 7.73l-.01 8.15L10 19.75V12.85h10.01z"
/>
</svg>
Windows
<span class="os-sub">Coming soon</span>
</span>
</div>
<div class="install-or"><span>or</span></div>
<div class="terminal terminal--glow">
<div class="terminal-bar"><i></i><i></i><i></i></div>
<div class="terminal-body">
<code
><span class="prompt">$</span> curl -fsSL https://pocketpaw.xyz/install.sh | sh</code
>
<button class="copy-btn" onclick="copyCmd(this)">
<svg
class="copy-icon"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect x="9" y="9" width="13" height="13" rx="2" />
<path
d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"
/></svg
><span class="copy-label">Copy</span>
</button>
</div>
</div>
<div class="time-badge">
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="10" />
<polyline points="12 6 12 12 16 14" />
</svg>
<span>30 seconds to your first message</span>
</div>
<div class="channel-icons">
<!-- Telegram -->
<div class="channel-icon" data-tip="Chat via Telegram">
<svg viewBox="0 0 24 24">
<path
d="M11.944 0A12 12 0 000 12a12 12 0 0012 12 12 12 0 0012-12A12 12 0 0012 0a12 12 0 00-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 01.171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.479.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"
/>
</svg>
</div>
<!-- Discord -->
<div class="channel-icon" data-tip="Chat via Discord">
<svg viewBox="0 0 24 24">
<path
d="M20.317 4.37a19.791 19.791 0 00-4.885-1.515.074.074 0 00-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 00-5.487 0 12.64 12.64 0 00-.617-1.25.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 00-.041-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128c.126-.094.252-.192.372-.292a.074.074 0 01.077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 01.078.01c.12.098.246.198.373.292a.077.077 0 01-.006.127 12.299 12.299 0 01-1.873.892.077.077 0 00-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 00.084.028 19.839 19.839 0 006.002-3.03.077.077 0 00.032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 00-.031-.03z"
/>
</svg>
</div>
<!-- Slack -->
<div class="channel-icon" data-tip="Chat via Slack">
<svg viewBox="0 0 24 24">
<path
d="M5.042 15.165a2.528 2.528 0 01-2.52 2.523A2.528 2.528 0 010 15.165a2.527 2.527 0 012.522-2.52h2.52v2.52zm1.271 0a2.527 2.527 0 012.521-2.52 2.527 2.527 0 012.521 2.52v6.313A2.528 2.528 0 018.834 24a2.528 2.528 0 01-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 01-2.521-2.52A2.528 2.528 0 018.834 0a2.528 2.528 0 012.521 2.522v2.52H8.834zm0 1.271a2.528 2.528 0 012.521 2.521 2.528 2.528 0 01-2.521 2.521H2.522A2.528 2.528 0 010 8.834a2.528 2.528 0 012.522-2.521h6.312zm10.122 2.521a2.528 2.528 0 012.522-2.521A2.528 2.528 0 0124 8.834a2.528 2.528 0 01-2.522 2.521h-2.522V8.834zm-1.268 0a2.528 2.528 0 01-2.523 2.521 2.527 2.527 0 01-2.52-2.521V2.522A2.527 2.527 0 0115.165 0a2.528 2.528 0 012.523 2.522v6.312zm-2.523 10.122a2.528 2.528 0 012.523 2.522A2.528 2.528 0 0115.165 24a2.527 2.527 0 01-2.52-2.522v-2.522h2.52zm0-1.268a2.527 2.527 0 01-2.52-2.523 2.526 2.526 0 012.52-2.52h6.313A2.527 2.527 0 0124 15.165a2.528 2.528 0 01-2.522 2.523h-6.313z"
/>
</svg>
</div>
<!-- WhatsApp -->
<div class="channel-icon" data-tip="Chat via WhatsApp">
<svg viewBox="0 0 24 24">
<path
d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"
/>
</svg>
</div>
<!-- Web Dashboard -->
<div class="channel-icon" data-tip="Web Dashboard">
<svg
class="stroke-icon"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
<line x1="8" y1="21" x2="16" y2="21" />
<line x1="12" y1="17" x2="12" y2="21" />
</svg>
</div>
<!-- QR / Mobile -->
<div class="channel-icon" data-tip="Scan QR to go mobile">
<svg
class="stroke-icon"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect x="2" y="2" width="8" height="8" rx="1" />
<rect x="14" y="2" width="8" height="8" rx="1" />
<rect x="2" y="14" width="8" height="8" rx="1" />
<rect x="14" y="14" width="4" height="4" rx="0.5" />
<line x1="22" y1="14" x2="22" y2="18" />
<line x1="18" y1="22" x2="22" y2="22" />
</svg>
</div>
</div>
</div>
</div>
</section>
<div class="section-break"><i></i><i></i><i></i></div>
<!-- ───────────── THE PROBLEM ───────────── -->
<section class="padded">
<div class="wrap" style="text-align: center">
<div class="wrap--sm" style="margin: 0 auto; padding: 0">
<span class="tag">The Problem</span>
<p class="problem-lead scroll-text heading-reveal">
Most AI agents take thirty minutes to set up. Docker, OAuth,
tunneling, fifty config options, all before you send your first
message.
</p>
<p class="problem-body">
Your API keys sit in plain&#8209;text config files. Dashboards get
exposed on Shodan. Safety controls can be toggled via API while the
agent runs. You install hundreds of packages whether you need them
or not. One bad dependency update breaks everything, even features
you never asked for.
</p>
</div>
<!-- Visual comparison — itemized breakdown -->
<div class="compare-panel">
<div class="compare-left">
<div class="compare-heading">
<svg
class="compare-heading-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="10" />
<line x1="15" y1="9" x2="9" y2="15" />
<line x1="9" y1="9" x2="15" y2="15" />
</svg>
<span>Typical AI Agent</span>
</div>
<ul class="compare-steps">
<li class="compare-step">
<span class="compare-step-name"
><span class="compare-step-num">1</span> Provision a VM or
VPS</span
>
<span class="compare-step-time">10 min</span>
</li>
<li class="compare-step">
<span class="compare-step-name"
><span class="compare-step-num">2</span> SSH setup &
authentication</span
>
<span class="compare-step-time">5 min</span>
</li>
<li class="compare-step">
<span class="compare-step-name"
><span class="compare-step-num">3</span> Clone repo & Docker
setup</span
>
<span class="compare-step-time">8 min</span>
</li>
<li class="compare-step">
<span class="compare-step-name"
><span class="compare-step-num">4</span> Edit 50+ config
variables</span
>
<span class="compare-step-time">5 min</span>
</li>
<li class="compare-step">
<span class="compare-step-name"
><span class="compare-step-num">5</span> Set up OAuth &
tunneling</span
>
<span class="compare-step-time">10 min</span>
</li>
<li class="compare-step">
<span class="compare-step-name"
><span class="compare-step-num">6</span> Install 200+
dependencies</span
>
<span class="compare-step-time">8 min</span>
</li>
<li class="compare-step">
<span class="compare-step-name"
><span class="compare-step-num">7</span> Debug dependency
conflicts</span
>
<span class="compare-step-time">10 min</span>
</li>
<li class="compare-step">
<span class="compare-step-name"
><span class="compare-step-num">8</span> Configure AI
provider</span
>
<span class="compare-step-time">4 min</span>
</li>
</ul>
<div class="compare-total">
<span class="compare-total-label">Total</span>
<span class="compare-total-time">~60 min</span>
</div>
</div>
<div class="compare-right">
<div class="compare-heading">
<svg
class="compare-heading-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
<polyline points="22 4 12 14.01 9 11.01" />
</svg>
<span>PocketPaw</span>
</div>
<div class="compare-hero-time">30<small>s</small></div>
<div class="compare-hero-unit">to your first message</div>
<div class="compare-right-cmd">
<span class="prompt">$ </span>curl -fsSL https://pocketpaw.xyz/install.sh | sh
</div>
<ul class="compare-checks">
<li><span class="check">&#10003;</span> Dashboard running</li>
<li><span class="check">&#10003;</span> Credentials encrypted</li>
<li><span class="check">&#10003;</span> Channels connected</li>
<li><span class="check">&#10003;</span> Memory initialized</li>
<li><span class="check">&#10003;</span> Ready to chat</li>
</ul>
<div class="compare-badges">
<span class="compare-badge--green compare-badge">Fast</span>
<span class="compare-badge--green compare-badge">Secure</span>
<span class="compare-badge">Modular</span>
<span class="compare-badge--honey compare-badge">Encrypted</span>
<span class="compare-badge">Open Source</span>
</div>
</div>
</div>
<p class="compare-footer">
<strong>One command.</strong> No Docker. No tunneling. No config
files.
</p>
</div>
</section>
<div class="section-break"><i></i><i></i><i></i></div>
<!-- ───────────── THE SOLUTION ───────────── -->
<section class="padded">
<div class="wrap">
<div>
<span class="tag">The Solution</span>
<h2 class="heading scroll-text">
Batteries included,<br />
<span class="honey">choose your pack.</span>
</h2>
<p class="s-desc">
One install gives you everything you need. Add channels when your
life demands them. Never carry what you don't use.
</p>
</div>
<div>
<div
class="terminal terminal--multi"
style="max-width: 700px; margin-top: 40px"
>
<div class="terminal-bar"><i></i><i></i><i></i></div>
<div class="terminal-body">
<code
><span class="prompt">$</span> curl -fsSL https://pocketpaw.xyz/install.sh | sh
<span class="comment"># Interactive installer</span>
<span class="prompt">$</span> pip install pocketpaw<span
class="hl"
>[memory]</span
>
<span class="comment"># Add Mem0 semantic search</span>
<span class="prompt">$</span> pip install pocketpaw<span
class="hl"
>[all]</span
>
<span class="comment"># Everything, all channels</span></code
>
</div>
</div>
</div>
<div class="core-includes">
<p class="core-label">The full includes:</p>
<div class="core-pills">
<span>Web Dashboard</span>
<span>Command Center</span>
<span>Telegram Bot</span>
<span>3 LLM Backends</span>
<span>Encrypted Credentials</span>
<span>Persistent Memory</span>
<span>Browser Automation</span>
<span>Guardian AI</span>
<span>Skills</span>
<span>Audit Log</span>
</div>
</div>
<div class="quick-stats">
<div>
<div class="qs-val">~30</div>
<div class="qs-lbl">Packages</div>
</div>
<div>
<div class="qs-val">~80<small>MB</small></div>
<div class="qs-lbl">Footprint</div>
</div>
<div>
<div class="qs-val">30<small>s</small></div>
<div class="qs-lbl">To Install</div>
</div>
</div>
</div>
</section>
<div class="section-break"><i></i><i></i><i></i></div>
<!-- ───────────── FEATURES ───────────── -->
<section class="padded">
<div class="wrap">
<div>
<span class="tag">The Thin Core</span>
<h2 class="heading scroll-text">
Everything you need.<br />
<span class="dim">Add the rest later.</span>
</h2>
</div>
<div class="features">
<div class="f-card">
<div class="f-icon">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect x="3" y="3" width="7" height="7" />
<rect x="14" y="3" width="7" height="7" />
<rect x="3" y="14" width="7" height="7" />
<rect x="14" y="14" width="7" height="7" />
</svg>
</div>
<h3>Multi-Channel</h3>
<p>
Telegram, Discord, Slack, WhatsApp, and web dashboard. Same agent,
same context, every channel.
</p>
</div>
<div class="f-card">
<div class="f-icon">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect x="4" y="4" width="16" height="16" rx="2" />
<path d="M9 9h6v6H9z" />
<path
d="M9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3"
/>
</svg>
</div>
<h3>3 LLM Backends</h3>
<p>
Claude Agent SDK, OpenAI, or Ollama for fully local models. $0 API
cost with local inference.
</p>
</div>
<div class="f-card">
<div class="f-icon">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
</svg>
</div>
<h3>Encrypted Credentials</h3>
<p>
Fernet AES with machine-derived PBKDF2 key. Real encryption, not
base64 obfuscation.
</p>
</div>
<div class="f-card">
<div class="f-icon">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
<path d="M9 12l2 2 4-4" />
</svg>
</div>
<h3>Guardian AI</h3>
<p>
Secondary LLM reviews dangerous commands before execution. 3-tier
tool policy with deny-lists.
</p>
</div>
<div class="f-card">
<div class="f-icon">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<ellipse cx="12" cy="5" rx="9" ry="3" />
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
</svg>
</div>
<h3>Persistent Memory</h3>
<p>
Session history and long-term facts. Upgrade to Mem0 semantic
vector search when ready.
</p>
</div>
<div class="f-card">
<div class="f-icon">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="10" />
<line x1="2" y1="12" x2="22" y2="12" />
<path
d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"
/>
</svg>
</div>
<h3>Browser Automation</h3>
<p>
Playwright-powered with accessibility tree snapshots. Navigate,
extract, interact.
</p>
</div>
<div class="f-card">
<div class="f-icon">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"
/>
<polyline points="3.27 6.96 12 12.01 20.73 6.96" />
<line x1="12" y1="22.08" x2="12" y2="12" />
</svg>
</div>
<h3>Modular Install</h3>
<p>
pip extras for genuine dependency isolation. Toggle channels from
the dashboard. Auto-install, no restart.
</p>
</div>
<div class="f-card">
<div class="f-icon">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M19.439 7.85c-.049.322.059.648.289.878l1.568 1.568c.47.47.706 1.087.706 1.704s-.235 1.233-.706 1.704l-1.611 1.611a.98.98 0 0 1-.837.276c-.47-.07-.802-.48-.968-.925a2.501 2.501 0 1 0-3.214 3.214c.446.166.855.497.925.968a.979.979 0 0 1-.276.837l-1.61 1.61a2.404 2.404 0 0 1-1.705.707 2.402 2.402 0 0 1-1.704-.706l-1.568-1.568a1.026 1.026 0 0 0-.877-.29c-.493.074-.84.504-1.02.968a2.5 2.5 0 1 1-3.237-3.237c.464-.18.894-.527.967-1.02a1.026 1.026 0 0 0-.289-.877l-1.568-1.568A2.402 2.402 0 0 1 1.998 12c0-.617.236-1.234.706-1.704L4.23 8.77c.24-.24.581-.353.917-.303.515.077.877.528 1.073 1.01a2.5 2.5 0 1 0 3.259-3.259c-.482-.196-.933-.558-1.01-1.073-.05-.336.062-.676.303-.917l1.525-1.525A2.402 2.402 0 0 1 12 1.998c.617 0 1.234.236 1.704.706l1.568 1.568c.23.23.556.338.877.29.493-.074.84-.504 1.02-.968a2.5 2.5 0 1 1 3.237 3.237c-.464.18-.894.527-.967 1.02Z"
/>
</svg>
</div>
<h3>Skills</h3>
<p>
Self-installing plugin system. Add capabilities from a registry or
build your own. The agent installs what it needs.
</p>
</div>
<div class="f-card">
<div class="f-icon">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"
/>
<polyline points="14 2 14 8 20 8" />
<line x1="16" y1="13" x2="8" y2="13" />
<line x1="16" y1="17" x2="8" y2="17" />
<polyline points="10 9 9 9 8 9" />
</svg>
</div>
<h3>Audit Log</h3>
<p>
Append-only JSONL. Every tool execution logged with severity
levels. Tamper-evident by design.
</p>
</div>
</div>
</div>
</section>
<div class="section-break"><i></i><i></i><i></i></div>
<!-- ───────────── SECURITY ───────────── -->
<section class="padded">
<div class="wrap">
<div class="sec-grid">
<div>
<span class="tag">Security</span>
<h2 class="heading scroll-text">
Security was the<br />
<span class="honey">starting point.</span>
</h2>
<p class="sec-intro">
Your AI agent has terminal access, file access, browser control,
and your API keys. The security architecture should match that
power.
</p>
</div>
<div>
<div class="sec-item">
<div class="sec-dot"></div>
<div>
<div class="sec-name">Fernet AES encrypted credentials</div>
<div class="sec-detail">
Machine-derived PBKDF2 key. Auto-migrates from plain text.
</div>
</div>
</div>
<div class="sec-item">
<div class="sec-dot"></div>
<div>
<div class="sec-name">3-tier tool policy with deny-lists</div>
<div class="sec-detail">
Minimal, coding, full. Deny always wins. No override possible.
</div>
</div>
</div>
<div class="sec-item">
<div class="sec-dot"></div>
<div>
<div class="sec-name">Guardian AI safety check</div>
<div class="sec-detail">
Secondary LLM reviews dangerous commands before execution.
</div>
</div>
</div>
<div class="sec-item">
<div class="sec-dot"></div>
<div>
<div class="sec-name">Append-only audit log</div>
<div class="sec-detail">
Every tool execution. Tamper-evident. Severity levels.
</div>
</div>
</div>
<div class="sec-item">
<div class="sec-dot"></div>
<div>
<div class="sec-name">No exposed ports by default</div>
<div class="sec-detail">
Telegram-first. Nothing for Shodan to find.
</div>
</div>
</div>
<div class="sec-item">
<div class="sec-dot"></div>
<div>
<div class="sec-name">WebSocket auth via message body</div>
<div class="sec-detail">
Not URL params. Not in logs. Not in scan results.
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<div class="section-break"><i></i><i></i><i></i></div>
<!-- ───────────── COMMAND CENTER ───────────── -->
<section class="padded" id="command-center">
<div class="wrap">
<div style="text-align: center">
<span class="tag">Command Center</span>
<h2 class="heading scroll-text">
One agent is useful.<br />
<span class="honey">A team gets real work done.</span>
</h2>
<p class="cc-desc" style="margin: 14px auto 0">
Describe your goal. Command Center breaks it into small meaningful
tasks, assigns agents, streams progress live, and pauses for your
approval before anything ships.
</p>
</div>
<!-- Example selector pills -->
<div class="cc-examples">
<button class="cc-ex-btn active" data-example="0">
Morning Briefing
</button>
<button class="cc-ex-btn" data-example="1">Content Pipeline</button>
<button class="cc-ex-btn" data-example="2">Bug Fix from Phone</button>
<button class="cc-ex-btn" data-example="3">Guest Booking</button>
<button class="cc-ex-btn" data-example="4">Security Audit</button>
</div>
<!-- Dark dashboard mockup -->
<div class="cc-panel" id="cc-panel">
<div class="cc-topbar">
<div class="cc-topbar-left">
<div class="cc-topbar-dots"><i></i><i></i><i></i></div>
<span class="cc-topbar-title">command-center</span>
</div>
<div class="cc-agents" id="cc-agents"></div>
<div class="cc-topbar-status">Live</div>
</div>
<!-- User prompt bubble -->
<div class="cc-prompt">
<div class="cc-prompt-avatar">You</div>
<div class="cc-prompt-text" id="cc-prompt-text">
<span class="cc-prompt-cursor"></span>
</div>
</div>
<!-- Flow header (hidden until typing done) -->
<div class="cc-header" id="cc-header">
<div>
<div class="cc-flow-name" id="cc-flow-name"></div>
<div class="cc-flow-meta" id="cc-flow-meta"></div>
</div>
<div class="cc-progress-wrap">
<div class="cc-progress-pct" id="cc-progress-pct">0%</div>
<div class="cc-progress-lbl">Progress</div>
</div>
</div>
<div class="cc-bar">
<div class="cc-bar-fill" id="cc-bar-fill"></div>
</div>
<!-- Node cards (5 nodes: idea→breakdown→execute→approve→complete) -->
<div class="cc-nodes" id="cc-nodes">
<div class="cc-node st-pending" data-node="0">
<div class="cc-node-badge badge-pending">Pending</div>
<div class="cc-node-name"></div>
<div class="cc-node-agent"></div>
</div>
<div class="cc-node st-pending" data-node="1">
<div class="cc-node-badge badge-pending">Pending</div>
<div class="cc-node-name"></div>
<div class="cc-node-agent"></div>
</div>
<div class="cc-node st-pending" data-node="2">
<div class="cc-node-badge badge-pending">Pending</div>
<div class="cc-node-name"></div>
<div class="cc-node-agent"></div>
</div>
<div class="cc-node st-pending" data-node="3">
<div class="cc-node-badge badge-pending">Pending</div>
<div class="cc-node-name"></div>
<div class="cc-node-agent"></div>
</div>
<div class="cc-node st-pending" data-node="4">
<div class="cc-node-badge badge-pending">Pending</div>
<div class="cc-node-name"></div>
<div class="cc-node-agent"></div>
</div>
</div>
</div>
</div>
</section>
<div class="section-break"><i></i><i></i><i></i></div>
<!-- ───────────── THE NUMBERS ───────────── -->
<section class="padded">
<div class="wrap">
<div style="text-align: center">
<span class="tag">By The Numbers</span>
</div>
<div class="nums">
<div>
<div class="n-val">5</div>
<div class="n-lbl">Channels</div>
</div>
<div>
<div class="n-val">3</div>
<div class="n-lbl">LLM Backends</div>
</div>
<div>
<div class="n-val">30<small>s</small></div>
<div class="n-lbl">To First Message</div>
</div>
<div>
<div class="n-val">~30</div>
<div class="n-lbl">Packages</div>
</div>
<div>
<div class="n-val">~80<small>MB</small></div>
<div class="n-lbl">Memory</div>
</div>
<div>
<div class="n-val">MIT</div>
<div class="n-lbl">Licensed</div>
</div>
</div>
</div>
</section>
<div class="section-break"><i></i><i></i><i></i></div>
<!-- ───────────── CTA ───────────── -->
<section class="cta">
<div class="wrap">
<div>
<p class="cta-meta">
Open Source &middot; MIT Licensed &middot; No Telemetry &middot; No
Cloud Dependency
</p>
<h2 class="heading scroll-text" style="margin-bottom: 40px">
Your agent.<br />
Your machine.<br />
<span class="blue">Your rules.</span>
</h2>
</div>
<div>
<div class="terminal">
<div class="terminal-bar"><i></i><i></i><i></i></div>
<div class="terminal-body">
<code
><span class="prompt">$</span> curl -fsSL https://pocketpaw.xyz/install.sh | sh</code
>
<button class="copy-btn" onclick="copyCmd(this)">
<svg
class="copy-icon"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect x="9" y="9" width="13" height="13" rx="2" />
<path
d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"
/></svg
><span class="copy-label">Copy</span>
</button>
</div>
</div>
</div>
<div class="hero-ctas" style="margin-top: 36px">
<a
href="https://github.com/pocketpaw/pocketpaw"
target="_blank"
rel="noopener"
class="btn btn--fill"
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"
/>
</svg>
View on GitHub
</a>
<a href="#" class="btn btn--ghost"
><!-- TODO: replace # with PH URL once live -->
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polygon
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
/>
</svg>
Product Hunt
</a>
</div>
</div>
</section>
<!-- ───────────── FOOTER ───────────── -->
<footer>
<div class="wrap foot">
<div class="foot-left">
<span
class="foot-logo"
style="font-family: &quot;Fraunces&quot;, serif"
>PocketPaw</span
>
<span class="foot-sep">|</span>
<span class="foot-sm">Built by You &amp; Us</span>
</div>
<div class="foot-links">
<a href="/introduction" class="foot-a">Docs</a>
<a
href="https://github.com/pocketpaw/pocketpaw"
target="_blank"
rel="noopener"
class="foot-a"
>GitHub</a
>
<a href="#" class="foot-a">Product Hunt</a
><!-- TODO: replace # with PH URL -->
<span class="foot-sm">MIT License</span>
</div>
</div>
</footer>
<!-- ───────────── JS ───────────── -->
<script>
/* ── Nav scroll ── */
var nav = document.getElementById("nav");
window.addEventListener(
"scroll",
function () {
nav.classList.toggle("scrolled", window.scrollY > 50);
},
{ passive: true },
);
/* ── Copy ── */
function copyCmd(btn) {
navigator.clipboard
.writeText("curl -fsSL https://pocketpaw.xyz/install.sh | sh")
.then(function () {
var label = btn.querySelector(".copy-label");
if (label) label.textContent = "Copied";
btn.classList.add("copied");
setTimeout(function () {
if (label) label.textContent = "Copy";
btn.classList.remove("copied");
}, 2000);
});
}
/* Load GSAP dynamically to bypass Vite/Astro strict-mode script processing */
(function () {
function loadScript(src) {
return new Promise(function (resolve, reject) {
var s = document.createElement("script");
s.src = src;
s.onload = resolve;
s.onerror = reject;
document.head.appendChild(s);
});
}
loadScript(
"https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js",
)
.then(function () {
return loadScript(
"https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js",
);
})
.then(initGSAP)
.catch(function (e) {
console.warn("GSAP failed to load:", e);
});
function initGSAP() {
gsap.registerPlugin(ScrollTrigger);
/* ── Mascot scroll parallax (blur + fade, scrub-tied) ── */
const mascot = document.querySelector(".mascot-img");
if (mascot) {
gsap.to(mascot, {
filter: "drop-shadow(0 20px 50px rgba(80,60,30,0.18)) blur(6px)",
opacity: 0.5,
ease: "none",
scrollTrigger: {
trigger: ".hero",
start: "top top",
end: "bottom top",
scrub: true,
},
});
}
/* ── Word-by-word text reveal (1 timeline per block, not per word) ── */
document.querySelectorAll(".scroll-text").forEach((el) => {
const html = el.innerHTML;
const wrapped = html.replace(
/((&[^;]+;)|(<[^>]+>)|[^\s<]+)/g,
(match) => {
if (match.startsWith("<")) return match;
return '<span class="word">' + match + "</span>";
},
);
el.innerHTML = wrapped;
const words = el.querySelectorAll(".word");
if (!words.length) return;
// Single timeline for all words in this block
const tl = gsap.timeline({
scrollTrigger: {
trigger: el,
start: "top 85%",
end: "bottom 40%",
scrub: true,
},
});
tl.to(words, {
opacity: 1,
duration: 1,
stagger: 0.1,
ease: "none",
});
});
/* ════════════════════════════════════════
COMMAND CENTER — interactive cycling
════════════════════════════════════════ */
(function () {
const examples = [
{
prompt:
"Prepare my morning briefing: calendar, emails, and top priorities for today",
flowName: "Morning Briefing",
meta: "5 tasks \u00b7 5 agents \u00b7 ~2 min",
nodes: [
{
name: "Fetch Calendar",
agent: "Calendar agent",
badge: "done",
},
{ name: "Scan Inbox", agent: "Email agent", badge: "done" },
{
name: "Look Up Contacts",
agent: "Research agent",
badge: "done",
},
{
name: "Compile Briefing",
agent: "Writing agent",
badge: "done",
},
{
name: "Deliver to Slack",
agent: "Channel agent",
badge: "done",
},
],
},
{
prompt:
"Turn my latest podcast episode into shorts and schedule across Instagram, X, and TikTok",
flowName: "Content Pipeline",
meta: "5 tasks \u00b7 4 agents \u00b7 ~8 min",
nodes: [
{
name: "Download & Transcribe",
agent: "Media agent",
badge: "done",
},
{
name: "Identify Highlights",
agent: "Analysis agent",
badge: "done",
},
{
name: "Generate Clips",
agent: "Video agent",
badge: "done",
},
{
name: "Your Review",
agent: "Human checkpoint",
badge: "review",
},
{
name: "Schedule Posts",
agent: "Publishing agent",
badge: "done",
},
],
},
{
prompt:
"Someone screenshotted a bug on WhatsApp. Find it in the repo, fix it, and open a PR",
flowName: "Bug Fix from Phone",
meta: "5 tasks \u00b7 3 agents \u00b7 ~3 min",
nodes: [
{
name: "Analyze Screenshot",
agent: "Vision agent",
badge: "done",
},
{
name: "Locate in Codebase",
agent: "Coding agent",
badge: "done",
},
{
name: "Write Fix + Tests",
agent: "Coding agent",
badge: "done",
},
{
name: "Your Approval",
agent: "Human checkpoint",
badge: "review",
},
{ name: "Push & Open PR", agent: "Git agent", badge: "done" },
],
},
{
prompt:
"Find 5 trending AI guests for next week\u2019s podcast, vet them, and draft outreach emails",
flowName: "Guest Booking Pipeline",
meta: "5 tasks \u00b7 4 agents \u00b7 ~12 min",
nodes: [
{
name: "Scan News & X",
agent: "Research agent",
badge: "done",
},
{
name: "Vet Against Past Guests",
agent: "Database agent",
badge: "done",
},
{
name: "Find Contact Info",
agent: "LeadIQ agent",
badge: "done",
},
{
name: "Build Show Dockets",
agent: "Writing agent",
badge: "done",
},
{
name: "Your Approval",
agent: "Human checkpoint",
badge: "review",
},
],
},
{
prompt:
"Run an overnight security audit on our main repo. Check deps, vulnerabilities, and have a report ready by morning",
flowName: "Security Audit",
meta: "5 tasks \u00b7 3 agents \u00b7 ~15 min",
nodes: [
{
name: "Clone & Scan Repo",
agent: "Git agent",
badge: "done",
},
{
name: "Check Dependencies",
agent: "Security agent",
badge: "done",
},
{
name: "Analyze Vulnerabilities",
agent: "Security agent",
badge: "done",
},
{
name: "Your Review",
agent: "Human checkpoint",
badge: "review",
},
{
name: "Generate Report",
agent: "Artifacts agent",
badge: "done",
},
],
},
];
const promptEl = document.getElementById("cc-prompt-text");
const headerEl = document.getElementById("cc-header");
const flowNameEl = document.getElementById("cc-flow-name");
const flowMetaEl = document.getElementById("cc-flow-meta");
const progressPctEl = document.getElementById("cc-progress-pct");
const barFillEl = document.getElementById("cc-bar-fill");
const nodesEl = document.getElementById("cc-nodes");
const nodeCards = nodesEl.querySelectorAll(".cc-node");
const exBtns = document.querySelectorAll(".cc-ex-btn");
const agentsEl = document.getElementById("cc-agents");
let currentExample = 0;
let animating = false;
let autoTimer = null;
let hasBeenVisible = false;
// Agent color palette (consistent per agent name)
const agentColors = {
"Calendar agent": "#3fae6f",
"Email agent": "#8b5cf6",
"Research agent": "#4a6fa5",
"Writing agent": "#d49a5c",
"Channel agent": "#06b6d4",
"Media agent": "#ec4899",
"Analysis agent": "#6366f1",
"Video agent": "#ef4444",
"Publishing agent": "#f59e0b",
"Vision agent": "#a78bfa",
"Coding agent": "#10b981",
"Git agent": "#f97316",
"Database agent": "#64748b",
"LeadIQ agent": "#22d3ee",
"Security agent": "#dc2626",
"Artifacts agent": "#c9a96e",
"Build agent": "#78716c",
"Formatting agent": "#84cc16",
"Human checkpoint": "#febc2e",
};
function agentInitials(name) {
return name
.replace(" agent", "")
.replace(" checkpoint", "")
.slice(0, 2)
.toUpperCase();
}
function getUniqueAgents(ex) {
const seen = new Set();
const out = [];
ex.nodes.forEach((n) => {
if (n.agent === "Human checkpoint") return;
if (!seen.has(n.agent)) {
seen.add(n.agent);
out.push(n.agent);
}
});
return out;
}
function renderAgents(ex) {
agentsEl.innerHTML = "";
const agents = getUniqueAgents(ex);
const show = Math.min(agents.length, 4);
const extra = agents.length - show;
for (let i = 0; i < show; i++) {
const a = agents[i];
const el = document.createElement("div");
el.className = "cc-agent-av";
Object.assign(el.style, {
background: agentColors[a] || "#4a6fa5",
width: "26px",
height: "26px",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: ".52rem",
fontWeight: "700",
color: "#fff",
border: "2px solid #13131f",
marginLeft: i === 0 ? "0" : "-7px",
opacity: "0",
transform: "scale(0.4)",
transition: "opacity .35s ease,transform .35s ease",
position: "relative",
cursor: "default",
});
var tip = document.createElement("span");
tip.className = "cc-av-tip";
tip.textContent = a;
Object.assign(tip.style, {
position: "absolute",
bottom: "calc(100% + 6px)",
left: "50%",
transform: "translateX(-50%)",
background: "#1e1e30",
color: "rgba(255,255,255,0.75)",
fontSize: ".58rem",
fontWeight: "500",
letterSpacing: ".01em",
padding: "4px 8px",
borderRadius: "5px",
whiteSpace: "nowrap",
opacity: "0",
pointerEvents: "none",
transition: "opacity .2s",
});
el.textContent = agentInitials(a);
el.appendChild(tip);
el.addEventListener("mouseenter", function () {
tip.style.opacity = "1";
});
el.addEventListener("mouseleave", function () {
tip.style.opacity = "0";
});
agentsEl.appendChild(el);
setTimeout(
(function (e) {
return function () {
e.style.opacity = "1";
e.style.transform = "scale(1)";
};
})(el),
120 + i * 80,
);
}
if (extra > 0) {
const c = document.createElement("div");
c.className = "cc-agent-count";
c.textContent = "+" + extra;
Object.assign(c.style, {
width: "26px",
height: "26px",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: ".52rem",
fontWeight: "700",
color: "rgba(255,255,255,0.5)",
background: "rgba(255,255,255,0.08)",
border: "2px solid #13131f",
marginLeft: "-7px",
opacity: "0",
transform: "scale(0.4)",
transition: "opacity .35s ease,transform .35s ease",
});
agentsEl.appendChild(c);
setTimeout(
(function (e) {
return function () {
e.style.opacity = "1";
e.style.transform = "scale(1)";
};
})(c),
120 + show * 80,
);
}
}
function resetPanel() {
// Hide header
headerEl.classList.remove("visible");
progressPctEl.textContent = "0%";
barFillEl.style.width = "0%";
barFillEl.style.transition = "none";
// Reset prompt
promptEl.innerHTML = '<span class="cc-prompt-cursor"></span>';
// Clear agent avatars
agentsEl.innerHTML = "";
// Reset all nodes
nodeCards.forEach((card) => {
card.className = "cc-node st-pending";
const badge = card.querySelector(".cc-node-badge");
badge.className = "cc-node-badge badge-pending";
badge.textContent = "Pending";
});
}
function typePrompt(text, cb) {
let i = 0;
promptEl.innerHTML = "";
const cursor = document.createElement("span");
cursor.className = "cc-prompt-cursor";
promptEl.appendChild(cursor);
function next() {
if (i < text.length) {
promptEl.insertBefore(
document.createTextNode(text[i]),
cursor,
);
i++;
setTimeout(next, 25 + Math.random() * 20);
} else {
setTimeout(() => {
cursor.remove();
cb();
}, 300);
}
}
next();
}
function showHeader(ex) {
flowNameEl.textContent = ex.flowName;
flowMetaEl.textContent = ex.meta;
headerEl.classList.add("visible");
}
function setNodeState(card, state, label) {
card.className = "cc-node st-" + state;
const badge = card.querySelector(".cc-node-badge");
badge.className =
"cc-node-badge badge-" +
(state === "done"
? "done"
: state === "active"
? "running"
: state === "review"
? "review"
: "pending");
badge.textContent = label;
}
function populateNodeContent(ex) {
nodeCards.forEach((card, i) => {
card.querySelector(".cc-node-name").textContent =
ex.nodes[i].name;
card.querySelector(".cc-node-agent").textContent =
ex.nodes[i].agent;
});
}
function animateNodes(ex, cb) {
const nodes = ex.nodes;
let step = 0;
const totalSteps = nodes.length;
function tick() {
if (step >= totalSteps) {
setTimeout(cb, 1800);
return;
}
const card = nodeCards[step];
const node = nodes[step];
const isReview = node.badge === "review";
// Mark current as active
setNodeState(card, "active", "Running");
// Update progress
const pct = Math.round(((step + 0.5) / totalSteps) * 100);
barFillEl.style.transition =
"width 0.8s cubic-bezier(.22,1,.36,1)";
barFillEl.style.width = pct + "%";
progressPctEl.textContent = pct + "%";
setTimeout(() => {
if (isReview) {
// Show as review first, then approve
setNodeState(card, "review", "Review");
const reviewPct = Math.round(
((step + 0.7) / totalSteps) * 100,
);
barFillEl.style.width = reviewPct + "%";
progressPctEl.textContent = reviewPct + "%";
setTimeout(() => {
setNodeState(card, "done", "Approved");
const donePct = Math.round(
((step + 1) / totalSteps) * 100,
);
barFillEl.style.width = donePct + "%";
progressPctEl.textContent = donePct + "%";
step++;
setTimeout(tick, 400);
}, 900);
} else {
setNodeState(card, "done", "Done");
const donePct = Math.round(((step + 1) / totalSteps) * 100);
barFillEl.style.width = donePct + "%";
progressPctEl.textContent = donePct + "%";
step++;
setTimeout(tick, 400);
}
}, 600);
}
tick();
}
function runExample(idx) {
if (animating) return;
animating = true;
currentExample = idx;
// Update pills
exBtns.forEach((b) => b.classList.remove("active"));
exBtns[idx].classList.add("active");
const ex = examples[idx];
resetPanel();
populateNodeContent(ex);
renderAgents(ex);
// Step 1: Type the prompt
setTimeout(() => {
typePrompt(ex.prompt, () => {
// Step 2: Show header
showHeader(ex);
// Step 3: Animate nodes sequentially
setTimeout(() => {
animateNodes(ex, () => {
animating = false;
// Auto-cycle to next after pause
autoTimer = setTimeout(() => {
runExample((currentExample + 1) % examples.length);
}, 2200);
});
}, 500);
});
}, 200);
}
// Pill click handlers
exBtns.forEach((btn) => {
btn.addEventListener("click", () => {
if (autoTimer) clearTimeout(autoTimer);
animating = false; // force-stop current animation
runExample(parseInt(btn.dataset.example));
});
});
// Start when section scrolls into view
ScrollTrigger.create({
trigger: "#command-center",
start: "top 75%",
once: true,
onEnter: () => {
if (!hasBeenVisible) {
hasBeenVisible = true;
runExample(0);
}
},
});
})();
} /* end initGSAP */
})();
</script>
<!-- ── Gradient color picker (easter egg — Shift+C) ── -->
<div class="grad-picker">
<button data-v="prism" class="active">Prism</button>
<button data-v="denim">Denim</button>
<button data-v="aurora">Aurora</button>
<button data-v="sunset">Sunset</button>
<button data-v="ice">Ice</button>
<button data-v="fire">Fire</button>
<button data-v="mono">Mono</button>
<div class="auto-btn" title="Auto-rotate colors">&#x21bb;</div>
</div>
<script>
(function () {
const themes = [
"prism",
"denim",
"aurora",
"sunset",
"ice",
"fire",
"mono",
];
let autoInterval = null;
let currentIdx = 0;
const picker = document.querySelector(".grad-picker");
const autoBtn = picker.querySelector(".auto-btn");
const allBtns = picker.querySelectorAll("button");
function setTheme(name) {
document.documentElement.setAttribute("data-grad", name);
allBtns.forEach((b) =>
b.classList.toggle("active", b.dataset.v === name),
);
currentIdx = themes.indexOf(name);
}
// Click a swatch
picker.addEventListener("click", (e) => {
const btn = e.target.closest("button");
if (!btn) return;
stopAuto();
setTheme(btn.dataset.v);
});
// Auto-rotate
function startAuto() {
autoBtn.classList.add("active");
autoInterval = setInterval(() => {
currentIdx = (currentIdx + 1) % themes.length;
setTheme(themes[currentIdx]);
}, 4000);
}
function stopAuto() {
autoBtn.classList.remove("active");
if (autoInterval) {
clearInterval(autoInterval);
autoInterval = null;
}
}
autoBtn.addEventListener("click", () => {
if (autoInterval) {
stopAuto();
} else {
startAuto();
}
});
// Shift+C toggle picker
document.addEventListener("keydown", (e) => {
if (e.shiftKey && e.code === "KeyC") {
picker.classList.toggle("visible");
}
});
})();
</script>
<!-- Floating Beta Badge -->
<div style="position:fixed;bottom:20px;right:20px;z-index:9999;background:rgba(10,132,255,.12);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(10,132,255,.25);border-radius:8px;padding:6px 14px;font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;font-weight:600;letter-spacing:.04em;color:#0A84FF;pointer-events:none;user-select:none;text-transform:uppercase">beta</div>
</body>
</html>