From 42e78d0d664291d6e2560f65ba44645174dc30f9 Mon Sep 17 00:00:00 2001 From: Andrew Moore Date: Tue, 11 Aug 2026 19:26:28 -0700 Subject: [PATCH] Merge nucleic/calm-willow-ibis-dozm into main Mirrored from corpo 1b3dc81. --- src/pages/index.astro | 54 ++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/src/pages/index.astro b/src/pages/index.astro index d0025ae..134ca36 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -872,12 +872,13 @@ const canonical = new URL('/', Astro.site); product of how many, not how much scroll each one happens to cost. */ grid-auto-rows: 1fr; gap: 0.45rem; - /* The card's own top rule continues across this column, and the marks hang from - it — so the composition keeps the unbroken right edge every other rule on the - page is ruled to, and the track reads as part of it rather than as a widget - parked alongside. */ - border-top: 1px solid var(--line); - padding-top: 0.6rem; + /* No rule of its own across this column. The card's rule belongs to the card and + travels with it as one card hands over to the next, so a fixed length of the + same hairline sitting beside it stays behind and breaks the line in two for the + length of the transition. The rule stops at the track instead, which also keeps + it clear of the marks when it moves. The pixel is the rule's, given back as + padding so the marks do not shift. */ + padding-top: calc(0.6rem + 1px); } .is-scroll-stack .technology-track { @@ -1537,14 +1538,23 @@ const canonical = new URL('/', Astro.site); already somewhere else. So a step entered at fling speed is entered slowly. The rule the whole thing reduces to: a gesture may cross any boundary it reaches at reading pace, and the moment it crosses one at fling speed, what is - left of that fling is held to a sixth of its weight. One flick, one product; - a reader who keeps pushing still gets through, just against the brakes. */ - const CATCH_STRENGTH = 0.85; + left of that fling is held to a fifteenth of its weight. One flick, one + product; a reader who keeps pushing still gets through, just against the + brakes. */ + const CATCH_STRENGTH = 0.95; + /* The stack answers to a shorter gesture than the page it sits in — three + products across nine hundred pixels means a careful two-finger drag spends + itself inside one of them, which reads as a page that has not noticed. So a + delta is worth a third again as much at reading pace, and that extra is given + back as the gesture speeds up, reaching nothing at fling speed. The one curve + answers both halves of the complaint: slow gestures cover more ground, fast + ones cover less, and it is the same number saying so. */ + const SLOW_GAIN = 1.35; /* Below the first, a gesture is reading and is never touched; above the second it is a fling. Both are per-frame, so they are speeds a hand can feel — about - 720px and 1800px a second — rather than counts of an event nobody controls. */ - const SLOW_FRAME_PX = 12; - const FAST_FRAME_PX = 30; + 840px and 2040px a second — rather than counts of an event nobody controls. */ + const SLOW_FRAME_PX = 14; + const FAST_FRAME_PX = 34; const GESTURE_GAP_MS = 140; let wheelAt = -Infinity; @@ -1556,15 +1566,20 @@ const canonical = new URL('/', Astro.site); the gesture is still free. */ let gestureCatch = 0; - const beatDrag = () => { + /* Nought at reading pace, one at fling speed. Everything below reads the gesture + through this single number: how much a delta is worth, and how hard a boundary + pushes back once one has been crossed at speed. */ + const wheelFast = () => + Math.min( + 1, + Math.max(0, (wheelSpeed - SLOW_FRAME_PX) / (FAST_FRAME_PX - SLOW_FRAME_PX)), + ); + + const beatDrag = (fast: number) => { const beat = technologyAtScroll(); if (beat === gestureBeat) return 1; if (!gestureCatch) { - const fast = Math.min( - 1, - Math.max(0, (wheelSpeed - SLOW_FRAME_PX) / (FAST_FRAME_PX - SLOW_FRAME_PX)), - ); /* Reading pace: the gesture is entitled to the beat it has reached, and to the next one it reaches the same way. Only speed is ever held back. */ if (fast <= 0) { @@ -1609,7 +1624,10 @@ const canonical = new URL('/', Astro.site); } wheelAt = now; - scrollRoot.scrollBy({ top: pixels * beatDrag(), behavior: 'auto' }); + const fast = wheelFast(); + const gain = 1 + (SLOW_GAIN - 1) * (1 - fast); + + scrollRoot.scrollBy({ top: pixels * gain * beatDrag(fast), behavior: 'auto' }); }, { capture: true, passive: false }, );