← back to Wallco Ai
Feature murals prominently on home: pinned Shop Murals quicknav + Featured Murals rail + /designs?murals=1 group filter
c85a001aad917906b20b6bacc7922328f4fa5595 · 2026-06-03 22:57:00 -0700 · Steve
Files touched
M public/css/site.cssM server.js
Diff
commit c85a001aad917906b20b6bacc7922328f4fa5595
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jun 3 22:57:00 2026 -0700
Feature murals prominently on home: pinned Shop Murals quicknav + Featured Murals rail + /designs?murals=1 group filter
---
public/css/site.css | 44 +++++++++++++++++++++++++
server.js | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 137 insertions(+)
diff --git a/public/css/site.css b/public/css/site.css
index 771d3cc..ee0aa3a 100644
--- a/public/css/site.css
+++ b/public/css/site.css
@@ -276,6 +276,50 @@ main { padding-top: 81px; } /* matches .site-header actual height (44px brand +
margin: 0;
}
+/* ── HERO QUICK-NAV (pinned Murals + Browse-all chips, Steve 2026-06-03) */
+.hero-quicknav {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin: 22px 0 0;
+}
+.quicknav-chip {
+ font-family: var(--sans, system-ui);
+ font-size: 13px;
+ letter-spacing: 0.04em;
+ text-decoration: none;
+ color: var(--ink-soft);
+ border: 1px solid var(--line);
+ border-radius: 999px;
+ padding: 9px 20px;
+ transition: background .15s ease, color .15s ease, border-color .15s ease;
+}
+.quicknav-chip:hover { color: var(--ink); border-color: var(--ink); }
+.quicknav-chip-primary {
+ background: var(--ink, #0d0d0d);
+ color: var(--bg, #fff);
+ border-color: var(--ink, #0d0d0d);
+ font-weight: 500;
+}
+.quicknav-chip-primary:hover { color: var(--bg, #fff); opacity: 0.88; }
+
+/* ── FEATURED MURALS RAIL (Steve 2026-06-03 — surface murals above the feed) */
+.mural-feature {
+ background: linear-gradient(180deg, rgba(120, 92, 64, 0.06), transparent 70%);
+ border-bottom: 1px solid var(--line);
+}
+.mural-eyebrow {
+ display: block;
+ font-family: var(--sans, system-ui);
+ font-size: 11px;
+ letter-spacing: 0.22em;
+ text-transform: uppercase;
+ color: var(--ink-faint, #999);
+ margin: 0 0 4px;
+}
+.mural-see-all { font-weight: 500; color: var(--ink); }
+
/* ── SECTION HEADERS */
.section-header {
display: flex;
diff --git a/server.js b/server.js
index 79b8f60..473b581 100644
--- a/server.js
+++ b/server.js
@@ -1183,6 +1183,28 @@ function cardAriaLabel(d) {
return `${d.title} — ${hue} ${cat} wallpaper${roomTxt}. View details.`;
}
+// ── Mural browse-grouping (Steve 2026-06-03 "make murals prominent") ─────────
+// The catalog's `?cat=` filter is exact-match, so it can't surface a whole
+// MURAL family in one view (e.g. `designer-scenic · slate-mist` colorways won't
+// match a plain `designer-scenic`). This presentation-level predicate groups
+// every mural line — the canonical insert-guard set in lib/mural-categories.js
+// PLUS `designer-scenic*` (Steve explicitly named it a mural line; it isn't in
+// the strict insert guard because it's not subject to the never-tile rule the
+// guard enforces). Used by the home "Featured · Murals" rail and the
+// `/designs?murals=1` group filter. Deliberately NOT mutating
+// lib/mural-categories.js so that file stays a clean insert-time tile guard.
+const { MURAL_CATEGORIES: _MURAL_CAT_SET } = require('./lib/mural-categories.js');
+function isMuralBrowseCategory(category) {
+ const c = String(category || '').toLowerCase().trim();
+ if (!c) return false;
+ // Match the base category even when a " · colorway" suffix is appended.
+ const base = c.split('·')[0].trim();
+ if (_MURAL_CAT_SET.has(base)) return true;
+ // Steve-named scenic mural lines (designer-scenic + its colorways).
+ if (base === 'designer-scenic' || base.startsWith('designer-scenic')) return true;
+ return false;
+}
+
function sortDesigns(designs, sort) {
const d = [...designs];
// Designs with a pre-generated room preview surface first within any sort —
@@ -6953,6 +6975,42 @@ app.get('/', (req, res) => {
// generated work to be the first thing visitors see. created_at lex sort is
// safe because every row is ISO-8601 UTC.
const featured = [...DESIGNS].sort((a,b) => String(b.created_at||'').localeCompare(String(a.created_at||''))).slice(0, 8);
+ // ── Featured · Murals rail (Steve 2026-06-03) ──────────────────────────────
+ // The home feed is newest-first, so the high-value MURAL lines (monterey-mural,
+ // designer-scenic, cactus-11ft-mural, tree-mural, cactus-pine-scenic) — which
+ // have older ids ~40k-52k — get buried under the 57xxx animal designs. This
+ // rail pins them prominently at the very top of the page. We curate a diverse
+ // spread: take the best from EACH named mural family (room-preview first, then
+ // newest) so no single family dominates the shelf. Empty → rail hides itself.
+ const muralFamilyOrder = ['monterey-mural', 'designer-scenic', 'cactus-11ft-mural', 'tree-mural', 'cactus-pine-scenic'];
+ const muralFamilyOf = (cat) => {
+ const base = String(cat || '').toLowerCase().split('·')[0].trim();
+ return muralFamilyOrder.find(f => base === f || base.startsWith(f)) || base;
+ };
+ const allMurals = DESIGNS.filter(d => isMuralBrowseCategory(d.category) && designHasImage(d));
+ // Rank within a family: designs with a room mockup first, then newest.
+ const muralRank = (a, b) => {
+ const ra = (a.room_mockups || []).length > 0 ? 1 : 0;
+ const rb = (b.room_mockups || []).length > 0 ? 1 : 0;
+ if (rb !== ra) return rb - ra;
+ return String(b.created_at || '').localeCompare(String(a.created_at || ''));
+ };
+ const byMuralFamily = Object.create(null);
+ for (const d of allMurals) {
+ const fam = muralFamilyOf(d.category);
+ (byMuralFamily[fam] = byMuralFamily[fam] || []).push(d);
+ }
+ // Round-robin across families so the rail shows variety, up to 8 cards.
+ const muralFeatured = [];
+ const muralPools = muralFamilyOrder
+ .map(f => (byMuralFamily[f] || []).sort(muralRank))
+ .filter(pool => pool.length);
+ for (let round = 0; muralFeatured.length < 8 && muralPools.some(p => p.length); round++) {
+ for (const pool of muralPools) {
+ if (muralFeatured.length >= 8) break;
+ if (pool[round]) muralFeatured.push(pool[round]);
+ }
+ }
// Pinned "Just-generated · Butterflies" rail — surfaces the latest 6
// butterfly-flavored designs above the Featured grid. Matches on category
// OR motif so we catch butterfly-trellis as well as future butterfly-*
@@ -7060,6 +7118,28 @@ app.get('/', (req, res) => {
${butterflyCards}
</div>
</section>
+` : '';
+
+ // ── Featured · Murals rail markup (Steve 2026-06-03) ───────────────────────
+ // Pinned at the very top of the page so the mural lines are the first thing a
+ // visitor sees, instead of being buried pages deep behind the newest-first
+ // feed. "See all murals →" links to /designs?murals=1 (the grouped, fully
+ // sortable + density-controlled grid). Hides entirely if no murals exist.
+ const muralCards = muralFeatured.map(renderCard).join('');
+ const muralSection = muralFeatured.length ? `
+<!-- Featured · Murals — pinned at the top of the page -->
+<section class="featured-section mural-feature" id="mural-feature">
+ <div class="section-header">
+ <div>
+ <span class="mural-eyebrow">Statement Walls</span>
+ <h2>Featured Murals</h2>
+ </div>
+ <a href="/designs?murals=1&sort=newest" class="see-all mural-see-all">Shop all murals →</a>
+ </div>
+ <div class="design-grid featured-grid">
+ ${muralCards}
+ </div>
+</section>
` : '';
res.type('html').send(`${htmlHead({
@@ -7092,8 +7172,13 @@ ${htmlHeader('/')}
<div class="hero-wordmark-below">
<h1 class="wordmark">wallco.ai</h1>
<p class="tagline">Curated-original wallpaper & murals — every pattern generated, never repeated.</p>
+ <div class="hero-quicknav">
+ <a href="/designs?murals=1&sort=newest" class="quicknav-chip quicknav-chip-primary">▲ Shop Murals</a>
+ <a href="/designs?sort=newest" class="quicknav-chip">Browse all designs</a>
+ </div>
</div>
+${muralSection}
${butterflySection}
<!-- Featured designs grid -->
<section class="featured-section">
@@ -7827,6 +7912,14 @@ app.get('/designs', (req, res) => {
filtered = filtered.filter(d => String(studioPack(d) || '').toLowerCase() === want);
}
if (cat) filtered = filtered.filter(d => d.category === cat);
+ // Murals group filter (Steve 2026-06-03) — `?murals=1` surfaces EVERY mural
+ // line (monterey-mural, designer-scenic + colorways, cactus-11ft-mural,
+ // tree-mural, cactus-pine-scenic, …) in one sortable view. The home page's
+ // "Featured · Murals" rail "See all" links here. Sort + density controls on
+ // this grid are untouched.
+ const muralsQ = String(req.query.murals || '').toLowerCase();
+ const wantMurals = muralsQ === '1' || muralsQ === 'true' || muralsQ === 'all';
+ if (wantMurals) filtered = filtered.filter(d => isMuralBrowseCategory(d.category));
if (motifQ) filtered = filtered.filter(d => (d.motifs || []).includes(motifQ));
if (hueQ) filtered = filtered.filter(d => hueBucketOf(d.dominant_hex) === hueQ);
// Room filter — supports binary ('1'/'any'/'all' = any mockup) AND specific
← 5499527 bulk-action 'digital': exempt designs with a built room-size
·
back to Wallco Ai
·
Add dedicated Monterey/California-coast scenic mural batch g bb95dd7 →