From 067f0e129a4dbbecc7e08348622c44f4545ce5be Mon Sep 17 00:00:00 2001 From: Jack Date: Tue, 11 Aug 2026 21:09:23 -0500 Subject: [PATCH] Feature set 177. Co-Authored-By: Claude Opus 5 --- src/MoogleAPI.Web/wwwroot/index.html | 46 ++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/MoogleAPI.Web/wwwroot/index.html b/src/MoogleAPI.Web/wwwroot/index.html index 0e9490a..bac001a 100644 --- a/src/MoogleAPI.Web/wwwroot/index.html +++ b/src/MoogleAPI.Web/wwwroot/index.html @@ -171,10 +171,39 @@ width: auto; vertical-align: middle; display: inline-block; - margin: 0 -0.04em; + /* Asymmetric on purpose. h1 has letter-spacing: 0.15em, which is trailing space added after + every character — so the "m" pushes 0.15em of it against the moogle's left side while the + "o" on the right gets none, and an even margin lands the mascot hard up against the "o". + The two values differ by that 0.15em and average out to the old -0.04em, so the wordmark + is the same width as before; only the moogle moved. */ + margin: 0 0.035em 0 -0.115em; -webkit-text-fill-color: initial; transform: rotate(-8deg) translateY(-0.05em); + transform-origin: 50% 65%; filter: drop-shadow(0 2px 6px rgba(20, 22, 26, 0.18)); + transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), filter 0.35s ease; + } + /* Hover is the only hint that the mascot does anything — it is the FFIV-mode toggle, and a + still image in the middle of a wordmark does not read as a button. The gold is the FFIV + accent rather than anything from the silver palette, which makes the hover a small preview + of what clicking does. + + The wobble ends on exactly the transform the hover rule holds, so when the animation drops + off nothing jumps, and the transition takes the lift back down on the way out. Nothing else + may set transform on this element: an !important rule elsewhere outranks the keyframes and + silently flattens the whole thing back to a pop. */ + .moogle-letter:hover, + .moogle-letter:focus-visible { + transform: rotate(-8deg) translateY(-0.12em) scale(1.12); + filter: drop-shadow(0 0 10px rgba(248, 208, 48, 0.9)); + animation: moogle-wobble 0.65s ease-in-out; + } + @keyframes moogle-wobble { + 0% { transform: rotate(-8deg) translateY(-0.05em) scale(1); } + 25% { transform: rotate(6deg) translateY(-0.16em) scale(1.16); } + 50% { transform: rotate(-16deg) translateY(-0.1em) scale(1.12); } + 75% { transform: rotate(-2deg) translateY(-0.14em) scale(1.14); } + 100% { transform: rotate(-8deg) translateY(-0.12em) scale(1.12); } } .hero p { color: var(--muted); @@ -467,11 +496,11 @@ * silver theme owns :root and ff4.css overrides the same tokens on :root.ff-mode, so * components never need to know which mode they are in. */ + /* The hover state itself is up with .moogle-letter, not here — an #id !important rule beat the + wobble keyframes to the transform, and since a running animation suppresses the transition + on the same property, the result was an instant pop to scale with no animation at all. One + owner for that transform now. The gold glow survived the move; only the fight did not. */ #moogle-egg { cursor: pointer; } - #moogle-egg:hover { - filter: drop-shadow(0 0 10px rgba(248, 208, 48, 0.9)) !important; - transform: rotate(-8deg) translateY(-0.05em) scale(1.2) !important; - } #ff-notify { position: fixed; @@ -584,11 +613,16 @@ 100% { transform: translateY(-14%) rotate(2deg); } } - /* The herd still crosses and the theme still changes — it just stops bouncing. */ + /* The herd still crosses and the theme still changes — it just stops bouncing. The moogle + keeps its hover state too, so the mascot still answers the pointer; it just arrives there + instead of wobbling into it. */ @media (prefers-reduced-motion: reduce) { #choco-wipe.run .choco img { animation: none; } #choco-wipe.run #choco-curtain, #choco-wipe.run .choco { animation-timing-function: ease; } + .moogle-letter { transition-duration: 0.01ms; } + .moogle-letter:hover, + .moogle-letter:focus-visible { animation: none; } } .ff-mode h2,