Files

2932 lines
123 KiB
TypeScript

'use client'
import React, { useState, useEffect } from 'react'
import Link from 'next/link'
import { Box, Text, XStack, YStack } from '@/components/ui'
import { Band, Card } from '@/components/band'
import { Action, Chip } from '@hanzo/ui/marketing'
import { Grid } from '@hanzo/ui/grid'
import {
Terminal,
CheckCircle2,
Clock,
Play,
Pause,
Award,
Cpu,
ShieldCheck,
Code2,
FileCode,
Layers,
ChevronRight,
ExternalLink,
RefreshCw,
Coins,
Check,
Copy,
Download,
Share2,
Sparkles,
BookOpen,
ArrowRight,
CheckCircle,
Volume2,
Maximize2,
FileText,
RotateCcw,
Zap,
Lock,
LogIn,
LogOut,
User,
KeyRound,
} from 'lucide-react'
import { UNIVERSITY_COURSES, type UniversityCourse } from '../courses-data'
import { COURSE_PORTAL_DATA } from './portal-data'
import { courseCheckoutUrl } from '@/lib/pay'
import {
resolveStudentSession,
signOutStudent,
syncBackendClassEnrollment,
getHanzoLoginUrl,
isUserEnrolledInCourse,
type StudentSession,
} from '@/lib/auth'
interface ActiveLectureItem {
title: string
weekCode: string
weekTitle: string
type: 'lecture' | 'reading'
summary: string
duration?: string
instructor?: string
}
export default function StudentPortalPage() {
const [session, setSession] = useState<StudentSession | null>(null)
const [authLoaded, setAuthLoaded] = useState<boolean>(false)
const [showManualLogin, setShowManualLogin] = useState<boolean>(false)
const [manualTokenInput, setManualTokenInput] = useState<string>('')
const [manualNameInput, setManualNameInput] = useState<string>('')
const [manualHandleInput, setManualHandleInput] = useState<string>('')
const [authError, setAuthError] = useState<string | null>(null)
const [backendSyncStatus, setBackendSyncStatus] = useState<'idle' | 'syncing' | 'synced' | 'offline'>('idle')
const [selectedCourseSlug, setSelectedCourseSlug] = useState<string>('agentic-coding')
const [activeTab, setActiveTab] = useState<'terminal' | 'ast' | 'grader' | 'metering'>('terminal')
const [activeWeekIndex, setActiveWeekIndex] = useState<number>(0)
const [studentHandle, setStudentHandle] = useState<string>('student')
const [studentName, setStudentName] = useState<string>('Student')
const [welcomeBanner, setWelcomeBanner] = useState<boolean>(false)
// Module completion map per course: slug -> list of completed week indices
const [completedModules, setCompletedModules] = useState<Record<string, number[]>>({
'agentic-coding': [0, 1, 2],
'reinforcement-learning': [0, 1, 2],
'systems-engineering': [0, 1],
})
// Capstone defense passed per course
const [capstonePassed, setCapstonePassed] = useState<Record<string, boolean>>({
'agentic-coding': false,
'reinforcement-learning': false,
'systems-engineering': false,
})
// Watched lectures / read papers
const [watchedLectures, setWatchedLectures] = useState<Record<string, string[]>>({})
// Active lecture / paper modal state
const [activeLectureModal, setActiveLectureModal] = useState<ActiveLectureItem | null>(null)
const [isPlayingMedia, setIsPlayingMedia] = useState<boolean>(false)
const [playbackRate, setPlaybackRate] = useState<number>(1)
const [isRunningCommand, setIsRunningCommand] = useState(false)
const [isDefending, setIsDefending] = useState(false)
const [showCredentialModal, setShowCredentialModal] = useState(false)
const [feedbackToast, setFeedbackToast] = useState<string | null>(null)
// Active course & course workspace data
const activeCourse: UniversityCourse =
UNIVERSITY_COURSES.find((c) => c.slug === selectedCourseSlug) || UNIVERSITY_COURSES[0]
const isCourseEnrolled = Boolean(session?.isFullAccess || session?.enrolledClasses?.includes(selectedCourseSlug))
const workspaceData =
COURSE_PORTAL_DATA[selectedCourseSlug] || COURSE_PORTAL_DATA['agentic-coding']
// Dynamic terminal prompt tailored to actual student username
const userTerminalPrompt = (workspaceData.terminalPrompt || 'student@hanzo-sandbox:~$').replace(
/^alex@/,
`${studentHandle}@`
)
// Terminal history state
const [terminalHistory, setTerminalHistory] = useState<string[]>(workspaceData.initialHistory)
// Hydrate from localStorage, resolve authentic IAM student session, and configure backend class
useEffect(() => {
if (typeof window !== 'undefined') {
try {
const savedModules = localStorage.getItem('hanzo_portal_completed_modules')
if (savedModules) {
setCompletedModules(JSON.parse(savedModules))
}
const savedCapstones = localStorage.getItem('hanzo_portal_capstone_passed')
if (savedCapstones) {
setCapstonePassed(JSON.parse(savedCapstones))
}
const savedWatched = localStorage.getItem('hanzo_portal_watched_lectures')
if (savedWatched) {
setWatchedLectures(JSON.parse(savedWatched))
}
} catch (e) {
console.error('Failed to parse portal saved state', e)
}
// Resolve active student session (OIDC / IAM token or local tuition checkout)
const activeSession = resolveStudentSession()
if (activeSession && activeSession.isAuthenticated) {
setSession(activeSession)
setStudentName(activeSession.name)
setStudentHandle(activeSession.handle)
const params = new URLSearchParams(window.location.search)
const enrolledParam = params.get('enrolled') || params.get('course')
const welcome = params.get('welcome')
// Configure initial active class with backend enrollment
let targetClass: string | null = null
if (enrolledParam && UNIVERSITY_COURSES.some((c) => c.slug === enrolledParam)) {
targetClass = enrolledParam
} else if (activeSession.isFullAccess) {
const savedCourse = localStorage.getItem('hanzo_portal_selected_course')
targetClass = savedCourse && UNIVERSITY_COURSES.some((c) => c.slug === savedCourse)
? savedCourse
: UNIVERSITY_COURSES[0].slug
} else if (activeSession.enrolledClasses.length > 0) {
const savedCourse = localStorage.getItem('hanzo_portal_selected_course')
if (savedCourse && activeSession.enrolledClasses.includes(savedCourse)) {
targetClass = savedCourse
} else {
targetClass = activeSession.enrolledClasses[0]
}
} else {
const savedCourse = localStorage.getItem('hanzo_portal_selected_course')
targetClass = savedCourse && UNIVERSITY_COURSES.some((c) => c.slug === savedCourse)
? savedCourse
: UNIVERSITY_COURSES[0].slug
}
if (targetClass) {
setSelectedCourseSlug(targetClass)
}
if (welcome === '1') {
setWelcomeBanner(true)
}
// Synchronize with backend class limits / plan
setBackendSyncStatus('syncing')
syncBackendClassEnrollment(activeSession)
.then((syncRes) => {
if (syncRes.status === 'ok') {
setSession((prev) =>
prev
? {
...prev,
enrolledClasses: syncRes.enrolledSlugs,
backendPlan: syncRes.planName || prev.backendPlan,
}
: prev
)
setBackendSyncStatus('synced')
} else {
setBackendSyncStatus(syncRes.status === 'unauthorized' ? 'offline' : 'synced')
}
})
.catch(() => setBackendSyncStatus('offline'))
} else {
setSession(null)
}
setAuthLoaded(true)
}
}, [])
// Sign out student
const handleSignOut = () => {
signOutStudent()
setSession(null)
setFeedbackToast('Successfully signed out of Hanzo University.')
setTimeout(() => setFeedbackToast(null), 3000)
}
// Handle manual token / student handle submission
const handleManualTokenSubmit = (e?: React.FormEvent) => {
if (e) e.preventDefault()
setAuthError(null)
const trimmedToken = manualTokenInput.trim()
if (trimmedToken) {
try {
localStorage.setItem('hanzo_iam_access_token', trimmedToken)
const resolved = resolveStudentSession()
if (resolved && resolved.isAuthenticated) {
setSession(resolved)
setStudentName(resolved.name)
setStudentHandle(resolved.handle)
if (resolved.enrolledClasses.length > 0) {
setSelectedCourseSlug(resolved.enrolledClasses[0])
}
setShowManualLogin(false)
setFeedbackToast(`Welcome back, ${resolved.name}!`)
setTimeout(() => setFeedbackToast(null), 3500)
return
} else {
setAuthError('Could not verify JWT token. Please ensure it is a valid Hanzo token.')
return
}
} catch (err) {
setAuthError('Failed to parse token.')
return
}
}
if (manualNameInput.trim() || manualHandleInput.trim()) {
const handle = (manualHandleInput.trim() || manualNameInput.trim().toLowerCase().replace(/\s+/g, '-')).replace(/[^a-z0-9_-]/g, '')
const name = manualNameInput.trim() || manualHandleInput.trim()
localStorage.setItem('hanzo_portal_student_handle', handle)
localStorage.setItem('hanzo_portal_student_name', name)
const resolved = resolveStudentSession()
if (resolved && resolved.isAuthenticated) {
setSession(resolved)
setStudentName(resolved.name)
setStudentHandle(resolved.handle)
setShowManualLogin(false)
setFeedbackToast(`Signed in as ${resolved.name}!`)
setTimeout(() => setFeedbackToast(null), 3500)
return
}
}
setAuthError('Please enter a valid IAM token or student handle.')
}
// Sync terminal and active week when course changes
useEffect(() => {
const data = COURSE_PORTAL_DATA[selectedCourseSlug] || COURSE_PORTAL_DATA['agentic-coding']
const personalizedHistory = (data.initialHistory || []).map((l) =>
l.replace(/^alex@/, `${studentHandle}@`)
)
setTerminalHistory(personalizedHistory)
setActiveWeekIndex(0)
if (typeof window !== 'undefined') {
try {
localStorage.setItem('hanzo_portal_selected_course', selectedCourseSlug)
} catch (_) {}
}
}, [selectedCourseSlug, studentHandle])
// Execute terminal command
const executeCommand = (cmd: string) => {
if (!isCourseEnrolled) {
setTerminalHistory((prev) => [
...prev,
`${userTerminalPrompt} ${cmd}`,
`[ACCESS DENIED] Course ${activeCourse.code} is locked. Active enrollment required to execute commands in this Hanzo Visor microVM sandbox.`,
`${userTerminalPrompt} _`,
])
return
}
setIsRunningCommand(true)
const prompt = userTerminalPrompt
setTerminalHistory((prev) => [...prev, `${prompt} ${cmd}`])
setTimeout(() => {
let output: string[] = []
if (workspaceData.commandOutputs[cmd]) {
output = workspaceData.commandOutputs[cmd]
} else {
output = [
`[COMMAND EXECUTED] Task "${cmd}" processed in Hanzo Visor sandbox pod (exit code 0).`,
`[EVAL] All unit tests passed with zero runtime errors.`,
]
}
setTerminalHistory((prev) => [...prev, ...output, `${prompt} _`])
setIsRunningCommand(false)
}, 600)
}
// Submit lab coursework and mark module as completed
const handleCompleteLab = (weekIdx: number) => {
if (!isCourseEnrolled) {
setFeedbackToast(`Course ${activeCourse.code} is locked. Active enrollment required to submit lab coursework.`)
setTimeout(() => setFeedbackToast(null), 3500)
return
}
setIsRunningCommand(true)
const week = activeCourse.syllabus[weekIdx]
const prompt = userTerminalPrompt
setTerminalHistory((prev) => [
...prev,
`${prompt} hanzo autograder --submit ${week.code}`,
`[AUTOGRADER] Evaluating coursework for ${week.code}: ${week.title}...`,
` [+] Testing runtime constraints in Hanzo Visor microVM container...`,
` [+] Running AST tree-sitter & benchmark assertions...`,
` [+] Verifying zero syntax regressions and clean memory bounds...`,
`[PASS] 100% Tests Passed for ${week.code}! Marking module as COMPLETED.`,
`${prompt} _`,
])
setTimeout(() => {
setCompletedModules((prev) => {
const current = prev[selectedCourseSlug] || []
const updated = !current.includes(weekIdx)
? { ...prev, [selectedCourseSlug]: [...current, weekIdx].sort((a, b) => a - b) }
: prev
if (typeof window !== 'undefined') {
try {
localStorage.setItem('hanzo_portal_completed_modules', JSON.stringify(updated))
} catch (_) {}
}
return updated
})
setIsRunningCommand(false)
setFeedbackToast(`Module ${week.code} passed! Coursework progress updated.`)
setTimeout(() => setFeedbackToast(null), 3500)
}, 800)
}
// Fast-track all labs for testing & evaluation
const handleFastTrackAllLabs = () => {
if (!isCourseEnrolled) {
setFeedbackToast(`Course ${activeCourse.code} is locked. Active enrollment required to evaluate coursework.`)
setTimeout(() => setFeedbackToast(null), 3500)
return
}
const totalCount = activeCourse.syllabus.length
const allIndices = Array.from({ length: totalCount }, (_, i) => i)
const prompt = userTerminalPrompt
setTerminalHistory((prev) => [
...prev,
`${prompt} hanzo autograder --fast-track-all ${activeCourse.code}`,
`[AUTOGRADER] Fast-tracking all ${totalCount} laboratory evaluations for ${activeCourse.code}...`,
` [✓] Module 1: Baseline sandbox initialization & unit tests verified.`,
` [✓] Module 2: Intermediate optimization & memory constraints passed.`,
` [✓] Module 3: Advanced architecture verification & zero regressions confirmed.`,
` [✓] Module 4: Pre-capstone integration & telemetry telemetry exit code 0.`,
`[SUCCESS] All laboratory modules satisfied! 100% pass mark recorded.`,
`[STATUS] Qualifications MET for official Capstone Defense.`,
`${prompt} _`,
])
setCompletedModules((prev) => {
const updated = { ...prev, [selectedCourseSlug]: allIndices }
if (typeof window !== 'undefined') {
try {
localStorage.setItem('hanzo_portal_completed_modules', JSON.stringify(updated))
} catch (_) {}
}
return updated
})
setFeedbackToast(`All ${totalCount} laboratory modules passed! Capstone defense unlocked.`)
setTimeout(() => setFeedbackToast(null), 3500)
}
// Reset progress for active course
const handleResetCourseProgress = () => {
if (!isCourseEnrolled) return
setCompletedModules((prev) => {
const updated = { ...prev, [selectedCourseSlug]: [0] }
if (typeof window !== 'undefined') {
try {
localStorage.setItem('hanzo_portal_completed_modules', JSON.stringify(updated))
} catch (_) {}
}
return updated
})
setCapstonePassed((prev) => {
const updated = { ...prev, [selectedCourseSlug]: false }
if (typeof window !== 'undefined') {
try {
localStorage.setItem('hanzo_portal_capstone_passed', JSON.stringify(updated))
} catch (_) {}
}
return updated
})
setActiveWeekIndex(0)
const prompt = userTerminalPrompt
setTerminalHistory((prev) => [
...prev,
`${prompt} hanzo dev reset-progress --course ${activeCourse.code}`,
`[RESET] Progress reset for ${activeCourse.code}. Workspace restored to baseline.`,
`${prompt} _`,
])
setFeedbackToast(`Coursework reset for ${activeCourse.code}. Ready to start fresh.`)
setTimeout(() => setFeedbackToast(null), 3000)
}
// Defend Capstone and officially graduate
const handleDefendCapstone = () => {
if (!isCourseEnrolled) {
setFeedbackToast(`Course ${activeCourse.code} is locked. Active enrollment required to defend capstone.`)
setTimeout(() => setFeedbackToast(null), 3500)
return
}
setIsDefending(true)
const prompt = userTerminalPrompt
setTerminalHistory((prev) => [
...prev,
`${prompt} hanzo defense --capstone ${activeCourse.code}`,
`==================== CAPSTONE DEFENSE: ${activeCourse.credential} ====================`,
`[1/4] Cloning candidate repository into cleanroom Hanzo Visor environment...`,
`[2/4] Executing full test suite & production benchmarks for ${activeCourse.title}...`,
`[3/4] Cryptographically auditing zero regression errors & budget bounds...`,
`[4/4] Generating W3C Verifiable Credential on Lux Chain...`,
`[CONGRATULATIONS] Capstone Defended with Distinction! Score: 100%.`,
`[ISSUANCE] Credential #${activeCourse.credential}-2026-9821 minted on-chain.`,
`${prompt} _`,
])
setTimeout(() => {
setIsDefending(false)
setCapstonePassed((prev) => {
const updated = { ...prev, [selectedCourseSlug]: true }
if (typeof window !== 'undefined') {
try {
localStorage.setItem('hanzo_portal_capstone_passed', JSON.stringify(updated))
} catch (_) {}
}
return updated
})
setShowCredentialModal(true)
}, 1200)
}
// Open Lecture or Reading Modal
const openLectureModal = (
week: (typeof activeCourse.syllabus)[0],
itemTitle: string,
type: 'lecture' | 'reading',
) => {
if (!isCourseEnrolled) {
setFeedbackToast(`Lecture locked. Active enrollment in ${activeCourse.code} required to access media.`)
setTimeout(() => setFeedbackToast(null), 3500)
return
}
setActiveLectureModal({
title: itemTitle,
weekCode: week.code,
weekTitle: week.title,
type,
summary: week.summary,
duration: type === 'lecture' ? '38 mins' : '15 min read',
instructor: activeCourse.instructor.name,
})
setIsPlayingMedia(false)
}
// Mark lecture / reading watched
const handleMarkLectureWatched = () => {
if (!activeLectureModal) return
const current = watchedLectures[selectedCourseSlug] || []
if (!current.includes(activeLectureModal.title)) {
const updated = {
...watchedLectures,
[selectedCourseSlug]: [...current, activeLectureModal.title],
}
setWatchedLectures(updated)
if (typeof window !== 'undefined') {
try {
localStorage.setItem('hanzo_portal_watched_lectures', JSON.stringify(updated))
} catch (_) {}
}
}
setFeedbackToast(`Marked "${activeLectureModal.title}" as completed!`)
setTimeout(() => setFeedbackToast(null), 3000)
setActiveLectureModal(null)
}
// Download SVG badge
const handleDownloadSvg = () => {
const svgContent = `<svg xmlns="http://www.w3.org/2000/svg" width="380" height="64" viewBox="0 0 380 64" fill="none">
<rect width="380" height="64" rx="12" fill="#0A0A0A" stroke="#262626"/>
<rect x="1" y="1" width="378" height="62" rx="11" stroke="#34D399" stroke-opacity="0.3"/>
<circle cx="28" cy="32" r="14" fill="#047857" fill-opacity="0.3"/>
<path d="M22 32L26 36L34 28" stroke="#34D399" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<text fill="#FFFFFF" font-family="-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif" font-size="12" font-weight="700" x="52" y="27">${activeCourse.credential}: ${activeCourse.title}</text>
<text fill="#A3A3A3" font-family="-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif" font-size="10" x="52" y="44">VERIFIED: ${studentName} · 100% DISTINCTION</text>
<rect x="290" y="20" width="76" height="24" rx="6" fill="#171717" stroke="#404040"/>
<text fill="#34D399" font-family="ui-monospace, monospace" font-size="9" font-weight="700" x="302" y="36">PASS 100%</text>
</svg>`
const blob = new Blob([svgContent], { type: 'image/svg+xml' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `Hanzo-${activeCourse.credential}-Badge.svg`
a.click()
URL.revokeObjectURL(url)
setFeedbackToast('SVG Badge downloaded successfully!')
setTimeout(() => setFeedbackToast(null), 3000)
}
// Calculate course progress
const currentCompleted = completedModules[selectedCourseSlug] || []
const totalWeeks = activeCourse.syllabus.length
const progressPercent = Math.min(100, Math.round((currentCompleted.length / totalWeeks) * 100))
const isDefenseUnlocked = currentCompleted.length >= totalWeeks || capstonePassed[selectedCourseSlug]
const isDegreeAwarded = capstonePassed[selectedCourseSlug]
// ── Authentication & Session Gate ──
if (!authLoaded) {
return (
<Box
minH="100vh"
bg="$background"
$platform-web={{ color: 'var(--foreground)' }}
display="flex"
items="center"
justify="center"
>
<YStack items="center" gap="$3">
<Box
width={40}
height={40}
rounded="var(--radius-full)"
borderWidth={3}
borderColor="var(--emerald-500)"
$platform-web={{
borderTopColor: 'transparent',
animation: 'spin 0.8s linear infinite',
}}
/>
<Text fontSize="$2" color="var(--white)" fontWeight="700" fontFamily="$mono">
VERIFYING HANZO CREDENTIALS...
</Text>
<Text fontSize="$1" color="var(--muted-foreground)">
Synchronizing student session with Hanzo IAM & Class Registry
</Text>
</YStack>
</Box>
)
}
if (!session || !session.isAuthenticated) {
return (
<Box minH="100vh" bg="$background" $platform-web={{ color: 'var(--foreground)' }}>
{/* Navigation Bar */}
<Box
borderBottomWidth={1}
borderColor="rgba(255, 255, 255, 0.06)"
bg="var(--pure-black)"
py="$3"
px="$6"
>
<XStack items="center" justify="space-between" flexWrap="wrap" gap="$4">
<Link href="/" style={{ textDecoration: 'none', color: 'inherit' }}>
<XStack items="center" gap="$2">
<Text fontSize="$2" fontWeight="700" color="var(--white)" fontFamily="$mono">
hanzo.university
</Text>
<Chip px={6} py={2} fontSize="$1" fontFamily="$mono" color="var(--emerald-400)">
STUDENT PORTAL
</Chip>
</XStack>
</Link>
<Link
href="/"
style={{
fontSize: '13px',
color: 'var(--white-80)',
textDecoration: 'none',
}}
>
Explore Course Catalog ↗
</Link>
</XStack>
</Box>
{/* Authentication Gate Band */}
<Band pad={60} measure={840}>
<YStack items="center" gap="$6" $platform-web={{ textAlign: 'center' }}>
<Box
p="$4"
rounded="var(--radius-full)"
bg="rgba(16, 185, 129, 0.15)"
borderWidth={0}
$platform-web={{
boxShadow: '0 0 36px rgba(16, 185, 129, 0.25)',
}}
>
<Lock size={36} color="var(--emerald-400)" />
</Box>
<YStack gap="$2" $platform-web={{ maxWidth: '640px' }}>
<Chip px={10} py={4} fontSize="$1" fontFamily="$mono" color="var(--emerald-400)" mx="auto">
AUTHENTICATION REQUIRED
</Chip>
<Text fontSize="$6" fontWeight="800" color="var(--white)" $platform-web={{ lineHeight: 34 }}>
Sign in to access your Learning Workstation
</Text>
<Text fontSize="$2" color="var(--muted-foreground)" $platform-web={{ lineHeight: 22 }}>
The Hanzo University learning workstation, live GPU microVM sandbox pod, and autograder evaluation systems require an active Hanzo IAM student account or enrolled class session.
</Text>
</YStack>
{/* Authentication Action Card */}
<Card
p="$6"
w="100%"
maxWidth={520}
bg="rgba(255, 255, 255, 0.02)"
borderWidth={0}
rounded="var(--radius-lg)"
>
<YStack gap="$4">
<Action
render="a"
href={getHanzoLoginUrl('/portal')}
fill
py={12}
$platform-web={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '8px',
fontSize: '15px',
fontWeight: '600',
textDecoration: 'none',
}}
>
<LogIn size={18} />
Sign In with Hanzo
<ArrowRight size={16} />
</Action>
<Text fontSize="$1" color="var(--muted-foreground)">
Single sign-on via Hanzo IAM across dev.chat, hanzo.ai, and hanzo.university
</Text>
{/* Divider */}
<Box borderBottomWidth={1} borderColor="rgba(255, 255, 255, 0.06)" my="$1" />
{/* Secondary Option: Manual Token or Handle */}
{!showManualLogin ? (
<Box
render="button"
onClick={() => setShowManualLogin(true)}
py="$2"
$platform-web={{
background: 'transparent',
border: 'none',
color: 'var(--white-70)',
cursor: 'pointer',
fontSize: '13px',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: '6px',
}}
>
<KeyRound size={14} />
Or authenticate with IAM Token / Hanzo ID
</Box>
) : (
<YStack gap="$3" pt="$2" $platform-web={{ textAlign: 'left' }}>
<Text fontSize="$1" fontWeight="600" color="var(--white)">
Manual IAM Token or Student Handle:
</Text>
{authError && (
<Box
p="$2"
rounded="var(--radius-sm)"
bg="rgba(239, 68, 68, 0.15)"
borderWidth={0}
>
<Text fontSize="$1" color="#f87171">
{authError}
</Text>
</Box>
)}
<input
type="text"
placeholder="Paste Hanzo IAM JWT token (or student handle)"
value={manualTokenInput}
onChange={(e) => setManualTokenInput(e.target.value)}
style={{
width: '100%',
padding: '10px 12px',
borderRadius: '8px',
background: 'rgba(255, 255, 255, 0.04)',
border: '1px solid rgba(255, 255, 255, 0.1)',
color: 'white',
fontFamily: 'monospace',
fontSize: '12px',
outline: 'none',
boxSizing: 'border-box',
}}
/>
<input
type="text"
placeholder="Student Full Name (optional, e.g. James Burns)"
value={manualNameInput}
onChange={(e) => setManualNameInput(e.target.value)}
style={{
width: '100%',
padding: '10px 12px',
borderRadius: '8px',
background: 'rgba(255, 255, 255, 0.04)',
border: '1px solid rgba(255, 255, 255, 0.1)',
color: 'white',
fontSize: '13px',
outline: 'none',
boxSizing: 'border-box',
}}
/>
<XStack gap="$2" justify="flex-end" mt="$1">
<Box
render="button"
onClick={() => setShowManualLogin(false)}
px="$3"
py="$2"
$platform-web={{
background: 'transparent',
border: 'none',
borderRadius: '6px',
color: 'var(--muted-foreground)',
cursor: 'pointer',
fontSize: '12px',
}}
>
Cancel
</Box>
<Action
render="button"
onClick={handleManualTokenSubmit}
px={16}
py={8}
$platform-web={{ fontSize: '13px', fontWeight: '600' }}
>
Verify & Enter Portal
</Action>
</XStack>
</YStack>
)}
</YStack>
</Card>
{/* Not Enrolled Promo Link */}
<XStack items="center" gap="$2">
<Text fontSize="$1" color="var(--muted-foreground)">
Not enrolled in a course yet?
</Text>
<Link
href="/"
style={{
fontSize: '13px',
color: 'var(--emerald-400)',
fontWeight: 600,
textDecoration: 'none',
}}
>
Browse Degree Tracks & Tuition Rebate →
</Link>
</XStack>
{/* Feature Grid */}
<Grid columns={{ min: 240, max: 3 }} gap={16} width="100%" $platform-web={{ maxWidth: '900px' }} mt="$4">
<Card
p="$4"
bg="rgba(255, 255, 255, 0.03)"
borderWidth={0}
rounded="var(--radius-md)"
textAlign="left"
>
<XStack items="center" gap="$2" mb="$2">
<ShieldCheck size={18} color="var(--emerald-400)" />
<Text fontSize="$2" fontWeight="700" color="var(--white)">
W3C On-Chain Diplomas
</Text>
</XStack>
<Text fontSize="$1" color="var(--muted-foreground)" $platform-web={{ lineHeight: 18 }}>
Sovereign cryptographic credentials issued directly to your Hanzo DID, registered on Lux.
</Text>
</Card>
<Card
p="$4"
bg="rgba(255, 255, 255, 0.03)"
borderWidth={0}
rounded="var(--radius-md)"
textAlign="left"
>
<XStack items="center" gap="$2" mb="$2">
<Cpu size={18} color="var(--emerald-400)" />
<Text fontSize="$2" fontWeight="700" color="var(--white)">
Hanzo Visor MicroVM
</Text>
</XStack>
<Text fontSize="$1" color="var(--muted-foreground)" $platform-web={{ lineHeight: 18 }}>
Pre-configured GPU workstation with PyTorch, CUDA, AST tree-sitter, and SWE-bench harness.
</Text>
</Card>
<Card
p="$4"
bg="rgba(255, 255, 255, 0.03)"
borderWidth={0}
rounded="var(--radius-md)"
textAlign="left"
>
<XStack items="center" gap="$2" mb="$2">
<Coins size={18} color="var(--emerald-400)" />
<Text fontSize="$2" fontWeight="700" color="var(--white)">
100% Tuition Rebate
</Text>
</XStack>
<Text fontSize="$1" color="var(--muted-foreground)" $platform-web={{ lineHeight: 18 }}>
Every dollar in tuition is loaded as live LLM and compute credits into your Hanzo account.
</Text>
</Card>
</Grid>
</YStack>
</Band>
</Box>
)
}
return (
<Box minH="100vh" bg="$background" $platform-web={{ color: 'var(--foreground)' }}>
{/* ── Single Unified Slim Portal Ribbon ── */}
<Box
borderBottomWidth={1}
borderColor="rgba(255, 255, 255, 0.06)"
bg="var(--pure-black)"
py="$3"
px="$5"
position="sticky"
t={0}
$platform-web={{ zIndex: 30, backdropFilter: 'blur(12px)' }}
>
<XStack items="center" justify="space-between" flexWrap="wrap" gap="$3">
{/* Left: Brand & Track Selector */}
<XStack items="center" gap="$3" flexWrap="wrap">
<Link href="/" style={{ textDecoration: 'none', color: 'inherit' }}>
<XStack items="center" gap="$2">
<Text fontSize="$2" fontWeight="700" color="var(--white)" fontFamily="$mono">
hanzo.university
</Text>
<Chip px={6} py={2} fontSize="$1" fontFamily="$mono" color="var(--emerald-400)">
STUDENT PORTAL
</Chip>
</XStack>
</Link>
{/* Course Track Switcher Pills */}
<XStack items="center" gap="$1.5" flexWrap="wrap">
{UNIVERSITY_COURSES.map((c) => {
const isCompleted = capstonePassed[c.slug]
const isCurrent = selectedCourseSlug === c.slug
const isEnrolledInCourse = Boolean(session?.isFullAccess || session?.enrolledClasses?.includes(c.slug))
return (
<Box
key={c.slug}
render="button"
onClick={() => setSelectedCourseSlug(c.slug)}
px="$2.5"
py="$1"
rounded="var(--radius-md)"
bg={isCurrent ? 'var(--white)' : 'rgba(255, 255, 255, 0.05)'}
borderWidth={0}
$platform-web={{
cursor: 'pointer',
display: 'inline-flex',
flexDirection: 'row',
alignItems: 'center',
gap: '5px',
transition: 'all 0.15s ease',
border: 'none',
opacity: isEnrolledInCourse ? 1 : 0.75,
}}
title={
isEnrolledInCourse
? `${c.code}: ${c.title} (Enrolled)`
: `${c.code}: ${c.title} (Locked — $${c.price} USD)`
}
>
{!isEnrolledInCourse && (
<Lock size={10} color={isCurrent ? 'var(--pure-black)' : 'var(--muted-foreground)'} />
)}
<Text
fontSize="$1"
fontWeight={isCurrent ? '700' : '500'}
fontFamily="$mono"
color={isCurrent ? 'var(--pure-black)' : isEnrolledInCourse ? 'var(--white-80)' : 'var(--white-60)'}
>
{c.credential}
</Text>
{isCompleted ? (
<Check size={11} color={isCurrent ? 'var(--pure-black)' : 'var(--emerald-400)'} />
) : null}
</Box>
)
})}
</XStack>
</XStack>
{/* Right: Student Profile & Credential CTAs */}
<XStack items="center" gap="$2.5" flexWrap="wrap">
{isCourseEnrolled ? (
<XStack
items="center"
gap="$2"
px="$2.5"
py="$1"
rounded="var(--radius-md)"
bg="rgba(255, 255, 255, 0.05)"
borderWidth={0}
>
<Coins size={13} color="var(--emerald-400)" />
<Text fontSize="$1" fontWeight="700" color="var(--emerald-300)" fontFamily="$mono">
${activeCourse.rebateCredits}.00 Credits
</Text>
</XStack>
) : (
<XStack
items="center"
gap="$1.5"
px="$2.5"
py="$1"
rounded="var(--radius-md)"
bg="rgba(245, 158, 11, 0.08)"
borderWidth={0}
>
<Lock size={11} color="#f59e0b" />
<Text fontSize="$1" fontWeight="600" color="#f59e0b" fontFamily="$mono">
Course Locked
</Text>
</XStack>
)}
<XStack
items="center"
gap="$1.5"
px="$2.5"
py="$1"
rounded="var(--radius-md)"
bg="rgba(255, 255, 255, 0.05)"
borderWidth={0}
>
<User size={13} color="var(--emerald-400)" />
<Text fontSize="$1" fontWeight="600" color="var(--white)">
{studentName}
</Text>
</XStack>
<Action
render="button"
onClick={() => setShowCredentialModal(true)}
px={10}
py={5}
$platform-web={{ fontSize: '11px', fontWeight: 600 }}
>
<XStack items="center" gap="$1">
<Award size={13} />
<Text fontSize="$1" fontWeight="600" color="inherit">
{isDegreeAwarded ? 'Degree Certificate' : 'Preview Credential'}
</Text>
</XStack>
</Action>
<Box
render="button"
onClick={handleSignOut}
px="$2"
py="$1"
rounded="var(--radius-md)"
borderWidth={0}
bg="rgba(255, 255, 255, 0.05)"
$platform-web={{
cursor: 'pointer',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: '4px',
color: 'var(--muted-foreground)',
fontSize: '12px',
border: 'none',
}}
title="Sign Out"
>
<LogOut size={13} />
<Text fontSize="$1" color="inherit">
Sign Out
</Text>
</Box>
</XStack>
</XStack>
</Box>
{/* ── Main Workstation Content ── */}
<Band pad={36} measure={1280}>
{/* Toast Notification */}
{feedbackToast && (
<Box
p="$3"
mb="$4"
rounded="var(--radius-md)"
bg="rgba(16, 185, 129, 0.15)"
borderWidth={0}
>
<XStack items="center" gap="$2">
<CheckCircle2 size={16} color="var(--emerald-400)" />
<Text fontSize="$2" fontWeight="600" color="var(--white)">
{feedbackToast}
</Text>
</XStack>
</Box>
)}
{/* Newly Enrolled Welcome Banner (Dismissible) */}
{welcomeBanner && (
<Box
p="$4"
mb="$5"
rounded="var(--radius-lg)"
bg="rgba(16, 185, 129, 0.08)"
borderWidth={0}
$platform-web={{
boxShadow: '0 0 24px rgba(16, 185, 129, 0.15)',
}}
>
<XStack items="center" justify="space-between" flexWrap="wrap" gap="$3">
<XStack items="center" gap="$3">
<Box p="$2" rounded="var(--radius-md)" bg="var(--emerald-950)">
<Sparkles size={20} color="var(--emerald-400)" />
</Box>
<YStack gap={2}>
<Text fontSize="$3" fontWeight="700" color="var(--white)">
🎉 Welcome to Hanzo University, {studentName}!
</Text>
<Text fontSize="$1" color="var(--white-70)">
Tuition confirmed. Your student DID (`did:hanzo:student:{studentHandle}`) is active, ${activeCourse.rebateCredits}.00 compute tokens are credited, and your sandbox pod is provisioned.
</Text>
</YStack>
</XStack>
<Action
render="button"
onClick={() => setWelcomeBanner(false)}
px={10}
py={5}
$platform-web={{ fontSize: '11px' }}
>
Dismiss
</Action>
</XStack>
</Box>
)}
{/* ── Course Mission & Progress Hero ── */}
<Box
p="$5"
mb="$5"
rounded="var(--radius-xl)"
bg="rgba(255, 255, 255, 0.02)"
borderWidth={0}
>
<XStack items="flex-start" justify="space-between" flexWrap="wrap" gap="$4">
<YStack gap="$1.5" flex={1} minW={280}>
<XStack items="center" gap="$2" flexWrap="wrap">
{isCourseEnrolled ? (
<>
<Chip px={8} py={2} fontSize="$1" fontFamily="$mono" color="var(--emerald-400)">
{activeCourse.code} · {activeCourse.credential}
</Chip>
{isDegreeAwarded ? (
<Chip px={8} py={2} fontSize="$1" fontFamily="$mono" color="var(--emerald-300)">
GRADUATED & CERTIFIED
</Chip>
) : (
<Chip px={8} py={2} fontSize="$1" fontFamily="$mono" color="var(--white-70)">
LEVEL: {activeCourse.level.toUpperCase()}
</Chip>
)}
</>
) : (
<>
<Chip px={8} py={2} fontSize="$1" fontFamily="$mono" color="var(--amber-400)">
<Lock size={10} style={{ marginRight: 4, display: 'inline', verticalAlign: 'middle' }} />
{activeCourse.code} · ENROLLMENT REQUIRED
</Chip>
<Chip px={8} py={2} fontSize="$1" fontFamily="$mono" color="var(--white-70)">
LEVEL: {activeCourse.level.toUpperCase()}
</Chip>
<Chip px={8} py={2} fontSize="$1" fontFamily="$mono" color="var(--white-70)">
{activeCourse.units}.0 UNITS · {activeCourse.credential}
</Chip>
</>
)}
</XStack>
<Text fontSize="$6" fontWeight="800" color="var(--white)">
{activeCourse.title}
</Text>
<Text fontSize="$1" color="var(--muted-foreground)">
{isCourseEnrolled
? `Complete all ${totalWeeks} coursework modules below to qualify for your Capstone Defense and earn your official W3C verifiable credential.`
: activeCourse.summary}
</Text>
</YStack>
<XStack items="center" gap="$3">
<Link
href={`/${activeCourse.slug}`}
style={{
fontSize: '12px',
color: 'var(--white-70)',
textDecoration: 'none',
}}
>
Course Syllabus ↗
</Link>
{isCourseEnrolled ? (
isDegreeAwarded ? (
<Action
render="button"
onClick={() => setShowCredentialModal(true)}
px={14}
py={7}
fill
$platform-web={{ fontSize: '12px' }}
>
View W3C Certificate →
</Action>
) : isDefenseUnlocked ? (
<Action
render="button"
onClick={handleDefendCapstone}
disabled={isDefending}
px={14}
py={7}
fill
$platform-web={{ fontSize: '12px' }}
>
{isDefending ? 'Defending Capstone...' : `Defend Capstone & Graduate →`}
</Action>
) : null
) : (
<Action
render="a"
href={courseCheckoutUrl(activeCourse.slug)}
px={16}
py={8}
fill
$platform-web={{ fontSize: '13px', fontWeight: 600, textDecoration: 'none' }}
>
Enroll in {activeCourse.code} — ${activeCourse.price} USD →
</Action>
)}
</XStack>
</XStack>
{/* Progress Bar & Milestone Summary */}
{isCourseEnrolled ? (
<YStack gap="$2" mt="$4" pt="$3" borderTopWidth={1} borderColor="rgba(255, 255, 255, 0.06)">
<XStack items="center" justify="space-between">
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">
COURSEWORK ROADMAP PROGRESS
</Text>
<Text fontSize="$1" fontWeight="700" color={isDefenseUnlocked ? 'var(--emerald-400)' : 'var(--white)'} fontFamily="$mono">
{currentCompleted.length} of {totalWeeks} Modules Passed ({progressPercent}%)
</Text>
</XStack>
<Box height={6} rounded={9999} bg="rgba(255, 255, 255, 0.08)" overflow="hidden">
<Box
height="100%"
width={`${progressPercent}%`}
bg="var(--emerald-400)"
$platform-web={{ transition: 'width 0.3s ease' }}
/>
</Box>
</YStack>
) : (
<YStack gap="$2" mt="$4" pt="$3" borderTopWidth={1} borderColor="rgba(255, 255, 255, 0.06)">
<XStack items="center" justify="space-between" flexWrap="wrap" gap="$2">
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">
ACCESS STATUS: LOCKED
</Text>
<Text fontSize="$1" fontWeight="600" color="var(--amber-400)" fontFamily="$mono">
Active tuition enrollment required for sandbox microVM pod & autograder
</Text>
</XStack>
</YStack>
)}
</Box>
{isCourseEnrolled ? (
<>
{/* ── Interactive 5-Step Credential Stepper ── */}
<Box mb="$5">
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono" mb="$2">
STEP-BY-STEP CREDENTIAL PATHWAY (CLICK ANY MODULE TO FOCUS):
</Text>
<div className="portal-stepper-grid">
{activeCourse.syllabus.map((week, idx) => {
const isCompleted = currentCompleted.includes(idx)
const isActive = activeWeekIndex === idx
return (
<Box
key={week.week}
render="button"
onClick={() => setActiveWeekIndex(idx)}
p="$3"
rounded="var(--radius-lg)"
bg={
isActive
? 'rgba(255, 255, 255, 0.1)'
: isCompleted
? 'rgba(16, 185, 129, 0.08)'
: 'rgba(255, 255, 255, 0.03)'
}
borderWidth={0}
className="portal-step-card"
$platform-web={{
cursor: 'pointer',
textAlign: 'left',
border: 'none',
boxShadow: isActive ? '0 0 16px rgba(255, 255, 255, 0.08)' : 'none',
}}
>
<XStack items="center" justify="space-between" mb="$1">
<Text fontSize="$1" fontFamily="$mono" color="var(--muted-foreground)">
0{idx + 1} · {week.code}
</Text>
{isCompleted ? (
<Chip px={6} py={1} fontSize="$1" fontFamily="$mono" color="var(--emerald-400)">
PASSED ✓
</Chip>
) : isActive ? (
<Chip px={6} py={1} fontSize="$1" fontFamily="$mono" color="var(--white)">
ACTIVE ▶
</Chip>
) : (
<Chip px={6} py={1} fontSize="$1" fontFamily="$mono" color="var(--muted-foreground)">
PENDING
</Chip>
)}
</XStack>
<div style={{ fontSize: '13px', fontWeight: 700, color: 'var(--white)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', width: '100%' }}>
{week.title}
</div>
</Box>
)
})}
{/* Step 5: Capstone Defense */}
<Box
render="button"
onClick={() => {
if (isDegreeAwarded) {
setShowCredentialModal(true)
} else if (isDefenseUnlocked) {
handleDefendCapstone()
}
}}
p="$3"
rounded="var(--radius-lg)"
bg={
isDegreeAwarded
? 'rgba(16, 185, 129, 0.15)'
: isDefenseUnlocked
? 'rgba(16, 185, 129, 0.1)'
: 'rgba(255, 255, 255, 0.03)'
}
borderWidth={0}
className="portal-step-card"
$platform-web={{
cursor: isDefenseUnlocked || isDegreeAwarded ? 'pointer' : 'default',
textAlign: 'left',
border: 'none',
boxShadow: isDefenseUnlocked ? '0 0 18px rgba(16, 185, 129, 0.2)' : 'none',
}}
>
<XStack items="center" justify="space-between" mb="$1">
<Text fontSize="$1" fontFamily="$mono" color="var(--emerald-400)">
FINAL MILESTONE
</Text>
<Award size={14} color="var(--emerald-400)" />
</XStack>
<div style={{ fontSize: '13px', fontWeight: 700, color: 'var(--white)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', width: '100%' }}>
{isDegreeAwarded ? 'Degree Minted 🎓' : isDefenseUnlocked ? 'Defend Capstone ⚡' : 'Capstone Defense'}
</div>
</Box>
</div>
</Box>
{/* ── Active Module Workspace (Responsive Dual-Column Grid) ── */}
<div className="portal-workspace-grid">
{/* LEFT: Current Module Guide & Coursework Checklist */}
{(() => {
const currentWeek = activeCourse.syllabus[activeWeekIndex] || activeCourse.syllabus[0]
const isWeekPassed = currentCompleted.includes(activeWeekIndex)
return (
<YStack gap="$4" minW={0} width="100%">
<Card p="$5" bg="rgba(255, 255, 255, 0.02)" borderWidth={0} rounded="var(--radius-xl)">
{/* Card Header & Qualifications */}
<XStack items="center" justify="space-between" mb="$4" flexWrap="wrap" gap="$3">
<YStack gap="$1" minW={0} flex={1}>
<Text fontSize="$3" fontWeight="800" color="var(--white)">
{activeCourse.code} Coursework & Qualifications
</Text>
<Text fontSize="$1" color="var(--muted-foreground)">
Pass all {totalWeeks} laboratory modules to unlock the official {activeCourse.credential} Capstone Defense
</Text>
</YStack>
<Chip
px={10}
py={3}
fontSize="$1"
fontFamily="$mono"
color={isDefenseUnlocked ? 'var(--emerald-400)' : 'var(--amber-400)'}
$platform-web={{ whiteSpace: 'nowrap', flexShrink: 0 }}
>
{progressPercent}% COMPLETED
</Chip>
</XStack>
{/* Progress Bar */}
<Box height={6} rounded={9999} bg="rgba(255, 255, 255, 0.08)" overflow="hidden" mb="$4">
<Box
height="100%"
width={`${progressPercent}%`}
bg="var(--emerald-400)"
$platform-web={{ transition: 'width 0.3s ease' }}
/>
</Box>
{/* Week Modules List */}
<YStack gap="$3">
{activeCourse.syllabus.map((week, idx) => {
const isCompleted = currentCompleted.includes(idx)
const isActive = activeWeekIndex === idx
return (
<Box
key={week.week}
p="$4"
rounded="var(--radius-md)"
bg={isActive ? 'rgba(255, 255, 255, 0.04)' : 'rgba(255, 255, 255, 0.02)'}
borderWidth={1}
borderColor={
isActive
? 'rgba(16, 185, 129, 0.5)'
: isCompleted
? 'rgba(255, 255, 255, 0.1)'
: 'rgba(255, 255, 255, 0.04)'
}
$platform-web={{ cursor: 'pointer', transition: 'border-color 0.15s ease' }}
onClick={() => setActiveWeekIndex(idx)}
>
<XStack items="center" justify="space-between" gap="$3" width="100%">
<XStack items="center" gap="$3" minW={0} flex={1}>
{/* Icon Indicator */}
<Box
width={24}
height={24}
minW={24}
minH={24}
rounded={9999}
items="center"
justify="center"
bg={
isCompleted
? 'rgba(16, 185, 129, 0.15)'
: isActive
? 'rgba(255, 255, 255, 0.1)'
: 'rgba(255, 255, 255, 0.03)'
}
borderWidth={1}
borderColor={
isCompleted
? 'rgba(16, 185, 129, 0.35)'
: isActive
? 'rgba(255, 255, 255, 0.25)'
: 'rgba(255, 255, 255, 0.08)'
}
$platform-web={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0,
}}
>
{isCompleted ? (
<Check size={13} color="#34d399" strokeWidth={2.5} />
) : (
<Text
fontSize="$1"
fontWeight="700"
fontFamily="$mono"
color={isActive ? 'var(--white)' : 'var(--muted-foreground)'}
>
{idx + 1}
</Text>
)}
</Box>
{/* Title & Lab Description */}
<YStack minW={0} flex={1}>
<XStack items="center" gap="$2" flexWrap="wrap">
<Text fontSize="$1" fontFamily="$mono" color="var(--muted-foreground)">
{week.code}
</Text>
<Text fontSize="$2" fontWeight="600" color="var(--white)" $platform-web={{ wordBreak: 'break-word' }}>
{week.title}
</Text>
</XStack>
<Text fontSize="$1" color="var(--muted-foreground)" numberOfLines={1}>
{week.lab}
</Text>
</YStack>
</XStack>
{/* Status Chip Badge */}
<Chip
px={8}
py={2}
fontSize="$1"
fontFamily="$mono"
color={
isCompleted
? 'var(--emerald-400)'
: isActive
? 'var(--amber-400)'
: 'var(--muted-foreground)'
}
$platform-web={{
whiteSpace: 'nowrap',
flexShrink: 0,
}}
>
{isCompleted ? 'COMPLETED' : isActive ? 'IN PROGRESS' : 'READY TO START'}
</Chip>
</XStack>
{/* Expanded Active Module Content */}
{isActive && (
<YStack
mt="$3"
pt="$3"
borderTopWidth={1}
borderColor="rgba(255, 255, 255, 0.08)"
gap="$3"
>
<Text fontSize="$1" color="var(--white-80)" $platform-web={{ lineHeight: 1.6 }}>
{week.summary}
</Text>
{/* Interactive Lectures & Seminars */}
<YStack gap="$2">
<Text fontSize="$1" fontWeight="600" color="var(--white)">
Interactive Lectures & Seminars:
</Text>
{week.lectures.map((lec, lIdx) => {
const isWatched = (watchedLectures[selectedCourseSlug] || []).includes(lec)
return (
<XStack
key={lIdx}
items="center"
justify="space-between"
p="$2"
px="$3"
rounded="var(--radius-sm)"
bg={isWatched ? 'rgba(16, 185, 129, 0.08)' : 'rgba(255, 255, 255, 0.03)'}
borderWidth={1}
borderColor={isWatched ? 'rgba(16, 185, 129, 0.3)' : 'rgba(255, 255, 255, 0.06)'}
gap="$2"
$platform-web={{ cursor: 'pointer', transition: 'border-color 0.15s ease' }}
onClick={(e) => {
e.stopPropagation()
openLectureModal(week, lec, 'lecture')
}}
>
<XStack items="center" gap="$2" minW={0} flex={1}>
<Play size={10} color="var(--emerald-400)" style={{ flexShrink: 0 }} />
<Text
fontSize="$1"
color={isWatched ? 'var(--emerald-300)' : 'var(--white-80)'}
numberOfLines={1}
>
{lec}
</Text>
</XStack>
<Text
fontSize="$1"
fontFamily="$mono"
color={isWatched ? 'var(--emerald-300)' : 'var(--emerald-400)'}
$platform-web={{ whiteSpace: 'nowrap', flexShrink: 0 }}
>
{isWatched ? '✓ Attended' : 'Play Lecture →'}
</Text>
</XStack>
)
})}
</YStack>
{/* Required Academic Readings & Benchmarks */}
{week.readings && week.readings.length > 0 && (
<YStack gap="$2">
<Text fontSize="$1" fontWeight="600" color="var(--white)">
Required Academic Readings & Benchmarks:
</Text>
{week.readings.map((read, rIdx) => {
const isRead = (watchedLectures[selectedCourseSlug] || []).includes(read)
return (
<XStack
key={rIdx}
items="center"
justify="space-between"
p="$2"
px="$3"
rounded="var(--radius-sm)"
bg={isRead ? 'rgba(16, 185, 129, 0.08)' : 'rgba(255, 255, 255, 0.03)'}
borderWidth={1}
borderColor={isRead ? 'rgba(16, 185, 129, 0.3)' : 'rgba(255, 255, 255, 0.06)'}
gap="$2"
$platform-web={{ cursor: 'pointer', transition: 'border-color 0.15s ease' }}
onClick={(e) => {
e.stopPropagation()
openLectureModal(week, read, 'reading')
}}
>
<XStack items="center" gap="$2" minW={0} flex={1}>
<BookOpen size={10} color="var(--white-70)" style={{ flexShrink: 0 }} />
<Text
fontSize="$1"
color={isRead ? 'var(--emerald-300)' : 'var(--white-80)'}
numberOfLines={1}
>
{read}
</Text>
</XStack>
<Text
fontSize="$1"
fontFamily="$mono"
color={isRead ? 'var(--emerald-300)' : 'var(--white-70)'}
$platform-web={{ whiteSpace: 'nowrap', flexShrink: 0 }}
>
{isRead ? '✓ Read' : 'View Paper →'}
</Text>
</XStack>
)
})}
</YStack>
)}
{/* Lab Assignment & Autograder Submission */}
<XStack items="center" justify="space-between" pt="$2" flexWrap="wrap" gap="$2">
<Text fontSize="$1" color={isCompleted ? 'var(--emerald-400)' : 'var(--muted-foreground)'} fontWeight="600">
{isCompleted ? '✓ Laboratory Passed (100% Score)' : 'Lab Assignment: Ready for testing'}
</Text>
<Action
render="button"
onClick={(e) => {
e.stopPropagation()
handleCompleteLab(idx)
}}
disabled={isRunningCommand}
px={12}
py={6}
$platform-web={{ fontSize: '11px', whiteSpace: 'nowrap' }}
>
{isCompleted ? 'Re-Run Autograder' : 'Submit Lab & Pass Module →'}
</Action>
</XStack>
</YStack>
)}
</Box>
)
})}
</YStack>
{/* Module Pager Bar */}
<XStack items="center" justify="space-between" pt="$4" mt="$3" borderTopWidth={1} borderColor="rgba(255, 255, 255, 0.06)">
<Box
render="button"
onClick={() => activeWeekIndex > 0 && setActiveWeekIndex((p) => Math.max(0, p - 1))}
px="$2.5"
py="$1.5"
rounded="var(--radius-md)"
borderWidth={0}
bg="rgba(255, 255, 255, 0.05)"
$platform-web={{
cursor: activeWeekIndex === 0 ? 'not-allowed' : 'pointer',
opacity: activeWeekIndex === 0 ? 0.35 : 1,
fontSize: '12px',
color: 'var(--white)',
border: 'none',
}}
>
← Previous Module
</Box>
<XStack items="center" gap="$1.5">
{activeCourse.syllabus.map((_, i) => (
<Box
key={i}
render="button"
onClick={() => setActiveWeekIndex(i)}
width={24}
height={24}
rounded={9999}
bg={activeWeekIndex === i ? 'rgba(255, 255, 255, 0.15)' : 'rgba(255, 255, 255, 0.03)'}
borderWidth={1}
borderColor={activeWeekIndex === i ? 'rgba(255, 255, 255, 0.3)' : 'rgba(255, 255, 255, 0.06)'}
items="center"
justify="center"
$platform-web={{
cursor: 'pointer',
}}
>
<Text
fontSize="$1"
fontWeight="700"
fontFamily="$mono"
color={activeWeekIndex === i ? 'var(--white)' : 'var(--muted-foreground)'}
>
{i + 1}
</Text>
</Box>
))}
</XStack>
<Box
render="button"
onClick={() => activeWeekIndex < totalWeeks - 1 && setActiveWeekIndex((p) => Math.min(totalWeeks - 1, p + 1))}
px="$2.5"
py="$1.5"
rounded="var(--radius-md)"
borderWidth={0}
bg="rgba(255, 255, 255, 0.05)"
$platform-web={{
cursor: activeWeekIndex === totalWeeks - 1 ? 'not-allowed' : 'pointer',
opacity: activeWeekIndex === totalWeeks - 1 ? 0.35 : 1,
fontSize: '12px',
color: 'var(--white)',
border: 'none',
}}
>
Next Module →
</Box>
</XStack>
</Card>
</YStack>
)
})()}
{/* RIGHT: Live Sandbox Pod & Autograder Console */}
<YStack gap="$4" minW={0} width="100%">
<Card p={0} overflow="hidden" borderWidth={0} rounded="var(--radius-xl)" bg="rgba(255, 255, 255, 0.02)">
{/* Terminal Header & Mode Selector */}
<XStack
items="center"
justify="space-between"
px="$4"
py="$3"
bg="rgba(0, 0, 0, 0.5)"
borderBottomWidth={1}
borderColor="rgba(255, 255, 255, 0.06)"
flexWrap="wrap"
gap="$2"
>
<XStack items="center" gap="$2">
<Terminal size={15} color="var(--emerald-400)" />
<Text fontSize="$1" fontWeight="700" color="var(--white)" fontFamily="$mono">
pod-vsr-{studentHandle}
</Text>
<Chip px={6} py={1} fontSize="$1" fontFamily="$mono" color="var(--emerald-400)">
ONLINE
</Chip>
</XStack>
<XStack gap="$1">
{[
{ tab: 'terminal', label: 'Shell' },
{ tab: 'ast', label: 'AST Inspector' },
{ tab: 'grader', label: 'Autograder Logs' },
{ tab: 'metering', label: 'Spend' },
].map(({ tab, label }) => (
<Box
key={tab}
render="button"
onClick={() => setActiveTab(tab as any)}
px="$2"
py="$1"
rounded="var(--radius-sm)"
bg={activeTab === tab ? 'rgba(255, 255, 255, 0.15)' : 'transparent'}
$platform-web={{
cursor: 'pointer',
border: 'none',
color: activeTab === tab ? 'var(--white)' : 'var(--muted-foreground)',
fontSize: '11px',
fontFamily: 'monospace',
}}
>
{label}
</Box>
))}
</XStack>
</XStack>
{/* Tab 1: Terminal Shell */}
{activeTab === 'terminal' && (
<YStack p="$4" bg="var(--pure-black)" minH={380} justify="space-between">
<div className="portal-terminal-box">
{terminalHistory.map((line, idx) => (
<div
key={idx}
style={{
color:
line.startsWith(userTerminalPrompt.slice(0, 5)) || line.includes('@hanzo-sandbox:')
? 'var(--emerald-400)'
: line.includes('PASSED') || line.includes('[PASS]') || line.includes('[CONGRATULATIONS]')
? 'var(--emerald-300)'
: line.includes('[OK]') || line.includes('[RUST VERIFIED]')
? 'var(--white)'
: 'var(--muted-foreground)',
}}
>
{line}
</div>
))}
</div>
{/* Terminal Action Bar */}
<YStack gap="$2" pt="$3" borderTopWidth={1} borderColor="rgba(255, 255, 255, 0.06)">
<XStack items="center" justify="space-between" flexWrap="wrap" gap="$2">
<XStack items="center" gap="$1.5" flexWrap="wrap">
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">
Quick:
</Text>
{workspaceData.quickCommands.map((cmd) => (
<Box
key={cmd}
render="button"
onClick={() => !isRunningCommand && executeCommand(cmd)}
px="$2"
py="$1"
rounded="var(--radius-sm)"
bg="rgba(255, 255, 255, 0.05)"
borderWidth={0}
$platform-web={{
cursor: 'pointer',
fontSize: '11px',
fontFamily: 'monospace',
color: 'var(--white-80)',
border: 'none',
}}
hoverStyle={{ bg: 'rgba(255, 255, 255, 0.12)' }}
>
{cmd}
</Box>
))}
</XStack>
<Action
render="button"
onClick={() => handleCompleteLab(activeWeekIndex)}
disabled={isRunningCommand}
px={12}
py={5}
fill
$platform-web={{ fontSize: '11px', fontWeight: 600 }}
>
{isRunningCommand ? 'Evaluating...' : `Submit Lab ${activeWeekIndex + 1} →`}
</Action>
</XStack>
</YStack>
</YStack>
)}
{/* Tab 2: Code / Architecture Inspector */}
{activeTab === 'ast' && (
<YStack p="$4" bg="var(--pure-black)" minH={380} gap="$3">
<YStack gap={1}>
<Text fontSize="$2" fontWeight="700" color="var(--white)">
{workspaceData.codeInspectorTitle}
</Text>
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">
{workspaceData.codeInspectorSubtitle}
</Text>
</YStack>
<Box
p="$3"
rounded="var(--radius-md)"
bg="rgba(255, 255, 255, 0.03)"
borderWidth={0}
$platform-web={{ whiteSpace: 'pre-wrap', fontFamily: 'monospace', wordBreak: 'break-all' }}
>
{workspaceData.codeInspectorDiff.map((line, dIdx) => (
<Text
key={dIdx}
fontSize="$1"
fontFamily="$mono"
color={
line.type === 'del'
? 'var(--red-400)'
: line.type === 'add'
? 'var(--emerald-400)'
: 'var(--white-70)'
}
>
{line.text}
</Text>
))}
</Box>
<XStack items="center" gap="$2" mt="$1">
<CheckCircle2 size={14} color="var(--emerald-400)" />
<Text fontSize="$1" color="var(--emerald-300)" fontWeight="600">
{workspaceData.codeInspectorValidation}
</Text>
</XStack>
</YStack>
)}
{/* Tab 3: Autograder Logs */}
{activeTab === 'grader' && (
<YStack p="$4" bg="var(--pure-black)" minH={380} gap="$3">
<XStack items="center" justify="space-between">
<Text fontSize="$2" fontWeight="700" color="var(--white)">
{workspaceData.graderTitle}
</Text>
<Chip px={8} py={2} fontSize="$1" fontFamily="$mono" color="var(--emerald-400)">
ALL TESTS PASSING
</Chip>
</XStack>
{workspaceData.graderTasks.map((row) => (
<XStack
key={row.task}
items="center"
justify="space-between"
p="$2"
px="$3"
rounded="var(--radius-sm)"
bg="rgba(255, 255, 255, 0.03)"
borderWidth={0}
>
<XStack items="center" gap="$2" minW={0} flex={1}>
<CheckCircle2 size={14} color="var(--emerald-400)" style={{ flexShrink: 0 }} />
<Text fontSize="$1" fontFamily="$mono" color="var(--white)" $platform-web={{ wordBreak: 'break-word' }}>
{row.task}
</Text>
</XStack>
<XStack items="center" gap="$3" $platform-web={{ flexShrink: 0 }}>
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">
{row.time} · {row.cost}
</Text>
<Chip px={6} py={2} fontSize="$1" fontFamily="$mono" color="var(--emerald-400)">
{row.status}
</Chip>
</XStack>
</XStack>
))}
</YStack>
)}
{/* Tab 4: Spend Metering */}
{activeTab === 'metering' && (
<YStack p="$4" bg="var(--pure-black)" minH={380} gap="$4">
<Text fontSize="$2" fontWeight="700" color="var(--white)">
Compute Credit Rebate Metering
</Text>
<Grid columns={{ min: 140, max: 2 }} gap={12}>
<YStack p="$3" rounded="var(--radius-md)" bg="rgba(255, 255, 255, 0.03)" gap="$1">
<Text fontSize="$1" color="var(--muted-foreground)">Day 1 Deposit</Text>
<Text fontSize="$4" fontWeight="800" color="var(--white)">${activeCourse.rebateCredits}.00 USD</Text>
</YStack>
<YStack p="$3" rounded="var(--radius-md)" bg="rgba(255, 255, 255, 0.03)" gap="$1">
<Text fontSize="$1" color="var(--muted-foreground)">Balance Remaining</Text>
<Text fontSize="$4" fontWeight="800" color="var(--emerald-400)">
${(activeCourse.rebateCredits * 0.75).toFixed(2)} USD
</Text>
</YStack>
</Grid>
<YStack gap="$2">
<Text fontSize="$1" color="var(--muted-foreground)">Resource Usage Breakdown:</Text>
{workspaceData.resourceBreakdown.map((res, rIdx) => (
<XStack key={rIdx} justify="space-between">
<Text fontSize="$1" color="var(--white)">{res.item}</Text>
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">{res.cost}</Text>
</XStack>
))}
</YStack>
</YStack>
)}
</Card>
{/* Subtle Accelerator Controls */}
<XStack items="center" justify="space-between" px="$2">
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">
TEST ACCELERATOR:
</Text>
<XStack items="center" gap="$2">
<Box
render="button"
onClick={handleFastTrackAllLabs}
px="$2"
py="$1"
rounded="var(--radius-sm)"
bg="rgba(16, 185, 129, 0.1)"
borderWidth={0}
$platform-web={{
cursor: 'pointer',
fontSize: '11px',
color: 'var(--emerald-300)',
fontFamily: 'monospace',
border: 'none',
}}
>
⚡ Fast-Track All Labs
</Box>
<Box
render="button"
onClick={handleResetCourseProgress}
px="$2"
py="$1"
rounded="var(--radius-sm)"
bg="rgba(255, 255, 255, 0.05)"
borderWidth={0}
$platform-web={{
cursor: 'pointer',
fontSize: '11px',
color: 'var(--muted-foreground)',
fontFamily: 'monospace',
border: 'none',
}}
>
↺ Reset Progress
</Box>
</XStack>
</XStack>
</YStack>
</div>
{/* ── Capstone Project Defense & Certification Card ── */}
<Box mt="$6">
<Card
p="$5"
bg="rgba(255, 255, 255, 0.02)"
borderWidth={0}
rounded="var(--radius-xl)"
$platform-web={{
boxShadow: isDefenseUnlocked ? '0 0 24px rgba(16, 185, 129, 0.15)' : 'none',
}}
>
<XStack items="center" justify="space-between" flexWrap="wrap" gap="$3" mb="$3">
<XStack items="center" gap="$2.5">
<Box p="$2" rounded="var(--radius-md)" bg="rgba(16, 185, 129, 0.15)">
<Award size={20} color="var(--emerald-400)" />
</Box>
<YStack gap={1}>
<Text fontSize="$3" fontWeight="800" color="var(--white)">
Capstone Defense & W3C Credential Issuance
</Text>
<Text fontSize="$1" color="var(--muted-foreground)">
Target Credential: {activeCourse.credentialFull} ({activeCourse.credential})
</Text>
</YStack>
</XStack>
<Chip
px={10}
py={3}
fontSize="$1"
fontFamily="$mono"
color={isDegreeAwarded ? 'var(--emerald-400)' : isDefenseUnlocked ? 'var(--emerald-400)' : 'var(--amber-400)'}
>
{isDegreeAwarded ? 'DEGREE AWARDED' : isDefenseUnlocked ? 'QUALIFICATIONS MET' : 'PREREQUISITES PENDING'}
</Chip>
</XStack>
<Text fontSize="$2" color="var(--white-80)" $platform-web={{ lineHeight: 22 }} mb="$4">
{activeCourse.capstone}
</Text>
{isDegreeAwarded ? (
<XStack items="center" justify="space-between" flexWrap="wrap" gap="$3" p="$3" rounded="var(--radius-md)" bg="rgba(16, 185, 129, 0.15)" borderWidth={0}>
<Text fontSize="$1" color="var(--emerald-300)" fontWeight="600">
🎉 Passed with 100% Distinction! Verified W3C Credential minted on Lux Chain.
</Text>
<Action
render="button"
onClick={() => setShowCredentialModal(true)}
px={14}
py={7}
fill
$platform-web={{ fontSize: '12px' }}
>
View & Share {activeCourse.credential} Certificate →
</Action>
</XStack>
) : isDefenseUnlocked ? (
<XStack items="center" justify="space-between" flexWrap="wrap" gap="$3" p="$3" rounded="var(--radius-md)" bg="rgba(16, 185, 129, 0.12)" borderWidth={0}>
<Text fontSize="$1" color="var(--emerald-300)" fontWeight="600">
All {totalWeeks} modules passed! You meet all qualifications to defend your capstone.
</Text>
<Action
render="button"
onClick={handleDefendCapstone}
disabled={isDefending}
px={16}
py={8}
fill
$platform-web={{ fontSize: '13px' }}
>
{isDefending ? 'Evaluating Defense in Pod...' : `Defend Capstone & Issue ${activeCourse.credential} Degree →`}
</Action>
</XStack>
) : (
<XStack items="center" justify="space-between" flexWrap="wrap" gap="$2" pt="$2" borderTopWidth={1} borderColor="rgba(255, 255, 255, 0.06)">
<Text fontSize="$1" color="var(--muted-foreground)">
Complete the remaining {totalWeeks - currentCompleted.length} laboratory modules to unlock the official Capstone Defense.
</Text>
<Action
render="button"
onClick={() => handleCompleteLab(activeWeekIndex)}
disabled={isRunningCommand}
px={12}
py={6}
$platform-web={{ fontSize: '11px' }}
>
Run Current Lab ({activeCourse.syllabus[activeWeekIndex]?.code}) →
</Action>
</XStack>
)}
</Card>
</Box>
</>
) : (
/* ── Course Locked / Enrollment Required View ── */
<YStack gap="$5">
{/* Main Paywall Card */}
<Card
p={32}
bg="var(--pure-black)"
borderWidth={1}
borderColor="var(--neutral-800)"
rounded="var(--radius-xl)"
>
<YStack gap="$6">
{/* Header Badge & Title */}
<XStack items="flex-start" justify="space-between" flexWrap="wrap" gap="$4">
<YStack gap="$2" maxW={720}>
<XStack items="center" gap="$2" flexWrap="wrap" mb="$1">
<Chip px={8} py={3} fontSize="$1" fontFamily="$mono" color="var(--amber-400)">
<Lock size={11} style={{ marginRight: 4, verticalAlign: 'middle', display: 'inline' }} />
COURSE ACCESS LOCKED
</Chip>
<Chip px={8} py={3} fontSize="$1" fontFamily="$mono" color="var(--white-70)">
{activeCourse.code} · {activeCourse.units}.0 UNITS · {activeCourse.credential}
</Chip>
</XStack>
<Text fontSize="$5" fontWeight="800" color="var(--white)">
Enrollment Required to Access {activeCourse.code}: {activeCourse.title}
</Text>
<Text fontSize="$2" color="var(--muted-foreground)">
You are signed in as <strong style={{ color: 'var(--white)' }}>{studentName}</strong> (<span style={{ fontFamily: 'monospace', color: 'var(--white-80)' }}>@{studentHandle}</span>). Your account does not hold an active enrollment license for this track. Dedicated Hanzo Visor microVM sandbox pods, AST autograder evaluations, and on-chain degree issuance are provisioned exclusively for enrolled students.
</Text>
</YStack>
{/* Price Tag & Action */}
<YStack
gap="$2"
p="$4"
rounded="var(--radius-lg)"
bg="var(--card)"
borderWidth={1}
borderColor="var(--border)"
minW={260}
$platform-web={{ textAlign: 'left' }}
>
<Text fontSize="$1" fontFamily="$mono" color="var(--muted-foreground)">
TRACK TUITION
</Text>
<XStack items="baseline" gap="$1">
<Text fontSize="$6" fontWeight="800" color="var(--white)">
${activeCourse.price}
</Text>
<Text fontSize="$1" color="var(--muted-foreground)">
USD
</Text>
</XStack>
<XStack items="center" gap="$1.5">
<Coins size={12} color="var(--emerald-400)" />
<Text fontSize="$1" color="var(--emerald-400)" fontFamily="$mono">
+${activeCourse.rebateCredits} Credits (25% Rebate)
</Text>
</XStack>
<XStack items="center" gap="$1.5">
<Zap size={12} color="var(--emerald-400)" />
<Text fontSize="$1" color="var(--emerald-400)" fontFamily="$mono">
+ $50 usage credit for free
</Text>
</XStack>
<Action
render="a"
href={courseCheckoutUrl(activeCourse.slug)}
fill
py={12}
mt="$2"
$platform-web={{
fontSize: '14px',
fontWeight: 700,
textAlign: 'center',
textDecoration: 'none',
}}
>
Enroll in {activeCourse.code} →
</Action>
</YStack>
</XStack>
{/* What Enrollment Unlocks - Benefit Grid */}
<YStack gap="$3" pt="$3" borderTopWidth={1} borderColor="var(--border)">
<Text fontSize="$1" fontFamily="$mono" color="var(--muted-foreground)">
WHAT ENROLLMENT IN THIS TRACK UNLOCKS:
</Text>
<Grid columns={{ min: 240, max: 4 }} gap={16}>
<Card p="$3.5" bg="var(--card)" borderWidth={0} rounded="var(--radius-md)">
<XStack items="center" gap="$2" mb="$1.5">
<Cpu size={16} color="var(--emerald-400)" />
<Text fontSize="$2" fontWeight="700" color="var(--white)">
Hanzo Visor Pod
</Text>
</XStack>
<Text fontSize="$1" color="var(--muted-foreground)" $platform-web={{ lineHeight: 18 }}>
Dedicated GPU microVM sandbox container with PyTorch, CUDA, AST tree-sitter, and SWE-bench harness.
</Text>
</Card>
<Card p="$3.5" bg="var(--card)" borderWidth={0} rounded="var(--radius-md)">
<XStack items="center" gap="$2" mb="$1.5">
<Terminal size={16} color="var(--emerald-400)" />
<Text fontSize="$2" fontWeight="700" color="var(--white)">
Autonomous Autograder
</Text>
</XStack>
<Text fontSize="$1" color="var(--muted-foreground)" $platform-web={{ lineHeight: 18 }}>
Real-time test suite evaluation with automated AST assertion grading and continuous verification.
</Text>
</Card>
<Card p="$3.5" bg="var(--card)" borderWidth={0} rounded="var(--radius-md)">
<XStack items="center" gap="$2" mb="$1.5">
<Award size={16} color="var(--emerald-400)" />
<Text fontSize="$2" fontWeight="700" color="var(--white)">
W3C Degree Credential
</Text>
</XStack>
<Text fontSize="$1" color="var(--muted-foreground)" $platform-web={{ lineHeight: 18 }}>
Sovereign cryptographic diploma registered on Lux Chain, signed directly to your Hanzo student DID.
</Text>
</Card>
<Card p="$3.5" bg="var(--card)" borderWidth={0} rounded="var(--radius-md)">
<XStack items="center" gap="$2" mb="$1.5">
<Coins size={16} color="var(--emerald-400)" />
<Text fontSize="$2" fontWeight="700" color="var(--white)">
Tuition Rebate & Grant
</Text>
</XStack>
<Text fontSize="$1" color="var(--muted-foreground)" $platform-web={{ lineHeight: 18 }}>
+ $50 free compute grant plus 25% tuition rebate credited immediately into your Hanzo Cloud wallet.
</Text>
</Card>
</Grid>
</YStack>
{/* Already Enrolled Courses Switcher */}
{session.enrolledClasses.length > 0 && (
<YStack gap="$2" pt="$3" borderTopWidth={1} borderColor="var(--border)">
<Text fontSize="$1" fontFamily="$mono" color="var(--emerald-400)">
YOUR ACTIVELY ENROLLED COURSES ({session.enrolledClasses.length}):
</Text>
<XStack gap="$2" flexWrap="wrap">
{session.enrolledClasses.map((enrolledSlug) => {
const enrolledCourseData = UNIVERSITY_COURSES.find((c) => c.slug === enrolledSlug)
if (!enrolledCourseData) return null
return (
<Box
key={enrolledSlug}
render="button"
onClick={() => setSelectedCourseSlug(enrolledSlug)}
px="$3"
py="$2"
rounded="var(--radius-md)"
bg="var(--card)"
borderWidth={1}
borderColor="var(--emerald-700)"
$platform-web={{
cursor: 'pointer',
display: 'inline-flex',
flexDirection: 'row',
alignItems: 'center',
gap: '6px',
}}
>
<CheckCircle2 size={13} color="var(--emerald-400)" />
<Text fontSize="$1" fontWeight="600" color="var(--white)">
Open {enrolledCourseData.code}: {enrolledCourseData.title} →
</Text>
</Box>
)
})}
</XStack>
</YStack>
)}
</YStack>
</Card>
{/* Curriculum Preview (Locked Modules) */}
<Box>
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono" mb="$3">
CURRICULUM MODULES INCLUDED IN {activeCourse.code} (PREVIEW):
</Text>
<Grid columns={{ min: 280, max: 2 }} gap={16}>
{activeCourse.syllabus.map((week) => (
<Card
key={week.week}
p="$4"
bg="var(--card)"
borderWidth={1}
borderColor="var(--border)"
rounded="var(--radius-lg)"
>
<XStack items="center" justify="space-between" mb="$2">
<Chip px={6} py={2} fontSize="$1" fontFamily="$mono" color="var(--muted-foreground)">
WEEK 0{week.week} · {week.code}
</Chip>
<XStack items="center" gap="$1">
<Lock size={12} color="var(--muted-foreground)" />
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">
LOCKED
</Text>
</XStack>
</XStack>
<Text fontSize="$3" fontWeight="700" color="var(--white)" mb="$1">
{week.title}
</Text>
<Text fontSize="$1" color="var(--muted-foreground)" mb="$3" $platform-web={{ lineHeight: 18 }}>
{week.summary}
</Text>
<XStack gap="$1.5" flexWrap="wrap">
{week.lab && (
<Chip px={6} py={1} fontSize={10} fontFamily="$mono">
Lab: {week.lab}
</Chip>
)}
{week.lectures.slice(0, 2).map((lecture, i) => (
<Chip key={i} px={6} py={1} fontSize={10} fontFamily="$mono">
{lecture}
</Chip>
))}
</XStack>
</Card>
))}
</Grid>
</Box>
</YStack>
)}
</Band>
{/* ── W3C Verifiable Credential Modal ── */}
{showCredentialModal && (
<Box
position="fixed"
t={0}
l={0}
width="100vw"
height="100vh"
bg="rgba(0, 0, 0, 0.85)"
items="center"
justify="center"
p="$4"
$platform-web={{ backdropFilter: 'blur(12px)', zIndex: 9999 }}
>
<Box
width="100%"
maxW={720}
bg="var(--pure-black)"
rounded="var(--radius-2xl)"
borderWidth={1}
borderColor="rgba(255, 255, 255, 0.1)"
p="$6"
position="relative"
maxH="90vh"
overflow="scroll"
>
{/* Modal Header */}
<XStack items="center" justify="space-between" mb="$4">
<XStack items="center" gap="$2">
<Award size={20} color="var(--emerald-400)" />
<Text fontSize="$3" fontWeight="700" color="var(--white)">
W3C Verifiable Credential · {activeCourse.credential}
</Text>
</XStack>
<Box
render="button"
onClick={() => setShowCredentialModal(false)}
p="$1"
rounded="var(--radius-sm)"
$platform-web={{
background: 'transparent',
border: 'none',
color: 'var(--muted-foreground)',
cursor: 'pointer',
fontSize: '18px',
}}
>
✕
</Box>
</XStack>
{/* Official Certificate Card */}
<Box
p="$6"
rounded="var(--radius-xl)"
bg="rgba(255, 255, 255, 0.03)"
borderWidth={0}
gap="$4"
position="relative"
overflow="hidden"
>
<Box
position="absolute"
t={-40}
r={-40}
width={140}
height={140}
rounded={9999}
$platform-web={{ backgroundColor: 'color-mix(in srgb, var(--emerald-500) 12%, transparent)' }}
pointerEvents="none"
/>
<XStack items="center" justify="space-between">
<Text fontSize="$1" fontFamily="$mono" color="var(--muted-foreground)">
HANZO UNIVERSITY · RESEARCH FOUNDATION
</Text>
<Chip px={8} py={2} fontSize="$1" fontFamily="$mono" color="var(--emerald-400)">
VERIFIED & SIGNED ON-CHAIN
</Chip>
</XStack>
<YStack gap="$1" my="$2">
<Text fontSize="$1" color="var(--muted-foreground)">
THIS CERTIFIES THAT
</Text>
<Text fontSize="$5" fontWeight="800" color="var(--white)">
{studentName}
</Text>
<Text fontSize="$1" color="var(--muted-foreground)">
HAS COMPLETED ALL COURSEWORK SPECIFICATIONS, PASSED THE AUTOGRADER CI SUITE, AND DEFENDED THE CAPSTONE FOR
</Text>
<Text fontSize="$3" fontWeight="700" color="var(--emerald-300)" mt="$1">
{activeCourse.credentialFull} ({activeCourse.credential})
</Text>
<Text fontSize="$1" color="var(--white-80)">
Course: {activeCourse.code} · Academic Units: {activeCourse.units}.0 · Level: {activeCourse.level}
</Text>
</YStack>
{/* Cryptographic Signature Box */}
<Box
p="$3"
rounded="var(--radius-md)"
bg="rgba(0, 0, 0, 0.6)"
borderWidth={0}
gap="$1"
>
<XStack justify="space-between">
<Text color="var(--muted-foreground)">DID Subject:</Text>
<Text color="var(--white)" fontFamily="$mono">
did:hanzo:student:{studentHandle}
</Text>
</XStack>
<XStack justify="space-between">
<Text color="var(--muted-foreground)">Issuer DID:</Text>
<Text color="var(--white)" fontFamily="$mono">
did:hanzo:university:accreditation
</Text>
</XStack>
<XStack justify="space-between">
<Text color="var(--muted-foreground)">Status:</Text>
<Text color="var(--emerald-400)" fontWeight="600">
VERIFIED & CERTIFIED (GRADE: 100% PASS WITH DISTINCTION)
</Text>
</XStack>
<XStack justify="space-between">
<Text color="var(--muted-foreground)">Lux Chain Proof:</Text>
<Text color="var(--emerald-400)" fontFamily="$mono">
0x7f4a8b1c...99e2e89b (Block #9,418,290)
</Text>
</XStack>
</Box>
<XStack items="center" justify="space-between" pt="$2">
<YStack>
<Text fontSize="$1" color="var(--muted-foreground)">Course Director & Evaluator</Text>
<Text fontSize="$1" fontWeight="600" color="var(--white)">{activeCourse.instructor.name}</Text>
<Text fontSize="$1" color="var(--muted-foreground)">{activeCourse.instructor.role}</Text>
</YStack>
<YStack items="flex-end">
<Text fontSize="$1" color="var(--muted-foreground)">Issuance Date</Text>
<Text fontSize="$1" fontWeight="600" color="var(--white)">October 2026</Text>
<Text fontSize="$1" color="var(--emerald-400)">W3C VC 2.0 Standard</Text>
</YStack>
</XStack>
</Box>
{/* Modal Actions */}
<XStack items="center" justify="space-between" mt="$4" flexWrap="wrap" gap="$3">
<XStack items="center" gap="$2">
<Box
render="button"
onClick={() => {
const jsonLd = JSON.stringify(
{
'@context': [
'https://www.w3.org/ns/credentials/v2',
'https://hanzo.university/credentials/v1',
],
id: `urn:uuid:hanzo-cert-2026-${activeCourse.code.replace(/\s+/g, '')}-9821a`,
type: ['VerifiableCredential', 'HanzoUniversityDegree'],
issuer: {
id: 'did:hanzo:university:accreditation',
name: 'Hanzo University Research Foundation',
},
validFrom: new Date().toISOString(),
credentialSubject: {
id: `did:hanzo:student:${studentHandle}`,
name: studentName,
course: `${activeCourse.code}: ${activeCourse.title}`,
degree: `${activeCourse.credentialFull} (${activeCourse.credential})`,
units: activeCourse.units,
grade: 'Pass with Distinction (100% Autograder Score)',
},
proof: {
type: 'Ed25519Signature2020',
verificationMethod: 'did:hanzo:university:accreditation#key-1',
proofValue: '0x7f4a8b1c99e2e89b3f4a1c2d88e0a1b2c3d4e5f6',
},
},
null,
2,
)
navigator.clipboard.writeText(jsonLd)
setFeedbackToast('W3C JSON-LD Credential copied to clipboard!')
setTimeout(() => setFeedbackToast(null), 3000)
}}
px="$3"
py="$2"
rounded="var(--radius-md)"
bg="rgba(255, 255, 255, 0.06)"
borderWidth={0}
$platform-web={{
cursor: 'pointer',
fontSize: '12px',
color: 'var(--white)',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: '6px',
border: 'none',
}}
>
<Copy size={13} />
Copy JSON-LD
</Box>
<Box
render="button"
onClick={() => {
const certSummary = `========================================================================\n` +
` HANZO UNIVERSITY DEGREE CERTIFICATE \n` +
`========================================================================\n` +
`Student: ${studentName}\n` +
`Student DID: did:hanzo:student:${studentHandle}\n` +
`Degree: ${activeCourse.credentialFull} (${activeCourse.credential})\n` +
`Course: ${activeCourse.code}: ${activeCourse.title}\n` +
`Academic Units: ${activeCourse.units}.0 Units\n` +
`Level: ${activeCourse.level}\n` +
`Instructor: ${activeCourse.instructor.name} (${activeCourse.instructor.role})\n` +
`Issuance Date: October 2026\n` +
`Blockchain Proof: Lux Chain Block #9,418,290 (Ed25519 Verified)\n` +
`W3C Standard: Verifiable Credentials Data Model v2.0\n` +
`========================================================================\n`
const blob = new Blob([certSummary], { type: 'text/plain' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `Hanzo-${activeCourse.credential}-Certificate-${studentHandle}.txt`
a.click()
URL.revokeObjectURL(url)
setFeedbackToast('Certificate downloaded successfully!')
setTimeout(() => setFeedbackToast(null), 3000)
}}
px="$3"
py="$2"
rounded="var(--radius-md)"
bg="rgba(255, 255, 255, 0.06)"
borderWidth={0}
$platform-web={{
cursor: 'pointer',
fontSize: '12px',
color: 'var(--white)',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: '6px',
border: 'none',
}}
>
<Download size={13} />
Download (.txt)
</Box>
<Box
render="button"
onClick={handleDownloadSvg}
px="$3"
py="$2"
rounded="var(--radius-md)"
bg="rgba(255, 255, 255, 0.06)"
borderWidth={0}
$platform-web={{
cursor: 'pointer',
fontSize: '12px',
color: 'var(--white)',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: '6px',
border: 'none',
}}
>
<Download size={13} />
SVG Badge (.svg)
</Box>
<Box
render="button"
onClick={() => {
const verifyUrl = `${typeof window !== 'undefined' ? window.location.origin : 'https://hanzo.university'}/verify?id=${activeCourse.credential}-2026-9821&student=${studentHandle}`
navigator.clipboard.writeText(verifyUrl)
setFeedbackToast('Verification URL copied to clipboard!')
setTimeout(() => setFeedbackToast(null), 3000)
}}
px="$3"
py="$2"
rounded="var(--radius-md)"
bg="rgba(255, 255, 255, 0.06)"
borderWidth={0}
$platform-web={{
cursor: 'pointer',
fontSize: '12px',
color: 'var(--white)',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: '6px',
border: 'none',
}}
>
<Copy size={13} />
Copy Link
</Box>
<Link
href={`/verify?id=${activeCourse.credential}-2026-9821&student=${studentHandle}`}
target="_blank"
style={{ textDecoration: 'none' }}
>
<Box
px="$3"
py="$2"
rounded="var(--radius-md)"
bg="rgba(16, 185, 129, 0.15)"
borderWidth={0}
$platform-web={{
cursor: 'pointer',
fontSize: '12px',
color: 'var(--emerald-300)',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: '6px',
fontWeight: 600,
}}
>
<ExternalLink size={13} />
Verify on Public Registry ↗
</Box>
</Link>
</XStack>
<Action
render="button"
onClick={() => setShowCredentialModal(false)}
px={16}
py={8}
fill
>
Close & Return to Workspace
</Action>
</XStack>
</Box>
</Box>
)}
{/* ── Interactive Lecture & Reading Viewer Modal ── */}
{activeLectureModal && (
<Box
position="fixed"
t={0}
l={0}
width="100vw"
height="100vh"
bg="rgba(0, 0, 0, 0.85)"
items="center"
justify="center"
p="$4"
$platform-web={{ backdropFilter: 'blur(12px)', zIndex: 9999 }}
>
<Box
width="100%"
maxW={780}
bg="var(--pure-black)"
rounded="var(--radius-2xl)"
borderWidth={1}
borderColor="rgba(255, 255, 255, 0.1)"
p="$6"
position="relative"
maxH="92vh"
overflow="scroll"
>
{/* Modal Header */}
<XStack items="center" justify="space-between" mb="$4">
<XStack items="center" gap="$2">
{activeLectureModal.type === 'lecture' ? (
<Play size={18} color="var(--emerald-400)" />
) : (
<BookOpen size={18} color="var(--emerald-400)" />
)}
<YStack>
<XStack items="center" gap="$2">
<Text fontSize="$2" fontWeight="700" color="var(--white)">
{activeLectureModal.title}
</Text>
<Chip px={6} py={2} fontSize="$1" fontFamily="$mono" color="var(--emerald-400)">
{activeLectureModal.type === 'lecture' ? 'STREAM · 4K 60FPS' : 'ARXIV / SPEC PAPER'}
</Chip>
</XStack>
<Text fontSize="$1" color="var(--muted-foreground)">
{activeCourse.code} · {activeLectureModal.weekCode}: {activeLectureModal.weekTitle} · Instructor: {activeLectureModal.instructor}
</Text>
</YStack>
</XStack>
<Box
render="button"
onClick={() => {
setActiveLectureModal(null)
setIsPlayingMedia(false)
}}
p="$1"
rounded="var(--radius-sm)"
$platform-web={{
background: 'transparent',
border: 'none',
color: 'var(--muted-foreground)',
cursor: 'pointer',
fontSize: '18px',
}}
>
✕
</Box>
</XStack>
{/* Video or Document Viewer Panel */}
{activeLectureModal.type === 'lecture' ? (
<YStack gap="$4">
{/* Simulated Video Player Screen */}
<Box
width="100%"
height={260}
rounded="var(--radius-lg)"
bg="#050505"
borderWidth={0}
position="relative"
overflow="hidden"
justify="center"
items="center"
>
<Box
position="absolute"
inset={0}
$platform-web={{
background: 'radial-gradient(circle at center, rgba(16, 185, 129, 0.15) 0%, rgba(0, 0, 0, 0.95) 75%)',
}}
/>
<YStack items="center" gap="$3" $platform-web={{ zIndex: 2 }}>
<Box
render="button"
onClick={() => setIsPlayingMedia(!isPlayingMedia)}
width={64}
height={64}
rounded={9999}
bg={isPlayingMedia ? 'var(--white)' : 'var(--emerald-500)'}
items="center"
justify="center"
$platform-web={{
cursor: 'pointer',
border: 'none',
boxShadow: '0 0 30px rgba(16, 185, 129, 0.4)',
transition: 'transform 0.15s ease',
}}
hoverStyle={{ transform: 'scale(1.05)' }}
>
{isPlayingMedia ? (
<Pause size={28} color="var(--pure-black)" />
) : (
<Play size={28} color="var(--pure-black)" style={{ marginLeft: 3 }} />
)}
</Box>
<Text fontSize="$1" fontFamily="$mono" color="var(--white-80)">
{isPlayingMedia ? 'STREAMING ACTIVE (1080p WebRTC)' : 'CLICK TO COMMENCE LECTURE'}
</Text>
</YStack>
{/* Player Controls Bar */}
<Box
position="absolute"
b={0}
l={0}
r={0}
p="$3"
bg="rgba(0, 0, 0, 0.75)"
borderTopWidth={1}
borderColor="rgba(255, 255, 255, 0.1)"
>
<XStack items="center" justify="space-between">
<XStack items="center" gap="$3">
<Box
render="button"
onClick={() => setIsPlayingMedia(!isPlayingMedia)}
$platform-web={{ background: 'transparent', border: 'none', cursor: 'pointer', color: 'var(--white)' }}
>
{isPlayingMedia ? <Pause size={16} /> : <Play size={16} />}
</Box>
<Volume2 size={16} color="var(--muted-foreground)" />
<Text fontSize="$1" color="var(--white-70)" fontFamily="$mono">
{isPlayingMedia ? '14:28' : '00:00'} / 38:00
</Text>
</XStack>
<XStack items="center" gap="$2">
{[1, 1.25, 1.5, 2].map((rate) => (
<Box
key={rate}
render="button"
onClick={() => setPlaybackRate(rate)}
px="$2"
py="$1"
rounded="var(--radius-sm)"
bg={playbackRate === rate ? 'var(--white)' : 'transparent'}
$platform-web={{
cursor: 'pointer',
border: 'none',
color: playbackRate === rate ? 'var(--pure-black)' : 'var(--muted-foreground)',
fontSize: '11px',
fontFamily: 'monospace',
fontWeight: 600,
}}
>
{rate}x
</Box>
))}
</XStack>
</XStack>
</Box>
</Box>
{/* Lecture Syllabus Notes & Code Excerpt */}
<Box p="$4" rounded="var(--radius-md)" bg="rgba(255, 255, 255, 0.03)" borderWidth={0}>
<Text fontSize="$1" fontWeight="700" color="var(--white)" mb="$2">
Key Architectural Takeaways & Proofs
</Text>
<Text fontSize="$1" color="var(--muted-foreground)" lineHeight="$2" mb="$3">
{activeLectureModal.summary}
</Text>
<Box
p="$3"
rounded="var(--radius-sm)"
bg="rgba(0, 0, 0, 0.5)"
borderWidth={0}
$platform-web={{ fontFamily: 'monospace', fontSize: '11px', color: 'var(--emerald-300)' }}
>
{activeCourse.slug === 'agentic-coding'
? `# Invariant: Zero syntax regressions across all AST tree mutations\nassert parser.validate(ast_tree, strict=True) == True\nsandbox.execute_safely(diff, budget_ceiling_usd=0.05)`
: activeCourse.slug === 'reinforcement-learning'
? `# Invariant: Group Relative Policy Optimization (GRPO) advantage\nadv = (rewards - rewards.mean()) / (rewards.std() + 1e-8)\nloss = -min(ratio * adv, clip(ratio, 1-eps, 1+eps) * adv).mean()`
: `# Invariant: Zero-copy Cap'n Proto buffer alignment over Unix domain socket\nlet header = zap::protocol::FrameHeader::validate(&slice[..32])?;\nkv_cache.allocate_paged_blocks(seq_id, prompt_tokens)?;`}
</Box>
</Box>
</YStack>
) : (
<YStack gap="$4">
<Box p="$4" rounded="var(--radius-md)" bg="rgba(255, 255, 255, 0.03)" borderWidth={0}>
<XStack items="center" justify="space-between" mb="$2">
<Text fontSize="$2" fontWeight="700" color="var(--white)">
Academic Preprint & Architecture Specification
</Text>
<Chip px={6} py={1} fontSize="$1" fontFamily="$mono" color="var(--emerald-400)">
DOI: 10.48550/arXiv.2610.09821
</Chip>
</XStack>
<Text fontSize="$1" color="var(--white-80)" lineHeight="$2" mb="$3">
Abstract: This curriculum monograph establishes the formal methodology used throughout Hanzo University's cleanroom testing suite. We demonstrate how autonomous verification harnesses evaluate model rollouts in Hanzo Visor microVMs, proving mathematically bounded runtime execution.
</Text>
<Text fontSize="$1" color="var(--muted-foreground)" lineHeight="$2">
Required reading for the {activeLectureModal.weekCode} laboratory. Students must implement the algorithms discussed in Section 3 and defend the performance metrics during capstone defense.
</Text>
</Box>
</YStack>
)}
{/* Modal Actions */}
<XStack items="center" justify="space-between" mt="$4" pt="$3" borderTopWidth={1} borderColor="rgba(255, 255, 255, 0.06)">
<Text fontSize="$1" color="var(--muted-foreground)">
Module Progress: Recorded to your student DID account
</Text>
<XStack items="center" gap="$2">
<Box
render="button"
onClick={() => {
setActiveLectureModal(null)
setIsPlayingMedia(false)
}}
px="$3"
py="$2"
rounded="var(--radius-md)"
bg="rgba(255, 255, 255, 0.06)"
borderWidth={0}
$platform-web={{ cursor: 'pointer', fontSize: '12px', color: 'var(--white)', border: 'none' }}
>
Dismiss
</Box>
<Action
render="button"
onClick={handleMarkLectureWatched}
px={14}
py={8}
fill
$platform-web={{ fontSize: '12px' }}
>
Mark as Attended & Completed ✓
</Action>
</XStack>
</XStack>
</Box>
</Box>
)}
</Box>
)
}