# Project export: Triply

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: A platform where friends save foodie videos. AI extracts restaurant info, plans the hangout, and splits the bill after—making food adventures effortless.
- Devpost: https://devpost.com/software/tripsplit-hudf59
- GitHub: https://github.com/jasonduongg/hackathong
- Demo: https://hackathon-seven-nu.vercel.app/
- Team: 3 GitHub contributor(s) — jasonduongg (57 commits), ZaafirH (11 commits), Audrey Zhang (7 commits)

## Devpost submission (written by the team)

### Inspiration

We’ve all been there—you’re scrolling through Instagram or TikTok and come across a video of a perfectly sizzling bowl of ramen or the crispiest chicken sandwich you’ve ever seen. You immediately send it to your group chat with, “WE NEED TO GO.” But then… nothing happens. Maybe it gets lost behind a flood of memes, or no one follows up, or the logistics of finding a time that works for everyone kills the vibe. Foodie inspiration is everywhere, but turning that excitement into actual plans is surprisingly hard. We built Triply to fix that. It’s your personal food adventure planner: automating everything from saving restaurant posts to booking the hangout and even splitting the bill afterward. What It Does Triply turns a social food recommendation into a real outing, effortlessly. Here’s how it works: Create a Party: A “party” is your friend group; think of it like your foodie crew or group chat. Drop a Post: Paste an Instagram post into the party. It could be a reel, a picture, or a carousel. AI Analysis: Our system uses AI to analyze the content (via screenshot) and extract: Restaurant name Location Food items Tags, vibes, and other context clues Restaurant name Location Food items Tags, vibes, and other context clues Scheduling the Hangout: Based on the restaurant's open hours and all party members’ availability, Triply suggests dates and times for the visit. Bill Management: After the hangout, users can upload a photo of the receipt. Our system parses it, identifies who ordered what, and intuitively splits the bill. No more awkward math or Venmo chasing! Triply isn’t just a restaurant recommender. It’s a full-circle tool to turn spontaneous foodie hype into actual memories. How We Built It Frontend: Built with Next.js, designed to be minimal and mobile-first for seamless use while texting or browsing social media. AI Backend: We use Claude to process screenshots of Instagram posts. It extracts structured data from unstructured visual media using carefully tuned prompts. We incorporate Google Maps APIs for resolving locations, hours, and directions. We use Claude to process screenshots of Instagram posts. It extracts structured data from unstructured visual media using carefully tuned prompts. We incorporate Google Maps APIs for resolving locations, hours, and directions. Bill Splitting: We use Claude to process screenshots, filter out irrelevant entries, and tag items for each person using menu recognition and named entity detection. We use Claude to process screenshots, filter out irrelevant entries, and tag items for each person using menu recognition and named entity detection. Data Handling: User data is stored on Firebase and used only to optimize planning and payment flows. User data is stored on Firebase and used only to optimize planning and payment flows. Challenges We Ran Into Our biggest challenge was post analysis. Instagram is notoriously bot-resistant, so we had to rely on screenshot inputs instead of scraping text or metadata. Screenshots are messy (there’s no standard format) but we engineered a system to consistently pull relevant info with high reliability. Other major challenges: Balancing Claude’s token limits, input fidelity, and number of images. Too little detail, and the model fails; too much, and it takes overly long to process. Building prompts that generalize across a variety of post types (reels, stories, photos, captions). Accurately identifying locations, even for lowkey or niche restaurants, and handling near-duplicate or misspelled names. Creating a user-friendly and reliable receipt splitting tool that works without requiring every user to itemize their own order. Accomplishments We’re Proud Of Our AI post analysis works surprisingly well. Even on visually chaotic posts or vague captions, it often extracts the correct restaurant and food items. We built a working prototype that turns a post into a scheduled plan end-to-end. We tackled an actual real-world pain point that we experience and made something we’d genuinely use. We created an intuitive UI that doesn’t require users to understand the tech under the hood. What We Learned Prompt engineering is no joke. It can be make or break for a technology like this. You can spend hours tuning just a few words to make a model behave better. Planning things with friends is a surprisingly deep problem space. There’s so much room for intelligent tooling. This was our first hackathon! Git was interesting at times. What’s Next for Triply We’re excited to expand Triply’s capabilities: Menu Matching: Automatically cross-reference food items with real restaurant menus for better recommendations and receipt tagging. Multi-platform Support: Bring in content from TikTok, Yelp, YouTube, and Google Reviews. Auto-invite & Calendar Integration: Instantly create calendar events with reservation links and reminders. Group Polls & Preferences: Let groups vote on where to go next, with intelligent ranking based on past hangouts. Food Journal Mode: Turn your visits into a shared food diary, with ratings, photos, and comments. Triply is just getting started. We want to make group food adventures effortless and way more fun! Slides Check out our demo slides here: Triply Demo Presentation

