diff --git a/protected/index.html b/protected/index.html
index bcb5ad2..e115a1a 100644
--- a/protected/index.html
+++ b/protected/index.html
@@ -2681,6 +2681,11 @@ html {
Innofounder
Innovationsfonden
+
+ Part of
+ AI Lab
+ BioInnovation Institute
+
Part of
The Regulatory AI-Sandbox
diff --git a/protected/mobile/index.html b/protected/mobile/index.html
index ea32f57..db0c44b 100644
--- a/protected/mobile/index.html
+++ b/protected/mobile/index.html
@@ -166,6 +166,11 @@
Innofounder
Innovationsfonden
+
+ Part of
+ AI Lab
+ BioInnovation Institute
+
Part of
The Regulatory AI-Sandbox
diff --git a/protected/mobile/mobile.js b/protected/mobile/mobile.js
index 20e02d2..429a99f 100644
--- a/protected/mobile/mobile.js
+++ b/protected/mobile/mobile.js
@@ -21,12 +21,30 @@
// protected/mobile/index.html and never by the desktop view.
// ─────────────────────────────────────────────────────────────
+/* ───── AI Lab credit visibility ─────
+ The "Part of AI Lab" affiliation credit is shown to everyone except
+ one viewer, who has asked that it never appear while they present.
+ Hidden client-side once /auth/me returns the email. Compared
+ lower-cased so casing doesn't matter; the container is display:flex,
+ so an inline display:none is used (it beats the stylesheet rule,
+ where the [hidden] attribute would not). ───── */
+const AILAB_HIDE_EMAIL = 'alz@bii.dk';
+function applyAilabVisibility(email) {
+ if (!email || email.trim().toLowerCase() !== AILAB_HIDE_EMAIL) return;
+ document.querySelectorAll('[data-ailab]').forEach((el) => {
+ el.style.display = 'none';
+ });
+}
+
(async function checkSession() {
try {
const res = await fetch('/auth/me', { credentials: 'same-origin' });
if (!res.ok) {
window.location.href = '/';
+ return;
}
+ const data = await res.json().catch(() => ({}));
+ applyAilabVisibility(data.email);
} catch {
// Network error — do not boot the user out; desktop behaviour is
// the same. If the next action actually needs the server, we'll
diff --git a/protected/timeline.js b/protected/timeline.js
index 3b885f6..a05ed7f 100644
--- a/protected/timeline.js
+++ b/protected/timeline.js
@@ -465,6 +465,22 @@ document.querySelectorAll('.dot-btn').forEach(btn => {
});
+/* ─────────────────────────────────────────────────────────────
+ AI Lab credit visibility — the "Part of AI Lab" affiliation in the
+ hero foot is shown to everyone except one viewer, who has asked that
+ it never appear while they present. Hidden client-side once /auth/me
+ returns the email. Compared lower-cased so casing doesn't matter;
+ the container is display:flex, so an inline display:none is used (it
+ beats the stylesheet rule, where the [hidden] attribute would not).
+ ───────────────────────────────────────────────────────────── */
+const AILAB_HIDE_EMAIL = 'alz@bii.dk';
+function applyAilabVisibility(email) {
+ if (!email || email.trim().toLowerCase() !== AILAB_HIDE_EMAIL) return;
+ document.querySelectorAll('[data-ailab]').forEach((el) => {
+ el.style.display = 'none';
+ });
+}
+
/* ─────────────────────────────────────────────────────────────
First name propagation — fetched from /auth/me on load.
Used by Scene 3 ("This is why we've invited you, [Name].").
@@ -477,6 +493,7 @@ document.querySelectorAll('.dot-btn').forEach(btn => {
if (res.ok) {
const data = await res.json().catch(() => ({}));
window.__fenjaFirstName = data.firstName || null;
+ applyAilabVisibility(data.email);
}
} catch {
// Offline — leave undefined; bifrost.js falls back to the
diff --git a/public/entrance.html b/public/entrance.html
index 4ab2542..bcdb8ee 100644
--- a/public/entrance.html
+++ b/public/entrance.html
@@ -377,6 +377,11 @@
Innofounder
Innovationsfonden
+
+ Part of
+ AI Lab
+ BioInnovation Institute
+
Part of
The Regulatory AI-Sandbox
diff --git a/public/entrance.js b/public/entrance.js
index d0cf192..85f3d41 100644
--- a/public/entrance.js
+++ b/public/entrance.js
@@ -53,6 +53,21 @@
requestAnimationFrame(() => setTimeout(() => wrap.classList.add('is-ready'), 120));
})();
+/* ───── AI Lab credit visibility ─────
+ The "Part of AI Lab" affiliation credit is shown to everyone except
+ one viewer, who has asked that it never appear while they present.
+ Hidden client-side once we know the email — compared lower-cased so
+ casing in the invite list or the typed address doesn't matter. The
+ container is display:flex, so an inline display:none is used (it beats
+ the stylesheet rule; the [hidden] attribute would not). ───── */
+const AILAB_HIDE_EMAIL = 'alz@bii.dk';
+function applyAilabVisibility(email) {
+ if (!email || email.trim().toLowerCase() !== AILAB_HIDE_EMAIL) return;
+ document.querySelectorAll('[data-ailab]').forEach((el) => {
+ el.style.display = 'none';
+ });
+}
+
/* ───── Step transitions ───── */
const steps = {
email: document.getElementById('step-email'),
@@ -146,6 +161,9 @@ emailForm.addEventListener('submit', async (e) => {
// welcome step and advance.
const data = await res.json().catch(() => ({}));
setWelcomeTitle(data.firstName || null);
+ // /auth/login returns {ok, firstName} — no email — so use the
+ // address the visitor just authenticated with.
+ applyAilabVisibility(email);
setAck(emailAck, '', false);
showStep('welcome');
} catch (err) {
@@ -173,6 +191,7 @@ document.getElementById('welcome-continue').addEventListener('click', () => {
authed = true;
const data = await res.json().catch(() => ({}));
firstName = data.firstName || null;
+ applyAilabVisibility(data.email);
}
} catch { /* offline — fall through to email */ }