@port60/template-kit 0.9.0 → 0.11.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 +52 -24
  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 +165 -0
  16. package/src/vendor/contract/v1/context.json +1497 -289
  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 +242 -235
  21. package/src/vendor/contract/v1/layout.json +4 -4
  22. package/src/vendor/contract/v1/manifest.schema.json +10 -12
  23. package/src/vendor/contract/v1/sections.json +189 -107
  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 +107 -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 +144 -59
  31. package/src/vendor/validator/site-context.mjs +97 -0
  32. package/src/vendor/validator/validate.mjs +70 -17
  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, 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,208 @@
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."
321
- },
322
- {
323
- "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."
326
- },
327
- {
328
- "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."
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
331
432
  },
332
433
  {
333
434
  "path": "infoEvents",
334
435
  "availableIn": [
335
436
  "section:whatsOn"
336
437
  ],
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)."
438
+ "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).",
439
+ "deprecated": true
338
440
  },
339
441
  {
340
442
  "path": "latestArticles",
341
443
  "availableIn": [
342
444
  "section:articles"
343
445
  ],
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."
446
+ "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.",
447
+ "deprecated": true
345
448
  },
346
449
  {
347
450
  "path": "latestArticles[].title",
348
451
  "availableIn": [
349
452
  "section:articles"
350
453
  ],
351
- "description": "The article's title."
454
+ "description": "The article's title.",
455
+ "deprecated": true
352
456
  },
353
457
  {
354
458
  "path": "latestArticles[].href",
355
459
  "availableIn": [
356
460
  "section:articles"
357
461
  ],
358
- "description": "The canonical link to the article — always use this."
462
+ "description": "The canonical link to the article, always use this.",
463
+ "deprecated": true
359
464
  },
360
465
  {
361
466
  "path": "latestArticles[].excerpt",
362
467
  "availableIn": [
363
468
  "section:articles"
364
469
  ],
365
- "description": "Short summary, or null — branch on it."
470
+ "description": "Short summary, or null, branch on it.",
471
+ "deprecated": true
366
472
  },
367
473
  {
368
474
  "path": "latestArticles[].imageUrl",
369
475
  "availableIn": [
370
476
  "section:articles"
371
477
  ],
372
- "description": "Cover image URL, or null — always branch."
478
+ "description": "Cover image URL, or null, always branch.",
479
+ "deprecated": true
373
480
  },
374
481
  {
375
482
  "path": "latestArticles[].publishedAt",
376
483
  "availableIn": [
377
484
  "section:articles"
378
485
  ],
379
- "description": "ISO-8601 publish time, or null — format with the date filter."
486
+ "description": "ISO-8601 publish time, or null, format with the date filter.",
487
+ "deprecated": true
380
488
  },
381
489
  {
382
490
  "path": "latestArticles[].readingMinutes",
383
491
  "availableIn": [
384
492
  "section:articles"
385
493
  ],
386
- "description": "Estimated reading minutes, or null."
494
+ "description": "Estimated reading minutes, or null.",
495
+ "deprecated": true
387
496
  },
388
497
  {
389
498
  "path": "latestArticles[].categories",
390
499
  "availableIn": [
391
500
  "section:articles"
392
501
  ],
393
- "description": "The article's category names (possibly empty) — treat as text."
502
+ "description": "The article's category names (possibly empty), treat as text.",
503
+ "deprecated": true
394
504
  },
395
505
  {
396
506
  "path": "campaigns",
397
507
  "availableIn": [
398
508
  "section:campaigns"
399
509
  ],
400
- "description": "The tenant's live campaigns, in display order. Empty when none are live — render nothing."
510
+ "description": "The tenant's live campaigns, in display order. Empty when none are live, render nothing.",
511
+ "deprecated": true
401
512
  },
402
513
  {
403
514
  "path": "campaigns[].title",
404
515
  "availableIn": [
405
516
  "section:campaigns"
406
517
  ],
407
- "description": "The campaign's name."
518
+ "description": "The campaign's name.",
519
+ "deprecated": true
408
520
  },
409
521
  {
410
522
  "path": "campaigns[].href",
411
523
  "availableIn": [
412
524
  "section:campaigns"
413
525
  ],
414
- "description": "The canonical link to the campaign page — always use this."
526
+ "description": "The canonical link to the campaign page, always use this.",
527
+ "deprecated": true
415
528
  },
416
529
  {
417
530
  "path": "campaigns[].summary",
418
531
  "availableIn": [
419
532
  "section:campaigns"
420
533
  ],
421
- "description": "Short summary, or null — branch on it."
534
+ "description": "Short summary, or null, branch on it.",
535
+ "deprecated": true
422
536
  },
423
537
  {
424
538
  "path": "campaigns[].imageUrl",
425
539
  "availableIn": [
426
540
  "section:campaigns"
427
541
  ],
428
- "description": "Featured image URL, or null — always branch."
542
+ "description": "Featured image URL, or null, always branch.",
543
+ "deprecated": true
429
544
  },
430
545
  {
431
546
  "path": "campaignsLabel",
432
547
  "availableIn": [
433
548
  "section:campaigns"
434
549
  ],
435
- "description": "What the tenant calls their campaigns section (\"Campaigns\" by default) — the heading fallback."
550
+ "description": "What the tenant calls their campaigns section (\"Campaigns\" by default), the heading fallback.",
551
+ "deprecated": true
436
552
  },
437
553
  {
438
554
  "path": "articles",
439
555
  "availableIn": [
440
556
  "page:articles"
441
557
  ],
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."
558
+ "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.",
559
+ "deprecated": true
443
560
  },
444
561
  {
445
562
  "path": "selectedCategory",
446
563
  "availableIn": [
447
564
  "page:articles"
448
565
  ],
449
- "description": "The active category filter, or null on an unfiltered listing."
566
+ "description": "The active category filter, or null on an unfiltered listing.",
567
+ "deprecated": true
450
568
  },
451
569
  {
452
570
  "path": "article",
453
571
  "availableIn": [
454
572
  "page:article"
455
573
  ],
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."
574
+ "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.",
575
+ "deprecated": true
457
576
  },
458
577
  {
459
578
  "path": "relatedArticles",
460
579
  "availableIn": [
461
580
  "page:article"
462
581
  ],
463
- "description": "Up to three other published article summaries, newest first."
582
+ "description": "Up to three other published article summaries, newest first.",
583
+ "deprecated": true
464
584
  },
465
585
  {
466
586
  "path": "categories",
@@ -468,216 +588,246 @@
468
588
  "page:articles",
469
589
  "page:article"
470
590
  ],
471
- "description": "Categories currently used by published articles. Each item has name, count and href."
591
+ "description": "Categories currently used by published articles. Each item has name, count and href.",
592
+ "deprecated": true
472
593
  },
473
594
  {
474
595
  "path": "events[].id",
475
596
  "availableIn": [
476
597
  "page:events"
477
598
  ],
478
- "description": "Stable event id."
599
+ "description": "Stable event id.",
600
+ "deprecated": true
479
601
  },
480
602
  {
481
603
  "path": "events[].name",
482
604
  "availableIn": [
483
605
  "page:events"
484
606
  ],
485
- "description": "The event's name."
607
+ "description": "The event's name.",
608
+ "deprecated": true
486
609
  },
487
610
  {
488
611
  "path": "events[].description",
489
612
  "availableIn": [
490
613
  "page:events"
491
614
  ],
492
- "description": "Plain-text description, or null — branch on it."
615
+ "description": "Plain-text description, or null, branch on it.",
616
+ "deprecated": true
493
617
  },
494
618
  {
495
619
  "path": "events[].registrationMode",
496
620
  "availableIn": [
497
621
  "page:events"
498
622
  ],
499
- "description": "INFO (no sign-up), RSVP (free sign-up) or TICKETED (paid)."
623
+ "description": "INFO (no sign-up), RSVP (free sign-up) or TICKETED (paid).",
624
+ "deprecated": true
500
625
  },
501
626
  {
502
627
  "path": "events[].startsAt",
503
628
  "availableIn": [
504
629
  "page:events"
505
630
  ],
506
- "description": "ISO-8601 start — format with the date filter."
631
+ "description": "ISO-8601 start, format with the date filter.",
632
+ "deprecated": true
507
633
  },
508
634
  {
509
635
  "path": "events[].endsAt",
510
636
  "availableIn": [
511
637
  "page:events"
512
638
  ],
513
- "description": "ISO-8601 end, or null."
639
+ "description": "ISO-8601 end, or null.",
640
+ "deprecated": true
514
641
  },
515
642
  {
516
643
  "path": "events[].venueName",
517
644
  "availableIn": [
518
645
  "page:events"
519
646
  ],
520
- "description": "Venue name, or null (online events may have none)."
647
+ "description": "Venue name, or null (online events may have none).",
648
+ "deprecated": true
521
649
  },
522
650
  {
523
651
  "path": "events[].online",
524
652
  "availableIn": [
525
653
  "page:events"
526
654
  ],
527
- "description": "True for online events."
655
+ "description": "True for online events.",
656
+ "deprecated": true
528
657
  },
529
658
  {
530
659
  "path": "events[].imageUrl",
531
660
  "availableIn": [
532
661
  "page:events"
533
662
  ],
534
- "description": "Promo image URL, or null — always branch."
663
+ "description": "Promo image URL, or null, always branch.",
664
+ "deprecated": true
535
665
  },
536
666
  {
537
667
  "path": "events[].onSale",
538
668
  "availableIn": [
539
669
  "page:events"
540
670
  ],
541
- "description": "False before sales open."
671
+ "description": "False before sales open.",
672
+ "deprecated": true
542
673
  },
543
674
  {
544
675
  "path": "events[].soldOut",
545
676
  "availableIn": [
546
677
  "page:events"
547
678
  ],
548
- "description": "True when no capacity remains."
679
+ "description": "True when no capacity remains.",
680
+ "deprecated": true
549
681
  },
550
682
  {
551
683
  "path": "events[].priceFrom",
552
684
  "availableIn": [
553
685
  "page:events"
554
686
  ],
555
- "description": "The cheapest ticket price (number), or null for free/INFO events."
687
+ "description": "The cheapest ticket price (number), or null for free/INFO events.",
688
+ "deprecated": true
556
689
  },
