# VYNDR — COMBAT INTELLIGENCE LAYER (spec)
### Wave 6 of the wiring/data train. Net-new sport (MMA/UFC). Governs the combat build. Honest free v1; the full matchup-GRADE engine is a DEFERRED sub-wave. Build toward `specs/design-reference/vyndr-system.html` (FIGHTER A / VERDICT / FIGHTER B, GRAPPLER%/STRIKER% blend, tale-of-the-tape, MONEYLINE/DECISION/SUBMISSION/KO grades). Keep the live wordmark.

## DATA SEMANTICS (unchanged, restated for combat)
VYNDR never generates odds — ML/round-total values are REAL book numbers at a timestamp. Fighter records/physicals/style stats are REAL sourced facts. Model output (style edge, any grade) is always labeled MODEL. `Number(null)===0` is the trap — absent stat ⇒ absent, never 0. **Never ingest a fighter's photo/likeness** (same rule as headshots) — initials monogram only. Scraping fragility ⇒ degrade to absent, never fabricate.

## SCOPE — v1 (this wave) vs DEFERRED
**v1 ships:** fight-card discovery + tale-of-the-tape + style-blend archetypes + ML & round-total odds + a **style-edge VERDICT (a MODEL style read, explicitly NOT a settled grade)**.
**DEFERRED (own sub-wave, do NOT build now):** the matchup-GRADE engine that produces settled ML/method/round grades; method-of-victory & round & fighter props (data-limited on the free feed); combat outcome SETTLEMENT (no free box-score settle path yet — grades would stay `pending`); ufcstats.com scraping (needs a parser dep — CONFIRM before adding).

## DATA SOURCES (zero-out-of-pocket)
- **ESPN MMA (FREE, JSON, no auth — same family VYNDR already uses):**
  - Fight cards / schedule: `site.api.espn.com/apis/site/v2/sports/mma/ufc/scoreboard` (date-pinned like the other sports).
  - Event / fight detail + results + tale-of-the-tape: `.../mma/ufc/summary?event={id}` and the athlete endpoints (`athlete.id`, record, stance, reach, weight class). ESPN athlete id also gives a headshot via the entity layer's `a.espncdn.com/i/headshots/mma/...` pattern — but per the likeness rule, v1 uses initials monograms; wire the id but default to monogram.
  - Depth caveat (honest): ESPN MMA striking/grappling granularity is THINNER than ufcstats. Style-blend is best-effort from what ESPN exposes (finish history, method-of-victory counts, takedown/strike splits where present); when a stat is absent, the blend says less — never invents.
- **The Odds API `mma_mixed_martial_arts` (ALREADY PAID — `ODDS_API_KEY`):** wire `oddsService.SPORT_KEYS.mma = 'mma_mixed_martial_arts'` + `MMA_MARKETS = ['h2h','totals']` (moneyline + round totals). Reuse `oddsNormalizer`. **PropLine has NO combat** → odds-api-only; combat does NOT enter the abundant-props path.

## COMBAT ARCHETYPE REGISTRY (PINNED — both `src/services/archetypeService.js` AND `web/src/lib/archetypes.js` use these EXACT names/colors/glyphs; a test asserts they match, same as other sports). Colors unique WITHIN combat; may reuse hues used in other sports (cross-sport reuse is fine).
Six styles. `classify('mma', fighterStats)` returns `{ primary, secondary|null, blend:[{archetype,weight}] }` (same contract as other sports) scored from finish-rate / method splits / takedown & strike tendencies.

