// ============================================================
// sections-a.jsx — shared hooks, Nav, Hero, Team Snapshot
// ============================================================
const { useState, useEffect, useRef, useCallback } = React;

// ---- hooks ----
function useInView(opts = { threshold: 0, rootMargin: "0px 0px -12% 0px" }) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  const [instant, setInstant] = useState(false);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    // Fallback: if already within the viewport at mount, reveal immediately, with no fade dependency.
    const r = el.getBoundingClientRect();
    if (r.top < (window.innerHeight || document.documentElement.clientHeight) && r.bottom > 0) {
      setInstant(true); setSeen(true); return;
    }
    const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setSeen(true); io.disconnect(); } }, opts);
    io.observe(el); return () => io.disconnect();
  }, []);
  return [ref, seen, instant];
}

function Reveal({ children, delay = 0, as: Tag = "div", className = "", ...rest }) {
  const [ref, seen, instant] = useInView();
  return (
    <Tag ref={ref} className={"reveal " + (seen ? "in " : "") + (instant ? "reveal--now " : "") + className} style={{ transitionDelay: instant ? "0ms" : delay + "ms" }} {...rest}>
      {children}
    </Tag>
  );
}

function useCountUp(target, run, dur = 1500) {
  const [val, setVal] = useState(0);
  useEffect(() => {
    if (!run) return;
    let raf, start;
    const tick = (t) => {
      if (!start) start = t;
      const p = Math.min((t - start) / dur, 1);
      const e = 1 - Math.pow(1 - p, 3);
      setVal(target * e);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [run, target]);
  return val;
}

const GLYPHS = "ABCDEF0123456789#$%&/<>{}*";
function useScramble(text, run, speed = 38) {
  const [out, setOut] = useState(text);
  useEffect(() => {
    if (!run) return;
    let frame = 0; const total = text.length * 3; let raf;
    const step = () => {
      const revealed = frame / 3;
      setOut(text.split("").map((c, i) => {
        if (c === " ") return " ";
        if (i < revealed) return c;
        return GLYPHS[Math.floor(Math.random() * GLYPHS.length)];
      }).join(""));
      frame++;
      if (frame <= total) raf = setTimeout(step, speed);
      else setOut(text);
    };
    step();
    return () => clearTimeout(raf);
  }, [run, text]);
  return out;
}

function Avatar({ m, size = 40 }) {
  const initials = m.name.split(" ").map(w => w[0]).slice(0, 2).join("");
  // Deterministic hue from the member id/name so colours are stable across renders.
  const seed = (m.id || m.name).split("").reduce((s, c) => s + c.charCodeAt(0), 0);
  const h = 168 + (seed % 42);
  return (
    <div className="avatar" style={{
      width: size, height: size, borderRadius: 7, flex: "none",
      display: "grid", placeItems: "center",
      fontFamily: "var(--font-mono)", fontWeight: 700, fontSize: size * 0.34,
      color: `oklch(0.92 0.03 ${h})`,
      background: `linear-gradient(135deg, oklch(0.42 0.09 ${h}), oklch(0.26 0.06 ${h + 12}))`,
      border: "1px solid rgba(255,255,255,.12)", letterSpacing: ".02em",
    }}>{initials}</div>
  );
}

// ============================================================
// NAV
// ============================================================
function Nav({ onJoin }) {
  const [scrolled, setScrolled] = useState(false);
  const [menuOpen, setMenuOpen] = useState(false);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 40);
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  const links = [["Leaderboards", "leaderboard"], ["Attendance", "attendance"], ["Activity", "feed"], ["Who we are", "manifesto"], ["Badges", "badges"], ["Story", "story"], ["Stats", "stats"]]
    .filter(([, id]) => SHOW_BADGES || id !== "badges");
  return (
    <header className={"nav" + (scrolled ? " nav--solid" : "") + (menuOpen ? " nav--open" : "")}>
      <div className="wrap nav-inner">
        <a href="#top" className="nav-logo" aria-label="Cyber Runner home"><CyberRunnerLogo /></a>
        <nav className="nav-links">
          {links.map(([label, id]) => (
            <a key={id} href={"#" + id} className="nav-link">{label}</a>
          ))}
        </nav>
        <div className="nav-actions">
          <button className="btn btn-primary nav-cta" onClick={onJoin}>Join the run</button>
          <button className="nav-burger" aria-label="Menu" aria-expanded={menuOpen} onClick={() => setMenuOpen(o => !o)}>
            <span className="nav-burger-bar" /><span className="nav-burger-bar" /><span className="nav-burger-bar" />
          </button>
        </div>
      </div>
      {menuOpen && (
        <nav className="nav-mobile">
          {links.map(([label, id]) => (
            <a key={id} href={"#" + id} className="nav-mobile-link" onClick={() => setMenuOpen(false)}>{label}</a>
          ))}
        </nav>
      )}
      <style>{`
        .nav { position: fixed; top: 0; left: 0; right: 0; z-index: 50; transition: background .3s, border-color .3s, backdrop-filter .3s; border-bottom: 1px solid transparent; }
        .nav--solid { background: color-mix(in oklab, var(--bg) 78%, transparent); backdrop-filter: blur(14px); border-bottom-color: var(--line); }
        .nav-inner { display: flex; align-items: center; gap: 28px; height: 72px; }
        .nav-logo { height: 30px; display: block; }
        .nav-links { display: flex; gap: 28px; margin-left: 8px; }
        .nav-link { font-family: var(--font-mono); font-size: 13px; letter-spacing: .04em; color: var(--text-dim); text-decoration: none; transition: color .15s; position: relative; padding: 4px 0; white-space: nowrap; }
        .nav-link:hover { color: var(--text); }
        .nav-link::after { content:""; position:absolute; left:0; right:100%; bottom:-2px; height:1px; background:var(--teal); transition:right .25s; }
        .nav-link:hover::after { right:0; }
        .nav-actions { margin-left: auto; display: flex; align-items: center; gap: 18px; }
        .nav-status { display: inline-flex; align-items: center; gap: 8px; font-size: 11px; letter-spacing: .18em; color: var(--text-dim); }
        .nav-burger { display: none; flex-direction: column; justify-content: center; gap: 4px; width: 38px; height: 34px; padding: 0 8px; background: transparent; border: 1px solid var(--line); border-radius: 5px; cursor: pointer; }
        .nav-burger-bar { display: block; height: 2px; width: 100%; background: var(--text); border-radius: 2px; transition: background .2s; }
        .nav--open .nav-burger { border-color: var(--teal); }
        .nav--open .nav-burger-bar { background: var(--teal); }
        .nav-mobile { display: flex; flex-direction: column; background: color-mix(in oklab, var(--bg) 94%, transparent); backdrop-filter: blur(14px); border-bottom: 1px solid var(--line); padding: 6px 0; }
        .nav-mobile-link { font-family: var(--font-mono); font-size: 14px; letter-spacing: .04em; color: var(--text-dim); text-decoration: none; padding: 13px var(--gutter); border-top: 1px solid var(--line-2); }
        .nav-mobile-link:first-child { border-top: 0; }
        .nav-mobile-link:hover, .nav-mobile-link:active { color: var(--teal); background: var(--teal-soft); }
        @media (max-width: 860px){ .nav-links { display: none; } .nav-status { display:none; } .nav-cta { display:none; } .nav-burger { display: inline-flex; } }
        @media (min-width: 861px){ .nav-mobile { display: none; } }
      `}</style>
    </header>
  );
}

// ============================================================
// HERO
// ============================================================
function Hero({ onJoin, figure = true }) {
  const [ref, seen] = useInView({ threshold: 0.1 });
  const line2 = useScramble("CYBERSECURITY PROFESSIONALS", seen);

  // Ticker cycles through the real recent-activity feed.
  const [tick, setTick] = useState(0);
  useEffect(() => {
    if (FEED_DATA.length < 2) return;
    const iv = setInterval(() => setTick(t => (t + 1) % FEED_DATA.length), 3800);
    return () => clearInterval(iv);
  }, []);
  const ticker = FEED_DATA[tick] || null;

  return (
    <section id="manifesto" ref={ref} className="hero grid-bg theme-dark">
      <div className="hero-glow" />
      <div className={"hero-figure" + (figure ? "" : " off")} aria-hidden="true"><CyberRunnerLogo className="mono" /></div>
      <div className="wrap hero-inner">
        <div className="hero-left">
          <div className={"hero-eyebrow mono" + (seen ? " on" : "")}>
            WHO WE ARE
          </div>
          <h1 className="hero-title">
            <span className="hl hl-1">WE ARE</span>
            <span className="hl hl-2 teal">{line2}</span>
            <span className="hl hl-3">WHO <span className="run-word">RUN<span className="caret">_</span></span></span>
          </h1>
          <p className="hero-sub">
            By day we build and secure applications, hunt threats, and defend organizations.
            By night, we run, chasing the same discipline that builds great systems & security.
          </p>
          <div className="hero-cta">
            <button className="btn btn-primary" onClick={onJoin}>Connect Strava</button>
          </div>
          {ticker && (
            <div className="hero-ticker mono">
              <span className="tk-label">LIVE_FEED</span>
              <span className="tk-body" key={ticker.id}>
                <span className="tk-who">{ticker.who}</span> {ticker.action}
              </span>
            </div>
          )}
        </div>
      </div>
      <style>{`
        .hero { min-height: 100svh; display: flex; align-items: center; overflow: hidden; padding-top: 72px; }
        .hero-glow { position: absolute; top: -10%; right: -5%; width: 60vw; height: 60vw; max-width: 760px; max-height: 760px;
          background: radial-gradient(circle, rgba(4,160,172,.22), transparent 62%); filter: blur(20px); pointer-events: none; }
        .hero-figure { position: absolute; right: -4vw; bottom: -6vh; width: min(58vw, 760px); color: rgba(4,160,172,.07); pointer-events: none; }
        .hero-figure .cr-logo { height: auto; width: 100%; }
        .hero-figure.off { display: none; }
        .hero-inner { position: relative; z-index: 2; width: 100%; }
        .hero-left { max-width: 1040px; }
        .hero-eyebrow { font-size: 12px; letter-spacing: .2em; color: var(--text-dim); display: inline-flex; align-items: center; gap: 11px;
          opacity: 0; transform: translateY(8px); transition: all .6s; }
        .hero-eyebrow.on { opacity: 1; transform: none; }
        .hero-title { font-family: var(--font-display); font-weight: 500; line-height: .98; letter-spacing: -.01em;
          font-size: clamp(34px, 6.6vw, 88px); margin: 22px 0 0; display: flex; flex-direction: column; }
        .hero-title .hl { display: block; overflow: hidden; }
        .hero-title .teal { color: var(--teal); }
        .run-word { position: relative; }
        .hero-sub { color: var(--text-dim); font-size: clamp(16px, 1.7vw, 20px); line-height: 1.6; max-width: 52ch; margin-top: 28px; }
        .hero-cta { display: flex; gap: 14px; margin-top: 34px; flex-wrap: wrap; }
        .hero-ticker { margin-top: 40px; display: inline-flex; align-items: center; gap: 14px; font-size: 13px;
          border: 1px solid var(--line); border-radius: 5px; padding: 11px 16px; background: rgba(0,0,0,.25); max-width: 100%; }
        .tk-label { color: var(--teal); letter-spacing: .12em; border-right: 1px solid var(--line); padding-right: 14px; }
        .tk-body { color: var(--text-dim); animation: tkin .5s ease; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .tk-who { color: var(--text); }
        @keyframes tkin { from { opacity: 0; transform: translateX(8px); } }
      `}</style>
    </section>
  );
}

// ============================================================
// TEAM SNAPSHOT
// ============================================================
function StatCounter({ value, decimals = 0 }) {
  const [ref, seen] = useInView();
  const v = useCountUp(value, seen, 1600);
  return <span ref={ref} className="tnum">{v.toLocaleString("en-US", { minimumFractionDigits: decimals, maximumFractionDigits: decimals })}</span>;
}

function Snapshot() {
  const stats = [
    { k: "Total distance", v: TEAM.distance, unit: "km", big: true, code: "DIST" },
    { k: "Total runs", v: TEAM.runs, unit: "sessions", code: "RUNS" },
    { k: "Total elevation", v: TEAM.elevation, unit: "m climbed", code: "ELEV" },
    { k: "Active members", v: TEAM.members, unit: "runners", code: "MBRS" },
    { k: "Running hours", v: TEAM.hours, unit: "hours", code: "TIME" },
  ];
  return (
    <section id="snapshot" className="section-pad">
      <div className="wrap">
        <Reveal><span className="eyebrow">// 05 — Team snapshot</span></Reveal>
        <Reveal delay={60}><h2 className="sec-title">The collective output,<br />updated every 15 minutes.</h2></Reveal>
        <div className="snap-grid">
          {stats.map((s, i) => (
            <Reveal key={s.code} delay={120 + i * 70} className={"snap-card card card-corner" + (s.big ? " snap-card--big" : "")}>
              <div className="snap-code mono">{s.code}</div>
              <div className="snap-val">
                <StatCounter value={s.v} />
                <span className="snap-unit mono">{s.unit}</span>
              </div>
              <div className="snap-label">{s.k}</div>
              <div className="snap-spark" aria-hidden="true">
                {Array.from({ length: 16 }).map((_, j) => (
                  <span key={j} style={{ height: (22 + Math.abs(Math.sin(i * 3 + j * 1.7)) * 60) + "%" }}></span>
                ))}
              </div>
            </Reveal>
          ))}
        </div>
      </div>
      <style>{`
        .snap-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-top: 48px; }
        .snap-card { padding: 26px 24px 22px; display: flex; flex-direction: column; min-height: 188px; overflow: hidden; transition: border-color .2s, transform .2s; }
        .snap-card:hover { border-color: color-mix(in oklab, var(--teal) 55%, var(--line)); transform: translateY(-3px); }
        .snap-card--big { grid-column: span 2; grid-row: span 1; }
        .snap-code { font-size: 11px; letter-spacing: .2em; color: var(--text-faint); }
        .snap-val { font-family: var(--font-display); font-weight: 500; line-height: 1; margin-top: auto;
          font-size: clamp(40px, 4.6vw, 64px); display: flex; align-items: baseline; gap: 10px; color: var(--text); }
        .snap-card--big .snap-val { font-size: clamp(56px, 7vw, 104px); }
        .snap-unit { font-family: var(--font-mono); font-size: 13px; font-weight: 500; color: var(--teal); letter-spacing: .04em; }
        .snap-label { color: var(--text-dim); font-size: 14px; margin-top: 8px; }
        .snap-spark { display: flex; align-items: flex-end; gap: 3px; height: 36px; margin-top: 16px; opacity: .5; }
        .snap-spark span { flex: 1; background: linear-gradient(var(--teal), transparent); border-radius: 1px; }
        .snap-card--big .snap-spark { height: 48px; }
        @media (max-width: 1000px){ .snap-grid { grid-template-columns: repeat(2,1fr); } .snap-card--big { grid-column: span 2; } }
        @media (max-width: 560px){ .snap-grid { grid-template-columns: 1fr; } .snap-card--big { grid-column: span 1; } }
      `}</style>
    </section>
  );
}

Object.assign(window, { useInView, Reveal, useCountUp, useScramble, Avatar, Nav, Hero, Snapshot, StatCounter });
