The PipingRock Category Management Dashboard is a fully local, browser-based analytics tool that loads your retail sales data (Parquet or CSV) and lets you explore it instantly — no internet required after load, no data leaves your computer.
Built for category managers, brand managers, and retail buyers to answer: How is my brand growing vs the market? Which channels are outperforming? Which items lost distribution? What is my promo dependency?
The engine is DuckDB-WASM — an in-browser SQL database that queries millions of rows in under a second directly from your RAM. All charts use Chart.js and all computation runs in your browser tab.
Click ⟳ Load Data File in the header, or drag and drop onto the upload zone. Two formats supported:
| Format | Extension | Notes |
|---|---|---|
| Parquet | .parquet | Recommended — faster, smaller, preserves column types exactly. |
| CSV | .csv | Supported. DuckDB auto-detects column types with a full scan. |
Once loaded, the dashboard auto-selects LATEST 4 WKS as default period, pre-selects US xAOC, US AMAZON, US NATURAL, US VITAMIN SHOPPE as default channels, and sets Category = VITAMIN as the initial filter. All defaults can be changed or cleared at any time — use ✕ Clear to remove all filters and see the full dataset.
Click 📊 Insights Deck in the header (right of Load Data File) to open the standalone PR Insights Deck Dashboard. It runs in an isolated frame inside this page — its own DuckDB engine, its own export buttons (CSV + PPTX). The main dashboard's filters operate on the main view; the Insights Deck has channel and period locked to fit its 13/12 normalization model (see below).
What it shows: 13/12 Natural+VMS-normalized projections to annual, channel-mapped to B&M (xAOC), N+VMS (Vitamin Shoppe + Natural Expanded), and Online (Amazon). Subtype-level totals, type rollups, form2 splits, and special supplement pulls (Hydration, Electrolyte, Kids' Magnesium, Sleep, Sugar-Free Gummies, Women's Segment).
Subtype mapping note: Children's Multi totals contain only the CHILD MULTI subtype. Complete Multi is shown as a separate row containing only the COMPLETE MULTI subtype (adult full-daily multivitamins) — these are no longer combined. Similarly, Other Digestive Aid only includes the OTHER DIGESTIVE AID subtype; probiotics roll up under their own Probiotic row.
Two decks via dropdown: The header's 📊 Insights Deck ▾ button opens a dropdown with two choices. (1) Full Channel View — the original deck, locks channels to US xAOC + US AMAZON + US NATURAL + US VITAMIN SHOPPE (4 channels) and shows B&M / Natural+VMS / Online splits. (2) Natural + Vitamin Shoppe Only — N+VMS focus deck, locks channels to US NATURAL + US VITAMIN SHOPPE (2 channels) and shows Natural Expanded vs Vitamin Shoppe splits separately. The two decks are independent: each has its own iframe with its own DuckDB-WASM instance and aggregations. The Full deck pre-boots when a data file is loaded; the N+VMS deck boots lazily the first time you select it from the dropdown.
N+VMS deck — what's different: Same architecture as the Full deck (same period chips, same Slide 1 special pulls, same Movers slides, same period sync, same merge taxonomy) but limited to 2 channels and with added Brand-level analysis. When you expand any type or subtype row, after the Top Selling Form / Top Growing Form tables you now also get Top Selling Brand and Top Growing Brand tables (5 each), sorted by current $ and by $ Chg vs YA respectively. Brand sales are aggregated across both Natural and Vitamin Shoppe for the active period. Each row also carries $ Share (brand $ ÷ total subtype/total $) and Share of Growth (brand $ Chg ÷ positive growth pool across all brands in that subtype/total). The positive-pool denominator — same approach as the Top Form sentence — guarantees no single gainer ever exceeds 100% even when other brands are declining; brands with non-positive change show "—" instead. The PPT export adds a fourth italic line per entry: "Top Selling Brand: NOW Foods ($1.2M, 18.5% $ share). Top Growing Brand: Solgar (+$185K, 28.0% share of growth)." Mover slides and Special Pulls slides don't get the brand line (they have their own bullet formats).
N+VMS deck — SPINS-only date convention: Because the N+VMS deck contains only SPINS-sourced data (Natural Expanded + Vitamin Shoppe), every user-facing date label says only "SPINS Ending MM/DD/YY" — no Nielsen or Stackline mentions. The PPT footer, CSV filename, PPT filename, in-app toolbar banner, and date bar all use the SPINS Ending date (file date + 1 day). Filenames are Insights_NVMS_MM_DD_YY_L13W.csv and Natural+VS Insights Deck Full A-Z Types and Subtypes MM.DD.YY.pptx. The Full deck keeps showing both Nielsen and SPINS Ending dates since it covers all three data sources (Nielsen for xAOC, Stackline for Amazon, SPINS for Natural+VMS).
Distribution detail (expand any row): When you click any type or subtype card to expand it, you'll see the Top Selling Form and Top Growing Form tables, then a Distribution detail table breaking down % Wtd ACV and TDP. In the Full Channel deck the columns are B&M (xAOC) and Natural + VMS; in the N+VMS deck the columns are Natural Expanded and Vitamin Shoppe (split out separately). Six rows: % Wtd ACV TY · % Wtd ACV YA · ACV Δ vs YA (pp) · TDP TY · TDP Δ vs YA (absolute) · TDP % Δ vs YA. Below the table is an auto-generated insight describing the distribution dynamic.
% Wtd ACV uses TDP-weighted aggregation: Σ(%ACV Reach × TDP) / Σ(TDP). TDP % Δ vs YA = (TDP − TDP_YA) / TDP_YA × 100, where TDP_YA = Σ GREATEST(TDP − TDP Chg vs YA, 0) across items. Because TDP_YA floors negative items at zero, this percent reflects distribution expansion including new-item launches — exactly what's needed for category growth narratives. Edge cases: subtype with no items YA shows NEW; fully delisted shows −100%. Online (Amazon) is omitted — Amazon syndicated data does not include % ACV or TDP.
PPT export — distribution sentence: Each type/subtype slide entry in the PPT export now gets a compact distribution line (8pt italic gray, below the form sentence) reading something like "Distribution: B&M 78% ACV (+2pp), TDP 1.2M (+85K, +8%) · Natural+VMS 38% ACV (−1pp), TDP 245K (−2K, −3%)." Mover slides (Top Growers / Top Decliners / Divergent) already carry inline ACV in their bullet text, so the extra sentence isn't duplicated there; Special Pulls slides skip it because Hydration/Electrolyte/Kids' Mag/Sleep/Sugar-Free Gummies/Women's queries don't carry ACV data.
🚀 No second loading screen. The moment you upload a file in the main dashboard, the Insights Deck iframe pre-boots in parallel: it spins up its own DuckDB-WASM instance, reads the same file (pushed from main via cross-frame postMessage), and runs all aggregations silently in the background while you're using the main dashboard. By the time you click 📊 Insights Deck, the data is already prepared. If you upload a new file later, the iframe is auto-resynced — no manual re-upload needed.
🔒 Channel lock — mode-aware. While an Insights Deck page is active, the main dashboard's Compare Channels chips are locked to the channels that deck requires. Full Channel View locks to all four: US xAOC · US AMAZON · US NATURAL · US VITAMIN SHOPPE. Natural + Vitamin Shoppe Only locks to just US NATURAL · US VITAMIN SHOPPE. The locked chips render in green with a 🔒 prefix; all other chips dim to ~35% opacity. Clicks are disabled at both the CSS layer (pointer-events:none) and the JS handler. Your previous channel selection is saved on entry and restored exactly when you click any tab in the navigation strip. Switching between decks via the dropdown re-locks to the new set without overwriting your saved selection.
📅 Period sync. The Insights Deck's period chips (L13W / L26W / L52W) are kept in sync with the main dashboard's period selector. They become non-clickable inside the iframe and gain a green 🔒 lock indicator + "synced from main" label. The mapping:
| Main dashboard period | Insights Deck shows |
|---|---|
| LATEST 4 WKS | L13W (closest available — the 13/12 normalization can't go shorter) |
| LATEST 8 WKS | L13W (same — folds into the L13W bucket) |
| LATEST 13 WKS | L13W |
| LATEST 26 WKS | L26W |
| LATEST 52 WKS | L52W |
Same data file: uses the same Detailed MPG Scorecard parquet/CSV you've already loaded. Filename format MM.DD.YY Ending.parquet — date auto-parsed (Nielsen/Stackline = file date, SPINS = file date + 1 day). If you open Insights Deck before uploading any file, the iframe shows its own upload prompt as a fallback.
To return to the main dashboard: click any tab in the navigation strip (Overview, Brand, etc.). Channel lock is released and your previous selection is restored.
The period chips control which time window all metrics are computed for:
Selecting a period re-runs all queries across every page. The raw column is Periods — every row belongs to exactly one period. When you pick LATEST 13 WKS, the dashboard filters WHERE Periods = 'LATEST 13 WKS' automatically.
The Compare Channels panel selects which retail channels appear in charts. These come from the Markets column, normalized on load (e.g. TOTAL US XAOC → US xAOC).
Four channels are always used as benchmarks in insights regardless of your selection:
US xAOC (Extended All Outlet Combined) covers food, drug, mass, club, dollar, and military — the standard "total market" reference in category management.
Multi-channel mode — clicking a chip toggles it in/out of the selection (aggregated together) on these pages:
| Mode | Pages | Behavior |
|---|---|---|
| Multi-channel (toggle add/remove) | Overview · Manufacturer · Benefit · Segment · Type · Subtype · Form · Brand · Flavor · Age Group · Strength · Base Size · Item Level · Distribution · Promo · Analytics | Aggregates SUM across all selected channels. Click chips to add/remove; multiple can be on at once. Weighted formulas (velocity, ACV) use per-row channel scale so multi-channel aggregation is mathematically correct. |
| Single-channel (replace selection) | Velocity · Switching · SKU Rationalization | Only one channel active at a time — clicking a different chip replaces the selection. Necessary because these analyses are intrinsically per-retailer (switching patterns differ by account, SKU Rat recommendations are retailer-specific, velocity comparisons depend on retailer scale). |
When you switch from a multi-channel page (e.g. Brand with 5 channels selected) to a single-channel page (e.g. Velocity), the selection auto-narrows to one channel — US xAOC is preferred if it was selected, otherwise the first in your list. Switching back doesn't auto-expand; click additional chips to bring them back.
All filters apply simultaneously (AND logic):
| Filter Label | Raw Column | What it filters |
|---|---|---|
| Manufacturer | Manufacturer | Parent company (e.g. Nature's Bounty, Abbott) |
| Brand | Brand | Brand name (e.g. Nature Made, Garden of Life) |
| Category | Category | Top-level category (e.g. VITAMIN, WT CTL NUTR) |
| Segment | Segment | Sub-category grouping (e.g. ACTIVE NUTR, SPECIALTY SUPP) |
| Type | Type | Product type within segment (e.g. PROTEIN, OMEGA) |
| Form | Form2 | Physical form (e.g. CAPSULE, GUMMY, POWDER) |
| Benefit | Benefit | Health benefit claim (e.g. IMMUNE, MUSCLE, WEIGHT) |
The View toggle switches all charts between dollar and unit views simultaneously. A brand with strong dollar growth but flat unit growth signals a price increase driving revenue — not volume expansion. Toggling between the two immediately surfaces this.
Total $ Sales is the sum of all dollar sales for the selected filters and period. YoY Growth compares this period's sales to the same period one year ago.
The Avg Price per Unit is $ ÷ Units. If dollars grow faster than units, price is increasing. If units grow faster, the brand is discounting or down-sizing.
This dashboard uses two different ACV measures — it's important to know which is which:
| %ACV Reach (raw) | % Weighted ACV (computed) | |
|---|---|---|
| What it is | The raw column value from Nielsen/SPINS — % of store ACV carrying one specific item | A TDP-weighted average of %ACV Reach across all items in a brand/group |
| Where you see it | Item Level page — charts, table, sort dropdown, CSV export | KPI card, Brand/Segment/Type summary tables, Distribution charts, Velocity table, Overview, Analytics |
| Formula | None — it's a raw column: %ACV Reach | SUM(%ACV Reach × TDP) ÷ SUM(TDP) |
| Why they differ | A brand has many items, each with different %ACV and different TDP. A flagship at 38% ACV with TDP = 17.5 should matter far more than a niche SKU at 2% ACV with TDP = 0.4. The TDP-weighted average gives the flagship proportionally more influence. A simple average would treat both equally — massively understating the brand's true distribution. | |
| When TDP cancels | Only when all items have the exact same TDP — then the weighted average equals the simple average. In real data, TDP varies by 100x–20,000x across items, so the weighting always matters. | |
The average everyday (non-promoted) price per unit — what shoppers pay when no promotion is active.
Base = what a brand would sell with no promotions. Incremental = extra sales generated by promotions. They always sum to total sales.
Is this brand speeding up or slowing down right now? Compares the recent 13-week growth rate to the full-year 52-week growth rate.
TDP = Total Distribution Points (%ACV × number of items). An increase means more stores, more SKUs ranged, or both.
Annualized run rate based on the most recent quarter. Always uses LATEST 13 WKS data × channel-aware multiplier regardless of which period is currently selected: ×4 for Nielsen / Stackline channels (xAOC, Amazon, individual B&M retailers) and ×52/12 ≈ 4.333 for SPINS channels (US NATURAL, US VITAMIN SHOPPE, US FRESH THYME, US INFRA, US NATURAL GROCERS, US NCG, US SPROUTS, and all US VS regional slices — these report 12W of data tagged as L13W since SPINS reports in 4-week buckets). Respects all other active filters (channels, brands, segments, etc.).
Where it appears:
| Location | What it shows |
|---|---|
| KPI card #8 | Projected Annual $ with YoY%, Proj YA, Proj Change, Proj Units |
| Insights (all pages) | 📅 Projected Annual: $X (YoY%) vs Proj YA · Chg · Units |
| PPT Export | Included in KPI summary slide |
Why L13W? The most recent 13-week window is the best proxy for current trajectory. L4W is too volatile (promotional spikes distort it). L52W includes stale history. L13W balances recency with stability — it's the standard CPG planning horizon.
Click 📊 Insights Deck → Custom Retailer Compare button to open a modal that builds 3 distinct PPT deck types for any combination of retailers, periods, categories, and additional baseline channels. All decks use the same locked baselines (US xAOC + US AMAZON) and respect SPINS-aware projections.
Modal controls:
| Control | Behavior |
|---|---|
| 🔒 Locked baselines | US xAOC + US AMAZON — always included as comparison columns. Cannot be deselected. |
| + Add context channels | US FOOD · US DRUG · US CONV chips. When toggled ON, each appears as its own standalone per-channel card in every deck. They are NEVER summed with the retailer or with omni (they're slices of xAOC, so summing would double-count). |
| PERIOD | L4W · L13W · L26W · L52W. SPINS markets get the 12W/24W offset for L13W/L26W respectively. |
| CATEGORIES | Discovered from Category column. Pick any subset, or use "All" / "Just VITAMIN" presets. Click All to include every category; leave nothing checked to require manual selection. |
| Retailer list | Every market in the dataset EXCEPT the locked baselines, US FOOD/DRUG/CONV (those move to + Add context channels above), and TOTAL US lines. Multi-select with checkboxes. |
The 3 deck types:
| Deck | Granularity | Length | Best for |
|---|---|---|---|
| Executive Summary | Type AND subtype rollups mixed into rankings (each entry tagged TYPE TOTAL / SUBTYPE) | 12 slides | C-suite / leadership reviews. 4-quadrant overview, top growers / $ gainers / decliners, outperform/underperform slides, channel divergence, appendix. |
| Retailer-Focused A-Z | Type totals + subtypes in separate sections | ~30-100 slides | Retailer-specific reviews. Each row shows COMBINED (if 2+ retailers) + per-retailer card. xAOC + Amazon shown in compact baselines summary. |
| Omnichannel Compare A-Z | Type totals + subtypes in separate sections | ~50-100 slides | Total-Omni vs xAOC vs Online vs Retailer comparison. Includes Notable Insights bar slides (top growers / biggest $ / decliners) at the front. |
Multi-baseline outperform badges:
Every row + side panel in all 3 decks shows a multi-baseline comparison badge. Format example:
| Symbol | Color | Meaning |
|---|---|---|
| ▲ | Green | Retailer % chg beats baseline by ≥2pp |
| ▼ | Red | Retailer % chg trails baseline by ≥2pp |
| ◆ | Slate | Within ±2pp ("in line") |
SPINS-aware projections:
When a SPINS retailer is picked (US NATURAL, US VITAMIN SHOPPE, US FRESH THYME, US INFRA, US NATURAL GROCERS, US NCG, US SPROUTS, all US VS *), each market's projection uses the right multiplier per source. For a mixed pick (e.g., Meijer + US NATURAL), each market's $ is multiplied independently then summed — no single-multiplier under-projection.
Data attribution:
Title slide, footer (every slide), and appendix all carry the actual data-ending dates parsed from the loaded filename. Nielsen / Stackline channels use that date; SPINS channels use Nielsen +1 day (per SPINS convention).
Measures what percentage of total growth (or total decline) is attributable to each row. Growing and declining rows are measured against separate pools so growers sum to +100% and decliners sum to −100%.
Reading the column:
Respects all filters: The pools are computed from the current filtered universe. Filter to Segment=MINERAL and the pools only include mineral brands.
A diverging horizontal bar chart showing 20 bars — the top 10 growth drivers and the top 10 decline drags.
How it works:
Layout:
Where it appears: Overview page (by Brand) and every dimension page (by the page's dimension — Type, Segment, Form, Benefit, Flavor, etc.). Respects the $/Units toggle.
A compact health bar displayed above the Growth Contributors chart. Computed from ALL rows in the filtered universe — not just the 20 displayed bars.
What it shows:
| Element | Meaning | Scope |
|---|---|---|
| Growing count (green) | Number of groups with positive $ change | ALL rows |
| Health bar | Visual % of groups that are growing | ALL rows |
| Declining count (red) | Number of groups with negative $ change | ALL rows |
| Growth pool | Sum of ALL positive $ changes (the denominator for grower %) | ALL rows |
| Decline pool | Sum of ALL |negative $ changes| (the denominator for decliner %) | ALL rows |
The Key Insights panel auto-generates executive-ready text from the current filtered data. All pages use a two-column, column-first layout (fills top→bottom in left column, then right column).
Two modes based on channel selection:
| Mode | When | What it shows |
|---|---|---|
| Retailer Comparison | Non-benchmark channel selected (MEIJER, HY-VEE, etc.) — no benchmarks mixed in | Retailer summary + vs xAOC, vs AMAZON, vs NATURAL, vs VITAMIN SHOPPE with pp gap (above/below). Plus all standard insights. |
| Benchmark Overview | Only benchmark channels selected (xAOC, Amazon, Natural, Vitamin Shoppe) | Individual benchmark summaries with $ Sales, YoY%, momentum. Food/Drug/Conv excluded (they roll up into xAOC). Plus all standard insights. |
Standard insights (both modes):
| Icon | Insight |
|---|---|
| 📊 | Total Summary — $ Sales, Units, YoY% |
| 🚀/⚠️ | Momentum — accelerating or decelerating (L13 vs L52) |
| 📈/📉 | Top 3 growth drivers & top 3 decline drags (Share of Growth) |
| 🎯 | Growth breadth — how many brands drive 80% of growth |
| 🔄 | Biggest share movers — gainer & loser vs YA |
| 💰 | Price vs Volume — 5-scenario analysis (see below) |
| ⚖️ | Base/Promo shift — 4-threshold assessment (see below) |
| 🆕 | New item contribution — handles growth & decline (see below) |
| 🏪 | Net TDP change — distribution expanding or contracting |
| 🌐 | Channel divergence — widest gap between channels |
| 🏷️ | Private Label pulse — PL gaining or losing share |
| 📅 | Projected Annual — annualized $ and Units (L13W × Nielsen ×4 / SPINS ×52/12 — channel-aware), YoY% vs projected YA, and $ change. Always uses LATEST 13 WKS regardless of selected period. |
Dimension page insights (Brand, Category, Segment, etc.):
In addition to the standard insights above, every dimension page generates these velocity-based insights:
| Icon | Insight |
|---|---|
| 🚀 | Fastest velocity growth — Top 2 by $ / $MM ACV % change (sell-through rate). Shows YA velocity → current velocity, velocity YoY%, plus $ YoY% and Units YoY% in one line. Only includes brands with positive velocity in both periods. |
| 📉 | Velocity declining — Top 2 most negative velocity % change. Same format. Flags brands whose sell-through efficiency is falling — even if total $ is growing from distribution expansion. |
💰 Price vs Volume — 5-Scenario Logic:
Compares Avg Base Price % Chg vs Base Units % Chg (uses base metrics to exclude promo noise). A 0.5% dead zone prevents labeling noise as a trend.
| Price | Base Units | Label | Meaning |
|---|---|---|---|
| +5% | +2% | PRICE-DRIVEN | Price increases outpacing unit growth |
| +2% | +8% | VOLUME-DRIVEN | Unit growth outpacing price increases |
| −2% | +8% | VOLUME-DRIVEN | Unit growth overcoming price erosion |
| +0.4% | −6.4% | PRICE-SUSTAINED ⚠️ | Price propping up revenue while volume declines |
| −3% | −5% | BOTH DECLINING ⚠️ | Price and volume both contracting |
| ~0% | ~0% | STABLE | Minimal movement in either direction |
⚖️ Base vs Promo Shift — 4-Threshold Logic:
Compares Base $ Share (current vs YA). Higher base share = healthier; declining base = rising promo dependency.
| Base shift | Signal | Text |
|---|---|---|
| ≥ +1pp | ✅ Green | Base demand STRENGTHENING |
| ≥ 0pp | ✅ Green | Base demand stable |
| ≥ −2pp | ⚠️ Amber | Base softening — promo dependency rising |
| < −2pp | ⚠️ Amber | INCREASING promo dependency |
🆕 New Item Contribution — Growth vs Decline:
| Category trend | What it shows |
|---|---|
| Category growing | New item $ as % of net $ growth + pipeline strength (STRONG if >15%) |
| Category declining | "Category declining — new items offsetting losses" + strength based on new $ vs 5% of YA total |
How productively a brand sells per unit of distribution. This dashboard uses the pre-computed dollar-based $ / $MM ACV field, so this metric remains dollar-based even when the global view is switched to Units.
Shows current $ (or Units) vs Year Ago $ for each selected channel side by side. Solid bars = current period, lighter bars = same period last year. If solid is taller, the channel is growing.
Top 12 items by selected dimension (Brand, Category, Manufacturer, etc.) sorted by $ Sales. Each shows current sales (solid) vs YA (lighter). Use the dropdown to switch dimensions — refreshes instantly from cache.
Growth rates (not size) for up to 12 items, fastest to slowest. Green = growing, Red = declining. Only items with YA sales are included — new items excluded to avoid infinite growth rates.
Total $ sales divided among top 12 brands. Each slice is proportional to share of the filtered total. Most intuitive way to see category concentration at a glance.
Two bars per channel: L13 YoY% (recent) and L52 YoY% (full year). When L13 bar is higher than L52, the channel is accelerating. When lower, decelerating.
Each bar = total sales split into blue = Base $ (everyday) and green = Incr $ (promo-driven). Total height = total sales; the color split shows promotional dependency.
Every insight line is automatically generated from a specific computation — not AI written. Here's exactly how each line is formed:
| Insight Line | How it is computed |
|---|---|
| Total: $X (YoY%) | SUM($) and YoY formula against $ YA |
| Momentum Accelerating/Decelerating | Compares L13 YoY% to L52 YoY%. Diff > +2pp = Accel, < −2pp = Decel |
| Benchmark channel lines | Queried without channel filter so benchmarks always appear regardless of selection |
| Channel gap vs xAOC | Custom channel YoY% minus xAOC YoY% = gap in pp |
| Top category line | Category with highest SUM($) in current filter |
| Promo % | Any Promo $ ÷ $ × 100 |
| % Wtd ACV Expanding/Contracting | Weighted %ACV now vs weighted %ACV YA — positive = Expanding |
Purpose: The executive summary. KPI cards, channel growth, % Weighted ACV by channel, retailer rankings, top performers, share, momentum, and key insights all in one view.
Layout: Channel Growth (left) and % Weighted ACV by Channel (right) sit side by side. Key Insights with 14+ auto-generated nuggets appears below in a two-column layout. Retailer Rankings shows top 10 non-benchmark channels with a metric toggle ($ Sales, YoY%, % Wtd ACV, TDP, etc.).
Loading: A spinner with "Rendering page…" text appears at the top while data loads. Channel cards are centered and handle long names gracefully.
Purpose: Parent-company lens — every manufacturer in scope ranked by $ with their full brand portfolio one click away. Replaces the old Channel page (channel filter on the top bar still works).
Questions it answers: Who are the top manufacturers in this category and how concentrated is the market? Which manufacturer is gaining or losing shelf? When a competing manufacturer is growing/declining, which of their brands is driving it?
Layout: KPI strip (selected manufacturer's $/Units/YoY/Brand count/ACV/Promo/Share rank) → Insights (concentration, top performer, biggest decliner, ACV movers, selected manufacturer narrative) → Manufacturer Ranking table (top 30 of N, with the selected manufacturer highlighted) → Top 15 bar chart → Deep dive sections for the selected manufacturer: Brand Portfolio table, Growth/Decline brand split, Channel Mix, Top 25 SKUs, Brand × Period YoY% Heatmap.
How to switch manufacturer: Use the dropdown in the page header. Every deep-dive section below recomputes for the selected manufacturer. The dropdown is alphabetically sorted with $ values shown for context.
Purpose: Shows which health benefit claims are driving growth — Immune, Muscle, Omega, Weight, Energy, etc.
Purpose: Performance by top-level category — VITAMIN, WT CTL NUTR, ACTIVE NUTR. Answers: Which product categories are growing fastest? How is vitamin performing vs sports nutrition?
Purpose: Sub-category level — e.g. within ACTIVE NUTR: PROTEIN, PRE-WORKOUT, RECOVERY. Is specialty supplement taking share from mainstream vitamins?
Purpose: Two levels of type hierarchy below Segment. Type = PROTEIN → Subtype = WHEY PROTEIN or PLANT PROTEIN. Is whey losing share to plant? Is collagen growing? Invaluable for innovation pipeline decisions.
Purpose: Physical format — CAPSULE, SOFTGEL, GUMMY, POWDER, LIQUID, etc.
Purpose: Full brand-level deep dive — all charts and the complete summary table showing every brand's $ sales, YoY%, share, momentum, ACV, promo %, base vs incr, price, and TDP.
Insights include: Top 3 by $ sales, fastest velocity growth (brands whose $/MM ACV sell-through rate is rising fastest, with both $ and Unit YoY%), velocity declining (brands losing sell-through efficiency), growth drivers & decline drags (by share of growth), price vs volume, base/promo shift, breadth, ACV, and share movers.
Best use: Pre-line review preparation. Know exactly how your brand stacks up against every competitor on every metric before any buyer meeting.
Purpose: Performance by product flavor — TANGERINE, VANILLA, BERRY BLAST, etc. With ~900 distinct flavors, charts display top performers while the table provides the full scrollable list.
Purpose: Performance by target age demographic — ADULT, CHILD, SENIOR, BABY, TEEN, ALL FAMILY, NOT APPLICABLE. Only 7 values, making every chart instantly readable.
Questions it answers: Is the children's supplement segment growing faster than adult? Are senior-targeted products losing share? Which age group has the highest promo dependency?
Purpose: Performance by product potency — 1000 MILLIGRAM, 500 INTERNATIONAL UNIT, etc. Answers questions about consumer preference for dosage levels.
Purpose: Performance by physical product size — 60 COUNT, 120 COUNT, 2 FLUID OUNCE, etc. Identifies which pack sizes consumers prefer and which are over/under-distributed.
Purpose: SKU-level analysis — individual product performance by item name and UPC. Includes New Items (Gained Distribution) and Lost Items (Lost Distribution) at the top, followed by item charts and the full sortable/searchable item table.
New & Lost Items: New Items = $ sales this period but zero $ YA (newly ranged/launched). Lost Items = $ YA but zero $ this period (discontinued/de-listed). Both show KPI summaries (count + $ impact) and scrollable item tables with brand, sales, units, and %ACV Reach.
Features: Search by item name or UPC. Sort by Sales, $ Chg, $ % Chg, Units, %ACV Reach, Momentum, Promo %, Price, or TDP Chg. Items with no YA sales tagged NEW.
Purpose: Distribution health — % Weighted ACV by channel and brand, TDP change, dollar-based $ / $MM ACV velocity, 3-period ACV trend, and assortment productivity.
Purpose: Full promotional analysis — % $ on promo, % units on promo, Base vs Incr by brand (stacked), Base vs Incr share doughnuts, promo % by brand, and growth decomposition bridge.
Purpose: Dedicated sell-through analysis — $/MM ACV by dimension (current vs YA), velocity YoY% change, velocity vs $ growth scatter (is growth real or just distribution?), ACV vs velocity quadrant, full sortable velocity table, and per-item computation breakdown with CSV export.
Single channel: Only one channel active at a time (default xAOC). Velocity formulas compare against retailer-specific $/$MM ACV scales, so mixing channels would distort the comparison. The selector auto-narrows when you land here from a multi-channel page.
Group by + Show N: Group by any dimension including Item Description. Show N defaults to 15 for charts/table, bubble charts minimum 25, computation breakdown defaults to 100 (follows Show N if higher). Header bar with controls stays sticky when scrolling.
Color legends: Both scatter charts have colored-dot legends matching each quadrant. Computation Breakdown has a sticky teal header showing the final velocity result.
Unique feature: The Computation Breakdown shows all items with UPC, Item Description, Subtype, Form, Flavor, Strength, Base Size + the 4 raw velocity columns and 3 computed denominators. Export to CSV for verification or presentation. See the Velocity Deep Dive section for full documentation.
Purpose: Advanced analytical views with a unified Dimension-Driven Group and Price Elasticity.
1. Dimension-Driven Group (controlled by Group by + Show dropdowns) — Channel Index, Watchlist Alerts, Cross-Period Heatmap, Fair Share / Gap Analysis, Retailer Scorecard, Assortment Productivity, ACV vs Velocity Quadrant, HHI Concentration, and $ Share. Changing the "Group by" dropdown re-renders all cards including Channel Index for the selected dimension.
2. Price Elasticity — independent, shows all brands with meaningful price changes.
See Section 5 for full documentation of each chart.
Every ranking table now includes two benchmark YoY% columns right after the existing YoY% / L13 YoY% area: $ YoY% xAOC and $ YoY% Food (separately, not combined). They show that row's YoY% growth in the broader channel benchmark — so you can immediately read whether your filtered scope is outperforming or underperforming the channel.
View-mode behavior: Manufacturer Ranking and Brand Portfolio tables (Manufacturer page) follow the $/Units toggle — header reads "$ YoY% xAOC" in $ mode and "Units YoY% xAOC" in Units mode, with cells matching. Dim ranking tables and Velocity Summary always show $-based benchmarks regardless of view mode (CPG benchmark convention).
Example: You're filtered to Walmart · Magnesium · Capsules. The Brand row "Nature's Bounty" shows +12% in your scope, +8% in xAOC, +5% in Food. Reading: Nature's Bounty Magnesium Capsules at Walmart is outperforming the broader xAOC channel by 4pp and Food by 7pp. The brand is performing better at Walmart than across the wider market.
Where the columns appear:
Where they're skipped (and why):
What the benchmark scope respects: All your active dim filters EXCEPT channel are preserved. So if you filter to Magnesium · Capsules · Senior, the xAOC benchmark column shows xAOC's $ YoY for Magnesium Capsules in the Senior age group — apples-to-apples with what you're seeing in your selected channel. Channel filter is overridden to xAOC or Food specifically for the benchmark column only.
When you'll see "—": The benchmark cell shows a dash when that row has no presence in the benchmark channel. Common cases: a brand that's exclusive to Vitamin Shoppe will show "—" in the Food column. A brand that hasn't launched in xAOC yet will show "—" in the xAOC column.
Color coding: Same as the main YoY% columns — green for positive, red for negative.
O / U badges: Each benchmark cell now includes a tiny pill next to the percentage — O when your scope is outperforming the channel (your YoY > benchmark YoY), U when underperforming (your YoY < benchmark YoY). Hover for the exact comparison. No badge appears when the row's main YoY% is null (no comparison possible) or the two values are exactly equal.
Reading the comparison: Eyeball the three columns side-by-side — your YoY% vs xAOC vs Food. The O/U badges save you that mental math.
CSV exports include both columns ($ YoY% xAOC, $ YoY% Food) plus their Units equivalents (Units YoY% xAOC, Units YoY% Food). When the row has no benchmark presence, the cell exports as empty string.
Performance: Two extra queries fire in parallel with the table's main query (one per benchmark channel), grouped by the page's dim. Cached per filter signature so re-renders within the same scope are free.
Bar charts that promise YoY% in their title now actually show it inline, color-coded by direction. The label format is $14.3M +12.5% with the entire label rendered in green for positive YoY or red for negative YoY.
Where it currently appears: Manufacturer page → "Top 15 Manufacturers — $ with YoY%" bar chart. View-mode-aware: shows $ YoY in $ mode, Units YoY in Units mode.
How it works under the hood: Charts opt in by passing _yoyData as a custom property on the dataset. The shared mkC datalabels formatter then appends the YoY% string and colors the label by sign. This generic hook can be reused on other bar charts whenever a YoY context is useful — adding it later is a one-line dataset property change.
What stays unchanged: Bar fill color (still highlights the selected manufacturer in deep navy, others in lighter navy). The YoY color is only on the bar-end label text.
Every Cross-Period YoY% Heatmap now includes a Rank column right after the dim label. Rank = position by $ in the current filter scope (#1 = the largest by dollars).
Where it appears:
Why it's useful: Instant context. When you see Brand X at +18% YoY in L13W, you can also see at a glance whether X is the #2 brand in the category (worth a celebration) or the #87 brand (interesting but small). Same for declines — a #5-ranked brand at −10% YoY matters more than a #50 brand at −10%.
Special case — Manufacturer page heatmap: Rank shown is GLOBAL across all brands in scope (not within the manufacturer). So if the heatmap shows 5 brands of PIPING ROCK HEALTH PRODUCTS and they rank #2, #14, #28, #44, #91 — that immediately tells you which Piping Rock brands are category-weight winners vs niche players, even though all 5 are #1-#5 within Piping Rock's own portfolio.
The dedicated Channel page is gone — replaced by a deep Manufacturer page in the same nav slot (right after Overview). The Channel filter on the top bar still works, so channel-level filtering is fully preserved across every page.
Why the swap: Channel-level analysis was already accessible via the Channel filter + Distribution / Promo / Velocity pages. What was missing: a parent-company lens. The Manufacturer page answers "who owns what, who's gaining shelf, who's losing it, and which of their brands is driving the trend?" — questions that come up constantly in line reviews and competitive briefings.
What's on the page (top to bottom):
Filter scope: respects every top-bar filter (Channel, Category, Period, Type, Subtype, Form, Age Group, Strength, Base Size, Flavor, Manufacturer, etc.), same as Switching. Slice mode is implicit — filter by Type=Magnesium and the page becomes "manufacturer rankings within Magnesium."
Visual treatment: standard navy header (NOT specialized) — this is a category-management workhorse page, not an analytical workflow like Switching or SKU Rat. No special tab gradient.
Every table on SKU Rat now carries a %ACV Δ pp column showing year-over-year change in distribution (in percentage points, the conventional measure for ACV movement). The brand-level KPI strip shows brand %ACV alongside its Δ pp value, and a new auto-generated Distribution Diagnosis insight at the top of the page interprets it.
Why this matters: A declining SKU looks the same on paper whether it's losing shelf or losing shoppers — but the right action is completely different. Without %ACV Δ, you can't separate the two. Now you can see at a glance:
Where it appears: Cut Candidates · Expand Candidates · Inactive SKUs Audit · In-scope Replacement watchlist + table · xAOC Channel-Expansion Replacement watchlist + table · Cannibalization CSV. The KPI strip's "% Wtd ACV" card now shows current %ACV with "+X.Xpp vs YA" and the YA value below.
Strongest swap pitch (Replacement): when the competitor SKU's Δ is negative AND your SKU's Δ is positive. Their SKU is losing shelf; yours is gaining. That's the easiest delisting case a buyer can make to their team.
See SKU Rat help → Section 3 (Distribution Diagnosis) for the full 4-pattern diagnostic table, formula, and reading guide.
The target brand selectors on Switching Analysis and SKU Rationalization are fully synced in real time. Selecting a brand on either page automatically updates the other — both the dropdown DOM and the underlying analysis — so the two pages always stay focused on the same brand.
How it works:
What if the linked brand isn't in the destination page's filter scope? Both pages have a defensive fallback: if the carried-over brand has no presence in the page's current scope, the page automatically falls back to the top brand in scope and the dropdown reflects the new selection. You won't see a broken or empty page.
Why this matters: Switching and SKU Rat answer complementary questions about the same brand — "what happens if we lose them?" vs. "which of their SKUs should we cut/keep/build?" Keeping the target in sync removes the friction of re-selecting the brand every time you toggle between pages.
Three pages in the dashboard are specialized analytical workflows rather than standard dimension breakdowns. They now have distinct gradient headers and a ◆ Specialized badge in the page title so you can see at a glance which kind of page you're on.
| Page | Theme | Why specialized |
|---|---|---|
| 🔄 Switching Analysis | Teal/cyan gradient — "flow / migration" | Models brand-removal scenarios with multi-dimensional substitute scoring. Respects every top-bar filter (slice mode). |
| 🔬 Advanced Analytics | Violet/purple gradient — "research / advanced" | Hosts higher-order analyses: Channel Index, Fair Share, ACV Quadrant, Price Elasticity, HHI. Different cards have different filter scopes — see the page itself for specifics. |
| 📋 SKU Rationalization | Amber/copper gradient — "audit / portfolio" | Whole-brand portfolio cleanup: Pareto, cannibalization, assortment depth, whitespace, replacement, inactive audit. Intentionally ignores Type/Form/etc. filters so portfolio diagnostics always reflect the complete SKU footprint. The orange info strip below the header reminds you of this scope. |
Standard dimension pages (Channel, Type, Form, Age Group, Distribution, Promo, Velocity, Item Level, etc.) keep the navy default header — they're "data view" pages that respect every filter and don't need special visual treatment.
The Switching page now respects every top-bar filter (Type, Subtype, Form, Age Group, Strength, Base Size, Flavor, Manufacturer, Segment, Benefit), just like SKU Rat does. Previously it only honored Channel, Category, and Period — making "if I lose Brand X's Magnesium specifically" impossible to ask without a different page.
What changes when filters are active (slice mode):
Filter → pinned dim mapping: Segment filter → pins Segment · Form filter → pins Form · Age Group filter → pins Age Group. Other filters narrow the slice but don't pin a Score dimension (their dims aren't in the formula). When all 3 score dims are pinned simultaneously, the formula reduces to Price × Distribution × Momentum — the right outcome since dimensional fit is fully constrained by the filter.
See Switching help → section 2 for the full slice-mode walkthrough and section 4 for the filter-aware score formula details.
SKU Rat now classifies every SKU in the brand portfolio as ACTIVE, INACTIVE, or NEW using a four-gate ruleset. Active SKUs power all the analytics on the page; inactive SKUs are surfaced separately for transparency and audit.
The four gates:
What this affects:
See SKU Rat help → section 2 for the complete ruleset, threshold rationale, and where each classification appears across the page.
An item declining ≤ −60% YoY (with a YA baseline) is treated as likely discontinued. Possible reasons: supplier exit, recall, retailer delisting in progress, packaging change with a new UPC. Whatever the cause, the item is on its way out — it shouldn't be proposed as a candidate for cuts (already being cut), expansion (no point pushing distribution), or replacement (don't recommend swapping in something the competitor is already pulling).
Where the ≤−60% rule applies (consistent across pages):
Where the rule does NOT apply (raw data & aggregates):
Edge cases handled:
All filter dropdowns now cascade dynamically. When you select a value in one filter, all other filters instantly update to show only values that exist within that selection.
Auto-deselect: If a previously selected value becomes invalid due to cascading (e.g. you had Brand = "NATURE MADE" then changed Manufacturer to one that doesn't own it), the invalid value is automatically removed.
The $ Dollars / 📦 Units toggle in the navigation bar controls all applicable charts across every page — Overview, Channel, Dimension pages, Item Level, Promo, Distribution, Switching, SKU Rationalization, and Analytics.
What toggles on Switching: KPI primary value (Units leads in units mode, $ Sales follows; reversed in dollars mode), Score Table sales column header + cells, Retailer/Channel exposure tables (Brand $ / Category $ become Brand Units / Category Units), Brand dropdown summary line at top of page.
What toggles on SKU Rationalization: Total/SKU and Bottom-25% KPIs, Zone $ summaries (CUT/EXPAND/PROTECT/FIX), Quadrant chart bubble size, Pareto chart Y-axis (cumulative share %), Cut Candidates table sales column, Expand Candidates table sales column, Cannibalization Cluster $ + Top SKU $ + Weakest SKU $, Assortment Gaps Category $ column, Whitespace and Whitespace × xAOC sections (where applicable).
What stays fixed (always dollar-based):
| Element | Reason |
|---|---|
| Switching score formula | Price gap and promo % require dollars by definition. Score multiplies these regardless of view mode. |
| Cut score formula | Velocity ($/$MM ACV) and ACV thresholds are intrinsically dollar-driven. Cut zone classification stays $-anchored. |
| Action zones (PROTECT/EXPAND/FIX/CUT) | Median splits use dollar-velocity and dollar-ACV. Zone boundaries don't move with the toggle. |
| Capture $ estimates & migration math | Buyer pitches are dollar conversations. Estimates always shown in dollars even when surrounding metrics show units. |
| Whitespace Opportunity $ | The opportunity at stake is a financial ask — keep in dollars to stay buyer-meeting-ready. |
| Growth Decomposition Bridge | The Volume / Price / Incremental decomposition is inherently dollar-based — Price Effect = (Price − Price YA) × Units. A "units bridge" would be a different analysis. |
| Price Elasticity | Elasticity = Base Units % Chg ÷ Price % Chg. It already uses base unit data — toggling has no meaning. |
| Avg Base Price | Price is price — there is no "units version" of price. |
| $ / $MM ACV velocity charts | The file contains a dollar-based velocity field but no raw units-per-$MM-ACV field, so these visuals remain dollar-based for accuracy. |
| % Weighted ACV charts | Distribution breadth is independent of $ or units. |
Why split the toggle this way: view-mode swaps the lens you LOOK through (sales sums, share calcs, table values) but never the math behind buyer recommendations. The Cut Score, Action Zone, Switching Score, and dollar-based capture estimates are anchored to financial fundamentals — they shouldn't drift just because you changed the display.
CSV exports include BOTH $ Sales AND Units columns regardless of active view mode (so analysts can re-pivot offline). The visible table shows whichever you have selected.
Four new dimension pages have been added, each with the full suite of charts ($ Sales, Units, Share, YoY%, Momentum, Base vs Incr, %ACV, Promo %) plus heatmaps and summary tables:
| Page | Column | Distinct Values | Description |
|---|---|---|---|
| 🍊 Flavor | Flavor | ~900 | Product flavor (e.g. TANGERINE, ACAI BERRY, VANILLA). High cardinality — charts show Top N, table scrolls. |
| 👥 Age Group | BC AGE GROUP TOTAL STORE | 7 | Target age demographic: ADULT, CHILD, SENIOR, BABY, TEEN, ALL FAMILY, NOT APPLICABLE. |
| 💪 Strength | STRENGTH | ~220 | Product potency (e.g. 1000 MILLIGRAM, 500 INTERNATIONAL UNIT). |
| 📏 Base Size | BASE SIZE | ~545 | Physical product size (e.g. 2 FLUID OUNCE, 120 COUNT). |
These pages appear in the tab bar between Brand and Item Level. Each is also available as a filter and as a "Group by" option in Analytics.
Tables and heatmaps export as styled XLSX (standard Excel format with pre-sized columns); charts export as CSV. The XLSX format preserves the visual formatting users see on screen — green/red color-coded YoY% cells, currency formatting, bold headers — so the file is presentation-ready without manual cleanup.
XLSX layout (every table/heatmap export):
| Row | Content |
|---|---|
| 1 | Title banner — Aptos Black 18pt white on navy. Text in column A only with wrapText so the title stays readable when scrolling horizontally with column A frozen. Banner fill extends across all columns for visual continuity. |
| 2 | Active filters and selected period — Calibri italic. Optional KPI prefix (e.g. "📈 50 new items adding $1.2M") for New/Lost Items and Velocity Breakdown exports. |
| 3 | Data Ending date |
| 4 | 💡 AutoFit tip (Ctrl+A → Alt+H+O+I) + contact: categoryteam@pipingrock.com |
| 5 | Spacer |
| 6 | Column headers — bold, dark navy bg, frozen pane. autoFilter dropdowns always-on for sort/filter without setup. |
| 7+ | Data rows with conditional cell formatting + auto-grown row heights for wrapped content |
Sheet view defaults: gridlines off, frozen pane at row 6 column A, autoFilter on row 6, row heights auto-fit to wrapped content.
Cell formatting rules:
$#,##0 currency format, right-aligned+$#,##0;-$#,##0;$0 with green/red sign coloring$#,##0.00 with 2 decimals+0.0%;-0.0%;0.0%, +12.5% green for positive, -12.5% red for negative, with light tinted cell background+0.0;-0.0;0.0 — NOT divided by 100#,##0 integer format. Units Chg / TDP Chg: +#,##0;-#,##0;0 with green/red@) so leading zeros are preserved and 12-digit codes never display as 1.23E+11Column widths: exports are standard .xlsx workbooks. Column widths are pre-computed using measured-pixel-width-per-character at the export font (Calibri 11pt), sized to fit the longest cell value plus a small padding buffer. Caps at 60 column units to prevent runaway widths from 130+ char item descriptions. For perfect AutoFit-from-true-font-metrics, click anywhere in the sheet, press Ctrl+A (select all), then Alt+H+O+I (Home → Format → AutoFit Column Width).
Row heights locked at 15pt (~20px): every data row is 15 points tall. Header row is 30pt for wrapped column titles. Metadata rows 1-4 (title/filters/data ending/contact) auto-size to fit wrapped content in column A.
Author metadata: Every XLSX file embeds Daven Roallos (creator + last-modified-by), croallos@pipingrock.com in the description, and PipingRock Health Products as company. Visible in Excel via File → Info → Properties. Same metadata on PowerPoint exports.
Charts (CSV export, unchanged): The Retailer Rankings chart on Overview and the per-chart fullscreen download button keep CSV format — charts are typically pasted into other tools or processed programmatically, where CSV simplicity wins over formatting.
Multi-sheet workbook export: The ⬇ XLSX button in the header combines every XLSX-capable export on the active page into a single multi-sheet workbook. CSV-only sections (raw data export, Retailer Rankings chart, per-chart fullscreen CSV) are skipped by design. Filename: PipingRock_Page_{pgId}_All_Exports_{Period}_{Date}.xlsx. Each sheet uses the same per-section formatting and the seven-row header banner. Sheet names dedupe automatically (Brand Table → Brand Table 2 if duplicated). Pre-computed column widths apply on every sheet at once.
Locations across the dashboard:
| Page | XLSX exports (tables/heatmaps) | CSV exports (charts) |
|---|---|---|
| Header | ⬇ XLSX (multi-sheet, active page only) | Full raw data export (all rows matching filters) |
| Overview | Summary Table, Cross-Period Heatmap, Retailer Scorecard | Retailer Rankings chart |
| Dimension pages | Dimension Table (Momentum + L13W/L52W YoY% included), Heatmap | — |
| Item Level | Item Table (XLSX, full 27 cols including Momentum), New Items (with KPI), Lost Items (with KPI), Item Heatmap | — |
| Promo | Promo Table | — |
| Distribution | Distribution Table | — |
| Velocity | Velocity Summary Table, Velocity Computation Breakdown (XLSX with aggregate KPI) | — |
| Manufacturer | Ranking, Brand Portfolio, Channel Mix, Top SKUs, Brand-Period Heatmap | — |
| Switching | Retailer Exposure, Switching Score, Product Matcher | — |
| SKU Rationalization | Whitespace, xAOC Listing Plays, Replacement (×2), Cut, Expand, Cannibalization, Depth, Gaps, Inactive Audit. T · Sub · F · Sz · Flv combine applied automatically. | — |
| Analytics | Watchlist, Heatmap, Scorecard | — |
What's in each export: $ Sales, $ YA, $ Chg, $ YoY%, $ L13W YoY%, $ L52W YoY%, $ Momentum, $ YoY% xAOC, $ YoY% Food, vs xAOC ($), vs Food ($), $ Share%, Units, Units YA, Units Chg, Units YoY%, Units L13W YoY%, Units L52W YoY%, Units Momentum, Units YoY% xAOC, Units YoY% Food, vs xAOC (Units), vs Food (Units), Unit Share%, Promo $, Promo $ YA, $ Promo%, Units Promo%, Base $, Incr $, Base Units, Incr Units, % Wtd ACV, % Wtd ACV YA, ACV Chg pp, Avg Base Price, Avg Base Price YA, TDP Chg, $ / $MM ACV, $ Shr of Growth%, U Shr of Growth%, SKU Count, Data Records — 36+ columns for the dim ranking tables.
Filenames: PipingRock_{TableName}_{Period}_{Date}.xlsx (single-section tables/heatmaps) · PipingRock_Page_{pgId}_All_Exports_{Period}_{Date}.xlsx (page → multi-sheet) · PipingRock_{ChartName}_{Period}_{Date}.csv (charts).
New Items (Gained Distribution) and Lost Items (Lost Distribution) are shown at the top of the Item Level page, before the main item charts and table, so assortment churn is visible immediately when you open the Item Level tab.
In the Compare Channels panel, benchmark channels (US xAOC, US AMAZON, US NATURAL, US VITAMIN SHOPPE, US FOOD, US DRUG) now display with a ★ star prefix and navy styling to visually distinguish them from individual retailer/geography channels. This makes it easy to identify the standard market reference channels at a glance.
The Channel Index vs Benchmarks card has been moved inside the Dimension-Driven Group on the Analytics page. It now responds to the "Group by" dropdown — when you switch from Brand to Category (or Flavor, Age Group, etc.), the Channel Index chart re-renders showing the top values for that dimension indexed against benchmark channels. The index cards above the chart still show your overall filtered selection's performance vs each benchmark.
Period selector moved to the rightmost position in the filter bar, after Clear/Apply. Periods display in a compact 2-column grid. All 11 filter dropdowns fit in a single line between the filters and the period selector.
Row count badge moved from the navigation bar to the brand logo strip, aligned to the right.
Six Vitamin Shoppe market variants are now normalized on load:
| Raw Market Name | Normalized |
|---|---|
| VITAMIN SHOPPE - TOTAL US W/O PL | US VITAMIN SHOPPE |
| VITAMIN SHOPPE - RETAIL W/O PL | VS RETAIL |
| VITAMIN SHOPPE - WEB W/O PL | VS WEB |
| VITAMIN SHOPPE - US CENSUS RGN - MIDWEST W/O PL | VS MIDWEST |
| VITAMIN SHOPPE - US CENSUS RGN - NORTHEAST W/O PL | VS NORTHEAST |
| VITAMIN SHOPPE - US CENSUS RGN - SOUTH W/O PL | VS SOUTH |
| VITAMIN SHOPPE - US CENSUS RGN - WEST W/O PL | VS WEST |
Tells you whether your filtered selection's growth factor is beating, matching, or lagging the market. It compares current vs year-ago ratios instead of dividing raw YoY% by raw YoY%, which avoids sign flips when a benchmark is flat or negative. Score of 100 = exactly matching the benchmark. Above 100 = outperforming. Below 100 = underperforming.
Four benchmarks:
Automatically scans all values for the selected "Group by" dimension and flags those breaching performance thresholds. Responds to the dimension dropdown — switch from Brand to Category and the watchlist rescans by category.
All values for the selected dimension. The watchlist scans every brand/category/segment/etc. — it does not limit to top N. A declining brand ranked #40 still needs flagging.
| Alert | Threshold | Meaning |
|---|---|---|
| 📉 Severe Decline | YoY% ≤ −10% | Freefall — immediate investigation |
| ⚠️ Soft Decline | YoY% −5% to −10% | Declining — monitor closely |
| 📡 Distribution Loss | %ACV drop ≥ 5pp | Losing shelf — at risk of de-listing |
| 🏷️ Promo Dependency | Promo% ≥ 60% | Over-reliant on promotions |
| 🚀 Growth Opportunity | YoY% ≥ +20% | Strong growth — expand distribution |
Note: Lost & Gained Items (New Items / Lost Items) have moved to the Item Level page where they appear first, before the item charts and table.
A color-coded table showing $ YoY% and Units YoY% across all 5 standard period windows (L4W, L8W, L13W, L26W, L52W) for each brand or dimension value. Green cells = growth, red = decline, darker = stronger signal. The final columns show $ and Units for the currently selected period.
Top N by $ Sales (controlled by the "Show" dropdown, default 10). A heatmap with 100+ rows would be unreadable. Each row is queried independently per period window.
Bubble scatter chart: X = %ACV Reach (how widely distributed), Y = $ Share % (what portion of total sales), Bubble size = relative $ Sales. Median crosshair lines divide brands into four quadrants.
Top 25 by $ Sales (minimum 25, even when Show is set lower). When Show=All, displays all values. A broader scope than other charts because the purpose of gap analysis is finding hidden gems and underperformers that a top-10 filter would miss. Scatter charts handle many points naturally — tiny brands get tiny bubbles.
Brand × Channel heatmap matrix. Each cell = $ YoY% for that brand in that channel. Columns are driven by your active benchmark toggles (xAOC, FOOD, DRUG, NATURAL, VITAMIN SHOPPE, AMAZON).
Top N brands by $ Sales (controlled by the "Show" dropdown, default 10). Channels are queried without channel filters to show all benchmark channels.
Ranks brands by dollar sales per Total Distribution Point. Higher $/TDP = more revenue per shelf slot.
Top N by $ Sales (controlled by the "Show" dropdown, default 10). Shows productivity for your key revenue-driving brands — whether they're earning their shelf space. Displays $/TDP current vs YA, change %, %ACV, total $, and share %.
Horizontal bar chart decomposing the total $ change (YoY) into its underlying drivers. Answers "why did the number move?" not just "did it move?"
What each bar means when positive or negative:
| Bar | + Positive | − Negative |
|---|---|---|
| Volume Effect | More base units selling at everyday price — genuine demand growing. Healthiest form of growth. | Fewer base units selling — shoppers walking away, switching, or leaving the category. A demand problem no amount of pricing or promos fixes long term. |
| Price Effect | Higher prices added $. But if Volume is negative at the same time, you raised prices and lost shoppers — pricing is masking a demand decline. | Prices dropped (deflation, competitive pressure, trade-down). Even if units are up, each unit brings fewer $. Selling more but earning less per unit. |
| Incremental $ Chg | Promos working harder — more deals ran or each deal drove stronger results. But if this is the only positive bar while Volume is negative, the category is becoming promo-dependent. | Promo lift shrank — fewer promos ran (budget cuts, retailer pulled back) or same promos produced weaker response (shopper fatigue, competitive drowning). If Volume also negative, declining on both fronts. |
| Net $ Change | Category grew in total $. But the bridge tells you how — healthy growth is Volume-driven. Price-driven with declining volume is fragile. Incremental-driven is expensive. | Category shrank. Bridge tells you where to act — biggest red bar = primary problem. Volume → innovation/assortment. Incremental → promo strategy. Price → deflation/margin defense. |
The combinations that tell a story:
| Volume | Price | Incr | What it means |
|---|---|---|---|
| + | + | + | Everything working — rare and strong |
| + | + | − | Base business healthy, promos pulled back (often intentional) |
| + | − | + | Units growing but prices falling, promos compensating — watch margin |
| − | + | + | Red flag — demand declining, price hikes + heavier promos masking it |
| − | + | − | Trouble — losing shoppers, promos failing, only pricing holds it up |
| − | − | + | Deep trouble — promos are the only thing keeping the lights on |
| − | − | − | Category in freefall — everything declining |
| + | − | − | Volume growing but $ shrinking — value/trade-down shift |
The single most important bar to look at first is Volume. That is the real demand signal. Everything else is levers being pulled around it.
Bubble scatter chart. X-axis = %ACV Reach (distribution breadth). Y-axis = $ per $MM ACV (velocity/productivity). Bubble size = total $ sales.
Top 25 by $ Sales with ACV + velocity data (minimum 25, even when Show is set lower). When Show=All, displays all values. Like Fair Share, the quadrant needs a broader view to spot Emerging brands (low ACV, high velocity) that are your #1 distribution expansion candidates.
The dashboard does NOT use simple SUM for %ACV or $/MM ACV. %ACV is aggregated as a TDP-weighted average. Velocity is recomputed from the implied ACV-dollar denominator row by row, which keeps mixed-channel views accurate when market ACV universes differ.
Your raw data might show Nature's Truth with SUM(%ACV Reach) = 1,535.51. But that's summing percentages across every item row — if the brand has 100 items each at 15% ACV, the sum is 1,500%, which is meaningless.
The TDP-weighted average gives you ~14% for distribution, while the velocity calculation divides total sales by the summed implied ACV-dollar denominator. That preserves the true productivity metric even when channels have different ACV universes.
| Item | %ACV Reach | TDP | %ACV × TDP |
|---|---|---|---|
| Item A (flagship) | 22.5% | 45 | 1,012.5 |
| Item B (niche) | 8.3% | 12 | 99.6 |
| Item C (mid-range) | 15.1% | 55 | 830.5 |
| … (other items) | … | … | … |
| Brand Total | 1,535.5 (useless sum) | ~110 | ~1,542 |
Dashboard X-axis = 1,542 ÷ 110 = 14.0% — the TDP-weighted average ACV
For Y-axis velocity, the dashboard recomputes the ACV-dollar denominator row by row and then divides total sales by that summed denominator. That keeps the brand-level velocity accurate even when you combine channels with different ACV universes.
Why weighting/recomputation matters: A flagship item with 45% ACV across 200 TDP matters far more to the brand's distribution picture than a niche SKU with 2% ACV across 0.5 TDP. The weighting ensures the brand-level number reflects the true distribution footprint.
How sensitive shoppers are to price changes. Price up 10% + base volume down 20% = elastic (price sensitive). Price up 10% + base volume down 2% = inelastic (price tolerant).
Note: This is a directional approximation using YoY comparisons. Base units are used instead of total units to exclude promo-driven volume spikes that would distort the price-volume relationship. True econometric elasticity requires week-level data and regression modelling.
Automatically scans every brand and flags those breaching performance thresholds:
| Alert | Threshold | Meaning |
|---|---|---|
| 📉 Severe Decline | YoY% ≤ −10% | Brand in freefall — immediate investigation |
| ⚠️ Soft Decline | YoY% −5% to −10% | Declining — monitor closely |
| 📡 Distribution Loss | %ACV drop ≥ 5pp | Losing shelf — at risk of further de-listing |
| 🏷️ Promo Dependency | Promo% ≥ 60% | Over-reliant on promotions — unsustainable |
| 🚀 Growth Opportunity | YoY% ≥ +20% | Strong growth — expand distribution |
The Herfindahl-Hirschman Index measures market concentration. Squares each selected-group share and sums them — the squaring punishes concentration, making it sensitive to dominant players.
HHI score: ALL values — concentration math must include every participant to be accurate. HHI bar chart + Share pie: Top N visible groups in the current view — for readability. The score reflects the full market, while the charts show the biggest visible players.
Companion to HHI — shows the actual selected-group share breakdown as a bar chart and doughnut. While HHI gives a single concentration score, these charts show exactly which groups hold the power under the current selection.
$ / $MM ACV measures how many dollars a brand sells per million dollars of store ACV (All Commodity Volume) where it is distributed. It isolates demand strength from distribution breadth.
A brand can grow 50% in total $ simply by gaining shelf space — but if its velocity is falling, the new stores aren't productive. Velocity tells you whether the growth is real demand or just more shelf space.
| Chart | What it shows | Why it matters |
|---|---|---|
| $/MM ACV by Dimension | Horizontal bar comparing current velocity vs YA for each group | Identifies who sells the most per point of distribution — strong sell-through vs shelf warmers |
| Velocity YoY% Change | Diverging bar showing velocity growth rate, green (up) / red (down) | Whose sell-through is improving or collapsing? The core velocity signal |
| Velocity Growth vs $ Growth | Bubble scatter: X = velocity YoY%, Y = $ YoY%. Four quadrants | The single most valuable velocity view. Upper-right = real growth. Upper-left = growing on distribution not demand. Lower-right = shrinking but productive. Lower-left = trouble |
| ACV vs Velocity Quadrant | Bubble scatter: X = %ACV, Y = $/MM ACV. Median crosshairs | Top-left = Emerging (high velocity, low ACV — expand distribution). Bottom-right = Shelf warmers (high ACV, low velocity — fix or delist) |
Group by dropdown: All charts, table, and insights update when you change the dimension — Brand, Category, Segment, Type, Subtype, Form, Manufacturer, Flavor, Age Group, Strength, Base Size, Item Description.
Show N dropdown: Default 15 for charts and table. Bubble charts use min(N, 25). Computation Breakdown defaults to 100 but follows Show N if set higher.
Single channel: Like dimension pages, the Velocity page only allows one channel at a time (default xAOC). This prevents mixed-channel velocity calculations from distorting results.
Sticky header: The title bar with Group by and Show N stays frozen at the top while scrolling through charts and tables.
Color legends: Both scatter charts show a colored-dot legend matching the quadrant colors — green/blue/orange/red for each quadrant meaning.
Auto-renders the top 100 items by $ sales (or more if Show N is set higher) showing the exact per-item math behind the aggregate velocity number. Columns in order:
| Column | Source | Description |
|---|---|---|
| UPC | Raw column | Item UPC code |
| Item Description | Raw column | Full un-abbreviated item name |
| Subtype / Form / Flavor / Strength / Base Size | Raw columns | Product attribute dimensions for each item |
| $ | Raw column $ | Current period dollar sales for this item |
| $/MM ACV | Raw column $ / $MM ACV | Item-level velocity (pre-computed by Nielsen/SPINS) |
| Current Den | $ ÷ $/MM ACV | Reverse-engineers the ACV-dollar universe for this item |
| $ YA | Raw column $ YA | Year-ago dollar sales |
| YA Vel | $/MM ACV − Chg YA | Reconstructed YA velocity (current minus the change) |
| YA Den | $ YA ÷ YA Vel | YA ACV-dollar universe for this item |
Sticky header: The teal summary row at the top shows the final velocity result (SUM($) ÷ SUM(Den)) and stays frozen while scrolling through items.
Row limit: Defaults to top 100 by $ sales. If Show N is set to 250, 500, or All — the breakdown follows that setting.
Click ⬇ CSV to download the full per-item table with all raw columns, computed columns, and the final velocity at the bottom.
Auto-generated insights specific to velocity:
| Icon | Insight |
|---|---|
| 🚄 | Overall velocity — aggregate $/MM ACV with YoY% and gainer/decliner counts |
| 🚀 | Top 3 velocity gainers — groups with the biggest velocity YoY% increase. Shows YA → Current, velocity %, $ %, and Units % |
| 📉 | Top 3 velocity decliners — groups losing sell-through efficiency fastest |
| ⚠️ | Divergence alerts — flags groups where velocity and $ YoY% move in opposite directions (velocity up + $ down = distribution loss; $ up + velocity down = growth from shelf expansion not demand) |
| 🪑 | Shelf warmers — groups with above-median ACV but below-median velocity, occupying shelf space but underperforming in sell-through |
| # | Section | What you'll learn |
|---|---|---|
| 1 | What is Switching Analysis? | Purpose, when to use it, data scope, channel defaults |
| 2 | Filter Behavior & Slice Mode | NEW — Switching now respects all top-bar filters (Type, Form, Age Group, etc.). How the slice banner, KPI subtext, and Score formula adapt when filters are active. |
| 3 | Page Layout (top to bottom) | Every section on the page — what it shows and why it's there |
| 4 | Switching Score — Full Formula | How each factor is computed, which dimensions are in the score vs informational, how filters affect scoring |
| 5 | Category Leakage Model | How we estimate shoppers leaving the category entirely |
| 6 | 8 Dimensions Explained | Why each dimension matters for switching, grouped by Demand vs Product |
| 7 | Worked Example | Nature's Bounty removal scenario with real numbers |
| 8 | How to Present in a Meeting | Slide-by-slide talking points, objection handling, recommended actions |
| 9 | Exports | CSV, PPT, and how to use switching data in presentations |
| 10 | Product-Level Substitute Matcher — Tier Logic | How the 5-tier attribute cascade picks each combo's substitute SKU |
| 11 | Charts & Calculations Deep-Dive | Every section explained card-by-card with axis math, full formula breakdowns, and a complete worked meeting example |
| 12 | Limitations & Caveats | What the model does NOT capture |
When a brand is removed, discontinued, or under review, category managers need to answer: where would the dollars go? This page models shopper migration — which competing brands absorb volume based on price, subcategory, form, age group, distribution overlap, and growth momentum.
When to use it: A retailer considering de-listing a brand · a brand announces discontinuation · pitching shelf space reallocation to a buyer · assessing your own brand's vulnerability · building a competitive threat assessment.
Data scope:
| Filter | Behavior |
|---|---|
| Channels | Respects header channel selection. If none selected, defaults to Omnichannel (US xAOC + US AMAZON + US NATURAL + US VITAMIN SHOPPE). Best used with one channel to avoid overlap. |
| Category | Respects header category filter (e.g., VITAMIN) |
| Period | Follows top filter bar. Typically LATEST 52 WKS for a full-year switching view. |
| All other filters (Type, Subtype, Form, Age Group, Strength, Base Size, Flavor, Manufacturer) | NEW — Switching now respects every top-bar filter. Filtering to e.g. Type=Magnesium analyzes "the brand's Magnesium portfolio" instead of the whole brand. See section 2 for details on slice-mode behavior, the slice banner, KPI subtext, and how the Switching Score adapts when dimensions are pinned by a filter. Note: SKU Rat is intentionally different — it always operates on the full brand portfolio, ignoring Type/Form/Age/etc. filters. To slice a brand by dimensions, use this Switching page. |
Switching now honors every top-bar filter (not just channel and category like before). When filters narrow the analysis to a slice, the page enters "slice mode" — KPIs, Score table, and all dependent sections recompute against the filtered scope.
A brand selling 30 product types isn't really one substitution problem — it's 30. "If I lose Brand X's Magnesium Gummies for Adults specifically, who absorbs?" is a much sharper question than the brand-wide one. Set Type = Magnesium, Form = Gummy, Age Group = Adult; the page now answers that exact question.
| Element | Behavior in slice mode |
|---|---|
| 🎯 Slice Banner | Appears at top of page above the KPI strip when ≥1 dim filter is active. Lists the active filters (e.g. "Type = Magnesium · Form = Gummy") and notes which Score dimensions got pinned (skipped from formula). |
| KPI Cards | Subtext gains a slice suffix on YoY/share lines (e.g. "+5.2% YoY · in Magnesium · Gummy") so users never lose track of the scope. Values reflect the filtered slice. |
| Switching Score formula | Skips score dimensions that filters pin. Example: if Type = Magnesium is filtered, every brand in scope sells Magnesium → Type overlap is 100% by construction → it would just multiply through as 1.0 and add no signal. We drop it from the multiplicative product so only varying dimensions contribute. Pinned dim's overlap still shows in the table (as 100%). |
| Score table dim columns | All 8 dimension overlaps still display. Pinned ones show 100% (correct — every brand in scope matches by definition). |
| Distribution overlap (retOv) | Filter-aware — within the selected channel/period/category/dim slice, measures "% of target's retailers also served by competitor X." Reflects within-slice retailer sharing. |
| Substitute Matcher | Operates within the slice. Each combo finds substitutes in matching scope. |
| Migration Model + Scatter | Both reflect filtered scope. |
| Brand DNA filter (existing, separate) | Still works independently — it filters the competitor universe by brand-type relevance. Top-bar filters filter the target's slice. Both axes can be combined. |
Three dimensions feed the Switching Score: Segment, Form, Age Group. When a top-bar filter constrains one of those, it's "pinned" — every brand in the slice matches it by construction. The Score formula skips that dim's contribution so the score reflects only signal that varies.
Filter → pinned dim mapping:
| Position | Section | What it shows | Story it tells |
|---|---|---|---|
| 1 | 🎯 Slice Banner (NEW) | Appears when filters narrow scope. Shows active slice + which Score dims got pinned. | "You're not analyzing the whole brand — here's the slice you've drilled into" |
| 2 | KPI Cards | Sales, Units, Share, Promo%, Price, SKUs, ACV, Leakage est. Subtext shows slice when active. | How big is the (slice of the) brand? How vulnerable are its shoppers? |
| 3 | Key Insights | 13 auto-generated executive insights | The "so what" — read this first for the full story |
| 4 | Retailer Exposure | Channels + Retailers with share and risk badges | Which retailers would be hurt most? Establishes the geographic/channel stakes before diving into substitution. |
| 5 | Analysis Flow | Visual tree: brand → leakage/capturable → score → top 5 | One-glance summary of the entire analysis — synced with the Switching Score table directly below |
| 6 | Switching Score Table | Top 30 brands ranked by composite score with all 9 overlaps | The definitive substitution ranking — the authority all other sections defer to |
| 7 | Price Positioning | $/Unit bar chart with target highlighted | Who fills the value gap? |
| 8 | Product-Level Substitute Matcher | For each Type × Subtype × Form × Base Size × Flavor combo in the target brand, top 3 alternative products from competing brands with tiered match quality | "Here's the actual SKU shoppers would switch to" — drills past the brand-level score to product-level recommendations |
| 9 | Migration Model + Scatter | Doughnut (est. capture) + Bubble (score vs growth) | Where do the dollars flow? Who's best positioned? |
| 10 | Retailer Deep-Dive | Multi-select: side-by-side retailer comparison | Who gains at THIS specific retailer? |
| 11 | Demand Profile | Segment, Type, Subtype breakdowns + alternatives | Diagnostic view of WHAT subcategories/benefits the brand serves — alternatives ranked by Switching Score authority |
| 12 | Product Characteristics | Form, Flavor, Age Group, Strength, Base Size + alternatives | Diagnostic view of HOW shoppers consume — alternatives ranked by Switching Score authority |
Reading the page top-to-bottom: 1-3 establish the brand/slice and the headline takeaways. 4-6 set up the void (where they sell, the analytical flow, the ranked substitutes). 7-8 dimensionalize the substitute ranking (price, specific SKUs, dollar migration). 10 lets you stress-test at a single retailer. 11-12 are the deep diagnostic — reference material for understanding why the score ranks brands the way it does, used when stakeholders ask "but why isn't [Brand] in the top 5?"
The score is multiplicative — a brand must score well on ALL core factors. A perfect price match with zero distribution overlap scores zero.
| Factor | In Score? | Range | Formula | Why |
|---|---|---|---|---|
| Price Proximity | ★ Yes | 0–1 | 1 − min(|price_gap|/target_price, 1) | Shoppers substitute within budget tier first |
| Segment Overlap | ★ Yes (may be pinned) | 0–1 | shared_segments / target_segments | Heart Health buyer won't switch to Sleep Aid |
| Form Overlap | ★ Yes (may be pinned) | 0–1 | shared_forms / target_forms | Gummy shoppers rarely switch to tablets |
| Age Group Overlap | ★ Yes (may be pinned) | 0–1 | shared_age_groups / target_age_groups | Child ≠ Adult — hard barrier |
| Distribution Overlap | ★ Yes | 0–1 | shared_retailers / target_retailers | Can't switch to a brand not on the same shelf |
| Momentum Bonus | ★ Yes | 0.9–1.3 | YoY ≥25% → 1.3, ≥10% → 1.15, ≥0% → 1.0, <0% → 0.9 | Retailers favor growing brands for shelf space |
| Type Overlap | Info only | 0–1 | shared / target | More granular classification |
| Subtype Overlap | Info only | 0–1 | shared / target | Most granular — specialist shoppers |
| Flavor Overlap | Info only | 0–1 | shared / target | Mainly gummies — flavor preference |
| Strength Overlap | Info only | 0–1 | shared / target | Dosage (1000mg vs 500mg) |
| Base Size Overlap | Info only | 0–1 | shared / target | Pack size (60ct vs 120ct) |
Why only 6 factors in the score? Multiplying 11 factors together pushes ALL scores toward zero and loses discriminating power. The 6 core factors capture the critical switching barriers (price, what they buy, how they take it, where they shop, age constraint, momentum). The other 5 are shown as columns so you can evaluate them yourself — e.g., a 0% Flavor overlap doesn't mean the brand can't absorb volume, it just means flavor-sensitive gummy shoppers may be less satisfied.
Final scores are normalized 0–100 — the brand with the highest raw score becomes 100, all others are proportional.
When a top-bar filter pins one of the three score dimensions (Segment, Form, Age Group), that dim's overlap is 100% by construction for every brand in the slice — multiplying by 1.0 just adds noise without signal. So the score formula skips pinned dimensions. Examples:
| Active filter | Pinned dim | Effective score formula |
|---|---|---|
| None | — | Price × Segment × Form × Age Group × Distribution × Momentum |
| Type = Magnesium | None (Type isn't in score) | Price × Segment × Form × Age Group × Distribution × Momentum |
| Form = Gummy | Form | Price × Segment × Age Group × Distribution × Momentum |
| Form = Gummy + Age = Adult | Form, Age Group | Price × Segment × Distribution × Momentum |
| Segment + Form + Age all filtered | All 3 score dims | Price × Distribution × Momentum (pure price/dist/momentum ranking — see Section 2) |
Distribution overlap is filter-aware. Within whatever channel/category/filter slice the user has selected, retOv = "% of target's retailers that competitor X also serves." This means in narrow slices the metric reflects in-slice footprint sharing, which is exactly the right signal — if neither brand sells in the slice's retailers, they can't switch volumes there.
All 8 dimension overlap columns still display in the Score table regardless of filters — pinned ones just show 100% (correct: every brand in scope matches by definition).
Not all dollars transfer to competitors — some shoppers leave the category entirely.
| Promo % | Leakage | Rationale |
|---|---|---|
| 30% | 5% | Base minimum — some loyal shoppers always leave |
| 50% | 7% | Moderate deal-seeking |
| 60% | 9% | Heavy promo — significant deal-driven base |
| 80% | 13% | Very heavy — most buy for the deal, not the product |
| 92% | 15.4% | Extreme (Drug channel) — nearly all deal-driven |
Demand Profile — defines WHAT the shopper is buying:
| Dim | In Score? | Switching relevance | Example |
|---|---|---|---|
| Segment | ★ Yes | Primary subcategory — THE critical switching barrier | Multivitamin, Heart Health, Sleep Aid, Digestive |
| Benefit | Info | Overlaps Segment but captures cross-segment needs | IMMUNE, OMEGA, WEIGHT, MUSCLE |
| Type | Info | More granular than Segment | FISH OIL, COQ10, MELATONIN |
| Subtype | Info | Most granular product classification | FISH OIL SOFTGEL, MELATONIN GUMMY |
Product Characteristics — defines HOW the shopper consumes it:
| Dim | In Score? | Switching relevance | Example |
|---|---|---|---|
| Form | ★ Yes | Strong loyalty — gummy shoppers won't take tablets | GUMMY, SOFTGEL, CAPSULE, TABLET, POWDER |
| Age Group | ★ Yes | Hard constraint — child ≠ adult ≠ senior | ADULT, CHILD, SENIOR, ALL FAMILY |
| Flavor | Info | Mainly gummies — flavor preference can drive loyalty | STRAWBERRY, ORANGE, MIXED BERRY |
| Strength | Info | Dosage specificity — shoppers may need exact potency | 1000 MILLIGRAM, 5000 IU |
| Base Size | Info | Pack size — affects perceived value | 60 COUNT, 120 COUNT, 200 COUNT |
Previously each dim breakdown ranked alternative brands by raw $ within that slice. That meant large-but-ill-fitting brands (Private Label, Nature Made for a niche brand like NELLO SUPER CALM) appeared as top alternatives even though they didn't make the top 5 in the authoritative Switching Score.
Now: alternatives in every dim table — Segment, Benefit, Type, Subtype, Form, Age Group, Flavor, Strength, Base Size — are ranked by slice $ × (brand's Switching Score / 100). Brands disqualified from the Switching Score (zero score, discontinuation guard) drop out entirely. The visible Score column in each row makes the basis explicit — you can see at a glance why a particular brand placed where it did.
Result: the alternatives in dim tables, the Substitute Matcher, the Analysis Flow tree at top, and the Switching Score table itself all agree on who the real top substitutes are. One ranking, four views.
Target: Nature's Bounty — $683M on US xAOC, 5.8% share, 60.1% promo, $11.19/unit
Step 1: Leakage = 5% + (60.1 − 40) × 0.2 = 5% + 4.02% = 9.0% → $61.5M exits category
Step 2: Capturable = $683M − $61.5M = $621.5M
Step 3: Score Nature Made — Price: $12.62 → gap 12.8% → score 0.87. Segment: 15/17 shared → 0.88. Form: 5/6 → 0.83. Age: 2/2 → 1.0. Dist: 28/30 → 0.93. Mom: +12% YoY → 1.15. Raw = 0.87 × 0.88 × 0.83 × 1.0 × 0.93 × 1.15 = 0.68
Step 4: If Nature Made has highest raw (0.68 → normalized to 100), Private Label raw 0.41 → 60.3, Olly raw 0.35 → 51.5...
Step 5: Migration allocation — Nature Made captures 100/(100+60+52+...) × $621.5M ≈ $171M
Slide 1 — The Void:
Slide 2 — Where the Dollars Go:
Slide 3 — The Risk:
Slide 4 — The Recommendation:
Handling objections:
| Objection | Response |
|---|---|
| "How do you know shoppers will switch to [brand]?" | The switching score is based on 5 measurable factors: price proximity, subcategory overlap, form overlap, age group match, and distribution presence. [Top Brand] scores highest because it competes in the same subcategories, at a similar price point, in the same forms, at the same retailers. |
| "What about brand loyalty?" | The leakage estimate accounts for this — [X]% of shoppers are expected to leave the category rather than switch. But the remaining [100-X]% are price/product-driven shoppers who WILL find an alternative. |
| "These are just estimates." | Correct — the model is directional, not a precise forecast. But it's built on actual sales data across [N] dimensions. The alternative to modeling is guessing. |
CSV exports available on the switching page:
| Button | What it exports |
|---|---|
| Retailer Exposure ⬇ CSV | All retailers with brand share, risk level, promo %, YoY% |
| Switching Score ⬇ CSV | Top 30 brands with all 8 dimension overlaps, price gap, momentum |
| Product Matches ⬇ CSV | Every target combo × top 3 alt products with tier label, base size, flavor, $, price |
PPT export: The switching page is included in the full dashboard PPT export — charts are captured at 3× resolution. Use the Migration Model doughnut and Switching Score table as your key slides.
The brand-level switching score tells you which brand would absorb volume. The Product Matcher drills one level deeper — which specific SKU. For each unique combination of Type × Subtype × Form × Base Size × Flavor in the target brand (e.g., "Botanical · Ashwagandha · Capsule · 60 Count · Unflavored"), it finds the closest-matching product from each competing brand.
Exact 5-attribute matches are rare — base size and flavor vary a lot between brands. Rather than returning "no match," the matcher falls back tier by tier:
| Tier | Matches on | Relaxed | When it triggers |
|---|---|---|---|
| 🟢 Perfect | Type + Subtype + Form + Base Size + Flavor | Nothing | A competitor makes the exact same product spec |
| 🔵 Strong | Type + Subtype + Form + Base Size | Flavor | Same product, different flavor available |
| 🟡 Good | Type + Subtype + Form | Base Size, Flavor | Same ingredient in same format, any size/flavor |
| 🟠 Loose | Type + Subtype | Form, Base Size, Flavor | Same ingredient, any form — acceptable for supplement substitutability |
| 🔴 Type-only | Type | Everything except Type | Last resort — niche combo with no close match |
The tier found is shown as a badge on each target row. Each alternative product also shows a mini strip indicating which of the 5 attributes actually matched (green) vs fell back (gray): T = Type, Sub = Subtype, F = Form, Sz = Base Size, Flv = Flavor.
Once the matcher locks onto a tier (e.g., Strong), it picks the top 3 alternative brands in that tier. Previously they were ranked by raw $ within the slice — which meant Private Label, Nature Made, etc. dominated nearly every row regardless of whether they were a credible substitute for the specific target brand. Now within-tier ranking uses slice $ × (brand's Switching Score / 100), so a brand has to both have presence AND score well overall to appear here.
Each alternative now displays a colored Score badge next to the brand name (green ≥70, amber ≥40, gray below). At a glance you can see WHY a particular brand placed there. Brands disqualified by the discontinuation guard or zero score drop out entirely — if no score-positive brand exists in the tier, the matcher falls back to raw-$ ranking so the row isn't blank.
Why this matters: When you sit across from a buyer and they ask "okay if we drop your Ashwagandha Capsule 60ct, which NATURE MADE SKU fills that hole?", the brand-level score doesn't answer. This table does — it walks product-by-product and names the substitute, anchored to the same authoritative ranking the Switching Score table uses.
Caveats: The matcher only sees combos that sell in your filtered channel scope — a real substitute might exist on another retailer's shelf. Base size parsing is string-based ("60 COUNT" ≠ "60CT" ≠ "60ct") so normalize your data if you see near-duplicates. Flavor matching is exact-string too: "UNFLAVORED" ≠ "NATURAL" even when effectively the same.
Card-by-card breakdown of every visual on the Switching page — the calculation behind it, how to interpret each axis or column, and a worked meeting example.
| KPI | How it's computed | Why it matters |
|---|---|---|
| Total $ Sales | SUM("$") in scope (channels × period × category × brand) | Scale of the void if brand is removed |
| Units | SUM(Units) | Volume context — high $ + low units = premium positioning |
| Share % | brand $ ÷ total category $ in scope × 100 | How dominant in the channels you're analyzing |
| Promo % | SUM("Any Promo $") ÷ SUM("$") × 100 | Drives leakage — high promo = many deal-driven shoppers won't switch |
| $/Unit | SUM("Base $") ÷ SUM("Base Units") | Price tier — substitutes within ±15% are most likely |
| Active SKUs | COUNT(DISTINCT UPC) where $ > 0 | Portfolio breadth at risk |
| %ACV | SUM(%ACV Reach × TDP) ÷ SUM(TDP) — TDP-weighted | How broadly distributed — affects who can absorb on shelf |
| Est. Leakage | min(25, 5 + max(0, (Promo% − 40) × 0.2)) | Pre-computed % that won't switch — read straight off the card |
Visual recap of the math: Brand $ → Leakage $ (drops out) + Capturable $ → top 5 beneficiaries with their estimated capture in dollars.
Each beneficiary's $ value is computed as: capturable $ × (their score / sum of top 15 scores). The top 15 anchor (rather than top 10) leaves a residual for "Other Brands" — preventing the top 10 from spuriously summing to 100% when there are more substitutes.
| Column | Calculation | Interpretation |
|---|---|---|
| Retailer / Channel | From Markets column | Each row is a channel or individual retailer |
| Brand $ | SUM("$") for target brand at this retailer | Target's revenue here |
| Brand Share % | brand $ ÷ retailer's total category $ × 100 | Higher = retailer hurts more if brand is removed |
| Cat $ | SUM("$") for entire category at this retailer | Retailer's total category size |
| YoY% | pct(brand $, brand $ YA) at this retailer | Is target growing or shrinking here |
| Promo% | retailer-specific promo % | Some retailers run heavier promo than others |
| $/Unit | retailer-specific Base $ ÷ Base Units | Pricing varies by channel |
| Risk Badge | ≥10% share = HIGH · 5-10% = MEDIUM · <5% = LOW | One-glance vulnerability indicator per retailer |
Each of the 8 dimensions (Segment, Type, Subtype, Form, Flavor, Age Group, Strength, Base Size) renders the same template:
Overlap math (per dim): |values brand X carries ∩ values target carries| ÷ |target's values|. So if target sells 5 segments and Nature Made sells 4 of those 5, Nature Made's segment overlap = 4/5 = 0.80.
$/Unit for the top brands in scope, target highlighted in navy. Y-axis = price per unit (Base $ ÷ Base Units, weighted by sales). Read as: brands within ±15% of target's price are the most natural switchers; price-sensitive shoppers stay near their tier. Brands far above signal "premium escape" routes (loyalists trading up) and far below signal "value escape" (deal-seekers trading down).
Ranked output of the multiplicative scoring. Every column shown:
| Column | Formula | Read |
|---|---|---|
| Score | 0–100 normalized (max raw = 100) | Composite ranking |
| $ Sales | brand $ in scope | Existing scale — they need infrastructure to absorb |
| YoY% | pct(D, DYA) | Momentum — feeds momBonus tier |
| $/Unit | Base $ ÷ Base Units | Driver of priceSc |
| Price gap | (brand price − target price) ÷ target price × 100 | ±15% = natural switchers |
| Seg / Type / Sub overlap | shared / target — fraction | Demand-side fit |
| Form / Flv / Age / Str / Sz overlap | shared / target | Product-side fit |
| Dist overlap | shared retailers / target retailers | ★ in score — must share retail footprint |
| Est. capture $ | capturable $ × score ÷ top-15 score sum | Dollar value of substitution |
Discontinuation guard: competitor brands at brand-level YoY worse than −60% are removed from the ranking entirely. They can't credibly absorb migrating shoppers if they're hemorrhaging share themselves. Brands with no YA baseline (potential new entrants) stay in.
For each Type × Subtype × Form × Base Size × Flavor combo in target's portfolio, the top 3 alternative products from competing brands. Each alt shows: brand · item · $/unit · price gap · attribute strip showing which of T/Sub/F/Sz/Flv matched (green) vs relaxed (gray) · tier badge.
Tier hierarchy: Perfect (all 5) → Strong (Flv relaxed) → Good (Sz+Flv relaxed) → Loose (T+Sub only) → Type-only.
Slices: top 10 brands (each labeled with $ and %), Other Brands (ranks 11-15 of substitutes), and Category Leakage (red slice). Total of all slices = target brand's full $.
Each brand slice: capturable $ × score_i ÷ Σ(top 15 scores)
Category Leakage slice: D × leakagePct ÷ 100 — the dollars that exit the category entirely.
X = Switching Score · Y = YoY% · bubble size = $ Sales. Quadrants colored:
Reading the chart: Bubbles in the top-right are the "you should be talking about these brands" brands — they have the structural fit AND the momentum retailers love. Big bubbles in the bottom-left are big brands that don't credibly substitute (different price tier, different shelf, etc.).
Pre-populated with the channels in your filter, but you can add or remove retailers and click Apply. For each chosen retailer, shows a side-by-side table of top brands with: $, YoY%, $/unit, price gap vs target, promo%. This lets you tell different switching stories at different retailers — "at ShopRite the threat is X, but at Walmart it's Y."
Buyer asks: "If we de-list Nature's Bounty Sleep Aid Gummies on ShopRite, what happens?"
Step 1 — Open Switching, set scope: Channel = ShopRite, Brand = NATURE'S BOUNTY (or filter the page narrower). Period = LATEST 52 WKS. Read KPIs: $4.2M brand at ShopRite, 3.8% category share, promo 68%, $/unit $9.50, %ACV 88%.
Step 2 — Read leakage: Promo 68% → Leakage = 5% + (68−40)×0.2 = 10.6%. So $445K leaves the category. Capturable = $4.2M − $445K = $3.76M.
Step 3 — Read top beneficiaries from the Switching Score table. Suppose: NATROL (score 92, +18% YoY, price $8.10, segment 100%, form 100%, dist 95%), OLLY (score 78), VITAFUSION (score 71), PRIVATE LABEL (score 65). NATROL captures roughly 92/(92+78+71+65+...) × $3.76M ≈ $1.05M.
Step 4 — Read Product Matcher for the specific de-listed SKU (e.g., Nature's Bounty Melatonin Gummy 60ct Strawberry). Tier = Perfect, alts = NATROL Melatonin Gummy 60ct Strawberry, OLLY Sleep Gummy 50ct Berry, VITAFUSION Melatonin Gummy 90ct Mixed Berry.
Step 5 — Frame the meeting: "If you de-list, $445K of shoppers walk out of the category entirely — those are the ones buying on deal. Of the $3.76M that stays, NATROL is best positioned to absorb $1.05M because their melatonin gummy 60ct strawberry is a perfect attribute match. They're already at 95% of your retailer footprint. We recommend you negotiate expanded NATROL facings BEFORE delisting, with a promo calendar in place to retain the deal-seekers we'd otherwise lose."
| Format | What's included | Best use |
|---|---|---|
| CSV (per card) | Retailer exposure, switching score, product matches — full data dumps | Pivot tables, custom slides, deeper analysis in Excel |
| PNG (per chart) | Price Positioning, Migration, Scatter chart images at 3× resolution | Drop directly into a slide |
| PPT (full export) | Native cover slide with KPIs + analysis flow tree text + every canvas + tables (capped at 6 per page) | Buyer meeting deck — use as starting point, polish narrative |
| Print-ready snapshot of the page | Internal review, archival |
The Switching Score is a modeled estimate, not a prediction. It does not capture:
1) Brand equity / loyalty — some shoppers will search multiple stores rather than switch · 2) Retailer negotiations — a retailer may already have a replacement deal · 3) Planogram constraints — shelf space may go to a new brand entirely · 4) Regional variation — switching patterns differ by geography · 5) Online migration — shoppers may move to Amazon · 6) Innovation pipeline — a new product launch could capture share · 7) Cross-category substitution — a vitamin shopper might switch to a functional food instead.
Use the score as a directional guide for buyer conversations, not as a precise forecast. The model is most accurate for large brands with broad distribution — small/niche brands have less data to score against.
| # | Section | What you'll learn |
|---|---|---|
| 1 | What is SKU Rationalization? | Purpose, when to use it, data scope, filter behavior (period follows top bar, category + channel filters respected) |
| 2 | Active vs Inactive Classification | The four gates that decide which SKUs power the analytics vs which are dying/vestigial |
| 3 | Distribution Diagnosis (%ACV Δ pp) | NEW — every table now shows YoY change in distribution (percentage points). Use this to separate "shelf-loss decline" from "demand-side decline" in any SKU's story. |
| 4 | Page Layout (top to bottom) | Every card on the page — what it shows and the story it tells |
| 5 | The 4 Action Zones | CUT / EXPAND / PROTECT / FIX — how each SKU gets classified via brand-median split |
| 6 | Cut Score Formula | The 0-100 composite that flags cut-worthy SKUs — every point explained |
| 7 | Whitespace Opportunities | 🚀 BUILD / 📈 INVEST / 🎯 EXPAND / 🛡️ DEFEND — growth framework + opportunity $ formula |
| 8 | SKU Replacement Opportunities | ⚡ QUICK WIN / 🎯 STRONG SWAP / 🔄 CONSIDER / ⚠️ MONITOR — declining competitor SKUs matched to your growing SKUs with confidence scoring and a priority watchlist |
| 9 | Cannibalization Detection | How sibling SKUs in the same Type · Subtype · Form are identified and ranked |
| 10 | Portfolio Depth Bands | SINGLE / LEAN / STANDARD / DEEP / BLOATED — what each band means for each family |
| 11 | Assortment Gap Analysis | Combos ≥2 competitors carry that your brand doesn't — validated demand with zero presence |
| 12 | How to Run a Review | Step-by-step framework for using the page in a buyer meeting or category review |
| 13 | Exports | 10 CSVs (incl. NEW Inactive SKUs Audit), PPT coverage, what's in each file |
| 14 | Charts & Calculations Deep-Dive | Every card explained, every formula broken down step-by-step (Confidence, Transfer $, Cut Score, Whitespace, Pareto), with worked meeting examples |
| 15 | Limitations & Caveats | What the framework does NOT capture — margin, strategic flags, shopper missions |
SKU Rationalization answers the hardest portfolio question: which SKUs should we keep, cut, expand, or build? Category managers run this before line reviews, when trimming working capital, after acquisitions, or when a buyer asks to reduce item count.
Unlike the Switching page (which models "what happens if we lose a brand"), this page operates within a single brand's portfolio and identifies both internal drag (cuts) and external growth (whitespace).
When to use it:
Data scope:
| Filter | Behavior |
|---|---|
| Period | Follows the top filter bar — whatever period is selected at the top applies here. Change it at the top and the page re-renders automatically. |
| Channels | Respects the header channel filter. If none selected, defaults to Omnichannel (xAOC + Amazon + Natural + Vitamin Shoppe). Best used with one channel or retailer for clean velocity/ACV benchmarks. |
| Category | Respects the header Category filter (VITAMIN, HERBAL SUPPLEMENT, etc.) |
| Brand | Page-local selector — pick the brand whose portfolio you're reviewing. Defaults to #1 brand by $ in filter scope. Linked to the Switching page's brand selector — changing here updates Switching too, in real time. |
| All other filters (Type, Subtype, Form, Age Group, Strength, Base Size, Flavor, Manufacturer, Segment, Benefit) | Intentionally ignored. SKU Rat operates on the full brand portfolio so Pareto curves, cannibalization, and assortment depth always reflect the brand's complete SKU footprint. Slicing here would break the "is this brand over-SKU'd?" diagnostic. To see brand-within-slice analysis, use the Switching page — that one respects every filter. An orange info strip on the SKU Rat page header reminds you of this whenever non-respected filters are active. |
Before any analysis runs, every SKU in the brand's portfolio is classified as ACTIVE, INACTIVE, or NEW. Only Active SKUs power the rest of the page — Cut Candidates, Expand Candidates, Pareto curve, Whitespace, Replacement opportunities. This prevents dying or vestigial SKUs from polluting the analysis (e.g. a SKU that lost 80% of its sales last year shouldn't show up as a "PROTECT zone" candidate just because it once sold well).
Four gates are evaluated in order. The first gate that triggers wins.
| Gate | Rule | Result |
|---|---|---|
| A | No current sales — D ≤ 0 | INACTIVE — no presence to analyze |
| B | New launch — DYA = 0 AND D > 0 AND has TDP/ACV (real distribution, not data ghost) | ACTIVE + flagged NEW — never demoted, even at tiny share. New SKUs need time to accumulate share. |
| C | YoY collapse — has YA baseline (DYA > 0) AND yoy ≤ −60% | INACTIVE — same threshold as the discontinuation guard used in Switching and Replacement code; an effectively-discontinued SKU. |
| D | Vestigial tail — in cumulative bottom 1% of brand $ AND has YA baseline AND yoy ≤ −25% | INACTIVE — both tiny AND clearly shrinking is the signal. We don't ding stable-but-niche SKUs (yoy > −25% keeps them active), and we don't dig out −5% noise SKUs at the top of the curve. |
| — | Anything that survives gates A–D | ACTIVE |
Every table on this page now carries a %ACV Δ pp column (percentage points change vs year ago) so you can answer the most important question in any review: "is this SKU's revenue change driven by distribution or by demand?" Without this, a declining SKU looks the same on paper whether it's losing shelf or losing shoppers — but the right action is completely different.
| YoY $ | %ACV Δ pp | Diagnosis | Action |
|---|---|---|---|
| + | + | Healthy growth — distribution gain is supporting velocity | Protect the trend; defend shelf at line review |
| + | − | Velocity is up despite shelf-loss — strong product, but vulnerable | Reverse the distribution slide before it catches up; demand alone won't carry forever |
| − | − | Distribution-driven decline — losing shelf AND losing dollars | Fix the listing/delisting problem first. Investigate which retailers cut and why |
| − | + | Demand-side decline — shelf is fine, shoppers aren't picking it up | Investigate price, promo cadence, brand health, competitive substitution. Distribution push won't fix this. |
Per SKU: acv = SUM(%ACV Reach × TDP) ÷ SUM(TDP) · acvYA = SUM(%ACV Reach YA × TDP YA) ÷ SUM(TDP YA) · acv Δ pp = acv − acvYA — measured in percentage points, not relative %. A SKU at 10%ACV current and 8% YA shows "+2pp", not "+25%".
Why pp, not %? Because %ACV is itself a percentage. A 25% relative change at low ACV ranges (8% → 10%) means very different things from 25% at high ranges (60% → 75%). The pp delta is the same regardless of base, and is how Nielsen/IRI conventionally report distribution change.
Brand-level number on the KPI card uses TDP-weighted aggregation across all SKUs (active + inactive + lost) — because withdrawn SKUs' shelf collapse is the brand-level distribution story.
| Position | Section | What it shows | Story it tells |
|---|---|---|---|
| 1 | Header + Brand Selector | Brand dropdown, period pill (read-only — follows top filter), channel summary | What portfolio am I looking at, and in what scope? |
| 2 | KPI Cards (8) | Total/active SKU counts, avg $/SKU, Pareto ratio, bottom-25% $, zone counts (CUT/EXPAND/PROTECT/FIX) | How big is the portfolio? How concentrated is revenue? How many SKUs are vulnerable? |
| 3 | Portfolio Health Insights | Auto-generated executive summary — bloat severity, zone breakdowns, cluster depth, cut volume | The "so what" — read first for a narrative summary |
| 4 | Performance Quadrant | Scatter: X = %ACV, Y = velocity. Each dot = one SKU, colored by action zone, sized by $, median-split | Visual map of the 4 zones — find the CUT cluster in the bottom-left |
| 5 | Pareto Curve | Cumulative $ share by SKU rank, with 80% line marked | Steep early curve + long flat tail = portfolio bloat. Gentle = healthy spread |
| 6 | Whitespace Opportunities | Bubble chart + ranked table: combos where brand has weak or no presence but market has demand | Where can this brand grow? The flip side of cutting — what to build or invest in |
| 7 | SKU Replacement Opportunities (in-scope) | Priority watchlist cards + full table: declining competitor SKUs paired with your growing equivalents, both in the selected channel | Walk into the buyer with "delete this, stock this" — backed by YoY evidence on both sides |
| 8 | SKU Replacement — Channel Expansion (xAOC) | Declining competitor SKUs in the selected channel, matched to your growing xAOC SKUs that are not yet listed there | Get on the shelf — "Brand X is losing velocity here, we have a proven matching SKU you don't stock yet" |
| 9 | Cut Candidates | Top 30 SKUs by cut score with zone badge, financials, cannibalizer count, and rationale chips | Who gets cut — with the rationale written out |
| 10 | Expand Candidates | SKUs in EXPAND zone (high velocity, low ACV) with dollar upside if pushed to 60% ACV | Which existing SKUs deserve more shelf space — trade these for the cuts |
| 11 | Internal Cannibalization | Clusters of 3+ SKUs in same Type · Subtype · Form — top vs weakest SKU with weakest-share % | Where is the portfolio stepping on its own toes |
| 12 | Portfolio Depth | SKU count per product family with SINGLE/LEAN/STANDARD/DEEP/BLOATED band | Where is shelf/working capital concentrated — is it justified by revenue? |
| 13 | Assortment Gaps | Combos competitors sell but your brand doesn't — ranked by category $ with competitor count | Pure whitespace — no presence at all in proven markets |
| 14 | 💀 Inactive SKUs Audit (NEW) | Every SKU classified inactive by the four-gate ruleset, with the gate that fired (A/C/D) and reason text. Sorted by $ YA descending. No row limit — all inactive SKUs render. Auto-hides if no inactive SKUs exist. | Audit transparency — "show me which SKUs got excluded from the analysis and why" |
Every SKU is assigned to one of 4 zones based on its position relative to the brand's own median for ACV and velocity. Anchoring to the brand's median means every brand gets a balanced view — you're not comparing a niche specialty brand to Nature Made's scale.
| Zone | ACV | Velocity | Meaning | Action |
|---|---|---|---|---|
| 🛡️ PROTECT | ≥ median | ≥ median | Core portfolio — broad distribution AND strong sell-through | Defend shelf, avoid promo erosion, keep in stock |
| 🟢 EXPAND | < median | ≥ median | Distribution opportunity — shoppers want it where they can find it | Push for ACV expansion at buyer reviews; each ACV point converts |
| 🔧 FIX | ≥ median | < median | Product problem — shelf space isn't paying off | Investigate pricing, pack size, placement, marketing BEFORE cutting |
| 🔴 CUT | < median | < median | Low distribution AND low sell-through — neither retailers nor shoppers want it | Primary rationalization target |
A composite score 0-100 that flags how cut-worthy a SKU is. Higher = more cuttable. Built additively so multiple weaknesses compound.
| Condition | Points | Rationale |
|---|---|---|
| Velocity below brand median | +25 | Core signal — not selling through relative to peers |
| Velocity ≪ brand median (< half) | +10 more | Severely underperforming — compounds |
| ACV below brand median | +20 | Core signal — not stocked widely |
| ACV ≪ brand median (< half) | +10 more | Severely under-distributed |
| YoY < −10% | +15 | Active decline on top of low baseline |
| YoY < −25% | +10 more | Falling off a cliff |
| Has ≥1 cannibalizer | +10 | Revenue likely transfers to sister SKU on cut |
| Has ≥2 cannibalizers | +5 more | Cluster is oversaturated |
| Total | Capped at 100 | Theoretical max from the additive stack is 105; capped for display |
How to read the score:
| Score band | Recommendation | Narrative for buyer |
|---|---|---|
| < 40 | Keep — marginal concerns only | This SKU is pulling its weight despite minor signals |
| 40-60 | Review — flag for category manager discussion | Multiple weaknesses — worth a conversation |
| 60-80 | Strong cut candidate — build a case | Three or more criteria met — low revenue risk |
| 80+ | Clear cut — minimal revenue risk, likely cannibalizing | "This SKU checks every box for removal — cannibalizers exist, low ACV, low velocity, declining" |
The growth-side counterpart to cutting. For every Type · Subtype · Form · Base Size · Flavor combo in the filter scope, the page computes the target brand's presence and classifies the opportunity.
How to read the bubble chart: X = Category $ (log scale — market size) · Y = brand's share % in that combo (0-100%) · bubble size = opportunity $ at stake · color = action tag. Bottom-right is the sweet spot — large markets where the brand has low share.
Four action tags, in order of priority:
| Tag | Trigger | Meaning | Action |
|---|---|---|---|
| 🚀 BUILD | Brand $ = 0 in combo | Market exists (≥$5K), brand has zero presence | New product candidate — launch a SKU that fills this combo |
| 📈 INVEST | Brand share < 20% | Brand is present but under-represented in a meaningful market | Push distribution + marketing spend here — there's room to grow |
| 🎯 EXPAND | Share 20-50% AND ACV gap ≥ 10pp | Fair share but top competitor has materially more distribution | Target buyer reviews — every ACV point converts at existing velocity |
| 🛡️ DEFEND | Share ≥ 50% OR ACV gap < 10pp | Already strong — protect the flank from competitors | Watch for incumbents entering, keep promo calendar sharp |
Best use: Pair with Cut Candidates. The story becomes "we're cutting X to fund Y" — where X is a low cut-score SKU and Y is a BUILD/INVEST whitespace combo. Net-neutral item count, better assortment.
The tactical counterpart to Whitespace. Instead of proposing to build new products, this section finds declining competitor SKUs in the current filter scope and pairs each with the target brand's equivalent growing SKU. Every row is a concrete "delete this, stock this instead" recommendation you can walk into a buyer meeting with — backed by YoY evidence on both sides.
How pairs are formed:
Confidence score (0-100):
| Component | Points | Rationale |
|---|---|---|
| Tier base — Perfect | 60 | All 5 attributes match — cleanest possible swap |
| Tier base — Strong | 45 | T+Sub+F+Sz, flavor differs |
| Tier base — Good | 30 | T+Sub+F, size & flavor differ |
| Tier base — Loose | 15 | T+Sub only — same ingredient, different format |
| Tier base — Type-only | 5 | Weak match, last resort |
| Momentum gap | up to +30 | |decliner YoY| + max(0, grower YoY) × 0.4 — rewards wide divergence |
| Distribution proof | +10 or +5 | Your SKU ≥ 10% ACV adds +10, ≥ 1% adds +5 — retailers already accepted it |
Action tags:
| Tag | Trigger | Meaning |
|---|---|---|
| ⚡ QUICK WIN | Confidence ≥ 80 AND tier ≥ Strong | Lead the buyer meeting with this one — tightest match, strongest story |
| 🎯 STRONG SWAP | Confidence ≥ 60 | Clear case — defensible with ordinary prep |
| 🔄 CONSIDER | Confidence ≥ 40 | Partial fit — useful if the decliner is big enough to matter |
| ⚠️ MONITOR | Confidence < 40 | Weak match — track the trend, don't propose yet |
Estimated transfer $: the declining SKU's dollars × a transfer rate keyed to match quality:
| Tier | Transfer rate | Reasoning |
|---|---|---|
| Perfect | 75% | Near-identical substitute — most shoppers follow the shelf swap |
| Strong | 65% | Minor flavor friction |
| Good | 50% | Different size/flavor may shift some shoppers elsewhere |
| Loose | 30% | Different format — real attrition expected |
| Type-only | 15% | Minimal substitutability — ceiling estimate |
Priority Watchlist: the top 5 pairs by (confidence × transfer $) are rendered as action cards at the top of the section. Each card shows the ❌ Delete side (competitor SKU in red) and the ✅ Replace With side (your SKU in green) side-by-side, the attribute-match strip, confidence %, and estimated transfer $ — a ready-to-paste summary for a slide.
A cluster is a set of SKUs in the same Brand with the same Type · Subtype · Form. Most supplement categories support 2-4 SKUs per cluster (different strengths, sizes, flavors). Beyond 4, new SKUs tend to cannibalize existing ones rather than growing the family.
The table shows clusters with 3+ SKUs, the strongest and weakest SKU in each, and what % of the cluster the weakest holds. Read as:
| Weakest share | Read |
|---|---|
| < 5% | Likely cannibalizing — cut is low-risk, volume transfers to siblings |
| 5-15% | Review — meaningful volume but could be concentrated into siblings |
| > 15% | Pulling weight — cut would lose real volume, not just shift it |
The table now shows YoY% and %ACV Δ pp for both the Top SKU and the Weakest SKU in each cluster. This sharpens the cut decision considerably:
The cleanest swap pitch: Top is positive on both AND Weakest is negative on both. Replace the weakest with another size/flavor of the strongest — same shelf footprint, better velocity, no shopper confusion.
| Band | SKU count | Interpretation |
|---|---|---|
| SINGLE | 1 | Minimal risk of cannibalization. Watch if the single SKU has declining velocity. |
| LEAN | 2 | Healthy for most families — one primary, one alternate (e.g., different strength). |
| STANDARD | 3-4 | Typical supplement depth — base, premium, and a flavor/size variant. |
| DEEP | 5-6 | Review — is each SKU contributing unique incremental demand? Check velocity spread. |
| BLOATED | 7+ | Red flag — working capital, shelf space, and SKU management overhead concentrated here. Look for cut candidates. |
Key check: if a DEEP or BLOATED cluster's total $ doesn't grow proportionally when SKUs are added (compare to LEAN/STANDARD clusters with similar $ per SKU), the extra SKUs are diluting, not adding.
The purest whitespace list. A gap is a Type · Subtype · Form · Base Size · Flavor combination where ≥2 competitors sell and your brand has zero SKUs. Ranked by total category $.
How to read the columns:
How this differs from Whitespace Opportunities: Whitespace includes combos where you have some presence (INVEST/EXPAND/DEFEND tags). Assortment Gaps is only the combos with zero presence — it's a subset useful for NPD planning specifically.
Best use: After identifying cut candidates, use this list to reallocate shelf space. Cutting a bloated cluster and launching a SKU that fills a gap is the ideal rationalization move — keep item count the same, shift working capital to validated demand.
A repeatable process to take outputs from this page into an actual SKU cut decision:
| Step | Action |
|---|---|
| 1. Set scope at the top | Set top period (typically LATEST 52 WKS for a full-year portfolio view) · pick one channel or retailer · confirm Category filter. Avoid mixing xAOC with individual retailers. |
| 2. Pick the brand | Select your brand in the page's Brand dropdown. If reviewing a buyer's request, pick the brand the buyer flagged. |
| 3. Read KPIs | Note the Pareto ratio. Below 25%? Severe bloat. 25-40%? Standard review. Above 40%? Be cautious with cuts. |
| 4. Quadrant scan | Count the CUT and FIX zones. CUT = remove. FIX = investigate before cutting — the distribution already exists, maybe fix pricing/placement. |
| 5. Whitespace check | Before proposing cuts, scan the Whitespace bubble chart. BUILD/INVEST combos become the "what we'll do with the freed-up shelf" story. |
| 6. Replacement watchlist | Open the Priority Watchlist cards. ⚡ QUICK WIN pairs are your lead proposals — each is a declining competitor SKU with your growing equivalent ready to replace it. |
| 7. Cut table | The table is pre-filtered to SKUs with material revenue ($1K+) or active cannibalizers, sorted by impact (cut score × log-revenue). Top entries are the highest-impact actionable cuts. SKUs with 3+ rationale chips are the cleanest cases. |
| 8. Cannibalization | For each cut candidate, find its cluster. Weakest-share <5% = confirmed cannibalizer, low cut risk. |
| 9. Expand table | For every cut, identify an EXPAND SKU to push. Pair cuts with expansions for "net neutral item count, better assortment." |
| 10. Gap table | Check top 10 gaps. Any match competitors' strongest-selling combos? Candidate for NPD to backfill the cut slot. |
| 11. Export | Download the 7 CSVs. Build your line review deck from the combined view. |
CSV exports available on this page (one button per card):
| Button | What it exports |
|---|---|
| Cut Candidates ⬇ CSV | Every candidate with UPC, item, dims, cut score, zone, financials, YoY, ACV, velocity, cannibalizer count, TDP, rationale joined with pipes. Already filtered to ACTIVE SKUs (inactive SKUs aren't candidates — they're already gone). |
| Expand Candidates ⬇ CSV | EXPAND-zone SKUs with upside-at-60%-ACV calculation. Active SKUs only. |
| Cannibalization ⬇ CSV | Exploded — one row per (cluster × rank) with cut score |
| Portfolio Depth ⬇ CSV | One row per Type-Subtype-Form family with count, $, $/SKU, median velocity, median ACV |
| Assortment Gaps ⬇ CSV | Combos with ≥2 competitors where brand has no presence |
| Whitespace Opportunities ⬇ CSV | Every combo × brand's share × top competitor × opportunity $ with action tag |
| Whitespace × xAOC ⬇ CSV | Concrete listing plays — each whitespace combo paired with the best matching xAOC SKU from your catalog (with confidence, capture rate, capture $) |
| Replacement Opportunities (in-scope) ⬇ CSV | Full swap pairs: competitor decliner (brand, UPC, item, dims, $, YoY, ACV) × your grower (UPC, item, $, YoY, ACV) × match tier, confidence, action tag, transfer rate, estimated transfer $ |
| Replacement Opportunities (xAOC) ⬇ CSV | Same swap-pair structure but where YOUR side is an xAOC SKU not yet listed in the selected channel (channel-expansion plays) |
| 💀 Inactive SKUs Audit ⬇ CSV (NEW) | Every SKU classified inactive by the four-gate ruleset. No row limit — all inactive SKUs export, same as the on-page table. Includes UPC, item, full dims, current $/Units + YA $/Units + YoY%, %ACV, TDP, the gate that triggered (A / C / D — gate B is the new-launch override which keeps a SKU active, never inactive), and the human-readable inactive reason. Sorted by $ YA descending so biggest historical contributors lead the audit list. |
PPT export: SKU Rat is included in the full dashboard PPT export. The deck includes a native KPI cover slide, an insights slide, dedicated KPI strip slides for both Replacement sections (in-scope and xAOC), every chart canvas at 3× resolution, and up to 10 tables per page (raised from 3 specifically for switching and skurat). Select "SKU Rat" in the export modal to include it.
Card-by-card breakdown of every visual on the SKU Rationalization page — what's plotted, the formulas behind each metric, and a meeting example for each.
| KPI | How it's computed | What it tells you |
|---|---|---|
| Total SKUs | COUNT(DISTINCT UPC) for the brand in scope | Total breadth — including dead/inactive UPCs |
| Active SKUs | Same but only where SUM("$") > 0 | Live portfolio |
| Avg $/SKU | brand $ ÷ active SKU count | Productivity — <$50K/SKU often signals bloat |
| Pareto 80/20 | Count of SKUs that capture 80% of $ ÷ active count × 100 | Below 25% = severe concentration; 25-40% normal; above 40% = healthy spread |
| Bottom 25% $ | SUM($) of bottom-25% ranked SKUs | Revenue at risk from a hypothetical "cut bottom quartile" rule |
| CUT zone count | Active SKUs with acv < brand-median AND vel < brand-median | Primary rationalization pile |
| EXPAND zone count | Active SKUs with acv < median AND vel >= median | Distribution opportunity SKUs |
| PROTECT zone count | Active SKUs with acv >= median AND vel >= median | Core defenders — keep stocked |
X = % ACV Reach (TDP-weighted distribution) · Y = $/$MM ACV velocity · bubble size = $ Sales · color = action zone.
Velocity formula: $ ÷ ($ACV × SKU's average %ACV ÷ 100) × 1,000,000 — dollars per million dollars of ACV reach.
Zone split: the X and Y axes are split at the brand's own median, not absolute thresholds. So a small specialty brand and Nature Made each get balanced quadrants — every brand has CUT zone candidates relative to its peers.
Hover any bubble to see SKU name + zone tag. Bubble labels are hidden when there are >20 SKUs (auto-decluttering).
X = SKUs ranked best to worst · Y = cumulative % of brand revenue.
How it's built: sort all active SKUs by $ descending, compute cumulative $ as you walk the list, divide by total brand $ × 100. Plot as a line. Mark the 80% threshold with a horizontal red dashed line.
| Curve shape | Read | Action |
|---|---|---|
| Steep early rise, long flat tail | Severe portfolio bloat — top 10-15% drives most revenue, the long tail is dead weight | Aggressive cuts justifiable |
| Gentle continuous slope | Healthy demand spread — each SKU contributes meaningfully | Cuts are riskier; investigate before pruning |
| Hockey stick — >90% from very few SKUs | Hyper-concentration — likely a few hero items | Never cut top SKUs; tail can be aggressively trimmed |
Bubble chart: X = Category $ (log scale, captures wide market-size range) · Y = brand's share of that combo (0-100%) · bubble size = opportunity $ at stake · color = action tag.
Tag thresholds (one per combo):
| Tag | Trigger | Reading |
|---|---|---|
| 🚀 BUILD | tgtD = 0 in combo | Zero presence in a market with ≥ $5K demand → launch candidate |
| 📈 INVEST | share < 20% | Under-represented but present → push distribution + spend |
| 🎯 EXPAND | share 20-50% AND ACV gap ≥ 10pp | Distribution gap vs top competitor |
| 🛡️ DEFEND | share ≥ 50% OR ACV gap < 10pp | Already strong — protect the flank |
Opportunity $ formula: Category $ in combo − your $ in combo
ACV Gap formula: top competitor's %ACV − your %ACV (in pp = percentage points)
A common problem with raw whitespace lists: a brand like Renew Life (probiotics/digestive specialist) gets shown Magnesium Gummies as a BUILD opportunity. They don't make magnesium. Surfacing those combos is noise.
The fix: a relevance toggle in the card header that filters combos to the Types/Subtypes the brand actually plays in across xAOC. Three modes:
| Mode | What's kept | When to use |
|---|---|---|
| All combos | Every combo in scope (legacy behavior) | Discovery / NPD ideation when you want to see categories the brand could enter for the first time |
| Brand Types only | Combos where the brand has meaningful xAOC presence in that Type | Looser filter — show me everything within categories the brand competes in, including new Subtypes they haven't entered yet |
| Brand Type+Subtype only DEFAULT | Combos matching both Type AND Subtype the brand sells meaningfully in xAOC | Day-to-day buyer prep — focus on opportunities for new Forms/Sizes/Flavors within the brand's exact subcategories. Tight, surgical, no irrelevant noise. |
Why xAOC anchor (not the selected channel): the selected channel may be a single retailer with a partial assortment. xAOC is the most complete picture of "what this brand actually makes." If Renew Life has a probiotic capsule somewhere in xAOC, that's their DNA — even if the selected channel doesn't carry it yet (which is precisely the opportunity).
Filter applies to: Whitespace Opportunities table + bubble chart, the Whitespace × xAOC Catalog section (since it's anchored to the same combos), AND Assortment Gaps. All three respect the toggle simultaneously so the analysis is coherent across sections.
A user-facing note below the card title shows the active filter, the count filtered out, and (in Type mode) lists the brand's xAOC Types so you can sanity-check the DNA detection.
Joins the abstract Whitespace combos to specific UPCs already in your xAOC catalog that aren't yet listed in the selected channel. Same channel-expansion narrative as the xAOC Replacement section below, but anchored to opportunity gaps rather than declining competitors.
The story: "We've already proven this UPC sells and grows across the US market. We're not asking you to take a risk on something new — just to list a SKU we already produce that's accelerating where you don't yet stock it."
Confidence is additive across five components, capped at 100:
| Component | Formula | Range |
|---|---|---|
| Tier base (combo match) | 5→60, 4→45, 3→30, 2→15 | 0–60 |
| Tag boost (combo type) | BUILD +20, INVEST +10, EXPAND 0 | 0–20 |
| SKU scale (xAOC $) | min(15, log10($) × 2.5) | 0–15 |
| Distribution proof (xAOC %ACV) | ≥10 → +5, ≥1 → +2, else 0 | 0–5 |
| Momentum (xAOC YoY) NEW | ≥25% → +15, ≥10% → +10, ≥0% → +5, new launch → +5, decline 0 to −10pts | −10 to +15 |
Why momentum matters: a SKU growing 30% YoY in xAOC is a much stronger pitch than a flat one. Both are "proven" but only one tells the buyer "this is accelerating — get on board now." Conversely, a SKU declining 5–60% in xAOC still appears (it's not crashing) but loses up to 10 confidence points to reflect the weaker narrative.
| Tag | Trigger | Reading |
|---|---|---|
| 🚀 LEAD PLAY | conf ≥ 70 AND tier ≥ 4 AND combo = BUILD/INVEST AND positive momentum (or new launch) | Proven xAOC SKU + tight match + meaningful gap + accelerating → lead the buyer story with this |
| 🎯 STRONG CASE | conf ≥ 55 | Good match, sizable opportunity |
| 🔄 CONSIDER | conf < 55 | Partial match, smaller opportunity, secondary asks |
Match tier is shown as a colored badge in both the table and watchlist. Color signals tightness of attribute match:
| Badge | Tier | Attributes Matched |
|---|---|---|
| Perfect | 5 | Type + Subtype + Form + BaseSize + Flavor (all 5) |
| Strong | 4 | Type + Subtype + Form + BaseSize (Flavor relaxed) |
| Good | 3 | Type + Subtype + Form (BaseSize + Flavor relaxed) |
| Loose | 2 | Type + Subtype only |
| Type-only | 1 | Type only (loosest) |
captureD = min(combo opportunity $, SKU's xAOC $ × tierRate × 4)
Where tierRate: 5 (Perfect) → 15%, 4 (Strong) → 12%, 3 (Good) → 10%, 2 (Loose) → 8%, 1 (Type-only) → 8%. Levels 1 and 2 share the same conservative 8% rate. The ×4 annualizes from a 13-week scope. The cap by combo opportunity $ keeps the estimate from exceeding the actual market size in that combo.
Each top-5 watchlist card shows: rank, confidence, action tag, the gap (BUILD/INVEST/EXPAND combo with combo $ and top competitor), the proposed UPC + item description with xAOC $ AND xAOC YoY% AND xAOC %ACV, the styled match tier badge, and the capture $ estimate. The full table adds a sortable xAOC YoY% column with red/green coloring (≥10% green-bold, 0–10% gray, <0% red).
How this differs from the xAOC Replacement section: Replacement starts from declining competitors; this starts from combo gaps. They overlap but answer different buyer questions. Replacement = "delete this, stock this." Whitespace × xAOC = "you don't have anything in this combo at all — start with our proven, growing SKU."
For every declining competitor SKU in your selected channels, finds your best growing SKU as a replacement candidate.
Decliner filter: $ > $10K AND −60% < YoY ≤ −5% in selected channels (the upper bound of −60% excludes already-discontinuing SKUs — no point recommending a swap for an item the competitor is already pulling). The exclusive bound matches the dashboard-wide rule: SKUs at ≤−60% YoY are treated as discontinuing.
Grower filter: target brand's SKUs with $ > 0 AND YoY > 0 in selected channels
Confidence is additive. Three components stacked, capped at 100:
Component A — Tier Base (attribute-match quality):
| Tier | What matches | Base points |
|---|---|---|
| 5 — Perfect | T + Sub + F + Sz + Flv (all 5) | 60 |
| 4 — Strong | T + Sub + F + Sz (Flavor relaxed) | 45 |
| 3 — Good | T + Sub + F (Size + Flavor relaxed) | 30 |
| 2 — Loose | T + Sub only | 15 |
| 1 — Type-only | T only | 5 |
Component B — Momentum Gap (how strongly trends diverge):
momentumGap = |decliner.YoY| + max(0, grower.YoY)
momentumPoints = min(30, momentumGap × 0.4)
Examples: decliner −18%, grower +42% → gap 60 → 60 × 0.4 = 24 pts. decliner −50%, grower +80% → gap 130 → capped at 30.
Component C — Distribution Proof (retailer acceptance signal):
| Your %ACV | Points |
|---|---|
| ≥ 10% | +10 — proven in retail |
| 1–10% | +5 — minor distribution |
| < 1% | 0 — basically untested |
Final formula: confidence = min(100, round(tierBase + momentumPoints + distProof))
Transfer rate is keyed to attribute match quality (NOT confidence). Rationale: shopper continuity depends on how close the replacement product is — even a high-confidence Loose match can't promise 75% transfer because the product is too different.
| Tier | Transfer rate | Reasoning |
|---|---|---|
| Perfect (lvl 5) | 75% | Near-identical substitute — most shoppers follow shelf swap |
| Strong (lvl 4) | 65% | Minor flavor friction |
| Good (lvl 3) | 50% | Different size or flavor — some shoppers shift elsewhere |
| Loose (lvl 2) | 30% | Different format — real attrition expected |
| Type-only (lvl 1) | 15% | Minimal substitutability — ceiling estimate |
Formula: transferD = decliner.D × transferRate
| Tag | Trigger | Read |
|---|---|---|
| ⚡ QUICK WIN | conf ≥80 AND tier ≥ 4 | Lead the buyer meeting with this — best match, strongest evidence |
| 🎯 STRONG SWAP | conf ≥60 | Defensible with ordinary prep |
| 🔄 CONSIDER | conf ≥40 | Useful if decliner is large enough to matter |
| ⚠️ MONITOR | conf <40 | Track only — too weak to propose |
Pairs are ranked by confidence × transferD — a high-confidence small-decliner pair will rank above a low-confidence big-decliner pair, capturing both certainty and impact.
Decliner: NATURE MADE Mag Glycinate Gummy 60ct Lemon @ ShopRite — $420K, YoY −18%, %ACV 42%. UPC 31604012345.
Match: NATURE'S TRUTH Mag Glycinate Gummy 60ct Lemon @ ShopRite — $85K, YoY +42%, %ACV 12%. UPC 84009350123.
Confidence math: All 5 attrs match → Perfect tier → 60 base. Momentum gap = 18 + 42 = 60 → 60 × 0.4 = 24 pts. %ACV 12% > 10 → +10. Total: 60 + 24 + 10 = 94. Tag: ⚡ QUICK WIN.
Transfer math: Perfect tier → 75% rate. $420K × 0.75 = $315K transfer.
Buyer pitch: "Nature Made's Mag Glycinate Gummy 60ct Lemon at ShopRite is bleeding — down 18% YoY. We have the exact same SKU on your shelf already, growing at 42%. Swap our facing in for theirs and you protect roughly $315K of category revenue from leaking out. We'd recommend doing this at the next category review."
Same scoring math as above but with a critical semantic difference: the matched SKU must be proven in xAOC but NOT yet listed in the selected channel. Three parallel queries drive it:
Action tags reflect the new semantic — every pair is a channel-expansion play by construction:
| Tag | Trigger | Read |
|---|---|---|
| 🚀 NEW LISTING | conf ≥75 AND tier ≥ 4 | Lead with this — buyer should add this SKU |
| 🎯 STRONG CASE | conf ≥60 | Solid case for adding |
| 🔄 CONSIDER | conf ≥40 | Worth proposing if decliner is large |
| ⚠️ WEAK | conf <40 | Track but don't propose |
ShopRite has NATURE MADE Mag Gummy declining. Nature's Truth has the same combo growing in xAOC at $540K +42% YoY but isn't listed at ShopRite. The opportunity: get on ShopRite's shelf with a proven product. Buyer pitch: "Brand X's SKU is losing velocity at ShopRite. We have a matching SKU performing nationally that you don't currently stock — let's add it as a replacement."
Top 30 SKUs by cut score, with rationale chips and zone badge. Every SKU shown has at least one weakness that triggered points.
Cut Score formula (additive, capped at 100):
| Condition | Points |
|---|---|
| Velocity below brand median | +25 |
| Velocity ≪ median (< half) | +10 more |
| ACV below median | +20 |
| ACV ≪ median (< half) | +10 more |
| YoY < −10% | +15 |
| YoY < −25% | +10 more |
| Has ≥1 cannibalizer in cluster | +10 |
| Has ≥2 cannibalizers | +5 more |
How to read score bands: <40 keep · 40-60 review · 60-80 strong cut · 80+ clear cut.
Four filter gates (all must pass):
Sort key: cutScore × log10(revenue + 10) — combines rationale strength with impact magnitude. A $50K SKU at score 70 outranks a $200 SKU at score 95 because the cut decision matters more.
SKUs in the EXPAND zone (high velocity, low ACV) with dollar upside if pushed to 60% ACV.
Upside formula: $ × (60 − current %ACV) ÷ current %ACV
Example: SKU at $100K and 20% ACV → upside = 100 × (60-20) ÷ 20 = $200K incremental. Total at 60% ACV = $300K. The formula assumes velocity holds as ACV expands (linear).
Three filter gates (all must pass):
Sort key: Upside $ descending — combines current revenue scale AND distribution gap, so a $50K SKU at 25% ACV outranks a $300 SKU at high $/$MM ACV velocity but trivial revenue. The expansion conversation is worth having when the dollars at stake matter.
Cluster definition: Brand × Type × Subtype × Form. Most supplement clusters support 2-4 SKUs (different strengths/sizes/flavors); beyond 4 typically cannibalizes.
Each row shows the strongest and weakest SKU in clusters with 3+ SKUs, plus weakest's % share of cluster $. Read as: weakest <5% = likely cannibalizing, >15% = pulling weight.
SKU count per Brand × Type × Subtype × Form family with band: SINGLE (1), LEAN (2), STANDARD (3-4), DEEP (5-6), BLOATED (7+).
Combos (Type × Subtype × Form × Base Size × Flavor) where ≥2 competitors sell but your brand has zero SKUs. Ranked by category $.
| Card | CSV | PNG | In PPT export |
|---|---|---|---|
| Quadrant | — | ✓ | ✓ |
| Pareto | — | ✓ | ✓ |
| Whitespace bubble | — | ✓ | ✓ |
| Whitespace table | ✓ | — | ✓ (capped at 10 tables/page for SKU Rat) |
| Replacement (in-scope) table | ✓ (24 cols) | — | ✓ |
| Replacement (xAOC) table | ✓ (24 cols) | — | ✓ |
| Cut Candidates | ✓ | — | ✓ |
| Expand Candidates | ✓ | — | ✓ |
| Cannibalization | ✓ | — | ✓ |
| Portfolio Depth | ✓ | — | ✓ |
| Assortment Gaps | ✓ | — | ✓ |
PPT export captures KPI cover slide, dedicated Replacement-section KPI strip slides (in-scope + xAOC), all canvases at 3× resolution, and up to 10 tables per page for SKU Rat. PDF export captures the full page as printable.
The SKU Rationalization page is decision support, not a decision. Known gaps:
| Raw CSV Header | Friendly Label | Type | Description |
|---|---|---|---|
| Source File | Source File | Text | Original data file (e.g. ALL CHANNELS.XLSX) |
| Periods | Period | Text | Time window: LATEST 4 WKS, LATEST 13 WKS, LATEST 26 WKS, LATEST 52 WKS |
| Markets | Channel / Market | Text | Retail channel or retailer (e.g. US xAOC, WALMART, WFM). Normalized on load. |
| Manufacturer | Manufacturer | Text | Parent company (e.g. 1440 FOODS COMPANY, NATROL LLC) |
| Brand | Brand | Text | Brand name (e.g. NATURE MADE, GARDEN OF LIFE, OLLY) |
| Category | Category | Text | Top-level category (e.g. VITAMIN, WT CTL NUTR, ACTIVE NUTR) |
| Benefit | Benefit | Text | Health benefit claim (e.g. IMMUNE, MUSCLE, WEIGHT, OMEGA) |
| Segment | Segment | Text | Sub-category segment |
| Type | Type | Text | Product type within segment |
| Subtype | Subtype | Text | Further type breakdown |
| Form2 | Form | Text | Physical format (e.g. CAPSULE, GUMMY, POWDER, NUTR BAR) |
| Flavor | Flavor | Text | Product flavor (e.g. TANGERINE, VANILLA, ACAI BERRY) |
| BC AGE GROUP TOTAL STORE | Age Group | Text | Target age demographic (ADULT, CHILD, SENIOR, BABY, TEEN, ALL FAMILY, NOT APPLICABLE) |
| STRENGTH | Strength | Text | Product potency/dosage (e.g. 1000 MILLIGRAM, 500 INTERNATIONAL UNIT) |
| BASE SIZE | Base Size | Text | Physical product size (e.g. 60 COUNT, 2 FLUID OUNCE) |
| UPC | UPC | Number | 12-digit Universal Product Code |
| Un-abbreviated Item Description | Item Description | Text | Full product name as it appears in the retail data system |
| Raw CSV Header | Friendly Label | Type | Description |
|---|---|---|---|
| $ | $ Sales | Decimal | Total dollar sales this period |
| $ YA | $ Year Ago | Decimal | Dollar sales same period last year |
| $ Prev | $ Previous Period | Decimal | Dollar sales previous comparable period (used for vs-Previous trend) |
| Units | Units | Decimal | Physical unit volume this period |
| Units YA | Units Year Ago | Decimal | Unit volume same period last year |
| Units Prev | Units Previous | Decimal | Unit volume previous comparable period |
| Raw CSV Header | Friendly Label | Type | Description |
|---|---|---|---|
| Any Promo $ | Promo $ Sales | Decimal | Dollars sold on any promotional condition (feature, display, TPR) |
| Any Promo $ YA | Promo $ Year Ago | Decimal | Promo dollars same period last year |
| Any Promo Units | Promo Units | Decimal | Units sold on promotion this period |
| Any Promo Units YA | Promo Units Year Ago | Decimal | Units on promotion same period last year |
| Base $ | Base $ Sales | Decimal | Modeled everyday (non-promoted) dollar sales |
| Base $ YA | Base $ Year Ago | Decimal | Base dollars same period last year |
| Base $ Prev | Base $ Previous | Decimal | Base dollars previous comparable period |
| Incr $ | Incremental $ Sales | Decimal | Dollar sales above baseline due to promotion |
| Incr $ YA | Incremental $ Year Ago | Decimal | Incremental dollars same period last year |
| Base Units | Base Units | Decimal | Modeled everyday unit volume |
| Base Units YA | Base Units Year Ago | Decimal | Base units same period last year |
| Incr Units | Incremental Units | Decimal | Extra unit volume driven by promotion |
| Incr Units YA | Incremental Units YA | Decimal | Incremental units same period last year |
| Raw CSV Header | Friendly Label | Type | Description |
|---|---|---|---|
| TDP | Total Distribution Points | Decimal | %ACV × number of items — combined distribution breadth |
| TDP Chg vs YA | TDP Change YoY | Decimal | Change in TDP vs same period last year |
| %ACV Reach | % ACV Distribution | Decimal (0–100) | % of store ACV carrying this item, weighted by store size |
| %ACV Reach YA | % ACV Distribution YA | Decimal (0–100) | %ACV same period last year |
| %ACV Reach Prev | % ACV Previous Period | Decimal (0–100) | %ACV previous comparable period |
| Base Unit Price | Avg Base Price | Decimal | Average everyday price per unit (used as weight in aggregation) |
| Base Unit Price YA | Avg Base Price YA | Decimal | Average everyday price same period last year |
| Base Unit Price Prev | Avg Base Price Previous | Decimal | Average everyday price previous period |
| $ / $MM ACV | Velocity ($ per $MM ACV) | Decimal | Dollar sales per $1M of ACV opportunity — productivity metric |
| $ / $MM ACV Chg YA | Velocity Change YoY | Decimal | Change in velocity vs same period last year |
Exports all rows matching your current filters and period, up to 500,000 rows, as a CSV file. Runs via DuckDB in the browser.
Filename: PipingRock_{Period}_{Date}.csv
Triggers the browser's print dialog with all pages set to display. Use "Save as PDF" in the dialog.
Tip: Set print scale to 75–80%, enable "Background graphics," use Landscape orientation for wider charts.
Generates a comprehensive PowerPoint presentation from the dashboard. A page selection modal lets you choose which pages to include.
Slide structure:
| # | Slide | Content |
|---|---|---|
| 1 | Title | Period, channels, active filters, date |
| 2 | KPI Summary | 8 native-text KPI cards: $ Sales, Units, Promo%, Incr $ Share, % Weighted ACV, Price, Base Share, Projected Annual $ |
| 3+ | Key Insights | All auto-generated insights as colored rows (multi-slide if 14+). Retailer comparison or benchmark mode. |
| 4 | Channel Comparison | Channel Growth chart (3× high-res capture) |
| 5–12 | Overview Charts | Top Performers, YoY Growth, Bridge, Fair Share, Share Pie, Growth Contributors (with balance text), %ACV by Channel, Retailer Rankings |
| 13 | Summary Table | Native PptxGenJS table (not screenshot) |
| 14 | Heatmap | Cross-Period YoY% Heatmap as native table |
| 15+ | Dimension Pages | Auto-discovers all canvases + tables per page (up to 3 tables) |
Technical details: Charts captured at 3× resolution with white background fill. Tables use native PptxGenJS cells (not screenshots) for crisp text. Hidden pages temporarily shown during capture then re-hidden. Navy header/footer bars on every slide.
Filename: PipingRock_Dashboard_{Period}_{Date}.pptx
Every chart card has a small ⬇ button in its header. Downloads that chart as a PNG image. Filename: dash_{chartId}_{Date}.png. Fastest way to grab a single chart for a slide or email.
Keyboard shortcuts work when focus is not inside any text input, dropdown, or overlay (except Shift+Enter and Esc-in-filter which work inside filter inputs).
| Key | Action |
|---|---|
| 1 – 0 | Switch to tab 1–10 (Overview, Channel, Benefit, Category, …) |
| / | Focus the channel search box |
| V | Toggle between $ Dollars and Units view |
| H | Open this Help overlay |
| Shift+E | Export full raw data as CSV |
| Enter (in filter box) | Select all matching values in the current filter search. Type vitamin c, press Enter → only VITAMIN C selected. Type vita, press Enter → all VITA* values selected. Uncheck any item to exclude it from future Enter-selects. |
| Shift+Enter | Apply filters (same as clicking ✓ Apply). Works even when focused inside a filter search box. |
| Esc (global) | Clear all filters / close overlay |
| Esc (in filter box) | Clear selections for that specific filter only. E.g., if focused on Subtype search and NIACIN is selected, Esc clears only the Subtype filter. |
| ? | Toggle keyboard shortcut panel (bottom-right badge) |
Filter Auto-Select Behavior:
When you type in any filter search box and press Enter, all matching values are automatically selected. For example, type vitamin c and press Enter → only VITAMIN C is selected. Type vita and press Enter → all 8 VITA* matches are selected. If you don't want one of them, uncheck it — it won't be re-selected on the next Enter press. Clearing the search box resets the exclusion tracking. Use Shift+Enter to apply all filters to the dashboard.
Bulk Paste (Custom Value List):
Paste a list of values (e.g. UPCs copied from Excel, one per line) directly into any filter search box. The dashboard auto-detects multi-line paste and selects all matching values instantly. A toast notification shows how many values matched. Works with any filter — UPC, Item, Brand, etc. This is equivalent to Tableau's "Custom Value List" feature.