{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "feature-section-v8",
  "title": "Feature Section v8",
  "description": "Centered automation headline with an Incharge-style bordered feature gridâ€”four cards with dot-pattern slots and copy-driven product mocks in square frames.",
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/new-york/blocks/feature-section-v8/page.tsx",
      "content": "import FeatureSectionV8 from \"./components/feature-section-v8\"\n\nexport default function Page() {\n  return <FeatureSectionV8 />\n}\n",
      "type": "registry:page",
      "target": "app/feature-section-v8/page.tsx"
    },
    {
      "path": "registry/new-york/blocks/feature-section-v8/components/feature-section-v8.tsx",
      "content": "﻿import FeatureGrid from \"./feature-grid\"\nimport { featureSectionContent } from \"../lib/config\"\n\nimport \"../styles/feature-section-v8.css\"\n\nexport default function FeatureSectionV8() {\n  return (\n    <section className=\"py-4 text-foreground md:py-6\">\n      <div className=\"mx-auto max-w-6xl px-4\">\n        <div className=\"mx-auto mb-8 max-w-3xl text-center md:mb-10 lg:mb-12\">\n          <h2 className=\"text-4xl leading-[1.1] font-medium tracking-[-0.03em] lg:text-5xl\">\n            {featureSectionContent.title}\n          </h2>\n        </div>\n        <FeatureGrid />\n      </div>\n    </section>\n  )\n}\n",
      "type": "registry:component",
      "target": "app/feature-section-v8/components/feature-section-v8.tsx"
    },
    {
      "path": "registry/new-york/blocks/feature-section-v8/components/feature-grid.tsx",
      "content": "\"use client\"\n\nimport { featureColumns } from \"../lib/config\"\nimport FeatureCard from \"./feature-card\"\nimport { useInViewOnce } from \"../hooks/use-in-view-once\"\n\nexport default function FeatureGrid() {\n  const { ref, active } = useInViewOnce(0.2)\n\n  return (\n    <div ref={ref} className=\"mx-auto max-w-5xl border border-border\">\n      <ul className=\"m-0 grid list-none grid-cols-1 p-0 md:grid-cols-2 lg:grid-cols-[2fr_3fr]\">\n        {featureColumns.slice(0, 2).map((column, index) => (\n          <FeatureCard key={column.id} column={column} index={index} live={active} />\n        ))}\n      </ul>\n      <ul className=\"m-0 grid list-none grid-cols-1 border-t border-border p-0 md:grid-cols-2 lg:grid-cols-[2fr_3fr]\">\n        {featureColumns.slice(2).map((column, index) => (\n          <FeatureCard\n            key={column.id}\n            column={column}\n            index={index}\n            live={active}\n            className=\"-mt-px\"\n          />\n        ))}\n      </ul>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "app/feature-section-v8/components/feature-grid.tsx"
    },
    {
      "path": "registry/new-york/blocks/feature-section-v8/components/feature-card.tsx",
      "content": "import { cn } from \"@/lib/utils\"\n\nimport type { FeatureColumn } from \"../lib/config\"\nimport { featurePreviewMinHeights } from \"../lib/config\"\nimport FeaturePreviewSlot from \"./feature-preview-slot\"\n\nexport default function FeatureCard({\n  column,\n  index,\n  className,\n  live = false,\n  subgrid = false,\n}: {\n  column: FeatureColumn\n  index: number\n  className?: string\n  live?: boolean\n  subgrid?: boolean\n}) {\n  const reversed = column.layout === \"reversed\"\n  const previewSize = column.previewSize ?? \"default\"\n  const previewMinHeight = featurePreviewMinHeights[previewSize]\n  const swapAt = subgrid ? \"md\" : \"lg\"\n\n  return (\n    <li\n      className={cn(\n        \"grid min-w-0 grid-rows-[auto_auto] border border-border\",\n        subgrid && \"md:row-span-2 md:grid-rows-subgrid\",\n        index > 0 && \"md:-ml-px\",\n        className\n      )}\n    >\n      <div\n        className={cn(\n          \"relative flex h-full min-h-0 flex-col border-border\",\n          previewMinHeight,\n          reversed &&\n            (swapAt === \"md\"\n              ? \"max-md:border-b md:row-start-2 md:border-t md:border-b-0\"\n              : \"max-lg:border-b lg:row-start-2 lg:border-t lg:border-b-0\"),\n          !reversed && \"border-b\"\n        )}\n      >\n        <FeaturePreviewSlot\n          id={column.id}\n          title={column.title}\n          description={column.description}\n          live={live}\n          previewSize={previewSize}\n        />\n      </div>\n      <div\n        className={cn(\n          \"p-4 sm:p-5\",\n          reversed &&\n            (swapAt === \"md\" ? \"md:row-start-1\" : \"lg:row-start-1\")\n        )}\n      >\n        <h3 className=\"m-0 text-base font-medium tracking-[-0.02em] text-foreground sm:text-lg\">\n          {column.title}\n        </h3>\n        <p className=\"m-0 mt-2 text-[0.875rem] leading-[1.6] text-muted-foreground\">\n          {column.description}\n        </p>\n      </div>\n    </li>\n  )\n}\n",
      "type": "registry:component",
      "target": "app/feature-section-v8/components/feature-card.tsx"
    },
    {
      "path": "registry/new-york/blocks/feature-section-v8/components/feature-preview-slot.tsx",
      "content": "\"use client\"\n\nimport { cn } from \"@/lib/utils\"\n\nimport { buildFeaturePreview } from \"../lib/build-feature-preview\"\nimport ProductPreviewFeature from \"./product-preview-feature\"\nimport { featurePreviewMinHeights, type FeaturePreviewSize } from \"../lib/config\"\n\nimport \"../styles/feature-section-v8.css\"\n\nexport default function FeaturePreviewSlot({\n  id,\n  title,\n  description,\n  live = false,\n  previewSize = \"default\",\n  className,\n}: {\n  id: string\n  title: string\n  description: string\n  live?: boolean\n  previewSize?: FeaturePreviewSize\n  className?: string\n}) {\n  const spec = buildFeaturePreview({ id, title, description, size: \"mini\" })\n\n  return (\n    <div\n      className={cn(\n        \"fsv8-dot-pattern relative h-full min-h-0 overflow-hidden\",\n        featurePreviewMinHeights[previewSize],\n        className\n      )}\n    >\n      <div className=\"absolute inset-0 flex min-w-0 items-stretch p-4 sm:p-5\">\n        <ProductPreviewFeature\n          className=\"min-h-0 min-w-0 flex-1\"\n          spec={spec}\n          title={title}\n          description={description}\n          instanceId={id}\n          live={live}\n        />\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "app/feature-section-v8/components/feature-preview-slot.tsx"
    },
    {
      "path": "registry/new-york/blocks/feature-section-v8/components/product-preview-feature.tsx",
      "content": "\"use client\"\n\nimport type { CSSProperties, ReactNode } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nimport { elementDelay, elementDuration } from \"../lib/animation-timing\"\nimport MetricCard from \"./metric-card\"\nimport { useLiveFeaturePreview } from \"../hooks/use-live-feature-preview\"\n\nimport type { FeaturePreviewSpec, MiniAppScreenZone } from \"../lib/build-feature-preview\"\n\nfunction MockScreen({ children }: { children: ReactNode }) {\n  return (\n    <div className=\"fsv8-mock-screen fsv8-mock-body flex min-h-0 flex-1 flex-col\">\n      {children}\n    </div>\n  )\n}\n\nfunction chartValues(chart: { heights?: ReadonlyArray<number>; points?: ReadonlyArray<number> }) {\n  return chart.heights ?? chart.points ?? []\n}\n\nfunction MiniAreaChart({ points }: { points: ReadonlyArray<number> }) {\n  if (points.length < 2) return null\n\n  const width = 100\n  const height = 100\n  const step = width / (points.length - 1)\n  const linePath = points\n    .map((point, index) => {\n      const x = index * step\n      const y = height - (point / 100) * height\n      return `${index === 0 ? \"M\" : \"L\"} ${x.toFixed(2)} ${y.toFixed(2)}`\n    })\n    .join(\" \")\n  const areaPath = `${linePath} L ${width} ${height} L 0 ${height} Z`\n\n  return (\n    <svg\n      className=\"fsv8-area-chart block h-full w-full\"\n      viewBox={`0 0 ${width} ${height}`}\n      preserveAspectRatio=\"none\"\n      aria-hidden\n    >\n      <path d={areaPath} fill=\"var(--pp-accent-muted)\" />\n      <path\n        d={linePath}\n        fill=\"none\"\n        stroke=\"var(--pp-accent)\"\n        strokeWidth=\"1.5\"\n        vectorEffect=\"non-scaling-stroke\"\n      />\n    </svg>\n  )\n}\n\nfunction MiniLineChart({ points }: { points: ReadonlyArray<number> }) {\n  if (points.length < 2) return null\n\n  const width = 100\n  const height = 100\n  const step = width / (points.length - 1)\n  const path = points\n    .map((point, index) => {\n      const x = index * step\n      const y = height - (point / 100) * height\n      return `${index === 0 ? \"M\" : \"L\"} ${x.toFixed(2)} ${y.toFixed(2)}`\n    })\n    .join(\" \")\n\n  return (\n    <svg\n      className=\"fsv8-line-chart block h-full w-full\"\n      viewBox={`0 0 ${width} ${height}`}\n      preserveAspectRatio=\"none\"\n      aria-hidden\n    >\n      <path\n        d={path}\n        fill=\"none\"\n        stroke=\"var(--pp-accent)\"\n        strokeWidth=\"1.75\"\n        vectorEffect=\"non-scaling-stroke\"\n      />\n    </svg>\n  )\n}\n\nfunction MiniBarChart({\n  heights,\n  thin = false,\n  live = false,\n  scan,\n  seed = \"bars\",\n}: {\n  heights: ReadonlyArray<number>\n  thin?: boolean\n  live?: boolean\n  scan?: boolean\n  seed?: string\n}) {\n  const maxIndex = heights.reduce(\n    (best, height, index) => (height > heights[best] ? index : best),\n    0\n  )\n  const showScan = scan ?? live\n\n  return (\n    <div\n      className={cn(\n        thin\n          ? \"fsv8-bar-chart fsv8-bar-chart--thin flex min-h-0 flex-1 items-end justify-between px-2.5 py-2\"\n          : \"fsv8-bar-chart flex min-h-0 flex-1 items-end gap-1 px-2.5 py-2\",\n        showScan && \"fsv8-bar-chart-live\"\n      )}\n      style={\n        showScan\n          ? ({\n              \"--fsv8-scan-delay\": `${elementDelay(`${seed}-scan`, 0, 4800)}ms`,\n            } as CSSProperties)\n          : undefined\n      }\n      aria-hidden\n    >\n      {heights.map((height, index) => (\n        <div\n          key={index}\n          className={cn(\n            thin\n              ? \"fsv8-bar fsv8-bar--thin shrink-0\"\n              : \"fsv8-bar w-1.5 shrink-0\",\n            live && \"fsv8-bar-live\",\n            index === maxIndex ? \"bg-(--pp-accent)\" : \"bg-(--pp-accent-muted)\"\n          )}\n          style={\n            live\n              ? ({\n                  height: `${height}%`,\n                  \"--fsv8-bar-delay\": `${elementDelay(`${seed}-bar-${index}`, 0, 720)}ms`,\n                  \"--fsv8-bar-duration\": `${elementDuration(`${seed}-bar-d-${index}`, 380, 1100)}ms`,\n                  \"--fsv8-bar-rise-delay\": `${elementDelay(`${seed}-bar-rise-${index}`, 0, 640)}ms`,\n                  \"--fsv8-bar-rise-duration\": `${elementDuration(`${seed}-bar-rise-d-${index}`, 420, 980)}ms`,\n                } as CSSProperties)\n              : { height: `${height}%` }\n          }\n        />\n      ))}\n    </div>\n  )\n}\n\nfunction DotGridPanel({\n  dotGrid,\n  compact = false,\n  live = false,\n  seed = \"dots\",\n}: {\n  dotGrid: ReadonlyArray<ReadonlyArray<boolean>>\n  compact?: boolean\n  live?: boolean\n  seed?: string\n}) {\n  const rows = dotGrid.length\n  const cols = dotGrid[0]?.length ?? 1\n\n  return (\n    <div\n      className={cn(\n        compact ? \"fsv8-dot-grid fsv8-dot-grid--compact\" : \"fsv8-dot-grid\",\n        live && \"fsv8-dot-grid-live\"\n      )}\n      style={{\n        gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,\n        gridTemplateRows: `repeat(${rows}, minmax(0, 1fr))`,\n      }}\n      aria-hidden\n    >\n      {dotGrid.flatMap((row, rowIndex) =>\n        row.map((active, colIndex) => (\n          <div key={`${rowIndex}-${colIndex}`} className=\"fsv8-dot-grid-cell\">\n            <span\n              className={cn(\n                \"fsv8-dot-grid-dot\",\n                active && \"fsv8-dot-grid-dot--active\",\n                live && active && \"fsv8-dot-grid-dot--live\"\n              )}\n              style={\n                live && active\n                  ? ({\n                      \"--fsv8-dot-delay\": `${elementDelay(`${seed}-dot-${rowIndex}-${colIndex}`, 0, 2200)}ms`,\n                      \"--fsv8-dot-duration\": `${elementDuration(`${seed}-dot-d-${rowIndex}-${colIndex}`, 1200, 2800)}ms`,\n                    } as CSSProperties)\n                  : undefined\n              }\n            />\n          </div>\n        ))\n      )}\n    </div>\n  )\n}\n\nfunction LiveStreamZone({\n  zone,\n  live,\n  instanceId,\n}: {\n  zone: Extract<MiniAppScreenZone, { type: \"live-stream\" }>\n  live: boolean\n  pulseKey: number\n  instanceId: string\n}) {\n  return (\n    <MockScreen>\n      <div className=\"fsv8-stat-strip flex shrink-0 border-b border-(--pp-border)\">\n        {zone.stats.map((stat) => (\n          <div\n            key={stat.label}\n            className=\"fsv8-stat-strip-item flex flex-1 flex-col gap-0.5 px-2.5 py-2\"\n          >\n            <span className=\"text-base font-medium tracking-tight text-foreground tabular-nums\">\n              {stat.value}\n            </span>\n            <span className=\"font-mono text-[0.625rem] uppercase tracking-wider text-muted-foreground\">\n              {stat.label}\n            </span>\n          </div>\n        ))}\n      </div>\n      <MiniBarChart heights={zone.bars} thin live={live} scan={false} seed={`${instanceId}-stream`} />\n    </MockScreen>\n  )\n}\n\nfunction SignalFlowZone({\n  zone,\n  live,\n  activeNodeIndex,\n  instanceId,\n}: {\n  zone: Extract<MiniAppScreenZone, { type: \"signal-flow\" }>\n  live: boolean\n  pulseKey: number\n  activeNodeIndex: number\n  instanceId: string\n}) {\n  return (\n    <MockScreen>\n      <div\n        className={cn(\n          \"fsv8-signal-route shrink-0 border-b border-(--pp-border) px-3 py-3\",\n          live && \"fsv8-signal-route-live\"\n        )}\n      >\n        <div className=\"flex items-end\">\n          {zone.nodes.map((node, index) => {\n            const isActive = live && index === activeNodeIndex\n            const isPast = live && index < activeNodeIndex\n\n            return (\n              <div key={node.label} className=\"flex min-w-0 flex-1 items-end\">\n                <div className=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n                  <div className=\"flex min-w-0 items-baseline justify-between gap-2\">\n                    <span\n                      className={cn(\n                        \"truncate font-mono text-[0.625rem] leading-none\",\n                        isActive ? \"text-foreground\" : \"text-muted-foreground\"\n                      )}\n                    >\n                      {node.label}\n                    </span>\n                    <span\n                      className={cn(\n                        \"shrink-0 font-mono text-[0.5625rem] tabular-nums leading-none\",\n                        isActive ? \"text-foreground/70\" : \"text-muted-foreground/55\"\n                      )}\n                    >\n                      {node.volume}\n                    </span>\n                  </div>\n                  <div\n                    className={cn(\n                      \"fsv8-signal-hop h-1 w-full\",\n                      isActive && \"fsv8-signal-hop--active\",\n                      isPast && \"fsv8-signal-hop--past\"\n                    )}\n                  />\n                </div>\n                {index < zone.nodes.length - 1 ? (\n                  <span\n                    className={cn(\n                      \"fsv8-signal-gap mb-0.5 h-px w-2 shrink-0\",\n                      isPast || isActive\n                        ? \"bg-(--pp-accent-muted)\"\n                        : \"bg-(--pp-border)\"\n                    )}\n                    aria-hidden\n                  />\n                ) : null}\n              </div>\n            )\n          })}\n        </div>\n      </div>\n      <DotGridPanel dotGrid={zone.dotGrid} compact live={live} seed={`${instanceId}-signal`} />\n    </MockScreen>\n  )\n}\n\nfunction HealthSplitZone({\n  zone,\n  live,\n}: {\n  zone: Extract<MiniAppScreenZone, { type: \"health-split\" }>\n  live: boolean\n  pulseKey: number\n}) {\n  const chokeIndex = zone.areaPoints.reduce(\n    (lowest, point, index) =>\n      point < zone.areaPoints[lowest]! ? index : lowest,\n    0\n  )\n  const chokeLeft =\n    zone.areaPoints.length > 1\n      ? `${(chokeIndex / (zone.areaPoints.length - 1)) * 100}%`\n      : \"50%\"\n\n  return (\n    <MockScreen>\n      <div className=\"flex min-h-0 flex-1\">\n        <div className=\"fsv8-health-sidebar flex w-[36%] shrink-0 flex-col border-r border-(--pp-border)\">\n          {zone.metrics.map((metric) => (\n            <MetricCard\n              key={metric.label}\n              value={metric.value}\n              label={metric.label}\n              valueClassName={live ? \"fsv8-stat-live\" : undefined}\n            />\n          ))}\n        </div>\n        <div className=\"flex min-h-0 flex-1 flex-col px-2.5 py-2\">\n          <div className=\"mb-2 flex shrink-0 items-center justify-between gap-2\">\n            <span className=\"font-mono text-[0.5625rem] uppercase tracking-wider text-muted-foreground\">\n              {zone.chartLabel}\n            </span>\n            <span className=\"font-mono text-[0.5625rem] text-muted-foreground/80\">\n              Choke\n            </span>\n          </div>\n          <div className=\"fsv8-area-chart-wrap relative min-h-0 flex-1\">\n            <MiniAreaChart points={zone.areaPoints} />\n            <span\n              className=\"fsv8-choke-mark pointer-events-none absolute bottom-1 top-1 w-px bg-(--pp-accent)/35\"\n              style={{ left: chokeLeft }}\n              aria-hidden\n            />\n          </div>\n        </div>\n      </div>\n    </MockScreen>\n  )\n}\n\nfunction titleCase(value: string) {\n  return value\n    .split(/\\s+/)\n    .filter(Boolean)\n    .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n    .join(\" \")\n}\n\nfunction DataTableZone({\n  zone,\n  live,\n  activeRowIndex,\n}: {\n  zone: Extract<MiniAppScreenZone, { type: \"data-table\" }>\n  live: boolean\n  pulseKey: number\n  activeRowIndex: number\n}) {\n  return (\n    <MockScreen>\n      <div className=\"fsv8-table flex min-h-0 w-full flex-1 flex-col font-mono text-[0.625rem]\">\n        <div className=\"fsv8-table-head grid shrink-0 grid-cols-[minmax(0,1.4fr)_minmax(0,0.8fr)_minmax(0,0.9fr)] items-center gap-2 border-b border-(--pp-border) px-3 py-2 text-muted-foreground\">\n          {zone.columns.map((column) => (\n            <span key={column.header} className=\"truncate\">\n              {titleCase(column.header)}\n            </span>\n          ))}\n        </div>\n\n        <div className=\"fsv8-table-rows flex min-h-0 flex-1 flex-col\">\n          {zone.rows.map((row, rowIndex) => {\n            const isActive = live && rowIndex === activeRowIndex\n\n            return (\n              <div\n                key={rowIndex}\n                className={cn(\n                  \"fsv8-table-row grid min-h-0 flex-1 grid-cols-[minmax(0,1.4fr)_minmax(0,0.8fr)_minmax(0,0.9fr)] items-center gap-2 border-b border-(--pp-border) px-3 text-foreground/80 last:border-b-0\",\n                  isActive && \"fsv8-table-row-live\"\n                )}\n              >\n                <span className=\"truncate text-foreground/90\">\n                  {titleCase(row.cells[0] ?? \"\")}\n                </span>\n                <span className=\"truncate text-muted-foreground\">\n                  {titleCase(row.cells[1] ?? \"\")}\n                </span>\n                <span\n                  className={cn(\n                    \"truncate tabular-nums\",\n                    isActive ? \"text-foreground\" : \"text-foreground/70\"\n                  )}\n                >\n                  {row.cells[2]}\n                </span>\n              </div>\n            )\n          })}\n        </div>\n\n        <div\n          className=\"fsv8-table-foot shrink-0 border-t border-(--pp-border) px-3 py-2\"\n          aria-hidden\n        >\n          {\"\\u00A0\"}\n        </div>\n      </div>\n    </MockScreen>\n  )\n}\n\ntype LivePreviewProps = {\n  live: boolean\n  pulseKey: number\n  activeNodeIndex: number\n  activeRowIndex: number\n  instanceId: string\n}\n\nfunction DashboardZone({\n  zone,\n  live,\n  instanceId,\n}: LivePreviewProps & {\n  zone: Extract<MiniAppScreenZone, { type: \"dashboard\" }>\n}) {\n  return (\n    <MockScreen>\n      <div className=\"grid shrink-0 grid-cols-2 border-b border-(--pp-border)\">\n        {zone.metrics.map((metric) => (\n          <MetricCard\n            key={metric.label}\n            value={metric.value}\n            label={metric.label}\n            valueClassName={live ? \"fsv8-stat-live\" : undefined}\n          />\n        ))}\n      </div>\n      {zone.bars ? (\n        <MiniBarChart heights={zone.bars} live={live} seed={`${instanceId}-dash`} />\n      ) : null}\n      {zone.dotGrid ? (\n        <DotGridPanel dotGrid={zone.dotGrid} live={live} seed={`${instanceId}-dash-dots`} />\n      ) : null}\n    </MockScreen>\n  )\n}\n\nfunction ChartDashboardZone({\n  zone,\n  live,\n  instanceId,\n}: LivePreviewProps & {\n  zone: Extract<MiniAppScreenZone, { type: \"chart-dashboard\" }>\n}) {\n  const { chart } = zone\n  const values = chartValues(chart)\n\n  return (\n    <MockScreen>\n      <div className=\"grid shrink-0 grid-cols-2 border-b border-(--pp-border)\">\n        {zone.metrics.map((metric) => (\n          <MetricCard\n            key={metric.label}\n            value={metric.value}\n            label={metric.label}\n            valueClassName={live ? \"fsv8-stat-live\" : undefined}\n          />\n        ))}\n      </div>\n      {chart.type === \"dots\" && zone.dotGrid ? (\n        <DotGridPanel dotGrid={zone.dotGrid} live={live} seed={`${instanceId}-chart-dots`} />\n      ) : chart.type === \"line\" || chart.type === \"sparkline\" ? (\n        <div\n          className={cn(\n            \"fsv8-line-chart-wrap flex min-h-0 flex-1 px-3 py-3\",\n            live && \"fsv8-area-chart-wrap-live\"\n          )}\n          style={\n            live\n              ? ({\n                  \"--fsv8-scan-delay\": `${elementDelay(`${instanceId}-line-scan`, 0, 4800)}ms`,\n                } as CSSProperties)\n              : undefined\n          }\n        >\n          <MiniLineChart points={values} />\n        </div>\n      ) : chart.type === \"area\" ? (\n        <div\n          className={cn(\n            \"fsv8-area-chart-wrap flex min-h-0 flex-1 px-3 py-3\",\n            live && \"fsv8-area-chart-wrap-live\"\n          )}\n          style={\n            live\n              ? ({\n                  \"--fsv8-scan-delay\": `${elementDelay(`${instanceId}-area-scan`, 0, 4800)}ms`,\n                } as CSSProperties)\n              : undefined\n          }\n        >\n          <MiniAreaChart points={values} />\n        </div>\n      ) : (\n        <MiniBarChart\n          heights={values}\n          thin={chart.barWidth === \"thin\"}\n          live={live}\n          seed={`${instanceId}-chart-bars`}\n        />\n      )}\n    </MockScreen>\n  )\n}\n\nfunction MiniAppScreen({\n  zone,\n  live,\n  pulseKey,\n  activeNodeIndex,\n  activeRowIndex,\n  instanceId,\n}: { zone: MiniAppScreenZone } & LivePreviewProps) {\n  const props = { live, pulseKey, activeNodeIndex, activeRowIndex, instanceId }\n\n  if (zone.type === \"live-stream\") return <LiveStreamZone zone={zone} {...props} />\n  if (zone.type === \"signal-flow\") return <SignalFlowZone zone={zone} {...props} />\n  if (zone.type === \"health-split\") return <HealthSplitZone zone={zone} {...props} />\n  if (zone.type === \"data-table\") return <DataTableZone zone={zone} {...props} />\n  if (zone.type === \"dashboard\") return <DashboardZone zone={zone} {...props} />\n  return <ChartDashboardZone zone={zone} {...props} />\n}\n\nexport default function ProductPreviewFeature({\n  spec,\n  title,\n  description,\n  instanceId,\n  live = false,\n  className,\n}: {\n  spec: FeaturePreviewSpec\n  title: string\n  description: string\n  instanceId: string\n  live?: boolean\n  className?: string\n}) {\n  const initialZone = spec.zones[0]\n  const preview = useLiveFeaturePreview(initialZone, live, instanceId)\n\n  return (\n    <div\n      className={cn(\n        \"fsv8-mock-frame relative flex h-full min-h-0 w-full min-w-0 flex-col overflow-hidden bg-(--pp-shell-bg)\",\n        className\n      )}\n      role=\"img\"\n      aria-label={`${title} — ${description}${live ? \" — simulated live data\" : \"\"}`}\n    >\n      {preview.zone ? (\n        <MiniAppScreen\n          zone={preview.zone}\n          live={live}\n          pulseKey={preview.pulseKey}\n          activeNodeIndex={preview.activeNodeIndex}\n          activeRowIndex={preview.activeRowIndex}\n          instanceId={instanceId}\n        />\n      ) : null}\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "app/feature-section-v8/components/product-preview-feature.tsx"
    },
    {
      "path": "registry/new-york/blocks/feature-section-v8/lib/build-feature-preview.ts",
      "content": "export type PreviewIntent =\n  | \"task\"\n  | \"monitor\"\n  | \"realtime\"\n  | \"throughput\"\n  | \"sync\"\n\nexport type MiniTableColumn = { header: string }\nexport type MiniTableRow = { cells: ReadonlyArray<string> }\n\nexport type MiniChartZone = {\n  type: \"line\" | \"area\" | \"bars\" | \"dots\" | \"sparkline\"\n  heights?: ReadonlyArray<number>\n  points?: ReadonlyArray<number>\n  barWidth?: \"thin\" | \"normal\"\n}\n\nexport type MiniAppScreenZone =\n  | {\n      type: \"live-stream\"\n      panelTitle: string\n      stats: ReadonlyArray<{ value: string; label: string }>\n      bars: ReadonlyArray<number>\n    }\n  | {\n      type: \"signal-flow\"\n      panelTitle: string\n      nodes: ReadonlyArray<{ label: string; volume: number }>\n      dotGrid: ReadonlyArray<ReadonlyArray<boolean>>\n    }\n  | {\n      type: \"health-split\"\n      panelTitle: string\n      metrics: ReadonlyArray<{ value: string; label: string }>\n      chartLabel: string\n      areaPoints: ReadonlyArray<number>\n    }\n  | {\n      type: \"data-table\"\n      variant: \"table-compact\" | \"table-mono\"\n      panelTitle: string\n      queueSummary: string\n      columns: ReadonlyArray<MiniTableColumn>\n      rows: ReadonlyArray<MiniTableRow>\n    }\n  | {\n      type: \"dashboard\"\n      variant: \"dashboard-kpi\" | \"dashboard-split\"\n      panelTitle: string\n      metrics: ReadonlyArray<{ value: string; label: string }>\n      bars?: ReadonlyArray<number>\n      dotGrid?: ReadonlyArray<ReadonlyArray<boolean>>\n    }\n  | {\n      type: \"chart-dashboard\"\n      variant: \"chart-line\" | \"chart-area\" | \"chart-dots\"\n      panelTitle: string\n      metrics: ReadonlyArray<{ value: string; label: string }>\n      chart: MiniChartZone\n      dotGrid?: ReadonlyArray<ReadonlyArray<boolean>>\n    }\n\nexport type FeaturePreviewSpec = {\n  intent: PreviewIntent\n  zones: ReadonlyArray<MiniAppScreenZone>\n}\n\nexport type BuildFeaturePreviewInput = {\n  id: string\n  title: string\n  description: string\n  size: \"large\" | \"mini\"\n}\n\nconst INTENT_KEYWORDS: Record<PreviewIntent, ReadonlyArray<string>> = {\n  task: [\n    \"task\",\n    \"queue\",\n    \"backlog\",\n    \"scheduled\",\n    \"track\",\n    \"timestamp\",\n    \"automation\",\n    \"row\",\n    \"owner\",\n  ],\n  monitor: [\n    \"monitor\",\n    \"health\",\n    \"uptime\",\n    \"pipeline\",\n    \"depth\",\n    \"bottleneck\",\n    \"glance\",\n    \"overview\",\n    \"review\",\n  ],\n  realtime: [\n    \"real-time\",\n    \"realtime\",\n    \"live\",\n    \"instant\",\n    \"latency\",\n    \"execution\",\n    \"performance\",\n    \"streaming\",\n    \"respond\",\n  ],\n  throughput: [\n    \"volume\",\n    \"throughput\",\n    \"runs\",\n    \"activity\",\n    \"signals\",\n    \"ingest\",\n    \"coordinate\",\n    \"route\",\n  ],\n  sync: [\"sync\", \"import\", \"export\", \"transform\", \"deliver\"],\n}\n\nconst INTENT_ROTATION: ReadonlyArray<PreviewIntent> = [\n  \"monitor\",\n  \"throughput\",\n  \"task\",\n  \"realtime\",\n  \"sync\",\n]\n\nexport function hashString(input: string): number {\n  let hash = 0\n  for (let index = 0; index < input.length; index += 1) {\n    hash = (hash * 31 + input.charCodeAt(index)) >>> 0\n  }\n  return hash\n}\n\nfunction seededValue(seed: string, min: number, max: number): number {\n  const span = max - min + 1\n  return min + (hashString(seed) % span)\n}\n\nfunction seededFloats(seed: string, count: number): ReadonlyArray<number> {\n  const values: number[] = []\n  for (let index = 0; index < count; index += 1) {\n    values.push(28 + (hashString(`${seed}-${index}`) % 72))\n  }\n  return values\n}\n\nfunction classifyIntent(id: string, title: string, description: string): PreviewIntent {\n  const haystack = `${title} ${description}`.toLowerCase()\n  let bestIntent: PreviewIntent = INTENT_ROTATION[hashString(id) % INTENT_ROTATION.length]\n  let bestScore = 0\n\n  for (const intent of INTENT_ROTATION) {\n    const score = INTENT_KEYWORDS[intent].reduce(\n      (total, keyword) => total + (haystack.includes(keyword) ? 1 : 0),\n      0\n    )\n    if (score > bestScore) {\n      bestScore = score\n      bestIntent = intent\n    }\n  }\n\n  return bestIntent\n}\n\nfunction splitClauses(description: string): string[] {\n  return description\n    .split(/[,;.]+/)\n    .map((clause) => clause.trim())\n    .filter(Boolean)\n}\n\nfunction titleWords(title: string): string[] {\n  return title\n    .toLowerCase()\n    .split(/\\s+/)\n    .filter((word) => word.length > 2)\n}\n\nfunction panelTitleFromCopy(title: string, description: string): string {\n  const clauses = splitClauses(description)\n  if (clauses[0] && clauses[0].length <= 36) {\n    return clauses[0].charAt(0).toUpperCase() + clauses[0].slice(1)\n  }\n  const words = title.split(/\\s+/).slice(0, 3)\n  return words.join(\" \")\n}\n\nfunction metricLabelFromClause(clause: string, fallback: string): string {\n  const trimmed = clause.trim()\n  if (trimmed.length <= 22) return trimmed\n  const words = trimmed.split(/\\s+/).slice(-3)\n  return words.join(\" \")\n}\n\nfunction buildDotGrid(\n  id: string,\n  rows = 10,\n  cols = 16\n): ReadonlyArray<ReadonlyArray<boolean>> {\n  return Array.from({ length: rows }, (_, row) =>\n    Array.from(\n      { length: cols },\n      (_, col) => hashString(`${id}-dot-${row}-${col}`) % 3 !== 0\n    )\n  )\n}\n\nfunction buildTableZone(\n  id: string,\n  title: string,\n  description: string,\n  variant: \"table-compact\" | \"table-mono\"\n): MiniAppScreenZone {\n  const clauses = splitClauses(description)\n  const panelTitle = panelTitleFromCopy(title, description)\n\n  const columns: MiniTableColumn[] =\n    variant === \"table-mono\"\n      ? [{ header: \"Handled\" }, { header: \"Assignee\" }, { header: \"Updated\" }]\n      : [{ header: \"Item\" }, { header: \"Stage\" }, { header: \"Window\" }]\n\n  const rowSources = [\n    clauses[0] ?? \"Primary flow\",\n    clauses[1] ?? \"Secondary path\",\n    clauses[2] ?? \"Fallback lane\",\n    \"Shared handoff\",\n  ]\n\n  const rows: MiniTableRow[] = rowSources.slice(0, 4).map((clause, index) => {\n    const words = clause.split(/\\s+/).filter(Boolean)\n    const cellA = words.slice(0, 2).join(\" \") || `Lane ${index + 1}`\n    const cellB = words.slice(2, 4).join(\" \") || `Phase ${index + 1}`\n    const cellC =\n      variant === \"table-mono\"\n        ? `${seededValue(`${id}-t-${index}`, 8, 23)}:${seededValue(`${id}-m-${index}`, 10, 59).toString().padStart(2, \"0\")}`\n        : `${seededValue(`${id}-w-${index}`, 2, 9)}h window`\n\n    return { cells: [cellA, cellB, cellC] }\n  })\n\n  return {\n    type: \"data-table\",\n    variant,\n    panelTitle,\n    queueSummary: `${rowSources.length} queued · next ${seededValue(`${id}-next-h`, 8, 23)}:${seededValue(`${id}-next-m`, 10, 59).toString().padStart(2, \"0\")}`,\n    columns,\n    rows,\n  }\n}\n\nfunction buildDashboardZone(\n  id: string,\n  title: string,\n  description: string,\n  variant: \"dashboard-kpi\" | \"dashboard-split\"\n): MiniAppScreenZone {\n  const clauses = splitClauses(description)\n  const panelTitle = panelTitleFromCopy(title, description)\n  const words = titleWords(title)\n\n  const metrics = [\n    {\n      value: `${seededValue(`${id}-m0`, 89, 99)}.${seededValue(`${id}-m0d`, 1, 9)}%`,\n      label: metricLabelFromClause(clauses[0] ?? \"Uptime trend\", \"Uptime trend\"),\n    },\n    {\n      value: `${seededValue(`${id}-m1`, 12, 148)}`,\n      label: metricLabelFromClause(clauses[1] ?? words[0] ?? \"Depth\", \"Queue depth\"),\n    },\n  ]\n\n  const bars = seededFloats(`${id}-bars`, 8)\n  const dotGrid = buildDotGrid(id)\n\n  return {\n    type: \"dashboard\",\n    variant,\n    panelTitle,\n    metrics,\n    bars: variant === \"dashboard-kpi\" ? bars : undefined,\n    dotGrid: variant === \"dashboard-split\" ? dotGrid : undefined,\n  }\n}\n\nfunction buildChartZone(\n  id: string,\n  title: string,\n  description: string,\n  variant: \"chart-line\" | \"chart-area\" | \"chart-dots\"\n): MiniAppScreenZone {\n  const clauses = splitClauses(description)\n  const panelTitle = panelTitleFromCopy(title, description)\n\n  const chartTypes: ReadonlyArray<MiniChartZone[\"type\"]> =\n    variant === \"chart-dots\"\n      ? [\"dots\"]\n      : variant === \"chart-area\"\n        ? [\"area\"]\n        : [\"line\", \"sparkline\"]\n\n  const chartType = chartTypes[hashString(`${id}-chart`) % chartTypes.length]\n  const points = seededFloats(`${id}-pts`, 8)\n\n  const metrics = [\n    {\n      value: `${seededValue(`${id}-r0`, 120, 890)}`,\n      label: metricLabelFromClause(clauses[0] ?? \"Active now\", \"Active now\"),\n    },\n    {\n      value: `${seededValue(`${id}-r1`, 1, 8)}.${seededValue(`${id}-r1d`, 1, 9)}s`,\n      label: metricLabelFromClause(clauses[1] ?? \"Avg delay\", \"Avg delay\"),\n    },\n  ]\n\n  return {\n    type: \"chart-dashboard\",\n    variant,\n    panelTitle,\n    metrics,\n    chart: {\n      type: chartType,\n      points,\n      heights: points,\n    },\n    dotGrid: chartType === \"dots\" ? buildDotGrid(`${id}-chart`) : undefined,\n  }\n}\n\ntype MiniVariant =\n  | \"table-compact\"\n  | \"table-mono\"\n  | \"dashboard-kpi\"\n  | \"dashboard-split\"\n  | \"chart-line\"\n  | \"chart-area\"\n  | \"chart-dots\"\n\nfunction pickMiniVariant(id: string, intent: PreviewIntent): MiniVariant {\n  const variantsByIntent: Record<PreviewIntent, ReadonlyArray<MiniVariant>> = {\n    task: [\"table-compact\", \"table-mono\"],\n    monitor: [\"dashboard-kpi\", \"dashboard-split\"],\n    realtime: [\"chart-line\", \"chart-area\", \"chart-dots\"],\n    throughput: [\"dashboard-split\", \"chart-dots\", \"chart-area\"],\n    sync: [\"table-mono\", \"table-compact\"],\n  }\n\n  const options = variantsByIntent[intent]\n  return options[hashString(id) % options.length]\n}\n\n/** Fan-out activity — density increases left → right under the hop path. */\nfunction buildFanOutDotGrid(\n  id: string,\n  rows = 8,\n  cols = 14\n): ReadonlyArray<ReadonlyArray<boolean>> {\n  return Array.from({ length: rows }, (_, row) =>\n    Array.from({ length: cols }, (_, col) => {\n      const band = Math.floor((col / cols) * 3)\n      const chance = band === 0 ? 2 : band === 1 ? 3 : 4\n      return hashString(`${id}-fan-${row}-${col}`) % 5 < chance\n    })\n  )\n}\n\n/** Delivery curve with a mid-span choke dip. */\nfunction seededDeliveryCurve(id: string, count = 10): number[] {\n  return Array.from({ length: count }, (_, index) => {\n    const base = seededValue(`${id}-del-${index}`, 58, 94)\n    const choke = index === 4 || index === 5\n    return choke ? Math.max(26, base - 38) : base\n  })\n}\n\n/** Live run monitor — header stat strip + full-width latency histogram. */\nfunction buildRealtimeExecutionZone(\n  id: string,\n  _title: string,\n  _description: string\n): MiniAppScreenZone {\n  return {\n    type: \"live-stream\",\n    panelTitle: \"Live runs\",\n    stats: [\n      {\n        value: `${seededValue(`${id}-r0`, 120, 890)}`,\n        label: \"Runs Firing\",\n      },\n      {\n        value: `${seededValue(`${id}-r1`, 1, 8)}.${seededValue(`${id}-r1d`, 1, 9)}s`,\n        label: \"Stage Delay\",\n      },\n    ],\n    bars: seededFloats(`${id}-bars`, 28),\n  }\n}\n\n/** Orchestration — hop path across services + fan-out activity grid. */\nfunction buildWorkflowOrchestrationZone(\n  id: string,\n  _title: string,\n  _description: string\n): MiniAppScreenZone {\n  const nodes = [\n    { label: \"Trigger\", volume: seededValue(`${id}-node-0`, 86, 148) },\n    { label: \"Fan Out\", volume: seededValue(`${id}-node-1`, 42, 96) },\n    { label: \"Handlers\", volume: seededValue(`${id}-node-2`, 18, 64) },\n  ] as const\n\n  return {\n    type: \"signal-flow\",\n    panelTitle: \"Service chain\",\n    nodes,\n    dotGrid: buildFanOutDotGrid(`${id}-volume`),\n  }\n}\n\n/** Pipeline health — backlog + reliability beside delivery curve with choke. */\nfunction buildPipelineHealthZone(\n  id: string,\n  _title: string,\n  _description: string\n): MiniAppScreenZone {\n  return {\n    type: \"health-split\",\n    panelTitle: \"Pipeline health\",\n    metrics: [\n      {\n        value: `${seededValue(`${id}-m1`, 24, 148)}`,\n        label: \"Backlog\",\n      },\n      {\n        value: `${seededValue(`${id}-m0`, 96, 99)}.${seededValue(`${id}-m0d`, 1, 9)}%`,\n        label: \"Reliability\",\n      },\n    ],\n    chartLabel: \"Delivery\",\n    areaPoints: seededDeliveryCurve(`${id}-uptime`),\n  }\n}\n\n/** Shared job queue — recurring jobs with owner and next run. */\nfunction buildTaskAutomationZone(\n  id: string,\n  _title: string,\n  _description: string\n): MiniAppScreenZone {\n  const jobNames = [\n    \"Daily Sync\",\n    \"Weekly Digest\",\n    \"Invoice Export\",\n    \"Backup Cron\",\n    \"Report Batch\",\n    \"Webhook Retry\",\n  ]\n  const owners = [\"Ops\", \"Finance\", \"Analytics\", \"Infra\", \"Platform\", \"Data\"]\n\n  const rows: MiniTableRow[] = Array.from({ length: 4 }, (_, index) => {\n    const job =\n      jobNames[hashString(`${id}-job-${index}`) % jobNames.length] ?? `Job ${index + 1}`\n    const owner = owners[hashString(`${id}-owner-${index}`) % owners.length] ?? \"Team\"\n    const nextRun = `${seededValue(`${id}-t-${index}`, 8, 23)}:${seededValue(`${id}-m-${index}`, 0, 59).toString().padStart(2, \"0\")}`\n\n    return { cells: [job, owner, nextRun] }\n  })\n\n  const nextHour = seededValue(`${id}-next-h`, 8, 23)\n  const nextMinute = seededValue(`${id}-next-m`, 0, 59).toString().padStart(2, \"0\")\n\n  return {\n    type: \"data-table\",\n    variant: \"table-mono\",\n    panelTitle: \"Scheduled jobs\",\n    queueSummary: `${rows.length} jobs · next run ${nextHour}:${nextMinute}`,\n    columns: [{ header: \"Job\" }, { header: \"Owner\" }, { header: \"Next Run\" }],\n    rows,\n  }\n}\n\nconst FEATURED_ZONE_BUILDERS: Record<\n  string,\n  (id: string, title: string, description: string) => MiniAppScreenZone\n> = {\n  \"real-time-execution\": buildRealtimeExecutionZone,\n  \"workflow-orchestration\": buildWorkflowOrchestrationZone,\n  \"pipeline-health\": buildPipelineHealthZone,\n  \"task-automation\": buildTaskAutomationZone,\n}\n\nfunction buildMiniZone(\n  id: string,\n  title: string,\n  description: string,\n  intent: PreviewIntent\n): MiniAppScreenZone {\n  const featured = FEATURED_ZONE_BUILDERS[id]\n  if (featured) {\n    return featured(id, title, description)\n  }\n\n  const variant = pickMiniVariant(id, intent)\n\n  if (variant === \"table-compact\" || variant === \"table-mono\") {\n    return buildTableZone(id, title, description, variant)\n  }\n\n  if (variant === \"dashboard-kpi\" || variant === \"dashboard-split\") {\n    return buildDashboardZone(id, title, description, variant)\n  }\n\n  return buildChartZone(id, title, description, variant)\n}\n\nexport function buildFeaturePreview(input: BuildFeaturePreviewInput): FeaturePreviewSpec {\n  const intent = classifyIntent(input.id, input.title, input.description)\n\n  return {\n    intent,\n    zones: [buildMiniZone(input.id, input.title, input.description, intent)],\n  }\n}\n",
      "type": "registry:lib",
      "target": "app/feature-section-v8/lib/build-feature-preview.ts"
    },
    {
      "path": "registry/new-york/blocks/feature-section-v8/components/metric-card.tsx",
      "content": "export default function MetricCard({\n  value,\n  label,\n  valueClassName,\n}: {\n  value: string\n  label: string\n  valueClassName?: string\n}) {\n  return (\n    <div className=\"fsv8-metric flex flex-col gap-0.5 px-2.5 py-2\">\n      <span\n        className={\n          valueClassName\n            ? `${valueClassName} text-base font-medium tracking-tight text-foreground tabular-nums`\n            : \"text-base font-medium tracking-tight text-foreground tabular-nums\"\n        }\n      >\n        {value}\n      </span>\n      <span className=\"font-mono text-[0.625rem] uppercase tracking-wider text-muted-foreground\">\n        {label}\n      </span>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "app/feature-section-v8/components/metric-card.tsx"
    },
    {
      "path": "registry/new-york/blocks/feature-section-v8/hooks/use-in-view-once.ts",
      "content": "\"use client\"\n\nimport { useEffect, useRef, useState } from \"react\"\n\nexport function useInViewOnce(threshold = 0.35) {\n  const ref = useRef<HTMLDivElement>(null)\n  const [active, setActive] = useState(false)\n\n  useEffect(() => {\n    const el = ref.current\n    if (!el || active) return\n\n    const observer = new IntersectionObserver(\n      ([entry]) => {\n        if (entry?.isIntersecting) {\n          setActive(true)\n          observer.disconnect()\n        }\n      },\n      { threshold }\n    )\n\n    observer.observe(el)\n    return () => observer.disconnect()\n  }, [active, threshold])\n\n  return { ref, active }\n}\n",
      "type": "registry:hook",
      "target": "app/feature-section-v8/hooks/use-in-view-once.ts"
    },
    {
      "path": "registry/new-york/blocks/feature-section-v8/hooks/use-live-feature-preview.ts",
      "content": "\"use client\"\n\nimport { useEffect, useRef, useState, useSyncExternalStore } from \"react\"\n\nimport {\n  jitterIntervalMs,\n  tickInitialDelayMs,\n  tickIntervalMs,\n} from \"../lib/animation-timing\"\nimport type { MiniAppScreenZone } from \"../lib/build-feature-preview\"\n\nfunction randomBarHeight(): number {\n  return 28 + Math.floor(Math.random() * 72)\n}\n\nfunction jitterLiveStreamBar(\n  zone: Extract<MiniAppScreenZone, { type: \"live-stream\" }>,\n  barIndex: number\n): Extract<MiniAppScreenZone, { type: \"live-stream\" }> {\n  const bars = zone.bars.slice()\n  bars[barIndex] = randomBarHeight()\n  return { ...zone, bars }\n}\n\nfunction subscribeReducedMotion(onStoreChange: () => void) {\n  const mq = window.matchMedia(\"(prefers-reduced-motion: reduce)\")\n  mq.addEventListener(\"change\", onStoreChange)\n  return () => mq.removeEventListener(\"change\", onStoreChange)\n}\n\nfunction getReducedMotion() {\n  return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n}\n\nfunction getReducedMotionServer() {\n  return false\n}\n\nexport type LiveFeaturePreview = {\n  zone: MiniAppScreenZone\n  pulseKey: number\n  activeNodeIndex: number\n  activeRowIndex: number\n}\n\nfunction tickBars(bars: ReadonlyArray<number>): number[] {\n  const next = bars.slice(1)\n  next.push(28 + Math.floor(Math.random() * 72))\n  return next\n}\n\nfunction tickLiveStream(\n  zone: Extract<MiniAppScreenZone, { type: \"live-stream\" }>\n): Extract<MiniAppScreenZone, { type: \"live-stream\" }> {\n  const runStat = zone.stats[0]\n  const latencyStat = zone.stats[1]\n  const runValue = Number.parseInt(runStat.value.replace(/\\D/g, \"\"), 10) || 600\n  const latencyValue = Number.parseFloat(latencyStat.value.replace(/[^\\d.]/g, \"\")) || 2\n\n  const nextLatency = Math.min(\n    9.9,\n    Math.max(1.0, latencyValue + (Math.random() - 0.5) * 0.35)\n  )\n\n  return {\n    ...zone,\n    stats: [\n      {\n        ...runStat,\n        value: `${runValue + 4 + Math.floor(Math.random() * 14)}`,\n      },\n      {\n        ...latencyStat,\n        value: `${nextLatency.toFixed(1)}s`,\n      },\n    ],\n    bars: tickBars(zone.bars),\n  }\n}\n\nfunction tickSignalFlow(\n  zone: Extract<MiniAppScreenZone, { type: \"signal-flow\" }>\n): Extract<MiniAppScreenZone, { type: \"signal-flow\" }> {\n  const nodes = zone.nodes.map((node, index) => {\n    const floor = index === 0 ? 70 : index === 1 ? 30 : 12\n    const ceiling = index === 0 ? 160 : index === 1 ? 110 : 70\n    const next = Math.min(\n      ceiling,\n      Math.max(floor, node.volume + Math.floor(Math.random() * 11) - 4)\n    )\n    return { ...node, volume: next }\n  })\n\n  // Keep the hop path reading as fan-out: volumes stay descending.\n  for (let index = 1; index < nodes.length; index += 1) {\n    const prev = nodes[index - 1]\n    const current = nodes[index]\n    if (current.volume >= prev.volume) {\n      nodes[index] = {\n        ...current,\n        volume: Math.max(6, prev.volume - (4 + Math.floor(Math.random() * 10))),\n      }\n    }\n  }\n\n  return {\n    ...zone,\n    nodes,\n    dotGrid: zone.dotGrid.map((row) =>\n      row.map((active, colIndex) => {\n        const band = Math.floor((colIndex / row.length) * 3)\n        const flipChance = band === 0 ? 0.88 : band === 1 ? 0.8 : 0.72\n        return Math.random() > flipChance ? !active : active\n      })\n    ),\n  }\n}\n\nfunction tickHealthSplit(\n  zone: Extract<MiniAppScreenZone, { type: \"health-split\" }>\n): Extract<MiniAppScreenZone, { type: \"health-split\" }> {\n  const depthMetric = zone.metrics[0]\n  const uptimeMetric = zone.metrics[1]\n  const depth = Number.parseInt(depthMetric.value, 10) || 20\n  const uptime = Number.parseFloat(uptimeMetric.value.replace(/[^\\d.]/g, \"\")) || 97\n  const nextUptime = Math.min(\n    99.9,\n    Math.max(95.0, uptime + (Math.random() > 0.55 ? -0.1 : 0.06))\n  )\n\n  const areaPoints = tickBars(zone.areaPoints).map((point, index) =>\n    index === 4 || index === 5 ? Math.min(point, 42) : Math.max(point, 52)\n  )\n\n  return {\n    ...zone,\n    metrics: [\n      {\n        ...depthMetric,\n        value: `${Math.max(6, depth + Math.floor(Math.random() * 5) - 2)}`,\n      },\n      {\n        ...uptimeMetric,\n        value: `${nextUptime.toFixed(1)}%`,\n      },\n    ],\n    areaPoints,\n  }\n}\n\nfunction tickQueueTime(time: string, stepMinutes = 1): string {\n  const [hours, minutes] = time.split(\":\").map((part) => Number.parseInt(part, 10))\n  if (Number.isNaN(hours) || Number.isNaN(minutes)) return time\n\n  let total = hours * 60 + minutes + stepMinutes\n  if (total >= 24 * 60) total -= 24 * 60\n\n  const nextHours = Math.floor(total / 60)\n  const nextMinutes = total % 60\n  return `${nextHours}:${nextMinutes.toString().padStart(2, \"0\")}`\n}\n\nfunction tickDataTable(\n  zone: Extract<MiniAppScreenZone, { type: \"data-table\" }>\n): Extract<MiniAppScreenZone, { type: \"data-table\" }> {\n  const rows = zone.rows.map((row) => {\n    const updated = row.cells[2]\n    if (!updated?.includes(\":\")) return row\n\n    return {\n      cells: [row.cells[0], row.cells[1], tickQueueTime(updated)] as [\n        string,\n        string,\n        string,\n      ],\n    }\n  })\n\n  const nextSummary = zone.queueSummary.replace(/next (\\d+:\\d+)/, (_, time) => {\n    return `next ${tickQueueTime(time)}`\n  })\n\n  return {\n    ...zone,\n    queueSummary: nextSummary,\n    rows,\n  }\n}\n\nfunction tickZone(zone: MiniAppScreenZone): MiniAppScreenZone {\n  switch (zone.type) {\n    case \"live-stream\":\n      return tickLiveStream(zone)\n    case \"signal-flow\":\n      return tickSignalFlow(zone)\n    case \"health-split\":\n      return tickHealthSplit(zone)\n    case \"data-table\":\n      return tickDataTable(zone)\n    case \"dashboard\":\n      return {\n        ...zone,\n        bars: zone.bars ? tickBars(zone.bars) : undefined,\n        dotGrid: zone.dotGrid\n          ? zone.dotGrid.map((row) =>\n              row.map((active) => (Math.random() > 0.8 ? !active : active))\n            )\n          : undefined,\n      }\n    case \"chart-dashboard\":\n      return {\n        ...zone,\n        chart: {\n          ...zone.chart,\n          heights: zone.chart.heights ? tickBars(zone.chart.heights) : zone.chart.heights,\n          points: zone.chart.points ? tickBars(zone.chart.points) : zone.chart.points,\n        },\n        dotGrid: zone.dotGrid\n          ? zone.dotGrid.map((row) =>\n              row.map((active) => (Math.random() > 0.8 ? !active : active))\n            )\n          : undefined,\n      }\n    default:\n      return zone\n  }\n}\n\nfunction createInitialPreview(zone: MiniAppScreenZone): LiveFeaturePreview {\n  return {\n    zone,\n    pulseKey: 0,\n    activeNodeIndex: 0,\n    activeRowIndex: 0,\n  }\n}\n\nexport function useLiveFeaturePreview(\n  initialZone: MiniAppScreenZone,\n  enabled: boolean,\n  instanceId: string\n): LiveFeaturePreview {\n  const reducedMotion = useSyncExternalStore(\n    subscribeReducedMotion,\n    getReducedMotion,\n    getReducedMotionServer\n  )\n\n  const isLive = enabled && !reducedMotion\n\n  const zoneRef = useRef(initialZone)\n  const pulseRef = useRef(0)\n  const nodeRef = useRef(0)\n  const rowRef = useRef(0)\n\n  const [preview, setPreview] = useState<LiveFeaturePreview>(() =>\n    createInitialPreview(initialZone)\n  )\n\n  useEffect(() => {\n    if (!isLive) {\n      return\n    }\n\n    zoneRef.current = initialZone\n    pulseRef.current = 0\n    nodeRef.current = 0\n    rowRef.current = 0\n\n    let cancelled = false\n    queueMicrotask(() => {\n      if (!cancelled) {\n        setPreview(createInitialPreview(initialZone))\n      }\n    })\n\n    const tickMs = tickIntervalMs(instanceId)\n    const initialDelay = tickInitialDelayMs(instanceId)\n\n    const runTick = () => {\n      zoneRef.current = tickZone(zoneRef.current)\n      pulseRef.current += 1\n      nodeRef.current = (nodeRef.current + 1) % 3\n      rowRef.current =\n        zoneRef.current.type === \"data-table\"\n          ? (rowRef.current + 1) % zoneRef.current.rows.length\n          : 0\n\n      setPreview({\n        zone: zoneRef.current,\n        pulseKey: pulseRef.current,\n        activeNodeIndex: nodeRef.current,\n        activeRowIndex: rowRef.current,\n      })\n    }\n\n    let intervalId: number | undefined\n    const startTimeout = window.setTimeout(() => {\n      runTick()\n      intervalId = window.setInterval(runTick, tickMs)\n    }, initialDelay)\n\n    return () => {\n      cancelled = true\n      window.clearTimeout(startTimeout)\n      if (intervalId !== undefined) window.clearInterval(intervalId)\n    }\n  }, [isLive, initialZone, instanceId])\n\n  useEffect(() => {\n    if (!isLive || initialZone.type !== \"live-stream\") {\n      return\n    }\n\n    const jitterMs = jitterIntervalMs(instanceId)\n\n    const jitterInterval = window.setInterval(() => {\n      const zone = zoneRef.current\n      if (zone.type !== \"live-stream\" || zone.bars.length === 0) return\n\n      const updates = 1 + (Math.random() > 0.55 ? 1 : 0)\n      let nextZone = zone\n\n      for (let count = 0; count < updates; count += 1) {\n        const barIndex = Math.floor(Math.random() * nextZone.bars.length)\n        nextZone = jitterLiveStreamBar(nextZone, barIndex)\n      }\n\n      zoneRef.current = nextZone\n      setPreview((current) => ({ ...current, zone: nextZone }))\n    }, jitterMs)\n\n    return () => window.clearInterval(jitterInterval)\n  }, [isLive, initialZone.type, instanceId])\n\n  if (!isLive) {\n    return createInitialPreview(initialZone)\n  }\n\n  return preview\n}\n",
      "type": "registry:hook",
      "target": "app/feature-section-v8/hooks/use-live-feature-preview.ts"
    },
    {
      "path": "registry/new-york/blocks/feature-section-v8/styles/feature-section-v8.css",
      "content": ".fsv8-mock-frame {\n  --pp-shell-bg: oklch(0.985 0 0);\n  --pp-border: rgb(0 0 0 / 0.1);\n  --pp-accent: rgb(39 39 42);\n  --pp-accent-muted: rgb(39 39 42 / 0.28);\n  --pp-accent-soft: rgb(39 39 42 / 0.06);\n  --pp-accent-row: rgb(39 39 42 / 0.04);\n  --pp-dot-idle: rgb(161 161 170 / 0.35);\n  border: 1px solid var(--border);\n}\n\n.dark .fsv8-mock-frame {\n  --pp-shell-bg: oklch(0.12 0 0);\n  --pp-border: rgb(255 255 255 / 0.1);\n  --pp-accent: rgb(228 228 231);\n  --pp-accent-muted: rgb(228 228 231 / 0.28);\n  --pp-accent-soft: rgb(228 228 231 / 0.08);\n  --pp-accent-row: rgb(228 228 231 / 0.06);\n  --pp-dot-idle: rgb(242 242 242 / 0.35);\n}\n\n.fsv8-dot-pattern {\n  background-image: radial-gradient(\n    circle at center,\n    color-mix(in oklab, var(--foreground) 12%, transparent) 1px,\n    transparent 1px\n  );\n  background-size: 18px 18px;\n  background-position: 12px 12px;\n}\n\n.fsv8-dot-grid {\n  flex: 1;\n  display: grid;\n  width: 100%;\n  min-height: 0;\n  height: 100%;\n  padding: 0.5rem 0.625rem 0.625rem;\n  gap: 0;\n}\n\n.fsv8-dot-grid--compact {\n  padding: 0.375rem 0.625rem 0.5rem;\n}\n\n.fsv8-dot-grid-cell {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  min-width: 0;\n  min-height: 0;\n}\n\n.fsv8-dot-grid-dot {\n  width: clamp(3px, 42%, 7px);\n  height: clamp(3px, 42%, 7px);\n  border-radius: 9999px;\n  background: var(--pp-dot-idle);\n}\n\n.fsv8-dot-grid-dot--active {\n  background: var(--pp-accent);\n}\n\n.fsv8-mock-screen {\n  height: 100%;\n  min-height: 0;\n}\n\n.fsv8-mock-body {\n  flex: 1;\n  min-height: 0;\n}\n\n.fsv8-mock-frame:has(.fsv8-table) {\n  height: 100%;\n  min-height: 100%;\n}\n\n.fsv8-mock-frame:has(.fsv8-table) .fsv8-mock-screen {\n  flex: 1;\n  height: 100%;\n  min-height: 10.25rem;\n}\n\n.fsv8-metric {\n  border-right: 1px solid var(--pp-border);\n}\n\n.fsv8-metric:nth-child(2n) {\n  border-right: none;\n}\n\n.fsv8-stat-strip-item:not(:last-child) {\n  border-right: 1px solid var(--pp-border);\n}\n\n.fsv8-health-sidebar .fsv8-metric {\n  flex: 1;\n  border-right: none;\n  border-bottom: 1px solid var(--pp-border);\n}\n\n.fsv8-health-sidebar .fsv8-metric:last-child {\n  border-bottom: none;\n}\n\n.fsv8-sparkline-wrap {\n  height: 2.75rem;\n}\n\n.fsv8-signal-hop {\n  background: var(--pp-border);\n}\n\n.fsv8-signal-hop--past {\n  background: var(--pp-accent-muted);\n}\n\n.fsv8-signal-hop--active {\n  background: var(--pp-accent);\n}\n\n.fsv8-bar-chart {\n  min-height: 0;\n}\n\n.fsv8-bar-chart--thin {\n  gap: 2px;\n}\n\n.fsv8-bar {\n  transform-origin: bottom;\n  transition:\n    height var(--fsv8-bar-duration, 700ms) cubic-bezier(0.4, 0, 0.2, 1)\n      var(--fsv8-bar-delay, 0ms),\n    background-color 500ms ease var(--fsv8-bar-delay, 0ms);\n}\n\n.fsv8-bar-live {\n  animation: fsv8-bar-rise var(--fsv8-bar-rise-duration, 700ms)\n    cubic-bezier(0.4, 0, 0.2, 1) var(--fsv8-bar-rise-delay, 0ms) backwards;\n}\n\n.fsv8-bar-chart-live {\n  position: relative;\n  overflow: hidden;\n}\n\n.fsv8-bar-chart-live::after {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  pointer-events: none;\n  background: linear-gradient(\n    90deg,\n    transparent 0%,\n    var(--pp-accent-soft) 50%,\n    transparent 100%\n  );\n  transform: translateX(-100%);\n  animation: fsv8-chart-scan 5s ease-in-out var(--fsv8-scan-delay, 0ms) infinite;\n}\n.fsv8-dot-grid-live .fsv8-dot-grid-dot--live {\n  animation: fsv8-dot-pulse var(--fsv8-dot-duration, 1.8s) ease-in-out\n    var(--fsv8-dot-delay, 0ms) infinite;\n}\n\n.fsv8-area-chart-wrap-live {\n  position: relative;\n  overflow: hidden;\n}\n\n.fsv8-area-chart-wrap-live::after {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  pointer-events: none;\n  background: linear-gradient(\n    90deg,\n    transparent 0%,\n    var(--pp-accent-soft) 50%,\n    transparent 100%\n  );\n  transform: translateX(-100%);\n  animation: fsv8-chart-scan 5s ease-in-out var(--fsv8-scan-delay, 0ms) infinite;\n}\n\n.fsv8-stat-live {\n  animation: fsv8-stat-tick var(--fsv8-stat-duration, 650ms) ease\n    var(--fsv8-stat-delay, 0ms);\n}\n\n.fsv8-table-row-live {\n  background: var(--pp-accent-row);\n  transition: background-color 450ms ease;\n}\n\n@keyframes fsv8-bar-rise {\n  from {\n    transform: scaleY(0.15);\n    opacity: 0.35;\n  }\n\n  to {\n    transform: scaleY(1);\n    opacity: 1;\n  }\n}\n\n@keyframes fsv8-chart-scan {\n  0%,\n  100% {\n    transform: translateX(-100%);\n    opacity: 0;\n  }\n\n  12% {\n    opacity: 1;\n  }\n\n  45% {\n    transform: translateX(100%);\n    opacity: 1;\n  }\n\n  55% {\n    opacity: 0;\n  }\n}\n\n@keyframes fsv8-stat-tick {\n  0% {\n    opacity: 0.55;\n    transform: translateY(2px);\n  }\n\n  100% {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n@keyframes fsv8-dot-pulse {\n  0%,\n  100% {\n    opacity: 0.45;\n    transform: scale(0.85);\n  }\n\n  50% {\n    opacity: 1;\n    transform: scale(1);\n  }\n}\n\n.fsv8-bar--thin {\n  width: 4px;\n  min-height: 2px;\n}\n\n.fsv8-line-chart-wrap,\n.fsv8-area-chart-wrap {\n  min-height: 0;\n}\n\n.fsv8-line-chart,\n.fsv8-area-chart {\n  overflow: visible;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .fsv8-bar-live,\n  .fsv8-bar-chart-live::after,\n  .fsv8-area-chart-wrap-live::after,\n  .fsv8-stat-live,\n  .fsv8-dot-grid-dot--live,\n  .fsv8-table-row-live {\n    animation: none;\n    transition: none;\n  }\n}\n",
      "type": "registry:file",
      "target": "app/feature-section-v8/styles/feature-section-v8.css"
    },
    {
      "path": "registry/new-york/blocks/feature-section-v8/lib/animation-timing.ts",
      "content": "import { hashString } from \"./build-feature-preview\"\n\nexport function elementDelay(seed: string, min = 0, max = 900): number {\n  const span = max - min + 1\n  return min + (hashString(seed) % span)\n}\n\nexport function elementDuration(seed: string, min = 450, max = 1300): number {\n  const span = max - min + 1\n  return min + (hashString(seed) % span)\n}\n\nexport function tickIntervalMs(instanceId: string, base = 2400, spread = 1000): number {\n  const offset = (hashString(`${instanceId}-tick`) % spread) - spread / 2\n  return Math.round(base + offset)\n}\n\nexport function tickInitialDelayMs(instanceId: string, max = 2800): number {\n  return hashString(`${instanceId}-init`) % max\n}\n\nexport function jitterIntervalMs(instanceId: string, min = 110, max = 320): number {\n  const span = max - min + 1\n  return min + (hashString(`${instanceId}-jitter`) % span)\n}\n",
      "type": "registry:lib",
      "target": "app/feature-section-v8/lib/animation-timing.ts"
    },
    {
      "path": "registry/new-york/blocks/feature-section-v8/lib/config.ts",
      "content": "export type FeatureSectionContent = {\n  title: string\n}\n\nexport const featureSectionContent: FeatureSectionContent = {\n  title: \"Core capabilities for modern operations\",\n}\n\nexport type FeatureColumnLayout = \"reversed\" | \"default\"\n\nexport type FeaturePreviewSize = \"compact\" | \"default\" | \"large\" | \"tall\"\n\nexport type FeatureColumn = {\n  id: string\n  title: string\n  description: string\n  layout?: FeatureColumnLayout\n  previewSize?: FeaturePreviewSize\n}\n\nexport const featurePreviewMinHeights: Record<FeaturePreviewSize, string> = {\n  compact: \"min-h-24 sm:min-h-28 lg:min-h-32\",\n  default: \"min-h-36 sm:min-h-40 lg:min-h-44\",\n  large: \"min-h-40 sm:min-h-44 lg:min-h-52\",\n  tall: \"min-h-48 sm:min-h-56 lg:min-h-64\",\n}\n\nexport const featureColumns: FeatureColumn[] = [\n  {\n    id: \"real-time-execution\",\n    title: \"Real-time execution\",\n    description:\n      \"Watch runs as they fire, read stage delay in seconds, and spot slowdowns before they reach customers.\",\n    layout: \"reversed\",\n  },\n  {\n    id: \"workflow-orchestration\",\n    title: \"Workflow orchestration\",\n    description:\n      \"Chain steps across services, fan out events to downstream handlers, and see throughput on one canvas.\",\n    previewSize: \"large\",\n  },\n  {\n    id: \"pipeline-health\",\n    title: \"Pipeline health\",\n    description:\n      \"Inspect backlog size, reliability curves, and choke points so delivery stays steady before incidents escalate.\",\n    layout: \"reversed\",\n    previewSize: \"tall\",\n  },\n  {\n    id: \"task-automation\",\n    title: \"Scheduled jobs\",\n    description:\n      \"Set cron-style schedules, assign owners, and scan upcoming handoffs in a single roster view.\",\n    previewSize: \"tall\",\n  },\n]\n",
      "type": "registry:lib",
      "target": "app/feature-section-v8/lib/config.ts"
    }
  ],
  "type": "registry:block"
}