/* ══════════════════════════════════════════════════
   PRODUCT VISUALS — the phone shell and every interface
   FRAGMENT the narrative uses.

   The redesign shows the app in pieces — a phone cropped by
   the fold, a lock-screen notification, an evidence card, a
   cinematic map — never three complete handsets side by side.
   Each chapter renders only the part of the product its one
   idea needs.
══════════════════════════════════════════════════ */

/* Counters that make the mockup feel live. Same cadence as the
   app's real send loop (a point every few seconds). */
const usePhoneSim = (running) => {
  const [points, setPoints]     = useState(1800);
  const [lastSend, setLastSend] = useState(0);
  const [km, setKm]             = useState(18.4);
  const [dur, setDur]           = useState(127);

  useEffect(() => {
    if (!running) return;
    const step = setInterval(() => {
      setPoints(p => p + 1);
      setLastSend(0);
      setKm(k => +(k + 0.1 + Math.random() * 0.05).toFixed(1));
      setDur(d => d + 1);
    }, 3400);
    const tick = setInterval(() => setLastSend(s => s + 1), 1000);
    return () => { clearInterval(step); clearInterval(tick); };
  }, [running]);

  const fmtLast = s => (s < 2 ? 'agora' : s < 60 ? `${s}s atrás` : `${Math.floor(s / 60)}min`);
  const fmtDur  = m => (m < 60 ? `${m}min` : `${Math.floor(m / 60)}h ${String(m % 60).padStart(2, '0')}min`);

  return { points, lastSend: fmtLast(lastSend), km, dur: fmtDur(dur) };
};

/* ── Phone hardware shell ─────────────────────────────
   Sized by --phone-w in css/styles.css (width, never
   transform:scale — a scaled frame keeps its stale layout box
   and crushes the app's 11px labels below legibility). */
const PhoneFrame = ({ children }) => (
  <div className="phone-frame"
       style={{ padding:10, background:T.ink, borderRadius:T.rPhone,
                boxShadow:T.shPhone, position:'relative', flexShrink:0 }}>
    {/* pill notch */}
    <div style={{ position:'absolute', top:20, left:'50%', transform:'translateX(-50%)',
                  width:74, height:7, background:'rgba(255,255,255,.22)', borderRadius:99, zIndex:8 }}/>
    <div className="phone-screen" style={{ position:'relative', borderRadius:30, overflow:'hidden', background:T.cream }}>
      {children}
    </div>
  </div>
);

/* ── CH.01 · hero ────────────────────────────────────
   Only the upper part of the device, cropped by the fold. The
   tracking screen runs its live counters; pointer-events are
   off because half a button is not a button. */
const PhoneCrop = () => {
  const stats = usePhoneSim(true);
  const Screen = SCREENS.tracking;
  return (
    <div className="phone-crop" aria-hidden="true">
      <PhoneFrame><Screen stats={stats} setView={() => {}}/></PhoneFrame>
    </div>
  );
};

/* ── CH.01 · institutional hero composition ─────────
   Documentary field photography leads the opening. The product UI now
   has its own full chapter immediately below, so both ideas can breathe. */
const HeroProductVisual = () => (
    <div className="hero-product-visual">
      <div className="hero-photo-frame">
        <img className="hero-field-photo" src={IMAGES.operation}
             alt="Entregador em motocicleta visto de cima durante uma rota urbana"/>
        <div className="hero-photo-shade" aria-hidden="true"/>
        <div className="hero-photo-label">
          <span className="live-dot"/>
          <div><strong>Operação em campo</strong><span>Rota acompanhada em tempo real</span></div>
        </div>
      </div>
    </div>
);

/* ── CH.02 · flow ────────────────────────────────────
   The two ends of the pipeline, each reduced to its essence:
   what the driver's phone is doing, and what the panel sees. */
const FragTrack = () => (
  <div className="frag">
    <div className="frag-head">
      <span className="frag-badge"><PPin sz={12} sw={2.6}/></span>
      <strong>Entregga Driver</strong>
      <span className="frag-live"><span className="live-dot" style={{ width:6, height:6, background:T.brand }}/>RASTREANDO</span>
    </div>
    <div className="frag-rows">
      <span><PGauge sz={13}/>18,4 km</span>
      <span><PPin sz={13}/>1.800 pontos</span>
      <span><PClock sz={13}/>3h 12min</span>
    </div>
  </div>
);

