// ContractFill — "How it works" film · brand tokens + reusable set pieces
// All motion is a pure function of time (no CSS animations) so the film is
// frame-exact under the deterministic recorder (record.html / __frame).

const HIW = {
  navy900: '#0C2237', navy800: '#0F2A44', navy700: '#15334F', navy600: '#1B3A5C',
  gold: '#BF953F', goldMid: '#DFC26A', goldDark: '#AA771C',
  cream: '#FAF9F7', beige: '#F5F1ED', warmGray: '#E8E5E1', warmBorder: '#DAD5CF',
  ink: '#0F2A44', muted: '#6B6560', mutedLight: '#9A938C', ok: '#2E8B57', err: '#C0504A',
  serif: "'Crimson Pro', Georgia, serif",
  sans: "'Inter', system-ui, sans-serif",
  mono: "'IBM Plex Mono', ui-monospace, monospace",
  goldGrad: 'linear-gradient(135deg,#BF953F 0%,#DFC26A 50%,#B38728 100%)',
  navyBg: [
    'radial-gradient(ellipse at 16% 12%, rgba(42,80,128,0.5) 0%, transparent 58%)',
    'radial-gradient(ellipse at 86% 90%, rgba(15,42,68,0.62) 0%, transparent 55%)',
    'radial-gradient(circle at 84% 14%, rgba(191,149,63,0.12) 0%, transparent 46%)',
    'linear-gradient(150deg,#0C2237 0%,#15334F 30%,#1B3A5C 55%,#1F4268 70%,#0A1F35 100%)',
  ].join(','),
};

// ── tiny helpers ─────────────────────────────────────────────────────────────
const useLocal = () => useSprite().localTime;

// fade+rise entry / fade exit, driven by local sprite time
function ease01(t, start, dur, ease = Easing.easeOutCubic) {
  return ease(clamp((t - start) / dur, 0, 1));
}

function In({ at = 0, dur = 0.5, dy = 24, dx = 0, ease = Easing.easeOutCubic, style = {}, children }) {
  const t = useLocal();
  const p = ease01(t, at, dur, ease);
  return (
    <div style={{
      opacity: p,
      transform: `translate(${(1 - p) * dx}px, ${(1 - p) * dy}px)`,
      willChange: 'transform, opacity',
      ...style,
    }}>
      {children}
    </div>
  );
}

function Pop({ at = 0, dur = 0.45, from = 0.82, ease = Easing.easeOutBack, style = {}, children }) {
  const t = useLocal();
  if (t < at) return <div style={{ opacity: 0, ...style }}>{children}</div>;
  const p = ease01(t, at, dur, ease);
  const op = ease01(t, at, dur * 0.6, Easing.easeOutQuad);
  return (
    <div style={{
      opacity: op,
      transform: `scale(${from + (1 - from) * p})`,
      transformOrigin: 'center',
      willChange: 'transform, opacity',
      ...style,
    }}>
      {children}
    </div>
  );
}

// scene-level exit fade (last `dur` seconds of the sprite window)
function SceneFade({ inDur = 0.35, outDur = 0.4, children }) {
  const { localTime, duration } = useSprite();
  const a = ease01(localTime, 0, inDur, Easing.easeOutQuad);
  const b = 1 - ease01(localTime, duration - outDur, outDur, Easing.easeInQuad);
  return <div style={{ position: 'absolute', inset: 0, opacity: Math.min(a, b) }}>{children}</div>;
}

// slow camera move across the scene hold
function Camera({ from = 1, to = 1.045, origin = '62% 42%', children }) {
  const { progress } = useSprite();
  const z = from + (to - from) * progress;
  return (
    <div style={{ position: 'absolute', inset: 0, transform: `scale(${z})`, transformOrigin: origin, willChange: 'transform' }}>
      {children}
    </div>
  );
}

function SceneBG() {
  return <div style={{ position: 'absolute', inset: 0, background: HIW.navyBg }}></div>;
}

// ── left-column step caption ────────────────────────────────────────────────
function StepCaption({ num, label, title, at = 0.15 }) {
  return (
    <div style={{ position: 'absolute', left: 120, top: 300, width: 600 }}>
      <In at={at} dur={0.55} dy={26}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 22 }}>
          <div style={{
            width: 76, height: 76, borderRadius: 20, background: HIW.goldGrad, color: '#fff',
            fontFamily: HIW.serif, fontWeight: 700, fontSize: 36,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            boxShadow: '0 12px 30px rgba(170,119,28,0.4)',
          }}>{num}</div>
          <div style={{ fontFamily: HIW.mono, fontSize: 21, letterSpacing: '0.2em', textTransform: 'uppercase', color: HIW.goldMid }}>{label}</div>
        </div>
      </In>
      <In at={at + 0.18} dur={0.6} dy={30}>
        <div style={{
          fontFamily: HIW.serif, fontWeight: 700, fontSize: 62, lineHeight: 1.08,
          color: '#fff', marginTop: 34, letterSpacing: '-0.01em', textWrap: 'balance',
        }}>{title}</div>
      </In>
      <In at={at + 0.36} dur={0.6} dy={20}>
        <div style={{ height: 5, width: 96, background: HIW.goldGrad, borderRadius: 3, marginTop: 36 }}></div>
      </In>
    </div>
  );
}

