/* ============================================================
   Collab Workshop — 30s product demo
   Apple-keynote styling: cinematic, slow zooms, big type, captions
   16:9 · 1920×1080 · 30s · captions + ambient (silent video)

   Beat plan:
     0.0 –  3.5  Title card "Collab workshop" + tagline
     3.5 –  9.0  Two people editing the same workshop live
     9.0 – 13.5  Soft-lock — a question locks; collaborator sees lock
    13.5 – 18.0  Inline action capture — answer becomes a task
    18.0 – 22.5  Linked action — jump from Actions tab to source Q
    22.5 – 26.5  Live presence rail close-up
    26.5 – 30.0  CTA — Try it / Book demo
   ============================================================ */

const { useEffect } = React;

/* Brand tokens for the demo (lifted from the design system) */
const BRAND = {
  bg: "#f5f5f7",        // Apple-keynote off-white
  ink: "#101828",
  ink2: "#344054",
  ink3: "#475467",
  mute: "#98a2b3",
  line: "#e4e7ec",
  blue: "#175cd3",
  blueBg: "#eff8ff",
  violet: "#6941c6",
  violetBg: "#f4ebff",
  amber: "#b54708",
  amberBg: "#fef0c7",
  green: "#067647",
  greenBg: "#ecfdf3",
  red: "#b42318",
  redBg: "#fef3f2",
};

/* Demo people — initials + color pair so we never need network images */
const PEOPLE = {
  me:     { name: "Adaeze Nwosu",  initials: "AN", bg: "#eff8ff", fg: "#175cd3" },
  marcus: { name: "Marcus Chen",   initials: "MC", bg: "#fdf4ff", fg: "#ba24d5" },
  priya:  { name: "Priya Iyer",    initials: "PI", bg: "#ecfdf3", fg: "#067647" },
  james:  { name: "James O'Brien", initials: "JO", bg: "#fef0c7", fg: "#b54708" },
  adetola:{ name: "Adetola Okafor",initials: "AO", bg: "#f4ebff", fg: "#6941c6" },
};

/* Component: Avatar — also handles a stacked group via `count` prop */
function Avatar({ person, size = "is-md" }) {
  const p = PEOPLE[person];
  return (
    <span className={`av ${size}`} style={{ "--bg": p.bg, "--fg": p.fg }}>{p.initials}</span>
  );
}

/* Component: typing caret — blinks every 530ms */
function Caret() {
  const t = window.useTime();
  const visible = Math.floor(t * 1.9) % 2 === 0;
  return (
    <span style={{
      display: "inline-block",
      width: 2, height: "1.05em",
      background: "#175cd3",
      verticalAlign: "text-bottom",
      marginLeft: 1,
      opacity: visible ? 1 : 0,
    }} />
  );
}

/* Component: presence-dot — pulsing green */
function PresenceDot({ size = 8, color = "#17b26a" }) {
  return (
    <span style={{
      width: size, height: size, borderRadius: 9999,
      background: color, display: "inline-block",
      boxShadow: `0 0 0 2px white, 0 0 0 4px ${color}33`,
    }} />
  );
}

/* ============================================================
   Scene 1 — Title card (0.0 – 3.5s)
   Big type with a soft camera drift. Sets the tone.
   ============================================================ */