557
690
  {
558
691
  "path": "events[].currency",
559
692
  "availableIn": [
560
693
  "page:events"
561
694
  ],
562
- "description": "ISO currency code for priceFrom, or null."
695
+ "description": "ISO currency code for priceFrom, or null.",
696
+ "deprecated": true
563
697
  },
564
698
  {
565
699
  "path": "events[].detailHref",
566
700
  "availableIn": [
567
701
  "page:events"
568
702
  ],
569
- "description": "The canonical link to this event's detail view — always use this, never build URLs yourself."
703
+ "description": "The canonical link to this event's detail view, always use this, never build URLs yourself.",
704
+ "deprecated": true
570
705
  },
571
706
  {
572
707
  "path": "course",
573
708
  "availableIn": [
574
709
  "page:course"
575
710
  ],
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."
711
+ "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.",
712
+ "deprecated": true
577
713
  },
578
714
  {
579
715
  "path": "course.id",
580
716
  "availableIn": [
581
717
  "page:course"
582
718
  ],
583
- "description": "Stable course id."
719
+ "description": "Stable course id.",
720
+ "deprecated": true
584
721
  },
585
722
  {
586
723
  "path": "course.title",
587
724
  "availableIn": [
588
725
  "page:course"
589
726
  ],
590
- "description": "The course's title."
727
+ "description": "The course's title.",
728
+ "deprecated": true
591
729
  },
592
730
  {
593
731
  "path": "course.description",
594
732
  "availableIn": [
595
733
  "page:course"
596
734
  ],
597
- "description": "Plain-text summary, or null — branch on it. Prefer aboutHtml for the body when present."
735
+ "description": "Plain-text summary, or null, branch on it. Prefer aboutHtml for the body when present.",
736
+ "deprecated": true
598
737
  },
599
738
  {
600
739
  "path": "course.aboutHtml",
601
740
  "availableIn": [
602
741
  "page:course"
603
742
  ],
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."
743
+ "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.",
744
+ "deprecated": true
605
745
  },
606
746
  {
607
747
  "path": "course.imageUrl",
608
748
  "availableIn": [
609
749
  "page:course"
610
750
  ],
611
- "description": "The MAIN image (hero) URL, or null — always branch."
751
+ "description": "The MAIN image (hero) URL, or null, always branch.",
752
+ "deprecated": true
612
753
  },
613
754
  {
614
755
  "path": "course.venue",
615
756
  "availableIn": [
616
757
  "page:course"
617
758
  ],
618
- "description": "Venue display text, or null."
759
+ "description": "Venue display text, or null.",
760
+ "deprecated": true
619
761
  },
620
762
  {
621
763
  "path": "course.audience",
622
764
  "availableIn": [
623
765
  "page:course"
624
766
  ],
625
- "description": "EVERYONE, or CHILD for children-only courses (parents enrol their children)."
767
+ "description": "EVERYONE, or CHILD for children-only courses (parents enrol their children).",
768
+ "deprecated": true
626
769
  },
627
770
  {
628
771
  "path": "course.billingMode",
629
772
  "availableIn": [
630
773
  "page:course"
631
774
  ],
632
- "description": "UPFRONT (one payment), TERM (committed term fee), or ROLLING (monthly with notice)."
775
+ "description": "UPFRONT (one payment), TERM (committed term fee), or ROLLING (monthly with notice).",
776
+ "deprecated": true
633
777
  },
634
778
  {
635
779
  "path": "course.termStart",
636
780
  "availableIn": [
637
781
  "page:course"
638
782
  ],
639
- "description": "ISO date the term starts, or null — format with the date filter."
783
+ "description": "ISO date the term starts, or null, format with the date filter.",
784
+ "deprecated": true
640
785
  },
641
786
  {
642
787
  "path": "course.termEnd",
643
788
  "availableIn": [
644
789
  "page:course"
645
790
  ],
646
- "description": "ISO date the term ends, or null."
791
+ "description": "ISO date the term ends, or null.",
792
+ "deprecated": true
647
793
  },
648
794
  {
649
795
  "path": "course.currency",
650
796
  "availableIn": [
651
797
  "page:course"
652
798
  ],
653
- "description": "ISO currency code for priceFrom."
799
+ "description": "ISO currency code for priceFrom.",
800
+ "deprecated": true
654
801
  },
655
802
  {
656
803
  "path": "course.priceFrom",
657
804
  "availableIn": [
658
805
  "page:course"
659
806
  ],
660
- "description": "The cheapest priced option (number), or null when every option is free."
807
+ "description": "The cheapest priced option (number), or null when every option is free.",
808
+ "deprecated": true
661
809
  },
662
810
  {
663
811
  "path": "course.soldOut",
664
812
  "availableIn": [
665
813
  "page:course"
666
814
  ],
667
- "description": "True when no option has places left (the island shows the standard note)."
815
+ "description": "True when no option has places left (the island shows the standard note).",
816
+ "deprecated": true
668
817
  },
669
818
  {
670
819
  "path": "course.backHref",
671
820
  "availableIn": [
672
821
  "page:course"
673
822
  ],
674
- "description": "The canonical link back to the courses listing — always use this."
823
+ "description": "The canonical link back to the courses listing, always use this.",
824
+ "deprecated": true
675
825
  }
676
826
  ],
677
827
  "fixtures": {
678
828
  "brand": {
679
- "name": "Fixture Charity",
680
- "tagline": "A fixture tagline.",
829
+ "name": "The Lantern Trust",
830
+ "tagline": "Neighbours, looked after.",
681
831
  "logoUrl": null,
682
832
  "logoType": "full",
683
833
  "footerLogoUrl": null
@@ -740,28 +890,64 @@
740
890
  }
741
891
  ],
742
892
  "footer": [
743
- { "label": "About us", "href": "/about" },
744
- { "label": "Contact", "href": "https://example.org/contact", "external": true },
893
+ {
894
+ "label": "About us",
895
+ "href": "/about"
896
+ },
897
+ {
898
+ "label": "Contact",
899
+ "href": "https://example.org/contact",
900
+ "external": true
901
+ },
745
902
  {
746
903
  "label": "Our work",
747
904
  "href": "#",
748
905
  "children": [
749
- { "label": "Food bank", "href": "/services?service=food-bank" }
906
+ {
907
+ "label": "Food bank",
908
+ "href": "/services?service=food-bank"
909
+ }
750
910
  ]
751
911
  }
752
912
  ],
753
913
  "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 }
914
+ {
915
+ "label": "Home",
916
+ "href": "/",
917
+ "cta": false
918
+ },
919
+ {
920
+ "label": "About us",
921
+ "href": "/about",
922
+ "cta": false
923
+ },
924
+ {
925
+ "label": "Events",
926
+ "href": "/events",
927
+ "cta": false
928
+ },
929
+ {
930
+ "label": "Donate",
931
+ "href": "/donate",
932
+ "cta": true
933
+ }
758
934
  ]
759
935
  },
760
936
  "socials": [
761
937
  {
762
938
  "platform": "facebook",
763
- "url": "https://facebook.com/fixture",
939
+ "url": "https://facebook.com/lanterntrust",
764
940
  "iconSvg": "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/></svg>"
941
+ },
942
+ {
943
+ "platform": "instagram",
944
+ "url": "https://instagram.com/lanterntrust",
945
+ "iconSvg": "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"5\"/></svg>"
946
+ },
947
+ {
948
+ "platform": "youtube",
949
+ "url": "https://youtube.com/@lanterntrust",
950
+ "iconSvg": "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><polygon points=\"10,8 16,12 10,16\"/></svg>"
765
951
  }
766
952
  ],
767
953
  "worship": {
@@ -805,21 +991,47 @@
805
991
  "target": "2030-07-24T19:00:00+01:00"
806
992
  },
807
993
  "jumah": [
808
- { "name": "Jum'a 1", "begins": null, "congregation": "13:15" },
809
- { "name": "Jum'a 2", "begins": null, "congregation": "14:15" }
994
+ {
995
+ "name": "Jum'a 1",
996
+ "begins": null,
997
+ "congregation": "13:15"
998
+ },
999
+ {
1000
+ "name": "Jum'a 2",
1001
+ "begins": null,
1002
+ "congregation": "14:15"
1003
+ }
810
1004
  ],
811
- "hijri": { "display": "11 Safar 1448" },
1005
+ "hijri": {
1006
+ "display": "11 Safar 1448"
1007
+ },
812
1008
  "observances": [
813
- { "name": "Community iftar", "date": "2 March 2030", "href": "/events" }
1009
+ {
1010
+ "name": "Community iftar",
1011
+ "date": "2 March 2030",
1012
+ "href": "/events"
1013
+ }
814
1014
  ]
815
1015
  },
816
1016
  "locale": {
817
1017
  "code": "en",
818
1018
  "direction": "ltr",
819
1019
  "languages": [
820
- { "code": "en", "label": "English", "direction": "ltr" },
821
- { "code": "cy", "label": "Cymraeg", "direction": "ltr" },
822
- { "code": "ar", "label": "العربية", "direction": "rtl" }
1020
+ {
1021
+ "code": "en",
1022
+ "label": "English",
1023
+ "direction": "ltr"
1024
+ },
1025
+ {
1026
+ "code": "cy",
1027
+ "label": "Cymraeg",
1028
+ "direction": "ltr"
1029
+ },
1030
+ {
1031
+ "code": "ar",
1032
+ "label": "العربية",
1033
+ "direction": "rtl"
1034
+ }
823
1035
  ]
824
1036
  }
825
1037
  },
