diff --git a/demo/index.html b/demo/index.html
index 536a11901..c424073dc 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -386,6 +386,11 @@
.js-story .story-intro > div { position: sticky; top: 0; height: 100svh; display: flex; flex-direction: column; justify-content: center; align-items: flex-start; max-width: 58rem; margin: 0 auto; padding: 5rem 1.5rem 2rem; }
.js-story .story-pin { height: 420vh; }
.js-story .story-stage { position: sticky; top: 0; height: 100svh; min-height: 0; padding-top: 5.5rem; padding-bottom: 2rem; }
+ .story-hint { display: none; }
+ .js-story .story-hint { display: flex; align-items: center; gap: 0.5rem; margin: 2rem 0 0; font: 500 0.8rem/1 'JetBrains Mono', monospace; letter-spacing: 0.08em; text-transform: uppercase; color: rgb(var(--c-slate-400)); transition: opacity 0.4s; }
+ .js-story .story-hint.gone { opacity: 0; pointer-events: none; }
+ .story-hint span { display: inline-block; animation: story-nudge 1.6s ease-in-out infinite; }
+ @keyframes story-nudge { 0%, 100% { transform: translate3d(0, 0, 0); } 50% { transform: translate3d(0, 5px, 0); } }
.js-story .story-words .w { color: rgb(var(--c-fg) / 0.16); }
.js-story .story-words .w.lit { color: rgb(var(--c-fg)); }
.js-story .story-verdict span { position: absolute; left: 0; top: 0; opacity: 0; transform: translate3d(0, 24px, 0); transition: opacity 0.5s, transform 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
@@ -561,6 +566,7 @@
Independent verification
An agent says it backed up the database. Joltrin checks the record, not the claim.
+
Scroll down to continue ↓
@@ -2853,9 +2859,11 @@ ${res.title}
var pin = story.querySelector('.story-pin');
var rows = story.querySelectorAll('.story-row');
var tagged = story.querySelectorAll('[data-for]');
+ var hint = document.getElementById('story-hint');
var step = -1, ticking = false;
function frame() {
ticking = false;
+ if (hint) hint.classList.toggle('gone', window.scrollY > 40);
var vh = window.innerHeight;
// Words: lit across the pinned intro track.
var r = intro.getBoundingClientRect();
diff --git a/tests/homepage.spec.ts b/tests/homepage.spec.ts
index 5deca52a4..5e9eb6b88 100644
--- a/tests/homepage.spec.ts
+++ b/tests/homepage.spec.ts
@@ -287,6 +287,17 @@ test.describe('Homepage', () => {
await expect(page.locator('.story-verdict .v-block')).not.toHaveClass(/show/);
});
+ test('a scroll hint shows at the top and goes away once the visitor scrolls', async ({ page }) => {
+ await page.goto('/', { waitUntil: 'domcontentloaded' });
+ const hint = page.locator('#story-hint');
+ await expect(hint).toBeVisible();
+ await expect(hint).toContainText('Scroll down to continue');
+ await page.evaluate(() => window.scrollTo(0, 200));
+ await expect(hint).toHaveClass(/gone/);
+ await page.evaluate(() => window.scrollTo(0, 0));
+ await expect(hint).not.toHaveClass(/gone/);
+ });
+
test('reduced motion renders the finished story with no scripted pinning', async ({ page }) => {
await page.emulateMedia({ reducedMotion: 'reduce' });
await page.goto('/', { waitUntil: 'domcontentloaded' });
@@ -297,6 +308,7 @@ test.describe('Homepage', () => {
}
const position = await page.locator('.story-stage').evaluate((el) => getComputedStyle(el).position);
expect(position).not.toBe('sticky');
+ await expect(page.locator('#story-hint')).toBeHidden();
});
});