@port60/template-kit 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +10 -3
  2. package/bin/cli.mjs +26 -4
  3. package/package.json +1 -1
  4. package/src/commands/content.mjs +42 -0
  5. package/src/commands/create.mjs +6 -6
  6. package/src/commands/dev.mjs +97 -9
  7. package/src/commands/model.mjs +34 -0
  8. package/src/commands/packageCmd.mjs +3 -3
  9. package/src/commands/refresh.mjs +48 -0
  10. package/src/commands/upgrade.mjs +44 -0
  11. package/src/commands/validate.mjs +2 -3
  12. package/src/lib/agentsMd.mjs +47 -19
  13. package/src/vendor/contract/v1/behaviours.json +4 -4
  14. package/src/vendor/contract/v1/content-bounds.json +3 -3
  15. package/src/vendor/contract/v1/content-model.json +193 -0
  16. package/src/vendor/contract/v1/context.json +1667 -275
  17. package/src/vendor/contract/v1/dialect.json +3 -3
  18. package/src/vendor/contract/v1/fonts.json +1 -1
  19. package/src/vendor/contract/v1/imagery.json +4 -4
  20. package/src/vendor/contract/v1/islands.json +256 -235
  21. package/src/vendor/contract/v1/layout.json +4 -4
  22. package/src/vendor/contract/v1/manifest.schema.json +9 -9
  23. package/src/vendor/contract/v1/sections.json +248 -92
  24. package/src/vendor/contract/v1/tokens.json +1 -1
  25. package/src/vendor/contract/v1.lock.json +1 -1
  26. package/src/vendor/engine/content-footprint.mjs +79 -0
  27. package/src/vendor/validator/fixture-art.mjs +109 -0
  28. package/src/vendor/validator/model-reference.mjs +92 -0
  29. package/src/vendor/validator/platform-base.css +21 -0
  30. package/src/vendor/validator/preview.mjs +149 -56
  31. package/src/vendor/validator/site-context.mjs +97 -0
  32. package/src/vendor/validator/validate.mjs +71 -14
  33. package/starter/assets/theme.css +10 -10
  34. package/starter/layout.liquid +15 -16
  35. package/starter/manifest.json +2 -2
  36. package/starter/sections/campaigns.liquid +5 -5
  37. package/starter/sections/homeHero.liquid +11 -11
  38. package/starter/sections/people.liquid +1 -1
  39. package/starter/sections/values.liquid +1 -1
@@ -1,12 +1,61 @@
1
1
  {
2
- "description": "The data context templates render with. Templates receive ONLY platform-provided data — never secrets, never other tenants' data. Additive-only within a contract major. Section renderers get {section, brand}; the layout gets {brand, nav, socials}; page templates (pages/{page}.liquid) get their page's documented objects + brand.",
2
+ "description": "The data context templates render with. Templates receive ONLY platform-provided data, never secrets, never other tenants' data. Additive-only within a contract major. Section renderers get {section, brand}; the layout gets {brand, nav, socials}; page templates (pages/{page}.liquid) get their page's documented objects + brand.",
3
3
  "variables": [
4
+ {
5
+ "path": "site",
6
+ "availableIn": [
7
+ "section",
8
+ "layout"
9
+ ],
10
+ "description": "The one content tree (content model v1, contract/v1/content-model.json): site.brand, site.nav, site.socials, site.locale and the typed collections under site.content.*. Available to every render scope; page templates receive it alongside their page objects."
11
+ },
12
+ {
13
+ "path": "site.brand",
14
+ "availableIn": [
15
+ "section",
16
+ "layout"
17
+ ],
18
+ "description": "The organisation's branding (same shape as the flat-era brand object)."
19
+ },
20
+ {
21
+ "path": "site.nav",
22
+ "availableIn": [
23
+ "section",
24
+ "layout"
25
+ ],
26
+ "description": "Navigation: items (curated main or platform-derived), footer, derived, the flat-era nav shape, re-addressed."
27
+ },
28
+ {
29
+ "path": "site.socials",
30
+ "availableIn": [
31
+ "section",
32
+ "layout"
33
+ ],
34
+ "description": "Social links, the flat-era socials shape."
35
+ },
36
+ {
37
+ "path": "site.locale",
38
+ "availableIn": [
39
+ "section",
40
+ "layout"
41
+ ],
42
+ "description": "Locale: code, direction, languages."
43
+ },
44
+ {
45
+ "path": "site.content",
46
+ "availableIn": [
47
+ "section",
48
+ "layout"
49
+ ],
50
+ "description": "The typed collections: services, events, articles, campaigns, causes, courses, volunteering, media, resources, locations, schedules, about. EVERY collection is bounded (cap + moreHref in the model registry), every enum is open, every optional field explicitly nullable, and the model only ever grows. The validator computes your template's content footprint from the paths you read and stamps the required minimum model version at publish; dynamic indexing into site.content is refused so the footprint stays decidable."
51
+ },
4
52
  {
5
53
  "path": "section",
6
54
  "availableIn": [
7
55
  "section"
8
56
  ],
9
- "description": "The typed content of the section being rendered — its shape is the section type's fields (see the section catalogue)."
57
+ "description": "The typed content of the section being rendered, its shape is the section type's fields (see the section catalogue).",
58
+ "deprecated": true
10
59
  },
11
60
  {
12
61
  "path": "brand.name",
@@ -14,7 +63,8 @@
14
63
  "section",
15
64
  "layout"
16
65
  ],
17
- "description": "The charity's display name."
66
+ "description": "The charity's display name.",
67
+ "deprecated": true
18
68
  },
19
69
  {
20
70
  "path": "brand.tagline",
@@ -22,244 +72,288 @@
22
72
  "section",
23
73
  "layout"
24
74
  ],
25
- "description": "The charity's tagline."
75
+ "description": "The charity's tagline.",
76
+ "deprecated": true
26
77
  },
27
78
  {
28
79
  "path": "brand.logoUrl",
29
80
  "availableIn": [
30
81
  "layout"
31
82
  ],
32
- "description": "The charity's logo URL, or null when none is set — always branch on it."
83
+ "description": "The charity's logo URL, or null when none is set, always branch on it.",
84
+ "deprecated": true
33
85
  },
34
86
  {
35
87
  "path": "brand.logoType",
36
88
  "availableIn": [
37
89
  "layout"
38
90
  ],
39
- "description": "'full' (the image already contains the name — show it alone) or 'icon' (a mark — show it beside brand.name)."
91
+ "description": "'full' (the image already contains the name, show it alone) or 'icon' (a mark, show it beside brand.name).",
92
+ "deprecated": true
40
93
  },
41
94
  {
42
95
  "path": "brand.footerLogoUrl",
43
- "description": "Optional FOOTER mark (often a lighter or single-colour logo variant the tenant uploads for dark footers), or null — always branch. When unset, fall back to your own choice: the main logo, or the name as text.",
96
+ "description": "Optional FOOTER mark (often a lighter or single-colour logo variant the tenant uploads for dark footers), or null, always branch. When unset, fall back to your own choice: the main logo, or the name as text.",
44
97
  "availableIn": [
45
98
  "layout"
46
- ]
99
+ ],
100
+ "deprecated": true
47
101
  },
48
102
  {
49
103
  "path": "nav.items",
50
104
  "availableIn": [
51
105
  "layout"
52
106
  ],
53
- "description": "The site's primary navigation. By default platform-derived per tenant (pages that exist, features that are on); when the tenant has curated a header menu, this IS that menu instead — the same { label, href, cta?, external?, children? } shape either way, so render ALL of it exactly as before."
107
+ "description": "The site's primary navigation. By default platform-derived per tenant (pages that exist, features that are on); when the tenant has curated a header menu, this IS that menu instead, the same { label, href, cta?, external?, children? } shape either way, so render ALL of it exactly as before.",
108
+ "deprecated": true
54
109
  },
55
110
  {
56
111
  "path": "nav.items[].label",
57
112
  "availableIn": [
58
113
  "layout"
59
114
  ],
60
- "description": "The item's display label (tenant-chosen for some items — treat as text, it is escaped like everything else)."
115
+ "description": "The item's display label (tenant-chosen for some items, treat as text, it is escaped like everything else).",
116
+ "deprecated": true
61
117
  },
62
118
  {
63
119
  "path": "nav.items[].href",
64
120
  "availableIn": [
65
121
  "layout"
66
122
  ],
67
- "description": "The item's destination path."
123
+ "description": "The item's destination path.",
124
+ "deprecated": true
68
125
  },
69
126
  {
70
127
  "path": "nav.items[].cta",
71
128
  "availableIn": [
72
129
  "layout"
73
130
  ],
74
- "description": "True for the call-to-action item (Donate) — style it prominently."
131
+ "description": "True for the call-to-action item (Donate), style it prominently.",
132
+ "deprecated": true
75
133
  },
76
134
  {
77
135
  "path": "nav.items[].children",
78
136
  "availableIn": [
79
137
  "layout"
80
138
  ],
81
- "description": "Optional sub-items for dropdowns and mega menus. Children use the same item shape and may carry one further children level, giving a maximum of two levels below the top item."
139
+ "description": "Optional sub-items for dropdowns and mega menus. Children use the same item shape and may carry one further children level, giving a maximum of two levels below the top item.",
140
+ "deprecated": true
82
141
  },
83
142
  {
84
143
  "path": "nav.items[].external",
85
144
  "availableIn": [
86
145
  "layout"
87
146
  ],
88
- "description": "True when the item points off-site (a tenant-curated external link) — add target=\"_blank\" rel=\"noopener\". Absent/false for internal links."
147
+ "description": "True when the item points off-site (a tenant-curated external link), add target=\"_blank\" rel=\"noopener\". Absent/false for internal links.",
148
+ "deprecated": true
89
149
  },
90
150
  {
91
151
  "path": "nav.items[].group",
92
152
  "availableIn": [
93
153
  "layout"
94
154
  ],
95
- "description": "Optional tenant-authored column or topic label for grouping items inside a mega menu."
155
+ "description": "Optional tenant-authored column or topic label for grouping items inside a mega menu.",
156
+ "deprecated": true
96
157
  },
97
158
  {
98
159
  "path": "nav.items[].description",
99
160
  "availableIn": [
100
161
  "layout"
101
162
  ],
102
- "description": "Optional short supporting text for a richer navigation card."
163
+ "description": "Optional short supporting text for a richer navigation card.",
164
+ "deprecated": true
103
165
  },
104
166
  {
105
167
  "path": "nav.items[].imageUrl",
106
168
  "availableIn": [
107
169
  "layout"
108
170
  ],
109
- "description": "Optional image URL for a navigation card. Always branch when absent."
171
+ "description": "Optional image URL for a navigation card. Always branch when absent.",
172
+ "deprecated": true
110
173
  },
111
174
  {
112
175
  "path": "nav.items[].megaMenu",
113
176
  "availableIn": [
114
177
  "layout"
115
178
  ],
116
- "description": "Optional mega-menu presentation metadata on a top-level item: columns (2 to 4) and an optional promo object with title, text, href, label and imageUrl. It is content only; the template owns hover, focus and responsive presentation in CSS."
179
+ "description": "Optional mega-menu presentation metadata on a top-level item: columns (2 to 4) and an optional promo object with title, text, href, label and imageUrl. It is content only; the template owns hover, focus and responsive presentation in CSS.",
180
+ "deprecated": true
117
181
  },
118
182
  {
119
183
  "path": "nav.footer",
120
184
  "availableIn": [
121
185
  "layout"
122
186
  ],
123
- "description": "The tenant's curated FOOTER menu — the same item shape as nav.items[] (label, href, cta?, external?, children?). EMPTY when the tenant hasn't built one, so branch on nav.footer.size before rendering a footer-menu block (derive or omit). Optional to support — a template that ignores it simply shows its own footer."
187
+ "description": "The tenant's curated FOOTER menu, the same item shape as nav.items[] (label, href, cta?, external?, children?). EMPTY when the tenant hasn't built one, so branch on nav.footer.size before rendering a footer-menu block (derive or omit). Optional to support, a template that ignores it simply shows its own footer.",
188
+ "deprecated": true
124
189
  },
125
190
  {
126
191
  "path": "nav.derived",
127
192
  "availableIn": [
128
193
  "layout"
129
194
  ],
130
- "description": "The platform's AUTOMATIC nav (same item shape as nav.items[]), always present even when the tenant has curated nav.items. Use it when you want the auto, feature-aware list somewhere regardless of the tenant's header curation; most templates just render nav.items."
195
+ "description": "The platform's AUTOMATIC nav (same item shape as nav.items[]), always present even when the tenant has curated nav.items. Use it when you want the auto, feature-aware list somewhere regardless of the tenant's header curation; most templates just render nav.items.",
196
+ "deprecated": true
131
197
  },
132
198
  {
133
199
  "path": "socials",
134
200
  "availableIn": [
135
201
  "layout"
136
202
  ],
137
- "description": "The tenant's configured social links (possibly empty). Render icons via iconSvg — platform-generated markup, safe for the raw filter (the one exception beyond richtext fields)."
203
+ "description": "The tenant's configured social links (possibly empty). Render icons via iconSvg, platform-generated markup, safe for the raw filter (the one exception beyond richtext fields).",
204
+ "deprecated": true
138
205
  },
139
206
  {
140
207
  "path": "socials[].platform",
141
208
  "availableIn": [
142
209
  "layout"
143
210
  ],
144
- "description": "Platform key (facebook, instagram, x, …)."
211
+ "description": "Platform key (facebook, instagram, x, …).",
212
+ "deprecated": true
145
213
  },
146
214
  {
147
215
  "path": "socials[].url",
148
216
  "availableIn": [
149
217
  "layout"
150
218
  ],
151
- "description": "The profile URL."
219
+ "description": "The profile URL.",
220
+ "deprecated": true
152
221
  },
153
222
  {
154
223
  "path": "socials[].iconSvg",
155
224
  "availableIn": [
156
225
  "layout"
157
226
  ],
158
- "description": "The platform-drawn icon SVG for this network — output with | raw."
227
+ "description": "The platform-drawn icon SVG for this network, output with | raw.",
228
+ "deprecated": true
159
229
  },
160
230
  {
161
231
  "path": "worship",
162
232
  "availableIn": [
163
233
  "layout"
164
234
  ],
165
- "description": "The tenant's worship-schedule rail (prayer/service times), or null when not configured — ALWAYS branch on it. Faith-neutral: every label is tenant data. Render it as header chrome (the reference pattern is a utility bar above the nav); omit entirely when null. Rendering it requires declaring supports.worship in the manifest (the validator checks both directions) — the choosers use that declaration to steer worship-enabled tenants."
235
+ "description": "The tenant's worship-schedule rail (prayer/service times), or null when not configured, ALWAYS branch on it. Faith-neutral: every label is tenant data. Render it as header chrome (the reference pattern is a utility bar above the nav); omit entirely when null. Rendering it requires declaring supports.worship in the manifest (the validator checks both directions), the choosers use that declaration to steer worship-enabled tenants.",
236
+ "deprecated": true
166
237
  },
167
238
  {
168
239
  "path": "worship.times",
169
240
  "availableIn": [
170
241
  "layout"
171
242
  ],
172
- "description": "Up to 8 rows of {name, begins, congregation} — name is the prayer/service name; begins and congregation are display strings (times or short text), either may be null."
243
+ "description": "Up to 8 rows of {name, begins, congregation}, name is the prayer/service name; begins and congregation are display strings (times or short text), either may be null.",
244
+ "deprecated": true
173
245
  },
