diff --git a/www/index.html b/www/index.html index 7dc1016..d74775b 100644 --- a/www/index.html +++ b/www/index.html @@ -11,28 +11,18 @@ banner - - - - - - - - - + + + + + + - + @@ -287,9 +277,15 @@ function applyLiquidGlass() { // ─── Nav glass ──────────────────────────────────────────────────────────────── -const NAV_TRANSITION_MS = 300; // Must match CSS transition duration +// Phase durations in ms — keep in sync with CSS transitions +const OPEN_WIDTH_MS = 250; // width/radius expand duration +const OPEN_DROPDOWN_MS = 300; // dropdown slide-down duration +const OPEN_DROPDOWN_DELAY = 220; // CSS delay before dropdown opens +const CLOSE_DROPDOWN_MS = 200; // dropdown collapse duration (no CSS delay) +const CLOSE_WIDTH_MS = 250; // width/radius collapse duration +const CLOSE_WIDTH_DELAY = 200; // wait for dropdown to finish before collapsing width -// Build and apply nav glass maps at a specific border-radius value +// Build and apply nav glass maps at a specific border-radius function applyNavGlassAt(radius) { const pill = document.getElementById('nav-pill'); const w = pill.offsetWidth; @@ -326,18 +322,13 @@ function applyNavGlassAt(radius) { } } -// Rebuild maps across the transition so refraction tracks the shape live -function animateNavGlass(startRadius, targetRadius) { - const steps = 12; - const interval = NAV_TRANSITION_MS / steps; - +// Animate refraction maps across a time window +function scheduleNavGlassFrames(startRadius, targetRadius, delayMs, durationMs, steps = 14) { for (let i = 1; i <= steps; i++) { - setTimeout(() => { - const progress = i / steps; - const eased = 1 - Math.pow(1 - progress, 3); // ease-out cubic - const radius = startRadius + (targetRadius - startRadius) * eased; - applyNavGlassAt(radius); - }, i * interval); + const t = i / steps; + const eased = 1 - Math.pow(1 - t, 3); + const radius = startRadius + (targetRadius - startRadius) * eased; + setTimeout(() => applyNavGlassAt(radius), delayMs + t * durationMs); } } @@ -369,14 +360,31 @@ const navBar = document.getElementById('nav-pill-bar'); navBar.addEventListener('click', () => { const opening = !navPill.classList.contains('open'); navPill.classList.toggle('open'); - // 999 = collapsed pill, 22 = expanded rounded rect (must match CSS) - animateNavGlass(opening ? 999 : 22, opening ? 22 : 999); + + if (opening) { + // Phase 1: width expands (0 → OPEN_WIDTH_MS) + scheduleNavGlassFrames(999, 25, 0, OPEN_WIDTH_MS); + // Phase 2: dropdown falls (OPEN_DROPDOWN_DELAY → +OPEN_DROPDOWN_MS) + // refraction height grows as the pill gets taller — track it live + scheduleNavGlassFrames(25, 25, OPEN_DROPDOWN_DELAY, OPEN_DROPDOWN_MS); + // Final settle + setTimeout(applyNavGlass, OPEN_DROPDOWN_DELAY + OPEN_DROPDOWN_MS + 50); + } else { + // Phase 1: dropdown collapses (0 → CLOSE_DROPDOWN_MS) + scheduleNavGlassFrames(25, 25, 0, CLOSE_DROPDOWN_MS); + // Phase 2: width shrinks after dropdown finishes + scheduleNavGlassFrames(25, 999, CLOSE_WIDTH_DELAY, CLOSE_WIDTH_MS); + // Final settle + setTimeout(applyNavGlass, CLOSE_WIDTH_DELAY + CLOSE_WIDTH_MS + 50); + } }); document.addEventListener('click', (e) => { if (!navPill.contains(e.target) && navPill.classList.contains('open')) { navPill.classList.remove('open'); - animateNavGlass(22, 999); + scheduleNavGlassFrames(25, 25, 0, CLOSE_DROPDOWN_MS); + scheduleNavGlassFrames(25, 999, CLOSE_WIDTH_DELAY, CLOSE_WIDTH_MS); + setTimeout(applyNavGlass, CLOSE_WIDTH_DELAY + CLOSE_WIDTH_MS + 50); } }); diff --git a/www/style.css b/www/style.css index b5eab51..8fd3780 100644 --- a/www/style.css +++ b/www/style.css @@ -10,7 +10,7 @@ body { } .banner { - width: 70%; + width: 99%; max-height: 280px; overflow: hidden; margin: 5px 5px 10px 5px; /* 5px on top/left/right, 10px on bottom */ @@ -128,8 +128,8 @@ p { inset 1px 1px 0 rgba(255, 255, 255, 0.45), inset -1px -1px 0 rgba(0, 0, 0, 0.10); overflow: hidden; - transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1), - border-radius 0.3s cubic-bezier(0.4, 0, 0.2, 1); + transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1), + border-radius 0.25s cubic-bezier(0.4, 0, 0.2, 1); } /* ── Pill expands and corners soften when open ── */ @@ -137,7 +137,7 @@ p { border-radius: 22px; width: 200px; } - + /* ── The always-visible top bar: logo + chevron ── */ .nav-pill-bar { display: flex; @@ -148,14 +148,14 @@ p { cursor: pointer; user-select: none; } - + .nav-logo { font-size: 14px; font-weight: 400; color: rgba(255, 255, 255, 0.88); letter-spacing: 0.06em; } - + /* ── Chevron rotates 180° when open ── */ .nav-chevron { width: 16px; @@ -163,45 +163,56 @@ p { flex-shrink: 0; transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); } - + .nav-pill.open .nav-chevron { transform: rotate(180deg); } - + /* ── Dropdown — hidden by default, slides down when open ── */ .nav-dropdown { max-height: 0; opacity: 0; overflow: hidden; - transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1), - opacity 0.2s ease; + /* Opening: wait for width to finish first, then drop down */ + transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1) 0.22s, + opacity 0.25s ease 0.22s; } - + .nav-pill.open .nav-dropdown { max-height: 300px; opacity: 1; } - + +/* Closing: dropdown collapses immediately (no delay), width waits after */ +.nav-pill:not(.open) .nav-dropdown { + transition: max-height 0.2s cubic-bezier(0.4, 0, 0.2, 1), + opacity 0.15s ease; +} + +/* Width/radius collapse waits for dropdown to finish */ +.nav-pill:not(.open) { + transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1) 0.2s, + border-radius 0.25s cubic-bezier(0.4, 0, 0.2, 1) 0.2s; +} + .nav-divider { height: 1px; background: rgba(255, 255, 255, 0.12); margin: 0 14px; } - + .nav-link { display: block; padding: 11px 18px; font-size: 14px; font-weight: 300; - text-align: center; - color: rgba(2, 2, 5, 1); + color: rgba(255, 255, 255, 0.70); text-decoration: none; transition: color 0.15s ease, background 0.15s ease; } - + .nav-link:hover { color: rgba(255, 255, 255, 0.95); background: rgba(255, 255, 255, 0.06); -} - +} \ No newline at end of file