Vertical witness list with accent bar and stat expand — logo-cloud-15 DNA applied to registry capabilities.
@nusaiba
registry/blocks/features/7/features-witness-section.tsx
"use client";
import { useCallback, useEffect, useState } from "react";
import { motion, useInView, useReducedMotion } from "motion/react";
import { defaultSpring, motionViewport } from "@/lib/motion-tokens";
import { cn } from "@/lib/utils";
import { SectionShell, ContentRail } from "@/components/layout-contract";
const capabilities = [
{ id: "motion", label: "Motion docs", stat: "40", unit: "live patterns" },
{ id: "cli", label: "Registry CLI", stat: "1", unit: "shadcn-native path" },
{ id: "pro", label: "Pro catalog", stat: "∞", unit: "lifetime updates" },
{ id: "prompt", label: "Copy prompt", stat: "✓", unit: "per block page" },
{ id: "preview", label: "Preview live", stat: "0", unit: "install risk" },
];
const CYCLE_MS = 3600;
function WitnessCapabilityRow({
item,
isActive,
reduceMotion,
onSelect,
}: {
item: (typeof capabilities)[number];
isActive: boolean;
reduceMotion: boolean;
onSelect: () => void;
}) {
return (
<motion.button
animate={
reduceMotion
? undefined
: {
opacity: isActive ? 1 : 0.42,
filter: isActive ? "blur(0px)" : "blur(1px)",
}
}
aria-label={item.label}
aria-pressed={isActive}
className={cn(
"relative flex w-full min-h-12 items-center gap-3 border-l-2 py-2.5 text-left transition-[border-color,transform] active:scale-[0.98]",
"after:absolute after:top-1/2 after:left-8 after:size-10 after:-translate-y-1/2",
isActive ? "border-primary pl-4 md:pl-5" : "border-transparent pl-[calc(1rem+2px)] md:pl-[calc(1.25rem+2px)]",
)}
onClick={onSelect}
transition={{ type: "spring", duration: 0.32, bounce: 0 }}
type="button"
>
<span
className={cn(
"shrink-0 font-medium text-lg tabular-nums tracking-tight md:text-xl",
isActive ? "text-primary" : "text-muted-foreground",
)}
>
{item.stat}
</span>
<motion.div
animate={
isActive
? { opacity: 1, width: "auto", filter: "blur(0px)" }
: { opacity: 0.65, width: "auto", filter: "blur(0px)" }
}
className="min-w-0"
initial={false}
transition={{ type: "spring", duration: 0.32, bounce: 0 }}
>
<p className={cn("font-medium text-sm", isActive && "text-foreground")}>
{item.label}
</p>
<motion.p
animate={
isActive
? { opacity: 1, height: "auto" }
: { opacity: 0, height: 0 }
}
className="overflow-hidden text-muted-foreground text-xs"
initial={false}
transition={{ type: "spring", duration: 0.3, bounce: 0 }}
>
{item.unit}
</motion.p>
</motion.div>
</motion.button>
);
}
export function FeaturesWitnessStackSection() {
const reduceMotion = useReducedMotion();
const [rootNode, setRootNode] = useState<HTMLElement | null>(null);
const isInView = useInView({ current: rootNode }, { ...motionViewport, amount: 0.35 });
const [active, setActive] = useState(0);
const [cycleActive, setCycleActive] = useState(false);
useEffect(() => {
if (!isInView || reduceMotion) return;
setCycleActive(true);
}, [isInView, reduceMotion]);
useEffect(() => {
if (!cycleActive || reduceMotion) return;
const timer = setInterval(() => {
setActive((current) => (current + 1) % capabilities.length);
}, CYCLE_MS);
return () => clearInterval(timer);
}, [cycleActive, reduceMotion]);
const selectItem = useCallback((index: number) => {
setActive(index);
setCycleActive(false);
setTimeout(() => setCycleActive(true), CYCLE_MS);
}, []);
return (
<SectionShell spacingMode="section" className="bg-background" ref={setRootNode}>
<ContentRail maxWidth="max-w-5xl" className="grid gap-8 md:grid-cols-[minmax(0,1fr)_minmax(0,18rem)] md:items-start md:gap-12 lg:gap-16">
<motion.div
className="max-w-md space-y-2 md:pt-2"
initial={reduceMotion ? false : { opacity: 0, y: 8, filter: "blur(4px)" }}
transition={defaultSpring}
viewport={motionViewport}
whileInView={reduceMotion ? undefined : { opacity: 1, y: 0, filter: "blur(0px)" }}
>
<p className="font-medium text-primary text-xs uppercase tracking-[0.22em]">
Proof
</p>
<h2 className="text-balance font-medium text-2xl tracking-tight md:text-4xl">
Built for install workflows
</h2>
<p className="text-pretty text-muted-foreground text-sm leading-relaxed md:text-base">
Witness list with accent bar — same language as logo-cloud-15, applied to
registry capabilities instead of brand marks.
</p>
</motion.div>
<motion.ul
aria-label="Registry capabilities"
className="flex flex-col gap-1"
initial={reduceMotion ? false : { opacity: 0, y: 10 }}
transition={{ ...defaultSpring, delay: 0.06 }}
viewport={motionViewport}
whileInView={reduceMotion ? undefined : { opacity: 1, y: 0 }}
>
{capabilities.map((item, index) => (
<li key={item.id}>
<WitnessCapabilityRow
isActive={active === index}
item={item}
onSelect={() => selectItem(index)}
reduceMotion={!!reduceMotion}
/>
</li>
))}
</motion.ul>
</ContentRail>
</SectionShell>
);
}




























