// content-journal.jsx — all journal posts, full content, keyed by slug.
// Body is an array of blocks rendered by page-post.jsx. Inline HTML allowed in p/lead/pull.
const JOURNAL = [
{
slug: 'studio-pricing-written-down',
date: 'Mar 3, 2026', dateISO: '2026-03-03', cat: 'Money', readMin: 7,
title: "A small studio's pricing,
written down.",
titlePlain: "A small studio's pricing, written down",
sum: "How I land on numbers, why I publish them, and what I'd charge if I started today. The money conversation, minus the squirming.",
hero: 'img/journal-1.jpg', heroCap: 'Most pricing decisions are made here, with a calculator and a guilty conscience.',
tags: ['Money', 'Running a studio', 'Pricing'],
body: [
{ t:'lead', html:'For years I did the thing every freelancer does: quoted a different number to every client based on vibes, then spent the project quietly resenting the ones I\'d under-charged. Writing the prices down fixed more than my income.' },
{ t:'p', html:'Published pricing is treated like a faux pas in this industry — "it depends," everyone says, "every project is different." And sure, it does, and they are. But "it depends" is also a wonderful place to hide, and I was hiding.' },
{ t:'h2', num:'01 / the floor', text:'Start from the year, not the project' },
{ t:'p', html:'I work backwards from an annual number I need to clear, divide by the realistic count of projects I can do well in a year — for me, around eighteen — and that quotient is the floor. Not the dream price. The floor. Anything below it means I\'m subsidizing a client with my own runway.' },
{ t:'pull', html:'A price isn\'t a reward for the work. It\'s the thing that lets the next piece of work happen.' },
{ t:'h2', num:'02 / the menu', text:'Three packages, on purpose' },
{ t:'p', html:'I offer three tiers — a fixed one-week site, a custom build, and a brand-plus-site — because a menu of three is a decision a human can actually make. Twelve options is not a menu, it\'s a negotiation, and the client always loses a negotiation against their own indecision.' },
{ t:'list', items:['One-Week Site — fixed scope, fixed price, no surprises.','Custom Build — designed and coded around you.','Brand & Site — identity plus the website to wear it.'] },
{ t:'aside', k:'The quiet part', paras:['Publishing prices is the best lead filter I have. The wrong-fit inquiries simply stop arriving. I lost the thrill of "winning" a cheap project and gained back about six hours a week of sales calls that went nowhere.'] },
{ t:'h2', num:'03 / the number', text:"What I'd charge starting today" },
{ t:'p', html:'If I were starting over with my current skills and zero reputation, I\'d still publish, and I\'d set the one-week site around $1,800 and custom builds north of $5,000 — and I\'d hold the line for the first ten clients even when it hurt, because the first ten set the anchor for everyone after.' },
{ t:'margin', text:'hold the line! it gets easier!' },
{ t:'mark' },
{ t:'p', html:'The honest summary: I make more, work with people I like more, and sleep better — not because I found a magic number, but because I stopped pretending the number was a secret. The whole calculator lives on the contact page if you want to poke at it.' },
],
},
{
slug: 'live-in-platform-mockups',
date: 'Jan 20, 2026', dateISO: '2026-01-20', cat: 'Process', readMin: 5,
title: 'The case for
live in-platform mockups.',
titlePlain: 'The case for live in-platform mockups',
sum: "I haven't opened Figma for client work in a year and a half. Here's what replaced it, and why my clients stopped being surprised at launch.",
hero: 'img/journal-2.jpg', heroCap: 'No artboards. The mockup is just the site, early.',
tags: ['Process', 'Tools', 'Squarespace'],
body: [
{ t:'lead', html:'The pixel-perfect Figma mockup is a beautiful lie. It promises the client something the browser can\'t always keep, and then I spend the build apologizing for the gap between the comp and the cursor.' },
{ t:'p', html:'So I stopped. For a year and a half now, the "mockup" I send clients is the actual site — rough, in-platform, clickable on their phone — built straight in Squarespace from the first week. It felt reckless the first time. Now I can\'t imagine working any other way.' },
{ t:'h2', num:'01 / why', text:'A static comp hides the hard parts' },
{ t:'p', html:'Figma never has to answer the question the browser asks constantly: what happens at 360 pixels wide? What about with the client\'s actual headline, which is nine words long, not three? Real content and real reflow are where designs live or die, and the comp is exactly where they\'re absent.' },
{ t:'pull', html:'A mockup the client can\'t click is a mockup that\'s lying about something.' },
{ t:'h2', num:'02 / what it costs', text:'It\'s slower at first, faster overall' },
{ t:'p', html:'Building in-platform is undeniably slower in week one. There\'s no infinite canvas, no "just nudge it" — every change is a real change. But I make that time back tenfold because there\'s no translation step: I\'m never rebuilding a Figma file in code, never discovering at handoff that the gorgeous comp doesn\'t actually work.' },
{ t:'aside', k:'When I still open Figma', paras:['For logo and identity work, where the artifact really is a static mark, vectors still win. The argument here is narrow: it\'s about websites, where the deliverable is a living thing in a browser.'] },
{ t:'h2', num:'03 / the client', text:'Surprise is the enemy' },
{ t:'p', html:'The best outcome of this method is the one nobody talks about: clients stop being surprised. They\'ve been clicking the real thing for weeks. Launch day becomes a quiet flip of a switch instead of a dramatic reveal — and quiet flips of a switch are the whole goal.' },
{ t:'margin', text:'launch day should be boring, in the best way' },
{ t:'mark' },
{ t:'p', html:'If you\'re a designer agonizing over whether to abandon the comp: keep it for the parts that are genuinely static, and let the browser do the rest. Your launches will get quieter, which is the highest compliment a launch can earn.' },
],
},
{
slug: 'five-squarespace-details',
date: 'Nov 11, 2025', dateISO: '2025-11-11', cat: 'Squarespace', readMin: 4,
title: 'Five Squarespace details
I tweak on every build.',
titlePlain: 'Five Squarespace details I tweak on every build',
sum: "Tiny CSS injections that make a stock template stop looking like one. None of these are clever — they're just the difference between fine and finished.",
hero: 'img/journal-3.jpg', heroCap: 'Five small habits, one custom CSS panel.',
tags: ['Squarespace', 'Process', 'CSS'],
body: [
{ t:'lead', html:'A stock Squarespace template looks like a stock Squarespace template for about five reasons, and all five are fixable in the custom CSS panel in under an hour. Here are the ones I do every single time, before I do anything else.' },
{ t:'h2', num:'01', text:'Fix the type scale' },
{ t:'p', html:'The default heading sizes are tuned for safety, not character. I almost always pull the largest heading up and tighten its line-height and letter-spacing. Big, confident type with negative tracking is the single fastest way to make a template stop looking like a template.' },
{ t:'h2', num:'02', text:'Kill the uniform spacing' },
{ t:'p', html:'Stock sites breathe in one rhythm — every section the same height, the same padding. Real editorial design has tempo: tight here, generous there. I vary section padding deliberately so the page has a pulse instead of a metronome.' },
{ t:'pull', html:'Templates fail at rhythm, not at looks. Fix the spacing and you\'ve fixed most of it.' },
{ t:'h2', num:'03', text:'Give links a real hover' },
{ t:'p', html:'Default underlines are fine. A considered hover — an underline that grows, a color that shifts on a 200ms ease — is the kind of thing nobody notices and everybody feels. It signals that a human cared.' },
{ t:'h2', num:'04', text:'Round the images, once' },
{ t:'p', html:'A consistent, small border-radius on every image and card — six pixels, applied globally — quietly unifies a page built from mismatched photography. Just pick one value and never deviate.' },
{ t:'h2', num:'05', text:'Tighten the buttons' },
{ t:'p', html:'Default buttons are slightly too tall and slightly too round. I trim the padding, set the radius to either fully-pill or barely-there (never the awkward in-between), and match the font to the body. Small, but it\'s the detail people unconsciously read as "professional."' },
{ t:'aside', k:'One rule', paras:['Pick your five tweaks and apply them with discipline before adding anything bespoke. Consistency is what reads as "designed" — not novelty.'] },
{ t:'mark' },
{ t:'p', html:'None of this is clever. That\'s the point. The gap between a fine site and a finished one is almost never a big idea — it\'s five small ones, applied without exception.' },
],
},
{
slug: 'onboard-clients-in-30-minutes',
date: 'Oct 7, 2025', dateISO: '2025-10-07', cat: 'Process', readMin: 5,
title: 'How I onboard new clients
in thirty minutes.',
titlePlain: 'How I onboard new clients in 30 minutes',
sum: 'A short call, three questions, one shared doc, zero back-and-forth. The least glamorous part of the job, made boring on purpose.',
hero: 'img/journal-4.jpg', heroCap: 'One call, one doc, and the project actually starts.',
tags: ['Process', 'Running a studio'],
body: [
{ t:'lead', html:'Onboarding used to eat a week. Emails, a questionnaire nobody filled out, a kickoff call that wandered, three follow-ups to get the logo files. Now it\'s a single thirty-minute call and one document, and the project starts the same day.' },
{ t:'h2', num:'01 / the call', text:'Three questions, that\'s it' },
{ t:'p', html:'I ask exactly three things, and I shut up between them: Who is this for? What should they do? What would make this a win in six months? Everything I need to design well is downstream of those three answers. Anything else can wait.' },
{ t:'pull', html:'Most kickoff calls fail because they try to gather everything instead of the right three things.' },
{ t:'h2', num:'02 / the doc', text:'One link, never an attachment' },
{ t:'p', html:'There\'s a single shared doc — sitemap, content checklist, asset drop-zone, timeline — and it is the only source of truth. No PDFs, no email threads, no "latest version final v3." If it isn\'t in the doc, it isn\'t happening, and everyone knows where to look.' },
{ t:'aside', k:'The asset trick', paras:['The doc has a clearly-labeled folder for logos, photos, and copy with an example of exactly what good looks like. Clients fill it because the bar is visible. Vague requests get vague results.'] },
{ t:'h2', num:'03 / the boredom', text:'Make it repeatable, then ignore it' },
{ t:'p', html:'The whole point is to make onboarding so boring and templated that it requires no thought — which frees all of my actual attention for the part that does. A dull, reliable start is what buys you an interesting middle.' },
{ t:'margin', text:'boring process = interesting work' },
{ t:'mark' },
{ t:'p', html:'If your onboarding is creative, that\'s a bug. Spend the creativity on the design. Spend the discipline on the doc.' },
],
},
{
slug: 'declining-sixty-percent-of-inbound',
date: 'Aug 19, 2025', dateISO: '2025-08-19', cat: 'Money', readMin: 6,
title: 'A year of declining
60% of inbound.',
titlePlain: 'A year of declining 60% of inbound',
sum: "What changed when I started saying no to wrong-fit projects — for the business, and for the much harder thing, my nerve.",
hero: 'img/journal-5.jpg', heroCap: 'Saying no is a skill you can practice. I practiced a lot.',
tags: ['Money', 'Running a studio'],
body: [
{ t:'lead', html:'A year ago I started keeping a tally: every inbound inquiry, and whether I said yes or no. By December I\'d declined just over sixty percent of them. My income went up. That sentence still feels like a typo.' },
{ t:'p', html:'Saying no is supposed to be the advanced move — the thing you earn the right to do once you\'re established. I did it scared, from a position of "what if the work dries up," and I want to report back from the other side.' },
{ t:'h2', num:'01 / the filter', text:'What earns a no' },
{ t:'p', html:'It\'s rarely the budget. It\'s the shape: a project that wants a fifth platform I don\'t do, a client who opens with "should be quick," a timeline that\'s already on fire before we\'ve met. I learned to read those signals in the first email instead of the third invoice.' },
{ t:'list', items:['"Should be a quick one" — it is never a quick one.','A platform outside my three — the answer is a warm referral.','Urgency before clarity — a fire I didn\'t start and can\'t put out.'] },
{ t:'pull', html:'Every wrong-fit yes is a right-fit project I won\'t have room to say yes to later.' },
{ t:'h2', num:'02 / the math', text:'Fewer projects, more income' },
{ t:'p', html:'The counter-intuitive part: declining more raised my income. Wrong-fit projects are the ones that overrun, generate unpaid support, and quietly burn the hours I\'d otherwise spend on well-fit work that pays properly. Cutting them was a margin decision dressed up as a lifestyle one.' },
{ t:'aside', k:'The referral habit', paras:['A no is more generous with a name attached. I keep a short list of people I trust for the work I don\'t take, so "not me" becomes "but try her." The client is helped, the colleague is fed, and the door stays open.'] },
{ t:'h2', num:'03 / the nerve', text:'It gets easier, then it gets quiet' },
{ t:'p', html:'The first few no\'s were physically uncomfortable. By the tenth it was routine. By the thirtieth I\'d stopped counting, because the pipeline had quietly reshaped itself around the work I actually wanted. The fear that the well would run dry was, it turns out, the thing keeping it shallow.' },
{ t:'margin', text:'the well refills when you stop drinking from puddles' },
{ t:'mark' },
{ t:'p', html:'I\'m not recommending recklessness. I\'m recommending a tally. Count your yeses and noes for a quarter, look at which ones you regret, and let the pattern give you permission you were waiting for someone else to grant.' },
],
},
{
slug: 'squarespace-to-shopify-migration',
date: 'Jun 24, 2025', dateISO: '2025-06-24', cat: 'Squarespace', readMin: 8,
title: 'Migrating Squarespace
to Shopify, properly.',
titlePlain: 'Migrating from Squarespace to Shopify, properly',
sum: 'A real-world checklist: redirects, content, SEO, the whole thing. The unglamorous work that decides whether a migration is invisible or a disaster.',
hero: 'img/journal-6.jpg', heroCap: 'A migration is 10% design and 90% spreadsheets.',
tags: ['Squarespace', 'Shopify', 'SEO', 'Migrations'],
body: [
{ t:'lead', html:'A migration done right is invisible — traffic holds, orders keep flowing, nobody notices but the analytics. A migration done wrong takes six months of search ranking down with it. The difference is almost entirely in the boring parts.' },
{ t:'p', html:'I\'ve moved enough stores between platforms to have a checklist I trust more than my memory. Here\'s the spine of it, in the order I actually do things.' },
{ t:'h2', num:'01 / before you touch anything', text:'Map the old site' },
{ t:'p', html:'Crawl the existing site and export every URL, every product, every page, every blog post. This list is the contract. Everything that exists today must have a known destination tomorrow — a matching page, or a deliberate redirect. Nothing gets to just vanish.' },
{ t:'pull', html:'The redirect map is the migration. Everything else is decoration.' },
{ t:'h2', num:'02 / the redirects', text:'One-to-one or bust' },
{ t:'p', html:'Squarespace and Shopify disagree about URL structure — product paths, collection paths, blog slugs, all different. Every old URL needs a 301 to its closest new equivalent. Lazy migrations dump everything to the homepage; that\'s how you tell search engines the old site died. One-to-one, or you bleed.' },
{ t:'aside', k:'The 12-minute window', paras:['I stage the entire Shopify build behind a password, rehearse the cutover, then flip DNS and redirects together. The longest a store has been "down" for me is about twelve minutes — and even that was just the DNS catching its breath.'] },
{ t:'h2', num:'03 / the content', text:"Don't trust the importer" },
{ t:'p', html:'Automated importers move data and mangle formatting. Product descriptions lose their structure, images lose their alt text, metadata quietly evaporates. I budget real hours to walk the catalog by hand afterward, because the importer\'s "done" and a human\'s "done" are very different claims.' },
{ t:'h2', num:'04 / after the flip', text:'Watch, don\'t celebrate' },
{ t:'p', html:'For two weeks post-launch I live in the analytics and the search console: watching for 404s the map missed, crawl errors, ranking wobbles. A migration isn\'t finished at launch. It\'s finished when a fortnight has passed and nothing bad happened.' },
{ t:'margin', text:'the launch is the middle, not the end' },
{ t:'mark' },
{ t:'p', html:'None of this is exciting and all of it is the job. A platform migration is a trust exercise with a search engine, and search engines reward the studios who did the spreadsheets. Want the design version of this story? The Beautygrass case study has the visible half.' },
],
},
{
slug: 'naming-things-youll-type-forever',
date: 'Apr 15, 2025', dateISO: '2025-04-15', cat: 'Working notes', readMin: 4,
title: "On naming things you'll
have to type forever.",
titlePlain: "On naming things you'll have to type forever",
sum: "CSS classes, file names, deploy environments, plant pots. A small meditation on the names that outlive the reason you chose them.",
hero: 'img/journal-7.jpg', heroCap: 'Some of these pots are named. I regret most of the names.',
tags: ['Working notes', 'Craft'],
body: [
{ t:'lead', html:'You name a CSS class at 11pm on a Tuesday, half-thinking, and then you type it four hundred times over the next three years. Naming is the most consequential five-second decision in the entire craft, and we treat it like nothing.' },
{ t:'p', html:'This is a small post about a small thing that turns out not to be small at all: the names you give the stuff you build, and the way they quietly govern every day you spend with it afterward.' },
{ t:'h2', num:'01', text:'Name for the reader, not the moment' },
{ t:'p', html:'A class called .hero2-fix-final made sense in the moment and makes a fool of you forever. The reader of a name is always future-you, tired and context-free, scanning for the thing. Name for that person. They deserve your kindness more than present-you deserves the shortcut.' },
{ t:'pull', html:'A good name is a letter to a stranger who happens to be you, later.' },
{ t:'h2', num:'02', text:'Boring beats clever' },
{ t:'p', html:'Clever names age like milk. .banner will mean "banner" in five years; the pun you were so pleased with will mean nothing to anyone, including its author. Boring names are a gift to the future precisely because they refuse to be interesting.' },
{ t:'aside', k:'Beyond code', paras:['This is true of deploy environments, shared docs, project folders, and — I say this from experience — the plants on the windowsill. Name the pothos "the pothos." Do not name it "Greg." You will have to explain Greg.'] },
{ t:'h2', num:'03', text:'The name outlives the reason' },
{ t:'p', html:'The reason you chose a name fades in a week. The name stays for the life of the thing. That asymmetry — five seconds of choosing, years of living with it — is the whole argument for slowing down at exactly the moment everyone speeds up.' },
{ t:'margin', text:'five seconds now, three years later' },
{ t:'mark' },
{ t:'p', html:'So: pause at the naming. It\'s the cheapest possible moment to be thoughtful, and the one you\'ll be most grateful for. Boring, clear, and for the reader. Every time.' },
],
},
];
Object.assign(window, { JOURNAL });