// scenes.jsx — all scenes for the 30s ITDR reel.

// ────────────────────────────────────────────────────────────────────────────
// Shared palette / type
// ────────────────────────────────────────────────────────────────────────────
// Obsidian Ridge palette — black/gold/cream/ink, with restrained semantic
// extensions (warm muted red for threat, warm green for success) that sit
// comfortably against the gold.
const PAL = {
  bg:     '#000000',                 // obsidian-black — primary surface
  bg2:    '#0a0a0a',                 // slate-bg low
  surface:'#141414',                 // slate-bg high
  surface2:'#1c1c1c',
  line:   'rgba(214,164,95,0.10)',   // hairline in dim gold
  line2:  'rgba(214,164,95,0.22)',
  text:   '#FDFBF7',                 // cream
  textDim:'#A8A097',                 // warm mid
  textMute:'#6b6458',                // warm low
  gold:   '#D6A45F',                 // ridge-gold — THE brand color
  goldDim:'#8e6a3d',                 // dimmer brass
  goldHi: '#EFC98A',                 // specular highlight
  red:    'oklch(64% 0.16 35)',      // warm muted alert, harmonizes with gold
  redDim: 'oklch(40% 0.12 35)',
  green:  'oklch(68% 0.13 135)',     // warm sage, not neon
  amber:  '#EFC98A',                 // reuse gold highlight
};
const FONT = 'Inter, system-ui, sans-serif';
const MONO = '"JetBrains Mono", ui-monospace, SFMono-Regular, monospace';
const SERIF = '"Cinzel", "Trajan Pro", "Cormorant Garamond", serif';

