Rename to Evergreen Applicator + SEO (title, meta, OG, JSON-LD, canonical)

This commit is contained in:
drjones
2026-09-01 14:23:25 -07:00
parent b975b445ca
commit 9ed89ae6ea
5 changed files with 279 additions and 24 deletions

View File

@@ -1,23 +1,25 @@
# Pest Nocturne 🪲 # Evergreen Applicator 🌲
A midnight specimen-cabinet study suite for the **WSU EM019** pest management **Washington pesticide applicator exam prep** — an interactive single-page study
certification exam. The complete *Study Manual for Pest Management suite for passing the WSDA pesticide applicator license (Core + Structural +
Professionals* rebuilt as an interactive, illustrated single-page app. Ornamental & Turf + Agricultural). Built from the *Study Manual for Pest
Management Professionals* (WSU EM019) plus an original question bank.
## Live ## Live
**https://nocturne.thetempleofdoom.com** **https://evergreenapplicator.thetempleofdoom.com**
## What's inside ## What's inside
- **Field guide** — all 13 chapters with illustrated plates - **WA Cert** — WSDA licensing guide: exam format, fees, categories, CE requirements, printable application checklist
- **Field guide** — all 13 chapters with real pest photos (Wikimedia, CC/PD)
- **Identification keys** — branching keys, walked one couplet at a time - **Identification keys** — branching keys, walked one couplet at a time
- **House faults** — 22 points of faulty construction/maintenance, in order - **House faults** — 22 points of faulty construction/maintenance, in order
- **Flashcards** 🔒 — premium - **WA Prep** — 318 questions (249 original + 69 from the manual) with instant feedback + a timed 50-question mock exam (120 min, 70% pass)
- **Practice exam** 🔒 — premium (scored, with the official answer key) - **Flashcards** 🔒 / **Practice exam** 🔒 — premium
- **Glossary** — every defined term, searchable as you type - **Glossary** — every defined term, searchable
## Monetization ## Monetization
Freemium: the field guide, keys, and house faults are free. **Flashcards and Freemium: the field guide, keys, house faults, WA Cert, and WA Prep are free.
the practice exam are a $5 Bitcoin unlock** via BTCPay. **Flashcards and the practice exam are a $5 Bitcoin unlock** via BTCPay.
- BTCPay store: `FDT6DHWEaA7DF5WFePkp4pufgQ9yy1G6JfzTCwey1jYn` - BTCPay store: `FDT6DHWEaA7DF5WFePkp4pufgQ9yy1G6JfzTCwey1jYn`
- Price: $5 USD (on-chain BTC) - Price: $5 USD (on-chain BTC)
@@ -25,17 +27,20 @@ the practice exam are a $5 Bitcoin unlock** via BTCPay.
## Stack ## Stack
- Single self-contained `index.html` (vanilla JS SPA, no build step) - Single self-contained `index.html` (vanilla JS SPA, no build step)
- Flask (`app.py`) for the paywall + BTCPay webhook - Flask (`app.py`) for the paywall + BTCPay webhook (HMAC-verified)
- nginx reverse proxy → Flask :5000 - nginx reverse proxy → Flask :5000
- SQLite (`orders.db`) for order state - SQLite (`orders.db`) for order state
## Deploy ## Deploy
- Proxmox LXC CT **117** (`nocturne`) @ 10.30.20.234 - Proxmox LXC CT **117** (`nocturne`) @ 10.30.20.234
- systemd `nocturne.service`, nginx `sites-enabled/nocturne` - systemd `nocturne.service`, nginx `sites-enabled/nocturne`
- Cloudflare tunnel `thetemple-fleet` (`1aeb1ac0`), ingress → `:80` - Cloudflare tunnel `thetemple-fleet` (`1aeb1ac0-3c49-4801-b61d-bc92e6e4932a`), ingress → `:80`
## Files ## Files
- `index.html` — the study suite + injected paywall gate + BMAC footer - `index.html` — the study suite (paywall gate, photos, WA Prep, BMAC footer injected)
- `app.py` — Flask backend (BTCPay invoice, webhook, unlock status) - `app.py` — Flask backend (BTCPay invoice, webhook, unlock status, image serving)
- `inject.py` — one-shot script that injects the paywall + footer into the HTML - `images/` — 25 real pest photos (Wikimedia Commons)
- `wa_exam_data.py` / `wa_exam_data2.py` — the original 249-question bank (part 2 imports part 1)
- `rebuild_wa_prep.py` — build script for the WA Prep view + mock exam
- `inject.py` / `integrate_photos.py` / `add_wa_view.py` / `rename.py` — one-shot build scripts
- `nocturne.service` / `nocturne.nginx` — service + nginx config - `nocturne.service` / `nocturne.nginx` — service + nginx config

