# Project export: MailMorph

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: Cal Hacks 11.0
- Tagline: Transforms how emails are organized and handled.
- Devpost: https://devpost.com/software/mailmorph
- GitHub: https://github.com/code4hari/Email
- Team: 1 GitHub contributor(s) — code4hari (19 commits)

## Devpost submission (written by the team)

### Inspiration

Two days ago, as we were standing in the long line to check-in to Calhacks, we were competing to see who had the most emails in their inbox. Surprisingly enough someone had more than 150k unread emails! As fate would have it when it was our turn to check-in we were franticly searching through our inboxes for our tickets. That's when we realized there are so many ways e-mail can be improved on to make life easier, one major one being organization of emails. But, no one in their right mind is going to manually categorize and organize more than 150k e-mails. That's where MailMorph comes in!

### What it does

MailMorph is a smart e-mail tool which employs the use of AI and AI-Agents to make life with e-mail easier in five main areas. First, MailMorph can automatically create user specified categories and organize the e-mail in their inbox into the categories. Second, MailMorph can automatically identify mails sitting in the sent folder of you inbox and recommend you to follow up with the other person, and automatically create a draft email for you. Third, MailMorph can automatically identify mails coming into your inbox with important events or dates and automatically add it to your calendar. Fourth, MailMorph can create a daily digest of your emails of that day and summarize it all for you, as well as highlighting the most important details. Last but not least, MailMorph can identify emails with pdf files which need to be filled, and auto fill them for you if there is sufficient information in the email itself.

### How we built it

For MailMorph, we combined the capabilities of Gemini AI and AutoGen AI Agents to build a powerful email and PDF automation solution. To integrate with Gmail and Google Calendar, we secured access to their APIs, set up user authentication, and configured credentials to extract the necessary data. Depending on the use case, we retrieved key information: for categorizing emails, we identified relevant details; for adding events to calendars, we extracted dates and times; and for summaries, we collected the full content. We ensured PII redaction before sending any data to the LLMs and used prompt engineering with Gemini AI to generate accurate outputs. On the backend, we structured individual APIs to handle different functionalities, allowing the frontend—built with React, Node.js, and Swift—to communicate seamlessly with the Python backend through API calls. For PDF automation, we leveraged AutoGen AI to process both fillable and scanned PDFs. With fillable PDFs, the agent identified fields and matched them with data from a JSON dataset before generating code to fill the PDF. For scanned PDFs, we incorporated OCR to detect fields and injected fillable inputs at the appropriate locations, following the same logic as the fillable PDFs. This architecture enabled us to efficiently manage emails, automate PDFs, and integrate everything into a smooth, user-friendly interface.

### Challenges we ran into

One of the biggest challenges was setting up access to the Gmail API and Google Calendar API, as it involved navigating OAuth authentication and configuring user credentials securely. Another hurdle was ensuring PII redaction before sending any sensitive information to the LLMs, which required careful preprocessing to avoid leaking personal data. Implementing OCR for scanned PDFs was also complex, especially when accurately detecting fields and injecting fillable elements into the correct locations. Additionally, integrating multiple technologies—React, Node.js, Swift, and Python—into a seamless frontend-backend system took considerable effort to ensure smooth communication between all components.

### Accomplishments we're proud of

We are proud of the fact that we got most of our features to perform in less than one second. This is huge for users with very large inboxes since they won't have to wait copious amounts of time. Also, it was our first time integrating flask API endpoints with node.js front end so figuring out how the endpoints worked and integrating everything and seeing it all come together was definitely something we were proud about,

### What we learned

Throughout the development of MailMorph, we learned the importance of efficient API integration and data security when working with sensitive user information. We also gained hands-on experience in prompt engineering for LLMs to optimize outputs based on different tasks. Another key takeaway was learning how to leverage AI agents to automate complex tasks like processing PDFs and identifying contextual information in emails. Furthermore, working with multiple programming languages and frameworks helped us improve our collaboration and technical integration skills.

