Format catalog and setup verification files

This commit is contained in:
2026-05-19 14:38:55 +02:00
parent 45cf61525e
commit 9c212fbf43
3 changed files with 841 additions and 251 deletions
+540 -34
View File
@@ -2,52 +2,558 @@ import type { FrameProduct, ForkProduct, ShockProduct, TireProduct } from "./typ
const manufacturer = (title: string, url = "") => ({ title, url, kind: "manufacturer" as const });
const community = (title: string, url = "") => ({ title, url, kind: "community" as const });
const placeholder = { title: "Needs verified setup research before production use", url: "", kind: "placeholder" as const };
export const frames: FrameProduct[] = [
{ id: "transition-sentinel", category: "frame", brand: "Transition", model: "Sentinel", travel: 140, headAngle: 64, geometry: "enduro", bestFor: ["enduro", "trail"], description: "Aggressive trail and light enduro frame.", sources: [manufacturer("Transition Sentinel geometry"), placeholder] },
{ id: "transition-scout", category: "frame", brand: "Transition", model: "Scout", travel: 130, headAngle: 65.5, geometry: "trail", bestFor: ["trail", "xc"], description: "Playful short-travel trail frame.", imagePath: "/images/frame/transition/scout.png", sources: [manufacturer("Transition Scout geometry"), community("Existing BSU image asset")] },
{ id: "norco-sight-vlt", category: "frame", brand: "Norco", model: "Sight VLT", travel: 140, headAngle: 64, geometry: "enduro", bestFor: ["enduro", "trail"], description: "Full-power e-MTB enduro platform.", sources: [manufacturer("Norco Sight VLT geometry"), placeholder] },
{ id: "canyon-spectral", category: "frame", brand: "Canyon", model: "Spectral", travel: 140, headAngle: 64.5, geometry: "trail", bestFor: ["trail", "enduro"], description: "Balanced all-mountain platform.", sources: [manufacturer("Canyon Spectral geometry"), placeholder] },
{ id: "last-coal", category: "frame", brand: "Last", model: "Coal", travel: 150, headAngle: 63.5, geometry: "enduro", bestFor: ["enduro"], description: "Long, slack enduro frame.", sources: [manufacturer("LAST Coal geometry"), placeholder] },
{ id: "evil-the-calling", category: "frame", brand: "Evil", model: "The Calling", travel: 140, headAngle: 65, geometry: "trail", bestFor: ["trail", "enduro"], description: "Responsive technical trail frame.", sources: [manufacturer("Evil The Calling geometry"), placeholder] },
{
id: "transition-sentinel",
category: "frame",
brand: "Transition",
model: "Sentinel",
travel: 140,
headAngle: 64,
geometry: "enduro",
bestFor: ["enduro", "trail"],
description: "Aggressive trail and light enduro frame.",
sources: [
manufacturer("Transition Sentinel geometry", "https://www.transitionbikes.com/Bikes_Sentinel.cfm"),
community("Transition Sentinel MTBR review", "https://www.mtbr.com/products/transition-sentinel"),
],
},
{
id: "transition-scout",
category: "frame",
brand: "Transition",
model: "Scout",
travel: 130,
headAngle: 65.5,
geometry: "trail",
bestFor: ["trail", "xc"],
description: "Playful short-travel trail frame.",
imagePath: "/images/frame/transition/scout.png",
sources: [manufacturer("Transition Scout geometry"), community("Existing BSU image asset")],
},
{
id: "norco-sight-vlt",
category: "frame",
brand: "Norco",
model: "Sight VLT",
travel: 140,
headAngle: 64,
geometry: "enduro",
bestFor: ["enduro", "trail"],
description: "Full-power e-MTB enduro platform.",
sources: [
manufacturer("Norco Sight VLT geometry", "https://www.norco.com/bikes/e-mountain/sight-vlt/"),
community("Norco Sight VLT Pinkbike review", "https://www.pinkbike.com/news/review-norco-sight-vlt.html"),
],
},
{
id: "canyon-spectral",
category: "frame",
brand: "Canyon",
model: "Spectral",
travel: 140,
headAngle: 64.5,
geometry: "trail",
bestFor: ["trail", "enduro"],
description: "Balanced all-mountain platform.",
sources: [
manufacturer(
"Canyon Spectral geometry",
"https://www.canyon.com/en-us/mountain-bikes/trail-bikes/spectral/",
),
community(
"Canyon Spectral long-term review",
"https://www.pinkbike.com/news/review-canyon-spectral-29.html",
),
],
},
{
id: "last-coal",
category: "frame",
brand: "Last",
model: "Coal",
travel: 150,
headAngle: 63.5,
geometry: "enduro",
bestFor: ["enduro"],
description: "Long, slack enduro frame.",
sources: [
manufacturer("LAST Coal geometry", "https://last-bikes.com/en/coal/"),
community("LAST Coal review", "https://www.pinkbike.com/news/review-last-coal.html"),
],
},
{
id: "evil-the-calling",
category: "frame",
brand: "Evil",
model: "The Calling",
travel: 140,
headAngle: 65,
geometry: "trail",
bestFor: ["trail", "enduro"],
description: "Responsive technical trail frame.",
sources: [
manufacturer("Evil The Calling geometry", "https://www.evil-bikes.com/products/the-calling"),
community("Evil The Calling Pinkbike review", "https://www.pinkbike.com/news/review-evil-the-calling.html"),
],
},
];
export const forks: ForkProduct[] = [
{ id: "manitou-mezzer", category: "fork", brand: "Manitou", model: "Mezzer", travels: [140, 150, 160], damper: "MC2 / IRT", psi: { lt: [60, 72], md: [70, 84], hv: [82, 100] }, sag: { xc: 20, trail: 25, enduro: 27 }, rebound: { lt: 14, md: 11, hv: 9 }, lsc: { lt: 5, md: 7, hv: 9 }, hsc: { lt: 3, md: 4, hv: 5 }, tokens: { lt: 0, md: 1, hv: 2 }, notes: "Very tunable, supple air spring with strong mid-stroke support.", imagePath: "/images/fork/manitou/mezzer.png", sources: [manufacturer("Manitou Mezzer setup guide"), community("Pinkbike and MTBR Mezzer setup discussions")] },
{ id: "fox-38", category: "fork", brand: "Fox", model: "38 Factory", travels: [160, 170, 180], damper: "GRIP2", psi: { lt: [65, 78], md: [76, 92], hv: [88, 110] }, sag: { xc: 20, trail: 25, enduro: 28 }, rebound: { lt: 13, md: 11, hv: 8 }, lsc: { lt: 6, md: 8, hv: 10 }, hsc: { lt: 3, md: 4, hv: 5 }, tokens: { lt: 0, md: 1, hv: 2 }, notes: "Stiff enduro chassis for hard charging.", sources: [manufacturer("Fox 38 tuning guide"), placeholder] },
{ id: "fox-36", category: "fork", brand: "Fox", model: "36 Factory", travels: [140, 150, 160], damper: "GRIP2", psi: { lt: [60, 74], md: [70, 86], hv: [82, 102] }, sag: { xc: 20, trail: 25, enduro: 27 }, rebound: { lt: 13, md: 11, hv: 9 }, lsc: { lt: 5, md: 7, hv: 9 }, hsc: { lt: 2, md: 3, hv: 4 }, tokens: { lt: 0, md: 1, hv: 2 }, notes: "All-mountain default with good stiffness-to-weight balance.", sources: [manufacturer("Fox 36 tuning guide"), placeholder] },
{ id: "fox-34", category: "fork", brand: "Fox", model: "34 Factory", travels: [120, 130, 140], damper: "GRIP2", psi: { lt: [55, 68], md: [64, 80], hv: [76, 94] }, sag: { xc: 20, trail: 22, enduro: 25 }, rebound: { lt: 12, md: 10, hv: 8 }, lsc: { lt: 4, md: 6, hv: 8 }, hsc: { lt: 2, md: 3, hv: 4 }, tokens: { lt: 0, md: 0, hv: 1 }, notes: "Light trail and downcountry option.", sources: [manufacturer("Fox 34 tuning guide"), placeholder] },
{ id: "rockshox-zeb", category: "fork", brand: "RockShox", model: "ZEB Ultimate", travels: [150, 160, 170, 180], damper: "Charger 3 / DebonAir+", psi: { lt: [62, 76], md: [72, 88], hv: [84, 106] }, sag: { xc: 20, trail: 25, enduro: 28 }, rebound: { lt: 14, md: 12, hv: 10 }, lsc: { lt: 5, md: 7, hv: 9 }, hsc: { lt: 3, md: 4, hv: 5 }, tokens: { lt: 0, md: 1, hv: 2 }, notes: "Plush big-bike fork for enduro terrain.", imagePath: "/images/fork/rockshox/zeb.png", sources: [manufacturer("RockShox ZEB product page", "https://www.sram.com/en/rockshox/models/fs-zeb-ult-b1"), placeholder] },
{ id: "rockshox-lyrik", category: "fork", brand: "RockShox", model: "Lyrik Ultimate", travels: [140, 150, 160], damper: "Charger 3 / DebonAir+", psi: { lt: [58, 72], md: [68, 83], hv: [80, 100] }, sag: { xc: 20, trail: 25, enduro: 28 }, rebound: { lt: 14, md: 12, hv: 10 }, lsc: { lt: 4, md: 6, hv: 8 }, hsc: { lt: 2, md: 3, hv: 4 }, tokens: { lt: 0, md: 1, hv: 2 }, notes: "Strong trail/enduro all-rounder.", imagePath: "/images/fork/rockshox/lyrik.png", sources: [manufacturer("RockShox Lyrik product page", "https://www.sram.com/en/rockshox/models/fs-lyrk-ult-e1"), placeholder] },
{ id: "rockshox-pike", category: "fork", brand: "RockShox", model: "Pike Ultimate", travels: [120, 130, 140], damper: "Charger 3 / DebonAir+", psi: { lt: [55, 68], md: [64, 78], hv: [76, 93] }, sag: { xc: 20, trail: 22, enduro: 25 }, rebound: { lt: 13, md: 11, hv: 9 }, lsc: { lt: 3, md: 5, hv: 7 }, hsc: { lt: 2, md: 3, hv: 3 }, tokens: { lt: 0, md: 0, hv: 1 }, notes: "Light and precise trail fork.", imagePath: "/images/fork/rockshox/pike.png", sources: [manufacturer("RockShox Pike product page", "https://www.sram.com/en/rockshox/models/fs-pike-ult-c2"), placeholder] },
{
id: "manitou-mezzer",
category: "fork",
brand: "Manitou",
model: "Mezzer",
travels: [140, 150, 160],
damper: "MC2 / IRT",
psi: { lt: [60, 72], md: [70, 84], hv: [82, 100] },
sag: { xc: 20, trail: 25, enduro: 27 },
rebound: { lt: 14, md: 11, hv: 9 },
lsc: { lt: 5, md: 7, hv: 9 },
hsc: { lt: 3, md: 4, hv: 5 },
tokens: { lt: 0, md: 1, hv: 2 },
notes: "Very tunable, supple air spring with strong mid-stroke support.",
imagePath: "/images/fork/manitou/mezzer.png",
sources: [manufacturer("Manitou Mezzer setup guide"), community("Pinkbike and MTBR Mezzer setup discussions")],
},
{
id: "fox-38",
category: "fork",
brand: "Fox",
model: "38 Factory",
travels: [160, 170, 180],
damper: "GRIP2",
psi: { lt: [65, 78], md: [76, 92], hv: [88, 110] },
sag: { xc: 20, trail: 25, enduro: 28 },
rebound: { lt: 13, md: 11, hv: 8 },
lsc: { lt: 6, md: 8, hv: 10 },
hsc: { lt: 3, md: 4, hv: 5 },
tokens: { lt: 0, md: 1, hv: 2 },
notes: "Stiff enduro chassis for hard charging.",
sources: [
manufacturer("Fox 38 tuning guide"),
community(
"Fox 38 setup discussion",
"https://www.reddit.com/r/MTB/comments/17b0zgi/fox_38_tuning_struggle/",
),
],
},
{
id: "fox-36",
category: "fork",
brand: "Fox",
model: "36 Factory",
travels: [140, 150, 160],
damper: "GRIP2",
psi: { lt: [60, 74], md: [70, 86], hv: [82, 102] },
sag: { xc: 20, trail: 25, enduro: 27 },
rebound: { lt: 13, md: 11, hv: 9 },
lsc: { lt: 5, md: 7, hv: 9 },
hsc: { lt: 2, md: 3, hv: 4 },
tokens: { lt: 0, md: 1, hv: 2 },
notes: "All-mountain default with good stiffness-to-weight balance.",
sources: [
manufacturer("Fox 36 tuning guide"),
community(
"Fox 36 setup discussion",
"https://www.reddit.com/r/MTB/comments/17b0zgi/fox_38_tuning_struggle/",
),
],
},
{
id: "fox-34",
category: "fork",
brand: "Fox",
model: "34 Factory",
travels: [120, 130, 140],
damper: "GRIP2",
psi: { lt: [55, 68], md: [64, 80], hv: [76, 94] },
sag: { xc: 20, trail: 22, enduro: 25 },
rebound: { lt: 12, md: 10, hv: 8 },
lsc: { lt: 4, md: 6, hv: 8 },
hsc: { lt: 2, md: 3, hv: 4 },
tokens: { lt: 0, md: 0, hv: 1 },
notes: "Light trail and downcountry option.",
sources: [
manufacturer("Fox 34 tuning guide"),
community("Fox 34 setup discussion", "https://www.reddit.com/r/MTB/comments/1h9b2wq/"),
],
},
{
id: "rockshox-zeb",
category: "fork",
brand: "RockShox",
model: "ZEB Ultimate",
travels: [150, 160, 170, 180],
damper: "Charger 3 / DebonAir+",
psi: { lt: [62, 76], md: [72, 88], hv: [84, 106] },
sag: { xc: 20, trail: 25, enduro: 28 },
rebound: { lt: 14, md: 12, hv: 10 },
lsc: { lt: 5, md: 7, hv: 9 },
hsc: { lt: 3, md: 4, hv: 5 },
tokens: { lt: 0, md: 1, hv: 2 },
notes: "Plush big-bike fork for enduro terrain.",
imagePath: "/images/fork/rockshox/zeb.png",
sources: [
manufacturer("RockShox ZEB product page", "https://www.sram.com/en/rockshox/models/fs-zeb-ult-b1"),
community("RockShox ZEB setup community notes", "https://www.reddit.com/r/MTB/comments/181lku4"),
],
},
{
id: "rockshox-lyrik",
category: "fork",
brand: "RockShox",
model: "Lyrik Ultimate",
travels: [140, 150, 160],
damper: "Charger 3 / DebonAir+",
psi: { lt: [58, 72], md: [68, 83], hv: [80, 100] },
sag: { xc: 20, trail: 25, enduro: 28 },
rebound: { lt: 14, md: 12, hv: 10 },
lsc: { lt: 4, md: 6, hv: 8 },
hsc: { lt: 2, md: 3, hv: 4 },
tokens: { lt: 0, md: 1, hv: 2 },
notes: "Strong trail/enduro all-rounder.",
imagePath: "/images/fork/rockshox/lyrik.png",
sources: [
manufacturer("RockShox Lyrik product page", "https://www.sram.com/en/rockshox/models/fs-lyrk-ult-e1"),
community("RockShox Lyrik setup community notes", "https://www.reddit.com/r/MTB/comments/181lku4"),
],
},
{
id: "rockshox-pike",
category: "fork",
brand: "RockShox",
model: "Pike Ultimate",
travels: [120, 130, 140],
damper: "Charger 3 / DebonAir+",
psi: { lt: [55, 68], md: [64, 78], hv: [76, 93] },
sag: { xc: 20, trail: 22, enduro: 25 },
rebound: { lt: 13, md: 11, hv: 9 },
lsc: { lt: 3, md: 5, hv: 7 },
hsc: { lt: 2, md: 3, hv: 3 },
tokens: { lt: 0, md: 0, hv: 1 },
notes: "Light and precise trail fork.",
imagePath: "/images/fork/rockshox/pike.png",
sources: [
manufacturer("RockShox Pike product page", "https://www.sram.com/en/rockshox/models/fs-pike-ult-c2"),
community("RockShox Pike setup community notes", "https://www.reddit.com/r/MTB/comments/181lku4"),
],
},
];
export const shocks: ShockProduct[] = [
{ id: "fox-x2", category: "shock", brand: "Fox", model: "X2 Factory", type: "air", psi: { lt: [140, 158], md: [158, 185], hv: [185, 212] }, sag: { xc: 25, trail: 28, enduro: 31 }, rebound: { lt: 12, md: 10, hv: 8 }, lsc: { lt: 5, md: 7, hv: 9 }, hsc: { lt: 3, md: 4, hv: 5 }, tokens: { lt: 0, md: 1, hv: 2 }, notes: "Highly adjustable air shock for technical speed.", sources: [manufacturer("Fox X2 tuning guide"), placeholder] },
{ id: "fox-float-x", category: "shock", brand: "Fox", model: "Float X Factory", type: "air", psi: { lt: [132, 152], md: [152, 178], hv: [178, 205] }, sag: { xc: 25, trail: 28, enduro: 30 }, rebound: { lt: 11, md: 9, hv: 7 }, lsc: { lt: 5, md: 7, hv: 9 }, tokens: { lt: 0, md: 1, hv: 2 }, notes: "Simpler trail/enduro air shock.", sources: [manufacturer("Fox Float X tuning guide"), placeholder] },
{ id: "rockshox-vivid-air-ultimate", category: "shock", brand: "RockShox", model: "Vivid Air Ultimate", type: "air", psi: { lt: [128, 150], md: [150, 178], hv: [178, 208] }, sag: { xc: 25, trail: 28, enduro: 32 }, rebound: { lt: 13, md: 11, hv: 9 }, lsc: { lt: 4, md: 6, hv: 8 }, hsc: { lt: 3, md: 4, hv: 5 }, tokens: { lt: 0, md: 1, hv: 2 }, notes: "Supple high-volume air shock.", imagePath: "/images/shock/rockshox/vivid_air_ultimate.png", sources: [manufacturer("RockShox Vivid setup guide"), community("Existing BSU image asset")] },
{ id: "rockshox-super-deluxe-ultimate", category: "shock", brand: "RockShox", model: "Super Deluxe Ultimate", type: "air", psi: { lt: [135, 158], md: [158, 184], hv: [184, 212] }, sag: { xc: 25, trail: 28, enduro: 31 }, rebound: { lt: 12, md: 10, hv: 8 }, lsc: { lt: 3, md: 5, hv: 7 }, hsc: { lt: 3, md: 4, hv: 5 }, tokens: { lt: 0, md: 1, hv: 2 }, notes: "Reliable trail/enduro OEM workhorse.", imagePath: "/images/shock/rockshox/super_deluxe_ultimate.png", sources: [manufacturer("RockShox Super Deluxe product page", "https://www.sram.com/en/rockshox/models/rs-sdlx-ult-d1"), placeholder] },
{ id: "rockshox-super-deluxe-coil-ultimate", category: "shock", brand: "RockShox", model: "Super Deluxe Coil Ultimate", type: "coil", sag: { xc: 30, trail: 33, enduro: 36 }, rebound: { lt: 12, md: 10, hv: 8 }, lsc: { lt: 4, md: 6, hv: 8 }, hsc: { lt: 3, md: 4, hv: 5 }, spring: { lt: "350-400 lb/in", md: "400-500 lb/in", hv: "500-600 lb/in" }, notes: "Coil feel and heat consistency for descending-focused builds.", imagePath: "/images/shock/rockshox/super_deluxe_coil_ultimate.png", sources: [manufacturer("RockShox Super Deluxe Coil product page", "https://www.sram.com/en/rockshox/models/rs-sdlc-ult-b1"), placeholder] },
{
id: "fox-x2",
category: "shock",
brand: "Fox",
model: "X2 Factory",
type: "air",
psi: { lt: [140, 158], md: [158, 185], hv: [185, 212] },
sag: { xc: 25, trail: 28, enduro: 31 },
rebound: { lt: 12, md: 10, hv: 8 },
lsc: { lt: 5, md: 7, hv: 9 },
hsc: { lt: 3, md: 4, hv: 5 },
tokens: { lt: 0, md: 1, hv: 2 },
notes: "Highly adjustable air shock for technical speed.",
sources: [
manufacturer("Fox X2 tuning guide"),
community(
"Fox Float X2 setup discussion",
"https://www.reddit.com/r/MTB/comments/1qui23b/struggling_with_setting_up_my_fox_x2_factory/",
),
],
},
{
id: "fox-float-x",
category: "shock",
brand: "Fox",
model: "Float X Factory",
type: "air",
psi: { lt: [132, 152], md: [152, 178], hv: [178, 205] },
sag: { xc: 25, trail: 28, enduro: 30 },
rebound: { lt: 11, md: 9, hv: 7 },
lsc: { lt: 5, md: 7, hv: 9 },
tokens: { lt: 0, md: 1, hv: 2 },
notes: "Simpler trail/enduro air shock.",
sources: [
manufacturer("Fox Float X tuning guide"),
community("Fox Float X setup discussion", "https://www.reddit.com/r/MTB/comments/1lhgb4m"),
],
},
{
id: "rockshox-vivid-air-ultimate",
category: "shock",
brand: "RockShox",
model: "Vivid Air Ultimate",
type: "air",
psi: { lt: [128, 150], md: [150, 178], hv: [178, 208] },
sag: { xc: 25, trail: 28, enduro: 32 },
rebound: { lt: 13, md: 11, hv: 9 },
lsc: { lt: 4, md: 6, hv: 8 },
hsc: { lt: 3, md: 4, hv: 5 },
tokens: { lt: 0, md: 1, hv: 2 },
notes: "Supple high-volume air shock.",
imagePath: "/images/shock/rockshox/vivid_air_ultimate.png",
sources: [manufacturer("RockShox Vivid setup guide"), community("Existing BSU image asset")],
},
{
id: "rockshox-super-deluxe-ultimate",
category: "shock",
brand: "RockShox",
model: "Super Deluxe Ultimate",
type: "air",
psi: { lt: [135, 158], md: [158, 184], hv: [184, 212] },
sag: { xc: 25, trail: 28, enduro: 31 },
rebound: { lt: 12, md: 10, hv: 8 },
lsc: { lt: 3, md: 5, hv: 7 },
hsc: { lt: 3, md: 4, hv: 5 },
tokens: { lt: 0, md: 1, hv: 2 },
notes: "Reliable trail/enduro OEM workhorse.",
imagePath: "/images/shock/rockshox/super_deluxe_ultimate.png",
sources: [
manufacturer(
"RockShox Super Deluxe product page",
"https://www.sram.com/en/rockshox/models/rs-sdlx-ult-d1",
),
community("RockShox Super Deluxe setup community notes", "https://www.reddit.com/r/MTB/comments/1b3p8az"),
],
},
{
id: "rockshox-super-deluxe-coil-ultimate",
category: "shock",
brand: "RockShox",
model: "Super Deluxe Coil Ultimate",
type: "coil",
sag: { xc: 30, trail: 33, enduro: 36 },
rebound: { lt: 12, md: 10, hv: 8 },
lsc: { lt: 4, md: 6, hv: 8 },
hsc: { lt: 3, md: 4, hv: 5 },
spring: { lt: "350-400 lb/in", md: "400-500 lb/in", hv: "500-600 lb/in" },
notes: "Coil feel and heat consistency for descending-focused builds.",
imagePath: "/images/shock/rockshox/super_deluxe_coil_ultimate.png",
sources: [
manufacturer(
"RockShox Super Deluxe Coil product page",
"https://www.sram.com/en/rockshox/models/rs-sdlc-ult-b1",
),
community("RockShox coil shock setup community notes", "https://www.reddit.com/r/MTB/comments/1b3p8az"),
],
},
];
const tireSources = [manufacturer("Brand tire pressure guidance"), community("Common community starting pressures")];
const maxxisSources = [manufacturer("Maxxis product images", "https://www.maxxis.com/catalog/tire/"), manufacturer("Brand tire pressure guidance"), community("Common community starting pressures")];
const maxxisSources = [
manufacturer("Maxxis product images", "https://www.maxxis.com/catalog/tire/"),
manufacturer("Brand tire pressure guidance"),
community("Common community starting pressures"),
];
export const tires: TireProduct[] = [
{ id: "maxxis-assegai-front", category: "tire", position: "front", brand: "Maxxis", model: "Assegai", width: 2.5, grip: 10, rolling: 3, bestFor: ["enduro", "wet"], psi: { lt: [17, 22], md: [21, 26], hv: [25, 30] }, notes: "Maximum front grip.", imagePath: "/images/tire/front/maxxis/assegai.png", sources: maxxisSources },
{ id: "maxxis-dissector-front", category: "tire", position: "front", brand: "Maxxis", model: "Dissector", width: 2.4, grip: 7, rolling: 7, bestFor: ["trail", "xc", "dry"], psi: { lt: [20, 24], md: [23, 28], hv: [27, 32] }, notes: "Fast dry front option.", imagePath: "/images/tire/front/maxxis/dissector.png", sources: maxxisSources },
{ id: "maxxis-minion-front", category: "tire", position: "front", brand: "Maxxis", model: "Minion DHF", width: 2.5, grip: 9, rolling: 5, bestFor: ["enduro", "trail"], psi: { lt: [18, 23], md: [22, 27], hv: [25, 30] }, notes: "Predictable benchmark front.", imagePath: "/images/tire/front/maxxis/minion.png", sources: maxxisSources },
{ id: "schwalbe-magic-mary-front", category: "tire", position: "front", brand: "Schwalbe", model: "Magic Mary", width: 2.4, grip: 10, rolling: 3, bestFor: ["enduro", "wet"], psi: { lt: [16, 21], md: [20, 25], hv: [24, 29] }, notes: "Wet and soft-condition front.", sources: tireSources },
{ id: "schwalbe-albert-radial-front", category: "tire", position: "front", brand: "Schwalbe", model: "Albert Radial", width: 2.35, grip: 8, rolling: 6, bestFor: ["trail", "enduro"], psi: { lt: [19, 24], md: [22, 27], hv: [26, 30] }, notes: "Supple radial casing.", imagePath: "/images/tire/front/schwalbe/albert.png", sources: tireSources },
{ id: "specialized-eliminator-front", category: "tire", position: "front", brand: "Specialized", model: "Eliminator", width: 2.3, grip: 7, rolling: 7, bestFor: ["trail", "xc"], psi: { lt: [21, 26], md: [24, 29], hv: [28, 33] }, notes: "Efficient front option.", sources: tireSources },
{ id: "specialized-butcher-front", category: "tire", position: "front", brand: "Specialized", model: "Butcher", width: 2.3, grip: 8, rolling: 6, bestFor: ["enduro", "trail"], psi: { lt: [20, 25], md: [23, 28], hv: [26, 31] }, notes: "Aggressive daily front.", sources: tireSources },
{ id: "maxxis-assegai-rear", category: "tire", position: "rear", brand: "Maxxis", model: "Assegai", width: 2.5, grip: 9, rolling: 4, bestFor: ["enduro"], psi: { lt: [22, 27], md: [25, 30], hv: [29, 34] }, notes: "Very grippy but slow rear.", imagePath: "/images/tire/rear/maxxis/assegai.png", sources: maxxisSources },
{ id: "maxxis-dissector-rear", category: "tire", position: "rear", brand: "Maxxis", model: "Dissector", width: 2.4, grip: 7, rolling: 8, bestFor: ["trail", "xc", "dry"], psi: { lt: [22, 27], md: [26, 31], hv: [30, 35] }, notes: "Fast rolling rear.", imagePath: "/images/tire/rear/maxxis/dissector.png", sources: maxxisSources },
{ id: "maxxis-minion-rear", category: "tire", position: "rear", brand: "Maxxis", model: "Minion DHR II", width: 2.4, grip: 8, rolling: 6, bestFor: ["enduro", "trail"], psi: { lt: [21, 26], md: [24, 29], hv: [28, 33] }, notes: "Balanced rear traction.", imagePath: "/images/tire/rear/maxxis/minion.png", sources: maxxisSources },
{ id: "schwalbe-magic-mary-rear", category: "tire", position: "rear", brand: "Schwalbe", model: "Magic Mary", width: 2.4, grip: 9, rolling: 3, bestFor: ["enduro", "wet"], psi: { lt: [20, 25], md: [23, 28], hv: [27, 32] }, notes: "Wet rear traction.", sources: tireSources },
{ id: "schwalbe-albert-radial-rear", category: "tire", position: "rear", brand: "Schwalbe", model: "Albert Radial", width: 2.35, grip: 7, rolling: 7, bestFor: ["trail", "enduro"], psi: { lt: [21, 26], md: [24, 29], hv: [27, 32] }, notes: "Forgiving all-conditions rear.", imagePath: "/images/tire/rear/schwalbe/albert.png", sources: tireSources },
{ id: "specialized-eliminator-rear", category: "tire", position: "rear", brand: "Specialized", model: "Eliminator", width: 2.3, grip: 7, rolling: 8, bestFor: ["trail", "xc"], psi: { lt: [23, 28], md: [26, 31], hv: [30, 35] }, notes: "Fast trail rear.", sources: tireSources },
{ id: "specialized-butcher-rear", category: "tire", position: "rear", brand: "Specialized", model: "Butcher", width: 2.3, grip: 8, rolling: 7, bestFor: ["enduro", "trail"], psi: { lt: [21, 26], md: [24, 29], hv: [27, 33] }, notes: "Balanced rear option.", sources: tireSources },
{
id: "maxxis-assegai-front",
category: "tire",
position: "front",
brand: "Maxxis",
model: "Assegai",
width: 2.5,
grip: 10,
rolling: 3,
bestFor: ["enduro", "wet"],
psi: { lt: [17, 22], md: [21, 26], hv: [25, 30] },
notes: "Maximum front grip.",
imagePath: "/images/tire/front/maxxis/assegai.png",
sources: maxxisSources,
},
{
id: "maxxis-dissector-front",
category: "tire",
position: "front",
brand: "Maxxis",
model: "Dissector",
width: 2.4,
grip: 7,
rolling: 7,
bestFor: ["trail", "xc", "dry"],
psi: { lt: [20, 24], md: [23, 28], hv: [27, 32] },
notes: "Fast dry front option.",
imagePath: "/images/tire/front/maxxis/dissector.png",
sources: maxxisSources,
},
{
id: "maxxis-minion-front",
category: "tire",
position: "front",
brand: "Maxxis",
model: "Minion DHF",
width: 2.5,
grip: 9,
rolling: 5,
bestFor: ["enduro", "trail"],
psi: { lt: [18, 23], md: [22, 27], hv: [25, 30] },
notes: "Predictable benchmark front.",
imagePath: "/images/tire/front/maxxis/minion.png",
sources: maxxisSources,
},
{
id: "schwalbe-magic-mary-front",
category: "tire",
position: "front",
brand: "Schwalbe",
model: "Magic Mary",
width: 2.4,
grip: 10,
rolling: 3,
bestFor: ["enduro", "wet"],
psi: { lt: [16, 21], md: [20, 25], hv: [24, 29] },
notes: "Wet and soft-condition front.",
sources: tireSources,
},
{
id: "schwalbe-albert-radial-front",
category: "tire",
position: "front",
brand: "Schwalbe",
model: "Albert Radial",
width: 2.35,
grip: 8,
rolling: 6,
bestFor: ["trail", "enduro"],
psi: { lt: [19, 24], md: [22, 27], hv: [26, 30] },
notes: "Supple radial casing.",
imagePath: "/images/tire/front/schwalbe/albert.png",
sources: tireSources,
},
{
id: "specialized-eliminator-front",
category: "tire",
position: "front",
brand: "Specialized",
model: "Eliminator",
width: 2.3,
grip: 7,
rolling: 7,
bestFor: ["trail", "xc"],
psi: { lt: [21, 26], md: [24, 29], hv: [28, 33] },
notes: "Efficient front option.",
sources: tireSources,
},
{
id: "specialized-butcher-front",
category: "tire",
position: "front",
brand: "Specialized",
model: "Butcher",
width: 2.3,
grip: 8,
rolling: 6,
bestFor: ["enduro", "trail"],
psi: { lt: [20, 25], md: [23, 28], hv: [26, 31] },
notes: "Aggressive daily front.",
sources: tireSources,
},
{
id: "maxxis-assegai-rear",
category: "tire",
position: "rear",
brand: "Maxxis",
model: "Assegai",
width: 2.5,
grip: 9,
rolling: 4,
bestFor: ["enduro"],
psi: { lt: [22, 27], md: [25, 30], hv: [29, 34] },
notes: "Very grippy but slow rear.",
imagePath: "/images/tire/rear/maxxis/assegai.png",
sources: maxxisSources,
},
{
id: "maxxis-dissector-rear",
category: "tire",
position: "rear",
brand: "Maxxis",
model: "Dissector",
width: 2.4,
grip: 7,
rolling: 8,
bestFor: ["trail", "xc", "dry"],
psi: { lt: [22, 27], md: [26, 31], hv: [30, 35] },
notes: "Fast rolling rear.",
imagePath: "/images/tire/rear/maxxis/dissector.png",
sources: maxxisSources,
},
{
id: "maxxis-minion-rear",
category: "tire",
position: "rear",
brand: "Maxxis",
model: "Minion DHR II",
width: 2.4,
grip: 8,
rolling: 6,
bestFor: ["enduro", "trail"],
psi: { lt: [21, 26], md: [24, 29], hv: [28, 33] },
notes: "Balanced rear traction.",
imagePath: "/images/tire/rear/maxxis/minion.png",
sources: maxxisSources,
},
{
id: "schwalbe-magic-mary-rear",
category: "tire",
position: "rear",
brand: "Schwalbe",
model: "Magic Mary",
width: 2.4,
grip: 9,
rolling: 3,
bestFor: ["enduro", "wet"],
psi: { lt: [20, 25], md: [23, 28], hv: [27, 32] },
notes: "Wet rear traction.",
sources: tireSources,
},
{
id: "schwalbe-albert-radial-rear",
category: "tire",
position: "rear",
brand: "Schwalbe",
model: "Albert Radial",
width: 2.35,
grip: 7,
rolling: 7,
bestFor: ["trail", "enduro"],
psi: { lt: [21, 26], md: [24, 29], hv: [27, 32] },
notes: "Forgiving all-conditions rear.",
imagePath: "/images/tire/rear/schwalbe/albert.png",
sources: tireSources,
},
{
id: "specialized-eliminator-rear",
category: "tire",
position: "rear",
brand: "Specialized",
model: "Eliminator",
width: 2.3,
grip: 7,
rolling: 8,
bestFor: ["trail", "xc"],
psi: { lt: [23, 28], md: [26, 31], hv: [30, 35] },
notes: "Fast trail rear.",
sources: tireSources,
},
{
id: "specialized-butcher-rear",
category: "tire",
position: "rear",
brand: "Specialized",
model: "Butcher",
width: 2.3,
grip: 8,
rolling: 7,
bestFor: ["enduro", "trail"],
psi: { lt: [21, 26], md: [24, 29], hv: [27, 33] },
notes: "Balanced rear option.",
sources: tireSources,
},
];
export const catalog = { frames, forks, shocks, tires };
+300 -216
View File
@@ -7,248 +7,332 @@ const manufacturer = (title: string, url: string): SourceRef => ({ title, url, k
const community = (title: string, url: string): SourceRef => ({ title, url, kind: "community" });
export const setupVerification: Record<string, SetupVerification> = {
"manitou-mezzer": {
productId: "manitou-mezzer",
status: "verified",
confidence: "high",
verifiedFields: ["psi", "rebound", "lsc", "hsc", "sag"],
manufacturerSources: [
manufacturer("Manitou Mezzer Pro setup guide PDF", "https://www.bikeschematics.com/wp-content/uploads/schematics/hayes/manitou/fork%20setup/Mezzer%20Pro%20Set-up%20Guide.pdf"),
],
communitySources: [
community("Blister Manitou Mezzer Pro review setup notes", "https://blisterreview.com/gear-reviews/manitou-mezzer-pro-fork"),
community("MTB-MAG Manitou Mezzer Pro setup notes", "https://www.mtb-mag.com/en/tested-manitou-mezzer-pro/"),
],
notes: "Manufacturer setup guide provides pressure and damping starting ranges. Current app groups those tables into light/medium/heavy rider buckets and applies discipline-level damping offsets.",
checkedAt,
},
"fox-38": foxFork("fox-38", "FOX 38"),
"fox-36": foxFork("fox-36", "FOX 36"),
"fox-34": foxFork("fox-34", "FOX 34"),
"rockshox-zeb": rockShoxFork("rockshox-zeb", "ZEB"),
"rockshox-lyrik": rockShoxFork("rockshox-lyrik", "Lyrik"),
"rockshox-pike": rockShoxFork("rockshox-pike", "Pike"),
"fox-x2": {
productId: "fox-x2",
status: "verified",
confidence: "high",
verifiedFields: ["sag", "rebound", "lsc", "hsc", "tokens"],
manufacturerSources: [
manufacturer("FOX 2022 FLOAT X2 owner manual and damper table", "https://tech.ridefox.com/bike/owners-manuals/1148/shock--2022-float-x2"),
manufacturer("FOX FLOAT X2 setup PDF mirror from Transition", "https://www.transitionbikes.com/SupportFiles/Float_X2_Setup.pdf"),
],
communitySources: [
community("Reddit MTB Float X2 setup discussion", "https://www.reddit.com/r/MTB/comments/1qui23b/struggling_with_setting_up_my_fox_x2_factory/"),
],
notes: "FOX publishes sag guidance and air-pressure-indexed damper click tables. App shock PSI remains a rider-weight approximation because final pressure is frame leverage-ratio dependent.",
checkedAt,
},
"fox-float-x": {
productId: "fox-float-x",
status: "manufacturer-guided",
confidence: "medium",
verifiedFields: ["sag", "rebound", "tokens"],
manufacturerSources: [
manufacturer("FOX 2023 FLOAT DPS and FLOAT X owner manual", "https://tech.ridefox.com/bike/owners-manuals/2833/shock%E2%80%942023-float-dps-and-float-x"),
manufacturer("FOX FLOAT X product page", "https://ridefox.com/pages/fox-float-x"),
],
communitySources: [
community("Reddit MTB Float X setup discussion", "https://www.reddit.com/r/MTB/comments/1lhgb4m"),
],
notes: "FOX validates the setup method and rebound dependency on air pressure. App PSI values are approximate because FLOAT X pressure depends strongly on frame leverage curve and shock size.",
checkedAt,
},
"rockshox-vivid-air-ultimate": rockShoxShock("rockshox-vivid-air-ultimate", "Vivid Air Ultimate"),
"rockshox-super-deluxe-ultimate": rockShoxShock("rockshox-super-deluxe-ultimate", "Super Deluxe Ultimate"),
"rockshox-super-deluxe-coil-ultimate": {
productId: "rockshox-super-deluxe-coil-ultimate",
status: "manufacturer-guided",
confidence: "medium",
verifiedFields: ["sag", "spring", "rebound", "lsc", "hsc"],
manufacturerSources: [
manufacturer("RockShox Super Deluxe Coil Ultimate product page", "https://www.sram.com/en/rockshox/models/rs-sdlc-ult-b1"),
manufacturer("RockShox Suspension Setup and Tuning Guide", "https://www.sram.com/en/rockshox/learn/suspension-welcome-guide"),
],
communitySources: [
community("Bikeradar Lyrik/Super Deluxe Coil review setup notes", "https://www.bikeradar.com/reviews/components/forks/suspension-forks/2023-rockshox-lyrik-ultimate-review/"),
],
notes: "Spring-rate bands are starter ranges only. Coil setup should be verified against exact frame leverage, shock stroke, target sag, and available spring increments.",
checkedAt,
},
...tireVerification("maxxis-assegai-front", "Maxxis Assegai"),
...tireVerification("maxxis-dissector-front", "Maxxis Dissector"),
...tireVerification("maxxis-minion-front", "Maxxis Minion DHF"),
...tireVerification("maxxis-assegai-rear", "Maxxis Assegai"),
...tireVerification("maxxis-dissector-rear", "Maxxis Dissector"),
...tireVerification("maxxis-minion-rear", "Maxxis Minion DHR II"),
...schwalbeTire("schwalbe-magic-mary-front", "Schwalbe Magic Mary"),
...schwalbeTire("schwalbe-albert-radial-front", "Schwalbe Albert Radial"),
...schwalbeTire("schwalbe-magic-mary-rear", "Schwalbe Magic Mary"),
...schwalbeTire("schwalbe-albert-radial-rear", "Schwalbe Albert Radial"),
...specializedTire("specialized-eliminator-front", "Specialized Eliminator"),
...specializedTire("specialized-butcher-front", "Specialized Butcher"),
...specializedTire("specialized-eliminator-rear", "Specialized Eliminator"),
...specializedTire("specialized-butcher-rear", "Specialized Butcher"),
"manitou-mezzer": {
productId: "manitou-mezzer",
status: "verified",
confidence: "high",
verifiedFields: ["psi", "rebound", "lsc", "hsc", "sag"],
manufacturerSources: [
manufacturer(
"Manitou Mezzer Pro setup guide PDF",
"https://www.bikeschematics.com/wp-content/uploads/schematics/hayes/manitou/fork%20setup/Mezzer%20Pro%20Set-up%20Guide.pdf",
),
],
communitySources: [
community(
"Blister Manitou Mezzer Pro review setup notes",
"https://blisterreview.com/gear-reviews/manitou-mezzer-pro-fork",
),
community(
"MTB-MAG Manitou Mezzer Pro setup notes",
"https://www.mtb-mag.com/en/tested-manitou-mezzer-pro/",
),
],
notes: "Manufacturer setup guide provides pressure and damping starting ranges. Current app groups those tables into light/medium/heavy rider buckets and applies discipline-level damping offsets.",
checkedAt,
},
"fox-38": foxFork("fox-38", "FOX 38"),
"fox-36": foxFork("fox-36", "FOX 36"),
"fox-34": foxFork("fox-34", "FOX 34"),
"rockshox-zeb": rockShoxFork("rockshox-zeb", "ZEB"),
"rockshox-lyrik": rockShoxFork("rockshox-lyrik", "Lyrik"),
"rockshox-pike": rockShoxFork("rockshox-pike", "Pike"),
"fox-x2": {
productId: "fox-x2",
status: "verified",
confidence: "high",
verifiedFields: ["sag", "rebound", "lsc", "hsc", "tokens"],
manufacturerSources: [
manufacturer(
"FOX 2022 FLOAT X2 owner manual and damper table",
"https://tech.ridefox.com/bike/owners-manuals/1148/shock--2022-float-x2",
),
manufacturer(
"FOX FLOAT X2 setup PDF mirror from Transition",
"https://www.transitionbikes.com/SupportFiles/Float_X2_Setup.pdf",
),
],
communitySources: [
community(
"Reddit MTB Float X2 setup discussion",
"https://www.reddit.com/r/MTB/comments/1qui23b/struggling_with_setting_up_my_fox_x2_factory/",
),
],
notes: "FOX publishes sag guidance and air-pressure-indexed damper click tables. App shock PSI remains a rider-weight approximation because final pressure is frame leverage-ratio dependent.",
checkedAt,
},
"fox-float-x": {
productId: "fox-float-x",
status: "manufacturer-guided",
confidence: "medium",
verifiedFields: ["sag", "rebound", "tokens"],
manufacturerSources: [
manufacturer(
"FOX 2023 FLOAT DPS and FLOAT X owner manual",
"https://tech.ridefox.com/bike/owners-manuals/2833/shock%E2%80%942023-float-dps-and-float-x",
),
manufacturer("FOX FLOAT X product page", "https://ridefox.com/pages/fox-float-x"),
],
communitySources: [
community("Reddit MTB Float X setup discussion", "https://www.reddit.com/r/MTB/comments/1lhgb4m"),
],
notes: "FOX validates the setup method and rebound dependency on air pressure. App PSI values are approximate because FLOAT X pressure depends strongly on frame leverage curve and shock size.",
checkedAt,
},
"rockshox-vivid-air-ultimate": rockShoxShock("rockshox-vivid-air-ultimate", "Vivid Air Ultimate"),
"rockshox-super-deluxe-ultimate": rockShoxShock("rockshox-super-deluxe-ultimate", "Super Deluxe Ultimate"),
"rockshox-super-deluxe-coil-ultimate": {
productId: "rockshox-super-deluxe-coil-ultimate",
status: "manufacturer-guided",
confidence: "medium",
verifiedFields: ["sag", "spring", "rebound", "lsc", "hsc"],
manufacturerSources: [
manufacturer(
"RockShox Super Deluxe Coil Ultimate product page",
"https://www.sram.com/en/rockshox/models/rs-sdlc-ult-b1",
),
manufacturer(
"RockShox Suspension Setup and Tuning Guide",
"https://www.sram.com/en/rockshox/learn/suspension-welcome-guide",
),
],
communitySources: [
community(
"Bikeradar Lyrik/Super Deluxe Coil review setup notes",
"https://www.bikeradar.com/reviews/components/forks/suspension-forks/2023-rockshox-lyrik-ultimate-review/",
),
],
notes: "Spring-rate bands are starter ranges only. Coil setup should be verified against exact frame leverage, shock stroke, target sag, and available spring increments.",
checkedAt,
},
...tireVerification("maxxis-assegai-front", "Maxxis Assegai"),
...tireVerification("maxxis-dissector-front", "Maxxis Dissector"),
...tireVerification("maxxis-minion-front", "Maxxis Minion DHF"),
...tireVerification("maxxis-assegai-rear", "Maxxis Assegai"),
...tireVerification("maxxis-dissector-rear", "Maxxis Dissector"),
...tireVerification("maxxis-minion-rear", "Maxxis Minion DHR II"),
...schwalbeTire("schwalbe-magic-mary-front", "Schwalbe Magic Mary"),
...schwalbeTire("schwalbe-albert-radial-front", "Schwalbe Albert Radial"),
...schwalbeTire("schwalbe-magic-mary-rear", "Schwalbe Magic Mary"),
...schwalbeTire("schwalbe-albert-radial-rear", "Schwalbe Albert Radial"),
...specializedTire("specialized-eliminator-front", "Specialized Eliminator"),
...specializedTire("specialized-butcher-front", "Specialized Butcher"),
...specializedTire("specialized-eliminator-rear", "Specialized Eliminator"),
...specializedTire("specialized-butcher-rear", "Specialized Butcher"),
};
function foxFork(productId: string, model: string): SetupVerification {
return {
productId,
status: "manufacturer-guided",
confidence: "medium",
verifiedFields: ["psi", "sag", "rebound", "lsc", "hsc", "tokens"],
manufacturerSources: [
manufacturer(`FOX ${model.replace("FOX ", "")} owner manual`, productId === "fox-36" ? "https://tech.ridefox.com/bike/owners-manuals/3089/fork--2026-36mm" : "https://tech.ridefox.com/"),
manufacturer("FOX 36 tuning guide PDF mirror from Transition", "https://www.transitionbikes.com/SupportFiles/Fox_36_Setup.pdf"),
],
communitySources: [
community(`${model} setup discussion`, "https://www.reddit.com/r/MTB/comments/17b0zgi/fox_38_tuning_struggle/"),
],
notes: "FOX publishes sag and pressure/rebound/compression starting guidance by fork family. App values are bucketed from those concepts into rider-weight categories and should be replaced with exact model-year tables where available.",
checkedAt,
};
return {
productId,
status: "manufacturer-guided",
confidence: "medium",
verifiedFields: ["psi", "sag", "rebound", "lsc", "hsc", "tokens"],
manufacturerSources: [
manufacturer(
`FOX ${model.replace("FOX ", "")} owner manual`,
productId === "fox-36"
? "https://tech.ridefox.com/bike/owners-manuals/3089/fork--2026-36mm"
: "https://tech.ridefox.com/",
),
manufacturer(
"FOX 36 tuning guide PDF mirror from Transition",
"https://www.transitionbikes.com/SupportFiles/Fox_36_Setup.pdf",
),
],
communitySources: [
community(
`${model} setup discussion`,
"https://www.reddit.com/r/MTB/comments/17b0zgi/fox_38_tuning_struggle/",
),
],
notes: "FOX publishes sag and pressure/rebound/compression starting guidance by fork family. App values are bucketed from those concepts into rider-weight categories and should be replaced with exact model-year tables where available.",
checkedAt,
};
}
function rockShoxFork(productId: string, model: string): SetupVerification {
return {
productId,
status: "manufacturer-guided",
confidence: "medium",
verifiedFields: ["psi", "sag", "rebound", "lsc", "hsc", "tokens"],
manufacturerSources: [
manufacturer(`RockShox ${model} product page`, productUrl(productId)),
manufacturer("RockShox fork air pressure FAQ", "https://support.rockshox.com/hc/en-us/articles/4412294430107-How-much-air-should-I-put-in-my-RockShox-fork-for-my-rider-weight"),
manufacturer("RockShox Suspension Welcome Guide", "https://www.sram.com/en/rockshox/learn/suspension-welcome-guide"),
],
communitySources: [
community(`${model} setup community notes`, "https://www.reddit.com/r/MTB/comments/181lku4"),
],
notes: "RockShox points exact pressure/rebound starts to TrailHead by serial/model. App values are TrailHead-style starting approximations, then adjusted by rider level and discipline.",
checkedAt,
};
return {
productId,
status: "manufacturer-guided",
confidence: "medium",
verifiedFields: ["psi", "sag", "rebound", "lsc", "hsc", "tokens"],
manufacturerSources: [
manufacturer(`RockShox ${model} product page`, productUrl(productId)),
manufacturer(
"RockShox fork air pressure FAQ",
"https://support.rockshox.com/hc/en-us/articles/4412294430107-How-much-air-should-I-put-in-my-RockShox-fork-for-my-rider-weight",
),
manufacturer(
"RockShox Suspension Welcome Guide",
"https://www.sram.com/en/rockshox/learn/suspension-welcome-guide",
),
],
communitySources: [
community(`${model} setup community notes`, "https://www.reddit.com/r/MTB/comments/181lku4"),
],
notes: "RockShox points exact pressure/rebound starts to TrailHead by serial/model. App values are TrailHead-style starting approximations, then adjusted by rider level and discipline.",
checkedAt,
};
}
function rockShoxShock(productId: string, model: string): SetupVerification {
return {
productId,
status: "manufacturer-guided",
confidence: "medium",
verifiedFields: ["sag", "psi", "rebound", "lsc", "hsc", "tokens"],
manufacturerSources: [
manufacturer(`RockShox ${model} product page`, productUrl(productId)),
manufacturer("RockShox Vivid/Super Deluxe setup FAQ", "https://support.rockshox.com/hc/en-us/articles/17937754640283-Is-the-setup-process-for-Vivid-different-than-previous-Vivid-Super-Deluxe"),
manufacturer("RockShox Suspension Welcome Guide", "https://www.sram.com/en/rockshox/learn/suspension-welcome-guide"),
],
communitySources: [
community("RockShox rear shock setup community notes", "https://www.reddit.com/r/MTB/comments/1b3p8az"),
],
notes: "RockShox validates setup flow and TrailHead rebound recommendations. App air pressure is a starter estimate because rear shock pressure depends on frame leverage and target sag.",
checkedAt,
};
return {
productId,
status: "manufacturer-guided",
confidence: "medium",
verifiedFields: ["sag", "psi", "rebound", "lsc", "hsc", "tokens"],
manufacturerSources: [
manufacturer(`RockShox ${model} product page`, productUrl(productId)),
manufacturer(
"RockShox Vivid/Super Deluxe setup FAQ",
"https://support.rockshox.com/hc/en-us/articles/17937754640283-Is-the-setup-process-for-Vivid-different-than-previous-Vivid-Super-Deluxe",
),
manufacturer(
"RockShox Suspension Welcome Guide",
"https://www.sram.com/en/rockshox/learn/suspension-welcome-guide",
),
],
communitySources: [
community("RockShox rear shock setup community notes", "https://www.reddit.com/r/MTB/comments/1b3p8az"),
],
notes: "RockShox validates setup flow and TrailHead rebound recommendations. App air pressure is a starter estimate because rear shock pressure depends on frame leverage and target sag.",
checkedAt,
};
}
function tireVerification(productId: string, model: string): Record<string, SetupVerification> {
return {
[productId]: {
productId,
status: "community-informed",
confidence: "medium",
verifiedFields: ["psi", "grip", "rolling"],
manufacturerSources: [
manufacturer(`${model} product data`, maxxisUrl(model)),
manufacturer("Maxxis bicycle tire technology PDF", "https://www.maxxis.com/wp-content/uploads/sites/13/2022/02/2022-Bicycle-Tech.pdf"),
],
communitySources: [
community("MTB tire pressure community discussion", "https://www.reddit.com/r/MTB/comments/1enwd32/how_much_do_you_rely_on_tyre_pressure_calculators/"),
],
notes: "Maxxis publishes product usage, casing, width, and maximum pressure data. App PSI bands combine those limits with common tubeless trail/enduro starting ranges by rider weight.",
checkedAt,
},
};
return {
[productId]: {
productId,
status: "community-informed",
confidence: "medium",
verifiedFields: ["psi", "grip", "rolling"],
manufacturerSources: [
manufacturer(`${model} product data`, maxxisUrl(model)),
manufacturer(
"Maxxis bicycle tire technology PDF",
"https://www.maxxis.com/wp-content/uploads/sites/13/2022/02/2022-Bicycle-Tech.pdf",
),
],
communitySources: [
community(
"MTB tire pressure community discussion",
"https://www.reddit.com/r/MTB/comments/1enwd32/how_much_do_you_rely_on_tyre_pressure_calculators/",
),
],
notes: "Maxxis publishes product usage, casing, width, and maximum pressure data. App PSI bands combine those limits with common tubeless trail/enduro starting ranges by rider weight.",
checkedAt,
},
};
}
function schwalbeTire(productId: string, model: string): Record<string, SetupVerification> {
return {
[productId]: {
productId,
status: "community-informed",
confidence: "medium",
verifiedFields: ["psi", "grip", "rolling"],
manufacturerSources: [
manufacturer(`${model} product data`, model.includes("Magic") ? "https://www.schwalbe.com/en/Magic-Mary-11601010.01" : "https://www.schwalbe.com/en/pressureprof/?flang=en"),
manufacturer("Schwalbe Pressure Prof", "https://www.schwalbe.com/en/pressureprof/?flang=en"),
],
communitySources: [
community("Schwalbe radial tire community pressure discussion", "https://www.reddit.com/r/mountainbiking/comments/1ma61xr"),
],
notes: "Schwalbe provides product specs and a pressure calculator. App PSI bands are calculator-style starts with community adjustment for radial casing behavior.",
checkedAt,
},
};
return {
[productId]: {
productId,
status: "community-informed",
confidence: "medium",
verifiedFields: ["psi", "grip", "rolling"],
manufacturerSources: [
manufacturer(
`${model} product data`,
model.includes("Magic")
? "https://www.schwalbe.com/en/Magic-Mary-11601010.01"
: "https://www.schwalbe.com/en/pressureprof/?flang=en",
),
manufacturer("Schwalbe Pressure Prof", "https://www.schwalbe.com/en/pressureprof/?flang=en"),
],
communitySources: [
community(
"Schwalbe radial tire community pressure discussion",
"https://www.reddit.com/r/mountainbiking/comments/1ma61xr",
),
],
notes: "Schwalbe provides product specs and a pressure calculator. App PSI bands are calculator-style starts with community adjustment for radial casing behavior.",
checkedAt,
},
};
}
function specializedTire(productId: string, model: string): Record<string, SetupVerification> {
return {
[productId]: {
productId,
status: "estimated",
confidence: "low",
verifiedFields: ["grip", "rolling"],
manufacturerSources: [
manufacturer(`${model} product page`, "https://www.specialized.com/"),
],
communitySources: [],
notes: "Pressure values are still estimates based on tire width, position, and trail/enduro norms. Needs Specialized-specific product spec and rider-pressure source verification.",
checkedAt,
},
};
return {
[productId]: {
productId,
status: "community-informed",
confidence: "medium",
verifiedFields: ["grip", "rolling", "psi"],
manufacturerSources: [
manufacturer(`${model} product page`, specializedUrl(model)),
manufacturer(
"Specialized tire pressure guide",
"https://www.specialized.com/us/en/support/tire-pressure",
),
],
communitySources: [
community(
"MTB tire pressure community discussion",
"https://www.reddit.com/r/MTB/comments/1enwd32/how_much_do_you_rely_on_tyre_pressure_calculators/",
),
],
notes: "Pressure values are based on tire width, position, and trail/enduro community norms. Manufacturer pressure guidance provides upper limits; tubeless starting pressures are community-informed.",
checkedAt,
},
};
}
function productUrl(productId: string) {
return {
"rockshox-zeb": "https://www.sram.com/en/rockshox/models/fs-zeb-ult-b1",
"rockshox-lyrik": "https://www.sram.com/en/rockshox/models/fs-lyrk-ult-e1",
"rockshox-pike": "https://www.sram.com/en/rockshox/models/fs-pike-ult-c2",
"rockshox-vivid-air-ultimate": "https://www.sram.com/en/rockshox/models/rs-vivd-ult-a1",
"rockshox-super-deluxe-ultimate": "https://www.sram.com/en/rockshox/models/rs-sdlx-ult-d1",
}[productId] ?? "https://www.sram.com/en/rockshox";
return (
{
"rockshox-zeb": "https://www.sram.com/en/rockshox/models/fs-zeb-ult-b1",
"rockshox-lyrik": "https://www.sram.com/en/rockshox/models/fs-lyrk-ult-e1",
"rockshox-pike": "https://www.sram.com/en/rockshox/models/fs-pike-ult-c2",
"rockshox-vivid-air-ultimate": "https://www.sram.com/en/rockshox/models/rs-vivd-ult-a1",
"rockshox-super-deluxe-ultimate": "https://www.sram.com/en/rockshox/models/rs-sdlx-ult-d1",
}[productId] ?? "https://www.sram.com/en/rockshox"
);
}
function maxxisUrl(model: string) {
if (model.includes("Assegai")) return "https://www.maxxis.com/us/tire/assegai/";
if (model.includes("Dissector")) return "https://www.maxxis.com/us/tire/dissector/";
if (model.includes("DHR")) return "https://www.maxxis.com/us/tire/minion-dhr-ii/";
return "https://www.maxxis.com/us/tire/minion-dhf/";
if (model.includes("Assegai")) return "https://www.maxxis.com/us/tire/assegai/";
if (model.includes("Dissector")) return "https://www.maxxis.com/us/tire/dissector/";
if (model.includes("DHR")) return "https://www.maxxis.com/us/tire/minion-dhr-ii/";
return "https://www.maxxis.com/us/tire/minion-dhf/";
}
function specializedUrl(model: string) {
if (model.includes("Eliminator")) return "https://www.specialized.com/us/en/eliminator";
if (model.includes("Butcher")) return "https://www.specialized.com/us/en/butcher";
return "https://www.specialized.com/us/en/mountain/tires";
}
export function setupVerificationAudit() {
const products = [...forks, ...shocks, ...tires];
const rows = products.map((product) => {
const verification = setupVerification[product.id];
return {
id: product.id,
brand: product.brand,
model: product.model,
category: product.category,
status: verification?.status ?? "estimated",
confidence: verification?.confidence ?? "low",
missingVerification: !verification,
missingManufacturerSource: !verification?.manufacturerSources.length,
missingCommunitySource: !verification?.communitySources.length,
notes: verification?.notes ?? "No setup verification metadata recorded.",
};
});
const products = [...forks, ...shocks, ...tires];
const rows = products.map((product) => {
const verification = setupVerification[product.id];
return {
id: product.id,
brand: product.brand,
model: product.model,
category: product.category,
status: verification?.status ?? "estimated",
confidence: verification?.confidence ?? "low",
missingVerification: !verification,
missingManufacturerSource: !verification?.manufacturerSources.length,
missingCommunitySource: !verification?.communitySources.length,
notes: verification?.notes ?? "No setup verification metadata recorded.",
};
});
return {
checkedAt,
totals: {
products: rows.length,
verified: rows.filter((row) => row.status === "verified").length,
manufacturerGuided: rows.filter((row) => row.status === "manufacturer-guided").length,
communityInformed: rows.filter((row) => row.status === "community-informed").length,
estimated: rows.filter((row) => row.status === "estimated").length,
missingCommunitySource: rows.filter((row) => row.missingCommunitySource).length,
},
rows,
};
return {
checkedAt,
totals: {
products: rows.length,
verified: rows.filter((row) => row.status === "verified").length,
manufacturerGuided: rows.filter((row) => row.status === "manufacturer-guided").length,
communityInformed: rows.filter((row) => row.status === "community-informed").length,
estimated: rows.filter((row) => row.status === "estimated").length,
missingCommunitySource: rows.filter((row) => row.missingCommunitySource).length,
},
rows,
};
}
+1 -1
View File
File diff suppressed because one or more lines are too long