// templates-ext.jsx — Extended templates specific to the deck content structure.
// TemplateNav (deck map / index), TemplateIdea (hypothesis cards), TemplateEvidence (CHL vs SUPPORT)

// Inject hover/connector stylesheet once. Two-layer card scheme: the OUTER
// div holds the entry animation + rotation + position (its transform is
// rewritten every RAF tick), while the INNER div carries the hover
// scale-up via plain CSS — so it doesn't fight the RAF-driven transform.
(function injectThumbStyles() {
  if (typeof document === 'undefined') return;
  if (document.getElementById('thumb-card-styles')) return;
  const s = document.createElement('style');
  s.id = 'thumb-card-styles';
  s.textContent = `
    .thumb-card-outer {
      /* When hovered, lift to top of stacking context so neighbouring
         cards (which come later in DOM order = naturally above) don't
         overlap the enlarged card. */
      transition: z-index 0s;
    }
    .thumb-card-outer:hover {
      z-index: 200;
    }
    .thumb-card-inner {
      width: 100%; height: 100%;
      transition: transform 240ms cubic-bezier(.22,1,.36,1),
                  filter 240ms cubic-bezier(.22,1,.36,1),
                  box-shadow 240ms ease-out;
      will-change: transform;
      transform-origin: center;
    }
    .thumb-card-outer:hover .thumb-card-inner {
      transform: scale(1.08);
      filter: brightness(1.08);
    }
    /* Section connector — vertical line + dot between buckets */
    .section-connector {
      display: flex; flex-direction: column; align-items: center;
      gap: 16px;
      margin: 70px 0 24px;
      opacity: 0.78;
    }
    .section-connector .conn-line {
      width: 1.5px; height: 140px;
      background: linear-gradient(to bottom,
        rgba(255,255,255,0) 0%,
        rgba(255,255,255,0.45) 50%,
        rgba(255,255,255,0) 100%);
    }
    .section-connector .conn-dot {
      width: 6px; height: 6px; border-radius: 50%;
      background: rgba(255,255,255,0.65);
    }
    .section-connector .conn-label {
      font-family: 'JetBrains Mono', monospace;
      font-size: 11px; letter-spacing: 0.32em;
      text-transform: uppercase;
      color: rgba(255,255,255,0.5);
      margin-top: 6px;
    }
  `;
  document.head.appendChild(s);
})();

