Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .okf/design/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ GENERATE). Surface-to-source table and the review contract:
* [Cover pipeline](cover-pipeline.md) - rebuilding course covers from the design spec
* [Course typography](course-typography.md) - the course-scoped reading scale and the specificity trap
* [Course-landing component rules](course-landing-components.md) - one loud element, 3 dark zones, and why the post-hero slot is not a router
* [Landing anatomy is settled; register is the variable](landing-anatomy.md) - the shared skeleton, and the register-pilot mechanics: copy stays identical, the accent dies on the inverted band, pinned gates stop guarding
55 changes: 55 additions & 0 deletions .okf/design/landing-anatomy.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,61 @@ Claude Design artifact `3dc819cf`, the 20.05 spec, and the 2026 external sweep
- Every number on the page passes [/content/claims-canon.md](/content/claims-canon.md)
BEFORE styling work starts; tenure derives from `site.Params.foundingYear`.

## Register-pilot mechanics (learned building pilots A and B)

**A pilot stub is a COPY of the previous pilot's, not a rewrite.** Pilot B's
stub was produced with `cp` and then three edits (title, description,
`register`/`font`); `diff` between the two stubs shows six lines and nothing
else. If the copy drifts, the demo compares two skins AND two texts and
measures neither. Give the pilots the same screenshot assertions for the same
reason - a copy edit that reaches only one pilot then fails a test.

**The accent that passes on paper can fail catastrophically when the band
inverts.** The dark testimonial/closing band is part of the settled anatomy, so
every register meets it, and a register whose own artifact has no dark band has
to invent one. Measured for the Artifact Editorial palette:

| pair | ratio | verdict |
|---|---|---|
| accent `#a91918` on paper `#fbf9f8` | 7.05:1 | AA |
| accent `#a91918` on ink `#1b1c1c` | **2.31:1** | FAIL - and this is what pilot A shipped |
| light label `#c9c2ba` on ink | 9.69:1 | AA |
| lightened accent `#e07a6e` on ink | 5.84:1 | clears 3:1 for the graphic rule |

So on an ink band: text takes a light neutral, the accent survives only as a
LIGHTENED cut on non-text rules, and a filled accent button becomes an inverted
paper one - an accent fill against a `#232424` card sits at ~2.2:1, a button
with no visible edge (WCAG 1.4.11). **Measure in the browser, not in the head**:
walking every text node's computed colour against its resolved background
checked 117 elements per viewport and is the only evidence that "every pair
passes" is a fact rather than an intention.

**A gate pinned to one pilot's path stops guarding the moment pilot N+1
lands.** `next_rail_test.rb` had the testimonial-verbatim and tenure-derived
gates pinned to the pilot-A path, so pilot B could have smoothed the Wozniak
quote undetected. Both now iterate the same `next/pilots/**/index.html` glob,
with the "found nothing" assertion inside the shared helper so no caller can
iterate zero pages and report green. Widen these when adding a pilot, and prove
it by breaking the new pilot's copy and watching the failure name that file.

**Self-hosted faces: one file per REGISTER when a register needs several.**
The stub's `font:` key carries one stylesheet and one preload, so a three-family
register ships `static/css/fonts-<register>.css` rather than three
`fonts-<family>.css` files and an edited shared template. Variable families
serve every declared weight from ONE latin binary, which is why two
`@font-face` rules can point at the same URL.

Ship only the weights the artifact actually uses - then **walk computed
`font-weight` in the rendered page**, because a declaration grep cannot see
inherited weights and a face asked for a weight it does not ship gets
FAKE-BOLDED by the browser. Missed exactly that way on 2026-08-21: JetBrains
Mono shipped 400 only, but the brand mark inherited 700 from `.rr-brand` and
the nav button inherited 600 from `:where(.rr-nav-links a)`, so the same mono
label rendered heavier in the nav than in the hero. The walk that catches it
resolves `fontFamily` per node and histograms `fontWeight`: every mono node
must read 400. Pin `font-weight` on any component that sets `font-family` to a
single-weight face.

# Citations

