okay so i spent way too long on this.
the docs say you need mdx-components.tsx at the root but don't really explain why. turns out without it the compiler just silently doesn't apply your custom components. no error. just... the defaults. i was staring at unstyled h1s for 45 minutes.
the pageExtensions thing also confused me. you need it so next.js treats .mdx files as valid routes. obvious in hindsight. not obvious when you're looking at a 404.
what's working now: frontmatter parsed with gray-matter, generateStaticParams + dynamicParams = false so the whole thing is static at build time, sugar-high for code blocks.
what i still don't understand: why @next/mdx and next-mdx-remote are different things and when you'd pick one over the other. also whether i actually need remark-gfm or if it's already included somewhere.
going to bed. will figure out the typeset css tomorrow.