# Project export: ∆ScrutinizerΩ

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: UC Berkeley AI Hackathon 2025
- Tagline: Redefine how we understand the world
- Devpost: https://devpost.com/software/scrutinizer
- GitHub: https://github.com/justin79653/-Scrutinizer-
- Video: https://www.youtube.com/embed/4NDnAkxBLIs?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

### Inspiration

The landscape of STEM education is robust, built on a strong foundation of proven pedagogical methods. We see a significant opportunity not to replace these methods, but to augment them with a powerful new modality enabled by generative AI. The next frontier in learning is the development of durable, quantitative intuition—an innate "feel" for abstract concepts. Cultivating this intuition is the key to unlocking higher-level problem-solving and, critically, enhancing real-world risk assessment. ∆ScrutinizerΩ is positioned to lead this new category of cognitive augmentation. Our mission is to complement formal education by providing a tool that translates abstract theory into tangible, intuitive understanding at scale.

### What it does

∆ScrutinizerΩ is a real-time, multimodal animation generation engine that functions as an on-demand visual-analytic partner. The system is designed to receive complex user queries in natural language, augmented by image-based inputs of real-world scenarios. Upon receiving a prompt, the engine processes the query and generates a concise, programmatically accurate Manim animation. This output is not a static video but a bespoke, dynamically-generated explanation that visualizes the underlying principles—be it physics, chemistry, or biology—of the user's specific query. This positions ∆ScrutinizerΩ not as a content library, but as a generative tool for personalized, just-in-time learning. How we build it Our MVP was rapidly prototyped on Replit and is powered by the Gemini 2.5 API, selected for its advanced, multimodal reasoning capabilities. Our core intellectual property lies in a proprietary, structured inference framework designed to constrain the LLM and ensure reliable, accurate, and pedagogically sound outputs. This framework operates via a multi-stage prompt-chaining strategy: Deconstruction & Principle Identification: The initial prompt layer instructs the model to parse the user's query and uploaded image (via Base64 encoding) to identify the core scientific principles and relevant physical variables. Object & Style Abstraction: The model is directed to abstract the identified objects into simple geometric primitives and to derive stylistic guidance from the source image, ensuring visual clarity. Dynamic Relationship Mapping: The central animation is generated to illustrate the cause-and-effect relationships between the identified variables. Quantitative Overlay: We enforce the integration of the relevant mathematical equations using Manim's MathTex function, directly linking the visual representation to the formal quantitative model. Defensive Prompting & Error Prevention: The prompt architecture is heavily "defensive." It includes a library of negative constraints, explicitly forbidding the use of Manim functions and syntax patterns that we have identified as sources of common generation errors. This defensive design is crucial for maintaining system reliability. This entire process is governed by a standardized flow structure to guarantee a consistent and effective user experience.

### Challenges we ran into

Our development process validated our architectural choices through methodical de-risking. Initial exploration with closed-loop platforms like Google AI Studio proved insufficient, as they lacked the necessary execution environment for dynamic Manim rendering. A subsequent iteration on Replit with its native LLM showed performance limitations in complex, multi-step reasoning, frequently resulting in incomplete or overly simplistic outputs. This led to our key strategic pivot: integrating the Gemini 2.5 Flash API. This decision unlocked the required level of reasoning fidelity. The final challenge involved managing minor code-generation errors. We mitigated this by refining our defensive prompting to restrict the model to a subset of highly reliable Manim functions, thereby maximizing output quality while operating within current API limitations.

### Accomplishments we're proud of

To date, we have successfully achieved two critical milestones: Functional Agent Validation: We have a functioning end-to-end agent capable of processing text prompts and generating accurate, relevant Manim animations. Multimodal Integration: We have successfully integrated and validated the image-upload feature. This is a pivotal technical achievement, as it validates our core thesis of bridging the gap between real-world observation and abstract scientific understanding.

### What we learned

