// process.jsx - "From kickoff to production". Horizontal timeline adapted from the
// original site: the line draws in, dots light in sequence, then a slow light-beam
// travels the line making each dot "breathe" as it passes. Hover reveals detail.
// Rebuilt on-brand: Violet Blue accent (no Ember), solid accent title (no gradient text).

const STEPS = [
  { title: 'Discovery', sub: 'Scoping your goals', time: '1–2 weeks', desc: 'We map your business goals, technical constraints, and success criteria in focused workshops.' },
  { title: 'Architecture', sub: 'Designing for scale', time: '1–2 weeks', desc: 'Data models, APIs, infrastructure, and deployment strategy, documented before a line of code.' },
  { title: 'Build', sub: 'Shipping working software', time: '4–12 weeks', desc: 'Senior engineers write production code in 1-week sprints with daily visibility.' },
  { title: 'Deployment', sub: 'Launch with confidence', time: '1–2 weeks', desc: 'Rigorous testing, staged rollouts, and comprehensive documentation.' },
  { title: 'Evolution', sub: 'Monitoring and iteration', time: 'Ongoing', desc: 'Production monitoring, SLA-backed support, and quarterly roadmap reviews.' },
];

// dot center in px from the left of the container. Flex space-between with 6px
// padding and 100px items puts the first center at 56px and the last at W-56px.
const HALF = 56;
function dotPx(i, width) {
  const track = width - 2 * HALF;
  return HALF + (i / (STEPS.length - 1)) * track;
}

