diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..32b16e7
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,52 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project overview
+
+Static PHP site for the Index NGO donation/support campaign (`soutenir.index.ngo`). No build step — PHP files are served directly. The site has two language versions: French (`index.php`) and English (`en/index.php`).
+
+## Architecture
+
+### Content flow
+
+All editorial content is managed via a **Kirby CMS** running in a separate repo (`/Users/adrienpayet/Documents/code/en-cours/index/main/`). The static site fetches content from the Kirby API and caches it locally:
+
+```
+Kirby CMS (main repo) → /support-content?lang=fr → includes/cache.php → index.php
+```
+
+- **`includes/config.php`** — API URL, token, cache TTL constants
+- **`includes/cache.php`** — `getContent(lang)` function: serves cache if fresh (<5 min), otherwise fetches from Kirby API via curl, falls back to stale cache
+- **`cache/`** — stores `content.fr.json` and `content.en.json`
+
+### Kirby backend (separate repo)
+
+- **Blueprint**: `/Users/adrienpayet/Documents/code/en-cours/index/main/site/blueprints/pages/support.yml` — defines all editable fields for the support page
+- **API route**: `/Users/adrienpayet/Documents/code/en-cours/index/main/site/config/routes/support-content.php` — builds and returns the JSON response consumed by this site; uses `->inline()` on writer fields
+
+When adding a new dynamic field: add it to the blueprint, expose it in the API route, then consume `$data['fieldName']` in the PHP templates.
+
+### Donation gauge
+
+- **`api/donorbox-proxy.php`** — server-side proxy to Donorbox API (Basic Auth). Paginates through all active plans to get `recurring_donors_count`. Caches result in `api/cache/donorbox_data.json` (5 min).
+- **`assets/js/donorbox-gauge.js`** — calls the proxy, adds `RECURRING_DONORS_OFFSET` to the real count, updates the gauge CSS variable `--pourcent` and the displayed number. Also sets up 5-minute auto-refresh.
+
+`RECURRING_DONORS_OFFSET` is defined at the top of `donorbox-gauge.js` and represents historic/offline supporters added to the live Donorbox count.
+
+### Donation pad
+
+**`assets/js/donation.js`** — IIFE that:
+- Dynamically generates all donation buttons (amounts, after-tax calculations) on `DOMContentLoaded`
+- Contains `TRANSLATIONS` object for FR/EN static strings in the donation pad
+- Generates Donorbox redirect URLs with UTM passthrough
+- Handles monthly/one-off tab switching
+
+Static strings in the donation pad (e.g. "Avec déduction fiscale de 66 %") live in the `TRANSLATIONS` object in this file, not in Kirby.
+
+## Key conventions
+
+- **Writer fields in Kirby**: use `->inline()` in the API route to get inline HTML without block wrappers. For multiline text (e.g. a title with a `
`), use a `writer` field with `nodes: false` and `buttons: false` — this preserves line breaks. In the API route, use `->inline()` which converts newlines to `
` tags; do **not** use `nl2br()`.
+- **`heroHeading` field**: rendered with `= $data['heroHeading'] ?>` (no `htmlspecialchars`) because it contains trusted HTML from the writer field.
+- **Language**: `document.documentElement.lang` drives JS translations. FR page sets `lang="fr"`, EN page sets `lang="en"`.
+- **Cache invalidation**: delete files in `cache/` to force a fresh fetch from Kirby.
diff --git a/assets/js/donation.js b/assets/js/donation.js
index f5c335c..bee9cdb 100644
--- a/assets/js/donation.js
+++ b/assets/js/donation.js
@@ -14,11 +14,13 @@
afterTax: 'Soit {amount} € après impôts',
perMonth: '€/mois',
withTaxReduction: 'Avec 66 % de déduction fiscale',
+ chooseAmount: 'Choisissez votre montant',
},
en: {
afterTax: 'That is {amount} € after tax',
perMonth: '€/month',
withTaxReduction: 'With 66 % tax deduction',
+ chooseAmount: 'Choose your amount',
},
};
@@ -115,6 +117,10 @@
window.open(generateDonorboxUrl(amount, false), '_blank');
});
} else {
+ button.innerHTML = `
+
${translate('chooseAmount')}
+${translate('withTaxReduction')}
+ `; button.addEventListener('click', () => { window.open(generateDonorboxUrl(null, false), '_blank'); }); @@ -144,6 +150,10 @@ window.open(generateDonorboxUrl(amount, true), '_blank'); }); } else { + button.innerHTML = ` +${translate('chooseAmount')}
+${translate('withTaxReduction')}
+ `; button.addEventListener('click', () => { window.open(generateDonorboxUrl(null, true), '_blank'); }); diff --git a/assets/js/donorbox-gauge.js b/assets/js/donorbox-gauge.js index fde403a..5fe2373 100644 --- a/assets/js/donorbox-gauge.js +++ b/assets/js/donorbox-gauge.js @@ -2,7 +2,7 @@ const DONORBOX_CONFIG = { proxyUrl: '/api/donorbox-proxy.php', }; -const RECURRING_DONORS_OFFSET = 98; +const RECURRING_DONORS_OFFSET = 62; const GOAL_SUPPORTERS = 500; async function fetchDonorboxData() { diff --git a/en/index.php b/en/index.php index 6f24182..38fd841 100644 --- a/en/index.php +++ b/en/index.php @@ -70,8 +70,8 @@ $data = getContent('en');
- = htmlspecialchars($data['heroObjectiveLabel'] ?? '') ?>
-
= htmlspecialchars($data['heroObjectiveDate']) ?>
+ = htmlspecialchars($data['gaugeBlackLabel'] ?? '') ?>
+
= htmlspecialchars($data['gaugeGreenLabel']) ?>
- = htmlspecialchars($data['heroObjectiveLabel'] ?? '') ?>
-
= htmlspecialchars($data['heroObjectiveDate']) ?>
+ = htmlspecialchars($data['gaugeBlackLabel'] ?? '') ?>
+
= htmlspecialchars($data['gaugeGreenLabel']) ?>