This project has yielded significant strategic insights into the current state of AI-driven product development: Accelerated Development Cycles: We have validated that a small, agile team can now prototype and deploy sophisticated, scalable AI-native applications in under 24 hours. This paradigm shift dramatically reduces time-to-market and capital requirements for new ventures. Prompt Engineering as Core Competency: Our work confirms that sophisticated prompt architecture—particularly defensive and structured prompting—is a core engineering discipline and a key differentiator in building reliable applications on top of foundational models.

### What's next

∆ScrutinizerΩ is architected for significant future expansion. Our strategic roadmap is focused on escalating our technological moat and market penetration. Phase 1 (Next 2-3 Months): Integrate real-time camera feeds to enable live analysis and animation generation. Begin expanding the knowledge base to adjacent quantitative fields, including chemistry, biology, and statistics etc. Phase 2 (1-2 Years): Develop and deploy the engine for AR/VR platforms. This will enable users to overlay our intuitive visualizations directly onto their physical environment, creating a truly immersive educational and analytical tool. The market for AR/VR in education is projected to grow exponentially, and we will be positioned as a premier content-generation engine for these platforms. Phase 3 (2+ Years - R&D): Initiate long-term research and development into direct brain-computer interfaces (BCI). The ultimate vision is to create a seamless cognitive augmentation tool that transcends screens, making intuitive quantitative understanding a native human sense. This represents a foundational technology with transformative potential across all sectors.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 69 recognized source files, 206 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code
- Python (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (75 of 75)

```
.gitignore
.replit
attached_assets/Pasted-Hello-I-d-like-to-start-a-new-project-My-goal-is-to-build-a-web-based-physics-tutoring-application-1750558625012_1750558625013.txt
client/index.html
client/src/App.tsx
client/src/components/ui/accordion.tsx
client/src/components/ui/alert-dialog.tsx
client/src/components/ui/alert.tsx
client/src/components/ui/aspect-ratio.tsx
client/src/components/ui/avatar.tsx
client/src/components/ui/badge.tsx
client/src/components/ui/breadcrumb.tsx
client/src/components/ui/button.tsx
client/src/components/ui/calendar.tsx
client/src/components/ui/card.tsx
client/src/components/ui/carousel.tsx
client/src/components/ui/chart.tsx
client/src/components/ui/checkbox.tsx
client/src/components/ui/collapsible.tsx
client/src/components/ui/command.tsx
client/src/components/ui/context-menu.tsx
client/src/components/ui/dialog.tsx
client/src/components/ui/drawer.tsx
client/src/components/ui/dropdown-menu.tsx
client/src/components/ui/form.tsx
client/src/components/ui/hover-card.tsx
client/src/components/ui/input-otp.tsx
client/src/components/ui/input.tsx
client/src/components/ui/label.tsx
client/src/components/ui/menubar.tsx
client/src/components/ui/navigation-menu.tsx
client/src/components/ui/pagination.tsx
client/src/components/ui/popover.tsx
client/src/components/ui/progress.tsx
client/src/components/ui/radio-group.tsx
client/src/components/ui/resizable.tsx
client/src/components/ui/scroll-area.tsx
client/src/components/ui/select.tsx
client/src/components/ui/separator.tsx
client/src/components/ui/sheet.tsx
client/src/components/ui/sidebar.tsx
client/src/components/ui/skeleton.tsx
client/src/components/ui/slider.tsx
client/src/components/ui/switch.tsx
client/src/components/ui/table.tsx
client/src/components/ui/tabs.tsx
client/src/components/ui/textarea.tsx
client/src/components/ui/toast.tsx
client/src/components/ui/toaster.tsx
client/src/components/ui/toggle-group.tsx
client/src/components/ui/toggle.tsx
client/src/components/ui/tooltip.tsx
client/src/hooks/use-mobile.tsx
client/src/hooks/use-toast.ts
client/src/index.css
client/src/lib/queryClient.ts
client/src/lib/utils.ts
client/src/main.tsx
client/src/pages/home.tsx
client/src/pages/not-found.tsx
components.json
drizzle.config.ts
package.json
postcss.config.js
replit.md
server/index.ts
server/routes.ts
server/services/gemini.ts
server/services/manim.ts
server/storage.ts
server/vite.ts
shared/schema.ts
tailwind.config.ts
tsconfig.json
vite.config.ts
```

### Dependencies

- package.json: @google/genai@^1.6.0, @hookform/resolvers@^3.10.0, @jridgewell/trace-mapping@^0.3.25, @neondatabase/serverless@^0.10.4, @radix-ui/react-accordion@^1.2.4, @radix-ui/react-alert-dialog@^1.1.7, @radix-ui/react-aspect-ratio@^1.1.3, @radix-ui/react-avatar@^1.1.4, @radix-ui/react-checkbox@^1.1.5, @radix-ui/react-collapsible@^1.1.4, @radix-ui/react-context-menu@^2.2.7, @radix-ui/react-dialog@^1.1.7, @radix-ui/react-dropdown-menu@^2.1.7, @radix-ui/react-hover-card@^1.1.7, @radix-ui/react-label@^2.1.3, @radix-ui/react-menubar@^1.1.7, @radix-ui/react-navigation-menu@^1.2.6, @radix-ui/react-popover@^1.1.7, @radix-ui/react-progress@^1.1.3, @radix-ui/react-radio-group@^1.2.4, @radix-ui/react-scroll-area@^1.2.4, @radix-ui/react-select@^2.1.7, @radix-ui/react-separator@^1.1.3, @radix-ui/react-slider@^1.2.4, @radix-ui/react-slot@^1.2.0, @radix-ui/react-switch@^1.1.4, @radix-ui/react-tabs@^1.1.4, @radix-ui/react-toast@^1.2.7, @radix-ui/react-toggle@^1.1.3, @radix-ui/react-toggle-group@^1.1.3, @radix-ui/react-tooltip@^1.2.0, @replit/vite-plugin-cartographer@^0.2.7, @replit/vite-plugin-runtime-error-modal@^0.0.3, @tailwindcss/typography@^0.5.15, @tailwindcss/vite@^4.1.3, @tanstack/react-query@^5.60.5, @types/connect-pg-simple@^7.0.3, @types/express@4.17.21, @types/express-session@^1.18.0, @types/multer@^1.4.13, @types/node@20.16.11, @types/passport@^1.0.16, @types/passport-local@^1.0.38, @types/react@^18.3.11, @types/react-dom@^18.3.1, @types/ws@^8.5.13, @vitejs/plugin-react@^4.3.2, autoprefixer@^10.4.20, bufferutil@^4.0.8, class-variance-authority@^0.7.1, clsx@^2.1.1, cmdk@^1.1.1, connect-pg-simple@^10.0.0, date-fns@^3.6.0, drizzle-kit@^0.30.4, drizzle-orm@^0.39.1, drizzle-zod@^0.7.0, embla-carousel-react@^8.6.0, esbuild@^0.25.0, express@^4.21.2, express-session@^1.18.1, framer-motion@^11.13.1, input-otp@^1.4.2, lucide-react@^0.453.0, memorystore@^1.6.7, multer@^2.0.1, nanoid@^5.1.5, next-themes@^0.4.6, passport@^0.7.0, passport-local@^1.0.0, postcss@^8.4.47, react@^18.3.1, react-day-picker@^8.10.1, react-dom@^18.3.1, react-hook-form@^7.55.0, react-icons@^5.4.0, react-resizable-panels@^2.1.7, recharts@^2.15.2, tailwind-merge@^2.6.0, tailwindcss@^3.4.17, tailwindcss-animate@^1.0.7, tsx@^4.19.1, tw-animate-css@^1.2.5, typescript@5.6.3, vaul@^1.1.2, vite@^5.4.19, wouter@^3.3.5, ws@^8.18.0, zod@^3.24.2, zod-validation-error@^3.4.0

### Recent commits (newest first)

- Improve AI animation generation by enforcing color and syntax rules
- Improve AI animation generation by enforcing color and syntax rules
- Improve AI animation generation by enforcing coding standard rules
- Improve AI animation generation by enforcing coding standard rules
- Improve animation generation by enforcing stricter code guidelines
- Improve animation generation by enforcing stricter code guidelines
- Allow users to include multiple images when creating new animations
- Allow users to include multiple images when creating new animations
- Ensure video links work and remove temporary files after generation
- Ensure video links work and remove temporary files after generation
- Allow users to upload images to enhance the learning model
- Allow users to upload images to enhance the learning model
- Enable users to upload images for more detailed physics explanations
- Enable users to upload images for more detailed physics explanations
- Enable instant animation generation when selecting popular physics concepts
- Enable instant animation generation when selecting popular physics concepts
- Update video descriptions and fix download functionality for animations
- Update video descriptions and fix download functionality for animations
- Switch to faster AI model to avoid usage limits and maintain functionality
- Switch to faster AI model to avoid usage limits and maintain functionality

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

### replit.md

```markdown
# Physics Animator

## Overview

Physics Animator is a web-based educational tool that generates interactive physics animations using AI. Students can input physics concepts and receive custom-generated Manim animations that visualize and explain the concepts in an engaging way.

## System Architecture

The application follows a full-stack architecture with clear separation of concerns:

### Frontend Architecture
- **React SPA**: Built with React 18 using TypeScript for type safety
- **UI Components**: Leverages shadcn/ui component library for consistent, modern design
- **Styling**: TailwindCSS for utility-first styling with CSS variables for theming
- **State Management**: React Query for server state management and caching
- **Routing**: Wouter for lightweight client-side routing

### Backend Architecture
- **Express.js Server**: RESTful API built with Express and TypeScript
- **Modular Structure**: Clean separation with dedicated services for AI and video processing
- **File Handling**: Static file serving for generated video content
- **Process Management**: Child process execution for Manim script rendering

### Data Storage
- **PostgreSQL**: Primary database using Neon serverless PostgreSQL
- **Drizzle ORM**: Type-safe database operations with schema validation
- **Schema Design**: 
  - Users table for authentication
  - Animations table tracking concept, script, status, and video path
  - Zod schemas for runtime validation

## Key Components

### AI Integration
- **Gemini API**: Uses Google's Gemini 2.5 Flash model for Python script generation
- **Prompt Engineering**: Specialized meta-prompts that instruct Gemini to generate educational Manim code
- **Error Handling**: Comprehensive error management for AI service failures

### Video Generation Pipeline
- **Manim Integration**: Python Manim library for creating mathematical animations
- **Script Execution**: Secure execution of AI-generated Python scripts in isolated environment
- **File Management**: Temporary file creation, execution, and cleanup processes
- **Video Serving**: Static file server for generated MP4 content

### User Interface
- **Progressive Enhancement**: Loading states and error boundaries for smooth UX
- **Responsive Design**: Mobile-first approach with adaptive layouts
- **Accessibility**: Proper ARIA labels and keyboard navigation support
- **Toast Notifications**: User feedback for successful operations and errors

## Data Flow

1. **User Input**: Student enters physics concept in text field
2. **AI Generation**: Backend sends structured prompt to Gemini API
3. **Script Processing**: Generated Python script is validated and saved
4. **Video Creation**: Manim executes script to produce MP4 animation
5. **File Serving**: Video is made available via static file endpoint
6. **Display**: Frontend shows generated animation to user

## External Dependencies

### Core Technologies
- **React Ecosystem**: React 18, TypeScript, Vite for development
- **UI Libraries**: Radix U
[truncated — 2312 more characters]
```

### package.json

```
{
  "name": "rest-express",
  "version": "1.0.0",
  "type": "module",
  "license": "MIT",
  "scripts": {
    "dev": "NODE_ENV=development tsx server/index.ts",
    "build": "vite build && esbuild server/index.ts --platform=node --packages=external --bundle --format=esm --outdir=dist",
    "start": "NODE_ENV=production node dist/index.js",
    "check": "tsc",
    "db:push": "drizzle-kit push"
  },
  "dependencies": {
    "@google/genai": "^1.6.0",
    "@hookform/resolvers": "^3.10.0",
    "@jridgewell/trace-mapping": "^0.3.25",
    "@neondatabase/serverless": "^0.10.4",
    "@radix-ui/react-accordion": "^1.2.4",
    "@radix-ui/react-alert-dialog": "^1.1.7",
    "@radix-ui/react-aspect-ratio": "^1.1.3",
    "@radix-ui/react-avatar": "^1.1.4",
    "@radix-ui/react-checkbox": "^1.1.5",
    "@radix-ui/react-collapsible": "^1.1.4",
    "@radix-ui/react-context-menu": "^2.2.7",
    "@radix-ui/react-dialog": "^1.1.7",
    "@radix-ui/react-dropdown-menu": "^2.1.7",
    "@radix-ui/react-hover-card": "^1.1.7",
    "@radix-ui/react-label": "^2.1.3",
    "@radix-ui/react-menubar": "^1.1.7",
    "@radix-ui/react-navigation-menu": "^1.2.6",
    "@radix-ui/react-popover": "^1.1.7",
    "@radix-ui/react-progress": "^1.1.3",
    "@radix-ui/react-radio-group": "^1.2.4",
    "@radix-ui/react-scroll-area": "^1.2.4",
    "@radix-ui/react-select": "^2.1.7",
    "@radix-ui/react-separator": "^1.1.3",
    "@radix-ui/react-slider": "^1.2.4",
    "@radix-ui/react-slot": "^1.2.0",
    "@radix-ui/react-switch": "^1.1.4",
    "@radix-ui/react-tabs": "^1.1.4",
    "@radix-ui/react-toast": "^1.2.7",
    "@radix-ui/react-toggle": "^1.1.3",
    "@radix-ui/react-toggle-group": "^1.1.3",
    "@radix-ui/react-tooltip": "^1.2.0",
    "@tanstack/react-query": "^5.60.5",
    "@types/multer": "^1.4.13",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "^1.1.1",
    "connect-pg-simple": "^10.0.0",
    "date-fns": "^3.6.0",
    "drizzle-orm": "^0.39.1",
    "drizzle-zod": "^0.7.0",
    "embla-carousel-react": "^8.6.0",
    "express": "^4.21.2",
    "express-session": "^1.18.1",
    "framer-motion": "^11.13.1",
    "input-otp": "^1.4.2",
    "lucide-react": "^0.453.0",
    "memorystore": "^1.6.7",
    "multer": "^2.0.1",
    "nanoid": "^5.1.5",
    "next-themes": "^0.4.6",
    "passport": "^0.7.0",
    "passport-local": "^1.0.0",
    "react": "^18.3.1",
    "react-day-picker": "^8.10.1",
    "react-dom": "^18.3.1",
    "react-hook-form": "^7.55.0",
    "react-icons": "^5.4.0",
    "react-resizable-panels": "^2.1.7",
    "recharts": "^2.15.2",
    "tailwind-merge": "^2.6.0",
    "tailwindcss-animate": "^1.0.7",
    "tw-animate-css": "^1.2.5",
    "vaul": "^1.1.2",
    "wouter": "^3.3.5",
    "ws": "^8.18.0",
    "zod": "^3.24.2",
    "zod-validation-error": "^3.4.0"
  },
  "devDependencies": {
    "@replit/vite-plugin-cartographer": "^0.2.7",
    "@replit/vite-plugin-runtime-error-modal": "^0.0.3",
    "@tailwindcss/typography": "^0.5.15",
    "@tailwindcss/vite": "^4.1.3",
    "@types/connect-pg-simple": "^7.0.3",
    "@types/express": "4.17.21",
    "@types/express-session": "^1.18.0",
    "@types/node": "20.16.11",
    "@types/passport": "^1.0.16",
    "@types/passport-local": "^1.0.38",
    "@types/react": "^18.3.11",
    "@types/react-dom": "^18.3.1",
    "@types/ws": "^8.5.13",
    "@vitejs/plugin-react": "^4.3.2",
    "autoprefixer": "^10.4.20",
    "drizzle-kit": "^0.30.4",
    "esbuild": "^0.25.0",
    "postcss": "^8.4.47",
    "tailwindcss": "^3.4.17",
    "tsx": "^4.19.1",
    "typescript": "5.6.3",
    "vite": "^5.4.19"
  },
  "optionalDependencies": {
    "bufferutil": "^4.0.8"
  }
}

```

### server/index.ts

```typescript
import express, { type Request, Response, NextFunction } from "express";
import { registerRoutes } from "./routes";
import { setupVite, serveStatic, log } from "./vite";

const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

app.use((req, res, next) => {
  const start = Date.now();
  const path = req.path;
  let capturedJsonResponse: Record<string, any> | undefined = undefined;

  const originalResJson = res.json;
  res.json = function (bodyJson, ...args) {
    capturedJsonResponse = bodyJson;
    return originalResJson.apply(res, [bodyJson, ...args]);
  };

  res.on("finish", () => {
    const duration = Date.now() - start;
    if (path.startsWith("/api")) {
      let logLine = `${req.method} ${path} ${res.statusCode} in ${duration}ms`;
      if (capturedJsonResponse) {
        logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`;
      }

      if (logLine.length > 80) {
        logLine = logLine.slice(0, 79) + "…";
      }

      log(logLine);
    }
  });

  next();
});

