← back to Consulting Designerwallcoverings Com
Unlikely Revenue: quantify sample→roll funnel (Pool 2) from dw_unified mirror
26ca9ee2967bb9ddb1745b230b0fd156d3efd823 · 2026-07-26 09:04:06 -0700 · Steve Abrams
Collector: sample_roll block — funnel (78,244 sampleable → 66% buyable, 26,580 gap
split 3,790 quote-only / 22,790 recoverable), per-vendor recoverable leak, and
order-data provenance (no shopify_orders; mp_orders seed, digital_sample_orders thin).
Brief: Pool 2 rewritten with measured figures, one stacked sample→roll chart by line,
and a Tier A/B/C provenance box — upper-bound labeled, Tier-C names the instrumentation
gap as the recommendation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Files touched
M build.mjsM data/dw-analysis.jsonM public/portal.htmlM scripts/collect-dw.mjs
Diff
commit 26ca9ee2967bb9ddb1745b230b0fd156d3efd823
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Jul 26 09:04:06 2026 -0700
Unlikely Revenue: quantify sample→roll funnel (Pool 2) from dw_unified mirror
Collector: sample_roll block — funnel (78,244 sampleable → 66% buyable, 26,580 gap
split 3,790 quote-only / 22,790 recoverable), per-vendor recoverable leak, and
order-data provenance (no shopify_orders; mp_orders seed, digital_sample_orders thin).
Brief: Pool 2 rewritten with measured figures, one stacked sample→roll chart by line,
and a Tier A/B/C provenance box — upper-bound labeled, Tier-C names the instrumentation
gap as the recommendation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
build.mjs | 24 ++++++++++++-
data/dw-analysis.json | 94 +++++++++++++++++++++++++++++++++++++++++++++++++-
public/portal.html | 30 +++++++++++-----
scripts/collect-dw.mjs | 48 ++++++++++++++++++++++++++
4 files changed, 186 insertions(+), 10 deletions(-)
diff --git a/build.mjs b/build.mjs
index b44cc98..7eea3e0 100644
--- a/build.mjs
+++ b/build.mjs
@@ -20,6 +20,9 @@ const GALLERY = Array.isArray(SNAP.gallery) ? SNAP.gallery : [];
const C = SNAP.catalog || {};
const FF = C.five_field || {};
const SO = C.sample_only || { total: 0, quote_only_by_design: 0, backlog: 0 };
+const SR = C.sample_roll || {};
+const SRF = SR.funnel || {};
+const SRO = SR.order_data || {};
const fmt = n => (n == null ? '—' : Number(n).toLocaleString());
const collected = SNAP.collected_at ? new Date(SNAP.collected_at) : null;
const collectedLabel = collected ? collected.toLocaleString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'not yet collected';
@@ -68,6 +71,11 @@ const CHART = {
bands: { labels: (C.price_bands || []).map(b => b.band), counts: (C.price_bands || []).map(b => b.count) },
channels: { labels: (SNAP.channels || []).map(c => c.label || c.key), accounts: (SNAP.channels || []).map(c => c.accounts || 0) },
sampleOnlyVendors: { labels: (C.sample_only_vendors || []).map(v => v.vendor), counts: (C.sample_only_vendors || []).map(v => v.count) },
+ sampleRoll: {
+ labels: (SR.vendors || []).slice(0, 8).map(v => v.vendor),
+ converted: (SR.vendors || []).slice(0, 8).map(v => v.converted),
+ leak: (SR.vendors || []).slice(0, 8).map(v => v.leak),
+ },
// Analyst-scored competitive read (authority vs digital polish, 0-100) — analyst tier
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] },
@@ -456,12 +464,25 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
<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>
- <div class="s"><div class="p">Pool 2 · compounding</div><h4>Sample → roll funnel</h4><p>The $4.25 memo program generates purchase-intent signals daily. Instrumented follow-up (sampled-but-didn't-buy sequences) converts existing intent at near-zero cost.</p></div>
+ <div class="s"><div class="p">Pool 2 · compounding</div><h4>Sample → roll funnel</h4><p>The $4.25 memo program is the top of the funnel. <b>${fmt(SRF.sampleable)}</b> active products can be memo-sampled, but only <b>${SRF.conversion_pct}%</b> have a roll a customer can then buy — leaving <b>${fmt(SRF.gap_backlog)}</b> recoverable dead-ends. Quantified below.</p></div>
<div class="s"><div class="p">Pool 3 · scheduled</div><h4>The staged-draft drip</h4><p>${fmt(C.drafts_total)} drafts activating ~500/day = a permanent "new arrivals" marketing engine. Sync email + social to the drip and every day ships its own campaign.</p></div>
<div class="s"><div class="p">Pool 4 · zero-inventory</div><h4>Original design licensing</h4><p>Settlement-gated original pattern IP licensed to print-on-demand marketplaces — revenue without inventory, already piloted in the wider operation.</p></div>
<div class="s"><div class="p">Pool 5 · trade</div><h4>Designer-net portal</h4><p>Formalize trade pricing behind a designer login. Trade buyers are the highest-LTV segment and currently buy through the retail flow.</p></div>
<div class="s"><div class="p">Pool 6 · projects</div><h4>Hospitality / contract desk</h4><p>Type II contract lines already in catalog. A spec-sheet + quote desk turns one hotel corridor into 400 rolls.</p></div>
</div>
+
+ <h3 class="disp" style="margin-top:38px">Pool 2, quantified — the sample → roll funnel</h3>
+ <p class="lead2">The $4.25 memo sample is a daily purchase-intent signal. The question is whether that intent has anywhere to convert. Measured from the live catalog mirror:</p>
+ <div class="kpis">
+ <div class="kpi"><div class="kv">${fmt(SRF.sampleable)}</div><div class="kl">Products a customer can memo-sample</div></div>
+ <div class="kpi"><div class="kv">${SRF.conversion_pct}%</div><div class="kl">…that also have a buyable roll (${fmt(SRF.converted)})</div></div>
+ <div class="kpi"><div class="kv">${fmt(SRF.gap_backlog)}</div><div class="kl">Recoverable dead-ends (sampled intent, no roll)</div></div>
+ <div class="kpi"><div class="kv">${fmt(SRF.gap_quote_only)}</div><div class="kl">Quote-only by design (excluded — not a leak)</div></div>
+ </div>
+ <p class="lead2">Two-thirds of the sampleable catalog converts cleanly; the remaining <b>${fmt(SRF.gap)}</b> products a customer can pay $4.25 to sample but cannot then buy. Of those, <b>${fmt(SRF.gap_quote_only)}</b> are intentionally quote-only, leaving a <b>${fmt(SRF.gap_backlog)}</b>-product recoverable pool — concentrated in a handful of lines. Fixing the roll variant on a line a customer already sampled is data work, not ad spend.</p>
+ <div class="chartwrap"><div class="ct">Sample → roll by line (top 8 by recoverable backlog): gold = memo converts to a buyable roll · rose = memo hits a dead end</div><canvas id="srChart"></canvas></div>
+ <div class="flagbox"><div class="i">◆</div><div><h3 class="disp">Provenance — tiered honestly</h3><p><b>Tier A (measured, catalog structure):</b> the funnel counts above come straight from the read-only <code>dw_unified</code> mirror — every ACTIVE product's memo-sample and roll variant flags. Solid.<br><b>Tier B (proxy, labeled):</b> "recoverable backlog" is <i>sampleable-but-not-buyable minus the 'quotes'-tagged lines</i>. That tag under-captures reality — contract lines like Koroseal and Phillip Jeffries are quote-only in practice but untagged — so <b>${fmt(SRF.gap_backlog)} is an upper bound</b>; the truly recoverable share is a subset, weighted toward the merchant private-label and residential lines.<br><b>Tier C (absent — the recommendation):</b> real memo-order-vs-roll-order <i>behavioral</i> conversion is <b>not in this mirror</b>. It is a catalog mirror — no <code>shopify_orders</code> table; <code>mp_orders</code> is ${fmt(SRO.mp_orders?.rows)} seed rows and <code>digital_sample_orders</code> only ${fmt(SRO.digital_sample_orders?.rows)} thin digital-only rows. So the true conversion <i>rate</i> can't be computed here — <b>instrumenting the memo→roll path is itself the first move</b> (Roadmap, Weeks 3–6).</p></div></div>
+
<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>
@@ -535,6 +556,7 @@ function initCharts(){if(charted||!window.Chart)return;charted=true;
mk('typeChart',{type:'doughnut',data:{labels:D.types.labels,datasets:[{data:D.types.counts,backgroundColor:['#c9a24b','#b78d3f','#a67c32','#d9b877','#8f6d2c','#e3c98d'],borderColor:'#0e0c09',borderWidth:2}]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{color:dim,boxWidth:12}}},cutout:'58%'}});
mk('ffChart',{type:'bar',data:{labels:D.integrity.labels,datasets:[{label:'Actives failing',data:D.integrity.counts,backgroundColor:D.integrity.counts.map(function(v){return v>10000?'rgba(212,100,122,.9)':'rgba(201,162,75,.6)';})}]},options:{indexAxis:'y',responsive:true,maintainAspectRatio:false,scales:{x:{grid:{color:line}},y:{grid:{display:false}}},plugins:{legend:{display:false}}}});
mk('soChart',{type:'bar',data:{labels:D.sampleOnlyVendors.labels,datasets:[{label:'Sample-only actives',data:D.sampleOnlyVendors.counts,backgroundColor:'rgba(212,100,122,.8)'}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{grid:{color:line}},x:{grid:{display:false}}},plugins:{legend:{display:false}}}});
+ mk('srChart',{type:'bar',data:{labels:D.sampleRoll.labels,datasets:[{label:'Roll available (sample converts)',data:D.sampleRoll.converted,backgroundColor:'rgba(201,162,75,.8)',borderRadius:3},{label:'Recoverable backlog (memo, no roll)',data:D.sampleRoll.leak,backgroundColor:'rgba(212,100,122,.85)',borderRadius:3}]},options:{indexAxis:'y',responsive:true,maintainAspectRatio:false,scales:{x:{stacked:true,grid:{color:line}},y:{stacked:true,grid:{display:false}}},plugins:{legend:{labels:{color:dim}}}}});
mk('compChart',{type:'bar',data:{labels:D.comp.labels,datasets:[{label:'Catalog depth',data:D.comp.catalog,backgroundColor:'rgba(201,162,75,.75)'},{label:'Digital polish',data:D.comp.digital,backgroundColor:'rgba(120,170,255,.6)'}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{grid:{color:line},max:100},x:{grid:{display:false}}},plugins:{legend:{labels:{color:dim}}}}});
mk('chanChart',{type:'bar',data:{labels:D.channels.labels,datasets:[{label:'Connected accounts',data:D.channels.accounts,backgroundColor:'rgba(201,162,75,.75)'}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{grid:{color:line}},x:{grid:{display:false}}},plugins:{legend:{display:false}}}});
mk('cadenceChart',{type:'line',data:{labels:D.cadence.labels,datasets:[{label:'New actives / week',data:D.cadence.added,borderColor:gold,backgroundColor:goldFill(.30),fill:true,tension:.4,pointRadius:5,pointBackgroundColor:gold,pointBorderWidth:0}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{grid:{color:line}},x:{grid:{display:false}}},plugins:{legend:{display:false}}}});
diff --git a/data/dw-analysis.json b/data/dw-analysis.json
index c5c887c..696d3e7 100644
--- a/data/dw-analysis.json
+++ b/data/dw-analysis.json
@@ -1,5 +1,5 @@
{
- "collected_at": "2026-07-26T15:55:16.907Z",
+ "collected_at": "2026-07-26T16:01:28.476Z",
"sources": {
"mirror": "dw_unified local mirror (host=/tmp) — READ ONLY",
"mcc": "marketing.designerwallcoverings.com/api/channels/status",
@@ -230,6 +230,98 @@
"count": 820
}
],
+ "sample_roll": {
+ "funnel": {
+ "sampleable": 78244,
+ "converted": 51664,
+ "gap": 26580,
+ "gap_quote_only": 3790,
+ "gap_backlog": 22790,
+ "conversion_pct": 66
+ },
+ "vendors": [
+ {
+ "vendor": "Phillipe Romano",
+ "sampleable": 9988,
+ "converted": 4064,
+ "leak": 3521
+ },
+ {
+ "vendor": "Phillip Jeffries",
+ "sampleable": 2469,
+ "converted": 8,
+ "leak": 2461
+ },
+ {
+ "vendor": "Koroseal",
+ "sampleable": 2369,
+ "converted": 1,
+ "leak": 2368
+ },
+ {
+ "vendor": "China Seas",
+ "sampleable": 1776,
+ "converted": 0,
+ "leak": 1776
+ },
+ {
+ "vendor": "Coordonné",
+ "sampleable": 1431,
+ "converted": 131,
+ "leak": 1300
+ },
+ {
+ "vendor": "Hollywood Wallcoverings",
+ "sampleable": 3514,
+ "converted": 2426,
+ "leak": 842
+ },
+ {
+ "vendor": "Designers Guild",
+ "sampleable": 839,
+ "converted": 0,
+ "leak": 839
+ },
+ {
+ "vendor": "Osborne & Little",
+ "sampleable": 931,
+ "converted": 111,
+ "leak": 820
+ },
+ {
+ "vendor": "Maya Romanoff",
+ "sampleable": 624,
+ "converted": 0,
+ "leak": 624
+ },
+ {
+ "vendor": "Newmor Wallcoverings",
+ "sampleable": 512,
+ "converted": 0,
+ "leak": 512
+ }
+ ],
+ "order_data": {
+ "mp_orders": {
+ "rows": 6,
+ "types": [
+ {
+ "type": "wallpaper",
+ "count": 6
+ }
+ ],
+ "note": "Marketplace seed data — 6 identical $496 wallpaper rows, one date, no sample-type rows. Not real customer history."
+ },
+ "digital_sample_orders": {
+ "rows": 25,
+ "span_start": "2026-01-17",
+ "span_end": "2026-02-15",
+ "note": "Real but thin — DIGITAL sample orders only (not the $4.25 physical memo program); too few to compute a behavioral conversion rate."
+ },
+ "shopify_orders_present": false,
+ "note": "dw_unified is a CATALOG mirror: no shopify_orders table. Real memo→roll order conversion is not measurable here — instrumenting it is the recommendation, not the finding."
+ }
+ },
"monthly_activation": [
{
"month": "Jan",
diff --git a/public/portal.html b/public/portal.html
index 6c3f0f9..006e2ec 100644
--- a/public/portal.html
+++ b/public/portal.html
@@ -116,7 +116,7 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
@media(max-width:640px){.metrics{gap:26px}}
</style></head><body>
<header class="bar">
- <div class="stamp"><span>Data collected Jul 26, 2026, 8:55 AM</span><button id="refbtn" title="Re-run the read-only collector">↻ Refresh data</button></div>
+ <div class="stamp"><span>Data collected Jul 26, 2026, 9:01 AM</span><button id="refbtn" title="Re-run the read-only collector">↻ Refresh data</button></div>
<button class="burger" onclick="document.body.classList.toggle('nav')" aria-label="Menu" aria-expanded="false" aria-controls="portalmenu"><span></span><span></span><span></span></button>
</header>
<div class="scrim" onclick="document.body.classList.remove('nav')"></div>
@@ -144,7 +144,7 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
<div class="accbar"><button id="xall">Expand all</button><button id="call">Collapse all</button></div>
<section id="swot"><div class="eyebrow">Strategic Snapshot · SWOT</div><h2 class="disp">The whole board on one screen.</h2><div class="accbody">
- <p class="lead2">Four lenses over the live snapshot — internal <b>Strengths</b> and <b>Weaknesses</b>, external <b>Opportunities</b> and <b>Threats</b> — so the strategy that follows starts from one honest picture. Every number here is from the Jul 26, 2026, 8:55 AM collection.</p>
+ <p class="lead2">Four lenses over the live snapshot — internal <b>Strengths</b> and <b>Weaknesses</b>, external <b>Opportunities</b> and <b>Threats</b> — so the strategy that follows starts from one honest picture. Every number here is from the Jul 26, 2026, 9:01 AM collection.</p>
<div class="swotgrid"><div class="swotq pos"><h3>Strengths</h3><ul><li>80,542 live SKUs across 174 vendor lines — one of the deepest designer-wallcovering catalogs online</li><li>A 281-site microsite fleet feeding discovery back to the flagship</li><li>7/7 social channels connected through one marketing command center</li><li>Private-label lines with owned margin (Phillipe Romano, Hollywood Wallcoverings, Malibu Wallpaper)</li><li>30+ years of trade relationships; memo-sample program ($4.25) as a proven funnel entry</li><li>Automated activation drip already shipping ~500 products/day, textures-first</li></ul></div><div class="swotq neg"><h3>Weaknesses</h3><ul><li>22,952 active products are sample-only WITHOUT a quote-only designation — browseable but not buyable</li><li>1,444 actives carry zero/null pricing; 333 lack a featured image</li><li>Social reach is broad (80 FB pages, 34 IG accounts) but thin — distribution without engagement depth</li><li>No social→revenue attribution discipline (UTM/coupon per channel) yet</li><li>Flagship UX carries catalog weight — 80k SKUs need stronger guided curation for retail buyers</li></ul></div><div class="swotq pos"><h3>Opportunities</h3><ul><li>Convert the 22,952 sample-only backlog into sellable variants — the single largest dormant-revenue pool in the business</li><li>16,035 staged drafts (2,778 fully stageable today) = months of "New Arrivals" marketing fuel</li><li>AEO/AI-answer citations for wallcovering queries — the flagship already ships an AEO block; extend it fleet-wide</li><li>Sample-order → full-roll conversion funnel instrumentation (order sync to the back office already exists)</li><li>Licensing original design IP (settlement-gated) into marketplaces as a zero-inventory revenue line</li></ul></div><div class="swotq neg"><h3>Threats</h3><ul><li>DTC-native entrants own the "approachable wallpaper" narrative with modern brand voices</li><li>Marketplace aggregators (Amazon, Wayfair, Etsy) commoditize mid-tier patterns</li><li>AI answer engines could disintermediate catalog-depth advantages if citations go to competitors</li><li>Vendor MAP / brand-control tightening limits pricing latitude on marquee lines</li></ul></div></div>
<div class="flagbox" style="margin-top:34px"><div class="i">⚡</div><div><h3 class="disp">The one-sentence thesis</h3><p>DW's moat is <b>catalog depth + trade authority</b>; its constraint is <b>conversion infrastructure</b>. The fastest wins activate what already exists — the sample-only backlog, the staged-draft drip, the connected-but-quiet channels — before building anything new.</p></div></div>
</div></section>
@@ -268,17 +268,30 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
<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>
- <div class="s"><div class="p">Pool 2 · compounding</div><h4>Sample → roll funnel</h4><p>The $4.25 memo program generates purchase-intent signals daily. Instrumented follow-up (sampled-but-didn't-buy sequences) converts existing intent at near-zero cost.</p></div>
+ <div class="s"><div class="p">Pool 2 · compounding</div><h4>Sample → roll funnel</h4><p>The $4.25 memo program is the top of the funnel. <b>78,244</b> active products can be memo-sampled, but only <b>66%</b> have a roll a customer can then buy — leaving <b>22,790</b> recoverable dead-ends. Quantified below.</p></div>
<div class="s"><div class="p">Pool 3 · scheduled</div><h4>The staged-draft drip</h4><p>16,035 drafts activating ~500/day = a permanent "new arrivals" marketing engine. Sync email + social to the drip and every day ships its own campaign.</p></div>
<div class="s"><div class="p">Pool 4 · zero-inventory</div><h4>Original design licensing</h4><p>Settlement-gated original pattern IP licensed to print-on-demand marketplaces — revenue without inventory, already piloted in the wider operation.</p></div>
<div class="s"><div class="p">Pool 5 · trade</div><h4>Designer-net portal</h4><p>Formalize trade pricing behind a designer login. Trade buyers are the highest-LTV segment and currently buy through the retail flow.</p></div>
<div class="s"><div class="p">Pool 6 · projects</div><h4>Hospitality / contract desk</h4><p>Type II contract lines already in catalog. A spec-sheet + quote desk turns one hotel corridor into 400 rolls.</p></div>
</div>
+
+ <h3 class="disp" style="margin-top:38px">Pool 2, quantified — the sample → roll funnel</h3>
+ <p class="lead2">The $4.25 memo sample is a daily purchase-intent signal. The question is whether that intent has anywhere to convert. Measured from the live catalog mirror:</p>
+ <div class="kpis">
+ <div class="kpi"><div class="kv">78,244</div><div class="kl">Products a customer can memo-sample</div></div>
+ <div class="kpi"><div class="kv">66%</div><div class="kl">…that also have a buyable roll (51,664)</div></div>
+ <div class="kpi"><div class="kv">22,790</div><div class="kl">Recoverable dead-ends (sampled intent, no roll)</div></div>
+ <div class="kpi"><div class="kv">3,790</div><div class="kl">Quote-only by design (excluded — not a leak)</div></div>
+ </div>
+ <p class="lead2">Two-thirds of the sampleable catalog converts cleanly; the remaining <b>26,580</b> products a customer can pay $4.25 to sample but cannot then buy. Of those, <b>3,790</b> are intentionally quote-only, leaving a <b>22,790</b>-product recoverable pool — concentrated in a handful of lines. Fixing the roll variant on a line a customer already sampled is data work, not ad spend.</p>
+ <div class="chartwrap"><div class="ct">Sample → roll by line (top 8 by recoverable backlog): gold = memo converts to a buyable roll · rose = memo hits a dead end</div><canvas id="srChart"></canvas></div>
+ <div class="flagbox"><div class="i">◆</div><div><h3 class="disp">Provenance — tiered honestly</h3><p><b>Tier A (measured, catalog structure):</b> the funnel counts above come straight from the read-only <code>dw_unified</code> mirror — every ACTIVE product's memo-sample and roll variant flags. Solid.<br><b>Tier B (proxy, labeled):</b> "recoverable backlog" is <i>sampleable-but-not-buyable minus the 'quotes'-tagged lines</i>. That tag under-captures reality — contract lines like Koroseal and Phillip Jeffries are quote-only in practice but untagged — so <b>22,790 is an upper bound</b>; the truly recoverable share is a subset, weighted toward the merchant private-label and residential lines.<br><b>Tier C (absent — the recommendation):</b> real memo-order-vs-roll-order <i>behavioral</i> conversion is <b>not in this mirror</b>. It is a catalog mirror — no <code>shopify_orders</code> table; <code>mp_orders</code> is 6 seed rows and <code>digital_sample_orders</code> only 25 thin digital-only rows. So the true conversion <i>rate</i> can't be computed here — <b>instrumenting the memo→roll path is itself the first move</b> (Roadmap, Weeks 3–6).</p></div></div>
+
<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">
- <p class="lead2">Live KPIs from the Jul 26, 2026, 8:55 AM snapshot — the numbers a weekly growth review should open with. Hit ↻ Refresh (top right) to re-collect on demand.</p>
+ <p class="lead2">Live KPIs from the Jul 26, 2026, 9:01 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>
<div class="kpi"><div class="kv">16,035</div><div class="kl">Staged drafts</div></div>
@@ -302,13 +315,13 @@ footer{padding:56px clamp(24px,7vw,120px);border-top:1px solid var(--line);color
</div></section>
<section id="sources"><div class="eyebrow">13 · Sources & Methodology</div><h2 class="disp">Where every number comes from.</h2><div class="accbody">
- <p class="lead2">Snapshot collected <b>Jul 26, 2026, 8:55 AM</b> by a read-only collector. Live rows are measured; analyst rows are authored judgment; anything illustrative says so.</p>
+ <p class="lead2">Snapshot collected <b>Jul 26, 2026, 9:01 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, 8:55 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:01 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>
<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>
-<footer><span>Designer Wallcoverings · Growth Strategy Brief</span><span>Data as of Jul 26, 2026, 8:55 AM</span><span>Confidential — for discussion</span></footer>
+<footer><span>Designer Wallcoverings · Growth Strategy Brief</span><span>Data as of Jul 26, 2026, 9:01 AM</span><span>Confidential — for discussion</span></footer>
<script>
(function(){var s=document.querySelectorAll('.hero .slide'),i=0;if(s.length<2)return;setInterval(function(){s[i].classList.remove('on');i=(i+1)%s.length;s[i].classList.add('on');},5200);})();
@@ -335,7 +348,7 @@ function initCharts(){if(charted||!window.Chart)return;charted=true;
var RM=window.matchMedia&&window.matchMedia('(prefers-reduced-motion:reduce)').matches;
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]},"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 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"]}};
function mk(id,cfg){var el=document.getElementById(id);if(el)new Chart(el,cfg);}
// 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;};}
@@ -347,6 +360,7 @@ function initCharts(){if(charted||!window.Chart)return;charted=true;
mk('typeChart',{type:'doughnut',data:{labels:D.types.labels,datasets:[{data:D.types.counts,backgroundColor:['#c9a24b','#b78d3f','#a67c32','#d9b877','#8f6d2c','#e3c98d'],borderColor:'#0e0c09',borderWidth:2}]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'right',labels:{color:dim,boxWidth:12}}},cutout:'58%'}});
mk('ffChart',{type:'bar',data:{labels:D.integrity.labels,datasets:[{label:'Actives failing',data:D.integrity.counts,backgroundColor:D.integrity.counts.map(function(v){return v>10000?'rgba(212,100,122,.9)':'rgba(201,162,75,.6)';})}]},options:{indexAxis:'y',responsive:true,maintainAspectRatio:false,scales:{x:{grid:{color:line}},y:{grid:{display:false}}},plugins:{legend:{display:false}}}});
mk('soChart',{type:'bar',data:{labels:D.sampleOnlyVendors.labels,datasets:[{label:'Sample-only actives',data:D.sampleOnlyVendors.counts,backgroundColor:'rgba(212,100,122,.8)'}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{grid:{color:line}},x:{grid:{display:false}}},plugins:{legend:{display:false}}}});
+ mk('srChart',{type:'bar',data:{labels:D.sampleRoll.labels,datasets:[{label:'Roll available (sample converts)',data:D.sampleRoll.converted,backgroundColor:'rgba(201,162,75,.8)',borderRadius:3},{label:'Recoverable backlog (memo, no roll)',data:D.sampleRoll.leak,backgroundColor:'rgba(212,100,122,.85)',borderRadius:3}]},options:{indexAxis:'y',responsive:true,maintainAspectRatio:false,scales:{x:{stacked:true,grid:{color:line}},y:{stacked:true,grid:{display:false}}},plugins:{legend:{labels:{color:dim}}}}});
mk('compChart',{type:'bar',data:{labels:D.comp.labels,datasets:[{label:'Catalog depth',data:D.comp.catalog,backgroundColor:'rgba(201,162,75,.75)'},{label:'Digital polish',data:D.comp.digital,backgroundColor:'rgba(120,170,255,.6)'}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{grid:{color:line},max:100},x:{grid:{display:false}}},plugins:{legend:{labels:{color:dim}}}}});
mk('chanChart',{type:'bar',data:{labels:D.channels.labels,datasets:[{label:'Connected accounts',data:D.channels.accounts,backgroundColor:'rgba(201,162,75,.75)'}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{grid:{color:line}},x:{grid:{display:false}}},plugins:{legend:{display:false}}}});
mk('cadenceChart',{type:'line',data:{labels:D.cadence.labels,datasets:[{label:'New actives / week',data:D.cadence.added,borderColor:gold,backgroundColor:goldFill(.30),fill:true,tension:.4,pointRadius:5,pointBackgroundColor:gold,pointBorderWidth:0}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{grid:{color:line}},x:{grid:{display:false}}},plugins:{legend:{display:false}}}});
diff --git a/scripts/collect-dw.mjs b/scripts/collect-dw.mjs
index 62d7e7b..d8b4c0d 100644
--- a/scripts/collect-dw.mjs
+++ b/scripts/collect-dw.mjs
@@ -64,6 +64,53 @@ const sampleOnly = { quote_only_by_design: +so[0] || 0, total: +so[1] || 0, back
const sampleOnlyVendors = sql(`select vendor, count(*) from shopify_products where status='ACTIVE' and not coalesce(has_product_variant,false) group by 1 order by 2 desc limit 8`)
.map(([v, c]) => ({ vendor: v, count: +c }));
+/* --------------------------------------------- sample → roll funnel (Pool 2) ---
+ * The $4.25 memo program is the top-of-funnel purchase-intent signal. Real
+ * memo-order-vs-roll-order data is NOT in this mirror (it's a CATALOG mirror —
+ * no shopify_orders table; mp_orders is 6 seed rows, digital_sample_orders 25
+ * thin digital-only rows — see order_data below). So we quantify the STRUCTURAL
+ * proxy the funnel demands: how many products a customer can memo-sample vs how
+ * many of those they can then actually buy as a roll. The gap is split honestly
+ * into quote-only-by-design (tagged 'quotes') and the recoverable backlog. */
+const srf = sql(`select
+ count(*) filter (where s) as sampleable,
+ count(*) filter (where s and b) as converted,
+ count(*) filter (where s and not b) as gap,
+ count(*) filter (where s and not b and q) as gap_quote_only,
+ count(*) filter (where s and not b and not q) as gap_backlog
+ from (select coalesce(has_sample_variant,false) s, coalesce(has_product_variant,false) b, tags ilike '%quotes%' q
+ from shopify_products where status='ACTIVE') x`)[0] || [];
+const sampleRollFunnel = {
+ sampleable: +srf[0] || 0, converted: +srf[1] || 0, gap: +srf[2] || 0,
+ gap_quote_only: +srf[3] || 0, gap_backlog: +srf[4] || 0,
+ conversion_pct: (+srf[0]) ? Math.round(1000 * (+srf[1]) / (+srf[0])) / 10 : 0,
+};
+// Per-vendor recoverable leak: sampleable, not buyable, NOT quote-tagged — the
+// lines where an existing memo customer hits a dead end. Forbidden upstream
+// names filtered at the data layer (customer-facing private labels pass through).
+const sampleRollVendors = sql(`select vendor,
+ count(*) filter (where coalesce(has_sample_variant,false)) sampleable,
+ count(*) filter (where coalesce(has_sample_variant,false) and coalesce(has_product_variant,false)) converted,
+ count(*) filter (where coalesce(has_sample_variant,false) and not coalesce(has_product_variant,false) and tags not ilike '%quotes%') leak
+ from shopify_products where status='ACTIVE' and vendor is not null
+ group by vendor
+ having count(*) filter (where coalesce(has_sample_variant,false) and not coalesce(has_product_variant,false) and tags not ilike '%quotes%') > 0
+ order by leak desc limit 20`)
+ .map(([vendor, sampleable, converted, leak]) => ({ vendor, sampleable: +sampleable, converted: +converted, leak: +leak }))
+ .filter(v => !FORBIDDEN_NAMES.test(v.vendor)).slice(0, 10);
+// Order-data provenance — proves WHY we use the proxy, not order history. These
+// are the only order-shaped tables in the mirror and both are seed/thin.
+const mpOrderTypes = sql(`select order_type, count(*) from mp_orders group by 1 order by 2 desc`).map(([t, c]) => ({ type: t, count: +c }));
+const dso = sql(`select count(*), min(detected_at)::date, max(detected_at)::date from digital_sample_orders`)[0] || [];
+const sampleRollOrderData = {
+ mp_orders: { rows: mpOrderTypes.reduce((a, r) => a + r.count, 0), types: mpOrderTypes,
+ note: 'Marketplace seed data — 6 identical $496 wallpaper rows, one date, no sample-type rows. Not real customer history.' },
+ digital_sample_orders: { rows: +dso[0] || 0, span_start: dso[1] || null, span_end: dso[2] || null,
+ note: 'Real but thin — DIGITAL sample orders only (not the $4.25 physical memo program); too few to compute a behavioral conversion rate.' },
+ shopify_orders_present: false,
+ note: 'dw_unified is a CATALOG mirror: no shopify_orders table. Real memo→roll order conversion is not measurable here — instrumenting it is the recommendation, not the finding.',
+};
+
// Monthly activation ramp — 7 calendar months (the growth-story curve)
const monthly = sql(`select to_char(date_trunc('month',created_at_shopify),'Mon'), count(*)
from shopify_products where status='ACTIVE' and created_at_shopify > now() - interval '7 months'
@@ -253,6 +300,7 @@ const snapshot = {
price_bands_basis: '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',
drafts_total: drafts, drafts_stageable: +staged || 0,
sample_only: sampleOnly, sample_only_vendors: sampleOnlyVendors,
+ sample_roll: { funnel: sampleRollFunnel, vendors: sampleRollVendors, order_data: sampleRollOrderData },
monthly_activation: monthly, needs_tags: needsTags, style_mix: styleMix, color_mix: colorMix,
},
channels,
← bf9f344 preserve prior loop: GSC traffic-collector wired into snapsh
·
back to Consulting Designerwallcoverings Com
·
designer LOW follow-ups: 2x16px line-marker HTML legends on 27e394b →