Index: src/assets/js/mod/lib.js
===================================================================
--- src/assets/js/mod/lib.js	(revision 9a22776edd793fd25b143973a722da5f5b5dfaab)
+++ src/assets/js/mod/lib.js	(revision 59f404ac3fc5229fc0676774f093cbf7eaa661c3)
@@ -64,2 +64,27 @@
   });
 }
+
+/**
+ * De WebAuthn-bibliotheek erbij halen, vanuit JavaScript (shaer-0i6).
+ *
+ * Hij stond als `<script src>` in paid-gate.ejs en paid-passkey.ejs, en dat
+ * werkt alleen bij een volledige laadbeurt. Kom je op zo'n pagina via een link
+ * BINNEN de site, dan is het een htmx-fragment met de nonce van een ANDER
+ * verzoek, en weigert de CSP hem. Het gevolg was stil: de module laadde wel,
+ * vond `window.SimpleWebAuthnBrowser` niet, en de knop deed niets -- op het
+ * passkey- en betaalpad.
+ *
+ * Een dynamische import vanuit deze module mag wel: de shell startte hem met
+ * een geldige nonce, en `strict-dynamic` bestaat er juist voor dat vertrouwde
+ * scripts verder mogen laden. Het bestand is een UMD-bundel; zonder CommonJS
+ * of AMD zet die zichzelf op globalThis, dus na afloop staat de global er.
+ *
+ * Faalt het laden, dan geeft dit undefined terug en niet een fout: beide
+ * aanroepers hebben al een nette terugval voor "geen WebAuthn hier".
+ */
+export async function loadWebAuthn() {
+  if (window.SimpleWebAuthnBrowser) return window.SimpleWebAuthnBrowser;
+  try { await import('/assets/vendor/simplewebauthn-browser.umd.min.js'); }
+  catch (e) { console.warn('[paid] WebAuthn-bibliotheek laadt niet:', e && e.message); }
+  return window.SimpleWebAuthnBrowser;
+}
Index: src/assets/js/mod/paid-gate.js
===================================================================
--- src/assets/js/mod/paid-gate.js	(revision 9a22776edd793fd25b143973a722da5f5b5dfaab)
+++ src/assets/js/mod/paid-gate.js	(revision 59f404ac3fc5229fc0676774f093cbf7eaa661c3)
@@ -5,9 +5,14 @@
 // interpolatie kan niet in een statisch bestand.
 
-import { pageData } from './lib.js';
+import { pageData, loadWebAuthn } from './lib.js';
 
 // Element-bedrading leeft zo lang als de pagina; de bootstrap roept init()
 // aan bij elke paginawissel waarop deze module actief is (shaer-5s1).
-export function init() { run(); }
+//
+// EERST de bibliotheek, dan pas bedraden (shaer-0i6): die stond als
+// `<script src>` in de pagina en overleefde een htmx-navigatie niet. Wacht
+// run() daar niet op, dan valt hij terug op "geen WebAuthn" en verbergt hij
+// de ontgrendelknop op een pagina waar ontgrendelen gewoon kan.
+export async function init() { await loadWebAuthn(); run(); }
 
 function run() {
Index: src/assets/js/mod/paid-passkey.js
===================================================================
--- src/assets/js/mod/paid-passkey.js	(revision 9a22776edd793fd25b143973a722da5f5b5dfaab)
+++ src/assets/js/mod/paid-passkey.js	(revision 59f404ac3fc5229fc0676774f093cbf7eaa661c3)
@@ -5,9 +5,14 @@
 // interpolatie kan niet in een statisch bestand.
 
-import { pageData } from './lib.js';
+import { pageData, loadWebAuthn } from './lib.js';
 
 // Element-bedrading leeft zo lang als de pagina; de bootstrap roept init()
 // aan bij elke paginawissel waarop deze module actief is (shaer-5s1).
-export function init() { run(); }
+//
+// EERST de bibliotheek, dan pas bedraden (shaer-0i6): die stond als
+// `<script src>` in de pagina en overleefde een htmx-navigatie niet. Wacht
+// run() daar niet op, dan ziet hij de global nog niet staan en zet hij de
+// knop uit terwijl er niets aan de hand is.
+export async function init() { await loadWebAuthn(); run(); }
 
 function run() {
