← back to Dw Mailer Glitter Naturals Custom

build.py

234 lines

#!/usr/bin/env python3
"""
Build the DW "New Arrivals" Constant Contact mailer (TK-11836).

Structure is cloned verbatim from the last DW send
(~/Projects/dw-vendor-mailer/mailer.html — "The Houses We Represent",
2026-09-09..16): 600px table-based, inline-styled, email-client-safe.

This edition refreshes the lineup to FOUR DW-owned / private-label lines Steve
asked to feature, and adds a full-bleed ROOM-SETTING hero at the top:

  1. Glitter Walls            (glitter / sequin / glass-bead line)
  2. Fentucci Naturals        (private label — real vendor NEVER customer-facing)
  3. Phillipe Romano Naturals (private label — real vendors NEVER customer-facing)
  4. Custom & Bespoke         (made-to-order CTA block)

The DW logo masthead AND the hero room image both link to
/collections/new-arrivals (Steve: "make sure dw banner clicks to new arrivals").

Every product page, collection/page link, and image URL below verified HTTP 200
on 2026-09-16 (www.designerwallcoverings.com canonical).

Regenerate:  python3 build.py   ->  mailer.html

LEAK RULE: no upstream/true-vendor name (Tokiwa, Greenland, Command54, WallQuest,
Chesapeake, Momentum, Versa, etc.) appears anywhere in copy, alt, or filenames.
Only customer-facing brand names (Fentucci, Phillipe Romano, Glitter Walls) are used.
"""
import html, pathlib

STORE = "https://www.designerwallcoverings.com"
NEW_ARRIVALS = STORE + "/collections/new-arrivals"
DW_LOGO = "https://www.designerwallcoverings.com/cdn/shop/files/Screen_Shot_2019-02-06_at_12.08.24_PM_525x300.png?v=1613539653"
# Full-bleed room-setting hero (real DW lifestyle render, landscape 1184x771).
HERO_IMG = "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/medici-real-cork-damask-wallpaper-cork-92713-living_room.jpg?v=1775084704"

INK = "#221f1c"
MUTED = "#8a8178"
LINE = "#e7e2db"
CREAM = "#f7f5f2"
SERIF = "'Playfair Display', Georgia, 'Times New Roman', serif"
SANS = "-apple-system, 'Helvetica Neue', Arial, sans-serif"

# Grid sections: display name (live serif wordmark — these are DW house / private
# labels, so a text wordmark IS the brand mark, exactly as PR is defined in the base),
# tagline, collection handle, and 4 products (caption, product handle, image url).
SECTIONS = [
    {
        "wordmark": "Glitter Walls",
        "wordmark_size": 30,
        "tagline": "High-shine sequin & glass-bead walls.",
        "collection": "glitter-walls",
        "shop_label": "Glitter Walls",
        # glm-still-*.jpg = plain still swatches (the -clean.jpg set are video
        # poster frames with a baked-in play triangle — wrong for a static email).
        "products": [
            ("Hollywood Glamour Sequin — Silver", "hollywood-glamour-sequin-silver-multi-color-glm-52009",
             "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/glm-still-GLM-52009-SAMPLE.jpg?v=1782431735"),
            ("Hollywood Glamour Sequin — Blush", "hollywood-glamour-sequin-blush-glm-52000",
             "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/glm-still-GLM-52000-SAMPLE.jpg?v=1782431715"),
            ("Hollywood Glamour Sequin — Black", "hollywood-glamour-sequin-deep-black-glam-glm-52012",
             "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/glm-still-GLM-52012-SAMPLE.jpg?v=1782431760"),
            ("Hollywood Glamour Sequin — Fuchsia", "hollywood-glamour-sequin-electric-fuschia-pink-glm-52007",
             "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/glm-still-GLM-52007-SAMPLE.jpg?v=1782431743"),
        ],
    },
    {
        "wordmark": "Fentucci Naturals",
        "wordmark_size": 28,
        "tagline": "Handwoven grasscloth & natural texture.",
        "collection": "fentucci-naturals",
        "shop_label": "Fentucci Naturals",
        "products": [
            ("Napoli — Caramel Basketweave", "napoli-caramel-basketweave-grasscloth-wallcovering-fentucci",
             "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/napoli-caramel-basketweave-grasscloth-wallcovering-fentucci-37227446927411.jpg?v=1785460624"),
            ("Oliva — Khaki Basketweave", "oliva-khaki-basketweave-grasscloth-wallcovering-fentucci",
             "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/GRS-27580.jpg?v=1776879830"),
            ("Modena — Gold Basketweave", "modena-gold-basketweave-grasscloth-wallcovering-fentucci",
             "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/GRS-27570.jpg?v=1776879826"),
            ("Cremona — Parchment Texture", "cremona-parchment-textured-grasscloth-wallcovering-fentucci",
             "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/GRS-27560.jpg?v=1776879822"),
        ],
    },
    {
        "wordmark": "Phillipe Romano Naturals",
        "wordmark_size": 25,
        "tagline": "Real cork, mica & natural grounds — from our own atelier.",
        "collection": "phillipe-romano-naturals",
        "shop_label": "Phillipe Romano Naturals",
        "products": [
            ("Oxel Cork", "oxel-cork-wallpaper-inw-4899",
             "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/oxel-cork-wallpaper-inw-4899-4176704045168.jpg?v=1785460958"),
            ("Million Dollar Mica Chip", "million-dollar-mica-chip-wallpaper-mdm-64006",
             "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/86c999e57ca94ffa6d42d7b55020cc1c.jpg?v=1774200201"),
            ("Corkytown Real Cork", "corkytown-real-cork-wallpaper-cork-98605",
             "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/05ff41edbe53075093de08e67597a75c.jpg?v=1775080877"),
            ("Bella Calabria Mica Herringbone", "bella-calabria-mica-chevron-herringbone-prn-62342",
             "https://cdn.shopify.com/s/files/1/0015/4117/7456/products/3c2ac84cbe04bbe26d7c388b57ed702c.jpg?v=1775116939"),
        ],
    },
]

