This commit is contained in:
knuthtimo-lab 2025-09-15 17:04:09 +02:00
parent ecce65db79
commit 3cbe01e458
21 changed files with 1568 additions and 92 deletions

103
.gitignore vendored
View File

@ -1,24 +1,113 @@
# Logs # Dependencies
logs node_modules/
*.log
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log* pnpm-debug.log*
lerna-debug.log* lerna-debug.log*
node_modules # Build outputs
dist/
build/
.vite/
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# IDE and editor files
.vscode/
.idea/
*.swp
*.swo
*~
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Coverage directory used by tools like istanbul
coverage/
*.lcov
# nyc test coverage
.nyc_output
# Dependency directories
jspm_packages/
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist dist
dist-ssr
*.local # Gatsby files
.cache/
public
# Storybook build outputs
.out
.storybook-out
# Temporary folders
tmp/
temp/
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
!.vscode/extensions.json !.vscode/extensions.json
.idea .idea
.DS_Store
*.suo *.suo
*.ntvs* *.ntvs*
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
# Docker
.dockerignore
# SSL certificates
*.pem
*.key
*.crt
ssl/
# Backup files
*.bak
*.backup

46
Dockerfile Normal file
View File

@ -0,0 +1,46 @@
# Use Node.js 18 Alpine as base image
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json package-lock.json* ./
RUN npm ci --only=production
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Build the application
RUN npm run build
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Copy the built application
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/public ./public
# Change ownership to nextjs user
RUN chown -R nextjs:nodejs /app
USER nextjs
EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
# Start the application
CMD ["npx", "serve", "-s", "dist", "-l", "3000"]

17
Dockerfile.dev Normal file
View File

@ -0,0 +1,17 @@
# Development Dockerfile
FROM node:18-alpine
WORKDIR /app
# Install dependencies
COPY package.json package-lock.json* ./
RUN npm ci
# Copy source code
COPY . .
# Expose port
EXPOSE 8080
# Start development server
CMD ["npm", "run", "dev"]

118
README-Docker.md Normal file
View File

@ -0,0 +1,118 @@
# Docker Setup für IIT Welders
Dieses Projekt kann mit Docker und Docker Compose ausgeführt werden.
## Voraussetzungen
- Docker
- Docker Compose
## Entwicklung
### Mit Docker Compose (empfohlen)
```bash
# Entwicklungsumgebung starten
docker-compose -f docker-compose.dev.yml up
# Im Hintergrund ausführen
docker-compose -f docker-compose.dev.yml up -d
# Logs anzeigen
docker-compose -f docker-compose.dev.yml logs -f
# Stoppen
docker-compose -f docker-compose.dev.yml down
```
Die Anwendung ist dann unter `http://localhost:8080` erreichbar.
### Manuell mit Docker
```bash
# Development Image bauen
docker build -f Dockerfile.dev -t iitwelders-dev .
# Container starten
docker run -p 8080:8080 -v $(pwd):/app -v /app/node_modules iitwelders-dev
```
## Produktion
### Mit Docker Compose
```bash
# Produktionsumgebung starten
docker-compose up -d
# Logs anzeigen
docker-compose logs -f
# Stoppen
docker-compose down
```
Die Anwendung ist dann unter `http://localhost:3000` erreichbar.
### Mit Nginx (optional)
```bash
# Mit Nginx Reverse Proxy
docker-compose up -d
# Anwendung ist dann unter http://localhost erreichbar
```
### Manuell mit Docker
```bash
# Produktions Image bauen
docker build -t iitwelders-prod .
# Container starten
docker run -p 3000:3000 iitwelders-prod
```
## Nützliche Befehle
```bash
# Container neu bauen
docker-compose build --no-cache
# Alle Container und Volumes entfernen
docker-compose down -v
# In laufenden Container einsteigen
docker exec -it iitwelders-web sh
# Images aufräumen
docker system prune -a
```
## Umgebungsvariablen
Kopieren Sie `.env.example` zu `.env` und passen Sie die Werte an:
```bash
cp .env.example .env
```
## Troubleshooting
### Port bereits belegt
Falls Port 3000 oder 8080 bereits belegt ist, ändern Sie die Ports in der entsprechenden docker-compose.yml Datei.
### Node Modules Probleme
```bash
# Container und Volumes entfernen
docker-compose down -v
# Neu starten
docker-compose up --build
```
### Permission Probleme (Linux/Mac)
```bash
# Ownership korrigieren
sudo chown -R $USER:$USER .
```

255
README-Server.md Normal file
View File

