diff --git a/containers/libreportal/frontend/components/apps/overview/js/overview-manager.js b/containers/libreportal/frontend/components/apps/overview/js/overview-manager.js
index be58c85..dae77d6 100644
--- a/containers/libreportal/frontend/components/apps/overview/js/overview-manager.js
+++ b/containers/libreportal/frontend/components/apps/overview/js/overview-manager.js
@@ -748,9 +748,25 @@ class OverviewManager {
const newer = a.newer_version
? `${esc(a.newer_version)} available`
: '';
+ // Two different actions, and the row shows whichever applies.
+ //
+ // `update` pulls a new image for the tag you already track — one click, low
+ // risk, primary styling.
+ //
+ // `upgrade` moves to a newer release LINE, which needs the pinned version
+ // changed and walks the releases one at a time. It used to live only inside
+ // the row detail, on the reasoning that it wants more care than a row button.
+ // But the care is already there: updater-page's upgrade() opens a confirmation
+ // spelling out the snapshot-per-step, the rollback behaviour and the likely
+ // duration, and nothing runs until that is accepted. Hiding the button bought
+ // no safety — it just left rows reading "CRITICAL · V1.159.0 AVAILABLE" with
+ // no way to act on them, which is exactly how it was reported. Secondary
+ // styling keeps the two visibly different.
const updBtn = a.update_available
? ``
- : '';
+ : (a.newer_version
+ ? ``
+ : '');
// Only rows with something to apply are selectable. A checkbox on a row
// that is already current would offer a choice with no outcome, and
// "selected 6, updated 2" is a worse answer than not offering the 4.