diff --git a/frontend/src/index.css b/frontend/src/index.css index a0b67bd..baf8fff 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -288,6 +288,18 @@ button:disabled { opacity: 0.45; cursor: default; transform: none; box-shadow: n .play-page { display: flex; flex-direction: column; min-height: calc(100vh - 56px); } +/* Keep the Plot/Memory/Scripts/Insights toggles reachable while the story + scrolls — pin the header just below the 56px top nav. */ +.play-page .page-header { + position: sticky; + top: 56px; + z-index: 5; + background: var(--bg); + margin-bottom: 12px; + padding: 12px 0; + border-bottom: 1px solid var(--border); +} + .story { flex: 1; font-family: var(--font-story); diff --git a/frontend/src/pages/Play.jsx b/frontend/src/pages/Play.jsx index 61f4100..4af09b5 100644 --- a/frontend/src/pages/Play.jsx +++ b/frontend/src/pages/Play.jsx @@ -444,8 +444,12 @@ export default function Play() { }, []) useEffect(() => { + // Snap to the real document bottom (below the sticky composer), not to + // storyEndRef — that ref sits above the composer, so block:'end' would + // stop short and fight a reader scrolling down. Instant, not smooth: at + // streaming speed a queued smooth animation never settles. if (pinnedRef.current) { - storyEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' }) + window.scrollTo({ top: document.documentElement.scrollHeight }) } }, [actions, streaming, reasoningStream])