(async () => {
  const server = await registerRoutes(app);

  app.use((err: any, _req: Request, res: Response, _next: NextFunction) => {
    const status = err.status || err.statusCode || 500;
    const message = err.message || "Internal Server Error";

    res.status(status).json({ message });
    throw err;
  });

  // importantly only setup vite in development and after
  // setting up all the other routes so the catch-all route
  // doesn't interfere with the other routes
  if (app.get("env") === "development") {
    await setupVite(app, server);
  } else {
    serveStatic(app);
  }

  // ALWAYS serve the app on port 5000
  // this serves both the API and the client.
  // It is the only port that is not firewalled.
  const port = 5000;
  server.listen({
    port,
    host: "0.0.0.0",
    reusePort: true,
  }, () => {
    log(`serving on port ${port}`);
  });
})();

```

### client/src/main.tsx

```typescript
import { createRoot } from "react-dom/client";
import App from "./App";
import "./index.css";

createRoot(document.getElementById("root")!).render(<App />);

```

### client/src/App.tsx

```typescript
import { Switch, Route } from "wouter";
import { queryClient } from "./lib/queryClient";
import { QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "@/components/ui/toaster";
import { TooltipProvider } from "@/components/ui/tooltip";
import Home from "@/pages/home";
import NotFound from "@/pages/not-found";

function Router() {
  return (
    <Switch>
      <Route path="/" component={Home} />
      <Route component={NotFound} />
    </Switch>
  );
}

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <TooltipProvider>
        <Toaster />
        <Router />
      </TooltipProvider>
    </QueryClientProvider>
  );
}