174
246
  {
175
247
  "path": "worship.beginsLabel",
176
248
  "availableIn": [
177
249
  "layout"
178
250
  ],
179
- "description": "Row label for the begins times (tenant-set; defaults to 'Begins')."
251
+ "description": "Row label for the begins times (tenant-set; defaults to 'Begins').",
252
+ "deprecated": true
180
253
  },
181
254
  {
182
255
  "path": "worship.congregationLabel",
183
256
  "availableIn": [
184
257
  "layout"
185
258
  ],
186
- "description": "Row label for the congregation times (tenant-set; e.g. \"Jama'at\"; defaults to 'Congregation'). Only meaningful when at least one row carries a congregation value."
259
+ "description": "Row label for the congregation times (tenant-set; e.g. \"Jama'at\"; defaults to 'Congregation'). Only meaningful when at least one row carries a congregation value.",
260
+ "deprecated": true
187
261
  },
188
262
  {
189
263
  "path": "worship.note",
190
264
  "availableIn": [
191
265
  "layout"
192
266
  ],
193
- "description": "A short free-text line for the rail (e.g. \"Jum'a 13:15 & 14:15\"), or null."
267
+ "description": "A short free-text line for the rail (e.g. \"Jum'a 13:15 & 14:15\"), or null.",
268
+ "deprecated": true
194
269
  },
195
270
  {
196
271
  "path": "worship.href",
197
272
  "availableIn": [
198
273
  "layout"
199
274
  ],
200
- "description": "A same-site path the note links to (a service page with full timetables), or null."
275
+ "description": "A same-site path the note links to (a service page with full timetables), or null.",
276
+ "deprecated": true
201
277
  },
202
278
  {
203
279
  "path": "worship.today",
204
280
  "availableIn": [
205
281
  "layout"
206
282
  ],
207
- "description": "Today's date, platform-formatted (e.g. \"24 July 2026\")."
283
+ "description": "Today's date, platform-formatted (e.g. \"24 July 2026\").",
284
+ "deprecated": true
208
285
  },
209
286
  {
210
287
  "path": "worship.todayAlt",
211
288
  "availableIn": [
212
289
  "layout"
213
290
  ],
214
- "description": "Today in the tenant's alternate calendar (Islamic civil calendar, e.g. \"11 Safar 1448\") when they opted in, else null — always branch."
291
+ "description": "Today in the tenant's alternate calendar (Islamic civil calendar, e.g. \"11 Safar 1448\") when they opted in, else null, always branch.",
292
+ "deprecated": true
215
293
  },
216
294
  {
217
295
  "path": "worship.next",
218
296
  "availableIn": [
219
297
  "layout"
220
298
  ],
221
- "description": "The next prayer or service as {name, begins, congregation, target}, or null. target is an ISO timestamp in the organisation's timezone. Templates may display it statically; use the next_prayer island for a ticking countdown."
299
+ "description": "The next prayer or service as {name, begins, congregation, target}, or null. target is an ISO timestamp in the organisation's timezone. Templates may display it statically; use the next_prayer island for a ticking countdown.",
300
+ "deprecated": true
222
301
  },
223
302
  {
224
303
  "path": "worship.jumah",
225
304
  "availableIn": [
226
305
  "layout"
227
306
  ],
228
- "description": "Structured Friday congregations as rows of {name, begins, congregation}. Empty for schedules without configured Friday times."
307
+ "description": "Structured Friday congregations as rows of {name, begins, congregation}. Empty for schedules without configured Friday times.",
308
+ "deprecated": true
229
309
  },
230
310
  {
231
311
  "path": "worship.hijri",
232
312
  "availableIn": [
233
313
  "layout"
234
314
  ],
235
- "description": "Structured alternate-calendar data. Contract v1 exposes {display}, or null when disabled."
315
+ "description": "Structured alternate-calendar data. Contract v1 exposes {display}, or null when disabled.",
316
+ "deprecated": true
236
317
  },
237
318
  {
238
319
  "path": "worship.observances",
239
320
  "availableIn": [
240
321
  "layout"
241
322
  ],
242
- "description": "Upcoming structured observances as {name, date, href}. Empty until the tenant has authored observances."
323
+ "description": "Upcoming structured observances as {name, date, href}. Empty until the tenant has authored observances.",
324
+ "deprecated": true
243
325
  },
244
326
  {
245
327
  "path": "locale",
246
- "availableIn": ["layout"],
247
- "description": "The public rendering locale and direction. Always present as {code, direction, languages}."
328
+ "availableIn": [
329
+ "layout"
330
+ ],
331
+ "description": "The public rendering locale and direction. Always present as {code, direction, languages}.",
332
+ "deprecated": true
248
333
  },
249
334
  {
250
335
  "path": "locale.code",
251
- "availableIn": ["layout"],
252
- "description": "Current BCP 47 language code, currently en, cy or ar."
336
+ "availableIn": [
337
+ "layout"
338
+ ],
339
+ "description": "Current BCP 47 language code, currently en, cy or ar.",
340
+ "deprecated": true
253
341
  },
254
342
  {
255
343
  "path": "locale.direction",
256
- "availableIn": ["layout"],
257
- "description": "ltr or rtl. The platform also applies this direction to the document element."
344
+ "availableIn": [
345
+ "layout"
346
+ ],
347
+ "description": "ltr or rtl. The platform also applies this direction to the document element.",
348
+ "deprecated": true
258
349
  },
259
350
  {
260
351
  "path": "locale.languages",
261
- "availableIn": ["layout"],
262
- "description": "Supported public languages as {code, label, direction}. Place the language_switch island for platform-owned switching behaviour."
352
+ "availableIn": [
353
+ "layout"
354
+ ],
355
+ "description": "Supported public languages as {code, label, direction}. Place the language_switch island for platform-owned switching behaviour.",
356
+ "deprecated": true
263
357
  },
264
358
  {
265
359
  "path": "events",
@@ -267,7 +361,8 @@
267
361
  "page:events",
268
362
  "section:events"
269
363
  ],
270
- "description": "The tenant's published events, soonest first — a CURATED read-only view for display. On page:events it is every published event; on section:events it is the TICKETED ones (the section's purpose). Transactional steps (RSVP, ticket purchase) stay platform-owned on the detail view — link with detailHref, never rebuild those flows."
364
+ "description": "The tenant's published events, soonest first, a CURATED read-only view for display. On page:events it is every published event; on section:events it is the TICKETED ones (the section's purpose). Transactional steps (RSVP, ticket purchase) stay platform-owned on the detail view, link with detailHref, never rebuild those flows.",
365
+ "deprecated": true
271
366
  },
272
367
  {
273
368
  "path": "causes",
@@ -275,7 +370,8 @@
275
370
  "section:appealGrid",
276
371
  "section:emergency"
277
372
  ],
278
- "description": "Published and currently available appeals. This is a display-only summary; payment processor identifiers and transaction configuration are never exposed. Empty when there is no active appeal."
373
+ "description": "Published and currently available appeals. This is a display-only summary; payment processor identifiers and transaction configuration are never exposed. Empty when there is no active appeal.",
374
+ "deprecated": true
279
375
  },
280
376
  {
281
377
  "path": "causes[]",
@@ -283,184 +379,224 @@
283
379
  "section:appealGrid",
284
380
  "section:emergency"
285
381
  ],
286
- "description": "An appeal card with id, title, slug, description, imageUrl, targetAmount, raisedAmount, progressPercentage, href and a boolean paymentOptions summary. Always use href to reach the platform donation flow."
382
+ "description": "An appeal card with id, title, slug, description, imageUrl, targetAmount, raisedAmount, progressPercentage, href and a boolean paymentOptions summary. Always use href to reach the platform donation flow.",
383
+ "deprecated": true
287
384
  },
288
385
  {
289
386
  "path": "services",
290
387
  "availableIn": [
291
388
  "section:programmes"
292
389
  ],
293
- "description": "Published services and programmes in display order. Empty when none are published, so omit the section rather than inventing cards."
390
+ "description": "Published services and programmes in display order. Empty when none are published, so omit the section rather than inventing cards.",
391
+ "deprecated": true
294
392
  },
295
393
  {
296
394
  "path": "services[]",
297
395
  "availableIn": [
298
396
  "section:programmes"
299
397
  ],
300
- "description": "A service card with id, title, slug, summary, imageUrl and href. Use href rather than constructing a route."
398
+ "description": "A service card with id, title, slug, summary, imageUrl and href. Use href rather than constructing a route.",
399
+ "deprecated": true
301
400
  },
302
401
  {
303
402
  "path": "resources",
304
- "availableIn": ["section:resources"],
305
- "description": "Public documents from the tenant's media library, newest first. Private assets are never included. Empty when no public documents exist."
403
+ "availableIn": [
404
+ "section:resources"
405
+ ],
406
+ "description": "Public documents from the tenant's media library, newest first. Private assets are never included. Empty when no public documents exist.",
407
+ "deprecated": true
306
408
  },
307
409
  {
308
410
  "path": "resources[]",
309
- "availableIn": ["section:resources"],
310
- "description": "A safe document link with id, title, folder, url, contentType, bytes and publishedAt. Always use url rather than constructing storage paths."
411
+ "availableIn": [
412
+ "section:resources"
413
+ ],
414
+ "description": "A safe document link with id, title, folder, url, contentType, bytes and publishedAt. Always use url rather than constructing storage paths.",
415
+ "deprecated": true
311
416
  },
312
417
  {
313
418
  "path": "locations",
314
- "availableIn": ["section:locations"],
315
- "description": "Public locations from the tenant's venue book, primary first. Empty when no venues are configured."
419
+ "availableIn": [
420
+ "section:locations"
421
+ ],
422
+ "description": "Public locations from the tenant's venue book, primary first. Empty when no venues are configured.",
423
+ "deprecated": true
316
424
  },
317
425
  {
318
426
  "path": "locations[]",
319
- "availableIn": ["section:locations"],
320
- "description": "A public location with id, name, address, latitude, longitude, primary and directionsHref. Provider references and private notes are excluded."
427
+ "availableIn": [
428
+ "section:locations"
429
+ ],
430
+ "description": "A public location with id, name, address, latitude, longitude, primary and directionsHref. Provider references and private notes are excluded.",
431
+ "deprecated": true
321
432
  },
322
433
  {
323
434
  "path": "opportunities",
324
- "availableIn": ["section:volunteering"],
325
- "description": "Published volunteering opportunities in display order. Each item contains id, slug, title, summary, imageUrl and content with location, schedule, commitment, dates, expected hours and shift dates. Sign-up remains the volunteer_signup island."
435
+ "availableIn": [
436
+ "section:volunteering"
437
+ ],
438
+ "description": "Published volunteering opportunities in display order. Each item contains id, slug, title, summary, imageUrl and content with location, schedule, commitment, dates, expected hours and shift dates. Sign-up remains the volunteer_signup island.",
439
+ "deprecated": true
326
440
  },
327
441
  {
328
442
  "path": "media",
329
- "availableIn": ["section:media"],
330
- "description": "Published structured media entries. Each item contains id, slug, title, summary, imageUrl and content with mediaType, mediaUrl, speaker, publishedAt and duration."
443
+ "availableIn": [
444
+ "section:media"
445
+ ],
446
+ "description": "Published structured media entries. Each item contains id, slug, title, summary, imageUrl and content with mediaType, mediaUrl, speaker, publishedAt and duration.",
447
+ "deprecated": true
331
448
  },
332
449
  {
333
450
  "path": "infoEvents",
334
451
  "availableIn": [
335
452
  "section:whatsOn"
336
453
  ],
337
- "description": "The tenant's INFO events (special days — no sign-up, no payment), soonest first. The same curated event shape as events[]. Render them your way, or place {% island 'whats_on_strip' %} for the ready-made strip. Empty when none — render nothing you invent (derive or omit)."
454
+ "description": "The tenant's INFO events (special days, no sign-up, no payment), soonest first. The same curated event shape as events[]. Render them your way, or place {% island 'whats_on_strip' %} for the ready-made strip. Empty when none, render nothing you invent (derive or omit).",
455
+ "deprecated": true
338
456
  },
339
457
  {
340
458
  "path": "latestArticles",
341
459
  "availableIn": [
342
460
  "section:articles"
343
461
  ],
344
- "description": "The newest published articles (up to 6), newest first. Render them your way, or place {% island 'latest_articles' %} for the ready-made strip. Empty when none — derive or omit."
462
+ "description": "The newest published articles (up to 6), newest first. Render them your way, or place {% island 'latest_articles' %} for the ready-made strip. Empty when none, derive or omit.",
463
+ "deprecated": true
345
464
  },
346
465
  {
347
466
  "path": "latestArticles[].title",
348
467
  "availableIn": [
349
468
  "section:articles"
350
469
  ],
351
- "description": "The article's title."
470
+ "description": "The article's title.",
471
+ "deprecated": true
352
472
  },
353
473
  {
354
474
  "path": "latestArticles[].href",
355
475
  "availableIn": [
356
476
  "section:articles"
357
477
  ],
358
- "description": "The canonical link to the article — always use this."
478
+ "description": "The canonical link to the article, always use this.",
479
+ "deprecated": true
359
480
  },
360
481
  {
361
482
  "path": "latestArticles[].excerpt",
362
483
  "availableIn": [
363
484
  "section:articles"
364
485
  ],
365
- "description": "Short summary, or null — branch on it."
486
+ "description": "Short summary, or null, branch on it.",
487
+ "deprecated": true
366
488
  },
367
489
  {
368
490
  "path": "latestArticles[].imageUrl",
369
491
  "availableIn": [
370
492
  "section:articles"
371
493
  ],
372
- "description": "Cover image URL, or null — always branch."
494
+ "description": "Cover image URL, or null, always branch.",
495
+ "deprecated": true
373
496
  },
374
497
  {
375
498
  "path": "latestArticles[].publishedAt",
376
499
  "availableIn": [
377
500
  "section:articles"
378
501
  ],
379
- "description": "ISO-8601 publish time, or null — format with the date filter."
502
+ "description": "ISO-8601 publish time, or null, format with the date filter.",
503
+ "deprecated": true
380
504
  },
381
505
  {
382
506
  "path": "latestArticles[].readingMinutes",
383
507
  "availableIn": [
384
508
  "section:articles"
385
509
  ],
386
- "description": "Estimated reading minutes, or null."
510
+ "description": "Estimated reading minutes, or null.",
511
+ "deprecated": true
387
512
  },
388
513
  {
389
514
  "path": "latestArticles[].categories",
390
515
  "availableIn": [
391
516
  "section:articles"
392
517
  ],
393
- "description": "The article's category names (possibly empty) — treat as text."
518
+ "description": "The article's category names (possibly empty), treat as text.",
519
+ "deprecated": true
394
520
  },
395
521
  {
396
522
  "path": "campaigns",
397
523
  "availableIn": [
398
524
  "section:campaigns"
399
525
  ],
400
- "description": "The tenant's live campaigns, in display order. Empty when none are live — render nothing."
526
+ "description": "The tenant's live campaigns, in display order. Empty when none are live, render nothing.",
527
+ "deprecated": true
401
528
  },
402
529
  {
403
530
  "path": "campaigns[].title",
404
531
  "availableIn": [
405
532
  "section:campaigns"
406
533
  ],
407
- "description": "The campaign's name."
534
+ "description": "The campaign's name.",
535
+ "deprecated": true
408
536
  },
