diff --git a/server/public/home.html b/server/public/home.html
index b5e2f7b..fb0fe19 100644
--- a/server/public/home.html
+++ b/server/public/home.html
@@ -4537,8 +4537,13 @@ let _chatReconnectT=null;
let _lastPresenceRefresh=0;
function refreshPresenceOnResume(){
try{
- if(!chatWs || chatWs.readyState>1){ connectChatWs(); } // dead/closed → force reconnect (skip if OPEN/CONNECTING)
- const t=Date.now(); if(t-_lastPresenceRefresh>4000){ _lastPresenceRefresh=t; try{ loadSidebar(); }catch(_){} try{ touchSession(); }catch(_){} } // throttle so rapid tab switches don't spam; also slide the session forward
+ // If the socket is dead/closed, reconnect — its onopen runs resyncChat → loadSidebar, which refreshes
+ // presence AND unread from the server. We deliberately do NOT force a loadSidebar when the socket is fine:
+ // the initial payload now carries last-seen (repos.listByTenant fix) and live presence events keep it
+ // current, so an unconditional focus-refresh only risked clobbering fresh state (it caused the #8 "Offline
+ // for all" regression and could momentarily reset an unread badge).
+ if(!chatWs || chatWs.readyState>1){ connectChatWs(); }
+ const t=Date.now(); if(t-_lastPresenceRefresh>4000){ _lastPresenceRefresh=t; try{ touchSession(); }catch(_){} } // slide the session forward (throttled)
}catch(_){}
}
function connectChatWs(){
diff --git a/server/repos.js b/server/repos.js
index 0c2d89e..da8ea37 100644
--- a/server/repos.js
+++ b/server/repos.js
@@ -38,7 +38,10 @@ const users = {
byBizgazeId: (bizId) => (bizId ? db.prepare('SELECT * FROM users WHERE bizgaze_user_id=?').get(String(bizId)) : undefined),
setBizgazeId: (id, bizId) => db.prepare('UPDATE users SET bizgaze_user_id=? WHERE id=?').run(bizId != null ? String(bizId) : null, id),
listByTenant: (tenantId) =>
- db.prepare('SELECT id,email,name,role,active,avatar_url,created_at FROM users WHERE team_id=?').all(tenantId),
+ // last_seen + status MUST be selected: the contacts/conversations DTOs read x.last_seen / x.status. Omitting
+ // them made every list payload carry lastSeen:null (and status:'active'), so a fresh load showed a bare
+ // "Offline" with no time — last-seen only appeared via live presence events (which read the full row).
+ db.prepare('SELECT id,email,name,role,active,avatar_url,created_at,last_seen,status FROM users WHERE team_id=?').all(tenantId),
inTenant: (id, tenantId) =>
db.prepare('SELECT * FROM users WHERE id=? AND team_id=?').get(id, tenantId),
create: async ({ tenantId, email, hash, salt, role, name, mfaSecret }) => {