- Fix VideoAdPlayer CSS conflict and YouTube API race condition
- Replace padding-bottom with aspect-video on homepage
- Add real AdSense slot IDs to all ad placements (4266907782 banner, 2044808434 in-article, 5341055619 PIP)
- AdFrame uses adSenseSlot from config instead of logical slot name
- AdFrame shows network name badge and 3 dot indicators for rotation
- Add InArticleAd component injecting ads every 4 paragraphs in blog posts
- Navigation ad: remove skip button, 5s countdown before Continue clickable
- Navigation ad: show on every page navigation (no counter)
- Navigation ad: square 400x400 ad area using PIP slot 5341055619
- Add VideoAdOverlay (VAST parser + ad player overlay)
- Add VideoAdPlayer (YouTube IFrame API wrapper with VAST ad integration)
- Add NavigationAdOverlay (consent-style interstitial ad on page navigation)
- Add NavigationAdController (route change interceptor with sessionStorage counter)
- Replace iframe embeds with VideoAdPlayer on all 6 video pages
- Add VAST ad overlay to PIP player with play/pause on ad start/end
- Add shared YouTube types (lib/types/youtube.ts) to avoid duplicate declarations
- Add videoAds + navigationAd config sections to config.json
- Add useVideoAdsConfig + useNavigationAdConfig hooks
- Refactor AdFrame: useMemo for enabledNetworks, string key for useEffect, AdsbygoogleWindow type
- All pages build and lint clean
- Wrap enabledNetworks in useMemo to avoid new array reference every render
- Use string key for useEffect dependency instead of array reference
- Remove slides from loadAd dependency array
- Remove stale GitHub Pages workflow (Vercel handles deploys)
- Add dev-server.log to .gitignore
- RuntimeConfigProvider fetches config.json from GitHub raw every 60s
- Components use useConfig() hook for live updates (no rebuild needed)
- Download section reads installer/bundle URLs from runtime config
- Hero download buttons use runtime URLs
- PIPVideoPlayer now embeds YouTube iframe with autoplay
- Falls back to GIF thumbnail when no YouTube URL
- Removed close/minimize buttons — player is persistent
- AdSlot reads publisher ID from runtime config
- useRuntimeConfig hook (TSX for JSX support)
- config.json at repo root as single source of truth
- TypeScript configs now import from config.json at build time
- AdSlot component with Carbon Ads + affiliate links integration
- 5 config-driven ad placements enabled
- Improved 3D DeviceMockup: screen texture with rendered UI,
detailed keyboard, ambient orbs, glass overlay
- Generated PNG images: OG image (1200x630), 5 screenshots
(welcome, system-check, install-progress, play-store, complete)
- Image generation script (scripts/generate-images.py)