"use client";
import * as React from "react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import { ArrowRight, Building2, Check, Eye, EyeOff, Fingerprint, Loader2, Quote } from "lucide-react";
import { cn } from "@/lib/utils";
export type AuthMode = "sign-in" | "sign-up";
export type AuthValues = { name: string; email: string; password: string; remember: boolean };
export type AuthTestimonial = { quote: string; name: string; role: string };
export interface AuthSplitProps {
brand?: string;
defaultMode?: AuthMode;
testimonials?: AuthTestimonial[];
/** Resolve to finish, throw an Error to show its message as a form error. */
onSubmit?: (mode: AuthMode, values: AuthValues) => Promise<void>;
onForgotPassword?: (email: string) => void;
onSocial?: (provider: "passkey" | "sso") => void;
className?: string;
}
const DEFAULT_TESTIMONIALS: AuthTestimonial[] = [
{ quote: "We replaced three internal tools in a weekend. The team actually enjoys opening it every morning.", name: "Maya Okafor", role: "Head of Ops, Northwind" },
{ quote: "Onboarding a new engineer went from two days of setup to about twenty minutes.", name: "Jonas Weber", role: "CTO, Orbit Labs" },
{ quote: "It's the rare product that gets faster the more we use it. Our reviews ship a day earlier.", name: "Priya Raman", role: "Design Lead, Acme" },
];
const emailOk = (v: string) => /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v.trim());
function strength(pw: string) {
let s = 0;
if (pw.length >= 8) s++;
if (pw.length >= 12) s++;
if (/[A-Z]/.test(pw) && /[a-z]/.test(pw)) s++;
if (/\d/.test(pw)) s++;
if (/[^A-Za-z0-9]/.test(pw)) s++;
return Math.min(4, s);
}
const STRENGTH = ["Too weak", "Weak", "Okay", "Good", "Strong"];
function useHeight<T extends HTMLElement>() {
const ref = React.useRef<T>(null);
const [height, setHeight] = React.useState<number | "auto">("auto");
React.useEffect(() => {
const el = ref.current;
if (!el) return;
const ro = new ResizeObserver(() => setHeight(el.offsetHeight));
ro.observe(el);
return () => ro.disconnect();
}, []);
return [ref, height] as const;
}
function BrandMark({ className }: { className?: string }) {
return (
<svg viewBox="0 0 32 32" className={className} aria-hidden>
<defs>
<linearGradient id="auth-split-mark" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stopColor="#818cf8" />
<stop offset="1" stopColor="#c026d3" />
</linearGradient>
</defs>
<rect width="32" height="32" rx="9" fill="url(#auth-split-mark)" />
<path d="M10 9v14h12" fill="none" stroke="white" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
<circle cx="21" cy="11" r="2.5" fill="white" />
</svg>
);
}
type Errors = Partial<Record<"name" | "email" | "password" | "terms" | "form", string>>;
export function AuthSplit({
brand = "Lumen",
defaultMode = "sign-in",
testimonials = DEFAULT_TESTIMONIALS,
onSubmit,
onForgotPassword,
onSocial,
className,
}: AuthSplitProps) {
const reduce = useReducedMotion();
const uid = React.useId();
const [mode, setMode] = React.useState<AuthMode>(defaultMode);
const [values, setValues] = React.useState<AuthValues>({ name: "", email: "", password: "", remember: true });
const [terms, setTerms] = React.useState(false);
const [show, setShow] = React.useState(false);
const [touched, setTouched] = React.useState<Record<string, boolean>>({});
const [submitted, setSubmitted] = React.useState(false);
const [status, setStatus] = React.useState<"idle" | "loading" | "done">("idle");
const [formError, setFormError] = React.useState("");
const [notice, setNotice] = React.useState("");
const [heightRef, height] = useHeight<HTMLDivElement>();
const signUp = mode === "sign-up";
const errors: Errors = {};
if (signUp && values.name.trim().length < 2) errors.name = "Enter your full name.";
if (!values.email.trim()) errors.email = "Email is required.";
else if (!emailOk(values.email)) errors.email = "Enter a valid email address.";
if (!values.password) errors.password = "Password is required.";
else if (signUp && values.password.length < 8) errors.password = "Use at least 8 characters.";
if (signUp && !terms) errors.terms = "Please accept the terms to continue.";
const visible = (k: keyof Errors) => (submitted || touched[k] ? errors[k] : undefined);
const set = <K extends keyof AuthValues>(k: K, v: AuthValues[K]) => {
setValues((s) => ({ ...s, [k]: v }));
setFormError("");
};
const switchMode = (m: AuthMode) => {
if (m === mode) return;
setMode(m);
setSubmitted(false);
setTouched({});
setFormError("");
setNotice("");
setStatus("idle");
};
const submit = async (e: React.FormEvent) => {
e.preventDefault();
setSubmitted(true);
setNotice("");
if (Object.keys(errors).length) {
const first = (["name", "email", "password", "terms"] as const).find((k) => errors[k]);
if (first) document.getElementById(`${uid}-${first}`)?.focus();
return;
}
setStatus("loading");
try {
if (onSubmit) await onSubmit(mode, values);
else await new Promise((r) => setTimeout(r, 1300));
setStatus("done");
} catch (err) {
setStatus("idle");
setFormError(err instanceof Error ? err.message : "Something went wrong. Try again.");
}
};
const forgot = () => {
if (!emailOk(values.email)) {
setTouched((t) => ({ ...t, email: true }));
setNotice("");
document.getElementById(`${uid}-email`)?.focus();
return;
}
onForgotPassword?.(values.email);
setNotice(`Reset link sent to ${values.email.trim()}.`);
};
const errorCount = submitted ? Object.keys(errors).length : 0;
const pwStrength = strength(values.password);
return (
<section className={cn("grid min-h-[760px] w-full bg-background text-foreground lg:grid-cols-[minmax(0,1fr)_minmax(0,1.05fr)]", className)}>
{/* Form side */}
<div className="flex flex-col px-5 py-8 sm:px-10 lg:px-16">
<div className="flex items-center gap-2.5">
<BrandMark className="size-8" />
<span className="text-lg font-semibold tracking-tight">{brand}</span>
</div>
<div className="mx-auto flex w-full max-w-sm flex-1 flex-col justify-center py-10">
<AnimatePresence mode="wait" initial={false}>
{status === "done" ? (
<motion.div
key="done"
initial={reduce ? { opacity: 0 } : { opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
className="text-center"
role="status"
>
<motion.div
initial={reduce ? false : { scale: 0.4 }}
animate={{ scale: 1 }}
transition={{ type: "spring", stiffness: 300, damping: 16 }}
className="mx-auto grid size-14 place-items-center rounded-full bg-emerald-500/12 text-emerald-600 ring-8 ring-emerald-500/5 dark:text-emerald-400"
>
<Check className="size-7" strokeWidth={2.5} />
</motion.div>
<h1 className="mt-6 text-2xl font-semibold tracking-tight">{signUp ? "Account created" : "Welcome back"}</h1>
<p className="mt-2 text-sm text-muted-foreground">
{signUp ? `We sent a confirmation email to ${values.email.trim()}.` : `Signed in as ${values.email.trim()}. Redirecting to your workspace…`}
</p>
<button
type="button"
onClick={() => {
setStatus("idle");
setSubmitted(false);
setValues((v) => ({ ...v, password: "" }));
}}
className="mt-6 text-sm font-medium text-primary underline-offset-4 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded"
>
Back to {signUp ? "sign up" : "sign in"}
</button>
</motion.div>
) : (
<motion.div key="form" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>
<AnimatePresence mode="wait" initial={false}>
<motion.div
key={mode}
initial={reduce ? { opacity: 0 } : { opacity: 0, y: 6 }}
animate={{ opacity: 1, y: 0 }}
exit={reduce ? { opacity: 0 } : { opacity: 0, y: -6 }}
transition={{ duration: 0.18 }}
>
<h1 className="text-2xl font-semibold tracking-tight sm:text-[1.7rem]">{signUp ? "Create your account" : "Sign in to " + brand}</h1>
<p className="mt-1.5 text-sm text-muted-foreground">
{signUp ? "Start your 14-day trial. No credit card required." : "Welcome back — pick up right where you left off."}
</p>
</motion.div>
</AnimatePresence>
<div role="tablist" aria-label="Authentication mode" className="mt-6 grid grid-cols-2 rounded-xl border bg-muted/60 p-1">
{(["sign-in", "sign-up"] as const).map((m) => (
<button
key={m}
role="tab"
type="button"
aria-selected={mode === m}
onClick={() => switchMode(m)}
onKeyDown={(e) => {
if (e.key === "ArrowRight" || e.key === "ArrowLeft") switchMode(mode === "sign-in" ? "sign-up" : "sign-in");
}}
tabIndex={mode === m ? 0 : -1}
className={cn(
"relative h-9 rounded-lg text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
mode === m ? "text-foreground" : "text-muted-foreground hover:text-foreground",
)}
>
{mode === m && (
<motion.span
layoutId={`${uid}-pill`}
className="absolute inset-0 rounded-lg bg-background shadow-sm ring-1 ring-border"
transition={reduce ? { duration: 0 } : { type: "spring", stiffness: 420, damping: 34 }}
/>
)}
<span className="relative">{m === "sign-in" ? "Sign in" : "Sign up"}</span>
</button>
))}
</div>
<div className="mt-5 grid grid-cols-2 gap-2.5">
<button
type="button"
onClick={() => onSocial?.("passkey")}
className="inline-flex h-10 items-center justify-center gap-2 rounded-lg border bg-background text-sm font-medium shadow-xs transition hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
<Fingerprint className="size-4" /> Passkey
</button>
<button
type="button"
onClick={() => onSocial?.("sso")}
className="inline-flex h-10 items-center justify-center gap-2 rounded-lg border bg-background text-sm font-medium shadow-xs transition hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
<Building2 className="size-4" /> Single sign-on
</button>
</div>
<div className="my-5 flex items-center gap-3 text-xs text-muted-foreground">
<span className="h-px flex-1 bg-border" />
or continue with email
<span className="h-px flex-1 bg-border" />
</div>
<motion.div animate={{ height }} transition={reduce ? { duration: 0 } : { type: "spring", stiffness: 380, damping: 36 }} className="-mx-1 overflow-hidden px-1">
<div ref={heightRef}>
<form noValidate onSubmit={submit} className="space-y-4 pb-1" aria-describedby={errorCount ? `${uid}-summary` : undefined}>
<p id={`${uid}-summary`} aria-live="polite" className="sr-only">
{errorCount ? `${errorCount} field${errorCount > 1 ? "s need" : " needs"} attention.` : ""}
</p>
<AnimatePresence initial={false}>
{signUp && (
<motion.div
key="name"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
>
<Field id={`${uid}-name`} label="Full name" error={visible("name")}>
<input
id={`${uid}-name`}
autoComplete="name"
value={values.name}
onChange={(e) => set("name", e.target.value)}
onBlur={() => setTouched((t) => ({ ...t, name: true }))}
aria-invalid={!!visible("name")}
aria-describedby={visible("name") ? `${uid}-name-err` : undefined}
placeholder="Ada Lovelace"
className={inputCls(!!visible("name"))}
/>
</Field>
</motion.div>
)}
</AnimatePresence>
<Field id={`${uid}-email`} label="Work email" error={visible("email")}>
<input
id={`${uid}-email`}
type="email"
inputMode="email"
autoComplete="email"
value={values.email}
onChange={(e) => set("email", e.target.value)}
onBlur={() => setTouched((t) => ({ ...t, email: true }))}
aria-invalid={!!visible("email")}
aria-describedby={visible("email") ? `${uid}-email-err` : undefined}
placeholder="[email protected]"
className={inputCls(!!visible("email"))}
/>
</Field>
<Field
id={`${uid}-password`}
label="Password"
error={visible("password")}
aside={
!signUp ? (
<button
type="button"
onClick={forgot}
className="rounded text-xs font-medium text-primary underline-offset-4 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
Forgot password?
</button>
) : null
}
>
<div className="relative">
<input
id={`${uid}-password`}
type={show ? "text" : "password"}
autoComplete={signUp ? "new-password" : "current-password"}
value={values.password}
onChange={(e) => set("password", e.target.value)}
onBlur={() => setTouched((t) => ({ ...t, password: true }))}
aria-invalid={!!visible("password")}
aria-describedby={[visible("password") ? `${uid}-password-err` : "", signUp ? `${uid}-strength` : ""].filter(Boolean).join(" ") || undefined}
placeholder={signUp ? "At least 8 characters" : "••••••••"}
className={cn(inputCls(!!visible("password")), "pr-11")}
/>
<button
type="button"
onClick={() => setShow((s) => !s)}
aria-label={show ? "Hide password" : "Show password"}
aria-pressed={show}
className="absolute right-1.5 top-1/2 grid size-8 -translate-y-1/2 place-items-center rounded-md text-muted-foreground transition hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
{show ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
</button>
</div>
</Field>
{signUp && (
<div id={`${uid}-strength`} className="-mt-1 space-y-1.5" aria-live="polite">
<div className="grid grid-cols-4 gap-1.5" aria-hidden>
{[0, 1, 2, 3].map((i) => (
<span key={i} className="h-1 overflow-hidden rounded-full bg-muted">
<motion.span
className={cn(
"block h-full rounded-full",
pwStrength <= 1 ? "bg-rose-500" : pwStrength === 2 ? "bg-amber-500" : "bg-emerald-500",
)}
initial={false}
animate={{ width: values.password && i < Math.max(1, pwStrength) ? "100%" : "0%" }}
transition={{ duration: 0.25 }}
/>
</span>
))}
</div>
<p className="text-xs text-muted-foreground">
{values.password ? `Password strength: ${STRENGTH[pwStrength]}` : "Mix letters, numbers and a symbol for a strong password."}
</p>
</div>
)}
{signUp ? (
<div>
<Checkbox
id={`${uid}-terms`}
checked={terms}
onChange={(v) => {
setTerms(v);
setTouched((t) => ({ ...t, terms: true }));
}}
invalid={!!visible("terms")}
describedBy={visible("terms") ? `${uid}-terms-err` : undefined}
>
I agree to the <a href="#terms" className="font-medium text-foreground underline underline-offset-4">Terms</a> and{" "}
<a href="#privacy" className="font-medium text-foreground underline underline-offset-4">Privacy Policy</a>
</Checkbox>
{visible("terms") && (
<p id={`${uid}-terms-err`} className="mt-1.5 text-xs font-medium text-destructive">
{visible("terms")}
</p>
)}
</div>
) : (
<Checkbox id={`${uid}-remember`} checked={values.remember} onChange={(v) => set("remember", v)}>
Keep me signed in for 30 days
</Checkbox>
)}
<AnimatePresence>
{(formError || notice) && (
<motion.p
role={formError ? "alert" : "status"}
initial={{ opacity: 0, y: -4 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
className={cn(
"rounded-lg border px-3 py-2 text-sm",
formError ? "border-destructive/30 bg-destructive/10 text-destructive" : "border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300",
)}
>
{formError || notice}
</motion.p>
)}
</AnimatePresence>
<button
type="submit"
disabled={status === "loading"}
aria-busy={status === "loading"}
className="group relative inline-flex h-11 w-full items-center justify-center gap-2 overflow-hidden rounded-lg bg-foreground text-sm font-medium text-background shadow-sm transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-wait disabled:opacity-80"
>
{status === "loading" ? (
<>
<Loader2 className="size-4 animate-spin" /> {signUp ? "Creating account…" : "Signing in…"}
</>
) : (
<>
{signUp ? "Create account" : "Sign in"}
<ArrowRight className="size-4 transition-transform group-hover:translate-x-0.5" />
</>
)}
</button>
</form>
</div>
</motion.div>
<p className="mt-6 text-center text-sm text-muted-foreground">
{signUp ? "Already have an account? " : "New to " + brand + "? "}
<button
type="button"
onClick={() => switchMode(signUp ? "sign-in" : "sign-up")}
className="rounded font-medium text-foreground underline-offset-4 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
{signUp ? "Sign in" : "Create an account"}
</button>
</p>
</motion.div>
)}
</AnimatePresence>
</div>
<p className="text-xs text-muted-foreground">© 2026 {brand} Inc. · Protected by device-bound sessions.</p>
</div>
{/* Art side */}
<ArtPanel testimonials={testimonials} brand={brand} />
</section>
);
}
function inputCls(invalid: boolean) {
return cn(
"h-11 w-full rounded-lg border bg-background px-3.5 text-sm shadow-xs outline-none transition placeholder:text-muted-foreground/70",
"focus-visible:border-ring focus-visible:ring-4 focus-visible:ring-ring/15",
invalid && "border-destructive focus-visible:border-destructive focus-visible:ring-destructive/15",
);
}
function Field({ id, label, error, aside, children }: { id: string; label: string; error?: string; aside?: React.ReactNode; children: React.ReactNode }) {
return (
<div>
<div className="mb-1.5 flex items-center justify-between">
<label htmlFor={id} className="text-sm font-medium">
{label}
</label>
{aside}
</div>
{children}
<AnimatePresence initial={false}>
{error && (
<motion.p
id={`${id}-err`}
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
className="overflow-hidden text-xs font-medium text-destructive"
>
<span className="block pt-1.5">{error}</span>
</motion.p>
)}
</AnimatePresence>
</div>
);
}
function Checkbox({
id,
checked,
onChange,
invalid,
describedBy,
children,
}: {
id: string;
checked: boolean;
onChange: (v: boolean) => void;
invalid?: boolean;
describedBy?: string;
children: React.ReactNode;
}) {
return (
<div className="flex items-start gap-2.5">
<span className="relative mt-0.5 grid size-4 shrink-0 place-items-center">
<input
id={id}
type="checkbox"
checked={checked}
onChange={(e) => onChange(e.target.checked)}
aria-invalid={invalid || undefined}
aria-describedby={describedBy}
className={cn(
"peer size-4 cursor-pointer appearance-none rounded-[5px] border bg-background shadow-xs transition checked:border-foreground checked:bg-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
invalid && "border-destructive",
)}
/>
<Check className="pointer-events-none absolute size-3 text-background opacity-0 peer-checked:opacity-100" strokeWidth={3} />
</span>
<label htmlFor={id} className="text-sm leading-5 text-muted-foreground">
{children}
</label>
</div>
);
}
function ArtPanel({ testimonials, brand }: { testimonials: AuthTestimonial[]; brand: string }) {
const reduce = useReducedMotion();
const [index, setIndex] = React.useState(0);
const [paused, setPaused] = React.useState(false);
const count = testimonials.length;
React.useEffect(() => {
if (paused || count < 2) return;
const t = setInterval(() => setIndex((i) => (i + 1) % count), 5500);
return () => clearInterval(t);
}, [paused, count]);
const t = testimonials[index % Math.max(1, count)];
const blobs = [
{ c: "#6366f1", x: ["-10%", "20%", "-10%"], y: ["-10%", "15%", "-10%"], s: "70%", d: 16 },
{ c: "#d946ef", x: ["60%", "35%", "60%"], y: ["10%", "45%", "10%"], s: "60%", d: 19 },
{ c: "#06b6d4", x: ["10%", "45%", "10%"], y: ["60%", "35%", "60%"], s: "65%", d: 22 },
];
return (
<aside
className="relative m-3 hidden overflow-hidden rounded-3xl bg-[#0b0b1a] text-white lg:flex lg:flex-col"
onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}
aria-label="Customer stories"
>
<div className="absolute inset-0" aria-hidden>
{blobs.map((b, i) => (
<motion.div
key={i}
className="absolute rounded-full opacity-70 blur-3xl"
style={{ width: b.s, height: b.s, background: b.c, left: 0, top: 0 }}
initial={{ x: b.x[0], y: b.y[0] }}
animate={reduce ? undefined : { x: b.x, y: b.y, scale: [1, 1.15, 1] }}
transition={{ duration: b.d, repeat: Infinity, ease: "easeInOut" }}
/>
))}
<div
className="absolute inset-0 opacity-[0.18]"
style={{
backgroundImage: "linear-gradient(rgba(255,255,255,.5) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.5) 1px, transparent 1px)",
backgroundSize: "44px 44px",
maskImage: "radial-gradient(ellipse at 50% 40%, black 20%, transparent 75%)",
WebkitMaskImage: "radial-gradient(ellipse at 50% 40%, black 20%, transparent 75%)",
}}
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/10 to-transparent" />
</div>
<div className="relative flex items-center justify-between p-8">
<span className="rounded-full border border-white/20 bg-white/10 px-3 py-1 text-xs font-medium backdrop-blur">Trusted by 4,000+ teams</span>
<span className="text-xs text-white/70">SOC 2 Type II · GDPR</span>
</div>
<div className="relative mt-auto p-8 xl:p-10">
<div className="mb-8 grid max-w-md grid-cols-3 gap-3">
{[
["99.99%", "uptime"],
["38ms", "p95 latency"],
["4.9/5", "avg. rating"],
].map(([v, l]) => (
<div key={l} className="rounded-xl border border-white/15 bg-white/[0.07] p-3 backdrop-blur-md">
<p className="text-lg font-semibold tabular-nums">{v}</p>
<p className="text-xs text-white/70">{l}</p>
</div>
))}
</div>
<Quote className="size-7 text-white/50" aria-hidden />
<div className="relative mt-3 min-h-[9.5rem]" aria-live="polite">
<AnimatePresence mode="wait">
{t && (
<motion.figure
key={index}
initial={reduce ? { opacity: 0 } : { opacity: 0, y: 14, filter: "blur(6px)" }}
animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
exit={reduce ? { opacity: 0 } : { opacity: 0, y: -10, filter: "blur(6px)" }}
transition={{ duration: 0.45 }}
>
<blockquote className="max-w-lg text-xl font-medium leading-snug tracking-tight text-balance xl:text-2xl">“{t.quote}”</blockquote>
<figcaption className="mt-5 flex items-center gap-3">
<span className="grid size-10 place-items-center rounded-full bg-white/15 text-sm font-semibold ring-1 ring-white/25">
{t.name
.split(" ")
.map((p) => p[0])
.join("")
.slice(0, 2)}
</span>
<span>
<span className="block text-sm font-semibold">{t.name}</span>
<span className="block text-sm text-white/70">{t.role}</span>
</span>
</figcaption>
</motion.figure>
)}
</AnimatePresence>
</div>
<div className="mt-6 flex items-center gap-2">
{testimonials.map((x, i) => (
<button
key={x.name}
type="button"
onClick={() => setIndex(i)}
aria-label={`Show story ${i + 1} of ${count}`}
aria-current={i === index}
className="group relative h-1.5 overflow-hidden rounded-full bg-white/20 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white"
style={{ width: i === index ? 40 : 16 }}
>
{i === index && !paused && !reduce && (
<motion.span key={index} className="absolute inset-y-0 left-0 bg-white" initial={{ width: "0%" }} animate={{ width: "100%" }} transition={{ duration: 5.5, ease: "linear" }} />
)}
{i === index && (paused || reduce) && <span className="absolute inset-0 bg-white" />}
</button>
))}
<span className="ml-auto text-xs text-white/60">{brand} customer stories</span>
</div>
</div>
</aside>
);
}