// scenes.jsx — TSCG animated explainer
// Laboratory-register motion design for SKZL AI's TSCG hero.
// Scenes composed inside a <Stage> — each gated by a <Sprite start end>.

/* ─────────────────────────────────────────────────────────────────
 * Shared atoms
 * ───────────────────────────────────────────────────────────────── */

// 1920×1080 stage. Numbers below assume this grid.

const C = {
  white: "#ffffff",
  paper: "#fbfaf7",
  paperDeep: "#f4f1ea",
  onyx: "#0b0a0a",
  ink: "#1a1814",
  inkSoft: "#3a332a",
  inkMute: "#6b5f4f",
  inkPale: "#9a8f7e",
  hair: "rgba(11,10,10,0.08)",
  hair40: "rgba(11,10,10,0.04)",
  hair80: "rgba(11,10,10,0.18)",
  rubedo: "#b8230c",
  rubedoGlow: "#d93515",
  rubedoDeep: "#8b1a1a",
  aurum: "#b98f34",
  aurumSoft: "#d4ac5a",
  aurumPale: "#e8d59f",
};

const FF = {
  display: '"Fraunces", Times, serif',
  body: '"Archivo", system-ui, sans-serif',
  mono: '"IBM Plex Mono", "SF Mono", Consolas, monospace',
};

// Eyebrow chip — mono uppercase with rubedo bar
function Eyebrow({ text, x, y, color = C.rubedo, barColor, opacity = 1 }) {
  return (
    <g transform={`translate(${x} ${y})`} style={{ opacity }}>
      <line x1="0" y1="0" x2="28" y2="0" stroke={barColor || color} strokeWidth="1" />
      <text
        x="40" y="4"
        fontFamily={FF.mono} fontSize="14"
        fontWeight="400" letterSpacing="3.4"
        fill={color} textAnchor="start"
      >
        {text}
      </text>
    </g>
  );
}

// Crosshair corners — laboratory framing
function Crosshairs({ x, y, w, h, opacity = 1, size = 18, stroke = C.aurum }) {
  const s = size;
  return (
    <g style={{ opacity }} stroke={stroke} strokeWidth="1" fill="none">
      <path d={`M${x} ${y + s} L${x} ${y} L${x + s} ${y}`} />
      <path d={`M${x + w - s} ${y} L${x + w} ${y} L${x + w} ${y + s}`} />
      <path d={`M${x + w} ${y + h - s} L${x + w} ${y + h} L${x + w - s} ${y + h}`} />
      <path d={`M${x + s} ${y + h} L${x} ${y + h} L${x} ${y + h - s}`} />
    </g>
  );
}

/* ─────────────────────────────────────────────────────────────────
 * SCENE 1 — THE BLOAT
 * Verbose JSON schemas fall in. Token counter climbs. Rubedo warning.
 * 0 → 5.5s
 * ───────────────────────────────────────────────────────────────── */

const JSON_LINES = [
  '{ "type": "function",',
  '  "function": {',
  '    "name": "get_weather",',
  '    "description": "Get the current',
  '     weather for a specified location",',
  '    "parameters": {',
  '      "type": "object",',
  '      "properties": {',
  '        "location": {',
  '          "type": "string",',
  '          "description": "City or',
  '           coordinates"',
  '        },',
  '        "units": {',
  '          "type": "string",',
  '          "enum": ["celsius",',
  '                   "fahrenheit"],',
  '          "description": "Units"',
  '        }',
  '      },',
  '      "required": ["location"]',
  '    }',
  '  }',
  '}',
];

