PageSourceSearch

https://luxmeterpro.com/login

html luxmeterpro.com collected 2026-10-02 16:02:34 UTC 25,426 bytes, 782 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4    <meta charset="UTF-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6    <title>Login | Lux Meter Pro - Professional Light Measurement App</title>
7    <meta name="description" content="Sign in to your Lux Meter Pro account to access premium features, sync your measurements, and manage your professional lighting assessments.">
8    <meta name="keywords" content="lux meter pro login, sign in, professional lighting app, light measurement account">
9    <meta name="robots" content="noindex, nofollow">
10    <link rel="icon" type="image/x-icon" href="favicon.ico">
11    <link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
12    <link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
13    <link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
14    <link rel="manifest" href="site.webmanifest">
15    
16    <style>
17        * {
18            margin: 0;
19            padding: 0;
20            box-sizing: border-box;
21        }
22        
23        body {
24            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
25            line-height: 1.6;
26            color: #333;
27            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
28            min-height: 100vh;
29            display: flex;
30            align-items: center;
31            justify-content: center;
32            padding: 20px;
33        }
34        
35        .login-container {
36            background: white;
37            border-radius: 20px;
38            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
39            width: 100%;
40            max-width: 400px;
41            padding: 3rem 2rem;
42            position: relative;
43            overflow: hidden;
44        }
45        
46        .login-container::before {
47            content: '';
48            position: absolute;
49            top: 0;
50            left: 0;
51            right: 0;
52            height: 4px;
53            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
54        }
55        
56        .logo {
57            text-align: center;
58            margin-bottom: 2rem;
59        }
60        
61        .logo h1 {
62            font-size: 2rem;
63            color: #2c3e50;
64            margin-bottom: 0.5rem;
65            font-weight: 700;
66        }
67        
68        .logo-icon {
69            font-size: 3rem;
70            margin-bottom: 1rem;
71        }
72        
73        .logo p {
74            color: #6c757d;
75            font-size: 0.9rem;
76        }
77        
78        .login-header {
79            text-align: center;
80            margin-bottom: 2rem;
81        }
82        
83        .login-header h2 {
84            font-size: 1.8rem;
85            color: #2c3e50;
86            margin-bottom: 0.5rem;
87            font-weight: 600;
88        }
89        
90        .login-header p {
91            color: #6c757d;
92            font-size: 0.95rem;
93        }
94        
95        .form-group {
96            margin-bottom: 1.5rem;
97        }
98        
99        .form-group label {
100            display: block;
101            margin-bottom: 0.5rem;
102            font-weight: 600;
103            color: #2c3e50;
104            font-size: 0.9rem;
105        }
106        
107        .form-group input {
108            width: 100%;
109            padding: 1rem;
110            border: 2px solid #e9ecef;
111            border-radius: 10px;
112            font-size: 1rem;
113            transition: all 0.3s ease;
114            font-family: inherit;
115        }
116        
117        .form-group input:focus {
118            outline: none;
119            border-color: #667eea;
120            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
121        }
122        
123        .form-group input.error {
124            border-color: #dc3545;
125            box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.1);
126        }
127        
128        .form-group input.success {
129            border-color: #28a745;
130            box-shadow: 0 0 0 3px rgba(40, 167, 69, 0.1);
131        }
132        
133        .password-toggle {
134            position: relative;
135        }
136        
137        .password-toggle-btn {
138            position: absolute;
139            right: 12px;
140            top: 50%;
141            transform: translateY(-50%);
142            background: none;
143            border: none;
144            color: #6c757d;
145            cursor: pointer;
146            font-size: 1.1rem;
147            padding: 0.5rem;
148            border-radius: 4px;
149            transition: all 0.3s ease;
150        }
151        
152        .password-toggle-btn:hover {
153            color: #2c3e50;
154            background: rgba(0,0,0,0.05);
155        }
156        
157        .remember-forgot {
158            display: flex;
159            justify-content: space-between;
160            align-items: center;
161            margin-bottom: 2rem;
162            font-size: 0.9rem;
163        }
164        
165        .remember-me {
166            display: flex;
167            align-items: center;
168            gap: 0.5rem;
169        }
170        
171        .remember-me input[type="checkbox"] {
172            width: auto;
173        }
174        
175        .forgot-password {
176            color: #667eea;
177            text-decoration: none;
178            transition: color 0.3s ease;
179        }
180        
181        .forgot-password:hover {
182            color: #5a6fd8;
183            text-decoration: underline;
184        }
185        
186        .login-btn {
187            width: 100%;
188            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
189            color: white;
190            padding: 1rem;
191            border: none;
192            border-radius: 10px;
193            font-size: 1.1rem;
194            font-weight: 600;
195            cursor: pointer;
196            transition: all 0.3s ease;
197            margin-bottom: 1.5rem;
198            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
199        }
200        
201        .login-btn:hover:not(:disabled) {
202            transform: translateY(-2px);
203            box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
204        }
205        
206        .login-btn:disabled {
207            opacity: 0.7;
208            cursor: not-allowed;
209            transform: none !important;
210        }
211        
212        .divider {
213            text-align: center;
214            margin: 2rem 0;
215            position: relative;
216            color: #6c757d;
217            font-size: 0.9rem;
218        }
219        
220        .divider::before {
221            content: '';
222            position: absolute;
223            top: 50%;
224            left: 0;
225            right: 0;
226            height: 1px;
227            background: #e9ecef;
228            z-index: 1;
229        }
230        
231        .divider span {
232            background: white;
233            padding: 0 1rem;
234            position: relative;
235            z-index: 2;
236        }
237        
238        .social-login {
239            display: flex;
240            flex-direction: column;
241            gap: 1rem;
242            margin-bottom: 2rem;
243        }
244        
245        .social-btn {
246            display: flex;
247            align-items: center;
248            justify-content: center;
249            gap: 0.5rem;
250            padding: 1rem;
251            border: 2px solid #e9ecef;
252            border-radius: 10px;
253            background: white;
254            color: #2c3e50;
255            text-decoration: none;
256            font-weight: 600;
257            transition: all 0.3s ease;
258            font-size: 0.95rem;
259        }
260        
261        .social-btn:hover {
262            border-color: #667eea;
263            background: #f8f9fa;
264            transform: translateY(-1px);
265        }
266        
267        .google-btn {
268            border-color: #db4437;
269            color: #db4437;
270        }
271        
272        .google-btn:hover {
273            background: rgba(219, 68, 55, 0.1);
274            border-color: #db4437;
275        }
276        
277        .apple-btn {
278            border-color: #000;
279            color: #000;
280        }
281        
282        .apple-btn:hover {
283            background: rgba(0, 0, 0, 0.05);
284            border-color: #000;
285        }
286        
287        .signup-link {
288            text-align: center;
289            color: #6c757d;
290            font-size: 0.9rem;
291        }
292        
293        .signup-link a {
294            color: #667eea;
295            text-decoration: none;
296            font-weight: 600;
297        }
298        
299        .signup-link a:hover {
300            text-decoration: underline;
301        }
302        
303        .error-message {
304            background: #f8d7da;
305            color: #721c24;
306            padding: 0.75rem;
307            border-radius: 8px;
308            margin-bottom: 1rem;
309            font-size: 0.9rem;
310            border: 1px solid #f5c6cb;
311            display: none;
312        }
313        
314        .success-message {
315            background: #d4edda;
316            color: #155724;
317            padding: 0.75rem;
318            border-radius: 8px;
319            margin-bottom: 1rem;
320            font-size: 0.9rem;
321            border: 1px solid #c3e6cb;
322            display: none;
323        }
324        
325        .loading-spinner {
326            display: none;
327            width: 20px;
328            height: 20px;
329            border: 2px solid #ffffff;
330            border-top: 2px solid transparent;
331            border-radius: 50%;
332            animation: spin 1s linear infinite;
333            margin-right: 0.5rem;
334        }
335        
336        @keyframes spin {
337            0% { transform: rotate(0deg); }
338            100% { transform: rotate(360deg); }
339        }
340        
341        .back-to-home {
342            position: absolute;
343            top: 20px;
344            left: 20px;
345            background: rgba(255,255,255,0.9);
346            color: #2c3e50;
347            padding: 0.5rem 1rem;
348            border-radius: 25px;
349            text-decoration: none;
350            font-weight: 500;
351            transition: all 0.3s ease;
352            backdrop-filter: blur(10px);
353            box-shadow: 0 4px 15px rgba(0,0,0,0.1);
354        }
355        
356        .back-to-home:hover {
357            background: white;
358            transform: translateY(-2px);
359            box-shadow: 0 8px 25px rgba(0,0,0,0.15);
360        }
361        
362        /* Responsive Design */
363        @media (max-width: 768px) {
364            .login-container {
365                padding: 2rem 1.5rem;
366                margin: 1rem;
367            }
368            
369            .logo h1 {
370                font-size: 1.8rem;
371            }
372            
373            .login-header h2 {
374                font-size: 1.6rem;
375            }
376            
377            .remember-forgot {
378                flex-direction: column;
379                gap: 1rem;
380                align-items: flex-start;
381            }
382        }
383        
384        @media (max-width: 414px) {
385            body {
386                padding: 10px;
387            }
388            
389            .login-container {
390                padding: 1.5rem 1rem;
391            }
392            
393            .back-to-home {
394                top: 10px;
395                left: 10px;
396                padding: 0.4rem 0.8rem;
397                font-size: 0.9rem;
398            }
399        }
400    </style>
401</head>
402<body>
403    <!-- Back to Home Link -->
404    <a href="index.html" class="back-to-home">← Back to Home</a>
405
406    <div class="login-container">
407        <!-- Logo Section -->
408        <div class="logo">
409            <div class="logo-icon">🔆</div>
410            <h1>Lux Meter Pro</h1>
411            <p>Professional Light Measurement</p>
412        </div>
413
414        <!-- Login Header -->
415        <div class="login-header">
416            <h2>Welcome Back</h2>
417            <p>Sign in to access your lighting measurements and reports</p>
418        </div>
419
420        <!-- Error/Success Messages -->
421        <div id="errorMessage" class="error-message"></div>
422        <div id="successMessage" class="success-message"></div>
423
424        <!-- Login Form -->
425        <form id="loginForm">
426            <div class="form-group">
427                <label for="email">Email Address</label>
428                <input type="email" id="email" name="email" placeholder="[email protected]" required>
429            </div>
430
431            <div class="form-group">
432                <label for="password">Password</label>
433                <div class="password-toggle">
434                    <input type="password" id="password" name="password" placeholder="Enter your password" required>
435                    <button type="button" class="password-toggle-btn" id="passwordToggle">👁️</button>
436                </div>
437            </div>
438
439            <div class="remember-forgot">
440                <label class="remember-me">
441                    <input type="checkbox" id="rememberMe" name="rememberMe">
442                    Remember me
443                </label>
444                <a href="#" class="forgot-password" id="forgotPassword">Forgot Password?</a>
445            </div>
446
447            <button type="submit" class="login-btn" id="loginBtn">
448                <span class="loading-spinner" id="loadingSpinner"></span>
449                <span id="loginBtnText">Sign In</span>
450            </button>
451        </form>
452
453        <!-- Signup Link -->
454        <div class="signup-link">
455            Don't have an account? <a href="signup.html">Sign up here</a>
456        </div>
457    </div>
458
459    <!-- Firebase SDK -->
460    
460<script type="module">
461        // Import Firebase modules
462        import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js';
463        import {
464            getAuth,
465            signInWithEmailAndPassword,
466            signInWithPopup,
467            GoogleAuthProvider,
468            OAuthProvider,
469            sendPasswordResetEmail,
470            setPersistence,
471            browserLocalPersistence,
472            browserSessionPersistence,
473            onAuthStateChanged
474        } from 'https://www.gstatic.com/firebasejs/10.7.1/firebase-auth.js';
475
476        // Firebase configuration
477        const firebaseConfig = {
478            apiKey: "AIzaSyDfdlgiOySnAXHppoart3ud_aVo2j--9sE",
479            authDomain: "luxmeter-fba54.firebaseapp.com",
480            projectId: "luxmeter-fba54",
481            storageBucket: "luxmeter-fba54.firebasestorage.app",
482            messagingSenderId: "233688623963",
483            appId: "1:233688623963:web:45f0e6517fa5d5f9ea087e",
484            measurementId: "G-PGP657TTYD"
485        };
486
487        // Initialize Firebase
488        const app = initializeApp(firebaseConfig);
489        const auth = getAuth(app);
490
491        // Providers
492        const googleProvider = new GoogleAuthProvider();
493        const appleProvider = new OAuthProvider('apple.com');
494
495        // DOM Elements
496        const loginForm = document.getElementById('loginForm');
497        const emailInput = document.getElementById('email');
498        const passwordInput = document.getElementById('password');
499        const rememberMeInput = document.getElementById('rememberMe');
500        const loginBtn = document.getElementById('loginBtn');
501        const loginBtnText = document.getElementById('loginBtnText');
502        const loadingSpinner = document.getElementById('loadingSpinner');
503        const errorMessage = document.getElementById('errorMessage');
504        const successMessage = document.getElementById('successMessage');
505        const passwordToggle = document.getElementById('passwordToggle');
506        const forgotPasswordLink = document.getElementById('forgotPassword');
507        const googleLoginBtn = document.getElementById('googleLogin');
508        const appleLoginBtn = document.getElementById('appleLogin');
509
510        // Check if user is already logged in
511        onAuthStateChanged(auth, (user) => {
512            if (user) {
513                // User is signed in, redirect to dashboard
514                showSuccess('Already signed in! Redirecting to dashboard...');
515                setTimeout(() => {
516                    window.location.href = 'accounts/my-dash.html';
517                }, 1500);
518            }
519        });
520
521        // Utility Functions
522        function showError(message) {
523            errorMessage.textContent = message;
524            errorMessage.style.display = 'block';
525            successMessage.style.display = 'none';
526            setTimeout(() => {
527                errorMessage.style.display = 'none';
528            }, 5000);
529        }
530
531        function showSuccess(message) {
532            successMessage.textContent = message;
533            successMessage.style.display = 'block';
534            errorMessage.style.display = 'none';
535            setTimeout(() => {
536                successMessage.style.display = 'none';
537            }, 5000);
538        }
539
540        function setLoading(loading) {
541            if (loading) {
542                loginBtn.disabled = true;
543                loadingSpinner.style.display = 'inline-block';
544                loginBtnText.textContent = 'Signing In...';
545            } else {
546                loginBtn.disabled = false;
547                loadingSpinner.style.display = 'none';
548                loginBtnText.textContent = 'Sign In';
549            }
550        }
551
552        function validateEmail(email) {
553            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
554            return emailRegex.test(email);
555        }
556
557        function validateForm() {
558            let isValid = true;
559
560            // Reset styles
561            emailInput.classList.remove('error', 'success');
562            passwordInput.classList.remove('error', 'success');
563
564            // Validate email
565            if (!emailInput.value.trim()) {
566                emailInput.classList.add('error');
567                isValid = false;
568            } else if (!validateEmail(emailInput.value)) {
569                emailInput.classList.add('error');
570                showError('Please enter a valid email address');
571                isValid = false;
572            } else {
573                emailInput.classList.add('success');
574            }
575
576            // Validate password
577            if (!passwordInput.value.trim()) {
578                passwordInput.classList.add('error');
579                isValid = false;
580            } else if (passwordInput.value.length < 6) {
581                passwordInput.classList.add('error');
582                showError('Password must be at least 6 characters long');
583                isValid = false;
584            } else {
585                passwordInput.classList.add('success');
586            }
587
588            return isValid;
589        }
590
591        // Password Toggle
592        passwordToggle.addEventListener('click', function() {
593            const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
594            passwordInput.setAttribute('type', type);
595            this.textContent = type === 'password' ? '👁️' : '🙈';
596        });
597
598        // Form Submission
599        loginForm.addEventListener('submit', async function(e) {
600            e.preventDefault();
601            
602            if (!validateForm()) {
603                return;
604            }
605
606            setLoading(true);
607
608            try {
609                // Set persistence based on remember me checkbox
610                const persistence = rememberMeInput.checked ? browserLocalPersistence : browserSessionPersistence;
611                await setPersistence(auth, persistence);
612
613                // Sign in with email and password
614                const userCredential = await signInWithEmailAndPassword(auth, emailInput.value, passwordInput.value);
615                const user = userCredential.user;
616
617                showSuccess('Login successful! Redirecting to dashboard...');
618                
619                // Store user info in localStorage if remember me is checked
620                if (rememberMeInput.checked) {
621                    localStorage.setItem('luxmeter_user', JSON.stringify({
622                        uid: user.uid,
623                        email: user.email,
624                        displayName: user.displayName,
625                        loginTime: new Date().toISOString()
626                    }));
627                }
628
629                // Redirect to dashboard after short delay
630                setTimeout(() => {
631                    window.location.href = 'accounts/my-dash.html';
632                }, 1500);
633
634            } catch (error) {
635                console.error('Login error:', error);
636                
637                let errorMsg = 'Login failed. Please try again.';
638                
639                switch (error.code) {
640                    case 'auth/user-not-found':
641                        errorMsg = 'No account found with this email address.';
642                        break;
643                    case 'auth/wrong-password':
644                        errorMsg = 'Incorrect password. Please try again.';
645                        break;
646                    case 'auth/invalid-email':
647                        errorMsg = 'Invalid email address format.';
648                        break;
649                    case 'auth/user-disabled':
650                        errorMsg = 'This account has been disabled.';
651                        break;
652                    case 'auth/too-many-requests':
653                        errorMsg = 'Too many failed attempts. Please try again later.';
654                        break;
655                    case 'auth/network-request-failed':
656                        errorMsg = 'Network error. Please check your connection.';
657                        break;
658                }
659                
660                showError(errorMsg);
661            } finally {
662                setLoading(false);
663            }
664        });
665
666        // Google Sign In
667        googleLoginBtn.addEventListener('click', async function() {
668            try {
669                const result = await signInWithPopup(auth, googleProvider);
670                const user = result.user;
671                
672                showSuccess('Google sign-in successful! Redirecting...');
673                
674                // Store user info
675                localStorage.setItem('luxmeter_user', JSON.stringify({
676                    uid: user.uid,
677                    email: user.email,
678                    displayName: user.displayName,
679                    photoURL: user.photoURL,
680                    loginTime: new Date().toISOString(),
681                    provider: 'google'
682                }));
683
684                setTimeout(() => {
685                    window.location.href = 'accounts/my-dash.html';
686                }, 1500);
687
688            } catch (error) {
689                console.error('Google sign-in error:', error);
690                showError('Google sign-in failed. Please try again.');
691            }
692        });
693
694        // Apple Sign In
695        appleLoginBtn.addEventListener('click', async function() {
696            try {
697                const result = await signInWithPopup(auth, appleProvider);
698                const user = result.user;
699                
700                showSuccess('Apple sign-in successful! Redirecting...');
701                
702                // Store user info
703                localStorage.setItem('luxmeter_user', JSON.stringify({
704                    uid: user.uid,
705                    email: user.email,
706                    displayName: user.displayName,
707                    loginTime: new Date().toISOString(),
708                    provider: 'apple'
709                }));
710
711                setTimeout(() => {
712                    window.location.href = 'accounts/my-dash.html';
713                }, 1500);
714
715            } catch (error) {
716                console.error('Apple sign-in error:', error);
717                showError('Apple sign-in failed. Please try again.');
718            }
719        });
720
721        // Forgot Password
722        forgotPasswordLink.addEventListener('click', async function(e) {
723            e.preventDefault();
724            
725            if (!emailInput.value.trim()) {
726                showError('Please enter your email address first');
727                emailInput.focus();
728                return;
729            }
730
731            if (!validateEmail(emailInput.value)) {
732                showError('Please enter a valid email address');
733                emailInput.focus();
734                return;
735            }
736
737            try {
738                await sendPasswordResetEmail(auth, emailInput.value);
739                showSuccess('Password reset email sent! Check your inbox.');
740            } catch (error) {
741                console.error('Password reset error:', error);
742                
743                let errorMsg = 'Failed to send password reset email.';
744                if (error.code === 'auth/user-not-found') {
745                    errorMsg = 'No account found with this email address.';
746                }
747                showError(errorMsg);
748            }
749        });
750
751        // Input validation on blur
752        emailInput.addEventListener('blur', function() {
753            if (this.value.trim() && validateEmail(this.value)) {
754                this.classList.remove('error');
755                this.classList.add('success');
756            } else if (this.value.trim()) {
757                this.classList.remove('success');
758                this.classList.add('error');
759            }
760        });
761
762        passwordInput.addEventListener('blur', function() {
763            if (this.value.trim() && this.value.length >= 6) {
764                this.classList.remove('error');
765                this.classList.add('success');
766            } else if (this.value.trim()) {
767                this.classList.remove('success');
768                this.classList.add('error');
769            }
770        });
771
772        // Clear validation on input
773        emailInput.addEventListener('input', function() {
774            this.classList.remove('error', 'success');
775        });
776
777        passwordInput.addEventListener('input', function() {
778            this.classList.remove('error', 'success');
779        });
780    </script>
780
781</body>
782</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.