### What's next

For now MailMorph is limit to Gmail, so in the future we look forward to implementing in outlook, yahoo, and other email services. Also, we look forward to adding more functionalities such as auto flight check-in, better spam detection, voice activated email, etc. Also, we look forward to implementing a mobile application to make an even more convenient way to organize e-mail.

## README (from the GitHub repository)

# Email

## Detected evidence (automated analysis)

Indexed codebase: 4 recognized source files, 37 KB.
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- CSS (language) — claimed on Devpost, not found in the code
- Flask (technology) — claimed on Devpost, not found in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code
- HTML (language) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- React (technology) — claimed on Devpost, not found in the code
- Tailwind CSS (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (6 of 6)

```
.gitignore
app.py
LandingPage.js
LoginPage.js
README.md
requirements.txt
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- Add files via upload
- Add files via upload
- Add files via upload
- Add files via upload
- Delete token.pickle
- Delete sample_email.json
- Delete linear_updates.py
- Delete email_summary.py
- Delete email_response.py
- Delete email_catagory.py
- Delete api_sum.py
- Delete api_res.py
- Delete api_liup.py
- Delete api_cat.py
- Update api_sum.py
- Update api_res.py
- Update api_liup.py
- Update api_cat.py
- Add files via upload
- Initial commit

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

### requirements.txt

```
��a n n o t a t e d - t y p e s = = 0 . 7 . 0  
 b l i n k e r = = 1 . 8 . 2  
 b l i s = = 1 . 0 . 1  
 c a c h e t o o l s = = 5 . 5 . 0  
 c a t a l o g u e = = 2 . 0 . 1 0  
 c e r t i f i = = 2 0 2 4 . 8 . 3 0  
 c h a r d e t = = 5 . 2 . 0  
 c h a r s e t - n o r m a l i z e r = = 3 . 4 . 0  
 c l i c k = = 8 . 1 . 7  
 c l o u d p a t h l i b = = 0 . 2 0 . 0  
 c o l o r a m a = = 0 . 4 . 6  
 c o n f e c t i o n = = 0 . 1 . 5  
 c y m e m = = 2 . 0 . 8  
 d a t e p a r s e r = = 1 . 2 . 0  
 e n _ c o r e _ w e b _ s m   @   h t t p s : / / g i t h u b . c o m / e x p l o s i o n / s p a c y - m o d e l s / r e l e a s e s / d o w n l o a d / e n _ c o r e _ w e b _ s m - 3 . 8 . 0 / e n _ c o r e _ w e b _ s m - 3 . 8 . 0 - p y 3 - n o n e - a n y . w h l # s h a 2 5 6 = 1 9 3 2 4 2 9 d b 7 2 7 d 4 b f f 3 d e e d 6 b 3 4 c f c 0 5 d f 1 7 7 9 4 f 4 a 5 2 e e b 2 6 c f 8 9 2 8 f 7 c 1 a 0 f b 8 5  
 F a k e r = = 3 0 . 6 . 0  
 F I T X = = 1 . 0 . 1  
 F l a s k = = 3 . 0 . 3  
 F l a s k - C o r s = = 5 . 0 . 0  
 g o o g l e - a i - g e n e r a t i v e l a n g u a g e = = 0 . 6 . 1 0  
 g o o g l e - a p i - c o r e = = 2 . 2 1 . 0  
 g o o g l e - a p i - p y t h o n - c l i e n t = = 2 . 1 4 9 . 0  
 g o o g l e - a u t h = = 2 . 3 5 . 0  
 g o o g l e - a u t h - h t t p l i b 2 = = 0 . 2 . 0  
 g o o g l e - a u t h - o a u t h l i b = = 1 . 2 . 1  
 g o o g l e - g e n e r a t i v e a i = = 0 . 8 . 3  
 g o o g l e a p i s - c o m m o n - p r o t o s = = 1 . 6 5 . 0  
 g r e e n l e t = = 3 . 0 . 3  
 g r p c i o = = 1 . 6 7 . 0  
 g r p c i o - s t a t u s = = 1 . 6 7 . 0  
 h t t p l i b 2 = = 0 . 2 2 . 0  
 i d n a = = 3 . 1 0  
 i t s d a n g e r o u s = = 2 . 2 . 0  
 J i n j a 2 = = 3 . 1 . 4  
 j o b l i b = = 1 . 4 . 2  
 l a n g c o d e s = = 3 . 4 . 1  
 l a n g u a g e _ d a t a = = 1 . 2 . 0  
 m a r i s a - t r i e = = 1 . 2 . 1  
 m a r k d o w n - i t - p y = = 3 . 0 . 0  
 M a r k u p S a f e = = 3 . 0 . 2  
 m d u r l = = 0 . 1 . 2  
 m u r m u r h a s h = = 1 . 0 . 1 0  
 n l t k = = 3 . 9 . 1  
 n u m p y = = 2 . 0 . 2  
 o a u t h l i b = = 3 . 2 . 2  
 p a c k a g i n g = = 2 4 . 1  
 p d f 2 i m a g e = = 1 . 1 7 . 0  
 p d f r w = = 0 . 4  
 p h o n e n u m b e r s = = 8 . 1 3 . 4 7  
 p i l l o w = = 1 1 . 0 . 0  
 p l a y w r i g h t = = 1 . 4 7 . 0  
 p r e s h e d = = 3 . 0 . 9  
 p r o t o - p l u s = = 1 . 2 4 . 0  
 p r o t o b u f = = 5 . 2 8 . 2  
 p y a s n 1 = = 0 . 6 . 1  
 p y a s n 1 _ m o d u l e s = = 0 . 4 . 1  
 p y d a n t i c = = 2 . 9 . 2  
 p y d a n t i c _ c o r e = = 2 . 2 3 . 4  
 p y e e = = 1 2 . 0 . 0  
 P y g m e n t s = = 2 . 1 8 . 0  
 p y p a r s i n g = = 3 . 2 . 0  
 P y P D F 2 = = 3 . 0 . 1  
 p y t h o n - d a t e u t i l = = 2 . 9 . 0 . p o s t 0  
 p y t h o n - d o t e n v = = 1 . 0 . 1  
 p y t h o n - s t d n u m = = 1 . 2 0  
 p y t z = = 2 0 2 4 . 2  
 r e g e x = = 2 0 2 4 . 9 . 1 1  
 r e p o r t l a b = = 4 . 2 . 5  
 r e q u e s t s = = 2 . 3 2 . 3  
 r e q u e s t s - o a u t h l i b = = 2 . 0 . 0  
 r i c h = = 1 3 . 9 . 2  
 r s a = = 4 . 9  
 s c i k i t - l e a r n = = 1 . 5 . 2  
 s c i p y = = 1 . 1 4 . 1  
 s c r u b a d u b = = 2 . 0 . 1  
 s e t u p t o o l s = = 7 5 . 2 . 0  
 s h e l l i n g h a m = = 1 . 5 . 4  
 s i x = = 1 . 1 6 . 0  
 s m a r t - o p e n = = 7 . 0 . 5  
 s p a c y - l e g a c y = = 3 . 0 . 1 2  
 s p a c y - l o g g e r s = = 1 . 0 . 5  
 s r s l y = = 2 . 4 . 8  
 t e x t b l o b = = 0 . 1 5 . 3  
 t h i n c = = 8 . 3 . 2  
 t h r e a d p o o l c t l = = 3 . 5 . 0  
 t q d m = = 4 . 6 6 . 5  
 t y p e r = = 0 . 1 2 . 5  
 t y p i n g _ e x t e n s i o n s = = 4 . 1 2 . 2  
 t z d a t a = = 2 0 2 4 . 2  
 t z l o c a l = = 5 . 2  
 u r i t e m p l a t e = = 4 . 1 . 1  
 u r l l i b 3 = = 2 . 2 . 3  
 w a s a b i = = 1 . 1 . 3  
 w e a s e l = = 0 . 4 . 1  
 W e r k z e u g = = 3 . 0 . 4  
 w r a p t = = 1 . 1 6 . 0  
 
```

### app.py

```python
import os
import pickle
import time
from flask import Flask, jsonify
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
import scrubadub
from dotenv import load_dotenv
from datetime import datetime
import google.generativeai as genai

load_dotenv()

app = Flask(__name__)

# Google API scope
SCOPES = ["https://www.googleapis.com/auth/gmail.modify"]

def authenticate_gmail():
    creds = None
    if os.path.exists("token.pickle"):
        with open("token.pickle", "rb") as token:
            creds = pickle.load(token)

    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                "credentials.json", SCOPES
            )
            creds = flow.run_local_server(port=0)

        with open("token.pickle", "wb") as token:
            pickle.dump(creds, token)

    return build("gmail", "v1", credentials=creds)