function SceneBloat() {
  const { localTime, progress } = useSprite();
  const t = localTime;

  // Token counter — 0 → 52_840
  const tokens = Math.round(
    interpolate([0, 3.2, 4.8], [0, 52840, 52840], Easing.easeOutCubic)(t)
  );

  // Column of JSON fades up line-by-line in 0.08s staggers
  const colX = 760;
  const lineH = 30;
  const colTop = 170;

  return (
    <g>
      {/* Top eyebrow */}
      <Eyebrow
        text="01 · DAS PROBLEM"
        x={120} y={110}
        opacity={interpolate([0, 0.4], [0, 1], Easing.easeOutQuad)(t)}
      />

      {/* Left column: question + annotation */}
      <g style={{ opacity: interpolate([0.3, 1.2], [0, 1], Easing.easeOutCubic)(t) }}>
        <text x="120" y="260" fontFamily={FF.display} fontSize="88"
              fontWeight="300" fill={C.onyx} letterSpacing="-2.5"
              style={{ fontVariationSettings: '"opsz" 120' }}>
          Jedes Werkzeug
        </text>
        <text x="120" y="352" fontFamily={FF.display} fontSize="88"
              fontWeight="300" fill={C.onyx} letterSpacing="-2.5"
              style={{ fontVariationSettings: '"opsz" 120' }}>
          ein <tspan fontStyle="italic" fill={C.rubedo}>Gedicht.</tspan>
        </text>
        <text x="120" y="430" fontFamily={FF.body} fontSize="22"
              fill={C.inkSoft} letterSpacing="-0.2">
          JSON-Schema pro Tool-Aufruf. Wieder und wieder.
        </text>
        <text x="120" y="464" fontFamily={FF.body} fontSize="22"
              fill={C.inkSoft} letterSpacing="-0.2">
          Der Kontext füllt sich, bevor die Arbeit beginnt.
        </text>

        {/* Hairline rule */}
        <line x1="120" y1="550" x2="580" y2="550" stroke={C.aurum} strokeWidth="1" />

        {/* Token counter */}
        <text x="120" y="620" fontFamily={FF.mono} fontSize="13"
              fill={C.inkMute} letterSpacing="2.4">
          TOKENS PRO AUFRUF
        </text>
        <text x="120" y="720" fontFamily={FF.display} fontSize="132"
              fontWeight="300" fill={C.onyx} letterSpacing="-3"
              style={{ fontVariantNumeric: 'tabular-nums',
                       fontVariationSettings: '"opsz" 144' }}>
          {tokens.toLocaleString('de-DE')}
        </text>

        {/* Cost annotation — appears late */}
        <g style={{ opacity: interpolate([3.6, 4.4], [0, 1], Easing.easeOutCubic)(t) }}>
          <line x1="120" y1="770" x2="180" y2="770" stroke={C.rubedo} strokeWidth="1" />
          <text x="120" y="810" fontFamily={FF.mono} fontSize="13"
                fill={C.rubedo} letterSpacing="2.4">
            ≈ $30.000 / MONAT BEI 100K CALLS / TAG
          </text>
        </g>
      </g>

      {/* Right column: the cascading JSON */}
      <g>
        <Eyebrow
          text="SCHEMA · VERBOSE"
          x={colX} y={140}
          color={C.inkMute} barColor={C.inkPale}
          opacity={interpolate([0.2, 0.8], [0, 1], Easing.easeOutQuad)(t)}
        />
        {JSON_LINES.map((line, i) => {
          const appearAt = 0.3 + i * 0.08;
          const op = interpolate([appearAt, appearAt + 0.25], [0, 1], Easing.easeOutCubic)(t);
          const dy = interpolate([appearAt, appearAt + 0.3], [8, 0], Easing.easeOutCubic)(t);
          const isKey = /"[a-z_]+":/.test(line);
          return (
            <text
              key={i}
              x={colX} y={colTop + i * lineH + dy}
              fontFamily={FF.mono} fontSize="18"
              fill={C.ink}
              style={{ opacity: op, whiteSpace: 'pre' }}
              letterSpacing="0"
            >
              {line}
            </text>
          );
        })}

        {/* Warning rule on right — appears with counter */}
        <line
          x1={colX} y1={colTop + JSON_LINES.length * lineH + 20}
          x2={colX + 480} y2={colTop + JSON_LINES.length * lineH + 20}
          stroke={C.rubedo} strokeWidth="1"
          strokeDasharray={`${interpolate([2.5, 4], [0, 480], Easing.easeOutCubic)(t)} 1000`}
        />
      </g>

      {/* Exit wipe */}
      <rect x="0" y="0" width="1920" height="1080" fill={C.white}
            style={{ opacity: interpolate([4.8, 5.5], [0, 1], Easing.easeInCubic)(t) }} />
    </g>
  );
}

/* ─────────────────────────────────────────────────────────────────
 * SCENE 2 — THE APPARATUS
 * Eight operators as alchemical glyphs in a circle. Fire in sequence.
 * 5.5 → 12s
 * ───────────────────────────────────────────────────────────────── */

const OPERATORS = [
  { abbr: "TAS",   name: "Tokenizer-Aligned Syntax",      gloss: "BPE-Grenzen" },
  { abbr: "CFL",   name: "Constraint-First Layout",        gloss: "Attention-Sink" },
  { abbr: "CFO",   name: "Causal-Flow Ordering",           gloss: "Kausalketten" },
  { abbr: "SDM",   name: "Semantic Density Maximization",  gloss: "104 Füllmuster" },
  { abbr: "DRO",   name: "Delimiter-Role Optimization",    gloss: "Kompakte Trenner" },
  { abbr: "CCP",   name: "Closure-Context Preservation",   gloss: "Recency-Bias" },
  { abbr: "CAS",   name: "Causal Access Scoring",          gloss: "Parameter-Fragilität" },
  { abbr: "SAD-F", name: "Selective Anchor Duplication",   gloss: "Anker-Budget" },
];

