{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "hero-lightRays",
  "title": "Hero · Light Rays",
  "description": "Hero · Light Rays section.",
  "dependencies": [
    "lucide-react",
    "three"
  ],
  "files": [
    {
      "path": "registry/talizen/hero/hero-lightRays/hero-lightRays.tsx",
      "content": "import React from 'react'\nimport { ArrowRight, CircleDotDashed } from 'lucide-react'\nimport LightRays from './LightRays'\n\nexport function HeroLightRays() {\n  return (\n    <section className=\"relative min-h-screen overflow-hidden bg-[#020407] px-6 py-14 text-white sm:px-8 lg:px-12\">\n      <LightRays\n        backgroundColor=\"#020407\"\n        intensity={28}\n        rays={42}\n        reach={56}\n        position={50}\n        raysColor={{ mode: 'multi', color1: '#6f96ff', color2: '#a7c3ff' }}\n        animation={{ animate: true, speed: 5 }}\n        className=\"opacity-95\"\n        style={{ zIndex: 0 }}\n      />\n\n      <div className=\"pointer-events-none absolute inset-0 z-[1] bg-[radial-gradient(circle_at_50%_0%,rgba(75,117,198,0.24),transparent_34%),linear-gradient(180deg,rgba(7,19,38,0.34)_0%,rgba(2,4,7,0.2)_42%,#020407_100%)]\" />\n      <div className=\"pointer-events-none absolute inset-0 z-[2] bg-[linear-gradient(rgba(255,255,255,0.035)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.035)_1px,transparent_1px)] bg-[size:64px_64px] [mask-image:linear-gradient(to_bottom,transparent_0%,black_22%,black_84%,transparent_100%)]\" />\n      <div className=\"pointer-events-none absolute inset-x-0 bottom-0 z-[3] h-1/2 bg-gradient-to-b from-transparent via-[#020407]/80 to-[#020407]\" />\n      <div className=\"pointer-events-none absolute inset-0 z-[4] bg-[radial-gradient(ellipse_at_center,transparent_0%,rgba(0,0,0,0.18)_52%,rgba(0,0,0,0.72)_100%)]\" />\n\n      <main className=\"relative z-10 mx-auto flex min-h-[calc(100vh-7rem)] max-w-5xl flex-col items-center justify-center pt-10 text-center\">\n        <div className=\"mb-8 inline-flex items-center gap-2 rounded-full border border-[#6fa8d8]/25 bg-[#10243e]/55 px-3.5 py-2 text-[11px] font-semibold uppercase tracking-[0.11em] text-slate-300 shadow-[0_0_30px_rgba(67,132,190,0.22)] backdrop-blur-md sm:text-xs\">\n          <CircleDotDashed className=\"h-4 w-4 animate-spin text-slate-400 [animation-duration:4s]\" />\n          <span>New</span>\n          <span className=\"text-white\">Creght</span>\n          <span>Component</span>\n        </div>\n\n        <h1 className=\"max-w-[880px] text-balance text-[3.35rem] font-semibold leading-[0.98] tracking-normal text-white sm:text-7xl lg:text-[5.25rem]\">\n          Animated{' '}\n          <span className=\"whitespace-nowrap font-serif text-[0.98em] font-normal italic text-white\">\n            Light Rays\n          </span>\n          <br />\n          Effect for Creght.\n        </h1>\n\n        <p className=\"mt-7 max-w-xl text-sm font-medium leading-6 text-slate-400 sm:text-base\">\n          Get more done with less effort, in a way that works for you.\n        </p>\n\n        <div className=\"mt-9 flex flex-col items-center justify-center gap-4 sm:flex-row sm:gap-6\">\n          <a\n            href=\"#\"\n            className=\"inline-flex h-12 min-w-36 items-center justify-center rounded-xl border border-[#6c94d9]/30 bg-[#10213c] px-6 text-sm font-semibold text-white shadow-[inset_0_1px_0_rgba(255,255,255,0.18),0_14px_38px_rgba(20,64,131,0.28)] transition duration-300 hover:border-[#91b3f7]/55 hover:bg-[#172b4b]\"\n          >\n            Remix this project\n          </a>\n          <a\n            href=\"#\"\n            className=\"inline-flex h-12 min-w-36 items-center justify-center gap-2 rounded-xl px-6 text-sm font-semibold text-slate-400 transition duration-300 hover:text-white\"\n          >\n            Learn more\n            <ArrowRight className=\"h-4 w-4\" />\n          </a>\n        </div>\n      </main>\n    </section>\n  )\n}\n\nexport default HeroLightRays\n\nexport const metadata = {\n  title: 'Animated Light Rays Effect for Creght',\n  description: 'Get more done with less effort, in a way that works for you.',\n}\n",
      "type": "registry:component"
    },
    {
      "path": "registry/talizen/hero/hero-lightRays/LightRays.tsx",
      "content": "\"use client\";\n\nimport { useRef, useEffect, useMemo, useState, type CSSProperties } from \"react\";\nimport * as THREE from \"three\";\n\ninterface AnimationConfig {\n  animate: boolean;\n  speed: number;\n}\n\ninterface SingleColorConfig {\n  mode: \"single\";\n  color: string;\n}\n\ninterface MultiColorConfig {\n  mode: \"multi\";\n  color1: string;\n  color2: string;\n}\n\ninterface RandomColorConfig {\n  mode: \"random\";\n}\n\ntype RaysColorConfig = SingleColorConfig | MultiColorConfig | RandomColorConfig;\n\ninterface RaysProps {\n  intensity?: number;\n  rays?: number;\n  reach?: number;\n  position?: number;\n  radius?: string;\n  backgroundColor?: string;\n  animation?: AnimationConfig;\n  raysColor?: RaysColorConfig;\n  style?: CSSProperties;\n  className?: string;\n}\n\nconst RAY_Y_POSITION_1 = -0.4;\nconst RAY_Y_POSITION_2 = -0.5;\n\nexport default function Rays({\n  intensity = 13,\n  rays = 32,\n  reach = 16,\n  position = 50,\n  radius = \"0px\",\n  backgroundColor = \"#000\",\n  animation = { animate: true, speed: 10 },\n  raysColor = { mode: \"single\", color: \"#639AFF\" },\n  style,\n  className,\n}: RaysProps) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const meshRef = useRef<THREE.Mesh | null>(null);\n  const frameIdRef = useRef<number | undefined>(undefined);\n  const animationRef = useRef<AnimationConfig>(animation);\n\n  const [isMounted, setIsMounted] = useState(false);\n  const [webglFailed, setWebglFailed] = useState(false);\n\n  useEffect(() => {\n    setIsMounted(true);\n    return () => setIsMounted(false);\n  }, []);\n\n  useEffect(() => {\n    animationRef.current = animation;\n  }, [animation]);\n\n  const [randomColor1RGB, randomColor2RGB] = useMemo(() => {\n    if (raysColor.mode === \"random\") {\n      const h = Math.random() * 360;\n      const s = 60 + Math.random() * 40;\n      return [hslToRgb(h, s, 50), hslToRgb(h, s, 65)];\n    }\n    return [\n      [1, 1, 1],\n      [1, 1, 1],\n    ] as [[number, number, number], [number, number, number]];\n  }, [raysColor.mode]);\n\n  const [color1RGB, color2RGB] = useMemo((): [\n    [number, number, number],\n    [number, number, number],\n  ] => {\n    if (raysColor.mode === \"random\") {\n      return [randomColor1RGB, randomColor2RGB] as [\n        [number, number, number],\n        [number, number, number],\n      ];\n    }\n\n    let color1 = \"#fff\";\n    let color2 = \"#fff\";\n\n    if (raysColor.mode === \"single\") {\n      color1 = raysColor.color;\n      color2 = raysColor.color;\n    } else if (raysColor.mode === \"multi\") {\n      color1 = raysColor.color1;\n      color2 = raysColor.color2;\n    }\n\n    return [colorToRGB(color1), colorToRGB(color2)];\n  }, [raysColor, randomColor1RGB, randomColor2RGB]);\n\n  useEffect(() => {\n    const container = containerRef.current;\n    if (!container || !isMounted) return;\n\n    const scene = new THREE.Scene();\n    const camera = new THREE.PerspectiveCamera(\n      75,\n      container.clientWidth / container.clientHeight,\n      0.1,\n      1000\n    );\n    camera.position.z = 5;\n\n    let renderer: THREE.WebGLRenderer;\n    try {\n      renderer = new THREE.WebGLRenderer({\n        preserveDrawingBuffer: true,\n        premultipliedAlpha: true,\n        alpha: true,\n        antialias: true,\n        precision: \"highp\",\n        powerPreference: \"high-performance\",\n      });\n      setWebglFailed(false);\n    } catch {\n      setWebglFailed(true);\n      return;\n    }\n\n    renderer.setSize(container.clientWidth, container.clientHeight);\n    renderer.setPixelRatio(1);\n    container.appendChild(renderer.domElement);\n\n    const geometry = new THREE.PlaneGeometry(1024, 1024);\n    const material = new THREE.ShaderMaterial({\n      fragmentShader: FRAGMENT_SHADER,\n      vertexShader: VERTEX_SHADER,\n      uniforms: {\n        u_colors: {\n          value: [\n            new THREE.Vector4(color1RGB[0], color1RGB[1], color1RGB[2], 1),\n            new THREE.Vector4(color2RGB[0], color2RGB[1], color2RGB[2], 1),\n          ],\n        },\n        u_intensity: { value: mapRange(intensity, 0, 100, 0, 0.5) },\n        u_rays: { value: mapRange(rays, 0, 100, 0, 0.3) },\n        u_reach: { value: mapRange(reach, 0, 100, 0, 0.5) },\n        u_time: { value: Math.random() * 10000 },\n        u_mouse: { value: [0, 0] },\n        u_resolution: {\n          value: [container.clientWidth, container.clientHeight],\n        },\n        u_rayPos1: {\n          value: [\n            (position / 100) * container.clientWidth,\n            RAY_Y_POSITION_1 * container.clientHeight,\n          ],\n        },\n        u_rayPos2: {\n          value: [\n            (position / 100 + 0.02) * container.clientWidth,\n            RAY_Y_POSITION_2 * container.clientHeight,\n          ],\n        },\n      },\n      wireframe: false,\n      dithering: false,\n      side: THREE.DoubleSide,\n    });\n\n    const mesh = new THREE.Mesh(geometry, material);\n    scene.add(mesh);\n\n    meshRef.current = mesh;\n\n    let lastTime = 0;\n    const animate = (time: number) => {\n      const anim = animationRef.current;\n      if (!anim.animate) {\n        lastTime = time;\n      }\n\n      const delta = time - lastTime;\n      lastTime = time;\n\n      if (mesh.material instanceof THREE.ShaderMaterial) {\n        if (anim.animate) {\n          mesh.material.uniforms.u_time.value +=\n            (delta * anim.speed) / 1000 / 10;\n        }\n      }\n\n      renderer.render(scene, camera);\n      frameIdRef.current = requestAnimationFrame(animate);\n    };\n\n    frameIdRef.current = requestAnimationFrame(animate);\n\n    return () => {\n      if (frameIdRef.current !== undefined) {\n        cancelAnimationFrame(frameIdRef.current);\n      }\n      renderer.dispose();\n      geometry.dispose();\n      material.dispose();\n      if (container.contains(renderer.domElement)) {\n        container.removeChild(renderer.domElement);\n      }\n    };\n  }, [color1RGB, color2RGB, intensity, isMounted, position, rays, reach]);\n\n  useEffect(() => {\n    if (meshRef.current?.material instanceof THREE.ShaderMaterial) {\n      const material = meshRef.current.material;\n      const container = containerRef.current;\n      if (!container) return;\n\n      material.uniforms.u_colors.value = [\n        new THREE.Vector4(color1RGB[0], color1RGB[1], color1RGB[2], 1),\n        new THREE.Vector4(color2RGB[0], color2RGB[1], color2RGB[2], 1),\n      ];\n      material.uniforms.u_intensity.value = mapRange(intensity, 0, 100, 0, 0.5);\n      material.uniforms.u_rays.value = mapRange(rays, 0, 100, 0, 0.3);\n      material.uniforms.u_reach.value = mapRange(reach, 0, 100, 0, 0.5);\n      material.uniforms.u_rayPos1.value = [\n        (position / 100) * container.clientWidth,\n        RAY_Y_POSITION_1 * container.clientHeight,\n      ];\n      material.uniforms.u_rayPos2.value = [\n        (position / 100 + 0.02) * container.clientWidth,\n        RAY_Y_POSITION_2 * container.clientHeight,\n      ];\n    }\n  }, [intensity, rays, reach, position, color1RGB, color2RGB]);\n\n  const fallbackColor =\n    raysColor.mode === \"multi\"\n      ? raysColor.color1\n      : raysColor.mode === \"single\"\n        ? raysColor.color\n        : \"#639AFF\";\n\n  return (\n    <div\n      ref={containerRef}\n      className={className}\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        zIndex: -1,\n        borderRadius: radius,\n        overflow: \"hidden\",\n        backgroundColor,\n        ...style,\n      }}\n    >\n      {webglFailed ? (\n        <div\n          aria-hidden=\"true\"\n          style={{\n            position: \"absolute\",\n            inset: 0,\n            background: `\n              linear-gradient(104deg, transparent 0%, transparent 34%, ${fallbackColor}32 44%, transparent 58%),\n              linear-gradient(78deg, transparent 0%, transparent 37%, ${fallbackColor}2b 48%, transparent 64%),\n              linear-gradient(94deg, transparent 0%, transparent 45%, ${fallbackColor}3d 51%, transparent 60%),\n              radial-gradient(circle at ${position}% -12%, ${fallbackColor}8a 0%, ${fallbackColor}34 30%, transparent 58%),\n              conic-gradient(from 205deg at ${position}% -18%, transparent 0deg, ${fallbackColor}40 20deg, transparent 38deg, transparent 58deg, ${fallbackColor}36 78deg, transparent 106deg),\n              linear-gradient(${backgroundColor}, ${backgroundColor})\n            `,\n            filter: \"blur(0.3px)\",\n            opacity: Math.min(1, Math.max(0.35, intensity / 36)),\n          }}\n        />\n      ) : null}\n    </div>\n  );\n}\n\nfunction colorToRGB(hex: string): [number, number, number] {\n  let r = 1,\n    g = 1,\n    b = 1;\n\n  if (hex.startsWith(\"rgba(\")) {\n    const parts = hex.slice(5, -1).split(\",\");\n    r = parseInt(parts[0]) / 255;\n    g = parseInt(parts[1]) / 255;\n    b = parseInt(parts[2]) / 255;\n  } else if (hex.startsWith(\"rgb(\")) {\n    const parts = hex.slice(4, -1).split(\",\");\n    r = parseInt(parts[0]) / 255;\n    g = parseInt(parts[1]) / 255;\n    b = parseInt(parts[2]) / 255;\n  } else if (hex.startsWith(\"#\")) {\n    const c = hex.slice(1);\n    if (c.length === 3) {\n      r = parseInt(c[0] + c[0], 16) / 255;\n      g = parseInt(c[1] + c[1], 16) / 255;\n      b = parseInt(c[2] + c[2], 16) / 255;\n    } else if (c.length >= 6) {\n      r = parseInt(c.slice(0, 2), 16) / 255;\n      g = parseInt(c.slice(2, 4), 16) / 255;\n      b = parseInt(c.slice(4, 6), 16) / 255;\n    }\n  }\n\n  return [r, g, b];\n}\n\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n  s /= 100;\n  l /= 100;\n  const c = (1 - Math.abs(2 * l - 1)) * s;\n  const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n  const m = l - c / 2;\n  let r = 0,\n    g = 0,\n    b = 0;\n\n  if (h >= 0 && h < 60) {\n    r = c;\n    g = x;\n    b = 0;\n  } else if (h >= 60 && h < 120) {\n    r = x;\n    g = c;\n    b = 0;\n  } else if (h >= 120 && h < 180) {\n    r = 0;\n    g = c;\n    b = x;\n  } else if (h >= 180 && h < 240) {\n    r = 0;\n    g = x;\n    b = c;\n  } else if (h >= 240 && h < 300) {\n    r = x;\n    g = 0;\n    b = c;\n  } else if (h >= 300 && h < 360) {\n    r = c;\n    g = 0;\n    b = x;\n  }\n\n  return [r + m, g + m, b + m];\n}\n\nfunction mapRange(\n  value: number,\n  fromLow: number,\n  fromHigh: number,\n  toLow: number,\n  toHigh: number\n): number {\n  const percentage = (value - fromLow) / (fromHigh - fromLow);\n  return toLow + percentage * (toHigh - toLow);\n}\n\nconst VERTEX_SHADER = `\nvoid main() {\n  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\nconst FRAGMENT_SHADER = `\nuniform vec2 u_resolution;\nuniform vec2 u_mouse;\nuniform float u_time;\nuniform vec4 u_colors[2];\nuniform float u_intensity;\nuniform float u_rays;\nuniform float u_reach;\nuniform vec2 u_rayPos1;\nuniform vec2 u_rayPos2;\n\nfloat rayStrength(vec2 raySource, vec2 rayRefDirection, vec2 coord, float seedA, float seedB, float speed) {\n    vec2 sourceToCoord = coord - raySource;\n    float cosAngle = dot(normalize(sourceToCoord), rayRefDirection);\n    float diagonal = length(u_resolution);\n\n    return clamp(\n        (.45 + 0.15 * sin(cosAngle * seedA + u_time * speed)) +\n        (0.3 + 0.2 * cos(-cosAngle * seedB + u_time * speed)),\n        u_reach, 1.0) *\n        clamp((diagonal - length(sourceToCoord)) / diagonal, u_reach, 1.0);\n}\n\nvoid main() {\n    vec2 uv = gl_FragCoord.xy / u_resolution.xy;\n    uv.y = 1.0 - uv.y;\n    vec2 coord = vec2(gl_FragCoord.x, u_resolution.y - gl_FragCoord.y);\n    float speed = u_rays * 10.0;\n\n    vec2 rayPos1 = u_rayPos1;\n    vec2 rayRefDir1 = normalize(vec2(1.0, -0.116));\n    float raySeedA1 = 36.2214 * speed;\n    float raySeedB1 = 21.11349 * speed;\n    float raySpeed1 = 1.5 * speed;\n\n    vec2 rayPos2 = u_rayPos2;\n    vec2 rayRefDir2 = normalize(vec2(1.0, 0.241));\n    float raySeedA2 = 22.39910 * speed;\n    float raySeedB2 = 18.0234 * speed;\n    float raySpeed2 = 1.1 * speed;\n\n    float strength1 = rayStrength(rayPos1, rayRefDir1, coord, raySeedA1, raySeedB1, raySpeed1);\n    float strength2 = rayStrength(rayPos2, rayRefDir2, coord, raySeedA2, raySeedB2, raySpeed2);\n\n    float brightness = 1.0 * u_reach - (coord.y / u_resolution.y);\n    float attenuation = clamp(brightness + (0.5 + u_intensity), 0.0, 1.0);\n\n    float alpha1 = strength1 * attenuation * u_colors[0].a;\n    float alpha2 = strength2 * attenuation * u_colors[1].a;\n\n    vec3 premultColor1 = u_colors[0].rgb * alpha1;\n    vec3 premultColor2 = u_colors[1].rgb * alpha2;\n\n    vec3 blendedColor = premultColor1 + premultColor2;\n    float blendedAlpha = alpha1 + alpha2 * (1.0 - alpha1);\n\n    vec3 finalRGB = blendedColor / max(blendedAlpha, 0.0001);\n\n    gl_FragColor = vec4(finalRGB * blendedAlpha, blendedAlpha);\n}\n`;\n",
      "type": "registry:component"
    }
  ],
  "categories": [
    "hero"
  ],
  "type": "registry:block"
}