# Build a law firm home page with the Counsel theme

You are building one page in an existing project: the home page of a commercial law firm, with a large opening statement, its practice areas, the firm in figures, three recent matters, four of its partners and a line to get in touch. 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 by Arthur Harrow and Eleanor Vane. One office, at 4 Chancery Row, Wexcombe WX1 2LN. 14 partners and 86 lawyers in five practice areas: Corporate, Commercial disputes, Employment, Real estate and Private client. Partners include Helena Marsh (managing partner, Corporate), David Achterberg (head of Commercial disputes), Priya Raman (Employment) and Ruth Calloway (Commercial disputes, made partner on 1 May 2026). Commercial disputes is ranked in the first band for the region by the Northern Legal Review, 2026, an invented directory. The main line is 01632 960 214, the enquiries address enquiries@harrowvane.example, and the office is open Monday to Friday 08:30–17:30. Every name, client and matter is invented: name no real firm, person, case or court ruling. Write the rest of the copy yourself, plain and exact, the way a careful letter reads: no superlatives, no "leading" or "world-class".

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:

- A label line in `text-muted-foreground`, columns 1–12 (`md:col-span-12`): `Commercial lawyers · Wexcombe, since 1952`, with `1952` in a `<time dateTime="1952">`.
- With `mt-4`, the `<h1>` in `text-h1 md:text-display`, columns 1–10 (`md:col-span-10`): "Clear advice for the decisions that shape a business". It runs over two or three lines on a large screen.
- 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: "We advise owner-managed companies, their boards and the families behind them, from a single office in Wexcombe. A partner reads every matter we take on, and stays with it to the end."
- With `mt-8`, in the same columns (`md:col-start-1 md:col-span-7`), an onward link from the layout notes: "Our practice areas", to `#practice-areas`.

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

### 3. Practice areas

A section with the `id` `practice-areas`, in the grid, with two parts:

- **Heading**, in columns 1–4 (`md:col-span-4`): an `<h2>` in `text-h2`, "Practice areas", then with `mt-4` one sentence in `text-body`: "Five teams, each led by partners who have practised in Wexcombe for most of their careers."
- **The list**, in columns 5–12 (`md:col-start-5 md:col-span-8`), with `mt-8 md:mt-0`: a `<ul>` as a list of entries (see the layout notes), five entries, each with `py-8`. From `md` up each `<li>` is also `md:grid md:grid-cols-8 md:items-baseline md:gap-x-8`, with two parts:
  - In columns 1–3 (`md:col-span-3`): the area's name as an `<h3>` in `text-h2` holding a link, styled as a name that is a link. Only Commercial disputes links to `/practice/commercial-disputes`; the other four point to `#`.
  - In columns 4–8 (`md:col-span-5`), with `mt-2 md:mt-0`: one sentence in `text-body`, at most `max-w-xl`, saying what the team does.

  Entries, in this order: Corporate (sales and purchases of companies, investment, shareholder agreements), Commercial disputes (contract and supply claims, warranty claims after a sale, urgent court orders), Employment (contracts, restrictive covenants, team moves, tribunal claims), Real estate (leases, development sites, the property side of a sale), Private client (wills, trusts and succession for business owners).

  In the Commercial disputes entry only, under its `<h3>` in the same grid item, a `<p>` with `mt-2` holding the page's one badge: "Ranked 2026". It is the page's one use of gold.

### 4. The firm in figures

A section in the grid:

- An `<h2>` in `text-h2`, columns 1–4 (`md:col-span-4`): "The firm in figures".
- With `mt-8`, across columns 1–12 (`md:col-start-1 md:col-span-12`), a `<dl>`. Here, instead of the Description lists note, it is a row of four figures: `grid grid-cols-2 gap-x-4 md:grid-cols-4 md:gap-x-8`, two by two on a phone. Each figure is a `<div>` with `border-t border-border pt-4`, and every `<div>` in the second row on a phone has `mt-8 md:mt-0` (the third and fourth). Inside each `<div>` the term comes first in the code and second on screen: the `<div>` is also `flex flex-col-reverse`; the `<dt>` is a label in `text-muted-foreground` with `mt-2`, the `<dd>` the figure in `font-heading text-h1`. Figures and terms: `1952` Founded (the `<dd>` holds a `<time dateTime="1952">`), `14` Partners, `86` Lawyers, `£310m` Transactions advised on in 2025.

### 5. Recent matters

A section that is not a grid:

- An `<h2>` in `text-h2`: "Recent matters".
- With `mt-8`, a `<ul>` as a list of entries, three entries, each with `py-8`. Each `<li>` is also `md:grid md:grid-cols-12 md:items-baseline md:gap-x-8`, with three parts:
  - Columns 1–3 (`md:col-span-3`): a label line in `text-muted-foreground`, the year and the practice area, `2026 · Corporate`, with the year in a `<time dateTime="2026">`.
  - Columns 4–10 (`md:col-span-7`), with `mt-2 md:mt-0`: a `<div>` holding the matter's name as an `<h3>` in `text-h2`, not a link, and with `mt-2` one sentence in `text-body`, at most `max-w-xl`.
  - Columns 11–12 (`md:col-span-2`), with `mt-2 md:mt-0`: the value, a `<p>` in `font-mono text-label uppercase tabular-nums text-foreground` with `md:text-right`.

  Entries, in this order: "Sale of Corran Marine" (`2026 · Corporate`, `£48m`): advised the founders of a boatbuilder on its sale to a Dutch shipping group. "Estley Mills v Brandreth Engineering" (`2026 · Commercial disputes`, `£6.2m`): won a claim for a textile mill, after a nine-day trial, over a production line that never ran to its specification. "Wexcombe University, Quay Street" (`2026 · Real estate`, `11,000 m²`): acted for the university on the 25-year lease of its new research building. In `11,000 m²` the unit keeps its case: put `m²` in a `<span>` with `normal-case`.

### 6. Partners

A section that is not a grid:

- A row with `flex flex-wrap items-baseline justify-between gap-x-8 gap-y-2`: an `<h2>` in `text-h2`, "Partners", and an onward link from the layout notes, "All 14 partners", pointing to `#`. Here the link's `<p>` is not below the list: it is the second item of this row, at its right end.
- With `mt-8`, a `<ul>` with `list-none` that is its own grid: `grid grid-cols-2 gap-x-4 gap-y-8 md:grid-cols-4 md:gap-x-8`. Here, instead of the Grid note's "never `gap-y-*`", this list of four people has `gap-y-8`, so the second row on a phone sits 32px under the first. Four `<li>`, each holding:
  - A portrait from the layout notes, with the person's initials.
  - With `mt-4`, the name as an `<h3>` in `text-h2` holding a link, styled as a name that is a link: Ruth Calloway links to `/people/ruth-calloway`, the others to `#`.
  - With `mt-2`, the role, a label line in `text-muted-foreground`.

  People, in this order: Helena Marsh (`HM`, `Managing partner · Corporate`), David Achterberg (`DA`, `Head of commercial disputes`), Priya Raman (`PR`, `Partner · Employment`), Ruth Calloway (`RC`, `Partner · Commercial disputes`). Each portrait's `aria-label` describes a photograph of that person in one sentence. At 375px a long name such as "David Achterberg" wraps onto two lines in its half of the row; that is fine.

### 7. Get in touch

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

- An `<h2>` in `text-h2`: "Talk to a partner".
- With `mt-4`, one or two sentences in `text-body`, at most `max-w-xl`: tell us briefly what the matter is, and a partner in the right team replies within one working day.
- With `mt-8`, a row with `flex flex-wrap items-center gap-x-8 gap-y-4`: the default button "Make an enquiry", an `<a>` styled with `buttonVariants` and the Buttons classes, to `/contact`, the page's one default button; and beside it a `<p>` label line: `Or call 01632 960 214`, where the number is a `tel:01632960214` link styled as a link in running text.

### 8. Footer

The footer from the layout notes.

### On a phone

Every part is one column. The `<h1>` is `text-h1` and wraps over four or five lines. In the practice areas each entry stacks: the name, the badge under Commercial disputes, then the sentence. The figures sit two by two. Each matter stacks: the label line, the name and sentence, then the value on its own line at the left. The partners sit two by two. The enquiry button and the phone line wrap onto two lines.

### Behavior

Nothing on this page has state. Links only: "Our practice areas" jumps to the practice areas section.

## 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.

- [ ] The `<h1>` is the page's only `text-display` text, from `md` up, and no heading or text on the page is bold or italic.
- [ ] The page has exactly one badge, Ranked 2026, on the Commercial disputes row, and nothing else on the page is `bg-tertiary`; there is exactly one default (navy) button, Make an enquiry.
- [ ] The five practice areas, the three matters and the four partners are lists, each entry with a 1px top border or, for the partners, a portrait; the figures are a `<dl>`.
- [ ] Every portrait is a rectangle with 4px corners, a hairline border and the person's initials, with an `aria-label`, and nothing on the page is round.
- [ ] Every section after the first is divided from the one before by one hairline, with equal white space above and below it.
- [ ] 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.
