{
  "name": "mesh-gradient-bg",
  "type": "registry:ui",
  "description": "Living mesh gradient blob background",
  "dependencies": [
    "remotion"
  ],
  "registryDependencies": [
    "motion-tokens"
  ],
  "files": [
    {
      "path": "registry/bases/default/primitives/mesh-gradient-bg.tsx",
      "type": "registry:ui",
      "content": "import { AbsoluteFill, interpolate, useCurrentFrame, useVideoConfig } from \"remotion\";\nimport { EASING } from \"@/remotion/lib/motion-tokens\";\n\nexport type MeshGradientBgProps = {\n  backgroundColor?: string;\n  /** Blob accent colors — solid hex, blended additively over the stage. */\n  colors?: [string, string, string];\n  intensity?: number;\n};\n\nconst DEFAULT_COLORS: [string, string, string] = [\"#e8b86d\", \"#2dd4bf\", \"#f472b6\"];\n\ntype BlobConfig = {\n  x: number;\n  y: number;\n  size: number;\n  driftX: number;\n  driftY: number;\n  phase: number;\n  period: number;\n  blur: number;\n  alpha: number;\n};\n\nconst BLOBS: BlobConfig[] = [\n  { x: 20, y: 30, size: 60, driftX: 12, driftY: 9, phase: 0, period: 2.6, blur: 60, alpha: 0.55 },\n  { x: 78, y: 22, size: 48, driftX: -10, driftY: 14, phase: 1.7, period: 3.3, blur: 46, alpha: 0.46 },\n  { x: 58, y: 78, size: 54, driftX: 9, driftY: -12, phase: 3.4, period: 2.9, blur: 54, alpha: 0.5 },\n];\n\nexport const MeshGradientBg: React.FC<MeshGradientBgProps> = ({\n  backgroundColor = \"#080810\",\n  colors = DEFAULT_COLORS,\n  intensity = 1,\n}) => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n  const time = frame / fps;\n\n  // Continuous breathing scale — driven by a sine so it loops with no seam,\n  // unlike a clamped ramp that would freeze once it reached its endpoint.\n  const breathe = interpolate(Math.sin(time / 5.5), [-1, 1], [0, 1], {\n    easing: EASING.editorial,\n  });\n\n  return (\n    <AbsoluteFill style={{ background: backgroundColor, overflow: \"hidden\" }}>\n      <AbsoluteFill style={{ transform: `scale(${1 + breathe * 0.03})` }}>\n        {BLOBS.map((blob, index) => {\n          const wave = Math.sin(time / blob.period + blob.phase);\n          const waveY = Math.cos(time / (blob.period * 1.3) + blob.phase * 0.7);\n          const x = blob.x + wave * blob.driftX * intensity;\n          const y = blob.y + waveY * blob.driftY * intensity;\n          const scale = 0.94 + breathe * 0.1 + wave * 0.05;\n          const color = colors[index % colors.length];\n\n          return (\n            <div\n              key={index}\n              style={{\n                position: \"absolute\",\n                left: `${x}%`,\n                top: `${y}%`,\n                width: `${blob.size}%`,\n                aspectRatio: \"1\",\n                transform: `translate(-50%, -50%) scale(${scale})`,\n                borderRadius: \"50%\",\n                background: `radial-gradient(circle at 42% 38%, ${color}, transparent 70%)`,\n                filter: `blur(${blob.blur}px)`,\n                opacity: blob.alpha * (0.85 + wave * 0.15),\n                mixBlendMode: \"screen\",\n              }}\n            />\n          );\n        })}\n      </AbsoluteFill>\n      <AbsoluteFill\n        style={{\n          background: \"radial-gradient(circle at 50% 115%, rgba(0,0,0,0.5), transparent 60%)\",\n          pointerEvents: \"none\",\n        }}\n      />\n    </AbsoluteFill>\n  );\n};\n"
    }
  ],
  "atlas": {
    "lane": "atoms",
    "drive": "time",
    "tier": "advanced",
    "tags": [
      "background"
    ]
  }
}