← GPT-AI-VIDEO
CODE · 8.0 KB

remotion/SessionArchiveShort.jsx

Workspace snapshot · 09/04 14:52

import React from "react";
import { AbsoluteFill, Easing, interpolate, Sequence, spring, useCurrentFrame, useVideoConfig } from "remotion";

const ink = "#11110f";
const paper = "#f2f0e7";
const blue = "#004294";
const gold = "#dfb642";
const acid = "#dfff2f";

const clamp = { extrapolateLeft: "clamp", extrapolateRight: "clamp" };
const fade = (frame, start, end) => interpolate(frame, [start, start + 12, end - 12, end], [0, 1, 1, 0], clamp);
const enterY = (frame, start, from = 80) => interpolate(frame, [start, start + 18], [from, 0], { ...clamp, easing: Easing.out(Easing.cubic) });

const Base = ({ children, tone = "paper" }) => (
  <AbsoluteFill style={{
    background: tone === "ink" ? ink : paper,
    color: tone === "ink" ? paper : ink,
    fontFamily: '"Yu Gothic", "Hiragino Kaku Gothic ProN", sans-serif',
    padding: "110px 84px",
    overflow: "hidden"
  }}>
    <div style={{ position: "absolute", inset: 0, opacity: tone === "ink" ? 0.08 : 0.12, backgroundImage: `radial-gradient(${tone === "ink" ? paper : ink} 1px, transparent 1px)`, backgroundSize: "24px 24px" }} />
    <div style={{ position: "absolute", top: 44, left: 84, right: 84, display: "flex", justifyContent: "space-between", fontFamily: "monospace", fontWeight: 700, fontSize: 18, letterSpacing: "0.12em" }}><span>AGENT VENTURE STUDIO</span><span>OWNED STORY 001</span></div>
    {children}
  </AbsoluteFill>
);

const SessionRow = ({ index, frame }) => {
  const delay = index * 2;
  const x = interpolate(frame, [delay, delay + 18], [80, 0], clamp);
  return <div style={{ transform: `translateX(${x}px)`, opacity: interpolate(frame, [delay, delay + 12], [0, 1], clamp), height: 54, borderBottom: "2px solid #4a4a45", display: "grid", gridTemplateColumns: "80px 1fr 160px", alignItems: "center", fontFamily: "monospace", fontSize: 18 }}><span>{String(index + 1).padStart(2, "0")}</span><span style={{ display: "flex", gap: 12 }}><i style={{ width: `${180 + ((index * 73) % 360)}px`, height: 10, background: "#8c8c84" }} /><i style={{ width: `${30 + ((index * 29) % 90)}px`, height: 10, background: "#55554f" }} /></span><span>REDACTED</span></div>;
};

const Opening = () => {
  const frame = useCurrentFrame();
  const discard = interpolate(frame, [120, 170], [0, 1], clamp);
  return <Base tone="ink"><div style={{ opacity: fade(frame, 0, 240) }}>
    <div style={{ marginTop: 150, opacity: 1 - discard, transform: `translateY(${-discard * 180}px)`, filter: `blur(${discard * 8}px)` }}>{Array.from({ length: 15 }, (_, index) => <SessionRow key={index} index={index} frame={frame} />)}</div>
    <div style={{ position: "absolute", left: 84, right: 84, bottom: 150, transform: `translateY(${enterY(frame, 28)}px)` }}>
      <div style={{ fontSize: 72, fontWeight: 900, letterSpacing: "-0.05em" }}>AIを毎日使っていた。</div>
      <div style={{ marginTop: 18, color: acid, fontSize: 112, lineHeight: 0.98, fontWeight: 950, letterSpacing: "-0.07em", opacity: interpolate(frame, [105, 125], [0, 1], clamp) }}>1,645回分の仕事を、<br/>毎回捨てていた。</div>
    </div>
  </div></Base>;
};

const Evidence = () => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const cards = [["CLAUDE", "200", "経営・企画・判断"], ["CODEX", "1,445", "実装・調査・検証"], ["MEET", "100+", "会議・葛藤・変化"]];
  return <Base><div style={{ opacity: fade(frame, 0, 150), marginTop: 220 }}>
    <div style={{ fontFamily: "monospace", fontSize: 24, fontWeight: 800 }}>THE ARCHIVE ALREADY EXISTED.</div>
    <div style={{ display: "grid", gap: 18, marginTop: 70 }}>{cards.map(([label, count, note], index) => {
      const scale = spring({ frame: frame - index * 12, fps, config: { damping: 16, stiffness: 100 } });
      return <div key={label} style={{ minHeight: 280, padding: 40, border: `4px solid ${ink}`, background: index === 1 ? blue : index === 2 ? gold : paper, color: index === 1 ? paper : ink, transform: `scale(${scale})`, transformOrigin: "left center", display: "grid", gridTemplateColumns: "240px 1fr", alignItems: "center" }}><div><div style={{ fontFamily: "monospace", fontSize: 28, fontWeight: 900 }}>{label}</div><div style={{ marginTop: 18, fontSize: 30, fontWeight: 800 }}>{note}</div></div><div style={{ textAlign: "right", fontFamily: "monospace", fontSize: 150, fontWeight: 950, letterSpacing: "-0.08em" }}>{count}</div></div>;
    })}</div>
  </div></Base>;
};

