{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "account-access-card",
  "title": "Account Access Card",
  "description": "A credentials card that auto-fills email and password on a loop, with a danger zone.",
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [
    "@spectrumui/use-typewriter"
  ],
  "files": [
    {
      "path": "app/registry/account-access-card/account-access-card.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion, useInView } from \"motion/react\";\nimport { ChevronRight, CircleAlert, Lock } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport { usePrefersReducedMotion } from \"@/components/spectrumui/use-typewriter\";\n\nexport interface AccountCredential {\n  email: string;\n  /** How many password dots get \"typed\" for this account. */\n  passwordLength?: number;\n}\n\nexport interface AccountAccessCardProps {\n  title?: string;\n  description?: string;\n  emailLabel?: string;\n  passwordLabel?: string;\n  forgotLabel?: string;\n  buttonLabel?: string;\n  buttonIcon?: React.ReactNode;\n  dangerTitle?: string;\n  dangerDescription?: string;\n  /** Accounts the card cycles through, typing each one in. */\n  credentials?: AccountCredential[];\n  /** Turn the auto-fill animation off and show static values. */\n  animated?: boolean;\n  emailValue?: string;\n  passwordValue?: string;\n  onSubmit?: () => void;\n  onForgot?: () => void;\n  onDanger?: () => void;\n  className?: string;\n}\n\nconst DEFAULT_CREDENTIALS: AccountCredential[] = [\n  { email: \"alfa@spectrum.com\", passwordLength: 14 },\n  { email: \"john.doe@gmail.com\", passwordLength: 10 },\n  { email: \"emma@example.com\", passwordLength: 16 },\n];\n\ntype ActiveField = \"email\" | \"password\" | null;\n\nfunction Caret() {\n  return (\n    <motion.span\n      aria-hidden\n      className=\"ml-px inline-block h-3.5 w-px shrink-0 bg-foreground align-middle\"\n      animate={{ opacity: [1, 1, 0, 0] }}\n      transition={{ duration: 1, repeat: Infinity, times: [0, 0.5, 0.5, 1] }}\n    />\n  );\n}\n\nexport function AccountAccessCard({\n  title = \"Account Access\",\n  description = \"Update your credentials or re-authenticate.\",\n  emailLabel = \"Email Address\",\n  passwordLabel = \"Current Password\",\n  forgotLabel = \"Forgot?\",\n  buttonLabel = \"Update Security\",\n  buttonIcon,\n  dangerTitle = \"Danger Zone\",\n  dangerDescription = \"Archive account and remove access\",\n  credentials = DEFAULT_CREDENTIALS,\n  animated = true,\n  emailValue = \"alfa@spectrum.com\",\n  passwordValue = \"••••••••••••••••••••••••\",\n  onSubmit,\n  onForgot,\n  onDanger,\n  className,\n}: AccountAccessCardProps) {\n  const rootRef = React.useRef<HTMLDivElement>(null);\n  const inView = useInView(rootRef, { margin: \"-10% 0px\" });\n  const reduced = usePrefersReducedMotion();\n  const play = animated && inView && !reduced && credentials.length > 0;\n\n  const [email, setEmail] = React.useState(animated ? \"\" : emailValue);\n  const [password, setPassword] = React.useState(animated ? \"\" : passwordValue);\n  const [activeField, setActiveField] = React.useState<ActiveField>(null);\n\n  React.useEffect(() => {\n    if (!animated) {\n      setEmail(emailValue);\n      setPassword(passwordValue);\n      setActiveField(null);\n      return;\n    }\n    if (reduced) {\n      setEmail(credentials[0]?.email ?? emailValue);\n      setPassword(\"•\".repeat(credentials[0]?.passwordLength ?? 14));\n      setActiveField(null);\n      return;\n    }\n    if (!play) return;\n\n    let cancelled = false;\n    const sleep = (ms: number) =>\n      new Promise<void>((resolve) => setTimeout(resolve, ms));\n\n    (async () => {\n      await sleep(800);\n      let index = 0;\n      while (!cancelled) {\n        const cred = credentials[index % credentials.length];\n        const dots = cred.passwordLength ?? 14;\n\n        setActiveField(\"email\");\n        for (let i = 1; i <= cred.email.length; i++) {\n          if (cancelled) return;\n          setEmail(cred.email.slice(0, i));\n          await sleep(80 + Math.random() * 60);\n        }\n        await sleep(550);\n\n        setActiveField(\"password\");\n        for (let i = 1; i <= dots; i++) {\n          if (cancelled) return;\n          setPassword(\"•\".repeat(i));\n          await sleep(85 + Math.random() * 45);\n        }\n        setActiveField(null);\n        await sleep(3200);\n\n        for (let i = dots - 1; i >= 0; i--) {\n          if (cancelled) return;\n          setPassword(\"•\".repeat(i));\n          await sleep(18);\n        }\n        for (let i = cred.email.length - 1; i >= 0; i--) {\n          if (cancelled) return;\n          setEmail(cred.email.slice(0, i));\n          await sleep(24);\n        }\n        await sleep(750);\n        index++;\n      }\n    })();\n\n    return () => {\n      cancelled = true;\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [play, animated, reduced]);\n\n  return (\n    <div\n      ref={rootRef}\n      className={cn(\n        \"w-full rounded-[24px] bg-white p-5\",\n        \"shadow-[0_0_0_1px_rgba(10,10,10,0.05),0_1px_3px_0_rgba(0,0,0,0.1),0_1px_2px_-1px_rgba(0,0,0,0.1)]\",\n        \"dark:bg-neutral-950 dark:shadow-[0_0_0_1px_rgba(255,255,255,0.12),0_1px_3px_0_rgba(0,0,0,0.5)]\",\n        className,\n      )}\n    >\n      <h3 className=\"text-[16px] font-medium leading-6 text-foreground\">\n        {title}\n      </h3>\n      <p className=\"mt-1.5 text-sm leading-5 text-muted-foreground\">\n        {description}\n      </p>\n\n      {/* Email */}\n      <div className=\"mt-5\">\n        <label className=\"text-sm font-medium leading-[19.25px] text-foreground\">\n          {emailLabel}\n        </label>\n        <div\n          className={cn(\n            \"mt-3 flex h-8 items-center rounded-[18px] bg-neutral-200/50 px-2.5 transition-all duration-300 dark:bg-neutral-800/50\",\n            activeField === \"email\" &&\n              \"bg-neutral-200/80 ring-1 ring-neutral-300 dark:bg-neutral-800/80 dark:ring-neutral-700\",\n          )}\n        >\n          <span className=\"flex min-w-0 items-center text-sm text-muted-foreground\">\n            <span className=\"truncate\">{email}</span>\n            {activeField === \"email\" ? <Caret /> : null}\n          </span>\n        </div>\n      </div>\n\n      {/* Password */}\n      <div className=\"mt-6\">\n        <div className=\"flex items-center justify-between\">\n          <label className=\"text-sm font-medium leading-[19.25px] text-foreground\">\n            {passwordLabel}\n          </label>\n          <motion.button\n            type=\"button\"\n            onClick={onForgot}\n            whileTap={{ scale: 0.94 }}\n            className=\"text-xs font-medium uppercase leading-4 tracking-[0.6px] text-muted-foreground transition-colors hover:text-foreground\"\n          >\n            {forgotLabel}\n          </motion.button>\n        </div>\n        <div\n          className={cn(\n            \"mt-3 flex h-8 items-center rounded-[18px] bg-neutral-200/50 px-2.5 transition-all duration-300 dark:bg-neutral-800/50\",\n            activeField === \"password\" &&\n              \"bg-neutral-200/80 ring-1 ring-neutral-300 dark:bg-neutral-800/80 dark:ring-neutral-700\",\n          )}\n        >\n          <span className=\"flex min-w-0 items-center text-sm leading-none text-muted-foreground\">\n            <span className=\"truncate pt-0.5 tracking-[1px]\">{password}</span>\n            {activeField === \"password\" ? <Caret /> : null}\n          </span>\n        </div>\n      </div>\n\n      {/* Submit */}\n      <motion.button\n        type=\"button\"\n        onClick={onSubmit}\n        whileHover=\"hover\"\n        whileTap={{ scale: 0.97 }}\n        className=\"mt-[21px] flex h-[36px] w-full items-center justify-center gap-2 rounded-[18px] bg-black text-sm font-medium leading-5 text-[#eff6ff] transition-colors hover:bg-neutral-800 dark:bg-white dark:text-neutral-950 dark:hover:bg-neutral-200\"\n      >\n        <motion.span\n          variants={{\n            hover: {\n              rotate: [0, -12, 10, -6, 0],\n              transition: { duration: 0.5 },\n            },\n          }}\n          className=\"flex\"\n        >\n          {buttonIcon ?? <Lock className=\"h-4 w-4\" />}\n        </motion.span>\n        {buttonLabel}\n      </motion.button>\n\n      {/* Danger zone */}\n      <motion.button\n        type=\"button\"\n        onClick={onDanger}\n        whileTap={{ scale: 0.985 }}\n        className=\"group mt-[17px] flex min-h-[73px] w-full items-center gap-3.5 rounded-[18px] bg-neutral-100/50 p-4 text-left transition-colors hover:bg-neutral-100 dark:bg-neutral-900/50 dark:hover:bg-neutral-900\"\n      >\n        <CircleAlert className=\"h-4 w-4 shrink-0 self-start text-red-500\" />\n        <span className=\"min-w-0 flex-1\">\n          <span className=\"block text-sm font-medium leading-[19.25px] text-foreground\">\n            {dangerTitle}\n          </span>\n          <span className=\"mt-1 block truncate text-sm leading-5 text-muted-foreground\">\n            {dangerDescription}\n          </span>\n        </span>\n        <ChevronRight className=\"h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200 group-hover:translate-x-0.5\" />\n      </motion.button>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/spectrumui/account-access-card.tsx"
    }
  ],
  "type": "registry:component"
}
