12 Commits
Author SHA1 Message Date
webmaster 9d14652a57 hide [hidden] site-wide: one [hidden] { display:none !important } in style.css
A filtered-out directory card stayed visible. The card is targeted by an
id-based ready-state selector (`.js #directory[data-dir-state="ready"]
#dirGrid > .dev-card`, two ids), which outranked the old per-element patch
`.dev-card[hidden] { display:none }` — so `card.hidden = true` set the
attribute and every card stayed on screen while the count line correctly
read "Showing 1 of 4".

Fix: one rule, `[hidden] { display:none !important }`, early in /style.css.
A class- or id-based `display` can no longer beat the attribute, so this
cannot recur with the next class that sets `display`. The per-element
`[hidden]` patches are removed so a single mechanism governs:
 - odoo-community-developers: cards + filter row + pager + modal
   (load-bearing — each sets `display`); focus-sliders/noscript (redundant)
 - index/de/es `.hero-notice[hidden]`, odoo-black-friday `.ads-consent[hidden]`
   (redundant — those elements set no `display`)
Re-fingerprint local assets (style.css changed): /style.css?v 3332fbba93 ->
061517baef across 51 pages.

Measured in a real browser (local harness, stubbed webhook; no record created,
no probe write) — computed display + geometry, never the `hidden` property:
 - filtered to one card: the other three report display:none and height 0;
   the visible set (1) equals the count line ("Showing 1 of 4") — 1 == 1
 - HEAD (pre-fix) reproduces the defect: 3 cards hidden=true yet display:flex,
   height 599.38 each; 4 visible vs the count's 1
 - pager: display:none height 0 with one page; display:flex height 66.83 with
   12 cards (Page 1 of 2); page 2 hides page-1 cards (display:none)
 - modals closed: display:none height 0; load state: cards 0, pager/empty
   display:none, filter row visibility:hidden by design
 - 375 px: same result, no horizontal overflow; avatar ask-row top identical
   across a row (2053.97 / links 2086.91)
2026-11-03 22:34:39 +00:00
webmaster bd0881f882 cache-bust local assets; narrow the language facet to scores >= 3
Part 1 — asset versioning: every local <script src> and <link rel=stylesheet> on the
tracked pages now carries ?v=<sha256-of-the-file> (10 hex), computed at BUILD time from
the file's bytes by work/build-asset-versions.py — never hand-typed, and idempotent
(re-running with no file change rewrites nothing). This is what makes an edited
focus-rule.js / photo-upload.js a new URL instead of a stale cache hit.

Part 2 — language facet: a language now counts for the Language filter only when its
0-5 self-assessment is >= LANG_FACET_MIN (3 = 'works in it'). The constant sits beside
the 15-point rule in the page script and its mirror in build-directory.py. Nothing
stored changes; the card's languages chart still prints every language above 0, the
search box is unchanged, and the facet stays an exact membership test against the
card's value. A '1' language no longer offers the person under that language.
2026-11-03 21:26:58 +00:00
webmaster 53e9ea999d feat(footer): add Odoo Freelancer Directory to Useful Links; drop the directory page's bottom Start CTA
- 13 tracked pages + the directory page's footer carry a new <li>
  <a href="/odoo-community-developers/">Odoo Freelancer Directory</a>,
  placed after "ODOO Hosting in 2027" (our service links together, before Support/Legal).
- The directory page itself is excluded (a page does not link to itself in its own footer).
- Removed the final Start / "Browse the freelancers" CTA section from the directory page;
  the hero copy keeps its one "Browse the freelancers" link. .cmp-cta CSS untouched (11 pages).
2026-11-03 17:05:18 +00:00
webmaster 04199bd606 fix(freelancer-directory): delete the dark anonymous-contact modal; retire hello@ -> support@ site-wide
Two user decisions (2026-11-03). The page is live.

