S2 (a1): compliance + approval pack

- /responsible-gambling rebuilt sincerely: 21+, 1-800-GAMBLER primary,
  17-state resource list, warning signs, self-exclusion guidance, links
  to the existing /settings surfaces. No marketing adjacency.
- /terms + /privacy honest drafts with entity placeholders ([ENTITY NAME],
  [STATE OF FORMATION], [ARBITRATION VENUE], [CONTACT EMAIL]); privacy
  sub-processors match reality (adds Sentry, honest PostHog description).
- NEW /methodology: pipeline -> engine -> letter grades, refusals,
  VYNDR Originals, ledger settle + CLV + n>=20, why misses are public.
- /about audited to North Star framing (THE PROOF card, methodology link).
- Footer: 1-800-GAMBLER + Methodology link; compliance audit found zero
  pages suppressing the global footer.
- 5 Ghost seed articles in content/articles/ + docs/GHOST-PUBLISHING.md
  manual runbook (no Ghost access used).
- tests/unit/compliancePages.test.js (repo source-text style). 2429 tests
  green, web build exit 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-11 14:29:31 -04:00
parent e4d2e79f95
commit 4b262fdd65
15 changed files with 908 additions and 76 deletions
+173
View File
@@ -0,0 +1,173 @@
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Methodology',
description:
'How VYNDR grades a prop, what archetypes are, how the Ledger settles, and why the misses are public. Plain language, no hype.',
};
// Session 2 (A1 board) — the approval-pack methodology page. Server component.
// Plain-language description of the real pipeline (feature vector → engine →
// letter grade → locked ledger entry → box-score settle → CLV). Every claim
// here matches the code; if the pipeline changes, change this page in the
// same commit.
const h2: React.CSSProperties = { fontSize: 20, fontWeight: 700, marginBottom: 12, color: 'var(--text-0)' };
const body: React.CSSProperties = { color: 'var(--text-1)', lineHeight: 1.75, fontSize: 15, marginBottom: 12 };
const card: React.CSSProperties = {
background: 'var(--bg-1)',
border: '1px solid var(--border)',
borderRadius: 10,
padding: '16px 18px',
};
function Mono({ children }: { children: React.ReactNode }) {
return <span className="mono" style={{ color: 'var(--text-0)' }}>{children}</span>;
}
export default function MethodologyPage() {
return (
<section style={{ maxWidth: 720, margin: '0 auto', padding: '48px 16px 120px' }}>
<header style={{ marginBottom: 32 }}>
<h1 style={{ fontSize: 36, fontWeight: 700, letterSpacing: '-0.03em', marginBottom: 8, color: 'var(--text-0)' }}>
How the grades work.
</h1>
<p className="mono" style={{ fontSize: 12, color: 'var(--text-2)', letterSpacing: '0.05em' }}>
METHODOLOGY · PLAIN LANGUAGE · NO HYPE
</p>
</header>
<p style={{ ...body, fontSize: 16 }}>
This page describes what actually happens between a sportsbook posting a line and a letter
grade appearing on VYNDR. It is written so a claim made here can be checked against the
Ledger. If a sentence on this page ever stops being true, the page is wrong and we fix it.
</p>
{/* ── The pipeline ─────────────────────────────────────────── */}
<section style={{ marginTop: 36 }}>
<h2 style={h2}>The pipeline</h2>
<p style={body}>
Grades are produced by a scheduled pipeline, not on demand. Several times a day the
system pulls the current prop lines from sportsbook feeds, then builds a feature vector
for each prop — the numbers the model reasons over. Depending on the sport, that includes
recent form (last-5 and last-20 averages, variance), opponent strength against the exact
stat, pace, home or away, rest days, schedule density, batter-versus-pitcher history for
MLB, and per-90 rates with expected-goals context for soccer.
</p>
<p style={body}>
The engine compares the model&apos;s projection to the posted line — in both directions.
It grades the over and the under separately and keeps whichever side it has more
conviction in. The output is an 11-step letter grade, <Mono>F</Mono> through{' '}
<Mono>A+</Mono>, with a confidence score behind it. The letter is a probabilistic
assessment of one side of one line. It is not a prediction and it is never a guarantee.
</p>
<div style={card}>
<p className="mono" style={{ fontSize: 12.5, color: 'var(--text-1)', lineHeight: 1.8, margin: 0 }}>
LINE FEED → FEATURE VECTOR → PROJECTION vs LINE (BOTH SIDES) → LETTER GRADE → LOCKED TO
THE LEDGER → SETTLED vs THE BOX SCORE
</p>
</div>
</section>
{/* ── Refusals ─────────────────────────────────────────────── */}
<section style={{ marginTop: 36 }}>
<h2 style={h2}>When the model refuses</h2>
<p style={body}>
If the pipeline cannot build a real projection for a prop — not enough recent games, no
per-90 baseline, a data source down — the model refuses to grade it. No letter renders,
the prop shows an absent state, and a free read is not consumed. A refusal is not a
failure mode we hide; it is the design. A grade with nothing behind it would still look
like a grade, and that is worse than no grade.
</p>
<p style={body}>
The same rule applies to every number on the platform: market values (lines, odds,
closes) are real book numbers captured at a timestamp, never computed by us. Model values
are labeled as model output. Where data is missing, the surface says less.
Absent beats wrong.
</p>
</section>
{/* ── Archetypes ───────────────────────────────────────────── */}
<section style={{ marginTop: 36 }}>
<h2 style={h2}>Archetypes — the VYNDR Originals</h2>
<p style={body}>
Archetypes are VYNDR&apos;s proprietary classification of how a player produces — 41
originals across NBA, WNBA, MLB, and soccer, built by scoring each player&apos;s real
season statistics against production patterns. A <Mono>TORCH</Mono> is a high-usage,
shot-dependent scorer whose points track his touches. A <Mono>BOMBER</Mono> is a power
bat whose home-run and total-base props behave differently from a contact hitter&apos;s.
A <Mono>GHOST</Mono> beats you with speed. A <Mono>CONDUCTOR</Mono> creates for everyone
else first.
</p>
<p style={body}>
The point is practical, not decorative: different archetypes have different reliable and
volatile props. Points for a TORCH and assists for a CONDUCTOR are stable; the inverse
legs swing with game script. Each classification carries a primary archetype, an optional
secondary, and a weighted blend — because most players are not one thing. The
classification comes from the same stats pipeline that feeds the grades, so an archetype
is re-earned from real numbers, not assigned once and left to rot.
</p>
</section>
{/* ── The Ledger ───────────────────────────────────────────── */}
<section style={{ marginTop: 36 }}>
<h2 style={h2}>How the Ledger settles</h2>
<p style={body}>
Every grade the pipeline produces is written to the Ledger at the moment it is made,
locked with the exact line, the odds, and a timestamp. That entry never changes. If the
line moves hard against a graded side later in the day, the system may re-grade at the
real new line — and when it does, the original letter stays on the record, struck
through, next to the revision. Nothing is revised silently.
</p>
<p style={body}>
After the games end, entries settle against the real box score — the stat the player
actually produced, from league data. Hit, miss, or push, recorded once, idempotently.
Alongside the result we record closing line value: the pipeline captures the closing
line for each graded prop (the last pre-game snapshot of the book&apos;s number) and
measures whether the market moved toward or away from the graded side between our
timestamp and the close. Beating the close consistently is the standard evidence that a
model is finding real edge rather than getting lucky, which is why we track it on every
entry instead of just counting wins.
</p>
<div style={card}>
<p className="mono" style={{ fontSize: 12.5, color: 'var(--text-1)', lineHeight: 1.8, margin: 0 }}>
THE n≥20 RULE — no hit percentage or CLV percentage renders anywhere on VYNDR until at
least 20 grades have settled in that window. Below that, the surface says
RECORD BUILDING. A percentage on a small sample is a marketing number, and we do not publish
marketing numbers.
</p>
</div>
</section>
{/* ── Misses ───────────────────────────────────────────────── */}
<section style={{ marginTop: 36 }}>
<h2 style={h2}>Why the misses are public</h2>
<p style={body}>
Every settled miss stays on the Ledger, by name, in the same format and the same
placement as the wins, permanently. There are two reasons. The honest one: a record you
curate is not a record, and the entire value of this product rests on the record being
real. The practical one: nobody else in this industry can afford to do it, because their
numbers do not survive it. Ours are built to. The model is wrong regularly — the Ledger
shows exactly how regularly, and that transparency is the product working as intended,
not a bug in it.
</p>
</section>
{/* ── What we never do ─────────────────────────────────────── */}
<section style={{ marginTop: 36 }}>
<h2 style={h2}>What we never do</h2>
<ul style={{ display: 'grid', gap: 8, ...body, paddingLeft: 18, listStyle: 'disc' }}>
<li>Fabricate a line, an odd, or a result. Market numbers are captured, never invented.</li>
<li>Ship a grade the model has no projection for.</li>
<li>Render a percentage on fewer than 20 settled grades.</li>
<li>Edit or delete a settled entry, win or miss.</li>
<li>Take a bet. VYNDR is an analytics tool, not a sportsbook.</li>
</ul>
<p style={{ ...body, marginTop: 8 }}>
The record is at <a href="/ledger" style={{ color: 'var(--text-0)' }}>vyndr.app/ledger</a>.
Draw your own conclusion.
</p>
</section>
</section>
);
}