def get_todays_emails(service):
    today = datetime.now().strftime('%Y/%m/%d')
    query = f'after:{today}'

    results = service.users().messages().list(userId='me', q=query).execute()
    messages = results.get('messages', [])
    return messages

import base64

def fetch_email_content(service, email_id):
    email = service.users().messages().get(userId='me', id=email_id, format='full').execute()
    payload = email.get('payload', {})
    headers = payload.get('headers', [])

    # Fetch subject
    subject = next((header['value'] for header in headers if header['name'] == 'Subject'), '')

    # Extract the body of the email (checking for multipart)
    body = get_email_body(payload)

    return {'subject': subject, 'body': body, 'id': email_id}

def get_email_body(payload):
    """Helper function to retrieve the email body content, handling multipart emails."""
    if 'parts' in payload:
        parts = payload['parts']
        for part in parts:
            if part['mimeType'] == 'text/plain':
                return base64.urlsafe_b64decode(part['body']['data']).decode('utf-8')
    else:
        # Single-part email body
        body_data = payload.get('body', {}).get('data', '')
        if body_data:
            return base64.urlsafe_b64decode(body_data).decode('utf-8')

    return "No plain text content found."

def mask_pii(text):
    scrubber = scrubadub.Scrubber()
    masked_text = scrubber.clean(text)
    return masked_text