function SceneApparatus() {
  const { localTime } = useSprite();
  const t = localTime;

  const cx = 960, cy = 560;
  const R = 280; // glyph ring radius

  // Overall intro fade-in
  const introOp = interpolate([0, 0.6], [0, 1], Easing.easeOutCubic)(t);

  // Ring draw-in
  const ringLen = 2 * Math.PI * R;
  const ringDraw = interpolate([0.2, 1.8], [0, ringLen], Easing.easeOutCubic)(t);

  // Caption for currently-firing operator
  const fireStart = 1.4;
  const fireDur = 0.45;
  const currentIdx = Math.min(
    OPERATORS.length - 1,
    Math.max(0, Math.floor((t - fireStart) / fireDur))
  );
  const captionVisible = t >= fireStart && t <= fireStart + fireDur * OPERATORS.length + 0.4;

  return (
    <g>
      <Eyebrow text="02 · DER APPARAT" x={120} y={110} opacity={introOp} />

      <g style={{ opacity: introOp }}>
        <text x="120" y="250" fontFamily={FF.display} fontSize="76"
              fontWeight="300" fill={C.onyx} letterSpacing="-2"
              style={{ fontVariationSettings: '"opsz" 120' }}>
          Acht Operatoren.
        </text>
        <text x="120" y="330" fontFamily={FF.display} fontSize="76"
              fontWeight="300" fill={C.onyx} letterSpacing="-2"
              style={{ fontVariationSettings: '"opsz" 120' }}>
          <tspan fontStyle="italic" fill={C.rubedo}>Deterministisch.</tspan>
        </text>
        <text x="120" y="395" fontFamily={FF.body} fontSize="20"
              fill={C.inkSoft} letterSpacing="-0.1">
          Keine Heuristik. Keine ML-Pipeline. Reine Grammatik.
        </text>

        <line x1="120" y1="450" x2="380" y2="450" stroke={C.aurum} strokeWidth="1" />

        <text x="120" y="490" fontFamily={FF.mono} fontSize="12"
              fill={C.inkMute} letterSpacing="2.4">
          1.200 LOC TYPESCRIPT · 0 DEPS · 2,4 MS / 50 TOOLS
        </text>
      </g>

      {/* The ring — stroke-dashed draw-in */}
      <circle
        cx={cx} cy={cy} r={R}
        fill="none"
        stroke={C.aurum}
        strokeWidth="1"
        strokeDasharray={`${ringDraw} ${ringLen}`}
      />
      <circle
        cx={cx} cy={cy} r={R - 18}
        fill="none"
        stroke={C.hair80}
        strokeWidth="0.5"
        style={{ opacity: interpolate([0.8, 1.6], [0, 1], Easing.easeOutQuad)(t) }}
      />

      {/* Crosshair tick marks at cardinal points */}
      {[0, 90, 180, 270].map((deg) => {
        const a = (deg - 90) * Math.PI / 180;
        const x1 = cx + Math.cos(a) * (R - 8);
        const y1 = cy + Math.sin(a) * (R - 8);
        const x2 = cx + Math.cos(a) * (R + 8);
        const y2 = cy + Math.sin(a) * (R + 8);
        return (
          <line key={deg} x1={x1} y1={y1} x2={x2} y2={y2}
                stroke={C.aurum} strokeWidth="1"
                style={{ opacity: interpolate([1.2, 1.8], [0, 1], Easing.easeOutQuad)(t) }} />
        );
      })}

      {/* Center mark */}
      <g style={{ opacity: interpolate([1.0, 1.6], [0, 1], Easing.easeOutQuad)(t) }}>
        <circle cx={cx} cy={cy} r="4" fill={C.rubedo} />
        <circle cx={cx} cy={cy} r="12" fill="none" stroke={C.rubedo} strokeWidth="1" opacity="0.3" />
      </g>

      {/* Operator glyphs around the ring */}
      {OPERATORS.map((op, i) => {
        const angle = (i / OPERATORS.length) * Math.PI * 2 - Math.PI / 2;
        const x = cx + Math.cos(angle) * R;
        const y = cy + Math.sin(angle) * R;

        const appear = 0.9 + i * 0.08;
        const op1 = interpolate([appear, appear + 0.3], [0, 1], Easing.easeOutCubic)(t);

        // fire pulse
        const fireAt = fireStart + i * fireDur;
        const fireProgress = (t - fireAt) / fireDur;
        const isActive = fireProgress >= 0 && fireProgress <= 1;
        const pulseScale = isActive ? 1 + Math.sin(fireProgress * Math.PI) * 0.5 : 1;
        const pulseOp = isActive ? 1 : 0;

        // Radial tick line from center to glyph fires at this operator's moment
        const tickOp = isActive
          ? interpolate([0, 0.3, 0.8, 1], [0, 1, 1, 0], Easing.linear)(fireProgress)
          : 0;

        return (
          <g key={op.abbr} style={{ opacity: op1 }}>
            {/* Radial fire line */}
            <line
              x1={cx + Math.cos(angle) * 20}
              y1={cy + Math.sin(angle) * 20}
              x2={x - Math.cos(angle) * 24}
              y2={y - Math.sin(angle) * 24}
              stroke={C.rubedo} strokeWidth="1"
              style={{ opacity: tickOp }}
            />
            {/* Pulse halo */}
            <circle cx={x} cy={y} r={24 * pulseScale}
                    fill="none" stroke={C.rubedo} strokeWidth="1"
                    style={{ opacity: pulseOp * 0.5 }} />
            {/* Glyph disc */}
            <circle cx={x} cy={y} r="22"
                    fill={isActive ? C.rubedo : C.white}
                    stroke={isActive ? C.rubedo : C.aurum}
                    strokeWidth="1"
                    style={{ transition: 'none' }} />
            <text x={x} y={y + 4}
                  fontFamily={FF.mono} fontSize={op.abbr.length > 3 ? "9" : "11"}
                  fontWeight="500"
                  fill={isActive ? C.white : C.onyx}
                  textAnchor="middle" letterSpacing="0.5">
              {op.abbr}
            </text>
          </g>
        );
      })}

      {/* Current operator caption below ring */}
      <g style={{ opacity: captionVisible ? 1 : 0, transition: 'opacity 200ms' }}>
        <text x={cx} y={cy + R + 80}
              fontFamily={FF.mono} fontSize="13"
              fill={C.rubedo} textAnchor="middle" letterSpacing="3">
          {`OPERATOR ${String(currentIdx + 1).padStart(2, '0')} / 08`}
        </text>
        <text x={cx} y={cy + R + 125}
              fontFamily={FF.display} fontSize="32"
              fontWeight="400" fill={C.onyx} textAnchor="middle"
              letterSpacing="-0.5">
          {OPERATORS[currentIdx].name}
        </text>
        <text x={cx} y={cy + R + 160}
              fontFamily={FF.body} fontSize="16"
              fill={C.inkMute} textAnchor="middle" fontStyle="italic">
          — {OPERATORS[currentIdx].gloss}
        </text>
      </g>

      {/* Exit wipe */}
      <rect x="0" y="0" width="1920" height="1080" fill={C.white}
            style={{ opacity: interpolate([6.0, 6.5], [0, 1], Easing.easeInCubic)(t) }} />
    </g>
  );
}