function TitleCard() {
  const { progress } = window.useSprite();
  const driftScale = 1 + 0.06 * progress;       // slow zoom-in
  const bgPosY     = -progress * 80;             // parallax background

  return (
    <div style={{
      position: "absolute", inset: 0,
      background: `
        radial-gradient(ellipse at 30% 20%, rgba(105,65,198,0.18) 0%, transparent 55%),
        radial-gradient(ellipse at 70% 80%, rgba(23,92,211,0.16) 0%, transparent 60%),
        linear-gradient(180deg, #f9fafb 0%, #eef2f6 100%)
      `,
      transform: `scale(${driftScale})`,
      transformOrigin: "center",
    }}>
      {/* Decorative dot grid */}
      <div style={{
        position: "absolute", inset: 0,
        backgroundImage: "radial-gradient(circle, rgba(16,24,40,0.06) 1.2px, transparent 1.5px)",
        backgroundSize: "32px 32px",
        backgroundPositionY: bgPosY + "px",
        maskImage: "radial-gradient(ellipse at center, black 30%, transparent 75%)",
        WebkitMaskImage: "radial-gradient(ellipse at center, black 30%, transparent 75%)",
      }} />

      <window.Sprite start={0.2} end={3.4}>
        <window.TextSprite
          text="Introducing"
          x={960} y={360} align="center"
          size={36} weight={500}
          color={BRAND.ink3}
          letterSpacing="0.04em"
          entryDur={0.5} exitDur={0.6}
        />
      </window.Sprite>

      <window.Sprite start={0.5} end={3.4}>
        <window.TextSprite
          text="Collaborative workshops"
          x={960} y={430} align="center"
          size={104} weight={700}
          color={BRAND.ink}
          letterSpacing="-0.03em"
          entryDur={0.7} exitDur={0.5}
        />
      </window.Sprite>

      <window.Sprite start={1.2} end={3.4}>
        <window.TextSprite
          text="Your whole team. One workshop. Live."
          x={960} y={580} align="center"
          size={32} weight={400}
          color={BRAND.ink3}
          entryDur={0.6} exitDur={0.4}
        />
      </window.Sprite>

      {/* Floating avatar trio peeking in from below */}
      <window.Sprite start={1.6} end={3.4}>
        {({ progress: p }) => {
          const rise = window.Easing.easeOutBack(window.clamp(p * 1.6, 0, 1));
          const exit = p > 0.85 ? (p - 0.85) / 0.15 : 0;
          const ty = 60 - rise * 60 + exit * 30;
          const opacity = window.clamp(rise - exit, 0, 1);
          return (
            <div style={{
              position: "absolute", left: "50%", top: 720,
              transform: `translate(-50%, ${ty}px)`,
              opacity,
              display: "flex", gap: -14,
            }}>
              {["me", "marcus", "priya", "james"].map((k, i) => (
                <span key={k} style={{ marginLeft: i === 0 ? 0 : -14, transform: `translateY(${Math.sin((i + 1) * 0.8) * 4}px)` }}>
                  <Avatar person={k} size="is-lg" />
                </span>
              ))}
            </div>
          );
        }}
      </window.Sprite>
    </div>
  );
}

/* ============================================================
   WorkshopMock — the recurring backdrop used across scenes 2-5.
   Pseudo-product UI showing a list of questions with sub-answers.
   `focus` (0..N) decides which row is "active"; `lockedBy` flags
   another person editing a row; `typingValue` injects live text
   into the active row's answer.
   ============================================================ */
