diff --git a/.okf/log.md b/.okf/log.md index 0ece2980e..a7bf96b11 100644 --- a/.okf/log.md +++ b/.okf/log.md @@ -51,6 +51,36 @@ 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 - PR-2 pilot: two CSS traps and a claims fix, from adversarial review + +The styled D1 pilot shipped through author -> verifier -> fixes. Three durable +learnings from the verifier's HOLD: + +**`.parent a { }` outranks single-class components.** `.next-article a` (0,1,1) +beat both `.np-link-cta` and `.np-button` (0,1,0) - the page's ONE filled CTA +shipped black-on-ruby at 3.33:1, an AA failure, verified in pixels (385px of ink +glyphs where ruby was intended). The fix is one token: scope descendant link +rules as `:where(a)` so they carry (0,1,0) and components win on order. Applies +to every future rail page. + +**A rendered guard can be non-discriminating NOW and still be right.** The +tenure assertion (np-stat-value == now.Year - foundingYear) cannot distinguish +derived from frozen until 2027, because this year they are equal - documented in +the test rather than pretended away. + +**The rail sat outside both copy ratchets.** marketing_copy_test globbed +`themes/beaver/layouts/**` (not root `layouts/`) and its RENDERED_GLOBS had no +`next/**` - the one surface with template-authored marketing copy was the one +neither pass read. Both globs extended. + +Also: the composition swap (FAQs in, clients/testimonials out) silently dropped +every trust signal on the page - restored as a minimal canon-exact proof line +(4.8/5 on Clutch, profile linked, no review count). And FAQ 5 carried an +unsourced "15+ years of industry experience" CONTRADICTING the derived 18+ stat +on the same screen - removed at the frontmatter source, which also fixes the +LIVE page's JSON-LD. Measured payoff of the rail, per the repo's own evidence +form: next-pilot 3.1 KB gzip vs single-service 21.6 KB - minus 85.6%. + ## 2026-08-21 - Phase 2.3 shipped: course module eyebrow, dual derivation The course lesson eyebrow ships with a detail worth keeping: **the module number diff --git a/content/services/fractional-cto/index.md b/content/services/fractional-cto/index.md index 47fb36734..d3ac63592 100644 --- a/content/services/fractional-cto/index.md +++ b/content/services/fractional-cto/index.md @@ -30,7 +30,7 @@ faqs: - question: "How does the engagement model work?" answer: "We offer flexible engagement models including part-time (10-20 hours/week), project-based contracts, interim CTO roles during transitions, and strategic consulting relationships. Most clients start with a part-time arrangement that can scale up or down based on your needs and growth stage." - question: "What experience do your Fractional CTOs have?" - answer: "Our Fractional CTOs average 8+ years of development experience with 15+ years of industry experience. They've led technical teams, managed complex software projects, and helped companies scale from startup to enterprise level. All have hands-on experience with modern technology stacks and proven track records of successful product delivery." + answer: "Our Fractional CTOs average 8+ years of development experience. They've led technical teams, managed complex software projects, and helped companies scale from startup to enterprise level. All have hands-on experience with modern technology stacks and proven track records of successful product delivery." overview: headline: The power of a CTO at a fraction of the cost diff --git a/docs/projects/2608-site-design-system/20-29-strategy/20.07-design-competition-brief.md b/docs/projects/2608-site-design-system/20-29-strategy/20.07-design-competition-brief.md index 6dbea3a45..b02fa5e4d 100644 --- a/docs/projects/2608-site-design-system/20-29-strategy/20.07-design-competition-brief.md +++ b/docs/projects/2608-site-design-system/20-29-strategy/20.07-design-competition-brief.md @@ -14,7 +14,7 @@ course system, which is exactly what this competition steps back from. | Direction 1 "Artifact Editorial" (stitch) | DONE — project `4487418121858436329` (disclosure: generated under a ruby-locked brief, before fully-open) | | Direction 2 "Institutional Authority" (stitch) | DONE 2026-08-21 — same project, screen `7cbde8e2e694438d82138be4d6f7f3f9`. Dark midnight-navy/charcoal, IBM Plex Sans, blue accent `#3B82F6`, forensic-artifact narrative (audit report, accountability dashboard, recovery timeline). **Canon flags for judging:** invents a "90-day stabilization guarantee" (no such claim in canon — copy must not ship as-is); dark chrome contradicts the peer-set evidence ADR-0003 cited (all services peers light) — the panel weighs, not auto-rejects, since the brief was fully open. | | Claude Design run (Paul) | Project **created and seeded via DesignSync** from this session: "JetThoughts — From-Scratch Competition (2608)", id `a5f25203-6dd9-45c6-a978-5842152d1389` — contains BRIEF.md, both directions' DESIGN.md + full-page renders as preview cards. Paul opens claude.ai/design and iterates there. | -| Judging board (fable critic + ICP panel) | BLOCKED on the above | +| Judging board | **RUN 2026-08-21 — D1 wins 4–1** (tally below). Paul's Claude Design pass can still override; until then D1 is the working winner and PR-2 styles against it (reversible: CSS on a noindexed staging page). | --- @@ -77,3 +77,18 @@ action, not six. - Paul sees ONE side-by-side board per direction: homepage + single-service screens, 3-line verdict per lens, one "what this says about us" sentence. - **Paul picks.** Rejection feedback, if both fail, seeds direction 3. + + +--- + +## The vote — 2026-08-21 (four personas on sonnet, fable critic in main loop) + +| Voter | Vote | Decisive reason | Objection to own pick | +|---|---|---|---| +| Alex, burned founder | **D2** | "Vague empathy is what the last shop gave me" — D2's specific failure-naming (Opaque Box, Misaligned Clock, Debt Spiral) reads as competence | dark enterprise-navy is the aesthetic of the shops that burn founders; slick dashboards are cheap to fake | +| CFO skeptic | **D1** | D2's fabricated "90-day guarantee" is a due-diligence disqualifier — one invented promise poisons every other number on the page. "Vague survives a diligence call; fabrication survives it with a lie or a retraction." | D1's informality is itself a red flag at a five-figure monthly spend | +| Mobile commuter | **D1** | D2's failure is structural (illegible dashboard hero, fixed-1280 grid rework); D1's is cosmetic (headline reflow) | D1's flat-lay photography reads as agency gloss | +| Brand strategist | **D1** | D1 is a *reading system* that carries blog/course/case-studies; D2 is a landing-page costume that fights body text everywhere else | evidentiary calm might read slow to a founder bleeding runway | +| **Fable critic** (render inspection) | **D1** | D2 has visible execution defects: hero + Phase-01 + final-CTA text renders washed-out pale on WHITE (near-invisible), and the page alternates white/black bands — the exact background-switch disease 2.4 exists to cure. D1's defects (staged wood styling ×2, two CTA idioms) are cosmetic. | D1's tablet "weekly report" mock is its weakest artifact — replace with a real report render | + +**Synthesis worth grafting regardless of winner:** D2's *problem-naming specificity* (Opaque Box / Misaligned Clock / Debt Spiral) is the single thing Alex trusted most — port that COPY register into D1's visual system, minus the fabricated guarantee. And D1's "VIEW A REAL AUDIT" link-as-primary-action is the best trust move on either page — keep it. diff --git a/layouts/next/single.html b/layouts/next/single.html index 79aa99ec4..00d283d55 100644 --- a/layouts/next/single.html +++ b/layouts/next/single.html @@ -22,10 +22,64 @@ {{- with site.GetPage . -}}{{- $src = . -}}{{- end -}} {{- end -}}
-