/* ─────────────────────────────────────────────────────────────────
 * SCENE 3 — THE DISTILLATION
 * Verbose JSON block compresses into a single elegant TSCG line.
 * Token counter collapses.
 * 12 → 18s
 * ───────────────────────────────────────────────────────────────── */

function SceneDistill() {
  const { localTime } = useSprite();
  const t = localTime;

  // Phase timeline inside scene:
  // 0.0–0.8  : JSON block re-establishes (from state of scene 1, but clean)
  // 0.8–2.4  : lines collapse / squeeze down
  // 2.4–3.6  : single TSCG line writes in
  // 3.6–5.5  : label + comparison

  const intro = interpolate([0, 0.5], [0, 1], Easing.easeOutCubic)(t);
  const collapse = interpolate([0.8, 2.4], [0, 1], Easing.easeInOutCubic)(t);
  const writeT = interpolate([2.4, 3.6], [0, 1], Easing.linear)(t);
  const labelsT = interpolate([3.4, 4.2], [0, 1], Easing.easeOutCubic)(t);

  const beforeBoxX = 240, beforeBoxY = 260;
  const beforeBoxW = 620, beforeBoxH = 520;

  const afterY = 560;

  // Token counts
  const beforeTokens = 52840;
  const afterTokens = Math.round(
    interpolate([2.4, 3.6], [beforeTokens, 19540], Easing.easeOutCubic)(t)
  );

  const tscgLine = 'get_weather(location:str units?:str[celsius|fahrenheit])|Get current weather';
  const visibleChars = Math.floor(writeT * tscgLine.length);

  return (
    <g style={{ opacity: intro }}>
      <Eyebrow text="03 · DIE DESTILLATION" x={120} y={110} />

      <text x="120" y="200" fontFamily={FF.display} fontSize="64"
            fontWeight="300" fill={C.onyx} letterSpacing="-1.8"
            style={{ fontVariationSettings: '"opsz" 120' }}>
        Vom Schema zur <tspan fontStyle="italic" fill={C.rubedo}>Grammatik.</tspan>
      </text>

      {/* BEFORE column — stays fully visible, JSON does not disappear */}
      <g>
        <text x={beforeBoxX} y={beforeBoxY - 30}
              fontFamily={FF.mono} fontSize="13" fill={C.inkMute} letterSpacing="2.4">
          VORHER · JSON SCHEMA
        </text>
        {/* Box frame — fixed size */}
        <rect x={beforeBoxX} y={beforeBoxY}
              width={beforeBoxW} height={beforeBoxH}
              fill="none" stroke={C.hair80} strokeWidth="1" />
        {/* Corner marks */}
        <Crosshairs x={beforeBoxX - 2} y={beforeBoxY - 2}
                    w={beforeBoxW + 4}
                    h={beforeBoxH + 4}
                    stroke={C.aurum} />

        {/* JSON lines — stay put, no fade, no collapse */}
        <g>
          {JSON_LINES.map((line, i) => {
            const y = beforeBoxY + 36 + i * 20;
            return (
              <text key={i} x={beforeBoxX + 28} y={y}
                    fontFamily={FF.mono}
                    fontSize="17"
                    fill={C.ink}
                    style={{ whiteSpace: 'pre' }}>
                {line}
              </text>
            );
          })}
        </g>

        {/* TSCG compiler pass — a horizontal sweep bar that moves down the block */}
        <g style={{ opacity: interpolate([0.8, 1.1, 2.4, 2.7], [0, 1, 1, 0], Easing.linear)(t) }}>
          {(() => {
            const sweepY = beforeBoxY + 20 + collapse * (beforeBoxH - 40);
            return (
              <>
                <rect x={beforeBoxX} y={sweepY - 14}
                      width={beforeBoxW} height={28}
                      fill={C.rubedo} opacity="0.08" />
                <line x1={beforeBoxX} y1={sweepY}
                      x2={beforeBoxX + beforeBoxW} y2={sweepY}
                      stroke={C.rubedo} strokeWidth="1" />
                <text x={beforeBoxX + beforeBoxW - 8} y={sweepY - 6}
                      fontFamily={FF.mono} fontSize="10" fill={C.rubedo}
                      textAnchor="end" letterSpacing="2">
                  TSCG COMPILER · PASS 1/8
                </text>
              </>
            );
          })()}
        </g>

        {/* Before count */}
        <text x={beforeBoxX} y={beforeBoxY + beforeBoxH + 60}
              fontFamily={FF.display} fontSize="64"
              fontWeight="300" fill={C.onyx} letterSpacing="-1.5"
              style={{ opacity: 1 - labelsT * 0.4,
                       fontVariantNumeric: 'tabular-nums',
                       fontVariationSettings: '"opsz" 120' }}>
          52.840
        </text>
        <text x={beforeBoxX} y={beforeBoxY + beforeBoxH + 90}
              fontFamily={FF.mono} fontSize="11"
              fill={C.inkMute} letterSpacing="2.4">
          TOKENS
        </text>
      </g>

      {/* Arrow from before → after */}
      <g transform={`translate(${beforeBoxX + beforeBoxW + 40} ${beforeBoxY + 260})`}
         style={{ opacity: interpolate([1.4, 2.0], [0, 1], Easing.easeOutCubic)(t) }}>
        <line x1="0" y1="0" x2="140" y2="0" stroke={C.rubedo} strokeWidth="1"
              strokeDasharray={`${interpolate([1.4, 2.2], [0, 140], Easing.easeOutCubic)(t)} 500`} />
        <text x="70" y="-16" fontFamily={FF.mono} fontSize="12"
              fill={C.rubedo} textAnchor="middle" letterSpacing="2.4">
          TSCG
        </text>
        <path d="M 130 -6 L 142 0 L 130 6"
              fill="none" stroke={C.rubedo} strokeWidth="1" />
      </g>

      {/* AFTER — the single line, split at the `|` */}
      <g transform={`translate(${beforeBoxX + beforeBoxW + 200} ${beforeBoxY + 180})`}>
        <text x="0" y="-30"
              fontFamily={FF.mono} fontSize="13" fill={C.rubedo} letterSpacing="2.4">
          NACHHER · TSCG
        </text>
        {/* Code block — sized to fit the two-line split */}
        <rect x="0" y="0" width="820" height="150"
              fill={C.paper} stroke={C.aurum} strokeWidth="1" />
        <rect x="0" y="0" width="820" height="1" fill={C.rubedo} />

        {(() => {
          const line1 = 'get_weather(location:str units?:str[celsius|fahrenheit])';
          const line2 = '|Get current weather';
          const full = line1 + line2;
          const shown = full.slice(0, visibleChars);
          const top = shown.length > line1.length ? shown.slice(0, line1.length) : shown;
          const bot = shown.length > line1.length ? shown.slice(line1.length) : '';
          return (
            <>
              <text x="24" y="54"
                    fontFamily={FF.mono} fontSize="17"
                    fill={C.ink} letterSpacing="0">
                {top}
                {writeT > 0 && writeT < 1 && shown.length <= line1.length && (
                  <tspan fill={C.rubedo}>▎</tspan>
                )}
              </text>
              <text x="24" y="82"
                    fontFamily={FF.mono} fontSize="17"
                    fill={C.inkSoft} letterSpacing="0">
                {bot}
                {writeT > 0 && writeT < 1 && shown.length > line1.length && (
                  <tspan fill={C.rubedo}>▎</tspan>
                )}
              </text>
            </>
          );
        })()}
        <text x="24" y="118"
              fontFamily={FF.mono} fontSize="12"
              fill={C.inkMute} letterSpacing="1">
          <tspan style={{ opacity: writeT > 0.95 ? 1 : 0 }}>
            ↳ ein Token-Strom. Kein Baum.
          </tspan>
        </text>

        {/* After count */}
        <text x="0" y="260"
              fontFamily={FF.display} fontSize="96"
              fontWeight="300" fill={C.onyx} letterSpacing="-2.5"
              style={{ opacity: labelsT,
                       fontVariantNumeric: 'tabular-nums',
                       fontVariationSettings: '"opsz" 144' }}>
          {afterTokens.toLocaleString('de-DE')}
        </text>
        <text x="0" y="295" fontFamily={FF.mono} fontSize="11"
              fill={C.inkMute} letterSpacing="2.4"
              style={{ opacity: labelsT }}>
          TOKENS
        </text>

        {/* Delta callout */}
        <g style={{ opacity: labelsT }}>
          <line x1="380" y1="220" x2="380" y2="300" stroke={C.aurum} strokeWidth="1" />
          <text x="400" y="245" fontFamily={FF.mono} fontSize="13"
                fill={C.rubedo} letterSpacing="2">
            − 63,0 %
          </text>
          <text x="400" y="275" fontFamily={FF.body} fontSize="14"
                fill={C.inkSoft} fontStyle="italic">
            deterministisch · garantiert ≥ 51 %
          </text>
        </g>
      </g>

      {/* Exit wipe */}
      <rect x="0" y="0" width="1920" height="1080" fill={C.white}
            style={{ opacity: interpolate([5.4, 6.0], [0, 1], Easing.easeInCubic)(t) }} />
    </g>
  );
}

