{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "got-em",
  "title": "Got Em",
  "description": "A suspense line resolves into an exploded 'GOT 'EM' over a gradient sunburst.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/got-em/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont as loadGeist } from \"@remotion/google-fonts/Geist\";\nimport { loadFont as loadManrope } from \"@remotion/google-fonts/Manrope\";\nimport { loadFont } from \"@remotion/google-fonts/Unbounded\";\nimport type React from \"react\";\nimport { useId } from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nconst { fontFamily: display } = loadFont(\"normal\", {\n  weights: [\"500\"],\n  subsets: [\"latin\"],\n});\nconst { fontFamily: geist } = loadGeist(\"normal\", {\n  weights: [\"400\"],\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 GotEmProps {\n  /** The suspense line; an animated ellipsis follows it. */\n  pending?: string;\n  /** The payoff, exploded huge. Use a real apostrophe (’). */\n  title?: string;\n  /** Subline under the payoff. \"remocn\" inside it renders the real logo. */\n  subline?: string;\n  /** Replaces the logo inside the subline. */\n  logo?: React.ReactNode;\n  /** Kept for the shared draft API; the rays take the \"halo\" gradient. */\n  accent?: string;\n  theme?: \"dark\" | \"light\";\n  speed?: number;\n}\n\n// Unbounded 500 (fontTools, wght 500): cap 0.75; G .955 O .956 T .801\n// space .256 ’ .277 E .78 M 1.206. Geist 400: \"…\" 0.571em, dots end at\n// 0.157 / 0.342 / 0.527em.\nconst CAP = 0.75;\nconst ADV: Record<string, number> = {\n  G: 0.955,\n  O: 0.956,\n  T: 0.801,\n  \" \": 0.256,\n  \"’\": 0.277,\n  \"'\": 0.252,\n  E: 0.78,\n  M: 1.206,\n};\nconst TRACK = -0.03;\nconst DOTS = [0, 0.2, 0.385, 0.571];\n\n// Beat sheet (30fps, speed 1):\n//   0–10   \"Entry submitted\" rises; a ring of 40 ticks draws on around it\n//  10–48   a spinner head sweeps the ring; the ellipsis counts 1-2-3\n//  48–62   the pause: everything stops dead, the ring settles, a slow creep\n//  62–70   GOT ’EM explodes from 1.6× with a flash and a zoom-punch; the\n//          ticks shoot out into full-frame gradient rays\n//  74–     subline with the logo rises; poster hold, the rays turn slowly\nconst PAUSE_AT = 48;\nconst BOOM_AT = 62;\nconst RAYS = 40;\n\nconst SLAM = 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,\n// laid out as rings: cool at the core, warm at the edge (reversed on light).\nconst THEMES = {\n  dark: {\n    stage: \"#0A0A0B\",\n    ink: \"#F3F2EF\",\n    dim: \"#8A8990\",\n    rays: [\"#3d4dff\", \"#e2457a\", \"#ffb36b\", \"#fff4ec\"],\n    rayAlpha: 0.95,\n    flash: \"#F3F2EF\",\n  },\n  light: {\n    stage: \"#F5F4F0\",\n    ink: \"#121214\",\n    dim: \"#77766F\",\n    rays: [\"#ffb36b\", \"#e2457a\", \"#3d4dff\", \"#3d4dff\"],\n    rayAlpha: 0.9,\n    flash: \"#FFFFFF\",\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 8. */\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) / 8);\n  return k * k;\n}\n\n/** A decaying shake that dies in 7 frames. Deterministic. */\nfunction shake(d: number, amp: number, seed: number) {\n  if (d < 0 || d > 7) return { x: 0, y: 0 };\n  const k = (1 - d / 7) ** 2;\n  return {\n    x: amp * k * Math.sin(d * 2.7 + seed),\n    y: amp * 0.7 * k * Math.cos(d * 3.3 + seed * 1.7),\n  };\n}\n\nexport function GotEm({\n  pending = \"Entry submitted\",\n  title = \"GOT ’EM\",\n  subline = \"You got remocn 2.0.\",\n  logo,\n  theme = \"dark\",\n  speed = 1,\n}: GotEmProps) {\n  const t = useCurrentFrame() * speed;\n  const { width: W, height: H } = useVideoConfig();\n  const u = Math.min(W / 1280, H / 720);\n  const c = THEMES[theme];\n  const uid = useId().replace(/[^a-zA-Z0-9_-]/g, \"\");\n  const cx = W / 2;\n  const cy = H * 0.47;\n\n  // ── Suspense ──\n  const boomed = t >= BOOM_AT;\n  const intro = SLAM(ramp(t, 0, 12));\n  const paused = t >= PAUSE_AT;\n  const dots = paused\n    ? 3\n    : t < 8\n      ? 0\n      : Math.min(3, Math.floor((t - 8) / 6) % 4);\n  const head = (Math.min(t, PAUSE_AT) - 6) * 13; // degrees, clockwise\n  const settle = ramp(t, PAUSE_AT, 6);\n  const creep = interpolate(t, [PAUSE_AT, BOOM_AT], [1, 1.035], {\n    ...CLAMP,\n    easing: Easing.bezier(0.33, 0, 0.67, 1),\n  });\n\n  // ── Boom ──\n  const d = t - BOOM_AT;\n  const zoom = SLAM(ramp(t, BOOM_AT, 8));\n  const camScale = boomed ? 1.16 - 0.16 * zoom + 0.03 * kick(d - 5) : creep;\n  const sh = shake(d - 5, 11 * u, 2.2);\n  const titleP = SLAM(ramp(t, BOOM_AT, 6));\n  const titleScale = 1.6 - 0.6 * titleP;\n  const titleBlur = (1 - titleP) * 18;\n  const flash = interpolate(\n    t,\n    [BOOM_AT, BOOM_AT + 1, BOOM_AT + 5],\n    [0, 0.7, 0],\n    CLAMP,\n  );\n  const spin = boomed ? d * 0.12 : 0;\n  const drift = ramp(t, BOOM_AT, 70);\n  const rise = SLAM(ramp(t, BOOM_AT + 12, 16));\n\n  // ── Rays: ticks on a ring, then full-frame bars ──\n  const R0 = 0.29 * H;\n  const tickLen = 0.04 * H;\n  const reach = Math.hypot(W, H) * 0.62;\n  const rays = Array.from({ length: RAYS }, (_, i) => {\n    const a0 = (i / RAYS) * 360;\n    const drawn = SLAM(ramp(t, (i / RAYS) * 10, 6));\n    // spinner brightness: a tail behind the head\n    const behind = (((head - a0) % 360) + 360) % 360;\n    const lit = Math.max(0, 1 - behind / 150) ** 1.6;\n    const glow = (0.22 + 0.78 * lit) * (1 - settle) + 0.4 * settle;\n    const shoot = SLAM(ramp(t, BOOM_AT + (i % 3), 9));\n    const inner = R0 + (0.15 * H - R0) * shoot;\n    const outer =\n      R0 + tickLen + (reach * (0.86 + 0.14 * (i % 2)) - R0 - tickLen) * shoot;\n    const w0 = 0.007 * H;\n    const w1 = boomed ? w0 + (0.06 * H - w0) * shoot : w0;\n    return { a: a0 + spin, drawn, glow, inner, outer, w0, w1 };\n  });\n\n  const titleChars = Array.from(title);\n  const titleEm =\n    titleChars.reduce((w, ch) => w + (ADV[ch] ?? 0.85), 0) +\n    TRACK * (titleChars.length - 1) -\n    0.041 -\n    0.076;\n  const titleFs = Math.min((0.92 * W) / titleEm, (0.4 * H) / CAP);\n  const titleBase = cy + (CAP * titleFs) / 2;\n\n  const pendFs = 0.058 * H;\n  const parts = subline.split(/(remocn)/i);\n  const subFs = 0.05 * H;\n\n  return (\n    <AbsoluteFill style={{ background: c.stage, overflow: \"hidden\" }}>\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 0,\n          transform: `translate(${boomed ? sh.x : 0}px, ${boomed ? sh.y : 0}px) scale(${camScale})`,\n          transformOrigin: `${cx}px ${cy}px`,\n        }}\n      >\n        <svg\n          width={W}\n          height={H}\n          viewBox={`0 0 ${W} ${H}`}\n          style={{ position: \"absolute\", inset: 0, overflow: \"visible\" }}\n          role=\"img\"\n          aria-label={boomed ? `${title} ${subline}` : `${pending}…`}\n        >\n          <defs>\n            <radialGradient\n              id={`${uid}rays`}\n              gradientUnits=\"userSpaceOnUse\"\n              cx={cx}\n              cy={cy}\n              r={reach * (0.95 + 0.08 * drift)}\n            >\n              <stop offset=\"0.12\" stopColor={c.rays[0]} />\n              <stop offset={0.42 - 0.04 * drift} stopColor={c.rays[1]} />\n              <stop offset={0.7 - 0.04 * drift} stopColor={c.rays[2]} />\n              <stop offset=\"1\" stopColor={c.rays[3]} />\n            </radialGradient>\n          </defs>\n\n          <g\n            fill={boomed ? `url(#${uid}rays)` : c.ink}\n            opacity={boomed ? c.rayAlpha : 1}\n          >\n            {rays.map((r) => {\n              const rad = ((r.a - 90) * Math.PI) / 180;\n              const nx = Math.cos(rad);\n              const ny = Math.sin(rad);\n              const px = -ny;\n              const py = nx;\n              const len = r.inner + (r.outer - r.inner) * r.drawn;\n              const pts = [\n                [r.inner, -r.w0 / 2],\n                [len, -r.w1 / 2],\n                [len, r.w1 / 2],\n                [r.inner, r.w0 / 2],\n              ]\n                .map(\n                  ([along, across]) =>\n                    `${(cx + nx * along + px * across).toFixed(2)},${(cy + ny * along + py * across).toFixed(2)}`,\n                )\n                .join(\" \");\n              return (\n                <polygon\n                  key={r.a - spin}\n                  points={pts}\n                  opacity={boomed ? 1 : r.glow * r.drawn}\n                />\n              );\n            })}\n          </g>\n\n          {boomed ? (\n            <text\n              x={cx}\n              y={titleBase}\n              textAnchor=\"middle\"\n              fontFamily={display}\n              fontWeight={500}\n              fontSize={titleFs}\n              fill={c.ink}\n              style={{\n                letterSpacing: `${TRACK}em`,\n                transform: `scale(${titleScale})`,\n                transformOrigin: `${cx}px ${cy}px`,\n                filter:\n                  titleBlur > 0.2 ? `blur(${titleBlur * u}px)` : undefined,\n              }}\n              opacity={ramp(t, BOOM_AT, 2)}\n            >\n              {title}\n            </text>\n          ) : null}\n        </svg>\n\n        {/* Suspense line inside the ring. */}\n        {!boomed ? (\n          <div\n            style={{\n              position: \"absolute\",\n              left: 0,\n              right: 0,\n              top: cy - pendFs * 0.62,\n              display: \"flex\",\n              justifyContent: \"center\",\n              fontFamily: geist,\n              fontWeight: 400,\n              fontSize: pendFs,\n              letterSpacing: \"-0.02em\",\n              lineHeight: 1.2,\n              color: c.ink,\n              opacity: intro,\n              transform: `translateY(${(1 - intro) * 12 * u}px)`,\n              whiteSpace: \"nowrap\",\n            }}\n          >\n            <span>{pending}</span>\n            <span\n              style={{\n                display: \"inline-block\",\n                width: `${DOTS[3]}em`,\n                overflow: \"hidden\",\n              }}\n            >\n              <span\n                style={{\n                  display: \"inline-block\",\n                  width: `${DOTS[dots]}em`,\n                  overflow: \"hidden\",\n                  verticalAlign: \"top\",\n                }}\n              >\n                …\n              </span>\n            </span>\n          </div>\n        ) : null}\n\n        {/* Subline with the logo. */}\n        {boomed ? (\n          <div\n            style={{\n              position: \"absolute\",\n              left: 0,\n              right: 0,\n              top: titleBase + 0.06 * H,\n              display: \"flex\",\n              justifyContent: \"center\",\n              fontFamily: geist,\n              fontWeight: 400,\n              fontSize: subFs,\n              letterSpacing: \"-0.02em\",\n              lineHeight: 1,\n              color: c.ink,\n              whiteSpace: \"pre\",\n              opacity: rise,\n              transform: `translateY(${(1 - rise) * 14 * u}px)`,\n            }}\n          >\n            <div\n              style={{\n                display: \"flex\",\n                alignItems: \"baseline\",\n                background: c.stage,\n                borderRadius: 999,\n                padding: `${0.024 * H}px ${0.034 * H}px ${0.026 * H}px`,\n              }}\n            >\n              {parts.map((p, i) =>\n                p.toLowerCase() === \"remocn\" ? (\n                  <span\n                    key={`${i}${p}`}\n                    style={{ display: \"inline-flex\", alignItems: \"baseline\" }}\n                  >\n                    {logo ?? (\n                      <RemocnLogo fontSize={subFs * 1.04} color={c.ink} />\n                    )}\n                  </span>\n                ) : (\n                  <span key={`${i}${p}`}>{p}</span>\n                ),\n              )}\n            </div>\n          </div>\n        ) : null}\n      </div>\n\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 0,\n          background: c.flash,\n          opacity: flash,\n        }}\n      />\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/got-em.tsx"
    }
  ],
  "type": "registry:component"
}
