Decline Diagnosis (Executive Summary & Store Diagnosis)
Each same-store is classified by what happened to its revenue YoY:
Optical (mix shift) — core flake (Concrete Coating) revenue fell, but total revenue held or grew because rubber (RevaFlex) backfilled it. These look like declines on the flake-only view but are managed product transitions, not real losses.
Real decline — total revenue is down YoY. Attributed to a primary driver, evaluated in order: Cannibalization (a new store overlaps ≥3 shared ZIPs whose revenue rate fell ≥10% since it opened, the overlap is material — ≥$50K prior revenue — and that decline covers ≥25% of the store's total decline) → Pricing/ticket (avg-ticket effect outweighs volume) → Demand (leads down >3%) → Demo rate (demos÷leads down >3pp) → Close rate (sold÷demos down >2pp, maturity-controlled: matured cohorts with a 90-day conversion window, so still-converting recent leads don't read as compression) → Volume (residual).
$ decomposition: ΔTotal = ΔFlake + ΔRubber + ΔCommercial (product bridge) and ΔTotal = (Δdeals × prior avg ticket) + remainder (volume vs price). Funnel metrics (leads, demo rate, close rate) are directional.
Funnel: Leads → Demos → Sold (close rate)
From the Master Opportunity Report, deduped by Opportunity ID and bucketed by the opportunity's created month (a cohort view — leads, demos, and sales for opportunities created in the window):
Lead = any opportunity (junk statuses excluded). Demo = the opportunity has a Demo Date (matches the Sales dashboard; REHASH counts). Sold = Lead Status ∈ {JOB SOLD, JOB IN PROGRESS, COMPLETED}.
Demo Rate = Demos ÷ Leads · Close Rate = Sold ÷ Demos · (Lead→Sale = Sold ÷ Leads = Demo Rate × Close Rate).
Company figures are computed from count sums (ΣSold ÷ ΣDemos), not by averaging store rates. Note: a small number of sales lack a recorded demo date, so a store's close rate can occasionally exceed 100%. This matches the Sales Performance dashboard's definition (Close Rate = Sold ÷ Demos); the prior SSS metric divided by leads, which blended demo-setting and closing.
Same-Store Qualification
A class (location) qualifies as a same-store when its first recorded sale predates the start of the prior TTM period, ensuring a full 24+ months of revenue history for comparison. Classes whose first sale falls within the current or prior TTM window are classified as new stores and excluded from same-store metrics.
Reactivated stores are classes that meet same-store tenure requirements but have $0 revenue in the prior TTM. They are flagged separately and excluded from YoY calculations and underperformer lists to avoid divide-by-zero distortion.
Time Periods
TTM (Trailing Twelve Months): the 12 complete months ending with the last completed calendar month — the in-progress current month is excluded so the TTM and prior windows are both full and directly comparable.
Prior TTM: the 12-month window immediately preceding the TTM, used as the comparison baseline.
All comparisons are TTM-only. No calendar-year or fiscal-year metrics are used.
YoY Growth Calculation
Formula: ((TTM Revenue / Prior TTM Revenue) - 1) × 100
Applied at every level: overall store, per product type, per ZIP code, and for Flake (Concrete Coating). A store with $0 prior revenue returns null (displayed as "---") rather than infinity.
Growth target: 5.0% YoY. Stores at or above 5.0% are "meeting target." Stores below 5.0% are "underperforming."
Revenue Source
All revenue is sourced from the Master Contract Report exported from BuilderPrime CRM. Only contracts flagged as "Is Sold And Current" = true are included. Revenue is the Grand Total field (includes tax). Each contract is counted once (deduplicated by Contract Number). The sold date is parsed from Sold Date Time.
Product Type Classification
Every contract's Project Type field is bucketed into three categories:
Rubber Coating — exact match on "Rubber Coating" only.
Commercial — Project Type contains any of: "commercial", "warehouse", "industrial" (case-insensitive).
Concrete Coating — everything else (Garage Floor, Basement Floor, Patio, Pool Deck, Driveway, Sidewalk, Cool Deck, Stone Coating, Polished Concrete, Clean and Seal, Residential, Multiple Areas, etc.).
Flake Definition
Flake = the Concrete Coating product (the flake-broadcast system) — exactly as defined in the Product Type classification above and used in the Product × Application lens. Rubber Coating and Commercial revenue are excluded. One consistent definition is used everywhere flake is reported (Executive Summary, Store Diagnosis, map, scorecard).
(Previously this was a date-aware "Traditional Coatings" metric that counted all pre-first-rubber revenue as traditional. That boundary was ambiguous under the Product × Application lens and has been replaced by this clean product definition.)
Why this matters for PE diligence: If a store shows +16% overall growth but −5% Flake growth, the overall number is being lifted by the rubber (RevaFlex) product line. The Executive Summary and Store Diagnosis separate this optical flake decline (total held by rubber) from real decline (total revenue down).
Growth Decomposition
Revenue growth is decomposed into two drivers on the scorecard:
Volume (Deal Δ): YoY change in deal count.
Ticket Size (Ticket Δ): YoY change in average deal value. Volume growth is generally healthier than ticket growth.
Weak ZIP Identification
For each same-store, individual ZIP codes are analyzed for YoY growth. ZIPs with YoY growth below 5% are flagged as weak ZIPs. Up to 25 weak ZIPs are retained per store, sorted by absolute dollar decline; they feed the cannibalization analysis and the store drill-down.
Excluded Classes
Ninja - Atlanta: defunct store. Unclassified, Admin Alerts: system-generated classes.
Data Pipeline
Source: BuilderPrime CRM → Master Contract Report (daily export)
Processing: build_sss_data.py reads the CSV, deduplicates contracts, computes all metrics, geocodes locations, and outputs a JSON file.
Rendering: inject_data.py embeds the JSON into this self-contained HTML template.
Deployment: Netlify CLI deploys the HTML daily at 6:30 AM CT.
Refresh frequency: Daily.