{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "perspective-squeeze",
  "title": "Perspective Squeeze",
  "description": "Two lines of heavy type endlessly trade height while corner-pinned perspective bends the flat stack into receding planes.",
  "dependencies": ["remotion"],
  "files": [
    {
      "path": "registry/remocn/perspective-squeeze/index.tsx",
      "content": "\"use client\";\n\nimport { Easing, interpolate, useCurrentFrame, useVideoConfig } from \"remotion\";\n\ntype Corners = [\n  [number, number],\n  [number, number],\n  [number, number],\n  [number, number],\n];\n\nexport interface PerspectiveSqueezeProps {\n  className?: string;\n  width?: number;\n  height?: number;\n  blockWidth?: number;\n  blockHeight?: number;\n  lowerBlockPosition?: [number, number];\n  upperBlockPosition?: [number, number];\n  cornersRest?: Corners;\n  cornerKeyframes?: number[];\n  upperCornersMid?: Corners;\n  upperCornersEnd?: Corners;\n  lowerCornersMid?: Corners;\n  lowerCornersEnd?: Corners;\n  upperText?: string;\n  lowerText?: string;\n  upperFontFamily?: string;\n  lowerFontFamily?: string;\n  upperFontSize?: number;\n  lowerFontSize?: number;\n  capRatio?: number;\n  lineGap?: number;\n  scaleKeyframes?: number[];\n  lowerScaleY?: number[];\n  upperScaleY?: number[];\n  fill?: string;\n}\n\nfunction cornerPinMatrix(src: Corners, dst: Corners): string {\n  const a: number[][] = [];\n  const rhs: number[] = [];\n\n  for (let i = 0; i < 4; i++) {\n    const [x, y] = src[i];\n    const [u, v] = dst[i];\n    a.push([x, y, 1, 0, 0, 0, -u * x, -u * y]);\n    rhs.push(u);\n    a.push([0, 0, 0, x, y, 1, -v * x, -v * y]);\n    rhs.push(v);\n  }\n\n  const n = 8;\n  for (let col = 0; col < n; col++) {\n    let pivot = col;\n    for (let r = col + 1; r < n; r++) {\n      if (Math.abs(a[r][col]) > Math.abs(a[pivot][col])) pivot = r;\n    }\n    [a[col], a[pivot]] = [a[pivot], a[col]];\n    [rhs[col], rhs[pivot]] = [rhs[pivot], rhs[col]];\n\n    const d = a[col][col];\n    if (Math.abs(d) < 1e-12) continue;\n    for (let c = col; c < n; c++) a[col][c] /= d;\n    rhs[col] /= d;\n\n    for (let r = 0; r < n; r++) {\n      if (r === col) continue;\n      const f = a[r][col];\n      if (f === 0) continue;\n      for (let c = col; c < n; c++) a[r][c] -= f * a[col][c];\n      rhs[r] -= f * rhs[col];\n    }\n  }\n\n  const [m0, m1, m2, m3, m4, m5, m6, m7] = rhs;\n  return `matrix3d(${m0}, ${m3}, 0, ${m6}, ${m1}, ${m4}, 0, ${m7}, 0, 0, 1, 0, ${m2}, ${m5}, 0, 1)`;\n}\n\nfunction lerpCorners(from: Corners, to: Corners, t: number): Corners {\n  return from.map((p, i) => [\n    p[0] + (to[i][0] - p[0]) * t,\n    p[1] + (to[i][1] - p[1]) * t,\n  ]) as Corners;\n}\n\nexport function PerspectiveSqueeze({\n  className,\n  width = 1920,\n  height = 1080,\n  blockWidth = 990,\n  blockHeight = 546,\n  lowerBlockPosition = [960, 860],\n  upperBlockPosition = [960, 314],\n  cornersRest = [\n    [0, 0],\n    [990, 0],\n    [0, 546],\n    [990, 546],\n  ],\n  cornerKeyframes = [0, 45, 70],\n  upperCornersMid = [\n    [0, -326],\n    [990, 0],\n    [0, 546],\n    [990, 546],\n  ],\n  upperCornersEnd = [\n    [0, 352],\n    [990, -428],\n    [460, 546],\n    [990, 546],\n  ],\n  lowerCornersMid = [\n    [0, 0],\n    [990, 0],\n    [0, 225],\n    [990, 546],\n  ],\n  lowerCornersEnd = [\n    [0, 0],\n    [990, 0],\n    [0, 897],\n    [990, 116],\n  ],\n  upperText = \"REMOCN\",\n  lowerText = \"BEST\",\n  upperFontFamily = \"sans-serif\",\n  lowerFontFamily = \"sans-serif\",\n  upperFontSize = 603,\n  lowerFontSize = 124,\n  capRatio = 0.65,\n  lineGap = 50,\n  scaleKeyframes = [0, 60, 118, 161, 196, 225],\n  lowerScaleY = [100, 384.8, 270, 330, 150, 100],\n  upperScaleY = [100, 35.8, 64, 45, 110, 100],\n  fill = \"#ffffff\",\n}: PerspectiveSqueezeProps) {\n  const frame = useCurrentFrame();\n  const { width: compWidth, height: compHeight } = useVideoConfig();\n  const stageScale = Math.min(compWidth / width, compHeight / height);\n\n  const squishEase = Easing.bezier(0.6, 0, 0.4, 1);\n\n  const scaleAt = (track: number[]) =>\n    interpolate(frame, scaleKeyframes, track, {\n      extrapolateLeft: \"clamp\",\n      extrapolateRight: \"clamp\",\n      easing: squishEase,\n    }) / 100;\n\n  const cornersAt = (mid: Corners, end: Corners): Corners => {\n    const [k0, k1, k2] = cornerKeyframes;\n    if (frame <= k0) return cornersRest;\n    if (frame >= k2) return end;\n    if (frame <= k1) {\n      return lerpCorners(\n        cornersRest,\n        mid,\n        squishEase((frame - k0) / Math.max(k1 - k0, 1)),\n      );\n    }\n    return lerpCorners(\n      mid,\n      end,\n      squishEase((frame - k1) / Math.max(k2 - k1, 1)),\n    );\n  };\n\n  const upperScale = scaleAt(upperScaleY);\n  const lowerScale = scaleAt(lowerScaleY);\n\n  const upperBoxHeight = upperFontSize * capRatio * upperScale;\n  const lowerBoxHeight = lowerFontSize * capRatio * lowerScale;\n  const stackTop =\n    (blockHeight - (upperBoxHeight + lineGap + lowerBoxHeight)) / 2;\n  const upperBaseline = stackTop + upperBoxHeight;\n  const lowerBaseline = upperBaseline + lineGap + lowerBoxHeight;\n\n  const line = (\n    text: string,\n    family: string,\n    size: number,\n    baseline: number,\n    scale: number,\n  ) => (\n    <text\n      x={0}\n      y={baseline}\n      textLength={blockWidth}\n      lengthAdjust=\"spacingAndGlyphs\"\n      fontFamily={family}\n      fontSize={size}\n      fontWeight={800}\n      fill={fill}\n      transform={`matrix(1, 0, 0, ${scale}, 0, ${baseline * (1 - scale)})`}\n    >\n      {text}\n    </text>\n  );\n\n  const block = (corners: Corners, position: [number, number]) => (\n    <div\n      style={{\n        position: \"absolute\",\n        left: position[0] - blockWidth / 2,\n        top: position[1] - blockHeight / 2,\n        width: blockWidth,\n        height: blockHeight,\n        transformOrigin: \"0 0\",\n        transform: cornerPinMatrix(cornersRest, corners),\n      }}\n    >\n      <svg\n        width={blockWidth}\n        height={blockHeight}\n        viewBox={`0 0 ${blockWidth} ${blockHeight}`}\n        style={{ display: \"block\", overflow: \"hidden\" }}\n      >\n        {line(\n          upperText,\n          upperFontFamily,\n          upperFontSize,\n          upperBaseline,\n          upperScale,\n        )}\n        {line(\n          lowerText,\n          lowerFontFamily,\n          lowerFontSize,\n          lowerBaseline,\n          lowerScale,\n        )}\n      </svg>\n    </div>\n  );\n\n  return (\n    <div\n      className={className}\n      style={{ position: \"absolute\", inset: 0, overflow: \"hidden\" }}\n    >\n      <div\n        style={{\n          position: \"absolute\",\n          left: (compWidth - width * stageScale) / 2,\n          top: (compHeight - height * stageScale) / 2,\n          width,\n          height,\n          scale: `${stageScale}`,\n          transformOrigin: \"0 0\",\n        }}\n      >\n        {block(cornersAt(upperCornersMid, upperCornersEnd), upperBlockPosition)}\n        {block(cornersAt(lowerCornersMid, lowerCornersEnd), lowerBlockPosition)}\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/perspective-squeeze.tsx"
    }
  ],
  "type": "registry:component"
}
