// scenes-v2-b.jsx — Scenes 5–8 of the CAMS animation
// 5. Resolución 40 km · 6. Downscaling · 7. Pronóstico 72h · 8. Outro

// ─── Scene 5: Resolución 40 km sobre el Valle ──────────────────────

function S5Resolution() {
  const { localTime, duration } = useSprite();
  const time = useTime();

  const tKick = Easing.easeOutCubic(clamp((localTime - 0.2) / 0.5, 0, 1));
  const tHead = Easing.easeOutCubic(clamp((localTime - 0.5) / 0.6, 0, 1));
  const tMap = Easing.easeOutCubic(clamp((localTime - 1.0) / 0.7, 0, 1));
  const tGrid = Easing.easeOutCubic(clamp((localTime - 1.6) / 0.8, 0, 1));
  const tHL = Easing.easeOutCubic(clamp((localTime - 3.0) / 0.6, 0, 1));
  const tFact = Easing.easeOutCubic(clamp((localTime - 4.2) / 0.6, 0, 1));
  const fadeOut = clamp((duration - localTime) / 0.45, 0, 1);

  // Stylised Colombia silhouette (very rough) — frame on right side
  const cx = 880, cy = 380;
  const colW = 360, colH = 480;

  // CAMS 40km grid in screen space — visualised as ~50px cells
  const cellPx = 56;
  const gridCols = Math.ceil(colW / cellPx) + 1;
  const gridRows = Math.ceil(colH / cellPx) + 1;
  const gridX = cx - colW / 2;
  const gridY = cy - colH / 2;

  // Valle de Aburrá lens-shape centered on one cell
  const valleX = cx - 20;
  const valleY = cy + 50;

  return (
    <SceneFade>
      <GridBg cell={48} color="rgba(255,255,255,0.018)" />

      {/* Left text */}
      <div style={{ position: 'absolute', left: 80, top: 140, width: 480, opacity: fadeOut }}>
        <div style={{
          fontFamily: MONO, fontSize: 11, fontWeight: 600,
          letterSpacing: '0.26em', textTransform: 'uppercase', color: C.cyan,
          opacity: tKick, transform: 'translateY(' + ((1 - tKick) * 8) + 'px)',
        }}>Capítulo IV · El reto de la escala</div>

        <div style={{
          marginTop: 18,
          fontFamily: FONT, fontSize: 42, fontWeight: 700,
          color: C.white, lineHeight: 1.1, letterSpacing: '-0.015em',
          opacity: tHead, transform: 'translateY(' + ((1 - tHead) * 14) + 'px)',
        }}>
          CAMS describe<br />
          el planeta<br />
          en celdas de <span style={{ color: C.orange }}>40&nbsp;km</span>.
        </div>

        <div style={{
          marginTop: 28,
          fontFamily: FONT, fontSize: 16, fontWeight: 400,
          color: C.gray, lineHeight: 1.6,
          opacity: tFact,
          textWrap: 'pretty',
        }}>
          Cada celda promedia <span style={{ color: C.white }}>~1&nbsp;600&nbsp;km²</span> de
          atmósfera. El Valle de Aburrá, con sus
          <span style={{ color: C.white }}> ~60 km</span> de norte a sur y montañas
          de <span style={{ color: C.white }}> 1&nbsp;800 m</span> de altura,
          cabe <span style={{ color: C.cyan, fontWeight: 700 }}>casi entero
          en una sola celda</span>.
        </div>

        {/* Big stat row */}
        <div style={{ marginTop: 32, display: 'flex', gap: 32, opacity: tFact }}>
          <div>
            <div style={{ fontFamily: FONT, fontSize: 42, fontWeight: 800, color: C.orange, lineHeight: 1 }}>1×</div>
            <div style={{ fontFamily: MONO, fontSize: 10, color: C.gray, marginTop: 5, letterSpacing: '0.12em', textTransform: 'uppercase' }}>celda CAMS</div>
          </div>
          <div style={{ fontFamily: FONT, fontSize: 30, color: C.dim, lineHeight: 1.2, alignSelf: 'center' }}>≈</div>
          <div>
            <div style={{ fontFamily: FONT, fontSize: 42, fontWeight: 800, color: C.cyan, lineHeight: 1 }}>10 mun.</div>
            <div style={{ fontFamily: MONO, fontSize: 10, color: C.gray, marginTop: 5, letterSpacing: '0.12em', textTransform: 'uppercase' }}>todo el Valle</div>
          </div>
        </div>
      </div>

      {/* Right: map + grid */}
      <div style={{ position: 'absolute', left: cx, top: cy, opacity: tMap * fadeOut }}>
        <svg width={colW + 80} height={colH + 80} style={{
          position: 'absolute', left: -(colW + 80) / 2, top: -(colH + 80) / 2,
        }}>
          <defs>
            <clipPath id="colClip">
              <path d="M 200 60 Q 250 80 270 130 Q 290 170 280 220 Q 320 240 340 290 Q 360 360 320 420 Q 280 480 240 520 Q 200 540 170 510 Q 140 470 145 410 Q 130 360 100 320 Q 80 280 100 230 Q 130 190 145 145 Q 165 100 200 60 Z" />
            </clipPath>
          </defs>

          {/* Colombia silhouette */}
          <path d="M 200 60 Q 250 80 270 130 Q 290 170 280 220 Q 320 240 340 290 Q 360 360 320 420 Q 280 480 240 520 Q 200 540 170 510 Q 140 470 145 410 Q 130 360 100 320 Q 80 280 100 230 Q 130 190 145 145 Q 165 100 200 60 Z"
            fill="rgba(255,255,255,0.04)" stroke="rgba(255,255,255,0.25)" strokeWidth={1.2} />

          {/* CAMS 40km grid — clipped to country */}
          <g clipPath="url(#colClip)" opacity={tGrid}>
            {Array.from({ length: gridRows }, (_, r) => (
              <line key={'gr' + r} x1={0} y1={r * cellPx + 30}
                x2={colW + 80} y2={r * cellPx + 30}
                stroke={C.orange} strokeOpacity={0.35} strokeWidth={0.8} />
            ))}
            {Array.from({ length: gridCols }, (_, c) => (
              <line key={'gc' + c} x1={c * cellPx + 30} y1={0}
                x2={c * cellPx + 30} y2={colH + 80}
                stroke={C.orange} strokeOpacity={0.35} strokeWidth={0.8} />
            ))}
            {/* Pollution heat — random colored cell fills */}
            {(() => {
              const cells = [];
              const r = rand(11);
              for (let i = 0; i < gridCols; i++) {
                for (let j = 0; j < gridRows; j++) {
                  const v = r();
                  const a = v * 0.22;
                  cells.push(
                    <rect key={'cl' + i + '_' + j}
                      x={i * cellPx + 30} y={j * cellPx + 30}
                      width={cellPx} height={cellPx}
                      fill={v > 0.8 ? C.orange : (v > 0.5 ? C.amber : C.sky)}
                      fillOpacity={a * tGrid} />
                  );
                }
              }
              return cells;
            })()}
          </g>

          {/* Country border on top */}
          <path d="M 200 60 Q 250 80 270 130 Q 290 170 280 220 Q 320 240 340 290 Q 360 360 320 420 Q 280 480 240 520 Q 200 540 170 510 Q 140 470 145 410 Q 130 360 100 320 Q 80 280 100 230 Q 130 190 145 145 Q 165 100 200 60 Z"
            fill="none" stroke={C.cyan} strokeOpacity={0.5} strokeWidth={1.2} />
        </svg>

        {/* Highlighted cell over Valle de Aburrá */}
        <div style={{
          position: 'absolute', left: 5 - cellPx / 2, top: 30 - cellPx / 2,
          width: cellPx, height: cellPx,
          border: '2px solid ' + C.white,
          background: 'rgba(255,255,255,0.06)',
          boxShadow: '0 0 24px ' + C.white + '40',
          opacity: tHL,
          transform: 'scale(' + (0.7 + 0.3 * tHL) + ')',
        }} />

        {/* Valle de Aburrá — small region marker */}
        <div style={{
          position: 'absolute', left: -2, top: 30,
          width: 8, height: 22,
          borderRadius: 4,
          background: C.cyan,
          boxShadow: '0 0 14px ' + C.cyan + 'b0',
          opacity: tHL,
        }} />
        <div style={{
          position: 'absolute', left: 14, top: 26,
          fontFamily: FONT, fontSize: 12, fontWeight: 700, color: C.white,
          whiteSpace: 'nowrap', opacity: tHL,
        }}>
          Valle de Aburrá
          <div style={{ fontFamily: MONO, fontSize: 9.5, color: C.gray, marginTop: 3, letterSpacing: '0.08em' }}>≈60 × 10 km · 10 municipios</div>
        </div>

        {/* Annotation lines */}
        <svg width={300} height={120} style={{ position: 'absolute', left: -150, top: 80, opacity: tHL }}>
          <line x1={150} y1={0} x2={150} y2={50} stroke={C.orange} strokeWidth={1} strokeDasharray="3 3" />
          <line x1={150} y1={50} x2={260} y2={50} stroke={C.orange} strokeWidth={1} strokeDasharray="3 3" />
          <text x={155} y={28} fill={C.orange} fontFamily={MONO} fontSize={10} letterSpacing="0.08em">40 km</text>
        </svg>
      </div>
    </SceneFade>
  );
}

