{
  "name": "image-expand",
  "type": "registry:block",
  "description": "Thumbnail expands fullscreen",
  "dependencies": [
    "remotion"
  ],
  "registryDependencies": [
    "timing"
  ],
  "composition": {
    "id": "ImageExpand",
    "component": "ImageExpand",
    "durationInFrames": 120,
    "fps": 30,
    "width": 1920,
    "height": 1080,
    "importPath": "@/compositions/image-expand/index"
  },
  "files": [
    {
      "path": "registry/bases/default/compositions/image-expand/index.tsx",
      "type": "registry:block",
      "content": "import { AbsoluteFill, interpolate, useCurrentFrame, useVideoConfig } from \"remotion\";\nimport { EASING_ENTER, EASING_EXIT } from \"@/remotion/lib/timing\";\n\nexport type ImageExpandProps = {\n  accentColor?: string;\n};\n\nexport const ImageExpand: React.FC<ImageExpandProps> = ({\n  accentColor = \"#e8b86d\",\n}) => {\n  const frame = useCurrentFrame();\n  const { fps, width, height } = useVideoConfig();\n\n  // Enter: the frame expands from a small card to fill the canvas.\n  const expand = interpolate(frame, [20, 70], [0, 1], {\n    easing: EASING_ENTER,\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n\n  // Hold-with-life: once expanded, the glow breathes gently instead of\n  // sitting frozen while we wait for the exit beat to begin.\n  const breathe = interpolate(\n    Math.sin((frame / fps) * Math.PI * 1.1),\n    [-1, 1],\n    [0.85, 1],\n  );\n  const holdEnvelope = interpolate(frame, [70, 85, 95, 110], [0, 1, 1, 0], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n\n  // Exit: the frame settles inward and fades, closing the beat out. Opacity\n  // fades linearly across the window so the beat reads clearly well before\n  // the end (an ease-in curve would keep it near-invisible until the last\n  // few frames); the shape settle keeps its ease-in curve for feel.\n  const exitWindowStart = 90;\n  const exitWindowEnd = 115;\n  const exit = interpolate(frame, [exitWindowStart, exitWindowEnd], [0, 1], {\n    easing: EASING_EXIT,\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n  const exitOpacityProgress = interpolate(frame, [exitWindowStart, exitWindowEnd], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n\n  const w = interpolate(expand, [0, 1], [width * 0.32, width]);\n  const h = interpolate(expand, [0, 1], [height * 0.28, height]);\n  const radius = interpolate(expand, [0, 1], [24, 0]) + exit * 32;\n  const scale = 1 - exit * 0.06;\n  const opacity = 1 - exitOpacityProgress;\n  const glowPulse = interpolate(breathe, [0.85, 1], [1, 1.25]);\n  const glowStrength = 60 * expand * (1 + holdEnvelope * (glowPulse - 1));\n\n  return (\n    <AbsoluteFill style={{ background: \"#080810\", display: \"grid\", placeItems: \"center\" }}>\n      <div\n        style={{\n          width: w,\n          height: h,\n          borderRadius: radius,\n          background: `linear-gradient(135deg, ${accentColor}44, rgba(45,212,191,0.2))`,\n          border: `1px solid ${accentColor}55`,\n          boxShadow: `0 0 ${glowStrength}px ${accentColor}33`,\n          opacity,\n          transform: `scale(${scale})`,\n        }}\n      />\n    </AbsoluteFill>\n  );\n};\n"
    }
  ],
  "atlas": {
    "lane": "reels",
    "drive": "media",
    "tier": "advanced"
  }
}