{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "centered-word-build",
  "title": "Centered Word Build",
  "description": "Words appear sharply from below while the centered phrase pushes cumulatively closer on every beat, then clears through a soft left-to-right opacity wipe.",
  "dependencies": ["remotion"],
  "files": [
    {
      "path": "registry/remocn/centered-word-build/index.tsx",
      "content": "\"use client\";\n\nimport { Easing, interpolate, useCurrentFrame } from \"remotion\";\n\nexport interface CenteredWordBuildProps {\n  text: string;\n  fontSize?: number;\n  color?: string;\n  fontWeight?: number;\n  wordGap?: number;\n  accel?: number;\n  rise?: number;\n  zoomStep?: number;\n  settleFrames?: number;\n  exitAt?: number;\n  exitFrames?: number;\n  speed?: number;\n  className?: string;\n}\n\nfunction revealFrame(index: number, wordGap: number, accel: number): number {\n  let frame = 0;\n\n  for (let step = 1; step <= index; step++) {\n    // Establish two even beats before tightening the cadence.\n    frame += wordGap * accel ** Math.max(step - 2, 0);\n  }\n\n  return Math.round(frame);\n}\n\nexport function centeredWordBuildLength(\n  text: string,\n  wordGap = 11,\n  accel = 0.75,\n  settleFrames = 9,\n  exitAt = 51,\n  exitFrames = 12,\n): number {\n  const words = text.trim().split(/\\s+/).filter(Boolean);\n  if (words.length === 0) return 0;\n\n  const lastReveal = revealFrame(words.length - 1, wordGap, accel);\n  return Math.max(lastReveal + settleFrames, exitAt + exitFrames);\n}\n\nconst settleEasing = Easing.bezier(0.16, 1, 0.3, 1);\n\nexport function CenteredWordBuild({\n  text,\n  fontSize = 52,\n  color = \"#fff3df\",\n  fontWeight = 400,\n  wordGap = 11,\n  accel = 0.75,\n  rise = 0.18,\n  zoomStep = 0.035,\n  settleFrames = 9,\n  exitAt = 51,\n  exitFrames = 12,\n  speed = 1,\n  className,\n}: CenteredWordBuildProps) {\n  const frame = useCurrentFrame() * speed;\n  const words = text.trim().split(/\\s+/).filter(Boolean);\n\n  if (words.length === 0) return null;\n\n  const visibleCount = words.filter(\n    (_, index) => frame >= revealFrame(index, wordGap, accel),\n  ).length;\n  let cameraScale = 1;\n  for (let index = 1; index < visibleCount; index++) {\n    const revealAt = revealFrame(index, wordGap, accel);\n    cameraScale +=\n      zoomStep *\n      interpolate(\n        frame,\n        [revealAt, revealAt + Math.max(settleFrames, 0.001)],\n        [0, 1],\n        {\n          easing: settleEasing,\n          extrapolateLeft: \"clamp\",\n          extrapolateRight: \"clamp\",\n        },\n      );\n  }\n  const exitProgress = interpolate(\n    frame,\n    [exitAt, exitAt + Math.max(exitFrames, 0.001)],\n    [0, 1],\n    {\n      extrapolateLeft: \"clamp\",\n      extrapolateRight: \"clamp\",\n    },\n  );\n  const fadeStart = -35 + exitProgress * 135;\n  const fadeEnd = fadeStart + 35;\n  const exitMask = `linear-gradient(90deg, transparent ${fadeStart}%, black ${fadeEnd}%)`;\n\n  return (\n    <div\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        overflow: \"hidden\",\n        background: \"transparent\",\n      }}\n    >\n      <span\n        className={className}\n        style={{\n          color,\n          display: \"inline-block\",\n          fontFamily:\n            \"var(--font-geist-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif)\",\n          fontSize,\n          fontWeight,\n          letterSpacing: \"-0.02em\",\n          lineHeight: 1.1,\n          whiteSpace: \"nowrap\",\n          scale: `${cameraScale}`,\n          opacity: exitProgress >= 1 ? 0 : 1,\n          maskImage: exitProgress > 0 ? exitMask : undefined,\n          WebkitMaskImage: exitProgress > 0 ? exitMask : undefined,\n          maskRepeat: \"no-repeat\",\n          WebkitMaskRepeat: \"no-repeat\",\n        }}\n      >\n        {words.slice(0, visibleCount).map((word, index) => {\n          const revealAt = revealFrame(index, wordGap, accel);\n          const y = interpolate(\n            frame,\n            [revealAt, revealAt + Math.max(settleFrames, 0.001)],\n            [rise * fontSize, 0],\n            {\n              easing: settleEasing,\n              extrapolateLeft: \"clamp\",\n              extrapolateRight: \"clamp\",\n            },\n          );\n\n          return (\n            <span\n              key={index}\n              style={{\n                display: \"inline-block\",\n                marginLeft: index > 0 ? \"0.25em\" : undefined,\n                translate: `0 ${y}px`,\n              }}\n            >\n              {word}\n            </span>\n          );\n        })}\n      </span>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/centered-word-build.tsx"
    }
  ],
  "type": "registry:component"
}
