S6 (a1): display — the full picture under the grammar

- specs/ROW-GRAMMAR.md: the row grammar law (slot order, color law, mark
  law, mobile stacking, no-truncation), locked by tests/unit/rowGrammar.
  StatStrip violations fixed: MovementChip before the grade (market
  context before model output); ViabilityChips after the archetype
  (identity is one contiguous run).
- Line-movement sparklines: intradayRefreshService.trackHistory captures
  real {t,line} points per grade (seeded with the lock, deduped when
  flat, capped 24) inside the snapshot write-back; StatStrip.LineSparkline
  renders at >=3 points (green toward / amber against / dim flat).
- Last-10 dot strips: services/last10Dots (streaksService accessors) ->
  /api/snapshot/:sport attaches last10_dots from rosterlogs:{sport};
  StatStrip.DotStrip renders vs the LOCKED line, newest first.
- CLV distribution: getModelAggregate emits clv_distribution (7 signed
  buckets, outliers clamped) only past the centralized n>=20 gate;
  ledger MODEL header renders the bar strip.
- Global search: SearchModal (cmd-K via GlobalHosts + window.__search),
  players via /api/players/search per sport + static lib/teams.js
  (soccer deliberately absent); Nav search icon + Search first in the
  mobile More sheet. Explore tab untouched.
- Landing LCP: fade-up floored at opacity .6 (hero h1 contentful on
  first frame, S33 visible-floor rule) + IBM Plex Mono preload:false
  (4 decorative font files off the slow-4G critical path).

2654 -> 2698 tests (226 suites) green; web build exit 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-11 20:08:24 -04:00
parent 1d46b446c9
commit d3637e7abd
26 changed files with 1408 additions and 25 deletions
+32
View File
@@ -97,6 +97,38 @@ export default function BottomTabBar() {
</button>
</div>
<div>
{/* S6 (A1 board) — global search, first item in the sheet.
Same modal as ⌘K / the Nav icon (window.__search). */}
<button
type="button"
onClick={() => {
setMoreOpen(false);
if (typeof window !== 'undefined' && window.__search) window.__search();
}}
className="mono"
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
minHeight: 48,
padding: '0 18px',
background: 'transparent',
border: 'none',
cursor: 'pointer',
color: 'var(--g-a)',
fontSize: 13,
fontWeight: 700,
letterSpacing: '0.06em',
textTransform: 'uppercase',
borderBottom: '1px solid var(--border)',
}}
>
Search
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
<circle cx="11" cy="11" r="7" /><path d="M21 21l-4.35-4.35" />
</svg>
</button>
{MORE_ITEMS.map((it) => (
<a
key={it.href}