Same-Store Sales Performance

vs  ·  Generated
Executive Summary
Surface Mix
Store Diagnosis
Appendix
Methodology
The core story
    Revenue Blend — product mix (same-store)
    Product TypeTTM RevenuePrior TTM YoY %YoY $TTM SharePrior ShareShare Shift
    TTM Revenue Trend — All Same Stores (vs Prior)
    Flake
    Rubber
    Other
    Prior LTM
    The Decline Question, Answered
    Why the real decliners are down
    Priority actions
    Store Diagnosis
    Show:
    Store Classification Total YoY % Total YoY $ Flake YoY $ Rubber YoY $ Primary driver Leads YoY % Close Δ (pp) Ticket YoY %
    Click a store for its full diagnosis. Optical = flake down but total revenue held by rubber (managed transition, not a real loss). Real = total revenue down YoY.  ·  Total YoY $ = Flake $ + Rubber $ + Commercial (Commercial ≈1% of revenue, the small residual; hover Total $ for the full split).
    Select a store above to see its YoY bridge, funnel, and root-cause breakdown.
    Same-Store Scorecard
    Table:
    Store Brand TTM Revenue Prior TTM YoY % YoY $ vs 5% Target Deals Deal Δ Leads Lead Δ Demo % Close % Avg Ticket Ticket Δ New ZIPs Lost ZIPs
    Monthly Drill-Down by Brand / Store
    Flake
    Rubber
    Commercial
    Prior TTM
    Performance by Brand
    Store Brand Opened TTM Revenue Flake Rev Rubber Rev Deals Avg Ticket Active ZIPs
    Surface mix — the core message
      Product × Application Matrix
      Stores: Market:
      Each cell shows LTM revenue and YoY %, colored by YoY direction (green ≥ target, yellow 0–target, red declining). Overall = the surface's total; Flake = Concrete Coating. In the all-stores / all-same-stores views, surfaces under $2M LTM are grouped as Other; selecting a specific market lists every surface.
      Flake → Rubber Substitution by Application
      Each application's revenue mix — Flake (Concrete Coating) vs Rubber (RevaFlex) — in the prior period vs the current LTM. A shrinking flake segment alongside a growing rubber segment is the substitution. Tagged flake→rubber when flake is down YoY but the application's total is up.
      Flake (Concrete Coating)
      Rubber (RevaFlex)
      Commercial
      Application Prior mix LTM mix
      Definitions & Methodology
      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 declinetotal 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.