// ============ MethodWheel.jsx ============
// Auto-cycling circular diagram for the Method section.
// Three phases (Diagnose · Redesign · Implement) sit on the rim of a hairline
// ring. The active phase highlights in burgundy and its layers fade into the
// right column.
//
// Two modes:
//  - Default (timer): auto-advances every MW_INTERVAL ms, pauses on hover.
//  - scrollDriven:    the section pins to the viewport; scroll progress through
//                     the section advances the phase. The page resumes scrolling
//                     only after phase 3 has been "held" for a moment.

const METHOD_PHASES = [
{
  n: "01",
  title: "Diagnose",
  verb: "We identify where patients, time, and revenue are leaking.",
  layers: [
  "Visibility",
  "Reputation",
  "Booking path",
  "Conversion",
  "Follow-up gaps",
  "Database opportunity",
  "Team roles",
  "Owner dependency"]

},
{
  n: "02",
  title: "Redesign",
  verb: "We turn the patient journey into a clear operating system.",
  layers: [
  "Patient journey",
  "Qualification flow",
  "Consultation SOP",
  "Follow-up structure",
  "Reactivation flow",
  "Team roles",
  "KPI rhythm",
  "Reputation system"]

},
{
  n: "03",
  title: "Implement",
  verb: "We train the team until the system runs without the owner carrying it.",
  layers: [
  "Team training",
  "Side-by-side coaching",
  "Live execution",
  "Dashboard rhythm",
  "System refinement",
  "Practice Playbook"]

}];


const MW_INTERVAL = 5400; // ms per phase

const polar = (cx, cy, r, deg) => {
  const rad = (deg - 90) * Math.PI / 180;
  return [cx + r * Math.cos(rad), cy + r * Math.sin(rad)];
};

const arcPath = (cx, cy, r, a0, a1) => {
  const [x0, y0] = polar(cx, cy, r, a0);
  const [x1, y1] = polar(cx, cy, r, a1);
  const sweep = (a1 - a0 + 360) % 360;
  const large = sweep > 180 ? 1 : 0;
  return `M ${x0} ${y0} A ${r} ${r} 0 ${large} 1 ${x1} ${y1}`;
};

