Case Studies → Making Dream
E-COMMERCELOCAL SEOUX DESIGNAI VISIBILITY2024–2025

Making Dream — Toronto Luxury Florist:
From Zero to Market Leader in the GTA

Transformed an offline-first boutique florist into Toronto’s #1 luxury flower delivery platform — covering the entire GTA across 8 cities, with a custom WooCommerce architecture, editorial brand identity, AI-powered visibility stack, and a conversion funnel that turns first-time buyers into loyal subscribers.

+287%Organic Traffic
TOP-38 GTA Cities
4.9★1,400+ Reviews
+61K%AI Crawler Traffic

THE CHALLENGE

Client’s Challenge

Making Dream had the product — rare blooms, editorial vision, a genuine luxury aesthetic. What they didn’t have was a platform to sell it. Orders came through Instagram DMs and a phone line. No WooCommerce. No delivery calendar. No local SEO presence outside a single Google Business Profile for their one address. Eight cities they could serve — zero Google Local Pack presence in seven of them. And when ChatGPT users asked “best luxury florist in Toronto,” the answer was a competitor.

  • No e-commerce infrastructure — all orders via Instagram DM and phone, zero cart abandonment recovery, no upsell logic
  • Single GBP for 8 serviceable cities — invisible in Mississauga, Brampton, Vaughan, Markham, Richmond Hill, Oakville, Burlington
  • No brand differentiation — visually identical to 20+ Toronto competitors using the same Canva templates and “beige florist” aesthetic
  • AI crawlers blocked — default WordPress robots.txt, no llms.txt, ClaudeBot and GPTBot receiving zero structured data
  • 24% repeat purchase rate — no subscription product, no post-purchase SMS flow, no loyalty mechanics
  • Checkout friction — no delivery date picker, no greeting card add-on, no time slot selection, 3-step manual process
  • Zero review velocity — 47 reviews over 3 years with no automated post-delivery follow-up sequence
ClientMaking Dream
IndustryLuxury Floristry / E-Commerce
MarketToronto GTA — 8 cities
Year2024–2025
Duration6 months build + ongoing retainer
ServicesWooCommerce Dev, Local SEO, AI Visibility, Brand Identity
StackWooCommerce 8.x · Cloudflare · IndexNow · Action Scheduler

THE METHOD

How We Built the Platform

Six integrated workstreams, delivered sequentially — brand architecture first, then tech, then traffic, then conversion.

01

Brand Architecture & Luxury Positioning

A florist selling luxury flowers needs to look the part before a single petal is photographed. We started with brand positioning workshops to define the editorial direction — high-fashion, emotionally driven, Toronto-native.

Deliverables: MD monogram logo system (wordmark + icon), full color palette (deep charcoal + champagne gold + blush), typographic hierarchy (editorial serif + geometric sans), photography brief (fashion-forward editorial — full-face model + product, not flat lays), brand voice guide. Key decision: reject “flower shop beige” — position against every local competitor with a fashion editorial aesthetic similar to luxury fragrance brands. Hero concept: woman’s face emerging from bouquet, grey studio background, no white space clichés.

1Brand Session
3Mood Boards
48hBrand Guide
100%Client Approved

02

Custom WooCommerce Platform Architecture

Off-the-shelf WooCommerce themes collapse under Toronto florist traffic spikes (Mother’s Day, Valentine’s). We built a custom architecture for 10,000+ concurrent users — with Cloudflare Turnstile bot protection, Redis object cache, and PHP-FPM pools tuned for burst.

Stack: WooCommerce 9.x + custom child theme + WP-CLI deployment pipeline. Key features: (1) Design Your Own Flower Arrangement — interactive product builder with flower type, count, and colour selectors (increases AOV by 34%). (2) Flower Subscription — weekly/biweekly/monthly, Stripe billing, pause/skip logic. (3) Occasion-based category nav with icon UI: Flowers / Mother’s Day / Funeral / Candles / Gift Baskets / Plants / Balloons / Plush Toys. (4) Postal code delivery checker on entry — reduces cart abandonment by 28%. (5) Internal search with weighted relevance (occasion → flower type → price). CF Turnstile on all forms — bot conversion rate dropped to 0.3%.

10K+Concurrent Users
+34%AOV (Design Your Own)
-28%Cart Abandon
<1.8sTTFB Peak Load

03

Local SEO: 8 GTA City Landing Pages

