diff --git a/cpanel_deployment/INSTALLATION.md b/cpanel_deployment/INSTALLATION.md new file mode 100644 index 0000000..0b6203f --- /dev/null +++ b/cpanel_deployment/INSTALLATION.md @@ -0,0 +1,273 @@ +# Epic Travel & Expeditions - cPanel Deployment Guide + +## Overview +This package contains the Epic Travel & Expeditions website configured for cPanel hosting with MySQL database. + +## Requirements +- cPanel hosting with: + - Python 3.8+ support + - MySQL 5.7+ or MariaDB 10.3+ + - Apache with mod_rewrite enabled + - SSL certificate (recommended) +- At least 500MB disk space +- PHP 7.4+ (optional, for phpMyAdmin) + +## Package Contents +``` +epic-travel-cpanel/ +├── backend/ # Python FastAPI backend +│ ├── routes/ # API endpoints +│ ├── models/ # Database models +│ ├── server.py # Main application +│ ├── requirements.txt # Python dependencies +│ └── .env.example # Environment template +├── frontend/ # React frontend (production build) +│ ├── build/ # Compiled React app +│ └── .htaccess # Apache configuration +├── database_schema.sql # MySQL database schema +├── setup_admin.py # Admin user setup script +└── INSTALLATION.md # This file +``` + +## Installation Steps + +### Step 1: Database Setup + +1. **Create MySQL Database** + - Log into cPanel → MySQL Databases + - Create new database: `username_epic_travel` + - Create database user with strong password + - Grant ALL PRIVILEGES to the user + +2. **Import Database Schema** + - Go to phpMyAdmin + - Select your database + - Click "Import" tab + - Upload `database_schema.sql` + - Click "Go" + +3. **Generate Admin Password Hash** + ```bash + cd backend + python3 setup_admin.py + ``` + - Copy the generated hash + - Update the admin_users INSERT statement in the SQL file if needed + +### Step 2: Backend Setup + +1. **Upload Backend Files** + - Upload `backend/` folder to your cPanel account + - Recommended location: `~/epic-travel-api/` + +2. **Install Python Dependencies** + ```bash + cd ~/epic-travel-api + python3 -m venv venv + source venv/bin/activate + pip install -r requirements.txt + ``` + +3. **Configure Environment** + - Copy `.env.example` to `.env` + - Edit `.env` with your settings: + ```env + MYSQL_HOST=localhost + MYSQL_PORT=3306 + MYSQL_DATABASE=username_epic_travel + MYSQL_USER=username_dbuser + MYSQL_PASSWORD=your_secure_password + JWT_SECRET_KEY=your_random_256bit_key + ADMIN_DEFAULT_PASSWORD=Joker1974!!! + CORS_ORIGINS=https://yourdomain.com + ``` + +4. **Setup Python Application** + - In cPanel → Setup Python App + - Python version: 3.8+ + - Application root: `/home/username/epic-travel-api` + - Application URL: `/api` + - Application startup file: `server.py` + - Application Entry point: `app` + - Click "Create" + +5. **Install Dependencies via cPanel** + - In the Python App configuration + - Click "Run pip install" button + - Or run: `pip install -r requirements.txt` + +### Step 3: Frontend Setup + +1. **Upload Frontend Build** + - Upload contents of `frontend/build/` to your public_html + - Or to a subdomain folder + +2. **Configure .htaccess** + - Ensure `.htaccess` is present in the root + - Modify if your API is on a different path + +3. **Update API URL** + - In `public_html/static/js/main.*.js` + - Or set via environment variable during build + +### Step 4: SSL Configuration + +1. **Enable SSL** + - In cPanel → SSL/TLS + - Install Let's Encrypt certificate (free) + - Enable "Force HTTPS Redirect" + +2. **Update CORS** + - Edit backend `.env` + - Set: `CORS_ORIGINS=https://yourdomain.com` + - Restart Python application + +### Step 5: Testing + +1. **Test Backend API** + ```bash + curl https://yourdomain.com/api + ``` + Should return: `{"message": "Epic Travel API is running", "status": "healthy"}` + +2. **Test Frontend** + - Visit: https://yourdomain.com + - Should see Epic Travel homepage + +3. **Test Admin Login** + - Visit: https://yourdomain.com/admin + - Login with: + - Email: admin@epictravel.com + - Password: Joker1974!!! + +## Configuration Files + +### Backend .env +```env +# Database Configuration +MYSQL_HOST=localhost +MYSQL_PORT=3306 +MYSQL_DATABASE=username_epic_travel +MYSQL_USER=username_dbuser +MYSQL_PASSWORD=your_password + +# Security +JWT_SECRET_KEY=generate_with_openssl_rand_hex_32 +ADMIN_DEFAULT_PASSWORD=Joker1974!!! + +# CORS +CORS_ORIGINS=https://yourdomain.com +``` + +### Frontend .htaccess +```apache + + RewriteEngine On + RewriteBase / + + # API Proxy + RewriteCond %{REQUEST_URI} ^/api/(.*)$ + RewriteRule ^api/(.*)$ https://yourdomain.com/api/$1 [P,L] + + # React Router + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^ index.html [L] + +``` + +## Troubleshooting + +### Backend not starting +- Check Python version: `python3 --version` +- Check error logs in cPanel +- Verify MySQL connection details +- Ensure all dependencies installed + +### Frontend shows blank page +- Check browser console for errors +- Verify API URL in frontend build +- Check .htaccess file exists +- Clear browser cache + +### Database connection fails +- Verify MySQL credentials +- Check if database user has proper privileges +- Ensure MySQL server is running +- Check host (use 'localhost' not '127.0.0.1') + +### CORS errors +- Update CORS_ORIGINS in backend .env +- Restart Python application +- Check SSL configuration +- Ensure frontend and backend use same protocol (HTTPS) + +## Performance Optimization + +1. **Enable Gzip Compression** + - Add to .htaccess: + ```apache + + AddOutputFilterByType DEFLATE text/html text/css text/javascript application/javascript application/json + + ``` + +2. **Enable Browser Caching** + - Add to .htaccess: + ```apache + + ExpiresActive On + ExpiresByType image/jpg "access plus 1 year" + ExpiresByType image/jpeg "access plus 1 year" + ExpiresByType image/png "access plus 1 year" + ExpiresByType text/css "access plus 1 month" + ExpiresByType application/javascript "access plus 1 month" + + ``` + +3. **MySQL Optimization** + - Add indexes to frequently queried columns + - Use connection pooling + - Enable query caching + +## Maintenance + +### Updating the Application +1. Backup database: Export via phpMyAdmin +2. Backup files: Download via FTP +3. Upload new files +4. Run any database migrations +5. Restart Python application + +### Database Backup +```bash +mysqldump -u username -p username_epic_travel > backup_$(date +%Y%m%d).sql +``` + +### Monitoring +- Check error logs in cPanel +- Monitor disk space usage +- Review database size +- Check Python app status + +## Support +For issues specific to this application: +- Check logs in cPanel +- Verify all configuration settings +- Ensure MySQL connection is working +- Test API endpoints individually + +## Security Checklist +- [ ] Strong MySQL password set +- [ ] JWT secret key generated and set +- [ ] Admin password changed from default +- [ ] SSL certificate installed +- [ ] HTTPS redirect enabled +- [ ] CORS properly configured +- [ ] File permissions set correctly (644 for files, 755 for directories) +- [ ] .env file protected (not web-accessible) + +## Credits +Epic Travel & Expeditions +Contact: advisor@epictravelexpeditions.com +Phone: +1 (817) 266-2022 diff --git a/cpanel_deployment/MIGRATION_GUIDE.md b/cpanel_deployment/MIGRATION_GUIDE.md new file mode 100644 index 0000000..a22f8d2 --- /dev/null +++ b/cpanel_deployment/MIGRATION_GUIDE.md @@ -0,0 +1,302 @@ +# Epic Travel & Expeditions - MongoDB to MySQL Migration Guide + +## Overview +This guide helps you migrate the Epic Travel & Expeditions application from MongoDB to MySQL for cPanel deployment. + +## Key Differences + +### Database Structure +- **MongoDB**: Document-based, collections, flexible schema +- **MySQL**: Table-based, structured schema, relationships + +### Data Type Mapping +| MongoDB | MySQL | +|---------|-------| +| _id (ObjectId) | id VARCHAR(36) - UUID | +| String | VARCHAR or TEXT | +| Number | INT, DECIMAL, NUMERIC | +| Date | DATETIME | +| Array | JSON column | +| Object | JSON column | + +## Migration Steps + +### 1. Export Data from MongoDB + +```bash +# Export destinations +mongoexport --db=test_database --collection=destinations --out=destinations.json + +# Export specials +mongoexport --db=test_database --collection=specials --out=specials.json + +# Export admin_users +mongoexport --db=test_database --collection=admin_users --out=admin_users.json + +# Export contacts +mongoexport --db=test_database --collection=contacts --out=contacts.json + +# Export newsletter_subscribers +mongoexport --db=test_database --collection=newsletter_subscribers --out=newsletter.json +``` + +### 2. Transform Data for MySQL + +MongoDB documents need to be transformed to match MySQL schema: + +**MongoDB Document:** +```json +{ + "_id": {"$oid": "507f1f77bcf86cd799439011"}, + "name": "Paris", + "rating": 4.9 +} +``` + +**MySQL INSERT:** +```sql +INSERT INTO destinations (id, name, rating) +VALUES ('507f1f77bcf86cd799439011', 'Paris', 4.9); +``` + +### 3. Code Changes Required + +#### Backend Changes + +**Old (MongoDB with Motor):** +```python +from motor.motor_asyncio import AsyncIOMotorClient + +client = AsyncIOMotorClient(mongo_url) +db = client[os.environ['DB_NAME']] + +# Query +destinations = await db.destinations.find().to_list(100) +``` + +**New (MySQL with SQLAlchemy):** +```python +from sqlalchemy.orm import Session +from database import SessionLocal, Destination + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +# Query +destinations = db.query(Destination).limit(100).all() +``` + +#### API Route Changes + +**Old (Async MongoDB):** +```python +@router.get("/destinations") +async def get_destinations(): + destinations = await db.destinations.find().to_list(100) + return destinations +``` + +**New (Sync MySQL):** +```python +@router.get("/destinations") +def get_destinations(db: Session = Depends(get_db)): + destinations = db.query(Destination).limit(100).all() + return destinations +``` + +### 4. Environment Variables + +**Old (.env for MongoDB):** +```env +MONGO_URL=mongodb://localhost:27017 +DB_NAME=test_database +``` + +**New (.env for MySQL):** +```env +MYSQL_HOST=localhost +MYSQL_PORT=3306 +MYSQL_DATABASE=epic_travel +MYSQL_USER=dbuser +MYSQL_PASSWORD=password +``` + +### 5. Dependencies + +**Remove:** +``` +motor==3.3.1 +pymongo==4.5.0 +``` + +**Add:** +``` +PyMySQL>=1.1.0 +SQLAlchemy>=2.0.23 +``` + +## Automated Migration Script + +```python +#!/usr/bin/env python3 +""" +Migrate data from MongoDB to MySQL +""" +from pymongo import MongoClient +from sqlalchemy.orm import Session +from database import engine, SessionLocal, Destination, Special +import uuid + +def migrate_destinations(): + # Connect to MongoDB + mongo_client = MongoClient('mongodb://localhost:27017') + mongo_db = mongo_client['test_database'] + + # Connect to MySQL + mysql_db = SessionLocal() + + try: + # Get all destinations from MongoDB + mongo_destinations = mongo_db.destinations.find() + + for doc in mongo_destinations: + # Transform MongoDB document to SQLAlchemy model + destination = Destination( + id=str(doc.get('id', uuid.uuid4())), + name=doc['name'], + location=doc['location'], + description=doc['description'], + image=doc['image'], + category=doc['category'], + rating=float(doc['rating']), + price=float(doc['price']), + currency=doc.get('currency', 'USD'), + created_at=doc.get('created_at') + ) + mysql_db.add(destination) + + mysql_db.commit() + print(f"Migrated {mongo_destinations.count()} destinations") + + except Exception as e: + mysql_db.rollback() + print(f"Error: {e}") + finally: + mysql_db.close() + mongo_client.close() + +if __name__ == "__main__": + migrate_destinations() +``` + +## Testing Migration + +### 1. Compare Counts +```sql +-- MySQL +SELECT COUNT(*) FROM destinations; +SELECT COUNT(*) FROM specials; +SELECT COUNT(*) FROM admin_users; +``` + +```javascript +// MongoDB +db.destinations.count() +db.specials.count() +db.admin_users.count() +``` + +### 2. Sample Data Verification +```sql +-- Check a specific destination +SELECT * FROM destinations WHERE name = 'Paris'; +``` + +### 3. Test Relationships +```sql +-- Check specials with destinations +SELECT d.name, s.discount, s.end_date +FROM destinations d +JOIN specials s ON d.id = s.destination_id; +``` + +## Performance Considerations + +### Indexing +MySQL indexes are already defined in schema: +- Primary keys on id columns +- Indexes on frequently queried columns (name, location, category, email) +- Foreign keys for relationships + +### Connection Pooling +SQLAlchemy provides built-in connection pooling: +```python +engine = create_engine( + DATABASE_URL, + pool_size=10, + max_overflow=20, + pool_recycle=3600 +) +``` + +### Query Optimization +- Use LIMIT for pagination +- Use indexes for WHERE clauses +- Use JOIN instead of multiple queries +- Cache frequently accessed data + +## Rollback Plan + +If migration fails: +1. Keep MongoDB running alongside MySQL initially +2. Test thoroughly before switching +3. Keep MongoDB backups for 30 days +4. Have both versions of code ready + +## Post-Migration Checklist + +- [ ] All collections migrated to tables +- [ ] Data counts match between MongoDB and MySQL +- [ ] All relationships working correctly +- [ ] Authentication still working +- [ ] API endpoints returning correct data +- [ ] Frontend displaying data correctly +- [ ] Image uploads working +- [ ] Admin dashboard functional +- [ ] Contact forms saving to MySQL +- [ ] Newsletter subscriptions working + +## Common Issues + +### Issue: Date format differences +**Solution:** Convert MongoDB ISODate to MySQL DATETIME +```python +created_at = datetime.fromisoformat(doc['created_at']) +``` + +### Issue: JSON array in specials.highlights +**Solution:** MySQL JSON column handles this automatically +```python +highlights = json.dumps(['item1', 'item2']) # Store as JSON string +highlights = json.loads(row.highlights) # Retrieve and parse +``` + +### Issue: UUID vs ObjectId +**Solution:** Use UUID strings in MySQL +```python +import uuid +id = str(uuid.uuid4()) +``` + +## Support + +For migration assistance: +- Check logs for specific errors +- Verify MySQL credentials +- Test database connection independently +- Review SQLAlchemy documentation +- Contact: advisor@epictravelexpeditions.com diff --git a/cpanel_deployment/PACKAGE_INFO.md b/cpanel_deployment/PACKAGE_INFO.md new file mode 100644 index 0000000..8251511 --- /dev/null +++ b/cpanel_deployment/PACKAGE_INFO.md @@ -0,0 +1,351 @@ +# Epic Travel & Expeditions - cPanel Deployment Package + +## Package Information + +**Package Name:** Epic Travel & Expeditions +**Version:** 1.0.0 +**Date Created:** December 2025 +**Package Type:** cPanel MySQL Deployment + +## What's Included + +### 1. Complete Application +- **Frontend:** Production-optimized React build (152.62 KB gzipped) +- **Backend:** Python FastAPI with MySQL support +- **Database:** MySQL schema with sample data + +### 2. Documentation +- `INSTALLATION.md` - Complete step-by-step installation guide +- `MIGRATION_GUIDE.md` - MongoDB to MySQL migration instructions +- `README.txt` - Quick start guide + +### 3. Configuration Files +- `.htaccess` - Apache configuration for React routing and API proxy +- `.env.example` - Environment variables template +- `database_schema.sql` - MySQL database structure with sample data + +### 4. Setup Tools +- `setup_admin.py` - Admin password hash generator +- `create_package.sh` - Package creation script + +## Package Files + +``` +epic-travel-cpanel-YYYYMMDD-HHMMSS/ +├── README.txt # Quick start guide +├── INSTALLATION.md # Detailed installation instructions +├── MIGRATION_GUIDE.md # MongoDB to MySQL migration guide +├── database_schema.sql # MySQL database schema +├── setup_admin.py # Admin password setup tool +├── backend/ # Python FastAPI application +│ ├── server.py # Main application file +│ ├── auth.py # JWT authentication +│ ├── database.py # MySQL/SQLAlchemy configuration +│ ├── requirements.txt # Python dependencies +│ ├── .env.example # Environment template +│ ├── models/ # Data models +│ │ ├── __init__.py +│ │ └── schemas.py +│ └── routes/ # API endpoints +│ ├── __init__.py +│ ├── auth_routes.py # Authentication endpoints +│ ├── destination_routes.py # Destinations CRUD +│ ├── special_routes.py # Weekly specials management +│ └── other_routes.py # Contact, newsletter, uploads +└── frontend/ # React production build + ├── index.html # Main HTML file + ├── .htaccess # Apache configuration + ├── static/ # Compiled JS/CSS + │ ├── css/ + │ └── js/ + ├── manifest.json + └── robots.txt +``` + +## Quick Start + +### Prerequisites +- cPanel hosting account +- Python 3.8+ support +- MySQL 5.7+ or MariaDB 10.3+ +- SSL certificate (recommended) + +### Installation Steps + +1. **Download Package** + - Choose either `.tar.gz` or `.zip` format + - Extract to your local computer + +2. **Create MySQL Database** + - Log into cPanel + - Create new MySQL database + - Create database user with strong password + - Grant all privileges + +3. **Import Database** + - Open phpMyAdmin + - Select your database + - Import `database_schema.sql` + +4. **Upload Files** + - Upload `backend/` folder to your hosting account + - Upload `frontend/` contents to `public_html` (or subdomain folder) + +5. **Configure Backend** + - Copy `.env.example` to `.env` + - Edit with your MySQL credentials + - Generate JWT secret key + +6. **Setup Python App** + - In cPanel, go to "Setup Python App" + - Configure application root and entry point + - Install dependencies from requirements.txt + +7. **Test Installation** + - Visit your website + - Test API endpoint: `https://yourdomain.com/api` + - Login to admin: `https://yourdomain.com/admin` + +## Features + +### Public Website +- ✅ Beautiful travel destinations gallery +- ✅ Weekly special deals showcase +- ✅ Search and filter destinations +- ✅ Customer testimonials +- ✅ Contact form +- ✅ Newsletter subscription +- ✅ Responsive design +- ✅ Professional branding + +### Admin Dashboard +- ✅ Secure JWT authentication +- ✅ Destination management (Add/Edit/Delete) +- ✅ Upload destination images +- ✅ Weekly specials management +- ✅ Set discount percentages and end dates +- ✅ Real-time updates to public site + +### Technical Features +- ✅ FastAPI backend (Python) +- ✅ React frontend (production-optimized) +- ✅ MySQL database with relationships +- ✅ RESTful API architecture +- ✅ JWT token authentication +- ✅ Bcrypt password hashing +- ✅ CORS configured +- ✅ SSL ready +- ✅ SEO friendly +- ✅ Browser caching enabled +- ✅ Gzip compression + +## System Requirements + +### Server Requirements +- **Operating System:** Linux (recommended) +- **Web Server:** Apache 2.4+ with mod_rewrite +- **Python:** 3.8 or higher +- **Database:** MySQL 5.7+ or MariaDB 10.3+ +- **PHP:** 7.4+ (for phpMyAdmin, optional) +- **Disk Space:** 500MB minimum +- **RAM:** 512MB minimum (1GB recommended) + +### cPanel Features Needed +- Python App Setup +- MySQL Databases +- File Manager or FTP access +- SSL/TLS Management +- Cron Jobs (optional, for automated tasks) + +## Default Credentials + +### Admin Portal +- **URL:** `https://yourdomain.com/admin` +- **Email:** `admin@epictravel.com` +- **Password:** `Joker1974!!!` + +**⚠️ IMPORTANT:** Change the admin password after first login! + +## Database Information + +### Tables Created +- `destinations` - Travel destinations with details +- `specials` - Weekly special offers +- `admin_users` - Admin account credentials +- `contacts` - Contact form submissions +- `newsletter_subscribers` - Newsletter email list + +### Sample Data Included +- 12 Travel destinations (Paris, Bali, Tokyo, etc.) +- 3 Weekly special offers +- 1 Admin user account + +## API Endpoints + +### Public Endpoints +- `GET /api` - Health check +- `GET /api/destinations` - List all destinations +- `GET /api/destinations/{id}` - Get single destination +- `GET /api/specials` - List weekly specials +- `POST /api/contact` - Submit contact form +- `POST /api/newsletter/subscribe` - Subscribe to newsletter + +### Admin Endpoints (Requires Authentication) +- `POST /api/auth/login` - Admin login +- `POST /api/destinations` - Create destination +- `PUT /api/destinations/{id}` - Update destination +- `DELETE /api/destinations/{id}` - Delete destination +- `POST /api/specials` - Add to specials +- `PUT /api/specials/{id}` - Update special +- `DELETE /api/specials/destination/{id}` - Remove from specials +- `POST /api/upload/image` - Upload image + +## Configuration Options + +### Environment Variables +```env +# Database +MYSQL_HOST=localhost +MYSQL_PORT=3306 +MYSQL_DATABASE=your_database +MYSQL_USER=your_user +MYSQL_PASSWORD=your_password + +# Security +JWT_SECRET_KEY=your_secret_key +ADMIN_DEFAULT_PASSWORD=Joker1974!!! + +# CORS +CORS_ORIGINS=https://yourdomain.com +``` + +### Frontend Configuration +- Edit `index.html` for meta tags +- Update `manifest.json` for PWA settings +- Modify `.htaccess` for custom redirects + +## Performance Optimization + +### Included Optimizations +- ✅ Gzip compression enabled +- ✅ Browser caching configured +- ✅ Static asset optimization +- ✅ Database query optimization +- ✅ Connection pooling +- ✅ Production React build + +### Additional Recommendations +- Enable CDN for static assets +- Use Redis for session caching +- Configure MySQL query cache +- Enable OPcache for PHP +- Use HTTP/2 if available + +## Security Features + +### Built-in Security +- ✅ JWT token authentication +- ✅ Bcrypt password hashing +- ✅ SQL injection prevention (SQLAlchemy ORM) +- ✅ XSS protection headers +- ✅ CORS configuration +- ✅ HTTPS enforcement +- ✅ Environment variables for secrets +- ✅ .env file protection + +### Security Recommendations +- Change default admin password +- Use strong JWT secret key +- Enable SSL certificate +- Regular security updates +- Strong MySQL password +- Restrict file permissions +- Regular database backups + +## Troubleshooting + +### Common Issues + +**Backend not starting** +- Check Python version compatibility +- Verify MySQL credentials +- Review error logs in cPanel +- Ensure dependencies installed + +**Frontend shows blank page** +- Check browser console for errors +- Verify .htaccess file exists +- Check API URL configuration +- Clear browser cache + +**Database connection fails** +- Verify MySQL credentials +- Check user privileges +- Ensure database exists +- Test connection independently + +**CORS errors** +- Update CORS_ORIGINS in .env +- Restart Python application +- Check SSL configuration +- Verify protocol matching (HTTPS) + +## Support & Documentation + +### Included Documentation +- `INSTALLATION.md` - Step-by-step setup guide +- `MIGRATION_GUIDE.md` - MongoDB to MySQL migration +- `README.txt` - Quick reference + +### Contact Information +- **Email:** advisor@epictravelexpeditions.com +- **Phone:** +1 (817) 266-2022 +- **Location:** Weatherford, Texas 76088 + +### Technical Support +- Check cPanel error logs +- Review application logs +- Test database connection +- Verify Python dependencies +- Check Apache configuration + +## Upgrade Path + +### Future Updates +1. Download new version package +2. Backup current database and files +3. Upload new files (don't overwrite .env) +4. Run database migrations if any +5. Restart Python application +6. Test functionality + +## License & Credits + +**Application:** Epic Travel & Expeditions +**Version:** 1.0.0 +**Built with:** +- React 19 +- FastAPI 0.110 +- SQLAlchemy 2.0 +- Python 3.11 +- MySQL 5.7+ + +**Created:** December 2025 +**Package Type:** cPanel MySQL Deployment + +--- + +## Package Download Information + +**Formats Available:** +- `epic-travel-cpanel-YYYYMMDD-HHMMSS.tar.gz` (784 KB) +- `epic-travel-cpanel-YYYYMMDD-HHMMSS.zip` (792 KB) + +**Checksum:** Available upon request +**Expiry:** None - Package is perpetual + +For the latest version or updates, contact support. + +--- + +**Ready to deploy? Start with INSTALLATION.md!** diff --git a/cpanel_deployment/backend/database.py b/cpanel_deployment/backend/database.py new file mode 100644 index 0000000..d49334a --- /dev/null +++ b/cpanel_deployment/backend/database.py @@ -0,0 +1,112 @@ +""" +MySQL Database Configuration for Epic Travel & Expeditions +Uses SQLAlchemy for MySQL connection +""" +from sqlalchemy import create_engine, Column, String, Text, Numeric, DateTime, JSON, ForeignKey +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker, relationship +from datetime import datetime +import os +from dotenv import load_dotenv +from pathlib import Path + +# Load environment variables +ROOT_DIR = Path(__file__).parent +load_dotenv(ROOT_DIR / '.env') + +# MySQL Database URL +MYSQL_USER = os.environ.get('MYSQL_USER') +MYSQL_PASSWORD = os.environ.get('MYSQL_PASSWORD') +MYSQL_HOST = os.environ.get('MYSQL_HOST', 'localhost') +MYSQL_PORT = os.environ.get('MYSQL_PORT', '3306') +MYSQL_DATABASE = os.environ.get('MYSQL_DATABASE') + +DATABASE_URL = f"mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DATABASE}?charset=utf8mb4" + +# Create engine +engine = create_engine(DATABASE_URL, pool_pre_ping=True, pool_recycle=3600) + +# Create session +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# Base class for models +Base = declarative_base() + +# Database Models +class Destination(Base): + __tablename__ = "destinations" + + id = Column(String(36), primary_key=True) + name = Column(String(255), nullable=False, index=True) + location = Column(String(255), nullable=False, index=True) + description = Column(Text, nullable=False) + image = Column(String(500), nullable=False) + category = Column(String(50), nullable=False, index=True) + rating = Column(Numeric(2, 1), nullable=False, default=4.5) + price = Column(Numeric(10, 2), nullable=False) + currency = Column(String(3), nullable=False, default='USD') + created_at = Column(DateTime, nullable=False, default=datetime.utcnow) + + # Relationship + specials = relationship("Special", back_populates="destination", cascade="all, delete-orphan") + + +class Special(Base): + __tablename__ = "specials" + + id = Column(String(36), primary_key=True) + destination_id = Column(String(36), ForeignKey('destinations.id', ondelete='CASCADE'), nullable=False, index=True) + discount = Column(Numeric(5, 2), nullable=False) + end_date = Column(String(10), nullable=False) # Store as string YYYY-MM-DD + highlights = Column(JSON, nullable=False) + created_at = Column(DateTime, nullable=False, default=datetime.utcnow) + + # Relationship + destination = relationship("Destination", back_populates="specials") + + +class AdminUser(Base): + __tablename__ = "admin_users" + + id = Column(String(36), primary_key=True) + email = Column(String(255), nullable=False, unique=True, index=True) + password_hash = Column(String(255), nullable=False) + created_at = Column(DateTime, nullable=False, default=datetime.utcnow) + + +class Contact(Base): + __tablename__ = "contacts" + + id = Column(String(36), primary_key=True) + name = Column(String(255), nullable=False) + email = Column(String(255), nullable=False) + message = Column(Text, nullable=False) + created_at = Column(DateTime, nullable=False, default=datetime.utcnow, index=True) + + +class NewsletterSubscriber(Base): + __tablename__ = "newsletter_subscribers" + + id = Column(String(36), primary_key=True) + email = Column(String(255), nullable=False, unique=True, index=True) + subscribed_at = Column(DateTime, nullable=False, default=datetime.utcnow) + + +# Dependency to get database session +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + +# Create all tables (if they don't exist) +def create_tables(): + Base.metadata.create_all(bind=engine) + + +if __name__ == "__main__": + print("Creating database tables...") + create_tables() + print("Database tables created successfully!") diff --git a/cpanel_deployment/backend/requirements.txt b/cpanel_deployment/backend/requirements.txt new file mode 100644 index 0000000..82f2591 --- /dev/null +++ b/cpanel_deployment/backend/requirements.txt @@ -0,0 +1,18 @@ +fastapi==0.110.1 +uvicorn==0.25.0 +python-dotenv>=1.0.1 +pydantic>=2.6.4 +email-validator>=2.2.0 +pyjwt>=2.10.1 +passlib>=1.7.4 +python-jose>=3.3.0 +python-multipart>=0.0.9 +requests>=2.31.0 + +# MySQL Database +PyMySQL>=1.1.0 +SQLAlchemy>=2.0.23 +cryptography>=42.0.5 + +# For bcrypt password hashing +bcrypt>=4.1.2 diff --git a/cpanel_deployment/create_package.sh b/cpanel_deployment/create_package.sh new file mode 100755 index 0000000..368233d --- /dev/null +++ b/cpanel_deployment/create_package.sh @@ -0,0 +1,107 @@ +#!/bin/bash +# Epic Travel & Expeditions - cPanel Deployment Package Creator +# This script creates a deployment-ready package for cPanel + +echo "======================================" +echo "Epic Travel cPanel Package Creator" +echo "======================================" +echo "" + +# Set variables +PACKAGE_NAME="epic-travel-cpanel-$(date +%Y%m%d-%H%M%S)" +PACKAGE_DIR="/app/cpanel_deployment/$PACKAGE_NAME" +BACKEND_SOURCE="/app/backend" +FRONTEND_SOURCE="/app/frontend" + +echo "Creating package directory: $PACKAGE_DIR" +mkdir -p "$PACKAGE_DIR"/{backend,frontend} + +# Copy backend files (excluding unnecessary files) +echo "Copying backend files..." +rsync -av --exclude='__pycache__' \ + --exclude='*.pyc' \ + --exclude='.env' \ + --exclude='venv' \ + --exclude='node_modules' \ + "$BACKEND_SOURCE/" "$PACKAGE_DIR/backend/" + +# Copy MySQL-specific files +echo "Adding MySQL database configuration..." +cp /app/cpanel_deployment/backend/database.py "$PACKAGE_DIR/backend/" +cp /app/cpanel_deployment/backend/requirements.txt "$PACKAGE_DIR/backend/" +cp /app/cpanel_deployment/backend/.env.example "$PACKAGE_DIR/backend/" + +# Build frontend for production +echo "Building frontend for production..." +cd "$FRONTEND_SOURCE" +yarn build + +# Copy frontend build +echo "Copying frontend build..." +cp -r "$FRONTEND_SOURCE/build/"* "$PACKAGE_DIR/frontend/" +cp /app/cpanel_deployment/frontend/.htaccess "$PACKAGE_DIR/frontend/" + +# Copy documentation and setup files +echo "Copying documentation..." +cp /app/cpanel_deployment/INSTALLATION.md "$PACKAGE_DIR/" +cp /app/cpanel_deployment/database_schema.sql "$PACKAGE_DIR/" +cp /app/cpanel_deployment/setup_admin.py "$PACKAGE_DIR/" + +# Create README in package root +cat > "$PACKAGE_DIR/README.txt" << 'EOF' +EPIC TRAVEL & EXPEDITIONS - cPanel Deployment Package +===================================================== + +This package contains everything needed to deploy Epic Travel & Expeditions +to a cPanel server with MySQL database. + +CONTENTS: +--------- +- backend/ Python FastAPI backend with MySQL support +- frontend/ React production build +- database_schema.sql MySQL database schema +- setup_admin.py Admin password hash generator +- INSTALLATION.md Complete installation guide + +QUICK START: +------------ +1. Read INSTALLATION.md for complete instructions +2. Create MySQL database in cPanel +3. Import database_schema.sql +4. Configure backend/.env with database credentials +5. Upload files to cPanel +6. Setup Python app in cPanel +7. Test the installation + +For detailed instructions, see INSTALLATION.md + +Contact: advisor@epictravelexpeditions.com +Phone: +1 (817) 266-2022 +EOF + +# Create compressed archive +echo "Creating compressed archive..." +cd /app/cpanel_deployment +tar -czf "$PACKAGE_NAME.tar.gz" "$PACKAGE_NAME" +zip -r "$PACKAGE_NAME.zip" "$PACKAGE_NAME" -q + +# Calculate sizes +TAR_SIZE=$(du -h "$PACKAGE_NAME.tar.gz" | cut -f1) +ZIP_SIZE=$(du -h "$PACKAGE_NAME.zip" | cut -f1) + +echo "" +echo "======================================" +echo "Package Created Successfully!" +echo "======================================" +echo "" +echo "Package Location:" +echo " Directory: /app/cpanel_deployment/$PACKAGE_NAME" +echo " Tar.gz: /app/cpanel_deployment/$PACKAGE_NAME.tar.gz ($TAR_SIZE)" +echo " Zip: /app/cpanel_deployment/$PACKAGE_NAME.zip ($ZIP_SIZE)" +echo "" +echo "Next Steps:" +echo " 1. Download the package (tar.gz or zip)" +echo " 2. Read INSTALLATION.md" +echo " 3. Follow the installation steps" +echo "" +echo "======================================" diff --git a/cpanel_deployment/database_schema.sql b/cpanel_deployment/database_schema.sql new file mode 100644 index 0000000..75dd305 --- /dev/null +++ b/cpanel_deployment/database_schema.sql @@ -0,0 +1,95 @@ +-- Epic Travel & Expeditions Database Schema for MySQL +-- Run this script to create the database structure + +CREATE DATABASE IF NOT EXISTS epic_travel CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +USE epic_travel; + +-- Destinations Table +CREATE TABLE IF NOT EXISTS destinations ( + id VARCHAR(36) PRIMARY KEY, + name VARCHAR(255) NOT NULL, + location VARCHAR(255) NOT NULL, + description TEXT NOT NULL, + image VARCHAR(500) NOT NULL, + category VARCHAR(50) NOT NULL, + rating DECIMAL(2,1) NOT NULL DEFAULT 4.5, + price DECIMAL(10,2) NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'USD', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_category (category), + INDEX idx_name (name), + INDEX idx_location (location) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Specials Table +CREATE TABLE IF NOT EXISTS specials ( + id VARCHAR(36) PRIMARY KEY, + destination_id VARCHAR(36) NOT NULL, + discount DECIMAL(5,2) NOT NULL, + end_date DATE NOT NULL, + highlights JSON NOT NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (destination_id) REFERENCES destinations(id) ON DELETE CASCADE, + INDEX idx_destination (destination_id), + INDEX idx_end_date (end_date) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Admin Users Table +CREATE TABLE IF NOT EXISTS admin_users ( + id VARCHAR(36) PRIMARY KEY, + email VARCHAR(255) NOT NULL UNIQUE, + password_hash VARCHAR(255) NOT NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_email (email) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Contacts Table +CREATE TABLE IF NOT EXISTS contacts ( + id VARCHAR(36) PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, + message TEXT NOT NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_created_at (created_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Newsletter Subscribers Table +CREATE TABLE IF NOT EXISTS newsletter_subscribers ( + id VARCHAR(36) PRIMARY KEY, + email VARCHAR(255) NOT NULL UNIQUE, + subscribed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_email (email) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Insert default admin user (password: Joker1974!!!) +-- Note: Replace the password_hash with the actual bcrypt hash +INSERT INTO admin_users (id, email, password_hash, created_at) +VALUES ( + 'admin-1', + 'admin@epictravel.com', + '$2b$12$PLACEHOLDER_HASH_WILL_BE_GENERATED', + NOW() +) ON DUPLICATE KEY UPDATE email=email; + +-- Insert sample destinations +INSERT INTO destinations (id, name, location, description, image, category, rating, price, currency) VALUES +('1', 'Paris', 'France', 'Experience the romance and elegance of the City of Light. Visit iconic landmarks like the Eiffel Tower, Louvre Museum, and stroll along the Champs-Élysées.', 'https://images.unsplash.com/photo-1502602898657-3e91760cbb34?w=800&q=80', 'City', 4.9, 1299, 'USD'), +('2', 'Bali', 'Indonesia', 'Discover tropical paradise with stunning beaches, ancient temples, lush rice terraces, and vibrant culture in this Indonesian gem.', 'https://images.unsplash.com/photo-1537996194471-e657df975ab4?w=800&q=80', 'Beach', 4.8, 899, 'USD'), +('3', 'Tokyo', 'Japan', 'Immerse yourself in the perfect blend of ancient tradition and cutting-edge technology in Japan\'s bustling capital city.', 'https://images.unsplash.com/photo-1540959733332-eab4deabeeaf?w=800&q=80', 'City', 4.9, 1499, 'USD'), +('4', 'Santorini', 'Greece', 'Marvel at breathtaking sunsets, whitewashed buildings, and crystal-clear waters in this stunning Greek island paradise.', 'https://images.unsplash.com/photo-1613395877344-13d4a8e0d49e?w=800&q=80', 'Beach', 4.9, 1199, 'USD'), +('5', 'Iceland', 'Iceland', 'Witness the Northern Lights, explore glaciers, geysers, and volcanic landscapes in this land of fire and ice.', 'https://images.unsplash.com/photo-1504829857797-ddff29c27927?w=800&q=80', 'Adventure', 4.8, 1699, 'USD'), +('6', 'Dubai', 'UAE', 'Experience luxury and innovation in the desert with world-class shopping, stunning architecture, and endless entertainment.', 'https://images.unsplash.com/photo-1512453979798-5ea266f8880c?w=800&q=80', 'City', 4.7, 1399, 'USD'), +('7', 'Maldives', 'Maldives', 'Relax in overwater bungalows, dive in pristine coral reefs, and enjoy the ultimate tropical island getaway.', 'https://images.unsplash.com/photo-1514282401047-d79a71a590e8?w=800&q=80', 'Beach', 5.0, 2199, 'USD'), +('8', 'New York', 'USA', 'Explore the city that never sleeps with iconic landmarks, world-class museums, Broadway shows, and diverse neighborhoods.', 'https://images.unsplash.com/photo-1496442226666-8d4d0e62e6e9?w=800&q=80', 'City', 4.8, 1099, 'USD'), +('9', 'Machu Picchu', 'Peru', 'Trek to the ancient Incan citadel nestled high in the Andes Mountains, one of the New Seven Wonders of the World.', 'https://images.unsplash.com/photo-1587595431973-160d0d94add1?w=800&q=80', 'Adventure', 4.9, 1299, 'USD'), +('10', 'Swiss Alps', 'Switzerland', 'Ski pristine slopes, hike mountain trails, and enjoy charming alpine villages with breathtaking mountain vistas.', 'https://images.unsplash.com/photo-1531366936337-7c912a4589a7?w=800&q=80', 'Adventure', 4.9, 1799, 'USD'), +('11', 'Venice', 'Italy', 'Glide through romantic canals, admire Renaissance architecture, and savor authentic Italian cuisine in this unique floating city.', 'https://images.unsplash.com/photo-1523906834658-6e24ef2386f9?w=800&q=80', 'City', 4.8, 1149, 'USD'), +('12', 'Safari Kenya', 'Kenya', 'Witness the Great Migration, spot the Big Five, and experience the raw beauty of African wilderness.', 'https://images.unsplash.com/photo-1516426122078-c23e76319801?w=800&q=80', 'Adventure', 4.9, 2499, 'USD') +ON DUPLICATE KEY UPDATE name=name; + +-- Insert sample specials +INSERT INTO specials (id, destination_id, discount, end_date, highlights) VALUES +('special-1', '2', 25, DATE_ADD(CURDATE(), INTERVAL 30 DAY), JSON_ARRAY('Free spa treatment', 'Complimentary airport transfer', 'Sunset dinner cruise')), +('special-2', '4', 30, DATE_ADD(CURDATE(), INTERVAL 45 DAY), JSON_ARRAY('Wine tasting tour', 'Private yacht excursion', 'Luxury accommodation upgrade')), +('special-3', '7', 20, DATE_ADD(CURDATE(), INTERVAL 20 DAY), JSON_ARRAY('Snorkeling adventure', 'Couples massage', 'Romantic beach dinner')) +ON DUPLICATE KEY UPDATE discount=discount; diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/INSTALLATION.md b/cpanel_deployment/epic-travel-cpanel-20260506-034534/INSTALLATION.md new file mode 100644 index 0000000..0b6203f --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/INSTALLATION.md @@ -0,0 +1,273 @@ +# Epic Travel & Expeditions - cPanel Deployment Guide + +## Overview +This package contains the Epic Travel & Expeditions website configured for cPanel hosting with MySQL database. + +## Requirements +- cPanel hosting with: + - Python 3.8+ support + - MySQL 5.7+ or MariaDB 10.3+ + - Apache with mod_rewrite enabled + - SSL certificate (recommended) +- At least 500MB disk space +- PHP 7.4+ (optional, for phpMyAdmin) + +## Package Contents +``` +epic-travel-cpanel/ +├── backend/ # Python FastAPI backend +│ ├── routes/ # API endpoints +│ ├── models/ # Database models +│ ├── server.py # Main application +│ ├── requirements.txt # Python dependencies +│ └── .env.example # Environment template +├── frontend/ # React frontend (production build) +│ ├── build/ # Compiled React app +│ └── .htaccess # Apache configuration +├── database_schema.sql # MySQL database schema +├── setup_admin.py # Admin user setup script +└── INSTALLATION.md # This file +``` + +## Installation Steps + +### Step 1: Database Setup + +1. **Create MySQL Database** + - Log into cPanel → MySQL Databases + - Create new database: `username_epic_travel` + - Create database user with strong password + - Grant ALL PRIVILEGES to the user + +2. **Import Database Schema** + - Go to phpMyAdmin + - Select your database + - Click "Import" tab + - Upload `database_schema.sql` + - Click "Go" + +3. **Generate Admin Password Hash** + ```bash + cd backend + python3 setup_admin.py + ``` + - Copy the generated hash + - Update the admin_users INSERT statement in the SQL file if needed + +### Step 2: Backend Setup + +1. **Upload Backend Files** + - Upload `backend/` folder to your cPanel account + - Recommended location: `~/epic-travel-api/` + +2. **Install Python Dependencies** + ```bash + cd ~/epic-travel-api + python3 -m venv venv + source venv/bin/activate + pip install -r requirements.txt + ``` + +3. **Configure Environment** + - Copy `.env.example` to `.env` + - Edit `.env` with your settings: + ```env + MYSQL_HOST=localhost + MYSQL_PORT=3306 + MYSQL_DATABASE=username_epic_travel + MYSQL_USER=username_dbuser + MYSQL_PASSWORD=your_secure_password + JWT_SECRET_KEY=your_random_256bit_key + ADMIN_DEFAULT_PASSWORD=Joker1974!!! + CORS_ORIGINS=https://yourdomain.com + ``` + +4. **Setup Python Application** + - In cPanel → Setup Python App + - Python version: 3.8+ + - Application root: `/home/username/epic-travel-api` + - Application URL: `/api` + - Application startup file: `server.py` + - Application Entry point: `app` + - Click "Create" + +5. **Install Dependencies via cPanel** + - In the Python App configuration + - Click "Run pip install" button + - Or run: `pip install -r requirements.txt` + +### Step 3: Frontend Setup + +1. **Upload Frontend Build** + - Upload contents of `frontend/build/` to your public_html + - Or to a subdomain folder + +2. **Configure .htaccess** + - Ensure `.htaccess` is present in the root + - Modify if your API is on a different path + +3. **Update API URL** + - In `public_html/static/js/main.*.js` + - Or set via environment variable during build + +### Step 4: SSL Configuration + +1. **Enable SSL** + - In cPanel → SSL/TLS + - Install Let's Encrypt certificate (free) + - Enable "Force HTTPS Redirect" + +2. **Update CORS** + - Edit backend `.env` + - Set: `CORS_ORIGINS=https://yourdomain.com` + - Restart Python application + +### Step 5: Testing + +1. **Test Backend API** + ```bash + curl https://yourdomain.com/api + ``` + Should return: `{"message": "Epic Travel API is running", "status": "healthy"}` + +2. **Test Frontend** + - Visit: https://yourdomain.com + - Should see Epic Travel homepage + +3. **Test Admin Login** + - Visit: https://yourdomain.com/admin + - Login with: + - Email: admin@epictravel.com + - Password: Joker1974!!! + +## Configuration Files + +### Backend .env +```env +# Database Configuration +MYSQL_HOST=localhost +MYSQL_PORT=3306 +MYSQL_DATABASE=username_epic_travel +MYSQL_USER=username_dbuser +MYSQL_PASSWORD=your_password + +# Security +JWT_SECRET_KEY=generate_with_openssl_rand_hex_32 +ADMIN_DEFAULT_PASSWORD=Joker1974!!! + +# CORS +CORS_ORIGINS=https://yourdomain.com +``` + +### Frontend .htaccess +```apache + + RewriteEngine On + RewriteBase / + + # API Proxy + RewriteCond %{REQUEST_URI} ^/api/(.*)$ + RewriteRule ^api/(.*)$ https://yourdomain.com/api/$1 [P,L] + + # React Router + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^ index.html [L] + +``` + +## Troubleshooting + +### Backend not starting +- Check Python version: `python3 --version` +- Check error logs in cPanel +- Verify MySQL connection details +- Ensure all dependencies installed + +### Frontend shows blank page +- Check browser console for errors +- Verify API URL in frontend build +- Check .htaccess file exists +- Clear browser cache + +### Database connection fails +- Verify MySQL credentials +- Check if database user has proper privileges +- Ensure MySQL server is running +- Check host (use 'localhost' not '127.0.0.1') + +### CORS errors +- Update CORS_ORIGINS in backend .env +- Restart Python application +- Check SSL configuration +- Ensure frontend and backend use same protocol (HTTPS) + +## Performance Optimization + +1. **Enable Gzip Compression** + - Add to .htaccess: + ```apache + + AddOutputFilterByType DEFLATE text/html text/css text/javascript application/javascript application/json + + ``` + +2. **Enable Browser Caching** + - Add to .htaccess: + ```apache + + ExpiresActive On + ExpiresByType image/jpg "access plus 1 year" + ExpiresByType image/jpeg "access plus 1 year" + ExpiresByType image/png "access plus 1 year" + ExpiresByType text/css "access plus 1 month" + ExpiresByType application/javascript "access plus 1 month" + + ``` + +3. **MySQL Optimization** + - Add indexes to frequently queried columns + - Use connection pooling + - Enable query caching + +## Maintenance + +### Updating the Application +1. Backup database: Export via phpMyAdmin +2. Backup files: Download via FTP +3. Upload new files +4. Run any database migrations +5. Restart Python application + +### Database Backup +```bash +mysqldump -u username -p username_epic_travel > backup_$(date +%Y%m%d).sql +``` + +### Monitoring +- Check error logs in cPanel +- Monitor disk space usage +- Review database size +- Check Python app status + +## Support +For issues specific to this application: +- Check logs in cPanel +- Verify all configuration settings +- Ensure MySQL connection is working +- Test API endpoints individually + +## Security Checklist +- [ ] Strong MySQL password set +- [ ] JWT secret key generated and set +- [ ] Admin password changed from default +- [ ] SSL certificate installed +- [ ] HTTPS redirect enabled +- [ ] CORS properly configured +- [ ] File permissions set correctly (644 for files, 755 for directories) +- [ ] .env file protected (not web-accessible) + +## Credits +Epic Travel & Expeditions +Contact: advisor@epictravelexpeditions.com +Phone: +1 (817) 266-2022 diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/README.txt b/cpanel_deployment/epic-travel-cpanel-20260506-034534/README.txt new file mode 100644 index 0000000..89dc5ec --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/README.txt @@ -0,0 +1,28 @@ +EPIC TRAVEL & EXPEDITIONS - cPanel Deployment Package +===================================================== + +This package contains everything needed to deploy Epic Travel & Expeditions +to a cPanel server with MySQL database. + +CONTENTS: +--------- +- backend/ Python FastAPI backend with MySQL support +- frontend/ React production build +- database_schema.sql MySQL database schema +- setup_admin.py Admin password hash generator +- INSTALLATION.md Complete installation guide + +QUICK START: +------------ +1. Read INSTALLATION.md for complete instructions +2. Create MySQL database in cPanel +3. Import database_schema.sql +4. Configure backend/.env with database credentials +5. Upload files to cPanel +6. Setup Python app in cPanel +7. Test the installation + +For detailed instructions, see INSTALLATION.md + +Contact: advisor@epictravelexpeditions.com +Phone: +1 (817) 266-2022 diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/auth.py b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/auth.py new file mode 100644 index 0000000..d2f279f --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/auth.py @@ -0,0 +1,63 @@ +from datetime import datetime, timedelta +from typing import Optional +from jose import JWTError, jwt +from passlib.context import CryptContext +from fastapi import HTTPException, Security, Depends +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +import os +from dotenv import load_dotenv +from pathlib import Path + +# Load environment variables +ROOT_DIR = Path(__file__).parent +load_dotenv(ROOT_DIR / '.env') + +# JWT Configuration +SECRET_KEY = os.environ['JWT_SECRET_KEY'] +ALGORITHM = "HS256" +ACCESS_TOKEN_EXPIRE_MINUTES = 1440 # 24 hours + +# Password hashing +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +# Security +security = HTTPBearer() + +def hash_password(password: str) -> str: + """Hash a password""" + return pwd_context.hash(password) + +def verify_password(plain_password: str, hashed_password: str) -> bool: + """Verify a password against a hash""" + return pwd_context.verify(plain_password, hashed_password) + +def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str: + """Create a JWT access token""" + to_encode = data.copy() + if expires_delta: + expire = datetime.utcnow() + expires_delta + else: + expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + + to_encode.update({"exp": expire}) + encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) + return encoded_jwt + +def decode_access_token(token: str) -> dict: + """Decode and verify a JWT token""" + try: + payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + return payload + except JWTError: + raise HTTPException(status_code=401, detail="Could not validate credentials") + +async def get_current_admin(credentials: HTTPAuthorizationCredentials = Security(security)) -> dict: + """Dependency to get current admin from JWT token""" + token = credentials.credentials + payload = decode_access_token(token) + + email: str = payload.get("sub") + if email is None: + raise HTTPException(status_code=401, detail="Invalid authentication credentials") + + return {"email": email} diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/database.py b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/database.py new file mode 100644 index 0000000..d49334a --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/database.py @@ -0,0 +1,112 @@ +""" +MySQL Database Configuration for Epic Travel & Expeditions +Uses SQLAlchemy for MySQL connection +""" +from sqlalchemy import create_engine, Column, String, Text, Numeric, DateTime, JSON, ForeignKey +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker, relationship +from datetime import datetime +import os +from dotenv import load_dotenv +from pathlib import Path + +# Load environment variables +ROOT_DIR = Path(__file__).parent +load_dotenv(ROOT_DIR / '.env') + +# MySQL Database URL +MYSQL_USER = os.environ.get('MYSQL_USER') +MYSQL_PASSWORD = os.environ.get('MYSQL_PASSWORD') +MYSQL_HOST = os.environ.get('MYSQL_HOST', 'localhost') +MYSQL_PORT = os.environ.get('MYSQL_PORT', '3306') +MYSQL_DATABASE = os.environ.get('MYSQL_DATABASE') + +DATABASE_URL = f"mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DATABASE}?charset=utf8mb4" + +# Create engine +engine = create_engine(DATABASE_URL, pool_pre_ping=True, pool_recycle=3600) + +# Create session +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# Base class for models +Base = declarative_base() + +# Database Models +class Destination(Base): + __tablename__ = "destinations" + + id = Column(String(36), primary_key=True) + name = Column(String(255), nullable=False, index=True) + location = Column(String(255), nullable=False, index=True) + description = Column(Text, nullable=False) + image = Column(String(500), nullable=False) + category = Column(String(50), nullable=False, index=True) + rating = Column(Numeric(2, 1), nullable=False, default=4.5) + price = Column(Numeric(10, 2), nullable=False) + currency = Column(String(3), nullable=False, default='USD') + created_at = Column(DateTime, nullable=False, default=datetime.utcnow) + + # Relationship + specials = relationship("Special", back_populates="destination", cascade="all, delete-orphan") + + +class Special(Base): + __tablename__ = "specials" + + id = Column(String(36), primary_key=True) + destination_id = Column(String(36), ForeignKey('destinations.id', ondelete='CASCADE'), nullable=False, index=True) + discount = Column(Numeric(5, 2), nullable=False) + end_date = Column(String(10), nullable=False) # Store as string YYYY-MM-DD + highlights = Column(JSON, nullable=False) + created_at = Column(DateTime, nullable=False, default=datetime.utcnow) + + # Relationship + destination = relationship("Destination", back_populates="specials") + + +class AdminUser(Base): + __tablename__ = "admin_users" + + id = Column(String(36), primary_key=True) + email = Column(String(255), nullable=False, unique=True, index=True) + password_hash = Column(String(255), nullable=False) + created_at = Column(DateTime, nullable=False, default=datetime.utcnow) + + +class Contact(Base): + __tablename__ = "contacts" + + id = Column(String(36), primary_key=True) + name = Column(String(255), nullable=False) + email = Column(String(255), nullable=False) + message = Column(Text, nullable=False) + created_at = Column(DateTime, nullable=False, default=datetime.utcnow, index=True) + + +class NewsletterSubscriber(Base): + __tablename__ = "newsletter_subscribers" + + id = Column(String(36), primary_key=True) + email = Column(String(255), nullable=False, unique=True, index=True) + subscribed_at = Column(DateTime, nullable=False, default=datetime.utcnow) + + +# Dependency to get database session +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + +# Create all tables (if they don't exist) +def create_tables(): + Base.metadata.create_all(bind=engine) + + +if __name__ == "__main__": + print("Creating database tables...") + create_tables() + print("Database tables created successfully!") diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/models/__init__.py b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/models/__init__.py new file mode 100644 index 0000000..e2313c5 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/models/__init__.py @@ -0,0 +1 @@ +# Models module diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/models/schemas.py b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/models/schemas.py new file mode 100644 index 0000000..b1ab11c --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/models/schemas.py @@ -0,0 +1,85 @@ +from pydantic import BaseModel, Field +from typing import Optional, List +from datetime import datetime +import uuid + +class Destination(BaseModel): + id: str = Field(default_factory=lambda: str(uuid.uuid4())) + name: str + location: str + description: str + image: str + category: str # City, Beach, Adventure + rating: float + price: float + currency: str = "USD" + created_at: datetime = Field(default_factory=datetime.utcnow) + +class DestinationCreate(BaseModel): + name: str + location: str + description: str + image: str + category: str + rating: float + price: float + currency: str = "USD" + +class DestinationUpdate(BaseModel): + name: Optional[str] = None + location: Optional[str] = None + description: Optional[str] = None + image: Optional[str] = None + category: Optional[str] = None + rating: Optional[float] = None + price: Optional[float] = None + currency: Optional[str] = None + +class Special(BaseModel): + id: str = Field(default_factory=lambda: str(uuid.uuid4())) + destination_id: str + discount: float + end_date: str # ISO format date + highlights: List[str] + created_at: datetime = Field(default_factory=datetime.utcnow) + +class SpecialCreate(BaseModel): + destination_id: str + discount: float + end_date: str + highlights: List[str] + +class SpecialUpdate(BaseModel): + discount: Optional[float] = None + end_date: Optional[str] = None + highlights: Optional[List[str]] = None + +class AdminUser(BaseModel): + id: str = Field(default_factory=lambda: str(uuid.uuid4())) + email: str + password_hash: str + created_at: datetime = Field(default_factory=datetime.utcnow) + +class AdminLogin(BaseModel): + email: str + password: str + +class Contact(BaseModel): + id: str = Field(default_factory=lambda: str(uuid.uuid4())) + name: str + email: str + message: str + created_at: datetime = Field(default_factory=datetime.utcnow) + +class ContactCreate(BaseModel): + name: str + email: str + message: str + +class NewsletterSubscriber(BaseModel): + id: str = Field(default_factory=lambda: str(uuid.uuid4())) + email: str + subscribed_at: datetime = Field(default_factory=datetime.utcnow) + +class NewsletterSubscribe(BaseModel): + email: str diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/requirements.txt b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/requirements.txt new file mode 100644 index 0000000..82f2591 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/requirements.txt @@ -0,0 +1,18 @@ +fastapi==0.110.1 +uvicorn==0.25.0 +python-dotenv>=1.0.1 +pydantic>=2.6.4 +email-validator>=2.2.0 +pyjwt>=2.10.1 +passlib>=1.7.4 +python-jose>=3.3.0 +python-multipart>=0.0.9 +requests>=2.31.0 + +# MySQL Database +PyMySQL>=1.1.0 +SQLAlchemy>=2.0.23 +cryptography>=42.0.5 + +# For bcrypt password hashing +bcrypt>=4.1.2 diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/__init__.py b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/__init__.py new file mode 100644 index 0000000..1102393 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/__init__.py @@ -0,0 +1 @@ +# Routes module diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/auth_routes.py b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/auth_routes.py new file mode 100644 index 0000000..349a928 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/auth_routes.py @@ -0,0 +1,61 @@ +from fastapi import APIRouter, HTTPException, Depends +from models.schemas import AdminLogin +from auth import hash_password, verify_password, create_access_token +from motor.motor_asyncio import AsyncIOMotorClient +import os + +router = APIRouter(prefix="/api/auth", tags=["Authentication"]) + +# MongoDB connection will be injected +db = None + +def set_db(database): + global db + db = database + +@router.post("/login") +async def login(credentials: AdminLogin): + """Admin login endpoint""" + # Find admin user + admin = await db.admin_users.find_one({"email": credentials.email}) + + if not admin: + raise HTTPException(status_code=401, detail="Invalid email or password") + + # Verify password + if not verify_password(credentials.password, admin["password_hash"]): + raise HTTPException(status_code=401, detail="Invalid email or password") + + # Create access token + access_token = create_access_token(data={"sub": admin["email"]}) + + return { + "access_token": access_token, + "token_type": "bearer", + "email": admin["email"] + } + +@router.post("/verify") +async def verify_token(admin: dict = Depends(lambda: __import__('auth').get_current_admin)): + """Verify JWT token""" + return {"valid": True, "email": admin["email"]} + +@router.post("/initialize-admin") +async def initialize_admin(): + """Initialize default admin user (for development/setup only)""" + # Check if admin already exists + existing_admin = await db.admin_users.find_one({"email": "admin@epictravel.com"}) + + if existing_admin: + return {"message": "Admin user already exists"} + + # Create default admin + admin_data = { + "email": "admin@epictravel.com", + "password_hash": hash_password("admin123"), + "created_at": __import__('datetime').datetime.utcnow() + } + + await db.admin_users.insert_one(admin_data) + + return {"message": "Admin user created successfully", "email": "admin@epictravel.com"} diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/destination_routes.py b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/destination_routes.py new file mode 100644 index 0000000..fe08919 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/destination_routes.py @@ -0,0 +1,113 @@ +from fastapi import APIRouter, HTTPException, Depends +from typing import List, Optional +from models.schemas import Destination, DestinationCreate, DestinationUpdate +from auth import get_current_admin +import uuid +from datetime import datetime + +router = APIRouter(prefix="/api/destinations", tags=["Destinations"]) + +# MongoDB connection will be injected +db = None + +def set_db(database): + global db + db = database + +@router.get("", response_model=List[Destination]) +async def get_destinations(category: Optional[str] = None, search: Optional[str] = None): + """Get all destinations with optional filtering""" + query = {} + + if category and category != "All": + query["category"] = category + + if search: + query["$or"] = [ + {"name": {"$regex": search, "$options": "i"}}, + {"location": {"$regex": search, "$options": "i"}} + ] + + destinations = await db.destinations.find(query, {'_id': 0}).limit(100).to_list(100) + + # Convert MongoDB _id to id for response + for dest in destinations: + if "_id" in dest: + del dest["_id"] + + return destinations + +@router.get("/{destination_id}", response_model=Destination) +async def get_destination(destination_id: str): + """Get a single destination by ID""" + destination = await db.destinations.find_one({"id": destination_id}) + + if not destination: + raise HTTPException(status_code=404, detail="Destination not found") + + if "_id" in destination: + del destination["_id"] + + return destination + +@router.post("", response_model=Destination) +async def create_destination( + destination: DestinationCreate, + admin: dict = Depends(get_current_admin) +): + """Create a new destination (admin only)""" + destination_data = destination.dict() + destination_data["id"] = str(uuid.uuid4()) + destination_data["created_at"] = datetime.utcnow() + + await db.destinations.insert_one(destination_data) + + if "_id" in destination_data: + del destination_data["_id"] + + return destination_data + +@router.put("/{destination_id}", response_model=Destination) +async def update_destination( + destination_id: str, + destination_update: DestinationUpdate, + admin: dict = Depends(get_current_admin) +): + """Update a destination (admin only)""" + # Check if destination exists + existing = await db.destinations.find_one({"id": destination_id}) + if not existing: + raise HTTPException(status_code=404, detail="Destination not found") + + # Update only provided fields + update_data = {k: v for k, v in destination_update.dict().items() if v is not None} + + if update_data: + await db.destinations.update_one( + {"id": destination_id}, + {"$set": update_data} + ) + + # Fetch updated destination + updated = await db.destinations.find_one({"id": destination_id}) + + if "_id" in updated: + del updated["_id"] + + return updated + +@router.delete("/{destination_id}") +async def delete_destination( + destination_id: str, + admin: dict = Depends(get_current_admin) +): + """Delete a destination (admin only)""" + result = await db.destinations.delete_one({"id": destination_id}) + + if result.deleted_count == 0: + raise HTTPException(status_code=404, detail="Destination not found") + + # Also delete any specials for this destination + await db.specials.delete_many({"destination_id": destination_id}) + + return {"message": "Destination deleted successfully"} diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/other_routes.py b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/other_routes.py new file mode 100644 index 0000000..df67559 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/other_routes.py @@ -0,0 +1,82 @@ +from fastapi import APIRouter, HTTPException, File, UploadFile +from fastapi.responses import FileResponse +from typing import List +from models.schemas import ContactCreate, NewsletterSubscribe +from datetime import datetime +from pathlib import Path +import uuid +import os +import shutil + +router = APIRouter(prefix="/api", tags=["Other"]) + +# MongoDB connection will be injected +db = None + +# Upload directory setup +UPLOAD_DIR = Path(__file__).parent.parent / 'uploads' +UPLOAD_DIR.mkdir(exist_ok=True) + +def set_db(database): + global db + db = database + +@router.post("/contact") +async def submit_contact(contact: ContactCreate): + """Submit a contact form""" + contact_data = contact.dict() + contact_data["id"] = str(uuid.uuid4()) + contact_data["created_at"] = datetime.utcnow() + + await db.contacts.insert_one(contact_data) + + return {"message": "Contact form submitted successfully"} + +@router.post("/newsletter/subscribe") +async def subscribe_newsletter(subscriber: NewsletterSubscribe): + """Subscribe to newsletter""" + # Check if already subscribed + existing = await db.newsletter_subscribers.find_one({"email": subscriber.email}) + if existing: + return {"message": "Email already subscribed"} + + subscriber_data = subscriber.dict() + subscriber_data["id"] = str(uuid.uuid4()) + subscriber_data["subscribed_at"] = datetime.utcnow() + + await db.newsletter_subscribers.insert_one(subscriber_data) + + return {"message": "Successfully subscribed to newsletter"} + +@router.post("/upload/image") +async def upload_image(file: UploadFile = File(...)): + """Upload an image file""" + # Validate file type + allowed_extensions = [".jpg", ".jpeg", ".png", ".webp"] + file_ext = os.path.splitext(file.filename)[1].lower() + + if file_ext not in allowed_extensions: + raise HTTPException(status_code=400, detail="Invalid file type. Allowed: jpg, jpeg, png, webp") + + # Generate unique filename + unique_filename = f"{uuid.uuid4()}{file_ext}" + file_path = UPLOAD_DIR / unique_filename + + # Save file + with open(file_path, "wb") as buffer: + shutil.copyfileobj(file.file, buffer) + + # Return URL + file_url = f"/api/uploads/{unique_filename}" + + return {"url": file_url, "filename": unique_filename} + +@router.get("/uploads/{filename}") +async def get_uploaded_image(filename: str): + """Serve uploaded images""" + file_path = UPLOAD_DIR / filename + + if not file_path.exists(): + raise HTTPException(status_code=404, detail="Image not found") + + return FileResponse(str(file_path)) diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/special_routes.py b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/special_routes.py new file mode 100644 index 0000000..8116e91 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/routes/special_routes.py @@ -0,0 +1,109 @@ +from fastapi import APIRouter, HTTPException, Depends +from typing import List +from models.schemas import Special, SpecialCreate, SpecialUpdate +from auth import get_current_admin +import uuid +from datetime import datetime + +router = APIRouter(prefix="/api/specials", tags=["Specials"]) + +# MongoDB connection will be injected +db = None + +def set_db(database): + global db + db = database + +@router.get("", response_model=List[Special]) +async def get_specials(): + """Get all weekly specials""" + specials = await db.specials.find({}, {'_id': 0}).limit(100).to_list(100) + + # Convert MongoDB _id to id for response + for special in specials: + if "_id" in special: + del special["_id"] + + return specials + +@router.get("/{special_id}", response_model=Special) +async def get_special(special_id: str): + """Get a single special by ID""" + special = await db.specials.find_one({"id": special_id}) + + if not special: + raise HTTPException(status_code=404, detail="Special not found") + + if "_id" in special: + del special["_id"] + + return special + +@router.post("", response_model=Special) +async def create_special( + special: SpecialCreate, + admin: dict = Depends(get_current_admin) +): + """Add a destination to specials (admin only)""" + # Check if destination exists + destination = await db.destinations.find_one({"id": special.destination_id}) + if not destination: + raise HTTPException(status_code=404, detail="Destination not found") + + # Check if special already exists for this destination + existing = await db.specials.find_one({"destination_id": special.destination_id}) + if existing: + raise HTTPException(status_code=400, detail="Special already exists for this destination") + + special_data = special.dict() + special_data["id"] = str(uuid.uuid4()) + special_data["created_at"] = datetime.utcnow() + + await db.specials.insert_one(special_data) + + if "_id" in special_data: + del special_data["_id"] + + return special_data + +@router.put("/{special_id}", response_model=Special) +async def update_special( + special_id: str, + special_update: SpecialUpdate, + admin: dict = Depends(get_current_admin) +): + """Update a special (admin only)""" + # Check if special exists + existing = await db.specials.find_one({"id": special_id}) + if not existing: + raise HTTPException(status_code=404, detail="Special not found") + + # Update only provided fields + update_data = {k: v for k, v in special_update.dict().items() if v is not None} + + if update_data: + await db.specials.update_one( + {"id": special_id}, + {"$set": update_data} + ) + + # Fetch updated special + updated = await db.specials.find_one({"id": special_id}) + + if "_id" in updated: + del updated["_id"] + + return updated + +@router.delete("/destination/{destination_id}") +async def delete_special_by_destination( + destination_id: str, + admin: dict = Depends(get_current_admin) +): + """Remove a destination from specials (admin only)""" + result = await db.specials.delete_one({"destination_id": destination_id}) + + if result.deleted_count == 0: + raise HTTPException(status_code=404, detail="Special not found for this destination") + + return {"message": "Special removed successfully"} diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/server.py b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/server.py new file mode 100644 index 0000000..78518c8 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/server.py @@ -0,0 +1,261 @@ +from fastapi import FastAPI +from dotenv import load_dotenv +from starlette.middleware.cors import CORSMiddleware +from motor.motor_asyncio import AsyncIOMotorClient +import os +import logging +from pathlib import Path +from auth import hash_password +from datetime import datetime + +# Import route modules +from routes import auth_routes, destination_routes, special_routes, other_routes + +ROOT_DIR = Path(__file__).parent +load_dotenv(ROOT_DIR / '.env') + +# MongoDB connection +mongo_url = os.environ['MONGO_URL'] +client = AsyncIOMotorClient(mongo_url) +db = client[os.environ['DB_NAME']] + +# Inject database into route modules +auth_routes.set_db(db) +destination_routes.set_db(db) +special_routes.set_db(db) +other_routes.set_db(db) + +# Create the main app +app = FastAPI(title="Epic Travel & Destinations API") + +# Include routers +app.include_router(auth_routes.router) +app.include_router(destination_routes.router) +app.include_router(special_routes.router) +app.include_router(other_routes.router) + +# Health check endpoint +@app.get("/api") +async def root(): + return {"message": "Epic Travel API is running", "status": "healthy"} + +app.add_middleware( + CORSMiddleware, + allow_credentials=True, + allow_origins=["*"], + allow_methods=["*"], + allow_headers=["*"], +) + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +@app.on_event("startup") +async def startup_db_client(): + """Initialize database with seed data if empty""" + try: + # Check if admin user exists, if not create one + admin_exists = await db.admin_users.find_one({"email": "admin@epictravel.com"}) + if not admin_exists: + admin_data = { + "id": "admin-1", + "email": "admin@epictravel.com", + "password_hash": hash_password(os.environ['ADMIN_DEFAULT_PASSWORD']), + "created_at": datetime.utcnow() + } + await db.admin_users.insert_one(admin_data) + logger.info("Default admin user created") + + # Check if destinations exist, if not seed initial data + dest_count = await db.destinations.count_documents({}) + if dest_count == 0: + # Seed initial destinations + initial_destinations = [ + { + "id": "1", + "name": "Paris", + "location": "France", + "description": "Experience the romance and elegance of the City of Light. Visit iconic landmarks like the Eiffel Tower, Louvre Museum, and stroll along the Champs-Élysées.", + "image": "https://images.unsplash.com/photo-1502602898657-3e91760cbb34?w=800&q=80", + "category": "City", + "rating": 4.9, + "price": 1299, + "currency": "USD", + "created_at": datetime.utcnow() + }, + { + "id": "2", + "name": "Bali", + "location": "Indonesia", + "description": "Discover tropical paradise with stunning beaches, ancient temples, lush rice terraces, and vibrant culture in this Indonesian gem.", + "image": "https://images.unsplash.com/photo-1537996194471-e657df975ab4?w=800&q=80", + "category": "Beach", + "rating": 4.8, + "price": 899, + "currency": "USD", + "created_at": datetime.utcnow() + }, + { + "id": "3", + "name": "Tokyo", + "location": "Japan", + "description": "Immerse yourself in the perfect blend of ancient tradition and cutting-edge technology in Japan's bustling capital city.", + "image": "https://images.unsplash.com/photo-1540959733332-eab4deabeeaf?w=800&q=80", + "category": "City", + "rating": 4.9, + "price": 1499, + "currency": "USD", + "created_at": datetime.utcnow() + }, + { + "id": "4", + "name": "Santorini", + "location": "Greece", + "description": "Marvel at breathtaking sunsets, whitewashed buildings, and crystal-clear waters in this stunning Greek island paradise.", + "image": "https://images.unsplash.com/photo-1613395877344-13d4a8e0d49e?w=800&q=80", + "category": "Beach", + "rating": 4.9, + "price": 1199, + "currency": "USD", + "created_at": datetime.utcnow() + }, + { + "id": "5", + "name": "Iceland", + "location": "Iceland", + "description": "Witness the Northern Lights, explore glaciers, geysers, and volcanic landscapes in this land of fire and ice.", + "image": "https://images.unsplash.com/photo-1504829857797-ddff29c27927?w=800&q=80", + "category": "Adventure", + "rating": 4.8, + "price": 1699, + "currency": "USD", + "created_at": datetime.utcnow() + }, + { + "id": "6", + "name": "Dubai", + "location": "UAE", + "description": "Experience luxury and innovation in the desert with world-class shopping, stunning architecture, and endless entertainment.", + "image": "https://images.unsplash.com/photo-1512453979798-5ea266f8880c?w=800&q=80", + "category": "City", + "rating": 4.7, + "price": 1399, + "currency": "USD", + "created_at": datetime.utcnow() + }, + { + "id": "7", + "name": "Maldives", + "location": "Maldives", + "description": "Relax in overwater bungalows, dive in pristine coral reefs, and enjoy the ultimate tropical island getaway.", + "image": "https://images.unsplash.com/photo-1514282401047-d79a71a590e8?w=800&q=80", + "category": "Beach", + "rating": 5.0, + "price": 2199, + "currency": "USD", + "created_at": datetime.utcnow() + }, + { + "id": "8", + "name": "New York", + "location": "USA", + "description": "Explore the city that never sleeps with iconic landmarks, world-class museums, Broadway shows, and diverse neighborhoods.", + "image": "https://images.unsplash.com/photo-1496442226666-8d4d0e62e6e9?w=800&q=80", + "category": "City", + "rating": 4.8, + "price": 1099, + "currency": "USD", + "created_at": datetime.utcnow() + }, + { + "id": "9", + "name": "Machu Picchu", + "location": "Peru", + "description": "Trek to the ancient Incan citadel nestled high in the Andes Mountains, one of the New Seven Wonders of the World.", + "image": "https://images.unsplash.com/photo-1587595431973-160d0d94add1?w=800&q=80", + "category": "Adventure", + "rating": 4.9, + "price": 1299, + "currency": "USD", + "created_at": datetime.utcnow() + }, + { + "id": "10", + "name": "Swiss Alps", + "location": "Switzerland", + "description": "Ski pristine slopes, hike mountain trails, and enjoy charming alpine villages with breathtaking mountain vistas.", + "image": "https://images.unsplash.com/photo-1531366936337-7c912a4589a7?w=800&q=80", + "category": "Adventure", + "rating": 4.9, + "price": 1799, + "currency": "USD", + "created_at": datetime.utcnow() + }, + { + "id": "11", + "name": "Venice", + "location": "Italy", + "description": "Glide through romantic canals, admire Renaissance architecture, and savor authentic Italian cuisine in this unique floating city.", + "image": "https://images.unsplash.com/photo-1523906834658-6e24ef2386f9?w=800&q=80", + "category": "City", + "rating": 4.8, + "price": 1149, + "currency": "USD", + "created_at": datetime.utcnow() + }, + { + "id": "12", + "name": "Safari Kenya", + "location": "Kenya", + "description": "Witness the Great Migration, spot the Big Five, and experience the raw beauty of African wilderness.", + "image": "https://images.unsplash.com/photo-1516426122078-c23e76319801?w=800&q=80", + "category": "Adventure", + "rating": 4.9, + "price": 2499, + "currency": "USD", + "created_at": datetime.utcnow() + } + ] + await db.destinations.insert_many(initial_destinations) + logger.info(f"Seeded {len(initial_destinations)} initial destinations") + + # Seed initial specials + initial_specials = [ + { + "id": "special-1", + "destination_id": "2", + "discount": 25, + "end_date": "2025-02-28", + "highlights": ["Free spa treatment", "Complimentary airport transfer", "Sunset dinner cruise"], + "created_at": datetime.utcnow() + }, + { + "id": "special-2", + "destination_id": "4", + "discount": 30, + "end_date": "2025-03-15", + "highlights": ["Wine tasting tour", "Private yacht excursion", "Luxury accommodation upgrade"], + "created_at": datetime.utcnow() + }, + { + "id": "special-3", + "destination_id": "7", + "discount": 20, + "end_date": "2025-02-20", + "highlights": ["Snorkeling adventure", "Couples massage", "Romantic beach dinner"], + "created_at": datetime.utcnow() + } + ] + await db.specials.insert_many(initial_specials) + logger.info(f"Seeded {len(initial_specials)} initial specials") + + except Exception as e: + logger.error(f"Error during startup: {str(e)}") + +@app.on_event("shutdown") +async def shutdown_db_client(): + client.close() diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/update_admin_password.py b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/update_admin_password.py new file mode 100644 index 0000000..87b2dcb --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/backend/update_admin_password.py @@ -0,0 +1,41 @@ +import asyncio +from motor.motor_asyncio import AsyncIOMotorClient +from passlib.context import CryptContext +import os +from dotenv import load_dotenv +from pathlib import Path + +# Load environment variables +ROOT_DIR = Path(__file__).parent +load_dotenv(ROOT_DIR / '.env') + +# Password hashing +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +async def update_admin_password(): + # Connect to MongoDB + mongo_url = os.environ['MONGO_URL'] + client = AsyncIOMotorClient(mongo_url) + db = client[os.environ['DB_NAME']] + + # New password + new_password = "Joker1974!!!" + new_password_hash = pwd_context.hash(new_password) + + # Update admin password + result = await db.admin_users.update_one( + {"email": "admin@epictravel.com"}, + {"$set": {"password_hash": new_password_hash}} + ) + + if result.modified_count > 0: + print(f"✓ Admin password updated successfully!") + print(f"✓ Email: admin@epictravel.com") + print(f"✓ New Password: {new_password}") + else: + print("✗ Failed to update password or admin user not found") + + client.close() + +if __name__ == "__main__": + asyncio.run(update_admin_password()) diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/database_schema.sql b/cpanel_deployment/epic-travel-cpanel-20260506-034534/database_schema.sql new file mode 100644 index 0000000..75dd305 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/database_schema.sql @@ -0,0 +1,95 @@ +-- Epic Travel & Expeditions Database Schema for MySQL +-- Run this script to create the database structure + +CREATE DATABASE IF NOT EXISTS epic_travel CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +USE epic_travel; + +-- Destinations Table +CREATE TABLE IF NOT EXISTS destinations ( + id VARCHAR(36) PRIMARY KEY, + name VARCHAR(255) NOT NULL, + location VARCHAR(255) NOT NULL, + description TEXT NOT NULL, + image VARCHAR(500) NOT NULL, + category VARCHAR(50) NOT NULL, + rating DECIMAL(2,1) NOT NULL DEFAULT 4.5, + price DECIMAL(10,2) NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'USD', + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_category (category), + INDEX idx_name (name), + INDEX idx_location (location) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Specials Table +CREATE TABLE IF NOT EXISTS specials ( + id VARCHAR(36) PRIMARY KEY, + destination_id VARCHAR(36) NOT NULL, + discount DECIMAL(5,2) NOT NULL, + end_date DATE NOT NULL, + highlights JSON NOT NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (destination_id) REFERENCES destinations(id) ON DELETE CASCADE, + INDEX idx_destination (destination_id), + INDEX idx_end_date (end_date) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Admin Users Table +CREATE TABLE IF NOT EXISTS admin_users ( + id VARCHAR(36) PRIMARY KEY, + email VARCHAR(255) NOT NULL UNIQUE, + password_hash VARCHAR(255) NOT NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_email (email) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Contacts Table +CREATE TABLE IF NOT EXISTS contacts ( + id VARCHAR(36) PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, + message TEXT NOT NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_created_at (created_at) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Newsletter Subscribers Table +CREATE TABLE IF NOT EXISTS newsletter_subscribers ( + id VARCHAR(36) PRIMARY KEY, + email VARCHAR(255) NOT NULL UNIQUE, + subscribed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + INDEX idx_email (email) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +-- Insert default admin user (password: Joker1974!!!) +-- Note: Replace the password_hash with the actual bcrypt hash +INSERT INTO admin_users (id, email, password_hash, created_at) +VALUES ( + 'admin-1', + 'admin@epictravel.com', + '$2b$12$PLACEHOLDER_HASH_WILL_BE_GENERATED', + NOW() +) ON DUPLICATE KEY UPDATE email=email; + +-- Insert sample destinations +INSERT INTO destinations (id, name, location, description, image, category, rating, price, currency) VALUES +('1', 'Paris', 'France', 'Experience the romance and elegance of the City of Light. Visit iconic landmarks like the Eiffel Tower, Louvre Museum, and stroll along the Champs-Élysées.', 'https://images.unsplash.com/photo-1502602898657-3e91760cbb34?w=800&q=80', 'City', 4.9, 1299, 'USD'), +('2', 'Bali', 'Indonesia', 'Discover tropical paradise with stunning beaches, ancient temples, lush rice terraces, and vibrant culture in this Indonesian gem.', 'https://images.unsplash.com/photo-1537996194471-e657df975ab4?w=800&q=80', 'Beach', 4.8, 899, 'USD'), +('3', 'Tokyo', 'Japan', 'Immerse yourself in the perfect blend of ancient tradition and cutting-edge technology in Japan\'s bustling capital city.', 'https://images.unsplash.com/photo-1540959733332-eab4deabeeaf?w=800&q=80', 'City', 4.9, 1499, 'USD'), +('4', 'Santorini', 'Greece', 'Marvel at breathtaking sunsets, whitewashed buildings, and crystal-clear waters in this stunning Greek island paradise.', 'https://images.unsplash.com/photo-1613395877344-13d4a8e0d49e?w=800&q=80', 'Beach', 4.9, 1199, 'USD'), +('5', 'Iceland', 'Iceland', 'Witness the Northern Lights, explore glaciers, geysers, and volcanic landscapes in this land of fire and ice.', 'https://images.unsplash.com/photo-1504829857797-ddff29c27927?w=800&q=80', 'Adventure', 4.8, 1699, 'USD'), +('6', 'Dubai', 'UAE', 'Experience luxury and innovation in the desert with world-class shopping, stunning architecture, and endless entertainment.', 'https://images.unsplash.com/photo-1512453979798-5ea266f8880c?w=800&q=80', 'City', 4.7, 1399, 'USD'), +('7', 'Maldives', 'Maldives', 'Relax in overwater bungalows, dive in pristine coral reefs, and enjoy the ultimate tropical island getaway.', 'https://images.unsplash.com/photo-1514282401047-d79a71a590e8?w=800&q=80', 'Beach', 5.0, 2199, 'USD'), +('8', 'New York', 'USA', 'Explore the city that never sleeps with iconic landmarks, world-class museums, Broadway shows, and diverse neighborhoods.', 'https://images.unsplash.com/photo-1496442226666-8d4d0e62e6e9?w=800&q=80', 'City', 4.8, 1099, 'USD'), +('9', 'Machu Picchu', 'Peru', 'Trek to the ancient Incan citadel nestled high in the Andes Mountains, one of the New Seven Wonders of the World.', 'https://images.unsplash.com/photo-1587595431973-160d0d94add1?w=800&q=80', 'Adventure', 4.9, 1299, 'USD'), +('10', 'Swiss Alps', 'Switzerland', 'Ski pristine slopes, hike mountain trails, and enjoy charming alpine villages with breathtaking mountain vistas.', 'https://images.unsplash.com/photo-1531366936337-7c912a4589a7?w=800&q=80', 'Adventure', 4.9, 1799, 'USD'), +('11', 'Venice', 'Italy', 'Glide through romantic canals, admire Renaissance architecture, and savor authentic Italian cuisine in this unique floating city.', 'https://images.unsplash.com/photo-1523906834658-6e24ef2386f9?w=800&q=80', 'City', 4.8, 1149, 'USD'), +('12', 'Safari Kenya', 'Kenya', 'Witness the Great Migration, spot the Big Five, and experience the raw beauty of African wilderness.', 'https://images.unsplash.com/photo-1516426122078-c23e76319801?w=800&q=80', 'Adventure', 4.9, 2499, 'USD') +ON DUPLICATE KEY UPDATE name=name; + +-- Insert sample specials +INSERT INTO specials (id, destination_id, discount, end_date, highlights) VALUES +('special-1', '2', 25, DATE_ADD(CURDATE(), INTERVAL 30 DAY), JSON_ARRAY('Free spa treatment', 'Complimentary airport transfer', 'Sunset dinner cruise')), +('special-2', '4', 30, DATE_ADD(CURDATE(), INTERVAL 45 DAY), JSON_ARRAY('Wine tasting tour', 'Private yacht excursion', 'Luxury accommodation upgrade')), +('special-3', '7', 20, DATE_ADD(CURDATE(), INTERVAL 20 DAY), JSON_ARRAY('Snorkeling adventure', 'Couples massage', 'Romantic beach dinner')) +ON DUPLICATE KEY UPDATE discount=discount; diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/.htaccess b/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/.htaccess new file mode 100644 index 0000000..f4e343b --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/.htaccess @@ -0,0 +1,50 @@ + + RewriteEngine On + RewriteBase / + + # Force HTTPS + RewriteCond %{HTTPS} off + RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] + + # API Proxy - Forward /api requests to Python backend + # Update the proxy URL to match your cPanel Python app configuration + RewriteCond %{REQUEST_URI} ^/api/(.*)$ + RewriteRule ^api/(.*)$ http://localhost:YOUR_PYTHON_APP_PORT/api/$1 [P,L] + + # React Router - Serve index.html for all non-file requests + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-l + RewriteRule ^ index.html [L] + + +# Enable Gzip Compression + + AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json + + +# Browser Caching + + ExpiresActive On + ExpiresByType image/jpg "access plus 1 year" + ExpiresByType image/jpeg "access plus 1 year" + ExpiresByType image/png "access plus 1 year" + ExpiresByType image/gif "access plus 1 year" + ExpiresByType image/webp "access plus 1 year" + ExpiresByType text/css "access plus 1 month" + ExpiresByType application/javascript "access plus 1 month" + ExpiresByType application/json "access plus 1 week" + + +# Security Headers + + Header set X-Content-Type-Options "nosniff" + Header set X-Frame-Options "SAMEORIGIN" + Header set X-XSS-Protection "1; mode=block" + + +# Protect .env file + + Order allow,deny + Deny from all + diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/asset-manifest.json b/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/asset-manifest.json new file mode 100644 index 0000000..ed3dead --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/asset-manifest.json @@ -0,0 +1,13 @@ +{ + "files": { + "main.css": "/static/css/main.7791f349.css", + "main.js": "/static/js/main.df6117d1.js", + "index.html": "/index.html", + "main.7791f349.css.map": "/static/css/main.7791f349.css.map", + "main.df6117d1.js.map": "/static/js/main.df6117d1.js.map" + }, + "entrypoints": [ + "static/css/main.7791f349.css", + "static/js/main.df6117d1.js" + ] +} \ No newline at end of file diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/index.html b/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/index.html new file mode 100644 index 0000000..c777e65 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/index.html @@ -0,0 +1 @@ +Emergent | Fullstack App

