Ledger Card Hero
A photoreal CSS-3D metal card that tilts to the cursor, then flips and docks on scroll while spend stats count up.
A terminal types a prompt and streams an agent's plan while the generated dashboard assembles itself beside it.
npx shadcn@latest add https://freedesignmd.com/r/hero-ai-terminal.json1/**2 * HeroAiTerminal — a dark developer-tools hero for an AI coding agent. A3 * terminal window types the user's prompt, streams the agent's plan line by4 * line with tool calls and diffs, and a live preview beside it assembles the5 * generated UI: cards slide into a grid, a chart draws, a toast lands. The6 * sequence loops with a short rest. The whole composition tilts slightly to7 * the pointer and, on scroll, the windows separate in depth while the headline8 * parallaxes away.9 *10 * Props: title, lede, prompt (string typed into the terminal), plan (lines),11 * primaryCta, secondaryCta, loop (default true).12 * CSS vars: --fd-agt-bg, --fd-agt-accent, --fd-agt-mono.13 *14 * Browser support: all evergreen browsers.15 * Motion: one rAF scheduler drives typing/streaming (paused off-screen and in16 * hidden tabs), pointer tilt via spring (k=380, c=30), scroll via rAF +17 * getBoundingClientRect (transform/opacity only). prefers-reduced-motion18 * shows the finished state instantly with no loop.19 * Accessibility: the terminal is an aria-hidden illustration; the prompt and20 * plan are summarised in a visually hidden paragraph.21 */22import { useEffect, useId, useRef, useState } from "react";23import type { ReactNode } from "react";24 25export type AgentLine =26 | { kind: "think"; text: string }27 | { kind: "tool"; name: string; arg: string; meta?: string }28 | { kind: "diff"; file: string; add: number; del: number }29 | { kind: "ok"; text: string };30 31export interface HeroAiTerminalProps {32 eyebrow?: ReactNode;33 title?: ReactNode;34 lede?: ReactNode;35 prompt?: string;36 plan?: AgentLine[];37 primaryCta?: { label: string; href: string };38 secondaryCta?: { label: string; href: string };39 loop?: boolean;40}41 42const DEFAULT_PLAN: AgentLine[] = [43 { kind: "think", text: "Reading schema for `invoices` and `customers`…" },44 { kind: "tool", name: "read", arg: "db/schema.prisma", meta: "142 lines" },45 { kind: "tool", name: "grep", arg: "\"useMetrics\" src/", meta: "3 hits" },46 { kind: "think", text: "Plan: KPI row, MRR chart, churn list, live toast." },47 { kind: "diff", file: "app/billing/page.tsx", add: 118, del: 4 },48 { kind: "diff", file: "components/kpi-card.tsx", add: 46, del: 0 },49 { kind: "tool", name: "test", arg: "billing.spec.ts", meta: "12 passed" },50 { kind: "ok", text: "Preview ready · 2.8s" },51];52 53const PROMPT = "build a billing dashboard: MRR, churn and failed payments — use our design tokens";54 55// timeline (ms)56const T_TYPE_START = 500;57const T_CHAR = 26;58const T_LINE = 380;59 60export function HeroAiTerminal({61 eyebrow = "Fathom Agent 3 — now with background tasks",62 title = (63 <>64 Describe it.65 <br />66 <span className="fd-agt-dim">Review it.</span> Ship it.67 </>68 ),69 lede = "An autonomous engineer that reads your codebase, writes the change, runs the tests and opens the pull request — while you stay in flow.",70 prompt = PROMPT,71 plan = DEFAULT_PLAN,72 primaryCta = { label: "Install for VS Code", href: "#install" },73 secondaryCta = { label: "npx fathom init", href: "#cli" },74 loop = true,75}: HeroAiTerminalProps) {76 const root = useRef<HTMLElement>(null);77 const scene = useRef<HTMLDivElement>(null);78 const term = useRef<HTMLDivElement>(null);79 const prev = useRef<HTMLDivElement>(null);80 const copyRef = useRef<HTMLDivElement>(null);81 const [typed, setTyped] = useState(0);82 const [lines, setLines] = useState(0);83 const [build, setBuild] = useState(0); // 0..6 preview pieces84 const [copied, setCopied] = useState(false);85 const gid = `fd-agt-g${useId().replace(/[^a-zA-Z0-9]/g, "")}`;86 87 // sequencing88 useEffect(() => {89 const el = root.current;90 if (!el) return;91 if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {92 setTyped(prompt.length);93 setLines(plan.length);94 setBuild(6);95 return;96 }97 const typeEnd = T_TYPE_START + prompt.length * T_CHAR;98 const planStart = typeEnd + 420;99 const planEnd = planStart + plan.length * T_LINE;100 const total = planEnd + 5200;101 let clock = 0;102 let last = 0;103 let raf = 0;104 let visible = true;105 const tick = (now: number) => {106 raf = 0;107 const dt = Math.min(now - last, 64);108 last = now;109 clock += dt;110 let t = clock;111 if (loop && t > total) {112 clock = 0;113 t = 0;114 }115 setTyped(Math.max(0, Math.min(prompt.length, Math.floor((t - T_TYPE_START) / T_CHAR))));116 setLines(Math.max(0, Math.min(plan.length, Math.floor((t - planStart) / T_LINE) + 1)));117 // preview builds in step with the diff lines118 const b = t < planStart + 3 * T_LINE ? 0 : Math.min(6, Math.floor((t - (planStart + 3 * T_LINE)) / 300) + 1);119 setBuild(b);120 if (visible && !document.hidden && (loop || t < total)) raf = requestAnimationFrame(tick);121 };122 const start = () => {123 if (!raf && visible && !document.hidden) {124 last = performance.now();125 raf = requestAnimationFrame(tick);126 }127 };128 const io = new IntersectionObserver(([e]) => {129 visible = e.isIntersecting;130 if (visible) start();131 });132 io.observe(el);133 document.addEventListener("visibilitychange", start);134 start();135 return () => {136 cancelAnimationFrame(raf);137 io.disconnect();138 document.removeEventListener("visibilitychange", start);139 };140 }, [prompt, plan, loop]);141 142 // pointer tilt + scroll depth143 useEffect(() => {144 const el = root.current;145 if (!el || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;146 const sp = { x: 0, y: 0, vx: 0, vy: 0, tx: 0, ty: 0 };147 const onMove = (e: PointerEvent) => {148 if (e.pointerType === "touch") return;149 const r = el.getBoundingClientRect();150 sp.tx = ((e.clientX - r.left) / r.width) * 2 - 1;151 sp.ty = ((e.clientY - r.top) / r.height) * 2 - 1;152 };153 const onLeave = () => {154 sp.tx = 0;155 sp.ty = 0;156 };157 el.addEventListener("pointermove", onMove);158 el.addEventListener("pointerleave", onLeave);159 let raf = 0;160 let last = performance.now();161 let visible = true;162 const frame = (now: number) => {163 raf = 0;164 const dt = Math.min((now - last) / 1000, 0.032);165 last = now;166 for (let i = 0; i < 2; i++) {167 const h = dt / 2;168 sp.vx += (-380 * (sp.x - sp.tx) - 30 * sp.vx) * h;169 sp.vy += (-380 * (sp.y - sp.ty) - 30 * sp.vy) * h;170 sp.x += sp.vx * h;171 sp.y += sp.vy * h;172 }173 const r = el.getBoundingClientRect();174 const p = Math.min(1, Math.max(0, -r.top / Math.max(1, r.height)));175 if (scene.current) scene.current.style.transform = `rotateX(${(6 - sp.y * 4 + p * 14).toFixed(2)}deg) rotateY(${(sp.x * 6).toFixed(2)}deg) translate3d(0, ${(p * -40).toFixed(1)}px, 0)`;176 if (term.current) term.current.style.transform = `translate3d(${(p * -60).toFixed(1)}px, 0, ${(30 + p * 60).toFixed(1)}px)`;177 if (prev.current) prev.current.style.transform = `translate3d(${(p * 80).toFixed(1)}px, ${(p * -30).toFixed(1)}px, ${(-20 - p * 120).toFixed(1)}px)`;178 if (copyRef.current) {179 copyRef.current.style.transform = `translate3d(0, ${(p * 120).toFixed(1)}px, 0)`;180 copyRef.current.style.opacity = `${(1 - p * 1.6).toFixed(3)}`;181 }182 if (visible && !document.hidden) raf = requestAnimationFrame(frame);183 };184 const start = () => {185 if (!raf && visible && !document.hidden) {186 last = performance.now();187 raf = requestAnimationFrame(frame);188 }189 };190 const io = new IntersectionObserver(([e]) => {191 visible = e.isIntersecting;192 if (visible) start();193 });194 io.observe(el);195 document.addEventListener("visibilitychange", start);196 start();197 return () => {198 cancelAnimationFrame(raf);199 io.disconnect();200 document.removeEventListener("visibilitychange", start);201 el.removeEventListener("pointermove", onMove);202 el.removeEventListener("pointerleave", onLeave);203 };204 }, []);205 206 const typing = typed < prompt.length;207 const done = lines >= plan.length;208 209 return (210 <section ref={root} className="fd-agt" aria-label="Fathom Agent">211 <style>{css}</style>212 <div className="fd-agt-bg" aria-hidden="true">213 <div className="fd-agt-grid" />214 <div className="fd-agt-beam" />215 </div>216 <div ref={copyRef} className="fd-agt-copy">217 <p className="fd-agt-eyebrow">218 <span className="fd-agt-dot" aria-hidden="true" />219 {eyebrow}220 </p>221 <h1 className="fd-agt-h1">{title}</h1>222 <p className="fd-agt-lede">{lede}</p>223 <div className="fd-agt-ctas">224 <a className="fd-agt-btn" href={primaryCta.href}>225 <svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true">226 <path d="M11.5 1.5 6 6.6 3 4.3 1.5 5v6l1.5.7L6 9.4l5.5 5.1 3-1.4v-10z M11.5 5v6L8 8z M3 6.4 4.7 8 3 9.6z" fill="currentColor" />227 </svg>228 {primaryCta.label}229 </a>230 <button231 type="button"232 className="fd-agt-cli"233 onClick={() => {234 navigator.clipboard?.writeText(secondaryCta.label).catch(() => {});235 setCopied(true);236 window.setTimeout(() => setCopied(false), 1600);237 }}238 aria-label={`Copy command: ${secondaryCta.label}`}239 >240 <span className="fd-agt-cli-p">$</span> {secondaryCta.label}241 <span className="fd-agt-cli-c">{copied ? "Copied" : "Copy"}</span>242 </button>243 </div>244 </div>245 246 <p className="fd-agt-sr">247 Example session: the prompt “{prompt}” produces a plan that reads the schema, edits two files, passes 12 tests and renders a billing dashboard preview.248 </p>249 250 <div className="fd-agt-stagewrap" aria-hidden="true">251 <div ref={scene} className="fd-agt-scene">252 <div ref={prev} className="fd-agt-prev">253 <div className="fd-agt-chrome">254 <i /><i /><i />255 <span className="fd-agt-url">localhost:3000/billing</span>256 <span className={`fd-agt-hmr ${build > 0 ? "is-on" : ""}`}>HMR</span>257 </div>258 <div className="fd-agt-app">259 <div className="fd-agt-app-h">260 <b>Billing</b>261 <span>Oct 2026</span>262 </div>263 <div className="fd-agt-kpis">264 {[265 ["MRR", "$482.9k", "+8.2%"],266 ["Net churn", "1.9%", "−0.4 pt"],267 ["Failed", "37", "$12.4k"],268 ].map(([k, v, d], i) => (269 <div key={k} className={`fd-agt-kpi ${build > i ? "is-in" : ""}`} style={{ transitionDelay: `${i * 40}ms` }}>270 <span>{k}</span>271 <b>{v}</b>272 <em>{d}</em>273 </div>274 ))}275 </div>276 <div className={`fd-agt-chart ${build > 3 ? "is-in" : ""}`}>277 <div className="fd-agt-chart-h">278 <span>Monthly recurring revenue</span>279 <em>12 mo</em>280 </div>281 <svg viewBox="0 0 300 90" preserveAspectRatio="none">282 <defs>283 <linearGradient id={gid} x1="0" x2="0" y1="0" y2="1">284 <stop offset="0" stopColor="#7C5CFF" stopOpacity=".35" />285 <stop offset="1" stopColor="#7C5CFF" stopOpacity="0" />286 </linearGradient>287 </defs>288 <path className="fd-agt-area" d="M0 78 L27 74 L54 70 L81 72 L109 60 L136 56 L163 50 L190 52 L218 38 L245 30 L272 24 L300 12 L300 90 L0 90Z" fill={`url(#${gid})`} />289 <path className="fd-agt-line" pathLength="1" d="M0 78 L27 74 L54 70 L81 72 L109 60 L136 56 L163 50 L190 52 L218 38 L245 30 L272 24 L300 12" fill="none" stroke="#9D85FF" strokeWidth="2" vectorEffect="non-scaling-stroke" />290 </svg>291 </div>292 <div className={`fd-agt-list ${build > 4 ? "is-in" : ""}`}>293 {[294 ["Northwind Bio", "Card declined", "$4,200"],295 ["Kestrel Air", "Expired card", "$2,950"],296 ["Oslo Freight", "Insufficient funds", "$1,180"],297 ].map(([c, r, a], i) => (298 <div key={c} className="fd-agt-row" style={{ transitionDelay: `${i * 70}ms` }}>299 <i style={{ background: ["#FF8A65", "#FFC34D", "#64D2FF"][i] }} />300 <b>{c}</b>301 <span>{r}</span>302 <em>{a}</em>303 </div>304 ))}305 </div>306 <div className={`fd-agt-toast ${build > 5 ? "is-in" : ""}`}>307 <span className="fd-agt-toast-i">✓</span>308 Retry scheduled for 37 invoices309 </div>310 </div>311 </div>312 313 <div ref={term} className="fd-agt-term">314 <div className="fd-agt-chrome fd-agt-chrome-t">315 <i /><i /><i />316 <span className="fd-agt-tab">fathom — ~/acme-billing</span>317 </div>318 <div className="fd-agt-body">319 <div className="fd-agt-prompt">320 <span className="fd-agt-ps">❯</span>321 <span>322 {prompt.slice(0, typed)}323 {typing && <span className="fd-agt-caret" />}324 </span>325 </div>326 <ol className="fd-agt-plan">327 {plan.slice(0, lines).map((l, i) => (328 <li key={i} className={`fd-agt-l fd-agt-l-${l.kind}`}>329 {l.kind === "think" && (330 <>331 <span className="fd-agt-g">✻</span>332 <span className="fd-agt-think">{l.text}</span>333 </>334 )}335 {l.kind === "tool" && (336 <>337 <span className="fd-agt-g">⏺</span>338 <span>339 <b>{l.name}</b>(<span className="fd-agt-arg">{l.arg}</span>)340 </span>341 {l.meta && <span className="fd-agt-meta">{l.meta}</span>}342 </>343 )}344 {l.kind === "diff" && (345 <>346 <span className="fd-agt-g">⏺</span>347 <span>348 <b>edit</b> <span className="fd-agt-arg">{l.file}</span>349 </span>350 <span className="fd-agt-meta">351 <span className="fd-agt-add">+{l.add}</span> <span className="fd-agt-del">−{l.del}</span>352 </span>353 </>354 )}355 {l.kind === "ok" && (356 <>357 <span className="fd-agt-g fd-agt-okg">✓</span>358 <span className="fd-agt-ok">{l.text}</span>359 </>360 )}361 </li>362 ))}363 {!typing && !done && (364 <li className="fd-agt-l fd-agt-working">365 <span className="fd-agt-spin" /> <span>Working · esc to interrupt</span>366 </li>367 )}368 </ol>369 {done && (370 <div className="fd-agt-pr">371 <span className="fd-agt-pr-b">PR #2481</span>372 feat(billing): dashboard with MRR, churn & dunning373 <span className="fd-agt-pr-c">Checks passing</span>374 </div>375 )}376 </div>377 </div>378 </div>379 </div>380 </section>381 );382}383 384const css = `385.fd-agt, .fd-agt *, .fd-agt *::before, .fd-agt *::after { box-sizing: border-box; }386.fd-agt {387 --fd-ease-out: cubic-bezier(0.22, 1, 0.36, 1);388 --fd-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);389 --fd-spring: cubic-bezier(0.34, 1.56, 0.64, 1);390 --fd-dur-1: 120ms; --fd-dur-2: 240ms; --fd-dur-3: 420ms; --fd-dur-4: 700ms;391 --fd-agt-bg: #07070A;392 --fd-agt-accent: #9D85FF;393 --fd-agt-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;394 --fd-agt-sans: "SF Pro Display", ui-sans-serif, -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif;395 position: relative; display: block; overflow: hidden; margin: 0; padding: 112px 32px 0; min-height: 880px;396 background: var(--fd-agt-bg); color: #EDEDF2; font: 400 1rem/1.5 var(--fd-agt-sans);397 isolation: isolate;398}399.fd-agt-bg { position: absolute; inset: 0; z-index: -1; pointer-events: none; }400.fd-agt-grid {401 position: absolute; inset: 0;402 background-image: linear-gradient(rgba(255,255,255,0.045) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.045) 1px, transparent 1px);403 background-size: 64px 64px; background-position: center top;404 -webkit-mask-image: radial-gradient(70% 60% at 50% 30%, #000 20%, transparent 75%); mask-image: radial-gradient(70% 60% at 50% 30%, #000 20%, transparent 75%);405}406.fd-agt-beam {407 position: absolute; left: 50%; top: 42%; width: 1100px; height: 640px; transform: translate(-50%, 0);408 background: radial-gradient(50% 50% at 50% 50%, rgba(124, 92, 255, 0.28), rgba(124,92,255,0.06) 50%, transparent 72%);409 filter: blur(20px); animation: fd-agt-breathe 7s var(--fd-ease-in-out) infinite alternate;410}411.fd-agt-copy { position: relative; z-index: 2; max-width: 860px; margin: 0 auto; text-align: center; will-change: transform, opacity; }412.fd-agt-eyebrow {413 display: inline-flex; align-items: center; gap: 10px; margin: 0 0 22px; padding: 7px 14px; border-radius: 9999px;414 border: 1px solid rgba(255,255,255,0.1); background: rgba(255,255,255,0.04);415 font: 500 0.8125rem/1 var(--fd-agt-mono); letter-spacing: -0.01em; color: #B9B9C6;416 animation: fd-agt-rise var(--fd-dur-4) var(--fd-ease-out) both;417}418.fd-agt-dot { display: block; width: 6px; height: 6px; border-radius: 9999px; background: #4ADE80; box-shadow: 0 0 0 3px rgba(74,222,128,0.18); }419.fd-agt-h1 {420 margin: 0; padding: 0; color: #F5F5F8;421 font: 600 clamp(2.75rem, 6vw, 5rem)/0.98 var(--fd-agt-sans); letter-spacing: -0.05em; text-wrap: balance;422 animation: fd-agt-blurin 900ms var(--fd-ease-out) 80ms both;423}424.fd-agt-dim { color: #6E6E7C; }425.fd-agt-lede {426 margin: 20px auto 0; padding: 0; max-width: 36rem; color: #9C9CAA;427 font: 400 clamp(1rem, 1.3vw, 1.1875rem)/1.55 var(--fd-agt-sans); letter-spacing: -0.01em; text-wrap: pretty;428 animation: fd-agt-rise var(--fd-dur-4) var(--fd-ease-out) 240ms both;429}430.fd-agt-ctas { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin: 28px 0 0; animation: fd-agt-rise var(--fd-dur-4) var(--fd-ease-out) 340ms both; }431.fd-agt-btn {432 display: inline-flex; align-items: center; gap: 10px; margin: 0; height: 46px; padding: 0 20px; border-radius: 9999px; border: 0;433 background: #F5F5F8; color: #0B0B0F; text-decoration: none; font: 500 0.9375rem/1 var(--fd-agt-sans); letter-spacing: -0.01em;434 box-shadow: 0 0 0 1px rgba(255,255,255,0.2), 0 10px 40px -10px rgba(157,133,255,0.6);435 transition: transform var(--fd-dur-2) var(--fd-spring);436}437.fd-agt-btn svg { display: block; }438.fd-agt-btn:active, .fd-agt-cli:active { transform: scale(0.96); transition-duration: var(--fd-dur-1); }439.fd-agt-cli {440 appearance: none; -webkit-appearance: none; cursor: pointer;441 display: inline-flex; align-items: center; gap: 10px; margin: 0; height: 46px; padding: 0 8px 0 18px; border-radius: 9999px;442 background: rgba(255,255,255,0.04); color: #E3E3EA; border: 1px solid rgba(255,255,255,0.12);443 font: 500 0.875rem/1 var(--fd-agt-mono); letter-spacing: -0.01em;444 transition: transform var(--fd-dur-2) var(--fd-spring), border-color var(--fd-dur-1);445}446.fd-agt-cli:hover { border-color: rgba(255,255,255,0.24); }447.fd-agt-cli-p { color: var(--fd-agt-accent); }448.fd-agt-cli-c { display: inline-flex; align-items: center; height: 30px; padding: 0 12px; border-radius: 9999px; background: rgba(255,255,255,0.08); color: #B9B9C6; font: 500 0.75rem/1 var(--fd-agt-sans); }449.fd-agt-btn:focus-visible, .fd-agt-cli:focus-visible { outline: 2px solid rgba(157,133,255,0.7); outline-offset: 2px; }450.fd-agt-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }451 452.fd-agt-stagewrap { position: relative; max-width: 1180px; margin: 52px auto 0; perspective: 1800px; perspective-origin: 50% 0%; animation: fd-agt-stagein 1200ms var(--fd-ease-out) 300ms both; }453.fd-agt-scene { position: relative; height: 470px; transform-style: preserve-3d; transform: rotateX(6deg); will-change: transform; }454.fd-agt-term, .fd-agt-prev {455 position: absolute; top: 0; border-radius: 16px; overflow: hidden; will-change: transform;456 border: 1px solid rgba(255,255,255,0.1);457 box-shadow: 0 1px 0 rgba(255,255,255,0.08) inset, 0 50px 100px -30px rgba(0,0,0,0.9), 0 0 0 1px rgba(0,0,0,0.6);458}459.fd-agt-term { left: 0; width: 45%; height: 440px; background: rgba(14, 14, 19, 0.94); -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px); transform: translateZ(30px); }460.fd-agt-prev { right: 0; top: 30px; width: 57%; height: 436px; background: #F6F6F8; transform: translateZ(-20px); color: #0B0B0F; }461.fd-agt-chrome { display: flex; align-items: center; gap: 7px; height: 38px; padding: 0 14px; border-bottom: 1px solid rgba(11,11,15,0.08); background: #ECECF0; }462.fd-agt-chrome i { display: block; width: 11px; height: 11px; border-radius: 9999px; background: #D6D6DC; }463.fd-agt-chrome-t { background: rgba(255,255,255,0.03); border-bottom-color: rgba(255,255,255,0.07); }464.fd-agt-chrome-t i { background: #2E2E36; }465.fd-agt-chrome-t i:nth-child(1) { background: #FF5F57; } .fd-agt-chrome-t i:nth-child(2) { background: #FEBC2E; } .fd-agt-chrome-t i:nth-child(3) { background: #28C840; }466.fd-agt-url { margin: 0 auto; padding: 5px 40px; border-radius: 8px; background: #fff; color: #6B6B76; font: 400 0.75rem/1 var(--fd-agt-mono); }467.fd-agt-hmr { padding: 4px 7px; border-radius: 6px; font: 600 0.625rem/1 var(--fd-agt-mono); letter-spacing: 0.06em; color: #9A9AA5; background: #E2E2E8; transition: background var(--fd-dur-2), color var(--fd-dur-2); }468.fd-agt-hmr.is-on { background: #DCFCE7; color: #15803D; }469.fd-agt-tab { margin: 0 auto; color: #7A7A88; font: 500 0.75rem/1 var(--fd-agt-mono); letter-spacing: -0.01em; transform: translateX(-20px); }470.fd-agt-body { padding: 20px 20px 18px; font: 400 0.8125rem/1.6 var(--fd-agt-mono); color: #C9C9D4; letter-spacing: -0.01em; }471.fd-agt-prompt { display: flex; gap: 10px; padding: 12px 14px; border-radius: 10px; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); color: #F2F2F6; min-height: 70px; }472.fd-agt-ps { color: var(--fd-agt-accent); }473.fd-agt-caret { display: inline-block; width: 8px; height: 1.05em; margin: 0 0 -0.18em 1px; background: #F2F2F6; animation: fd-agt-blink 1s steps(1) infinite; }474.fd-agt-plan { list-style: none; margin: 14px 0 0; padding: 0; }475.fd-agt-l { display: flex; gap: 10px; align-items: baseline; margin: 0; padding: 3px 2px; animation: fd-agt-line var(--fd-dur-3) var(--fd-ease-out) both; white-space: nowrap; }476.fd-agt-l > span:nth-child(2) { overflow: hidden; text-overflow: ellipsis; }477.fd-agt-g { flex: none; width: 12px; color: #6B6B7A; }478.fd-agt-l-tool .fd-agt-g, .fd-agt-l-diff .fd-agt-g { color: #4ADE80; }479.fd-agt-think { color: #8A8A99; font-style: italic; }480.fd-agt-l b { font-weight: 600; color: #F2F2F6; }481.fd-agt-arg { color: #C4B5FD; }482.fd-agt-meta { margin-left: auto; padding-left: 10px; color: #6B6B7A; font-size: 0.75rem; }483.fd-agt-add { color: #4ADE80; } .fd-agt-del { color: #F87171; }484.fd-agt-okg { color: #4ADE80; }485.fd-agt-ok { color: #4ADE80; font-weight: 500; }486.fd-agt-working { color: #8A8A99; }487.fd-agt-spin { display: inline-block; flex: none; width: 10px; height: 10px; border-radius: 9999px; border: 1.5px solid #3A3A46; border-top-color: var(--fd-agt-accent); animation: fd-agt-spin 700ms linear infinite; transform: translateY(1px); }488.fd-agt-pr {489 display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 14px 0 0; padding: 12px 14px; border-radius: 10px;490 background: linear-gradient(180deg, rgba(124,92,255,0.14), rgba(124,92,255,0.06)); border: 1px solid rgba(157,133,255,0.3); color: #E6E1FF;491 font: 500 0.75rem/1.4 var(--fd-agt-mono); animation: fd-agt-pop var(--fd-dur-3) var(--fd-spring) both;492}493.fd-agt-pr-b { padding: 3px 7px; border-radius: 6px; background: var(--fd-agt-accent); color: #0B0B0F; font-weight: 700; }494.fd-agt-pr-c { margin-left: auto; color: #4ADE80; }495 496.fd-agt-app { position: relative; height: calc(100% - 38px); padding: 18px 20px; font: 400 0.8125rem/1.4 var(--fd-agt-sans); }497.fd-agt-app-h { display: flex; justify-content: space-between; align-items: baseline; margin: 0 0 14px; }498.fd-agt-app-h b { font: 600 1.0625rem/1 var(--fd-agt-sans); letter-spacing: -0.02em; }499.fd-agt-app-h span { color: #8A8A95; font: 500 0.75rem/1 var(--fd-agt-sans); }500.fd-agt-kpis { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }501.fd-agt-kpi, .fd-agt-chart, .fd-agt-list {502 border-radius: 12px; background: #fff; box-shadow: 0 0 0 1px rgba(11,11,15,0.06), 0 6px 16px -10px rgba(11,11,15,0.2);503 opacity: 0; transform: translateY(18px) scale(0.97); transition: opacity var(--fd-dur-3) var(--fd-ease-out), transform var(--fd-dur-4) var(--fd-spring);504}505.fd-agt .is-in { opacity: 1; transform: none; }506.fd-agt-kpi { padding: 12px; }507.fd-agt-kpi span { display: block; color: #8A8A95; font: 500 0.6875rem/1 var(--fd-agt-sans); }508.fd-agt-kpi b { display: block; margin: 8px 0 4px; font: 600 1.25rem/1 var(--fd-agt-sans); letter-spacing: -0.03em; color: #0B0B0F; }509.fd-agt-kpi em { font: 500 0.6875rem/1 var(--fd-agt-sans); font-style: normal; color: #16A34A; }510.fd-agt-chart { margin: 10px 0 0; padding: 12px 12px 6px; }511.fd-agt-chart-h { display: flex; justify-content: space-between; color: #6B6B76; font: 500 0.6875rem/1 var(--fd-agt-sans); }512.fd-agt-chart-h em { font-style: normal; color: #9A9AA5; }513.fd-agt-chart svg { display: block; width: 100%; height: 92px; margin-top: 6px; }514.fd-agt-chart .fd-agt-line { stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset 1400ms var(--fd-ease-out) 120ms; }515.fd-agt-chart .fd-agt-area { opacity: 0; transition: opacity 900ms var(--fd-ease-out) 500ms; }516.fd-agt-chart.is-in .fd-agt-line { stroke-dashoffset: 0; }517.fd-agt-chart.is-in .fd-agt-area { opacity: 1; }518.fd-agt-list { margin: 10px 0 0; padding: 4px 12px; }519.fd-agt-row { display: grid; grid-template-columns: 8px 1.1fr 1.2fr auto; align-items: center; gap: 10px; padding: 8px 0; border-top: 1px solid rgba(11,11,15,0.06); opacity: 0; transform: translateX(-10px); transition: opacity var(--fd-dur-3) var(--fd-ease-out), transform var(--fd-dur-3) var(--fd-ease-out); }520.fd-agt-row:first-child { border-top: 0; }521.fd-agt-list.is-in .fd-agt-row { opacity: 1; transform: none; }522.fd-agt-row i { display: block; width: 8px; height: 8px; border-radius: 9999px; }523.fd-agt-row b { font: 600 0.75rem/1 var(--fd-agt-sans); color: #1C1C22; }524.fd-agt-row span { font: 400 0.75rem/1 var(--fd-agt-sans); color: #8A8A95; }525.fd-agt-row em { font: 500 0.75rem/1 var(--fd-agt-mono); font-style: normal; color: #1C1C22; }526.fd-agt-toast {527 position: absolute; right: 18px; bottom: 18px; display: flex; align-items: center; gap: 10px; padding: 10px 14px 10px 10px; border-radius: 12px;528 background: #0B0B0F; color: #F2F2F6; font: 500 0.75rem/1 var(--fd-agt-sans); box-shadow: 0 16px 40px -12px rgba(11,11,15,0.6);529 opacity: 0; transform: translateY(16px) scale(0.94); transition: opacity var(--fd-dur-3) var(--fd-ease-out), transform var(--fd-dur-3) var(--fd-spring);530}531.fd-agt-toast.is-in { opacity: 1; transform: none; }532.fd-agt-toast-i { display: grid; place-items: center; width: 20px; height: 20px; border-radius: 9999px; background: #22C55E; color: #0B0B0F; font: 700 0.6875rem/1 var(--fd-agt-sans); }533 534@keyframes fd-agt-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }535@keyframes fd-agt-blurin { from { opacity: 0; filter: blur(14px); transform: translateY(10px) scale(0.98); } to { opacity: 1; filter: none; transform: none; } }536@keyframes fd-agt-stagein { from { opacity: 0; transform: translateY(80px) scale(0.94); filter: blur(8px); } to { opacity: 1; transform: none; filter: none; } }537@keyframes fd-agt-line { from { opacity: 0; transform: translateY(6px); filter: blur(3px); } to { opacity: 1; transform: none; filter: none; } }538@keyframes fd-agt-pop { from { opacity: 0; transform: scale(0.94); } to { opacity: 1; transform: none; } }539@keyframes fd-agt-blink { 50% { opacity: 0; } }540@keyframes fd-agt-spin { to { transform: translateY(1px) rotate(360deg); } }541@keyframes fd-agt-breathe { from { opacity: 0.75; transform: translate(-50%, 0) scale(0.96); } to { opacity: 1; transform: translate(-50%, -2%) scale(1.04); } }542 543@media (max-width: 900px) {544 .fd-agt { padding: 104px 16px 0; min-height: 844px; }545 .fd-agt-stagewrap { margin-top: 44px; }546 .fd-agt-scene { height: 540px; }547 .fd-agt-term { width: 100%; height: 330px; }548 .fd-agt-prev { left: 8%; right: auto; width: 92%; top: 220px; height: 400px; }549 .fd-agt-body { padding: 14px; font-size: 0.75rem; }550 .fd-agt-meta { display: none; }551 .fd-agt-url { padding: 5px 14px; }552 .fd-agt-cli { display: none; }553 .fd-agt-eyebrow { font-size: 0.6875rem; letter-spacing: -0.02em; white-space: nowrap; padding: 7px 12px; }554}555@media (prefers-reduced-motion: reduce) {556 .fd-agt *, .fd-agt *::before { animation: none !important; transition-duration: 1ms !important; transition-delay: 0ms !important; }557}558@media (prefers-reduced-transparency: reduce) { .fd-agt-term { background: #0E0E13; -webkit-backdrop-filter: none; backdrop-filter: none; } }559`;560 561/* ───────────────────────── Demo ───────────────────────── */562 563export function Demo() {564 return (565 <div className="fd-agt-demo">566 <style>{demoCss}</style>567 <header className="fd-agt-demo-nav">568 <a className="fd-agt-demo-logo" href="#top">569 <svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true">570 <path d="M3 3h14v4H7v3h8v4H7v3H3z" fill="#F5F5F8" />571 </svg>572 fathom573 </a>574 <nav aria-label="Primary">575 <a href="#product">Product</a>576 <a href="#docs">Docs</a>577 <a href="#changelog">Changelog</a>578 <a href="#enterprise">Enterprise</a>579 <a href="#pricing">Pricing</a>580 </nav>581 <div className="fd-agt-demo-r">582 <a href="#gh" className="fd-agt-demo-gh">583 <svg viewBox="0 0 16 16" width="14" height="14" aria-hidden="true"><path fill="currentColor" d="M8 .2l2.4 4.9 5.4.8-3.9 3.8.9 5.4L8 12.6l-4.8 2.5.9-5.4L.2 5.9l5.4-.8z" /></svg>584 41.2k585 </a>586 <a href="#login" className="fd-agt-demo-in">Log in</a>587 </div>588 </header>589 <HeroAiTerminal />590 </div>591 );592}593 594const demoCss = `595.fd-agt-demo { position: relative; margin: 0; padding: 0; min-height: 100vh; background: #07070A; }596.fd-agt-demo *, .fd-agt-demo *::before, .fd-agt-demo *::after { box-sizing: border-box; }597.fd-agt-demo-nav {598 position: absolute; z-index: 10; top: 0; left: 0; right: 0; max-width: 1180px; margin: 0 auto; height: 68px; padding: 0 32px;599 display: flex; align-items: center; justify-content: space-between; gap: 20px;600 border-bottom: 1px solid rgba(255,255,255,0.06);601}602.fd-agt-demo-nav .fd-agt-demo-logo { display: inline-flex; align-items: center; gap: 9px; margin: 0; color: #F5F5F8; text-decoration: none; font: 600 1.0625rem/1 ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: -0.04em; }603.fd-agt-demo-logo svg { display: block; }604.fd-agt-demo-nav nav { display: flex; gap: 28px; }605.fd-agt-demo-nav a { color: #9C9CAA; text-decoration: none; font: 500 0.875rem/1 ui-sans-serif, -apple-system, "Inter", sans-serif; letter-spacing: -0.005em; transition: color 120ms; }606.fd-agt-demo-nav a:hover { color: #F5F5F8; }607.fd-agt-demo-nav a:focus-visible { outline: 2px solid rgba(157,133,255,.7); outline-offset: 2px; border-radius: 6px; }608.fd-agt-demo-r { display: flex; align-items: center; gap: 18px; }609.fd-agt-demo-gh { display: inline-flex; align-items: center; gap: 6px; font-family: ui-monospace, "SF Mono", Menlo, monospace !important; }610.fd-agt-demo-gh svg { display: block; color: #FEBC2E; }611.fd-agt-demo-in { display: inline-flex; align-items: center; height: 32px; padding: 0 14px; border-radius: 9999px; border: 1px solid rgba(255,255,255,0.14); color: #F5F5F8 !important; }612@media (max-width: 900px) { .fd-agt-demo-nav { padding: 0 16px; } .fd-agt-demo-nav nav, .fd-agt-demo-gh { display: none !important; } }613`;A photoreal CSS-3D metal card that tilts to the cursor, then flips and docks on scroll while spend stats count up.
A giant serif masthead and procedural duotone fabric frames revealed by clip-path wipes, drifting apart on scroll.
A layered SVG landscape with deep pointer and scroll parallax, a mirrored lake and golden hour fading to dusk.