function Process() {
  const [ref, inView] = useInView({ threshold: 0.3, once: false });
  const wrapRef = React.useRef(null);
  const lineRef = React.useRef(null);
  const beamRef = React.useRef(null);
  const dotRefs = React.useRef([]);
  const bgRef = React.useRef(null);
  const beamRaf = React.useRef(null);
  const drawRaf = React.useRef(null);
  const [lit, setLit] = React.useState(() => new Set());
  const [hovered, setHovered] = React.useState(null);

  // measure real dot centers (px, relative to .ptl) so the line/beam align exactly
  const measure = () => {
    const wrap = wrapRef.current;
    if (!wrap) return null;
    const wl = wrap.getBoundingClientRect().left;
    const centers = dotRefs.current.map((el) => {
      if (!el) return 0;
      const r = el.getBoundingClientRect();
      return r.left + r.width / 2 - wl;
    });
    return { centers, first: centers[0], last: centers[centers.length - 1] };
  };

  // pin the background line to span exactly first→last dot center
  const placeBg = () => {
    const m = measure();
    if (!m || !bgRef.current || !lineRef.current) return m;
    bgRef.current.style.left = m.first + 'px';
    bgRef.current.style.right = '';
    bgRef.current.style.width = (m.last - m.first) + 'px';
    lineRef.current.style.left = m.first + 'px';
    return m;
  };

  // light-beam loop: travels the line, dots breathe as it passes
  const startBeam = React.useCallback(() => {
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    const duration = 13000, travel = 0.78;
    let start;
    const loop = (now) => {
      if (!start) start = now;
      const m = measure();
      if (!m) { beamRaf.current = requestAnimationFrame(loop); return; }
      const range = m.last - m.first;
      const prog = ((now - start) % duration) / duration;
      const tp = prog < travel ? prog / travel : 1;
      const cur = m.first + tp * range; // px
      let op = 1;
      if (prog >= travel) op = 0;
      else { const n = prog / travel; if (n < 0.1) op = n / 0.1; else if (n > 0.9) op = (1 - n) / 0.1; }
      if (beamRef.current) { beamRef.current.style.left = cur + 'px'; beamRef.current.style.opacity = op; }
      dotRefs.current.forEach((el, i) => {
        if (!el) return;
        const d = Math.abs(cur - m.centers[i]);
        const k = op > 0 ? Math.max(0, 1 - d / 60) : 0;
        el.style.transform = `scale(${(1 + k * 0.55).toFixed(3)})`;
        el.style.boxShadow = `0 0 ${(10 + k * 20).toFixed(0)}px color-mix(in srgb, var(--accent) ${(35 + k * 50).toFixed(0)}%, transparent)`;
      });
      beamRaf.current = requestAnimationFrame(loop);
    };
    beamRaf.current = requestAnimationFrame(loop);
  }, []);

  React.useEffect(() => {
    const stopBeam = () => { if (beamRaf.current) cancelAnimationFrame(beamRaf.current); };
    if (!inView) {
      setLit(new Set());
      if (lineRef.current) lineRef.current.style.width = '0px';
      if (drawRaf.current) cancelAnimationFrame(drawRaf.current);
      stopBeam();
      return;
    }
    const m = placeBg();
    if (!m) return;
    const track = m.last - m.first;
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
      setLit(new Set(STEPS.map((_, i) => i)));
      if (lineRef.current) lineRef.current.style.width = track + 'px';
      return;
    }
    let start;
    const dur = 2100;
    const tick = (now) => {
      if (!start) start = now;
      const p = Math.min(1, (now - start) / dur);
      const eased = 1 - Math.pow(1 - p, 3);
      const mm = placeBg() || m; // re-measure each frame (layout/scrollbar can settle)
      const tk = mm.last - mm.first;
      const curW = eased * tk;
      if (lineRef.current) lineRef.current.style.width = curW + 'px';
      setLit((prev) => {
        let changed = false; const s = new Set(prev);
        STEPS.forEach((_, i) => { if (mm.first + curW >= mm.centers[i] - 1 && !s.has(i)) { s.add(i); changed = true; } });
        return changed ? s : prev;
      });
      if (p < 1) drawRaf.current = requestAnimationFrame(tick);
      else { setLit(new Set(STEPS.map((_, i) => i))); const mf = placeBg() || mm; if (lineRef.current) lineRef.current.style.width = (mf.last - mf.first) + 'px'; startBeam(); }
    };
    drawRaf.current = requestAnimationFrame(tick);
    const onResize = () => { const mr = placeBg(); if (mr && lineRef.current) lineRef.current.style.width = (mr.last - mr.first) + 'px'; };
    window.addEventListener('resize', onResize);
    return () => { if (drawRaf.current) cancelAnimationFrame(drawRaf.current); stopBeam(); window.removeEventListener('resize', onResize); };
  }, [inView, startBeam]);

  const Label = ({ s, i, isLit, where }) => (
    <div className={`ptl-label ${where}`} style={{ opacity: isLit ? 1 : 0.4, transform: `translateX(-50%) translateY(${isLit ? 0 : (where === 'above' ? 6 : -6)}px)`, transitionDelay: `${i * 80}ms` }}>
      <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: '1.1rem', margin: 0, color: isLit ? 'var(--fg)' : 'var(--fg-faint)' }}>{s.title}</h3>
      <p style={{ fontSize: 12.5, fontWeight: 500, margin: '5px 0 0', color: isLit ? 'var(--accent)' : 'var(--fg-faint)' }}>{s.sub}</p>
      <span className="mono" style={{ fontSize: 10.5, textTransform: 'uppercase', letterSpacing: '0.12em', color: 'var(--fg-faint)', display: 'block', marginTop: 5 }}>{s.time}</span>
    </div>
  );

  return (
    <section id="process" data-screen-label="Process" className="sec" aria-labelledby="proc-h"
      style={{ minHeight: 'calc(100svh - 72px)', display: 'flex', flexDirection: 'column', justifyContent: 'center', paddingBlock: 'clamp(24px, 3.5vh, 56px)' }}>
      <div className="sec-wrap">
        <SectionHead eyebrow="How we work" align="center" max={720} style={{ marginBottom: 'clamp(24px, 4vh, 48px)' }}
          sub="A predictable path from first conversation to production, and beyond.">
          <span id="proc-h">From kickoff to <span className="accent">production</span></span>
        </SectionHead>

        {/* desktop horizontal timeline */}
        <div className="ptl" ref={(el) => { ref.current = el; wrapRef.current = el; }}>
          <div className="ptl-line-bg" ref={bgRef} />
          <div className="ptl-line-fill" ref={lineRef} />
          <div className="ptl-beam" ref={beamRef} />
          <div className="ptl-steps">
            {STEPS.map((s, i) => {
              const isLit = lit.has(i);
              const above = i % 2 === 0;
              return (
                <div key={s.title} className="ptl-step" tabIndex={0}
                  onMouseEnter={() => setHovered(i)} onMouseLeave={() => setHovered(null)}
                  onFocus={() => setHovered(i)} onBlur={() => setHovered(null)}
                  aria-label={`${s.title}: ${s.sub}, ${s.time}`}>
                  <Label s={s} i={i} isLit={isLit} where={above ? 'above' : 'below'} />
                  <div ref={(el) => (dotRefs.current[i] = el)} className="ptl-dot" style={{
                    background: isLit ? 'var(--accent)' : 'var(--bg)',
                    border: isLit ? 'none' : '2px solid color-mix(in srgb, var(--accent) 35%, transparent)',
                    boxShadow: isLit ? '0 0 12px color-mix(in srgb, var(--accent) 40%, transparent)' : 'none',
                  }} />
                  <div className={`ptl-tip glass eglass eglass-soft ${above ? 'above' : 'below'}`} aria-hidden={hovered !== i}
                    style={{ opacity: hovered === i ? 1 : 0, transform: `translateX(-50%) translateY(${hovered === i ? 0 : (above ? -8 : 8)}px)` }}>
                    <p style={{ fontSize: 12.5, lineHeight: 1.6, color: 'var(--fg-muted)', margin: 0 }}>{s.desc}</p>
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        {/* mobile vertical timeline */}
        <div className="ptl-mobile">
          <div className="vline" />
          <div style={{ display: 'flex', flexDirection: 'column', gap: 22 }}>
            {STEPS.map((s, i) => (
              <Reveal key={s.title} delay={i * 80} style={{ display: 'flex', gap: 16, position: 'relative' }}>
                <div style={{ flexShrink: 0, width: 24, height: 24, borderRadius: 999, marginTop: 2, display: 'flex', alignItems: 'center', justifyContent: 'center', border: '2px solid var(--accent)', background: 'var(--bg)' }}>
                  <span style={{ width: 8, height: 8, borderRadius: 999, background: 'var(--accent)', boxShadow: '0 0 8px color-mix(in srgb, var(--accent) 60%, transparent)' }} />
                </div>
                <div>
                  <div style={{ display: 'flex', alignItems: 'baseline', gap: 10, flexWrap: 'wrap' }}>
                    <h3 style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: '1rem', color: 'var(--fg)', margin: 0 }}>{s.title}</h3>
                    <span className="mono" style={{ fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.12em', color: 'var(--fg-faint)' }}>{s.time}</span>
                  </div>
                  <p style={{ fontSize: 12.5, color: 'var(--accent)', margin: '4px 0 0', fontWeight: 500 }}>{s.sub}</p>
                  <p style={{ fontSize: 13, color: 'var(--fg-muted)', lineHeight: 1.6, margin: '6px 0 0' }}>{s.desc}</p>
                </div>
              </Reveal>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
}

window.Process = Process;
