# Project export: Yuvth.raw

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: OpenAI Build Week
- Tagline: Your Semester has a Digital Twin. A proactive academic command center powered by AI Intelligence.
- Devpost: https://devpost.com/software/yuvth
- GitHub: https://github.com/prudhvi1611/Semester-Copilot
- Video: https://www.youtube.com/embed/-SBBLuw9USc?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Prudhvi Sai Sabavat (6 commits)

## Devpost submission (written by the team)

### Inspiration

We built Semester Copilot because we were tired of the usual semester burnout. Juggling deadlines across different PDF syllabuses, missing important messages in WhatsApp study groups, and manually calculating our attendance to see if we could skip a morning class was taking up too much time. Managing college shouldn't be harder than the classes themselves. We didn't want another generic to-do list; we wanted an app that actually helps us plan and warns us before we make mistakes.

### What it does

Semester Copilot brings your academic life into one dashboard. It focuses on four main areas: 1. The Smart Skip Engine Instead of just tracking attendance, it tells you if it's actually safe to skip. We built a logic engine that calculates a risk score \(R\) by looking at how frequently a topic appears in past exams (\(F_{exam}\)) and its weight in the syllabus (\(W_{topic}\)): \[ R = w_1 \cdot F_{exam} + w_2 \cdot W_{topic} - (A_{target} - A_{proj}) \] If your projected attendance (\(A_{proj}\)) drops below 75%, or if the risk score for tomorrow's lecture gets too high, the app gives you a warning that you shouldn't skip. 2. Classroom Chat We added a WebSocket-based chat room for each subject. If your study group is stuck on a concept, you can type @AI in the chat. We hooked this up to Artificial Intelligence, which reads the specific PDF notes for that class and drops the answer directly into the chat, along with a link to the exact page it got the info from. 3. AI Notes Studio You can upload a raw lecture PDF, and the app extracts the text to automatically generate summaries, structured notes, flashcards, and quick practice quizzes based on the professor's material. 4. Exam Prep To help with finals, we wrote a script that parses past question papers. It calculates the probability \(P(T_i)\) of a specific topic \(T_i\) showing up based on past years \(Y\): \[ P(T_i) = \frac{1}{Y} \sum_{k=1}^{Y} \text{Count}(T_i, Y_k) \] It uses this to generate a heat map so you know exactly which chapters to prioritize during revision.

### How we built it

We wanted to build something that didn't look like a standard, clunky hackathon prototype. Frontend: We used Next.js and Tailwind CSS. We utilized Shadcn UI for our base components and added Framer Motion for some clean transitions. Backend: We built the API using Python (FastAPI) and used a local SQLite database to keep things lightweight and fast for the MVP. AI & RAG: We used Artificial Intelligence LLM's. For the document retrieval, we chunk the uploaded PDFs, generate embeddings, and use standard cosine similarity to find the most relevant context for the user's chat query: \[ \text{Similarity}(Q, V_d) = \frac{Q \cdot V_d}{|Q| |V_d|} \] Real-time: We implemented standard WebSockets for the live study group chats.

### Challenges we ran into

Getting the RAG pipeline to stop hallucinating was our biggest headache. At first, AI kept bringing in outside internet knowledge instead of strictly using the university syllabus we uploaded. We spent hours adjusting our chunk overlap parameters and tweaking the system prompts to force the AI to cite its sources correctly. On the frontend, building the 3-panel layout for the Classroom AI was tough. Making it responsive so the sidebars slide out cleanly on mobile (under 768px) without triggering React hydration errors or breaking the CSS Grid took a lot of trial and error.

### Accomplishments we're proud of

Getting the Smart Skip Engine to actually work was a huge win. Tying together a student's attendance math, the parsed syllabus topics, and past exam data into a single "safe to skip" alert was a really satisfying engineering problem to solve. We're also really happy with the UI. It looks clean, runs fast, and feels like an app we would actually use every day.

### What we learned

We learned that UX matters just as much as the AI backend. If the app feels slow or the UI is confusing, a smart LLM doesn't save it. Adding simple things like loading skeletons and clickable citation links made the app feel much more usable. We also got hands-on experience using Playwright for automated browser testing to make sure our demo flow didn't break at the last minute.

### What's next

Our next immediate step is to connect the app directly to university portals like Canvas or Moodle via their APIs, so schedules and PDFs import automatically without the user having to upload them manually. We'd also like to expand the study rooms to include voice chat for easier collaboration.

## README (from the GitHub repository)


# 🎓 Semester Copilot
> Your Semester has a Digital Twin. A proactive academic command center powered by AI Intelligence.

**Team:** Yuvth.raw  
**Built for:** [OpenAI Build Week Hackathon]

