Alex Dunmow 6c03f6da6c feat(brutalist): Wave A override contract — 49 builtin overrides, 7 templates, seed
WO-TF-012. Neo-brutalist creative-studio theme brought up to the Wave A
OVERRIDE-CONTRACT (frozen against cms a0f07e2ae).

- Override all 49 mandatory builtin keys in templates/overrides/brutalist/
  (chrome, marketing, primitives, commerce, blog family, auth surface, 404),
  each carrying the brutalist chrome (chunky borders, hard offset shadows,
  hover snap, mono uppercase meta, candy accents) via a raw-CSS "bru-*" kit
  shipped in manifest.yaml. Load-bearing wiring (HTMX auth endpoints, signup
  form, contact-form submit + honeypot, facade scripts, lightbox) preserved
  verbatim.
- 7 page templates: default, full-width, landing, article, blog-index,
  contact, auth (fixed a bug where templates omitted the `dark` class so dark
  presets never activated).
- Blocks rework: removed 6 blocks that duplicated builtins (masthead,
  concrete_hero, meta_strip, caption_image, pull_quote, colophon) — now
  overrides; kept project_ledger; added persona blocks sticker_wall,
  changelog, spec_sheet (4 total).
- 4 dual-mode presets (all mode "both", 19 tokens light + dark): Concrete &
  Cadmium, Hazard, Riso, Acid.
- Bundled 8 latin-subset OFL woff2 (Space Grotesk, Archivo Black, Inter, IBM
  Plex Mono) in assets/fonts/ + LICENSES.md; fonts.json populated; all
  font-family via var(--font-*). RECOMMENDED_FONTS/ICONS updated;
  required_icon_packs declared.
- Email-safe themed wrapper (tables, inline styles) cleaned up.
- master_pages.json reworked to builtin navbar/hero/cta/footer.
- Demo seed (seed/seed.json + workflows.json, demo:true): home, about, blog
  (3 posts), contact (form + contact_submissions data table + row_inserted →
  email-admins workflow), login — studio voice.

Known codeless limitation: page-suggestions renders themed 404 chrome but the
similar-pages list (server-side DB query, no template provider) can't be
reproduced; auth-form captcha widget is server-generated and can't be emitted
(seed leaves captchaEnabled=false). Verified: make exit 0; check-safety
exit 0. Visual quality UNVERIFIED until Wave B.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 10:56:49 +08:00

213 lines
10 KiB
JSON