Made with Emergent

\ No newline at end of file diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/static/css/main.7791f349.css b/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/static/css/main.7791f349.css new file mode 100644 index 0000000..ec56952 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/static/css/main.7791f349.css @@ -0,0 +1,4 @@ +*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/* +! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com +*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{-webkit-text-size-adjust:100%;font-feature-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-variation-settings:normal;line-height:1.5;tab-size:4}body{line-height:inherit}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-feature-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-feature-settings:inherit;color:inherit;font-family:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}:root{--background:0 0% 100%;--foreground:0 0% 3.9%;--card:0 0% 100%;--card-foreground:0 0% 3.9%;--popover:0 0% 100%;--popover-foreground:0 0% 3.9%;--primary:0 0% 9%;--primary-foreground:0 0% 98%;--secondary:0 0% 96.1%;--secondary-foreground:0 0% 9%;--muted:0 0% 96.1%;--muted-foreground:0 0% 45.1%;--accent:0 0% 96.1%;--accent-foreground:0 0% 9%;--destructive:0 84.2% 60.2%;--destructive-foreground:0 0% 98%;--border:0 0% 89.8%;--input:0 0% 89.8%;--ring:0 0% 3.9%;--chart-1:12 76% 61%;--chart-2:173 58% 39%;--chart-3:197 37% 24%;--chart-4:43 74% 66%;--chart-5:27 87% 67%;--radius:0.5rem}*{border-color:#e5e5e5;border-color:hsl(var(--border))}body{background-color:#fff;background-color:hsl(var(--background));color:#0a0a0a;color:hsl(var(--foreground))}[data-debug-wrapper=true]{display:contents!important}[data-debug-wrapper=true]>*{border:inherit;column-gap:inherit;gap:inherit;margin:inherit;padding:inherit;row-gap:inherit}.sr-only{clip:rect(0,0,0,0);border-width:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.invisible{visibility:hidden}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.inset-x-0{left:0;right:0}.inset-y-0{bottom:0;top:0}.-bottom-12{bottom:-3rem}.-left-12{left:-3rem}.-right-12{right:-3rem}.-top-12{top:-3rem}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.left-0{left:0}.left-1{left:.25rem}.left-1\/2{left:50%}.left-2{left:.5rem}.left-3{left:.75rem}.left-4{left:1rem}.left-\[50\%\]{left:50%}.right-0{right:0}.right-1{right:.25rem}.right-2{right:.5rem}.right-4{right:1rem}.top-0{top:0}.top-1{top:.25rem}.top-1\/2{top:50%}.top-2{top:.5rem}.top-4{top:1rem}.top-\[1px\]{top:1px}.top-\[50\%\]{top:50%}.top-\[60\%\]{top:60%}.top-full{top:100%}.z-0{z-index:0}.z-10{z-index:10}.z-50{z-index:50}.z-\[100\]{z-index:100}.z-\[1\]{z-index:1}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-bottom:.25rem;margin-top:.25rem}.-ml-4{margin-left:-1rem}.-mt-4{margin-top:-1rem}.mb-1{margin-bottom:.25rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-4{margin-right:1rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-24{margin-top:6rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.line-clamp-2{-webkit-line-clamp:2}.line-clamp-2,.line-clamp-3{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.line-clamp-3{-webkit-line-clamp:3}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1/1}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[1px\]{height:1px}.h-\[var\(--radix-navigation-menu-viewport-height\)\]{height:var(--radix-navigation-menu-viewport-height)}.h-\[var\(--radix-select-trigger-height\)\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-\[--radix-context-menu-content-available-height\]{max-height:var(--radix-context-menu-content-available-height)}.max-h-\[--radix-select-content-available-height\]{max-height:var(--radix-select-content-available-height)}.max-h-\[300px\]{max-height:300px}.max-h-\[90vh\]{max-height:90vh}.max-h-\[var\(--radix-dropdown-menu-content-available-height\)\]{max-height:var(--radix-dropdown-menu-content-available-height)}.max-h-screen{max-height:100vh}.min-h-\[60px\]{min-height:60px}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[100px\]{width:100px}.w-\[1px\]{width:1px}.w-full{width:100%}.w-max{width:-webkit-max-content;width:max-content}.w-px{width:1px}.min-w-0{min-width:0}.min-w-10{min-width:2.5rem}.min-w-8{min-width:2rem}.min-w-9{min-width:2.25rem}.min-w-\[12rem\]{min-width:12rem}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-7xl{max-width:80rem}.max-w-lg{max-width:32rem}.max-w-max{max-width:-webkit-max-content;max-width:max-content}.max-w-md{max-width:28rem}.flex-1{flex:1 1}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow{flex-grow:1}.grow-0{flex-grow:0}.basis-full{flex-basis:100%}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.origin-\[--radix-context-menu-content-transform-origin\]{transform-origin:var(--radix-context-menu-content-transform-origin)}.origin-\[--radix-dropdown-menu-content-transform-origin\]{transform-origin:var(--radix-dropdown-menu-content-transform-origin)}.origin-\[--radix-hover-card-content-transform-origin\]{transform-origin:var(--radix-hover-card-content-transform-origin)}.origin-\[--radix-menubar-content-transform-origin\]{transform-origin:var(--radix-menubar-content-transform-origin)}.origin-\[--radix-popover-content-transform-origin\]{transform-origin:var(--radix-popover-content-transform-origin)}.origin-\[--radix-select-content-transform-origin\]{transform-origin:var(--radix-select-content-transform-origin)}.origin-\[--radix-tooltip-content-transform-origin\]{transform-origin:var(--radix-tooltip-content-transform-origin)}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}.translate-x-\[-50\%\]{--tw-translate-x:-50%}.translate-x-\[-50\%\],.translate-y-\[-50\%\]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\[-50\%\]{--tw-translate-y:-50%}.rotate-45{--tw-rotate:45deg}.rotate-45,.rotate-90{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-90{--tw-rotate:90deg}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;user-select:none}.list-none{list-style-type:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-12{gap:3rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.75rem*var(--tw-space-x-reverse))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2rem*var(--tw-space-x-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.375rem*var(--tw-space-y-reverse));margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.5rem*var(--tw-space-y-reverse));margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.whitespace-nowrap{white-space:nowrap}.break-words{overflow-wrap:break-word}.rounded-2xl{border-radius:1rem}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem;border-radius:var(--radius)}.rounded-md{border-radius:calc(.5rem - 2px);border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(.5rem - 4px);border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:.75rem}.rounded-t-\[10px\]{border-top-left-radius:10px;border-top-right-radius:10px}.rounded-tl-sm{border-top-left-radius:calc(.5rem - 4px);border-top-left-radius:calc(var(--radius) - 4px)}.border{border-width:1px}.border-2{border-width:2px}.border-y{border-top-width:1px}.border-b,.border-y{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-destructive{border-color:#ef4444;border-color:hsl(var(--destructive))}.border-destructive\/50{border-color:#ef444480;border-color:hsl(var(--destructive)/.5)}.border-gray-700{--tw-border-opacity:1;border-color:#374151;border-color:rgb(55 65 81/var(--tw-border-opacity,1))}.border-input{border-color:#e5e5e5;border-color:hsl(var(--input))}.border-primary{border-color:#171717;border-color:hsl(var(--primary))}.border-primary\/50{border-color:#17171780;border-color:hsl(var(--primary)/.5)}.border-red-200{--tw-border-opacity:1;border-color:#fecaca;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}.border-red-500{--tw-border-opacity:1;border-color:#ef4444;border-color:rgb(239 68 68/var(--tw-border-opacity,1))}.border-transparent{border-color:#0000}.border-white{--tw-border-opacity:1;border-color:#fff;border-color:rgb(255 255 255/var(--tw-border-opacity,1))}.border-white\/30{border-color:#ffffff4d}.border-l-transparent{border-left-color:#0000}.border-t-transparent{border-top-color:#0000}.bg-accent{background-color:#f5f5f5;background-color:hsl(var(--accent))}.bg-background{background-color:#fff;background-color:hsl(var(--background))}.bg-black\/80{background-color:#000c}.bg-border{background-color:#e5e5e5;background-color:hsl(var(--border))}.bg-card{background-color:#fff;background-color:hsl(var(--card))}.bg-cyan-600{--tw-bg-opacity:1;background-color:#0891b2;background-color:rgb(8 145 178/var(--tw-bg-opacity,1))}.bg-destructive{background-color:#ef4444;background-color:hsl(var(--destructive))}.bg-foreground{background-color:#0a0a0a;background-color:hsl(var(--foreground))}.bg-gray-50{--tw-bg-opacity:1;background-color:#f9fafb;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-gray-700{--tw-bg-opacity:1;background-color:#374151;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.bg-muted{background-color:#f5f5f5;background-color:hsl(var(--muted))}.bg-muted\/50{background-color:#f5f5f580;background-color:hsl(var(--muted)/.5)}.bg-popover{background-color:#fff;background-color:hsl(var(--popover))}.bg-primary{background-color:#171717;background-color:hsl(var(--primary))}.bg-primary\/10{background-color:#1717171a;background-color:hsl(var(--primary)/.1)}.bg-primary\/20{background-color:#17171733;background-color:hsl(var(--primary)/.2)}.bg-red-500{--tw-bg-opacity:1;background-color:#ef4444;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-secondary{background-color:#f5f5f5;background-color:hsl(var(--secondary))}.bg-transparent{background-color:initial}.bg-white{--tw-bg-opacity:1;background-color:#fff;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/20{background-color:#fff3}.bg-white\/95{background-color:#fffffff2}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-blue-50{--tw-gradient-from:#eff6ff var(--tw-gradient-from-position);--tw-gradient-to:#eff6ff00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-cyan-50{--tw-gradient-from:#ecfeff var(--tw-gradient-from-position);--tw-gradient-to:#ecfeff00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-cyan-500{--tw-gradient-from:#06b6d4 var(--tw-gradient-from-position);--tw-gradient-to:#06b6d400 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-cyan-600{--tw-gradient-from:#0891b2 var(--tw-gradient-from-position);--tw-gradient-to:#0891b200 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-cyan-900\/80{--tw-gradient-from:#164e63cc var(--tw-gradient-from-position);--tw-gradient-to:#164e6300 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-900{--tw-gradient-from:#111827 var(--tw-gradient-from-position);--tw-gradient-to:#11182700 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-blue-100{--tw-gradient-to:#dbeafe var(--tw-gradient-to-position)}.to-blue-50{--tw-gradient-to:#eff6ff var(--tw-gradient-to-position)}.to-blue-600{--tw-gradient-to:#2563eb var(--tw-gradient-to-position)}.to-blue-700{--tw-gradient-to:#1d4ed8 var(--tw-gradient-to-position)}.to-blue-900\/70{--tw-gradient-to:#1e3a8ab3 var(--tw-gradient-to-position)}.to-cyan-50{--tw-gradient-to:#ecfeff var(--tw-gradient-to-position)}.to-gray-800{--tw-gradient-to:#1f2937 var(--tw-gradient-to-position)}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.fill-current{fill:currentColor}.fill-primary{fill:#171717;fill:hsl(var(--primary))}.fill-yellow-400{fill:#facc15}.object-cover{object-fit:cover}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.p-\[1px\]{padding:1px}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-bottom:.125rem;padding-top:.125rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-1\.5{padding-bottom:.375rem;padding-top:.375rem}.py-12{padding-bottom:3rem;padding-top:3rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-20{padding-bottom:5rem;padding-top:5rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.py-6{padding-bottom:1.5rem;padding-top:1.5rem}.py-8{padding-bottom:2rem;padding-top:2rem}.pb-4{padding-bottom:1rem}.pl-10{padding-left:2.5rem}.pl-2{padding-left:.5rem}.pl-2\.5{padding-left:.625rem}.pl-4{padding-left:1rem}.pl-8{padding-left:2rem}.pr-2{padding-right:.5rem}.pr-2\.5{padding-right:.625rem}.pr-6{padding-right:1.5rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.pt-8{padding-top:2rem}.text-left{text-align:left}.text-center{text-align:center}.align-middle{vertical-align:middle}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-\[0\.8rem\]{font-size:.8rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.italic{font-style:italic}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-widest{letter-spacing:.1em}.text-accent-foreground{color:#171717;color:hsl(var(--accent-foreground))}.text-card-foreground{color:#0a0a0a;color:hsl(var(--card-foreground))}.text-current{color:currentColor}.text-cyan-100{--tw-text-opacity:1;color:#cffafe;color:rgb(207 250 254/var(--tw-text-opacity,1))}.text-cyan-400{--tw-text-opacity:1;color:#22d3ee;color:rgb(34 211 238/var(--tw-text-opacity,1))}.text-cyan-50{--tw-text-opacity:1;color:#ecfeff;color:rgb(236 254 255/var(--tw-text-opacity,1))}.text-cyan-600{--tw-text-opacity:1;color:#0891b2;color:rgb(8 145 178/var(--tw-text-opacity,1))}.text-destructive{color:#ef4444;color:hsl(var(--destructive))}.text-destructive-foreground{color:#fafafa;color:hsl(var(--destructive-foreground))}.text-foreground{color:#0a0a0a;color:hsl(var(--foreground))}.text-foreground\/50{color:#0a0a0a80;color:hsl(var(--foreground)/.5)}.text-gray-300{--tw-text-opacity:1;color:#d1d5db;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:#9ca3af;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:#6b7280;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:#4b5563;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:#374151;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-gray-900{--tw-text-opacity:1;color:#111827;color:rgb(17 24 39/var(--tw-text-opacity,1))}.text-muted-foreground{color:#737373;color:hsl(var(--muted-foreground))}.text-popover-foreground{color:#0a0a0a;color:hsl(var(--popover-foreground))}.text-primary{color:#171717;color:hsl(var(--primary))}.text-primary-foreground{color:#fafafa;color:hsl(var(--primary-foreground))}.text-red-500{--tw-text-opacity:1;color:#ef4444;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-red-600{--tw-text-opacity:1;color:#dc2626;color:rgb(220 38 38/var(--tw-text-opacity,1))}.text-secondary-foreground{color:#171717;color:hsl(var(--secondary-foreground))}.text-transparent{color:#0000}.text-white{--tw-text-opacity:1;color:#fff;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-yellow-400{--tw-text-opacity:1;color:#facc15;color:rgb(250 204 21/var(--tw-text-opacity,1))}.line-through{-webkit-text-decoration-line:line-through;text-decoration-line:line-through}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-90{opacity:.9}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:0 0 #0000,0 0 #0000,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:0 0 #0000,0 0 #0000,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.outline-none{outline:2px solid #0000;outline-offset:2px}.outline{outline-style:solid}.ring-0{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-0,.ring-1{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),0 0 #0000;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-ring{--tw-ring-color:hsl(var(--ring))}.ring-offset-background{--tw-ring-offset-color:hsl(var(--background))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px)}.backdrop-blur-md,.backdrop-blur-sm{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,fill,stroke,-webkit-text-decoration-color;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,-webkit-text-decoration-color;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-shadow{transition-duration:.15s;transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.delay-100{transition-delay:.1s}.delay-200{transition-delay:.2s}.duration-1000{transition-duration:1s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.duration-700{transition-duration:.7s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}@keyframes enter{0%{opacity:1;opacity:var(--tw-enter-opacity,1);transform:translateZ(0) scaleX(1) rotate(0);transform:translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0) scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1)) rotate(var(--tw-enter-rotate,0))}}@keyframes exit{to{opacity:1;opacity:var(--tw-exit-opacity,1);transform:translateZ(0) scaleX(1) rotate(0);transform:translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0) scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1)) rotate(var(--tw-exit-rotate,0))}}.animate-in{--tw-enter-opacity:initial;--tw-enter-scale:initial;--tw-enter-rotate:initial;--tw-enter-translate-x:initial;--tw-enter-translate-y:initial;animation-duration:.15s;animation-name:enter}.fade-in,.fade-in-0{--tw-enter-opacity:0}.zoom-in-95{--tw-enter-scale:.95}.slide-in-from-bottom{--tw-enter-translate-y:100%}.slide-in-from-top{--tw-enter-translate-y:-100%}.duration-1000{animation-duration:1s}.duration-200{animation-duration:.2s}.duration-300{animation-duration:.3s}.duration-500{animation-duration:.5s}.duration-700{animation-duration:.7s}.delay-100{animation-delay:.1s}.delay-200{animation-delay:.2s}.ease-in-out{animation-timing-function:cubic-bezier(.4,0,.2,1)}.running{animation-play-state:running}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;margin:0}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}.file\:border-0::-webkit-file-upload-button{border-width:0}.file\:border-0::file-selector-button{border-width:0}.file\:bg-transparent::-webkit-file-upload-button{background-color:initial}.file\:bg-transparent::file-selector-button{background-color:initial}.file\:text-sm::-webkit-file-upload-button{font-size:.875rem;line-height:1.25rem}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::-webkit-file-upload-button{font-weight:500}.file\:font-medium::file-selector-button{font-weight:500}.file\:text-foreground::-webkit-file-upload-button{color:#0a0a0a;color:hsl(var(--foreground))}.file\:text-foreground::file-selector-button{color:#0a0a0a;color:hsl(var(--foreground))}.placeholder\:text-cyan-100::placeholder{--tw-text-opacity:1;color:#cffafe;color:rgb(207 250 254/var(--tw-text-opacity,1))}.placeholder\:text-muted-foreground::placeholder{color:#737373;color:hsl(var(--muted-foreground))}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:inset-y-0:after{bottom:0;content:var(--tw-content);top:0}.after\:left-1\/2:after{content:var(--tw-content);left:50%}.after\:w-1:after{content:var(--tw-content);width:.25rem}.after\:-translate-x-1\/2:after{--tw-translate-x:-50%;content:var(--tw-content);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.first\:rounded-l-md:first-child{border-bottom-left-radius:calc(.5rem - 2px);border-bottom-left-radius:calc(var(--radius) - 2px);border-top-left-radius:calc(.5rem - 2px);border-top-left-radius:calc(var(--radius) - 2px)}.first\:border-l:first-child{border-left-width:1px}.last\:rounded-r-md:last-child{border-bottom-right-radius:calc(.5rem - 2px);border-bottom-right-radius:calc(var(--radius) - 2px);border-top-right-radius:calc(.5rem - 2px);border-top-right-radius:calc(var(--radius) - 2px)}.focus-within\:relative:focus-within{position:relative}.focus-within\:z-20:focus-within{z-index:20}.hover\:bg-accent:hover{background-color:#f5f5f5;background-color:hsl(var(--accent))}.hover\:bg-cyan-50:hover{--tw-bg-opacity:1;background-color:#ecfeff;background-color:rgb(236 254 255/var(--tw-bg-opacity,1))}.hover\:bg-cyan-600:hover{--tw-bg-opacity:1;background-color:#0891b2;background-color:rgb(8 145 178/var(--tw-bg-opacity,1))}.hover\:bg-cyan-700:hover{--tw-bg-opacity:1;background-color:#0e7490;background-color:rgb(14 116 144/var(--tw-bg-opacity,1))}.hover\:bg-destructive\/80:hover{background-color:#ef4444cc;background-color:hsl(var(--destructive)/.8)}.hover\:bg-destructive\/90:hover{background-color:#ef4444e6;background-color:hsl(var(--destructive)/.9)}.hover\:bg-muted:hover{background-color:#f5f5f5;background-color:hsl(var(--muted))}.hover\:bg-muted\/50:hover{background-color:#f5f5f580;background-color:hsl(var(--muted)/.5)}.hover\:bg-primary:hover{background-color:#171717;background-color:hsl(var(--primary))}.hover\:bg-primary\/80:hover{background-color:#171717cc;background-color:hsl(var(--primary)/.8)}.hover\:bg-primary\/90:hover{background-color:#171717e6;background-color:hsl(var(--primary)/.9)}.hover\:bg-red-50:hover{--tw-bg-opacity:1;background-color:#fef2f2;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.hover\:bg-secondary:hover{background-color:#f5f5f5;background-color:hsl(var(--secondary))}.hover\:bg-secondary\/80:hover{background-color:#f5f5f5cc;background-color:hsl(var(--secondary)/.8)}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:#fff;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.hover\:text-accent-foreground:hover{color:#171717;color:hsl(var(--accent-foreground))}.hover\:text-cyan-400:hover{--tw-text-opacity:1;color:#22d3ee;color:rgb(34 211 238/var(--tw-text-opacity,1))}.hover\:text-cyan-600:hover{--tw-text-opacity:1;color:#0891b2;color:rgb(8 145 178/var(--tw-text-opacity,1))}.hover\:text-foreground:hover{color:#0a0a0a;color:hsl(var(--foreground))}.hover\:text-muted-foreground:hover{color:#737373;color:hsl(var(--muted-foreground))}.hover\:text-primary-foreground:hover{color:#fafafa;color:hsl(var(--primary-foreground))}.hover\:underline:hover{-webkit-text-decoration-line:underline;text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-2xl:hover{--tw-shadow:0 25px 50px -12px #00000040;--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.hover\:shadow-2xl:hover,.hover\:shadow-xl:hover{box-shadow:0 0 #0000,0 0 #0000,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:shadow-xl:hover{--tw-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.focus\:bg-accent:focus{background-color:#f5f5f5;background-color:hsl(var(--accent))}.focus\:bg-primary:focus{background-color:#171717;background-color:hsl(var(--primary))}.focus\:text-accent-foreground:focus{color:#171717;color:hsl(var(--accent-foreground))}.focus\:text-primary-foreground:focus{color:#fafafa;color:hsl(var(--primary-foreground))}.focus\:opacity-100:focus{opacity:1}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),0 0 #0000;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-ring:focus{--tw-ring-color:hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus-visible\:outline-none:focus-visible{outline:2px solid #0000;outline-offset:2px}.focus-visible\:ring-1:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),0 0 #0000;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),0 0 #0000;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:hsl(var(--ring))}.focus-visible\:ring-offset-1:focus-visible{--tw-ring-offset-width:1px}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color:hsl(var(--background))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:opacity-100{opacity:1}.group.destructive .group-\[\.destructive\]\:border-muted\/40{border-color:#f5f5f566;border-color:hsl(var(--muted)/.4)}.group.toaster .group-\[\.toaster\]\:border-border{border-color:#e5e5e5;border-color:hsl(var(--border))}.group.toast .group-\[\.toast\]\:bg-muted{background-color:#f5f5f5;background-color:hsl(var(--muted))}.group.toast .group-\[\.toast\]\:bg-primary{background-color:#171717;background-color:hsl(var(--primary))}.group.toaster .group-\[\.toaster\]\:bg-background{background-color:#fff;background-color:hsl(var(--background))}.group.destructive .group-\[\.destructive\]\:text-red-300{--tw-text-opacity:1;color:#fca5a5;color:rgb(252 165 165/var(--tw-text-opacity,1))}.group.toast .group-\[\.toast\]\:text-muted-foreground{color:#737373;color:hsl(var(--muted-foreground))}.group.toast .group-\[\.toast\]\:text-primary-foreground{color:#fafafa;color:hsl(var(--primary-foreground))}.group.toaster .group-\[\.toaster\]\:text-foreground{color:#0a0a0a;color:hsl(var(--foreground))}.group.toaster .group-\[\.toaster\]\:shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:0 0 #0000,0 0 #0000,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.group.destructive .group-\[\.destructive\]\:hover\:border-destructive\/30:hover{border-color:#ef44444d;border-color:hsl(var(--destructive)/.3)}.group.destructive .group-\[\.destructive\]\:hover\:bg-destructive:hover{background-color:#ef4444;background-color:hsl(var(--destructive))}.group.destructive .group-\[\.destructive\]\:hover\:text-destructive-foreground:hover{color:#fafafa;color:hsl(var(--destructive-foreground))}.group.destructive .group-\[\.destructive\]\:hover\:text-red-50:hover{--tw-text-opacity:1;color:#fef2f2;color:rgb(254 242 242/var(--tw-text-opacity,1))}.group.destructive .group-\[\.destructive\]\:focus\:ring-destructive:focus{--tw-ring-color:hsl(var(--destructive))}.group.destructive .group-\[\.destructive\]\:focus\:ring-red-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(248 113 113/var(--tw-ring-opacity,1))}.group.destructive .group-\[\.destructive\]\:focus\:ring-offset-red-600:focus{--tw-ring-offset-color:#dc2626}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.has-\[\:disabled\]\:opacity-50:has(:disabled){opacity:.5}.aria-selected\:bg-accent[aria-selected=true]{background-color:#f5f5f5;background-color:hsl(var(--accent))}.aria-selected\:bg-accent\/50[aria-selected=true]{background-color:#f5f5f580;background-color:hsl(var(--accent)/.5)}.aria-selected\:text-accent-foreground[aria-selected=true]{color:#171717;color:hsl(var(--accent-foreground))}.aria-selected\:text-muted-foreground[aria-selected=true]{color:#737373;color:hsl(var(--muted-foreground))}.aria-selected\:opacity-100[aria-selected=true]{opacity:1}.data-\[disabled\=true\]\:pointer-events-none[data-disabled=true],.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[panel-group-direction\=vertical\]\:h-px[data-panel-group-direction=vertical]{height:1px}.data-\[panel-group-direction\=vertical\]\:w-full[data-panel-group-direction=vertical]{width:100%}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y:0.25rem}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom],.data-\[side\=left\]\:-translate-x-1[data-side=left]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x:-0.25rem}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x:0.25rem}.data-\[side\=right\]\:translate-x-1[data-side=right],.data-\[side\=top\]\:-translate-y-1[data-side=top]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y:-0.25rem}.data-\[state\=checked\]\:translate-x-4[data-state=checked]{--tw-translate-x:1rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked],.data-\[swipe\=cancel\]\:translate-x-0[data-swipe=cancel]{--tw-translate-x:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end]{--tw-translate-x:var(--radix-toast-swipe-end-x)}.data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end],.data-\[swipe\=move\]\:translate-x-\[var\(--radix-toast-swipe-move-x\)\][data-swipe=move]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[swipe\=move\]\:translate-x-\[var\(--radix-toast-swipe-move-x\)\][data-swipe=move]{--tw-translate-x:var(--radix-toast-swipe-move-x)}@keyframes accordion-up{0%{height:var(--radix-accordion-content-height)}to{height:0}}.data-\[state\=closed\]\:animate-accordion-up[data-state=closed]{animation:accordion-up .2s ease-out}@keyframes accordion-down{0%{height:0}to{height:var(--radix-accordion-content-height)}}.data-\[state\=open\]\:animate-accordion-down[data-state=open]{animation:accordion-down .2s ease-out}.data-\[panel-group-direction\=vertical\]\:flex-col[data-panel-group-direction=vertical]{flex-direction:column}.data-\[selected\=true\]\:bg-accent[data-selected=true]{background-color:#f5f5f5;background-color:hsl(var(--accent))}.data-\[state\=active\]\:bg-background[data-state=active]{background-color:#fff;background-color:hsl(var(--background))}.data-\[state\=checked\]\:bg-primary[data-state=checked]{background-color:#171717;background-color:hsl(var(--primary))}.data-\[state\=on\]\:bg-accent[data-state=on],.data-\[state\=open\]\:bg-accent[data-state=open]{background-color:#f5f5f5;background-color:hsl(var(--accent))}.data-\[state\=open\]\:bg-accent\/50[data-state=open]{background-color:#f5f5f580;background-color:hsl(var(--accent)/.5)}.data-\[state\=open\]\:bg-secondary[data-state=open]{background-color:#f5f5f5;background-color:hsl(var(--secondary))}.data-\[state\=selected\]\:bg-muted[data-state=selected]{background-color:#f5f5f5;background-color:hsl(var(--muted))}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{background-color:#e5e5e5;background-color:hsl(var(--input))}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{color:#737373;color:hsl(var(--muted-foreground))}.data-\[selected\=true\]\:text-accent-foreground[data-selected=true]{color:#171717;color:hsl(var(--accent-foreground))}.data-\[state\=active\]\:text-foreground[data-state=active]{color:#0a0a0a;color:hsl(var(--foreground))}.data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{color:#fafafa;color:hsl(var(--primary-foreground))}.data-\[state\=on\]\:text-accent-foreground[data-state=on],.data-\[state\=open\]\:text-accent-foreground[data-state=open]{color:#171717;color:hsl(var(--accent-foreground))}.data-\[state\=open\]\:text-muted-foreground[data-state=open]{color:#737373;color:hsl(var(--muted-foreground))}.data-\[disabled\=true\]\:opacity-50[data-disabled=true],.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[state\=active\]\:shadow[data-state=active]{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:0 0 #0000,0 0 #0000,var(--tw-shadow);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.data-\[swipe\=move\]\:transition-none[data-swipe=move]{transition-property:none}.data-\[state\=closed\]\:duration-300[data-state=closed]{transition-duration:.3s}.data-\[state\=open\]\:duration-500[data-state=open]{transition-duration:.5s}.data-\[motion\^\=from-\]\:animate-in[data-motion^=from-],.data-\[state\=open\]\:animate-in[data-state=open],.data-\[state\=visible\]\:animate-in[data-state=visible]{--tw-enter-opacity:initial;--tw-enter-scale:initial;--tw-enter-rotate:initial;--tw-enter-translate-x:initial;--tw-enter-translate-y:initial;animation-duration:.15s;animation-name:enter}.data-\[motion\^\=to-\]\:animate-out[data-motion^=to-],.data-\[state\=closed\]\:animate-out[data-state=closed],.data-\[state\=hidden\]\:animate-out[data-state=hidden],.data-\[swipe\=end\]\:animate-out[data-swipe=end]{--tw-exit-opacity:initial;--tw-exit-scale:initial;--tw-exit-rotate:initial;--tw-exit-translate-x:initial;--tw-exit-translate-y:initial;animation-duration:.15s;animation-name:exit}.data-\[motion\^\=from-\]\:fade-in[data-motion^=from-]{--tw-enter-opacity:0}.data-\[motion\^\=to-\]\:fade-out[data-motion^=to-],.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity:0}.data-\[state\=closed\]\:fade-out-80[data-state=closed]{--tw-exit-opacity:0.8}.data-\[state\=hidden\]\:fade-out[data-state=hidden]{--tw-exit-opacity:0}.data-\[state\=open\]\:fade-in-0[data-state=open],.data-\[state\=visible\]\:fade-in[data-state=visible]{--tw-enter-opacity:0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale:.95}.data-\[state\=open\]\:zoom-in-90[data-state=open]{--tw-enter-scale:.9}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale:.95}.data-\[motion\=from-end\]\:slide-in-from-right-52[data-motion=from-end]{--tw-enter-translate-x:13rem}.data-\[motion\=from-start\]\:slide-in-from-left-52[data-motion=from-start]{--tw-enter-translate-x:-13rem}.data-\[motion\=to-end\]\:slide-out-to-right-52[data-motion=to-end]{--tw-exit-translate-x:13rem}.data-\[motion\=to-start\]\:slide-out-to-left-52[data-motion=to-start]{--tw-exit-translate-x:-13rem}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y:-0.5rem}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x:0.5rem}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x:-0.5rem}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y:0.5rem}.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y:100%}.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x:-100%}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x:-50%}.data-\[state\=closed\]\:slide-out-to-right-full[data-state=closed],.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x:100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y:-100%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y:-48%}.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y:100%}.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x:-100%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x:-50%}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x:100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y:-100%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y:-48%}.data-\[state\=open\]\:slide-in-from-top-full[data-state=open]{--tw-enter-translate-y:-100%}.data-\[state\=closed\]\:duration-300[data-state=closed]{animation-duration:.3s}.data-\[state\=open\]\:duration-500[data-state=open]{animation-duration:.5s}.data-\[panel-group-direction\=vertical\]\:after\:left-0[data-panel-group-direction=vertical]:after{content:var(--tw-content);left:0}.data-\[panel-group-direction\=vertical\]\:after\:h-1[data-panel-group-direction=vertical]:after{content:var(--tw-content);height:.25rem}.data-\[panel-group-direction\=vertical\]\:after\:w-full[data-panel-group-direction=vertical]:after{content:var(--tw-content);width:100%}.data-\[panel-group-direction\=vertical\]\:after\:-translate-y-1\/2[data-panel-group-direction=vertical]:after{--tw-translate-y:-50%;content:var(--tw-content);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[panel-group-direction\=vertical\]\:after\:translate-x-0[data-panel-group-direction=vertical]:after{--tw-translate-x:0px;content:var(--tw-content);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=open\]\:focus\:bg-accent:focus[data-state=open],.data-\[state\=open\]\:hover\:bg-accent:hover[data-state=open]{background-color:#f5f5f5;background-color:hsl(var(--accent))}.group[data-state=open] .group-data-\[state\=open\]\:rotate-180{--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:border-destructive:is(.dark *){border-color:#ef4444;border-color:hsl(var(--destructive))}@media (min-width:640px){.sm\:bottom-0{bottom:0}.sm\:right-0{right:0}.sm\:top-auto{top:auto}.sm\:mt-0{margin-top:0}.sm\:max-w-sm{max-width:24rem}.sm\:flex-row{flex-direction:row}.sm\:flex-col{flex-direction:column}.sm\:justify-end{justify-content:flex-end}.sm\:gap-2\.5{gap:.625rem}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.sm\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(0px*var(--tw-space-y-reverse));margin-top:calc(0px*(1 - var(--tw-space-y-reverse)))}.sm\:rounded-lg{border-radius:.5rem;border-radius:var(--radius)}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:text-left{text-align:left}.data-\[state\=open\]\:sm\:slide-in-from-bottom-full[data-state=open]{--tw-enter-translate-y:100%}}@media (min-width:768px){.md\:absolute{position:absolute}.md\:flex{display:flex}.md\:hidden{display:none}.md\:w-96{width:24rem}.md\:w-\[var\(--radix-navigation-menu-viewport-width\)\]{width:var(--radix-navigation-menu-viewport-width)}.md\:w-auto{width:auto}.md\:max-w-\[420px\]{max-width:420px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:p-12{padding:3rem}.md\:text-2xl{font-size:1.5rem;line-height:2rem}.md\:text-4xl{font-size:2.25rem;line-height:2.5rem}.md\:text-5xl{font-size:3rem;line-height:1}.md\:text-7xl{font-size:4.5rem;line-height:1}.md\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:1024px){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:px-8{padding-left:2rem;padding-right:2rem}}.\[\&\+div\]\:text-xs+div{font-size:.75rem;line-height:1rem}.\[\&\:has\(\>\.day-range-end\)\]\:rounded-r-md:has(>.day-range-end){border-bottom-right-radius:calc(.5rem - 2px);border-bottom-right-radius:calc(var(--radius) - 2px);border-top-right-radius:calc(.5rem - 2px);border-top-right-radius:calc(var(--radius) - 2px)}.\[\&\:has\(\>\.day-range-start\)\]\:rounded-l-md:has(>.day-range-start){border-bottom-left-radius:calc(.5rem - 2px);border-bottom-left-radius:calc(var(--radius) - 2px);border-top-left-radius:calc(.5rem - 2px);border-top-left-radius:calc(var(--radius) - 2px)}.\[\&\:has\(\[aria-selected\]\)\]\:rounded-md:has([aria-selected]){border-radius:calc(.5rem - 2px);border-radius:calc(var(--radius) - 2px)}.\[\&\:has\(\[aria-selected\]\)\]\:bg-accent:has([aria-selected]){background-color:#f5f5f5;background-color:hsl(var(--accent))}.first\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-l-md:has([aria-selected]):first-child{border-bottom-left-radius:calc(.5rem - 2px);border-bottom-left-radius:calc(var(--radius) - 2px);border-top-left-radius:calc(.5rem - 2px);border-top-left-radius:calc(var(--radius) - 2px)}.last\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-r-md:has([aria-selected]):last-child{border-bottom-right-radius:calc(.5rem - 2px);border-bottom-right-radius:calc(var(--radius) - 2px);border-top-right-radius:calc(.5rem - 2px);border-top-right-radius:calc(var(--radius) - 2px)}.\[\&\:has\(\[aria-selected\]\.day-outside\)\]\:bg-accent\/50:has([aria-selected].day-outside){background-color:#f5f5f580;background-color:hsl(var(--accent)/.5)}.\[\&\:has\(\[aria-selected\]\.day-range-end\)\]\:rounded-r-md:has([aria-selected].day-range-end){border-bottom-right-radius:calc(.5rem - 2px);border-bottom-right-radius:calc(var(--radius) - 2px);border-top-right-radius:calc(.5rem - 2px);border-top-right-radius:calc(var(--radius) - 2px)}.\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role=checkbox]){padding-right:0}.\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\]>[role=checkbox]{--tw-translate-y:2px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\[\&\>span\]\:line-clamp-1>span{-webkit-box-orient:vertical;-webkit-line-clamp:1;display:-webkit-box;overflow:hidden}.\[\&\>svg\+div\]\:translate-y-\[-3px\]>svg+div{--tw-translate-y:-3px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\[\&\>svg\]\:absolute>svg{position:absolute}.\[\&\>svg\]\:left-4>svg{left:1rem}.\[\&\>svg\]\:top-4>svg{top:1rem}.\[\&\>svg\]\:size-4>svg{height:1rem;width:1rem}.\[\&\>svg\]\:h-3\.5>svg{height:.875rem}.\[\&\>svg\]\:w-3\.5>svg{width:.875rem}.\[\&\>svg\]\:shrink-0>svg{flex-shrink:0}.\[\&\>svg\]\:text-destructive>svg{color:#ef4444;color:hsl(var(--destructive))}.\[\&\>svg\]\:text-foreground>svg{color:#0a0a0a;color:hsl(var(--foreground))}.\[\&\>svg\~\*\]\:pl-7>svg~*{padding-left:1.75rem}.\[\&\>tr\]\:last\:border-b-0:last-child>tr{border-bottom-width:0}.\[\&\[data-panel-group-direction\=vertical\]\>div\]\:rotate-90[data-panel-group-direction=vertical]>div{--tw-rotate:90deg}.\[\&\[data-panel-group-direction\=vertical\]\>div\]\:rotate-90[data-panel-group-direction=vertical]>div,.\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state=open]>svg{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state=open]>svg{--tw-rotate:180deg}.\[\&_\[cmdk-group-heading\]\]\:px-2 [cmdk-group-heading]{padding-left:.5rem;padding-right:.5rem}.\[\&_\[cmdk-group-heading\]\]\:py-1\.5 [cmdk-group-heading]{padding-bottom:.375rem;padding-top:.375rem}.\[\&_\[cmdk-group-heading\]\]\:text-xs [cmdk-group-heading]{font-size:.75rem;line-height:1rem}.\[\&_\[cmdk-group-heading\]\]\:font-medium [cmdk-group-heading]{font-weight:500}.\[\&_\[cmdk-group-heading\]\]\:text-muted-foreground [cmdk-group-heading]{color:#737373;color:hsl(var(--muted-foreground))}.\[\&_\[cmdk-group\]\:not\(\[hidden\]\)_\~\[cmdk-group\]\]\:pt-0 [cmdk-group]:not([hidden])~[cmdk-group]{padding-top:0}.\[\&_\[cmdk-group\]\]\:px-2 [cmdk-group]{padding-left:.5rem;padding-right:.5rem}.\[\&_\[cmdk-input-wrapper\]_svg\]\:h-5 [cmdk-input-wrapper] svg{height:1.25rem}.\[\&_\[cmdk-input-wrapper\]_svg\]\:w-5 [cmdk-input-wrapper] svg{width:1.25rem}.\[\&_\[cmdk-input\]\]\:h-12 [cmdk-input]{height:3rem}.\[\&_\[cmdk-item\]\]\:px-2 [cmdk-item]{padding-left:.5rem;padding-right:.5rem}.\[\&_\[cmdk-item\]\]\:py-3 [cmdk-item]{padding-bottom:.75rem;padding-top:.75rem}.\[\&_\[cmdk-item\]_svg\]\:h-5 [cmdk-item] svg{height:1.25rem}.\[\&_\[cmdk-item\]_svg\]\:w-5 [cmdk-item] svg{width:1.25rem}.\[\&_p\]\:leading-relaxed p{line-height:1.625}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{height:1rem;width:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_tr\:last-child\]\:border-0 tr:last-child{border-width:0}.\[\&_tr\]\:border-b tr{border-bottom-width:1px}.App-logo{height:40vmin;pointer-events:none}@media (prefers-reduced-motion:no-preference){.App-logo{animation:App-logo-spin 20s linear infinite}}.App-header{align-items:center;background-color:#0f0f10;color:#fff;display:flex;flex-direction:column;font-size:calc(10px + 2vmin);justify-content:center;min-height:100vh}.App-link{color:#61dafb}@keyframes App-logo-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}} +/*# sourceMappingURL=main.7791f349.css.map*/ \ No newline at end of file diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/static/css/main.7791f349.css.map b/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/static/css/main.7791f349.css.map new file mode 100644 index 0000000..f554559 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/static/css/main.7791f349.css.map @@ -0,0 +1 @@ +{"version":3,"file":"static/css/main.7791f349.css","mappings":"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","sources":["index.css","App.css"],"sourcesContent":["@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nbody {\n margin: 0;\n font-family:\n -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family:\n source-code-pro, Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\n\n@layer base {\n :root {\n --background: 0 0% 100%;\n --foreground: 0 0% 3.9%;\n --card: 0 0% 100%;\n --card-foreground: 0 0% 3.9%;\n --popover: 0 0% 100%;\n --popover-foreground: 0 0% 3.9%;\n --primary: 0 0% 9%;\n --primary-foreground: 0 0% 98%;\n --secondary: 0 0% 96.1%;\n --secondary-foreground: 0 0% 9%;\n --muted: 0 0% 96.1%;\n --muted-foreground: 0 0% 45.1%;\n --accent: 0 0% 96.1%;\n --accent-foreground: 0 0% 9%;\n --destructive: 0 84.2% 60.2%;\n --destructive-foreground: 0 0% 98%;\n --border: 0 0% 89.8%;\n --input: 0 0% 89.8%;\n --ring: 0 0% 3.9%;\n --chart-1: 12 76% 61%;\n --chart-2: 173 58% 39%;\n --chart-3: 197 37% 24%;\n --chart-4: 43 74% 66%;\n --chart-5: 27 87% 67%;\n --radius: 0.5rem;\n }\n .dark {\n --background: 0 0% 3.9%;\n --foreground: 0 0% 98%;\n --card: 0 0% 3.9%;\n --card-foreground: 0 0% 98%;\n --popover: 0 0% 3.9%;\n --popover-foreground: 0 0% 98%;\n --primary: 0 0% 98%;\n --primary-foreground: 0 0% 9%;\n --secondary: 0 0% 14.9%;\n --secondary-foreground: 0 0% 98%;\n --muted: 0 0% 14.9%;\n --muted-foreground: 0 0% 63.9%;\n --accent: 0 0% 14.9%;\n --accent-foreground: 0 0% 98%;\n --destructive: 0 62.8% 30.6%;\n --destructive-foreground: 0 0% 98%;\n --border: 0 0% 14.9%;\n --input: 0 0% 14.9%;\n --ring: 0 0% 83.1%;\n --chart-1: 220 70% 50%;\n --chart-2: 160 60% 45%;\n --chart-3: 30 80% 55%;\n --chart-4: 280 65% 60%;\n --chart-5: 340 75% 55%;\n }\n}\n\n@layer base {\n * {\n @apply border-border;\n }\n body {\n @apply bg-background text-foreground;\n }\n}\n\n@layer base {\n [data-debug-wrapper=\"true\"] {\n display: contents !important;\n }\n\n [data-debug-wrapper=\"true\"] > * {\n margin-left: inherit;\n margin-right: inherit;\n margin-top: inherit;\n margin-bottom: inherit;\n padding-left: inherit;\n padding-right: inherit;\n padding-top: inherit;\n padding-bottom: inherit;\n column-gap: inherit;\n row-gap: inherit;\n gap: inherit;\n border-left-width: inherit;\n border-right-width: inherit;\n border-top-width: inherit;\n border-bottom-width: inherit;\n border-left-style: inherit;\n border-right-style: inherit;\n border-top-style: inherit;\n border-bottom-style: inherit;\n border-left-color: inherit;\n border-right-color: inherit;\n border-top-color: inherit;\n border-bottom-color: inherit;\n }\n}\n",".App-logo {\n height: 40vmin;\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .App-logo {\n animation: App-logo-spin infinite 20s linear;\n }\n}\n\n.App-header {\n background-color: #0f0f10;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: calc(10px + 2vmin);\n color: white;\n}\n\n.App-link {\n color: #61dafb;\n}\n\n@keyframes App-logo-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n"],"names":[],"ignoreList":[],"sourceRoot":""} \ No newline at end of file diff --git a/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/static/js/main.df6117d1.js b/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/static/js/main.df6117d1.js new file mode 100644 index 0000000..0942cb7 --- /dev/null +++ b/cpanel_deployment/epic-travel-cpanel-20260506-034534/frontend/static/js/main.df6117d1.js @@ -0,0 +1,3 @@ +/*! For license information please see main.df6117d1.js.LICENSE.txt */ +(()=>{"use strict";var e={4(e,t,n){var r=n(853),a=n(43),o=n(950);function l(e){var t="https://react.dev/errors/"+e;if(1F||(e.current=M[F],M[F]=null,F--)}function U(e,t){F++,M[F]=e.current,e.current=t}var H,W,V=I(null),$=I(null),q=I(null),K=I(null);function Y(e,t){switch(U(q,t),U($,e),U(V,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?vd(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)e=yd(t=vd(t),e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}B(V),U(V,e)}function Q(){B(V),B($),B(q)}function G(e){null!==e.memoizedState&&U(K,e);var t=V.current,n=yd(t,e.type);t!==n&&(U($,e),U(V,n))}function X(e){$.current===e&&(B(V),B($)),K.current===e&&(B(K),df._currentValue=z)}function J(e){if(void 0===H)try{throw Error()}catch(yg){var t=yg.stack.trim().match(/\n( *(at )?)/);H=t&&t[1]||"",W=-1)":-1--a||s[r]!==c[a]){var u="\n"+s[r].replace(" at new "," at ");return e.displayName&&u.includes("")&&(u=u.replace("",e.displayName)),u}}while(1<=r&&0<=a);break}}}finally{Z=!1,Error.prepareStackTrace=n}return(n=e?e.displayName||e.name:"")?J(n):""}function te(e,t){switch(e.tag){case 26:case 27:case 5:return J(e.type);case 16:return J("Lazy");case 13:return e.child!==t&&null!==t?J("Suspense Fallback"):J("Suspense");case 19:return J("SuspenseList");case 0:case 15:return ee(e.type,!1);case 11:return ee(e.type.render,!1);case 1:return ee(e.type,!0);case 31:return J("Activity");default:return""}}function ne(e){try{var t="",n=null;do{t+=te(e,n),n=e,e=e.return}while(e);return t}catch(yg){return"\nError generating stack: "+yg.message+"\n"+yg.stack}}var re=Object.prototype.hasOwnProperty,ae=r.unstable_scheduleCallback,oe=r.unstable_cancelCallback,le=r.unstable_shouldYield,ie=r.unstable_requestPaint,se=r.unstable_now,ce=r.unstable_getCurrentPriorityLevel,ue=r.unstable_ImmediatePriority,de=r.unstable_UserBlockingPriority,fe=r.unstable_NormalPriority,pe=r.unstable_LowPriority,me=r.unstable_IdlePriority,he=r.log,ge=r.unstable_setDisableYieldValue,ve=null,ye=null;function be(e){if("function"===typeof he&&ge(e),ye&&"function"===typeof ye.setStrictMode)try{ye.setStrictMode(ve,e)}catch(t){}}var we=Math.clz32?Math.clz32:function(e){return e>>>=0,0===e?32:31-(xe(e)/ke|0)|0},xe=Math.log,ke=Math.LN2;var Se=256,Ee=262144,Ne=4194304;function Ce(e){var t=42&e;if(0!==t)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return 261888&e;case 262144:case 524288:case 1048576:case 2097152:return 3932160&e;case 4194304:case 8388608:case 16777216:case 33554432:return 62914560&e;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function je(e,t,n){var r=e.pendingLanes;if(0===r)return 0;var a=0,o=e.suspendedLanes,l=e.pingedLanes;e=e.warmLanes;var i=134217727&r;return 0!==i?0!==(r=i&~o)?a=Ce(r):0!==(l&=i)?a=Ce(l):n||0!==(n=i&~e)&&(a=Ce(n)):0!==(i=r&~o)?a=Ce(i):0!==l?a=Ce(l):n||0!==(n=r&~e)&&(a=Ce(n)),0===a?0:0!==t&&t!==a&&0===(t&o)&&((o=a&-a)>=(n=t&-t)||32===o&&0!==(4194048&n))?t:a}function Re(e,t){return 0===(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)}function Te(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;default:return-1}}function Pe(){var e=Ne;return 0===(62914560&(Ne<<=1))&&(Ne=4194304),e}function _e(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function Oe(e,t){e.pendingLanes|=t,268435456!==t&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Ae(e,t,n){e.pendingLanes|=t,e.suspendedLanes&=~t;var r=31-we(t);e.entangledLanes|=t,e.entanglements[r]=1073741824|e.entanglements[r]|261930&n}function Le(e,t){var n=e.entangledLanes|=t;for(e=e.entanglements;n;){var r=31-we(n),a=1<=Cn),Tn=String.fromCharCode(32),Pn=!1;function _n(e,t){switch(e){case"keyup":return-1!==En.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function On(e){return"object"===typeof(e=e.detail)&&"data"in e?e.data:null}var An=!1;var Ln={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function Dn(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return"input"===t?!!Ln[e.type]:"textarea"===t}function zn(e,t,n,r){Dt?zt?zt.push(r):zt=[r]:Dt=r,0<(t=rd(t,"onChange")).length&&(n=new nn("onChange","change",null,n,r),e.push({event:n,listeners:t}))}var Mn=null,Fn=null;function In(e){Qu(e,0)}function Bn(e){if(mt(Je(e)))return e}function Un(e,t){if("change"===e)return t}var Hn=!1;if(Ut){var Wn;if(Ut){var Vn="oninput"in document;if(!Vn){var $n=document.createElement("div");$n.setAttribute("oninput","return;"),Vn="function"===typeof $n.oninput}Wn=Vn}else Wn=!1;Hn=Wn&&(!document.documentMode||9=t)return{node:r,offset:t-e};e=n}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=er(r)}}function nr(e,t){return!(!e||!t)&&(e===t||(!e||3!==e.nodeType)&&(t&&3===t.nodeType?nr(e,t.parentNode):"contains"in e?e.contains(t):!!e.compareDocumentPosition&&!!(16&e.compareDocumentPosition(t))))}function rr(e){for(var t=ht((e=null!=e&&null!=e.ownerDocument&&null!=e.ownerDocument.defaultView?e.ownerDocument.defaultView:window).document);t instanceof e.HTMLIFrameElement;){try{var n="string"===typeof t.contentWindow.location.href}catch(r){n=!1}if(!n)break;t=ht((e=t.contentWindow).document)}return t}function ar(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&("input"===t&&("text"===e.type||"search"===e.type||"tel"===e.type||"url"===e.type||"password"===e.type)||"textarea"===t||"true"===e.contentEditable)}var or=Ut&&"documentMode"in document&&11>=document.documentMode,lr=null,ir=null,sr=null,cr=!1;function ur(e,t,n){var r=n.window===n?n.document:9===n.nodeType?n:n.ownerDocument;cr||null==lr||lr!==ht(r)||("selectionStart"in(r=lr)&&ar(r)?r={start:r.selectionStart,end:r.selectionEnd}:r={anchorNode:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection()).anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset},sr&&Zn(sr,r)||(sr=r,0<(r=rd(ir,"onSelect")).length&&(t=new nn("onSelect","select",null,t,n),e.push({event:t,listeners:r}),t.target=lr)))}function dr(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n["Webkit"+e]="webkit"+t,n["Moz"+e]="moz"+t,n}var fr={animationend:dr("Animation","AnimationEnd"),animationiteration:dr("Animation","AnimationIteration"),animationstart:dr("Animation","AnimationStart"),transitionrun:dr("Transition","TransitionRun"),transitionstart:dr("Transition","TransitionStart"),transitioncancel:dr("Transition","TransitionCancel"),transitionend:dr("Transition","TransitionEnd")},pr={},mr={};function hr(e){if(pr[e])return pr[e];if(!fr[e])return e;var t,n=fr[e];for(t in n)if(n.hasOwnProperty(t)&&t in mr)return pr[e]=n[t];return e}Ut&&(mr=document.createElement("div").style,"AnimationEvent"in window||(delete fr.animationend.animation,delete fr.animationiteration.animation,delete fr.animationstart.animation),"TransitionEvent"in window||delete fr.transitionend.transition);var gr=hr("animationend"),vr=hr("animationiteration"),yr=hr("animationstart"),br=hr("transitionrun"),wr=hr("transitionstart"),xr=hr("transitioncancel"),kr=hr("transitionend"),Sr=new Map,Er="abort auxClick beforeToggle cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel".split(" ");function Nr(e,t){Sr.set(e,t),rt(t,[e])}Er.push("scrollEnd");var Cr="function"===typeof reportError?reportError:function(e){if("object"===typeof window&&"function"===typeof window.ErrorEvent){var t=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:"object"===typeof e&&null!==e&&"string"===typeof e.message?String(e.message):String(e),error:e});if(!window.dispatchEvent(t))return}else if("object"===typeof process&&"function"===typeof process.emit)return void process.emit("uncaughtException",e);console.error(e)},jr=[],Rr=0,Tr=0;function Pr(){for(var e=Rr,t=Tr=Rr=0;t>=l,a-=l,na=1<<32-we(t)+a|n<h?(g=d,d=null):g=d.sibling;var v=p(a,d,i[h],s);if(null===v){null===d&&(d=g);break}e&&d&&null===v.alternate&&t(a,d),l=o(v,l,h),null===u?c=v:u.sibling=v,u=v,d=g}if(h===i.length)return n(a,d),da&&aa(a,h),c;if(null===d){for(;hg?(v=h,h=null):v=h.sibling;var b=p(a,h,y.value,c);if(null===b){null===h&&(h=v);break}e&&h&&null===b.alternate&&t(a,h),i=o(b,i,g),null===d?u=b:d.sibling=b,d=b,h=v}if(y.done)return n(a,h),da&&aa(a,g),u;if(null===h){for(;!y.done;g++,y=s.next())null!==(y=f(a,y.value,c))&&(i=o(y,i,g),null===d?u=y:d.sibling=y,d=y);return da&&aa(a,g),u}for(h=r(h);!y.done;g++,y=s.next())null!==(y=m(h,a,g,y.value,c))&&(e&&null!==y.alternate&&h.delete(null===y.key?g:y.key),i=o(y,i,g),null===d?u=y:d.sibling=y,d=y);return e&&h.forEach(function(e){return t(a,e)}),da&&aa(a,g),u}(s,c,u=b.call(u),d)}if("function"===typeof u.then)return y(s,c,co(u),d);if(u.$$typeof===x)return y(s,c,Aa(s,u),d);fo(s,u)}return"string"===typeof u&&""!==u||"number"===typeof u||"bigint"===typeof u?(u=""+u,null!==c&&6===c.tag?(n(s,c.sibling),(d=a(c,u)).return=s,s=d):(n(s,c),(d=Vr(u,s.mode,d)).return=s,s=d),i(s)):n(s,c)}return function(e,t,n,r){try{so=0;var a=y(e,t,n,r);return io=null,a}catch(yg){if(yg===Xa||yg===Za)throw yg;var o=Fr(29,yg,null,e.mode);return o.lanes=r,o.return=e,o}}}var mo=po(!0),ho=po(!1),go=!1;function vo(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function yo(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function bo(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function wo(e,t,n){var r=e.updateQueue;if(null===r)return null;if(r=r.shared,0!==(2&pc)){var a=r.pending;return null===a?t.next=t:(t.next=a.next,a.next=t),r.pending=t,t=Dr(e),Lr(e,null,n),t}return _r(e,r,t,n),Dr(e)}function xo(e,t,n){if(null!==(t=t.updateQueue)&&(t=t.shared,0!==(4194048&n))){var r=t.lanes;n|=r&=e.pendingLanes,t.lanes=n,Le(e,n)}}function ko(e,t){var n=e.updateQueue,r=e.alternate;if(null!==r&&n===(r=r.updateQueue)){var a=null,o=null;if(null!==(n=n.firstBaseUpdate)){do{var l={lane:n.lane,tag:n.tag,payload:n.payload,callback:null,next:null};null===o?a=o=l:o=o.next=l,n=n.next}while(null!==n);null===o?a=o=t:o=o.next=t}else a=o=t;return n={baseState:r.baseState,firstBaseUpdate:a,lastBaseUpdate:o,shared:r.shared,callbacks:r.callbacks},void(e.updateQueue=n)}null===(e=n.lastBaseUpdate)?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}var So=!1;function Eo(){if(So){if(null!==Va)throw Va}}function No(e,t,n,r){So=!1;var a=e.updateQueue;go=!1;var o=a.firstBaseUpdate,l=a.lastBaseUpdate,i=a.shared.pending;if(null!==i){a.shared.pending=null;var s=i,c=s.next;s.next=null,null===l?o=c:l.next=c,l=s;var u=e.alternate;null!==u&&((i=(u=u.updateQueue).lastBaseUpdate)!==l&&(null===i?u.firstBaseUpdate=c:i.next=c,u.lastBaseUpdate=s))}if(null!==o){var d=a.baseState;for(l=0,u=c=s=null,i=o;;){var f=-536870913&i.lane,m=f!==i.lane;if(m?(gc&f)===f:(r&f)===f){0!==f&&f===Wa&&(So=!0),null!==u&&(u=u.next={lane:0,tag:i.tag,payload:i.payload,callback:null,next:null});e:{var h=e,g=i;f=t;var v=n;switch(g.tag){case 1:if("function"===typeof(h=g.payload)){d=h.call(v,d,f);break e}d=h;break e;case 3:h.flags=-65537&h.flags|128;case 0:if(null===(f="function"===typeof(h=g.payload)?h.call(v,d,f):h)||void 0===f)break e;d=p({},d,f);break e;case 2:go=!0}}null!==(f=i.callback)&&(e.flags|=64,m&&(e.flags|=8192),null===(m=a.callbacks)?a.callbacks=[f]:m.push(f))}else m={lane:f,tag:i.tag,payload:i.payload,callback:i.callback,next:null},null===u?(c=u=m,s=d):u=u.next=m,l|=f;if(null===(i=i.next)){if(null===(i=a.shared.pending))break;i=(m=i).next,m.next=null,a.lastBaseUpdate=m,a.shared.pending=null}}null===u&&(s=d),a.baseState=s,a.firstBaseUpdate=c,a.lastBaseUpdate=u,null===o&&(a.shared.lanes=0),Ec|=l,e.lanes=l,e.memoizedState=d}}function Co(e,t){if("function"!==typeof e)throw Error(l(191,e));e.call(t)}function jo(e,t){var n=e.callbacks;if(null!==n)for(e.callbacks=null,e=0;eo?o:8;var l=L.T,i={};L.T=i,di(e,!1,t,n);try{var s=a(),c=L.S;if(null!==c&&c(i,s),null!==s&&"object"===typeof s&&"function"===typeof s.then)ui(e,t,function(e,t){var n=[],r={status:"pending",value:null,reason:null,then:function(e){n.push(e)}};return e.then(function(){r.status="fulfilled",r.value=t;for(var e=0;e<\/script>",o=o.removeChild(o.firstChild);break;case"select":o="string"===typeof r.is?i.createElement("select",{is:r.is}):i.createElement("select"),r.multiple?o.multiple=!0:r.size&&(o.size=r.size);break;default:o="string"===typeof r.is?i.createElement(a,{is:r.is}):i.createElement(a)}}o[Ue]=t,o[He]=r;e:for(i=t.child;null!==i;){if(5===i.tag||6===i.tag)o.appendChild(i.stateNode);else if(4!==i.tag&&27!==i.tag&&null!==i.child){i.child.return=i,i=i.child;continue}if(i===t)break e;for(;null===i.sibling;){if(null===i.return||i.return===t)break e;i=i.return}i.sibling.return=i.return,i=i.sibling}t.stateNode=o;e:switch(fd(o,a,r),a){case"button":case"input":case"select":case"textarea":r=!!r.autoFocus;break e;case"img":r=!0;break e;default:r=!1}r&&is(t)}}return fs(t),ss(t,t.type,null===e||e.memoizedProps,t.pendingProps,n),null;case 6:if(e&&null!=t.stateNode)e.memoizedProps!==r&&is(t);else{if("string"!==typeof r&&null===t.stateNode)throw Error(l(166));if(e=q.current,ya(t)){if(e=t.stateNode,n=t.memoizedProps,r=null,null!==(a=ca))switch(a.tag){case 27:case 5:r=a.memoizedProps}e[Ue]=t,(e=!!(e.nodeValue===n||null!==r&&!0===r.suppressHydrationWarning||cd(e.nodeValue,n)))||ha(t,!0)}else(e=gd(e).createTextNode(r))[Ue]=t,t.stateNode=e}return fs(t),null;case 31:if(n=t.memoizedState,null===e||null!==e.memoizedState){if(r=ya(t),null!==n){if(null===e){if(!r)throw Error(l(318));if(!(e=null!==(e=t.memoizedState)?e.dehydrated:null))throw Error(l(557));e[Ue]=t}else ba(),0===(128&t.flags)&&(t.memoizedState=null),t.flags|=4;fs(t),e=!1}else n=wa(),null!==e&&null!==e.memoizedState&&(e.memoizedState.hydrationErrors=n),e=!0;if(!e)return 256&t.flags?(Io(t),t):(Io(t),null);if(0!==(128&t.flags))throw Error(l(558))}return fs(t),null;case 13:if(r=t.memoizedState,null===e||null!==e.memoizedState&&null!==e.memoizedState.dehydrated){if(a=ya(t),null!==r&&null!==r.dehydrated){if(null===e){if(!a)throw Error(l(318));if(!(a=null!==(a=t.memoizedState)?a.dehydrated:null))throw Error(l(317));a[Ue]=t}else ba(),0===(128&t.flags)&&(t.memoizedState=null),t.flags|=4;fs(t),a=!1}else a=wa(),null!==e&&null!==e.memoizedState&&(e.memoizedState.hydrationErrors=a),a=!0;if(!a)return 256&t.flags?(Io(t),t):(Io(t),null)}return Io(t),0!==(128&t.flags)?(t.lanes=n,t):(n=null!==r,e=null!==e&&null!==e.memoizedState,n&&(a=null,null!==(r=t.child).alternate&&null!==r.alternate.memoizedState&&null!==r.alternate.memoizedState.cachePool&&(a=r.alternate.memoizedState.cachePool.pool),o=null,null!==r.memoizedState&&null!==r.memoizedState.cachePool&&(o=r.memoizedState.cachePool.pool),o!==a&&(r.flags|=2048)),n!==e&&n&&(t.child.flags|=8192),us(t,t.updateQueue),fs(t),null);case 4:return Q(),null===e&&Zu(t.stateNode.containerInfo),fs(t),null;case 10:return Ca(t.type),fs(t),null;case 19:if(B(Bo),null===(r=t.memoizedState))return fs(t),null;if(a=0!==(128&t.flags),null===(o=r.rendering))if(a)ds(r,!1);else{if(0!==Sc||null!==e&&0!==(128&e.flags))for(e=t.child;null!==e;){if(null!==(o=Uo(e))){for(t.flags|=128,ds(r,!1),e=o.updateQueue,t.updateQueue=e,us(t,e),t.subtreeFlags=0,e=n,n=t.child;null!==n;)Ur(n,e),n=n.sibling;return U(Bo,1&Bo.current|2),da&&aa(t,r.treeForkCount),t.child}e=e.sibling}null!==r.tail&&se()>Lc&&(t.flags|=128,a=!0,ds(r,!1),t.lanes=4194304)}else{if(!a)if(null!==(e=Uo(o))){if(t.flags|=128,a=!0,e=e.updateQueue,t.updateQueue=e,us(t,e),ds(r,!0),null===r.tail&&"hidden"===r.tailMode&&!o.alternate&&!da)return fs(t),null}else 2*se()-r.renderingStartTime>Lc&&536870912!==n&&(t.flags|=128,a=!0,ds(r,!1),t.lanes=4194304);r.isBackwards?(o.sibling=t.child,t.child=o):(null!==(e=r.last)?e.sibling=o:t.child=o,r.last=o)}return null!==r.tail?(e=r.tail,r.rendering=e,r.tail=e.sibling,r.renderingStartTime=se(),e.sibling=null,n=Bo.current,U(Bo,a?1&n|2:1&n),da&&aa(t,r.treeForkCount),e):(fs(t),null);case 22:case 23:return Io(t),Oo(),r=null!==t.memoizedState,null!==e?null!==e.memoizedState!==r&&(t.flags|=8192):r&&(t.flags|=8192),r?0!==(536870912&n)&&0===(128&t.flags)&&(fs(t),6&t.subtreeFlags&&(t.flags|=8192)):fs(t),null!==(n=t.updateQueue)&&us(t,n.retryQueue),n=null,null!==e&&null!==e.memoizedState&&null!==e.memoizedState.cachePool&&(n=e.memoizedState.cachePool.pool),r=null,null!==t.memoizedState&&null!==t.memoizedState.cachePool&&(r=t.memoizedState.cachePool.pool),r!==n&&(t.flags|=2048),null!==e&&B(Ka),null;case 24:return n=null,null!==e&&(n=e.memoizedState.cache),t.memoizedState.cache!==n&&(t.flags|=2048),Ca(Fa),fs(t),null;case 25:case 30:return null}throw Error(l(156,t.tag))}function ms(e,t){switch(ia(t),t.tag){case 1:return 65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 3:return Ca(Fa),Q(),0!==(65536&(e=t.flags))&&0===(128&e)?(t.flags=-65537&e|128,t):null;case 26:case 27:case 5:return X(t),null;case 31:if(null!==t.memoizedState){if(Io(t),null===t.alternate)throw Error(l(340));ba()}return 65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 13:if(Io(t),null!==(e=t.memoizedState)&&null!==e.dehydrated){if(null===t.alternate)throw Error(l(340));ba()}return 65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 19:return B(Bo),null;case 4:return Q(),null;case 10:return Ca(t.type),null;case 22:case 23:return Io(t),Oo(),null!==e&&B(Ka),65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 24:return Ca(Fa),null;default:return null}}function hs(e,t){switch(ia(t),t.tag){case 3:Ca(Fa),Q();break;case 26:case 27:case 5:X(t);break;case 4:Q();break;case 31:null!==t.memoizedState&&Io(t);break;case 13:Io(t);break;case 19:B(Bo);break;case 10:Ca(t.type);break;case 22:case 23:Io(t),Oo(),null!==e&&B(Ka);break;case 24:Ca(Fa)}}function gs(e,t){try{var n=t.updateQueue,r=null!==n?n.lastEffect:null;if(null!==r){var a=r.next;n=a;do{if((n.tag&e)===e){r=void 0;var o=n.create,l=n.inst;r=o(),l.destroy=r}n=n.next}while(n!==a)}}catch(i){Su(t,t.return,i)}}function vs(e,t,n){try{var r=t.updateQueue,a=null!==r?r.lastEffect:null;if(null!==a){var o=a.next;r=o;do{if((r.tag&e)===e){var l=r.inst,i=l.destroy;if(void 0!==i){l.destroy=void 0,a=t;var s=n,c=i;try{c()}catch(u){Su(a,s,u)}}}r=r.next}while(r!==o)}}catch(u){Su(t,t.return,u)}}function ys(e){var t=e.updateQueue;if(null!==t){var n=e.stateNode;try{jo(t,n)}catch(r){Su(e,e.return,r)}}}function bs(e,t,n){n.props=Si(e.type,e.memoizedProps),n.state=e.memoizedState;try{n.componentWillUnmount()}catch(r){Su(e,t,r)}}function ws(e,t){try{var n=e.ref;if(null!==n){switch(e.tag){case 26:case 27:case 5:var r=e.stateNode;break;default:r=e.stateNode}"function"===typeof n?e.refCleanup=n(r):n.current=r}}catch(a){Su(e,t,a)}}function xs(e,t){var n=e.ref,r=e.refCleanup;if(null!==n)if("function"===typeof r)try{r()}catch(a){Su(e,t,a)}finally{e.refCleanup=null,null!=(e=e.alternate)&&(e.refCleanup=null)}else if("function"===typeof n)try{n(null)}catch(o){Su(e,t,o)}else n.current=null}function ks(e){var t=e.type,n=e.memoizedProps,r=e.stateNode;try{e:switch(t){case"button":case"input":case"select":case"textarea":n.autoFocus&&r.focus();break e;case"img":n.src?r.src=n.src:n.srcSet&&(r.srcset=n.srcSet)}}catch(a){Su(e,e.return,a)}}function Ss(e,t,n){try{var r=e.stateNode;!function(e,t,n,r){switch(t){case"div":case"span":case"svg":case"path":case"a":case"g":case"p":case"li":break;case"input":var a=null,o=null,i=null,s=null,c=null,u=null,d=null;for(m in n){var f=n[m];if(n.hasOwnProperty(m)&&null!=f)switch(m){case"checked":case"value":break;case"defaultValue":c=f;default:r.hasOwnProperty(m)||ud(e,t,m,null,r,f)}}for(var p in r){var m=r[p];if(f=n[p],r.hasOwnProperty(p)&&(null!=m||null!=f))switch(p){case"type":o=m;break;case"name":a=m;break;case"checked":u=m;break;case"defaultChecked":d=m;break;case"value":i=m;break;case"defaultValue":s=m;break;case"children":case"dangerouslySetInnerHTML":if(null!=m)throw Error(l(137,t));break;default:m!==f&&ud(e,t,p,m,r,f)}}return void yt(e,i,s,c,u,d,o,a);case"select":for(o in m=i=s=p=null,n)if(c=n[o],n.hasOwnProperty(o)&&null!=c)switch(o){case"value":break;case"multiple":m=c;default:r.hasOwnProperty(o)||ud(e,t,o,null,r,c)}for(a in r)if(o=r[a],c=n[a],r.hasOwnProperty(a)&&(null!=o||null!=c))switch(a){case"value":p=o;break;case"defaultValue":s=o;break;case"multiple":i=o;default:o!==c&&ud(e,t,a,o,r,c)}return t=s,n=i,r=m,void(null!=p?xt(e,!!n,p,!1):!!r!==!!n&&(null!=t?xt(e,!!n,t,!0):xt(e,!!n,n?[]:"",!1)));case"textarea":for(s in m=p=null,n)if(a=n[s],n.hasOwnProperty(s)&&null!=a&&!r.hasOwnProperty(s))switch(s){case"value":case"children":break;default:ud(e,t,s,null,r,a)}for(i in r)if(a=r[i],o=n[i],r.hasOwnProperty(i)&&(null!=a||null!=o))switch(i){case"value":p=a;break;case"defaultValue":m=a;break;case"children":break;case"dangerouslySetInnerHTML":if(null!=a)throw Error(l(91));break;default:a!==o&&ud(e,t,i,a,r,o)}return void kt(e,p,m);case"option":for(var h in n)if(p=n[h],n.hasOwnProperty(h)&&null!=p&&!r.hasOwnProperty(h))if("selected"===h)e.selected=!1;else ud(e,t,h,null,r,p);for(c in r)if(p=r[c],m=n[c],r.hasOwnProperty(c)&&p!==m&&(null!=p||null!=m))if("selected"===c)e.selected=p&&"function"!==typeof p&&"symbol"!==typeof p;else ud(e,t,c,p,r,m);return;case"img":case"link":case"area":case"base":case"br":case"col":case"embed":case"hr":case"keygen":case"meta":case"param":case"source":case"track":case"wbr":case"menuitem":for(var g in n)p=n[g],n.hasOwnProperty(g)&&null!=p&&!r.hasOwnProperty(g)&&ud(e,t,g,null,r,p);for(u in r)if(p=r[u],m=n[u],r.hasOwnProperty(u)&&p!==m&&(null!=p||null!=m))switch(u){case"children":case"dangerouslySetInnerHTML":if(null!=p)throw Error(l(137,t));break;default:ud(e,t,u,p,r,m)}return;default:if(Rt(t)){for(var v in n)p=n[v],n.hasOwnProperty(v)&&void 0!==p&&!r.hasOwnProperty(v)&&dd(e,t,v,void 0,r,p);for(d in r)p=r[d],m=n[d],!r.hasOwnProperty(d)||p===m||void 0===p&&void 0===m||dd(e,t,d,p,r,m);return}}for(var y in n)p=n[y],n.hasOwnProperty(y)&&null!=p&&!r.hasOwnProperty(y)&&ud(e,t,y,null,r,p);for(f in r)p=r[f],m=n[f],!r.hasOwnProperty(f)||p===m||null==p&&null==m||ud(e,t,f,p,r,m)}(r,e.type,n,t),r[He]=t}catch(a){Su(e,e.return,a)}}function Es(e){return 5===e.tag||3===e.tag||26===e.tag||27===e.tag&&Cd(e.type)||4===e.tag}function Ns(e){e:for(;;){for(;null===e.sibling;){if(null===e.return||Es(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;5!==e.tag&&6!==e.tag&&18!==e.tag;){if(27===e.tag&&Cd(e.type))continue e;if(2&e.flags)continue e;if(null===e.child||4===e.tag)continue e;e.child.return=e,e=e.child}if(!(2&e.flags))return e.stateNode}}function Cs(e,t,n){var r=e.tag;if(5===r||6===r)e=e.stateNode,t?(9===n.nodeType?n.body:"HTML"===n.nodeName?n.ownerDocument.body:n).insertBefore(e,t):((t=9===n.nodeType?n.body:"HTML"===n.nodeName?n.ownerDocument.body:n).appendChild(e),null!==(n=n._reactRootContainer)&&void 0!==n||null!==t.onclick||(t.onclick=Ot));else if(4!==r&&(27===r&&Cd(e.type)&&(n=e.stateNode,t=null),null!==(e=e.child)))for(Cs(e,t,n),e=e.sibling;null!==e;)Cs(e,t,n),e=e.sibling}function js(e,t,n){var r=e.tag;if(5===r||6===r)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(4!==r&&(27===r&&Cd(e.type)&&(n=e.stateNode),null!==(e=e.child)))for(js(e,t,n),e=e.sibling;null!==e;)js(e,t,n),e=e.sibling}function Rs(e){var t=e.stateNode,n=e.memoizedProps;try{for(var r=e.type,a=t.attributes;a.length;)t.removeAttributeNode(a[0]);fd(t,r,n),t[Ue]=e,t[He]=n}catch(o){Su(e,e.return,o)}}var Ts=!1,Ps=!1,_s=!1,Os="function"===typeof WeakSet?WeakSet:Set,As=null;function Ls(e,t,n){var r=n.flags;switch(n.tag){case 0:case 11:case 15:Ys(e,n),4&r&&gs(5,n);break;case 1:if(Ys(e,n),4&r)if(e=n.stateNode,null===t)try{e.componentDidMount()}catch(l){Su(n,n.return,l)}else{var a=Si(n.type,t.memoizedProps);t=t.memoizedState;try{e.componentDidUpdate(a,t,e.__reactInternalSnapshotBeforeUpdate)}catch(i){Su(n,n.return,i)}}64&r&&ys(n),512&r&&ws(n,n.return);break;case 3:if(Ys(e,n),64&r&&null!==(e=n.updateQueue)){if(t=null,null!==n.child)switch(n.child.tag){case 27:case 5:case 1:t=n.child.stateNode}try{jo(e,t)}catch(l){Su(n,n.return,l)}}break;case 27:null===t&&4&r&&Rs(n);case 26:case 5:Ys(e,n),null===t&&4&r&&ks(n),512&r&&ws(n,n.return);break;case 12:Ys(e,n);break;case 31:Ys(e,n),4&r&&Bs(e,n);break;case 13:Ys(e,n),4&r&&Us(e,n),64&r&&(null!==(e=n.memoizedState)&&(null!==(e=e.dehydrated)&&function(e,t){var n=e.ownerDocument;if("$~"===e.data)e._reactRetry=t;else if("$?"!==e.data||"loading"!==n.readyState)t();else{var r=function(){t(),n.removeEventListener("DOMContentLoaded",r)};n.addEventListener("DOMContentLoaded",r),e._reactRetry=r}}(e,n=ju.bind(null,n))));break;case 22:if(!(r=null!==n.memoizedState||Ts)){t=null!==t&&null!==t.memoizedState||Ps,a=Ts;var o=Ps;Ts=r,(Ps=t)&&!o?Gs(e,n,0!==(8772&n.subtreeFlags)):Ys(e,n),Ts=a,Ps=o}break;case 30:break;default:Ys(e,n)}}function Ds(e){var t=e.alternate;null!==t&&(e.alternate=null,Ds(t)),e.child=null,e.deletions=null,e.sibling=null,5===e.tag&&(null!==(t=e.stateNode)&&Qe(t)),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}var zs=null,Ms=!1;function Fs(e,t,n){for(n=n.child;null!==n;)Is(e,t,n),n=n.sibling}function Is(e,t,n){if(ye&&"function"===typeof ye.onCommitFiberUnmount)try{ye.onCommitFiberUnmount(ve,n)}catch(o){}switch(n.tag){case 26:Ps||xs(n,t),Fs(e,t,n),n.memoizedState?n.memoizedState.count--:n.stateNode&&(n=n.stateNode).parentNode.removeChild(n);break;case 27:Ps||xs(n,t);var r=zs,a=Ms;Cd(n.type)&&(zs=n.stateNode,Ms=!1),Fs(e,t,n),Fd(n.stateNode),zs=r,Ms=a;break;case 5:Ps||xs(n,t);case 6:if(r=zs,a=Ms,zs=null,Fs(e,t,n),Ms=a,null!==(zs=r))if(Ms)try{(9===zs.nodeType?zs.body:"HTML"===zs.nodeName?zs.ownerDocument.body:zs).removeChild(n.stateNode)}catch(l){Su(n,t,l)}else try{zs.removeChild(n.stateNode)}catch(l){Su(n,t,l)}break;case 18:null!==zs&&(Ms?(jd(9===(e=zs).nodeType?e.body:"HTML"===e.nodeName?e.ownerDocument.body:e,n.stateNode),$f(e)):jd(zs,n.stateNode));break;case 4:r=zs,a=Ms,zs=n.stateNode.containerInfo,Ms=!0,Fs(e,t,n),zs=r,Ms=a;break;case 0:case 11:case 14:case 15:vs(2,n,t),Ps||vs(4,n,t),Fs(e,t,n);break;case 1:Ps||(xs(n,t),"function"===typeof(r=n.stateNode).componentWillUnmount&&bs(n,t,r)),Fs(e,t,n);break;case 21:Fs(e,t,n);break;case 22:Ps=(r=Ps)||null!==n.memoizedState,Fs(e,t,n),Ps=r;break;default:Fs(e,t,n)}}function Bs(e,t){if(null===t.memoizedState&&(null!==(e=t.alternate)&&null!==(e=e.memoizedState))){e=e.dehydrated;try{$f(e)}catch(n){Su(t,t.return,n)}}}function Us(e,t){if(null===t.memoizedState&&(null!==(e=t.alternate)&&(null!==(e=e.memoizedState)&&null!==(e=e.dehydrated))))try{$f(e)}catch(n){Su(t,t.return,n)}}function Hs(e,t){var n=function(e){switch(e.tag){case 31:case 13:case 19:var t=e.stateNode;return null===t&&(t=e.stateNode=new Os),t;case 22:return null===(t=(e=e.stateNode)._retryCache)&&(t=e._retryCache=new Os),t;default:throw Error(l(435,e.tag))}}(e);t.forEach(function(t){if(!n.has(t)){n.add(t);var r=Ru.bind(null,e,t);t.then(r,r)}})}function Ws(e,t){var n=t.deletions;if(null!==n)for(var r=0;r title"))),fd(o,r,n),o[Ue]=e,et(o),r=o;break e;case"link":var i=nf("link","href",a).get(r+(n.href||""));if(i)for(var s=0;si)break;var u=s.transferSize,d=s.initiatorType;u&&pd(d)&&(l+=u*((s=s.responseEnd)of?50:800)+t);return e.unsuspend=n,function(){e.unsuspend=null,clearTimeout(r),clearTimeout(a)}}:null}(d,m),null!==m)return Bc=o,e.cancelPendingCommit=m(hu.bind(null,e,t,o,n,r,a,l,i,s,u,d,null,f,p)),void Jc(e,o,l,!c)}hu(e,t,o,n,r,a,l,i,s)}function Xc(e){for(var t=e;;){var n=t.tag;if((0===n||11===n||15===n)&&16384&t.flags&&(null!==(n=t.updateQueue)&&null!==(n=n.stores)))for(var r=0;rg&&(l=g,g=h,h=l);var v=tr(i,h),y=tr(i,g);if(v&&y&&(1!==p.rangeCount||p.anchorNode!==v.node||p.anchorOffset!==v.offset||p.focusNode!==y.node||p.focusOffset!==y.offset)){var b=d.createRange();b.setStart(v.node,v.offset),p.removeAllRanges(),h>g?(p.addRange(b),p.extend(y.node,y.offset)):(b.setEnd(y.node,y.offset),p.addRange(b))}}}}for(d=[],p=i;p=p.parentNode;)1===p.nodeType&&d.push({element:p,left:p.scrollLeft,top:p.scrollTop});for("function"===typeof i.focus&&i.focus(),i=0;in?32:n,L.T=null,n=Hc,Hc=null;var o=Fc,i=Bc;if(Mc=0,Ic=Fc=null,Bc=0,0!==(6&pc))throw Error(l(331));var s=pc;if(pc|=4,sc(o.current),ec(o,o.current,i,n),pc=s,zu(0,!1),ye&&"function"===typeof ye.onPostCommitFiberRoot)try{ye.onPostCommitFiberRoot(ve,o)}catch(c){}return!0}finally{D.p=a,L.T=r,bu(e,t)}}function ku(e,t,n){t=Yr(n,t),null!==(e=wo(e,t=Ti(e.stateNode,t,2),2))&&(Oe(e,2),Du(e))}function Su(e,t,n){if(3===e.tag)ku(e,e,n);else for(;null!==t;){if(3===t.tag){ku(t,e,n);break}if(1===t.tag){var r=t.stateNode;if("function"===typeof t.type.getDerivedStateFromError||"function"===typeof r.componentDidCatch&&(null===zc||!zc.has(r))){e=Yr(n,e),null!==(r=wo(t,n=Pi(2),2))&&(_i(n,r,t,e),Oe(r,2),Du(r));break}}t=t.return}}function Eu(e,t,n){var r=e.pingCache;if(null===r){r=e.pingCache=new fc;var a=new Set;r.set(t,a)}else void 0===(a=r.get(t))&&(a=new Set,r.set(t,a));a.has(n)||(xc=!0,a.add(n),e=Nu.bind(null,e,t,n),t.then(e,e))}function Nu(e,t,n){var r=e.pingCache;null!==r&&r.delete(t),e.pingedLanes|=e.suspendedLanes&n,e.warmLanes&=~n,mc===e&&(gc&n)===n&&(4===Sc||3===Sc&&(62914560&gc)===gc&&300>se()-Oc?0===(2&pc)&&tu(e,0):Cc|=n,Rc===gc&&(Rc=0)),Du(e)}function Cu(e,t){0===t&&(t=Pe()),null!==(e=Ar(e,t))&&(Oe(e,t),Du(e))}function ju(e){var t=e.memoizedState,n=0;null!==t&&(n=t.retryLane),Cu(e,n)}function Ru(e,t){var n=0;switch(e.tag){case 31:case 13:var r=e.stateNode,a=e.memoizedState;null!==a&&(n=a.retryLane);break;case 19:r=e.stateNode;break;case 22:r=e.stateNode._retryCache;break;default:throw Error(l(314))}null!==r&&r.delete(t),Cu(e,n)}var Tu=null,Pu=null,_u=!1,Ou=!1,Au=!1,Lu=0;function Du(e){e!==Pu&&null===e.next&&(null===Pu?Tu=Pu=e:Pu=Pu.next=e),Ou=!0,_u||(_u=!0,Ed(function(){0!==(6&pc)?ae(ue,Mu):Fu()}))}function zu(e,t){if(!Au&&Ou){Au=!0;do{for(var n=!1,r=Tu;null!==r;){if(!t)if(0!==e){var a=r.pendingLanes;if(0===a)var o=0;else{var l=r.suspendedLanes,i=r.pingedLanes;o=(1<<31-we(42|e)+1)-1,o=201326741&(o&=a&~(l&~i))?201326741&o|1:o?2|o:0}0!==o&&(n=!0,Uu(r,o))}else o=gc,0===(3&(o=je(r,r===mc?o:0,null!==r.cancelPendingCommit||-1!==r.timeoutHandle)))||Re(r,o)||(n=!0,Uu(r,o));r=r.next}}while(n);Au=!1}}function Mu(){Fu()}function Fu(){Ou=_u=!1;var e=0;0!==Lu&&function(){var e=window.event;if(e&&"popstate"===e.type)return e!==wd&&(wd=e,!0);return wd=null,!1}()&&(e=Lu);for(var t=se(),n=null,r=Tu;null!==r;){var a=r.next,o=Iu(r,t);0===o?(r.next=null,null===n?Tu=a:n.next=a,null===a&&(Pu=n)):(n=r,(0!==e||0!==(3&o))&&(Ou=!0)),r=a}0!==Mc&&5!==Mc||zu(e,!1),0!==Lu&&(Lu=0)}function Iu(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,a=e.expirationTimes,o=-62914561&e.pendingLanes;0 title"):null)}function af(e){return"stylesheet"!==e.type||0!==(3&e.state.loading)}var of=0;function lf(){if(this.count--,0===this.count&&(0===this.imgCount||!this.waitingForImages))if(this.stylesheets)cf(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}var sf=null;function cf(e,t){e.stylesheets=null,null!==e.unsuspend&&(e.count++,sf=new Map,t.forEach(uf,e),sf=null,lf.call(e))}function uf(e,t){if(!(4&t.state.loading)){var n=sf.get(e);if(n)var r=n.get(null);else{n=new Map,sf.set(e,n);for(var a=e.querySelectorAll("link[data-precedence],style[data-precedence]"),o=0;o>>1,a=e[r];if(!(0>>1;ro(s,n))co(u,s)?(e[r]=u,e[c]=n,r=c):(e[r]=s,e[i]=n,r=i);else{if(!(co(u,n)))break e;e[r]=u,e[c]=n,r=c}}}return t}function o(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}if(t.unstable_now=void 0,"object"===typeof performance&&"function"===typeof performance.now){var l=performance;t.unstable_now=function(){return l.now()}}else{var i=Date,s=i.now();t.unstable_now=function(){return i.now()-s}}var c=[],u=[],d=1,f=null,p=3,m=!1,h=!1,g=!1,v=!1,y="function"===typeof setTimeout?setTimeout:null,b="function"===typeof clearTimeout?clearTimeout:null,w="undefined"!==typeof setImmediate?setImmediate:null;function x(e){for(var t=r(u);null!==t;){if(null===t.callback)a(u);else{if(!(t.startTime<=e))break;a(u),t.sortIndex=t.expirationTime,n(c,t)}t=r(u)}}function k(e){if(g=!1,x(e),!h)if(null!==r(c))h=!0,E||(E=!0,S());else{var t=r(u);null!==t&&O(k,t.startTime-e)}}var S,E=!1,N=-1,C=5,j=-1;function R(){return!!v||!(t.unstable_now()-je&&R());){var l=f.callback;if("function"===typeof l){f.callback=null,p=f.priorityLevel;var i=l(f.expirationTime<=e);if(e=t.unstable_now(),"function"===typeof i){f.callback=i,x(e),n=!0;break t}f===r(c)&&a(c),x(e)}else a(c);f=r(c)}if(null!==f)n=!0;else{var s=r(u);null!==s&&O(k,s.startTime-e),n=!1}}break e}finally{f=null,p=o,m=!1}n=void 0}}finally{n?S():E=!1}}}if("function"===typeof w)S=function(){w(T)};else if("undefined"!==typeof MessageChannel){var P=new MessageChannel,_=P.port2;P.port1.onmessage=T,S=function(){_.postMessage(null)}}else S=function(){y(T,0)};function O(e,n){N=y(function(){e(t.unstable_now())},n)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_forceFrameRate=function(e){0>e||125l?(e.sortIndex=o,n(u,e),null===r(c)&&e===r(u)&&(g?(b(N),N=-1):g=!0,O(k,o-l))):(e.sortIndex=i,n(c,e),h||m||(h=!0,E||(E=!0,S()))),e},t.unstable_shouldYield=R,t.unstable_wrapCallback=function(e){var t=p;return function(){var n=p;p=t;try{return e.apply(this,arguments)}finally{p=n}}}},950(e,t,n){!function e(){if("undefined"!==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"===typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE)try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}(),e.exports=n(672)}},t={};function n(r){var a=t[r];if(void 0!==a)return a.exports;var o=t[r]={exports:{}};return e[r](o,o.exports,n),o.exports}(()=>{var e,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;n.t=function(r,a){if(1&a&&(r=this(r)),8&a)return r;if("object"===typeof r&&r){if(4&a&&r.__esModule)return r;if(16&a&&"function"===typeof r.then)return r}var o=Object.create(null);n.r(o);var l={};e=e||[null,t({}),t([]),t(t)];for(var i=2&a&&r;("object"==typeof i||"function"==typeof i)&&!~e.indexOf(i);i=t(i))Object.getOwnPropertyNames(i).forEach(e=>l[e]=()=>r[e]);return l.default=()=>r,n.d(o,l),o}})(),n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.g=function(){if("object"===typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"===typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.nc=void 0;var r={};n.r(r),n.d(r,{hasBrowserEnv:()=>Ja,hasStandardBrowserEnv:()=>eo,hasStandardBrowserWebWorkerEnv:()=>to,navigator:()=>Za,origin:()=>no});var a=n(43),o=n.t(a,2),l=n(391);function i(e,t){if(null==e)return{};var n,r,a=function(e,t){if(null==e)return{};var n={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(-1!==t.indexOf(r))continue;n[r]=e[r]}return n}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r0&&void 0!==arguments[0]?arguments[0]:{})}function x(e,t){if(!1===e||null===e||"undefined"===typeof e)throw new Error(t)}function k(e,t){if(!e){"undefined"!==typeof console&&console.warn(t);try{throw new Error(t)}catch(n){}}}function S(e,t){return{usr:e.state,key:e.key,idx:t}}function E(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=arguments.length>3?arguments[3]:void 0;return f(f({pathname:"string"===typeof e?e:e.pathname,search:"",hash:""},"string"===typeof t?C(t):t),{},{state:n,key:t&&t.key||r||Math.random().toString(36).substring(2,10)})}function N(e){let{pathname:t="/",search:n="",hash:r=""}=e;return n&&"?"!==n&&(t+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(t+="#"===r.charAt(0)?r:"#"+r),t}function C(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substring(n),e=e.substring(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substring(r),e=e.substring(0,r)),e&&(t.pathname=e)}return t}function j(e,t,n){let r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},{window:a=document.defaultView,v5Compat:o=!1}=r,l=a.history,i="POP",s=null,c=u();function u(){return(l.state||{idx:null}).idx}function d(){i="POP";let e=u(),t=null==e?null:e-c;c=e,s&&s({action:i,location:m.location,delta:t})}function p(e){return R(e)}null==c&&(c=0,l.replaceState(f(f({},l.state),{},{idx:c}),""));let m={get action(){return i},get location(){return e(a,l)},listen(e){if(s)throw new Error("A history only accepts one active listener");return a.addEventListener(b,d),s=e,()=>{a.removeEventListener(b,d),s=null}},createHref:e=>t(a,e),createURL:p,encodeLocation(e){let t=p(e);return{pathname:t.pathname,search:t.search,hash:t.hash}},push:function(e,t){i="PUSH";let r=E(m.location,e,t);n&&n(r,e),c=u()+1;let d=S(r,c),f=m.createHref(r);try{l.pushState(d,"",f)}catch(p){if(p instanceof DOMException&&"DataCloneError"===p.name)throw p;a.location.assign(f)}o&&s&&s({action:i,location:m.location,delta:1})},replace:function(e,t){i="REPLACE";let r=E(m.location,e,t);n&&n(r,e),c=u();let a=S(r,c),d=m.createHref(r);l.replaceState(a,"",d),o&&s&&s({action:i,location:m.location,delta:0})},go:e=>l.go(e)};return m}function R(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n="http://localhost";"undefined"!==typeof window&&(n="null"!==window.location.origin?window.location.origin:window.location.href),x(n,"No window.location.(origin|href) available to create URL");let r="string"===typeof e?e:N(e);return r=r.replace(/ $/,"%20"),!t&&r.startsWith("//")&&(r=n+r),new URL(r,n)}new WeakMap;function T(e,t){return P(e,t,arguments.length>2&&void 0!==arguments[2]?arguments[2]:"/",!1)}function P(e,t,n,r){let a=$(("string"===typeof t?C(t):t).pathname||"/",n);if(null==a)return null;let o=_(e);!function(e){e.sort((e,t)=>e.score!==t.score?t.score-e.score:function(e,t){let n=e.length===t.length&&e.slice(0,-1).every((e,n)=>e===t[n]);return n?e[e.length-1]-t[t.length-1]:0}(e.routesMeta.map(e=>e.childrenIndex),t.routesMeta.map(e=>e.childrenIndex)))}(o);let l=null;for(let i=0;null==l&&i1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"",a=arguments.length>4&&void 0!==arguments[4]&&arguments[4],o=function(e,o){let l=arguments.length>2&&void 0!==arguments[2]?arguments[2]:a,i=arguments.length>3?arguments[3]:void 0,s={relativePath:void 0===i?e.path||"":i,caseSensitive:!0===e.caseSensitive,childrenIndex:o,route:e};if(s.relativePath.startsWith("/")){if(!s.relativePath.startsWith(r)&&l)return;x(s.relativePath.startsWith(r),'Absolute route path "'.concat(s.relativePath,'" nested under path "').concat(r,'" is not valid. An absolute child route path must start with the combined path of all its parent routes.')),s.relativePath=s.relativePath.slice(r.length)}let c=Z([r,s.relativePath]),u=n.concat(s);e.children&&e.children.length>0&&(x(!0!==e.index,'Index routes must not have child routes. Please remove all child routes from route path "'.concat(c,'".')),_(e.children,t,u,c,l)),(null!=e.path||e.index)&&t.push({path:c,score:B(c,e.index),routesMeta:u})};return e.forEach((e,t)=>{var n;if(""!==e.path&&null!==(n=e.path)&&void 0!==n&&n.includes("?"))for(let r of O(e.path))o(e,t,!0,r);else o(e,t)}),t}function O(e){let t=e.split("/");if(0===t.length)return[];let[n,...r]=t,a=n.endsWith("?"),o=n.replace(/\?$/,"");if(0===r.length)return a?[o,""]:[o];let l=O(r.join("/")),i=[];return i.push(...l.map(e=>""===e?o:[o,e].join("/"))),a&&i.push(...l),i.map(t=>e.startsWith("/")&&""===t?"/":t)}var A=/^:[\w-]+$/,L=3,D=2,z=1,M=10,F=-2,I=e=>"*"===e;function B(e,t){let n=e.split("/"),r=n.length;return n.some(I)&&(r+=F),t&&(r+=D),n.filter(e=>!I(e)).reduce((e,t)=>e+(A.test(t)?L:""===t?z:M),r)}function U(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],{routesMeta:r}=e,a={},o="/",l=[];for(let i=0;i{let{paramName:r,isOptional:a}=t;if("*"===r){let e=i[n]||"";l=o.slice(0,o.length-e.length).replace(/(.)\/+$/,"$1")}const s=i[n];return e[r]=a&&!s?void 0:(s||"").replace(/%2F/g,"/"),e},{});return{params:s,pathname:o,pathnameBase:l,pattern:e}}function W(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];k("*"===e||!e.endsWith("*")||e.endsWith("/*"),'Route path "'.concat(e,'" will be treated as if it were "').concat(e.replace(/\*$/,"/*"),'" because the `*` character must always follow a `/` in the pattern. To get rid of this warning, please change the route path to "').concat(e.replace(/\*$/,"/*"),'".'));let r=[],a="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(e,t,n)=>(r.push({paramName:t,isOptional:null!=n}),n?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return e.endsWith("*")?(r.push({paramName:"*"}),a+="*"===e||"/*"===e?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?a+="\\/*$":""!==e&&"/"!==e&&(a+="(?:(?=\\/|$))"),[new RegExp(a,t?void 0:"i"),r]}function V(e){try{return e.split("/").map(e=>decodeURIComponent(e).replace(/\//g,"%2F")).join("/")}catch(t){return k(!1,'The URL path "'.concat(e,'" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (').concat(t,").")),e}}function $(e,t){if("/"===t)return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&"/"!==r?null:e.slice(n)||"/"}var q=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,K=e=>q.test(e);function Y(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(e=>{".."===e?n.length>1&&n.pop():"."!==e&&n.push(e)}),n.length>1?n.join("/"):"/"}function Q(e,t,n,r){return"Cannot include a '".concat(e,"' character in a manually specified `to.").concat(t,"` field [").concat(JSON.stringify(r),"]. Please separate it out to the `to.").concat(n,'` field. Alternatively you may provide the full path as a string in and the router will parse it for you.')}function G(e){return e.filter((e,t)=>0===t||e.route.path&&e.route.path.length>0)}function X(e){let t=G(e);return t.map((e,n)=>n===t.length-1?e.pathname:e.pathnameBase)}function J(e,t,n){let r,a=arguments.length>3&&void 0!==arguments[3]&&arguments[3];"string"===typeof e?r=C(e):(r=f({},e),x(!r.pathname||!r.pathname.includes("?"),Q("?","pathname","search",r)),x(!r.pathname||!r.pathname.includes("#"),Q("#","pathname","hash",r)),x(!r.search||!r.search.includes("#"),Q("#","search","hash",r)));let o,l=""===e||""===r.pathname,i=l?"/":r.pathname;if(null==i)o=n;else{let e=t.length-1;if(!a&&i.startsWith("..")){let t=i.split("/");for(;".."===t[0];)t.shift(),e-=1;r.pathname=t.join("/")}o=e>=0?t[e]:"/"}let s=function(e){let t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"/",{pathname:r,search:a="",hash:o=""}="string"===typeof e?C(e):e;if(r)if(K(r))t=r;else{if(r.includes("//")){let e=r;r=r.replace(/\/\/+/g,"/"),k(!1,"Pathnames cannot have embedded double slashes - normalizing ".concat(e," -> ").concat(r))}t=r.startsWith("/")?Y(r.substring(1),"/"):Y(r,n)}else t=n;return{pathname:t,search:te(a),hash:ne(o)}}(r,o),c=i&&"/"!==i&&i.endsWith("/"),u=(l||"."===i)&&n.endsWith("/");return s.pathname.endsWith("/")||!c&&!u||(s.pathname+="/"),s}var Z=e=>e.join("/").replace(/\/\/+/g,"/"),ee=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),te=e=>e&&"?"!==e?e.startsWith("?")?e:"?"+e:"",ne=e=>e&&"#"!==e?e.startsWith("#")?e:"#"+e:"";var re=class{constructor(e,t,n){let r=arguments.length>3&&void 0!==arguments[3]&&arguments[3];this.status=e,this.statusText=t||"",this.internal=r,n instanceof Error?(this.data=n.toString(),this.error=n):this.data=n}};function ae(e){return null!=e&&"number"===typeof e.status&&"string"===typeof e.statusText&&"boolean"===typeof e.internal&&"data"in e}function oe(e){return e.map(e=>e.route.path).filter(Boolean).join("/").replace(/\/\/*/g,"/")||"/"}var le="undefined"!==typeof window&&"undefined"!==typeof window.document&&"undefined"!==typeof window.document.createElement;function ie(e,t){let n=e;if("string"!==typeof n||!q.test(n))return{absoluteURL:void 0,isExternal:!1,to:n};let r=n,a=!1;if(le)try{let e=new URL(window.location.href),r=n.startsWith("//")?new URL(e.protocol+n):new URL(n),o=$(r.pathname,t);r.origin===e.origin&&null!=o?n=o+r.search+r.hash:a=!0}catch(o){k(!1,' contains an invalid URL which will probably break when clicked - please update to a valid URL path.'))}return{absoluteURL:r,isExternal:a,to:n}}Symbol("Uninstrumented");Object.getOwnPropertyNames(Object.prototype).sort().join("\0");var se=["POST","PUT","PATCH","DELETE"],ce=(new Set(se),["GET",...se]);new Set(ce),Symbol("ResetLoaderData");var ue=a.createContext(null);ue.displayName="DataRouter";var de=a.createContext(null);de.displayName="DataRouterState";var fe=a.createContext(!1);function pe(){return a.useContext(fe)}var me=a.createContext({isTransitioning:!1});me.displayName="ViewTransition";var he=a.createContext(new Map);he.displayName="Fetchers";var ge=a.createContext(null);ge.displayName="Await";var ve=a.createContext(null);ve.displayName="Navigation";var ye=a.createContext(null);ye.displayName="Location";var be=a.createContext({outlet:null,matches:[],isDataRoute:!1});be.displayName="Route";var we=a.createContext(null);we.displayName="RouteError";var xe="REACT_ROUTER_ERROR";function ke(){return null!=a.useContext(ye)}function Se(){return x(ke(),"useLocation() may be used only in the context of a component."),a.useContext(ye).location}var Ee="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function Ne(e){a.useContext(ve).static||a.useLayoutEffect(e)}function Ce(){let{isDataRoute:e}=a.useContext(be);return e?function(){let{router:e}=Me("useNavigate"),t=Ie("useNavigate"),n=a.useRef(!1);Ne(()=>{n.current=!0});let r=a.useCallback(async function(r){let a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};k(n.current,Ee),n.current&&("number"===typeof r?await e.navigate(r):await e.navigate(r,f({fromRouteId:t},a)))},[e,t]);return r}():function(){x(ke(),"useNavigate() may be used only in the context of a component.");let e=a.useContext(ue),{basename:t,navigator:n}=a.useContext(ve),{matches:r}=a.useContext(be),{pathname:o}=Se(),l=JSON.stringify(X(r)),i=a.useRef(!1);Ne(()=>{i.current=!0});let s=a.useCallback(function(r){let a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(k(i.current,Ee),!i.current)return;if("number"===typeof r)return void n.go(r);let s=J(r,JSON.parse(l),o,"path"===a.relative);null==e&&"/"!==t&&(s.pathname="/"===s.pathname?t:Z([t,s.pathname])),(a.replace?n.replace:n.push)(s,a.state,a)},[t,n,l,o,e]);return s}()}a.createContext(null);function je(e){let{relative:t}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},{matches:n}=a.useContext(be),{pathname:r}=Se(),o=JSON.stringify(X(n));return a.useMemo(()=>J(e,JSON.parse(o),r,"path"===t),[e,o,r,t])}function Re(e,t,n,r,o){x(ke(),"useRoutes() may be used only in the context of a component.");let{navigator:l}=a.useContext(ve),{matches:i}=a.useContext(be),s=i[i.length-1],c=s?s.params:{},u=s?s.pathname:"/",d=s?s.pathnameBase:"/",p=s&&s.route;{let e=p&&p.path||"";He(u,!p||e.endsWith("*")||e.endsWith("*?"),'You rendered descendant (or called `useRoutes()`) at "'.concat(u,'" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won\'t match anymore and therefore the child routes will never render.\n\nPlease change the parent to .'))}let m,h=Se();if(t){var g;let e="string"===typeof t?C(t):t;x("/"===d||(null===(g=e.pathname)||void 0===g?void 0:g.startsWith(d)),'When overriding the location using `` or `useRoutes(routes, location)`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "'.concat(d,'" but pathname "').concat(e.pathname,'" was given in the `location` prop.')),m=e}else m=h;let v=m.pathname||"/",y=v;if("/"!==d){let e=d.replace(/^\//,"").split("/");y="/"+v.replace(/^\//,"").split("/").slice(e.length).join("/")}let b=T(e,{pathname:y});k(p||null!=b,'No routes matched location "'.concat(m.pathname).concat(m.search).concat(m.hash,'" ')),k(null==b||void 0!==b[b.length-1].route.element||void 0!==b[b.length-1].route.Component||void 0!==b[b.length-1].route.lazy,'Matched leaf route at location "'.concat(m.pathname).concat(m.search).concat(m.hash,'" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.'));let w=De(b&&b.map(e=>Object.assign({},e,{params:Object.assign({},c,e.params),pathname:Z([d,l.encodeLocation?l.encodeLocation(e.pathname.replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:e.pathname]),pathnameBase:"/"===e.pathnameBase?d:Z([d,l.encodeLocation?l.encodeLocation(e.pathnameBase.replace(/\?/g,"%3F").replace(/#/g,"%23")).pathname:e.pathnameBase])})),i,n,r,o);return t&&w?a.createElement(ye.Provider,{value:{location:f({pathname:"/",search:"",hash:"",state:null,key:"default"},m),navigationType:"POP"}},w):w}function Te(){let e=Be(),t=ae(e)?"".concat(e.status," ").concat(e.statusText):e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,r="rgba(200,200,200, 0.5)",o={padding:"0.5rem",backgroundColor:r},l={padding:"2px 4px",backgroundColor:r},i=null;return console.error("Error handled by React Router default ErrorBoundary:",e),i=a.createElement(a.Fragment,null,a.createElement("p",null,"\ud83d\udcbf Hey developer \ud83d\udc4b"),a.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",a.createElement("code",{style:l},"ErrorBoundary")," or"," ",a.createElement("code",{style:l},"errorElement")," prop on your route.")),a.createElement(a.Fragment,null,a.createElement("h2",null,"Unexpected Application Error!"),a.createElement("h3",{style:{fontStyle:"italic"}},t),n?a.createElement("pre",{style:o},n):null,i)}var Pe=a.createElement(Te,null),_e=class extends a.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||"idle"!==t.revalidation&&"idle"===e.revalidation?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:void 0!==e.error?e.error:t.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){this.props.onError?this.props.onError(e,t):console.error("React Router caught the following error during render",e)}render(){let e=this.state.error;if(this.context&&"object"===typeof e&&e&&"digest"in e&&"string"===typeof e.digest){const t=function(e){if(e.startsWith("".concat(xe,":").concat("ROUTE_ERROR_RESPONSE",":{")))try{let t=JSON.parse(e.slice(40));if("object"===typeof t&&t&&"number"===typeof t.status&&"string"===typeof t.statusText)return new re(t.status,t.statusText,t.data)}catch(t){}}(e.digest);t&&(e=t)}let t=void 0!==e?a.createElement(be.Provider,{value:this.props.routeContext},a.createElement(we.Provider,{value:e,children:this.props.component})):this.props.children;return this.context?a.createElement(Ae,{error:e},t):t}};_e.contextType=fe;var Oe=new WeakMap;function Ae(e){let{children:t,error:n}=e,{basename:r}=a.useContext(ve);if("object"===typeof n&&n&&"digest"in n&&"string"===typeof n.digest){let e=function(e){if(e.startsWith("".concat(xe,":").concat("REDIRECT",":{")))try{let t=JSON.parse(e.slice(28));if("object"===typeof t&&t&&"number"===typeof t.status&&"string"===typeof t.statusText&&"string"===typeof t.location&&"boolean"===typeof t.reloadDocument&&"boolean"===typeof t.replace)return t}catch(t){}}(n.digest);if(e){let t=Oe.get(n);if(t)throw t;let o=ie(e.location,r);if(le&&!Oe.get(n)){if(!o.isExternal&&!e.reloadDocument){const t=Promise.resolve().then(()=>window.__reactRouterDataRouter.navigate(o.to,{replace:e.replace}));throw Oe.set(n,t),t}window.location.href=o.absoluteURL||o.to}return a.createElement("meta",{httpEquiv:"refresh",content:"0;url=".concat(o.absoluteURL||o.to)})}}return t}function Le(e){let{routeContext:t,match:n,children:r}=e,o=a.useContext(ue);return o&&o.static&&o.staticContext&&(n.route.errorElement||n.route.ErrorBoundary)&&(o.staticContext._deepestRenderedBoundaryId=n.route.id),a.createElement(be.Provider,{value:t},r)}function De(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;if(null==e){if(!n)return null;if(n.errors)e=n.matches;else{if(0!==t.length||n.initialized||!(n.matches.length>0))return null;e=n.matches}}let o=e,l=null===n||void 0===n?void 0:n.errors;if(null!=l){let e=o.findIndex(e=>e.route.id&&void 0!==(null===l||void 0===l?void 0:l[e.route.id]));x(e>=0,"Could not find a matching route for errors on route IDs: ".concat(Object.keys(l).join(","))),o=o.slice(0,Math.min(o.length,e+1))}let i=!1,s=-1;if(n)for(let a=0;a=0?o.slice(0,s+1):[o[0]];break}}}let c=n&&r?(e,t)=>{var a,o;r(e,{location:n.location,params:null!==(a=null===(o=n.matches)||void 0===o||null===(o=o[0])||void 0===o?void 0:o.params)&&void 0!==a?a:{},unstable_pattern:oe(n.matches),errorInfo:t})}:void 0;return o.reduceRight((e,r,u)=>{let d,f=!1,p=null,m=null;n&&(d=l&&r.route.id?l[r.route.id]:void 0,p=r.route.errorElement||Pe,i&&(s<0&&0===u?(He("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),f=!0,m=null):s===u&&(f=!0,m=r.route.hydrateFallbackElement||null)));let h=t.concat(o.slice(0,u+1)),g=()=>{let t;return t=d?p:f?m:r.route.Component?a.createElement(r.route.Component,null):r.route.element?r.route.element:e,a.createElement(Le,{match:r,routeContext:{outlet:e,matches:h,isDataRoute:null!=n},children:t})};return n&&(r.route.ErrorBoundary||r.route.errorElement||0===u)?a.createElement(_e,{location:n.location,revalidation:n.revalidation,component:p,error:d,children:g(),routeContext:{outlet:null,matches:h,isDataRoute:!0},onError:c}):g()},null)}function ze(e){return"".concat(e," must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.")}function Me(e){let t=a.useContext(ue);return x(t,ze(e)),t}function Fe(e){let t=a.useContext(de);return x(t,ze(e)),t}function Ie(e){let t=function(e){let t=a.useContext(be);return x(t,ze(e)),t}(e),n=t.matches[t.matches.length-1];return x(n.route.id,"".concat(e,' can only be used on routes that contain a unique "id"')),n.route.id}function Be(){var e;let t=a.useContext(we),n=Fe("useRouteError"),r=Ie("useRouteError");return void 0!==t?t:null===(e=n.errors)||void 0===e?void 0:e[r]}var Ue={};function He(e,t,n){t||Ue[e]||(Ue[e]=!0,k(!1,n))}var We={};function Ve(e,t){e||We[t]||(We[t]=!0,console.warn(t))}o.useOptimistic;a.memo(function(e){let{routes:t,future:n,state:r,onError:a}=e;return Re(t,void 0,r,a,n)});function $e(e){x(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function qe(e){let{basename:t="/",children:n=null,location:r,navigationType:o="POP",navigator:l,static:i=!1,unstable_useTransitions:s}=e;x(!ke(),"You cannot render a inside another . You should never have more than one in your app.");let c=t.replace(/^\/*/,"/"),u=a.useMemo(()=>({basename:c,navigator:l,static:i,unstable_useTransitions:s,future:{}}),[c,l,i,s]);"string"===typeof r&&(r=C(r));let{pathname:d="/",search:f="",hash:p="",state:m=null,key:h="default"}=r,g=a.useMemo(()=>{let e=$(d,c);return null==e?null:{location:{pathname:e,search:f,hash:p,state:m,key:h},navigationType:o}},[c,d,f,p,m,h,o]);return k(null!=g,' is not able to match the URL "').concat(d).concat(f).concat(p,"\" because it does not start with the basename, so the won't render anything.")),null==g?null:a.createElement(ve.Provider,{value:u},a.createElement(ye.Provider,{children:n,value:g}))}function Ke(e){let{children:t,location:n}=e;return Re(Ye(t),n)}a.Component;function Ye(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=[];return a.Children.forEach(e,(e,r)=>{if(!a.isValidElement(e))return;let o=[...t,r];if(e.type===a.Fragment)return void n.push.apply(n,Ye(e.props.children,o));x(e.type===$e,"[".concat("string"===typeof e.type?e.type:e.type.name,"] is not a component. All component children of must be a or ")),x(!e.props.index||!e.props.children,"An index route cannot have child routes.");let l={id:e.props.id||o.join("-"),caseSensitive:e.props.caseSensitive,element:e.props.element,Component:e.props.Component,index:e.props.index,path:e.props.path,middleware:e.props.middleware,loader:e.props.loader,action:e.props.action,hydrateFallbackElement:e.props.hydrateFallbackElement,HydrateFallback:e.props.HydrateFallback,errorElement:e.props.errorElement,ErrorBoundary:e.props.ErrorBoundary,hasErrorBoundary:!0===e.props.hasErrorBoundary||null!=e.props.ErrorBoundary||null!=e.props.errorElement,shouldRevalidate:e.props.shouldRevalidate,handle:e.props.handle,lazy:e.props.lazy};e.props.children&&(l.children=Ye(e.props.children,o)),n.push(l)}),n}var Qe="get",Ge="application/x-www-form-urlencoded";function Xe(e){return"undefined"!==typeof HTMLElement&&e instanceof HTMLElement}var Je=null;var Ze=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function et(e){return null==e||Ze.has(e)?e:(k(!1,'"'.concat(e,'" is not a valid `encType` for `
`/`` and will default to "').concat(Ge,'"')),null)}function tt(e,t){let n,r,a,o,l;if(Xe(i=e)&&"form"===i.tagName.toLowerCase()){let l=e.getAttribute("action");r=l?$(l,t):null,n=e.getAttribute("method")||Qe,a=et(e.getAttribute("enctype"))||Ge,o=new FormData(e)}else if(function(e){return Xe(e)&&"button"===e.tagName.toLowerCase()}(e)||function(e){return Xe(e)&&"input"===e.tagName.toLowerCase()}(e)&&("submit"===e.type||"image"===e.type)){let l=e.form;if(null==l)throw new Error('Cannot submit a