You are correct that you definitely don't want to put anything sensitive into .env files that are committed into VCS. Most people do use gitignored .env files in some capacity for sensitive config during local dev. Whether you commit any .env files at all (for non-sensitive values) and use a combination of files and runtime env vars is really a matter of personal preference.
You are also correct that NEXT_PUBLIC_ env vars are considered both "public" (not sensitive) and "static" (bundled in at build time). The coupling of these concepts is a bit annoying, but it is how many frameworks work.
Your client code can only access these process.env.NEXT_PUBLIC_... vars, while your server code does have access to everything. What makes it confusing is that Next.js is doing a ton of magic, so it's not always obvious if you are operating within the client or server. It is also not always obvious if non bundled env vars are being used during static pre-rendering, effectively making them "static".
For many cases this setup is fine, but if you are needing any (non-sensitive) config in your client code that you want to be able to change at boot time, you'll need to wire up an additional API endpoint that fetches it and returns it to the client. Luckily, most situations don't need this...
As for how to get production secrets into your system, it depends a bit on how you are deploying your app, but generally folks will set those env vars within the platforms proprietary environment variable management UI. On most platforms (ex: Vercel/Netlify/Cloudflare), when you change env vars in their UI, it will trigger a new build and deploy. So effectively they are always being set at build time, regardless of if they are bundled into the code or not. If you are hosting things yourself, you'll need to pass env vars into the boot command / docker container / etc...
While most people get by, the whole setup leaves much to be desired, and it's easy to make mistakes that are confusing reason about.
If you want a more robust config system that tries to solve many of these issues and provide guardrails, check out dmno and its Next.js Integration. It provides validations, type safety with awesome intellisense, leak detection, tighter control over dynamic/static config, and the ability to pull data from backends like 1password. Full disclosure - I am one of the creators.