StatsHub Docs
Specs

3-Tier Pricing Page — Design

The design record for the three-tier pricing page. Written 2026-04-11.

Date: 2026-04-11 Status: Draft — ready for review Author: Brainstorm session with James

Context

The current pricing page at apps/statshub/src/pages/pricing.tsx offers two tiers (Pro £29.99, Pro+ £59.99). James wants to experiment with a 3-tier structure that adds an entry-level Core tier at £12.99 and repositions the middle and top tiers. The existing page stays untouched while the new page is evaluated.

This spec covers a pure visual / static page. No Stripe wiring, no subscription state, no checkout logic. Buttons are cosmetic — the goal is to see and iterate on the layout before committing to billing changes.

Goals

  • Ship a new pricing page at /pricing/3-tier that showcases 3 tiers in a feature-comparison grid as the hero element.
  • Preserve proven conversion elements from the existing page (countdown banner, hero, results chart, bottom CTA) so the two pages can be compared like-for-like.
  • Make the grid fully readable on mobile without awkward horizontal scroll.
  • Keep the new page 100% independent from the existing page so iteration on one can't break the other.

Non-Goals (explicit YAGNI)

  • No Stripe checkout, useUser() state, or "current plan" detection.
  • No A/B testing infrastructure or analytics events.
  • No annual/monthly toggle.
  • No tooltips on feature rows (type reserves the field; not rendered).
  • No "How It Works" section — the feature grid replaces it.
  • No FAQ, testimonials, or changes to the results chart beyond a possible restyle.
  • No shared-component extraction with the existing pricing page — duplicate inline.

File & Route

  • File: src/pages/pricing/3-tier.tsx
  • Route: /pricing/3-tier
  • Layout: Wraps in HeaderLayout via .getLayout (matches existing page)
  • Nav: No changes to top-nav or footer links. Page is reachable only via direct URL during the experiment.

Page Structure

Top to bottom:

  1. CountdownBanner — Early Access 40% off countdown (duplicated inline from existing page)
  2. PricingHero — Bebas headline + subtitle + chip row (duplicated inline)
  3. FeatureGridNEW — the primary content of the page
  4. ResultsChart — +213u profit chart (duplicated inline)
  5. BottomCTA — "Ready to find your edge?" bar; scroll target = FeatureGrid (duplicated inline)

"How It Works" is intentionally dropped — the feature grid already communicates what each tier includes, and having both would be redundant.

Data Shape

Two flat constants at the top of the file:

type TierId = "core" | "sharp" | "sharp_pro";

type Tier = {
  id: TierId;
  name: string;
  tagline: string;
  price: string;          // "12.99"
  originalPrice?: string; // "19.99"
  popular?: boolean;
};

type CellValue =
  | { kind: "check" }
  | { kind: "cross" }
  | { kind: "text"; text: string };

type FeatureRow = {
  label: string;
  tooltip?: string;       // reserved, not rendered yet
  values: Record<TierId, CellValue>;
};

type FeatureCategory = {
  title: string;          // "Core Scanning", etc.
  rows: FeatureRow[];
};

const TIERS: Tier[] = [...];
const CATEGORIES: FeatureCategory[] = [...];

Tiers (Draft Content)

IDNamePriceOriginalTaglinePopular
coreCore£12.99£19.99The essentials — find mispriced props fast
sharpSharp£29.99£49.99Macca's pre-built playbook, ready to bet
sharp_proSharp Pro£59.99£99.99Full control, full transparency, full access

Original prices are retained for the crossed-out "40% off" styling consistent with the existing page's early-access treatment.

Feature Grid Content (Draft — 12 rows across 3 categories)

Core Scanning

RowCoreSharpSharp Pro
AI Bet Scanner
PropHunter / Prop Screener
Lineup Alerts (Out of Pos.)
Leagues scanned daily80+150+150+
Hit rate history (L10/20/30)
Multi-book odds comparison

Macca's Models

RowCoreSharpSharp Pro
Macca's Private ModelsPreconfigured (Macca's settings)Full access + custom control
Markets AvailablePlayer markets onlyPlayer + Team markets
Model Transparency (EV, Fair Odds)
Custom Filters / Strategy Building

Community & Access

RowCoreSharpSharp Pro
Private Sharps Community Chat
Early access to new features

Desktop Layout (≥ 768px)

A 4-column grid: [Feature label] [Core] [Sharp] [Sharp Pro].

Header row (sticky on scroll):

  • Row 1: tier name + "Most Popular" badge on Sharp
  • Row 2: big price (£29 with .99 smaller) + crossed-out original + /monthly
  • Row 3: tagline
  • Row 4: CTA button

Body:

  • Category divider rows (small uppercase muted label, full-width, zinc-50/800 background)
  • Feature rows: label in col 1, cell values in cols 2–4
  • Cells:
    • ✓ → filled primary-color circle with white Check icon (same pattern as existing page lines 362–364)
    • ✗ → muted zinc-300/700 X icon
    • text → 13px zinc-700/300, left-aligned, allows wrap

Sharp (middle) column emphasis:

  • border-[2px] border-amber-400/60 dark:border-amber-500/40
  • Subtle gradient wash from-amber-50/50 via-white to-white (same as existing .popular cards)
  • Full-height column background extends through header and body
  • -translate-y-1 on the header to lift it visually