409
537
  {
410
538
  "path": "campaigns[].href",
411
539
  "availableIn": [
412
540
  "section:campaigns"
413
541
  ],
414
- "description": "The canonical link to the campaign page — always use this."
542
+ "description": "The canonical link to the campaign page, always use this.",
543
+ "deprecated": true
415
544
  },
416
545
  {
417
546
  "path": "campaigns[].summary",
418
547
  "availableIn": [
419
548
  "section:campaigns"
420
549
  ],
421
- "description": "Short summary, or null — branch on it."
550
+ "description": "Short summary, or null, branch on it.",
551
+ "deprecated": true
422
552
  },
423
553
  {
424
554
  "path": "campaigns[].imageUrl",
425
555
  "availableIn": [
426
556
  "section:campaigns"
427
557
  ],
428
- "description": "Featured image URL, or null — always branch."
558
+ "description": "Featured image URL, or null, always branch.",
559
+ "deprecated": true
429
560
  },
430
561
  {
431
562
  "path": "campaignsLabel",
432
563
  "availableIn": [
433
564
  "section:campaigns"
434
565
  ],
435
- "description": "What the tenant calls their campaigns section (\"Campaigns\" by default) — the heading fallback."
566
+ "description": "What the tenant calls their campaigns section (\"Campaigns\" by default), the heading fallback.",
567
+ "deprecated": true
436
568
  },
437
569
  {
438
570
  "path": "articles",
439
571
  "availableIn": [
440
572
  "page:articles"
441
573
  ],
442
- "description": "Published article summaries for the current listing or selected category. Each item contains id, title, slug, excerpt, imageUrl, authorName, authorRole, categories, publishedAt, readingMinutes, engagement counts and href."
574
+ "description": "Published article summaries for the current listing or selected category. Each item contains id, title, slug, excerpt, imageUrl, authorName, authorRole, categories, publishedAt, readingMinutes, engagement counts and href.",
575
+ "deprecated": true
443
576
  },
444
577
  {
445
578
  "path": "selectedCategory",
446
579
  "availableIn": [
447
580
  "page:articles"
448
581
  ],
449
- "description": "The active category filter, or null on an unfiltered listing."
582
+ "description": "The active category filter, or null on an unfiltered listing.",
583
+ "deprecated": true
450
584
  },
451
585
  {
452
586
  "path": "article",
453
587
  "availableIn": [
454
588
  "page:article"
455
589
  ],
456
- "description": "The article detail being displayed. It carries the article summary fields plus sanitised bodyHtml, updatedAt and allowComments. Render bodyHtml with the raw filter; engagement and comments remain platform islands."
590
+ "description": "The article detail being displayed. It carries the article summary fields plus sanitised bodyHtml, updatedAt and allowComments. Render bodyHtml with the raw filter; engagement and comments remain platform islands.",
591
+ "deprecated": true
457
592
  },
458
593
  {
459
594
  "path": "relatedArticles",
460
595
  "availableIn": [
461
596
  "page:article"
462
597
  ],
463
- "description": "Up to three other published article summaries, newest first."
598
+ "description": "Up to three other published article summaries, newest first.",
599
+ "deprecated": true
464
600
  },
465
601
  {
466
602
  "path": "categories",
@@ -468,216 +604,246 @@
468
604
  "page:articles",
469
605
  "page:article"
470
606
  ],
471
- "description": "Categories currently used by published articles. Each item has name, count and href."
607
+ "description": "Categories currently used by published articles. Each item has name, count and href.",
608
+ "deprecated": true
472
609
  },
473
610
  {
474
611
  "path": "events[].id",
475
612
  "availableIn": [
476
613
  "page:events"
477
614
  ],
478
- "description": "Stable event id."
615
+ "description": "Stable event id.",
616
+ "deprecated": true
479
617
  },
480
618
  {
481
619
  "path": "events[].name",
482
620
  "availableIn": [
483
621
  "page:events"
484
622
  ],
485
- "description": "The event's name."
623
+ "description": "The event's name.",
624
+ "deprecated": true
486
625
  },
487
626
  {
488
627
  "path": "events[].description",
489
628
  "availableIn": [
490
629
  "page:events"
491
630
  ],
492
- "description": "Plain-text description, or null — branch on it."
631
+ "description": "Plain-text description, or null, branch on it.",
632
+ "deprecated": true
493
633
  },
494
634
  {
495
635
  "path": "events[].registrationMode",
496
636
  "availableIn": [
497
637
  "page:events"
498
638
  ],
499
- "description": "INFO (no sign-up), RSVP (free sign-up) or TICKETED (paid)."
639
+ "description": "INFO (no sign-up), RSVP (free sign-up) or TICKETED (paid).",
640
+ "deprecated": true
500
641
  },
501
642
  {
502
643
  "path": "events[].startsAt",
503
644
  "availableIn": [
504
645
  "page:events"
505
646
  ],
506
- "description": "ISO-8601 start — format with the date filter."
647
+ "description": "ISO-8601 start, format with the date filter.",
648
+ "deprecated": true
507
649
  },
508
650
  {
509
651
  "path": "events[].endsAt",
510
652
  "availableIn": [
511
653
  "page:events"
512
654
  ],
513
- "description": "ISO-8601 end, or null."
655
+ "description": "ISO-8601 end, or null.",
656
+ "deprecated": true
514
657
  },
515
658
  {
516
659
  "path": "events[].venueName",
517
660
  "availableIn": [
518
661
  "page:events"
519
662
  ],
520
- "description": "Venue name, or null (online events may have none)."
663
+ "description": "Venue name, or null (online events may have none).",
664
+ "deprecated": true
521
665
  },
522
666
  {
523
667
  "path": "events[].online",
524
668
  "availableIn": [
525
669
  "page:events"
526
670
  ],
527
- "description": "True for online events."
671
+ "description": "True for online events.",
672
+ "deprecated": true
528
673
  },
529
674
  {
530
675
  "path": "events[].imageUrl",
531
676
  "availableIn": [
532
677
  "page:events"
533
678
  ],
534
- "description": "Promo image URL, or null — always branch."
679
+ "description": "Promo image URL, or null, always branch.",
680
+ "deprecated": true
535
681
  },
536
682
  {
537
683
  "path": "events[].onSale",
538
684
  "availableIn": [
539
685
  "page:events"
540
686
  ],
541
- "description": "False before sales open."
687
+ "description": "False before sales open.",
688
+ "deprecated": true
542
689
  },
543
690
  {
544
691
  "path": "events[].soldOut",
545
692
  "availableIn": [
546
693
  "page:events"
547
694
  ],
548
- "description": "True when no capacity remains."
695
+ "description": "True when no capacity remains.",
696
+ "deprecated": true
549
697
  },
550
698
  {
551
699
  "path": "events[].priceFrom",
552
700
  "availableIn": [
553
701
  "page:events"
554
702
  ],
555
- "description": "The cheapest ticket price (number), or null for free/INFO events."
703
+ "description": "The cheapest ticket price (number), or null for free/INFO events.",
704
+ "deprecated": true
556
705
  },
557
706
  {
558
707
  "path": "events[].currency",
559
708
  "availableIn": [
560
709
  "page:events"
561
710
  ],
562
- "description": "ISO currency code for priceFrom, or null."
711
+ "description": "ISO currency code for priceFrom, or null.",
712
+ "deprecated": true
563
713
  },
564
714
  {
565
715
  "path": "events[].detailHref",
566
716
  "availableIn": [
567
717
  "page:events"
568
718
  ],
569
- "description": "The canonical link to this event's detail view — always use this, never build URLs yourself."
719
+ "description": "The canonical link to this event's detail view, always use this, never build URLs yourself.",
720
+ "deprecated": true
570
721
  },
571
722
  {
572
723
  "path": "course",
573
724
  "availableIn": [
574
725
  "page:course"
575
726
  ],
576
- "description": "The course this page addresses — a CURATED read-only view for display. The enrolment journey itself is the platform-owned course_enrol island; place it, never rebuild it."
727
+ "description": "The course this page addresses, a CURATED read-only view for display. The enrolment journey itself is the platform-owned course_enrol island; place it, never rebuild it.",
728
+ "deprecated": true
577
729
  },
578
730
  {
579
731
  "path": "course.id",
580
732
  "availableIn": [
581
733
  "page:course"
582
734
  ],
583
- "description": "Stable course id."
735
+ "description": "Stable course id.",
736
+ "deprecated": true
584
737
  },
585
738
  {
586
739
  "path": "course.title",
587
740
  "availableIn": [
588
741
  "page:course"
589
742
  ],
590
- "description": "The course's title."
743
+ "description": "The course's title.",
744
+ "deprecated": true
591
745
  },
592
746
  {
593
747
  "path": "course.description",
594
748
  "availableIn": [
595
749
  "page:course"
596
750
  ],
597
- "description": "Plain-text summary, or null — branch on it. Prefer aboutHtml for the body when present."
751
+ "description": "Plain-text summary, or null, branch on it. Prefer aboutHtml for the body when present.",
752
+ "deprecated": true
598
753
  },
599
754
  {
600
755
  "path": "course.aboutHtml",
601
756
  "availableIn": [
602
757
  "page:course"
603
758
  ],
604
- "description": "The tenant-authored About page as SANITISED HTML (platform-sanitised on save; images resolved) — render with the raw filter inside your prose styles, or null when not written."
759
+ "description": "The tenant-authored About page as SANITISED HTML (platform-sanitised on save; images resolved), render with the raw filter inside your prose styles, or null when not written.",
760
+ "deprecated": true
605
761
  },
606
762
  {
607
763
  "path": "course.imageUrl",
608
764
  "availableIn": [
609
765
  "page:course"
610
766
  ],
611
- "description": "The MAIN image (hero) URL, or null — always branch."
767
+ "description": "The MAIN image (hero) URL, or null, always branch.",
768
+ "deprecated": true
612
769
  },
613
770
  {
614
771
  "path": "course.venue",
615
772
  "availableIn": [
616
773
  "page:course"
617
774
  ],
618
- "description": "Venue display text, or null."
775
+ "description": "Venue display text, or null.",
776
+ "deprecated": true
619
777
  },
620
778
  {
621
779
  "path": "course.audience",
622
780
  "availableIn": [
623
781
  "page:course"
624
782
  ],
625
- "description": "EVERYONE, or CHILD for children-only courses (parents enrol their children)."
783
+ "description": "EVERYONE, or CHILD for children-only courses (parents enrol their children).",
784
+ "deprecated": true
626
785
  },
627
786
  {
628
787
  "path": "course.billingMode",
629
788
  "availableIn": [
630
789
  "page:course"
631
790
  ],
632
- "description": "UPFRONT (one payment), TERM (committed term fee), or ROLLING (monthly with notice)."
791
+ "description": "UPFRONT (one payment), TERM (committed term fee), or ROLLING (monthly with notice).",
792
+ "deprecated": true
633
793
  },
634
794
  {
635
795
  "path": "course.termStart",
636
796
  "availableIn": [
637
797
  "page:course"
638
798
  ],
639
- "description": "ISO date the term starts, or null — format with the date filter."
799
+ "description": "ISO date the term starts, or null, format with the date filter.",
800
+ "deprecated": true
640
801
  },
641
802
  {
642
803
  "path": "course.termEnd",
643
804
  "availableIn": [
644
805
  "page:course"
645
806
  ],
646
- "description": "ISO date the term ends, or null."
807
+ "description": "ISO date the term ends, or null.",
808
+ "deprecated": true
647
809
  },
648
810
  {
649
811
  "path": "course.currency",
650
812
  "availableIn": [
651
813
  "page:course"
652
814
  ],
653
- "description": "ISO currency code for priceFrom."
815
+ "description": "ISO currency code for priceFrom.",
816
+ "deprecated": true
654
817
  },
655
818
  {
656
819
  "path": "course.priceFrom",
657
820
  "availableIn": [
658
821
  "page:course"
659
822
  ],
660
- "description": "The cheapest priced option (number), or null when every option is free."
823
+ "description": "The cheapest priced option (number), or null when every option is free.",
824
+ "deprecated": true
661
825
  },
662
826
  {
663
827
  "path": "course.soldOut",
664
828
  "availableIn": [
665
829
  "page:course"
666
830
  ],
667
- "description": "True when no option has places left (the island shows the standard note)."
831
+ "description": "True when no option has places left (the island shows the standard note).",
832
+ "deprecated": true
668
833
  },
669
834
  {
670
835
  "path": "course.backHref",
671
836
  "availableIn": [
672
837
  "page:course"
673
838
  ],
674
- "description": "The canonical link back to the courses listing — always use this."
839
+ "description": "The canonical link back to the courses listing, always use this.",
840
+ "deprecated": true
675
841
  }
676
842
  ],
677
843
  "fixtures": {
678
844
  "brand": {
679
- "name": "Fixture Charity",
680
- "tagline": "A fixture tagline.",
845
+ "name": "The Lantern Trust",
846
+ "tagline": "Neighbours, looked after.",
681
847
  "logoUrl": null,
682
848
  "logoType": "full",
683
849
  "footerLogoUrl": null
@@ -740,28 +906,64 @@
740
906
  }
741
907
  ],
742
908
  "footer": [
743
- { "label": "About us", "href": "/about" },
744
- { "label": "Contact", "href": "https://example.org/contact", "external": true },
909
+ {
910
+ "label": "About us",
911
+ "href": "/about"
912
+ },
913
+ {
914
+ "label": "Contact",
915
+ "href": "https://example.org/contact",
916
+ "external": true
917
+ },
745
918
  {
746
919
  "label": "Our work",
747
920
  "href": "#",
748
921
  "children": [
749
- { "label": "Food bank", "href": "/services?service=food-bank" }
922
+ {
923
+ "label": "Food bank",
924
+ "href": "/services?service=food-bank"
925
+ }
750
926
  ]
751
927
  }
752
928
  ],
753
929
  "derived": [
754
- { "label": "Home", "href": "/", "cta": false },
755
- { "label": "About us", "href": "/about", "cta": false },
756
- { "label": "Events", "href": "/events", "cta": false },
757
- { "label": "Donate", "href": "/donate", "cta": true }
930
+ {
931
+ "label": "Home",
932
+ "href": "/",
933
+ "cta": false
934
+ },
935
+ {
936
+ "label": "About us",
937
+ "href": "/about",
938
+ "cta": false
939
+ },
940
+ {
941
+ "label": "Events",
942
+ "href": "/events",
943
+ "cta": false
944
+ },
945
+ {
946
+ "label": "Donate",
947
+ "href": "/donate",
948
+ "cta": true
949
+ }
758
950
  ]
759
951
  },
760
952
  "socials": [
761
953
  {
762
954
  "platform": "facebook",
763
- "url": "https://facebook.com/fixture",
955
+ "url": "https://facebook.com/lanterntrust",
764
956
  "iconSvg": "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/></svg>"
957
+ },
958
+ {
959
+ "platform": "instagram",
960
+ "url": "https://instagram.com/lanterntrust",
961
+ "iconSvg": "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"5\"/></svg>"
962
+ },
963
+ {
964
+ "platform": "youtube",
965
+ "url": "https://youtube.com/@lanterntrust",
966
+ "iconSvg": "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><polygon points=\"10,8 16,12 10,16\"/></svg>"
765
967
  }
766
968
  ],
767
969
  "worship": {
@@ -805,21 +1007,47 @@
805
1007
  "target": "2030-07-24T19:00:00+01:00"
806
1008
  },
