// ff-utils.jsx — small shared helpers + logo mark

const { useState, useEffect, useRef, useLayoutEffect, useMemo, useCallback } = React;

// Reveal-on-scroll wrapper. Adds .in once the element enters the viewport.
function Reveal({ as = "div", delay = 0, threshold = 0.15, className = "", style, children, ...rest }) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    // Fallback — if IO never fires (some embed contexts), still reveal.
    const fallback = setTimeout(() => setSeen(true), 1200);
    if (typeof IntersectionObserver === "undefined") {
      setSeen(true);
      return () => clearTimeout(fallback);
    }
    const io = new IntersectionObserver(
      (entries) => {
        for (const e of entries) {
          if (e.isIntersecting) {
            setSeen(true);
            clearTimeout(fallback);
            io.disconnect();
          }
        }
      },
      { threshold, rootMargin: "0px 0px -40px 0px" }
    );
    io.observe(el);
    // Manual check after a tick in case IO is slow / inert.
    const tick = setTimeout(() => {
      const r = el.getBoundingClientRect();
      if (r.top < window.innerHeight && r.bottom > 0) {
        setSeen(true);
        clearTimeout(fallback);
        io.disconnect();
      }
    }, 50);
    return () => {
      io.disconnect();
      clearTimeout(fallback);
      clearTimeout(tick);
    };
  }, [threshold]);
  const Tag = as;
  const cls = `reveal ${delay ? "delay-" + delay : ""} ${seen ? "in" : ""} ${className}`;
  return (
    <Tag ref={ref} className={cls} style={style} {...rest}>
      {children}
    </Tag>
  );
}

// Animated counter — counts to `to` once visible.
function Counter({ to, duration = 1400, prefix = "", suffix = "", className, style, format }) {
  const ref = useRef(null);
  const [v, setV] = useState(0);
  const started = useRef(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const start = () => {
      if (started.current) return;
      started.current = true;
      const t0 = performance.now();
      const tick = (now) => {
        const t = Math.min(1, (now - t0) / duration);
        const eased = 1 - Math.pow(1 - t, 3);
        setV(Math.round(to * eased));
        if (t < 1) requestAnimationFrame(tick);
      };
      requestAnimationFrame(tick);
    };
    // Fallback — always start the counter even if IO never fires.
    const fallback = setTimeout(start, 1200);
    if (typeof IntersectionObserver === "undefined") {
      start();
      return () => clearTimeout(fallback);
    }
    const io = new IntersectionObserver((entries) => {
      for (const e of entries) {
        if (e.isIntersecting) {
          clearTimeout(fallback);
          start();
        }
      }
    }, { threshold: 0.4 });
    io.observe(el);
    const tick = setTimeout(() => {
      const r = el.getBoundingClientRect();
      if (r.top < window.innerHeight && r.bottom > 0) {
        clearTimeout(fallback);
        start();
      }
    }, 50);
    return () => {
      io.disconnect();
      clearTimeout(fallback);
      clearTimeout(tick);
    };
  }, [to, duration]);
  const display = format ? format(v) : v.toLocaleString();
  return <span ref={ref} className={className} style={style}>{prefix}{display}{suffix}</span>;
}

// Logo mark — interlocking arc + flow-line glyph
function LogoMark({ size = 22, color = "currentColor", accent = "var(--accent)" }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <circle cx="12" cy="12" r="10.5" stroke={color} strokeOpacity="0.35" strokeWidth="1" />
      <path
        d="M5 14 C 9 14, 9 8, 13 8 C 17 8, 17 14, 19 14"
        stroke={accent}
        strokeWidth="1.6"
        strokeLinecap="round"
        fill="none"
      />
      <circle cx="5" cy="14" r="1.6" fill={accent} />
      <circle cx="19" cy="14" r="1.6" fill={color} />
    </svg>
  );
}

// Section marker — animated accent rule + accent label. Index kept in signature for back-compat (rendered as aria only).
function SectionEyebrow({ index, label }) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el || seen) return;
    if (typeof IntersectionObserver === "undefined") { setSeen(true); return; }
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => { if (e.isIntersecting) { setSeen(true); io.disconnect(); } });
      },
      { threshold: 0.5, rootMargin: "0px 0px -10% 0px" }
    );
    io.observe(el);
    return () => io.disconnect();
  }, [seen]);
  return (
    <div ref={ref} className={`eyebrow${seen ? " is-in" : ""}`} aria-label={label}>
      <span className="eyebrow-label">{label}</span>
    </div>
  );
}

// Font pairings
const FONT_PAIRS = {
  geist:   { sans: '"Geist"',         mono: '"Geist Mono"' },
  manrope: { sans: '"Manrope"',       mono: '"JetBrains Mono"' },
  inter:   { sans: '"Inter Tight"',   mono: '"IBM Plex Mono"' },
  space:   { sans: '"Space Grotesk"', mono: '"Space Mono"' },
};

// Full wordmark — "WW Wayrelay". Blue glyph tracks --accent; wordmark uses currentColor.
function BrandLogo({ height = 22, className = "", style = {} }) {
  return (
    <svg
      className={`brand-logo ${className}`}
      height={height}
      viewBox="0 0 180 28"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
      aria-label="Wayrelay"
      style={style}
    >
      <path d="M39 23.3448L35.7209 26.3954L28.4302 19.2598L19.5 28L10.5698 19.2598L3.27907 26.3954L0 23.3448L10.5698 13L19.5 21.7402L28.4302 13L39 23.3448Z" fill="currentColor"/>
      <path d="M39 10.3448L35.7209 13.3954L28.4302 6.25982L19.5 15L10.5698 6.25982L3.27907 13.3954L0 10.3448L10.5698 0L19.5 8.74018L28.4302 0L39 10.3448Z" fill="var(--accent)"/>
      <text x="52" y="22" fill="currentColor" fontFamily="var(--font-sans)" fontSize="22" fontWeight="600" letterSpacing="-0.02em">Wayrelay</text>
    </svg>
  );
}

// Icon-only mark — the stacked W glyph, monochrome.
function BrandIcon({ height = 28, className = "", style = {} }) {
  return (
    <svg
      className={`brand-icon ${className}`}
      height={height}
      viewBox="0 0 39 28"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
      aria-label="Wayrelay"
      style={style}
    >
      <path d="M39 23.3448L35.7209 26.3954L28.4302 19.2598L19.5 28L10.5698 19.2598L3.27907 26.3954L0 23.3448L10.5698 13L19.5 21.7402L28.4302 13L39 23.3448Z" fill="currentColor"/>
      <path d="M39 10.3448L35.7209 13.3954L28.4302 6.25982L19.5 15L10.5698 6.25982L3.27907 13.3954L0 10.3448L10.5698 0L19.5 8.74018L28.4302 0L39 10.3448Z" fill="currentColor"/>
    </svg>
  );
}

Object.assign(window, { Reveal, Counter, LogoMark, BrandLogo, BrandIcon, SectionEyebrow, FONT_PAIRS });
