{ "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": "

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.

We like clear briefs and hard deadlines. We ship. Then we help you run it.

" } }, { "block_key": "brutalist:spec_sheet", "title": "Studio facts", "slot": "main", "sort_order": 4, "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": 5, "content": { "heading": "Lately", "entries": [ { "version": "Now", "date": "This quarter", "title": "Booking two new projects", "note": "

We have room for two more brand or web projects this quarter.

" }, { "version": "New", "date": "Last month", "title": "Two more designers", "note": "

The studio grew to six. Same way of working, more hands.

" } ] } }, { "block_key": "quote", "title": "Quote", "slot": "main", "sort_order": 3, "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": "

Why a thick black line still does more work than a soft shadow.

", "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": "

How shipping early keeps a project honest.

", "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": "

A short case for doing more with less type.

", "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": "rich-text", "title": "Body", "slot": "main", "sort_order": 1, "content": { "html": "

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. We reach for the line first because it is honest.

The border is the cheapest structure in design. One rule, one weight, and a page stops being a soup of floating cards. It prints. It reads on a bad screen in bright sun. It survives a screenshot pasted into a slide deck at quarter size.

We came to it the hard way. Early on we chased depth — blurs, gradients, shadows stacked three deep to fake a little lift. It looked fine in the file and fell apart in the wild. On a cheap monitor the shadows vanished and the whole layout went flat.

A border never vanishes. It is one pixel or it is four, and it is there in every rendering context you will ever ship into. When a client forwards a mockup to their printer, the printer sees the same edges we did.

There is a discipline in it too. A border forces you to decide where a thing begins. You cannot let a section bleed vaguely into the next and hope the eye sorts it out. You commit to the edge, and the commitment shows.

Weight is the only variable that matters. A hairline says these belong together, quietly. A four-pixel rule says stop here, this is a wall. We keep two weights on most projects and never more than three. Past that the borders start arguing with each other.

Colour is where the energy comes from. The border stays black. Then we add one loud accent — a single hot hue — and let it do all the shouting. Black holds the structure; the accent carries the mood. That split keeps a loud page from turning to noise.

Offset is the last trick. Take a bordered block and drop a hard shadow four pixels down and right — no blur, just a second solid shape. Now the block sits on the page like a physical object. It reads as printed, stamped, real. It is the whole brutalist move in one property.

None of this is nostalgia. We are not printing zines. We are building interfaces that have to survive resizing, dark mode, reduced motion, and a decade of browser churn. Hard edges survive all of it because there is nothing to degrade.

So we start with the line. Draw the edge, hold the block, pick one loud colour, offset the shadow. That is the whole trick, and it has never once let us down on a real screen in bright sun.

" } } ] }, { "slug": "/blog/ship-the-ugly-first-draft", "title": "Ship the ugly first draft", "template_key": "article", "parent_slug": "/blog", "blocks": [ { "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 1, "content": { "html": "

The first draft is meant to be wrong. Its only job is to exist. Once it is on a real screen, the real problems show up and the fake ones you were bracing for quietly disappear.

We ship early and we ship rough. A grey box where the photo goes, placeholder copy that says what it needs to say, a layout held together with the bluntest possible structure. Ugly, but real, and in front of someone who can react to it.

The point is to move the argument out of the abstract. Nobody can usefully review a description of a page. Everybody can react to a page. The ugly draft turns opinions into decisions because there is finally something concrete to point at.

It also keeps us honest. A polished mock hides its weak spots under nice type and a good photo. A rough one has nowhere to hide — if the structure is wrong, the rough draft shows it on day one, while it is still cheap to fix.

Polish comes last, after the shape is right. We would rather spend the back half of a project making a correct thing beautiful than the front half making a wrong thing pretty. Ship the ugly draft. Let it take the hits. Then make it good.

" } } ] }, { "slug": "/blog/set-it-in-one-typeface", "title": "Set it in one typeface", "template_key": "article", "parent_slug": "/blog", "blocks": [ { "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 1, "content": { "html": "

One family, used well, beats three used at random. Pick a face with real range, learn its weights, and let scale do the talking. Most projects need far less type than they think.

Range is the thing to look for. A grotesque with a heavy black, a workable regular, and a tight mono cousin can carry an entire site — headlines, body, meta, code — without ever reaching for a second family.

The discipline pays off in coherence. When every word on a site comes from one voice, the page reads as one thing. Mixing families is how sites end up feeling like a ransom note assembled from three different brands.

It pays off in engineering too. One family is fewer font files, fewer weights to load, less layout shift while they arrive. The performance budget thanks you, and so does anyone on a slow connection.

We set most of a project in a single grotesque. Big and tight for headlines, plain for body, mono for the small print. The result reads as one voice speaking at different volumes — which is exactly what a brand should sound like.

" } } ] }, { "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": "

Clients sign in to review work and approve invoices.

" } } ] } ], "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 } ] }