{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "logo-flicker",
  "title": "Logo Flicker",
  "description": "Images flip across a central card very fast, the flicker decelerates and fades, and the logo and brand name resolve underneath — a strobe of imagery settling onto the mark.",
  "dependencies": [
    "remotion"
  ],
  "registryDependencies": [
    "https://snapcn.dev/r/snap-cn-ui.json"
  ],
  "files": [
    {
      "path": "registry/snap-cn/logo-flicker/index.tsx",
      "content": "\"use client\";\n\nimport {\n  Easing,\n  getRemotionEnvironment,\n  Img,\n  interpolate,\n  staticFile,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\nimport { type SnapCnTheme, useSnapCnTheme } from \"@/lib/snap-cn-ui\";\n\n/**\n * A Marvel-Studios-style flicker reveal: images flip full-screen at a constant\n * fast rate right to the end, then cross-fade — the flicker fading OUT while the\n * logo and brand name fade IN, together, in one move.\n */\n\nexport interface LogoFlickerProps {\n  /** The logo mark (simple/monochrome — it resolves over the dark backdrop). */\n  logoSrc?: string;\n  /** Brand name shown under the logo. */\n  brandName?: string;\n  /** Image pool the flicker flips through, full-screen. */\n  images?: string[];\n  /** Frames each image holds — the (constant) flip speed. */\n  flipInterval?: number;\n  /** Backdrop color. Overrides the design system's `background`. */\n  background?: string;\n  speed?: number;\n  className?: string;\n  /** Design-system token overrides. */\n  theme?: Partial<SnapCnTheme>;\n  /**\n   * Defaults to `\"dark\"`: the shipped `logoSrc` is a white mark, so the stage\n   * has to be dark for it to read. Pass `\"light\"` with a dark logo asset.\n   */\n  mode?: \"light\" | \"dark\";\n}\n\nconst FONT_FAMILY =\n  'Inter, var(--font-geist-sans), -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif';\n\n/** Sample images. Absolute, because a root-relative default becomes\n *  `staticFile()` in a render and 404s in the project this file was just\n *  copied into. Pass your own — root-relative or remote, both resolve. */\nconst DEFAULT_IMAGES = [\n  \"https://snapcn.dev/showcase-assets/438b9e6b50654a44d404fbf358c26e9f.webp\",\n  \"https://snapcn.dev/showcase-assets/5e5305b05bd405a0d89570725434099e.webp\",\n  \"https://snapcn.dev/showcase-assets/767d99bb371a54d0d36751e8cecae43c.jpg\",\n  \"https://snapcn.dev/showcase-assets/821d815affa6496c39cbdeeec7a84603.jpg\",\n  \"https://snapcn.dev/showcase-assets/937438c560ada1c83317f2c11b3454b0.jpg\",\n  \"https://snapcn.dev/showcase-assets/98f89cb9994f5c382ab964062c4039db.jpg\",\n  \"https://snapcn.dev/showcase-assets/b25b82db2892efff9be3204e860d30ee.jpg\",\n  \"https://snapcn.dev/showcase-assets/c9ebc6337aa2268ac4b357f9cb1ac547.jpg\",\n];\n\n// ─── Timeline (frames @ 30fps; ~3.5s) ──────────────────────────────────────────\n/** Default frames per image — the flip speed, held constant to the very end. */\nexport const FLIP_INTERVAL = 2;\n/**\n * The single cross-fade window: the flicker fades OUT and the logo + name fade\n * IN across exactly these frames — simultaneously, not one after the other.\n */\nexport const FADE_START = 66;\nexport const FADE_END = 84;\n\nexport const EASE = Easing.bezier(0.2, 0.6, 0.35, 1);\nconst CLAMP = { extrapolateLeft: \"clamp\", extrapolateRight: \"clamp\" } as const;\n\n/**\n * Which image shows at `frame`. A constant `interval` (no deceleration) and a\n * coprime stride so it hops through the whole pool as it flips.\n */\nexport function flickerImage(\n  frame: number,\n  count: number,\n  interval: number = FLIP_INTERVAL,\n): number {\n  const n = Math.max(1, count);\n  const step = Math.floor(frame / Math.max(1, interval));\n  return (step * 7) % n;\n}\n\n/** Flicker opacity — solid, then fades out across the cross-fade window. */\nexport function flickerOpacity(frame: number): number {\n  return interpolate(frame, [FADE_START, FADE_END], [1, 0], CLAMP);\n}\n\n/** Logo + name opacity — the INVERSE of the flicker, on the SAME window. */\nexport function revealOpacity(frame: number): number {\n  return interpolate(frame, [FADE_START, FADE_END], [0, 1], CLAMP);\n}\n\n/** A small settle on the lockup as it fades in (shared by logo and name). */\nexport function revealScale(frame: number): number {\n  return interpolate(frame, [FADE_START, FADE_END], [0.92, 1], {\n    ...CLAMP,\n    easing: EASE,\n  });\n}\n\n/** Rewrite root-relative assets through staticFile only while rendering. */\nfunction resolveSrc(src: string): string {\n  const isLocal = src.startsWith(\"/\") && !src.startsWith(\"//\");\n  if (isLocal && getRemotionEnvironment().isRendering) {\n    return staticFile(src.replace(/^\\/+/, \"\"));\n  }\n  return src;\n}\n\nexport function LogoFlicker({\n  logoSrc = \"https://snapcn.dev/logo/snapcn-white.png\",\n  brandName = \"snapcn\",\n  images = DEFAULT_IMAGES,\n  flipInterval = FLIP_INTERVAL,\n  background,\n  speed = 1,\n  className,\n  theme,\n  mode,\n}: LogoFlickerProps) {\n  const frame = useCurrentFrame() * speed;\n  const { width, height } = useVideoConfig();\n  const t = useSnapCnTheme(theme, mode ?? \"dark\");\n  const stage = background ?? t.background;\n  const short = Math.min(width, height);\n  const isRendering = getRemotionEnvironment().isRendering;\n\n  const flkOpacity = flickerOpacity(frame);\n  const reveal = revealOpacity(frame);\n  const scale = revealScale(frame);\n  const imgIdx = flickerImage(frame, images.length, flipInterval);\n\n  const logoSize = Math.round(short * 0.15);\n  const nameSize = Math.round(short * 0.055);\n\n  return (\n    <div\n      className={className}\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        overflow: \"hidden\",\n        backgroundColor: stage,\n        fontFamily: FONT_FAMILY,\n        textRendering: \"geometricPrecision\",\n      }}\n    >\n      {/* Full-screen flicker */}\n      {flkOpacity > 0 && (\n        <Img\n          src={resolveSrc(images[imgIdx] ?? images[0])}\n          style={{\n            position: \"absolute\",\n            inset: 0,\n            width: \"100%\",\n            height: \"100%\",\n            maxWidth: \"none\",\n            objectFit: \"cover\",\n            opacity: flkOpacity,\n            ...(isRendering ? {} : { willChange: \"opacity\" as const }),\n          }}\n        />\n      )}\n\n      {/* Logo + brand name — fade IN on the same window the flicker fades OUT */}\n      {reveal > 0 && (\n        <div\n          style={{\n            position: \"absolute\",\n            inset: 0,\n            display: \"flex\",\n            flexDirection: \"column\",\n            alignItems: \"center\",\n            justifyContent: \"center\",\n            gap: Math.round(short * 0.02),\n            opacity: reveal,\n            transform: `scale(${scale})`,\n            transformOrigin: \"center center\",\n          }}\n        >\n          <Img\n            src={resolveSrc(logoSrc)}\n            style={{\n              width: logoSize,\n              height: logoSize,\n              maxWidth: \"none\",\n              objectFit: \"contain\",\n            }}\n          />\n          <div\n            style={{\n              color: t.foreground,\n              fontSize: nameSize,\n              fontWeight: 600,\n              letterSpacing: \"-0.02em\",\n            }}\n          >\n            {brandName}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/snap-cn/logo-flicker.tsx"
    }
  ],
  "type": "registry:component"
}