Enrich the landing page with a bento grid and aurora atmosphere
Close the flat-vs-adaline gap by adding depth and motion to our product shots: - bento: replace the flat stacked features with a varied 12-column grid (org chart, cost, office, messages, connectors, plus a local-first highlight card), each with a corner glow and hover - showcase: a dark aurora "Set the goal" statement band (Newsreader serif over a drifting northern-lights glow) for a light -> dark -> light rhythm - hero + footer: aurora glow staging behind the dashboard and under the CTA - ui/aurora + globals: mint/teal/lime drift-animated glow tokens - remove the superseded flat feature components e2e green (12 passed); build verified.
This commit is contained in:
1 parent
4f0d03ad72
commit
8f1a5f1f4e
9 files changed
+199
-42
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import { Container } from "../ui/container";
|
||||
import { Reveal } from "../ui/reveal";
|
||||
import { Button } from "../ui/button";
|
||||
import { Aurora } from "../ui/aurora";
|
||||
import { IconMark } from "../brand/icon-mark";
|
||||
import { getTrackedDownloadHref } from "../../lib/downloads";
|
||||
import { CONTACT_EMAIL, WEBSITE_URL } from "../../lib/site-constants";
|
||||
@@ -40,8 +41,9 @@ export function Footer() {
|
||||
const year = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<footer className="bg-[var(--footer-bg)] text-[var(--footer-text)]">
|
||||
<Container>
|
||||
<footer className="relative overflow-hidden bg-[var(--footer-bg)] text-[var(--footer-text)]">
|
||||
<Aurora className="opacity-90" />
|
||||
<Container className="relative">
|
||||
<div className="border-b border-white/10 py-24 text-center md:py-32">
|
||||
<Reveal>
|
||||
<h2 className="font-display text-[clamp(2.75rem,1.8rem+4vw,5.5rem)] leading-[1] tracking-[-0.02em]">
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
"use client";
|
||||
|
||||
import { ShieldCheck } from "reicon-react/icons/ShieldCheck";
|
||||
import { Container } from "../ui/container";
|
||||
import { Reveal } from "../ui/reveal";
|
||||
import { Eyebrow } from "../ui/eyebrow";
|
||||
import { SectionHeading } from "../ui/section-heading";
|
||||
import { featureSections, type FeatureSection } from "../../lib/landing-content";
|
||||
|
||||
// Explicit order + column spans so the cells pack into full 12-column rows.
|
||||
const layout: { id: string; span: string }[] = [
|
||||
{ id: "org-chart", span: "lg:col-span-7" },
|
||||
{ id: "cost-controls", span: "lg:col-span-5" },
|
||||
{ id: "office", span: "lg:col-span-5" },
|
||||
{ id: "messages", span: "lg:col-span-7" },
|
||||
{ id: "connectors", span: "lg:col-span-8" },
|
||||
];
|
||||
|
||||
const featureById = new Map(featureSections.map((feature) => [feature.id, feature]));
|
||||
|
||||
function CornerGlow() {
|
||||
return (
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute -right-20 -top-20 h-56 w-56 rounded-full opacity-70"
|
||||
style={{ filter: "blur(44px)", background: "radial-gradient(circle, var(--aurora-mint) 0%, transparent 70%)" }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function BentoCard({ feature, span }: { feature: FeatureSection; span: string }) {
|
||||
return (
|
||||
<div
|
||||
className={`group relative flex flex-col overflow-hidden rounded-2xl border border-[var(--border)] bg-[var(--panel)] p-6 transition-colors duration-300 hover:border-[var(--border-strong)] lg:p-7 ${span}`}
|
||||
>
|
||||
<CornerGlow />
|
||||
<div className="relative flex flex-col gap-1.5">
|
||||
<Eyebrow>{feature.eyebrow}</Eyebrow>
|
||||
<h3 className="text-xl font-medium tracking-[-0.02em] text-[var(--text)]">{feature.title}</h3>
|
||||
<p className="max-w-[46ch] text-sm leading-relaxed text-[var(--muted)]">{feature.description}</p>
|
||||
</div>
|
||||
<div className="relative mt-6 overflow-hidden rounded-xl border border-[var(--border)] bg-[var(--panel-illustration)] shadow-[0_18px_50px_-24px_rgba(10,29,8,0.28)]">
|
||||
<img
|
||||
src={feature.image}
|
||||
alt={feature.imageAlt}
|
||||
width={2400}
|
||||
height={1500}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
className="block aspect-[16/10] w-full object-cover object-top transition-transform duration-500 group-hover:scale-[1.02]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LocalFirstCard() {
|
||||
return (
|
||||
<div
|
||||
className="relative flex flex-col justify-between overflow-hidden rounded-2xl border border-[var(--border)] p-7 lg:col-span-4"
|
||||
style={{
|
||||
background:
|
||||
"linear-gradient(160deg, var(--aurora-mint) -40%, transparent 55%), var(--panel)",
|
||||
}}
|
||||
>
|
||||
<span className="inline-flex h-11 w-11 items-center justify-center rounded-full bg-[var(--accent-soft)] text-[var(--accent)]">
|
||||
<ShieldCheck size={22} />
|
||||
</span>
|
||||
<div className="mt-8">
|
||||
<h3 className="text-xl font-medium tracking-[-0.02em] text-[var(--text)]">Local-first and private</h3>
|
||||
<p className="mt-2 max-w-[36ch] text-sm leading-relaxed text-[var(--muted)]">
|
||||
Every company runs on your machine. Data lives in a local SQLite database, secrets are held in your OS
|
||||
keychain, and the only network calls go to the AI provider you connect.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Bento() {
|
||||
return (
|
||||
<section id="features" className="scroll-mt-20 py-20 md:py-28 lg:py-32">
|
||||
<Container>
|
||||
<Reveal>
|
||||
<SectionHeading
|
||||
eyebrow="The platform"
|
||||
title="Everything a company needs, on your desktop"
|
||||
description="One goal in — an org that hires itself, works in the open, plugs into your stack, and holds its own budget."
|
||||
align="center"
|
||||
className="mx-auto"
|
||||
/>
|
||||
</Reveal>
|
||||
|
||||
<Reveal delay={80} className="mt-14 grid grid-cols-1 gap-5 lg:grid-cols-12">
|
||||
{layout.map(({ id, span }) => {
|
||||
const feature = featureById.get(id);
|
||||
return feature ? <BentoCard key={id} feature={feature} span={span} /> : null;
|
||||
})}
|
||||
<LocalFirstCard />
|
||||
</Reveal>
|
||||
</Container>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Reveal } from "../ui/reveal";
|
||||
import { SectionHeading } from "../ui/section-heading";
|
||||
import { MediaFrame } from "../ui/media-frame";
|
||||
import type { FeatureSection as FeatureSectionData } from "../../lib/landing-content";
|
||||
|
||||
/** One feature: heading + subhead above a full-width product panel (adaline rhythm). */
|
||||
export function FeatureSection({ feature }: { feature: FeatureSectionData }) {
|
||||
return (
|
||||
<div id={feature.id} className="flex scroll-mt-24 flex-col gap-8 md:gap-10">
|
||||
<Reveal>
|
||||
<SectionHeading eyebrow={feature.eyebrow} title={feature.title} description={feature.description} />
|
||||
</Reveal>
|
||||
<Reveal delay={90}>
|
||||
<MediaFrame src={feature.image} alt={feature.imageAlt} />
|
||||
</Reveal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Container } from "../ui/container";
|
||||
import { FeatureSection } from "./feature-section";
|
||||
import { featureSections } from "../../lib/landing-content";
|
||||
|
||||
export function Features() {
|
||||
return (
|
||||
<section id="features" className="scroll-mt-20 py-20 md:py-28 lg:py-32">
|
||||
<Container className="flex flex-col gap-24 md:gap-32">
|
||||
{featureSections.map((feature) => (
|
||||
<FeatureSection key={feature.id} feature={feature} />
|
||||
))}
|
||||
</Container>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -52,12 +52,25 @@ export function Hero() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="enter-rise mt-14 md:mt-20" style={{ animationDelay: "0.3s" }}>
|
||||
<MediaFrame
|
||||
src="/images/dashboard.webp"
|
||||
alt="AgentCompany desktop dashboard showing the quarter goal, budget, agent workload, and recent runs"
|
||||
priority
|
||||
<div className="enter-rise relative mt-14 md:mt-20" style={{ animationDelay: "0.3s" }}>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute -inset-x-8 -top-10 bottom-0"
|
||||
style={{
|
||||
filter: "blur(52px)",
|
||||
background:
|
||||
"radial-gradient(55% 45% at 50% 22%, var(--aurora-mint) 0%, transparent 70%)," +
|
||||
"radial-gradient(45% 40% at 22% 65%, var(--aurora-teal) 0%, transparent 72%)," +
|
||||
"radial-gradient(45% 42% at 80% 60%, var(--aurora-lime) 0%, transparent 72%)",
|
||||
}}
|
||||
/>
|
||||
<div className="relative z-10">
|
||||
<MediaFrame
|
||||
src="/images/dashboard.webp"
|
||||
alt="AgentCompany desktop dashboard showing the quarter goal, budget, agent workload, and recent runs"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Container } from "../ui/container";
|
||||
import { Reveal } from "../ui/reveal";
|
||||
import { Aurora } from "../ui/aurora";
|
||||
|
||||
export function Showcase() {
|
||||
return (
|
||||
<section className="relative overflow-hidden bg-[var(--band)] text-[var(--band-text)]">
|
||||
<Aurora />
|
||||
<Container className="relative py-24 md:py-32 lg:py-40">
|
||||
<Reveal className="mx-auto max-w-4xl text-center">
|
||||
<p className="eyebrow text-sm opacity-60">One human in the loop</p>
|
||||
<h2 className="mt-6 font-serif text-[clamp(2.25rem,1.4rem+3.6vw,4.5rem)] font-light leading-[1.05] tracking-[-0.02em]">
|
||||
Set the goal.
|
||||
<br />
|
||||
The company forms around it.
|
||||
</h2>
|
||||
<p className="mx-auto mt-6 max-w-[52ch] text-lg leading-relaxed opacity-75">
|
||||
An AI CEO hires the roles the mission needs, delegates the work, and reports back — while you keep the final
|
||||
say on every decision that matters.
|
||||
</p>
|
||||
</Reveal>
|
||||
</Container>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Northern-lights glow for dark showcase surfaces: three soft, slowly drifting
|
||||
* radial blooms in mint / teal / lime. Purely decorative.
|
||||
*/
|
||||
export function Aurora({ className = "" }: { className?: string }) {
|
||||
return (
|
||||
<div aria-hidden="true" className={`pointer-events-none absolute inset-0 overflow-hidden ${className}`}>
|
||||
<div
|
||||
className="aurora-layer absolute -inset-[25%]"
|
||||
style={{
|
||||
filter: "blur(56px)",
|
||||
background:
|
||||
"radial-gradient(42% 55% at 22% 62%, var(--aurora-mint) 0%, transparent 62%)," +
|
||||
"radial-gradient(38% 50% at 72% 38%, var(--aurora-teal) 0%, transparent 60%)," +
|
||||
"radial-gradient(34% 44% at 52% 84%, var(--aurora-lime) 0%, transparent 58%)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user