// ═══════════════════════════════════════════════════════════════════════════
// TEMPLATE NAV — DECK MAP / INDEX
//   • Use as the "Explore the deck" slide
//   • 4 BUCKETS in a 4-col strip (large cards, clickable to jump)
//   • Below: row of SHIFTS (S1..S5)
//   • Below: TAIL AREAS (A..J) as small tiles
//   • Below: IDEAS (IDEA-01..07) as horizontal rail
// ═══════════════════════════════════════════════════════════════════════════
function TemplateNav({ slide, scheme, onJump }) {
  const { title, subtitle, buckets = [], shifts = [], tail = [], ideas = [] } = slide;
  return (
    <SlideShell scheme={scheme}>
      {/* Heading */}
      <div style={{
        position: 'absolute', left: 80, right: 80, top: 130,
        textAlign: 'center',
      }}>
        <div style={{
          fontFamily: Tokens.fontMono, fontSize: 13,
          color: scheme.accent, letterSpacing: '0.32em',
          textTransform: 'uppercase',
          marginBottom: 16,
          fontWeight: Tokens.weight.semibold,
        }}>Deck Index · 4 Buckets + 5 Shifts + Tail + Ideas</div>
        <div style={{
          fontFamily: Tokens.fontDisplay,
          fontSize: 88, fontWeight: Tokens.weight.black,
          color: scheme.text,
          letterSpacing: '-0.025em',
          textTransform: 'uppercase',
          lineHeight: 0.95,
        }}>{title || 'Explore the Deck'}</div>
        {subtitle && (
          <div style={{
            marginTop: 14,
            fontFamily: Tokens.fontDisplay,
            fontSize: 17, color: scheme.textDim,
            maxWidth: 900, margin: '14px auto 0',
            lineHeight: 1.45,
          }}>{subtitle}</div>
        )}
      </div>

      {/* 4 Buckets — top row */}
      <div style={{
        position: 'absolute',
        left: 80, right: 80, top: 360,
        display: 'grid',
        gridTemplateColumns: 'repeat(4, 1fr)',
        gap: 16,
        height: 240,
      }}>
        {buckets.map((b, i) => {
          const panel = CARD_PANELS[b.panel || 'purpleMid'];
          return (
            <button key={i}
              onClick={() => onJump && onJump(b.slideIdx)}
              style={{
                position: 'relative',
                background: panel.fill,
                color: panel.text,
                border: 'none',
                padding: '20px 22px',
                borderRadius: Tokens.radius.lg,
                textAlign: 'left',
                cursor: 'pointer',
                display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
                overflow: 'hidden',
              }}>
              {/* corner brackets */}
              <CornerBrackets color={panel.text} size={20} thickness={1.5} opacity={0.5}/>
              <div>
                <div style={{
                  fontFamily: Tokens.fontMono, fontSize: 11,
                  letterSpacing: '0.28em', opacity: 0.75,
                  fontWeight: Tokens.weight.semibold,
                }}>BUCKET {String(i + 1).padStart(2, '0')}</div>
                <div style={{
                  marginTop: 8,
                  fontFamily: Tokens.fontDisplay,
                  fontSize: 26, fontWeight: Tokens.weight.extrabold,
                  letterSpacing: '-0.02em',
                  lineHeight: 1.0,
                  textTransform: 'uppercase',
                }}>{b.title}</div>
              </div>
              <div style={{
                display: 'flex', alignItems: 'center',
                justifyContent: 'space-between',
              }}>
                <div style={{
                  fontFamily: Tokens.fontDisplay, fontSize: 12,
                  opacity: 0.85, lineHeight: 1.3,
                  fontWeight: Tokens.weight.medium,
                }}>{b.note}</div>
                <div style={{
                  fontFamily: Tokens.fontMono, fontSize: 11,
                  letterSpacing: '0.18em',
                  fontWeight: Tokens.weight.semibold,
                }}>→</div>
              </div>
            </button>
          );
        })}
      </div>

      {/* SHIFTS — middle row */}
      <div style={{
        position: 'absolute',
        left: 80, right: 80, top: 630,
      }}>
        <div style={{
          fontFamily: Tokens.fontMono, fontSize: 11,
          color: scheme.textDim, letterSpacing: '0.28em',
          textTransform: 'uppercase', marginBottom: 10,
        }}>Strategic Shifts</div>
        <div style={{
          display: 'grid',
          gridTemplateColumns: `repeat(${shifts.length || 5}, 1fr)`,
          gap: 12,
        }}>
          {shifts.map((s, i) => (
            <button key={i}
              onClick={() => onJump && onJump(s.slideIdx)}
              style={{
                background: 'rgba(255,255,255,0.04)',
                border: `1px solid ${Tokens.inkLine}`,
                borderLeft: `3px solid ${scheme.accent}`,
                borderRadius: Tokens.radius.md,
                padding: '12px 16px',
                textAlign: 'left',
                cursor: 'pointer',
                color: scheme.text,
              }}>
              <div style={{
                fontFamily: Tokens.fontMono, fontSize: 10,
                color: scheme.accent, letterSpacing: '0.22em',
                fontWeight: Tokens.weight.semibold,
              }}>S{String(i + 1).padStart(2, '0')}</div>
              <div style={{
                marginTop: 4,
                fontFamily: Tokens.fontDisplay, fontSize: 14,
                fontWeight: Tokens.weight.bold,
                letterSpacing: '-0.01em',
                lineHeight: 1.15,
                textTransform: 'uppercase',
              }}>{s.title}</div>
            </button>
          ))}
        </div>
      </div>

      {/* TAIL + IDEAS — bottom row, 2 col */}
      <div style={{
        position: 'absolute',
        left: 80, right: 80, top: 770, height: 100,
        display: 'grid',
        gridTemplateColumns: '1.4fr 1fr',
        gap: 24,
      }}>
        {/* Tail */}
        <div>
          <div style={{
            fontFamily: Tokens.fontMono, fontSize: 11,
            color: scheme.textDim, letterSpacing: '0.28em',
            textTransform: 'uppercase', marginBottom: 10,
          }}>Tail Areas · {tail.length}</div>
          <div style={{
            display: 'flex', flexWrap: 'wrap', gap: 6,
          }}>
            {tail.map((t, i) => (
              <button key={i}
                onClick={() => onJump && onJump(t.slideIdx)}
                style={{
                  background: 'transparent',
                  border: `1px solid ${Tokens.inkLine}`,
                  borderRadius: Tokens.radius.sm,
                  padding: '6px 12px',
                  fontFamily: Tokens.fontMono, fontSize: 11,
                  color: scheme.text, letterSpacing: '0.18em',
                  textTransform: 'uppercase',
                  cursor: 'pointer',
                  fontWeight: Tokens.weight.semibold,
                }}>
                <span style={{ color: scheme.accent, marginRight: 6 }}>{t.id}</span>
                {t.title}
              </button>
            ))}
          </div>
        </div>
        {/* Ideas */}
        <div>
          <div style={{
            fontFamily: Tokens.fontMono, fontSize: 11,
            color: scheme.textDim, letterSpacing: '0.28em',
            textTransform: 'uppercase', marginBottom: 10,
          }}>Own Ideas · {ideas.length}</div>
          <div style={{
            display: 'flex', flexWrap: 'wrap', gap: 6,
          }}>
            {ideas.map((id, i) => (
              <button key={i}
                onClick={() => onJump && onJump(id.slideIdx)}
                style={{
                  background: 'transparent',
                  border: `1px solid ${scheme.accentBold}`,
                  borderRadius: Tokens.radius.sm,
                  padding: '6px 12px',
                  fontFamily: Tokens.fontMono, fontSize: 11,
                  color: scheme.accent, letterSpacing: '0.18em',
                  textTransform: 'uppercase',
                  cursor: 'pointer',
                  fontWeight: Tokens.weight.semibold,
                }}>
                {id.id}
              </button>
            ))}
          </div>
        </div>
      </div>
    </SlideShell>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// TEMPLATE IDEA — HYPOTHESIS / FUTURISTIC
//   • For IDEA-01..07 slides
//   • Big "IDEA · 0X" header
//   • Title + hypothesis statement
//   • Validation grid: SUPPORT × N | CHALLENGE × N
//   • Glow accent, holographic vibe
// ═══════════════════════════════════════════════════════════════════════════
function TemplateIdea({ slide, scheme }) {
  const {
    ideaNum, title, hypothesis, status = 'EARLY',
    upside, caveats = [], proof = [], chl = [],
    quantification, source,
  } = slide;
  return (
    <SlideShell scheme={scheme}>
      {/* Glow halo */}
      <div style={{
        position: 'absolute',
        left: '50%', top: '40%',
        width: 1600, height: 800,
        transform: 'translate(-50%, -50%)',
        background: `radial-gradient(ellipse, ${scheme.accent}25 0%, transparent 60%)`,
        mixBlendMode: 'screen',
        pointerEvents: 'none',
      }}/>

      {/* IDEA header */}
      <div style={{
        position: 'absolute', left: 80, top: 140,
        display: 'flex', alignItems: 'baseline', gap: 24,
      }}>
        <div style={{
          fontFamily: Tokens.fontMono, fontSize: 14,
          color: scheme.accent, letterSpacing: '0.32em',
          fontWeight: Tokens.weight.semibold,
          padding: '6px 14px',
          border: `1px solid ${scheme.accent}`,
          borderRadius: Tokens.radius.sm,
        }}>IDEA · {String(ideaNum || 1).padStart(2, '0')}</div>
        <div style={{
          fontFamily: Tokens.fontMono, fontSize: 11,
          color: scheme.textDim, letterSpacing: '0.28em',
          textTransform: 'uppercase',
        }}>Status · <span style={{ color: scheme.accent }}>{status}</span></div>
      </div>

      {/* Title */}
      <div style={{
        position: 'absolute', left: 80, right: 80, top: 200,
      }}>
        <div style={{
          fontFamily: Tokens.fontDisplay,
          fontSize: title.length > 30 ? 64 : 84,
          fontWeight: Tokens.weight.black,
          color: scheme.text,
          letterSpacing: '-0.025em',
          textTransform: 'uppercase',
          lineHeight: 0.94,
        }}>{renderTitle(title)}</div>
      </div>

      {/* Hypothesis */}
      {hypothesis && (
        <div style={{
          position: 'absolute', left: 80, right: 80, top: 380,
        }}>
          <div style={{
            fontFamily: Tokens.fontMono, fontSize: 11,
            color: scheme.accent, letterSpacing: '0.28em',
            marginBottom: 12,
            fontWeight: Tokens.weight.semibold,
          }}>HYPOTHESIS</div>
          <div style={{
            fontFamily: Tokens.fontDisplay,
            fontSize: 24, fontWeight: Tokens.weight.medium,
            color: scheme.text,
            letterSpacing: '-0.01em', lineHeight: 1.4,
            maxWidth: 1200,
            paddingLeft: 22,
            borderLeft: `3px solid ${scheme.accent}`,
            fontStyle: 'italic',
          }}>"{hypothesis}"</div>
        </div>
      )}

      {/* Quantification card (centered between hypothesis and evidence) */}
      {quantification && (
        <div style={{
          position: 'absolute',
          right: 80, top: 230, width: 360,
          padding: 24,
          background: 'rgba(255,255,255,0.04)',
          border: `1px solid ${scheme.accent}`,
          borderRadius: Tokens.radius.lg,
        }}>
          <div style={{
            fontFamily: Tokens.fontMono, fontSize: 11,
            color: scheme.accent, letterSpacing: '0.24em',
            marginBottom: 8,
            fontWeight: Tokens.weight.semibold,
          }}>QUANTIFICATION</div>
          <div style={{
            fontFamily: Tokens.fontDisplay,
            fontSize: 44, fontWeight: Tokens.weight.black,
            color: scheme.accent,
            letterSpacing: '-0.03em',
            lineHeight: 1,
          }}>{quantification.value}</div>
          <div style={{
            marginTop: 10,
            fontFamily: Tokens.fontDisplay, fontSize: 13,
            color: scheme.textDim, lineHeight: 1.4,
          }}>{quantification.label}</div>
        </div>
      )}

      {/* Evidence grid */}
      <div style={{
        position: 'absolute',
        left: 80, right: 80, top: 580, height: 240,
        display: 'grid',
        gridTemplateColumns: '1fr 1fr',
        gap: 20,
      }}>
        {/* PROOF */}
        <div style={{
          padding: 22,
          background: 'rgba(46,213,115,0.06)',
          border: `1px solid rgba(46,213,115,0.4)`,
          borderRadius: Tokens.radius.lg,
        }}>
          <div style={{
            fontFamily: Tokens.fontMono, fontSize: 11,
            color: '#3DFFA0', letterSpacing: '0.24em',
            marginBottom: 12,
            fontWeight: Tokens.weight.semibold,
          }}>✓ PROOF · {proof.length}</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {proof.map((p, i) => (
              <div key={i} style={{
                fontFamily: Tokens.fontDisplay, fontSize: 14,
                color: scheme.text, lineHeight: 1.4,
              }}>
                <span style={{ color: '#3DFFA0', marginRight: 8 }}>•</span>
                {p}
              </div>
            ))}
          </div>
        </div>
        {/* CHALLENGE */}
        <div style={{
          padding: 22,
          background: 'rgba(255,77,46,0.06)',
          border: `1px solid rgba(255,77,46,0.4)`,
          borderRadius: Tokens.radius.lg,
        }}>
          <div style={{
            fontFamily: Tokens.fontMono, fontSize: 11,
            color: '#FF6B5A', letterSpacing: '0.24em',
            marginBottom: 12,
            fontWeight: Tokens.weight.semibold,
          }}>⚠ CHALLENGE · {chl.length}</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {chl.map((p, i) => (
              <div key={i} style={{
                fontFamily: Tokens.fontDisplay, fontSize: 14,
                color: scheme.text, lineHeight: 1.4,
              }}>
                <span style={{ color: '#FF6B5A', marginRight: 8 }}>•</span>
                {p}
              </div>
            ))}
          </div>
        </div>
      </div>

      {source && (
        <div style={{ position: 'absolute', right: 80, bottom: 200, whiteSpace: 'nowrap' }}>
          <SourceCite>{source}</SourceCite>
        </div>
      )}
    </SlideShell>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// TEMPLATE EVIDENCE — CHL vs SUPPORT stack
//   • For tail areas / shift slides with strong evidence base
//   • 2 columns: SUPPORT cards on left (green), CHALLENGE cards on right (red)
//   • Each card: tier badge (A/B/C), source, snippet
// ═══════════════════════════════════════════════════════════════════════════
function TemplateEvidence({ slide, scheme }) {
  const {
    kicker, title, subtitle,
    support = [], challenge = [],
    source,
  } = slide;
  return (
    <SlideShell scheme={scheme}>
      {/* Heading */}
      <div style={{
        position: 'absolute', left: 80, right: 80, top: 130,
      }}>
        {kicker && (
          <div style={{
            fontFamily: Tokens.fontMono, fontSize: 13,
            color: scheme.accent, letterSpacing: '0.28em',
            textTransform: 'uppercase',
            marginBottom: 14,
            fontWeight: Tokens.weight.semibold,
          }}>{kicker}</div>
        )}
        <div style={{
          fontFamily: Tokens.fontDisplay,
          fontSize: title.length > 28 ? 56 : 72,
          fontWeight: Tokens.weight.black,
          color: scheme.text,
          letterSpacing: '-0.025em',
          textTransform: 'uppercase',
          lineHeight: 0.95,
        }}>{renderTitle(title)}</div>
        {subtitle && (
          <div style={{
            marginTop: 14,
            fontFamily: Tokens.fontDisplay,
            fontSize: 17, color: scheme.textDim,
            maxWidth: 1100, lineHeight: 1.5,
          }}>{subtitle}</div>
        )}
      </div>

      {/* 2-col evidence */}
      <div style={{
        position: 'absolute',
        left: 80, right: 80, top: 360, bottom: 200,
        display: 'grid',
        gridTemplateColumns: '1fr 1fr',
        gap: 20,
      }}>
        <EvidenceColumn title="SUPPORT" color="#3DFFA0" items={support}/>
        <EvidenceColumn title="CHALLENGE" color="#FF6B5A" items={challenge}/>
      </div>

      {source && (
        <div style={{ position: 'absolute', right: 80, bottom: 200, whiteSpace: 'nowrap' }}>
          <SourceCite>{source}</SourceCite>
        </div>
      )}
    </SlideShell>
  );
}

function EvidenceColumn({ title, color, items }) {
  return (
    <div>
      <div style={{
        display: 'flex', alignItems: 'baseline', gap: 12,
        marginBottom: 16,
      }}>
        <div style={{
          fontFamily: Tokens.fontMono, fontSize: 12,
          color: color, letterSpacing: '0.28em',
          fontWeight: Tokens.weight.semibold,
        }}>{title === 'SUPPORT' ? '✓' : '⚠'} {title}</div>
        <div style={{
          fontFamily: Tokens.fontMono, fontSize: 11,
          color: Tokens.inkSoft, letterSpacing: '0.18em',
        }}>· {items.length} ITEMS</div>
      </div>
      <div style={{
        display: 'flex', flexDirection: 'column', gap: 12,
      }}>
        {items.map((it, i) => (
          <div key={i} style={{
            padding: '14px 16px',
            background: 'rgba(255,255,255,0.03)',
            border: `1px solid ${Tokens.inkLine}`,
            borderLeft: `3px solid ${color}`,
            borderRadius: Tokens.radius.md,
          }}>
            <div style={{
              display: 'flex', alignItems: 'center', gap: 10,
              marginBottom: 8,
            }}>
              <span style={{
                fontFamily: Tokens.fontMono, fontSize: 10,
                color: color, letterSpacing: '0.2em',
                fontWeight: Tokens.weight.bold,
                padding: '3px 8px',
                border: `1px solid ${color}`,
                borderRadius: Tokens.radius.sm,
              }}>TIER {it.tier || 'B'}</span>
              <span style={{
                fontFamily: Tokens.fontMono, fontSize: 11,
                color: Tokens.inkSoft, letterSpacing: '0.16em',
                textTransform: 'uppercase',
              }}>{it.source}</span>
            </div>
            <div style={{
              fontFamily: Tokens.fontDisplay, fontSize: 15,
              color: Tokens.ink, lineHeight: 1.45,
              fontWeight: Tokens.weight.medium,
            }}>{it.text}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { TemplateNav, TemplateIdea, TemplateEvidence, EvidenceColumn });

// ═══════════════════════════════════════════════════════════════════════════
// TEMPLATE GLOBE — Earth + arcing cards (signature, à la Netflix Effect)
//   • Europe-focused earth-from-space at center bottom (CSS sphere)
//   • Fan of N thumbnails arcing across the top of the earth
//   • Each card is tangentially rotated to the arc
//   • Below: STORY 0X label + huge title + paragraph
//   • Corner brackets frame the image area
//   • Use for: bucket intros, where N thumbnails tell the bucket story
// ═══════════════════════════════════════════════════════════════════════════
function TemplateGlobe({ slide, scheme, onJump }) {
  const { kicker, storyNum, title, subtitle, thumbs = [], source } = slide;

  // ── Geometry ────────────────────────────────────────────────────────────
  const stageW = 1920;
  const boxLeft = 220, boxRight = 220, boxTop = 130, boxHeight = 460;
  const boxW = stageW - boxLeft - boxRight;

  // Earth center — below the image box so only the curved top is visible.
  const earthCx = stageW / 2;
  const earthCy = boxTop + boxHeight + 100;  // = 690
  const earthR  = 340;
  const arcR    = earthR + 40;               // = 380

  const N = thumbs.length;
  // Narrower 90° arc keeps every card inside the box bounds, even at the ends.
  const arcSpanDeg = 90;
  const arcStartDeg = -135;

  // ── Animation state machine ─────────────────────────────────────────────
  //   "entering" → cards spin into place (≈1.4s)
  //   "orbiting" → cards drift gently around the earth (forever)
  // (We skip the "playing" phase now that the video is removed.)
  const [phase, setPhase] = React.useState('entering');
  const phaseStartRef = React.useRef(performance.now());
  const [now, setNow] = React.useState(performance.now());

  // RAF clock
  React.useEffect(() => {
    let raf;
    const tick = () => {
      setNow(performance.now());
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);

  // Transition entering → orbiting
  const ENTRY_MS = 1400;
  React.useEffect(() => {
    if (phase !== 'entering') return;
    const id = setTimeout(() => {
      phaseStartRef.current = performance.now();
      setPhase('orbiting');
    }, ENTRY_MS);
    return () => clearTimeout(id);
  }, [phase]);

  const elapsedSec = (now - phaseStartRef.current) / 1000;

  let cardOpacity = 0;
  let cardScale   = 0.4;
  let arcDriftDeg = 0;
  let cardSpinDeg = 0;

  if (phase === 'entering') {
    const t = Math.min(1, elapsedSec / (ENTRY_MS / 1000));
    const eased = 1 - Math.pow(1 - t, 3);
    cardOpacity = eased;
    cardScale   = 0.4 + 0.6 * eased;
    arcDriftDeg = -45 * (1 - eased);
    cardSpinDeg = -90 * (1 - eased);
  } else if (phase === 'orbiting') {
    cardOpacity = 1;
    cardScale   = 1;
    // Static at base angles — the orbital wobble made the arc midpoint
    // drift ±20px off the earth's vertical axis, which read as "not
    // centered". Locked to 0 keeps the arc symmetric around the earth.
    arcDriftDeg = 0;
  }

  // ── Compute card positions ──────────────────────────────────────────────
  const cardData = thumbs.map((t, i) => {
    const u = N === 1 ? 0.5 : i / (N - 1);
    const baseAngDeg = arcStartDeg + u * arcSpanDeg;
    const angDeg = baseAngDeg + arcDriftDeg;
    const angRad = angDeg * Math.PI / 180;
    const cx = earthCx + Math.cos(angRad) * arcR;
    const cy = earthCy + Math.sin(angRad) * arcR;
    const rot = angDeg + 90 + cardSpinDeg;
    return { ...t, cx, cy, rot };
  });

  return (
    <SlideShell scheme={scheme}>
      {/* Image box ────────────────────────────────────────────────────── */}
      <div style={{
        position: 'absolute',
        left: boxLeft, right: boxRight, top: boxTop,
        height: boxHeight,
        background: '#020610',
        borderRadius: Tokens.radius.lg,
        overflow: 'hidden',
      }}>
        {/* Earth — CSS sphere (clean, no video for now) */}
        <Earth cx={earthCx - boxLeft} cy={earthCy - boxTop} r={earthR} scheme={scheme}/>

        {/* Arcing cards — two-layer outer/inner so hover scale-up doesn't
            collide with the RAF-driven rotation+scale on the outer ─────── */}
        {cardData.map((c, i) => {
          const w = c.w || 112;
          const h = c.h || 156;
          const clickable = !!c.slideIdx;
          return (
            <div key={i}
              className="thumb-card-outer"
              onClick={() => { if (clickable) onJump && onJump(c.slideIdx); }}
              style={{
                position: 'absolute',
                left: c.cx - boxLeft,
                top: c.cy - boxTop,
                width: w, height: h,
                transform: `translate(-50%, -50%) rotate(${c.rot}deg) scale(${cardScale})`,
                transformOrigin: 'center',
                opacity: cardOpacity,
                willChange: 'transform, opacity',
                cursor: clickable ? 'pointer' : 'default',
              }}>
              <div
                className="thumb-card-inner"
                style={{
                  borderRadius: Tokens.radius.lg,
                  overflow: 'hidden',
                  boxShadow: '0 20px 60px rgba(0,0,0,0.7)',
                  border: `1px solid rgba(255,255,255,0.12)`,
                  position: 'relative',
                }}
                onMouseEnter={(e) => {
                  if (!clickable) return;
                  e.currentTarget.style.boxShadow = `0 30px 80px rgba(0,0,0,0.8), 0 0 0 2px ${scheme.accent}`;
                }}
                onMouseLeave={(e) => {
                  if (!clickable) return;
                  e.currentTarget.style.boxShadow = '0 20px 60px rgba(0,0,0,0.7)';
                }}
              >
                <img src={c.src} alt={c.label || ''} style={{
                  width: '100%', height: '100%', objectFit: 'cover', display: 'block',
                }}/>
                <div style={{
                  position: 'absolute', inset: 0,
                  background: 'linear-gradient(0deg, rgba(0,0,0,0.55) 0%, transparent 45%)',
                  pointerEvents: 'none',
                }}/>
                <div style={{
                  position: 'absolute',
                  left: 6, top: 6,
                  padding: '3px 6px',
                  background: 'rgba(0,0,0,0.55)',
                  backdropFilter: 'blur(6px)',
                  border: `1px solid rgba(255,255,255,0.18)`,
                  borderRadius: 4,
                  fontFamily: Tokens.fontMono,
                  fontSize: 8, letterSpacing: '0.14em',
                  color: '#fff', fontWeight: 700,
                  textTransform: 'uppercase',
                }}>
                  {c.channelLogo
                    ? <img src={c.channelLogo} alt={c.channel || ''} style={{ height: 14, display: 'block' }}/>
                    : (c.channel || 'CHANNEL')}
                </div>
              </div>
            </div>
          );
        })}
      </div>

      {/* Corner brackets framing the image area */}
      <div style={{
        position: 'absolute',
        left: boxLeft - 30, top: boxTop - 30,
        right: boxRight - 30, height: boxHeight + 60,
        pointerEvents: 'none',
      }}>
        <CornerBrackets color={scheme.brackets} size={80} thickness={3} opacity={0.75}/>
      </div>

      {/* STORY 0X label */}
      {storyNum != null && (
        <div style={{
          position: 'absolute',
          left: 0, right: 0, top: boxTop + boxHeight + 30,
          display: 'flex', justifyContent: 'center',
        }}>
          <StoryLabel num={storyNum} color={scheme.storyLabel}/>
        </div>
      )}

      {kicker && storyNum == null && (
        <div style={{
          position: 'absolute',
          left: 0, right: 0, top: boxTop + boxHeight + 30,
          textAlign: 'center',
          fontFamily: Tokens.fontMono, fontSize: 13,
          color: scheme.accent, letterSpacing: '0.32em',
          textTransform: 'uppercase',
          fontWeight: Tokens.weight.semibold,
        }}>{kicker}</div>
      )}

      {/* Title */}
      <div style={{
        position: 'absolute',
        left: 80, right: 80, top: boxTop + boxHeight + 70,
        textAlign: 'center',
      }}>
        <div style={{
          fontFamily: Tokens.fontDisplay,
          fontSize: title.length > 28 ? 44 : title.length > 18 ? 56 : 68,
          fontWeight: Tokens.weight.black,
          color: scheme.text,
          letterSpacing: '-0.025em',
          textTransform: 'uppercase',
          lineHeight: 0.95,
        }}>{renderTitle(title)}</div>
        {subtitle && (
          <div style={{
            marginTop: 14,
            fontFamily: Tokens.fontDisplay,
            fontSize: 15, fontWeight: Tokens.weight.regular,
            color: scheme.textDim,
            lineHeight: 1.45,
            maxWidth: 1000,
            margin: '14px auto 0',
          }}>{subtitle}</div>
        )}
      </div>

      {source && (
        <div style={{ position: 'absolute', right: 80, bottom: 200, whiteSpace: 'nowrap' }}>
          <SourceCite>{source}</SourceCite>
        </div>
      )}
    </SlideShell>
  );
}

// ── Earth (CSS sphere with Europe-focus styling) ─────────────────────────
function Earth({ cx, cy, r, scheme }) {
  return (
    <>
      {/* Outer atmospheric glow */}
      <div style={{
        position: 'absolute',
        left: cx - r - 60,
        top: cy - r - 60,
        width: (r + 60) * 2,
        height: (r + 60) * 2,
        borderRadius: '50%',
        background: `radial-gradient(circle, transparent 60%, ${scheme.accent}22 70%, transparent 82%)`,
        filter: 'blur(8px)',
        pointerEvents: 'none',
      }}/>

      {/* Earth sphere — brighter so it reads against the dark box */}
      <div style={{
        position: 'absolute',
        left: cx - r,
        top: cy - r,
        width: r * 2,
        height: r * 2,
        borderRadius: '50%',
        background: `
          radial-gradient(circle at 50% 35%,
            rgba(140,200,255,0.6) 0%,
            rgba(60,110,180,0.85) 25%,
            rgba(20,50,110,0.95) 55%,
            #0A1F4A 80%,
            #050E28 100%)
        `,
        boxShadow: `
          inset 0 0 60px rgba(0,0,0,0.4),
          inset 30px -30px 100px rgba(0,0,0,0.4),
          0 0 100px rgba(100,180,255,0.25)
        `,
      }}>
        {/* Subtle land mass hint via faint city-light pattern (SVG dots) */}
        <svg width="100%" height="100%" viewBox={`0 0 ${r * 2} ${r * 2}`}
          style={{ position: 'absolute', inset: 0 }}>
          {/* Europe nightside subtle glow dots — clustered around top center */}
          {Array.from({ length: 80 }).map((_, i) => {
            // Random scatter within a Europe-shaped patch (top center of sphere)
            const seed = i * 137.51;
            const angle = (seed % 360) * Math.PI / 180;
            const dist = ((seed * 7) % 100) / 100;
            // Bias toward top half + Europe area
            const u = (seed % 100) / 100;
            const v = ((seed * 3) % 100) / 100;
            const x = r + (u - 0.5) * r * 1.3;
            const y = r * 0.45 + (v - 0.5) * r * 0.5;
            // Distance from sphere center for clipping
            const dx = x - r, dy = y - r;
            const distFromCenter = Math.sqrt(dx * dx + dy * dy);
            if (distFromCenter > r * 0.9) return null;
            const op = 0.15 + 0.5 * ((seed * 11) % 100) / 100;
            return (
              <circle key={i} cx={x} cy={y} r={0.8 + ((seed * 5) % 3)}
                fill="rgba(255,220,140,1)" opacity={op}/>
            );
          })}
        </svg>

        {/* Top rim light — brighter atmospheric glow */}
        <div style={{
          position: 'absolute',
          left: 0, top: 0, right: 0, height: '22%',
          borderRadius: '50% 50% 0 0 / 100% 100% 0 0',
          background: `linear-gradient(180deg, ${scheme.accent}cc 0%, ${scheme.accent}33 60%, transparent 100%)`,
          filter: 'blur(14px)',
          opacity: 0.85,
          pointerEvents: 'none',
        }}/>
      </div>
    </>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// TEMPLATE CLAIM — Statement-led slide with optional killer number
//   • Big bold statement (centered or left-aligned)
//   • Optional killer number that pops below or to the side
//   • Source footer
//   • Use for: thesis/claim slides like "MFE non è più broadcaster italiano"
// ═══════════════════════════════════════════════════════════════════════════
function TemplateClaim({ slide, scheme }) {
  const {
    kicker, statement, body,
    killerNumber, killerLabel, killerPrefix, killerSuffix,
    quote, quoteAuthor,
    source, align = 'left',
  } = slide;

  // Auto-shrink statement when there's a lot of other content on the slide,
  // so a 3-line statement + body + killer card + quote can all fit without
  // overlapping the source line at the bottom.
  const heavyContent = !!killerNumber && !!quote;
  const statementSize = heavyContent ? 46 : statement.length > 60 ? 52 : 60;

  return (
    <SlideShell scheme={scheme}>
      <div style={{
        position: 'absolute',
        left: 80, right: 80, top: 150, bottom: 180,
        display: 'flex', flexDirection: 'column',
        justifyContent: 'flex-start',
        gap: 22,
      }}>
        {kicker && (
          <div style={{
            fontFamily: Tokens.fontMono, fontSize: 13,
            color: scheme.accent, letterSpacing: '0.32em',
            textTransform: 'uppercase',
            fontWeight: Tokens.weight.semibold,
            textAlign: align,
          }}>{kicker}</div>
        )}

        {/* Statement */}
        <div style={{
          fontFamily: Tokens.fontDisplay,
          fontSize: statementSize,
          fontWeight: Tokens.weight.black,
          color: scheme.text,
          letterSpacing: '-0.025em',
          lineHeight: 1.05,
          textAlign: align,
          maxWidth: 1400,
        }}>{renderTitle(statement)}</div>

        {/* Body paragraph */}
        {body && (
          <div style={{
            fontFamily: Tokens.fontDisplay,
            fontSize: heavyContent ? 17 : 19,
            fontWeight: Tokens.weight.regular,
            color: scheme.textDim,
            lineHeight: 1.5,
            maxWidth: 1100,
            textAlign: align,
          }}>{body}</div>
        )}

        {/* Killer number card — number + label vertically centered, card
            horizontally centered on the slide (more punch for the single
            anchor stat). alignItems:center instead of baseline so the small
            label doesn't sink to the bottom of the big number. */}
        {killerNumber && (
          <div style={{
            padding: '28px 40px',
            border: `1px solid ${scheme.accent}`,
            borderLeft: `4px solid ${scheme.accent}`,
            background: 'rgba(255,255,255,0.02)',
            borderRadius: Tokens.radius.lg,
            alignSelf: 'center',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            gap: 32,
            maxWidth: 1100,
          }}>
            <div style={{
              fontFamily: Tokens.fontDisplay,
              fontWeight: Tokens.weight.black,
              fontSize: 92,
              color: scheme.accent,
              letterSpacing: '-0.04em',
              lineHeight: 0.9,
              display: 'flex', alignItems: 'baseline',
              whiteSpace: 'nowrap',
              flex: '0 0 auto',
            }}>
              {killerPrefix && <span style={{ fontSize: 44, marginRight: 4 }}>{killerPrefix}</span>}
              <span>{killerNumber}</span>
              {killerSuffix && <span style={{ fontSize: 44, marginLeft: 4 }}>{killerSuffix}</span>}
            </div>
            {killerLabel && (
              <div style={{
                fontFamily: Tokens.fontDisplay,
                fontSize: 17,
                fontWeight: Tokens.weight.medium,
                color: scheme.textDim,
                lineHeight: 1.45,
                maxWidth: 560,
                textAlign: 'left',
              }}>{killerLabel}</div>
            )}
          </div>
        )}

        {/* Quote */}
        {quote && (
          <div style={{
            paddingLeft: 20,
            borderLeft: `3px solid ${scheme.accent}`,
            fontFamily: Tokens.fontDisplay,
            fontSize: 16,
            fontStyle: 'italic',
            fontWeight: Tokens.weight.medium,
            color: scheme.textDim,
            lineHeight: 1.45,
            maxWidth: 1100,
          }}>
            "{quote}"
            {quoteAuthor && (
              <div style={{
                marginTop: 6,
                fontStyle: 'normal',
                fontFamily: Tokens.fontMono, fontSize: 11,
                color: Tokens.inkSoft, letterSpacing: '0.18em',
                textTransform: 'uppercase',
              }}>— {quoteAuthor}</div>
            )}
          </div>
        )}
      </div>

      {source && (
        <div style={{ position: 'absolute', left: 80, bottom: 130, whiteSpace: 'nowrap' }}>
          <SourceCite>{source}</SourceCite>
        </div>
      )}
    </SlideShell>
  );
}

Object.assign(window, { TemplateGlobe, TemplateClaim, Earth });

// ═══════════════════════════════════════════════════════════════════════════
// TEMPLATE LONG-SCROLL — Scroll-driven editorial story page
//   • The slide BECOMES a scrollable canvas: scroll-wheel inside the slide
//     scrolls vertically through stacked sections.
//   • Layout: small hero photo at top → big title → N "ContentHighlight"
//     sections (each: stat + scattered thumbs + paragraph) → "More stories"
//   • Right-side pagination bullets reflect which section is in view.
//   • Thumbnails per section reveal with stagger on scroll-into-view.
//   • Use for: deep-dive stories where you want the Netflix-Effect feel of
//     scrolling through a long editorial page on the same "slide".
//
//  Slide data shape:
//    {
//      template: 'LongScroll',
//      scheme:   'purpleDeep',
//      kicker:   'STORY 01',
//      title:    'GROWING THE\\nEUROPEAN PLATFORM',
//      subtitle: 'short claim under title',
//      image:    __a('show-drama', 'jpg'),   // small hero photo at top
//      imagePos: 'center 30%',
//      sections: [
//        { stat: '€325B', label: 'GLOBAL GVA',
//          body: 'paragraph of context',
//          thumbs: [{ src, x, y, w, h, rotate }, ...] },
//        ...
//      ],
//      moreStories: [{ shortLabel, thumb, slideIdx }, ...]
//    }
// ═══════════════════════════════════════════════════════════════════════════
function TemplateLongScroll({ slide, scheme, onJump }) {
  const {
    kicker, storyNum, title, subtitle,
    image, imagePos = 'center 30%',
    sections = [], moreStories = [], source,
  } = slide;

  const scrollRef = React.useRef(null);
  const [activeSection, setActiveSection] = React.useState(0);
  const [scrollY, setScrollY] = React.useState(0);

  React.useEffect(() => {
    const el = scrollRef.current;
    if (!el) return;
    const onScroll = () => {
      setScrollY(el.scrollTop);
      // find the section closest to the viewport center
      const sectionEls = el.querySelectorAll('[data-section-idx]');
      const viewCenter = el.scrollTop + el.clientHeight / 2;
      let bestIdx = 0;
      let bestDist = Infinity;
      sectionEls.forEach((s) => {
        const idx = parseInt(s.getAttribute('data-section-idx'), 10);
        const r = s.getBoundingClientRect();
        const elR = el.getBoundingClientRect();
        const center = r.top - elR.top + el.scrollTop + r.height / 2;
        const d = Math.abs(center - viewCenter);
        if (d < bestDist) { bestDist = d; bestIdx = idx; }
      });
      setActiveSection(bestIdx);
    };
    el.addEventListener('scroll', onScroll, { passive: true });
    return () => el.removeEventListener('scroll', onScroll);
  }, [sections.length]);

  // Helper: scroll to a section by index
  const jumpToSection = (idx) => {
    const el = scrollRef.current;
    if (!el) return;
    const target = el.querySelector(`[data-section-idx="${idx}"]`);
    if (target) target.scrollIntoView({ behavior: 'smooth', block: 'center' });
  };

  return (
    <SlideShell scheme={scheme}>
      {/* Scrollable canvas — the whole slide body becomes a scroll container */}
      <div
        ref={scrollRef}
        style={{
          position: 'absolute',
          left: 0, right: 0, top: 110, bottom: 0,
          overflowY: 'auto',
          overflowX: 'hidden',
          scrollBehavior: 'smooth',
        }}
      >
        {/* ── REDUCED HERO ────────────────────────────────────────────── */}
        <div style={{
          position: 'relative',
          margin: '0 auto',
          width: 1480,
          height: 400,
          borderRadius: Tokens.radius.xxl,
          overflow: 'hidden',
          marginTop: 30,
          boxShadow: '0 30px 90px rgba(0,0,0,0.55)',
        }}>
          <div style={{
            position: 'absolute', inset: 0,
            backgroundImage: `url(${image})`,
            backgroundSize: 'cover',
            backgroundPosition: imagePos,
          }}/>
          <div style={{
            position: 'absolute', inset: 0,
            background: 'linear-gradient(0deg, rgba(8,8,14,0.92) 0%, rgba(8,8,14,0.25) 60%, transparent 100%)',
          }}/>
          {/* Back to overview pill */}
          <div style={{
            position: 'absolute', top: 24, left: 24,
            padding: '8px 16px 8px 12px',
            background: 'rgba(20,20,28,0.6)',
            border: `1px solid ${Tokens.inkLine}`,
            borderRadius: Tokens.radius.pill,
            backdropFilter: 'blur(14px)',
            color: Tokens.ink,
            fontFamily: Tokens.fontDisplay,
            fontSize: 13, fontWeight: 500,
            display: 'flex', alignItems: 'center', gap: 6,
          }}>
            <span style={{ fontSize: 16, lineHeight: 1 }}>‹</span>
            Back to overview
          </div>
        </div>

        {/* Corner brackets framing the hero photo */}
        <div style={{
          position: 'absolute', top: 30, left: 'calc(50% - 740px - 30px)',
          width: 1540, height: 460,
          pointerEvents: 'none',
        }}>
          <CornerBrackets color={scheme.brackets} size={60} thickness={2.5} opacity={0.85}/>
        </div>

        {/* ── BIG TITLE BLOCK ─────────────────────────────────────────── */}
        <div style={{
          margin: '64px auto 0',
          maxWidth: 1200,
          textAlign: 'center',
        }}>
          {kicker && (
            <div style={{
              fontFamily: Tokens.fontMono, fontSize: 13,
              color: scheme.accent, letterSpacing: '0.32em',
              textTransform: 'uppercase',
              marginBottom: 16,
              fontWeight: Tokens.weight.semibold,
            }}>{kicker}</div>
          )}
          {storyNum != null && (
            <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 18 }}>
              <StoryLabel num={storyNum} color={scheme.storyLabel}/>
            </div>
          )}
          <div style={{
            fontFamily: Tokens.fontDisplay,
            fontSize: title.length > 26 ? 72 : 92,
            fontWeight: Tokens.weight.black,
            color: scheme.text,
            letterSpacing: '-0.025em',
            textTransform: 'uppercase',
            lineHeight: 0.94,
          }}>{renderTitle(title)}</div>
          {subtitle && (
            <div style={{
              marginTop: 24,
              fontFamily: Tokens.fontDisplay,
              fontSize: 18, fontWeight: Tokens.weight.regular,
              color: scheme.textDim,
              lineHeight: 1.55,
              maxWidth: 900, margin: '24px auto 0',
            }}>{subtitle}</div>
          )}
          {/* Scroll affordance */}
          <div style={{
            marginTop: 56,
            display: 'inline-flex', alignItems: 'center', gap: 10,
            fontFamily: Tokens.fontMono, fontSize: 11,
            color: scheme.textDim, letterSpacing: '0.28em',
            textTransform: 'uppercase',
            opacity: scrollY > 50 ? 0 : 0.85,
            transition: 'opacity 200ms',
          }}>
            Scroll
            <span style={{
              display: 'inline-block',
              transform: `translateY(${Math.sin(scrollY / 100) * 3}px)`,
              animation: 'scrollNudge 1.6s ease-in-out infinite',
            }}>↓</span>
          </div>
        </div>

        {/* ── CONTENT HIGHLIGHT SECTIONS ──────────────────────────────── */}
        {sections.map((s, i) => (
          <ContentHighlight key={i} idx={i} section={s} scheme={scheme} scrollEl={scrollRef.current} onJump={onJump}/>
        ))}

        {/* ── MORE STORIES (footer) ───────────────────────────────────── */}
        {moreStories.length > 0 && (
          <div style={{ margin: '120px auto 80px', maxWidth: 1480, padding: '0 80px' }}>
            <div style={{
              fontFamily: Tokens.fontMono, fontSize: 13,
              color: scheme.textDim, letterSpacing: '0.32em',
              textTransform: 'uppercase',
              marginBottom: 22,
              fontWeight: Tokens.weight.semibold,
            }}>More stories for you</div>
            <div style={{
              display: 'grid',
              gridTemplateColumns: moreStories.length >= 4 ? 'repeat(4, 1fr)' : 'repeat(2, 1fr)',
              gap: 16,
            }}>
              {moreStories.map((ms, mi) => (
                <button key={mi}
                  onClick={() => onJump && ms.slideIdx != null && onJump(ms.slideIdx)}
                  style={{
                    position: 'relative',
                    height: moreStories.length >= 4 ? 180 : 220,
                    borderRadius: Tokens.radius.xl,
                    overflow: 'hidden',
                    border: `1px solid ${Tokens.inkLine}`,
                    cursor: 'pointer',
                    background: 'none', padding: 0, textAlign: 'left',
                  }}>
                  <div style={{
                    position: 'absolute', inset: 0,
                    backgroundImage: ms.thumb ? `url(${ms.thumb})` : 'none',
                    backgroundSize: 'cover',
                    backgroundPosition: ms.imagePos || 'center',
                    backgroundColor: Tokens.bgSurface,
                  }}/>
                  <div style={{
                    position: 'absolute', inset: 0,
                    background: 'linear-gradient(0deg, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.3) 60%, transparent 100%)',
                  }}/>
                  <div style={{
                    position: 'absolute', left: 24, bottom: 22, right: 24,
                  }}>
                    <div style={{
                      fontFamily: Tokens.fontMono, fontSize: 11,
                      color: scheme.accent, letterSpacing: '0.24em',
                      textTransform: 'uppercase',
                      marginBottom: 6,
                      fontWeight: Tokens.weight.semibold,
                    }}>Story {String((ms.slideIdx ?? 0) + 1).padStart(2, '0')} →</div>
                    <div style={{
                      fontFamily: Tokens.fontDisplay, fontSize: 24,
                      fontWeight: Tokens.weight.extrabold,
                      color: Tokens.ink, letterSpacing: '-0.015em',
                      lineHeight: 1.1,
                      textTransform: 'uppercase',
                    }}>{ms.shortLabel || ms.title}</div>
                  </div>
                </button>
              ))}
            </div>
          </div>
        )}

        {source && (
          <div style={{
            margin: '0 auto 60px', maxWidth: 1480, padding: '0 80px',
            textAlign: 'right',
          }}>
            <SourceCite>{source}</SourceCite>
          </div>
        )}
      </div>

      {/* ── PAGINATION BULLETS (vertical, right side) ────────────────── */}
      {sections.length > 1 && (
        <div style={{
          position: 'absolute',
          right: 40, top: '50%',
          transform: 'translateY(-50%)',
          display: 'flex', flexDirection: 'column', gap: 10,
          zIndex: 40,
        }}>
          {sections.map((_, i) => (
            <button key={i}
              onClick={() => jumpToSection(i)}
              aria-label={`Section ${i + 1}`}
              style={{
                width: i === activeSection ? 22 : 10,
                height: 10,
                borderRadius: Tokens.radius.pill,
                background: i === activeSection ? scheme.text : Tokens.inkFaint,
                border: 'none',
                cursor: 'pointer',
                transition: 'all 220ms',
              }}/>
          ))}
        </div>
      )}
    </SlideShell>
  );
}

// ── ContentHighlight: one section of the LongScroll template ──────────────
//   Animates in when its top enters the viewport. Renders big stat + thumb
//   cards (clickable, with #XX/label captions) + a paragraph below.
//
//   Two layouts:
//     • 'scatter' (default) — thumbs scattered around the stat at x/y coords
//     • 'row' — thumbs arranged as a horizontal grid below the stat block
//       (better for filling horizontal viewport space; ignores x/y on thumbs)
//
//   Thumbs sit on TOP (z-index) so their clicks land; the stat block has
//   pointer-events:none so clicks pass through to overlapping cards.
function ContentHighlight({ idx, section, scheme, scrollEl, onJump }) {
  const { stat, prefix, suffix, label, body, thumbs = [], layout = 'scatter' } = section;
  const ref = React.useRef(null);
  const [visible, setVisible] = React.useState(false);
  const [progress, setProgress] = React.useState(0);
  const [floatT, setFloatT] = React.useState(0);

  React.useEffect(() => {
    if (!ref.current) return;
    const obs = new IntersectionObserver(
      (entries) => {
        for (const e of entries) {
          if (e.isIntersecting) setVisible(true);
        }
      },
      { threshold: 0.15, root: scrollEl || null }
    );
    obs.observe(ref.current);
    return () => obs.disconnect();
  }, [scrollEl]);

  // Smooth ramp progress 0→1 after visible; keep ticking afterwards for
  // the idle thumb float and continuous bob.
  React.useEffect(() => {
    if (!visible) return;
    let raf, t0 = performance.now();
    const tick = () => {
      const elapsed = (performance.now() - t0) / 1000;
      setFloatT(elapsed);
      setProgress(Math.min(1, elapsed / 1.4));
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [visible]);

  const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
  const easeOutBack  = (t) => {
    const c1 = 1.70158, c3 = c1 + 1;
    return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
  };

  // Number animation — bouncy easeOutBack, scale 0.45→1, slide-up 80px
  const numScale   = 0.45 + 0.55 * easeOutBack(progress);
  const numOpacity = Math.min(1, progress * 1.6);
  const numTY      = (1 - easeOutCubic(progress)) * 80;

  // Reusable thumb-card renderer (animated, clickable)
  const renderThumb = (t, i, opts = {}) => {
    const { positionMode = 'absolute', cardW = t.w || 240, cardH = t.h || 300 } = opts;
    const delay = 0.18 + i * 0.10;
    const localP = Math.max(0, Math.min(1, (floatT - delay) / 0.85));
    const ease   = easeOutBack(localP);
    const op     = Math.min(1, localP * 1.4);
    const idleY  = localP >= 1 ? Math.sin(floatT * 0.7 + i * 1.3) * 4 : 0;
    const clickable = !!t.slideIdx;
    const handleClick = () => { if (clickable && onJump) onJump(t.slideIdx); };

    const baseStyle = positionMode === 'absolute' ? {
      position: 'absolute',
      left: `${(t.x ?? 0.5) * 100}%`,
      top: `${(t.y ?? 0.5) * 100}%`,
      transform: `translate(-50%, calc(-50% + ${idleY}px)) rotate(${(t.rotate || 0) * ease}deg) scale(${0.55 + 0.45 * ease})`,
    } : {
      position: 'relative',
      transform: `translateY(${idleY}px) rotate(${(t.rotate || 0) * ease * 0.4}deg) scale(${0.55 + 0.45 * ease})`,
    };

    return (
      <div key={i}
        className="thumb-card-outer"
        onClick={handleClick}
        style={{
          ...baseStyle,
          width: cardW, height: cardH,
          opacity: op * (t.opacity ?? 1),
          cursor: clickable ? 'pointer' : 'default',
          willChange: 'transform',
          flex: positionMode === 'row' ? '1 1 0' : 'none',
        }}
      >
        {/* Inner layer carries hover scale + outline (CSS-driven, doesn't
            fight the RAF transform on the outer layer) */}
        <div
          className="thumb-card-inner"
          style={{
            borderRadius: Tokens.radius.lg,
            overflow: 'hidden',
            boxShadow: localP >= 1
              ? '0 18px 50px rgba(0,0,0,0.55), 0 2px 6px rgba(0,0,0,0.35)'
              : '0 14px 40px rgba(0,0,0,0.55)',
            border: `1px solid ${Tokens.inkLine}`,
            filter: t.blur ? `blur(${t.blur}px)` : 'none',
            position: 'relative',
          }}
          onMouseEnter={(e) => {
            if (!clickable) return;
            e.currentTarget.style.boxShadow = `0 28px 80px rgba(0,0,0,0.75), 0 0 0 2px ${scheme.accent}`;
          }}
          onMouseLeave={(e) => {
            if (!clickable) return;
            e.currentTarget.style.boxShadow = '0 18px 50px rgba(0,0,0,0.55), 0 2px 6px rgba(0,0,0,0.35)';
          }}
        >
          <div style={{
            position: 'absolute', left: 0, right: 0, top: 0,
            height: cardH - 68,
            background: `url(${t.src}) center/cover no-repeat`,
          }}/>
          <div style={{
            position: 'absolute', left: 0, right: 0, bottom: 0,
            height: 68,
            background: 'linear-gradient(0deg, rgba(0,0,0,0.92) 0%, rgba(0,0,0,0.75) 100%)',
            padding: '10px 14px',
            display: 'flex', flexDirection: 'column', justifyContent: 'center',
            borderTop: `1px solid ${scheme.accent}44`,
          }}>
            {t.actionId && (
              <div style={{
                fontFamily: Tokens.fontMono, fontSize: 11,
                color: scheme.accent, letterSpacing: '0.20em',
                fontWeight: 700, marginBottom: 4,
              }}>{t.actionId}</div>
            )}
            {t.label && (
              <div style={{
                fontFamily: Tokens.fontDisplay, fontSize: 14,
                color: Tokens.ink, fontWeight: 700,
                letterSpacing: '-0.005em',
                lineHeight: 1.2,
                overflow: 'hidden',
                textOverflow: 'ellipsis',
                whiteSpace: 'nowrap',
              }}>{t.label}</div>
            )}
          </div>
        </div>
      </div>
    );
  };

  // ── ROW layout — used by the cover bucket sections to fill horizontal space ──
  if (layout === 'row') {
    return (
      <div ref={ref} data-section-idx={idx} style={{
        position: 'relative',
        margin: '120px auto 0',
        maxWidth: 1760,
        padding: '40px 60px 60px',
        minHeight: 800,
      }}>
        {/* Stat + label + body stacked at top */}
        <div style={{
          textAlign: 'center',
          opacity: numOpacity,
          transform: `translateY(${numTY}px) scale(${numScale})`,
          transformOrigin: 'center top',
          marginBottom: 48,
          willChange: 'transform, opacity',
        }}>
          <div style={{
            display: 'inline-flex', alignItems: 'baseline',
            fontFamily: Tokens.fontDisplay, fontWeight: Tokens.weight.black,
            color: scheme.accent, letterSpacing: '-0.05em', lineHeight: 0.9,
          }}>
            {prefix && <span style={{ fontSize: 56, marginRight: 8, fontWeight: Tokens.weight.bold }}>{prefix}</span>}
            <span style={{ fontSize: 160 }}>{stat}</span>
            {suffix && <span style={{ fontSize: 56, marginLeft: 8, fontWeight: Tokens.weight.bold }}>{suffix}</span>}
          </div>
          {label && (
            <div style={{
              marginTop: 12,
              fontFamily: Tokens.fontDisplay,
              fontSize: 26, fontWeight: Tokens.weight.semibold,
              color: scheme.text,
              letterSpacing: '0.01em',
              textTransform: 'uppercase',
            }}>{label}</div>
          )}
          {body && (
            <div style={{
              marginTop: 18,
              fontFamily: Tokens.fontDisplay,
              fontSize: 17, fontWeight: Tokens.weight.regular,
              color: scheme.textDim,
              lineHeight: 1.55,
              maxWidth: 900, margin: '18px auto 0',
            }}>{body}</div>
          )}
        </div>

        {/* Row of cards — fills horizontal space, equal flex */}
        <div style={{
          display: 'grid',
          gridTemplateColumns: `repeat(${thumbs.length}, 1fr)`,
          gap: 20,
          width: '100%',
        }}>
          {thumbs.map((t, i) => renderThumb(t, i, { positionMode: 'row', cardW: '100%', cardH: 320 }))}
        </div>
      </div>
    );
  }

  // ── SCATTER layout (default) — thumbs at absolute x/y around the stat ──
  return (
    <div ref={ref} data-section-idx={idx} style={{
      position: 'relative',
      margin: '160px auto 0',
      maxWidth: 1480,
      padding: '40px 80px 80px',
      minHeight: 600,
    }}>
      <div style={{ position: 'absolute', inset: 0, zIndex: 5 }}>
        {thumbs.map((t, i) => renderThumb(t, i, { positionMode: 'absolute' }))}
      </div>

      <div style={{
        position: 'relative',
        zIndex: 1,
        pointerEvents: 'none',
        textAlign: 'center',
        opacity: numOpacity,
        transform: `translateY(${numTY}px) scale(${numScale})`,
        transformOrigin: 'center',
        willChange: 'transform, opacity',
      }}>
        <div style={{
          display: 'inline-flex',
          alignItems: 'baseline',
          fontFamily: Tokens.fontDisplay,
          fontWeight: Tokens.weight.black,
          color: scheme.accent,
          letterSpacing: '-0.05em',
          lineHeight: 0.9,
        }}>
          {prefix && <span style={{ fontSize: 72, marginRight: 8, fontWeight: Tokens.weight.bold }}>{prefix}</span>}
          <span style={{ fontSize: 220 }}>{stat}</span>
          {suffix && <span style={{ fontSize: 72, marginLeft: 8, fontWeight: Tokens.weight.bold }}>{suffix}</span>}
        </div>
        {label && (
          <div style={{
            marginTop: 18,
            fontFamily: Tokens.fontDisplay,
            fontSize: 22, fontWeight: Tokens.weight.semibold,
            color: scheme.text,
            letterSpacing: '0em',
            textTransform: 'uppercase',
          }}>{label}</div>
        )}
        {body && (
          <div style={{
            marginTop: 24,
            fontFamily: Tokens.fontDisplay,
            fontSize: 17, fontWeight: Tokens.weight.regular,
            color: scheme.textDim,
            lineHeight: 1.55,
            maxWidth: 720, margin: '24px auto 0',
          }}>{body}</div>
        )}
      </div>
    </div>
  );
}

Object.assign(window, { TemplateLongScroll, ContentHighlight });
