diff --git a/public/css/mobile.css b/public/css/mobile.css
index ccc5c87..41ab2e9 100644
--- a/public/css/mobile.css
+++ b/public/css/mobile.css
@@ -342,6 +342,27 @@
transform: rotate(180deg);
}
+ /* Refresh button: hidden by default, revealed only when the card is open.
+ Larger than the desktop icon to give a comfortable touch target (≥44px). */
+ .m-card__head .m-refresh-icon {
+ display: none;
+ }
+
+ .m-card.is-open .m-card__head .m-refresh-icon {
+ display: grid;
+ width: 44px;
+ height: 44px;
+ margin-left: 0;
+ font-size: 15px;
+ opacity: 0.7;
+ flex-shrink: 0;
+ }
+
+ .m-card.is-open .m-card__head .m-refresh-icon:active {
+ opacity: 1;
+ color: var(--accent);
+ }
+
.m-card__body {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
diff --git a/public/js/players.js b/public/js/players.js
index 7334ee4..ffd4024 100644
--- a/public/js/players.js
+++ b/public/js/players.js
@@ -131,10 +131,16 @@ async function clearCache() {
// Refreshes both the current rating and the prediction in one click, then
// re-swaps the table so every derived value (deltas, pills, sparkline) reflects
// the new state. Cheaper than fine-grained DOM updates and guaranteed consistent
-// because the server renders the truth.
+// because the server renders the truth. The mobile cards partial is included
+// inside ratings-table, so swapping #ratings-table re-renders both views at once.
async function refreshPlayerData(pdgaNumber) {
- const icon = document.querySelector(`#row-${pdgaNumber} .cell-actions .refresh-icon`);
- if (icon) icon.classList.add('spinning');
+ // The desktop row exists in the DOM even on mobile (hidden via CSS), so spin
+ // both possible icons; only the one visible in the active viewport is seen.
+ const icons = [
+ document.querySelector(`#row-${pdgaNumber} .cell-actions .refresh-icon`),
+ document.querySelector(`#m-card-${pdgaNumber} .m-refresh-icon`)
+ ].filter(Boolean);
+ icons.forEach(icon => icon.classList.add('spinning'));
try {
await Promise.allSettled([
fetch(`/api/refresh-player/${pdgaNumber}`, { method: 'POST' }),
@@ -144,7 +150,7 @@ async function refreshPlayerData(pdgaNumber) {
} catch (error) {
console.error('Error refreshing player data:', error);
} finally {
- if (icon) icon.classList.remove('spinning');
+ icons.forEach(icon => icon.classList.remove('spinning'));
}
}
diff --git a/views/partials/ratings-cards.ejs b/views/partials/ratings-cards.ejs
index 009de31..eec74ad 100644
--- a/views/partials/ratings-cards.ejs
+++ b/views/partials/ratings-cards.ejs
@@ -65,6 +65,11 @@ function renderSparkline(values, opts) {
<%= player.name %>#<%= player.pdgaNumber %>
+
▼