export default App;

```

### postcss.config.js

```javascript
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

```

### drizzle.config.ts

```typescript
import { defineConfig } from "drizzle-kit";

if (!process.env.DATABASE_URL) {
  throw new Error("DATABASE_URL, ensure the database is provisioned");
}

export default defineConfig({
  out: "./migrations",
  schema: "./shared/schema.ts",
  dialect: "postgresql",
  dbCredentials: {
    url: process.env.DATABASE_URL,
  },
});

```

### vite.config.ts

```typescript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
import runtimeErrorOverlay from "@replit/vite-plugin-runtime-error-modal";

export default defineConfig({
  plugins: [
    react(),
    runtimeErrorOverlay(),
    ...(process.env.NODE_ENV !== "production" &&
    process.env.REPL_ID !== undefined
      ? [
          await import("@replit/vite-plugin-cartographer").then((m) =>
            m.cartographer(),
          ),
        ]
      : []),
  ],
  resolve: {
    alias: {
      "@": path.resolve(import.meta.dirname, "client", "src"),
      "@shared": path.resolve(import.meta.dirname, "shared"),
      "@assets": path.resolve(import.meta.dirname, "attached_assets"),
    },
  },
  root: path.resolve(import.meta.dirname, "client"),
  build: {
    outDir: path.resolve(import.meta.dirname, "dist/public"),
    emptyOutDir: true,
  },
  server: {
    fs: {
      strict: true,
      deny: ["**/.*"],
    },
  },
});

