Self-audit v2 (SELF_AUDIT_2026_ROUND20.md) gave the site a clean bill of health: 0 html-validate errors across 259 pages, 551 JSON-LD blocks all valid, no broken internal links, no duplicate content. The only real gap was missing geo coordinates on 140 venues. Round 20 closes the biggest remaining items.
- Geo - Hand-plotted lat/lng for 39 well-known Pattaya landmarks (Fairtex, Sityodtong, Royal Cliff, Sanctuary of Truth, Cartoon Network Amazone, Phoenix Gold Golf, Siam Country Club, etc.). LocalBusiness geo coverage went from 18/158 (11%) to 55/158 (35%).
- Geo - New scripts/geocode-venues-v2.js with smarter 3-strategy Nominatim retry (name+area, name+Pattaya, area-centroid fallback) for filling more over time.
- F4.Desc - Wired truncateDesc(155) to venue and area page meta descriptions. 10 over-160 description warnings closed.
- F22.CSP - sync-csp-hashes.js now PRUNES obsolete inline-script hashes by default (4 unused hashes removed). --keep-obsolete flag preserves the legacy behaviour.
- F24.Analytics - 4 new GA4 custom events: share_venue (venue share button), compare_pick (compare selection change), filter_apply (search filter change, debounced), sister_site_click (outbound to any of the 7 sister sites).
- Asset version 415 -> 416.
Closed the biggest finding from the Round 19 self-audit: 4 of 7 TimPaemi sister sites had zero inbound links from pattaya-gym.com. Plus killed 22 orphan pages by adding a category cross-link matrix to area pages and an expanded footer "Site" column.
- F-Sister - Sister network now fully cross-linked: timpaemi.com, pattaya-authority.com, pattaya-restaurant-guide.com, pattayavisahelp.com, pattayastream.com, pattaya-coffee.com, pattaya-school-guide.com all linked from footer + Organization.sameAs + Person.sameAs + privacy + about + press pages + llms.txt + humans.txt.
- F08.1 - Area pages now include a "Browse this area by sport" matrix linking every existing /area/<a>/<sport>/ combination. Closes 15 orphan category-area pages.
- F08.1 - Footer "The site" column expanded with /compare/, /pattaya-sport-stats/, /changelog/, /privacy/, /press/, /add-your-gym/. Closes 7 utility-page orphans.
- F05.1 - truncateTitle() and truncateDesc() helpers added; auto-truncate at word boundary for venue/category/area/category-area pages. Caps titles at 65 chars, descs at 155.
- Self-audit script wrote SELF_AUDIT_2026_ROUND19.md with 71 findings before the round started.
- Asset version bumped 414 -> 415.
Two big engineering wins from the Round 17 Codex audit backlog. Removed every third-party font request and shrank the inline-style surface so a future strict CSP becomes realistic.
- F14.1 — Google Fonts removed from every page. Self-hosted Space Grotesk (variable), Inter (400/500/600/700), JetBrains Mono (500/600/700) in /fonts/ with font-display: swap. CSP no longer allowlists fonts.googleapis.com or fonts.gstatic.com.
- F10.1 — Inline style="..." count dropped from 11,212 to 4,905 (-56%) by adding 30+ utility classes covering the most-repeated patterns. Builds the foundation for removing 'unsafe-inline' from style-src in a future round.
- Privacy page updated to drop the "may self-host in future" caveat — fonts are now self-hosted.
- New /fonts/* immutable cache rule in _headers.
- verify-deploy.js still passes the hardened gate (NUL/BOM scan + sitemap URL check).
- Asset version bumped 413 -> 414.
Closed every P1 finding from the Codex Round 17 audit plus 10 quick P2 wins. The single biggest improvement: generated venue HTML went from 210 html-validate errors to 0 by replacing the regex Markdown converter with markdown-it.
- F02.1 — Replaced bespoke regex mdToHtml with markdown-it; generated venue HTML now passes html-validate cleanly (210 → 0 structural errors).
- F07.1 — Sitemap GUIDE_SLUGS is now derived from disk so the sitemap can no longer advertise URLs whose local files do not exist.
- F20.1 — New /privacy/ page: Google Analytics 4, localStorage keys, AI crawler policy, GDPR/PDPA rights, full disclosure.
- F21.1 — Softened every "updated weekly" / "within 7 days" claim across homepage, marquees, generated pages, methodology, and about. Honest copy matching actual rolling-verification cadence.
- F23.1 — Hardened verify-deploy.js: NUL/BOM scan now covers every text source file; every sitemap URL must resolve to a local file.
- F01.1 — Search "Open now" filter now parses real HH:MM windows against ICT.
- F01.2 — Fixed 2 venue Markdown front-matter category mismatches (ALFA → bjj, Rambaa → mma).
- F04.1 — Geo coordinates in LocalBusiness JSON-LD rounded to 6 decimals.
- F05.2 — robots.txt now explicitly allows /og/ so Google/Twitter can fetch per-venue OG images.
- F07.3 — Deleted stray extensionless junk files from repo root, added .wrangler/ to .gitignore.
- F12.1 — ARIA live regions on search stats and compare status.
- F12.2 — Visible 3px focus-visible outline on search filter selects.
- F19.2 — Added browsing-topics=() to Permissions-Policy.
- F20.2 — Replaced overstated "privacy-respecting" GA copy with precise statement linking to /privacy/.
- Asset version bumped 412 → 413.
The /compare/ page was the last remaining honest-stub tool from Round 7. Round 16 turns it into a real, fully-functional, client-side comparison tool — pick up to 4 venues, see them side-by-side across 12 attributes, share the URL.
- /compare/ now embeds a 158-venue summary JSON and renders a side-by-side comparison table on demand.
- Up to 4 venues compared at once (slots a/b/c/d) with bookmarkable, shareable ?a=&b=&c=&d= URLs.
- 12 comparison rows: name · editors-pick · sport · area · price · hours · phone · website · maps · tags · description · verified-date.
- 3 preset buttons (premium Muay Thai, hotel gyms, budget camps) for instant exploration.
- Web Share API + clipboard fallback on the Share button.
- rebuild-tool-stubs.js skips /compare/ now that it has its own real builder.
- Asset version bumped 411 → 412.
/pattaya-sport-stats/ was ~150 words of static bullets. Now it is a server-rendered SVG dashboard regenerated on every build — proof of breadth and transparency competitors do not have.
- 4 big stat tiles: venues / categories / areas / paid placements.
- Horizontal bar chart of 15 categories ranked by venue count (15-color accent rotation).
- Horizontal bar chart of 6 neighborhoods ranked by venue count.
- SVG donut chart of price-tier distribution (฿/฿฿/฿฿฿/฿฿฿฿) with legend.
- Verification-freshness breakdown (within 30 / 30-60 / older).
- 4 schema-completeness ring gauges (body / geo / phone / website).
- All inline SVG — no JS, no charting library, no external dependencies. Asset version 410 → 411.
Codex V3 P1-6 finding closed — every area page now has a real ~2000-word neighborhood guide instead of a sparse venue list. 6 areas covered in editorial depth.
- AREA_CONTENT map keyed by area slug: summary, intro, best-for, transport, landmarks, starter picks.
- areaPage() rewritten to render rich, opinionated neighborhood guides above the venue grid.
- 6 areas covered: Jomtien, Naklua, Pratamnak, Central Pattaya, East Pattaya, Sattahip.
- Sattahip page went from 104 words to ~2100 words — same uplift across all 6 areas.
- Channel-card h3/h4 markup mismatches from earlier rounds fixed across 5 variants.
- Asset version bumped 409 → 410.
Live operational signals on every venue page + cleaner social sharing + richer authorship signal for E-E-A-T.
- Live "Open now / Closed" pill on every venue page driven by data-hours-spec attribute and visitor local time.
- Recently-verified feed surfaces the freshest hand-checks at the top of the site.
- Web Share API button on every venue page with clipboard fallback for unsupported browsers.
- Person schema for Tim Paemi on /about/ — explicit author entity for E-E-A-T.
- Asset version bumped 408 → 409.
Sources, editor-pick badges, recently-viewed memory + machine-readable site state + operational tooling for the long tail.
- Per-venue Sources block populated from each venue's front-matter for full citation transparency.
- "Spot an error?" mailto link on every venue — explicit invitation to crowdsource corrections.
- Editor's Pick trust pill surfaces hand-selected favorites in the trust bar.
- Recently-viewed (localStorage, last 8) at the top of every venue page.
- /status.json — machine-readable site identity, build version, schema completeness, freshness, endpoints, policies.
- scripts/ping-sitemap.js — pings Google + Bing on every push.
- scripts/stale-venues.js — CSV report of venues whose verified date is > 30 days.
- Asset version bumped 407 → 408.
Per-venue verified-by-Tim badge, trust signals row, guide bylines, reading-time estimator, Pattaya local-time widget, print stylesheet, public changelog page (this one), larger footer version badge, /methodology/ cross-links from every ranking surface.
- Every venue page now shows "★ Verified by Tim · [date]" in the hero.
- Every venue + category + area page shows "Hand-checked · No paid placement · How we rank" trust pills.
- Guide pages display author byline + reading time + last-updated date.
- Footer shows live Pattaya time (ICT) — useful for tourists checking from abroad.
- Print stylesheet means venue pages print clean for offline reference.
- Public /changelog/ page (this one) — full transparency on what changes each round.
- Asset version bumped 406 → 407.
Autonomous editorial round — wrote 3 substantive new guides targeting high-intent commercial queries Codex flagged as missing.
- /guides/english-speaking-muay-thai-pattaya/ — 10 gyms with English-fluent kru, ~1900 words.
- /guides/muay-thai-camps-with-accommodation-pattaya/ — 8 stay-and-train camps with pricing tiers, ~1800 words.
- /guides/gym-day-pass-pattaya/ — 12 gyms accepting walk-in day passes, ~1700 words.
- Each guide auto-gets Article schema + FAQPage schema + 8-12 internal venue links.
Closed remaining Codex V3 P2/P3 polish items + wrote the off-page playbook.
- .gitignore cleaned (stripped UTF-16 garbage, added archive patterns).
- Duplicate sitemap_index.xml deleted.
- README.md rewritten to V2 reality (build-v2.js, PUSH workflow, full schema map).
- NEXT_STEPS.md — comprehensive off-page playbook (GSC, Bing, GBP, backlinks, content).
Codex called geo "the single biggest local-SEO unlock." Built the Nominatim pipeline.
- scripts/geocode-venues.js — one-time Nominatim geocoder for all 158 venues.
- build-v2.js reads data/venue-geo.json and injects geo into LocalBusiness JSON-LD.
- Sitemap now emits priority + changefreq per URL pattern.
Closed three biggest remaining Codex V3 findings in one pass.
- Mobile CLS fixes — reserved space on .marquee, .btn-row, .result-card, .pa-network, .nav-row.
- Tool pages (/map/, /compare/, /plan-my-trip/, /find-my-coach/, /favorites/) converted to honest V2 static stubs with 3 working-alternative cards each.
- Article + FAQPage JSON-LD auto-injected on 17 guide pages.
The biggest long-tail SEO unlock per Codex V3 Section L.
- ~80 combined category-area pages now generated at /area/<area>/<category>/ targeting "muay thai in jomtien pattaya" etc.
- parsePostalAddress() now infers Thai postal codes from area context (Bang Lamung=20150, Sattahip=20250).
- <h4 class="channel-card-name"> → <h3> globally (fixes 163-page heading hierarchy issue).
Closed all four Round 4 Codex P0s.
- Removed `immutable` from _headers CSS/JS cache rules (now max-age=3600, must-revalidate).
- Asset version bumped 405 → 406 to force Cloudflare cache invalidation.
- Added og:site_name, twitter:site, robots meta, x-dns-prefetch-control, dns-prefetch hints to head().
- Back-to-top button text color #fff → #000 on pink (contrast 3.53:1 → 5.94:1, WCAG AA pass).
Repaired three critical production issues + added deploy guard.
- Homepage truncation repaired (was 307 lines mid-attribute, now 603 with proper closing tags).
- styles.css unclosed .back-to-top block fixed; restored .skip-link, .back-to-top.is-visible, prefers-reduced-motion rules.
- CSP rewrote with 3 active sha256 hashes + Cloudflare Insights origin allowed.
- NEW: scripts/verify-deploy.js — pre-push integrity check (truncation, NULs, brace balance, CSP coverage).
Sanitized tel: links, sitemap completeness, multi-session hours parsing, footer/heading fixes.
- phoneToTel() helper sanitizes display phones into E.164-clean dial values.
- Sitemap +26 URLs (17 guides, 6 tool pages, 4 utility pages).
- parseHoursSpec() handles multi-session via & continuation (Fairtex now emits both 07:30-10:30 AND 15:30-18:30).
- rel="noopener noreferrer" sitewide; footer h4 → div.footer-col-h.
- 5 broken YAML venue MDs fixed; --hint #555 → #888 (WCAG AA pass).
Codex audit Round 2 fix order — biggest schema completion pass.
- 158/158 venue pages get BreadcrumbList + LocalBusiness with PostalAddress + parsed openingHoursSpecification.
- 15/15 category + 6/6 area pages get BreadcrumbList alongside ItemList.
- Homepage gets WebSite + Organization + SearchAction JSON-LD graph.
- 24 legacy pages (guides + tools) migrated to V2 chrome.
- HOTFIX: linked 141 orphan venue body markdowns via detailFile in data.js.
Complete visual rebuild + content recovery.
- TimPaemi-inspired V2 design: black bg, multi-color neon accents, infinite seamless marquees, pattaya<dot>gym brand mark.
- Restored truncated index.html, stripped NUL bytes from 7 corrupted files, repointed package.json at build-v2.js.
- 158/158 venues now render with full body content (median 1,165 words).
- redesign-2026 branch merged to main; pattaya-gym.com live on V2.