diff --git a/public/app.js b/public/app.js index b76e83d..d8e1faf 100644 --- a/public/app.js +++ b/public/app.js @@ -114,6 +114,8 @@ async function initLoginPage() { if (btn.disabled) return errorEl.classList.add('hidden') btn.disabled = true + btn.classList.add('is-loading') + btn.setAttribute('aria-busy', 'true') let cooldownActive = false try { await api('/api/auth/login', { @@ -132,6 +134,8 @@ async function initLoginPage() { await applyCooldown(err.retryAfterSeconds || 900) } } finally { + btn.classList.remove('is-loading') + btn.removeAttribute('aria-busy') if (!cooldownActive) btn.disabled = false } }) diff --git a/public/login.css b/public/login.css new file mode 100644 index 0000000..63f12ee --- /dev/null +++ b/public/login.css @@ -0,0 +1,267 @@ +/* Login-Seite – angelehnt an Fandom Sign-in (Drawer + AuthForm) */ + +.login-page { + --primary: #9d3dfe; + --error: #f9191d; + --ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --input-size: 50px; + --input-gap: 10px; + --input-radius: 20px; + min-height: 100svh; + margin: 0; + background: #0a0a0a; + color: #fff; + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + overflow-x: hidden; +} + +@media (min-width: 62em) { + .login-page { + --input-size: 64px; + --input-gap: 5px; + --input-radius: 25px; + } +} + +.login-page::before { + content: ''; + position: fixed; + inset: 0; + z-index: 0; + pointer-events: none; + background: + radial-gradient(circle at 50% 45%, rgba(157, 61, 254, 0.35) 0%, transparent 55%), + radial-gradient(ellipse 80% 50% at 50% 50%, #1a1030 0%, #0a0a0a 70%); +} + +.login-bg { + position: fixed; + inset: 0; + z-index: 0; + background: #0a0a0a; +} + +.login-header { + position: fixed; + top: 0; + left: 0; + z-index: 2; + width: 100%; + padding: 24px; + display: flex; + align-items: center; + pointer-events: none; +} + +@media (min-width: 62em) { + .login-header { + padding: 42px 50px; + } +} + +.login-logo { + pointer-events: auto; + text-decoration: none; + color: #fff; + font-size: 1.125rem; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; + transition: color 0.25s var(--ease-out); +} + +.login-logo:hover, +.login-logo:focus-visible { + color: var(--primary); +} + +.login-main { + position: relative; + z-index: 1; + min-height: 100svh; + display: flex; + justify-content: flex-end; + align-items: stretch; +} + +.login-panel { + width: 100%; + max-width: 730px; + min-height: 100svh; + padding: 60px 40px; + display: flex; + flex-direction: column; + justify-content: center; + box-sizing: border-box; +} + +@media (min-width: 62em) { + .login-panel { + padding: 164px 40px; + } +} + +.AuthForm { + width: 100%; + max-width: 420px; + margin: 0 auto; +} + +.AuthForm .login-section { + margin-bottom: 0; +} + +.auth-label { + text-align: center; + margin: 0 0 50px; + font-size: 0.8125rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.1em; + line-height: 1; + color: #fff; +} + +@media (min-width: 62em) { + .auth-label { + font-size: 1rem; + } +} + +.login-form { + display: flex; + flex-direction: column; + gap: var(--input-gap); +} + +.auth-input { + color: #fff; + background-color: rgba(255, 255, 255, 0.25); + padding-inline: 40px; + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border-radius: var(--input-radius); + width: 100%; + height: var(--input-size); + border: none; + margin: 0; + box-sizing: border-box; + font-size: 1rem; + font-family: inherit; +} + +.auth-input::placeholder { + color: #fff; + opacity: 0.5; +} + +.auth-input:focus { + outline: 2px solid rgba(157, 61, 254, 0.6); + outline-offset: 2px; +} + +.auth-input.invalid { + border: 2px solid var(--error); +} + +.login-form .error { + text-align: center; + margin: 0; + font-size: 0.875rem; +} + +.auth-button { + width: 100%; + height: var(--input-size); + margin-top: var(--input-gap); + border: none; + border-radius: 32px; + background: #fff; + color: #1c1c1c; + font-size: 1rem; + font-weight: 600; + text-transform: uppercase; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + transition: + color 0.25s var(--ease-out), + background-color 0.25s var(--ease-out), + transform 0.25s var(--ease-out); +} + +.auth-button svg { + width: 12px; + height: 12px; + flex-shrink: 0; +} + +.auth-button:hover:not(:disabled) { + background: #1c1c1c; + color: #fff; +} + +.auth-button:active:not(:disabled) { + transform: scale(0.98); +} + +.auth-button:disabled { + opacity: 0.65; + cursor: not-allowed; +} + +.auth-button.is-loading .button-label { + opacity: 0; +} + +.auth-button .loading-spinner { + display: none; + width: 22px; + height: 22px; +} + +.auth-button.is-loading .loading-spinner { + display: block; + position: absolute; +} + +.auth-button { + position: relative; +} + +.loading-spinner { + animation: login-spin 1s linear infinite; +} + +@keyframes login-spin { + to { + transform: rotate(360deg); + } +} + +.login-hint { + text-align: center; + margin: 50px 0 0; + font-size: 0.875rem; + line-height: 1.5; + color: rgba(255, 255, 255, 0.55); + max-width: 420px; + margin-inline: auto; +} + +.login-page .hidden { + display: none !important; +} + +@media (max-width: 48em) { + .login-main { + justify-content: center; + } + + .login-panel { + max-width: none; + padding: 100px 24px 60px; + } +} diff --git a/public/login.html b/public/login.html index e33cb1a..e03c4b6 100644 --- a/public/login.html +++ b/public/login.html @@ -4,23 +4,58 @@ Anmelden – Webklar Kundenbereich - + - -

Webklar Kundenbereich

-
-
- - - - -
-

Zugangsdaten erhältst du von Webklar. Passwortänderungen erfolgen über das Ticketsystem.

+ + + +
+ +
+ +
+
+