{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "carousel-circle",
  "type": "registry:block",
  "title": "Carousel circle",
  "description": "Carousel circle",
  "files": [
    {
      "path": "components/usages/carouselcircleusage.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nimport CarouselCircle from \"@/registry/open-source/carousel-circle\";\n\nexport default function Usage() {\n\treturn (\n\t\t<div className=\"h-screen w-full flex items-center justify-center relative overflow-hidden bg-background\">\n\t\t\t<CarouselCircle\n\t\t\t\timages={[\n\t\t\t\t\t\"/itjustworks.jpg\",\n\t\t\t\t\t\"/itjustworks.jpg\",\n\t\t\t\t\t\"/itjustworks.jpg\",\n\t\t\t\t\t\"/itjustworks.jpg\",\n\t\t\t\t\t\"/itjustworks.jpg\",\n\t\t\t\t\t\"/itjustworks.jpg\",\n\t\t\t\t]}\n\t\t\t/>{\" \"}\n\t\t</div>\n\t);\n}\n",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/carouselcircleusage.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nimport CarouselCircle from \"@/registry/open-source/carousel-circle\";\n\nexport default function Usage() {\n\treturn (\n\t\t<div className=\"h-screen w-full flex items-center justify-center relative overflow-hidden bg-background\">\n\t\t\t<CarouselCircle\n\t\t\t\timages={[\n\t\t\t\t\t\"/itjustworks.jpg\",\n\t\t\t\t\t\"/itjustworks.jpg\",\n\t\t\t\t\t\"/itjustworks.jpg\",\n\t\t\t\t\t\"/itjustworks.jpg\",\n\t\t\t\t\t\"/itjustworks.jpg\",\n\t\t\t\t\t\"/itjustworks.jpg\",\n\t\t\t\t]}\n\t\t\t/>{\" \"}\n\t\t</div>\n\t);\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/carousel-circle.tsx",
      "content": "// \"use client\";\r\n\r\n// import React, { useEffect, useState } from \"react\";\r\n\r\n// import Image from \"next/image\";\r\n\r\n// import { motion } from \"motion/react\";\r\n// import { FaArrowLeft, FaArrowRight } from \"react-icons/fa\";\r\n\r\n// Credit:\r\n// https://www.serenity-ui.com/components/carousels/carousel360\r\n\r\n// interface CarouselCircleProps {\r\n// \timages: string[];\r\n// }\r\n\r\n// const CarouselCircle: React.FC<CarouselCircleProps> = ({ images }) => {\r\n// \tconst [rotation, setRotation] = useState(0);\r\n// \tconst [centerImage, setCenterImage] = useState(images[0]);\r\n// \tconst numimages = images?.length;\r\n\r\n// \tuseEffect(() => {\r\n// \t\tconst interval = setInterval(() => {\r\n// \t\t\tsetRotation((prevRotation) => prevRotation + 360 / numimages);\r\n// \t\t}, 2000);\r\n\r\n// \t\treturn () => clearInterval(interval);\r\n// \t}, [numimages]);\r\n\r\n// \tuseEffect(() => {\r\n// \t\tconst index =\r\n// \t\t\tMath.round((rotation % 360) / (360 / numimages)) % numimages;\r\n// \t\tsetCenterImage(images[index < 0 ? numimages + index : index]);\r\n// \t}, [rotation, numimages, images]);\r\n\r\n// \tconst rotateCarousel = (direction: \"left\" | \"right\") => {\r\n// \t\tconst newRotation =\r\n// \t\t\trotation + (direction === \"left\" ? -360 / numimages : 360 / numimages);\r\n// \t\tsetRotation(newRotation);\r\n// \t};\r\n\r\n// \treturn (\r\n// \t\t<div className=\"relative h-screen w-full bg-background text-foreground overflow-hidden\">\r\n// \t\t\t{/* Carousel Container */}\r\n// \t\t\t<div className=\"absolute inset-0 flex items-center justify-center\">\r\n// \t\t\t\t<motion.div\r\n// \t\t\t\t\tclassName=\"relative w-[90vw] max-w-[600px] h-[60vw] max-h-[400px]\"\r\n// \t\t\t\t\tstyle={{ perspective: 500 }}\r\n// \t\t\t\t>\r\n// \t\t\t\t\t{images?.map((item, index) => (\r\n// \t\t\t\t\t\t<motion.div\r\n// \t\t\t\t\t\t\tkey={index + \"carousel-circle\"}\r\n// \t\t\t\t\t\t\tclassName=\"absolute top-0 left-0 w-full h-full flex items-center justify-center\"\r\n// \t\t\t\t\t\t\tstyle={{\r\n// \t\t\t\t\t\t\t\trotateY: `${rotation + (360 / numimages) * index}deg`,\r\n// \t\t\t\t\t\t\t\trotateX: `${-40}deg`,\r\n// \t\t\t\t\t\t\t\ttransformStyle: \"preserve-3d\",\r\n// \t\t\t\t\t\t\t\tbackfaceVisibility: \"hidden\",\r\n// \t\t\t\t\t\t\t}}\r\n// \t\t\t\t\t\t\tanimate={{\r\n// \t\t\t\t\t\t\t\trotateY: `${rotation + (360 / numimages) * index}deg`,\r\n// \t\t\t\t\t\t\t}}\r\n// \t\t\t\t\t\t\ttransition={{ type: \"spring\", stiffness: 70, damping: 18 }}\r\n// \t\t\t\t\t\t>\r\n// \t\t\t\t\t\t\t<div\r\n// \t\t\t\t\t\t\t\tclassName=\"rounded-xl overflow-hidden shadow-lg transform-gpu\"\r\n// \t\t\t\t\t\t\t\tstyle={{\r\n// \t\t\t\t\t\t\t\t\ttransform: `translateZ(350px) rotateY(${\r\n// \t\t\t\t\t\t\t\t\t\t-rotation - (360 / numimages) * index\r\n// \t\t\t\t\t\t\t\t\t}deg)`,\r\n// \t\t\t\t\t\t\t\t\tboxShadow: \"0px 10px 50px rgba(0, 0, 0, 0.5)\",\r\n// \t\t\t\t\t\t\t\t}}\r\n// \t\t\t\t\t\t\t>\r\n// \t\t\t\t\t\t\t\t<Image\r\n// \t\t\t\t\t\t\t\t\tsrc={item}\r\n// \t\t\t\t\t\t\t\t\talt={`Carousel Image ${index + 1}`}\r\n// \t\t\t\t\t\t\t\t\twidth={100}\r\n// \t\t\t\t\t\t\t\t\theight={100}\r\n// \t\t\t\t\t\t\t\t\tclassName=\"object-cover hover:scale-105 transition duration-500\"\r\n// \t\t\t\t\t\t\t\t/>\r\n// \t\t\t\t\t\t\t</div>\r\n// \t\t\t\t\t\t</motion.div>\r\n// \t\t\t\t\t))}\r\n// \t\t\t\t\t{/* Central Image */}\r\n// \t\t\t\t\t<motion.div\r\n// \t\t\t\t\t\tclassName=\"absolute inset-0 flex items-center justify-center z-10\"\r\n// \t\t\t\t\t\tstyle={{ transformStyle: \"preserve-3d\" }}\r\n// \t\t\t\t\t>\r\n// \t\t\t\t\t\t<div\r\n// \t\t\t\t\t\t\tclassName=\"rounded-xl overflow-hidden shadow-lg\"\r\n// \t\t\t\t\t\t\tstyle={{\r\n// \t\t\t\t\t\t\t\ttransform: `translateZ(0px) rotateX(20deg)`,\r\n// \t\t\t\t\t\t\t\tboxShadow: \"0px 10px 50px rgba(0, 0, 0, 0.5)\",\r\n// \t\t\t\t\t\t\t}}\r\n// \t\t\t\t\t\t>\r\n// \t\t\t\t\t\t\t<Image\r\n// \t\t\t\t\t\t\t\tsrc={centerImage}\r\n// \t\t\t\t\t\t\t\talt=\"Central Large Image\"\r\n// \t\t\t\t\t\t\t\twidth={400}\r\n// \t\t\t\t\t\t\t\theight={400}\r\n// \t\t\t\t\t\t\t\tclassName=\"object-cover\"\r\n// \t\t\t\t\t\t\t/>\r\n// \t\t\t\t\t\t</div>\r\n// \t\t\t\t\t</motion.div>\r\n// \t\t\t\t</motion.div>\r\n// \t\t\t</div>\r\n// \t\t\t{/* Buttons */}\r\n// \t\t\t<div className=\"absolute bottom-12 left-1/2 transform -translate-x-1/2 flex space-x-4\">\r\n// \t\t\t\t<button\r\n// \t\t\t\t\tclassName=\"bg-background/20 text-foreground px-8 py-3 rounded-full shadow-lg backdrop-blur-md border border-white/30 hover:bg-background/30 transition duration-300 ease-in-out transform hover:scale-105 focus:outline-hidden\"\r\n// \t\t\t\t\tonClick={() => rotateCarousel(\"left\")}\r\n// \t\t\t\t>\r\n// \t\t\t\t\t<FaArrowLeft className=\"text-foreground\" />\r\n// \t\t\t\t</button>\r\n// \t\t\t\t<button\r\n// \t\t\t\t\tclassName=\"bg-background/20 text-foreground px-8 py-3 rounded-full shadow-lg backdrop-blur-md border border-white/30 hover:bg-background/30 transition duration-300 ease-in-out transform hover:scale-105 focus:outline-hidden\"\r\n// \t\t\t\t\tonClick={() => rotateCarousel(\"right\")}\r\n// \t\t\t\t>\r\n// \t\t\t\t\t<FaArrowRight className=\"text-foreground \" />\r\n// \t\t\t\t</button>\r\n// \t\t\t</div>\r\n// \t\t\t{/* Overlay */}\r\n// \t\t\t<div className=\"absolute inset-0 bg-linear-to-t from-background via-transparent to-background pointer-events-none\" />\r\n// \t\t</div>\r\n// \t);\r\n// };\r\n\r\n// export default CarouselCircle;\r\n\r\n\r\n\"use client\";\r\n\r\nimport React, { useState, useEffect, useRef, useMemo } from \"react\";\r\nimport {\r\n\tanimate,\r\n\tmotion,\r\n\tuseMotionValue,\r\n\tuseTransform,\r\n} from \"motion/react\";\r\nimport { Iphone } from \"./iphone\";\r\n\r\nconst CAROUSEL_SPRING = { type: \"spring\" as const, stiffness: 70, damping: 18 };\r\nconst CENTER_CROSSFADE = { duration: 0.45, ease: [0.4, 0, 0.2, 1] as const };\r\n\r\n/** RotateZ impulse (degrees) when the ring steps — trailing twist then settles upright */\r\nconst ORBIT_BANK_PEAK = 26;\r\n\r\n/** Orbit slot at viewer yaw 0°: `rotation + i * (360/n) ≡ 0 (mod 360)` → `i ≡ -rotation/step (mod n)`. */\r\nfunction frontSlotIndex(rotation: number, n: number): number {\r\n\tif (n === 0) return 0;\r\n\tconst step = 360 / n;\r\n\tconst k = Math.round(-rotation / step);\r\n\treturn ((k % n) + n) % n;\r\n}\r\n\r\nconst Carousel360: React.FC = ({ images }) => {\r\n\tconst [rotation, setRotation] = useState(45);\r\n\tconst [autoRotateEpoch, setAutoRotateEpoch] = useState(0);\r\n\tconst [centerTopLayer, setCenterTopLayer] = useState<\"a\" | \"b\">(\"a\");\r\n\tconst [centerSrcA, setCenterSrcA] = useState(images[0].src);\r\n\tconst [centerSrcB, setCenterSrcB] = useState(images[0].src);\r\n\tconst centerPendingFlip = useRef(false);\r\n\tconst numimages = images.length;\r\n\r\n\tconst centerSrc = useMemo(() => {\r\n\t\tconst i = frontSlotIndex(rotation, numimages);\r\n\t\treturn images[i]?.src ?? images[0].src;\r\n\t}, [rotation, numimages, images]);\r\n\r\n\tconst prevRotationRef = useRef(rotation);\r\n\tconst bankPlaybackRef = useRef<{ stop: () => void } | null>(null);\r\n\tconst orbitBankMv = useMotionValue(0);\r\n\tconst orbitBankDeg = useTransform(orbitBankMv, (deg) => `${deg}deg`);\r\n\r\n\tuseEffect(() => {\r\n\t\treturn () => bankPlaybackRef.current?.stop();\r\n\t}, []);\r\n\r\n\tuseEffect(() => {\r\n\t\tconst prev = prevRotationRef.current;\r\n\t\tprevRotationRef.current = rotation;\r\n\t\tconst delta = rotation - prev;\r\n\t\tif (Math.abs(delta) < 1e-5) return;\r\n\r\n\t\tconst step = 360 / numimages;\r\n\t\tconst thrust = Math.min(Math.max(Math.abs(delta) / step, 0.55), 2.75);\r\n\r\n\t\tbankPlaybackRef.current?.stop();\r\n\r\n\t\tconst sign = Math.sign(delta) || 1;\r\n\t\tconst peak = sign * -(ORBIT_BANK_PEAK * thrust);\r\n\t\tconst from = orbitBankMv.get();\r\n\r\n\t\tbankPlaybackRef.current = animate(\r\n\t\t\torbitBankMv,\r\n\t\t\t[from, peak, peak * 0.32, sign * -(ORBIT_BANK_PEAK * 0.06 * thrust), 0],\r\n\t\t\t{\r\n\t\t\t\tduration: 1,\r\n\t\t\t\tease: [\r\n\t\t\t\t\t[0.45, 0, 0.55, 1],\r\n\t\t\t\t\t[0.28, 0.82, 0.35, 1],\r\n\t\t\t\t\t[0.45, 0, 0.2, 1],\r\n\t\t\t\t\t[0.22, 1, 0.36, 1],\r\n\t\t\t\t],\r\n\t\t\t\ttimes: [0, 0.12, 0.32, 0.62, 1],\r\n\t\t\t},\r\n\t\t);\r\n\t}, [rotation, numimages, orbitBankMv]);\r\n\r\n\t// useEffect(() => {\r\n\t//     const interval = setInterval(() => {\r\n\t//         setRotation((prevRotation) => prevRotation + 360 / numimages);\r\n\t//     }, 10000);\r\n\r\n\t//     return () => clearInterval(interval);\r\n\t// }, [numimages, autoRotateEpoch]);\r\n\r\n\tuseEffect(() => {\r\n\t\tconst topSrc = centerTopLayer === \"a\" ? centerSrcA : centerSrcB;\r\n\t\tif (centerSrc === topSrc) return;\r\n\t\tcenterPendingFlip.current = true;\r\n\t\tif (centerTopLayer === \"a\") setCenterSrcB(centerSrc);\r\n\t\telse setCenterSrcA(centerSrc);\r\n\t}, [centerSrc, centerTopLayer, centerSrcA, centerSrcB]);\r\n\r\n\tconst onCenterLayerReady = (layer: \"a\" | \"b\") => {\r\n\t\tif (!centerPendingFlip.current) return;\r\n\t\tconst hiddenWhenATop = centerTopLayer === \"a\" ? \"b\" : \"a\";\r\n\t\tif (layer !== hiddenWhenATop) return;\r\n\t\tconst hiddenSrc = layer === \"a\" ? centerSrcA : centerSrcB;\r\n\t\tif (hiddenSrc !== centerSrc) return;\r\n\t\tcenterPendingFlip.current = false;\r\n\t\tsetCenterTopLayer((t) => (t === \"a\" ? \"b\" : \"a\"));\r\n\t};\r\n\r\n\tconst rotateCarousel = (direction: \"left\" | \"right\") => {\r\n\t\tconst newRotation =\r\n\t\t\trotation + (direction === \"left\" ? -360 / numimages : 360 / numimages);\r\n\t\tsetRotation(newRotation);\r\n\t\tsetAutoRotateEpoch((n) => n + 1);\r\n\t};\r\n\r\n\treturn (\r\n\t\t<div className=\"relative pb-56 pt-24 w-full  overflow-hidden\">\r\n\t\t\t{/* Carousel Container */}\r\n\t\t\t<div className=\" flex items-center justify-center\">\r\n\t\t\t\t<motion.div\r\n\t\t\t\t\tclassName=\"relative w-[90vw] max-w-[600px] h-[60vw] max-h-[400px]\"\r\n\t\t\t\t\tstyle={{ perspective: 800 }}\r\n\t\t\t\t>\r\n\t\t\t\t\t{images.map(({ src, key }, index) => {\r\n\t\t\t\t\t\tconst slotDeg = rotation + (360 / numimages) * index;\r\n\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t<motion.div\r\n\t\t\t\t\t\t\t\tkey={key}\r\n\t\t\t\t\t\t\t\tclassName=\"absolute top-0 left-0 w-full h-full flex items-center justify-center\"\r\n\t\t\t\t\t\t\t\tstyle={{\r\n\t\t\t\t\t\t\t\t\trotateX: `${-40}deg`,\r\n\t\t\t\t\t\t\t\t\ttransformStyle: \"preserve-3d\",\r\n\t\t\t\t\t\t\t\t\tbackfaceVisibility: \"hidden\",\r\n\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\tanimate={{\r\n\t\t\t\t\t\t\t\t\trotateY: `${slotDeg}deg`,\r\n\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\ttransition={CAROUSEL_SPRING}\r\n\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t<motion.div\r\n\t\t\t\t\t\t\t\t\tclassName=\"relative shrink-0 overflow-hidden rounded-xl transform-gpu h-[min(112px,22vw)] w-[calc(min(112px,22vw)*9/16)]\"\r\n\t\t\t\t\t\t\t\t\tstyle={{\r\n\t\t\t\t\t\t\t\t\t\ttransformStyle: \"preserve-3d\",\r\n\t\t\t\t\t\t\t\t\t\ttranslateZ: 350,\r\n\t\t\t\t\t\t\t\t\t\trotateZ: orbitBankDeg,\r\n\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\tanimate={{\r\n\t\t\t\t\t\t\t\t\t\trotateY: `${-slotDeg}deg`,\r\n\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\ttransition={CAROUSEL_SPRING}\r\n\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t<video\r\n\t\t\t\t\t\t\t\t\t\tsrc={src}\r\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-full w-full object-cover hover:scale-105 transition duration-500 pointer-events-none\"\r\n\t\t\t\t\t\t\t\t\t\tmuted\r\n\t\t\t\t\t\t\t\t\t\tplaysInline\r\n\t\t\t\t\t\t\t\t\t\tloop\r\n\t\t\t\t\t\t\t\t\t\tautoPlay\r\n\t\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t\t</motion.div>\r\n\t\t\t\t\t\t\t</motion.div>\r\n\t\t\t\t\t\t);\r\n\t\t\t\t\t})}\r\n\t\t\t\t\t{/* Central video: fixed box + A/B crossfade avoids size flicker on src change */}\r\n\t\t\t\t\t<motion.div\r\n\t\t\t\t\t\tclassName=\"absolute inset-0 flex items-center justify-center z-10 px-5 sm:px-0\"\r\n\t\t\t\t\t\tstyle={{ transformStyle: \"preserve-3d\" }}\r\n\t\t\t\t\t>\r\n\t\t\t\t\t\t{/* YouTube Shorts: 9:16; height-capped so the frame stays inside the carousel */}\r\n\t\t\t\t\t\t<div\r\n\t\t\t\t\t\t\tclassName=\"relative mx-auto h-[min(48vh,352px)] w-[calc(min(48vh,352px)*9/16)] shrink-0\"\r\n\t\t\t\t\t\t\tstyle={{\r\n\t\t\t\t\t\t\t\ttransform: `translateZ(0px) rotateX(20deg)`,\r\n\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t<motion.div\r\n\t\t\t\t\t\t\t\tclassName=\"absolute inset-0\"\r\n\t\t\t\t\t\t\t\tinitial={false}\r\n\t\t\t\t\t\t\t\tanimate={{ opacity: centerTopLayer === \"a\" ? 1 : 0 }}\r\n\t\t\t\t\t\t\t\ttransition={CENTER_CROSSFADE}\r\n\t\t\t\t\t\t\t\tstyle={{ zIndex: centerTopLayer === \"a\" ? 2 : 1 }}\r\n\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t<Iphone\r\n\t\t\t\t\t\t\t\t\tvideoSrc={centerSrcA}\r\n\t\t\t\t\t\t\t\t\tclassName=\"h-full w-full min-h-0 min-w-0\"\r\n\t\t\t\t\t\t\t\t\tonVideoReady={() => onCenterLayerReady(\"a\")}\r\n\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t</motion.div>\r\n\t\t\t\t\t\t\t<motion.div\r\n\t\t\t\t\t\t\t\tclassName=\"absolute inset-0\"\r\n\t\t\t\t\t\t\t\tinitial={false}\r\n\t\t\t\t\t\t\t\tanimate={{ opacity: centerTopLayer === \"b\" ? 1 : 0 }}\r\n\t\t\t\t\t\t\t\ttransition={CENTER_CROSSFADE}\r\n\t\t\t\t\t\t\t\tstyle={{ zIndex: centerTopLayer === \"b\" ? 2 : 1 }}\r\n\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t<Iphone\r\n\t\t\t\t\t\t\t\t\tvideoSrc={centerSrcB}\r\n\t\t\t\t\t\t\t\t\tclassName=\"h-full w-full min-h-0 min-w-0\"\r\n\t\t\t\t\t\t\t\t\tonVideoReady={() => onCenterLayerReady(\"b\")}\r\n\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t</motion.div>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t</motion.div>\r\n\t\t\t\t</motion.div>\r\n\t\t\t</div>\r\n\t\t\t{/* Buttons */}\r\n\t\t\t<div className=\"absolute bottom-12 left-1/2 transform -translate-x-1/2 flex space-x-4\">\r\n\t\t\t\t<button\r\n\t\t\t\t\tclassName=\"bg-white/20 text-black px-8 py-3 rounded-full shadow-lg backdrop-blur-md border border-white/30 hover:bg-white/30 transition duration-300 ease-in-out transform hover:scale-105 focus:outline-none\"\r\n\t\t\t\t\tonClick={() => rotateCarousel(\"left\")}\r\n\t\t\t\t>\r\n\t\t\t\t\t{\"<\"}\r\n\t\t\t\t</button>\r\n\t\t\t\t<button\r\n\t\t\t\t\tclassName=\"bg-white/20 text-black px-8 py-3 rounded-full shadow-lg backdrop-blur-md border border-white/30 hover:bg-white/30 transition duration-300 ease-in-out transform hover:scale-105 focus:outline-none\"\r\n\t\t\t\t\tonClick={() => rotateCarousel(\"right\")}\r\n\t\t\t\t>\r\n\t\t\t\t\t{\">\"}\r\n\t\t\t\t</button>\r\n\t\t\t</div>\r\n\t\t\t{/* Overlay */}\r\n\t\t</div>\r\n\t);\r\n};\r\n\r\nexport default Carousel360;\r\n\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/iphone.tsx",
      "content": "import type { HTMLAttributes } from \"react\"\nimport { useId } from \"react\"\n\nconst PHONE_WIDTH = 433\nconst PHONE_HEIGHT = 882\nconst SCREEN_X = 30\nconst SCREEN_Y = 19.25\nconst SCREEN_WIDTH = 375\nconst SCREEN_HEIGHT = 843.5\nconst SCREEN_RADIUS = 55.75\n\n// Calculated percentages\nconst LEFT_PCT = (SCREEN_X / PHONE_WIDTH) * 100\nconst TOP_PCT = (SCREEN_Y / PHONE_HEIGHT) * 100\nconst WIDTH_PCT = (SCREEN_WIDTH / PHONE_WIDTH) * 100\nconst HEIGHT_PCT = (SCREEN_HEIGHT / PHONE_HEIGHT) * 100\nconst RADIUS_H = (SCREEN_RADIUS / SCREEN_WIDTH) * 100\nconst RADIUS_V = (SCREEN_RADIUS / SCREEN_HEIGHT) * 100\n\nexport interface IphoneProps extends HTMLAttributes<HTMLDivElement> {\n    src?: string\n    videoSrc?: string\n    onVideoReady?: () => void\n}\n\nexport function Iphone({\n    src,\n    videoSrc,\n    onVideoReady,\n    className,\n    style,\n    ...props\n}: IphoneProps) {\n    const hasVideo = !!videoSrc\n    const hasMedia = hasVideo || !!src\n    const uid = useId().replace(/[:]/g, \"\")\n    const punchMaskId = `iphone-screenPunch-${uid}`\n\n    return (\n        <div\n            className={`relative flex min-h-0 min-w-0 shrink-0 items-center justify-center overflow-hidden ${className ?? \"\"}`}\n            style={{\n                ...style,\n            }}\n            {...props}\n        >\n            <div className=\"relative mx-auto aspect-[433/882] h-full max-h-full w-auto max-w-full min-h-0 min-w-0 [&_svg]:block [&_svg]:h-full [&_svg]:w-full\">\n                {hasVideo && (\n                    <div\n                        className=\"pointer-events-none absolute z-0 overflow-hidden\"\n                        style={{\n                            left: `${LEFT_PCT}%`,\n                            top: `${TOP_PCT}%`,\n                            width: `${WIDTH_PCT}%`,\n                            height: `${HEIGHT_PCT}%`,\n                            borderRadius: `${RADIUS_H}% / ${RADIUS_V}%`,\n                            transform: \"translateZ(0) scale(1.008)\",\n                            transformOrigin: \"center center\",\n                        }}\n                    >\n                        <video\n                            className=\"block size-full object-cover\"\n                            src={videoSrc}\n                            autoPlay\n                            loop\n                            muted\n                            playsInline\n                            preload=\"metadata\"\n                            onLoadedData={onVideoReady}\n                        />\n                    </div>\n                )}\n\n                {!hasVideo && src && (\n                    <div\n                        className=\"pointer-events-none absolute z-0 overflow-hidden\"\n                        style={{\n                            left: `${LEFT_PCT}%`,\n                            top: `${TOP_PCT}%`,\n                            width: `${WIDTH_PCT}%`,\n                            height: `${HEIGHT_PCT}%`,\n                            borderRadius: `${RADIUS_H}% / ${RADIUS_V}%`,\n                        }}\n                    >\n                        <img\n                            src={src}\n                            alt=\"\"\n                            className=\"block size-full object-cover object-top\"\n                        />\n                    </div>\n                )}\n\n                <svg\n                    viewBox={`0 0 ${PHONE_WIDTH} ${PHONE_HEIGHT}`}\n                    fill=\"none\"\n                    xmlns=\"http://www.w3.org/2000/svg\"\n                    className=\"absolute inset-0 h-full w-full\"\n                    preserveAspectRatio=\"xMidYMid meet\"\n                    style={{ transform: \"translateZ(0)\" }}\n                >\n                    <g mask={hasMedia ? `url(#${punchMaskId})` : undefined}>\n                        <path\n                            d=\"M2 73C2 32.6832 34.6832 0 75 0H357C397.317 0 430 32.6832 430 73V809C430 849.317 397.317 882 357 882H75C34.6832 882 2 849.317 2 809V73Z\"\n                            className=\"fill-[#E5E5E5] dark:fill-[#404040]\"\n                        />\n                        <path\n                            d=\"M0 171C0 170.448 0.447715 170 1 170H3V204H1C0.447715 204 0 203.552 0 203V171Z\"\n                            className=\"fill-[#E5E5E5] dark:fill-[#404040]\"\n                        />\n                        <path\n                            d=\"M1 234C1 233.448 1.44772 233 2 233H3.5V300H2C1.44772 300 1 299.552 1 299V234Z\"\n                            className=\"fill-[#E5E5E5] dark:fill-[#404040]\"\n                        />\n                        <path\n                            d=\"M1 319C1 318.448 1.44772 318 2 318H3.5V385H2C1.44772 385 1 384.552 1 384V319Z\"\n                            className=\"fill-[#E5E5E5] dark:fill-[#404040]\"\n                        />\n                        <path\n                            d=\"M430 279H432C432.552 279 433 279.448 433 280V384C433 384.552 432.552 385 432 385H430V279Z\"\n                            className=\"fill-[#E5E5E5] dark:fill-[#404040]\"\n                        />\n                        <path\n                            d=\"M6 74C6 35.3401 37.3401 4 76 4H356C394.66 4 426 35.3401 426 74V808C426 846.66 394.66 878 356 878H76C37.3401 878 6 846.66 6 808V74Z\"\n                            className={\n                                hasVideo\n                                    ? \"fill-[#0a0a0a] dark:fill-[#050505]\"\n                                    : \"fill-white dark:fill-[#262626]\"\n                            }\n                        />\n                    </g>\n\n                    <path\n                        opacity=\"0.5\"\n                        d=\"M174 5H258V5.5C258 6.60457 257.105 7.5 256 7.5H176C174.895 7.5 174 6.60457 174 5.5V5Z\"\n                        className=\"fill-[#E5E5E5] dark:fill-[#404040]\"\n                    />\n\n                    <path\n                        d={`M${SCREEN_X} 75C${SCREEN_X} 44.2101 46.2101 ${SCREEN_Y} 77 ${SCREEN_Y}H355C385.79 ${SCREEN_Y} 410.75 44.2101 410.75 75V807C410.75 837.79 385.79 862.75 355 862.75H77C46.2101 862.75 ${SCREEN_X} 837.79 ${SCREEN_X} 807V75Z`}\n                        className={\n                            hasVideo\n                                ? \"fill-transparent stroke-none\"\n                                : \"fill-[#E5E5E5] stroke-[#E5E5E5] stroke-[0.5] dark:fill-[#404040] dark:stroke-[#404040]\"\n                        }\n                        mask={hasMedia ? `url(#${punchMaskId})` : undefined}\n                    />\n\n                    <path\n                        d=\"M154 48.5C154 38.2827 162.283 30 172.5 30H259.5C269.717 30 278 38.2827 278 48.5C278 58.7173 269.717 67 259.5 67H172.5C162.283 67 154 58.7173 154 48.5Z\"\n                        className=\"fill-[#F5F5F5] dark:fill-[#262626]\"\n                    />\n                    <path\n                        d=\"M249 48.5C249 42.701 253.701 38 259.5 38C265.299 38 270 42.701 270 48.5C270 54.299 265.299 59 259.5 59C253.701 59 249 54.299 249 48.5Z\"\n                        className=\"fill-[#F5F5F5] dark:fill-[#262626]\"\n                    />\n                    <path\n                        d=\"M254 48.5C254 45.4624 256.462 43 259.5 43C262.538 43 265 45.4624 265 48.5C265 51.5376 262.538 54 259.5 54C256.462 54 254 51.5376 254 48.5Z\"\n                        className=\"fill-[#E5E5E5] dark:fill-[#404040]\"\n                    />\n\n                    <defs>\n                        <mask id={punchMaskId} maskUnits=\"userSpaceOnUse\">\n                            <rect\n                                x=\"0\"\n                                y=\"0\"\n                                width={PHONE_WIDTH}\n                                height={PHONE_HEIGHT}\n                                fill=\"white\"\n                            />\n                            <rect\n                                x={SCREEN_X}\n                                y={SCREEN_Y}\n                                width={SCREEN_WIDTH}\n                                height={SCREEN_HEIGHT}\n                                rx={SCREEN_RADIUS}\n                                ry={SCREEN_RADIUS}\n                                fill=\"black\"\n                            />\n                        </mask>\n                    </defs>\n                </svg>\n            </div>\n        </div>\n    )\n}\n",
      "type": "registry:ui"
    }
  ]
}