// ── product browser window ──────────────────────────────────────────────────
function BrowserWin({ url, x = 800, y = 150, w = 980, h = 780, at = 0.25, children }) {
  return (
    <div style={{ position: 'absolute', left: x, top: y, width: w }}>
      <In at={at} dur={0.65} dy={44}>
        <div style={{
          borderRadius: 18, overflow: 'hidden', background: '#fff',
          boxShadow: '0 40px 90px rgba(4,14,24,0.55)', height: h,
          display: 'flex', flexDirection: 'column',
        }}>
          <div style={{ background: HIW.navy800, padding: '16px 22px', display: 'flex', alignItems: 'center', gap: 10, flex: 'none' }}>
            <span style={{ width: 14, height: 14, borderRadius: '50%', background: '#ff5f57' }}></span>
            <span style={{ width: 14, height: 14, borderRadius: '50%', background: '#ffbd2e' }}></span>
            <span style={{ width: 14, height: 14, borderRadius: '50%', background: '#28ca42' }}></span>
            <span style={{ fontFamily: HIW.mono, fontSize: 17, color: 'rgba(255,255,255,0.55)', marginLeft: 14 }}>{url}</span>
          </div>
          <div style={{ padding: 30, flex: 1, position: 'relative' }}>{children}</div>
        </div>
      </In>
    </div>
  );
}

// ── chat bubbles ────────────────────────────────────────────────────────────
function Bubble({ kind = 'q', at = 0, children }) {
  const styles = {
    q:    { background: HIW.beige, color: '#333', borderRadius: '20px 20px 20px 5px', marginRight: 120 },
    a:    { background: HIW.navy600, color: '#fff', borderRadius: '20px 20px 5px 20px', marginLeft: 120 },
    flag: { background: '#FFF8E7', borderLeft: `6px solid ${HIW.gold}`, color: '#333', borderRadius: '5px 20px 20px 5px', marginRight: 60 },
  }[kind];
  return (
    <Pop at={at} dur={0.5} from={0.92} style={{ marginBottom: 18 }}>
      <div style={{ padding: '20px 26px', fontSize: 25, lineHeight: 1.45, fontFamily: HIW.sans, ...styles }}>
        {children}
      </div>
    </Pop>
  );
}

// three thinking dots, deterministic pulse
function TypingDots({ at = 0, until = 99 }) {
  const t = useLocal();
  if (t < at || t > until) return null;
  const op = ease01(t, at, 0.3);
  return (
    <div style={{ opacity: op, display: 'flex', gap: 9, padding: '22px 26px', background: HIW.navy600, borderRadius: '20px 20px 5px 20px', marginLeft: 480, width: 'fit-content', marginBottom: 18 }}>
      {[0, 1, 2].map((i) => {
        const phase = Math.sin((t - at) * 7 - i * 0.9) * 0.5 + 0.5;
        return <span key={i} style={{ width: 12, height: 12, borderRadius: '50%', background: `rgba(255,255,255,${0.35 + 0.6 * phase})` }}></span>;
      })}
    </div>
  );
}

// typewriter text, deterministic
function TypeText({ text, at = 0, cps = 22, caret = true, style = {} }) {
  const t = useLocal();
  const n = t < at ? 0 : Math.min(text.length, Math.floor((t - at) * cps));
  const doneAt = at + text.length / cps;
  const caretOn = caret && t >= at && t < doneAt + 0.6 && Math.floor(t * 2.5) % 2 === 0;
  return (
    <span style={style}>
      {text.slice(0, n)}
      <span style={{ opacity: caretOn ? 1 : 0, color: HIW.goldDark }}>▏</span>
    </span>
  );
}

// ── data cards ──────────────────────────────────────────────────────────────
function FieldCard({ k, v, at }) {
  return (
    <Pop at={at} dur={0.5} from={0.88}>
      <div style={{ background: '#fff', border: `1.5px solid ${HIW.warmBorder}`, borderRadius: 14, padding: '18px 22px' }}>
        <div style={{ fontFamily: HIW.mono, fontSize: 15, letterSpacing: '0.08em', textTransform: 'uppercase', color: HIW.mutedLight }}>{k}</div>
        <div style={{ fontSize: 27, fontWeight: 600, color: HIW.ink, marginTop: 8, fontFamily: HIW.sans }}>{v}</div>
      </div>
    </Pop>
  );
}