807
1009
  "jumah": [
808
- { "name": "Jum'a 1", "begins": null, "congregation": "13:15" },
809
- { "name": "Jum'a 2", "begins": null, "congregation": "14:15" }
1010
+ {
1011
+ "name": "Jum'a 1",
1012
+ "begins": null,
1013
+ "congregation": "13:15"
1014
+ },
1015
+ {
1016
+ "name": "Jum'a 2",
1017
+ "begins": null,
1018
+ "congregation": "14:15"
1019
+ }
810
1020
  ],
811
- "hijri": { "display": "11 Safar 1448" },
1021
+ "hijri": {
1022
+ "display": "11 Safar 1448"
1023
+ },
812
1024
  "observances": [
813
- { "name": "Community iftar", "date": "2 March 2030", "href": "/events" }
1025
+ {
1026
+ "name": "Community iftar",
1027
+ "date": "2 March 2030",
1028
+ "href": "/events"
1029
+ }
814
1030
  ]
815
1031
  },
816
1032
  "locale": {
817
1033
  "code": "en",
818
1034
  "direction": "ltr",
819
1035
  "languages": [
820
- { "code": "en", "label": "English", "direction": "ltr" },
821
- { "code": "cy", "label": "Cymraeg", "direction": "ltr" },
822
- { "code": "ar", "label": "العربية", "direction": "rtl" }
1036
+ {
1037
+ "code": "en",
1038
+ "label": "English",
1039
+ "direction": "ltr"
1040
+ },
1041
+ {
1042
+ "code": "cy",
1043
+ "label": "Cymraeg",
1044
+ "direction": "ltr"
1045
+ },
1046
+ {
1047
+ "code": "ar",
1048
+ "label": "العربية",
1049
+ "direction": "rtl"
1050
+ }
823
1051
  ]
824
1052
  }
825
1053
  },
@@ -827,52 +1055,100 @@
827
1055
  "events": {
828
1056
  "events": [
829
1057
  {
830
- "id": "11111111-1111-1111-1111-111111111111",
831
- "name": "Summer Gala",
832
- "description": "An evening of music and giving.",
1058
+ "id": "22222222-2222-2222-2222-222222222222",
1059
+ "name": "The Lantern Gala",
1060
+ "description": "Our biggest night of the year: supper, music from the Alderford Community Choir, and the annual lantern walk along the river to close.",
833
1061
  "registrationMode": "TICKETED",
834
- "startsAt": "2030-08-15T18:00:00Z",
1062
+ "startsAt": "2030-09-20T19:00:00Z",
835
1063
  "endsAt": null,
836
- "venueName": "Town Hall",
1064
+ "venueName": "The Maltings, Alderford",
837
1065
  "online": false,
838
- "imageUrl": null,
1066
+ "imageUrl": "p60fixture:event/gala",
839
1067
  "onSale": true,
840
1068
  "soldOut": false,
841
- "priceFrom": 10.0,
1069
+ "priceFrom": 25.0,
842
1070
  "currency": "GBP",
843
- "detailHref": "/events?event=11111111-1111-1111-1111-111111111111"
1071
+ "detailHref": "/events?event=22222222-2222-2222-2222-222222222222"
844
1072
  },
845
1073
  {
846
- "id": "22222222-2222-2222-2222-222222222222",
847
- "name": "Coffee Morning",
848
- "description": null,
1074
+ "id": "44444444-4444-4444-4444-444444444444",
1075
+ "name": "Riverside Harvest Supper",
1076
+ "description": "A long-table supper on the Aldern meadow, cooked by our kitchen volunteers with produce from the community garden.",
1077
+ "registrationMode": "TICKETED",
1078
+ "startsAt": "2030-10-04T18:30:00Z",
1079
+ "endsAt": "2030-10-04T22:00:00Z",
1080
+ "venueName": "Aldern Meadow",
1081
+ "online": false,
1082
+ "imageUrl": "p60fixture:event/harvest",
1083
+ "onSale": true,
1084
+ "soldOut": false,
1085
+ "priceFrom": 12.5,
1086
+ "currency": "GBP",
1087
+ "detailHref": "/events?event=44444444-4444-4444-4444-444444444444"
1088
+ },
1089
+ {
1090
+ "id": "55555555-5555-5555-5555-555555555555",
1091
+ "name": "Volunteer Welcome Evening",
1092
+ "description": "Meet the team, hear where help is needed most this season, and find a role that fits the time you have.",
849
1093
  "registrationMode": "RSVP",
850
- "startsAt": "2030-08-01T10:00:00Z",
851
- "endsAt": "2030-08-01T12:00:00Z",
852
- "venueName": null,
853
- "online": true,
854
- "imageUrl": null,
1094
+ "startsAt": "2030-09-03T18:00:00Z",
1095
+ "endsAt": "2030-09-03T19:30:00Z",
1096
+ "venueName": "Foundry Lane Hub",
1097
+ "online": false,
1098
+ "imageUrl": "p60fixture:event/welcome",
855
1099
  "onSale": true,
856
- "soldOut": true,
1100
+ "soldOut": false,
857
1101
  "priceFrom": null,
858
1102
  "currency": null,
859
- "detailHref": "/events?event=22222222-2222-2222-2222-222222222222"
1103
+ "detailHref": "/events?event=55555555-5555-5555-5555-555555555555"
860
1104
  },
861
1105
  {
862
- "id": "33333333-3333-3333-3333-333333333333",
863
- "name": "Open Day",
864
- "description": "Drop in and meet the team.",
1106
+ "id": "66666666-6666-6666-6666-666666666666",
1107
+ "name": "Warm Space Wednesdays",
1108
+ "description": "Our hall is open, warm and stocked with tea every Wednesday through the cold months. No booking, no questions, everyone welcome.",
865
1109
  "registrationMode": "INFO",
866
- "startsAt": "2030-09-01T09:00:00Z",
867
- "endsAt": null,
868
- "venueName": "Community Centre",
1110
+ "startsAt": "2030-11-06T10:00:00Z",
1111
+ "endsAt": "2030-11-06T15:00:00Z",
1112
+ "venueName": "St Chad's Hall",
869
1113
  "online": false,
870
1114
  "imageUrl": null,
871
1115
  "onSale": true,
872
1116
  "soldOut": false,
873
1117
  "priceFrom": null,
874
1118
  "currency": null,
875
- "detailHref": "/events?event=33333333-3333-3333-3333-333333333333"
1119
+ "detailHref": "/events?event=66666666-6666-6666-6666-666666666666"
1120
+ },
1121
+ {
1122
+ "id": "77777777-7777-7777-7777-777777777777",
1123
+ "name": "Money Advice Drop-in",
1124
+ "description": "A quiet hour with our trained advisers. Bring letters you are worried about; we will work through them together.",
1125
+ "registrationMode": "INFO",
1126
+ "startsAt": "2030-09-12T10:00:00Z",
1127
+ "endsAt": "2030-09-12T12:00:00Z",
1128
+ "venueName": null,
1129
+ "online": true,
1130
+ "imageUrl": null,
1131
+ "onSale": true,
1132
+ "soldOut": false,
1133
+ "priceFrom": null,
1134
+ "currency": null,
1135
+ "detailHref": "/events?event=77777777-7777-7777-7777-777777777777"
1136
+ },
1137
+ {
1138
+ "id": "88888888-8888-8888-8888-888888888888",
1139
+ "name": "Winter Fair",
1140
+ "description": "Stalls, carols and the grand raffle in aid of the Winter Appeal. Free entry; every stall staffed by volunteers.",
1141
+ "registrationMode": "TICKETED",
1142
+ "startsAt": "2030-12-07T11:00:00Z",
1143
+ "endsAt": "2030-12-07T16:00:00Z",
1144
+ "venueName": "Alderford Market Square",
1145
+ "online": false,
1146
+ "imageUrl": "p60fixture:event/winterfair",
1147
+ "onSale": true,
1148
+ "soldOut": true,
1149
+ "priceFrom": 0.0,
1150
+ "currency": "GBP",
1151
+ "detailHref": "/events?event=88888888-8888-8888-8888-888888888888"
876
1152
  }
877
1153
  ]
878
1154
  },
@@ -880,9 +1156,9 @@
880
1156
  "course": {
881
1157
  "id": "33333333-3333-3333-3333-333333333333",
882
1158
  "title": "Junior Athletics",
883
- "description": "Weekly athletics for children aged 7–12.",
884
- "aboutHtml": "<h2>What we do</h2><p>Track and field fundamentals in a friendly, coached setting.</p><ul><li>Warm-ups and drills</li><li>Sprints, jumps and throws</li></ul>",
885
- "imageUrl": null,
1159
+ "description": "Weekly athletics for children aged 7 to 12, coached by qualified volunteers at the King's Park track.",
1160
+ "aboutHtml": "<h2>What we do</h2><p>Track and field fundamentals in a friendly, coached setting: warm-ups, drills, and a different discipline every week.</p><ul><li>Sprints, relays and middle distance</li><li>Long jump, high jump and throws</li><li>A club mini-meet at the end of every term</li></ul><h2>What to bring</h2><p>Trainers, a water bottle and a waterproof. Everything else, including spikes for the older group, is provided.</p>",
1161
+ "imageUrl": "p60fixture:course/athletics",
886
1162
  "venue": "King's Park track",
887
1163
  "audience": "CHILD",
888
1164
  "billingMode": "ROLLING",
@@ -897,70 +1173,212 @@
897
1173
  "articles": {
898
1174
  "selectedCategory": null,
899
1175
  "categories": [
900
- { "name": "News", "count": 4, "href": "/articles/all?category=News" },
901
- { "name": "Stories", "count": 2, "href": "/articles/all?category=Stories" }
1176
+ {
1177
+ "name": "News",
1178
+ "count": 3,
1179
+ "href": "/articles/all?category=News"
1180
+ },
1181
+ {
1182
+ "name": "Stories",
1183
+ "count": 2,
1184
+ "href": "/articles/all?category=Stories"
1185
+ },
1186
+ {
1187
+ "name": "Guides",
1188
+ "count": 1,
1189
+ "href": "/articles/all?category=Guides"
1190
+ }
902
1191
  ],
903
1192
  "articles": [
904
1193
  {
905
1194
  "id": "88888888-8888-8888-8888-888888888888",
906
- "title": "Fixture Community Garden Opens",
907
- "slug": "fixture-community-garden-opens",
908
- "excerpt": "Neighbours came together to create a new shared space.",
909
- "imageUrl": null,
1195
+ "title": "The Community Garden Opens Its Gates",
1196
+ "slug": "community-garden-opens",
1197
+ "excerpt": "Two years of digging, fundraising and Saturday mornings in the rain: the Foundry Lane garden is finally open to everyone.",
1198
+ "imageUrl": "p60fixture:article/garden",
910
1199
  "authorName": "Amina Khan",
911
1200
  "authorRole": "Community Editor",
912
- "categories": ["News"],
1201
+ "categories": [
1202
+ "News"
1203
+ ],
913
1204
  "publishedAt": "2030-04-12T09:00:00Z",
1205
+ "readingMinutes": 5,
1206
+ "viewCount": 412,
1207
+ "likeCount": 38,
1208
+ "commentCount": 9,
1209
+ "href": "/articles/community-garden-opens"
1210
+ },
1211
+ {
1212
+ "id": "99999999-9999-9999-9999-999999999999",
1213
+ "title": "What a Food Parcel Actually Holds",
1214
+ "slug": "what-a-food-parcel-holds",
1215
+ "excerpt": "People imagine tins and pasta. The real list is longer, more personal, and put together with more care than you might think.",
1216
+ "imageUrl": "p60fixture:article/parcel",
1217
+ "authorName": "Derek Osei",
1218
+ "authorRole": "Food Bank Lead",
1219
+ "categories": [
1220
+ "Stories"
1221
+ ],
1222
+ "publishedAt": "2030-03-28T09:00:00Z",
1223
+ "readingMinutes": 6,
1224
+ "viewCount": 655,
1225
+ "likeCount": 61,
1226
+ "commentCount": 14,
1227
+ "href": "/articles/what-a-food-parcel-holds"
1228
+ },
1229
+ {
1230
+ "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
1231
+ "title": "Meet the Wednesday Kitchen Crew",
1232
+ "slug": "wednesday-kitchen-crew",
1233
+ "excerpt": "Eleven volunteers, one borrowed oven, and a hundred and forty hot meals a week. The kitchen crew tell it in their own words.",
1234
+ "imageUrl": "p60fixture:people/kitchen",
1235
+ "authorName": "Amina Khan",
1236
+ "authorRole": "Community Editor",
1237
+ "categories": [
1238
+ "Stories"
1239
+ ],
1240
+ "publishedAt": "2030-03-02T09:00:00Z",
1241
+ "readingMinutes": 7,
1242
+ "viewCount": 388,
1243
+ "likeCount": 45,
1244
+ "commentCount": 11,
1245
+ "href": "/articles/wednesday-kitchen-crew"
1246
+ },
1247
+ {
1248
+ "id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
1249
+ "title": "Winter Appeal: Where Your Money Went",
1250
+ "slug": "winter-appeal-report",
1251
+ "excerpt": "You gave 18,400 pounds in eight weeks. Here is the line-by-line account of what it paid for, and what we learned.",
1252
+ "imageUrl": "p60fixture:campaign/winterreport",
1253
+ "authorName": "Priya Sharma",
1254
+ "authorRole": "Trustee",
1255
+ "categories": [
1256
+ "News"
1257
+ ],
1258
+ "publishedAt": "2030-02-14T09:00:00Z",
1259
+ "readingMinutes": 8,
1260
+ "viewCount": 902,
1261
+ "likeCount": 84,
1262
+ "commentCount": 21,
1263
+ "href": "/articles/winter-appeal-report"
1264
+ },
1265
+ {
1266
+ "id": "cccccccc-cccc-cccc-cccc-cccccccccccc",
1267
+ "title": "Five Things Our Money Advisers Wish You Knew",
1268
+ "slug": "money-advice-five-things",
1269
+ "excerpt": "The advice team see the same worries again and again. Most of them have a fix that takes less than an hour.",
1270
+ "imageUrl": null,
1271
+ "authorName": "Tom Bradley",
1272
+ "authorRole": "Advice Service",
1273
+ "categories": [
1274
+ "Guides"
1275
+ ],
1276
+ "publishedAt": "2030-01-30T09:00:00Z",
914
1277
  "readingMinutes": 4,
915
- "viewCount": 82,
916
- "likeCount": 12,
917
- "commentCount": 3,
918
- "href": "/articles/fixture-community-garden-opens"
1278
+ "viewCount": 1204,
1279
+ "likeCount": 96,
1280
+ "commentCount": 17,
1281
+ "href": "/articles/money-advice-five-things"
1282
+ },
1283
+ {
1284
+ "id": "dddddddd-dddd-dddd-dddd-dddddddddddd",
1285
+ "title": "A Year of Warm Spaces, in Numbers",
1286
+ "slug": "warm-spaces-in-numbers",
1287
+ "excerpt": "Fourteen hundred visits, ninety regulars, and one chess league nobody planned. What happens when you just open the door.",
1288
+ "imageUrl": "p60fixture:service/warmspace",
1289
+ "authorName": "Amina Khan",
1290
+ "authorRole": "Community Editor",
1291
+ "categories": [
1292
+ "News"
1293
+ ],
1294
+ "publishedAt": "2030-01-08T09:00:00Z",
1295
+ "readingMinutes": 5,
1296
+ "viewCount": 517,
1297
+ "likeCount": 42,
1298
+ "commentCount": 8,
1299
+ "href": "/articles/warm-spaces-in-numbers"
919
1300
  }
920
1301
  ]
921
1302
  },
922
1303
  "article": {
923
1304
  "article": {
924
1305
  "id": "88888888-8888-8888-8888-888888888888",
925
- "title": "Fixture Community Garden Opens",
926
- "slug": "fixture-community-garden-opens",
927
- "excerpt": "Neighbours came together to create a new shared space.",
928
- "imageUrl": null,
1306
+ "title": "The Community Garden Opens Its Gates",
1307
+ "slug": "community-garden-opens",
1308
+ "excerpt": "Two years of digging, fundraising and Saturday mornings in the rain: the Foundry Lane garden is finally open to everyone.",
1309
+ "imageUrl": "p60fixture:article/garden",
929
1310
  "authorName": "Amina Khan",
930
1311
  "authorRole": "Community Editor",
931
- "categories": ["News"],
1312
+ "categories": [
1313
+ "News"
1314
+ ],
932
1315
  "publishedAt": "2030-04-12T09:00:00Z",
1316
+ "readingMinutes": 5,
1317
+ "viewCount": 412,
1318
+ "likeCount": 38,
1319
+ "commentCount": 9,
1320
+ "href": "/articles/community-garden-opens",
933
1321
  "updatedAt": "2030-04-12T10:00:00Z",
934
- "readingMinutes": 4,
935
- "viewCount": 82,
936
- "likeCount": 12,
937
- "commentCount": 3,
938
- "href": "/articles/fixture-community-garden-opens",
939
- "bodyHtml": "<h2>A shared place to grow</h2><p>The garden is now open to everyone.</p>",
1322
+ "bodyHtml": "<h2>A shared place to grow</h2><p>The gates opened at ten. By half past, every raised bed had a family around it, the tool shed had a queue, and somebody had already claimed the sunny corner by the wall for tomatoes.</p><p>The Foundry Lane garden has been two years in the making. It began as a patch of rubble behind the old ironworks, offered to us on a peppercorn lease after the Winter Fair of 2028, and it has been built almost entirely by volunteer weekends since: twenty-two raised beds, a wheelchair-accessible path, a rainwater store, and a greenhouse pieced together from three donated ones.</p><blockquote><p>I have lived on this street forty years and never once stood on this ground. Now my grandchildren grow beans here.</p></blockquote><h2>What happens next</h2><p>The beds are allocated, but the garden is not finished, and it is not meant to be. This season the growers' committee has three priorities:</p><ul><li>A covered seating area, so the garden works in the rain it will certainly get</li><li>Weekly sessions with the youth club, growing the salads the kitchen crew cook with</li><li>A seed library in the old gatehouse, free to any Alderford household</li></ul><p>If you can lay a slab, mend a frame or simply keep a kettle going, the volunteer team would love to hear from you. And if you just want somewhere green to sit, the gate is open every day until dusk.</p><h3>Thank you</h3><p>To the eighty-one people who gave to the garden appeal, the six local firms who lent tools and time, and the council officers who said yes when it mattered: this one is yours as much as ours.</p>",
940
1323
  "allowComments": true
941
1324
  },
942
1325
  "relatedArticles": [
943
1326
  {
944
1327
  "id": "99999999-9999-9999-9999-999999999999",
945
- "title": "Fixture Volunteer Morning",
946
- "slug": "fixture-volunteer-morning",
947
- "excerpt": "How local volunteers prepared the garden.",
948
- "imageUrl": null,
949
- "authorName": null,
950
- "authorRole": null,
951
- "categories": ["Stories"],
952
- "publishedAt": "2030-04-01T09:00:00Z",
953
- "readingMinutes": 3,
954
- "viewCount": 44,
955
- "likeCount": 7,
956
- "commentCount": 0,
957
- "href": "/articles/fixture-volunteer-morning"
1328
+ "title": "What a Food Parcel Actually Holds",
1329
+ "slug": "what-a-food-parcel-holds",
1330
+ "excerpt": "People imagine tins and pasta. The real list is longer, more personal, and put together with more care than you might think.",
1331
+ "imageUrl": "p60fixture:article/parcel",
1332
+ "authorName": "Derek Osei",
1333
+ "authorRole": "Food Bank Lead",
1334
+ "categories": [
1335
+ "Stories"
1336
+ ],
1337
+ "publishedAt": "2030-03-28T09:00:00Z",
1338
+ "readingMinutes": 6,
1339
+ "viewCount": 655,
1340
+ "likeCount": 61,
1341
+ "commentCount": 14,
1342
+ "href": "/articles/what-a-food-parcel-holds"
1343
+ },
1344
+ {
1345
+ "id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
1346
+ "title": "Winter Appeal: Where Your Money Went",
1347
+ "slug": "winter-appeal-report",
1348
+ "excerpt": "You gave 18,400 pounds in eight weeks. Here is the line-by-line account of what it paid for, and what we learned.",
1349
+ "imageUrl": "p60fixture:campaign/winterreport",
1350
+ "authorName": "Priya Sharma",
1351
+ "authorRole": "Trustee",
1352
+ "categories": [
1353
+ "News"
1354
+ ],
1355
+ "publishedAt": "2030-02-14T09:00:00Z",
1356
+ "readingMinutes": 8,
1357
+ "viewCount": 902,
1358
+ "likeCount": 84,
1359
+ "commentCount": 21,
1360
+ "href": "/articles/winter-appeal-report"
1361
+ },
1362
+ {
1363
+ "id": "dddddddd-dddd-dddd-dddd-dddddddddddd",
1364
+ "title": "A Year of Warm Spaces, in Numbers",
1365
+ "slug": "warm-spaces-in-numbers",
1366
+ "excerpt": "Fourteen hundred visits, ninety regulars, and one chess league nobody planned. What happens when you just open the door.",
1367
+ "imageUrl": "p60fixture:service/warmspace",
1368
+ "authorName": "Amina Khan",
1369
+ "authorRole": "Community Editor",
1370
+ "categories": [
1371
+ "News"
1372
+ ],
1373
+ "publishedAt": "2030-01-08T09:00:00Z",
1374
+ "readingMinutes": 5,
1375
+ "viewCount": 517,
1376
+ "likeCount": 42,
1377
+ "commentCount": 8,
1378
+ "href": "/articles/warm-spaces-in-numbers"
958
1379
  }
959
1380
  ],
960
- "categories": [
961
- { "name": "News", "count": 4, "href": "/articles/all?category=News" },
962
- { "name": "Stories", "count": 2, "href": "/articles/all?category=Stories" }
963
- ]
1381
+ "backHref": "/articles"
964
1382
  }
965
1383
  },
