import { interpolate } from 'remotion';
import { C } from '../lib/colors';
import { springIn, softSpring, clampLerp } from '../lib/easings';

// Phase 3 (now 456 frames, ~15.2s): prompt panel + vault panel + arrow fork
// (original beats over first ~90 frames). Then new beats:
//   • AES-256-GCM stamp appears next to the vault (frame ~95).
//   • Masked key value animates from the vault into a `key={NOTION_API_KEY}`
//     env-var pill placed beside a small container outline → "injected at
//     runtime" moment (frames ~180–340).
//   • Late beat: the prompt panel flickers back in DIM and labeled
//     "what the model sees", visually separating from the vault.

const PROMPT_TEXT = 'Build me an MCP server that searches our Notion workspace.';
const KEY_LABEL = 'NOTION_API_KEY';
const KEY_VISIBLE = 'secret_';
const KEY_BULLETS = '•••••••••••••••••••';
const KEY_TAIL = '3a8f';

const SCENE_LEN = 456;

// Original phase-1.5 beats (unchanged):
const TYPE_START = 30;
const TYPE_END = 44;
const MASK_START = 46;
const MASK_END = 60;
const TAIL_START = 60;
const TAIL_END = 66;
const LOCK_SNAP_AT = 66;
const ARROW_START = 70;
const ARROW_END = 88;

// New beats:
const AES_STAMP_AT = 95;
const INJECT_START = 180;       // ~6s — key chip pops out of vault
const INJECT_FLY_END = 240;     // chip arrives at container
const CONTAINER_IN = 200;
const FLICKER_PROMPT_AT = 300;  // "the model only sees the description"

const EXIT_START = SCENE_LEN - 12;