167
add_features.py Normal file
View File

@@ -0,0 +1,167 @@
import io
p = "/Users/drjones/pest-nocturne/index.html"
s = open(p).read()
# ============ 1. PROGRESS TRACKER (localStorage, per-category) ============
# add progress state after the mock declaration
s = s.replace(
" let mock = { qs: [], answers: [], secs: 0, timer: null };",
" let mock = { qs: [], answers: [], secs: 0, timer: null };\n"
" let progress = JSON.parse(localStorage.getItem('pn_progress')||'{}');\n"
" function saveProgress(){ localStorage.setItem('pn_progress', JSON.stringify(progress)); }\n"
" function clearProgress(){ progress = {}; localStorage.removeItem('pn_progress'); }",
1,
)
# record progress on every practice answer
s = s.replace(
" if (correct) score++;\n answered++;",
" if (correct) score++;\n answered++;\n"
" const pc = q.cat; if (!progress[pc]) progress[pc] = {c:0, t:0};\n"
" progress[pc].t++; if (correct) progress[pc].c++; saveProgress();",
1,
)
# progress bars UI (after the category buttons row)
s = s.replace(
" <div class=\"wa-cats\">${cats}</div>\n"
" <div style=\"margin-top:14px\">",
" <div class=\"wa-cats\">${cats}</div>\n"
" <div class=\"wa-progress\">\n"
" ${CATS.filter(c=>c!=='All').map(c=>{const pr=progress[c]||{c:0,t:0};const pct=pr.t?Math.round(pr.c/pr.t*100):0;return `<div class=\"wa-mrow\"><span class=\"wa-mcat\">${c}</span><div class=\"wa-mbar\"><div class=\"wa-mfill\" style=\"width:${pct}%\"></div></div><span class=\"wa-mpct\">${pr.t?pr.c+'/'+pr.t+' ('+pct+'%)':'untouched'}</span></div>`;}).join('')}\n"
" <button class=\"wa-clear\" id=\"wa-clear\">Reset progress</button>\n"
" </div>\n"
" <div style=\"margin-top:14px\">",
1,
)
# wire the clear button
s = s.replace(
" const modeBtn = root.querySelector('#wa-mode');",
" const clearBtn = root.querySelector('#wa-clear');\n"
" if (clearBtn) clearBtn.addEventListener('click', ()=>{ clearProgress(); render(); });\n"
" const modeBtn = root.querySelector('#wa-mode');",
1,
)
# progress CSS
s = s.replace(
".wa-cats .wac.on{",
".wa-progress{margin-top:16px;display:flex;flex-direction:column;gap:7px;max-width:560px}\n"
".wa-mrow{display:flex;align-items:center;gap:10px;font-size:.78rem}\n"
".wa-mcat{width:130px;color:var(--text-dim,#A3A99C);flex-shrink:0}\n"
".wa-mbar{flex:1;height:8px;border-radius:999px;background:var(--surface-2,#1B2239);overflow:hidden}\n"
".wa-mfill{height:100%;background:linear-gradient(90deg,var(--brass,#E3A53F),var(--verdigris,#54BFA9));border-radius:999px;transition:width .3s}\n"
".wa-mpct{width:120px;text-align:right;color:var(--text-dim,#A3A99C);font-family:monospace}\n"
".wa-clear{margin-top:10px;background:none;border:1px solid var(--line,#2C3455);color:var(--text-faint,#6E7688);padding:5px 12px;border-radius:8px;cursor:pointer;font-size:.72rem;align-self:flex-start}\n"
".wa-clear:hover{border-color:var(--crimson,#DE5F45);color:var(--crimson,#DE5F45)}\n"
".wa-cats .wac.on{",
1,
)
# ============ 2. WSDA CHECKLIST (printable one-pager in WA Cert view) ============
CHECKLIST = r'''
<div class="wa-checklist">
<div class="chk-head"><h3>License application checklist</h3><button class="btn" id="wa-print" style="padding:8px 16px;font-size:.8rem">Print / save PDF</button></div>
<ol class="chk-list">
<li><b>Confirm your category.</b> Structural = the commercial pest-control route; add Ornamental &amp; Turf or Agricultural as needed. Each category = its own exam.</li>
<li><b>Order study materials.</b> WSU Extension (<a href="https://extension.wsu.edu" target="_blank">extension.wsu.edu</a>) + the EM019 manual. The field guide, flashcards and WA Prep tab on this site cover the same ground.</li>
<li><b>Schedule the exam</b> through WSDA at (360) 902-2020. The core (general) exam is required first; category exams after.</li>
<li><b>Pay the fee.</b> $33 exam + $73 license. Bring ID.</li>
<li><b>Sit the exam.</b> 50&ndash;100 multiple-choice, 120 minutes, closed book, 70% to pass.</li>
<li><b>After passing</b> — your WSDA license is issued; renew every 5 years with 40 CEUs.</li>
<li><b>Keep records.</b> Restricted-use pesticide applications must be recorded for 2 years.</li>
</ol>
<p class="chk-note">One-pager · WSDA (360) 902-2020 · agr.wa.gov · Olympia WA 98504</p>
</div>
<style>@media print{body *{visibility:hidden}.wa-checklist,.wa-checklist *{visibility:visible}.wa-checklist{position:absolute;left:0;top:0;width:100%;padding:20px}#wa-print{display:none}}</style>
'''
# insert the checklist before the WA quick test in the v-wa render
s = s.replace(
" <div class=\"wa-checklist\"", # no-op guard
" <div class=\"wa-checklist\"", # keep
1,
) if "wa-checklist" in s else None
# find the v-wa render and insert checklist before the quick test section
anchor = '<div class="facts"'
# Actually inject into the v-wa innerHTML — find a stable anchor inside it
old_wa_anchor = 'WA quick test'
if old_wa_anchor in s:
# find the start of the WA quick test section within the v-wa render
idx = s.find(old_wa_anchor)
# back up to the section start
sec = s.rfind('<section', 0, idx)
s = s[:sec] + CHECKLIST + s[sec:]
# print button wiring (v-wa render is a bare innerHTML at EOF; wire via a tiny script)
s = s.rstrip() + r'''
<script>
(function(){ var b=document.getElementById('wa-print'); if(b) b.addEventListener('click', function(){ window.print(); }); })();
</script>
'''
# ============ 3. DISTINGUISH-THESE-PESTS ID section ============
DIST = r'''
<div class="wa-dist">
<h3>Distinguish these pests (exam favorites)</h3>
<div class="dist-grid">
<div class="dist-card"><h4>Ant vs. termite</h4><ul>
<li><b>Wings</b> — ant: front pair longer than hind; termite: all four equal.</li>
<li><b>Waist</b> — ant: pinched (narrow); termite: broad, straight-sided.</li>
<li><b>Antennae</b> — ant: elbowed (bent); termite: straight, bead-like.</li>
</ul></div>
<div class="dist-card"><h4>Carpenter ant vs. moisture ant</h4><ul>
<li><b>Thorax</b> — carpenter: smooth, evenly rounded (no notch); moisture: uneven/notched.</li>
<li><b>Habitat</b> — carpenter: sound wood, hollows galleries; moisture: rotting, damp wood.</li>
<li><b>Frass</b> — carpenter: coarse sawdust + dead ants; moisture: fine, mixed with soil.</li>
</ul></div>
<div class="dist-card"><h4>Dampwood vs. subterranean vs. drywood termite</h4><ul>
<li><b>Dampwood</b> — largest, big dark-headed soldiers, needs moist wood, NO ground contact.</li>
<li><b>Subterranean</b> — needs soil contact, builds mud shelter tubes.</li>
<li><b>Drywood</b> — dry wood only, kicks out hard 6-sided pellets (no mud).</li>
</ul></div>
<div class="dist-card"><h4>Hobo spider vs. giant house spider</h4><ul>
<li><b>Legs</b> — hobo: shorter, ~1.5× body; giant house: very long, ~2× body.</li>
<li><b>Markings</b> — hobo: chevron pattern on abdomen; giant house: plainer.</li>
<li><b>Bite</b> — hobo's "necrotic bite" is a disproven myth; neither is medically significant.</li>
</ul></div>
<div class="dist-card"><h4>German vs. Oriental vs. American roach</h4><ul>
<li><b>German</b> — small (~½"), two dark stripes on pronotum, indoor, fastest breeder.</li>
<li><b>Oriental</b> — dark, shiny, ~1", damp basements/sewers, male wings short.</li>
<li><b>American</b> — largest (~1½"), reddish-brown, sewers/basements, can fly.</li>
</ul></div>
<div class="dist-card"><h4>Western black widow vs. false widow</h4><ul>
<li><b>Hourglass</b> — widow: red hourglass on the <em>underside</em> (not the top).</li>
<li><b>False widow</b> — brownish, no red hourglass.</li>
<li><b>Only the female widow</b> is dangerously venomous; males are tiny and harmless.</li>
</ul></div>
</div>
</div>
<style>
.wa-dist{margin-top:28px;border-top:1px solid var(--line,#2C3455);padding-top:22px}
.wa-dist h3{font-family:Fraunces,serif;font-size:1.4rem;margin-bottom:14px}
.dist-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:14px}
.dist-card{background:var(--surface,#141A30);border:1px solid var(--line,#2C3455);border-radius:12px;padding:16px}
.dist-card h4{color:var(--brass,#E3A53F);margin-bottom:8px;font-size:1rem}
.dist-card ul{margin:0;padding-left:18px}
.dist-card li{margin-bottom:6px;font-size:.88rem;line-height:1.45;color:var(--text,#EDE7D6)}
.dist-card li b{color:var(--verdigris,#54BFA9)}
.wa-checklist{margin-top:28px;border:1px solid var(--line,#2C3455);border-radius:14px;padding:20px;background:var(--surface,#141A30)}
.chk-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;gap:10px}
.chk-head h3{font-family:Fraunces,serif;font-size:1.4rem;margin:0}
.chk-list{padding-left:20px;margin:0}
.chk-list li{margin-bottom:8px;line-height:1.5;color:var(--text,#EDE7D6)}
.chk-note{font-size:.78rem;color:var(--text-faint,#6E7688);margin-top:12px}
</style>
'''
# inject DIST into the v-wa render, after the checklist (or before quick test)
if 'wa-checklist' in s and 'distinguish these pests' not in s.lower():
# inject after the checklist block
s = s.replace('</div>\n <style>@media print', DIST + '\n <style>@media print', 1)
open(p, "w").write(s)
print("added progress tracker + WSDA checklist + ID distinctions; size", len(s))

