Three bootcamp pages (EN/DE/ES) get a small waiting-list block below the Buy
button, in the Price and booking section next to the live seat count.
- two required fields: email + comment; framed as "another timezone or
language?" and worded around the January cohort's own schedule
- posts to the same n8n CRM webhook as the site's other forms, with the
footer-message payload shape copied exactly: area:'message',
requirements carries the comment, no name field, wrapped in UTM.inject()
- success hides the form and shows a confirmation; a failure restores the
button and shows a retryable error (never stuck on "sending")
- Plausible event 'Bootcamp Waitlist' on success only (cookieless); no
Google Ads tag and no conversion, as on the rest of these pages
- new page-local /static/bootcamp-waitlist.js carries the verbatim UTM
helpers from script.js (this page does not load script.js)
- buying is untouched: no markup change to the three product-17 forms
Verified: exact posted body per language captured on the wire, success and
error states in a real browser, 375 px no overflow, verify-html 0, JS syntax
0, competitor QC 0 new, impeccable detect byte-identical (zero new findings).
Show the honest remaining seats for product 17 (Bootcamp Q1-2027) on all
three bootcamp pages, in the page's own language, from the availability
webhook. Progressive enhancement (static/bootcamp-availability.js): empty
and hidden by default, filled on a successful fetch; on qty 0 the cohort is
reported full, the next cohort is pointed at, and the Buy buttons are
disabled; on any timeout/error/unexpected payload the page is left exactly
as-is and the Buy buttons stay enabled. The count is never written into the
HTML as a fixed string.
All nine Buy forms on the bootcamp pages (EN/DE/ES, three each) posted
product_id 12, which the shop resolves to 'Bootcamp - Q1-2026-eng'. The
advertised cohort is Q1 2027 (starts Thu 21 Jan 2027), so a customer
buying the 2027 bootcamp received the 2026 cohort. Both are 595.00 and
neither cart was empty, so it went unnoticed; the form's info field reads
'Q1 2027, English' while the product was 2026.
product_id 12 -> 17 in every bootcamp Buy form, all three languages.
The info field is kept (it renders as the cart line description via
sale.order.line.name, so it is read downstream); it is truthful.
static/buy.js doc comment updated to match. Nothing else changed:
plausible-event-name=Buy class, target=_blank form post, prices and
layout untouched.
- de|es/index.html: order widget #orderPlanTax '+ MwSt.'/'+ impuestos'
-> 'inkl. MwSt.'/'impuestos incluidos' (static span, no JS writes it)
- de|es/support.html: add the AI-agent urgent + 24h email support sentence
- de|es/odoo-erp-automation-bootcamp/index.html: add the seat terms
(transferable any time, cancellable up to two weeks before 21 Jan 2027)
- terms.html has no DE/ES page (legal pages held back) -> nothing to mirror
Mirror of 9715a3b into the two translated bootcamp pages. Each page's three
surfaces now read 595.00: the three buy buttons, the JSON-LD Offer `price`
field, and the `.bc-price` sentence. Matches the shop product, so the DE/ES
page can no longer lose the sale at the cart.
Price-only: the term (Q1 2027), the dates, the six-seat cap and the "one year
of hosted Odoo Community Edition" wording are untouched. Currency stays USD,
numeral format unchanged per page.
Verified (see translator/work/verify-price-change.py):
- HEAD vs worktree per page identical on tag sequence, attribute list,
<style> bytes, non-JSON-LD <script> bytes and JSON-LD shape -> text-only.
- 595.00 x5, 465.00 x0, no stray `465` token on either page.
- the three buy forms label-normalised byte-identical to EN.
No DE/ES workshops, black-friday or what-can-i-use-odoo-for translations
exist; llms.txt already carries 595.00.
All three bootcamp pages (EN/DE/ES), every buy form: the basket line now
carries info="Q1 2027, English" alongside product_id 12 and qty 1, the
same field shape australia.html uses for location. Value verbatim on all
three pages (the programme is delivered in English in every language).
clear=1 / action / method / target / button labels unchanged.
Verified end to end against the live shop: POST /shop/post_basket returns
303 -> /shop/checkout and /shop/cart shows product 12 at qty 1 with the
info text rendered on the order line.
Mirror the EN cap from 2940d33 in native register, cap only — the Black
Friday call-out (window, second hosted year, BF2026) is not added; it was
dropped from EN in fd3f1dc and stays out of every language.
- de|es/odoo-erp-automation-bootcamp/index.html: Class-size fact row
(Begrenzt auf 6 Teilnehmer / Limitado a 6 participantes), the .bc-price
cap line, and JSON-LD CourseInstance maximumAttendeeCapacity: 6.
- No workshops DE/ES copies exist, so no card / comparison row applies.
- why-odoo4projects.html left untracked.
Mirror the EN term change (01c5dd3) into the published German and Spanish
bootcamp pages: hero kicker, hero first-session stat, the four schedule
facts, the DST note and both JSON-LD dates. Text/date splice only — the
verifier reports the tag sequence, attribute list, <style>/<script> bytes
and JSON-LD shape identical before and after, english_leftovers: none.
Plausible's click handler walks up from the clicked element and bails out the
moment it meets a <form>, so a plausible-event-name=Buy class on a
<button type="submit"> inside a plain form never fires Buy — the tracker sends
its automatic "Form: Submission" event instead and the goal records nothing.
Measured live on /odoo-erp-automation-bootcamp/ before this change:
{"n":"Form: Submission"}.
- odoo-erp-automation-bootcamp/index.html (3 CTAs): class moved from the
<button type="submit"> to the enclosing <form>.
- australia.html (2 CTAs): same move.
- de|es/odoo-erp-automation-bootcamp/index.html (3 forms each): the buy forms
carried no Buy marking at all, so the DE and ES bootcamp sales were invisible
to the goal. Class added to the <form>.
- odoo/upsell.html: the three .plan-btn buttons drive the same JS buy path and
were unmarked. A class on a plain <button> (not a form submit) does fire, so
it was added there and browser-verified. The disabled Trial button is left
unmarked.
Class attribute only: every button label, every form action and every basket
payload is byte-identical. No CSS or JS rule references the class.
Verified in a real browser (fetch shimmed, tracker's localhost/automation gates
bypassed): EN bootcamp 3/3, australia 2/2, DE 3/3, ES 3/3, upsell 1/1 clicks
each send exactly one {"n":"Buy"} to /api/event and never "Form: Submission".
The snippet was on 10 of 40 pages, so the Trial / FAQ / Buy / Custom Quote /
search posts / Project Plan goals were inert everywhere else. It now sits
verbatim, in the same <head> position as index.html, on all 39 visitor-facing
pages. email/trial-confirmation.html is deliberately left out (email template).
FAQ: the five <details class="bc-faq"> <summary> elements on the bootcamp page
and its DE/ES twins now carry plausible-event-name=FAQ, and the 15
.faq-question buttons on de/es/support.html get the marking they were believed
to already have. No goal renamed: one FAQ goal across all languages.
No markup, copy or behaviour change, and no third-party request beyond the
snippet itself. verify-html.py PROBLEMS 0; check-links.py OK; event checker
59 PASS / 0 FAIL; impeccable detector +0/-0 findings on the changed files.
build-i18n.py's rebase_paths() only re-pointed a root-absolute page link when it
contained ".html", so extensionless directory links stayed on the EN route even
when the language had its own copy (`/odoo-erp-automation-bootcamp/` on all four
DE and ES pages, and `/` on the DE/ES bootcamp pages). Same guard as the .html
branch: localise only when <lang>/<path>/index.html exists.
Re-applied the rebasing to the 8 already-built DE/ES pages (work/i18n/
relocalise-links.py) - a full re-transform is not safe for them because the
switcher-slot/hero rework means repo pages are not raw transform output. 18 refs
in 8 files. The DE/ES homepages were the stale case: their body links were built
before support.html/odoo-hosting-2027.html existed in that language and were
never revisited.
check-links.py now flags an EN-route page link whose language has its own copy:
0 such links on all 10 DE/ES pages after the rebuild. test-build.py 107 passed;
verify-html.py 0 problems.
The translated pages now carry the same four footer items as the three English pages:
ODOO Bootcamp -> /odoo-erp-automation-bootcamp/, ODOO Hosting in 2027 (DE) / ODOO Hosting en
2027 (ES), Support/Soporte, Rechtliches/Aviso legal. Deleted: Startseite/Inicio,
Datenschutz/Privacidad, AGB/Terminos, Kontakt/Contactenos.
Rebuilt with work/i18n/build-i18n.py from the translator's six files (work/i18n/I18N-BUILD-PLAN.md
section 5), then: place-hero-slot.py for de|es/index.html, place-slot.py for the two 2027 pages,
insert-hero-i18n.py for the two bootcamp pages (the hero CSS is language-neutral chrome held
outside the translated source). Nothing else changed: git diff 31c02b1 over these six files is
the footer list and nothing else - bootcamp hero, three buy forms (product_id 12), 465.00,
selector and hreflang clusters untouched.
Checked: verify-html.py PROBLEMS: 0; node --check static/lang.js OK; verify-footer-useful-links-de-es.py
PROBLEMS: 0; test-build.py 88/88; test-lang.js 31/31; check-links.py BROKEN/MISPLACED: 0;
impeccable detector identical finding set on all six pages (0 changed lines).
The uniform .72 -> .55 scrim composited the whole hero band to luma ~37 and the
right zone to ~35, against a flat midnight of 25.4: a dark photo made darker.
The scrim is now two zones on the same photo:
.78 0%-88% (copy zone: kicker, H1, value line, the four facts, the CTA)
ramp 88%-94%
.25 94%-100% (the lit skyline, no copy over it)
.78 is the smallest flat alpha that holds every text block over its floor
(body 4.5:1, large 3.0:1) at 1920/1440/1024/390 in EN, DE and ES - measured on
the rendered page, not modelled. 88% is where the ramp has to start: the longest
translated fact line (DE, 1024px) reaches 89.3% of the hero.
Measured before -> after (full-range mean luma of the rendered band):
whole band 37.3 / 36.8 / 37.6 / 37.3 -> 33.8 / 33.6 / 34.1 / 34.0
right 88-100% 34.8 / 35.9 / 35.5 / 38.4 -> 38.7 / 40.2 / 39.4 / 43.4
plateau 94-100% -> 41.4 / 44.2 / 42.7 / 46.9
(= 1.63x-1.91x the flat-midnight 25.4, was 1.37x-1.51x)
Worst text block: 4.87:1 (was 3.13:1). No copy, buy form, JSON-LD, selector,
hreflang or price touched: de/ and es/ differ ONLY by this inserted hero block.
Rebuilt de|es/odoo-erp-automation-bootcamp/index.html from the translator's
updated files with work/i18n/build-i18n.py (same recipe as 89bb7b7: lang,
canonical, the four hreflang links, og:url, the top-bar selector kept as-is).
- 2 occurrences per file: the facts-list <dd> and the "Your instructor"
<strong> now read "Oliver - The OC Guy - Arnold, Technical Lead bei/de
ODOO4projects" (the user's alter ego, typed hyphen form).
- Both JSON-LD instructor Person nodes gain alternateName "The OC Guy"
(the translator mirrored the EN page e5e4f5b; name stays Oliver Arnold).
- Buy forms untouched: three POST forms, product_id 12, clear=1, price 465.00
in five places per page.
- git diff --stat e5e4f5b = these two files only (+10/-6).
- add de|es/odoo-erp-automation-bootcamp/index.html (build-i18n.py from the
translator's files; canonical + hreflang cluster + top-bar selector)
- EN bootcamp page gains the four hreflang alternates (was asymmetric)
- price 790 -> 465.00 across all three pages (JSON-LD offers.price, the three
buy-button labels, the bc-price sentence); the three buy forms stay
byte-identical apart from the label language
- sitemap.xml: +/de/ and /es/odoo-erp-automation-bootcamp/; llms.txt: Languages
section names the new pages