{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"switch-cards","type":"registry:component","title":"Switch cards","description":"Three settings cards built on a springy switch — notification preferences with a saved cue, plan features with dependent options and a save bar, and privacy toggles that ask before turning protection off.","author":"GetUrUI <hello@geturui.dev>","dependencies":["motion","lucide-react"],"registryDependencies":[],"files":[{"path":"registry/geturui/examples/switch-cards/notifications.tsx","type":"registry:component","target":"components/examples/switch-cards/notifications.tsx","content":"\"use client\";\n\nimport * as React from \"react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { BellOff, Check, Mail, Smartphone } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface NotificationSetting {\n  id: string;\n  label: string;\n  description: string;\n  channel: \"email\" | \"push\";\n  enabled: boolean;\n}\n\nexport interface NotificationSwitchCardProps {\n  title?: string;\n  settings?: NotificationSetting[];\n  onChange?: (id: string, enabled: boolean) => void;\n  className?: string;\n}\n\nconst SETTINGS: NotificationSetting[] = [\n  { id: \"mentions\", label: \"Mentions and replies\", description: \"When someone @mentions you or replies to your thread.\", channel: \"email\", enabled: true },\n  { id: \"digest\", label: \"Weekly digest\", description: \"A Monday summary of activity in your projects.\", channel: \"email\", enabled: true },\n  { id: \"product\", label: \"Product updates\", description: \"New features and changes, about once a month.\", channel: \"email\", enabled: false },\n  { id: \"assigned\", label: \"Assigned to you\", description: \"New tasks and reviews that need your input.\", channel: \"push\", enabled: true },\n  { id: \"deploys\", label: \"Deploy status\", description: \"Builds that fail or finish on production.\", channel: \"push\", enabled: false },\n];\nconst GROUPS = [\n  { channel: \"email\", label: \"Email\", Icon: Mail },\n  { channel: \"push\", label: \"Push\", Icon: Smartphone },\n] as const;\nconst spring = { type: \"spring\", stiffness: 520, damping: 32 } as const;\n\n/** Accessible switch with a springy thumb that stretches while pressed. */\nfunction Switch({ checked, onToggle, labelledBy, describedBy }: { checked: boolean; onToggle: () => void; labelledBy: string; describedBy: string }) {\n  return (\n    <motion.button\n      type=\"button\"\n      role=\"switch\"\n      aria-checked={checked}\n      aria-labelledby={labelledBy}\n      aria-describedby={describedBy}\n      onClick={onToggle}\n      whileTap=\"press\"\n      className={cn(\n        \"relative inline-flex h-6 w-10 shrink-0 items-center rounded-full border p-[2px] outline-none transition-colors duration-200 focus-visible:shadow-[var(--focus-ring)]\",\n        checked ? \"border-transparent bg-volt\" : \"border-border-strong bg-surface-sunken\",\n      )}\n    >\n      <motion.span\n        animate={{ x: checked ? 16 : 0 }}\n        variants={{ press: { scaleX: 1.25 } }}\n        transition={spring}\n        style={{ originX: checked ? 1 : 0 }}\n        className={cn(\"block size-[18px] rounded-full shadow-sm\", checked ? \"bg-on-volt\" : \"bg-ink-subtle\")}\n      />\n    </motion.button>\n  );\n}\n\n/** Notification preferences grouped by channel; each switch springs and a quiet \"Saved\" confirmation fades in. */\nexport function NotificationSwitchCard({ title = \"Notifications\", settings = SETTINGS, onChange, className }: NotificationSwitchCardProps) {\n  const reduce = useReducedMotion();\n  const uid = React.useId();\n  const [state, setState] = React.useState(() => Object.fromEntries(settings.map((s) => [s.id, s.enabled])));\n  const [saved, setSaved] = React.useState(0);\n  const allOff = settings.every((s) => !state[s.id]);\n\n  React.useEffect(() => {\n    if (!saved) return;\n    const t = setTimeout(() => setSaved(0), 1800);\n    return () => clearTimeout(t);\n  }, [saved]);\n\n  function toggle(id: string) {\n    const next = !state[id];\n    setState((s) => ({ ...s, [id]: next }));\n    setSaved((n) => n + 1);\n    onChange?.(id, next);\n  }\n\n  return (\n    <section aria-labelledby={`${uid}-title`} className={cn(\"w-full max-w-[460px] rounded-lg border border-border bg-surface-raised shadow-sm\", className)}>\n      <header className=\"flex items-start justify-between gap-3 border-b border-border px-5 py-4\">\n        <div>\n          <h2 id={`${uid}-title`} className=\"font-display text-[16px] font-semibold text-ink\">{title}</h2>\n          <p className=\"mt-0.5 text-[12.5px] text-ink-muted\">Choose what reaches you and where.</p>\n        </div>\n        <p role=\"status\" className=\"h-6 text-[12px]\">\n          <AnimatePresence>\n            {saved > 0 && (\n              <motion.span key={saved} initial={{ opacity: 0, y: reduce ? 0 : 4, filter: \"blur(4px)\" }} animate={{ opacity: 1, y: 0, filter: \"blur(0px)\" }} exit={{ opacity: 0 }} className=\"inline-flex items-center gap-1 rounded-full bg-surface-sunken px-2 py-1 font-medium text-ink-muted ring-1 ring-border\">\n                <Check aria-hidden className=\"size-3 text-success\" />\n                Saved\n              </motion.span>\n            )}\n          </AnimatePresence>\n        </p>\n      </header>\n\n      {GROUPS.map(({ channel, label, Icon }) => (\n        <div key={channel} role=\"group\" aria-labelledby={`${uid}-${channel}`} className=\"px-5 py-3\">\n          <p id={`${uid}-${channel}`} className=\"flex items-center gap-1.5 py-1 text-[11.5px] font-medium uppercase tracking-wide text-ink-subtle\">\n            <Icon aria-hidden className=\"size-3.5\" />\n            {label}\n          </p>\n          <ul>\n            {settings.filter((s) => s.channel === channel).map((s) => (\n              <li key={s.id} className=\"flex items-center justify-between gap-4 py-2.5\">\n                <div className=\"min-w-0\">\n                  <p id={`${uid}-${s.id}`} className={cn(\"text-[13.5px] font-medium transition-colors\", state[s.id] ? \"text-ink\" : \"text-ink-muted\")}>{s.label}</p>\n                  <p id={`${uid}-${s.id}-d`} className=\"text-[12px] text-ink-subtle\">{s.description}</p>\n                </div>\n                <Switch checked={!!state[s.id]} onToggle={() => toggle(s.id)} labelledBy={`${uid}-${s.id}`} describedBy={`${uid}-${s.id}-d`} />\n              </li>\n            ))}\n          </ul>\n        </div>\n      ))}\n\n      <AnimatePresence initial={false}>\n        {allOff && (\n          <motion.p initial={{ height: 0, opacity: 0 }} animate={{ height: \"auto\", opacity: 1 }} exit={{ height: 0, opacity: 0 }} transition={reduce ? { duration: 0 } : spring} className=\"overflow-hidden\">\n            <span className=\"flex items-center gap-2 border-t border-border px-5 py-3 text-[12.5px] text-warning\">\n              <BellOff aria-hidden className=\"size-4 shrink-0\" />\n              All notifications are off. You may miss mentions and failed deploys.\n            </span>\n          </motion.p>\n        )}\n      </AnimatePresence>\n    </section>\n  );\n}\n"},{"path":"registry/geturui/examples/switch-cards/plan-features.tsx","type":"registry:component","target":"components/examples/switch-cards/plan-features.tsx","content":"\"use client\";\n\nimport * as React from \"react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { Check, LoaderCircle, Lock } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface PlanFeature {\n  id: string;\n  label: string;\n  description: string;\n  enabled: boolean;\n  /** Id of the feature this one depends on. */\n  parent?: string;\n  /** Plan needed to use it; renders a locked row. */\n  requires?: string;\n}\n\nexport interface PlanFeatureCardProps {\n  workspace?: string;\n  features?: PlanFeature[];\n  onSave?: (values: Record<string, boolean>) => void | Promise<void>;\n  onUpgrade?: (plan: string) => void;\n  className?: string;\n}\n\nconst FEATURES: PlanFeature[] = [\n  { id: \"public\", label: \"Public links\", description: \"Anyone with the link can view shared pages.\", enabled: true },\n  { id: \"password\", label: \"Require a password\", description: \"Visitors enter a password before viewing.\", enabled: false, parent: \"public\" },\n  { id: \"index\", label: \"Allow search engines\", description: \"Let public pages appear in search results.\", enabled: false, parent: \"public\" },\n  { id: \"ai\", label: \"Writing assistant\", description: \"Draft, summarize and rewrite inside docs.\", enabled: false },\n  { id: \"ai-train\", label: \"Learn from workspace\", description: \"Use your docs to improve suggestions for your team.\", enabled: false, parent: \"ai\" },\n  { id: \"sso\", label: \"SAML single sign-on\", description: \"Sign in through Okta, Entra ID or Google Workspace.\", enabled: false, requires: \"Business\" },\n];\nconst spring = { type: \"spring\", stiffness: 520, damping: 32 } as const;\nconst soft = { type: \"spring\", stiffness: 320, damping: 30 } as const;\n\nfunction Switch({ checked, disabled, onToggle, labelledBy, describedBy }: { checked: boolean; disabled?: boolean; onToggle: () => void; labelledBy: string; describedBy: string }) {\n  return (\n    <motion.button\n      type=\"button\"\n      role=\"switch\"\n      aria-checked={checked}\n      aria-labelledby={labelledBy}\n      aria-describedby={describedBy}\n      disabled={disabled}\n      onClick={onToggle}\n      whileTap={disabled ? undefined : \"press\"}\n      className={cn(\n        \"relative inline-flex h-6 w-10 shrink-0 items-center rounded-full border p-[2px] outline-none transition-[background-color,border-color,opacity] duration-200 focus-visible:shadow-[var(--focus-ring)] disabled:cursor-not-allowed disabled:opacity-40\",\n        checked ? \"border-transparent bg-volt\" : \"border-border-strong bg-surface-sunken\",\n      )}\n    >\n      <motion.span animate={{ x: checked ? 16 : 0 }} variants={{ press: { scaleX: 1.25 } }} transition={spring} style={{ originX: checked ? 1 : 0 }} className={cn(\"block size-[18px] rounded-full shadow-sm\", checked ? \"bg-on-volt\" : \"bg-ink-subtle\")} />\n    </motion.button>\n  );\n}\n\n/** Workspace feature toggles where child options unlock with their parent, locked features show the plan they need, and a save bar springs in once something changes. */\nexport function PlanFeatureCard({ workspace = \"Northwind Studio\", features = FEATURES, onSave = () => new Promise((r) => setTimeout(r, 900)), onUpgrade, className }: PlanFeatureCardProps) {\n  const reduce = useReducedMotion();\n  const uid = React.useId();\n  const initial = React.useMemo(() => Object.fromEntries(features.map((f) => [f.id, f.enabled])), [features]);\n  const [base, setBase] = React.useState(initial);\n  const [values, setValues] = React.useState(initial);\n  const [status, setStatus] = React.useState<\"idle\" | \"saving\" | \"saved\">(\"idle\");\n  const dirty = features.filter((f) => values[f.id] !== base[f.id]).length;\n\n  async function save() {\n    setStatus(\"saving\");\n    await onSave(values);\n    setBase(values);\n    setStatus(\"saved\");\n    setTimeout(() => setStatus(\"idle\"), 1600);\n  }\n\n  return (\n    <section aria-labelledby={`${uid}-t`} className={cn(\"w-full max-w-[480px] overflow-hidden rounded-lg border border-border bg-surface-raised shadow-sm\", className)}>\n      <header className=\"border-b border-border px-5 py-4\">\n        <h2 id={`${uid}-t`} className=\"font-display text-[16px] font-semibold text-ink\">Features</h2>\n        <p className=\"mt-0.5 text-[12.5px] text-ink-muted\">Applies to everyone in {workspace}.</p>\n      </header>\n      <ul className=\"divide-y divide-border\">\n        {features.filter((f) => !f.parent).map((f) => {\n          const children = features.filter((c) => c.parent === f.id);\n          const on = !!values[f.id];\n          return (\n            <li key={f.id} className=\"px-5 py-3.5\">\n              <Row f={f} uid={uid} checked={on} disabled={!!f.requires} onToggle={() => setValues((v) => ({ ...v, [f.id]: !on }))} onUpgrade={onUpgrade} />\n              {children.length > 0 && (\n                <motion.ul\n                  aria-label={`${f.label} options`}\n                  animate={{ opacity: on ? 1 : 0.55 }}\n                  transition={{ duration: 0.2 }}\n                  className=\"relative ml-1 mt-2 space-y-2.5 border-l border-border-strong/40 pl-4\"\n                >\n                  {children.map((c) => (\n                    <li key={c.id}>\n                      <Row f={c} uid={uid} checked={on && !!values[c.id]} disabled={!on} onToggle={() => setValues((v) => ({ ...v, [c.id]: !v[c.id] }))} />\n                    </li>\n                  ))}\n                  <AnimatePresence initial={false}>\n                    {!on && (\n                      <motion.li initial={{ height: 0, opacity: 0 }} animate={{ height: \"auto\", opacity: 1 }} exit={{ height: 0, opacity: 0 }} transition={reduce ? { duration: 0 } : soft} className=\"overflow-hidden text-[11.5px] text-ink-subtle\">\n                        Turn on {f.label.toLowerCase()} to change these.\n                      </motion.li>\n                    )}\n                  </AnimatePresence>\n                </motion.ul>\n              )}\n            </li>\n          );\n        })}\n      </ul>\n      <AnimatePresence initial={false}>\n        {(dirty > 0 || status !== \"idle\") && (\n          <motion.div initial={{ height: 0 }} animate={{ height: \"auto\" }} exit={{ height: 0 }} transition={reduce ? { duration: 0 } : soft} className=\"overflow-hidden\">\n            <div className=\"flex items-center justify-between gap-3 border-t border-border bg-surface px-5 py-3\">\n              <p aria-live=\"polite\" className=\"text-[12.5px] text-ink-muted\">\n                {status === \"saved\" ? (\n                  <span className=\"inline-flex items-center gap-1.5 font-medium text-success\"><Check aria-hidden className=\"size-3.5\" />Changes saved</span>\n                ) : (\n                  `${dirty} unsaved ${dirty === 1 ? \"change\" : \"changes\"}`\n                )}\n              </p>\n              {status !== \"saved\" && (\n                <div className=\"flex gap-2\">\n                  <button type=\"button\" disabled={status === \"saving\"} onClick={() => setValues(base)} className=\"h-8 rounded-full px-3 text-[12.5px] font-medium text-ink-muted outline-none hover:bg-surface-hover hover:text-ink focus-visible:shadow-[var(--focus-ring)]\">\n                    Discard\n                  </button>\n                  <motion.button type=\"button\" disabled={status === \"saving\"} whileTap={{ scale: 0.96 }} onClick={save} className=\"inline-flex h-8 items-center gap-1.5 rounded-full bg-volt px-3.5 text-[12.5px] font-semibold text-on-volt outline-none focus-visible:shadow-[var(--focus-ring)] disabled:opacity-80\">\n                    {status === \"saving\" && <LoaderCircle aria-hidden className=\"size-3.5 animate-spin\" />}\n                    Save changes\n                  </motion.button>\n                </div>\n              )}\n            </div>\n          </motion.div>\n        )}\n      </AnimatePresence>\n    </section>\n  );\n}\n\nfunction Row({ f, uid, checked, disabled, onToggle, onUpgrade }: { f: PlanFeature; uid: string; checked: boolean; disabled: boolean; onToggle: () => void; onUpgrade?: (plan: string) => void }) {\n  return (\n    <div className=\"flex items-center justify-between gap-4\">\n      <div className=\"min-w-0\">\n        <p id={`${uid}-${f.id}`} className={cn(\"flex items-center gap-2 font-medium\", f.parent ? \"text-[13px]\" : \"text-[13.5px]\", disabled ? \"text-ink-muted\" : \"text-ink\")}>\n          {f.label}\n          {f.requires && (\n            <span className=\"inline-flex items-center gap-1 rounded-full bg-ember-soft px-1.5 py-0.5 text-[10.5px] font-semibold text-ember-ink\">\n              <Lock aria-hidden className=\"size-2.5\" />\n              {f.requires}\n            </span>\n          )}\n        </p>\n        <p id={`${uid}-${f.id}-d`} className=\"text-[12px] text-ink-subtle\">\n          {f.description}\n          {f.requires && (\n            <>\n              {\" \"}\n              <button type=\"button\" onClick={() => onUpgrade?.(f.requires ?? \"\")} className=\"rounded-xs font-medium text-ink underline underline-offset-2 outline-none focus-visible:shadow-[var(--focus-ring)]\">\n                Upgrade to {f.requires}\n              </button>\n            </>\n          )}\n        </p>\n      </div>\n      <Switch checked={checked} disabled={disabled} onToggle={onToggle} labelledBy={`${uid}-${f.id}`} describedBy={`${uid}-${f.id}-d`} />\n    </div>\n  );\n}\n"},{"path":"registry/geturui/examples/switch-cards/privacy.tsx","type":"registry:component","target":"components/examples/switch-cards/privacy.tsx","content":"\"use client\";\n\nimport * as React from \"react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { ShieldAlert, ShieldCheck } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface PrivacySetting {\n  id: string;\n  label: string;\n  description: string;\n  enabled: boolean;\n  /** Ask before turning this off; the text explains the consequence. */\n  confirmOff?: string;\n}\n\nexport interface PrivacySwitchCardProps {\n  settings?: PrivacySetting[];\n  onChange?: (id: string, enabled: boolean) => void;\n  className?: string;\n}\n\nconst SETTINGS: PrivacySetting[] = [\n  { id: \"presence\", label: \"Show when I’m online\", description: \"Teammates see a green dot next to your name.\", enabled: true },\n  { id: \"receipts\", label: \"Read receipts\", description: \"Let people know when you’ve read their message.\", enabled: false },\n  { id: \"analytics\", label: \"Share usage data\", description: \"Anonymous diagnostics that help us fix bugs faster.\", enabled: true },\n  { id: \"alerts\", label: \"Sign-in alerts\", description: \"Email me when a new device signs in.\", enabled: true, confirmOff: \"You won’t hear about sign-ins from new devices, including ones that aren’t yours.\" },\n  { id: \"2fa\", label: \"Two-step verification\", description: \"Ask for a code from your authenticator app.\", enabled: true, confirmOff: \"Anyone with your password could sign in. We recommend keeping this on.\" },\n];\nconst spring = { type: \"spring\", stiffness: 520, damping: 32 } as const;\nconst soft = { type: \"spring\", stiffness: 320, damping: 30 } as const;\n\n/** Privacy settings where risky switches ask first: turning one off springs open an inline confirmation that holds focus until you choose. */\nexport function PrivacySwitchCard({ settings = SETTINGS, onChange, className }: PrivacySwitchCardProps) {\n  const reduce = useReducedMotion();\n  const uid = React.useId();\n  const [state, setState] = React.useState(() => Object.fromEntries(settings.map((s) => [s.id, s.enabled])));\n  const [pending, setPending] = React.useState<string | null>(null);\n  const switchRefs = React.useRef<Record<string, HTMLButtonElement | null>>({});\n  const keepRef = React.useRef<HTMLButtonElement>(null);\n  const protectedCount = settings.filter((s) => s.confirmOff && state[s.id]).length;\n  const totalProtected = settings.filter((s) => s.confirmOff).length;\n\n  React.useEffect(() => {\n    if (pending) requestAnimationFrame(() => keepRef.current?.focus());\n  }, [pending]);\n\n  function set(id: string, value: boolean) {\n    setState((s) => ({ ...s, [id]: value }));\n    onChange?.(id, value);\n  }\n\n  function toggle(s: PrivacySetting) {\n    if (state[s.id] && s.confirmOff) return setPending(pending === s.id ? null : s.id);\n    setPending(null);\n    set(s.id, !state[s.id]);\n  }\n\n  function resolve(turnOff: boolean) {\n    if (!pending) return;\n    if (turnOff) set(pending, false);\n    switchRefs.current[pending]?.focus();\n    setPending(null);\n  }\n\n  const secure = protectedCount === totalProtected;\n\n  return (\n    <section aria-labelledby={`${uid}-t`} className={cn(\"w-full max-w-[460px] overflow-hidden rounded-lg border border-border bg-surface-raised shadow-sm\", className)}>\n      <header className=\"flex items-center justify-between gap-3 border-b border-border px-5 py-4\">\n        <div>\n          <h2 id={`${uid}-t`} className=\"font-display text-[16px] font-semibold text-ink\">Privacy and security</h2>\n          <p className=\"mt-0.5 text-[12.5px] text-ink-muted\">Control what others see and how you sign in.</p>\n        </div>\n        <AnimatePresence mode=\"wait\" initial={false}>\n          <motion.span\n            key={String(secure)}\n            role=\"status\"\n            initial={{ opacity: 0, scale: reduce ? 1 : 0.85 }}\n            animate={{ opacity: 1, scale: 1 }}\n            exit={{ opacity: 0, scale: reduce ? 1 : 0.85 }}\n            transition={spring}\n            className={cn(\"inline-flex shrink-0 items-center gap-1 rounded-full px-2 py-1 text-[11.5px] font-medium ring-1\", secure ? \"text-success ring-success/30\" : \"text-warning ring-warning/40\")}\n          >\n            {secure ? <ShieldCheck aria-hidden className=\"size-3.5\" /> : <ShieldAlert aria-hidden className=\"size-3.5\" />}\n            {secure ? \"Protected\" : \"At risk\"}\n          </motion.span>\n        </AnimatePresence>\n      </header>\n      <ul className=\"divide-y divide-border\">\n        {settings.map((s) => {\n          const on = !!state[s.id];\n          const asking = pending === s.id;\n          return (\n            <li key={s.id} className=\"px-5 py-3\">\n              <div className=\"flex items-center justify-between gap-4\">\n                <div className=\"min-w-0\">\n                  <p id={`${uid}-${s.id}`} className=\"text-[13.5px] font-medium text-ink\">{s.label}</p>\n                  <p id={`${uid}-${s.id}-d`} className=\"text-[12px] text-ink-subtle\">{s.description}</p>\n                </div>\n                <motion.button\n                  ref={(el) => {\n                    switchRefs.current[s.id] = el;\n                  }}\n                  type=\"button\"\n                  role=\"switch\"\n                  aria-checked={on}\n                  aria-labelledby={`${uid}-${s.id}`}\n                  aria-describedby={`${uid}-${s.id}-d`}\n                  aria-expanded={s.confirmOff ? asking : undefined}\n                  aria-controls={s.confirmOff ? `${uid}-${s.id}-c` : undefined}\n                  onClick={() => toggle(s)}\n                  whileTap=\"press\"\n                  className={cn(\n                    \"relative inline-flex h-6 w-10 shrink-0 items-center rounded-full border p-[2px] outline-none transition-colors duration-200 focus-visible:shadow-[var(--focus-ring)]\",\n                    on ? \"border-transparent bg-volt\" : \"border-border-strong bg-surface-sunken\",\n                    asking && \"ring-2 ring-danger/50\",\n                  )}\n                >\n                  <motion.span animate={{ x: asking ? 8 : on ? 16 : 0 }} variants={{ press: { scaleX: 1.25 } }} transition={spring} style={{ originX: on ? 1 : 0 }} className={cn(\"block size-[18px] rounded-full shadow-sm\", on ? \"bg-on-volt\" : \"bg-ink-subtle\")} />\n                </motion.button>\n              </div>\n              <AnimatePresence initial={false}>\n                {asking && (\n                  <motion.div\n                    id={`${uid}-${s.id}-c`}\n                    role=\"alertdialog\"\n                    aria-labelledby={`${uid}-${s.id}-q`}\n                    aria-describedby={`${uid}-${s.id}-w`}\n                    initial={{ height: 0, opacity: 0 }}\n                    animate={{ height: \"auto\", opacity: 1 }}\n                    exit={{ height: 0, opacity: 0 }}\n                    transition={reduce ? { duration: 0 } : soft}\n                    onKeyDown={(e) => {\n                      if (e.key !== \"Escape\") return;\n                      e.stopPropagation();\n                      resolve(false);\n                    }}\n                    className=\"overflow-hidden\"\n                  >\n                    <div className=\"mt-3 rounded-md border border-danger/30 bg-surface-sunken p-3\">\n                      <p id={`${uid}-${s.id}-q`} className=\"flex items-center gap-1.5 text-[13px] font-medium text-danger\">\n                        <ShieldAlert aria-hidden className=\"size-4\" />\n                        Turn off {s.label.toLowerCase()}?\n                      </p>\n                      <p id={`${uid}-${s.id}-w`} className=\"mt-1 text-[12.5px] text-ink-muted\">{s.confirmOff}</p>\n                      <div className=\"mt-3 flex justify-end gap-2\">\n                        <button ref={keepRef} type=\"button\" onClick={() => resolve(false)} className=\"h-8 rounded-full bg-ink px-3.5 text-[12.5px] font-semibold text-bg outline-none focus-visible:shadow-[var(--focus-ring)]\">\n                          Keep it on\n                        </button>\n                        <motion.button type=\"button\" whileTap={{ scale: 0.96 }} onClick={() => resolve(true)} className=\"h-8 rounded-full border border-danger/50 px-3.5 text-[12.5px] font-medium text-danger outline-none transition-colors hover:bg-danger/10 focus-visible:shadow-[var(--focus-ring)]\">\n                          Turn off\n                        </motion.button>\n                      </div>\n                    </div>\n                  </motion.div>\n                )}\n              </AnimatePresence>\n            </li>\n          );\n        })}\n      </ul>\n    </section>\n  );\n}\n"},{"path":"LICENSE-geturui.md","type":"registry:file","target":"LICENSE-geturui.md","content":"MIT License\n\nCopyright (c) 2026 geturui.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"}],"categories":["examples","switch","toggle","settings","preferences","notifications","privacy","features","form"],"meta":{"tier":"free","license":"MIT","docs":"https://geturui.com/docs/switch-cards"}}