// ────────────────────────────────────────────────────────────────────────────
// Obsidian Ridge flat mark — hexagonal gateway containing three peaks
// ────────────────────────────────────────────────────────────────────────────
function ObsidianMark({ size = 64, color = '#D6A45F', glow = 1 }) {
  // Hexagon points (flat-top), inset slightly
  const cx = 32, cy = 32;
  const hex = [
    [cx - 24, cy - 14],
    [cx,      cy - 28],
    [cx + 24, cy - 14],
    [cx + 24, cy + 14],
    [cx,      cy + 28],
    [cx - 24, cy + 14],
  ].map(p => p.join(',')).join(' ');
  return (
    <svg viewBox="0 0 64 64" width={size} height={size}
         style={{ display: 'block', overflow: 'visible' }}>
      <polygon points={hex}
               fill="none" stroke={color} strokeWidth="2"
               strokeLinejoin="miter" opacity={glow}/>
      {/* Three peaks inside */}
      <path d="M 12 42 L 22 26 L 28 34 L 34 22 L 42 34 L 48 30 L 52 42 Z"
            fill={color} opacity={0.95}/>
      {/* Specular highlight edge */}
      <path d="M 22 26 L 28 34 L 34 22"
            fill="none" stroke="#FDFBF7" strokeWidth="1" opacity="0.4"/>
    </svg>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Reusable: starfield / scan grid background (very subtle, always present)
// ────────────────────────────────────────────────────────────────────────────
function GlobalBackground() {
  const t = useTime();
  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: `
        radial-gradient(ellipse at 50% 0%, ${PAL.surface} 0%, ${PAL.bg} 65%),
        ${PAL.bg}
      `,
      overflow: 'hidden',
    }}>
      {/* Grid */}
      <div style={{
        position: 'absolute', inset: 0,
        backgroundImage: `
          linear-gradient(${PAL.line} 1px, transparent 1px),
          linear-gradient(90deg, ${PAL.line} 1px, transparent 1px)
        `,
        backgroundSize: '80px 80px',
        opacity: 0.5,
        transform: `translateY(${(t * 8) % 80}px)`,
      }}/>
      {/* Vignette */}
      <div style={{
        position: 'absolute', inset: 0,
        background: 'radial-gradient(ellipse at center, transparent 35%, rgba(0,0,0,0.55) 100%)',
        pointerEvents: 'none',
      }}/>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Scene 1 — Clock to 02:47 AM (0 → 3s)
// ────────────────────────────────────────────────────────────────────────────
function SceneClock() {
  const { localTime, progress, duration } = useSprite();

  // Time counts from 02:43 up to 02:47:00 then holds
  const t = clamp(localTime / 3.0, 0, 1);
  const baseSec = 2 * 3600 + 43 * 60; // 02:43
  const targetSec = 2 * 3600 + 47 * 60; // 02:47
  const cur = baseSec + (targetSec - baseSec) * Easing.easeOutCubic(t);
  const totalMin = Math.floor(cur / 60);
  const sec = Math.floor(cur % 60);

  const titleAppear = clamp((localTime - 1.3) / 0.6, 0, 1);
  const subAppear = clamp((localTime - 1.7) / 0.6, 0, 1);

  // Exit fade
  const exit = clamp((localTime - (duration - 0.4)) / 0.4, 0, 1);
  const opacity = 1 - exit;

  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', flexDirection: 'column',
      alignItems: 'center', justifyContent: 'center',
      opacity,
    }}>
      {/* Pulsing dot */}
      <div style={{
        width: 14, height: 14, borderRadius: 999,
        background: PAL.gold,
        boxShadow: `0 0 24px ${PAL.gold}, 0 0 60px ${PAL.goldDim}`,
        marginBottom: 28,
        opacity: 0.85 + 0.15 * Math.sin(localTime * 6),
        transform: `scale(${1 + 0.15 * Math.sin(localTime * 6)})`,
      }}/>

      {/* Time */}
      <div style={{
        fontFamily: MONO,
        fontSize: 220,
        fontWeight: 500,
        letterSpacing: '-0.04em',
        color: PAL.text,
        fontVariantNumeric: 'tabular-nums',
        textShadow: `0 4px 40px ${PAL.goldDim}40`,
        lineHeight: 1,
      }}>
        {String(Math.floor(totalMin / 60)).padStart(2, '0')}
        <span style={{ opacity: 0.4 + 0.6 * Math.abs(Math.sin(localTime * Math.PI)) }}>:</span>
        {String(totalMin % 60).padStart(2, '0')}
        <span style={{ fontSize: 80, color: PAL.textDim, marginLeft: 16, letterSpacing: '0.1em' }}>
          {String(sec).padStart(2, '0')}
        </span>
      </div>

      {/* AM + day */}
      <div style={{
        marginTop: 16,
        fontFamily: MONO,
        fontSize: 22,
        color: PAL.textDim,
        letterSpacing: '0.4em',
        textTransform: 'uppercase',
        opacity: titleAppear,
        transform: `translateY(${(1 - titleAppear) * 12}px)`,
      }}>
        AM &nbsp;·&nbsp; FRIDAY &nbsp;·&nbsp; HOLIDAY WEEKEND
      </div>

      {/* Sub */}
      <div style={{
        marginTop: 50,
        fontFamily: FONT,
        fontSize: 28,
        color: PAL.textMute,
        letterSpacing: '0.02em',
        opacity: subAppear,
        transform: `translateY(${(1 - subAppear) * 12}px)`,
      }}>
        80 employees asleep. Payroll runs Monday.
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Scene 2 — Suspicious login from overseas (3 → 6.5s)
// ────────────────────────────────────────────────────────────────────────────
function SceneLogin() {
  const { localTime, duration } = useSprite();

  // Two nodes: HQ (right) and attacker (left)
  const hq = { x: 1380, y: 560 };       // US east coast-ish
  const atk = { x: 360, y: 380 };       // somewhere overseas

  // Line traces from attacker to HQ
  const trace = clamp((localTime - 0.4) / 1.2, 0, 1);
  const ex = atk.x + (hq.x - atk.x) * Easing.easeInOutCubic(trace);
  const ey = atk.y + (hq.y - atk.y) * Easing.easeInOutCubic(trace);

  // Sign-in card appears once trace hits HQ
  const cardAppear = clamp((localTime - 1.5) / 0.5, 0, 1);
  const flagAppear = clamp((localTime - 2.1) / 0.4, 0, 1);

  const exit = clamp((localTime - (duration - 0.4)) / 0.4, 0, 1);
  const opacity = 1 - exit;

  return (
    <div style={{ position: 'absolute', inset: 0, opacity }}>
      {/* Map dots */}
      <MapDots />

      {/* Attacker location */}
      <Pulse x={atk.x} y={atk.y} color={PAL.red} delay={0}/>
      <div style={{
        position: 'absolute', left: atk.x + 22, top: atk.y - 30,
        fontFamily: MONO, fontSize: 16, color: PAL.red, letterSpacing: '0.08em',
      }}>
        185.220.•••.••
        <div style={{ fontSize: 12, color: PAL.textMute, marginTop: 2 }}>
          UNKNOWN ORIGIN
        </div>
      </div>

      {/* Trace line */}
      <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}>
        <line x1={atk.x} y1={atk.y} x2={ex} y2={ey}
              stroke={PAL.red} strokeWidth="2" strokeDasharray="6 6"
              opacity="0.85"/>
      </svg>

      {/* HQ location */}
      <Pulse x={hq.x} y={hq.y} color={trace >= 1 ? PAL.red : PAL.gold} delay={0}/>
      <div style={{
        position: 'absolute', left: hq.x - 240, top: hq.y + 22,
        fontFamily: MONO, fontSize: 14, color: PAL.textDim, letterSpacing: '0.1em',
        textAlign: 'right', width: 220,
      }}>
        HQ — TENANT
      </div>

      {/* Sign-in card */}
      <div style={{
        position: 'absolute',
        left: 240, top: 120,
        width: 880,
        background: PAL.surface,
        border: `1px solid ${PAL.line2}`,
        borderLeft: `3px solid ${PAL.red}`,
        borderRadius: 10,
        padding: '24px 28px',
        opacity: cardAppear,
        transform: `translateY(${(1 - cardAppear) * 16}px)`,
        boxShadow: '0 30px 80px rgba(0,0,0,0.5)',
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14 }}>
          <div style={{ width: 8, height: 8, borderRadius: 999, background: PAL.red,
                        boxShadow: `0 0 12px ${PAL.red}`}}/>
          <div style={{ fontFamily: MONO, fontSize: 13, color: PAL.red,
                        letterSpacing: '0.18em', textTransform: 'uppercase' }}>
            Sign-in · Success
          </div>
        </div>
        <div style={{ fontFamily: FONT, fontSize: 36, color: PAL.text,
                      fontWeight: 600, letterSpacing: '-0.01em' }}>
          finance.director@acme-health.com
        </div>
        <div style={{ marginTop: 18, display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 18 }}>
          <KV label="MFA" value="PASSED" valueColor={PAL.amber}/>
          <KV label="METHOD" value="SESSION TOKEN"/>
          <KV label="ORIGIN" value="OFF-CONTINENT" valueColor={PAL.red}/>
        </div>
      </div>

      {/* The "flag" — impossible travel (placed lower-center, away from IP label) */}
      <div style={{
        position: 'absolute', left: '50%', top: 820,
        transform: `translate(-50%, ${(1 - flagAppear) * 10}px)`,
        background: 'transparent',
        opacity: flagAppear,
      }}>
        <div style={{
          fontFamily: MONO, fontSize: 15, color: PAL.red, letterSpacing: '0.2em',
          textTransform: 'uppercase',
          padding: '6px 12px',
          border: `1px solid ${PAL.red}`,
          borderRadius: 4,
          display: 'inline-block',
        }}>
          ⚠ Impossible Travel · 6,200 km in 4 min
        </div>
      </div>
    </div>
  );
}