{{ $src.Params.headline | default $src.Title }}

- {{- with $src.Params.excerpt }} -

{{ . }}

+
+

{{ $src.Params.headline | default $src.Title }}

+ {{- with $src.Params.excerpt }} +

{{ . }}

+ {{- end }} + Talk to an expert +
+ + {{- with $src.Params.overview }} +
+

Service overview

+

{{ .headline }}

+ {{- range .list }} +
+

{{ .name }}

+ {{ .value | markdownify }} +
+ {{- end }} + {{- with .outcome }} + + {{/* Minimal proof line (review F3): the composition swap dropped every + trust signal; this restores the one that is canon-exact - 4.8/5 on + Clutch with the profile linked, NO review count (claims-canon). */}} +

Rated 4.8/5 on Clutchread the verified reviews.

+ {{- end }} +
+ {{- end }} + + {{- with $src.Params.faqs }} +
+

Common questions

+
+ {{- range . }} +
{{ .question }}
+
{{ .answer | markdownify }}
+ {{- end }} +
+
{{- end }} + {{ $src.Content }} + +
+

Let’s get started now

+

Get in touch with one of our experts to get a technical strategy & planning session for your project at no cost.

+ Contact Us +
{{ end }} diff --git a/test/fixtures/screenshots/linux/desktop/next/pilot.png b/test/fixtures/screenshots/linux/desktop/next/pilot.png index 8ba0eb83f..9eab6881d 100644 Binary files a/test/fixtures/screenshots/linux/desktop/next/pilot.png and b/test/fixtures/screenshots/linux/desktop/next/pilot.png differ diff --git a/test/fixtures/screenshots/linux/mobile/next/pilot.png b/test/fixtures/screenshots/linux/mobile/next/pilot.png index dac1eb47a..0e9321ad5 100644 Binary files a/test/fixtures/screenshots/linux/mobile/next/pilot.png and b/test/fixtures/screenshots/linux/mobile/next/pilot.png differ diff --git a/test/fixtures/screenshots/macos/desktop/next/pilot.png b/test/fixtures/screenshots/macos/desktop/next/pilot.png index dd998224f..5f2acbf72 100644 Binary files a/test/fixtures/screenshots/macos/desktop/next/pilot.png and b/test/fixtures/screenshots/macos/desktop/next/pilot.png differ diff --git a/test/fixtures/screenshots/macos/mobile/next/pilot.png b/test/fixtures/screenshots/macos/mobile/next/pilot.png index 28114ad6c..ebaa3bd89 100644 Binary files a/test/fixtures/screenshots/macos/mobile/next/pilot.png and b/test/fixtures/screenshots/macos/mobile/next/pilot.png differ diff --git a/test/unit/marketing_copy_test.rb b/test/unit/marketing_copy_test.rb index 78b51ea30..9d4ba1609 100644 --- a/test/unit/marketing_copy_test.rb +++ b/test/unit/marketing_copy_test.rb @@ -35,6 +35,7 @@ class MarketingCopyTest < Minitest::Test "content/pages/**/*.md", "content/services/**/*.md", "content/use-cases/**/*.md", + "layouts/next/**/*.html", # the v2 rail is template-authored marketing copy on root layouts "themes/beaver/layouts/home.html", "themes/beaver/layouts/page/*.html", # Blog CHROME (list hero, section furniture) is a marketing surface even @@ -119,7 +120,7 @@ def test_every_declared_surface_matches_at_least_one_file # original authors and are governed by the separate dev.to ICP gate, not this # ratchet. The excluded set is DERIVED from `source: dev_to` frontmatter, so # it tracks the imports instead of rotting as a hand-typed list. - RENDERED_GLOBS = %w[blog/**/*.html course/**/*.html services/**/*.html].freeze + RENDERED_GLOBS = %w[blog/**/*.html course/**/*.html services/**/*.html next/**/*.html].freeze # Paginated list views (/blog/page/7/, /blog/tags/startup/page/4/) only # re-print excerpts from posts this pass already counts, so every hit on them diff --git a/test/unit/next_rail_test.rb b/test/unit/next_rail_test.rb index 4fad8ecf1..ec09d6ae7 100644 --- a/test/unit/next_rail_test.rb +++ b/test/unit/next_rail_test.rb @@ -28,6 +28,19 @@ def test_next_pages_are_absent_from_the_sitemap # The stub mirrors a real page; the copy lives there, so that URL is the # canonical one - and exactly one canonical may ship. + # HONEST LIMIT: in 2026 the derived value equals the frozen frontmatter 18, + # so this assertion cannot distinguish them until 2027-01-01 - from then on + # it bites forever. Kept rendered-level per repo doctrine (no template greps). + def test_tenure_stat_is_derived_not_frozen + doc = parse_html_file(PILOT) + tenure = doc.css(".np-stat-value").map(&:text).find { |t| t.include?("+") } + expected = "#{Time.now.year - 2008}+" + assert_includes doc.css(".np-stat-value").map(&:text), expected, + "tenure stat must derive from foundingYear (#{expected}), not the frozen frontmatter 18 - " \ + "the label-keyed branch in layouts/next/single.html silently falls back if the label is renamed" + refute_nil tenure + end + def test_next_pages_canonicalise_to_their_source_page canonicals = parse_html_file(PILOT).css("link[rel='canonical']") diff --git a/themes/beaver/assets/css/pages/next-pilot.css b/themes/beaver/assets/css/pages/next-pilot.css index f25fa2258..e1b38231e 100644 --- a/themes/beaver/assets/css/pages/next-pilot.css +++ b/themes/beaver/assets/css/pages/next-pilot.css @@ -6,9 +6,24 @@ * a /next/ page renders comes from foundations/css-variables.css, the two * reused chrome stylesheets (navigation.css, footer.css), and this file. * - * Deliberately near-empty: PR-2 styles the pilot per the winning design - * direction. House style written here before that decision is work done - * twice — the exact failure mode this rail exists to stop. */ + * Design direction: D1 "Artifact Editorial" (won the 4-1 vote, 2026-08-21). + * Warm paper surface, charcoal ink, brand ruby ONLY as hairline rules / small + * caps labels / the single filled CTA. Sharp 0px corners; depth from 1px + * outlines and hairline rules (--line) — the #fff-on-paper tonal step is 1.05:1, near-invisible; the lines do the work, never shadows. */ + +:root { + --np-paper: #fbf9f8; + --np-card: var(--surface); + --np-ink: #1b1c1c; + --np-ink-muted: #5a5a58; + /* No serif/grotesk is self-hosted in this repo (only Caveat and Space + Grotesk are, and neither is the D1 pair), and the visual tests must stay + hermetic - no external font fetch. Source Serif 4 / Hanken Grotesk are + named first so the intended faces apply where the visitor has them, with + a system stack behind. Self-hosting them is its own PR. */ + --np-serif: "Source Serif 4", "Iowan Old Style", Georgia, "Times New Roman", serif; + --np-sans: "Hanken Grotesk", var(--font-system-ui); +} *, *::before, @@ -16,11 +31,17 @@ box-sizing: border-box; } +/* navigation.css:20 sets a site-wide `body { font-size: 18px; line-height: + 1.6; color: #121212 }` and it is inlined ahead of this file in the bundle, + so every one of those three has to be restated here or it leaks into the + rail (ADR-0006 trap). */ body { margin: 0; - background: var(--surface); - color: var(--ink-700); - font-family: var(--font-system-ui); + background: var(--np-paper); + color: var(--np-ink); + font-family: var(--np-sans); + font-size: 17px; + line-height: 1.65; } /* Rail-level, not page-level: the reused site header is `position: absolute` @@ -34,11 +55,206 @@ main#main-content { } .next-article { - max-width: 68ch; + max-width: 40rem; margin: 0 auto; - padding: var(--spacing-md) var(--spacing-sm); + padding: 0 1.25rem 5rem; +} + +.next-article h1, +.next-article h2, +.next-article h3 { + font-family: var(--np-serif); + font-weight: 600; + letter-spacing: -0.01em; + line-height: 1.15; + margin: 0; +} + +.next-article :where(a) { + color: var(--np-ink); + text-decoration-color: var(--color-ruby); + text-underline-offset: 2px; +} + +/* Small caps label - one of the three sanctioned ruby uses. */ +.np-label { + margin: 0 0 0.75rem; + color: var(--color-ruby); + font-size: 0.75rem; + font-weight: 600; + letter-spacing: 0.14em; + text-transform: uppercase; +} + +/* ---- hero ---- */ + +.np-hero { + padding: 2rem 0 2.5rem; +} + +.np-hero h1 { + font-size: clamp(2rem, 6vw, 3.25rem); +} + +.np-lede { + margin: 1.25rem 0 0; + color: var(--np-ink-muted); + font-size: 1.125rem; + line-height: 1.6; +} + +/* Link-with-arrow, deliberately NOT a button: the page carries exactly one + filled CTA and it is at the bottom. */ +.np-link-cta { + display: inline-block; + margin-top: 1.5rem; + color: var(--color-ruby); + font-weight: 600; + text-decoration: none; + border-bottom: 1px solid var(--color-ruby); + padding-bottom: 2px; } -.next-article h1 { - color: var(--ink-900); +/* ---- editorial sections ---- */ + +/* The Rule of Red Line: a section opens on a ruby hairline. */ +.np-section { + border-top: 1px solid var(--color-ruby); + padding-top: 2rem; + margin-top: 3rem; +} + +.np-section > h2:not(.np-label) { + font-size: clamp(1.5rem, 4vw, 2rem); + margin-bottom: 2rem; +} + +.np-block + .np-block { + border-top: 1px solid var(--line); + margin-top: 2rem; + padding-top: 2rem; +} + +.np-block h3 { + font-size: 1.125rem; + margin-bottom: 0.5rem; +} + +.np-block p { + margin: 0; + color: var(--np-ink-muted); +} + +/* ---- outcome stats ---- */ + +.np-stats { + display: grid; + grid-template-columns: 1fr; + gap: 1px; + background: var(--line); + border: 1px solid var(--line); + list-style: none; + margin: 3rem 0 0; + padding: 0; +} + +.np-stats li { + background: var(--np-card); + padding: 1.5rem; +} + +.np-stat-value { + display: block; + font-family: var(--np-serif); + font-size: 2.5rem; + font-variant-numeric: tabular-nums; + line-height: 1; +} + +.np-stat-label { + display: block; + margin-top: 0.5rem; + color: var(--np-ink-muted); + font-size: 0.875rem; +} + +/* ---- FAQ ---- */ + +.np-faq { + margin: 0; +} + +.np-faq dt { + border-top: 1px solid var(--line); + padding-top: 1.5rem; + font-family: var(--np-serif); + font-size: 1.125rem; + font-weight: 600; +} + +.np-faq dt:first-child { + border-top: 0; + padding-top: 0; +} + +.np-faq dd { + margin: 0.5rem 0 1.5rem; + color: var(--np-ink-muted); +} + +.np-faq dd p { + margin: 0; +} + +/* ---- closing CTA (the only filled button on the page) ---- */ + +.np-close { + border: 1px solid var(--line); + background: var(--np-card); + margin-top: 4rem; + padding: 2.5rem 1.5rem; + text-align: center; +} + +.np-close h2 { + font-size: 1.75rem; +} + +.np-close p { + margin: 1rem auto 2rem; + max-width: 32rem; + color: var(--np-ink-muted); +} + +.np-button { + display: inline-block; + background: var(--color-ruby); + color: #fff; + padding: 0.875rem 2rem; + font-weight: 600; + text-decoration: none; +} + +.np-button:hover { + background: var(--ruby-700); +} + +@media (min-width: 40rem) { + .np-stats { + grid-template-columns: repeat(3, 1fr); + } + + .np-close { + padding: 3.5rem 3rem; + } +} + +/* Review F3: the minimal canon-safe proof line under the stats. */ +.np-proof { + margin-top: 1rem; + font-size: 0.95rem; + color: var(--np-ink-muted); +} +.np-proof a { + color: var(--color-ruby); }