# Custom / bespoke CTA block.
CUSTOM = {
    "wordmark": "Custom & Bespoke",
    "tagline": "Made to order — murals, bespoke colorways & scale.",
    "img": "https://cdn.shopify.com/s/files/1/0015/4117/7456/files/bella-calabria-mica-chevron-herringbone-prn-62342-bedroom.jpg?v=1775117018",
    "img_alt": "Custom wallcovering room setting",
    "blurb": "Bring us your reference — we match, scale, and print to your specification. Murals, custom colorways, oversized repeats, and specialty grounds, all produced to order.",
    "link": STORE + "/pages/custom",
    "cta": "Start a Custom Project",
}


def product_cell(name, handle, img):
    url = f"{STORE}/products/{handle}"
    name_e = html.escape(name)
    return f"""              <td width="132" valign="top" style="padding:2px;">
                <a href="{url}" target="_blank" style="text-decoration:none;color:{INK};">
                  <img src="{img}" width="128" alt="{name_e}" style="display:block;width:128px;height:128px;object-fit:cover;border:1px solid {LINE};border-radius:3px;background:#fff;">
                  <div style="font-family:{SANS};font-size:11px;line-height:1.3;color:{INK};padding:5px 2px 0;text-align:center;">{name_e}</div>
                </a>
              </td>"""


def section_block(v):
    ws = v.get("wordmark_size", 30)
    header = (f'<div style="font-family:{SERIF};font-weight:600;font-size:{ws}px;'
              f'letter-spacing:.3px;color:{INK};">{html.escape(v["wordmark"])}</div>')
    cells = "\n".join(product_cell(*p) for p in v["products"])
    coll = f"{STORE}/collections/{v['collection']}"
    tagline = html.escape(v.get("tagline", ""))
    shop = html.escape(v.get("shop_label", v["wordmark"]))
    return f"""
        <!-- {html.escape(v['wordmark'])} -->
        <tr><td style="padding:16px 24px 4px;text-align:center;">
          {header}
          <div style="font-family:{SANS};font-size:12px;color:{MUTED};padding-top:6px;">{tagline}</div>
        </td></tr>
        <tr><td style="padding:6px 10px 0;">
          <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%"><tr>
{cells}
          </tr></table>
        </td></tr>
        <tr><td style="padding:12px 24px 6px;text-align:center;">
          <div style="font-family:{SANS};font-size:12px;color:{MUTED};padding-bottom:12px;">Order any pattern as a <strong style="color:{INK};">sample</strong>.</div>
          <a href="{coll}" target="_blank" style="display:inline-block;font-family:{SANS};font-size:12px;letter-spacing:1.2px;text-transform:uppercase;color:{INK};text-decoration:none;border:1px solid {INK};padding:10px 26px;border-radius:2px;">Shop {shop} &rarr;</a>
        </td></tr>
        <tr><td style="padding:22px 24px 0;"><div style="border-top:1px solid {LINE};font-size:0;line-height:0;">&nbsp;</div></td></tr>"""


def custom_block(c):
    return f"""
        <!-- {html.escape(c['wordmark'])} -->
        <tr><td style="padding:16px 24px 4px;text-align:center;">
          <div style="font-family:{SERIF};font-weight:600;font-size:30px;letter-spacing:.3px;color:{INK};">{html.escape(c['wordmark'])}</div>
          <div style="font-family:{SANS};font-size:12px;color:{MUTED};padding-top:6px;">{html.escape(c['tagline'])}</div>
        </td></tr>
        <tr><td style="padding:10px 24px 0;">
          <a href="{c['link']}" target="_blank" style="text-decoration:none;border:0;">
            <img src="{c['img']}" width="552" alt="{html.escape(c['img_alt'])}" style="display:block;width:100%;max-width:552px;height:auto;border:1px solid {LINE};border-radius:3px;">
          </a>
        </td></tr>
        <tr><td style="padding:12px 24px 6px;text-align:center;">
          <div style="font-family:{SANS};font-size:12px;line-height:1.55;color:{MUTED};padding:0 6px 14px;">{html.escape(c['blurb'])}</div>
          <a href="{c['link']}" target="_blank" style="display:inline-block;font-family:{SANS};font-size:12px;letter-spacing:1.2px;text-transform:uppercase;color:{INK};text-decoration:none;border:1px solid {INK};padding:10px 26px;border-radius:2px;">{html.escape(c['cta'])} &rarr;</a>
        </td></tr>
        <tr><td style="padding:22px 24px 0;"><div style="border-top:1px solid {LINE};font-size:0;line-height:0;">&nbsp;</div></td></tr>"""