function WorkshopMock({ focus = 0, lockedBy = null, typingValue = "", cameraScale = 1, cameraOffset = [0, 0], dimNonFocus = true, actionPulse = false }) {
  const ROWS = [
    { num: "1.1.1", title: "Sustainability Policy is current and communicated.", answer: "Yes — signed April 2026.", priority: null },
    { num: "1.1.2", title: "Leadership conducts visible safety walks monthly.", answer: "Yes — weekly logs reviewed.", priority: null },
    { num: "1.1.3", title: "Stop-work authority is communicated to all personnel.", answer: "Partial — induction module needs refresh.", priority: "high" },
    { num: "1.1.4", title: "Behavioural safety observations are trended quarterly.", answer: "", priority: null },
    { num: "2.1.1", title: "Site-wide hazard register is reviewed annually.", answer: "Yes — last reviewed Feb 2026.", priority: null },
  ];

  const [cx, cy] = cameraOffset;
  return (
    <div style={{
      position: "absolute", inset: 0,
      background: BRAND.bg,
      transform: `translate(${cx}px, ${cy}px) scale(${cameraScale})`,
      transformOrigin: "center",
    }}>
      {/* App chrome — left nav rail */}
      <div style={{
        position: "absolute", left: 0, top: 0, bottom: 0, width: 72,
        background: "#fff", borderRight: `1px solid ${BRAND.line}`,
        display: "flex", flexDirection: "column", alignItems: "center", padding: "16px 0", gap: 8,
      }}>
        <div style={{
          width: 40, height: 40, borderRadius: 10,
          background: "linear-gradient(135deg,#84caff,#2e90fa)",
          display: "grid", placeItems: "center", color: "#fff", fontWeight: 700,
        }}>M</div>
        <div style={{ flex: 1 }} />
        <Avatar person="me" />
      </div>

      {/* Top bar */}
      <div style={{
        position: "absolute", left: 72, right: 0, top: 0, height: 60,
        background: "#fff", borderBottom: `1px solid ${BRAND.line}`,
        display: "flex", alignItems: "center", padding: "0 24px", gap: 16,
        font: "500 14px/1 'Inter', sans-serif", color: BRAND.ink3,
      }}>
        <span style={{ color: BRAND.ink, fontWeight: 600 }}>Safety Standard Gap Assessment</span>
        <span style={{ marginLeft: "auto", display: "flex", alignItems: "center", gap: 12 }}>
          <span style={{ display: "flex" }}>
            {["marcus", "priya", "james"].map((k, i) => (
              <span key={k} style={{ marginLeft: i === 0 ? 0 : -8 }}><Avatar person={k} /></span>
            ))}
          </span>
          <span style={{ display: "inline-flex", alignItems: "center", gap: 6, color: BRAND.green, fontWeight: 600, fontSize: 12 }}>
            <PresenceDot /> 4 editing
          </span>
        </span>
      </div>

      {/* Question rows */}
      <div style={{
        position: "absolute", left: 72, right: 0, top: 60, bottom: 0,
        padding: "32px 56px", overflow: "hidden",
      }}>
        {ROWS.map((r, i) => {
          const isFocus = i === focus;
          const isLocked = lockedBy && i === lockedBy.row;
          const dim = dimNonFocus && !isFocus && !isLocked;
          return (
            <div key={r.num} style={{
              background: "#fff",
              border: `1px solid ${isFocus ? "#84caff" : isLocked ? "#fde68a" : BRAND.line}`,
              boxShadow: isFocus ? "0 0 0 4px #b2ddff66" : isLocked ? "0 0 0 4px #fde68a55" : "none",
              borderRadius: 16,
              padding: 20,
              marginBottom: 12,
              opacity: dim ? 0.4 : 1,
              transition: "opacity .3s, border-color .3s, box-shadow .3s",
              display: "flex", flexDirection: "column", gap: 10,
            }}>
              <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
                <span style={{
                  font: "600 11px/14px 'JetBrains Mono', monospace",
                  color: BRAND.mute, background: BRAND.bg,
                  padding: "3px 8px", borderRadius: 4,
                }}>{r.num}</span>
                <span style={{ font: "600 16px/22px 'Inter', sans-serif", color: BRAND.ink }}>
                  {r.title}
                </span>
                {isLocked && (
                  <span style={{ marginLeft: "auto", display: "inline-flex", alignItems: "center", gap: 8 }}>
                    <Avatar person={lockedBy.person} />
                    <span className="ui-chip" style={{ background: BRAND.amberBg, color: BRAND.amber, borderColor: "#fde68a" }}>
                      {PEOPLE[lockedBy.person].name.split(" ")[0]} is editing
                    </span>
                  </span>
                )}
                {isFocus && !isLocked && (
                  <span style={{ marginLeft: "auto", display: "inline-flex", alignItems: "center", gap: 8 }}>
                    <Avatar person="me" />
                    <span className="ui-chip" style={{ background: BRAND.blueBg, color: BRAND.blue, borderColor: "#b2ddff" }}>
                      You are editing
                    </span>
                  </span>
                )}
              </div>
              <div style={{
                font: "400 15px/22px 'Inter', sans-serif", color: BRAND.ink2,
                background: BRAND.bg, borderRadius: 10, padding: "12px 14px",
                minHeight: 44,
              }}>
                {isFocus ? (
                  <>{typingValue || r.answer}<Caret /></>
                ) : (
                  r.answer || <span style={{ color: BRAND.mute }}>—</span>
                )}
              </div>
              {actionPulse && isFocus && (
                <div style={{
                  display: "inline-flex", alignSelf: "flex-start", alignItems: "center", gap: 8,
                  padding: "6px 12px", borderRadius: 9999,
                  background: BRAND.violetBg, color: BRAND.violet,
                  border: "1px solid #d6bbfb",
                  font: "600 12px/1 'Inter', sans-serif",
                  animation: "actionPulse 1.2s ease-in-out infinite",
                }}>
                  + Action created · Refresh induction module
                </div>
              )}
            </div>
          );
        })}
      </div>
      <style>{`
        @keyframes actionPulse {
          0%, 100% { transform: translateY(0); box-shadow: 0 0 0 0 rgba(105,65,198,0.0); }
          50%      { transform: translateY(-2px); box-shadow: 0 0 0 8px rgba(105,65,198,0.12); }
        }
      `}</style>
    </div>
  );
}

/* ============================================================
   Scene 2 — Co-editing presence (3.5 – 9.0s)
   Camera holds wide, slowly pushes in on the focused row as
   "Adaeze" types her answer. Marcus + Priya hover in the topbar.
   ============================================================ */
function SceneCoEditing() {
  const { localTime } = window.useSprite();
  const dur = 5.5;

  /* Slow Ken-Burns push from 1.0 → 1.08 over 5s */
  const scale = window.interpolate([0, dur], [1.0, 1.08], window.Easing.easeInOutCubic)(localTime);

  /* Typing the answer — characters appear one at a time after t=1s */
  const full = "Partial — induction module needs refresh.";
  const typeStart = 0.8;
  const typeEnd = 3.0;
  const typed = localTime < typeStart ? "" :
    localTime > typeEnd ? full :
    full.slice(0, Math.floor((localTime - typeStart) / (typeEnd - typeStart) * full.length));

  return (
    <div style={{ position: "absolute", inset: 0 }}>
      <WorkshopMock focus={2} typingValue={typed} cameraScale={scale} cameraOffset={[0, -60 * (scale - 1) * 4]} />

      {/* Caption strip */}
      <window.Sprite start={0.3} end={5.0}>
        <CaptionStrip>
          Adaeze, Marcus and Priya — all working on the same workshop. Live.
        </CaptionStrip>
      </window.Sprite>

      {/* Floating presence callouts pointing at the topbar avatars */}
      <window.Sprite start={1.6} end={4.8}>
        {({ progress: p }) => {
          const fade = window.Easing.easeOutCubic(window.clamp(p * 2.5, 0, 1));
          const exit = p > 0.8 ? (p - 0.8) / 0.2 : 0;
          const opacity = window.clamp(fade - exit, 0, 1);
          return (
            <>
              <PresenceCallout x={1300} y={140} person="marcus" label="editing 2.3" opacity={opacity} />
              <PresenceCallout x={1500} y={210} person="priya"  label="editing 4.1" opacity={opacity} delayPx={-10} />
            </>
          );
        }}
      </window.Sprite>
    </div>
  );
}