export function SecretsScene({ localFrame, fps }: { localFrame: number; fps: number }) {
  // Prompt panel: enters by sliding left from center.
  const promptShift = clampLerp(localFrame, 0, 18);
  const promptX = interpolate(promptShift, [0, 1], [410, 60]);

  // Secrets vault panel: spring in from below-right.
  const vaultIn = springIn(localFrame, fps, 8);
  const vaultOpacity = clampLerp(localFrame, 8, 24);
  const vaultY = interpolate(vaultIn, [0, 1], [40, 0]);
  const vaultScale = interpolate(vaultIn, [0, 1], [0.92, 1]);

  const typingProgress = clampLerp(localFrame, TYPE_START, TYPE_END);
  const maskProgress = clampLerp(localFrame, MASK_START, MASK_END);
  const tailProgress = clampLerp(localFrame, TAIL_START, TAIL_END);
  const lockOpen = localFrame < LOCK_SNAP_AT;

  const typedChars = Math.floor(typingProgress * KEY_VISIBLE.length);
  const typedShown = KEY_VISIBLE.slice(0, typedChars);
  const bulletChars = Math.floor(maskProgress * KEY_BULLETS.length);
  const bulletsShown = KEY_BULLETS.slice(0, bulletChars);
  const tailChars = Math.floor(tailProgress * KEY_TAIL.length);
  const tailShown = KEY_TAIL.slice(0, tailChars);

  const arrowProgress = clampLerp(localFrame, ARROW_START, ARROW_END);
  const xMarkPop = springIn(localFrame, fps, ARROW_START + 8);

  // After ~110 frames, the arrows + LLM icon fade out to clear the canvas for
  // the new beats (env-var injection).
  const arrowFadeOut = 1 - clampLerp(localFrame, 110, 140);

  // AES stamp.
  const aesIn = springIn(localFrame, fps, AES_STAMP_AT);

  // Env-var injection: the masked key value flies from the vault into a
  // container outline.
  const injectProgress = clampLerp(localFrame, INJECT_START, INJECT_FLY_END);
  const containerIn = springIn(localFrame, fps, CONTAINER_IN);

  // Lock-snap flash — a short bright indigo ring expands outward from the
  // lock the moment it closes. Lifts the snap from "subtle SVG morph" to
  // "the vault clicked shut". Lasts ~12 frames, peaks at frame 66+4.
  const lockFlashT = clampLerp(localFrame, LOCK_SNAP_AT, LOCK_SNAP_AT + 16);
  const lockFlashFade = 1 - clampLerp(localFrame, LOCK_SNAP_AT + 6, LOCK_SNAP_AT + 22);
  const lockFlash = Math.min(lockFlashT, lockFlashFade);

  // Late stage: keep vault at FULL opacity throughout so the eye still has
  // it as the anchor while the env-var injection happens to the side. The
  // prompt panel still fades — that beat is owned by the arrow fork.
  const stageShift = clampLerp(localFrame, INJECT_START - 20, INJECT_START + 10);
  const vaultLateOpacity = 1;
  const promptLateOpacity = interpolate(stageShift, [0, 1], [1, 0]);

  // Vault → container connector — a soft dashed line drawn between the
  // vault's bottom edge and the container's env slot at the moment the
  // chip travels. Anchors the architectural story: this key came FROM
  // that vault.
  const connectorReveal = clampLerp(localFrame, INJECT_START - 6, INJECT_START + 18);
  const connectorFade = 1 - clampLerp(localFrame, INJECT_FLY_END + 6, INJECT_FLY_END + 30);
  const connectorAlpha = Math.min(connectorReveal, connectorFade);

  // Exit drift.
  const exitProgress = clampLerp(localFrame, EXIT_START, SCENE_LEN);
  const exitY = interpolate(exitProgress, [0, 1], [0, -24]);

  return (
    <div
      style={{
        position: 'absolute',
        inset: 0,
        transform: `translateY(${exitY}px)`,
      }}
    >
      {/* Original prompt mini-panel (left). Fades into the late stage. */}
      <div style={{ opacity: promptLateOpacity }}>
        <PromptMiniPanel x={promptX} promptText={PROMPT_TEXT} />
      </div>

      {/* Vault panel (right) — stays at FULL opacity through the whole
          scene so the eye keeps it as the anchor while the env-var
          chip travels to the container below. */}
      <div
        style={{
          position: 'absolute',
          left: 1040,
          top: (1080 - 380) / 2,
          width: 820,
          opacity: vaultOpacity * vaultLateOpacity,
          transform: `translateY(${vaultY}px) scale(${vaultScale})`,
          transformOrigin: 'center center',
        }}
      >
        <VaultPanel
          lockOpen={lockOpen}
          fps={fps}
          localFrame={localFrame}
          keyLabel={KEY_LABEL}
          keyValue={`${typedShown}${bulletsShown}${tailShown}`}
        />
        {/* Lock-snap flash — bright ring expanding out from the lock
            icon at the exact moment it closes. The lock icon itself
            sits at the vault panel's header (top-left), so the flash
            is anchored there. */}
        {lockFlash > 0.02 && (
          <div
            style={{
              position: 'absolute',
              left: 24,
              top: 22,
              width: 22,
              height: 22,
              borderRadius: 11,
              pointerEvents: 'none',
            }}
          >
            <div
              style={{
                position: 'absolute',
                inset: 0,
                borderRadius: '50%',
                border: `2px solid ${C.accent}`,
                transform: `scale(${1 + lockFlash * 6})`,
                opacity: lockFlash * (1 - lockFlash) * 4,
                transformOrigin: 'center',
              }}
            />
            <div
              style={{
                position: 'absolute',
                inset: 0,
                borderRadius: '50%',
                boxShadow: `0 0 ${20 * lockFlash}px ${10 * lockFlash}px rgba(99, 102, 241, ${0.55 * lockFlash})`,
                opacity: lockFlash,
              }}
            />
          </div>
        )}
      </div>

      {/* AES-256-GCM stamp */}
      {aesIn > 0.02 && (
        <div
          style={{
            position: 'absolute',
            left: 1040,
            top: (1080 - 380) / 2 - 50,
            opacity: Math.min(aesIn, vaultLateOpacity),
            transform: `translateY(${interpolate(aesIn, [0, 1], [10, 0])}px) scale(${interpolate(aesIn, [0, 1], [0.8, 1])})`,
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 12,
            letterSpacing: 2,
            textTransform: 'uppercase',
            color: C.accent,
            border: `1px solid ${C.accent}`,
            backgroundColor: 'rgba(99,102,241,0.10)',
            borderRadius: 999,
            padding: '6px 14px',
            boxShadow: `0 4px 14px rgba(99,102,241,0.30)`,
          }}
        >
          AES-256-GCM
        </div>
      )}

      {/* Arrow fork SVG (fades out after initial beat) */}
      <div style={{ opacity: arrowFadeOut * vaultLateOpacity }}>
        <ArrowFork progress={arrowProgress} xPop={xMarkPop} />
      </div>

      {/* Vault → container connector — appears as the chip starts its
          flight, so the eye reads "the key in the vault BECOMES the
          env var in the container." Without this the chip-flight
          reads as "thing moves" rather than "vault feeds container". */}
      <VaultContainerConnector alpha={connectorAlpha} />

      {/* New beat: container outline + env-var injection */}
      {containerIn > 0.02 && (
        <ContainerWithEnvVar
          containerProgress={containerIn}
          injectProgress={injectProgress}
        />
      )}

      {/* AI-view vs your-stack reveal — replaces the earlier "flicker
          back" beat. Two compact callouts that frame the architectural
          contrast: what the AI saw (just the prompt sentence), versus
          what your stack holds (vault + container with the secret).
          Both labelled, both readable. Appears late and stays until
          scene-exit. */}
      <AiViewVsStack localFrame={localFrame} />
    </div>
  );
}

