# Build a therapy practice home page with the Solace theme

You are building one page in an existing project: the home page of a private therapy practice of one therapist, with a quiet opening, what people bring as a short list, how it starts in three steps, the therapist in brief with a round portrait, the fee in one line and one action. Follow this brief closely. `DESIGN.md` decides the tokens: colors, type, spacing steps and corners. This brief decides the layout, its widths included, and which token goes where. If this brief names a color, a type size, a spacing step or a corner that `DESIGN.md` does not have, `DESIGN.md` wins.

## Before you start

1. **Check the theme is installed.** The project should have `DESIGN.md` in its root and the Solace variables in its global CSS. If not, run `npx shadcn@latest add https://strictthemes.com/r/solace.json` and save https://strictthemes.com/themes/solace/DESIGN.md as `DESIGN.md` in the project root. In a project without shadcn, use https://strictthemes.com/themes/solace/tailwind.css instead. If the global CSS still imports a font the theme does not use (a new shadcn project imports Geist), remove that import, and uninstall its package if nothing else uses it. Keep every font import the theme's install added, also one for a style no page uses yet.
2. **Read `DESIGN.md` from start to finish.** It is the source of truth for every visual decision.
3. **Look at the project before writing code.** Use its framework, router and file layout. For controls (buttons, inputs, tabs, menus, dialogs, accordions, data tables) use the components in `components/ui`, and add a missing one with `npx shadcn@latest add <name>` rather than writing your own, unless this brief names a plain element. Layout is plain elements with theme classes: sections, panels, figures and lists need no component.
4. **Teach `cn()` the theme's type classes.** shadcn's class merger does not know `text-display` or `text-h1`. It takes them for colors, so `cn("text-label text-muted-foreground")` silently drops one of the two, and a type class passed to a stock component does not replace its size. In a new shadcn project, `lib/utils.ts` only passes `cn` on from the `cn` package. Unless the file already holds the lines below, change it to:

   ```ts
   import { createCn } from "cn/config"

   // The theme's type classes are font sizes, not colors.
   export const cn = createCn({
     extend: {
       classGroups: {
         "font-size": [{ text: ["display", "h1", "h2", "body", "label"] }],
       },
     },
   })
   ```

   Then make every file in `components/ui` use it: where one has `import { cn } from "cn"`, change that line to import `cn` from the project's utils file (`@/lib/utils` in a default setup). Do the same for each component you add later. If the project uses `tailwind-merge` instead of the `cn` package, its components already import from the utils file: pass the same object to `extendTailwindMerge` and keep `clsx`.
5. **Write real content.** If the user gave you details about their product, use them. Otherwise use the sample product in this brief and write the rest of the copy yourself: specific and plain, the way the samples read. Invent the company and people names; do not use real ones. Never use lorem ipsum. A link with no destination yet points to `#`. Set the page's `<title>` and meta description.

## The theme in short

- **Fonts:** Literata for headings, Mulish for body text, Xanh Mono for labels and code.
- **Type scale** (size/weight; each class also sets line height and tracking): `text-display` 56px/400, `text-h1` 38px/400, `text-h2` 24px/500, `text-body` 17px/400, `text-label` 14px/400.
- **Font per size:** a type class does not set the font family. Headings (`<h1>` to `<h6>`) get the heading font on their own; on any other element add the font class: `font-heading` with `text-display`, `text-h1`, `text-h2`; `font-mono` with `text-label`.
- **Spacing scale**, with the Tailwind step in brackets: 4px (`1`), 8px (`2`), 16px (`4`), 28px (`7`), 48px (`12`), 112px (`28`).
- **Corners:** `rounded-sm` 6px, `rounded-md` 12px, `rounded-xl` 20px (the `lg` corner in `DESIGN.md`). `rounded-lg` is 12px as well: it is shadcn's name for the base corner, the one its stock buttons and inputs use.
- **Colors (light mode):** primary #22223B, on-primary #F2E9E4, secondary #C9ADA7, on-secondary #22223B, tertiary #9A8C98, on-tertiary #22223B, neutral #4A4E69, background #F2E9E4, surface #FAF5F2, foreground #22223B, border #DFD0C9, error #9B2C2C.

### How Solace pages are laid out