function PresenceCallout({ x, y, person, label, opacity = 1, delayPx = 0 }) {
  const p = PEOPLE[person];
  return (
    <div style={{
      position: "absolute", left: x, top: y + delayPx,
      transform: `translate(0, 0)`,
      opacity,
      display: "flex", alignItems: "center", gap: 10,
      background: "#fff",
      border: `1px solid ${BRAND.line}`,
      boxShadow: "0 8px 20px rgba(16,24,40,0.10)",
      borderRadius: 12, padding: "8px 12px",
      font: "500 13px/16px 'Inter', sans-serif", color: BRAND.ink,
    }}>
      <Avatar person={person} />
      <div style={{ display: "flex", flexDirection: "column" }}>
        <span style={{ fontWeight: 600 }}>{p.name}</span>
        <span style={{ color: BRAND.ink3, fontSize: 12 }}>{label}</span>
      </div>
      <PresenceDot />
    </div>
  );
}

/* ============================================================
   Scene 3 — Soft-lock (9.0 – 13.5s)
   Marcus locks the next row; Adaeze (our completer) sees the
   lock pill light up. Camera zooms tight on that row.
   ============================================================ */
function SceneSoftLock() {
  const { localTime } = window.useSprite();
  const dur = 4.5;

  /* Push and pan over to row index 3 */
  const scale = window.interpolate([0, 2.0, dur], [1.0, 1.18, 1.18], window.Easing.easeInOutCubic)(localTime);
  const offY  = window.interpolate([0, 2.0, dur], [0, -110, -110], window.Easing.easeInOutCubic)(localTime);

  return (
    <div style={{ position: "absolute", inset: 0 }}>
      <WorkshopMock
        focus={2}
        lockedBy={localTime > 1.0 ? { row: 3, person: "marcus" } : null}
        cameraScale={scale}
        cameraOffset={[-40 * (scale - 1) * 4, offY]}
      />

      <window.Sprite start={0.2} end={4.3}>
        <CaptionStrip>
          When Marcus opens a question, your team sees it instantly. No conflicts. No lost work.
        </CaptionStrip>
      </window.Sprite>
    </div>
  );
}

/* ============================================================
   Scene 4 — Inline action capture (13.5 – 18.0s)
   The answer turns into a follow-up action — pill animates in.
   ============================================================ */
