{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "phone-mockup",
  "title": "Phone Mockup",
  "description": "A device-frame illustration with side buttons, dynamic island, and a cropped/scrolled variant for showcasing app screens.",
  "dependencies": [],
  "files": [
    {
      "path": "components/illustration/phone-mockup.tsx",
      "type": "registry:ui",
      "content": "'use client'\n\nimport { forwardRef, type ComponentPropsWithoutRef, type ReactNode } from 'react'\nimport { cn } from '@/lib/utils'\n\nconst mockupChassisVariants = {\n  black: {\n    frame: 'bg-(--color-foreground)',\n    button: 'bg-(--color-foreground)',\n  },\n  white: {\n    frame: 'bg-(--color-card)',\n    button: 'bg-(--color-card)',\n  },\n  accent: {\n    frame:\n      'bg-(--color-accent) dark:bg-[color-mix(in_srgb,var(--color-accent)_60%,var(--color-card))]',\n    button:\n      'bg-(--color-accent) dark:bg-[color-mix(in_srgb,var(--color-accent)_60%,var(--color-card))]',\n  },\n} as const\n\ntype MockupChassisVariant = keyof typeof mockupChassisVariants\n\nconst CHASSIS_ASPECT_RATIO = 70.6 / 146.6\n\ntype PhoneMockupCardProps = Readonly<\n  ComponentPropsWithoutRef<'div'> & {\n    variant?: MockupChassisVariant\n    visibleRatio?: number\n    showDynamicIsland?: boolean\n  }\n>\n\nconst sideControlClass = (color: string) => cn('absolute w-0.5 rounded-l-sm', color)\n\nfunction SideControls({\n  frame,\n}: Readonly<{\n  frame: (typeof mockupChassisVariants)[MockupChassisVariant]\n}>) {\n  return (\n    <>\n      <div\n        className={cn(sideControlClass(frame.button), 'top-[15.5%] -left-0.5 h-[3.2%]')}\n        aria-hidden=\"true\"\n      />\n      <div\n        className={cn(sideControlClass(frame.button), 'top-[21%] -left-0.5 h-[7.2%]')}\n        aria-hidden=\"true\"\n      />\n      <div\n        className={cn(sideControlClass(frame.button), 'top-[30.5%] -left-0.5 h-[7.2%]')}\n        aria-hidden=\"true\"\n      />\n      <div\n        className={cn(\n          sideControlClass(frame.button),\n          'top-[23%] -right-0.5 h-[11.5%] rounded-l-none rounded-r-sm',\n        )}\n        aria-hidden=\"true\"\n      />\n    </>\n  )\n}\n\nfunction MockupScreen({\n  children,\n  showDynamicIsland,\n}: Readonly<{\n  children: ReactNode\n  showDynamicIsland: boolean\n}>) {\n  return (\n    <div className=\"relative h-full w-full overflow-hidden rounded-[2.5rem] bg-black\">\n      <div className=\"absolute inset-[3.5px] overflow-hidden rounded-[2.3rem] bg-white\">\n        <div className=\"relative h-full w-full\">{children}</div>\n        {showDynamicIsland && (\n          <div\n            className=\"absolute top-[9px] left-1/2 z-20 h-5 w-[66px] -translate-x-1/2 rounded-full bg-black\"\n            aria-hidden=\"true\"\n          >\n            <div\n              className=\"absolute top-1/2 right-[5px] block h-2 w-2 shrink-0 -translate-y-1/2 rounded-full bg-(--color-muted)/40\"\n              aria-hidden=\"true\"\n            />\n          </div>\n        )}\n        <div\n          className=\"absolute bottom-[5.5px] left-1/2 z-20 h-[3px] w-[32%] -translate-x-1/2 rounded-full bg-black/20\"\n          aria-hidden=\"true\"\n        />\n      </div>\n    </div>\n  )\n}\n\nexport const PhoneMockupCard = forwardRef<HTMLDivElement, PhoneMockupCardProps>(\n  (\n    {\n      className,\n      children,\n      variant = 'accent',\n      visibleRatio = 1,\n      showDynamicIsland = true,\n      style,\n      ...props\n    },\n    ref,\n  ) => {\n    const frame = mockupChassisVariants[variant]\n    const ratio = Math.min(1, Math.max(0, visibleRatio))\n    const isCropped = ratio < 1\n\n    const phoneFrameClassName = cn('relative w-64 rounded-[2.6rem] p-0.5', frame.frame)\n\n    if (!isCropped) {\n      return (\n        <div\n          ref={ref}\n          data-slot=\"phone-mockup-card\"\n          data-variant={variant}\n          className={cn(phoneFrameClassName, 'aspect-[70.6/146.6]', className)}\n          style={style}\n          {...props}\n        >\n          <SideControls frame={frame} />\n          <MockupScreen showDynamicIsland={showDynamicIsland}>{children}</MockupScreen>\n        </div>\n      )\n    }\n\n    return (\n      <div\n        ref={ref}\n        data-slot=\"phone-mockup-card\"\n        data-variant={variant}\n        data-visible-ratio={ratio}\n        className={cn('relative w-65 overflow-hidden', className)}\n        style={{ aspectRatio: `${CHASSIS_ASPECT_RATIO / ratio}`, ...style }}\n        {...props}\n      >\n        <div className=\"absolute inset-0 right-0.5 left-0.5 overflow-hidden\">\n          <div\n            className={cn(phoneFrameClassName, 'w-full shrink-0')}\n            style={{ height: `${100 / ratio}%` }}\n          >\n            <MockupScreen showDynamicIsland={showDynamicIsland}>{children}</MockupScreen>\n          </div>\n        </div>\n        <div\n          className=\"pointer-events-none absolute top-0 left-0.5 z-10 w-64 shrink-0\"\n          style={{ height: `${100 / ratio}%` }}\n        >\n          <SideControls frame={frame} />\n        </div>\n      </div>\n    )\n  },\n)\n\nPhoneMockupCard.displayName = 'PhoneMockupCard'\n"
    }
  ],
  "type": "registry:ui"
}