const FragPanel = () => (
  <div className="frag">
    <div className="frag-head">
      <span className="frag-dots" aria-hidden="true"><i/><i/><i/></span>
      <strong>Painel Entregga</strong>
      <span className="frag-live" style={{ color:T.green }}><span className="live-dot" style={{ width:6, height:6 }}/>AO VIVO</span>
    </div>
    <div className="frag-driver">
      <span className="frag-avatar"><PUser sz={13}/></span>
      <div style={{ minWidth:0 }}>
        <div className="frag-name">Nome do Motorista</div>
        <div className="frag-sub">Em rota · atualizado agora</div>
      </div>
      <span className="frag-ok"><PCheck sz={10}/></span>
    </div>
  </div>
);

/* ── CH.05 · evidence ────────────────────────────────
   A documentary handoff shows the proof left by an official delivery. */
const EvidenceCard = () => (
  <div className="evidence-product-art">
    <div className="evidence-product-shot">
      <img src={IMAGES.evidenceArt} alt="Entregador registrando por foto a entrega de um envelope em um endereço comercial"/>
    </div>
    <div className="evidence-product-badge">
      <span><PCheck sz={12}/></span>
      <div><strong>Entrega comprovada</strong><small>Foto, assinatura e protocolo registrados</small></div>
    </div>
  </div>
);

/* ── CH.04 · geolocation campaign visual ───────────
   A true overhead street photograph makes location feel precise and real. */
const ProductRouteMap = ({ active = true }) => {
  const [toast, setToast] = useState(0);
  useEffect(() => {
    if (!active || REDUCED_MOTION) return;
    const id = setInterval(() => setToast(t => t + 1), 4600);
    return () => clearInterval(id);
  }, [active]);

  return (
    <div className="geo-stage geo-product-stage">
      <div className="geo-product-shot">
        <img src={IMAGES.geoArt} alt="Entregador visto de cima atravessando um cruzamento urbano durante a rota"/>
      </div>
      <div className="geo-product-shade" aria-hidden="true"/>
      <div className="geo-chip geo-chip-addr">
        <span className="geo-chip-ic"><PPin sz={13}/></span>
        <div><strong>8.560 pontos registrados</strong><em>trajeto completo do motorista</em></div>
      </div>
      <div className="geo-chip geo-chip-time">
        <span className="geo-chip-ic"><PClock sz={13}/></span>
        <div><strong>06:13 → 18:59</strong><em>12h 46min em rota</em></div>
      </div>
      {active && !REDUCED_MOTION && (
        <div key={toast} className="geo-toast">
          <span className="live-dot" style={{ width:6, height:6 }}/> posição sincronizada
        </div>
      )}
    </div>
  );
};

/* ── CH.06 · background ──────────────────────────────
   The app seen from the driver's side of the day: a lock
   screen. The product here is the notification — the driver
   didn't open anything, and the work got recorded anyway. */
const NotificationPhone = () => (
  <PhoneFrame>
    <div className="lockscreen">
      <div className="lock-clock">{PHONE.clock}</div>
      <div className="lock-date">terça-feira, 17 de junho</div>
      <div className="notif-card">
        <span className="notif-ic"><PPin sz={15} sw={2.6}/></span>
        <div className="notif-body">
          <div className="notif-title">Entregga Driver <em>agora</em></div>
          <div className="notif-text">Rastreamento ativo. 1.800 pontos enviados hoje.</div>
        </div>
      </div>
      <div className="notif-card notif-dim">
        <span className="notif-ic notif-ic-green"><PCheck sz={13}/></span>
        <div className="notif-body">
          <div className="notif-title">Entregga Driver <em>14:32</em></div>
          <div className="notif-text">Entrega comprovada na Rua do Comércio, 1204.</div>
        </div>
      </div>
    </div>
  </PhoneFrame>
);

