{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "feature-alternating-rows",
  "title": "Feature Deep-Dive — Alternating Rows",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/feature-alternating-rows/feature-alternating-rows.component.ts",
      "content": "// Classic zig-zag feature deep-dive: three full-width rows alternating copy and\n// visual, each with an eyebrow, headline, checklist, inline link, and a bordered\n// preview panel built from real primitives. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input } from '@angular/core'\nimport {\n  ArrowRight,\n  Check,\n  GitBranch,\n  ShieldCheck,\n  Zap,\n  LucideAngularModule,\n  type LucideIconData,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\n\n// Explicit shape: the three preview panels are structurally different, so the\n// optional keys keep the union from collapsing when the template narrows.\nexport interface AlternatingRowPreviewLine {\n  text: string\n  tone: 'add' | 'remove' | 'same'\n}\n\nexport interface AlternatingRowPreviewRule {\n  scope: string\n  value: string\n  allowed: boolean\n}\n\nexport interface AlternatingRowPreviewStat {\n  name: string\n  detail: string\n}\n\nexport interface FeatureRow {\n  eyebrow: string\n  icon: LucideIconData\n  title: string\n  body: string\n  points: string[]\n  link: string\n  reverse: boolean\n  preview: {\n    label: string\n    lines?: AlternatingRowPreviewLine[]\n    rules?: AlternatingRowPreviewRule[]\n    stats?: AlternatingRowPreviewStat[]\n  }\n}\n\nconst rows: FeatureRow[] = [\n  {\n    eyebrow: 'Versioning',\n    icon: GitBranch,\n    title: 'Every change is a reviewable diff',\n    body: 'Dashboards, metrics, and permissions are stored as plain files. Changes open a pull request instead of silently overwriting production.',\n    points: ['Branch per change', 'Review before publish', 'One-click revert'],\n    link: 'How version control works',\n    reverse: false,\n    preview: {\n      label: 'metrics/revenue.yml',\n      lines: [\n        { text: '+ window: trailing_28d', tone: 'add' },\n        { text: '- window: trailing_30d', tone: 'remove' },\n        { text: '  owner: finance-analytics', tone: 'same' },\n      ],\n    },\n  },\n  {\n    eyebrow: 'Access',\n    icon: ShieldCheck,\n    title: 'Permissions that follow the row, not the dashboard',\n    body: 'Scope is evaluated at query time from your identity provider, so a shared link never leaks a region or account the viewer cannot see.',\n    points: ['Row-level filters', 'SCIM group sync', 'Audited every query'],\n    link: 'Read the access model',\n    reverse: true,\n    preview: {\n      label: 'Effective access — EMEA analyst',\n      rules: [\n        { scope: 'region', value: 'EMEA only', allowed: true },\n        { scope: 'revenue.net', value: 'Visible', allowed: true },\n        { scope: 'payroll.*', value: 'Blocked', allowed: false },\n      ],\n    },\n  },\n  {\n    eyebrow: 'Performance',\n    icon: Zap,\n    title: 'Cached where it matters, fresh where it counts',\n    body: 'Hot aggregates are materialised on a schedule you control. Everything else falls through to the warehouse so the number is never stale by accident.',\n    points: ['Per-metric freshness', 'Warehouse pass-through', 'Cost ceiling per team'],\n    link: 'See the caching rules',\n    reverse: false,\n    preview: {\n      label: 'Freshness',\n      stats: [\n        { name: 'Revenue rollup', detail: 'materialised · 5 min' },\n        { name: 'Pipeline by stage', detail: 'materialised · 1 hr' },\n        { name: 'Raw event search', detail: 'live query' },\n      ],\n    },\n  },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-feature-alternating-rows, [ui-feature-alternating-rows]',\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: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiSeparatorComponent,\n  ],\n  template: `\n    <section data-slot=\"feature-alternating-rows\" [class]=\"rootClass\">\n      <div class=\"mx-auto max-w-6xl px-6 py-20 lg:py-28\">\n        <div class=\"max-w-2xl\">\n          <span ui-badge variant=\"secondary\">Platform</span>\n          <h2 class=\"mt-4 text-3xl font-semibold tracking-tight sm:text-4xl\">\n            Built like infrastructure, not a widget\n          </h2>\n          <p class=\"text-muted-foreground mt-3 text-lg\">\n            Three decisions that stop a reporting layer from rotting after the first quarter.\n          </p>\n        </div>\n\n        <div class=\"mt-16 space-y-16 lg:space-y-24\">\n          @for (row of rows; track row.title) {\n            <div class=\"grid items-center gap-10 lg:grid-cols-2 lg:gap-16\">\n              <div [class]=\"row.reverse ? 'lg:order-2' : ''\">\n                <div class=\"text-muted-foreground flex items-center gap-2\">\n                  <lucide-icon [img]=\"row.icon\" class=\"size-4\" aria-hidden=\"true\" />\n                  <span class=\"font-mono text-xs tracking-[0.14em] uppercase\">{{ row.eyebrow }}</span>\n                </div>\n                <h3 class=\"mt-4 text-2xl font-semibold tracking-tight\">{{ row.title }}</h3>\n                <p class=\"text-muted-foreground mt-3 leading-relaxed\">{{ row.body }}</p>\n                <ul class=\"mt-6 space-y-2.5\">\n                  @for (point of row.points; track point) {\n                    <li class=\"flex items-start gap-2.5 text-sm\">\n                      <lucide-icon [img]=\"Check\" class=\"text-primary mt-0.5 size-4 shrink-0\" aria-hidden=\"true\" />\n                      <span>{{ point }}</span>\n                    </li>\n                  }\n                </ul>\n                <button ui-button variant=\"link\" class=\"mt-5 h-auto p-0\">\n                  {{ row.link }}\n                  <lucide-icon [img]=\"ArrowRight\" class=\"ml-1.5 size-3.5\" aria-hidden=\"true\" />\n                </button>\n              </div>\n\n              <div ui-card [class]=\"row.reverse ? 'lg:order-1' : ''\">\n                <div ui-card-content class=\"p-0\">\n                  <div class=\"border-border text-muted-foreground border-b px-4 py-2.5 font-mono text-xs\">\n                    {{ row.preview.label }}\n                  </div>\n\n                  <!-- Diff preview -->\n                  @if (row.preview.lines) {\n                    <div class=\"space-y-1 p-4 font-mono text-xs\">\n                      @for (line of row.preview.lines; track line.text) {\n                        <p [class]=\"lineClass(line.tone)\">{{ line.text }}</p>\n                      }\n                    </div>\n                  } @else if (row.preview.rules) {\n                    <!-- Access preview -->\n                    <div class=\"divide-border divide-y\">\n                      @for (rule of row.preview.rules; track rule.scope) {\n                        <div class=\"flex items-center justify-between gap-4 px-4 py-3\">\n                          <span class=\"font-mono text-xs\">{{ rule.scope }}</span>\n                          <span ui-badge [variant]=\"rule.allowed ? 'secondary' : 'outline'\">{{ rule.value }}</span>\n                        </div>\n                      }\n                    </div>\n                  } @else {\n                    <!-- Freshness preview -->\n                    <div class=\"p-4\">\n                      <ul class=\"space-y-3\">\n                        @for (stat of row.preview.stats; track stat.name) {\n                          <li>\n                            <div class=\"flex items-baseline justify-between gap-4\">\n                              <span class=\"text-sm font-medium\">{{ stat.name }}</span>\n                              <span class=\"text-muted-foreground font-mono text-xs\">{{ stat.detail }}</span>\n                            </div>\n                            <ui-separator class=\"mt-3\" />\n                          </li>\n                        }\n                      </ul>\n                    </div>\n                  }\n                </div>\n              </div>\n            </div>\n          }\n        </div>\n      </div>\n    </section>\n  `,\n})\nexport class UiFeatureAlternatingRowsComponent {\n  protected readonly ArrowRight = ArrowRight\n  protected readonly Check = Check\n\n  protected readonly rows = rows\n\n  @Input('class') className?: string\n\n  lineClass(tone: 'add' | 'remove' | 'same'): string {\n    return tone === 'add' ? 'text-success' : tone === 'remove' ? 'text-destructive' : 'text-muted-foreground'\n  }\n\n  get rootClass(): string {\n    return cn('bg-background', this.className)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/feature-alternating-rows/feature-alternating-rows.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/feature-alternating-rows/index.ts",
      "content": "export {\n  UiFeatureAlternatingRowsComponent,\n  type FeatureRow,\n  type AlternatingRowPreviewLine,\n  type AlternatingRowPreviewRule,\n  type AlternatingRowPreviewStat,\n} from './feature-alternating-rows.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/feature-alternating-rows/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/separator.json"
  ],
  "description": "Classic zig-zag feature deep-dive: three full-width rows alternating copy and visual, each with an eyebrow, headline, checklist, inline link, and a bordered preview panel built from real primitives.",
  "categories": [
    "marketing"
  ]
}