The point at which passkeys become visible to customers. Everything before this was reachable only by knowing the endpoints existed. Below the password form rather than above it. Passwords are how every existing customer signs in and a passkey is the alternative, so putting it first would demote the path that works for everyone. Absent entirely where WebAuthn is unavailable, rather than shown disabled. A greyed-out control invites a customer to wonder what they are missing and offers nothing they can act on, and password login is the fallback in every case regardless. The check is read once at render because it decides whether the control exists, not whether pressing it works. The passkey button has its own loading flag rather than sharing the form's. The requirement is that a dismissed prompt leaves a usable password form behind it, and a shared flag would leave that form disabled and spinning while the browser's prompt is open. Dismissing the prompt is a cancellation and shows nothing. NotAllowedError and AbortError are the two the browser raises for it, and reporting either as a failure would tell a customer something went wrong when they changed their mind — leaving a red alert sitting above a form that is working perfectly. Everything else shows a message that says what to do next rather than only that something failed. That message says nothing about whether an account exists, which costs nothing to hold to here because the server already answers every refusal identically. There is also no email on this path at all, so there is nothing to be asked about. The end-to-end test covers the half of this issue that can be proven without an authenticator. The failure is injected at the first request, before the browser prompt, so it needs no credential and cannot hang waiting for a gesture nobody will make — and then the password form behind the error is used to sign in for real. That is the requirement: not a dead end. The other half cannot be tested anywhere but production, and this issue says so itself. Credentials bind to the Relying Party ID, so a passkey registered against QA will not work against production. QA proves the flow, the fallbacks and the copy; production needs its own smoke test with a real registration afterwards, and that is a standing property of the feature rather than a gap in this change. Verified: tsc clean for src and tests, lint clean with no warnings, frontend build green. Closes #41 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>