#!/usr/bin/env python3
"""Build the standalone Zap report — letter to Zap Ltd documenting the in-house transition.

Includes:
- Wayback Machine timeline with evidence
- Sales decline data
- 90-day audit findings (campaigns, frequency, malformed UTMs)
- Decision: paid media moves in-house, hosting Package 11 retained
- Transition timeline + handover requirements
"""
import json
from datetime import datetime

ROOT = "/home/sol1/Desktop/MARKETING/nitrous_3mo"
OUT = f"{ROOT}/ZAP_NOTICE.html"

# Load underlying data
zap = json.load(open(f"{ROOT}/zap_audit.json"))
meta = json.load(open(f"{ROOT}/meta_intel.json"))
intel = json.load(open(f"{ROOT}/deep_intel.json"))

s = zap["summary"]
zap_camps = [c for c in zap["all_campaigns"] if c["is_zap"]]
waste_camps = [c for c in zap["all_campaigns"] if c["revenue"] < 200 and c["name"] != "(no campaign tag)"]

# Wayback timeline
wayback = [
    {"date": "2024-03-26", "label": "Pre-Zap baseline", "page_size_kb": 442,
     "prize_given_M": 30, "social_k": 390, "max_cash": "£10,000",
     "title": "Nitrous Competitions – You got to be in it to win it",
     "iw": 58, "cars": 57, "cash_mentions": 30, "buy_mentions": 96},
    {"date": "2024-04-19", "label": "Zap onboarding — first Advantage+ campaign", "is_marker": True,
     "note": "Confirmed by your own UTM: 'Advantage++shopping+campaign+19/04/2024+Campaign'"},
    {"date": "2024-06-17", "label": "Peak ramp — Bank of Nitrous flagship era", "page_size_kb": 530,
     "prize_given_M": 100, "social_k": 400, "max_cash": "£10,000",
     "title": "Nitrous Competitions – You got to be in it to win it",
     "iw": 219, "cars": 85, "cash_mentions": 23, "buy_mentions": 104},
    {"date": "2024-09-28", "label": "Stagnation begins", "page_size_kb": 481,
     "prize_given_M": 100, "social_k": 400},
    {"date": "2024-12-04", "label": "End of year — same stat", "page_size_kb": 494,
     "prize_given_M": 100, "social_k": 400},
    {"date": "2025-02-09", "label": "Q1 2025 — followers tick up to 450k but prizes flat", "page_size_kb": 501,
     "prize_given_M": 100, "social_k": 450},
    {"date": "2025-04-14", "label": "12 months in. Prize-given stat STILL £100M+", "page_size_kb": 498,
     "prize_given_M": 100, "social_k": 450, "max_cash": "£2,500",
     "title_change": "Generic 'Online Competitions' tagline replaces 'You got to be in it to win it'"},
    {"date": "2025-07-28", "label": "🚨 'Given out in prizes' stat REMOVED from homepage", "page_size_kb": 525,
     "prize_given_M": None, "social_k": 450, "max_cash": "£1,000",
     "is_critical": True,
     "note": "First snapshot without the £100M+ headline number — clear signal that the metric was no longer celebratory"},
    {"date": "2025-10-15", "label": "Page size starts collapsing", "page_size_kb": 316,
     "prize_given_M": None, "social_k": 450, "max_cash": "£1,000",
     "iw": 40, "cars": 65, "cash_mentions": 6, "buy_mentions": 92},
    {"date": "2025-12-18", "label": "Followers grow but funnel breaks — 550k followers", "page_size_kb": 413,
     "prize_given_M": None, "social_k": 550, "max_cash": "£1,000"},
    {"date": "2026-02-14", "label": "Current era — homepage at 251KB, no prize stat, no followers stat", "page_size_kb": 245,
     "prize_given_M": None, "social_k": None, "max_cash": "£1,000",
     "iw": 25, "cars": 38, "cash_mentions": 4, "buy_mentions": 60,
     "is_critical": True,
     "note": "Page size has HALVED since 2024 peak. Both growth-vanity stats removed."},
]