966
1384
  "sections": {
@@ -968,97 +1386,174 @@
968
1386
  "events": [
969
1387
  {
970
1388
  "id": "22222222-2222-2222-2222-222222222222",
971
- "name": "Fixture Lantern Gala",
972
- "description": "An evening fixture event.",
1389
+ "name": "The Lantern Gala",
1390
+ "description": "Our biggest night of the year: supper, music from the Alderford Community Choir, and the annual lantern walk along the river to close.",
973
1391
  "registrationMode": "TICKETED",
974
1392
  "startsAt": "2030-09-20T19:00:00Z",
975
1393
  "endsAt": null,
976
- "venueName": "Fixture Hall",
1394
+ "venueName": "The Maltings, Alderford",
977
1395
  "online": false,
978
- "imageUrl": null,
1396
+ "imageUrl": "p60fixture:event/gala",
979
1397
  "onSale": true,
980
1398
  "soldOut": false,
981
- "priceFrom": 12.5,
1399
+ "priceFrom": 25.0,
982
1400
  "currency": "GBP",
983
1401
  "detailHref": "/events?event=22222222-2222-2222-2222-222222222222"
984
1402
  },
985
1403
  {
986
1404
  "id": "44444444-4444-4444-4444-444444444444",
987
- "name": "Fixture Riverside Concert",
988
- "description": "An evening fixture event.",
1405
+ "name": "Riverside Harvest Supper",
1406
+ "description": "A long-table supper on the Aldern meadow, cooked by our kitchen volunteers with produce from the community garden.",
989
1407
  "registrationMode": "TICKETED",
990
- "startsAt": "2030-09-20T19:00:00Z",
991
- "endsAt": null,
992
- "venueName": "Fixture Hall",
1408
+ "startsAt": "2030-10-04T18:30:00Z",
1409
+ "endsAt": "2030-10-04T22:00:00Z",
1410
+ "venueName": "Aldern Meadow",
993
1411
  "online": false,
994
- "imageUrl": null,
1412
+ "imageUrl": "p60fixture:event/harvest",
995
1413
  "onSale": true,
996
1414
  "soldOut": false,
997
1415
  "priceFrom": 12.5,
998
1416
  "currency": "GBP",
999
1417
  "detailHref": "/events?event=44444444-4444-4444-4444-444444444444"
1418
+ },
1419
+ {
1420
+ "id": "88888888-8888-8888-8888-888888888888",
1421
+ "name": "Winter Fair",
1422
+ "description": "Stalls, carols and the grand raffle in aid of the Winter Appeal. Free entry; every stall staffed by volunteers.",
1423
+ "registrationMode": "TICKETED",
1424
+ "startsAt": "2030-12-07T11:00:00Z",
1425
+ "endsAt": "2030-12-07T16:00:00Z",
1426
+ "venueName": "Alderford Market Square",
1427
+ "online": false,
1428
+ "imageUrl": "p60fixture:event/winterfair",
1429
+ "onSale": true,
1430
+ "soldOut": true,
1431
+ "priceFrom": 0.0,
1432
+ "currency": "GBP",
1433
+ "detailHref": "/events?event=88888888-8888-8888-8888-888888888888"
1000
1434
  }
1001
1435
  ]
1002
1436
  },
1003
1437
  "whatsOn": {
1004
1438
  "infoEvents": [
1005
1439
  {
1006
- "id": "33333333-3333-3333-3333-333333333333",
1007
- "name": "Fixture Open Morning",
1008
- "description": "An evening fixture event.",
1440
+ "id": "66666666-6666-6666-6666-666666666666",
1441
+ "name": "Warm Space Wednesdays",
1442
+ "description": "Open, warm, tea always on. Every Wednesday through winter.",
1009
1443
  "registrationMode": "INFO",
1010
- "startsAt": "2030-09-20T19:00:00Z",
1444
+ "startsAt": "2030-11-06T10:00:00Z",
1445
+ "endsAt": null,
1446
+ "venueName": "St Chad's Hall",
1447
+ "online": false,
1448
+ "imageUrl": null,
1449
+ "onSale": true,
1450
+ "soldOut": false,
1451
+ "priceFrom": null,
1452
+ "currency": null,
1453
+ "detailHref": "/events?event=66666666-6666-6666-6666-666666666666"
1454
+ },
1455
+ {
1456
+ "id": "77777777-7777-7777-7777-777777777777",
1457
+ "name": "Money Advice Drop-in",
1458
+ "description": "Trained advisers, no appointment needed.",
1459
+ "registrationMode": "INFO",
1460
+ "startsAt": "2030-09-12T10:00:00Z",
1461
+ "endsAt": null,
1462
+ "venueName": null,
1463
+ "online": true,
1464
+ "imageUrl": null,
1465
+ "onSale": true,
1466
+ "soldOut": false,
1467
+ "priceFrom": null,
1468
+ "currency": null,
1469
+ "detailHref": "/events?event=77777777-7777-7777-7777-777777777777"
1470
+ },
1471
+ {
1472
+ "id": "99999999-9999-9999-9999-999999999999",
1473
+ "name": "Community Garden Open Morning",
1474
+ "description": "See the beds, meet the growers, take some seedlings home.",
1475
+ "registrationMode": "INFO",
1476
+ "startsAt": "2030-09-14T10:00:00Z",
1011
1477
  "endsAt": null,
1012
- "venueName": "Fixture Hall",
1478
+ "venueName": "Foundry Lane Garden",
1013
1479
  "online": false,
1014
1480
  "imageUrl": null,
1015
1481
  "onSale": true,
1016
1482
  "soldOut": false,
1017
1483
  "priceFrom": null,
1018
1484
  "currency": null,
1019
- "detailHref": "/events?event=33333333-3333-3333-3333-333333333333"
1485
+ "detailHref": "/events?event=99999999-9999-9999-9999-999999999999"
1020
1486
  }
1021
1487
  ]
1022
1488
  },
