/* global React */
/*
 * PortariaScene — wide cinematic SVG scene driven by scroll progress (0..1).
 *
 * Phases:
 *   P0  0.00–0.10  establish — calm portaria, gate down, doorman idle
 *   P1  0.10–0.32  chaos — cars arrive and stack up
 *   P2  0.32–0.42  peak — doorman overwhelmed
 *   P3  0.42–0.50  reset — cars fade away
 *   P4  0.50–0.60  Luna enters from above
 *   P5  0.60–0.72  Luna installs camera on the pole
 *   P6  0.72–0.80  system online — beam connects camera ↔ booth
 *   P7  0.80–1.00  solved — cars return; smooth flow, scans, decisions (loops forever)
 */

const { useState, useEffect, useRef } = React;

const clamp = (v, a = 0, b = 1) => Math.max(a, Math.min(b, v));
const lerp = (a, b, t) => a + (b - a) * t;
const smooth = (t) => t * t * (3 - 2 * t);
const easeOut = (t) => 1 - Math.pow(1 - t, 3);
const easeIn = (t) => t * t * t;
const rng = (p, a, b) => clamp((p - a) / (b - a), 0, 1);

/* ─────────────────── Car (sedan side-view, facing LEFT) ───────────────────
 *  Local coords:  width 200 × height ~80 (body 10..60, wheels to 78)
 *  Origin (0,0) = top-left bounding box. Front bumper is on the LEFT.
 */
