DS5 — Pricing (Desk-as-hero) + Motion + Empty/Error + archetype glyphs
Part 6 #8 — Desk $44.99 is the hero tier. New DeskShowcase leads the pricing page with the "$1M terminal · $44.99" story + real feature visuals (alt-line ladder, quarter-Kelly, parlay φ, real-time feed) in a balanced two-column layout (kills the dead right-half). Desk is the sole highlighted tier / single primary CTA (color contract #9); Analyst is secondary. Real prices: Free 5 scans, Analyst $14.99/$19.99, Desk $34.99/$44.99. ClaimMeter + Stripe checkout wiring untouched. Part 4 #7 — Ticker → punctuated stillness. The continuous marquee is retired; the ticker now RESTS ≥4s on each ranked item and pulses only on change. The EKG heartbeat is a static readout — the header's ONE idle proof-of-life is the single SIGNAL-LIVE live-dot (the ticker dropped its competing pulse). All durations tokenized (--motion-*, --ticker-hold); prefers-reduced-motion kills the motion entirely. Part 8 #20 — new EmptyState component modeled on the north-star 404 (scanlines + glitch wordmark + amber system voice + CTA hierarchy), reused at the bare-red "Team not found", "Game not found", and the ledger empties — one unified voice. Part 5 — archetype glyph+chip propagated to STREAKS rows + ledger rows (optional + self-hiding; absent beats fabricated); grade reveal already carries ArchetypeBlend. Tests: new tests/unit/ds5PricingStates.test.js (22) locks all four workstreams; updated teamHubUI + vyndrDesignSystem for the new surfaces. 237 suites / 2864 tests green; next build exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+28
-23
@@ -3,7 +3,7 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { GradePill } from '@/components/GradeCard';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
import { Skeleton } from '@/components/vyndr';
|
||||
import { Skeleton, EmptyState, ArchetypeBadge } from '@/components/vyndr';
|
||||
import { clvMode, CLV_FLAT_LINE } from '@/lib/clvDisplay';
|
||||
|
||||
/**
|
||||
@@ -44,6 +44,9 @@ interface LedgerRow {
|
||||
outcome?: 'hit' | 'miss' | 'push' | null;
|
||||
actual_value?: number | null;
|
||||
revised_from_grade?: string | null;
|
||||
// DS5 (Part 5) — the player's locked archetype, when the pipeline supplies it.
|
||||
// Optional + self-hiding: absent beats fabricated.
|
||||
archetype?: string | null;
|
||||
}
|
||||
|
||||
interface TierRecord { settled: number; hits: number; misses: number; hit_pct: number | null }
|
||||
@@ -352,6 +355,12 @@ function LedgerCard({ row, index }: { row: LedgerRow; index: number }) {
|
||||
</span>
|
||||
</div>
|
||||
<h3 style={{ fontSize: 15, fontWeight: 600, marginBottom: 2 }}>{row.player_name}</h3>
|
||||
{/* Part 5 — the archetype glyph+chip, propagated (self-hides when absent). */}
|
||||
{row.archetype && (
|
||||
<div style={{ marginBottom: 6 }}>
|
||||
<ArchetypeBadge archetype={row.archetype} size="sm" showDesc />
|
||||
</div>
|
||||
)}
|
||||
<p className="mono" style={{ fontSize: 12, color: 'var(--text-secondary)', textTransform: 'capitalize', marginBottom: 4 }}>
|
||||
{row.side} {row.line} {row.stat.replace(/_/g, ' ')}
|
||||
</p>
|
||||
@@ -369,28 +378,24 @@ function LedgerCard({ row, index }: { row: LedgerRow; index: number }) {
|
||||
}
|
||||
|
||||
function EmptyLedger({ tab }: { tab: Tab }) {
|
||||
return (
|
||||
<div className="surface diagonal-cut tex-scan" style={{ padding: 48, textAlign: 'center', display: 'grid', gap: 10, justifyItems: 'center' }}>
|
||||
<p className="lbl" style={{ color: 'var(--grade-c)' }}>LEDGER EMPTY</p>
|
||||
{tab === 'mine' ? (
|
||||
<>
|
||||
<h3 style={{ fontSize: 18, fontWeight: 700 }}>No reads yet.</h3>
|
||||
<p style={{ color: 'var(--text-1)', fontSize: 14, maxWidth: 440 }}>
|
||||
Read your first prop to start building your Ledger. Every grade you run lands here the moment it completes — and settles against the real result.
|
||||
</p>
|
||||
<a href="/scan" className="btn-primary" style={{ marginTop: 8, padding: '10px 18px' }}>
|
||||
Read a Prop →
|
||||
</a>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h3 style={{ fontSize: 18, fontWeight: 700 }}>The model record starts with the next pipeline run.</h3>
|
||||
<p style={{ color: 'var(--text-1)', fontSize: 14, maxWidth: 440 }}>
|
||||
Every pre-graded prop lands here — hits, misses, pushes, and closing-line value. Nothing is deleted.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
// DS5 (#20) — the ONE unified empty surface, inline variant.
|
||||
return tab === 'mine' ? (
|
||||
<EmptyState
|
||||
inline
|
||||
wordmark={false}
|
||||
code="LEDGER EMPTY"
|
||||
title="No reads yet."
|
||||
message="Read your first prop to start building your Ledger. Every grade lands here the moment it completes — and settles against the real result."
|
||||
actions={[{ label: 'Read a Prop →', href: '/scan', primary: true }]}
|
||||
/>
|
||||
) : (
|
||||
<EmptyState
|
||||
inline
|
||||
wordmark={false}
|
||||
code="MODEL RECORD BUILDING"
|
||||
title="The record starts with the next pipeline run."
|
||||
message="Every pre-graded prop lands here — hits, misses, pushes, and closing-line value. Nothing is deleted."
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user