- **Container:** one `<div>` wraps the whole page and is the container: `mx-auto max-w-[1136px] px-4 md:px-7`, plus `text-body text-foreground`. From `md` up the content is at most 1080px wide, the grid in `DESIGN.md`, with 28px at each side. This width is the one arbitrary value allowed. Inside it sit the `<header>`, a `<main>` holding everything between the two, and the `<footer>`.
- **Grid:** a section's content sits in a grid: `grid grid-cols-1 md:grid-cols-12 md:gap-x-7`. The grid sets columns only, never `gap-y-*`: every vertical distance comes from the rhythm rule. Below `md` every grid item takes the full width. From `md` up every grid item gets `md:col-span-*`, because an item without one takes a single column: "columns 1–12" is `md:col-span-12`. It also gets `md:col-start-*` when it starts after column 1, and `md:col-start-1` when it must start a new row under an item that leaves room beside it; or the grid puts it in that room. Where a brief puts "a section's content in columns 1–7", the `<section>` is the grid and the content is one `<div>` with those column classes. Where a list "is itself the grid", the `<section>` is not a grid: the heading above the list sits directly in it.
- **Reading column and note:** running text, lists and forms sit in seven of the twelve columns (`md:col-span-7`), or the columns this brief gives. Beside them, where this brief asks for one, sits one short note in columns 9–12 (`md:col-start-9 md:col-span-4`), with `mt-12 md:mt-0`, so on a phone it comes after the text. Leave the columns this brief does not fill empty, and center nothing to fill them: the empty columns are the calm of the page.
- **Vertical rhythm:** written as a top margin on the lower element (`mt-*`), never as `space-y-*`, `gap-y-*`, a bottom margin or padding, except the padding of a card, a list entry or a table cell, which these notes give. Between a label and the one thing it names (a field's label and its field, a name and the line under it, a term and its value when they stack): `mt-2`. Between a heading and the text under it, between paragraphs, and from a label line to the heading under it: `mt-4`. Between blocks inside a section (a paragraph and a button, a heading and a list or a table, a card and the text after it, two fields of a form): `mt-7`. Between two parts of a section that each have their own heading: `mt-12`. Where two grid items share a row from `md` up, the second has its phone margin and `md:mt-0`. Inside a flex row (the header, a portrait and a name, a row of badges) the space is the row's `gap-*` as this brief states it. Where this brief gives an element its own margin, it replaces the one from this list.
- **Sections:** `<main>` holds a stack of `<section>` elements, each with `aria-labelledby` naming its heading. The first has `mt-12 md:mt-28`. Every other section has `mt-28`: 112px, at every width. Sections are divided by that space alone: no hairline, no fill and no border between them. A section's first element takes no top margin of its own.
- **Header:** the `<header>` is itself the row, with no border: `flex flex-wrap items-center gap-x-7 py-4`. On the left the therapist's name, `Nora Ellingham`, a link to `/` with `inline-flex h-11 items-center font-heading text-h2 text-foreground`, never marked as the current page. It is not a heading element. Then a `<nav>` with the `aria-label` "Main" and `w-full md:ml-auto md:w-auto`, so from `md` up it sits at the row's right end and below `md` it takes a line of its own under the name. It holds a `<ul>` with `flex flex-wrap gap-x-4 md:gap-x-7` and four links, in this order: "Approach" (`/approach`), "About" (`/about`), "Fees" (`/fees`) and "First session" (`/first-session`). Each is `inline-flex h-11 items-center text-body text-foreground decoration-1 underline-offset-4 hover:underline`. Each brief names the current one, if any: it keeps its address and adds `aria-current="page"` and `underline`. The four fit on one line at 375px, so there is no "Menu" button. The header holds no button: the page's one action is in the page.
- **Footer:** a `<footer>` with `mt-28 border-t border-border py-12`, the one hairline that runs the width of a page. Inside it a grid like the one above, with two parts:
  - Columns 1–7 (`md:col-span-7`): the name as a `<p>` in `font-heading text-h2`, `Nora Ellingham` (not a heading and not a link); with `mt-2` a label line in `text-muted-foreground`, `Registered psychotherapist · Larkmere and online`; with `mt-7` an `<address>` with `not-italic` in `text-body`, one line, `The Garden Room, 14 Orchard Row, Larkmere LK2 4DN`; with `mt-2` a `<p>` holding the email address `hello@ellingham.example` as a `mailto:` link, styled as a link in running text.
  - Columns 9–12 (`md:col-start-9 md:col-span-4`, with `mt-7 md:mt-0`): a `<nav>` with the `aria-label` "Footer" holding a `<ul>` of the header's four links, one under the other, in the same order and with the same classes. The current page is marked here too.
  - Under the grid, with `mt-7`, a `<p>` in `text-body text-foreground`, at most `max-w-xl`: "This practice is not an emergency service. In an emergency, call your local emergency number or go to the nearest emergency department." Then with `mt-4` a label line in `text-muted-foreground`: `© 2027 Nora Ellingham`.
- **The practice:** Nora Ellingham is an invented registered psychotherapist who works alone, with adults, one to one, in private practice since 2014. She sees people in The Garden Room, 14 Orchard Row, Larkmere LK2 4DN (an invented town) and online by video. Sessions are held on Tuesday 09:00–17:00 (in person), Wednesday 12:00–20:00 (in person or online), Thursday 09:00–17:00 (online) and Friday 09:00–13:00 (online). A session is 50 minutes and costs £68. It starts with an introductory call of 20 minutes, by phone or video, which is free. A longer session of 80 minutes is £102, and a reduced-fee session is £42. She replies to a request within two working days, by email from `hello@ellingham.example`; the practice has no public phone number. Cancelling is free with 48 hours' notice. She sends one invoice on the last Friday of each month, paid by bank transfer within 14 days. She speaks English and Dutch. Every page uses these names, times and fees as they are here. Name no real person, place, register, insurer, school or helpline.
- **Writing:** the words are part of the design. Nora writes as one person to another: "I", never "we" for the practice, and "you". Short sentences and plain words. No exclamation marks, no urgency ("book now", "today only", "limited places"), no promise of a result ("heal", "fix", "overcome", "transform"), no praise of herself. Copy that this brief gives in quotation marks is used word for word. Where you write a sentence yourself, write it the same way, and say what happens rather than how it will feel.
- **Headings:** the `<h1>` is `text-h1`, and `text-display` from `md` up where this brief says so (`text-h1 md:text-display`). An `<h2>` is `text-h2`, and so is an `<h3>`, unless this brief gives another size. Headings get the heading font on their own; any other element in `text-display`, `text-h1` or `text-h2` adds `font-heading`. Sentence case, no full stop. Nothing on these pages is bold: the theme has two weights, 400 and the 500 that `text-h2` and `font-medium` give. Never `font-semibold` or `font-bold`, no `<strong>` or `<b>`, and a `<th>`, which is bold by default, gets `font-normal`.
- **The italic:** both the heading font and the body font have a true italic. It is the page's one emphasis, and it is used in two places only. A phrase inside a heading, where this brief marks it, is an `<em>` with `italic`. Words a client said are a `<blockquote>` whose `<p>` is `font-heading text-h2 italic`. Nothing else is italic: an `<address>` is italic by default, so every `<address>` gets `not-italic`.
- **Labels:** times, lengths of sessions, fees, days of the week, dates, step numbers and places in a short line are labels: `font-mono text-label`, in sentence case. Nothing is uppercased, in the text or with a class, and no `tracking-*` class is added. A label is a short fragment. Join the parts of a label line with ` · `. A label on its own line is a block element such as a `<p>`. Nav links, button text, field labels and legends are not labels: they are in the body font.
- **Captions:** a caption under a quotation or a table and the helper text of a field use the label style in `text-muted-foreground` (`font-mono text-label text-muted-foreground`) and may be a short sentence or two.
- **Text color:** headings, body text, nav links, names, field labels, fees and times in a table are `text-foreground`. Label lines above or under a heading, terms in a `<dl>`, column headers of a table, step numbers and captions are `text-muted-foreground`. Muted text sits only on `bg-background` or `bg-card`. Paragraphs are never muted. No other text colors, except `text-destructive` on an error line and the colors a button, a badge and a portrait bring.
- **Measure:** a paragraph of body text is at most `max-w-xl` wide, however many columns it sits in. A heading may run wider.
- **Times, dates and fees:** a clock time is 24-hour with two-digit hours, `09:00`, and a span of hours has an en dash and no spaces, `09:00–17:00`. A time that stands in a table or a label line is a `<time>` with a `dateTime` (`<time dateTime="09:00">09:00</time>`), with `tabular-nums`. A time or a length inside a sentence, a field's label or a message is plain text. A length is written out: `50 minutes`. A fee is whole pounds with the pound sign, `£68`, or the word `Free`. A date is written `1 January 2027`, and in a label line it is a `<time>` too (`dateTime="2027-01-01"`). A year is a `<time>` only where this brief says so; the copyright year is plain text.
- **Links:** a link in running text is `text-foreground underline decoration-1 underline-offset-4 hover:text-muted-foreground`, and inherits its size and font. Links are the text color; nothing is colored to mark it as a link. Every link gets `transition-colors duration-150`. A link that stands alone at the end of a part is a `<p>` holding such a link, with no icon. A link whose destination this brief does not give points to `#`. The class lists in these notes and briefs leave out the focus outline: add the outline classes from the rules to every link, every native radio and every plain `<button>`.
- **Buttons:** the shadcn `Button` with `className="h-auto px-7 py-2 text-body"`: the body font, sentence case, two to four words, 12px corners (its stock `rounded-lg`) and about 46px tall. It keeps its stock `font-medium`: weight 500 is the heaviest weight the theme's type scale has, so no button is `font-semibold`. The default variant (indigo, rose taupe in dark mode) is for the page's one main action: one on a page, and this brief names it. Any other button is `variant="secondary"` (rose taupe, slate in dark mode). In `components/ui/button.tsx`, replace that variant's stock `hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)]` with `hover:bg-secondary/80`, once, not beside it: the 5% mix cannot be told from the resting color. Make this change on every page, also one with no secondary button, so every page's `button.tsx` is the same. Do not use the `outline`, `ghost`, `link` or `destructive` variants, or the `xs`, `sm` or icon sizes. A button that sends a form needs `type="submit"`: this `Button` is `type="button"` unless told otherwise. A call to action that goes to another page is an `<a>` with the same classes: write `cn(buttonVariants({ variant }), "h-auto px-7 py-2 text-body")`, because `buttonVariants` alone does not merge classes. No icon inside a button.
- **The one action:** a page asks for one thing. Where this brief ends a page with the action, it is a card from these notes holding an `<h2>`, with `mt-4` one or two sentences that say what happens after it, and with `mt-7` a `<div>` holding the default button "Request a first session", an `<a>` to `/first-session`. Nothing on a page hurries: no countdown, no "limited", no banner, no pop-up.
- **Added components:** a component you add with `npx shadcn@latest add` during the build (an `Input`, a `Textarea`, a `Checkbox`, a `Badge`) gets the `cn` import from the setup steps, the focus outline only if it takes focus on the page, and the edits these notes give its file.
- **Inputs:** the shadcn `Input` and `Textarea` with `className="h-auto border-muted-foreground px-4 py-2 text-body md:text-body"`; a `Textarea` adds `min-h-32`. The stock `border-input` is too faint on blush; `border-muted-foreground` reaches 3:1 in both modes. They keep their stock 12px corner (`rounded-lg`), the fill they ship with (in dark mode too), the stock `focus-visible:border-ring` and the stock `aria-invalid:border-destructive`. Every field has a visible `<label>` above it with `block text-body font-medium text-foreground`, tied to it with `htmlFor`. Under the label, where this brief gives one, a helper line with `mt-2`: a caption in a `<p>` with an `id`, which the field names in `aria-describedby` at all times. The field has `mt-2` under the label or the helper line. Fields follow each other with `mt-7`, on the wrapper `<div>` that holds a label, its helper line, its field and its error. A field that may be left empty says so in its label: `Phone number (optional)`. No placeholder text.
- **Groups of choices:** a set of radios or checkboxes that answers one question is a `<fieldset>` with `min-w-0`, inside its wrapper `<div>`, with a `<legend>` as its first child in `text-body font-medium text-foreground`. A helper line, where this brief gives one, follows with `mt-2`, then the options, the first with `mt-2`, then the error line while there is one. The fieldset's `aria-describedby` names the helper line and, while it shows, the error line.
- **Radios:** native `<input type="radio">` elements, drawn round with classes, not the shadcn `RadioGroup`: `size-5 shrink-0 appearance-none rounded-full border border-muted-foreground checked:border-6 checked:border-primary`, so the chosen one is a thick indigo ring (rose taupe in dark mode) around a hole. Each option is a row, `flex min-h-11 items-center gap-4`, holding the input with an `id` and its `<label>` with `htmlFor` in `text-body text-foreground`. All options of a group share a `name`. No option is chosen when the page opens.
- **Checkboxes:** the shadcn `Checkbox` with `className="size-5"`. In `components/ui/checkbox.tsx`, change its `rounded-[4px]` to `rounded-sm` and its `border-input` to `border-muted-foreground`, once, so it has the theme's 6px corner and a border that can be seen (where the file ships another corner or border class, change that one). Its stock check icon and checked fill (`bg-primary`) stay. In a group, each option is a row, `flex min-h-11 items-center gap-4`. A checkbox whose label is a sentence that may wrap is a row with `flex items-start gap-4`, and `mt-1` on the checkbox so it lines up with the first line. The `<label>` is `text-body text-foreground`. Give the `Checkbox` an `id` and the `<label>` `htmlFor` with it, so a click on the words ticks the box; give the `<label>` an `id` too, and the `Checkbox` `aria-labelledby` naming it, because in a Base UI project the `Checkbox`'s `id` lands on a hidden input and the visible box would otherwise have no name.
- **Errors:** an error is a line under its field or under the last option of its group, with `mt-2`: a `<p>` with an `id` and `flex items-start gap-2 text-body text-destructive`, holding the `CircleAlert` icon (`mt-1 shrink-0`) and one or two short sentences that say how to put it right, in the same calm voice as the rest. A field gets `aria-invalid` and adds the line's `id` to its `aria-describedby` (after the helper line's); a group names the line in its fieldset's `aria-describedby`. Fields are checked in code when the form is sent, with `noValidate` on the `<form>`; before the first send no error shows. Focus moves to the first field with an error, or to the first option of the first group with one. Render the error lines first and move focus after that render, in an effect or a `requestAnimationFrame`, so the field's description is there when it is read. An error line and its field's `aria-invalid` are removed when that field changes (on `input` or `change`; for a `Checkbox`, on `onCheckedChange`; for a group, when any of its options changes). Every other error stays until the next send. An email is well formed when, trimmed, it matches `/^[^\s@]+@[^\s@]+\.[^\s@]+$/`.
- **Forms:** a form sits in a card, and the card is the wrapper for the form and the confirmation that replaces it: a card from these notes holding the `<form>` and, after it, the status element. The `<form>` has `noValidate` and an `aria-labelledby` naming the heading inside it. Its send button sits under the last field with `mt-7`, full width below `md` (`w-full md:w-auto`).
- **Confirmations:** a message that replaces a form goes into an element with `role="status"` that is in the card from the start, empty, so that its message is announced. It is never hidden (no `hidden` attribute, no `empty:hidden`) and has no classes and no margin; empty, it takes no space. The form is removed when the message appears, so the message stands where the form stood. The message is a `<div>` rendered only once there is one: an `<h2>` in `text-h2`, then with `mt-4` the paragraphs this brief gives, in `text-body`, each a `<p>`, `mt-4` apart. Give the heading `tabIndex={-1}` and move focus to it, because the button that had focus is gone. No colored box, no tick icon.
- **Cards:** a card is a plain element with `rounded-xl border border-border bg-card p-7`: paler blush, 20px corners, 28px padding, a 1px border, no shadow. Not the shadcn `Card`, which ships its own padding and a ring. Inside it the first element takes no top margin. Use one only where this brief asks for it. Never a card inside a card, and nothing with a border and corners inside a card except the fields of a form: divide a card's parts with space.
- **Portraits:** there are no image files. A portrait is a round fill with the person's initials: a `<div>` with `flex size-18 shrink-0 items-center justify-center rounded-full bg-secondary font-heading text-h2 text-secondary-foreground`, `role="img"` and the `aria-label` "Portrait of Nora Ellingham", holding one `<span>` with `aria-hidden="true"`, `NE`. It is 72px across, the largest full round the theme allows, so it is never made larger. Nothing else is drawn in it and nothing is drawn around it: no icon, no ring, no leaf, no wave. It stands beside the name and a line of training, in a row with `flex items-center gap-4`, and never inside a button or a link.
- **Badges:** the shadcn `Badge` (`npx shadcn@latest add badge`) with `className="h-auto rounded-sm border-transparent bg-tertiary px-2 py-1 font-mono text-label font-normal text-tertiary-foreground"`: mauve grey with ink, in both modes. One or two words in sentence case, no icon: "Online", "In person", "Evenings". Only where this brief puts one. Nothing else on a page is `bg-tertiary`.
- **Lists with hairlines:** a list where this brief gives each entry a hairline (what people bring, training, questions) is a `<ul>`, `<ol>` or `<dl>` with `list-none`, no padding and no space between its entries; each entry has `border-t border-border py-4`, and the list a `border-b border-border` that closes it. Where this brief lays an entry out in columns, the entry itself carries the grid classes beside these.
- **Description lists:** a `<dl>` where a term stands beside its value is a list with hairlines whose rows are `<div>` elements with `grid grid-cols-3 items-baseline gap-x-4` beside the entry's classes: the `<dt>` is a label in `text-muted-foreground`, the `<dd>` takes the other two columns (`col-span-2`) in `text-body`. This holds at 375px too.
- **Tables:** a plain `<table>` with `w-full border-collapse border-b border-border text-left`, not the shadcn `Table`. Its first child is a `<caption>` with `sr-only` that names it; the heading above it names it on screen. Header cells are `<th scope="col">` with `py-2 pr-4 font-mono text-label font-normal text-muted-foreground`. Each row of the `<tbody>` has `border-t border-border`. Its first cell is a `<th scope="row">` with `py-4 pr-4 align-baseline text-body font-normal text-foreground`; the other cells are `<td>` with `py-4 pr-4 align-baseline`. The last cell of every row, the header row included, has `pr-0` in place of `pr-4`. No stripes, no hover fill, no outer box. The table is not in a scroll box: the cells this brief names get `whitespace-nowrap` and the others wrap, so it fits at 375px.
- **Icons:** from the project's icon set, `lucide-react`, with `size-4`, `strokeWidth={1.5}` and `aria-hidden="true"`. Use them only where this brief or these notes name one: `CircleAlert` in an error line, `Plus` and `Minus` on a question. Never emoji, and no drawn decoration.
- **Corners:** `rounded-sm` (6px) for badges and checkboxes; the stock 12px of the `Button`, `Input` and `Textarea` (their `rounded-lg`, which this theme sets to 12px); `rounded-xl` (20px, the `lg` corner in `DESIGN.md`) for cards. `rounded-full` only for a portrait and a radio.
- **Surfaces:** flat and soft. A card is the only surface, and it has a border and no shadow. No shadows, blur, gradients or glows in the code you write, and no colored band behind a section. A stock component that floats over the page keeps the one soft shadow it ships with; these pages use none.
- **Motion:** in the code you write, the only transition is color, and nothing moves on scroll or on load. Stock components keep the motion they ship with, the `Button`'s included.