function Car({ x, y = 600, color = "#cfd6e6", opacity = 1, scanned = false, plate = null, decision = null }) {
  return (
    <g transform={`translate(${x}, ${y})`} opacity={opacity}>
      {/* shadow under car */}
      <ellipse cx="100" cy="78" rx="105" ry="6" fill="#000" opacity="0.55" />

      {/* body silhouette: front-left hood → windshield → roof → rear window → trunk → rear bumper */}
      <path
        d="M 4 52
           L 14 52
           L 18 38
           Q 24 30 34 30
           L 58 30
           L 78 14
           Q 84 11 92 11
           L 142 11
           Q 150 11 156 17
           L 170 30
           L 184 30
           Q 192 30 194 38
           L 198 52
           L 198 60
           L 4 60 Z"
        fill={color}
      />

      {/* lower-body shading */}
      <path d="M 4 52 L 198 52 L 198 60 L 4 60 Z" fill="#000" opacity="0.18" />

      {/* windows (windshield + rear) */}
      <path
        d="M 62 30 L 80 16 Q 84 13 92 13 L 142 13 Q 150 13 154 17 L 166 30 Z"
        fill="#0d1a30"
      />
      <path
        d="M 62 30 L 80 16 Q 84 13 92 13 L 142 13 Q 150 13 154 17 L 166 30 Z"
        fill="#7ab8ff" opacity="0.22"
      />
      {/* B-pillar separating windshield from rear window */}
      <line x1="113" y1="13" x2="113" y2="30" stroke="#0a1428" strokeWidth="2" />

      {/* roof highlight (subtle gloss) */}
      <path d="M 80 16 Q 84 13 92 13 L 130 13 L 122 22 L 78 22 Z" fill="#fff" opacity="0.10" />

      {/* door line */}
      <line x1="100" y1="30" x2="100" y2="52" stroke="#0a1428" strokeWidth="1" opacity="0.35" />
      {/* door handle */}
      <rect x="80" y="42" width="14" height="3" rx="1" fill="#0a1428" opacity="0.5" />

      {/* wheels (with arch shadows) */}
      <g>
        <ellipse cx="40" cy="60" rx="20" ry="4" fill="#000" opacity="0.4" />
        <circle cx="40" cy="62" r="15" fill="#0a0a14" />
        <circle cx="40" cy="62" r="9" fill="#3b4a66" />
        <circle cx="40" cy="62" r="4" fill="#0a0a14" />
        <ellipse cx="160" cy="60" rx="20" ry="4" fill="#000" opacity="0.4" />
        <circle cx="160" cy="62" r="15" fill="#0a0a14" />
        <circle cx="160" cy="62" r="9" fill="#3b4a66" />
        <circle cx="160" cy="62" r="4" fill="#0a0a14" />
      </g>

      {/* front headlight (LEFT side — car faces left) */}
      <ellipse cx="6" cy="44" rx="4.5" ry="5.5" fill="#fff8c0" />
      <ellipse cx="6.5" cy="43.5" rx="2" ry="3" fill="#ffffff" />
      {/* front plate */}
      <rect x="22" y="48" width="18" height="7" rx="1" fill="#f6f8ff" opacity="0.9" />
      <rect x="22" y="48" width="18" height="7" rx="1" fill="none" stroke="#0a1428" strokeWidth="0.6" opacity="0.5" />

      {/* tail light (right) */}
      <rect x="189" y="40" width="6" height="10" rx="2" fill="#E74C3C" opacity="0.9" />
      <rect x="189" y="48" width="6" height="4" rx="1" fill="#f4c34d" opacity="0.8" />

      {/* ───── Scan overlay (Luna's identifier) ───── */}
      {scanned && (
        <g>
          {/* dashed framing */}
          <rect x="0" y="2" width="200" height="76" fill="none"
            stroke="#4A90CC" strokeWidth="1" strokeDasharray="4 6" opacity="0.5" />

          {/* corner brackets (legs 18px each) */}
          {[
            { x: 0,   y: 2,  dx:  1, dy:  1 },
            { x: 200, y: 2,  dx: -1, dy:  1 },
            { x: 0,   y: 78, dx:  1, dy: -1 },
            { x: 200, y: 78, dx: -1, dy: -1 },
          ].map((c, i) => (
            <g key={i} stroke="#7ab8ff" strokeWidth="2" strokeLinecap="square" fill="none">
              <path d={`M ${c.x} ${c.y} l ${18 * c.dx} 0`} />
              <path d={`M ${c.x} ${c.y} l 0 ${18 * c.dy}`} />
            </g>
          ))}

          {/* center crosshair on the windshield */}
          <g stroke="#7ab8ff" strokeWidth="1" opacity="0.7">
            <line x1="95"  y1="22" x2="105" y2="22" />
            <line x1="100" y1="17" x2="100" y2="27" />
            <circle cx="100" cy="22" r="6" fill="none" />
          </g>

          {/* connector dot above */}
          <circle cx="100" cy="-6" r="3" fill="#7ab8ff" />
          <line x1="100" y1="-6" x2="100" y2="2" stroke="#7ab8ff" strokeWidth="1" opacity="0.6" />

          {/* PLATE readout — left */}
          {plate && (
            <g transform="translate(-2, -38)">
              <rect width="96" height="28" rx="3" fill="#0a1428" stroke="#4A90CC" strokeWidth="1.2" />
              <rect x="0" y="0" width="96" height="9" rx="3" fill="#4A90CC" opacity="0.18" />
              <text x="6" y="7" fontFamily="JetBrains Mono" fontSize="6" fill="#7ab8ff" letterSpacing="0.18em" fontWeight="600">PLACA</text>
              <text x="48" y="22" textAnchor="middle"
                fontFamily="JetBrains Mono" fontSize="12" fontWeight="700"
                fill="#cfe4ff" letterSpacing="0.14em">{plate}</text>
            </g>
          )}

          {/* STATUS / decision — right */}
          {decision && (() => {
            const ok = decision === 'AUTORIZADO';
            const visit = decision === 'VISITANTE';
            const stroke = ok ? '#5fd29a' : visit ? '#f4c34d' : '#E74C3C';
            const tint   = ok ? 'rgba(95,210,154,0.12)' : visit ? 'rgba(244,195,77,0.12)' : 'rgba(231,76,60,0.14)';
            return (
              <g transform="translate(106, -38)">
                <rect width="96" height="28" rx="3" fill="#0a1428" stroke={stroke} strokeWidth="1.2" />
                <rect width="96" height="28" rx="3" fill={tint} />
                <rect x="0" y="0" width="96" height="9" rx="3" fill={stroke} opacity="0.18" />
                <text x="6" y="7" fontFamily="JetBrains Mono" fontSize="6" fill={stroke} letterSpacing="0.18em" fontWeight="600">STATUS</text>
                <text x="48" y="22" textAnchor="middle"
                  fontFamily="JetBrains Mono" fontSize="11" fontWeight="700"
                  fill={stroke} letterSpacing="0.10em">{decision}</text>
              </g>
            );
          })()}
        </g>
      )}
    </g>
  );
}

