{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "testimonial-masonry-verified-grid",
  "title": "Testimonial Masonry Verified Grid",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/testimonial-masonry-verified-grid/testimonial-masonry-verified-grid.component.ts",
      "content": "// Masonry grid of verified engineer endorsements with role filters, star ratings,\n// avatar badges, and quantifiable impact metrics. Port of the Vue/React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport { CheckCircle2, LucideAngularModule, Star } from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiCardComponent } from '@/ui/card/card.component'\n\nexport type MasonryRoleFilter = 'all' | 'frontend' | 'founder' | 'design-eng'\n\nexport interface VerifiedTestimonial {\n  id: string\n  name: string\n  role: string\n  roleCategory: 'frontend' | 'founder' | 'design-eng'\n  company: string\n  avatar: string\n  handle: string\n  verifiedSource: 'Twitter' | 'GitHub' | 'LinkedIn'\n  quote: string\n  metricBadge?: string\n  starCount: number\n}\n\nconst testimonials: VerifiedTestimonial[] = [\n  {\n    id: '1',\n    name: 'Alexandre Rivière',\n    role: 'Principal Design Engineer',\n    roleCategory: 'design-eng',\n    company: 'Linear Ecosystem',\n    avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=150&auto=format&fit=crop&q=80',\n    handle: '@alex_riviere',\n    verifiedSource: 'Twitter',\n    quote:\n      'The unbundled component model completely cured our team from npm dependency fatigue. We get raw SFCs and TSX with exact Tailwind v4 token alignments. Zero wrapper bloat, 100% code ownership.',\n    metricBadge: '4.2x Faster Ship Velocity',\n    starCount: 5,\n  },\n  {\n    id: '2',\n    name: 'Sarah Chen',\n    role: 'VP of Engineering',\n    roleCategory: 'founder',\n    company: 'ScaleDev AI',\n    avatar: 'https://images.unsplash.com/photo-1580489944761-15a19d654956?w=150&auto=format&fit=crop&q=80',\n    handle: '@schen_ai',\n    verifiedSource: 'GitHub',\n    quote:\n      'We had severe bundle bloat with our previous UI package (over 500kB of unused JS). Switching to UIPKGE reduced our initial bundle to 38kB and solved our INP scores overnight.',\n    metricBadge: '-88% Bundle Size',\n    starCount: 5,\n  },\n  {\n    id: '3',\n    name: 'Marcus Vance',\n    role: 'Staff Frontend Architect',\n    roleCategory: 'frontend',\n    company: 'HyperQubit Cloud',\n    avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&auto=format&fit=crop&q=80',\n    handle: '@marcus_vance',\n    verifiedSource: 'Twitter',\n    quote:\n      'Dual-framework parity is not a gimmick here—it is mathematically verified. We maintain a Nuxt 3 admin console and Next.js customer portal with identical design tokens and micro-interactions.',\n    metricBadge: '100% Token Parity',\n    starCount: 5,\n  },\n  {\n    id: '4',\n    name: 'Elena Rostova',\n    role: 'Head of Product Design',\n    roleCategory: 'design-eng',\n    company: 'Fintech Velocity',\n    avatar: 'https://images.unsplash.com/photo-1517841905240-472988babdf9?w=150&auto=format&fit=crop&q=80',\n    handle: '@elena_craft',\n    verifiedSource: 'GitHub',\n    quote:\n      'The spring physics and micro-interactions match Linear and Raycast levels of craft. No arbitrary sub-12px micro-text or sloppy contrast issues.',\n    metricBadge: 'WCAG AA AA Certified',\n    starCount: 5,\n  },\n  {\n    id: '5',\n    name: 'David Kim',\n    role: 'Founder & CTO',\n    roleCategory: 'founder',\n    company: 'PulseOps',\n    avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=150&auto=format&fit=crop&q=80',\n    handle: '@dkim_ops',\n    verifiedSource: 'Twitter',\n    quote:\n      'Being able to run `npx shadcn-vue add` and have clean, pristine components in our git repository is the single biggest DX breakthrough since Vite.',\n    metricBadge: 'Zero Upstream Lock-in',\n    starCount: 5,\n  },\n  {\n    id: '6',\n    name: 'Liam O’Connor',\n    role: 'Lead UI Engineer',\n    roleCategory: 'frontend',\n    company: 'Starlight Media',\n    avatar: 'https://images.unsplash.com/photo-1522075469751-3a6694fb2f61?w=150&auto=format&fit=crop&q=80',\n    handle: '@liam_dev',\n    verifiedSource: 'GitHub',\n    quote:\n      'The marketing blocks are actually functional workbenches with live sliders, real SVG telemetry sparklines, and zero dummy shapes. Huge time saver.',\n    metricBadge: '450+ Verified Blocks',\n    starCount: 5,\n  },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-testimonial-masonry-verified-grid, [ui-testimonial-masonry-verified-grid]',\n  standalone: true,\n  // React renders the root <section> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [LucideAngularModule, UiBadgeComponent, UiCardComponent],\n  template: `\n    <section data-slot=\"testimonial-masonry-verified-grid\" [class]=\"rootClass\">\n      <div class=\"mx-auto max-w-7xl space-y-12\">\n        <!-- Section Header -->\n        <div class=\"mx-auto max-w-3xl space-y-4 text-center\">\n          <span ui-badge variant=\"secondary\" class=\"gap-1.5 px-3 py-1 font-mono text-xs shadow-xs\">\n            <lucide-icon [img]=\"Star\" class=\"fill-warning text-warning size-3.5\" />\n            Verified Engineer Endorsements\n          </span>\n          <h2 class=\"text-foreground text-3xl font-bold tracking-tight sm:text-4xl\">\n            Loved by design engineers and CTOs worldwide.\n          </h2>\n          <p class=\"text-muted-foreground text-base\">\n            Real feedback from engineers who dumped monolithic npm packages for unbundled code ownership.\n          </p>\n\n          <!-- Category Filters -->\n          <div class=\"flex flex-wrap items-center justify-center gap-2 pt-2\">\n            <button type=\"button\" [class]=\"filterClass('all')\" (click)=\"setFilter('all')\">\n              All Perspectives ({{ totalCount }})\n            </button>\n            <button type=\"button\" [class]=\"filterClass('design-eng')\" (click)=\"setFilter('design-eng')\">\n              Design Engineers\n            </button>\n            <button type=\"button\" [class]=\"filterClass('frontend')\" (click)=\"setFilter('frontend')\">\n              Frontend Architects\n            </button>\n            <button type=\"button\" [class]=\"filterClass('founder')\" (click)=\"setFilter('founder')\">\n              CTOs &amp; Founders\n            </button>\n          </div>\n        </div>\n\n        <!-- Masonry Grid (3 Columns) -->\n        <div class=\"grid grid-cols-1 items-start gap-6 md:grid-cols-2 lg:grid-cols-3\">\n          @for (t of filteredTestimonials(); track t.id) {\n            <div\n              ui-card\n              class=\"border-border bg-card/95 hover:border-primary/40 group relative flex flex-col justify-between space-y-4 overflow-hidden rounded-2xl p-6 text-left shadow-lg transition-colors hover:shadow-xl\"\n            >\n              <!-- Top Row: Avatar, Name, Handle, Verified Badge -->\n              <div class=\"flex items-start justify-between gap-3\">\n                <div class=\"flex items-center gap-3\">\n                  <img\n                    [src]=\"t.avatar\"\n                    [alt]=\"t.name\"\n                    class=\"border-border size-10 shrink-0 rounded-full border object-cover\"\n                  />\n                  <div class=\"min-w-0\">\n                    <div class=\"flex items-center gap-1.5\">\n                      <h4 class=\"text-foreground truncate font-mono text-xs font-bold\">{{ t.name }}</h4>\n                      <lucide-icon [img]=\"CheckCircle2\" class=\"text-info size-3.5 shrink-0\" />\n                    </div>\n                    <p class=\"text-muted-foreground truncate text-xs\">{{ t.role }} &bull; {{ t.company }}</p>\n                  </div>\n                </div>\n\n                <span ui-badge variant=\"outline\" class=\"text-muted-foreground shrink-0 font-mono text-xs\">\n                  {{ t.verifiedSource }}\n                </span>\n              </div>\n\n              <!-- Quote text -->\n              <p class=\"text-foreground/90 text-xs leading-relaxed italic sm:text-sm\">&ldquo;{{ t.quote }}&rdquo;</p>\n\n              <!-- Footer Row: Star Rating & Impact Metric Badge -->\n              <div class=\"border-border/60 flex flex-wrap items-center justify-between gap-2 border-t pt-3\">\n                <div class=\"flex items-center gap-0.5\">\n                  @for (s of stars(t.starCount); track s) {\n                    <lucide-icon [img]=\"Star\" class=\"fill-warning text-warning size-3.5\" />\n                  }\n                </div>\n\n                @if (t.metricBadge) {\n                  <span\n                    ui-badge\n                    variant=\"outline\"\n                    class=\"border-success/20 bg-success/10 text-success font-mono text-xs\"\n                  >\n                    {{ t.metricBadge }}\n                  </span>\n                }\n              </div>\n            </div>\n          }\n        </div>\n      </div>\n    </section>\n  `,\n})\nexport class UiTestimonialMasonryVerifiedGridComponent {\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly Star = Star\n\n  @Input() set initialFilter(v: MasonryRoleFilter) {\n    this.activeFilter.set(v ?? 'all')\n  }\n  @Input('class') className?: string\n\n  readonly activeFilter = signal<MasonryRoleFilter>('all')\n\n  readonly filteredTestimonials = computed(() => {\n    const f = this.activeFilter()\n    if (f === 'all') return testimonials\n    return testimonials.filter((t) => t.roleCategory === f)\n  })\n\n  get totalCount(): number {\n    return testimonials.length\n  }\n\n  get rootClass(): string {\n    return cn('bg-background relative overflow-hidden px-4 py-16 sm:px-6 sm:py-24 lg:px-8', this.className)\n  }\n\n  setFilter(f: MasonryRoleFilter): void {\n    this.activeFilter.set(f)\n  }\n\n  filterClass(f: MasonryRoleFilter): string {\n    return cn(\n      'rounded-lg border px-3.5 py-1 font-mono text-xs transition-colors',\n      this.activeFilter() === f\n        ? 'border-primary bg-primary text-primary-foreground font-semibold shadow-xs'\n        : 'border-border bg-card text-muted-foreground hover:text-foreground',\n    )\n  }\n\n  stars(count: number): number[] {\n    return Array.from({ length: count }, (_, i) => i)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/testimonial-masonry-verified-grid/testimonial-masonry-verified-grid.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/testimonial-masonry-verified-grid/index.ts",
      "content": "export {\n  UiTestimonialMasonryVerifiedGridComponent,\n  type MasonryRoleFilter,\n  type VerifiedTestimonial,\n} from './testimonial-masonry-verified-grid.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/testimonial-masonry-verified-grid/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/card.json"
  ],
  "description": "Masonry grid of verified engineer endorsements with role filters, star ratings, avatar badges, and quantifiable impact metrics.",
  "categories": [
    "marketing"
  ],
  "deprecated": true,
  "replacedBy": "testimonial"
}