{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "avatar-picker",
  "type": "registry:component",
  "title": "Avatar Picker",
  "description": "Profile-setup card with an avatar stage and username field (KokonutUI, MIT). Chrome is contract-bridged; the avatar artwork keeps its illustration color.",
  "dependencies": [
    "framer-motion"
  ],
  "registryDependencies": [
    "https://design.subconscious.ai/r/utils.json",
    "button",
    "card",
    "input"
  ],
  "files": [
    {
      "path": "registry/components/avatar-picker.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\n/**\n * @author: @dorianbaffier\n * @description: Avatar Picker\n * @version: 2.0.0\n * @date: 2026-02-22\n * @license: MIT\n * @website: https://kokonutui.com\n * @github: https://github.com/kokonut-labs/kokonutui\n *\n * Bridged to the Subconscious design contract: the three lucide-react imports\n * (Check, ChevronRight, User2) were inlined as raw SVG per issue #463. Stroke\n * style follows the brand iconography canon (1.5px, square cap, miter join)\n * rather than lucide's round/round default. User2 is lucide's alias for\n * UserRound; path data from lucide-react@0.400.0 (ISC).\n */\n\nimport * as React from \"react\";\nimport type { Variants } from \"framer-motion\";\nimport { AnimatePresence, motion, useReducedMotion } from \"framer-motion\";\nimport type { ReactNode } from \"react\";\nimport { useState } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { Input } from \"@/components/ui/input\";\nimport { cn } from \"@/registry/lib/utils\";\n\ntype IconProps = React.SVGProps<SVGSVGElement>;\n\nfunction CheckIcon({ className, ...props }: IconProps) {\n  return (\n    <svg\n      className={className}\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeLinecap=\"square\"\n      strokeLinejoin=\"miter\"\n      strokeWidth={1.5}\n      viewBox=\"0 0 24 24\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      {...props}\n    >\n      <path d=\"M20 6 9 17l-5-5\" />\n    </svg>\n  );\n}\n\nfunction ChevronRightIcon({ className, ...props }: IconProps) {\n  return (\n    <svg\n      className={className}\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeLinecap=\"square\"\n      strokeLinejoin=\"miter\"\n      strokeWidth={1.5}\n      viewBox=\"0 0 24 24\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      {...props}\n    >\n      <path d=\"m9 18 6-6-6-6\" />\n    </svg>\n  );\n}\n\nfunction User2Icon({ className, ...props }: IconProps) {\n  return (\n    <svg\n      className={className}\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeLinecap=\"square\"\n      strokeLinejoin=\"miter\"\n      strokeWidth={1.5}\n      viewBox=\"0 0 24 24\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      {...props}\n    >\n      <circle cx=\"12\" cy=\"8\" r=\"5\" />\n      <path d=\"M20 21a8 8 0 0 0-16 0\" />\n    </svg>\n  );\n}\n\ninterface Avatar {\n  id: number;\n  svg: ReactNode;\n  alt: string;\n}\n\n// The avatar artwork below is intentional illustration: its raw-hex SVG fills\n// and the AVATAR_RGB ring colors are picture content, not UI chrome, so they\n// are not theme-bridged (re-coloring them would erase the avatars). Every\n// surface/text/border color in this component IS bridged to contract tokens.\n//\n// RGB values for the per-avatar color ring on the stage\nconst AVATAR_RGB: Record<number, string> = {\n  1: \"255, 0, 91\",\n  2: \"255, 125, 16\",\n  3: \"255, 0, 91\",\n  4: \"137, 252, 179\",\n};\n\nconst avatars: Avatar[] = [\n  {\n    id: 1,\n    svg: (\n      <svg\n        aria-label=\"Avatar 1\"\n        fill=\"none\"\n        height=\"40\"\n        role=\"img\"\n        viewBox=\"0 0 36 36\"\n        width=\"40\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      >\n        <title>Avatar 1</title>\n        <mask\n          height=\"36\"\n          id=\":r111:\"\n          maskUnits=\"userSpaceOnUse\"\n          width=\"36\"\n          x=\"0\"\n          y=\"0\"\n        >\n          <rect fill=\"#FFFFFF\" height=\"36\" rx=\"72\" width=\"36\" />\n        </mask>\n        <g mask=\"url(#:r111:)\">\n          <rect fill=\"#ff005b\" height=\"36\" width=\"36\" />\n          <rect\n            fill=\"#ffb238\"\n            height=\"36\"\n            rx=\"6\"\n            transform=\"translate(9 -5) rotate(219 18 18) scale(1)\"\n            width=\"36\"\n            x=\"0\"\n            y=\"0\"\n          />\n          <g transform=\"translate(4.5 -4) rotate(9 18 18)\">\n            <path\n              d=\"M15 19c2 1 4 1 6 0\"\n              fill=\"none\"\n              stroke=\"#000000\"\n              strokeLinecap=\"round\"\n            />\n            <rect\n              fill=\"#000000\"\n              height=\"2\"\n              rx=\"1\"\n              stroke=\"none\"\n              width=\"1.5\"\n              x=\"10\"\n              y=\"14\"\n            />\n            <rect\n              fill=\"#000000\"\n              height=\"2\"\n              rx=\"1\"\n              stroke=\"none\"\n              width=\"1.5\"\n              x=\"24\"\n              y=\"14\"\n            />\n          </g>\n        </g>\n      </svg>\n    ),\n    alt: \"Avatar 1\",\n  },\n  {\n    id: 2,\n    svg: (\n      <svg\n        aria-label=\"Avatar 2\"\n        fill=\"none\"\n        height=\"40\"\n        role=\"img\"\n        viewBox=\"0 0 36 36\"\n        width=\"40\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      >\n        <title>Avatar 2</title>\n        <mask\n          height=\"36\"\n          id=\":R4mrttb:\"\n          maskUnits=\"userSpaceOnUse\"\n          width=\"36\"\n          x=\"0\"\n          y=\"0\"\n        >\n          <rect fill=\"#FFFFFF\" height=\"36\" rx=\"72\" width=\"36\" />\n        </mask>\n        <g mask=\"url(#:R4mrttb:)\">\n          <rect fill=\"#ff7d10\" height=\"36\" width=\"36\" />\n          <rect\n            fill=\"#0a0310\"\n            height=\"36\"\n            rx=\"6\"\n            transform=\"translate(5 -1) rotate(55 18 18) scale(1.1)\"\n            width=\"36\"\n            x=\"0\"\n            y=\"0\"\n          />\n          <g transform=\"translate(7 -6) rotate(-5 18 18)\">\n            <path\n              d=\"M15 20c2 1 4 1 6 0\"\n              fill=\"none\"\n              stroke=\"#FFFFFF\"\n              strokeLinecap=\"round\"\n            />\n            <rect\n              fill=\"#FFFFFF\"\n              height=\"2\"\n              rx=\"1\"\n              stroke=\"none\"\n              width=\"1.5\"\n              x=\"14\"\n              y=\"14\"\n            />\n            <rect\n              fill=\"#FFFFFF\"\n              height=\"2\"\n              rx=\"1\"\n              stroke=\"none\"\n              width=\"1.5\"\n              x=\"20\"\n              y=\"14\"\n            />\n          </g>\n        </g>\n      </svg>\n    ),\n    alt: \"Avatar 2\",\n  },\n  {\n    id: 3,\n    svg: (\n      <svg\n        aria-label=\"Avatar 3\"\n        fill=\"none\"\n        height=\"40\"\n        role=\"img\"\n        viewBox=\"0 0 36 36\"\n        width=\"40\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      >\n        <title>Avatar 3</title>\n        <mask\n          height=\"36\"\n          id=\":r11c:\"\n          maskUnits=\"userSpaceOnUse\"\n          width=\"36\"\n          x=\"0\"\n          y=\"0\"\n        >\n          <rect fill=\"#FFFFFF\" height=\"36\" rx=\"72\" width=\"36\" />\n        </mask>\n        <g mask=\"url(#:r11c:)\">\n          <rect fill=\"#0a0310\" height=\"36\" width=\"36\" />\n          <rect\n            fill=\"#ff005b\"\n            height=\"36\"\n            rx=\"36\"\n            transform=\"translate(-3 7) rotate(227 18 18) scale(1.2)\"\n            width=\"36\"\n            x=\"0\"\n            y=\"0\"\n          />\n          <g transform=\"translate(-3 3.5) rotate(7 18 18)\">\n            <path d=\"M13,21 a1,0.75 0 0,0 10,0\" fill=\"#FFFFFF\" />\n            <rect\n              fill=\"#FFFFFF\"\n              height=\"2\"\n              rx=\"1\"\n              stroke=\"none\"\n              width=\"1.5\"\n              x=\"12\"\n              y=\"14\"\n            />\n            <rect\n              fill=\"#FFFFFF\"\n              height=\"2\"\n              rx=\"1\"\n              stroke=\"none\"\n              width=\"1.5\"\n              x=\"22\"\n              y=\"14\"\n            />\n          </g>\n        </g>\n      </svg>\n    ),\n    alt: \"Avatar 3\",\n  },\n  {\n    id: 4,\n    svg: (\n      <svg\n        aria-label=\"Avatar 4\"\n        fill=\"none\"\n        height=\"40\"\n        role=\"img\"\n        viewBox=\"0 0 36 36\"\n        width=\"40\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      >\n        <title>Avatar 4</title>\n        <mask\n          height=\"36\"\n          id=\":r1gg:\"\n          maskUnits=\"userSpaceOnUse\"\n          width=\"36\"\n          x=\"0\"\n          y=\"0\"\n        >\n          <rect fill=\"#FFFFFF\" height=\"36\" rx=\"72\" width=\"36\" />\n        </mask>\n        <g mask=\"url(#:r1gg:)\">\n          <rect fill=\"#d8fcb3\" height=\"36\" width=\"36\" />\n          <rect\n            fill=\"#89fcb3\"\n            height=\"36\"\n            rx=\"6\"\n            transform=\"translate(9 -5) rotate(219 18 18) scale(1)\"\n            width=\"36\"\n            x=\"0\"\n            y=\"0\"\n          />\n          <g transform=\"translate(4.5 -4) rotate(9 18 18)\">\n            <path\n              d=\"M15 19c2 1 4 1 6 0\"\n              fill=\"none\"\n              stroke=\"#000000\"\n              strokeLinecap=\"round\"\n            />\n            <rect\n              fill=\"#000000\"\n              height=\"2\"\n              rx=\"1\"\n              stroke=\"none\"\n              width=\"1.5\"\n              x=\"10\"\n              y=\"14\"\n            />\n            <rect\n              fill=\"#000000\"\n              height=\"2\"\n              rx=\"1\"\n              stroke=\"none\"\n              width=\"1.5\"\n              x=\"24\"\n              y=\"14\"\n            />\n          </g>\n        </g>\n      </svg>\n    ),\n    alt: \"Avatar 4\",\n  },\n];\n\ninterface ProfileSetupProps {\n  onComplete?: (data: { username: string; avatarId: number }) => void;\n  className?: string;\n}\n\nconst containerVariants: Variants = {\n  initial: { opacity: 0 },\n  animate: {\n    opacity: 1,\n    transition: { staggerChildren: 0.06, delayChildren: 0.05 },\n  },\n};\n\nconst thumbnailVariants: Variants = {\n  initial: { opacity: 0, y: 6 },\n  animate: {\n    opacity: 1,\n    y: 0,\n    transition: { duration: 0.28, ease: \"easeOut\" },\n  },\n};\n\nexport default function ProfileSetup({\n  onComplete,\n  className,\n}: ProfileSetupProps) {\n  const [selectedAvatar, setSelectedAvatar] = useState<Avatar>(avatars[0]);\n  const [username, setUsername] = useState(\"\");\n  const [isFocused, setIsFocused] = useState(false);\n  const shouldReduceMotion = useReducedMotion();\n\n  const handleAvatarSelect = (avatar: Avatar) => {\n    if (avatar.id === selectedAvatar.id) return;\n    setSelectedAvatar(avatar);\n  };\n\n  const handleSubmit = () => {\n    if (username.trim() && onComplete) {\n      onComplete({\n        username: username.trim(),\n        avatarId: selectedAvatar.id,\n      });\n    }\n  };\n\n  const isValid = username.trim().length >= 3;\n  const showError = username.trim().length > 0 && username.trim().length < 3;\n  const rgb = AVATAR_RGB[selectedAvatar.id];\n\n  return (\n    <Card\n      className={cn(\"relative mx-auto w-full\", className)}\n      style={{\n        background: \"var(--backgroundColor)\",\n        borderColor: \"color-mix(in srgb, var(--color) 15%, transparent)\",\n        maxWidth: \"400px\",\n      }}\n    >\n      <CardContent className=\"p-8\">\n        <div className=\"space-y-8\">\n          {/* Header */}\n          <div className=\"space-y-1 text-center\">\n            <h2 className=\"font-semibold text-xl tracking-tight\">\n              Pick Your Avatar\n            </h2>\n            <p\n              className=\"text-sm\"\n              style={{\n                color: \"color-mix(in srgb, var(--color) 60%, transparent)\",\n              }}\n            >\n              Choose one to get started\n            </p>\n          </div>\n\n          {/* Avatar Stage */}\n          <div className=\"flex flex-col items-center gap-4\">\n            {/*\n             * Two-div approach: outer div holds the animated color ring\n             * (no overflow-hidden so border color updates cleanly),\n             * inner div clips the avatar SVG.\n             * scale-[4] fills the 160px circle with the avatar's background.\n             */}\n            <div className=\"relative h-40 w-40\">\n              {/* Animated per-avatar color ring */}\n              <motion.div\n                animate={{\n                  borderColor: `rgba(${rgb}, 0.55)`,\n                }}\n                aria-hidden=\"true\"\n                className=\"pointer-events-none absolute inset-0 rounded-full border-2\"\n                transition={\n                  shouldReduceMotion\n                    ? { duration: 0 }\n                    : { duration: 0.45, ease: \"easeOut\" }\n                }\n              />\n\n              {/* Avatar circle — clips content */}\n              <div className=\"relative h-full w-full overflow-hidden rounded-full\">\n                <AnimatePresence mode=\"wait\">\n                  <motion.div\n                    animate={{ opacity: 1 }}\n                    className=\"absolute inset-0 flex items-center justify-center\"\n                    exit={{ opacity: 0 }}\n                    initial={{ opacity: 0 }}\n                    key={selectedAvatar.id}\n                    transition={\n                      shouldReduceMotion\n                        ? { duration: 0 }\n                        : { duration: 0.2, ease: \"easeOut\" }\n                    }\n                  >\n                    {/* scale-[4]: 40px SVG × 4 = 160px, fills the circle */}\n                    <div className=\"scale-[4] transform\">\n                      {selectedAvatar.svg}\n                    </div>\n                  </motion.div>\n                </AnimatePresence>\n              </div>\n            </div>\n\n            {/* Avatar name — fades with selection */}\n            <AnimatePresence mode=\"wait\">\n              <motion.span\n                animate={{ opacity: 1 }}\n                className=\"text-[11px] uppercase tracking-[0.12em]\"\n                style={{\n                  color: \"color-mix(in srgb, var(--color) 60%, transparent)\",\n                }}\n                exit={{ opacity: 0 }}\n                initial={{ opacity: 0 }}\n                key={selectedAvatar.id}\n                transition={\n                  shouldReduceMotion\n                    ? { duration: 0 }\n                    : { duration: 0.16, ease: \"easeOut\" }\n                }\n              >\n                {selectedAvatar.alt}\n              </motion.span>\n            </AnimatePresence>\n\n            {/* Thumbnail strip */}\n            <motion.div\n              animate=\"animate\"\n              className=\"flex gap-3\"\n              initial=\"initial\"\n              variants={containerVariants}\n            >\n              {avatars.map((avatar) => {\n                const isSelected = selectedAvatar.id === avatar.id;\n                return (\n                  <motion.button\n                    aria-label={`Select ${avatar.alt}`}\n                    aria-pressed={isSelected}\n                    className={cn(\n                      \"relative h-14 w-14 overflow-hidden rounded-md border transition-opacity duration-200 ease-out\",\n                      isSelected\n                        ? \"opacity-100\"\n                        : \"opacity-50 hover:opacity-100\"\n                    )}\n                    key={avatar.id}\n                    onClick={() => handleAvatarSelect(avatar)}\n                    style={{\n                      background:\n                        \"color-mix(in srgb, var(--color) 8%, transparent)\",\n                      borderColor: isSelected\n                        ? \"color-mix(in srgb, var(--color) 20%, transparent)\"\n                        : \"color-mix(in srgb, var(--color) 15%, transparent)\",\n                    }}\n                    type=\"button\"\n                    variants={thumbnailVariants}\n                  >\n                    <div className=\"absolute inset-0 flex items-center justify-center\">\n                      <div className=\"scale-[2.3] transform\">{avatar.svg}</div>\n                    </div>\n                    {isSelected && (\n                      <div\n                        className=\"absolute -right-0.5 -bottom-0.5 flex h-5 w-5 items-center justify-center rounded-full\"\n                        style={{ background: \"var(--color)\" }}\n                      >\n                        <CheckIcon\n                          aria-hidden=\"true\"\n                          className=\"h-3 w-3\"\n                          style={{ color: \"var(--backgroundColor)\" }}\n                        />\n                      </div>\n                    )}\n                  </motion.button>\n                );\n              })}\n            </motion.div>\n          </div>\n\n          {/* Username field */}\n          <div className=\"space-y-4\">\n            <div className=\"space-y-2\">\n              <div className=\"flex items-center justify-between\">\n                <label className=\"font-medium text-sm\" htmlFor=\"username\">\n                  Username\n                </label>\n                <span\n                  className=\"text-xs tabular-nums transition-colors duration-200 ease-out\"\n                  style={{\n                    color:\n                      username.length >= 18\n                        ? \"var(--color)\"\n                        : \"color-mix(in srgb, var(--color) 30%, transparent)\",\n                  }}\n                >\n                  {username.length}/20\n                </span>\n              </div>\n\n              <div className=\"relative\">\n                <Input\n                  autoComplete=\"username\"\n                  className={cn(\n                    \"h-10 pl-9 text-sm\",\n                    showError &&\n                      \"border-destructive/50 focus-visible:ring-destructive\"\n                  )}\n                  id=\"username\"\n                  maxLength={20}\n                  name=\"username\"\n                  onBlur={() => setIsFocused(false)}\n                  onChange={(e) => setUsername(e.target.value)}\n                  onFocus={() => setIsFocused(true)}\n                  placeholder=\"your_username…\"\n                  spellCheck={false}\n                  type=\"text\"\n                  value={username}\n                />\n                <User2Icon\n                  aria-hidden=\"true\"\n                  className=\"absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transition-colors duration-200 ease-out\"\n                  style={{\n                    color: isFocused\n                      ? \"var(--color)\"\n                      : \"color-mix(in srgb, var(--color) 60%, transparent)\",\n                  }}\n                />\n              </div>\n\n              <AnimatePresence>\n                {showError && (\n                  <motion.p\n                    animate={{ opacity: 1, y: 0 }}\n                    className=\"ml-0.5 text-destructive text-xs\"\n                    exit={{ opacity: 0, y: -4 }}\n                    initial={{ opacity: 0, y: -4 }}\n                    role=\"alert\"\n                    transition={{ duration: 0.15, ease: \"easeOut\" }}\n                  >\n                    Username must be at least 3 characters\n                  </motion.p>\n                )}\n              </AnimatePresence>\n            </div>\n\n            <Button\n              className=\"group h-10 w-full text-sm\"\n              disabled={!isValid}\n              onClick={handleSubmit}\n              type=\"button\"\n            >\n              Get Started\n              <ChevronRightIcon\n                aria-hidden=\"true\"\n                className=\"ml-1 h-4 w-4 transition-colors duration-200 ease-out\"\n              />\n            </Button>\n          </div>\n        </div>\n      </CardContent>\n    </Card>\n  );\n}\n"
    }
  ]
}