## README (from the GitHub repository)

This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
# hackathong

## Enhanced Video Analysis Features

### Improved Frame Extraction
- **1 frame per 2 seconds**: Videos are now analyzed more thoroughly with frame extraction every 2 seconds
- **Progress tracking**: Real-time progress bar shows frame extraction and analysis status
- **Better coverage**: Ensures comprehensive analysis of video content

### Smart Place Name Validation
- **Google Search Integration**: Validates place names by searching online to distinguish between:
  - Real business names (e.g., "Cali Spartan Restaurant")
  - Account names (e.g., "@calispartan")
  - Generic terms that aren't specific places
- **Context Analysis**: Uses location hints like "san jose", "bay area" to improve place identification
- **Confidence Scoring**: Each identified place gets a confidence score based on search results

### Enhanced Location Detection
- **Context Clues**: Extracts location hints from tags and captions
- **Multiple Validation**: Combines visual analysis with online search validation
- **Geocoding**: Provides real addresses and coordinates for validated places

### Setup Requirements
To enable Google search validation, add your SerpAPI key to your environment variables:
```bash
SERPAPI_KEY=your_api_key_here
```

Note: The system will work without the API key but place validation will be limited.


## Detected evidence (automated analysis)

Indexed codebase: 102 recognized source files, 1037 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- Firebase (technology) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (111 of 111)