function SceneActionCapture() {
  const { localTime } = window.useSprite();
  const dur = 4.5;

  /* Stay zoomed on row 2; the action pill rises out of it */
  const scale = window.interpolate([0, dur], [1.18, 1.22], window.Easing.easeInOutCubic)(localTime);

  return (
    <div style={{ position: "absolute", inset: 0 }}>
      <WorkshopMock
        focus={2}
        typingValue="Partial — induction module needs refresh."
        cameraScale={scale}
        cameraOffset={[0, -20]}
        actionPulse={localTime > 0.8}
      />

      <window.Sprite start={0.2} end={4.3}>
        <CaptionStrip>
          Spot a gap? Turn the answer into a follow-up action right where you sit.
        </CaptionStrip>
      </window.Sprite>

      {/* Floating action card that flies up to the right */}
      <window.Sprite start={1.4} end={4.3}>
        {({ progress: p }) => {
          const e = window.Easing.easeOutCubic(window.clamp(p * 1.4, 0, 1));
          const exit = p > 0.8 ? (p - 0.8) / 0.2 : 0;
          const x = 600 + e * 700;
          const y = 720 - e * 380;
          const opacity = window.clamp(e - exit, 0, 1);
          const scale2 = 0.7 + e * 0.4 - exit * 0.05;
          return (
            <div style={{
              position: "absolute", left: x, top: y,
              transform: `translate(-50%, -50%) scale(${scale2})`,
              opacity,
              background: "#fff",
              border: `1px solid ${BRAND.line}`,
              boxShadow: "0 20px 48px rgba(16,24,40,0.18)",
              borderRadius: 16, padding: 16,
              width: 380,
              font: "500 14px/20px 'Inter', sans-serif", color: BRAND.ink,
            }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
                <span style={{
                  font: "700 10px/14px 'JetBrains Mono', monospace",
                  color: BRAND.mute, background: BRAND.bg,
                  padding: "2px 7px", borderRadius: 4,
                }}>ACT-Q042</span>
                <span className="ui-chip" style={{ background: BRAND.redBg, color: BRAND.red, borderColor: "#fda29b" }}>
                  High priority
                </span>
              </div>
              <div style={{ fontWeight: 700, fontSize: 15, marginBottom: 6 }}>Refresh induction module</div>
              <div style={{ color: BRAND.ink3, fontSize: 13, marginBottom: 10 }}>
                Stop-work authority not covered in current induction. Update by next intake.
              </div>
              <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                <Avatar person="marcus" />
                <span style={{ color: BRAND.ink2 }}>Marcus Chen</span>
                <span style={{ marginLeft: "auto", color: BRAND.mute, fontSize: 12 }}>Due Jun 14</span>
              </div>
            </div>
          );
        }}
      </window.Sprite>
    </div>
  );
}

/* ============================================================
   Scene 5 — Actions tab + linked-action jump (18.0 – 22.5s)
   Camera dollies up to reveal the Actions tab where the new
   action lives, then a cursor clicks "Linked to 1.1.3" and we
   snap back to the question.
   ============================================================ */
function SceneActionsTab() {
  const { localTime } = window.useSprite();
  const dur = 4.5;

  /* Two-phase: 0-2.5s the Actions tab fades in zoomed; 2.5-4.5s we click the link and the question row pings */
  const phase = localTime < 2.5 ? "actions" : "jump";

  return (
    <div style={{ position: "absolute", inset: 0, background: BRAND.bg }}>
      {phase === "actions" ? (
        <ActionsBoard localTime={localTime} />
      ) : (
        <JumpBack localTime={localTime - 2.5} />
      )}

      <window.Sprite start={0.2} end={4.3}>
        <CaptionStrip>
          Every action is linked back to where it came from — one click away.
        </CaptionStrip>
      </window.Sprite>
    </div>
  );
}

function ActionsBoard({ localTime }) {
  const items = [
    { id: "ACT-Q017", title: "Schedule Q3 hazard-register review", owner: "priya",  prio: "med",  due: "Jun 20", status: "In progress" },
    { id: "ACT-Q042", title: "Refresh induction module",          owner: "marcus", prio: "high", due: "Jun 14", status: "In progress", isNew: true, linkedTo: "1.1.3" },
    { id: "ACT-Q051", title: "Capture stop-work toolbox photos",  owner: "james",  prio: "med",  due: "Jul 02", status: "Open" },
    { id: "ACT-Q063", title: "Re-trend behavioural observations", owner: "me",     prio: "low",  due: "Aug 10", status: "Open" },
  ];
  /* Subtle scale-in */
  const scale = window.interpolate([0, 1.4], [0.96, 1.0], window.Easing.easeOutCubic)(localTime);

  return (
    <div style={{
      position: "absolute", inset: 0,
      transform: `scale(${scale})`,
      transformOrigin: "center",
    }}>
      <div style={{
        position: "absolute", left: 72, right: 0, top: 0, bottom: 0,
        padding: "32px 56px",
      }}>
        <div style={{ display: "flex", alignItems: "center", gap: 14, marginBottom: 24 }}>
          <span style={{ font: "700 28px/32px 'Inter', sans-serif", color: BRAND.ink, letterSpacing: "-0.02em" }}>Actions</span>
          <span className="ui-chip" style={{ background: BRAND.bg, color: BRAND.ink3, borderColor: BRAND.line, fontWeight: 600 }}>
            {items.length} captured
          </span>
        </div>
        {items.map((it, i) => {
          const isHighlighted = it.isNew && localTime > 0.4;
          const ringScale = isHighlighted ? 1 + Math.sin(localTime * 3) * 0.02 : 1;
          return (
            <div key={it.id} style={{
              background: "#fff",
              border: `1px solid ${isHighlighted ? "#d6bbfb" : BRAND.line}`,
              borderLeft: `3px solid ${it.prio === "high" ? BRAND.red : it.prio === "med" ? BRAND.amber : BRAND.blue}`,
              boxShadow: isHighlighted ? "0 0 0 4px rgba(105,65,198,0.10)" : "none",
              borderRadius: 12, padding: 16, marginBottom: 10,
              display: "grid", gridTemplateColumns: "auto 1fr auto auto", gap: 18,
              alignItems: "center",
              transform: `scale(${i === 1 ? ringScale : 1})`,
              transformOrigin: "left center",
            }}>
              <span style={{
                font: "700 10px/14px 'JetBrains Mono', monospace",
                color: BRAND.mute, background: BRAND.bg,
                padding: "3px 8px", borderRadius: 4,
              }}>{it.id}</span>
              <div>
                <div style={{ font: "600 15px/20px 'Inter', sans-serif", color: BRAND.ink, marginBottom: 4 }}>{it.title}</div>
                <div style={{ display: "flex", gap: 6 }}>
                  <span className="ui-chip" style={
                    it.prio === "high" ? { background: BRAND.redBg,   color: BRAND.red,   borderColor: "#fda29b" } :
                    it.prio === "med"  ? { background: BRAND.amberBg, color: BRAND.amber, borderColor: "#fde68a" } :
                                          { background: BRAND.blueBg,  color: BRAND.blue,  borderColor: "#b2ddff" }
                  }>
                    {it.prio === "high" ? "High" : it.prio === "med" ? "Med" : "Low"} priority
                  </span>
                  {it.linkedTo && (
                    <span className="ui-chip" style={{ background: BRAND.blueBg, color: BRAND.blue, borderColor: "#b2ddff" }}>
                      🔗 Linked to {it.linkedTo}
                    </span>
                  )}
                </div>
              </div>
              <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                <Avatar person={it.owner} />
                <span style={{ font: "500 13px/18px 'Inter', sans-serif", color: BRAND.ink2 }}>{PEOPLE[it.owner].name}</span>
              </div>
              <span className="ui-chip" style={{ background: BRAND.amberBg, color: BRAND.amber, borderColor: "#fde68a" }}>
                {it.status}
              </span>
            </div>
          );
        })}

        {/* Animated cursor + click ripple targeting the linked chip */}
        {localTime > 0.8 && (
          <FakeCursor x={760} y={420} clickAt={2.0} localTime={localTime} />
        )}
      </div>
    </div>
  );
}

/* JumpBack — flash back to the workshop with the row highlighted */
function JumpBack({ localTime }) {
  const scale = window.interpolate([0, 0.5, 2.0], [1.1, 1.05, 1.04], window.Easing.easeOutCubic)(localTime);
  const flashAlpha = localTime < 0.4 ? 1 - localTime / 0.4 : 0;
  return (
    <div style={{ position: "absolute", inset: 0 }}>
      <WorkshopMock focus={2} typingValue="Partial — induction module needs refresh." cameraScale={scale} cameraOffset={[0, -120 * (scale - 1) * 5]} />
      {flashAlpha > 0 && (
        <div style={{ position: "absolute", inset: 0, background: "#fff", opacity: flashAlpha }} />
      )}
      <div style={{
        position: "absolute", left: 720, top: 340,
        background: BRAND.blue, color: "#fff",
        padding: "8px 14px", borderRadius: 9999,
        font: "600 13px/16px 'Inter', sans-serif",
        boxShadow: "0 8px 24px rgba(23,92,211,0.4)",
        animation: "actionPulse 1.2s ease-in-out infinite",
      }}>
        Jumped from ACT-Q042 → 1.1.3
      </div>
    </div>
  );
}

function FakeCursor({ x, y, clickAt, localTime }) {
  const t = localTime;
  /* Cursor flies from off-screen bottom-right toward target */
  const startX = 1750, startY = 950;
  const ease = window.Easing.easeInOutCubic(window.clamp(t / clickAt, 0, 1));
  const cx = startX + (x - startX) * ease;
  const cy = startY + (y - startY) * ease;

  /* Click ripple at clickAt */
  const ringT = Math.max(0, t - clickAt);
  const ringR = ringT < 0.5 ? ringT * 80 : 0;
  const ringO = ringT < 0.5 ? 1 - ringT / 0.5 : 0;

  return (
    <>
      {ringR > 0 && (
        <div style={{
          position: "absolute", left: cx, top: cy,
          width: ringR * 2, height: ringR * 2,
          border: `2px solid ${BRAND.blue}`,
          borderRadius: 9999,
          transform: "translate(-50%, -50%)",
          opacity: ringO,
        }} />
      )}
      <svg style={{ position: "absolute", left: cx, top: cy, transform: "translate(-4px, -2px)" }} width="28" height="28" viewBox="0 0 24 24">
        <path d="M4 2 L20 12 L13 13.5 L9.5 21 L4 2 Z" fill="#101828" stroke="#fff" strokeWidth="1.2" />
      </svg>
    </>
  );
}

/* ============================================================
   Scene 6 — Live presence rail (22.5 – 26.5s)
   Tight close-up on the activity rail: who's editing what,
   stacked rows pulsing.
   ============================================================ */
function ScenePresenceRail() {
  const { localTime } = window.useSprite();
  const dur = 4.0;
  const scale = window.interpolate([0, dur], [1.0, 1.06], window.Easing.easeInOutCubic)(localTime);

  return (
    <div style={{ position: "absolute", inset: 0, background: BRAND.bg }}>
      <div style={{
        position: "absolute", inset: 0,
        transform: `scale(${scale})`,
        transformOrigin: "center",
        display: "grid", placeItems: "center",
      }}>
        <div style={{
          width: 720, background: "#fff",
          border: `1px solid ${BRAND.line}`,
          borderRadius: 24, padding: 32,
          boxShadow: "0 24px 60px rgba(16,24,40,0.12)",
        }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 20 }}>
            <PresenceDot size={10} />
            <span style={{ font: "700 11px/16px 'Inter', sans-serif", color: BRAND.green, letterSpacing: "0.06em", textTransform: "uppercase" }}>Live now</span>
            <span style={{ font: "600 18px/24px 'Inter', sans-serif", color: BRAND.ink, marginLeft: 4 }}>Your team</span>
          </div>
          {[
            { who: "marcus", item: "2.3 Hazard register review",    state: "Editing", since: "2m" },
            { who: "priya",  item: "4.1 Skills matrix",              state: "Typing",  since: "just now" },
            { who: "james",  item: "1.2 Stop-work authority",        state: "Reading", since: "5m" },
            { who: "adetola",item: "6.1 Operational bottlenecks",   state: "Editing", since: "1m" },
          ].map((r, i) => {
            const showAt = 0.2 + i * 0.25;
            const e = window.Easing.easeOutCubic(window.clamp((localTime - showAt) / 0.5, 0, 1));
            return (
              <div key={r.who} style={{
                display: "grid", gridTemplateColumns: "auto 1fr auto", gap: 14,
                alignItems: "center", padding: "12px 0",
                borderBottom: i === 3 ? "none" : `1px solid ${BRAND.line}`,
                transform: `translateY(${(1 - e) * 12}px)`,
                opacity: e,
              }}>
                <span style={{ position: "relative" }}>
                  <Avatar person={r.who} size="is-md" />
                  <span style={{ position: "absolute", right: -2, bottom: -2 }}><PresenceDot /></span>
                </span>
                <div>
                  <div style={{ font: "600 14px/18px 'Inter', sans-serif", color: BRAND.ink }}>{PEOPLE[r.who].name}</div>
                  <div style={{ font: "400 12px/16px 'Inter', sans-serif", color: BRAND.ink3 }}>
                    {r.state} <b style={{ color: BRAND.ink2 }}>{r.item}</b> · {r.since}
                  </div>
                </div>
                <span className="ui-chip" style={{ background: r.state === "Typing" ? BRAND.greenBg : BRAND.bg, color: r.state === "Typing" ? BRAND.green : BRAND.ink3, borderColor: r.state === "Typing" ? "#abefc6" : BRAND.line }}>
                  {r.state}
                </span>
              </div>
            );
          })}
        </div>
      </div>

      <window.Sprite start={0.2} end={3.8}>
        <CaptionStrip>
          See who's where. Every cursor. Every conversation. In real time.
        </CaptionStrip>
      </window.Sprite>
    </div>
  );
}

