diff --git a/tests/unit/vyndrParityQA.test.js b/tests/unit/vyndrParityQA.test.js index 33574e1..4a2e25e 100644 --- a/tests/unit/vyndrParityQA.test.js +++ b/tests/unit/vyndrParityQA.test.js @@ -265,3 +265,19 @@ describe('P0-4 — mobile app bar is one line (ticker + heartbeat hidden)', () = expect(css).toMatch(/\.slate-sticky-head\s*\{\s*top:\s*60px/); }); }); + +// ── P1-5 lock — 390px containment (strengthened: content, not just doc scroll) ─ +describe('P1-5 — dense rows CONTAIN at 390px (not just html overflow-x:hidden)', () => { + const css = read('app/globals.css'); + it('consensus rows stack at <430px so they never bleed off the right edge', () => { + expect(css).toMatch(/@media\s*\(max-width:\s*430px\)\s*\{[^]*?\.breadth-row\s*\{[^}]*flex-direction:\s*column/); + expect(css).toMatch(/\.breadth-row\s*>\s*span\s*\{[^}]*overflow-wrap:\s*anywhere/); + expect(read('components/vyndr/MarketBreadth.tsx')).toMatch(/breadth-row/); + }); + it('the starting-pitcher line wraps its name/ERA inside the card (no truncation)', () => { + const gc = read('components/vyndr/GameCard.tsx'); + // the inner pitcher spans allow wrap + shrink so "2.64 ERA" never gets cut + expect(gc).toMatch(/g\.pitchers\.away\.era/); + expect((gc.match(/flexWrap: 'wrap', minWidth: 0/g) || []).length).toBeGreaterThanOrEqual(2); + }); +}); diff --git a/web/src/app/globals.css b/web/src/app/globals.css index 160661e..c507a3e 100644 --- a/web/src/app/globals.css +++ b/web/src/app/globals.css @@ -1372,6 +1372,20 @@ html[data-font="readable"] .wm::after { opacity: 0.45 !important; } } + +/* P1-5 — 390px CONTAINMENT: dense data rows must WRAP/STACK inside their card, + never bleed off the right edge (the M4 no-overflow lock only hid the document + scroll; content still clipped). */ +@media (max-width: 430px) { + /* CONSENSUS VS MODEL rows stack — player / consensus / model each own line. */ + .breadth-row { flex-direction: column; align-items: flex-start; gap: 3px; } + .breadth-row > span { min-width: 0 !important; max-width: 100%; overflow-wrap: anywhere; } +} +@media (max-width: 767px) { + /* opt-in scroll-within-card for wide tables (pitcher lines / pill rows). */ + .mobile-scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; } +} + /* P0-4 — MOBILE APP BAR: collapse the 3-row header (nav + ticker + heartbeat) to ONE line <768px. The ticker row + the whole heartbeat bar are hidden; the nav's right cluster shows the app-bar clock (nav-mobile-clock). main's top diff --git a/web/src/components/vyndr/GameCard.tsx b/web/src/components/vyndr/GameCard.tsx index 2c8d4b8..698c7e7 100644 --- a/web/src/components/vyndr/GameCard.tsx +++ b/web/src/components/vyndr/GameCard.tsx @@ -263,12 +263,12 @@ export default function GameCard({ game: g, onAddParlay, onOpen, preferredBooks {g.pitchers && (
STARTING - + {g.pitchers.away.name} {g.pitchers.away.era} ERA {g.pitchers.away.archetype && } vs - + {g.pitchers.home.name} {g.pitchers.home.era} ERA {g.pitchers.home.archetype && } diff --git a/web/src/components/vyndr/MarketBreadth.tsx b/web/src/components/vyndr/MarketBreadth.tsx index 598b47f..0240789 100644 --- a/web/src/components/vyndr/MarketBreadth.tsx +++ b/web/src/components/vyndr/MarketBreadth.tsx @@ -87,8 +87,8 @@ export default function MarketBreadth({ return (
{r.player && {r.player}}