# Frenday design system, 2026-09-24 (D1, wave 1)

Direction: refine inside the current identity. The wordmark, the logomark, the paper / ink / sun palette and the two faces stay. What this document adds is a named scale, one vocabulary for space, radius and motion, states on every tappable thing, a photo doctrine, one signature element, and a page-by-page fix list. Audit shots: 27 pages at 390 / 820 / 1440 (scratchpad `wave1/d1/`). Everything in section 2 to 8 is implemented in `app/globals.css` (block "THE FOUNDATION", last in the file); sections 9 and 10 are for the owners of the other files.

## 1. The identity in one paragraph

Warm paper (`#f5efe4`), pine-black ink (`#17140f`), one sun (`#e39a2a`) that is never text, and a person's own OKLCH palette derived from their hue (`.v-person`, `--h`). Two faces: Pridi (the voice: names, headlines, the person's own words, prices) and Anuphan (the system: everything read at length or tapped). The page is the person: their name enormous in Pridi, their photo dissolving into their paper, one ink button. The product's one idea is TIME: hours 06:00 to 24:00, "when are you free", the hour as a typographic object. That is why the signature is the sun that marks now.

What must not creep in: cards with grey shadows, icon rows, numbered eyebrows where nothing is a sequence, ALL-CAPS labels (Thai has no caps; the English label rule in `.v-label` is the one exception), gradients as decoration, more than one filled button per view, jade as a brand colour (jade is the base layer's action colour for internal surfaces; public pages use the ink of the surface).

## 2. Type scale (`--t-*`, set on `html[data-v3]`)

| Token | Value | Face | Used for |
|---|---|---|---|
| `--t-fine` | 12px | Anuphan | provenance captions on photos, axis labels, the tile's demo line |
| `--t-small` | 13px | Anuphan | row labels (`.v-cards-lbl`, `.v-listhead`), legal, `.v-foot`, `.v-small`, `.v-label`, `.v-hint` |
| `--t-meta` | 14px | Anuphan | hints, secondary facts, week rows, `.v-err`, `.v-mute` |
| `--t-ui` | 15px | Anuphan | chip text on cards, `.v-truth`, a price's unit, "free" labels |
| `--t-body` | 16px | Anuphan | running UI text (`html[data-v3] body`), `.v-chip` |
| `--t-read` | 17px | both | `.v-cta`, `.v-in`, running Pridi in the chat bubbles, `.v-verbs` |
| `--t-lead` | 19px | Pridi | ledes (`.pq-lede`, `.vj-truth`), `.v-door`, `.v-wordmark`, `.v-paused` |
| `--t-voice` | 21px | Pridi | the person's own line under the name (`.v-voice`) |
| `--t-h3` | 24px | Pridi | section words (`.v-cards-h`, `.v-row .nm`, `.v-confirm .sent`) |
| `--t-fact` | 27px | Anuphan 600 | a time or a price on a phone (`.v-free .time`, `.v-price b`, `.c-price`) |
| `--t-h2` | clamp(22px, 6vw, 28px) | Pridi 600 | a page's section heading (`.pq-h2`, sell sections) |
| `--t-h1` | clamp(32px, 9vw, 44px) | Pridi 600 | a page's one title (`.pq-title`, `.vj-title`, `.v-sheet h1`) |
| `--t-big` | clamp(56px, 18vw, 84px) | Pridi 600 | the one price (`.pq-price .amount`) |
| `--t-hour` | clamp(64px, 26vw, 104px) | Pridi 400 | the hour on the front door (`.v-hour .big`) |
| (fluid) | `.v-nick`, `.c-tile-nick` | Pridi 600 | the person's name, sized from glyph count; not a step |

Line heights stay per script (`--lh-body` 1.7 Thai / 1.55 English, `--lh-head` 1.2 / 1.12). Thai display type keeps `padding-top: 0.04em to 0.08em` for tone marks. Measure: 34em Thai running text, 27 to 30em Pridi ledes, 22em for a voice line. Rule: from now on page CSS names the step, never the px. The v3 rules above the foundation still spell px; migrate a rule when you touch it (identical values, zero visual change).

Wide screens: `--max` is 640 to 719px, 680 from 720px, and now 720 from 1100px. A reading page at 1440 was a 320 to 440px column of 17px text; the wider column is the fix, not larger type.

## 3. Spacing scale

`--sp-1..8` = 4, 8, 12, 16, 24, 32, 48, 64px. `--sp-section` = clamp(40px, 8vw, 64px) between two ideas on a reading page. The v3 rhythm tokens `--s-3/4/5` (24/32/48) stay as the section rhythm (`.v-read section`, `.v-form fieldset`). Inside a component: 4/8/12/16. Between components: 24/32. Between sections: 48/64 or `--sp-section`. Gutter: `--pad` (22px phone, 7vw from 720). Never a margin that is not one of these.

## 4. Radius scale (`--rad-*`)

| Token | Value | Things |
|---|---|---|
| `--rad-tile` | 6px | browse tiles (`.c-tile`), a row's monogram (`.v-rowmono` 8 to 6 when touched), thumbnails, the kit thumbnails |
| `--rad-field` | 12px | `.v-in`, `.v-day`, the sell specimen plate on a phone |
| `--rad-card` | 18px | a panel on the paper: `.v-est`, `.v-confirm` (22 to 18 when touched), the pro kit panel, the "card free forever" panel |
| `--rad-sheet` | 24px | the sheet on a wide screen (`.v-sheet-panel`) |
| `--rad-pill` | 999px | `.v-cta`, `.v-chip`, `.v-langs`, `.v-wordmark`, `.v-stepper` |

The base layer's `--r-field/card/pill` (6/16/999) are unchanged; internal surfaces read them.

## 5. Elevation

Nothing on the paper casts a shadow. The only floating thing is the sheet over a page: `--shadow-sheet`. The sign-in card (Clerk) currently has a border plus a shadow; it is the one off-system elevation on the site (fix list).

## 6. Colour roles and allowed uses

| Role | Token | Allowed | Never |
|---|---|---|---|
| paper | `--paper`, `--paper-2`, `--paper-3` | ground, the second tone for a panel, pressed fill | as text |
| ink | `--ink`, `--ink-2`, `--ink-3` | text, the one filled button, chosen states (day, chip, language) | as a decorative block wider than a button (the story's ink "Kathy cannot" block is the exception, once) |
| sun | `--sun` | the logomark disc, the scrubber thumb, the sun mark (section 8), selection highlight | text (`--sun-ink` exists for the two "now" labels only), fills, borders |
| line | `--line`, `--line-2` | hairlines that separate list rows, the foot | boxes around content |
| person | `--p-*` from `--h` | everything on their page, the specimen, their tile | on the neutral chrome |
| jade / rose | base layer | internal surfaces (portal, admin), LINE's own green on the LINE hint only | public v3 pages |
| focus | `--ring` (the surface's ink) | every focus-visible ring | the old blue `--focus` on v3 surfaces |

Night (`html[data-night]`) inverts every token in one place; nothing else may declare a colour.

## 7. Component inventory (canonical spec)

- Button `.v-cta`: 54px min (58 for a page's one hero action, 48 in the bar), pill, `600 var(--t-read)/1.3 var(--ui)`, padding 0 24px, fill = the surface's ink (`--p-btn`), text = the surface's paper. `.ghost`: transparent, 1.5px inset rule, weight 500. One filled button per view. States: hover fill 86% ink + 14% paper; ghost hover `--wash-hover`; active translateY(1px); disabled 0.55; `aria-busy` cursor progress; focus ring 2px ink offset 3. Label says what happens; no arrow glued on by CSS (the two pro buttons carry arrows in their copy; fix list).
- Chip `.v-chip`: 44px, pill, 1.5px rule, `500 var(--t-body)`; `aria-pressed`/`aria-current` = ink fill. Hover wash + darker rule; active `--wash-press`. A chip is a word (an activity, an area, a channel), never an icon. `components/chip.tsx` (internal): 32px look, 44px hit halo.
- State pill `.iv-pill` (internal only): outlined, one tone dot; semantic tones wait/good/go/stop/quiet. Unchanged.
- Field `.v-in`: 50px, `--rad-field`, 1.5px `--line-2`, `--t-read`; hover rule to `--ink-3`; focus 2px ink; invalid rule = ink; error `.v-err` `--t-meta` in ink (never rose on v3). Label `.v-label` `--t-small` 600 (English uppercase 12px tracked: the one caps label).
- Day cell `.v-day`: 62px, `--rad-field`; pressed ink; hover wash.
- Day strip `.v-dayb`: 72x64 tab, 2px ink underline = chosen; today carries the sun mark.
- Language pill `.v-langs`: glass pill, pressed = ink disc; hover wash on the other. (Consider: the ink disc is the strongest mark on every page's corner and competes with the one button; a paper-3 fill for the current language is the quieter option. James's call.)
- Chrome `.v-chrome`: absolute, wordmark (Pridi `--t-lead`) + sun mark + Bangkok weekday and time, language pill. Scrolls away; the bar owns the action at depth.
- Row `.v-row` (front door list): 60px monogram, name `--t-h3` Pridi, price right; hover underlines the name.
- Tile `.c-tile` (browse cards): `--rad-tile`, 4:5 photo dissolving into the person's paper, name sized to the column, hint line; hover underlines the hint; focus ring inset 3px.
- Poster `.v-poster` (browse deck, /p head): the person's paper, name, voice line, verbs, plate (photo or monogram), the facts, the two actions.
- Sheet `.v-sheet-panel`: full height on a phone; 720px column, `--rad-sheet`, `--shadow-sheet` from 720.
- Bar `.v-bar`: fixed bottom, fact + one button, glass.
- Door `.v-door` / `.v-go`: Pridi `--t-lead` with a trailing arrow; hover underlines and moves the arrow 4px (reduced motion: no move).
- Foot `.v-foot` / `.v-homefoot`: `--t-small`, one hairline above, the demo disclaimer.
- Monogram `.avatar` / `.v-rowmono` / `.c-mono`: Pridi 600 initial on the person's plate tone.

## 8. Photo treatment, motion, the signature

Photo: 4:5 portrait, `object-fit: cover` at the person's focal point, full-bleed on a phone. The picture dissolves into the person's paper over its last 34 to 38% so the name sits on ONE tone and never on the picture. On the deck poster the photo sits in the plate with the provenance caption on a 66% black scrim band (AA). Small photos (sell specimen, 96/160px) are `--rad-field`/16px with no dissolve. No filters, no duotone, no rounded corners on a full-bleed photo. Only the eight fictional companions have generated pictures and every one carries its caption. A real Companion's photo is theirs, uploaded by them, unedited by us.

Motion: `--dur-fast` 140ms answers a tap (colour, transform), `--dur-base` 220ms moves a thing (the arrow, a chip), `--dur-open` 320ms reveals a surface (the sheet, the bar). Easing `--ease-out` for all three; `--ease-settle` only for the sun landing. One orchestrated moment per page at most (the band filling on the base layer; none on v3 pages). No entrance fades on sections, no hover zoom on photos. `prefers-reduced-motion` zeroes every duration.

The signature: THE SUN MARKS NOW. The logomark's disc, small, wherever the page says "at this moment": beside the Bangkok time in the chrome on every page (done, CSS), on today in the day strip (CSS hook `.v-dayb[data-today]`, needs the attribute), the scrubber's thumb (already the sun), and later: the "free now" fact in the bar, the "ว่างวันนี้" label on a poster, "Live in Bangkok since September 2026" on the story. One sun per view. Never a bullet, never decoration, never on a date that is not today. Inline element: `<i class="v-sun" aria-hidden />`.

## 9. Fix list, page by page (for the owners; file + what)

Front door `/` (`components/v3/home-picker.tsx`, `app/page.tsx`):
- Add `data-today` to the today button in the day strip so the sun mark lights (CSS hook exists).
- `.v-hour .big` to `var(--t-hour)`, `.v-row .nm` to `--t-h3`, `.v-listhead` to `--t-small` (values identical).
- At 1440 the picker column and the list are far apart (72px gap, 5fr/6fr); tighten the grid to `minmax(0, 520px) minmax(0, 560px)` with `justify-content: start` so the two halves read as one composition.

Browse `/browse` (`app/browse/page.tsx`, `components/card/card-tile.css`):
- `.c-tile` radius to `var(--rad-tile)`; `.v-cards-lbl` to `--t-small`; `.v-cards-h` to `--t-h3`.
- At 1440 a deck poster is 640px tall with the name at 220px and the photo 44% wide; the eight posters make an 8,700px page. Consider a 2-up deck from 1100px (`grid-template-columns: 1fr 1fr` on `.v-deck`), each poster then the phone composition. Not urgent.
- The filter chip row: keep; the pressed chip is the one ink fill on that screen.

Companion page `/p/[slug]` (`app/p/[slug]/page.tsx`, `components/v3/*`):
- The two actions (filled + ghost) match the system; keep. `.v-tlist` numerals: keep (a real list).
- `.v-week .d`/`.t` to `--t-meta`; `.v-rate .big` 44px to `--t-h1`; `.v-mute` to `--t-meta`.
- `not-found.tsx` still mounts the v1 `Shell` (header with the lockup and a jade "Sign in" button) inside a v3 page; move it to `Chrome` + `.v-wrap` so the 404 is not the one page with a different header.

Card `/c/[handle]` (`components/card/card.css`, `card-page.tsx`):
- `.c-price` to `--t-fact`; `.c-areas` to `--t-ui`; `.c-trust`, `.c-id` to `--t-meta`; `.c-edits` to `--t-fine`.
- The channel chips (Instagram, TikTok) are `.v-chip` lite; keep.
- Wide (1440): the head column starts at 682px next to a 634px photo; the name's 240px is right. Fine.

Sell `/sell` (`app/sell/sell.css`, `app/sell/page.tsx`):
- `.vs-pair li` 19px to `--t-lead`; `.vs-pair b` to `--t-small`; `.vs-promise` to `--t-body`; `.vs-safety p` to `--t-lead`; `.vs-spec .plate` radius to `--rad-field` / 16px.
- The specimen panel is the one place the person's paper is a rounded panel (24px); use `--rad-card` (18) to match `.v-est`.
- Second door "ขอดูสักใบก่อน" is an underlined line while `/pro` uses a ghost pill for the same rank of action. Pick one: the underlined door is the v3 idiom for "go read"; the ghost pill is for "do the other thing". Here it is "go read", so keep the door, and change the pro page's second button (below) rather than this.

Pro `/pro` (`app/pro/pro-public.css`, `app/pro/page.tsx`):
- `.pq-title` to `--t-h1`, `.pq-price .amount` to `--t-big`, `.pq-h2` to `--t-h2`, `.pq-lede` to `--t-lead`, `.pq-nosub` to `--t-meta`.
- The two buttons carry arrows inside their copy ("มีการ์ดแล้ว → เปิดโปร"); a button names what happens: "เปิดโปร" / "ทำการ์ดก่อน", with the condition as a `--t-small` line under the pair. Copy owner's call (content is James's; propose, do not edit).
- The "how to pay" list uses Arabic 1 to 4 while /sell uses Thai numerals; use `counter(n, thai)` on the Thai page like `.v-tlist`.
- The kit panel (`--paper-2`, 24px radius) to `--rad-card`.

Story `/story` (`components/story/story.css`):
- At 1440 the 480px column leaves 70% paper; allow `max-width: 560px` from 1100 and `--sp-section` between screens.
- "Kathy" italic in headlines is the one inline mark; keep. The icon row on "Your profile card / Your request" (three line icons) is the one icon row on the site; it is illustrative, not a feature list, so keep, but draw the three from the system (the card = a tile, the request = a `.v-chip`, the meetup = the sun mark) instead of generic outline icons.
- The "Live in Bangkok since September 2026" line: candidate for the sun mark.

Sign in / sign up (`components/owner/clerk-shell.tsx`):
- Clerk's card has a border, a shadow, a dotted footer, a "Development mode" banner and its own button (arrow icon, 48px). Set `appearance.variables`: `borderRadius` 12px, `colorPrimary` ink, `colorBackground` paper-2, `fontFamily` `var(--ui)`, and `elements.card { boxShadow: none; border: 0 }`, `elements.formButtonPrimary { minHeight: 54px }`; hide the footer branding where the plan allows.
- The page's own copy ("การ์ดของฉัน", the two lines) is right; keep.

Chrome / all pages (`components/v3/chrome.tsx`):
- Optional: render `<i class="v-sun" aria-hidden />` explicitly instead of the CSS pseudo, once the attribute pattern is agreed.
- Consider the quieter current-language state (section 7).

Base layer (`components/button.tsx` etc., internal surfaces): no change needed; `chip.tsx` gained the 44px hit halo.

## 10. Open questions for James

1. The language pill: ink disc (current) or quiet paper-3 fill for the current language?
2. Arrows: keep the trailing arrow on doors (the v3 idiom) or drop them site-wide? The CSS keeps them; the pro buttons' copy arrows are the only ones inside a button.
3. Bolder directions: `docs/design/mockups/direction-a.html` (the hour as the whole identity) and `direction-b.html` (the poster wall) are for a glance, not for tonight.
