{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "flashcard-study-deck",
  "title": "Flashcard Study Deck",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-react/blocks/flashcard-study-deck/FlashcardStudyDeck.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport {\n  Bookmark,\n  Check,\n  ChevronLeft,\n  ChevronRight,\n  Copy,\n  Flame,\n  GraduationCap,\n  Lightbulb,\n  RotateCw,\n  Shuffle,\n} from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card } from '@/components/ui/card'\nimport { Progress } from '@/components/ui/progress'\nimport { Separator } from '@/components/ui/separator'\n\nexport interface Flashcard {\n  id: string\n  category: string\n  difficulty?: 'Level A' | 'Level AA' | 'Level AAA' | 'Design System' | 'Engineering'\n  question: string\n  hint: string\n  answer: string\n  codeSnippet?: string\n  codeLanguage?: string\n  source?: string\n}\n\nexport type RatingInterval = 'again' | 'hard' | 'good' | 'easy'\n\nconst DEFAULT_DECK_TITLE = 'Design Engineering & Web Accessibility (WCAG 2.2)'\n\nconst DEFAULT_CARDS: Flashcard[] = [\n  {\n    id: 'wcag-1-4-3',\n    category: 'WCAG Criterion 1.4.3',\n    difficulty: 'Level AA',\n    question: 'What is the minimum contrast ratio required for regular body text under WCAG AA standards?',\n    hint: 'Consider standard text (< 18pt or < 14pt bold) versus large display headings (≥ 18pt or ≥ 14pt bold).',\n    answer: '4.5:1 for normal text (< 18pt or < 14pt bold) and 3:1 for large text (≥ 18pt or ≥ 14pt bold).',\n    codeSnippet: 'color: oklch(0.20 0 0); /* on oklch(0.98 0 0) -> 14.2:1 passes AA/AAA */',\n    codeLanguage: 'css',\n    source: 'WCAG 2.2 § 1.4.3 Contrast (Minimum)',\n  },\n  {\n    id: 'wcag-2-5-8',\n    category: 'WCAG Criterion 2.5.8',\n    difficulty: 'Level AA',\n    question: 'What is the minimum interactive touch target size required in WCAG 2.2 Level AA?',\n    hint: 'Introduced in WCAG 2.2 to prevent accidental taps on mobile touchscreens without requiring 44×44px AAA.',\n    answer:\n      'At least 24×24 CSS pixels, or have sufficient spacing (undersized targets surrounded by at least 24px diameter clear space).',\n    codeSnippet: '<button class=\"min-h-6 min-w-6 p-2 touch-manipulation\">',\n    codeLanguage: 'html',\n    source: 'WCAG 2.2 § 2.5.8 Target Size (Minimum)',\n  },\n  {\n    id: 'wcag-2-4-7',\n    category: 'WCAG Criterion 2.4.7',\n    difficulty: 'Level AA',\n    question: 'What visual contrast requirement applies to keyboard focus indicators under WCAG 2.2?',\n    hint: 'Focus indicators must be distinguishable against both the focused element and adjacent background.',\n    answer:\n      'The focus indicator must have a contrast ratio of at least 3:1 against the unfocused state and surrounding background, with minimum 2px perimeter thickness.',\n    codeSnippet: 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',\n    codeLanguage: 'css',\n    source: 'WCAG 2.2 § 2.4.7 & 2.4.11 Focus Appearance',\n  },\n  {\n    id: 'wcag-4-1-2',\n    category: 'WCAG Criterion 4.1.2',\n    difficulty: 'Level A',\n    question: 'How should custom collapsible or expandable widgets communicate state to screen readers?',\n    hint: 'Use native semantic elements or explicit ARIA state attributes rather than visual classes alone.',\n    answer:\n      'Declare explicit ARIA roles (e.g. role=\"button\"), aria-expanded=\"true|false\", and aria-controls linking to the target element ID.',\n    codeSnippet: '<button aria-expanded=\"true\" aria-controls=\"card-back-content\">',\n    codeLanguage: 'html',\n    source: 'WAI-ARIA 1.2 Authoring Practices',\n  },\n  {\n    id: 'wcag-1-4-11',\n    category: 'WCAG Criterion 1.4.11',\n    difficulty: 'Level AA',\n    question: 'What is the contrast threshold for UI components, boundaries, and graphical objects?',\n    hint: 'Applies to active button borders, input outlines, checkboxes, and state badges.',\n    answer:\n      'A contrast ratio of at least 3:1 against adjacent colors for user interface components and graphical objects essential for comprehension.',\n    codeSnippet: 'border: 1px solid var(--border); /* >= 3:1 against surface */',\n    codeLanguage: 'css',\n    source: 'WCAG 2.2 § 1.4.11 Non-text Contrast',\n  },\n  {\n    id: 'oklch-tokens',\n    category: 'Design Systems & Color Science',\n    difficulty: 'Design System',\n    question: 'Why is the OKLCH color space preferred over HSL for dark mode and accessibility contrast derivation?',\n    hint: 'Perceptual lightness is uniform across all hues in OKLCH, unlike HSL where yellow looks brighter than blue at L=50%.',\n    answer:\n      'OKLCH is perceptually uniform. Equal changes in lightness (L) yield identical perceived contrast regardless of hue or chroma, preventing washed-out dark mode shifts.',\n    codeSnippet: '--color-primary: oklch(0.62 0.22 264.4);',\n    codeLanguage: 'css',\n    source: 'CSS Color Module Level 4',\n  },\n]\n\nexport interface FlashcardStudyDeckProps extends React.HTMLAttributes<HTMLDivElement> {\n  initialTitle?: string\n  initialCards?: Flashcard[]\n}\n\nexport function FlashcardStudyDeck({\n  initialTitle = DEFAULT_DECK_TITLE,\n  initialCards = DEFAULT_CARDS,\n  className,\n  ...props\n}: FlashcardStudyDeckProps) {\n  const [deck, setDeck] = React.useState<Flashcard[]>(initialCards)\n  const [currentIndex, setCurrentIndex] = React.useState(0)\n  const [isFlipped, setIsFlipped] = React.useState(false)\n  const [showHint, setShowHint] = React.useState(false)\n  const [copiedSnippet, setCopiedSnippet] = React.useState(false)\n  const [bookmarkedCards, setBookmarkedCards] = React.useState<Set<string>>(new Set())\n  const [cardScores, setCardScores] = React.useState<Record<string, number>>({})\n  const [lastRatingFeedback, setLastRatingFeedback] = React.useState<string | null>(null)\n\n  const currentCard = deck[currentIndex] ?? deck[0]\n  const totalCards = deck.length\n  const currentNumber = currentIndex + 1\n  const progressPercentage = Math.round((currentNumber / totalCards) * 100)\n\n  const masteryPercentage = React.useMemo(() => {\n    const ratedKeys = Object.keys(cardScores)\n    if (ratedKeys.length === 0) return 0\n    const totalScore = ratedKeys.reduce((sum, key) => sum + (cardScores[key] ?? 0), 0)\n    return Math.min(100, Math.round(totalScore / totalCards))\n  }, [cardScores, totalCards])\n\n  const isBookmarked = currentCard ? bookmarkedCards.has(currentCard.id) : false\n\n  const toggleFlip = React.useCallback(() => {\n    setIsFlipped((prev) => !prev)\n  }, [])\n\n  const toggleHint = React.useCallback(() => {\n    setShowHint((prev) => !prev)\n  }, [])\n\n  const toggleBookmark = React.useCallback(() => {\n    if (!currentCard) return\n    setBookmarkedCards((prev) => {\n      const next = new Set(prev)\n      if (next.has(currentCard.id)) {\n        next.delete(currentCard.id)\n      } else {\n        next.add(currentCard.id)\n      }\n      return next\n    })\n  }, [currentCard])\n\n  const handlePrev = React.useCallback(() => {\n    if (currentIndex > 0) {\n      setCurrentIndex((prev) => prev - 1)\n      setIsFlipped(false)\n      setShowHint(false)\n      setLastRatingFeedback(null)\n    }\n  }, [currentIndex])\n\n  const handleNext = React.useCallback(() => {\n    if (currentIndex < totalCards - 1) {\n      setCurrentIndex((prev) => prev + 1)\n      setIsFlipped(false)\n      setShowHint(false)\n      setLastRatingFeedback(null)\n    }\n  }, [currentIndex, totalCards])\n\n  const handleShuffle = React.useCallback(() => {\n    const shuffled = [...deck]\n    for (let i = shuffled.length - 1; i > 0; i--) {\n      const j = Math.floor(Math.random() * (i + 1))\n      ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]\n    }\n    setDeck(shuffled)\n    setCurrentIndex(0)\n    setIsFlipped(false)\n    setShowHint(false)\n    setLastRatingFeedback('Deck shuffled')\n    setTimeout(() => {\n      setLastRatingFeedback((prev) => (prev === 'Deck shuffled' ? null : prev))\n    }, 2000)\n  }, [deck])\n\n  const handleRate = React.useCallback(\n    (interval: RatingInterval) => {\n      if (!currentCard) return\n\n      const scoreMap: Record<RatingInterval, { score: number; label: string }> = {\n        again: { score: 25, label: 'Again (< 1 min)' },\n        hard: { score: 50, label: 'Hard (12 hours)' },\n        good: { score: 80, label: 'Good (2 days)' },\n        easy: { score: 100, label: 'Easy (4 days)' },\n      }\n\n      const { score, label } = scoreMap[interval]\n      setCardScores((prev) => ({ ...prev, [currentCard.id]: score }))\n      setLastRatingFeedback(`Rated: ${label}`)\n\n      setTimeout(() => {\n        if (currentIndex < totalCards - 1) {\n          handleNext()\n        } else {\n          setIsFlipped(false)\n          setShowHint(false)\n        }\n      }, 350)\n    },\n    [currentCard, currentIndex, totalCards, handleNext],\n  )\n\n  const copyCodeSnippet = React.useCallback(async () => {\n    if (!currentCard?.codeSnippet) return\n    try {\n      await navigator.clipboard.writeText(currentCard.codeSnippet)\n      setCopiedSnippet(true)\n      setTimeout(() => {\n        setCopiedSnippet(false)\n      }, 2000)\n    } catch {\n      // Clipboard fallback\n    }\n  }, [currentCard])\n\n  React.useEffect(() => {\n    function handleKeyDown(event: KeyboardEvent) {\n      const target = event.target as HTMLElement | null\n      if (target && ['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName)) return\n\n      if (event.key === 'ArrowLeft') {\n        event.preventDefault()\n        handlePrev()\n      } else if (event.key === 'ArrowRight') {\n        event.preventDefault()\n        handleNext()\n      } else if (event.key === ' ' || event.code === 'Space') {\n        event.preventDefault()\n        toggleFlip()\n      } else if (event.key.toLowerCase() === 'h') {\n        event.preventDefault()\n        toggleHint()\n      } else if (isFlipped) {\n        if (event.key === '1') {\n          event.preventDefault()\n          handleRate('again')\n        } else if (event.key === '2') {\n          event.preventDefault()\n          handleRate('hard')\n        } else if (event.key === '3') {\n          event.preventDefault()\n          handleRate('good')\n        } else if (event.key === '4') {\n          event.preventDefault()\n          handleRate('easy')\n        }\n      }\n    }\n\n    window.addEventListener('keydown', handleKeyDown)\n    return () => {\n      window.removeEventListener('keydown', handleKeyDown)\n    }\n  }, [handlePrev, handleNext, toggleFlip, toggleHint, handleRate, isFlipped])\n\n  return (\n    <div\n      data-slot=\"flashcard-study-deck\"\n      className={cn('mx-auto w-full max-w-4xl space-y-6 p-4 md:p-6 lg:p-8', className)}\n      {...props}\n    >\n      {/* Header Section */}\n      <header className=\"space-y-4\">\n        <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n          <div className=\"space-y-1\">\n            <div className=\"flex items-center gap-2\">\n              <Badge variant=\"secondary\" className=\"gap-1 px-2.5 py-0.5 font-medium\">\n                <GraduationCap className=\"text-primary size-3.5\" />\n                <span>Study Deck</span>\n              </Badge>\n              <Badge variant=\"outline\" className=\"text-muted-foreground gap-1 text-xs\">\n                <Flame className=\"text-warning size-3\" />\n                <span>5-day streak</span>\n              </Badge>\n            </div>\n            <h2 className=\"text-foreground text-xl font-bold tracking-tight md:text-2xl\">{initialTitle}</h2>\n          </div>\n\n          <div className=\"flex items-center gap-2\">\n            <Badge variant=\"outline\" className=\"px-3 py-1 font-mono text-xs font-medium\">\n              Card {currentNumber} of {totalCards} · {masteryPercentage}% Mastery\n            </Badge>\n          </div>\n        </div>\n\n        {/* Progress Track */}\n        <div className=\"space-y-1.5\">\n          <Progress value={progressPercentage} className=\"h-2\" />\n          <div className=\"text-muted-foreground flex items-center justify-between text-xs\">\n            <span>\n              Progress: {currentNumber} / {totalCards} cards\n            </span>\n            <span>\n              {Object.keys(cardScores).length} of {totalCards} reviewed\n            </span>\n          </div>\n        </div>\n      </header>\n\n      {/* 3D Interactive Flashcard Container */}\n      <div className=\"mx-auto w-full max-w-2xl [perspective:1200px]\">\n        <div\n          className={cn(\n            'relative min-h-[440px] w-full rounded-xl transition-transform duration-500 [transform-style:preserve-3d] md:min-h-[460px]',\n            isFlipped && '[transform:rotateY(180deg)]',\n          )}\n        >\n          {/* FRONT FACE: Question State */}\n          <Card\n            className={cn(\n              'border-border bg-card absolute inset-0 flex h-full w-full flex-col justify-between rounded-xl border p-6 shadow-xs [backface-visibility:hidden] md:p-8',\n              isFlipped ? 'pointer-events-none opacity-0' : 'pointer-events-auto opacity-100',\n            )}\n          >\n            {/* Front Header */}\n            <div className=\"flex items-center justify-between gap-3\">\n              <div className=\"flex flex-wrap items-center gap-2\">\n                <Badge variant=\"secondary\" className=\"text-xs font-medium\">\n                  {currentCard.category}\n                </Badge>\n                {currentCard.difficulty && (\n                  <Badge variant=\"outline\" className=\"text-xs\">\n                    {currentCard.difficulty}\n                  </Badge>\n                )}\n              </div>\n\n              <div className=\"flex items-center gap-1.5\">\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon-sm\"\n                  className={cn('text-muted-foreground hover:text-foreground', isBookmarked && 'text-warning')}\n                  aria-label=\"Bookmark card\"\n                  onClick={(e) => {\n                    e.stopPropagation()\n                    toggleBookmark()\n                  }}\n                >\n                  <Bookmark className={cn('size-4', isBookmarked && 'fill-warning text-warning')} />\n                </Button>\n              </div>\n            </div>\n\n            {/* Question Content */}\n            <div className=\"my-auto space-y-4 py-4\">\n              <div className=\"space-y-1.5\">\n                <span className=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">\n                  Question Prompt\n                </span>\n                <p className=\"text-foreground text-lg leading-snug font-semibold tracking-tight md:text-xl\">\n                  {currentCard.question}\n                </p>\n              </div>\n\n              {/* Hint Drawer / Toggle */}\n              {currentCard.hint && (\n                <div className=\"pt-2\">\n                  <Button\n                    variant=\"ghost\"\n                    size=\"xs\"\n                    className=\"text-muted-foreground hover:text-foreground gap-1.5 text-xs\"\n                    onClick={(e) => {\n                      e.stopPropagation()\n                      toggleHint()\n                    }}\n                  >\n                    <Lightbulb className=\"text-warning size-3.5\" />\n                    <span>{showHint ? 'Hide Hint' : 'Show Hint'}</span>\n                    <span className=\"text-muted-foreground/60 font-mono\">[H]</span>\n                  </Button>\n\n                  {showHint && (\n                    <div className=\"text-muted-foreground border-warning/20 bg-warning/5 mt-2.5 flex items-start gap-2 rounded-lg border p-3 text-xs leading-relaxed\">\n                      <Lightbulb className=\"text-warning mt-0.5 size-4 shrink-0\" />\n                      <div>\n                        <strong className=\"text-foreground font-semibold\">Hint: </strong>\n                        <span>{currentCard.hint}</span>\n                      </div>\n                    </div>\n                  )}\n                </div>\n              )}\n            </div>\n\n            {/* Front Footer / Flip Trigger */}\n            <div className=\"space-y-3 pt-2\">\n              <Separator />\n              <div className=\"text-muted-foreground flex flex-col items-center justify-between gap-3 text-xs sm:flex-row\">\n                <span className=\"hidden sm:inline\">\n                  Press <kbd className=\"bg-muted border-border rounded border px-1 py-0.5 font-mono\">Space</kbd> or\n                  click button\n                </span>\n                <Button\n                  variant=\"default\"\n                  size=\"sm\"\n                  className=\"w-full cursor-pointer gap-2 font-medium sm:w-auto\"\n                  onClick={(e) => {\n                    e.stopPropagation()\n                    toggleFlip()\n                  }}\n                >\n                  <RotateCw className=\"size-3.5\" />\n                  <span>Flip Card to Reveal Answer</span>\n                </Button>\n              </div>\n            </div>\n          </Card>\n\n          {/* BACK FACE: Answer State */}\n          <Card\n            className={cn(\n              'border-border bg-card absolute inset-0 flex h-full w-full [transform:rotateY(180deg)] flex-col justify-between rounded-xl border p-6 shadow-xs [backface-visibility:hidden] md:p-8',\n              isFlipped ? 'pointer-events-auto opacity-100' : 'pointer-events-none opacity-0',\n            )}\n          >\n            {/* Back Header */}\n            <div className=\"flex items-center justify-between gap-3\">\n              <div className=\"flex flex-wrap items-center gap-2\">\n                <Badge variant=\"secondary\" className=\"text-xs font-medium\">\n                  {currentCard.category}\n                </Badge>\n                <Badge variant=\"default\" className=\"border-success/30 bg-success/15 text-success text-xs\">\n                  Answer Revealed\n                </Badge>\n              </div>\n\n              <Button\n                variant=\"ghost\"\n                size=\"xs\"\n                className=\"text-muted-foreground hover:text-foreground gap-1 text-xs\"\n                onClick={(e) => {\n                  e.stopPropagation()\n                  toggleFlip()\n                }}\n              >\n                <RotateCw className=\"size-3.5\" />\n                <span>Flip Back</span>\n              </Button>\n            </div>\n\n            {/* Answer Content */}\n            <div className=\"my-auto space-y-4 overflow-y-auto py-3\">\n              <div className=\"space-y-1.5\">\n                <span className=\"text-primary text-xs font-semibold tracking-wider uppercase\">Answer Explanation</span>\n                <p className=\"text-foreground text-base leading-relaxed font-medium md:text-lg\">{currentCard.answer}</p>\n              </div>\n\n              {/* Code / Token Example Block */}\n              {currentCard.codeSnippet && (\n                <div className=\"border-border bg-muted/60 dark:bg-muted/30 space-y-2 rounded-lg border p-3.5\">\n                  <div className=\"text-muted-foreground flex items-center justify-between text-xs\">\n                    <span className=\"font-mono font-medium\">Code / Token Example</span>\n                    <Button\n                      variant=\"ghost\"\n                      size=\"xs\"\n                      className=\"h-6 gap-1 px-2 text-xs\"\n                      onClick={(e) => {\n                        e.stopPropagation()\n                        copyCodeSnippet()\n                      }}\n                    >\n                      {copiedSnippet ? <Check className=\"text-success size-3\" /> : <Copy className=\"size-3\" />}\n                      <span>{copiedSnippet ? 'Copied' : 'Copy'}</span>\n                    </Button>\n                  </div>\n                  <pre className=\"text-foreground bg-background/80 border-border/60 overflow-x-auto rounded border p-2 font-mono text-xs md:text-sm\">\n                    <code>{currentCard.codeSnippet}</code>\n                  </pre>\n                  {currentCard.source && (\n                    <p className=\"text-muted-foreground text-xs italic\">Source: {currentCard.source}</p>\n                  )}\n                </div>\n              )}\n            </div>\n\n            {/* Spaced-Repetition Feedback Buttons */}\n            <div className=\"space-y-2.5 pt-2\">\n              <div className=\"text-muted-foreground flex items-center justify-between text-xs\">\n                <span className=\"font-medium\">Rate recall difficulty (SM-2 Spaced Repetition):</span>\n                {lastRatingFeedback && <span className=\"text-primary text-xs font-medium\">{lastRatingFeedback}</span>}\n              </div>\n\n              <div className=\"grid grid-cols-2 gap-2 sm:grid-cols-4\">\n                <Button\n                  variant=\"outline\"\n                  size=\"sm\"\n                  className=\"border-destructive/30 bg-destructive/10 text-destructive hover:bg-destructive/20 text-destructive flex h-auto flex-col py-2 text-xs active:scale-95\"\n                  onClick={(e) => {\n                    e.stopPropagation()\n                    handleRate('again')\n                  }}\n                >\n                  <span className=\"font-semibold\">Again</span>\n                  <span className=\"text-xs opacity-80\">&lt; 1 min [1]</span>\n                </Button>\n\n                <Button\n                  variant=\"outline\"\n                  size=\"sm\"\n                  className=\"border-warning/30 bg-warning/10 text-warning hover:bg-warning/20 text-warning flex h-auto flex-col py-2 text-xs active:scale-95\"\n                  onClick={(e) => {\n                    e.stopPropagation()\n                    handleRate('hard')\n                  }}\n                >\n                  <span className=\"font-semibold\">Hard</span>\n                  <span className=\"text-xs opacity-80\">12 hours [2]</span>\n                </Button>\n\n                <Button\n                  variant=\"outline\"\n                  size=\"sm\"\n                  className=\"border-info/30 bg-info/10 text-info hover:bg-info/20 text-info flex h-auto flex-col py-2 text-xs active:scale-95\"\n                  onClick={(e) => {\n                    e.stopPropagation()\n                    handleRate('good')\n                  }}\n                >\n                  <span className=\"font-semibold\">Good</span>\n                  <span className=\"text-xs opacity-80\">2 days [3]</span>\n                </Button>\n\n                <Button\n                  variant=\"outline\"\n                  size=\"sm\"\n                  className=\"border-success/30 bg-success/10 text-success hover:bg-success/20 text-success flex h-auto flex-col py-2 text-xs active:scale-95\"\n                  onClick={(e) => {\n                    e.stopPropagation()\n                    handleRate('easy')\n                  }}\n                >\n                  <span className=\"font-semibold\">Easy</span>\n                  <span className=\"text-xs opacity-80\">4 days [4]</span>\n                </Button>\n              </div>\n            </div>\n          </Card>\n        </div>\n      </div>\n\n      {/* Deck Navigation & Utility Controls */}\n      <div className=\"mx-auto flex max-w-2xl flex-col items-center justify-between gap-3 pt-2 sm:flex-row\">\n        <div className=\"flex w-full items-center justify-between gap-2 sm:w-auto sm:justify-start\">\n          <Button variant=\"outline\" size=\"sm\" disabled={currentIndex === 0} className=\"gap-1.5\" onClick={handlePrev}>\n            <ChevronLeft className=\"size-4\" />\n            <span>Previous</span>\n          </Button>\n\n          <Button variant=\"outline\" size=\"sm\" className=\"gap-1.5\" onClick={handleShuffle}>\n            <Shuffle className=\"size-3.5\" />\n            <span>Shuffle</span>\n          </Button>\n        </div>\n\n        <div className=\"flex w-full items-center justify-between gap-2 sm:w-auto sm:justify-end\">\n          <Button variant=\"secondary\" size=\"sm\" className=\"gap-1.5\" onClick={toggleFlip}>\n            <RotateCw className=\"size-3.5\" />\n            <span>{isFlipped ? 'Show Question' : 'Flip Card'}</span>\n          </Button>\n\n          <Button\n            variant=\"default\"\n            size=\"sm\"\n            disabled={currentIndex === totalCards - 1}\n            className=\"gap-1.5\"\n            onClick={handleNext}\n          >\n            <span>Next</span>\n            <ChevronRight className=\"size-4\" />\n          </Button>\n        </div>\n      </div>\n\n      {/* Keyboard Shortcuts Legend Footer */}\n      <div className=\"mx-auto max-w-2xl pt-2 text-center\">\n        <p className=\"text-muted-foreground font-mono text-xs\">\n          Shortcuts: <span className=\"text-foreground font-semibold\">← / →</span> Navigate ·{' '}\n          <span className=\"text-foreground font-semibold\">Space</span> Flip ·{' '}\n          <span className=\"text-foreground font-semibold\">H</span> Hint ·{' '}\n          <span className=\"text-foreground font-semibold\">1-4</span> Rate Interval\n        </p>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:block",
      "target": "~/components/blocks/FlashcardStudyDeck.tsx"
    }
  ],
  "dependencies": [
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/badge.json",
    "https://uipkge.dev/r/react/button.json",
    "https://uipkge.dev/r/react/card.json",
    "https://uipkge.dev/r/react/progress.json",
    "https://uipkge.dev/r/react/separator.json"
  ],
  "description": "Anki and Quizlet style spaced-repetition flashcard study deck with smooth 3D flip card interaction, WCAG 2.2 accessibility curriculum, hint drawer, mastery percentage tracking, keyboard shortcuts, and SM-2 interval rating buttons.",
  "categories": [
    "education",
    "app"
  ]
}