/* Motion helpers — restrained per the design system: 200–600ms,
   cubic-bezier(.4,0,.2,1), opacity/transform only, no bounce or spring.
   Everything is disabled under prefers-reduced-motion.
   Reveals run off one shared scroll/rAF sweep rather than IntersectionObserver,
   which this preview environment throttles unpredictably. */
const REDUCED = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

const kgWatchers = new Set();
function kgSweep() {
  kgWatchers.forEach((w) => {
    if (!w.el || !w.el.isConnected) { kgWatchers.delete(w); return; }
    const r = w.el.getBoundingClientRect();
    if (r.top < window.innerHeight * 0.92) { kgWatchers.delete(w); w.cb(); }
  });
}
window.addEventListener("scroll", kgSweep, { passive: true });
window.addEventListener("resize", kgSweep);
setInterval(kgSweep, 250);

function useInView() {
  const ref = React.useRef(null);
  const [inView, setInView] = React.useState(REDUCED);
  React.useEffect(() => {
    if (REDUCED || !ref.current) return;
    const w = { el: ref.current, cb: () => setInView(true) };
    kgWatchers.add(w);
    kgSweep();
    return () => kgWatchers.delete(w);
  }, []);
  return [ref, inView];
}

/* Fade + rise on first scroll into view. Takes the place of a plain wrapper div,
   so it never adds layout of its own. */
function Reveal({ as: Tag = "div", delay = 0, y = 20, style, children, ...rest }) {
  const [ref, inView] = useInView();
  return (
    <Tag
      ref={ref}
      {...rest}
      style={{
        opacity: inView ? 1 : 0,
        transform: inView ? "none" : `translateY(${y}px)`,
        transition: REDUCED ? "none" : `opacity 600ms cubic-bezier(.4,0,.2,1) ${delay}ms, transform 600ms cubic-bezier(.4,0,.2,1) ${delay}ms`,
        ...style,
      }}
    >{children}</Tag>
  );
}

/* Layout-mode helpers. `sp` scales a drawn desktop spacing value by the global
   rhythm multiplier in responsive.css (1 on desktop, .72 tablet, .58 phone,
   .5 small phone), so one number keeps the Figma value and adapts below it.
   `useNarrow`/`usePhone` are for the sections that need a different layout
   rather than a smaller one — pinned bands, hover-to-expand cards. */
const sp = (n) => `calc(${n}px * var(--kg-rhythm))`;

function useMedia(query) {
  const [match, setMatch] = React.useState(() => window.matchMedia(query).matches);
  React.useEffect(() => {
    const mq = window.matchMedia(query);
    const on = () => setMatch(mq.matches);
    on();
    mq.addEventListener("change", on);
    return () => mq.removeEventListener("change", on);
  }, [query]);
  return match;
}
const useNarrow = () => useMedia("(max-width: 1023px)");
const usePhone = () => useMedia("(max-width: 767px)");

/* Rail of cards on phones (see .kg-swipe in responsive.css). Renders the row
   plus the progress hairline that tracks it; above 768px the bar is hidden and
   the row keeps the layout it was drawn with. */
function SwipeRail({ wide, tone = "light", className = "", style, children, ...rest }) {
  const railRef = React.useRef(null), fillRef = React.useRef(null);
  React.useEffect(() => {
    const rail = railRef.current, fill = fillRef.current;
    if (!rail || !fill) return;
    let raf = 0;
    const paint = () => {
      raf = 0;
      const max = rail.scrollWidth - rail.clientWidth;
      const p = max > 8 ? Math.max(0, Math.min(1, rail.scrollLeft / max)) : 1;
      fill.style.transform = `scaleX(${(0.2 + p * 0.8).toFixed(3)})`;
    };
    const on = () => { if (!raf) raf = requestAnimationFrame(paint); };
    paint();
    rail.addEventListener("scroll", on, { passive: true });
    window.addEventListener("resize", on);
    const t = setTimeout(paint, 500);
    return () => { clearTimeout(t); rail.removeEventListener("scroll", on); window.removeEventListener("resize", on); if (raf) cancelAnimationFrame(raf); };
  }, []);
  return (
    <React.Fragment>
      <div ref={railRef} className={`kg-swipe${wide ? " kg-swipe-wide" : ""} ${className}`} style={style} {...rest}>{children}</div>
      <div className={`kg-swipebar${tone === "dark" ? " kg-swipebar-dark" : ""}`} aria-hidden="true"><i ref={fillRef} style={{ transform: "scaleX(.2)" }} /></div>
    </React.Fragment>
  );
}

/* Scroll cue — phones and tablets only, fades out as soon as the reader moves. */
function ScrollCue({ tone = "light", style }) {
  const narrow = window.useNarrow ? window.useNarrow() : false;
  const [gone, setGone] = React.useState(false);
  React.useEffect(() => {
    const on = () => setGone(window.scrollY > 60);
    on();
    window.addEventListener("scroll", on, { passive: true });
    return () => window.removeEventListener("scroll", on);
  }, []);
  if (!narrow || REDUCED) return null;
  const ink = tone === "light" ? "#FFFFFF" : "#0F172B";
  return (
    <div aria-hidden="true" style={{ marginTop: "auto", paddingTop: 28, alignSelf: "center", opacity: gone ? 0 : 1, transform: gone ? "translateY(6px)" : "none", transition: "opacity 400ms cubic-bezier(.4,0,.2,1), transform 400ms cubic-bezier(.4,0,.2,1)", ...style }}>
      <div className="kg-cue" style={{ width: 26, height: 42, boxSizing: "border-box", borderRadius: 13, border: `1px solid ${tone === "light" ? "rgba(255,255,255,.55)" : "rgba(15,23,43,.28)"}`, display: "flex", justifyContent: "center", paddingTop: 8 }}>
        <i style={{ width: 4, height: 4, borderRadius: "50%", background: ink }} />
      </div>
    </div>
  );
}

const groupDigits = (n) => String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ".");

/* Counts a statistic up once its band is on screen. */
function useCountUp(target, inView, duration = 1600) {
  const [value, setValue] = React.useState(REDUCED ? target : 0);
  React.useEffect(() => {
    if (!inView || REDUCED) return;
    let raf, start;
    const step = (t) => {
      if (start === undefined) start = t;
      const p = Math.min(1, (t - start) / duration);
      setValue(Math.round(target * (1 - Math.pow(1 - p, 3))));
      if (p < 1) raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [inView, target, duration]);
  return value;
}

Object.assign(window, { REDUCED, useInView, Reveal, useCountUp, groupDigits, sp, useMedia, useNarrow, usePhone, SwipeRail, ScrollCue });
