# Delta LP Playbook

The "how to build the next Delta LP" reference. Built from what shipped on `/5`, `/10`, `/20`, `/5-guide`, `/10-guide`, `/20-guide`. If a future LP doesn't follow this, the deviation should be deliberate.

Companion docs:
- [DELTA_LANDING_PAGE_BRIEF.md](DELTA_LANDING_PAGE_BRIEF.md) — paid-media data, sub-personas, why each page exists
- [copy-reference-5-10-20.md](copy-reference-5-10-20.md) — brand voice, claims, block ideas, FAQ bank, pull-quote bank
- [README.md](README.md) — stack and deploy notes

---

## Table of contents

1. The two LP archetypes
2. Tier system and naming rules
3. Compliance rules (never break these)
4. Stack and file layout
5. Anatomy of a Sampler LP (`/N`)
6. Anatomy of a Guide LP (`/N-guide`)
7. Shared building blocks
8. Buy block, cart, and checkout wiring
9. Imagery system
10. Copy patterns and signature lines
11. Pricing and offer math
12. How to add a new LP
13. Pre-ship checklist
14. Common mistakes (already learned)
15. Future LP ideas (from the brief)
16. Shopify IDs (truth source from production go.drinkdelta.com)
17. Production code patterns (HTML, JS, checkout)
18. V2 vs production: the deltas worth knowing

---

## 1. The two LP archetypes

Every Delta acquisition page is one of two formats. Pick one before writing anything.

### Sampler LP (`/5`, `/10`, `/20`)
Editorial PDP. Hero with hero image and price card, then a long product story (USPs, moments, comparison, reviews, founder, FAQ), then a buy block that runs a flavor + pack + subscribe configurator. CTA is "shop now," the page is built to convert traffic that already self-identified with the tier (e.g. clicked an ad for "the social drink").

Use when: the ad sold a tier-specific product. The job is to close.

### Guide LP (`/5-guide`, `/10-guide`, `/20-guide`)
Advertorial listicle. Article-style hero, then a numbered "5 reasons / 5 nights / 5 things" body. Each item is one flavor + one moment + one teaching point. Buy block lands once mid-article and again at the bottom. No configurator, no flavor switcher, just the Sampler.

Use when: the ad sold curiosity, hosting, ritual, or a frame ("what your friends are quietly drinking instead of wine"). The job is to teach, then close.

The two formats share a navbar, top promo bar, footer, FAQ, color tokens, and the buy block. They diverge entirely in the middle.

---

## 2. Tier system and naming rules

Three tiers. They map 1:1 to the three SKUs. The dose number never appears in user-visible copy.

| Tier | Internal | User-visible name | Vibe | Audience |
|---|---|---|---|---|
| Light | 5mg | **The Light** | Sessionable, daytime, casual | First-timers, sober-curious, social weekday |
| Moderate | 10mg | **The Moderate** | Social, balanced, sweet spot | Regulars, hosts, sober-curious at the table |
| Bold | 20mg | **The Bold** | Wind-down, deep, longer-lasting | Experienced, sleep, evening ritual |

Naming scheme used everywhere:
- Product: "The Light Sampler", "The Moderate Sampler", "The Bold Sampler"
- Eyebrow on `/N`: "The Light Tier · 21+", "Sober curious · 21+", etc.
- Reading-level for guides: "Garden Seltzer 101" (Light), "Garden Seltzer 102" (Moderate), "Garden Seltzer 103" (Bold)

Internal/code references that may keep "5mg/10mg/20mg" (filenames, JS variables, comments, image paths like `/assets/images/sampler/10mg/hero.jpg`) are fine. User-visible HTML, alt text, meta tags, JS-rendered strings, and titles are not.

---

## 3. Compliance rules (never break these)

These are project rules. They have already been violated and corrected once each. Don't repeat.

1. **No "mg" in user-visible copy.** Use Light / Moderate / Bold. This is Meta-ad compliance for cannabis dose claims. Grep `\d+mg` before shipping. (See [memory: feedback_no_mg_in_copy.md](../../../../../Users/williamsartorius/.claude/projects/-Volumes-selfmade-lucid-Claude-Code-Landing-Pages-Drink-Delta-LP-V2/memory/feedback_no_mg_in_copy.md))
2. **No em dashes (`—`, `–`).** Project voice rule. Use periods, commas, or colons. Hyphens (`-`) for compound words are fine. Grep before shipping.
3. **No banned words in body copy:** "THC", "high", "buzz", "stoner", "weed". "Hemp" is OK in spec/factual context. "Plant-based", "garden seltzer", "feel-good", "lift", "sip" are the working alternatives.
4. **No sticky add-to-cart bar.** Don't include `<div class="sticky-buy">` or the `js-sticky-add` handler on new LPs. The `/5` legacy version may still have one — leave it.
5. **All visible cans are NO-THC variants** from the brand library. The product is photographed and rendered without dose call-outs on the can.
6. **21+ marker** appears in the hero eyebrow or trust strip on every page.
7. **FDA-style disclaimer** lives in the footer. Already in `site-footer`. Don't remove.

---

## 4. Stack and file layout

Static HTML, no build step. Bootstrap 5 + jQuery + a few small site scripts. Hosted on Vercel.

```
/                         → redirects to /5 (handled by index.html)
/5/index.html             → Light Sampler LP
/5-guide/index.html       → Light Guide
/10/index.html            → Moderate Sampler LP
/10-guide/index.html      → Moderate Guide
/20/index.html            → Bold Sampler LP
/20-guide/index.html      → Bold Guide
/index.html               → SelfMade-facing card grid (preview / showcase, not a landing page)

/assets/css/site.css      → all shared styles (one file, ~2k lines)
/assets/css/bootstrap.min.css

/assets/js/jquery-3.7.1.min.js
/assets/js/bootstrap.bundle.min.js
/assets/js/shop.js                    → cart, checkout permalink, mini-cart drawer
/assets/js/sampler-configurator.js    → /5 PDP configurator (flavor/pack/subscribe)
/assets/js/sampler-configurator-10.js → /10 variant
/assets/js/sampler-configurator-20.js → /20 variant
/assets/js/animations.js              → IntersectionObserver fade-ins, can rotators

/assets/images/cans/                  → individual flavor cans (5mg-rotation/, 10mg-rotation/, 20mg-rotation/)
/assets/images/sampler/{5,10,20}mg/   → sampler box renders, gallery thumbs
/assets/images/lifestyle-{5,10,20}mg/ → lifestyle moments per tier
/assets/images/icons/zero/            → 0/0/0 SVG icons (sugar, alcohol, calories, gluten)
/assets/images/retailers/             → Total Wine, Sprouts, Circle K, Winn-Dixie, ABC, Kwik Trip, Refuel, Lowes Foods
/assets/images/founder/jack.jpg
/assets/images/logo.svg
/assets/images/favicon-delta.png

/vercel.json              → cleanUrls, rewrites /N → /N/index.html, immutable cache for assets
```

### Versioning the cached assets

`site.css` and the JS files are referenced with a `?v=YYYYMMDD<letter>` cache-buster query (`site.css?v=20260502c`). When you edit a shared file, bump the version on every page that uses it. Vercel headers cache them with `must-revalidate` so the bust matters.

### Local dev

```
npx serve .       # static-serve the project
```

There's no test runner, no bundler, no TS. Edits go straight in. Vercel builds from main.

---

