Public Access
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:
1 parent
4801e09466
commit
3e2a7a71a6
1 file changed
+111
-79
+111
-79
@@ -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 © ODOO4projects LLC</div>
|
<div class="fcopy">Copyright © 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>
|
||||||
Reference in new issue
Block a user