// FOUR HURDLES · cover mirror (noise → signal canvas + quarter zones)
//
// Illustrative funnel, orientation, not a census. Percentages show how many
// career-focused producers are still in the game at each stage.

const HURDLES = [
  {
    n: "01",
    pct: 92,
    tab: "Talent",
    label: "Most reach here",
    title: "Get good at making music",
    body: "You spend years getting good. That's the bare minimum. Plenty of producers can make something good. That still doesn't mean anyone's hearing it, or paying you.",
  },
  {
    n: "02",
    pct: 28,
    tab: "Attention",
    label: "Some break through",
    title: "Get in front of the right artists",
    body: "Most producers send beats into the void and pray. The ones who land placements build relationships on purpose. They know which artist they're trying to reach.",
  },
  {
    n: "03",
    pct: 9,
    tab: "Release",
    label: "Few land it",
    title: "Land a record that actually comes out",
    body: "Your beat might get used in a session. That is not a release. Records get shelved, pushed back, leaked, killed. Your name on something that actually comes out is a different fight.",
  },
  {
    n: "04",
    pct: 2,
    tab: "Payment",
    label: "Almost nobody",
    title: "Get paid, not just credited",
    body: "You can have a Spotify credit and an empty bank account. No paperwork means no payment. Hire an entertainment lawyer before you need them.",
  },
];

const HURDLE_QUARTERS = [
  { lo: 0, hi: 0.25, stamp: 12.5 },
  { lo: 0.25, hi: 0.5, stamp: 37.5 },
  { lo: 0.5, hi: 0.75, stamp: 62.5 },
  { lo: 0.75, hi: 1, stamp: 87.5 },
];

const HURDLE_METHODOLOGY =
  "These numbers are a directional model, not a research paper. We started with roughly 1 million career-focused producers, people trying to make a living from production, not hobbyists uploading their own music. The percentages come from how the industry actually behaves: attrition at each stage, how few credits turn into releases, and how few releases turn into real income. We looked at credited work across singles and albums (Billboard Hot 100, Billboard 200, and the long tail below that), talked to working producers, and rounded to something you can actually remember. If you want to argue the exact math, you've missed the point, the shape of the funnel is what matters.";

function hurdleSmoothstep(a, b, x) {
  const t = Math.min(1, Math.max(0, (x - a) / (b - a)));
  return t * t * (3 - 2 * t);
}

function Hurdles({ navTarget }) {
  const [active, setActive] = React.useState(0);
  const [showMethod, setShowMethod] = React.useState(false);

  React.useEffect(() => {
    if (!navTarget || navTarget.chapter !== "01") return;
    if (navTarget.hurdle != null) setActive(navTarget.hurdle);
  }, [navTarget?.key]);

  return (
    <section className="section tight" id="hurdles">
      <div className="page">
        <div className="grid" style={{ alignItems: "end", marginBottom: 24 }}>
          <div className="col-4 col-md-5 col-lg-8">
            <h2 className="h-section" style={{ margin: 0 }}>
              Four hurdles
            </h2>
          </div>
          <div className="col-4 col-md-3 col-lg-4">
            <p className="lede">
              Most producers clear the first hurdle. Almost none have a map for the last three. That's the difference between having a moment or having a 10+ year career.
            </p>
          </div>
        </div>

        <HurdlesLayout
          active={active}
          setActive={setActive}
          showMethod={showMethod}
          setShowMethod={setShowMethod}
        />
      </div>
    </section>
  );
}

function HurdleMethodology({ showMethod, setShowMethod }) {
  return (
    <div className="hurdle-a-method hurdle-a-method--head">
      <button
        type="button"
        className="hurdle-a-method-toggle caption"
        onClick={() => setShowMethod(!showMethod)}
        aria-expanded={showMethod}
      >
        {showMethod ? "▾" : "▸"} About these numbers
      </button>
      {showMethod && (
        <div className="hurdle-a-method-body">
          <p className="body-text" style={{ fontSize: 14, lineHeight: 1.6, margin: 0, color: "var(--ink-2)" }}>
            {HURDLE_METHODOLOGY}
          </p>
        </div>
      )}
    </div>
  );
}