```
.gitignore
.vscode/settings.json
docs/onboarding-system.md
env.example
eslint.config.mjs
examples/video-api-usage.md
next.config.ts
package.json
PAYPAL_SETUP.md
postcss.config.mjs
README.md
src/app/api/analyze-receipt/route.ts
src/app/api/delete-receipt/route.ts
src/app/api/events/route.ts
src/app/api/get-members/route.ts
src/app/api/get-times/route.ts
src/app/api/party-payment-requests/route.ts
src/app/api/party-receipts/route.ts
src/app/api/paypal/cancel/route.ts
src/app/api/paypal/create-order/route.ts
src/app/api/paypal/existing-requests/route.ts
src/app/api/paypal/return/route.ts
src/app/api/paypal/webhook/route.ts
src/app/api/process-video/route.ts
src/app/api/refresh-s3-url/route.ts
src/app/api/screenshot-instagram/route.ts
src/app/api/search-restaurant/route.ts
src/app/api/update-receipt-assignments/route.ts
src/app/api/update-receipt/route.ts
src/app/auth/page.tsx
src/app/font-test/page.tsx
src/app/globals.css
src/app/home/page.tsx
src/app/layout-simple.tsx
src/app/layout.tsx
src/app/minimal-test/page.tsx
src/app/page.tsx
src/app/plans/page.tsx
src/app/restaurant-analyzer/page.tsx
src/app/structured-video-test/page.tsx
src/app/test-css/page.tsx
src/components/auth/LogoutButton.tsx
src/components/auth/SignInForm.tsx
src/components/auth/SignUpForm.tsx
src/components/auth/UserProfile.tsx
src/components/Availability.tsx
src/components/BeforeFlowTab.tsx
src/components/ChainLocationFinder.tsx
src/components/ChooseRestaurant.tsx
src/components/CreatePartyForm.tsx
src/components/CreatePartyModal.tsx
src/components/EnhancedProgressBar.tsx
src/components/EventCard.tsx
src/components/EventModal.tsx
src/components/EventsList.tsx
src/components/ExamplesSection.tsx
src/components/Hero.tsx
src/components/HeroBackground.tsx
src/components/HowItWorks.tsx
src/components/ItemAssignmentFlow.tsx
src/components/Navbar.tsx
src/components/NewHero.tsx
src/components/onboarding/OnboardingForm.tsx
src/components/PartyCard.tsx
src/components/PartyList.tsx
src/components/PartyPaymentRequests.tsx
src/components/PartyReceipts.tsx
src/components/PartyReceiptUpload.tsx
src/components/PartyView.tsx
src/components/paypal/PayPalPaymentModal.tsx
src/components/ReceiptCard.tsx
src/components/ReceiptDetailsModal.tsx
src/components/ReceiptImageModal.tsx
src/components/ReceiptUpload.tsx
src/components/SingleParty.tsx
src/components/theme-provider.tsx
src/components/ui/animated-group.tsx
src/components/ui/button.tsx
src/components/ui/card.tsx
src/components/ui/dark-gradient-pricing.tsx
src/components/ui/demo.tsx
src/components/ui/footer.tsx
src/components/ui/hero-section-1.tsx
src/components/ui/testimonials-columns-1.tsx
src/components/ui/testimonials.tsx
src/components/VideoUpload.tsx
src/contexts/AuthContext.tsx
src/contexts/PartyContext.tsx
src/contexts/UploadContext.tsx
src/contexts/VideoAnalysisContext.tsx
src/lib/auth-server.ts
src/lib/auth.ts
src/lib/events.ts
src/lib/firebase.ts
src/lib/llm-services.ts
src/lib/location-services.ts
src/lib/parties.ts
src/lib/paypal.ts
src/lib/prompts.ts
src/lib/s3.ts
src/lib/users.ts
src/lib/utils.ts
src/types/party.ts
src/types/paypal.ts
src/types/receipt.ts
tailwind.config.js
temp_page.tsx
tsconfig.json
UPDATE_INSTRUCTIONS.md
update-code.ps1
update.bat
```

### Dependencies

- package.json: @anthropic-ai/sdk@^0.54.0, @aws-sdk/client-s3@^3.832.0, @aws-sdk/s3-request-presigner@^3.832.0, @eslint/eslintrc@^3, @heroicons/react@^2.2.0, @radix-ui/react-slot@^1.2.3, @react-three/drei@^10.1.2, @react-three/fiber@^9.1.2, @tailwindcss/forms@^0.5.10, @types/node@^20, @types/react@^19, @types/react-dom@^19, autoprefixer@^10.4.17, axios@^1.10.0, class-variance-authority@^0.7.1, clsx@^2.1.1, eslint@^9, eslint-config-next@15.3.3, firebase@^11.9.1, firebase-admin@^13.4.0, framer-motion@^12.18.1, lucide-react@^0.513.0, maath@^0.10.8, motion@^12.18.1, next@15.3.3, next-themes@^0.4.6, postcss@^8.4.31, process@^0.11.10, puppeteer@^24.10.2, puppeteer-core@^24.10.2, react@^19.0.0, react-dom@^19.0.0, sharp@^0.34.2, tailwind-merge@^3.3.0, tailwindcss@^3.4.17, tailwindcss-animate@^1.0.7, three@^0.177.0, typescript@^5

### Recent commits (newest first)

- final production
- adadsA
- invite move
- invite
- test
- Merge pull request #7 from jasonduongg/alyssa
- fix hour slot display
- update calendar for no overlapping scheduling, unschedule button
- Merge pull request #6 from jasonduongg/alyssa
- Merge branch 'main' into alyssa
- trying to solve production uploiad
- Merge branch 'main' of https://github.com/jasonduongg/hackathong into alyssa
- fix video analysis chain location, video analysis works for ig reels
- prod
- search data
- auth
- landing
- navbar logo
- website event modal move
- time fix

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

