# Dealer SEO Meta Descriptions

Source: [`lib/dealer-meta-description.ts`](../lib/dealer-meta-description.ts).
Consumed by the dealer homepage `generateMetadata` in
`app/sites/[subdomain]/[domainSlug]/page.tsx`.

## Why

Every dealer homepage used to emit the **same** templated meta description,
swapping only city/state/name into a fixed `"...Buy your product now! Click here
to learn more!"` string. Across ~1,100 near-duplicate dealer pages this gave
Google nothing unique to index (a driver of the "Crawled – currently not
indexed" bucket) and matched the low-value, swap-the-variables pattern the
May 2026 core update demoted.

## Strategy

`buildDealerMetaDescription(dealer)` picks one of two strategies, in order:

1. **Bio-first.** If the dealer wrote a business description
   (`dealer.description`), the snippet leads with the first ~160 plain-text
   characters of it - a genuinely unique, dealer-authored signal. The stored
   description is sanitized HTML, so `htmlToPlainText` (shared util in
   [`lib/html-to-text.ts`](../lib/html-to-text.ts), also used by the apex dealer
   directory) does a lightweight regex strip + entity decode (no DOM sanitizer in
   the metadata path). Bios shorter than `MIN_BIO_CHARS` (50) fall through to
   strategy 2.

   Entity decoding covers numeric references (decimal `&#NN;` and hex `&#xNN;`,
   so any code point) plus the structural and common typographic named entities.
   The WYSIWYG editor emits all other characters as literal Unicode, so the long
   tail of named entities (`&eacute;`, `&copy;`, `&trade;`, …) does not occur in
   stored bios and is intentionally not decoded.
2. **Locality-grounded fallback.** With no usable bio, build a truthful
   description from real data: business name + city/state + phone. Local data is
   the one axis on which otherwise-identical dealer pages legitimately differ.
   Example: `Find AMSOIL synthetic oil, filters, and lubricants through {name},
   your independent AMSOIL dealer in {city}, {state}. Call {phone} or order
   online.` The tail degrades gracefully (full CTA → phone only → none) and the
   lead word-truncates if needed, to stay within Google's ~160-char window.

   The phone token is surfaced only when it passes `isCompletePhoneNumber`
   (10 NANP digits, or 11 with a leading US/CA country code `1`); malformed or
   short values (e.g. a stray 7-digit number) fall straight to the
   `Order online for fast shipping.` tail rather than emitting raw digits.

The `<title>` is unchanged (already localized as
`AMSOIL Synthetic Oil in {city}, {state} | {name}`).

## Constraints

- **contactName privacy.** The name token routes through `getDealerDisplayName`,
  so a personal `contactName` is only used when `showContactName === true`
  (default false). `null` is treated as not-opted-in. A dealer with no
  `businessName` and the toggle off renders "an independent AMSOIL dealer..."
  never the personal name. See `docs/` / `lib/dealer-display-name.ts`.
- **No street address.** City/state only (mirrors what the `<title>` exposes);
  the street is never placed in a meta description.
- **AMSOIL referral model.** Copy says products are available *through* the
  dealer and to "order online" - never that the dealer stocks or sells
  inventory. AMSOIL prices, ships, and fulfills.

## Rollout

Descriptions are part of statically-rendered metadata, so new copy takes effect
as each dealer page is rebuilt/revalidated (on publish or on-demand
`revalidateTag`). No backfill is required.
