Session 38: Design system Phase G — living layer, i18n/odds, a11y, paywall, parlay math (1890 tests)
VYNDR 2.0 conversion, Phase G (the systems that make the design alive). All 5 wired. Frontend-only; zero backend changes. - lib/parlayMath.js: correlation model (0.62/0.34/0.06/0) + parlayGrade penalty + grade->odds + combined odds (frontend; backend parlayService unchanged). - lib/oddsFormat.js: fmtOdds across american/decimal/fractional/implied with the totals-pass-through rule (safer than the prototype's parseAm, which would mis-convert 228.5) + region presets. - lib/prefs.js: applyPrefs sets <html data-*> (the S33 a11y CSS layer) + load/save. - lib/liveTick.js: single tick engine (SSR/test-safe, no auto-start, fresh state). - lib/checkout.js: checkoutUrl(plan). - LiveLayer (useLive/LiveNumber/HeartbeatBar) under the Nav ticker; GlobalHosts in layout applies prefs + registers __prefs/__goPaywall/__checkout + hosts the Preferences and Paywall modals. Nav read-meter is now a paywall trigger. Gotchas: useEffect can't return a Set.delete unsub directly (boolean != cleanup); header grew to 124px so layout paddingTop + Slate sticky-top updated to match. 18 new tests. Backend 1872 -> 1890, 146 suites, zero regressions. Web build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import Nav from '@/components/Nav';
|
||||
import Footer from '@/components/Footer';
|
||||
import AuthGate from '@/components/AuthGate';
|
||||
import HashRedirect from '@/components/vyndr/HashRedirect';
|
||||
import GlobalHosts from '@/components/vyndr/GlobalHosts';
|
||||
import ParlayTray from '@/components/ParlayTray';
|
||||
import BottomTabBar from '@/components/BottomTabBar';
|
||||
import InstallPrompt from '@/components/InstallPrompt';
|
||||
@@ -128,9 +129,9 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||
<ParlayProvider>
|
||||
<HashRedirect />
|
||||
<Nav />
|
||||
{/* Header = 60px nav + 32px ticker; offset main so content clears it. */}
|
||||
{/* Header = 60px nav + 32px ticker + 30px heartbeat (§8); offset main. */}
|
||||
<AuthGate>
|
||||
<main style={{ paddingTop: 96, minHeight: '100vh', paddingBottom: 80 }}>{children}</main>
|
||||
<main style={{ paddingTop: 124, minHeight: '100vh', paddingBottom: 80 }}>{children}</main>
|
||||
</AuthGate>
|
||||
<Footer />
|
||||
<ParlayTray />
|
||||
@@ -141,6 +142,8 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||
<MFAChallenge />
|
||||
<CookieConsent />
|
||||
<SentryInit />
|
||||
{/* Session 38 — prefs apply + paywall/checkout/prefs globals (§9/§10/§12) */}
|
||||
<GlobalHosts />
|
||||
</ParlayProvider>
|
||||
</ExplainModeProvider>
|
||||
</AuthProvider>
|
||||
|
||||
Reference in New Issue
Block a user