### UPDATE_INSTRUCTIONS.md

```markdown
��C r e a t i n g   u p d a t e   i n s t r u c t i o n s . . .  
 
```

### PAYPAL_SETUP.md

```markdown
# PayPal Integration Setup

This guide will help you set up PayPal integration to enable charging your friends for items in your receipt splitting app.

## Prerequisites

1. A PayPal Developer Account
2. A Next.js application with Firebase authentication
3. Environment variables configured

## Step 1: PayPal Developer Setup

1. Go to [PayPal Developer Portal](https://developer.paypal.com/)
2. Sign in with your PayPal account or create a new one
3. Navigate to "Apps & Credentials"
4. Create a new app:
   - Click "Create App"
   - Choose "Business" app type
   - Give it a name (e.g., "Receipt Splitter")
   - Select "PayPal Checkout" as the integration type

5. Configure your app settings:
   - **App Name**: Your app name
   - **Sandbox/Live**: Start with Sandbox for testing
   - **Webhook URL**: `https://yourdomain.com/api/paypal/webhook` (for production)
   - **Return URL**: `https://yourdomain.com/api/paypal/return`
   - **Cancel URL**: `https://yourdomain.com/api/paypal/cancel`

6. Get your credentials:
   - Copy the **Client ID** and **Secret** from your app
   - Note: Use Sandbox credentials for testing, Live for production

## Step 2: Environment Variables

Add the following environment variables to your `.env.local` file:

```bash
# PayPal Configuration
PAYPAL_CLIENT_ID=your_paypal_client_id
PAYPAL_CLIENT_SECRET=your_paypal_client_secret
PAYPAL_MODE=sandbox
PAYPAL_WEBHOOK_ID=your_webhook_id

# Base URL for your app
NEXT_PUBLIC_BASE_URL=http://localhost:3000
```

For production, change `PAYPAL_MODE` to `live` and update `NEXT_PUBLIC_BASE_URL`.

## Step 3: Firestore Security Rules

Update your Firestore security rules to allow PayPal payment data:

```javascript
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Allow users to read and write their own profile
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
    
    // Allow authenticated users to read all user profiles
    match /users/{userId} {
      allow read: if request.auth != null;
    }
    
    // Allow users to manage their own PayPal payments
    match /paypal_payments/{paymentId} {
      allow read, write: if request.auth != null && 
        (resource.data.requester_id == request.auth.uid || 
         resource.data.target_user_id == request.auth.uid);
    }
  }
}
```

## Step 4: Webhook Setup (Production)

For production, you need to set up PayPal webhooks:

1. In your PayPal Developer Dashboard, go to "Webhooks"
2. Click "Add Webhook"
3. Set the webhook URL to: `https://yourdomain.com/api/paypal/webhook`
4. Select the following events:
   - `PAYMENT.CAPTURE.COMPLETED`
   - `PAYMENT.CAPTURE.DENIED`
   - `PAYMENT.CAPTURE.REFUNDED`
5. Copy the Webhook ID and add it to your environment variables

## Step 5: Testing the Integration

1. Start your development server:
   ```bash
   npm run dev
   ```

2. Navigate to your app and upload a receipt

3. Assign items to people