# Compute key derived numbers for the letter
ZAP_FEE_INC = 42542.26
ZAP_FEE_NET = 35451.88
META_SPEND_90D = 474015.13
META_SPEND_MONTHLY = META_SPEND_90D / 3
META_FREQUENCY = 19.52
META_REACH = 5350041
META_IMPRESSIONS = 104437066
META_ATTRIBUTED = meta['fb_tagged_revenue']
ATTRIBUTED_ROAS = META_ATTRIBUTED / META_SPEND_90D
NO_TAG = next((c for c in zap['all_campaigns'] if c['name'] == '(no campaign tag)'), {'revenue': 0, 'orders': 0})
MALFORMED_PCT = 100 * meta['malformed_utm_orders'] / meta['fb_tagged_orders']
WASTE_COUNT = len(waste_camps)
WASTE_REV = sum(c['revenue'] for c in waste_camps)
TODAY = datetime.now().strftime("%d %B %Y")
NOTICE_DATE = "7 May 2026"

# Build HTML
html = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Notice of Marketing Service Restructure — Nitrous Competitions to Zap Ltd</title>
<style>
  :root {{
    --primary: #0b1220;
    --accent: #1e3a8a;
    --warm: #b45309;
    --red: #991b1b;
    --green: #15803d;
    --bg: #fafaf7;
    --paper: #fff;
    --line: #e5e7eb;
    --muted: #64748b;
  }}
  * {{ box-sizing: border-box; }}
  body {{
    font-family: 'Charter','Iowan Old Style','Apple Garamond','Baskerville','Times New Roman',serif;
    margin: 0; padding: 0; background: var(--bg);
    color: #0f172a; line-height: 1.7; font-size: 16px;
  }}
  .page {{
    max-width: 920px; margin: 40px auto;
    background: var(--paper); padding: 70px 80px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.08);
    border-top: 6px solid var(--primary);
  }}
  .letterhead {{
    display: flex; justify-content: space-between;
    align-items: flex-start; margin-bottom: 50px;
    padding-bottom: 24px; border-bottom: 1px solid var(--line);
  }}
  .letterhead .from h1 {{
    font-size: 26px; margin: 0 0 4px; font-weight: 800;
    letter-spacing: -0.5px; font-family: 'Inter', sans-serif;
  }}
  .letterhead .from p {{ margin: 0; font-size: 13px; color: var(--muted); line-height: 1.4; font-family: 'Inter', sans-serif; }}
  .letterhead .meta {{ text-align: right; font-family: 'Inter', sans-serif; font-size: 13px; color: var(--muted); }}
  .letterhead .meta .ref {{ font-weight: 700; color: var(--primary); margin-bottom: 4px;}}

  .doc-title {{
    text-align: center; margin: 30px 0 50px;
  }}
  .doc-title .kicker {{
    font-size: 12px; letter-spacing: 4px; text-transform: uppercase;
    color: var(--accent); margin-bottom: 14px;
    font-family: 'Inter', sans-serif; font-weight: 600;
  }}
  .doc-title h2 {{
    font-size: 36px; font-weight: 800; margin: 0;
    letter-spacing: -1px; line-height: 1.15;
    font-family: 'Inter', sans-serif;
  }}
  .doc-title .sub {{
    font-size: 17px; color: var(--muted);
    margin-top: 14px; font-style: italic;
  }}

  .recipient {{
    background: #f8fafc; padding: 24px 28px;
    border-left: 4px solid var(--accent);
    border-radius: 4px; margin: 0 0 40px; font-family: 'Inter', sans-serif;
  }}
  .recipient .label {{ font-size: 11px; letter-spacing: 2px;
    text-transform: uppercase; color: var(--muted); margin-bottom: 6px;}}
  .recipient .name {{ font-weight: 700; font-size: 16px;}}

  h3 {{
    font-size: 22px; margin: 50px 0 14px;
    color: var(--primary); font-weight: 700;
    font-family: 'Inter', sans-serif;
    letter-spacing: -0.3px;
  }}
  h3 .num {{
    color: var(--accent); margin-right: 12px; font-weight: 800;
  }}
  h4 {{
    font-size: 16px; margin: 24px 0 8px; font-weight: 700;
    color: var(--accent); font-family: 'Inter', sans-serif;
  }}

  p {{ margin: 12px 0; }}

  .pull {{
    border: 2px solid var(--primary);
    background: #f8fafc; padding: 22px 28px;
    margin: 30px 0; border-radius: 4px;
    font-family: 'Inter', sans-serif;
    font-size: 17px; line-height: 1.55;
  }}
  .pull.warn {{ border-color: var(--warm); background: #fffbeb; }}
  .pull.red {{ border-color: var(--red); background: #fef2f2; }}
  .pull .small {{ font-size: 11px; letter-spacing: 2px; text-transform: uppercase;
    color: var(--muted); margin-bottom: 10px; font-weight: 700;}}

  table {{
    width: 100%; border-collapse: collapse;
    margin: 18px 0; font-size: 14px;
    font-family: 'Inter', sans-serif;
  }}
  th {{ background: var(--primary); color: #fff;
    padding: 10px 12px; text-align: left; font-weight: 600; font-size: 13px; }}
  td {{ padding: 9px 12px; border-bottom: 1px solid var(--line); vertical-align: top; }}
  tr:last-child td {{ border-bottom: none; }}
  td.num {{ text-align: right; font-variant-numeric: tabular-nums; }}

  /* Timeline */
  .timeline {{
    margin: 24px 0; padding-left: 0;
  }}
  .timeline-item {{
    display: grid; grid-template-columns: 130px 1fr;
    gap: 18px; align-items: flex-start;
    padding: 16px 0; border-bottom: 1px solid var(--line);
    font-family: 'Inter', sans-serif;
  }}
  .timeline-item.critical {{
    background: #fef2f2; padding: 16px 14px;
    border-bottom: 2px solid var(--red);
    margin: 6px -14px;
  }}
  .timeline-item.marker {{
    background: #fffbeb; padding: 16px 14px;
    border-bottom: 2px solid var(--warm);
    margin: 6px -14px;
  }}
  .timeline-date {{
    font-weight: 700; font-size: 14px;
    color: var(--accent); font-variant-numeric: tabular-nums;
  }}
  .timeline-item.critical .timeline-date {{ color: var(--red); }}
  .timeline-item.marker .timeline-date {{ color: var(--warm); }}
  .timeline-content .label {{
    font-size: 15px; font-weight: 600; color: var(--primary);
    margin-bottom: 6px;
  }}
  .timeline-content .stats {{
    display: flex; flex-wrap: wrap; gap: 14px; font-size: 12.5px;
    color: var(--muted); margin: 6px 0;
  }}
  .timeline-content .stats span {{
    background: #f1f5f9; padding: 3px 8px; border-radius: 3px;
  }}
  .timeline-content .note {{
    font-style: italic; color: var(--muted); margin-top: 6px; font-size: 13.5px;
  }}

  .signature {{
    margin: 60px 0 30px; padding-top: 30px;
    border-top: 1px solid var(--line);
    font-family: 'Inter', sans-serif;
  }}
  .signature .sign-line {{
    margin-top: 60px; border-bottom: 1px solid var(--primary);
    width: 280px; padding-bottom: 4px;
  }}
  .signature .name {{ margin-top: 10px; font-weight: 700; }}
  .signature .role {{ color: var(--muted); font-size: 14px; }}

  .pill {{
    display: inline-block; padding: 2px 9px; border-radius: 3px;
    font-size: 12px; font-weight: 600; letter-spacing: 0.3px;
  }}
  .pill.red {{ background: #fee2e2; color: var(--red); }}
  .pill.warn {{ background: #fef3c7; color: var(--warm); }}
  .pill.green {{ background: #d1fae5; color: var(--green); }}
  .pill.blue {{ background: #dbeafe; color: var(--accent); }}

  ul.cleanlist {{ list-style: none; padding-left: 0; }}
  ul.cleanlist li {{
    padding: 8px 0 8px 28px; position: relative;
    border-bottom: 1px dotted var(--line);
  }}
  ul.cleanlist li::before {{
    content: '→'; position: absolute; left: 0;
    color: var(--accent); font-weight: 700;
  }}

  @media print {{
    body {{ background: #fff; font-size: 14px; }}
    .page {{ box-shadow: none; padding: 30px; margin: 0; max-width: 100%; }}
    .timeline-item.critical, .timeline-item.marker {{ break-inside: avoid; }}
    h3 {{ break-after: avoid; }}
    table, .pull {{ break-inside: avoid; }}
  }}

  .page-footer {{
    margin-top: 60px; padding-top: 24px;
    border-top: 1px solid var(--line);
    font-size: 11px; color: var(--muted); text-align: center;
    font-family: 'Inter', sans-serif;
  }}
</style>
</head>
<body>
<div class="page">

<div class="letterhead">
  <div class="from">
    <h1>Nitrous Competitions Ltd.</h1>
    <p>10 Genesis Park, Magna Road<br>
    Wigston, Leicester<br>
    East Midlands LE18 4AJ<br>
    United Kingdom</p>
  </div>
  <div class="meta">
    <div class="ref">REF: NC-ZAP-NOTICE-2026-05</div>
    <div>Issued: {NOTICE_DATE}</div>
    <div>Effective: 1 June 2026</div>
  </div>
</div>

<div class="doc-title">
  <div class="kicker">Formal Notice · Service Restructure</div>
  <h2>Marketing Services — Bringing Paid Media In-House</h2>
  <div class="sub">Hosting &amp; technical services to be retained on a revised scope</div>
</div>

<div class="recipient">
  <div class="label">Delivered to</div>
  <div class="name">Zap Ltd<br>
  Pavillion 2, Finnieston Business Park<br>
  Minerva Way, Glasgow G3 8AU<br>
  Reg. SC509259 · VAT 270887176</div>
</div>

<p>Dear Team at Zap,</p>

<p>Thank you for the work over the last two years. We're writing to formally notify you of a restructure to our service arrangement, taking effect <strong>1 June 2026</strong>.</p>

<p>After completing a 90-day forensic audit of our marketing performance, attribution data and a full review of the Wayback Machine archive of our website (March 2024 onwards), we've made the decision to <strong>bring all paid-media management in-house</strong>. We'd like to retain Zap for hosting, licence and technical support only.</p>

<p>The decision is data-led — and we'd like to share the data with you below in the spirit of transparency, so the handover can be cooperative and professional.</p>

<h3><span class="num">1.</span>The Wayback Machine evidence — 24-month timeline</h3>
<p>Below is the Internet Archive's record of our homepage at key dates, with the metrics that were visible to anyone who visited. This is what an outside party would see. It's also what shareholders see.</p>

<div class="timeline">
"""

for w in wayback:
    is_critical = w.get('is_critical', False)
    is_marker = w.get('is_marker', False)
    cls = 'critical' if is_critical else ('marker' if is_marker else '')
    html += f'  <div class="timeline-item {cls}">\n'
    html += f'    <div class="timeline-date">{w["date"]}</div>\n'
    html += f'    <div class="timeline-content">\n'
    html += f'      <div class="label">{w["label"]}</div>\n'

    stats = []
    if 'page_size_kb' in w and w['page_size_kb']:
        stats.append(f'<span>Page: {w["page_size_kb"]} KB</span>')
    if 'prize_given_M' in w and w['prize_given_M'] is not None:
        stats.append(f'<span>Prizes given out: <strong>£{w["prize_given_M"]}M+</strong></span>')
    elif 'prize_given_M' in w:
        stats.append('<span class="pill red">Prize-given stat REMOVED</span>')
    if 'social_k' in w and w['social_k']:
        stats.append(f'<span>Social: {w["social_k"]}k+ followers</span>')
    elif 'social_k' in w:
        stats.append('<span class="pill red">Social-followers stat REMOVED</span>')
    if 'max_cash' in w and w['max_cash']:
        stats.append(f'<span>Max homepage cash prize: <strong>{w["max_cash"]}</strong></span>')
    if 'iw' in w:
        stats.append(f'<span>Instant-win mentions: {w["iw"]}</span>')
    if 'buy_mentions' in w:
        stats.append(f'<span>Buy/Enter mentions: {w["buy_mentions"]}</span>')
    if stats:
        html += f'      <div class="stats">{"".join(stats)}</div>\n'

    if 'title' in w:
        html += f'      <div style="font-size: 13px; color: var(--muted); margin-top: 6px;"><strong>Page title:</strong> "{w["title"]}"</div>\n'
    if 'title_change' in w:
        html += f'      <div style="font-size: 13px; color: var(--warm); margin-top: 6px;"><strong>Title change:</strong> {w["title_change"]}</div>\n'
    if 'note' in w:
        html += f'      <div class="note">{w["note"]}</div>\n'

    html += f'    </div>\n  </div>\n'

html += f"""</div>

<div class="pull warn">
  <div class="small">What the timeline shows</div>
  Three documented turning points: <strong>(1)</strong> March → June 2024: prize give-outs grew from £30M+ to £100M+ as Zap onboarded — a strong launch period. <strong>(2)</strong> June 2024 → April 2025: the £100M+ figure plateaued for 10+ months — growth stopped. <strong>(3)</strong> July 2025 onwards: the prize-given stat was quietly removed from the homepage and has not returned. The website page size has since halved (442KB → 245KB) and the homepage cash prize ceiling collapsed from £10,000 to £1,000.
</div>

<h3><span class="num">2.</span>The 90-day Meta Ads audit (Feb 6 – May 6, 2026)</h3>
<p>Pulled live from the WooCommerce REST API — every order, every UTM, every campaign. Cross-referenced against the Meta Ads Manager export (Q1 2026, account "Nitrous Competitions") which showed:</p>

<table>
  <thead><tr><th>Metric (Q1 2026 Meta export)</th><th class="num">Value</th></tr></thead>
  <tbody>
    <tr><td>Spend (90 days)</td><td class="num">£{META_SPEND_90D:,.0f}</td></tr>
    <tr><td>Reach</td><td class="num">{META_REACH/1e6:.2f}M people</td></tr>
    <tr><td>Impressions</td><td class="num">{META_IMPRESSIONS/1e6:.0f}M</td></tr>
    <tr><td><strong>Frequency</strong></td><td class="num"><span class="pill red">{META_FREQUENCY:.2f}× ⚠</span></td></tr>
    <tr><td>Attributed revenue (Meta-tagged orders)</td><td class="num">£{META_ATTRIBUTED:,.0f}</td></tr>
    <tr><td>Attributed ROAS</td><td class="num"><span class="pill warn">{ATTRIBUTED_ROAS:.2f}×</span></td></tr>
  </tbody>
</table>

<p>Three findings stand out:</p>

<h4>2.1 Frequency of {META_FREQUENCY:.2f}× — creative fatigue at scale</h4>
<p>Industry benchmark for healthy ecom Meta is 3–7× frequency. {META_FREQUENCY:.2f}× means each person in the 5.35M reach saw an ad on average <strong>{META_FREQUENCY:.0f} times</strong> over 90 days. That's 3–7× over-saturation. The same hooks are being shown to the same people repeatedly, which fails to convert and leads to ad-set fatigue.</p>

<h4>2.2 Campaign sprawl — {s['total_campaigns']} active campaigns, {s['zap_campaigns']} Zap-named</h4>
<p>Meta's algorithm requires ≥50 conversions per campaign per week to optimise effectively. With {s['total_campaigns']} campaigns active, the budget is fragmented well below this threshold. Of the {s['zap_campaigns']} Zap-named campaigns:</p>
<ul class="cleanlist">
  <li>{sum(1 for c in zap_camps if c['revenue']>=1000)} earned £1,000+ in 90 days (sum: £{sum(c['revenue'] for c in zap_camps if c['revenue']>=1000):,.0f})</li>
  <li>{sum(1 for c in zap_camps if 200<=c['revenue']<1000)} earned between £200 and £1,000 (sum: £{sum(c['revenue'] for c in zap_camps if 200<=c['revenue']<1000):,.0f})</li>
  <li><strong>{sum(1 for c in zap_camps if c['revenue']<200)} campaigns earned less than £200 over 90 days</strong> (combined sum: £{sum(c['revenue'] for c in zap_camps if c['revenue']<200):,.0f})</li>
</ul>

<h4>2.3 The malformed UTM bug — {meta['malformed_utm_orders']:,} orders ({MALFORMED_PCT:.0f}%) with utm_content="/"</h4>
<p>Of the {meta['fb_tagged_orders']:,} Meta-attributed orders in 90 days, <strong>{meta['malformed_utm_orders']:,} ({MALFORMED_PCT:.0f}%)</strong> arrived with the literal string "/" as utm_content. This indicates Meta's <code>{{{{ad.name}}}}</code> dynamic parameter was never enabled at ad-set level — meaning per-ad attribution has been impossible for the entire campaign run. We can see Meta is generating revenue, but neither party can tell <em>which creative inside which campaign</em> is responsible.</p>

<p>Compounding this: <strong>£{NO_TAG['revenue']:,.0f} of Meta-attributed revenue from {NO_TAG['orders']:,} orders has no campaign tag at all</strong>. That's 72% of our Meta revenue invisible to your dashboard.</p>

<h4>2.4 The "Advantage++ Shopping Campaign 19/04/2024" finding</h4>
<p>The campaign tagged with this date has been running, by its own naming convention, for over <strong>13 months</strong> with no audit, no creative refresh, and no rename. It's earning £10,084 over the recent 90 days, which we appreciate — but a paid-media programme that doesn't refresh creative or audiences in 13 months is, by industry standard, on autopilot rather than being managed.</p>

<h3><span class="num">3.</span>The cost &amp; ROAS reality</h3>
<table>
  <thead><tr><th>Line</th><th class="num">90-day total</th><th>Notes</th></tr></thead>
  <tbody>
    <tr><td>Zap fees (3× £42,542 inc VAT)</td><td class="num">£127,627</td><td>Hosting + Support + Marketing Accelerator</td></tr>
    <tr><td>Meta ad spend (your management)</td><td class="num">£474,015</td><td>Q1 2026 Meta Ads Manager export</td></tr>
    <tr><td>Total cost (Zap + Meta) over 90d</td><td class="num"><strong>£601,642</strong></td><td>—</td></tr>
    <tr><td>Meta-attributed revenue</td><td class="num">£636,947</td><td>WC order_attribution metadata</td></tr>
    <tr><td>Apparent ROAS (cost-of-Meta basis)</td><td class="num">{META_ATTRIBUTED/META_SPEND_90D:.2f}×</td><td>Excludes Zap fee</td></tr>
    <tr><td>True ROAS (with Zap fee absorbed)</td><td class="num"><span class="pill warn">{META_ATTRIBUTED/(META_SPEND_90D+127627):.2f}×</span></td><td>Including agency fee</td></tr>
  </tbody>
</table>

<p>We acknowledge that the true Meta ROAS (post-CAPI install, factoring 30–50% cross-device leakage into Direct/Typein) is likely higher than the dashboard shows. That's part of the reason we've made this decision: <strong>the attribution gap means we cannot manage what we cannot measure</strong>, and the cost of running blind has now exceeded the cost of bringing it in-house.</p>

<h3><span class="num">4.</span>The decision &amp; new scope</h3>

<div class="pull">
  <div class="small">Effective 1 June 2026</div>
  <strong>We are bringing all paid-media management in-house.</strong> We will continue to retain Zap for hosting and technical services on a revised scope.
</div>

<h4>4.1 What we are RETAINING</h4>
<table>
  <thead><tr><th>Service</th><th class="num">Monthly fee (inc VAT)</th><th>Status</th></tr></thead>
  <tbody>
    <tr><td>Hosting / Package 11</td><td class="num">£30,000.00</td><td><span class="pill green">RETAIN</span></td></tr>
    <tr><td>Licence / Support / Maintenance</td><td class="num">£898.80</td><td><span class="pill green">RETAIN</span></td></tr>
    <tr><td><strong>New monthly total</strong></td><td class="num"><strong>£30,898.80</strong></td><td>—</td></tr>
  </tbody>
</table>

<h4>4.2 What we are CANCELLING</h4>
<table>
  <thead><tr><th>Service</th><th class="num">Monthly fee (inc VAT)</th><th>Status</th></tr></thead>
  <tbody>
    <tr><td>Marketing — Accelerator Program Package</td><td class="num">£11,643.46</td><td><span class="pill red">CANCEL effective 1 June 2026</span></td></tr>
  </tbody>
</table>

<p>Component services included under this line and now moving in-house:</p>
<ul class="cleanlist">
  <li>Marketing Consultant / Coordinator</li>
  <li>Fully Managed Paid Media (Meta, TikTok, Google)</li>
  <li>Fully Managed Retention</li>
  <li>Fully Managed SEO</li>
  <li>Organic Social</li>
  <li>Video Creative Services</li>
</ul>

<h4>4.3 The 1% revenue-share clause (May 2026 onwards)</h4>
<div class="pull red">
  <div class="small">Specifically declined</div>
  We understand that from May 2026 the Marketing Accelerator fee was scheduled to move to 1% of net sales revenue (capped at a £15,000/month floor). <strong>We are formally declining this revised structure.</strong> A revenue-share contract that scales with our growth — but is decoupled from your scope of work — is not aligned with the operating model we're moving towards.
</div>

<h3><span class="num">5.</span>Transition handover — what we need from Zap</h3>
<p>To make the handover clean and professional, please action the following by <strong>31 May 2026</strong>:</p>

<table>
  <thead><tr><th>#</th><th>Item</th><th>Owner at Zap</th></tr></thead>
  <tbody>
    <tr><td>1</td><td>Full export of Meta Business Manager admin access — transfer Business Manager primary admin role to Nitrous Competitions Ltd</td><td>Account manager</td></tr>
    <tr><td>2</td><td>Final list of all active Meta campaigns + ad sets + creative assets (image/video files) at handover</td><td>Paid media team</td></tr>
    <tr><td>3</td><td>Pixel + CAPI configuration documentation (current state, including known gaps)</td><td>Tech lead</td></tr>
    <tr><td>4</td><td>Custom Audience list (full list of audiences currently uploaded to Meta), with sources documented</td><td>Paid media team</td></tr>
    <tr><td>5</td><td>List of any TikTok, Google or other paid-media accounts currently managed under your access</td><td>Account manager</td></tr>
    <tr><td>6</td><td>Klaviyo administrator role transfer (currently shared) — confirm Nitrous as sole owner</td><td>Retention team</td></tr>
    <tr><td>7</td><td>Final 90-day report from your dashboard, including spend by campaign and any internal attribution overrides</td><td>Account manager</td></tr>
    <tr><td>8</td><td>Continued hosting / Package 11 SLA confirmation under the new scope (uptime, backups, deploy access)</td><td>Hosting team</td></tr>
  </tbody>
</table>

<h3><span class="num">6.</span>Closing</h3>

<p>This is a business decision based on data, not on personal feeling. We recognise the work that's gone in over two years, and we appreciate the partnership that brought us through the £30M-to-£100M prize-give-out phase in 2024. The Wayback record of that period speaks for itself.</p>

<p>The path from here looks different from the path that brought us here. We're going to run paid media in-house, with focused creative refresh, server-side attribution (CAPI), Advantage+ Shopping Campaign autopilot, and tight UTM hygiene. We hope Zap can continue as our hosting and tech partner under the revised scope, and we wish the Marketing Accelerator team success with other clients.</p>

<p>Please confirm receipt of this notice and acknowledgement of the new scope by <strong>14 May 2026</strong>. If we don't hear back by that date, we'll proceed with handover steps as outlined.</p>

<p>For any questions on this letter, contact us directly. We'd welcome a 30-minute handover call between 14 May and 31 May.</p>

<div class="signature">
  <p>Kind regards,</p>
  <div class="sign-line"></div>
  <div class="name">Matty &amp; Dave</div>
  <div class="role">Directors, Nitrous Competitions Ltd.</div>
  <div style="margin-top: 8px; font-size: 13px; color: var(--muted);">Wigston, Leicester · {NOTICE_DATE}</div>
</div>

<div class="page-footer">
  Reference: NC-ZAP-NOTICE-2026-05 · Distributed to: Zap Ltd account team · Copy retained: Nitrous Competitions Ltd<br>
  This notice is supported by a 90-day forensic audit available on request, including: WooCommerce order attribution data ({intel['summary']['total_orders_90d']:,} orders), Klaviyo campaign performance ({len(json.load(open(f'{ROOT}/klaviyo/email_campaigns.json')))} email + {len(json.load(open(f'{ROOT}/klaviyo/sms_campaigns.json')))} SMS campaigns analysed), Meta Ads Manager export, and Wayback Machine snapshots from March 2024 to February 2026.
</div>

</div>
</body>
</html>
"""

with open(OUT, "w") as f:
    f.write(html)

import os
print(f"Wrote {OUT}: {os.path.getsize(OUT):,} bytes")
print()
print("=== KEY DATA POINTS USED ===")
print(f"Wayback timeline points: {len(wayback)}")
print(f"Zap fee April 2026: £{ZAP_FEE_INC:,.2f}")
print(f"Meta spend Q1 2026: £{META_SPEND_90D:,.0f}")
print(f"Frequency: {META_FREQUENCY:.2f}x")
print(f"Total campaigns: {s['total_campaigns']}")
print(f"Zap campaigns: {s['zap_campaigns']}")
print(f"Waste campaigns: {WASTE_COUNT}")
print(f"Malformed UTM orders: {meta['malformed_utm_orders']:,} ({MALFORMED_PCT:.0f}%)")
print(f"No-tag revenue: £{NO_TAG['revenue']:,.0f}")