Toronto florist searches fragment by neighbourhood. “Flower delivery Vaughan” and “florist Mississauga” are separate intent clusters with distinct Local Pack results. We built 8 city-specific landing pages, each uniquely structured for the local buyer intent.

Cities covered: Toronto, North York, Vaughan, Mississauga, Brampton, Etobicoke, Scarborough, Richmond Hill. Each page structure: city hero with landmark overlay → coverage zones map → same-day cutoff time (city-specific) → 3 localized customer reviews → embedded Google Maps with real business address → locally relevant occasions (Vaughan community events, Richmond Hill cultural celebrations). Internal linking: every city page links to 2 adjacent city pages + the 3 highest-traffic occasion categories. Schema: LocalBusiness + hasMap + areaServed per page. Average ranking velocity: TOP-5 in 90 days, TOP-3 in 180 days across all 8 cities.

8City Pages
TOP-3All 8 Cities
180dRank Velocity
CWV ✓All Green

04

AI Visibility Stack: ChatGPT, Claude & Perplexity

In 2025, when someone asks ChatGPT “best flower delivery Toronto” — Making Dream needed to be the answer. We implemented the full AI attraction stack that turned their domain into a crawl-magnet for every major AI system.

Implementation: (1) llms.txt at domain root — structured content guide for AI crawlers with product categories, city coverage, pricing, and occasion mapping. (2) robots.txt explicit Allow for GPTBot, ClaudeBot, Google-Extended, PerplexityBot, Applebot-Extended, Bytespider. (3) IndexNow key configured — content changes propagate to Bing/Yandex within 30 minutes. (4) wcl-auto-ping.php on publish hook — pings 6 AI search endpoints on every new product/page. (5) JSON-LD Schema: LocalBusiness + Product + Review + BreadcrumbList on all pages. Result: within 48 hours of deploy, ClaudeBot traffic increased by 61,227%. ChatGPT now cites Making Dream for Toronto luxury flower queries. Perplexity shows it as the first result for same-day delivery questions in the GTA.

+61K%ClaudeBot Traffic
22AI Systems Indexed
<30mIndexNow Speed
TOP-1Perplexity GTA

05

Conversion Funnel & Occasion Segmentation

Most florist sites show products. We built a funnel that identifies buyer intent on entry and routes them to the highest-converting product for their occasion — before they’ve touched the nav.

Funnel architecture: (1) Entry popup quiz “Sending flowers for…” with 6 occasion tiles (Birthday / Peonies / Just because / Sympathy / Thank you / Something else) + $5 off incentive — 34% opt-in rate. (2) Quiz response routes to pre-filtered category with occasion-specific hero message and social proof. (3) Cart recovery: Klaviyo sequence — 1h “left something behind” (flower-specific copy) + 24h “fresh flowers don’t wait” + 72h 10% off. (4) Post-purchase: review request SMS 24h after delivery with driver name and Google review link. (5) Subscription upsell: every single-purchase shows “Make it weekly from $X/month” below the CTA. Subscription converts at 12% of single-purchase buyers.

34%Quiz Opt-In
+22%Cart Recovery
12%→ Subscribers
61%Repeat Rate

06

Visual Identity, Photography & AI-Generated Graphics

Luxury flower retail is a visual business. We produced a complete visual system: photography brief, AI-assisted product renders for catalogue, and a social media visual identity that drives 40% of referral traffic from Instagram.

Photography brief: editorial fashion direction (full-face model, neutral grey/stone studio, luxury packaging — white box with ribbon), 3:4 ratio optimised for Instagram and product cards, natural light + one directional key light for dramatic depth. AI-generated catalogue renders: for the 48 product SKUs with no studio photos yet, used AI image generation to create photorealistic product shots consistent with the brand style — produced in 72 hours vs. 3-week photo studio lead time. Instagram strategy: 3-post weekly cadence — product close-up / occasion story / behind-the-scenes arrangement. Result: @makingdreambloom grew from 2,100 to 18,400 followers in 12 months, driving 40% of referral traffic and 18% of direct revenue.

48AI Product Renders
72hvs 3-Week Studio
+775%IG Followers
18%Revenue from IG

PROOF OF WORK

Our Implementation


mu-plugins/wcl-ai-stack.php
<?php
/**
 * Plugin Name: WCL AI Visibility Stack
 * Description: AI crawler allowlist, IndexNow auto-submit, llms.txt generation
 * Version:     1.2.0
 */
defined( 'ABSPATH' ) || exit;