def summarize_email_content(email_texts):
    
    genai.configure(api_key=os.environ["GEMINI_API_KEY"])

    prompt = (
        f"First summarize all the emails recieved today in one paragraph atleast 500 words. Then summarize each individual email recieved today also identifying which email you are summarizing. Finally, print the inportant details from all the emails you think should be highlights, and specify which emails these important details came from:\n"
        f"---\n" + '\n'.join(email_texts) + "\n---"
    )

    model = genai.GenerativeModel("gemini-1.5-flash")
    response = model.generate_content(prompt)

    if response and response.text:
        return response.text.strip()
    else:
        print("Failed to get summary from LLM.")
        return None

def process_todays_emails(service):
    emails = get_todays_emails(service)
    email_texts = []

    for email_data in emails:
        email = fetch_email_content(service, email_data['id'])
        masked_content = mask_pii(email['subject'] + '\n' + email['body'])
        email_texts.append(masked_content)

    if email_texts:
        summary = summarize_email_content(email_texts)
        if summary:
            print("Today's email summary:")
            print(summary)
            return summary
        else:
            print("No important content found.")
            return "No important content found."
    else:
        print("No emails received today.")
        return "No emails received today."


@app.route('/api/summarize_emails', methods=['POST'])
def summarize_emails_api():
    service = authenticate_gmail()
    summary = process_todays_emails(service)
    return jsonify({"message": "Email summarization complete", "summary": summary}), 200

import os
import pickle
import time
from flask import Flask, request, jsonify
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
import scrubadub
from dotenv import load_dotenv

load_dotenv()

