{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "isometric-build",
  "title": "Isometric Build",
  "description": "An isometric tile grid springs up row by row into a stepped block sculpture with one accent tower, then holds.",
  "dependencies": ["remotion"],
  "files": [
    {
      "path": "registry/remocn/isometric-build/index.tsx",
      "content": "\"use client\";\n\nimport { useMemo } from \"react\";\nimport {\n  AbsoluteFill,\n  interpolate,\n  random,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nexport type IsometricBuildProps = {\n  /** Tiles per side. */\n  grid?: number;\n  /** Tallest regular block, in tile widths. The accent tower rises above it. */\n  maxHeight?: number;\n  /** Frames between consecutive diagonals rising. */\n  stagger?: number;\n  seed?: number;\n  speed?: number;\n  background?: string;\n  /** Block color; side faces are this color shaded toward the background. */\n  color?: string;\n  /** The single accent tower. */\n  accent?: string;\n  /** Draw floor lines on the block faces. */\n  floors?: boolean;\n  /** Gap between neighbouring blocks, as a share of a tile. */\n  gap?: number;\n};\n\nexport const isometricBuildDefaults: Required<IsometricBuildProps> = {\n  grid: 5,\n  maxHeight: 2.2,\n  stagger: 2,\n  seed: 4,\n  speed: 1,\n  background: \"#111111\",\n  color: \"#FFFFFF\",\n  accent: \"#E5322D\",\n  floors: true,\n  gap: 0.04,\n};\n\n// True isometric: tile half-height = half-width · tan 30°, unit vertical edge\n// matches the projected tile edge.\nconst ISO_Y = Math.tan(Math.PI / 6);\nconst ISO_Z = 1 / Math.cos(Math.PI / 6);\nconst FLOOR = 0.42;\nconst SHADE_LEFT = 0.32;\nconst SHADE_RIGHT = 0.6;\n\nconst fmt = (n: number) => Math.round(n * 10) / 10;\n\ntype Block = {\n  i: number;\n  j: number;\n  height: number;\n  accent: boolean;\n  delay: number;\n};\n\nfunction makeBlocks(\n  n: number,\n  maxHeight: number,\n  seed: number,\n  stagger: number,\n) {\n  const r = (key: string) => random(`isometric-build-${seed}-${key}`);\n  const inner = Math.max(1, n - 2);\n  const ai = n > 2 ? 1 + Math.floor(r(\"ai\") * inner) : 0;\n  const aj = n > 2 ? 1 + Math.floor(r(\"aj\") * inner) : 0;\n  const blocks: Block[] = [];\n  // Painter order: back (i + j small) to front.\n  for (let sum = 0; sum <= 2 * (n - 1); sum++) {\n    for (let i = 0; i < n; i++) {\n      const j = sum - i;\n      if (j < 0 || j >= n) continue;\n      const accent = i === ai && j === aj;\n      // One compact mass: heights follow a mound that peaks in the middle,\n      // with a little seeded variation, snapped to whole floors so the\n      // blocks stack like a single stepped sculpture rather than noise.\n      const c = (n - 1) / 2;\n      const centre = 1 - Math.hypot(i - c, j - c) / (Math.hypot(c, c) || 1);\n      const raw =\n        maxHeight *\n        (0.2 + 0.8 * centre ** 1.3) *\n        (0.75 + 0.5 * r(`h${i}-${j}`));\n      const h = Math.max(FLOOR, Math.round(raw / FLOOR) * FLOOR);\n      blocks.push({\n        i,\n        j,\n        accent,\n        height: accent ? maxHeight * 1.35 : h,\n        // Front rows rise first.\n        delay: (2 * (n - 1) - sum) * stagger + r(`d${i}-${j}`) * stagger,\n      });\n    }\n  }\n  return blocks;\n}\n\nexport function IsometricBuild({\n  grid = isometricBuildDefaults.grid,\n  maxHeight = isometricBuildDefaults.maxHeight,\n  stagger = isometricBuildDefaults.stagger,\n  seed = isometricBuildDefaults.seed,\n  speed = isometricBuildDefaults.speed,\n  background = isometricBuildDefaults.background,\n  color = isometricBuildDefaults.color,\n  accent = isometricBuildDefaults.accent,\n  floors = isometricBuildDefaults.floors,\n  gap = isometricBuildDefaults.gap,\n}: IsometricBuildProps) {\n  const inset = Math.min(0.45, Math.max(0, gap)) / 2;\n  const frame = useCurrentFrame();\n  const { width, height, fps } = useVideoConfig();\n  const n = Math.max(1, Math.round(grid));\n  const blocks = useMemo(\n    () => makeBlocks(n, maxHeight, seed, stagger),\n    [n, maxHeight, seed, stagger],\n  );\n\n  const tallest = maxHeight * 1.35;\n  // Fit the diamond plus the tallest tower into the frame.\n  const a = Math.min(\n    (width * 0.64) / (2 * n),\n    (height * 0.8) / (2 * n * ISO_Y + tallest * ISO_Z),\n  );\n  const b = a * ISO_Y;\n  const hz = a * ISO_Z;\n  const top = (height - (2 * n * b + tallest * hz)) / 2 + tallest * hz;\n  const cx = width / 2;\n  const P = (u: number, v: number, z: number) =>\n    `${fmt(cx + (u - v) * a)} ${fmt(top + (u + v) * b - z * hz)}`;\n  const poly = (...pts: string[]) => `M ${pts.join(\" L \")} Z`;\n\n  const unit = Math.min(width, height) / 720;\n  const time = frame * speed;\n\n  let ground = \"\";\n  for (let k = 0; k <= n; k++) {\n    ground += `M ${P(k, 0, 0)} L ${P(k, n, 0)} M ${P(0, k, 0)} L ${P(n, k, 0)} `;\n  }\n\n  return (\n    <AbsoluteFill style={{ backgroundColor: background }}>\n      <svg\n        width=\"100%\"\n        height=\"100%\"\n        viewBox={`0 0 ${width} ${height}`}\n        role=\"img\"\n        aria-label=\"Isometric city build\"\n        style={{ display: \"block\" }}\n      >\n        <path\n          d={ground}\n          fill=\"none\"\n          stroke={color}\n          strokeOpacity={0.12}\n          strokeWidth={unit}\n        />\n        {blocks.map((block) => {\n          const s = spring({\n            frame: time - block.delay,\n            fps,\n            config: { damping: 11, stiffness: 140, mass: 0.7 },\n          });\n          const opacity = interpolate(s, [0, 0.15], [0, 1], {\n            extrapolateLeft: \"clamp\",\n            extrapolateRight: \"clamp\",\n          });\n          if (opacity <= 0) return null;\n          const h = Math.max(0.02, block.height * s);\n          const u0 = block.i + inset;\n          const u1 = block.i + 1 - inset;\n          const v0 = block.j + inset;\n          const v1 = block.j + 1 - inset;\n          const fill = block.accent ? accent : color;\n          const left = poly(\n            P(u0, v1, 0),\n            P(u1, v1, 0),\n            P(u1, v1, h),\n            P(u0, v1, h),\n          );\n          const right = poly(\n            P(u1, v0, 0),\n            P(u1, v1, 0),\n            P(u1, v1, h),\n            P(u1, v0, h),\n          );\n          const roof = poly(\n            P(u0, v0, h),\n            P(u1, v0, h),\n            P(u1, v1, h),\n            P(u0, v1, h),\n          );\n          let lines = \"\";\n          if (floors) {\n            for (let z = FLOOR; z < h - FLOOR * 0.5; z += FLOOR) {\n              lines += `M ${P(u0, v1, z)} L ${P(u1, v1, z)} L ${P(u1, v0, z)} `;\n            }\n          }\n          return (\n            <g key={`${block.i}-${block.j}`} opacity={opacity}>\n              <path d={left} fill={fill} />\n              <path d={left} fill={background} fillOpacity={SHADE_LEFT} />\n              <path d={right} fill={fill} />\n              <path d={right} fill={background} fillOpacity={SHADE_RIGHT} />\n              {lines ? (\n                <path\n                  d={lines}\n                  fill=\"none\"\n                  stroke={background}\n                  strokeOpacity={0.35}\n                  strokeWidth={unit * 1.2}\n                />\n              ) : null}\n              <path\n                d={roof}\n                fill={fill}\n                stroke={background}\n                strokeOpacity={0.5}\n                strokeWidth={unit}\n                strokeLinejoin=\"round\"\n              />\n            </g>\n          );\n        })}\n      </svg>\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/isometric-build.tsx"
    }
  ],
  "type": "registry:component"
}