4. Cl
[truncated — 4403 more characters]
```

### package.json

```
{
  "name": "portfolio_new",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.54.0",
    "@aws-sdk/client-s3": "^3.832.0",
    "@aws-sdk/s3-request-presigner": "^3.832.0",
    "@heroicons/react": "^2.2.0",
    "@radix-ui/react-slot": "^1.2.3",
    "@react-three/drei": "^10.1.2",
    "@react-three/fiber": "^9.1.2",
    "axios": "^1.10.0",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "firebase": "^11.9.1",
    "firebase-admin": "^13.4.0",
    "framer-motion": "^12.18.1",
    "lucide-react": "^0.513.0",
    "maath": "^0.10.8",
    "motion": "^12.18.1",
    "next": "15.3.3",
    "next-themes": "^0.4.6",
    "process": "^0.11.10",
    "puppeteer": "^24.10.2",
    "puppeteer-core": "^24.10.2",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "sharp": "^0.34.2",
    "tailwind-merge": "^3.3.0",
    "tailwindcss-animate": "^1.0.7",
    "three": "^0.177.0"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@tailwindcss/forms": "^0.5.10",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "autoprefixer": "^10.4.17",
    "eslint": "^9",
    "eslint-config-next": "15.3.3",
    "postcss": "^8.4.31",
    "tailwindcss": "^3.4.17",
    "typescript": "^5"
  }
}
```

### src/app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "@/components/theme-provider";
import { AuthProvider } from "@/contexts/AuthContext";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

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

export const metadata: Metadata = {
  title: "Party App",
  description: "A party management app with restaurant finder",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
        suppressHydrationWarning
      >
        <ThemeProvider
          attribute="class"
          defaultTheme="light"
          enableSystem={false}
          disableTransitionOnChange
          storageKey="party-app-theme"
        >
          <AuthProvider>
            {children}
          </AuthProvider>
        </ThemeProvider>
      </body>
    </html>
  );
}

```

### src/app/page.tsx

```typescript
'use client';

import React, { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '@/contexts/AuthContext';
import NewHero from '@/components/NewHero';
import HowItWorks from '@/components/HowItWorks';
import ExamplesSection from '@/components/ExamplesSection';
import Testimonials from '@/components/ui/testimonials';
import Footer from '@/components/ui/footer';

export default function Home() {
  const { user, loading } = useAuth();
  const router = useRouter();

  // Redirect to home if user is already logged in
  useEffect(() => {
    if (!loading && user) {
      router.push('/home');
    }
  }, [user, loading, router]);

  if (loading) {
    return (
      <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-soft-orange-50 via-soft-orange-25 to-soft-orange-100">
        <div className="text-lg text-gray-700">Loading...</div>
      </div>
    );
  }

  // If user is logged in, show loading while redirecting
  if (user) {
    return (
      <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-soft-orange-50 via-soft-orange-25 to-soft-orange-100">
        <div className="text-lg text-gray-700">Redirecting to home...</div>
      </div>
    );
  }

  return (
    <div className="bg-gradient-to-br from-soft-orange-50 via-soft-orange-25 to-soft-orange-100">
      {/* Hero Section */}
      <section id="hero" className="relative min-h-screen scroll-mt-16">
        <NewHero />
      </section>

      {/* How It Works Section */}
      <section id="how-it-works" className="px-4 md:px-8 lg:px-16 py-20 scroll-mt-16 bg-soft-orange-25/50 backdrop-blur-sm">
        <HowItWorks />
      </section>

      {/* Examples Section */}
      <section id="examples" className="px-4 md:px-8 lg:px-16 py-20 scroll-mt-16">
        <ExamplesSection />
      </section>

      {/* Testimonials Section */}
      <section id="testimonials" className="px-4 md:px-8 lg:px-16 py-20 scroll-mt-16 bg-gradient-to-r from-soft-orange-50/50 to-soft-orange-100/50">
        <Testimonials />
      </section>

      {/* Footer Section */}
      <section id="footer" className="px-4 md:px-8 lg:px-16 scroll-mt-16 bg-soft-orange-25/70 backdrop-blur-sm">
        <Footer />
      </section>
    </div>
  );
}

```

### src/app/test-css/page.tsx

```typescript
export default function TestCSS() {
  return (
    <div className="min-h-screen bg-blue-500 flex items-center justify-center">
      <div className="bg-white p-8 rounded-lg shadow-lg">
        <h1 className="text-3xl font-bold text-gray-900 mb-4">
          CSS Test Page
        </h1>
        <p className="text-gray-600 mb-4">
          If you can see this styled properly, Tailwind CSS is working!
        </p>
        <div className="space-y-2">
          <div className="bg-red-100 text-red-800 p-2 rounded">Red background</div>
          <div className="bg-green-100 text-green-800 p-2 rounded">Green background</div>
          <div className="bg-blue-100 text-blue-800 p-2 rounded">Blue background</div>
        </div>
        <button className="mt-4 bg-indigo-600 text-white px-4 py-2 rounded hover:bg-indigo-700">
          Test Button
        </button>
      </div>
    </div>
  );
} 
```

