# Project export: Crux — Built to Prepare

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: Crux turns your syllabus, notes, and past papers into a deadline-aware study map—then tells you the highest-yield thing to learn, practice, or review next. with several features
- Devpost: https://devpost.com/software/crux-built-to-prepare
- GitHub: https://github.com/nothariharan/crux
- Demo: https://crux-snowy.vercel.app/
- Video: https://www.youtube.com/embed/A-4FdzeNV60?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — nothariharan (93 commits), Narain Karti (1 commits)

## Devpost submission (written by the team)

### Inspiration

Most study tools assume you have weeks. Real students often have hours. LETS BE HONEST HERE , literally everyone i know from my college start preparing 1-2 days or even 12 hours before exam 😭🥀 , but we dont need a platform whcih builds us roadmap to study which is oriented with the thought we have lots of time to study so a big elaborated roadmap , thats why what we are building is for students or anyone with a deadline and a problem in mind we are having custom agentic harness building out perfect roadmaps for the students after testing their capabilities to get them the top marks within 12 hrs Before an exam, the problem is rarely “not enough content.” It’s decision paralysis: notes in PDFs, slides in folders, past papers in chats, and no clear answer to the only question that matters— What deserves the next 25 minutes? Crux was built for that moment. Not another chatbot tutor that invents answers—a deadline-first workspace that reads your materials and turns them into an explainable study plan.

### What it does

Crux is a deadline-aware study workspace. The core loop: Create a workspace with an exam date and constraints Upload sources — syllabus, lecture slides, notes, past papers (PDF, DOCX, PPTX, TXT, Markdown) Extract a Study Map — topics, dependencies, and cited source evidence Calibrate confidence — a fast check of what you already know Plan a mission — a realistic time-budgeted sequence of learn / practice / review / skip Study and prove it stuck — source-grounded briefs, questions, and a review desk Crux is opinionated on purpose. Every screen pushes toward one next action, with evidence you can inspect—not a black box grade prediction.

### How we built it

Product stack Next.js 15 + React 19 for the app shell and study surfaces Supabase for Postgres, auth-ready architecture, and private file storage Drizzle ORM for schema and migrations Inngest for async document ingestion and extraction React Flow for the interactive Study Map TanStack Query + Zustand for client state AI architecture Deterministic topic inference and mission planning stay explainable Optional generation runs through a bounded harness: retrieve evidence → pack context → generate → validate → cite Students can bring their own OpenAI key (Responses API) and select GPT-5.6 for source-grounded study briefs and concept guides Built with Codex + GPT-5.6 Codex accelerated the end-to-end product: ingestion pipeline, roadmap extraction, mission planner, Study Map UI, API routes, and review flows GPT-5.6 is the generation model for study briefs—grounded in uploaded excerpts, not generic textbook regurgitation Key product decisions stayed human: deadline-first UX, citation-first answers, and “next action” clarity over chat-first UI

### Challenges we ran into

Trust vs. speed — students need a map in minutes, but incomplete uploads can’t pretend to be complete. We surface uncertainty instead of hiding it. Grounding — AI answers must point back to the student’s sources; unconstrained tutoring was a non-goal. Planning under pressure — optimizing for marks-per-minute without feeling harsh or opaque. Real document mess — PDFs, slides, scanned notes, and pasted text all behave differently; extraction + OCR had to stay resilient.

### Accomplishments we're proud of

A working spine from upload → extract → map → calibrate → plan → study → practice → review Source-cited topic evidence, not floating flashcards An interactive Study Map that makes dependencies and progress visible A BYOK path so students can use GPT-5.6 without Crux holding their keys in plaintext A product thesis that stayed narrow: ruthless next moves under a deadline

### What we learned

Under exam pressure, clarity beats features. One obvious next action beats a dashboard of options. Separating retrieval / planning / generation keeps the system honest when the model is wrong. Codex is strongest when the product loop is already clear—then it compresses implementation time without diluting the thesis. Students trust systems that show their work: citations, confidence, and “why this topic now.”

### What's next

Deeper past-paper pattern clustering (what repeats, what converts to marks) Stronger last-day revision modes (including audio briefs) Collaborative workspaces for study groups Better OCR and image-heavy notes support Tighter GPT-5.6 study modes: trap detection, formula recall, and timed practice sets

## README (from the GitHub repository)

# Crux

