{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "iphone-mockup",
  "type": "registry:ui",
  "title": "iPhone Mockup",
  "description": "Pixel-perfect iPhone Pro mockup with Dynamic Island, side buttons, status bar and home indicator. Geometry matched to orchid.ai (390 x 815 chassis, 56/46 px corner radii, 120 x 37 Dynamic Island).",
  "dependencies": [],
  "files": [
    {
      "path": "registry/new-york/ui/iphone-mockup.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface IPhoneMockupProps {\n\t/** Status bar time, e.g. \"9:41\" */\n\ttime?: string;\n\t/** Screen background color (CSS color) */\n\tscreenBackground?: string;\n\t/** Tint the status bar icons/time. \"auto\" picks based on screen background brightness */\n\tstatusBarTone?: \"auto\" | \"light\" | \"dark\";\n\t/** Hide the status bar entirely (useful for full-screen apps that draw their own) */\n\thideStatusBar?: boolean;\n\t/** Show a 5px home-indicator bar at the bottom */\n\thomeIndicator?: boolean;\n\t/** Tint the home indicator. \"auto\" picks based on screen background brightness */\n\thomeIndicatorTone?: \"auto\" | \"light\" | \"dark\";\n\t/** Outer chassis className overrides */\n\tclassName?: string;\n\t/** Inner screen className overrides */\n\tscreenClassName?: string;\n\tchildren?: React.ReactNode;\n}\n\n/**\n * Realistic iPhone (Pro) mockup with Dynamic Island, side buttons,\n * status bar and home indicator. 390 × 815 chassis, 10 px bezel,\n * 56 / 46 px corner radii, 120 × 37 Dynamic Island.\n */\nexport function IPhoneMockup({\n\ttime = \"9:41\",\n\tscreenBackground,\n\tstatusBarTone = \"auto\",\n\thideStatusBar = false,\n\thomeIndicator = true,\n\thomeIndicatorTone = \"auto\",\n\tclassName,\n\tscreenClassName,\n\tchildren,\n}: IPhoneMockupProps) {\n\tconst resolvedTone =\n\t\tstatusBarTone === \"auto\"\n\t\t\t? isDarkColor(screenBackground)\n\t\t\t\t? \"light\"\n\t\t\t\t: \"dark\"\n\t\t\t: statusBarTone;\n\n\tconst resolvedHomeTone =\n\t\thomeIndicatorTone === \"auto\"\n\t\t\t? isDarkColor(screenBackground)\n\t\t\t\t? \"light\"\n\t\t\t\t: \"dark\"\n\t\t\t: homeIndicatorTone;\n\n\tconst fg = resolvedTone === \"light\" ? \"#ffffff\" : \"#000000\";\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"relative inline-block rounded-[56px] bg-black p-[10px]\",\n\t\t\t\t\"shadow-[0_50px_100px_-20px_rgba(0,0,0,0.35),0_30px_60px_-30px_rgba(0,0,0,0.4),inset_0_0_0_1px_rgba(255,255,255,0.04)]\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tstyle={{ width: 390, height: 815 }}\n\t\t\taria-label=\"iPhone mockup\"\n\t\t\trole=\"img\"\n\t\t>\n\t\t\t<div\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"pointer-events-none absolute inset-0 rounded-[56px]\"\n\t\t\t\tstyle={{\n\t\t\t\t\tbackground:\n\t\t\t\t\t\t\"linear-gradient(135deg, rgba(255,255,255,0.06) 0%, rgba(255,255,255,0) 30%, rgba(255,255,255,0) 70%, rgba(255,255,255,0.04) 100%)\",\n\t\t\t\t}}\n\t\t\t/>\n\n\t\t\t<SideButtons />\n\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"relative flex flex-col overflow-hidden rounded-[46px]\",\n\t\t\t\t\tscreenClassName,\n\t\t\t\t)}\n\t\t\t\tstyle={{\n\t\t\t\t\twidth: 370,\n\t\t\t\t\theight: 795,\n\t\t\t\t\tbackground: screenBackground ?? \"#ffffff\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{/* Dynamic Island */}\n\t\t\t\t<div\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\tclassName=\"absolute top-[12px] left-1/2 z-20 -translate-x-1/2 rounded-full bg-black\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\twidth: 96,\n\t\t\t\t\t\theight: 28,\n\t\t\t\t\t\tboxShadow:\n\t\t\t\t\t\t\t\"inset 0 0 0 0.5px rgba(255,255,255,0.06), 0 1px 2px rgba(0,0,0,0.3)\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"absolute top-1/2 right-[9px] -translate-y-1/2 rounded-full\"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\twidth: 8,\n\t\t\t\t\t\t\theight: 8,\n\t\t\t\t\t\t\tbackground:\n\t\t\t\t\t\t\t\t\"radial-gradient(circle at 30% 30%, #2a2f3a 0%, #0b0e14 60%, #000 100%)\",\n\t\t\t\t\t\t\tboxShadow: \"inset 0 0 0 0.5px rgba(255,255,255,0.08)\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclassName=\"absolute top-[2px] left-[2px] rounded-full\"\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\twidth: 2,\n\t\t\t\t\t\t\t\theight: 2,\n\t\t\t\t\t\t\t\tbackground: \"rgba(120,200,255,0.55)\",\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{!hideStatusBar && (\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"relative z-10 flex shrink-0 items-center justify-between\"\n\t\t\t\t\t\tstyle={{ height: 54, padding: \"0 26px\" }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\twidth: 80,\n\t\t\t\t\t\t\t\tfontSize: 17,\n\t\t\t\t\t\t\t\tlineHeight: 1,\n\t\t\t\t\t\t\t\tfontWeight: 600,\n\t\t\t\t\t\t\t\tletterSpacing: \"-0.01em\",\n\t\t\t\t\t\t\t\tcolor: fg,\n\t\t\t\t\t\t\t\tfontFamily:\n\t\t\t\t\t\t\t\t\t'-apple-system, BlinkMacSystemFont, \"SF Pro Display\", \"SF Pro Text\", \"Helvetica Neue\", Helvetica, Arial, sans-serif',\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{time}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"flex items-center justify-end\"\n\t\t\t\t\t\t\tstyle={{ width: 80, gap: 5, color: fg }}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<CellularIcon color={fg} />\n\t\t\t\t\t\t\t<WifiIcon color={fg} />\n\t\t\t\t\t\t\t<BatteryIcon color={fg} />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"relative z-0 flex min-h-0 flex-1 flex-col\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tpaddingBottom: homeIndicator ? 22 : 0,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\n\t\t\t\t{homeIndicator && (\n\t\t\t\t\t<div\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclassName=\"pointer-events-none absolute bottom-[8px] left-1/2 z-30 -translate-x-1/2 rounded-full\"\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\twidth: 134,\n\t\t\t\t\t\t\theight: 5,\n\t\t\t\t\t\t\tbackground:\n\t\t\t\t\t\t\t\tresolvedHomeTone === \"light\"\n\t\t\t\t\t\t\t\t\t? \"rgba(255,255,255,0.85)\"\n\t\t\t\t\t\t\t\t\t: \"rgba(0,0,0,0.85)\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction SideButtons() {\n\treturn (\n\t\t<>\n\t\t\t<span\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"absolute rounded-l-[2px]\"\n\t\t\t\tstyle={{\n\t\t\t\t\tleft: -2,\n\t\t\t\t\ttop: 105,\n\t\t\t\t\twidth: 4,\n\t\t\t\t\theight: 32,\n\t\t\t\t\tbackground: \"linear-gradient(90deg, #1a1d22 0%, #0a0c10 100%)\",\n\t\t\t\t}}\n\t\t\t/>\n\t\t\t<span\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"absolute rounded-l-[2px]\"\n\t\t\t\tstyle={{\n\t\t\t\t\tleft: -2,\n\t\t\t\t\ttop: 175,\n\t\t\t\t\twidth: 4,\n\t\t\t\t\theight: 60,\n\t\t\t\t\tbackground: \"linear-gradient(90deg, #1a1d22 0%, #0a0c10 100%)\",\n\t\t\t\t}}\n\t\t\t/>\n\t\t\t<span\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"absolute rounded-l-[2px]\"\n\t\t\t\tstyle={{\n\t\t\t\t\tleft: -2,\n\t\t\t\t\ttop: 250,\n\t\t\t\t\twidth: 4,\n\t\t\t\t\theight: 60,\n\t\t\t\t\tbackground: \"linear-gradient(90deg, #1a1d22 0%, #0a0c10 100%)\",\n\t\t\t\t}}\n\t\t\t/>\n\t\t\t<span\n\t\t\t\taria-hidden=\"true\"\n\t\t\t\tclassName=\"absolute rounded-r-[2px]\"\n\t\t\t\tstyle={{\n\t\t\t\t\tright: -2,\n\t\t\t\t\ttop: 200,\n\t\t\t\t\twidth: 4,\n\t\t\t\t\theight: 96,\n\t\t\t\t\tbackground: \"linear-gradient(270deg, #1a1d22 0%, #0a0c10 100%)\",\n\t\t\t\t}}\n\t\t\t/>\n\t\t</>\n\t);\n}\n\n/* ------------------------------------------------------------------\n * Status bar icons extracted from Frame 2.svg.\n * Paths preserved verbatim (translated to local viewBoxes) so the\n * curvature, corner radii and proportions match pixel-for-pixel.\n * ------------------------------------------------------------------ */\n\nfunction CellularIcon({ color }: { color: string }) {\n\treturn (\n\t\t<svg\n\t\t\twidth=\"18\"\n\t\t\theight=\"12\"\n\t\t\tviewBox=\"185 211 139 88\"\n\t\t\tfill={color}\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t<path\n\t\t\t\tfillRule=\"evenodd\"\n\t\t\t\tclipRule=\"evenodd\"\n\t\t\t\td=\"M323.798 219.308C323.798 214.759 320.366 211.072 316.133 211.072H308.468C304.235 211.072 300.803 214.759 300.803 219.308V290.692C300.803 295.241 304.235 298.928 308.468 298.928H316.133C320.366 298.928 323.798 295.241 323.798 290.692V219.308ZM270.378 228.643H278.042C282.276 228.643 285.707 232.419 285.707 237.077V290.494C285.707 295.152 282.276 298.928 278.042 298.928H270.378C266.144 298.928 262.713 295.152 262.713 290.494V237.077C262.713 232.419 266.144 228.643 270.378 228.643ZM239.25 247.679H231.585C227.352 247.679 223.921 251.503 223.921 256.22V290.387C223.921 295.104 227.352 298.928 231.585 298.928H239.25C243.483 298.928 246.915 295.104 246.915 290.387V256.22C246.915 251.503 243.483 247.679 239.25 247.679ZM201.16 265.25H193.495C189.262 265.25 185.83 269.02 185.83 273.67V290.509C185.83 295.159 189.262 298.928 193.495 298.928H201.16C205.393 298.928 208.825 295.159 208.825 290.509V273.67C208.825 269.02 205.393 265.25 201.16 265.25Z\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction WifiIcon({ color }: { color: string }) {\n\t// viewBox covers the full curvature of the outer wave (Bezier apex ≈ y 210.7)\n\t// so the top of the largest arc isn't clipped.\n\treturn (\n\t\t<svg\n\t\t\twidth=\"17\"\n\t\t\theight=\"12\"\n\t\t\tviewBox=\"370 207 132 95\"\n\t\t\tfill={color}\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t<path\n\t\t\t\tfillRule=\"evenodd\"\n\t\t\t\tclipRule=\"evenodd\"\n\t\t\t\td=\"M435.69 228.428C453.562 228.429 470.75 235.054 483.703 246.936C484.678 247.853 486.237 247.842 487.198 246.91L496.521 237.831C497.008 237.359 497.279 236.718 497.275 236.052C497.271 235.386 496.992 234.75 496.5 234.282C462.504 202.847 408.871 202.847 374.875 234.282C374.382 234.749 374.103 235.386 374.098 236.052C374.094 236.718 374.364 237.358 374.851 237.831L384.177 246.91C385.137 247.843 386.697 247.855 387.672 246.936C400.626 235.054 417.816 228.428 435.69 228.428ZM435.666 258.754C445.419 258.753 454.825 262.431 462.054 269.071C463.032 270.014 464.573 269.993 465.526 269.025L474.776 259.545C475.263 259.048 475.533 258.373 475.526 257.672C475.519 256.971 475.236 256.302 474.739 255.815C452.723 235.042 418.628 235.042 396.612 255.815C396.114 256.302 395.831 256.971 395.824 257.673C395.818 258.374 396.089 259.048 396.577 259.545L405.824 269.025C406.778 269.993 408.318 270.014 409.296 269.071C416.521 262.435 425.919 258.758 435.666 258.754ZM453.806 278.828C453.82 279.585 453.553 280.315 453.07 280.845L437.429 298.484C436.97 299.003 436.345 299.294 435.693 299.294C435.041 299.294 434.415 299.003 433.957 298.484L418.313 280.845C417.83 280.314 417.564 279.584 417.578 278.827C417.593 278.07 417.886 277.353 418.389 276.846C428.378 267.404 443.008 267.404 452.997 276.846C453.499 277.354 453.792 278.071 453.806 278.828Z\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction BatteryIcon({\n\tcolor,\n\tpercent = 80,\n}: {\n\tcolor: string;\n\tpercent?: number;\n}) {\n\tconst clamped = Math.max(0, Math.min(100, percent));\n\t// Geometry preserved from Frame 2.svg, but drawn as stroke-only so the\n\t// status bar background shows through (no grey wash).\n\treturn (\n\t\t<svg\n\t\t\twidth=\"27\"\n\t\t\theight=\"13\"\n\t\t\tviewBox=\"547 204 197 101\"\n\t\t\tfill=\"none\"\n\t\t\txmlns=\"http://www.w3.org/2000/svg\"\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t{/* Outer body — outline only */}\n\t\t\t<rect\n\t\t\t\tx={550}\n\t\t\t\ty={207.2}\n\t\t\t\twidth={174.4}\n\t\t\t\theight={95}\n\t\t\t\trx={28.7432}\n\t\t\t\try={28.7432}\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke={color}\n\t\t\t\tstrokeOpacity=\"0.45\"\n\t\t\t\tstrokeWidth={5}\n\t\t\t/>\n\t\t\t{/* Battery tip */}\n\t\t\t<rect\n\t\t\t\tx={729}\n\t\t\t\ty={243.9}\n\t\t\t\twidth={11.5}\n\t\t\t\theight={21.6}\n\t\t\t\trx={4}\n\t\t\t\try={4}\n\t\t\t\tfill={color}\n\t\t\t\tfillOpacity=\"0.45\"\n\t\t\t/>\n\t\t\t{/* Fill bar */}\n\t\t\t<rect\n\t\t\t\tx={559}\n\t\t\t\ty={216}\n\t\t\t\twidth={156 * (clamped / 100)}\n\t\t\t\theight={78}\n\t\t\t\trx={20}\n\t\t\t\try={20}\n\t\t\t\tfill={color}\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\nfunction isDarkColor(color?: string): boolean {\n\tif (!color) return false;\n\tconst c = color.trim().toLowerCase();\n\tif (c === \"#000\" || c === \"#000000\" || c === \"black\") return true;\n\tconst hex = c.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/);\n\tif (hex) {\n\t\tlet h = hex[1];\n\t\tif (h.length === 3)\n\t\t\th = h\n\t\t\t\t.split(\"\")\n\t\t\t\t.map((ch) => ch + ch)\n\t\t\t\t.join(\"\");\n\t\tconst r = parseInt(h.slice(0, 2), 16);\n\t\tconst g = parseInt(h.slice(2, 4), 16);\n\t\tconst b = parseInt(h.slice(4, 6), 16);\n\t\tconst yiq = (r * 299 + g * 587 + b * 114) / 1000;\n\t\treturn yiq < 140;\n\t}\n\tconst rgb = c.match(/rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/);\n\tif (rgb) {\n\t\tconst r = +rgb[1];\n\t\tconst g = +rgb[2];\n\t\tconst b = +rgb[3];\n\t\tconst yiq = (r * 299 + g * 587 + b * 114) / 1000;\n\t\treturn yiq < 140;\n\t}\n\treturn false;\n}\n",
      "type": "registry:ui"
    }
  ]
}