/* ─────────────────── Guard booth ─────────────────── */
function GuardBooth({ doormanState = 'calm', linked = false }) {
  return (
    <g>
      {/* booth shadow on ground */}
      <ellipse cx="110" cy="725" rx="130" ry="8" fill="#000" opacity="0.5" />
      {/* booth body */}
      <rect x="20" y="510" width="200" height="210" fill="#10203d" stroke="#1a2a4a" strokeWidth="2" />
      {/* roof */}
      <rect x="10" y="498" width="220" height="18" fill="#1a2a4a" />
      <rect x="10" y="498" width="220" height="4" fill="#2E75B6" opacity="0.5" />
      {/* window frame */}
      <rect x="44" y="538" width="152" height="112" fill="#050a17" stroke="#2E75B6" strokeWidth="2" />
      {/* horizontal window slats */}
      <path d="M44 590 L196 590" stroke="#2E75B6" strokeWidth="1" opacity="0.4" />
      <path d="M120 538 L120 650" stroke="#2E75B6" strokeWidth="1" opacity="0.4" />
      {/* inside glow */}
      <rect x="46" y="540" width="148" height="108" fill="#1a2e52" opacity="0.4" />

      {/* doorman head & shoulders */}
      <g transform="translate(120, 612)">
        {/* shoulders / uniform */}
        <path d="M-44 36 Q-44 8 0 5 Q44 8 44 36 L44 50 L-44 50 Z" fill="#1E3A5F" />
        <path d="M-44 36 Q-44 8 0 5 Q44 8 44 36 L44 42 L-44 42 Z" fill="#2E75B6" opacity="0.6" />
        {/* tie / collar */}
        <path d="M-6 5 L 0 18 L 6 5 Z" fill="#0a1428" />
        {/* head */}
        <ellipse cx="0" cy="-18" rx="22" ry="24" fill="#d6a988" />
        {/* hair */}
        <path d="M-22 -22 Q-22 -42 0 -42 Q22 -42 22 -22 L18 -18 Q15 -30 0 -30 Q-15 -30 -18 -18 Z" fill="#1a0e08" />
        {/* cap brim */}
        <path d="M-24 -22 L24 -22 L20 -18 L-20 -18 Z" fill="#0a1428" />

        {/* eyes — vary with state */}
        <Eyes state={doormanState} />

        {/* mouth */}
        <Mouth state={doormanState} />

        {/* sweat for stressed */}
        {doormanState === 'stressed' && (
          <g>
            <path d="M-24 -16 Q-26 -8 -24 -2 Q-22 -8 -24 -16 Z" fill="#7ab8ff" />
            <path d="M22 -12 Q24 -4 22 2 Q20 -4 22 -12 Z" fill="#7ab8ff" opacity="0.85" />
          </g>
        )}
      </g>

      {/* link indicator above booth */}
      {linked && (
        <g transform="translate(120, 488)">
          <circle r="6" fill="#5fd29a" />
          <circle r="12" fill="none" stroke="#5fd29a" strokeWidth="1" opacity="0.5">
            <animate attributeName="r" values="6;14;6" dur="1.6s" repeatCount="indefinite" />
            <animate attributeName="opacity" values="0.6;0;0.6" dur="1.6s" repeatCount="indefinite" />
          </circle>
        </g>
      )}
    </g>
  );
}

function Eyes({ state }) {
  if (state === 'stressed') {
    return (
      <g>
        <path d="M-14 -16 L-4 -12" stroke="#0a1428" strokeWidth="2.5" strokeLinecap="round" />
        <path d="M14 -16 L4 -12" stroke="#0a1428" strokeWidth="2.5" strokeLinecap="round" />
        <circle cx="-9" cy="-12" r="2.5" fill="#0a1428" />
        <circle cx="9" cy="-12" r="2.5" fill="#0a1428" />
      </g>
    );
  }
  if (state === 'focused') {
    return (
      <g>
        <path d="M-15 -14 L-3 -14" stroke="#0a1428" strokeWidth="2" strokeLinecap="round" />
        <path d="M3 -14 L15 -14" stroke="#0a1428" strokeWidth="2" strokeLinecap="round" />
        <rect x="-11" y="-13" width="6" height="4" fill="#0a1428" />
        <rect x="5" y="-13" width="6" height="4" fill="#0a1428" />
      </g>
    );
  }
  if (state === 'happy') {
    return (
      <g>
        <path d="M-12 -12 Q-9 -16 -6 -12" stroke="#0a1428" strokeWidth="2" fill="none" strokeLinecap="round" />
        <path d="M12 -12 Q9 -16 6 -12" stroke="#0a1428" strokeWidth="2" fill="none" strokeLinecap="round" />
      </g>
    );
  }
  return (
    <g>
      <circle cx="-9" cy="-12" r="2.4" fill="#0a1428" />
      <circle cx="9" cy="-12" r="2.4" fill="#0a1428" />
    </g>
  );
}

function Mouth({ state }) {
  if (state === 'stressed') {
    return <path d="M-7 -2 Q0 -8 7 -2" stroke="#0a1428" strokeWidth="1.8" fill="none" strokeLinecap="round" />;
  }
  if (state === 'focused') {
    return <path d="M-6 -2 L6 -2" stroke="#0a1428" strokeWidth="1.8" strokeLinecap="round" />;
  }
  if (state === 'happy') {
    return <path d="M-7 -3 Q0 4 7 -3" stroke="#0a1428" strokeWidth="1.8" fill="none" strokeLinecap="round" />;
  }
  return <path d="M-5 -2 L5 -2" stroke="#0a1428" strokeWidth="1.5" strokeLinecap="round" />;
}

/* ─────────────────── Gate (cancela) ─────────────────── */
function Cancela({ openness = 0 }) {
  const angle = -openness * 78; // 0 = horizontal, -78 = up
  return (
    <g>
      {/* base/post */}
      <rect x="248" y="540" width="22" height="180" fill="#1a2a4a" />
      <rect x="244" y="528" width="30" height="14" fill="#2E75B6" />
      {/* pivot */}
      <circle cx="259" cy="540" r="6" fill="#0a1428" stroke="#7ab8ff" strokeWidth="1" />
      {/* arm */}
      <g transform={`translate(259, 540) rotate(${angle})`}>
        <rect x="0" y="-6" width="220" height="12" fill="#f6f8ff" />
        {[20, 60, 100, 140, 180].map((px) => (
          <rect key={px} x={px} y="-6" width="20" height="12" fill="#E74C3C" />
        ))}
        <rect x="216" y="-8" width="6" height="16" fill="#0a1428" />
      </g>
      {/* small light on post */}
      <circle cx="259" cy="525" r="4" fill={openness > 0.1 ? '#5fd29a' : '#E74C3C'}>
        <animate attributeName="opacity" values="1;0.4;1" dur="1.2s" repeatCount="indefinite" />
      </circle>
    </g>
  );
}