@@ -827,52 +1039,100 @@
827
1039
  "events": {
828
1040
  "events": [
829
1041
  {
830
- "id": "11111111-1111-1111-1111-111111111111",
831
- "name": "Summer Gala",
832
- "description": "An evening of music and giving.",
1042
+ "id": "22222222-2222-2222-2222-222222222222",
1043
+ "name": "The Lantern Gala",
1044
+ "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
1045
  "registrationMode": "TICKETED",
834
- "startsAt": "2030-08-15T18:00:00Z",
1046
+ "startsAt": "2030-09-20T19:00:00Z",
835
1047
  "endsAt": null,
836
- "venueName": "Town Hall",
1048
+ "venueName": "The Maltings, Alderford",
837
1049
  "online": false,
838
- "imageUrl": null,
1050
+ "imageUrl": "p60fixture:event/gala",
839
1051
  "onSale": true,
840
1052
  "soldOut": false,
841
- "priceFrom": 10.0,
1053
+ "priceFrom": 25.0,
842
1054
  "currency": "GBP",
843
- "detailHref": "/events?event=11111111-1111-1111-1111-111111111111"
1055
+ "detailHref": "/events?event=22222222-2222-2222-2222-222222222222"
844
1056
  },
845
1057
  {
846
- "id": "22222222-2222-2222-2222-222222222222",
847
- "name": "Coffee Morning",
848
- "description": null,
1058
+ "id": "44444444-4444-4444-4444-444444444444",
1059
+ "name": "Riverside Harvest Supper",
1060
+ "description": "A long-table supper on the Aldern meadow, cooked by our kitchen volunteers with produce from the community garden.",
1061
+ "registrationMode": "TICKETED",
1062
+ "startsAt": "2030-10-04T18:30:00Z",
1063
+ "endsAt": "2030-10-04T22:00:00Z",
1064
+ "venueName": "Aldern Meadow",
1065
+ "online": false,
1066
+ "imageUrl": "p60fixture:event/harvest",
1067
+ "onSale": true,
1068
+ "soldOut": false,
1069
+ "priceFrom": 12.5,
1070
+ "currency": "GBP",
1071
+ "detailHref": "/events?event=44444444-4444-4444-4444-444444444444"
1072
+ },
1073
+ {
1074
+ "id": "55555555-5555-5555-5555-555555555555",
1075
+ "name": "Volunteer Welcome Evening",
1076
+ "description": "Meet the team, hear where help is needed most this season, and find a role that fits the time you have.",
849
1077
  "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,
1078
+ "startsAt": "2030-09-03T18:00:00Z",
1079
+ "endsAt": "2030-09-03T19:30:00Z",
1080
+ "venueName": "Foundry Lane Hub",
1081
+ "online": false,
1082
+ "imageUrl": "p60fixture:event/welcome",
855
1083
  "onSale": true,
856
- "soldOut": true,
1084
+ "soldOut": false,
857
1085
  "priceFrom": null,
858
1086
  "currency": null,
859
- "detailHref": "/events?event=22222222-2222-2222-2222-222222222222"
1087
+ "detailHref": "/events?event=55555555-5555-5555-5555-555555555555"
860
1088
  },
861
1089
  {
862
- "id": "33333333-3333-3333-3333-333333333333",
863
- "name": "Open Day",
864
- "description": "Drop in and meet the team.",
1090
+ "id": "66666666-6666-6666-6666-666666666666",
1091
+ "name": "Warm Space Wednesdays",
1092
+ "description": "Our hall is open, warm and stocked with tea every Wednesday through the cold months. No booking, no questions, everyone welcome.",
865
1093
  "registrationMode": "INFO",
866
- "startsAt": "2030-09-01T09:00:00Z",
867
- "endsAt": null,
868
- "venueName": "Community Centre",
1094
+ "startsAt": "2030-11-06T10:00:00Z",
1095
+ "endsAt": "2030-11-06T15:00:00Z",
1096
+ "venueName": "St Chad's Hall",
869
1097
  "online": false,
870
1098
  "imageUrl": null,
871
1099
  "onSale": true,
872
1100
  "soldOut": false,
873
1101
  "priceFrom": null,
874
1102
  "currency": null,
875
- "detailHref": "/events?event=33333333-3333-3333-3333-333333333333"
1103
+ "detailHref": "/events?event=66666666-6666-6666-6666-666666666666"
1104
+ },
1105
+ {
1106
+ "id": "77777777-7777-7777-7777-777777777777",
1107
+ "name": "Money Advice Drop-in",
1108
+ "description": "A quiet hour with our trained advisers. Bring letters you are worried about; we will work through them together.",
1109
+ "registrationMode": "INFO",
1110
+ "startsAt": "2030-09-12T10:00:00Z",
1111
+ "endsAt": "2030-09-12T12:00:00Z",
1112
+ "venueName": null,
1113
+ "online": true,
1114
+ "imageUrl": null,
1115
+ "onSale": true,
1116
+ "soldOut": false,
1117
+ "priceFrom": null,
1118
+ "currency": null,
1119
+ "detailHref": "/events?event=77777777-7777-7777-7777-777777777777"
1120
+ },
1121
+ {
1122
+ "id": "88888888-8888-8888-8888-888888888888",
1123
+ "name": "Winter Fair",
1124
+ "description": "Stalls, carols and the grand raffle in aid of the Winter Appeal. Free entry; every stall staffed by volunteers.",
1125
+ "registrationMode": "TICKETED",
1126
+ "startsAt": "2030-12-07T11:00:00Z",
1127
+ "endsAt": "2030-12-07T16:00:00Z",
1128
+ "venueName": "Alderford Market Square",
1129
+ "online": false,
1130
+ "imageUrl": "p60fixture:event/winterfair",
1131
+ "onSale": true,
1132
+ "soldOut": true,
1133
+ "priceFrom": 0.0,
1134
+ "currency": "GBP",
1135
+ "detailHref": "/events?event=88888888-8888-8888-8888-888888888888"
876
1136
  }
877
1137
  ]
878
1138
  },
