credits: hide AI Lab affiliation only for alz@bii.dk

Restore the "Part of AI Lab" credit block in all three views and hide
it client-side once /auth/me (or fresh login) identifies the viewer as
alz@bii.dk. Everyone else sees it as before. Marked with data-ailab;
hidden via inline display:none since the flex containers override the
[hidden] attribute. No inline scripts — logic lives in the existing
page JS, CSP stays script-src 'self'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan Hvid 2026-06-10 15:43:14 +02:00
parent b4babd82d5
commit b3bd5e4af6
6 changed files with 69 additions and 0 deletions

View file

@ -2681,6 +2681,11 @@ html {
<span class="support-credit-name">Innofounder</span>
<span class="support-credit-auth">Innovationsfonden</span>
</div>
<div class="support-credit" data-ailab>
<span class="support-credit-label">Part of</span>
<span class="support-credit-name">AI Lab</span>
<span class="support-credit-auth">BioInnovation Institute</span>
</div>
<div class="support-credit">
<span class="support-credit-label">Part of</span>
<span class="support-credit-name">The Regulatory AI-Sandbox</span>

View file

@ -166,6 +166,11 @@
<span class="m-credit-name">Innofounder</span>
<span class="m-credit-auth">Innovationsfonden</span>
</div>
<div class="m-credit" data-ailab>
<span class="m-credit-label">Part of</span>
<span class="m-credit-name">AI Lab</span>
<span class="m-credit-auth">BioInnovation Institute</span>
</div>
<div class="m-credit">
<span class="m-credit-label">Part of</span>
<span class="m-credit-name">The Regulatory AI-Sandbox</span>

View file

@ -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

View file

@ -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

View file

@ -377,6 +377,11 @@
<span class="welcome-credit-name">Innofounder</span>
<span class="welcome-credit-auth">Innovationsfonden</span>
</div>
<div class="welcome-credit" data-ailab>
<span class="welcome-credit-label">Part of</span>
<span class="welcome-credit-name">AI Lab</span>
<span class="welcome-credit-auth">BioInnovation Institute</span>
</div>
<div class="welcome-credit">
<span class="welcome-credit-label">Part of</span>
<span class="welcome-credit-name">The Regulatory AI-Sandbox</span>

View file

@ -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 */ }