/* ─────────────────────────────────────────────────────────────────
 * SCENE 4 — THE EVIDENCE
 * Accuracy bars rise. Not only cheaper — better.
 * 18 → 24s
 * ───────────────────────────────────────────────────────────────── */

const BENCH_ROWS = [
  { model: "Claude Opus 4.7",  base: 70.0, tscg: 75.0 },
  { model: "GPT-5.2",          base: 61.9, tscg: 89.4 },
  { model: "GPT-4o",           base: 31.7, tscg: 57.4 },
  { model: "Phi-4 14B",        base:  0.0, tscg: 84.4 },
  { model: "Mistral 7B",       base: 35.0, tscg: 80.1 },
];

function SceneEvidence() {
  const { localTime } = useSprite();
  const t = localTime;

  const intro = interpolate([0, 0.5], [0, 1], Easing.easeOutCubic)(t);

  const barMaxW = 820;
  const barH = 24;
  const rowGap = 72;
  const rowsTop = 340;
  const labelX = 120;
  const barX = 420;

  return (
    <g style={{ opacity: intro }}>
      <Eyebrow text="04 · DER BEWEIS" x={120} y={110} />

      <text x="120" y="210" fontFamily={FF.display} fontSize="72"
            fontWeight="300" fill={C.onyx} letterSpacing="-2"
            style={{ fontVariationSettings: '"opsz" 120' }}>
        Nicht nur sparsamer.
      </text>
      <text x="120" y="288" fontFamily={FF.display} fontSize="72"
            fontWeight="300" fill={C.onyx} letterSpacing="-2"
            style={{ fontVariationSettings: '"opsz" 120' }}>
        <tspan fontStyle="italic" fill={C.rubedo}>Genauer.</tspan>
        <tspan fontFamily={FF.mono} fontSize="16" fill={C.inkMute}
               letterSpacing="2.4" dx="40" dy="-18">
          · BFCL · BERKELEY
        </tspan>
      </text>

      {/* Column headers */}
      <g style={{ opacity: interpolate([0.3, 0.9], [0, 1], Easing.easeOutCubic)(t) }}>
        <text x={labelX} y={rowsTop - 20} fontFamily={FF.mono}
              fontSize="11" fill={C.inkMute} letterSpacing="2">
          MODELL · BFCL
        </text>
        <text x={barX} y={rowsTop - 20} fontFamily={FF.mono}
              fontSize="11" fill={C.inkMute} letterSpacing="2">
          BASELINE  →  MIT TSCG
        </text>
        <text x={barX + barMaxW + 40} y={rowsTop - 20}
              fontFamily={FF.mono} fontSize="11" fill={C.rubedo} letterSpacing="2">
          Δ ACCURACY
        </text>
      </g>

      {/* Rows */}
      {BENCH_ROWS.map((row, i) => {
        const appear = 0.6 + i * 0.22;
        const rowT = interpolate([appear, appear + 1.0], [0, 1], Easing.easeOutCubic)(t);
        const y = rowsTop + i * rowGap;
        const baseW = (row.base / 100) * barMaxW;
        const tscgW = (row.tscg / 100) * barMaxW * rowT;
        const delta = row.tscg - row.base;

        return (
          <g key={row.model} style={{ opacity: interpolate([appear - 0.2, appear + 0.2], [0, 1], Easing.easeOutCubic)(t) }}>
            {/* Model label */}
            <text x={labelX} y={y + 18}
                  fontFamily={FF.body} fontSize="18"
                  fontWeight="500" fill={C.onyx}>
              {row.model}
            </text>

            {/* Bar track — light */}
            <rect x={barX} y={y} width={barMaxW} height={barH}
                  fill={C.paperDeep} />
            {/* Baseline marker — thin onyx segment */}
            <rect x={barX} y={y + barH - 4} width={baseW} height="4"
                  fill={C.inkPale} />
            <line x1={barX + baseW} y1={y - 4}
                  x2={barX + baseW} y2={y + barH + 4}
                  stroke={C.ink} strokeWidth="1" />
            <text x={barX + baseW} y={y - 8}
                  fontFamily={FF.mono} fontSize="11"
                  fill={C.inkSoft} textAnchor="middle">
              {row.base.toFixed(1)}
            </text>

            {/* TSCG bar — rubedo */}
            <rect x={barX} y={y} width={tscgW} height={barH}
                  fill={C.rubedo} style={{ opacity: 0.92 }} />

            {/* TSCG value label at end of bar */}
            {rowT > 0.3 && (
              <text x={barX + tscgW + 12} y={y + 17}
                    fontFamily={FF.mono} fontSize="15"
                    fontWeight="500" fill={C.rubedo}
                    style={{ fontVariantNumeric: 'tabular-nums' }}>
                {(row.tscg * Math.min(1, rowT * 1.1)).toFixed(1)}
              </text>
            )}

            {/* Δ chip */}
            {rowT > 0.8 && (
              <g transform={`translate(${barX + barMaxW + 40} ${y + 5})`}
                 style={{ opacity: (rowT - 0.8) * 5 }}>
                <text x="0" y="14"
                      fontFamily={FF.display} fontSize="22"
                      fontWeight="400" fill={C.onyx}
                      style={{ fontVariantNumeric: 'tabular-nums' }}>
                  +{delta.toFixed(1)}
                </text>
                <text x="58" y="14"
                      fontFamily={FF.mono} fontSize="11"
                      fill={C.inkMute} letterSpacing="1.5">
                  pp
                </text>
              </g>
            )}
          </g>
        );
      })}

      {/* Bottom footnote */}
      <g style={{ opacity: interpolate([3.2, 4.0], [0, 1], Easing.easeOutCubic)(t),
                  transform: `translateY(${interpolate([3.2, 4.0], [12, 0], Easing.easeOutCubic)(t)}px)` }}>
        <line x1="120" y1="820" x2="400" y2="820" stroke={C.aurum} strokeWidth="1" />
        <text x="120" y="870" fontFamily={FF.mono} fontSize="13"
              fill={C.inkMute} letterSpacing="2.4">
          QUELLE — BFCL (GORILLA, UC BERKELEY) · N ≈ 19.000 CALLS · 12 MODELLE · 5 SZENARIEN
        </text>
      </g>

      {/* Exit wipe */}
      <rect x="0" y="0" width="1920" height="1080" fill={C.white}
            style={{ opacity: interpolate([5.4, 6.0], [0, 1], Easing.easeInCubic)(t) }} />
    </g>
  );
}