function CheckRow({ at, color = HIW.ok, icon = '✓', children, sub, mono }) {
  return (
    <In at={at} dur={0.45} dy={14}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 16, padding: '17px 22px', borderRadius: 14, background: '#fff', border: `1.5px solid ${HIW.warmBorder}`, borderLeft: `5px solid ${color}` }}>
        <span style={{ width: 36, height: 36, borderRadius: '50%', background: color, color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 19, fontWeight: 700, flex: 'none' }}>{icon}</span>
        <div style={{ flex: 1, fontSize: 24, fontWeight: 600, color: HIW.ink, fontFamily: HIW.sans }}>
          {children}
          {sub ? <div style={{ fontSize: 17, color: HIW.muted, fontWeight: 400, marginTop: 3 }}>{sub}</div> : null}
        </div>
        {mono ? <span style={{ fontFamily: HIW.mono, fontSize: 18, fontWeight: 600, color }}>{mono}</span> : null}
      </div>
    </In>
  );
}

// completeness meter that climbs from -> to over [at, at+dur]
function Meter({ at = 0, dur = 1.4, from = 62, to = 100 }) {
  const t = useLocal();
  const p = ease01(t, at, dur, Easing.easeInOutCubic);
  const val = Math.round(from + (to - from) * p);
  const done = val >= 100;
  return (
    <div>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', fontFamily: HIW.mono, fontSize: 20, color: HIW.muted, marginBottom: 12 }}>
        <span>Contract completeness</span>
        <b style={{ color: done ? HIW.ok : HIW.goldDark, fontSize: 30 }}>{val}%</b>
      </div>
      <div style={{ height: 16, borderRadius: 9, background: HIW.warmGray, overflow: 'hidden' }}>
        <div style={{ height: '100%', width: `${val}%`, borderRadius: 9, background: HIW.goldGrad }}></div>
      </div>
    </div>
  );
}

function SigRow({ initials, name, role, status, at }) {
  const signed = status === 'signed';
  return (
    <In at={at} dur={0.45} dy={16}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 16, padding: '16px 20px', borderRadius: 13, border: `1.5px solid ${HIW.warmBorder}`, background: '#fff' }}>
        <span style={{ width: 46, height: 46, borderRadius: '50%', background: HIW.navy600, color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: HIW.serif, fontWeight: 600, fontSize: 19, flex: 'none' }}>{initials}</span>
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 23, fontWeight: 600, color: HIW.ink, fontFamily: HIW.sans }}>{name}</div>
          <div style={{ fontSize: 16, color: HIW.muted, fontFamily: HIW.sans }}>{role}</div>
        </div>
        <span style={{
          fontFamily: HIW.mono, fontSize: 16, padding: '7px 16px', borderRadius: 100,
          background: signed ? 'rgba(46,139,87,.12)' : 'rgba(191,149,63,.14)',
          color: signed ? HIW.ok : HIW.goldDark,
        }}>{status}</span>
      </div>
    </In>
  );
}

// ── animated cursor ─────────────────────────────────────────────────────────
// keyframes: [{t, x, y}] in scene-local seconds; clickAt: pulse moment
function Cursor({ frames, clickAt = null, scale = 1.6 }) {
  const t = useLocal();
  if (t < frames[0].t) return null;
  const xs = interpolate(frames.map(f => f.t), frames.map(f => f.x), Easing.easeInOutCubic);
  const ys = interpolate(frames.map(f => f.t), frames.map(f => f.y), Easing.easeInOutCubic);
  let ringOp = 0, ringR = 0;
  if (clickAt != null && t >= clickAt && t < clickAt + 0.55) {
    const p = (t - clickAt) / 0.55;
    ringOp = (1 - p) * 0.9;
    ringR = 14 + p * 44;
  }
  const press = clickAt != null && t >= clickAt && t < clickAt + 0.18 ? 0.82 : 1;
  return (
    <div style={{ position: 'absolute', left: xs(t), top: ys(t), zIndex: 50, pointerEvents: 'none', willChange: 'transform' }}>
      <div style={{
        position: 'absolute', left: -ringR, top: -ringR, width: ringR * 2, height: ringR * 2,
        borderRadius: '50%', border: `3px solid ${HIW.goldMid}`, opacity: ringOp,
      }}></div>
      <svg width={26 * scale} height={32 * scale} viewBox="0 0 26 32" style={{ transform: `scale(${press})`, transformOrigin: '4px 4px', display: 'block', filter: 'drop-shadow(0 4px 10px rgba(0,0,0,0.45))' }}>
        <path d="M4 2 L4 24 L10 19 L14 28 L18 26 L14 17 L22 16 Z" fill="#fff" stroke="#0C2237" strokeWidth="1.6"></path>
      </svg>
    </div>
  );
}

Object.assign(window, {
  HIW, useLocal, ease01, In, Pop, SceneFade, Camera, SceneBG,
  StepCaption, BrowserWin, Bubble, TypingDots, TypeText,
  FieldCard, CheckRow, Meter, SigRow, Cursor,
});