1. The anonymous-contact modal is removed in full, not just its gate: its markup and
   comment, the .contact-modal and .dev-anon-btn CSS, the JS (modalEls, openContactModal,
   closeContactModal, initContactModal, enhanceContact), the two init-time calls and the
   per-card render call. ODOO_EXPERTS_QUOTE_WEBHOOK is deleted — the only readers were the
   two gates (initContactModal, enhanceContact) and the postTo inside the modal's own
   submit handler; nothing else read it. The modal's lede ("We pass your request on ...")
   goes with it, so no relay copy survives. Kept on purpose: the invite relay form and
   ODOO_EXPERTS_INVITE_WEBHOOK, the OCA filter + badge, ODOO_EXPERTS_WEBHOOK, and the
   card's direct "Email this freelancer" chip.

2. hello@odoo4projects.com is retired site-wide: 24 occurrences (mailto + visible text)
   become support@odoo4projects.com, and the 12 support@ODOO4projects.com case variants
   are normalised to lower case. oliver@odoo4projects.com is a freelancer's own published
   contact address on their card and is left untouched.

Tests run: verify-html.py PROBLEMS 0; check-inline-js.py OK; competitor QC exit 3 with 74
pre-existing hits and 0 added; impeccable detect 1 -> 1 (identical, 0 new).
2026-11-03 13:52:44 +00:00
webmaster 0e832d8fd1 perf(site-wide): render-blocking /style.css on 17 pages; <main> landmark; BF footer form action
Same defect the bootcamp carried (2aa9b19), fixed site-wide in one mechanical pass.

P0 (17 pages): /style.css was deferred via media="print" onload=... -> unstyled
first paint -> a BODY layout shift. Now a normal render-blocking
<link rel="stylesheet">; the now-redundant no-JS <noscript> for style.css is
dropped. Web fonts + Font Awesome keep their legitimate deferral.
Pages: index.html, odoo-black-friday/, odoo-hosting-offers/, de/index.html,
es/index.html, odoo-hosting-2027.html (+de/+es), odoo-community-upgrade.html
(+de/+es), odoo-staging-environment/, odoo-community-ai/,
odoo-n8n-masterclass/, australia.html, workshops/, what-can-i-use-odoo-for/.

P1 (1 page): /odoo-black-friday/ footer form action mailto: -> CRM webhook
(Lighthouse is-on-https). Changed only because the page already loads a handler
for #footerContactForm (static/black-friday.js). The other 13 mailto-form pages
load no handler and are left alone on purpose (no drive-by handler copy-in).
Added the no-JS <noscript> fallback so a JS-off visitor keeps a path.

P2 (35 pages): add a single <main> landmark wrapping the primary content; top
bar and footer stay outside. Additive only, no redesign.

Guards held: bootcamp EN/DE/ES and every JS/static file untouched; Buy forms
product_id 4/17 intact; one [data-bc-seats]; ads-consent gate intact.
verify-html.py PROBLEMS 0; competitor QC 74 pre-existing, 0 added.
2026-11-02 18:18:46 +00:00
webmaster 2359e87a35 feat(bf): put the HOST27 deal on the homepage; deal window now starts 2 November
The coupon applies today (signed-out test: post_basket -> /coupon/HOST27 ->
cart 295.00 from a 395.00 list), so the offer starts today, not on 16 November.

- index.html: one seasonal banner above the hero, a single <a> to
  /odoo-black-friday/. Homepage components and CI only (midnight/brass/cream,
  Marcellus/Inter). No script, no tracking tag, no event: the homepage has no
  consent gate, so nothing measurable may be added there. Hero, server
  selector, section order and JSON-LD untouched. Dated for removal after
  30 November 2026.
- odoo-black-friday/: every start claim 16 -> 2 (meta, og:image:alt, JSON-LD
  WebPage description + two FAQ answers, hero-sub, two body FAQ answers).
  End date, countdown instant, forms, consent gate and the three conversions
  unchanged.
- odoo-hosting-offers/: same start claim on the second ad lander, so the AG1
  visitor is not told the deal begins in two weeks.

