diff --git a/library/components/poster/PosterWall.vue b/library/components/poster/PosterWall.vue
index 0fcda8c3e..fe3097e40 100644
--- a/library/components/poster/PosterWall.vue
+++ b/library/components/poster/PosterWall.vue
@@ -173,11 +173,23 @@ const props = defineProps({
// before the reader has said otherwise; once they choose, their choice is the default.
const perRow = ref(readStoredPerRow() ?? (props.columns || PER_ROW_DEFAULT))
watch(() => props.columns, (n) => { if (readStoredPerRow() == null) perRow.value = n || PER_ROW_DEFAULT })
-watch(perRow, (n) => { if (Number.isFinite(n) && n >= PER_ROW_MIN && n <= PER_ROW_MAX) storePerRow(n) })
+// ⚠ PER_ROW_AUTO (0) IS STORED TOO, so "Auto" survives a reload like any other choice. Without this
+// clause the range check below rejects 0, nothing is written, and the next visit silently falls back
+// to the default — a preference that forgets only ONE of its values is worse than one that forgets
+// them all, because the reader cannot tell it is broken.
+watch(perRow, (n) => {
+ if (n === PER_ROW_AUTO || (Number.isFinite(n) && n >= PER_ROW_MIN && n <= PER_ROW_MAX)) storePerRow(n)
+})
// The choices the control offers. Two is the Driver's default; the ceiling is the wall's own
// auto-fill behaviour, offered as "fit" so the knob can always get back to where it started.
// Driver: "poster per row should have a minimum of 2 and a max of 5 and a default of 2".
-const PER_ROW_CHOICES = [2, 3, 4, 5]
+// ★ AUTO IS 0, AND 0 IS NOT A COUNT — it is the absence of an override. gridStyle already returns
+// {} for a non-positive value, leaving the stylesheet's auto-fill rule in charge, so Auto needed no
+// new branch in the layout: it needed to become REACHABLE from the control and from storage.
+// This extends the earlier ruling (min 2, max 5, default 2) rather than contradicting it: those
+// bound the COUNTS, and Auto is the option that declines to pick one.
+const PER_ROW_AUTO = 0
+const PER_ROW_CHOICES = [PER_ROW_AUTO, 2, 3, 4, 5]
const PER_ROW_MIN = 2, PER_ROW_MAX = 5, PER_ROW_DEFAULT = 2
// ...and "it needs to honour local storage so if you change the default number it is saved in local
// storage so you can reload and your default you set is persistent". One key for the whole wall,
@@ -188,7 +200,14 @@ const PER_ROW_KEY = 'doxa-maps:poster-wall:per-row'
function readStoredPerRow() {
try {
const raw = window.localStorage.getItem(PER_ROW_KEY)
+ // ⚠ A MISSING KEY MUST NOT READ AS AUTO. getItem returns null when nothing is stored, and
+ // Number(null) is 0 — which is exactly PER_ROW_AUTO now that 0 means something. Without this
+ // guard every first-time reader would silently get Auto instead of the default, i.e. adding an
+ // option would have CHANGED the layout for everyone who never touched the control. Caught by
+ // round-tripping the storage values rather than by reading the code.
+ if (raw == null || raw === '') return null
const n = Number(raw)
+ if (n === PER_ROW_AUTO) return PER_ROW_AUTO
return Number.isFinite(n) && n >= PER_ROW_MIN && n <= PER_ROW_MAX ? n : null
} catch (_) { return null }
}
@@ -1061,7 +1080,7 @@ useShadowStyles(