| Name | Glyph | Color | One-liner (shown where it leads) |
|---|---|---|---|
| STRIKER | ✦ | `#E8703A` | Wins on the feet — volume + power at range. |
| GRAPPLER | ⊗ | `#2FA4E7` | Fight hits the mat on his terms — control + subs. |
| PRESSURE | ➤ | `#E4574C` | Forward, relentless, breaks the pace. |
| COUNTER | ◊ | `#8E7BE0` | Patient — punishes what you show him. |
| FINISHER | ▲ | `#12B886` | Ends nights — high KO/SUB rate. |
| GRINDER | ▦ | `#B0883B` | Goes the distance, wins the rounds. |
STRIKER↔GRAPPLER is the primary range axis the mockup renders as the two blend bars; PRESSURE/COUNTER is tempo; FINISHER/GRINDER is the outcome tendency. A fighter is a BLEND (e.g. GRAPPLER 80% / STRIKER 30%). Discipline pedigree tags (Combat Sambo, Dagestan Wrestling, BJJ, Wrestling Base, Kickboxing, Muay Thai, Boxing) are **verifiable credentials**, rendered separately from the archetype blend — VERIFIABLE ONLY, mark unknowns absent, never guess a fighter's base.

## STYLE-MATCHUP VERDICT (v1 — a MODEL read, not a grade)
`styleMatchup(fighterA, fighterB)` → a style-edge verdict (e.g. "GRAPPLER EDGE · Islam") from comparing the two blends + finish/defense tendencies. This is a **descriptive MODEL read**, labeled as such — NOT a settled grade, NOT an edge %, NO fabricated confidence. The mockup's CENTER VERDICT. Keep it honest: if the data is too thin to call, say "STYLES EVEN / INSUFFICIENT READ".

## CONFIG WIRING (the recurring 4-layer-desync trap — do all in sync)
- `src/config/sports.js` `SPORT_CONFIG` + legacy `SPORTS` (flip `mma.active`), `web/src/config/sports.ts` mirror.
- `oddsService.SPORT_KEYS`/`SPORT_MARKETS` + `oddsNormalizer` MMA market keys.
- `src/config/statFilters.js` + `web/src/config/statFilters.ts` — combat stat categories (if any props surface).
- Combat is NOT added to `snapshotService.ACTIVE_SPORTS` in v1 (no settle path) — fight cards + odds + style card render on-read; grades stay OUT of the locked-snapshot loop until the deferred engine.

## FRONTEND (toward the mockup)
- **Tale-of-the-tape / head-to-head style card** — `web/src/components/vyndr/FightCard.tsx`: FIGHTER A (initials monogram, name, record e.g. 26–1, stance, reach) · GRAPPLER%/STRIKER% blend bars · discipline pedigree tags · archetype chip(s) via the shared `ArchetypeBadge` · CENTER VERDICT (style edge) · FIGHTER B mirror · a grades/odds row (MONEYLINE + round total from the odds feed; method/KO/SUB shown as "— data-limited" honest placeholders, NOT fabricated). Two fighters side-by-side (NOT the player-strip row grammar). Mono data, no glitch on data.
- **MMA badge/color** — the `#D4AF37` combat token already exists in `shareCards/tokens.js`; add a frontend MMA sport token + `SportBadge` support.
- **Route** — `web/src/app/fight/[id]/page.tsx` (server wrapper + client card) and/or a combat surface on the schedule; add Next proxies for the ESPN-MMA read (S25 rule). Self-hide / honest empty (reuse `EmptyState`) off-season.
- Sportsbook links via the existing `bookLinks.js`/affiliate layer unchanged.

## TESTS
- `archetypeService`/`archetypes.js` combat colors MATCH (extend the existing cross-file color test).
- `classify('mma', …)` blends correctly from fixtures; thin data → fewer/absent style claims (never fabricated).
- `styleMatchup` → honest "insufficient read" on thin data; a clear edge on divergent styles.
- combat adapter: defensive parse (ESPN shape → normalized fight card; unrecognized → empty, never throw); injectable, NO network.
- `oddsNormalizer` MMA market keys map (h2h/totals), else combat odds silently zero.
- FightCard self-hides / honest-empty; renders tale-of-the-tape mono; method/round shown as data-limited, not fabricated.

## OUT OF SCOPE REMINDER
No settled grades, no method/round/props board, no fighter photos, no scraping dep — all DEFERRED and flagged in-UI as data-limited rather than promised. The cheapest honest v1 is the goal: styles + tape + ML/round-totals, beautifully rendered.
