# Build a new enquiry page with the Counsel theme

You are building one page in an existing project: a commercial law firm's page for a new enquiry, with its address, telephone and office hours, what happens next, and a form for who you are, what the matter concerns, a message and consent, checked in code and replaced by a confirmation. 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 Counsel variables in its global CSS. If not, run `npx shadcn@latest add https://strictthemes.com/r/counsel.json` and save https://strictthemes.com/themes/counsel/DESIGN.md as `DESIGN.md` in the project root. In a project without shadcn, use https://strictthemes.com/themes/counsel/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:** Marcellus for headings, Jost for body text, Courier Prime for labels and code.
- **Type scale** (size/weight; each class also sets line height and tracking): `text-display` 60px/400, `text-h1` 40px/400, `text-h2` 24px/400, `text-body` 17px/400, `text-label` 13px/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`), 32px (`8`), 56px (`14`), 112px (`28`).
- **Corners:** `rounded-sm` 1px, `rounded-md` 2px, `rounded-xl` 4px (the `lg` corner in `DESIGN.md`). `rounded-lg` is 2px as well: it is shadcn's name for the base corner, the one its stock buttons and inputs use.
- **Colors (light mode):** primary #14213D, on-primary #FFFFFF, secondary #000000, on-secondary #FFFFFF, tertiary #FCA311, on-tertiary #000000, neutral #5B6170, background #FFFFFF, surface #F6F6F6, foreground #000000, border #E5E5E5, error #B3261E.

### How Counsel pages are laid out

- **Container:** one `<div>` wraps the whole page and is the container: `mx-auto max-w-[1200px] px-4 md:px-8`, plus `text-body`. 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-8`. 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. Leave the columns this brief does not fill empty; an empty column is part of the design, so center nothing to fill it. 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. An item whose columns overlap those of the item before it always starts a new row. Where a brief puts "a section's content in columns 5–12", 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 at the full width.
- **Text and note:** running text sits in seven of the twelve columns (`md:col-span-7`, or the columns this brief gives). Beside it, where this brief asks for one, sits one short note in columns 9–12 (`md:col-start-9 md:col-span-4`), with `mt-14 md:mt-0` on a phone, so it comes after the text. Nothing else stands beside the text.
- **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 a section gets around its hairline (see Sections) and the padding of a card or a list entry, which these notes give. Between a label and the one thing it names (a form label and its field, a name and the role under it, a term and its value when they stack): `mt-2`. Between a heading and the text under it, and between paragraphs: `mt-4`. A label line above a heading, and a label heading above its text, its `<address>` or its `<dl>`, count as headings: `mt-4` to what follows. Between blocks inside a section (a paragraph and a button, a heading and a list, a card and the text after it, two parts of a side column): `mt-8`. Between two parts of a section that each have their own `<h2>`: `mt-14`. Where two grid items share a row from `md` up, the second has its phone margin and `md:mt-0`, unless this brief gives a margin from `md` up. Inside a flex row (the header, a label line with a badge, a group of buttons) 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. The first has `mt-14 md:mt-28` and no border: the header's hairline is above it. Every other section is divided from the one before it by one hairline, with white space on both sides of it: `mt-8 border-t border-border pt-8 md:mt-14 md:pt-14`, which is 112px from section to section on a large screen and 64px on a phone, with the line in the middle. That is the whole gap and the only padding a section has; the section's first element takes no top margin of its own.
- **Header:** the `<header>` is itself the row, with `border-b border-border py-8`: `flex flex-col items-start gap-4 md:flex-row md:items-baseline md:justify-between`. On the left the firm's name, `Harrow & Vane`, a link to `/` in `font-heading text-h2`, styled as a header link and never marked as the current page. It is not a heading element, and it has no weight class. On the right a `<nav>` with the `aria-label` "Main" and no classes, holding a `<ul>` with `flex flex-wrap gap-x-4 gap-y-2 md:gap-x-8` of four links as labels, in this order: "Practice" (`/practice/commercial-disputes`), "People" (`/people/ruth-calloway`), "Insights" (`/insights/notice-clauses`) and "Contact" (`/contact`). Each brief names the current one, if any. Below `md` the links sit under the name and may wrap onto a second line; there is no "Menu" button.
- **Footer:** a `<footer>` with `mt-14 md:mt-28`, a 1px `border-border` top border and `py-8`. Inside it a grid like the one above, with three parts:
  - Columns 1–4 (`md:col-span-4`): the firm's full name as plain text in `font-heading text-h2`, `Harrow & Vane LLP`, then with `mt-2` the office's address: one `<address>` with `not-italic font-mono text-label uppercase text-muted-foreground` holding two `<p>` lines with no margin between them, `4 Chancery Row` and `Wexcombe WX1 2LN`.
  - Columns 5–8 (`md:col-start-5 md:col-span-4`, with `mt-8 md:mt-0`): two `<p>` label lines, the second with `mt-2`: the telephone number `01632 960 214` as a `tel:01632960214` link, and the email address `enquiries@harrowvane.example` as a `mailto:` link in `normal-case`. Both styled as footer links.
  - Columns 9–12 (`md:col-start-9 md:col-span-4`, with `mt-8 md:mt-0`): a `<nav>` with the `aria-label` "Footer" holding a `<ul>` of the header's four links, in the same order, as labels, `mt-2` between items. The current page is marked here too, with `aria-current="page"` and `underline`.
  - Under the grid, with `mt-8`, a label line in `text-muted-foreground`: `© 2026 Harrow & Vane LLP · Registered in England and Wales`.
