From b627126713a160eaf16d7415cd30c1dbefe7e1c3 Mon Sep 17 00:00:00 2001 From: R-Bharathraj Date: Wed, 15 Jul 2026 15:13:10 +0530 Subject: [PATCH] updated mobile widget --- src/components/Header.tsx | 21 +++-- .../for-people/SpendAnywhereSection.tsx | 77 +++++++++++++++++-- src/components/home/TheLoop.tsx | 21 +++-- 3 files changed, 89 insertions(+), 30 deletions(-) diff --git a/src/components/Header.tsx b/src/components/Header.tsx index deb3ee6..e151c7d 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useEffect, useRef, useSyncExternalStore } from 'react'; +import { useState, useEffect, useRef } from 'react'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; import Image from 'next/image'; @@ -15,15 +15,6 @@ const NAV_LINKS = [ ]; const MOBILE_QUERY = '(max-width: 1023px)'; -const subscribeMobileQuery = (callback: () => void) => { - const mql = window.matchMedia(MOBILE_QUERY); - mql.addEventListener('change', callback); - return () => mql.removeEventListener('change', callback); -}; -const getMobileSnapshot = () => window.matchMedia(MOBILE_QUERY).matches; -// Server has no viewport, so it always renders the desktop header; the client's -// first render must match that to avoid a hydration mismatch. -const getMobileServerSnapshot = () => false; export default function Header() { const pathname = usePathname(); @@ -87,7 +78,15 @@ export default function Header() { href === '/' ? pathname === '/' : pathname.startsWith(href); // Mobile keeps the original plain white header — the transparent hero overlay is desktop-only. - const isMobileViewport = useSyncExternalStore(subscribeMobileQuery, getMobileSnapshot, getMobileServerSnapshot); + const [isMobileViewport, setIsMobileViewport] = useState(false); + + useEffect(() => { + const mql = window.matchMedia(MOBILE_QUERY); + setIsMobileViewport(mql.matches); + const update = () => setIsMobileViewport(mql.matches); + mql.addEventListener('change', update); + return () => mql.removeEventListener('change', update); + }, []); // Every page's hero is built with top clearance for an overlaid header, so let it // show through transparently until the user scrolls (desktop only). diff --git a/src/components/for-people/SpendAnywhereSection.tsx b/src/components/for-people/SpendAnywhereSection.tsx index d54f6b2..1a60719 100644 --- a/src/components/for-people/SpendAnywhereSection.tsx +++ b/src/components/for-people/SpendAnywhereSection.tsx @@ -3,7 +3,6 @@ import React, { useEffect, useLayoutEffect, useRef } from 'react'; import { gsap } from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger'; -import { PhoneMockupImage } from './PhoneMockup'; if (typeof window !== 'undefined') gsap.registerPlugin(ScrollTrigger); @@ -77,14 +76,76 @@ export default function SpendAnywhereSection() { - {/* Right — phone mockup: where to spend */} + {/* Right — live LytsUp Spend preview embedded in a real phone frame */}
- +
+ {/* Screen — locked to a real phone's aspect ratio (~9:19.5) */} +
+ {/* Dynamic island */} +
+ {/* Safe area — reserves the notch strip above the app's top bar */} +
+ {/* The embed is a responsive Flutter web app. We render it at a + comfortable logical width (390px) and scale it down to fill the + screen exactly, so the UI stays proportioned as designed instead + of cramped at ~260px. The iframe width is exactly the design + width (no horizontal overscan) so the app lays out edge-to-edge + and stays centered — nothing on the right (e.g. the Profile tab) + gets clipped. Flutter's thin right-edge list scrollbar is hidden + by a slim mask strip rather than by cutting into the layout. */} +
+