{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toggle-on",
  "title": "Toggle on",
  "description": "A large switch flips on and lights the stage as the Auto-captions label turns On.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "registryDependencies": ["@remocn/gsh"],
  "files": [
    {
      "path": "registry/remocn/toggle-on/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont } from \"@remotion/google-fonts/SpaceGrotesk\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\nimport type { GradientParams } from \"@/lib/remocn/gsh/gradient-engine\";\nimport { GshGround } from \"@/lib/remocn/gsh/gsh-ground\";\nimport type { GshPresetId } from \"@/lib/remocn/gsh/presets\";\n\nconst { fontFamily: sans } = loadFont(\"normal\", {\n  weights: [\"300\", \"400\"],\n  subsets: [\"latin\"],\n});\n\nexport interface ToggleOnProps {\n  label?: string;\n  state?: string;\n  accent?: string;\n  theme?: \"dark\" | \"light\";\n  ground?: GshPresetId;\n  groundParams?: Partial<GradientParams>;\n  speed?: number;\n}\n\nconst ENTER = Easing.bezier(0.32, 0.72, 0, 1);\nconst MOVE = Easing.bezier(0.65, 0, 0.35, 1);\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\n\nconst BEATS = {\n  press: 12,\n  flip: 24,\n  light: 26,\n  label: 34,\n  state: 42,\n} as const;\n\nconst THEMES = {\n  dark: {\n    fg: \"#F3F2EF\",\n    stateInk: \"#9DC2FF\",\n    scrim: \"4,6,12\",\n    scrimFrom: 0.86,\n    scrimTo: 0.34,\n    track: \"#121317\",\n    trackTop: \"rgba(0,0,0,0.7)\",\n    trackLip: \"rgba(255,255,255,0.12)\",\n    bezel: \"#191A1F\",\n    rim: \"rgba(255,255,255,0.09)\",\n    knob: [\"#FFFFFF\", \"#F1F0ED\", \"#D4D3CE\"],\n    shadow: \"0,0,0\",\n    shadowA: 0.75,\n  },\n  light: {\n    fg: \"#141413\",\n    stateInk: \"\",\n    scrim: \"244,243,239\",\n    scrimFrom: 0.88,\n    scrimTo: 0.32,\n    track: \"#E0DED7\",\n    trackTop: \"rgba(70,58,38,0.28)\",\n    trackLip: \"rgba(255,255,255,0.95)\",\n    bezel: \"#ECEAE4\",\n    rim: \"rgba(255,255,255,0.9)\",\n    knob: [\"#FFFFFF\", \"#FAF9F6\", \"#DAD8D1\"],\n    shadow: \"48,40,28\",\n    shadowA: 0.34,\n  },\n} as const;\n\nfunction hexToRgb(hex: string) {\n  const h = hex.replace(\"#\", \"\");\n  const n = Number.parseInt(\n    h.length === 3\n      ? h\n          .split(\"\")\n          .map((c) => c + c)\n          .join(\"\")\n      : h,\n    16,\n  );\n  return `${(n >> 16) & 255},${(n >> 8) & 255},${n & 255}`;\n}\n\nexport function ToggleOn({\n  label = \"Auto-captions\",\n  state = \"On\",\n  accent = \"#3B5BFD\",\n  theme = \"dark\",\n  ground,\n  groundParams,\n  speed = 1,\n}: ToggleOnProps) {\n  const t = useCurrentFrame() * speed;\n  const { width: VW, height: VH, fps } = useVideoConfig();\n  const tall = VH > VW;\n  const u = tall ? VW / 760 : Math.min(VW / 1280, VH / 720);\n  const p = THEMES[theme];\n  const dark = theme === \"dark\";\n  const acc = hexToRgb(accent);\n  const stateInk = p.stateInk || accent;\n  const resolvedGround: GshPresetId | undefined =\n    ground ?? (groundParams ? undefined : dark ? \"earthshine\" : \"paper-crease\");\n\n  const W = 330 * u;\n  const H = 192 * u;\n  const pad = 13 * u;\n  const d = H - pad * 2;\n  const travel = W - pad * 2 - d;\n\n  const press = interpolate(t, [BEATS.press, BEATS.flip], [0, 1], {\n    ...CLAMP,\n    easing: ENTER,\n  });\n  const pos = spring({\n    frame: t - BEATS.flip,\n    fps,\n    config: { mass: 0.8, damping: 18, stiffness: 160 },\n  });\n  const relax = spring({\n    frame: t - BEATS.flip - 6,\n    fps,\n    config: { mass: 0.5, damping: 12, stiffness: 200 },\n  });\n  const stretch = d * 0.2 * press * (1 - Math.min(1, relax));\n  const posC = Math.min(1, pos);\n  const over = Math.max(0, pos - 1);\n  const knobLeft = pad + posC * (travel - stretch) + over * travel;\n  const knobRight = Math.min(\n    W - pad,\n    pad + posC * travel + d + stretch * (1 - posC),\n  );\n  const knobW = knobRight - knobLeft;\n\n  const fillEnd = knobLeft + knobW * (0.5 + 0.5 * posC) + pad * 1.2 * posC;\n  const fillOn = interpolate(pos, [0, 0.05], [0, 1], CLAMP);\n  const fillStart = pad * 0.6 * (1 - Math.min(1, pos * 3));\n\n  const cam = spring({\n    frame: t,\n    fps,\n    config: { mass: 1, damping: 26, stiffness: 120 },\n    durationInFrames: 54,\n  });\n  const camK = interpolate(cam, [0, 1], [1.1, 1]);\n  const lit = interpolate(t, [BEATS.light, BEATS.light + 30], [0, 1], {\n    ...CLAMP,\n    easing: ENTER,\n  });\n  const scrim = interpolate(lit, [0, 1], [p.scrimFrom, p.scrimTo]);\n  const open = interpolate(t, [BEATS.flip + 2, BEATS.flip + 26], [0, 1], {\n    ...CLAMP,\n    easing: MOVE,\n  });\n\n  const word = (start: number) =>\n    interpolate(t, [start, start + 14], [0, 1], { ...CLAMP, easing: ENTER });\n  const wLabel = word(BEATS.label);\n  const wState = word(BEATS.state);\n  const fontSize = (tall ? 92 : 76) * u;\n\n  const ink = (v: number) => ({\n    display: \"inline-block\",\n    opacity: v,\n    transform: `translateY(${(1 - v) * 0.22}em)`,\n    filter: v < 1 ? `blur(${(1 - v) * 6 * u}px)` : undefined,\n  });\n\n  const knobShadow = [\n    `0 0 0 ${0.6 * u}px rgba(${p.shadow},${p.shadowA * 0.35})`,\n    `0 ${2 * u}px ${3 * u}px rgba(${p.shadow},${p.shadowA * 0.5})`,\n    `0 ${10 * u}px ${20 * u}px rgba(${p.shadow},${p.shadowA * 0.45})`,\n    `inset 0 ${-3 * u}px ${2 * u}px rgba(0,0,0,0.07)`,\n    `inset 0 ${1.5 * u}px 0 rgba(255,255,255,1)`,\n  ].join(\", \");\n\n  const toggle = (\n    <div\n      style={{\n        position: \"relative\",\n        width: W + 14 * u,\n        height: H + 14 * u,\n        borderRadius: (H + 14 * u) / 2,\n        background: p.bezel,\n        boxShadow: [\n          `inset 0 ${-1 * u}px 0 ${p.rim}`,\n          `inset 0 ${1 * u}px ${1.5 * u}px rgba(${p.shadow},${p.shadowA * 0.25})`,\n          `0 ${24 * u}px ${48 * u}px -${12 * u}px rgba(${p.shadow},${p.shadowA * 0.55})`,\n          `0 0 ${60 * u}px ${-10 * u}px rgba(${acc},${0.45 * fillOn * lit})`,\n        ].join(\", \"),\n        flexShrink: 0,\n      }}\n    >\n      <div\n        style={{\n          position: \"absolute\",\n          left: 7 * u,\n          top: 7 * u,\n          width: W,\n          height: H,\n          borderRadius: H / 2,\n          overflow: \"hidden\",\n          background: p.track,\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            left: fillStart,\n            top: 0,\n            width: fillEnd - fillStart,\n            height: H,\n            borderRadius: H / 2,\n            opacity: fillOn,\n            background: `linear-gradient(180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0) 46%, rgba(0,0,0,0.14) 100%), ${accent}`,\n          }}\n        />\n        <div\n          style={{\n            position: \"absolute\",\n            inset: 0,\n            borderRadius: H / 2,\n            boxShadow: `inset 0 ${5 * u}px ${9 * u}px ${p.trackTop}, inset 0 ${1.5 * u}px ${2 * u}px ${p.trackTop}, inset 0 ${-1.2 * u}px 0 ${p.trackLip}`,\n          }}\n        />\n        <div\n          style={{\n            position: \"absolute\",\n            left: knobLeft,\n            top: pad,\n            width: knobW,\n            height: d,\n            borderRadius: d / 2,\n            background: `radial-gradient(ellipse 58% 34% at 50% 16%, rgba(255,255,255,0.95), transparent 80%), linear-gradient(180deg, ${p.knob[0]} 0%, ${p.knob[1]} 48%, ${p.knob[2]} 100%)`,\n            boxShadow: knobShadow,\n          }}\n        />\n      </div>\n    </div>\n  );\n\n  const labelEl = (\n    <div\n      style={{\n        whiteSpace: \"nowrap\",\n        fontFamily: sans,\n        fontWeight: 400,\n        fontSize,\n        lineHeight: 1.08,\n        letterSpacing: \"-0.035em\",\n        color: p.fg,\n        textAlign: tall ? \"center\" : \"left\",\n      }}\n    >\n      <span style={ink(wLabel)}>{label}</span>\n      {state ? (\n        <>\n          {tall ? <br /> : \" \"}\n          <span style={{ ...ink(wState), color: stateInk }}>{state}</span>\n        </>\n      ) : null}\n    </div>\n  );\n\n  return (\n    <AbsoluteFill\n      style={{ overflow: \"hidden\", background: dark ? \"#03050b\" : \"#eeece8\" }}\n    >\n      <GshGround\n        preset={resolvedGround}\n        params={groundParams}\n        timeOffset={dark ? 3 : 6}\n        timeScale={0.5}\n      />\n      <AbsoluteFill\n        style={{\n          background: `radial-gradient(70% 60% at 50% 45%, rgba(${p.scrim},${scrim}) 0%, rgba(${p.scrim},${Math.min(1, scrim + 0.12)}) 100%)`,\n        }}\n      />\n      <AbsoluteFill\n        style={{\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          transform: `scale(${camK})`,\n        }}\n      >\n        {tall ? (\n          <div\n            style={{\n              display: \"flex\",\n              flexDirection: \"column\",\n              alignItems: \"center\",\n              gap: 72 * u,\n              transform: `translateY(${(1 - open) * 90 * u}px)`,\n            }}\n          >\n            {toggle}\n            {labelEl}\n          </div>\n        ) : (\n          <div style={{ display: \"flex\", alignItems: \"center\" }}>\n            {toggle}\n            <div style={{ display: \"grid\", gridTemplateColumns: `${open}fr` }}>\n              <div style={{ minWidth: 0, paddingLeft: 56 * u }}>{labelEl}</div>\n            </div>\n          </div>\n        )}\n      </AbsoluteFill>\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/toggle-on.tsx"
    }
  ],
  "type": "registry:component"
}
