Ominvo beta launches July 23, 2026 — only 10 spots remain Join the waitlist →
All posts
Building Ominvo

The library had no door yet

July 2, 20265 min read

For weeks we've had good content scattered across /blog, /help, /roi-calculator, and /review-reply-playbook. Nobody looking at the site would know it's connected — four separate rooms with no hallway between them. Today shipped the door: /resources, a hub that gives the content library one address instead of four scattered ones.

What went out

  • A /resources hub mirroring /help's structure — hero, category grid, MDX-backed article routes, full sitemap coverage.
  • Three categories: the-basics, damage-control, the-numbers.
  • Five MDX article stubs — four marked featured for a "Four things we learned the expensive way" callout block, one non-featured that feeds a "More articles" section instead.
  • A FeaturedResources component that ships in two places from one file: on /resources itself, and on the homepage between FAQ and Pricing. Same four cards, same tagline, one source of truth — no copy-pasted JSX to drift out of sync later.
  • A Resources link in the primary nav (desktop and mobile) and the footer.

The stubs are stubs

I want to be straight about this part. The five articles that live under /resources right now are frontmatter and a bulleted outline, not finished writing. Days 70 through 74 replace each stub with a real 1,500-2,500-word article — starting tomorrow with What a fake review actually costs a small business — and 3 ways to fight it.

I'm not apologizing for shipping the frame before the words. I'm explaining the order: writing five full articles with no home for them, then bolting a hub onto them afterward, means retrofitting URLs, categories, and a featured-article system around content that's already locked in. Building the hub once, with the routing and the sitemap and the homepage integration all correct, and letting the articles land into slots that already work — that's the cheaper path. The stubs exist so the architecture has something real to render instead of an empty array.

The tagline

The homepage and /resources both carry the same line: "Four things about Google reviews we learned the expensive way. If you have a Google profile, one of them pays for itself." No fake urgency, no SHOCKING, no "you won't believe." Just the actual claim being made. If the four articles that follow don't deliver on it — if reading them doesn't save a business owner more than the five minutes it takes to read — the tagline is a lie, and lying in your own marketing copy about your own product content is the kind of thing that costs you the one thing a review-management company can't fake: trust in what it says.

The polish commit

Here's the honest arc of the day, in order. I shipped the /resources architecture, mounted FeaturedResources on the homepage, ran the type checker, saw green, and moved on. Then I loaded the actual page and looked at it.

The tagline was one flat block of text — a single sentence-and-a-half wrapped like body copy, no visual hierarchy. And the four cards sat directly above the Pricing section, which is the one place on this homepage where restraint doesn't read as elegant, it reads as thin. Pricing's tier cards are roughly 500px tall with heavy padding and a bold heading. My four resource cards were about half that size with a heading that just wrapped.

Fixed it in a third commit, not a rewrite:

  • Split the tagline into a heading (big, bold) and a subheading (smaller, softer) — reverse pyramid, top dominates, bottom supports. Mirrored Pricing's own heading and subheading classNames verbatim rather than inventing a new type scale.
  • Mirrored Pricing's tier-card className onto the FeaturedResources cards — same padding, same border weight, same background. The two sections now read like they came from the same page instead of two different products glued together.
  • Dropped the tablet two-across breakpoint entirely. It's one card per row on mobile, four across on desktop, nothing in between — a middle state nobody asked for and that just made the grid look indecisive at in-between widths.

This is the actual argument for shipping something, looking at the real rendered thing, and fixing what you see instead of what you specced. TSC passed clean through all three commits. It didn't catch any of this, because none of it was a type error — same lesson Day 67 already taught me about what "clean" actually verifies and what it doesn't.

A small technical note, for future us

The category browsing route lives at /resources/category/[slug] instead of the cleaner-looking /resources/[category]. That's not an accident — Next.js won't let two sibling dynamic route segments coexist at the same directory level, and /resources/[slug] is already spoken for by individual articles. A category page at /resources/[category] sitting next to it would collide the moment both tried to claim the same URL shape. The category/ prefix is a static segment that sidesteps the collision entirely. The URL is one segment longer than I'd like. It's stable, and it works. Worth remembering the next time a route naming choice looks obviously wrong at a glance — check whether a sibling dynamic segment is the reason.

Closing

The changelog has the full breakdown of what shipped today, commit by commit. Day 70 is the first real article — the fake-review-cost piece linked above. The URL is already live and already in the sitemap. The words show up tomorrow.

Written by

The founder of Ominvo

Building review management for single-location small businesses. Join the waitlist →