Restyle runtimes, how-it-works, and FAQ in adaline style

- runtimes: bordered "supported runtimes" strip, text-based for consistency
  (Reicon brand coverage is partial); all eight runtimes preserved
- how-it-works: centered heading + clean numbered stepper for the seven-step
  first-run flow; drop the framer-motion terminal demo
- faq: adaline accordion (grid-rows expand, Reicon plus icon); all answers
  preserved, including the platform-support-derived ones
- move all three into components/sections and remove the old flat components
This commit is contained in:
ZacharyZhang-NY committed 2026-07-07 22:14:24 -04:00
1 parent c0f677ccc9
commit 32e521beae
6 files changed
+107 -311

No files matched your search

+4 -4
View File
@@ -1,11 +1,11 @@
import { DownloadSection } from "../components/download-section";
import { FAQ } from "../components/faq";
import { FAQ } from "../components/sections/faq";
import { Features } from "../components/sections/features";
import { Footer } from "../components/footer";
import { Hero } from "../components/sections/hero";
import { HowItWorks } from "../components/how-it-works";
import { HowItWorks } from "../components/sections/how-it-works";
import { Navbar } from "../components/layout/navbar";
import { RuntimesBar } from "../components/runtimes-bar";
import { Runtimes } from "../components/sections/runtimes";
export default function Page() {
return (
@@ -21,7 +21,7 @@ export default function Page() {
<main id="main">
<Hero />
<RuntimesBar />
<Runtimes />
<Features />
<HowItWorks />
<FAQ />
-149
View File
@@ -1,149 +0,0 @@
"use client";
import { motion, useInView } from "framer-motion";
import { useEffect, useRef, useState } from "react";
import { landingHowItWorksNarrative } from "../lib/product-narrative";
const demoMessages = landingHowItWorksNarrative.demoMessages;
const logTone: Record<(typeof demoMessages)[number]["role"], string> = {
system: "text-[var(--muted-strong)]",
};
const dotTone: Record<(typeof demoMessages)[number]["role"], string> = {
system: "bg-[var(--muted-strong)]",
};
const steps = landingHowItWorksNarrative.steps;
function DemoPanel() {
const anchorRef = useRef<HTMLDivElement | null>(null);
const isVisible = useInView(anchorRef, { once: true, amount: 0.4 });
const [visibleCount, setVisibleCount] = useState(0);
const logRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (!isVisible) return;
const timers = demoMessages.map((message, index) =>
window.setTimeout(() => setVisibleCount(index + 1), message.delay),
);
return () => timers.forEach((timer) => window.clearTimeout(timer));
}, [isVisible]);
useEffect(() => {
if (logRef.current) {
logRef.current.scrollTop = logRef.current.scrollHeight;
}
}, [visibleCount]);
return (
<div ref={anchorRef} className="relative">
<div className="rounded-[22px] overflow-hidden border border-[var(--border)] bg-[var(--panel)] shadow-[0_8px_40px_-8px_rgba(0,0,0,0.06)]">
<div className="flex items-center gap-2 px-4 py-3 bg-[var(--panel-soft)] border-b border-[var(--border)]">
<span className="w-3 h-3 rounded-full bg-[#ff5f57]" aria-hidden="true" />
<span className="w-3 h-3 rounded-full bg-[#febc2e]" aria-hidden="true" />
<span className="w-3 h-3 rounded-full bg-[#28c840]" aria-hidden="true" />
<span className="ml-2 text-xs text-[var(--muted)] font-mono">AgentCompany</span>
</div>
<div ref={logRef} role="log" aria-label="Agent activity demonstration" className="p-5 lg:p-6 font-mono text-sm leading-relaxed h-[340px] overflow-y-auto">
{demoMessages.slice(0, visibleCount).map((message, index) => (
<motion.div
key={`${message.agent}-${index}`}
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="flex gap-3 mb-3 last:mb-0"
>
<span className={`shrink-0 font-semibold inline-flex items-center gap-1.5 ${logTone[message.role]}`}>
<span
className={`w-1.5 h-1.5 rounded-full ${dotTone[message.role]}`}
aria-hidden="true"
/>
{message.agent}
</span>
<span className="text-[var(--text)]">{message.text}</span>
</motion.div>
))}
{visibleCount < demoMessages.length && isVisible && (
<span className="inline-block w-2 h-4 bg-[var(--accent)] animate-pulse rounded-sm" aria-hidden="true" />
)}
{visibleCount >= demoMessages.length && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.3 }}
className="mt-4 pt-4 border-t border-[var(--border)] flex items-center gap-2 text-[#059669]"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="m9 12 2 2 4-4" />
<circle cx="12" cy="12" r="10" />
</svg>
<span className="font-semibold">{landingHowItWorksNarrative.completionText}</span>
</motion.div>
)}
<div />
</div>
</div>
</div>
);
}
export function HowItWorks() {
return (
<section id="how-it-works" className="py-24 lg:py-40 px-4 sm:px-6 lg:px-8">
<div className="mx-auto max-w-6xl">
<div className="text-center mb-20 lg:mb-24">
<p className="font-mono text-xs font-medium uppercase tracking-[0.25em] text-[var(--accent)] mb-5">
How It Works
</p>
<h2 className="font-serif text-[clamp(2rem,1.5rem+2.5vw,3.25rem)] tracking-[-0.01em] text-[var(--text)]">
{landingHowItWorksNarrative.heading}
</h2>
</div>
<div className="grid grid-cols-1 lg:grid-cols-5 gap-12 lg:gap-16 items-start">
<div className="lg:col-span-2 relative">
<div className="absolute left-[15px] top-10 bottom-4 w-px bg-[var(--border)] hidden lg:block" aria-hidden="true" />
<div className="space-y-7">
{steps.map((step, index) => (
<motion.div
key={step.number}
initial={{ opacity: 0, x: -24 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, amount: 0.5 }}
transition={{ duration: 0.5, delay: 0.12 * index, ease: [0.16, 1, 0.3, 1] }}
className="flex gap-5"
>
<div className="relative z-10 shrink-0 flex items-center justify-center w-8 h-8 rounded-full bg-[var(--accent)] text-[var(--text-on-accent)] text-sm font-bold shadow-[0_2px_12px_-2px_var(--accent)]">
{step.number}
</div>
<div className="pt-0.5">
<h3 className="text-base font-bold text-[var(--text)] mb-1.5 tracking-tight">{step.title}</h3>
<p className="text-sm leading-relaxed text-[var(--muted)]">{step.description}</p>
</div>
</motion.div>
))}
</div>
</div>
<motion.div
className="lg:col-span-3"
initial={{ opacity: 0, y: 32 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.7, ease: [0.16, 1, 0.3, 1] }}
>
<DemoPanel />
</motion.div>
</div>
</div>
</section>
);
}
-117
View File
@@ -1,117 +0,0 @@
"use client";
import { motion } from "framer-motion";
function AnthropicIcon() {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M13.827 3.52h3.603L24 20.48h-3.603l-6.57-16.96zm-7.257 0H10.173L16.74 20.48H13.14L11.07 14.98H5.355l-1.937 5.5H0L6.57 3.52zm4.132 8.69L8.18 5.86l-2.522 6.35h5.244z" />
</svg>
);
}
function OpenAIIcon() {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M22.282 9.821a5.985 5.985 0 0 0-.516-4.91 6.046 6.046 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a5.998 5.998 0 0 0-3.998 2.9 6.047 6.047 0 0 0 .743 7.097 5.98 5.98 0 0 0 .51 4.911 6.051 6.051 0 0 0 6.515 2.9A5.985 5.985 0 0 0 13.26 24a6.056 6.056 0 0 0 5.772-4.206 5.99 5.99 0 0 0 3.997-2.9 6.056 6.056 0 0 0-.747-7.073zM13.26 22.43a4.476 4.476 0 0 1-2.876-1.04l.141-.081 4.779-2.758a.795.795 0 0 0 .392-.681v-6.737l2.02 1.168a.071.071 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494zM3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085 4.783 2.759a.771.771 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646zM2.34 7.896a4.485 4.485 0 0 1 2.366-1.973V11.6a.766.766 0 0 0 .388.676l5.815 3.355-2.02 1.168a.076.076 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.872zm16.597 3.855l-5.833-3.387L15.119 7.2a.076.076 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667zm2.01-3.023l-.141-.085-4.774-2.782a.776.776 0 0 0-.785 0L9.409 9.23V6.897a.066.066 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135l-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08L8.704 5.46a.795.795 0 0 0-.393.681zm1.097-2.365l2.602-1.5 2.607 1.5v2.999l-2.597 1.5-2.607-1.5z" />
</svg>
);
}
function GoogleIcon() {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 11.366v3.473h5.51c-.226 1.408-1.644 4.126-5.51 4.126-3.314 0-6.017-2.744-6.017-6.127S8.686 6.71 12 6.71c1.888 0 3.152.804 3.874 1.497l2.637-2.54C16.63 3.952 14.534 3 12 3 6.477 3 2 7.477 2 13s4.477 10 10 10c5.773 0 9.6-4.057 9.6-9.773 0-.656-.07-1.157-.156-1.657H12z" />
</svg>
);
}
function MoonshotIcon() {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 2a8 8 0 0 1 .5 15.98A10.5 10.5 0 0 0 17 12c0-3.14-1.37-5.96-3.54-7.9A8.03 8.03 0 0 1 12 4z" />
</svg>
);
}
function ZhipuIcon() {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M4 4h16v3H8.5l11 10H20v3H4v-3h11.5L4.5 7H4V4z" />
</svg>
);
}
function MiniMaxIcon() {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M2 20V4l5 8 5-8 5 8 5-8v16h-3V10.5l-2 4-5-8-5 8-2-4V20H2z" />
</svg>
);
}
function OllamaIcon() {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 2C8.13 2 5 5.13 5 9c0 2.38 1.19 4.47 3 5.74V17a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.87-3.13-7-7-7zm-2 15v-1h4v1h-4zm3.5-4.15l-.5.35V15h-2v-1.8l-.5-.35A4.98 4.98 0 0 1 7 9c0-2.76 2.24-5 5-5s5 2.24 5 5a4.98 4.98 0 0 1-3.5 4.85zM10 9.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm7 0a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z" />
</svg>
);
}
function ApiIcon() {
return (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
</svg>
);
}
const runtimes = [
{ name: "Claude Code", provider: "Anthropic", icon: AnthropicIcon },
{ name: "Codex CLI", provider: "OpenAI", icon: OpenAIIcon },
{ name: "Gemini CLI", provider: "Google", icon: GoogleIcon },
{ name: "Kimi", provider: "Moonshot AI", icon: MoonshotIcon },
{ name: "GLM", provider: "Zhipu AI", icon: ZhipuIcon },
{ name: "MiniMax", provider: "MiniMax", icon: MiniMaxIcon },
{ name: "Ollama", provider: "Local", icon: OllamaIcon },
{ name: "Anthropic Compatible API", provider: "Any Provider", icon: ApiIcon },
];
export function RuntimesBar() {
return (
<section className="border-y border-[var(--border)] py-12 lg:py-16">
<div className="mx-auto max-w-6xl px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 16 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.5 }}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
className="text-center"
>
<p className="font-mono text-xs uppercase tracking-[0.25em] text-[var(--muted)] mb-10">
Supported AI Runtimes
</p>
<div className="flex flex-wrap items-center justify-center gap-x-8 gap-y-6 lg:gap-x-12">
{runtimes.map((runtime) => {
const Icon = runtime.icon;
return (
<div key={runtime.name} className="flex items-center gap-2.5">
<span className="text-[var(--muted)] opacity-60">
<Icon />
</span>
<span className="text-base font-semibold tracking-tight text-[var(--text)]">
{runtime.name}
</span>
<span className="text-sm text-[var(--muted)] hidden sm:inline">
{runtime.provider}
</span>
</div>
);
})}
</div>
</motion.div>
</div>
</section>
);
}
@@ -1,8 +1,11 @@
"use client";
import { motion } from "framer-motion";
import { useState } from "react";
import { platformSupportSummary } from "../lib/platform-support";
import { Plus } from "reicon-react/icons/Plus";
import { Container } from "../ui/container";
import { Reveal } from "../ui/reveal";
import { SectionHeading } from "../ui/section-heading";
import { platformSupportSummary } from "../../lib/platform-support";
const faqs = [
{
@@ -45,65 +48,49 @@ const faqs = [
},
];
function FaqItem({ question, answer, index }: { question: string; answer: string; index: number }) {
function FaqItem({ question, answer }: { question: string; answer: string }) {
const [open, setOpen] = useState(false);
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.5, delay: 0.06 * index, ease: [0.16, 1, 0.3, 1] }}
className="border-b border-[var(--border)] last:border-b-0"
>
<div className="border-b border-[var(--border)]">
<button
type="button"
onClick={() => setOpen((value) => !value)}
aria-expanded={open}
className="flex w-full items-center justify-between gap-4 py-6 text-left transition-colors duration-200 hover:text-[var(--accent)]"
className="group flex w-full cursor-pointer items-center justify-between gap-4 py-5 text-left"
>
<h3 className="text-base lg:text-lg font-semibold text-[var(--text)] pr-4">{question}</h3>
<span className="text-base font-medium tracking-tight text-[var(--text)] md:text-lg">{question}</span>
<span
className={`shrink-0 text-[var(--muted)] transition-transform duration-300 ${open ? "rotate-45" : ""}`}
className={`shrink-0 text-[var(--muted)] transition-transform duration-300 group-hover:text-[var(--text)] ${open ? "rotate-45" : ""}`}
aria-hidden="true"
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="12" y1="5" x2="12" y2="19" />
<line x1="5" y1="12" x2="19" y2="12" />
</svg>
<Plus size={20} />
</span>
</button>
<div className={`overflow-hidden transition-all duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] ${open ? "max-h-96 pb-6" : "max-h-0"}`}>
<p className="text-[var(--muted-strong)] leading-relaxed text-base pr-12">{answer}</p>
<div
className={`grid overflow-hidden transition-all duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] ${open ? "grid-rows-[1fr] pb-5" : "grid-rows-[0fr]"}`}
>
<div className="min-h-0">
<p className="max-w-[68ch] pr-8 leading-relaxed text-[var(--muted)]">{answer}</p>
</div>
</div>
</motion.div>
</div>
);
}
export function FAQ() {
return (
<section id="faq" className="py-24 lg:py-40 px-4 sm:px-6 lg:px-8">
<div className="mx-auto max-w-3xl">
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }}
className="text-center mb-16 lg:mb-20"
>
<p className="font-mono text-xs font-medium uppercase tracking-[0.25em] text-[var(--accent)] mb-5">FAQ</p>
<h2 className="font-serif text-[clamp(2rem,1.5rem+2.5vw,3.25rem)] tracking-[-0.01em] text-[var(--text)]">
Frequently Asked Questions
</h2>
</motion.div>
<div className="border-t border-[var(--border)]">
{faqs.map((item, index) => (
<FaqItem key={item.question} question={item.question} answer={item.answer} index={index} />
<section id="faq" className="scroll-mt-20 py-20 md:py-28">
<Container>
<Reveal>
<SectionHeading eyebrow="FAQ" title="Frequently asked questions" align="center" className="mx-auto" />
</Reveal>
<Reveal delay={80} className="mx-auto mt-12 max-w-3xl border-t border-[var(--border)]">
{faqs.map((faq) => (
<FaqItem key={faq.question} question={faq.question} answer={faq.answer} />
))}
</div>
</div>
</Reveal>
</Container>
</section>
);
}
@@ -0,0 +1,42 @@
import { Container } from "../ui/container";
import { Reveal } from "../ui/reveal";
import { SectionHeading } from "../ui/section-heading";
import { landingHowItWorksNarrative } from "../../lib/product-narrative";
const steps = landingHowItWorksNarrative.steps;
export function HowItWorks() {
return (
<section id="how-it-works" className="scroll-mt-20 bg-[var(--bg-subtle)] py-20 md:py-28">
<Container>
<Reveal>
<SectionHeading
eyebrow="How it works"
title={landingHowItWorksNarrative.heading}
align="center"
className="mx-auto"
/>
</Reveal>
<Reveal delay={80} className="mx-auto mt-14 max-w-2xl">
<div className="relative">
<div className="absolute bottom-3 left-5 top-3 w-px bg-[var(--border)]" aria-hidden="true" />
<ol className="space-y-8">
{steps.map((step) => (
<li key={step.number} className="relative flex gap-5">
<span className="relative z-10 flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-[var(--accent)] text-sm font-medium text-[var(--text-on-accent)]">
{step.number}
</span>
<div className="pt-1.5">
<h3 className="text-base font-medium tracking-tight text-[var(--text)]">{step.title}</h3>
<p className="mt-1 leading-relaxed text-[var(--muted)]">{step.description}</p>
</div>
</li>
))}
</ol>
</div>
</Reveal>
</Container>
</section>
);
}
+33
View File
@@ -0,0 +1,33 @@
import { Container } from "../ui/container";
import { Reveal } from "../ui/reveal";
const runtimes = [
{ name: "Claude Code", provider: "Anthropic" },
{ name: "Codex CLI", provider: "OpenAI" },
{ name: "Gemini CLI", provider: "Google" },
{ name: "Kimi", provider: "Moonshot AI" },
{ name: "GLM", provider: "Zhipu AI" },
{ name: "MiniMax", provider: "MiniMax" },
{ name: "Ollama", provider: "Local" },
{ name: "Anthropic-compatible API", provider: "Any provider" },
];
export function Runtimes() {
return (
<section className="border-y border-[var(--border)]">
<Container className="py-12 md:py-16">
<Reveal>
<p className="eyebrow text-center text-sm text-[var(--muted)]">Supported runtimes</p>
<ul className="mt-8 flex flex-wrap items-center justify-center gap-x-8 gap-y-4 md:gap-x-10 lg:gap-x-12">
{runtimes.map((runtime) => (
<li key={runtime.name} className="flex items-baseline gap-2">
<span className="text-base font-medium tracking-tight text-[var(--text)]">{runtime.name}</span>
<span className="hidden text-sm text-[var(--muted)] sm:inline">{runtime.provider}</span>
</li>
))}
</ul>
</Reveal>
</Container>
</section>
);
}