Every price claim keeps the "first year" qualifier.
2026-11-02 10:27:02 +00:00
webmaster 46b943bffe fix(copy): qualify version upgrades as in-version only; major migration is a separate quote
The four 'version upgrades are run for you' lines (black-friday, hosting-offers,
hosting-2027 body + table row, llms.txt) and their JSON-LD descriptions now say
updates and in-version upgrades are included, and name a major-version migration
as a separate, quoted service. terms.html: migration = separate service, quoted
separately; release list no longer reads stale (names Odoo 20 as current).
No offer, price, page structure or ad change.
2026-11-02 08:31:51 +00:00
webmaster 4801e09466 tracking(bf): mark the offer CTAs on the two Black Friday pages as Buy
The two Black Friday landing pages carried 0 Buy marks, so the page we are
about to advertise reported nothing. Buy is a CLICK PROXY by contract (it marks
a click on the offer CTA, not a completed purchase); the real purchase is fired
on the checkout host (20.odoo4projects.com) by odoo-dev. Both CTAs on
/odoo-black-friday/ and the BF-block link on /odoo-hosting-offers/ now carry
plausible-event-name=Buy, each labelled in-page as the proxy it is.

No Google Ads tag added: see handoff 2026-11-01-webmaster-to-olchi-ads-tag-and-buy-proxy.md
(privacy.html discloses no advertising cookies and the site has no consent gate).
2026-11-01 18:45:42 +00:00
webmaster f457858a0f fix(bf): strip the last off-panel mentions of the discount code
The code may not be named where a visitor or crawler can read it before
opening a reveal panel. Three off-panel leaks removed:

- /odoo-black-friday/ FAQ (visible answer + its FAQPage JSON-LD twin,
  byte-identical): 'the code stops working' -> 'the offer stops working'.
- /odoo-hosting-offers/ og:image:alt: the trailing ', and the code
  HOST27' dropped; the offer and the 16-30 November window stay.
- /odoo-hosting-offers/ date-scoped Black Friday block: the line no
  longer names the code; it states the offer and links to
  /odoo-black-friday/ (the only page with a reveal panel), marked with a
  dated comment for removal after 30 November 2026.

HOST27 now survives only inside the .bf-panel on /odoo-black-friday/;
/odoo-hosting-offers/ carries zero.
2026-11-01 14:24:39 +00:00
webmaster 6c488a443f feat(seo): set the Black Friday share image on the two BF pages
/odoo-black-friday/ gains og:image + og:image:width/height/alt + twitter:image
pointing at the 1200x630 OG card; /odoo-hosting-offers/ reuses the same card.
Both pages declared no og:image before.
2026-10-30 13:33:24 +00:00
webmaster 2a62ac871a fix(bf-pages): restore ODOO Bootcamp footer link; add HOST27 to the BF block
Both pages shipped (355c5d0) with a three-item footer; the site footer is a
four-item commitment. The /odoo-erp-automation-bootcamp/ entry is restored on
both pages, matching index.html / odoo-hosting-2027.html.

/odoo-hosting-offers/: the code HOST27 is added inside the date-scoped Black
Friday block only (BF-BLOCK-START..END), so it disappears with the block after
30 Nov. Title, meta description and H1 are untouched — the intent split holds.

verify-html.py: PROBLEMS 0. node --check script.js: OK.
2026-10-30 13:22:48 +00:00
webmaster 355c5d022e feat(hosting): add /odoo-black-friday/ and /odoo-hosting-offers/ pages
Page A /odoo-black-friday/ owns "odoo black friday": hero, one On the Rise
box with a CSS-only reveal (native <details>) of the HOST27 offer, "what the
code does", FAQ. Page B /odoo-hosting-offers/ is the evergreen hub owning
"odoo hosting offers/deals/discount", with a date-scoped Black Friday block
(BF-BLOCK-START/END) linking to Page A. Both carry the copy's title/meta/H1,
@graph JSON-LD (Organization, WebPage, BreadcrumbList, Product+Offer, FAQPage).
sitemap.xml and llms.txt updated. Copy source: copywriter 2026-10-30.
2026-10-30 13:18:16 +00:00