{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "quiz-assessment-runner",
  "title": "Quiz Assessment Runner",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/quiz-assessment-runner/quiz-assessment-runner.component.ts",
      "content": "// Interactive multiple-choice exam & quiz assessment runner: 2-column test canvas with\n// real-time countdown timer, question navigator grid with status indicators, code context\n// snippets, single-choice selection cards, submit confirmation modal, and a comprehensive\n// score breakdown with review explanations. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, DestroyRef, Input, computed, inject, signal } from '@angular/core'\nimport {\n  AlertCircle,\n  Check,\n  CheckCircle2,\n  ChevronLeft,\n  ChevronRight,\n  Clock,\n  FileCode,\n  Flag,\n  Lightbulb,\n  LogOut,\n  LucideAngularModule,\n  RotateCcw,\n  XCircle,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardFooterComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport {\n  UiDialogComponent,\n  UiDialogContentComponent,\n  UiDialogDescriptionComponent,\n  UiDialogFooterComponent,\n  UiDialogHeaderComponent,\n  UiDialogTitleComponent,\n} from '@/ui/dialog/dialog.component'\nimport { UiProgressComponent } from '@/ui/progress/progress.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\n\nexport interface QuizOption {\n  id: string\n  text: string\n}\n\nexport interface QuizQuestion {\n  id: number\n  category: string\n  prompt: string\n  codeSnippet?: string\n  codeLanguage?: string\n  options: QuizOption[]\n  correctOptionId: string\n  explanation: string\n}\n\nexport const DEFAULT_QUESTIONS: QuizQuestion[] = [\n  {\n    id: 1,\n    category: 'Architecture',\n    prompt:\n      'What is the primary architectural difference between a UI Primitive and a UI Block in the UIPKGE component registry?',\n    codeSnippet: `// Primitive (registry:ui) vs Block (registry:block)\\nconst Primitive = <Button variant=\"outline\">Save Changes</Button>\\nconst Block = <QuizAssessmentRunner initialQuestion={1} />`,\n    codeLanguage: 'TypeScript',\n    options: [\n      { id: 'A', text: 'Primitives are pre-compiled npm packages, while Blocks are static JSON schema files.' },\n      {\n        id: 'B',\n        text: 'Primitives encapsulate low-level mechanics and styling tokens, while Blocks compose primitives into transparent, copy-pastable domain layouts.',\n      },\n      { id: 'C', text: 'Primitives can only be rendered server-side, while Blocks require client-side WebSockets.' },\n      {\n        id: 'D',\n        text: 'Primitives require Zod runtime validation schemas, while Blocks only support TypeScript interfaces.',\n      },\n    ],\n    correctOptionId: 'B',\n    explanation:\n      'In the UIPKGE architecture, primitives (registry:ui) encapsulate accessibility, focus states, and styling tokens, whereas blocks (registry:block) compose those primitives raw into unabstracted layouts where developers own and customize the code.',\n  },\n  {\n    id: 2,\n    category: 'Vue 3.5 SSR',\n    prompt:\n      'Why must CVA (Class Variance Authority) variant functions in Vue registry components be placed in a dedicated `<name>.variants.ts` file rather than `index.ts`?',\n    codeSnippet: `// button.variants.ts\\nexport const buttonVariants = cva(...)\\n\\n// Button.vue\\nimport { buttonVariants } from './button.variants'\\n\\n// index.ts\\nexport { default as Button } from './Button.vue'\\nexport { buttonVariants } from './button.variants'`,\n    codeLanguage: 'TypeScript',\n    options: [\n      { id: 'A', text: 'To comply with ECMAScript dynamic tree-shaking requirements for bundlers.' },\n      {\n        id: 'B',\n        text: 'To prevent Vue SSR circular module dependency deadlocks where $setup.xxxVariants is undefined at runtime.',\n      },\n      { id: 'C', text: 'Because TypeScript forbids exporting types and constants from the same index file.' },\n      { id: 'D', text: 'To enable hot-module replacement specifically for PostCSS variable transformations.' },\n    ],\n    correctOptionId: 'B',\n    explanation:\n      'Circular imports between Component.vue and index.ts cause runtime evaluation failures during SSR/SSG pre-rendering, resulting in \"$setup.xxxVariants is not a function\" errors.',\n  },\n  {\n    id: 3,\n    category: 'Design Tokens',\n    prompt: \"Which Tailwind CSS v4 directive establishes the OKLCH design token mapping in UIPKGE's theme layer?\",\n    codeSnippet: `@theme inline {\\n  --color-background: var(--background);\\n  --color-foreground: var(--foreground);\\n  --color-primary: var(--primary);\\n  --color-card: var(--card);\\n}`,\n    codeLanguage: 'CSS',\n    options: [\n      { id: 'A', text: '@apply tokens.oklch;' },\n      { id: 'B', text: '@theme inline' },\n      { id: 'C', text: \"@config 'tailwind.theme.ts';\" },\n      { id: 'D', text: '@utility theme-variables;' },\n    ],\n    correctOptionId: 'B',\n    explanation:\n      'Tailwind CSS v4 uses `@theme inline` blocks to bind OKLCH CSS variables directly to Tailwind utility classes without needing a separate tailwind.config.js file.',\n  },\n  {\n    id: 4,\n    category: 'Reka UI / Vue',\n    prompt:\n      'When authoring polymorphic components in Vue 3.5 with Reka UI, which pattern cleanly strips the class prop for `cn()` forwarding?',\n    codeSnippet: `const props = defineProps<ButtonProps>()\\nconst delegatedProps = reactiveOmit(props, 'class')\\nconst forwarded = useForwardProps(delegatedProps)`,\n    codeLanguage: 'TypeScript',\n    options: [\n      { id: 'A', text: \"Directly passing v-bind='props' to the template without stripping the class prop.\" },\n      {\n        id: 'B',\n        text: \"Using reactiveOmit(props, 'class') combined with useForwardProps and explicit :class='cn(...)'. \",\n      },\n      { id: 'C', text: 'Using Object.assign({}, props) inside an onMounted lifecycle hook.' },\n      { id: 'D', text: \"Using v-bind='$attrs' with inheritAttrs: true set on the SFC.\" },\n    ],\n    correctOptionId: 'B',\n    explanation:\n      'reactiveOmit from @vueuse/core strips the class attribute while retaining reactivity, allowing custom class names to merge via cn() on the root element.',\n  },\n  {\n    id: 5,\n    category: 'Design Craft',\n    prompt:\n      'Why is the sub-12px micro-text rule (avoiding `text-xs`, `text-xs`) strictly enforced in UIPKGE craft standards?',\n    codeSnippet: `// Anti-pattern:\\n<span class=\"text-xs uppercase text-muted-foreground\">Status</span>\\n\\n// Standard-compliant:\\n<span class=\"text-xs font-medium uppercase text-muted-foreground\">Status</span>`,\n    codeLanguage: 'HTML',\n    options: [\n      { id: 'A', text: 'Sub-12px text triggers subpixel antialiasing rendering bugs on macOS WebKit.' },\n      {\n        id: 'B',\n        text: 'Arbitrary micro-text degrades legibility, violates WCAG AA contrast guidelines, and fragments typographic scale discipline.',\n      },\n      { id: 'C', text: 'Tailwind CSS v4 ignores arbitrary bracket pixel values in production builds.' },\n      { id: 'D', text: 'Screen readers automatically skip DOM elements rendered smaller than 12px.' },\n    ],\n    correctOptionId: 'B',\n    explanation:\n      'UIPKGE design craft mandates semantic typography starting at text-xs (12px) for badges and metadata to maintain readability and systematic hierarchy across devices.',\n  },\n  {\n    id: 6,\n    category: 'React Architecture',\n    prompt:\n      'In the React registry mirror, what headless primitive pattern allows consumers to pass custom trigger elements via `asChild`?',\n    codeSnippet: `const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\\n  ({ className, asChild = false, ...props }, ref) => {\\n    const Comp = asChild ? Slot : 'button'\\n    return <Comp ref={ref} className={cn(buttonVariants(), className)} {...props} />\\n  }\\n)`,\n    codeLanguage: 'TSX',\n    options: [\n      { id: 'A', text: 'Radix UI Slot primitive forwarded with asChild boolean prop.' },\n      { id: 'B', text: 'React cloneElement with recursive prop decoration.' },\n      { id: 'C', text: 'Custom Shadow DOM web component injection.' },\n      { id: 'D', text: 'Direct DOM mutation through React portal boundaries.' },\n    ],\n    correctOptionId: 'A',\n    explanation:\n      'The Radix UI Slot primitive merges props and event listeners directly onto its immediate child, enabling seamless element composition without wrapper DIVs.',\n  },\n  {\n    id: 7,\n    category: 'Typography / UX',\n    prompt:\n      'Which CSS font utility prevents numerical values like timers, counters, and metrics from causing horizontal layout shift during updates?',\n    codeSnippet: `<div class=\"font-mono text-sm font-semibold tabular-nums text-foreground\">\\n  18:42 remaining\\n</div>`,\n    codeLanguage: 'HTML',\n    options: [\n      { id: 'A', text: 'tabular-nums (font-variant-numeric: tabular-nums)' },\n      { id: 'B', text: 'font-stretch-condensed' },\n      { id: 'C', text: 'tracking-tightest' },\n      { id: 'D', text: 'text-balance' },\n    ],\n    correctOptionId: 'A',\n    explanation:\n      'tabular-nums enforces uniform width for numerical glyphs (0-9), preventing visual jitter when numbers change in timers and live feeds.',\n  },\n  {\n    id: 8,\n    category: 'Registry Conventions',\n    prompt: 'What is the core rule regarding data arrays and tile layout abstractions when authoring UIPKGE blocks?',\n    codeSnippet: `// Allowed in blocks:\\n<div class=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\\n  <Card><CardHeader><CardTitle>Total Revenue</CardTitle></CardHeader>...</Card>\\n  <Card><CardHeader><CardTitle>Active Users</CardTitle></CardHeader>...</Card>\\n</div>`,\n    codeLanguage: 'TSX',\n    options: [\n      { id: 'A', text: 'Always abstract card lists into a generic <StatCard items={data} /> primitive.' },\n      {\n        id: 'B',\n        text: 'Blocks must compose primitives raw and top-to-bottom so developers can inspect and edit layout structure directly.',\n      },\n      { id: 'C', text: 'Never render more than two cards per block layout.' },\n      { id: 'D', text: 'All block components must persist their state to IndexedDB storage.' },\n    ],\n    correctOptionId: 'B',\n    explanation:\n      'UIPKGE explicitly bans monolithic StatCard-shaped primitives. Blocks must expose raw primitive composition inline so users have full ownership of the rendered markup.',\n  },\n  {\n    id: 9,\n    category: 'Accessibility (a11y)',\n    prompt:\n      'Which focus ring class pattern guarantees high-contrast keyboard accessibility without creating persistent outlines on mouse clicks?',\n    codeSnippet: `class=\"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none\"`,\n    codeLanguage: 'HTML',\n    options: [\n      { id: 'A', text: 'focus:outline-none with no replacement outline or ring.' },\n      { id: 'B', text: 'focus-visible:ring-2 focus-visible:ring-ring with visible focus ring tokens.' },\n      { id: 'C', text: 'active:scale-95 on all interactive mouse events.' },\n      { id: 'D', text: 'hover:border-destructive on standard form input elements.' },\n    ],\n    correctOptionId: 'B',\n    explanation:\n      'focus-visible only applies the focus ring when keyboard navigation is detected, preserving clean aesthetics on mouse clicks while strictly satisfying WCAG 2.1 criteria.',\n  },\n  {\n    id: 10,\n    category: 'Registry Distribution',\n    prompt: 'What is the purpose of the `registryDependencies` array in `<name>.registry.ts` item manifests?',\n    codeSnippet: `export default defineRegistryItem({\\n  name: 'quiz-assessment-runner',\\n  type: 'registry:block',\\n  registryDependencies: [\\n    'https://uipkge.dev/r/badge.json',\\n    'https://uipkge.dev/r/button.json',\\n    'https://uipkge.dev/r/card.json',\\n  ],\\n})`,\n    codeLanguage: 'TypeScript',\n    options: [\n      { id: 'A', text: 'It publishes private registry tarballs directly to npmjs.com.' },\n      {\n        id: 'B',\n        text: 'It instructs shadcn / shadcn-vue CLIs to transitively resolve and copy required UI primitives into the consumer project.',\n      },\n      { id: 'C', text: 'It configures Webpack chunk splitting for client-side routing.' },\n      { id: 'D', text: 'It generates backend database migration tables automatically.' },\n    ],\n    correctOptionId: 'B',\n    explanation:\n      'registryDependencies allows shadcn and shadcn-vue CLI tools to resolve and download all required component dependencies when installing a block.',\n  },\n]\n\nexport type ReviewFilter = 'all' | 'correct' | 'incorrect' | 'flagged'\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-quiz-assessment-runner, [ui-quiz-assessment-runner]',\n  standalone: true,\n  host: {\n    '[attr.data-slot]': '\"quiz-assessment-runner\"',\n    '[class]': 'hostClass',\n  },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardFooterComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiDialogComponent,\n    UiDialogContentComponent,\n    UiDialogDescriptionComponent,\n    UiDialogFooterComponent,\n    UiDialogHeaderComponent,\n    UiDialogTitleComponent,\n    UiProgressComponent,\n    UiSeparatorComponent,\n  ],\n  template: `\n    @if (!isSubmitted()) {\n      <!-- Quiz Top Bar / Header -->\n      <div ui-card class=\"border-border bg-card shadow-xs\">\n        <div ui-card-content class=\"p-4 sm:p-5\">\n          <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n            <div class=\"space-y-1\">\n              <div class=\"flex flex-wrap items-center gap-2\">\n                <span ui-badge variant=\"secondary\" class=\"text-xs font-medium\"> Certification Assessment </span>\n                <span ui-badge variant=\"outline\" class=\"text-muted-foreground font-mono text-xs\"> Exam #TS-804 </span>\n              </div>\n              <h1 class=\"text-foreground text-base font-bold tracking-tight sm:text-lg\">\n                TypeScript & Component Architecture Certification Quiz\n              </h1>\n            </div>\n            <div class=\"flex flex-wrap items-center gap-2.5 sm:gap-3\">\n              <div [class]=\"timerPillClass\">\n                <lucide-icon [img]=\"Clock\" class=\"size-3.5 shrink-0\" />\n                <span class=\"font-mono font-semibold tabular-nums\">{{ formattedTime() }}</span>\n                <span class=\"text-muted-foreground text-xs\">remaining</span>\n              </div>\n              <span ui-badge variant=\"outline\" class=\"h-8 px-2.5 text-xs font-medium tabular-nums\">\n                Question {{ currentQuestionIndex() + 1 }} of {{ totalQuestions }}\n              </span>\n              <button\n                ui-button\n                variant=\"ghost\"\n                size=\"sm\"\n                class=\"text-muted-foreground hover:text-foreground text-xs\"\n                (click)=\"isExitDialogOpen.set(true)\"\n              >\n                <lucide-icon [img]=\"LogOut\" class=\"mr-1.5 size-3.5\" />\n                Exit Quiz\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <!-- 2-Column Quiz Canvas -->\n      <div class=\"grid grid-cols-1 items-start gap-6 lg:grid-cols-12\">\n        <div class=\"space-y-6 lg:col-span-8\">\n          <div ui-card class=\"border-border shadow-xs\">\n            <div ui-card-header class=\"space-y-3 pb-4\">\n              <div class=\"flex items-center justify-between gap-2\">\n                <div class=\"flex items-center gap-2\">\n                  <span ui-badge variant=\"secondary\" class=\"text-xs font-medium\">\n                    {{ currentQuestion().category }}\n                  </span>\n                  <span class=\"text-muted-foreground text-xs font-medium tabular-nums\">\n                    Question {{ currentQuestionIndex() + 1 }} of {{ totalQuestions }}\n                  </span>\n                </div>\n                <button\n                  ui-button\n                  variant=\"outline\"\n                  size=\"sm\"\n                  [class]=\"flagButtonClass\"\n                  (click)=\"toggleFlag(currentQuestion().id)\"\n                >\n                  <lucide-icon\n                    [img]=\"Flag\"\n                    [class]=\"\n                      flagged().has(currentQuestion().id)\n                        ? 'fill-warning text-warning mr-1.5 size-3.5'\n                        : 'mr-1.5 size-3.5'\n                    \"\n                  />\n                  {{ flagged().has(currentQuestion().id) ? 'Flagged' : 'Flag for Review' }}\n                </button>\n              </div>\n              <h3 ui-card-title class=\"text-foreground text-base leading-relaxed font-semibold sm:text-lg\">\n                {{ currentQuestion().prompt }}\n              </h3>\n            </div>\n\n            <div ui-card-content class=\"space-y-5\">\n              @if (currentQuestion().codeSnippet) {\n                <div class=\"border-border/80 bg-muted/40 dark:bg-muted/20 overflow-hidden rounded-lg border\">\n                  <div\n                    class=\"border-border/70 bg-muted/80 dark:bg-muted/40 flex items-center justify-between border-b px-3.5 py-1.5\"\n                  >\n                    <div class=\"flex items-center gap-2\">\n                      <lucide-icon [img]=\"FileCode\" class=\"text-muted-foreground size-3.5\" />\n                      <span class=\"text-muted-foreground font-mono text-xs font-medium\">\n                        {{ currentQuestion().codeLanguage || 'TypeScript' }}\n                      </span>\n                    </div>\n                    <span class=\"text-muted-foreground font-mono text-xs\">Context</span>\n                  </div>\n                  <pre\n                    class=\"text-foreground/90 overflow-x-auto p-4 font-mono text-xs leading-relaxed whitespace-pre\"\n                  ><code>{{ currentQuestion().codeSnippet }}</code></pre>\n                </div>\n              }\n\n              <div class=\"space-y-3\">\n                <p class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">\n                  Select the correct answer:\n                </p>\n                <div class=\"grid gap-2.5\">\n                  @for (opt of currentQuestion().options; track opt.id) {\n                    <div\n                      role=\"button\"\n                      tabindex=\"0\"\n                      [class]=\"optionClass(opt.id)\"\n                      (click)=\"selectOption(opt.id)\"\n                      (keydown.enter)=\"selectOption(opt.id)\"\n                      (keydown.space)=\"selectOption(opt.id); $event.preventDefault()\"\n                    >\n                      <div [class]=\"optionLetterClass(opt.id)\">{{ opt.id }}</div>\n                      <div class=\"flex-1 space-y-0.5\">\n                        <p class=\"text-foreground text-sm leading-relaxed font-medium\">{{ opt.text }}</p>\n                      </div>\n                      @if (answers()[currentQuestion().id] === opt.id) {\n                        <div class=\"text-primary mt-0.5 shrink-0\">\n                          <lucide-icon [img]=\"Check\" class=\"size-4\" />\n                        </div>\n                      }\n                    </div>\n                  }\n                </div>\n              </div>\n            </div>\n\n            <div\n              ui-card-footer\n              class=\"border-border bg-muted/10 flex flex-wrap items-center justify-between gap-3 border-t p-4 sm:p-5\"\n            >\n              <button\n                ui-button\n                variant=\"outline\"\n                size=\"sm\"\n                [disabled]=\"currentQuestionIndex() === 0\"\n                class=\"text-xs\"\n                (click)=\"prevQuestion()\"\n              >\n                <lucide-icon [img]=\"ChevronLeft\" class=\"mr-1 size-4\" />\n                Previous Question\n              </button>\n              <div class=\"flex items-center gap-2\">\n                @if (currentQuestionIndex() === totalQuestions - 1) {\n                  <button\n                    ui-button\n                    variant=\"default\"\n                    size=\"sm\"\n                    class=\"text-xs font-medium\"\n                    (click)=\"isSubmitDialogOpen.set(true)\"\n                  >\n                    <lucide-icon [img]=\"CheckCircle2\" class=\"mr-1.5 size-4\" />\n                    Review & Submit\n                  </button>\n                } @else {\n                  <button ui-button variant=\"default\" size=\"sm\" class=\"text-xs font-medium\" (click)=\"nextQuestion()\">\n                    Save & Next\n                    <lucide-icon [img]=\"ChevronRight\" class=\"ml-1 size-4\" />\n                  </button>\n                }\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <!-- Right: Question Navigation Sidebar -->\n        <div class=\"space-y-4 lg:sticky lg:top-6 lg:col-span-4\">\n          <div ui-card class=\"border-border shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <div class=\"flex items-center justify-between\">\n                <h3 ui-card-title class=\"text-sm font-semibold\">Question Navigator</h3>\n                <span ui-badge variant=\"outline\" class=\"font-mono text-xs tabular-nums\">\n                  {{ answeredCount() }}/{{ totalQuestions }} done\n                </span>\n              </div>\n              <p ui-card-description class=\"text-xs\">Jump directly to any question or review status.</p>\n            </div>\n            <div ui-card-content class=\"space-y-4\">\n              <div class=\"space-y-1.5\">\n                <div class=\"flex items-center justify-between text-xs\">\n                  <span class=\"text-muted-foreground font-medium\">Exam Progress</span>\n                  <span class=\"text-foreground font-semibold tabular-nums\">{{ progressPercentage() }}% Complete</span>\n                </div>\n                <ui-progress [value]=\"progressPercentage()\" class=\"h-2\" />\n              </div>\n              <ui-separator />\n              <div class=\"space-y-2\">\n                <span class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\"> Questions </span>\n                <div class=\"grid grid-cols-5 gap-2\">\n                  @for (q of questions; track q.id; let idx = $index) {\n                    <button type=\"button\" [class]=\"navButtonClass(idx, q.id)\" (click)=\"goToQuestion(idx)\">\n                      {{ q.id }}\n                      @if (flagged().has(q.id) && idx !== currentQuestionIndex()) {\n                        <span\n                          class=\"ring-background bg-warning absolute -top-1 -right-1 size-2 rounded-full ring-2\"\n                          title=\"Flagged\"\n                        ></span>\n                      }\n                    </button>\n                  }\n                </div>\n              </div>\n              <div class=\"border-border/70 bg-muted/30 space-y-2 rounded-lg border p-3 text-xs\">\n                <span class=\"text-foreground font-medium\">Status Legend</span>\n                <div class=\"text-muted-foreground grid grid-cols-2 gap-2\">\n                  <div class=\"flex items-center gap-1.5\">\n                    <span class=\"bg-success size-2 rounded-full\"></span>\n                    <span>Answered ({{ answeredCount() }})</span>\n                  </div>\n                  <div class=\"flex items-center gap-1.5\">\n                    <span class=\"bg-warning size-2 rounded-full\"></span>\n                    <span>Flagged ({{ flaggedCount() }})</span>\n                  </div>\n                  <div class=\"flex items-center gap-1.5\">\n                    <span class=\"bg-primary size-2 rounded-full\"></span>\n                    <span>Current</span>\n                  </div>\n                  <div class=\"flex items-center gap-1.5\">\n                    <span class=\"bg-muted-foreground/40 size-2 rounded-full\"></span>\n                    <span>Unanswered ({{ unansweredCount() }})</span>\n                  </div>\n                </div>\n              </div>\n              <ui-separator />\n              <button\n                ui-button\n                variant=\"default\"\n                class=\"h-10 w-full text-xs font-semibold shadow-xs\"\n                (click)=\"isSubmitDialogOpen.set(true)\"\n              >\n                <lucide-icon [img]=\"CheckCircle2\" class=\"mr-2 size-4\" />\n                Submit Exam\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n    } @else {\n      <div class=\"space-y-6\">\n        <!-- Results Hero Score Card -->\n        <div ui-card class=\"border-border bg-card overflow-hidden shadow-xs\">\n          <div class=\"border-border bg-muted/20 border-b p-6 sm:p-8\">\n            <div class=\"flex flex-col gap-6 sm:flex-row sm:items-center sm:justify-between\">\n              <div class=\"space-y-2\">\n                <div class=\"flex flex-wrap items-center gap-2\">\n                  <span\n                    ui-badge\n                    [variant]=\"isPassed() ? 'secondary' : 'outline'\"\n                    [class]=\"\n                      isPassed()\n                        ? 'border-success/30 bg-success/10 text-success text-xs font-semibold'\n                        : 'bg-destructive/10 text-destructive border-destructive/30 text-xs font-semibold'\n                    \"\n                  >\n                    {{ isPassed() ? 'PASSED · CERTIFIED' : 'DID NOT PASS · RETAKE RECOMMENDED' }}\n                  </span>\n                  <span ui-badge variant=\"outline\" class=\"text-muted-foreground font-mono text-xs\">\n                    Exam Code #TS-804\n                  </span>\n                </div>\n                <h1 class=\"text-foreground text-2xl font-bold tracking-tight sm:text-3xl\">\n                  <span class=\"font-mono tabular-nums\">{{ scorePercentage() }}%</span> Score · {{ gradeLabel() }}\n                </h1>\n                <p class=\"text-muted-foreground max-w-xl text-sm\">\n                  {{\n                    isPassed()\n                      ? 'Congratulations! You have satisfied the technical competency standards for TypeScript and UIPKGE Component Architecture.'\n                      : 'You scored below the 80% certification threshold. Review the explanations below and retake the assessment when ready.'\n                  }}\n                </p>\n              </div>\n              <div class=\"flex shrink-0 items-center gap-3\">\n                <button ui-button variant=\"default\" size=\"sm\" class=\"text-xs font-semibold\" (click)=\"retakeQuiz()\">\n                  <lucide-icon [img]=\"RotateCcw\" class=\"mr-1.5 size-3.5\" />\n                  Retake Exam\n                </button>\n              </div>\n            </div>\n          </div>\n          <div ui-card-content class=\"p-6\">\n            <div class=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n              <div class=\"border-border bg-card space-y-1 rounded-lg border p-4\">\n                <span class=\"text-muted-foreground text-xs font-medium\">Total Score</span>\n                <div class=\"text-foreground text-xl font-bold tabular-nums\">\n                  {{ correctCount() }}\n                  <span class=\"text-muted-foreground text-sm font-normal\">/ {{ totalQuestions }}</span>\n                </div>\n                <div class=\"text-muted-foreground font-mono text-xs\">{{ scorePercentage() }}% accuracy</div>\n              </div>\n              <div class=\"border-border bg-card space-y-1 rounded-lg border p-4\">\n                <span class=\"text-muted-foreground text-xs font-medium\">Result Status</span>\n                <div [class]=\"cn('text-xl font-bold', isPassed() ? 'text-success' : 'text-destructive')\">\n                  {{ isPassed() ? 'Passed' : 'Failed' }}\n                </div>\n                <div class=\"text-muted-foreground font-mono text-xs\">Passing threshold: 80%</div>\n              </div>\n              <div class=\"border-border bg-card space-y-1 rounded-lg border p-4\">\n                <span class=\"text-muted-foreground text-xs font-medium\">Time Elapsed</span>\n                <div class=\"text-foreground font-mono text-xl font-bold tabular-nums\">{{ timeSpentFormatted() }}</div>\n                <div class=\"text-muted-foreground font-mono text-xs\">18m 42s allocated</div>\n              </div>\n              <div class=\"border-border bg-card space-y-1 rounded-lg border p-4\">\n                <span class=\"text-muted-foreground text-xs font-medium\">Flagged Questions</span>\n                <div class=\"text-foreground text-xl font-bold tabular-nums\">{{ flaggedCount() }}</div>\n                <div class=\"text-muted-foreground font-mono text-xs\">Reviewed items</div>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <!-- Question Review Section -->\n        <div ui-card class=\"border-border shadow-xs\">\n          <div ui-card-header class=\"pb-4\">\n            <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n              <div>\n                <h3 ui-card-title class=\"text-base font-semibold\">Answer Review & Explanations</h3>\n                <p ui-card-description class=\"text-xs\">\n                  Detailed technical breakdown for every question in the assessment.\n                </p>\n              </div>\n              <div class=\"flex flex-wrap items-center gap-1.5\">\n                <button\n                  ui-button\n                  [variant]=\"reviewFilter() === 'all' ? 'default' : 'outline'\"\n                  size=\"sm\"\n                  class=\"h-7.5 px-2.5 text-xs\"\n                  (click)=\"reviewFilter.set('all')\"\n                >\n                  All ({{ totalQuestions }})\n                </button>\n                <button\n                  ui-button\n                  [variant]=\"reviewFilter() === 'correct' ? 'default' : 'outline'\"\n                  size=\"sm\"\n                  class=\"h-7.5 px-2.5 text-xs\"\n                  (click)=\"reviewFilter.set('correct')\"\n                >\n                  Correct ({{ correctCount() }})\n                </button>\n                <button\n                  ui-button\n                  [variant]=\"reviewFilter() === 'incorrect' ? 'default' : 'outline'\"\n                  size=\"sm\"\n                  class=\"h-7.5 px-2.5 text-xs\"\n                  (click)=\"reviewFilter.set('incorrect')\"\n                >\n                  Incorrect ({{ incorrectCount() }})\n                </button>\n                <button\n                  ui-button\n                  [variant]=\"reviewFilter() === 'flagged' ? 'default' : 'outline'\"\n                  size=\"sm\"\n                  class=\"h-7.5 px-2.5 text-xs\"\n                  (click)=\"reviewFilter.set('flagged')\"\n                >\n                  Flagged ({{ flaggedCount() }})\n                </button>\n              </div>\n            </div>\n          </div>\n          <div ui-card-content class=\"space-y-4\">\n            @for (q of filteredReviewQuestions(); track q.id) {\n              <div [class]=\"reviewCardClass(q)\">\n                <div class=\"flex flex-wrap items-center justify-between gap-2\">\n                  <div class=\"flex items-center gap-2\">\n                    <span ui-badge variant=\"outline\" class=\"text-xs font-bold tabular-nums\"> Question {{ q.id }} </span>\n                    <span ui-badge variant=\"secondary\" class=\"text-xs\">{{ q.category }}</span>\n                    @if (flagged().has(q.id)) {\n                      <span ui-badge variant=\"outline\" class=\"border-warning/40 bg-warning/10 text-warning text-xs\">\n                        <lucide-icon [img]=\"Flag\" class=\"fill-warning mr-1 size-3\" />\n                        Flagged\n                      </span>\n                    }\n                  </div>\n                  <span\n                    ui-badge\n                    [variant]=\"answers()[q.id] === q.correctOptionId ? 'secondary' : 'outline'\"\n                    [class]=\"\n                      answers()[q.id] === q.correctOptionId\n                        ? 'border-success/30 bg-success/10 text-success text-xs font-semibold'\n                        : 'bg-destructive/10 text-destructive border-destructive/30 text-xs font-semibold'\n                    \"\n                  >\n                    <lucide-icon\n                      [img]=\"answers()[q.id] === q.correctOptionId ? CheckCircle2 : XCircle\"\n                      class=\"mr-1 size-3.5\"\n                    />\n                    {{\n                      answers()[q.id] === q.correctOptionId\n                        ? 'Correct (+10 pts)'\n                        : answers()[q.id]\n                          ? 'Incorrect (0 pts)'\n                          : 'Unanswered (0 pts)'\n                    }}\n                  </span>\n                </div>\n                <p class=\"text-foreground text-sm leading-relaxed font-semibold\">{{ q.prompt }}</p>\n                @if (q.codeSnippet) {\n                  <div class=\"border-border/80 bg-muted/40 dark:bg-muted/20 overflow-hidden rounded-md border\">\n                    <pre\n                      class=\"text-foreground/80 overflow-x-auto p-3 font-mono text-xs leading-relaxed whitespace-pre\"\n                    ><code>{{ q.codeSnippet }}</code></pre>\n                  </div>\n                }\n                <div class=\"grid gap-2 sm:grid-cols-2\">\n                  <div [class]=\"userAnswerClass(q)\">\n                    <span class=\"block text-xs font-semibold tracking-wider uppercase opacity-80\">\n                      Your Response:\n                    </span>\n                    <div class=\"font-medium\">\n                      @if (answers()[q.id]) {\n                        <span class=\"mr-1 font-bold\">Option {{ answers()[q.id] }}:</span>\n                        <span>{{ optionText(q, answers()[q.id]!) }}</span>\n                      } @else {\n                        <span>No answer submitted</span>\n                      }\n                    </div>\n                  </div>\n                  <div\n                    class=\"border-success/30 bg-success/10 text-foreground text-success space-y-1 rounded-md border p-3 text-xs\"\n                  >\n                    <span class=\"block text-xs font-semibold tracking-wider uppercase opacity-80\">\n                      Correct Answer:\n                    </span>\n                    <div class=\"font-medium\">\n                      <span class=\"mr-1 font-bold\">Option {{ q.correctOptionId }}:</span>\n                      <span>{{ optionText(q, q.correctOptionId) }}</span>\n                    </div>\n                  </div>\n                </div>\n                <div class=\"border-border/80 bg-muted/40 text-muted-foreground space-y-1 rounded-md border p-3 text-xs\">\n                  <div class=\"text-foreground flex items-center gap-1.5 font-semibold\">\n                    <lucide-icon [img]=\"Lightbulb\" class=\"text-primary size-3.5\" />\n                    <span>Architectural Explanation:</span>\n                  </div>\n                  <p class=\"leading-relaxed\">{{ q.explanation }}</p>\n                </div>\n              </div>\n            }\n          </div>\n          <div ui-card-footer class=\"border-border bg-muted/10 flex items-center justify-between border-t p-4 sm:p-5\">\n            <span class=\"text-muted-foreground font-mono text-xs\"> Exam ID: 804-FE-UIPKGE </span>\n            <button ui-button variant=\"default\" size=\"sm\" class=\"text-xs\" (click)=\"retakeQuiz()\">\n              <lucide-icon [img]=\"RotateCcw\" class=\"mr-1.5 size-3.5\" />\n              Retake Assessment\n            </button>\n          </div>\n        </div>\n      </div>\n    }\n\n    <!-- DIALOG: CONFIRM EXAM SUBMISSION -->\n    <ui-dialog [open]=\"isSubmitDialogOpen()\" (openChange)=\"isSubmitDialogOpen.set($event)\">\n      <ui-dialog-content class=\"sm:max-w-md\">\n        <div ui-dialog-header>\n          <h2 ui-dialog-title class=\"text-base font-semibold\">Submit Assessment?</h2>\n          <p ui-dialog-description class=\"text-xs\">Review your completion status before finalizing your submission.</p>\n        </div>\n        <div class=\"space-y-3 py-2 text-xs\">\n          <div class=\"grid grid-cols-3 gap-2 text-center\">\n            <div class=\"border-border bg-muted/40 space-y-0.5 rounded-lg border p-2.5\">\n              <span class=\"text-muted-foreground\">Answered</span>\n              <p class=\"text-foreground text-base font-bold tabular-nums\">{{ answeredCount() }}/{{ totalQuestions }}</p>\n            </div>\n            <div class=\"border-border bg-muted/40 space-y-0.5 rounded-lg border p-2.5\">\n              <span class=\"text-muted-foreground\">Flagged</span>\n              <p class=\"text-warning text-base font-bold tabular-nums\">{{ flaggedCount() }}</p>\n            </div>\n            <div class=\"border-border bg-muted/40 space-y-0.5 rounded-lg border p-2.5\">\n              <span class=\"text-muted-foreground\">Unanswered</span>\n              <p\n                [class]=\"\n                  cn('text-base font-bold tabular-nums', unansweredCount() > 0 ? 'text-destructive' : 'text-foreground')\n                \"\n              >\n                {{ unansweredCount() }}\n              </p>\n            </div>\n          </div>\n          @if (unansweredCount() > 0) {\n            <div\n              class=\"border-warning/30 bg-warning/10 text-warning flex items-start gap-2 rounded-lg border p-3 text-xs\"\n            >\n              <lucide-icon [img]=\"AlertCircle\" class=\"mt-0.5 size-4 shrink-0\" />\n              <p>\n                You have\n                <strong>{{ unansweredCount() }} unanswered question{{ unansweredCount() === 1 ? '' : 's' }}</strong\n                >. Unanswered questions receive 0 points.\n              </p>\n            </div>\n          }\n          <p class=\"text-muted-foreground text-xs\">\n            Once submitted, you will immediately receive your final score and detailed technical explanations.\n          </p>\n        </div>\n        <div ui-dialog-footer class=\"gap-2 sm:gap-0\">\n          <button ui-button variant=\"outline\" size=\"sm\" class=\"text-xs\" (click)=\"isSubmitDialogOpen.set(false)\">\n            Continue Quiz\n          </button>\n          <button ui-button variant=\"default\" size=\"sm\" class=\"text-xs font-semibold\" (click)=\"submitExam()\">\n            Confirm & Submit\n          </button>\n        </div>\n      </ui-dialog-content>\n    </ui-dialog>\n\n    <!-- DIALOG: CONFIRM EXIT -->\n    <ui-dialog [open]=\"isExitDialogOpen()\" (openChange)=\"isExitDialogOpen.set($event)\">\n      <ui-dialog-content class=\"sm:max-w-md\">\n        <div ui-dialog-header>\n          <h2 ui-dialog-title class=\"text-base font-semibold\">Exit Assessment?</h2>\n          <p ui-dialog-description class=\"text-xs\">\n            Are you sure you want to exit? Your answers for this session will be cleared.\n          </p>\n        </div>\n        <div class=\"text-muted-foreground py-2 text-xs\">\n          You have answered {{ answeredCount() }} of {{ totalQuestions }} questions. Exiting now will reset your\n          attempt.\n        </div>\n        <div ui-dialog-footer class=\"gap-2 sm:gap-0\">\n          <button ui-button variant=\"outline\" size=\"sm\" class=\"text-xs\" (click)=\"isExitDialogOpen.set(false)\">\n            Cancel\n          </button>\n          <button ui-button variant=\"destructive\" size=\"sm\" class=\"text-xs\" (click)=\"confirmExit()\">Exit Quiz</button>\n        </div>\n      </ui-dialog-content>\n    </ui-dialog>\n  `,\n})\nexport class UiQuizAssessmentRunnerComponent {\n  protected readonly AlertCircle = AlertCircle\n  protected readonly Check = Check\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly ChevronLeft = ChevronLeft\n  protected readonly ChevronRight = ChevronRight\n  protected readonly Clock = Clock\n  protected readonly FileCode = FileCode\n  protected readonly Flag = Flag\n  protected readonly Lightbulb = Lightbulb\n  protected readonly LogOut = LogOut\n  protected readonly RotateCcw = RotateCcw\n  protected readonly XCircle = XCircle\n  protected readonly cn = cn\n\n  protected readonly questions = DEFAULT_QUESTIONS\n  protected readonly totalQuestions = DEFAULT_QUESTIONS.length\n\n  @Input() set initialQuestion(v: number | undefined) {\n    const idx = Math.max(0, Math.min((v ?? 1) - 1, this.totalQuestions - 1))\n    this.currentQuestionIndex.set(idx)\n  }\n  @Input() set initialTimeRemaining(v: number | undefined) {\n    this.initialBudget = v ?? 1122\n    this.timeRemaining.set(v ?? 1122)\n  }\n  @Input() set initialAnswers(v: Record<number, string> | undefined) {\n    this.answers.set({ ...(v ?? {}) })\n  }\n  @Input() set initialFlagged(v: number[] | undefined) {\n    this.flagged.set(new Set(v ?? []))\n  }\n  @Input() set initialSubmitted(v: boolean | undefined) {\n    this.isSubmitted.set(!!v)\n  }\n  @Input('class') className?: string\n\n  private initialBudget = 1122\n  private timer: ReturnType<typeof setInterval> | null = null\n\n  readonly currentQuestionIndex = signal(0)\n  readonly answers = signal<Record<number, string>>({})\n  readonly flagged = signal<Set<number>>(new Set())\n  readonly timeRemaining = signal(1122)\n  readonly isSubmitted = signal(false)\n  readonly isSubmitDialogOpen = signal(false)\n  readonly isExitDialogOpen = signal(false)\n  readonly reviewFilter = signal<ReviewFilter>('all')\n\n  constructor() {\n    const destroyRef = inject(DestroyRef)\n    destroyRef.onDestroy(() => this.clearTimer())\n    // Start the countdown on the next macrotask so constructor-time input setters land first.\n    setTimeout(() => this.startTimer(), 0)\n  }\n\n  readonly currentQuestion = computed(() => DEFAULT_QUESTIONS[this.currentQuestionIndex()]!)\n  readonly answeredCount = computed(() => Object.keys(this.answers()).length)\n  readonly flaggedCount = computed(() => this.flagged().size)\n  readonly unansweredCount = computed(() => this.totalQuestions - this.answeredCount())\n  readonly progressPercentage = computed(() => Math.round((this.answeredCount() / this.totalQuestions) * 100))\n  readonly correctCount = computed(\n    () => DEFAULT_QUESTIONS.filter((q) => this.answers()[q.id] === q.correctOptionId).length,\n  )\n  readonly incorrectCount = computed(\n    () => DEFAULT_QUESTIONS.filter((q) => this.answers()[q.id] && this.answers()[q.id] !== q.correctOptionId).length,\n  )\n  readonly scorePercentage = computed(() => Math.round((this.correctCount() / this.totalQuestions) * 100))\n  readonly isPassed = computed(() => this.scorePercentage() >= 80)\n  readonly gradeLabel = computed(() => {\n    const s = this.scorePercentage()\n    if (s >= 90) return 'Grade A (Mastery)'\n    if (s >= 80) return 'Grade B (Proficient)'\n    if (s >= 70) return 'Grade C (Needs Review)'\n    return 'Grade F (Did Not Pass)'\n  })\n  readonly formattedTime = computed(() => {\n    const t = Math.max(0, this.timeRemaining())\n    return `${String(Math.floor(t / 60)).padStart(2, '0')}:${String(t % 60).padStart(2, '0')}`\n  })\n  readonly timeSpentFormatted = computed(() => {\n    const spent = Math.max(0, this.initialBudget - this.timeRemaining())\n    return `${Math.floor(spent / 60)}m ${String(spent % 60).padStart(2, '0')}s`\n  })\n  readonly filteredReviewQuestions = computed(() => {\n    const f = this.reviewFilter()\n    if (f === 'correct') return DEFAULT_QUESTIONS.filter((q) => this.answers()[q.id] === q.correctOptionId)\n    if (f === 'incorrect') return DEFAULT_QUESTIONS.filter((q) => this.answers()[q.id] !== q.correctOptionId)\n    if (f === 'flagged') return DEFAULT_QUESTIONS.filter((q) => this.flagged().has(q.id))\n    return DEFAULT_QUESTIONS\n  })\n\n  get hostClass(): string {\n    return cn('mx-auto w-full max-w-6xl space-y-6', this.className)\n  }\n\n  get timerPillClass(): string {\n    return cn(\n      'flex items-center gap-2 rounded-lg border px-3 py-1.5 text-xs font-medium shadow-xs transition-colors',\n      this.timeRemaining() < 300\n        ? 'border-destructive/40 bg-destructive/10 text-destructive'\n        : 'border-border bg-muted/50 text-foreground',\n    )\n  }\n\n  get flagButtonClass(): string {\n    return cn(\n      'text-xs',\n      this.flagged().has(this.currentQuestion().id)\n        ? 'border-warning/40 bg-warning/10 text-warning font-semibold'\n        : 'text-muted-foreground hover:text-foreground',\n    )\n  }\n\n  optionClass(optionId: string): string {\n    return cn(\n      'group relative flex cursor-pointer items-start gap-3.5 rounded-lg border p-3.5 text-left transition-colors sm:p-4',\n      'focus-visible:ring-ring select-none focus-visible:ring-2 focus-visible:outline-none',\n      this.answers()[this.currentQuestion().id] === optionId\n        ? 'border-primary bg-primary/[0.04] dark:bg-primary/10 ring-primary shadow-xs ring-1'\n        : 'border-border bg-card hover:bg-muted/40 hover:border-muted-foreground/30',\n    )\n  }\n\n  optionLetterClass(optionId: string): string {\n    return cn(\n      'mt-0.5 flex size-6 shrink-0 items-center justify-center rounded-full border text-xs font-bold transition-colors',\n      this.answers()[this.currentQuestion().id] === optionId\n        ? 'border-primary bg-primary text-primary-foreground'\n        : 'border-border bg-muted/60 text-muted-foreground group-hover:border-foreground/30 group-hover:text-foreground',\n    )\n  }\n\n  navButtonClass(idx: number, qid: number): string {\n    return cn(\n      'relative flex size-10 items-center justify-center rounded-lg border text-xs font-semibold tabular-nums transition-colors',\n      'focus-visible:ring-ring cursor-pointer focus-visible:ring-2 focus-visible:outline-none',\n      idx === this.currentQuestionIndex()\n        ? 'border-primary bg-primary/10 text-primary ring-primary font-bold shadow-xs ring-2'\n        : this.answers()[qid]\n          ? 'border-success/30 bg-success/10 text-success hover:bg-success/20'\n          : this.flagged().has(qid)\n            ? 'border-warning/30 bg-warning/10 text-warning hover:bg-warning/20'\n            : 'border-border bg-muted/40 text-muted-foreground hover:bg-muted hover:text-foreground',\n    )\n  }\n\n  reviewCardClass(q: QuizQuestion): string {\n    return cn(\n      'space-y-4 rounded-lg border p-4 transition-colors sm:p-5',\n      this.answers()[q.id] === q.correctOptionId\n        ? 'border-success/30 bg-success/[0.02] bg-success/[0.05]'\n        : 'border-destructive/30 bg-destructive/[0.02] dark:bg-destructive/[0.05]',\n    )\n  }\n\n  userAnswerClass(q: QuizQuestion): string {\n    return cn(\n      'space-y-1 rounded-md border p-3 text-xs',\n      this.answers()[q.id] === q.correctOptionId\n        ? 'border-success/30 bg-success/10 text-foreground text-success'\n        : 'border-destructive/30 bg-destructive/10 text-destructive',\n    )\n  }\n\n  optionText(q: QuizQuestion, optionId: string): string {\n    return q.options.find((o) => o.id === optionId)?.text ?? ''\n  }\n\n  toggleFlag(questionId: number): void {\n    this.flagged.update((prev) => {\n      const next = new Set(prev)\n      if (next.has(questionId)) next.delete(questionId)\n      else next.add(questionId)\n      return next\n    })\n  }\n\n  selectOption(optionId: string): void {\n    const qid = this.currentQuestion().id\n    this.answers.update((prev) => ({ ...prev, [qid]: optionId }))\n  }\n\n  goToQuestion(index: number): void {\n    this.currentQuestionIndex.set(Math.max(0, Math.min(index, this.totalQuestions - 1)))\n  }\n\n  prevQuestion(): void {\n    if (this.currentQuestionIndex() > 0) this.currentQuestionIndex.update((i) => i - 1)\n  }\n\n  nextQuestion(): void {\n    if (this.currentQuestionIndex() < this.totalQuestions - 1) this.currentQuestionIndex.update((i) => i + 1)\n    else this.isSubmitDialogOpen.set(true)\n  }\n\n  submitExam(): void {\n    this.isSubmitted.set(true)\n    this.isSubmitDialogOpen.set(false)\n    this.clearTimer()\n  }\n\n  retakeQuiz(): void {\n    this.answers.set({})\n    this.flagged.set(new Set())\n    this.timeRemaining.set(this.initialBudget)\n    this.currentQuestionIndex.set(0)\n    this.isSubmitted.set(false)\n    this.reviewFilter.set('all')\n    this.clearTimer()\n    this.startTimer()\n  }\n\n  confirmExit(): void {\n    this.isExitDialogOpen.set(false)\n    this.retakeQuiz()\n  }\n\n  /** Decrement the clock once — extracted so specs can drive time without fake timers. */\n  tick(): void {\n    if (this.timeRemaining() > 0) this.timeRemaining.update((t) => t - 1)\n    else if (!this.isSubmitted()) {\n      this.isSubmitted.set(true)\n      this.isSubmitDialogOpen.set(false)\n    }\n  }\n\n  private startTimer(): void {\n    this.clearTimer()\n    if (this.isSubmitted()) return\n    this.timer = setInterval(() => this.tick(), 1000)\n  }\n\n  private clearTimer(): void {\n    if (this.timer) {\n      clearInterval(this.timer)\n      this.timer = null\n    }\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/quiz-assessment-runner/quiz-assessment-runner.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/quiz-assessment-runner/index.ts",
      "content": "export {\n  UiQuizAssessmentRunnerComponent,\n  DEFAULT_QUESTIONS,\n  type QuizOption,\n  type QuizQuestion,\n  type ReviewFilter,\n} from './quiz-assessment-runner.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/quiz-assessment-runner/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/dialog.json",
    "https://uipkge.dev/r/angular/progress.json",
    "https://uipkge.dev/r/angular/separator.json"
  ],
  "description": "Interactive multiple-choice exam & quiz assessment runner: 2-column test canvas with real-time countdown timer, question navigator grid with status indicators (completed, active, flagged, unanswered), syntax-highlighted code snippets, single-choice selection cards, submit confirmation modal, and comprehensive score breakdown with review explanations.",
  "categories": [
    "education",
    "app"
  ]
}