diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 1e6bb8e..d794d04 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -555,13 +555,20 @@ export default function App() {
if (toastTimer.current !== undefined) { window.clearTimeout(toastTimer.current); toastTimer.current = undefined; }
advanceToast(); // skip to the next queued toast (or clear if none)
}, [advanceToast]);
+ // Status-bar message expanded into a popover (long errors — a TQSL or Club Log
+ // failure — don't fit on one line, and truncating them with no way to read the
+ // rest is useless).
+ const [msgOpen, setMsgOpen] = useState(false);
+ // Reset when the message goes away, or the NEXT one would pop open by itself.
+ useEffect(() => { if (!error && !toast) setMsgOpen(false); }, [error, toast]);
// Error banners auto-dismiss after a few seconds (longer than toasts since
- // they may be multi-line). The X button still closes them immediately.
+ // they may be multi-line). The X button still closes them immediately. Do NOT
+ // dismiss while the operator has it expanded and is reading it.
useEffect(() => {
- if (!error) return;
+ if (!error || msgOpen) return;
const t = window.setTimeout(() => setError(''), 6000);
return () => window.clearTimeout(t);
- }, [error]);
+ }, [error, msgOpen]);
// True while the QSO recorder is capturing the current contact (set when we
// leave the callsign field, cleared on log/cancel). Drives the REC badge.
const [recording, setRecording] = useState(false);
@@ -3358,25 +3365,8 @@ export default function App() {