Landing flow + cohesion rework — cut the Features beat (re-home its guarantees to the OKF beat + loop and its one net-new card to a direct-adopter door), reframe the hero stakes-first, and restructure get-started into two equal doors
0049-landing-flow-cohesion-rework-cut-features-stakes-hero
- Reversibility
- two-way door
DEC-0049 — Landing flow + cohesion rework (cut Features, stakes-first hero, two equal doors)
Reversibility: two-way door — a content-model + flow refactor on the DEC-0037 template, revertible by restoring the FeatureContent shape + the features array and the {earlyAccess, partner} getStarted shape. The durable parts are the editorial calls (one beat fewer; stakes-first hero) and the "universal value props live structurally in okf+loop, not in a parallel features list" invariant that removes the duplicate restatement.
This continues the DEC-0037 polish line (after the length/rhythm pass and the two-new-beats pass, both recorded as notes atop DEC-0037). It is recorded as its OWN record because it makes product/editorial decisions (cut a beat, reframe the hero stakes-first, two equal doors) on top of the contract evolution — a consequential change worth an atomic record, not just an annotation. All five DEC-0037 invariants still hold by construction (no theme field added; universal messaging preserved; the OKF artifact round-trip untouched; both instances conform to the same type).
Context
The FDE ran a Product-Owner + UX teardown of the @dossier/app landing (/). Two product
decisions came back locked (implement, do not relitigate):
The Features beat earns its cut. The five feature cards re-stated, as prose, guarantees the page already makes structurally: sovereignty (
okf.properties"Atomic Markdown + YAML"- "In your own git" +
okf.lead), provenance (okf.properties"Typed + sourced + scored" → "verified, asserted, or inferred"), GraphRAG explainability (okf.payoff→ "the atoms and edges it actually traversed, never a black box"), and "humans curate, agents extend" (loop.returnArcLabel). The array was a duplicate restatement — cutting it removes a beat and a screen of length without weakening invariant #3. Its one net-new card, "Run it yourself" (direct adoption, Market to every organization; agencies are the highest-leverage channel, not a gate), is the only content that wasn't said elsewhere — so it was re-homed as a real get-started door.
- "In your own git" +
The hero leads with stakes, not mechanism. The old headline ("Your knowledge is just files in your git.") described the mechanism; the approved reframe leads with what's at stake ("The institutional memory you own, not rent.") and lets the subhead carry the mechanism. The eyebrow moved from "Institutional memory, owned" (which now echoed the new headline) to "A sovereign learning loop" (names the product). The secondary CTA stopped leaving the page ("See it on GitHub", new-tab) and became an in-page jump to the live-surfaces section ("See it running",
#surfaces) — GitHub stays reachable in the shared header + footer.
Decision
- Schema (
schema.ts): removedFeatureContent,featuresHead,features,EarlyAccessContent,PartnerContent(no dead code). Added a single sharedDoorContentshape (kicker/title/body/submitLabel/emailLabel/emailPlaceholder+ optionalnote?: { label; placeholder }) and restructuredGetStartedContentinto{ head: SectionHead; selfServe: DoorContent; partner: DoorContent }— two EQUAL doors under a shared "two ways in" head. The CaptureFormtypeis hardcoded per door in the component (selfServe → early-access,partner → design-partner), never a content field (CaptureForm.svelte is the SSOT and stays untouched). - Instances (
dossier.ts,examples/rba.ts): applied the stakes-first hero copy verbatim to Dossier; RBA's hero (a valid #4 tailorable instance) was kept as-is (only its secondary CTA label moved to "See it running" to conform). Both droppedfeaturesHead/featuresand restructuredgetStartedto the two-door shape, re-homing each instance's "Run it yourself" feature body intoselfServe. - Component (
LandingPage.svelte): removed the Features<section>+ its$derivedconsts; changed the hero secondary anchor to in-page#surfaces; added a quiet dual-audience hero affordance routing agencies to#get-started(DEC-0023, no gating); gave the Surfaces sectionid="surfaces"+ a left-aligned, beat-separated section-head; rendered get-started as two peer.doorcards (partner door keeps a clay accent edge). The now-unusedgithubHrefconst was removed (header/footer own the GitHub link). - Cohesion polish (
landing.css, tokens-only, zero hex): in-card kicker → clay (.surface__kicker);.section-head--left+.section-head--beatmodifiers (center → left → left → center rhythm; a hairline beat-separator on the OKF + Surfaces interior heads); a monotonic type ramp (weight-lightonly at ≥--ds-text-5xl—.getstarted__title5xl-light → 4xl-regular, the door title 3xl-light → 3xl-regular); a tighter.hero + .okfgap (--ds-space-24); hero-headline leading--ds-leading-none→--ds-leading-tight; door-card hover-lift +:focus-withinkeyboard parity mirroring the surface card. The resting card-shadow was explicitly HELD (brand restraint — door/surface cards stay flat-until-hover). Dead CSS removed: the.features/.feature*rules + their micro-interaction, the old.getstarted__partner/.partner__*selectors (re-mapped onto.door), and the now-orphaned.landing-external+--landing-arrow-neexternal-arrow gesture (its only consumer — the external GitHub secondary CTA — went in-page). - Skill (
generate-landing/SKILL.md): dropped thefeatures/featuresHeadtailorable slot; added theokf.lead/okf.payoff/okf.properties[].detailandgetStarted.{head,selfServe, partner}slots; rewrote the UNIVERSAL section to state plainly that there is no features array anymore and the value props live in the OKF beat + loop (re-frame those, never reintroduce a features list). - Tests (
landing-rba.test.ts): the#3universal-messaging assertions that readrbaLanding.features[...]were re-pointed to the structural homes — sovereignty →okf.propertiesterms + details; provenance → the "Typed + sourced + scored" property detail; GraphRAG →okf.payoff; "humans curate, agents extend" →loop.returnArcLabel; plus a new assertion that the self-serve door carries the days-long-install reframe. The.featuresassertions were removed; the#4hero block (RBA hero unchanged) was kept.
Why a stakes-first hero + structural (not parallel) value props
A first-time visitor decides on stakes ("own, not rent"), then learns the mechanism (plain files in your git). Leading with the mechanism buried the stakes. And a parallel "features" list that re-states guarantees the page already makes structurally is redundant surface area — it reads as filler and lengthens the page. Keeping the value props only in their structural homes (the format's own properties; the loop's own return arc) means the page states each guarantee once, where it's load-bearing, and the messaging can't drift between two copies.
Verification (build-proven)
pnpm --filter @dossier/app check(svelte-kit sync + svelte-check) → 470 files, 0 errors, 0 warnings.pnpm vitest run packages/app/test/landing-rba.test.ts packages/app/test/landing-dossier-roundtrip.test.ts→ 2 files / 16 tests pass (the RBA round-trip + honest-confidence + grounded-atom + the re-pointed #3 universal-messaging + the #4 tailored + the new self-serve-door assertion, and the Dossier OKF-artifact byte-for-byte round-trip — unaffected, onlyokf.artifactis read).- No hex literals in
landing.css(grep clean); brace balance verified; every--ds-*token used confirmed present inpackages/design/styles/dossier.css. - The FDE owns the visual/browser verification (both themes, focus states, reduced-motion, keyboard nav, no-JS fallback, zero-nav-flash) separately.
Files (all under packages/app/, plus the skill)
src/lib/content/landing/schema.ts—DoorContent+ two-doorGetStartedContent; removedFeatureContent/featuresHead/features/EarlyAccessContent/PartnerContent; doc/invariant notes updated.src/lib/content/landing/dossier.ts— stakes-first hero; two doors; features removed.src/lib/content/landing/examples/rba.ts— secondary CTA conform; two doors (RBA voice); features removed; hero kept.src/lib/components/LandingPage.svelte— Features section removed; hero secondary in-page + dual-audience affordance; Surfacesid+left head; two-door get-started.src/styles/landing.css— Tier A cohesion polish; door restructure; dead CSS removed.test/landing-rba.test.ts— #3 assertions re-pointed; self-serve-door assertion added..claude/skills/generate-landing/SKILL.md— features slot dropped; two-door + OKF/loop homes.