Re-persona noir from photography to the locked detective/true-crime
direction: case-file panels, typewriter type, redaction bars, evidence
board, film grain. High-key gallery (light) / darkroom black + amber
safelight (dark).
- 49 mandatory builtin overrides (chrome, marketing, primitives,
commerce, blog family, auth, page-suggestions), re-skinned from the
frozen builtin defaults with every field/provider/tag/JS preserved;
legacy button/card field reads reconciled to the new contract.
- 7 page templates (default, full-width, landing, article, blog-index,
contact, auth) + themed email wrapper.
- 4 persona blocks (case_file, evidence_board, redacted_quote,
case_dossier); old photography blocks deleted (now overrides).
- 5 dual-mode presets (19 tokens, light+dark tuned).
- Bundled OFL fonts (Source Serif 4, IBM Plex Mono) + licenses;
RECOMMENDED_FONTS/ICONS updated; required_icon_packs=lucide.
- Master pages + seed demo ("The Cold File": home/about/blog x3/contact
with case_tips data table + row_inserted email-admins workflow/login).
- noir CSS vocabulary via manifest css.input_css_append; MID motion
(scroll-reveal + gallery lightbox, reduced-motion honored, no canvas).
Verified: make build exit 0; check-safety 32 checks 19 ok 13 skip -> OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
220 lines
11 KiB
JSON
220 lines
11 KiB
JSON
{
|
|
"demo": true,
|
|
"settings": {
|
|
"merge": {
|
|
"companyName": "The Cold File",
|
|
"tagline": "Cases that were never closed."
|
|
}
|
|
},
|
|
"data_tables": [
|
|
{
|
|
"key": "case_tips",
|
|
"name": "Case Tips",
|
|
"description": "Seeded tip-line submissions for The Cold File demo. A submission becomes a row here and emails the site admins.",
|
|
"columns": [
|
|
{ "key": "name", "label": "Name", "type": "text", "required": false },
|
|
{ "key": "email", "label": "Email", "type": "email", "required": true },
|
|
{ "key": "subject", "label": "Case or subject", "type": "text", "required": false },
|
|
{ "key": "message", "label": "Message", "type": "text", "required": true }
|
|
]
|
|
}
|
|
],
|
|
"pages": [
|
|
{
|
|
"slug": "/",
|
|
"title": "The Cold File",
|
|
"template_key": "landing",
|
|
"blocks": [
|
|
{
|
|
"block_key": "hero",
|
|
"title": "Hero",
|
|
"slot": "main",
|
|
"sort_order": 1,
|
|
"content": {
|
|
"template": "centered",
|
|
"eyebrow": "A true-crime file",
|
|
"headline": "The Cold File",
|
|
"subheadline": "Cases that were never closed.",
|
|
"description": "We read the record, walk the ground, and lay out what is known. One case at a time. You decide what it adds up to.",
|
|
"ctaPrimary": { "text": "Read the files", "url": "/blog" },
|
|
"ctaSecondary": { "text": "Submit a tip", "url": "/contact" }
|
|
}
|
|
},
|
|
{
|
|
"block_key": "noir:case_file",
|
|
"title": "Featured case",
|
|
"slot": "main",
|
|
"sort_order": 2,
|
|
"content": {
|
|
"caseNumber": "CF-0451",
|
|
"subject": "The Harbour Light",
|
|
"status": "cold",
|
|
"summary": "A night watchman clocked out at two and never reached the gate. His lamp was still burning at dawn. The tide had turned twice.",
|
|
"fields": [
|
|
{ "label": "Filed", "value": "March 1998" },
|
|
{ "label": "Location", "value": "Pier 9, East Dock" },
|
|
{ "label": "Last contact", "value": "02:04, radio check" },
|
|
{ "label": "Status", "value": "Open, unsolved" }
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"block_key": "feature-grid",
|
|
"title": "How we work",
|
|
"slot": "main",
|
|
"sort_order": 3,
|
|
"content": {
|
|
"eyebrow": "Method",
|
|
"heading": "How we work a file",
|
|
"intro": "No theory before the record. We build the case from what can be checked.",
|
|
"columns": 3,
|
|
"layout": "card",
|
|
"items": [
|
|
{ "icon": "lucide:file-text", "title": "Read the record", "text": "Reports, transcripts, and the file as it was left. We start where the paper starts." },
|
|
{ "icon": "lucide:map-pin", "title": "Walk the ground", "text": "We stand where it happened and check the file against the place." },
|
|
{ "icon": "lucide:search", "title": "Follow the thread", "text": "One fact at a time. We show our work and mark what stays unknown." }
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"block_key": "noir:redacted_quote",
|
|
"title": "Transcript",
|
|
"slot": "main",
|
|
"sort_order": 4,
|
|
"content": {
|
|
"label": "Interview 3, page 12",
|
|
"quote": "I told them what I saw at the water. They wrote it down and the name came back",
|
|
"redacted": "withheld pending review",
|
|
"attribution": "Witness statement",
|
|
"source": "Case CF-0451, sealed appendix"
|
|
}
|
|
},
|
|
{
|
|
"block_key": "cta",
|
|
"title": "Closing CTA",
|
|
"slot": "main",
|
|
"sort_order": 5,
|
|
"content": {
|
|
"background": "band",
|
|
"heading": "Know something we don't?",
|
|
"text": "Tips are read in confidence. Names stay off the page unless you ask otherwise.",
|
|
"primaryLabel": "Submit a tip",
|
|
"primaryUrl": "/contact",
|
|
"secondaryLabel": "Read the files",
|
|
"secondaryUrl": "/blog"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"slug": "/about",
|
|
"title": "About the File",
|
|
"template_key": "default",
|
|
"blocks": [
|
|
{ "block_key": "heading", "title": "Heading", "slot": "main", "sort_order": 1, "content": { "text": "About the File", "level": 1 } },
|
|
{ "block_key": "rich-text", "title": "Story", "slot": "main", "sort_order": 2, "content": { "html": "<p>The Cold File is a true-crime publication. We take one unsolved case at a time and set it out plainly, from the record up.</p><p>We do not name the guilty. We do not sell a theory. We show what is known, mark what is not, and leave the reader to weigh it. When new facts arrive, the file is updated in the open.</p>" } },
|
|
{ "block_key": "divider", "title": "Divider", "slot": "main", "sort_order": 3, "content": { "variant": "line", "weight": "thick", "width": "narrow" } },
|
|
{ "block_key": "quote", "title": "Quote", "slot": "main", "sort_order": 4, "content": { "style": "pull", "align": "center", "quote": "A cold case is not a closed one. It is a file waiting for one more fact.", "attribution": "The Cold File" } }
|
|
]
|
|
},
|
|
{
|
|
"slug": "/blog",
|
|
"title": "The Case Files",
|
|
"template_key": "blog-index",
|
|
"blocks": [
|
|
{ "block_key": "card", "title": "Case: Harbour Light", "slot": "main", "sort_order": 1, "content": { "layout": "vertical", "title": "The Harbour Light", "text": "<p>A watchman clocked out and never reached the gate. The lamp burned until dawn.</p>", "link": "/blog/the-harbour-light", "linkLabel": "Open the file" } },
|
|
{ "block_key": "card", "title": "Case: Room 14", "slot": "main", "sort_order": 2, "content": { "layout": "vertical", "title": "Room 14", "text": "<p>A guest checked in under a false name and left one suitcase behind. Nobody saw the door open.</p>", "link": "/blog/room-14", "linkLabel": "Open the file" } },
|
|
{ "block_key": "card", "title": "Case: The Last Bus", "slot": "main", "sort_order": 3, "content": { "layout": "vertical", "title": "The Last Bus", "text": "<p>The driver logged the final stop on time. The bus reached the depot empty and locked from inside.</p>", "link": "/blog/the-last-bus", "linkLabel": "Open the file" } }
|
|
]
|
|
},
|
|
{
|
|
"slug": "/blog/the-harbour-light",
|
|
"title": "The Harbour Light",
|
|
"template_key": "article",
|
|
"parent_slug": "/blog",
|
|
"blocks": [
|
|
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "The Harbour Light", "level": 1 } },
|
|
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>The watchman made his last radio check at four minutes past two. He was due at the gate by half past. He never signed out.</p><p>The lamp on Pier 9 was still lit at dawn. The tide had turned twice. No boat was reported missing. No body was found. The file was opened, worked for a season, and set aside.</p><p>We walked the pier at the same hour. The record and the place do not agree on one point. We are still checking which one is wrong.</p>" } }
|
|
]
|
|
},
|
|
{
|
|
"slug": "/blog/room-14",
|
|
"title": "Room 14",
|
|
"template_key": "article",
|
|
"parent_slug": "/blog",
|
|
"blocks": [
|
|
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "Room 14", "level": 1 } },
|
|
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>The guest signed the register in a hand that did not match the name. He paid a week in advance and asked for the quiet end of the hall.</p><p>On the fourth night the room went dark and stayed that way. The suitcase was still there. The bed was made. The window was latched from the inside.</p><p>The file lists three people who saw him arrive and none who saw him leave. We are trying to reconcile that.</p>" } }
|
|
]
|
|
},
|
|
{
|
|
"slug": "/blog/the-last-bus",
|
|
"title": "The Last Bus",
|
|
"template_key": "article",
|
|
"parent_slug": "/blog",
|
|
"blocks": [
|
|
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "The Last Bus", "level": 1 } },
|
|
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>The driver logged the final stop on schedule. Twelve minutes later the bus rolled into the depot. It was empty and the doors were locked from inside.</p><p>The fare box held the right count for a full run. The seats were cold. The last passenger on the ticket record never reached home.</p><p>We have the route, the timing, and the log. What we do not have is the twelve minutes.</p>" } }
|
|
]
|
|
},
|
|
{
|
|
"slug": "/contact",
|
|
"title": "Tip Line",
|
|
"template_key": "contact",
|
|
"blocks": [
|
|
{
|
|
"block_key": "contact-form",
|
|
"title": "Tip Line",
|
|
"slot": "main",
|
|
"sort_order": 1,
|
|
"content": {
|
|
"heading": "Submit a tip",
|
|
"description": "Tips are read in confidence. Tell us the case and what you know. Leave contact details only if you want a reply.",
|
|
"submitLabel": "Send tip",
|
|
"successMessage": "Thank you. Your tip has been logged. We read every one.",
|
|
"fields": [
|
|
{ "name": "name", "label": "Your name", "type": "text", "required": false, "width": "half" },
|
|
{ "name": "email", "label": "Email", "type": "email", "required": true, "width": "half" },
|
|
{ "name": "subject", "label": "Case or subject", "type": "text", "required": false, "width": "full" },
|
|
{ "name": "message", "label": "What do you know?", "type": "textarea", "required": true, "width": "full" }
|
|
],
|
|
"formConfig": { "enabled": true, "captchaEnabled": false, "targetTable": "case_tips" }
|
|
}
|
|
},
|
|
{
|
|
"block_key": "hours-location",
|
|
"title": "Where to find us",
|
|
"slot": "aside",
|
|
"sort_order": 1,
|
|
"content": {
|
|
"heading": "The Desk",
|
|
"hours": [
|
|
{ "day": "Monday to Friday", "hours": "9am to 6pm" },
|
|
{ "day": "Saturday", "hours": "By appointment" },
|
|
{ "day": "Sunday", "closed": true }
|
|
],
|
|
"specialHoursNote": "Tips are read outside these hours. Replies are not.",
|
|
"address": "Room 14, The Old Exchange\nEast Dock",
|
|
"phone": "+1 555 0199"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"slug": "/login",
|
|
"title": "Access",
|
|
"template_key": "auth",
|
|
"blocks": [
|
|
{ "block_key": "auth-form", "title": "Sign in", "slot": "main", "sort_order": 1, "content": { "default_tab": "login", "show_social": false } },
|
|
{ "block_key": "text", "title": "Foot", "slot": "footer", "sort_order": 1, "content": { "text": "<p>Registered readers get new files first and the sealed appendices as they clear review.</p>" } }
|
|
]
|
|
}
|
|
],
|
|
"menu_items": [
|
|
{ "menu": "main", "label": "Home", "page_slug": "/", "sort_order": 1 },
|
|
{ "menu": "main", "label": "The Case Files", "page_slug": "/blog", "sort_order": 2 },
|
|
{ "menu": "main", "label": "About the File", "page_slug": "/about", "sort_order": 3 },
|
|
{ "menu": "main", "label": "Tip Line", "page_slug": "/contact", "sort_order": 4 }
|
|
]
|
|
}
|