{
"demo": true,
"settings": {
"merge": {
"companyName": "HARDSET",
"tagline": "A creative studio for people who ship."
}
},
"data_tables": [
{
"key": "contact_submissions",
"name": "Project Enquiries",
"description": "Seeded contact-form submissions for the HARDSET demo. A submission becomes a row here and emails the site admins.",
"columns": [
{ "key": "name", "label": "Name", "type": "text", "required": true },
{ "key": "email", "label": "Email", "type": "email", "required": true },
{ "key": "project", "label": "Project type", "type": "text", "required": false },
{ "key": "message", "label": "Message", "type": "text", "required": true }
]
}
],
"pages": [
{
"slug": "/",
"title": "HARDSET",
"template_key": "landing",
"blocks": [
{
"block_key": "brutalist:sticker_wall",
"title": "Disciplines",
"slot": "main",
"sort_order": 1,
"content": {
"heading": "What we do",
"items": [
{ "text": "Brand", "tone": "accent", "size": "1.6rem" },
{ "text": "Web", "tone": "ink", "size": "1.6rem" },
{ "text": "Product", "tone": "paper", "size": "1.6rem" },
{ "text": "Motion", "tone": "accent", "size": "1.6rem" },
{ "text": "Type", "tone": "ink", "size": "1.6rem" },
{ "text": "Print", "tone": "paper", "size": "1.6rem" }
]
}
},
{
"block_key": "feature-grid",
"title": "Services",
"slot": "main",
"sort_order": 2,
"content": {
"eyebrow": "Services",
"heading": "How we work",
"layout": "card",
"columns": 3,
"items": [
{ "icon": "lucide:box", "title": "Brand systems", "text": "Names, marks, and the rules that hold them together." },
{ "icon": "lucide:code", "title": "Websites", "text": "Fast, accessible sites built to last past the launch." },
{ "icon": "lucide:zap", "title": "Product design", "text": "Interfaces that read clearly and ship on schedule." }
]
}
},
{
"block_key": "brutalist:project_ledger",
"title": "Selected work",
"slot": "main",
"sort_order": 3,
"content": {
"rows": [
{ "no": "01", "year": "2024", "client": "Independent record label", "role": "Brand system" },
{ "no": "02", "year": "2024", "client": "Climbing gym chain", "role": "Website" },
{ "no": "03", "year": "2023", "client": "Furniture maker", "role": "Product + web" },
{ "no": "04", "year": "2023", "client": "Community radio station", "role": "Identity" }
]
}
}
]
},
{
"slug": "/about",
"title": "The Studio",
"template_key": "default",
"blocks": [
{ "block_key": "heading", "title": "Heading", "slot": "main", "sort_order": 1, "content": { "text": "The Studio", "level": 1 } },
{ "block_key": "rich-text", "title": "Story", "slot": "main", "sort_order": 2, "content": { "html": "<p>HARDSET is a small studio. We take on a few projects at a time and give each one the whole team. No account layer, no handoff. You talk to the people doing the work.</p><p>We like clear briefs and hard deadlines. We ship. Then we help you run it.</p>" } },
{
"block_key": "brutalist:spec_sheet",
"title": "Studio facts",
"slot": "main",
"sort_order": 3,
"content": {
"heading": "Studio",
"rows": [
{ "label": "Founded", "value": "2019" },
{ "label": "Team", "value": "Six" },
{ "label": "Based", "value": "Melbourne" },
{ "label": "Focus", "value": "Brand, web, product" }
]
}
},
{
"block_key": "brutalist:changelog",
"title": "Recent",
"slot": "main",
"sort_order": 4,
"content": {
"heading": "Lately",
"entries": [
{ "version": "Now", "date": "This quarter", "title": "Booking two new projects", "note": "<p>We have room for two more brand or web projects this quarter.</p>" },
{ "version": "New", "date": "Last month", "title": "Two more designers", "note": "<p>The studio grew to six. Same way of working, more hands.</p>" }
]
}
},
{ "block_key": "quote", "title": "Quote", "slot": "main", "sort_order": 5, "content": { "style": "pull", "quote": "Make it obvious, then make it loud.", "attribution": "HARDSET" } }
]
},
{
"slug": "/blog",
"title": "Journal",
"template_key": "blog-index",
"blocks": [
{ "block_key": "card", "title": "Post: Borders", "slot": "main", "sort_order": 1, "content": { "layout": "vertical", "title": "In praise of a hard border", "text": "<p>Why a thick black line still does more work than a soft shadow.</p>", "link": "/blog/in-praise-of-a-hard-border", "linkLabel": "Read" } },
{ "block_key": "card", "title": "Post: Ship", "slot": "main", "sort_order": 2, "content": { "layout": "vertical", "title": "Ship the ugly first draft", "text": "<p>How shipping early keeps a project honest.</p>", "link": "/blog/ship-the-ugly-first-draft", "linkLabel": "Read" } },
{ "block_key": "card", "title": "Post: Type", "slot": "main", "sort_order": 3, "content": { "layout": "vertical", "title": "Set it in one typeface", "text": "<p>A short case for doing more with less type.</p>", "link": "/blog/set-it-in-one-typeface", "linkLabel": "Read" } }
]
},
{
"slug": "/blog/in-praise-of-a-hard-border",
"title": "In praise of a hard border",
"template_key": "article",
"parent_slug": "/blog",
"blocks": [
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "In praise of a hard border", "level": 1 } },
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>A soft shadow asks you to squint. A hard border does not. It draws the edge, holds the block, and tells you where one thing ends and the next begins.</p><p>We reach for the line first. It is honest. It prints. It reads on a bad screen in bright sun. When we want energy we add one loud colour and a hard offset. That is the whole trick.</p>" } }
]
},
{
"slug": "/blog/ship-the-ugly-first-draft",
"title": "Ship the ugly first draft",
"template_key": "article",
"parent_slug": "/blog",
"blocks": [
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "Ship the ugly first draft", "level": 1 } },
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>The first draft is meant to be wrong. Its job is to exist. Once it is on a real screen, the real problems show up and the fake ones fall away.</p><p>We ship early and often. It keeps the brief honest and the feedback specific. Polish comes last, after the shape is right.</p>" } }
]
},
{
"slug": "/blog/set-it-in-one-typeface",
"title": "Set it in one typeface",
"template_key": "article",
"parent_slug": "/blog",
"blocks": [
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "Set it in one typeface", "level": 1 } },
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>One family, used well, beats three used at random. Pick a face with range, learn its weights, and let scale do the talking.</p><p>We set most of a project in a single grotesque. Big for headlines, tight for meta, plain for body. The result reads as one voice.</p>" } }
]
},
{
"slug": "/contact",
"title": "Start a project",
"template_key": "contact",
"blocks": [
{
"block_key": "contact-form",
"title": "Project enquiry",
"slot": "main",
"sort_order": 1,
"content": {
"heading": "Start a project",
"description": "Tell us what you are building and when you need it. We reply within two working days.",
"submitLabel": "Send it",
"successMessage": "Thanks. Your enquiry is in. We will reply within two working days.",
"fields": [
{ "name": "name", "label": "Your name", "type": "text", "required": true, "width": "half" },
{ "name": "email", "label": "Email", "type": "email", "required": true, "width": "half" },
{ "name": "project", "label": "Project type", "type": "text", "required": false, "width": "full" },
{ "name": "message", "label": "What are you building?", "type": "textarea", "required": true, "width": "full" }
],
"formConfig": { "enabled": true, "captchaEnabled": false, "targetTable": "contact_submissions" }
}
},
{
"block_key": "contact-card",
"title": "Direct",
"slot": "aside",
"sort_order": 1,
"content": {
"heading": "Direct",
"columns": 1,
"channels": [
{ "icon": "lucide:mail", "label": "Email", "value": "studio@hardset.example", "link": "mailto:studio@hardset.example" },
{ "icon": "lucide:map-pin", "label": "Studio", "value": "Melbourne, AU" }
]
}
}
]
},
{
"slug": "/login",
"title": "Sign in",
"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>Clients sign in to review work and approve invoices.</p>" } }
]
}
],
"menu_items": [
{ "menu": "main", "label": "Home", "page_slug": "/", "sort_order": 1 },
{ "menu": "main", "label": "Studio", "page_slug": "/about", "sort_order": 2 },
{ "menu": "main", "label": "Journal", "page_slug": "/blog", "sort_order": 3 },
{ "menu": "main", "label": "Contact", "page_slug": "/contact", "sort_order": 4 }
]
}