{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "beeswarm",
  "title": "Beeswarm",
  "description": "Two dot swarms, before and after, settle on an axis showing 3.4 versus 10.6.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/beeswarm/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont } from \"@remotion/google-fonts/DMSans\";\nimport type React from \"react\";\nimport { useId, useMemo } from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  interpolateColors,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nconst { fontFamily: sans } = loadFont(\"normal\", {\n  weights: [\"400\", \"500\"],\n  subsets: [\"latin\"],\n});\n\nexport interface BeeswarmGroup {\n  label: string;\n  median: number;\n  spread: number;\n}\n\nexport interface BeeswarmProps {\n  before?: BeeswarmGroup;\n  after?: BeeswarmGroup;\n  count?: number;\n  domain?: [number, number];\n  tickStep?: number;\n  axisLabel?: string;\n  totalLabel?: string;\n  accent?: string;\n  speed?: number;\n  theme?: \"dark\" | \"light\";\n}\n\nconst DEFAULT_BEFORE: BeeswarmGroup = {\n  label: \"Before\",\n  median: 3.4,\n  spread: 0.85,\n};\nconst DEFAULT_AFTER: BeeswarmGroup = {\n  label: \"With remocn\",\n  median: 10.6,\n  spread: 1.05,\n};\n\nconst EASE_IO = Easing.bezier(0.65, 0, 0.35, 1);\nconst EASE_OUT = Easing.bezier(0.22, 1, 0.36, 1);\nconst FALL = Easing.bezier(0.55, 0, 0.9, 0.6);\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\n\nconst THEMES = {\n  dark: {\n    bg: \"#0A0A0B\",\n    fg: \"#F3F2EF\",\n    muted: \"#8B8A90\",\n    neutral: \"#B9B8BD\",\n    grey: \"#6E6D73\",\n    axis: \"rgba(255,255,255,0.2)\",\n    rule: \"rgba(255,255,255,0.07)\",\n    // gradients.sh \"mesh\" as a radial bloom through the hero cluster\n    mesh: [\"#ecdcf2\", \"#8796ee\", \"accent\", \"#2e45b7\"],\n    label: \"#aab4f4\",\n    grain: 0.06,\n  },\n  light: {\n    bg: \"#F5F4F0\",\n    fg: \"#121214\",\n    muted: \"#6E6C67\",\n    neutral: \"#5B5955\",\n    grey: \"#A5A39D\",\n    axis: \"rgba(18,18,20,0.22)\",\n    rule: \"rgba(18,18,20,0.07)\",\n    mesh: [\"#8796ee\", \"accent\", \"#2e45b7\"],\n    label: \"#2e45b7\",\n    grain: 0.045,\n  },\n} as const;\n\nconst POUR = [0, 40] as const;\nconst FALL_T = 16;\nconst SPLIT = 64;\nconst SPLIT_STAGGER = 10;\nconst SPLIT_T = 32;\nconst HIT = SPLIT + SPLIT_STAGGER + SPLIT_T - 4;\n\nfunction hash(n: number) {\n  const x = Math.sin(n * 127.1 + 311.7) * 43758.5453;\n  return x - Math.floor(x);\n}\n\nfunction normal(seed: number) {\n  const a = Math.max(1e-6, hash(seed));\n  const b = hash(seed + 91.7);\n  return Math.sqrt(-2 * Math.log(a)) * Math.cos(2 * Math.PI * b);\n}\n\nfunction swarm(xs: number[], d: number) {\n  const order = xs.map((_, i) => i).sort((a, b) => xs[a] - xs[b]);\n  const placed: { x: number; y: number }[] = [];\n  const ys = new Array<number>(xs.length).fill(0);\n  const d2 = d * d - 1e-6;\n  for (const i of order) {\n    const x = xs[i];\n    const near = placed.filter((q) => Math.abs(q.x - x) < d);\n    const cands = [0];\n    for (const q of near) {\n      const dy = Math.sqrt(Math.max(0, d * d - (q.x - x) ** 2));\n      cands.push(q.y + dy, q.y - dy);\n    }\n    const flip = i % 2 === 0 ? 1 : -1;\n    cands.sort((a, b) => Math.abs(a) - Math.abs(b) || (a - b) * flip);\n    let y = 0;\n    for (const c of cands) {\n      if (near.every((q) => (q.x - x) ** 2 + (q.y - c) ** 2 >= d2)) {\n        y = c;\n        break;\n      }\n    }\n    placed.push({ x, y });\n    ys[i] = y;\n  }\n  return ys;\n}\n\nfunction Grain({ opacity }: { opacity: number }) {\n  const id = `g${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n  return (\n    <svg\n      width=\"100%\"\n      height=\"100%\"\n      style={{ position: \"absolute\", inset: 0, opacity, pointerEvents: \"none\" }}\n      aria-hidden\n    >\n      <filter id={id}>\n        <feTurbulence\n          type=\"fractalNoise\"\n          baseFrequency=\"0.9\"\n          numOctaves={2}\n          seed={31}\n          stitchTiles=\"stitch\"\n        />\n        <feColorMatrix values=\"0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0.9 0\" />\n      </filter>\n      <rect width=\"100%\" height=\"100%\" filter={`url(#${id})`} />\n    </svg>\n  );\n}\n\nconst fmt = (n: number) => n.toFixed(1).replace(/\\.0$/, \"\");\n\nexport function Beeswarm({\n  before = DEFAULT_BEFORE,\n  after = DEFAULT_AFTER,\n  count = 200,\n  domain = [0, 14],\n  tickStep = 2,\n  axisLabel = \"launch videos shipped per quarter\",\n  totalLabel = \"200 teams\",\n  accent = \"#3B5BFD\",\n  speed = 1,\n  theme = \"dark\",\n}: BeeswarmProps) {\n  const t = useCurrentFrame() * speed;\n  const { width: W, height: H, fps } = useVideoConfig();\n  const u = Math.min(W / 1280, H / 720);\n  const p = THEMES[theme];\n\n  const N = Math.max(20, Math.min(320, Math.round(count)));\n  const [d0, d1] = domain;\n  const x0 = W * 0.12;\n  const x1 = W * 0.88;\n  const xOf = (v: number) => x0 + ((v - d0) / (d1 - d0 || 1)) * (x1 - x0);\n  const axisY = H * 0.46;\n  const r = 6.6 * u;\n  const pitch = 2 * r + 1.2 * u;\n\n  const layout = useMemo(() => {\n    const mid = (before.median + after.median) / 2;\n    const midSpread = Math.max(before.spread, after.spread) * 1.45;\n    const clampV = (v: number) => Math.min(d1 - 0.3, Math.max(d0 + 0.3, v));\n    const v1 = Array.from({ length: N }, (_, i) =>\n      clampV(mid + midSpread * normal(i * 2.31 + 0.17)),\n    );\n    const rank = v1\n      .map((v, i) => ({ v, i }))\n      .sort((a, b) => a.v - b.v)\n      .map((o, k) => ({ i: o.i, score: k / N + (hash(o.i + 6.6) - 0.5) * 0.7 }))\n      .sort((a, b) => a.score - b.score);\n    const half = Math.floor(N / 2);\n    const group = new Array<number>(N).fill(0);\n    rank.forEach((o, k) => {\n      group[o.i] = k < half ? 0 : 1;\n    });\n    const members = [0, 1].map((g) =>\n      v1\n        .map((v, i) => ({ v, i }))\n        .filter((o) => group[o.i] === g)\n        .sort((a, b) => a.v - b.v),\n    );\n    const v2 = new Array<number>(N).fill(0);\n    members.forEach((list, g) => {\n      const src = g === 0 ? before : after;\n      const vals = list\n        .map((_, k) =>\n          clampV(src.median + src.spread * normal(k * 3.7 + g * 101.3)),\n        )\n        .sort((a, b) => a - b);\n      const med = vals[Math.floor(vals.length / 2)] ?? src.median;\n      list.forEach((o, k) => {\n        v2[o.i] = vals[k] - med + src.median;\n      });\n    });\n    const toX = (v: number) => x0 + ((v - d0) / (d1 - d0 || 1)) * (x1 - x0);\n    const xs1 = v1.map(toX);\n    const ys1 = swarm(xs1, pitch);\n    const xs2 = v2.map(toX);\n    const ys2 = [0, 1].reduce((acc, g) => {\n      const idx = members[g].map((o) => o.i);\n      const ys = swarm(\n        idx.map((i) => xs2[i]),\n        pitch,\n      );\n      idx.forEach((i, k) => {\n        acc[i] = ys[k];\n      });\n      return acc;\n    }, new Array<number>(N).fill(0));\n    const pourOrder = ys1\n      .map((y, i) => ({ i, k: y + (hash(i + 4.2) - 0.5) * pitch * 0.6 }))\n      .sort((a, b) => b.k - a.k);\n    const pourRank = new Array<number>(N).fill(0);\n    pourOrder.forEach((o, k) => {\n      pourRank[o.i] = k / Math.max(1, N - 1);\n    });\n    const ext = (g: number) =>\n      Math.max(pitch, ...members[g].map((o) => Math.abs(ys2[o.i])));\n    return {\n      xs1,\n      ys1,\n      xs2,\n      ys2,\n      group,\n      pourRank,\n      ext1: Math.max(pitch, ...ys1.map(Math.abs)),\n      ext: [ext(0), ext(1)],\n    };\n  }, [N, before, after, d0, d1, x0, x1, pitch]);\n\n  const ruleIn = interpolate(t, [0, 18], [0, 1], { ...CLAMP, easing: EASE_IO });\n  const totalIn =\n    interpolate(t, [POUR[1] + 6, POUR[1] + 16], [0, 1], {\n      ...CLAMP,\n      easing: EASE_OUT,\n    }) * interpolate(t, [SPLIT - 2, SPLIT + 8], [1, 0], CLAMP);\n  const push = interpolate(t, [0, HIT + 40], [0, 1], {\n    ...CLAMP,\n    easing: EASE_IO,\n  });\n  const camScale = 0.975 + 0.025 * push;\n\n  const meshId = `${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}-mesh`;\n  const meshStops = p.mesh.map((c) => (c === \"accent\" ? accent : c));\n  const bloomX = xOf(after.median);\n  const bloomR = Math.max(\n    after.spread * 2.4 * ((x1 - x0) / (d1 - d0 || 1)),\n    layout.ext[1] * 1.6,\n  );\n  const circles: React.ReactNode[] = [];\n  for (let i = 0; i < N; i++) {\n    const g = layout.group[i];\n    const start = POUR[0] + layout.pourRank[i] * (POUR[1] - POUR[0]);\n    const fall = interpolate(t, [start, start + FALL_T], [0, 1], {\n      ...CLAMP,\n      easing: FALL,\n    });\n    if (fall <= 0) continue;\n    const fromY = -H * 0.08 - hash(i + 12.1) * H * 0.12;\n    const ty1 = axisY + layout.ys1[i];\n    const landT = t - start - FALL_T;\n    const settle =\n      landT > 0 && landT < 8\n        ? Math.sin((landT / 8) * Math.PI) * 1.6 * u * (1 - landT / 8)\n        : 0;\n    const y1 = fromY + (ty1 - fromY) * fall + settle;\n    const sStart = SPLIT + hash(i + 2.9) * SPLIT_STAGGER;\n    const k = interpolate(t, [sStart, sStart + SPLIT_T], [0, 1], {\n      ...CLAMP,\n      easing: EASE_IO,\n    });\n    const x = layout.xs1[i] + (layout.xs2[i] - layout.xs1[i]) * k;\n    const y2 = axisY + layout.ys2[i];\n    const lift = Math.sin(k * Math.PI) * (g === 0 ? -1 : 1) * 10 * u;\n    const y = y1 + (y2 - y1) * k + lift;\n    const o = interpolate(fall, [0, 0.25], [0, 1], CLAMP);\n    if (g === 0) {\n      const fill = interpolateColors(k, [0, 1], [p.neutral, p.grey]);\n      circles.push(\n        <circle key={i} cx={x} cy={y} r={r} fill={fill} opacity={o} />,\n      );\n    } else {\n      circles.push(\n        <g key={i} opacity={o}>\n          {k < 1 && <circle cx={x} cy={y} r={r} fill={p.neutral} />}\n          {k > 0 && (\n            <circle cx={x} cy={y} r={r} fill={`url(#${meshId})`} opacity={k} />\n          )}\n        </g>,\n      );\n    }\n  }\n\n  const ticks: number[] = [];\n  for (let v = d0; v <= d1 + 1e-6; v += tickStep) ticks.push(v);\n  const axisBase = axisY + Math.max(layout.ext1, ...layout.ext) + 72 * u;\n\n  const groups = [before, after].map((grp, g) => {\n    const snap = spring({\n      frame: t - HIT - g * 4,\n      fps,\n      config: { damping: 14, stiffness: 190, mass: 0.6 },\n    });\n    const a = interpolate(t, [HIT + g * 4, HIT + g * 4 + 8], [0, 1], CLAMP);\n    const lab = interpolate(t, [HIT + 6 + g * 4, HIT + 24 + g * 4], [0, 1], {\n      ...CLAMP,\n      easing: EASE_OUT,\n    });\n    const ext = Math.max(...layout.ext) + 16 * u;\n    return {\n      grp,\n      g,\n      x: xOf(grp.median),\n      top: axisY - ext,\n      bot: axisY + ext,\n      labelBottom: H - axisY + ext + 14 * u,\n      snap,\n      a,\n      lab,\n      color: g === 0 ? p.muted : p.label,\n      line: g === 0 ? p.fg : accent,\n    };\n  });\n\n  return (\n    <AbsoluteFill style={{ background: p.bg, overflow: \"hidden\" }}>\n      <AbsoluteFill\n        style={{\n          transform: `scale(${camScale})`,\n          transformOrigin: \"50% 50%\",\n        }}\n      >\n        <svg\n          width={W}\n          height={H}\n          style={{ position: \"absolute\", inset: 0 }}\n          aria-hidden\n        >\n          <line\n            x1={W / 2 - ((x1 - x0) / 2) * ruleIn}\n            x2={W / 2 + ((x1 - x0) / 2) * ruleIn}\n            y1={axisY}\n            y2={axisY}\n            stroke={p.rule}\n            strokeWidth={Math.max(1, u)}\n          />\n          <line\n            x1={W / 2 - ((x1 - x0) / 2) * ruleIn}\n            x2={W / 2 + ((x1 - x0) / 2) * ruleIn}\n            y1={axisBase}\n            y2={axisBase}\n            stroke={p.axis}\n            strokeWidth={Math.max(1, u)}\n          />\n          {ticks.map((v) => (\n            <line\n              key={v}\n              x1={xOf(v)}\n              x2={xOf(v)}\n              y1={axisBase}\n              y2={axisBase + 6 * u}\n              stroke={p.axis}\n              strokeWidth={Math.max(1, u)}\n              opacity={ruleIn}\n            />\n          ))}\n          <defs>\n            <radialGradient\n              id={meshId}\n              gradientUnits=\"userSpaceOnUse\"\n              cx={bloomX + (push - 0.5) * 6 * u}\n              cy={axisY}\n              r={bloomR}\n            >\n              {meshStops.map((c, k) => (\n                <stop\n                  key={c}\n                  offset={(k / (meshStops.length - 1)) ** 1.15}\n                  stopColor={c}\n                />\n              ))}\n            </radialGradient>\n          </defs>\n          {circles}\n          {groups.map((m) => {\n            const len = (m.bot - m.top) * (0.4 + 0.6 * m.snap);\n            const cy = (m.top + m.bot) / 2;\n            return (\n              <g key={m.grp.label} opacity={m.a}>\n                <line\n                  x1={m.x}\n                  x2={m.x}\n                  y1={cy - len / 2}\n                  y2={cy + len / 2}\n                  stroke={p.bg}\n                  strokeWidth={6 * u}\n                  strokeLinecap=\"round\"\n                />\n                <line\n                  x1={m.x}\n                  x2={m.x}\n                  y1={cy - len / 2}\n                  y2={cy + len / 2}\n                  stroke={m.line}\n                  strokeWidth={2.4 * u}\n                  strokeLinecap=\"round\"\n                />\n              </g>\n            );\n          })}\n        </svg>\n\n        <div\n          style={{\n            position: \"absolute\",\n            left: 0,\n            width: W,\n            top: axisY - layout.ext1 - 52 * u,\n            textAlign: \"center\",\n            fontFamily: sans,\n            fontWeight: 500,\n            fontSize: 18 * u,\n            letterSpacing: \"-0.01em\",\n            color: p.muted,\n            opacity: totalIn,\n          }}\n        >\n          {totalLabel}\n        </div>\n\n        {groups.map((m) => (\n          <div\n            key={`lab${m.grp.label}`}\n            style={{\n              position: \"absolute\",\n              left: m.x - 200 * u,\n              width: 400 * u,\n              bottom: m.labelBottom,\n              textAlign: \"center\",\n              opacity: m.lab,\n              transform: `translateY(${(1 - m.lab) * 10 * u}px)`,\n            }}\n          >\n            <div\n              style={{\n                fontFamily: sans,\n                fontWeight: 500,\n                fontSize: 18 * u,\n                letterSpacing: \"-0.01em\",\n                color: m.color,\n                whiteSpace: \"nowrap\",\n              }}\n            >\n              {m.grp.label}\n            </div>\n            <div\n              style={{\n                marginTop: 2 * u,\n                fontFamily: sans,\n                fontWeight: 400,\n                fontSize: 64 * u,\n                lineHeight: 1,\n                letterSpacing: \"-0.035em\",\n                color: p.fg,\n                fontVariantNumeric: \"tabular-nums\",\n                whiteSpace: \"nowrap\",\n              }}\n            >\n              {fmt(m.grp.median)}\n            </div>\n          </div>\n        ))}\n\n        {ticks.map((v) => (\n          <div\n            key={`t${v}`}\n            style={{\n              position: \"absolute\",\n              left: xOf(v) - 30 * u,\n              width: 60 * u,\n              top: axisBase + 12 * u,\n              textAlign: \"center\",\n              fontFamily: sans,\n              fontWeight: 500,\n              fontSize: 15 * u,\n              color: p.muted,\n              fontVariantNumeric: \"tabular-nums\",\n              opacity: ruleIn,\n            }}\n          >\n            {fmt(v)}\n          </div>\n        ))}\n        <div\n          style={{\n            position: \"absolute\",\n            left: 0,\n            width: W,\n            top: axisBase + 40 * u,\n            textAlign: \"center\",\n            fontFamily: sans,\n            fontWeight: 500,\n            fontSize: 16 * u,\n            color: p.muted,\n            opacity: ruleIn,\n          }}\n        >\n          {axisLabel}\n        </div>\n      </AbsoluteFill>\n      <Grain opacity={p.grain} />\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/beeswarm.tsx"
    }
  ],
  "type": "registry:component"
}