function KV({ label, value, valueColor = PAL.text }) {
  return (
    <div>
      <div style={{ fontFamily: MONO, fontSize: 11, color: PAL.textMute,
                    letterSpacing: '0.18em', textTransform: 'uppercase' }}>{label}</div>
      <div style={{ fontFamily: MONO, fontSize: 18, color: valueColor, marginTop: 4,
                    letterSpacing: '0.04em' }}>{value}</div>
    </div>
  );
}

function Pulse({ x, y, color, delay = 0, size = 14 }) {
  const t = useTime();
  const phase = ((t - delay) % 2) / 2;
  return (
    <>
      <div style={{
        position: 'absolute', left: x, top: y,
        width: size, height: size,
        marginLeft: -size/2, marginTop: -size/2,
        borderRadius: 999,
        background: color,
        boxShadow: `0 0 24px ${color}`,
        zIndex: 2,
      }}/>
      <div style={{
        position: 'absolute', left: x, top: y,
        width: size, height: size,
        marginLeft: -size/2, marginTop: -size/2,
        borderRadius: 999,
        border: `2px solid ${color}`,
        opacity: 1 - phase,
        transform: `scale(${1 + phase * 4})`,
        pointerEvents: 'none',
      }}/>
    </>
  );
}

function MapDots() {
  // Static decorative dot grid suggesting a world map silhouette.
  // Lightweight: pre-computed dot positions.
  const dots = React.useMemo(() => {
    const arr = [];
    // Just a wide dot field – not a literal map.
    for (let r = 0; r < 14; r++) {
      for (let c = 0; c < 28; c++) {
        // Carve a vague continental shape with a noisy mask
        const cx = c * 70 + 40;
        const cy = r * 60 + 100;
        const seed = (Math.sin(c * 12.9898 + r * 78.233) * 43758.5453) % 1;
        const v = Math.abs(seed);
        if (v < 0.55) arr.push({ x: cx, y: cy, a: 0.05 + v * 0.35 });
      }
    }
    return arr;
  }, []);
  return (
    <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}>
      {dots.map((d, i) => (
        <circle key={i} cx={d.x} cy={d.y} r="2" fill={PAL.goldDim} opacity={d.a}/>
      ))}
    </svg>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Scene 3 — Malicious inbox rule auto-types (6.5 → 10s)
// ────────────────────────────────────────────────────────────────────────────
function SceneInboxRule() {
  const { localTime, duration } = useSprite();

  const exit = clamp((localTime - (duration - 0.4)) / 0.4, 0, 1);
  const opacity = 1 - exit;

  // Cards appear in sequence
  const titleAppear = clamp(localTime / 0.4, 0, 1);

  const cond = "subject contains 'wire' OR 'ACH' OR 'routing'";
  const act1 = "→ FORWARD to attacker@protonm***.•••";
  const act2 = "→ DELETE from inbox";

  const typeStart = 0.4;
  const typeDur = 1.6;
  const typeProg = clamp((localTime - typeStart) / typeDur, 0, 1);

  // Split typing across the three lines
  const totalChars = cond.length + act1.length + act2.length;
  const reveal = Math.floor(totalChars * typeProg);
  let s1 = cond.slice(0, Math.min(reveal, cond.length));
  let s2 = reveal > cond.length ? act1.slice(0, Math.min(reveal - cond.length, act1.length)) : '';
  let s3 = reveal > cond.length + act1.length ? act2.slice(0, reveal - cond.length - act1.length) : '';

  const stampAppear = clamp((localTime - 2.4) / 0.4, 0, 1);
  const stampScale = 0.7 + 0.3 * Easing.easeOutBack(stampAppear);

  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      opacity,
    }}>
      <div style={{
        width: 1400,
        background: PAL.surface,
        border: `1px solid ${PAL.line2}`,
        borderRadius: 14,
        boxShadow: '0 40px 100px rgba(0,0,0,0.55)',
        overflow: 'hidden',
      }}>
        {/* Header */}
        <div style={{
          padding: '20px 28px',
          borderBottom: `1px solid ${PAL.line}`,
          display: 'flex', alignItems: 'center', gap: 14,
          opacity: titleAppear,
        }}>
          <div style={{
            width: 28, height: 28, borderRadius: 6,
            background: 'rgba(232,80,80,0.12)',
            border: `1px solid ${PAL.red}`,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            color: PAL.red, fontFamily: MONO, fontSize: 16,
          }}>!</div>
          <div style={{ fontFamily: FONT, fontSize: 22, color: PAL.text, fontWeight: 600 }}>
            New inbox rule created
          </div>
          <div style={{ marginLeft: 'auto', fontFamily: MONO, fontSize: 13, color: PAL.textMute }}>
            00:00:03 after sign-in
          </div>
        </div>

        {/* Body */}
        <div style={{ padding: '36px 40px 44px', fontFamily: MONO, fontSize: 26,
                      lineHeight: 1.6, color: PAL.text }}>
          <div style={{ color: PAL.textDim, fontSize: 14, letterSpacing: '0.2em',
                        textTransform: 'uppercase', marginBottom: 12 }}>
            IF
          </div>
          <div style={{ color: PAL.amber }}>
            {s1}{typeProg < cond.length / totalChars ? <Caret/> : null}
          </div>

          <div style={{ color: PAL.textDim, fontSize: 14, letterSpacing: '0.2em',
                        textTransform: 'uppercase', marginTop: 24, marginBottom: 12 }}>
            THEN
          </div>
          <div style={{ color: PAL.red }}>
            {s2}{reveal > cond.length && typeProg < (cond.length + act1.length) / totalChars ? <Caret/> : null}
          </div>
          <div style={{ color: PAL.red, marginTop: 10 }}>
            {s3}{reveal > cond.length + act1.length && typeProg < 1 ? <Caret/> : null}
          </div>
        </div>

        {/* Stamp */}
        <div style={{
          padding: '18px 28px',
          borderTop: `1px solid ${PAL.line}`,
          background: 'rgba(232,80,80,0.06)',
          display: 'flex', alignItems: 'center', gap: 14,
          opacity: stampAppear,
        }}>
          <div style={{
            fontFamily: MONO, fontSize: 14, color: PAL.red,
            letterSpacing: '0.25em', textTransform: 'uppercase',
            transform: `scale(${stampScale})`, transformOrigin: 'left center',
          }}>
            ✕ Hide all matches from user
          </div>
          <div style={{ marginLeft: 'auto', fontFamily: FONT, fontSize: 15,
                        color: PAL.textDim }}>
            Goal: own the conversation, invisibly.
          </div>
        </div>
      </div>
    </div>
  );
}

