Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Wealth Engine | Institutional Login</title> | |
| <link rel="stylesheet" href="/static/style.css?v=6"> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.net.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script> | |
| <style> | |
| /* Fallback to prevent white screens on laptop WebGL/CSS failure */ | |
| body { background-color: #0f172a ; color: #f8fafc; } | |
| .glass-panel { transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); } | |
| .glass-panel:hover { transform: translateY(-3px) scale(1.01); } | |
| </style> | |
| <style> | |
| /* Mobile fixes for login */ | |
| @media (max-width: 600px) { | |
| #loginContainer { | |
| padding: 1rem ; | |
| } | |
| .glass-panel { | |
| padding: 1.5rem ; | |
| } | |
| h2 { | |
| font-size: 1.5rem ; | |
| } | |
| } | |
| </style> | |
| <script> | |
| // Safe storage wrappers to prevent cross-origin iframe crashes | |
| window.safeLocalGet = function(k) { try { return localStorage.getItem(k); } catch(e) { return window['__ls_'+k] || null; } }; | |
| window.safeLocalSet = function(k, v) { try { localStorage.setItem(k, v); } catch(e) { window['__ls_'+k] = v; } }; | |
| window.safeLocalRem = function(k) { try { localStorage.removeItem(k); } catch(e) { delete window['__ls_'+k]; } }; | |
| window.safeSessionGet = function(k) { try { return sessionStorage.getItem(k); } catch(e) { return window['__ss_'+k] || null; } }; | |
| window.safeSessionSet = function(k, v) { try { sessionStorage.setItem(k, v); } catch(e) { window['__ss_'+k] = v; } }; | |
| window.safeSessionRem = function(k) { try { sessionStorage.removeItem(k); } catch(e) { delete window['__ss_'+k]; } }; | |
| </script> | |
| </head> | |
| <body id="vanta-bg" style="display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; overflow-x: hidden; overflow-y: auto; background: #0f172a; padding: 1rem; box-sizing: border-box;"> | |
| <div class="noise-overlay" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: -1;"></div> | |
| <div id="loginContainer" style="width: 100%; max-width: 600px; padding: 2rem; z-index: 10;"> | |
| <!-- Step 1: Disclaimer --> | |
| <div id="stepDisclaimer" class="glass-panel" style="padding: 3rem; text-align: left;"> | |
| <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2" style="margin-bottom: 1rem;"> | |
| <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path> | |
| <line x1="12" y1="9" x2="12" y2="13"></line> | |
| <line x1="12" y1="17" x2="12.01" y2="17"></line> | |
| </svg> | |
| <h2 style="margin-bottom: 1rem; color: #fff;">Important Legal Disclaimer</h2> | |
| <div style="color: var(--text-muted); font-size: 0.9rem; line-height: 1.6; margin-bottom: 2rem;"> | |
| <p style="margin-bottom: 10px;">By accessing this platform, you acknowledge and agree to the following:</p> | |
| <ul style="margin-bottom: 10px; padding-left: 20px;"> | |
| <li style="margin-bottom: 8px;"><strong>Not Financial Advice:</strong> This platform is an experimental mathematical research tool, not a registered financial advisor.</li> | |
| <li style="margin-bottom: 8px;"><strong>Historical Illusion:</strong> All optimizations and models are based on historical backtesting. Past performance carries absolutely no guarantee of future results.</li> | |
| <li style="margin-bottom: 8px;"><strong>AI Limitations & Usage:</strong> AI-generated insights, models, and narratives are provided "as is" and may contain inaccuracies or hallucinations. You must independently verify all AI outputs and agree not to misuse the AI services for malicious purposes.</li> | |
| <li><strong>Your Responsibility:</strong> You are solely responsible for your own investment decisions, capital losses, and risk management.</li> | |
| </ul> | |
| </div> | |
| <button class="btn-primary" style="width: 100%; justify-content: center; background: #f59e0b; color: #000; font-weight: 600; border: none; padding: 12px; border-radius: 8px; cursor: pointer;" onclick="acceptDisclaimer()"> | |
| I Understand and Agree | |
| </button> | |
| </div> | |
| <!-- Step 2: Authentication --> | |
| <div id="stepAuth" class="glass-panel" style="padding: 3rem; text-align: left; display: none;"> | |
| <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-bottom: 1rem; color: #60a5fa;"> | |
| <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect> | |
| <path d="M7 11V7a5 5 0 0 1 10 0v4"></path> | |
| </svg> | |
| <h2 style="margin-bottom: 0.5rem; color: #fff;">Restricted Access</h2> | |
| <p class="text-muted" style="margin-bottom: 1.5rem;">Please enter your institutional access key.</p> | |
| <form id="authForm" style="display: flex; flex-direction: column; gap: 1rem;"> | |
| <input type="text" id="username" placeholder="Username" style="width: 100%; text-align: center; letter-spacing: 1px;"> | |
| <input type="password" id="accessKey" placeholder="Access Key" style="width: 100%; text-align: center; letter-spacing: 2px;" required> | |
| <button type="submit" id="authBtn" class="btn-primary" style="justify-content: center; padding: 12px; position: relative;"> | |
| <span id="authBtnText">Authenticate</span> | |
| <span id="authBtnLoading" class="spinner-anim" style="display: none; position: absolute;">⠋</span> | |
| </button> | |
| <div id="authError" style="color: #ef4444; font-size: 0.85rem; text-align: center; display: none;">Invalid Key</div> | |
| </form> | |
| <div style="margin-top: 1.5rem; text-align: center; padding-top: 1.5rem; border-top: 1px solid rgba(255,255,255,0.1);"> | |
| <p class="text-muted" style="font-size: 0.85rem; margin-bottom: 0.8rem;">Don't have an access key?</p> | |
| <form id="requestAccessForm" style="display: flex; gap: 0.5rem; justify-content: center;"> | |
| <input type="email" name="email" id="requestEmail" placeholder="Your Email Address" required style="padding: 10px; border-radius: 6px; background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.1); color: #fff; width: 65%; outline: none; font-size: 0.9rem;"> | |
| <button type="submit" id="requestBtn" class="btn-secondary" style="padding: 10px; flex: 1; justify-content: center; font-size: 0.85rem;">Request Access</button> | |
| </form> | |
| <div id="requestStatus" style="font-size: 0.85rem; margin-top: 0.8rem; display: none;"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.getElementById('requestAccessForm').addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const btn = document.getElementById('requestBtn'); | |
| const statusDiv = document.getElementById('requestStatus'); | |
| btn.textContent = 'Sending...'; | |
| btn.disabled = true; | |
| fetch('https://formsubmit.co/ajax/mkoustis05@gmail.com', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, | |
| body: JSON.stringify({ | |
| _subject: "New Access Request for Wealth Engine!", | |
| _autoresponse: "We have received your access request for the Wealth Engine platform. We will review your email and get back to you shortly.", | |
| email: document.getElementById('requestEmail').value | |
| }) | |
| }) | |
| .then(r => r.json()) | |
| .then(data => { | |
| statusDiv.style.display = 'block'; | |
| statusDiv.style.color = '#10b981'; | |
| statusDiv.textContent = 'Sent! Check your email.'; | |
| btn.textContent = 'Requested'; | |
| }) | |
| .catch(err => { | |
| statusDiv.style.display = 'block'; | |
| statusDiv.style.color = '#ef4444'; | |
| statusDiv.textContent = 'Error sending request. Please try again.'; | |
| btn.textContent = 'Request Access'; | |
| btn.disabled = false; | |
| }); | |
| }); | |
| // Mouse Glow Tracking | |
| document.addEventListener("mousemove", (e) => { | |
| document.querySelectorAll(".glass-panel").forEach((el) => { | |
| const rect = el.getBoundingClientRect(); | |
| el.style.setProperty("--mouse-x", `${e.clientX - rect.left}px`); | |
| el.style.setProperty("--mouse-y", `${e.clientY - rect.top}px`); | |
| el.classList.add("mouse-glow"); | |
| }); | |
| }); | |
| // GSAP Initial Reveal | |
| gsap.fromTo("#stepDisclaimer", | |
| { opacity: 0, y: 40 }, | |
| { opacity: 1, y: 0, duration: 1.2, ease: "power3.out", delay: 0.2 } | |
| ); | |
| function acceptDisclaimer() { | |
| const disc = document.getElementById('stepDisclaimer'); | |
| const auth = document.getElementById('stepAuth'); | |
| gsap.to(disc, { | |
| opacity: 0, scale: 0.95, duration: 0.4, ease: "power2.inOut", | |
| onComplete: () => { | |
| disc.style.display = 'none'; | |
| auth.style.display = 'block'; | |
| gsap.fromTo(auth, | |
| { opacity: 0, y: 40 }, | |
| { opacity: 1, y: 0, duration: 0.8, ease: "power3.out", | |
| onComplete: () => document.getElementById('accessKey').focus() | |
| } | |
| ); | |
| } | |
| }); | |
| } | |
| document.getElementById('authForm').addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const inputUsername = document.getElementById('username').value; | |
| const inputKey = document.getElementById('accessKey').value; | |
| const errDiv = document.getElementById('authError'); | |
| const authBtn = document.getElementById('authBtn'); | |
| const authBtnText = document.getElementById('authBtnText'); | |
| const authBtnLoading = document.getElementById('authBtnLoading'); | |
| authBtn.disabled = true; | |
| authBtnText.style.opacity = '0'; | |
| authBtnLoading.style.display = 'inline-block'; | |
| errDiv.style.display = 'none'; | |
| try { | |
| const res = await fetch('/api/auth', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ key: inputKey, username: inputUsername }) | |
| }); | |
| const data = await res.json().catch(() => ({})); | |
| if (res.ok) { | |
| window.safeSessionSet('accessKey', inputKey); | |
| window.safeSessionSet('username', inputUsername); | |
| if (data.is_master) { | |
| window.safeSessionSet('isMaster', 'true'); | |
| } else { | |
| window.safeSessionRem('isMaster'); | |
| } | |
| window.safeSessionSet('authNavigating', 'true'); | |
| window.location.href = '/main'; | |
| } else { | |
| errDiv.style.display = 'block'; | |
| errDiv.textContent = data.detail || "Access Denied. Invalid or Expired Key."; | |
| document.getElementById('accessKey').value = ''; | |
| authBtn.disabled = false; | |
| authBtnText.style.opacity = '1'; | |
| authBtnLoading.style.display = 'none'; | |
| } | |
| } catch(err) { | |
| errDiv.style.display = 'block'; | |
| errDiv.textContent = "Authentication server unreachable."; | |
| authBtn.disabled = false; | |
| authBtnText.style.opacity = '1'; | |
| authBtnLoading.style.display = 'none'; | |
| } | |
| }); | |
| // Initialize Vanta 3D Background | |
| function initAmbientBackground() { | |
| if (window.VANTA) { | |
| window.VANTA.NET({ | |
| el: "#vanta-bg", | |
| mouseControls: true, | |
| touchControls: true, | |
| gyroControls: false, | |
| minHeight: 200.00, | |
| minWidth: 200.00, | |
| scale: 1.00, | |
| scaleMobile: 1.00, | |
| color: 0x3b82f6, | |
| backgroundColor: 0x0f172a, | |
| points: 12.00, | |
| maxDistance: 22.00, | |
| spacing: 16.00, | |
| showDots: true | |
| }); | |
| } | |
| } | |
| initAmbientBackground(); | |
| </script> | |
| <footer style="text-align: center; padding: 2rem 0; margin-top: 3rem; border-top: 1px solid rgba(255,255,255,0.05); color: var(--text-muted); font-size: 0.9rem;"> | |
| © 2026 All Rights Reserved. Wealth Engine. | |
| </footer> | |
| </body> | |
| </html> | |