/* ============================================================
   Scene 7 — CTA (26.5 – 30.0s)
   Logo + tagline + Try it / Book demo buttons.
   ============================================================ */
function SceneCTA() {
  return (
    <div style={{
      position: "absolute", inset: 0,
      background: `
        radial-gradient(ellipse at 30% 30%, rgba(23,92,211,0.20) 0%, transparent 55%),
        radial-gradient(ellipse at 70% 70%, rgba(105,65,198,0.20) 0%, transparent 60%),
        linear-gradient(180deg, #ffffff 0%, #f0f4fa 100%)
      `,
    }}>
      <window.Sprite start={0.0} end={3.5}>
        <window.TextSprite
          text="Workshops, together."
          x={960} y={400} align="center"
          size={92} weight={700}
          color={BRAND.ink}
          letterSpacing="-0.03em"
          entryDur={0.55} exitDur={0.4}
        />
      </window.Sprite>

      <window.Sprite start={0.4} end={3.5}>
        <window.TextSprite
          text="Bring your whole team into one live workspace."
          x={960} y={530} align="center"
          size={28} weight={400}
          color={BRAND.ink3}
          entryDur={0.5} exitDur={0.4}
        />
      </window.Sprite>

      <window.Sprite start={0.8} end={3.5}>
        {({ progress: p }) => {
          const e = window.Easing.easeOutBack(window.clamp(p * 2.2, 0, 1));
          const exit = p > 0.85 ? (p - 0.85) / 0.15 : 0;
          const opacity = window.clamp(e - exit, 0, 1);
          const ty = (1 - e) * 24;
          return (
            <div style={{
              position: "absolute", left: "50%", top: 640,
              transform: `translate(-50%, ${ty}px)`,
              opacity,
              display: "flex", gap: 12,
            }}>
              <button style={{
                padding: "16px 32px", borderRadius: 12,
                background: BRAND.ink, color: "#fff",
                border: 0, fontWeight: 600, fontSize: 18,
                boxShadow: "0 12px 32px rgba(16,24,40,0.24)",
                cursor: "pointer",
              }}>Try it free</button>
              <button style={{
                padding: "16px 32px", borderRadius: 12,
                background: "#fff", color: BRAND.ink,
                border: `1px solid ${BRAND.line}`, fontWeight: 600, fontSize: 18,
                boxShadow: "0 4px 12px rgba(16,24,40,0.06)",
                cursor: "pointer",
              }}>Book a demo</button>
            </div>
          );
        }}
      </window.Sprite>

      <window.Sprite start={0.0} end={3.5}>
        <window.TextSprite
          text="method 8"
          x={960} y={140} align="center"
          size={20} weight={700}
          color={BRAND.ink2}
          letterSpacing="0.18em"
          entryDur={0.4} exitDur={0.3}
        />
      </window.Sprite>
    </div>
  );
}