/* ─────────────────── Camera on pole ─────────────────── */
function CameraPole({ progress = 0, beamOn = false }) {
  // progress 0..1 — 0 nothing, 0.4 pole appears, 0.7 camera appears, 1 fully connected
  const poleH = lerp(0, 360, smooth(clamp(progress / 0.5, 0, 1)));
  const camOpacity = clamp((progress - 0.45) / 0.25, 0, 1);
  const ringOpacity = clamp((progress - 0.7) / 0.3, 0, 1);

  return (
    <g>
      {/* base */}
      {progress > 0 && (
        <g>
          <rect x="320" y="720" width="40" height="6" fill="#1a2a4a" />
          <rect x="324" y="710" width="32" height="10" fill="#2a3a5a" />
        </g>
      )}
      {/* pole */}
      <rect x="335" y={720 - poleH} width="10" height={poleH} fill="#2a3a5a" />
      {/* horizontal arm */}
      {camOpacity > 0 && (
        <g opacity={camOpacity}>
          <rect x="280" y={720 - poleH - 4} width="60" height="8" fill="#2a3a5a" />
        </g>
      )}
      {/* camera head */}
      {camOpacity > 0 && (
        <g transform={`translate(265, ${720 - poleH - 16})`} opacity={camOpacity}>
          <rect x="-30" y="-18" width="60" height="36" rx="6" fill="#0a1428" stroke="#2E75B6" strokeWidth="1.5" />
          <circle cx="-8" cy="0" r="10" fill="#050a17" stroke="#7ab8ff" strokeWidth="1" />
          <circle cx="-8" cy="0" r="6" fill="#0a1428" />
          <circle cx="-8" cy="0" r="3" fill="#7ab8ff" opacity="0.8">
            {beamOn && <animate attributeName="opacity" values="0.4;1;0.4" dur="1.8s" repeatCount="indefinite" />}
          </circle>
          <rect x="14" y="-10" width="14" height="6" fill="#1a2a4a" />
          {/* status LED */}
          <circle cx="22" cy="-12" r="2" fill={beamOn ? "#5fd29a" : "#E74C3C"}>
            <animate attributeName="opacity" values="1;0.3;1" dur="1.3s" repeatCount="indefinite" />
          </circle>
        </g>
      )}
      {/* scan ring emanating */}
      {beamOn && (
        <g transform={`translate(257, ${720 - poleH - 16})`} opacity={ringOpacity}>
          <circle r="20" fill="none" stroke="#7ab8ff" strokeWidth="1">
            <animate attributeName="r" values="20;90;20" dur="2.4s" repeatCount="indefinite" />
            <animate attributeName="opacity" values="0.8;0;0.8" dur="2.4s" repeatCount="indefinite" />
          </circle>
          <circle r="20" fill="none" stroke="#7ab8ff" strokeWidth="1">
            <animate attributeName="r" values="20;90;20" dur="2.4s" begin="0.8s" repeatCount="indefinite" />
            <animate attributeName="opacity" values="0.8;0;0.8" dur="2.4s" begin="0.8s" repeatCount="indefinite" />
          </circle>
        </g>
      )}
    </g>
  );
}

