diff --git a/frontend/index.html b/frontend/index.html
index fb8b0f3..6c87cd8 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -9,6 +9,10 @@
// hidden — force light regardless of stored/OS preference. To re-enable,
// restore the localStorage/prefers-color-scheme logic and unhide the toggle.
document.documentElement.setAttribute('data-theme', 'light');
+ // Role is unknown until Keycloak check-sso resolves (async). Start in the
+ // "pending" state that hides manager-only controls, so anonymous visitors
+ // never see a flash of them before applyRoleUi() runs.
+ document.documentElement.classList.add('role-pending');
@@ -47,7 +51,7 @@
-
+
-
+
-
+
-
+
@@ -128,7 +132,7 @@
-
+
diff --git a/frontend/src/main.ts b/frontend/src/main.ts
index 6a49ba8..bb66ed2 100644
--- a/frontend/src/main.ts
+++ b/frontend/src/main.ts
@@ -59,6 +59,9 @@ const el = {
// sense for the full-data (manager) tier so the anonymous view stays clean.
function applyRoleUi(): void {
const manager = isManager();
+ // Role resolved — leave the pre-paint "pending" state; from here explicit
+ // per-element display below is the source of truth.
+ document.documentElement.classList.remove('role-pending');
const hideField = (node: Element | null): void => {
const f = node?.closest('.field') as HTMLElement | null;
if (f) f.style.display = manager ? '' : 'none';
diff --git a/frontend/src/styles.css b/frontend/src/styles.css
index 8a0295e..d802aa1 100644
--- a/frontend/src/styles.css
+++ b/frontend/src/styles.css
@@ -313,6 +313,11 @@ input::placeholder { color: var(--text-muted); }
}
.theme-toggle:hover { border-color: var(--border-strong); background: var(--surface); }
+/* Anti-flash: until the role is resolved (Keycloak check-sso), hide every
+ manager-only control so anonymous visitors never see them flash on load.
+ applyRoleUi() drops .role-pending and then sets each element's final state. */
+html.role-pending .manager-only { display: none !important; }
+
/* Login / logout button — pinned to the header's top-right corner */
.auth-wrap { position: absolute; right: 18px; top: 12px; z-index: 3; }