@@ -209,6 +209,12 @@ function LandingPage() {
209209 const [ fetchRequestId , setFetchRequestId ] = useState ( 0 ) ;
210210 const [ showRetryBanner , setShowRetryBanner ] = useState ( false ) ;
211211 const [ finalFetchError , setFinalFetchError ] = useState ( '' ) ;
212+ // Simulated background key-price refresh (#305). A real implementation
213+ // would be driven by a WebSocket or polling hook; here we flip the flag
214+ // on a fixed cadence so the card's loading state is observable until that
215+ // pipeline lands. `prefers-reduced-motion` disables the simulation so we
216+ // don't surface a non-essential animation to users who opted out.
217+ const [ isPriceRefreshing , setIsPriceRefreshing ] = useState ( false ) ;
212218 const [ page , setPage ] = useState ( ( ) => {
213219 if ( typeof window === 'undefined' ) return 0 ;
214220 const saved = window . sessionStorage . getItem ( CREATOR_PAGE_KEY ) ;
@@ -262,6 +268,20 @@ function LandingPage() {
262268 window . scrollTo ( { top : parsed } ) ;
263269 } , [ ] ) ;
264270
271+ useEffect ( ( ) => {
272+ if ( typeof window === 'undefined' ) return ;
273+ const reduceMotion = window . matchMedia (
274+ '(prefers-reduced-motion: reduce)'
275+ ) . matches ;
276+ if ( reduceMotion ) return ;
277+ // Every 30s, simulate an ~800ms in-flight refresh.
278+ const intervalId = window . setInterval ( ( ) => {
279+ setIsPriceRefreshing ( true ) ;
280+ window . setTimeout ( ( ) => setIsPriceRefreshing ( false ) , 800 ) ;
281+ } , 30_000 ) ;
282+ return ( ) => window . clearInterval ( intervalId ) ;
283+ } , [ ] ) ;
284+
265285 useEffect ( ( ) => {
266286 const fetchCreators = async ( ) => {
267287 setIsLoading ( true ) ;
@@ -440,7 +460,11 @@ function LandingPage() {
440460 } ;
441461
442462 return (
443- < main className = "relative min-h-screen overflow-x-hidden bg-[linear-gradient(160deg,#08111f_0%,#10213b_45%,#f0b14d_160%)] px-6 pt-12 pb-28 md:px-12 md:pb-12" >
463+ // #306: the outer wrapper is just a decorative shell; the actual
464+ // landmark structure is a top-level <header> sibling of the <main>
465+ // below, so screen-reader landmark navigation lands directly on the
466+ // marketplace content rather than on the brand banner.
467+ < div className = "relative min-h-screen overflow-x-hidden bg-[linear-gradient(160deg,#08111f_0%,#10213b_45%,#f0b14d_160%)] px-6 pt-12 pb-28 md:px-12 md:pb-12" >
444468 < div className = "absolute left-[-4rem] top-[10%] size-72 rounded-full bg-amber-300/20 blur-[100px]" />
445469 < div className = "absolute bottom-[8%] right-[-3rem] size-72 rounded-full bg-emerald-300/15 blur-[100px]" />
446470 < div className = "absolute inset-0 bg-[radial-gradient(circle_at_top,rgba(255,186,73,0.1),transparent_40%),radial-gradient(circle_at_bottom_left,rgba(74,222,128,0.08),transparent_35%)]" />
@@ -471,9 +495,13 @@ function LandingPage() {
471495 </ div >
472496 </ MarketplaceSection >
473497
474- < SectionDivider title = "Discover creators" spacing = "relaxed" />
498+ < main
499+ id = "creator-marketplace-main"
500+ aria-label = "Creator marketplace"
501+ >
502+ < SectionDivider title = "Discover creators" spacing = "relaxed" />
475503
476- < StickyFilterBar
504+ < StickyFilterBar
477505 eyebrow = "Marketplace filters"
478506 title = "Find creators without losing your place"
479507 description = "Search by creator name or handle while you keep scrolling through the marketplace. The filter shell stays visible and compact so you can refine results without losing your place."
@@ -536,7 +564,7 @@ function LandingPage() {
536564 </ div >
537565 < div className = "grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3 opacity-50" >
538566 { pagedCreators . map ( creator => (
539- < CreatorCard key = { creator . id } creator = { creator } />
567+ < CreatorCard key = { creator . id } creator = { creator } isPriceRefreshing = { isPriceRefreshing } />
540568 ) ) }
541569 </ div >
542570 </ div >
@@ -560,7 +588,7 @@ function LandingPage() {
560588 ) }
561589 < div className = "grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3" >
562590 { pagedCreators . map ( creator => (
563- < CreatorCard key = { creator . id } creator = { creator } />
591+ < CreatorCard key = { creator . id } creator = { creator } isPriceRefreshing = { isPriceRefreshing } />
564592 ) ) }
565593 </ div >
566594 < div className = "mt-8 flex items-center justify-center gap-3" >
@@ -806,6 +834,7 @@ function LandingPage() {
806834 < MarketplaceSection spacing = "relaxed" >
807835 < EmptyTransactionTimelineState />
808836 </ MarketplaceSection >
837+ </ main >
809838 </ div >
810839
811840 < TradeDialog
@@ -826,7 +855,7 @@ function LandingPage() {
826855 description = "Waiting for Stellar confirmation, then refreshing holdings."
827856 />
828857 < ScrollToTop />
829- </ main >
858+ </ div >
830859 ) ;
831860}
832861
0 commit comments