const MethodWheel = ({ scrollDriven = false }) => {
  const [active, setActive] = React.useState(0);
  const [paused, setPaused] = React.useState(false);
  const [scrollProgress, setScrollProgress] = React.useState(0); // 0..1 within pinned region
  const [transitionTick, setTransitionTick] = React.useState(0); // increments on each phase change
  const [transitionDir, setTransitionDir] = React.useState(1); // +1 forward, -1 backward
  const [curtainPlaying, setCurtainPlaying] = React.useState(false);
  // In scrollDriven mode the wheel itself is hidden until the first curtain
  // has wiped across — so the user actually witnesses the introduction.
  const [revealed, setRevealed] = React.useState(!scrollDriven);
  const wrapRef = React.useRef(null);
  const prevActive = React.useRef(0);
  const hasEntered = React.useRef(false);

  // Fire a curtain transition whenever the active phase changes.
  // Note: prevActive starts at 0 so mount does NOT trigger a curtain — we
  // wait for the first viewport-entry (handled below) and for genuine
  // phase changes.
  React.useEffect(() => {
    if (active !== prevActive.current) {
      setTransitionDir(active > prevActive.current ? 1 : -1);
      setTransitionTick((t) => t + 1);
      prevActive.current = active;
      // Hold the highlight at item 0 while the curtain plays (~1.1s).
      // Without this, scroll momentum carries past the first few items
      // before the curtain finishes clearing.
      setCurtainPlaying(true);
      const timer = setTimeout(() => setCurtainPlaying(false), 1000);
      return () => clearTimeout(timer);
    }
  }, [active]);

  // First-entry curtain: when the section's pin engages for the first time,
  // play a curtain wipe to announce phase 01 explicitly. The wheel itself
  // stays hidden until the curtain fully covers (~440ms), at which point we
  // flip `revealed` and the wheel fades in behind the receding band.
  React.useEffect(() => {
    if (!scrollDriven) return;
    const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduced) { setRevealed(true); return; }
    const el = wrapRef.current;
    if (!el) return;
    const outer = el.closest('.mc-method-scroll');
    if (!outer) return;
    // Observe the inner sticky (~100vh) rather than the full scroll container
    // (which is ~9× viewport — can never reach 35% intersection).
    const target = outer.querySelector('.mc-method-sticky') || outer;
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting && !hasEntered.current) {
            hasEntered.current = true;
            setTransitionDir(1);
            setTransitionTick((t) => t + 1);
            // Reveal the wheel mid-curtain so it's "uncovered" by the band.
            // Curtain covers fully ~35% of 1100ms = ~385ms.
            setTimeout(() => setRevealed(true), 420);
          }
        });
      },
      { threshold: 0.45 }
    );
    io.observe(target);
    return () => io.disconnect();
  }, [scrollDriven]);

  // --- Timer mode (default) ---
  React.useEffect(() => {
    if (scrollDriven) return;
    if (paused) return;
    const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduced) return;
    const t = setInterval(() => setActive((a) => (a + 1) % METHOD_PHASES.length), MW_INTERVAL);
    return () => clearInterval(t);
  }, [paused, scrollDriven]);

  // --- Scroll-driven mode ---
  // The wrapper sits inside a tall outer container (.mc-method-scroll).
  // When pinned, we read its position to derive progress 0..1, then split
  // that into 3 phases with uneven thirds — and reserve a "tail" at the
  // end where phase 3 stays fully shown before the pin releases. Without
  // the tail, scrolling out of phase 3 feels like it cuts off abruptly.
  const PHASE_BOUNDS = [0.28, 0.56, 0.86]; // upper bounds; 0.86–1.0 is tail
  const TAIL_BOUND = 0.86;
  React.useEffect(() => {
    if (!scrollDriven) return;
    const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduced) return;
    const el = wrapRef.current;
    if (!el) return;
    const outer = el.closest('.mc-method-scroll');
    if (!outer) return;

    let raf = 0;
    const compute = () => {
      raf = 0;
      const rect = outer.getBoundingClientRect();
      const vh = window.innerHeight;
      const total = rect.height - vh;
      if (total <= 0) return;
      const p = Math.min(1, Math.max(0, -rect.top / total));
      setScrollProgress(p);
      // Find which phase bound contains p
      let idx = 0;
      for (let i = 0; i < PHASE_BOUNDS.length; i++) {
        if (p < PHASE_BOUNDS[i]) { idx = i; break; }
        idx = i;
      }
      setActive(idx);
    };
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(compute);
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    compute();
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [scrollDriven]);

  // Local progress within the current phase (0..1) — used to reveal the
  // layer list progressively, so scrolling slowly always shows motion.
  // Phase 3 (last) uses TAIL_BOUND as its upper edge, so it reaches 100%
  // before the scroll budget ends — giving a "hold" before release.
  const phaseBoundLow = active === 0 ? 0 : PHASE_BOUNDS[active - 1];
  const phaseBoundHigh = active === PHASE_BOUNDS.length - 1 ? TAIL_BOUND : PHASE_BOUNDS[active];
  const phaseRange = phaseBoundHigh - phaseBoundLow;
  const localProgress = scrollDriven && phaseRange > 0 ?
  Math.min(1, Math.max(0, (scrollProgress - phaseBoundLow) / phaseRange)) :
  1;

  const cx = 300,cy = 300,r = 224;
  const gap = 10;
  // Three arcs centered at 0°, 120°, 240° from top
  const segs = [
  { a0: -54 + gap / 2, a1: 54 - gap / 2 },
  { a0: 66 + gap / 2, a1: 174 - gap / 2 },
  { a0: 186 + gap / 2, a1: 294 - gap / 2 }];


  // Number labels just outside the ring
  const numPos = segs.map(({ a0, a1 }) => polar(cx, cy, r + 38, (a0 + a1) / 2));
  // Tick marks at arc start
  const tickPos = segs.map(({ a0 }) => ({
    inner: polar(cx, cy, r - 8, a0),
    outer: polar(cx, cy, r + 8, a0)
  }));

  const phase = METHOD_PHASES[active];

  return (
    <div
      ref={wrapRef}
      className={"mc-method-wheel-wrap" + (scrollDriven ? " is-scroll-driven" : "") + (revealed ? " is-revealed" : "")}
      onMouseEnter={() => !scrollDriven && setPaused(true)}
      onMouseLeave={() => !scrollDriven && setPaused(false)}>

      {/* Curtain wipe that announces every phase transition — including the
          first entry into the section. Re-mounts on each change via the
          transitionTick key, so the animation always plays in full. */}
      <div
        key={"curtain-" + transitionTick}
        className={"mc-mw-curtain" + (transitionDir < 0 ? " is-reverse" : "")}
        aria-hidden="true">

        <div className="curtain-band" />
        <div className="curtain-marker">
          <span className="word">Phase</span>
          <span className="num">{active + 1}</span>
          <span className="rule" />
          <span className="name">{METHOD_PHASES[active].title}</span>
        </div>
      </div>

      <div className="mc-method-wheel" aria-hidden="false">
        <svg viewBox="0 0 600 600" role="img" aria-label={`Method phase ${phase.n} of ${METHOD_PHASES.length} — ${phase.title}`}>

          {/* decorative inner dashed ring */}
          <circle
            cx={cx} cy={cy} r={r - 64}
            fill="none"
            stroke="var(--rule)"
            strokeWidth="1"
            strokeDasharray="2 7"
            opacity="0.55" />


          {/* === Three ring segments with progressive fill === */}
          {/*
              Each phase is rendered as a thick hairline arc. As scroll
              progresses through that phase, a burgundy arc draws over the
              hairline from a→b. Completed phases stay solid burgundy.
              The unfilled portion of the active phase remains hairline.
          */}
          {segs.map((s, i) => {
            const isPast = i < active;
            const isCurrent = i === active;
            const phaseFill = isPast ? 1 : isCurrent ? scrollDriven ? localProgress : 1 : 0;
            return (
              <RingSegment
                key={"seg-" + i}
                cx={cx}
                cy={cy}
                r={r}
                a0={s.a0}
                a1={s.a1}
                fillProgress={phaseFill}
                isActive={isCurrent}
                isPast={isPast} />);


          })}

          {/* small ticks at arc starts */}
          {tickPos.map((t, i) =>
          <line
            key={"tick-" + i}
            x1={t.inner[0]} y1={t.inner[1]}
            x2={t.outer[0]} y2={t.outer[1]}
            stroke={i === active ? "var(--mc-burgundy)" : "var(--mc-stone)"}
            strokeWidth="1"
            opacity={i === active ? 1 : 0.5}
            style={{ transition: "stroke 600ms ease, opacity 600ms ease" }} />

          )}

          {/* phase numerals */}
          {segs.map((s, i) => {
            const [tx, ty] = numPos[i];
            return (
              <text
                key={"num-" + i}
                x={tx} y={ty}
                textAnchor="middle"
                dominantBaseline="middle"
                onClick={() => setActive(i)}
                style={{ cursor: "pointer" }}
                className={"mc-mw-num" + (i === active ? " is-active" : "") + (i < active ? " is-past" : "")}>

                {METHOD_PHASES[i].n}
              </text>);

          })}

          {/* progress dots no longer needed — the arc fill itself shows
              forward motion through each phase. */}
        </svg>

        {/* center label */}
        <div className="mc-mw-center">
          <div className="overline">The method</div>
          <div className="num" key={"n" + active}>{phase.n}</div>
          <div className="title" key={"t" + active}>{phase.title}</div>
        </div>
      </div>

      {/* right column — phase detail */}
      <div className="mc-mw-text">
        <div className="label">
          <span className="rule" aria-hidden="true" />
          Phase {phase.n} of {String(METHOD_PHASES.length).padStart(2, "0")}
        </div>
        <div className="mc-mw-pane" key={active}>
          <h3>{phase.title}.</h3>
          <p className="verb">{phase.verb}</p>
          <ul className="layers">
            {phase.layers.map((l, i) => {
              // As scroll progresses within a phase, a single bullet is
              // "spotlighted" — burgundy fill with cream text. All bullets
              // remain visible at full opacity; only the highlight moves.
              // While the curtain is playing on a fresh phase, the highlight
              // is held on the first item so the user can't "scroll past"
              // bullets they never saw highlighted.
              // As scroll progresses within a phase, a single bullet is
              // "spotlighted" — burgundy fill with cream text. All bullets
              // remain visible at full opacity; only the highlight moves.
              // We hold the first bullet for a brief "lead-in" (20%) so
              // phases with many bullets don't blow past the first 2-3 at
              // the start of the phase.
              const LEAD_IN = 0.20;
              const slot = scrollDriven ?
              curtainPlaying ? 0 :
              localProgress < LEAD_IN ?
              0 :
              Math.min(
                phase.layers.length - 1,
                Math.floor((localProgress - LEAD_IN) / (1 - LEAD_IN) * phase.layers.length)
              ) :
              -1;
              const isHighlighted = scrollDriven && i === slot;
              return (
                <li
                  key={l}
                  className={"layer is-visible" + (isHighlighted ? " is-highlight" : "")}>

                  <span className="bullet" aria-hidden="true" />
                  <span className="text">{l}</span>
                </li>);

            })}
          </ul>
        </div>
        <div className="mc-mw-progress">
          {METHOD_PHASES.map((p, i) => {
            const isActive = i === active;
            // In scroll mode, the bar fills based on local progress within
            // each phase (not global scroll progress).
            let localFill = null;
            if (scrollDriven) {
              if (i < active) localFill = 1;
              else if (i === active) localFill = localProgress;
              else localFill = 0;
            }
            return (
              <button
                key={i}
                className={"mc-mw-dot" + (isActive ? " is-active" : "") + (scrollDriven ? " is-scroll" : "")}
                onClick={() => !scrollDriven && setActive(i)}
                aria-label={`Show phase ${p.n}: ${p.title}`}>

                <span className="bar" aria-hidden="true">
                  <span
                    className="bar-fill"
                    style={scrollDriven ? {
                      right: `${(1 - localFill) * 100}%`,
                      opacity: localFill > 0 ? 1 : 0,
                      animation: "none"
                    } : {
                      animationPlayState: paused ? "paused" : "running"
                    }} />

                </span>
                <span className="lbl">{p.n} · {p.title}</span>
              </button>);

          })}
        </div>
      </div>
    </div>);

};

