ci(deploy): rewrite for build-on-droplet + local runner #5

Merged
plt merged 2 commits from feat/ci-runner-setup into main 2026-09-02 17:59:23 +00:00
35 changed files with 2261 additions and 151 deletions
Showing only changes of commit f03fe7f232 - Show all commits
+14 -16
View File
@@ -1,14 +1,14 @@
---
title: "AIBook.fun — Implementation Plan"
title: "AiBook.fun — Implementation Plan"
type: implementation-plan
status: draft
owner: Patrick (Product)
launch-target: 2026-09-26
last-updated: 2026-08-26
last-updated: 2026-08-31
companions: [AiBook_PRD.md, AiBook_Spec.md]
---
# 📚 AIBook.fun — Implementation Plan
# 📚 AiBook.fun — Implementation Plan
> **Status: DRAFT.** The schedule below assumes decisions that aren't final yet (see "Do this
> week"). Dates, owners, and dollar figures are planning assumptions until signed off. This is a
@@ -16,12 +16,11 @@ companions: [AiBook_PRD.md, AiBook_Spec.md]
> [Appendix — What changed](#appendix--what-changed-in-this-revision) for how this revision
> differs from the prior draft.
**The clock:** today is late August 2026; **launch is 9/26/2026** — roughly **4–5 weeks out**.
Every week counts, and the critical path has little slack. The domain expires **09-05**, so
step one is non-negotiable and immediate.
**The clock:** today is 31 Aug 2026; **launch is 26 Sep 2026**. Domain and price are signed.
Content is the remaining critical path.
**The critical path in one line:**
`renew domain + make the pricing decisions → seed the Minimum Lovable Library → freeze copy → no-crypto scrub → submit to Kickstarter → launch 9/26`
`assign MLL authors + D-3 co-authors → write the library → freeze copy → no-crypto scrub → submit to Kickstarter → launch 9/26`
---
@@ -31,17 +30,16 @@ Before any copy or build locks, close these. Most are decisions or de-risking, n
| # | Task | Owner | Why it's first |
|:--|:--|:--|:--|
| 0.1 | **Renew aibook.fun today.** It expires 09-05; launch is 09-26. | Whoever holds the registrar login | It's a chore, not a decision. A lapsed domain 3 weeks before launch is a self-inflicted disaster. |
| 0.2 | **Decide the pricing spine — lifetime *and* royalties together** (D-1 + D-2). | Commercial lead (@GTM) | Both block copy freeze; they're linked (see [Spec §6](AiBook_Spec.md)). |
| 0.3 | **Ratify the North-Star + product-health floors** (activation, submissions, renewal). | Product (@pat) + team | So we measure the right thing from Day 1, not just dollars. |
| 0.1 | **~~Renew aibook.fun~~ Done 2026-08-31.** | Registrar owner | Closed. |
| 0.2 | **~~Pricing spine~~ Done 2026-08-31.** **$47/yr** is the product. No lifetime SKU as an equal. Founding writers on **contract** (v1 authored). | Commercial + Product | Copy freeze unblocked on price. |
| 0.3 | **~~Ratify the North-Star + product-health floors~~ Done 2026-08-29.** Activation 20/35 from access date; campaign waitlist 50/100; post-key submissions 10/25 in 90 days; renewal 40/60 excluding lifetime; curation is an ops SLA, not a launch bar. See PRD §8. | Product (@pat) | Locked. Don't quote 60% renewal publicly. |
| 0.4 | **Ratify the Minimum Lovable Library number and assign every workflow an author.** | Product + co-authors | This is the #1 risk; turn it from hope into owned work. |
| 0.5 | **Confirm the two unconfirmed co-authors, or cut their verticals from v1** (D-3). | Commercial lead (@GTM) | Scope certainty before outline lock. |
| 0.6 | Open the claims register; assign a post-campaign renewal owner. | Product + Commercial | Truth-in-claims gate; don't leave renewal unstaffed. |
| 0.7 | Set up the Kickstarter creator account + identity verification. | Campaign lead (@THY) | Long lead time; do it early. |
**✅ Gate 0 — decisions closed.** Pricing spine decided · targets ratified · Minimum Lovable
Library scoped with authors · domain renewed · claims register open. *Until pricing is decided,
don't freeze copy. Until the domain is renewed, don't push anything live.*
**✅ Gate 0 — decisions closed.** **Pricing spine signed ($47/yr; writers on contract)** · **targets ratified (2026-08-29)** · Minimum Lovable
Library scoped with authors · **domain renewed (2026-08-31)** · claims register open. *Until the library has named authors and D-3 is closed, don't treat content as on track. Copy freeze still waits on claims + co-author scope.*
---
@@ -53,8 +51,8 @@ campaign around an empty library fails.
### 1A · Content — seed the Minimum Lovable Library (critical path)
| Task | Owner |
|:--|:--|
| Lock the chapter outline (chapters per vertical, a named owner each) | Co-authors |
| Publish the Workflow Recipe template + 3 filled examples | Product |
| Lock the chapter outline (chapters per vertical, a named owner each). Blank is [`workflows/chapter-structure.md`](workflows/chapter-structure.md) | Co-authors |
| Review and freeze the chapter blank, then 3 filled examples | Product + authors |
| **Seed the Minimum Lovable Library to its floor number** (team + co-authors write & test the workflows) | Co-authors + team |
| 3 "clone-and-run" kits, each **verified by actually running it** | Dev + QA support |
| 2 sample chapters (the backer preview) | Two lead co-authors |
@@ -75,7 +73,7 @@ campaign around an empty library fails.
| Task | Owner |
|:--|:--|
| Kickstarter page copy (all sections, 9/26 dates) | Campaign lead + exec co-author |
| **Copy freeze — commercial terms locked** (lifetime/royalties per D-1/D-2) | Commercial lead (sign) |
| **Copy freeze** (price already signed: $47/yr, writers on contract; still freeze claims + NFT scrub) | Commercial lead (sign) |
| **No-crypto scrub gate on every asset** (translation table) | Campaign lead (owner) |
| Hero video: script → shoot → edit (2–3 min) | Creative lead (@LFG) + co-authors |
| Cover design + 3D mockups | Creative lead |
+90 -36
View File
@@ -1,13 +1,13 @@
---
title: "AIBook.fun — Product Requirements Document"
title: "AiBook.fun — Product Requirements Document"
type: prd
status: draft
owner: Patrick (Product)
launch-target: 2026-09-26
last-updated: 2026-08-26
last-updated: 2026-08-31
---
# 📚 AIBook.fun — Product Requirements Document
# 📚 AiBook.fun — Product Requirements Document
> **Status: DRAFT.** Dollar figures, dates past today, and commercial terms in this
> document are planning assumptions, not commitments. Nothing here should be quoted
@@ -20,7 +20,7 @@ last-updated: 2026-08-26
## TL;DR
AIBook.fun is a **crowd-written, practical field guide to using AI agents responsibly at
AiBook.fun is a **crowd-written, practical field guide to using AI agents responsibly at
work** — sold as an annual subscription, launched on Kickstarter on **September 26, 2026**.
Its unit of content is not a chapter but a **workflow you can actually run**: a documented,
tested recipe for getting a real job done with AI, complete with the guardrails.
@@ -142,8 +142,9 @@ the structural answer to "how do I know this AI advice isn't hallucinated?"
| **Costs** | Model/tool costs where known — honesty about the economics |
| **Author & sources** | Who wrote it, when, for which vertical, with citations |
**Chapters** are curated collections of these recipes, one set per vertical: Real Estate ·
Financial Advisors · Fractional C-Suite · Higher Ed · Communities · Technology. **Editions**
**Chapters** are substantial essays, one per vertical (Real Estate · Financial Advisors ·
Fractional C-Suite · Higher Ed · Communities · Technology), with full Workflow Recipes
**nested in the argument**. See [§5.4](#54-the-reading-surface-locked-2026-08-31). **Editions**
refresh annually — which is the honest justification for a *subscription* rather than a
one-time purchase: the content genuinely goes stale as AI changes.
@@ -160,15 +161,61 @@ post-launch aspiration. Community contribution is **Layer 2** — an experiment
we've seeded a library worth subscribing to.
### 5.3 Be honest: v1 is an *authored* book, not yet a *crowd* book
The prior draft's "contributor economy" pays co-authors **15% affiliate commission** — which
is a reward for *referring backers*, not for *writing workflows*. Writing a workflow currently
earns a byline and nothing else. The mechanic that would actually pay contributors — royalties
— is unresolved (see [D-2](#10-open-decisions)).
The prior draft's "contributor economy" pays co-authors **15% affiliate commission**, which
is a reward for *referring backers*, not for *writing workflows*.
So for v1, we state plainly: **the first edition is written by a founding set of co-authors and
the team.** Open community contribution — with a real incentive attached — is the very next
thing we build, and its incentive design ([D-2](#10-open-decisions)) is a **launch-blocking
decision, not a nice-to-have**, because the entire "living book" thesis depends on it.
**Signed 2026-08-31 (D-2):** founding writers are paid **on contract**. v1 is an authored book.
Affiliate referral can still pay for referring backers. It is not how writing is paid. Open
contribution with royalties or bounties waits for Layer 2.
So we state plainly: **the first edition is written by a founding set of co-authors and the
team.** Layer 2 is the next thing we build, after the library is worth subscribing to.
---
### 5.4 The reading surface (locked 2026-08-31)
People read **chapters**, not a shelf of detached cards. A chapter is a substantial essay
for one vertical: it argues a way of working, then **nests full workflows inside that
argument** as the proof.
This is a deliberate break from treating the workflow as the only page. The workflow is
still the unit of *practice* (and of activation). The chapter is the unit of *reading*.
**Chapter page**
- Title, vertical, edition, authors
- Opening promise (what this chapter is for) plus a **Skip to a Try-it** control — activation
must not require finishing the essay
- Essay body: sections that teach the practice (stakes, constraints, how we work here)
- Nested **Workflow blocks** at the points in the argument they earn — not bunched in an appendix
- End matter: further workflows in this vertical, sources, last updated
**Nested workflow block** (the recipe, in full — not a teaser)
1. Chrome: recipe ID, version, time, cost
2. Title as the job
3. Problem
4. Actors
5. Setup (tools, sample files, cost)
6. **Safeguards before the steps** so data-handling is read before anyone pastes a client list
7. Method, with real tool output *in* the steps
8. Evidence
9. Try-it (~15 min, sample data, mark complete — fires Activation)
10. Share result (community template; events, not content)
11. Author, sources, last updated
**Addressability**
- Every workflow has a stable ID (e.g. `RE-01`) and a URL that opens the chapter scrolled to
that block (and a focused/print view of the same block). Campaign samples, clone-and-run
kits, and Workflow-of-the-Week link here — never to an essay with no way in.
- PDF matches this stack: chapter essay + in-place recipes. Print and web must not drift.
**Non-goals for the reader**
- No chatbot on the chapter page
- No crypto/Web3 language
- Nested recipes are not collapsed “click to reveal” cards that hide the Method
**Writing drafts** live in [`workflows/chapter-structure.md`](workflows/chapter-structure.md).
Copy that blank into a vertical file (`workflows/real-estate.md`, etc.) when a chapter is assigned.
---
@@ -202,8 +249,8 @@ Requirements are grouped by the layer they serve; **most 🔴 items serve the co
| ID | Requirement | Pri |
|:--|:--|:--|
| A-1 | Post-campaign access: a digital key unlocks PDF + web reader for 12 months, renews annually | 🟢 |
| A-2 | Anchor price **$47/yr** *(draft — needs commercial re-sign)* + hardcopy at-cost upcharge | 🔴 |
| A-3 | Lifetime tier — **decision required before copy freeze** (see [D-1](#10-open-decisions)) | 🟡 |
| A-2 | Anchor price **$47/yr** *(signed 2026-08-31)* + hardcopy at-cost upcharge. No lifetime SKU as an equal product. | 🔴 |
| A-3 | Lifetime tier — **not offered as a v1 SKU** (D-1 signed 2026-08-31: $47/yr only). Revisit only as a capped early-bird later, not as an equal product. | 🟢 |
| A-4 | Fulfillment ledger: backer ↔ key/upcharge tracked (a spreadsheet is fine for v1) | 🟢 |
### 6.4 The other layers — mostly 🟡 / 🟢
@@ -236,7 +283,7 @@ Requirements are grouped by the layer they serve; **most 🔴 items serve the co
| NFR-3 | **Privacy:** no confidential client data in any workflow or campaign asset; track events, not content. |
| NFR-4 | **Availability:** landing page up through the campaign; emails on schedule; affiliate tracking live from Day 1 (the 30-day cookie means every hour of Day 1 counts). |
| NFR-5 | **Content quality:** every workflow passes the Recipe template + at least one co-author review. |
| NFR-6 | **Domain continuity:** aibook.fun must not lapse (it currently expires 09-05 — see [§9](#9-immediate-next-steps)). |
| NFR-6 | **Domain continuity:** aibook.fun must not lapse (**renewed 2026-08-31**). |
| NFR-7 | **Accessibility:** reader and landing page are mobile-readable, targeting WCAG AA. |
| NFR-8 | **Cost discipline:** pre-revenue burn stays within the ~$6K budget ([Appendix C](#appendix-c--budget)). |
@@ -249,12 +296,18 @@ metrics; those measure the *campaign*, not the *product.* A funded campaign can
dead product if nobody activates, renews, or contributes.
### Product health (report these first)
| Metric | Floor | Target | Why it matters |
|:--|:--|:--|:--|
| **Activation** — read 1 workflow + do 1 Try-it in 14 days | 20% | 35% | The North-Star. Is the core loop actually useful? |
| **Workflow submissions** during campaign window | 10 | 25 | Early signal that Layer 2 (contribution) is real |
| **Renewal rate** — year-1 cohort | — | 60% | Is a *subscription* the right model at all? |
| **Curation turnaround** ≤ 10 working days | 80% | 95% | Can we keep quality without becoming the bottleneck? |
**Ratified 2026-08-29 (Product).** These are the numbers we run. Do not quote the 60% renewal target on Kickstarter or any public surface.
| Metric | Floor | Target | Clock / notes | Why it matters |
|:--|:--|:--|:--|:--|
| **Activation** — read 1 workflow + finish 1 Try-it | 20% | 35% | **14 days from access-key date, not pledge date.** North-Star. | Is the core loop actually useful? |
| **Contributor waitlist** during the campaign | 50 | 100 | People who *say* they will contribute a workflow. Not written recipes. | Early Layer-2 signal without betting launch on cold-start writing |
| **Workflow submissions** | 10 | 25 | First **90 days after keys go out** | Is paid/open contribution real once the library exists? |
| **Renewal rate** — year-1 **paid** cohort | 40% | 60% | **Exclude lifetime SKUs** from the denominator. 60% is internal. | Is a *subscription* the right model at all? |
| **Curation turnaround** ≤ 10 working days | 80% | 95% | **Ops SLA after Layer 2 is on** — not a launch health bar | Can we keep quality without becoming the bottleneck? |
Leading indicator before fulfillment: % of the pre-launch list who run the *sample* Try-it.
### Campaign performance (the fundraise)
| Metric | Min | Target | Stretch |
@@ -279,13 +332,13 @@ dead product if nobody activates, renews, or contributes.
## 9. Immediate next steps
**This week — decisions and de-risking (before any copy or build locks):**
1. **Renew aibook.fun today.** It expires 09-05; launch is 09-26. This is a chore, not a
strategic decision — do it now and delete it from the risk list. *(Owner: whoever holds the
registrar login.)*
2. **Resolve the pricing spine together — lifetime ([D-1](#10-open-decisions)) *and* royalties
([D-2](#10-open-decisions)).** They're linked, and both block copy freeze.
3. **Ratify the North-Star + the product-health floors** (§8) so we're measuring the right
thing from Day 1.
1. **~~Renew aibook.fun~~ Done.** Domain renewed (confirmed 2026-08-31). Launch remains 09-26.
2. **~~Resolve the pricing spine (D-1 / D-2)~~ Done 2026-08-31.** Anchor is **$47/yr**. No
lifetime SKU as an equal product. Founding writers are engaged **on contract** — v1 is an
authored book; open royalties are not the v1 mechanic. Copy freeze is unblocked on price.
3. **~~Ratify the North-Star + the product-health floors~~ Done 2026-08-29.** Locked in §8:
activation 20/35 from access date; campaign waitlist 50/100; post-key submissions 10/25 in
90 days; renewal 40/60 excluding lifetime; curation SLA not a launch bar.
4. **Ratify the Minimum Lovable Library number and assign every workflow an author** (§5.2).
5. **Confirm the two unconfirmed co-authors** (Higher Ed, Communities) or re-scope those
verticals out of v1.
@@ -313,8 +366,8 @@ decision-maker has a starting point — but the recommendation is not yet a deci
| # | Decision | Recommendation | Blocks |
|:--|:--|:--|:--|
| **D-1** | **Lifetime tier — offer it, and at what price?** A ~$97–147 lifetime breaks even against $47/yr in ~2–3 years, which *undercuts the recurring-revenue thesis.* | **Pick a spine.** Keep $47/yr as the anchor. If you want a lifetime option, frame it *only* as a bounded early-bird instrument to capitalize the community fund (capped quantity), not the default SKU. Don't ship recurring + lifetime as equals. | KS copy freeze |
| **D-2** | **Contributor payment — how do people earn for *writing workflows* (not just referring)?** Today the only paid mechanic is affiliate referral. | **Resolve before launch.** This is the engine of the "living book." Decide: royalty pool, per-accepted-workflow bounty, or an explicit "v1 is authored, paid contribution ships in Q1" — but decide, don't leave blank. | The core value prop |
| **D-1** | ~~Lifetime tier~~ **Signed 2026-08-31.** Product price is **$47/yr**. No lifetime SKU as an equal v1 product. | Locked. Don't quote a lifetime price. Hardcopy remains an at-cost upcharge. | — |
| **D-2** | ~~Writer payment~~ **Signed 2026-08-31.** The first set of writers is engaged **on contract**. v1 is an authored book. Affiliate referral can still pay for *referring backers*; it is not how writing is paid. Open contribution + any royalty/bounty is post-v1 (Layer 2), not a launch mechanic. | Locked for v1. Revisit royalties before opening Layer 2. | — |
| **D-3** | **Co-author confirmations** (Higher Ed, Communities verticals). | Confirm within the week or cut those verticals from v1 and add post-launch. | Chapter outline, scope |
| **D-4** | **Refresh the source-of-truth plan** to the 9/26 dates and re-signed terms (depends on D-1/D-2). | Do after D-1/D-2 land. | Copy freeze |
| **D-5** | **Human-first or agent-first for v1?** Writing for human motivation and for machine ingestion are different jobs. | **Human-first.** Build the content format so agents *can* read it later (K-7), but optimize v1 for human learners. Agent onboarding is Layer 4. | Content design |
@@ -375,16 +428,17 @@ changes from the prior draft, and the reasoning:
funnel are explicitly later layers.
3. **Elevated the content cold-start** to the #1 risk and added the *Minimum Lovable Library*
launch gate (§5.2).
4. **Told the truth about incentives** (§5.3): v1 is an authored book; the "contributor
economy" is currently a referral scheme; paid contribution (D-2) is now launch-blocking.
5. **Forced the subscription-vs-lifetime tension** into a real decision (D-1).
4. **Told the truth about incentives** (§5.3). **D-2 signed 2026-08-31:** founding writers on
contract; v1 authored; royalties wait for Layer 2. Affiliate still pays referrals, not writing.
5. **Forced the subscription-vs-lifetime tension** into a real decision. **D-1 signed 2026-08-31:**
**$47/yr** only. No lifetime SKU as an equal v1 product.
6. **Split the validation bar from the funding floor** (§8) so a funded-but-dead outcome can't
hide.
7. **Reframed the pitch as utility-first** (§4, §9 hook test) — the buyer wants a workflow, not
an ownership philosophy.
8. **Demoted governance to proportionate** (Appendix B), keeping only the NFT-scrub and
truth-in-claims as active gates.
9. **Turned the domain "decision" into a same-day chore** (§9).
9. **Turned the domain into a same-day chore** (§9). **Done 2026-08-31:** `aibook.fun` renewed.
10. **Translated the jargon out.** Internal codes, handles, and merge-history are gone from the
body; a who's-who / glossary can be maintained separately if the team wants traceability
back to the source-of-truth IDs.
+17 -14
View File
@@ -1,14 +1,14 @@
---
title: "AIBook.fun — Product Specification"
title: "AiBook.fun — Product Specification"
type: spec
status: draft
owner: Patrick (Product)
launch-target: 2026-09-26
last-updated: 2026-08-26
last-updated: 2026-08-31
companions: [AiBook_PRD.md, AiBook_Implementation_Plan.md]
---
# 📚 AIBook.fun — Product Specification
# 📚 AiBook.fun — Product Specification
> **Status: DRAFT.** This spec is the *thinking* behind the product — the vision, the
> principles, the model. The [PRD](AiBook_PRD.md) turns it into requirements; the
@@ -22,7 +22,7 @@ companions: [AiBook_PRD.md, AiBook_Implementation_Plan.md]
## TL;DR
AIBook.fun is a **living, crowd-written field guide to using AI agents responsibly at work.**
AiBook.fun is a **living, crowd-written field guide to using AI agents responsibly at work.**
Its unit of value is a **workflow you can run** — a documented, tested recipe for doing a real
job with AI, including the guardrails. It launches as an annual subscription via Kickstarter on
**September 26, 2026**.
@@ -56,7 +56,8 @@ ambition — but an engine needs one cylinder firing before you add the rest. He
Everything else the product wants to be — a contributor economy, a cohort program, an
onboarding corpus for AI agents — is a **layer built on top of this loop**, and only worth
building once the loop demonstrably works. We measure that with one number, **Activation**: *did
a reader run at least one workflow within 14 days?* (Full metric set in [PRD §8](AiBook_PRD.md).)
a reader run at least one workflow and finish one Try-it within 14 days of getting access
(access key, not pledge)?* Floors/targets ratified 2026-08-29 — full set in [PRD §8](AiBook_PRD.md).
### The product in layers
| Layer | What | When |
@@ -95,7 +96,7 @@ These are gates, not preferences — a workflow or a claim either clears them or
| Actor | Who | Their job | The boundary |
|:--|:--|:--|:--|
| **Reader / subscriber** | Practitioners (real estate, financial advisors, fractional execs, higher ed, communities, tech) | Get current, trustworthy AI workflows to apply this week | Pays for access (~$47/yr — *draft*) |
| **Reader / subscriber** | Practitioners (real estate, financial advisors, fractional execs, higher ed, communities, tech) | Get current, trustworthy AI workflows to apply this week | Pays for access (**$47/yr** — signed) |
| **Co-author** | Confirmed: three founders across RE/FA, C-suite, and tech. Potential: two more for Higher Ed & Communities | Own a vertical; write and curate its workflows | Content follows the template; curated |
| **Curator / editor** | Co-authors + community editors | Keep the quality bar; run the editorial pipeline | Curating ≠ owning the platform |
| **Affiliate** | Community, podcast, press | Refer backers; earn commission | Commission rates below |
@@ -146,9 +147,11 @@ answer to "how do I know this isn't hallucinated AI advice?"
| **Costs** | Model/tool costs where known — honesty about economics |
| **Author & sources** | Who, when, which vertical, with citations |
**Chapters** are curated collections of these per vertical. **Editions** refresh annually —
**Chapters** are the reading unit: a substantial essay per vertical, with **full workflows
nested in the argument** (not a table of contents of cards). **Editions** refresh annually —
which is the honest justification for a *subscription*: AI moves fast enough that a static book
goes stale, so the value is in staying current.
goes stale, so the value is in staying current. Reading IA locked 2026-08-31; see
[PRD §5.4](AiBook_PRD.md).
### 4.2 Seed the shelf before you sell tickets (the cold-start rule)
The value proposition is a rich library. The failure mode is a near-empty one. Crowd-sourced
@@ -179,7 +182,7 @@ around and after launch, not gating it:
### 4.4 Digital surface (current state)
| Surface | What it is | Status |
|:--|:--|:--|
| `aibook.fun` domain | Public home: pitch, sample workflows, affiliate signup, campaign link | Parked — **expires 09-05, renew immediately** |
| `aibook.fun` domain | Public home: pitch, sample workflows, affiliate signup, campaign link | **Renewed 2026-08-31** |
| Landing page (built) | A staged proof-of-concept, ready to review and push live | Staged locally, not yet public |
| Subscription access | Digital key → PDF + web reader + annual updates | Not built yet (post-campaign) |
| Community | Team channel live; cohort spaces not built | Partial |
@@ -218,9 +221,9 @@ from the core loop.*
| Item | Value | Status |
|:--|:--|:--|
| Anchor subscription | $47/yr digital + hardcopy upcharge | **Draft** — needs commercial re-sign for the 9/26 relaunch |
| Lifetime access | Proposed; ~$97–147 one-time *(figures illustrative — do not quote)* | **Open decision** ([D-1](#8-open-decisions)) |
| Contributor royalties | None documented today; current mechanic is affiliate referral only | **Open decision** ([D-2](#8-open-decisions)) |
| Anchor subscription | **$47/yr** digital + hardcopy at-cost upcharge | **Signed 2026-08-31** |
| Lifetime access | Not a v1 SKU | **Signed D-1:** $47/yr only |
| Founding writers | Paid **on contract** (v1 is authored). Affiliate still pays referrals, not writing. | **Signed D-2 2026-08-31.** Royalties/bounties wait for Layer 2. |
| Projected revenue | ~$53,600 at 706 backers | Draft planning figure |
| Pre-revenue budget | ~$6,000 (ads + KS fees + affiliate fee) | Draft planning figure |
| "eBookNFT platform" | A future idea; this book is the first product | Not in scope |
@@ -274,8 +277,8 @@ carries a recommendation as a starting point, not a settled answer.
| # | Decision | Recommendation |
|:--|:--|:--|
| **D-1** | Lifetime tier — offer it, at what price/position? | Keep $47/yr as the anchor; a lifetime option only as a bounded early-bird capitalization play, not the default. |
| **D-2** | Contributor payment — how do people earn for *writing* workflows, not just referring? | **Resolve before launch** — it's the engine of the "living book." Royalty pool, per-workflow bounty, or an explicit "v1 authored, paid contribution in Q1." |
| **D-1** | ~~Lifetime~~ **Signed 2026-08-31:** **$47/yr** only. No lifetime SKU as an equal product. | Locked. |
| **D-2** | ~~Writer pay~~ **Signed 2026-08-31:** founding writers on **contract**. v1 authored. Layer 2 royalties later. | Locked for v1. |
| **D-3** | Confirm the two unconfirmed co-authors (Higher Ed, Communities) | Confirm this week, or cut those verticals from v1 and add post-launch. |
| **D-4** | Refresh the source-of-truth plan to the 9/26 dates & re-signed terms | Do after D-1/D-2 land. |
| **D-5** | Human-first or agent-first for v1? | Human-first. Build the format so agents *can* read it later; optimize v1 for human learners. |
+18 -17
View File
@@ -1,15 +1,15 @@
# 📚 AIBook.fun
# 📚 AiBook.fun
**A living, crowd-written field guide to using AI agents responsibly at work.**
AIBook.fun's unit of content isn't a chapter — it's a **workflow you can actually run**: a
documented, tested recipe for getting a real job done with AI, guardrails included. It's written
and kept current by the practitioners who use it, and it launches as an annual subscription,
crowdfunded on Kickstarter.
People read **chapters**. Each chapter is a substantial essay for one vertical, with full
workflows nested in the argument. The workflow is the unit of practice: a documented, tested
recipe for a real job, guardrails included. The first edition is written on contract by founding
authors. It launches as a **$47/yr** subscription, crowdfunded on Kickstarter.
> **Status: planning stage.** This repository holds the product's planning documents. The product
> is not yet live, and everything here is a working draft — figures, dates, and commercial terms
> are planning assumptions, not commitments. Public launch is **targeted for September 26, 2026**.
> **Status: planning + writing.** Planning docs and the chapter blank live here. The product is
> not yet live. **$47/yr** is signed. Domain `aibook.fun` is renewed. Other figures and unsigned
> terms are still planning assumptions, not for public quote. Launch target **26 Sep 2026**.
## The idea in one loop
@@ -30,22 +30,23 @@ not read AI theory.
## What's in this repository
This is a documentation and planning repo. Three companion documents define the product:
This is a documentation and writing repo.
| Document | What it covers |
|:--|:--|
| [Product Requirements (PRD)](AiBook_PRD.md) | What we're building and why — scope, requirements, success metrics, open decisions |
| [Product Specification (Spec)](AiBook_Spec.md) | The thinking behind it — vision, principles, content model, business model |
| [Implementation Plan](AiBook_Implementation_Plan.md) | How and when — the path from now to launch and fulfillment |
| [Product Requirements (PRD)](AiBook_PRD.md) | What we're building and why. Scope, requirements, success metrics, open decisions |
| [Product Specification (Spec)](AiBook_Spec.md) | The thinking behind it. Vision, principles, content model, business model |
| [Implementation Plan](AiBook_Implementation_Plan.md) | How and when. Path from now to launch and fulfillment |
| [Chapter structure](workflows/chapter-structure.md) | The blank chapter for review. Copy it into a vertical file when writing starts |
New here? Start with the **[Spec](AiBook_Spec.md)** for the vision, then the **[PRD](AiBook_PRD.md)**
for the specifics.
for the specifics. Writers start at **[chapter structure](workflows/chapter-structure.md)**.
## How it works (the short version)
- **Content:** every entry follows one *Workflow Recipe* template — problem, method (showing the
real tool output), safeguards, evidence, a ~15-minute Try-it, and costs. The template is the
quality guarantee — the structural answer to "how do I know this AI advice isn't made up?"
- **Content:** readers get a chapter essay. Nested in it, every workflow follows one recipe:
problem, actors, setup, safeguards, method (real tool output), evidence, a ~15-minute Try-it,
share, costs, author. Safeguards sit before the steps. The template is the quality guarantee.
- **Editions:** the guide refreshes annually — which is why it's a subscription. AI moves fast,
and stale advice is worthless.
- **Contribution:** the first edition is written by a founding set of co-authors; open community
@@ -61,7 +62,7 @@ for the specifics.
| Post-launch | Fulfill the first edition; open community contribution |
| Later | Cohort learning, annual editions, deeper ecosystem integration |
AIBook.fun is part of the **WeOwnNet** ecosystem.
AiBook.fun is part of the **WeOwnNet** ecosystem.
## License
+12 -1
View File
@@ -1,4 +1,4 @@
# AIBook.fun Marketing Site
# AiBook.fun Marketing Site
Scaffolded for Framer import and Next.js (Cloudflare Pages / DigitalOcean).
@@ -33,3 +33,14 @@ Note: Framer Sites don't run Next.js natively. For a live site with routing (A/B
- 3 sample tiers shown ($10 / $47yr / $4,888)
- No NFT/crypto/Web3 language (Kickstarter compliance scrubbed)
- Event tracking only (not content) — PostHog/Cloudflare Analytics slot available
## Reading demo
A sample reading environment is wired into this Next.js app (pages router).
```bash
npm install
npm run dev
```
Then open [http://localhost:3000/read](http://localhost:3000/read). One chapter is filled (Real Estate, with nested RE-01 and RE-02). This is a demo, not the live book.
+2 -2
View File
@@ -13,8 +13,8 @@ export default function Authors() {
<span className="spec reveal">Who’s writing this</span>
<h2 className="reveal">Practitioners, one per vertical.</h2>
<p className="lede reveal">
Each chapter is owned by someone who does the work — not a generalist writing about
it. Names are announced as co-authors confirm.
Each chapter is owned by someone who does that work for a living. Names go up as
co-authors confirm.
</p>
<div className="auth-grid">
{AUTHORS.map((a) => (
+2 -2
View File
@@ -11,7 +11,7 @@ export default function Book3D() {
<div className="book3d__face book3d__pages" />
<div className="book3d__face book3d__top" />
<div className="book3d__face book3d__spine">
<span>AIBook.fun · Responsible Agentic AI</span>
<span>AiBook.fun · Responsible Agentic AI</span>
</div>
<div className="book3d__face book3d__cover">
<span className="book3d__kicker">The Field Guide</span>
@@ -28,7 +28,7 @@ export default function Book3D() {
<path d="M20 5 L22.6 20 L20 35 L17.4 20 Z" fill="#efe9db" />
<path d="M5 20 L20 17.4 L35 20 L20 22.6 Z" fill="#efe9db" opacity="0.55" />
</svg>
<span className="book3d__brand">AIBook.fun</span>
<span className="book3d__brand">AiBook.fun</span>
</div>
{/* Belly band — the classic publisher's wraparound strip for a themed
tie-in, rather than redesigning the whole cover around it. */}
+7 -7
View File
@@ -1,28 +1,28 @@
const QA = [
{
q: "Is this AI theory, or something I can actually run?",
a: "Something you run. Every workflow shows the real tool output, lists the real steps, and ends in a ~15-minute Try-it on non-confidential sample data. If you can’t run it, it doesn’t ship.",
a: "Something you run. Every workflow shows the real tool output, lists the real steps, and ends in a ~15-minute Try-it on non-confidential sample data. If you can't run it, it doesn't ship.",
open: true,
},
{
q: "How is this different from a course?",
a: "A course teaches concepts; this is a field guide of tested recipes you apply to your real job this week. It refreshes annually because AI moves fast — stale advice is worthless.",
a: "A course teaches concepts. This is a field guide of tested recipes you apply to your job this week. The recipes get rewritten as the tools change, not left sitting until next year.",
},
{
q: "What does “responsible by design” actually mean?",
a: "Every recipe includes data-handling rules, human-oversight steps, failure modes, and escalation paths. They’re part of the template — not an appendix, not an afterthought.",
q: "What does \"responsible by design\" actually mean?",
a: "Every recipe includes data-handling rules, human-oversight steps, failure modes, and escalation paths. They sit in the template above Method, not in a footnote.",
},
{
q: "Can I contribute a workflow after backing?",
a: "Yes — open contribution opens once the founding library is live. We’re finalizing how contributors get paid (not just a byline) before launch.",
a: "Yes. Open contribution starts once the founding library is live. How contributors get paid is still being finalized before launch.",
},
{
q: "Why subscribe instead of buy once?",
a: "Because a static AI book is out of date in months. The subscription is what keeps every workflow current — and it’s what funds the practitioners keeping them that way.",
a: "A one-time AI book goes stale in weeks. The $47/yr subscription funds ongoing updates as models and products move, often weekly or monthly.",
},
{
q: "Why frame this around the American Dream?",
a: "Because for most of the people writing and reading this guide, real fluency with these tools isn’t a slogan — it’s a practical ladder. Nobody in this book inherited their edge; they built it, one real skill at a time. We think this is the next one worth building.",
a: "For most of the people writing and reading this guide, fluency with these tools is a practical ladder. Nobody in this book inherited their edge. They built it, one real skill at a time. We think this is the next one worth building.",
},
];
+6 -6
View File
@@ -1,18 +1,18 @@
const NOTES = [
{
n: "01 / Stays current",
h: "A subscription, not a static book",
p: "AI moves too fast for a book you buy once. Each annual edition is refreshed so the ground you gain here doesn’t erode the moment you look away.",
h: "Rewritten as the tools move",
p: "Models and products change by the week. We update recipes when a step breaks or a better path shows up, often weekly or monthly. The $47/yr subscription is what pays for that work.",
},
{
n: "02 / Authored, then open",
h: "Written by practitioners first",
p: "The first edition is seeded by co-authors — a real founding library, not an empty shelf. Open, paid contribution opens once it’s worth browsing, so the people who build it get to own a piece of it.",
h: "Co-authors fill it before launch",
p: "Founding writers are on contract. They write and test the first library. Outside contribution opens later, once there is a real catalog to join, not an empty page waiting for strangers.",
},
{
n: "03 / Responsible by design",
h: "Guardrails in every recipe",
p: "Data handling, human oversight, failure modes, escalation — part of the template on every workflow, never an appendix.",
h: "Safeguards sit above the steps",
p: "Every recipe names what never goes in the prompt, who reviews before anything leaves the building, and when you stop. That block is read before Method.",
},
];
+3 -4
View File
@@ -5,17 +5,16 @@ export default function Footer() {
<div className="foot-top">
<div>
<div className="logo">
AIBook<b>.</b>
AiBook<b>.</b>
<span className="dot">fun</span>
</div>
<p className="foot-blurb">
A living, crowd-written field guide to using AI agents responsibly at work. Part
of the WeOwnNet ecosystem.
An authored field guide of runnable AI workflows for work. Part of the WeOwnNet
ecosystem.
</p>
</div>
<div className="foot-links">
<a href="#recipe">A recipe</a>
<a href="#contents">Contents</a>
<a href="#get">Preview</a>
<a href="#faq">FAQ</a>
</div>
+4 -3
View File
@@ -27,9 +27,10 @@ export default function Hero() {
<span className="serif-it">responsibly</span> at work.
</h1>
<p className="hero-sub">
Not theory. Not hype. A subscription guide of{" "}
<span className="hl">tested workflows you can actually run</span> — each with the
real tool output, the guardrails, and a 15-minute exercise so you learn by doing.
A living subscription guide of{" "}
<span className="hl">tested workflows you can run</span>. Each one shows the real
tool output, the safeguards, and a 15-minute Try-it. Recipes get updated as the
tools change.
</p>
<EmailForm className="hero-form" cta="Send me the preview chapter" />
<p className="hero-note">
+7 -7
View File
@@ -4,17 +4,17 @@ export default function Letter() {
<div className="letter-in">
<span className="spec reveal">A note on why</span>
<p className="letter-body reveal">
Most AI advice is either hype or theory — and neither survives contact with real
work. We’re writing the guide we wish we’d had: workflows that <em>actually run</em>,
with the <span className="mark">guardrails built in</span>, kept honest by the people
who use them every day. For a lot of us, mastering these tools isn’t optional
anymore — it’s how you build the life you’re working toward. Fund the first edition,
and you don’t just get the book — you help decide what goes in it.
Most AI advice is hype or theory, and neither survives contact with real work. We are
writing the guide we wish we had: workflows that <em>actually run</em>, with the{" "}
<span className="mark">safeguards built in</span>, kept current by the people who use
them every day as the tools move. For a lot of us, fluency with these tools is how you
build the life you are working toward. Fund the first edition and you help decide what
goes in it.
</p>
<div className="sign reveal">
<span className="scrawl">The co-authors</span>
<span className="who">
AIBook.fun founding team
AiBook.fun founding team
<br />
Real estate · finance · leadership · tech
</span>
+32 -13
View File
@@ -1,12 +1,27 @@
import { useEffect, useRef, useState } from "react";
import Link from "next/link";
const LINKS = [
{ href: "#recipe", label: "A recipe" },
{ href: "#contents", label: "Contents" },
{ href: "#letter", label: "Why" },
{ href: "#faq", label: "FAQ" },
];
function NavItem({ href, label, onClick }) {
if (href.startsWith("/")) {
return (
<Link href={href} onClick={onClick}>
{label}
</Link>
);
}
return (
<a href={href} onClick={onClick}>
{label}
</a>
);
}
export default function Nav() {
const [scrolled, setScrolled] = useState(false);
const [open, setOpen] = useState(false);
@@ -39,22 +54,25 @@ export default function Nav() {
return (
<nav ref={navRef} className={`nav${scrolled ? " scrolled" : ""}${open ? " open" : ""}`}>
<div className="nav-in">
<a className="logo" href="#top" onClick={close}>
AIBook<b>.</b>
<Link className="logo" href="/" onClick={close}>
AiBook<b>.</b>
<span className="dot">fun</span>
</a>
</Link>
<div className="nav-links">
{LINKS.map((l) => (
<a key={l.href} href={l.href}>
{l.label}
</a>
<NavItem key={l.href} href={l.href} label={l.label} />
))}
</div>
<a href="#get" className="btn btn-primary nav-cta">
Get the preview
</a>
<div className="nav-actions">
<Link href="/read" className="btn btn-ghost nav-cta" onClick={close}>
Go to Library
</Link>
<a href="#get" className="btn btn-primary nav-cta">
Get the preview
</a>
</div>
<button
type="button"
@@ -70,10 +88,11 @@ export default function Nav() {
<div className="nav-menu" id="mobile-menu">
{LINKS.map((l) => (
<a key={l.href} href={l.href} onClick={close}>
{l.label}
</a>
<NavItem key={l.href} href={l.href} label={l.label} onClick={close} />
))}
<Link href="/read" className="btn btn-ghost" onClick={close}>
Go to Library
</Link>
<a href="#get" className="btn btn-primary" onClick={close}>
Get the preview
</a>
+1 -1
View File
@@ -34,7 +34,7 @@ export default function Recipe() {
<div className="recipe reveal">
<div className="recipe-top">
<span className="recipe-id">
recipe <b>RE-04</b> · v1.2 · ~15 min
recipe <b>RE-01</b> · v1.2 · ~15 min
</span>
<span className="recipe-vert">Real Estate</span>
</div>
+5 -5
View File
@@ -9,7 +9,7 @@ export default function Tiers() {
</div>
<p className="tiers-note reveal">
Preview of planned pledge tiers. Final pricing is confirmed at the Kickstarter
launch — nothing here is a live checkout yet.
launch. Nothing here is a live checkout yet.
</p>
</div>
<div className="tier-grid">
@@ -19,7 +19,7 @@ export default function Tiers() {
<div className="t-unit">one-time</div>
<div className="t-desc">
The preview chapter, community updates, and an early look at the founding library
as it’s written.
as it is written.
</div>
</div>
<div className="tier feature reveal">
@@ -30,8 +30,8 @@ export default function Tiers() {
</div>
<div className="t-unit">annual subscription</div>
<div className="t-desc">
The full guide — PDF + web reader — and every annual update while you’re
subscribed. Recurring value, no lifetime gimmick.
The full guide in PDF and web reader, plus ongoing recipe updates while you are
subscribed. Billed yearly. No lifetime SKU in v1.
</div>
</div>
<div className="tier reveal">
@@ -40,7 +40,7 @@ export default function Tiers() {
<div className="t-unit">limited · final terms at launch</div>
<div className="t-desc">
A named acknowledgment in the guide, a private cohort seat, and a hand in shaping
the first edition — the ground floor of something you helped build.
the first edition.
</div>
</div>
</div>
@@ -0,0 +1,60 @@
import { collectChapterKit } from "../../data/sample-chapter";
export default function ChapterKit({ recipes }) {
const kit = collectChapterKit(recipes);
if (!kit.tools.length && !kit.files.length) return null;
return (
<section className="chapter-kit" id="need" aria-label="What you need">
<h2 className="chapter-kit-title">What you need</h2>
<p className="chapter-kit-lede">
{recipes.length} Try-it{recipes.length === 1 ? "" : "s"} on sample data. Nothing
from a live client file.
</p>
<dl className="kit-grid">
<div>
<dt>Tools</dt>
<dd>
<ul>
{kit.tools.map((t) => (
<li key={t}>{t}</li>
))}
</ul>
</dd>
</div>
<div>
<dt>Files</dt>
<dd>
<ul>
{kit.files.map((f) => (
<li key={f}>{f}</li>
))}
</ul>
</dd>
</div>
<div>
<dt>Model</dt>
<dd>
<ul>
{kit.models.map((m) => (
<li key={m}>{m}</li>
))}
</ul>
</dd>
</div>
<div>
<dt>Try-its</dt>
<dd>
<ul>
{kit.tryits.map((t) => (
<li key={t.id}>
{t.id} · {t.time} · {t.cost}
</li>
))}
</ul>
</dd>
</div>
</dl>
</section>
);
}
@@ -0,0 +1,99 @@
import { useEffect, useMemo, useState } from "react";
function OutlineBody({ groups, complete, count, total, active, showTitle }) {
return (
<>
<p className="outline-progress">
{count}/{total} Try-its complete
</p>
{showTitle ? <p className="outline-heading">In this chapter</p> : null}
{groups.map((g) => (
<div key={g.label} className="outline-group">
<p className="outline-group-label spec">{g.label}</p>
<ul>
{g.items.map((item) => {
const done = item.recipeId ? !!complete[item.recipeId] : false;
const isActive = active === item.id;
return (
<li key={item.id}>
<a
href={`#${item.id}`}
className={isActive ? "is-active" : undefined}
aria-current={isActive ? "location" : undefined}
>
<span>{item.label}</span>
{done ? (
<span className="outline-done" aria-label="Try-it complete">
✓
</span>
) : null}
</a>
</li>
);
})}
</ul>
</div>
))}
</>
);
}
export default function ChapterOutline({
groups,
complete = {},
count = 0,
total = 0,
variant = "desktop",
}) {
const ids = useMemo(
() => groups.flatMap((g) => g.items.map((item) => item.id)),
[groups]
);
const [active, setActive] = useState(ids[0] || "");
useEffect(() => {
const els = ids.map((id) => document.getElementById(id)).filter(Boolean);
if (!els.length || typeof IntersectionObserver === "undefined") return;
const observer = new IntersectionObserver(
(entries) => {
const visible = entries
.filter((e) => e.isIntersecting)
.sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
if (visible[0]?.target?.id) setActive(visible[0].target.id);
},
{ rootMargin: "-88px 0px -55% 0px", threshold: [0, 0.1, 0.25, 0.5, 1] }
);
els.forEach((el) => observer.observe(el));
return () => observer.disconnect();
}, [ids]);
if (variant === "mobile") {
return (
<details className="chapter-outline-mobile">
<summary>In this chapter</summary>
<OutlineBody
groups={groups}
complete={complete}
count={count}
total={total}
active={active}
showTitle={false}
/>
</details>
);
}
return (
<nav className="chapter-outline" aria-label="In this chapter">
<OutlineBody
groups={groups}
complete={complete}
count={count}
total={total}
active={active}
showTitle
/>
</nav>
);
}
@@ -0,0 +1,24 @@
import Link from "next/link";
export default function ReaderCrumbs({ items }) {
return (
<nav className="crumbs" aria-label="Breadcrumb">
<ol>
{items.map((item, i) => (
<li key={item.label}>
{i > 0 ? (
<span className="crumbs-sep" aria-hidden="true">
/
</span>
) : null}
{item.current || !item.href ? (
<span aria-current="page">{item.label}</span>
) : (
<Link href={item.href}>{item.label}</Link>
)}
</li>
))}
</ol>
</nav>
);
}
@@ -0,0 +1,32 @@
import Link from "next/link";
export default function ReaderFooter() {
return (
<footer className="footer reader-footer">
<div className="foot-in">
<div className="foot-top">
<div>
<div className="logo">
AiBook<b>.</b>
<span className="dot">fun</span>
</div>
<p className="foot-blurb">
Reading demo. Not the live book. Workflows sit inside a chapter,
not on a shelf of cards.
</p>
</div>
<div className="foot-links">
<Link href="/">Home</Link>
<Link href="/read">Library</Link>
<Link href="/#get">Preview</Link>
</div>
</div>
<div className="colophon">
© 2026 WeOwnAI · MIT-licensed docs · This page is a reading-environment demo.
<br />
Activation is stored in this browser only. No chatbot. No live client data.
</div>
</div>
</footer>
);
}
@@ -0,0 +1,40 @@
import { useEffect, useState } from "react";
import Link from "next/link";
export default function ReaderNav({ here = "library" }) {
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 12);
onScroll();
window.addEventListener("scroll", onScroll, { passive: true });
return () => window.removeEventListener("scroll", onScroll);
}, []);
return (
<nav className={`nav reader-nav${scrolled ? " scrolled" : ""}`}>
<div className="nav-in">
<Link className="logo" href="/">
AiBook<b>.</b>
<span className="dot">fun</span>
</Link>
<div className="nav-links reader-links">
{here === "library" ? (
<span className="nav-current" aria-current="page">
Library
</span>
) : (
<Link href="/read" className="nav-title">
Library
</Link>
)}
</div>
<Link href="/#get" className="btn btn-primary nav-cta">
Get the preview
</Link>
</div>
</nav>
);
}
@@ -0,0 +1,60 @@
/** Same terminal chrome as the marketing Recipe (.term / .term-bar). */
export function TermRE01() {
return (
<div className="term" role="img" aria-label="Sample terminal output from the workflow">
<div className="term-bar">
<i></i>
<i></i>
<i></i>
<span>agent — property-brief · sample dataset (non-confidential)</span>
</div>
<pre>
<span className="c-dim">{"$ "}</span>
<span className="c-user">{"brief"}</span>
{" --input "}
<span className="c-key">{"sample_listings.csv"}</span>
{" --template market-1pager\n\n"}
<span className="c-dim">{"✓ redacted 3 fields (owner_name, phone, exact_address)\n✓ 41 comps in radius · median $/sqft computed\n\n"}</span>
<span className="c-ok">{"MARKET BRIEF — Maple Heights (sample)\n"}</span>
{" · Median list: $420/sqft (▲ 4.1% QoQ)\n · Days on market: 22 (was 31)\n · Inventory: 1.8 mo — seller-leaning\n · Read: price to comp, expect <2 wks at ask\n\n"}
<span className="c-dim">{"# cost this run: $0.03 · model: claude · 2,140 tokens"}</span>
</pre>
</div>
);
}
export function TermRE02() {
return (
<div className="term" role="img" aria-label="Sample terminal output from the listing-copy workflow">
<div className="term-bar">
<i></i>
<i></i>
<i></i>
<span>agent — listing-copy · sample comps (public facts)</span>
</div>
<pre>
<span className="c-dim">{"$ "}</span>
<span className="c-user">{"listing-copy"}</span>
{" --input "}
<span className="c-key">{"sample_comps.json"}</span>
{" --voice house-voice --out draft.md\n\n"}
<span className="c-dim">{"✓ used 6 public comps (no CRM fields present)\n✓ 184 words · reading grade 8\n\n"}</span>
<span className="c-ok">{"LISTING DRAFT · 14xx Cedar (sample street, public facts)\n"}</span>
{" · Bright corner lot, 3 bed / 2 bath, maple kitchen from 2019.\n · Priced to the block: comps closed $398–$441/sqft in 60 days.\n · No owner name, phone, or showing list in the prompt.\n\n"}
<span className="c-dim">{"# cost this run: $0.02 · model: claude · 1,280 tokens"}</span>
</pre>
</div>
);
}
const TERMS = {
"re-01": TermRE01,
"re-02": TermRE02,
};
export default function TermOutput({ id }) {
const Cmp = TERMS[id];
if (!Cmp) return null;
return <Cmp />;
}
@@ -0,0 +1,262 @@
import { useState } from "react";
import Link from "next/link";
import TermOutput from "./TermOutput";
const JUMP = [
{ id: "kit", label: "Kit" },
{ id: "problem", label: "Problem" },
{ id: "actors", label: "Actors" },
{ id: "setup", label: "Setup" },
{ id: "safeguards", label: "Safeguards" },
{ id: "method", label: "Method" },
{ id: "evidence", label: "Evidence" },
{ id: "tryit", label: "Try-it" },
];
function ShieldCheck() {
return (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
<path
d="M8 1.5 2.5 4v3.5c0 3 2.3 5.4 5.5 7 3.2-1.6 5.5-4 5.5-7V4L8 1.5Z"
stroke="#1C5D4C"
strokeWidth="1.3"
strokeLinejoin="round"
/>
<path
d="m5.6 8 1.7 1.7L10.6 6"
stroke="#1C5D4C"
strokeWidth="1.3"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
function CheckMark() {
return (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
<circle cx="8" cy="8" r="7" stroke="currentColor" strokeWidth="1.4" />
<path
d="m5.2 8.2 1.8 1.8 3.8-4"
stroke="currentColor"
strokeWidth="1.4"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
async function copyText(text) {
try {
await navigator.clipboard.writeText(text);
return true;
} catch {
try {
const el = document.createElement("textarea");
el.value = text;
el.setAttribute("readonly", "");
el.style.position = "fixed";
el.style.left = "-9999px";
document.body.appendChild(el);
el.select();
const ok = document.execCommand("copy");
el.remove();
return ok;
} catch {
return false;
}
}
}
function WorkflowKit({ recipe }) {
const k = recipe.kit;
if (!k) return null;
const rows = [
["You run", k.youRun],
["Tools", (k.tools || []).join(" · ")],
["Files", (k.files || []).join(" · ")],
["Model", k.model],
["Time", recipe.time],
["Cost", recipe.cost],
["You produce", k.artifact],
].filter(([, v]) => v);
return (
<dl className="workflow-kit" id={`${recipe.anchor}-kit`} aria-label="What this workflow uses">
{rows.map(([label, value]) => (
<div key={label}>
<dt>{label}</dt>
<dd>{value}</dd>
</div>
))}
</dl>
);
}
function Field({ anchor, name, label, children }) {
return (
<div className="row" id={`${anchor}-${name}`}>
<h4 className="k">{label}</h4>
<div className="v">{children}</div>
</div>
);
}
export default function WorkflowBlock({
recipe,
complete = false,
onToggleComplete,
focused = false,
index = 1,
total = 1,
}) {
const [copied, setCopied] = useState(false);
const a = recipe.anchor;
const onShare = async () => {
const ok = await copyText(recipe.shareBlurb);
if (!ok) return;
setCopied(true);
window.setTimeout(() => setCopied(false), 1800);
};
return (
<article
id={a}
className={`recipe recipe-nested${focused ? " recipe-focus" : ""}`}
aria-labelledby={`${a}-title`}
>
<div className="recipe-top">
<span className="recipe-id">
Workflow {index} of {total} · <b>{recipe.id}</b> · v{recipe.version} · {recipe.time} · {recipe.cost}
</span>
<span className="recipe-vert">{recipe.vertical}</span>
</div>
<div className="recipe-body">
<h3 className="recipe-title" id={`${a}-title`}>
{recipe.title}
</h3>
<WorkflowKit recipe={recipe} />
<nav className="recipe-jump" aria-label="In this workflow">
{JUMP.map((j) => (
<a key={j.id} href={`#${a}-${j.id}`}>
{j.label}
</a>
))}
</nav>
<Field anchor={a} name="problem" label="Problem">
{recipe.problem} <span className="sub">{recipe.problemJob}</span>
</Field>
<Field anchor={a} name="actors" label="Actors">
<ul className="plain-list">
{recipe.actors.map((line) => (
<li key={line}>{line}</li>
))}
</ul>
</Field>
<Field anchor={a} name="setup" label="Setup">
<ul className="plain-list">
{recipe.setup.map((line) => (
<li key={line}>{line}</li>
))}
</ul>
</Field>
<Field anchor={a} name="safeguards" label="Safeguards">
{recipe.safeguards.map((line) => (
<div className="guard" key={line}>
<ShieldCheck />
<span>{line}</span>
</div>
))}
</Field>
<Field anchor={a} name="method" label="Method">
<ol className="steps">
{recipe.method.map((step, i) => (
<li key={i}>
<div className="step-copy">
{step.text}
{step.term ? <TermOutput id={step.term} /> : null}
</div>
</li>
))}
</ol>
</Field>
<Field anchor={a} name="evidence" label="Evidence">
{recipe.evidence.map((line) => (
<p key={line} className="evidence-p">
{line}
</p>
))}
</Field>
<Field anchor={a} name="tryit" label="Try-it">
<p>
About {recipe.tryit.minutes} minutes. {recipe.tryit.sample}{" "}
{recipe.tryit.produce}
</p>
<p className="sub-line">
{recipe.tryit.done}{" "}
<a className="inline-sample" href={recipe.tryit.href} download>
{recipe.tryit.hrefLabel}
</a>
</p>
<p>
<span className="chip">◷ {recipe.tryit.chip}</span>
</p>
{onToggleComplete ? (
<div className="tryit-actions">
<button
type="button"
className={`btn ${complete ? "btn-primary tryit-done" : "btn-ghost"}`}
aria-pressed={complete}
onClick={() => onToggleComplete(recipe.id)}
>
{complete ? <CheckMark /> : null}
{complete ? "Counted as activation (demo)" : "Mark complete"}
</button>
</div>
) : null}
</Field>
<Field anchor={a} name="share" label="Share">
<p>Copy a short result line. Share the outcome, not the recipe text.</p>
<div className="tryit-actions">
<button type="button" className="btn btn-ghost" onClick={onShare}>
{copied ? "Copied" : "Copy result line"}
</button>
</div>
</Field>
<Field anchor={a} name="record" label="Record">
<div className="recipe-record">
<span>Author: {recipe.author}</span>
<span>Sources: {recipe.sources}</span>
<span>Last updated: {recipe.lastUpdated}</span>
{focused ? (
<span>
<Link href={`/read/real-estate#${recipe.anchor}`}>
Back to the chapter
</Link>
</span>
) : (
<span>
<Link href={`/read/recipe/${recipe.id}`}>Open on paper</Link>
</span>
)}
</div>
</Field>
</div>
</article>
);
}
@@ -0,0 +1,35 @@
export default function WorkflowRail({ recipes, complete = {}, total }) {
return (
<aside className="chapter-rail" aria-label="Workflows in this chapter">
<p className="spec">Workflows in this chapter</p>
<ol className="rail-list">
{recipes.map((r, i) => {
const done = !!complete[r.id];
return (
<li key={r.id}>
<a className={`rail-card${done ? " is-done" : ""}`} href={`#${r.anchor}`}>
<span className="rail-kicker">
{i + 1} of {total} · {r.id}
</span>
<span className="rail-title">{r.shortTitle}</span>
<span className="rail-meta">
{r.time} · {r.cost}
{done ? " · done" : ""}
</span>
{r.kit && r.kit.tools && r.kit.tools.length ? (
<span className="rail-kit">{r.kit.tools.join(" · ")}</span>
) : null}
{r.kit && r.kit.artifact ? (
<span className="rail-kit rail-out">{r.kit.artifact}</span>
) : null}
</a>
</li>
);
})}
</ol>
<a className="rail-skip" href={`#${recipes[0]?.anchor}-tryit`}>
Skip to the first Try-it
</a>
</aside>
);
}
@@ -0,0 +1,43 @@
import { useCallback, useEffect, useMemo, useState } from "react";
function keyFor(id) {
return `aibook-demo-activation:${id}`;
}
/**
* Demo-only activation state. Completing a Try-it writes
* localStorage key `aibook-demo-activation:<RECIPE-ID>`.
*/
export default function useActivation(ids) {
const list = useMemo(() => ids, [ids.join("|")]);
const [complete, setComplete] = useState(() =>
Object.fromEntries(list.map((id) => [id, false]))
);
useEffect(() => {
const next = {};
list.forEach((id) => {
try {
next[id] = window.localStorage.getItem(keyFor(id)) === "1";
} catch {
next[id] = false;
}
});
setComplete(next);
}, [list]);
const toggle = useCallback((id) => {
setComplete((prev) => {
const value = !prev[id];
try {
window.localStorage.setItem(keyFor(id), value ? "1" : "0");
} catch {
/* private mode: still toggle in memory */
}
return { ...prev, [id]: value };
});
}, []);
const count = list.filter((id) => complete[id]).length;
return { complete, toggle, count, total: list.length };
}
+311
View File
@@ -0,0 +1,311 @@
/**
* Sample chapter + nested recipes for the reading demo.
* Imported by pages under /read. Not a CMS.
*/
export const LIBRARY = [
{
num: "01",
name: "Real Estate",
slug: "real-estate",
href: "/read/real-estate",
desc: "Market briefs and listings, without leaking client data",
status: "ready",
},
{
num: "02",
name: "Financial Advisors",
slug: "financial-advisors",
desc: "Portfolio summaries that stay compliant and reviewed",
status: "coming",
},
{
num: "03",
name: "Fractional C-Suite",
slug: "fractional-c-suite",
desc: "Board updates and plans with real escalation paths",
status: "coming",
},
{
num: "04",
name: "Higher Ed",
slug: "higher-ed",
desc: "Course and research workflows with academic integrity",
status: "coming",
},
{
num: "05",
name: "Communities",
slug: "communities",
desc: "Moderation and programming that respects members",
status: "coming",
},
{
num: "06",
name: "Technology",
slug: "technology",
desc: "Agent patterns, evals, and the responsible defaults",
status: "coming",
},
];
const RE01 = {
id: "RE-01",
anchor: "re-01",
version: "1.2",
time: "~15 min",
cost: "≈ $0.03",
vertical: "Real Estate",
shortTitle: "Market brief",
title: "Draft a property-market brief with an AI agent without leaking client data.",
problem:
"You need a neighborhood market brief for a client meeting this week. The listing sheet and client notes cannot go into a public model.",
problemJob:
"Job: turn messy local data into a credible one-pager in minutes, safely.",
actors: [
"Listing agent runs it.",
"Client receives the brief.",
"Model may draft and classify. A human must review before send.",
],
setup: [
"Tools: local redaction script (brief CLI), Claude or your usual model.",
"Sample files: sample_listings.csv, market-1pager template.",
"Cost: about $0.03 on the sample run (2,140 tokens).",
],
safeguards: [
"Redaction runs on your machine. Raw client data never reaches the model.",
"A human checklist is required before the brief is shared. The checklist is part of the recipe.",
"Stop if identifiers remain after the strip report. Do not prompt until owner_name, phone, and exact_address are gone.",
],
method: [
{
text: "Strip identifiers from the source data locally (a provided script does this).",
},
{
text: "Prompt the agent with the anonymized sample and a fixed brief template.",
term: "re-01",
},
{
text: "Review the draft against the checklist before anything leaves your machine.",
},
],
evidence: [
"A one-pager brief on Maple Heights (sample), ready for a meeting once a human signs the checklist.",
"Sample metrics from that run: median $420/sqft, 22 days on market, 1.8 months of inventory.",
],
tryit: {
minutes: 15,
sample: "Use the included sample_listings.csv (no live client file).",
produce: "Produce the market-1pager brief and walk the human checklist.",
done: "Mark complete when you have a brief you would send after review. Completing this is the activation demo.",
href: "/samples/sample_listings.csv",
hrefLabel: "sample_listings.csv",
chip: "cost to try ≈ $0.03",
},
shareBlurb:
"Ran RE-01 on sample comps. Maple Heights median $420/sqft, 22 DOM.",
author: "Founding library · sample",
sources: "Sample MLS packet (non-confidential); market-1pager template shipped with the recipe.",
lastUpdated: "31 Aug 2026",
kit: {
youRun: "Listing agent",
tools: ["brief CLI (local redaction)", "Claude"],
files: ["sample_listings.csv", "market-1pager template"],
model: "Claude",
artifact: "One-pager market brief",
},
};
const RE02 = {
id: "RE-02",
anchor: "re-02",
version: "1.0",
time: "~12 min",
cost: "≈ $0.02",
vertical: "Real Estate",
shortTitle: "Listing copy",
title: "Rewrite a listing description from public comps without pasting the client address book.",
problem:
"You need listing copy that sounds like this house, not every house on the street. The CRM holds owner notes and the showing list. Those do not go into a public model.",
problemJob:
"Job: draft 180-word listing copy from public facts only, then edit it yourself.",
actors: [
"Listing agent runs it.",
"The public listing receives the copy.",
"Model drafts. A human edits, files, and posts to MLS.",
],
setup: [
"Tools: listing-copy CLI, listing-voice template, Claude or your usual model.",
"Sample files: sample_comps.json (public comps only).",
"Cost: about $0.02 on the sample run (1,280 tokens).",
],
safeguards: [
"Prompt only with public listing facts. No CRM export, no showing list, no owner notes.",
"A human edits the draft before it hits MLS. Do not auto-post.",
"Stop if a private note, phone, or exact owner name appears in the draft.",
],
method: [
{
text: "Export public comps for the block. Leave the CRM closed.",
},
{
text: "Run listing-copy against the sample comps and the house-voice template.",
term: "re-02",
},
{
text: "Edit the draft. Cut anything that could only have come from a private note.",
},
],
evidence: [
"A 184-word listing draft, public facts only, reading grade 8.",
"Comps on the block closed between $398 and $441/sqft in 60 days. The draft prices to that range without naming a client.",
],
tryit: {
minutes: 12,
sample: "Use the included sample_comps.json (public comps, no address book).",
produce: "Produce a ~180-word listing description and a human edit pass.",
done: "Mark complete when the draft has no private fields. Completing this is the activation demo.",
href: "/samples/sample_comps.json",
hrefLabel: "sample_comps.json",
chip: "cost to try ≈ $0.02",
},
shareBlurb:
"Ran RE-02 on sample comps. 184-word listing draft, public facts only.",
author: "Founding library · sample",
sources: "Public comps sample packet; listing-voice template shipped with the recipe.",
lastUpdated: "31 Aug 2026",
kit: {
youRun: "Listing agent",
tools: ["listing-copy CLI", "Claude"],
files: ["sample_comps.json", "listing-voice template"],
model: "Claude",
artifact: "180-word listing draft",
},
};
export const RECIPES = [RE01, RE02];
export const RECIPES_BY_ID = {
"RE-01": RE01,
"RE-02": RE02,
// Old demo URLs. Displayed ID is RE-01 / RE-02.
"RE-04": RE01,
"RE-05": RE02,
};
export const CHAPTER = {
slug: "real-estate",
vertical: "Real Estate",
edition: "2026",
authors: "Founding library · sample",
title: "Work the listing without leaking the client",
promise:
"In this chapter you run two jobs a realtor actually has this week, on sample data, without putting a client file in a public model.",
lastUpdated: "31 Aug 2026",
skip: [
{ id: "RE-01", href: "#re-01", title: "Market brief" },
{ id: "RE-02", href: "#re-02", title: "Listing copy" },
],
stakes: {
id: "stakes",
heading: "The stakes",
paragraphs: [
"A listing that stalls because the market brief is late is a lost weekend. A listing that leaks because you pasted the client sheet into a public model is a lost license. Real estate work runs on both clocks: the showing at 4pm, and the file that must never leave the office.",
"The failure is not that AI sits unused. It is a one-pager that is late, generic, or built from a client name that should not have been in the prompt. Buyers notice fluff. Brokers notice a privacy incident. Neither is recoverable in the same week.",
],
},
constraints: {
id: "constraints",
heading: "Constraints you cannot ignore",
paragraphs: [
"You already work in MLS exports, a CRM, and a phone that rings during the write-up. Done is a brief the client can read in a meeting, with numbers you can stand behind, reviewed by a human. Public models are useful for draft and classify. They are not a filing cabinet.",
"Owner names, phones, exact addresses, and showing lists stay on your machine. Comps, medians, and days on market can travel, once identifiers are stripped. If a field could pick one household out of the block, it does not go in the prompt.",
],
},
practice: {
id: "practice",
heading: "How we work here",
paragraphs: [
"Redact locally. Prompt with a sample or a stripped file. Review against a checklist. Share the artifact, not the workflow text. The first job this week is the neighborhood market brief. Run it on sample comps before you touch a live listing.",
],
},
between: {
id: "between",
heading: "The brief is half the meeting",
paragraphs: [
"The brief gets you into the room with numbers. The other half is the listing copy: public comps, a description that does not sound like every other house on the street, and an address book that never belongs in the prompt. Same rule. Different job.",
],
},
further: [
{ id: "RE-01", href: "#re-01", title: "Draft a property-market brief without leaking client data" },
{ id: "RE-02", href: "#re-02", title: "Rewrite a listing description from public comps" },
{ id: "RE-03", title: "Offer-response notes from public remarks (later)", coming: true },
],
sources: [
"Sample MLS packet shipped with this chapter (non-confidential).",
"market-1pager and listing-voice templates in the recipe kit.",
"Local redaction fields: owner_name, phone, exact_address.",
],
};
/** Outline groups for the chapter map. Headings come from the essay objects. */
export function buildOutline(chapter, recipes) {
return [
{
label: "Start",
items: [{ id: "need", label: "What you need" }],
},
{
label: "Essay",
items: [chapter.stakes, chapter.constraints, chapter.practice, chapter.between].map((s) => ({
id: s.id,
label: s.heading,
})),
},
{
label: "Workflows",
items: recipes.map((r) => ({
id: r.anchor,
recipeId: r.id,
label: r.id + " · " + r.shortTitle,
time: r.time,
shortTitle: r.shortTitle,
tools: r.kit && r.kit.tools ? r.kit.tools.join(", ") : "",
})),
},
{
label: "End",
items: [
{ id: "further", label: "Further workflows" },
{ id: "sources", label: "Sources" },
],
},
];
}
/** Union of recipe kits for the chapter "What you need" strip. */
export function collectChapterKit(recipes) {
const tools = [];
const files = [];
const models = [];
const add = (arr, value) => {
if (value && !arr.includes(value)) arr.push(value);
};
recipes.forEach((r) => {
const k = r.kit || {};
(k.tools || []).forEach((item) => add(tools, item));
(k.files || []).forEach((item) => add(files, item));
add(models, k.model);
});
return {
tools,
files,
models,
tryits: recipes.map((r) => ({
id: r.id,
time: r.time,
cost: r.cost,
artifact: r.kit && r.kit.artifact,
})),
};
}
-3
View File
@@ -1,9 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
// Deploy layer (deploy/docker/Dockerfile) builds the standalone
// output. No runtime behavior change for preview/dev.
output: 'standalone',
// A stray package-lock.json sits at the repo root; pin the workspace root here
// so Turbopack doesn't infer the wrong one.
turbopack: {
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "aibook-fun-marketing",
"version": "1.0.0",
"private": true,
"description": "AIBook.fun — pre-launch marketing site (field-guide design)",
"description": "AiBook.fun — pre-launch marketing site (field-guide design)",
"license": "MIT",
"scripts": {
"dev": "next dev -p 3000",
+2 -2
View File
@@ -18,10 +18,10 @@ export default function Home() {
return (
<>
<Head>
<title>AIBook.fun — a field guide to AI agents at work</title>
<title>AiBook.fun · a field guide to AI agents at work</title>
<meta
name="description"
content="A living, crowd-written field guide to using AI agents responsibly at work. Tested workflows you can run — launching on Kickstarter, Sept 26, 2026."
content="An authored field guide of runnable AI workflows for work. Updated as the tools change. Kickstarter Sept 26, 2026. $47/yr."
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
+56
View File
@@ -0,0 +1,56 @@
import Head from "next/head";
import Link from "next/link";
import ReaderNav from "../../components/reader/ReaderNav";
import ReaderFooter from "../../components/reader/ReaderFooter";
import { LIBRARY, RECIPES } from "../../data/sample-chapter";
export default function ReadLibrary() {
return (
<>
<Head>
<title>Library · AiBook.fun reading demo</title>
<meta
name="description"
content="Library for the 2026 AiBook.fun field guide. Six verticals. One preview chapter open."
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<ReaderNav />
<main>
<section className="section contents reader-library">
<div className="wrap reader-wide">
<span className="spec demo-chip">Reading demo · not the live book</span>
<h1 className="library-title">Library</h1>
<p className="library-lede">
Six verticals for the 2026 edition.
</p>
<div className="toc">
{LIBRARY.map((item) =>
item.status === "ready" ? (
<Link href={item.href} key={item.num}>
<span className="num">{item.num}</span>
<span className="name">{item.name}</span>
<span className="desc">{item.desc}</span>
<span className="toc-count">{RECIPES.length} workflows</span>
<span className="arrow" aria-hidden="true">
→
</span>
</Link>
) : (
<div className="toc-soon" key={item.num} aria-disabled="true">
<span className="num">{item.num}</span>
<span className="name">{item.name}</span>
<span className="desc">{item.desc}</span>
<span className="soon">Coming</span>
</div>
)
)}
</div>
</div>
</section>
</main>
<ReaderFooter />
</>
);
}
+208
View File
@@ -0,0 +1,208 @@
import { useEffect, useMemo } from "react";
import Head from "next/head";
import ReaderNav from "../../components/reader/ReaderNav";
import ReaderCrumbs from "../../components/reader/ReaderCrumbs";
import ReaderFooter from "../../components/reader/ReaderFooter";
import WorkflowBlock from "../../components/reader/WorkflowBlock";
import ChapterOutline from "../../components/reader/ChapterOutline";
import WorkflowRail from "../../components/reader/WorkflowRail";
import ChapterKit from "../../components/reader/ChapterKit";
import useActivation from "../../components/reader/useActivation";
import { CHAPTER, RECIPES, buildOutline } from "../../data/sample-chapter";
const RECIPE_IDS = RECIPES.map((r) => r.id);
const ESSAY_KEYS = ["stakes", "constraints", "practice"];
function Essay({ id, heading, paragraphs }) {
return (
<section className="essay" id={id}>
<h2>{heading}</h2>
{paragraphs.map((p) => (
<p key={p}>{p}</p>
))}
</section>
);
}
export default function RealEstateChapter() {
const { complete, toggle, count, total } = useActivation(RECIPE_IDS);
const [first, second] = RECIPES;
const outline = useMemo(() => buildOutline(CHAPTER, RECIPES), []);
const essayGroup = outline.find((g) => g.label === "Essay");
const workflowGroup = outline.find((g) => g.label === "Workflows");
useEffect(() => {
const hash = window.location.hash;
if (!hash) return;
const el = document.getElementById(hash.slice(1));
if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
}, []);
return (
<>
<Head>
<title>{CHAPTER.title} · AiBook.fun reading demo</title>
<meta name="description" content={CHAPTER.promise} />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<ReaderNav here="chapter" />
<main>
<article className="chapter-page">
<div className="wrap reader-wide">
<ReaderCrumbs
items={[
{ href: "/read", label: "Library" },
{ label: CHAPTER.vertical, current: true },
]}
/>
</div>
<div className="wrap reader-wide chapter-shell">
<ChapterOutline
groups={outline}
complete={complete}
count={count}
total={total}
/>
<div className="chapter-col">
<span className="spec demo-chip">Reading demo · not the live book</span>
<p className="spec chapter-kicker">
{CHAPTER.vertical} · {CHAPTER.edition} edition
</p>
<h1 className="chapter-title">{CHAPTER.title}</h1>
<p className="chapter-by">{CHAPTER.authors}</p>
<ChapterOutline
variant="mobile"
groups={outline}
complete={complete}
count={count}
total={total}
/>
<p className="chapter-promise">{CHAPTER.promise}</p>
<ChapterKit recipes={RECIPES} />
<section className="chapter-map" aria-label="What's in this chapter">
<h2 className="chapter-map-title">What's in this chapter</h2>
<div className="chapter-map-block">
<p className="spec">Essay</p>
<ul>
{essayGroup.items.map((item) => (
<li key={item.id}>
<a href={`#${item.id}`}>{item.label}</a>
</li>
))}
</ul>
</div>
<div className="chapter-map-block">
<p className="spec">Workflows</p>
<ul>
{workflowGroup.items.map((item) => (
<li key={item.id}>
<a href={`#${item.id}`}>
{item.recipeId} · {item.shortTitle} · {item.time}
{item.tools ? ` · ${item.tools}` : ""}
</a>
</li>
))}
</ul>
</div>
</section>
<div className="skip-row">
<span className="skip-label">Skip to a Try-it</span>
{RECIPES.map((r) => (
<a key={r.id} className="skip-chip" href={`#${r.anchor}`}>
{r.id}
<span className="skip-chip-job">{r.shortTitle}</span>
</a>
))}
</div>
{ESSAY_KEYS.map((key) => (
<Essay
key={key}
id={CHAPTER[key].id}
heading={CHAPTER[key].heading}
paragraphs={CHAPTER[key].paragraphs}
/>
))}
<p className="spec workflow-kicker">
Workflow 1 of {RECIPES.length} in this chapter
</p>
<WorkflowBlock
recipe={first}
index={1}
total={RECIPES.length}
complete={!!complete[first.id]}
onToggleComplete={toggle}
/>
<Essay
id={CHAPTER.between.id}
heading={CHAPTER.between.heading}
paragraphs={CHAPTER.between.paragraphs}
/>
<p className="spec workflow-kicker">
Workflow 2 of {RECIPES.length} in this chapter
</p>
<WorkflowBlock
recipe={second}
index={2}
total={RECIPES.length}
complete={!!complete[second.id]}
onToggleComplete={toggle}
/>
<section className="end-matter" id="further">
<h2>Further workflows in this vertical</h2>
<ul className="further-list">
{CHAPTER.further.map((item) => (
<li key={item.id}>
{item.coming ? (
<>
<span className="recipe-id">
<b>{item.id}</b>
</span>{" "}
{item.title}
</>
) : (
<>
<a href={item.href}>
<span className="recipe-id">
<b>{item.id}</b>
</span>
</a>{" "}
{item.title}
</>
)}
</li>
))}
</ul>
</section>
<section className="end-sources" id="sources">
<h2>Sources</h2>
<ul className="further-list">
{CHAPTER.sources.map((s) => (
<li key={s}>{s}</li>
))}
</ul>
<p className="chapter-updated">Last updated: {CHAPTER.lastUpdated}</p>
</section>
</div>
<WorkflowRail recipes={RECIPES} complete={complete} total={total} />
</div>
</article>
</main>
<ReaderFooter />
</>
);
}
+91
View File
@@ -0,0 +1,91 @@
import Head from "next/head";
import Link from "next/link";
import { useRouter } from "next/router";
import ReaderNav from "../../../components/reader/ReaderNav";
import ReaderCrumbs from "../../../components/reader/ReaderCrumbs";
import ReaderFooter from "../../../components/reader/ReaderFooter";
import WorkflowBlock from "../../../components/reader/WorkflowBlock";
import useActivation from "../../../components/reader/useActivation";
import { RECIPES, RECIPES_BY_ID } from "../../../data/sample-chapter";
export default function RecipeFocus() {
const router = useRouter();
const raw = typeof router.query.id === "string" ? router.query.id : "";
const recipe = RECIPES_BY_ID[raw] || RECIPES_BY_ID[raw.toUpperCase()] || null;
const { complete, toggle } = useActivation(recipe ? [recipe.id] : []);
if (!router.isReady) {
return (
<>
<ReaderNav here="chapter" />
<main>
<div className="wrap reader-wide recipe-focus-page">
<p className="spec">Loading recipe…</p>
</div>
</main>
</>
);
}
if (!recipe) {
return (
<>
<Head>
<title>Recipe not in this demo · AiBook.fun</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<ReaderNav />
<main>
<div className="wrap reader-wide recipe-focus-page">
<span className="spec demo-chip">Reading demo · not the live book</span>
<h1 className="chapter-title">This recipe is not in the sample.</h1>
<p className="library-lede">
The demo ships two nested workflows, RE-01 and RE-02, inside the Real
Estate chapter.
</p>
<p>
<Link href="/read/real-estate" className="btn btn-primary">
Back to the chapter
</Link>
</p>
</div>
</main>
<ReaderFooter />
</>
);
}
return (
<>
<Head>
<title>{`${recipe.id} on paper · AiBook.fun reading demo`}</title>
<meta name="description" content={recipe.title} />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</Head>
<ReaderNav here="chapter" />
<main>
<div className="wrap reader-wide recipe-focus-page">
<ReaderCrumbs
items={[
{ href: "/read", label: "Library" },
{ href: "/read/real-estate", label: "Real Estate" },
{ label: recipe.id, current: true },
]}
/>
<span className="spec demo-chip">Reading demo · not the live book</span>
<p className="spec chapter-kicker">Print / focus view</p>
<WorkflowBlock
recipe={recipe}
index={Math.max(RECIPES.findIndex((r) => r.id === recipe.id) + 1, 1)}
total={RECIPES.length}
complete={!!complete[recipe.id]}
onToggleComplete={toggle}
focused
/>
</div>
</main>
<ReaderFooter />
</>
);
}
@@ -0,0 +1,19 @@
{
"note": "Public comps only. No CRM fields, no owner names, no phones, no showing lists.",
"block": "14xx Cedar (sample street)",
"subject": {
"beds": 3,
"baths": 2,
"sqft": 1320,
"year_kitchen": 2019,
"lot": "corner, public record"
},
"comps": [
{ "id": "C1", "closed_price_sqft": 398, "dom": 19, "beds": 3, "baths": 2, "closed_days_ago": 18 },
{ "id": "C2", "closed_price_sqft": 441, "dom": 11, "beds": 3, "baths": 2, "closed_days_ago": 27 },
{ "id": "C3", "closed_price_sqft": 412, "dom": 22, "beds": 3, "baths": 2, "closed_days_ago": 41 },
{ "id": "C4", "closed_price_sqft": 425, "dom": 16, "beds": 3, "baths": 3, "closed_days_ago": 9 },
{ "id": "C5", "closed_price_sqft": 405, "dom": 28, "beds": 2, "baths": 2, "closed_days_ago": 55 },
{ "id": "C6", "closed_price_sqft": 433, "dom": 14, "beds": 3, "baths": 2, "closed_days_ago": 33 }
]
}
@@ -0,0 +1,7 @@
id,neighborhood,list_price,sqft,dom,owner_name,phone,exact_address,beds,baths
MH-01,Maple Heights,504000,1200,18,Jane Client,555-0140,12 Oak Street,3,2
MH-02,Maple Heights,612000,1450,24,Sam Owner,555-0141,88 Birch Avenue,3,2
MH-03,Maple Heights,459000,1100,31,R. Patel,555-0142,4 Cedar Court,2,2
MH-04,Maple Heights,670000,1580,12,Lee Family Trust,555-0143,21 Maple Drive,4,3
MH-05,Maple Heights,538000,1288,22,A. Gomez,555-0144,9 Pine Lane,3,2
MH-06,Maple Heights,481000,1140,27,Hidden,555-0145,16 Walnut Place,3,1
1 id neighborhood list_price sqft dom owner_name phone exact_address beds baths
2 MH-01 Maple Heights 504000 1200 18 Jane Client 555-0140 12 Oak Street 3 2
3 MH-02 Maple Heights 612000 1450 24 Sam Owner 555-0141 88 Birch Avenue 3 2
4 MH-03 Maple Heights 459000 1100 31 R. Patel 555-0142 4 Cedar Court 2 2
5 MH-04 Maple Heights 670000 1580 12 Lee Family Trust 555-0143 21 Maple Drive 4 3
6 MH-05 Maple Heights 538000 1288 22 A. Gomez 555-0144 9 Pine Lane 3 2
7 MH-06 Maple Heights 481000 1140 27 Hidden 555-0145 16 Walnut Place 3 1
+467 -11
View File
@@ -1,7 +1,7 @@
@import "tailwindcss";
/* ============================================================
AIBook.fun — "field guide" design system
AiBook.fun — "field guide" design system
Tokens live in @theme so they're available BOTH as CSS vars
(var(--color-pine)) and as Tailwind utilities (bg-pine, font-display).
============================================================ */
@@ -104,21 +104,33 @@ h1, h2, h3 { text-wrap: balance; margin: 0; }
/* ============ NAV ============ */
.nav {
position: sticky; top: 0; z-index: 50;
background: color-mix(in srgb, var(--color-paper) 82%, transparent);
backdrop-filter: blur(14px) saturate(1.3); -webkit-backdrop-filter: blur(14px) saturate(1.3);
background: var(--color-paper);
border-bottom: 1px solid transparent; transition: border-color 0.3s, box-shadow 0.3s;
}
.nav.scrolled { border-bottom-color: var(--color-line); box-shadow: var(--sh-sm); }
.nav-in {
position: relative;
max-width: var(--wrap); margin: 0 auto; padding: 0 28px; height: 68px;
display: flex; align-items: center; justify-content: space-between; gap: 20px;
}
.logo { display: flex; align-items: baseline; gap: 2px; font-family: var(--font-display); font-weight: 900; font-size: 21px; letter-spacing: -0.02em; }
.logo {
display: flex; align-items: baseline; gap: 2px;
font-family: var(--font-display); font-weight: 900; font-size: 21px; letter-spacing: -0.02em;
flex: none; position: relative; z-index: 1;
}
.logo b { color: var(--color-pine); }
.logo .dot { color: var(--color-muted); font-weight: 500; }
.nav-links { display: flex; align-items: center; gap: 30px; font-size: 14.5px; font-weight: 600; color: var(--color-muted); }
.nav-links {
display: flex; align-items: center; gap: 30px;
font-size: 14.5px; font-weight: 600; color: var(--color-muted);
position: absolute; left: 50%; transform: translateX(-50%);
}
.nav-links a { transition: color 0.18s; }
.nav-links a:hover { color: var(--color-ink); }
.nav-actions {
display: flex; align-items: center; gap: 10px;
flex: none; margin-left: auto; position: relative; z-index: 1;
}
.nav-cta { font-size: 14px; padding: 10px 18px; }
/* mobile hamburger toggle (hidden on desktop) */
@@ -144,6 +156,7 @@ h1, h2, h3 { text-wrap: balance; margin: 0; }
@media (max-width: 860px) {
.nav-links { display: none; }
.nav-actions { display: none; }
.nav-cta { display: none; } /* CTA moves into the menu */
.nav-toggle { display: inline-flex; }
.nav-menu {
@@ -281,7 +294,7 @@ h1, h2, h3 { text-wrap: balance; margin: 0; }
/* ============ RECIPE (value centerpiece) ============ */
.recipe-head { max-width: 40rem; margin-bottom: 44px; }
.recipe-head h2 { font-family: var(--font-display); font-weight: 900; font-size: clamp(2rem, 4.3vw, 3.3rem); letter-spacing: -0.025em; line-height: 1.02; margin: 16px 0 18px; }
.recipe-head h2 { font-family: var(--font-display); font-weight: 900; font-size: clamp(2rem, 4.3vw, 3.3rem); letter-spacing: -0.025em; line-height: 1.15; margin: 16px 0 18px; }
.recipe-head p { color: var(--color-muted); font-size: 1.08rem; }
.recipe { background: var(--color-card); border: 1px solid var(--color-line); border-radius: 20px; box-shadow: var(--sh-lg); overflow: hidden; }
.recipe-top { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 20px 28px; border-bottom: 1px solid var(--color-line); background: linear-gradient(var(--color-card), var(--color-paper)); }
@@ -327,7 +340,7 @@ h1, h2, h3 { text-wrap: balance; margin: 0; }
/* ============ CONTENTS (table-of-contents) ============ */
.contents { background: var(--color-paper-2); border-top: 1px solid var(--color-line); border-bottom: 1px solid var(--color-line); }
.contents h2 { font-family: var(--font-display); font-weight: 900; font-size: clamp(1.9rem, 4vw, 3rem); letter-spacing: -0.025em; margin: 14px 0 34px; max-width: 20ch; }
.contents h2 { font-family: var(--font-display); font-weight: 900; font-size: clamp(1.9rem, 4vw, 3rem); letter-spacing: -0.025em; line-height: 1.2; margin: 14px 0 34px; max-width: 20ch; }
.toc { border-top: 1px solid var(--color-line-2); }
.toc a { display: grid; grid-template-columns: 52px 1fr auto auto; align-items: center; gap: 20px; padding: 22px 8px; border-bottom: 1px solid var(--color-line); transition: background 0.2s, padding 0.2s; }
.toc a:hover { background: var(--color-pine-wash); padding-left: 16px; }
@@ -352,7 +365,7 @@ h1, h2, h3 { text-wrap: balance; margin: 0; }
@media (max-width: 600px) { .letter-in { padding: 0 20px; } }
/* ============ AUTHORS ============ */
.authors h2 { font-family: var(--font-display); font-weight: 900; font-size: clamp(1.9rem, 3.6vw, 2.7rem); letter-spacing: -0.025em; margin: 14px 0 10px; }
.authors h2 { font-family: var(--font-display); font-weight: 900; font-size: clamp(1.9rem, 3.6vw, 2.7rem); letter-spacing: -0.025em; line-height: 1.2; margin: 14px 0 10px; }
.authors .lede { color: var(--color-muted); max-width: 38rem; margin-bottom: 38px; }
.auth-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.auth { background: var(--color-card); border: 1px solid var(--color-line); border-radius: 16px; padding: 24px; box-shadow: var(--sh-sm); transition: box-shadow 0.2s, transform 0.2s; }
@@ -372,7 +385,7 @@ h1, h2, h3 { text-wrap: balance; margin: 0; }
/* ============ EMAIL CAPTURE ============ */
.capture { background: var(--color-paper-2); border-top: 1px solid var(--color-line); border-bottom: 1px solid var(--color-line); }
.capture-in { max-width: 640px; margin: 0 auto; text-align: center; padding: 0 28px; }
.capture h2 { font-family: var(--font-display); font-weight: 900; font-size: clamp(2rem, 4vw, 3rem); letter-spacing: -0.025em; line-height: 1.02; margin: 16px 0 14px; }
.capture h2 { font-family: var(--font-display); font-weight: 900; font-size: clamp(2rem, 4vw, 3rem); letter-spacing: -0.025em; line-height: 1.15; margin: 16px 0 14px; }
.capture p { color: var(--color-muted); font-size: 1.06rem; margin-bottom: 28px; }
.capture form { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }
.capture .fine { font-family: var(--font-mono); font-size: 11.5px; color: var(--color-muted); margin-top: 18px; }
@@ -381,7 +394,7 @@ h1, h2, h3 { text-wrap: balance; margin: 0; }
/* ============ TIERS PREVIEW ============ */
.tiers-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; flex-wrap: wrap; margin-bottom: 34px; }
.tiers-head h2 { font-family: var(--font-display); font-weight: 900; font-size: clamp(1.9rem, 3.6vw, 2.7rem); letter-spacing: -0.025em; margin: 12px 0 0; }
.tiers-head h2 { font-family: var(--font-display); font-weight: 900; font-size: clamp(1.9rem, 3.6vw, 2.7rem); letter-spacing: -0.025em; line-height: 1.2; margin: 12px 0 0; }
.tiers-note { font-family: var(--font-mono); font-size: 12px; color: var(--color-muted); max-width: 22rem; text-align: right; line-height: 1.6; }
.tier-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.tier { background: var(--color-card); border: 1px solid var(--color-line); border-radius: 18px; padding: 30px 26px; display: flex; flex-direction: column; transition: box-shadow 0.2s, transform 0.2s; }
@@ -399,7 +412,7 @@ h1, h2, h3 { text-wrap: balance; margin: 0; }
@media (max-width: 820px) { .tier-grid { grid-template-columns: 1fr; } .tiers-note { text-align: left; } }
/* ============ FAQ ============ */
.faq h2 { font-family: var(--font-display); font-weight: 900; font-size: clamp(1.9rem, 3.6vw, 2.7rem); letter-spacing: -0.025em; margin-bottom: 34px; }
.faq h2 { font-family: var(--font-display); font-weight: 900; font-size: clamp(1.9rem, 3.6vw, 2.7rem); letter-spacing: -0.025em; line-height: 1.2; margin-bottom: 34px; }
.qa { border-top: 1px solid var(--color-line); }
.qa details { border-bottom: 1px solid var(--color-line); padding: 6px 0; }
.qa summary { cursor: pointer; list-style: none; padding: 20px 0; display: flex; align-items: center; justify-content: space-between; gap: 20px; font-family: var(--font-display); font-weight: 600; font-size: 1.2rem; letter-spacing: -0.02em; }
@@ -421,3 +434,446 @@ h1, h2, h3 { text-wrap: balance; margin: 0; }
.foot-links a:hover { color: #f7f5f0; }
.colophon { padding-top: 26px; font-family: var(--font-mono); font-size: 11.5px; color: #7a7266; letter-spacing: 0.02em; line-height: 1.8; }
@media (max-width: 600px) { .foot-in { padding: 48px 20px 40px; } }
/* ============ READER DEMO ============ */
/* Layout the landing page does not already have. Reader pages use a wider wrap.
Same tokens, same ~20px cards and 999px pills. Do not restyle the homepage recipe. */
.demo-chip {
display: inline-flex; align-items: center; gap: 8px;
font-family: var(--font-mono); font-size: 11px; font-weight: 500;
letter-spacing: 0.12em; text-transform: uppercase;
color: var(--color-amber);
background: rgba(180, 121, 31, 0.08);
border: 1px solid rgba(180, 121, 31, 0.22);
padding: 5px 12px; border-radius: 999px;
margin-bottom: 22px;
}
.reader-nav .nav-in,
.reader-footer .foot-in,
.wrap.reader-wide {
max-width: min(1520px, calc(100vw - 40px));
}
.crumbs {
padding: 4px 0 18px;
font-size: 13.5px;
}
.crumbs ol {
display: flex; flex-wrap: wrap; align-items: center;
list-style: none; margin: 0; padding: 0;
}
.crumbs li { display: flex; align-items: center; }
.crumbs-sep {
margin: 0 10px; color: var(--color-muted); font-weight: 400;
}
.crumbs a { color: var(--color-pine); font-weight: 600; }
.crumbs a:hover { color: var(--color-pine-deep); }
.crumbs [aria-current="page"] { color: var(--color-ink); font-weight: 600; }
.reader-nav .nav-title {
font-size: 14.5px; font-weight: 600; color: var(--color-ink);
}
.reader-nav .nav-title:hover { color: var(--color-pine); }
.reader-nav .reader-links { gap: 22px; }
.reader-nav .nav-current {
font-size: 14.5px; font-weight: 600; color: var(--color-ink);
max-width: 22ch; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
@media (max-width: 860px) {
.reader-nav .nav-links {
display: flex; align-items: center; gap: 16px; font-size: 13.5px;
}
.reader-nav .nav-cta {
display: inline-flex; font-size: 13px; padding: 8px 14px;
}
.reader-nav .nav-current { display: none; }
.reader-nav .nav-toggle { display: none; }
}
.chapter-page { padding: 48px 0 96px; }
.chapter-shell {
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
gap: 32px 40px;
align-items: start;
}
@media (min-width: 1080px) {
.chapter-shell {
grid-template-columns: 240px minmax(0, 1fr) 280px;
}
}
.chapter-col { min-width: 0; }
.chapter-kicker { margin: 8px 0 14px; }
.chapter-title {
font-family: var(--font-display); font-weight: 900;
font-size: clamp(2.1rem, 4.6vw, 3.4rem);
letter-spacing: -0.028em; line-height: 1.15; margin: 0 0 12px;
max-width: 22ch;
}
.chapter-by {
font-family: var(--font-mono); font-size: 12.5px; color: var(--color-muted);
letter-spacing: 0.04em; margin-bottom: 20px;
}
.chapter-promise {
font-size: 1.12rem; line-height: 1.62; color: var(--color-ink); margin: 0 0 28px;
max-width: 46rem;
}
.chapter-outline {
position: sticky;
top: 68px;
z-index: 30;
align-self: start;
background: var(--color-paper);
border-right: 1px solid var(--color-line);
padding: 16px 20px 28px 0;
max-height: calc(100vh - 68px);
overflow-y: auto;
}
.outline-progress {
font-family: var(--font-mono); font-size: 11.5px; color: var(--color-muted);
letter-spacing: 0.04em; margin: 0 0 14px;
}
.outline-heading {
font-family: var(--font-display); font-weight: 600;
font-size: 1.08rem; letter-spacing: -0.02em; margin: 0 0 16px;
}
.outline-group { margin: 0 0 18px; }
.outline-group:last-child { margin-bottom: 0; }
.outline-group-label { margin: 0 0 8px; }
.outline-group ul { list-style: none; margin: 0; padding: 0; }
.outline-group li { margin: 0; }
.outline-group a {
display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
padding: 5px 0 5px 10px;
border-left: 2px solid transparent;
font-size: 13.5px; line-height: 1.35; color: var(--color-muted);
}
.outline-group a:hover { color: var(--color-ink); }
.outline-group a.is-active {
color: var(--color-pine); border-left-color: var(--color-pine); font-weight: 600;
}
.outline-done {
font-family: var(--font-mono); font-size: 12px; color: var(--color-pine); flex: none;
}
.chapter-rail {
position: sticky;
top: 84px;
align-self: start;
display: none;
}
@media (min-width: 1080px) {
.chapter-rail { display: block; }
}
.chapter-rail .spec { margin: 0 0 14px; }
.rail-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.rail-card {
display: flex; flex-direction: column; gap: 6px;
padding: 16px 18px;
background: var(--color-card);
border: 1px solid var(--color-line);
border-radius: 16px;
box-shadow: var(--sh-sm);
transition: border-color 0.18s, transform 0.14s var(--ease), box-shadow 0.18s;
}
.rail-card:hover {
border-color: var(--color-pine-tint);
transform: translateY(-1px);
box-shadow: var(--sh-md);
}
.rail-card.is-done { border-color: var(--color-pine-tint); background: var(--color-pine-wash); }
.rail-kicker {
font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em;
text-transform: uppercase; color: var(--color-pine);
}
.rail-title {
font-family: var(--font-display); font-weight: 600;
font-size: 1.15rem; letter-spacing: -0.02em; line-height: 1.2;
color: var(--color-ink);
}
.rail-meta {
font-family: var(--font-mono); font-size: 12px; color: var(--color-muted);
letter-spacing: 0.02em;
}
.rail-skip {
display: inline-block; margin-top: 16px;
font-family: var(--font-mono); font-size: 12.5px; font-weight: 600;
color: var(--color-pine);
}
.rail-skip:hover { color: var(--color-pine-deep); }
.chapter-outline-mobile {
margin: 0 0 24px;
padding: 10px 16px 12px;
border: 1px solid var(--color-line);
border-radius: 12px;
background: var(--color-card);
}
.chapter-outline-mobile summary {
cursor: pointer; list-style: none;
font-family: var(--font-display); font-weight: 600;
font-size: 1.05rem; letter-spacing: -0.02em;
padding: 6px 0;
}
.chapter-outline-mobile summary::-webkit-details-marker { display: none; }
.chapter-outline-mobile[open] summary { margin-bottom: 10px; }
.chapter-outline-mobile .outline-progress { margin-top: 4px; }
.chapter-kit {
margin: 0 0 28px;
padding: 18px 20px 16px;
background: var(--color-pine-wash);
border: 1px solid var(--color-pine-tint);
border-radius: 16px;
}
.chapter-kit-title {
font-family: var(--font-display); font-weight: 600;
font-size: 1.2rem; letter-spacing: -0.02em;
margin: 0 0 6px;
}
.chapter-kit-lede {
font-size: 14.5px; color: var(--color-muted);
margin: 0 0 16px;
}
.kit-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px 28px;
margin: 0;
}
@media (min-width: 900px) {
.kit-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 720px) {
.kit-grid { grid-template-columns: 1fr; }
}
.kit-grid dt {
font-family: var(--font-mono); font-size: 11px; font-weight: 500;
letter-spacing: 0.12em; text-transform: uppercase;
color: var(--color-pine); margin: 0 0 6px;
}
.kit-grid dd { margin: 0; font-size: 14.5px; line-height: 1.4; color: var(--color-ink); }
.kit-grid ul { list-style: none; margin: 0; padding: 0; }
.kit-grid li { margin: 0 0 4px; }
.kit-grid li:last-child { margin-bottom: 0; }
.workflow-kit {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
gap: 12px 16px;
margin: 4px 0 8px;
padding: 14px 0 16px;
border-bottom: 1px dashed var(--color-line-2);
}
.workflow-kit dt {
font-family: var(--font-mono); font-size: 11px; font-weight: 500;
letter-spacing: 0.1em; text-transform: uppercase;
color: var(--color-pine); margin: 0 0 4px;
}
.workflow-kit dd {
margin: 0; font-size: 14px; line-height: 1.4; color: var(--color-ink);
}
.rail-kit {
font-size: 12.5px; line-height: 1.35; color: var(--color-muted);
}
.rail-out { color: var(--color-ink); }
.chapter-map {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px 40px;
margin: 0 0 8px;
padding: 18px 0 6px;
border-top: 1px solid var(--color-line);
}
.chapter-map-title { grid-column: 1 / -1; }
@media (max-width: 720px) {
.chapter-map { grid-template-columns: 1fr; }
}
.chapter-map-title {
font-family: var(--font-display); font-weight: 600;
font-size: 1.2rem; letter-spacing: -0.02em;
margin: 0 0 16px;
}
.chapter-map-block { margin: 0 0 16px; }
.chapter-map-block .spec { margin: 0 0 8px; }
.chapter-map-block ul { margin: 0; padding: 0 0 0 1.1em; }
.chapter-map-block li { margin: 0 0 6px; }
.chapter-map-block a { color: var(--color-pine); font-weight: 600; }
.chapter-map-block a:hover { color: var(--color-pine-deep); }
.skip-row {
display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
margin: 0 0 48px; padding: 16px 0;
border-top: 1px dashed var(--color-line-2);
border-bottom: 1px dashed var(--color-line-2);
}
.skip-label {
font-family: var(--font-mono); font-size: 11.5px; font-weight: 500;
letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-pine);
margin-right: 4px;
}
.skip-chip {
display: inline-flex; align-items: baseline; gap: 8px;
font-family: var(--font-mono); font-size: 12.5px; color: var(--color-pine);
background: var(--color-pine-wash); border: 1px solid var(--color-pine-tint);
padding: 7px 13px; border-radius: 999px;
transition: background 0.18s, transform 0.14s var(--ease);
}
.skip-chip:hover { background: var(--color-pine-tint); transform: translateY(-1px); }
.skip-chip:focus-visible { outline: 2px solid var(--color-pine); outline-offset: 3px; }
.skip-chip-job {
font-family: var(--font-body); font-size: 13px; font-weight: 600;
color: var(--color-ink); letter-spacing: 0; text-transform: none;
}
.essay,
.recipe-nested,
.end-matter,
.end-sources,
.recipe-nested .row {
scroll-margin-top: 88px;
}
.essay { margin: 0 0 36px; max-width: 46rem; }
.essay h2 {
font-family: var(--font-display); font-weight: 600;
font-size: 1.45rem; letter-spacing: -0.02em; line-height: 1.2;
margin: 0 0 12px;
}
.essay p { color: var(--color-ink); margin: 0 0 14px; }
.essay p:last-child { margin-bottom: 0; }
.workflow-kicker { margin: 8px 0 10px; }
.recipe-nested {
margin: 0 0 52px;
}
.recipe-nested .step-copy { min-width: 0; flex: 1; }
.recipe-nested .step-copy .term { margin-top: 12px; }
.recipe-nested h4.k { margin: 0; font-weight: 500; }
.recipe-nested .term {
overflow-x: auto;
overflow-y: hidden;
}
.recipe-nested .term pre { overflow-x: auto; }
.recipe-jump {
display: flex; flex-wrap: wrap; gap: 8px 16px;
margin: 0 0 8px; padding: 0 0 16px;
border-bottom: 1px dashed var(--color-line-2);
font-size: 13.5px;
}
.recipe-jump a { color: var(--color-pine); font-weight: 600; }
.recipe-jump a:hover { color: var(--color-pine-deep); }
.recipe-jump a:focus-visible { outline: 2px solid var(--color-pine); outline-offset: 2px; }
.plain-list { margin: 0; padding: 0 0 0 1.1em; }
.plain-list li { margin: 0 0 6px; }
.plain-list li:last-child { margin-bottom: 0; }
.evidence-p { margin: 0 0 10px; }
.evidence-p:last-child { margin-bottom: 0; }
.sub-line { color: var(--color-muted); font-size: 14.5px; margin: 8px 0 12px; }
.inline-sample {
font-family: var(--font-mono); font-size: 13px; color: var(--color-pine);
border-bottom: 1px solid var(--color-pine-tint);
}
.inline-sample:hover { color: var(--color-pine-deep); }
.tryit-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.tryit-actions .btn { font-size: 14px; padding: 11px 18px; }
.tryit-done { gap: 8px; }
.recipe-record {
display: flex; flex-direction: column; gap: 6px;
font-size: 14.5px; color: var(--color-muted);
}
.recipe-record a { color: var(--color-pine); font-weight: 600; }
.recipe-record a:hover { color: var(--color-pine-deep); }
.end-matter {
margin-top: 56px; padding-top: 28px;
border-top: 1px solid var(--color-line);
}
.end-sources { margin-top: 32px; }
.end-matter h2,
.end-sources h2 {
font-family: var(--font-display); font-weight: 600;
font-size: 1.35rem; letter-spacing: -0.02em; margin: 0 0 12px;
}
.end-matter h2 + .further-list { margin-bottom: 8px; }
.further-list { margin: 0 0 8px; padding: 0 0 0 1.1em; color: var(--color-ink); }
.further-list li { margin: 0 0 8px; }
.further-list a { color: var(--color-pine); font-weight: 600; }
.chapter-updated {
font-family: var(--font-mono); font-size: 12px; color: var(--color-muted);
letter-spacing: 0.04em; margin-top: 18px;
}
.library-title {
font-family: var(--font-display); font-weight: 900;
font-size: clamp(1.9rem, 4vw, 3rem); letter-spacing: -0.025em;
line-height: 1.15; margin: 14px 0 16px;
white-space: nowrap;
}
.library-lede { color: var(--color-muted); max-width: 38rem; margin-bottom: 34px; }
.toc .toc-soon {
display: grid; grid-template-columns: 52px 1fr auto auto;
align-items: center; gap: 20px; padding: 22px 8px;
border-bottom: 1px solid var(--color-line);
color: var(--color-muted);
}
.toc .soon {
font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.12em;
text-transform: uppercase; color: var(--color-muted);
background: var(--color-card); border: 1px solid var(--color-line);
padding: 4px 10px; border-radius: 999px; white-space: nowrap;
}
.reader-library .toc a {
grid-template-columns: 52px 1fr auto auto auto;
}
.toc-count {
font-family: var(--font-mono); font-size: 11.5px; font-weight: 500;
letter-spacing: 0.08em; text-transform: uppercase;
color: var(--color-pine); white-space: nowrap;
}
@media (max-width: 720px) {
.toc .toc-soon .desc { display: none; }
.toc .toc-soon { grid-template-columns: 40px 1fr auto; }
.reader-library .toc a { grid-template-columns: 40px 1fr auto auto; }
}
.recipe-focus-page { padding: 40px 0 96px; }
.recipe-focus-page .demo-chip { margin-bottom: 12px; }
.recipe-focus-page .recipe { max-width: none; }
.focus-back {
font-family: var(--font-mono); font-size: 13px; color: var(--color-muted);
margin: 0 0 28px;
}
.focus-back a { color: var(--color-pine); font-weight: 600; }
@media (max-width: 959px) {
.chapter-shell { grid-template-columns: 1fr; gap: 0; }
.chapter-outline { display: none; }
.chapter-rail { display: none; }
}
@media (min-width: 960px) {
.chapter-outline-mobile { display: none; }
}
@media print {
.reader-nav, .chapter-outline, .chapter-outline-mobile, .chapter-rail, .reader-footer, .skip-row, .tryit-actions, .demo-chip, .recipe-jump {
display: none !important;
}
.recipe-nested { break-inside: avoid; box-shadow: none; }
}
+224
View File
@@ -0,0 +1,224 @@
---
status: draft-for-review
type: chapter-template
edition: 2026
vertical: # real-estate | financial-advisors | fractional-c-suite | higher-ed | communities | technology
title: ""
authors: []
recipe_ids: [XX-01, XX-02]
last_updated: 2026-08-31
---
# Chapter structure (for review)
This is the blank chapter, matching PRD §5.4 (locked 2026-08-31). Review the shape before anyone writes a real vertical.
**How to use it.** Copy this file to `workflows/[vertical-slug].md` (example: `workflows/real-estate.md`). Replace `XX` with the vertical prefix. Leave this file as the blank.
**What a chapter is.** A substantial essay for one vertical. Full workflows sit inside the argument, at the point they earn, not in an appendix and not as a stack of cards.
**What a workflow is.** The unit of practice. Read it, run the Try-it (~15 min), share the result. That Try-it is what counts as activation.
**Rules that do not move.**
- Skip-to-Try-it at the top. Activation does not wait for the essay.
- Safeguards before Method. Data handling is read before anyone pastes a client list.
- Method shows real tool output, not a description of it.
- Nested recipes stay open. Do not hide Method behind "click to reveal."
- Every recipe has a stable ID and a heading it can link to.
- Every recipe names its kit (who runs it, tools, files, model, time, cost, artifact). The chapter page shows the union at the top and the kit on each nested recipe.
- No chatbot on the page. No crypto or Web3 language.
- PDF must match this stack: essay + in-place recipes.
**Vertical prefixes**
| Vertical | Slug | ID prefix |
|:--|:--|:--|
| Real Estate | `real-estate` | `RE` |
| Financial Advisors | `financial-advisors` | `FA` |
| Fractional C-Suite | `fractional-c-suite` | `CS` |
| Higher Ed | `higher-ed` | `HE` |
| Communities | `communities` | `CM` |
| Technology | `technology` | `TE` |
Fill every `[bracket]`. Two nested recipes are shown so the essay-wraps-workflow pattern is obvious. Add more at the points in the argument they belong. Delete the unused slot rather than leaving a hollow block.
---
# [Chapter title]
[Vertical] · 2026 edition · [Author names]
## What this chapter is for
[One short promise. The job this vertical's reader is trying to get done this week, and what they will be able to run by the end of the chapter.]
**Skip to a Try-it:** [XX-01](#xx-01) · [XX-02](#xx-02)
## What you need
The chapter page shows this before the essay. Union of the nested workflows. Named tools, sample files, Try-it time, sample-run cost. No live client data.
- Tools: [named]
- Files: [named samples we ship]
- Model: [named, or "the model you already use"]
- Time: [each Try-it]
- Cost: [sample-run total, or "unknown"]
## [Essay: the stakes]
[Why this work is hard here. Specific to the vertical, not a generic AI preamble. Name the actual failure: a listing that stalls, a client file that leaks, a committee that will not move.]
## [Essay: constraints]
[What you cannot ignore in this vertical. Tools people already use. Compliance, clients, time. What "done" looks like for them.]
## [Essay: how we work here]
[The practice this chapter argues for. Then the first workflow, at the moment the argument needs proof.]
---
<a id="xx-01"></a>
### [Job title. This is the workflow title, written as the job.]
**XX-01** · v0.1 · ~[N] min · ~$[cost or "free with tools you already have"]
#### Kit
Shown at the top of the nested recipe. Inventory, not instructions. Setup below still explains how to use these.
- You run: [role]
- Tools: [named tools, versions if it matters]
- Files: [sample files we ship]
- Model: [named, or "the model you already use"]
- Time: ~[N] min
- Cost: [amount or "free with tools you already have"]
- You produce: [artifact]
#### Problem
[The real task. One job, not a category.]
#### Actors
- Who runs it: [role]
- Who is affected: [client, student, team]
- What the model is allowed to do: [draft / classify / retrieve]. What a human must still do: [send / approve / file]
#### Setup
- Tools: [named tools, versions if it matters]
- Sample files: [what we ship with this recipe, or "use the anonymized packet below"]
- Cost: [model/tool cost if known. If unknown, say so.]
#### Safeguards
Read this before the steps.
- Data handling: [what never goes into the prompt. What is ok if stripped.]
- Human oversight: [the checkpoint before anything leaves the building]
- Failure modes: [how this goes wrong in this vertical]
- Escalation: [who you call, and when you stop]
#### Method
Ordered steps. Paste the actual tool output inside the step that produced it. A screenshot or a quoted block beats a paraphrase.
1. [Step]
```
[real output]
```
2. [Step]
3. [Step]
#### Evidence
[What "it worked" looks like. An artifact, a number, a before/after. Not "the user felt more confident."]
#### Try it (~15 min)
- Sample data: [what they use so they do not need a live client]
- What to produce: [the artifact]
- Done when: [a check they can mark. Completing this is activation.]
#### Share the result
[What they post: the outcome, not the workflow text. Point at the community template when it exists. Events, not content.]
**Author:** [name] · **Sources:** [links or citations] · **Last updated:** [date]
---
## [Essay continues]
[The argument picks back up. What the first recipe proved, what it did not. Then the next workflow, when the next claim needs proof.]
---
<a id="xx-02"></a>
### [Second job title]
**XX-02** · v0.1 · ~[N] min · ~$[cost]
#### Kit
Shown at the top of the nested recipe. Inventory, not instructions. Setup below still explains how to use these.
- You run: [role]
- Tools: [named tools, versions if it matters]
- Files: [sample files we ship]
- Model: [named, or "the model you already use"]
- Time: ~[N] min
- Cost: [amount or "free with tools you already have"]
- You produce: [artifact]
#### Problem
[ ]
#### Actors
[ ]
#### Setup
[ ]
#### Safeguards
[ ]
#### Method
1. [ ]
2. [ ]
#### Evidence
[ ]
#### Try it (~15 min)
[ ]
#### Share the result
[ ]
**Author:** [name] · **Sources:** [ ] · **Last updated:** [date]
---
## Further workflows in this vertical
- [XX-01](#xx-01) — [job title]
- [XX-02](#xx-02) — [job title]
- [Later IDs, once assigned]
## Sources
[Chapter-level sources. Recipe-level citations stay on the recipe.]
**Last updated:** 2026-08-31