function VaultContainerConnector({ alpha }: { alpha: number }) {
  if (alpha < 0.01) return null;
  // Vault sits at (1040–1860, ~350–730). Container sits at (720–1200,
  // 720–920). Draw a dashed indigo curve from the vault's bottom-left
  // (about 1100, 720) to the container's right-top env slot
  // (about 1200, 850). Slight arc for visual interest.
  return (
    <svg
      width={1920}
      height={1080}
      style={{ position: 'absolute', inset: 0, pointerEvents: 'none', opacity: alpha }}
    >
      <path
        d="M 1140 720 Q 1180 800 1200 850"
        stroke={C.accent}
        strokeWidth={1.8}
        strokeDasharray="6 6"
        fill="none"
        opacity={0.75}
      />
    </svg>
  );
}

function AiViewVsStack({ localFrame }: { localFrame: number }) {
  // Appears at FLICKER_PROMPT_AT (300) after the env-var has landed
  // in the container — once both halves of the stack exist on screen,
  // we can label them. Designed in v10 to be more visually emphatic
  // than the previous compact card: literal viewfinder bracket corners
  // around the prompt text frame the idea "this is everything the
  // model can see," plus a stronger three-line "denied list" below.
  const inT = clampLerp(localFrame, FLICKER_PROMPT_AT, FLICKER_PROMPT_AT + 22);
  if (inT < 0.02) return null;
  const fadeOut = 1 - clampLerp(localFrame, SCENE_LEN - 24, SCENE_LEN - 8);
  const opacity = inT * fadeOut;

  const promptShortened = '“Build me an MCP server that searches our Notion workspace.”';

  // Bigger, more central. Width 680 (was 560), positioned so the
  // viewfinder frame is the dominant left-side element.
  const W = 680;
  const LEFT = 60;
  const TOP = 180;

  return (
    <div style={{ position: 'absolute', left: LEFT, top: TOP, width: W, opacity }}>
      {/* Header — "what the AI sees" with eye icon for instant readability */}
      <div
        style={{
          fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
          fontSize: 12,
          letterSpacing: 3.5,
          textTransform: 'uppercase',
          color: C.fgSubtle,
          marginBottom: 16,
          display: 'flex',
          alignItems: 'center',
          gap: 12,
        }}
      >
        <EyeIcon />
        what the AI sees
      </div>

      {/* Viewfinder frame: prompt text surrounded by four corner brackets
          to read as "this is the entire field of view." */}
      <div
        style={{
          position: 'relative',
          padding: '28px 32px',
          backgroundColor: 'rgba(20,20,24,0.80)',
          borderRadius: 14,
          fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
          fontSize: 18,
          color: C.fg,
          lineHeight: 1.5,
          letterSpacing: 0.2,
        }}
      >
        <ViewfinderCorners />
        <span style={{ color: C.fgMuted }}>{promptShortened}</span>
      </div>

      {/* Denied list — three explicit "not in view" lines so the
          contrast with the stack on the right reads even on a quick
          scrub. */}
      <div
        style={{
          marginTop: 18,
          display: 'flex',
          flexDirection: 'column',
          gap: 8,
        }}
      >
        {[
          'no secrets',
          'no environment variables',
          'no tokens',
        ].map((line) => (
          <div
            key={line}
            style={{
              display: 'flex',
              alignItems: 'center',
              gap: 12,
              fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
              fontSize: 14,
              letterSpacing: 0.4,
              color: '#9ca3af',
            }}
          >
            <DeniedDot />
            <span>{line}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function EyeIcon() {
  return (
    <svg width="18" height="14" viewBox="0 0 22 14" fill="none">
      <path
        d="M 1 7 Q 11 1 21 7 Q 11 13 1 7 Z"
        stroke={C.fgSubtle}
        strokeWidth={1.3}
        fill="none"
      />
      <circle cx="11" cy="7" r="3" stroke={C.fgSubtle} strokeWidth={1.3} fill="rgba(161,161,170,0.15)" />
      <circle cx="11" cy="7" r="1.4" fill={C.fgSubtle} />
    </svg>
  );
}

function ViewfinderCorners() {
  // Four L-shaped brackets at the corners of the parent frame. SVG
  // path d-attributes do NOT accept CSS calc(); instead each corner is
  // a separate <div> sized in pixels with two borders showing through.
  // Positioned absolutely to the frame's inside, 4 px from each edge.
  const armLen = 18;
  const stroke = 1.6;
  const colour = C.fgSubtle;
  const opacity = 0.75;
  const inset = 4;
  const cornerStyle = {
    position: 'absolute' as const,
    width: armLen,
    height: armLen,
    pointerEvents: 'none' as const,
    opacity,
  };
  return (
    <>
      {/* top-left */}
      <div
        style={{
          ...cornerStyle,
          top: inset,
          left: inset,
          borderTop: `${stroke}px solid ${colour}`,
          borderLeft: `${stroke}px solid ${colour}`,
        }}
      />
      {/* top-right */}
      <div
        style={{
          ...cornerStyle,
          top: inset,
          right: inset,
          borderTop: `${stroke}px solid ${colour}`,
          borderRight: `${stroke}px solid ${colour}`,
        }}
      />
      {/* bottom-left */}
      <div
        style={{
          ...cornerStyle,
          bottom: inset,
          left: inset,
          borderBottom: `${stroke}px solid ${colour}`,
          borderLeft: `${stroke}px solid ${colour}`,
        }}
      />
      {/* bottom-right */}
      <div
        style={{
          ...cornerStyle,
          bottom: inset,
          right: inset,
          borderBottom: `${stroke}px solid ${colour}`,
          borderRight: `${stroke}px solid ${colour}`,
        }}
      />
    </>
  );
}

function DeniedDot() {
  return (
    <svg width="16" height="16" viewBox="0 0 18 18" fill="none">
      <circle cx="9" cy="9" r="6.5" stroke="#9ca3af" strokeWidth={1.4} fill="rgba(156,163,175,0.08)" />
      <path d="M 5 9 L 13 9" stroke="#9ca3af" strokeWidth={1.6} strokeLinecap="round" />
    </svg>
  );
}

// ------- Sub-components below -------

function PromptMiniPanel({ x, promptText }: { x: number; promptText: string }) {
  return (
    <div
      style={{
        position: 'absolute',
        left: x,
        top: (1080 - 220) / 2,
        width: 760,
      }}
    >
      <div
        style={{
          fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
          fontSize: 14,
          letterSpacing: 4,
          textTransform: 'uppercase',
          color: C.fgSubtle,
          marginBottom: 18,
        }}
      >
        prompt
      </div>
      <div
        style={{
          width: '100%',
          minHeight: 92,
          borderRadius: 14,
          backgroundColor: C.bgElevated,
          border: `1px solid ${C.borderStrong}`,
          boxShadow: `0 16px 50px rgba(0,0,0,0.5)`,
          padding: '22px 28px',
          display: 'flex',
          alignItems: 'center',
        }}
      >
        <div
          style={{
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 22,
            color: C.accent,
            marginRight: 16,
            lineHeight: 1,
          }}
        >
          ›
        </div>
        <div
          style={{
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 20,
            color: C.fg,
            letterSpacing: 0.2,
            lineHeight: 1.4,
          }}
        >
          {promptText}
        </div>
      </div>
      <div
        style={{
          marginTop: 16,
          display: 'flex',
          alignItems: 'center',
          gap: 10,
          fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
          fontSize: 12,
          color: C.fgSubtle,
          letterSpacing: 1,
          textTransform: 'uppercase',
        }}
      >
        <BrainIcon size={14} />
        <span>sent to LLM</span>
      </div>
    </div>
  );
}

function VaultPanel({
  lockOpen,
  fps,
  localFrame,
  keyLabel,
  keyValue,
}: {
  lockOpen: boolean;
  fps: number;
  localFrame: number;
  keyLabel: string;
  keyValue: string;
}) {
  return (
    <div
      style={{
        width: '100%',
        backgroundColor: C.bgElevated,
        border: `2px solid ${C.accentDim}`,
        borderRadius: 16,
        padding: '26px 32px 28px 32px',
        boxShadow: `0 0 0 4px rgba(79, 70, 229, 0.15), 0 24px 70px rgba(0,0,0,0.55)`,
        position: 'relative',
      }}
    >
      <div
        style={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'space-between',
          marginBottom: 22,
        }}
      >
        <div
          style={{
            display: 'flex',
            alignItems: 'center',
            gap: 14,
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 14,
            letterSpacing: 3,
            textTransform: 'uppercase',
            color: C.fgMuted,
          }}
        >
          <LockIcon open={lockOpen} fps={fps} localFrame={localFrame} />
          <span>secrets · encrypted vault</span>
        </div>
        <div
          style={{
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 11,
            letterSpacing: 1.5,
            textTransform: 'uppercase',
            color: C.accent,
            border: `1px solid ${C.accentDim}`,
            padding: '4px 10px',
            borderRadius: 999,
            backgroundColor: 'rgba(99, 102, 241, 0.08)',
          }}
        >
          backend-only
        </div>
      </div>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
        <div
          style={{
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 13,
            color: C.fgSubtle,
            letterSpacing: 1.2,
          }}
        >
          {keyLabel}
        </div>
        <div
          style={{
            width: '100%',
            height: 70,
            borderRadius: 10,
            backgroundColor: C.bgSubtle,
            border: `1px solid ${C.border}`,
            display: 'flex',
            alignItems: 'center',
            padding: '0 22px',
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 22,
            color: C.fg,
            letterSpacing: 1,
            overflow: 'hidden',
          }}
        >
          <span>{keyValue}</span>
        </div>
      </div>

      <div
        style={{
          marginTop: 18,
          display: 'flex',
          alignItems: 'center',
          gap: 10,
          fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
          fontSize: 13,
          color: C.fgMuted,
          letterSpacing: 0.5,
        }}
      >
        <ShieldIcon />
        <span>
          AES-256 encrypted ·{' '}
          <span style={{ color: C.fg, fontWeight: 600 }}>never sent to the AI</span>
        </span>
      </div>
    </div>
  );
}

function LockIcon({
  open,
  fps,
  localFrame,
}: {
  open: boolean;
  fps: number;
  localFrame: number;
}) {
  const snapAt = 66;
  const snap = springIn(localFrame, fps, snapAt);
  const shackleY = open ? -3 : interpolate(snap, [0, 1], [-3, 0]);

  return (
    <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
      <path
        d={
          open
            ? `M 7 12 L 7 8 A 5 5 0 0 1 17 8 L 17 9`
            : `M 7 12 L 7 8 A 5 5 0 0 1 17 8 L 17 12`
        }
        stroke={C.accent}
        strokeWidth={2}
        strokeLinecap="round"
        fill="none"
        transform={`translate(0 ${shackleY})`}
      />
      <rect x="5" y="11" width="14" height="10" rx="2" stroke={C.accent} strokeWidth={2} fill="rgba(99,102,241,0.12)" />
      <circle cx="12" cy="15.5" r="1.4" fill={C.accent} />
      <rect x="11.5" y="16" width="1" height="3" fill={C.accent} />
    </svg>
  );
}

function ShieldIcon() {
  return (
    <svg width="14" height="14" viewBox="0 0 16 16" fill="none">
      <path
        d="M 8 1.5 L 13.5 3.5 L 13.5 8 Q 13.5 12 8 14.5 Q 2.5 12 2.5 8 L 2.5 3.5 Z"
        stroke={C.accent}
        strokeWidth={1.4}
        fill="rgba(99,102,241,0.10)"
        strokeLinejoin="round"
      />
      <path d="M 5.5 8 L 7.2 9.6 L 10.5 6.4" stroke={C.accent} strokeWidth={1.4} fill="none" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

function BrainIcon({ size = 14 }: { size?: number }) {
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" fill="none">
      <circle cx="8" cy="8" r="5" stroke={C.fgMuted} strokeWidth={1.2} fill="rgba(161,161,170,0.08)" />
      <path d="M 5.5 8 Q 8 5.5 10.5 8 Q 8 10.5 5.5 8 Z" stroke={C.fgMuted} strokeWidth={1.2} fill="none" />
      <circle cx="8" cy="8" r="1" fill={C.fgMuted} />
    </svg>
  );
}

function ArrowFork({ progress, xPop }: { progress: number; xPop: number }) {
  const LLM = { x: 960, y: 240 };
  const VAULT_OUT = { x: 1040, y: 520 };
  const PROMPT_OUT = { x: 820, y: 540 };
  const CONVERGE = { x: 960, y: 920 };

  const pathP = `M ${PROMPT_OUT.x} ${PROMPT_OUT.y} L 880 ${PROMPT_OUT.y} L 880 ${LLM.y + 30} L ${LLM.x} ${LLM.y + 30}`;
  const pathPCont = `M ${LLM.x + 30} ${LLM.y + 30} L 1040 ${LLM.y + 30} L 1040 ${CONVERGE.y - 60} L ${CONVERGE.x} ${CONVERGE.y - 60}`;
  const pathS = `M ${VAULT_OUT.x} ${VAULT_OUT.y} L 900 ${VAULT_OUT.y} L 900 ${CONVERGE.y - 30} L ${CONVERGE.x} ${CONVERGE.y - 30}`;

  const lenP = 480;
  const lenPCont = 750;
  const lenS = 610;

  return (
    <svg
      width={1920}
      height={1080}
      style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}
    >
      <defs>
        <marker id="arrowAccent" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 0 L 10 5 L 0 10 Z" fill={C.accent} />
        </marker>
        <marker id="arrowMuted" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 0 L 10 5 L 0 10 Z" fill={C.fgSubtle} />
        </marker>
      </defs>

      <path
        d={pathP}
        stroke={C.accent}
        strokeWidth={2.5}
        fill="none"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeDasharray={lenP}
        strokeDashoffset={(1 - progress) * lenP}
        opacity={progress > 0 ? 1 : 0}
      />

      {progress > 0.4 && (
        <g transform={`translate(${LLM.x - 28} ${LLM.y - 4})`} opacity={Math.min(1, (progress - 0.4) * 4)}>
          <rect width="56" height="56" rx="12" fill={C.bgElevated} stroke={C.accent} strokeWidth={1.5} />
          <circle cx="28" cy="28" r="14" stroke={C.accent} strokeWidth={1.5} fill="rgba(99,102,241,0.10)" />
          <path d="M 20 28 Q 28 20 36 28 Q 28 36 20 28 Z" stroke={C.accent} strokeWidth={1.5} fill="none" />
          <circle cx="28" cy="28" r="2.5" fill={C.accent} />
          <text x="28" y="74" textAnchor="middle" fill={C.fgMuted} fontSize="13" fontFamily="ui-monospace, SF Mono, Menlo, monospace" letterSpacing="2">
            LLM
          </text>
        </g>
      )}

      <path
        d={pathPCont}
        stroke={C.accent}
        strokeWidth={2.5}
        fill="none"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeDasharray={lenPCont}
        strokeDashoffset={(1 - Math.max(0, (progress - 0.5) * 2)) * lenPCont}
        opacity={progress > 0.5 ? 1 : 0}
        markerEnd="url(#arrowAccent)"
      />

      <path
        d={pathS}
        stroke={C.fgSubtle}
        strokeWidth={2.5}
        fill="none"
        strokeLinecap="round"
        strokeLinejoin="round"
        strokeDasharray="8 6"
        opacity={progress > 0 ? interpolate(progress, [0, 0.3, 1], [0, 0.5, 0.9]) : 0}
        markerEnd="url(#arrowMuted)"
      />

      {xPop > 0.05 && (
        <g
          transform={`translate(${LLM.x + 28} ${LLM.y + 24}) scale(${0.5 + xPop * 0.5})`}
          opacity={xPop}
        >
          <circle r="16" fill={C.bg} stroke="#dc2626" strokeWidth={2} />
          <path d="M -6 -6 L 6 6 M 6 -6 L -6 6" stroke="#dc2626" strokeWidth={2.5} strokeLinecap="round" />
        </g>
      )}

      {progress > 0.1 && (
        <g transform={`translate(${VAULT_OUT.x - 36} ${VAULT_OUT.y - 22})`} opacity={Math.min(1, (progress - 0.1) * 4)}>
          <rect width="56" height="44" rx="8" fill={C.bgElevated} stroke={C.accentDim} strokeWidth={1.5} />
          <path d="M 19 22 L 19 16 A 9 9 0 0 1 37 16 L 37 22" stroke={C.accent} strokeWidth={1.5} fill="none" />
          <rect x="17" y="21" width="22" height="14" rx="2" fill="rgba(99,102,241,0.18)" stroke={C.accent} strokeWidth={1.5} />
          <circle cx="28" cy="27" r="1.6" fill={C.accent} />
          <text x="28" y="62" textAnchor="middle" fill={C.fgMuted} fontSize="12" fontFamily="ui-monospace, SF Mono, Menlo, monospace" letterSpacing="2">
            vault
          </text>
        </g>
      )}
    </svg>
  );
}

function ContainerWithEnvVar({
  containerProgress,
  injectProgress,
}: {
  containerProgress: number;
  injectProgress: number;
}) {
  // Container outline sits below-center of the canvas (around y ~720), the
  // vault is up at y ~520. The chip flies from the vault center down into
  // the container's "env" slot.
  const containerX = 1920 / 2 - 240;
  const containerY = 720;
  const containerW = 480;
  const containerH = 200;

  // Chip origin (vault center): roughly (1450, 540).
  const ORIGIN = { x: 1450, y: 520 };
  // Chip destination (env slot inside container): (containerX + 240, containerY + 130)
  const DEST = { x: containerX + 240, y: containerY + 130 };

  const chipX = interpolate(injectProgress, [0, 1], [ORIGIN.x, DEST.x]);
  const chipY = interpolate(injectProgress, [0, 1], [ORIGIN.y, DEST.y]);
  // Arc trajectory: gentle parabola
  const arc = Math.sin(injectProgress * Math.PI) * -40;

  // Chip scales down slightly as it travels.
  const chipScale = interpolate(injectProgress, [0, 1], [1.0, 0.85]);

  const opacity = clampLerp(containerProgress, 0, 1);
  const scale = interpolate(containerProgress, [0, 1], [0.95, 1]);

  // Once chip has landed, the env-var "slot" inside the container glows.
  const slotGlow = clampLerp(injectProgress, 0.85, 1);

  return (
    <>
      {/* Container outline */}
      <div
        style={{
          position: 'absolute',
          left: containerX,
          top: containerY,
          width: containerW,
          height: containerH,
          opacity,
          transform: `scale(${scale})`,
          transformOrigin: 'center center',
        }}
      >
        {/* Label */}
        <div
          style={{
            position: 'absolute',
            top: -28,
            left: 0,
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 11,
            letterSpacing: 3,
            textTransform: 'uppercase',
            color: C.fgSubtle,
          }}
        >
          container · runtime
        </div>
        <div
          style={{
            width: '100%',
            height: '100%',
            border: `1.5px dashed ${C.borderStrong}`,
            borderRadius: 14,
            backgroundColor: 'rgba(17,17,20,0.6)',
            padding: '24px 28px',
            display: 'flex',
            flexDirection: 'column',
            justifyContent: 'center',
            gap: 14,
          }}
        >
          <div
            style={{
              fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
              fontSize: 13,
              color: C.fgSubtle,
              letterSpacing: 1.5,
              textTransform: 'uppercase',
            }}
          >
            env
          </div>
          <div
            style={{
              borderRadius: 10,
              border: `1px solid ${slotGlow > 0.2 ? C.accent : C.border}`,
              backgroundColor: slotGlow > 0.2 ? 'rgba(99,102,241,0.10)' : C.bgSubtle,
              padding: '14px 18px',
              fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
              fontSize: 18,
              color: C.fg,
              boxShadow: slotGlow > 0.2 ? `0 0 ${10 + slotGlow * 14}px rgba(99,102,241,${0.25 + slotGlow * 0.20})` : 'none',
              transition: 'none',
            }}
          >
            <span style={{ color: C.fgMuted }}>key=</span>
            <span>{KEY_LABEL}</span>
            <span style={{ color: C.fgMuted, marginLeft: 8 }}>
              {slotGlow > 0.4 ? '•••••••3a8f' : '...'}
            </span>
          </div>
        </div>
      </div>

      {/* Flying chip (only visible during injection) */}
      {injectProgress > 0.02 && injectProgress < 0.98 && (
        <div
          style={{
            position: 'absolute',
            left: chipX,
            top: chipY + arc,
            transform: `translate(-50%, -50%) scale(${chipScale})`,
            display: 'flex',
            alignItems: 'center',
            gap: 8,
            padding: '8px 14px',
            backgroundColor: 'rgba(99,102,241,0.18)',
            border: `1px solid ${C.accent}`,
            borderRadius: 999,
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 13,
            color: C.fg,
            boxShadow: `0 0 18px rgba(99,102,241,0.40)`,
            whiteSpace: 'nowrap',
          }}
        >
          <span style={{ color: C.accent }}>🔒</span>
          <span>{KEY_LABEL}</span>
        </div>
      )}
    </>
  );
}