function Caret() {
  const t = useTime();
  return (
    <span style={{
      display: 'inline-block',
      width: '0.5em', height: '1em',
      background: PAL.amber, marginLeft: 4,
      verticalAlign: 'middle',
      opacity: Math.floor(t * 3) % 2 === 0 ? 1 : 0,
    }}/>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Scene 4 — Fraudulent wire emails fly out + $96,000 counter (10 → 13.5s)
// ────────────────────────────────────────────────────────────────────────────
function SceneWireEmails() {
  const { localTime, duration } = useSprite();

  const exit = clamp((localTime - (duration - 0.4)) / 0.4, 0, 1);
  const opacity = 1 - exit;

  // Email 1 flies up-left, Email 2 flies up-right
  const e1 = clamp((localTime - 0.2) / 0.9, 0, 1);
  const e2 = clamp((localTime - 0.7) / 0.9, 0, 1);

  const e1x = -300 + 300 * Easing.easeOutCubic(e1);
  const e1y = 300 - 320 * Easing.easeOutCubic(e1);

  const e2x = 300 - 300 * Easing.easeOutCubic(e2);
  const e2y = 300 - 320 * Easing.easeOutCubic(e2);

  // Counter ticks 0 → 96000
  const cStart = 1.1;
  const cDur = 1.4;
  const cP = clamp((localTime - cStart) / cDur, 0, 1);
  const value = Math.floor(96000 * Easing.easeOutCubic(cP));

  const labelAppear = clamp((localTime - 1.0) / 0.4, 0, 1);

  return (
    <div style={{ position: 'absolute', inset: 0, opacity }}>
      {/* Sender mailbox at bottom-center */}
      <div style={{
        position: 'absolute', left: '50%', bottom: 80,
        transform: 'translateX(-50%)',
        fontFamily: MONO, fontSize: 18, color: PAL.textDim,
        letterSpacing: '0.1em',
        textAlign: 'center',
      }}>
        <div style={{
          width: 18, height: 18, borderRadius: 999, background: PAL.red,
          boxShadow: `0 0 24px ${PAL.red}`,
          margin: '0 auto 12px',
        }}/>
        FINANCE DIRECTOR · OUTBOX
      </div>

      {/* Two flying emails */}
      <FlyingEmail
        baseX={760} baseY={520}
        dx={-380 + e1x} dy={-100 + e1y * -1}
        scale={0.7 + e1 * 0.3}
        opacity={e1}
        vendor="VENDOR A"
        amount="$48,200"
      />
      <FlyingEmail
        baseX={1100} baseY={520}
        dx={380 + e2x * -1} dy={-100 + e2y * -1}
        scale={0.7 + e2 * 0.3}
        opacity={e2}
        vendor="VENDOR B"
        amount="$47,800"
        flipped
      />

      {/* Big counter */}
      <div style={{
        position: 'absolute', left: '50%', top: 100,
        transform: 'translateX(-50%)',
        textAlign: 'center',
      }}>
        <div style={{
          fontFamily: MONO, fontSize: 16, color: PAL.red,
          letterSpacing: '0.3em', textTransform: 'uppercase',
          opacity: labelAppear,
        }}>
          Outgoing wire fraud · staged
        </div>
        <div style={{
          marginTop: 18,
          fontFamily: MONO, fontSize: 200, fontWeight: 600,
          color: PAL.red,
          letterSpacing: '-0.04em',
          lineHeight: 1,
          textShadow: '0 0 60px rgba(232,80,80,0.45)',
          fontVariantNumeric: 'tabular-nums',
        }}>
          ${value.toLocaleString()}
        </div>
        <div style={{
          marginTop: 14,
          fontFamily: FONT, fontSize: 22, color: PAL.textDim,
          opacity: labelAppear,
        }}>
          24–48 hours from being paid
        </div>
      </div>
    </div>
  );
}

function FlyingEmail({ baseX, baseY, dx, dy, scale, opacity, vendor, amount, flipped }) {
  return (
    <div style={{
      position: 'absolute',
      left: baseX + dx, top: baseY + dy,
      transform: `scale(${scale}) rotate(${flipped ? 4 : -4}deg)`,
      transformOrigin: 'center',
      opacity,
      width: 360,
      background: PAL.surface,
      border: `1px solid ${PAL.line2}`,
      borderTop: `3px solid ${PAL.red}`,
      borderRadius: 10,
      padding: 18,
      boxShadow: '0 30px 60px rgba(0,0,0,0.5)',
      fontFamily: FONT,
    }}>
      <div style={{ fontFamily: MONO, fontSize: 11, color: PAL.textMute,
                    letterSpacing: '0.2em', textTransform: 'uppercase' }}>
        To · {vendor}
      </div>
      <div style={{ fontSize: 20, color: PAL.text, fontWeight: 600, marginTop: 8,
                    letterSpacing: '-0.01em' }}>
        Updated wire instructions
      </div>
      <div style={{ fontSize: 13, color: PAL.textDim, marginTop: 6 }}>
        Hi team — please use the attached banking details for tomorrow's transfer…
      </div>
      <div style={{ marginTop: 14, display: 'flex', alignItems: 'center',
                    justifyContent: 'space-between' }}>
        <div style={{
          fontFamily: MONO, fontSize: 12, color: PAL.red,
          padding: '4px 8px', border: `1px solid ${PAL.red}`, borderRadius: 4,
        }}>
          📎 routing-update.pdf
        </div>
        <div style={{ fontFamily: MONO, fontSize: 16, color: PAL.red }}>
          {amount}
        </div>
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Scene 5 — Alert detonates (13.5 → 16.5s)
// ────────────────────────────────────────────────────────────────────────────
function SceneAlert() {
  const { localTime, duration } = useSprite();
  const exit = clamp((localTime - (duration - 0.4)) / 0.4, 0, 1);
  const opacity = 1 - exit;

  const flash = clamp(localTime / 0.25, 0, 1);
  const cardAppear = clamp((localTime - 0.2) / 0.4, 0, 1);
  const shake = 0;

  // Strobing red overlay decays
  const strobe = Math.max(0, 1 - localTime / 0.6) * (0.5 + 0.5 * Math.sin(localTime * 30));

  return (
    <div style={{ position: 'absolute', inset: 0, opacity }}>
      {/* Red flash */}
      <div style={{
        position: 'absolute', inset: 0,
        background: PAL.red, opacity: strobe * 0.35,
        pointerEvents: 'none',
      }}/>

      <div style={{
        position: 'absolute', inset: 0,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        transform: `translateX(${shake}px)`,
      }}>
        <div style={{
          width: 1280,
          background: PAL.surface,
          border: `2px solid ${PAL.red}`,
          borderRadius: 14,
          padding: '40px 56px',
          boxShadow: `0 0 0 6px rgba(232,80,80,0.18), 0 0 80px rgba(232,80,80,0.4)`,
          opacity: cardAppear,
          transform: `scale(${0.92 + 0.08 * Easing.easeOutBack(cardAppear)})`,
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 18, marginBottom: 18 }}>
            <div style={{
              width: 56, height: 56, borderRadius: 999,
              background: PAL.red,
              boxShadow: `0 0 30px ${PAL.red}`,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontFamily: FONT, fontSize: 32, fontWeight: 700, color: '#0a0e1a',
            }}>
              !
            </div>
            <div>
              <div style={{ fontFamily: MONO, fontSize: 14, color: PAL.red,
                            letterSpacing: '0.3em', textTransform: 'uppercase' }}>
                ITDR · Managed Detection
              </div>
              <div style={{ fontFamily: FONT, fontSize: 44, color: PAL.text,
                            fontWeight: 700, letterSpacing: '-0.02em', marginTop: 6 }}>
                Session hijack detected
              </div>
            </div>
            <div style={{ marginLeft: 'auto', textAlign: 'right',
                          fontFamily: MONO, fontSize: 14, color: PAL.textDim }}>
              02:47:09 EST
              <div style={{ color: PAL.green, marginTop: 6, fontSize: 13 }}>
                SOC ON-CALL ENGAGED
              </div>
            </div>
          </div>

          <div style={{
            display: 'grid', gridTemplateColumns: '1fr 1fr 1fr',
            gap: 24, marginTop: 18, paddingTop: 22,
            borderTop: `1px solid ${PAL.line}`,
          }}>
            <SmallSig label="ANOMALOUS LOGIN" detail="Off-continent IP, replayed token"/>
            <SmallSig label="POLICY EVASION" detail="External forward rule created"/>
            <SmallSig label="EXFIL STAGED" detail="Wire emails in Sent items"/>
          </div>
        </div>
      </div>
    </div>
  );
}

function SmallSig({ label, detail }) {
  return (
    <div>
      <div style={{ fontFamily: MONO, fontSize: 12, color: PAL.red,
                    letterSpacing: '0.2em', textTransform: 'uppercase' }}>{label}</div>
      <div style={{ fontFamily: FONT, fontSize: 17, color: PAL.text, marginTop: 6 }}>{detail}</div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Scene 6 — Response checklist (16.5 → 20.5s)
// ────────────────────────────────────────────────────────────────────────────
function SceneResponse() {
  const { localTime, duration } = useSprite();
  const exit = clamp((localTime - (duration - 0.4)) / 0.4, 0, 1);
  const opacity = 1 - exit;

  const items = [
    { t: 0.1, label: 'Session isolated',         meta: '02:47:11' },
    { t: 0.6, label: 'All active tokens revoked', meta: '02:47:24' },
    { t: 1.1, label: 'Passwords rotated · 4 accts', meta: '02:51:08' },
    { t: 1.6, label: 'Malicious rules removed · 4 total', meta: '02:58:42' },
    { t: 2.1, label: 'Vendors called · before banks open', meta: '06:14:00' },
    { t: 2.6, label: 'Insurance notified · 12h · 60h to spare', meta: '06:42:00' },
  ];

  const titleAppear = clamp(localTime / 0.4, 0, 1);

  return (
    <div style={{ position: 'absolute', inset: 0, opacity,
                  display: 'flex', flexDirection: 'column',
                  alignItems: 'center', justifyContent: 'center', padding: '0 80px' }}>
      <div style={{
        fontFamily: MONO, fontSize: 18, color: PAL.gold,
        letterSpacing: '0.3em', textTransform: 'uppercase',
        opacity: titleAppear, marginBottom: 14,
      }}>
        24/7 SOC · Human-verified · Before anyone woke up
      </div>
      <div style={{
        fontFamily: SERIF, fontSize: 56, color: PAL.text,
        fontWeight: 500, letterSpacing: '0.02em',
        opacity: titleAppear,
        marginBottom: 48,
      }}>
        The team did this.
      </div>

      <div style={{
        width: 1280,
        display: 'flex', flexDirection: 'column', gap: 14,
      }}>
        {items.map((it, i) => {
          const appear = clamp((localTime - it.t) / 0.35, 0, 1);
          const checkP = clamp((localTime - it.t - 0.2) / 0.4, 0, 1);
          return (
            <div key={i} style={{
              display: 'flex', alignItems: 'center', gap: 22,
              padding: '18px 26px',
              background: PAL.surface,
              border: `1px solid ${PAL.line2}`,
              borderLeft: `3px solid ${PAL.green}`,
              borderRadius: 10,
              opacity: appear,
              transform: `translateX(${(1 - appear) * 40}px)`,
            }}>
              <div style={{
                width: 32, height: 32, borderRadius: 8,
                border: `2px solid ${PAL.green}`,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                background: checkP > 0.5 ? PAL.green : 'transparent',
                transition: 'background 200ms',
              }}>
                <svg width="18" height="18" viewBox="0 0 18 18">
                  <path d="M3 9.5 L7 13 L15 5"
                        stroke={checkP > 0.5 ? '#0a0e1a' : PAL.green}
                        strokeWidth="2.5" fill="none"
                        strokeDasharray="20"
                        strokeDashoffset={20 - 20 * checkP}/>
                </svg>
              </div>
              <div style={{ fontFamily: FONT, fontSize: 28, color: PAL.text,
                            fontWeight: 500, letterSpacing: '-0.005em' }}>
                {it.label}
              </div>
              <div style={{ marginLeft: 'auto', fontFamily: MONO, fontSize: 14,
                            color: PAL.textMute, letterSpacing: '0.1em' }}>
                {it.meta}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Scene 7 — "$96,000 BLOCKED" stamp (20.5 → 23.5s)
// ────────────────────────────────────────────────────────────────────────────
function SceneBlocked() {
  const { localTime, duration } = useSprite();
  const exit = clamp((localTime - (duration - 0.4)) / 0.4, 0, 1);
  const opacity = 1 - exit;

  // Stamp slams in with overshoot
  const slam = Easing.easeOutBack(clamp(localTime / 0.4, 0, 1));
  const stampScale = 0.4 + 0.6 * slam;
  const stampRot = -8 + 4 * slam;

  const labelAppear = clamp((localTime - 0.6) / 0.5, 0, 1);
  const subAppear = clamp((localTime - 1.4) / 0.5, 0, 1);

  // Shake on impact
  const shake = 0;

  return (
    <div style={{ position: 'absolute', inset: 0, opacity,
                  display: 'flex', flexDirection: 'column',
                  alignItems: 'center', justifyContent: 'center',
                  transform: `translate(${shake}px, ${shake * 0.5}px)` }}>

      <div style={{
        fontFamily: MONO, fontSize: 20, color: PAL.green,
        letterSpacing: '0.3em', textTransform: 'uppercase',
        opacity: labelAppear,
        marginBottom: 10,
      }}>
        Wire fraud · blocked
      </div>

      {/* Crossed-out number */}
      <div style={{
        position: 'relative',
        transform: `scale(${stampScale}) rotate(${stampRot}deg)`,
        transformOrigin: 'center',
      }}>
        <div style={{
          fontFamily: MONO, fontSize: 280, fontWeight: 700,
          color: PAL.green,
          letterSpacing: '-0.04em', lineHeight: 1,
          textShadow: '0 0 60px rgba(80,220,140,0.45)',
          fontVariantNumeric: 'tabular-nums',
        }}>
          $96,000
        </div>
        {/* Strike */}
        <div style={{
          position: 'absolute', left: -20, right: -20,
          top: '52%',
          height: 14,
          background: PAL.green,
          transform: `scaleX(${clamp((localTime - 0.45) / 0.35, 0, 1)})`,
          transformOrigin: 'left center',
          borderRadius: 8,
          boxShadow: `0 0 24px ${PAL.green}`,
        }}/>
      </div>

      <div style={{
        marginTop: 30,
        fontFamily: FONT, fontSize: 30, color: PAL.text,
        opacity: subAppear, fontWeight: 500,
        letterSpacing: '-0.01em',
      }}>
        Both vendors called before they opened the email.
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Scene 8 — The lesson: MFA was on, it didn't matter (23.5 → 26.5s)
// ────────────────────────────────────────────────────────────────────────────
function SceneLesson() {
  const { localTime, duration } = useSprite();
  const exit = clamp((localTime - (duration - 0.4)) / 0.4, 0, 1);
  const opacity = 1 - exit;

  const a1 = clamp((localTime - 0.0) / 0.5, 0, 1);
  const strikeP = clamp((localTime - 0.55) / 0.5, 0, 1);
  const a2 = clamp((localTime - 1.1) / 0.6, 0, 1);
  const a3 = clamp((localTime - 1.9) / 0.6, 0, 1);

  return (
    <div style={{ position: 'absolute', inset: 0, opacity,
                  display: 'flex', flexDirection: 'column',
                  alignItems: 'center', justifyContent: 'center', gap: 28 }}>
      <div style={{
        position: 'relative',
        fontFamily: FONT, fontSize: 96, fontWeight: 700,
        color: PAL.text, letterSpacing: '-0.03em',
        opacity: a1,
        transform: `translateY(${(1 - a1) * 12}px)`,
      }}>
        MFA was on.
        <div style={{
          position: 'absolute', left: -10, right: -10,
          top: '52%',
          height: 8,
          background: PAL.red,
          transform: `scaleX(${strikeP})`,
          transformOrigin: 'left center',
          borderRadius: 6,
        }}/>
      </div>

      <div style={{
        fontFamily: FONT, fontSize: 64, fontWeight: 600,
        color: PAL.red, letterSpacing: '-0.02em',
        opacity: a2,
        transform: `translateY(${(1 - a2) * 12}px)`,
      }}>
        It didn't matter.
      </div>

      <div style={{
        marginTop: 12,
        maxWidth: 1400, textAlign: 'center',
        fontFamily: FONT, fontSize: 32, color: PAL.textDim, lineHeight: 1.4,
        opacity: a3,
        transform: `translateY(${(1 - a3) * 8}px)`,
      }}>
        Modern phishing kits steal <span style={{ color: PAL.amber }}>session tokens</span>,
        not passwords. <br/>
        Detection has to live on session <span style={{ color: PAL.amber }}>behavior</span> —
        not credentials.
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Scene 9 — CTA (26.5 → 30s)
// ────────────────────────────────────────────────────────────────────────────
function SceneCTA() {
  const { localTime, duration } = useSprite();
  const exit = clamp((localTime - (duration - 0.4)) / 0.4, 0, 1);
  const opacity = 1 - exit;

  const a1 = clamp(localTime / 0.5, 0, 1);
  const a2 = clamp((localTime - 0.5) / 0.5, 0, 1);
  const a3 = clamp((localTime - 1.2) / 0.5, 0, 1);
  const a4 = clamp((localTime - 1.9) / 0.5, 0, 1);

  // Pulsing dot
  const pulse = 0.85 + 0.15 * Math.sin(localTime * 4);

  return (
    <div style={{ position: 'absolute', inset: 0, opacity,
                  display: 'flex', flexDirection: 'column',
                  alignItems: 'center', justifyContent: 'center' }}>
      {/* Mark — hexagon gateway + three peaks (Obsidian Ridge flat mark) */}
      <div style={{
        width: 140, height: 140,
        opacity: a1, transform: `scale(${0.7 + 0.3 * Easing.easeOutBack(a1)})`,
        marginBottom: 36, position: 'relative',
        filter: `drop-shadow(0 0 24px ${PAL.goldDim})`,
      }}>
        <ObsidianMark size={140} color={PAL.gold} glow={pulse}/>
      </div>

      <div style={{
        fontFamily: SERIF, fontSize: 36, color: PAL.gold,
        letterSpacing: '0.35em', textTransform: 'uppercase',
        fontWeight: 500,
        opacity: a1,
        marginBottom: 40,
      }}>
        Obsidian&nbsp;&nbsp;Ridge
      </div>

      <div style={{
        fontFamily: FONT, fontSize: 96, fontWeight: 700,
        color: PAL.text, letterSpacing: '-0.03em',
        opacity: a2,
        transform: `translateY(${(1 - a2) * 14}px)`,
        textAlign: 'center', lineHeight: 1.05,
      }}>
        While you sleep, <br/>
        <span style={{ color: PAL.gold, fontFamily: SERIF, fontWeight: 500,
                       letterSpacing: '0em' }}>we're watching.</span>
      </div>

      <div style={{
        marginTop: 28,
        fontFamily: FONT, fontSize: 26, color: PAL.textDim,
        opacity: a3, textAlign: 'center', maxWidth: 1100,
        transform: `translateY(${(1 - a3) * 8}px)`,
        lineHeight: 1.4,
      }}>
        Managed Identity Threat Detection &amp; Response for SMBs. <br/>
        24/7 SOC · Containment in seconds · Microsoft 365 native · Human-verified alerts
      </div>

      <div style={{
        marginTop: 56,
        display: 'flex', alignItems: 'center', gap: 18,
        padding: '20px 36px',
        border: `1px solid ${PAL.gold}`,
        borderRadius: 999,
        background: 'rgba(214,164,95,0.05)',
        opacity: a4,
        transform: `translateY(${(1 - a4) * 8}px)`,
        boxShadow: `0 0 40px rgba(214,164,95,0.18)`,
      }}>
        <div style={{
          width: 10, height: 10, borderRadius: 999,
          background: PAL.gold,
          boxShadow: `0 0 16px ${PAL.gold}`,
          opacity: pulse,
        }}/>
        <div style={{
          fontFamily: MONO, fontSize: 28, color: PAL.text,
          letterSpacing: '0.08em',
        }}>
          obsidianridge.io
        </div>
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────────────────────
// Top-level reel: glue scenes together
// ────────────────────────────────────────────────────────────────────────────
function Reel() {
  const t = useTime();

  // Update data-screen-label every second so comments include timestamp
  React.useEffect(() => {
    const root = document.querySelector('[data-reel-root]');
    if (!root) return;
    root.setAttribute('data-screen-label', `t=${Math.floor(t)}s`);
  }, [Math.floor(t)]);

  // Slight global ken-burns push that resets between scenes for energy
  const beats = [0, 4.5, 9.75, 15, 20.25, 24.75, 30.75, 35.25, 39.75, 45];
  let activeBeat = 0;
  for (let i = 0; i < beats.length - 1; i++) {
    if (t >= beats[i] && t < beats[i + 1]) { activeBeat = i; break; }
  }
  const beatLocal = (t - beats[activeBeat]) / (beats[activeBeat + 1] - beats[activeBeat]);
  const cameraScale = 1 + 0.03 * Easing.easeOutQuad(beatLocal);

  return (
    <div data-reel-root style={{ position: 'absolute', inset: 0, overflow: 'hidden' }}>
      <GlobalBackground/>
      <div style={{
        position: 'absolute', inset: 0,
        transform: `scale(${cameraScale})`,
        transformOrigin: 'center',
      }}>
        <Sprite start={0}     end={4.5}>   <SceneClock/>      </Sprite>
        <Sprite start={4.5}   end={9.75}>  <SceneLogin/>      </Sprite>
        <Sprite start={9.75}  end={15.0}>  <SceneInboxRule/>  </Sprite>
        <Sprite start={15.0}  end={20.25}> <SceneWireEmails/> </Sprite>
        <Sprite start={20.25} end={24.75}> <SceneAlert/>      </Sprite>
        <Sprite start={24.75} end={30.75}> <SceneResponse/>   </Sprite>
        <Sprite start={30.75} end={35.25}> <SceneBlocked/>    </Sprite>
        <Sprite start={35.25} end={39.75}> <SceneLesson/>     </Sprite>
        <Sprite start={39.75} end={45.0}>  <SceneCTA/>        </Sprite>
      </div>

      {/* Persistent corner brand mark + timecode */}
      <div style={{
        position: 'absolute', left: 40, top: 32,
        display: 'flex', alignItems: 'center', gap: 14,
        fontFamily: SERIF, fontSize: 14, color: PAL.gold,
        letterSpacing: '0.3em', textTransform: 'uppercase',
        zIndex: 10,
      }}>
        <ObsidianMark size={26} color={PAL.gold}/>
        Obsidian&nbsp;Ridge
        <span style={{ color: PAL.textMute, fontFamily: MONO,
                       letterSpacing: '0.2em', marginLeft: 6 }}>
          ·&nbsp;FIELD&nbsp;NOTE
        </span>
      </div>
      <div style={{
        position: 'absolute', right: 40, top: 32,
        fontFamily: MONO, fontSize: 14, color: PAL.textMute,
        letterSpacing: '0.2em',
        zIndex: 10,
      }}>
        REC · {String(Math.floor(t / 60)).padStart(2,'0')}:{String(Math.floor(t % 60)).padStart(2,'0')}
      </div>
    </div>
  );
}

Object.assign(window, { Reel });