// story.jsx — cinematic shell helpers + scenes 1-3 of the "saved by Scam Protect" story.
// Independent of scenes.jsx. Relies on theme.jsx, ui.jsx, phone.jsx, animations.jsx.

// ── helpers ──────────────────────────────────────────────────────────────────
function envelope(lt, dur, inDur = 0.6, outDur = 0.7) {
  const ein = Easing.easeOutCubic(clamp(lt / inDur, 0, 1));
  const eout = 1 - Easing.easeInCubic(clamp((lt - (dur - outDur)) / outDur, 0, 1));
  return Math.min(ein, eout);
}
function CineScene({ lt, dur, children, bg = 'transparent' }) {
  return <div style={{ position: 'absolute', inset: 0, background: bg, opacity: envelope(lt, dur) }}>{children}</div>;
}
function Rise({ lt, at, dur = 0.7, dy = 22, dx = 0, children, style = {}, ease = Easing.easeOutCubic }) {
  const p = ease(clamp((lt - at) / dur, 0, 1));
  return <div style={{ opacity: p, transform: `translate(${(1 - p) * dx}px, ${(1 - p) * dy}px)`, ...style }}>{children}</div>;
}
// Lower-third cinematic caption
function LowerThird({ lt, at, k, line, sub, color = '#fff', dur = 99 }) {
  const show = lt >= at && lt < at + dur;
  const p = Easing.easeOutCubic(clamp((lt - at) / 0.6, 0, 1)) * (1 - Easing.easeInCubic(clamp((lt - (at + dur - 0.5)) / 0.5, 0, 1)));
  if (!show && p <= 0.01) return null;
  return (
    <div style={{ position: 'absolute', left: 120, bottom: 150, maxWidth: 1100, opacity: p, transform: `translateY(${(1 - p) * 16}px)` }}>
      {k && <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
        <span style={{ width: 40, height: 2, background: color, opacity: 0.7 }} />
        <span style={{ fontFamily: T.mono, fontSize: 15, letterSpacing: '0.22em', textTransform: 'uppercase', color, opacity: 0.9 }}>{k}</span>
      </div>}
      <div style={{ fontFamily: T.display, fontWeight: 800, fontSize: 58, color, letterSpacing: '-0.03em', lineHeight: 1.12, textShadow: '0 2px 30px rgba(0,0,0,0.5)' }}>{line}</div>
      {sub && <div style={{ fontFamily: T.display, fontSize: 25, color, opacity: 0.78, marginTop: 20, textShadow: '0 2px 20px rgba(0,0,0,0.5)' }}>{sub}</div>}
    </div>
  );
}

// Soft scrim so captions read over any photo
function Scrim({ from = 'bottom', strength = 0.7 }) {
  const dir = { bottom: 'to top', top: 'to bottom', left: 'to right', right: 'to left' }[from];
  return <div style={{ position: 'absolute', inset: 0, background: `linear-gradient(${dir}, rgba(12,9,6,${strength}), rgba(12,9,6,0) 55%)`, pointerEvents: 'none' }} />;
}

// Ken-Burns wrapper for an image slot
function KenBurns({ lt, dur, from = 1.0, to = 1.12, ox = 50, oy = 50, children }) {
  const p = clamp(lt / dur, 0, 1);
  const s = from + (to - from) * p;
  return <div style={{ position: 'absolute', inset: 0, transform: `scale(${s})`, transformOrigin: `${ox}% ${oy}%` }}>{children}</div>;
}

function Slot({ id, placeholder, style, src, shape = 'rect', position }) {
  const props = { id, placeholder, shape, style };
  if (src) props.src = src;
  if (position) props.position = position;
  return React.createElement('image-slot', props);
}

// Real photos sourced from Unsplash (free license) — users can drop their own to replace.
const PHOTOS = {
  evening: 'garcia-evening.png',
  family:  'garcia-family.png',
  person:  'garcia-face.png',
};