CTA buttons:

  • Rounded-full, 56px tall, matches existing button styling families
  • Core/Sharp Pro: bg-zinc-900 dark:bg-zinc-100 dark variant
  • Sharp: bg-gradient-to-r from-amber-500 to-amber-600 amber variant
  • All buttons are <Link href="/auth/signup"> — cosmetic, no Stripe

Sticky header behavior:

  • When user scrolls past the tier headers, the header row sticks to top-0 so tier names + prices + CTAs remain visible
  • Implemented with position: sticky on the header row, with a background so it occludes rows passing behind it

Mobile Layout (< 768px)

The 4-column grid is unusable on narrow screens. Collapse to a 2-column grid (Feature | SelectedTier) controlled by a segmented tier switcher.

Structure:

  1. Segmented tier switcher — 3 buttons (Core / Sharp ★ / Sharp Pro), pill-shaped, default selection = Sharp
  2. Selected tier detail card — name, price, tagline, CTA (duplicated from the switcher state)
  3. 2-column feature grid — left = feature label, right = value for the selected tier, grouped by category

Interaction:

  • Tapping a switcher button updates activeTier state; the detail card and the value column re-render
  • Switcher is sticky top-0 when scrolled so users can compare tiers without scrolling back to the top
  • Active pill has the Sharp amber treatment if Sharp is selected, otherwise zinc-900
  • No page transition / animation on switch (may add later if feels flat)

State:

const [activeTier, setActiveTier] = useState<TierId>("sharp");

Rendering strategy:

  • Dual-render desktop and mobile trees with hidden md:block / md:hidden classes
  • Avoids useMediaQuery hydration flicker
  • State for the mobile tier switcher lives in the mobile tree only

Component Breakdown

Everything lives in the single file src/pages/pricing/3-tier.tsx:

  • Top of file: TIERS, CATEGORIES constants and type defs
  • CountdownBanner — inline copy of the existing CountdownBanner (lines 103–154 of pricing.tsx). Small, self-contained, fine to duplicate.
  • PricingHero — inline, copied from the existing page's hero JSX (lines 234–275).
  • FeatureGridDesktop — new inline component. Renders the 4-column grid.
  • FeatureGridMobile — new inline component. Renders the switcher + 2-column grid. Owns activeTier state.
  • ResultsChart — inline copy of the existing results chart (lines 477–584).
  • BottomCTA — inline. Scroll target selector updated to match the new feature grid's class/id.
  • Default export: ThreeTierPricing component that composes all of the above.
  • .getLayout assigns HeaderLayout.

No extraction of shared components between old and new pricing pages. Duplication is the feature during the experimentation phase. Changes to one page must not affect the other. When a winner is chosen, a cleanup pass can DRY it up.

Styling Notes

  • All colors via existing Tailwind tokens: bg-primary, text-primary, bg-zinc-*, amber scale
  • Dark mode support on every element (follow existing page patterns)
  • No new dependencies, no new @/components/ui primitives
  • Icons: Check, X, ArrowRight, TrendingUp from lucide-react (all already used in the existing page)
  • Typography: Bebas for big headlines, default sans for body (matches existing page)
  • Spacing: max-w-5xl container matches existing page width

Accessibility

  • Tier switcher uses role="tablist" with each button as role="tab" and aria-selected
  • Feature grid rows use semantic <table> on desktop for screen-reader clarity; mobile uses a <dl> list
  • ✓ and ✗ cells include visually-hidden text (sr-only "Included" / "Not included") so screen readers don't just announce "checkmark"
  • Sticky headers have proper z-index so they don't trap focus
  • CTA buttons have descriptive labels (aria-label="Get started with Sharp plan")

Out-of-Scope / Follow-ups

Parked for later consideration:

  • Tooltip popovers on feature rows (field reserved in type)
  • Annual billing toggle with savings %
  • Animated tier-switcher transitions on mobile
  • Actual Stripe wiring once a winning design is chosen
  • Shared-component extraction between /pricing and /pricing/3-tier
  • A/B test infrastructure to route traffic between the two pages

Open Questions for Reviewer (James)

  1. Feature rows — the 12 rows above. Any to cut, add, or re-label? Especially "Leagues scanned daily — 80+ for Core" — is 80 accurate, or should Core have a different league count?
  2. Taglines — "essentials / pre-built playbook / full control" — any rewording?
  3. Original prices — I've invented £19.99 as Core's original. Should Core also have a crossed-out "Early Access 40% off" treatment, or should it feel different (no strike-through, positioned as "just £12.99 forever")?
  4. Sticky mobile tier switcher — good call or too busy / too much sticky UI?
  5. Inline duplication vs extraction — happy with the duplication-for-now approach?

Success Criteria

  • Page renders at /pricing/3-tier without errors, in light and dark mode
  • Desktop grid is visually comparable to the reference screenshot — 4 columns, middle column emphasized, prices and CTAs visible
  • Mobile switcher works: tapping Core/Sharp/Sharp Pro updates the displayed feature values
  • No regression to the existing /pricing page (since it's untouched)
  • Countdown banner, hero, results chart, and bottom CTA render correctly on both pages

References

On this page