const SourceMap = () => {
  const frame = useCurrentFrame();
  const sources = ["AI SESSIONS", "MEET NOTES", "PENGIN WEB", "YOUTUBE", "PROJECT FILES"];
  return <Base tone="ink"><div style={{ opacity: fade(frame, 0, 180), marginTop: 250 }}>
    <div style={{ fontSize: 76, lineHeight: 1.08, fontWeight: 950, letterSpacing: "-0.055em", wordBreak: "keep-all" }}>モデルにはない。<br/><span style={{ color: acid }}>自社にしかない素材</span>がある。</div>
    <div style={{ position: "relative", marginTop: 160, height: 640 }}>
      {sources.map((source, index) => {
        const y = index * 118;
        const width = interpolate(frame, [20 + index * 8, 65 + index * 8], [0, 520], clamp);
        return <React.Fragment key={source}><div style={{ position: "absolute", left: 0, top: y, width: 340, padding: "20px 22px", border: `2px solid ${paper}`, fontFamily: "monospace", fontSize: 22, fontWeight: 800 }}>{source}</div><div style={{ position: "absolute", left: 340, top: y + 32, width, height: 3, background: index % 2 ? gold : acid }} /></React.Fragment>;
      })}
      <div style={{ position: "absolute", right: 0, top: 170, width: 370, height: 300, display: "grid", placeItems: "center", border: `5px solid ${acid}`, background: blue, transform: `scale(${interpolate(frame, [80, 110], [0.8, 1], clamp)})` }}><div style={{ textAlign: "center", fontFamily: "monospace", fontSize: 42, fontWeight: 950 }}>STORY<br/>BANK</div></div>
    </div>
  </div></Base>;
};

const Stories = () => {
  const frame = useCurrentFrame();
  const stories = ["組織を、0か100で変える。", "SEOとSNSを、同じ数字で見る。", "失敗を、次の制作基盤にする。"];
  return <Base><div style={{ opacity: fade(frame, 0, 180), marginTop: 230 }}>
    <div style={{ fontFamily: "monospace", fontSize: 22, fontWeight: 900 }}>NOT CONTENT IDEAS. REAL DECISIONS.</div>
      <div style={{ marginTop: 70, display: "grid", gap: 28 }}>{stories.map((story, index) => <div key={story} style={{ padding: "48px 42px", minHeight: 250, background: index === 0 ? ink : index === 1 ? blue : gold, color: index < 2 ? paper : ink, transform: `translateX(${enterY(frame, 20 + index * 16, index % 2 ? -100 : 100)}px)`, display: "flex", alignItems: "center", gap: 36 }}><span style={{ fontFamily: "monospace", fontSize: 24 }}>0{index + 1}</span><b style={{ fontSize: 46, lineHeight: 1.25, letterSpacing: "-0.035em", wordBreak: "keep-all" }}>{story}</b></div>)}</div>
  </div></Base>;
};

const EndCard = () => {
  const frame = useCurrentFrame();
  const cursor = Math.floor(frame / 15) % 2 === 0;
  return <Base tone="ink"><div style={{ opacity: interpolate(frame, [0, 18], [0, 1], clamp), marginTop: 350 }}>
    <div style={{ width: 140, height: 18, background: acid, marginBottom: 58 }} />
    <div style={{ fontSize: 88, lineHeight: 1.04, fontWeight: 950, letterSpacing: "-0.065em" }}>眠っている<br/>仕事の記録を、<br/><span style={{ color: acid }}>次の事業と発信へ。</span></div>
    <div style={{ marginTop: 100, display: "flex", alignItems: "center", gap: 20, fontFamily: "monospace", fontSize: 28, fontWeight: 800 }}><span>AGENT VENTURE STUDIO</span><i style={{ display: "inline-block", width: 18, height: 38, background: cursor ? gold : "transparent" }} /></div>
  </div></Base>;
};

export const SessionArchiveShort = () => <AbsoluteFill style={{ background: ink }}>
  <Sequence from={0} durationInFrames={240}><Opening /></Sequence>
  <Sequence from={210} durationInFrames={180}><Evidence /></Sequence>
  <Sequence from={360} durationInFrames={210}><SourceMap /></Sequence>
  <Sequence from={540} durationInFrames={210}><Stories /></Sequence>
  <Sequence from={720} durationInFrames={180}><EndCard /></Sequence>
</AbsoluteFill>;