@@ -880,9 +1140,9 @@
880
1140
  "course": {
881
1141
  "id": "33333333-3333-3333-3333-333333333333",
882
1142
  "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,
1143
+ "description": "Weekly athletics for children aged 7 to 12, coached by qualified volunteers at the King's Park track.",
1144
+ "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>",
1145
+ "imageUrl": "p60fixture:course/athletics",
886
1146
  "venue": "King's Park track",
887
1147
  "audience": "CHILD",
888
1148
  "billingMode": "ROLLING",
@@ -897,70 +1157,212 @@
897
1157
  "articles": {
898
1158
  "selectedCategory": null,
899
1159
  "categories": [
900
- { "name": "News", "count": 4, "href": "/articles/all?category=News" },
901
- { "name": "Stories", "count": 2, "href": "/articles/all?category=Stories" }
1160
+ {
1161
+ "name": "News",
1162
+ "count": 3,
1163
+ "href": "/articles/all?category=News"
1164
+ },
1165
+ {
1166
+ "name": "Stories",
1167
+ "count": 2,
1168
+ "href": "/articles/all?category=Stories"
1169
+ },
1170
+ {
1171
+ "name": "Guides",
1172
+ "count": 1,
1173
+ "href": "/articles/all?category=Guides"
1174
+ }
902
1175
  ],
903
1176
  "articles": [
904
1177
  {
905
1178
  "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,
1179
+ "title": "The Community Garden Opens Its Gates",
1180
+ "slug": "community-garden-opens",
1181
+ "excerpt": "Two years of digging, fundraising and Saturday mornings in the rain: the Foundry Lane garden is finally open to everyone.",
1182
+ "imageUrl": "p60fixture:article/garden",
910
1183
  "authorName": "Amina Khan",
911
1184
  "authorRole": "Community Editor",
912
- "categories": ["News"],
1185
+ "categories": [
1186
+ "News"
1187
+ ],
913
1188
  "publishedAt": "2030-04-12T09:00:00Z",
1189
+ "readingMinutes": 5,
1190
+ "viewCount": 412,
1191
+ "likeCount": 38,
1192
+ "commentCount": 9,
1193
+ "href": "/articles/community-garden-opens"
1194
+ },
1195
+ {
1196
+ "id": "99999999-9999-9999-9999-999999999999",
1197
+ "title": "What a Food Parcel Actually Holds",
1198
+ "slug": "what-a-food-parcel-holds",
1199
+ "excerpt": "People imagine tins and pasta. The real list is longer, more personal, and put together with more care than you might think.",
1200
+ "imageUrl": "p60fixture:article/parcel",
1201
+ "authorName": "Derek Osei",
1202
+ "authorRole": "Food Bank Lead",
1203
+ "categories": [
1204
+ "Stories"
1205
+ ],
1206
+ "publishedAt": "2030-03-28T09:00:00Z",
1207
+ "readingMinutes": 6,
1208
+ "viewCount": 655,
1209
+ "likeCount": 61,
1210
+ "commentCount": 14,
1211
+ "href": "/articles/what-a-food-parcel-holds"
1212
+ },
1213
+ {
1214
+ "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
1215
+ "title": "Meet the Wednesday Kitchen Crew",
1216
+ "slug": "wednesday-kitchen-crew",
1217
+ "excerpt": "Eleven volunteers, one borrowed oven, and a hundred and forty hot meals a week. The kitchen crew tell it in their own words.",
1218
+ "imageUrl": "p60fixture:people/kitchen",
1219
+ "authorName": "Amina Khan",
1220
+ "authorRole": "Community Editor",
1221
+ "categories": [
1222
+ "Stories"
1223
+ ],
1224
+ "publishedAt": "2030-03-02T09:00:00Z",
1225
+ "readingMinutes": 7,
1226
+ "viewCount": 388,
1227
+ "likeCount": 45,
1228
+ "commentCount": 11,
1229
+ "href": "/articles/wednesday-kitchen-crew"
1230
+ },
1231
+ {
1232
+ "id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
1233
+ "title": "Winter Appeal: Where Your Money Went",
1234
+ "slug": "winter-appeal-report",
1235
+ "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.",
1236
+ "imageUrl": "p60fixture:campaign/winterreport",
1237
+ "authorName": "Priya Sharma",
1238
+ "authorRole": "Trustee",
1239
+ "categories": [
1240
+ "News"
1241
+ ],
1242
+ "publishedAt": "2030-02-14T09:00:00Z",
1243
+ "readingMinutes": 8,
1244
+ "viewCount": 902,
1245
+ "likeCount": 84,
1246
+ "commentCount": 21,
1247
+ "href": "/articles/winter-appeal-report"
1248
+ },
1249
+ {
1250
+ "id": "cccccccc-cccc-cccc-cccc-cccccccccccc",
1251
+ "title": "Five Things Our Money Advisers Wish You Knew",
1252
+ "slug": "money-advice-five-things",
1253
+ "excerpt": "The advice team see the same worries again and again. Most of them have a fix that takes less than an hour.",
1254
+ "imageUrl": null,
1255
+ "authorName": "Tom Bradley",
1256
+ "authorRole": "Advice Service",
1257
+ "categories": [
1258
+ "Guides"
1259
+ ],
1260
+ "publishedAt": "2030-01-30T09:00:00Z",
914
1261
  "readingMinutes": 4,
915
- "viewCount": 82,
916
- "likeCount": 12,
917
- "commentCount": 3,
918
- "href": "/articles/fixture-community-garden-opens"
1262
+ "viewCount": 1204,
1263
+ "likeCount": 96,
1264
+ "commentCount": 17,
1265
+ "href": "/articles/money-advice-five-things"
1266
+ },
1267
+ {
1268
+ "id": "dddddddd-dddd-dddd-dddd-dddddddddddd",
1269
+ "title": "A Year of Warm Spaces, in Numbers",
1270
+ "slug": "warm-spaces-in-numbers",
1271
+ "excerpt": "Fourteen hundred visits, ninety regulars, and one chess league nobody planned. What happens when you just open the door.",
1272
+ "imageUrl": "p60fixture:service/warmspace",
1273
+ "authorName": "Amina Khan",
1274
+ "authorRole": "Community Editor",
1275
+ "categories": [
1276
+ "News"
1277
+ ],
1278
+ "publishedAt": "2030-01-08T09:00:00Z",
1279
+ "readingMinutes": 5,
1280
+ "viewCount": 517,
1281
+ "likeCount": 42,
1282
+ "commentCount": 8,
1283
+ "href": "/articles/warm-spaces-in-numbers"
919
1284
  }
920
1285
  ]
921
1286
  },
922
1287
  "article": {
923
1288
  "article": {
924
1289
  "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,
1290
+ "title": "The Community Garden Opens Its Gates",
1291
+ "slug": "community-garden-opens",
1292
+ "excerpt": "Two years of digging, fundraising and Saturday mornings in the rain: the Foundry Lane garden is finally open to everyone.",
1293
+ "imageUrl": "p60fixture:article/garden",
929
1294
  "authorName": "Amina Khan",
930
1295
  "authorRole": "Community Editor",
931
- "categories": ["News"],
1296
+ "categories": [
1297
+ "News"
1298
+ ],
932
1299
  "publishedAt": "2030-04-12T09:00:00Z",
1300
+ "readingMinutes": 5,
1301
+ "viewCount": 412,
1302
+ "likeCount": 38,
1303
+ "commentCount": 9,
1304
+ "href": "/articles/community-garden-opens",
933
1305
  "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>",
1306
+ "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
1307
  "allowComments": true
941
1308
  },
942
1309
  "relatedArticles": [
943
1310
  {
944
1311
  "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"
1312
+ "title": "What a Food Parcel Actually Holds",
1313
+ "slug": "what-a-food-parcel-holds",
1314
+ "excerpt": "People imagine tins and pasta. The real list is longer, more personal, and put together with more care than you might think.",
1315
+ "imageUrl": "p60fixture:article/parcel",
1316
+ "authorName": "Derek Osei",
1317
+ "authorRole": "Food Bank Lead",
1318
+ "categories": [
1319
+ "Stories"
1320
+ ],
1321
+ "publishedAt": "2030-03-28T09:00:00Z",
1322
+ "readingMinutes": 6,
1323
+ "viewCount": 655,
1324
+ "likeCount": 61,
1325
+ "commentCount": 14,
1326
+ "href": "/articles/what-a-food-parcel-holds"
1327
+ },
1328
+ {
1329
+ "id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
1330
+ "title": "Winter Appeal: Where Your Money Went",
1331
+ "slug": "winter-appeal-report",
1332
+ "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.",
1333
+ "imageUrl": "p60fixture:campaign/winterreport",
1334
+ "authorName": "Priya Sharma",
1335
+ "authorRole": "Trustee",
1336
+ "categories": [
1337
+ "News"
1338
+ ],
1339
+ "publishedAt": "2030-02-14T09:00:00Z",
1340
+ "readingMinutes": 8,
1341
+ "viewCount": 902,
1342
+ "likeCount": 84,
1343
+ "commentCount": 21,
1344
+ "href": "/articles/winter-appeal-report"
1345
+ },
1346
+ {
1347
+ "id": "dddddddd-dddd-dddd-dddd-dddddddddddd",
1348
+ "title": "A Year of Warm Spaces, in Numbers",
1349
+ "slug": "warm-spaces-in-numbers",
1350
+ "excerpt": "Fourteen hundred visits, ninety regulars, and one chess league nobody planned. What happens when you just open the door.",
1351
+ "imageUrl": "p60fixture:service/warmspace",
1352
+ "authorName": "Amina Khan",
1353
+ "authorRole": "Community Editor",
1354
+ "categories": [
1355
+ "News"
1356
+ ],
1357
+ "publishedAt": "2030-01-08T09:00:00Z",
1358
+ "readingMinutes": 5,
1359
+ "viewCount": 517,
1360
+ "likeCount": 42,
1361
+ "commentCount": 8,
1362
+ "href": "/articles/warm-spaces-in-numbers"
958
1363
  }
959
1364
  ],
960
- "categories": [
961
- { "name": "News", "count": 4, "href": "/articles/all?category=News" },
962
- { "name": "Stories", "count": 2, "href": "/articles/all?category=Stories" }
963
- ]
1365
+ "backHref": "/articles"
964
1366
  }
965
1367
  },
966
1368
  "sections": {
@@ -968,97 +1370,174 @@
968
1370
  "events": [
969
1371
  {
970
1372
  "id": "22222222-2222-2222-2222-222222222222",
971
- "name": "Fixture Lantern Gala",
972
- "description": "An evening fixture event.",
1373
+ "name": "The Lantern Gala",
1374
+ "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
1375
  "registrationMode": "TICKETED",
974
1376
  "startsAt": "2030-09-20T19:00:00Z",
975
1377
  "endsAt": null,
976
- "venueName": "Fixture Hall",
1378
+ "venueName": "The Maltings, Alderford",
977
1379
  "online": false,
978
- "imageUrl": null,
1380
+ "imageUrl": "p60fixture:event/gala",
979
1381
  "onSale": true,
980
1382
  "soldOut": false,
981
- "priceFrom": 12.5,
1383
+ "priceFrom": 25.0,
982
1384
  "currency": "GBP",
983
1385
  "detailHref": "/events?event=22222222-2222-2222-2222-222222222222"
984
1386
  },
985
1387
  {
986
1388
  "id": "44444444-4444-4444-4444-444444444444",
987
- "name": "Fixture Riverside Concert",
988
- "description": "An evening fixture event.",
1389
+ "name": "Riverside Harvest Supper",
1390
+ "description": "A long-table supper on the Aldern meadow, cooked by our kitchen volunteers with produce from the community garden.",
989
1391
  "registrationMode": "TICKETED",
990
- "startsAt": "2030-09-20T19:00:00Z",
991
- "endsAt": null,
992
- "venueName": "Fixture Hall",
1392
+ "startsAt": "2030-10-04T18:30:00Z",
1393
+ "endsAt": "2030-10-04T22:00:00Z",
1394
+ "venueName": "Aldern Meadow",
993
1395
  "online": false,
994
- "imageUrl": null,
1396
+ "imageUrl": "p60fixture:event/harvest",
995
1397
  "onSale": true,
996
1398
  "soldOut": false,
997
1399
  "priceFrom": 12.5,
998
1400
  "currency": "GBP",
999
1401
  "detailHref": "/events?event=44444444-4444-4444-4444-444444444444"
1402
+ },
1403
+ {
1404
+ "id": "88888888-8888-8888-8888-888888888888",
1405
+ "name": "Winter Fair",
1406
+ "description": "Stalls, carols and the grand raffle in aid of the Winter Appeal. Free entry; every stall staffed by volunteers.",
1407
+ "registrationMode": "TICKETED",
1408
+ "startsAt": "2030-12-07T11:00:00Z",
1409
+ "endsAt": "2030-12-07T16:00:00Z",
1410
+ "venueName": "Alderford Market Square",
1411
+ "online": false,
1412
+ "imageUrl": "p60fixture:event/winterfair",
1413
+ "onSale": true,
1414
+ "soldOut": true,
1415
+ "priceFrom": 0.0,
1416
+ "currency": "GBP",
1417
+ "detailHref": "/events?event=88888888-8888-8888-8888-888888888888"
1000
1418
  }
1001
1419
  ]
1002
1420
  },
1003
1421
  "whatsOn": {
1004
1422
  "infoEvents": [
1005
1423
  {
1006
- "id": "33333333-3333-3333-3333-333333333333",
1007
- "name": "Fixture Open Morning",
1008
- "description": "An evening fixture event.",
1424
+ "id": "66666666-6666-6666-6666-666666666666",
1425
+ "name": "Warm Space Wednesdays",
1426
+ "description": "Open, warm, tea always on. Every Wednesday through winter.",
1009
1427
  "registrationMode": "INFO",
1010
- "startsAt": "2030-09-20T19:00:00Z",
1428
+ "startsAt": "2030-11-06T10:00:00Z",
1429
+ "endsAt": null,
1430
+ "venueName": "St Chad's Hall",
1431
+ "online": false,
1432
+ "imageUrl": null,
1433
+ "onSale": true,
1434
+ "soldOut": false,
1435
+ "priceFrom": null,
1436
+ "currency": null,
1437
+ "detailHref": "/events?event=66666666-6666-6666-6666-666666666666"
1438
+ },
1439
+ {
1440
+ "id": "77777777-7777-7777-7777-777777777777",
1441
+ "name": "Money Advice Drop-in",
1442
+ "description": "Trained advisers, no appointment needed.",
1443
+ "registrationMode": "INFO",
1444
+ "startsAt": "2030-09-12T10:00:00Z",
1445
+ "endsAt": null,
1446
+ "venueName": null,
1447
+ "online": true,
1448
+ "imageUrl": null,
1449
+ "onSale": true,
1450
+ "soldOut": false,
1451
+ "priceFrom": null,
1452
+ "currency": null,
1453
+ "detailHref": "/events?event=77777777-7777-7777-7777-777777777777"
1454
+ },
1455
+ {
1456
+ "id": "99999999-9999-9999-9999-999999999999",
1457
+ "name": "Community Garden Open Morning",
1458
+ "description": "See the beds, meet the growers, take some seedlings home.",
1459
+ "registrationMode": "INFO",
1460
+ "startsAt": "2030-09-14T10:00:00Z",
1011
1461
  "endsAt": null,
1012
- "venueName": "Fixture Hall",
1462
+ "venueName": "Foundry Lane Garden",
1013
1463
  "online": false,
1014
1464
  "imageUrl": null,
1015
1465
  "onSale": true,
1016
1466
  "soldOut": false,
1017
1467
  "priceFrom": null,
1018
1468
  "currency": null,
1019
- "detailHref": "/events?event=33333333-3333-3333-3333-333333333333"
1469
+ "detailHref": "/events?event=99999999-9999-9999-9999-999999999999"
1020
1470
  }
1021
1471
  ]
1022
1472
  },
