{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "lesson-video-player",
  "title": "Lesson Video Player",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/lesson-video-player/LessonVideoPlayer.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, onUnmounted, ref } from 'vue'\nimport {\n  ArrowRight,\n  Bookmark,\n  BookmarkCheck,\n  Check,\n  CheckCircle2,\n  ChevronRight,\n  Clock,\n  Download,\n  FileCode2,\n  FileText,\n  Layers,\n  Lightbulb,\n  Maximize2,\n  MessageSquare,\n  MessageSquareQuote,\n  Minimize2,\n  Pause,\n  Play,\n  Plus,\n  RotateCcw,\n  RotateCw,\n  Search,\n  Subtitles,\n  ThumbsUp,\n  Trash2,\n  Volume1,\n  Volume2,\n  VolumeX,\n} from 'lucide-vue-next'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Input } from '@/components/ui/input'\nimport { Progress } from '@/components/ui/progress'\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'\nimport { Textarea } from '@/components/ui/textarea'\n\n// Video Playback State\nconst isPlaying = ref(false)\nconst currentTime = ref(384) // 06:24\nconst totalDuration = ref(1120) // 18:40\nconst bufferedPercent = ref(78)\nconst volume = ref(85)\nconst isMuted = ref(false)\nconst playbackSpeed = ref(1.25)\nconst speedOptions = [0.75, 1.0, 1.25, 1.5, 1.75, 2.0]\nconst showCaptions = ref(true)\nconst isFullscreen = ref(false)\nconst activeTab = ref<'transcript' | 'notes' | 'qa'>('transcript')\nconst transcriptSearch = ref('')\nconst isCompleted = ref(false)\nconst isBookmarked = ref(false)\nconst copiedAsset = ref<string | null>(null)\n\n// Timer ticker when video is playing\nlet playInterval: ReturnType<typeof setInterval> | null = null\n\nfunction togglePlay() {\n  isPlaying.value = !isPlaying.value\n  if (isPlaying.value) {\n    playInterval = setInterval(() => {\n      if (currentTime.value < totalDuration.value) {\n        currentTime.value += 1\n      } else {\n        isPlaying.value = false\n        if (playInterval) clearInterval(playInterval)\n      }\n    }, 1000 / playbackSpeed.value)\n  } else if (playInterval) {\n    clearInterval(playInterval)\n  }\n}\n\nonUnmounted(() => {\n  if (playInterval) clearInterval(playInterval)\n})\n\nfunction formatTime(seconds: number): string {\n  const m = Math.floor(seconds / 60)\n  const s = Math.floor(seconds % 60)\n  return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`\n}\n\nconst formattedCurrentTime = computed(() => formatTime(currentTime.value))\nconst formattedTotalTime = computed(() => formatTime(totalDuration.value))\nconst progressPercent = computed(() => (currentTime.value / totalDuration.value) * 100)\n\nfunction seekTo(seconds: number) {\n  currentTime.value = Math.max(0, Math.min(seconds, totalDuration.value))\n}\n\nfunction handleSeekClick(event: MouseEvent) {\n  const target = event.currentTarget as HTMLElement\n  const rect = target.getBoundingClientRect()\n  const clickX = event.clientX - rect.left\n  const ratio = Math.max(0, Math.min(1, clickX / rect.width))\n  seekTo(Math.round(ratio * totalDuration.value))\n}\n\nfunction skipSeconds(delta: number) {\n  seekTo(currentTime.value + delta)\n}\n\nfunction cycleSpeed() {\n  const currentIndex = speedOptions.indexOf(playbackSpeed.value)\n  const nextIndex = (currentIndex + 1) % speedOptions.length\n  playbackSpeed.value = speedOptions[nextIndex]\n  if (isPlaying.value) {\n    // Restart interval with new speed\n    if (playInterval) clearInterval(playInterval)\n    playInterval = setInterval(() => {\n      if (currentTime.value < totalDuration.value) {\n        currentTime.value += 1\n      } else {\n        isPlaying.value = false\n        if (playInterval) clearInterval(playInterval)\n      }\n    }, 1000 / playbackSpeed.value)\n  }\n}\n\nfunction toggleMute() {\n  isMuted.value = !isMuted.value\n}\n\n// Transcript Data\ninterface TranscriptItem {\n  id: string\n  start: number\n  end: number\n  speaker: string\n  text: string\n}\n\nconst transcriptItems = ref<TranscriptItem[]>([\n  {\n    id: 't-1',\n    start: 0,\n    end: 75,\n    speaker: 'Sarah Connor',\n    text: 'Welcome back! In this lesson, we are tackling one of the trickiest parts of design systems: building resilient dark-mode token palettes using the OKLCH color space.',\n  },\n  {\n    id: 't-2',\n    start: 75,\n    end: 220,\n    speaker: 'Sarah Connor',\n    text: 'Why do traditional HSL and sRGB color models fall short? Because perceptual brightness is non-uniform — pure blue at 50% lightness looks vastly darker to the human eye than pure yellow at 50% lightness.',\n  },\n  {\n    id: 't-3',\n    start: 220,\n    end: 384,\n    speaker: 'Sarah Connor',\n    text: 'OKLCH solves this by decoupling perceived lightness (L) from chroma (C) and hue (H). When you step lightness down by 10%, the perceived luminance decreases identically regardless of hue angle.',\n  },\n  {\n    id: 't-4',\n    start: 384,\n    end: 555,\n    speaker: 'Sarah Connor',\n    text: 'When calculating contrast in OKLCH, lightness is perceptually uniform across hues, which prevents dark-mode contrast crushing. Notice our formula on the slide.',\n  },\n  {\n    id: 't-5',\n    start: 555,\n    end: 750,\n    speaker: 'Sarah Connor',\n    text: 'Next, let us discuss wide-gamut Display P3 displays. While P3 provides 25% richer saturation, we must ensure automatic fallback clamping for legacy sRGB monitors.',\n  },\n  {\n    id: 't-6',\n    start: 750,\n    end: 945,\n    speaker: 'Sarah Connor',\n    text: 'In dark mode, human retinas are more sensitive to intense chroma saturation on dark surfaces. A solid heuristic is to reduce chroma by 15% to 20% on OLED dark backgrounds.',\n  },\n  {\n    id: 't-7',\n    start: 945,\n    end: 1120,\n    speaker: 'Sarah Connor',\n    text: 'To wrap up, download the starter configuration below. In the next lesson, we will wire these token variables directly into our Tailwind CSS v4 @theme inline directives.',\n  },\n])\n\nconst activeTranscriptItem = computed(() => {\n  return (\n    transcriptItems.value.find((item) => currentTime.value >= item.start && currentTime.value < item.end) ||\n    transcriptItems.value[0]\n  )\n})\n\nconst filteredTranscript = computed(() => {\n  const query = transcriptSearch.value.trim().toLowerCase()\n  if (!query) return transcriptItems.value\n  return transcriptItems.value.filter(\n    (item) => item.text.toLowerCase().includes(query) || item.speaker.toLowerCase().includes(query),\n  )\n})\n\n// Notes Data\ninterface NoteItem {\n  id: string\n  timestamp: number\n  text: string\n  createdAt: string\n}\n\nconst newNoteText = ref('')\nconst notesList = ref<NoteItem[]>([\n  {\n    id: 'n-1',\n    timestamp: 102, // 01:42\n    text: 'Pure blue vs pure yellow in HSL: huge luminance mismatch. OKLCH fixes this at the root.',\n    createdAt: '2 hours ago',\n  },\n  {\n    id: 'n-2',\n    timestamp: 252, // 04:12\n    text: 'Review color-mix in oklab formulas — can generate surface elevation tints with a single token!',\n    createdAt: '1 hour ago',\n  },\n  {\n    id: 'n-3',\n    timestamp: 384, // 06:24\n    text: 'Lightness rule: Keep L >= 0.70 for primary brand text on dark surfaces to guarantee APCA Lc 60 minimum.',\n    createdAt: 'Just now',\n  },\n])\n\nfunction handleAddNote() {\n  const text = newNoteText.value.trim()\n  if (!text) return\n  notesList.value.unshift({\n    id: `note-${Date.now()}`,\n    timestamp: currentTime.value,\n    text,\n    createdAt: 'Just now',\n  })\n  newNoteText.value = ''\n}\n\nfunction handleDeleteNote(id: string) {\n  notesList.value = notesList.value.filter((n) => n.id !== id)\n}\n\n// Q&A Data\ninterface QuestionItem {\n  id: string\n  timestamp: number\n  author: string\n  avatar: string\n  question: string\n  upvotes: number\n  hasUpvoted?: boolean\n  answer?: {\n    author: string\n    role: string\n    text: string\n    isInstructor?: boolean\n  }\n}\n\nconst newQuestionText = ref('')\nconst questionsList = ref<QuestionItem[]>([\n  {\n    id: 'q-1',\n    timestamp: 225, // 03:45\n    author: 'David K.',\n    avatar: 'DK',\n    question: 'Is browser support for OKLCH color-mix and raw oklch() ready for enterprise SaaS applications?',\n    upvotes: 18,\n    hasUpvoted: false,\n    answer: {\n      author: 'Sarah Connor',\n      role: 'Instructor',\n      isInstructor: true,\n      text: 'Yes! OKLCH has 98%+ global browser support across all evergreen browsers. Tailwind CSS v4 uses it as the default token format natively.',\n    },\n  },\n  {\n    id: 'q-2',\n    timestamp: 380, // 06:20\n    author: 'Elena R.',\n    avatar: 'ER',\n    question: 'How do you prevent high chroma vibration on dark OLED zinc-950 surfaces?',\n    upvotes: 12,\n    hasUpvoted: false,\n    answer: {\n      author: 'Sarah Connor',\n      role: 'Instructor',\n      isInstructor: true,\n      text: 'Scale down chroma by multiplying C * 0.82 on dark tokens, while boosting lightness by +0.08 to preserve legibility without glare.',\n    },\n  },\n])\n\nfunction handleAddQuestion() {\n  const text = newQuestionText.value.trim()\n  if (!text) return\n  questionsList.value.unshift({\n    id: `q-${Date.now()}`,\n    timestamp: currentTime.value,\n    author: 'You (Student)',\n    avatar: 'ME',\n    question: text,\n    upvotes: 1,\n    hasUpvoted: true,\n  })\n  newQuestionText.value = ''\n}\n\nfunction toggleUpvote(q: QuestionItem) {\n  if (q.hasUpvoted) {\n    q.upvotes -= 1\n    q.hasUpvoted = false\n  } else {\n    q.upvotes += 1\n    q.hasUpvoted = true\n  }\n}\n\nfunction handleCopyAsset(name: string) {\n  copiedAsset.value = name\n  setTimeout(() => {\n    copiedAsset.value = null\n  }, 2000)\n}\n</script>\n\n<template>\n  <div data-slot=\"lesson-video-player\" class=\"bg-background text-foreground w-full space-y-5\">\n    <!-- Top Lesson Navigation & Course Header Bar -->\n    <header class=\"bg-card rounded-xl border p-4 shadow-xs sm:px-6 sm:py-4\">\n      <div class=\"flex flex-col gap-3 md:flex-row md:items-center md:justify-between\">\n        <!-- Left: Course Context & Lesson Title -->\n        <div class=\"space-y-1.5\">\n          <div class=\"flex flex-wrap items-center gap-2 text-xs\">\n            <span class=\"text-muted-foreground font-medium\">Design Systems with Tailwind CSS v4</span>\n            <ChevronRight class=\"text-muted-foreground size-3.5\" />\n            <Badge variant=\"outline\" class=\"gap-1 text-xs font-semibold\">\n              <Layers class=\"text-primary size-3\" />\n              <span>Module 1: Design Tokens Architecture</span>\n            </Badge>\n            <span class=\"text-muted-foreground hidden font-medium sm:inline\">Lesson 4 of 12</span>\n          </div>\n\n          <h1 class=\"text-foreground text-lg font-bold tracking-tight sm:text-xl\">\n            Lesson 4: Building Resilient OKLCH Dark-Mode Token Palettes\n          </h1>\n        </div>\n\n        <!-- Right: Progress Indicator & Navigation Buttons -->\n        <div class=\"flex flex-wrap items-center gap-2.5\">\n          <!-- Course Progress Pill -->\n          <div class=\"bg-muted/40 hidden items-center gap-2 rounded-lg border px-3 py-1.5 sm:flex\">\n            <div class=\"space-y-1 text-right\">\n              <div class=\"text-muted-foreground text-xs font-medium\">Module Progress</div>\n              <div class=\"font-mono text-xs font-bold tabular-nums\">4/12 (33%)</div>\n            </div>\n            <div class=\"w-12\">\n              <Progress :model-value=\"33\" class=\"h-1.5\" />\n            </div>\n          </div>\n\n          <!-- Bookmark Button -->\n          <Button\n            variant=\"outline\"\n            size=\"icon\"\n            class=\"size-8.5\"\n            :aria-label=\"isBookmarked ? 'Remove bookmark' : 'Bookmark lesson'\"\n            @click=\"isBookmarked = !isBookmarked\"\n          >\n            <BookmarkCheck v-if=\"isBookmarked\" class=\"text-primary size-4\" />\n            <Bookmark v-else class=\"text-muted-foreground size-4\" />\n          </Button>\n\n          <!-- Complete Lesson Button -->\n          <Button\n            :variant=\"isCompleted ? 'default' : 'outline'\"\n            size=\"sm\"\n            class=\"h-8.5 gap-1.5 text-xs font-semibold shadow-xs\"\n            @click=\"isCompleted = !isCompleted\"\n          >\n            <CheckCircle2 v-if=\"isCompleted\" class=\"size-3.5\" />\n            <Check v-else class=\"size-3.5\" />\n            <span>{{ isCompleted ? 'Completed' : 'Mark Complete' }}</span>\n          </Button>\n\n          <!-- Next Lesson Button -->\n          <Button variant=\"default\" size=\"sm\" class=\"h-8.5 gap-1.5 text-xs font-semibold shadow-xs\">\n            <span>Next Lesson</span>\n            <ArrowRight class=\"size-3.5\" />\n          </Button>\n        </div>\n      </div>\n    </header>\n\n    <!-- 2-Column Classroom Layout -->\n    <div class=\"grid grid-cols-1 gap-6 lg:grid-cols-12\">\n      <!-- Left Column: Video Player Canvas + Takeaways & Downloads (8 cols) -->\n      <section class=\"space-y-5 lg:col-span-8\">\n        <!-- Video Screen Container (Dark Video Canvas) -->\n        <div\n          class=\"group relative flex min-h-[260px] w-full flex-col justify-between overflow-hidden rounded-xl border border-zinc-800 bg-zinc-950 text-white shadow-lg select-none\"\n        >\n          <!-- Top Video Overlay Info Bar -->\n          <div class=\"z-20 flex items-center justify-between p-3.5 sm:p-4\">\n            <div class=\"flex items-center gap-2\">\n              <Badge\n                variant=\"outline\"\n                class=\"border-zinc-700 bg-zinc-900/80 px-2 py-0.5 font-mono text-xs font-medium text-zinc-300 backdrop-blur-md\"\n              >\n                1080p 60fps HD\n              </Badge>\n              <Badge\n                variant=\"outline\"\n                class=\"border-primary/40 bg-primary/20 text-primary-foreground px-2 py-0.5 text-xs font-semibold backdrop-blur-md\"\n              >\n                Lesson 4 / 12\n              </Badge>\n            </div>\n\n            <!-- Instructor Watermark -->\n            <div\n              class=\"flex items-center gap-2 rounded-full border border-zinc-800 bg-zinc-900/80 px-3 py-1 text-xs text-zinc-300 backdrop-blur-md\"\n            >\n              <span class=\"bg-success size-2 rounded-full\" />\n              <span class=\"font-medium\">Sarah Connor</span>\n              <span class=\"text-muted-foreground\">·</span>\n              <span class=\"text-muted-foreground\">Principal Design Engineer</span>\n            </div>\n          </div>\n\n          <!-- Video Presentation Slide & Diagram Visual -->\n          <div class=\"relative my-auto flex flex-col items-center justify-center px-6 py-4 text-center\">\n            <!-- Simulated Code & Color Token Slide Matrix -->\n            <div\n              class=\"relative w-full max-w-xl space-y-3 rounded-lg border border-zinc-800/80 bg-zinc-900/85 p-4 text-left shadow-sm backdrop-blur-md\"\n            >\n              <div class=\"flex items-center justify-between border-b border-zinc-800 pb-2\">\n                <div class=\"flex items-center gap-2\">\n                  <div class=\"flex gap-1.5\">\n                    <span class=\"bg-destructive/80 size-2.5 rounded-full\" />\n                    <span class=\"bg-warning/80 size-2.5 rounded-full\" />\n                    <span class=\"bg-success/80 size-2.5 rounded-full\" />\n                  </div>\n                  <span class=\"text-muted-foreground font-mono text-xs\">tokens.config.css — OKLCH Palette Engine</span>\n                </div>\n                <Badge variant=\"outline\" class=\"border-success/40 bg-success/10 text-success font-mono text-xs\">\n                  APCA Lc 74 (AAA)\n                </Badge>\n              </div>\n\n              <!-- Code Syntax Sample -->\n              <pre\n                class=\"overflow-x-auto font-mono text-xs leading-relaxed text-zinc-300\"\n              ><code><span class=\"text-muted-foreground\">/* OKLCH Perceptual Uniformity Matrix */</span>\n<span class=\"text-chart-1\">@theme inline</span> {\n  <span class=\"text-info\">--color-primary</span>: <span class=\"text-warning\">oklch(0.62 0.19 259.8)</span>; <span class=\"text-muted-foreground\">/* Brand Base */</span>\n  <span class=\"text-info\">--color-surface-dark</span>: <span class=\"text-warning\">oklch(0.18 0.02 260.0)</span>; <span class=\"text-muted-foreground\">/* OLED Safe */</span>\n}</code></pre>\n\n              <!-- Color Palette Swatch Strip -->\n              <div class=\"flex items-center gap-2 pt-1\">\n                <span class=\"text-muted-foreground text-xs font-medium\">Lightness Ramp:</span>\n                <div class=\"flex flex-1 items-center gap-1.5\">\n                  <div class=\"h-5 flex-1 rounded bg-[oklch(0.95_0.04_259.8)] shadow-xs\" title=\"95% L\" />\n                  <div class=\"h-5 flex-1 rounded bg-[oklch(0.80_0.10_259.8)] shadow-xs\" title=\"80% L\" />\n                  <div\n                    class=\"h-5 flex-1 rounded bg-[oklch(0.62_0.19_259.8)] shadow-xs ring-1 ring-white/60\"\n                    title=\"62% L (Base)\"\n                  />\n                  <div class=\"h-5 flex-1 rounded bg-[oklch(0.40_0.15_259.8)] shadow-xs\" title=\"40% L\" />\n                  <div class=\"h-5 flex-1 rounded bg-[oklch(0.18_0.02_259.8)] shadow-xs\" title=\"18% L (Dark)\" />\n                </div>\n              </div>\n            </div>\n\n            <!-- Big Center Play / Pause Floating Button -->\n            <button\n              type=\"button\"\n              class=\"bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-ring absolute inset-0 m-auto flex size-14 cursor-pointer items-center justify-center rounded-full shadow-lg transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n              :aria-label=\"isPlaying ? 'Pause video' : 'Play video'\"\n              @click=\"togglePlay\"\n            >\n              <Pause v-if=\"isPlaying\" class=\"size-6 fill-current\" />\n              <Play v-else class=\"ml-0.5 size-6 fill-current\" />\n            </button>\n          </div>\n\n          <!-- Captions Subtitles Overlay Box -->\n          <div\n            v-if=\"showCaptions && activeTranscriptItem\"\n            class=\"z-20 mx-auto mb-1 max-w-xl rounded-lg border border-white/10 bg-black/80 px-4 py-1.5 text-center text-xs font-medium text-zinc-200 shadow-md backdrop-blur-md sm:text-sm\"\n          >\n            <span class=\"text-muted-foreground\">[{{ formatTime(activeTranscriptItem.start) }}]</span>\n            {{ activeTranscriptItem.text }}\n          </div>\n\n          <!-- Custom Player Controls Bar (Bottom Overlay) -->\n          <div\n            class=\"z-20 space-y-2 bg-gradient-to-t from-black/95 via-black/85 to-transparent p-3 sm:px-4 sm:pt-4 sm:pb-3.5\"\n          >\n            <!-- Scrubber Timeline Bar -->\n            <div\n              class=\"group/bar relative flex h-4 w-full cursor-pointer items-center\"\n              role=\"slider\"\n              aria-label=\"Video Timeline Scrubber\"\n              :aria-valuenow=\"currentTime\"\n              :aria-valuemin=\"0\"\n              :aria-valuemax=\"totalDuration\"\n              @click=\"handleSeekClick\"\n            >\n              <!-- Background Full Track -->\n              <div\n                class=\"relative h-1.5 w-full overflow-hidden rounded-full bg-zinc-800 transition-[height] group-hover/bar:h-2.5\"\n              >\n                <!-- Buffer Progress -->\n                <div\n                  class=\"absolute top-0 bottom-0 left-0 bg-zinc-700 transition-colors\"\n                  :style=\"{ width: `${bufferedPercent}%` }\"\n                />\n                <!-- Played Progress -->\n                <div\n                  class=\"bg-primary absolute top-0 bottom-0 left-0 transition-colors\"\n                  :style=\"{ width: `${progressPercent}%` }\"\n                />\n              </div>\n\n              <!-- Scrubber Dot Handle -->\n              <div\n                class=\"bg-primary absolute size-3.5 -translate-x-1/2 rounded-full shadow-md ring-2 ring-white transition-transform group-hover/bar:scale-125\"\n                :style=\"{ left: `${progressPercent}%` }\"\n              />\n            </div>\n\n            <!-- Controls Row: Play, Skip, Timestamps, Speed, Audio, Screen -->\n            <div class=\"flex items-center justify-between gap-2 pt-0.5\">\n              <!-- Left Controls -->\n              <div class=\"flex items-center gap-1 sm:gap-2\">\n                <!-- Play / Pause -->\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  class=\"size-8 text-white hover:bg-white/15\"\n                  :aria-label=\"isPlaying ? 'Pause' : 'Play'\"\n                  @click=\"togglePlay\"\n                >\n                  <Pause v-if=\"isPlaying\" class=\"size-4.5 fill-current\" />\n                  <Play v-else class=\"size-4.5 fill-current\" />\n                </Button>\n\n                <!-- 15s Rewind -->\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  class=\"size-8 text-zinc-300 hover:bg-white/15 hover:text-white\"\n                  aria-label=\"Rewind 15 seconds\"\n                  @click=\"skipSeconds(-15)\"\n                >\n                  <RotateCcw class=\"size-4\" />\n                </Button>\n\n                <!-- 15s Fast Forward -->\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  class=\"size-8 text-zinc-300 hover:bg-white/15 hover:text-white\"\n                  aria-label=\"Fast forward 15 seconds\"\n                  @click=\"skipSeconds(15)\"\n                >\n                  <RotateCw class=\"size-4\" />\n                </Button>\n\n                <!-- Volume Toggle -->\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  class=\"size-8 text-zinc-300 hover:bg-white/15 hover:text-white\"\n                  :aria-label=\"isMuted ? 'Unmute' : 'Mute'\"\n                  @click=\"toggleMute\"\n                >\n                  <VolumeX v-if=\"isMuted || volume === 0\" class=\"text-destructive size-4\" />\n                  <Volume1 v-else-if=\"volume < 50\" class=\"size-4\" />\n                  <Volume2 v-else class=\"size-4\" />\n                </Button>\n\n                <!-- Timestamp Display -->\n                <div class=\"flex items-center gap-1 font-mono text-xs font-semibold text-zinc-300 tabular-nums sm:ml-1\">\n                  <span class=\"text-white\">{{ formattedCurrentTime }}</span>\n                  <span class=\"text-muted-foreground\">/</span>\n                  <span class=\"text-muted-foreground\">{{ formattedTotalTime }}</span>\n                </div>\n              </div>\n\n              <!-- Right Controls -->\n              <div class=\"flex items-center gap-1 sm:gap-2\">\n                <!-- Speed Selector Button -->\n                <Button\n                  variant=\"ghost\"\n                  size=\"sm\"\n                  class=\"h-7 px-2 font-mono text-xs font-bold text-zinc-300 hover:bg-white/15 hover:text-white\"\n                  aria-label=\"Cycle Playback Speed\"\n                  @click=\"cycleSpeed\"\n                >\n                  {{ playbackSpeed }}x\n                </Button>\n\n                <!-- Captions Subtitles Toggle -->\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  :class=\"[\n                    'size-8',\n                    showCaptions\n                      ? 'text-primary hover:bg-white/15'\n                      : 'text-muted-foreground hover:bg-white/15 hover:text-white',\n                  ]\"\n                  :aria-label=\"showCaptions ? 'Hide Subtitles' : 'Show Subtitles'\"\n                  @click=\"showCaptions = !showCaptions\"\n                >\n                  <Subtitles class=\"size-4\" />\n                </Button>\n\n                <!-- Fullscreen Toggle -->\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  class=\"size-8 text-zinc-300 hover:bg-white/15 hover:text-white\"\n                  :aria-label=\"isFullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'\"\n                  @click=\"isFullscreen = !isFullscreen\"\n                >\n                  <Minimize2 v-if=\"isFullscreen\" class=\"size-4\" />\n                  <Maximize2 v-else class=\"size-4\" />\n                </Button>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <!-- Lesson Key Takeaways & Downloadable Assets Row -->\n        <div class=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n          <!-- Key Takeaways Card -->\n          <Card class=\"shadow-xs\">\n            <CardHeader class=\"pb-2.5\">\n              <div class=\"flex items-center justify-between\">\n                <CardTitle class=\"text-foreground flex items-center gap-1.5 text-xs font-bold tracking-tight uppercase\">\n                  <Lightbulb class=\"text-primary size-3.5\" />\n                  Key Takeaways\n                </CardTitle>\n                <Badge variant=\"outline\" class=\"font-mono text-xs\">OKLCH v4</Badge>\n              </div>\n              <CardDescription class=\"text-xs\">\n                Essential architectural concepts taught in this lecture\n              </CardDescription>\n            </CardHeader>\n            <CardContent class=\"space-y-2.5 text-xs\">\n              <div class=\"bg-muted/30 flex items-start gap-2.5 rounded-lg border p-2.5\">\n                <div\n                  class=\"bg-primary/10 text-primary mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full text-xs font-bold\"\n                >\n                  1\n                </div>\n                <div>\n                  <span class=\"text-foreground font-semibold\">Perceptual Uniformity:</span>\n                  <p class=\"text-muted-foreground mt-0.5 leading-relaxed\">\n                    OKLCH lightness remains constant across all hues, maintaining predictable contrast ratios across\n                    both dark and light palettes.\n                  </p>\n                </div>\n              </div>\n\n              <div class=\"bg-muted/30 flex items-start gap-2.5 rounded-lg border p-2.5\">\n                <div\n                  class=\"bg-primary/10 text-primary mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full text-xs font-bold\"\n                >\n                  2\n                </div>\n                <div>\n                  <span class=\"text-foreground font-semibold\">Display P3 Gamut Clamping:</span>\n                  <p class=\"text-muted-foreground mt-0.5 leading-relaxed\">\n                    Leverage vibrant P3 color gamuts on supported displays while automatically fallback-clamping for\n                    sRGB displays.\n                  </p>\n                </div>\n              </div>\n\n              <div class=\"bg-muted/30 flex items-start gap-2.5 rounded-lg border p-2.5\">\n                <div\n                  class=\"bg-primary/10 text-primary mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full text-xs font-bold\"\n                >\n                  3\n                </div>\n                <div>\n                  <span class=\"text-foreground font-semibold\">Chroma Scaling in Dark Mode:</span>\n                  <p class=\"text-muted-foreground mt-0.5 leading-relaxed\">\n                    Scale down chroma by 15-20% on deep dark backgrounds to prevent ocular fatigue and visual vibration.\n                  </p>\n                </div>\n              </div>\n            </CardContent>\n          </Card>\n\n          <!-- Downloadable Source Code & Assets Card -->\n          <Card class=\"shadow-xs\">\n            <CardHeader class=\"pb-2.5\">\n              <div class=\"flex items-center justify-between\">\n                <CardTitle class=\"text-foreground flex items-center gap-1.5 text-xs font-bold tracking-tight uppercase\">\n                  <FileCode2 class=\"text-primary size-3.5\" />\n                  Lesson Assets & Code\n                </CardTitle>\n                <Badge variant=\"secondary\" class=\"text-xs\">3 Files</Badge>\n              </div>\n              <CardDescription class=\"text-xs\"> Starter boilerplate and token configuration files </CardDescription>\n            </CardHeader>\n            <CardContent class=\"space-y-2 text-xs\">\n              <!-- Asset 1 -->\n              <div\n                class=\"bg-muted/30 hover:bg-muted/50 flex items-center justify-between rounded-lg border p-2.5 transition-colors\"\n              >\n                <div class=\"flex min-w-0 items-center gap-2.5\">\n                  <div\n                    class=\"bg-primary/10 text-primary flex size-8 shrink-0 items-center justify-center rounded-md font-mono text-xs font-bold\"\n                  >\n                    TS\n                  </div>\n                  <div class=\"min-w-0\">\n                    <p class=\"text-foreground truncate text-xs font-semibold\">tokens.config.ts</p>\n                    <p class=\"text-muted-foreground font-mono text-xs\">14.2 KB · Tailwind v4 Theme</p>\n                  </div>\n                </div>\n                <Button\n                  aria-label=\"Download attachment\"\n                  variant=\"outline\"\n                  size=\"sm\"\n                  class=\"h-7 gap-1 px-2 text-xs\"\n                  @click=\"handleCopyAsset('tokens.config.ts')\"\n                >\n                  <Check v-if=\"copiedAsset === 'tokens.config.ts'\" class=\"text-success size-3\" />\n                  <Download v-else class=\"size-3\" />\n                  <span>{{ copiedAsset === 'tokens.config.ts' ? 'Saved' : 'Download' }}</span>\n                </Button>\n              </div>\n\n              <!-- Asset 2 -->\n              <div\n                class=\"bg-muted/30 hover:bg-muted/50 flex items-center justify-between rounded-lg border p-2.5 transition-colors\"\n              >\n                <div class=\"flex min-w-0 items-center gap-2.5\">\n                  <div\n                    class=\"bg-chart-2/10 text-chart-2 flex size-8 shrink-0 items-center justify-center rounded-md font-mono text-xs font-bold\"\n                  >\n                    FIG\n                  </div>\n                  <div class=\"min-w-0\">\n                    <p class=\"text-foreground truncate text-xs font-semibold\">palette-matrix.fig</p>\n                    <p class=\"text-muted-foreground font-mono text-xs\">4.8 MB · Token Library</p>\n                  </div>\n                </div>\n                <Button\n                  aria-label=\"Download attachment\"\n                  variant=\"outline\"\n                  size=\"sm\"\n                  class=\"h-7 gap-1 px-2 text-xs\"\n                  @click=\"handleCopyAsset('palette-matrix.fig')\"\n                >\n                  <Check v-if=\"copiedAsset === 'palette-matrix.fig'\" class=\"text-success size-3\" />\n                  <Download v-else class=\"size-3\" />\n                  <span>{{ copiedAsset === 'palette-matrix.fig' ? 'Saved' : 'Download' }}</span>\n                </Button>\n              </div>\n\n              <!-- Asset 3 -->\n              <div\n                class=\"bg-muted/30 hover:bg-muted/50 flex items-center justify-between rounded-lg border p-2.5 transition-colors\"\n              >\n                <div class=\"flex min-w-0 items-center gap-2.5\">\n                  <div\n                    class=\"bg-destructive/10 text-destructive flex size-8 shrink-0 items-center justify-center rounded-md font-mono text-xs font-bold\"\n                  >\n                    PDF\n                  </div>\n                  <div class=\"min-w-0\">\n                    <p class=\"text-foreground truncate text-xs font-semibold\">apca-contrast-guide.pdf</p>\n                    <p class=\"text-muted-foreground font-mono text-xs\">1.1 MB · Cheat Sheet</p>\n                  </div>\n                </div>\n                <Button\n                  aria-label=\"Download attachment\"\n                  variant=\"outline\"\n                  size=\"sm\"\n                  class=\"h-7 gap-1 px-2 text-xs\"\n                  @click=\"handleCopyAsset('apca-contrast-guide.pdf')\"\n                >\n                  <Check v-if=\"copiedAsset === 'apca-contrast-guide.pdf'\" class=\"text-success size-3\" />\n                  <Download v-else class=\"size-3\" />\n                  <span>{{ copiedAsset === 'apca-contrast-guide.pdf' ? 'Saved' : 'Download' }}</span>\n                </Button>\n              </div>\n            </CardContent>\n          </Card>\n        </div>\n      </section>\n\n      <!-- Right Column: Interactive Transcript & Notes Workspace (4 cols) -->\n      <aside class=\"flex flex-col lg:col-span-4\">\n        <Card class=\"flex h-full flex-col overflow-hidden shadow-xs\">\n          <Tabs v-model=\"activeTab\" class=\"flex h-full flex-col\">\n            <!-- Tabs Navigation Header -->\n            <div class=\"bg-muted/30 border-b p-2.5\">\n              <TabsList class=\"grid h-8.5 w-full grid-cols-3\">\n                <TabsTrigger value=\"transcript\" class=\"gap-1 text-xs font-medium\">\n                  <FileText class=\"size-3\" />\n                  <span>Transcript</span>\n                </TabsTrigger>\n                <TabsTrigger value=\"notes\" class=\"gap-1 text-xs font-medium\">\n                  <BookmarkCheck class=\"size-3\" />\n                  <span>Notes</span>\n                  <Badge variant=\"secondary\" class=\"ml-0.5 px-1 py-0 text-xs\">\n                    {{ notesList.length }}\n                  </Badge>\n                </TabsTrigger>\n                <TabsTrigger value=\"qa\" class=\"gap-1 text-xs font-medium\">\n                  <MessageSquareQuote class=\"size-3\" />\n                  <span>Q&A</span>\n                  <Badge variant=\"secondary\" class=\"ml-0.5 px-1 py-0 text-xs\">\n                    {{ questionsList.length }}\n                  </Badge>\n                </TabsTrigger>\n              </TabsList>\n            </div>\n\n            <!-- TAB 1: Interactive Transcript -->\n            <TabsContent value=\"transcript\" class=\"m-0 flex flex-1 flex-col focus-visible:outline-none\">\n              <!-- Search Transcript Filter -->\n              <div class=\"border-b p-3\">\n                <div class=\"relative\">\n                  <Search class=\"text-muted-foreground absolute top-2.5 left-2.5 size-3.5\" />\n                  <Input\n                    v-model=\"transcriptSearch\"\n                    placeholder=\"Search lecture transcript...\"\n                    class=\"h-8 pl-8 text-xs\"\n                  />\n                </div>\n              </div>\n\n              <!-- Transcript Segments List -->\n              <div class=\"max-h-[580px] flex-1 space-y-2.5 overflow-y-auto p-3.5\">\n                <div\n                  v-for=\"item in filteredTranscript\"\n                  :key=\"item.id\"\n                  :class=\"[\n                    'group cursor-pointer rounded-lg border p-3 text-xs transition-colors duration-200',\n                    currentTime >= item.start && currentTime < item.end\n                      ? 'border-primary/50 bg-primary/10 ring-primary/20 shadow-xs ring-1'\n                      : 'border-border/70 bg-card hover:bg-muted/40',\n                  ]\"\n                  @click=\"seekTo(item.start)\"\n                >\n                  <div class=\"mb-1.5 flex items-center justify-between\">\n                    <div class=\"flex items-center gap-1.5\">\n                      <Button\n                        variant=\"secondary\"\n                        size=\"sm\"\n                        class=\"h-6 gap-1 rounded-md px-1.5 font-mono text-xs font-bold tabular-nums\"\n                        @click.stop=\"seekTo(item.start)\"\n                      >\n                        <Play class=\"size-2.5 fill-current\" />\n                        <span>{{ formatTime(item.start) }}</span>\n                      </Button>\n                      <span class=\"text-muted-foreground text-xs font-medium\">{{ item.speaker }}</span>\n                    </div>\n\n                    <Badge\n                      v-if=\"currentTime >= item.start && currentTime < item.end\"\n                      variant=\"default\"\n                      class=\"gap-1 px-1.5 py-0 text-xs font-semibold\"\n                    >\n                      <span class=\"size-1.5 animate-pulse rounded-full bg-white\" />\n                      <span>Playing</span>\n                    </Badge>\n                  </div>\n\n                  <p\n                    :class=\"[\n                      'leading-relaxed',\n                      currentTime >= item.start && currentTime < item.end\n                        ? 'text-foreground font-medium'\n                        : 'text-muted-foreground group-hover:text-foreground',\n                    ]\"\n                  >\n                    {{ item.text }}\n                  </p>\n                </div>\n              </div>\n\n              <!-- Transcript Footer Notice -->\n              <div class=\"bg-muted/30 text-muted-foreground border-t p-2.5 text-center text-xs\">\n                Click any line to jump to that timestamp in the lecture.\n              </div>\n            </TabsContent>\n\n            <!-- TAB 2: Timestamped Notes Workspace -->\n            <TabsContent value=\"notes\" class=\"m-0 flex flex-1 flex-col focus-visible:outline-none\">\n              <!-- Note Composer -->\n              <div class=\"bg-muted/20 space-y-2 border-b p-3\">\n                <div class=\"flex items-center justify-between\">\n                  <label class=\"text-foreground flex items-center gap-1.5 text-xs font-bold tracking-tight\">\n                    <Clock class=\"text-primary size-3.5\" />\n                    <span>Add Note at {{ formattedCurrentTime }}</span>\n                  </label>\n                  <span class=\"text-muted-foreground font-mono text-xs\">Captures current playback time</span>\n                </div>\n\n                <Textarea\n                  v-model=\"newNoteText\"\n                  rows=\"2\"\n                  class=\"resize-y text-xs leading-relaxed\"\n                  :placeholder=\"`Write your thoughts or takeaway at ${formattedCurrentTime}...`\"\n                  @keydown.enter.meta=\"handleAddNote\"\n                  @keydown.enter.ctrl=\"handleAddNote\"\n                />\n\n                <div class=\"flex items-center justify-between\">\n                  <span class=\"text-muted-foreground text-xs\">Press ⌘+Enter to save</span>\n                  <Button\n                    size=\"sm\"\n                    class=\"h-7.5 gap-1 px-3 text-xs font-semibold\"\n                    :disabled=\"!newNoteText.trim()\"\n                    @click=\"handleAddNote\"\n                  >\n                    <Plus class=\"size-3.5\" />\n                    <span>Save Note</span>\n                  </Button>\n                </div>\n              </div>\n\n              <!-- Saved Notes List -->\n              <div class=\"max-h-[500px] flex-1 space-y-2.5 overflow-y-auto p-3.5\">\n                <div\n                  v-for=\"note in notesList\"\n                  :key=\"note.id\"\n                  class=\"group bg-card hover:border-border rounded-lg border p-3 text-xs shadow-xs transition-colors\"\n                >\n                  <div class=\"mb-1.5 flex items-center justify-between\">\n                    <Button\n                      variant=\"outline\"\n                      size=\"sm\"\n                      class=\"text-primary h-5 gap-1 rounded px-1.5 font-mono text-xs font-bold tabular-nums\"\n                      @click=\"seekTo(note.timestamp)\"\n                    >\n                      <Play class=\"size-2.5 fill-current\" />\n                      <span>{{ formatTime(note.timestamp) }}</span>\n                    </Button>\n\n                    <div class=\"flex items-center gap-1\">\n                      <span class=\"text-muted-foreground text-xs\">{{ note.createdAt }}</span>\n                      <Button\n                        variant=\"ghost\"\n                        size=\"icon\"\n                        class=\"text-muted-foreground hover:text-destructive size-5 opacity-0 group-hover:opacity-100\"\n                        aria-label=\"Delete note\"\n                        @click=\"handleDeleteNote(note.id)\"\n                      >\n                        <Trash2 class=\"size-3\" />\n                      </Button>\n                    </div>\n                  </div>\n\n                  <p class=\"text-foreground leading-relaxed\">{{ note.text }}</p>\n                </div>\n\n                <div v-if=\"notesList.length === 0\" class=\"text-muted-foreground py-8 text-center text-xs\">\n                  No personal notes yet. Add your first note above!\n                </div>\n              </div>\n            </TabsContent>\n\n            <!-- TAB 3: Q&A Community Discussion -->\n            <TabsContent value=\"qa\" class=\"m-0 flex flex-1 flex-col focus-visible:outline-none\">\n              <!-- Question Composer -->\n              <div class=\"bg-muted/20 space-y-2 border-b p-3\">\n                <div class=\"flex items-center justify-between\">\n                  <label class=\"text-foreground flex items-center gap-1.5 text-xs font-bold tracking-tight\">\n                    <MessageSquare class=\"text-primary size-3.5\" />\n                    <span>Ask Instructor at {{ formattedCurrentTime }}</span>\n                  </label>\n                  <span class=\"text-muted-foreground font-mono text-xs\">Tagged to this video time</span>\n                </div>\n\n                <Input\n                  v-model=\"newQuestionText\"\n                  placeholder=\"Ask a question about this topic...\"\n                  class=\"h-8 text-xs\"\n                  @keydown.enter=\"handleAddQuestion\"\n                />\n\n                <div class=\"flex items-center justify-end\">\n                  <Button\n                    size=\"sm\"\n                    class=\"h-7 gap-1 px-3 text-xs font-semibold\"\n                    :disabled=\"!newQuestionText.trim()\"\n                    @click=\"handleAddQuestion\"\n                  >\n                    <span>Post Question</span>\n                  </Button>\n                </div>\n              </div>\n\n              <!-- Questions Thread List -->\n              <div class=\"max-h-[500px] flex-1 space-y-3 overflow-y-auto p-3.5\">\n                <div\n                  v-for=\"q in questionsList\"\n                  :key=\"q.id\"\n                  class=\"bg-card space-y-2.5 rounded-lg border p-3 text-xs shadow-xs\"\n                >\n                  <!-- Question Header -->\n                  <div class=\"flex items-center justify-between\">\n                    <div class=\"flex items-center gap-2\">\n                      <div\n                        class=\"bg-primary/10 text-primary flex size-6 items-center justify-center rounded-full text-xs font-bold\"\n                      >\n                        {{ q.avatar }}\n                      </div>\n                      <div>\n                        <span class=\"text-foreground font-semibold\">{{ q.author }}</span>\n                        <Button\n                          variant=\"link\"\n                          size=\"sm\"\n                          class=\"text-muted-foreground ml-1.5 h-auto p-0 font-mono text-xs\"\n                          @click=\"seekTo(q.timestamp)\"\n                        >\n                          [{{ formatTime(q.timestamp) }}]\n                        </Button>\n                      </div>\n                    </div>\n\n                    <!-- Upvote Button -->\n                    <Button\n                      :variant=\"q.hasUpvoted ? 'default' : 'outline'\"\n                      size=\"sm\"\n                      class=\"h-6 gap-1 px-1.5 text-xs\"\n                      @click=\"toggleUpvote(q)\"\n                    >\n                      <ThumbsUp class=\"size-2.5\" />\n                      <span class=\"font-mono tabular-nums\">{{ q.upvotes }}</span>\n                    </Button>\n                  </div>\n\n                  <!-- Question Text -->\n                  <p class=\"text-foreground leading-relaxed\">{{ q.question }}</p>\n\n                  <!-- Instructor Answer if available -->\n                  <div v-if=\"q.answer\" class=\"bg-muted/40 border-primary/20 space-y-1 rounded-md border p-2.5\">\n                    <div class=\"flex items-center gap-1.5\">\n                      <span class=\"text-foreground text-xs font-semibold\">{{ q.answer.author }}</span>\n                      <Badge variant=\"default\" class=\"bg-success px-1 py-0 text-xs font-normal\">\n                        Instructor Verified\n                      </Badge>\n                    </div>\n                    <p class=\"text-muted-foreground text-xs leading-relaxed\">{{ q.answer.text }}</p>\n                  </div>\n                </div>\n              </div>\n            </TabsContent>\n          </Tabs>\n        </Card>\n      </aside>\n    </div>\n  </div>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/LessonVideoPlayer.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/card.json",
    "https://uipkge.dev/r/vue/input.json",
    "https://uipkge.dev/r/vue/progress.json",
    "https://uipkge.dev/r/vue/tabs.json",
    "https://uipkge.dev/r/vue/textarea.json"
  ],
  "description": "Online lecture video player with playback speed selector, interactive timestamped transcript, and note-taking drawer.",
  "categories": [
    "education",
    "app",
    "media"
  ]
}