{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "kill-feed",
  "title": "Kill Feed",
  "description": "A kill-feed of eliminations builds to a big gradient 'PENTA SHIP' banner.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/kill-feed/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont } from \"@remotion/google-fonts/ChakraPetch\";\nimport { loadFont as loadManrope } from \"@remotion/google-fonts/Manrope\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nconst { fontFamily: chakra } = loadFont(\"normal\", {\n  weights: [\"500\"],\n  subsets: [\"latin\"],\n});\nconst { fontFamily: chakraItalic } = loadFont(\"italic\", {\n  weights: [\"500\"],\n  subsets: [\"latin\"],\n});\nconst manrope = loadManrope(\"normal\", {\n  weights: [\"600\"],\n  subsets: [\"latin\"],\n});\n\nconst REMOCN_R_PATH =\n  \"M 0.01 0.81 C 0.01 1.73, 0.36 2.79, 1.09 4.13 C 4.91 11.04, 13.45 17.16, 21.7 18.9 C 22.94 19.16, 23.18 19.16, 51.39 19.27 C 76.07 19.36, 80.02 19.4, 81.32 19.57 C 89.89 20.69, 96.2 24.68, 99.38 31.01 C 103.19 38.56, 102.53 50.1, 97.91 57.07 C 94.66 61.96, 89.68 64.99, 83.26 66 C 82.81 66.07, 70.18 66.83, 55.2 67.69 C 24.82 69.43, 27.03 69.23, 24.18 70.4 C 19.9 72.15, 14.84 75.7, 10.65 79.89 C 4.86 85.68, 1.3 91.91, 0.25 98.13 C 0.12 98.85, 0.08 103.13, 0.04 116.66 L 0 134.26 9.5 134.26 L 19 134.26 19.05 119.41 C 19.1 103.27, 19.08 103.82, 19.82 101.04 C 21.79 93.65, 27.86 88.75, 36.45 87.63 C 37.23 87.53, 39.41 87.5, 43.57 87.53 C 50.12 87.59, 50.75 87.65, 53.22 88.45 C 56.61 89.56, 59.67 91.86, 62.02 95.07 C 62.52 95.76, 69.35 103.85, 77.2 113.07 C 85.04 122.28, 91.63 130.04, 91.85 130.32 C 92.07 130.59, 92.5 131.34, 92.82 131.97 C 93.52 133.35, 94.11 133.93, 95.06 134.13 C 95.5 134.23, 98.97 134.26, 106.36 134.23 L 117.01 134.19 100.82 113.07 C 91.91 101.45, 84.52 91.78, 84.39 91.57 C 83.36 89.89, 83.66 87.53, 85.09 86 C 85.79 85.25, 86.36 84.94, 88.07 84.38 C 96.18 81.72, 104.15 76.62, 109.97 70.36 C 120.59 58.93, 124.06 44.32, 119.44 30.43 C 114.59 15.81, 101.93 4.02, 87.64 0.83 C 83.93 0.01, 88.09 0.08, 41.85 0.04 L 0.01 0 0.01 0.81 Z\";\n\n/** Full logo as on remocn.dev: R mark (0.76em tall, baseline-aligned) + \"emocn\" in Manrope 600. */\nfunction RemocnLogo({ fontSize, color }: { fontSize: number; color: string }) {\n  return (\n    <div\n      style={{\n        display: \"flex\",\n        alignItems: \"baseline\",\n        color,\n        fontFamily: manrope.fontFamily,\n        fontWeight: 600,\n        fontSize,\n        letterSpacing: \"-0.025em\",\n        lineHeight: 1,\n      }}\n    >\n      <svg\n        viewBox=\"0 0 124.06 134.26\"\n        style={{ height: \"0.76em\", width: \"auto\" }}\n        aria-hidden\n      >\n        <path fill=\"currentColor\" d={REMOCN_R_PATH} />\n      </svg>\n      <span style={{ marginLeft: \"0.04em\" }}>emocn</span>\n    </div>\n  );\n}\n\nexport interface KillFeedProps {\n  /** One feed line per elimination, in order. remocn is always the one getting the kill. */\n  kills?: string[];\n  /** The streak callout after each kill, bottom left. The last kill fires the banner instead. */\n  callouts?: string[];\n  /** The multi-kill banner that slams across the middle after the last kill. */\n  banner?: string;\n  /** Small line under the banner, next to the logo. */\n  caption?: string;\n  /** Small markers: the streak tick and the caption rule. */\n  accent?: string;\n  theme?: \"dark\" | \"light\";\n  speed?: number;\n}\n\n// Beat sheet (internal clock = frame + 3, 30fps, speed 1, five kills):\n//   2–48   five kill lines slide into the feed (top right) from the right edge,\n//          gaps tightening 14 → 8 frames; each lands with a white flash, older\n//          lines settle to a dimmer level\n//   3–49   the streak callout bottom left rolls up to the next one on every\n//          kill: First blood → Double kill → Triple kill → Quadra kill\n//  52–59   the fifth kill fires the \"halo\" band: it slams across the middle,\n//          the frame kicks, the feed sinks back (lines under the band go)\n//  63–74   logo and caption rise under the band; poster holds\nconst FIRST_KILL = 2;\nconst KILL_GAPS = [14, 12, 10, 8];\nconst BANNER_DELAY = 4;\nconst BAND_IN = 7;\n\nconst SLAM = Easing.bezier(0.12, 0.9, 0.25, 1);\nconst OUT = Easing.bezier(0.22, 1, 0.36, 1);\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\n\n// gradients.sh \"halo\": bg #030306, [#fff4ec, #ffb36b, #e2457a, #3d4dff].\nconst HALO = [\"#FFF4EC\", \"#FFB36B\", \"#E2457A\", \"#3D4DFF\"] as const;\nconst BAND = `linear-gradient(100deg, #2A34C9 0%, ${HALO[3]} 22%, #8C47B8 48%, ${HALO[2]} 72%, #EE6F5E 90%, ${HALO[1]} 100%)`;\nconst CHIP = `linear-gradient(100deg, ${HALO[3]} 0%, #7B48C2 70%, ${HALO[2]} 130%)`;\n\nconst THEMES = {\n  dark: {\n    stage: \"#08080A\",\n    slab: \"#17171B\",\n    slabEdge: \"#232329\",\n    ink: \"#F3F2EF\",\n    victim: \"#FF8DA6\",\n    dim: \"#86858C\",\n    callout: \"#F3F2EF\",\n  },\n  light: {\n    stage: \"#F5F4F0\",\n    slab: \"#FFFFFF\",\n    slabEdge: \"#E3E1DA\",\n    ink: \"#121214\",\n    victim: \"#D3305F\",\n    dim: \"#7E7D77\",\n    callout: \"#121214\",\n  },\n} as const;\n\n// Flat weapon-ish glyphs, all in a 80 × 28 box.\nconst WEAPONS = [\n  // rifle\n  \"M3 9 L13 8 L17 10 L50 10 L52 7.5 L60 7.5 L60 10 L78 10 L78 13 L60 13 L57 15.5 L42 15.5 L38 24 L31.5 24 L34 15.5 L24 15.5 L17 21 L3 21 Z\",\n  // blade\n  \"M3 12.5 L19 11 L21 7.5 L26 7.5 L26 11 L70 11.5 L78 14 L70 17 L26 17 L26 20.5 L21 20.5 L19 17 L3 15.5 Z\",\n  // sniper with scope\n  \"M2 11.5 L15 11 L22 13 L56 13 L56 12 L79 12 L79 14.5 L56 14.5 L53 17 L37 17 L33 23 L27 23 L29.5 17 L19 17 L12 21 L2 21 Z M31 6.5 L51 6.5 L51 10.5 L31 10.5 Z\",\n  // sidearm\n  \"M18 6 L62 6 L62 13 L42 13 L40 15.5 L35 15.5 L37.5 24.5 L27 24.5 L25 15.5 L18 13 Z\",\n  // ability bolt\n  \"M45 2 L29 15.5 L39 15.5 L33 26 L52 11 L42 11 L49 2 Z\",\n] as const;\n\nfunction ramp(t: number, from: number, len: number) {\n  return interpolate(t, [from, from + len], [0, 1], CLAMP);\n}\n\n/** A hit: up in 1 frame, eases back over `len`. */\nfunction kick(d: number, len = 8) {\n  if (d < 0) return 0;\n  if (d < 1) return d;\n  const k = Math.max(0, 1 - (d - 1) / len);\n  return k * k;\n}\n\nfunction Crosshair({ size, color }: { size: number; color: string }) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 24 24\" aria-hidden>\n      <circle\n        cx=\"12\"\n        cy=\"12\"\n        r=\"7.5\"\n        fill=\"none\"\n        stroke={color}\n        strokeWidth=\"2.4\"\n      />\n      <path\n        d=\"M12 1.5 V7 M12 17 V22.5 M1.5 12 H7 M17 12 H22.5\"\n        stroke={color}\n        strokeWidth=\"2.4\"\n      />\n    </svg>\n  );\n}\n\nexport function KillFeed({\n  kills = [\n    \"Manual keyframes\",\n    \"Render queue\",\n    \"Agency invoice\",\n    \"Stock templates\",\n    \"Export presets\",\n  ],\n  callouts = [\"First blood\", \"Double kill\", \"Triple kill\", \"Quadra kill\"],\n  banner = \"Penta ship\",\n  caption = \"5 / 5 eliminated\",\n  accent = \"#3B5BFD\",\n  theme = \"dark\",\n  speed = 1,\n}: KillFeedProps) {\n  // Starts three frames in, so frame 0 already has the first line on its way.\n  const t = useCurrentFrame() * speed + 3;\n  const { width: W, height: H } = useVideoConfig();\n  const c = THEMES[theme];\n  const u = Math.min(W / 1280, H / 720);\n\n  const killAt: number[] = [];\n  kills.forEach((_, i) => {\n    killAt.push(\n      i === 0\n        ? FIRST_KILL\n        : killAt[i - 1] + (KILL_GAPS[i - 1] ?? KILL_GAPS[KILL_GAPS.length - 1]),\n    );\n  });\n  const lastKill = killAt[killAt.length - 1] ?? FIRST_KILL;\n  const bandAt = lastKill + BANNER_DELAY;\n  const landAt = bandAt + BAND_IN;\n\n  // ── Camera kick on every kill and on the band landing ─────────────────────\n  const killKick = killAt.reduce((a, k) => Math.max(a, kick(t - k - 2, 6)), 0);\n  const bandKick = kick(t - landAt + 1, 9);\n  const shakeX =\n    bandKick * 9 * u * Math.sin(t * 2.7) + killKick * 2 * u * Math.sin(t * 3.1);\n  const shakeY = bandKick * 5 * u * Math.cos(t * 3.3);\n\n  // ── Feed ──────────────────────────────────────────────────────────────────\n  const lineH = 74 * u;\n  const lineGap = 11 * u;\n  const feedRight = 52 * u;\n  const feedTop = 46 * u;\n  const fs = 35 * u;\n  const cut = 16 * u;\n\n  // ── Callout ───────────────────────────────────────────────────────────────\n  const calloutFs = 124 * u;\n  const calloutLeft = 60 * u;\n  const calloutBottom = 48 * u;\n  const calloutH = calloutFs * 1.08 + 34 * u;\n\n  // ── Band ──────────────────────────────────────────────────────────────────\n  const bandH = 256 * u;\n  const bandY = H / 2 - bandH / 2 + 14 * u;\n  const slant = 0.24 * bandH;\n  const bandIn = SLAM(ramp(t, bandAt, BAND_IN));\n  const bandX = -(W + 2 * slant) * (1 - bandIn);\n  const textIn = OUT(ramp(t, bandAt + 1, 14));\n  const textX = -0.22 * W * (1 - textIn);\n  const textScale = 1 + 0.1 * (1 - textIn);\n  const capIn = OUT(ramp(t, landAt + 4, 11));\n  const flash = kick(t - landAt + 1, 5);\n\n  return (\n    <AbsoluteFill style={{ background: c.stage, overflow: \"hidden\" }}>\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 0,\n          transform: `translate(${shakeX}px, ${shakeY}px)`,\n        }}\n      >\n        {/* Kill feed, top right. New lines join at the bottom. */}\n        <div\n          style={{\n            position: \"absolute\",\n            inset: 0,\n          }}\n        >\n          {kills.map((victim, i) => {\n            const at = killAt[i];\n            if (t < at) return null;\n            const p = SLAM(ramp(t, at, 6));\n            const sink = OUT(ramp(t, bandAt + 2 + i, 10));\n            const hit = kick(t - at - 4, 7);\n            const next = killAt[i + 1];\n            const op = next === undefined ? 1 : 1 - 0.42 * ramp(t, next, 8);\n            const weapon = WEAPONS[i % WEAPONS.length];\n            const headshot = i === 1 || i === kills.length - 1;\n            return (\n              <div\n                key={`${i}-${victim}`}\n                style={{\n                  position: \"absolute\",\n                  right: feedRight,\n                  top: feedTop + i * (lineH + lineGap),\n                  height: lineH,\n                  display: \"flex\",\n                  alignItems: \"stretch\",\n                  transform: `translateX(${(1 - p) * 115}%)`,\n                  opacity:\n                    op *\n                    (1 -\n                      (feedTop + (i + 1) * (lineH + lineGap) > bandY\n                        ? 1\n                        : 0.62) *\n                        sink),\n                  clipPath: `polygon(${cut}px 0, 100% 0, 100% 100%, 0 100%)`,\n                  background: c.slab,\n                  boxShadow: `inset 0 0 0 ${1 * u}px ${c.slabEdge}`,\n                }}\n              >\n                <div\n                  style={{\n                    display: \"flex\",\n                    alignItems: \"center\",\n                    padding: `0 ${22 * u}px 0 ${30 * u}px`,\n                    background: CHIP,\n                  }}\n                >\n                  <RemocnLogo fontSize={36 * u} color=\"#FFFFFF\" />\n                </div>\n                <div\n                  style={{\n                    display: \"flex\",\n                    alignItems: \"center\",\n                    gap: 10 * u,\n                    padding: `0 ${22 * u}px`,\n                  }}\n                >\n                  <svg\n                    width={92 * u}\n                    height={(92 * u * 28) / 80}\n                    viewBox=\"0 0 80 28\"\n                    aria-hidden\n                  >\n                    <path d={weapon} fill={c.ink} />\n                  </svg>\n                  {headshot ? <Crosshair size={26 * u} color={c.ink} /> : null}\n                </div>\n                <div\n                  style={{\n                    display: \"flex\",\n                    alignItems: \"center\",\n                    paddingRight: 26 * u,\n                    fontFamily: chakra,\n                    fontWeight: 500,\n                    fontSize: fs,\n                    letterSpacing: \"-0.005em\",\n                    color: c.victim,\n                    whiteSpace: \"nowrap\",\n                  }}\n                >\n                  {victim}\n                </div>\n                <div\n                  style={{\n                    position: \"absolute\",\n                    inset: 0,\n                    background: \"#FFFFFF\",\n                    opacity: 0.6 * hit + (1 - p) * 0.5,\n                  }}\n                />\n              </div>\n            );\n          })}\n        </div>\n\n        {/* Streak callout, bottom left: rolls up to the next one on every kill. */}\n        <div\n          style={{\n            position: \"absolute\",\n            left: calloutLeft,\n            bottom: calloutBottom,\n            width: W - 2 * calloutLeft,\n            height: calloutH,\n            overflow: \"hidden\",\n          }}\n        >\n          {callouts.map((text, i) => {\n            const at = killAt[i];\n            if (at === undefined || t < at) return null;\n            const exitAt = i + 1 < callouts.length ? killAt[i + 1] : bandAt - 3;\n            const inP = SLAM(ramp(t, at + 1, 7));\n            const outP =\n              exitAt === undefined ? 0 : SLAM(ramp(t, exitAt + 1, 7));\n            if (outP >= 1) return null;\n            return (\n              <div\n                key={`${i}-${text}`}\n                style={{\n                  position: \"absolute\",\n                  left: 0,\n                  bottom: 0,\n                  height: calloutH,\n                  display: \"flex\",\n                  flexDirection: \"column\",\n                  justifyContent: \"flex-end\",\n                  transform: `translate(${(1 - inP) * 40 * u}px, ${(1 - inP - outP) * calloutH}px)`,\n                  opacity: 1 - outP,\n                }}\n              >\n                <div\n                  style={{\n                    display: \"flex\",\n                    alignItems: \"center\",\n                    gap: 14 * u,\n                    marginLeft: 8 * u,\n                    fontFamily: chakra,\n                    fontWeight: 500,\n                    fontSize: 22 * u,\n                    letterSpacing: \"0.22em\",\n                    textTransform: \"uppercase\",\n                    color: c.dim,\n                  }}\n                >\n                  <div\n                    style={{\n                      width: 26 * u,\n                      height: 6 * u,\n                      background: accent,\n                      transform: \"skewX(-20deg)\",\n                    }}\n                  />\n                  <span>Streak ×{i + 1}</span>\n                </div>\n                <div\n                  style={{\n                    fontFamily: chakraItalic,\n                    fontWeight: 500,\n                    fontStyle: \"italic\",\n                    fontSize: calloutFs,\n                    lineHeight: 1.08,\n                    letterSpacing: \"-0.02em\",\n                    textTransform: \"uppercase\",\n                    color: c.callout,\n                    whiteSpace: \"nowrap\",\n                  }}\n                >\n                  {text}\n                </div>\n              </div>\n            );\n          })}\n        </div>\n\n        {/* The multi-kill band. */}\n        {t >= bandAt ? (\n          <div\n            style={{\n              position: \"absolute\",\n              left: bandX - slant,\n              top: bandY,\n              width: W + 2 * slant,\n              height: bandH,\n              clipPath: `polygon(${slant}px 0, 100% 0, calc(100% - ${slant}px) 100%, 0 100%)`,\n              background: BAND,\n              overflow: \"hidden\",\n            }}\n          >\n            <div\n              style={{\n                position: \"absolute\",\n                left: 0,\n                right: 0,\n                top: 10 * u,\n                height: 2 * u,\n                background: HALO[0],\n                opacity: 0.55,\n              }}\n            />\n            <div\n              style={{\n                position: \"absolute\",\n                left: 0,\n                right: 0,\n                bottom: 10 * u,\n                height: 2 * u,\n                background: HALO[0],\n                opacity: 0.55,\n              }}\n            />\n            <div\n              style={{\n                position: \"absolute\",\n                inset: 0,\n                background: HALO[0],\n                opacity: 0.35 * flash,\n              }}\n            />\n            <div\n              style={{\n                position: \"absolute\",\n                inset: 0,\n                display: \"flex\",\n                alignItems: \"center\",\n                justifyContent: \"center\",\n                transform: `translateX(${textX}px) scale(${textScale})`,\n                fontFamily: chakraItalic,\n                fontStyle: \"italic\",\n                fontWeight: 500,\n                fontSize: 214 * u,\n                lineHeight: 1,\n                letterSpacing: \"-0.025em\",\n                textTransform: \"uppercase\",\n                color: \"#FFFFFF\",\n                whiteSpace: \"nowrap\",\n                paddingTop: 6 * u,\n              }}\n            >\n              {banner}\n            </div>\n          </div>\n        ) : null}\n\n        {/* Logo + caption under the band. */}\n        {capIn > 0 ? (\n          <div\n            style={{\n              position: \"absolute\",\n              left: 0,\n              right: 0,\n              top: bandY + bandH + 34 * u,\n              display: \"flex\",\n              alignItems: \"center\",\n              justifyContent: \"center\",\n              gap: 22 * u,\n              opacity: capIn,\n              transform: `translateY(${(1 - capIn) * 16 * u}px)`,\n            }}\n          >\n            <RemocnLogo fontSize={46 * u} color={c.ink} />\n            <div\n              style={{\n                width: 2 * u,\n                height: 30 * u,\n                background: accent,\n                transform: \"skewX(-20deg)\",\n              }}\n            />\n            <div\n              style={{\n                fontFamily: chakra,\n                fontWeight: 500,\n                fontSize: 28 * u,\n                letterSpacing: \"0.2em\",\n                textTransform: \"uppercase\",\n                color: c.dim,\n                paddingTop: 2 * u,\n              }}\n            >\n              {caption}\n            </div>\n          </div>\n        ) : null}\n      </div>\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/kill-feed.tsx"
    }
  ],
  "type": "registry:component"
}