/* ─────────────────── Background skyline + grid ─────────────────── */
function SceneBackground() {
  return (
    <g>
      <defs>
        <linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0" stopColor="#040814" />
          <stop offset="0.7" stopColor="#0a1530" />
          <stop offset="1" stopColor="#0d1a36" />
        </linearGradient>
        <linearGradient id="ground" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0" stopColor="#0a1428" />
          <stop offset="1" stopColor="#050a17" />
        </linearGradient>
        <pattern id="dot-grid" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse">
          <circle cx="2" cy="2" r="1" fill="#2E75B6" opacity="0.18" />
        </pattern>
      </defs>

      {/* sky */}
      <rect x="0" y="0" width="1600" height="720" fill="url(#sky)" />

      {/* distant building silhouettes */}
      <g opacity="0.45" fill="#0d1a30">
        <rect x="800" y="420" width="120" height="300" />
        <rect x="930" y="460" width="80" height="260" />
        <rect x="1020" y="380" width="180" height="340" />
        <rect x="1210" y="440" width="100" height="280" />
        <rect x="1320" y="400" width="160" height="320" />
        <rect x="1490" y="460" width="110" height="260" />
      </g>
      {/* tiny windows on buildings */}
      <g fill="#7ab8ff" opacity="0.5">
        {Array.from({ length: 60 }).map((_, i) => {
          const x = 810 + (i * 13) % 780;
          const y = 440 + Math.floor(i / 20) * 30 + ((i * 7) % 3) * 10;
          return <rect key={i} x={x} y={y} width="3" height="3" />;
        })}
      </g>

      {/* horizon glow */}
      <ellipse cx="800" cy="720" rx="900" ry="80" fill="#2E75B6" opacity="0.12" />

      {/* ground */}
      <rect x="0" y="720" width="1600" height="180" fill="url(#ground)" />
      <rect x="0" y="720" width="1600" height="180" fill="url(#dot-grid)" />
      {/* ground edge line */}
      <rect x="0" y="720" width="1600" height="1" fill="#2E75B6" opacity="0.5" />

      {/* condo boundary wall on far left */}
      <g>
        <rect x="0" y="380" width="240" height="340" fill="#0c1830" stroke="#1a2a4a" strokeWidth="1" />
        {/* wall texture - horizontal bricks */}
        {Array.from({ length: 8 }).map((_, i) => (
          <line key={i} x1="0" y1={400 + i * 40} x2="240" y2={400 + i * 40} stroke="#1a2a4a" strokeWidth="1" opacity="0.6" />
        ))}
        {/* small windows */}
        <rect x="40" y="420" width="40" height="60" fill="#1a2e52" stroke="#2E75B6" strokeWidth="1" />
        <rect x="40" y="420" width="40" height="60" fill="#7ab8ff" opacity="0.12" />
        <rect x="100" y="420" width="40" height="60" fill="#1a2e52" stroke="#2E75B6" strokeWidth="1" />
        <rect x="100" y="420" width="40" height="60" fill="#7ab8ff" opacity="0.18" />
      </g>

      {/* driveway markings */}
      <g opacity="0.3">
        {Array.from({ length: 12 }).map((_, i) => (
          <rect key={i} x={400 + i * 100} y="800" width="50" height="3" fill="#7ab8ff" />
        ))}
      </g>
    </g>
  );
}

/* ─────────────────── Mini Luna for the scene ─────────────────── */
function SceneLuna({ x, y, size = 140, state = 'happy', floating = true }) {
  return (
    <g transform={`translate(${x}, ${y})`}>
      {floating && (
        <ellipse cx={size * 0.5} cy={size * 1.12} rx={size * 0.32} ry={size * 0.04} fill="#000" opacity="0.45">
          <animate attributeName="rx" values={`${size * 0.32};${size * 0.26};${size * 0.32}`} dur="3s" repeatCount="indefinite" />
        </ellipse>
      )}
      <g>
        {floating && (
          <animateTransform attributeName="transform" type="translate" values="0,0; 0,-12; 0,0" dur="3s" repeatCount="indefinite" />
        )}
        <svg width={size} height={size * 1.08} viewBox="0 0 240 260">
          {/* simplified Luna inline */}
          <defs>
            <linearGradient id={`luna-scene-body-${x}-${y}`} x1="0" y1="0" x2="0" y2="1">
              <stop offset="0" stopColor="#5A7AA0" />
              <stop offset="0.5" stopColor="#3D5F88" />
              <stop offset="1" stopColor="#1E3A5F" />
            </linearGradient>
          </defs>
          {/* ears */}
          <path d="M48 60 L48 28 Q48 18 58 18 L88 18 Q98 18 98 28 L98 70 Z" fill={`url(#luna-scene-body-${x}-${y})`} stroke="#0b1428" strokeWidth="5" strokeLinejoin="round" />
          <path d="M142 70 L142 28 Q142 18 152 18 L182 18 Q192 18 192 28 L192 60 Z" fill={`url(#luna-scene-body-${x}-${y})`} stroke="#0b1428" strokeWidth="5" strokeLinejoin="round" />
          {/* head */}
          <path d="M30 80 Q30 60 50 60 L190 60 Q210 60 210 80 L210 200 Q210 222 188 222 L52 222 Q30 222 30 200 Z" fill={`url(#luna-scene-body-${x}-${y})`} stroke="#0b1428" strokeWidth="6" strokeLinejoin="round" />
          {/* eyes */}
          <rect x="55" y="115" rx="10" ry="10" width="58" height="58" fill="#FFFFFF" stroke="#0b1428" strokeWidth="4" />
          <rect x="70" y="130" rx="4" ry="4" width="24" height="24" fill="#0b1428" />
          <rect x="127" y="115" rx="10" ry="10" width="58" height="58" fill="#FFFFFF" stroke="#0b1428" strokeWidth="4" />
          <rect x="146" y="130" rx="4" ry="4" width="24" height="24" fill="#0b1428" />
          {/* happy mouth */}
          {state === 'happy' && <path d="M105 188 Q120 200 135 188" stroke="#0b1428" strokeWidth="4" fill="none" strokeLinecap="round" />}
          {state === 'idle' && <path d="M108 188 L132 188" stroke="#0b1428" strokeWidth="3" strokeLinecap="round" />}
        </svg>
      </g>
    </g>
  );
}