@ -0,0 +1,255 @@
# Server Deployment Guide - IIT Welders
## Voraussetzungen für Server-Deployment
### Server-Anforderungen
- **Betriebssystem**: Ubuntu 20.04+ / CentOS 7+ / Debian 10+
- **RAM**: Mindestens 2GB (4GB empfohlen)
- **CPU**: 1 Core (2 Cores empfohlen)
- **Speicher**: Mindestens 10GB freier Speicher
- **Docker**: Version 20.10+
- **Docker Compose**: Version 2.0+
### Domain & SSL (optional)
- Domain-Name für die Website
- SSL-Zertifikat (Let's Encrypt empfohlen)
## Schnellstart
### 1. Server vorbereiten
```bash
# Docker installieren (Ubuntu/Debian)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
# Docker Compose installieren
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Neustart erforderlich
sudo reboot
```
### 2. Projekt auf Server übertragen
```bash
# Mit Git (empfohlen)
git clone <your-repository-url>
cd iitwelders
# Oder mit SCP
scp -r ./iitwelders user@server:/home/user/
```
### 3. Deployment ausführen
```bash
# Produktions-Deployment
./deploy.sh
# Oder manuell
docker-compose up -d
```
## Detaillierte Anleitung
### Option 1: Einfaches Deployment (nur App)
```bash
# 1. Projekt klonen/übertragen
git clone <repository-url>
cd iitwelders
# 2. Dependencies installieren
npm install
# 3. Build erstellen
npm run build
# 4. Mit Docker starten
docker-compose up -d
# 5. Status prüfen
./deploy.sh status
```
### Option 2: Vollständiges Deployment (mit Nginx)
```bash
# 1. Alle Schritte aus Option 1
# 2. Nginx-Konfiguration anpassen (falls nötig)
# 3. SSL-Zertifikate hinzufügen (optional)
mkdir ssl
# SSL-Zertifikate in ssl/ Ordner kopieren
# 4. Mit Nginx starten
docker-compose up -d
# 5. Nginx-Konfiguration testen
docker exec iitwelders-nginx nginx -t
```
### Option 3: Mit Reverse Proxy (Traefik/Nginx)
```bash
# Für Produktionsumgebung mit automatischem SSL
# docker-compose.prod.yml erstellen (siehe unten)
```
## Konfiguration
### Umgebungsvariablen
```bash
# .env Datei erstellen
cp .env.example .env
# Anpassen nach Bedarf
nano .env
```
### Nginx-Konfiguration anpassen
```bash
# nginx.conf bearbeiten
nano nginx.conf
# Neustart nach Änderungen
docker-compose restart nginx
```
### SSL-Zertifikate (Let's Encrypt)
```bash
# Certbot installieren
sudo apt install certbot
# Zertifikat erstellen
sudo certbot certonly --standalone -d yourdomain.com
# Zertifikate in Docker-Container kopieren
sudo cp /etc/letsencrypt/live/yourdomain.com/*.pem ./ssl/
```
## Monitoring & Wartung
### Logs anzeigen
```bash
# Alle Logs
./deploy.sh logs
# Nur App-Logs
docker logs iitwelders-web
# Nur Nginx-Logs
docker logs iitwelders-nginx
```
### Health Check
```bash
# Automatischer Health Check
./deploy.sh health
# Manueller Check
curl http://localhost:3000
```
### Updates
```bash
# Code aktualisieren
git pull origin main
# Neues Deployment
./deploy.sh
# Oder nur App neu starten
docker-compose restart iitwelders-app
```
### Backup
```bash
# Container stoppen
./deploy.sh stop
# Backup erstellen
tar -czf backup-$(date +%Y%m%d).tar.gz .
# Container starten
./deploy.sh
```
## Troubleshooting
### Häufige Probleme
#### Port bereits belegt
```bash
# Ports prüfen
netstat -tulpn | grep :3000
# Prozess beenden
sudo kill -9 <PID>
```
#### Docker-Probleme
```bash
# Docker neu starten
sudo systemctl restart docker
# Container bereinigen
docker system prune -a
```
#### Speicherplatz
```bash
# Speicherplatz prüfen
df -h
# Docker-Images bereinigen
docker image prune -a
```
#### Logs prüfen
```bash
# System-Logs
journalctl -u docker
# Container-Logs
docker logs iitwelders-web --tail 100
```
## Sicherheit
### Firewall konfigurieren
```bash
# UFW aktivieren
sudo ufw enable
# Nur notwendige Ports öffnen
sudo ufw allow 22 # SSH
sudo ufw allow 80 # HTTP
sudo ufw allow 443 # HTTPS
```
### SSL/TLS
- Let's Encrypt für kostenlose SSL-Zertifikate
- Automatische Erneuerung einrichten
- HSTS-Header aktivieren
### Updates
- Regelmäßige System-Updates
- Docker-Images aktualisieren
- Sicherheits-Patches installieren
## Performance-Optimierung
### Nginx-Optimierung
- Gzip-Kompression aktiviert
- Browser-Caching konfiguriert
- Rate Limiting aktiviert
### Docker-Optimierung
- Multi-stage Builds
- Alpine Linux Images
- Volume-Optimierung
## Support
Bei Problemen:
1. Logs prüfen: `./deploy.sh logs`
2. Health Check: `./deploy.sh health`
3. Status prüfen: `./deploy.sh status`
4. Container neu starten: `./deploy.sh stop && ./deploy.sh`

171
deploy.sh Normal file
View File

@ -0,0 +1,171 @@
#!/bin/bash
# Deployment script for IIT Welders
# This script handles the deployment process
set -e
# Configuration
APP_NAME="iitwelders"
DOCKER_COMPOSE_FILE="docker-compose.yml"
DOCKER_COMPOSE_DEV_FILE="docker-compose.dev.yml"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Function to check if Docker is running
check_docker() {
if ! docker info > /dev/null 2>&1; then
print_error "Docker is not running. Please start Docker and try again."
exit 1
fi
print_success "Docker is running"
}
# Function to build and deploy
deploy() {
local environment=${1:-production}
print_status "Starting deployment for $environment environment..."
# Check Docker
check_docker
# Stop existing containers
print_status "Stopping existing containers..."
if [ "$environment" = "development" ]; then
docker-compose -f $DOCKER_COMPOSE_DEV_FILE down || true
else
docker-compose -f $DOCKER_COMPOSE_FILE down || true
fi
# Remove old images
print_status "Cleaning up old images..."
docker image prune -f
# Build and start containers
print_status "Building and starting containers..."
if [ "$environment" = "development" ]; then
docker-compose -f $DOCKER_COMPOSE_DEV_FILE up --build -d
else
docker-compose -f $DOCKER_COMPOSE_FILE up --build -d
fi
# Wait for application to start
print_status "Waiting for application to start..."
sleep 10
# Run health check
if [ -f "./healthcheck.sh" ]; then
print_status "Running health check..."
chmod +x ./healthcheck.sh
./healthcheck.sh
fi
print_success "Deployment completed successfully!"
if [ "$environment" = "development" ]; then
print_status "Application is available at: http://localhost:8080"
else
print_status "Application is available at: http://localhost:3000"
print_status "With Nginx at: http://localhost"
fi
}
# Function to show logs
show_logs() {
local environment=${1:-production}
if [ "$environment" = "development" ]; then
docker-compose -f $DOCKER_COMPOSE_DEV_FILE logs -f
else
docker-compose -f $DOCKER_COMPOSE_FILE logs -f
fi
}
# Function to stop application
stop() {
local environment=${1:-production}
print_status "Stopping $environment environment..."
if [ "$environment" = "development" ]; then
docker-compose -f $DOCKER_COMPOSE_DEV_FILE down
else
docker-compose -f $DOCKER_COMPOSE_FILE down
fi
print_success "Application stopped"
}
# Function to show status
status() {
print_status "Container status:"
docker ps --filter "name=$APP_NAME" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
}
# Main script logic
case "${1:-deploy}" in
"deploy")
deploy "${2:-production}"
;;
"dev")
deploy "development"
;;
"logs")
show_logs "${2:-production}"
;;
"stop")
stop "${2:-production}"
;;
"status")
status
;;
"help"|"-h"|"--help")
echo "Usage: $0 [command] [environment]"
echo ""
echo "Commands:"
echo " deploy Deploy to production (default)"
echo " dev Deploy to development"
echo " logs Show application logs"
echo " stop Stop application"
echo " status Show container status"
echo " help Show this help message"
echo ""
echo "Environments:"
echo " production Production environment (default)"
echo " development Development environment"
echo ""
echo "Examples:"
echo " $0 deploy production"
echo " $0 dev"
echo " $0 logs development"
echo " $0 stop"
;;
*)
print_error "Unknown command: $1"
echo "Use '$0 help' for usage information"
exit 1
;;
esac

21
docker-compose.dev.yml Normal file
View File

@ -0,0 +1,21 @@
version: '3.8'
services:
iitwelders-dev:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "8080:8080"
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=true
restart: unless-stopped
container_name: iitwelders-dev
command: npm run dev
volumes:
node_modules:

30
docker-compose.yml Normal file
View File

@ -0,0 +1,30 @@
version: '3.8'
services:
iitwelders-app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=production
restart: unless-stopped
container_name: iitwelders-web
# Optional: Add nginx reverse proxy for production
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- iitwelders-app
restart: unless-stopped
container_name: iitwelders-nginx
volumes:
node_modules:

63
healthcheck.sh Normal file
View File

@ -0,0 +1,63 @@
#!/bin/bash
# Health check script for IIT Welders application
# This script checks if the application is running and responding
set -e
# Configuration
HEALTH_URL="http://localhost:3000"
TIMEOUT=10
MAX_RETRIES=3
# Function to check health
check_health() {
local url=$1
local timeout=$2
if curl -f -s --max-time $timeout "$url" > /dev/null 2>&1; then
echo "✅ Health check passed: $url"
return 0
else
echo "❌ Health check failed: $url"
return 1
fi
}
# Function to check if port is listening
check_port() {
local port=$1
if netstat -tuln | grep -q ":$port "; then
echo "✅ Port $port is listening"
return 0
else
echo "❌ Port $port is not listening"
return 1
fi
}
# Main health check
echo "🔍 Starting health check for IIT Welders..."
# Check if port is listening
if ! check_port 3000; then
echo "❌ Application is not running on port 3000"
exit 1
fi
# Check application health
retry_count=0
while [ $retry_count -lt $MAX_RETRIES ]; do
if check_health "$HEALTH_URL" $TIMEOUT; then
echo "✅ Application is healthy and responding"
exit 0
fi
retry_count=$((retry_count + 1))
echo "⏳ Retry $retry_count/$MAX_RETRIES in 5 seconds..."
sleep 5
done
echo "❌ Health check failed after $MAX_RETRIES retries"
exit 1

