fix(bf): page-scoped CRO fixes on /odoo-black-friday/ (deal price, one deal buy, live countdown)

Fix 1: the On the Rise pricing card quoted $395 while the deal block said $295 first
year — a price contradiction within one scroll. On this page only the card now reads
$295.00 / 'for your first year, then $395 a year'. Other pages keep $395.
JSON-LD Offer price was already 295.00 and still matches the visible price.

Fix 2: the three list-price Buy Now buttons (data-id 6/4/5) competed with the deal.
Side Hustle and Powerhouse buy buttons removed on this page. The On the Rise card's
buy is now the deal purchase — the same one-click checkout as the offer block
(product 4, /coupon/HOST27?r=/shop/checkout), native POST so it works with JS off.
Tracking: the offer-block form is the single element carrying the Plausible Buy class;
the Bootcamp CTA and the two list-price buttons no longer carry it.

Fix 10: a time-remaining element near the offer and near the buy, computed client-side
from one documented constant (2026-11-30T23:59:59-05:00, US/Eastern). The static
sentence 'Ends Monday 30 November 2026.' still reads with JS off; past the instant the
text switches to the past tense. No visibility animation.
This commit is contained in:
webmaster committed 2026-11-02 08:36:55 +00:00
1 parent 46b943bffe
commit ec6e496900
2 files changed
+92 -8

No files matched your search

