From dd6943c7e8b5db2d113d96ba57d528a50831cec8 Mon Sep 17 00:00:00 2001 From: jwinterm Date: Sat, 6 Jun 2026 21:09:50 -0400 Subject: [PATCH] Mobile nebula: drop the pinned monitor + parallax below md (overlapped hero text) On phones the fixed monitor icon and its scroll parallax had no whitespace to live in, so it drifted over the title/tagline and under the header. Fall back to the static Cherenkov glow under 768px (same path as reduced-motion); desktop keeps the full monitor + streamers. --- packages/ui/src/procedural/nebula-field.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/procedural/nebula-field.tsx b/packages/ui/src/procedural/nebula-field.tsx index 6ae99a0..425eaac 100644 --- a/packages/ui/src/procedural/nebula-field.tsx +++ b/packages/ui/src/procedural/nebula-field.tsx @@ -84,6 +84,9 @@ export const NebulaField = ({ className, density = 8, themeMode = 'dark', positi const monitorControls = useAnimation(); const [monitorColor, setMonitorColor] = useState(themeMode === 'light' ? "text-sky-500" : "text-cyan-400/70"); const [prefersReducedMotion, setPrefersReducedMotion] = useState(false); + const [isMobile, setIsMobile] = useState(() => + typeof window !== 'undefined' && window.matchMedia('(max-width: 767px)').matches, + ); // Parallax Scroll hooks (called unconditionally) const { scrollY } = useScroll(); @@ -108,6 +111,17 @@ export const NebulaField = ({ className, density = 8, themeMode = 'dark', positi return () => mediaQuery.removeEventListener('change', handler); }, []); + // On small screens the monitor and streamers have nowhere to sit without crossing + // the hero text, and the scroll parallax drags them over it. Use the static glow + // there instead. + useEffect(() => { + const mq = window.matchMedia('(max-width: 767px)'); + setIsMobile(mq.matches); + const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches); + mq.addEventListener('change', handler); + return () => mq.removeEventListener('change', handler); + }, []); + // Sync color with theme useEffect(() => { if (!prefersReducedMotion) { @@ -162,8 +176,9 @@ export const NebulaField = ({ className, density = 8, themeMode = 'dark', positi background: `radial-gradient(circle at center, ${glowColor} 0%, transparent 70%)`, } as const; - // Return static background for reduced motion users (still tinted Cherenkov blue, no motion). - if (prefersReducedMotion) { + // Static background for reduced-motion users and small screens (still Cherenkov-tinted, + // with no monitor or streamers to overlap the hero text). + if (prefersReducedMotion || isMobile) { return (