// ─── Scene 6: Ensamble local ────────────────────────────────────

function S6Downscale() {
  const { localTime, duration } = useSprite();
  const time = useTime();

  const tKick = Easing.easeOutCubic(clamp((localTime - 0.2) / 0.5, 0, 1));
  const tHead = Easing.easeOutCubic(clamp((localTime - 0.6) / 0.6, 0, 1));
  const tCams = Easing.easeOutCubic(clamp((localTime - 1.2) / 0.6, 0, 1));
  const tWrf  = Easing.easeOutCubic(clamp((localTime - 1.8) / 0.6, 0, 1));
  const tStn  = Easing.easeOutCubic(clamp((localTime - 2.4) / 0.6, 0, 1));
  const tArr  = Easing.easeOutCubic(clamp((localTime - 3.2) / 0.7, 0, 1));
  const tOut  = Easing.easeOutCubic(clamp((localTime - 4.2) / 0.7, 0, 1));
  const tCap  = Easing.easeOutCubic(clamp((localTime - 5.4) / 0.6, 0, 1));
  const fadeOut = clamp((duration - localTime) / 0.45, 0, 1);

  // Layout: 3 input panels left, ML pipe middle, output right
  const inputs = [
    {
      key: 'cams', label: 'CAMS · 40 km', sub: 'background químico',
      color: C.orange, t: tCams, y: 170,
      // Coarse 3×3 grid
      cells: 3,
    },
    {
      key: 'wrf', label: 'WRF · 2 km', sub: 'meteorología regional',
      color: C.cyan, t: tWrf, y: 320,
      cells: 9,
    },
    {
      key: 'stn', label: '20 estaciones SIATA', sub: 'PM2.5 in situ',
      color: C.amber, t: tStn, y: 470,
      cells: 0,
    },
  ];

  // Output high-res field (15x15 cells)
  const outCells = 15;

  return (
    <SceneFade>
      <GridBg cell={48} color="rgba(255,255,255,0.018)" />

      {/* Header */}
      <div style={{
        position: 'absolute', left: '50%', top: 60,
        transform: 'translateX(-50%)', textAlign: 'center',
        opacity: fadeOut,
      }}>
        <div style={{
          fontFamily: MONO, fontSize: 11, fontWeight: 600,
          letterSpacing: '0.26em', textTransform: 'uppercase', color: C.cyan,
          opacity: tKick, transform: 'translateY(' + ((1 - tKick) * 6) + 'px)',
        }}>Capítulo VI · Ensamble local</div>
        <div style={{
          marginTop: 14,
          fontFamily: FONT, fontSize: 32, fontWeight: 700,
          color: C.white, letterSpacing: '-0.015em',
          opacity: tHead, transform: 'translateY(' + ((1 - tHead) * 12) + 'px)',
        }}>
          Del modelo global a <span style={{ color: C.cyan }}>cada estación</span> SIATA
        </div>
      </div>

      {/* Input panels */}
      {inputs.map((inp, i) => (
        <div key={inp.key} style={{
          position: 'absolute', left: 100, top: inp.y,
          width: 280, height: 110,
          opacity: inp.t * fadeOut,
          transform: 'translateX(' + ((1 - inp.t) * -20) + 'px)',
        }}>
          {/* Card */}
          <div style={{
            position: 'absolute', inset: 0,
            border: '1px solid ' + hexWithAlpha(inp.color, 0.35),
            borderRadius: 8,
            background: 'rgba(5,10,30,0.5)',
          }} />
          {/* Label */}
          <div style={{
            position: 'absolute', left: 16, top: 14,
          }}>
            <div style={{ fontFamily: FONT, fontSize: 14, fontWeight: 700, color: inp.color }}>{inp.label}</div>
            <div style={{ fontFamily: MONO, fontSize: 10, color: C.gray, letterSpacing: '0.08em', marginTop: 3 }}>{inp.sub}</div>
          </div>
          {/* Visual */}
          <div style={{
            position: 'absolute', right: 16, top: 14,
            width: 80, height: 80,
            display: 'grid',
            gridTemplateColumns: 'repeat(' + (inp.cells || 1) + ', 1fr)',
            gridTemplateRows: 'repeat(' + (inp.cells || 1) + ', 1fr)',
            gap: inp.cells > 0 ? 2 : 0,
          }}>
            {inp.cells > 0 ? (
              Array.from({ length: inp.cells * inp.cells }, (_, k) => {
                const v = (Math.sin(k * 1.7 + time * 0.5) + 1) / 2;
                return (
                  <div key={k} style={{
                    background: hexWithAlpha(inp.color, 0.15 + v * 0.45),
                  }} />
                );
              })
            ) : (
              // Station dots scattered
              <svg width={80} height={80} style={{ gridColumn: '1 / -1', gridRow: '1 / -1' }}>
                {Array.from({ length: 20 }, (_, k) => {
                  const r = rand(k + 9);
                  const x = 8 + r() * 64;
                  const y = 8 + r() * 64;
                  const pulse = 0.55 + Math.sin(time * 2 + k) * 0.35;
                  return <circle key={k} cx={x} cy={y} r={2.2}
                    fill={inp.color} opacity={pulse} />;
                })}
              </svg>
            )}
          </div>
        </div>
      ))}

      {/* Arrows + ML node */}
      <svg width="1280" height="720" style={{ position: 'absolute', inset: 0, opacity: tArr * fadeOut }}>
        {inputs.map((inp, i) => {
          const sy = inp.y + 55;
          const len = 1; // for dash anim
          return (
            <line key={inp.key} x1={380} y1={sy} x2={580} y2={360}
              stroke={inp.color} strokeWidth={1.4} strokeOpacity={0.55}
              strokeDasharray="4 4" />
          );
        })}
        {/* ML out arrow */}
        <line x1={720} y1={360} x2={900} y2={360}
          stroke={C.green} strokeWidth={1.8} strokeOpacity={0.7} />
        <polygon points="900,355 910,360 900,365"
          fill={C.green} fillOpacity={0.85} />

        {/* Travelling dots into ML */}
        {inputs.map((inp, i) => {
          const ph = ((time * 0.5 + i * 0.33) % 1);
          const x = 380 + (580 - 380) * ph;
          const y = (inp.y + 55) + (360 - (inp.y + 55)) * ph;
          return (
            <circle key={'pp' + i} cx={x} cy={y} r={3}
              fill={inp.color} opacity={(1 - ph) * 0.95} />
          );
        })}
      </svg>

      {/* ML / ensemble node */}
      <div style={{
        position: 'absolute', left: 650, top: 360,
        transform: 'translate(-50%,-50%) scale(' + (Easing.easeOutBack(clamp((localTime - 3.0) / 0.6, 0, 1))) + ')',
        opacity: tArr * fadeOut,
      }}>
        <div style={{
          width: 130, height: 130, borderRadius: '50%',
          background: C.coral,
          display: 'flex', flexDirection: 'column',
          alignItems: 'center', justifyContent: 'center',
          boxShadow: '0 0 36px ' + C.coral + '50, 0 0 70px ' + C.coral + '20',
        }}>
          <div style={{
            fontFamily: FONT, fontSize: 12, fontWeight: 800,
            color: C.white, letterSpacing: '0.05em',
            textTransform: 'uppercase', textAlign: 'center',
          }}>Machine<br />Learning</div>
          <div style={{
            fontFamily: MONO, fontSize: 9, color: 'rgba(255,255,255,0.7)',
            marginTop: 6, letterSpacing: '0.12em',
          }}>ensamble</div>
        </div>
      </div>

      {/* Output: 20 stations × 72h forecast heatmap */}
      <div style={{
        position: 'absolute', left: 870, top: 200,
        opacity: tOut * fadeOut,
        transform: 'translateX(' + ((1 - tOut) * 20) + 'px)',
      }}>
        <div style={{
          fontFamily: MONO, fontSize: 10, color: C.cyan,
          letterSpacing: '0.2em', textTransform: 'uppercase',
          marginBottom: 12,
        }}>Pronóstico · 20 estaciones × 72 h</div>

        <div style={{
          position: 'relative',
          padding: 12,
          border: '1px solid ' + hexWithAlpha(C.green, 0.4),
          borderRadius: 8,
          background: 'rgba(5,10,30,0.5)',
        }}>
          {/* Hour-axis header */}
          <div style={{
            display: 'flex', justifyContent: 'space-between',
            marginBottom: 6, paddingLeft: 28,
            fontFamily: MONO, fontSize: 8.5, color: C.dim, letterSpacing: '0.08em',
          }}>
            <span>T+0</span><span>+24h</span><span>+48h</span><span>+72h</span>
          </div>

          {/* Matrix: 20 rows × 24 cols */}
          {Array.from({ length: 20 }, (_, row) => (
            <div key={row} style={{
              display: 'flex', alignItems: 'center', gap: 4,
              marginBottom: 1.5,
            }}>
              {/* Station label */}
              <div style={{
                width: 22,
                fontFamily: MONO, fontSize: 8, color: C.gray,
                letterSpacing: '0.06em', textAlign: 'right',
              }}>E{String(row + 1).padStart(2, '0')}</div>
              {/* Forecast cells */}
              <div style={{
                display: 'grid',
                gridTemplateColumns: 'repeat(24, 1fr)',
                gap: 1,
                width: 192,
              }}>
                {Array.from({ length: 24 }, (_, col) => {
                  // Per-station baseline (some stations consistently higher)
                  const stationLevel = 0.4 + (Math.sin(row * 1.3) + 1) / 2 * 0.5;
                  // Diurnal pattern + station phase
                  const phase = col / 24 * Math.PI * 6 + row * 0.18;
                  const diurnal = (Math.sin(phase - Math.PI / 2) + 1) / 2;
                  const drift = (Math.sin(col * 0.18 + row * 0.07) + 1) / 2 * 0.3;
                  const v = stationLevel * (0.4 + diurnal * 0.5) + drift;
                  const color = v > 0.75 ? C.coral
                             : v > 0.55 ? C.orange
                             : v > 0.35 ? C.amber
                             : C.green;
                  const ap = Easing.easeOutCubic(clamp((localTime - 4.2 - (row + col) * 0.02) / 0.4, 0, 1));
                  return (
                    <div key={col} style={{
                      height: 5,
                      background: hexWithAlpha(color, 0.35 + v * 0.55),
                      opacity: ap,
                    }} />
                  );
                })}
              </div>
            </div>
          ))}

          {/* Color legend strip */}
          <div style={{
            display: 'flex', alignItems: 'center', gap: 8,
            marginTop: 12, paddingLeft: 28,
          }}>
            <div style={{
              flex: 1, height: 6, borderRadius: 3,
              background: 'linear-gradient(90deg,' + C.green + ',' + C.amber + ',' + C.orange + ',' + C.coral + ')',
            }} />
          </div>
          <div style={{
            display: 'flex', justifyContent: 'space-between',
            marginTop: 4, paddingLeft: 28,
            fontFamily: MONO, fontSize: 8.5, color: C.dim, letterSpacing: '0.08em',
          }}>
            <span>0</span><span>µg/m³</span><span>50+</span>
          </div>
        </div>
      </div>

      {/* Bottom caption */}
      <div style={{
        position: 'absolute', left: 100, bottom: 70, right: 100,
        opacity: tCap * fadeOut, textAlign: 'center',
      }}>
        <div style={{
          fontFamily: FONT, fontSize: 16, color: C.gray, lineHeight: 1.5,
          maxWidth: 820, margin: '0 auto', textWrap: 'pretty',
        }}>
          Modelos de <span style={{ color: C.white }}>machine learning</span> aprenden a corregir el sesgo de CAMS
          <span style={{ color: C.white }}> en la coordenada exacta</span> de cada estación,
          usando WRF como contexto meteorológico y los <span style={{ color: C.white }}>datos históricos</span> de
          PM<span style={{ fontSize: 11 }}>2.5</span> como anclaje real.
        </div>
      </div>
    </SceneFade>
  );
}