### src/app/minimal-test/page.tsx

```typescript
export default function MinimalTest() {
  return (
    <html>
      <head>
        <title>Minimal CSS Test</title>
      </head>
      <body>
        <div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
          <h1 style={{ color: 'red', fontSize: '24px' }}>Inline Styles Test</h1>
          <p>This uses inline styles and should always work.</p>
          
          <h2 className="text-blue-600 text-2xl font-bold mt-4">Tailwind Test</h2>
          <p className="text-gray-700 bg-yellow-100 p-4 rounded">This uses Tailwind classes.</p>
          
          <div className="mt-4">
            <button className="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">
              Tailwind Button
            </button>
            <button style={{ backgroundColor: 'purple', color: 'white', padding: '8px 16px', marginLeft: '10px', border: 'none', borderRadius: '4px' }}>
              Inline Button
            </button>
          </div>
        </div>
      </body>
    </html>
  );
} 
```

### src/app/font-test/page.tsx

```typescript
"use client";
import { useState } from "react";

// Explicit class usage to ensure Tailwind includes these in the build
const explicitClasses = "text-massive text-massive-sm text-massive-md text-header text-header-sm text-header-md text-subheader text-subheader-sm text-subheader-md text-normal text-tiny";

const fontSizes = [
    { label: "Massive", className: "text-massive" },
    { label: "Massive SM", className: "text-massive-sm" },
    { label: "Massive MD", className: "text-massive-md" },
    { label: "Header", className: "text-header" },
    { label: "Header SM", className: "text-header-sm" },
    { label: "Header MD", className: "text-header-md" },
    { label: "Subheader", className: "text-subheader" },
    { label: "Subheader SM", className: "text-subheader-sm" },
    { label: "Subheader MD", className: "text-subheader-md" },
    { label: "Normal", className: "text-normal" },
    { label: "Tiny", className: "text-tiny" },
];

export default function FontTestPage() {
    const [bold, setBold] = useState(false);
    const [italic, setItalic] = useState(false);

    return (
        <div className="container mx-auto py-8">
            {/* Hidden div to ensure Tailwind includes all classes */}
            <div className={explicitClasses} style={{ display: 'none' }}></div>

            <h1 className="text-header mb-6 font-sans">Font Test Page</h1>
            <div className="mb-4 flex gap-4">
                <label className="flex items-center gap-1">
                    <input type="checkbox" checked={bold} onChange={() => setBold((b) => !b)} /> Bold
                </label>
                <label className="flex items-center gap-1">
                    <input type="checkbox" checked={italic} onChange={() => setItalic((i) => !i)} /> Italic
                </label>
            </div>
            <div className="flex flex-col gap-6 mt-8">
                {fontSizes.map((fs) => (
                    <div key={fs.className} className="flex flex-col gap-1">
                        <span className="text-sm text-muted-foreground">{fs.label} ({fs.className})</span>
                        <p
                            className={`font-sans ${fs.className} ${bold ? "font-bold" : "font-normal"} ${italic ? "italic" : "not-italic"}`}
                        >
                            The quick brown fox jumps over the lazy dog.
                        </p>
                    </div>
                ))}
            </div>
        </div>
    );
} 
```

### src/app/home/page.tsx

