The 20-Minute Market — Opportunity Tiers
One sentence: for every Irish business category in every town, fcr_operations.v_opportunity_tier
answers “is there already an established website competing inside this business’s real service radius?”
— and therefore which rung of the product ladder (BizSite → SitePro / LocalRank, with SayMore as the
moat-builder) is the honest recommendation.
All boundaries signed off 2026-06-11/12 (Cathal Dempsey). Numbers on this page are as of 2026-06-12; re-derive from the view rather than quoting this page for anything load-bearing.
Working with this in Claude Code: start with this page and
scripts/opportunity-tier/README.md(the rebuild runbook). The full research record (plan, architecture, report, 16-slide deck, catchment viewer) lives in Cathal’sPresent/folder — ask for the pack. Project owner: Cathal Dempsey.
The consumption surface
Section titled “The consumption surface”SELECT * FROM `listingmanager-1529856313699.fcr_operations.v_opportunity_tier`WHERE canonical_key = 'plumber' AND locality = 'Clonmel'One row per (locality, county, canonical_key), served at the category’s class minutes. Key columns:
| Column | Meaning |
|---|---|
bucket | THIN / CONTESTED / SATURATED — the competitive verdict |
market_class, pack_minutes | destination (20-min packs) or SAB service-area trade (30-min) |
pack_n, pack_site25, pack_sitecomp, pack_dominator_reviews | the pack evidence behind the verdict |
home_n, home_no_site | businesses (and no-website businesses) based in this locality cell |
confident | pack ≥ 5 — false rows must not drive automated recommendations |
wide_radius | county-wide callout trades (drain_clearing, building_contractor) — verify THIN against rank data before pitching |
desert_candidate | THIN + under 10K population at class minutes — no prize, excluded from BizSite lists |
population, households, pp_weighted, pobal_weighted | CSO context (purchasing power is ownership-weighted — NOT income) |
hh_per_competitor, market_depth | the prize grade: SHALLOW <1,000 / MID 1,000–3,000 / DEEP >3,000 households per pack member |
Conventions every consumer MUST honour
Section titled “Conventions every consumer MUST honour”- Never filter
minutes = 20— that silently drops every service-area trade. Filtermarket_classif needed. - A missing row means ultra-THIN (market too small to measure), not “no data”.
- Respect
confident = false— under 5 pack members is noise, not a verdict. - Depth never moves the bucket — competition decides the verdict; depth grades the prize and orders the work.
- Exclude bucket classes (
market_class = 'bucket': wholesale / manufacturing / nonmarket / other_*) andschoolfrom any opportunity count or targeting list.
The signed-off boundaries (and their evidence)
Section titled “The signed-off boundaries (and their evidence)”| Boundary | Value | Evidence | Signed off |
|---|---|---|---|
| Market width (destination) | 20-minute drive-time catchment | With/without-website outcome separation sharpest at 20 min (0.97× THIN vs 0.67× CONTESTED), gone at 45; 89% of visible top-3 rankers inside 20 min | 2026-06-11 |
| Market width (SAB trades) | 30 minutes | Observed ranks: SAB visible pack 75% in 20 min, 87% in 30 (9,094-search recovered scan). The decision holds, but the stated multiple was wrong: trades’ competitors reach ~1.4× further, not “~2× further” — re-measured 2026-07-13 on 1.68M ranked positions at a fixed lens (emergency_sab 9.4 km / scheduled_trades 9.1 km vs retail_errand 6.8 km). 30/20 = 1.5× time ≈ the measured 1.38× distance, so 30 minutes stands | 2026-06-12 · corrected 2026-07-13 |
| Buckets | THIN = no established site (site + 25+ reviews) in pack; SATURATED = 3+ complete site-holders + 100+-review dominator; else CONTESTED | Step-2 outcome validation | 2026-06-11 |
| Desert floor | THIN + population < 10K at class minutes | Removes ~410 of ~16.8K THIN (no-customers cases) | 2026-06-11 |
| Depth bands | SHALLOW <1,000 / MID 1,000–3,000 / DEEP >3,000 households per pack member | Rounded quartiles of the observed distribution (p25≈1,000 / p75≈3,300); re-derive with APPROX_QUANTILES(households/pack_n, 100) | 2026-06-12 |
The tables
Section titled “The tables”Table (all in fcr_operations) | What it is |
|---|---|
drive_time_catchments | 1,601 calibrated drive-time polygons (Valhalla/OSM, calibrated vs 30 timed Google journeys; GEOGRAPHY) |
category_bridge | all 4,479 raw category labels → 179 canonical markets (100% coverage). The supplies trap rule: trade-supplies sellers never map into the consumer trade |
canonical_key_class | per-market class (destination/SAB/bucket), pack minutes, wide_radius flags |
opportunity_tier_lookup | verdicts at every grain (20/30/45-min rows) — analysis only; consumers read the view |
drive_time_catchment_demographics | CSO Census population / households / purchasing power per catchment |
serp_grid_results | 109K observed ranking rows (May 2026 scans, competitor place_ids + premises GPS) — the reach evidence base |
v_opportunity_tier | the one consumption surface — everything downstream reads this |
Headline numbers (as of 2026-06-12)
Section titled “Headline numbers (as of 2026-06-12)”- Universe 228,832 → 226,856 with category → 187,835 in the analysis (39,021 deliberately excluded: 14,239 nonmarket, 10,989 other_*, 11,409 wholesale+manufacturing, 2,384 state schools).
- No-website businesses: 96,482 total → 75,826 in confident measurable markets (+6,543 in pack<5 micro-markets, +14,113 with no measurable home cell — a geocoding gap that improves over time).
- Opportunity (confident, non-bucket, non-school): BizSite/THIN 16,348 · LocalRank/CONTESTED 49,281 · SATURATED 9,787 · desert 410.
- Depth grid of the THIN pool: DEEP 2,436 (the premium list) · MID 8,302 · SHALLOW 6,013.
- Evidence on rankings (75K observed positions): 88% of the visible top-3 link a website; site-holders reach top-3 at 1.5× within the same search; ⚠ the “at home the website is a switch / reviews are the dial” MECHANISM IS CONTRADICTED — see Revalidation before quoting it; at home the website is a switch (15%→23–25% top-3, flat across review bands), away from home reviews are the dial (5.4%→9.1%) but proximity still wins (no-site local 15.1% beats remote site+100-reviews 9.1%).
Refresh cadence (quarterly)
Section titled “Refresh cadence (quarterly)”Runbook with commands: scripts/opportunity-tier/README.md. In brief: re-run the calibration journeys (cents),
re-run the with/without outcome measurement (cells grow past today’s 14–22), re-run the rank scan (~$25, diffs
against the May base in serp_grid_results — always store full results, never rely on InSites’ copy: LRC
reports return no competitor place_ids), extend the bridge for new labels (agent-batch pattern), then rebuild
the lookup CTAS. The KEYWORD_INTELLIGENCE city/county backfill survives until the manual KI pipeline re-runs.
Known limitations & open items
Section titled “Known limitations & open items”- Three mis-geocoded localities excluded, pending human review: Crumlin/Antrim, Dundrum/Down, Kilnamanagh/Wexford (seeds landed on their Dublin namesakes).
- Outcome-evidence cells are small (14–22 clients); directionally strong, re-measured quarterly.
- Depth uses households as a demand proxy; the planned upgrade is the demand overlay (category search volume per area). Organic-reach domain-matching from the recovered scans is also unbuilt.
- Engine wiring (
worker/src/lib/opportunity-rules.js→ the view) is the next build step; it awaits sales/product input on the per-class product benchmarks. - Counts are headcounts, not euros.
Related pages
Section titled “Related pages”- Early-Stage Opportunity Scan — Spec & Competitive-Fit Model — the deal-time consumer this lookup was built for.
- CSO & Area Data Sources — the census inputs.
- Prospect DB — Composition — the business spine.
Revalidation 2026-07-13
Section titled “Revalidation 2026-07-13”Re-measured against the 20-Minute-Market reach sweep: 1,676,469 ranked positions / 84,994 searches /
94,446 businesses / 120 categories × 694 localities, fired at the InSites-matched google_maps z13 lens
(fcr_operations.serp_reach_observations_20260712). The original evidence came from a 9,094-search scan at a
tighter zoom — that single fact explains most of what follows.
✅ What survives
Section titled “✅ What survives”- The 20-minute market boundary. Its evidence is outcome-based and therefore lens-free (no-website businesses earn 0.97× of site-holders’ Google actions in THIN markets vs 0.67× in CONTESTED; separation sharpest at 20 min, gone at 45). A search viewport cannot fabricate a client-outcome difference. This is the strongest evidence in the model and the sweep cannot overturn it.
- The 30-minute SAB trades split. Trades genuinely reach further; only the stated multiple was wrong (below).
- The website advantage (~1.5×). It survives — but not for the reason the deck gives (below).
❌ What was wrong
Section titled “❌ What was wrong”1. “Trades’ competitors reach ~2× further.” Measured at a fixed lens: ~1.4×. The 30-minute decision it justifies is sound; the multiple is not. Corrected in the boundaries table above.
2. “The visible top-3 sits at median 2 km” — a LENS reading, not a market fact. The zoom pilot (plumber × 3 seeds × z12/13/14/15) shows the median distance to a top-3 ranker halves with every zoom step:
| seed | z12 | z13 (InSites) | z14 | z15 |
|---|---|---|---|---|
| city | 15.3 km | 8.9 km | 4.5 km | 1.8 km |
| town | 16.1 km | 8.4 km | 4.4 km | 6.5 km |
| rural | 13.7 km | 8.6 km | 6.9 km | 11.6 km |
The deck’s 2 km is a z15 reading; the sweep’s 7.8 km is a z13 reading. They do not contradict — both are instrument readings.
LENS RULE — any distance drawn from a zoom-based search grid measures the viewport, not the market. Never quote a km radius from grid SERP data. Only two things survive: comparisons at a fixed lens (category vs category, town vs town), and outcome-based evidence. Prefer the InSites-matched lens (z13) — not because it is “true”, but because it is what the client sees in their own report.
3. The mechanism behind the website advantage is contradicted. Top-3 rate by review band on the identifiable population:
| Reviews | No website | With website |
|---|---|---|
| 0 | 8.4% | 8.5% |
| 1–24 | 17.8% | 16.2% |
| 25–99 | 21.7% | 23.0% |
| 100+ | 22.1% | 25.1% |
There is no website advantage below 25 reviews. Ranking tracks reviews. This is corroborated by Google’s own ranking documentation, which names prominence as “based on info like how many websites link to your business and how many reviews you have… more reviews and positive ratings can help your business’s local ranking” — reviews are a named factor; owning a website is not.
So the slide-13 story (“at home the website is the SWITCH; reviews are the DIAL”) is backwards, and the sales script “reviews will not win you the local pack — the website will” is withdrawn pending re-measure.
🔍 Why the ~1.5× still holds — and what it really measures
Section titled “🔍 Why the ~1.5× still holds — and what it really measures”Measuring the website effect only on businesses we can observe (those with a place_id) gives a feeble
1.15×. But that silently deletes the effect, because it drops the businesses missing from Google entirely —
who are disproportionately the no-website ones. A 500-listing probe (searching unmatched goldenpages listings by
name at their own coordinates; scripts/saymore-ads-reach/probe_lst_google_presence.py):
| Has a Google place | |
|---|---|
| With a website | 199/250 — 80% |
| Without a website | 149/250 — 60% |
40% of no-website businesses have NO Google place at all — they can never rank. Weighting each arm by its probability of having a place restores ~1.45×, against the claimed 1.51×. Within noise.
The website’s real predictive power is that the business is on Google at all. Website and Google Business Profile travel together as a digital-presence bundle; the website is not ranking you, and reviews are.
💰 The commercial consequence
Section titled “💰 The commercial consequence”For 40% of no-website prospects the honest opener is not “your competitors outrank you” — it is “you are not on the map at all.” Concrete, verifiable per prospect, and it points at presence management before a website.
Caveats on the revalidation itself
Section titled “Caveats on the revalidation itself”- The sweep covers 120 of the 178 canonical categories (694 localities — the same national spread).
- The Google-presence probe is n=500 (250/arm) → roughly ±6 points at 95%. Enough to say 1.5× stands; not enough to publish 1.45× as a replacement number.
- The review-band table pools home and away searches; slide 13 separates them. A like-for-like replication is
blocked because
PROSPECT_LISTINGScarries no lat/lng, so per-point distance cannot be computed for the non-appearing population. The mechanism is contradicted, but not yet re-measured on the exact construct. - SQL trap that hid this for one run:
website LIKE 'http%'is NULL, not FALSE, whenwebsite IS NULL, soCOUNTIF(NOT has_site)silently returns 0. Wrap inIFNULL(..., FALSE).