diff --git a/static/css/main.css b/static/css/main.css index 1a4e1ad..7674c82 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -71,52 +71,51 @@ .hero-img { width: 100%; height: 100%; object-fit: cover; display: block; } - /* --- Marquee --- */ - .partners-section { - padding: 2.5rem 0; - border-top: 1px solid var(--border); - border-bottom: 1px solid var(--border); - overflow: hidden; - - } - - .marquee-track { - display: flex; - gap: 3rem; /* Reduced from 6rem to 3rem for tighter look with fewer items */ - width: max-content; - /* ADJUSTMENT 1: Added padding to ensure track has width even with few items */ - padding: 0 1rem; - /* ADJUSTMENT 2: Increased animation duration from 40s to 80s for smoother speed */ - animation: marquee 120s linear infinite; - } - - .partner-logo { - height: 40px; - width: auto; - object-fit: contain; - filter: grayscale(100%); - opacity: 0.5; - } - - /* Keep existing text style for names without logos */ - .partner-item { - font-weight: 700; - color: #d4d4d8; - font-size: 1.1rem; - /* ADJUSTMENT 3: Ensure text doesn't break or wrap */ - white-space: nowrap; - /* Flexbox to align text vertically with logos if they appear mixed */ - display: flex; - align-items: center; - /* Give text elements some space if mixed with logos */ - padding: 0 0.5rem; - } + /* --- Marquee --- */ +.partners-section { + padding: 2.5rem 0; + border-top: 1px solid var(--border); + border-bottom: 1px solid var(--border); + overflow: hidden; + /* Prevents selection while dragging over logos */ + user-select: none; +} - @keyframes marquee { - from { transform: translateX(0); } - to { transform: translateX(-50%); } - } +.marquee-track { + display: flex; + gap: 3rem; + width: max-content; + padding: 0 1rem; + /* Removed 120s here - will be handled by JS for consistent speed */ + animation: marquee linear infinite; + /* Ensure smooth rendering */ + will-change: transform; +} +.partner-logo { + height: 40px; + width: auto; + object-fit: contain; + filter: grayscale(100%); + opacity: 0.5; + transition: opacity 0.3s; +} +.partner-logo:hover { opacity: 1; filter: grayscale(0%); } + +.partner-item { + font-weight: 700; + color: #d4d4d8; + font-size: 1.1rem; + white-space: nowrap; + display: flex; + align-items: center; + padding: 0 0.5rem; +} + +@keyframes marquee { + from { transform: translateX(0); } + to { transform: translateX(-50%); } +} /* Cards & Badge */ diff --git a/static/js/main.js b/static/js/main.js index 7ceba91..eafc079 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -151,32 +151,64 @@ }); }); - // --- Marquee Width Fix --- - // Ensures the partner list is always wide enough to scroll smoothly + // --- Marquee Width & Speed Fix --- + + // Helper: Debounce function prevents the code from running too often + // (e.g. while scrolling on mobile which triggers resize events) + function debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + } + function adjustMarqueeWidth() { const track = document.getElementById('marquee-track'); if (!track) return; - // Get the rendered width of the single list + // 1. Get the rendered width of the single list of partners const currentWidth = track.scrollWidth; - // Target width: We want the track to be at least 200% of screen width - // so it has enough room to scroll off-screen before resetting. - const targetWidth = window.innerWidth * 2.0; - // Calculate how many times we need to repeat the list to reach target width + // 2. Calculate how many times we need to repeat the list + // Target: We want the track to be at least 3x the screen width + // so it scrolls smoothly without running out of items before looping. + const targetWidth = window.innerWidth * 3; + const repeats = Math.max(2, Math.ceil(targetWidth / currentWidth)); - // Get the raw HTML content (the first run of the loop) + // 3. Store the original HTML (the first run of the loop) const originalContent = track.innerHTML; - // Rebuild the track with the repeated content + // 4. Rebuild the track with the repeated content track.innerHTML = ''; for (let i = 0; i < repeats; i++) { track.innerHTML += originalContent; } + + // --- NEW: Calculate Constant Speed --- + // The animation moves -50% of the track width. + const totalWidth = track.scrollWidth; + const distanceToMove = totalWidth / 2; + + // Desired speed in pixels per second (Adjust this number to make it faster/slower) + // 50 = Relaxed/Easy reading speed + // 100 = Faster + const pixelsPerSecond = 50; + + // Duration = Distance / Speed + const durationInSeconds = distanceToMove / pixelsPerSecond; + + // Apply the calculated duration + track.style.animationDuration = `${durationInSeconds}s`; } - // Run on load and resize + // Run on load window.addEventListener('load', adjustMarqueeWidth); - window.addEventListener('resize', adjustMarqueeWidth); - \ No newline at end of file + + // Run on resize (Debounced by 200ms to prevent flickering during scroll) + window.addEventListener('resize', debounce(adjustMarqueeWidth, 200)); \ No newline at end of file diff --git a/tenhal/settings.py b/tenhal/settings.py index 13cfd2e..c5d773f 100644 --- a/tenhal/settings.py +++ b/tenhal/settings.py @@ -196,8 +196,15 @@ GEOIP_PATH = BASE_DIR / 'geoip' -# 3. Security Headers (Required for 10k visitors) -SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') -SECURE_SSL_REDIRECT = True -SESSION_COOKIE_SECURE = True -CSRF_COOKIE_SECURE = True \ No newline at end of file +if not DEBUG: + SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') + SECURE_SSL_REDIRECT = True + SESSION_COOKIE_SECURE = True + CSRF_COOKIE_SECURE = True +else: + SECURE_SSL_REDIRECT = False + SESSION_COOKIE_SECURE = False + CSRF_COOKIE_SECURE = False + + +CSRF_TRUSTED_ORIGINS = [f"https://{host}" for host in ALLOWED_HOSTS if "ngrok-free.app" in host] \ No newline at end of file