{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "demo-page-transition",
  "title": "Page Transition Demo",
  "description": "Demo showing four fake pages cycling through curtain, slide, push, and fade transition variants.",
  "registryDependencies": [
    "@unlumen-ui/page-transition"
  ],
  "files": [
    {
      "path": "registry/demo/components/unlumen/page-transition/index.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport gsap from \"gsap\";\nimport { CustomEase } from \"gsap/CustomEase\";\n\ngsap.registerPlugin(CustomEase);\n\nconst EASE_CURTAIN = CustomEase.create(\n  \"pt_curtain\",\n  \"M0,0 C0.38,0.05 0.48,0.58 0.65,0.82 0.82,1 1,1 1,1\",\n);\n\nconst EASE_SLIDE = CustomEase.create(\n  \"pt_slide\",\n  \"M0,0 C0.178,0.031 0.279,0.802 0.345,0.856 0.421,0.918 0.374,1 1,1\",\n);\n\ninterface PageData {\n  namespace: string;\n  label: string;\n  title: string;\n  subtitle: string;\n  accent: string;\n  bg: string;\n  path: string;\n}\n\nconst PAGES: PageData[] = [\n  {\n    namespace: \"home\",\n    label: \"01\",\n    title: \"Design\",\n    subtitle: \"Where ideas become interfaces.\",\n    accent: \"#818cf8\",\n    bg: \"#0c0c12\",\n    path: \"/home\",\n  },\n  {\n    namespace: \"motion\",\n    label: \"02\",\n    title: \"Motion\",\n    subtitle: \"Breath between states.\",\n    accent: \"#fb7185\",\n    bg: \"#120c0e\",\n    path: \"/motion\",\n  },\n  {\n    namespace: \"code\",\n    label: \"03\",\n    title: \"Code\",\n    subtitle: \"Precision in every keystroke.\",\n    accent: \"#34d399\",\n    bg: \"#0c1210\",\n    path: \"/code\",\n  },\n  {\n    namespace: \"ship\",\n    label: \"04\",\n    title: \"Ship\",\n    subtitle: \"Done is better than perfect.\",\n    accent: \"#fbbf24\",\n    bg: \"#121009\",\n    path: \"/ship\",\n  },\n];\n\ntype TransitionVariant = \"curtain\" | \"slide\";\n\nfunction runCurtainTransition(\n  currentEl: HTMLElement,\n  nextEl: HTMLElement,\n  duration: number,\n): Promise<void> {\n  gsap.set(nextEl, {\n    clipPath: \"inset(100% 0% 0% 0%)\",\n    opacity: 1,\n    position: \"absolute\",\n    top: 0,\n    left: 0,\n    width: \"100%\",\n    height: \"100%\",\n    zIndex: 10,\n    force3D: true,\n  });\n  return new Promise<void>((resolve) => {\n    const tl = gsap.timeline({ onComplete: resolve });\n    tl.to(\n      currentEl,\n      {\n        y: \"-30%\",\n        opacity: 0.4,\n        scale: 0.8,\n        duration,\n        force3D: true,\n        ease: EASE_CURTAIN,\n      },\n      0,\n    ).to(\n      nextEl,\n      {\n        clipPath: \"inset(0% 0% 0% 0%)\",\n        duration,\n        force3D: true,\n        ease: EASE_CURTAIN,\n      },\n      0,\n    );\n  });\n}\n\nfunction runSlideTransition(\n  currentEl: HTMLElement,\n  nextEl: HTMLElement,\n  duration: number,\n): Promise<void> {\n  gsap.set(nextEl, {\n    opacity: 1,\n    position: \"absolute\",\n    top: 0,\n    left: 0,\n    width: \"100%\",\n    height: \"100%\",\n    x: \"100%\",\n    zIndex: 10,\n    force3D: true,\n  });\n  return new Promise<void>((resolve) => {\n    const tl = gsap.timeline({ onComplete: resolve });\n    tl.to(\n      currentEl,\n      {\n        x: \"-50%\",\n        scale: 0.8,\n        opacity: 0.4,\n        duration,\n        force3D: true,\n        ease: EASE_SLIDE,\n      },\n      0,\n    ).to(nextEl, { x: 0, duration, force3D: true, ease: EASE_SLIDE }, 0);\n  });\n}\n\ninterface PageTransitionDemoProps {\n  variant?: TransitionVariant;\n  duration?: number;\n}\n\nexport default function PageTransitionDemo({\n  variant = \"curtain\",\n  duration,\n}: PageTransitionDemoProps) {\n  const effectiveDuration = duration ?? (variant === \"slide\" ? 1.5 : 0.7);\n\n  const [currentIndex, setCurrentIndex] = React.useState(0);\n  const isTransitioningRef = React.useRef(false);\n  const wrapperRef = React.useRef<HTMLDivElement>(null);\n  const currentContainerRef = React.useRef<HTMLDivElement>(null);\n\n  const configRef = React.useRef({ variant, duration: effectiveDuration });\n  React.useEffect(() => {\n    configRef.current = { variant, duration: effectiveDuration };\n  }, [variant, effectiveDuration]);\n\n  const goTo = React.useCallback(\n    async (nextIndex: number) => {\n      if (isTransitioningRef.current) return;\n      if (nextIndex === currentIndex) return;\n\n      const wrapper = wrapperRef.current;\n      const currentEl = currentContainerRef.current;\n      if (!wrapper || !currentEl) return;\n\n      isTransitioningRef.current = true;\n      setCurrentIndex(nextIndex);\n\n      const nextPage = PAGES[nextIndex];\n\n      const nextEl = document.createElement(\"div\");\n      nextEl.style.cssText = \"position:absolute;inset:0;overflow:hidden;\";\n      nextEl.innerHTML = `\n        <div style=\"display:flex;height:100%;width:100%;flex-direction:column;align-items:center;justify-content:center;background:${nextPage.bg};box-sizing:border-box;\">\n          <p style=\"font-family:monospace;font-size:10px;text-transform:uppercase;letter-spacing:0.2em;color:${nextPage.accent};opacity:0.6;margin-bottom:1.25rem;\">${nextPage.label} — ${nextPage.path}</p>\n          <h2 style=\"color:white;font-size:clamp(3rem,8vw,5rem);font-weight:700;line-height:1;letter-spacing:-0.04em;margin:0 0 0.75rem;\">${nextPage.title}</h2>\n          <p style=\"color:rgba(255,255,255,0.3);font-size:0.8rem;letter-spacing:0.02em;\">${nextPage.subtitle}</p>\n        </div>\n      `;\n\n      wrapper.appendChild(nextEl);\n\n      const { variant: v, duration: d } = configRef.current;\n      const runner = v === \"slide\" ? runSlideTransition : runCurtainTransition;\n      await runner(currentEl, nextEl, d);\n\n      nextEl.remove();\n      gsap.set(currentEl, {\n        clearProps:\n          \"clipPath,position,top,left,width,height,zIndex,opacity,x,y,scale,transform\",\n      });\n\n      isTransitioningRef.current = false;\n    },\n    [currentIndex],\n  );\n\n  const page = PAGES[currentIndex];\n\n  return (\n    <div className=\"relative h-full w-full overflow-hidden\">\n      {/* Page viewport */}\n      <div\n        ref={wrapperRef}\n        className=\"absolute inset-0 overflow-hidden\"\n        style={{ backgroundColor: page.bg }}\n      >\n        {/* Current page — centred minimal layout */}\n        <div\n          ref={currentContainerRef}\n          className=\"absolute inset-0 overflow-hidden\"\n          data-namespace={page.namespace}\n          style={{ willChange: \"transform\", backfaceVisibility: \"hidden\" }}\n        >\n          <div\n            className=\"flex h-full w-full flex-col items-center justify-center\"\n            style={{ backgroundColor: page.bg }}\n          >\n            <p\n              style={{\n                fontFamily: \"monospace\",\n                fontSize: 10,\n                textTransform: \"uppercase\",\n                letterSpacing: \"0.2em\",\n                color: page.accent,\n                opacity: 0.6,\n                marginBottom: \"1.25rem\",\n              }}\n            >\n              {page.label} — {page.path}\n            </p>\n            <h2\n              style={{\n                color: \"white\",\n                fontSize: \"clamp(3rem, 8vw, 5rem)\",\n                fontWeight: 700,\n                lineHeight: 1,\n                letterSpacing: \"-0.04em\",\n                margin: \"0 0 0.75rem\",\n              }}\n            >\n              {page.title}\n            </h2>\n            <p\n              style={{\n                color: \"rgba(255,255,255,0.3)\",\n                fontSize: \"0.8rem\",\n                letterSpacing: \"0.02em\",\n              }}\n            >\n              {page.subtitle}\n            </p>\n          </div>\n        </div>\n      </div>\n\n      {/* Centered navbar overlay */}\n      <div className=\"pointer-events-none absolute inset-x-0 top-5 flex justify-center\">\n        <nav\n          className=\"pointer-events-auto flex items-center gap-1 rounded-full px-2 py-1.5\"\n          style={{\n            background: \"rgba(255,255,255,0.06)\",\n            backdropFilter: \"blur(12px)\",\n            border: \"1px solid rgba(255,255,255,0.08)\",\n          }}\n        >\n          {PAGES.map((p, i) => (\n            <button\n              key={p.namespace}\n              onClick={() => goTo(i)}\n              aria-label={`Go to ${p.title}`}\n              className=\"rounded-full px-4 py-1 text-xs transition-all duration-200\"\n              style={{\n                color: currentIndex === i ? \"white\" : \"rgba(255,255,255,0.4)\",\n                background:\n                  currentIndex === i ? \"rgba(255,255,255,0.1)\" : \"transparent\",\n                fontWeight: currentIndex === i ? 500 : 400,\n              }}\n            >\n              {p.title}\n            </button>\n          ))}\n        </nav>\n      </div>\n    </div>\n  );\n}\n\nexport { PageTransitionDemo };\n",
      "type": "registry:example"
    }
  ],
  "type": "registry:example"
}