Commit Graph

16 Commits

Author SHA1 Message Date
popovskik 14d7e7e280 Merge pull request 'seo: enable canonical-host 301 redirect to msosorg.com' (#19) from seo/canonical-host-golive into develop
Deploy to Production on Develop Push / deploy (push) Successful in 24s Details
Reviewed-on: #19
2026-08-26 00:00:23 +00:00
popovskik b0ac0831b3 seo(htaccess): enable canonical-host 301 to msosorg.com (go-live)
Now that msosorg.com serves the docroot, 301 every non-canonical host
(the temp domain msos.spletnimojster.si, any www., the origin host) to
https://msosorg.com to consolidate ranking signals. Runs before the
HTTPS-force so the temp domain reaches the canonical in a single hop; the
condition excludes msosorg.com itself so there is no redirect loop.

Mark the redirect steps done in the launch checklist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-26 01:43:46 +02:00
popovskik 2d535db7d5 feat(cms): U4 one-command site build + local preview
Add tools/build_site.py: runs the full pipeline in dependency order
(build_content -> build_listings -> seo_inject --all --sitemap ->
build_botlinks -> build_search_index), each as a subprocess so a failing
step aborts the build before anything downstream runs (no partial
deploy). --check verifies non-destructively; --images also runs
optimize_images. This is what CI will run in U5.

Fix a build_content idempotence bug found via U4: the hero and
article-header regions were emitted with leading indentation while the
regex left the template's pre-tag whitespace in place, so indentation
compounded (+16 spaces) on every rebuild. First line is now unindented;
the full build is verified idempotent (identical git tree across two
runs). Added a fixpoint test (re-rendering a page's own output is
byte-identical) so this can't regress.

docs/local-preview.md: build + serve on :8000 in all three languages.
tools/README.md: document build_site + build_content.

26 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 17:04:00 +02:00
popovskik 8ff968739a feat(cms): U3 derive article dates from content/ front-matter
Replace the hand-maintained ARTICLE_DATES registry as the source of
truth with dates read from content/ front-matter, so publishing a post
via the CMS needs no manual registry edit (plan R4).

- tools/content_index.py: load_dates() reads content/ front-matter into
  {source_folder/slug: {published|modified: iso}}. Standalone (os+yaml),
  no import cycle with seo_inject.
- tools/seo_inject.py: ARTICLE_DATES is now {**static_fallback,
  **content_dates}; content wins. The static table is kept only as a
  fallback for entries not present in content/.
- migration now preserves the published-vs-modified distinction
  (date_type), so the switch is byte-identical: content-derived dates
  equal the old static table exactly (17/17, incl. the one 'modified').

Verified: full `seo_inject --all --sitemap` + build_listings +
build_botlinks run produces ZERO changes to any site page. build_content
--check still reproduces all 51 pages.

build_botlinks.py needs no change (it scrapes built pages, which are a
pure function of content/). Listings get content-driven dates for free
via seo_inject.ARTICLE_DATES.

tools/tests/test_content_index.py: 5 tests (dates == legacy registry,
merged registry unchanged, published/modified distinction, events key on
projects/<slug>, one ISO date per entry).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 16:47:03 +02:00
popovskik 596290acf8 feat(cms): U2 render content/ into detail pages (characterization-verified)
Add tools/build_content.py: renders every content/ entry into its blog /
news / event detail page by self-templating the page's own chrome and
replacing only content-derived regions (title, article header, hero,
body, and — for new pages only — the scrollspy).

Faithful reproduction, proven against the live site:
- body_format: html fragments injected verbatim, so guide-boxes, inline
  figures and event galleries render unchanged.
- scrollspy sidebars are preserved (labels are hand-curated, shorter than
  the H2s); only generated when a page has none.
- <title> suffix derived per page, so EN/SI " - MSOS", MK " - МСОС", and
  suffix-less event titles all reproduce exactly.
- hero /images/… rewritten to the page's ../../../ depth.
- ASCII-only console output (no cp1252 crash on Windows/CI).

tools/tests/test_build_content.py: 8 characterization tests; the core one
renders all 51 pages and asserts each is semantically equivalent
(whitespace/attr-order-insensitive) to the current page — the R5
no-regression guarantee.

Does NOT overwrite the committed pages; whether generated pages are
committed vs built fresh in CI is decided in U4/U5. Run:
  python tools/build_content.py --check   # verify equivalence
  python tools/build_content.py --write    # emit pages (for U4)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 16:36:11 +02:00
popovskik 9ddd0f88ef feat(cms): U1 content model + migration scaffold for blog/news/events
Establish content/ as the source of truth for editable content and
back-fill it from the existing detail pages, per U1 of the no-code CMS
plan.

- docs/content-model.md: front-matter schema (EN/MK/SI), body_format
  html-vs-markdown, content/events -> projects/ output mapping.
- tools/migrate_pages_to_content.py: bs4/yaml extractor; pulls title,
  subtitle, category, ISO date (from ARTICLE_DATES), hero image+caption;
  preserves the article body PLUS trailing sibling sections (event
  galleries) verbatim as body_format: html. Reports unparseable pages
  instead of emitting half-formed files.
- content/**: 51 files (17 entries x EN/MK/SI), 0 skips.
- tools/tests/test_migrate_roundtrip.py: 12 characterization tests
  (structured fields, news-has-no-subtitle, event gallery/lightbox
  preserved, Cyrillic UTF-8 round-trip, every page parses).
- .gitignore: ignore __pycache__/ and *.pyc.

U1 is unblocked and standalone. U2 (render content -> pages) is next;
U5/U6 remain blocked on Open Question Q1 (Gitea<->GitHub topology).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 16:18:29 +02:00
popovskik 6204db5f0c docs: add no-code CMS implementation plan 2026-08-08 16:09:31 +02:00
popovskik 0d49293693 GEO: FAQPage/NGO/Event schema + autonomous generation for new projects
Generative Engine Optimization pass so AI answer engines (ChatGPT, Gemini,
Perplexity, AI Overviews) can confidently identify and cite MSOS.

Schema (tools/seo_inject.py, propagated to all 231 pages):
- FAQPage JSON-LD parsed from the /faq/ Q&A blocks (33 Q&A x EN/MK/SI)
- Organization upgraded to NGO with entity-disambiguation fields:
  alternateName (all spellings), description, areaServed Slovenia,
  knowsLanguage, and foundingDate 2023-03-05
- Event JSON-LD on all 9 /projects/ recap pages (date + real location)

Autonomous for future content (no code edits to add a project):
- Event/Article dates auto-read from the English page's publish date;
  event city auto-detected from the body (Slovenian-city whitelist).
  ARTICLE_DATES/PROJECT_LOCATIONS are now optional overrides only.
  (Auto-detect corrected Paint & Wine to Nova Gorica.)
- build_listings.py: auto-date fallback so undated new content still lists
- pre-commit hook now runs seo_inject.py --all --sitemap first, then
  listings/botlinks, and re-stages changed pages (idempotent)

Docs: docs/geo-offsite-worklist.md - copy-paste-ready off-site plan
(Wikidata, Wikipedia fixes, directories, Google Business, AI-visibility tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-07 22:17:03 +02:00
popovskik 53ae93a813 docs: note the SEO regeneration step for new pages
Record in PRE-LAUNCH-TODO.md that new pages still need ARTICLE_DATES + a
seo_inject.py --all --sitemap run (the commit hook covers News/homepage/bot
sync but not SEO), so it isn't forgotten.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 18:39:04 +02:00
popovskik 30bc83e064 content(MK): apply proofreading corrections from owner + rename header menu
Applies the corrections from "msos web corrections.docx" across 6 Macedonian
pages, plus two global fixes, plus matching English tweaks.

Per-page (MK):
- Landing: hero eyebrow/subtitle, story section, testimonials, FAQ block
- About us / Our team: "Раководен тим" -> "Управен одбор", team names to
  Cyrillic, Marko's title, removed duplicate top gradient + added spacing,
  fixed the broken "Still have questions" footer to match the landing page
- MSOS Hub: title widow (nbsp), hub descriptions
- FAQ: intro + 9 answer corrections (also fixed leftover English fragments)
- Become a Member: card label, removed stats note, consistent "ти" voice,
  Cyrillic names, form step + success text
- Support: title, subtitle, community section, support options

Global (all MK pages): header menu "Нашиот тим, мисија, визија и вредности"
-> "Нашиот тим"; напатница -> напотница. Visible punctuation dashes changed
to commas per owner preference.

English: testimonials section (en/index.html) and two document answers
(en/faq) updated to match.

Adds docs/MK-PROOFREADING.md (6 proofread, 63 remaining of 69 MK pages).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-04 13:13:04 +02:00
popovskik 4721b3cc75 docs: master pre-launch to-do list (paused before go-live)
Consolidates outstanding items so nothing is lost during the pause: domain
decision, content placeholders, MK/SI proofread, GA4 setup, plus a record of
what's already hardened. Dev-only doc (docs/ excluded from deploy).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-04 10:56:58 +02:00
popovskik 55bc101410 feat(assistant): one-time disclaimer note + GA4 question-report guide
- Add a subtle disclaimer under the greeting (EN/MK/SI): "automated guide,
  can make mistakes — check the linked page for official steps." Shown once,
  not on every message, so it sets expectations without clutter. Persistent
  footer disclaimer stays.
- docs/GA4-ASSISTANT-REPORT.md: step-by-step for registering the custom
  dimensions and building the "top questions" + "unanswered questions"
  explorations in GA4 (dev-only doc, not deployed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-04 01:57:55 +02:00
popovskik c4da5de491 a11y: make mobile menu toggle keyboard & screen-reader accessible
The mobile hamburger is a <div class="mobile-menu-icon"> with a click
handler, so it worked on tap but was invisible to keyboard and screen-reader
users. Expose it as a button in main.js: role="button", tabindex="0",
aria-label, aria-expanded toggling, and Enter/Space activation. Verified in
a headless browser (focusable, Enter opens, aria state flips).

Also adds docs/PRE-LAUNCH-CHECKLIST.md (dev-only, excluded from deploy):
inventory of remaining content placeholders for the public launch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-04 00:57:27 +02:00
popovskik 788084e094 Add article dates, brand OG image, launch SEO checklist
- Add datePublished/dateModified to Article JSON-LD on the 11 dated
  articles (curated once from EN, applied to all languages)
- Replace og-default.jpg with the official brand OG image (1200x630)
- Add docs/launch-seo-checklist.md (go-live steps: redirects, Search
  Console, analytics, rich-result validation, monitoring)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-02 12:38:20 +02:00
popovskik 9e8cbc5876 Homepage refresh, activities redesign + newsletter rollout
Navigation
- Replace "Donate" with a "Support MSOS ❤️" CTA site-wide
- Remove dead nav links (Gallery, Press releases)
- Turn the one-item News dropdown into a direct link

Homepage
- Replace the non-functional "Become part" contact form with a
  "Find your people in Slovenia" join CTA (blue/white gradient band,
  orange button, photo collage), in all three languages
- Fix the placeholder Activities subtitle
- Redesign Activities into card-style tabs with icons; move per-tab
  content into HTML panels and switch by index so the tabs work in
  every language (MK/SI tabs were previously dead)

Newsletter (MailerLite)
- Add the subscribe block to the News hub, news articles, and every
  events/projects page and hub, in all three languages
- Resize/rebalance the block, shrink the privacy line, add top spacing
- Standardise on honest copy (drop the "2,000+ subscribers" claim)
- Load main.js on the News hub pages (they shipped without it, so the
  form and header dropdowns didn't work there)
- Fix a dead privacy-policy link on the makedonska-vecer page
- Document the signup convention in docs/newsletter-signup.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 19:51:59 +02:00
popovskik 69a9011b0e Add five 2025 event blogs (EN/SI/MK), remove placeholders
Add the 2025 events batch to the Events & Projects section, each as a
full article page in all three languages following the established
template (auto-generated TOC, sticky mobile header, inline figures):

- Humanitarian sports tournament in Maribor (05 Apr 2025)
- Public gathering for the Kocani victims (21 Mar 2025)
- Paint & Wine at Sunset in Gorizia (08 Mar 2025, with aftermovie embed)
- Public gathering for Frosina Kulakova (08 Feb 2025)
- Watching handball together in Slovenia (25 Jan 2025)

Also:
- Add 33 event images to images/ (oversized JPGs downscaled to <=1600px).
- Add localized cards for the five events to the EN/SI/MK listing pages.
- Remove the two placeholder cards (Bill Walsh, PM mental models) and
  delete the testproject article folders they linked to.
- Convert punctuation dashes in the new copy to commas or sentence
  breaks for a more human tone; keep compound-word hyphens.
- Add docs handover brief for the future Get to know Slovenia and
  North Macedonia sections.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-28 15:44:23 +02:00