**Deadline-first study workspace.** Upload the materials you already have (syllabus, notes, PYQs), get a sourced topic map, calibrate what you know, then plan and clear units before the exam.

```text
Create workspace → Upload sources (+ PYQ) → Extract map → Calibrate → Plan → Study → Questions → Clearance → Deadline
```

Live product surface: [crux-snowy.vercel.app](https://crux-snowy.vercel.app)

---

## Why Crux

Most study tools optimize for long courses. Crux optimizes for the **last stretch**:

- **Deadline-aware planning** around the hours you can protect
- **Source-backed notes and briefs** (citations, not free-floating chat)
- **PYQ-aware clearance** so practice tracks what the paper actually asks
- **Study Map** for pathing units, **Clearance** for proving mastery, optional **oral viva** / Last Lap audio when configured

Crux is not “an AI that knows everything.” It helps you decide **what deserves the next block of time, why, and how to prove it stuck.**

---

## Stack

| Layer | Choice |
|--------|--------|
| App | Next.js 15 (App Router), React 19, TypeScript |
| UI | Tailwind, local craft components, React Flow (Study Map) |
| Data | Postgres via Drizzle ORM, Supabase Auth + Storage |
| Jobs | Inngest (optional locally; inline ingest without a worker) |
| AI (optional) | Fireworks / Bedrock / BYOK OpenAI & Anthropic, ElevenLabs TTS |

---

## Quick start

**Requirements:** Node.js **20+**, npm.

### UI-only preview

Enough for marketing pages and most screens without a database:

```bash
git clone https://github.com/nothariharan/crux.git
cd crux
npm install
npm run dev
```

Open [http://localhost:3000](http://localhost:3000).

### Full study loop

You need your own Supabase project (database + private Storage bucket).

1. Create a Supabase project and a **private** Storage bucket named `resources`.
2. Copy env template and fill required keys:

```bash
cp .env.example .env   # macOS / Linux
# Copy-Item .env.example .env   # Windows PowerShell
```

Minimum required:

- `NEXT_PUBLIC_SUPABASE_URL`
- `NEXT_PUBLIC_SUPABASE_ANON_KEY`
- `SUPABASE_SERVICE_ROLE_KEY`
- `DATABASE_URL`

3. Apply schema and seed the local demo user:

```bash
npm run db:push
npm run db:seed
npm run dev
```

4. Smoke path:

   1. `/onboarding` — create a workspace  
   2. Upload syllabus/notes + previous papers (PYQ)  
   3. Wait until sources are **Ready**  
   4. Calibrate → build a mission → open Study Map / Study / Questions / Clearance  

Auth stays off for local single-user testing unless `ENABLE_AUTH=true`. **Never commit `.env`.**

See [`.env.example`](.env.example) for optional AI, OCR, Inngest, and demo flags.

---

## Scripts

| Command | Purpose |
|---------|---------|
| `npm run dev` | Next.js development server |
| `npm run build` / `npm start` | Production build & serve |
| `npm run lint` | ESLint (zero warnings) |
| `npm run typecheck` | TypeScript `--noEmit` |
| `npm test` | Unit tests (Node test runner + tsx) |
| `npm run db:push` | Push Drizzle schema + apply Supabase RLS helpers |
| `npm run db:migrate` | Run SQL migrations + RLS |
| `npm run db:seed` | Seed local demo user |
| `npm run db:baseline -- --confirm` | One-time migration journal baseline for legacy DBs |

Optional Inngest worker:

```bash
npx inngest-cli@latest dev
```

Without `INNGEST_EVENT_KEY`, uploads still process inline in the Next process.

---

## Product map

| Route | Role |
|-------|------|
| `/` | Landing |
| `/onboarding` | Mission, time boundary, sources + PYQ, calibration |
| `/workspace` | Desk: sources, mission, quick calibration |
| `/study-map` | Interactive roadmap (React Flow, lazy-loaded) |
| `/study` | Source-backed notes / briefs per topic |
| `/questions` | PYQ practice + active recall |
| `/review` | Clearance quizzes, oral assessment, reports |
| `/settings` | Workspace, AI keys, exports, optional demo prep |

Study API responses are **view-scoped** (`?view=map|notes|questions|review|planner|calibration`) so sections do not download the full workspace payload on every navigation.

---

## Security notes (before going public)

- Secrets live only in `.env` / host env vars — see [SECURITY.md](SECURITY.md).
- **Demo prep** (fast-forward mastery) is **off by default**. Enable only in trusted demos:

  ```env
  ENABLE_DEMO_PREP=true
  NEXT_PUBLIC_ENABLE_DEMO_PREP=true
  ```

  Leave both `false` on public production.
- Workspace “public share” exposes **metadata** for link access; keep it off unless you intend that.
- If `ENABLE_AUTH=true`, configure Supabase Auth redirect URLs for your domain (e.g. `https://your-app.vercel.app/auth/callback`).

---

## Documentation

| Doc | Contents |
|-----|----------|
| [docs/README.md](docs/README.md) | Index of planning & design docs |
| [docs/PRD.md](docs/PRD.md) | Product requirements (historical name: LastLap) |
| [docs/TECHNICAL-DESIGN.md](docs/TECHNICAL-DESIGN.md) | Architecture & data model |
| [docs/UX-SPEC.md](docs/UX-SPEC.md) | UX specification |
| [docs/AI-HARNESS.md](docs/AI-HARNESS.md) | Generation / grounding notes |
| [CONTRIBUTING.md](CONTRIBUTING.md) | How to contribute |
| [SECURITY.md](SECURITY.md) | Vulnerability reporting |

---

## Contributing

Issues and PRs are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a large change.

```bash
npm run lint && npm run typecheck && npm test
```

---

## License

MIT — see [LICENSE](LICENSE).

---

## Disclaimer

Crux helps you organize and practice from **your** materials. It does not guarantee exam outcomes. Treat model output as assistive; verify against your syllabus and past papers.


## Detected evidence (automated analysis)

Indexed codebase: 188 recognized source files, 943 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- SQL (language) — detected in the code
- Supabase (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- AWS (technology) — claimed on Devpost, not found in the code
- OpenAI (technology) — claimed on Devpost, not found in the code
- PostgreSQL (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 212)

```
.env.example
.gitignore
.vercelignore
actions/workspaces.ts
app/(product)/layout.tsx
app/(product)/questions/page.tsx
app/(product)/review/page.tsx
app/(product)/settings/page.tsx
app/(product)/study-map/page.tsx
app/(product)/study/page.tsx
app/(product)/workspace/page.tsx
app/api/inngest/route.ts
app/api/user/ai-keys/route.ts
app/api/workspaces/[id]/artifacts/[artifactId]/html/route.ts
app/api/workspaces/[id]/audio/last-lap/route.ts
app/api/workspaces/[id]/audio/topic-summary/route.ts
app/api/workspaces/[id]/demo-prep/route.ts
app/api/workspaces/[id]/export/route.ts
app/api/workspaces/[id]/health/route.ts
app/api/workspaces/[id]/metrics/route.ts
app/api/workspaces/[id]/resources/[resourceId]/process/route.ts
app/api/workspaces/[id]/resources/[resourceId]/route.ts
app/api/workspaces/[id]/resources/paste/route.ts
app/api/workspaces/[id]/resources/presign/route.ts
app/api/workspaces/[id]/resources/route.ts
app/api/workspaces/[id]/revision/route.ts
app/api/workspaces/[id]/route.ts
app/api/workspaces/[id]/study/route.ts
app/api/workspaces/[id]/topics/[topicId]/videos/route.ts
app/api/workspaces/[id]/viva/route.ts
app/api/workspaces/calendar/route.ts
app/api/workspaces/collisions/route.ts
app/api/workspaces/route.ts
app/auth/callback/route.ts
app/error.tsx
app/globals.css
app/layout.tsx
app/login/page.tsx
app/onboarding/page.tsx
app/page.tsx
components.json
components/app-shell.tsx
components/craft-panel.tsx
components/mission-brief.tsx
components/product-page-header.tsx
components/product-route-loader.tsx
components/product-section-loader.tsx
components/return-workspace-link.tsx
components/ui/avatar.tsx
components/ui/badge.tsx
components/ui/button.tsx
components/ui/card.tsx
components/ui/input.tsx
components/ui/progress.tsx
components/ui/separator.tsx
components/ui/textarea.tsx
components/workspace-bootstrap.tsx
components/workspace-open-fallback.tsx
components/workspace-path-card.tsx
components/workspace-switcher.tsx
CONTRIBUTING.md
docs/AI-HARNESS.md
docs/crux_aws_rag_integration.html
docs/crux_optimization_blueprint.html
docs/crux_rag_strengthening.html
docs/crux-demo-script-3min.html
docs/IMPLEMENTATION-ROADMAP.md
docs/plan.html
docs/PRD.md
docs/README.md
docs/TECHNICAL-DESIGN.md
docs/UX-SPEC.md
drizzle.config.ts
eslint.config.mjs
features/audio/components/last-lap-briefing.tsx
features/audio/components/topic-audio-summary.tsx
features/calibration/components/calibration-card.tsx
features/onboarding/components/intake-flow.tsx
features/planner/components/mission-planner.tsx
features/planner/components/rescue-mode.tsx
features/questions/components/question-panel.tsx
features/review/components/review-desk.tsx
features/review/components/unit-clearance-panel.tsx
features/roadmap/components/roadmap-workbench-dynamic.tsx
features/roadmap/components/roadmap-workbench.tsx
features/study/components/topic-study-page.tsx
features/upload/components/upload-queue.tsx
features/viva/components/oral-assessment.tsx
hooks/use-active-workspace.ts
hooks/use-deadline-countdown.ts
lib/api/resources.ts
lib/auth/config.ts
lib/constants.ts
lib/query-client-provider.tsx
lib/resources/validate.ts
lib/study/question-rank.ts
lib/study/unit-clearance.ts
lib/supabase/admin.ts
lib/supabase/client.ts
lib/supabase/server.ts
lib/utils.ts
LICENSE
middleware.ts
next-env.d.ts
next.config.mjs
package.json
postcss.config.mjs
README.md
SECURITY.md
server/ai/__tests__/concept-guide.test.ts
server/ai/__tests__/generation-provider.test.ts
server/ai/__tests__/study-brief.test.ts
server/ai/concept-guide.ts
server/ai/config.ts
server/ai/context-pack.ts
server/ai/contracts.ts
server/ai/generation-provider.ts
server/ai/study-brief.ts
server/ai/telemetry.ts
server/ai/user-keys.ts
[92 more files omitted for size]
```

### Dependencies

- package.json: @aws-sdk/client-bedrock-runtime@^3.1090.0, @aws-sdk/client-textract@^3.1090.0, @radix-ui/react-slot@^1.1.0, @supabase/ssr@^0.5.2, @supabase/supabase-js@^2.45.4, @tanstack/react-query@^5.59.16, @types/node@^22.9.0, @types/react@^19.0.0, @types/react-dom@^19.0.0, @xyflow/react@^12.3.5, autoprefixer@^10.4.20, class-variance-authority@^0.7.0, clsx@^2.1.1, compromise@^14.16.0, dotenv@^17.4.2, drizzle-kit@^0.26.2, drizzle-orm@^0.35.3, eslint@^9.14.0, eslint-config-next@^15.0.3, inngest@^4.13.0, jszip@^3.10.1, lucide-react@^0.468.0, mammoth@^1.12.0, next@^15.0.3, pdf-parse@^2.4.5, postcss@^8.4.49, postgres@^3.4.5, react@^19.0.0, react-dom@^19.0.0, tailwind-merge@^2.5.4, tailwindcss@^3.4.15, tsx@^4.23.1, typescript@^5.6.3, zustand@^5.0.1

### Recent commits (newest first)

- Prepare the repository for a clean public release.
- Ignore heavy local artifacts in Vercel uploads.
- Ship study view splits, demo prep, and section load optimizations.
- Merge pull request #3 from nothariharan/narain-new-ready
- Fix narain-new for safe merge: migration, public API, and timer.
- Implement performance, UI, rescue mode, active recall and public workspace sharing
- Add oral assessment viva with prep reports on Review Desk.
- Add ranked question review and section mastery tests.
- Polish product UI into a cohesive Craft desk experience.
- feat: add illustrative route loader
- fix: smooth product route transitions
- feat: add rescue roadmap and last lap audio
- feat: gate onboarding with resources and calibration
- fix: format notes html actions
- feat: rework notes and question organization
- fix: smooth roadmap node dragging
- feat: make roadmap canvas horizontal and draggable
- feat: restore zoomable roadmap canvas
- feat: make roadmap expandable with route loading
- feat: add return to workspace shortcut

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

### SECURITY.md

```markdown
# Security policy

## Supported versions

Security fixes are applied on the `main` branch of this repository.

## Reporting a vulnerability

Please **do not** open a public GitHub issue for security-sensitive bugs.

Email the maintainer via the contact listed on the GitHub profile for [nothariharan](https://github.com/nothariharan), or open a **private** GitHub security advisory on this repository if available.

Include:

- Description of the issue and impact
- Steps to reproduce (PoC if possible)
- Affected routes, env flags, or deployment assumptions

You should receive an acknowledgement within a few days. Please give a reasonable window before any public disclosure.

## What to expect in this codebase

- **Secrets** must never be committed. Use `.env` locally and your host’s secret store in production (see `.env.example`).
- **Service role / database URLs** are server-only. Do not expose them to the browser or `NEXT_PUBLIC_*` variables.
- **BYOK provider keys** are encrypted at rest only when `AI_KEY_ENCRYPTION_SECRET` is set.
- **Demo prep** (`ENABLE_DEMO_PREP`) can alter mastery state for demos. Keep it disabled on public production.
- **Public workspace sharing** is opt-in per workspace and should be treated as limited metadata exposure, not a full ACL system.

## Dependency and supply-chain hygiene

```bash
npm audit
npm run lint
npm run typecheck
```

Report malicious dependency behavior the same way as application vulnerabilities.

```

### CONTRIBUTING.md

```markdown
# Contributing to Crux

Thanks for helping improve Crux. This guide keeps the deadline-first product sharp and the repo safe to keep public.

## Before you start

1. Fork and clone the repo (or use a branch on a collaborator checkout).
2. Copy `.env.example` → `.env` and fill at least the Supabase + `DATABASE_URL` values if you need the full study loop.
3. Install and verify:

```bash
npm install
npm run lint
npm run typecheck
npm test
```

## Development workflow

1. Create a focused branch: `feat/…`, `fix/…`, or `docs/…`.
2. Prefer small PRs that solve one problem.
3. Match existing patterns under `app/`, `features/`, `server/`, and `components/`.
4. Do **not** commit `.env`, PDFs under `docs/`, log files, or `.vercel` state.
5. Run checks before opening a PR:

```bash
npm run lint
npm run typecheck
npm test
npm run build
```

Avoid running `npm run build` while `npm run dev` is still using the same `.next` directory — stop the dev server first.

## Architecture expectations

- **Feature UI** lives in `features/<area>/components`.
- **Server logic** lives in `server/` (ingestion, planning, retrieval, AI, demo helpers).
- **API routes** under `app/api` should stay thin: auth, validation, call into `server/`.
- Study data should stay **view-scoped** (`/api/workspaces/[id]/study?view=…`) so navigation stays light.
- Do not reintroduce a “fetch the entire study universe” path for every section unless there is a strong reason.

## Demo prep

The Settings **Demo prep** panel and `/api/workspaces/[id]/demo-prep` exist for presentations. They are gated by:

```env
ENABLE_DEMO_PREP=true
NEXT_PUBLIC_ENABLE_DEMO_PREP=true
```

Do not enable these flags in shared production by default. Do not remove the server-side gate.

## Documentation

- User-facing setup belongs in `README.md`.
- Design / PRD material belongs in `docs/` — update [docs/README.md](docs/README.md) when you add a doc.
- Historical docs may still say **LastLap**; the product name is **Crux**. Prefer Crux in new writing.

## Pull requests

- Describe **why** the change exists, not only what files moved.
- Link issues when relevant.
- Call out migrations, new env vars, or breaking API changes in the PR body.
- Screenshots or short clips help for UI changes.

## Code of conduct

Be respectful. Assume good intent. No harassment, spam, or drive-by secret dumps. Maintainers may close PRs that ship credentials, scraped private data, or unsafe demo bypasses without documentation.

```

### package.json

```
{
  "name": "crux",
  "version": "0.1.0",
  "private": true,
  "description": "Deadline-first study workspace: upload sources, map topics, calibrate, plan, and clear units before the exam.",
  "license": "MIT",
  "engines": {
    "node": ">=20"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/nothariharan/crux.git"
  },
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint . --max-warnings=0",
    "typecheck": "tsc --noEmit",
    "test": "node --import tsx --test server/planning/__tests__/priority.test.ts server/ingestion/__tests__/roadmap.test.ts server/ingestion/__tests__/extract.test.ts server/ingestion/__tests__/excerpts.test.ts server/ingestion/__tests__/trust.test.ts server/questions/__tests__/clusters.test.ts server/questions/__tests__/answer-shape.test.ts server/retrieval/__tests__/study-materials.test.ts server/retrieval/__tests__/hybrid-materials.test.ts server/study/__tests__/source-description.test.ts server/ai/__tests__/study-brief.test.ts server/ai/__tests__/concept-guide.test.ts server/ai/__tests__/generation-provider.test.ts server/learning/__tests__/video-recommendations.test.ts",
    "db:generate": "drizzle-kit generate",
    "db:migrate": "drizzle-kit migrate && tsx server/db/apply-supabase-rls.ts",
    "db:push": "drizzle-kit push && tsx server/db/apply-supabase-rls.ts",
    "db:seed": "tsx server/db/seed.ts",
    "db:rls": "tsx server/db/apply-supabase-rls.ts",
    "db:apply": "tsx server/db/apply-sql-file.ts",
    "db:baseline": "tsx server/db/baseline-drizzle-migrations.ts"
  },
  "dependencies": {
    "@aws-sdk/client-bedrock-runtime": "^3.1090.0",
    "@aws-sdk/client-textract": "^3.1090.0",
    "@radix-ui/react-slot": "^1.1.0",
    "@supabase/ssr": "^0.5.2",
    "@supabase/supabase-js": "^2.45.4",
    "@tanstack/react-query": "^5.59.16",
    "@xyflow/react": "^12.3.5",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.1",
    "compromise": "^14.16.0",
    "drizzle-orm": "^0.35.3",
    "inngest": "^4.13.0",
    "jszip": "^3.10.1",
    "lucide-react": "^0.468.0",
    "mammoth": "^1.12.0",
    "next": "^15.0.3",
    "pdf-parse": "^2.4.5",
    "postgres": "^3.4.5",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "tailwind-merge": "^2.5.4",
    "zustand": "^5.0.1"
  },
  "devDependencies": {
    "@types/node": "^22.9.0",
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "autoprefixer": "^10.4.20",
    "dotenv": "^17.4.2",
    "drizzle-kit": "^0.26.2",
    "eslint": "^9.14.0",
    "eslint-config-next": "^15.0.3",
    "postcss": "^8.4.49",
    "tailwindcss": "^3.4.15",
    "tsx": "^4.23.1",
    "typescript": "^5.6.3"
  }
}

```

### app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Suspense } from "react";
import { Source_Sans_3, Source_Serif_4 } from "next/font/google";
import "./globals.css";
import { QueryClientProvider } from "@/lib/query-client-provider";
import { ProductRouteLoader } from "@/components/product-route-loader";

const sans = Source_Sans_3({
  subsets: ["latin"],
  variable: "--font-sans",
  display: "swap"
});

const display = Source_Serif_4({
  subsets: ["latin"],
  variable: "--font-display",
  display: "swap"
});

export const metadata: Metadata = {
  title: "Crux — Study with a point of view",
  description: "Deadline-first study planning grounded in your own material."
};

export default function RootLayout({
  children
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en" className={`${sans.variable} ${display.variable}`}>
      <body>
        <a href="#main-content" className="sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-50 focus:rounded-md focus:bg-background focus:px-3 focus:py-2 focus:text-sm focus:shadow-lg">Skip to content</a>
        <QueryClientProvider>
          <Suspense fallback={null}>
            <ProductRouteLoader />
          </Suspense>
          {children}
        </QueryClientProvider>
      </body>
    </html>
  );
}

```

### app/page.tsx

```typescript
import Image from "next/image";
import Link from "next/link";
import {
  ArrowRightIcon,
  BookOpenCheckIcon,
  BracesIcon,
  CheckIcon,
  CirclePlayIcon,
  FileUpIcon,
  KeyRoundIcon,
  LightbulbIcon,
  RouteIcon,
  ScanSearchIcon,
  SparklesIcon
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { ReturnWorkspaceLink } from "@/components/return-workspace-link";

const workflow = [
  {
    number: "01",
    title: "Bring your material",
    copy: "Drop in a syllabus, lecture slides, notes, and past papers. Crux keeps the source trail intact.",
    icon: FileUpIcon
  },
  {
    number: "02",
    title: "Find the real work",
    copy: "A quick confidence check and source analysis reveal the concepts worth your limited time.",
    icon: ScanSearchIcon
  },
  {
    number: "03",
    title: "Study the next move",
    copy: "Follow a living roadmap with linked explanations, source excerpts, videos, and review notes.",
    icon: RouteIcon
  }
];

const materials = ["Syllabus.pdf", "Week 6 slides", "Past-paper set", "My notes.md"];

export default function HomePage() {
  return (
    <main id="main-content" className="crux-home">
      <div className="crux-ribbon">A study workspace for people with a date circled on the calendar.</div>

      <section className="crux-stage">
        <nav aria-label="Primary navigation" className="crux-nav">
          <Link href="/" className="crux-wordmark" aria-label="Crux home">
            crux<span aria-hidden="true">.</span>
          </Link>
          <div className="crux-nav-links" aria-label="Explore Crux">
            <a href="#how-it-works">How it works</a>
            <a href="#study-system">Study system</a>
            <a href="#for-students">For students</a>
          </div>
          <div className="flex items-center gap-1.5 sm:gap-2">
            <ReturnWorkspaceLink />
            <Button variant="ghost" className="hidden sm:inline-flex" asChild>
              <Link href="/login">Log in</Link>
            </Button>
            <Button className="crux-nav-cta" size="sm" asChild>
              <Link href="/onboarding">Start free <ArrowRightIcon data-icon="inline-end" /></Link>
            </Button>
          </div>
        </nav>

        <div className="crux-hero-copy">
          <p className="crux-eyebrow"><span>✦</span> A clearer way to prepare</p>
          <h1>Your study plan, <em>with a point of view.</em></h1>
          <p className="crux-hero-summary">Crux reads the material you have, checks where you stand, and gives you the most useful thing to study next.</p>
          <div className="crux-hero-actions">
            <Button size="lg" className="crux-dark-button" asChild>
              <Link href="/onboarding">Build my study plan <ArrowRightIcon data-icon="inline-end" /></Link>
            </Button>
            <Button size="lg" variant="ghost" className="crux-watch-button" asChild>
              <a href="#how-it-works"><CirclePlayIcon /> See how it works</a>
            </Button>
          </div>
        </div>

        <div className="crux-sky-line" aria-hidden="true" />
        <div className="crux-hero-visual" aria-label="An example Crux study plan">
          <div className="crux-paper-hill crux-paper-hill-left" aria-hidden="true" />
          <div className="crux-paper-hill crux-paper-hill-right" aria-hidden="true" />
          <div className="crux-hero-window">
            <div className="crux-window-topbar">
              <div className="crux-window-dots"><i /><i /><i /></div>
              <span>My exam desk</span>
              <SparklesIcon />
            </div>
            <aside className="crux-window-sidebar">
              <p>WORKSPACE</p>
              <strong>Operating systems</strong>
              <span>Workspace</span>
              <span>Study Map</span>
              <span>Study</span>
              <span>Clearance</span>
            </aside>
            <div className="crux-window-main">
              <div className="crux-window-heading"><div><p>Monday · 9:00 AM</p><h2>What should I study?</h2></div><span className="crux-ready-chip">72% ready</span></div>
              <div className="crux-window-columns">
                <div className="crux-plan-card crux-plan-card-primary"><p>Best next move</p><h3>CPU scheduling</h3><span>High impact · 45 min</span><div className="crux-progress"><i /></div><button>Open concept <ArrowRightIcon /></button></div>
                <div className="crux-window-list"><p>UP NEXT</p><div><b>2</b><span><strong>Deadlocks</strong><small>40 min · Source linked</small></span></div><div><b>3</b><span><strong>Memory management</strong><small>35 min · Quick review</small></span></div><div><b>4</b><span><strong>File systems</strong><small>30 min · New concept</small></span></div></div>
              </div>
            </div>
          </div>
          <Image className="crux-hero-collage" src="/images/crux-study-collage.png" alt="A paper collage of study cards, books, a pencil, and an alarm clock" width={1536} height={1024} priority sizes="(max-width: 900px) 90vw, 55vw" />
        </div>
      </section>

      <section id="how-it-works" className="crux-section crux-workflow">
        <div className="crux-section-intro"><p className="crux-kicker">The study loop</p><h2>Less organizing.<br /><em>More understanding.</em></h2></div>
        <div className="crux-workflow-grid">
          {workflow.map(({ number, title, copy, icon: Icon }) => <article className="crux-workflow-card" key={number}><div className="crux-workflow-icon"><Icon /></div><span>{number}</span><h3>{title}</h3><p>{copy}</p></article>)}
        </div>
      </section>

      <section id="study-system" className="crux-section crux-material-section">
        <div className="crux-material-copy"><p className="crux-kicker">Your material, connected</p><h2>From a pile of files to <em>one place to think.</em></h2><p>Every recommendation carries its reason with it. Open the concept, see the source excerpt, use the stu
[truncated — 2506 more characters]
```

### app/onboarding/page.tsx

```typescript
import { IntakeFlow } from "@/features/onboarding/components/intake-flow";

export default function OnboardingPage() {
  return <IntakeFlow />;
}

```

### server/db/index.ts

```typescript
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import * as schema from "./schema";

const connectionString = process.env.DATABASE_URL;

export const db = connectionString
  ? drizzle(postgres(connectionString, { prepare: false }), { schema })
  : null;

```

### app/(product)/layout.tsx

```typescript
import { Suspense } from "react";
import { AppShell } from "@/components/app-shell";
import { WorkspaceBootstrap } from "@/components/workspace-bootstrap";
import { WorkspaceOpenFallback } from "@/components/workspace-open-fallback";

export default function ProductLayout({
  children
}: Readonly<{
  children: React.ReactNode;
}>) {
  return <><WorkspaceBootstrap /><Suspense fallback={<WorkspaceOpenFallback />}><AppShell>{children}</AppShell></Suspense></>;
}

```

### lib/supabase/server.ts

```typescript
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";

type CookieToSet = {
  name: string;
  value: string;
  options?: {
    domain?: string;
    expires?: Date;
    httpOnly?: boolean;
    maxAge?: number;
    path?: string;
    sameSite?: boolean | "lax" | "strict" | "none";
    secure?: boolean;
  };
};

export async function createClient() {
  const cookieStore = await cookies();

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL ?? "",
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ?? "",
    {
      cookies: {
        getAll() {
          return cookieStore.getAll();
        },
        setAll(cookiesToSet: CookieToSet[]) {
          cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options));
        }
      }
    }
  );
}

```

### app/login/page.tsx

```typescript
"use client";

import { useState } from "react";
import Link from "next/link";
import { MailIcon, MoveLeftIcon } from "lucide-react";
import { createClient } from "@/lib/supabase/client";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";

export default function LoginPage() {
  const [email, setEmail] = useState("");
  const [notice, setNotice] = useState("");
  const [sending, setSending] = useState(false);
  async function signInWithEmail() {
    setSending(true);
    const { error } = await createClient().auth.signInWithOtp({ email, options: { emailRedirectTo: `${window.location.origin}/auth/callback` } });
    setNotice(error?.message ?? "Check your inbox for a secure sign-in link.");
    setSending(false);
  }
  async function signInWithGoogle() {
    const { error } = await createClient().auth.signInWithOAuth({ provider: "google", options: { redirectTo: `${window.location.origin}/auth/callback` } });
    if (error) setNotice(error.message);
  }
  return <main className="login-page"><Link href="/" className="intake-brand">crux<span>.</span></Link><Card className="login-card w-full max-w-md"><CardHeader><Button variant="ghost" size="sm" asChild className="mb-3 w-fit"><Link href="/"><MoveLeftIcon data-icon="inline-start" />Back</Link></Button><p className="intake-kicker">Your private study desk</p><CardTitle className="font-display text-4xl tracking-normal">Enter Crux</CardTitle><CardDescription>Your workspaces are private to your account.</CardDescription></CardHeader><CardContent className="flex flex-col gap-3"><label className="flex flex-col gap-2 text-sm font-semibold">Email<Input type="email" value={email} onChange={(event) => setEmail(event.target.value)} placeholder="you@example.com" /></label><Button className="crux-dark-button" disabled={!email || sending} onClick={() => void signInWithEmail()}><MailIcon data-icon="inline-start" />{sending ? "Sending…" : "Email me a sign-in link"}</Button><Button variant="outline" onClick={() => void signInWithGoogle()}><span data-icon="inline-start" className="font-bold">G</span>Continue with Google</Button>{notice && <p role="status" className="text-sm text-muted-foreground">{notice}</p>}</CardContent></Card></main>;
}

```

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