# CLAUDE.md - Tendd

Rules for this repository. This file is loaded in full at the start of every session,
so it holds only what must hold NEXT time. It is not a journal and not a report.

- **Status** (what is done, what is next) lives in the README table and in `done:true`
  in `/_nav.js`. Never here: a third copy only drifts from the other two.
- **Decision records** live in `../Tendd Product/docs/decisions.md`, the live log since 2026-09-02.
  `docs/decisions.md` here is its **mirror**, whole, as of the last sync (header names the commit).
- **Budget: 200 lines.** A new rule enters by replacing or generalizing an existing one,
  not by being added next to it. Over budget means two rules inside already contradict.

## Product

**Tendd** helps people who are not into finance see and control their recurring payments
and subscriptions, turning money anxiety into calm, everyday clarity. Mobile-first
responsive web app, desktop in scope (a native app is not). Not a budgeting app: a calm,
low-friction visibility and control layer for recurring spend, for people who feel anxious
about money and avoid finance apps.

**Audience:** 22 to 42 (core 26 to 36), low to medium financial confidence, skeptical about
connecting a bank. Primary driver: reducing anxiety ("I know what is going out, and I am
okay"). Secondary: pride in a small win. They are not budget optimizers or spreadsheet fans.

**Primary job (J-MAIN):** "When I suspect I am paying for things I forgot about, I want to
see all my recurring charges in one calm view, so that I feel in control without becoming a
finance person." Secondary: cancel what I do not use (J3), never be surprised by a price
change or failed payment (J4).

**Free and Pro** are split by **D-Gate** below and the split is not written twice; a plain export of
your own data is free either way (D-Export). Out of MVP: full budgeting, investments, native app,
bill negotiation, bill pay, household view, priority support.

**Markets:** US and EU (US and Plaid first, per D5). **Live at tendd.co** since September 2026:
Next.js on Vercel, Supabase, Plaid, Stripe, Resend, and no analytics by decision (P12).

**Riskiest assumption (H0):** that an avoider looks and feels calmer; built to test, not assume. `[?]`

## Locked decisions

Founder, June 14 2026 (ground in `research/docs/strategy.md` section 6):

- **D1** Gradual reveal with a paired action (count, then categories, then the total).
- **D2** Manual entry plus presets at launch, as a second path. Equal weight ended with D-Gate:
  manual is the free door and the bank the paid one, and node 1.2 says which is which.
- **D3** **D-Gate moved the paywall ON to basic visibility.** Still true: never at the cancel
  moment, guides free; Free keeps **one comparison on Trends**; **one product, not two**: a plan
  is a STATE of a screen, never a twin. Where plan and source change a screen, it has **three
  states**, Free by hand, Free after the scan, Pro (founder, 2026-09-30). `plans13.cjs` counts it.
- **D4** Pro is 9.99 a month or 69 a year (amended 2026-09-30; it was 7.99).
- **D5** US and Plaid first, EU deferred.
- **D-Gate** (founder 2026-08-29, shaped 2026-09-02, **replaces D-Free**) The bank is Pro. One free
  scan returns the count, the total and three names across three categories; the rest are withheld,
  and the connection is held seven days unread so paying names them (P32), then removed. Typing
  stays uncapped, which is all that survives of D-Free. Ground: `docs/decisions.md`, 2026-09-02.
- **D-Concept** (July 2026, amended by D-Brand and by D-Plot 2026-08-18) Petrol and Paper: off-white
  canvas, white cards, Inter, petrol #1c6a76 inside a screen's content only on the primary action,
  the current selection, the trust line and **the plotted line** (one element, one screen, never
  beside a filled action); status a quiet gray badge, never red; price change amber, error clay.
- **D-Brand** (2026-08-12) The identity is Crop: one letterform larger than any frame, a window cut
  out of it. Crop A is the mark (22px in the app bar, favicon, touch icon); the wordmark is Inter
  800 at -0.02em, the `dd` pair at -0.09em, the **last letter petrol everywhere**. The brand is
  petrol's **fourth place, not a fourth job**: chrome only, never in a screen's content. Ground:
  `DESIGN.md`, The brand.

## Design principles

1. **Calm over clever.** Every screen lowers anxiety. No red, no urgency, no clutter.
2. **One thing at a time.** Progressive disclosure by default; the most important number is
   the biggest thing on screen.
3. **Plain money language.** "You are paying for 14 subscriptions", never "monthly recurring
   expenditure". Numbers always in context.
4. **Trust through transparency.** Say what happens with the data, every time. Show the
   source of every figure.
5. **Small wins feel good.** The moment a forgotten subscription is found is the product's
   most important emotional beat.

## Rules that bind every stage

**Language.** Internal md (this file, README, every `*/docs/*.md`) in English. Chat in
Ukrainian. Product copy and html pages in English. Registry labels in `/_nav.js` are
interface text: one language across the sidebar, mixed language counts as a defect.

**No em dash** in any output file of the project. Use a hyphen or rewrite the sentence.

**Navigation has two levels, and the roadmap has one owner.** `/_nav.js` is the single registry of
the project and it lists STAGES: it renders the sidebar everywhere, and active / Next / SOON are
computed at render. A new page means one row plus `done:true`, never a hand written roadmap;
`/_nav.css` owns the look and a page never describes `nav-*` itself. The MATERIAL of a stage lists
itself in the stage's own registry (`wireframes/_nav.js`, `design/_nav.js`, `design/kit/_nav.js`),
each keeping its data and classes in its own namespace because hub pages load both; a roadmap that
listed every component would stop being a roadmap. A stage page carries the roadmap, **and where a
stage HAS its own registry its pages carry that panel instead**, with the roadmap at its foot.
`ready:true, done:false` is the one honest way to say "the page exists and the stage is running".

**`index.html` is the product home page, `overview.html` is the hub.** In any stage folder:
opening the folder lands on the product screen (node 1.1, Welcome); the list of all
pages of that stage is `overview.html`. This holds for `wireframes/` and `design/` alike.

**The wireframes are grey and frozen at v1.0.** `wireframes/` is the structure contract as handed
over: greyscale, semantic, real copy, one page per state, a state page named after its state, and
it never loads a file from `design/`. Colour went onto COPIES in `design/`, differing from the grey
by styling only until the mirror: a page the product changed or added since has no grey twin and
says so in its top comment. The etalon and the first flow: line 1 of `wireframes/docs/screens.md`.

**Every artifact has a visible place.** A md that no page shows does not exist for the person
who decides. Three legal forms: its own page in the registry, a named section on the stage
page (with a `NAV_SECTIONS` entry), or a satellite page that declares `NAV_ACTIVE` and the
label of its nearest registry page. Critique and audit logs appear as a "was to became"
summary in a closed state, not as a list of open defects.

**Live md, unfrozen html, and a COUNT is live in the same way.** Changing a md that already has a
published page means rebuilding the affected section of that page in the same step; if the page
cannot hold the new material, put a visible "updated after publishing" note on it and say so out
loud. A number counted off a corpus obeys the same rule: growing the corpus means recounting every
claim about it in the same step, by script and never from memory, and a claim left un-recounted
keeps the corpus it was measured on, NAMED ("on the 28 pages of stage 08").

**One owner per string.** SEO copy belongs to the IA node in `ia/docs/pages/`; interface strings to
`microcopy.md`, written in the product (`docs/spec/`) and mirrored at `voice/docs/`. The IA node says
what a place needs, not the wording; no line has two editions; all copy obeys `voice/docs/voice.md`.

**Nothing new reaches a screen first, and "we keep it" is an address rather than a verdict.**
A VALUE goes to `design/system/tokens.css` at its level, **two levels and not three**: primitive
(raw values) and semantic (roles through `var()`); a role or a state token lives in both themes
or it does not exist. A COMPONENT is five things: css in `design/system/components/`, a page in
`design/kit/`, a row in `design/kit/_nav.js` in its own LEVEL group, a line in `docs/inventory.md`
with its level, and an `@import` in `index.css` in its own level group, never at the end. A
COMPOSITION on three screens is a pattern: a file in `design/system/patterns/` plus its page,
assembled from components and declaring no style of its own; on two it stays markup. A USAGE RULE
is a row in `design/kit/docs/architecture.md` with its "where it comes from" filled, plus a Limits
item on every component page it names. **A value is never re-derived:** it changes by a named
decision ("variable, value, why") and carries its origin in a comment beside it or in the block
comment governing its run. A **screen carries no style of its own**: no style block, no style
attribute, no `@media`, no class the system does not define. **`rem` is what a reader resizes and
`px` is geometry:** the type scale and the two width points, not spacing; a query cannot read a
`var()`, so a point is a register and its literal the application, and a third is the founder's.
So a correction goes to the token of its level, which reaches every screen by itself, or to the
component css plus its page plus every screen holding it. A fix applied on one screen is a desync,
and a contextual override (`.host .btn{font-size:15px}`) is an undeclared variant: declare the
modifier and put the class in the markup.

**Critique runs on two instruments.** Claude and Codex (read-only, see `AGENTS.md`), taken
independently and merged afterwards, with a "who found it" column. Codex owns what is falsifiable
in the source (contradiction between files, an orphan, unbalanced markup, a state that is not in
the code, a broken link, a rule violation); "breaks at 360px" and pixel checks stay with Claude in
a browser. Verify a finding by re-reading the place before fixing it; a finding that does not hold
stays in the log marked "dropped at verification", with the reason.

**Acceptance happens at both ends of the range AND BETWEEN THEM.** Where a stage produces a screen
or text on it, open it in a browser, walk every state, and sweep the width instead of sampling two
ends: the worst width is where the content has stopped fitting and the query has not fired, and a
28px band with a pane scrolling sideways survived two accepted stages that way. A document that
does not scroll is not proof, a pane with its own `overflow` scrolls inside a clean page. Ask for
the fix with a prompt: a hand edit does not survive the next clone.

**Zone rules live in their own docs, this file only points at them:**
`wireframes/docs/conventions.md` (the grey contract), `voice/docs/voice.md` (the voice) and
`voice/docs/microcopy.md` (the line inventory), `ia/docs/sitemap.md` and `ia/docs/flows.md`
(structure), `design/concept/docs/concept.md` (taste and attributes; a visual decision with
no attribute is an invention).

## Where things live, and where the whole route is written

```
/_nav.js /_nav.css /index.html   the project registry, its look, the entry page
CLAUDE.md  README.md  AGENTS.md  rules, the status index, the entry for the Codex critic
docs/                            decisions.md (mirror), gaps.md, bank-connection.md, never loaded
DESIGN-artifacts.md DESIGN.md    the draft from Concept, and the doc built from the code
research/  ia/  voice/           who it is for, the structure, every product string
wireframes/  design/             the grey prototype and the product in colour, each with
                                 index.html as the home and overview.html as the hub
design/system/  design/kit/      the CODE, liftable whole and carrying its own CLAUDE.md,
                                 and the STAND that shows it, + docs/ and screens/
handoff/                         the package handed over: handoff.html + docs/
```

The full route, folder by folder with what to open first in each, is the table on
`handoff/handoff.html`, and it is not copied here.

## Handoff

**The package is `handoff/`**: the page plus `docs/` (behaviour, map, a11y, onboarding-gaps,
one-shot). **The documents reference the code and never copy it**: a hex, a spacing value, a ready
product string or a css fragment inside `handoff/docs/` is a defect. Two things are not copies and
are allowed - a MEASURED figure, which names what measured it and when, and the stage page's own
look, which is chrome like every other stage account's and reaches no screen. Every behaviour row
names its source, and a row with no source goes to a NOT DECIDED list addressed to the founder
rather than into the spec. The map is read off the code and its reverse list is produced by
INVERSION of the same data, never by a second pass. **"Confirmed" means a run NOW with a named
instrument**, never a memory of an earlier stage doing it. The route is checked by
`design/kit/screens/route13.cjs`, never by eye.

## The mirror

**The product lives in `../Tendd Product`; this repository is its mirror**, synced on the founder's
request (first 2026-09-30, account in `handoff/live.html`). A change is made THERE and arrives by a
sync: `system/`, the spec, decisions and gaps copied whole, each page brought level with the running
product. What the product DECIDED changes a page; what it has not built yet stays as design intent.
A hole found here is a row in `design/kit/docs/backlog.md` or the product's `docs/gaps.md`, not a fix.