// ─── Scene 7: Pronóstico 72h sobre el Valle ────────────────────────

function S7Forecast() {
  const { localTime, duration } = useSprite();
  const time = useTime();

  const tKick = Easing.easeOutCubic(clamp((localTime - 0.2) / 0.5, 0, 1));
  const tHead = Easing.easeOutCubic(clamp((localTime - 0.6) / 0.6, 0, 1));
  const tAxes = Easing.easeOutCubic(clamp((localTime - 1.0) / 0.5, 0, 1));
  const tBands = Easing.easeOutCubic(clamp((localTime - 1.3) / 0.7, 0, 1));
  const tCams = clamp((localTime - 1.8) / 1.8, 0, 1);
  const tCorr = clamp((localTime - 2.6) / 2.0, 0, 1);
  const tObs = Easing.easeOutCubic(clamp((localTime - 3.4) / 0.5, 0, 1));
  const tLegend = Easing.easeOutCubic(clamp((localTime - 4.8) / 0.5, 0, 1));
  const tCallout = Easing.easeOutCubic(clamp((localTime - 5.6) / 0.5, 0, 1));
  const fadeOut = clamp((duration - localTime) / 0.45, 0, 1);

  // Chart bounds
  const cx0 = 220, cy0 = 220;
  const cw = 880, ch = 320;

  // Generate forecast curves (deterministic)
  const hours = 73;
  const data = React.useMemo(() => {
    const arr = [];
    for (let h = 0; h < hours; h++) {
      // Diurnal cycle + slow drift
      const hour = h % 24;
      const diurnal = 18 + Math.sin((hour - 6) / 24 * Math.PI * 2) * 14;
      const drift = Math.sin(h / 24 * Math.PI * 0.8) * 6;
      const noise = Math.sin(h * 0.7) * 2 + Math.cos(h * 1.3) * 1.5;
      const obs = Math.max(2, diurnal + drift + noise);
      // Corrected: closely tracks obs (model output ≈ measured)
      const corr = Math.max(2.5, obs + Math.sin(h * 0.4) * 1.2);
      // CAMS: dampened amplitude + systematic underestimation.
      // Enforced to stay at least 5 µg/m³ below the corrected line at every hour.
      const camsRaw = diurnal * 0.32 + drift * 0.15 + 3.5;
      const cams = Math.max(0.5, Math.min(camsRaw, corr - 5));
      arr.push({ h, obs, cams, corr });
    }
    return arr;
  }, []);

  // Y axis 0..50
  const yMin = 0, yMax = 50;
  const xToPx = h => cx0 + (h / 72) * cw;
  const yToPx = v => cy0 + ch - (v - yMin) / (yMax - yMin) * ch;

  // ICA bands (Colombia): Buena 0-12, Aceptable 13-37, Dañina SG 38-55, etc.
  const bands = [
    { from: 0, to: 12, color: C.green, label: 'Buena' },
    { from: 12, to: 37, color: C.amber, label: 'Aceptable' },
    { from: 37, to: 50, color: C.orange, label: 'Dañina · GS' },
  ];

  // Build path strings (animated by current progress)
  const buildPath = (key, prog) => {
    const cap = Math.floor(prog * (hours - 1));
    if (cap <= 0) return '';
    let p = 'M ' + xToPx(0) + ' ' + yToPx(data[0][key]);
    for (let h = 1; h <= cap; h++) {
      p += ' L ' + xToPx(h) + ' ' + yToPx(data[h][key]);
    }
    return p;
  };

  return (
    <SceneFade>
      <GridBg cell={48} color="rgba(255,255,255,0.018)" />

      {/* Header */}
      <div style={{
        position: 'absolute', left: cx0, top: 88,
        opacity: fadeOut,
      }}>
        <div style={{
          fontFamily: MONO, fontSize: 11, fontWeight: 600,
          letterSpacing: '0.26em', textTransform: 'uppercase', color: C.cyan,
          opacity: tKick, transform: 'translateY(' + ((1 - tKick) * 6) + 'px)',
        }}>Capítulo VII · El resultado</div>
        <div style={{
          marginTop: 12,
          fontFamily: FONT, fontSize: 30, fontWeight: 700,
          color: C.white, letterSpacing: '-0.015em',
          opacity: tHead, transform: 'translateY(' + ((1 - tHead) * 10) + 'px)',
        }}>
          Pronóstico horario · PM<span style={{ fontSize: 18 }}>2.5</span> · 72&nbsp;h
        </div>
      </div>

      {/* Chart area */}
      <svg width="1280" height="720" style={{ position: 'absolute', inset: 0 }}>
        {/* Bands */}
        {bands.map((b, i) => (
          <rect key={i}
            x={cx0} y={yToPx(b.to)}
            width={cw} height={yToPx(b.from) - yToPx(b.to)}
            fill={b.color} fillOpacity={tBands * 0.06 * fadeOut} />
        ))}
        {/* Band labels */}
        {bands.map((b, i) => (
          <text key={'bl' + i}
            x={cx0 + cw + 6}
            y={(yToPx(b.from) + yToPx(b.to)) / 2 + 4}
            fill={b.color} fillOpacity={tBands * 0.7 * fadeOut}
            fontFamily={MONO} fontSize={10} letterSpacing="0.08em">
            {b.label}
          </text>
        ))}

        {/* Axes */}
        <line x1={cx0} y1={cy0} x2={cx0} y2={cy0 + ch}
          stroke="rgba(255,255,255,0.18)" strokeWidth={1} opacity={tAxes * fadeOut} />
        <line x1={cx0} y1={cy0 + ch} x2={cx0 + cw} y2={cy0 + ch}
          stroke="rgba(255,255,255,0.18)" strokeWidth={1} opacity={tAxes * fadeOut} />

        {/* X ticks every 12h */}
        {[0, 12, 24, 36, 48, 60, 72].map((h, i) => {
          const x = xToPx(h);
          const isMid = h % 24 === 0;
          return (
            <React.Fragment key={'xt' + i}>
              <line x1={x} y1={cy0 + ch} x2={x} y2={cy0 + ch + (isMid ? 8 : 4)}
                stroke="rgba(255,255,255,0.3)" strokeWidth={1} opacity={tAxes * fadeOut} />
              <text x={x} y={cy0 + ch + 26}
                fill={C.gray} fillOpacity={tAxes * fadeOut}
                fontFamily={MONO} fontSize={10} textAnchor="middle"
                fontWeight={isMid ? 700 : 400}>
                {h === 0 ? 'T+0' : ('T+' + h + 'h')}
              </text>
              {isMid && h > 0 && (
                <line x1={x} y1={cy0} x2={x} y2={cy0 + ch}
                  stroke="rgba(255,255,255,0.06)" strokeWidth={1} opacity={tAxes * fadeOut} />
              )}
            </React.Fragment>
          );
        })}

        {/* Y ticks */}
        {[0, 12, 25, 37, 50].map((v, i) => (
          <React.Fragment key={'yt' + i}>
            <text x={cx0 - 10} y={yToPx(v) + 4}
              fill={C.gray} fillOpacity={tAxes * fadeOut}
              fontFamily={MONO} fontSize={10} textAnchor="end">{v}</text>
            <line x1={cx0} y1={yToPx(v)} x2={cx0 + cw} y2={yToPx(v)}
              stroke="rgba(255,255,255,0.05)" strokeWidth={1} opacity={tAxes * fadeOut} />
          </React.Fragment>
        ))}
        <text x={cx0 - 60} y={cy0 + ch / 2}
          fill={C.gray} fillOpacity={tAxes * fadeOut}
          fontFamily={MONO} fontSize={10} letterSpacing="0.12em"
          textAnchor="middle"
          transform={'rotate(-90 ' + (cx0 - 60) + ' ' + (cy0 + ch / 2) + ')'}>
          µg/m³
        </text>

        {/* CAMS coarse line (dashed) */}
        <path d={buildPath('cams', tCams)} fill="none"
          stroke={C.orange} strokeWidth={1.8} strokeOpacity={0.7}
          strokeDasharray="6 4" opacity={fadeOut} />
        {/* Corrected line (solid) */}
        <path d={buildPath('corr', tCorr)} fill="none"
          stroke={C.cyan} strokeWidth={2.5} strokeOpacity={0.95}
          opacity={fadeOut} />
        {/* Glow under corrected line */}
        <path d={buildPath('corr', tCorr)} fill="none"
          stroke={C.cyan} strokeWidth={6} strokeOpacity={0.18}
          opacity={fadeOut} />

        {/* Observed dots — only on first 24h */}
        {data.filter(d => d.h <= 24 && d.h % 3 === 0).map((d, i) => {
          const ap = clamp((tObs - i * 0.05), 0, 1);
          return (
            <circle key={'obs' + i} cx={xToPx(d.h)} cy={yToPx(d.obs)} r={3}
              fill={C.white} stroke={C.cyan} strokeWidth={1.5}
              opacity={ap * fadeOut} />
          );
        })}

        {/* T-now divider */}
        <line x1={xToPx(24)} y1={cy0 - 8} x2={xToPx(24)} y2={cy0 + ch + 8}
          stroke="rgba(255,255,255,0.45)" strokeWidth={1} strokeDasharray="2 3"
          opacity={tBands * fadeOut} />
        <text x={xToPx(24)} y={cy0 - 14}
          fill={C.white} fillOpacity={tBands * fadeOut}
          fontFamily={MONO} fontSize={10} letterSpacing="0.16em" textAnchor="middle">
          AHORA
        </text>

        {/* Annotation arrows: gap between cams and corrected */}
        {tCallout > 0 && (() => {
          const h = 48;
          const xA = xToPx(h);
          const yCams = yToPx(data[h].cams);
          const yCorr = yToPx(data[h].corr);
          return (
            <g opacity={tCallout * fadeOut}>
              <line x1={xA + 30} y1={yCams} x2={xA + 30} y2={yCorr}
                stroke={C.green} strokeWidth={1.5} />
              <line x1={xA} y1={yCams} x2={xA + 30} y2={yCams}
                stroke={C.green} strokeWidth={1} strokeDasharray="2 3" />
              <line x1={xA} y1={yCorr} x2={xA + 30} y2={yCorr}
                stroke={C.green} strokeWidth={1} strokeDasharray="2 3" />
              <polygon points={(xA + 30) + ',' + (yCorr + 4) + ' ' + (xA + 26) + ',' + (yCorr - 2) + ' ' + (xA + 34) + ',' + (yCorr - 2)}
                fill={C.green} />
              <polygon points={(xA + 30) + ',' + (yCams - 4) + ' ' + (xA + 26) + ',' + (yCams + 2) + ' ' + (xA + 34) + ',' + (yCams + 2)}
                fill={C.green} />
              <text x={xA + 42} y={(yCams + yCorr) / 2 + 4}
                fill={C.green} fontFamily={MONO} fontSize={11}
                fontWeight={700} letterSpacing="0.08em">
                +Δ corregido
              </text>
            </g>
          );
        })()}
      </svg>

      {/* Legend */}
      <div style={{
        position: 'absolute', left: cx0, top: cy0 + ch + 70,
        display: 'flex', gap: 30, alignItems: 'center',
        opacity: tLegend * fadeOut,
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <svg width={32} height={3}>
            <line x1={0} y1={1.5} x2={32} y2={1.5}
              stroke={C.orange} strokeWidth={2} strokeDasharray="6 3" />
          </svg>
          <div>
            <div style={{ fontFamily: FONT, fontSize: 13, fontWeight: 600, color: C.orange }}>CAMS · 40 km</div>
            <div style={{ fontFamily: MONO, fontSize: 9.5, color: C.gray, letterSpacing: '0.08em' }}>background sin ajustar</div>
          </div>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <svg width={32} height={3}>
            <line x1={0} y1={1.5} x2={32} y2={1.5} stroke={C.cyan} strokeWidth={2.5} />
          </svg>
          <div>
            <div style={{ fontFamily: FONT, fontSize: 13, fontWeight: 600, color: C.cyan }}>Ajuste SIATA</div>
            <div style={{ fontFamily: MONO, fontSize: 9.5, color: C.gray, letterSpacing: '0.08em' }}>CAMS + WRF + estaciones</div>
          </div>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <div style={{
            width: 10, height: 10, borderRadius: '50%',
            background: C.white, border: '1.5px solid ' + C.cyan,
          }} />
          <div>
            <div style={{ fontFamily: FONT, fontSize: 13, fontWeight: 600, color: C.white }}>Observado</div>
            <div style={{ fontFamily: MONO, fontSize: 9.5, color: C.gray, letterSpacing: '0.08em' }}>20 estaciones</div>
          </div>
        </div>
      </div>

    </SceneFade>
  );
}

// ─── Scene 8: Outro / cierre ───────────────────────────────────────

function S8Outro() {
  const { localTime, duration } = useSprite();
  const time = useTime();

  // Subtle background flow
  const field = React.useMemo(() =>
    directionalField(Math.PI * 0.98, 0.0035, 0.15, 0.55, 0.4), []);

  const tKick = Easing.easeOutCubic(clamp((localTime - 0.3) / 0.5, 0, 1));
  const tHead = Easing.easeOutCubic(clamp((localTime - 0.7) / 0.7, 0, 1));
  const tStats = Easing.easeOutCubic(clamp((localTime - 1.5) / 0.6, 0, 1));
  const tFoot = Easing.easeOutCubic(clamp((localTime - 3.0) / 0.5, 0, 1));
  const fadeOut = clamp((duration - localTime) / 0.6, 0, 1);

  const stats = [
    { v: '4', l: 'fuentes integradas', c: C.cyan, d: 0 },
    { v: '20', l: 'estaciones SIATA', c: C.amber, d: 0.15 },
    { v: '72 h', l: 'horizonte de pronóstico', c: C.orange, d: 0.3, nowrap: true },
    { v: '>80%', l: 'acierto categoría ICA', c: C.green, d: 0.45 },
  ];

  return (
    <SceneFade>
      <WindyCanvas
        width={1280} height={720}
        count={750}
        field={field}
        palette={[C.cyan, C.sky, C.orange, C.amber, C.purple]}
        fade={0.08}
        speed={0.6}
        life={140}
        lineWidth={1}
        opacity={0.55}
        background={C.navy}
      />

      <div style={{
        position: 'absolute', inset: 0,
        background: 'radial-gradient(ellipse at 50% 50%, rgba(5,10,30,0) 25%, rgba(5,10,30,0.9) 95%)',
        pointerEvents: 'none',
      }} />

      <div style={{
        position: 'absolute', left: '50%', top: 130,
        transform: 'translateX(-50%)', textAlign: 'center',
        opacity: fadeOut,
      }}>
        <div style={{
          fontFamily: MONO, fontSize: 12, fontWeight: 600,
          letterSpacing: '0.32em', textTransform: 'uppercase', color: C.cyan,
          opacity: tKick, transform: 'translateY(' + ((1 - tKick) * 8) + 'px)',
        }}>El círculo se cierra</div>

        <div style={{
          marginTop: 22,
          fontFamily: FONT, fontSize: 48, fontWeight: 800,
          color: C.white, lineHeight: 1.15, letterSpacing: '-0.02em',
          opacity: tHead, transform: 'translateY(' + ((1 - tHead) * 16) + 'px)',
          maxWidth: 900, margin: '0 auto',
          textShadow: '0 4px 24px rgba(5,10,30,0.8)',
        }}>
          CAMS observa el planeta.<br />
          <span style={{
            background: 'linear-gradient(90deg,' + C.cyan + ',' + C.orange + ')',
            WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
            backgroundClip: 'text', color: 'transparent',
          }}>SIATA lo aterriza en el Valle.</span>
        </div>
      </div>

      {/* Stats grid */}
      <div style={{
        position: 'absolute', left: '50%', top: 360,
        transform: 'translateX(-50%)',
        display: 'flex', gap: 56, alignItems: 'flex-start',
        opacity: fadeOut,
      }}>
        {stats.map((s, i) => {
          const ap = Easing.easeOutBack(clamp((localTime - 1.5 - s.d) / 0.6, 0, 1));
          return (
            <div key={i} style={{
              textAlign: 'center',
              opacity: ap,
              transform: 'translateY(' + ((1 - ap) * 18) + 'px)',
            }}>
              <div style={{
                fontFamily: FONT, fontSize: 56, fontWeight: 800,
                color: s.c, lineHeight: 1,
                whiteSpace: 'nowrap',
                textShadow: '0 0 24px ' + hexWithAlpha(s.c, 0.4),
              }}>{s.v}</div>
              <div style={{
                fontFamily: MONO, fontSize: 11, color: C.gray, marginTop: 10,
                letterSpacing: '0.16em', textTransform: 'uppercase',
                maxWidth: 130,
              }}>{s.l}</div>
            </div>
          );
        })}
      </div>

      {/* Footer */}
      <div style={{
        position: 'absolute', left: '50%', bottom: 110,
        transform: 'translateX(-50%)', textAlign: 'center',
        opacity: tFoot * fadeOut,
      }}>
        <div style={{
          fontFamily: FONT, fontSize: 17, color: C.gray,
          maxWidth: 700, lineHeight: 1.55, textWrap: 'pretty',
        }}>
          Publicado cada 6 horas en <span style={{ color: C.white }}>siata.gov.co</span> ·
          PM<span style={{ fontSize: 13 }}>2.5</span> horario, 20 estaciones, 3 días vista.
        </div>
        <div style={{
          marginTop: 22,
          fontFamily: MONO, fontSize: 10, color: C.dim,
          letterSpacing: '0.28em', textTransform: 'uppercase',
        }}>
          CCT 261/2025 · SIATA
        </div>
      </div>
    </SceneFade>
  );
}

Object.assign(window, { S5Resolution, S6Downscale, S7Forecast, S8Outro });