- Research + candidate shortlist: `docs/projects/2608-site-design-system/10-19-research/10.01-design-register-research-2026-08-reference.md`
Expand Down
42 changes: 42 additions & 0 deletions .okf/log.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,48 @@ make it green: restructure same-day entries under one heading, and add
`timestamp` to the 23 concepts missing it (anchored to each file's last commit
time, which is verifiable - never invented).

## 2026-08-21 - register pilot B, and the two ways a second pilot breaks the first pilot's gates

`/next/pilots/editorial/fractional-cto/` ships the "Artifact Editorial" register
over the SAME anatomy and the SAME words as pilot A - `diff` between the two
stubs is six lines (title, description, `register`, `font`). Added to
[/design/landing-anatomy.md](/design/landing-anatomy.md), which was also missing
from the design index until now.

Two findings worth more than the pilot itself:

**The accent dies when the band inverts.** The settled anatomy has a dark
testimonial/closing band, so every register meets it even when its own artifact
has none. Deep red `#a91918` is 7.05:1 on paper and **2.31:1** on ink `#1b1c1c` -
pilot A shipped exactly that miss. On ink, text takes a light neutral
(`#c9c2ba`, 9.69:1), the accent survives only as a lightened non-text rule
(`#e07a6e`, 5.84:1), and the filled CTA inverts to paper, because an accent fill
on the `#232424` card is ~2.2:1 - a button with no visible edge (1.4.11).
Verified by walking every text node's computed colour against its resolved
background in the browser: 117 elements at 1440 and at 390, zero failures.
Palette arithmetic in your head is not this evidence.

**Gates pinned to one pilot's path quietly stop guarding.** The
testimonial-verbatim and tenure-derived gates in `test/unit/next_rail_test.rb`
named the pilot-A path, so pilot B could have smoothed the Wozniak quote
undetected. Both now iterate `next/pilots/**/index.html` through one helper that
owns the "found nothing" assertion. Proved by smoothing pilot B's quote and
watching the failure name `next/pilots/editorial/fractional-cto/index.html`,
then reverting.

**A declaration grep cannot see an inherited weight, and the browser fake-bolds
the difference.** A multi-family register ships ONE
`static/css/fonts-<register>.css` (the stub's `font:` key is one stylesheet +
one preload) carrying only the weights the artifact uses - but picking those
weights by grepping `font-weight` declarations shipped a defect: JetBrains Mono
ships 400 only, while the brand mark inherited 700 from `.rr-brand` and the nav
button inherited 600 from `:where(.rr-nav-links a)`, so Chrome synthesized both
and the same mono label rendered heavier in the nav than in the hero. Caught by
the reviewer, not by any gate. The check is a computed-weight walk in the
rendered page - resolve `fontFamily` per node, histogram `fontWeight`: 55 mono
nodes, 53 at 400 and 2 synthesized before the fix, 55 at 400 after. Pin
`font-weight` on any component that sets `font-family` to a single-weight face.

## 2026-08-21 - the visual gate was blind by 50x, and green runs never refresh a baseline