/* ============================================================
   CaptionStrip — bottom-of-frame caption ribbon, Apple style.
   ============================================================ */
function CaptionStrip({ children }) {
  const { progress } = window.useSprite();
  const fade = window.Easing.easeOutCubic(window.clamp(progress * 4, 0, 1));
  const exit = progress > 0.85 ? (progress - 0.85) / 0.15 : 0;
  const opacity = window.clamp(fade - exit, 0, 1);
  return (
    <div style={{
      position: "absolute", left: 0, right: 0, bottom: 80,
      display: "flex", justifyContent: "center",
      opacity,
      transform: `translateY(${(1 - fade) * 14}px)`,
    }}>
      <div style={{
        padding: "16px 32px",
        background: "rgba(16,24,40,0.86)",
        backdropFilter: "blur(20px)",
        WebkitBackdropFilter: "blur(20px)",
        borderRadius: 16,
        color: "#fff",
        font: "500 28px/36px 'Inter', sans-serif",
        letterSpacing: "-0.01em",
        textAlign: "center",
        maxWidth: 1400,
        boxShadow: "0 12px 40px rgba(0,0,0,0.32)",
      }}>
        {children}
      </div>
    </div>
  );
}

/* ============================================================
   ScreenLabel — updates the root data-screen-label every 0.5s
   with the current timestamp so the user can comment on a
   specific moment in the video.
   ============================================================ */