4
app.py
View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Pest Nocturne — WSU EM019 study suite, freemium paywall (BTCPay) # Evergreen Applicator — Washington pesticide applicator exam prep, freemium paywall (BTCPay)
import os, json, uuid, time, sqlite3, hmac, hashlib import os, json, uuid, time, sqlite3, hmac, hashlib
from flask import Flask, request, jsonify, send_from_directory, redirect from flask import Flask, request, jsonify, send_from_directory, redirect
@@ -32,7 +32,7 @@ def create_btcpay_invoice(order_id):
"amount": PRICE_USD, "amount": PRICE_USD,
"currency": "USD", "currency": "USD",
"metadata": {"orderId": order_id}, "metadata": {"orderId": order_id},
"checkout": {"redirectURL": f"https://nocturne.thetempleofdoom.com/?paid=1&order={order_id}"}, "checkout": {"redirectURL": f"https://evergreenapplicator.thetempleofdoom.com/?paid=1&order={order_id}"},
}).encode() }).encode()
req = urllib.request.Request( req = urllib.request.Request(
f"{BTCPAY_URL}/api/v1/stores/{BTCPAY_STORE}/invoices", f"{BTCPAY_URL}/api/v1/stores/{BTCPAY_STORE}/invoices",

View File

@@ -1,11 +1,26 @@
<title>Pest Nocturne</title> <title>Evergreen Applicator — Washington Pesticide Applicator Exam Prep &amp; Practice Tests</title>
<meta name="description" content="Pass the Washington pesticide applicator exam. 318 original + WSU EM019 practice questions, timed 50-question mock exams, a pest ID field guide, and the WSDA licensing guide — free.">
<meta name="keywords" content="Washington pesticide applicator exam, WA pesticide license, WSDA exam prep, pesticide applicator practice test, WDO inspection, IPM, structural pest control license, ornamental and turf license">
<meta name="robots" content="index, follow, max-image-preview:large">
<link rel="canonical" href="https://evergreenapplicator.thetempleofdoom.com/">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Evergreen Applicator">
<meta property="og:title" content="Evergreen Applicator — Washington Pesticide Applicator Exam Prep">
<meta property="og:description" content="318 practice questions, timed mock exams, pest ID field guide, and the WSDA licensing guide. Pass the WA pesticide applicator test.">
<meta property="og:url" content="https://evergreenapplicator.thetempleofdoom.com/">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Evergreen Applicator — Washington Pesticide Applicator Exam Prep">
<meta name="twitter:description" content="318 practice questions, mock exams, pest ID guide, and the WSDA licensing guide.">
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"WebSite","name":"Evergreen Applicator","url":"https://evergreenapplicator.thetempleofdoom.com/","description":"Washington pesticide applicator exam prep — 318 practice questions, timed mock exams, and a pest identification field guide for the WSDA pesticide applicator license.","inLanguage":"en-US","about":{"@type":"Thing","name":"Washington pesticide applicator certification"},"publisher":{"@type":"Organization","name":"Evergreen Applicator"}}
</script>
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Karla:ital,wght@0,300..800;1,300..800&family=JetBrains+Mono:wght@400;600&display=swap"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Karla:ital,wght@0,300..800;1,300..800&family=JetBrains+Mono:wght@400;600&display=swap">
<style> <style>
/* ============================================================ /* ============================================================
PEST NOCTURNE — a midnight specimen cabinet for WSU EM019 EVERGREEN APPLICATOR — Washington pesticide applicator exam prep
Palette: ink ground, brass pins, verdigris, bone plates Palette: ink ground, brass pins, verdigris, bone plates
============================================================ */ ============================================================ */
:root{ :root{
@@ -342,7 +357,7 @@ mark{background:color-mix(in srgb,var(--brass) 30%,transparent); color:inherit;
<div class="bar-in"> <div class="bar-in">
<div class="mark"> <div class="mark">
<svg class="pin" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="6" r="4" fill="currentColor"/><path d="M12 10v12" stroke="currentColor" stroke-width="1.6"/><circle cx="12" cy="6" r="1.4" fill="var(--ground)"/></svg> <svg class="pin" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="6" r="4" fill="currentColor"/><path d="M12 10v12" stroke="currentColor" stroke-width="1.6"/><circle cx="12" cy="6" r="1.4" fill="var(--ground)"/></svg>
Pest Nocturne Evergreen Applicator
</div> </div>
<nav class="tabs" id="tabs" role="tablist"></nav> <nav class="tabs" id="tabs" role="tablist"></nav>
<button class="icon-btn" id="themeBtn" title="Switch theme" aria-label="Switch theme">◐</button> <button class="icon-btn" id="themeBtn" title="Switch theme" aria-label="Switch theme">◐</button>
@@ -2016,7 +2031,7 @@ $('#v-home').innerHTML=`
<div class="hero-grid"> <div class="hero-grid">
<div> <div>
<div class="eyebrow">WSU Extension EM019 · Revised April 2021</div> <div class="eyebrow">WSU Extension EM019 · Revised April 2021</div>
<h1><span class="lo">A hyper study suite for the</span>Pest<br><em>Nocturne</em></h1> <h1><span class="lo">Washington pesticide applicator</span>Evergreen<br><em>Applicator</em></h1>
<p class="lede">Every chapter of the <strong>Study Manual for Pest Management Professionals</strong>, rebuilt as a specimen cabinet you can open at midnight: illustrated plates, branching identification keys, a house you can pick apart point by point, ${FLASH.length} flashcards, and the manual's own ${EXAM.length}-question practice exam with the official answer key.</p> <p class="lede">Every chapter of the <strong>Study Manual for Pest Management Professionals</strong>, rebuilt as a specimen cabinet you can open at midnight: illustrated plates, branching identification keys, a house you can pick apart point by point, ${FLASH.length} flashcards, and the manual's own ${EXAM.length}-question practice exam with the official answer key.</p>
<div class="cta-row"> <div class="cta-row">
<button class="btn" data-goto="field">Open the field guide →</button> <button class="btn" data-goto="field">Open the field guide →</button>
@@ -2292,7 +2307,7 @@ $('#gq').addEventListener('input',e=>drawGloss(e.target.value));
drawGloss(''); drawGloss('');
</script> </script>
<!-- ══════════════ PEST NOCTURNE — PREMIUM GATE (injected) ══════════════ --> <!-- ══════════════ EVERGREEN APPLICATOR — PREMIUM GATE (injected) ══════════════ -->
<style> <style>
#pn-paywall{position:fixed;inset:0;z-index:99999;display:none;align-items:center;justify-content:center; #pn-paywall{position:fixed;inset:0;z-index:99999;display:none;align-items:center;justify-content:center;
background:rgba(5,8,16,.86);backdrop-filter:blur(6px);padding:20px;font-family:Karla,system-ui,sans-serif} background:rgba(5,8,16,.86);backdrop-filter:blur(6px);padding:20px;font-family:Karla,system-ui,sans-serif}
@@ -2370,7 +2385,7 @@ drawGloss('');
<div style="text-align:center;padding:26px 14px 30px;font-family:Karla,system-ui,sans-serif;color:var(--text-faint,#6E7688);font-size:.78rem;line-height:1.6"> <div style="text-align:center;padding:26px 14px 30px;font-family:Karla,system-ui,sans-serif;color:var(--text-faint,#6E7688);font-size:.78rem;line-height:1.6">
Pest Nocturne &mdash; a study suite for WSU EM019 &middot; <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank" rel="noopener" style="color:var(--verdigris,#54BFA9)">Buy me a coffee</a> Evergreen Applicator &mdash; Washington pesticide applicator exam prep &middot; <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank" rel="noopener" style="color:var(--verdigris,#54BFA9)">Buy me a coffee</a>
</div> </div>
<script> <script>

68
rename.py Normal file
View File

@@ -0,0 +1,68 @@
import io
p = "/Users/drjones/pest-nocturne/index.html"
s = open(p).read()
# ---- SEO head block (title + meta + OG + JSON-LD) ----
seo_head = '''<title>Evergreen Applicator — Washington Pesticide Applicator Exam Prep &amp; Practice Tests</title>
<meta name="description" content="Pass the Washington pesticide applicator exam. 318 original + WSU EM019 practice questions, timed 50-question mock exams, a pest ID field guide, and the WSDA licensing guide — free.">
<meta name="keywords" content="Washington pesticide applicator exam, WA pesticide license, WSDA exam prep, pesticide applicator practice test, WDO inspection, IPM, structural pest control license, ornamental and turf license">
<meta name="robots" content="index, follow, max-image-preview:large">
<link rel="canonical" href="https://evergreenapplicator.thetempleofdoom.com/">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Evergreen Applicator">
<meta property="og:title" content="Evergreen Applicator — Washington Pesticide Applicator Exam Prep">
<meta property="og:description" content="318 practice questions, timed mock exams, pest ID field guide, and the WSDA licensing guide. Pass the WA pesticide applicator test.">
<meta property="og:url" content="https://evergreenapplicator.thetempleofdoom.com/">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Evergreen Applicator — Washington Pesticide Applicator Exam Prep">
<meta name="twitter:description" content="318 practice questions, mock exams, pest ID guide, and the WSDA licensing guide.">
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"WebSite","name":"Evergreen Applicator","url":"https://evergreenapplicator.thetempleofdoom.com/","description":"Washington pesticide applicator exam prep — 318 practice questions, timed mock exams, and a pest identification field guide for the WSDA pesticide applicator license.","inLanguage":"en-US","about":{"@type":"Thing","name":"Washington pesticide applicator certification"},"publisher":{"@type":"Organization","name":"Evergreen Applicator"}}
</script>'''
# 1. Replace the bare title with the full SEO head
s = s.replace("<title>Pest Nocturne</title>", seo_head, 1)
# 2. Brand comment header
s = s.replace(
" PEST NOCTURNE — a midnight specimen cabinet for WSU EM019",
" EVERGREEN APPLICATOR — Washington pesticide applicator exam prep",
1,
)
# 3. Header brand mark
s = s.replace(
" Pest Nocturne\n </div>",
" Evergreen Applicator\n </div>",
1,
)
# 4. WA Cert view H1 (line ~2019) — make it keyword-rich
s = s.replace(
'<h1><span class="lo">A hyper study suite for the</span>Pest<br><em>Nocturne</em></h1>',
'<h1><span class="lo">Washington pesticide applicator</span>Evergreen<br><em>Applicator</em></h1>',
1,
)
# 5. Premium gate comment
s = s.replace(
"PEST NOCTURNE — PREMIUM GATE (injected)",
"EVERGREEN APPLICATOR — PREMIUM GATE (injected)",
1,
)
# 6. BMAC footer
s = s.replace(
"Pest Nocturne &mdash; a study suite for WSU EM019",
"Evergreen Applicator &mdash; Washington pesticide applicator exam prep",
1,
)
open(p, "w").write(s)
# report any remaining "Nocturne" (case-insensitive)
import re
left = re.findall(r'(?i)nocturne', s)
print("remaining 'nocturne' refs:", len(left))
print("renamed + SEO head added; size", len(s))