/* React Bits: ClickSpark, SpotlightCard, Magnet, DecryptedText, ShinyText, GlareHover.
   Vendored JS-CSS for Babel standalone. https://reactbits.dev */
(function () {
  const { useRef, useEffect, useState, useCallback } = React;
  const reduceMotion = () => window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;

  function ClickSpark({
    sparkColor = '#E8B33B',
    sparkSize = 10,
    sparkRadius = 22,
    sparkCount = 10,
    duration = 420,
    extraScale = 1,
    children,
  }) {
    const canvasRef = useRef(null);
    const sparksRef = useRef([]);
    useEffect(() => {
      const canvas = canvasRef.current;
      if (!canvas) return;
      const parent = canvas.parentElement;
      if (!parent) return;
      const resize = () => {
        const { width, height } = parent.getBoundingClientRect();
        if (canvas.width !== width || canvas.height !== height) {
          canvas.width = Math.max(1, Math.floor(width));
          canvas.height = Math.max(1, Math.floor(height));
        }
      };
      const ro = new ResizeObserver(resize);
      ro.observe(parent);
      resize();
      let id;
      const draw = (ts) => {
        const ctx = canvas.getContext('2d');
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        sparksRef.current = sparksRef.current.filter((spark) => {
          const elapsed = ts - spark.startTime;
          if (elapsed >= duration) return false;
          const t = elapsed / duration;
          const eased = t * (2 - t);
          const distance = eased * sparkRadius * extraScale;
          const lineLength = sparkSize * (1 - eased);
          ctx.strokeStyle = sparkColor;
          ctx.lineWidth = 1.5;
          ctx.beginPath();
          ctx.moveTo(spark.x + distance * Math.cos(spark.angle), spark.y + distance * Math.sin(spark.angle));
          ctx.lineTo(
            spark.x + (distance + lineLength) * Math.cos(spark.angle),
            spark.y + (distance + lineLength) * Math.sin(spark.angle)
          );
          ctx.stroke();
          return true;
        });
        id = requestAnimationFrame(draw);
      };
      id = requestAnimationFrame(draw);
      return () => { ro.disconnect(); cancelAnimationFrame(id); };
    }, [sparkColor, sparkSize, sparkRadius, sparkCount, duration, extraScale]);

    const onClick = (e) => {
      if (reduceMotion()) return;
      const canvas = canvasRef.current;
      if (!canvas) return;
      const rect = canvas.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;
      const now = performance.now();
      sparksRef.current.push(...Array.from({ length: sparkCount }, (_, i) => ({
        x, y, angle: (2 * Math.PI * i) / sparkCount, startTime: now,
      })));
    };

    return (
      <div className="rb-clickspark" onClick={onClick}>
        <canvas ref={canvasRef} className="rb-clickspark__canvas" aria-hidden="true" />
        {children}
      </div>
    );
  }

  function SpotlightCard({ children, className = '', spotlightColor = 'rgba(232,179,59,.18)', style }) {
    const ref = useRef(null);
    const onMove = (e) => {
      const el = ref.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      el.style.setProperty('--mouse-x', e.clientX - r.left + 'px');
      el.style.setProperty('--mouse-y', e.clientY - r.top + 'px');
      el.style.setProperty('--spotlight-color', spotlightColor);
    };
    return (
      <div ref={ref} onMouseMove={onMove} className={'card-spotlight ' + className} style={style}>
        {children}
      </div>
    );
  }

  function Magnet({ children, padding = 48, magnetStrength = 8, disabled }) {
    const ref = useRef(null);
    const [pos, setPos] = useState({ x: 0, y: 0 });
    const [active, setActive] = useState(false);
    useEffect(() => {
      if (disabled || reduceMotion()) { setPos({ x: 0, y: 0 }); return; }
      const move = (e) => {
        const el = ref.current;
        if (!el) return;
        const { left, top, width, height } = el.getBoundingClientRect();
        const cx = left + width / 2;
        const cy = top + height / 2;
        if (Math.abs(cx - e.clientX) < width / 2 + padding && Math.abs(cy - e.clientY) < height / 2 + padding) {
          setActive(true);
          setPos({ x: (e.clientX - cx) / magnetStrength, y: (e.clientY - cy) / magnetStrength });
        } else {
          setActive(false);
          setPos({ x: 0, y: 0 });
        }
      };
      window.addEventListener('mousemove', move);
      return () => window.removeEventListener('mousemove', move);
    }, [padding, magnetStrength, disabled]);
    return (
      <div ref={ref} style={{ position: 'relative', display: 'inline-block' }}>
        <div style={{
          transform: 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)',
          transition: active ? 'transform .25s ease-out' : 'transform .45s ease-in-out',
          willChange: 'transform',
        }}>{children}</div>
      </div>
    );
  }

  function DecryptedText({
    text,
    speed = 32,
    maxIterations = 8,
    animateOn = 'view',
    characters = '01ABCDEF<>/$#',
    className = '',
    style,
  }) {
    const [display, setDisplay] = useState(text);
    const [done, setDone] = useState(reduceMotion());
    const ref = useRef(null);
    const run = useCallback(() => {
      if (reduceMotion()) { setDisplay(text); setDone(true); return; }
      let n = 0;
      const id = setInterval(() => {
        n += 1;
        if (n >= maxIterations) {
          clearInterval(id);
          setDisplay(text);
          setDone(true);
          return;
        }
        setDisplay(text.split('').map((ch) => {
          if (ch === ' ' || ch === '/') return ch;
          return characters[Math.floor(Math.random() * characters.length)];
        }).join(''));
      }, speed);
      return () => clearInterval(id);
    }, [text, speed, maxIterations, characters]);

    useEffect(() => {
      if (animateOn === 'mount') return run();
      if (animateOn !== 'view') return;
      const el = ref.current;
      if (!el) return;
      const io = new IntersectionObserver((entries) => {
        entries.forEach((en) => { if (en.isIntersecting) { run(); io.disconnect(); } });
      }, { threshold: 0.2 });
      io.observe(el);
      return () => io.disconnect();
    }, [animateOn, run]);

    return (
      <span ref={ref} className={className} style={style} onMouseEnter={animateOn === 'hover' ? run : undefined}>
        <span className="rb-decrypt-sr">{text}</span>
        <span aria-hidden="true" style={{ fontVariantNumeric: 'tabular-nums' }}>{display}</span>
      </span>
    );
  }

  function ShinyText({ text, className = '', style }) {
    return <span className={'shiny-text ' + className} style={style}>{text}</span>;
  }

  function GlareHover({ children, className = '', style, glareColor = '#E8B33B', glareOpacity = 0.22 }) {
    const hex = glareColor.replace('#', '');
    const r = parseInt(hex.slice(0, 2), 16);
    const g = parseInt(hex.slice(2, 4), 16);
    const b = parseInt(hex.slice(4, 6), 16);
    return (
      <div
        className={'glare-hover ' + className}
        style={Object.assign({
          '--gh-rgba': 'rgba(' + r + ',' + g + ',' + b + ',' + glareOpacity + ')',
          '--gh-duration': '650ms',
          '--gh-size': '180%',
          '--gh-angle': '-45deg',
        }, style)}
      >{children}</div>
    );
  }

  window.ClickSpark = ClickSpark;
  window.SpotlightCard = SpotlightCard;
  window.Magnet = Magnet;
  window.DecryptedText = DecryptedText;
  window.ShinyText = ShinyText;
  window.GlareHover = GlareHover;
})();