## The page

Where this section and the layout notes above differ, this section wins.

Sample content, if the user gave none: the practice of **Nora Ellingham**, an invented registered psychotherapist who works alone, with adults, one to one, in a garden room at 14 Orchard Row in Larkmere, an invented town, and online by video. In private practice since 2014, after four years of training and nine years as a teacher. A session is 50 minutes and costs £68; it starts with a free call of 20 minutes; she replies to a request within two working days. The copy below is given word for word, because the page is only as calm as its words: use it as it is.

Build these parts in order.

### 1. Header

The header from the layout notes. None of the links is the current page.

### 2. Opening

The first section, in the grid, with its `aria-labelledby` naming the `<h1>`:

- A label line in `text-muted-foreground`, columns 1–12 (`md:col-span-12`): `Psychotherapy · Larkmere and online`.
- With `mt-4`, the `<h1>` in `text-h1 md:text-display`, columns 1–9 (`md:col-span-9`): "A quiet room, and time to think". The words "time to think" are an `<em>` with `italic`. It runs over two lines on a large screen.
- With `mt-7`, a `<p>` in columns 1–7 (`md:col-start-1 md:col-span-7`, which overlap the `<h1>`'s, so it starts a row under it), at most `max-w-xl`, in `text-body`: "I am Nora Ellingham, a psychotherapist. I work with adults, one to one, in a garden room in Larkmere and online. You do not need to know what to say before you come."
- With `mt-7`, in the same columns (`md:col-start-1 md:col-span-7`), a `<p>` holding one link in running text: "How it starts", to `#how-it-starts`.

No picture, no button and no badge in this section: the heading and the space around it are the whole opening.

### 3. What people bring

A section in the grid, with two parts:

- **Heading**, in columns 1–4 (`md:col-span-4`): an `<h2>`, "What people bring", then with `mt-4` a `<p>` in `text-body`: "Most people come with one of these, or with something they cannot name yet. Both are fine."
- **The list**, in columns 6–12 (`md:col-start-6 md:col-span-7`), with `mt-7 md:mt-0`: one `<div>` holding a `<ul>` as a list with hairlines (see the layout notes), six entries. Each `<li>` holds an `<h3>` in `text-h2` and, with `mt-2`, one sentence in `text-body`. Under the list, in the same `<div>`, with `mt-7`, a `<p>` in `text-body`, at most `max-w-xl`: "If what you carry is not on this list, write to me anyway. I will tell you honestly whether I am the right person."

  The entries, in this order, word for word:

  1. "Anxiety and worry": "Thoughts that will not settle, or a body that is always braced."
  2. "Low mood": "Days that feel flat or heavy, often with no clear reason."
  3. "Grief and loss": "Someone or something has gone, and the world carries on anyway."
  4. "Burnout and work": "You keep going, and it costs more than it used to."
  5. "Relationships": "The same hurt keeps coming round, with a partner, with family or at work."
  6. "A change you did not choose": "Illness, redundancy, a move, or the end of something."

### 4. How it starts

A section with the `id` `how-it-starts` and `scroll-mt-7`, that is not a grid:

- An `<h2>`: "How it starts".
- With `mt-4`, a `<p>` in `text-body`: "Three steps, at your own pace. You can stop after any of them."
- With `mt-7`, an `<ol>` with `list-none` and no padding that is itself the grid (`grid grid-cols-1 md:grid-cols-12 md:gap-x-7`). Its three `<li>` are cards from the layout notes, each with `md:col-span-4`, the second and third with `mt-4 md:mt-0`. A card is also `flex flex-col`, and holds, in this order:
  - A label line in `text-muted-foreground`: `Step 1`, `Step 2`, `Step 3`.
  - With `mt-4`, an `<h3>` in `text-h2`.
  - With `mt-4`, a `<p>` in `text-body`.
  - Last, a label line in `text-foreground` with `mt-auto pt-7` (here, in place of a top margin from the rhythm rule), so the three lines stand on one level whatever the length of the text above them.

  The steps, word for word:

  1. "You write to me": "Use the form, and say as much or as little as you like. A name and a way to reach you is enough." Label: `I reply within 2 working days`.
  2. "We speak for 20 minutes": "A call, by phone or video. You can ask me anything, and I ask what you hope for. There is no charge." Label: `20 minutes · Free`.
  3. "We meet for a first session": "In the room or online. At the end we each say whether we would like to go on. Either answer is fine." Label: `50 minutes · £68`.

### 5. Who you would be talking to

A section in the grid, with two parts:

**The text**, in columns 1–7 (`md:col-span-7`):

- An `<h2>`: "Who you would be talking to".
- With `mt-7`, a row with `flex items-center gap-4`: the portrait from the layout notes, and beside it a `<div>` with `min-w-0` holding the name as a `<p>` in `font-heading text-h2`, "Nora Ellingham", and with `mt-2` a label line in `text-muted-foreground`, `Registered psychotherapist · In practice since 2014`.
- With `mt-7`, two paragraphs in `text-body`, each at most `max-w-xl`, the second with `mt-4`: "I trained for four years, after nine years as a teacher. I have worked with adults in private practice since 2014." and "I listen more than I talk. I will not hurry you, and I will not tell you what to do with your life."
- With `mt-7`, a `<p>` holding one link in running text: "More about me and my training", to `/about`.

**The note**, in columns 9–12 (`md:col-start-9 md:col-span-4`), with `mt-12 md:mt-0`: a card from the layout notes.

- An `<h3>` in `text-h2`: "In short".
- With `mt-4`, a `<dl>` of three rows. Here, instead of the Description lists note, a row is a plain `<div>` with no hairline, the second and third with `mt-4`: the `<dt>` holds a badge from the layout notes, and the `<dd>`, with `mt-2`, one line in `text-body`. The rows: "In person" / "The Garden Room, 14 Orchard Row, Larkmere"; "Online" / "By video, with a link I send the day before"; "Evenings" / "On Wednesdays, until 20:00".

These three are the page's only badges.

### 6. The fee

A section in the grid, with its content in columns 1–8 (`md:col-span-8`):

- A label line in `text-muted-foreground`: `Fees from 1 January 2027`.
- With `mt-4`, the fee in one line: an `<h2>` in `text-h1` (here, instead of the notes' `text-h2`), "A session is 50 minutes and costs £68".
- With `mt-4`, a `<p>` in `text-body`, at most `max-w-xl`: "The first call is 20 minutes and free. I send one invoice a month, and there is no charge if you cancel with 48 hours' notice."
- With `mt-7`, a `<p>` holding one link in running text: "Fees, paying and cancelling", to `/fees`.

### 7. The one action

A section in the grid, with one card in columns 1–8 (`md:col-span-8`), built as the note The one action says. The section's `aria-labelledby` names the card's `<h2>`.

- The `<h2>`: "When you are ready".
- With `mt-4`, a `<p>` in `text-body`, at most `max-w-xl`: "Write to me with the form. I reply within two working days, and nothing is booked until we have spoken."
- With `mt-7`, a `<div>` holding the default button "Request a first session", an `<a>` to `/first-session`. The page's one default button.

### 8. Footer

The footer from the layout notes.

### On a phone

Every part is one column. The `<h1>` is `text-h1` and wraps over three lines. The list of what people bring comes 28px under its heading and sentence. The three step cards stack, 16px apart, each as tall as its own text. The portrait keeps its 72px and the name stays beside it; the label line under the name wraps. The In short card comes 48px under the text. The fee's line wraps over three or four lines. The button is as wide as its words.

### Behavior

Nothing on this page has state. Links only: "How it starts" jumps to the steps, and `scroll-mt-7` keeps their heading clear of the top edge.

## Rules

- **Colors:** only the theme's colors, through the semantic classes (`bg-background`, `text-foreground`, `bg-card`, `bg-primary`, `text-primary-foreground`, `text-muted-foreground`, `border-border` and so on). No hex, rgb or named colors in components, and no Tailwind palette classes such as `bg-blue-500` or `text-gray-600`.
- **Spacing:** padding, margin and gap use only the scale's steps (`1`, `2`, `4`, `7`, `12`, `28`) plus 0, 1px and 2px. No arbitrary values such as `p-[18px]`, and no other steps such as `p-3` or `gap-6`. `auto` margins are fine. Widths and heights such as `w-64` or `size-4` are sizes, not spacing. This applies to the code you write: a stock component keeps the spacing inside it, unless this brief changes it.
- **Type:** every piece of text you write gets its size from one of `text-display`, `text-h1`, `text-h2`, `text-body`, `text-label`, set on the element or on a parent it inherits from. Put `text-body` on the outermost element of the page you write. No other sizes. A stock shadcn component keeps the size it ships with, unless `DESIGN.md` or this brief gives that component its own style. A type class does not replace a weight class: where a stock component ships `font-medium` and the text should be regular, add `font-normal`.
- **Fonts:** only the theme's fonts, through `font-sans`, `font-heading` and `font-mono`. Do not add a font.
- **Corners:** only the `rounded-*` classes, which the theme already sets. No arbitrary radius, and `rounded-full` only where `DESIGN.md` or this brief names a full round, such as an avatar or a status dot. The theme allows a full round on a box up to 72px across; Strict Check reports a larger one, and a pill counts by its height.
- **Contrast:** all text meets WCAG AA against its background.
- **Stock components:** a component from `components/ui`, and an `<a>` styled with `buttonVariants`, keeps the hover, pressed and disabled colors and the motion it ships with, unless this brief changes them. They are the theme's colors under other names.
- **Counts:** a number with a noun reads right at one: "1 set", not "1 sets". Where this brief gives the words for only one of the two, write the other the same way.
- **Sizes from data:** a height or a width that comes from data (a chart bar, a meter) is set with an inline `style`. That is the one use for an inline style.
- **Focus:** every link and control shows a 2px outline on keyboard focus: `focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring`. Keep `focus-visible:outline-solid` in it: in Tailwind 4 an `outline-none` on the same element, which stock components carry, otherwise hides the outline. An element that only gets focus from code (a heading or a message with `tabIndex={-1}`) gets the same outline written with `focus:` in place of `focus-visible:` (`focus:outline-solid focus:outline-2 focus:outline-offset-2 focus:outline-ring`): after a mouse click, a browser does not match `:focus-visible` on an element that code focuses. A link, a button or a field that code focuses keeps the `focus-visible:` classes it has: after a mouse click it shows no outline, which is how a browser treats every control, and is right. A stock component that ships its own focus ring (`Button`, `buttonVariants`, `Input`, `Textarea`, `Checkbox`, `Switch`, `AccordionTrigger`, the close button of a `Sheet`) must lose that ring. Do it once, in the component's file in `components/ui`, which is yours to change: replace its `focus-visible:ring-*` classes with the outline classes, and in a field replace its `aria-invalid:ring-*` classes, the `dark:` ones included, with `aria-invalid:ring-0`, or a field with an error shows a tinted ring. `Button` has two more sets. Its base classes also carry `aria-invalid:ring-*` classes: replace them with `aria-invalid:ring-0`, as in a field. Its `destructive` variant carries `focus-visible:ring-*` classes of its own, the `dark:` one included: delete them and put nothing in their place, because the base classes already give every variant the outline. Wherever this rule says replace, change the classes in the file and do not add `ring-0` beside them: an added class only wins where it comes later and passes through `cn`. The stock `focus-visible:border-ring` can stay. These ring changes go only into the files of components that this page uses and that can take focus on it: a `Badge` that is not a link keeps its ring classes. The `cn` import from the setup steps is another matter and goes into every file. A box that scrolls sideways (a code block, a wide table) gets `tabIndex={0}`, `role="region"`, an `aria-label` and the same outline, also at widths where it happens to fit. Where a focusable box touches the edges of a parent that has `overflow-hidden`, draw its outline inside it: `focus-visible:-outline-offset-2` in place of `focus-visible:outline-offset-2`.
- **Not allowed:** purple or multi-color gradients; emoji used as icons or bullets; glows and colored shadows.
- **Avoid:** a card inside a card.
- **Dark mode:** the page must work with the `dark` class on `<html>`. Because every color comes from a semantic class, this needs no extra code; do not write `dark:` color overrides. A new shadcn project follows the system's mode and switches on the `d` key: leave that as it is, and add no mode switch of your own. If the page has a native `<select>` or a date or time field, tell the browser the mode, or its list and its picker stay light in dark mode: in the global CSS, give `:root` `color-scheme: light` and `.dark` `color-scheme: dark`.

From the theme's own do's and don'ts:

- Do write as one person speaks to another: short sentences, plain words, "you" and "I".
- Do give one action on a page and say what happens after it.
- Do say fees, lengths and how to cancel plainly.
- Don't use urgency: no countdowns, no "limited places", no pop-ups.
- Don't promise results, and don't decorate with leaves, waves or sunrises drawn in code.
- Don't use gradients, glows or a card inside a card.
- Don't introduce a color, size or radius that is not a token above.

## Check before you finish

Go through this list against the code you wrote, and fix anything that fails.

- [ ] The `<h1>` is the page's only `text-display` text, from `md` up, its phrase time to think is an `<em>` in the italic, and no heading or text on the page is bold.
- [ ] The page has exactly one default (indigo) button, Request a first session, a link to /first-session inside the last card; the header and the opening hold no button.
- [ ] The three steps are an ordered list of three cards, each with a step number, a heading, a sentence or two and a label line: I reply within 2 working days, 20 minutes and Free, 50 minutes and £68.
- [ ] The portrait is a 72px round fill with the initials NE, with the img role and an aria-label, beside the name and a line of training; nothing else on the page is round.
- [ ] The fee is one line in the heading font at `text-h1`, an `<h2>`, and it says 50 minutes and £68.
- [ ] Sections are divided by 112px of space alone, with no hairline or fill between them, and no sentence on the page hurries the reader or promises a result.
- [ ] No color is written as a hex, rgb or Tailwind palette value in the page's code.
- [ ] Every padding, margin and gap in the code you wrote is a step of the spacing scale.
- [ ] Every text element you wrote gets its size from a type-scale class, its own or an inherited one. Visually hidden text is exempt.
- [ ] No file in `components/ui` imports `cn` from `"cn"` directly.
- [ ] Text contrast meets WCAG AA in light and dark mode.
- [ ] The page has one `<h1>`, headings are in order, and every image has alt text.
- [ ] Every interactive element can be reached with the keyboard and shows the focus outline from the rules.
- [ ] At 375px wide the page does not scroll sideways. Wide tables and code blocks scroll inside their own box.
- [ ] The project builds without errors or warnings.

Then tell the user which checks pass, which you could not verify, and anything in this brief you did not do and why.
