{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "score-bug",
  "title": "Score Bug",
  "description": "A broadcast score bug counts a 2-0 full-time result between remocn and the old way.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/score-bug/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont } from \"@remotion/google-fonts/BarlowCondensed\";\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: condensed } = loadFont(\"normal\", {\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 ScoreBugProps {\n  /** The opponent's name, set in caps on the right block. The left block is always the remocn logo. */\n  away?: string;\n  /** Goals remocn scores, one digit flip each. */\n  score?: number;\n  /** The opponent's score, shown from the start. */\n  awayScore?: number;\n  /** One scorer line per goal, under the remocn block. Use the prime for minutes: \"Ship 47′\". */\n  scorers?: string[];\n  /** Word on the flash band that sweeps across after the last goal. */\n  flash?: string;\n  /** Tab label while the match runs. */\n  liveLabel?: string;\n  /** Tab label once the band has passed. */\n  finalLabel?: string;\n  /** The live marker in the tab and the scorer ticks. */\n  accent?: string;\n  theme?: \"dark\" | \"light\";\n  speed?: number;\n}\n\n// Beat sheet (internal clock = frame + 2, 30fps, speed 1, score 2):\n//  -2–4    the white score core slams open from its centre line\n//   3–13   both team blocks shoot out from behind the core; names follow\n//  12–19   the \"Live\" tab rises from behind the panel\n//  30      goal: the home digit flips 0→1 (6f), the panel kicks, the remocn\n//          block flashes and the scorer line drops in under it\n//  48      goal: 1→2, same mechanic\n//  60–82   the gradient \"Shipped\" band sweeps across; while it covers the\n//          panel the tab turns to \"Full time\" and the opponent dims\n//  82–     poster scoreline holds\nconst GOAL_AT = 30;\nconst GOAL_GAP = 18;\nconst FLIP = 6;\nconst BAND_DELAY = 12;\nconst BAND_IN = 7;\nconst BAND_HOLD = 9;\nconst BAND_OUT = 7;\n\nconst SLAM = Easing.bezier(0.22, 1, 0.36, 1);\nconst FLIPEASE = Easing.bezier(0.7, 0, 0.25, 1);\nconst EXIT = Easing.bezier(0.6, 0, 0.9, 0.4);\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\n\n// gradients.sh \"beam\": #f4f7ff, #8fb4ff, #3d5bff, #1a1660.\nconst BEAM = [\"#F4F7FF\", \"#8FB4FF\", \"#3D5BFF\", \"#1A1660\"] as const;\n\nconst THEMES = {\n  dark: {\n    stage: \"#0A0A0B\",\n    core: \"#F3F2EF\",\n    coreInk: \"#0A0A0B\",\n    coreRule: \"#D9D7D1\",\n    away: \"#1C1C20\",\n    awayOff: \"#131316\",\n    awayInk: \"#F3F2EF\",\n    awayDim: \"#4D4C53\",\n    tab: \"#1C1C20\",\n    tabInk: \"#F3F2EF\",\n    dim: \"#86858C\",\n  },\n  light: {\n    stage: \"#F5F4F0\",\n    core: \"#121214\",\n    coreInk: \"#F5F4F0\",\n    coreRule: \"#2A2A2E\",\n    away: \"#E2E0D9\",\n    awayOff: \"#EAE8E2\",\n    awayInk: \"#121214\",\n    awayDim: \"#B3B1AA\",\n    tab: \"#121214\",\n    tabInk: \"#F5F4F0\",\n    dim: \"#7E7D77\",\n  },\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 short hit: up in 2 frames, eases back over 9. */\nfunction kick(d: number) {\n  if (d < 0) return 0;\n  if (d < 2) return d / 2;\n  const k = Math.max(0, 1 - (d - 2) / 9);\n  return k * k;\n}\n\nexport function ScoreBug({\n  away = \"Old way\",\n  score = 2,\n  awayScore = 0,\n  scorers = [\"Install 12′\", \"Ship 47′\"],\n  flash = \"Shipped\",\n  liveLabel = \"Live · 2nd half\",\n  finalLabel = \"Full time\",\n  accent = \"#3B5BFD\",\n  theme = \"dark\",\n  speed = 1,\n}: ScoreBugProps) {\n  // Starts two frames in, so frame 0 already shows the core opening.\n  const t = useCurrentFrame() * speed + 2;\n  const { width: W, height: H } = useVideoConfig();\n  const c = THEMES[theme];\n  const u = Math.min(W / 1280, H / 720);\n\n  const goals = Math.max(0, Math.floor(score));\n  const goalAt = Array.from(\n    { length: goals },\n    (_, i) => GOAL_AT + i * GOAL_GAP,\n  );\n  const bandAt = (goalAt[goals - 1] ?? GOAL_AT - GOAL_GAP) + BAND_DELAY;\n  const fullTimeAt = bandAt + BAND_IN + 4;\n  const fullTime = t >= fullTimeAt;\n\n  // ── Layout ────────────────────────────────────────────────────────────────\n  const panelW = 1160 * u;\n  const panelH = 250 * u;\n  const coreW = 360 * u;\n  const gap = 8 * u;\n  const blockW = (panelW - coreW - 2 * gap) / 2;\n  const panelX = (W - panelW) / 2;\n  const panelY = (H - panelH) / 2 - 14 * u;\n  const tabH = 52 * u;\n  const digitFs = 270 * u;\n  const cellW = 140 * u;\n\n  // ── Entrance ──────────────────────────────────────────────────────────────\n  const coreOpen = SLAM(ramp(t, 0, 6));\n  const blockOut = SLAM(ramp(t, 3, 10));\n  const nameIn = SLAM(ramp(t, 8, 9));\n  const digitIn = SLAM(ramp(t, 6, 8));\n  const tabUp = SLAM(ramp(t, 12, 8));\n\n  // Every hit nudges the panel up in scale for a beat.\n  const hits = [5, ...goalAt.map((g) => g + 2)];\n  const bump = hits.reduce((a, h) => Math.max(a, kick(t - h)), 0);\n  const panelScale = 1 + 0.022 * bump;\n\n  // Home digit: which goal is flipping, and how far.\n  let home = 0;\n  let flipP = 1;\n  for (let i = 0; i < goals; i++) {\n    if (t >= goalAt[i]) {\n      home = i + 1;\n      flipP = FLIPEASE(ramp(t, goalAt[i], FLIP));\n    }\n  }\n  const prev = home - 1;\n\n  const blockFlash = goalAt.reduce((a, g) => {\n    const d = t - g - 1;\n    if (d < 0 || d > 10) return a;\n    return Math.max(a, (1 - d / 10) ** 2);\n  }, 0);\n\n  const awayDimP = ramp(t, fullTimeAt, 6);\n  const awayBg = fullTime ? c.awayOff : c.away;\n  const awayInk = awayDimP > 0 ? c.awayDim : c.awayInk;\n\n  // Gentle drift on the home block's gradient.\n  const drift = interpolate(t, [0, 120], [0, 10], CLAMP);\n  const homeGround = [\n    `radial-gradient(70% 140% at ${10 + drift}% 0%, ${BEAM[1]}e6 0%, transparent 62%)`,\n    `radial-gradient(60% 120% at ${96 - drift * 0.6}% 110%, ${BEAM[2]} 0%, transparent 72%)`,\n    `linear-gradient(115deg, ${BEAM[3]} 0%, #2B3BCF 100%)`,\n  ].join(\", \");\n\n  // ── Band ──────────────────────────────────────────────────────────────────\n  const rowH = 40 * u;\n  const bandTop = panelY - tabH - gap - 14 * u;\n  const bandBottom =\n    panelY +\n    panelH +\n    14 * u +\n    Math.max(1, Math.min(goals, scorers.length)) * rowH +\n    14 * u;\n  const bandH = bandBottom - bandTop;\n  const slant = 0.2 * bandH;\n  const bandIn = SLAM(ramp(t, bandAt, BAND_IN));\n  const bandOut = EXIT(ramp(t, bandAt + BAND_IN + BAND_HOLD, BAND_OUT));\n  const span = W + 2 * slant;\n  const bandX = -span * (1 - bandIn) + span * bandOut;\n  const hold = ramp(t, bandAt + BAND_IN, BAND_HOLD);\n  const textLag =\n    -0.16 * W * (1 - bandIn) + 0.025 * W * hold + 0.22 * W * bandOut;\n  const bandVisible = t >= bandAt && bandOut < 1;\n  const bandY = bandTop;\n\n  const digit = (n: number, y: number, key: string) => (\n    <div\n      key={key}\n      style={{\n        position: \"absolute\",\n        left: 0,\n        right: 0,\n        top: 0,\n        height: panelH,\n        transform: `translateY(${y}px)`,\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        fontFamily: condensed,\n        fontWeight: 500,\n        fontSize: digitFs,\n        lineHeight: 1,\n        color: c.coreInk,\n        fontVariantNumeric: \"tabular-nums\",\n        letterSpacing: \"-0.02em\",\n        paddingTop: 4 * u,\n      }}\n    >\n      {n}\n    </div>\n  );\n\n  const dropIn = -(1 - digitIn) * panelH * 1.05;\n\n  return (\n    <AbsoluteFill style={{ background: c.stage, overflow: \"hidden\" }}>\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 0,\n          transform: `scale(${panelScale})`,\n          transformOrigin: `${W / 2}px ${panelY + panelH / 2}px`,\n        }}\n      >\n        {/* Tab, rising from behind the panel. */}\n        <div\n          style={{\n            position: \"absolute\",\n            left: panelX + blockW + gap,\n            top: panelY - tabH - gap,\n            width: coreW,\n            height: tabH + gap,\n            overflow: \"hidden\",\n          }}\n        >\n          <div\n            style={{\n              position: \"absolute\",\n              left: 0,\n              top: 0,\n              width: coreW,\n              height: tabH,\n              transform: `translateY(${(1 - tabUp) * (tabH + gap)}px)`,\n              background: c.tab,\n              display: \"flex\",\n              alignItems: \"center\",\n              justifyContent: \"center\",\n              gap: 12 * u,\n              fontFamily: condensed,\n              fontWeight: 500,\n              fontSize: 28 * u,\n              letterSpacing: \"0.1em\",\n              textTransform: \"uppercase\",\n              color: c.tabInk,\n            }}\n          >\n            {fullTime ? null : (\n              <div\n                style={{\n                  width: 11 * u,\n                  height: 11 * u,\n                  background: accent,\n                  opacity: Math.floor(t / 15) % 2 === 0 ? 1 : 0.35,\n                }}\n              />\n            )}\n            <span style={{ paddingTop: 2 * u }}>\n              {fullTime ? finalLabel : liveLabel}\n            </span>\n          </div>\n        </div>\n\n        {/* Home block: emerges from behind the core to the left. */}\n        <div\n          style={{\n            position: \"absolute\",\n            left: panelX,\n            top: panelY,\n            width: blockW,\n            height: panelH,\n            overflow: \"hidden\",\n          }}\n        >\n          <div\n            style={{\n              position: \"absolute\",\n              inset: 0,\n              transform: `translateX(${(1 - blockOut) * (blockW + gap)}px)`,\n              background: homeGround,\n            }}\n          >\n            <div\n              style={{\n                position: \"absolute\",\n                inset: 0,\n                background: \"#FFFFFF\",\n                opacity: 0.4 * blockFlash,\n              }}\n            />\n            <div\n              style={{\n                position: \"absolute\",\n                inset: 0,\n                display: \"flex\",\n                alignItems: \"center\",\n                justifyContent: \"center\",\n                transform: `translateX(${(1 - nameIn) * 60 * u}px)`,\n                opacity: nameIn,\n              }}\n            >\n              <RemocnLogo fontSize={86 * u} color=\"#FFFFFF\" />\n            </div>\n          </div>\n        </div>\n\n        {/* Away block: emerges from behind the core to the right. */}\n        <div\n          style={{\n            position: \"absolute\",\n            left: panelX + blockW + coreW + 2 * gap,\n            top: panelY,\n            width: blockW,\n            height: panelH,\n            overflow: \"hidden\",\n          }}\n        >\n          <div\n            style={{\n              position: \"absolute\",\n              inset: 0,\n              transform: `translateX(${-(1 - blockOut) * (blockW + gap)}px)`,\n              background: awayBg,\n            }}\n          >\n            <div\n              style={{\n                position: \"absolute\",\n                inset: 0,\n                display: \"flex\",\n                alignItems: \"center\",\n                justifyContent: \"center\",\n                transform: `translateX(${-(1 - nameIn) * 60 * u}px)`,\n                opacity: nameIn,\n                fontFamily: condensed,\n                fontWeight: 500,\n                fontSize: 116 * u,\n                lineHeight: 1,\n                letterSpacing: \"0.005em\",\n                textTransform: \"uppercase\",\n                color: awayInk,\n                paddingTop: 6 * u,\n                whiteSpace: \"nowrap\",\n              }}\n            >\n              {away}\n            </div>\n          </div>\n        </div>\n\n        {/* Core: the score itself. */}\n        <div\n          style={{\n            position: \"absolute\",\n            left: panelX + blockW + gap,\n            top: panelY,\n            width: coreW,\n            height: panelH,\n            background: c.core,\n            clipPath: `inset(${50 * (1 - coreOpen)}% 0 ${50 * (1 - coreOpen)}% 0)`,\n          }}\n        >\n          <div\n            style={{\n              position: \"absolute\",\n              left: coreW / 2 - 22 * u - cellW,\n              top: 0,\n              width: cellW,\n              height: panelH,\n              overflow: \"hidden\",\n            }}\n          >\n            {flipP < 1 && prev >= 0\n              ? [\n                  digit(prev, -flipP * panelH, \"out\"),\n                  digit(home, (1 - flipP) * panelH, \"in\"),\n                ]\n              : digit(home, dropIn, \"now\")}\n          </div>\n          <div\n            style={{\n              position: \"absolute\",\n              left: coreW / 2 - 15 * u,\n              top: panelH / 2 - 4 * u,\n              width: 30 * u,\n              height: 9 * u,\n              background: c.coreRule,\n              opacity: digitIn,\n            }}\n          />\n          <div\n            style={{\n              position: \"absolute\",\n              left: coreW / 2 + 22 * u,\n              top: 0,\n              width: cellW,\n              height: panelH,\n              overflow: \"hidden\",\n            }}\n          >\n            {digit(awayScore, dropIn, \"away\")}\n          </div>\n        </div>\n\n        {/* Scorer lines under the home block. */}\n        {scorers.slice(0, goals).map((line, i) => {\n          const p = SLAM(ramp(t, goalAt[i] + 4, 8));\n          return (\n            <div\n              key={`${i}-${line}`}\n              style={{\n                position: \"absolute\",\n                left: panelX,\n                top: panelY + panelH + 14 * u + i * rowH,\n                width: blockW,\n                height: rowH,\n                overflow: \"hidden\",\n              }}\n            >\n              <div\n                style={{\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  gap: 12 * u,\n                  height: rowH,\n                  transform: `translateY(${-(1 - p) * rowH}px)`,\n                  fontFamily: condensed,\n                  fontWeight: 500,\n                  fontSize: 30 * u,\n                  letterSpacing: \"0.06em\",\n                  textTransform: \"uppercase\",\n                  color: c.dim,\n                }}\n              >\n                <div\n                  style={{\n                    width: 4 * u,\n                    height: 20 * u,\n                    background: accent,\n                    flexShrink: 0,\n                  }}\n                />\n                <span style={{ paddingTop: 2 * u }}>{line}</span>\n              </div>\n            </div>\n          );\n        })}\n      </div>\n\n      {/* The flash band. */}\n      {bandVisible ? (\n        <div\n          style={{\n            position: \"absolute\",\n            left: bandX - slant,\n            top: bandY,\n            width: span,\n            height: bandH,\n            clipPath: `polygon(${slant}px 0, 100% 0, calc(100% - ${slant}px) 100%, 0 100%)`,\n            background: `linear-gradient(90deg, ${BEAM[3]} 0%, #2A3BD8 40%, ${BEAM[2]} 72%, ${BEAM[1]} 93%, ${BEAM[0]} 100%)`,\n          }}\n        >\n          <div\n            style={{\n              position: \"absolute\",\n              inset: 0,\n              display: \"flex\",\n              alignItems: \"center\",\n              justifyContent: \"center\",\n              transform: `translateX(${textLag}px)`,\n              fontFamily: condensed,\n              fontWeight: 500,\n              fontSize: 340 * u,\n              lineHeight: 1,\n              letterSpacing: \"0.01em\",\n              textTransform: \"uppercase\",\n              color: \"#FFFFFF\",\n              paddingTop: 10 * u,\n              whiteSpace: \"nowrap\",\n            }}\n          >\n            {flash}\n          </div>\n        </div>\n      ) : null}\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/score-bug.tsx"
    }
  ],
  "type": "registry:component"
}
