Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <meta name="csrf-token" content="{{ csrf_token() }}"> | |
| <title>{% block title %}Too Many Cables - Premium Cables & Accessories{% endblock %}</title> | |
| <!-- <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> --> | |
| <link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}"> | |
| <meta name="description" content="{% block description %}Too Many Cables - Your trusted source for premium USB-C, HDMI, charging cables and tech accessories. Fast shipping, lifetime warranty.{% endblock %}"> | |
| <!-- Ultra-Modern Professional Design --> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| html, body { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) ; | |
| min-height: 100vh ; | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif ; | |
| line-height: 1.6 ; | |
| color: #333 ; | |
| } | |
| /* Premium Navigation Header */ | |
| .navbar { | |
| background: linear-gradient(135deg, rgba(255, 255, 255, 0.98), rgba(255, 255, 255, 0.95)) ; | |
| backdrop-filter: blur(25px) ; | |
| -webkit-backdrop-filter: blur(25px) ; | |
| box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12), 0 4px 16px rgba(102, 126, 234, 0.08) ; | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.3) ; | |
| padding: 1.5rem 0 ; | |
| position: sticky ; | |
| top: 0 ; | |
| z-index: 1000 ; | |
| } | |
| .nav-container { | |
| max-width: 1400px ; | |
| margin: 0 auto ; | |
| padding: 0 2.5rem ; | |
| display: flex ; | |
| justify-content: space-between ; | |
| align-items: center ; | |
| } | |
| .nav-brand { | |
| display: flex ; | |
| align-items: center ; | |
| } | |
| .brand-logo { | |
| display: flex ; | |
| align-items: center ; | |
| text-decoration: none ; | |
| color: #1a202c ; | |
| font-size: 2rem ; | |
| font-weight: 800 ; | |
| letter-spacing: -0.03em ; | |
| transition: all 0.3s ease ; | |
| } | |
| .brand-logo:hover { | |
| transform: scale(1.02) ; | |
| color: #667eea ; | |
| } | |
| .logo-icon { | |
| font-size: 2.5rem ; | |
| margin-right: 0.75rem ; | |
| filter: drop-shadow(0 4px 8px rgba(0,0,0,0.15)) ; | |
| transition: all 0.3s ease ; | |
| } | |
| .brand-logo:hover .logo-icon { | |
| transform: rotate(5deg) ; | |
| } | |
| .brand-text { | |
| background: linear-gradient(135deg, #1a202c 0%, #667eea 100%) ; | |
| -webkit-background-clip: text ; | |
| -webkit-text-fill-color: transparent ; | |
| background-clip: text ; | |
| } | |
| /* Navigation Menu */ | |
| .nav-menu { | |
| display: flex ; | |
| list-style: none ; | |
| gap: 0.25rem ; | |
| align-items: center ; | |
| } | |
| .nav-link { | |
| text-decoration: none ; | |
| color: #2d3748 ; | |
| font-weight: 600 ; | |
| padding: 0.85rem 1.5rem ; | |
| border-radius: 14px ; | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) ; | |
| font-size: 1rem ; | |
| position: relative ; | |
| overflow: hidden ; | |
| } | |
| .nav-link::before { | |
| content: '' ; | |
| position: absolute ; | |
| top: 0 ; | |
| left: -100% ; | |
| width: 100% ; | |
| height: 100% ; | |
| background: linear-gradient(135deg, rgba(102, 126, 234, 0.15), rgba(118, 75, 162, 0.15)) ; | |
| transition: left 0.3s ease ; | |
| z-index: -1 ; | |
| } | |
| .nav-link:hover { | |
| background: linear-gradient(135deg, rgba(102, 126, 234, 0.12), rgba(118, 75, 162, 0.08)) ; | |
| color: #667eea ; | |
| transform: translateY(-2px) ; | |
| box-shadow: 0 8px 24px rgba(102, 126, 234, 0.15) ; | |
| } | |
| .nav-link:hover::before { | |
| left: 0 ; | |
| } | |
| .nav-link.active { | |
| background: linear-gradient(135deg, rgba(102, 126, 234, 0.15), rgba(118, 75, 162, 0.1)) ; | |
| color: #667eea ; | |
| } | |
| /* Special styling for Live Chat button */ | |
| .chat-link { | |
| background: linear-gradient(135deg, #667eea, #764ba2) ; | |
| color: white ; | |
| font-weight: 700 ; | |
| box-shadow: 0 6px 20px rgba(102, 126, 234, 0.3) ; | |
| } | |
| .chat-link:hover { | |
| background: linear-gradient(135deg, #5a6fd8, #6a42a0) ; | |
| transform: translateY(-3px) ; | |
| box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4) ; | |
| } | |
| .chat-icon { | |
| margin-right: 0.5rem ; | |
| } | |
| /* User Menu and Buttons */ | |
| .nav-user { | |
| display: flex ; | |
| align-items: center ; | |
| gap: 1rem ; | |
| } | |
| .user-menu { | |
| display: flex ; | |
| align-items: center ; | |
| gap: 1rem ; | |
| } | |
| .user-greeting { | |
| color: #2d3748 ; | |
| font-weight: 500 ; | |
| font-size: 0.95rem ; | |
| background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)) ; | |
| padding: 0.5rem 1rem ; | |
| border-radius: 8px ; | |
| backdrop-filter: blur(5px) ; | |
| border: 1px solid rgba(102, 126, 234, 0.15) ; | |
| } | |
| .btn { | |
| display: inline-flex ; | |
| align-items: center ; | |
| justify-content: center ; | |
| padding: 0.7rem 1.5rem ; | |
| border-radius: 12px ; | |
| font-weight: 600 ; | |
| font-size: 0.95rem ; | |
| text-decoration: none ; | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) ; | |
| border: none ; | |
| cursor: pointer ; | |
| } | |
| .btn-outline { | |
| background: transparent ; | |
| color: #2d3748 ; | |
| border: 2px solid rgba(45, 55, 72, 0.2) ; | |
| } | |
| .btn-outline:hover { | |
| background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)) ; | |
| border-color: #667eea ; | |
| color: #667eea ; | |
| transform: translateY(-2px) ; | |
| box-shadow: 0 6px 20px rgba(102, 126, 234, 0.2) ; | |
| } | |
| /* Primary Button Style for User Actions */ | |
| .nav-user .btn-outline { | |
| background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.05)) ; | |
| backdrop-filter: blur(10px) ; | |
| border: 2px solid rgba(255, 255, 255, 0.3) ; | |
| color: #2d3748 ; | |
| font-weight: 600 ; | |
| text-shadow: 0 1px 2px rgba(255, 255, 255, 0.5) ; | |
| } | |
| .nav-user .btn-outline:hover { | |
| background: linear-gradient(135deg, #667eea, #764ba2) ; | |
| color: white ; | |
| border-color: transparent ; | |
| box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3) ; | |
| text-shadow: none ; | |
| } | |
| /* Mobile Navigation Toggle */ | |
| .nav-toggle { | |
| display: none ; | |
| flex-direction: column ; | |
| cursor: pointer ; | |
| padding: 0.5rem ; | |
| } | |
| .nav-toggle .bar { | |
| width: 25px ; | |
| height: 3px ; | |
| background: #2d3748 ; | |
| margin: 3px 0 ; | |
| transition: 0.3s ; | |
| } | |
| @media (max-width: 768px) { | |
| .nav-toggle { | |
| display: flex ; | |
| } | |
| .nav-menu { | |
| position: fixed ; | |
| left: -100% ; | |
| top: 70px ; | |
| flex-direction: column ; | |
| background: rgba(255, 255, 255, 0.95) ; | |
| backdrop-filter: blur(20px) ; | |
| width: 100% ; | |
| text-align: center ; | |
| transition: 0.3s ; | |
| box-shadow: 0 10px 27px rgba(0, 0, 0, 0.05) ; | |
| padding: 2rem 0 ; | |
| } | |
| .nav-menu.active { | |
| left: 0 ; | |
| } | |
| .nav-menu .nav-link { | |
| padding: 1rem ; | |
| display: block ; | |
| margin: 0.5rem 0 ; | |
| } | |
| .nav-user { | |
| position: absolute ; | |
| right: 70px ; | |
| top: 50% ; | |
| transform: translateY(-50%) ; | |
| } | |
| } | |
| /* Mobile responsiveness */ | |
| @media (max-width: 768px) { | |
| .nav-container { | |
| padding: 0 1.5rem ; | |
| } | |
| .brand-logo { | |
| font-size: 1.5rem ; | |
| } | |
| .logo-icon { | |
| font-size: 2rem ; | |
| } | |
| .nav-links { | |
| gap: 0 ; | |
| } | |
| .nav-links a { | |
| padding: 0.6rem 1rem ; | |
| font-size: 0.9rem ; | |
| } | |
| } | |
| /* Hero Section - Premium Design */ | |
| .hero, .hero-section { | |
| background: linear-gradient(135deg, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.08)) ; | |
| backdrop-filter: blur(25px) ; | |
| -webkit-backdrop-filter: blur(25px) ; | |
| border-radius: 32px ; | |
| border: 1px solid rgba(255, 255, 255, 0.25) ; | |
| padding: 5rem 4rem ; | |
| margin: 4rem 3rem 5rem 3rem ; | |
| max-width: calc(100% - 6rem) ; | |
| color: white ; | |
| text-align: center ; | |
| box-shadow: 0 32px 96px rgba(0, 0, 0, 0.2), 0 16px 48px rgba(102, 126, 234, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.3) ; | |
| position: relative ; | |
| overflow: hidden ; | |
| } | |
| .hero-section::before { | |
| content: '' ; | |
| position: absolute ; | |
| top: 0 ; | |
| left: 0 ; | |
| right: 0 ; | |
| bottom: 0 ; | |
| background: radial-gradient(circle at 30% 20%, rgba(255,255,255,0.15) 0%, transparent 70%), | |
| linear-gradient(135deg, rgba(255,255,255,0.08) 0%, transparent 50%, rgba(255,255,255,0.05) 100%) ; | |
| pointer-events: none ; | |
| } | |
| .hero-section::after { | |
| content: '' ; | |
| position: absolute ; | |
| top: -50% ; | |
| left: -50% ; | |
| width: 200% ; | |
| height: 200% ; | |
| background: conic-gradient(from 0deg, transparent, rgba(255,255,255,0.03), transparent 60deg) ; | |
| animation: rotate 20s linear infinite ; | |
| pointer-events: none ; | |
| } | |
| @keyframes rotate { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .hero h1, .hero-section h1, .hero-title { | |
| font-size: 4rem ; | |
| font-weight: 800 ; | |
| margin-bottom: 2rem ; | |
| text-shadow: 0 8px 16px rgba(0,0,0,0.3) ; | |
| letter-spacing: -0.05em ; | |
| line-height: 1.05 ; | |
| position: relative ; | |
| z-index: 2 ; | |
| background: linear-gradient(135deg, #ffffff 0%, #f0f8ff 100%) ; | |
| -webkit-background-clip: text ; | |
| -webkit-text-fill-color: transparent ; | |
| background-clip: text ; | |
| } | |
| .hero p, .hero-section p, .hero-subtitle { | |
| font-size: 1.4rem ; | |
| margin-bottom: 3rem ; | |
| opacity: 0.95 ; | |
| font-weight: 400 ; | |
| max-width: 800px ; | |
| margin-left: auto ; | |
| margin-right: auto ; | |
| position: relative ; | |
| z-index: 2 ; | |
| line-height: 1.7 ; | |
| color: white ; | |
| } | |
| .hero-buttons, .hero-actions { | |
| display: flex ; | |
| gap: 1.5rem ; | |
| justify-content: center ; | |
| flex-wrap: wrap ; | |
| position: relative ; | |
| z-index: 2 ; | |
| } | |
| /* Hero container styling */ | |
| .hero-container { | |
| max-width: 1400px ; | |
| margin: 0 auto ; | |
| padding: 2rem ; | |
| text-align: center ; | |
| } | |
| /* Section Headers */ | |
| .section-header { | |
| text-align: center ; | |
| margin: 6rem 0 4rem 0 ; | |
| } | |
| .section-header h2 { | |
| font-size: 3.2rem ; | |
| font-weight: 700 ; | |
| margin-bottom: 1.5rem ; | |
| background: linear-gradient(135deg, #ffffff 0%, #e6f3ff 100%) ; | |
| -webkit-background-clip: text ; | |
| -webkit-text-fill-color: transparent ; | |
| background-clip: text ; | |
| text-shadow: none ; | |
| letter-spacing: -0.03em ; | |
| } | |
| .section-header p { | |
| font-size: 1.2rem ; | |
| color: rgba(255, 255, 255, 0.8) ; | |
| max-width: 600px ; | |
| margin: 0 auto ; | |
| } | |
| /* Feature Cards - Ultra Premium */ | |
| .features-grid { | |
| display: grid ; | |
| grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)) ; | |
| gap: 2.5rem ; | |
| margin: 4rem 0 6rem 0 ; | |
| } | |
| .feature-card { | |
| background: linear-gradient(145deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.05)) ; | |
| backdrop-filter: blur(25px) ; | |
| -webkit-backdrop-filter: blur(25px) ; | |
| border-radius: 24px ; | |
| border: 1px solid rgba(255, 255, 255, 0.2) ; | |
| padding: 3rem 2.5rem ; | |
| color: white ; | |
| text-align: center ; | |
| box-shadow: 0 20px 60px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.2) ; | |
| transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1) ; | |
| position: relative ; | |
| overflow: hidden ; | |
| } | |
| .feature-card::before { | |
| content: '' ; | |
| position: absolute ; | |
| top: 0 ; | |
| left: 0 ; | |
| right: 0 ; | |
| bottom: 0 ; | |
| background: radial-gradient(circle at 50% 0%, rgba(255,255,255,0.12) 0%, transparent 70%) ; | |
| opacity: 0 ; | |
| transition: opacity 0.4s ease ; | |
| pointer-events: none ; | |
| } | |
| .feature-card:hover { | |
| transform: translateY(-12px) scale(1.02) ; | |
| box-shadow: 0 32px 80px rgba(0, 0, 0, 0.18), 0 16px 48px rgba(102, 126, 234, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.3) ; | |
| } | |
| .feature-card:hover::before { | |
| opacity: 1 ; | |
| } | |
| .feature-icon { | |
| font-size: 3rem ; | |
| margin-bottom: 1.5rem ; | |
| display: block ; | |
| filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2)) ; | |
| } | |
| .feature-card h3 { | |
| font-size: 1.6rem ; | |
| font-weight: 700 ; | |
| margin-bottom: 1.5rem ; | |
| color: #fff ; | |
| position: relative ; | |
| z-index: 1 ; | |
| letter-spacing: -0.025em ; | |
| } | |
| .feature-card p { | |
| font-size: 1.1rem ; | |
| opacity: 0.9 ; | |
| line-height: 1.7 ; | |
| position: relative ; | |
| z-index: 1 ; | |
| } | |
| /* Support CTA Section */ | |
| .support-cta { | |
| background: linear-gradient(135deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.04)) ; | |
| backdrop-filter: blur(25px) ; | |
| -webkit-backdrop-filter: blur(25px) ; | |
| border-radius: 32px ; | |
| border: 1px solid rgba(255, 255, 255, 0.2) ; | |
| margin: 4rem 3rem 5rem 3rem ; | |
| max-width: calc(100% - 6rem) ; | |
| padding: 4rem ; | |
| text-align: center ; | |
| box-shadow: 0 32px 96px rgba(0, 0, 0, 0.15), 0 16px 48px rgba(102, 126, 234, 0.1) ; | |
| } | |
| .cta-content h2 { | |
| color: white ; | |
| font-size: 2.5rem ; | |
| font-weight: 700 ; | |
| margin-bottom: 1.5rem ; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.2) ; | |
| } | |
| .cta-content p { | |
| color: rgba(255, 255, 255, 0.9) ; | |
| font-size: 1.2rem ; | |
| line-height: 1.6 ; | |
| margin-bottom: 3rem ; | |
| max-width: 600px ; | |
| margin-left: auto ; | |
| margin-right: auto ; | |
| } | |
| .cta-actions { | |
| display: flex ; | |
| gap: 1.5rem ; | |
| justify-content: center ; | |
| margin-bottom: 4rem ; | |
| flex-wrap: wrap ; | |
| } | |
| .cta-features { | |
| display: flex ; | |
| justify-content: center ; | |
| align-items: center ; | |
| gap: 3rem ; | |
| flex-wrap: wrap ; | |
| margin-top: 3rem ; | |
| } | |
| .cta-feature { | |
| display: flex ; | |
| flex-direction: column ; | |
| align-items: center ; | |
| gap: 1rem ; | |
| text-align: center ; | |
| min-width: 150px ; | |
| } | |
| .cta-feature .feature-icon { | |
| font-size: 2.5rem ; | |
| margin-bottom: 0.5rem ; | |
| filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2)) ; | |
| display: block ; | |
| } | |
| .cta-feature span:last-child { | |
| color: white ; | |
| font-weight: 600 ; | |
| font-size: 1rem ; | |
| text-shadow: 0 1px 2px rgba(0,0,0,0.2) ; | |
| } | |
| /* Customer Reviews Section */ | |
| .reviews { | |
| padding: 5rem 0 ; | |
| margin: 2rem 0 ; | |
| } | |
| .reviews .container { | |
| max-width: 1400px ; | |
| margin: 0 auto ; | |
| padding: 0 3rem ; | |
| } | |
| .reviews .section-title { | |
| color: white ; | |
| font-size: 2.5rem ; | |
| font-weight: 700 ; | |
| text-align: center ; | |
| margin-bottom: 4rem ; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.2) ; | |
| } | |
| .reviews-grid { | |
| display: grid ; | |
| grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)) ; | |
| gap: 2.5rem ; | |
| margin-bottom: 3rem ; | |
| } | |
| .review-card { | |
| background: linear-gradient(145deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.05)) ; | |
| backdrop-filter: blur(25px) ; | |
| -webkit-backdrop-filter: blur(25px) ; | |
| border-radius: 24px ; | |
| border: 1px solid rgba(255, 255, 255, 0.2) ; | |
| padding: 2.5rem ; | |
| color: white ; | |
| box-shadow: 0 20px 60px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.2) ; | |
| transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1) ; | |
| position: relative ; | |
| overflow: hidden ; | |
| } | |
| .review-card::before { | |
| content: '' ; | |
| position: absolute ; | |
| top: 0 ; | |
| left: 0 ; | |
| right: 0 ; | |
| bottom: 0 ; | |
| background: radial-gradient(circle at 50% 0%, rgba(255,255,255,0.08) 0%, transparent 70%) ; | |
| opacity: 0 ; | |
| transition: opacity 0.4s ease ; | |
| pointer-events: none ; | |
| } | |
| .review-card:hover { | |
| transform: translateY(-8px) scale(1.01) ; | |
| box-shadow: 0 32px 80px rgba(0, 0, 0, 0.18), 0 16px 48px rgba(102, 126, 234, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.3) ; | |
| } | |
| .review-card:hover::before { | |
| opacity: 1 ; | |
| } | |
| .review-rating { | |
| font-size: 1.5rem ; | |
| margin-bottom: 1.5rem ; | |
| filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2)) ; | |
| } | |
| .review-text { | |
| font-size: 1.1rem ; | |
| line-height: 1.7 ; | |
| margin-bottom: 2rem ; | |
| color: rgba(255, 255, 255, 0.95) ; | |
| font-style: italic ; | |
| position: relative ; | |
| z-index: 1 ; | |
| } | |
| .review-text::before { | |
| content: '"' ; | |
| font-size: 3rem ; | |
| color: rgba(102, 126, 234, 0.3) ; | |
| position: absolute ; | |
| top: -1rem ; | |
| left: -0.5rem ; | |
| font-family: serif ; | |
| line-height: 1 ; | |
| } | |
| .review-author { | |
| font-weight: 600 ; | |
| color: rgba(255, 255, 255, 0.8) ; | |
| font-size: 0.95rem ; | |
| position: relative ; | |
| z-index: 1 ; | |
| } | |
| /* Simple Footer */ | |
| .footer { | |
| background: linear-gradient(135deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.04)) ; | |
| backdrop-filter: blur(25px) ; | |
| -webkit-backdrop-filter: blur(25px) ; | |
| border-radius: 32px 32px 0 0 ; | |
| border: 1px solid rgba(255, 255, 255, 0.2) ; | |
| border-bottom: none ; | |
| margin: 4rem 3rem 0 3rem ; | |
| max-width: calc(100% - 6rem) ; | |
| box-shadow: 0 -16px 48px rgba(0, 0, 0, 0.15), 0 -8px 24px rgba(102, 126, 234, 0.1) ; | |
| color: white ; | |
| padding: 4rem 0 6rem 0 ; /* extra bottom padding to make room for footer-bottom */ | |
| position: relative ; /* make footer-bottom position relative to footer */ | |
| } | |
| .footer-container { | |
| max-width: 1400px ; | |
| margin: 0 auto ; | |
| padding: 0 3rem ; | |
| text-align: center ; | |
| } | |
| .footer-simple h3 { | |
| color: white ; | |
| font-size: 2rem ; | |
| font-weight: 700 ; | |
| margin-bottom: 1rem ; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.2) ; | |
| } | |
| .footer-simple p { | |
| color: rgba(255, 255, 255, 0.8) ; | |
| line-height: 1.6 ; | |
| margin: 0 ; | |
| font-size: 1.1rem ; | |
| max-width: 500px ; | |
| margin: 0 auto ; | |
| } | |
| .footer-bottom { | |
| position: absolute ; | |
| left: 0 ; | |
| right: 0 ; | |
| bottom: 0 ; | |
| background: rgba(0, 0, 0, 0.06) ; /* subtle divider look */ | |
| border-top: 1px solid rgba(255, 255, 255, 0.06) ; | |
| padding: 1rem 0 ; | |
| } | |
| .footer-bottom p { | |
| color: rgba(255, 255, 255, 0.75) ; | |
| margin: 0 ; | |
| font-size: 0.9rem ; | |
| text-align: center ; | |
| } | |
| /* Premium Buttons */ | |
| .btn { | |
| display: inline-flex ; | |
| align-items: center ; | |
| justify-content: center ; | |
| padding: 1rem 2rem ; | |
| border-radius: 16px ; | |
| text-decoration: none ; | |
| font-weight: 600 ; | |
| font-size: 1rem ; | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) ; | |
| position: relative ; | |
| overflow: hidden ; | |
| border: none ; | |
| cursor: pointer ; | |
| background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.05)) ; | |
| backdrop-filter: blur(10px) ; | |
| color: white ; | |
| box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1) ; | |
| } | |
| .btn-primary { | |
| background: linear-gradient(135deg, #667eea, #764ba2) ; | |
| color: white ; | |
| box-shadow: 0 6px 20px rgba(102, 126, 234, 0.3) ; | |
| } | |
| .btn-primary:hover { | |
| background: linear-gradient(135deg, #5a6fd8, #6a42a0) ; | |
| transform: translateY(-3px) ; | |
| box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4) ; | |
| } | |
| .btn-outline { | |
| background: transparent ; | |
| border: 2px solid rgba(255, 255, 255, 0.3) ; | |
| color: white ; | |
| } | |
| .btn-outline:hover { | |
| background: linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.05)) ; | |
| border-color: rgba(255, 255, 255, 0.5) ; | |
| } | |
| .btn::before { | |
| content: '' ; | |
| position: absolute ; | |
| top: 0 ; | |
| left: -100% ; | |
| width: 100% ; | |
| height: 100% ; | |
| background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent) ; | |
| transition: left 0.5s ; | |
| } | |
| .btn:hover:not(.btn-primary):not(.btn-outline) { | |
| background: linear-gradient(135deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.15)) ; | |
| transform: translateY(-2px) ; | |
| box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2) ; | |
| } | |
| .btn:hover::before { | |
| left: 100% ; | |
| } | |
| /* Container Improvements */ | |
| .container { | |
| max-width: 1400px ; | |
| margin: 0 auto ; | |
| padding: 2rem ; | |
| } | |
| /* Products Section */ | |
| .popular-products { | |
| padding: 5rem 0 ; | |
| margin: 2rem 0 ; | |
| } | |
| .popular-products .container { | |
| max-width: 1400px ; | |
| margin: 0 auto ; | |
| padding: 0 3rem ; | |
| } | |
| .popular-products .section-title { | |
| color: white ; | |
| font-size: 2.5rem ; | |
| font-weight: 700 ; | |
| text-align: center ; | |
| margin-bottom: 4rem ; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.2) ; | |
| } | |
| .products-grid { | |
| display: grid ; | |
| grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) ; | |
| gap: 2.5rem ; | |
| margin-bottom: 4rem ; | |
| } | |
| .product-card { | |
| background: linear-gradient(145deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.05)) ; | |
| backdrop-filter: blur(25px) ; | |
| -webkit-backdrop-filter: blur(25px) ; | |
| border-radius: 24px ; | |
| border: 1px solid rgba(255, 255, 255, 0.2) ; | |
| padding: 2.5rem ; | |
| color: white ; | |
| text-align: center ; | |
| box-shadow: 0 20px 60px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.2) ; | |
| transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1) ; | |
| position: relative ; | |
| overflow: hidden ; | |
| } | |
| .product-card::before { | |
| content: '' ; | |
| position: absolute ; | |
| top: 0 ; | |
| left: 0 ; | |
| right: 0 ; | |
| bottom: 0 ; | |
| background: radial-gradient(circle at 50% 0%, rgba(255,255,255,0.08) 0%, transparent 70%) ; | |
| opacity: 0 ; | |
| transition: opacity 0.4s ease ; | |
| pointer-events: none ; | |
| } | |
| .product-card:hover { | |
| transform: translateY(-12px) scale(1.02) ; | |
| box-shadow: 0 32px 80px rgba(0, 0, 0, 0.18), 0 16px 48px rgba(102, 126, 234, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.3) ; | |
| } | |
| .product-card:hover::before { | |
| opacity: 1 ; | |
| } | |
| .product-image { | |
| font-size: 4rem ; | |
| margin-bottom: 1.5rem ; | |
| filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2)) ; | |
| height: 80px ; | |
| display: flex ; | |
| align-items: center ; | |
| justify-content: center ; | |
| } | |
| .product-card h3 { | |
| font-size: 1.4rem ; | |
| font-weight: 700 ; | |
| margin-bottom: 1rem ; | |
| color: #fff ; | |
| position: relative ; | |
| z-index: 1 ; | |
| letter-spacing: -0.025em ; | |
| } | |
| .product-description { | |
| font-size: 0.95rem ; | |
| opacity: 0.9 ; | |
| line-height: 1.6 ; | |
| margin-bottom: 1.5rem ; | |
| position: relative ; | |
| z-index: 1 ; | |
| color: rgba(255, 255, 255, 0.8) ; | |
| } | |
| .product-price { | |
| font-size: 1.8rem ; | |
| font-weight: 700 ; | |
| color: #000000 ; | |
| margin-bottom: 1rem ; | |
| text-shadow: 0 1px 2px rgba(255, 255, 255, 0.8) ; | |
| position: relative ; | |
| z-index: 1 ; | |
| } | |
| .product-rating { | |
| font-size: 0.9rem ; | |
| margin-bottom: 2rem ; | |
| color: rgba(255, 255, 255, 0.8) ; | |
| position: relative ; | |
| z-index: 1 ; | |
| } | |
| .product-card .btn { | |
| width: 100% ; | |
| margin-top: auto ; | |
| position: relative ; | |
| z-index: 1 ; | |
| } | |
| .section-cta { | |
| text-align: center ; | |
| margin-top: 3rem ; | |
| } | |
| /* Page Header with Glassmorphism */ | |
| .page-header { | |
| padding: 6rem 0 4rem 0 ; | |
| margin-bottom: 3rem ; | |
| } | |
| .header-container { | |
| max-width: 1400px ; | |
| margin: 0 auto ; | |
| padding: 0 3rem ; | |
| } | |
| .header-card { | |
| background: linear-gradient(135deg, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.08)) ; | |
| backdrop-filter: blur(25px) ; | |
| -webkit-backdrop-filter: blur(25px) ; | |
| border-radius: 32px ; | |
| border: 1px solid rgba(255, 255, 255, 0.25) ; | |
| padding: 4rem 3rem ; | |
| margin: 0 3rem ; | |
| max-width: calc(100% - 6rem) ; | |
| color: white ; | |
| text-align: center ; | |
| box-shadow: 0 32px 96px rgba(0, 0, 0, 0.2), 0 16px 48px rgba(102, 126, 234, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.3) ; | |
| position: relative ; | |
| overflow: hidden ; | |
| } | |
| .header-card::before { | |
| content: '' ; | |
| position: absolute ; | |
| top: 0 ; | |
| left: 0 ; | |
| right: 0 ; | |
| bottom: 0 ; | |
| background: radial-gradient(circle at 50% 0%, rgba(255,255,255,0.12) 0%, transparent 70%) ; | |
| opacity: 0.5 ; | |
| pointer-events: none ; | |
| } | |
| .header-card h1 { | |
| font-size: 3rem ; | |
| font-weight: 700 ; | |
| margin-bottom: 1.5rem ; | |
| color: white ; | |
| text-shadow: 0 4px 8px rgba(0,0,0,0.3) ; | |
| position: relative ; | |
| z-index: 1 ; | |
| letter-spacing: -0.025em ; | |
| } | |
| .header-card p { | |
| font-size: 1.3rem ; | |
| line-height: 1.6 ; | |
| color: rgba(255, 255, 255, 0.9) ; | |
| margin: 0 ; | |
| max-width: 600px ; | |
| margin: 0 auto ; | |
| position: relative ; | |
| z-index: 1 ; | |
| } | |
| /* Typography Improvements */ | |
| h1, h2, h3, h4, h5, h6 { | |
| font-weight: 600 ; | |
| letter-spacing: -0.025em ; | |
| } | |
| h2 { | |
| color: white ; | |
| font-size: 2.5rem ; | |
| margin-bottom: 2rem ; | |
| text-align: center ; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.2) ; | |
| } | |
| /* Responsive Design */ | |
| @media (max-width: 768px) { | |
| .hero, .hero-section { | |
| margin: 3rem 1.5rem 4rem 1.5rem ; | |
| max-width: calc(100% - 3rem) ; | |
| padding: 3rem 2rem ; | |
| border-radius: 24px ; | |
| } | |
| .hero-section h1 { | |
| font-size: 2.5rem ; | |
| } | |
| .nav-container { | |
| padding: 0 1rem ; | |
| } | |
| .container { | |
| padding: 1rem ; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .hero, .hero-section { | |
| margin: 2rem 1rem 3rem 1rem ; | |
| max-width: calc(100% - 2rem) ; | |
| padding: 2.5rem 1.5rem ; | |
| border-radius: 20px ; | |
| } | |
| } | |
| /* Support CTA Responsive Design */ | |
| @media (max-width: 768px) { | |
| .support-cta { | |
| margin: 3rem 1.5rem 4rem 1.5rem ; | |
| max-width: calc(100% - 3rem) ; | |
| padding: 3rem 2rem ; | |
| border-radius: 24px ; | |
| } | |
| .cta-content h2 { | |
| font-size: 2rem ; | |
| } | |
| .cta-features { | |
| gap: 2rem ; | |
| } | |
| .cta-actions { | |
| flex-direction: column ; | |
| align-items: center ; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .support-cta { | |
| margin: 2rem 1rem 3rem 1rem ; | |
| max-width: calc(100% - 2rem) ; | |
| padding: 2.5rem 1.5rem ; | |
| border-radius: 20px ; | |
| } | |
| .cta-content h2 { | |
| font-size: 1.8rem ; | |
| } | |
| .cta-features { | |
| flex-direction: column ; | |
| gap: 1.5rem ; | |
| } | |
| } | |
| /* Reviews Responsive Design */ | |
| @media (max-width: 768px) { | |
| .reviews .container { | |
| padding: 0 1.5rem ; | |
| } | |
| .reviews .section-title { | |
| font-size: 2rem ; | |
| margin-bottom: 3rem ; | |
| } | |
| .reviews-grid { | |
| grid-template-columns: 1fr ; | |
| gap: 2rem ; | |
| } | |
| .review-card { | |
| padding: 2rem ; | |
| border-radius: 20px ; | |
| } | |
| .review-text { | |
| font-size: 1rem ; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .reviews .container { | |
| padding: 0 1rem ; | |
| } | |
| .reviews .section-title { | |
| font-size: 1.8rem ; | |
| } | |
| .review-card { | |
| padding: 1.5rem ; | |
| border-radius: 16px ; | |
| } | |
| .review-text::before { | |
| font-size: 2.5rem ; | |
| top: -0.8rem ; | |
| left: -0.3rem ; | |
| } | |
| } | |
| /* Footer Responsive Design */ | |
| @media (max-width: 768px) { | |
| .footer { | |
| margin: 3rem 1.5rem 0 1.5rem ; | |
| max-width: calc(100% - 3rem) ; | |
| border-radius: 24px 24px 0 0 ; | |
| padding: 3rem 0 3.5rem 0 ; /* room for bottom bar */ | |
| position: relative ; | |
| } | |
| .footer-container { | |
| padding: 0 2rem ; | |
| } | |
| .footer-simple h3 { | |
| font-size: 1.7rem ; | |
| } | |
| .footer-simple p { | |
| font-size: 1rem ; | |
| } | |
| .footer-bottom { | |
| position: absolute ; | |
| left: 0 ; | |
| right: 0 ; | |
| bottom: 0 ; | |
| padding: 1rem 0 ; | |
| background: rgba(0,0,0,0.06) ; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .footer { | |
| margin: 2rem 1rem 0 1rem ; | |
| max-width: calc(100% - 2rem) ; | |
| border-radius: 20px 20px 0 0 ; | |
| padding: 2.5rem 0 3rem 0 ; | |
| position: relative ; | |
| } | |
| .footer-container { | |
| padding: 0 1.5rem ; | |
| } | |
| .footer-simple h3 { | |
| font-size: 1.5rem ; | |
| } | |
| .footer-simple p { | |
| font-size: 0.95rem ; | |
| } | |
| .footer-bottom { | |
| position: absolute ; | |
| left: 0 ; | |
| right: 0 ; | |
| bottom: 0 ; | |
| padding: 1rem 0 ; | |
| background: rgba(0,0,0,0.06) ; | |
| } | |
| .footer-bottom p { | |
| font-size: 0.85rem ; | |
| } | |
| } | |
| /* Product Cards Responsive Design */ | |
| @media (max-width: 768px) { | |
| .popular-products .container { | |
| padding: 0 1.5rem ; | |
| } | |
| .popular-products .section-title { | |
| font-size: 2rem ; | |
| margin-bottom: 3rem ; | |
| } | |
| .products-grid { | |
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)) ; | |
| gap: 2rem ; | |
| } | |
| .product-card { | |
| padding: 2rem ; | |
| border-radius: 20px ; | |
| } | |
| .product-image { | |
| font-size: 3rem ; | |
| height: 60px ; | |
| } | |
| .product-card h3 { | |
| font-size: 1.2rem ; | |
| } | |
| .product-price { | |
| font-size: 1.5rem ; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .popular-products .container { | |
| padding: 0 1rem ; | |
| } | |
| .popular-products .section-title { | |
| font-size: 1.8rem ; | |
| } | |
| .products-grid { | |
| grid-template-columns: 1fr ; | |
| gap: 1.5rem ; | |
| } | |
| .product-card { | |
| padding: 1.5rem ; | |
| border-radius: 16px ; | |
| } | |
| .product-image { | |
| font-size: 2.5rem ; | |
| height: 50px ; | |
| } | |
| } | |
| /* Page Header Responsive Design */ | |
| @media (max-width: 768px) { | |
| .page-header { | |
| padding: 4rem 0 3rem 0 ; | |
| } | |
| .header-container { | |
| padding: 0 1.5rem ; | |
| } | |
| .header-card { | |
| margin: 0 1.5rem ; | |
| max-width: calc(100% - 3rem) ; | |
| padding: 3rem 2rem ; | |
| border-radius: 24px ; | |
| } | |
| .header-card h1 { | |
| font-size: 2.2rem ; | |
| } | |
| .header-card p { | |
| font-size: 1.1rem ; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .page-header { | |
| padding: 3rem 0 2rem 0 ; | |
| } | |
| .header-container { | |
| padding: 0 1rem ; | |
| } | |
| .header-card { | |
| margin: 0 1rem ; | |
| max-width: calc(100% - 2rem) ; | |
| padding: 2.5rem 1.5rem ; | |
| border-radius: 20px ; | |
| } | |
| .header-card h1 { | |
| font-size: 1.9rem ; | |
| } | |
| .header-card p { | |
| font-size: 1rem ; | |
| } | |
| } | |
| /* Product Support CTA Section */ | |
| .product-support-cta { | |
| padding: 4rem 0 ; | |
| margin: 3rem 0 ; | |
| } | |
| .product-support-cta .container { | |
| max-width: 1400px ; | |
| margin: 0 auto ; | |
| padding: 0 3rem ; | |
| } | |
| .product-support-cta .cta-content { | |
| text-align: center ; | |
| max-width: 800px ; | |
| margin: 0 auto ; | |
| } | |
| .product-support-cta h2 { | |
| color: white ; | |
| font-size: 2.2rem ; | |
| font-weight: 700 ; | |
| margin-bottom: 1.5rem ; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.2) ; | |
| } | |
| .product-support-cta p { | |
| color: rgba(255, 255, 255, 0.9) ; | |
| font-size: 1.1rem ; | |
| line-height: 1.6 ; | |
| margin-bottom: 2.5rem ; | |
| } | |
| .product-support-cta .btn { | |
| margin: 0 auto ; | |
| } | |
| /* Product Support CTA Responsive */ | |
| @media (max-width: 768px) { | |
| .product-support-cta .container { | |
| padding: 0 1.5rem ; | |
| } | |
| .product-support-cta h2 { | |
| font-size: 1.8rem ; | |
| } | |
| .product-support-cta p { | |
| font-size: 1rem ; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .product-support-cta .container { | |
| padding: 0 1rem ; | |
| } | |
| .product-support-cta h2 { | |
| font-size: 1.6rem ; | |
| } | |
| } | |
| /* Chat Page Styles */ | |
| .status-indicator { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| margin-top: 1rem; | |
| } | |
| .status-dot { | |
| width: 12px; | |
| height: 12px; | |
| border-radius: 50%; | |
| background: #10b981; | |
| box-shadow: 0 0 10px rgba(16, 185, 129, 0.5); | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| .chat-section { | |
| padding: 4rem 0; | |
| } | |
| .chat-container { | |
| max-width: 800px; | |
| margin: 0 auto; | |
| } | |
| .chat-messages { | |
| height: 500px; | |
| overflow-y: auto; | |
| padding: 2rem; | |
| margin-bottom: 0; | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| border-bottom: none; | |
| border-radius: 20px 20px 0 0; | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(10px); | |
| } | |
| .welcome-message { | |
| display: flex; | |
| gap: 1rem; | |
| align-items: flex-start; | |
| padding: 1.5rem; | |
| background: rgba(102, 126, 234, 0.1); | |
| border-radius: 15px; | |
| border: 1px solid rgba(102, 126, 234, 0.2); | |
| } | |
| .agent-avatar { | |
| font-size: 2rem; | |
| flex-shrink: 0; | |
| } | |
| .message-content h4 { | |
| margin: 0 0 0.5rem 0; | |
| color: #2d3748; | |
| font-weight: 600; | |
| } | |
| .message-content p { | |
| margin: 0; | |
| color: #4a5568; | |
| line-height: 1.5; | |
| } | |
| .user-message { | |
| display: flex; | |
| justify-content: flex-end; | |
| margin-bottom: 1rem; | |
| } | |
| .user-message .message-bubble { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| padding: 1rem 1.5rem; | |
| border-radius: 20px 20px 5px 20px; | |
| max-width: 70%; | |
| word-wrap: break-word; | |
| } | |
| .ai-message { | |
| display: flex; | |
| gap: 1rem; | |
| margin-bottom: 1rem; | |
| } | |
| .ai-message .message-bubble { | |
| background: #f7fafc; | |
| color: #2d3748; | |
| padding: 1rem 1.5rem; | |
| border-radius: 20px 20px 20px 5px; | |
| max-width: 70%; | |
| word-wrap: break-word; | |
| border: 1px solid #e2e8f0; | |
| } | |
| .typing-indicator { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| color: #718096; | |
| font-size: 0.9rem; | |
| margin-bottom: 1rem; | |
| } | |
| .typing-dots { | |
| display: flex; | |
| gap: 3px; | |
| } | |
| .typing-dots span { | |
| width: 6px; | |
| height: 6px; | |
| border-radius: 50%; | |
| background: #718096; | |
| animation: typing 1.4s infinite ease-in-out; | |
| } | |
| .typing-dots span:nth-child(1) { animation-delay: -0.32s; } | |
| .typing-dots span:nth-child(2) { animation-delay: -0.16s; } | |
| @keyframes typing { | |
| 0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; } | |
| 40% { transform: scale(1); opacity: 1; } | |
| } | |
| .chat-input-form { | |
| position: relative; | |
| } | |
| .input-group { | |
| display: flex; | |
| align-items: end; | |
| gap: 1rem; | |
| padding: 1.5rem; | |
| background: rgba(255, 255, 255, 0.95); | |
| border-radius: 0 0 20px 20px; | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| border-top: 1px solid #e2e8f0; | |
| backdrop-filter: blur(10px); | |
| } | |
| .input-group textarea { | |
| flex: 1; | |
| background: transparent; | |
| border: none; | |
| color: #2d3748; | |
| font-size: 1rem; | |
| line-height: 1.5; | |
| resize: none; | |
| outline: none; | |
| min-height: 20px; | |
| max-height: 120px; | |
| } | |
| .input-group textarea::placeholder { | |
| color: #a0aec0; | |
| } | |
| .send-button { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| border: none; | |
| border-radius: 50%; | |
| width: 44px; | |
| height: 44px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| flex-shrink: 0; | |
| } | |
| .send-button:hover:not(:disabled) { | |
| transform: scale(1.05); | |
| box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4); | |
| } | |
| .send-button:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| } | |
| .send-icon { | |
| color: white; | |
| font-size: 1.2rem; | |
| transform: rotate(-45deg); | |
| } | |
| /* Chat Responsive Design */ | |
| @media (max-width: 768px) { | |
| .chat-messages { | |
| height: 400px; | |
| padding: 1rem; | |
| } | |
| .welcome-message { | |
| flex-direction: column; | |
| text-align: center; | |
| } | |
| .user-message .message-bubble, | |
| .ai-message .message-bubble { | |
| max-width: 85%; | |
| } | |
| .input-group { | |
| padding: 1rem; | |
| } | |
| } | |
| /* Floating Chat Widget Styles */ | |
| .floating-chat-widget { | |
| position: fixed; | |
| bottom: 20px; | |
| right: 20px; | |
| z-index: 1000; | |
| font-family: 'Inter', sans-serif; | |
| } | |
| .chat-toggle-btn { | |
| position: relative; | |
| width: 60px; | |
| height: 60px; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4); | |
| transition: all 0.3s ease; | |
| border: 3px solid rgba(255, 255, 255, 0.2); | |
| } | |
| .chat-toggle-btn:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 12px 30px rgba(102, 126, 234, 0.6); | |
| } | |
| .chat-toggle-btn .chat-icon { | |
| font-size: 1.5rem; | |
| color: white; | |
| } | |
| .notification-dot { | |
| position: absolute; | |
| top: -2px; | |
| right: -2px; | |
| width: 12px; | |
| height: 12px; | |
| background: #ef4444; | |
| border-radius: 50%; | |
| border: 2px solid white; | |
| animation: pulse 2s infinite; | |
| } | |
| .chat-window { | |
| position: absolute; | |
| bottom: 80px; | |
| right: 0; | |
| width: 380px; | |
| height: 500px; | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(20px); | |
| border-radius: 20px; | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1); | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| animation: slideUp 0.3s ease; | |
| } | |
| @keyframes slideUp { | |
| from { | |
| opacity: 0; | |
| transform: translateY(20px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .chat-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 1rem 1.5rem; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| border-radius: 20px 20px 0 0; | |
| } | |
| .chat-header-info { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| } | |
| .agent-avatar-small { | |
| font-size: 1.2rem; | |
| } | |
| .agent-info h4 { | |
| margin: 0; | |
| font-size: 1rem; | |
| font-weight: 600; | |
| } | |
| .status-text { | |
| font-size: 0.8rem; | |
| opacity: 0.9; | |
| } | |
| .chat-controls { | |
| display: flex; | |
| gap: 0.5rem; | |
| } | |
| .control-btn { | |
| background: rgba(255, 255, 255, 0.2); | |
| border: none; | |
| color: white; | |
| width: 24px; | |
| height: 24px; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 1rem; | |
| transition: background 0.2s ease; | |
| } | |
| .control-btn:hover { | |
| background: rgba(255, 255, 255, 0.3); | |
| } | |
| .control-btn#close-chat { | |
| background: rgba(255, 193, 193, 0.3); | |
| color: #fff; | |
| } | |
| .control-btn#close-chat:hover { | |
| background: rgba(255, 193, 193, 0.6); | |
| color: #fff; | |
| } | |
| .floating-chat-messages { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 1rem; | |
| } | |
| .welcome-message-floating { | |
| display: flex; | |
| gap: 0.75rem; | |
| align-items: flex-start; | |
| padding: 1rem; | |
| background: rgba(102, 126, 234, 0.1); | |
| border-radius: 12px; | |
| border: 1px solid rgba(102, 126, 234, 0.2); | |
| margin-bottom: 1rem; | |
| } | |
| .welcome-message-floating .agent-avatar { | |
| font-size: 1.5rem; | |
| flex-shrink: 0; | |
| } | |
| .welcome-message-floating .message-content h4 { | |
| margin: 0 0 0.5rem 0; | |
| color: #2d3748; | |
| font-weight: 600; | |
| font-size: 0.9rem; | |
| } | |
| .welcome-message-floating .message-content p { | |
| margin: 0; | |
| color: #4a5568; | |
| line-height: 1.4; | |
| font-size: 0.85rem; | |
| } | |
| .floating-user-message { | |
| display: flex; | |
| justify-content: flex-end; | |
| margin-bottom: 1rem; | |
| } | |
| .floating-user-message .message-bubble { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| padding: 0.75rem 1rem; | |
| border-radius: 15px 15px 4px 15px; | |
| max-width: 80%; | |
| word-wrap: break-word; | |
| font-size: 0.9rem; | |
| } | |
| .floating-ai-message { | |
| display: flex; | |
| gap: 0.75rem; | |
| margin-bottom: 1rem; | |
| } | |
| .floating-ai-message .message-bubble { | |
| background: #f7fafc; | |
| color: #2d3748; | |
| padding: 0.75rem 1rem; | |
| border-radius: 15px 15px 15px 4px; | |
| max-width: 80%; | |
| word-wrap: break-word; | |
| border: 1px solid #e2e8f0; | |
| font-size: 0.9rem; | |
| } | |
| .floating-ai-message .agent-avatar { | |
| font-size: 1.2rem; | |
| flex-shrink: 0; | |
| } | |
| /* Enhancement Status Indicators */ | |
| .enhancement-status { | |
| margin-top: 0.5rem; | |
| padding: 0.25rem 0.5rem; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| font-size: 0.7rem; | |
| border-radius: 12px; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.3rem; | |
| opacity: 0.9; | |
| } | |
| .rag-indicator, .ticket-indicator { | |
| font-size: 0.8rem; | |
| } | |
| .response-time { | |
| margin-top: 0.3rem; | |
| color: #a0aec0; | |
| font-size: 0.6rem; | |
| text-align: right; | |
| } | |
| .floating-typing-indicator { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| color: #718096; | |
| font-size: 0.8rem; | |
| padding: 0 1rem 0.5rem 1rem; | |
| } | |
| .floating-chat-input-form { | |
| border-top: 1px solid #e2e8f0; | |
| } | |
| .floating-input-group { | |
| display: flex; | |
| align-items: end; | |
| gap: 0.5rem; | |
| padding: 1rem; | |
| background: rgba(255, 255, 255, 0.95); | |
| flex-wrap: wrap; | |
| } | |
| .floating-input-group textarea { | |
| flex: 1; | |
| background: transparent; | |
| border: 1px solid #e2e8f0; | |
| border-radius: 12px; | |
| padding: 0.75rem; | |
| color: #2d3748; | |
| font-size: 0.9rem; | |
| line-height: 1.4; | |
| resize: none; | |
| outline: none; | |
| min-height: 20px; | |
| max-height: 80px; | |
| font-family: 'Inter', sans-serif; | |
| } | |
| .floating-input-group textarea::placeholder { | |
| color: #a0aec0; | |
| } | |
| .floating-input-group textarea:focus { | |
| border-color: #667eea; | |
| box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); | |
| } | |
| .floating-send-button { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| border: none; | |
| border-radius: 50%; | |
| width: 36px; | |
| height: 36px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| flex-shrink: 0; | |
| } | |
| .floating-send-button:hover:not(:disabled) { | |
| transform: scale(1.05); | |
| box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4); | |
| } | |
| .floating-send-button:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| } | |
| .floating-send-button .send-icon { | |
| color: white; | |
| font-size: 1rem; | |
| transform: rotate(-45deg); | |
| } | |
| /* Floating Chat Responsive Design */ | |
| @media (max-width: 480px) { | |
| .floating-chat-widget { | |
| bottom: 15px; | |
| right: 15px; | |
| } | |
| .chat-window { | |
| width: calc(100vw - 30px); | |
| height: 70vh; | |
| bottom: 80px; | |
| right: -15px; | |
| } | |
| .chat-toggle-btn { | |
| width: 50px; | |
| height: 50px; | |
| } | |
| .chat-toggle-btn .chat-icon { | |
| font-size: 1.2rem; | |
| } | |
| .floating-user-message .message-bubble, | |
| .floating-ai-message .message-bubble { | |
| max-width: 90%; | |
| } | |
| .floating-input-group { | |
| padding: 0.75rem; | |
| gap: 0.4rem; | |
| } | |
| } | |
| </style> | |
| <!-- Global modal readability overrides --> | |
| <style> | |
| /* Make modals readable by default */ | |
| .modal-content { | |
| background: rgba(255,255,255,0.98) ; | |
| color: #1a202c ; | |
| } | |
| .modal-content * { | |
| color: #1a202c ; | |
| } | |
| .modal .close-modal { | |
| color: #1a202c ; | |
| } | |
| </style> | |
| {% block extra_head %}{% endblock %} | |
| </head> | |
| <body> | |
| <!-- Navigation Header --> | |
| <nav class="navbar"> | |
| <div class="nav-container"> | |
| <div class="nav-brand"> | |
| <a href="{{ url_for('homepage') }}" class="brand-logo"> | |
| <span class="logo-icon">🔌</span> | |
| <span class="brand-text">Too Many Cables</span> | |
| </a> | |
| </div> | |
| <div class="nav-menu" id="nav-menu"> | |
| <a href="{{ url_for('homepage') }}" class="nav-link {% if request.endpoint == 'homepage' %}active{% endif %}">Home</a> | |
| <a href="{{ url_for('products') }}" class="nav-link {% if request.endpoint == 'products' %}active{% endif %}">Products</a> | |
| <a href="{{ url_for('tickets') }}" class="nav-link {% if request.endpoint == 'tickets' %}active{% endif %}" id="tickets-link" style="display: none;"> | |
| My Tickets | |
| </a> | |
| <a href="#" id="nav-chat-trigger" class="nav-link chat-link"> | |
| <span class="chat-icon">💬</span> Live Chat | |
| </a> | |
| </div> | |
| <div class="nav-user"> | |
| <div id="user-menu" class="user-menu"> | |
| <span id="user-greeting" class="user-greeting" style="display: none;"> | |
| Hello, <span id="user-name"></span> | |
| </span> | |
| <button id="auth-btn" class="btn btn-outline">Login</button> | |
| </div> | |
| </div> | |
| <div class="nav-toggle" id="nav-toggle"> | |
| <span class="bar"></span> | |
| <span class="bar"></span> | |
| <span class="bar"></span> | |
| </div> | |
| </div> | |
| </nav> | |
| <!-- Main Content --> | |
| <main class="main-content"> | |
| {% block content %}{% endblock %} | |
| </main> | |
| <!-- Footer --> | |
| <footer class="footer"> | |
| <div class="footer-container"> | |
| <div class="footer-simple"> | |
| <h3>Too Many Cables</h3> | |
| <p>Your trusted source for premium cables and accessories since 2018.</p> | |
| </div> | |
| </div> | |
| <div class="footer-bottom"> | |
| <div class="footer-container"> | |
| <p>© 2025 Too Many Cables. All rights reserved.</p> | |
| </div> | |
| </div> | |
| </footer> | |
| <!-- Modal CSS --> | |
| <style> | |
| .modal { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(0, 0, 0, 0.5); | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| z-index: 10000; | |
| } | |
| .modal-content { | |
| background: white; | |
| border-radius: 10px; | |
| padding: 20px; | |
| max-width: 400px; | |
| width: 90%; | |
| max-height: 90vh; | |
| overflow-y: auto; | |
| } | |
| .modal-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 20px; | |
| border-bottom: 1px solid #eee; | |
| padding-bottom: 10px; | |
| } | |
| .close-modal { | |
| font-size: 24px; | |
| cursor: pointer; | |
| color: #999; | |
| } | |
| .close-modal:hover { | |
| color: #333; | |
| } | |
| .form-group { | |
| margin-bottom: 15px; | |
| } | |
| .form-group label { | |
| display: block; | |
| margin-bottom: 5px; | |
| font-weight: bold; | |
| } | |
| .form-group input { | |
| width: 100%; | |
| padding: 10px; | |
| border: 1px solid #ddd; | |
| border-radius: 5px; | |
| font-size: 14px; | |
| } | |
| .form-actions { | |
| display: flex; | |
| gap: 10px; | |
| justify-content: space-between; | |
| margin-top: 20px; | |
| } | |
| .btn { | |
| padding: 10px 20px; | |
| border: none; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| font-size: 14px; | |
| text-decoration: none; | |
| display: inline-block; | |
| text-align: center; | |
| } | |
| .btn-primary { | |
| background: #007bff; | |
| color: white; | |
| } | |
| .btn-primary:hover { | |
| background: #0056b3; | |
| } | |
| .btn-outline { | |
| background: transparent; | |
| color: #007bff; | |
| border: 1px solid #007bff; | |
| } | |
| .btn-outline:hover { | |
| background: #007bff; | |
| color: white; | |
| } | |
| .btn-link { | |
| background: transparent; | |
| color: #007bff; | |
| text-decoration: underline; | |
| } | |
| .btn-link:hover { | |
| color: #0056b3; | |
| } | |
| </style> | |
| <!-- Login Modal --> | |
| <div id="login-modal" class="modal" style="display: none;"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h3>Customer Login</h3> | |
| <span class="close-modal" id="close-login-modal">×</span> | |
| </div> | |
| <div class="modal-body"> | |
| <form id="login-form"> | |
| <div class="form-group"> | |
| <label for="login-email">Email:</label> | |
| <input type="email" id="login-email" name="email" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="login-password">Password:</label> | |
| <input type="password" id="login-password" name="password" required> | |
| </div> | |
| <div class="form-actions"> | |
| <button type="submit" class="btn btn-primary">Login</button> | |
| <button type="button" id="show-register" class="btn btn-link">Create Account</button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Ensure any modal we open uses readable colors — defensive inline styles | |
| function ensureModalReadability(modalEl) { | |
| if (!modalEl) return; | |
| const content = modalEl.querySelector('.modal-content'); | |
| if (content) { | |
| content.style.backgroundColor = 'rgba(255,255,255,0.98)'; | |
| content.style.color = '#1a202c'; | |
| // also ensure close buttons are visible | |
| const close = content.querySelector('.close-modal'); | |
| if (close) close.style.color = '#1a202c'; | |
| // force all text descendants to inherit the color | |
| const descendants = content.querySelectorAll('*'); | |
| descendants.forEach(el => el.style.color = '#1a202c'); | |
| } | |
| } | |
| // Hook into modal opens across the site | |
| document.addEventListener('click', function(e) { | |
| // If something opens a modal by toggling style.display = 'flex' or similar, detect the modal ancestor | |
| const modal = e.target.closest('.modal'); | |
| if (modal && (modal.style.display === 'flex' || getComputedStyle(modal).display === 'flex')) { | |
| ensureModalReadability(modal); | |
| } | |
| }); | |
| // Also observe DOM for modals being shown programmatically | |
| const observer = new MutationObserver((mutations) => { | |
| for (const m of mutations) { | |
| if (m.type === 'attributes' && m.target && m.target.classList && m.target.classList.contains('modal')) { | |
| if (getComputedStyle(m.target).display === 'flex') ensureModalReadability(m.target); | |
| } | |
| } | |
| }); | |
| // Start observing any modals present now | |
| document.querySelectorAll('.modal').forEach(modal => observer.observe(modal, { attributes: true })); | |
| </script> | |
| <!-- Register Modal --> | |
| <div id="register-modal" class="modal" style="display: none;"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h3>Create Account</h3> | |
| <span class="close-modal" id="close-register-modal">×</span> | |
| </div> | |
| <div class="modal-body"> | |
| <form id="register-form"> | |
| <div class="form-group"> | |
| <label for="register-email">Email:</label> | |
| <input type="email" id="register-email" name="email" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="register-first-name">First Name:</label> | |
| <input type="text" id="register-first-name" name="first_name" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="register-last-name">Last Name:</label> | |
| <input type="text" id="register-last-name" name="last_name" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="register-password">Password:</label> | |
| <input type="password" id="register-password" name="password" required> | |
| </div> | |
| <div class="form-group"> | |
| <label for="register-phone">Phone (optional):</label> | |
| <input type="tel" id="register-phone" name="phone"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="register-company">Company (optional):</label> | |
| <input type="text" id="register-company" name="company"> | |
| </div> | |
| <div class="form-actions"> | |
| <button type="submit" class="btn btn-primary">Create Account</button> | |
| <button type="button" id="show-login" class="btn btn-link">Already have account? Login</button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Floating Chat Widget --> | |
| <div id="floating-chat-widget" class="floating-chat-widget"> | |
| <!-- Chat Toggle Button --> | |
| <div id="chat-toggle-btn" class="chat-toggle-btn"> | |
| <span class="chat-icon">💬</span> | |
| <span class="notification-dot" id="notification-dot" style="display: none;"></span> | |
| </div> | |
| <!-- Chat Window --> | |
| <div id="chat-window" class="chat-window" style="display: none;"> | |
| <div class="chat-header"> | |
| <div class="chat-header-info"> | |
| <div class="agent-avatar-small">🤖</div> | |
| <div class="agent-info"> | |
| <h4>TMCBot</h4> | |
| <span class="status-text">Online</span> | |
| </div> | |
| </div> | |
| <div class="chat-controls"> | |
| <button id="minimize-chat" class="control-btn" title="Minimize">−</button> | |
| <button id="close-chat" class="control-btn" title="End Conversation & Close">×</button> | |
| </div> | |
| </div> | |
| <div id="floating-chat-messages" class="floating-chat-messages"> | |
| <div class="welcome-message-floating"> | |
| <div class="agent-avatar">🤖</div> | |
| <div class="message-content"> | |
| <h4>Hi! I'm TMCBot</h4> | |
| <p>I'm here to help you find the perfect cables and accessories. Ask me about products, compatibility, or any technical questions!</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="floating-typing-indicator" id="floating-typing-indicator" style="display: none;"> | |
| <div class="typing-dots"> | |
| <span></span> | |
| <span></span> | |
| <span></span> | |
| </div> | |
| <span>TMCBot is typing...</span> | |
| </div> | |
| <form id="floating-chat-form" class="floating-chat-input-form" onsubmit="event.preventDefault(); return false;" novalidate> | |
| <div class="floating-input-group"> | |
| <textarea | |
| id="floating-message-input" | |
| placeholder="Type your message here..." | |
| rows="1" | |
| maxlength="1000" | |
| ></textarea> | |
| <button type="submit" id="floating-send-button" class="floating-send-button" disabled> | |
| <span class="send-icon">➤</span> | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| <script src="{{ url_for('static', filename='script.js') }}"></script> | |
| <!-- Login Modal JavaScript --> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Authentication elements | |
| const authBtn = document.getElementById('auth-btn'); | |
| const loginModal = document.getElementById('login-modal'); | |
| const closeLoginModal = document.getElementById('close-login-modal'); | |
| const loginForm = document.getElementById('login-form'); | |
| const registerModal = document.getElementById('register-modal'); | |
| const closeRegisterModal = document.getElementById('close-register-modal'); | |
| const registerForm = document.getElementById('register-form'); | |
| const showRegisterBtn = document.getElementById('show-register'); | |
| const showLoginBtn = document.getElementById('show-login'); | |
| let currentUser = null; | |
| // Immediately set logged-out state on page load | |
| updateUserInterface(null); | |
| // Handle auth button click (login or logout) | |
| if (authBtn) { | |
| authBtn.addEventListener('click', function(e) { | |
| e.preventDefault(); | |
| if (currentUser) { | |
| // User is logged in, so logout | |
| handleLogout(); | |
| } else { | |
| // User is logged out, so show login modal | |
| if (loginModal) { | |
| loginModal.style.display = 'flex'; | |
| } | |
| } | |
| }); | |
| } | |
| // Close modals | |
| if (closeLoginModal) { | |
| closeLoginModal.addEventListener('click', function() { | |
| loginModal.style.display = 'none'; | |
| }); | |
| } | |
| if (closeRegisterModal) { | |
| closeRegisterModal.addEventListener('click', function() { | |
| registerModal.style.display = 'none'; | |
| }); | |
| } | |
| // Switch between login and register | |
| if (showRegisterBtn) { | |
| showRegisterBtn.addEventListener('click', function() { | |
| loginModal.style.display = 'none'; | |
| registerModal.style.display = 'flex'; | |
| }); | |
| } | |
| if (showLoginBtn) { | |
| showLoginBtn.addEventListener('click', function() { | |
| registerModal.style.display = 'none'; | |
| loginModal.style.display = 'flex'; | |
| }); | |
| } | |
| // Close modals when clicking outside | |
| window.addEventListener('click', function(event) { | |
| if (event.target === loginModal) { | |
| loginModal.style.display = 'none'; | |
| } | |
| if (event.target === registerModal) { | |
| registerModal.style.display = 'none'; | |
| } | |
| }); | |
| // Handle login form submission | |
| if (loginForm) { | |
| loginForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const email = document.getElementById('login-email').value; | |
| const password = document.getElementById('login-password').value; | |
| fetch('/api/login', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ email, password }) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| if (data.success) { | |
| // Login successful | |
| loginModal.style.display = 'none'; | |
| updateUserInterface(data.user); | |
| showNotification('Login successful!', 'success'); | |
| // Reload page to update all user-specific content | |
| setTimeout(() => { | |
| window.location.reload(); | |
| }, 1000); | |
| } else { | |
| showNotification('Login failed: ' + data.error, 'error'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Login error:', error); | |
| showNotification('Login failed. Please try again.', 'error'); | |
| }); | |
| }); | |
| } | |
| // Handle register form submission | |
| if (registerForm) { | |
| registerForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const formData = new FormData(registerForm); | |
| const registerData = Object.fromEntries(formData); | |
| fetch('/api/register', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(registerData) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| if (data.success) { | |
| registerModal.style.display = 'none'; | |
| showNotification('Registration successful! Please check your email to verify your account.', 'success'); | |
| // Switch to login modal | |
| setTimeout(() => { | |
| loginModal.style.display = 'flex'; | |
| }, 2000); | |
| } else { | |
| showNotification('Registration failed: ' + data.error, 'error'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Registration error:', error); | |
| showNotification('Registration failed. Please try again.', 'error'); | |
| }); | |
| }); | |
| } | |
| // Handle logout | |
| function handleLogout() { | |
| fetch('/api/logout', { | |
| method: 'POST' | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| if (data.success) { | |
| updateUserInterface(null); | |
| showNotification('Logged out successfully!', 'success'); | |
| // Reload page to clear user-specific content | |
| setTimeout(() => { | |
| window.location.reload(); | |
| }, 1000); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Logout error:', error); | |
| }); | |
| } | |
| // Update user interface based on login status | |
| function updateUserInterface(user) { | |
| const userGreeting = document.getElementById('user-greeting'); | |
| const userName = document.getElementById('user-name'); | |
| const ticketsLink = document.getElementById('tickets-link'); | |
| console.log('updateUserInterface called with user:', user); | |
| console.log('authBtn found:', !!authBtn); | |
| currentUser = user; | |
| if (user) { | |
| // User is logged in | |
| console.log('Setting logged in state'); | |
| if (authBtn) { | |
| authBtn.textContent = 'Logout'; | |
| authBtn.title = 'Click to logout'; | |
| } | |
| if (userGreeting) { | |
| userGreeting.style.display = 'inline-block'; | |
| if (userName) userName.textContent = user.name; | |
| } | |
| if (ticketsLink) ticketsLink.style.display = 'inline-block'; | |
| } else { | |
| // User is logged out | |
| console.log('Setting logged out state'); | |
| if (authBtn) { | |
| authBtn.textContent = 'Login'; | |
| authBtn.title = 'Click to login'; | |
| } | |
| if (userGreeting) userGreeting.style.display = 'none'; | |
| if (ticketsLink) ticketsLink.style.display = 'none'; | |
| } | |
| } | |
| // Show notification | |
| function showNotification(message, type) { | |
| // Create notification element | |
| const notification = document.createElement('div'); | |
| notification.className = `notification ${type}`; | |
| notification.innerHTML = ` | |
| <div class="notification-content"> | |
| <span>${message}</span> | |
| <button class="notification-close">×</button> | |
| </div> | |
| `; | |
| // Add styles if not already present | |
| if (!document.getElementById('notification-styles')) { | |
| const style = document.createElement('style'); | |
| style.id = 'notification-styles'; | |
| style.textContent = ` | |
| .notification { | |
| position: fixed; | |
| top: 20px; | |
| right: 20px; | |
| background: white; | |
| padding: 1rem 1.5rem; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 20px rgba(0,0,0,0.15); | |
| z-index: 10000; | |
| transition: all 0.3s ease; | |
| } | |
| .notification.success { | |
| border-left: 4px solid #10b981; | |
| } | |
| .notification.error { | |
| border-left: 4px solid #ef4444; | |
| } | |
| .notification-content { | |
| display: flex; | |
| align-items: center; | |
| gap: 1rem; | |
| } | |
| .notification-close { | |
| background: none; | |
| border: none; | |
| font-size: 1.2rem; | |
| cursor: pointer; | |
| opacity: 0.7; | |
| } | |
| .notification-close:hover { | |
| opacity: 1; | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| } | |
| document.body.appendChild(notification); | |
| // Close notification | |
| const closeBtn = notification.querySelector('.notification-close'); | |
| closeBtn.addEventListener('click', () => { | |
| notification.remove(); | |
| }); | |
| // Auto-close after 5 seconds | |
| setTimeout(() => { | |
| if (notification.parentNode) { | |
| notification.remove(); | |
| } | |
| }, 5000); | |
| } | |
| // Check current user status on page load | |
| fetch('/api/user') | |
| .then(response => response.json()) | |
| .then(data => { | |
| if (data.success && data.user) { | |
| updateUserInterface(data.user); | |
| } else { | |
| updateUserInterface(null); | |
| } | |
| }) | |
| .catch(error => { | |
| console.log('No user logged in'); | |
| updateUserInterface(null); | |
| }); | |
| }); | |
| </script> | |
| <!-- Floating Chat Widget JavaScript --> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const chatToggleBtn = document.getElementById('chat-toggle-btn'); | |
| const chatWindow = document.getElementById('chat-window'); | |
| const minimizeBtn = document.getElementById('minimize-chat'); | |
| const closeBtn = document.getElementById('close-chat'); | |
| const navChatTrigger = document.getElementById('nav-chat-trigger'); | |
| const floatingChatForm = document.getElementById('floating-chat-form'); | |
| const floatingMessageInput = document.getElementById('floating-message-input'); | |
| const floatingSendButton = document.getElementById('floating-send-button'); | |
| const floatingChatMessages = document.getElementById('floating-chat-messages'); | |
| const floatingTypingIndicator = document.getElementById('floating-typing-indicator'); | |
| let isChatOpen = localStorage.getItem('floating_chat_open') === 'true'; | |
| let messageHistory = JSON.parse(localStorage.getItem('floating_chat_history') || '[]'); | |
| // Initialize chat state on page load | |
| function initializeChatState() { | |
| if (isChatOpen) { | |
| chatWindow.style.display = 'flex'; | |
| floatingMessageInput.focus(); | |
| } else { | |
| chatWindow.style.display = 'none'; | |
| } | |
| // Restore chat history | |
| restoreChatHistory(); | |
| } | |
| // Toggle chat window | |
| function toggleChat() { | |
| if (isChatOpen) { | |
| chatWindow.style.display = 'none'; | |
| isChatOpen = false; | |
| localStorage.setItem('floating_chat_open', 'false'); | |
| } else { | |
| chatWindow.style.display = 'flex'; | |
| isChatOpen = true; | |
| localStorage.setItem('floating_chat_open', 'true'); | |
| floatingMessageInput.focus(); | |
| } | |
| } | |
| // Event listeners | |
| chatToggleBtn.addEventListener('click', toggleChat); | |
| navChatTrigger.addEventListener('click', function(e) { | |
| e.preventDefault(); | |
| if (!isChatOpen) { | |
| toggleChat(); | |
| } | |
| }); | |
| // Handle all chat trigger buttons | |
| document.addEventListener('click', function(e) { | |
| if (e.target.classList.contains('chat-trigger-btn') || e.target.closest('.chat-trigger-btn')) { | |
| e.preventDefault(); | |
| if (!isChatOpen) { | |
| toggleChat(); | |
| } | |
| } | |
| }); | |
| minimizeBtn.addEventListener('click', function() { | |
| chatWindow.style.display = 'none'; | |
| isChatOpen = false; | |
| localStorage.setItem('floating_chat_open', 'false'); | |
| }); | |
| closeBtn.addEventListener('click', function() { | |
| smartCloseChat(); | |
| }); | |
| // Smart close: End conversation and minimize | |
| async function smartCloseChat() { | |
| const conversationId = localStorage.getItem('floating_chat_conversation_id'); | |
| const hasMessages = messageHistory.length > 0; | |
| // Immediately close the chat for responsive UI | |
| chatWindow.style.display = 'none'; | |
| isChatOpen = false; | |
| localStorage.setItem('floating_chat_open', 'false'); | |
| // Clear chat history immediately | |
| clearChatHistory(); | |
| // End conversation in the background (non-blocking) | |
| if (conversationId && hasMessages) { | |
| // Don't await - let it run in background | |
| fetch('/api/conversation/end', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| conversation_id: conversationId | |
| }) | |
| }).then(response => { | |
| if (response.ok) { | |
| return response.json(); | |
| } | |
| }).then(result => { | |
| if (result) { | |
| console.log('Conversation ended:', result.message || 'Successfully ended conversation'); | |
| } | |
| }).catch(error => { | |
| console.error('Error ending conversation:', error); | |
| }); | |
| } | |
| } | |
| // Auto-resize textarea | |
| floatingMessageInput.addEventListener('input', function() { | |
| this.style.height = 'auto'; | |
| this.style.height = Math.min(this.scrollHeight, 80) + 'px'; | |
| // Enable/disable send button | |
| floatingSendButton.disabled = this.value.trim().length === 0; | |
| }); | |
| // Handle message sending | |
| floatingChatForm.addEventListener('submit', function(e) { | |
| console.log('=== FORM SUBMIT EVENT TRIGGERED ==='); | |
| e.preventDefault(); | |
| const message = floatingMessageInput.value.trim(); | |
| console.log('Message value:', message); | |
| if (!message) { | |
| console.log('Message empty, returning'); | |
| return; | |
| } | |
| // Add user message to chat | |
| addMessage(message, 'user'); | |
| floatingMessageInput.value = ''; | |
| floatingMessageInput.style.height = 'auto'; | |
| floatingSendButton.disabled = true; | |
| // Show typing indicator | |
| floatingTypingIndicator.style.display = 'flex'; | |
| // Log the request for debugging | |
| console.log('Sending message to /api/chat'); | |
| console.log('Current location:', window.location.href); | |
| console.log('Will fetch from:', window.location.origin + '/api/chat'); | |
| // Send message to backend | |
| const apiUrl = window.location.origin + '/api/chat'; | |
| console.log('Starting fetch to:', apiUrl); | |
| fetch(apiUrl, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| message: message, | |
| conversation_id: localStorage.getItem('floating_chat_conversation_id') | |
| }) | |
| }) | |
| .then(response => { | |
| console.log('Response received, status:', response.status); | |
| console.log('Response ok:', response.ok); | |
| // Parse JSON regardless of status | |
| return response.json().then(data => ({ data, status: response.status, ok: response.ok })); | |
| }) | |
| .then(({ data, status, ok }) => { | |
| console.log('JSON parsed, data:', data); | |
| // Hide typing indicator | |
| floatingTypingIndicator.style.display = 'none'; | |
| // Check if request was successful | |
| if (!ok) { | |
| const errorMsg = data.error || `Server error: ${status}`; | |
| addMessage('Error: ' + errorMsg, 'ai'); | |
| return; | |
| } | |
| // Store conversation ID for future messages | |
| if (data.conversation_id) { | |
| localStorage.setItem('floating_chat_conversation_id', data.conversation_id); | |
| } | |
| // Add AI response to chat with RAG and tickets info | |
| if (data.success && data.response) { | |
| addMessage(data.response, 'ai', false, { | |
| rag_used: data.rag_used, | |
| rag_context_length: data.rag_context_length, | |
| tickets_used: data.tickets_used, | |
| tickets_count: data.tickets_count, | |
| response_time_ms: data.response_time_ms | |
| }); | |
| } else if (data.error) { | |
| addMessage('Sorry, there was an error: ' + data.error, 'ai'); | |
| } else { | |
| // Handle case where success is false but no error message | |
| addMessage('Sorry, an unexpected error occurred. Please try again.', 'ai'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error:', error); | |
| console.error('Error type:', error.name); | |
| console.error('Error message:', error.message); | |
| console.error('Full error:', JSON.stringify(error, Object.getOwnPropertyNames(error))); | |
| floatingTypingIndicator.style.display = 'none'; | |
| // Display the actual error message | |
| const errorMsg = error.message || 'Sorry, there was an error processing your message. Please try again.'; | |
| addMessage('Error: ' + errorMsg, 'ai'); | |
| }); | |
| }); | |
| // Add message to chat | |
| function addMessage(message, sender, skipHistory = false, metadata = {}) { | |
| const messageElement = document.createElement('div'); | |
| if (sender === 'user') { | |
| messageElement.className = 'floating-user-message'; | |
| messageElement.innerHTML = ` | |
| <div class="message-bubble">${escapeHtml(message)}</div> | |
| `; | |
| } else { | |
| messageElement.className = 'floating-ai-message'; | |
| // Build enhancement status indicators | |
| let enhancementStatus = ''; | |
| const statusItems = []; | |
| if (metadata.rag_used) { | |
| statusItems.push(`<span class="rag-indicator">📚</span> Knowledge Enhanced`); | |
| } | |
| if (metadata.tickets_used) { | |
| statusItems.push(`<span class="ticket-indicator">🎫</span> Tickets Accessed`); | |
| } | |
| if (statusItems.length > 0) { | |
| const title = []; | |
| if (metadata.rag_used) title.push(`Knowledge enhanced (${metadata.rag_context_length} chars)`); | |
| if (metadata.tickets_used) title.push(`Tickets accessed (${metadata.tickets_count} tickets)`); | |
| enhancementStatus = `<div class="enhancement-status" title="${title.join(', ')}"> | |
| ${statusItems.join(' • ')} | |
| </div>`; | |
| } | |
| // Build response time info | |
| let responseTime = ''; | |
| if (metadata.response_time_ms) { | |
| responseTime = `<div class="response-time">${metadata.response_time_ms}ms</div>`; | |
| } | |
| messageElement.innerHTML = ` | |
| <div class="agent-avatar">🤖</div> | |
| <div class="message-bubble"> | |
| ${escapeHtml(message)} | |
| ${enhancementStatus} | |
| ${responseTime} | |
| </div> | |
| `; | |
| } | |
| floatingChatMessages.appendChild(messageElement); | |
| floatingChatMessages.scrollTop = floatingChatMessages.scrollHeight; | |
| // Store in message history and localStorage (unless restoring from history) | |
| if (!skipHistory) { | |
| const messageData = { message, sender, timestamp: new Date().toISOString() }; | |
| messageHistory.push(messageData); | |
| localStorage.setItem('floating_chat_history', JSON.stringify(messageHistory)); | |
| } | |
| } | |
| // Restore chat history from localStorage | |
| function restoreChatHistory() { | |
| // Clear current messages except welcome message | |
| const welcomeMessage = floatingChatMessages.querySelector('.welcome-message-floating'); | |
| floatingChatMessages.innerHTML = ''; | |
| if (welcomeMessage) { | |
| floatingChatMessages.appendChild(welcomeMessage); | |
| } | |
| // Add stored messages | |
| messageHistory.forEach(msgData => { | |
| addMessage(msgData.message, msgData.sender, true); | |
| }); | |
| } | |
| // Clear chat history | |
| function clearChatHistory() { | |
| messageHistory = []; | |
| localStorage.removeItem('floating_chat_history'); | |
| localStorage.removeItem('floating_chat_conversation_id'); | |
| // Reset UI to welcome message only | |
| const welcomeMessage = floatingChatMessages.querySelector('.welcome-message-floating'); | |
| floatingChatMessages.innerHTML = ''; | |
| if (welcomeMessage) { | |
| floatingChatMessages.appendChild(welcomeMessage); | |
| } | |
| } | |
| // Escape HTML function | |
| function escapeHtml(text) { | |
| const div = document.createElement('div'); | |
| div.textContent = text; | |
| return div.innerHTML; | |
| } | |
| // Enter key to send message (with Shift+Enter for new line) | |
| floatingMessageInput.addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| floatingChatForm.dispatchEvent(new Event('submit')); | |
| } | |
| }); | |
| // Initialize chat state when page loads | |
| initializeChatState(); | |
| // Close chat when clicking outside (optional) | |
| document.addEventListener('click', function(e) { | |
| if (isChatOpen && !document.getElementById('floating-chat-widget').contains(e.target)) { | |
| // Uncomment the next line if you want clicking outside to close the chat | |
| // toggleChat(); | |
| } | |
| }); | |
| }); | |
| </script> | |
| {% block extra_scripts %}{% endblock %} | |
| </body> | |
| </html> | |