function HurdleFunnelTabs({ active, setActive }) {
  return (
    <div className="hurdle-funnel-tabs" role="tablist" aria-label="Four hurdles">
      {HURDLES.map((item, i) => {
        const isActive = active === i;
        return (
          <button
            key={item.n}
            type="button"
            role="tab"
            aria-selected={isActive}
            className={`hurdle-funnel-tab${isActive ? " active" : ""}`}
            onClick={() => setActive(i)}
            aria-label={`${item.n} ${item.tab}: ${item.pct}%, ${item.title}`}
          >
            {item.n} {item.tab}
          </button>
        );
      })}
    </div>
  );
}

function HurdleFunnelCanvas({ active }) {
  const canvasRef = React.useRef(null);
  const activeRef = React.useRef(active);
  activeRef.current = active;

  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;

    const reduce =
      typeof window.matchMedia === "function" &&
      window.matchMedia("(prefers-reduced-motion: reduce)").matches;

    let W = 0;
    let H = 0;
    let dpr = 1;
    let raf = 0;
    let running = false;
    let startT = 0;
    const NECK = 0.5;

    const seeds = new Float32Array(2048);
    for (let i = 0; i < seeds.length; i++) seeds[i] = Math.random() * 2 - 1;

    const parts = [];
    for (let i = 0; i < 64; i++) {
      const r = Math.random();
      parts.push({ x: r * r * 0.7, y: Math.random(), s: Math.random() * 2 + 0.6, ph: Math.random() * 6.28 });
    }

    function funnelScale(xN) {
      const dist = Math.abs(xN - NECK) / NECK;
      return 0.88 + 0.12 * hurdleSmoothstep(0, 1, dist);
    }

    function cohAt(xN) {
      return hurdleSmoothstep(0.34, 0.64, xN);
    }

    function edgeFade(xN) {
      return hurdleSmoothstep(0, 0.12, xN) * (1 - hurdleSmoothstep(0.88, 1, xN));
    }

    function zoneNow() {
      return HURDLE_QUARTERS[activeRef.current] ?? HURDLE_QUARTERS[0];
    }

    function inZone(xN) {
      const z = zoneNow();
      return xN >= z.lo && xN <= z.hi;
    }

    function signalAt(xN, t) {
      const env = Math.sin(xN * Math.PI);
      const a = Math.sin(xN * Math.PI * 6 + t * 1.05);
      const b = Math.sin(xN * Math.PI * 13 - t * 0.7) * 0.36;
      const c = Math.sin(xN * Math.PI * 2.5 + t * 0.45) * 0.55;
      return ((a + b + c) / 2) * env;
    }

    function drawQuarterMask() {
      const z = zoneNow();
      const qLo = z.lo * W;
      const qHi = z.hi * W;
      ctx.fillStyle = "rgba(0,0,0,0.5)";
      if (qLo > 0) ctx.fillRect(0, 0, qLo, H);
      if (qHi < W) ctx.fillRect(qHi, 0, W - qHi, H);
      ctx.fillStyle = "rgba(212,166,74,0.07)";
      ctx.fillRect(qLo, 0, qHi - qLo, H);
      ctx.strokeStyle = "rgba(255,255,255,0.12)";
      ctx.lineWidth = 1;
      [0.25, 0.5, 0.75].forEach((frac) => {
        const x = frac * W;
        ctx.beginPath();
        ctx.moveTo(x, 0);
        ctx.lineTo(x, H);
        ctx.stroke();
      });
    }

    function frame(t) {
      ctx.clearRect(0, 0, W, H);
      if (W < 2 || H < 2) return;

      const mid = H * 0.5;
      const breathe = 0.82 + 0.18 * Math.sin(t * 0.6);
      const amp = H * 0.15 * breathe;
      const fillFloor = mid + amp * 1.35;
      const step = Math.max(2, Math.round(W / 560));

      drawQuarterMask();

      for (let i = 0; i < parts.length; i++) {
        const p = parts[i];
        const edge = edgeFade(p.x);
        const fade = (1 - cohAt(p.x)) * edge;
        if (fade <= 0.02) continue;
        const jx = reduce ? 0 : Math.sin(t * 2 + p.ph) * 6 * edge;
        const jy = reduce ? 0 : Math.cos(t * 2.4 + p.ph) * 8 * edge;
        const px = p.x * W + jx;
        const spread = funnelScale(p.x) * H * 0.42 * edge;
        const py = mid + (p.y - 0.5) * spread + jy;
        const coh = cohAt(p.x);
        let a = (0.14 + coh * 0.1) * fade;
        if (inZone(p.x)) a *= 1.25;
        if (coh < 0.35) {
          ctx.fillStyle = "rgba(192,57,43," + Math.min(1, a).toFixed(3) + ")";
        } else {
          const mix = (coh - 0.35) / 0.65;
          const r = Math.round(192 + (212 - 192) * mix);
          const g = Math.round(57 + (166 - 57) * mix);
          const b = Math.round(43 + (74 - 43) * mix);
          ctx.fillStyle = "rgba(" + r + "," + g + "," + b + "," + Math.min(1, a).toFixed(3) + ")";
        }
        ctx.fillRect(px, py, p.s, p.s);
      }

      const pts = [];
      for (let x = 0; x <= W; x += step) {
        const xN = x / W;
        const edge = edgeFade(xN);
        const coh = cohAt(xN);
        const si = Math.min(seeds.length - 1, Math.floor(xN * (seeds.length - 1)));
        const sig = signalAt(xN, t);
        const jitter = reduce ? seeds[si] : seeds[si] * 0.7 + Math.sin(xN * 140 + t * 7.5) * 0.5;
        let v = (sig * coh + jitter * (1 - coh)) * edge;
        if (inZone(xN)) v *= 1.12;
        pts.push(x, mid - v * amp * funnelScale(xN));
      }

      const stroke = ctx.createLinearGradient(0, 0, W, 0);
      stroke.addColorStop(0, "rgba(192,57,43,0)");
      stroke.addColorStop(0.08, "rgba(192,57,43,0.75)");
      stroke.addColorStop(0.34, "rgba(192,57,43,0.82)");
      stroke.addColorStop(0.5, "rgba(176,86,52,0.78)");
      stroke.addColorStop(0.62, "rgba(212,166,74,0.88)");
      stroke.addColorStop(0.92, "rgba(232,200,120,0.95)");
      stroke.addColorStop(1, "rgba(232,200,120,0)");

      ctx.beginPath();
      ctx.moveTo(0, pts[1]);
      for (let i = 0; i < pts.length; i += 2) ctx.lineTo(pts[i], pts[i + 1]);
      ctx.lineTo(W, fillFloor);
      ctx.lineTo(0, fillFloor);
      ctx.closePath();
      const fill = ctx.createLinearGradient(0, mid - amp, 0, fillFloor);
      fill.addColorStop(0, "rgba(192,57,43,0.06)");
      fill.addColorStop(0.45, "rgba(192,57,43,0.03)");
      fill.addColorStop(0.62, "rgba(212,166,74,0.1)");
      fill.addColorStop(1, "rgba(212,166,74,0)");
      ctx.fillStyle = fill;
      ctx.fill();

      ctx.beginPath();
      ctx.moveTo(pts[0], pts[1]);
      for (let i = 2; i < pts.length; i += 2) ctx.lineTo(pts[i], pts[i + 1]);
      ctx.lineJoin = "round";
      ctx.lineWidth = 2;
      ctx.shadowColor = "rgba(212,166,74,0.45)";
      ctx.shadowBlur = 14;
      ctx.strokeStyle = stroke;
      ctx.stroke();
      ctx.shadowBlur = 0;
    }

    function resize() {
      const rect = canvas.getBoundingClientRect();
      W = Math.max(1, Math.round(rect.width));
      H = Math.max(1, Math.round(rect.height));
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      canvas.width = Math.round(W * dpr);
      canvas.height = Math.round(H * dpr);
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      if (!running || reduce) frame(0);
    }

    function loop(nowMs) {
      if (!running) return;
      if (!startT) startT = nowMs;
      frame((nowMs - startT) / 1000);
      raf = window.requestAnimationFrame(loop);
    }

    function play() {
      if (reduce || running) return;
      running = true;
      raf = window.requestAnimationFrame(loop);
    }

    function pause() {
      running = false;
      if (raf) window.cancelAnimationFrame(raf);
      raf = 0;
    }

    resize();
    if (reduce) frame(0);
    else play();

    const onResize = () => resize();
    const onVis = () => {
      if (reduce) return;
      if (document.hidden) pause();
      else play();
    };
    window.addEventListener("resize", onResize);
    document.addEventListener("visibilitychange", onVis);

    const io = new IntersectionObserver(
      (entries) => {
        if (reduce) return;
        const e = entries[0];
        if (e && e.isIntersecting) play();
        else pause();
      },
      { threshold: 0 }
    );
    io.observe(canvas);

    return () => {
      pause();
      window.removeEventListener("resize", onResize);
      document.removeEventListener("visibilitychange", onVis);
      io.disconnect();
    };
  }, [active]);

  return <canvas ref={canvasRef} aria-hidden="true" />;
}

