PageSourceSearch

https://birdfy-openid-auth.birdfy.com/login?next=https%3A%2F%2Fbir…s%3Den%26ui_country_code%3DUS&ui_locales=en

html birdfy.com collected 2026-09-24 10:20:31 UTC 31,797 bytes, 978 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  <link rel="icon" href="/static/birdfy-favicon.ico">
7  <title>Log in with Birdfy</title>
8  <link rel="stylesheet" href="/static/birdfy-styles.css?v=20260308d" />
9  <style>
10    :root {
11      --primary: #00D4CD;
12      --bg-start: #eef5f8;
13      --bg-end: #f3f6fb;
14      --border: #d9e0ea;
15    }
16
17    body {
18      perspective: 1400px;
19    }
20
21    .card.intro-spin {
22      transform-style: preserve-3d;
23      transform-origin: center center;
24      will-change: transform;
25      animation: introSpinY 0.75s cubic-bezier(0.2, 0.8, 0.2, 1) 1;
26    }
27
28    @keyframes introSpinY {
29      0% {
30        transform: rotateY(0deg);
31      }
32      100% {
33        transform: rotateY(360deg);
34      }
35    }
36
37    @media (prefers-reduced-motion: reduce) {
38      .card.intro-spin {
39        animation: none;
40      }
41    }
42
43    .submit-btn {
44      display: inline-flex;
45      align-items: center;
46      justify-content: center;
47      gap: 8px;
48      min-height: 42px;
49    }
50
51    .submit-btn:disabled {
52      opacity: 0.75;
53      cursor: not-allowed;
54    }
55
56    .signup-btn {
57      width: 100%;
58      min-height: 42px;
59      background: #ffffff;
60      color: var(--primary);
61      border: 1px solid var(--primary);
62      margin-top: 2px;
63    }
64
65    .signup-btn:hover {
66      background: rgba(0, 212, 205, 0.08);
67    }
68
69    .success-banner {
70      background: #eaf8f1;
71      color: #1e8e5a;
72      border: 1px solid #b9e2cc;
73      border-radius: 8px;
74      padding: 8px 10px;
75      margin-bottom: 12px;
76    }
77
78    .login-success-modal[hidden] {
79      display: none;
80    }
81
82    .login-success-modal {
83      position: fixed;
84      inset: 0;
85      z-index: 20;
86      display: flex;
87      align-items: center;
88      justify-content: center;
89      padding: 24px;
90      background: rgba(9, 20, 31, 0.42);
91    }
92
93    .login-success-dialog {
94      width: min(420px, 100%);
95      background: #fff;
96      border: 1px solid var(--border);
97      border-radius: 14px;
98      box-shadow: 0 18px 44px rgba(0, 0, 0, 0.18);
99      padding: 22px;
100    }
101
102    .login-success-dialog h2 {
103      margin: 0 0 10px;
104      font-size: 22px;
105    }
106
107    .login-success-dialog p {
108      margin: 0 0 18px;
109      color: #5f6f82;
110      line-height: 1.5;
111    }
112
113    .login-success-dialog .submit-btn {
114      width: 100%;
115    }
116
117    .forgot-btn {
118      align-self: flex-end;
119      min-height: 24px;
120      margin: -2px 0 2px;
121      padding: 0;
122      border: none;
123      background: transparent;
124      color: #5f6f82;
125      font-size: 13px;
126      font-weight: 500;
127      line-height: 1.2;
128      text-decoration: none;
129    }
130
131    .forgot-btn:hover {
132      color: #3f4f62;
133      text-decoration: underline;
134      text-decoration-thickness: 1px;
135      text-underline-offset: 2px;
136      background: transparent;
137    }
138
139    .credential-panel[hidden] {
140      display: none;
141    }
142
143    .credential-panel {
144      display: flex;
145      flex-direction: column;
146      gap: 8px;
147    }
148
149    .credential-panel > label {
150      display: block;
151    }
152
153    .field-row {
154      display: grid;
155      grid-template-columns: minmax(0, 1fr) auto;
156      gap: 10px;
157      align-items: center;
158    }
159
160    .send-code-btn {
161      min-width: 122px;
162      min-height: 42px;
163      padding: 0 14px;
164      border: 1px solid var(--primary);
165      border-radius: 10px;
166      background: #ffffff;
167      color: var(--primary);
168      font-weight: 600;
169      white-space: nowrap;
170    }
171
172    .send-code-btn:disabled {
173      opacity: 0.72;
174      cursor: not-allowed;
175    }
176
177    .send-code-btn:hover {
178      background: rgba(0, 212, 205, 0.08);
179    }
180
181    .login-utility-row {
182      display: flex;
183      align-items: center;
184      justify-content: space-between;
185      gap: 12px;
186      margin-top: 8px;
187    }
188
189    .marketing-opt-in {
190      display: flex;
191      align-items: flex-start;
192      gap: 10px;
193      margin-top: 10px;
194      color: #4f6072;
195      font-size: 14px;
196      line-height: 1.4;
197      cursor: pointer;
198    }
199
200    .marketing-opt-in input {
201      width: 16px;
202      height: 16px;
203      margin: 2px 0 0;
204      accent-color: var(--primary);
205      flex: 0 0 auto;
206    }
207
208    .marketing-opt-in span {
209      flex: 1 1 auto;
210    }
211
212    .alt-toggle {
213      width: auto;
214      min-height: 30px;
215      padding: 2px 0;
216      margin-top: 0;
217      border: none;
218      background: transparent;
219      color: #6a7787;
220      font-size: 14px;
221      font-weight: 500;
222      display: inline-flex;
223      align-items: center;
224      justify-content: flex-start;
225      gap: 6px;
226    }
227
228    .alt-toggle:hover {
229      color: #445265;
230      background: transparent;
231    }
232
233    .alt-toggle-icon {
234      font-size: 12px;
235      line-height: 1;
236      transition: transform 0.18s ease;
237    }
238
239    .alt-toggle[aria-expanded="true"] .alt-toggle-icon {
240      transform: rotate(180deg);
241    }
242
243    .mode-switch-btn {
244      min-height: 30px;
245      padding: 2px 0;
246      border: none;
247      background: transparent;
248      color: #5d6d81;
249      font-size: 14px;
250      font-weight: 600;
251      display: inline-flex;
252      align-items: center;
253      justify-content: flex-end;
254      gap: 6px;
255      margin-left: auto;
256    }
257
258    .mode-switch-btn:hover {
259      color: #21384d;
260      background: transparent;
261    }
262
263    .social-options {
264      max-height: 0;
265      opacity: 0;
266      overflow: hidden;
267      transform: translateY(-4px);
268      transition: max-height 0.24s ease, opacity 0.2s ease, transform 0.2s ease;
269    }
270
271    .social-options.open {
272      max-height: 240px;
273      opacity: 1;
274      transform: translateY(0);
275    }
276
277    .alt-divider {
278      display: flex;
279      align-items: center;
280      margin: 8px 0 10px;
281      color: #6a7787;
282      font-size: 13px;
283      line-height: 1.2;
284    }
285
286    .alt-divider::before,
287    .alt-divider::after {
288      content: "";
289      flex: 1;
290      border-top: 1px solid #d9e0ea;
291    }
292
293    .alt-divider span {
294      padding: 0 10px;
295      text-align: center;
296    }
297
298    .social-btn {
299      width: 100%;
300      min-height: 42px;
301      display: inline-flex;
302      align-items: center;
303      justify-content: center;
304      gap: 10px;
305      border: 1px solid transparent;
306      margin-top: 0;
307    }
308
309    .social-btn:disabled {
310      opacity: 0.72;
311      cursor: not-allowed;
312    }
313
314    .social-options .facebook-btn {
315      margin-top: 10px;
316    }
317
318    .social-options .apple-btn {
319      margin-top: 14px;
320    }
321
322    .social-btn img {
323      width: 18px;
324      height: 18px;
325      display: block;
326    }
327
328    .facebook-btn {
329      background: #1877f2;
330      color: #ffffff;
331    }
332
333    .facebook-btn:hover {
334      background: #166fe5;
335    }
336
337    .apple-btn {
338      background: #000000;
339      color: #ffffff;
340    }
341
342    .apple-btn:hover {
343      background: #161616;
344    }
345
346    .btn-spinner {
347      width: 14px;
348      height: 14px;
349      border: 2px solid rgba(255, 255, 255, 0.4);
350      border-top-color: #ffffff;
351      border-radius: 9999px;
352      display: none;
353      animation: spin 0.8s linear infinite;
354    }
355
356    .submit-btn.is-loading .btn-spinner {
357      display: inline-block;
358    }
359
360    .auth-shell {
361      width: 100%;
362      max-width: 420px;
363      max-height: 100%;
364      display: flex;
365      flex-direction: column;
366      align-items: stretch;
367    }
368
369    .auth-shell .card {
370      max-width: none;
371      max-height: none;
372      overflow-y: visible;
373    }
374
375    .page-footer {
376      margin-top: 10px;
377      text-align: center;
378      font-size: 12px;
379      color: #7a8593;
380      line-height: 1.3;
381      flex: 0 0 auto;
382      user-select: none;
383    }
384
385    @media (max-height: 900px) {
386      .auth-shell .card {
387        max-height: calc(100dvh - 74px);
388        overflow-y: auto;
389      }
390    }
391
392    @keyframes spin {
393      to {
394        transform: rotate(360deg);
395      }
396    }
397  </style>
398</head>
399<body>
400  <div class="auth-shell">
401    <main class="card intro-spin">
402      <div class="brand-logo-wrap">
403        <img src="/static/logos/birdfy-logo.svg" alt="Birdfy logo" class="brand-logo" />
404      </div>
405      <h1>Log in with Birdfy</h1>
406      <p class="subtitle">
407        
408        
409          
410          Use your Birdfy Account to log in to <strong>Birdfy.com</strong>.
411        
412      </p>
413
414      <div id="login-success" class="success-banner" hidden></div>
415      <div id="login-error" class="error" hidden></div>
416
417      <form method="post" action="/login" id="login-form">
418        <input type="hidden" name="next" value="https://birdfy-openid-auth.birdfy.com/authorize?scope=openid+email&amp;client_id=birdfy-shopify&amp;response_type=code&amp;redirect_uri=https%3A%2F%2Fshopify.com%2Fauthentication%2F78293467416%2Flogin%2Fexternal%2Fcallback&amp;state=hWNHCLOpaEkoLie8FAi6zR6g&amp;nonce=c79a1e59-ce2e-4c73-bdd7-6e1525f690c0&amp;ui_locales=en&amp;ui_country_code=US" />
419        <input type="hidden" name="locale" value="en" />
420        <input type="hidden" name="login_method" id="login-method-input" value="code" />
421
422        <label for="username">Email</label>
423        <input id="username" name="username" type="email" inputmode="email" autocomplete="username email" required />
424
425        <div id="login-code-panel" class="credential-panel">
426          <label for="code">Verification Code</label>
427          <div class="field-row">
428            <input id="code" name="code" type="text" inputmode="numeric" maxlength="6" autocomplete="one-time-code" />
429            <button type="button" id="send-code-btn" class="send-code-btn">
430              <span class="btn-label">Send Code</span>
431            </button>
432          </div>
433        </div>
434
435        <div id="login-password-panel" class="credential-panel" hidden>
436          <label for="password">Password</label>
437          <input id="password" name="password" type="password" autocomplete="current-password" />
438          <button type="button" id="forgot-password-btn" class="forgot-btn">Forgot password?</button>
439        </div>
440
441        <label class="marketing-opt-in" for="news-and-offers-opt-in">
442          <input
443            id="news-and-offers-opt-in"
444            name="news_and_offers_opt_in"
445            type="checkbox"
446            value="true"
447            checked
448          />
449          <span>Email me with news and offers</span>
450        </label>
451
452        <button type="submit" id="login-submit" class="submit-btn">
453          <span class="btn-spinner" aria-hidden="true"></span>
454          <span class="btn-label">Continue</span>
455        </button>
456        <button type="button" id="signup-btn" class="signup-btn">Sign up</button>
457
458        <div class="login-utility-row">
459          <button
460            type="button"
461            class="alt-toggle"
462            id="social-toggle"
463            aria-expanded="false"
464            aria-controls="social-options"
465          >
466            <span>Other log-in options</span>
467            <span class="alt-toggle-icon" aria-hidden="true">▾</span>
468          </button>
469
470          <button
471            type="button"
472            class="mode-switch-btn"
473            id="login-mode-switch"
474            aria-label="Login method"
475          >
476            Password
477          </button>
478        </div>
479
480        <div id="social-options" class="social-options" aria-hidden="true">
481          <div class="alt-divider">
482            <span>Or continue with</span>
483          </div>
484
485          <button type="button" id="facebook-login-btn" class="social-btn facebook-btn">
486            <img src="/static/logos/facebook.svg" alt="Facebook" />
487            <span class="social-label">Continue with Facebook</span>
488          </button>
489
490          <button type="button" id="apple-login-btn" class="social-btn apple-btn">
491            <img src="/static/logos/apple.svg" alt="Apple" />
492            <span class="social-label">Continue with Apple</span>
493          </button>
494        </div>
495      </form>
496      <form method="post" action="https://birdfy-openid-auth.birdfy.com/facebook-login" id="facebook-login-form" hidden>
497        <input type="hidden" name="next" value="https://birdfy-openid-auth.birdfy.com/authorize?scope=openid+email&amp;client_id=birdfy-shopify&amp;response_type=code&amp;redirect_uri=https%3A%2F%2Fshopify.com%2Fauthentication%2F78293467416%2Flogin%2Fexternal%2Fcallback&amp;state=hWNHCLOpaEkoLie8FAi6zR6g&amp;nonce=c79a1e59-ce2e-4c73-bdd7-6e1525f690c0&amp;ui_locales=en&amp;ui_country_code=US" />
498        <input type="hidden" name="locale" value="en" />
499        <input type="hidden" name="token" id="facebook-login-token" value="" />
500        <input
501          type="hidden"
502          name="news_and_offers_opt_in"
503          id="facebook-news-and-offers-opt-in"
504          value="true"
505        />
506      </form>
507    </main>
508    <div class="page-footer">Copyright © 2026 Netvue Inc.</div>
509  </div>
510  <div id="login-success-modal" class="login-success-modal" hidden>
511    <div class="login-success-dialog" role="dialog" aria-modal="true" aria-labelledby="login-success-title">
512      <h2 id="login-success-title">Check your email</h2>
513      <p id="login-success-modal-message"></p>
514      <button type="button" id="login-success-modal-close" class="submit-btn">OK</button>
515    </div>
516  </div>
517  
517<script>
518    (() => {
519      const form = document.getElementById("login-form");
520      const submitButton = document.getElementById("login-submit");
521      const label = submitButton ? submitButton.querySelector(".btn-label") : null;
522      const errorBox = document.getElementById("login-error");
523      const successBox = document.getElementById("login-success");
524      const loginMethodInput = document.getElementById("login-method-input");
525      const loginModeSwitch = document.getElementById("login-mode-switch");
526      const loginCodePanel = document.getElementById("login-code-panel");
527      const loginPasswordPanel = document.getElementById("login-password-panel");
528      const usernameInput = document.getElementById("username");
529      const passwordInput = document.getElementById("password");
530      const codeInput = document.getElementById("code");
531      const sendCodeButton = document.getElementById("send-code-btn");
532      const sendCodeLabel = sendCodeButton ? sendCodeButton.querySelector(".btn-label") : null;
533      const forgotButton = document.getElementById("forgot-password-btn");
534      const signupButton = document.getElementById("signup-btn");
535      const facebookLoginButton = document.getElementById("facebook-login-btn");
536      const facebookLoginForm = document.getElementById("facebook-login-form");
537      const facebookLoginToken = document.getElementById("facebook-login-token");
538      const facebookNewsAndOffersOptIn = document.getElementById("facebook-news-and-offers-opt-in");
539      const appleLoginButton = document.getElementById("apple-login-btn");
540      const newsAndOffersOptIn = document.getElementById("news-and-offers-opt-in");
541      const forgotPasswordUrl = "https://birdfy-openid-auth.birdfy.com/forgot-password?next=https%3A%2F%2Fbirdfy-openid-auth.birdfy.com%2Fauthorize%3Fscope%3Dopenid%2Bemail%26client_id%3Dbirdfy-shopify%26response_type%3Dcode%26redirect_uri%3Dhttps%253A%252F%252Fshopify.com%252Fauthentication%252F78293467416%252Flogin%252Fexternal%252Fcallback%26state%3DhWNHCLOpaEkoLie8FAi6zR6g%26nonce%3Dc79a1e59-ce2e-4c73-bdd7-6e1525f690c0%26ui_locales%3Den%26ui_country_code%3DUS\u0026ui_locales=en";
542      const socialToggle = document.getElementById("social-toggle");
543      const socialOptions = document.getElementById("social-options");
544      const signupUrl = "https://birdfy-openid-auth.birdfy.com/signup?next=https%3A%2F%2Fbirdfy-openid-auth.birdfy.com%2Fauthorize%3Fscope%3Dopenid%2Bemail%26client_id%3Dbirdfy-shopify%26response_type%3Dcode%26redirect_uri%3Dhttps%253A%252F%252Fshopify.com%252Fauthentication%252F78293467416%252Flogin%252Fexternal%252Fcallback%26state%3DhWNHCLOpaEkoLie8FAi6zR6g%26nonce%3Dc79a1e59-ce2e-4c73-bdd7-6e1525f690c0%26ui_locales%3Den%26ui_country_code%3DUS\u0026ui_locales=en";
545      const appleLoginUrl = "https://birdfy-openid-auth.birdfy.com/apple-login?next=https%3A%2F%2Fbirdfy-openid-auth.birdfy.com%2Fauthorize%3Fscope%3Dopenid%2Bemail%26client_id%3Dbirdfy-shopify%26response_type%3Dcode%26redirect_uri%3Dhttps%253A%252F%252Fshopify.com%252Fauthentication%252F78293467416%252Flogin%252Fexternal%252Fcallback%26state%3DhWNHCLOpaEkoLie8FAi6zR6g%26nonce%3Dc79a1e59-ce2e-4c73-bdd7-6e1525f690c0%26ui_locales%3Den%26ui_country_code%3DUS\u0026ui_locales=en";
546      const loginSendCodeUrl = "https://birdfy-openid-auth.birdfy.com/login/send-code";
547      const initialLoginMethod = "code";
548      const successModal = document.getElementById("login-success-modal");
549      const successModalMessage = document.getElementById("login-success-modal-message");
550      const successModalClose = document.getElementById("login-success-modal-close");
551      const loadingText = "Logging in...";
552      const sendCodeText = "Send Code";
553      const resendWaitPrefix = "Resend in";
554      const sendingCodeText = "Sending code...";
555      const useCodeLoginText = "Verification Code";
556      const usePasswordLoginText = "Password";
557      const facebookLoadingText = "Connecting to Facebook...";
558      const appleLoadingText = "Redirecting to Apple...";
559      const facebookSdkLoadFailedText = "Facebook sign-in is currently unavailable on this device or browser.";
560      const requiredEmailMessage = "Please enter your email address.";
561      const invalidEmailMessage = "Please enter a valid email address.";
562      const invalidCodeMessage = "Please enter a valid 6-digit verification code.";
563      const sendCodeFailedText = "Unable to send the verification code right now. Please try again later.";
564      const facebookAppId = "6748322615250899";
565      const facebookSdkSrcUrl = "https://connect.facebook.net/en_US/sdk.js";
566      const facebookSdkVersion = "v19.0";
567      const forgotPasswordSuccessCode = "forgot_password_email_sent";
568      const emailRegex = /^[a-z0-9]+([._-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+\.){1,63}[a-z0-9]+$/;
569      let facebookSdkPromise = null;
570      let sendCodeCooldownSeconds = 0;
571      let sendCodeCountdownTimer = null;
572
573      const showInlineError = (message) => {
574        if (!errorBox || !message) {
575          return;
576        }
577        errorBox.hidden = false;
578        errorBox.textContent = message;
579      };
580
581      const showInlineSuccess = (message) => {
582        if (!successBox || !message) {
583          return;
584        }
585        successBox.hidden = false;
586        successBox.textContent = message;
587      };
588
589      const setSocialButtonLoading = (button, text) => {
590        if (!button) {
591          return;
592        }
593        button.disabled = true;
594        const labelNode = button.querySelector(".social-label");
595        if (labelNode && text) {
596          labelNode.textContent = text;
597        }
598      };
599
600      const restoreSocialButtonLabel = (button, text) => {
601        if (!button) {
602          return;
603        }
604        button.disabled = false;
605        const labelNode = button.querySelector(".social-label");
606        if (labelNode && text) {
607          labelNode.textContent = text;
608        }
609      };
610
611      const loadFacebookSdk = () => {
612        if (window.FB && typeof window.FB.init === "function") {
613          return Promise.resolve(window.FB);
614        }
615        if (facebookSdkPromise) {
616          return facebookSdkPromise;
617        }
618        facebookSdkPromise = new Promise((resolve, reject) => {
619          const existing = document.getElementById("facebook-jssdk");
620          if (existing) {
621            const waitForSdk = () => {
622              if (window.FB && typeof window.FB.init === "function") {
623                resolve(window.FB);
624                return;
625              }
626              window.setTimeout(waitForSdk, 50);
627            };
628            waitForSdk();
629            return;
630          }
631
632          const script = document.createElement("script");
633          script.id = "facebook-jssdk";
634          script.async = true;
635          script.defer = true;
636          script.crossOrigin = "anonymous";
637          script.src = facebookSdkSrcUrl;
638          script.onload = () => {
639            if (!window.FB || typeof window.FB.init !== "function") {
640              reject(new Error("facebook_sdk_missing"));
641              return;
642            }
643            window.FB.init({
644              appId: facebookAppId,
645              autoLogAppEvents: true,
646              xfbml: true,
647              version: facebookSdkVersion,
648            });
649            resolve(window.FB);
650          };
651          script.onerror = () => reject(new Error("facebook_sdk_load_failed"));
652          document.body.appendChild(script);
653        }).catch((error) => {
654          facebookSdkPromise = null;
655          throw error;
656        });
657        return facebookSdkPromise;
658      };
659
660      const clearMessages = () => {
661        if (errorBox) {
662          errorBox.hidden = true;
663          errorBox.textContent = "";
664        }
665        if (successBox) {
666          successBox.hidden = true;
667          successBox.textContent = "";
668        }
669        if (successModal) {
670          successModal.hidden = true;
671        }
672        if (window.location.hash) {
673          history.replaceState(null, "", window.location.pathname + window.location.search);
674        }
675      };
676
677      const stopSendCodeCountdown = () => {
678        if (sendCodeCountdownTimer) {
679          window.clearInterval(sendCodeCountdownTimer);
680          sendCodeCountdownTimer = null;
681        }
682      };
683
684      const renderSendCodeCountdown = () => {
685        if (!sendCodeButton || !sendCodeLabel) {
686          return;
687        }
688        if (sendCodeCooldownSeconds > 0) {
689          sendCodeButton.disabled = true;
690          sendCodeLabel.textContent = `${resendWaitPrefix} ${sendCodeCooldownSeconds}s`;
691          sendCodeCooldownSeconds -= 1;
692          return;
693        }
694        sendCodeButton.disabled = false;
695        sendCodeLabel.textContent = sendCodeText;
696        stopSendCodeCountdown();
697      };
698
699      const startSendCodeCountdown = (seconds) => {
700        sendCodeCooldownSeconds = Math.max(0, Number(seconds) || 0);
701        stopSendCodeCountdown();
702        renderSendCodeCountdown();
703        if (sendCodeCooldownSeconds > 0) {
704          sendCodeCountdownTimer = window.setInterval(renderSendCodeCountdown, 1000);
705        }
706      };
707
708      const validateEmail = () => {
709        const email = (usernameInput?.value || "").trim().toLowerCase();
710        if (!email) {
711          showInlineError(requiredEmailMessage);
712          usernameInput?.focus();
713          return "";
714        }
715        if (!emailRegex.test(email)) {
716          showInlineError(invalidEmailMessage);
717          usernameInput?.focus();
718          return "";
719        }
720        return email;
721      };
722
723      const isNewsAndOffersOptedIn = () => {
724        return Boolean(newsAndOffersOptIn?.checked);
725      };
726
727      const getActiveLoginMethod = () => {
728        return (loginMethodInput?.value || "code").trim() === "password" ? "password" : "code";
729      };
730
731      const applyLoginMethod = (method) => {
732        const resolvedMethod = method === "password" ? "password" : "code";
733        if (loginMethodInput) {
734          loginMethodInput.value = resolvedMethod;
735        }
736        if (loginCodePanel) {
737          loginCodePanel.hidden = resolvedMethod !== "code";
738        }
739        if (loginPasswordPanel) {
740          loginPasswordPanel.hidden = resolvedMethod !== "password";
741        }
742        if (passwordInput) {
743          passwordInput.disabled = resolvedMethod !== "password";
744        }
745        if (codeInput) {
746          codeInput.disabled = resolvedMethod !== "code";
747        }
748        if (forgotButton) {
749          forgotButton.hidden = resolvedMethod !== "password";
750        }
751        if (loginModeSwitch) {
752          loginModeSwitch.textContent = resolvedMethod === "password" ? useCodeLoginText : usePasswordLoginText;
753        }
754      };
755
756      const showSuccessModal = (message) => {
757        if (!successModal || !successModalMessage) {
758          return;
759        }
760        successModalMessage.textContent = message;
761        successModal.hidden = false;
762      };
763
764      const decodeBase64Utf8 = (value) => {
765        try {
766          const binary = atob(value);
767          const bytes = Uint8Array.from(binary, (ch) => ch.charCodeAt(0));
768          return new TextDecoder().decode(bytes);
769        } catch {
770          return "";
771        }
772      };
773
774      const renderLoginErrorFromHash = () => {
775        if (!errorBox && !successBox) {
776          return;
777        }
778        const params = new URLSearchParams(window.location.hash.replace(/^#/, ""));
779        const successCode = (params.get("success") || "").trim();
780        const encodedSuccessMessage = (params.get("success_b64") || "").trim();
781        const successMessage = decodeBase64Utf8(encodedSuccessMessage);
782        if (successMessage) {
783          if (successCode === forgotPasswordSuccessCode) {
784            showSuccessModal(successMessage);
785          } else if (successBox) {
786            successBox.hidden = false;
787            successBox.textContent = successMessage;
788          }
789        }
790        const encodedMessage = (params.get("error_b64") || "").trim();
791        const message = decodeBase64Utf8(encodedMessage);
792        if (message && errorBox) {
793          errorBox.hidden = false;
794          errorBox.textContent = message;
795        }
796      };
797
798      renderLoginErrorFromHash();
799      applyLoginMethod(initialLoginMethod);
800
801      if (loginModeSwitch) {
802        loginModeSwitch.addEventListener("click", () => {
803          clearMessages();
804          applyLoginMethod(getActiveLoginMethod() === "password" ? "code" : "password");
805        });
806      }
807
808      if (forgotButton) {
809        forgotButton.addEventListener("click", () => {
810          clearMessages();
811          window.location.href = forgotPasswordUrl;
812        });
813      }
814      if (signupButton) {
815        signupButton.addEventListener("click", () => {
816          clearMessages();
817          window.location.href = signupUrl;
818        });
819      }
820      if (codeInput) {
821        codeInput.addEventListener("input", () => {
822          codeInput.value = (codeInput.value || "").replace(/\D/g, "").slice(0, 6);
823        });
824      }
825      if (sendCodeButton) {
826        sendCodeButton.addEventListener("click", async () => {
827          if (sendCodeButton.disabled) {
828            return;
829          }
830          clearMessages();
831          const email = validateEmail();
832          if (!email) {
833            return;
834          }
835          sendCodeButton.disabled = true;
836          if (sendCodeLabel) {
837            sendCodeLabel.textContent = sendingCodeText;
838          }
839          const formData = new FormData();
840          formData.set("username", email);
841          formData.set("next", document.querySelector('input[name="next"]')?.value || "");
842          formData.set("locale", document.querySelector('input[name="locale"]')?.value || "");
843          try {
844            const response = await fetch(loginSendCodeUrl, {
845              method: "POST",
846              body: formData,
847            });
848            let payload = {};
849            try {
850              payload = await response.json();
851            } catch {
852              payload = {};
853            }
854            const message = (payload?.message || "").trim();
855            const cooldownSeconds = Number(payload?.cooldown_seconds) || 0;
856            if (!response.ok) {
857              if (cooldownSeconds > 0) {
858                startSendCodeCountdown(cooldownSeconds);
859              } else {
860                sendCodeButton.disabled = false;
861                if (sendCodeLabel) {
862                  sendCodeLabel.textContent = sendCodeText;
863                }
864              }
865              showInlineError(message || sendCodeFailedText);
866              return;
867            }
868            showInlineSuccess(message);
869            startSendCodeCountdown(cooldownSeconds);
870          } catch {
871            sendCodeButton.disabled = false;
872            if (sendCodeLabel) {
873              sendCodeLabel.textContent = sendCodeText;
874            }
875            showInlineError(sendCodeFailedText);
876          }
877        });
878      }
879      if (facebookLoginButton && facebookLoginForm && facebookLoginToken) {
880        const initialFacebookText = facebookLoginButton.querySelector(".social-label")?.textContent || "";
881        facebookLoginButton.addEventListener("click", async () => {
882          if (facebookLoginButton.disabled) {
883            return;
884          }
885          clearMessages();
886          setSocialButtonLoading(facebookLoginButton, facebookLoadingText);
887          try {
888            const FB = await loadFacebookSdk();
889            FB.login((response) => {
890              const accessToken = response?.authResponse?.accessToken;
891              if (response?.status === "connected" && accessToken) {
892                facebookLoginToken.value = accessToken;
893                if (facebookNewsAndOffersOptIn) {
894                  facebookNewsAndOffersOptIn.value = isNewsAndOffersOptedIn() ? "true" : "false";
895                }
896                requestAnimationFrame(() => facebookLoginForm.submit());
897                return;
898              }
899              restoreSocialButtonLabel(facebookLoginButton, initialFacebookText);
900              showInlineError("Facebook sign-in failed. Please try again or use another login method.");
901            }, {
902              auth_type: "reauthenticate",
903            });
904          } catch {
905            restoreSocialButtonLabel(facebookLoginButton, initialFacebookText);
906            showInlineError(facebookSdkLoadFailedText);
907          }
908        });
909      }
910      if (appleLoginButton) {
911        const initialAppleText = appleLoginButton.querySelector(".social-label")?.textContent || "";
912        appleLoginButton.addEventListener("click", () => {
913          if (appleLoginButton.disabled) {
914            return;
915          }
916          clearMessages();
917          setSocialButtonLoading(appleLoginButton, appleLoadingText);
918          const target = new URL(appleLoginUrl, window.location.origin);
919          target.searchParams.set("news_and_offers_opt_in", isNewsAndOffersOptedIn() ? "true" : "false");
920          window.location.href = target.toString();
921        });
922      }
923      if (socialToggle && socialOptions) {
924        socialToggle.addEventListener("click", () => {
925          const expanded = socialToggle.getAttribute("aria-expanded") === "true";
926          const next = !expanded;
927          socialToggle.setAttribute("aria-expanded", String(next));
928          socialOptions.classList.toggle("open", next);
929          socialOptions.setAttribute("aria-hidden", String(!next));
930        });
931      }
932
933      if (successModal) {
934        successModal.addEventListener("click", (event) => {
935          if (event.target === successModal) {
936            clearMessages();
937          }
938        });
939      }
940
941      if (successModalClose) {
942        successModalClose.addEventListener("click", clearMessages);
943      }
944
945      if (!form || !submitButton || !label) {
946        return;
947      }
948
949      form.addEventListener("submit", (event) => {
950        if (submitButton.disabled) {
951          event.preventDefault();
952          return;
953        }
954        clearMessages();
955        if (getActiveLoginMethod() === "code") {
956          const email = validateEmail();
957          if (!email) {
958            event.preventDefault();
959            return;
960          }
961          if (!codeInput || !/^\d{6}$/.test((codeInput.value || "").trim())) {
962            event.preventDefault();
963            showInlineError(invalidCodeMessage);
964            codeInput?.focus();
965            return;
966          }
967        }
968        event.preventDefault();
969        submitButton.disabled = true;
970        submitButton.classList.add("is-loading");
971        submitButton.setAttribute("aria-busy", "true");
972        label.textContent = loadingText;
973        requestAnimationFrame(() => form.submit());
974      });
975    })();
976  </script>
976
977</body>
978</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.