/* ─────────────────── Connection beam: camera ↔ booth ─────────────────── */
function ConnectionBeam({ strength = 0, poleTop = 340 }) {
  // beam from camera (~257, poleTop) curving to top of booth (~120, 488)
  if (strength <= 0) return null;
  const camX = 257, camY = poleTop;
  const bX = 120, bY = 490;
  const ctrlX = (camX + bX) / 2, ctrlY = Math.min(camY, bY) - 60;
  const d = `M${camX} ${camY} Q${ctrlX} ${ctrlY} ${bX} ${bY}`;
  return (
    <g opacity={strength}>
      <path d={d} stroke="#7ab8ff" strokeWidth="2" fill="none" strokeDasharray="6 6" opacity="0.6">
        <animate attributeName="stroke-dashoffset" from="0" to="-24" dur="1s" repeatCount="indefinite" />
      </path>
      <path d={d} stroke="#5fd29a" strokeWidth="1" fill="none" opacity="0.5" />
      {/* small data packets */}
      <circle r="4" fill="#7ab8ff">
        <animateMotion dur="2s" repeatCount="indefinite" path={d} />
      </circle>
      <circle r="3" fill="#5fd29a">
        <animateMotion dur="2s" begin="0.7s" repeatCount="indefinite" path={d} />
      </circle>
    </g>
  );
}

/* ─────────────────── Main scene ─────────────────── */

