{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "inbox-zero",
  "type": "registry:component",
  "title": "Inbox Zero",
  "description": "A tidy desk for the moment the inbox empties: the last email folds into a paper plane, loops out through the window, the counter rolls to zero and the dusk sky turns to day.",
  "categories": [
    "illustration",
    "Celebrations"
  ],
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/devl/inbox-zero.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport {\n  AnimatePresence,\n  animate,\n  motion,\n  useInView,\n  useMotionValue,\n  useReducedMotion,\n  useTransform,\n} from \"motion/react\";\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport type InboxZeroHue = \"sky\" | \"emerald\" | \"amber\" | \"violet\" | \"rose\" | \"orange\" | \"indigo\" | \"teal\";\n\nconst hues: Record<InboxZeroHue, string> = {\n  sky: \"[--hue:var(--color-sky-500)] [--hue-deep:var(--color-sky-700)]\",\n  emerald: \"[--hue:var(--color-emerald-500)] [--hue-deep:var(--color-emerald-700)]\",\n  amber: \"[--hue:var(--color-amber-500)] [--hue-deep:var(--color-amber-700)]\",\n  violet: \"[--hue:var(--color-violet-500)] [--hue-deep:var(--color-violet-700)]\",\n  rose: \"[--hue:var(--color-rose-500)] [--hue-deep:var(--color-rose-700)]\",\n  orange: \"[--hue:var(--color-orange-500)] [--hue-deep:var(--color-orange-700)]\",\n  indigo: \"[--hue:var(--color-indigo-500)] [--hue-deep:var(--color-indigo-700)]\",\n  teal: \"[--hue:var(--color-teal-500)] [--hue-deep:var(--color-teal-700)]\",\n};\n\ntype Phase = \"waiting\" | \"sending\" | \"clear\";\n\nexport interface InboxZeroProps {\n  title?: string;\n  description?: string;\n  hue?: InboxZeroHue;\n  loop?: boolean;\n  className?: string;\n}\n\nconst W = 400;\nconst H = 240;\nconst WIN = { x: 132, y: 18, w: 136, h: 120 };\nconst DESK_Y = 192;\nconst LIFT_Y = DESK_Y - 52;\nconst FLIGHT = `M136 ${LIFT_Y} C 150 118 170 112 196 112 C 224 112 232 78 208 70 C 186 63 174 92 196 99 C 216 105 226 76 246 50`;\n\nfunction Plane() {\n  return (\n    <g transform=\"scale(1.35)\">\n      <path className=\"fill-(--hue)\" d=\"M-13 -7 L15 0 L-7 0 Z\" />\n      <path className=\"fill-(--hue-deep)\" d=\"M-7 0 L15 0 L-13 7 Z\" />\n      <path className=\"fill-white/35\" d=\"M-13 -7 L15 0 L-10 -2.5 Z\" />\n    </g>\n  );\n}\n\nconst sparkles = [\n  { x: 112, y: 44, s: 1, d: 0.1 },\n  { x: 290, y: 32, s: 0.8, d: 0.35 },\n  { x: 300, y: 104, s: 0.6, d: 0.6 },\n  { x: 102, y: 118, s: 0.7, d: 0.85 },\n  { x: 214, y: 156, s: 0.55, d: 0.5 },\n];\n\nfunction Sparkle({ x, y, s }: { x: number; y: number; s: number }) {\n  return (\n    <path\n      d={`M${x} ${y - 9 * s} C ${x + 1.2 * s} ${y - 2 * s} ${x + 2 * s} ${y - 1.2 * s} ${x + 9 * s} ${y} C ${x + 2 * s} ${y + 1.2 * s} ${x + 1.2 * s} ${y + 2 * s} ${x} ${y + 9 * s} C ${x - 1.2 * s} ${y + 2 * s} ${x - 2 * s} ${y + 1.2 * s} ${x - 9 * s} ${y} C ${x - 2 * s} ${y - 1.2 * s} ${x - 1.2 * s} ${y - 2 * s} ${x} ${y - 9 * s} Z`}\n    />\n  );\n}\n\nfunction Letter() {\n  return (\n    <g>\n      <rect className=\"fill-white stroke-black/10\" height={32} rx={3} width={48} x={-24} y={-16} />\n      <path className=\"fill-none stroke-(--hue)\" d=\"M-22 -13 L0 3 L22 -13\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} />\n    </g>\n  );\n}\n\nfunction useCalmMotion() {\n  const prefers = useReducedMotion();\n  const [mounted, setMounted] = useState(false);\n  useEffect(() => setMounted(true), []);\n  return mounted && !!prefers;\n}\n\nexport function InboxZero({\n  title = \"Inbox zero\",\n  description = \"Every thread handled. Go enjoy the afternoon.\",\n  hue = \"rose\",\n  loop = true,\n  className,\n}: InboxZeroProps) {\n  const uid = useId().replace(/:/g, \"\");\n  const reduce = useCalmMotion();\n  const root = useRef<HTMLDivElement>(null);\n  const inView = useInView(root);\n  const live = inView && !reduce;\n  const [phase, setPhase] = useState<Phase>(\"waiting\");\n  const [round, setRound] = useState(0);\n  const [folded, setFolded] = useState(false);\n  const flight = useRef<SVGPathElement>(null);\n  const t = useMotionValue(0);\n  const trail = useMotionValue(0);\n  const at = (p: number) => {\n    const el = flight.current;\n    if (!el) return { x: 136, y: LIFT_Y };\n    return el.getPointAtLength(Math.min(1, Math.max(0, p)) * el.getTotalLength());\n  };\n  const planeX = useTransform(t, (p) => at(p).x);\n  const planeY = useTransform(t, (p) => at(p).y);\n  const planeR = useTransform(t, (p) => {\n    const a = at(p - 0.01);\n    const b = at(p + 0.01);\n    return (Math.atan2(b.y - a.y, b.x - a.x) * 180) / Math.PI;\n  });\n  const planeS = useTransform(t, [0, 0.55, 1], [1, 0.9, 0.42]);\n  const planeO = useTransform(t, [0, 0.85, 1], [1, 1, 0]);\n\n  useEffect(() => {\n    if (reduce) {\n      setPhase(\"clear\");\n      return;\n    }\n    if (!inView) return;\n    let alive = true;\n    const timers: ReturnType<typeof setTimeout>[] = [];\n    const stops: { stop: () => void }[] = [];\n    const wait = (ms: number) => new Promise<void>((r) => timers.push(setTimeout(r, ms)));\n    (async () => {\n      let first = true;\n      while (alive) {\n        t.set(0);\n        trail.set(0);\n        setFolded(false);\n        setPhase(\"waiting\");\n        if (!first) setRound((r) => r + 1);\n        first = false;\n        await wait(1800);\n        if (!alive) return;\n        setPhase(\"sending\");\n        await wait(320);\n        if (!alive) return;\n        setFolded(true);\n        await wait(160);\n        if (!alive) return;\n        const fly = animate(t, 1, { duration: 1.5, ease: [0.45, 0, 0.35, 1] });\n        stops.push(fly, animate(trail, 1, { duration: 0.2 }));\n        await fly;\n        if (!alive) return;\n        setPhase(\"clear\");\n        stops.push(animate(trail, 0, { duration: 0.9, ease: \"easeOut\" }));\n        if (!loop) return;\n        await wait(4600);\n      }\n    })();\n    return () => {\n      alive = false;\n      for (const x of timers) clearTimeout(x);\n      for (const x of stops) x.stop();\n    };\n  }, [loop, reduce, inView, t, trail]);\n\n  const clear = phase === \"clear\";\n  const count = phase === \"waiting\" ? 1 : 0;\n\n  return (\n    <div\n      ref={root}\n      className={cn(\n        \"relative w-full max-w-[440px] overflow-hidden rounded-2xl border bg-card p-4 shadow-xs/5 sm:p-6\",\n        \"[--tint:color-mix(in_oklab,var(--color-foreground)_5%,var(--color-card))] [--sky-top:var(--color-sky-300)] [--sky-low:var(--color-sky-100)] [--dawn:var(--color-amber-200)] [--dusk-top:var(--color-indigo-400)] [--dusk-low:var(--color-rose-200)]\",\n        hues[hue],\n        className,\n      )}\n    >\n      <div\n        aria-hidden\n        className=\"dot-grid pointer-events-none absolute inset-0 opacity-50 [mask-image:radial-gradient(closest-side,black,transparent)]\"\n      />\n      <div className=\"relative mx-auto aspect-[5/3] w-full max-w-[400px]\">\n        <svg aria-hidden className=\"absolute inset-0 size-full overflow-visible\" viewBox={`0 0 ${W} ${H}`}>\n          <defs>\n            <linearGradient id={`${uid}-dusk`} x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\">\n              <stop offset=\"0\" stopColor=\"var(--dusk-top)\" />\n              <stop offset=\"1\" stopColor=\"var(--dusk-low)\" />\n            </linearGradient>\n            <mask height={H} id={`${uid}-dash`} maskUnits=\"userSpaceOnUse\" width={W} x={0} y={0}>\n              <path className=\"stroke-white\" d={FLIGHT} fill=\"none\" strokeDasharray=\"4 5\" strokeLinecap=\"round\" strokeWidth={4} />\n            </mask>\n            <linearGradient id={`${uid}-sky`} x1=\"0\" x2=\"0\" y1=\"0\" y2=\"1\">\n              <stop offset=\"0\" stopColor=\"var(--sky-top)\" />\n              <stop offset=\"1\" stopColor=\"var(--sky-low)\" />\n            </linearGradient>\n            <radialGradient cx=\"0.5\" cy=\"1\" id={`${uid}-dawn`} r=\"0.9\">\n              <stop offset=\"0\" stopColor=\"var(--dawn)\" stopOpacity=\"0.95\" />\n              <stop offset=\"1\" stopColor=\"var(--dawn)\" stopOpacity=\"0\" />\n            </radialGradient>\n            <clipPath id={`${uid}-win`}>\n              <rect height={WIN.h} rx={12} width={WIN.w} x={WIN.x} y={WIN.y} />\n            </clipPath>\n          </defs>\n\n          <g clipPath={`url(#${uid}-win)`}>\n            <rect fill={`url(#${uid}-dusk)`} height={WIN.h} width={WIN.w} x={WIN.x} y={WIN.y} />\n            <motion.rect\n              animate={{ opacity: clear ? 1 : 0 }}\n              fill={`url(#${uid}-sky)`}\n              height={WIN.h}\n              initial={false}\n              transition={{ duration: clear ? 1.4 : 0.9, ease: \"easeOut\" }}\n              width={WIN.w}\n              x={WIN.x}\n              y={WIN.y}\n            />\n            <motion.rect\n              animate={{ opacity: clear ? 1 : 0 }}\n              fill={`url(#${uid}-dawn)`}\n              height={WIN.h}\n              initial={false}\n              transition={{ duration: 1.2, ease: \"easeOut\" }}\n              width={WIN.w}\n              x={WIN.x}\n              y={WIN.y}\n            />\n            <motion.g\n              animate={{ y: clear ? 0 : 104 }}\n              initial={false}\n              transition={clear ? { delay: 0.15, duration: 1.6, ease: [0.22, 1, 0.36, 1] } : { duration: 0.9, ease: [0.65, 0, 0.35, 1] }}\n            >\n              <motion.g\n                animate={live ? { rotate: 360 } : undefined}\n                className=\"stroke-amber-300\"\n                strokeLinecap=\"round\"\n                strokeWidth={3}\n                style={{ originX: \"200px\", originY: \"84px\", transformBox: \"view-box\" }}\n                transition={{ duration: 24, ease: \"linear\", repeat: Number.POSITIVE_INFINITY }}\n              >\n                {Array.from({ length: 10 }, (_, i) => {\n                  const a = (i / 10) * Math.PI * 2;\n                  return (\n                    <line\n                      key={i}\n                      x1={200 + Math.cos(a) * 30}\n                      x2={200 + Math.cos(a) * 38}\n                      y1={84 + Math.sin(a) * 30}\n                      y2={84 + Math.sin(a) * 38}\n                    />\n                  );\n                })}\n              </motion.g>\n              <circle className=\"fill-amber-300\" cx={200} cy={84} r={24} />\n              <circle className=\"fill-amber-400\" cx={200} cy={84} r={19} />\n            </motion.g>\n            {[\n              { x: WIN.x + 18, y: 46, s: 1, d: 26 },\n              { x: WIN.x + 84, y: 70, s: 0.75, d: 34 },\n            ].map((c) => (\n              <motion.g\n                animate={live ? { x: [0, 26, 0] } : undefined}\n                className=\"fill-white\"\n                key={c.x}\n                transition={{ duration: c.d, ease: \"easeInOut\", repeat: Number.POSITIVE_INFINITY }}\n              >\n                <ellipse cx={c.x} cy={c.y} rx={16 * c.s} ry={6 * c.s} />\n                <circle cx={c.x - 4 * c.s} cy={c.y - 5 * c.s} r={7 * c.s} />\n                <circle cx={c.x + 6 * c.s} cy={c.y - 3 * c.s} r={5.5 * c.s} />\n              </motion.g>\n            ))}\n          </g>\n          <rect className=\"fill-none stroke-foreground/20\" height={WIN.h} rx={12} strokeWidth={3} width={WIN.w} x={WIN.x} y={WIN.y} />\n          <line className=\"stroke-foreground/12\" strokeWidth={3} x1={WIN.x + WIN.w / 2} x2={WIN.x + WIN.w / 2} y1={WIN.y + 2} y2={WIN.y + WIN.h - 2} />\n          <rect className=\"fill-(--tint) stroke-foreground/15\" height={7} rx={3.5} width={WIN.w + 20} x={WIN.x - 10} y={WIN.y + WIN.h + 2} />\n\n          <rect className=\"fill-(--tint) stroke-foreground/15\" height={10} rx={4} width={312} x={44} y={DESK_Y} />\n          <g className=\"stroke-foreground/15\" strokeLinecap=\"round\" strokeWidth={3}>\n            <line x1={70} x2={70} y1={DESK_Y + 10} y2={H - 4} />\n            <line x1={330} x2={330} y1={DESK_Y + 10} y2={H - 4} />\n          </g>\n\n          <g>\n            <path className=\"fill-emerald-500\" d=\"M322 158 C 310 150 306 138 310 128 C 320 134 324 146 322 158 Z\" />\n            <path className=\"fill-emerald-600\" d=\"M326 160 C 326 144 334 134 346 132 C 346 146 338 156 326 160 Z\" />\n            <path className=\"fill-emerald-400\" d=\"M324 162 C 316 158 304 160 298 168 C 308 172 318 170 324 162 Z\" />\n            <path className=\"fill-orange-300\" d=\"M308 166 H340 L336 192 H312 Z\" />\n            <rect className=\"fill-orange-400\" height={6} rx={2} width={36} x={306} y={164} />\n          </g>\n\n          <g>\n            <path className=\"fill-none stroke-(--hue-deep)\" d=\"M274 172 h4 a8 8 0 0 1 0 16 h-4\" strokeWidth={4} />\n            <rect className=\"fill-(--hue)\" height={30} rx={5} width={28} x={248} y={162} />\n            <rect className=\"fill-white/25\" height={22} rx={2} width={4} x={253} y={166} />\n            {live &&\n              [0, 1].map((n) => (\n                <motion.path\n                  animate={{ opacity: [0, 0.8, 0], y: [4, -10] }}\n                  className=\"fill-none stroke-foreground/25\"\n                  d={`M${256 + n * 10} 154 q -5 -6 0 -12 q 5 -6 0 -12`}\n                  key={n}\n                  strokeLinecap=\"round\"\n                  strokeWidth={2.5}\n                  transition={{ delay: n * 1.1, duration: 2.6, ease: \"easeOut\", repeat: Number.POSITIVE_INFINITY }}\n                />\n              ))}\n          </g>\n\n          <path className=\"fill-(--tint) stroke-foreground/20\" d={`M88 ${DESK_Y - 30} H184 L178 ${DESK_Y} H94 Z`} strokeLinejoin=\"round\" strokeWidth={1.5} />\n          <AnimatePresence>\n            {!folded && phase !== \"clear\" && (\n              <motion.g\n                animate={\n                  phase === \"sending\"\n                    ? { x: 136, y: LIFT_Y, rotate: 0, scale: 1, opacity: 1 }\n                    : { x: 136, y: DESK_Y - 30, rotate: -4, scale: 1, opacity: 1 }\n                }\n                exit={{ opacity: 0, scale: 0.7, transition: { duration: 0.16, ease: \"easeIn\" } }}\n                initial={round === 0 ? false : { x: 136, y: DESK_Y - 110, rotate: 10, scale: 1, opacity: 0 }}\n                key={`letter-${round}`}\n                transition={\n                  phase === \"sending\"\n                    ? { duration: 0.3, ease: [0.23, 1, 0.32, 1] }\n                    : { type: \"spring\", stiffness: 320, damping: 24 }\n                }\n              >\n                <Letter />\n              </motion.g>\n            )}\n          </AnimatePresence>\n          <path className=\"fill-(--tint) stroke-foreground/20\" d={`M84 ${DESK_Y - 16} H188 L182 ${DESK_Y} H90 Z`} strokeLinejoin=\"round\" strokeWidth={1.5} />\n          <rect className=\"fill-foreground/15\" height={3} rx={1.5} width={30} x={121} y={DESK_Y - 9} />\n\n          <path d={FLIGHT} fill=\"none\" ref={flight} stroke=\"none\" />\n          <g mask={`url(#${uid}-dash)`}>\n            <motion.path\n              className=\"stroke-foreground/30\"\n              d={FLIGHT}\n              fill=\"none\"\n              strokeLinecap=\"round\"\n              strokeWidth={2}\n              style={{ opacity: trail, pathLength: t }}\n            />\n          </g>\n          {folded && phase !== \"waiting\" && (\n            <motion.g style={{ opacity: planeO, rotate: planeR, scale: planeS, x: planeX, y: planeY }}>\n              <motion.g animate={{ opacity: 1, scale: 1 }} initial={{ opacity: 0, scale: 0.8 }} transition={{ duration: 0.16, ease: \"easeOut\" }}>\n                <Plane />\n              </motion.g>\n            </motion.g>\n          )}\n\n          <g className=\"fill-amber-400\">\n            {sparkles.map((sp) => (\n              <motion.g\n                animate={\n                  clear && !reduce\n                    ? { opacity: [0, 1, 0.2, 1, 0], scale: [0.6, 1, 0.8, 1, 0.6] }\n                    : { opacity: clear ? 1 : 0, scale: 1 }\n                }\n                initial={false}\n                key={sp.x}\n                style={{ originX: \"50%\", originY: \"50%\" }}\n                transition={clear && !reduce ? { delay: 0.6 + sp.d, duration: 2.6, ease: \"easeInOut\", repeat: 1 } : { duration: 0.3 }}\n              >\n                <Sparkle s={sp.s} x={sp.x} y={sp.y} />\n              </motion.g>\n            ))}\n          </g>\n        </svg>\n\n        <div className=\"absolute top-0 left-0 flex items-center gap-1.5 rounded-full border bg-popover py-1 pr-1 pl-2.5 text-xs shadow-xs/5\">\n          <span className=\"text-muted-foreground\">Inbox</span>\n          <span\n            className={cn(\n              \"relative flex h-5 min-w-5 items-center justify-center overflow-hidden rounded-full px-1.5 font-medium tabular-nums transition-colors duration-300\",\n              count > 0 ? \"bg-foreground text-background\" : \"bg-success/15 text-success-foreground\",\n            )}\n          >\n            <AnimatePresence initial={false} mode=\"popLayout\">\n              <motion.span\n                animate={{ y: 0, opacity: 1 }}\n                exit={{ y: reduce ? 0 : count > 0 ? 10 : -10, opacity: 0 }}\n                initial={{ y: reduce ? 0 : count > 0 ? -10 : 10, opacity: 0 }}\n                key={count}\n                transition={{ duration: 0.22, ease: [0.23, 1, 0.32, 1] }}\n              >\n                {count}\n              </motion.span>\n            </AnimatePresence>\n          </span>\n        </div>\n      </div>\n\n      <div className=\"relative mt-3 text-center\">\n        <p className=\"text-balance font-medium text-foreground text-sm\">{title}</p>\n        <p className=\"mx-auto mt-1 max-w-[32ch] text-pretty text-muted-foreground text-xs\">{description}</p>\n      </div>\n    </div>\n  );\n}\n"
    }
  ]
}