1023
1489
  "articles": {
1024
1490
  "latestArticles": [
1025
1491
  {
1026
- "title": "Fixture Winter Diary",
1027
- "href": "/articles/fixture-winter-diary",
1028
- "excerpt": "A fixture article summary.",
1029
- "imageUrl": null,
1030
- "publishedAt": "2030-01-05T09:00:00Z",
1031
- "readingMinutes": 4,
1492
+ "title": "The Community Garden Opens Its Gates",
1493
+ "href": "/articles/community-garden-opens",
1494
+ "excerpt": "Two years of digging, fundraising and Saturday mornings in the rain: the Foundry Lane garden is finally open to everyone.",
1495
+ "imageUrl": "p60fixture:article/garden",
1496
+ "publishedAt": "2030-04-12T09:00:00Z",
1497
+ "readingMinutes": 5,
1032
1498
  "categories": [
1033
1499
  "News"
1034
1500
  ]
1035
1501
  },
1036
1502
  {
1037
- "title": "Fixture Spring Appeal Notes",
1038
- "href": "/articles/fixture-spring-appeal-notes",
1039
- "excerpt": "A fixture article summary.",
1040
- "imageUrl": null,
1041
- "publishedAt": "2030-01-05T09:00:00Z",
1042
- "readingMinutes": 4,
1503
+ "title": "What a Food Parcel Actually Holds",
1504
+ "href": "/articles/what-a-food-parcel-holds",
1505
+ "excerpt": "People imagine tins and pasta. The real list is longer, more personal, and put together with more care than you might think.",
1506
+ "imageUrl": "p60fixture:article/parcel",
1507
+ "publishedAt": "2030-03-28T09:00:00Z",
1508
+ "readingMinutes": 6,
1043
1509
  "categories": [
1044
- "News"
1510
+ "Stories"
1045
1511
  ]
1046
- }
1047
- ]
1048
- },
1049
- "campaigns": {
1050
- "campaigns": [
1512
+ },
1513
+ {
1514
+ "title": "Meet the Wednesday Kitchen Crew",
1515
+ "href": "/articles/wednesday-kitchen-crew",
1516
+ "excerpt": "Eleven volunteers, one borrowed oven, and a hundred and forty hot meals a week. The kitchen crew tell it in their own words.",
1517
+ "imageUrl": "p60fixture:people/kitchen",
1518
+ "publishedAt": "2030-03-02T09:00:00Z",
1519
+ "readingMinutes": 7,
1520
+ "categories": [
1521
+ "Stories"
1522
+ ]
1523
+ },
1524
+ {
1525
+ "title": "Winter Appeal: Where Your Money Went",
1526
+ "href": "/articles/winter-appeal-report",
1527
+ "excerpt": "You gave 18,400 pounds in eight weeks. Here is the line-by-line account of what it paid for, and what we learned.",
1528
+ "imageUrl": "p60fixture:campaign/winterreport",
1529
+ "publishedAt": "2030-02-14T09:00:00Z",
1530
+ "readingMinutes": 8,
1531
+ "categories": [
1532
+ "News"
1533
+ ]
1534
+ }
1535
+ ]
1536
+ },
1537
+ "campaigns": {
1538
+ "campaigns": [
1051
1539
  {
1052
1540
  "id": "99999999-9999-9999-9999-999999999991",
1053
- "title": "Fixture Winter Campaign",
1054
- "href": "/campaigns/fixture-winter-campaign",
1055
- "summary": "Help keep neighbours warm this winter.",
1056
- "imageUrl": "/fixture/campaign.jpg"
1541
+ "title": "Keep the 27 Bus",
1542
+ "href": "/campaigns/keep-the-27-bus",
1543
+ "summary": "The only bus serving the Fenwick estate is set to be cut. Add your name before the consultation closes.",
1544
+ "imageUrl": "p60fixture:campaign/bus"
1057
1545
  },
1058
1546
  {
1059
1547
  "id": "99999999-9999-9999-9999-999999999992",
1060
- "title": "Fixture Community Survey",
1061
- "href": "/campaigns/fixture-community-survey",
1548
+ "title": "A Defibrillator on Every Street",
1549
+ "href": "/campaigns/defibrillator-every-street",
1550
+ "summary": "We are raising for twelve more public defibrillators and training two hundred neighbours to use them.",
1551
+ "imageUrl": "p60fixture:campaign/defib"
1552
+ },
1553
+ {
1554
+ "id": "99999999-9999-9999-9999-999999999993",
1555
+ "title": "Alderford Housing Survey",
1556
+ "href": "/campaigns/housing-survey",
1062
1557
  "summary": null,
1063
1558
  "imageUrl": null
1064
1559
  }
@@ -1068,15 +1563,15 @@
1068
1563
  "emergency": {
1069
1564
  "causes": [
1070
1565
  {
1071
- "id": "55555555-5555-5555-5555-555555555555",
1072
- "title": "Fixture Emergency Appeal",
1073
- "slug": "fixture-emergency-appeal",
1074
- "description": "Urgent food, water and shelter.",
1075
- "imageUrl": null,
1566
+ "id": "55555555-5555-5555-5555-555555555550",
1567
+ "title": "Flood Response Appeal",
1568
+ "slug": "flood-response",
1569
+ "description": "The Aldern broke its banks on Sunday. Thirty households need dry bedding, dehumidifiers and somewhere warm now.",
1570
+ "imageUrl": "p60fixture:cause/flood",
1076
1571
  "targetAmount": 50000,
1077
1572
  "raisedAmount": 18750,
1078
1573
  "progressPercentage": 38,
1079
- "href": "/donate/fixture-emergency-appeal",
1574
+ "href": "/donate/flood-response",
1080
1575
  "paymentOptions": {
1081
1576
  "oneOff": true,
1082
1577
  "recurringCard": true,
@@ -1088,18 +1583,50 @@
1088
1583
  "appealGrid": {
1089
1584
  "causes": [
1090
1585
  {
1091
- "id": "55555555-5555-5555-5555-555555555555",
1092
- "title": "Fixture Winter Appeal",
1093
- "slug": "fixture-winter-appeal",
1094
- "description": "Warm meals and heating support.",
1095
- "imageUrl": null,
1586
+ "id": "55555555-5555-5555-5555-555555555551",
1587
+ "title": "Winter Appeal",
1588
+ "slug": "winter-appeal",
1589
+ "description": "Heating top-ups, warm bedding and a hot meal for the households the cold hits hardest.",
1590
+ "imageUrl": "p60fixture:cause/winter",
1096
1591
  "targetAmount": 20000,
1097
1592
  "raisedAmount": 12500,
1098
1593
  "progressPercentage": 63,
1099
- "href": "/donate/fixture-winter-appeal",
1594
+ "href": "/donate/winter-appeal",
1595
+ "paymentOptions": {
1596
+ "oneOff": true,
1597
+ "recurringCard": true,
1598
+ "directDebit": false
1599
+ }
1600
+ },
1601
+ {
1602
+ "id": "55555555-5555-5555-5555-555555555552",
1603
+ "title": "Food Bank Fund",
1604
+ "slug": "food-bank-fund",
1605
+ "description": "Keeps the shelves stocked between donation drives. Ten pounds fills a family parcel.",
1606
+ "imageUrl": "p60fixture:cause/foodbank",
1607
+ "targetAmount": 15000,
1608
+ "raisedAmount": 9800,
1609
+ "progressPercentage": 65,
1610
+ "href": "/donate/food-bank-fund",
1100
1611
  "paymentOptions": {
1101
1612
  "oneOff": true,
1102
1613
  "recurringCard": true,
1614
+ "directDebit": true
1615
+ }
1616
+ },
1617
+ {
1618
+ "id": "55555555-5555-5555-5555-555555555553",
1619
+ "title": "Youth Club Futures",
1620
+ "slug": "youth-club-futures",
1621
+ "description": "Coaching, kit and trips for the seventy young people who come through our doors each week.",
1622
+ "imageUrl": "p60fixture:cause/youth",
1623
+ "targetAmount": 8000,
1624
+ "raisedAmount": 2150,
1625
+ "progressPercentage": 27,
1626
+ "href": "/donate/youth-club-futures",
1627
+ "paymentOptions": {
1628
+ "oneOff": true,
1629
+ "recurringCard": false,
1103
1630
  "directDebit": false
1104
1631
  }
1105
1632
  }
@@ -1108,46 +1635,106 @@
1108
1635
  "programmes": {
1109
1636
  "services": [
1110
1637
  {
1111
- "id": "66666666-6666-6666-6666-666666666666",
1112
- "title": "Fixture Food Bank",
1113
- "slug": "fixture-food-bank",
1114
- "summary": "Practical food support for local households.",
1115
- "imageUrl": null,
1116
- "href": "/services?service=fixture-food-bank"
1638
+ "id": "66666666-6666-6666-6666-666666666661",
1639
+ "title": "Food Bank",
1640
+ "slug": "food-bank",
1641
+ "summary": "Emergency parcels, no referral needed, from the Foundry Lane depot every weekday morning.",
1642
+ "imageUrl": "p60fixture:service/foodbank",
1643
+ "href": "/services?service=food-bank"
1117
1644
  },
1118
1645
  {
1119
- "id": "77777777-7777-7777-7777-777777777777",
1120
- "title": "Fixture Youth Club",
1121
- "slug": "fixture-youth-club",
1122
- "summary": "Weekly activities for young people.",
1123
- "imageUrl": null,
1124
- "href": "/services?service=fixture-youth-club"
1646
+ "id": "66666666-6666-6666-6666-666666666662",
1647
+ "title": "Youth Club",
1648
+ "slug": "youth-club",
1649
+ "summary": "Sports, cooking and somewhere to be for ages 10 to 16, four evenings a week.",
1650
+ "imageUrl": "p60fixture:service/youthclub",
1651
+ "href": "/services?service=youth-club"
1652
+ },
1653
+ {
1654
+ "id": "66666666-6666-6666-6666-666666666663",
1655
+ "title": "Money Advice",
1656
+ "slug": "money-advice",
1657
+ "summary": "Free, confidential help with debt, benefits and the letters that keep you up at night.",
1658
+ "imageUrl": "p60fixture:service/advice",
1659
+ "href": "/services?service=money-advice"
1660
+ },
1661
+ {
1662
+ "id": "66666666-6666-6666-6666-666666666664",
1663
+ "title": "Community Garden",
1664
+ "slug": "community-garden",
1665
+ "summary": "Twenty-two raised beds, a greenhouse and a seed library, open to every Alderford household.",
1666
+ "imageUrl": "p60fixture:service/garden",
1667
+ "href": "/services?service=community-garden"
1668
+ },
1669
+ {
1670
+ "id": "66666666-6666-6666-6666-666666666665",
1671
+ "title": "Warm Spaces",
1672
+ "slug": "warm-spaces",
1673
+ "summary": "Our hall, open and heated through winter, with tea, wifi and company.",
1674
+ "imageUrl": "p60fixture:service/warmspace",
1675
+ "href": "/services?service=warm-spaces"
1125
1676
  }
1126
1677
  ]
1127
1678
  },
1128
1679
  "resources": {
1129
1680
  "resources": [
1130
1681
  {
1131
- "id": "88888888-8888-8888-8888-888888888888",
1132
- "title": "Fixture annual report.pdf",
1682
+ "id": "88888888-8888-8888-8888-888888888881",
1683
+ "title": "Annual report 2029.pdf",
1133
1684
  "folder": "Reports",
1134
- "url": "/fixture/annual-report.pdf",
1685
+ "url": "/fixture/annual-report-2029.pdf",
1135
1686
  "contentType": "application/pdf",
1136
- "bytes": 245760,
1687
+ "bytes": 1245760,
1137
1688
  "publishedAt": "2030-02-01T10:00:00Z"
1689
+ },
1690
+ {
1691
+ "id": "88888888-8888-8888-8888-888888888882",
1692
+ "title": "Safeguarding policy.pdf",
1693
+ "folder": "Policies",
1694
+ "url": "/fixture/safeguarding-policy.pdf",
1695
+ "contentType": "application/pdf",
1696
+ "bytes": 384000,
1697
+ "publishedAt": "2029-11-12T10:00:00Z"
1698
+ },
1699
+ {
1700
+ "id": "88888888-8888-8888-8888-888888888883",
1701
+ "title": "Volunteer handbook.pdf",
1702
+ "folder": "Volunteering",
1703
+ "url": "/fixture/volunteer-handbook.pdf",
1704
+ "contentType": "application/pdf",
1705
+ "bytes": 942080,
1706
+ "publishedAt": "2030-01-20T10:00:00Z"
1707
+ },
1708
+ {
1709
+ "id": "88888888-8888-8888-8888-888888888884",
1710
+ "title": "Hall hire form.pdf",
1711
+ "folder": "Forms",
1712
+ "url": "/fixture/hall-hire-form.pdf",
1713
+ "contentType": "application/pdf",
1714
+ "bytes": 128000,
1715
+ "publishedAt": "2029-09-02T10:00:00Z"
1138
1716
  }
1139
1717
  ]
1140
1718
  },
1141
1719
  "locations": {
1142
1720
  "locations": [
1143
1721
  {
1144
- "id": "99999999-9999-9999-9999-999999999999",
1145
- "name": "Fixture Community Hall",
1146
- "address": "1 Fixture Street, Leeds",
1722
+ "id": "99999999-9999-9999-9999-999999999901",
1723
+ "name": "Foundry Lane Hub",
1724
+ "address": "14 Foundry Lane, Alderford AL2 4RS",
1147
1725
  "latitude": 53.8008,
1148
1726
  "longitude": -1.5491,
1149
1727
  "primary": true,
1150
- "directionsHref": "https://www.google.com/maps/dir/?api=1&destination=Fixture%20Community%20Hall"
1728
+ "directionsHref": "https://www.google.com/maps/dir/?api=1&destination=Foundry%20Lane%20Hub"
1729
+ },
1730
+ {
1731
+ "id": "99999999-9999-9999-9999-999999999902",
1732
+ "name": "St Chad's Hall",
1733
+ "address": "Church Walk, Alderford AL1 2JW",
1734
+ "latitude": 53.7975,
1735
+ "longitude": -1.5562,
1736
+ "primary": false,
1737
+ "directionsHref": "https://www.google.com/maps/dir/?api=1&destination=St%20Chad%27s%20Hall"
1151
1738
  }
1152
1739
  ]
1153
1740
  },
@@ -1155,25 +1742,830 @@
1155
1742
  "opportunities": [
1156
1743
  {
1157
1744
  "id": "aaaaaaaa-1111-4111-8111-111111111111",
1158
- "slug": "fixture-garden-volunteer",
1159
- "title": "Fixture Garden Volunteer",
1160
- "summary": "Help care for the community garden on Saturday mornings.",
1745
+ "slug": "garden-volunteer",
1746
+ "title": "Garden Volunteer",
1747
+ "summary": "Keep the Foundry Lane beds thriving: sowing, watering, and showing first-timers where the trowels live.",
1748
+ "imageUrl": "p60fixture:volunteer/garden",
1749
+ "content": {
1750
+ "location": "Foundry Lane Garden",
1751
+ "schedule": "Saturday mornings",
1752
+ "commitment": "Twice a month",
1753
+ "hoursExpected": "3 hours"
1754
+ }
1755
+ },
1756
+ {
1757
+ "id": "aaaaaaaa-1111-4111-8111-111111111112",
1758
+ "slug": "kitchen-crew",
1759
+ "title": "Wednesday Kitchen Crew",
1760
+ "summary": "Help cook and serve the weekly community meal. Full food hygiene training provided.",
1761
+ "imageUrl": "p60fixture:volunteer/kitchen",
1762
+ "content": {
1763
+ "location": "St Chad's Hall",
1764
+ "schedule": "Wednesdays 9am to 2pm",
1765
+ "commitment": "Weekly",
1766
+ "hoursExpected": "5 hours"
1767
+ }
1768
+ },
1769
+ {
1770
+ "id": "aaaaaaaa-1111-4111-8111-111111111113",
1771
+ "slug": "food-bank-driver",
1772
+ "title": "Food Bank Driver",
1773
+ "summary": "Collect supermarket surplus on the morning round. A full licence and a friendly wave required.",
1161
1774
  "imageUrl": null,
1162
- "content": { "location": "Fixture Garden", "schedule": "Saturday mornings", "commitment": "Twice a month", "hoursExpected": "3 hours" }
1775
+ "content": {
1776
+ "location": "Foundry Lane depot",
1777
+ "schedule": "Weekday mornings",
1778
+ "commitment": "One morning a week",
1779
+ "hoursExpected": "2 hours"
1780
+ }
1163
1781
  }
1164
1782
  ]
1165
1783
  },
1166
1784
  "media": {
1167
1785
  "media": [
1786
+ {
1787
+ "id": "bbbbbbbb-2222-4222-8222-222222222221",
1788
+ "slug": "garden-opening-film",
1789
+ "title": "The Garden Opening, on Film",
1790
+ "summary": "Four minutes from the gates opening, shot by the youth club media group.",
1791
+ "imageUrl": "p60fixture:media/gardenfilm",
1792
+ "content": {
1793
+ "mediaType": "VIDEO",
1794
+ "mediaUrl": "/fixture/garden-opening",
1795
+ "speaker": null,
1796
+ "duration": "4 minutes"
1797
+ }
1798
+ },
1168
1799
  {
1169
1800
  "id": "bbbbbbbb-2222-4222-8222-222222222222",
1170
- "slug": "fixture-community-talk",
1171
- "title": "Fixture Community Talk",
1172
- "summary": "A short update from the project team.",
1801
+ "slug": "trustee-talk-2030",
1802
+ "title": "Where the Trust Goes Next",
1803
+ "summary": "Chair of trustees Priya Sharma on the three-year plan, recorded at the spring members' evening.",
1804
+ "imageUrl": "p60fixture:media/talk",
1805
+ "content": {
1806
+ "mediaType": "VIDEO",
1807
+ "mediaUrl": "/fixture/trustee-talk",
1808
+ "speaker": "Priya Sharma",
1809
+ "duration": "18 minutes"
1810
+ }
1811
+ },
1812
+ {
1813
+ "id": "bbbbbbbb-2222-4222-8222-222222222223",
1814
+ "slug": "sound-of-the-kitchen",
1815
+ "title": "Sound of the Kitchen",
1816
+ "summary": "An audio portrait of Wednesday lunchtime, best enjoyed hungry.",
1173
1817
  "imageUrl": null,
1174
- "content": { "mediaType": "VIDEO", "mediaUrl": "/fixture/community-talk", "speaker": "Amina Khan", "duration": "18 minutes" }
1818
+ "content": {
1819
+ "mediaType": "AUDIO",
1820
+ "mediaUrl": "/fixture/kitchen-audio",
1821
+ "speaker": null,
1822
+ "duration": "9 minutes"
1823
+ }
1824
+ }
1825
+ ]
1826
+ },
1827
+ "impactMap": {
1828
+ "impactMap": {
1829
+ "slug": "our-work",
1830
+ "title": "Where the Trust works",
1831
+ "intro": "Every marker is a real place in Alderford.",
1832
+ "viewport": {
1833
+ "centerLat": 53.7995,
1834
+ "centerLng": -1.552,
1835
+ "zoom": 14
1836
+ },
1837
+ "points": [
1838
+ {
1839
+ "latitude": 53.8008,
1840
+ "longitude": -1.5491,
1841
+ "kind": "LOCATION",
1842
+ "title": "Foundry Lane Hub",
1843
+ "summary": "The front door: food bank depot, advice desk and the volunteer office.",
1844
+ "imageUrl": null,
1845
+ "href": null
1846
+ },
1847
+ {
1848
+ "latitude": 53.8014,
1849
+ "longitude": -1.5502,
1850
+ "kind": "CAUSE",
1851
+ "title": "Community Garden",
1852
+ "summary": "Twenty-two raised beds and a seed library, open to every household.",
1853
+ "imageUrl": null,
1854
+ "href": "/donate/food-bank-fund"
1855
+ },
1856
+ {
1857
+ "latitude": 53.7975,
1858
+ "longitude": -1.5562,
1859
+ "kind": "EVENT",
1860
+ "title": "Warm Space Wednesdays",
1861
+ "summary": "St Chad's Hall, open and heated all winter.",
1862
+ "imageUrl": null,
1863
+ "href": "/events?event=66666666-6666-6666-6666-666666666666"
1864
+ },
1865
+ {
1866
+ "latitude": 53.7969,
1867
+ "longitude": -1.5474,
1868
+ "kind": "CAMPAIGN",
1869
+ "title": "Keep the 27 Bus",
1870
+ "summary": "The only bus serving the Fenwick estate is set to be cut.",
1871
+ "imageUrl": null,
1872
+ "href": "/campaigns/keep-the-27-bus"
1873
+ }
1874
+ ]
1875
+ }
1876
+ }
1877
+ },
1878
+ "site": {
1879
+ "brand": {
1880
+ "name": "The Lantern Trust",
1881
+ "tagline": "Neighbours, looked after.",
1882
+ "logoUrl": null,
1883
+ "logoType": "full",
1884
+ "footerLogoUrl": null
1885
+ },
1886
+ "nav": {
1887
+ "items": [
1888
+ {
1889
+ "label": "Home",
1890
+ "href": "/",
1891
+ "cta": false
1892
+ },
1893
+ {
1894
+ "label": "About us",
1895
+ "href": "/about",
1896
+ "cta": false
1897
+ },
1898
+ {
1899
+ "label": "Events",
1900
+ "href": "/events",
1901
+ "cta": false
1902
+ },
1903
+ {
1904
+ "label": "Services",
1905
+ "href": "/services",
1906
+ "cta": false,
1907
+ "children": [
1908
+ {
1909
+ "label": "Food bank",
1910
+ "href": "/services?service=food-bank",
1911
+ "group": "Support",
1912
+ "description": "Emergency food parcels and practical advice."
1913
+ },
1914
+ {
1915
+ "label": "Community",
1916
+ "href": "#",
1917
+ "group": "Get involved",
1918
+ "children": [
1919
+ {
1920
+ "label": "Youth club",
1921
+ "href": "/services?service=youth-club"
1922
+ }
1923
+ ]
1924
+ }
1925
+ ],
1926
+ "megaMenu": {
1927
+ "columns": 3,
1928
+ "promo": {
1929
+ "title": "Visit this week",
1930
+ "text": "Find a programme and meet the team.",
1931
+ "href": "/services",
1932
+ "label": "Explore services"
1933
+ }
1934
+ }
1935
+ },
1936
+ {
1937
+ "label": "Donate",
1938
+ "href": "/donate",
1939
+ "cta": true
1940
+ }
1941
+ ],
1942
+ "footer": [
1943
+ {
1944
+ "label": "About us",
1945
+ "href": "/about"
1946
+ },
1947
+ {
1948
+ "label": "Contact",
1949
+ "href": "https://example.org/contact",
1950
+ "external": true
1951
+ },
1952
+ {
1953
+ "label": "Our work",
1954
+ "href": "#",
1955
+ "children": [
1956
+ {
1957
+ "label": "Food bank",
1958
+ "href": "/services?service=food-bank"
1959
+ }
1960
+ ]
1961
+ }
1962
+ ],
1963
+ "derived": [
1964
+ {
1965
+ "label": "Home",
1966
+ "href": "/",
1967
+ "cta": false
1968
+ },
1969
+ {
1970
+ "label": "About us",
1971
+ "href": "/about",
1972
+ "cta": false
1973
+ },
1974
+ {
1975
+ "label": "Events",
1976
+ "href": "/events",
1977
+ "cta": false
1978
+ },
1979
+ {
1980
+ "label": "Donate",
1981
+ "href": "/donate",
1982
+ "cta": true
1983
+ }
1984
+ ]
1985
+ },
1986
+ "socials": [
1987
+ {
1988
+ "platform": "facebook",
1989
+ "url": "https://facebook.com/lanterntrust",
1990
+ "iconSvg": "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/></svg>"
1991
+ },
1992
+ {
1993
+ "platform": "instagram",
1994
+ "url": "https://instagram.com/lanterntrust",
1995
+ "iconSvg": "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"5\"/></svg>"
1996
+ },
1997
+ {
1998
+ "platform": "youtube",
1999
+ "url": "https://youtube.com/@lanterntrust",
2000
+ "iconSvg": "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><polygon points=\"10,8 16,12 10,16\"/></svg>"
2001
+ }
2002
+ ],
2003
+ "locale": {
2004
+ "code": "en",
2005
+ "direction": "ltr",
2006
+ "languages": [
2007
+ {
2008
+ "code": "en",
2009
+ "label": "English",
2010
+ "direction": "ltr"
2011
+ },
2012
+ {
2013
+ "code": "cy",
2014
+ "label": "Cymraeg",
2015
+ "direction": "ltr"
2016
+ },
2017
+ {
2018
+ "code": "ar",
2019
+ "label": "العربية",
2020
+ "direction": "rtl"
1175
2021
  }
1176
2022
  ]
2023
+ },
2024
+ "content": {
2025
+ "services": [
2026
+ {
2027
+ "id": "66666666-6666-6666-6666-666666666661",
2028
+ "title": "Food Bank",
2029
+ "slug": "food-bank",
2030
+ "summary": "Emergency parcels, no referral needed, from the Foundry Lane depot every weekday morning.",
2031
+ "imageUrl": "p60fixture:service/foodbank",
2032
+ "href": "/services?service=food-bank"
2033
+ },
2034
+ {
2035
+ "id": "66666666-6666-6666-6666-666666666662",
2036
+ "title": "Youth Club",
2037
+ "slug": "youth-club",
2038
+ "summary": "Sports, cooking and somewhere to be for ages 10 to 16, four evenings a week.",
2039
+ "imageUrl": "p60fixture:service/youthclub",
2040
+ "href": "/services?service=youth-club"
2041
+ },
2042
+ {
2043
+ "id": "66666666-6666-6666-6666-666666666663",
2044
+ "title": "Money Advice",
2045
+ "slug": "money-advice",
2046
+ "summary": "Free, confidential help with debt, benefits and the letters that keep you up at night.",
2047
+ "imageUrl": "p60fixture:service/advice",
2048
+ "href": "/services?service=money-advice"
2049
+ },
2050
+ {
2051
+ "id": "66666666-6666-6666-6666-666666666664",
2052
+ "title": "Community Garden",
2053
+ "slug": "community-garden",
2054
+ "summary": "Twenty-two raised beds, a greenhouse and a seed library, open to every Alderford household.",
2055
+ "imageUrl": "p60fixture:service/garden",
2056
+ "href": "/services?service=community-garden"
2057
+ },
2058
+ {
2059
+ "id": "66666666-6666-6666-6666-666666666665",
2060
+ "title": "Warm Spaces",
2061
+ "slug": "warm-spaces",
2062
+ "summary": "Our hall, open and heated through winter, with tea, wifi and company.",
2063
+ "imageUrl": "p60fixture:service/warmspace",
2064
+ "href": "/services?service=warm-spaces"
2065
+ }
2066
+ ],
2067
+ "events": [
2068
+ {
2069
+ "id": "22222222-2222-2222-2222-222222222222",
2070
+ "name": "The Lantern Gala",
2071
+ "description": "Our biggest night of the year: supper, music from the Alderford Community Choir, and the annual lantern walk along the river to close.",
2072
+ "registrationMode": "TICKETED",
2073
+ "startsAt": "2030-09-20T19:00:00Z",
2074
+ "endsAt": null,
2075
+ "venueName": "The Maltings, Alderford",
2076
+ "online": false,
2077
+ "imageUrl": "p60fixture:event/gala",
2078
+ "onSale": true,
2079
+ "soldOut": false,
2080
+ "priceFrom": 25.0,
2081
+ "currency": "GBP",
2082
+ "detailHref": "/events?event=22222222-2222-2222-2222-222222222222"
2083
+ },
2084
+ {
2085
+ "id": "44444444-4444-4444-4444-444444444444",
2086
+ "name": "Riverside Harvest Supper",
2087
+ "description": "A long-table supper on the Aldern meadow, cooked by our kitchen volunteers with produce from the community garden.",
2088
+ "registrationMode": "TICKETED",
2089
+ "startsAt": "2030-10-04T18:30:00Z",
2090
+ "endsAt": "2030-10-04T22:00:00Z",
2091
+ "venueName": "Aldern Meadow",
2092
+ "online": false,
2093
+ "imageUrl": "p60fixture:event/harvest",
2094
+ "onSale": true,
2095
+ "soldOut": false,
2096
+ "priceFrom": 12.5,
2097
+ "currency": "GBP",
2098
+ "detailHref": "/events?event=44444444-4444-4444-4444-444444444444"
2099
+ },
2100
+ {
2101
+ "id": "55555555-5555-5555-5555-555555555555",
2102
+ "name": "Volunteer Welcome Evening",
2103
+ "description": "Meet the team, hear where help is needed most this season, and find a role that fits the time you have.",
2104
+ "registrationMode": "RSVP",
2105
+ "startsAt": "2030-09-03T18:00:00Z",
2106
+ "endsAt": "2030-09-03T19:30:00Z",
2107
+ "venueName": "Foundry Lane Hub",
2108
+ "online": false,
2109
+ "imageUrl": "p60fixture:event/welcome",
2110
+ "onSale": true,
2111
+ "soldOut": false,
2112
+ "priceFrom": null,
2113
+ "currency": null,
2114
+ "detailHref": "/events?event=55555555-5555-5555-5555-555555555555"
2115
+ },
2116
+ {
2117
+ "id": "66666666-6666-6666-6666-666666666666",
2118
+ "name": "Warm Space Wednesdays",
2119
+ "description": "Our hall is open, warm and stocked with tea every Wednesday through the cold months. No booking, no questions, everyone welcome.",
2120
+ "registrationMode": "INFO",
2121
+ "startsAt": "2030-11-06T10:00:00Z",
2122
+ "endsAt": "2030-11-06T15:00:00Z",
2123
+ "venueName": "St Chad's Hall",
2124
+ "online": false,
2125
+ "imageUrl": null,
2126
+ "onSale": true,
2127
+ "soldOut": false,
2128
+ "priceFrom": null,
2129
+ "currency": null,
2130
+ "detailHref": "/events?event=66666666-6666-6666-6666-666666666666"
2131
+ },
2132
+ {
2133
+ "id": "77777777-7777-7777-7777-777777777777",
2134
+ "name": "Money Advice Drop-in",
2135
+ "description": "A quiet hour with our trained advisers. Bring letters you are worried about; we will work through them together.",
2136
+ "registrationMode": "INFO",
2137
+ "startsAt": "2030-09-12T10:00:00Z",
2138
+ "endsAt": "2030-09-12T12:00:00Z",
2139
+ "venueName": null,
2140
+ "online": true,
2141
+ "imageUrl": null,
2142
+ "onSale": true,
2143
+ "soldOut": false,
2144
+ "priceFrom": null,
2145
+ "currency": null,
2146
+ "detailHref": "/events?event=77777777-7777-7777-7777-777777777777"
2147
+ },
2148
+ {
2149
+ "id": "88888888-8888-8888-8888-888888888888",
2150
+ "name": "Winter Fair",
2151
+ "description": "Stalls, carols and the grand raffle in aid of the Winter Appeal. Free entry; every stall staffed by volunteers.",
2152
+ "registrationMode": "TICKETED",
2153
+ "startsAt": "2030-12-07T11:00:00Z",
2154
+ "endsAt": "2030-12-07T16:00:00Z",
2155
+ "venueName": "Alderford Market Square",
2156
+ "online": false,
2157
+ "imageUrl": "p60fixture:event/winterfair",
2158
+ "onSale": true,
2159
+ "soldOut": true,
2160
+ "priceFrom": 0.0,
2161
+ "currency": "GBP",
2162
+ "detailHref": "/events?event=88888888-8888-8888-8888-888888888888"
2163
+ }
2164
+ ],
2165
+ "articles": [
2166
+ {
2167
+ "id": "88888888-8888-8888-8888-888888888888",
2168
+ "title": "The Community Garden Opens Its Gates",
2169
+ "slug": "community-garden-opens",
2170
+ "excerpt": "Two years of digging, fundraising and Saturday mornings in the rain: the Foundry Lane garden is finally open to everyone.",
2171
+ "imageUrl": "p60fixture:article/garden",
2172
+ "authorName": "Amina Khan",
2173
+ "authorRole": "Community Editor",
2174
+ "categories": [
2175
+ "News"
2176
+ ],
2177
+ "publishedAt": "2030-04-12T09:00:00Z",
2178
+ "readingMinutes": 5,
2179
+ "href": "/articles/community-garden-opens"
2180
+ },
2181
+ {
2182
+ "id": "99999999-9999-9999-9999-999999999999",
2183
+ "title": "What a Food Parcel Actually Holds",
2184
+ "slug": "what-a-food-parcel-holds",
2185
+ "excerpt": "People imagine tins and pasta. The real list is longer, more personal, and put together with more care than you might think.",
2186
+ "imageUrl": "p60fixture:article/parcel",
2187
+ "authorName": "Derek Osei",
2188
+ "authorRole": "Food Bank Lead",
2189
+ "categories": [
2190
+ "Stories"
2191
+ ],
2192
+ "publishedAt": "2030-03-28T09:00:00Z",
2193
+ "readingMinutes": 6,
2194
+ "href": "/articles/what-a-food-parcel-holds"
2195
+ },
2196
+ {
2197
+ "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
2198
+ "title": "Meet the Wednesday Kitchen Crew",
2199
+ "slug": "wednesday-kitchen-crew",
2200
+ "excerpt": "Eleven volunteers, one borrowed oven, and a hundred and forty hot meals a week. The kitchen crew tell it in their own words.",
2201
+ "imageUrl": "p60fixture:people/kitchen",
2202
+ "authorName": "Amina Khan",
2203
+ "authorRole": "Community Editor",
2204
+ "categories": [
2205
+ "Stories"
2206
+ ],
2207
+ "publishedAt": "2030-03-02T09:00:00Z",
2208
+ "readingMinutes": 7,
2209
+ "href": "/articles/wednesday-kitchen-crew"
2210
+ },
2211
+ {
2212
+ "id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
2213
+ "title": "Winter Appeal: Where Your Money Went",
2214
+ "slug": "winter-appeal-report",
2215
+ "excerpt": "You gave 18,400 pounds in eight weeks. Here is the line-by-line account of what it paid for, and what we learned.",
2216
+ "imageUrl": "p60fixture:campaign/winterreport",
2217
+ "authorName": "Priya Sharma",
2218
+ "authorRole": "Trustee",
2219
+ "categories": [
2220
+ "News"
2221
+ ],
2222
+ "publishedAt": "2030-02-14T09:00:00Z",
2223
+ "readingMinutes": 8,
2224
+ "href": "/articles/winter-appeal-report"
2225
+ },
2226
+ {
2227
+ "id": "cccccccc-cccc-cccc-cccc-cccccccccccc",
2228
+ "title": "Five Things Our Money Advisers Wish You Knew",
2229
+ "slug": "money-advice-five-things",
2230
+ "excerpt": "The advice team see the same worries again and again. Most of them have a fix that takes less than an hour.",
2231
+ "imageUrl": null,
2232
+ "authorName": "Tom Bradley",
2233
+ "authorRole": "Advice Service",
2234
+ "categories": [
2235
+ "Guides"
2236
+ ],
2237
+ "publishedAt": "2030-01-30T09:00:00Z",
2238
+ "readingMinutes": 4,
2239
+ "href": "/articles/money-advice-five-things"
2240
+ },
2241
+ {
2242
+ "id": "dddddddd-dddd-dddd-dddd-dddddddddddd",
2243
+ "title": "A Year of Warm Spaces, in Numbers",
2244
+ "slug": "warm-spaces-in-numbers",
2245
+ "excerpt": "Fourteen hundred visits, ninety regulars, and one chess league nobody planned. What happens when you just open the door.",
2246
+ "imageUrl": "p60fixture:service/warmspace",
2247
+ "authorName": "Amina Khan",
2248
+ "authorRole": "Community Editor",
2249
+ "categories": [
2250
+ "News"
2251
+ ],
2252
+ "publishedAt": "2030-01-08T09:00:00Z",
2253
+ "readingMinutes": 5,
2254
+ "href": "/articles/warm-spaces-in-numbers"
2255
+ }
2256
+ ],
2257
+ "campaigns": [
2258
+ {
2259
+ "id": "99999999-9999-9999-9999-999999999991",
2260
+ "title": "Keep the 27 Bus",
2261
+ "href": "/campaigns/keep-the-27-bus",
2262
+ "summary": "The only bus serving the Fenwick estate is set to be cut. Add your name before the consultation closes.",
2263
+ "imageUrl": "p60fixture:campaign/bus"
2264
+ },
2265
+ {
2266
+ "id": "99999999-9999-9999-9999-999999999992",
2267
+ "title": "A Defibrillator on Every Street",
2268
+ "href": "/campaigns/defibrillator-every-street",
2269
+ "summary": "We are raising for twelve more public defibrillators and training two hundred neighbours to use them.",
2270
+ "imageUrl": "p60fixture:campaign/defib"
2271
+ },
2272
+ {
2273
+ "id": "99999999-9999-9999-9999-999999999993",
2274
+ "title": "Alderford Housing Survey",
2275
+ "href": "/campaigns/housing-survey",
2276
+ "summary": null,
2277
+ "imageUrl": null
2278
+ }
2279
+ ],
2280
+ "causes": [
2281
+ {
2282
+ "id": "55555555-5555-5555-5555-555555555550",
2283
+ "title": "Flood Response Appeal",
2284
+ "slug": "flood-response",
2285
+ "description": "The Aldern broke its banks on Sunday. Thirty households need dry bedding, dehumidifiers and somewhere warm now.",
2286
+ "imageUrl": "p60fixture:cause/flood",
2287
+ "targetAmount": 50000,
2288
+ "raisedAmount": 18750,
2289
+ "progressPercentage": 38,
2290
+ "href": "/donate/flood-response",
2291
+ "paymentOptions": {
2292
+ "oneOff": true,
2293
+ "recurringCard": true,
2294
+ "directDebit": true
2295
+ }
2296
+ },
2297
+ {
2298
+ "id": "55555555-5555-5555-5555-555555555551",
2299
+ "title": "Winter Appeal",
2300
+ "slug": "winter-appeal",
2301
+ "description": "Heating top-ups, warm bedding and a hot meal for the households the cold hits hardest.",
2302
+ "imageUrl": "p60fixture:cause/winter",
2303
+ "targetAmount": 20000,
2304
+ "raisedAmount": 12500,
2305
+ "progressPercentage": 63,
2306
+ "href": "/donate/winter-appeal",
2307
+ "paymentOptions": {
2308
+ "oneOff": true,
2309
+ "recurringCard": true,
2310
+ "directDebit": false
2311
+ }
2312
+ },
2313
+ {
2314
+ "id": "55555555-5555-5555-5555-555555555552",
2315
+ "title": "Food Bank Fund",
2316
+ "slug": "food-bank-fund",
2317
+ "description": "Keeps the shelves stocked between donation drives. Ten pounds fills a family parcel.",
2318
+ "imageUrl": "p60fixture:cause/foodbank",
2319
+ "targetAmount": 15000,
2320
+ "raisedAmount": 9800,
2321
+ "progressPercentage": 65,
2322
+ "href": "/donate/food-bank-fund",
2323
+ "paymentOptions": {
2324
+ "oneOff": true,
2325
+ "recurringCard": true,
2326
+ "directDebit": true
2327
+ }
2328
+ },
2329
+ {
2330
+ "id": "55555555-5555-5555-5555-555555555553",
2331
+ "title": "Youth Club Futures",
2332
+ "slug": "youth-club-futures",
2333
+ "description": "Coaching, kit and trips for the seventy young people who come through our doors each week.",
2334
+ "imageUrl": "p60fixture:cause/youth",
2335
+ "targetAmount": 8000,
2336
+ "raisedAmount": 2150,
2337
+ "progressPercentage": 27,
2338
+ "href": "/donate/youth-club-futures",
2339
+ "paymentOptions": {
2340
+ "oneOff": true,
2341
+ "recurringCard": false,
2342
+ "directDebit": false
2343
+ }
2344
+ }
2345
+ ],
2346
+ "courses": [
2347
+ {
2348
+ "id": "33333333-3333-3333-3333-333333333333",
2349
+ "title": "Junior Athletics",
2350
+ "description": "Weekly athletics for children aged 7 to 12, coached by qualified volunteers at the King's Park track.",
2351
+ "imageUrl": "p60fixture:course/athletics",
2352
+ "venue": "King's Park track",
2353
+ "audience": "CHILD",
2354
+ "priceFrom": 15.0,
2355
+ "currency": "GBP",
2356
+ "soldOut": false,
2357
+ "href": "/courses"
2358
+ }
2359
+ ],
2360
+ "volunteering": [
2361
+ {
2362
+ "id": "aaaaaaaa-1111-4111-8111-111111111111",
2363
+ "slug": "garden-volunteer",
2364
+ "title": "Garden Volunteer",
2365
+ "summary": "Keep the Foundry Lane beds thriving: sowing, watering, and showing first-timers where the trowels live.",
2366
+ "imageUrl": "p60fixture:volunteer/garden",
2367
+ "content": {
2368
+ "location": "Foundry Lane Garden",
2369
+ "schedule": "Saturday mornings",
2370
+ "commitment": "Twice a month",
2371
+ "hoursExpected": "3 hours"
2372
+ }
2373
+ },
2374
+ {
2375
+ "id": "aaaaaaaa-1111-4111-8111-111111111112",
2376
+ "slug": "kitchen-crew",
2377
+ "title": "Wednesday Kitchen Crew",
2378
+ "summary": "Help cook and serve the weekly community meal. Full food hygiene training provided.",
2379
+ "imageUrl": "p60fixture:volunteer/kitchen",
2380
+ "content": {
2381
+ "location": "St Chad's Hall",
2382
+ "schedule": "Wednesdays 9am to 2pm",
2383
+ "commitment": "Weekly",
2384
+ "hoursExpected": "5 hours"
2385
+ }
2386
+ },
2387
+ {
2388
+ "id": "aaaaaaaa-1111-4111-8111-111111111113",
2389
+ "slug": "food-bank-driver",
2390
+ "title": "Food Bank Driver",
2391
+ "summary": "Collect supermarket surplus on the morning round. A full licence and a friendly wave required.",
2392
+ "imageUrl": null,
2393
+ "content": {
2394
+ "location": "Foundry Lane depot",
2395
+ "schedule": "Weekday mornings",
2396
+ "commitment": "One morning a week",
2397
+ "hoursExpected": "2 hours"
2398
+ }
2399
+ }
2400
+ ],
2401
+ "media": [
2402
+ {
2403
+ "id": "bbbbbbbb-2222-4222-8222-222222222221",
2404
+ "slug": "garden-opening-film",
2405
+ "title": "The Garden Opening, on Film",
2406
+ "summary": "Four minutes from the gates opening, shot by the youth club media group.",
2407
+ "imageUrl": "p60fixture:media/gardenfilm",
2408
+ "content": {
2409
+ "mediaType": "VIDEO",
2410
+ "mediaUrl": "/fixture/garden-opening",
2411
+ "speaker": null,
2412
+ "duration": "4 minutes"
2413
+ }
2414
+ },
2415
+ {
2416
+ "id": "bbbbbbbb-2222-4222-8222-222222222222",
2417
+ "slug": "trustee-talk-2030",
2418
+ "title": "Where the Trust Goes Next",
2419
+ "summary": "Chair of trustees Priya Sharma on the three-year plan, recorded at the spring members' evening.",
2420
+ "imageUrl": "p60fixture:media/talk",
2421
+ "content": {
2422
+ "mediaType": "VIDEO",
2423
+ "mediaUrl": "/fixture/trustee-talk",
2424
+ "speaker": "Priya Sharma",
2425
+ "duration": "18 minutes"
2426
+ }
2427
+ },
2428
+ {
2429
+ "id": "bbbbbbbb-2222-4222-8222-222222222223",
2430
+ "slug": "sound-of-the-kitchen",
2431
+ "title": "Sound of the Kitchen",
2432
+ "summary": "An audio portrait of Wednesday lunchtime, best enjoyed hungry.",
2433
+ "imageUrl": null,
2434
+ "content": {
2435
+ "mediaType": "AUDIO",
2436
+ "mediaUrl": "/fixture/kitchen-audio",
2437
+ "speaker": null,
2438
+ "duration": "9 minutes"
2439
+ }
2440
+ }
2441
+ ],
2442
+ "resources": [
2443
+ {
2444
+ "id": "88888888-8888-8888-8888-888888888881",
2445
+ "title": "Annual report 2029.pdf",
2446
+ "folder": "Reports",
2447
+ "url": "/fixture/annual-report-2029.pdf",
2448
+ "contentType": "application/pdf",
2449
+ "bytes": 1245760,
2450
+ "publishedAt": "2030-02-01T10:00:00Z"
2451
+ },
2452
+ {
2453
+ "id": "88888888-8888-8888-8888-888888888882",
2454
+ "title": "Safeguarding policy.pdf",
2455
+ "folder": "Policies",
2456
+ "url": "/fixture/safeguarding-policy.pdf",
2457
+ "contentType": "application/pdf",
2458
+ "bytes": 384000,
2459
+ "publishedAt": "2029-11-12T10:00:00Z"
2460
+ },
2461
+ {
2462
+ "id": "88888888-8888-8888-8888-888888888883",
2463
+ "title": "Volunteer handbook.pdf",
2464
+ "folder": "Volunteering",
2465
+ "url": "/fixture/volunteer-handbook.pdf",
2466
+ "contentType": "application/pdf",
2467
+ "bytes": 942080,
2468
+ "publishedAt": "2030-01-20T10:00:00Z"
2469
+ },
2470
+ {
2471
+ "id": "88888888-8888-8888-8888-888888888884",
2472
+ "title": "Hall hire form.pdf",
2473
+ "folder": "Forms",
2474
+ "url": "/fixture/hall-hire-form.pdf",
2475
+ "contentType": "application/pdf",
2476
+ "bytes": 128000,
2477
+ "publishedAt": "2029-09-02T10:00:00Z"
2478
+ }
2479
+ ],
2480
+ "locations": [
2481
+ {
2482
+ "id": "99999999-9999-9999-9999-999999999901",
2483
+ "name": "Foundry Lane Hub",
2484
+ "address": "14 Foundry Lane, Alderford AL2 4RS",
2485
+ "latitude": 53.8008,
2486
+ "longitude": -1.5491,
2487
+ "primary": true,
2488
+ "directionsHref": "https://www.google.com/maps/dir/?api=1&destination=Foundry%20Lane%20Hub"
2489
+ },
2490
+ {
2491
+ "id": "99999999-9999-9999-9999-999999999902",
2492
+ "name": "St Chad's Hall",
2493
+ "address": "Church Walk, Alderford AL1 2JW",
2494
+ "latitude": 53.7975,
2495
+ "longitude": -1.5562,
2496
+ "primary": false,
2497
+ "directionsHref": "https://www.google.com/maps/dir/?api=1&destination=St%20Chad%27s%20Hall"
2498
+ }
2499
+ ],
2500
+ "schedules": [
2501
+ {
2502
+ "type": "worship",
2503
+ "content": {
2504
+ "times": [
2505
+ {
2506
+ "name": "Fajr",
2507
+ "begins": "03:20",
2508
+ "congregation": "04:00"
2509
+ },
2510
+ {
2511
+ "name": "Zuhr",
2512
+ "begins": "13:11",
2513
+ "congregation": "13:30"
2514
+ },
2515
+ {
2516
+ "name": "Asr",
2517
+ "begins": "18:30",
2518
+ "congregation": "19:00"
2519
+ },
2520
+ {
2521
+ "name": "Maghrib",
2522
+ "begins": "21:04",
2523
+ "congregation": "21:04"
2524
+ },
2525
+ {
2526
+ "name": "Isha",
2527
+ "begins": "22:12",
2528
+ "congregation": "22:30"
2529
+ }
2530
+ ],
2531
+ "beginsLabel": "Begins",
2532
+ "congregationLabel": "Jama'at",
2533
+ "note": "Jum'a 13:15 & 14:15",
2534
+ "href": "/services?service=prayer-times",
2535
+ "today": "24 July 2026",
2536
+ "todayAlt": "11 Safar 1448",
2537
+ "next": {
2538
+ "name": "Asr",
2539
+ "begins": "18:30",
2540
+ "congregation": "19:00",
2541
+ "target": "2030-07-24T19:00:00+01:00"
2542
+ },
2543
+ "jumah": [
2544
+ {
2545
+ "name": "Jum'a 1",
2546
+ "begins": null,
2547
+ "congregation": "13:15"
2548
+ },
2549
+ {
2550
+ "name": "Jum'a 2",
2551
+ "begins": null,
2552
+ "congregation": "14:15"
2553
+ }
2554
+ ],
2555
+ "hijri": {
2556
+ "display": "11 Safar 1448"
2557
+ },
2558
+ "observances": [
2559
+ {
2560
+ "name": "Community iftar",
2561
+ "date": "2 March 2030",
2562
+ "href": "/events"
2563
+ }
2564
+ ]
2565
+ }
2566
+ }
2567
+ ],
2568
+ "about": []
1177
2569
  }
1178
2570
  }
1179
2571
  }