```

### tailwind.config.ts

```typescript
import type { Config } from "tailwindcss";

export default {
  darkMode: ["class"],
  content: ["./client/index.html", "./client/src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {
      borderRadius: {
        lg: "var(--radius)",
        md: "calc(var(--radius) - 2px)",
        sm: "calc(var(--radius) - 4px)",
      },
      colors: {
        background: "var(--background)",
        foreground: "var(--foreground)",
        card: {
          DEFAULT: "var(--card)",
          foreground: "var(--card-foreground)",
        },
        popover: {
          DEFAULT: "var(--popover)",
          foreground: "var(--popover-foreground)",
        },
        primary: {
          DEFAULT: "var(--primary)",
          foreground: "var(--primary-foreground)",
        },
        secondary: {
          DEFAULT: "var(--secondary)",
          foreground: "var(--secondary-foreground)",
        },
        muted: {
          DEFAULT: "var(--muted)",
          foreground: "var(--muted-foreground)",
        },
        accent: {
          DEFAULT: "var(--accent)",
          foreground: "var(--accent-foreground)",
        },
        destructive: {
          DEFAULT: "var(--destructive)",
          foreground: "var(--destructive-foreground)",
        },
        border: "var(--border)",
        input: "var(--input)",
        ring: "var(--ring)",
        chart: {
          "1": "var(--chart-1)",
          "2": "var(--chart-2)",
          "3": "var(--chart-3)",
          "4": "var(--chart-4)",
          "5": "var(--chart-5)",
        },
        sidebar: {
          DEFAULT: "var(--sidebar-background)",
          foreground: "var(--sidebar-foreground)",
          primary: "var(--sidebar-primary)",
          "primary-foreground": "var(--sidebar-primary-foreground)",
          accent: "var(--sidebar-accent)",
          "accent-foreground": "var(--sidebar-accent-foreground)",
          border: "var(--sidebar-border)",
          ring: "var(--sidebar-ring)",
        },
      },
      keyframes: {
        "accordion-down": {
          from: {
            height: "0",
          },
          to: {
            height: "var(--radix-accordion-content-height)",
          },
        },
        "accordion-up": {
          from: {
            height: "var(--radix-accordion-content-height)",
          },
          to: {
            height: "0",
          },
        },
      },
      animation: {
        "accordion-down": "accordion-down 0.2s ease-out",
        "accordion-up": "accordion-up 0.2s ease-out",
      },
    },
  },
  plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
} satisfies Config;

```

### client/index.html

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
    <!-- This is a replit script which adds a banner on the top of the page when opened in development mode outside the replit environment -->
    <script type="text/javascript" src="https://replit.com/public/js/replit-dev-banner.js"></script>
  </body>
</html>
```

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