← back to Consulting Designerwallcoverings Com
Traffic & Search Reality section: GSC 28d totals, 51% organic-click decline flagged, striking-distance table, branded split, GA4 gap; sections renumbered
2a832f2f2fe830c89c05c80c3ae960ccbaa636d1 · 2026-07-26 14:23:23 -0700 · Steve Abrams
Files touched
M build.mjsM public/portal.html
Diff
commit 2a832f2f2fe830c89c05c80c3ae960ccbaa636d1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Jul 26 14:23:23 2026 -0700
Traffic & Search Reality section: GSC 28d totals, 51% organic-click decline flagged, striking-distance table, branded split, GA4 gap; sections renumbered
---
build.mjs | 55 ++++++++++++++++++++++++++++++++++++++++++------------
public/portal.html | 42 ++++++++++++++++++++++++++++-------------
2 files changed, 72 insertions(+), 25 deletions(-)
diff --git a/build.mjs b/build.mjs
index 9399ff4..4c41df0 100644
--- a/build.mjs
+++ b/build.mjs
@@ -16,6 +16,7 @@ const readJ = (p, d) => { try { return JSON.parse(readFileSync(join(HERE, p), 'u
const client = readJ('data/client.json', {});
const SNAP = readJ('data/dw-analysis.json', { catalog: {}, channels: [], fleet: { count: 0, sites: [] }, competitors: [] });
const ADS = readJ('data/ads.json', []);
+const TR = SNAP.traffic && SNAP.traffic.available !== false ? SNAP.traffic : null;
const GALLERY = Array.isArray(SNAP.gallery) ? SNAP.gallery : [];
const C = SNAP.catalog || {};
const FF = C.five_field || {};
@@ -180,10 +181,23 @@ const calendar = [
];
const calRows = calendar.map(c => `<tr><td class="mo">${c[0]}</td><td>${c[1]}</td><td class="act">${c[2]}</td></tr>`).join('');
+/* ---- traffic & search reality (GSC, cached report) --------------------------- */
+const pct = x => (x == null ? '—' : (100 * x).toFixed(1) + '%');
+const trStriking = (TR?.striking_distance || []).slice(0, 8).map(s =>
+ `<tr><td><b>${s.query}</b></td><td>${s.position.toFixed(1)}</td><td>${fmt(s.impressions)}</td><td>${fmt(s.clicks)}</td></tr>`).join('');
+const trPages = (TR?.top_pages || []).slice(0, 8).map(p =>
+ `<tr><td><b>${p.path}</b></td><td>${fmt(p.clicks)}</td><td>${fmt(p.impressions)}</td></tr>`).join('');
+const trQueries = (TR?.top_queries || []).slice(0, 8).map(q =>
+ `<tr><td><b>${q.query}</b></td><td>${fmt(q.clicks)}</td><td>${fmt(q.impressions)}</td><td>${pct(q.ctr)}</td><td>${q.position.toFixed(1)}</td></tr>`).join('');
+const trDecline = (() => {
+ const m = TR?.monthly_trend || []; if (m.length < 2) return null;
+ return { from: m[0], to: m[m.length - 1], pctDown: Math.round(100 * (1 - m[m.length - 1].clicks / m[0].clicks)) };
+})();
+
const CATS = [
['summary', 'Executive Summary'], ['swot', 'SWOT Analysis'], ['empire', 'The Catalog Empire'],
['integrity', 'Catalog Integrity — the 5-Field Read'], ['competitors', 'Competitive Landscape'],
- ['advertising', 'Channels & Distribution'], ['timing', 'Timing & Cadence'],
+ ['advertising', 'Channels & Distribution'], ['traffic', 'Traffic & Search Reality'], ['timing', 'Timing & Cadence'],
['audit', 'Website Audit'], ['social', 'Social Audit'], ['suggested', 'Suggested New Flagships'],
['partners', 'Unlikely Partners'], ['revenue', 'Unlikely Revenue'],
['desk', 'Live Commerce Desk'], ['roadmap', '90-Day Roadmap'], ['sources', 'Sources & Methodology'],
@@ -207,6 +221,7 @@ const SOURCES = [
['Catalog DNA — style, color & workflow-tag charts', 'Tag-match SQL over all actives (the same tag buckets the storefront sort uses)', 'Live counts per style/color/Needs-* tag; the color doughnut renders in representative hues of the tag names', 'live'],
['Monthly activation ramp', 'created_at_shopify over the last 7 calendar months, live mirror', 'Direct SQL; the ramp multiplier in the copy is computed from the same series', 'live'],
['Product mosaic + concept-grid imagery', "The store's own CDN (one current ACTIVE product per vendor line, forbidden-vendor-filtered)", 'Already customer-facing on the live store; hotlinked at thumbnail width', 'live'],
+ ['Traffic & Search Reality', 'Google Search Console via the cached DW SEO report (age-labeled in the section footer)', 'Totals, branded split, striking-distance and page tables rendered verbatim; the decline copy is computed from the same monthly series', 'observed'],
['Hero imagery', 'Unsplash editorial stock (luxury materials, per the luxe-only imagery rule)', 'Hotlinked, decorative only — carries no data meaning', 'estimate'],
];
const srcRows = SOURCES.map(s => { const t = SRC_TIER[s[3]] || ['—', 'a']; return `<tr><td><b>${s[0]}</b></td><td>${s[1]}</td><td>${s[2]}</td><td class="srccell"><span class="srcb ${t[1]}">${t[0]}</span></td></tr>`; }).join('');
@@ -437,23 +452,37 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
</div>
</div></section>
-<section id="timing"><div class="eyebrow">05 · Timing & Cadence</div><h2 class="disp">The drip is live. Point it at revenue.</h2><div class="accbody">
+${TR ? `<section id="traffic"><div class="eyebrow">05 · Traffic & Search Reality</div><h2 class="disp">What Google actually sends — and what it's offering.</h2><div class="accbody">
+ <p class="lead2">Search Console, last 28 days: <b>${fmt(TR.totals_28d?.clicks)}</b> clicks on <b>${fmt(TR.totals_28d?.impressions)}</b> impressions (CTR ${pct(TR.totals_28d?.ctr)}, avg position ${TR.totals_28d?.avg_position?.toFixed(1)}). <b>${pct(1 - (TR.branded_split?.branded_pct ?? 0))} of clicks are non-branded</b> — people finding DW without knowing DW. That's earned discovery, and it makes the next number matter more.</p>
+ ${trDecline ? `<div class="flagbox"><div class="i">▼</div><div><h3 class="disp">The decline nobody's measuring</h3><p>Organic clicks have fallen <b>${trDecline.pctDown}%</b> since ${trDecline.from.month} — ${fmt(trDecline.from.clicks)}/mo then, <b>${fmt(trDecline.to.clicks)}</b>/mo now — while the catalog grew by tens of thousands of products. And <b>no GA4 property exists on the main store</b>, so nothing downstream of the click is measured at all. This is the brief's most urgent measured finding: the roadmap's Week-1 attribution KPI is not optional hygiene, it's the tourniquet.</p></div></div>` : ''}
+ <div class="chartwrap"><div class="ct">Organic clicks per month — the trend, unvarnished (GSC)</div><canvas id="gscChart"></canvas></div>
+ <div class="eyebrow" style="margin-top:52px">Striking distance — page-one demand already offered</div>
+ <p class="lead2">Queries ranking positions 8–15 with real impression volume: each is a content/AEO sprint away from page-one clicks. This is the cheapest traffic DW can buy, because it's not bought.</p>
+ <table><thead><tr><th>Query</th><th>Position</th><th>Impressions (28d)</th><th>Clicks</th></tr></thead><tbody>${trStriking}</tbody></table>
+ <div class="duo" style="margin-top:34px">
+ <div><div class="eyebrow">Top queries</div><table><thead><tr><th>Query</th><th>Clicks</th><th>Impr.</th><th>CTR</th><th>Pos.</th></tr></thead><tbody>${trQueries}</tbody></table></div>
+ <div><div class="eyebrow">Top pages</div><table><thead><tr><th>Path</th><th>Clicks</th><th>Impr.</th></tr></thead><tbody>${trPages}</tbody></table></div>
+ </div>
+ <p class="lead2" style="font-size:14px;opacity:.6;margin-top:18px">Source: ${TR.source} · pulled ${TR.age_days} day(s) before this snapshot · data through ${TR.last_data_date}. GA4: ${TR.ga4?.available ? 'connected' : 'not provisioned — a roadmap item'}.</p>
+</div></section>` : ''}
+
+<section id="timing"><div class="eyebrow">06 · Timing & Cadence</div><h2 class="disp">The drip is live. Point it at revenue.</h2><div class="accbody">
<p class="lead2">The growth curve, measured: monthly activations ramped from <b>${fmt((C.monthly_activation || [])[0]?.added)}</b> in ${(C.monthly_activation || [])[0]?.month || 'Jan'} to <b>${fmt((C.monthly_activation || []).slice(-1)[0]?.added)}</b> in ${(C.monthly_activation || []).slice(-1)[0]?.month || 'Jul'} — a ${(() => { const m = C.monthly_activation || []; const a = m[0]?.added || 1, b = m.slice(-1)[0]?.added || 0; return fmt(Math.round(b / a)); })()}× ramp as the activation engine came online. The merchandising calendar below aligns campaigns to the category's natural intent cycle.</p>
<div class="chartwrap"><div class="ct">The ramp — new actives per month, last 7 months (live)</div><canvas id="rampChart"></canvas></div>
<div class="chartwrap"><div class="ct">New active products per week — last 12 weeks (live)</div><canvas id="cadenceChart"></canvas></div>
<table><thead><tr><th>Month</th><th>What the market is doing</th><th>The DW play</th></tr></thead><tbody>${calRows}</tbody></table>
</div></section>
-<section id="audit"><div class="eyebrow">06 · Website Audit</div><h2 class="disp">A strong engine that hides its best features.</h2><div class="accbody">
+<section id="audit"><div class="eyebrow">07 · Website Audit</div><h2 class="disp">A strong engine that hides its best features.</h2><div class="accbody">
<p class="lead2">Analyst audit of the live flagship (Shopify + Boost search): the merchandising infrastructure is genuinely advanced — the gap is guided discovery for the retail buyer facing ${fmt(FF.active_total)} choices.</p>
<div class="cards2">
<div class="c"><h3>Working well</h3><p class="good">Left-rail collection navigation · shop-by-color wheel with type pills · AEO content blocks on collections · sort + density controls on grids · New Arrivals / Trending smart collections refreshed by daily tag-sync · $4.25 memo-sample funnel on every product.</p></div>
<div class="c"><h3>Gaps</h3><p class="flag">Sample-only products present a dead-end buy box (${fmt(SO.backlog)} products) · zero-price actives surface in feeds (${fmt(FF.zero_price)}) · ${fmt(FF.missing_image)} actives lack a featured image · curation layers (rooms, moods, designer picks) are thin relative to catalog depth.</p></div>
- <div class="c"><h3>Action</h3><p>Fix the integrity backlog (Section 02), then ship a guided-discovery layer: room/mood entry points, "pairs well with" on every PDP, and a designer-picks editorial tier — the concept directions in Section 08 show the face of it.</p></div>
+ <div class="c"><h3>Action</h3><p>Fix the integrity backlog (Section 02), then ship a guided-discovery layer: room/mood entry points, "pairs well with" on every PDP, and a designer-picks editorial tier — the concept directions in Section 09 show the face of it.</p></div>
</div>
</div></section>
-<section id="social"><div class="eyebrow">07 · Social Audit</div><h2 class="disp">Broad reach, quiet voice.</h2><div class="accbody">
+<section id="social"><div class="eyebrow">08 · Social Audit</div><h2 class="disp">Broad reach, quiet voice.</h2><div class="accbody">
<p class="lead2">All major platforms are connected through one command center — a real infrastructure asset. The audit finding is consistent across platforms: <b>distribution exists, engagement discipline doesn't yet</b>. The page network posts; it doesn't yet converse, attribute, or convert.</p>
<div class="cards2">
<div class="c"><h3>Connected & ready</h3><p class="good">${(SNAP.channels || []).filter(c => c.connected).map(c => c.label || c.key).join(' · ')}</p></div>
@@ -462,17 +491,17 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
</div>
</div></section>
-<section id="suggested"><div class="eyebrow">08 · Suggested New Flagships</div><h2 class="disp">Six directions worthy of the catalog.</h2><div class="accbody">
+<section id="suggested"><div class="eyebrow">09 · Suggested New Flagships</div><h2 class="disp">Six directions worthy of the catalog.</h2><div class="accbody">
<p class="lead2">Six full front-page concepts — each after a house that owns its lane, each a working page with the mandatory sort + density controls on its grid. Not palette swaps: different merchandising philosophies. Open each live and pick the direction.</p>
<div class="vgrid">${houseTiles}</div>
</div></section>
-<section id="partners"><div class="eyebrow">09 · Unlikely Partners</div><h2 class="disp">Channels that don't look like channels.</h2><div class="accbody">
+<section id="partners"><div class="eyebrow">10 · Unlikely Partners</div><h2 class="disp">Channels that don't look like channels.</h2><div class="accbody">
<p class="lead2">Eight partnership lanes where DW's LA position and trade heritage do the acquisition work.</p>
${partnerRows}
</div></section>
-<section id="revenue"><div class="eyebrow">10 · Unlikely Revenue</div><h2 class="disp">The money already in the building.</h2><div class="accbody">
+<section id="revenue"><div class="eyebrow">11 · Unlikely Revenue</div><h2 class="disp">The money already in the building.</h2><div class="accbody">
<p class="lead2">Before any new audience is bought, six revenue pools sit inside the existing operation:</p>
<div class="sugg">
<div class="s"><div class="p">Pool 1 · largest</div><h4>The sellable-variant backlog</h4><p>${fmt(SO.backlog)} active products browseable but not buyable. Even a 1% conversion at category AOV is a six-figure annual pool. Remediation is data work, not marketing spend.</p></div>
@@ -498,7 +527,7 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
<div class="chartwrap"><div class="ct">Priced-subset architecture (basis: min variant > $4.25 memo — partial, labeled)</div><canvas id="bandChart"></canvas></div>
</div></section>
-<section id="desk"><div class="eyebrow">11 · Live Commerce Desk</div><h2 class="disp">The operation, on one screen.</h2><div class="accbody">
+<section id="desk"><div class="eyebrow">12 · Live Commerce Desk</div><h2 class="disp">The operation, on one screen.</h2><div class="accbody">
<p class="lead2">Live KPIs from the ${collectedLabel} snapshot — the numbers a weekly growth review should open with. Hit ↻ Refresh (top right) to re-collect on demand.</p>
<div class="kpis">
<div class="kpi"><div class="kv">${fmt(FF.active_total)}</div><div class="kl">Active products</div></div>
@@ -513,16 +542,16 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
<div class="chartwrap"><div class="ct">Weekly activation cadence (same data as Section 05 — the desk's pulse line)</div><canvas id="deskChart"></canvas></div>
</div></section>
-<section id="roadmap"><div class="eyebrow">12 · 90-Day Roadmap</div><h2 class="disp">What to do, in order.</h2><div class="accbody">
+<section id="roadmap"><div class="eyebrow">13 · 90-Day Roadmap</div><h2 class="disp">What to do, in order.</h2><div class="accbody">
<ul class="road">
- <li><div class="w">Weeks 1–2</div><div class="d"><b>Integrity sprint:</b> triage the ${fmt(SO.backlog)} sample-only backlog by vendor (top 8 lines = ${fmt((C.sample_only_vendors || []).reduce((a, v) => a + v.count, 0))} products); fix zero-price and image-less actives. Wire per-channel UTM + coupon attribution. Pick a concept direction from Section 08.<br><span style="color:var(--gold);font-size:14px">KPI · 2,500+ sellable variants added · zero-price actives < 500 · UTM live on all 7 channels · concept direction chosen (Week-2 decision gate)</span></div></li>
+ <li><div class="w">Weeks 1–2</div><div class="d"><b>Integrity sprint:</b> triage the ${fmt(SO.backlog)} sample-only backlog by vendor (top 8 lines = ${fmt((C.sample_only_vendors || []).reduce((a, v) => a + v.count, 0))} products); fix zero-price and image-less actives. Wire per-channel UTM + coupon attribution. Pick a concept direction from Section 09.<br><span style="color:var(--gold);font-size:14px">KPI · 2,500+ sellable variants added · zero-price actives < 500 · UTM live on all 7 channels · concept direction chosen (Week-2 decision gate)</span></div></li>
<li><div class="w">Weeks 3–6</div><div class="d"><b>Convert intent:</b> ship the sampled-but-didn't-buy email sequence off the memo funnel. Sync daily social posting to the activation drip. Start the designer-net trade portal build.<br><span style="color:var(--gold);font-size:14px">KPI · first attributed sample→roll conversion · sequence live with ≥25% open rate · 7 drip-synced posts/week shipping · re-collect this brief and re-baseline (↻)</span></div></li>
<li><div class="w">Weeks 6–10</div><div class="d"><b>Ship the face:</b> launch the chosen flagship direction (guided discovery: rooms, moods, designer picks). Extend AEO blocks fleet-wide. Sign 3 unlikely-partner deals (one installer network, one stager program, one hospitality designer).<br><span style="color:var(--gold);font-size:14px">KPI · new front page live · AEO blocks on 100+ fleet sites · 3 signed partner agreements · backlog under 15,000</span></div></li>
<li><div class="w">Weeks 10–13</div><div class="d"><b>Measure & double down:</b> channel-level revenue review off the new attribution; scale the winner. Open the contract/hospitality quote desk. Publish the first licensing-line results.<br><span style="color:var(--gold);font-size:14px">KPI · every channel shows attributed revenue (or is cut) · first contract quote issued · Week-13 review: backlog, conversion rate, channel ROI vs this brief's baseline</span></div></li>
</ul>
</div></section>
-<section id="sources"><div class="eyebrow">13 · Sources & Methodology</div><h2 class="disp">Where every number comes from.</h2><div class="accbody">
+<section id="sources"><div class="eyebrow">14 · Sources & Methodology</div><h2 class="disp">Where every number comes from.</h2><div class="accbody">
<p class="lead2">Snapshot collected <b>${collectedLabel}</b> by a read-only collector. Live rows are measured; analyst rows are authored judgment; anything illustrative says so.</p>
<div class="srclegend">${srcLegend}</div>
<table><thead><tr><th>Section</th><th>Where it comes from</th><th>How it's produced</th><th>Provenance</th></tr></thead><tbody>${srcRows}</tbody></table>
@@ -557,6 +586,7 @@ function initCharts(){if(charted||!window.Chart)return;charted=true;
Chart.defaults.animation=RM?false:{duration:1150,easing:'easeInOutQuart'};
if(!RM){Chart.defaults.animations={y:{from:function(c){return c.chart.scales.y?c.chart.scales.y.getPixelForValue(0):0;}}};}
var D=${JSON.stringify(CHART)};
+ var TRD=${JSON.stringify({ labels: (TR?.monthly_trend || []).map(m => m.month), clicks: (TR?.monthly_trend || []).map(m => m.clicks) })};
function mk(id,cfg){var el=document.getElementById(id);if(el)new Chart(el,cfg);}
// Custom HTML legend for the doughnuts — thin 2x16px line markers instead of box swatches, click-to-toggle
var htmlLegend={id:'htmlLegend',afterUpdate:function(chart){
@@ -573,6 +603,7 @@ function initCharts(){if(charted||!window.Chart)return;charted=true;
// Vertical gold gradient for area fills — scriptable so it re-derives per chart area
function goldFill(a){return function(c){var ch=c.chart,area=ch.chartArea;if(!area)return 'rgba(201,162,75,.12)';var g=ch.ctx.createLinearGradient(0,area.top,0,area.bottom);g.addColorStop(0,'rgba(201,162,75,'+a+')');g.addColorStop(1,'rgba(201,162,75,0)');return g;};}
mk('rampChart',{type:'bar',data:{labels:D.monthly.labels,datasets:[{label:'New actives / month',data:D.monthly.added,backgroundColor:function(c){var ch=c.chart,area=ch.chartArea;if(!area)return 'rgba(201,162,75,.7)';var g=ch.ctx.createLinearGradient(0,area.bottom,0,area.top);g.addColorStop(0,'rgba(201,162,75,.25)');g.addColorStop(1,'rgba(201,162,75,.9)');return g;},borderRadius:5}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{grid:{color:line}},x:{grid:{display:false}}},plugins:{legend:{display:false}}}});
+ mk('gscChart',{type:'line',data:{labels:TRD.labels,datasets:[{label:'Organic clicks / month',data:TRD.clicks,borderColor:'#d4647a',backgroundColor:function(c){var ch=c.chart,area=ch.chartArea;if(!area)return 'rgba(212,100,122,.10)';var g=ch.ctx.createLinearGradient(0,area.top,0,area.bottom);g.addColorStop(0,'rgba(212,100,122,.30)');g.addColorStop(1,'rgba(212,100,122,0)');return g;},fill:true,tension:.4,pointRadius:5,pointBackgroundColor:'#d4647a',pointBorderWidth:0}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{grid:{color:line}},x:{grid:{display:false}}},plugins:{legend:{display:false}}}});
mk('needsChart',{type:'bar',data:{labels:D.needs.labels,datasets:[{label:'Actives tagged',data:D.needs.counts,backgroundColor:'rgba(212,100,122,.85)',borderRadius:5}]},options:{indexAxis:'y',responsive:true,maintainAspectRatio:false,scales:{x:{grid:{color:line}},y:{grid:{display:false}}},plugins:{legend:{display:false}}}});
mk('styleChart',{type:'bar',data:{labels:D.styles.labels,datasets:[{label:'Actives tagged',data:D.styles.counts,backgroundColor:'rgba(201,162,75,.72)',borderRadius:5}]},options:{indexAxis:'y',responsive:true,maintainAspectRatio:false,scales:{x:{grid:{color:line}},y:{grid:{display:false}}},plugins:{legend:{display:false}}}});
mk('colorChart',{type:'doughnut',plugins:[htmlLegend],data:{labels:D.colors.labels,datasets:[{data:D.colors.counts,backgroundColor:D.colors.hex,borderColor:'#0e0c09',borderWidth:2}]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false},htmlLegend:{}},cutout:'58%'}});
diff --git a/public/portal.html b/public/portal.html
index 6b65b51..dfa2aa1 100644
--- a/public/portal.html
+++ b/public/portal.html
@@ -134,7 +134,7 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
<div class="scrim" onclick="document.body.classList.remove('nav')"></div>
<nav class="menu" id="portalmenu" onclick="if(event.target.closest('a'))document.body.classList.remove('nav')">
<a class="mbrand" href="#summary">Designer <span>Wallcoverings</span><small>Growth Strategy · 2026</small></a>
- <div class="mh">Contents</div><a href="#summary">Executive Summary</a><a href="#swot">SWOT Analysis</a><a href="#empire">The Catalog Empire</a><a href="#integrity">Catalog Integrity — the 5-Field Read</a><a href="#competitors">Competitive Landscape</a><a href="#advertising">Channels & Distribution</a><a href="#timing">Timing & Cadence</a><a href="#audit">Website Audit</a><a href="#social">Social Audit</a><a href="#suggested">Suggested New Flagships</a><a href="#partners">Unlikely Partners</a><a href="#revenue">Unlikely Revenue</a><a href="#desk">Live Commerce Desk</a><a href="#roadmap">90-Day Roadmap</a><a href="#sources">Sources & Methodology</a>
+ <div class="mh">Contents</div><a href="#summary">Executive Summary</a><a href="#swot">SWOT Analysis</a><a href="#empire">The Catalog Empire</a><a href="#integrity">Catalog Integrity — the 5-Field Read</a><a href="#competitors">Competitive Landscape</a><a href="#advertising">Channels & Distribution</a><a href="#traffic">Traffic & Search Reality</a><a href="#timing">Timing & Cadence</a><a href="#audit">Website Audit</a><a href="#social">Social Audit</a><a href="#suggested">Suggested New Flagships</a><a href="#partners">Unlikely Partners</a><a href="#revenue">Unlikely Revenue</a><a href="#desk">Live Commerce Desk</a><a href="#roadmap">90-Day Roadmap</a><a href="#sources">Sources & Methodology</a>
<div class="minfo"><div class="mh">Designer Wallcoverings</div><p>Los Angeles · nationwide e-commerce + trade</p><p>steve@designerwallcoverings.com</p><p>designerwallcoverings.com</p></div>
</nav>
@@ -211,23 +211,37 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
</div>
</div></section>
-<section id="timing"><div class="eyebrow">05 · Timing & Cadence</div><h2 class="disp">The drip is live. Point it at revenue.</h2><div class="accbody">
+<section id="traffic"><div class="eyebrow">05 · Traffic & Search Reality</div><h2 class="disp">What Google actually sends — and what it's offering.</h2><div class="accbody">
+ <p class="lead2">Search Console, last 28 days: <b>957</b> clicks on <b>49,214</b> impressions (CTR 1.9%, avg position 9.0). <b>92.1% of clicks are non-branded</b> — people finding DW without knowing DW. That's earned discovery, and it makes the next number matter more.</p>
+ <div class="flagbox"><div class="i">▼</div><div><h3 class="disp">The decline nobody's measuring</h3><p>Organic clicks have fallen <b>51%</b> since Jan — 4,790/mo then, <b>2,335</b>/mo now — while the catalog grew by tens of thousands of products. And <b>no GA4 property exists on the main store</b>, so nothing downstream of the click is measured at all. This is the brief's most urgent measured finding: the roadmap's Week-1 attribution KPI is not optional hygiene, it's the tourniquet.</p></div></div>
+ <div class="chartwrap"><div class="ct">Organic clicks per month — the trend, unvarnished (GSC)</div><canvas id="gscChart"></canvas></div>
+ <div class="eyebrow" style="margin-top:52px">Striking distance — page-one demand already offered</div>
+ <p class="lead2">Queries ranking positions 8–15 with real impression volume: each is a content/AEO sprint away from page-one clicks. This is the cheapest traffic DW can buy, because it's not bought.</p>
+ <table><thead><tr><th>Query</th><th>Position</th><th>Impressions (28d)</th><th>Clicks</th></tr></thead><tbody><tr><td><b>versace wallpaper</b></td><td>8.0</td><td>1,913</td><td>27</td></tr><tr><td><b>wall coverings</b></td><td>15.1</td><td>1,213</td><td>1</td></tr><tr><td><b>osborne and little</b></td><td>8.7</td><td>1,102</td><td>2</td></tr><tr><td><b>designer wallpaper</b></td><td>19.8</td><td>896</td><td>4</td></tr><tr><td><b>zuber wallpaper</b></td><td>9.9</td><td>789</td><td>6</td></tr><tr><td><b>ralph lauren fabric</b></td><td>11.4</td><td>786</td><td>9</td></tr><tr><td><b>maya romanoff wallpaper</b></td><td>10.4</td><td>759</td><td>2</td></tr><tr><td><b>zuber</b></td><td>8.6</td><td>683</td><td>1</td></tr></tbody></table>
+ <div class="duo" style="margin-top:34px">
+ <div><div class="eyebrow">Top queries</div><table><thead><tr><th>Query</th><th>Clicks</th><th>Impr.</th><th>CTR</th><th>Pos.</th></tr></thead><tbody><tr><td><b>designer wallcoverings</b></td><td>60</td><td>227</td><td>26.4%</td><td>4.2</td></tr><tr><td><b>ralph lauren wallpaper</b></td><td>49</td><td>2,290</td><td>2.1%</td><td>6.0</td></tr><tr><td><b>versace wallpaper</b></td><td>27</td><td>1,913</td><td>1.4%</td><td>8.0</td></tr><tr><td><b>velvet wallpaper</b></td><td>22</td><td>1,121</td><td>2.0%</td><td>7.1</td></tr><tr><td><b>lee jofa</b></td><td>20</td><td>1,479</td><td>1.4%</td><td>5.8</td></tr><tr><td><b>lee jofa wallpaper</b></td><td>16</td><td>538</td><td>3.0%</td><td>5.7</td></tr><tr><td><b>missoni fabric</b></td><td>16</td><td>1,255</td><td>1.3%</td><td>4.6</td></tr><tr><td><b>american silk mills</b></td><td>14</td><td>55</td><td>25.5%</td><td>2.2</td></tr></tbody></table></div>
+ <div><div class="eyebrow">Top pages</div><table><thead><tr><th>Path</th><th>Clicks</th><th>Impr.</th></tr></thead><tbody><tr><td><b>/</b></td><td>93</td><td>1,547</td></tr><tr><td><b>/collections/ralph-lauren-home</b></td><td>73</td><td>2,987</td></tr><tr><td><b>/collections/missoni-home</b></td><td>43</td><td>1,825</td></tr><tr><td><b>/collections/lee-jofa</b></td><td>40</td><td>2,339</td></tr><tr><td><b>/collections/champs-elysees-chenille</b></td><td>32</td><td>1,987</td></tr><tr><td><b>/collections/flocked-velvet-wallpaper-collection</b></td><td>31</td><td>2,203</td></tr><tr><td><b>/collections/versace-wallpaper</b></td><td>23</td><td>1,749</td></tr><tr><td><b>/pages/bn-wallcoverings</b></td><td>22</td><td>307</td></tr></tbody></table></div>
+ </div>
+ <p class="lead2" style="font-size:14px;opacity:.6;margin-top:18px">Source: cached-dw-seo-report · pulled 6 day(s) before this snapshot · data through 2026-07-17. GA4: not provisioned — a roadmap item.</p>
+</div></section>
+
+<section id="timing"><div class="eyebrow">06 · Timing & Cadence</div><h2 class="disp">The drip is live. Point it at revenue.</h2><div class="accbody">
<p class="lead2">The growth curve, measured: monthly activations ramped from <b>223</b> in Jan to <b>10,349</b> in Jul — a 46× ramp as the activation engine came online. The merchandising calendar below aligns campaigns to the category's natural intent cycle.</p>
<div class="chartwrap"><div class="ct">The ramp — new actives per month, last 7 months (live)</div><canvas id="rampChart"></canvas></div>
<div class="chartwrap"><div class="ct">New active products per week — last 12 weeks (live)</div><canvas id="cadenceChart"></canvas></div>
<table><thead><tr><th>Month</th><th>What the market is doing</th><th>The DW play</th></tr></thead><tbody><tr><td class="mo">Jan</td><td>New-year refresh intent peaks</td><td class="act">Push "one statement wall" entry offers; retarget Q4 samplers</td></tr><tr><td class="mo">Feb</td><td>Design-award season</td><td class="act">Editorial: trend reports off the catalog's own tag data</td></tr><tr><td class="mo">Mar</td><td>Spring project starts</td><td class="act">Trade outreach wave; hospitality spec season begins</td></tr><tr><td class="mo">Apr</td><td>High-intent renovation search</td><td class="act">Paid search on pattern + brand terms; AEO content sprint</td></tr><tr><td class="mo">May</td><td>Peak sample-ordering</td><td class="act">Sample-bundle promos; instrument sample→roll conversion</td></tr><tr><td class="mo">Jun</td><td>Mid-year new-arrivals surge</td><td class="act">The activation drip = daily "new" stories across channels</td></tr><tr><td class="mo">Jul</td><td>Summer install season</td><td class="act">Installer-partner co-marketing; project galleries</td></tr><tr><td class="mo">Aug</td><td>Pre-fall trade buying</td><td class="act">Designer-net program push; hospitality lookbook release</td></tr><tr><td class="mo">Sep</td><td>Fall flagship season</td><td class="act">Concept-direction launch (the new front page ships here)</td></tr><tr><td class="mo">Oct</td><td>Q4 project commits</td><td class="act">Retarget spring samplers; gift-adjacent art-line cross-sell</td></tr><tr><td class="mo">Nov</td><td>Holiday statement walls</td><td class="act">Peel-and-stick line forward; fast-ship messaging</td></tr><tr><td class="mo">Dec</td><td>Planning season</td><td class="act">Year-in-review design content; January intent capture</td></tr></tbody></table>
</div></section>
-<section id="audit"><div class="eyebrow">06 · Website Audit</div><h2 class="disp">A strong engine that hides its best features.</h2><div class="accbody">
+<section id="audit"><div class="eyebrow">07 · Website Audit</div><h2 class="disp">A strong engine that hides its best features.</h2><div class="accbody">
<p class="lead2">Analyst audit of the live flagship (Shopify + Boost search): the merchandising infrastructure is genuinely advanced — the gap is guided discovery for the retail buyer facing 80,542 choices.</p>
<div class="cards2">
<div class="c"><h3>Working well</h3><p class="good">Left-rail collection navigation · shop-by-color wheel with type pills · AEO content blocks on collections · sort + density controls on grids · New Arrivals / Trending smart collections refreshed by daily tag-sync · $4.25 memo-sample funnel on every product.</p></div>
<div class="c"><h3>Gaps</h3><p class="flag">Sample-only products present a dead-end buy box (22,952 products) · zero-price actives surface in feeds (1,444) · 333 actives lack a featured image · curation layers (rooms, moods, designer picks) are thin relative to catalog depth.</p></div>
- <div class="c"><h3>Action</h3><p>Fix the integrity backlog (Section 02), then ship a guided-discovery layer: room/mood entry points, "pairs well with" on every PDP, and a designer-picks editorial tier — the concept directions in Section 08 show the face of it.</p></div>
+ <div class="c"><h3>Action</h3><p>Fix the integrity backlog (Section 02), then ship a guided-discovery layer: room/mood entry points, "pairs well with" on every PDP, and a designer-picks editorial tier — the concept directions in Section 09 show the face of it.</p></div>
</div>
</div></section>
-<section id="social"><div class="eyebrow">07 · Social Audit</div><h2 class="disp">Broad reach, quiet voice.</h2><div class="accbody">
+<section id="social"><div class="eyebrow">08 · Social Audit</div><h2 class="disp">Broad reach, quiet voice.</h2><div class="accbody">
<p class="lead2">All major platforms are connected through one command center — a real infrastructure asset. The audit finding is consistent across platforms: <b>distribution exists, engagement discipline doesn't yet</b>. The page network posts; it doesn't yet converse, attribute, or convert.</p>
<div class="cards2">
<div class="c"><h3>Connected & ready</h3><p class="good">Facebook Pages · Instagram · TikTok · YouTube · Bluesky · Threads · LinkedIn</p></div>
@@ -236,7 +250,7 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
</div>
</div></section>
-<section id="suggested"><div class="eyebrow">08 · Suggested New Flagships</div><h2 class="disp">Six directions worthy of the catalog.</h2><div class="accbody">
+<section id="suggested"><div class="eyebrow">09 · Suggested New Flagships</div><h2 class="disp">Six directions worthy of the catalog.</h2><div class="accbody">
<p class="lead2">Six full front-page concepts — each after a house that owns its lane, each a working page with the mandatory sort + density controls on its grid. Not palette swaps: different merchandising philosophies. Open each live and pick the direction.</p>
<div class="vgrid">
<a class="vtile rv" href="/versions/atelier.html" target="_blank" style="--vb:#f4f0e6;--vi:#26221a;--vg:#8a7340">
@@ -271,12 +285,12 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
</a></div>
</div></section>
-<section id="partners"><div class="eyebrow">09 · Unlikely Partners</div><h2 class="disp">Channels that don't look like channels.</h2><div class="accbody">
+<section id="partners"><div class="eyebrow">10 · Unlikely Partners</div><h2 class="disp">Channels that don't look like channels.</h2><div class="accbody">
<p class="lead2">Eight partnership lanes where DW's LA position and trade heritage do the acquisition work.</p>
<div class="pcard rv"><div class="pnum">01</div><div><h4>Interior-design trade programs (ASID, IIDA chapters)</h4><p>The trade IS the buyer. Chapter sponsorships + a designer-net portal turn 30 years of relationships into a structured acquisition channel.</p></div></div><div class="pcard rv"><div class="pnum">02</div><div><h4>Professional installers / paper-hangers network</h4><p>An installer directory referral loop: DW sends install-ready clients, installers send spec-stage clients. Both sides win on every roll.</p></div></div><div class="pcard rv"><div class="pnum">03</div><div><h4>Boutique hotels & hospitality designers</h4><p>Contract lines (Type II) already in catalog — a hospitality lookbook + spec-sheet service opens recurring project-scale orders.</p></div></div><div class="pcard rv"><div class="pnum">04</div><div><h4>Real-estate stagers & luxury listing agents</h4><p>Statement walls sell listings. A stager program (rapid samples + the peel-and-stick line) creates repeat commercial buyers.</p></div></div><div class="pcard rv"><div class="pnum">05</div><div><h4>Art-print & framing houses</h4><p>The art-print line proves the adjacency: walls + art as one "finished room" offer, cross-sold at zero acquisition cost.</p></div></div><div class="pcard rv"><div class="pnum">06</div><div><h4>Paint brands & color consultants</h4><p>Color-matched pairings ("this paper + these three paints") — co-marketing that borrows the paint brand's search volume.</p></div></div><div class="pcard rv"><div class="pnum">07</div><div><h4>Design schools (LA trade programs)</h4><p>Student memo access + a young-designer program seeds the next decade of trade accounts for the cost of samples.</p></div></div><div class="pcard rv"><div class="pnum">08</div><div><h4>Film / TV set decorators</h4><p>LA-native advantage: a set-decorator fast lane (rush memos, buyout-friendly licensing) monetizes proximity to the studios.</p></div></div>
</div></section>
-<section id="revenue"><div class="eyebrow">10 · Unlikely Revenue</div><h2 class="disp">The money already in the building.</h2><div class="accbody">
+<section id="revenue"><div class="eyebrow">11 · Unlikely Revenue</div><h2 class="disp">The money already in the building.</h2><div class="accbody">
<p class="lead2">Before any new audience is bought, six revenue pools sit inside the existing operation:</p>
<div class="sugg">
<div class="s"><div class="p">Pool 1 · largest</div><h4>The sellable-variant backlog</h4><p>22,952 active products browseable but not buyable. Even a 1% conversion at category AOV is a six-figure annual pool. Remediation is data work, not marketing spend.</p></div>
@@ -302,7 +316,7 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
<div class="chartwrap"><div class="ct">Priced-subset architecture (basis: min variant > $4.25 memo — partial, labeled)</div><canvas id="bandChart"></canvas></div>
</div></section>
-<section id="desk"><div class="eyebrow">11 · Live Commerce Desk</div><h2 class="disp">The operation, on one screen.</h2><div class="accbody">
+<section id="desk"><div class="eyebrow">12 · Live Commerce Desk</div><h2 class="disp">The operation, on one screen.</h2><div class="accbody">
<p class="lead2">Live KPIs from the Jul 26, 2026, 9:31 AM snapshot — the numbers a weekly growth review should open with. Hit ↻ Refresh (top right) to re-collect on demand.</p>
<div class="kpis">
<div class="kpi"><div class="kv">80,542</div><div class="kl">Active products</div></div>
@@ -317,19 +331,19 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
<div class="chartwrap"><div class="ct">Weekly activation cadence (same data as Section 05 — the desk's pulse line)</div><canvas id="deskChart"></canvas></div>
</div></section>
-<section id="roadmap"><div class="eyebrow">12 · 90-Day Roadmap</div><h2 class="disp">What to do, in order.</h2><div class="accbody">
+<section id="roadmap"><div class="eyebrow">13 · 90-Day Roadmap</div><h2 class="disp">What to do, in order.</h2><div class="accbody">
<ul class="road">
- <li><div class="w">Weeks 1–2</div><div class="d"><b>Integrity sprint:</b> triage the 22,952 sample-only backlog by vendor (top 8 lines = 16,664 products); fix zero-price and image-less actives. Wire per-channel UTM + coupon attribution. Pick a concept direction from Section 08.<br><span style="color:var(--gold);font-size:14px">KPI · 2,500+ sellable variants added · zero-price actives < 500 · UTM live on all 7 channels · concept direction chosen (Week-2 decision gate)</span></div></li>
+ <li><div class="w">Weeks 1–2</div><div class="d"><b>Integrity sprint:</b> triage the 22,952 sample-only backlog by vendor (top 8 lines = 16,664 products); fix zero-price and image-less actives. Wire per-channel UTM + coupon attribution. Pick a concept direction from Section 09.<br><span style="color:var(--gold);font-size:14px">KPI · 2,500+ sellable variants added · zero-price actives < 500 · UTM live on all 7 channels · concept direction chosen (Week-2 decision gate)</span></div></li>
<li><div class="w">Weeks 3–6</div><div class="d"><b>Convert intent:</b> ship the sampled-but-didn't-buy email sequence off the memo funnel. Sync daily social posting to the activation drip. Start the designer-net trade portal build.<br><span style="color:var(--gold);font-size:14px">KPI · first attributed sample→roll conversion · sequence live with ≥25% open rate · 7 drip-synced posts/week shipping · re-collect this brief and re-baseline (↻)</span></div></li>
<li><div class="w">Weeks 6–10</div><div class="d"><b>Ship the face:</b> launch the chosen flagship direction (guided discovery: rooms, moods, designer picks). Extend AEO blocks fleet-wide. Sign 3 unlikely-partner deals (one installer network, one stager program, one hospitality designer).<br><span style="color:var(--gold);font-size:14px">KPI · new front page live · AEO blocks on 100+ fleet sites · 3 signed partner agreements · backlog under 15,000</span></div></li>
<li><div class="w">Weeks 10–13</div><div class="d"><b>Measure & double down:</b> channel-level revenue review off the new attribution; scale the winner. Open the contract/hospitality quote desk. Publish the first licensing-line results.<br><span style="color:var(--gold);font-size:14px">KPI · every channel shows attributed revenue (or is cut) · first contract quote issued · Week-13 review: backlog, conversion rate, channel ROI vs this brief's baseline</span></div></li>
</ul>
</div></section>
-<section id="sources"><div class="eyebrow">13 · Sources & Methodology</div><h2 class="disp">Where every number comes from.</h2><div class="accbody">
+<section id="sources"><div class="eyebrow">14 · Sources & Methodology</div><h2 class="disp">Where every number comes from.</h2><div class="accbody">
<p class="lead2">Snapshot collected <b>Jul 26, 2026, 9:31 AM</b> by a read-only collector. Live rows are measured; analyst rows are authored judgment; anything illustrative says so.</p>
<div class="srclegend"><span class="srcb r">Live snapshot</span><span class="srcb o">Observed live</span><span class="srcb a">Analyst view</span><span class="srcb d">Computed</span><span class="srcb e">Illustrative</span></div>
- <table><thead><tr><th>Section</th><th>Where it comes from</th><th>How it's produced</th><th>Provenance</th></tr></thead><tbody><tr><td><b>Catalog KPIs — statuses, vendors, types, integrity, cadence</b></td><td>The dw_unified local mirror (READ-ONLY psql over the /tmp socket)</td><td>Collected by <code>scripts/collect-dw.mjs</code> at <b>Jul 26, 2026, 9:31 AM</b>; every number on this page renders from that snapshot</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>5-field / sample-only split</b></td><td>Mirror rollup columns (has_sample_variant, has_product_variant, has_description, image_url) + the quotes tag</td><td>Direct SQL counts; quote-only lines (by-design sample-only) separated from true backlog</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Social channel status</b></td><td>The DW marketing command center API (marketing.designerwallcoverings.com)</td><td>Authenticated status read at collection time — connected flags + account counts per platform</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Microsite fleet</b></td><td>The fleet aggregator directory API (all.designerwallcoverings.com)</td><td>281 sites enumerated at collection time</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Competitor watch-list</b></td><td>The connie_competitors table (the DW competitor-scan registry)</td><td>Rendered verbatim: domain, watch status, products found by prior scans</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Competitor paid-ads table</b></td><td>A $0 homepage pixel scan of each tracked domain (Google/Meta/TikTok/Pinterest/Bing tag signatures)</td><td>Measured at seed time by <code>scripts/seed-buckets.mjs</code>; unreachable homepages say so rather than guessing</td><td class="srccell"><span class="srcb o">Observed live</span></td></tr><tr><td><b>Competitor scoring chart + map pins</b></td><td>Analyst assessment over public sites; HQ geo compiled from public records</td><td>Authored consulting view — NOT scraped metrics; the map is oriented, not surveyed</td><td class="srccell"><span class="srcb a">Analyst view</span></td></tr><tr><td><b>Price-band chart</b></td><td>products whose minimum variant exceeds the $4.25 memo sample — the priced subset of the mirror (~3.8k); most actives resolve min-variant to the sample, and full variant-level pricing lives Shopify-side</td><td>Partial-basis chart, labeled as such — full variant pricing lives Shopify-side</td><td class="srccell"><span class="srcb d">Computed</span></td></tr><tr><td><b>Website & social audits, partners, revenue plays, roadmap</b></td><td>Analyst strategy grounded in the operating knowledge of the DW stack</td><td>Consulting recommendations — models and plays, not measurements</td><td class="srccell"><span class="srcb a">Analyst view</span></td></tr><tr><td><b>Catalog DNA — style, color & workflow-tag charts</b></td><td>Tag-match SQL over all actives (the same tag buckets the storefront sort uses)</td><td>Live counts per style/color/Needs-* tag; the color doughnut renders in representative hues of the tag names</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Monthly activation ramp</b></td><td>created_at_shopify over the last 7 calendar months, live mirror</td><td>Direct SQL; the ramp multiplier in the copy is computed from the same series</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Product mosaic + concept-grid imagery</b></td><td>The store's own CDN (one current ACTIVE product per vendor line, forbidden-vendor-filtered)</td><td>Already customer-facing on the live store; hotlinked at thumbnail width</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Hero imagery</b></td><td>Unsplash editorial stock (luxury materials, per the luxe-only imagery rule)</td><td>Hotlinked, decorative only — carries no data meaning</td><td class="srccell"><span class="srcb e">Illustrative</span></td></tr></tbody></table>
+ <table><thead><tr><th>Section</th><th>Where it comes from</th><th>How it's produced</th><th>Provenance</th></tr></thead><tbody><tr><td><b>Catalog KPIs — statuses, vendors, types, integrity, cadence</b></td><td>The dw_unified local mirror (READ-ONLY psql over the /tmp socket)</td><td>Collected by <code>scripts/collect-dw.mjs</code> at <b>Jul 26, 2026, 9:31 AM</b>; every number on this page renders from that snapshot</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>5-field / sample-only split</b></td><td>Mirror rollup columns (has_sample_variant, has_product_variant, has_description, image_url) + the quotes tag</td><td>Direct SQL counts; quote-only lines (by-design sample-only) separated from true backlog</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Social channel status</b></td><td>The DW marketing command center API (marketing.designerwallcoverings.com)</td><td>Authenticated status read at collection time — connected flags + account counts per platform</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Microsite fleet</b></td><td>The fleet aggregator directory API (all.designerwallcoverings.com)</td><td>281 sites enumerated at collection time</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Competitor watch-list</b></td><td>The connie_competitors table (the DW competitor-scan registry)</td><td>Rendered verbatim: domain, watch status, products found by prior scans</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Competitor paid-ads table</b></td><td>A $0 homepage pixel scan of each tracked domain (Google/Meta/TikTok/Pinterest/Bing tag signatures)</td><td>Measured at seed time by <code>scripts/seed-buckets.mjs</code>; unreachable homepages say so rather than guessing</td><td class="srccell"><span class="srcb o">Observed live</span></td></tr><tr><td><b>Competitor scoring chart + map pins</b></td><td>Analyst assessment over public sites; HQ geo compiled from public records</td><td>Authored consulting view — NOT scraped metrics; the map is oriented, not surveyed</td><td class="srccell"><span class="srcb a">Analyst view</span></td></tr><tr><td><b>Price-band chart</b></td><td>products whose minimum variant exceeds the $4.25 memo sample — the priced subset of the mirror (~3.8k); most actives resolve min-variant to the sample, and full variant-level pricing lives Shopify-side</td><td>Partial-basis chart, labeled as such — full variant pricing lives Shopify-side</td><td class="srccell"><span class="srcb d">Computed</span></td></tr><tr><td><b>Website & social audits, partners, revenue plays, roadmap</b></td><td>Analyst strategy grounded in the operating knowledge of the DW stack</td><td>Consulting recommendations — models and plays, not measurements</td><td class="srccell"><span class="srcb a">Analyst view</span></td></tr><tr><td><b>Catalog DNA — style, color & workflow-tag charts</b></td><td>Tag-match SQL over all actives (the same tag buckets the storefront sort uses)</td><td>Live counts per style/color/Needs-* tag; the color doughnut renders in representative hues of the tag names</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Monthly activation ramp</b></td><td>created_at_shopify over the last 7 calendar months, live mirror</td><td>Direct SQL; the ramp multiplier in the copy is computed from the same series</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Product mosaic + concept-grid imagery</b></td><td>The store's own CDN (one current ACTIVE product per vendor line, forbidden-vendor-filtered)</td><td>Already customer-facing on the live store; hotlinked at thumbnail width</td><td class="srccell"><span class="srcb r">Live snapshot</span></td></tr><tr><td><b>Traffic & Search Reality</b></td><td>Google Search Console via the cached DW SEO report (age-labeled in the section footer)</td><td>Totals, branded split, striking-distance and page tables rendered verbatim; the decline copy is computed from the same monthly series</td><td class="srccell"><span class="srcb o">Observed live</span></td></tr><tr><td><b>Hero imagery</b></td><td>Unsplash editorial stock (luxury materials, per the luxe-only imagery rule)</td><td>Hotlinked, decorative only — carries no data meaning</td><td class="srccell"><span class="srcb e">Illustrative</span></td></tr></tbody></table>
<div class="srcnote"><div class="i">⚙</div><div><h4>How this document is built</h4><p>Generated, not hand-typed. A read-only collector (<code>scripts/collect-dw.mjs</code>) snapshots the DW systems into <code>data/dw-analysis.json</code>; the build (<code>build.mjs</code>) renders that snapshot into this page. The ↻ Refresh button re-runs collector + build on demand. Nothing writes to any DW production system.</p></div></div>
</div></section>
@@ -361,6 +375,7 @@ function initCharts(){if(charted||!window.Chart)return;charted=true;
Chart.defaults.animation=RM?false:{duration:1150,easing:'easeInOutQuart'};
if(!RM){Chart.defaults.animations={y:{from:function(c){return c.chart.scales.y?c.chart.scales.y.getPixelForValue(0):0;}}};}
var D={"cadence":{"labels":["05-11","05-25","06-01","06-08","06-15","06-22","06-29","07-06","07-13","07-20"],"added":[490,3,492,924,1993,1510,1854,4260,1486,3377]},"vendors":{"labels":["Phillipe Romano","Rebel Walls","Clarke And Clarke","Hollywood Wallcoverings","Malibu Wallpaper","Thibaut","Jeffrey Stevens","Brunschwig & Fils","Kravet","Phillip Jeffries"],"counts":[11270,3770,3570,3515,3232,2987,2899,2761,2616,2469]},"types":{"labels":["Wallcovering","Fabric","Commercial Fabric","Mural","Wallcoverings","Memo Sample"],"counts":[58298,16200,1797,1305,453,451]},"integrity":{"labels":["Missing sellable variant","Missing sample variant","Zero / null price","Missing image","Missing description"],"counts":[26742,2298,1444,333,172]},"bands":{"labels":["Under $50","$50–150","$150–300","$300–600","$600+"],"counts":[2118,699,435,443,129]},"channels":{"labels":["Facebook Pages","Instagram","TikTok","YouTube","Bluesky","Threads","LinkedIn"],"accounts":[80,34,1,1,1,1,1]},"sampleOnlyVendors":{"labels":["Phillipe Romano","Phillip Jeffries","Koroseal","China Seas","Coordonné","Hollywood Wallcoverings","Designers Guild","Osborne & Little"],"counts":[5924,2461,2454,1776,1300,1089,840,820]},"sampleRoll":{"labels":["Phillipe Romano","Phillip Jeffries","Koroseal","China Seas","Coordonné","Hollywood Wallcoverings","Designers Guild","Osborne & Little"],"converted":[4064,8,1,0,131,2426,0,111],"leak":[3521,2461,2368,1776,1300,842,839,820]},"comp":{"labels":["Designer Wallcoverings","DecoratorsBest","Burke Decor","Mahones","Finest Wallpaper","Steve's B&W"],"catalog":[96,62,48,55,40,45],"digital":[58,82,78,60,42,55]},"monthly":{"labels":["Jan","Feb","Mar","Apr","May","Jun","Jul"],"added":[223,1363,4578,3416,493,5547,10349]},"needs":{"labels":["Needs Price","Needs Image","Needs Width","Needs Description"],"counts":[19246,16531,19656,27]},"styles":{"labels":["Contemporary","Textured","Traditional","Geometric","Floral","Grasscloth","Stripe","Mural","Metallic","Damask"],"counts":[32580,29302,22704,11748,10286,9846,9153,4769,3212,1926]},"colors":{"labels":["Beige","White","Gray","Blue","Green","Brown","Black","Gold","Pink","Red"],"counts":[22079,22061,12163,8551,7749,5192,4883,4326,3456,2946],"hex":["#d9c9a8","#f2efe8","#9a9a99","#5a7fa8","#6b8f6d","#8a6a4c","#2b2b2b","#c9a24b","#d3a0a6","#a84a44"]}};
+ var TRD={"labels":["Jan","Feb","Mar","Apr","May","Jun","Jul"],"clicks":[4790,8903,7318,5477,4908,4249,2335]};
function mk(id,cfg){var el=document.getElementById(id);if(el)new Chart(el,cfg);}
// Custom HTML legend for the doughnuts — thin 2x16px line markers instead of box swatches, click-to-toggle
var htmlLegend={id:'htmlLegend',afterUpdate:function(chart){
@@ -377,6 +392,7 @@ function initCharts(){if(charted||!window.Chart)return;charted=true;
// Vertical gold gradient for area fills — scriptable so it re-derives per chart area
function goldFill(a){return function(c){var ch=c.chart,area=ch.chartArea;if(!area)return 'rgba(201,162,75,.12)';var g=ch.ctx.createLinearGradient(0,area.top,0,area.bottom);g.addColorStop(0,'rgba(201,162,75,'+a+')');g.addColorStop(1,'rgba(201,162,75,0)');return g;};}
mk('rampChart',{type:'bar',data:{labels:D.monthly.labels,datasets:[{label:'New actives / month',data:D.monthly.added,backgroundColor:function(c){var ch=c.chart,area=ch.chartArea;if(!area)return 'rgba(201,162,75,.7)';var g=ch.ctx.createLinearGradient(0,area.bottom,0,area.top);g.addColorStop(0,'rgba(201,162,75,.25)');g.addColorStop(1,'rgba(201,162,75,.9)');return g;},borderRadius:5}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{grid:{color:line}},x:{grid:{display:false}}},plugins:{legend:{display:false}}}});
+ mk('gscChart',{type:'line',data:{labels:TRD.labels,datasets:[{label:'Organic clicks / month',data:TRD.clicks,borderColor:'#d4647a',backgroundColor:function(c){var ch=c.chart,area=ch.chartArea;if(!area)return 'rgba(212,100,122,.10)';var g=ch.ctx.createLinearGradient(0,area.top,0,area.bottom);g.addColorStop(0,'rgba(212,100,122,.30)');g.addColorStop(1,'rgba(212,100,122,0)');return g;},fill:true,tension:.4,pointRadius:5,pointBackgroundColor:'#d4647a',pointBorderWidth:0}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{grid:{color:line}},x:{grid:{display:false}}},plugins:{legend:{display:false}}}});
mk('needsChart',{type:'bar',data:{labels:D.needs.labels,datasets:[{label:'Actives tagged',data:D.needs.counts,backgroundColor:'rgba(212,100,122,.85)',borderRadius:5}]},options:{indexAxis:'y',responsive:true,maintainAspectRatio:false,scales:{x:{grid:{color:line}},y:{grid:{display:false}}},plugins:{legend:{display:false}}}});
mk('styleChart',{type:'bar',data:{labels:D.styles.labels,datasets:[{label:'Actives tagged',data:D.styles.counts,backgroundColor:'rgba(201,162,75,.72)',borderRadius:5}]},options:{indexAxis:'y',responsive:true,maintainAspectRatio:false,scales:{x:{grid:{color:line}},y:{grid:{display:false}}},plugins:{legend:{display:false}}}});
mk('colorChart',{type:'doughnut',plugins:[htmlLegend],data:{labels:D.colors.labels,datasets:[{data:D.colors.counts,backgroundColor:D.colors.hex,borderColor:'#0e0c09',borderWidth:2}]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false},htmlLegend:{}},cutout:'58%'}});
← ec34562 weekly-refresh.sh: harden with set -euo pipefail note; drop
·
back to Consulting Designerwallcoverings Com
·
attribution prep (DTD-A): per-channel UTM+coupon scheme (dat a4ad6c6 →