- **Headings:** the `<h1>` is `text-h1`, and `text-display` from `md` up where this brief says so (`text-h1 md:text-display`). An `<h2>` in the heading font is `text-h2`, and so is an `<h3>`. Nothing in the heading font is smaller than `text-h2`: a smaller heading is a label heading. Headings keep the regular weight their type class gives them: add no weight class, never `font-bold` or `font-semibold`. Sentence case, no full stop.
- **No bold, no italics:** Marcellus has one weight and no italic, and nothing on these pages fakes either. No `<em>`, `<i>`, `<strong>`, `<b>` or `<cite>`, and no `italic` or bold class, in headings or in running text. An `<address>` is italic by default: give every `<address>` `not-italic`.
- **Labels:** nav links, dates, matter numbers, the names of practice areas in a label line, roles under a name, form labels, terms in a `<dl>`, and button and badge text are labels: `font-mono text-label uppercase`. The wide tracking comes with `text-label`; do not add a `tracking-*` class. Write a label as a short fragment, not a sentence. Error lines and helper text use the label's font and size but are sentences, and are not uppercased. Join the parts of a label line with ` · `. A label on its own line is a block element such as a `<p>`. Email addresses and what someone typed are never uppercased: put an email address inside a label in a `<span>` (or give its link) `normal-case`.
- **Label headings:** a heading that is styled as a label (Address, Hours, Contact) is an `<h2>` or `<h3>`, whichever keeps the heading order, with `font-mono text-label uppercase text-foreground`. Keep the `font-mono`: a heading element gets the heading font on its own.
- **Text color:** headings, label headings, body text, nav links, names and form labels are `text-foreground`. A button's or a badge's text color comes from its variant or its classes. Dates, matter numbers, roles under a name, `<dt>` terms, caption lines and helper text are `text-muted-foreground`. Muted text sits only on `bg-background` or `bg-card`. Paragraphs are never muted. No other text colors: gold (`tertiary`) and navy (`primary`) are never text colors.
- **Measure:** a paragraph of body text is at most `max-w-xl` wide (576px, about 70 characters in Jost at this size), however many columns it sits in. A heading may run wider.
- **Figures:** a sum, a count or a year that stands on its own in a label is `font-mono tabular-nums`. A large figure in a row of figures is `font-heading text-h1`, where this brief says so. Sums are written with the pound sign before the number: `£6.2m`, `£48m`, `£1,480`. A date or a year that stands on its own (in a label line, a `<dd>`, a list entry) is a `<time>` with a `dateTime`. A date or a year inside a sentence (running text, a confirmation message, an error line) is plain text, not a `<time>`, unless this brief says otherwise. A date is written `10 November 2026` (`dateTime="2026-11-10"`), a time `08:30`, a year `2026`. The copyright year is plain text.
- **Telephone numbers** are written in groups, `01632 960 214`; a `tel:` link's `href` has the digits with no spaces (`tel:01632960214`).
- **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. A link in the header or the footer, and a name or a title that is a link, is `text-foreground decoration-1 underline-offset-4 hover:underline`; the link for the current page has `aria-current="page"` and `underline`. Mark one only where this brief names the current page. Links are never colored, and get `transition-colors duration-150`. A link whose destination this brief does not give points to `#`; the firm's name always points to `/`.
- **Onward links:** a link that stands alone at the end of a part ("All partners", "All briefings") is a `<p>` holding the link (also where a brief puts it in a heading's row), as a label (`font-mono text-label uppercase`) styled as a link in running text, in `inline-flex items-center gap-2`, followed by the `ArrowRight` icon.
- **Buttons:** the shadcn `Button` with `className="h-auto px-8 py-2 font-mono text-label font-normal uppercase"`: `font-normal` because the labels are regular. The default variant (navy, gold in dark mode) is for the page's one main action: one on a page, unless this brief gives a second. Any other button is `variant="secondary"` (black, near-white 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 even on a page 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, or a `mailto:` or `tel:` link styled as a button, is an `<a>` with the same classes: write `cn(buttonVariants({ variant }), "…")`, because `buttonVariants` alone does not merge classes. Button text is at most four words.
- **Inputs:** the shadcn `Input` and `Textarea` (`npx shadcn@latest add input textarea`) with `className="h-auto border-muted-foreground px-4 py-2 text-body md:text-body"`. The stock `border-input` is too faint against the page and the card; `border-muted-foreground` reaches 3:1 in both modes. They keep their stock 2px corner, 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`, tied to it with `htmlFor` and styled as a label in `text-foreground`; the field has `mt-2`. Fields follow each other with `mt-8`, on the wrapper `<div>` that holds a label, its field and its error. Optional fields say so in the label: `Telephone (optional)`.
- **Selects:** a choice from a short list is a native `<select>` with `h-auto w-full rounded-md border border-muted-foreground bg-transparent px-4 py-2 text-body text-foreground dark:bg-input/30 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring` and the four `focus-visible:outline-*` classes from the rules: the fill, border and error classes are the ones the stock `Input` ships, so the two look alike in both modes; these two `dark:` classes are the one exception to the rules' "no `dark:` color overrides". It takes the same label, margins and error line as any field, and shows an error with `aria-invalid` and its red border, like an `Input`. Its first option is a prompt with an empty value.
- **Checkboxes:** the shadcn `Checkbox` (`npx shadcn@latest add checkbox`). In `components/ui/checkbox.tsx`, change its `rounded-[4px]` to `rounded-md` and its `border-input` to `border-muted-foreground`, once, so it has the inputs' corner and border (where the file ships another corner or border class, change that one). Its stock size, check icon and checked fill (`bg-primary`) stay. Its row is `flex items-start gap-4`, with `mt-1` on the checkbox so it lines up with the first line of its label, and the `<label>` in `text-body text-foreground`, not uppercased: it is a sentence, not a label. 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 with `mt-2`: a `<p>` with an `id`, `flex items-start gap-2 font-mono text-label text-destructive`, not uppercased, holding the `CircleAlert` icon (`shrink-0`, no margin) and a sentence that says what to do. The field gets `aria-invalid` and points to the line with `aria-describedby` (beside any helper line it already names). Fields are checked in code when the form is sent, with `noValidate` on the `<form>`, and focus moves to the first field with an error. 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 the checkbox, on `onCheckedChange`). Every other error stays until the next send.
- **Forms:** a form sits on the pale grey: the `<form>` is `rounded-xl bg-card p-8`, with no border and no shadow. The send button sits under the last field with `mt-8`.
- **Confirmations:** a message that replaces a form goes into an element with `role="status"` that is on the page from the start, empty. The form and the status element share one wrapper, and the wrapper carries the top margin and the column classes, so the message stands where the form stood. The status element has no classes and no margin, so empty it takes no space; never hide it. Inside it, rendered only once there is a message, a `<div>` with `rounded-xl bg-card p-8`, like the form: an `<h2>` or `<h3>` in `text-h2` (whichever keeps the heading order), then with `mt-4` one or two sentences in `text-body`, then whatever this brief adds. A button that follows the message sits after the status element in the same wrapper, not inside it, with `mt-8`. Give the heading `tabIndex={-1}` and move focus to it, because the button that had focus is gone.
- **Badges:** exactly one badge on a page, where this brief puts it, and it is the page's one use of gold: 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 uppercase text-tertiary-foreground"`. One or two words, no icon. Nothing else on the page is `bg-tertiary`. In dark mode the gold moves to the main button and the badge turns pale grey, as `DESIGN.md` says; that needs no code.
- **Cards:** a card is a plain element with `rounded-xl bg-card p-8`: pale grey, no border, no shadow. Not the shadcn `Card`, which ships its own padding and a ring. Inside it, a label comes first, with no top margin. Use one only where this brief asks for it, never a card inside a card, and nothing with a border or a fill inside a card except the fields of a form.
- **Portraits:** there are no image files. A portrait is a rectangle, never a circle: a plain `<div>` with `aspect-3/4 w-full rounded-xl border border-border bg-card`, `role="img"` and an `aria-label` that describes the photograph in a sentence ("Ruth Calloway in a dark suit, at a window of the firm's library"). It is drawn as a monogram plate: the `<div>` also has `flex items-end p-4` (`p-8` for a portrait this brief calls large), and holds one `<span>` with `aria-hidden="true"`, the person's initials in `font-heading text-h1 text-muted-foreground` (`text-display` for a large portrait). Nothing else is drawn in it: no icon, no gradient, no shape. A portrait never sits inside a card.
- **Lists of people and matters:** a list where this brief gives each item a 1px top border (people, matters, briefings, steps) is a `<ul>` or `<ol>` with `list-none`, no padding, and no space between its items; each item has `border-t border-border py-8`, unless this brief gives `py-4`, and the list a `border-b border-border` that closes it. In each item a name or a title is in the heading font (an `<h3>` in `text-h2`, or the element this brief names), and the role, the date or the matter number is a label line in `text-muted-foreground`, `mt-2` under the name or above the title as this brief says. Where this brief lays an entry out in columns, the `<li>` itself carries the grid classes beside these, and its parts are the grid items.
- **Description lists:** a `<dl>` is a list of rows. Each row is a `<div>` with `grid grid-cols-3 items-baseline gap-x-4 border-t border-border py-4`: the `<dt>` is a label in `text-muted-foreground`, the `<dd>` takes the other two columns (`col-span-2`) in `text-body`. The `<dl>` has `border-b border-border`, so the last row is closed too. This holds at 375px as well, unless this brief stacks the rows.
- **Rules:** every line is 1px and `border-border`. Hairlines, not boxes, divide a page. No shadows, no blur, no gradients, no colored backgrounds behind sections.
- **Corners:** `rounded-sm` (1px) for the badge, `rounded-md` (2px) for the select and the checkbox, the stock 2px of the `Button`, `Input` and `Textarea` (their `rounded-lg`, which this theme sets to 2px), and `rounded-xl` (4px, the `lg` corner in `DESIGN.md`) for cards, forms and portraits. Nothing is a pill or a circle: no portrait, no badge, no dot and no switch is round.
- **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. Never emoji.
- **Surfaces:** flat. A stock component that floats over the page (a menu, a dialog) keeps the one short shadow it ships with; these pages use none.
- **Motion:** in the code you write, the only transition is color, and nothing moves on scroll. 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 firm, if the user gave none: **Harrow & Vane LLP**, an invented commercial law firm in Wexcombe, an invented English city, founded in 1952, with 14 partners and 86 lawyers in five practice areas: Corporate, Commercial disputes, Employment, Real estate and Private client. Its one office is at 4 Chancery Row, Wexcombe WX1 2LN, on the second floor, with a lift. The main line is `01632 960 214` and the enquiries address enquiries@harrowvane.example. The office is open Monday to Friday 08:30–17:30 and closed at weekends. Every enquiry is checked for conflicts of interest before anyone replies; then a partner in the right team replies within one working day, and the first meeting, of up to an hour, is free. Every name is invented.

Build these parts in order.

### 1. Header

The header from the layout notes. "Contact" is the current page, in the header and in the footer.

### 2. Opening

The first section, in the grid:

- A label line in `text-muted-foreground`, columns 1–12 (`md:col-span-12`): `Enquiries · Wexcombe`.
- With `mt-4`, the `<h1>` in `text-h1 md:text-display`, columns 1–9 (`md:col-span-9`): "Tell us about your matter".
- With `mt-8`, 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`, two sentences in `text-body`. Sample: "Give us the outline and the names of the other parties, and a partner in the right team will reply within one working day. Please send no documents yet: we check for conflicts of interest first."

### 3. Office and enquiry

A section in the grid with two parts.

**The office**, in columns 1–4 (`md:col-span-4`):

- A label heading "Address", an `<h2>`.
- With `mt-4`, an `<address>` with `not-italic` in `text-body`: `Harrow & Vane LLP`, `4 Chancery Row`, `Wexcombe WX1 2LN` and "Second floor, with a lift", each on its own line with `<br>`.
- With `mt-8`, a label heading "Telephone", an `<h2>`, and with `mt-4` a `<p>` in `text-body` holding `01632 960 214` as a `tel:01632960214` link, styled as a link in running text.
- With `mt-8`, a label heading "Hours", an `<h2>`.
- With `mt-4`, a `<dl>` as the layout notes describe it, with two rows: Monday to Friday, and Saturday and Sunday. Here, instead of the notes' three columns at every width, a row is `grid grid-cols-1 lg:grid-cols-3 lg:items-baseline lg:gap-x-4` and its `<dd>` is `mt-2 lg:col-span-2 lg:mt-0`: from `md` to `lg` this column is too narrow for a day and a span of hours side by side. The row keeps its `border-t border-border py-4`. The weekday times are `font-mono tabular-nums` inside `<time>` elements (`<time dateTime="08:30">08:30</time>–<time dateTime="17:30">17:30</time>`); the weekend reads "Closed" in `text-body`.
- With `mt-8`, a label heading "What happens next", an `<h2>`.
- With `mt-4`, an `<ol>` as a list of entries (see the layout notes), three entries, each with `py-4` (here, instead of the notes' `py-8`). Each `<li>` is also `grid grid-cols-4 items-baseline gap-x-4`, which holds at 375px too: in column 1 the step's number as a label in `text-muted-foreground` with `tabular-nums`, `01`, `02`, `03`; in the other three a `<p>` with `col-span-3`, one sentence in `text-body`. Steps: we check the names you give for conflicts of interest; a partner in the right team replies within one working day; if we can help, the first meeting, of up to an hour, is free.

**The enquiry**, in columns 6–12 (`md:col-start-6 md:col-span-7`), with `mt-14 md:mt-0`: one wrapper holding the form and the empty `role="status"` element, as the layout notes describe (Forms and Confirmations). The form is `rounded-xl bg-card p-8` with `noValidate`:

- A row with `flex flex-wrap items-center gap-x-4 gap-y-2`: an `<h2>` in `text-h2`, "Send an enquiry", and beside it the page's one badge, "Free consultation". The badge sits on the card's pale grey, which is fine.
- With `mt-4`, one sentence in `text-body`: everything you send is read by a partner and kept confidential.
- With `mt-8`, the fields, `mt-8` apart, each in its own wrapper (`npx shadcn@latest add input textarea checkbox`):
  - "Your name": an `Input`, `autoComplete="name"`.
  - "Organisation (optional)": an `Input`, `autoComplete="organization"`.
  - A row with the email and the telephone: `md:grid md:grid-cols-2 md:gap-x-8`, each field in its own wrapper, the telephone's wrapper with `mt-8 md:mt-0`. On a phone the two stack. "Email address": an `Input` of type `email`, `autoComplete="email"`. "Telephone (optional)": an `Input` of type `tel`, `autoComplete="tel"`.
  - "What does the matter concern?": a native `<select>` from the layout notes, with the options `Choose an area` (empty value), `Corporate`, `Commercial disputes`, `Employment`, `Real estate`, `Private client` and `Something else`.
  - "Other parties involved (optional)": an `Input`. Under its label, with `mt-2`, a helper line: a `<p>` in `font-mono text-label text-muted-foreground`, not uppercased, "Names of the other companies or people, so that we can check for conflicts.", with the `id` `parties-help`, which the field's `aria-describedby` names at all times. Here the field has `mt-2` under the helper line, in place of the Inputs note's `mt-2` under the label.
  - "Tell us about the matter": a `Textarea` with `min-h-48`. Under its label, with `mt-2`, a helper line as above with the `id` `message-help`: "A few sentences: what happened, what you would like, and any deadline you know of." The textarea has `mt-2` under it and names it in its `aria-describedby` at all times.
  - The consent: a wrapper holding the checkbox row from the layout notes, and under the row, with `mt-2`, its error line while there is one. The `<label>`: "I agree that Harrow & Vane may use these details to reply to my enquiry and to check for conflicts of interest." Under the wrapper, with `mt-4`, a `<p>` in `font-mono text-label text-muted-foreground`, not uppercased: "How we handle your details is set out in our privacy notice.", where "privacy notice" is a link to `#`, styled as a link in running text.
- With `mt-8`, the default button "Send enquiry", `type="submit"`. The page's only default button.

### 4. Footer

The footer from the layout notes.

### On a phone

The office's details come first and the form after them, at the full width, 56px under. The email and telephone fields stack. The form keeps its `p-8`. Inputs and the select take the full width of the form. The badge wraps under the form's heading if the line is too short. The send button is `w-full md:w-auto`: full width on a phone, its own width from `md` up.

### Behavior

- **Sending:** check each field in code, in this order, and show one error per field, the first that applies, under that field. Empty name: "Write your name." Empty or malformed email (it must have the form `name@domain.tld`): "Write an email address such as name@example.com." A telephone that is not empty but holds anything other than digits, spaces and one leading `+`, or fewer than ten digits: "Write a telephone number such as 01632 960 214, or leave it empty." No area chosen: "Choose what the matter concerns." A message that is empty or under 20 characters once trimmed: "Tell us about the matter in a sentence or two." The box not ticked: "Tick the box so that we may reply to you." The organisation and the other parties are not checked. A field with a helper line names both lines in its `aria-describedby` while it has an error: the helper's `id` first, then the error's.
- **Focus:** focus moves to the first field with an error, after the error lines are rendered, as the Errors note says. For the consent, focus goes to the visible checkbox: hold a ref on the `Checkbox` and call `focus()` on it. The `Checkbox` gets `aria-invalid` and names its error line in `aria-describedby` while the error shows; ticking it removes both.
- **Confirmation:** when all is good, the form is replaced by the message in the status element, drawn as the Confirmations note says: an `<h2>` in `text-h2`, "Thank you. We have your enquiry", with `tabIndex={-1}` and focus moved to it; then with `mt-4` a sentence in `text-body` that repeats the area as chosen and the email address as written: "A partner in Commercial disputes will reply to ruth@example.com within one working day." For `Something else` it reads "A partner will reply to … within one working day." Then with `mt-4` a label line in `text-muted-foreground`: `Reference · HV-2611-0418`, a fixed sample reference. After the status element, in the same wrapper and only while the message shows, a `secondary` button with `mt-8`, "Send another enquiry", which empties the status element, brings back the empty form with no errors, and moves focus to the name field.
- Nothing is sent anywhere: the confirmation is shown in the page.

## 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`, `8`, `14`, `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. This theme has no full rounds: do not use `rounded-full`, and give a small mark a corner under half its height or none, because a box whose corner is half its height or more is a full round however it is written.
- **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 use gold once on a page, and let it mean something. The focus outline and a ticked checkbox do not count.
- Do leave wide margins: an empty column is part of the design.
- Do set names and titles in the heading font and the facts about them in labels.
- Don't set Marcellus in bold or italics: it has neither.
- Don't use gradients, glows or photographs with color casts.
- 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.

- [ ] Every field has a visible label; what the matter concerns is a native `<select>` whose first option is an empty prompt, and the consent is a checkbox whose label is tied to it.
- [ ] Sending the form empty shows a message in words with an icon under each required field and under the checkbox, sets `aria-invalid` on each, and moves focus to the first of them.
- [ ] A good enquiry is replaced by a confirmation in an element with the status role that was on the page from the start; the confirmation's heading takes focus, and it repeats the practice area and the email address as written.
- [ ] The address is in an `<address>` element with `not-italic`, and the office hours are a `<dl>` with each time in a `<time>` element.
- [ ] The page has exactly one badge, Free consultation, and nothing else is `bg-tertiary`; Send enquiry is the page's one default (navy) button, and nothing on the page is round, bold or italic.
- [ ] 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.
