New flat landing page for Australian buyers, built from the approved Phase 2
copy with the five buyer-lens changes (R1-R5) applied. English only, no hreflang.
- australia.html: hero, five sections, four-plan pricing table (On the Rise
heroed), 7-question FAQ, final CTA band; house CI (midnight/brass/cream,
Marcellus + Inter), page-scoped CSS only.
- Buy CTAs are native POST forms to the live checkout with Australia (Sydney)
pre-selected (basket product_id 4, verified against the homepage card).
- images/city-sydney.webp: city-sydney.jpg resized 6720x2240 -> 1920x640,
9.8 MB -> 131 KB.
- index.html: the Sydney server card now links to the new page.
- sitemap.xml + llms.txt: page registered.
Evidence: verify-html.py PROBLEMS 0, verify-australia-page.py PROBLEMS 0,
JSON-LD parses, impeccable detect run (findings recorded).
The list becomes the same four items in all three English pages:
ODOO Bootcamp -> /odoo-erp-automation-bootcamp/ (new, first),
ODOO Hosting in 2027 -> the existing comparison page (renamed from
"Odoo hosting 2027", added to the homepage which lacked it),
Support, Legal. Home / Privacy / Terms / Contact us removed.
Content-only edit inside the existing <ul>; each page keeps its own
href style. No CSS, no markup pattern change, no other footer column
touched. de/ and es/ untouched (translator owns those in parallel).
Checks: verify-html.py PROBLEMS 0; work/verify-footer-useful-links.py
PROBLEMS 0; impeccable detect finding set identical to HEAD (87
anti-patterns / 100 advisories, all pre-existing); browser check at
1280px and 375px on all three pages: 4 items, no overlap, no
horizontal overflow.
The EN | DE | ES switcher existed but rendered into the footer slot, so a
visitor had to scroll to the bottom of the page to find it (measured live:
y=5582 on /, y=5498 on /odoo-erp-automation-bootcamp/). The user asked for a
selector a visitor can actually see.
- index.html, de/index.html, es/index.html: the slot is now the first child of
.hero .container, right-aligned in flow above the kicker. In flow, not a
fixed pill, so it cannot collide with the absolute .hero-notice.
- odoo-erp-automation-bootcamp/index.html: the slot is now the last child of
.bc-topbar .container, right of the nav links, behind a hairline brass
divider; .bc-topbar-links gets margin-left:auto so the brand stays left.
- One [data-lang-switcher] per page; the footer slot is removed everywhere.
- Styling follows the CI: Inter 12px / .06em / uppercase codes (the existing
.lang-switch rules), active language in --brass on these midnight surfaces,
the other two in the surrounding text colour. CSS-only, in each page's
inline <style>; lang.js is untouched, so its redirect behaviour is unchanged.
- The rules are recorded in work/i18n/I18N-BUILD-PLAN.md §3 for the phase-2
pages (de|es/odoo-hosting-2027.html, de|es/support.html).
Homepage pilot of the website i18n project. Adds the translated homepage for DE and
ES, the shared selector at /static/lang.js, and the hreflang cluster on all three
language versions (EN included, or the cluster is asymmetric and ignored).
- de/index.html, es/index.html: translator output plus the i18n build (html lang,
self-referencing canonical, hreflang cluster, lang.js tag, switcher slot in the footer).
- index.html: same cluster + lang.js tag + switcher slot; canonical and og:url
normalised to the lowercase host.
- static/lang.js: the tested selector (client-side language choice, auto-detect on the
EN homepage only, once per session; a stored choice or ?lang= always wins).
- sitemap.xml: /de/ and /es/ (priority 1.0, lastmod 2026-10-24).
- llms.txt: new '## Languages' section.
Two gaps in the build recipe were closed while building: a mirrored page cannot use
root-relative source paths (style.css, images/* would resolve to /de/style.css -> 404),
so the build now re-points assets at the site root and keeps page links on the EN route
until the language page exists; and og:url is now this page's own URL instead of the
EN one inherited from the source. Not built yet: support.html, odoo-hosting-2027.html,
the blog, the bootcamp page, the legal pages.
User-supplied, private self-hosted Plausible instance. Snippet inserted verbatim (unmodified,
byte-for-byte) as the last thing in <head> of index.html only, immediately before </head>.
No other page touched; no CSP exists on the site so no policy change needed.
Checks: verify-html.py PROBLEMS 0; node --check script.js OK; tracker script HTTP 200
application/javascript.
The homepage cards gave no domain count to Side Hustle or On the Rise and
gave three to Powerhouse, while the in-app plan page (odoo/upsell.html) and
the published terms.html both say one domain for Side Hustle and three for
On the Rise and Powerhouse. Cards now read 1 / 3 / 3; PRODUCT.md and
llms.txt carry the same counts so no future page has to guess.
Applies the 16 findings the user answered in projects/active/odoo-hosting-2027-landing/
decisions-2026-10-21-user.md, using the wording from
copywriter/out/site-fix-wording-2026-10-21.md.
index.html: Powerhouse 535 -> 595 in the card and in the JSON-LD Offer; delete the
three Save 8%/9%/10% badges and the empty badge div on the Empire card; replace the
'Save 15% with yearly billing' line and the coupon message text (ids kept - script.js
reads them); delete the FREE TRIAL coupon input block; qualify the checkout line as
'Card required to buy, not to start'; fix 'redundant slots' to the real rotating-slot
model; drop the 'coupon codes for startups and NGOs' promise; add Privacy and Terms to
the footer.
email/trial-confirmation.html: stop promising backups, domain mapping and SSL on the
trial (the trial has none); state what the trial is not; copyright 2024 -> 2026;
repoint Privacy/Terms from the 404 /legal/privacy and /legal/terms to the new pages.
support.html: state that custom and OCA modules install from On the Rise up and that
Side Hustle has no module installation.
odoo/upsell.html: '4 weeks free' -> '30 days free'.
blog: the backup speed-run no longer sends readers to a non-existent hosting
dashboard; my-first-post loses the 30-day refund promise and the dashboard domain
step; community-vs-enterprise-cost quotes $214/year instead of $19.40/month;
odoocommunity-agents drops 'per-seat licensing' from the body and both meta
descriptions.
sitemap.xml: add /privacy.html and /terms.html.
llms.txt: publish the Powerhouse price and list the two new pages.
Evidence: every old string verified gone and every new string present in the working
tree; 12 HTML files tag-balanced; JSON-LD and sitemap XML valid.
New page odoo-hosting-2027.html (feature + lifetime-cost comparison tables,
Odoo Online exclusion, Enterprise growth path, JSON-LD). Links it from the
homepage pricing CTA, adds it to sitemap.xml, adds llms.txt for AI-SEO.
Updates the homepage footer tagline to name AI Agents and N8N
automations explicitly. Markup-preserving: existing <strong> emphasis
on "stability" and "power" is unchanged; no structural, style or
layout changes.
- Buy Now product IDs aligned to the Odoo shop (54/55/56), matching odoo/upsell.html
- support.html contact form: rewire from dead ODOO4projects.com/website/form endpoint to n8n CRM webhook; remove /web/csrf_token prefetch; show success only on res.ok (previously always showed 'submitted' even when nothing was delivered)
- /contactus links (footer nav, Schema contactPoint, order widget fallback, trial email) retargeted to support.html#contact; canonical/og:url corrected to /support.html
- Custom Quote and footer contact handlers hardened with res.ok checks
- buyNow basket target verified: 20.odoo4projects.com/shop/post_basket (200)
- Dark text (#171c26) for all form fields in modal on ivory background
- Explicit colors for labels, inputs, placeholders
- Close button (X) now visible with midnight background
- Change section header to 'Order & Pricing'
- Add 'Get Custom Quote' button that opens modal with name, email, requirements fields
- Requirements field has placeholder: 'Describe your requirements (how many users? onsite, cloud? how many workers?)'
- Submit to https://n8n.odoo4projects.com/webhook/ODOO4projects/crm with UTM tracking
- Empire 'Get Custom Quote' button now opens modal instead of Order Widget
- Add CSS for modal overlay, form styling, and mobile responsiveness
- includes spin_up_free_trial skill with MCP server reference
- defines six server locations (Manchester, Sydney, Mumbai, São Paulo, Meppel, Boston)
- provides workflow steps and clear instructions for conversational agents
- Replace hidden form POST (target='_blank') with silent fetch() that
ignores the response — no new window opens
- Send boolean values directly instead of serializing to 'true'/'false' strings
- Update subtitle: 'Download your PDF in minutes.' -> 'We will send the report in minutes.'
- Add email input field after textareas, before checkboxes
- Email required via both HTML 'required' attribute and JS validation
- POST email field to webhook as 'email' key
- Add .biz-field input[type='email'] CSS (inline + external) matching textarea style
- Update success message: 'We will email you the report once it is ready. AI generation typically takes 1-2 minutes.'
- Remove reference to new window / auto-download (no longer opens window)
- Replace fetch() with hidden form POST + target='_blank' so the webhook's
auto-download HTML page opens in a new tab/window
- Send form-encoded fields (not JSON) since webhook expects POST form data
- Update success message: 'A new window has opened with your personalized
Odoo project plan. The download will start automatically after research
is complete.'
- Remove dead bizDownloadBtn references (button no longer needed)
- Add 3 checkboxes between textareas and submit button:
- I already have a homepage (bizHasHomepage)
- I already use ODOO (bizUsesOdoo)
- I already have a developer for modules (bizHasDeveloper)
- POST to /webhook/report instead of old webhook UUID
- Include checkbox boolean values in request body
- Add .biz-checkbox-group and .biz-checkbox CSS (inline + external)
- Replace .final-cta section with .biz-wizard section
- Three textarea fields: Business Description, My Goals, Current Challenges
- On submit, POSTs to webhook endpoint that triggers AI Agent to
generate a personalized Odoo project plan PDF
- On success, shows download button if webhook returns download_url
- Inline + external CSS: midnight background, brass-accented form fields
- Scroll animation observer updated to target .biz-wizard .container
- Remove all .final-cta CSS from style.css and inline critical styles
- Remove billing-toggle-wrap HTML (switch + labels)
- Update all 4 pricing cards to show yearly prices by default
with / year period and save percentage
- Simplify openOrderWidget() to always use yearly pricing only
- Remove billing toggle JS block (updatePrices, setBilling, event listeners)
- Remove billing toggle CSS (billing-toggle-wrap, .toggle-*, .toggle-switch)
- Update order widget default display to / year
- Update CTA note to 'Save money, less administration'
- Inline all above-the-fold CSS (hero, server-banner, buttons, layout) as
inline <style> in <head> to eliminate render-blocking style.css
- Load full style.css via media='print' onload pattern (async, non-blocking)
- Move GSAP from <head> to before </body> (right before script.js)
- Preconnect to cdnjs.cloudflare.com
- Override Font Awesome 6 font-display to 'swap' to prevent FOIT
- features-grid changed to repeat(3, 1fr) for 2x3 layout
- 6th cell is a Free Trial card with midnight bg, brass border
- Email input, location dropdown (preselected to best ping server),
brass Send button
- POST to webhook as JSON {email, location}
- On success: form hides, success message shown
- Responsive: 3col -> 2col -> 1col
- Header changed to 'Our Server Locations'
- Cards hardcoded in HTML with just city name + region
- Removed all ping JS (IIFE, pingServer, markBest, etc.)
- Removed server-ping-wrap, best-badge, server-best, ping-spinner,
status-dot, server-optimal CSS
- Removed empty server-optimal div
- Removed card entrance animation (static display)
- Added GSAP 3.12.5 from CDN
- Card stagger entrance now uses gsap.fromTo with 0.6s duration, 0.09 stagger
- Carousel slide uses gsap.timeline: old slides out right (3s), new slides
in from right (3s), label slides in (2.5s after 0.3s offset)
- All animations use power2.out easing
- Image brightness: opacity 0.12→0.25, filter brightness(1.15) saturate(1.1)
- ::after overlay changed from solid midnight @ 0.6 to gradient
(midnight→transparent @ 0.5) — brighter image with smooth falloff