// A small dot that animates along the active arc each time the phase changes.
// Kept for the timer-mode (non-scroll) variant. Quiet visual signal.
const ProgressDot = ({ cx, cy, r, seg, keyTrigger }) => {
  const [t, setT] = React.useState(0);
  React.useEffect(() => {
    let raf;
    const start = performance.now();
    const tick = (now) => {
      const elapsed = (now - start) / MW_INTERVAL;
      setT(Math.min(elapsed, 1));
      if (elapsed < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [keyTrigger]);

  const a = seg.a0 + (seg.a1 - seg.a0) * t;
  const [x, y] = polar(cx, cy, r, a);
  return <circle cx={x} cy={y} r="4" fill="var(--mc-burgundy)" />;
};

// ===== MethodMobile =====
// Three stacked phase cards, each with its own small ring mark, phase
// number, title, verb, and layer list. No pin, no scroll-driven anim.
function MethodMobile() {
  return (
    <div className="mc-method-mobile">
      {METHOD_PHASES.map((p, i) =>
      <article key={i} className="mc-mm-card">
          <div className="mc-mm-head">
            <div className="mc-mm-num" aria-hidden="true">{p.n}</div>
            <div className="mc-mm-titles">
              <div className="mc-mm-label">
                <span className="rule" aria-hidden="true" />
                Phase {String(i + 1).padStart(2, "0")} of 03
              </div>
              <h3 className="mc-mm-title">{p.title}.</h3>
              <p className="mc-mm-verb">{p.verb}</p>
            </div>
          </div>
          <ul className="mc-mm-layers">
            {p.layers.map((l) =>
          <li key={l}>
                <span className="bullet" aria-hidden="true" />
                <span>{l}</span>
              </li>
          )}
          </ul>
        </article>
      )}
    </div>);

}

// ===== MethodSection =====
// Decides which variant to render: the animated scroll-driven wheel on
// desktop, or a clean stacked mobile layout. Keeps the rest of the page
// agnostic — Home.jsx just renders <MethodSection />.
function MethodSection() {
  const [isMobile, setIsMobile] = React.useState(
    typeof window !== "undefined" ? window.innerWidth < 720 : false
  );
  React.useEffect(() => {
    const mq = window.matchMedia("(max-width: 720px)");
    const onChange = (e) => setIsMobile(e.matches);
    if (mq.addEventListener) mq.addEventListener("change", onChange);else
    mq.addListener(onChange);
    return () => {
      if (mq.removeEventListener) mq.removeEventListener("change", onChange);else
      mq.removeListener(onChange);
    };
  }, []);

  if (isMobile) {
    return (
      <div className="mc-method-scroll mc-method-scroll-mobile">
        <div className="mc-method-sticky">
          <MethodWheel scrollDriven={true} />
        </div>
      </div>
    );
  }

  return (
    <div className="mc-method-scroll">
      <div className="mc-method-sticky">
        <MethodWheel scrollDriven={true} />
      </div>
    </div>);

}

Object.assign(window, { MethodWheel, MethodSection, MethodMobile });

// A thick ring segment that draws as a hairline base + a burgundy fill arc
// growing from a0 to (a0 + (a1-a0)*progress). Renders crisp on retina at
// any size because it's two SVG paths.
const RingSegment = ({ cx, cy, r, a0, a1, fillProgress, isActive, isPast }) => {
  // Base hairline (always rendered, slightly thicker for visual weight)
  // Fill arc — draws from a0 to current progress.
  const p = Math.max(0, Math.min(1, fillProgress));
  const aEnd = a0 + (a1 - a0) * p;
  const baseStrokeWidth = isActive ? 14 : isPast ? 14 : 12;
  const baseStroke = isPast || isActive ? "color-mix(in oklab, var(--mc-burgundy) 18%, transparent)" : "var(--rule)";
  const fillStroke = "var(--mc-burgundy)";
  return (
    <g>
      {/* base arc — the "empty" ring channel */}
      <path
        d={arcPath(cx, cy, r, a0, a1)}
        fill="none"
        stroke={baseStroke}
        strokeWidth={baseStrokeWidth}
        strokeLinecap="butt"
        style={{ transition: "stroke 600ms ease, stroke-width 600ms ease" }} />

      {/* fill arc — burgundy, grows from a0 to aEnd */}
      {p > 0.001 &&
      <path
        d={arcPath(cx, cy, r, a0, aEnd)}
        fill="none"
        stroke={fillStroke}
        strokeWidth={baseStrokeWidth}
        strokeLinecap="butt"
        style={{ transition: "stroke-width 600ms ease" }} />

      }
    </g>);

};