76
nginx.conf Normal file
View File

@ -0,0 +1,76 @@
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private must-revalidate auth;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/xml+rss
application/json;
# Rate limiting
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
upstream app {
server iitwelders-app:3000;
}
server {
listen 80;
server_name localhost;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
# Static files caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
proxy_pass http://app;
}
# Main application
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# Health check
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
}

View File

@ -8,7 +8,11 @@
"build": "vite build", "build": "vite build",
"build:dev": "vite build --mode development", "build:dev": "vite build --mode development",
"lint": "eslint .", "lint": "eslint .",
"preview": "vite preview" "preview": "vite preview",
"start": "serve -s dist -l 3000",
"deploy": "./deploy.sh",
"deploy:dev": "./deploy.sh dev",
"health": "./healthcheck.sh"
}, },
"dependencies": { "dependencies": {
"@hookform/resolvers": "^3.10.0", "@hookform/resolvers": "^3.10.0",
@ -59,7 +63,8 @@
"tailwind-merge": "^2.6.0", "tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"vaul": "^0.9.9", "vaul": "^0.9.9",
"zod": "^3.25.76" "zod": "^3.25.76",
"serve": "^14.2.1"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.32.0", "@eslint/js": "^9.32.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -16,7 +16,7 @@ const Footer = () => {
}; };
return ( return (
<footer className="bg-slate-900 border-t border-slate-700"> <footer className="bg-slate-900 border-t border-slate-700 footer-transition">
<div className="container mx-auto px-4 py-16"> <div className="container mx-auto px-4 py-16">
{/* Main Footer Content */} {/* Main Footer Content */}
<div className="grid grid-cols-1 md:grid-cols-12 gap-12 mb-12"> <div className="grid grid-cols-1 md:grid-cols-12 gap-12 mb-12">

View File

@ -51,6 +51,7 @@ const Hero = () => {
<Button <Button
size="xl" size="xl"
className="bg-white hover:bg-gray-100 text-slate-900 font-semibold text-lg px-8 py-4 h-auto shadow-lg rounded-lg hover-lift hover-glow" className="bg-white hover:bg-gray-100 text-slate-900 font-semibold text-lg px-8 py-4 h-auto shadow-lg rounded-lg hover-lift hover-glow"
onClick={() => window.location.href = '/contact-us'}
> >
Get Quote Get Quote
</Button> </Button>
@ -58,6 +59,7 @@ const Hero = () => {
variant="outline" variant="outline"
size="xl" size="xl"
className="border-slate-500/50 bg-slate-800/50 text-white hover:bg-slate-700/50 hover:border-slate-400 font-semibold text-lg px-8 py-4 h-auto rounded-lg backdrop-blur-sm hover-lift" className="border-slate-500/50 bg-slate-800/50 text-white hover:bg-slate-700/50 hover:border-slate-400 font-semibold text-lg px-8 py-4 h-auto rounded-lg backdrop-blur-sm hover-lift"
onClick={() => window.location.href = '/services-and-specialties'}
> >
Explore Services Explore Services
</Button> </Button>

View File

@ -125,11 +125,247 @@
@apply bg-card/95 backdrop-blur-sm border border-border/50 shadow-xl; @apply bg-card/95 backdrop-blur-sm border border-border/50 shadow-xl;
} }
/* Dispel-style gradient backgrounds */ /* Enhanced gradient backgrounds */
.bg-dispel-gradient { .bg-dispel-gradient {
background: linear-gradient(135deg, hsl(210 24% 8%) 0%, hsl(215 25% 10%) 50%, hsl(210 24% 8%) 100%); background: linear-gradient(135deg, hsl(210 24% 8%) 0%, hsl(215 25% 10%) 50%, hsl(210 24% 8%) 100%);
} }
.bg-industrial-gradient {
background: linear-gradient(135deg, hsl(220 26% 14%) 0%, hsl(215 25% 12%) 50%, hsl(210 24% 10%) 100%);
}
.bg-section-gradient {
background: linear-gradient(180deg, hsl(220 26% 14%) 0%, hsl(215 25% 12%) 100%);
}
.bg-card-gradient {
background: linear-gradient(145deg, hsl(215 20% 12%) 0%, hsl(215 18% 14%) 100%);
}
.bg-accent-gradient {
background: linear-gradient(135deg, hsl(217 91% 60%) 0%, hsl(213 94% 68%) 100%);
}
/* Enhanced color utilities */
.text-accent-blue {
color: #60a5fa;
}
.text-accent-cyan {
color: #22d3ee;
}
.bg-accent-blue {
background-color: #3b82f6;
}
.bg-accent-cyan {
background-color: #06b6d4;
}
.border-accent-blue {
border-color: #3b82f6;
}
.border-accent-cyan {
border-color: #06b6d4;
}
/* Soft Section Transition Effects */
.section-transition-soft {
position: relative;
}
.section-transition-soft::before {
content: '';
position: absolute;
top: -60px;
left: 0;
right: 0;
height: 60px;
background: linear-gradient(180deg,
rgba(30, 41, 59, 0) 0%,
rgba(30, 41, 59, 0.1) 20%,
rgba(30, 41, 59, 0.3) 40%,
rgba(30, 41, 59, 0.6) 70%,
rgba(30, 41, 59, 0.9) 90%,
rgba(30, 41, 59, 1) 100%);
pointer-events: none;
}
/* Gentle wave transition */
.section-transition-wave {
position: relative;
}
.section-transition-wave::before {
content: '';
position: absolute;
top: -80px;
left: 0;
right: 0;
height: 80px;
background:
radial-gradient(ellipse 120% 100% at center top,
rgba(30, 41, 59, 0) 0%,
rgba(30, 41, 59, 0.05) 10%,
rgba(30, 41, 59, 0.15) 25%,
rgba(30, 41, 59, 0.35) 50%,
rgba(30, 41, 59, 0.65) 75%,
rgba(30, 41, 59, 0.9) 95%,
rgba(30, 41, 59, 1) 100%);
pointer-events: none;
}
/* Ultra-soft gradient transition */
.section-transition-gradient {
position: relative;
}
.section-transition-gradient::before {
content: '';
position: absolute;
top: -100px;
left: 0;
right: 0;
height: 100px;
background: linear-gradient(180deg,
rgba(30, 41, 59, 0) 0%,
rgba(30, 41, 59, 0.02) 10%,
rgba(30, 41, 59, 0.08) 25%,
rgba(30, 41, 59, 0.18) 40%,
rgba(30, 41, 59, 0.35) 60%,
rgba(30, 41, 59, 0.55) 75%,
rgba(30, 41, 59, 0.75) 88%,
rgba(30, 41, 59, 0.9) 95%,
rgba(30, 41, 59, 1) 100%);
pointer-events: none;
}
/* Soft curve transition */
.section-transition-curve {
position: relative;
overflow: hidden;
}
.section-transition-curve::before {
content: '';
position: absolute;
top: -120px;
left: 0;
right: 0;
height: 120px;
background:
radial-gradient(ellipse 150% 120% at center top,
rgba(30, 41, 59, 0) 0%,
rgba(30, 41, 59, 0.03) 8%,
rgba(30, 41, 59, 0.12) 20%,
rgba(30, 41, 59, 0.28) 40%,
rgba(30, 41, 59, 0.5) 65%,
rgba(30, 41, 59, 0.75) 85%,
rgba(30, 41, 59, 0.92) 96%,
rgba(30, 41, 59, 1) 100%);
pointer-events: none;
}
/* Gentle slant transition */
.section-transition-slant {
position: relative;
overflow: hidden;
}
.section-transition-slant::before {
content: '';
position: absolute;
top: -80px;
left: 0;
right: 0;
height: 80px;
background: linear-gradient(135deg,
rgba(30, 41, 59, 0) 0%,
rgba(30, 41, 59, 0.05) 15%,
rgba(30, 41, 59, 0.2) 35%,
rgba(30, 41, 59, 0.45) 60%,
rgba(30, 41, 59, 0.7) 80%,
rgba(30, 41, 59, 0.9) 95%,
rgba(30, 41, 59, 1) 100%);
clip-path: polygon(0 100%, 100% 0%, 100% 100%, 0% 100%);
pointer-events: none;
}
/* Soft diagonal transition */
.section-transition-diagonal {
position: relative;
overflow: hidden;
}
.section-transition-diagonal::before {
content: '';
position: absolute;
top: -100px;
left: 0;
right: 0;
height: 100px;
background: linear-gradient(45deg,
rgba(30, 41, 59, 0) 0%,
rgba(30, 41, 59, 0.03) 10%,
rgba(30, 41, 59, 0.15) 30%,
rgba(30, 41, 59, 0.35) 55%,
rgba(30, 41, 59, 0.6) 75%,
rgba(30, 41, 59, 0.8) 90%,
rgba(30, 41, 59, 0.95) 98%,
rgba(30, 41, 59, 1) 100%);
transform: skewY(-1deg);
pointer-events: none;
}
/* Footer transition */
.footer-transition {
position: relative;
}
.footer-transition::before {
content: '';
position: absolute;
top: -60px;
left: 0;
right: 0;
height: 60px;
background: linear-gradient(180deg,
rgba(15, 23, 42, 0) 0%,
rgba(15, 23, 42, 0.1) 15%,
rgba(15, 23, 42, 0.25) 35%,
rgba(15, 23, 42, 0.45) 55%,
rgba(15, 23, 42, 0.65) 75%,
rgba(15, 23, 42, 0.8) 90%,
rgba(15, 23, 42, 1) 100%);
pointer-events: none;
}
/* Hero section transitions */
.hero-transition {
position: relative;
}
.hero-transition::after {
content: '';
position: absolute;
bottom: -80px;
left: 0;
right: 0;
height: 80px;
background: linear-gradient(180deg,
rgba(30, 41, 59, 0) 0%,
rgba(30, 41, 59, 0.05) 15%,
rgba(30, 41, 59, 0.15) 35%,
rgba(30, 41, 59, 0.3) 55%,
rgba(30, 41, 59, 0.5) 75%,
rgba(30, 41, 59, 0.7) 90%,
rgba(30, 41, 59, 0.85) 98%,
rgba(30, 41, 59, 1) 100%);
pointer-events: none;
}
/* Dispel-style floating elements */ /* Dispel-style floating elements */
.floating-element { .floating-element {
@apply absolute opacity-20; @apply absolute opacity-20;
@ -143,6 +379,69 @@
@apply bottom-20 left-20 w-64 h-64 bg-accent/5 rounded-full blur-2xl; @apply bottom-20 left-20 w-64 h-64 bg-accent/5 rounded-full blur-2xl;
} }
/* Background Patterns */
.bg-pattern-dots {
background-image: radial-gradient(circle, rgba(59, 130, 246, 0.1) 1px, transparent 1px);
background-size: 20px 20px;
}
.bg-pattern-grid {
background-image:
linear-gradient(rgba(59, 130, 246, 0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(59, 130, 246, 0.1) 1px, transparent 1px);
background-size: 20px 20px;
}
.bg-pattern-lines {
background-image: repeating-linear-gradient(
45deg,
transparent,
transparent 10px,
rgba(59, 130, 246, 0.05) 10px,
rgba(59, 130, 246, 0.05) 20px
);
}
.bg-pattern-industrial {
background-image:
linear-gradient(rgba(59, 130, 246, 0.08) 1px, transparent 1px),
linear-gradient(90deg, rgba(59, 130, 246, 0.08) 1px, transparent 1px),
linear-gradient(rgba(59, 130, 246, 0.04) 1px, transparent 1px),
linear-gradient(90deg, rgba(59, 130, 246, 0.04) 1px, transparent 1px);
background-size: 50px 50px, 50px 50px, 10px 10px, 10px 10px;
}
/* Enhanced Card Designs */
.card-enhanced {
@apply bg-slate-800/60 backdrop-blur-sm border border-slate-600/50 rounded-xl shadow-xl;
background: linear-gradient(145deg, rgba(30, 41, 59, 0.8) 0%, rgba(51, 65, 85, 0.6) 100%);
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -1px rgba(0, 0, 0, 0.06),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.card-enhanced:hover {
@apply border-blue-500/30 shadow-2xl;
background: linear-gradient(145deg, rgba(30, 41, 59, 0.9) 0%, rgba(51, 65, 85, 0.7) 100%);
box-shadow:
0 20px 25px -5px rgba(0, 0, 0, 0.1),
0 10px 10px -5px rgba(0, 0, 0, 0.04),
0 0 0 1px rgba(59, 130, 246, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
/* Glassmorphism Effects */
.glass-card {
@apply bg-white/5 backdrop-blur-md border border-white/10 rounded-xl;
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
}
.glass-card:hover {
@apply bg-white/10 border-white/20;
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.5);
}
/* Carousel specific styling - Dispel inspired */ /* Carousel specific styling - Dispel inspired */
.carousel-container { .carousel-container {
@apply relative overflow-hidden rounded-2xl border border-border/30 bg-gradient-to-br from-bg-elevated/20 to-transparent backdrop-blur-sm; @apply relative overflow-hidden rounded-2xl border border-border/30 bg-gradient-to-br from-bg-elevated/20 to-transparent backdrop-blur-sm;
@ -266,22 +565,164 @@
animation: bounceSubtle 2s ease-in-out infinite; animation: bounceSubtle 2s ease-in-out infinite;
} }
/* Simple hover animations that should be immediately visible */ /* Enhanced Statistics Styles */
.stat-number {
@apply text-4xl lg:text-5xl font-bold text-white mb-2;
background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 50%, #1d4ed8 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 0 30px rgba(59, 130, 246, 0.5);
animation: statPulse 3s ease-in-out infinite;
}
.stat-number:hover {
transform: scale(1.1);
filter: brightness(1.2);
transition: all 0.3s ease;
}
.stat-card {
@apply bg-slate-800/30 backdrop-blur-sm border border-slate-600/30 rounded-2xl p-6 text-center;
background: linear-gradient(145deg, rgba(30, 41, 59, 0.3) 0%, rgba(51, 65, 85, 0.2) 100%);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.1),
0 0 0 1px rgba(59, 130, 246, 0.1);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(59, 130, 246, 0.1), transparent);
transition: left 0.6s ease;
}
.stat-card:hover {
transform: translateY(-8px) scale(1.05);
box-shadow:
0 20px 40px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.2),
0 0 0 1px rgba(59, 130, 246, 0.3),
0 0 30px rgba(59, 130, 246, 0.2);
}
.stat-card:hover::before {
left: 100%;
}
.stat-label {
@apply text-slate-400 text-sm font-medium;
text-transform: uppercase;
letter-spacing: 0.05em;
opacity: 0.8;
transition: all 0.3s ease;
}
.stat-card:hover .stat-label {
color: #60a5fa;
opacity: 1;
}
/* Enhanced hover animations */
.hover-lift { .hover-lift {
transition: transform 0.3s ease, box-shadow 0.3s ease; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
} }
.hover-lift:hover { .hover-lift:hover {
transform: translateY(-2px); transform: translateY(-4px) scale(1.02);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
} }
.hover-glow { .hover-glow {
transition: box-shadow 0.3s ease; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
} }
.hover-glow:hover { .hover-glow:hover {
box-shadow: 0 0 20px rgba(59, 130, 246, 0.3); box-shadow: 0 0 30px rgba(59, 130, 246, 0.4);
border-color: rgba(59, 130, 246, 0.5);
}
/* Advanced hover effects */
.hover-slide {
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.hover-slide::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transition: left 0.5s ease;
}
.hover-slide:hover::before {
left: 100%;
}
.hover-rotate {
transition: transform 0.3s ease;
}
.hover-rotate:hover {
transform: rotate(2deg) scale(1.05);
}
.hover-pulse {
transition: all 0.3s ease;
}
.hover-pulse:hover {
animation: pulse 0.6s ease-in-out;
}
/* Button hover effects */
.btn-hover-lift {
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.btn-hover-lift:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
}
.btn-hover-lift:active {
transform: translateY(0);
}
/* Card hover effects */
.card-hover {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.card-hover:hover {
transform: translateY(-8px) scale(1.02);
box-shadow:
0 25px 50px -12px rgba(0, 0, 0, 0.25),
0 0 0 1px rgba(59, 130, 246, 0.1);
}
/* Text hover effects */
.text-hover-glow {
transition: all 0.3s ease;
}
.text-hover-glow:hover {
text-shadow: 0 0 8px rgba(59, 130, 246, 0.6);
color: #60a5fa;
} }
} }
@ -357,6 +798,26 @@
} }
} }
@keyframes statPulse {
0%, 100% {
text-shadow: 0 0 30px rgba(59, 130, 246, 0.5);
transform: scale(1);
}
50% {
text-shadow: 0 0 50px rgba(59, 130, 246, 0.8);
transform: scale(1.05);
}
}
@keyframes statGlow {
0%, 100% {
text-shadow: 0 0 10px rgba(59, 130, 246, 0.5);
}
50% {
text-shadow: 0 0 20px rgba(59, 130, 246, 0.8);
}
}
@layer utilities { @layer utilities {
/* Text truncation utilities */ /* Text truncation utilities */
.line-clamp-1 { .line-clamp-1 {

View File

@ -2,8 +2,42 @@ import React from 'react';
import Header from '@/components/Header'; import Header from '@/components/Header';
import Footer from '@/components/Footer'; import Footer from '@/components/Footer';
import { Building, Users, Award, ChevronRight } from 'lucide-react'; import { Building, Users, Award, ChevronRight } from 'lucide-react';
import { useCountUp } from '@/hooks/useCountUp';
import content from '@/content/content.json'; import content from '@/content/content.json';
// Stat Card Component with Count Up Animation
const StatCard = ({
icon: Icon,
value,
label,
description,
suffix = '',
delay = 0
}: {
icon: React.ComponentType<{ className?: string }>;
value: number;
label: string;
description: string;
suffix?: string;
delay?: number;
}) => {
const { count, ref } = useCountUp({
end: value,
duration: 2500,
delay: delay,
suffix: suffix
});
return (
<div ref={ref} className="stat-card hover-lift">
<Icon className="h-12 w-12 text-blue-400 mx-auto mb-4" />
<div className="stat-number">{count}</div>
<div className="stat-label">{label}</div>
<div className="text-sm text-slate-500 mt-2">{description}</div>
</div>
);
};
const About = () => { const About = () => {
return ( return (
<div className="min-h-screen bg-slate-900"> <div className="min-h-screen bg-slate-900">
@ -27,7 +61,7 @@ const About = () => {
<main> <main>
{/* Hero Section with Image */} {/* Hero Section with Image */}
<section className="relative py-32 overflow-hidden"> <section className="relative py-32 overflow-hidden hero-transition">
{/* Background Image */} {/* Background Image */}
<div <div
className="absolute inset-0 bg-cover bg-center bg-no-repeat" className="absolute inset-0 bg-cover bg-center bg-no-repeat"
@ -56,7 +90,7 @@ const About = () => {
</section> </section>
{/* Main Content */} {/* Main Content */}
<section className="py-24 bg-slate-800"> <section className="py-24 bg-slate-800 bg-pattern-dots section-transition-soft">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto"> <div className="max-w-6xl mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-16">
@ -102,8 +136,17 @@ const About = () => {
</div> </div>
</section> </section>
{/* Soft Transition */}
<div className="relative h-40 bg-gradient-to-b from-slate-800 via-slate-800/80 to-slate-900/60">
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-slate-800/30 to-slate-900/80"></div>
<div className="absolute inset-0 bg-gradient-to-b from-slate-800/20 via-transparent to-slate-900/40"></div>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-24 h-1 bg-gradient-to-r from-transparent via-blue-400/20 to-transparent rounded-full"></div>
</div>
</div>
{/* Stats Section */} {/* Stats Section */}
<section className="py-24 bg-slate-900"> <section className="py-24 bg-slate-900 bg-pattern-industrial section-transition-wave">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto"> <div className="max-w-6xl mx-auto">
<div className="text-center mb-16"> <div className="text-center mb-16">
@ -112,40 +155,47 @@ const About = () => {
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
<div className="bg-slate-800/50 border border-slate-600 rounded-xl p-8 text-center"> <StatCard
<Users className="h-12 w-12 text-blue-400 mx-auto mb-4" /> icon={Users}
<div className="text-4xl font-bold text-white mb-2">800+</div> value={800}
<div className="text-slate-400">Skilled Craftsmen</div> label="Skilled Craftsmen"
<div className="text-sm text-slate-500 mt-2">Ready for mobilization 24/7</div> description="Ready for mobilization 24/7"
</div> suffix="+"
delay={0}
/>
<div className="bg-slate-800/50 border border-slate-600 rounded-xl p-8 text-center"> <StatCard
<Building className="h-12 w-12 text-blue-400 mx-auto mb-4" /> icon={Building}
<div className="text-4xl font-bold text-white mb-2">13+</div> value={13}
<div className="text-slate-400">Years Experience</div> label="Years Experience"
<div className="text-sm text-slate-500 mt-2">Serving the industry since 2008</div> description="Serving the industry since 2008"
</div> suffix="+"
delay={200}
/>
<div className="bg-slate-800/50 border border-slate-600 rounded-xl p-8 text-center"> <StatCard
<Award className="h-12 w-12 text-blue-400 mx-auto mb-4" /> icon={Award}
<div className="text-4xl font-bold text-white mb-2">3</div> value={3}
<div className="text-slate-400">Texas Locations</div> label="Texas Locations"
<div className="text-sm text-slate-500 mt-2">Brownsville, Ingleside, Corpus Christi</div> description="Brownsville, Ingleside, Corpus Christi"
</div> delay={400}
/>
<div className="bg-slate-800/50 border border-slate-600 rounded-xl p-8 text-center"> <StatCard
<Users className="h-12 w-12 text-blue-400 mx-auto mb-4" /> icon={Users}
<div className="text-4xl font-bold text-white mb-2">24/7</div> value={24}
<div className="text-slate-400">Project Support</div> label="Project Support"
<div className="text-sm text-slate-500 mt-2">Always available for our clients</div> description="Always available for our clients"
</div> suffix="/7"
delay={600}
/>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
{/* Commitment Section */} {/* Commitment Section */}
<section className="py-24 bg-slate-800"> <section className="py-24 bg-slate-800 bg-pattern-dots section-transition-soft">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto text-center"> <div className="max-w-4xl mx-auto text-center">
<h2 className="text-4xl font-bold text-white mb-8">Our Commitment</h2> <h2 className="text-4xl font-bold text-white mb-8">Our Commitment</h2>

View File

@ -44,7 +44,7 @@ const Careers = () => {
<main> <main>
{/* Hero Section with Image */} {/* Hero Section with Image */}
<section className="relative py-32 overflow-hidden"> <section className="relative py-32 overflow-hidden hero-transition">
{/* Background Image */} {/* Background Image */}
<div <div
className="absolute inset-0 bg-cover bg-center bg-no-repeat" className="absolute inset-0 bg-cover bg-center bg-no-repeat"
@ -73,7 +73,7 @@ const Careers = () => {
</section> </section>
{/* Current Openings */} {/* Current Openings */}
<section className="py-24 bg-slate-800"> <section className="py-24 bg-slate-800 section-transition-soft">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto"> <div className="max-w-6xl mx-auto">
<div className="text-center mb-16"> <div className="text-center mb-16">
@ -122,6 +122,15 @@ const Careers = () => {
</div> </div>
</section> </section>
{/* Soft Transition */}
<div className="relative h-40 bg-gradient-to-b from-slate-800 via-slate-800/80 to-slate-900/60">
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-slate-800/30 to-slate-900/80"></div>
<div className="absolute inset-0 bg-gradient-to-b from-slate-800/20 via-transparent to-slate-900/40"></div>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-24 h-1 bg-gradient-to-r from-transparent via-blue-400/20 to-transparent rounded-full"></div>
</div>
</div>
{/* Benefits Section */} {/* Benefits Section */}
<section className="py-24 bg-slate-900"> <section className="py-24 bg-slate-900">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
@ -144,7 +153,7 @@ const Careers = () => {
</section> </section>
{/* Application Process */} {/* Application Process */}
<section className="py-24 bg-slate-800"> <section className="py-24 bg-slate-800 section-transition-soft">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto">
<div className="text-center mb-16"> <div className="text-center mb-16">

View File

@ -564,7 +564,7 @@ const ClientsProjects = () => {
<main> <main>
{/* Hero Section with Image */} {/* Hero Section with Image */}
<section className="relative py-32 overflow-hidden"> <section className="relative py-32 overflow-hidden hero-transition">
{/* Background Image */} {/* Background Image */}
<div <div
className="absolute inset-0 bg-cover bg-center bg-no-repeat" className="absolute inset-0 bg-cover bg-center bg-no-repeat"
@ -592,57 +592,75 @@ const ClientsProjects = () => {
</div> </div>
</section> </section>
{/* Soft Transition */}
<div className="relative h-40 bg-gradient-to-b from-slate-900 via-slate-900/80 to-slate-800/60">
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-slate-900/30 to-slate-800/80"></div>
<div className="absolute inset-0 bg-gradient-to-b from-slate-900/20 via-transparent to-slate-800/40"></div>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-24 h-1 bg-gradient-to-r from-transparent via-blue-400/20 to-transparent rounded-full"></div>
</div>
</div>
{/* Stats Section */} {/* Stats Section */}
<section className="py-16 bg-slate-800"> <section className="py-16 bg-slate-800">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto"> <div className="max-w-6xl mx-auto">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8"> <div className="grid grid-cols-1 md:grid-cols-4 gap-8">
<div className="text-center animate-fadeInUp"> <div className="stat-card animate-fadeInUp">
<div <div
ref={clientsCount.ref} ref={clientsCount.ref}
className="text-4xl lg:text-5xl font-bold text-white mb-2 animate-bounce-subtle" className="stat-number"
> >
{clientsCount.count} {clientsCount.count}
</div> </div>
<p className="text-slate-400 text-sm">Major Clients Served</p> <p className="stat-label">Major Clients Served</p>
</div> </div>
<div className="text-center animate-fadeInUp" style={{animationDelay: '0.1s'}}>
<div className="stat-card animate-fadeInUp" style={{animationDelay: '0.1s'}}>
<div <div
ref={projectsCount.ref} ref={projectsCount.ref}
className="text-4xl lg:text-5xl font-bold text-white mb-2 animate-bounce-subtle" className="stat-number"
style={{animationDelay: '0.2s'}}
> >
{projectsCount.count} {projectsCount.count}
</div> </div>
<p className="text-slate-400 text-sm">Projects Completed</p> <p className="stat-label">Projects Completed</p>
</div> </div>
<div className="text-center animate-fadeInUp" style={{animationDelay: '0.2s'}}>
<div className="stat-card animate-fadeInUp" style={{animationDelay: '0.2s'}}>
<div <div
ref={longestProjectCount.ref} ref={longestProjectCount.ref}
className="text-4xl lg:text-5xl font-bold text-white mb-2 animate-bounce-subtle" className="stat-number"
style={{animationDelay: '0.4s'}}
> >
{longestProjectCount.count} {longestProjectCount.count}
</div> </div>
<p className="text-slate-400 text-sm">Months Longest Project</p> <p className="stat-label">Months Longest Project</p>
</div> </div>
<div className="text-center animate-fadeInUp" style={{animationDelay: '0.3s'}}>
<div className="stat-card animate-fadeInUp" style={{animationDelay: '0.3s'}}>
<div <div
ref={maxPersonnelCount.ref} ref={maxPersonnelCount.ref}
className="text-4xl lg:text-5xl font-bold text-white mb-2 animate-bounce-subtle" className="stat-number"
style={{animationDelay: '0.6s'}}
> >
{maxPersonnelCount.count} {maxPersonnelCount.count}
</div> </div>
<p className="text-slate-400 text-sm">Max Personnel on Site</p> <p className="stat-label">Max Personnel on Site</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
{/* Soft Transition */}
<div className="relative h-40 bg-gradient-to-b from-slate-800 via-slate-800/80 to-slate-800/60">
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-slate-800/30 to-slate-800/80"></div>
<div className="absolute inset-0 bg-gradient-to-b from-slate-800/20 via-transparent to-slate-800/40"></div>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-24 h-1 bg-gradient-to-r from-transparent via-blue-400/20 to-transparent rounded-full"></div>
</div>
</div>
{/* Search and Filter Section */} {/* Search and Filter Section */}
<section className="py-8 bg-slate-800"> <section className="py-8 bg-slate-800 bg-pattern-dots section-transition-soft">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-1 lg:grid-cols-4 gap-4">
@ -655,7 +673,7 @@ const ClientsProjects = () => {
placeholder="Search projects, clients, or locations..." placeholder="Search projects, clients, or locations..."
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-3 bg-slate-700 border border-slate-600 rounded-lg text-white placeholder-slate-400 focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-colors" className="w-full pl-10 pr-4 py-3 bg-slate-700/80 backdrop-blur-sm border border-slate-600/50 rounded-lg text-white placeholder-slate-400 focus:outline-none focus:border-accent-blue focus:ring-2 focus:ring-accent-blue/20 transition-all duration-300 hover:border-accent-blue/50"
/> />
</div> </div>
</div> </div>
@ -665,7 +683,7 @@ const ClientsProjects = () => {
<select <select
value={selectedIndustry} value={selectedIndustry}
onChange={(e) => setSelectedIndustry(e.target.value)} onChange={(e) => setSelectedIndustry(e.target.value)}
className="w-full py-3 px-4 bg-slate-700 border border-slate-600 rounded-lg text-white focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-colors" className="w-full py-3 px-4 bg-slate-700/80 backdrop-blur-sm border border-slate-600/50 rounded-lg text-white focus:outline-none focus:border-accent-blue focus:ring-2 focus:ring-accent-blue/20 transition-all duration-300 hover:border-accent-blue/50"
> >
{industries.map(industry => ( {industries.map(industry => (
<option key={industry} value={industry}>{industry}</option> <option key={industry} value={industry}>{industry}</option>
@ -678,7 +696,7 @@ const ClientsProjects = () => {
<select <select
value={selectedLocation} value={selectedLocation}
onChange={(e) => setSelectedLocation(e.target.value)} onChange={(e) => setSelectedLocation(e.target.value)}
className="w-full py-3 px-4 bg-slate-700 border border-slate-600 rounded-lg text-white focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-colors" className="w-full py-3 px-4 bg-slate-700/80 backdrop-blur-sm border border-slate-600/50 rounded-lg text-white focus:outline-none focus:border-accent-blue focus:ring-2 focus:ring-accent-blue/20 transition-all duration-300 hover:border-accent-blue/50"
> >
{locations.map(location => ( {locations.map(location => (
<option key={location} value={location}>{location}</option> <option key={location} value={location}>{location}</option>
@ -697,8 +715,17 @@ const ClientsProjects = () => {
</div> </div>
</section> </section>
{/* Soft Transition */}
<div className="relative h-40 bg-gradient-to-b from-slate-800 via-slate-800/80 to-slate-900/60">
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-slate-800/30 to-slate-900/80"></div>
<div className="absolute inset-0 bg-gradient-to-b from-slate-800/20 via-transparent to-slate-900/40"></div>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-24 h-1 bg-gradient-to-r from-transparent via-blue-400/20 to-transparent rounded-full"></div>
</div>
</div>
{/* Projects Grid */} {/* Projects Grid */}
<section className="py-24 bg-slate-900"> <section className="py-24 bg-slate-900 bg-pattern-industrial section-transition-gradient">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
<div className="text-center mb-16"> <div className="text-center mb-16">
@ -737,13 +764,13 @@ const ClientsProjects = () => {
</div> </div>
) : ( ) : (
filteredProjects.map((project, index) => ( filteredProjects.map((project, index) => (
<div key={index} className="bg-slate-800/50 border border-slate-600 rounded-xl p-6 hover:bg-slate-800/70 transition-all duration-300 hover:scale-[1.02] touch-manipulation"> <div key={index} className="card-enhanced card-hover hover-slide p-6 touch-manipulation">
<div className="space-y-4"> <div className="space-y-4">
{/* Industry Badge */} {/* Industry Badge */}
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div className="flex-1"> <div className="flex-1">
<div className="bg-blue-500/10 border border-blue-500/20 rounded-lg px-2 py-1 inline-block mb-3"> <div className="bg-accent-gradient border border-accent-blue/30 rounded-lg px-3 py-1 inline-block mb-3 shadow-lg">
<span className="text-blue-400 text-xs font-medium">{project.industry}</span> <span className="text-white text-xs font-medium">{project.industry}</span>
</div> </div>
<h3 className="text-lg font-bold text-white mb-2 line-clamp-2">{project.client}</h3> <h3 className="text-lg font-bold text-white mb-2 line-clamp-2">{project.client}</h3>
<div className="flex items-center space-x-2 text-slate-400"> <div className="flex items-center space-x-2 text-slate-400">
@ -751,8 +778,8 @@ const ClientsProjects = () => {
<span className="text-sm line-clamp-1">{project.location}</span> <span className="text-sm line-clamp-1">{project.location}</span>
</div> </div>
</div> </div>
<div className="bg-blue-500/20 rounded-lg px-3 py-1 ml-2"> <div className="bg-accent-blue/20 border border-accent-blue/30 rounded-lg px-3 py-1 ml-2 hover-glow">
<span className="text-blue-400 text-sm font-medium">{project.personnel}</span> <span className="text-accent-blue text-sm font-medium">{project.personnel}</span>
</div> </div>
</div> </div>
@ -790,7 +817,7 @@ const ClientsProjects = () => {
</section> </section>
{/* Industry Sectors */} {/* Industry Sectors */}
<section className="py-24 bg-slate-800"> <section className="py-24 bg-slate-800 section-transition-wave">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto text-center"> <div className="max-w-6xl mx-auto text-center">
<h2 className="text-4xl font-bold text-white mb-12">Industry Sectors</h2> <h2 className="text-4xl font-bold text-white mb-12">Industry Sectors</h2>
@ -824,7 +851,7 @@ const ClientsProjects = () => {
{showBackToTop && ( {showBackToTop && (
<button <button
onClick={scrollToTop} onClick={scrollToTop}
className="fixed bottom-8 right-8 z-50 bg-blue-600 hover:bg-blue-700 text-white p-3 rounded-full shadow-lg transition-all duration-300 hover:scale-110 touch-manipulation" className="fixed bottom-8 right-8 z-50 bg-accent-gradient hover:bg-blue-700 text-white p-4 rounded-full shadow-xl hover-lift btn-hover-lift touch-manipulation border border-accent-blue/30"
aria-label="Back to top" aria-label="Back to top"
> >
<ArrowUp className="h-6 w-6" /> <ArrowUp className="h-6 w-6" />

View File

@ -30,7 +30,7 @@ const Contact = () => {
<main> <main>
{/* Hero Section with Image */} {/* Hero Section with Image */}
<section className="relative py-32 overflow-hidden"> <section className="relative py-32 overflow-hidden hero-transition">
{/* Background Image */} {/* Background Image */}
<div <div
className="absolute inset-0 bg-cover bg-center bg-no-repeat" className="absolute inset-0 bg-cover bg-center bg-no-repeat"
@ -59,7 +59,7 @@ const Contact = () => {
</section> </section>
{/* Contact Methods */} {/* Contact Methods */}
<section className="py-24 bg-slate-800"> <section className="py-24 bg-slate-800 section-transition-soft">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto"> <div className="max-w-6xl mx-auto">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-16"> <div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-16">
@ -89,6 +89,15 @@ const Contact = () => {
</div> </div>
</section> </section>
{/* Soft Transition */}
<div className="relative h-40 bg-gradient-to-b from-slate-800 via-slate-800/80 to-slate-900/60">
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-slate-800/30 to-slate-900/80"></div>
<div className="absolute inset-0 bg-gradient-to-b from-slate-800/20 via-transparent to-slate-900/40"></div>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-24 h-1 bg-gradient-to-r from-transparent via-blue-400/20 to-transparent rounded-full"></div>
</div>
</div>
{/* Office Locations */} {/* Office Locations */}
<section className="py-24 bg-slate-900"> <section className="py-24 bg-slate-900">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
@ -171,7 +180,7 @@ const Contact = () => {
</section> </section>
{/* Operations Manager */} {/* Operations Manager */}
<section className="py-24 bg-slate-800"> <section className="py-24 bg-slate-800 section-transition-soft">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto">
<div className="text-center mb-12"> <div className="text-center mb-12">
@ -238,7 +247,7 @@ const Contact = () => {
</section> </section>
{/* Interactive Map Section */} {/* Interactive Map Section */}
<section className="py-24 bg-slate-800"> <section className="py-24 bg-slate-800 section-transition-soft">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto"> <div className="max-w-6xl mx-auto">
<div className="text-center mb-12"> <div className="text-center mb-12">
@ -338,6 +347,15 @@ const Contact = () => {
</div> </div>
</section> </section>
{/* Soft Transition */}
<div className="relative h-40 bg-gradient-to-b from-slate-800 via-slate-800/80 to-slate-900/60">
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-slate-800/30 to-slate-900/80"></div>
<div className="absolute inset-0 bg-gradient-to-b from-slate-800/20 via-transparent to-slate-900/40"></div>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-24 h-1 bg-gradient-to-r from-transparent via-blue-400/20 to-transparent rounded-full"></div>
</div>
</div>
{/* Contact Forms Section */} {/* Contact Forms Section */}
<section className="py-24 bg-slate-900"> <section className="py-24 bg-slate-900">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">

View File

@ -55,7 +55,7 @@ const Locations = () => {
<main> <main>
{/* Hero Section with Image */} {/* Hero Section with Image */}
<section className="relative py-32 overflow-hidden"> <section className="relative py-32 overflow-hidden hero-transition">
{/* Background Image */} {/* Background Image */}
<div <div
className="absolute inset-0 bg-cover bg-center bg-no-repeat" className="absolute inset-0 bg-cover bg-center bg-no-repeat"
@ -84,7 +84,7 @@ const Locations = () => {
</section> </section>
{/* Locations Grid */} {/* Locations Grid */}
<section className="py-24 bg-slate-800"> <section className="py-24 bg-slate-800 section-transition-soft">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-7xl mx-auto space-y-16"> <div className="max-w-7xl mx-auto space-y-16">
{/* South Texas Office */} {/* South Texas Office */}
@ -223,6 +223,15 @@ const Locations = () => {
</div> </div>
</section> </section>
{/* Soft Transition */}
<div className="relative h-40 bg-gradient-to-b from-slate-800 via-slate-800/80 to-slate-900/60">
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-slate-800/30 to-slate-900/80"></div>
<div className="absolute inset-0 bg-gradient-to-b from-slate-800/20 via-transparent to-slate-900/40"></div>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-24 h-1 bg-gradient-to-r from-transparent via-blue-400/20 to-transparent rounded-full"></div>
</div>
</div>
{/* Operations Manager */} {/* Operations Manager */}
<section className="py-24 bg-slate-900"> <section className="py-24 bg-slate-900">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
@ -297,7 +306,7 @@ const Locations = () => {
</section> </section>
{/* Service Areas */} {/* Service Areas */}
<section className="py-24 bg-slate-800"> <section className="py-24 bg-slate-800 section-transition-soft">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto text-center"> <div className="max-w-6xl mx-auto text-center">
<h2 className="text-4xl font-bold text-white mb-12">Service Areas</h2> <h2 className="text-4xl font-bold text-white mb-12">Service Areas</h2>

View File

@ -47,7 +47,7 @@ const Services = () => {
<main> <main>
{/* Hero Section with Image */} {/* Hero Section with Image */}
<section className="relative py-32 overflow-hidden"> <section className="relative py-32 overflow-hidden hero-transition">
{/* Background Image */} {/* Background Image */}
<div <div
className="absolute inset-0 bg-cover bg-center bg-no-repeat" className="absolute inset-0 bg-cover bg-center bg-no-repeat"
@ -76,7 +76,7 @@ const Services = () => {
</section> </section>
{/* Services Section */} {/* Services Section */}
<section className="py-24 bg-slate-800"> <section className="py-24 bg-slate-800 bg-pattern-grid section-transition-soft">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-16">
@ -91,7 +91,7 @@ const Services = () => {
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4"> <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{services.map((service, index) => ( {services.map((service, index) => (
<div key={index} className="bg-slate-700/50 border border-slate-600 rounded-lg p-4 hover:bg-slate-700/70 transition-all duration-300"> <div key={index} className="card-enhanced card-hover hover-slide p-4">
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<CheckCircle className="h-5 w-5 text-blue-400 flex-shrink-0" /> <CheckCircle className="h-5 w-5 text-blue-400 flex-shrink-0" />
<span className="text-white font-medium">{service}</span> <span className="text-white font-medium">{service}</span>
@ -112,7 +112,7 @@ const Services = () => {
<div className="space-y-4"> <div className="space-y-4">
{specialties.map((specialty, index) => ( {specialties.map((specialty, index) => (
<div key={index} className="bg-slate-700/50 border border-slate-600 rounded-lg p-6 hover:bg-slate-700/70 transition-all duration-300"> <div key={index} className="card-enhanced card-hover hover-slide p-6">
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<Award className="h-6 w-6 text-blue-400 flex-shrink-0" /> <Award className="h-6 w-6 text-blue-400 flex-shrink-0" />
<div> <div>
@ -129,8 +129,17 @@ const Services = () => {
</div> </div>
</section> </section>
{/* Soft Transition */}
<div className="relative h-40 bg-gradient-to-b from-slate-800 via-slate-800/80 to-slate-900/60">
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-slate-800/30 to-slate-900/80"></div>
<div className="absolute inset-0 bg-gradient-to-b from-slate-800/20 via-transparent to-slate-900/40"></div>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-24 h-1 bg-gradient-to-r from-transparent via-blue-400/20 to-transparent rounded-full"></div>
</div>
</div>
{/* Certifications Section */} {/* Certifications Section */}
<section className="py-24 bg-slate-900"> <section className="py-24 bg-slate-900 bg-pattern-lines section-transition-gradient">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto text-center"> <div className="max-w-6xl mx-auto text-center">
<div className="flex items-center justify-center space-x-4 mb-12"> <div className="flex items-center justify-center space-x-4 mb-12">
@ -142,7 +151,7 @@ const Services = () => {
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-6"> <div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-6">
{certifications.map((cert, index) => ( {certifications.map((cert, index) => (
<div key={index} className="bg-slate-800/50 border border-slate-600 rounded-xl p-6 hover:bg-slate-800/70 transition-all duration-300"> <div key={index} className="card-enhanced card-hover hover-slide p-6">
<div className="space-y-4"> <div className="space-y-4">
<div className="w-12 h-12 bg-blue-500/20 rounded-lg flex items-center justify-center mx-auto"> <div className="w-12 h-12 bg-blue-500/20 rounded-lg flex items-center justify-center mx-auto">
<Shield className="h-6 w-6 text-blue-400" /> <Shield className="h-6 w-6 text-blue-400" />
@ -154,19 +163,19 @@ const Services = () => {
</div> </div>
<div className="mt-16 grid grid-cols-1 md:grid-cols-3 gap-8"> <div className="mt-16 grid grid-cols-1 md:grid-cols-3 gap-8">
<div className="bg-slate-800/50 border border-slate-600 rounded-xl p-8"> <div className="card-enhanced card-hover hover-slide p-8">
<Users className="h-12 w-12 text-blue-400 mx-auto mb-4" /> <Users className="h-12 w-12 text-blue-400 mx-auto mb-4" />
<h3 className="text-xl font-bold text-white mb-2">Expert Team</h3> <h3 className="text-xl font-bold text-white mb-2">Expert Team</h3>
<p className="text-slate-400">Certified professionals with decades of experience in industrial welding</p> <p className="text-slate-400">Certified professionals with decades of experience in industrial welding</p>
</div> </div>
<div className="bg-slate-800/50 border border-slate-600 rounded-xl p-8"> <div className="card-enhanced card-hover hover-slide p-8">
<Settings className="h-12 w-12 text-blue-400 mx-auto mb-4" /> <Settings className="h-12 w-12 text-blue-400 mx-auto mb-4" />
<h3 className="text-xl font-bold text-white mb-2">Modern Equipment</h3> <h3 className="text-xl font-bold text-white mb-2">Modern Equipment</h3>
<p className="text-slate-400">State-of-the-art welding equipment and fabrication tools</p> <p className="text-slate-400">State-of-the-art welding equipment and fabrication tools</p>
</div> </div>
<div className="bg-slate-800/50 border border-slate-600 rounded-xl p-8"> <div className="card-enhanced card-hover hover-slide p-8">
<Award className="h-12 w-12 text-blue-400 mx-auto mb-4" /> <Award className="h-12 w-12 text-blue-400 mx-auto mb-4" />
<h3 className="text-xl font-bold text-white mb-2">Quality Assurance</h3> <h3 className="text-xl font-bold text-white mb-2">Quality Assurance</h3>
<p className="text-slate-400">Rigorous quality control and safety standards on every project</p> <p className="text-slate-400">Rigorous quality control and safety standards on every project</p>