The visible FAQ answer used arrows while the structured-data copy spelled the
chain out with 'to', so acceptedAnswer.text was not verbatim-present on the
page. FAQ parity check now reports 8/8 on EN/DE/ES.
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.
Copy-only mirrors of odoo-community-upgrade.html built by translator against the
post-R1-R5 English (EN af27098a, commit 2fe0c31), rendered through work/i18n/build-i18n.py:
<html lang>, canonical, 4-way hreflang cluster (en-US/de/es/x-default), og:url,
/static/lang.js and 15 rebased refs. No EN page change.
- sitemap.xml: 33 -> 35 <loc>, both new pages at priority 0.9
- llms.txt ## Languages: DE + ES lists now name /<lang>/odoo-community-upgrade.html
- checks: check-links.py 0 broken, verify-html.py 0 problems (tag balance, JSON-LD,
sitemap XML), JSON-LD FAQPage answers verbatim in the visible FAQ 8/8 per page
Retrospective ICP 1 review (kanban t_03b62410) found the EN page made a
non-technical owner-operator hesitate: the offer sat behind four blocks of
shell commands, the hero ended on "a developer", one hero line carried an
untraced claim, there was no route to a number, the FAQ printed the
OpenUpgrade data-loss warning against our own fresh-database service, and
the "restaurant meal" simile was unitless.
R1 move "The ODOO4projects way" above the process and the commands; swap the
two band classes so the page still alternates; keep the offer's code cards
legible on the dark band; reword the §5 intro and §6 opening sentence.
R2 hero subhead reworded, dropping the untraced "costs you your history".
R3 "See the plans" route added inside §6, with the honest "not priced on that
page" clause. No price, no plan hero, no Offer node.
R4 FAQ "What happens to my history…" answer rewritten (JSON-LD acceptedAnswer
kept verbatim-equal to the visible text).
R5 "restaurant meal" clause deleted.
Band-aware text colours added for the elements the class swap re-parented
(process list on light, technical section and its notes on dark, the §6 link).
No other content changed: H1, CTAs, code blocks (all 4 byte-identical),
Sweat Equity, the 8 FAQs and the JSON-LD shape are untouched.
Four follow-up defects found beyond the 2026-10-27 SEO audit (no copy changes):
1. legal.html canonical was https://ODOO4projects.com/legal — uppercase host AND a
path that 404s. Now https://odoo4projects.com/legal.html. The file's og:url keeps
its path /legal but its host is lowercased (path fix awaiting a decision).
2. The no-"4" domain typo in reader-visible <pre><code> body text of
blog/posts/{guide-migrate-enterprise,speed-run-deploy,my-first-post}.html:
odooprojects.com -> odoo4projects.com. Domain string only, no other wording.
3. Lowercase host in every machine-read value: the robots.txt Sitemap line, the
homepage + de/ + es/ Organization JSON-LD url / contactPoint.url / offers[].url,
legal.html og:url, and the four absolute self-URL hrefs in blog posts,
odoo/support.html and email/trial-confirmation.html. Display text untouched
(support@ODOO4projects.com stays as written).
4. One BlogPosting per blog post (15), every value taken from the page itself:
headline = its <h1>, datePublished = its visible post-meta date, url = its own
canonical, author/publisher = the Organization the site already publishes,
image = its og:image where one exists (omitted on the 10 posts that have none).
Empire ">950.00", FAQPage on support.html, the blog-chrome change and the bootcamp
date conflict are untouched — decisions pending with the user.
From seo/out/site-seo-ai-search-audit-2026-10-27.md (§6 items 1, 2, 3a, 5).
- canonical: 10 blog posts declared https://odooprojects.com/... — a live parked
third-party domain — as their canonical. Now self-referencing on
https://odoo4projects.com/blog/posts/<slug>.html (1 line each, nothing else).
- sitemap.xml: 27 -> 33 URLs. Added the 6 live pages that were missing
(support.html, legal.html, community-vs-enterprise-cost, 6-tips,
odoocommunity-agents, odoo-20-is-live); corrected 13 lastmod values to the
real last content commit (homepage was 4 months stale); /de/ and /es/
priority 1.0 -> 0.7 so a translation does not outrank its original.
- blog/index.json: 7 -> 15 posts. The 8 orphan posts are now listed, so every
post is reachable from the homepage grid.
- llms.txt: removed the link to /blog/ (that URL returns 403) and added a
"## Blog posts" section listing all 15 posts with date and summary.
Not changed on purpose: FAQPage on support.html, the homepage "price": ">950.00",
the bootcamp start-date conflict, and the blog post chrome (15 files).
New EN landing page for the head term "ODOO Community Upgrade": hero, what an
upgrade is, the 10-step published process, a developer-labelled technical section
with four sourced command blocks and their substitution notes, the ODOO4projects
migration service (new database on our servers), the self-hosting cost honesty
section, an 8-question FAQ and a final CTA. House CI only, page-scoped CSS, no
new blocking resources.
- CTAs are plain links to the real flows: /support.html#contact (opens the
migration ticket) and /index.html#pricing. No basket POST and no product id:
no price is published for the migration service, so wiring a buy would invent one.
- JSON-LD: WebPage + Service + FAQPage (8 questions), no Offer node.
- sitemap.xml: new URL added; llms.txt: page listed under Key pages.
- support.html: the "older version" FAQ answer now links the new page so it is not orphaned.
Copy: copywriter/out/odoo-community-upgrade-page-copy-2026-10-27.md. The 7
NOT PUBLISHED gaps (G1-G7) are not in the build.
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 post carried the planned/September copy ("scheduled to go online on the
06.10.2026"). Body replaced verbatim with the supplied live copy (1379 bytes,
2 h2, 221 words), post-meta -> October 27, 2026 · News, meta/og description
refreshed, title/canonical/chrome/og:image unchanged.
- blog/index.json: the existing entry moved to first, date 2026-10-27,
teaser = first 120 chars of the new first paragraph (text splice).
- sitemap.xml: one new <url> as the first 0.6 blog entry (24 -> 25 <loc>).
- work/verify-html.py: page added to FILES (in webmaster/work, not in repo).
The user reversed the earlier 'publish it' decision: the trial endpoint
paragraph (and its now-dead .ai-endpoint CSS rules) come off the AI
section. The 30-day trial line in 'Some other information' stays.
- HTML: <p class="ai-endpoint"> removed (was the AI section's last para)
- CSS: .ai-endpoint, .ai-endpoint a, .ai-endpoint a:focus-visible deleted
(the focus-visible selector dropped from its group; group otherwise intact)
- grep ai-endpoint 0, mcp/trial 0, competitor grep 0
- verify-html.py PROBLEMS: 0; impeccable detect 10 (no new finding)
The user asked to drop the 'Start a free trial' button from the hero.
The hero now ends with the supporting paragraph; the now-dead
.andrew-actions rule (its only consumer) goes with it. The 30-day trial
line in 'Some other information' is untouched.
Diff: 8 deletions, one file. verify-html.py PROBLEMS: 0; competitor
grep 0; impeccable detect 11 -> 10 (the removed finding was the CTA
button's low-contrast), no new findings.
git mv for-andrew/index.html odooityourself.html (history kept) and drop the
now-empty for-andrew/ directory, so the page serves at the flat root URL
https://odoo4projects.com/odooityourself.html with no directory redirect.
Update every reference to the old path: canonical, og:url and the WebPage
JSON-LD url in the page; the sitemap.xml <loc> (replaced, lastmod 2026-10-25,
still 24 entries); and the llms.txt key-page line. Copy, CSS, assets and all
other pages are untouched.
/for-andrew/ was never announced or indexed, so it is deliberately left as a
404 — no redirect added.
Six-section briefing page (hero vision, 2x3 feature grid, AI/MCP section
with art-director's CI SVG + mobile variant, management console with the
real screenshot, pricing with Empire quoted and no number, some other
information with the demo checklist). Zero competitor mentions.
Reuses the existing bootcamp hero assets; adds /images/andrew/ SVGs,
sitemap + llms.txt entries.
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 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 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.
The user asked for a photo in the header of /odoo-erp-automation-bootcamp/.
Adds our own London night aerial (art-director's chosen cut, measured) as the
page-scoped .bc-hero background, under a midnight gradient scrim
(rgba(13,27,46,.72) -> .55, inside CI §5's band) and over the --midnight base.
- desktop: bootcamp-hero-desktop-1920.webp, cover, background-position 50% 25%
- <=640px: bootcamp-hero-mobile-900.webp, background-position 50% 50%
- the global .hero::after 50% wash is switched off on this page only, so the
scrim above is the one that was measured
No copy, buy form, JSON-LD, language selector or hreflang change. One CSS block,
two images, nothing else. verify-html.py PROBLEMS: 0; impeccable detect finding
set identical to HEAD; check-links 0. Worst p05 over rendered type 5.64:1.
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).
The user's instruction: use 'Oliver - The OC Guy - Arnold' (his alter ego
'The OC Guy'). Rendered with the typed hyphen form in the two places the name
is shown - the facts-list <dd> and the 'Your instructor' <strong>. Bio
paragraphs, <title>, meta description and FAQ text untouched.
JSON-LD: both Course/CourseInstance instructor Person nodes keep
name 'Oliver Arnold' (legal name) and gain alternateName 'The OC Guy'.
- 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
The three CTAs (hero, mid-page, §7) were <a class="btn-buy"> that only worked
through JavaScript. They are now native <form method="post" target="_blank">
elements posting basket=[{"product_id":12,"qty":1}] + clear=1 to
https://20.odoo4projects.com/shop/post_basket, so the buy works with JavaScript
disabled; the button keeps the brass styling and the same size classes.
static/buy.js no longer builds a form on click. It is now pure progressive
enhancement: a capture-phase submit listener appends the utm_source/medium/
campaign hidden inputs from the cookies and lets the native submit proceed.
One page-scoped declaration keeps the button height at 55px: a <button> takes
line-height:normal from the UA font shorthand where the <a>s inherited the body
line-height (measured 49.19px vs 54.78px without it).
Rebuild the two German phase-2 pages from translator/out/de/ with the standard
build recipe (lang, canonical, four hreflang links, top-bar language selector):
em-dashes cut in the German copy (odoo-hosting-2027 30 -> 5 body em-dashes,
support 8 -> 6 body) so the DE pages no longer exceed the EN source. Punctuation
only, no re-translation. ES and EN pages unchanged.
impeccable detect: finding profile unchanged except the [em-dash-overuse]
advisory on de/odoo-hosting-2027.html, which is resolved.
The three Reserve-your-seat CTAs posted to /support.html#contact. They now
run the same checkout path as the main page's Buy Now: a new page-local
static/buy.js mirrors script.js buyNow() (POST basket to
20.odoo4projects.com/shop/post_basket, clear=1, UTM cookies appended) and
binds .btn-buy via one delegated click handler. CTAs become
.btn-buy[data-id=12]; label unchanged. script.js and the homepage untouched.
The Price-and-booking section keeps a second, smaller link to
/support.html#contact so readers who have questions still have a fallback.
Four new pages, built from the verified translator output with work/i18n/build-i18n.py,
plus the same hreflang cluster and top-bar language selector on the two English pages.
Same recipe as the homepage pilot (main 937dbd4 / 06beab4); nothing else touched.
- de|es/odoo-hosting-2027.html, de|es/support.html: per-language canonical, the full
hreflang cluster (en-US, de, es, x-default -> EN), /static/lang.js, and the switcher
slot placed in the page's own top bar (.cmp-topbar on the 2027 page, .blog-header on
support.html) with the per-shape CSS hook from I18N-BUILD-PLAN.md §3.
- odoo-hosting-2027.html, support.html: same cluster, same selector, so the cluster is
symmetric (asymmetric hreflang is discarded by Google). support.html's canonical and
og:url were still on the capitalised ODOO4projects.com host; the build normalises them
to the lowercase form the rest of the site and sitemap.xml use.
- Internal links: the build's rebase step now also localises a root-absolute page link
(/support.html#contact in the translator's 2027 pages) once that page exists in the
language. /de/support.html, /de/odoo-hosting-2027.html, /de/, /de/#pricing and
/de/#trialCard are local; /legal.html stays on the EN route because the legal pages
are not translated (lawyer review pending).
- sitemap.xml: +4 URLs, lastmod 2026-10-24, priority reused from the EN entry (2027 -> 0.9,
support.html -> 0.6 fallback, it has no EN entry). 17 -> 21 <loc>, no duplicates.
- llms.txt: the ## Languages section now names the mirrored pages.
Checks: verify-html.py PROBLEMS: 0; node --check static/lang.js OK; test-lang.js 31/31;
test-build.py 64/64 (was 43; +21 for the four built pages and the root-absolute rule);
check-links.py on the four new pages BROKEN/MISPLACED: 0.
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).
The paragraph explained the 12-to-10 lesson merge — internal reasoning
about the old page, with no place on a sales page. The ten-lesson <ol>
and the section layout are unchanged.
The user confirmed the price (USD 790) and the term (Q1 2026, already
correct). Replaces the 'Price and booking' placeholder sentence with the
price statement and drops the TODO(olchi) comment that marked it. Adds an
Offer (price 790, USD, Paid) to the Course node so the price is
machine-readable; the FAQPage node is unchanged.
New directory page built from the copywriter's reviewed copy (webmaster/out copy:
/root/workspace/copywriter/out/2026-10-24-bootcamp-page-copy.md), with the four
placeholders resolved: Thursdays 16:00-17:00 (UTC-3), first session Thursday
8 January 2026 (ten sessions, last 12 March 2026), no price printed (price is
confirmed on registration, offers omitted from JSON-LD), booking CTA is
/support.html#contact.
- odoo-erp-automation-bootcamp/index.html: standalone landing page in the CI
(midnight/brass/cream, Marcellus+Inter, page-scoped CSS, non-blocking
style.css, no third-party JS), 8 H2 sections from the copy, Course + FAQPage
JSON-LD, footer from index.html with the language-switcher slot.
- sitemap.xml: 16 -> 17 <loc> entries (lastmod 2026-10-24, priority 0.9).
- llms.txt: one line for the bootcamp under Key pages.
Verified: verify-html.py PROBLEMS 0 (FILES extended with the new page),
node --check script.js OK, no console errors at 1280/375, no horizontal
overflow at 375.
The switcher links were plain anchors: clicking one navigated but stored nothing, so
the auto-detect on the EN homepage bounced the visitor straight back. A de-DE browser
on /de/ that clicked EN landed on / and was replaced back to /de/ — the 'an explicit
choice always wins' promise in the plan and in llms.txt was only true for ?lang=.
A click now calls remember() with the link's own hreflang, so the choice is stored in
localStorage + the o4p_lang cookie and survives the next session. Selector tests
27 -> 31 (switcher click stores EN, sets the cookie, stores DE, chosen EN survives a
new session).
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.
Reuses the exact snippet shipped on index.html in 6bb529a, inserted
verbatim as the last thing in <head> (before </head>). Diff is this
file only; the ld+json @graph block is byte-identical.
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.
- blog/posts/odoo-19-to-20-invoice-migration-with-n8n-and-mcp.html: in-post image
much.png + og:image
- blog/posts/track-the-oca-modules-you-actually-use.html: in-post image
oca_module_status.png + og:image
- blog/index.json: optional "image" key on those two entries only (text splice)
- script.js: renderBlog() draws .blog-thumb only when the entry carries an image
- style.css: .blog-thumb rule
- sitemap.xml: lastmod 2026-10-24 for the invoice-migration post
Built by profile webmaster (run 20261024_094206_5c4f28, verified PROBLEMS: 0 /
node --check OK); committed by olchi after that run died on an OpenRouter 402.
Add a Video Credits legal-card after Image Credits and before Trademark
Notice, carrying the 10 user-supplied Pixabay video attributions verbatim
(hrefs, utm params and the intentional /de// double slash preserved).
Rows use the existing .credit-row pattern; .video-credits mirrors
.image-credits in the page's inline <style>. Links carry target=_blank
rel=noopener to match the Image Credits convention. Meta description now
lists image and video credits.
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.
script.js: delete applyCoupon(). Its only caller was the coupon button deleted above,
so the function was unreachable dead code that still carried the removed 'Coupon FREE
TRIAL applied - 1 month free' promise in a publicly served file. It would also have
thrown on the two DOM ids that no longer exist.
PRODUCT.md: this file is served publicly and still advertised $19.40/mo, $36.25/mo,
$49.60/mo, the FREE TRIAL coupon, 'yearly billing saves roughly 8-10%', a 'Satisfied
or reimbursed' guarantee and 'redundant slots'. Corrected to the yearly list prices
the site publishes and to the decisions above.
Two new root-level pages, assembled from legal.html so they inherit the existing
head, fonts, style block and .legal-* cards. The trial email's /legal/privacy and
/legal/terms links 404 on the live site and are repointed here.
Both are factual drafts built from product facts (data held, the six regions, Stripe,
cancel-anytime, backup slots) and are NOT lawyer-authored. Decision 12 marks them for
legal review; they must not be treated as final before that review.
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.
Match the pattern index.html already uses (media=print + onload swap,
noscript fallback) so the new page adds no render-blocking stylesheet.
Page-scoped critical CSS stays inline.
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.