function PortariaScene({ progress = 0 }) {
  const p = progress;

  // ─── Infinite loop timer for solved-phase cars ───
  // One full car cycle = 0.20 progress units × 26 000 ms = 5 200 ms.
  // We start the RAF timer the first time p crosses 0.78 and keep it running
  // forever, so the cars never stop even when the timeline pauses at 1.0.
  const CYCLE_MS = 5200;
  const [loopT, setLoopT] = useState(0);
  const timerRef = useRef({ started: false, startAt: 0 });
  const inSolved = p >= 0.78;

  useEffect(() => {
    if (!inSolved) {
      timerRef.current.started = false;
      return;
    }
    if (!timerRef.current.started) {
      timerRef.current.started = true;
      const initialT = ((p - 0.78) * 5) % 1;
      timerRef.current.startAt = performance.now() - initialT * CYCLE_MS;
    }
    let active = true;
    let rafId;
    const tick = (now) => {
      if (!active) return;
      setLoopT(((now - timerRef.current.startAt) % CYCLE_MS) / CYCLE_MS);
      rafId = requestAnimationFrame(tick);
    };
    rafId = requestAnimationFrame(tick);
    return () => { active = false; cancelAnimationFrame(rafId); };
  }, [inSolved]); // eslint-disable-line react-hooks/exhaustive-deps

  // ─── Doorman state by phase ───
  let doormanState = 'calm';
  if (p > 0.18 && p < 0.45) doormanState = 'stressed';
  else if (p > 0.50 && p < 0.72) doormanState = 'calm';
  else if (p >= 0.72) doormanState = 'focused';
  if (p > 0.95) doormanState = 'happy';

  // ─── Gate openness ───
  // closed by default; in solved phase, the gate opens for each passing car
  let gateOpen = 0;
  // In P7, oscillate based on the lead car's position
  // We'll compute this inline below based on solved cars

  // ─── Cars: define a set with spawn + queue position ───
  // Chaos phase queue (cars come in but don't pass)
  const chaosTargets = [320, 540, 760, 980, 1200]; // x of car LEFT edge when stopped
  const chaosCars = chaosTargets.map((target, i) => {
    const spawn = 0.10 + i * 0.045;
    const stopBy = spawn + 0.07;
    const t = smooth(rng(p, spawn, stopBy));
    let x = lerp(1700, target, t);
    // fade-out in P3
    let opacity = 1;
    if (p > 0.42) {
      const t2 = rng(p, 0.42, 0.50);
      x = lerp(x, x + 60, smooth(t2));
      opacity = 1 - smooth(t2);
    }
    if (p > 0.50) opacity = 0;
    return { x, y: 600, color: ['#cf3c3c', '#d9c265', '#8da7c4', '#5b6b85', '#a06f4a'][i], opacity };
  });

  // Solved phase — a slow loop of 3 cars passing through
  // Use the portion of progress in [0.80, 1.0] to drive a continuous loop
  const solvedActive = clamp((p - 0.78) / 0.22, 0, 1);
  // loopT now comes from the internal RAF timer (see top of component) so it
  // keeps cycling even after progress freezes at 1.0.
  // Three cars staggered by 1/3 of loop
  const solvedCarDefs = [
    { color: '#2E75B6', plate: 'BRA·2A18', decision: 'AUTORIZADO', offset: 0.0 },
    { color: '#d9c265', plate: 'JLU·5C92', decision: 'AUTORIZADO', offset: 0.33 },
    { color: '#7c4dff', plate: 'XKR·9F31', decision: 'VISITANTE', offset: 0.66 },
  ];

  // Solved cars: each follows the same loop but offset. They appear from x=1700, slow at the gate (~x=320) for scan, then accelerate through to x=-300.
  const solvedCars = solvedCarDefs.map((def, i) => {
    let lt = (loopT + def.offset) % 1;
    // 0.00–0.55: drive in from 1700 to 320 (easeOut)
    // 0.55–0.70: scan dwell at 320
    // 0.70–1.00: drive through to -300 (easeIn)
    let x;
    let scanning = false;
    let showDecision = null;
    if (lt < 0.55) {
      const t = lt / 0.55;
      x = lerp(1700, 320, easeOut(t));
    } else if (lt < 0.70) {
      x = 320;
      scanning = true;
      showDecision = def.decision;
    } else {
      const t = (lt - 0.70) / 0.30;
      x = lerp(320, -300, easeIn(t));
      scanning = lt < 0.78; // keep brackets briefly as it leaves
      showDecision = lt < 0.78 ? def.decision : null;
    }
    return {
      x, y: 600, color: def.color,
      opacity: solvedActive * (x > -250 && x < 1700 ? 1 : 0),
      scanned: scanning,
      plate: def.plate,
      decision: showDecision,
    };
  });

  // Compute gate openness based on whether any solved car is in the gate zone
  if (p > 0.78) {
    // open gate when a car is near 320 or passing through
    let maxOpen = 0;
    solvedCars.forEach((c) => {
      // open when car between x=400 and x=-100
      if (c.x < 450 && c.x > -250 && c.opacity > 0) {
        // scale: smooth opening as car approaches scan, full open while passing
        let openness = 0;
        if (c.x > 320) openness = 1 - (c.x - 320) / 130; // approaching
        else if (c.x > -100) openness = 1; // passing
        else openness = 1 + (c.x + 100) / 150; // closing after
        openness = clamp(openness, 0, 1);
        if (openness > maxOpen) maxOpen = openness;
      }
    });
    gateOpen = maxOpen;
  }

  // ─── Luna in scene ───
  // P4 (0.50-0.60): Luna flies in from top to position above pole
  // P5 (0.60-0.72): Luna stays near pole, "installs" the camera
  // P6 (0.72-0.80): Luna drifts to upper-right (overseeing)
  let lunaX = null, lunaY = null, lunaSize = 0, lunaState = 'happy';
  if (p > 0.48 && p < 0.85) {
    const enterT = smooth(rng(p, 0.48, 0.60));
    // P4 enter: from above
    const startX = 800, startY = -200;
    const installX = 250, installY = 240;
    const overseeX = 1280, overseeY = 80;
    lunaSize = lerp(80, 160, enterT);

    if (p < 0.72) {
      lunaX = lerp(startX, installX, enterT);
      lunaY = lerp(startY, installY, enterT);
      lunaState = p < 0.62 ? 'idle' : 'happy';
    } else {
      const moveT = smooth(rng(p, 0.72, 0.82));
      lunaX = lerp(installX, overseeX, moveT);
      lunaY = lerp(installY, overseeY, moveT);
      lunaSize = lerp(160, 100, moveT);
      lunaState = 'happy';
    }
    // exit fade (after 0.85)
  }
  if (p >= 0.85) {
    // Luna stays in upper right but smaller
    lunaX = 1280;
    lunaY = 80;
    lunaSize = 100;
    lunaState = 'happy';
  }

  // ─── Camera install progress: P5 ───
  // The pole and camera grow during P5 (0.55..0.72) — but actually we want them to appear AS Luna installs
  const cameraProgress = clamp((p - 0.58) / 0.18, 0, 1);
  const beamOn = p > 0.72;
  const beamStrength = clamp((p - 0.72) / 0.08, 0, 1);
  const poleTop = 720 - lerp(0, 360, smooth(cameraProgress));

  // ─── Scene camera transform (subtle zoom/pan) ───
  // Slight zoom-in during chaos (peak 0.42), pull back during install (0.65), zoom in for solved (0.9)
  let zoom = 1, panY = 0;
  if (p < 0.42) {
    zoom = lerp(1.0, 1.08, smooth(rng(p, 0.10, 0.42)));
  } else if (p < 0.60) {
    zoom = lerp(1.08, 1.0, smooth(rng(p, 0.42, 0.60)));
  } else if (p < 0.78) {
    zoom = lerp(1.0, 0.96, smooth(rng(p, 0.60, 0.78)));
    panY = lerp(0, -20, smooth(rng(p, 0.60, 0.78)));
  } else {
    zoom = lerp(0.96, 1.04, smooth(rng(p, 0.78, 1.0)));
    panY = lerp(-20, 0, smooth(rng(p, 0.78, 1.0)));
  }

  const sceneTransform = `translate(0, ${panY}) scale(${zoom})`;

  // ─── Chaos "alert" overlay (speed lines + red vignette) ───
  const chaosIntensity = clamp((p - 0.20) / 0.18, 0, 1) * (1 - clamp((p - 0.42) / 0.08, 0, 1));

  return (
    <svg className="stage-svg" viewBox="0 0 1600 900" preserveAspectRatio="xMidYMid slice">
      <g transform={sceneTransform} style={{ transformOrigin: '800px 600px' }}>
        <SceneBackground />

        {/* chaos cars (only opacity > 0) */}
        {chaosCars.map((c, i) => c.opacity > 0.01 && (
          <Car key={`chaos-${i}`} x={c.x} y={c.y} color={c.color} opacity={c.opacity} />
        ))}

        {/* solved cars */}
        {solvedCars.map((c, i) => c.opacity > 0.01 && (
          <Car key={`solved-${i}`} x={c.x} y={c.y} color={c.color} opacity={c.opacity}
               scanned={c.scanned} plate={c.plate} decision={c.decision} />
        ))}

        {/* cancela (gate) */}
        <Cancela openness={gateOpen} />

        {/* camera pole */}
        <CameraPole progress={cameraProgress} beamOn={beamOn} />

        {/* booth */}
        <GuardBooth doormanState={doormanState} linked={beamOn} />

        {/* connection beam between camera and booth */}
        <ConnectionBeam strength={beamStrength} poleTop={poleTop + 20} />

        {/* Luna in scene */}
        {lunaX !== null && lunaSize > 0 && (
          <SceneLuna x={lunaX} y={lunaY} size={lunaSize} state={lunaState} />
        )}

        {/* Luna installing — small "tool" beam between Luna and the pole top during P5 */}
        {p > 0.60 && p < 0.72 && lunaX !== null && (
          <g opacity={clamp((p - 0.60) / 0.05, 0, 1) * (1 - clamp((p - 0.68) / 0.04, 0, 1))}>
            <path d={`M${lunaX + lunaSize * 0.5} ${lunaY + lunaSize * 0.8} L 340 ${poleTop}`} stroke="#7ab8ff" strokeWidth="2" strokeDasharray="3 4" />
            <circle cx="340" cy={poleTop} r="14" fill="none" stroke="#7ab8ff" strokeWidth="1.5">
              <animate attributeName="r" values="6;18;6" dur="0.9s" repeatCount="indefinite" />
              <animate attributeName="opacity" values="1;0;1" dur="0.9s" repeatCount="indefinite" />
            </circle>
          </g>
        )}

        {/* chaos overlay: motion lines and red vignette */}
        {chaosIntensity > 0.05 && (
          <g opacity={chaosIntensity}>
            {/* motion lines on cars */}
            {chaosCars.map((c, i) => c.opacity > 0.5 && (
              <g key={`ml-${i}`} opacity="0.4">
                <line x1={c.x + 195} y1={620} x2={c.x + 250} y2={620} stroke="#fff" strokeWidth="1" />
                <line x1={c.x + 195} y1={640} x2={c.x + 240} y2={640} stroke="#fff" strokeWidth="1" />
              </g>
            ))}
            {/* red vignette */}
            <radialGradient id="alert-vignette">
              <stop offset="0.6" stopColor="#E74C3C" stopOpacity="0" />
              <stop offset="1" stopColor="#E74C3C" stopOpacity="0.25" />
            </radialGradient>
            <rect x="0" y="0" width="1600" height="900" fill="url(#alert-vignette)" />
            {/* honk badges */}
            {chaosIntensity > 0.6 && (
              <g fontFamily="Space Grotesk, sans-serif" fontWeight="700" fill="#E74C3C">
                <text x={chaosCars[1].x + 80} y={chaosCars[1].y - 14} fontSize="22">!</text>
                <text x={chaosCars[2].x + 80} y={chaosCars[2].y - 18} fontSize="26">!?</text>
                <text x={chaosCars[3].x + 80} y={chaosCars[3].y - 14} fontSize="22">!!</text>
              </g>
            )}
          </g>
        )}

        {/* solved overlay: success glow */}
        {solvedActive > 0.2 && (
          <g opacity={solvedActive * 0.6}>
            <radialGradient id="solved-glow">
              <stop offset="0" stopColor="#5fd29a" stopOpacity="0.18" />
              <stop offset="1" stopColor="#5fd29a" stopOpacity="0" />
            </radialGradient>
            <ellipse cx="260" cy="600" rx="500" ry="260" fill="url(#solved-glow)" />
          </g>
        )}
      </g>

      {/* cinematic letterboxing (fades in during chaos peak + solved) */}
      <rect x="0" y="0" width="1600" height={lerp(0, 50, smooth(rng(p, 0.0, 0.10)))} fill="#000" />
      <rect x="0" y={900 - lerp(0, 50, smooth(rng(p, 0.0, 0.10)))} width="1600" height={lerp(0, 50, smooth(rng(p, 0.0, 0.10)))} fill="#000" />
    </svg>
  );
}

window.PortariaScene = PortariaScene;