def build():
    rows = "".join(section_block(v) for v in SECTIONS) + custom_block(CUSTOM)
    doc = f"""<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>New Arrivals — Designer Wallcoverings</title></head>
<body style="margin:0;padding:0;background:{CREAM};">
<div style="display:none;max-height:0;overflow:hidden;opacity:0;">New arrivals across Glitter Walls, Fentucci Naturals, Phillipe Romano Naturals &amp; Custom. Order any pattern as a sample, shipped the same week.</div>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:{CREAM};">
<tr><td align="center" style="padding:24px 12px;">
  <table role="presentation" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;background:#ffffff;border:1px solid {LINE};">

    <!-- Masthead — logo links to New Arrivals -->
    <tr><td style="padding:24px 24px 2px;text-align:center;">
      <a href="{NEW_ARRIVALS}" target="_blank" style="text-decoration:none;border:0;">
        <img src="{DW_LOGO}" alt="Designer Wallcoverings" width="188" style="display:inline-block;width:188px;max-width:62%;height:auto;border:0;">
      </a>
    </td></tr>
    <tr><td style="padding:2px 24px 12px;text-align:center;">
      <a href="{NEW_ARRIVALS}" target="_blank" style="font-family:{SANS};font-size:11px;letter-spacing:3px;text-transform:uppercase;color:{MUTED};text-decoration:none;">Designer Wallcoverings &nbsp;&middot;&nbsp; est. 2003 &nbsp;&middot;&nbsp; to the trade</a>
    </td></tr>

    <!-- HERO room setting — full-bleed, links to New Arrivals -->
    <tr><td style="padding:0;">
      <a href="{NEW_ARRIVALS}" target="_blank" style="text-decoration:none;border:0;">
        <img src="{HERO_IMG}" width="600" alt="Designer Wallcoverings new arrivals — room setting" style="display:block;width:100%;max-width:600px;height:auto;border:0;">
      </a>
    </td></tr>

    <!-- Headline -->
    <tr><td style="padding:20px 24px 4px;text-align:center;">
      <div style="font-family:{SERIF};font-size:32px;line-height:1.15;color:{INK};">New Arrivals</div>
      <div style="font-family:{SANS};font-size:13px;line-height:1.55;color:{MUTED};padding:10px 18px 0;">A fresh edit across four of our lines &mdash; high-shine <strong style="color:{INK};">Glitter Walls</strong>, handwoven <strong style="color:{INK};">Fentucci Naturals</strong>, cork &amp; mica <strong style="color:{INK};">Phillipe Romano Naturals</strong>, and made-to-order <strong style="color:{INK};">Custom</strong>. Order any pattern as a <strong style="color:{INK};">sample</strong>, shipped to your studio the same week.</div>
    </td></tr>
    <tr><td style="padding:18px 24px 0;"><div style="border-top:2px solid {INK};font-size:0;line-height:0;">&nbsp;</div></td></tr>
{rows}

    <!-- Footer -->
    <tr><td style="padding:24px;text-align:center;">
      <div style="font-family:{SERIF};font-size:16px;color:{INK};">Designer Wallcoverings</div>
      <div style="font-family:{SANS};font-size:11px;line-height:1.7;color:{MUTED};padding-top:8px;">
        To the trade &middot; Los Angeles, since 2003<br>
        15442 Ventura Blvd &middot; Sherman Oaks, CA 91403 &middot; 1-888-373-4564<br>
        <a href="mailto:info@designerwallcoverings.com" style="color:{MUTED};">info@designerwallcoverings.com</a> &middot;
        <a href="{STORE}" target="_blank" style="color:{MUTED};">designerwallcoverings.com</a>
      </div>
      <div style="font-family:{SANS};font-size:10px;line-height:1.6;color:#b3aa9f;padding-top:14px;">
        You're receiving this because you've shopped or registered with Designer Wallcoverings.<br>
        Unsubscribe &amp; preference links are added automatically below by Constant Contact.
      </div>
    </td></tr>

  </table>
</td></tr>
</table>
</body></html>"""
    out = pathlib.Path(__file__).parent / "mailer.html"
    out.write_text(doc, encoding="utf-8")
    n_prod = sum(len(s["products"]) for s in SECTIONS)
    print(f"wrote {out}  ({len(doc):,} bytes)  sections={len(SECTIONS)}+custom  products={n_prod}")


if __name__ == "__main__":
    build()