`DEFAULT_SCREENSHOT_CONFIG` tolerance 0.02 -> **0.0001**
Expand Down
1 change: 1 addition & 0 deletions bin/qtest
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ PAGE_TESTS = {
"next-pilot" => "next_pilot",
# Design-register pilots on the same rail: pages/next-<register>.css
"next-rescue-room" => "next_rescue_room",
"next-editorial" => "next_editorial",
"simple-page" => "privacy_policy",
# critical/privacy-policy-critical.css maps by basename; alias it to the
# simple-page tests so a touch there doesn't abort as an unknown key
Expand Down
170 changes: 170 additions & 0 deletions content/next/pilots/editorial/fractional-cto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
title: Fractional CTO (Editorial pilot)
description: Design-register pilot B - "Artifact Editorial" skin over the settled fractional-CTO landing anatomy. Same copy as pilot A; only the register differs.
layout: landing
type: next
# Drives both the CSS file and the bundle name in layouts/next/landing.html,
# so pilots B and C reuse this template by changing one line.
register: editorial
# Self-hosted display faces for this register (themes/beaver/static/). Another
# register omits this key and ships no webfont at all.
font:
css: /css/fonts-editorial.css
preload: /fonts/source-serif-4-latin.woff2

nav:
links:
- name: Services
url: /services/
- name: Use Cases
url: /use-cases/
- name: Blog
url: /blog/
- name: Course
url: /course/tech-for-non-technical-founders-2026/
- name: Clients
url: /clients/
cta:
name: Book a free consultation
url: /free-consultation/

hero:
eyebrow: Fractional CTO
headline: A technical partner in days, not months of hiring
subhead: Your dev shop isn't working. Your investors want a technical update. You need someone who can audit the code, fix the team, and give you a straight answer.
primary:
name: Book a free consultation
url: /free-consultation/
secondary:
name: Book a 30-min call
url: /contact-us/
derisk: You keep the written assessment whether or not we work together.
# Markdown, rendered inline - the bold carries the emphasis the blueprint
# gave <strong>. Clutch rating is linked to the profile (claims-canon).
stats:
- Starts in **days**
- No equity, no lock-in
- "[**4.8**/5 on Clutch](https://clutch.co/profile/jetthoughts)"

monthOne:
headline: What a fractional CTO does in month one
items:
- Reads the codebase and tells you what state it's actually in
- Sits in on your standups and tells you which ones are theatre
- Writes the technical section of your investor update
- Decides what gets rebuilt and what you can live with
- Sends you the Friday report from week one

comparison:
eyebrow: The maths
headline: What you're comparing it against
fractionalLabel: Fractional CTO
fulltimeLabel: Full-time CTO
rows:
- label: Cost
fractional: A fraction of a full-time hire
fulltime: Full salary, benefits, and equity
- label: Equity
fractional: None
fulltime: Typically required
- label: Time to start
fractional: Days
fulltime: Months of hiring
- label: If it isn't working
fractional: You stop. No severance, no re-hire.
fulltime: Severance and a re-hire
- label: Best when
fractional: Pre-Series A, or stabilising after a bad build
fulltime: Post-Series A, with a team to own

steps:
eyebrow: How it starts
headline: Four weeks from first email to a plan you can act on
intro: You don't sign a retainer to find out what's wrong. The assessment comes first.
list:
- title: Code review first
body: "You send access. You get a written assessment: what's solid, what's fragile, what's going to cost you."
- title: A 30-minute call
body: We walk you through the assessment in plain English. Most founders take the document to their board whether or not they hire us.
- title: Week one on the ground
body: Your fractional CTO joins standups, reads the backlog, and sends the first Friday report.
- title: Month one plan
body: What gets rebuilt, what gets left alone, what it costs, in what order. You own the code and the plan.

clients:
eyebrow: Clients + case studies
headline: Clients stay for years, not quarters.
stats:
# `derived: tenure` is what makes the template compute the value from
# site.Params.foundingYear. Keyed on its own field, not on the label -
# a label-keyed branch falls back silently when someone rewords it.
- derived: tenure
label: Years of industry experience
- value: "5"
label: Years of average client relationship
- value: 8+
label: Years of average developer experience
cases:
- name: Agent Inbox
body: Hired us to build their real-estate communication platform, get it market-ready, and grow the team around it.
tech: [Rails, React, Node.js]
url: /clients/agent-inbox/
- name: Open Apply
body: Hired us to debug their school-admissions product, optimize the user experience, and streamline internal project management.
tech: [Rails, Hotwire, Tailwind]
url: /clients/open-apply/
- name: Mobile Coach
body: Hired us to support and extend the products around their enterprise chatbot platform, and to scale their R&D and engineering teams.
tech: [Rails, AWS, PostgreSQL]
url: /clients/mobile-coach/

testimonial:
eyebrow: From a client
# VERBATIM tail of the canon description in data/testimonials.yaml (Bruno
# Wozniak). The design blueprint had smoothed it to "They were detailed and
# precise, helping us find problems..." - a real person's words are not copy
# to tighten. Asserted by test/unit/next_rail_test.rb against the yaml.
quote: Their team was also detailed and precise, helping us to find problems before they appear. We knew exactly what was happening and where we were going the whole time.
attribution: Bruno Wozniak · Director of Engineering, PubNative
sourceName: verified Clutch review
sourceUrl: https://clutch.co/profile/jetthoughts
proof:
- "[**4.8**/5.0 on Clutch](https://clutch.co/profile/jetthoughts)"
- "**5-year** average client relationship"

closing:
headline: Still deciding?
body: The consultation is free. The assessment is yours either way.
cta:
name: Book a free consultation
url: /free-consultation/

footer:
columns:
- heading: Services
links:
- name: Fractional CTO
url: /services/fractional-cto/
- name: Fractional Product Management
url: /services/fractional-product-management/
- name: App/Web Development
url: /services/app-web-development/
- name: Talent Recruiting & Training
url: /services/talent-recruiting-training/
- heading: Proof
links:
- name: Clients
url: /clients/
- name: Use Cases
url: /use-cases/
- name: Friday report
url: /friday-report/
- heading: Learn
links:
- name: Blog
url: /blog/
- name: Course
url: /course/tech-for-non-technical-founders-2026/
- name: About us
url: /about-us/
---
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ zero FL-Builder export files remain in any slice.
| pagination | `list.html` (second bundle) | — (pagination.css only) | 0.2K → 0.2K | no-FL |
| next-pilot | `layouts/next/single.html` (repo root, NOT in theme) | pages/next-pilot.css | new 2026-08-21 | v2 clean-slate rail (ADR-0006): css-variables + navigation.css + footer.css only — no style.css/586/base-4/legacy-theme-skin. Serves `/next/*` staging pages |
| next-rescue-room | `layouts/next/landing.html` (repo root, NOT in theme) | pages/next-rescue-room.css | new 2026-08-21 | Design-register pilot A (10.01). The register skin is the bundle's ONLY file — not even css-variables — because the pilot renders its own nav and footer and must be judged on its own surface. Serves `/next/pilots/rescue-room/*`. A new register adds a row here, a skin file, and one line to the `$skins` dict |
| next-editorial | `layouts/next/landing.html` (repo root, NOT in theme) | pages/next-editorial.css | new 2026-08-21 | Design-register pilot B, "Artifact Editorial" (10.01). Same rule as pilot A — the skin is the bundle's only file. Serves `/next/pilots/editorial/*`. Its three webfonts ship as ONE `static/css/fonts-editorial.css`, keyed by register rather than by family, so the shared `font:` frontmatter key stays one stylesheet + one preload |

¹ careers was a byte-identical verbatim move (3086-layout2 had zero dead
nodes); the small growth vs 07-12 is upstream content drift (course v2 merge
Expand Down
1 change: 1 addition & 0 deletions layouts/next/landing.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
register adds one line here. */}}
{{- $skins := dict
"rescue-room" (resources.Get "css/pages/next-rescue-room.css")
"editorial" (resources.Get "css/pages/next-editorial.css")
-}}
{{- $resources := slice (index $skins $register) -}}
{{ partial "assets/css-processor.html" (dict "resources" $resources "bundleName" (printf "next-%s" $register)) }}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions test/system/desktop_site_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,18 @@ def test_next_rescue_room
assert_stable_screenshot "next/rescue-room"
end

# Design-register pilot B, "Artifact Editorial". Deliberately the SAME
# assertions as pilot A: the demo compares two skins over one set of words,
# so a copy edit that reached only one pilot has to fail here.
def test_next_editorial
visit "/next/pilots/editorial/fractional-cto/"

assert_text "A technical partner in days, not months of hiring"
assert_text "#{Time.now.year - 2008}+"

assert_stable_screenshot "next/editorial"
end

def test_free_consultation
visit "/"

Expand Down
11 changes: 11 additions & 0 deletions test/system/mobile_site_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,17 @@ def test_next_rescue_room
assert_stable_screenshot "next/rescue-room"
end

# Pilot B on a phone - same assertions as pilot A for the same reason (see
# the desktop test): one anatomy, one set of words, two skins.
def test_next_editorial
visit "/next/pilots/editorial/fractional-cto/"

assert_text "A technical partner in days, not months of hiring"
assert_text "What you're comparing it against"

assert_stable_screenshot "next/editorial"
end

def test_free_consultation
visit "/"
# Add more specific scoping for Talk to an Expert button
Expand Down
Loading
Loading