{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "absorb-stack",
  "title": "Absorb Stack",
  "description": "A centred stack of small lines, ending in a red \"on top.\", on a dark brown ground.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/absorb-stack/index.tsx",
      "content": "\"use client\";\n\nimport { loadVariableFont } from \"@remotion/google-fonts/Jost\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nconst jost = loadVariableFont(\"normal\", { subsets: [\"latin\"] });\n\nexport interface AbsorbStackProps {\n  lines?: string[];\n  accentLine?: number;\n  accent?: string;\n  background?: string;\n  speed?: number;\n  theme?: \"dark\" | \"light\";\n}\n\nconst THEMES = {\n  dark: { bg: \"#2A1C17\", ink: \"#E8C9B0\", accent: \"#C8102E\" },\n  light: { bg: \"#EAD3C3\", ink: \"#2A1C17\", accent: \"#C8102E\" },\n} as const;\n\nconst LEADING = 1.02;\nconst WEIGHT = 400;\nconst ENTER_LEN = 12;\nconst SETTLE_AT = 14;\nconst RING_GAP = 5;\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\nconst EASE_OUT = Easing.bezier(0.22, 1, 0.36, 1);\n\nexport function AbsorbStack({\n  lines = [\"made to\", \"sink into\", \"your cut,\", \"not sit\", \"on top.\"],\n  accentLine,\n  accent,\n  background,\n  speed = 1,\n  theme = \"dark\",\n}: AbsorbStackProps) {\n  const frame = useCurrentFrame() * speed;\n  const { width, height, fps } = useVideoConfig();\n  const c = THEMES[theme];\n  const hot = accent ?? c.accent;\n  const bg = background ?? c.bg;\n  const n = Math.max(1, lines.length);\n  const hotIndex = accentLine ?? n - 1;\n\n  const fs = Math.min(height * 0.125, width * 0.1);\n  const pitch = fs * LEADING;\n  const mid = (n - 1) / 2;\n  const centreY = height * 0.5;\n  const spreadPitch = n > 1 ? (height * 0.84) / (n - 1) : 0;\n\n  return (\n    <AbsoluteFill style={{ background: bg, overflow: \"hidden\" }}>\n      {lines.map((line, i) => {\n        const offset = i - mid;\n        const ring = Math.abs(offset);\n        const enter = interpolate(\n          frame,\n          [ring * 3, ring * 3 + ENTER_LEN],\n          [0, 1],\n          {\n            ...CLAMP,\n            easing: EASE_OUT,\n          },\n        );\n        const settle = spring({\n          frame: frame - SETTLE_AT - ring * RING_GAP,\n          fps,\n          config: { damping: 20, stiffness: 70, mass: 1 },\n        });\n        const from = centreY + offset * spreadPitch;\n        const to = centreY + offset * pitch;\n        const y = from + (to - from) * settle;\n        const drift = interpolate(\n          enter,\n          [0, 1],\n          [offset >= 0 ? 0.25 : -0.25, 0],\n        );\n        const sat = interpolate(settle, [0, 1], [72, 100], CLAMP);\n        const ink = i === hotIndex ? hot : c.ink;\n        return (\n          <div\n            key={`${i}-${line}`}\n            style={{\n              position: \"absolute\",\n              left: 0,\n              right: 0,\n              top: y - pitch / 2,\n              height: pitch,\n              display: \"flex\",\n              alignItems: \"center\",\n              justifyContent: \"center\",\n              fontFamily: jost.fontFamily,\n              fontWeight: WEIGHT,\n              fontSize: fs,\n              lineHeight: 1,\n              letterSpacing: \"-0.025em\",\n              textTransform: \"lowercase\",\n              whiteSpace: \"pre\",\n              color: `color-mix(in oklab, ${ink} ${sat.toFixed(1)}%, ${bg})`,\n              opacity: enter,\n              transform: `translateY(${(drift * fs).toFixed(2)}px)`,\n            }}\n          >\n            {line}\n          </div>\n        );\n      })}\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/absorb-stack.tsx"
    }
  ],
  "type": "registry:component"
}
