{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cover",
  "type": "registry:component",
  "title": "Cover",
  "description": "Deterministic FNV-1a-seeded generative SVG cover art in three variants: curves, blocks, bars.",
  "dependencies": [],
  "registryDependencies": [
    "https://mocoui.site/r/tokens.json"
  ],
  "files": [
    {
      "path": "registry/cover/cover.tsx",
      "type": "registry:component",
      "target": "components/moco/cover.tsx",
      "content": "import * as React from \"react\";\nimport \"./cover.css\";\n\n/**\n * Deterministic generative cover art. The same seed always draws the same\n * SVG — hashing is FNV-1a, randomness is a seeded PRNG, never Math.random.\n * Three variants: curves, blocks, bars. Strokes are non-scaling, so the same\n * artwork reads at 640px and at 150px.\n */\n\nconst W = 640;\nconst H = 280;\n\nexport interface CoverProps {\n  /** Usually the post slug. The same seed always draws the same cover. */\n  seed: string;\n  alt: string;\n  /** Force a variant (0 curves, 1 blocks, 2 bars). Defaults to seed-derived. */\n  variant?: 0 | 1 | 2;\n  className?: string;\n}\n\n/** FNV-1a. Stable across builds, unlike anything involving Math.random. */\nfunction hash(s: string): number {\n  let h = 0x811c9dc5;\n  for (let i = 0; i < s.length; i++) {\n    h ^= s.charCodeAt(i);\n    h = Math.imul(h, 0x01000193);\n  }\n  return h >>> 0;\n}\n\n/** mulberry32 — tiny seeded PRNG, deterministic across engines. */\nfunction rng(seed: number): () => number {\n  let s = seed >>> 0;\n  return () => {\n    s = (s + 0x6d2b79f5) >>> 0;\n    let t = Math.imul(s ^ (s >>> 15), 1 | s);\n    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n  };\n}\n\n/* One accent stroke per composition; supporting strokes stay quiet. */\nconst TONES = [\"var(--moco-faint)\", \"var(--moco-warn)\", \"var(--moco-ink)\"];\nconst DASHES = [\"2 4\", \"7 5\", \"12 4 3 4\"];\n\n/** Smooth open path through points via quadratic midpoint segments. */\nfunction smoothPath(pts: [number, number][]): string {\n  const d = [`M${pts[0][0].toFixed(1)},${pts[0][1].toFixed(1)}`];\n  for (let i = 1; i < pts.length - 1; i++) {\n    const [x, y] = pts[i];\n    const mx = (x + pts[i + 1][0]) / 2;\n    const my = (y + pts[i + 1][1]) / 2;\n    d.push(`Q${x.toFixed(1)},${y.toFixed(1)} ${mx.toFixed(1)},${my.toFixed(1)}`);\n  }\n  const last = pts[pts.length - 1];\n  d.push(`L${last[0].toFixed(1)},${last[1].toFixed(1)}`);\n  return d.join(\" \");\n}\n\n/** Variant 0 — gridlines and a family of drifting curves, one in accent. */\nfunction Curves({ seed }: { seed: number }) {\n  const r = rng(seed);\n  const rules = 3 + (seed % 3);\n  const curves = 3 + Math.floor(r() * 2); // 3–4 quiet curves + 1 accent\n  const steps = 9;\n\n  const makeCurve = (drift: number) => {\n    let y = 56 + r() * (H - 140);\n    const pts: [number, number][] = [];\n    for (let i = 0; i < steps; i++) {\n      pts.push([32 + ((W - 64) * i) / (steps - 1), y]);\n      y += (r() - 0.5 + drift) * 44;\n      y = Math.min(H - 40, Math.max(36, y));\n    }\n    return pts;\n  };\n\n  const accent = makeCurve(0.28); // the accent curve trends down-right\n\n  return (\n    <>\n      {Array.from({ length: rules }, (_, i) => {\n        const y = 36 + ((H - 72) * (i + 1)) / (rules + 1);\n        return (\n          <line\n            key={i}\n            x1={32}\n            x2={W - 32}\n            y1={y}\n            y2={y}\n            stroke=\"var(--moco-line)\"\n            vectorEffect=\"non-scaling-stroke\"\n          />\n        );\n      })}\n      {Array.from({ length: curves }, (_, i) => (\n        <path\n          key={i}\n          d={smoothPath(makeCurve((r() - 0.5) * 0.3))}\n          fill=\"none\"\n          stroke={TONES[i % TONES.length]}\n          strokeWidth={1.25}\n          strokeDasharray={DASHES[i % DASHES.length]}\n          opacity={0.7}\n          vectorEffect=\"non-scaling-stroke\"\n        />\n      ))}\n      <path\n        d={smoothPath(accent)}\n        fill=\"none\"\n        stroke=\"var(--moco-accent-dk)\"\n        strokeWidth={2.25}\n        vectorEffect=\"non-scaling-stroke\"\n      />\n      <circle\n        cx={accent[accent.length - 1][0]}\n        cy={accent[accent.length - 1][1]}\n        r=\"4\"\n        fill=\"var(--moco-accent-dk)\"\n      />\n    </>\n  );\n}\n\n/** Variant 1 — rows of proportional blocks with one diamond breakpoint each. */\nfunction Blocks({ seed }: { seed: number }) {\n  const r = rng(seed ^ 0x9e3779b9);\n  const rows = 4;\n  const gap = 10;\n  const rowH = 32;\n  const top = (H - (rows * rowH + (rows - 1) * 20)) / 2;\n  const count = 4 + (seed % 2);\n  const widths = Array.from({ length: count }, () => 0.5 + r());\n  const total = widths.reduce((a, b) => a + b, 0);\n  const inner = W - 64;\n\n  return (\n    <>\n      {Array.from({ length: rows }, (_, row) => {\n        const y = top + row * (rowH + 20);\n        const cut = ((seed >> (row * 2)) % (count - 1)) + 1;\n        let x = 32;\n        const marks: React.ReactNode[] = [];\n        widths.forEach((w, i) => {\n          const bw = ((inner - gap * (count - 1)) * w) / total;\n          marks.push(\n            <rect\n              key={i}\n              x={x}\n              y={y}\n              width={bw}\n              height={rowH}\n              rx=\"2\"\n              fill={i < cut ? \"var(--moco-accent-dk)\" : \"none\"}\n              stroke={i < cut ? \"var(--moco-accent-dk)\" : \"var(--moco-line)\"}\n              strokeDasharray={i >= cut + 1 ? \"5 5\" : undefined}\n              opacity={i < cut ? 1 - row * 0.17 : 1}\n              vectorEffect=\"non-scaling-stroke\"\n            />,\n          );\n          if (i === cut - 1) {\n            const cx = x + bw + gap / 2;\n            marks.push(\n              <rect\n                key={`d${i}`}\n                x={cx - 5}\n                y={y + rowH / 2 - 5}\n                width=\"10\"\n                height=\"10\"\n                fill=\"var(--moco-bg)\"\n                stroke=\"var(--moco-accent-dk)\"\n                transform={`rotate(45 ${cx} ${y + rowH / 2})`}\n                vectorEffect=\"non-scaling-stroke\"\n              />,\n            );\n          }\n          x += bw + gap;\n        });\n        return <g key={row}>{marks}</g>;\n      })}\n    </>\n  );\n}\n\n/** Variant 2 — paired bars: a full outline bar over a shorter accent bar,\n *  with a dashed bracket spanning the difference. */\nfunction Bars({ seed }: { seed: number }) {\n  const r = rng(seed ^ 0x85ebca6b);\n  const groups = 3;\n  const barH = 20;\n  const inner = W - 64;\n  const pairGap = 10;\n  const groupGap = 26;\n  const groupH = barH * 2 + pairGap;\n  const top = (H - (groups * groupH + (groups - 1) * groupGap)) / 2;\n\n  return (\n    <>\n      {Array.from({ length: groups }, (_, gi) => {\n        const gy = top + gi * (groupH + groupGap);\n        const full = inner * (0.82 + r() * 0.18);\n        const kept = full * (0.2 + r() * 0.4);\n        const split = 0.35 + r() * 0.35; // solid/dashed split inside each bar\n        const rowFor = (len: number, y: number, on: boolean) => {\n          const solid = len * split;\n          return (\n            <g key={y}>\n              <rect\n                x={32}\n                y={y}\n                width={Math.max(solid, 2)}\n                height={barH}\n                rx=\"2\"\n                fill={on ? \"var(--moco-accent-dk)\" : \"none\"}\n                stroke={on ? \"var(--moco-accent-dk)\" : \"var(--moco-line)\"}\n                vectorEffect=\"non-scaling-stroke\"\n              />\n              <rect\n                x={32 + solid + 3}\n                y={y}\n                width={Math.max(len - solid - 3, 2)}\n                height={barH}\n                rx=\"2\"\n                fill=\"none\"\n                stroke={on ? \"var(--moco-accent-dk)\" : \"var(--moco-line)\"}\n                strokeDasharray=\"5 5\"\n                opacity={0.8}\n                vectorEffect=\"non-scaling-stroke\"\n              />\n            </g>\n          );\n        };\n        return (\n          <g key={gi}>\n            {rowFor(full, gy, false)}\n            {rowFor(kept, gy + barH + pairGap, true)}\n            <line\n              x1={32 + kept}\n              x2={32 + full}\n              y1={gy + groupH + 6}\n              y2={gy + groupH + 6}\n              stroke=\"var(--moco-accent-dk)\"\n              strokeDasharray=\"2 3\"\n              vectorEffect=\"non-scaling-stroke\"\n            />\n          </g>\n        );\n      })}\n    </>\n  );\n}\n\nconst VARIANTS = [Curves, Blocks, Bars];\n\nexport function Cover({ seed, alt, variant, className }: CoverProps) {\n  const h = hash(seed);\n  const Art = VARIANTS[variant ?? h % VARIANTS.length];\n  return (\n    <svg\n      className={`moco-cover${className ? ` ${className}` : \"\"}`}\n      viewBox={`0 0 ${W} ${H}`}\n      preserveAspectRatio=\"xMidYMid meet\"\n      role={alt ? \"img\" : \"presentation\"}\n      aria-label={alt || undefined}\n      aria-hidden={alt ? undefined : true}\n    >\n      <rect width={W} height={H} fill=\"var(--moco-panel)\" />\n      <Art seed={h} />\n    </svg>\n  );\n}\n"
    },
    {
      "path": "registry/cover/cover.css",
      "type": "registry:file",
      "target": "components/moco/cover.css",
      "content": ".moco-cover {\n  display: block;\n  width: 100%;\n  height: auto;\n  aspect-ratio: 16 / 7;\n  border: 1px solid var(--moco-line);\n  border-radius: 3px;\n  background: var(--moco-panel);\n}\n"
    }
  ],
  "docs": "import { Cover } from \"./cover\";\n\nexport default function CoverDemo() {\n  return (\n    <div style={{ display: \"grid\", gap: \"1rem\", maxWidth: \"40rem\" }}>\n      <Cover seed=\"prompt-caching\" alt=\"Generative curves cover\" variant={0} />\n      <Cover seed=\"prefix-blocks\" alt=\"Generative blocks cover\" variant={1} />\n      <Cover seed=\"cost-bars\" alt=\"Generative bars cover\" variant={2} />\n      <Cover seed=\"any-post-slug\" alt=\"Variant chosen by the seed itself\" />\n    </div>\n  );\n}\n",
  "meta": {
    "tags": [
      "generative",
      "svg",
      "cover",
      "art"
    ]
  }
}