"use client";
import { motion, useReducedMotion } from "motion/react";
const EASE_OUT: [number, number, number, number] = [0.16, 1, 0.3, 1];
const VIEWPORT = { once: true, amount: 0.4 } as const;
export type UseCaseArtVariant = "price" | "brand" | "leads" | "serp" | "chat" | "api";
/** Soft infinite pulse ring marking the "live" signal in each artifact. */
function Pulse({
cx,
cy,
reduce,
delay = 0,
}: {
cx: number;
cy: number;
reduce: boolean;
delay?: number;
}) {
if (reduce) return null;
return (
);
}
/** Competitor price monitoring: a price line steps up; the change point alerts. */
function PriceArt({ reduce }: { reduce: boolean }) {
return (
{/* Grid */}
{[24, 48, 72].map((y) => (
))}
{/* Price line with a step change at x=132 */}
{/* Alert at the step */}
{/* Price-change tag */}
+$10/mo
);
}
/** Brand monitoring: a listening radar; mentions blip in around the brand. */
function BrandArt({ reduce }: { reduce: boolean }) {
const blips = [
{ cx: 84, cy: 30, delay: 0.5 },
{ cx: 168, cy: 38, delay: 0.7 },
{ cx: 100, cy: 70, delay: 0.9 },
];
return (
{/* Radar rings */}
{[16, 30, 44].map((r, i) => (
))}
{/* Brand at the center */}
{/* Mention blips */}
{blips.map((blip) => (
))}
);
}
/** B2B lead generation: a lead list fills in row by row, each verified. */
function LeadsArt({ reduce }: { reduce: boolean }) {
const rows = [22, 48, 74];
return (
{rows.map((y, i) => (
{/* Avatar */}
{/* Name + contact lines */}
{/* Verified check */}
))}
);
}
/** Market research: SERP rows; your result climbs from #3 to #1. */
function SerpArt({ reduce }: { reduce: boolean }) {
const swapDelay = 0.8;
const swapY = { duration: 0.5, ease: EASE_OUT, delay: swapDelay, times: [0, 0.6, 1] };
return (
{/* Competitor rows: start at ranks 1 and 2, shift down one slot */}
{[
{ startY: 14, width: 148 },
{ startY: 40, width: 120 },
].map((row, i) => (
))}
{/* Your row: starts at rank 3, climbs to rank 1 */}
);
}
/** Founders & marketers: a plain-English ask streams back an agent brief with cited sources. */
function ChatArt({ reduce }: { reduce: boolean }) {
const briefLines = [
{ y: 38, width: 150, delay: 0.55 },
{ y: 52, width: 180, delay: 0.7 },
{ y: 66, width: 120, delay: 0.85 },
];
return (
{/* User ask: right-aligned bubble */}
{/* Agent avatar */}
{/* Brief streams in line by line */}
{briefLines.map((line) => (
))}
{/* Citation chip */}
3 sources
);
}
/** Developers & agents: a typed request gets a 200 and JSON streams back. */
function ApiArt({ reduce }: { reduce: boolean }) {
const jsonLines = [
{ y: 50, text: '{ "items": [', delay: 0.6 },
{ y: 66, text: ' { "title": "...", "score": 812 },', delay: 0.75 },
{ y: 82, text: "] }", delay: 0.9 },
];
return (
{/* Request line */}
POST
/scrapers/reddit/scrape
{/* 200 OK badge */}
200 OK
{/* JSON response streams in */}
{jsonLines.map((line) => (
{line.text}
))}
{/* Blinking cursor after the closing brace */}
);
}
const ART: Record React.ReactNode> = {
price: PriceArt,
brand: BrandArt,
leads: LeadsArt,
serp: SerpArt,
chat: ChatArt,
api: ApiArt,
};
/** Small animated artifact rendered at the top of each use-case card. */
export function UseCaseArt({ variant }: { variant: UseCaseArtVariant }) {
const reduce = useReducedMotion() ?? false;
const Art = ART[variant];
return (
);
}