{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "testimonials-marquee",
  "title": "Testimonial marquee",
  "description": "Two rows of review cards scrolling in opposite directions, with faded edges and pause on hover or focus.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "avatar",
    "https://velora.colorlib.com/r/blur-fade.json",
    "https://velora.colorlib.com/r/marquee.json"
  ],
  "files": [
    {
      "path": "src/components/blocks/testimonials-marquee.tsx",
      "content": "import { StarIcon } from \"lucide-react\";\n\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\nimport { BlurFade } from \"@/components/velora/blur-fade\";\nimport { Marquee } from \"@/components/velora/marquee\";\nimport { cn } from \"@/lib/utils\";\n\nconst photo = (id: string) =>\n  `https://images.unsplash.com/photo-${id}?w=160&h=160&q=70&auto=format&fit=crop`;\n\ntype Review = {\n  quote: string;\n  name: string;\n  role: string;\n  company: string;\n  avatar?: string;\n};\n\nconst TOP_ROW: Review[] = [\n  {\n    quote:\n      \"We replaced three tools and a weekly sync with Acme. Planning used to take a morning — now it takes a coffee.\",\n    name: \"Maya Lindqvist\",\n    role: \"Head of Product\",\n    company: \"Northwind\",\n    avatar: photo(\"1494790108377-be9c29b29330\"),\n  },\n  {\n    quote:\n      \"The release notes write themselves. Our support queue dropped by a third the month we switched.\",\n    name: \"Daniel Okafor\",\n    role: \"Engineering Manager\",\n    company: \"Lumen Labs\",\n    avatar: photo(\"1507003211169-0a1dd7228f2d\"),\n  },\n  {\n    quote:\n      \"Finally a roadmap the whole company actually reads. Sales stopped asking me what ships next.\",\n    name: \"Priya Raman\",\n    role: \"VP Product\",\n    company: \"Halcyon\",\n  },\n  {\n    quote:\n      \"Onboarding a new engineer went from two weeks of Slack archaeology to a single linked doc.\",\n    name: \"Tomás Herrera\",\n    role: \"Staff Engineer\",\n    company: \"Orbit\",\n    avatar: photo(\"1500648767791-00dcc994a43e\"),\n  },\n  {\n    quote:\n      \"It's calm software. Nothing pings unless it matters, and everything is where I expect it.\",\n    name: \"Hannah Weiss\",\n    role: \"Design Lead\",\n    company: \"Kestrel\",\n    avatar: photo(\"1438761681033-6461ffad8d80\"),\n  },\n  {\n    quote:\n      \"We run quarterly planning for 40 teams in Acme. It scales without turning into a spreadsheet.\",\n    name: \"Samuel Achebe\",\n    role: \"COO\",\n    company: \"Meridian\",\n  },\n];\n\nconst BOTTOM_ROW: Review[] = [\n  {\n    quote:\n      \"Docs, specs and tickets finally live next to each other. Context switching is way down.\",\n    name: \"Lena Fischer\",\n    role: \"Product Designer\",\n    company: \"Foxglove\",\n    avatar: photo(\"1534528741775-53994a69daeb\"),\n  },\n  {\n    quote:\n      \"The GitHub sync is the first one that doesn't need babysitting. It just stays correct.\",\n    name: \"Marcus Bell\",\n    role: \"CTO\",\n    company: \"Tidewater\",\n    avatar: photo(\"1506794778202-cad84cf45f1d\"),\n  },\n  {\n    quote:\n      \"I can see what shipped, what slipped and why, in one screen, without asking anyone.\",\n    name: \"Aiko Tanaka\",\n    role: \"Chief of Staff\",\n    company: \"Acme Corp\",\n  },\n  {\n    quote:\n      \"Our changelog went from an afterthought to something customers subscribe to. Wild.\",\n    name: \"Nadia Haddad\",\n    role: \"Growth Lead\",\n    company: \"Quartzite\",\n    avatar: photo(\"1531123897727-8f129e1688ce\"),\n  },\n  {\n    quote:\n      \"Setup took an afternoon. The import brought over five years of issues without a hitch.\",\n    name: \"Grace Whitfield\",\n    role: \"Engineering Director\",\n    company: \"Pinewood\",\n    avatar: photo(\"1580489944761-15a19d654956\"),\n  },\n  {\n    quote:\n      \"Keyboard-first, fast and quiet. My team stopped complaining about process tooling.\",\n    name: \"Rafael Costa\",\n    role: \"Tech Lead\",\n    company: \"Solstice\",\n  },\n];\n\nconst initials = (name: string) =>\n  name\n    .split(\" \")\n    .map((part) => part[0])\n    .slice(0, 2)\n    .join(\"\");\n\nfunction ReviewCard({ quote, name, role, company, avatar }: Review) {\n  return (\n    <figure className=\"flex w-72 shrink-0 flex-col justify-between gap-6 rounded-2xl border bg-card p-6 text-card-foreground shadow-xs transition-colors hover:border-foreground/20 sm:w-96\">\n      <div>\n        <div className=\"flex gap-0.5 text-brand\" role=\"img\" aria-label=\"Rated 5 out of 5\">\n          {Array.from({ length: 5 }).map((_, i) => (\n            <StarIcon key={i} aria-hidden className=\"size-4 fill-current\" />\n          ))}\n        </div>\n        <blockquote className=\"mt-4 text-sm leading-relaxed text-pretty sm:text-base\">\n          <p>&ldquo;{quote}&rdquo;</p>\n        </blockquote>\n      </div>\n      <figcaption className=\"flex items-center gap-3\">\n        <Avatar size=\"lg\">\n          {avatar && <AvatarImage src={avatar} alt=\"\" />}\n          <AvatarFallback className=\"bg-gradient-to-br from-brand-from to-brand-to text-xs font-semibold text-brand-foreground\">\n            {initials(name)}\n          </AvatarFallback>\n        </Avatar>\n        <div className=\"min-w-0 text-sm\">\n          <div className=\"truncate font-medium\">{name}</div>\n          <div className=\"truncate text-muted-foreground\">\n            {role}, {company}\n          </div>\n        </div>\n      </figcaption>\n    </figure>\n  );\n}\n\nfunction ReviewRow({ reviews, reverse }: { reviews: Review[]; reverse?: boolean }) {\n  return (\n    <Marquee\n      pauseOnHover\n      reverse={reverse}\n      repeat={2}\n      className={cn(\n        \"py-1 [--gap:1.25rem]\",\n        reverse ? \"[--duration:70s]\" : \"[--duration:60s]\"\n      )}\n    >\n      {reviews.map((review) => (\n        <ReviewCard key={review.name} {...review} />\n      ))}\n    </Marquee>\n  );\n}\n\n/**\n * Wall of love: two rows of review cards drifting in opposite directions.\n * Rows pause on hover or keyboard focus and stop entirely under\n * `prefers-reduced-motion`, where the cards wrap into a static grid.\n */\nexport function TestimonialsMarquee() {\n  return (\n    <section className=\"relative isolate overflow-hidden px-6 py-24 sm:py-32 lg:px-8\">\n      <div\n        aria-hidden\n        className=\"absolute inset-x-0 top-1/2 -z-10 mx-auto h-72 max-w-4xl -translate-y-1/2 rounded-full bg-brand/10 blur-3xl\"\n      />\n\n      <div className=\"mx-auto max-w-2xl text-center\">\n        <BlurFade>\n          <p className=\"text-sm font-semibold text-brand\">Testimonials</p>\n          <h2 className=\"mt-3 text-3xl font-semibold tracking-tight text-balance sm:text-5xl\">\n            Loved by teams who ship every week\n          </h2>\n          <p className=\"mx-auto mt-5 max-w-xl text-lg text-pretty text-muted-foreground\">\n            Thousands of product teams plan, build and launch with Acme. Here is\n            what a few of them have to say.\n          </p>\n        </BlurFade>\n      </div>\n\n      <div className=\"-mx-6 mt-16 flex flex-col gap-5 lg:-mx-8\">\n        <ReviewRow reviews={TOP_ROW} />\n        <ReviewRow reviews={BOTTOM_ROW} reverse />\n      </div>\n    </section>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/blocks/testimonials-marquee.tsx"
    }
  ],
  "categories": [
    "testimonial-sections"
  ],
  "type": "registry:block"
}