## 5. Anatomy of a Sampler LP (`/N`)

Every sampler page has this section order. Keep it stable; the cart and configurator wire through these IDs.

```
1.  <head>                    — title/meta, fonts, bootstrap, site.css?v=...
2.  .bar                      — top promo strip ("Free shipping over $50 · NEW20 · 30-day guarantee")
3.  .site-header > .nav       — logo, nav links (#why, #flavors, #reviews, #faq), .cart-trigger
4.  .page-hero                — H1 + lede, 0/0/0 line, CTA, mini-trust, hero image with .hero-promo-card overlay
5.  .quick-testi-bar          — 3 short testimonial cards (Gruns-style social-proof bar right under hero)
6.  .brand-marquee            — scrolling pull-quote ribbon
7.  .zero-strip-section       — 4 0/0/0 icons (sugar, gluten, alcohol, calories)
8.  .press-strip              — retailer logos row ("Stocked at retailers nationwide")
9.  .can-usps-section         — big rotating can in the middle, 6 USP blocks around it
10. .section-stats            — 4 stats (4.88★, 100k+ sippers, 15min onset, 0/0/0)
11. .section-divider-text     — italic pull-quote divider
12. .product-section #shop    — THE BUY BLOCK (configurator + add-to-cart)
13. .moments-grid             — 4 lifestyle moments specific to the tier
14. .compare table            — Delta vs Other Seltzers vs Wine/Cocktail
15. .pullquote-section        — single hero pullquote block
16. .testi-grid #reviews      — 3 image testimonials + 3 text testimonials
17. .section-deep             — dark "made with the good stuff" CTA
18. .founder-section          — Jack Sherrie quote + photo + secondary CTA
19. .faq-list #faq            — 5 to 7 FAQ details
20. .section-cream            — final "ready when you are" CTA
21. .site-footer              — links, FDA disclaimer, copyright
22. .offcanvas#cartDrawer     — cart drawer (Bootstrap offcanvas)
23. <script>                  — jQuery → bootstrap.bundle → shop.js → sampler-configurator-N.js → animations.js
```

You can drop or reorder a section. You shouldn't add a section without a clear job. Every block earns its scroll.

### Required IDs for cart/configurator wiring
- `#shop` on the product section (anchor for in-page links)
- `#mainImage` on the hero product image
- `#productThumbs` on the gallery thumb row
- `#flavors` on the flavor chip grid
- `.product[data-mode="variety|flavor"]` toggles the configurator state
- `.buy-block` with `data-product-id`, `data-variant-id`, `data-title`, `data-pack-size`, `data-image`, `data-price`
- `.add-to-cart` button inside the buy-block

---

## 6. Anatomy of a Guide LP (`/N-guide`)

Listicle / advertorial format. Designed to read like a Friday-newsletter explainer.

```
1.  <head>                    — title/meta ("X reasons / X nights / X things to know")
2.  .bar                      — same promo strip as sampler
3.  .site-header > .nav       — nav links collapsed: #shop, "All <Tier> Flavors" → /N
4.  .article-hero             — eyebrow ("Garden Seltzer 101/102/103"), H1, lede, byline, stars line, hero image
5.  .trust-badge-row          — Lab-tested · 21+ · Made in USA · 30-day guarantee · Free shipping over $50
6.  <article>.article-body
    6a.  Intro paragraph (1-2 paragraphs, sets up the listicle)
    6b.  Inline btn-coral CTA ("Skip ahead, start with the <Tier> Sampler →")
    6c.  .sampler-reveal      — what's in the box (image + brief)
    6d.  .reason × 5          — numbered list items, each with:
         - .reason-num         (the big numeral)
         - h2                  (the rule / the night)
         - 2-3 paragraphs
         - .reason-img / .reason-asset-square / .reason-asset-portrait
         - optional .pull-quote, .compare-wrap table, .ingredient-card, .stress-grid
    6e.  Mid-funnel .buy-block.buy-block-inline (drop after Reason #3)
    6f.  Continuation reasons #4, #5
    6g.  Offer block with .offer-stack + second .buy-block.buy-block-inline
    6h.  Social proof testimonial block
    6i.  .recap (30-second recap, bullet list, third CTA)
    6j.  Final shop block (h2 + lede + .buy-block.buy-block-inline)
    6k.  FAQ (5 to 7 items, includes a "How is the <Tier> different from..." cross-tier question)
7.  .section-stats            — same 4 stats as sampler (or guide-specific variants)
8.  .press-strip              — retailer logos
9.  .section-cream            — final "make tomorrow morning part of tonight" CTA
10. .site-footer              — same as sampler
11. .offcanvas#cartDrawer
12. <script>                  — jQuery → bootstrap → shop.js → animations.js (NO configurator)
```

### Listicle structure rule

Every numbered item should map to one specific flavor and one specific moment. Generic ("unwind", "relax") underperforms specific ("the bath night, Blood Orange"). The 20-guide refactor that swapped "5 reasons" → "5 nights" is the canonical example: each night is named, has its own image, its own pull-quote, and its own pairing tag.

For the next guide, name the five things first (each with one flavor), then write the prose around them.

### Pairing tag colors
The `.pair-tag` chips on guide H2s are color-coded by course/role:
- `.pair-aperitif` (Pink Lemonade)
- `.pair-apps` (Blueberry Acai)
- `.pair-main` (Passion Fruit)
- `.pair-after` (Blood Orange)