+53 -8
View File
@@ -204,6 +204,27 @@
color: var(--ink-soft); color: var(--ink-soft);
margin: 0 0 1.25rem; margin: 0 0 1.25rem;
} }
/* Time remaining (Fix 10). The day count is written into the span by /static/black-friday.js
from one documented end instant. The span is empty until then, so with JavaScript off the
static sentence "Ends Monday 30 November 2026." still carries the deadline on its own.
No visibility/opacity animation touches this element. */
.bf-days {
font-weight: 600;
color: var(--ink);
}
.bf-days-line {
margin: 0 0 0.75rem;
font-size: 0.85rem;
color: var(--ink-soft);
}
.pcard-buy {
margin: 0;
}
/* Fix 1: the deal card's year-one framing is a sentence, not a "/ year" tag — give it its own
line instead of wrapping mid-phrase beside the number. */
.pcard.featured .price small {
display: block;
}
.bf-cta { .bf-cta {
margin: 0; margin: 0;
} }
@@ -550,7 +571,9 @@
<!-- mechanism string, used verbatim — see §3. The code is the offer surface: it stays out <!-- mechanism string, used verbatim — see §3. The code is the offer surface: it stays out
of meta, alt text, structured data and build comments. --> of meta, alt text, structured data and build comments. -->
<p class="bf-mechanism bf-code-wrap"><span class="bf-code-label">Your code</span><span class="bf-code">HOST27</span><span class="bf-code-note">takes $100 off your first year at checkout. One redemption per customer.</span></p> <p class="bf-mechanism bf-code-wrap"><span class="bf-code-label">Your code</span><span class="bf-code">HOST27</span><span class="bf-code-note">takes $100 off your first year at checkout. One redemption per customer.</span></p>
<p class="bf-deadline">Ends Monday 30 November 2026.</p> <!-- Fix 10: the static deadline sentence stands alone with JavaScript off; the day count
is appended by /static/black-friday.js. -->
<p class="bf-deadline">Ends Monday 30 November 2026. <span class="bf-days" data-bf-days></span></p>
<!-- Buy = CLICK PROXY (offer-CTA click), NOT a sale. <!-- Buy = CLICK PROXY (offer-CTA click), NOT a sale.
See wiki/concepts/conversion-tracking-contract. The form posts the On the Rise See wiki/concepts/conversion-tracking-contract. The form posts the On the Rise
product (id 4) straight to the shop basket, so one click lands the visitor in product (id 4) straight to the shop basket, so one click lands the visitor in
@@ -733,7 +756,9 @@
<small class="price-period">/ year</small> <small class="price-period">/ year</small>
</div> </div>
<p class="desc">"ODOO Standard .. that is all my business needs"</p> <p class="desc">"ODOO Standard .. that is all my business needs"</p>
<a href="javascript:void(0)" class="btn btn-brass btn-buy plausible-event-name=Buy" data-id="6">Buy Now</a> <!-- Fix 2 (this page only): the list-price buy buttons are removed here. They are
not what paid traffic came for, and a list-price purchase must not sit one
click away beside the deal. The plan stays as a price anchor. -->
<ul> <ul>
<li><i class="fas fa-check" style="color: var(--success)"></i> Launch Odoo CE in 5 minutes</li> <li><i class="fas fa-check" style="color: var(--success)"></i> Launch Odoo CE in 5 minutes</li>
<li><i class="fas fa-check" style="color: var(--success)"></i> Automatic backups — no remembering</li> <li><i class="fas fa-check" style="color: var(--success)"></i> Automatic backups — no remembering</li>
@@ -746,12 +771,29 @@
<div class="pcard featured"> <div class="pcard featured">
<div class="pcard-body"> <div class="pcard-body">
<h3>On the Rise</h3> <h3>On the Rise</h3>
<!-- Fix 1 (this page only): the card must agree with the offer block above. On this
page the On the Rise card shows the Black Friday price — $295 for the first year,
then the $395 list price. $395 remains correct on every other page. -->
<div class="price"> <div class="price">
<strong>$ 395.00</strong> <strong>$ 295.00</strong>
<small class="price-period">/ year</small> <small class="price-period">for your first year, then $395 a year</small>
</div> </div>
<p class="desc">"I need some modules and professional reports"</p> <p class="desc">"I need some modules and professional reports"</p>
<a href="javascript:void(0)" class="btn btn-brass btn-buy plausible-event-name=Buy" data-id="4">Buy Now</a> <!-- Fix 2: the On the Rise buy is the Black Friday deal purchase — the same one-click
checkout as the offer block above (product 4, coupon HOST27 auto-applied,
redirect to /shop/checkout). Native POST form, so it works with JavaScript off.
Deliberately NOT marked with the Plausible Buy goal class: the offer-block form
above is the single Buy-marked element on this page, so the click proxy counts
one deal intent, not two. -->
<form method="post" action="https://20.odoo4projects.com/shop/post_basket" target="_blank" class="pcard-buy">
<input type="hidden" name="basket" value='[{"product_id":4,"qty":1}]' />
<input type="hidden" name="clear" value="1" />
<input type="hidden" name="redirect" value="/coupon/HOST27?r=/shop/checkout" />
<button type="submit" class="btn btn-brass">Buy On the Rise — $295 first year</button>
</form>
<!-- Fix 10: time remaining, filled in by /static/black-friday.js from one documented
end instant. Empty with JavaScript off. -->
<p class="bf-days-line"><span class="bf-days" data-bf-days></span></p>
<ul> <ul>
<li><i class="fas fa-check" style="color: var(--success)"></i> Launch Odoo CE in 5 minutes</li> <li><i class="fas fa-check" style="color: var(--success)"></i> Launch Odoo CE in 5 minutes</li>
<li><i class="fas fa-check" style="color: var(--success)"></i> Automatic backups — no remembering</li> <li><i class="fas fa-check" style="color: var(--success)"></i> Automatic backups — no remembering</li>
@@ -770,7 +812,7 @@
<small class="price-period">/ year</small> <small class="price-period">/ year</small>
</div> </div>
<p class="desc">"ODOO Enterprise is a must have!"</p> <p class="desc">"ODOO Enterprise is a must have!"</p>
<a href="javascript:void(0)" class="btn btn-brass btn-buy plausible-event-name=Buy" data-id="5">Buy Now</a> <!-- Fix 2 (this page only): list-price buy button removed — see the Side Hustle card. -->
<ul> <ul>
<li><i class="fas fa-check" style="color: var(--success)"></i> Launch Odoo CE in 5 minutes</li> <li><i class="fas fa-check" style="color: var(--success)"></i> Launch Odoo CE in 5 minutes</li>
<li><i class="fas fa-check" style="color: var(--success)"></i> Automatic backups — no remembering</li> <li><i class="fas fa-check" style="color: var(--success)"></i> Automatic backups — no remembering</li>
@@ -856,8 +898,11 @@
<p class="bf-mechanism">Black Friday: <strong>a second year of hosted Odoo Community Edition</strong>. One redemption per customer.<!-- OPEN-SLOT: bootcamp Black Friday price line --></p> <p class="bf-mechanism">Black Friday: <strong>a second year of hosted Odoo Community Edition</strong>. One redemption per customer.<!-- OPEN-SLOT: bootcamp Black Friday price line --></p>
<p class="bf-deadline">Ends Monday 30 November 2026.</p> <p class="bf-deadline">Ends Monday 30 November 2026.</p>
<!-- Buy = CLICK PROXY (offer-CTA click), NOT a sale. The real purchase event is fired on the <!-- Buy = CLICK PROXY (offer-CTA click), NOT a sale. The real purchase event is fired on the
checkout host (20.odoo4projects.com) by odoo-dev. See wiki/concepts/conversion-tracking-contract. --> checkout host (20.odoo4projects.com) by odoo-dev. See wiki/concepts/conversion-tracking-contract.
<p class="bf-cta"><a class="plausible-event-name=Buy" href="/odoo-erp-automation-bootcamp/">See the Bootcamp and what you build</a></p> Fix 2: the existing Plausible Buy goal class was removed here — this is a
navigation link to the Bootcamp page, not a deal purchase, and the deal purchase
(the Hosting form above) is the single element that may carry the class. -->
<p class="bf-cta"><a href="/odoo-erp-automation-bootcamp/">See the Bootcamp and what you build</a></p>
</div> </div>
</div> </div>
</div> </div>
+39
View File
@@ -423,4 +423,43 @@
observer.observe(g); observer.observe(g);
}); });
})(); })();
/* ---- Black Friday time remaining (Fix 10) ----
ONE documented constant: the instant the offer ends — 30 November 2026, end of day, in the
account's US/Eastern context (EST, UTC-5, after the 1 Nov DST change). The day count is
computed client-side from this instant on every page load; it is NEVER hard-coded, so it
stays correct as the date changes.
Fail-safe: the HTML carries the static sentence "Ends Monday 30 November 2026." with an empty
span beside it, so with JavaScript off the deadline still reads correctly on its own. Once the
end instant has passed the text switches to the past tense — it never keeps claiming time
remains. Nothing here animates visibility. */
(function () {
var BF_END = new Date('2026-11-30T23:59:59-05:00');
var DAY = 86400000;
var nodes = document.querySelectorAll('[data-bf-days]');
if (!nodes.length) return;
/* Whole calendar days in US/Eastern, so "days left" is a date difference and not a 24-hour
window (the offer ends at the end of a day, not 28 x 24h from now). */
function easternDayNumber(d) {
var parts = new Intl.DateTimeFormat('en-CA', {
timeZone: 'America/New_York', year: 'numeric', month: '2-digit', day: '2-digit'
}).format(d).split('-');
return Date.UTC(parseInt(parts[0], 10), parseInt(parts[1], 10) - 1, parseInt(parts[2], 10)) / DAY;
}
var daysLeft = easternDayNumber(BF_END) - easternDayNumber(new Date());
var text;
if (daysLeft > 1) {
text = daysLeft + ' days left';
} else if (daysLeft === 1) {
text = '1 day left';
} else if (daysLeft === 0) {
text = 'last day today';
} else {
text = 'this offer has ended';
}
nodes.forEach(function (n) { n.textContent = text; });
})();
})(); })();