Owl login: sad droop, peek glow, tap jump; title in card; docs refreshed

- Wrong password: owl droops its head with a slow blink (replaces the head-shake)
- Show password: the owl's eye rims and lenses glow amber
- Tap the owl and it jumps
- Title "G2Team Potential Updater" and "Sign in" centered inside the card; subtitle hides after sign-in
- Password field autofocuses
- DESIGN.md, design.json, PRODUCT.md and the design brief updated to match the page

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
VasanthandClaude Opus 5.5 committed 2026-10-22 17:01:24 +05:30
1 parent 3b6e35fa62
commit 1e44758398
5 files changed
+443 -106

No files matched your search

+34 -29
View File
@@ -52,18 +52,19 @@
flex-direction: column;
align-items: center;
/* Anchored near the middle rather than centered, so an error line grows the card downward instead of moving the owl. */
padding: max(32px, calc(50vh - 250px)) 16px 48px;
padding: max(32px, calc(50vh - 230px)) 16px 48px;
text-align: center;
}
h1 {
margin: 0;
font-size: clamp(24px, 6vw, 30px);
font-size: clamp(20px, 5vw, 23px);
font-weight: 650;
line-height: 1.2;
letter-spacing: -.01em;
text-wrap: balance;
text-align: center;
}
.sub { margin: 8px 0 0; color: var(--ink-2); }
.sub { margin: 4px 0 22px; color: var(--ink-2); text-align: center; }
.card {
position: relative;
@@ -155,7 +156,8 @@
.done p { margin: 6px 0 0; color: var(--ink-2); }
/* Owl. Origins are in the SVG's own units (viewBox 0 0 48 50). */
#head, #glasses, #wing-l, #wing-r { transform-box: view-box; }
#bob, #head, #glasses, #wing-l, #wing-r { transform-box: view-box; }
#bob { transform-origin: 24px 30px; }
#head { transform-origin: 24px 30px; }
#glasses { transform-origin: 24px 24px; }
#wing-l { transform-origin: 11px 25px; }
@@ -169,27 +171,25 @@
.owl.idle #bob { animation: breathe 3.2s ease-in-out infinite; }
@keyframes breathe { 50% { transform: translateY(.4px); } }
.owl.shake #head { animation: no .65s ease-in-out; }
@keyframes no {
15% { transform: translateX(-1.5px) rotate(-5deg); }
32% { transform: translateX(1.5px) rotate(5deg); }
50% { transform: translateX(-1.2px) rotate(-4deg); }
68% { transform: translateX(.9px) rotate(3deg); }
85% { transform: translateX(-.3px) rotate(-1deg); }
}
.owl.shake #glasses { animation: wobble .65s ease-in-out .07s; }
@keyframes wobble {
20% { transform: rotate(4deg); } 40% { transform: rotate(-4deg); }
60% { transform: rotate(3deg); } 80% { transform: rotate(-1.5deg); }
}
.owl.sad #head { animation: droop 1.4s ease-in-out; }
@keyframes droop { 20%, 75% { transform: translateY(1.2px) rotate(-6deg); } }
.owl.sad .lid { animation: slow-blink 1.4s ease-in-out; }
@keyframes slow-blink { 30%, 55% { transform: scaleY(1); } }
.eye-rim { transition: stroke .15s ease-out, filter .15s ease-out; }
.owl.peek .eye-rim { stroke: #ffc400; stroke-width: 1.5; filter: drop-shadow(0 0 2px #ffd84d) drop-shadow(0 0 5px #ffb300); }
#glasses rect { transition: fill .15s ease-out, fill-opacity .15s ease-out, filter .15s ease-out; }
.owl.peek #glasses rect { fill: #ffe066; fill-opacity: .45; filter: drop-shadow(0 0 3px #ffc400); }
.owl.tilt #head { animation: tilt .8s ease-in-out; }
@keyframes tilt { 25%, 70% { transform: rotate(-10deg); } }
.owl.happy { animation: hop .75s cubic-bezier(.3, .6, .35, 1); }
.owl { cursor: pointer; }
.owl.happy, .owl.jump { animation: hop .75s cubic-bezier(.3, .6, .35, 1); }
@keyframes hop { 40% { transform: translateY(-14px); } 70% { transform: none; } 82% { transform: translateY(-3px); } }
.owl.happy #wing-l { animation: flap-l .75s ease-in-out; }
.owl.happy #wing-r { animation: flap-r .75s ease-in-out; }
.owl.happy #wing-l, .owl.jump #wing-l { animation: flap-l .75s ease-in-out; }
.owl.happy #wing-r, .owl.jump #wing-r { animation: flap-r .75s ease-in-out; }
@keyframes flap-l { 20%, 60% { transform: rotate(32deg); } 40%, 80% { transform: rotate(8deg); } }
@keyframes flap-r { 20%, 60% { transform: rotate(-32deg); } 40%, 80% { transform: rotate(-8deg); } }
@@ -206,9 +206,6 @@
<div class="sky" id="sky" aria-hidden="true"></div>
<main class="stage">
<h1>G2Team Potential Updater</h1>
<p class="sub">Sign in to run the bot</p>
<section class="card">
<svg class="owl idle" id="owl" viewBox="0 0 48 50" aria-hidden="true">
<defs>
@@ -240,14 +237,14 @@
<rect class="lid" x="13" y="15" width="8" height="10" fill="#ffe8b3" stroke="none"/>
<g class="smile"><path fill="#ffe8b3" stroke="none" d="M13 21Q17 16.5 21 21L21 25L13 25Z"/><path fill="none" stroke="#6b3518" stroke-width=".7" stroke-linecap="round" d="M13.6 20.6Q17 17 20.4 20.6"/></g>
</g>
<ellipse cx="17" cy="20" rx="4" ry="5" fill="none" stroke-width="1"/>
<ellipse class="eye-rim" cx="17" cy="20" rx="4" ry="5" fill="none" stroke-width="1"/>
<g clip-path="url(#eye-r)">
<ellipse cx="31" cy="20" rx="4" ry="5" fill="#fffdf4" stroke="none"/>
<g class="iris"><ellipse cx="31" cy="21" rx="2.7" ry="3.3" fill="#6b3518" stroke="none"/><ellipse cx="31" cy="21.5" rx="1.8" ry="2.5" fill="#170e08" stroke="none"/><circle cx="30.3" cy="19.5" r="1" fill="#fff" stroke="none"/></g>
<rect class="lid" x="27" y="15" width="8" height="10" fill="#ffe8b3" stroke="none"/>
<g class="smile"><path fill="#ffe8b3" stroke="none" d="M27 21Q31 16.5 35 21L35 25L27 25Z"/><path fill="none" stroke="#6b3518" stroke-width=".7" stroke-linecap="round" d="M27.6 20.6Q31 17 34.4 20.6"/></g>
</g>
<ellipse cx="31" cy="20" rx="4" ry="5" fill="none" stroke-width="1"/>
<ellipse class="eye-rim" cx="31" cy="20" rx="4" ry="5" fill="none" stroke-width="1"/>
<g id="glasses" fill="none" stroke="#2a180c" stroke-width="1">
<rect x="12" y="22" width="10" height="6" rx="1.5"/>
<rect x="26" y="22" width="10" height="6" rx="1.5"/>
@@ -259,10 +256,13 @@
</g>
</svg>
<h1>G2Team Potential Updater</h1>
<p class="sub" id="sub">Sign in</p>
<form id="login" novalidate>
<label for="password">Password</label>
<div class="field">
<input id="password" name="password" type="password" autocomplete="current-password"
<input id="password" name="password" type="password" autocomplete="current-password" autofocus
aria-describedby="password-msg caps" required>
<button type="button" class="reveal" id="reveal" aria-label="Show password" aria-pressed="false"></button>
</div>
@@ -300,7 +300,7 @@ function drawSky() {
let seed = 7;
const rand = () => (seed = (seed * 16807) % 2147483647) / 2147483647;
const colors = ["#6ea8ff", "#4fd1c5", "#f29c4b", "#dfe7ff"];
const keepOut = [...document.querySelectorAll("h1, .sub, .card, .owl")].map(e => e.getBoundingClientRect());
const keepOut = [...document.querySelectorAll(".card, .owl")].map(e => e.getBoundingClientRect());
let html = "";
for (let i = 0; i < 60; i++) {
const tw = rand() < .35, x = rand() * innerWidth, y = rand() * innerHeight;
@@ -353,6 +353,7 @@ function setReveal(show) {
pw.type = show ? "text" : "password";
reveal.setAttribute("aria-pressed", String(show));
reveal.setAttribute("aria-label", show ? "Hide password" : "Show password");
owl.classList.toggle("peek", show);
reveal.innerHTML = `<svg viewBox="0 0 24 24" aria-hidden="true">${show ? EYE_OFF : EYE}</svg>`;
}
setReveal(false);
@@ -380,7 +381,7 @@ form.addEventListener("submit", e => {
showError("That password isn't right. Try again.");
pw.focus();
pw.select();
play("shake", 720, followCaret);
play("sad", 1400, followCaret);
return;
}
showError("");
@@ -388,6 +389,7 @@ form.addEventListener("submit", e => {
setReveal(false);
const enter = () => {
form.hidden = true;
document.getElementById("sub").hidden = true;
done.hidden = false;
document.getElementById("done-title").focus();
};
@@ -395,9 +397,12 @@ form.addEventListener("submit", e => {
else play("happy", 750, enter);
});
// Tap the owl and it jumps.
owl.addEventListener("click", () => { if (!reducedMotion.matches) play("jump", 750); });
(function blink() {
setTimeout(() => {
if (!reducedMotion.matches && !owl.classList.contains("happy")) play("blink", 160);
if (!reducedMotion.matches && !owl.classList.contains("happy") && !owl.classList.contains("sad")) play("blink", 160);
blink();
}, 2500 + Math.random() * 3500);
})();