[![Demo Video](https://img.shields.io/badge/Watch-Demo_Video-red?style=for-the-badge&logo=youtube)](#) *(https://youtu.be/-SBBLuw9USc)*

## 🚀 The "Try It Out" Quickstart 
We built Semester Copilot with a local SQLite database so you can easily test the entire AI pipeline on your own machine without relying on external cloud databases.

### Prerequisites
* Node.js (v18+)
* Python (3.10+)
* A Working AI API Key

### 1. **Start the FastAPI Backend**
Open a terminal and navigate to the `apps/api` folder:
```bash
cd apps/api
# Create a virtual environment
python -m venv venv
source venv/bin/activate  # On Windows use: venv\Scripts\activate

# Install dependencies
pip install -r requirements.txt

# Add your AI API Key
# Create a .env file in the apps/api folder and add:
# AI_API_KEY=your_key_here

# Run the server
uvicorn app.main:app --reload
```

### 2. **We as a Team Developed this project by using the help of ChatGPT 5.6 model, and to test the frontend, improve features, and fix issues we used Codex as the AI agent**


## Detected evidence (automated analysis)

Indexed codebase: 76 recognized source files, 432 KB.
- CSS (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- FastAPI (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (83 of 83)

```
.agents/skills/magicpath/references/cli-reference.md
.agents/skills/magicpath/references/using-magicpath-designs-in-local-code.md
.agents/skills/magicpath/references/working-with-embedded-browsers.md
.agents/skills/magicpath/references/working-with-repositories.md
.agents/skills/magicpath/SKILL.md
.gitignore
apps/api/app/api/v1/attendance.py
apps/api/app/config.py
apps/api/app/db/database.py
apps/api/app/db/fixtures.py
apps/api/app/db/models.py
apps/api/app/main.py
apps/api/app/routers/auth.py
apps/api/app/routers/chat.py
apps/api/app/routers/dashboard.py
apps/api/app/routers/import_router.py
apps/api/app/routers/pyq.py
apps/api/app/routers/recovery.py
apps/api/app/routers/skip.py
apps/api/app/routers/sources.py
apps/api/app/routers/timetable.py
apps/api/app/routers/tutor.py
apps/api/app/schemas/attendance.py
apps/api/app/schemas/auth.py
apps/api/app/schemas/domain.py
apps/api/app/schemas/pyq.py
apps/api/app/schemas/tutor.py
apps/api/app/services/attendance.py
apps/api/app/services/notes.py
apps/api/app/services/pyq.py
apps/api/app/services/rag/generation.py
apps/api/app/services/rag/ingestion.py
apps/api/app/services/skip_engine.py
apps/api/app/services/tutor.py
apps/api/app/services/vision.py
apps/web/.gitignore
apps/web/components.json
apps/web/eslint.config.mjs
apps/web/next.config.ts
apps/web/package.json
apps/web/postcss.config.mjs
apps/web/README.md
apps/web/src/app/attendance/page.tsx
apps/web/src/app/class-chat/[subject_id]/page.tsx
apps/web/src/app/context/PinnedNotesContext.tsx
apps/web/src/app/dashboard/page.tsx
apps/web/src/app/exam-prep/page.tsx
apps/web/src/app/globals.css
apps/web/src/app/imports/page.tsx
apps/web/src/app/layout.tsx
apps/web/src/app/login/page.tsx
apps/web/src/app/page.tsx
apps/web/src/app/recovery/page.tsx
apps/web/src/app/signup/page.tsx
apps/web/src/app/sources/page.tsx
apps/web/src/app/template.tsx
apps/web/src/app/timetable/page.tsx
apps/web/src/app/tutor/page.tsx
apps/web/src/components/demo-provider.tsx
apps/web/src/components/layout/app-shell.tsx
apps/web/src/components/layout/mobile-nav.tsx
apps/web/src/components/layout/sidebar.tsx
apps/web/src/components/layout/topbar.tsx
apps/web/src/components/pdf-viewer.tsx
apps/web/src/components/skip-card.tsx
apps/web/src/components/ui/badge.tsx
apps/web/src/components/ui/button.tsx
apps/web/src/components/ui/card.tsx
apps/web/src/components/ui/dialog.tsx
apps/web/src/components/ui/input.tsx
apps/web/src/components/ui/label.tsx
apps/web/src/components/ui/progress.tsx
apps/web/src/components/ui/select.tsx
apps/web/src/components/ui/sheet.tsx
apps/web/src/components/ui/skeleton.tsx
apps/web/src/components/ui/sonner.tsx
apps/web/src/components/ui/table.tsx
apps/web/src/components/user-provider.tsx
apps/web/src/lib/mock-data.ts
apps/web/src/lib/utils.ts
apps/web/tsconfig.json
apps/web/types/schema.ts
README.md
```

### Dependencies

- apps/web/package.json: @base-ui/react@^1.6.0, @eslint/eslintrc@^3, @tailwindcss/postcss@^4, @types/canvas-confetti@^1.9.0, @types/node@^20, @types/react@^19, @types/react-dom@^19, canvas-confetti@^1.9.4, class-variance-authority@^0.7.1, clsx@^2.1.1, eslint@^9, eslint-config-next@15.5.20, framer-motion@^12.42.2, lucide-react@^1.25.0, next@15.5.20, next-themes@^0.4.6, react@19.1.0, react-dom@19.1.0, react-pdf@^10.4.1, shadcn@^4.13.1, sonner@^2.0.7, tailwind-merge@^3.6.0, tailwindcss@^4, tw-animate-css@^1.4.0, typescript@^5

### Recent commits (newest first)

- Working 8
- Working 7
- Working 6
- Working 5
- Working 4
- Working 3
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### .agents/skills/magicpath/references/working-with-embedded-browsers.md

```markdown
# Working With Embedded Browsers - Keep a MagicPath Project Open as a Canvas

Use this reference when MagicPath runs through an external agent host that exposes an embedded browser, browser pane, or webview, such as Codex or Cursor when that capability is available.

## Purpose

A MagicPath project can become a persistent visual canvas beside the agent: the agent reasons across the user's local repository, assets, notes, skills, and tools while the user sees and selects work on the MagicPath canvas in the same workspace.

This guidance applies to **projects/files**, not individual design previews. Keep the browser focused on the project canvas. Only open an individual component or design in the embedded browser when the user explicitly asks to see that specific design.

Do not assume a named host always has an internal browser capability. Check the capabilities available in the current session and use this workflow only when the host can actually show or navigate that surface.

## When to use the embedded project canvas

Open or keep a MagicPath project in the host's embedded browser when:

- The user asks to create a new MagicPath project/file and will work on it visually.
- The user asks to build, edit, iterate on, or select designs in a named or currently open project.
- A new project has just been created and the requested next step is canvas authoring.
- The user asks to open a project in MagicPath inside Codex, Cursor, or the current agent.
- You need the user to make a selection on the canvas for a follow-up edit.

This should feel like one workflow: the agent works with local code and context while MagicPath remains visible as the visual canvas beside it.

When a request creates a new project and asks for any design work inside it, opening the project canvas is not an optional final preview step. Open it immediately after project creation and before starting the design work.

## Do not navigate to individual designs by default

- Creating, submitting, or editing a component does not require leaving the project canvas. The new or edited design appears in that project.
- Do not open each design or component in its own preview page after `code submit`.
- Do not replace the project canvas with a single-design preview merely to show progress.
- If the user explicitly asks to open a particular design or see it on its own, open it as requested.
- If the user asks for a single-design link, return the link without navigating away from the project canvas unless they also ask to open it.

## Resolve a project URL without opening an external browser

`view <projectId>` opens a project in the operating-system browser. In an agent host with an embedded browser, obtain the project URL without opening another window:

```bash
npx -y magicpath-ai share <projectId> -o json
```

The response includes the project URL:

```json
{ "type": "project", "url": "https://www.magicpath.ai/files/<projectId>", "projectId": "<projectId>" }
```

Navigate the host's embed
[truncated — 4627 more characters]
```

### .agents/skills/magicpath/references/working-with-repositories.md

```markdown
# Working With Repositories — Bring an Existing Codebase Into MagicPath

> **IMPORTANT:** This flow is the **inverse** of `add`/`inspect`. With `add`/`inspect` the source of truth is the MagicPath registry and the destination is the user's app. Here the **source of truth is the user's repository** and the **destination is the MagicPath canvas**. You recreate the repo's UI as a canvas component using the `code start` → `code submit` authoring flow. Do **not** use `add`, `inspect`, or `code context` for this — they are for other workflows (see the boundaries section below).
>
> For the opposite direction—exporting a MagicPath design or replacing local UI with one—use [Using MagicPath designs in local code](using-magicpath-designs-in-local-code.md).

This reference tells an external agent (MagicPath's own agent, Claude Code, Codex, Cursor, etc.) exactly what to do when the user wants to take UI that already exists in a Git repository — local or online — and reproduce it faithfully as a React component on their MagicPath canvas.

## When this applies (triggers)

Reach for this reference when the user points at existing code and asks to get it onto the canvas. Examples:

- "Bring the sidebar of my app into MagicPath."
- "Render this project in MagicPath." / "Import my repo." / "Recreate my landing page in MagicPath."
- "Design from my existing dashboard." / "Pull my `<Header />` into a MagicPath design."
- Any message that pairs a **local path** (e.g. `~/code/acme-web`) or an **online repo URL** (GitHub/GitLab/Bitbucket) with MagicPath intent.

If the user is instead asking to install a MagicPath registry component into their app, that is the `add`/`inspect` flow — not this one.

## The mental model

A MagicPath canvas component is a single, self-contained, interactive React + Tailwind v4 mini-app (see the skill's **Design Defaults**). Your job is to read the relevant slice of the repo, understand both its **visual output** and its **behavior**, and reproduce that as faithfully as possible inside the canvas authoring template — translating whatever framework and styling system the repo uses into the canvas's React + Tailwind v4 conventions.

Fidelity is the goal: the canvas result should look like the same product, not a reinterpretation of it.

---

## Phase 0 — Get the code

**Local repository.** Confirm the path with the user if it isn't explicit. Read files directly. If the path is read-only (e.g. an uploads mount), copy the slice you need into your working directory before doing anything stateful. Do not assume the repo root — ask or detect it (look for `package.json`, `.git/`, a framework config).

**Online repository.** Clone it shallowly into a scratch directory, then read from there:

```bash
git clone --depth 1 <repo-url> ./_repo
# specific branch:
git clone --depth 1 --branch <branch> <repo-url> ./_repo
```

- If the user gave a URL to a **specific file or folder** (e.g. a GitHub `/blob/` or `/tree/` link), you only need that slice plus its
[truncated — 11637 more characters]
```

### apps/web/package.json

```
{
  "name": "web",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build --turbopack",
    "start": "next start",
    "lint": "eslint"
  },
  "dependencies": {
    "@base-ui/react": "^1.6.0",
    "@types/canvas-confetti": "^1.9.0",
    "canvas-confetti": "^1.9.4",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "framer-motion": "^12.42.2",
    "lucide-react": "^1.25.0",
    "next": "15.5.20",
    "next-themes": "^0.4.6",
    "react": "19.1.0",
    "react-dom": "19.1.0",
    "react-pdf": "^10.4.1",
    "shadcn": "^4.13.1",
    "sonner": "^2.0.7",
    "tailwind-merge": "^3.6.0",
    "tw-animate-css": "^1.4.0"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.5.20",
    "tailwindcss": "^4",
    "typescript": "^5"
  }
}

```

### apps/api/app/main.py

```python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api.v1.attendance import router as attendance_router
from app.routers.timetable import router as timetable_router
from app.routers.recovery import router as recovery_router
from app.routers.tutor import router as tutor_router
from app.routers.skip import router as skip_router
from app.routers.pyq import router as pyq_router
from app.routers.import_router import router as import_router
from app.routers.sources import router as sources_router
from app.routers.chat import router as chat_router
from app.routers.dashboard import router as dashboard_router
from app.routers.auth import router as auth_router

from contextlib import asynccontextmanager
from app.db.database import init_db, SessionLocal
from app.db.fixtures import seed_db

@asynccontextmanager
async def lifespan(app: FastAPI):
    init_db()
    db = SessionLocal()
    try:
        seed_db(db)
    finally:
        db.close()
    yield

app = FastAPI(title="Semester Copilot API", lifespan=lifespan)

# Add CORS so Next.js frontend can communicate with FastAPI
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(attendance_router, prefix="/api/v1/attendance", tags=["attendance"])
app.include_router(timetable_router, prefix="/api/v1/timetable", tags=["timetable"])
app.include_router(recovery_router, prefix="/api/v1/recovery", tags=["recovery"])
app.include_router(tutor_router, prefix="/api/v1/tutor", tags=["tutor"])
app.include_router(skip_router, prefix="/api/v1", tags=["skip"])
app.include_router(pyq_router, prefix="/api/v1/pyq", tags=["pyq"])
app.include_router(import_router, prefix="/api/v1/import", tags=["import"])
app.include_router(sources_router, prefix="/api/v1/sources", tags=["sources"])
app.include_router(chat_router, prefix="/api/v1/chat", tags=["chat"])
app.include_router(dashboard_router, prefix="/api/v1/dashboard", tags=["dashboard"])
app.include_router(auth_router, prefix="/api/v1/auth", tags=["auth"])

```

### apps/web/src/app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Inter, Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { AppShell } from "@/components/layout/app-shell";
import { ThemeProvider } from "next-themes";
import { UserProvider } from "@/components/user-provider";
import { PinnedNotesProvider } from "@/app/context/PinnedNotesContext";
import { cn } from "@/lib/utils";

const geist = Geist({subsets:['latin'],variable:'--font-sans'});
const geistMono = Geist_Mono({ subsets: ["latin"], variable: '--font-mono' });
const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Semester Copilot",
  description: "Your academic co-pilot",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en" className={cn("font-sans", geist.variable, geistMono.variable)} suppressHydrationWarning>
      <body className={`${inter.className} flex h-screen bg-background text-foreground antialiased overflow-hidden`}>
        <div className="absolute inset-0 z-[-1] h-full w-full bg-[radial-gradient(#e5e7eb_1px,transparent_1px)] dark:bg-[radial-gradient(#1f2937_1px,transparent_1px)] [background-size:16px_16px] pointer-events-none opacity-50"></div>
        <ThemeProvider attribute="class" defaultTheme="light" enableSystem={false}>
          <UserProvider>
            <PinnedNotesProvider>
              <AppShell>
                {children}
              </AppShell>
            </PinnedNotesProvider>
          </UserProvider>
        </ThemeProvider>
      </body>
    </html>
  );
}

```

### apps/web/src/app/page.tsx

```typescript
"use client"
import Link from "next/link"
import { motion } from "framer-motion"
import { Button } from "@/components/ui/button"
import { Play, Sparkles, BrainCircuit, Users, ChevronRight, GraduationCap, Upload, Zap, Trophy, LineChart } from "lucide-react"

export default function LandingPage() {
  const container: any = {
    hidden: { opacity: 0 },
    show: {
      opacity: 1,
      transition: { staggerChildren: 0.1, delayChildren: 0.1 }
    }
  }

  const item: any = {
    hidden: { opacity: 0, y: 20 },
    show: { opacity: 1, y: 0, transition: { type: "spring", stiffness: 300, damping: 24 } }
  }

  return (
    <div className="min-h-screen flex flex-col relative overflow-hidden bg-background">
      {/* Navbar */}
      <header className="absolute top-0 w-full p-6 flex justify-between items-center z-50">
        <div className="flex items-center gap-2">
          <div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center text-primary-foreground shadow-sm">
            <GraduationCap className="w-5 h-5" />
          </div>
          <span className="font-bold text-xl tracking-tight">Semester Copilot</span>
        </div>
        <div className="flex items-center gap-4">
          <Link href="/login" className="text-sm font-medium hover:text-primary transition-colors hidden sm:block">Sign In</Link>
          <Link href="/signup">
            <Button size="sm" className="rounded-full px-6">Get Started</Button>
          </Link>
        </div>
      </header>

      {/* Hero Section */}
      <main className="flex-1 flex flex-col items-center justify-center pt-32 pb-16 px-4 z-10 relative">
        <div className="absolute inset-0 z-[-1] flex items-center justify-center">
          <div className="w-[600px] h-[600px] bg-primary/20 rounded-full blur-[120px] opacity-60 mix-blend-screen" />
        </div>
        
        <motion.div 
          variants={container} 
          initial="hidden" 
          animate="show"
          className="max-w-4xl w-full flex flex-col items-center text-center space-y-8"
        >
          <motion.div variants={item} className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-primary/20 bg-primary/10 text-primary hover:bg-primary/20 shadow-[0_0_15px_rgba(59,130,246,0.5)]">
            <Sparkles className="w-3.5 h-3.5 mr-2" />
            Introducing the ultimate academic OS
          </motion.div>

          <motion.h1 variants={item} className="text-5xl md:text-7xl font-black tracking-tighter leading-[1.1]">
            Your Semester has a <br className="hidden md:block"/>
            <span className="bg-gradient-to-r from-primary via-blue-500 to-cyan-400 bg-clip-text text-transparent drop-shadow-sm">Digital Twin.</span>
          </motion.h1>

          <motion.p variants={item} className="text-lg md:text-xl text-muted-foreground max-w-2xl leading-relaxed">
            Stop juggling ten different apps. Semester Copilot tracks your attendance, reads your syllabus, and predicts your exams—all in one intelligent command center.
          </motion.p>

          <motion.div variants={item} className="flex flex-col sm:flex-row items-center gap-4 pt-4 w-full sm:w-auto">
            <Link href="/signup" className="w-full sm:w-auto">
              <Button size="lg" className="w-full sm:w-auto rounded-full px-8 h-12 text-base group bg-primary hover:bg-primary/90 shadow-[0_0_30px_rgba(59,130,246,0.3)]">
                Get Started 
                <ChevronRight className="w-4 h-4 ml-2 group-hover:translate-x-1 transition-transform" />
              </Button>
            </Link>
            <Button size="lg" variant="outline" className="w-full sm:w-auto rounded-full px-8 h-12 text-base border-primary/20 hover:bg-primary/5">
              <Play className="w-4 h-4 mr-2" />
              Watch Demo
            </Button>
          </motion.div>

          {/* Visual Asset (Mockup) */}
          <motion.div variants={item} className="w-full max-w-3xl mt-16 relative">
            {/* Glow effect */}
            <div className="absolute inset-0 bg-gradient-to-r from-primary/20 to-blue-500/20 blur-3xl -z-10 rounded-full" />
            
            <div className="bg-card border rounded-2xl shadow-2xl overflow-hidden flex flex-col">
              <div className="h-10 bg-muted/50 border-b flex items-center px-4 gap-2">
                <div className="w-3 h-3 rounded-full bg-rose-400" />
                <div className="w-3 h-3 rounded-full bg-amber-400" />
                <div className="w-3 h-3 rounded-full bg-emerald-400" />
              </div>
              <div className="p-8 grid md:grid-cols-2 gap-6 bg-[radial-gradient(#e5e7eb_1px,transparent_1px)] dark:bg-[radial-gradient(#1f2937_1px,transparent_1px)] [background-size:16px_16px]">
                <div className="bg-background rounded-xl p-6 border shadow-sm flex flex-col justify-center items-center text-center">
                  <h3 className="text-lg font-bold mb-2">Academic Health</h3>
                  <div className="text-6xl font-black text-primary font-mono tracking-tighter">92<span className="text-2xl text-muted-foreground">%</span></div>
                  <div className="mt-4 px-3 py-1 bg-emerald-500/10 text-emerald-600 rounded-full text-xs font-bold uppercase tracking-wider border border-emerald-500/20">Perfect Track</div>
                </div>
                <div className="bg-background rounded-xl p-6 border shadow-sm flex flex-col justify-center">
                  <div className="flex items-center gap-2 text-rose-500 font-bold mb-4">
                    <Sparkles className="w-5 h-5" /> Smart Skip Engine
                  </div>
                  <p className="text-sm font-medium mb-1">DBMS201 (Database Systems)</p>
                  <p className="text-xs text-muted-foreground mb-4">You have 0 safe skips left. Skipping this lecture will drop your attendance t
[truncated — 10513 more characters]
```

### apps/web/src/app/login/page.tsx

```typescript
"use client"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from "@/components/ui/card"
import { GraduationCap, Loader2 } from "lucide-react"
import Link from "next/link"
import { useRouter } from "next/navigation"
import { motion } from "framer-motion"
import { useUser } from "@/components/user-provider"
import { toast } from "sonner"
import { useState } from "react"

export default function LoginPage() {
  const router = useRouter()
  const { login } = useUser()
  const [loading, setLoading] = useState(false)
  const [formData, setFormData] = useState({ email: "", password: "" })

  const handleLogin = async (e: React.FormEvent) => {
    e.preventDefault()
    setLoading(true)

    try {
      const res = await fetch("http://localhost:8000/api/v1/auth/login", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(formData)
      })

      if (res.ok) {
        const data = await res.json()
        login(data.name, data.email)
        toast.success("Welcome back!")
        router.push("/dashboard")
      } else {
        const err = await res.json()
        toast.error(err.detail || "Login failed")
      }
    } catch (e) {
      toast.error("Network error. Is the backend running?")
    } finally {
      setLoading(false)
    }
  }

  return (
    <div className="min-h-screen flex items-center justify-center p-4">
      <motion.div 
        initial={{ opacity: 0, y: 20 }} 
        animate={{ opacity: 1, y: 0 }} 
        transition={{ duration: 0.5, ease: "easeOut" }}
        className="w-full max-w-md"
      >
        <div className="flex flex-col items-center mb-8">
          <div className="w-12 h-12 bg-primary rounded-xl flex items-center justify-center text-primary-foreground mb-4 shadow-sm">
            <GraduationCap className="w-6 h-6" />
          </div>
          <h1 className="text-2xl font-bold tracking-tight">Welcome back</h1>
          <p className="text-sm text-muted-foreground mt-1">Sign in to your intelligent command center</p>
        </div>

        <Card className="border-border shadow-sm">
          <form onSubmit={handleLogin}>
            <CardContent className="pt-6 space-y-4">
              <div className="space-y-2">
                <Label htmlFor="email">Email</Label>
                <Input 
                  id="email" 
                  type="email" 
                  placeholder="student@university.edu" 
                  required 
                  className="bg-background"
                  value={formData.email}
                  onChange={(e) => setFormData({...formData, email: e.target.value})} 
                />
              </div>
              <div className="space-y-2">
                <div className="flex items-center justify-between">
                  <Label htmlFor="password">Password</Label>
                  <a href="#" className="text-xs text-primary hover:underline font-medium">Forgot password?</a>
                </div>
                <Input 
                  id="password" 
                  type="password" 
                  required 
                  className="bg-background"
                  value={formData.password}
                  onChange={(e) => setFormData({...formData, password: e.target.value})} 
                />
              </div>
            </CardContent>
            <CardFooter className="flex flex-col space-y-4 pb-6">
              <Button type="submit" className="w-full" disabled={loading}>
                {loading ? <Loader2 className="w-4 h-4 mr-2 animate-spin" /> : null}
                Sign In
              </Button>
              <div className="text-sm text-center text-muted-foreground">
                Don't have an account? <Link href="/signup" className="text-primary font-medium hover:underline">Sign up</Link>
              </div>
            </CardFooter>
          </form>
        </Card>
      </motion.div>
    </div>
  )
}

```

### apps/web/src/app/signup/page.tsx

```typescript
"use client"
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Card, CardContent, CardHeader, CardTitle, CardFooter } from "@/components/ui/card"
import { GraduationCap, Loader2 } from "lucide-react"
import Link from "next/link"
import { useRouter } from "next/navigation"
import { motion } from "framer-motion"
import { useUser } from "@/components/user-provider"
import { toast } from "sonner"

export default function SignupPage() {
  const router = useRouter()
  const { login } = useUser()
  const [loading, setLoading] = useState(false)
  const [formData, setFormData] = useState({ name: "", email: "", password: "" })

  const handleSignup = async (e: React.FormEvent) => {
    e.preventDefault()
    setLoading(true)
    
    try {
      const res = await fetch("http://localhost:8000/api/v1/auth/signup", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(formData)
      })

      if (res.ok) {
        const data = await res.json()
        login(data.name, data.email)
        toast.success("Account created successfully!")
        router.push("/dashboard")
      } else {
        const err = await res.json()
        toast.error(err.detail || "Signup failed")
      }
    } catch (e) {
      toast.error("Network error. Is the backend running?")
    } finally {
      setLoading(false)
    }
  }

  return (
    <div className="min-h-screen flex items-center justify-center p-4">
      <motion.div 
        initial={{ opacity: 0, y: 20 }} 
        animate={{ opacity: 1, y: 0 }} 
        transition={{ duration: 0.5, ease: "easeOut" }}
        className="w-full max-w-md"
      >
        <div className="flex flex-col items-center mb-8">
          <div className="w-12 h-12 bg-primary rounded-xl flex items-center justify-center text-primary-foreground mb-4 shadow-sm">
            <GraduationCap className="w-6 h-6" />
          </div>
          <h1 className="text-2xl font-bold tracking-tight">Create your account</h1>
          <p className="text-sm text-muted-foreground mt-1">Start optimizing your semester today</p>
        </div>

        <Card className="border-border shadow-sm">
          <form onSubmit={handleSignup}>
            <CardContent className="pt-6 space-y-4">
              <div className="space-y-2">
                <Label htmlFor="name">Full Name</Label>
                <Input 
                  id="name" 
                  required 
                  className="bg-background" 
                  value={formData.name}
                  onChange={(e) => setFormData({...formData, name: e.target.value})}
                />
              </div>
              <div className="space-y-2">
                <Label htmlFor="email">Email</Label>
                <Input 
                  id="email" 
                  type="email" 
                  placeholder="student@university.edu" 
                  required 
                  className="bg-background" 
                  value={formData.email}
                  onChange={(e) => setFormData({...formData, email: e.target.value})}
                />
              </div>
              <div className="space-y-2">
                <Label htmlFor="password">Password</Label>
                <Input 
                  id="password" 
                  type="password" 
                  required 
                  className="bg-background" 
                  value={formData.password}
                  onChange={(e) => setFormData({...formData, password: e.target.value})}
                />
              </div>
            </CardContent>
            <CardFooter className="flex flex-col space-y-4 pb-6">
              <Button type="submit" className="w-full" disabled={loading}>
                {loading ? <Loader2 className="w-4 h-4 mr-2 animate-spin" /> : null}
                Sign Up
              </Button>
              <div className="text-sm text-center text-muted-foreground">
                Already have an account? <Link href="/login" className="text-primary font-medium hover:underline">Sign in</Link>
              </div>
            </CardFooter>
          </form>
        </Card>
      </motion.div>
    </div>
  )
}

```

### apps/web/src/app/exam-prep/page.tsx

```typescript
/* eslint-disable @typescript-eslint/no-explicit-any, react/no-unescaped-entities, @typescript-eslint/no-unused-vars */
"use client"
import { useState } from "react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { FileUp, Flame, Zap, Eye, EyeOff, LayoutGrid, CheckCircle } from "lucide-react"

interface RepeatedTopic {
  topic: string
  frequencyCount: number
  probability: 'HIGH' | 'MEDIUM' | 'LOW'
}

interface PredictedQuestion {
  questionNumber: number
  questionText: string
  marks: number
  topic: string
  stepByStepSolution: string
}

interface PYQData {
  repeatedTopics: RepeatedTopic[]
  predictedQuestionPaper: PredictedQuestion[]
}

export default function ExamPrepPage() {
  const [data, setData] = useState<PYQData | null>(null)
  const [loading, setLoading] = useState(false)
  const [revealedAnswers, setRevealedAnswers] = useState<Record<number, boolean>>({})
  
  const handleLoadDemo = async () => {
    setLoading(true)
    try {
      const formData = new FormData()
      formData.append("is_demo", "true")
      
      const res = await fetch("http://localhost:8000/api/v1/pyq/analyze", {
        method: "POST",
        body: formData
      })
      if (res.ok) {
        setData(await res.json())
      }
    } catch (err) {
      console.error("Failed to load demo data", err)
    } finally {
      setLoading(false)
    }
  }

  const toggleAnswer = (idx: number) => {
    setRevealedAnswers(prev => ({ ...prev, [idx]: !prev[idx] }))
  }

  // Calculate dynamic opacity for the heatmap based on frequency (max 5 years)
  const getHeatmapOpacity = (freq: number) => {
    const ratio = Math.min(freq / 5, 1)
    if (ratio >= 0.8) return 'bg-rose-500 text-rose-50'
    if (ratio >= 0.6) return 'bg-rose-500/70 text-rose-50'
    if (ratio >= 0.4) return 'bg-rose-500/40 text-foreground'
    return 'bg-rose-500/20 text-foreground'
  }

  return (
    <div className="flex-1 space-y-6 p-8 pt-6">
      <div className="flex flex-col md:flex-row md:items-center justify-between space-y-4 md:space-y-0 mb-6">
        <div>
          <h1 className="text-3xl font-bold tracking-tight">Exam Prep</h1>
          <p className="text-muted-foreground mt-1">Predictive analysis of past year question papers.</p>
        </div>
      </div>

      {!data && (
        <Card className="max-w-xl mx-auto mt-12 bg-card border-dashed border-2 rounded-2xl shadow-sm hover:shadow-lg transition-all duration-300">
          <CardContent className="flex flex-col items-center justify-center py-24 px-6 text-center">
            {loading ? (
              <div className="flex flex-col items-center">
                <div className="w-16 h-16 border-4 border-primary/20 border-t-primary rounded-full animate-spin mb-6"></div>
                <h3 className="text-xl font-semibold mb-2 animate-pulse">Running Neural Analysis...</h3>
                <p className="text-muted-foreground">Parsing past 5 years of exam metadata.</p>
              </div>
            ) : (
              <>
                <div className="w-16 h-16 rounded-full bg-primary/10 flex items-center justify-center mb-6 text-primary">
                  <Flame className="w-8 h-8" />
                </div>
                <h3 className="text-2xl font-bold mb-2">Upload Past Exams</h3>
                <p className="text-muted-foreground mb-8">Upload PDFs of previous years' papers and the AI will generate a hyper-realistic predicted mock exam.</p>
                
                <div className="flex items-center gap-4 w-full justify-center">
                  <Button className="px-8 shadow-sm">
                    <FileUp className="w-4 h-4 mr-2" /> Upload Papers
                  </Button>
                  <Button variant="outline" onClick={handleLoadDemo} className="border-primary/20 bg-primary/5 hover:bg-primary/10">
                    <Zap className="w-4 h-4 mr-2 text-primary" /> Load Sample Data
                  </Button>
                </div>
              </>
            )}
          </CardContent>
        </Card>
      )}

      {data && (
        <div className="space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
          
          {/* Topic Heatmap */}
          <div>
            <h3 className="text-2xl font-bold tracking-tight mb-4 flex items-center gap-2">
              <LayoutGrid className="w-6 h-6 text-rose-500" /> Topic Frequency Heatmap
            </h3>
            <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
              {(data.repeatedTopics || []).map((t: any, idx: number) => {
                const count = t.frequencyCount || t.frequency_count || 0
                return (
                  <div 
                    key={idx} 
                    className={`p-6 rounded-xl border shadow-sm flex flex-col justify-between transition-all hover:scale-[1.02] ${getHeatmapOpacity(count)}`}
                  >
                    <div className="font-semibold text-lg leading-tight mb-4">{t.topic || "Unknown"}</div>
                    <div className="flex items-center justify-between mt-auto">
                      <span className="text-sm font-medium opacity-90">{count}/5 Years</span>
                      <Badge variant="outline" className="bg-background/20 backdrop-border-none border-none text-inherit shadow-none px-2 py-0.5 text-xs">
                        {t.probability || "MEDIUM"}
                      </Badge>
                    </div>
                  </div>
                )
              })}
            </div>
          </div>

          {/* Predicted Mock Exam */}
          <div className="pb-12">
            <h3 className="text-2xl font-bold tracking-tight mb-4 flex items-center gap-2">
              <Zap className="w-6 h-6 text-amber-500" /> Generated Mock Exam
            </h3>
            <div className="space-y-4">
              {(data.predictedQuestionPaper || []).map((q: 
[truncated — 3448 more characters]
```

### apps/web/next.config.ts

```typescript
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* config options here */
};

export default nextConfig;

```

[64 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]