function ScreenLabel() {
  const t = window.useTime();
  useEffect(() => {
    const root = document.getElementById("video-root");
    if (!root) return;
    const sec = Math.floor(t * 10) / 10;
    root.setAttribute("data-screen-label", `t=${sec.toFixed(1)}s · Collab workshop demo`);
  }, [t]);
  return null;
}

/* ============================================================
   App — orchestrates the 30-second timeline.
   Beat boundaries (in seconds):
     0.0 –  3.5  Title
     3.5 –  9.0  Co-editing
     9.0 – 13.5  Soft-lock
    13.5 – 18.0  Action capture
    18.0 – 22.5  Actions tab + jump-back
    22.5 – 26.5  Presence rail
    26.5 – 30.0  CTA
   ============================================================ */
function App() {
  return (
    <div id="video-root" style={{ position: "absolute", inset: 0 }} data-screen-label="t=0.0s · Collab workshop demo">
      <window.Stage width={1920} height={1080} duration={30} background="#f5f5f7" autoplay loop persistKey="collab-demo">
        <ScreenLabel />
        <window.AmbientAudioController />
        <window.SfxScheduler events={[
          { at: 0.5,  kind: "ding"   },
          { at: 1.6,  kind: "pop"    },
          { at: 4.0,  kind: "whoosh" },
          { at: 9.5,  kind: "ding"   },
          { at: 13.8, kind: "whoosh" },
          { at: 14.4, kind: "pop"    },
          { at: 18.4, kind: "click"  },
          { at: 20.5, kind: "whoosh" },
          { at: 27.0, kind: "pop"    },
          { at: 27.5, kind: "ding"   },
        ]} />

        <window.Sprite start={0}    end={3.5}>{() => <TitleCard />}</window.Sprite>
        <window.Sprite start={3.5}  end={9.0}>{() => <SceneCoEditing />}</window.Sprite>
        <window.Sprite start={9.0}  end={13.5}>{() => <SceneSoftLock />}</window.Sprite>
        <window.Sprite start={13.5} end={18.0}>{() => <SceneActionCapture />}</window.Sprite>
        <window.Sprite start={18.0} end={22.5}>{() => <SceneActionsTab />}</window.Sprite>
        <window.Sprite start={22.5} end={26.5}>{() => <ScenePresenceRail />}</window.Sprite>
        <window.Sprite start={26.5} end={30.0}>{() => <SceneCTA />}</window.Sprite>
      </window.Stage>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
