MosswartOverlord/static/login.html

302 lines
9.6 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Dereth Tracker - Login</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #0a0a0a;
background-image:
radial-gradient(ellipse at 50% 35%, rgba(30, 20, 10, 0.9) 0%, transparent 60%),
linear-gradient(180deg, #0a0806 0%, #12100a 50%, #0a0806 100%);
font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif;
color: #d4c9a8;
}
.logo-container {
margin-bottom: 28px;
}
.logo-container img {
display: block;
width: 380px;
height: auto;
}
.quote-container {
height: 44px;
margin-bottom: 24px;
text-align: center;
width: 500px;
display: flex;
align-items: center;
justify-content: center;
}
.quote-text {
font-size: 0.8rem;
font-style: italic;
color: #8a7a5a;
line-height: 1.4;
opacity: 0;
transition: opacity 0.8s ease;
}
.quote-text.visible { opacity: 1; }
.quote-attribution {
display: block;
font-size: 0.6rem;
font-style: normal;
color: #5a4a34;
margin-top: 4px;
letter-spacing: 1px;
}
.login-card {
width: 440px;
background: linear-gradient(180deg, #1a1610 0%, #0e0c08 100%);
border: 1px solid #5a4a28;
border-radius: 4px;
padding: 24px 28px 20px;
box-shadow:
inset 0 1px 0 rgba(212, 175, 55, 0.08),
0 8px 32px rgba(0, 0, 0, 0.8),
0 0 60px rgba(138, 122, 68, 0.06);
}
.login-title {
text-align: center;
margin-bottom: 20px;
font-size: 0.75rem;
color: #8a7a5a;
text-transform: uppercase;
letter-spacing: 3px;
}
.form-row {
display: flex;
gap: 12px;
margin-bottom: 16px;
}
.form-field {
flex: 1;
}
.form-field label {
display: block;
font-size: 0.7rem;
color: #7a6a4a;
margin-bottom: 5px;
text-transform: uppercase;
letter-spacing: 1px;
}
.form-field input {
width: 100%;
padding: 9px 11px;
font-size: 0.9rem;
font-family: inherit;
background: #080704;
color: #d4c9a8;
border: 1px solid #3a3018;
border-radius: 3px;
outline: none;
transition: border-color 0.2s;
}
.form-field input:focus {
border-color: #d4af37;
box-shadow: 0 0 6px rgba(212, 175, 55, 0.12);
}
.form-field input::placeholder {
color: #3a3220;
}
.login-btn {
width: 100%;
padding: 10px;
font-family: inherit;
font-size: 0.85rem;
font-weight: bold;
color: #1a1610;
background: linear-gradient(180deg, #d4af37 0%, #a08520 100%);
border: 1px solid #8a7a44;
border-radius: 3px;
cursor: pointer;
text-transform: uppercase;
letter-spacing: 2px;
transition: background 0.2s, box-shadow 0.2s;
}
.login-btn:hover {
background: linear-gradient(180deg, #e0c050 0%, #b89a30 100%);
box-shadow: 0 2px 8px rgba(212, 175, 55, 0.3);
}
.login-btn:active {
background: linear-gradient(180deg, #a08520 0%, #8a7a44 100%);
}
.login-error {
margin-top: 12px;
padding: 8px;
text-align: center;
font-size: 0.8rem;
color: #ff6b6b;
background: rgba(255, 50, 50, 0.08);
border: 1px solid rgba(255, 50, 50, 0.2);
border-radius: 3px;
display: none;
}
.login-footer {
margin-top: 16px;
text-align: center;
font-size: 0.6rem;
color: #3a3220;
letter-spacing: 1px;
}
</style>
</head>
<body>
<div class="logo-container">
<img src="/icons/06001343.png" alt="Asheron's Call">
</div>
<div class="quote-container">
<div class="quote-text" id="quoteText"></div>
</div>
<div class="login-card">
<div class="login-title">Dereth Tracker</div>
<form id="loginForm" onsubmit="return handleLogin(event)">
<div class="form-row">
<div class="form-field">
<label for="username">Username</label>
<input type="text" id="username" name="username" autocomplete="username" autofocus required>
</div>
<div class="form-field">
<label for="password">Password</label>
<input type="password" id="password" name="password" autocomplete="current-password" required>
</div>
</div>
<button type="submit" class="login-btn" id="loginBtn">Enter Dereth</button>
<div class="login-error" id="loginError"></div>
</form>
<div class="login-footer">Mosswart Enjoyers Club</div>
</div>
<script>
const alexQuotes = [
"I don't run late. I glide fashionably through time.",
"Time is a flat circle\u2014and I'm still trying to find the starting point.",
"Being on time is like spotting a unicorn: rare, beautiful, and definitely not me.",
"Time zones are a social construct. I operate on instinct.",
"Every time I try to be early, time responds with traffic.",
"Time is an illusion. My ETA is performance art.",
"I treat deadlines like speed limits: suggestions.",
"I don't set alarms. I set vague intentions.",
"If you want me to be on time, lie about when it starts.",
"Why stress about time when you can just embrace chaos?",
"My to-do list is a historical document.",
"Time and I broke up. It was toxic.",
"I'm not late. I'm temporally creative.",
"'In five minutes' means something different in my language.",
"I don't track time. I let it track me\u2014poorly.",
"Time management is easy. Just avoid doing anything.",
"Time is fleeting. So is my attention span.",
"I live in the moment. Just never the right one.",
"Time doesn't control me. It just heavily inconveniences me.",
"I'm a time optimist: always wrong, always hopeful.",
"Punctuality is for people who don't trust spontaneity.",
"My life is a series of missed trains and strong coffee.",
"If time is a river, I'm definitely upstream without a paddle.",
"I don't watch the clock. I avoid eye contact with it.",
"'ASAP' means 'as soon as procrastination ends.'",
"Early is suspicious. On time is impressive. Late is expected.",
"Time management? I prefer time improvisation.",
"I like to keep time on its toes. Mostly by ignoring it.",
"Alarms are like plot twists. I didn't see it coming, and I still ignore it.",
"If being late was a sport, I'd already have missed the medal ceremony.",
"I don't lose track of time. I just pretend it doesn't exist.",
"Watches are decorative lies.",
"Time flies. I miss every flight.",
"My planner is just a coloring book for stress.",
"I'm not in a hurry. I'm in denial.",
"I live in the now, just usually a little bit after everyone else.",
"My calendar and I are estranged, but we're working on it.",
"The early bird catches the worm. I order food later.",
"Time moves fast. I move slower.",
"I was going to be on time, but then I remembered who I am.",
"I see '9:00 AM' and read it as 'guideline.'",
"Every plan is a maybe with extra steps.",
"I don't have time blindness\u2014I just have time indifference.",
"I'm not late. Reality is early.",
"Time and I aren't speaking after what happened last Monday.",
"Being on time is impressive. Being consistently late is a brand.",
"I'm in sync with the universe\u2014just in a different dimension.",
"My sense of time is like my sock drawer: chaotic and mostly missing.",
"Some people chase time. I let it wander off.",
"I'm not running late. I'm setting the tone for a relaxed experience.",
];
function shuffle(arr) {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
const shuffled = shuffle([...alexQuotes]);
let idx = 0;
const el = document.getElementById('quoteText');
function showQuote() {
el.classList.remove('visible');
setTimeout(() => {
if (idx >= shuffled.length) { idx = 0; shuffle(shuffled); }
el.innerHTML = '\u201C' + shuffled[idx] + '\u201D'
+ '<span class="quote-attribution">\u2014 Time according to Alex</span>';
idx++;
el.classList.add('visible');
}, 800);
}
showQuote();
setInterval(showQuote, 5000);
async function handleLogin(e) {
e.preventDefault();
const btn = document.getElementById('loginBtn');
const errDiv = document.getElementById('loginError');
errDiv.style.display = 'none';
btn.textContent = 'Authenticating...';
btn.disabled = true;
try {
const resp = await fetch('/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: document.getElementById('username').value,
password: document.getElementById('password').value,
}),
});
if (resp.ok) {
window.location.href = '/';
return;
}
const data = await resp.json();
errDiv.textContent = data.detail || 'Login failed';
errDiv.style.display = 'block';
} catch (err) {
errDiv.textContent = 'Connection error';
errDiv.style.display = 'block';
}
btn.textContent = 'Enter Dereth';
btn.disabled = false;
}
</script>
</body>
</html>