function HurdlesLayout({ active, setActive, showMethod, setShowMethod }) {
  const h = HURDLES[active];
  const quarter = HURDLE_QUARTERS[active];
  const isLast = active === HURDLES.length - 1;
  const go = (idx) => setActive(idx);

  return (
    <div className="hurdle-funnel">
      <HurdleMethodology showMethod={showMethod} setShowMethod={setShowMethod} />
      <HurdleFunnelTabs active={active} setActive={setActive} />

      <div className="hurdle-funnel-body">
        <div className="hurdle-funnel-copy" role="tabpanel">
          <span className="caption">{h.label}</span>
          <h3 className="kicker" style={{ margin: "12px 0 0" }}>
            {h.title}
          </h3>
          <p className="hurdle-a-panel-copy" style={{ marginTop: 16 }}>
            {h.body}
          </p>
        </div>

        <div className="hurdle-funnel-canvas-wrap">
          <HurdleFunnelCanvas active={active} />
          <div className="hurdle-funnel-quarters" aria-hidden="true">
            {HURDLES.map((_, i) => (
              <div key={i} className={`hurdle-funnel-q${active === i ? " active" : ""}`} />
            ))}
          </div>
          <div
            className="hurdle-funnel-pct"
            style={{ "--hurdle-stamp-x": `${quarter.stamp}%` }}
            aria-live="polite"
          >
            {h.pct}%
          </div>
          <div className="hurdle-funnel-labels" aria-hidden="true">
            <span>NOISE</span>
            <span className="signal">SIGNAL</span>
          </div>
        </div>
      </div>

      <div className="hurdle-a-nav">
        <button
          type="button"
          className="btn btn-ghost"
          disabled={active === 0}
          onClick={() => go(active - 1)}
          style={{ opacity: active === 0 ? 0.35 : 1 }}
        >
          ← Previous
        </button>
        <span className="caption">
          {active + 1} / {HURDLES.length}
        </span>
        <button
          type="button"
          className="btn btn-ghost"
          disabled={isLast}
          onClick={() => go(active + 1)}
          style={{ opacity: isLast ? 0.35 : 1 }}
        >
          Next →
        </button>
      </div>
    </div>
  );
}

window.Hurdles = Hurdles;
