← back to Wallco Ai
pdp variants: clamp prev link to id>=1 (10 variants) + scheme/quote-sanitize image_url before <img src> across all 17 — codex review pass 2 hardening
3e32b5f9537660b179ecb353a8c9cfb5042b5311 · 2026-05-31 19:22:02 -0700 · Steve Abrams
Files touched
M public/theme-variants/v1-compact.htmlM public/theme-variants/v10-stack.htmlM public/theme-variants/v11-best.htmlM public/theme-variants/v12-roomsplit.htmlM public/theme-variants/v13-roomdrawer.htmlM public/theme-variants/v14-roomzine.htmlM public/theme-variants/v15-roomsticky.htmlM public/theme-variants/v16-roommin.htmlM public/theme-variants/v17-roomshow.htmlM public/theme-variants/v2-bento.htmlM public/theme-variants/v3-tabs.htmlM public/theme-variants/v4-hero.htmlM public/theme-variants/v5-vertical.htmlM public/theme-variants/v6-drawer.htmlM public/theme-variants/v7-editorial.htmlM public/theme-variants/v8-spec.htmlM public/theme-variants/v9-config.html
Diff
commit 3e32b5f9537660b179ecb353a8c9cfb5042b5311
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun May 31 19:22:02 2026 -0700
pdp variants: clamp prev link to id>=1 (10 variants) + scheme/quote-sanitize image_url before <img src> across all 17 — codex review pass 2 hardening
---
public/theme-variants/v1-compact.html | 6 +++---
public/theme-variants/v10-stack.html | 4 ++--
public/theme-variants/v11-best.html | 4 ++--
public/theme-variants/v12-roomsplit.html | 2 +-
public/theme-variants/v13-roomdrawer.html | 2 +-
public/theme-variants/v14-roomzine.html | 2 +-
public/theme-variants/v15-roomsticky.html | 2 +-
public/theme-variants/v16-roommin.html | 2 +-
public/theme-variants/v17-roomshow.html | 2 +-
public/theme-variants/v2-bento.html | 4 ++--
public/theme-variants/v3-tabs.html | 4 ++--
public/theme-variants/v4-hero.html | 6 +++---
public/theme-variants/v5-vertical.html | 2 +-
public/theme-variants/v6-drawer.html | 4 ++--
public/theme-variants/v7-editorial.html | 4 ++--
public/theme-variants/v8-spec.html | 4 ++--
public/theme-variants/v9-config.html | 4 ++--
17 files changed, 29 insertions(+), 29 deletions(-)
diff --git a/public/theme-variants/v1-compact.html b/public/theme-variants/v1-compact.html
index 9ca1118..8b2fc9b 100644
--- a/public/theme-variants/v1-compact.html
+++ b/public/theme-variants/v1-compact.html
@@ -374,7 +374,7 @@
const isMural = design.kind === 'mural_panel' || design.kind === 'mural';
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#888';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const w = Number(design.width_in) || 24;
const h = Number(design.height_in) || 24;
const repeatLabel = w + '"' + (w !== h ? ' × ' + h + '"' : '');
@@ -397,7 +397,7 @@
<a href="/designs?cat=${encodeURIComponent(cat)}">${escapeHtml(catDisplay)}</a>
<span class="grow"></span>
<span class="nav">
- <a href="/design/${id - 2}?theme=compact">← prev</a>
+ <a href="/design/${Math.max(1, id - 2)}?theme=compact">← prev</a>
<a href="/design/${id + 2}?theme=compact">next →</a>
</span>
</div>
@@ -422,7 +422,7 @@
</div>
<div class="prevnext">
- <a href="/design/${id - 2}?theme=compact">← prev</a>
+ <a href="/design/${Math.max(1, id - 2)}?theme=compact">← prev</a>
<a href="/design/${id + 2}?theme=compact">next →</a>
<span class="grow"></span>
<span>Pattern No.${id}</span>
diff --git a/public/theme-variants/v10-stack.html b/public/theme-variants/v10-stack.html
index b24f3de..db8331f 100644
--- a/public/theme-variants/v10-stack.html
+++ b/public/theme-variants/v10-stack.html
@@ -185,7 +185,7 @@
const isMural = design.kind === 'mural_panel' || design.kind === 'mural';
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#888';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const w = Number(design.width_in) || 24, h = Number(design.height_in) || 24;
const repeatLabel = w + '"' + (w !== h ? ' × ' + h + '"' : '');
const rollOrPanel = isMural ? 'Per panel (36" × 11 ft)' : 'Per roll (27 ft)';
@@ -200,7 +200,7 @@
<div class="crumb">
<a href="/designs">Designs</a><span>›</span><a href="/designs?cat=${encodeURIComponent(cat)}">${escapeHtml(catDisplay)}</a>
<span class="grow"></span>
- <span><a href="/design/${id - 2}?theme=stack">← prev</a> · <a href="/design/${id + 2}?theme=stack">next →</a></span>
+ <span><a href="/design/${Math.max(1, id - 2)}?theme=stack">← prev</a> · <a href="/design/${id + 2}?theme=stack">next →</a></span>
</div>
<main class="stack">
diff --git a/public/theme-variants/v11-best.html b/public/theme-variants/v11-best.html
index 336a1d3..8bf1f2f 100644
--- a/public/theme-variants/v11-best.html
+++ b/public/theme-variants/v11-best.html
@@ -301,7 +301,7 @@
const isMural = design.kind === 'mural_panel' || design.kind === 'mural';
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#888';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const w = Number(design.width_in) || 24, h = Number(design.height_in) || 24;
const repeatLabel = w + '"' + (w !== h ? ' × ' + h + '"' : '');
const rollOrPanel = isMural ? 'Per panel (36" × 11 ft)' : 'Per roll (27 ft)';
@@ -316,7 +316,7 @@
<div class="crumb">
<a href="/designs">Designs</a><span>›</span><a href="/designs?cat=${encodeURIComponent(cat)}">${escapeHtml(catDisplay)}</a>
<span class="grow"></span>
- <span class="nav"><a href="/design/${id - 2}?theme=best">← prev</a> · <a href="/design/${id + 2}?theme=best">next →</a></span>
+ <span class="nav"><a href="/design/${Math.max(1, id - 2)}?theme=best">← prev</a> · <a href="/design/${id + 2}?theme=best">next →</a></span>
</div>
<main class="best">
diff --git a/public/theme-variants/v12-roomsplit.html b/public/theme-variants/v12-roomsplit.html
index a5f16f9..a90cedb 100644
--- a/public/theme-variants/v12-roomsplit.html
+++ b/public/theme-variants/v12-roomsplit.html
@@ -201,7 +201,7 @@
const kindLabel = design.kind === 'seamless_tile' ? 'Seamless Tile (24×24")' : isMural ? 'Mural Panel' : (design.kind || 'pattern');
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#999';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const handle = design.handle || ('wallco-' + id);
const generator = design.generator || 'wallco.ai';
const created = design.created_at ? new Date(design.created_at) : null;
diff --git a/public/theme-variants/v13-roomdrawer.html b/public/theme-variants/v13-roomdrawer.html
index a04f180..2548b1c 100644
--- a/public/theme-variants/v13-roomdrawer.html
+++ b/public/theme-variants/v13-roomdrawer.html
@@ -201,7 +201,7 @@
const kindLabel = design.kind === 'seamless_tile' ? 'Seamless Tile (24×24")' : isMural ? 'Mural Panel' : (design.kind || 'pattern');
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#999';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const handle = design.handle || ('wallco-' + id);
const generator = design.generator || 'wallco.ai';
const created = design.created_at ? new Date(design.created_at) : null;
diff --git a/public/theme-variants/v14-roomzine.html b/public/theme-variants/v14-roomzine.html
index e3d3a60..30bddc1 100644
--- a/public/theme-variants/v14-roomzine.html
+++ b/public/theme-variants/v14-roomzine.html
@@ -201,7 +201,7 @@
const kindLabel = design.kind === 'seamless_tile' ? 'Seamless Tile (24×24")' : isMural ? 'Mural Panel' : (design.kind || 'pattern');
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#999';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const handle = design.handle || ('wallco-' + id);
const generator = design.generator || 'wallco.ai';
const created = design.created_at ? new Date(design.created_at) : null;
diff --git a/public/theme-variants/v15-roomsticky.html b/public/theme-variants/v15-roomsticky.html
index d1b58c2..a2481e5 100644
--- a/public/theme-variants/v15-roomsticky.html
+++ b/public/theme-variants/v15-roomsticky.html
@@ -201,7 +201,7 @@
const kindLabel = design.kind === 'seamless_tile' ? 'Seamless Tile (24×24")' : isMural ? 'Mural Panel' : (design.kind || 'pattern');
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#999';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const handle = design.handle || ('wallco-' + id);
const generator = design.generator || 'wallco.ai';
const created = design.created_at ? new Date(design.created_at) : null;
diff --git a/public/theme-variants/v16-roommin.html b/public/theme-variants/v16-roommin.html
index 5369b06..0e25271 100644
--- a/public/theme-variants/v16-roommin.html
+++ b/public/theme-variants/v16-roommin.html
@@ -201,7 +201,7 @@
const kindLabel = design.kind === 'seamless_tile' ? 'Seamless Tile (24×24")' : isMural ? 'Mural Panel' : (design.kind || 'pattern');
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#999';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const handle = design.handle || ('wallco-' + id);
const generator = design.generator || 'wallco.ai';
const created = design.created_at ? new Date(design.created_at) : null;
diff --git a/public/theme-variants/v17-roomshow.html b/public/theme-variants/v17-roomshow.html
index dc2ebab..694eb3d 100644
--- a/public/theme-variants/v17-roomshow.html
+++ b/public/theme-variants/v17-roomshow.html
@@ -201,7 +201,7 @@
const kindLabel = design.kind === 'seamless_tile' ? 'Seamless Tile (24×24")' : isMural ? 'Mural Panel' : (design.kind || 'pattern');
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#999';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const handle = design.handle || ('wallco-' + id);
const generator = design.generator || 'wallco.ai';
const created = design.created_at ? new Date(design.created_at) : null;
diff --git a/public/theme-variants/v2-bento.html b/public/theme-variants/v2-bento.html
index 93cfaea..8c305a3 100644
--- a/public/theme-variants/v2-bento.html
+++ b/public/theme-variants/v2-bento.html
@@ -353,7 +353,7 @@
const isMural = design.kind === 'mural_panel' || design.kind === 'mural';
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#888';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const w = Number(design.width_in) || 24;
const h = Number(design.height_in) || 24;
const repeatLabel = w + '"' + (w !== h ? ' × ' + h + '"' : '');
@@ -377,7 +377,7 @@
<a href="/designs?cat=${encodeURIComponent(cat)}">${escapeHtml(catDisplay)}</a>
<span class="grow"></span>
<span class="nav">
- <a href="/design/${id - 2}?theme=bento">← prev</a>
+ <a href="/design/${Math.max(1, id - 2)}?theme=bento">← prev</a>
<a href="/design/${id + 2}?theme=bento">next →</a>
</span>
</div>
diff --git a/public/theme-variants/v3-tabs.html b/public/theme-variants/v3-tabs.html
index bea61cd..b0115a1 100644
--- a/public/theme-variants/v3-tabs.html
+++ b/public/theme-variants/v3-tabs.html
@@ -377,7 +377,7 @@
const isMural = design.kind === 'mural_panel' || design.kind === 'mural';
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#888';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const w = Number(design.width_in) || 24;
const h = Number(design.height_in) || 24;
const repeatLabel = w + '"' + (w !== h ? ' × ' + h + '"' : '');
@@ -400,7 +400,7 @@
<a href="/designs?cat=${encodeURIComponent(cat)}">${escapeHtml(catDisplay)}</a>
<span class="grow"></span>
<span class="nav">
- <a href="/design/${id - 2}?theme=tabs">← prev</a>
+ <a href="/design/${Math.max(1, id - 2)}?theme=tabs">← prev</a>
<a href="/design/${id + 2}?theme=tabs">next →</a>
</span>
</div>
diff --git a/public/theme-variants/v4-hero.html b/public/theme-variants/v4-hero.html
index 3f309e0..15054b0 100644
--- a/public/theme-variants/v4-hero.html
+++ b/public/theme-variants/v4-hero.html
@@ -316,7 +316,7 @@
const isMural = design.kind === 'mural_panel' || design.kind === 'mural';
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#888';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const w = Number(design.width_in) || 24;
const h = Number(design.height_in) || 24;
const repeatLabel = w + '"' + (w !== h ? ' × ' + h + '"' : '');
@@ -357,7 +357,7 @@
<a href="/designs?cat=${encodeURIComponent(cat)}">${escapeHtml(catDisplay)}</a>
<span class="grow"></span>
<span class="nav">
- <a href="/design/${id - 2}?theme=hero">← prev</a>
+ <a href="/design/${Math.max(1, id - 2)}?theme=hero">← prev</a>
<a href="/design/${id + 2}?theme=hero">next →</a>
</span>
</div>
@@ -527,7 +527,7 @@
</main>
<div class="prevnext-band">
- <a href="/design/${id - 2}?theme=hero">← previous pattern</a>
+ <a href="/design/${Math.max(1, id - 2)}?theme=hero">← previous pattern</a>
<span class="grow"></span>
<a href="/design/${id + 2}?theme=hero">next pattern →</a>
</div>
diff --git a/public/theme-variants/v5-vertical.html b/public/theme-variants/v5-vertical.html
index f813035..71394aa 100644
--- a/public/theme-variants/v5-vertical.html
+++ b/public/theme-variants/v5-vertical.html
@@ -219,7 +219,7 @@
const isMural = design.kind === 'mural_panel' || design.kind === 'mural';
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#888';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const w = Number(design.width_in) || 24, h = Number(design.height_in) || 24;
const repeatLabel = w + '"' + (w !== h ? ' × ' + h + '"' : '');
const rollOrPanel = isMural ? 'Per panel (36" × 11 ft)' : 'Per roll (27 ft)';
diff --git a/public/theme-variants/v6-drawer.html b/public/theme-variants/v6-drawer.html
index 0e9088b..4f8d8ea 100644
--- a/public/theme-variants/v6-drawer.html
+++ b/public/theme-variants/v6-drawer.html
@@ -220,7 +220,7 @@
const isMural = design.kind === 'mural_panel' || design.kind === 'mural';
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#888';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const w = Number(design.width_in) || 24, h = Number(design.height_in) || 24;
const repeatLabel = w + '"' + (w !== h ? ' × ' + h + '"' : '');
const rollOrPanel = isMural ? 'Per panel (36" × 11 ft)' : 'Per roll (27 ft)';
@@ -235,7 +235,7 @@
<div class="crumb">
<a href="/designs">Designs</a><span>›</span><a href="/designs?cat=${encodeURIComponent(cat)}">${escapeHtml(catDisplay)}</a>
<span class="grow"></span>
- <span><a href="/design/${id - 2}?theme=drawer">← prev</a> · <a href="/design/${id + 2}?theme=drawer">next →</a></span>
+ <span><a href="/design/${Math.max(1, id - 2)}?theme=drawer">← prev</a> · <a href="/design/${id + 2}?theme=drawer">next →</a></span>
</div>
<main class="minimal">
diff --git a/public/theme-variants/v7-editorial.html b/public/theme-variants/v7-editorial.html
index a5c2ceb..18995c1 100644
--- a/public/theme-variants/v7-editorial.html
+++ b/public/theme-variants/v7-editorial.html
@@ -211,7 +211,7 @@
const isMural = design.kind === 'mural_panel' || design.kind === 'mural';
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#888';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const w = Number(design.width_in) || 24, h = Number(design.height_in) || 24;
const repeatLabel = w + '"' + (w !== h ? ' × ' + h + '"' : '');
const rollOrPanel = isMural ? 'Per panel (36" × 11 ft)' : 'Per roll (27 ft)';
@@ -244,7 +244,7 @@
<div class="crumb">
<a href="/designs">Designs</a><span>›</span><a href="/designs?cat=${encodeURIComponent(cat)}">${escapeHtml(catDisplay)}</a>
<span class="grow"></span>
- <span><a href="/design/${id - 2}?theme=editorial">← prev</a> · <a href="/design/${id + 2}?theme=editorial">next →</a></span>
+ <span><a href="/design/${Math.max(1, id - 2)}?theme=editorial">← prev</a> · <a href="/design/${id + 2}?theme=editorial">next →</a></span>
</div>
<main class="editorial">
diff --git a/public/theme-variants/v8-spec.html b/public/theme-variants/v8-spec.html
index 705ddb9..c10e073 100644
--- a/public/theme-variants/v8-spec.html
+++ b/public/theme-variants/v8-spec.html
@@ -186,7 +186,7 @@
const isMural = design.kind === 'mural_panel' || design.kind === 'mural';
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#888';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const w = Number(design.width_in) || 24, h = Number(design.height_in) || 24;
const repeatLabel = w + '"' + (w !== h ? ' × ' + h + '"' : '');
const rollOrPanel = isMural ? 'Per panel (36" × 11 ft)' : 'Per roll (27 ft)';
@@ -202,7 +202,7 @@
<div class="crumb">
<a href="/designs">Designs</a><span>›</span><a href="/designs?cat=${encodeURIComponent(cat)}">${escapeHtml(catDisplay)}</a>
<span class="grow"></span>
- <span><a href="/design/${id - 2}?theme=spec">← prev</a> · <a href="/design/${id + 2}?theme=spec">next →</a></span>
+ <span><a href="/design/${Math.max(1, id - 2)}?theme=spec">← prev</a> · <a href="/design/${id + 2}?theme=spec">next →</a></span>
</div>
<main class="specsheet">
diff --git a/public/theme-variants/v9-config.html b/public/theme-variants/v9-config.html
index 7ae6963..52cac45 100644
--- a/public/theme-variants/v9-config.html
+++ b/public/theme-variants/v9-config.html
@@ -190,7 +190,7 @@
const isMural = design.kind === 'mural_panel' || design.kind === 'mural';
const palette = Array.isArray(design.palette) ? design.palette : [];
const dominant = design.dominant_hex || (palette[0] && palette[0].hex) || '#888';
- const img = design.image_url || ('/designs/img/by-id/' + id);
+ const img = (/^(\/[^"'<>\s]*|https?:\/\/[^"'<>\s]*)$/i.test(String(design.image_url||'')) ? design.image_url : null) || ('/designs/img/by-id/' + id);
const w = Number(design.width_in) || 24, h = Number(design.height_in) || 24;
const repeatLabel = w + '"' + (w !== h ? ' × ' + h + '"' : '');
const rollOrPanel = isMural ? 'Per panel (36" × 11 ft)' : 'Per roll (27 ft)';
@@ -205,7 +205,7 @@
<div class="crumb">
<a href="/designs">Designs</a><span>›</span><a href="/designs?cat=${encodeURIComponent(cat)}">${escapeHtml(catDisplay)}</a>
<span class="grow"></span>
- <span><a href="/design/${id - 2}?theme=config">← prev</a> · <a href="/design/${id + 2}?theme=config">next →</a></span>
+ <span><a href="/design/${Math.max(1, id - 2)}?theme=config">← prev</a> · <a href="/design/${id + 2}?theme=config">next →</a></span>
</div>
<main class="config">
← 50ab4a2 Harden pattern-generation engine per Claude-Codex debate: ga
·
back to Wallco Ai
·
Generate + publish GRASSCLOTH/LINEN/SILK drunk-animals sets 724ffb9 →