1023
1473
  "articles": {
1024
1474
  "latestArticles": [
1025
1475
  {
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,
1476
+ "title": "The Community Garden Opens Its Gates",
1477
+ "href": "/articles/community-garden-opens",
1478
+ "excerpt": "Two years of digging, fundraising and Saturday mornings in the rain: the Foundry Lane garden is finally open to everyone.",
1479
+ "imageUrl": "p60fixture:article/garden",
1480
+ "publishedAt": "2030-04-12T09:00:00Z",
1481
+ "readingMinutes": 5,
1032
1482
  "categories": [
1033
1483
  "News"
1034
1484
  ]
1035
1485
  },
1036
1486
  {
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,
1487
+ "title": "What a Food Parcel Actually Holds",
1488
+ "href": "/articles/what-a-food-parcel-holds",
1489
+ "excerpt": "People imagine tins and pasta. The real list is longer, more personal, and put together with more care than you might think.",
1490
+ "imageUrl": "p60fixture:article/parcel",
1491
+ "publishedAt": "2030-03-28T09:00:00Z",
1492
+ "readingMinutes": 6,
1043
1493
  "categories": [
1044
- "News"
1494
+ "Stories"
1045
1495
  ]
1046
- }
1047
- ]
1048
- },
1049
- "campaigns": {
1050
- "campaigns": [
1496
+ },
1497
+ {
1498
+ "title": "Meet the Wednesday Kitchen Crew",
1499
+ "href": "/articles/wednesday-kitchen-crew",
1500
+ "excerpt": "Eleven volunteers, one borrowed oven, and a hundred and forty hot meals a week. The kitchen crew tell it in their own words.",
1501
+ "imageUrl": "p60fixture:people/kitchen",
1502
+ "publishedAt": "2030-03-02T09:00:00Z",
1503
+ "readingMinutes": 7,
1504
+ "categories": [
1505
+ "Stories"
1506
+ ]
1507
+ },
1508
+ {
1509
+ "title": "Winter Appeal: Where Your Money Went",
1510
+ "href": "/articles/winter-appeal-report",
1511
+ "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.",
1512
+ "imageUrl": "p60fixture:campaign/winterreport",
1513
+ "publishedAt": "2030-02-14T09:00:00Z",
1514
+ "readingMinutes": 8,
1515
+ "categories": [
1516
+ "News"
1517
+ ]
1518
+ }
1519
+ ]
1520
+ },
1521
+ "campaigns": {
1522
+ "campaigns": [
1051
1523
  {
1052
1524
  "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"
1525
+ "title": "Keep the 27 Bus",
1526
+ "href": "/campaigns/keep-the-27-bus",
1527
+ "summary": "The only bus serving the Fenwick estate is set to be cut. Add your name before the consultation closes.",
1528
+ "imageUrl": "p60fixture:campaign/bus"
1057
1529
  },
1058
1530
  {
1059
1531
  "id": "99999999-9999-9999-9999-999999999992",
1060
- "title": "Fixture Community Survey",
1061
- "href": "/campaigns/fixture-community-survey",
1532
+ "title": "A Defibrillator on Every Street",
1533
+ "href": "/campaigns/defibrillator-every-street",
1534
+ "summary": "We are raising for twelve more public defibrillators and training two hundred neighbours to use them.",
1535
+ "imageUrl": "p60fixture:campaign/defib"
1536
+ },
1537
+ {
1538
+ "id": "99999999-9999-9999-9999-999999999993",
1539
+ "title": "Alderford Housing Survey",
1540
+ "href": "/campaigns/housing-survey",
1062
1541
  "summary": null,
1063
1542
  "imageUrl": null
1064
1543
  }
@@ -1068,15 +1547,15 @@
1068
1547
  "emergency": {
1069
1548
  "causes": [
1070
1549
  {
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,
1550
+ "id": "55555555-5555-5555-5555-555555555550",
1551
+ "title": "Flood Response Appeal",
1552
+ "slug": "flood-response",
1553
+ "description": "The Aldern broke its banks on Sunday. Thirty households need dry bedding, dehumidifiers and somewhere warm now.",
1554
+ "imageUrl": "p60fixture:cause/flood",
1076
1555
  "targetAmount": 50000,
1077
1556
  "raisedAmount": 18750,
1078
1557
  "progressPercentage": 38,
1079
- "href": "/donate/fixture-emergency-appeal",
1558
+ "href": "/donate/flood-response",
1080
1559
  "paymentOptions": {
1081
1560
  "oneOff": true,
1082
1561
  "recurringCard": true,
@@ -1088,92 +1567,821 @@
1088
1567
  "appealGrid": {
1089
1568
  "causes": [
1090
1569
  {
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,
1570
+ "id": "55555555-5555-5555-5555-555555555551",
1571
+ "title": "Winter Appeal",
1572
+ "slug": "winter-appeal",
1573
+ "description": "Heating top-ups, warm bedding and a hot meal for the households the cold hits hardest.",
1574
+ "imageUrl": "p60fixture:cause/winter",
1096
1575
  "targetAmount": 20000,
1097
1576
  "raisedAmount": 12500,
1098
1577
  "progressPercentage": 63,
1099
- "href": "/donate/fixture-winter-appeal",
1578
+ "href": "/donate/winter-appeal",
1100
1579
  "paymentOptions": {
1101
1580
  "oneOff": true,
1102
1581
  "recurringCard": true,
1103
1582
  "directDebit": false
1104
1583
  }
1584
+ },
1585
+ {
1586
+ "id": "55555555-5555-5555-5555-555555555552",
1587
+ "title": "Food Bank Fund",
1588
+ "slug": "food-bank-fund",
1589
+ "description": "Keeps the shelves stocked between donation drives. Ten pounds fills a family parcel.",
1590
+ "imageUrl": "p60fixture:cause/foodbank",
1591
+ "targetAmount": 15000,
1592
+ "raisedAmount": 9800,
1593
+ "progressPercentage": 65,
1594
+ "href": "/donate/food-bank-fund",
1595
+ "paymentOptions": {
1596
+ "oneOff": true,
1597
+ "recurringCard": true,
1598
+ "directDebit": true
1599
+ }
1600
+ },
1601
+ {
1602
+ "id": "55555555-5555-5555-5555-555555555553",
1603
+ "title": "Youth Club Futures",
1604
+ "slug": "youth-club-futures",
1605
+ "description": "Coaching, kit and trips for the seventy young people who come through our doors each week.",
1606
+ "imageUrl": "p60fixture:cause/youth",
1607
+ "targetAmount": 8000,
1608
+ "raisedAmount": 2150,
1609
+ "progressPercentage": 27,
1610
+ "href": "/donate/youth-club-futures",
1611
+ "paymentOptions": {
1612
+ "oneOff": true,
1613
+ "recurringCard": false,
1614
+ "directDebit": false
1615
+ }
1105
1616
  }
1106
1617
  ]
1107
1618
  },
1108
1619
  "programmes": {
1109
1620
  "services": [
1110
1621
  {
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"
1622
+ "id": "66666666-6666-6666-6666-666666666661",
1623
+ "title": "Food Bank",
1624
+ "slug": "food-bank",
1625
+ "summary": "Emergency parcels, no referral needed, from the Foundry Lane depot every weekday morning.",
1626
+ "imageUrl": "p60fixture:service/foodbank",
1627
+ "href": "/services?service=food-bank"
1117
1628
  },
1118
1629
  {
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"
1630
+ "id": "66666666-6666-6666-6666-666666666662",
1631
+ "title": "Youth Club",
1632
+ "slug": "youth-club",
1633
+ "summary": "Sports, cooking and somewhere to be for ages 10 to 16, four evenings a week.",
1634
+ "imageUrl": "p60fixture:service/youthclub",
1635
+ "href": "/services?service=youth-club"
1636
+ },
1637
+ {
1638
+ "id": "66666666-6666-6666-6666-666666666663",
1639
+ "title": "Money Advice",
1640
+ "slug": "money-advice",
1641
+ "summary": "Free, confidential help with debt, benefits and the letters that keep you up at night.",
1642
+ "imageUrl": "p60fixture:service/advice",
1643
+ "href": "/services?service=money-advice"
1644
+ },
1645
+ {
1646
+ "id": "66666666-6666-6666-6666-666666666664",
1647
+ "title": "Community Garden",
1648
+ "slug": "community-garden",
1649
+ "summary": "Twenty-two raised beds, a greenhouse and a seed library, open to every Alderford household.",
1650
+ "imageUrl": "p60fixture:service/garden",
1651
+ "href": "/services?service=community-garden"
1652
+ },
1653
+ {
1654
+ "id": "66666666-6666-6666-6666-666666666665",
1655
+ "title": "Warm Spaces",
1656
+ "slug": "warm-spaces",
1657
+ "summary": "Our hall, open and heated through winter, with tea, wifi and company.",
1658
+ "imageUrl": "p60fixture:service/warmspace",
1659
+ "href": "/services?service=warm-spaces"
1125
1660
  }
1126
1661
  ]
1127
1662
  },
1128
1663
  "resources": {
1129
1664
  "resources": [
1130
1665
  {
1131
- "id": "88888888-8888-8888-8888-888888888888",
1132
- "title": "Fixture annual report.pdf",
1666
+ "id": "88888888-8888-8888-8888-888888888881",
1667
+ "title": "Annual report 2029.pdf",
1133
1668
  "folder": "Reports",
1134
- "url": "/fixture/annual-report.pdf",
1669
+ "url": "/fixture/annual-report-2029.pdf",
1135
1670
  "contentType": "application/pdf",
1136
- "bytes": 245760,
1671
+ "bytes": 1245760,
1137
1672
  "publishedAt": "2030-02-01T10:00:00Z"
1673
+ },
1674
+ {
1675
+ "id": "88888888-8888-8888-8888-888888888882",
1676
+ "title": "Safeguarding policy.pdf",
1677
+ "folder": "Policies",
1678
+ "url": "/fixture/safeguarding-policy.pdf",
1679
+ "contentType": "application/pdf",
1680
+ "bytes": 384000,
1681
+ "publishedAt": "2029-11-12T10:00:00Z"
1682
+ },
1683
+ {
1684
+ "id": "88888888-8888-8888-8888-888888888883",
1685
+ "title": "Volunteer handbook.pdf",
1686
+ "folder": "Volunteering",
1687
+ "url": "/fixture/volunteer-handbook.pdf",
1688
+ "contentType": "application/pdf",
1689
+ "bytes": 942080,
1690
+ "publishedAt": "2030-01-20T10:00:00Z"
1691
+ },
1692
+ {
1693
+ "id": "88888888-8888-8888-8888-888888888884",
1694
+ "title": "Hall hire form.pdf",
1695
+ "folder": "Forms",
1696
+ "url": "/fixture/hall-hire-form.pdf",
1697
+ "contentType": "application/pdf",
1698
+ "bytes": 128000,
1699
+ "publishedAt": "2029-09-02T10:00:00Z"
1138
1700
  }
1139
1701
  ]
1140
1702
  },
1141
1703
  "locations": {
1142
1704
  "locations": [
1143
1705
  {
1144
- "id": "99999999-9999-9999-9999-999999999999",
1145
- "name": "Fixture Community Hall",
1146
- "address": "1 Fixture Street, Leeds",
1706
+ "id": "99999999-9999-9999-9999-999999999901",
1707
+ "name": "Foundry Lane Hub",
1708
+ "address": "14 Foundry Lane, Alderford AL2 4RS",
1147
1709
  "latitude": 53.8008,
1148
1710
  "longitude": -1.5491,
1149
1711
  "primary": true,
1150
- "directionsHref": "https://www.google.com/maps/dir/?api=1&destination=Fixture%20Community%20Hall"
1712
+ "directionsHref": "https://www.google.com/maps/dir/?api=1&destination=Foundry%20Lane%20Hub"
1713
+ },
1714
+ {
1715
+ "id": "99999999-9999-9999-9999-999999999902",
1716
+ "name": "St Chad's Hall",
1717
+ "address": "Church Walk, Alderford AL1 2JW",
1718
+ "latitude": 53.7975,
1719
+ "longitude": -1.5562,
1720
+ "primary": false,
1721
+ "directionsHref": "https://www.google.com/maps/dir/?api=1&destination=St%20Chad%27s%20Hall"
1151
1722
  }
1152
1723
  ]
1153
1724
  },
1154
- "volunteering": {
1155
- "opportunities": [
1725
+ "impactMap": {
1726
+ "impactMap": {
1727
+ "slug": "our-work",
1728
+ "title": "Where the Trust works",
1729
+ "intro": "Every marker is a real place in Alderford.",
1730
+ "viewport": {
1731
+ "centerLat": 53.7995,
1732
+ "centerLng": -1.552,
1733
+ "zoom": 14
1734
+ },
1735
+ "points": [
1736
+ {
1737
+ "latitude": 53.8008,
1738
+ "longitude": -1.5491,
1739
+ "kind": "LOCATION",
1740
+ "title": "Foundry Lane Hub",
1741
+ "summary": "The front door: food bank depot, advice desk and the volunteer office.",
1742
+ "imageUrl": null,
1743
+ "href": null
1744
+ },
1745
+ {
1746
+ "latitude": 53.8014,
1747
+ "longitude": -1.5502,
1748
+ "kind": "CAUSE",
1749
+ "title": "Community Garden",
1750
+ "summary": "Twenty-two raised beds and a seed library, open to every household.",
1751
+ "imageUrl": null,
1752
+ "href": "/donate/food-bank-fund"
1753
+ },
1754
+ {
1755
+ "latitude": 53.7975,
1756
+ "longitude": -1.5562,
1757
+ "kind": "EVENT",
1758
+ "title": "Warm Space Wednesdays",
1759
+ "summary": "St Chad's Hall, open and heated all winter.",
1760
+ "imageUrl": null,
1761
+ "href": "/events?event=66666666-6666-6666-6666-666666666666"
1762
+ },
1763
+ {
1764
+ "latitude": 53.7969,
1765
+ "longitude": -1.5474,
1766
+ "kind": "CAMPAIGN",
1767
+ "title": "Keep the 27 Bus",
1768
+ "summary": "The only bus serving the Fenwick estate is set to be cut.",
1769
+ "imageUrl": null,
1770
+ "href": "/campaigns/keep-the-27-bus"
1771
+ }
1772
+ ]
1773
+ }
1774
+ }
1775
+ },
1776
+ "site": {
1777
+ "brand": {
1778
+ "name": "The Lantern Trust",
1779
+ "tagline": "Neighbours, looked after.",
1780
+ "logoUrl": null,
1781
+ "logoType": "full",
1782
+ "footerLogoUrl": null
1783
+ },
1784
+ "nav": {
1785
+ "items": [
1156
1786
  {
1157
- "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.",
1161
- "imageUrl": null,
1162
- "content": { "location": "Fixture Garden", "schedule": "Saturday mornings", "commitment": "Twice a month", "hoursExpected": "3 hours" }
1787
+ "label": "Home",
1788
+ "href": "/",
1789
+ "cta": false
1790
+ },
1791
+ {
1792
+ "label": "About us",
1793
+ "href": "/about",
1794
+ "cta": false
1795
+ },
1796
+ {
1797
+ "label": "Events",
1798
+ "href": "/events",
1799
+ "cta": false
1800
+ },
1801
+ {
1802
+ "label": "Services",
1803
+ "href": "/services",
1804
+ "cta": false,
1805
+ "children": [
1806
+ {
1807
+ "label": "Food bank",
1808
+ "href": "/services?service=food-bank",
1809
+ "group": "Support",
1810
+ "description": "Emergency food parcels and practical advice."
1811
+ },
1812
+ {
1813
+ "label": "Community",
1814
+ "href": "#",
1815
+ "group": "Get involved",
1816
+ "children": [
1817
+ {
1818
+ "label": "Youth club",
1819
+ "href": "/services?service=youth-club"
1820
+ }
1821
+ ]
1822
+ }
1823
+ ],
1824
+ "megaMenu": {
1825
+ "columns": 3,
1826
+ "promo": {
1827
+ "title": "Visit this week",
1828
+ "text": "Find a programme and meet the team.",
1829
+ "href": "/services",
1830
+ "label": "Explore services"
1831
+ }
1832
+ }
1833
+ },
1834
+ {
1835
+ "label": "Donate",
1836
+ "href": "/donate",
1837
+ "cta": true
1838
+ }
1839
+ ],
1840
+ "footer": [
1841
+ {
1842
+ "label": "About us",
1843
+ "href": "/about"
1844
+ },
1845
+ {
1846
+ "label": "Contact",
1847
+ "href": "https://example.org/contact",
1848
+ "external": true
1849
+ },
1850
+ {
1851
+ "label": "Our work",
1852
+ "href": "#",
1853
+ "children": [
1854
+ {
1855
+ "label": "Food bank",
1856
+ "href": "/services?service=food-bank"
1857
+ }
1858
+ ]
1859
+ }
1860
+ ],
1861
+ "derived": [
1862
+ {
1863
+ "label": "Home",
1864
+ "href": "/",
1865
+ "cta": false
1866
+ },
1867
+ {
1868
+ "label": "About us",
1869
+ "href": "/about",
1870
+ "cta": false
1871
+ },
1872
+ {
1873
+ "label": "Events",
1874
+ "href": "/events",
1875
+ "cta": false
1876
+ },
1877
+ {
1878
+ "label": "Donate",
1879
+ "href": "/donate",
1880
+ "cta": true
1163
1881
  }
1164
1882
  ]
1165
1883
  },
1166
- "media": {
1167
- "media": [
1884
+ "socials": [
1885
+ {
1886
+ "platform": "facebook",
1887
+ "url": "https://facebook.com/lanterntrust",
1888
+ "iconSvg": "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\"/></svg>"
1889
+ },
1890
+ {
1891
+ "platform": "instagram",
1892
+ "url": "https://instagram.com/lanterntrust",
1893
+ "iconSvg": "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"5\"/></svg>"
1894
+ },
1895
+ {
1896
+ "platform": "youtube",
1897
+ "url": "https://youtube.com/@lanterntrust",
1898
+ "iconSvg": "<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><polygon points=\"10,8 16,12 10,16\"/></svg>"
1899
+ }
1900
+ ],
1901
+ "locale": {
1902
+ "code": "en",
1903
+ "direction": "ltr",
1904
+ "languages": [
1168
1905
  {
1169
- "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.",
1173
- "imageUrl": null,
1174
- "content": { "mediaType": "VIDEO", "mediaUrl": "/fixture/community-talk", "speaker": "Amina Khan", "duration": "18 minutes" }
1906
+ "code": "en",
1907
+ "label": "English",
1908
+ "direction": "ltr"
1909
+ },
1910
+ {
1911
+ "code": "cy",
1912
+ "label": "Cymraeg",
1913
+ "direction": "ltr"
1914
+ },
1915
+ {
1916
+ "code": "ar",
1917
+ "label": "العربية",
1918
+ "direction": "rtl"
1175
1919
  }
1176
1920
  ]
1921
+ },
1922
+ "content": {
1923
+ "services": [
1924
+ {
1925
+ "id": "66666666-6666-6666-6666-666666666661",
1926
+ "title": "Food Bank",
1927
+ "slug": "food-bank",
1928
+ "summary": "Emergency parcels, no referral needed, from the Foundry Lane depot every weekday morning.",
1929
+ "imageUrl": "p60fixture:service/foodbank",
1930
+ "href": "/services?service=food-bank"
1931
+ },
1932
+ {
1933
+ "id": "66666666-6666-6666-6666-666666666662",
1934
+ "title": "Youth Club",
1935
+ "slug": "youth-club",
1936
+ "summary": "Sports, cooking and somewhere to be for ages 10 to 16, four evenings a week.",
1937
+ "imageUrl": "p60fixture:service/youthclub",
1938
+ "href": "/services?service=youth-club"
1939
+ },
1940
+ {
1941
+ "id": "66666666-6666-6666-6666-666666666663",
1942
+ "title": "Money Advice",
1943
+ "slug": "money-advice",
1944
+ "summary": "Free, confidential help with debt, benefits and the letters that keep you up at night.",
1945
+ "imageUrl": "p60fixture:service/advice",
1946
+ "href": "/services?service=money-advice"
1947
+ },
1948
+ {
1949
+ "id": "66666666-6666-6666-6666-666666666664",
1950
+ "title": "Community Garden",
1951
+ "slug": "community-garden",
1952
+ "summary": "Twenty-two raised beds, a greenhouse and a seed library, open to every Alderford household.",
1953
+ "imageUrl": "p60fixture:service/garden",
1954
+ "href": "/services?service=community-garden"
1955
+ },
1956
+ {
1957
+ "id": "66666666-6666-6666-6666-666666666665",
1958
+ "title": "Warm Spaces",
1959
+ "slug": "warm-spaces",
1960
+ "summary": "Our hall, open and heated through winter, with tea, wifi and company.",
1961
+ "imageUrl": "p60fixture:service/warmspace",
1962
+ "href": "/services?service=warm-spaces"
1963
+ }
1964
+ ],
1965
+ "events": [
1966
+ {
1967
+ "id": "22222222-2222-2222-2222-222222222222",
1968
+ "name": "The Lantern Gala",
1969
+ "description": "Our biggest night of the year: supper, music from the Alderford Community Choir, and the annual lantern walk along the river to close.",
1970
+ "registrationMode": "TICKETED",
1971
+ "startsAt": "2030-09-20T19:00:00Z",
1972
+ "endsAt": null,
1973
+ "venueName": "The Maltings, Alderford",
1974
+ "online": false,
1975
+ "imageUrl": "p60fixture:event/gala",
1976
+ "onSale": true,
1977
+ "soldOut": false,
1978
+ "priceFrom": 25.0,
1979
+ "currency": "GBP",
1980
+ "detailHref": "/events?event=22222222-2222-2222-2222-222222222222"
1981
+ },
1982
+ {
1983
+ "id": "44444444-4444-4444-4444-444444444444",
1984
+ "name": "Riverside Harvest Supper",
1985
+ "description": "A long-table supper on the Aldern meadow, cooked by our kitchen volunteers with produce from the community garden.",
1986
+ "registrationMode": "TICKETED",
1987
+ "startsAt": "2030-10-04T18:30:00Z",
1988
+ "endsAt": "2030-10-04T22:00:00Z",
1989
+ "venueName": "Aldern Meadow",
1990
+ "online": false,
1991
+ "imageUrl": "p60fixture:event/harvest",
1992
+ "onSale": true,
1993
+ "soldOut": false,
1994
+ "priceFrom": 12.5,
1995
+ "currency": "GBP",
1996
+ "detailHref": "/events?event=44444444-4444-4444-4444-444444444444"
1997
+ },
1998
+ {
1999
+ "id": "55555555-5555-5555-5555-555555555555",
2000
+ "name": "Volunteer Welcome Evening",
2001
+ "description": "Meet the team, hear where help is needed most this season, and find a role that fits the time you have.",
2002
+ "registrationMode": "RSVP",
2003
+ "startsAt": "2030-09-03T18:00:00Z",
2004
+ "endsAt": "2030-09-03T19:30:00Z",
2005
+ "venueName": "Foundry Lane Hub",
2006
+ "online": false,
2007
+ "imageUrl": "p60fixture:event/welcome",
2008
+ "onSale": true,
2009
+ "soldOut": false,
2010
+ "priceFrom": null,
2011
+ "currency": null,
2012
+ "detailHref": "/events?event=55555555-5555-5555-5555-555555555555"
2013
+ },
2014
+ {
2015
+ "id": "66666666-6666-6666-6666-666666666666",
2016
+ "name": "Warm Space Wednesdays",
2017
+ "description": "Our hall is open, warm and stocked with tea every Wednesday through the cold months. No booking, no questions, everyone welcome.",
2018
+ "registrationMode": "INFO",
2019
+ "startsAt": "2030-11-06T10:00:00Z",
2020
+ "endsAt": "2030-11-06T15:00:00Z",
2021
+ "venueName": "St Chad's Hall",
2022
+ "online": false,
2023
+ "imageUrl": null,
2024
+ "onSale": true,
2025
+ "soldOut": false,
2026
+ "priceFrom": null,
2027
+ "currency": null,
2028
+ "detailHref": "/events?event=66666666-6666-6666-6666-666666666666"
2029
+ },
2030
+ {
2031
+ "id": "77777777-7777-7777-7777-777777777777",
2032
+ "name": "Money Advice Drop-in",
2033
+ "description": "A quiet hour with our trained advisers. Bring letters you are worried about; we will work through them together.",
2034
+ "registrationMode": "INFO",
2035
+ "startsAt": "2030-09-12T10:00:00Z",
2036
+ "endsAt": "2030-09-12T12:00:00Z",
2037
+ "venueName": null,
2038
+ "online": true,
2039
+ "imageUrl": null,
2040
+ "onSale": true,
2041
+ "soldOut": false,
2042
+ "priceFrom": null,
2043
+ "currency": null,
2044
+ "detailHref": "/events?event=77777777-7777-7777-7777-777777777777"
2045
+ },
2046
+ {
2047
+ "id": "88888888-8888-8888-8888-888888888888",
2048
+ "name": "Winter Fair",
2049
+ "description": "Stalls, carols and the grand raffle in aid of the Winter Appeal. Free entry; every stall staffed by volunteers.",
2050
+ "registrationMode": "TICKETED",
2051
+ "startsAt": "2030-12-07T11:00:00Z",
2052
+ "endsAt": "2030-12-07T16:00:00Z",
2053
+ "venueName": "Alderford Market Square",
2054
+ "online": false,
2055
+ "imageUrl": "p60fixture:event/winterfair",
2056
+ "onSale": true,
2057
+ "soldOut": true,
2058
+ "priceFrom": 0.0,
2059
+ "currency": "GBP",
2060
+ "detailHref": "/events?event=88888888-8888-8888-8888-888888888888"
2061
+ }
2062
+ ],
2063
+ "articles": [
2064
+ {
2065
+ "id": "88888888-8888-8888-8888-888888888888",
2066
+ "title": "The Community Garden Opens Its Gates",
2067
+ "slug": "community-garden-opens",
2068
+ "excerpt": "Two years of digging, fundraising and Saturday mornings in the rain: the Foundry Lane garden is finally open to everyone.",
2069
+ "imageUrl": "p60fixture:article/garden",
2070
+ "authorName": "Amina Khan",
2071
+ "authorRole": "Community Editor",
2072
+ "categories": [
2073
+ "News"
2074
+ ],
2075
+ "publishedAt": "2030-04-12T09:00:00Z",
2076
+ "readingMinutes": 5,
2077
+ "href": "/articles/community-garden-opens"
2078
+ },
2079
+ {
2080
+ "id": "99999999-9999-9999-9999-999999999999",
2081
+ "title": "What a Food Parcel Actually Holds",
2082
+ "slug": "what-a-food-parcel-holds",
2083
+ "excerpt": "People imagine tins and pasta. The real list is longer, more personal, and put together with more care than you might think.",
2084
+ "imageUrl": "p60fixture:article/parcel",
2085
+ "authorName": "Derek Osei",
2086
+ "authorRole": "Food Bank Lead",
2087
+ "categories": [
2088
+ "Stories"
2089
+ ],
2090
+ "publishedAt": "2030-03-28T09:00:00Z",
2091
+ "readingMinutes": 6,
2092
+ "href": "/articles/what-a-food-parcel-holds"
2093
+ },
2094
+ {
2095
+ "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
2096
+ "title": "Meet the Wednesday Kitchen Crew",
2097
+ "slug": "wednesday-kitchen-crew",
2098
+ "excerpt": "Eleven volunteers, one borrowed oven, and a hundred and forty hot meals a week. The kitchen crew tell it in their own words.",
2099
+ "imageUrl": "p60fixture:people/kitchen",
2100
+ "authorName": "Amina Khan",
2101
+ "authorRole": "Community Editor",
2102
+ "categories": [
2103
+ "Stories"
2104
+ ],
2105
+ "publishedAt": "2030-03-02T09:00:00Z",
2106
+ "readingMinutes": 7,
2107
+ "href": "/articles/wednesday-kitchen-crew"
2108
+ },
2109
+ {
2110
+ "id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
2111
+ "title": "Winter Appeal: Where Your Money Went",
2112
+ "slug": "winter-appeal-report",
2113
+ "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.",
2114
+ "imageUrl": "p60fixture:campaign/winterreport",
2115
+ "authorName": "Priya Sharma",
2116
+ "authorRole": "Trustee",
2117
+ "categories": [
2118
+ "News"
2119
+ ],
2120
+ "publishedAt": "2030-02-14T09:00:00Z",
2121
+ "readingMinutes": 8,
2122
+ "href": "/articles/winter-appeal-report"
2123
+ },
2124
+ {
2125
+ "id": "cccccccc-cccc-cccc-cccc-cccccccccccc",
2126
+ "title": "Five Things Our Money Advisers Wish You Knew",
2127
+ "slug": "money-advice-five-things",
2128
+ "excerpt": "The advice team see the same worries again and again. Most of them have a fix that takes less than an hour.",
2129
+ "imageUrl": null,
2130
+ "authorName": "Tom Bradley",
2131
+ "authorRole": "Advice Service",
2132
+ "categories": [
2133
+ "Guides"
2134
+ ],
2135
+ "publishedAt": "2030-01-30T09:00:00Z",
2136
+ "readingMinutes": 4,
2137
+ "href": "/articles/money-advice-five-things"
2138
+ },
2139
+ {
2140
+ "id": "dddddddd-dddd-dddd-dddd-dddddddddddd",
2141
+ "title": "A Year of Warm Spaces, in Numbers",
2142
+ "slug": "warm-spaces-in-numbers",
2143
+ "excerpt": "Fourteen hundred visits, ninety regulars, and one chess league nobody planned. What happens when you just open the door.",
2144
+ "imageUrl": "p60fixture:service/warmspace",
2145
+ "authorName": "Amina Khan",
2146
+ "authorRole": "Community Editor",
2147
+ "categories": [
2148
+ "News"
2149
+ ],
2150
+ "publishedAt": "2030-01-08T09:00:00Z",
2151
+ "readingMinutes": 5,
2152
+ "href": "/articles/warm-spaces-in-numbers"
2153
+ }
2154
+ ],
2155
+ "campaigns": [
2156
+ {
2157
+ "id": "99999999-9999-9999-9999-999999999991",
2158
+ "title": "Keep the 27 Bus",
2159
+ "href": "/campaigns/keep-the-27-bus",
2160
+ "summary": "The only bus serving the Fenwick estate is set to be cut. Add your name before the consultation closes.",
2161
+ "imageUrl": "p60fixture:campaign/bus"
2162
+ },
2163
+ {
2164
+ "id": "99999999-9999-9999-9999-999999999992",
2165
+ "title": "A Defibrillator on Every Street",
2166
+ "href": "/campaigns/defibrillator-every-street",
2167
+ "summary": "We are raising for twelve more public defibrillators and training two hundred neighbours to use them.",
2168
+ "imageUrl": "p60fixture:campaign/defib"
2169
+ },
2170
+ {
2171
+ "id": "99999999-9999-9999-9999-999999999993",
2172
+ "title": "Alderford Housing Survey",
2173
+ "href": "/campaigns/housing-survey",
2174
+ "summary": null,
2175
+ "imageUrl": null
2176
+ }
2177
+ ],
2178
+ "causes": [
2179
+ {
2180
+ "id": "55555555-5555-5555-5555-555555555550",
2181
+ "title": "Flood Response Appeal",
2182
+ "slug": "flood-response",
2183
+ "description": "The Aldern broke its banks on Sunday. Thirty households need dry bedding, dehumidifiers and somewhere warm now.",
2184
+ "imageUrl": "p60fixture:cause/flood",
2185
+ "targetAmount": 50000,
2186
+ "raisedAmount": 18750,
2187
+ "progressPercentage": 38,
2188
+ "href": "/donate/flood-response",
2189
+ "paymentOptions": {
2190
+ "oneOff": true,
2191
+ "recurringCard": true,
2192
+ "directDebit": true
2193
+ }
2194
+ },
2195
+ {
2196
+ "id": "55555555-5555-5555-5555-555555555551",
2197
+ "title": "Winter Appeal",
2198
+ "slug": "winter-appeal",
2199
+ "description": "Heating top-ups, warm bedding and a hot meal for the households the cold hits hardest.",
2200
+ "imageUrl": "p60fixture:cause/winter",
2201
+ "targetAmount": 20000,
2202
+ "raisedAmount": 12500,
2203
+ "progressPercentage": 63,
2204
+ "href": "/donate/winter-appeal",
2205
+ "paymentOptions": {
2206
+ "oneOff": true,
2207
+ "recurringCard": true,
2208
+ "directDebit": false
2209
+ }
2210
+ },
2211
+ {
2212
+ "id": "55555555-5555-5555-5555-555555555552",
2213
+ "title": "Food Bank Fund",
2214
+ "slug": "food-bank-fund",
2215
+ "description": "Keeps the shelves stocked between donation drives. Ten pounds fills a family parcel.",
2216
+ "imageUrl": "p60fixture:cause/foodbank",
2217
+ "targetAmount": 15000,
2218
+ "raisedAmount": 9800,
2219
+ "progressPercentage": 65,
2220
+ "href": "/donate/food-bank-fund",
2221
+ "paymentOptions": {
2222
+ "oneOff": true,
2223
+ "recurringCard": true,
2224
+ "directDebit": true
2225
+ }
2226
+ },
2227
+ {
2228
+ "id": "55555555-5555-5555-5555-555555555553",
2229
+ "title": "Youth Club Futures",
2230
+ "slug": "youth-club-futures",
2231
+ "description": "Coaching, kit and trips for the seventy young people who come through our doors each week.",
2232
+ "imageUrl": "p60fixture:cause/youth",
2233
+ "targetAmount": 8000,
2234
+ "raisedAmount": 2150,
2235
+ "progressPercentage": 27,
2236
+ "href": "/donate/youth-club-futures",
2237
+ "paymentOptions": {
2238
+ "oneOff": true,
2239
+ "recurringCard": false,
2240
+ "directDebit": false
2241
+ }
2242
+ }
2243
+ ],
2244
+ "courses": [
2245
+ {
2246
+ "id": "33333333-3333-3333-3333-333333333333",
2247
+ "title": "Junior Athletics",
2248
+ "description": "Weekly athletics for children aged 7 to 12, coached by qualified volunteers at the King's Park track.",
2249
+ "imageUrl": "p60fixture:course/athletics",
2250
+ "venue": "King's Park track",
2251
+ "audience": "CHILD",
2252
+ "priceFrom": 15.0,
2253
+ "currency": "GBP",
2254
+ "soldOut": false,
2255
+ "href": "/courses"
2256
+ }
2257
+ ],
2258
+ "resources": [
2259
+ {
2260
+ "id": "88888888-8888-8888-8888-888888888881",
2261
+ "title": "Annual report 2029.pdf",
2262
+ "folder": "Reports",
2263
+ "url": "/fixture/annual-report-2029.pdf",
2264
+ "contentType": "application/pdf",
2265
+ "bytes": 1245760,
2266
+ "publishedAt": "2030-02-01T10:00:00Z"
2267
+ },
2268
+ {
2269
+ "id": "88888888-8888-8888-8888-888888888882",
2270
+ "title": "Safeguarding policy.pdf",
2271
+ "folder": "Policies",
2272
+ "url": "/fixture/safeguarding-policy.pdf",
2273
+ "contentType": "application/pdf",
2274
+ "bytes": 384000,
2275
+ "publishedAt": "2029-11-12T10:00:00Z"
2276
+ },
2277
+ {
2278
+ "id": "88888888-8888-8888-8888-888888888883",
2279
+ "title": "Volunteer handbook.pdf",
2280
+ "folder": "Volunteering",
2281
+ "url": "/fixture/volunteer-handbook.pdf",
2282
+ "contentType": "application/pdf",
2283
+ "bytes": 942080,
2284
+ "publishedAt": "2030-01-20T10:00:00Z"
2285
+ },
2286
+ {
2287
+ "id": "88888888-8888-8888-8888-888888888884",
2288
+ "title": "Hall hire form.pdf",
2289
+ "folder": "Forms",
2290
+ "url": "/fixture/hall-hire-form.pdf",
2291
+ "contentType": "application/pdf",
2292
+ "bytes": 128000,
2293
+ "publishedAt": "2029-09-02T10:00:00Z"
2294
+ }
2295
+ ],
2296
+ "locations": [
2297
+ {
2298
+ "id": "99999999-9999-9999-9999-999999999901",
2299
+ "name": "Foundry Lane Hub",
2300
+ "address": "14 Foundry Lane, Alderford AL2 4RS",
2301
+ "latitude": 53.8008,
2302
+ "longitude": -1.5491,
2303
+ "primary": true,
2304
+ "directionsHref": "https://www.google.com/maps/dir/?api=1&destination=Foundry%20Lane%20Hub"
2305
+ },
2306
+ {
2307
+ "id": "99999999-9999-9999-9999-999999999902",
2308
+ "name": "St Chad's Hall",
2309
+ "address": "Church Walk, Alderford AL1 2JW",
2310
+ "latitude": 53.7975,
2311
+ "longitude": -1.5562,
2312
+ "primary": false,
2313
+ "directionsHref": "https://www.google.com/maps/dir/?api=1&destination=St%20Chad%27s%20Hall"
2314
+ }
2315
+ ],
2316
+ "schedules": [
2317
+ {
2318
+ "type": "worship",
2319
+ "content": {
2320
+ "times": [
2321
+ {
2322
+ "name": "Fajr",
2323
+ "begins": "03:20",
2324
+ "congregation": "04:00"
2325
+ },
2326
+ {
2327
+ "name": "Zuhr",
2328
+ "begins": "13:11",
2329
+ "congregation": "13:30"
2330
+ },
2331
+ {
2332
+ "name": "Asr",
2333
+ "begins": "18:30",
2334
+ "congregation": "19:00"
2335
+ },
2336
+ {
2337
+ "name": "Maghrib",
2338
+ "begins": "21:04",
2339
+ "congregation": "21:04"
2340
+ },
2341
+ {
2342
+ "name": "Isha",
2343
+ "begins": "22:12",
2344
+ "congregation": "22:30"
2345
+ }
2346
+ ],
2347
+ "beginsLabel": "Begins",
2348
+ "congregationLabel": "Jama'at",
2349
+ "note": "Jum'a 13:15 & 14:15",
2350
+ "href": "/services?service=prayer-times",
2351
+ "today": "24 July 2026",
2352
+ "todayAlt": "11 Safar 1448",
2353
+ "next": {
2354
+ "name": "Asr",
2355
+ "begins": "18:30",
2356
+ "congregation": "19:00",
2357
+ "target": "2030-07-24T19:00:00+01:00"
2358
+ },
2359
+ "jumah": [
2360
+ {
2361
+ "name": "Jum'a 1",
2362
+ "begins": null,
2363
+ "congregation": "13:15"
2364
+ },
2365
+ {
2366
+ "name": "Jum'a 2",
2367
+ "begins": null,
2368
+ "congregation": "14:15"
2369
+ }
2370
+ ],
2371
+ "hijri": {
2372
+ "display": "11 Safar 1448"
2373
+ },
2374
+ "observances": [
2375
+ {
2376
+ "name": "Community iftar",
2377
+ "date": "2 March 2030",
2378
+ "href": "/events"
2379
+ }
2380
+ ]
2381
+ }
2382
+ }
2383
+ ],
2384
+ "about": []
1177
2385
  }
1178
2386
  }
1179
2387
  }