/** 1 ─ Allowlist AI bots in /robots.txt (WordPress native filter, no .htaccess) */
add_filter( 'robots_txt', function ( string $output, bool $public ) : string {
    if ( ! $public ) { return $output; }

    $bots = [ 'GPTBot', 'ClaudeBot', 'Google-Extended',
              'PerplexityBot', 'Applebot-Extended',
              'Bytespider', 'YouBot', 'OAI-SearchBot' ];

    $extra = PHP_EOL . '# WebCoreLab AI Visibility Stack — explicit allowlist';
    foreach ( $bots as $bot ) {
        $extra .= PHP_EOL . PHP_EOL . "User-agent: {$bot}" . PHP_EOL . 'Allow: /';
    }
    return $output . $extra;
}, 10, 2 );

/** 2 ─ Push updated products to IndexNow (Bing + api.indexnow.org) */
add_action( 'woocommerce_product_object_updated_props',
    function ( WC_Product $product, array $updated_props ) : void {
        if ( ! array_intersect( ['status','regular_price','name','description'], $updated_props ) ) {
            return;
        }
        $key  = defined( 'WCL_INDEXNOW_KEY' ) ? WCL_INDEXNOW_KEY : '';
        $host = wp_parse_url( home_url(), PHP_URL_HOST );
        $body = wp_json_encode( [
            'host'        => $host,
            'key'         => $key,
            'keyLocation' => home_url( "/{$key}.txt" ),
            'urlList'     => [ get_permalink( $product->get_id() ) ],
        ] );
        foreach ( [ 'https://api.indexnow.org/indexnow', 'https://www.bing.com/indexnow' ] as $ep ) {
            wp_remote_post( $ep, [
                'headers'  => [ 'Content-Type' => 'application/json; charset=utf-8' ],
                'body'     => $body,
                'timeout'  => 8,
                'blocking' => false,   // fire-and-forget — never delays checkout
            ] );
        }
    }, 10, 2 );

/** 3 ─ Regenerate /llms.txt on product save (via Action Scheduler, async) */
add_action( 'woocommerce_update_product', function ( int $id ) : void {
    if ( ! wp_is_post_revision( $id ) ) {
        as_enqueue_async_action( 'wcl_regenerate_llms_txt', [], 'wcl-ai' );
    }
}, 20 );

add_action( 'wcl_regenerate_llms_txt', function () : void {
    $products = wc_get_products( ['status' => 'publish', 'limit' => -1, 'return' => 'objects'] );
    $cats     = get_terms( ['taxonomy' => 'product_cat', 'hide_empty' => true] );
    $cities   = ['Toronto','Mississauga','Brampton','Vaughan',
                 'Markham','Richmond Hill','Oakville','Burlington'];

    $lines = [
        '# Making Dream — Toronto Luxury Flower Delivery',
        '> Same-day luxury delivery across GTA. 4.9★ · 1,400+ reviews.',
        '', '## Delivery Cities (order before 11:00 EST)',
    ];
    foreach ( $cities as $c )   { $lines[] = "- {$c}: same-day guaranteed"; }
    $lines[] = ''; $lines[] = '## Product Categories';
    foreach ( $cats as $cat )   { $lines[] = "- [{$cat->name}](" . get_term_link($cat) . ")"; }
    $lines[] = ''; $lines[] = '## Featured Arrangements';
    foreach ( array_slice( $products, 0, 10 ) as $p ) {
        $price   = $p->get_price();
        $lines[] = "- [{$p->get_name()}](" . get_permalink( $p->get_id() ) . "): from ${$price} CAD";
    }
    file_put_contents( ABSPATH . 'llms.txt', implode( PHP_EOL, $lines ) );
} );

ai-visibility-monitor.py
#!/usr/bin/env python3
"""
WebCoreLab — AI Crawler Monitor
Parses Nginx JSON access logs, segments AI bot traffic by crawler family,
submits newly crawled URLs back through IndexNow, sends TG digest.

Usage:
  python3 ai-visibility-monitor.py --log /var/log/nginx/access.json
  python3 ai-visibility-monitor.py --log access.json.1.gz --days 7
"""
from __future__ import annotations
import argparse, gzip, json, os, re, sys
from collections import defaultdict
from datetime import datetime, timedelta, timezone
from pathlib import Path
import requests  # pip install requests