```typescript
'use client';

import React, { useState } from 'react';
import { useAuth } from '@/contexts/AuthContext';
import { UserProfile } from '@/components/auth/UserProfile';
import { OnboardingForm } from '@/components/onboarding/OnboardingForm';
import { Navbar } from '@/components/Navbar';
import { PartyView } from '@/components/PartyView';

import { VideoUpload } from '@/components/VideoUpload';

export default function HomePage() {
    const { user, userProfile, loading } = useAuth();
    const [showOnboarding, setShowOnboarding] = useState(false);
    const [showProfile, setShowProfile] = useState(false);
    const [showVideoUpload, setShowVideoUpload] = useState(false);

    if (loading) {
        return (
            <div className="min-h-screen flex items-center justify-center">
                <div className="text-lg">Loading...</div>
            </div>
        );
    }

    if (!user) {
        return (
            <div className="min-h-screen flex items-center justify-center">
                <div className="text-center">
                    <h1 className="text-2xl font-bold text-gray-900 mb-4">Access Denied</h1>
                    <p className="text-gray-600 mb-4">You need to be logged in to access this page.</p>
                    <a
                        href="/auth"
                        className="inline-block bg-indigo-600 text-white px-6 py-2 rounded-md hover:bg-indigo-700"
                    >
                        Go to Login
                    </a>
                </div>
            </div>
        );
    }

    // Check if user needs to complete onboarding
    if (userProfile && !userProfile.hasCompletedOnboarding && !showOnboarding) {
        return (
            <div className="min-h-screen bg-gray-50 flex items-center justify-center p-4 pt-8">
                <Navbar
                    onProfileClick={() => setShowProfile(true)}
                    onHomeClick={() => {
                        setShowProfile(false);
                    }}
                />
                <div className="max-w-md w-full bg-white rounded-xl shadow-lg p-8 text-center">
                    <div className="mb-6">
                        <div className="w-16 h-16 bg-indigo-100 rounded-full flex items-center justify-center mx-auto mb-4">
                            <svg className="w-8 h-8 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
                            </svg>
                        </div>
                        <h2 className="text-2xl font-bold text-gray-900 mb-2">Welcome!</h2>
                        <p className="text-gray-600">
                            Let's get to know you better to personalize your experience.
                        </p>
                    </div>
                    <button
                        onClick={() => setShowOnboarding(true)}
                        className="w-full px-6 py-3 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors"
                    >
                        Start Onboarding
                    </button>
                </div>
            </div>
        );
    }

    // Show onboarding form
    if (showOnboarding) {
        return (
            <div className="min-h-screen bg-gray-50 pt-20">
                <Navbar
                    onProfileClick={() => setShowProfile(true)}
                    onHomeClick={() => {
                        setShowProfile(false);
                    }}
                />
                <OnboardingForm
                    onComplete={() => {
                        setShowOnboarding(false);
                    }}
                />
            </div>
        );
    }

    // Show profile if requested
    if (showProfile) {
        return (
            <div className="min-h-screen bg-gray-50 pt-20">
                <Navbar
                    onProfileClick={() => setShowProfile(false)}
                    onHomeClick={() => {
                        setShowProfile(false);
                    }}
                />
                <main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
                    <div className="px-4 py-6 sm:px-0">
                        <UserProfile
                            onRestartOnboarding={() => {
                                setShowOnboarding(true);
                            }}
                        />
                    </div>
                </main>
            </div>
        );
    }

    // Show parties as the default home page
    return (
        <div className="min-h-screen bg-gray-50 pt-20">
            <Navbar
                onProfileClick={() => setShowProfile(true)}
                onHomeClick={() => {
                    setShowProfile(false);
                }}
            />
            <main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
                <div className="px-4 py-6 sm:px-0">
                    <PartyView />
                </div>
            </main>

            {/* Video Upload Modal */}
            {showVideoUpload && (
                <VideoUpload onClose={() => setShowVideoUpload(false)} />
            )}
        </div>
    );
} 
```

### src/app/plans/page.tsx