/* ─────────────────────────────────────────────────────────────────
 * SCENE 5 — THE SEAL
 * SKZL·ai wordmark resolves. TSCG credit line.
 * 24 → 28s
 * ───────────────────────────────────────────────────────────────── */

function SceneSeal() {
  const { localTime } = useSprite();
  const t = localTime;

  // Entry
  const op = interpolate([0, 0.5], [0, 1], Easing.easeOutCubic)(t);

  // TSCG headline — letters drop in one by one, Fraunces large
  const tscgLetters = ["T", "S", "C", "G"];

  // SKZL AI mark — S/KZL/AI each with staggered entry and a Fraunces opsz/SOFT flex
  const markT = interpolate([0.6, 1.4], [0, 1], Easing.easeOutCubic)(t);
  const sT   = interpolate([0.6, 1.0], [0, 1], Easing.easeOutCubic)(t);
  const kzlT = interpolate([0.8, 1.2], [0, 1], Easing.easeOutCubic)(t);
  const aiT  = interpolate([1.0, 1.4], [0, 1], Easing.easeOutCubic)(t);

  // Fraunces axis animation — SOFT/WONK ease in as letters settle
  const soft = Math.round(interpolate([0.6, 2.0], [0, 80], Easing.easeOutCubic)(t));
  const wonk = interpolate([1.0, 2.2], [0, 1], Easing.easeOutCubic)(t);
  const opsz = Math.round(interpolate([0.4, 1.8], [9, 144], Easing.easeOutCubic)(t));

  // Aurum rule + tagline
  const ruleW = interpolate([1.6, 2.4], [0, 520], Easing.easeOutCubic)(t);
  const lineOp = interpolate([2.0, 2.6], [0, 1], Easing.easeOutCubic)(t);

  // slow ken-burns
  const scale = 1 + interpolate([0, 4], [0, 0.025], Easing.linear)(t);

  const cx = 960;

  return (
    <g style={{ opacity: op, transform: `scale(${scale})`, transformOrigin: '960px 540px' }}>
      {/* Eyebrow over the seal — positioned ABOVE the TSCG headline with safe margin */}
      <g style={{ opacity: interpolate([0.2, 0.7], [0, 1], Easing.easeOutCubic)(t) }}>
        <line x1={cx - 24} y1="180" x2={cx + 24} y2="180" stroke={C.aurum} strokeWidth="1" />
        <text x={cx} y="212" fontFamily={FF.mono} fontSize="13"
              fill={C.rubedo} textAnchor="middle" letterSpacing="4.8">
          TOOL-SCHEMA COMPRESSION GRAMMAR
        </text>
      </g>

      {/* TSCG — the big headline */}
      <g>
        {tscgLetters.map((ltr, i) => {
          const delay = 0.1 + i * 0.1;
          const lt = interpolate([delay, delay + 0.5], [0, 1], Easing.easeOutCubic)(t);
          const dy = (1 - lt) * 40;
          const op1 = lt;
          const x = cx - 330 + i * 220;
          return (
            <text key={ltr}
                  x={x} y={540 + dy}
                  fontFamily={FF.display}
                  fontSize="300"
                  fontWeight="300"
                  fill={C.onyx}
                  textAnchor="middle"
                  letterSpacing="-12"
                  style={{
                    opacity: op1,
                    fontVariationSettings: `"opsz" ${opsz}, "SOFT" ${soft}, "WONK" 0`
                  }}>
              {ltr}
            </text>
          );
        })}
      </g>

      {/* Aurum rule — filigree divider between TSCG and SKZL AI */}
      <line x1={cx - ruleW/2} y1="640" x2={cx + ruleW/2} y2="640"
            stroke={C.aurum} strokeWidth="1" />

      {/* SAKIZLI attribution mark — replaces the old SKZL AI wordmark */}
      <g style={{ opacity: markT }}>
        {(() => {
          const sz = 72;
          const soft1 = Math.round(interpolate([0.6, 2.2], [0, 60], Easing.easeOutCubic)(t));
          const dyMark = interpolate([0.6, 1.4], [14, 0], Easing.easeOutCubic)(t);
          return (
            <text x={cx} y={780 + dyMark}
                  fontFamily={FF.display}
                  fontSize={sz}
                  fontWeight="400"
                  textAnchor="middle"
                  letterSpacing="-1.5"
                  style={{
                    fontVariationSettings: `"opsz" 96, "SOFT" ${soft1}, "WONK" ${wonk}`
                  }}>
              <tspan fill={C.inkMute} fontStyle="italic" fontSize="38">by </tspan>
              <tspan fill={C.onyx}>SAKIZLI</tspan>
            </text>
          );
        })()}
      </g>

      {/* Tagline */}
      <g style={{ opacity: lineOp }}>
        <text x={cx} y="820" fontFamily={FF.display} fontSize="26"
              fill={C.inkSoft} textAnchor="middle" fontStyle="italic"
              style={{ fontVariationSettings: '"opsz" 72, "SOFT" 60, "WONK" 1' }}>
          Sparsamer. Genauer. Deterministisch.
        </text>
        <text x={cx} y="870" fontFamily={FF.mono} fontSize="12"
              fill={C.inkMute} textAnchor="middle" letterSpacing="3.2">
          OPEN SOURCE · MIT · GITHUB.COM/SKZL-AI/TSCG
        </text>
      </g>
    </g>
  );
}