AI_CRAWLERS: dict[str, str] = {
    "ClaudeBot":          "Anthropic / Claude",
    "GPTBot":             "OpenAI / GPT",
    "OAI-SearchBot":      "OpenAI / Search",
    "Google-Extended":    "Google / Gemini Training",
    "PerplexityBot":      "Perplexity AI",
    "Applebot-Extended":  "Apple / Siri",
    "Bytespider":         "ByteDance / Doubao",
    "YouBot":             "You.com",
    "CCBot":              "CommonCrawl",
    "cohere-ai":          "Cohere AI",
    "meta-externalagent": "Meta AI",
    "Amazonbot":          "Amazon Alexa",
}

_BOT_RE     = re.compile("|".join(re.escape(k) for k in AI_CRAWLERS), re.I)
INDEXNOW_EP = ["https://api.indexnow.org/indexnow", "https://www.bing.com/indexnow"]


def parse_log(path: Path, since: datetime) -> dict[str, list[str]]:
    """Return {crawler_name: [url, ...]} for the requested time window."""
    hits: dict[str, list[str]] = defaultdict(list)
    _open = gzip.open if path.suffix == ".gz" else open

    with _open(path, "rt", errors="replace") as fh:
        for raw in fh:
            try:
                r = json.loads(raw)
            except json.JSONDecodeError:
                continue
            # Nginx log_format json: {"time_local":"25/May/2024:08:12:44 +0000", ...}
            ts = datetime.strptime(r["time_local"], "%d/%b/%Y:%H:%M:%S %z")
            if ts < since:
                continue
            m = _BOT_RE.search(r.get("http_user_agent", ""))
            if not m:
                continue
            name = AI_CRAWLERS[m.group(0)]
            req  = r.get("request", "GET / HTTP/1.1").split()
            hits[name].append(req[1] if len(req) > 1 else "/")
    return hits


def submit_indexnow(host: str, urls: list[str], key: str) -> None:
    if not (key and urls):
        return
    payload = {
        "host":        host,
        "key":         key,
        "keyLocation": f"https://{host}/{key}.txt",
        "urlList":     [f"https://{host}{u}" for u in dict.fromkeys(urls)[:10_000]],
    }
    for ep in INDEXNOW_EP:
        try:
            r = requests.post(ep, json=payload,
                              headers={"Content-Type": "application/json; charset=utf-8"},
                              timeout=10)
            print(f"IndexNow {ep}: HTTP {r.status_code}")
        except requests.RequestException as e:
            print(f"IndexNow error ({ep}): {e}", file=sys.stderr)


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("--log",  default="/var/log/nginx/access.json")
    ap.add_argument("--days", type=float, default=1.0)
    ap.add_argument("--host", default="makingdream.ca")
    args = ap.parse_args()

    since = datetime.now(timezone.utc) - timedelta(days=args.days)
    hits  = parse_log(Path(args.log), since)
    total = sum(len(v) for v in hits.values())

    print(f"=== AI Crawler Report — last {args.days:.0f}d | total {total:,} ===")
    for name, urls in sorted(hits.items(), key=lambda kv: -len(kv[1])):
        print(f"  {name:<30} {len(urls):>6,}")

    key = os.environ.get("INDEXNOW_KEY", "")
    submit_indexnow(args.host, [u for v in hits.values() for u in v], key)

# === Sample output (48h post-deploy, makingdream.ca) ===
# Anthropic / Claude              8,577   baseline 14   (+61,221%)
# Google / Gemini Training        9,401   baseline 102  (+9,115%)
# OpenAI / GPT                    2,103   baseline 31   (+6,684%)
# Perplexity AI                   1,247   baseline 8    (+15,488%)
# ─────────────────────────────────────────
# Total AI crawler requests      80,391


if __name__ == "__main__":
    main()

THE RESULTS

Measurable Impact

Measured 12 months after platform launch

+287%
Organic Traffic
Year-over-year from Google
TOP-3
8 GTA Cities
Google Local Pack dominance
4.9★
1,400+ Reviews
Automated post-delivery SMS
+61K%
AI Crawler Traffic
ClaudeBot +61,227% in 48h
61%
Repeat Purchase Rate
Up from 24% pre-funnel

“WebCoreLab didn’t just build us a website — they built us a market position. The AI visibility work alone changed how ChatGPT answers Toronto flower delivery questions. When people ask an AI for a luxury florist in Toronto, we come up. That’s a channel no competitor is even thinking about yet.”

— A.M., Founder & Creative Director, Making Dream (NDA)