{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "GradualBlur-TS-CSS",
	"title": "GradualBlur",
	"description": "Progressively un-blurs content based on scroll or trigger creating a cinematic reveal.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "GradualBlur.css",
			"target": "@components/GradualBlur.css",
			"content": ".gradual-blur-inner {\n  position: relative;\n  width: 100%;\n  height: 100%;\n}\n\n.gradual-blur-inner > div {\n  -webkit-backdrop-filter: inherit;\n  backdrop-filter: inherit;\n}\n\n.gradual-blur {\n  isolation: isolate;\n}\n\n@supports not (backdrop-filter: blur(1px)) {\n  .gradual-blur-inner > div {\n    background: rgba(0, 0, 0, 0.3);\n    opacity: 0.5;\n  }\n}\n\n.gradual-blur-fixed {\n  position: fixed !important;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  pointer-events: none;\n  z-index: 1000;\n}\n"
		},
		{
			"type": "registry:component",
			"path": "GradualBlur.tsx",
			"content": "import React, { type CSSProperties, useEffect, useRef, useState, useMemo, type PropsWithChildren } from 'react';\n\nimport './GradualBlur.css';\n\ntype GradualBlurProps = {\n  position?: 'top' | 'bottom' | 'left' | 'right';\n  strength?: number;\n  height?: string;\n  width?: string;\n  divCount?: number;\n  exponential?: boolean;\n  zIndex?: number;\n  animated?: boolean | 'scroll';\n  duration?: string;\n  easing?: string;\n  opacity?: number;\n  curve?: 'linear' | 'bezier' | 'ease-in' | 'ease-out' | 'ease-in-out';\n  responsive?: boolean;\n  mobileHeight?: string;\n  tabletHeight?: string;\n  desktopHeight?: string;\n  mobileWidth?: string;\n  tabletWidth?: string;\n  desktopWidth?: string;\n  preset?:\n    | 'top'\n    | 'bottom'\n    | 'left'\n    | 'right'\n    | 'subtle'\n    | 'intense'\n    | 'smooth'\n    | 'sharp'\n    | 'header'\n    | 'footer'\n    | 'sidebar'\n    | 'page-header'\n    | 'page-footer';\n  gpuOptimized?: boolean;\n  hoverIntensity?: number;\n  target?: 'parent' | 'page';\n  onAnimationComplete?: () => void;\n  className?: string;\n  style?: CSSProperties;\n};\n\nconst DEFAULT_CONFIG: Partial<GradualBlurProps> = {\n  position: 'bottom',\n  strength: 2,\n  height: '6rem',\n  divCount: 5,\n  exponential: false,\n  zIndex: 1000,\n  animated: false,\n  duration: '0.3s',\n  easing: 'ease-out',\n  opacity: 1,\n  curve: 'linear',\n  responsive: false,\n  target: 'parent',\n  className: '',\n  style: {}\n};\n\nconst PRESETS: Record<string, Partial<GradualBlurProps>> = {\n  top: { position: 'top', height: '6rem' },\n  bottom: { position: 'bottom', height: '6rem' },\n  left: { position: 'left', height: '6rem' },\n  right: { position: 'right', height: '6rem' },\n  subtle: { height: '4rem', strength: 1, opacity: 0.8, divCount: 3 },\n  intense: { height: '10rem', strength: 4, divCount: 8, exponential: true },\n  smooth: { height: '8rem', curve: 'bezier', divCount: 10 },\n  sharp: { height: '5rem', curve: 'linear', divCount: 4 },\n  header: { position: 'top', height: '8rem', curve: 'ease-out' },\n  footer: { position: 'bottom', height: '8rem', curve: 'ease-out' },\n  sidebar: { position: 'left', height: '6rem', strength: 2.5 },\n  'page-header': {\n    position: 'top',\n    height: '10rem',\n    target: 'page',\n    strength: 3\n  },\n  'page-footer': {\n    position: 'bottom',\n    height: '10rem',\n    target: 'page',\n    strength: 3\n  }\n};\n\nconst CURVE_FUNCTIONS: Record<string, (p: number) => number> = {\n  linear: p => p,\n  bezier: p => p * p * (3 - 2 * p),\n  'ease-in': p => p * p,\n  'ease-out': p => 1 - Math.pow(1 - p, 2),\n  'ease-in-out': p => (p < 0.5 ? 2 * p * p : 1 - Math.pow(-2 * p + 2, 2) / 2)\n};\n\nconst mergeConfigs = (...configs: Partial<GradualBlurProps>[]): Partial<GradualBlurProps> => {\n  return configs.reduce((acc, config) => ({ ...acc, ...config }), {});\n};\n\nconst getGradientDirection = (position: string): string => {\n  const directions: Record<string, string> = {\n    top: 'to top',\n    bottom: 'to bottom',\n    left: 'to left',\n    right: 'to right'\n  };\n  return directions[position] || 'to bottom';\n};\n\nconst debounce = <T extends (...a: any[]) => void>(fn: T, wait: number) => {\n  let t: ReturnType<typeof setTimeout>;\n  return (...a: Parameters<T>) => {\n    clearTimeout(t);\n    t = setTimeout(() => fn(...a), wait);\n  };\n};\n\nconst useResponsiveDimension = (\n  responsive: boolean | undefined,\n  config: Partial<GradualBlurProps>,\n  key: keyof GradualBlurProps\n) => {\n  const [val, setVal] = useState<any>(config[key]);\n  useEffect(() => {\n    if (!responsive) return;\n    const calc = () => {\n      const w = window.innerWidth;\n      let v: any = config[key];\n      const cap = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);\n      const k = cap(key as string);\n      if (w <= 480 && (config as any)['mobile' + k]) v = (config as any)['mobile' + k];\n      else if (w <= 768 && (config as any)['tablet' + k]) v = (config as any)['tablet' + k];\n      else if (w <= 1024 && (config as any)['desktop' + k]) v = (config as any)['desktop' + k];\n      setVal(v);\n    };\n    const deb = debounce(calc, 100);\n    calc();\n    window.addEventListener('resize', deb);\n    return () => window.removeEventListener('resize', deb);\n  }, [responsive, config, key]);\n  return responsive ? val : (config as any)[key];\n};\n\nconst useIntersectionObserver = (ref: React.RefObject<HTMLDivElement | null>, shouldObserve: boolean = false) => {\n  const [isVisible, setIsVisible] = useState(!shouldObserve);\n\n  useEffect(() => {\n    if (!shouldObserve || !ref.current) return;\n\n    const observer = new IntersectionObserver(([entry]) => setIsVisible(entry.isIntersecting), { threshold: 0.1 });\n\n    observer.observe(ref.current);\n    return () => observer.disconnect();\n  }, [ref, shouldObserve]);\n\n  return isVisible;\n};\n\nconst GradualBlur: React.FC<PropsWithChildren<GradualBlurProps>> = props => {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const [isHovered, setIsHovered] = useState(false);\n\n  const config = useMemo(() => {\n    const presetConfig = props.preset && PRESETS[props.preset] ? PRESETS[props.preset] : {};\n    return mergeConfigs(DEFAULT_CONFIG, presetConfig, props) as Required<GradualBlurProps>;\n  }, [props]);\n\n  const responsiveHeight = useResponsiveDimension(config.responsive, config, 'height');\n  const responsiveWidth = useResponsiveDimension(config.responsive, config, 'width');\n\n  const isVisible = useIntersectionObserver(containerRef, config.animated === 'scroll');\n\n  const blurDivs = useMemo(() => {\n    const divs: React.ReactNode[] = [];\n    const increment = 100 / config.divCount;\n    const currentStrength =\n      isHovered && config.hoverIntensity ? config.strength * config.hoverIntensity : config.strength;\n\n    const curveFunc = CURVE_FUNCTIONS[config.curve] || CURVE_FUNCTIONS.linear;\n\n    for (let i = 1; i <= config.divCount; i++) {\n      let progress = i / config.divCount;\n      progress = curveFunc(progress);\n\n      let blurValue: number;\n      if (config.exponential) {\n        blurValue = Number(Math.pow(2, progress * 4)) * 0.0625 * currentStrength;\n      } else {\n        blurValue = 0.0625 * (progress * config.divCount + 1) * currentStrength;\n      }\n\n      const p1 = Math.round((increment * i - increment) * 10) / 10;\n      const p2 = Math.round(increment * i * 10) / 10;\n      const p3 = Math.round((increment * i + increment) * 10) / 10;\n      const p4 = Math.round((increment * i + increment * 2) * 10) / 10;\n\n      let gradient = `transparent ${p1}%, black ${p2}%`;\n      if (p3 <= 100) gradient += `, black ${p3}%`;\n      if (p4 <= 100) gradient += `, transparent ${p4}%`;\n\n      const direction = getGradientDirection(config.position);\n\n      const divStyle: CSSProperties = {\n        position: 'absolute',\n        inset: '0',\n        maskImage: `linear-gradient(${direction}, ${gradient})`,\n        WebkitMaskImage: `linear-gradient(${direction}, ${gradient})`,\n        backdropFilter: `blur(${blurValue.toFixed(3)}rem)`,\n        WebkitBackdropFilter: `blur(${blurValue.toFixed(3)}rem)`,\n        opacity: config.opacity,\n        transition:\n          config.animated && config.animated !== 'scroll'\n            ? `backdrop-filter ${config.duration} ${config.easing}`\n            : undefined\n      };\n\n      divs.push(<div key={i} style={divStyle} />);\n    }\n\n    return divs;\n  }, [config, isHovered]);\n\n  const containerStyle: CSSProperties = useMemo(() => {\n    const isVertical = ['top', 'bottom'].includes(config.position);\n    const isHorizontal = ['left', 'right'].includes(config.position);\n    const isPageTarget = config.target === 'page';\n\n    const baseStyle: CSSProperties = {\n      position: isPageTarget ? 'fixed' : 'absolute',\n      pointerEvents: config.hoverIntensity ? 'auto' : 'none',\n      opacity: isVisible ? 1 : 0,\n      transition: config.animated ? `opacity ${config.duration} ${config.easing}` : undefined,\n      zIndex: isPageTarget ? config.zIndex + 100 : config.zIndex,\n      ...config.style\n    };\n\n    if (isVertical) {\n      baseStyle.height = responsiveHeight;\n      baseStyle.width = responsiveWidth || '100%';\n      baseStyle[config.position] = 0;\n      baseStyle.left = 0;\n      baseStyle.right = 0;\n    } else if (isHorizontal) {\n      baseStyle.width = responsiveWidth || responsiveHeight;\n      baseStyle.height = '100%';\n      baseStyle[config.position] = 0;\n      baseStyle.top = 0;\n      baseStyle.bottom = 0;\n    }\n\n    return baseStyle;\n  }, [config, responsiveHeight, responsiveWidth, isVisible]);\n\n  const { hoverIntensity, animated, onAnimationComplete, duration } = config as any;\n  useEffect(() => {\n    if (isVisible && animated === 'scroll' && onAnimationComplete) {\n      const t = setTimeout(() => onAnimationComplete(), parseFloat(duration) * 1000);\n      return () => clearTimeout(t);\n    }\n  }, [isVisible, animated, onAnimationComplete, duration]);\n\n  return (\n    <div\n      ref={containerRef}\n      className={`gradual-blur ${config.target === 'page' ? 'gradual-blur-page' : 'gradual-blur-parent'} ${config.className}`}\n      style={containerStyle}\n      onMouseEnter={hoverIntensity ? () => setIsHovered(true) : undefined}\n      onMouseLeave={hoverIntensity ? () => setIsHovered(false) : undefined}\n    >\n      <div\n        className=\"gradual-blur-inner\"\n        style={{\n          position: 'relative',\n          width: '100%',\n          height: '100%'\n        }}\n      >\n        {blurDivs}\n      </div>\n    </div>\n  );\n};\n\nconst GradualBlurMemo = React.memo(GradualBlur);\nGradualBlurMemo.displayName = 'GradualBlur';\n(GradualBlurMemo as any).PRESETS = PRESETS;\n(GradualBlurMemo as any).CURVE_FUNCTIONS = CURVE_FUNCTIONS;\nexport default GradualBlurMemo;\n\nconst injectStyles = () => {\n  if (typeof document === 'undefined') return;\n  const styleId = 'gradual-blur-styles';\n  if (document.getElementById(styleId)) return;\n  const styleElement = document.createElement('style');\n  styleElement.id = styleId;\n  styleElement.textContent = `.gradual-blur{pointer-events:none;transition:opacity 0.3s ease-out}.gradual-blur-inner{pointer-events:none}`;\n  document.head.appendChild(styleElement);\n};\n\nif (typeof document !== 'undefined') {\n  injectStyles();\n}\n"
		}
	],
	"registryDependencies": [],
	"dependencies": []
}