/* ── CH.04 · geolocation ─────────────────────────────
   The follow-camera route map at cinematic size. A procedural
   manhattan grid; the camera tracks the pin; a toast lands
   every few seconds as a point is captured. Runs its rAF loop
   only while `active` (the section gates it by visibility).
   SVG shapes take flat HEX.* — var() dies silently in SVG
   presentation attributes. */
const RouteMap = ({ active = true }) => {
  const VIEW_W = 760, VIEW_H = 400, STEP = 56;
  const clipId = React.useId();

  const routeRef = useRef(null);
  if (!routeRef.current) {
    const pts = [{ x:0, y:0 }];
    let lastDir = 0;                       // 0=right 1=down 2=left 3=up
    for (let i = 0; i < 70; i++) {
      const choices  = [0, 1, 2, 3].filter(d => Math.abs(d - lastDir) !== 2);
      const weighted = [...choices, 0, 0]; // bias rightward so the route trends forward
      const dir = weighted[Math.floor(Math.random() * weighted.length)];
      lastDir = dir;
      const len  = (1 + Math.floor(Math.random() * 3)) * STEP;
      const last = pts[pts.length - 1];
      pts.push({
        x: last.x + (dir === 0 ? len : dir === 2 ? -len : 0),
        y: last.y + (dir === 1 ? len : dir === 3 ? -len : 0),
      });
    }
    routeRef.current = pts;
  }
  const route = routeRef.current;

  const segsRef = useRef(null);
  if (!segsRef.current) {
    const segs = [];
    let total = 0;
    for (let i = 1; i < route.length; i++) {
      const a = route[i - 1], b = route[i];
      const len = Math.hypot(b.x - a.x, b.y - a.y);
      segs.push({ a, b, len, start: total });
      total += len;
    }
    segsRef.current = { segs, total };
  }
  const { segs, total } = segsRef.current;

  const SPEED = 30; // px/s
  const distRef   = useRef(80);
  const lastTsRef = useRef(0);
  const [, force] = useState(0);

  useEffect(() => {
    if (!active || REDUCED_MOTION) return;
    let raf;
    const loop = ts => {
      if (!lastTsRef.current) lastTsRef.current = ts;
      distRef.current += SPEED * (ts - lastTsRef.current) / 1000;
      lastTsRef.current = ts;
      if (distRef.current >= total - 80) distRef.current = 80;
      force(t => t + 1);
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => { cancelAnimationFrame(raf); lastTsRef.current = 0; };
  }, [active]);

  /* "+1 ponto capturado" toast, re-keyed to replay its animation. */
  const [toast, setToast] = useState(0);
  useEffect(() => {
    if (!active || REDUCED_MOTION) return;
    const id = setInterval(() => setToast(t => t + 1), 4600);
    return () => clearInterval(id);
  }, [active]);

  const distToPoint = d => {
    for (const s of segs) {
      if (d <= s.start + s.len) {
        const t = (d - s.start) / s.len;
        return { x: s.a.x + (s.b.x - s.a.x) * t, y: s.a.y + (s.b.y - s.a.y) * t };
      }
    }
    const last = route[route.length - 1];
    return { x: last.x, y: last.y };
  };

  const pin        = distToPoint(distRef.current);
  const TRAIL      = 460;
  const trailStart = Math.max(0, distRef.current - TRAIL);
  const trailPts   = [distToPoint(trailStart)];
  for (const s of segs) {
    if (s.start > trailStart && s.start <= distRef.current) trailPts.push({ x:s.a.x, y:s.a.y });
  }
  trailPts.push(pin);
  const trailD = trailPts.map((p, i) => (i ? 'L' : 'M') + p.x.toFixed(1) + ',' + p.y.toFixed(1)).join(' ');

  const camX = VIEW_W / 2 - pin.x;
  const camY = VIEW_H / 2 - pin.y;

  const gridSpacing = 56, gridSize = 1600, blockSize = 112;
  const originX = Math.floor((pin.x - gridSize / 2) / gridSpacing) * gridSpacing;
  const originY = Math.floor((pin.y - gridSize / 2) / gridSpacing) * gridSpacing;

  /* Deterministic per-coordinate hash so each block keeps its tone while
     the camera pans — a uniform grid reads as wallpaper, not a city. */
  const blockTone = (bx, by) => {
    const h = Math.abs(Math.sin(bx * 12.9898 + by * 78.233) * 43758.5453) % 1;
    return h < 0.09 ? HEX.park : h < 0.55 ? HEX.block : HEX.block2;
  };
  const blocks = [];
  for (let bx = originX; bx < originX + gridSize; bx += blockSize)
    for (let by = originY; by < originY + gridSize; by += blockSize)
      blocks.push({ x:bx + 5, y:by + 5, w:blockSize - 10, h:blockSize - 10, f:blockTone(bx, by) });

  const lines = [];
  for (let i = 0; i <= gridSize / gridSpacing; i++) lines.push(i * gridSpacing);

  /* The chapter's actual message: capture points, dotted along the trail
     at a fixed distance interval. */
  const CAPTURE_EVERY = 120;
  const capturePts = [];
  for (let d = Math.ceil(trailStart / CAPTURE_EVERY) * CAPTURE_EVERY; d < distRef.current; d += CAPTURE_EVERY)
    capturePts.push(distToPoint(d));

  return (
    <div className="geo-stage">
      <svg viewBox={`0 0 ${VIEW_W} ${VIEW_H}`} preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <clipPath id={clipId}><rect x="0" y="0" width={VIEW_W} height={VIEW_H}/></clipPath>
        </defs>
        <rect x="0" y="0" width={VIEW_W} height={VIEW_H} fill={HEX.mapBg}/>
        <g clipPath={`url(#${clipId})`}>
          <g transform={`translate(${camX.toFixed(2)} ${camY.toFixed(2)})`}>
            {blocks.map((b, i) => <rect key={'b'+i} x={b.x} y={b.y} width={b.w} height={b.h} rx="5" fill={b.f}/>)}
            {lines.map((v, i) => <line key={'gx'+i} x1={originX+v} y1={originY} x2={originX+v} y2={originY+gridSize} stroke={HEX.road} strokeWidth="9"/>)}
            {lines.map((v, i) => <line key={'gy'+i} x1={originX} y1={originY+v} x2={originX+gridSize} y2={originY+v} stroke={HEX.road} strokeWidth="9"/>)}
            {/* white casing lifts the trail off same-value roads */}
            <path d={trailD} fill="none" stroke="#fff" strokeWidth="8.5" strokeLinecap="round" strokeLinejoin="round" opacity=".85"/>
            <path d={trailD} fill="none" stroke={HEX.green} strokeWidth="5" strokeLinecap="round" strokeLinejoin="round"/>
            {capturePts.map((p, i) => (
              <circle key={'c'+i} cx={p.x.toFixed(1)} cy={p.y.toFixed(1)} r="4.5" fill="#fff" stroke={HEX.green} strokeWidth="2.4"/>
            ))}
          </g>
          <circle cx={VIEW_W/2} cy={VIEW_H/2} r="14" fill={HEX.brand} opacity={active ? '.26' : '.14'}>
            {active && !REDUCED_MOTION && <animate attributeName="r"       values="10;18;10" dur="1.5s" repeatCount="indefinite"/>}
            {active && !REDUCED_MOTION && <animate attributeName="opacity" values=".3;0;.3"  dur="1.5s" repeatCount="indefinite"/>}
          </circle>
          <circle cx={VIEW_W/2} cy={VIEW_H/2} r="7.5" fill={HEX.brand} stroke="#fff" strokeWidth="3"/>
        </g>
      </svg>

      <div className="geo-chip geo-chip-addr">
        <span className="geo-chip-ic"><PPin sz={13}/></span>
        <div><strong>Rua do Comércio, 1204</strong><em>endereço do ponto atual</em></div>
      </div>
      <div className="geo-chip geo-chip-time">
        <span className="geo-chip-ic"><PClock sz={13}/></span>
        <div><strong>{PHONE.clock} · em rota</strong><em>há 3h 12min</em></div>
      </div>
      {active && !REDUCED_MOTION && (
        <div key={toast} className="geo-toast">
          <span className="live-dot" style={{ width:6, height:6 }}/> +1 ponto capturado
        </div>
      )}
    </div>
  );
};