These line up with the 10/20mg flavors. Light-tier guides use a different mapping (the 4 Light flavors don't carry the dinner-course frame).

---

## 7. Shared building blocks

These are the reusable section types. Each has a CSS class that already exists in `site.css`. Use them; don't reinvent.

| Block | Class | Use |
|---|---|---|
| Top promo bar | `.bar` | Discount and shipping above the nav |
| Site header | `.site-header > .nav` | Logo + links + cart |
| Page hero | `.page-hero > .page-hero-grid` | Sampler hero (H1 + lede + price card) |
| Article hero | `.article-hero` | Guide hero (centered, longer lede, byline) |
| Quick testimonial bar | `.quick-testi-bar` | 3 short reviews right under hero |
| Brand marquee | `.brand-marquee` | Auto-scrolling pull-quote ribbon |
| Zero-strip | `.zero-strip-section` | 4 SVG icons (sugar, gluten, alcohol, calories) |
| Trust badge row | `.trust-badge-row > .trust-badges` | Inline trust pills (guide pages) |
| Press strip | `.press-strip > .retailer-row-logos` | Retailer logo row |
| USP grid | `.can-usps-section > .can-usps-grid` | Big rotating can with USP cards |
| Stats grid | `.section-stats > .stats-grid` | 4 stat cards (counted up via animations.js) |
| Section divider | `.section-divider-text > .section-divider-line` | Italic pull-quote that breaks the flow |
| Product section | `.product-section#shop` | Full configurator buy block |
| Inline buy block | `.buy-block.buy-block-inline` | Compact mid-article buy block (guides) |
| Moments grid | `.moments-grid` (with `--four`) | Lifestyle figure cards |
| Compare table | `.compare-wrap > table.compare` or `.nutri-table` | Delta vs alternatives |
| Pullquote | `.pullquote-section > .pullquote-line` | Big italicized hero pullquote |
| Testimonials | `.testi-grid` (with `.testi-grid-3`) | Review cards, image or text |
| Section deep | `.section-deep` | Dark CTA strip (space-blue background) |
| Founder card | `.founder-section > .founder-card` | Jack quote + photo |
| Ingredient card | `.ingredient-card > .ingredient-list` | "Five ingredients" bullet list |
| Stress grid | `.stress-grid > .stress-card` | 3-up icon + label cards (mind/body/sleep style) |
| FAQ | `.faq-list > details.faq-item` | Native `<details>` accordion |
| Recap | `.recap` | Guide page wrap-up bullet list with CTA |
| Cart drawer | `.offcanvas#cartDrawer` | Bootstrap offcanvas, wired by shop.js |

### Animation hooks

`animations.js` wires:
- `.fade-in` and `.fade-in-stagger` → IntersectionObserver, fade up on scroll
- `[data-count-to]` → counted number animations on `.section-stats`
- `#usp-can-rotator[data-rotation='[...]']` → cycles through can images on the USP block

Add `class="fade-in"` to any new block; it'll just work.

---

## 8. Buy block, cart, and checkout wiring

### Sampler variant IDs (truth source)
```
Light Sampler:    productId 8371013451949 · variantId 45102353678509 · $54.37 (was $67.96)
Moderate Sampler: productId 8449097400493 · variantId 45486123516077 · $60.77 (was $75.96)
Bold Sampler:     productId 8449097433261 · variantId 45486167949485 · $70.37 (was $87.96)
```

These are the only Sampler variants. They are read by `shop.js`'s `PRODUCT_IMAGES` map, which forces the cart line image/title/packSize to the Sampler box no matter what was on the page when added. If you change a variant ID, update both the buy-block and the map.

### Buy block markup
```html
<div class="buy-block"
     data-product-id="8449097433261"
     data-variant-id="45486167949485"
     data-title="The Bold Sampler Pack"
     data-pack-size="16-Pack · 4 flavors"
     data-image="/assets/images/sampler/20mg/hero.jpg"
     data-price="70.37">
  <!-- ...inputs and config UI... -->
  <button type="button" class="btn btn-primary add-to-cart" data-cta-static="1">Add to Cart · $70.37</button>
</div>
```

For inline guide buy blocks, add `class="buy-block buy-block-inline"` and `data-cta-static="1"` on the button (so shop.js doesn't auto-rewrite the label when the price doesn't change).

### Cart and checkout flow
1. `shop.js` listens on `.add-to-cart`, reads `data-*`, writes to `localStorage` (`delta_v2_cart`).
2. Cart drawer (`.offcanvas#cartDrawer`) renders from localStorage.
3. Checkout link is built as a Shopify cart-permalink against `bfac5a-e3.myshopify.com`, with `?discount=NEW20` appended. Single-line carts with a `sellingPlanId` add `&selling_plan=...`.
4. There is no server-side cart. Refresh persists state, checkout is a redirect.

### Discount code
`NEW20` = 20% off first order. Auto-applies via the permalink. The code is hardcoded in `shop.js` (`DISCOUNT_CODE = 'NEW20'`) and quoted in the bar, hero copy, FAQ, and product foot. If it ever changes, update all five places.

### Subscribe & save (Sampler page only)
Uses `data-selling-plan-id` on the buy-block. Plan IDs:
- Bundle (Sampler): `3776938228` (every 4 weeks, default)
- Individual flavor: `2698674349` (every 2 weeks)

The Sampler one-time vs subscribe toggle is in `sampler-configurator{,-10,-20}.js`. The variety pack is *one-time only* on the Sampler — the configurator hides the subscribe card when `data-mode="variety"`. Subscriptions only kick in when the user picks an individual flavor.

---

## 9. Imagery system

### Asset categories (per tier)
```
/assets/images/cans/<tier>-rotation/<flavor>.png  → rotation source for #usp-can-rotator
/assets/images/cans/<tier>/<flavor>.jpg           → flavor chip thumbnails
/assets/images/sampler/<tier>/                    → hero.jpg, 1.jpg…6.jpg, hero-lifestyle.png
/assets/images/lifestyle-<tier>/                  → moments. Numbered M1..M5 are the canonical "5 moments" set.
```

### Sampler hero
`/N` page hero is a *lifestyle* shot, not a stack of cans. The pattern:
- Light → garden table / ice bucket (`hero-5mg-garden-table.png`)
- Moderate → kitchen island at evening with friends in soft focus (`sampler/10mg/hero-lifestyle.png`)
- Bold → bath caddy with all 4 cans across a tub (`lifestyle-20mg/hero.png`)

The lesson from the /20 hero swap: a single can doesn't carry the variety story above the fold. Lead with all 4 flavors on a meaningful surface. Variety pack is the default offer; show it.

### Guide hero
`/N-guide` page hero is the *Sampler box* itself (`/assets/images/sampler/<tier>/hero.jpg`). The product is the lede; the article is the explainer.

### Lifestyle naming
The `M1.png … M5.png` files in `lifestyle-20mg/` and `lifestyle-10mg/` are the canonical "5 moments" set used by the guides. When generating new lifestyle imagery for a future LP, use the `generate-lifestyle-*.mjs` scripts as a template. Each script writes prompts that produce 5 numbered moments + alternates. Run them with the FAL API token.

### NO-THC rule
Every can rendered or photographed for an LP must use the NO-THC variant. The `NO THC/` directory holds the brand-approved sources. When generating new imagery, the prompt must specify "no THC text on the can, brand label only." This is Meta-ad compliance.

### Image format and loading
- `.png` for renders with transparency or text on can
- `.jpg` for photography and lifestyle
- `loading="lazy" decoding="async"` on everything below the fold
- `fetchpriority="high"` on the hero image only
- Provide `width` and `height` to avoid layout shift

---

## 10. Copy patterns and signature lines

### Hero formula (Sampler)
- **Eyebrow** (12-14 chars): tier + "21+" or sub-persona name
- **H1** (2-line): observation + emphasized closer. Pattern: `<H1>Hook line.<br><span class="hero-h1-emph">Closer.</span></H1>`
- **Lede** (1-2 sentences): the offer in plain English
- **Triple-zero line**: `0 cal · 0 sugar · 0 alcohol`
- **CTA row**: primary button + savings note ("Save $X with code NEW20")
- **Mini trust strip**: "Free shipping over $50 · 30-day guarantee · Stocked in 30+ states"

### Hero formula (Guide)
- **Eyebrow**: "Garden Seltzer 101/102/103 · <subtitle>"
- **H1**: declarative listicle title ("The 5 Things to Know..." / "5 reasons your most social friends are drinking this." / "5 nights the Bold was built for.")
- **Lede**: 1-2 sentences, second-person, sets up the format
- **Byline**: "By **The Delta team** · 5 min read · Updated for 2026"
- **Stars line**: `★★★★★ 4.88 from 13,458 verified buyers · 100k+ sippers`
- Hero image: the Sampler box

### Voice rules
- Plain English. No pharma stiffness. No clinical claims.
- Short declarative sentences. The em-dash flourish is out (project rule).
- Second person ("you", "your") for guides; third person or product-voice for sampler.
- "Sip", "lift", "vibe", "edge", "smooth", "balanced", "sessionable" are the working vocabulary.
- "Drink less. Drink better." / "Sip slow. Set the pace." / "There's a Delta for that." are the canonical pull-quotes.

### The trust constants (use verbatim)
- `4.88` average rating
- `13,458` verified reviews (also seen as `13,741+` brand-wide; pick one and use it consistently within a single page)
- `100,000+` sippers (or `100k+`)
- `30+` states
- `15` minute onset (also seen as 10-20 min in long-form)
- `5` ingredients (water, plant extract, citric acid, natural fruit flavor, stevia)

### Founder
- Jack Sherrie, Founder & CEO
- One-line quote: "I built Delta because I wanted a drink that felt better in my body and didn't wreck the next day."
- Photo: `/assets/images/founder/jack.jpg`

### Guarantee
- Always called "**30-day Sip-It-and-Love-It refund**"
- Always paired with: "If you don't love your first can, we refund the full pack. No questions."

---

## 11. Pricing and offer math

### Sampler pricing (canonical)
```
Tier      OneTime    NEW20 (-20%)  Per-can OT  Per-can NEW20
Light     $67.96     $54.37        $4.25       $3.40
Moderate  $75.96     $60.77        $4.75       $3.80
Bold      $87.96     $70.37        $5.50       $4.40
```

All Samplers: 16 cans · 4 flavors · 4-pack of each.

### Display rule
The hero `.hero-promo-card` always shows the *strike* price and the *NEW20 price*. The `.add-to-cart` label always reflects the user-facing post-discount price. The strike price is NEVER the actual cart price — `NEW20` auto-applies at checkout via the permalink.

### Offer stack (used in guide pages)
```
Sampler · 16 cans · 4 flavors          $XX.XX
First-order discount, code NEW20       −$YY.YY
Free shipping over $50                 Included
30-day Sip-It-and-Love-It Guarantee    Included
Your price today                       $ZZ.ZZ
```

### Subscribe pricing
Individual flavors only. 20% off, free shipping over $50, swap/skip/cancel anytime. The subscribe option does NOT show on the Sampler bundle (variety mode).

---

## 12. How to add a new LP

The fastest path is "clone the closest existing LP, replace the angle." Most of what differs between `/5` and `/10` is hero image + H1 + a few moment captions + the configurator file reference + the variant ID.

### Steps for a new tier-page (e.g. `/sleep`, `/social-occasion`)

1. **Pick the archetype.** Sampler-style (configurator + closing) or Guide-style (listicle + advertorial)?
2. **Pick the tier.** Map the new page to Light, Moderate, or Bold. The tier dictates the buy block, the colors of the pair-tags, the flavor lineup, and the per-can math.
3. **Create the directory.** `mkdir <route>` and `cp <closest-existing>/index.html <route>/index.html`.
4. **Add the rewrite to `vercel.json`.**
   ```json
   { "source": "/<route>", "destination": "/<route>/index.html" }
   ```
5. **Replace these strings:**
   - `<title>` and `<meta name="description">`
   - Hero eyebrow, H1, lede
   - Hero image (`page-hero-img > img` or `article-hero-img > img`)
   - Moment grid captions (4 to 5 of them)
   - Founder quote attribution stays the same
   - FAQ (keep "How is the X different from..." up to date)
   - Final CTA copy
6. **Update buy-block data-attrs.** Pull the right variant ID from §8.
7. **Pick the configurator script** (`sampler-configurator{,-10,-20}.js`) by tier. Guide pages don't need a configurator.
8. **Bump the `?v=...` cache-bust** on `site.css` and any shared JS.
9. **Run the pre-ship checklist** (§13).
10. **Add a card to `/index.html`** (the SelfMade-facing showcase grid). Match the existing pattern: `data-tier`, `card-eyebrow`, `h3`, `h1quote`, `thesis`, `.proof-row × 2`, `.tags`. Don't skip — that grid is how the work gets reviewed.

### Steps for a new sub-persona page (e.g. `/social-occasion`, `/deal`, `/sugar-free`)

These come from the [DELTA_LANDING_PAGE_BRIEF.md](DELTA_LANDING_PAGE_BRIEF.md) recommendations. Same archetype choice, but the angle is the sub-persona, not the tier. Pick the tier the angle maps best to (often Moderate for social/lifestyle, Light for first-timers, Bold for sleep/wind-down) and follow the steps above.

The angle changes:
- The hero copy
- The eyebrow (sub-persona name)
- The moments grid (each moment is a sub-persona scene)
- The FAQ (questions the sub-persona is asking)

The buy block, color tokens, footer, FAQ tail, retailer strip, and stats block stay the same.

---

## 13. Pre-ship checklist

Run these every time before pushing.

### Compliance
- [ ] No `\d+mg` in user-visible copy. Grep: `grep -nE '[0-9]+mg' <route>/index.html` (filenames in `src=` and `href=` are fine — review case by case)
- [ ] No em dashes. Grep: `grep -nE '[—–]' <route>/index.html`
- [ ] No "THC", "high", "buzz", "stoner", "weed" in body copy
- [ ] 21+ marker visible in hero or trust strip
- [ ] FDA disclaimer in footer
- [ ] All can imagery is NO-THC variant

### Wiring
- [ ] `<title>` and `<meta name="description">` are page-specific
- [ ] All `data-*` on the buy-block point to the right product/variant/price/image
- [ ] `add-to-cart` button label matches `data-price`
- [ ] All internal anchors (`#shop`, `#faq`, `#reviews`) resolve
- [ ] `vercel.json` has the rewrite for the new route
- [ ] `?v=...` bumped on shared CSS/JS if those files changed
- [ ] Cart drawer offcanvas is present (`#cartDrawer`)

### Content
- [ ] Hero has H1, lede, 0/0/0 line, CTA, mini-trust
- [ ] Guarantee phrasing matches: "30-day Sip-It-and-Love-It refund"
- [ ] Pull-quotes are from the [copy-reference-5-10-20.md](copy-reference-5-10-20.md) bank
- [ ] Trust constants match across the page (4.88, 13,458, 100k+)
- [ ] FAQ has 5-7 items including a cross-tier question
- [ ] Each lifestyle image has descriptive alt text
- [ ] Founder card and quote present (sampler) or skipped intentionally (guide)
- [ ] Final CTA section before footer

### Visual
- [ ] Hero image has `width`, `height`, `fetchpriority="high"`
- [ ] All other images have `loading="lazy" decoding="async"`
- [ ] Tier accent colors are consistent (Light=forest, Moderate=mustard, Bold=carmine)
- [ ] No sticky bottom buy bar

### Test (manual, in browser)
- [ ] Page loads, no console errors
- [ ] Hero CTA scrolls to `#shop`
- [ ] Configurator: switching flavor, pack, subscribe/onetime updates the `add-to-cart` price label and the data-attrs
- [ ] Add to Cart writes to localStorage and pops the drawer
- [ ] Drawer "Checkout" link goes to `bfac5a-e3.myshopify.com/checkout?discount=NEW20...`
- [ ] FAQ details open/close
- [ ] All retailer logos load
- [ ] Mobile viewport: hero stacks, configurator works, no horizontal scroll

---

## 14. Common mistakes (already learned)

These are mistakes that have shipped at least once and been corrected. Don't repeat.

- **Generic listicle items.** "5 reasons" with abstract reasons ("relax", "unwind") underperformed "5 nights" with named scenes (the bath, the record, the book, the balcony, the closer) on `/20-guide`. Specificity converts.
- **Single-can hero on a variety-pack page.** The `/20` original hero showed one can — the variety story didn't carry above the fold. Swapped to all 4 cans on a bath caddy. Lesson: when the offer is the Sampler, the hero shows the Sampler.
- **Prescriptive scene labels on flavor cards.** The `/20` "moments" originally had cards labeled with the use case ("the bath night, Blood Orange") — too on-the-nose for a sampler PDP. Stripped down to flavor names; the visual + descriptive copy carries the moment frame.
- **Prescriptive anatomy in moment images.** A "leg-anatomy" lifestyle image on `/20-guide` Reason 1 was off-brand. Swapped for the multi-can bath caddy shot. Lesson: stick to environment-led imagery, not body-part-led.
- **`.sticky-buy` carry-over.** Cloned LPs inherited the bottom sticky add-to-cart bar from `/5`. Removed on `/10` per feedback; don't add it to new pages.
- **"mg" in copy.** Shipped on `/10` ("How is 10mg different from 5mg or 20mg?") — corrected to tier names. Run the grep.
- **Em dashes.** Snuck back into `/10` copy; project rule violation. Run the grep.
- **`hero.png` referenced when it doesn't exist.** `/20` originally pointed at a missing file; fixed to `M1-bath-caddy.png`. Verify the actual file is staged before pointing at it.

---

## 15. Future LP ideas (from the brief)

Pulled from [DELTA_LANDING_PAGE_BRIEF.md](DELTA_LANDING_PAGE_BRIEF.md), ranked by paid-media-data confidence. Each one has a tier, an archetype, and an angle.

| Route | Tier | Archetype | Angle | From brief |
|---|---|---|---|---|
| `/social-occasion` | Moderate | Sampler | "What you reach for when everyone else is drinking" | Sober Curious × Social-Occasion sub-persona, 1.05x ROAS, $9.6k spend |
| `/deal` | Light | Sampler | Stacked-discount math, $10-off-first-order, "Sale Stacked" aesthetic | Deal-Seeking sub-persona, 1.76x ROAS (highest under-indexed) |
| `/sugar-free` | Light or Moderate | Guide | Nutrition-forward, Passion Fruit / Lemon / Orange flavor focus | Clean Label × Joy, 4.81x ROAS sustained |
| `/dry-january` | Light | Guide | Seasonal (Dec/Jan), 31-day challenge mechanic | Sober Curious × Dry-January, $444k lifetime |
| `/regional/<city>` | All | Sampler | Geo-gated, "Now available in <city>" | Region-Locked Deal Seekers, 1.36x ROAS |
| `/guilt-free` | Moderate | Sampler | "Guilt-free indulgence", joyful tone, "Delta Bag + Something" bundle | Currently active winner, 4.31x ROAS |
| `/wind-down` | Bold | Guide | Pre-bedtime ritual, Blood Orange-led, sleep frame | Sober Curious × Sober protocol play |

The brief has the full ROAS table, sub-persona breakdowns, and creative references for each. Build orders should follow ROAS efficiency, not volume.

---

## Appendix: file template starting points

When starting a new LP, the closest match is usually:

| New LP type | Clone from |
|---|---|
| Sampler for a new sub-persona | `/10/index.html` (most refined Sampler structure) |
| Guide for ritual / wind-down / Bold-adjacent | `/20-guide/index.html` (cleanest 5-item structure) |
| Guide for first-timer / curiosity | `/5-guide/index.html` |
| Guide for hosting / social | `/10-guide/index.html` |
| Sampler with a configurator | `/5/index.html` (simplest configurator wiring) |

Then: replace the angle, swap the buy block, run the checklist, ship.

---

## 16. Shopify IDs (truth source from production go.drinkdelta.com)

These are the actual Shopify product IDs, variant IDs, and selling plan IDs in use on production. Pulled live from `go.drinkdelta.com/assets/js/custom.js`. Use them verbatim.

### Storefront

```
Shopify shop domain: bfac5a-e3.myshopify.com
Products API:        https://bfac5a-e3.myshopify.com/products.json
Cart base:           https://bfac5a-e3.myshopify.com/cart
Checkout base:       https://bfac5a-e3.myshopify.com/checkout
Subscription engine: Stay AI
Stay AI widget:      https://cdn.stay-ai.com/widget/stay-widget.js
Stay AI API token:   wnz46gq-ohgumla-v73w4di-fgc5cvq
```

### Sampler bundle products (the three core SKUs)

| Tier | Internal | Bundle product ID | One-time price | Per-can |
|---|---|---|---|---|
| Light | 5mg | `8371013451949` | $69.96 | $4.37 |
| Moderate | 10mg | `8449097007277` | $75.96 | $4.75 |
| Bold (a.k.a. "Experience") | 20mg | `8449097433261` | $87.96 | $5.50 |

Production calls the 20mg "Experience" in the strength-pill `data-strength` attribute. User-facing copy still uses "Bold". Don't break the data attribute, don't show the word "Experience" to users.

### Sampler bundle variant IDs (V2 only — confirm in Shopify before any new LP)

```
Light Sampler variant:    45102353678509
Moderate Sampler variant: 45486123516077
Bold Sampler variant:     45486167949485
```

These are the variant IDs already wired into V2's `assets/js/shop.js` `PRODUCT_IMAGES` map. Production uses bare `product_id` attributes and resolves variants via the `/products.json` API at runtime, so the production HTML doesn't expose variant IDs directly — V2 is more explicit and that's fine.

### Individual flavor variants (4-pack and 12-pack)

#### Light (5mg) flavors — productId `8371013451949`
```
Bright Berry      4-pack: 43706600226989    12-pack: 43476426260653
Squeeze of Lime   4-pack: 43706638991533    12-pack: 43644594618541
Tropical Mango    4-pack: 43706652000429    12-pack: 43644594421933
Juicy Watermelon  4-pack: 43706563559597    12-pack: 43644594520237
```
Pricing for all Light flavors: `4-pack $16.99 OT / $13.59 sub` ($4.25/can OT, $3.40/can sub) · `12-pack $44.99 OT / $35.99 sub` ($3.75/can OT, $3.00/can sub)

#### Moderate (10mg) flavors — productId `8449097007277`
```
Passion Fruit     4-pack: 44305077665965    12-pack: 44305077698733
Blood Orange      4-pack: 44305076453549    12-pack: 44305076486317
Pink Lemonade     4-pack: 44305071112365    12-pack: 44305071145133
Blueberry         4-pack: 44305054793901    12-pack: 44305054826669
```

#### Bold (20mg) flavors — productId `8449097433261`
```
Passion Fruit     4-pack: 43644594290861    12-pack: 44031199215789
Blood Orange      4-pack: 43644594356397    12-pack: 43916737282221
Pink Lemonade     4-pack: 43644596551853    12-pack: 44031200755885
Blueberry         4-pack: 43644596715693    12-pack: 43644596748461
```

#### Cannabis Spirit (separate product, only used on `/bloodorange` route)
```
productId:  8460535595181
variantId:  45708036374701
title:      Blood Orange Spirit
price:      $59.00
pack:       1 Bottle
image:      https://cdn.shopify.com/s/files/1/0661/4014/3789/files/slider1-spiritbottle-award.jpg?v=1777403862
```

### Selling plan IDs

Two sets are in play. They map to *different* Shopify subscription configurations.

**Production (`go.drinkdelta.com`) — generic Stay AI plans for individual flavors:**
```js
const SELLING_PLAN_IDS = {
  every_week:     '2698510509',
  every_2_weeks:  '2698674349',   // also: V2 SUBSCRIBE_PLAN_FLAVOR
  every_4_weeks:  '1892745389',   // production's "every 4 weeks" / "every month"
  every_month:    '1892745389',
  every_6_weeks:  '2698707117',
  every_8_weeks:  '3462889645',
  every_10_weeks: '3462922413'
};
```

**V2 (`/5`, `/10`, `/20`) — bundle-specific plans, used for the Sampler subscribe flow only:**
```js
const SUBSCRIBE_PLAN_BUNDLE = '3776938228'; // bundle: every 4 weeks (default)
// frequency dropdown options:
//   3776938228  Every 4 Weeks
//   3776970996  Every 6 Weeks
//   3776970997  Every 8 Weeks
//   3776970998  Every Month
const SUBSCRIBE_PLAN_FLAVOR = '2698674349'; // individual flavors: every 2 weeks
```

If you build a new LP that subscribes the Sampler bundle, use the V2 set (`3776...`). If you subscribe an individual flavor, the V2 code uses `2698674349` (the "every 2 weeks" plan that's also valid in production).

### Discount codes (THIS IS WHERE V2 AND PRODUCTION DIVERGE)

```
Production go.drinkdelta.com:  NEW   (20% off first order)
V2 (/5, /10, /20, *-guide):    NEW20 (20% off first order)
```

The V2 codebase hardcodes `NEW20` in `shop.js` (`DISCOUNT_CODE = 'NEW20'`), in body copy, in the bar, in the FAQ, and in the buy block. Production uses bare `NEW` per route in `ROUTE_CONFIGS`. Don't mix them. When you ship a new LP under V2, use `NEW20`. When you sync something back into production, use `NEW`.

### Cart storage keys

```
Production: drink_delta_cart  (localStorage key in custom.js)
V2:         delta_v2_cart     (localStorage key in shop.js)
```

Different keys mean V2 and production carts don't collide for users browsing both, but they also don't share state. That's intentional.

### Other product IDs seen in production code (for reference)

```
9291389534452  "Sampler Pack Pro" / promo-only sampler — used by isSamplerPack() in custom.js
8460535595181  Blood Orange Spirit (Cannabis Spirit)
```

---

## 17. Production code patterns (HTML, JS, checkout)

This is the production HTML/JS shape from `go.drinkdelta.com`. V2 follows the same patterns but with cleaner data-attribute names. When in doubt, copy production verbatim — it's what Shopify expects.

### Strength selector (the 3 tier pills)

Production uses `data-strength` (`light` | `moderate` | `experience`) and `data-product-id` directly on the buttons:

```html
<div class="d-flex gap-2 mb-3 strength-selector">
  <button class="strength-pill active"
          data-strength="light"
          data-product-id="8371013451949">Light</button>
  <button class="strength-pill"
          data-strength="moderate"
          data-product-id="8449097007277">Moderate</button>
  <button class="strength-pill"
          data-strength="experience"
          data-product-id="8449097433261">Experience</button>
</div>
```

Note: production uses `data-strength="experience"` for what users see as "Bold". Don't change the attribute, change the label.

### Pack-quantity radio group

Production uses unscoped attributes (`variant_price`, `product_id`, `variation`) on the input itself:

```html
<div class="quantity-toggle-group variant" role="radiogroup">
  <div>
    <input class="btn-check quantity-input"
           variant_price="69.96"
           product_id="8371013451949"
           variation=""
           type="radio"
           name="packQuantity"
           id="pack-default"
           value="16" checked>
    <label class="pill quantity-pill" for="pack-default">16-Pack</label>
  </div>
</div>
```

V2's `data-pack` and `data-price` on the buy-block do the same job in a more explicit way. Either pattern works.

### Subscribe vs One-time card

Production uses radios with a `selling_plan` attribute and a `<select>` for the frequency:

```html
<div class="selling_plans">
  <div class="subscribe-banner">SUBSCRIBE & SAVE 20%</div>
  <div class="option-card subscription active">
    <input class="form-check-input"
           selling_plan="3776938228"
           plan_price="57.60"
           type="radio"
           name="purchaseOption"
           value="subscribe" checked>
    <select class="form-select">
      <option value="3776938228">Every 4 Weeks</option>
      <option value="3776970996">Every 6 Weeks</option>
      <option value="3776970997">Every 8 Weeks</option>
      <option value="3776970998">Every Month</option>
    </select>
  </div>

  <div class="option-card one_time_purchase">
    <input class="form-check-input"
           selling_plan=""
           plan_price="69.96"
           type="radio"
           name="purchaseOption"
           value="one-time">
  </div>
</div>
```

Frequency options on the production `/5` page use the V2 bundle plan IDs (`3776...`), not the `1892...` set. Both ID sets are valid in Shopify — they correspond to different subscription products. The bundle uses `3776...`, individual flavors use `2698.../1892...`.

### The buy block (V2 canonical pattern)

```html
<div class="buy-block"
     data-product-id="8449097433261"
     data-variant-id="45486167949485"
     data-title="The Bold Sampler Pack"
     data-pack-size="16-Pack · 4 flavors"
     data-image="/assets/images/sampler/20mg/hero.jpg"
     data-price="70.37">

  <!-- Pack pills -->
  <div class="pack-row">
    <button class="pack-pill pack-pill-variety active" data-pack="16">
      <span class="pack-name">16-Pack</span>
    </button>
    <button class="pack-pill pack-pill-flavor" data-pack="4">
      <span class="pack-name">4-Pack</span>
      <span class="pack-price">$13.59</span>
    </button>
    <button class="pack-pill pack-pill-flavor" data-pack="12">
      <span class="pack-name">12-Pack</span>
      <span class="pack-price">$35.99</span>
    </button>
  </div>

  <!-- Subscribe / One-time -->
  <div class="purchase-row">
    <button class="purchase-card purchase-card-subscribe active" data-purchase="subscribe">
      <select class="purchase-frequency">
        <option value="3776938228" selected>Every 4 Weeks</option>
        <option value="3776970996">Every 6 Weeks</option>
        <option value="3776970997">Every 8 Weeks</option>
        <option value="3776970998">Every Month</option>
      </select>
    </button>
    <button class="purchase-card purchase-card-onetime" data-purchase="onetime">
      <!-- ... -->
    </button>
  </div>

  <button type="button" class="btn btn-primary btn-lg btn-block add-to-cart">
    ADD TO CART · $70.37
  </button>
</div>
```

For the inline guide-page version, the buy-block is wrapped with `class="buy-block buy-block-inline"` and the configurator UI is dropped — just the data attributes and the CTA. Add `data-cta-static="1"` on the button so `shop.js` doesn't auto-rewrite the label.

### Bundle buy-block (multiple variants in one cart line)

Used when a single CTA needs to add several individual flavor 4-packs at once. The format is `data-bundle-variants="<variantId>:<qty>,<variantId>:<qty>,..."`.

```html
<div class="buy-block"
     data-product-id="bundle"
     data-variant-id="custom-variety-pack"
     data-title="Build Your Own Sampler"
     data-pack-size="16-Pack · 4 flavors"
     data-image="/assets/images/sampler/10mg/hero.jpg"
     data-price="60.77"
     data-bundle-variants="44305077665965:1,44305076453549:1,44305071112365:1,44305054793901:1">
  <!-- ... -->
</div>
```

`shop.js` parses `data-bundle-variants` into individual cart-add lines at checkout time:

```js
var bundleAttr = $block.attr('data-bundle-variants');
if (bundleAttr) {
  bundleVariants = bundleAttr.split(',').map(function (pair) {
    var parts = pair.trim().split(':');
    return { variantId: parts[0], qty: parseInt(parts[1] || '1', 10) };
  });
}
```

### Add-to-cart handler (production custom.js, simplified)

```js
$('.add-to-cart').on('click', function () {
  const productId   = $(this).attr('product_id');
  const variationId = $(this).attr('variation');
  const title       = $('.product_title').text();
  const variantVal  = $(`input[variation="${variationId}"]`).val() || '';
  const packSize    = /^\d+$/.test(variantVal) ? variantVal + '-PACK' : variantVal;

  // Bundle/Variety mode: one-time only, no selling plan
  const BUNDLE_IDS = [8371013451949, 8449097007277, 8449097433261];
  const isBundleProduct  = BUNDLE_IDS.includes(Number(productId));
  const isVarietySelected = $('.mc-variety-pill').hasClass('active');

  let price, sellingPlanId;
  if (isBundleProduct && isVarietySelected) {
    price = getCurrentVariantPrice();
    sellingPlanId = null;
  } else {
    price = parseFloat($('.option-card input:checked').attr('plan_price').replace('$', ''));
    sellingPlanId = $('.selling_plans input:checked').attr('selling_plan') || null;
  }

  const image = $('#mainImage').attr('src');

  const existing = cart.find(item =>
    item.variationId === variationId && item.sellingPlanId === sellingPlanId
  );
  if (existing) {
    existing.quantity += 1;
  } else {
    cart.push({ productId, variationId, title, packSize, price, image, quantity: 1, sellingPlanId });
  }
  saveAndRender();
});
```

### Checkout permalink (production)

This is the magic. Shopify lets you stack a discount + cart-add chain into one URL. Production builds it like this:

```js
const baseUrl = 'https://bfac5a-e3.myshopify.com';
let finalReturn = '/checkout';

cart.forEach(item => {
  let addUrl = `/cart/add?id=${item.variationId}&quantity=${item.quantity}`;
  if (item.sellingPlanId) addUrl += `&selling_plan=${item.sellingPlanId}`;
  addUrl += `&return_to=${encodeURIComponent(finalReturn)}`;
  finalReturn = addUrl;
});

const cartClearPath = `/cart/clear?return_to=${encodeURIComponent(finalReturn)}`;
const Checkout_URL = ROUTE_CONFIG?.discountCode
  ? `${baseUrl}/discount/${encodeURIComponent(ROUTE_CONFIG.discountCode)}?redirect=${encodeURIComponent(cartClearPath)}`
  : `${baseUrl}${cartClearPath}`;

$('.offcanvas a.checkout').attr('href', Checkout_URL);
```

The chain reads as:
```
1. Hit /discount/NEW       → cookie-applies the discount
2. Redirect to /cart/clear → empties any pre-existing cart
3. Then /cart/add (line 1) → adds first item
4. Then /cart/add (line 2) → adds second item
... (chained via return_to)
N. Final /checkout         → user lands at checkout with cart populated and discount applied
```

V2's `shop.js` builds the same URL with `?discount=NEW20` as a query param on the final permalink — slightly different shape, same outcome.

### Route-config pattern (production custom.js)

Production routes (`/5`, `/10`, `/20`, `/bloodorange`) all serve the same `index.html`, with the per-route experience driven by a config object keyed on `window.location.pathname`:

```js
const ROUTE_CONFIGS = {
  '/5': {
    strength: 'light',
    hideStrengthSelector: true,
    offerBanner: 'Flat $5 shipping on orders under $50',
    discountCode: null,
    notSure: { headline: '...', subhead: '...', body: '...', ctaText: '...' }
  },
  '/10': {
    strength: 'moderate',
    hideStrengthSelector: true,
    offerBanner: 'NEW? Save 20% with code: NEW',
    discountCode: 'NEW',
    notSure: { /* ... */ }
  },
  '/20': {
    strength: 'experience',
    hideStrengthSelector: true,
    offerBanner: 'NEW? Save 20% with code: NEW',
    discountCode: 'NEW',
    notSure: { /* ... */ }
  },
  '/bloodorange': {
    productId: 8460535595181,
    variantId: 45708036374701,
    hideStrengthSelector: true,
    hideFlavorPills: true,
    hideSubscription: true,
    productTitleOverride: 'Blood Orange Spirit',
    productDescriptionOverride: 'A zero-proof spirit ...',
    bottomImage: 'assets/images/5_product-detail-D28zw9Gq.webp',
    offerBanner: 'NEW? Save 20% with code: NEW',
    discountCode: 'NEW',
    notSure: { /* ... */ }
  }
};
const ROUTE_PATH = window.location.pathname.replace(/\/+$/, '') || '/';
const ROUTE_CONFIG = ROUTE_CONFIGS[ROUTE_PATH] || null;
```

Production's `vercel.json` rewrites all four paths back to `/index.html`. The route-config object is what makes `/5` render differently from `/20` despite serving the exact same HTML. Three CSS classes are then toggled on `<body>`:

```js
if (ROUTE_CONFIG?.hideStrengthSelector) $('body').addClass('route-locked-dose');
if (ROUTE_CONFIG?.hideFlavorPills)      $('body').addClass('route-no-flavor-pills');
if (ROUTE_CONFIG?.hideSubscription)     $('body').addClass('route-no-subscription');
```

Pattern to copy: **don't fork HTML files for each route — fork a route-config object and let CSS classes do the rest.** V2 chose the opposite (separate `index.html` per route) because the Sampler vs Guide split required diverging structure, not just diverging copy. Both are valid. For a future lightweight variant (e.g. `/social-occasion` that's *almost* the same as `/10`), use the route-config pattern.

### Stay AI subscription widget (production initializes it like this)

```html
<script>
  window.StayAiConfig = {
    apiToken: 'wnz46gq-ohgumla-v73w4di-fgc5cvq',
    shop:     'bfac5a-e3.myshopify.com'
  };
</script>
<script src="https://cdn.stay-ai.com/widget/stay-widget.js" defer></script>
```

V2 doesn't load the Stay AI widget — V2 builds the subscribe-card UI in HTML directly and just passes the selling-plan ID through to Shopify at checkout. If you ever need to expose the full Stay AI subscriber portal (account management, swap, skip), wire in the widget. Otherwise V2's lightweight pattern is enough.

### Vercel rewrite pattern (V2 vercel.json)

```json
{
  "version": 2,
  "cleanUrls": true,
  "trailingSlash": false,
  "rewrites": [
    { "source": "/5",        "destination": "/5/index.html" },
    { "source": "/5-guide",  "destination": "/5-guide/index.html" },
    { "source": "/10",       "destination": "/10/index.html" },
    { "source": "/10-guide", "destination": "/10-guide/index.html" },
    { "source": "/20",       "destination": "/20/index.html" },
    { "source": "/20-guide", "destination": "/20-guide/index.html" }
  ],
  "headers": [
    {
      "source": "/assets/(.*)\\.(png|jpg|jpeg|webp|avif|gif|svg|ico|woff|woff2|ttf|otf|eot)",
      "headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
    },
    {
      "source": "/assets/(.*)\\.(js|css|mjs|map)",
      "headers": [{ "key": "Cache-Control", "value": "public, max-age=0, must-revalidate" }]
    }
  ]
}
```

When adding a new route (e.g. `/social-occasion`), append to the rewrites array. Asset cache headers are aggressive on images/fonts (`immutable`, 1 year) and conservative on JS/CSS (`must-revalidate`, 0). That's why the `?v=YYYYMMDDx` cache-bust on `site.css` matters: assets are immutable-cached, so the URL must change to force a re-fetch.

---

## 18. V2 vs production: the deltas worth knowing

V2 is *not* a clone of production. It's a different codebase serving different acquisition routes. Knowing where they diverge prevents bugs.

| Aspect | Production go.drinkdelta.com | V2 (this repo) |
|---|---|---|
| Routes | `/5`, `/10`, `/20`, `/bloodorange` | `/5`, `/5-guide`, `/10`, `/10-guide`, `/20`, `/20-guide` |
| HTML | One `index.html`, route-config object | One `index.html` per route |
| Discount code | `NEW` | `NEW20` |
| Cart localStorage key | `drink_delta_cart` | `delta_v2_cart` |
| Subscription widget | Stay AI widget loaded (`stay-widget.js`) | Custom HTML + selling-plan-id passthrough |
| Bundle subscription IDs | `3776938228` etc. | `3776938228` etc. (same — they share the bundle product) |
| Flavor subscription IDs | `1892745389` (4w), `2698510509` (1w), `2698674349` (2w) etc. | `2698674349` (2w) only |
| Product fetch | `/products.json` API call at runtime | Hardcoded variant maps in `shop.js` and configurator JS |
| Strength label internal | "experience" for 20mg | "bold" for 20mg (in V2 CSS data-tier and copy) |
| Tier name shown to user | "Light / Moderate / Experience" — production drift | "Light / Moderate / Bold" — V2 follows brand brief |
| Compliance ("mg" in copy) | Some legacy "5/10/20" framing remains | Stripped out, project rule enforced |
| Sticky add-to-cart bar | Not present | Removed from new LPs; `/5` legacy still has it |
| Top promo bar copy | Per-route via ROUTE_CONFIG | Hardcoded in HTML |
| Guide / advertorial pages | Not present | Three (`/5-guide`, `/10-guide`, `/20-guide`) |

### When to sync something back into production

If a V2 LP wins and we want to ship the angle on production:
1. Port the *copy* and *imagery*, not the route-shape.
2. Add a new entry to production's `ROUTE_CONFIGS` keyed by the new path.
3. Use production's discount code (`NEW`), not V2's (`NEW20`).
4. Use production's cart key (`drink_delta_cart`) — don't introduce a second.
5. Use the `1892745389` (4-week) plan for individual-flavor subscribes on production. V2's `3776...` plans are bundle-only.

### When to leave production alone

Production serves established acquisition flows with proven creative. Don't touch it for tests — that's what V2 (this repo) is for. V2 is the lab. Production is the store.

---

## Appendix A: full ID cheat sheet (paste-ready)

Drop this into the head of any new V2 page or buy-block reference.

```js
// Shopify
const SHOPIFY_DOMAIN = 'bfac5a-e3.myshopify.com';
const DISCOUNT_CODE  = 'NEW20'; // V2 only. Production uses 'NEW'.
const STORAGE_KEY    = 'delta_v2_cart';

// Sampler bundle products
const SAMPLERS = {
  light:    { productId: '8371013451949', variantId: '45102353678509', oneTime: 67.96, subscribe: 54.37, perCanOT: 4.25, perCanSub: 3.40 },
  moderate: { productId: '8449097007277', variantId: '45486123516077', oneTime: 75.96, subscribe: 60.77, perCanOT: 4.75, perCanSub: 3.80 },
  bold:     { productId: '8449097433261', variantId: '45486167949485', oneTime: 87.96, subscribe: 70.37, perCanOT: 5.50, perCanSub: 4.40 }
};

// Selling plans (V2 bundle subscription)
const SUBSCRIBE_PLAN_BUNDLE = '3776938228'; // every 4 weeks (default)
const BUNDLE_FREQUENCIES = {
  '3776938228': 'Every 4 Weeks',
  '3776970996': 'Every 6 Weeks',
  '3776970997': 'Every 8 Weeks',
  '3776970998': 'Every Month'
};

// Selling plans (individual flavor subscription)
const SUBSCRIBE_PLAN_FLAVOR = '2698674349'; // every 2 weeks

// Individual flavor variants
const FLAVOR_VARIANTS = {
  light: {
    productId: '8371013451949',
    flavors: {
      'bright-berry':     { '4': '43706600226989', '12': '43476426260653' },
      'tropical-mango':   { '4': '43706652000429', '12': '43644594421933' },
      'juicy-watermelon': { '4': '43706563559597', '12': '43644594520237' },
      'squeeze-of-lime':  { '4': '43706638991533', '12': '43644594618541' }
    }
  },
  moderate: {
    productId: '8449097007277',
    flavors: {
      'passion-fruit':    { '4': '44305077665965', '12': '44305077698733' },
      'blood-orange':     { '4': '44305076453549', '12': '44305076486317' },
      'pink-lemonade':    { '4': '44305071112365', '12': '44305071145133' },
      'blueberry':        { '4': '44305054793901', '12': '44305054826669' }
    }
  },
  bold: {
    productId: '8449097433261',
    flavors: {
      'passion-fruit':    { '4': '43644594290861', '12': '44031199215789' },
      'blood-orange':     { '4': '43644594356397', '12': '43916737282221' },
      'pink-lemonade':    { '4': '43644596551853', '12': '44031200755885' },
      'blueberry':        { '4': '43644596715693', '12': '43644596748461' }
    }
  }
};

// Cannabis Spirit (only for /bloodorange or cross-sell)
const SPIRIT = {
  productId: '8460535595181',
  variantId: '45708036374701',
  title:     'Blood Orange Spirit',
  price:     59.00,
  packSize:  '1 Bottle'
};
```

## Appendix B: minimal Sampler buy-block (V2)

The smallest valid V2 buy-block. Drop this into any new page, fill the four data-attrs from Appendix A, and `shop.js` does the rest.

```html
<div class="buy-block buy-block-inline"
     data-product-id="8371013451949"
     data-variant-id="45102353678509"
     data-title="The Light Sampler Pack"
     data-pack-size="16-Pack · 4 flavors"
     data-image="/assets/images/sampler/5mg/hero.jpg"
     data-price="54.37">
  <div class="product-thumb product-thumb-sampler" aria-hidden="true">
    <img loading="lazy" src="/assets/images/sampler/5mg/1.jpg" alt="The Light Sampler Pack">
  </div>
  <div>
    <h3>The Light Sampler · 16 Cans · 4 Flavors</h3>
    <p class="price-line">
      <span class="strike">$67.96</span>
      <strong>$54.37</strong>
      <span class="save-tag">20% off · code NEW20</span>
    </p>
    <p class="price-sub muted">Free shipping over $50 · Code <strong>NEW20</strong> auto-applies at checkout</p>
    <button type="button" class="btn btn-primary add-to-cart" data-cta-static="1">Add to Cart · $54.37</button>
    <p class="bb-guarantee"><strong>30-day Sip-It-and-Love-It refund.</strong> If it's not for you, we refund the full pack. No questions.</p>
  </div>
</div>
```

That's it. The four `data-*` attributes are the contract. `shop.js` reads them on click, writes the cart line to `localStorage.delta_v2_cart`, opens the cart drawer, and sets the checkout permalink with `?discount=NEW20` baked in. Everything else is window dressing.