// ═══════════════════════════════════════════════════════════════════════════
// SCENE 1 — AN ORDINARY EVENING
// ═══════════════════════════════════════════════════════════════════════════
function EveningScene({ lt, dur }) {
  return (
    <CineScene lt={lt} dur={dur} bg="#0C0906">
      <KenBurns lt={lt} dur={dur} from={1.05} to={1.16} oy={42}>
        <Slot id="scene-home" src={PHOTOS.evening} placeholder="Drop a photo: a woman at home in the evening" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }} />
      </KenBurns>
      <div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(120% 90% at 50% 40%, rgba(204,138,43,0.10), rgba(12,9,6,0) 55%)' }} />
      <Scrim from="bottom" strength={0.82} />
      <Scrim from="top" strength={0.4} />

      {/* time stamp */}
      <Rise lt={lt} at={0.6} style={{ position: 'absolute', top: 120, left: 120 }}>
        <div style={{ fontFamily: T.mono, fontSize: 18, letterSpacing: '0.18em', color: 'rgba(255,255,255,0.7)' }}>TUE · 9:47 PM</div>
      </Rise>

      {/* buzzing phone hint removed — the photo already shows the phone */}

      <LowerThird lt={lt} at={1.4} k="A quiet evening" line={<>It started like any other night.</>} sub="Then the phone rang." />
    </CineScene>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// SCENE 2 — THE CALL
// ═══════════════════════════════════════════════════════════════════════════
function CallScene({ lt, dur }) {
  const phoneIn = Easing.easeOutCubic(clamp(lt / 0.9, 0, 1));
  return (
    <CineScene lt={lt} dur={dur} bg="#0E1719">
      <div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(90% 80% at 68% 45%, rgba(40,60,66,0.55), rgba(8,12,13,0) 60%)' }} />
      {/* phone */}
      <div style={{ position: 'absolute', top: 90, right: 200, transform: `translateY(${(1 - phoneIn) * 60}px) scale(${0.94 + 0.06 * phoneIn})`, opacity: phoneIn, perspective: 1400 }}>
        <PhoneFrame tilt={-3} glow={lt > 3.2 ? 'rgba(46,157,113,0.0)' : 'rgba(207,75,51,0.45)'}>
          <CallScreen t={lt} />
        </PhoneFrame>
      </div>

      {/* left copy */}
      <div style={{ position: 'absolute', left: 120, top: 320, maxWidth: 720 }}>
        <Rise lt={lt} at={0.5}><div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 18 }}>
          <span style={{ width: 40, height: 2, background: T.threat }} />
          <span style={{ fontFamily: T.mono, fontSize: 15, letterSpacing: '0.22em', textTransform: 'uppercase', color: '#E89A86' }}>The call</span>
        </div></Rise>
        <Rise lt={lt} at={0.9} dur={0.8}>
          <div style={{ fontFamily: T.display, fontWeight: 800, fontSize: 60, color: '#fff', letterSpacing: '-0.03em', lineHeight: 1.06 }}>
            "This is your bank.<br />Your account's been<br />compromised."
          </div>
        </Rise>
        <Rise lt={lt} at={1.8} dy={14}>
          <div style={{ fontFamily: T.display, fontSize: 25, color: 'rgba(255,255,255,0.72)', marginTop: 22, lineHeight: 1.4 }}>
            The voice sounds official. Calm. Urgent.<br />It knows his name.
          </div>
        </Rise>
      </div>
    </CineScene>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// SCENE 3 — THE BRINK
// ═══════════════════════════════════════════════════════════════════════════
function BrinkScene({ lt, dur }) {
  const zoom = 1 + Easing.easeInOutCubic(clamp(lt / dur, 0, 1)) * 0.06;
  // thumb approaches confirm near the end
  const thumbX = interpolate([4.5, 7.5], [1180, 980], Easing.easeInOutCubic)(lt);
  const thumbY = interpolate([4.5, 7.5], [980, 720], Easing.easeInOutCubic)(lt);
  const showThumb = lt > 4.3;
  const press = clamp((lt - 7.3) / 0.4, 0, 1);
  return (
    <CineScene lt={lt} dur={dur} bg="#0C0906">
      <div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(80% 80% at 50% 46%, rgba(60,30,22,0.5), rgba(8,6,4,0) 62%)' }} />
      <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', transform: `scale(${zoom})` }}>
        <div style={{ position: 'relative', perspective: 1500 }}>
          <PhoneFrame tilt={2} glow="rgba(207,75,51,0.4)">
            <TransferScreen t={lt} />
          </PhoneFrame>
          {showThumb && <Thumb x={thumbX - 760} y={thumbY - 120} press={press} />}
        </div>
      </div>

      <LowerThird lt={lt} at={0.6} dur={3.6} k="Pressure" line={<>"Don't hang up. Transfer the money now."</>} />
      <LowerThird lt={lt} at={4.6} k="The brink" line={<>One tap from losing $8,740.</>} color="#fff" />
    </CineScene>
  );
}

Object.assign(window, { envelope, CineScene, Rise, LowerThird, Scrim, KenBurns, Slot, PHOTOS, EveningScene, CallScene, BrinkScene });
