feat(bf): show the code on arrival and buy in one click on /odoo-black-friday/

The code was behind a reveal (click 'Open the Black Friday price'). Both offer
cards are now always open: the price and HOST27 are readable without any
interaction. The scroll animation (opacity/transform only, base state fully
opaque, behind @supports) is presentation, never a gate.

The hosting CTA is now a native form that posts the On the Rise product straight
to the shop basket, so one click from the page lands in checkout with the plan in
the cart — replacing a three-navigation detour (offers hub -> homepage trial ->
pricing).

Bootcamp box content is untouched; only its reveal gate was removed.
This commit is contained in:
webmaster committed 2026-11-01 23:10:54 +00:00
1 parent 4801e09466
commit 3e2a7a71a6
1 file changed
+111 -79
+111 -79
View File
@@ -152,56 +152,16 @@
margin-bottom: 1.25rem; margin-bottom: 1.25rem;
} }
/* Reveal mechanic — native <details>/<summary>: the panel is in the DOM in both states, /* Both offer cards are always open: the Black Friday price and the code are readable on
keyboard-operable, and works with JavaScript disabled. CSS reveals it; JS only mirrors arrival, with no reveal and no click. The scroll animation further down is presentation
the open state to aria-expanded. The panel is never display:none by our stylesheet. */ only — the base state is fully visible, so the offer is complete with JavaScript off,
.bf-reveal { under reduced motion, or if the animation never runs. */
border-top: 1px solid var(--line);
}
.bf-reveal-summary {
cursor: pointer;
list-style: none;
display: block;
padding: 1.25rem 0 1rem;
}
.bf-reveal-summary::-webkit-details-marker {
display: none;
}
.bf-reveal-summary:focus-visible {
outline: 2px solid var(--brass);
outline-offset: 3px;
border-radius: var(--radius);
}
.bf-teaser { .bf-teaser {
font-size: 1.02rem; font-size: 1.02rem;
color: var(--ink); color: var(--ink);
line-height: 1.6; line-height: 1.6;
margin: 0 0 1rem; margin: 0 0 1rem;
} }
.bf-open-label {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-family: Inter, sans-serif;
font-weight: 600;
font-size: 0.82rem;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--midnight);
background: var(--brass);
border-radius: var(--radius-pill);
padding: 0.6rem 1.25rem;
}
.bf-open-label::after {
content: "+";
font-size: 1.05rem;
line-height: 1;
}
/* closed state only: the label is the invitation, so it goes once the panel is open.
`[open]` is set by the browser natively — this needs no JavaScript. */
.bf-reveal[open] .bf-open-label {
display: none;
}
.bf-panel { .bf-panel {
padding: 0.25rem 0 1.25rem; padding: 0.25rem 0 1.25rem;
border-top: 1px solid var(--line); border-top: 1px solid var(--line);
@@ -236,6 +196,62 @@
.bf-mechanism strong { .bf-mechanism strong {
color: var(--midnight); color: var(--midnight);
} }
/* The code lives on the offer surface only. It is readable on arrival; the chip is easy to
select so it can be copied at checkout. */
.bf-code-wrap {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 0.4rem 0.7rem;
}
.bf-code-label {
font-family: Inter, sans-serif;
font-weight: 600;
font-size: 0.72rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--brass-deep);
}
.bf-code {
font-family: Marcellus, Georgia, serif;
font-size: 1.3rem;
letter-spacing: 0.12em;
color: var(--midnight);
background: var(--brass);
border-radius: var(--radius-pill);
padding: 0.2rem 0.9rem;
user-select: all;
}
.bf-code-note {
font-size: 0.9rem;
color: var(--ink-soft);
}
/* Presentation only: the code fades and rises a little as it scrolls into view. Opacity and
transform only — never visibility. The base state is fully opaque, so a build where the
animation never fires still shows the code. Everything here is behind @supports, so an
unsupported browser renders the static, visible code. */
@media (prefers-reduced-motion: no-preference) {
@supports (animation-timeline: view()) {
.bf-code {
animation-name: bf-code-in;
animation-timing-function: linear;
animation-fill-mode: both;
animation-duration: auto;
animation-timeline: view();
animation-range: entry 0% cover 45%;
}
}
}
@keyframes bf-code-in {
from {
opacity: 0.8;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.bf-deadline { .bf-deadline {
font-size: 0.85rem; font-size: 0.85rem;
color: var(--ink-soft); color: var(--ink-soft);
@@ -264,6 +280,41 @@
outline: 3px solid var(--midnight); outline: 3px solid var(--midnight);
outline-offset: 2px; outline-offset: 2px;
} }
/* Hosting CTA: a native form that posts the plan straight to the shop basket, so one click
from this page lands the visitor in checkout with the plan already in the cart. Works with
JavaScript off (it is a plain form POST). */
.bf-cta-form {
margin: 0 0 0.75rem;
}
.bf-cta-form .btn {
display: inline-block;
font-family: Marcellus, Georgia, serif;
font-size: 0.9rem;
letter-spacing: 0.04em;
color: var(--midnight);
background: var(--brass);
border: 0;
border-radius: var(--radius-pill);
padding: 0.8rem 1.75rem;
cursor: pointer;
}
.bf-cta-form .btn:hover {
background: var(--brass-bright);
color: var(--midnight);
}
.bf-cta-form .btn:focus-visible {
outline: 3px solid var(--midnight);
outline-offset: 2px;
}
.bf-cta-secondary {
font-size: 0.85rem;
margin: 0;
}
.bf-cta-secondary a {
color: var(--brass-deep);
text-decoration: underline;
text-underline-offset: 3px;
}
/* FAQ (native details/summary — no JS) */ /* FAQ (native details/summary — no JS) */
.cmp-faq-grid { .cmp-faq-grid {
@@ -523,20 +574,15 @@
</div> </div>
</section> </section>
<!-- The two Black Friday offers — one section, two boxes, Bootcamp first. Each box carries the <!-- The two Black Friday offers — one section, two boxes, Bootcamp first. Both cards are always
same reveal mechanic: a native <details class="bf-reveal"> with a closed teaser <summary>, open: the price and the code are readable on arrival, with no reveal and no click. -->
then the open panel. -->
<section class="section-light bf-box" id="offer"> <section class="section-light bf-box" id="offer">
<div class="container"> <div class="container">
<!-- Box 1 — Bootcamp --> <!-- Box 1 — Bootcamp -->
<div class="bf-box-inner"> <div class="bf-box-inner">
<h2 class="bf-box-title">Bootcamp: ten live sessions to a working system</h2> <h2 class="bf-box-title">Bootcamp: ten live sessions to a working system</h2>
<details class="bf-reveal">
<summary class="bf-reveal-summary" aria-expanded="false">
<p class="bf-teaser">A working system for your business in ten live sessions, USD 595. Open to see the Black Friday offer.</p>
<span class="bf-open-label">Open the Black Friday offer</span>
</summary>
<div class="bf-panel"> <div class="bf-panel">
<p class="bf-teaser">A working system for your business in ten live sessions, USD 595 — with a second hosted year added for Black Friday.</p>
<p class="bf-price"><strong>USD 595.00</strong> for the full programme. Black Friday adds <strong>a second year of hosted Odoo Community Edition</strong>, on top of the one already included.</p> <p class="bf-price"><strong>USD 595.00</strong> for the full programme. Black Friday adds <strong>a second year of hosted Odoo Community Edition</strong>, on top of the one already included.</p>
<p class="bf-included">Ten live Thursday sessions, 18:00–19:00 Central Time, starting Thursday 21 January 2027. Six seats, and no more. Your first hosted year is included as standard; the second year is the Black Friday add-on.</p> <p class="bf-included">Ten live Thursday sessions, 18:00–19:00 Central Time, starting Thursday 21 January 2027. Six seats, and no more. Your first hosted year is included as standard; the second year is the Black Friday add-on.</p>
<!-- Bootcamp mechanism string — ONE reusable line, isolated exactly as the Hosting box's is <!-- Bootcamp mechanism string — ONE reusable line, isolated exactly as the Hosting box's is
@@ -549,27 +595,29 @@
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> <p class="bf-cta"><a class="plausible-event-name=Buy" href="/odoo-erp-automation-bootcamp/">See the Bootcamp and what you build</a></p>
</div> </div>
</details>
</div> </div>
<!-- Box 2 — Hosting, On the Rise --> <!-- Box 2 — Hosting, On the Rise -->
<div class="bf-box-inner"> <div class="bf-box-inner">
<h2 class="bf-box-title">Hosting: On the Rise</h2> <h2 class="bf-box-title">Hosting: On the Rise</h2>
<details class="bf-reveal">
<summary class="bf-reveal-summary" aria-expanded="false">
<p class="bf-teaser">Your whole business in one system, $395 a year. Open to see the Black Friday price.</p>
<span class="bf-open-label">Open the Black Friday price</span>
</summary>
<div class="bf-panel"> <div class="bf-panel">
<p class="bf-teaser">Your whole business in one system. The Black Friday price is right here — no code to hunt for.</p>
<p class="bf-price"><strong>$395.00</strong> a year — <strong>$295.00</strong> for your first year.</p> <p class="bf-price"><strong>$395.00</strong> a year — <strong>$295.00</strong> for your first year.</p>
<p class="bf-included">Then it renews at the $395 list price. One yearly price, no per-user licence, no setup fee. We run the server, the backups, your certificate and the version upgrades for you.</p> <p class="bf-included">Then it renews at the $395 list price. One yearly price, no per-user licence, no setup fee. We run the server, the backups, your certificate and the version upgrades for you.</p>
<!-- mechanism string, used verbatim — see §3 --> <!-- mechanism string, used verbatim — see §3. The code is the offer surface: it stays out
<p class="bf-mechanism">Black Friday: <strong>$100 off your first year</strong> with code <strong>HOST27</strong>. One redemption per customer.</p> 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-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 (see the other box on this page). --> <!-- Buy = CLICK PROXY (offer-CTA click), NOT a sale (see the other box on this page).
<p class="bf-cta"><a class="plausible-event-name=Buy" href="/odoo-hosting-offers/">See what a year of hosting includes</a></p> The form posts the On the Rise product (id 4) straight to the shop basket, so one
click lands the visitor in checkout with the plan already in the cart. -->
<form method="post" action="https://20.odoo4projects.com/shop/post_basket" target="_blank" class="bf-cta-form plausible-event-name=Buy">
<input type="hidden" name="basket" value='[{"product_id":4,"qty":1}]' />
<input type="hidden" name="clear" value="1" />
<button type="submit" class="btn btn-brass btn-lg">Buy On the Rise — $295 first year</button>
</form>
<p class="bf-cta-secondary"><a href="/odoo-hosting-offers/">See everything a year of hosting includes</a></p>
</div> </div>
</details>
</div> </div>
</div> </div>
</section> </section>
@@ -661,21 +709,5 @@
<div class="fcopy">Copyright &copy; ODOO4projects LLC</div> <div class="fcopy">Copyright &copy; ODOO4projects LLC</div>
</div> </div>
</footer> </footer>
<script>
/* Enhancement only: mirror each reveal's open state to aria-expanded. Without JS the native
<details> still toggles, keyboard-operable, and exposes its state. */
(function () {
var reveals = document.querySelectorAll(".bf-reveal");
Array.prototype.forEach.call(reveals, function (reveal) {
var summary = reveal.querySelector("summary");
function sync() {
if (summary) summary.setAttribute("aria-expanded", reveal.open ? "true" : "false");
}
sync();
reveal.addEventListener("toggle", sync);
});
})();
</script>
</body> </body>
</html> </html>