← back to Wallco Ai
feat(scenic): hospitality style-pack sub-filter — Aman / Auberge / Four Seasons
2bd382304b54717a7bf8b57be3a18afe72c4e7e6 · 2026-05-22 13:38:21 -0700 · Steve Abrams
Surfaces the hospitalityFit() classifier as a customer-facing filter so the
90 photoreal Scenic designs become 3 curated style packs.
Wiring:
- /designs accepts ?hospitality=Aman|Auberge|Four Seasons (case-insensitive,
dashes-to-spaces tolerant). Filter is meaningful only when combined with
?line=Scenic since hospitalityFit() returns null for Studio.
- /scenic landing page renders a 3-card style-pack grid below the brand
pitch — each card shows the pack name + 1-line subtitle + count of
designs in that pack, links to /designs?line=Scenic&hospitality=...
- /designs?line=Scenic browse now renders a chip strip 'Hospitality:
All Scenic · Aman · Auberge · Four Seasons' above the existing Hue
swatches. Inherits cat / motif / hue filters when active, so chips
compose cleanly.
Pack subtitles:
Aman — Minimalist · restrained palette · dark or neutral
Auberge — Warm · hand-crafted · amber-bronze
Four Seasons — Classic · traditional · everything else
Style-pack mapping is owned by hospitalityFit(d) at server.js:12405
(lightness < 0.25 OR saturation < 0.15 → Aman; hue 15-45° + sat < 0.5 →
Auberge; everything else → Four Seasons). When the catalog grows the
classifier auto-tags new designs by palette — no manual curation needed.
Smoke (all prod):
/scenic 200, packs grid present
/designs?line=Scenic 200, hosp-chip strip
/designs?line=Scenic&hospitality=Aman 200
/designs?line=Scenic&hospitality=Auberge 200
/designs?line=Scenic&hospitality=Four%20Seasons 200
Files touched
Diff
commit 2bd382304b54717a7bf8b57be3a18afe72c4e7e6
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri May 22 13:38:21 2026 -0700
feat(scenic): hospitality style-pack sub-filter — Aman / Auberge / Four Seasons
Surfaces the hospitalityFit() classifier as a customer-facing filter so the
90 photoreal Scenic designs become 3 curated style packs.
Wiring:
- /designs accepts ?hospitality=Aman|Auberge|Four Seasons (case-insensitive,
dashes-to-spaces tolerant). Filter is meaningful only when combined with
?line=Scenic since hospitalityFit() returns null for Studio.
- /scenic landing page renders a 3-card style-pack grid below the brand
pitch — each card shows the pack name + 1-line subtitle + count of
designs in that pack, links to /designs?line=Scenic&hospitality=...
- /designs?line=Scenic browse now renders a chip strip 'Hospitality:
All Scenic · Aman · Auberge · Four Seasons' above the existing Hue
swatches. Inherits cat / motif / hue filters when active, so chips
compose cleanly.
Pack subtitles:
Aman — Minimalist · restrained palette · dark or neutral
Auberge — Warm · hand-crafted · amber-bronze
Four Seasons — Classic · traditional · everything else
Style-pack mapping is owned by hospitalityFit(d) at server.js:12405
(lightness < 0.25 OR saturation < 0.15 → Aman; hue 15-45° + sat < 0.5 →
Auberge; everything else → Four Seasons). When the catalog grows the
classifier auto-tags new designs by palette — no manual curation needed.
Smoke (all prod):
/scenic 200, packs grid present
/designs?line=Scenic 200, hosp-chip strip
/designs?line=Scenic&hospitality=Aman 200
/designs?line=Scenic&hospitality=Auberge 200
/designs?line=Scenic&hospitality=Four%20Seasons 200
---
server.js | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/server.js b/server.js
index 1d7939f..fcfd0b0 100644
--- a/server.js
+++ b/server.js
@@ -3570,6 +3570,31 @@ function renderSubBrandLanding(req, res, line) {
+ '<section style="max-width:780px;margin:0 auto;padding:56px 24px 36px;text-align:center">'
+ '<p style="font:16px/1.75 var(--sans,system-ui);color:var(--ink,#1a1a1a);margin:0">' + pitch + '</p>'
+ '</section>'
+ // Hospitality style-pack chips — Scenic only. Each chip filters the
+ // catalog to the designs whose dominant palette fits one of the three
+ // hospitality brand aesthetics (Aman = restrained-dark/neutral, Auberge
+ // = warm amber-bronze, Four Seasons = classic everything-else).
+ + (!isStudio ? (() => {
+ const packs = ['Aman', 'Auberge', 'Four Seasons'].map(name => {
+ const count = lineDesigns.filter(d => hospitalityFit(d) === name).length;
+ const subtitle = name === 'Aman'
+ ? 'Minimalist · restrained palette · dark or neutral'
+ : name === 'Auberge'
+ ? 'Warm · hand-crafted · amber-bronze'
+ : 'Classic · traditional · everything else';
+ return ''
+ + '<a href="/designs?line=Scenic&hospitality=' + encodeURIComponent(name) + '" style="display:flex;flex-direction:column;align-items:center;gap:6px;padding:18px 14px;background:#faf6ec;border:1px solid #e7dcc3;border-radius:10px;text-decoration:none;color:var(--ink,#1a1a1a);transition:transform .12s ease, box-shadow .12s ease" onmouseover="this.style.transform=\'translateY(-2px)\';this.style.boxShadow=\'0 6px 18px rgba(122,74,40,0.18)\'" onmouseout="this.style.transform=\'\';this.style.boxShadow=\'\'">'
+ + '<div style="font:13px var(--serif,Georgia,serif);font-weight:700;letter-spacing:.05em">' + name + '</div>'
+ + '<div style="font:11px var(--sans,system-ui);color:var(--ink-soft,#555);text-align:center;line-height:1.4">' + subtitle + '</div>'
+ + '<div style="font:10px var(--sans,system-ui);letter-spacing:.10em;color:var(--ink-faint,#999);text-transform:uppercase">' + count + ' designs</div>'
+ + '</a>';
+ }).join('');
+ return ''
+ + '<section style="max-width:920px;margin:0 auto;padding:0 24px 36px">'
+ + '<div style="font:11px var(--sans,system-ui);letter-spacing:.20em;text-transform:uppercase;color:var(--ink-faint,#999);text-align:center;margin-bottom:18px">Filter by hospitality aesthetic</div>'
+ + '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px">' + packs + '</div>'
+ + '</section>';
+ })() : '')
// Featured grid
+ (gridCards
? '<section style="max-width:1280px;margin:0 auto;padding:0 24px 24px"><div style="font:11px var(--sans,system-ui);letter-spacing:.20em;text-transform:uppercase;color:var(--ink-faint,#999);text-align:center;margin-bottom:18px">Featured ' + line + ' designs</div><div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px">' + gridCards + '</div></section>'
@@ -3721,6 +3746,15 @@ app.get('/designs', (req, res) => {
const want = lineQ.toLowerCase();
filtered = filtered.filter(d => String(productLine(d) || '').toLowerCase() === want);
}
+ // Hospitality style-pack filter — `?hospitality=Aman|Auberge|Four Seasons`.
+ // Only meaningful when combined with line=Scenic (the classifier returns
+ // null for Studio). Case-insensitive accept; "four-seasons" + "four seasons"
+ // both work.
+ const hospQ = String(req.query.hospitality || '').trim();
+ if (hospQ) {
+ const want = hospQ.toLowerCase().replace(/-/g, ' ');
+ filtered = filtered.filter(d => String(hospitalityFit(d) || '').toLowerCase() === want);
+ }
if (cat) filtered = filtered.filter(d => d.category === cat);
if (motifQ) filtered = filtered.filter(d => (d.motifs || []).includes(motifQ));
if (hueQ) filtered = filtered.filter(d => hueBucketOf(d.dominant_hex) === hueQ);
@@ -4184,6 +4218,27 @@ ${(req.query.source === 'all') ? `
<!-- 2026-05-20 Steve directive — Color + Style scrub sliders stripped. -->
${''}
+ <!-- Hospitality style-pack chips — only render when ?line=Scenic.
+ Lets trade designers slice the 90+ photoreal scenics into 3 brand-fit
+ packs (Aman / Auberge / Four Seasons). Chips link to the same /designs
+ page with an extra ?hospitality= filter. -->
+ ${lineQ.toLowerCase() === 'scenic' ? (() => {
+ const hospQActive = String(req.query.hospitality || '').toLowerCase().replace(/-/g, ' ');
+ const baseQs = ['line=Scenic'];
+ if (cat) baseQs.push('cat=' + encodeURIComponent(cat));
+ if (motifQ) baseQs.push('motif=' + encodeURIComponent(motifQ));
+ if (hueQ) baseQs.push('hue=' + encodeURIComponent(hueQ));
+ // Render "All / Aman / Auberge / Four Seasons" chips with on-state when
+ // the active query matches.
+ const all = `<a href="/designs?${baseQs.join('&')}" class="hosp-chip${!hospQActive ? ' is-on' : ''}" style="font:11px var(--sans);padding:5px 11px;border:1px solid ${!hospQActive ? 'var(--accent)' : 'var(--line)'};background:${!hospQActive ? 'var(--accent)' : 'transparent'};color:${!hospQActive ? 'var(--bg)' : 'var(--ink-soft)'};border-radius:999px;text-decoration:none">All Scenic</a>`;
+ const packs = ['Aman', 'Auberge', 'Four Seasons'].map(name => {
+ const on = hospQActive === name.toLowerCase();
+ const href = '/designs?' + baseQs.concat('hospitality=' + encodeURIComponent(name)).join('&');
+ return `<a href="${href}" class="hosp-chip${on ? ' is-on' : ''}" style="font:11px var(--sans);padding:5px 11px;border:1px solid ${on ? 'var(--accent)' : 'var(--line)'};background:${on ? 'var(--accent)' : 'transparent'};color:${on ? 'var(--bg)' : 'var(--ink-soft)'};border-radius:999px;text-decoration:none">${name}</a>`;
+ }).join('');
+ return '<div class="hosp-swatches" style="display:flex;flex-wrap:wrap;gap:6px;margin:14px 0 4px;align-items:center"><span style="font:11px var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-faint);margin-right:6px">Hospitality</span>' + all + packs + '</div>';
+ })() : ''}
+
<!-- Color-wheel hue swatches -->
<div class="hue-swatches" style="display:flex;flex-wrap:wrap;gap:6px;margin:18px 0 4px;align-items:center">
<span style="font:11px var(--sans);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-faint);margin-right:6px">Hue</span>
← 49e0ab6 ghost-layer: detector + scanner + pre-publish gate + fix-que
·
back to Wallco Ai
·
feat(admin): Crop & Fix tool — drag rect on any design, Gemi d2d3d8e →