```typescript
'use client';

import { PricingCard } from "@/components/ui/dark-gradient-pricing"
import { useRouter } from 'next/navigation';
import { ArrowLeft } from 'lucide-react';

export default function PlansPage() {
    const router = useRouter();

    const handleGetStarted = () => {
        router.push('/auth?tab=signup');
    };

    const handleContactUs = () => {
        // You can implement contact form or email link here
        window.location.href = 'mailto:contact@dillydally.com';
    };

    const handleBack = () => {
        router.push('/');
    };

    return (
        <section className="relative overflow-hidden bg-background text-foreground min-h-screen">
            <div className="relative z-10 mx-auto max-w-5xl px-4 py-20 md:px-8">
                {/* Back button */}
                <button
                    onClick={handleBack}
                    className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors mb-8"
                >
                    <ArrowLeft className="w-4 h-4" />
                    Back to Home
                </button>

                <div className="mb-12 space-y-3">
                    <h2 className="text-center text-3xl font-semibold leading-tight sm:text-4xl sm:leading-tight md:text-5xl md:leading-tight">
                        Choose Your Plan
                    </h2>
                    <p className="text-center text-base text-muted-foreground md:text-lg">
                        Start splitting expenses with friends for free, upgrade when you need advanced features.
                    </p>
                </div>
                <div className="grid grid-cols-1 gap-6 md:grid-cols-3">
                    <PricingCard
                        tier="Free"
                        price="$0/mo"
                        bestFor="Perfect for small groups"
                        CTA="Get started free"
                        benefits={[
                            { text: "Up to 5 people per group", checked: true },
                            { text: "Basic receipt scanning", checked: true },
                            { text: "Manual expense splitting", checked: true },
                            { text: "Email support", checked: true },
                            { text: "7-day receipt history", checked: false },
                            { text: "Advanced analytics", checked: false },
                            { text: "Priority support", checked: false },
                            { text: "Custom categories", checked: false },
                        ]}
                    />
                    <PricingCard
                        tier="Pro"
                        price="$9.99/mo"
                        bestFor="Best for active groups"
                        CTA="Start free trial"
                        benefits={[
                            { text: "Up to 20 people per group", checked: true },
                            { text: "Advanced receipt scanning", checked: true },
                            { text: "Automatic expense splitting", checked: true },
                            { text: "Email support", checked: true },
                            { text: "30-day receipt history", checked: true },
                            { text: "Advanced analytics", checked: true },
                            { text: "Priority support", checked: true },
                            { text: "Custom categories", checked: true },
                        ]}
                    />
                    <PricingCard
                        tier="Enterprise"
                        price="Contact us"
                        bestFor="Large organizations"
                        CTA="Contact us"
                        benefits={[
                            { text: "Unlimited group members", checked: true },
                            { text: "AI-powered receipt scanning", checked: true },
                            { text: "Smart expense splitting", checked: true },
                            { text: "Dedicated support", checked: true },
                            { text: "Unlimited receipt history", checked: true },
                            { text: "Advanced analytics & reports", checked: true },
                            { text: "Priority support", checked: true },
                            { text: "Custom integrations", checked: true },
                        ]}
                    />
                </div>

                {/* Additional features section */}
                <div className="mt-16 text-center">
                    <h3 className="text-2xl font-semibold mb-6">All plans include:</h3>
                    <div className="grid grid-cols-1 md:grid-cols-3 gap-6 text-sm">
                        <div className="p-4 rounded-lg bg-gray-50 dark:bg-gray-800">
                            <h4 className="font-semibold mb-2">Smart Receipt Detection</h4>
                            <p className="text-gray-600 dark:text-gray-400">Automatically detect and extract receipt information from photos and videos</p>
                        </div>
                        <div className="p-4 rounded-lg bg-gray-50 dark:bg-gray-800">
                            <h4 className="font-semibold mb-2">Secure & Private</h4>
                            <p className="text-gray-600 dark:text-gray-400">Your data is encrypted and never shared with third parties</p>
                        </div>
                        <div className="p-4 rounded-lg bg-gray-50 dark:bg-gray-800">
                            <h4 className="font-semibold mb-2">Cross-Platform</h4>
                            <p className="text-gray-600 dark:text-gray-400">Works seamlessly across web, iOS, and Android devices</p>
                        </div>
                    </div>
                </div>
            </div>
        </section>
    )
} 
```

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