/* ─────────────────────────────────────────────────────────────────
 * The composed video
 * ───────────────────────────────────────────────────────────────── */

function TimestampLabel() {
  // Updates the stage's data-screen-label each whole second.
  const time = useTime();
  React.useEffect(() => {
    const root = document.querySelector('[data-video-root]');
    if (root) root.setAttribute('data-screen-label', `t=${time.toFixed(1)}s`);
  }, [Math.floor(time)]);
  return null;
}

function TSCGVideo() {
  return (
    <Stage width={1920} height={1080} duration={28} background={C.white}>
      <TimestampLabel />
      <svg viewBox="0 0 1920 1080" width="1920" height="1080"
           xmlns="http://www.w3.org/2000/svg"
           style={{ display: 'block', background: C.white }}>
      {/* Persistent paper backdrop */}
      <rect x="0" y="0" width="1920" height="1080" fill={C.white} />

      {/* Thin top/bottom hairline rules — lab chrome */}
      <line x1="120" y1="60" x2="1800" y2="60" stroke={C.hair} strokeWidth="1" />
      <line x1="120" y1="1020" x2="1800" y2="1020" stroke={C.hair} strokeWidth="1" />

      {/* Seal mark top-right */}
      <g transform="translate(1700 100)">
        <circle cx="0" cy="0" r="32" fill="none" stroke={C.aurum} strokeWidth="1" />
        <circle cx="0" cy="0" r="27" fill="none" stroke={C.aurum} strokeWidth="0.5" />
        <text x="0" y="5" fontFamily={FF.body} fontSize="14" fontWeight="500"
              fill={C.onyx} textAnchor="middle" letterSpacing="1.4">
          SKZL
        </text>
      </g>
      <text x="1760" y="104" fontFamily={FF.mono} fontSize="11"
            fill={C.inkMute} letterSpacing="2.4" textAnchor="start">
        TSCG / v1.4.1
      </text>

      {/* Running time counter — bottom-right */}
      <RunningTime />

      {/* Scenes */}
      <Sprite start={0}    end={5.8}> <SceneBloat /> </Sprite>
      <Sprite start={5.5}  end={12.2}><SceneApparatus /></Sprite>
      <Sprite start={12}   end={18.2}><SceneDistill /></Sprite>
      <Sprite start={18}   end={24.2}><SceneEvidence /></Sprite>
      <Sprite start={24}   end={28}>  <SceneSeal /> </Sprite>
      </svg>
    </Stage>
  );
}

function RunningTime() {
  const time = useTime();
  return (
    <g transform="translate(1700 1005)">
      <text x="0" y="0" fontFamily={FF.mono} fontSize="11"
            fill={C.inkPale} letterSpacing="2" textAnchor="start">
        T+{time.toFixed(2)}s
      </text>
    </g>
  );
}

window.TSCGVideo = TSCGVideo;
