# TRACKING & ANALYTICS

All events are pushed to `window.dataLayer` by `cwc-page.js` / `cwc-header.js`
via a single delegated listener on `[data-cwc-event]`, so adding a new tracked
CTA needs no JS - just the two data attributes.

```html
<a href="tel:08009549679" data-cwc-event="phone_click" data-cwc-location="hero_secondary">Call</a>
```

## 1. dataLayer event definitions

| Event | Fires when | Parameters |
|---|---|---|
| `page_view` (GTM built-in) | page load | `page_type`, `service`, `page_variant` |
| `cta_click` | any quote/CTA button | `cta_location`, `cta_text`, `page_variant` |
| `phone_click` | any `tel:` link | `cta_location`, `cta_text` |
| `email_click` | any `mailto:` link | `cta_location` |
| `callback_open` | sticky-bar "Request a callback" | `cta_location` |
| `form_start` | first keystroke in quote form | `form_id` |
| `form_submit` | valid submit | `form_id`, `page_variant` |
| `form_error` | invalid submit | `form_id`, `error_count` |
| `faq_open` | FAQ accordion opened | `faq_question` |
| `video_open` / `video_play` | process video requested | `video_title` |
| `postcode_search` | local-team search submitted | `cta_location` |
| `newsletter_submit` | footer mailing list | - |
| `find_local_open` | local-team panel opened | - |
| `nav_toggle` | mobile menu opened/closed | `nav_state` |
| `scroll_depth` | 25/50/75/100 % reached | `percent_scrolled` |
| `brochure_download` | add to any brochure link | `file_name` |
| `trust_element_click` | a proof point in the trust strip below the hero is clicked | `trust_element` |
| `calculator_interact` | first change to either "lost morning cost" input (once per page view) | `calculator` |
| `calculator_cta_click` | "Get a gritting quote" under the calculator result | `calculator_value` |
| `plan_compare_view` | plan feature-comparison grid scrolls into view (once) | `section` |
| `sector_click` | a sector chip in the coverage section is activated | `sector` |
| `scroll_cue_click` | the hero "scroll for more" affordance is clicked | `cta_location` |

Baseline push (already in `<head>`):

```js
window.dataLayer = window.dataLayer || [];
dataLayer.push({ page_type:'service_landing', service:'gritting', page_variant:'A' });
```

Example manual push (e.g. a brochure link the CMS inserts):

```html
<a href="/assets/pdf/winter-maintenance.pdf" download
   data-cwc-event="brochure_download" data-cwc-location="trust_section">Download the winter brochure</a>
```

## 2. GTM setup

1. Create a **Custom Event** trigger per event name above.
2. Create **Data Layer Variables**: `cta_location`, `cta_text`, `form_id`,
   `page_variant`, `faq_question`, `percent_scrolled`, `error_count`,
   `trust_element`, `calculator`, `calculator_value`, `section`, `sector`.
3. GA4 Event tags - one per conversion event, forwarding the variables as
   event parameters. Register `cta_location` and `page_variant` as **custom
   dimensions** in GA4 Admin so they're reportable.

## 3. Google Ads conversion (placeholders)

```html
<!-- Global site tag - replace AW-XXXXXXXXX -->
<script async src="https://www.googletagmanager.com/gtag/js?id=AW-XXXXXXXXX"></script>
<script>
  window.dataLayer=window.dataLayer||[];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config','AW-XXXXXXXXX');
</script>

<!-- Conversion: quote form submit - fire from a GTM tag on form_submit -->
<script>
  gtag('event','conversion',{
    'send_to':'AW-XXXXXXXXX/FORM_LABEL',
    'value': 0.0, 'currency':'GBP'
  });
</script>

<!-- Conversion: phone click - fire on phone_click -->
<script>
  gtag('event','conversion',{ 'send_to':'AW-XXXXXXXXX/CALL_LABEL' });
</script>
```

Integration notes: prefer firing both through **GTM tags bound to the custom
events** rather than hard-coding in the page, so the IDs live in one place and
survive CMS edits. If call tracking numbers (Google forwarding numbers) are used,
insert them via GTM number-swap and keep `tel:` markup identical.

## 4. Meta Pixel (placeholder)

```html
<script>
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
document,'script','https://connect.facebook.net/en_US/fbevents.js');
fbq('init','PIXEL_ID');
fbq('track','PageView');
</script>
<noscript><img height="1" width="1" style="display:none"
  src="https://www.facebook.com/tr?id=PIXEL_ID&ev=PageView&noscript=1" alt=""></noscript>
```

Map `form_submit` → `fbq('track','Lead')`, `phone_click` → `fbq('trackCustom','PhoneClick')`.

**Consent:** the site already runs a cookie-consent tool. Gate the Ads and Meta
tags behind consent (GTM Consent Mode v2, `ad_storage` / `analytics_storage`).
Do not fire marketing pixels before consent - UK GDPR/PECR.

## 5. GA4 conversions to mark

Primary: `form_submit`, `phone_click`, `callback_open`.
Secondary (micro): `form_start`, `faq_open`, `video_play`, `brochure_download`,
`postcode_search`, `scroll_depth` (75).

Recommended GA4 explorations: conversion rate by `page_variant`, by
`cta_location`, and by device; funnel `page_view → form_start → form_submit`.


## 6. Pass-2 engagement events - how to read them

These are **engagement / diagnostic** events, not conversions. Do not mark them
as GA4 key events; use them as segments on the real conversions
(`form_submit`, `phone_click`).

| Event | What it tells you | Suggested use |
|---|---|---|
| `trust_element_click` | which proof point buyers probe first (depots vs Met Office vs GPS vs ISO) | order the trust strip by demand |
| `calculator_interact` | the visitor engaged with the cost estimator | compare `form_submit` rate of engagers vs non-engagers |
| `calculator_cta_click` | the estimator drove an intent click; `calculator_value` is **the visitor's own figure**, never a CWC claim | qualify lead size, sanity-check the 4-hour assumption |
| `plan_compare_view` | the comparison grid was actually seen | check whether the Fixed Season Price emphasis moves plan choice |
| `sector_click` | which sectors self-identify | landing-page and PPC ad-group ideas |
| `scroll_cue_click` | above-the-fold copy did not close the visitor | hero copy test signal |

Implementation notes:

* `calculator_interact` is de-duplicated per page view (a JS flag), so it can be
  used directly as an engaged-session rate.
* `plan_compare_view` uses an `IntersectionObserver` at a 0.4 threshold and
  disconnects after the first fire - one event per page view maximum.
* `trust_element_click` and `sector_click` fire on activation only (click or
  keyboard Enter/Space via native `<button>`), so they are keyboard-parity safe.
* No event carries personal data. `calculator_value` is a rounded integer
  derived only from numbers the visitor typed.
