diff --git a/server/public/home.html b/server/public/home.html
index eac7b7d..81e42c7 100644
--- a/server/public/home.html
+++ b/server/public/home.html
@@ -3420,6 +3420,11 @@ let _openUnread=0; // set by selectChat (the unread count before it's reset) so
function draftKey(kind,id){ return 'draft_'+((ME&&ME.id)||'')+'_'+kind+':'+id; }
function getDraft(kind,id){ try{ return localStorage.getItem(draftKey(kind,id))||''; }catch(_){ return ''; } }
function setDraft(kind,id,val){ try{ if(val&&val.trim()) localStorage.setItem(draftKey(kind,id), val); else localStorage.removeItem(draftKey(kind,id)); }catch(_){} }
+// #14: GUARANTEED save of the currently-open chat's draft — called the instant we leave it (switching chats
+// OR going back to the list). The per-keystroke 'input' save can be missed on mobile (predictive text / a
+// fast tap-away fires no final input event), which lost the whole draft on switch-and-return. Snapshotting
+// the composer value at leave-time makes it reliable regardless. Skipped while editing (that's not a draft).
+function persistCurrentDraft(){ try{ if(selected && !editTarget){ const inp=document.getElementById('msgInput'); if(inp) setDraft(selected.kind, selected.id, inp.value); } }catch(_){} }
// Compositor-only open slide (workflow-verified). armOpenSlide() runs AFTER openConvo's synchronous
// render: it pins the pane off-screen (chat-opening), lets a frame paint it, then adds .chat-anim on a
// double-rAF so the compositor runs the 300ms transform with the main thread idle. All scrollTop/innerHTML
@@ -3810,6 +3815,7 @@ function openPollModal(gid){
}
async function selectChat(kind,id){
ensureNotifyPermission();
+ persistCurrentDraft(); // #14: snapshot the outgoing chat's draft BEFORE we swap (reliable across the switch)
stopTyping(); // leaving the previous conversation → tell that peer we stopped
if(_selMode) exitSelect(); // #1: leave forward-selection when changing chats
selected={kind,id};
@@ -6045,6 +6051,7 @@ function switchTab(tab){
if(tab==='meeting' && meetState==='idle'){ renderMeetingLobby(); }
}
function showWelcome(skipAnim){
+ persistCurrentDraft(); // #14: save the open chat's draft before we leave it for the list (back button)
const finish=()=>{ selected=null; document.body.classList.remove('chat-open','chat-closing','chat-dragging'); renderChats(searchVal()); renderChatPanel(); updateRailUnread(); };
// Mobile: slide the outgoing conversation off to the right before swapping back to the chat list, so
// "back" has a clear push/pop direction (like Teams/WhatsApp). Desktop just closes instantly.