app = Flask(__name__)

# Google API scope
SCOPES = ["https://www.googleapis.com/auth/gmail.modify"]

def authenticate_gmail():
    creds = None
    if os.path.exists("token.pickle"):
        with open("token.pickle", "rb") as token:
            creds = pickle.load(token)

    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                "credentials.json", SCOPES
            )
            creds = flow.run_local_server(port=0)

        with open("token.pickle", "wb") as token:
            pickle.dump(creds, token)

    return build("gmail", "v1", credentials=creds)

def get_unread_emails(service, categories):
    query = 'is:unread AND ' + ' AND '.join([f'-label:{category}' for category in categories])
    results = service.users().messages().list(userId='me', q=query).execute()
    messages = results.get('messages', [])
    return messages

def fetch_email_content(service, email_id):
    email = service.users().messages().get(userId='me', id=email_id, format='full').execut
[truncated — 15863 more characters]
```

### LoginPage.js

```javascript
import React, { useState } from 'react';

const LoginPage = () => {
  const [emailSummary, setEmailSummary] = useState('');
  const [categories, setCategories] = useState('');
  const [isSummarizing, setIsSummarizing] = useState(false);
  const [isCategorizing, setIsCategorizing] = useState(false);
  const [isScheduling, setIsScheduling] = useState(false);
  const [isRecommending, setIsRecommending] = useState(false);

  const handleApiError = (error, message) => {
    console.error(message, error);
    alert(`${message}. Please try again.`);
  };

  const handleSummarizeEmails = async () => {
    setIsSummarizing(true);
  
    const myHeaders = new Headers();
    myHeaders.append('Content-Type', 'application/json');
  
    const categoriesArray = categories.split(',').map((c) => c.trim()); 
    const raw = JSON.stringify({
      categories: categoriesArray,
    });
  
    const requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: raw,
      redirect: 'follow',
    };
  
    try {
      const response = await fetch('http://127.0.0.1:5000/api/categorize_emails', requestOptions);
      const result = await response.text();
      console.log(result);
      setEmailSummary(result); 
    } catch (error) {
      handleApiError(error, 'Error summarizing emails');
    } finally {
      setIsSummarizing(false);
    }
  };
  

  const handleCategorizeEmails = async () => {
    setIsCategorizing(true);

    const myHeaders = new Headers();
    myHeaders.append('Content-Type', 'application/json');

    const categoriesArray = categories.split(',').map((c) => c.trim());
    const raw = JSON.stringify({ categories: categoriesArray });

    const requestOptions = {
      method: 'POST',
      headers: myHeaders,
      body: raw,
      redirect: 'follow',
    };

    try {
      const response = await fetch('http://127.0.0.1:5000/api/categorize_emails', requestOptions);
      const result = await response.text();
      console.log(result);
      alert('Emails categorized successfully!');
    } catch (error) {
      handleApiError(error, 'Error categorizing emails');
    } finally {
      setIsCategorizing(false);
    }
  };

  const handleScheduleEmails = async () => {
    setIsScheduling(true);
    const requestOptions = {
      method: 'POST',
      redirect: 'follow',
    };

    try {
      const response = await fetch('http://172.210.16.114:6060/api/linear_updates', requestOptions);
      const result = await response.text();
      console.log(result);
      alert('Emails scheduled successfully!');
    } catch (error) {
      handleApiError(error, 'Error scheduling emails');
    } finally {
      setIsScheduling(false);
    }
  };

  const handleFollowUpRecommendations = async () => {
    setIsRecommending(true);
    const requestOptions = {
      method: 'POST',
      redirect: 'follow',
    };

    try {
      const response = await fetch('http://172.210.16.114:6060/api/process_emails', requestOptions);
      const result = await response.text();
      console.log(result);
      alert('Follow-up recommendations generated successfully!');
    } catch (error) {
      handleApiError(error, 'Error getting follow-up recommendations');
    } finally {
      setIsRecommending(false);
    }
  };

  return (
    <div className="min-h-screen bg-gray-100 py-6 flex flex-col justify-center sm:py-12">
      <div className="relative py-3 sm:max-w-xl sm:mx-auto">
        <div className="relative px-4 py-10 bg-white shadow-lg sm:rounded-3xl sm:p-20">
          <div className="max-w-md mx-auto">
            <h2 className="text-2xl font-bold mb-4">Email Management Dashboard</h2>

            {/* Email Summarization */}
            <div className="mb-6">
              <button
                onClick={handleSummarizeEmails}
                disabled={isSummarizing}
                className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
              >
                {isSummarizing ? 'Summarizing...' : 'Summarize Emails'}
              </button>
              {emailSummary && (
                <div className="mt-4 p-4 border rounded">
                  <h3 className="font-bold mb-2">Email Summary</h3>
                  <p>{emailSummary}</p>
                </div>
              )}
            </div>

            {/* Email Categorization */}
            <div className="mb-6">
              <input
                type="text"
                placeholder="Enter categories (comma-separated)"
                value={categories}
                onChange={(e) => setCategories(e.target.value)}
                className="mb-2 p-2 border rounded w-full"
              />
              <button
                onClick={handleCategorizeEmails}
                disabled={isCategorizing || !categories}
                className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
              >
                {isCategorizing ? 'Categorizing...' : 'Categorize Emails'}
              </button>
            </div>

            {/* Automatic Scheduler */}
            <div className="mb-6">
              <button
                onClick={handleScheduleEmails}
                disabled={isScheduling}
                className="bg-yellow-500 hover:bg-yellow-700 text-white font-bold py-2 px-4 rounded"
              >
                {isScheduling ? 'Scheduling...' : 'Schedule Emails'}
              </button>
            </div>

            {/* Follow-up Recommender */}
            <div className="mb-6">
              <button
                onClick={handleFollowUpRecommendations}
                disabled={isRecommending}
                className="bg-purple-500 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded"
              >
                {isRecommending ? 'Getting Recommendations...' : 'Get Foll
[truncated — 169 more characters]
```

### LandingPage.js

```javascript
import React, { useEffect, useRef } from 'react';
import { Link } from 'react-router-dom';
import { Heart, Globe, Shield, BarChart, File, Rss, ChevronDown, Search, Inbox, FileText, Send, Trash2, Archive, Users, Bell, ShoppingCart, Zap } from 'lucide-react';
import EmailToolAd from './EmailToolAd.js';

const EmailInterface = () => {
  return (
    <div className="w-full max-w-5xl bg-zinc-900 rounded-lg overflow-hidden shadow-2xl">
      <div className="bg-zinc-800 p-2 flex items-center space-x-2">
        <div className="w-3 h-3 rounded-full bg-red-500"></div>
        <div className="w-3 h-3 rounded-full bg-yellow-500"></div>
        <div className="w-3 h-3 rounded-full bg-green-500"></div>
      </div>
      <div className="flex">
        {/* Sidebar */}
        <div className="w-1/4 bg-zinc-900 p-4 border-r border-zinc-800">
          <div className="flex items-center mb-6">
            <div className="w-8 h-8 bg-zinc-700 rounded-full mr-2"></div>
            <div className="text-sm font-semibold">Alicia Koch</div>
            <ChevronDown size={16} className="ml-auto" />
          </div>
          <div className="space-y-2">
            {[
              { icon: Inbox, label: 'Inbox', count: 128 },
              { icon: FileText, label: 'Drafts', count: 9 },
              { icon: Send, label: 'Sent', count: null },
              { icon: Trash2, label: 'Junk', count: 23 },
              { icon: Trash2, label: 'Trash', count: null },
              { icon: Archive, label: 'Archive', count: null },
              { icon: Users, label: 'Social', count: 972 },
              { icon: Bell, label: 'Updates', count: 342 },
              { icon: Users, label: 'Forums', count: 128 },
              { icon: ShoppingCart, label: 'Shopping', count: 8 },
              { icon: Zap, label: 'Promotions', count: 21 }
            ].map(({ icon: Icon, label, count }, index) => (
              <div key={index} className="flex items-center text-zinc-400 hover:text-white">
                <Icon size={16} className="mr-2" />
                <span>{label}</span>
                {count !== null && <span className="ml-auto">{count}</span>}
              </div>
            ))}
          </div>
        </div>

        {/* Main Content */}
        <div className="w-3/4 p-4">
          <div className="flex justify-between items-center mb-4">
            <h2 className="text-xl font-bold">Inbox</h2>
            <div className="flex space-x-2">
              <button className="bg-zinc-800 px-3 py-1 rounded text-sm">All mail</button>
              <button className="bg-zinc-800 px-3 py-1 rounded text-sm">Unread</button>
            </div>
          </div>
          <div className="bg-zinc-800 p-2 rounded mb-4 flex items-center">
            <Search size={16} className="text-zinc-400 mr-2" />
            <input type="text" placeholder="Search" className="bg-transparent text-sm w-full outline-none" />
          </div>
          {/* Email List */}
          <div className="space-y-2">
            {[
              { name: 'William Smith', subject: 'Meeting Tomorrow', time: '4 months ago', preview: 'Hi, let\'s have a meeting tomorrow to discuss the project. I\'ve been reviewing the project...' },
              { name: 'Alice Smith', subject: 'Re: Project Update', time: '4 months ago', preview: 'Thank you for the project update. It looks great! I\'ve gone through the report, and the...' },
              { name: 'Bob Johnson', subject: 'Weekend Plans', time: '5 months ago', preview: 'Hey team, I hope this email finds you well. I wanted to touch base about our plans for the...' }
            ].map((email, index) => (
              <div key={index} className="bg-zinc-800 p-3 rounded">
                <div className="flex justify-between items-center mb-1">
                  <span className="font-semibold">{email.name}</span>
                  <span className="text-xs text-zinc-400">{email.time}</span>
                </div>
                <div className="text-sm font-medium mb-1">{email.subject}</div>
                <div className="text-xs text-zinc-400">{email.preview}</div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

const LandingPage = () => {
  const iconRef = useRef(null);

  useEffect(() => {
    const icons = iconRef.current.children;
    let index = 0;
    const interval = setInterval(() => {
      icons[index].style.opacity = '0';
      index = (index + 1) % icons.length;
      icons[index].style.opacity = '1';
    }, 2000);
    return () => clearInterval(interval);
  }, []);

  return (
    <div className="min-h-screen flex flex-col bg-black text-white overflow-hidden">
      <header className="p-6 flex justify-between items-center z-10">
        <div className="text-sm font-bold">i love email lol</div>
        <div className="space-x-6">
          <Link to="/login" className="text-sm">Log in</Link>
          <button className="text-sm bg-zinc-800 text-white px-4 py-2 rounded-full">Sign up</button>
        </div>
      </header>
  
        <main className="flex-grow flex flex-col items-center justify-center text-center px-4 relative mt-16">
          <div ref={iconRef} className="absolute inset-0 flex items-center justify-center opacity-5">
            {[Heart, Globe, Shield, BarChart, File, Rss].map((Icon, index) => (
              <Icon key={index} size={400} className="absolute transition-opacity duration-1000" style={{opacity: index === 0 ? 1 : 0}} />
            ))}
          </div>
  
          <h1 className="text-6xl sm:text-7xl font-bold mb-6 relative z-10 bg-clip-text bg-gradient-to-b from-white to-gray-400">
  a new way to<br />
  <span className="relative inline-block">
    love email again.
    <svg className="absolute -bottom-2 left-0 w-full" height="10" viewBox="0 0 300 10" preserveAspectRatio="none">
      <path d="M0,5 C50,0 100,10 150
[truncated — 3904 more characters]
```