{
  "name": "bento-pan",
  "type": "registry:block",
  "description": "Diagonal bento grid pan",
  "dependencies": [
    "remotion"
  ],
  "registryDependencies": [],
  "composition": {
    "id": "BentoPan",
    "component": "BentoPan",
    "durationInFrames": 180,
    "fps": 30,
    "width": 1920,
    "height": 1080,
    "importPath": "@/compositions/bento-pan/index"
  },
  "files": [
    {
      "path": "registry/bases/default/compositions/bento-pan/index.tsx",
      "type": "registry:block",
      "content": "import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from \"remotion\";\nimport { EASING_EXIT } from \"@/remotion/lib/timing\";\n\nexport type BentoTile = {\n  w: number;\n  h: number;\n  color: string;\n};\n\nexport type BentoPanProps = {\n  backgroundColor?: string;\n  tiles?: BentoTile[];\n};\n\nconst DEFAULT_TILES: BentoTile[] = [\n  { w: 32, h: 28, color: \"rgba(232,184,109,0.2)\" },\n  { w: 28, h: 36, color: \"rgba(45,212,191,0.18)\" },\n  { w: 36, h: 30, color: \"rgba(244,114,182,0.16)\" },\n  { w: 30, h: 34, color: \"rgba(245,158,11,0.18)\" },\n];\n\nexport const BentoPan: React.FC<BentoPanProps> = ({\n  backgroundColor = \"#080810\",\n  tiles = DEFAULT_TILES,\n}) => {\n  const frame = useCurrentFrame();\n  const { fps, width, height } = useVideoConfig();\n\n  // Enter + hold-with-life: the camera keeps drifting across the grid for\n  // the whole hold instead of stopping dead once the initial pan lands.\n  const panProgress = interpolate(frame, [0, 130], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n  const panX = panProgress * -width * 0.26;\n  const panY = panProgress * -height * 0.14;\n\n  // A slow breathing rotation keeps the hold visually alive between the pan\n  // settling and the exit beat kicking in.\n  const breathe = Math.sin((frame / fps) * Math.PI * 0.6) * 0.6;\n\n  // Exit: the grid settles a touch closer and fades through the vignette\n  // rather than freezing on the final pan position. Opacity fades linearly\n  // so the beat reads clearly through the whole window (an ease-in curve\n  // here would keep it near-invisible until the last few frames), and it\n  // fully resolves a few frames before the composition ends.\n  const exitWindowStart = 130;\n  const exitWindowEnd = 172;\n  const exit = interpolate(frame, [exitWindowStart, exitWindowEnd], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n  const exitScale = interpolate(exit, [0, 1], [1, 1.08], {\n    easing: EASING_EXIT,\n  });\n  const exitOpacity = 1 - exit;\n\n  return (\n    <AbsoluteFill style={{ background: backgroundColor, overflow: \"hidden\" }}>\n      <div\n        style={{\n          position: \"absolute\",\n          inset: \"-20%\",\n          display: \"grid\",\n          gridTemplateColumns: \"repeat(4, 1fr)\",\n          gap: 24,\n          opacity: exitOpacity,\n          transform: `translate(${panX}px, ${panY}px) scale(${exitScale}) rotate(${-6 + breathe}deg)`,\n        }}\n      >\n        {Array.from({ length: 16 }, (_, i) => {\n          const tile = tiles[i % tiles.length]!;\n          const enter = spring({\n            frame: frame - i * 3,\n            fps,\n            config: { damping: 20, stiffness: 100, mass: 0.85 },\n            durationInFrames: 24,\n          });\n          return (\n            <div\n              key={i}\n              style={{\n                width: tile.w * 8,\n                height: tile.h * 8,\n                borderRadius: 20,\n                background: tile.color,\n                border: \"1px solid rgba(255,255,255,0.08)\",\n                opacity: enter,\n                transform: `scale(${0.92 + enter * 0.08})`,\n              }}\n            />\n          );\n        })}\n      </div>\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 0,\n          background:\n            \"radial-gradient(ellipse at center, transparent 35%, rgba(8,8,16,0.85) 100%)\",\n        }}\n      />\n    </AbsoluteFill>\n  );\n};\n"
    }
  ],
  "atlas": {
    "lane": "reels",
    "drive": "time",
    "tier": "advanced"
  }
}