-
- `#006cb8`) to achieve ≥ 4.5:1 contrast against white backgrounds.
+ - Add text underlines to links within body copy (`p a`, `li a`) to comply with WCAG 1.4.1 (not relying on color alone).
+ - Update footer link hover state from `#ffffff` (low contrast on `#00d1ff`) to high-contrast `#231f20` with underline.
+- [x] **Focus State Enhancement**:
+ - Replace low-contrast focus outlines with high-contrast 2px solid rings (`#231f20` / `#005fa3`) with 2px offset for keyboard navigation.
+ - Ensure the skip-to-content link (`.wp-block-skip-link`) is prominently visible upon initial tab keypress.
+- [x] **Touch Target Sizing**:
+ - Ensure all interactive elements (social icons, navigation items, buttons) have a minimum hit area of 44×44px.
+- [x] **Screen Reader & Semantic Landmarks**:
+ - Audit and standardize heading levels (`
` -> `
` -> `
`) across all block patterns (`hero-punch`, `feature-cards`, `latest-posts`).
+ - Add clear `aria-label` tags to icon-only buttons (search toggle, mobile menu button, social links).
+
+---
+
+### 📌 Milestone 2: SEO & Core Web Vitals Optimization (v3.2.0) — ✅ COMPLETED
+*Target: Sub-second First Contentful Paint (FCP), zero Cumulative Layout Shift (CLS), and crawlability.*
+
+- [x] **Font Preloading & Performance**:
+ - Inject `` in `wp_head` for primary fonts (`HKGrotesk-Regular.woff2` and `HKGrotesk-Bold.woff2`) to prevent layout shift and FOUT.
+- [x] **Structured Data (Schema.org)**:
+ - Add JSON-LD structured data support for `Organization`, `WebSite`, and `Article` schema.
+ - Integrate breadcrumbs support for deep page hierarchies.
+- [x] **Image Optimization & CLS Prevention**:
+ - Ensure explicit `width` and `height` attributes are declared on all SVGs and fallback theme images.
+- [x] **Search Engine Indexing**:
+ - Test compatibility with WordPress core XML Sitemaps (`/wp-sitemap.xml`) and popular SEO plugins (Yoast, Rank Math, SEOPress).
+ - Add Open Graph and Twitter Card fallback meta tags for social media sharing.
+
+---
+
+### 📌 Milestone 3: Multilingual Architecture (ES / EN & SV / EN) (v3.3.0) — ✅ COMPLETED
+*Target: Network chapter branch default with Spanish (`es_ES`) and English (`en_US`) fallback (with Swedish `sv_SE` on the Sweden build), automatic browser language negotiation, and dynamic hreflang tags.*
+
+- [x] **URL & Routing Structure**:
+ - Primary language at root (`https://chapter.okfn.org/`, `lang="es"` or `lang="sv"`).
+ - English section at `/en/` (`https://chapter.okfn.org/en/`, `lang="en"`).
+- [x] **Browser Language Negotiation**:
+ - Implemented bot-safe HTTP `Accept-Language` detection helper (`okfn_chapter_get_browser_language()`).
+ - Visitor language preferences stored via cookie (`okfn_lang`).
+ - Search engine spiders (Googlebot, Bingbot, etc.) detected and never redirected (`okfn_chapter_is_bot()`).
+- [x] **SEO `hreflang` Support**:
+ - Output bidirectional `hreflang` alternate links in `wp_head`:
+ ```html
+
+
+
+ ```
+- [x] **Header Language Switcher**:
+ - Created dedicated accessible Header Language Switcher pattern (`patterns/language-switcher.php`) integrated into `parts/header.html` with pill styling.
+ - Full compatibility with Polylang / Polylang Pro FSE blocks.
+- [x] **Complete Translation Bundles**:
+ - Created Spanish translations `languages/okfn-chapter-es_ES.po` and high-performance `languages/okfn-chapter-es_ES.l10n.php`.
+ - Maintained Swedish translations (`languages/okfn-chapter-sv_SE.po` and `languages/okfn-chapter-sv_SE.l10n.php`) and template `languages/okfn-chapter.pot`.
+
+---
+
+### 📌 Milestone 4: Block Patterns & Customization (v3.4.0) — ✅ COMPLETED
+*Target: Expand pattern library for chapters with zero page-builder dependencies.*
+
+- [x] **New Block Patterns**:
+ - Project showcase & Case study cards (`patterns/project-cards.php`).
+ - Event / Meetup listing pattern (`patterns/events.php`).
+ - FAQ / Accordion pattern with accessible HTML `` and `` (`patterns/faq-accordion.php`).
+ - Donation & Membership CTA block with multiple payment options (`patterns/donate-cta.php`).
+- [x] **Style Variations**:
+ - Added `styles/chapter-purple.json`, `styles/chapter-yellow.json`, and `styles/dark-mode.json`.
+- [x] **Site Editor Documentation**:
+ - Created comprehensive volunteer and editor guide in [`docs/editor-guide.md`](docs/editor-guide.md).
+
+---
+
+### 📌 Milestone 5: Quality Assurance, Automated Testing & CI/CD (v3.5.0)
+*Target: Automated validation on every commit and release packaging.*
+
+- [ ] **Automated Testing Suite**:
+ - GitHub Actions workflow for PHP syntax check (`php -l`) and PHP_CodeSniffer with WordPress-Core / WordPress-VIP rules.
+ - Automated `theme.json` validation against official WordPress schema (`schemas.wp.org/wp/7.0/theme.json`).
+ - Automated accessibility auditing with Pa11y / axe-core in CI.
+- [ ] **Release Packaging**:
+ - GitHub Actions workflow to build clean production zip archives (`okfn-chapter.zip`) excluding dev dependencies on release tag.
+
+---
+
+## 📊 Summary of Review & QA Findings
+
+| Category | Finding | Recommended Fix | Severity |
+|---|---|---|---|
+| **A11y** | Link color `#00a9e0` on `#ffffff` is 2.88:1 (fails WCAG AA 4.5:1). | Darken to `#006cb8` (4.64:1) + underline body links. | 🔴 High |
+| **A11y** | Focus outline `#00d1ff` has 1.45:1 contrast against white background. | Use `#005fa3` with 2px offset. | 🔴 High |
+| **Performance** | Webfonts (`HK Grotesk`) are self-hosted but not preloaded. | Add `` in `wp_head`. | 🟡 Medium |
+| **SEO / i18n** | Lack of bidirectional `hreflang` tags and browser language switcher. | Implement `hreflang` injector and header language toggle. | 🔴 High |
+| **UX** | Footer social links have small touch area on mobile. | Increase hit area to 44×44px. | 🟢 Low |
diff --git a/archive.php b/archive.php
deleted file mode 100644
index d38b210..0000000
--- a/archive.php
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
+
+
diff --git a/assets/icons/arrow-curved.svg b/assets/icons/arrow-curved.svg
new file mode 100644
index 0000000..a227f81
--- /dev/null
+++ b/assets/icons/arrow-curved.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/arrow-right.svg b/assets/icons/arrow-right.svg
new file mode 100644
index 0000000..b2f3da1
--- /dev/null
+++ b/assets/icons/arrow-right.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/arrow-solid-down.svg b/assets/icons/arrow-solid-down.svg
new file mode 100644
index 0000000..5266d06
--- /dev/null
+++ b/assets/icons/arrow-solid-down.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/close.svg b/assets/icons/close.svg
new file mode 100644
index 0000000..c6d363b
--- /dev/null
+++ b/assets/icons/close.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/country.svg b/assets/icons/country.svg
new file mode 100644
index 0000000..1735fba
--- /dev/null
+++ b/assets/icons/country.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/assets/icons/envelope.svg b/assets/icons/envelope.svg
new file mode 100644
index 0000000..6035d8b
--- /dev/null
+++ b/assets/icons/envelope.svg
@@ -0,0 +1,8 @@
+
\ No newline at end of file
diff --git a/assets/icons/facebook.svg b/assets/icons/facebook.svg
new file mode 100644
index 0000000..d9fc604
--- /dev/null
+++ b/assets/icons/facebook.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/assets/icons/footer-attribution.svg b/assets/icons/footer-attribution.svg
new file mode 100644
index 0000000..cc9a680
--- /dev/null
+++ b/assets/icons/footer-attribution.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/footer-cc.svg b/assets/icons/footer-cc.svg
new file mode 100644
index 0000000..2d6ec1f
--- /dev/null
+++ b/assets/icons/footer-cc.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/header-globe.svg b/assets/icons/header-globe.svg
new file mode 100644
index 0000000..400e360
--- /dev/null
+++ b/assets/icons/header-globe.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/header-search.svg b/assets/icons/header-search.svg
new file mode 100644
index 0000000..2f6867f
--- /dev/null
+++ b/assets/icons/header-search.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/link.svg b/assets/icons/link.svg
new file mode 100644
index 0000000..ffdf8b5
--- /dev/null
+++ b/assets/icons/link.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/assets/icons/menu.svg b/assets/icons/menu.svg
new file mode 100644
index 0000000..0e6627b
--- /dev/null
+++ b/assets/icons/menu.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/social/bluesky.svg b/assets/icons/social/bluesky.svg
new file mode 100644
index 0000000..7db4dc8
--- /dev/null
+++ b/assets/icons/social/bluesky.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/social/flickr.svg b/assets/icons/social/flickr.svg
new file mode 100644
index 0000000..cc84127
--- /dev/null
+++ b/assets/icons/social/flickr.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/social/github.svg b/assets/icons/social/github.svg
new file mode 100644
index 0000000..160cae4
--- /dev/null
+++ b/assets/icons/social/github.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/social/linkedin.svg b/assets/icons/social/linkedin.svg
new file mode 100644
index 0000000..a2c10ed
--- /dev/null
+++ b/assets/icons/social/linkedin.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/social/mailinglist.svg b/assets/icons/social/mailinglist.svg
new file mode 100644
index 0000000..dd34ac6
--- /dev/null
+++ b/assets/icons/social/mailinglist.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/social/mastodon.svg b/assets/icons/social/mastodon.svg
new file mode 100644
index 0000000..dea389e
--- /dev/null
+++ b/assets/icons/social/mastodon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/social/x.svg b/assets/icons/social/x.svg
new file mode 100644
index 0000000..0061fa4
--- /dev/null
+++ b/assets/icons/social/x.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/social/youtube.svg b/assets/icons/social/youtube.svg
new file mode 100644
index 0000000..3f48365
--- /dev/null
+++ b/assets/icons/social/youtube.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/twitter.svg b/assets/icons/twitter.svg
new file mode 100644
index 0000000..41170eb
--- /dev/null
+++ b/assets/icons/twitter.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/assets/img/by.svg b/assets/images/by.svg
similarity index 100%
rename from assets/img/by.svg
rename to assets/images/by.svg
diff --git a/assets/images/call-to-action-newsletter.svg b/assets/images/call-to-action-newsletter.svg
new file mode 100644
index 0000000..1679904
--- /dev/null
+++ b/assets/images/call-to-action-newsletter.svg
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/assets/img/cc.svg b/assets/images/cc.svg
similarity index 100%
rename from assets/img/cc.svg
rename to assets/images/cc.svg
diff --git a/assets/img/favicon.ico b/assets/images/favicon.ico
similarity index 100%
rename from assets/img/favicon.ico
rename to assets/images/favicon.ico
diff --git a/assets/images/lg-okfn-black.svg b/assets/images/lg-okfn-black.svg
new file mode 100644
index 0000000..6282805
--- /dev/null
+++ b/assets/images/lg-okfn-black.svg
@@ -0,0 +1,195 @@
+
+
+
diff --git a/inc/logo.svg b/assets/images/lg-okfn.svg
similarity index 100%
rename from inc/logo.svg
rename to assets/images/lg-okfn.svg
diff --git a/assets/images/section-newsletter.svg b/assets/images/section-newsletter.svg
new file mode 100644
index 0000000..358d524
--- /dev/null
+++ b/assets/images/section-newsletter.svg
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/assets/img/cc-by-sa.png b/assets/img/cc-by-sa.png
deleted file mode 100755
index 9d6600a..0000000
Binary files a/assets/img/cc-by-sa.png and /dev/null differ
diff --git a/assets/js/bootstrap.min.js b/assets/js/bootstrap.min.js
deleted file mode 100755
index e5a2429..0000000
--- a/assets/js/bootstrap.min.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*!
- * Bootstrap v4.4.1 (https://getbootstrap.com/)
- * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */
-!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t=t||self).bootstrap={},t.jQuery,t.Popper)}(this,function(t,g,u){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)g(this._element).one(Y.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Se,popperConfig:null},Fe="show",Ue="out",We={HIDE:"hide"+Oe,HIDDEN:"hidden"+Oe,SHOW:"show"+Oe,SHOWN:"shown"+Oe,INSERTED:"inserted"+Oe,CLICK:"click"+Oe,FOCUSIN:"focusin"+Oe,FOCUSOUT:"focusout"+Oe,MOUSEENTER:"mouseenter"+Oe,MOUSELEAVE:"mouseleave"+Oe},qe="fade",Me="show",Ke=".tooltip-inner",Qe=".arrow",Be="hover",Ve="focus",Ye="click",ze="manual",Xe=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Me))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&g(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(qe);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,this._getPopperConfig(a)),g(o).addClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===Ue&&e._leave(null,e)};if(g(this.tip).hasClass(qe)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){function e(){n._hoverState!==Fe&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),g(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),t&&t()}var n=this,i=this.getTipElement(),o=g.Event(this.constructor.Event.HIDE);if(g(this.element).trigger(o),!o.isDefaultPrevented()){if(g(i).removeClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ye]=!1,this._activeTrigger[Ve]=!1,this._activeTrigger[Be]=!1,g(this.tip).hasClass(qe)){var r=_.getTransitionDurationFromElement(i);g(i).one(_.TRANSITION_END,e).emulateTransitionEnd(r)}else e();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Pe+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(Ke)),this.getTitle()),g(t).removeClass(qe+" "+Me)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=we(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text())},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t=t||("function"==typeof this.config.title?this.config.title.call(this.element):this.config.title)},t._getPopperConfig=function(t){var e=this;return l({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Qe},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},{},this.config.popperConfig)},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,{},e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return Re[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==ze){var e=t===Be?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Be?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),this._hideModalHandler=function(){i.element&&i.hide()},g(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");!this.element.getAttribute("title")&&"string"==t||(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Ve:Be]=!0),g(e.getTipElement()).hasClass(Me)||e._hoverState===Fe?e._hoverState=Fe:(clearTimeout(e._timeout),e._hoverState=Fe,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===Fe&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Ve:Be]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=Ue,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===Ue&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){var e=g(this.element).data();return Object.keys(e).forEach(function(t){-1!==je.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,{},e,{},"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),_.typeCheckConfig(Ae,t,this.constructor.DefaultType),t.sanitize&&(t.template=we(t.template,t.whiteList,t.sanitizeFn)),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Le);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(qe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(Ne),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ne,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.4.1"}},{key:"Default",get:function(){return xe}},{key:"NAME",get:function(){return Ae}},{key:"DATA_KEY",get:function(){return Ne}},{key:"Event",get:function(){return We}},{key:"EVENT_KEY",get:function(){return Oe}},{key:"DefaultType",get:function(){return He}}]),i}();g.fn[Ae]=Xe._jQueryInterface,g.fn[Ae].Constructor=Xe,g.fn[Ae].noConflict=function(){return g.fn[Ae]=ke,Xe._jQueryInterface};var $e="popover",Ge="bs.popover",Je="."+Ge,Ze=g.fn[$e],tn="bs-popover",en=new RegExp("(^|\\s)"+tn+"\\S+","g"),nn=l({},Xe.Default,{placement:"right",trigger:"click",content:"",template:'
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/
+```
+
+## Install (recommended)
+
+1. Create an account at [cloud.umami.is](https://cloud.umami.is) and pick **Hobby**.
+2. **Add a website** for the real domain (e.g. `okfn.se`). Copy the **Website ID**. Optionally add a second site for staging (Hobby allows three).
+3. In WordPress: Plugins → Add New → **[Integrate Umami](https://wordpress.org/plugins/integrate-umami/)** (GPL, tested on WordPress 7.x).
+4. Settings:
+ - Script URL: `https://cloud.umami.is/script.js`
+ - Website ID: the UUID from step 2
+ - Enable tracking
+ - **Ignore admin users**
+5. Open the public site **logged out** and confirm a pageview in the Umami dashboard.
+
+Do not put the UUID in the theme repository. Production and staging IDs differ. Do not enable tracking on wp-env (`localhost:8888`); that burns the Hobby event quota.
+
+## Privacy
+
+Umami Cloud is cookieless. Note it on the privacy policy page (`/dataskyddspolicy/` on okfn.se) if that document still claims only unspecified “analytics”.
+
+## Optional: no plugin
+
+If you refuse extra plugins, define this in `wp-config.php` on the server only (not in git):
+
+```php
+define( 'OKFN_UMAMI_WEBSITE_ID', 'your-uuid-here' );
+```
+
+Then a small theme loader can enqueue the Cloud script. That helper is not shipped yet; use Integrate Umami unless you ask for it.
diff --git a/footer.php b/footer.php
deleted file mode 100644
index 2e2c21b..0000000
--- a/footer.php
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
-
-
-
-
-
-