{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pattern-tile",
  "title": "Pattern Tile",
  "description": "A quarter-disc tile is rotated, mirrored and translated into a wall of 72 tiles.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/pattern-tile/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont } from \"@remotion/google-fonts/SchibstedGrotesk\";\nimport { useId } from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nconst { fontFamily: sans } = loadFont(\"normal\", {\n  weights: [\"400\", \"500\"],\n  subsets: [\"latin\"],\n});\n\nexport interface PatternTileProps {\n  steps?: [string, string, string, string];\n  unit?: string;\n  accent?: string;\n  theme?: \"dark\" | \"light\";\n  speed?: number;\n}\n\n// gradients.sh \"fluted\" palette, laid across the whole wall in grid space so\n// every quarter-disc is a slice of one continuous field.\nconst FLUTED = [\"#1d2a8f\", \"#2f7bff\", \"#ff7a3d\", \"#ffe2c4\"] as const;\n\nconst COLS = 12;\nconst ROWS = 6;\nconst HERO: [number, number] = [4, 2];\nconst MIRROR_X = 6;\n\nconst ROT = [32, 43, 54];\nconst ROT_LEN = 11;\nconst MIRROR: [number, number] = [68, 92];\nconst SLIDE_H: [number, number] = [98, 116];\nconst SLIDE_V: [number, number] = [116, 134];\nconst FOCUS = 140;\n\nconst EASE_IO = Easing.bezier(0.65, 0, 0.35, 1);\nconst EASE_OUT = Easing.bezier(0.22, 1, 0.36, 1);\nconst EASE_TURN = Easing.bezier(0.55, 0, 0.2, 1);\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\n\nconst THEMES = {\n  dark: {\n    bg: \"#0A0A0B\",\n    key: \"rgba(255,255,255,0.04)\",\n    surface: \"#141416\",\n    disc: \"#E6E5E1\",\n    bar: \"#8E8C88\",\n    fg: \"#F3F2EF\",\n    muted: \"#8B8A90\",\n    edge: \"rgba(255,255,255,0.16)\",\n    dim: 0.6,\n    grain: 0.06,\n  },\n  light: {\n    bg: \"#F5F4F0\",\n    key: \"rgba(255,255,255,0.85)\",\n    surface: \"#EAE8E2\",\n    disc: \"#18181A\",\n    bar: \"#8A8882\",\n    fg: \"#121214\",\n    muted: \"#6E6C67\",\n    edge: \"rgba(18,18,20,0.18)\",\n    dim: 0.55,\n    grain: 0.045,\n  },\n} as const;\n\nfunction Grain({ opacity }: { opacity: number }) {\n  const id = `g${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n  return (\n    <svg\n      width=\"100%\"\n      height=\"100%\"\n      style={{ position: \"absolute\", inset: 0, opacity, pointerEvents: \"none\" }}\n      aria-hidden\n    >\n      <filter id={id}>\n        <feTurbulence\n          type=\"fractalNoise\"\n          baseFrequency=\"0.9\"\n          numOctaves={2}\n          seed={23}\n          stitchTiles=\"stitch\"\n        />\n        <feColorMatrix values=\"0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0.9 0\" />\n      </filter>\n      <rect width=\"100%\" height=\"100%\" filter={`url(#${id})`} />\n    </svg>\n  );\n}\n\nfunction pie(r: number, a: number, bleed: number) {\n  if (a >= Math.PI / 2 - 1e-6) {\n    const b = bleed.toFixed(3);\n    const rr = r.toFixed(3);\n    return `M-${b} -${b} L${rr} -${b} L${rr} 0 A${rr} ${rr} 0 0 1 0 ${rr} L-${b} ${rr} Z`;\n  }\n  const ex = r * Math.cos(a);\n  const ey = r * Math.sin(a);\n  return `M0 0 L${r.toFixed(3)} 0 A${r.toFixed(3)} ${r.toFixed(3)} 0 0 1 ${ex.toFixed(3)} ${ey.toFixed(3)} Z`;\n}\n\ninterface Look {\n  disc: string;\n  bar: string;\n  motif: number;\n}\n\ninterface TileInst {\n  key: string;\n  tf: string;\n  sweep: number;\n  grow: number;\n}\n\ninterface Group {\n  key: string;\n  tf: string;\n  tiles: TileInst[];\n}\n\nfunction Motif({\n  T,\n  look,\n  sweep,\n  grow,\n  bleed,\n  part = \"both\",\n}: {\n  T: number;\n  look: Look;\n  sweep: number;\n  grow: number;\n  bleed: number;\n  part?: \"both\" | \"disc\" | \"bar\";\n}) {\n  return (\n    <g opacity={look.motif}>\n      {sweep > 0 && part !== \"bar\" ? (\n        <path\n          d={pie(0.6 * T, (Math.PI / 2) * Math.min(1, sweep), bleed)}\n          fill={look.disc}\n        />\n      ) : null}\n      {grow > 0 && part !== \"disc\" ? (\n        <rect\n          x={0.72 * T}\n          y={0.12 * T}\n          width={0.16 * T}\n          height={0.5 * T * grow}\n          fill={look.bar}\n        />\n      ) : null}\n    </g>\n  );\n}\n\nfunction GroupView({\n  group,\n  T,\n  surface,\n  look,\n  bleed,\n  field,\n  maskId,\n}: {\n  group: Group;\n  T: number;\n  surface: string;\n  look: Look;\n  bleed: number;\n  field: string;\n  maskId: string;\n}) {\n  const box = {\n    x: -COLS * T,\n    y: -ROWS * T,\n    width: COLS * T * 3,\n    height: ROWS * T * 3,\n  };\n  const white: Look = { disc: \"#FFFFFF\", bar: \"#FFFFFF\", motif: 1 };\n  return (\n    <g>\n      <g transform={group.tf || undefined}>\n        {group.tiles.map((tile) => (\n          <rect\n            key={`s${tile.key}`}\n            transform={tile.tf}\n            x={0}\n            y={0}\n            width={T}\n            height={T}\n            fill={surface}\n            stroke={surface}\n            strokeWidth={0.8}\n          />\n        ))}\n        {group.tiles.map((tile) => (\n          <g key={`b${tile.key}`} transform={tile.tf}>\n            <Motif\n              T={T}\n              look={look}\n              sweep={tile.sweep}\n              grow={tile.grow}\n              bleed={bleed}\n              part=\"bar\"\n            />\n          </g>\n        ))}\n      </g>\n      <mask id={maskId} maskUnits=\"userSpaceOnUse\" {...box}>\n        <g transform={group.tf || undefined}>\n          {group.tiles.map((tile) => (\n            <g key={`m${tile.key}`} transform={tile.tf}>\n              <Motif\n                T={T}\n                look={white}\n                sweep={tile.sweep}\n                grow={tile.grow}\n                bleed={bleed}\n                part=\"disc\"\n              />\n            </g>\n          ))}\n        </g>\n      </mask>\n      <rect\n        {...box}\n        fill={field}\n        mask={`url(#${maskId})`}\n        opacity={look.motif}\n      />\n    </g>\n  );\n}\n\nexport function PatternTile({\n  steps = [\"tile\", \"rotate 90°\", \"mirror\", \"translate\"],\n  unit = \"tiles\",\n  accent = \"#3B5BFD\",\n  theme = \"dark\",\n  speed = 1,\n}: PatternTileProps) {\n  const t = useCurrentFrame() * speed;\n  const { width: W, height: H } = useVideoConfig();\n  const u = H / 720;\n  const c = THEMES[theme];\n\n  const labelF = 18 * u;\n  const labelGap = 30 * u;\n  const T = Math.min((W * 0.84) / COLS, (H * 0.66) / ROWS);\n  const gridW = T * COLS;\n  const gridH = T * ROWS;\n  const blockTop = (H - (gridH + labelGap + labelF)) / 2;\n  const pcx = W / 2;\n  const pcy = blockTop + gridH / 2;\n\n  const camAt = (k: number[], v: number[]) =>\n    interpolate(t, k, v, { ...CLAMP, easing: EASE_IO });\n  const camK = [24, 64, 66, 94, 96, 124];\n  const logS = camAt(camK, [\n    Math.log(2.8),\n    Math.log(2.05),\n    Math.log(2.05),\n    Math.log(1.45),\n    Math.log(1.45),\n    0,\n  ]);\n  const S = Math.exp(logS);\n  const camX = camAt(camK, [4.5, 5, 5, 6, 6, 6]);\n  const camY = camAt(camK, [2.5, 3, 3, 3, 3, 3]);\n\n  const appear = interpolate(t, [0, 12], [0, 1], {\n    ...CLAMP,\n    easing: EASE_OUT,\n  });\n  const sweep = interpolate(t, [6, 24], [0, 1], { ...CLAMP, easing: EASE_IO });\n  const grow = interpolate(t, [14, 28], [0, 1], { ...CLAMP, easing: EASE_OUT });\n  const edgeDraw = interpolate(t, [0, 22], [0, 1], {\n    ...CLAMP,\n    easing: EASE_IO,\n  });\n  const edgeFade = interpolate(t, [28, 44], [1, 0], CLAMP);\n\n  const rotP = ROT.map((s) =>\n    interpolate(t, [s, s + ROT_LEN], [0, 1], { ...CLAMP, easing: EASE_TURN }),\n  );\n  const flip = interpolate(t, MIRROR, [1, -1], { ...CLAMP, easing: EASE_IO });\n  const slideH = interpolate(t, SLIDE_H, [0, 1], { ...CLAMP, easing: EASE_IO });\n  const slideV = interpolate(t, SLIDE_V, [0, 1], { ...CLAMP, easing: EASE_IO });\n  const focus = interpolate(t, [FOCUS, FOCUS + 16], [0, 1], {\n    ...CLAMP,\n    easing: EASE_IO,\n  });\n\n  const look: Look = {\n    disc: c.disc,\n    bar: c.bar,\n    motif: 1 - (1 - c.dim) * focus,\n  };\n  const heroTf = `translate(${HERO[0] * T} ${HERO[1] * T})`;\n  const rotTf = (angle: number) =>\n    `${heroTf} rotate(${angle.toFixed(3)} ${T} ${T})`;\n  const mirTf = (sx: number) =>\n    `translate(${MIRROR_X * T} 0) scale(${sx.toFixed(4)} 1) translate(${-MIRROR_X * T} 0)`;\n  const tile = (key: string, tf: string, sw = 1, gr = 1): TileInst => ({\n    key,\n    tf,\n    sweep: sw,\n    grow: gr,\n  });\n  const rosette = (prefix: string, pre = \"\"): TileInst[] =>\n    [0, 90, 180, 270].map((ang) =>\n      tile(`${prefix}${ang}`, `${pre} ${rotTf(ang)}`.trim()),\n    );\n  const blockTiles = (prefix: string, pre = \"\"): TileInst[] => [\n    ...rosette(`${prefix}a`, pre),\n    ...rosette(`${prefix}m`, `${pre} ${mirTf(-1)}`.trim()),\n  ];\n\n  const still: TileInst[] = [tile(\"hero\", rotTf(0), sweep, grow)];\n  const under: Group[] = [];\n  const over: Group[] = [];\n  rotP.forEach((p, k) => {\n    if (p >= 1) still.push(tile(`r${k}`, rotTf(90 * (k + 1))));\n    else if (p > 0)\n      over.push({\n        key: `rot${k}`,\n        tf: \"\",\n        tiles: [tile(`r${k}`, rotTf(90 * k + 90 * p))],\n      });\n  });\n  if (t >= MIRROR[1]) still.push(...rosette(\"m\", mirTf(-1)));\n  else if (t >= MIRROR[0])\n    under.push({ key: \"mir\", tf: mirTf(flip), tiles: rosette(\"m\") });\n  for (const dx of [-4, 4]) {\n    if (t >= SLIDE_H[1])\n      still.push(...blockTiles(`h${dx}`, `translate(${dx * T} 0)`));\n    else if (t >= SLIDE_H[0])\n      over.push({\n        key: `h${dx}`,\n        tf: `translate(${(dx * T * slideH).toFixed(3)} 0)`,\n        tiles: blockTiles(`h${dx}`),\n      });\n  }\n  for (const dy of [-2, 2]) {\n    for (const dx of [-4, 0, 4]) {\n      const pre = `translate(${dx * T} 0)`;\n      if (t >= SLIDE_V[1])\n        still.push(\n          ...blockTiles(`v${dx}${dy}`, `translate(${dx * T} ${dy * T})`),\n        );\n      else if (t >= SLIDE_V[0])\n        over.push({\n          key: `v${dx}${dy}`,\n          tf: `translate(0 ${(dy * T * slideV).toFixed(3)})`,\n          tiles: blockTiles(`v${dx}${dy}`, pre),\n        });\n    }\n  }\n  const stillGroup: Group = { key: \"still\", tf: \"\", tiles: still };\n  const heroLook: Look = { disc: c.fg, bar: c.fg, motif: 1 };\n  const gid = `pt${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n  const field = `url(#${gid}f)`;\n  const fdx = Math.sin(t / 48) * 0.35 * T;\n\n  const pivotO =\n    interpolate(t, [ROT[0] - 4, ROT[0] + 2], [0, 1], CLAMP) *\n    interpolate(t, [ROT[2] + ROT_LEN, ROT[2] + ROT_LEN + 8], [1, 0], CLAMP);\n  const axisO =\n    interpolate(t, [MIRROR[0] - 4, MIRROR[0] + 4], [0, 1], CLAMP) *\n    interpolate(t, [MIRROR[1], MIRROR[1] + 8], [1, 0], CLAMP);\n  const axisGrow = interpolate(t, [MIRROR[0] - 4, MIRROR[0] + 8], [0, 1], {\n    ...CLAMP,\n    easing: EASE_OUT,\n  });\n\n  const tiles =\n    t < ROT[0] + ROT_LEN\n      ? 1\n      : t < ROT[1] + ROT_LEN\n        ? 2\n        : t < ROT[2] + ROT_LEN\n          ? 3\n          : t < MIRROR[1]\n            ? 4\n            : t < SLIDE_H[1]\n              ? 8\n              : t < SLIDE_V[1]\n                ? 24\n                : 72;\n\n  const phase =\n    t < ROT[0] ? 0 : t < MIRROR[0] - 2 ? 1 : t < SLIDE_H[0] - 2 ? 2 : 3;\n  const allDone = t >= FOCUS;\n\n  const tx = pcx - camX * T * S;\n  const ty = pcy - camY * T * S;\n  const edgeLen = 4 * T;\n  const labelIn = interpolate(t, [4, 18], [0, 1], {\n    ...CLAMP,\n    easing: EASE_OUT,\n  });\n\n  return (\n    <AbsoluteFill style={{ background: c.bg, overflow: \"hidden\" }}>\n      <AbsoluteFill\n        style={{\n          background: `radial-gradient(ellipse 70% 80% at 45% 35%, ${c.key}, transparent 70%)`,\n        }}\n      />\n      <svg\n        width={W}\n        height={H}\n        style={{ position: \"absolute\", inset: 0 }}\n        aria-hidden\n      >\n        <g\n          transform={`translate(${tx.toFixed(3)} ${ty.toFixed(3)}) scale(${S.toFixed(5)})`}\n          opacity={appear}\n        >\n          <defs>\n            <linearGradient\n              id={`${gid}f`}\n              gradientUnits=\"userSpaceOnUse\"\n              x1={fdx}\n              y1={0}\n              x2={COLS * T + fdx}\n              y2={ROWS * T}\n            >\n              <stop offset={0.04} stopColor={FLUTED[0]} />\n              <stop offset={0.36} stopColor={FLUTED[1]} />\n              <stop offset={0.68} stopColor={FLUTED[2]} />\n              <stop offset={0.96} stopColor={FLUTED[3]} />\n            </linearGradient>\n          </defs>\n          {under.map((g) => (\n            <GroupView\n              key={g.key}\n              group={g}\n              T={T}\n              surface={c.surface}\n              bleed={0.6 / S}\n              look={look}\n              field={field}\n              maskId={`${gid}${g.key}`}\n            />\n          ))}\n          <GroupView\n            group={stillGroup}\n            T={T}\n            surface={c.surface}\n            look={look}\n            bleed={0.6 / S}\n            field={field}\n            maskId={`${gid}still`}\n          />\n          {over.map((g) => (\n            <GroupView\n              key={g.key}\n              group={g}\n              T={T}\n              surface={c.surface}\n              bleed={0.6 / S}\n              look={look}\n              field={field}\n              maskId={`${gid}${g.key}`}\n            />\n          ))}\n          {focus > 0 ? (\n            <g transform={heroTf} opacity={focus}>\n              <Motif T={T} look={heroLook} sweep={1} grow={1} bleed={0} />\n            </g>\n          ) : null}\n          {pivotO > 0 ? (\n            <circle\n              cx={(HERO[0] + 1) * T}\n              cy={(HERO[1] + 1) * T}\n              r={(5 * u) / S}\n              fill={accent}\n              opacity={pivotO}\n            />\n          ) : null}\n          {axisO > 0 ? (\n            <rect\n              x={MIRROR_X * T - (0.75 * u) / S}\n              y={(HERO[1] + 1) * T - 1.25 * T * axisGrow}\n              width={(1.5 * u) / S}\n              height={2.5 * T * axisGrow}\n              fill={accent}\n              opacity={axisO}\n            />\n          ) : null}\n          {edgeFade > 0 ? (\n            <rect\n              x={HERO[0] * T}\n              y={HERO[1] * T}\n              width={T}\n              height={T}\n              fill=\"none\"\n              stroke={c.edge}\n              strokeWidth={1.2 / S}\n              strokeDasharray={`${edgeLen * edgeDraw} ${edgeLen}`}\n              opacity={edgeFade}\n            />\n          ) : null}\n        </g>\n      </svg>\n\n      <div\n        style={{\n          position: \"absolute\",\n          left: pcx - gridW / 2,\n          width: gridW,\n          top: blockTop + gridH + labelGap,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"space-between\",\n          fontFamily: sans,\n          fontWeight: 500,\n          fontSize: labelF,\n          lineHeight: 1,\n          letterSpacing: \"-0.01em\",\n          whiteSpace: \"nowrap\",\n          opacity: labelIn,\n        }}\n      >\n        <div style={{ display: \"flex\", alignItems: \"center\", gap: 14 * u }}>\n          {steps.map((s, i) => {\n            const active = i === phase && !allDone;\n            const done = i < phase || allDone;\n            return (\n              <div\n                key={s}\n                style={{ display: \"flex\", alignItems: \"center\", gap: 14 * u }}\n              >\n                {i > 0 ? (\n                  <span style={{ color: c.muted, opacity: 0.5 }}>→</span>\n                ) : null}\n                <span\n                  style={{\n                    color: active ? c.fg : c.muted,\n                    opacity: active || done ? 1 : 0.45,\n                  }}\n                >\n                  {s}\n                </span>\n              </div>\n            );\n          })}\n        </div>\n        <div\n          style={{\n            display: \"flex\",\n            alignItems: \"center\",\n            gap: 10 * u,\n            color: c.fg,\n            fontWeight: 500,\n            fontVariantNumeric: \"tabular-nums\",\n          }}\n        >\n          <span\n            style={{\n              width: 10 * u,\n              height: 10 * u,\n              background: accent,\n              opacity: focus,\n            }}\n          />\n          <span>\n            {tiles}{\" \"}\n            <span style={{ color: c.muted, fontWeight: 400 }}>\n              {tiles === 1 ? unit.replace(/s$/, \"\") : unit}\n            </span>\n          </span>\n        </div>\n      </div>\n      <Grain opacity={c.grain} />\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/pattern-tile.tsx"
    }
  ],
  "type": "registry:component"
}
