# Build a lookbook page with the Atelier theme

You are building one page in an existing project: a season's lookbook for a fashion house, with numbered looks laid out large and uneven across the grid, each with a caption and credits, and a short text about the season. 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 Atelier variables in its global CSS. If not, run `npx shadcn@latest add https://strictthemes.com/r/atelier.json` and save https://strictthemes.com/themes/atelier/DESIGN.md as `DESIGN.md` in the project root. In a project without shadcn, use https://strictthemes.com/themes/atelier/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:** Bodoni Moda for headings, Hanken Grotesk for body text, Fragment Mono for labels and code.
- **Type scale** (size/weight; each class also sets line height and tracking): `text-display` 72px/500, `text-h1` 44px/500, `text-h2` 28px/500, `text-body` 16px/400, `text-label` 12px/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`), 64px (`16`), 128px (`32`).
- **Corners:** `rounded-none` 0px.
- **Colors (light mode):** primary #141414, on-primary #FAF8F5, secondary #E4DFD6, on-secondary #141414, tertiary #DDBBAA, on-tertiary #141414, neutral #68635C, background #FAF8F5, surface #F1EEE8, foreground #141414, border #D6D1C8, error #A32020.

### How Atelier pages are laid out

- **Container:** one `<div>` wraps the whole page and is the container: `mx-auto max-w-[1320px] 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; center nothing to fill them. 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.
- **Vertical rhythm:** written as a top margin on the lower element (`mt-*`), never as `space-y-*`, `gap-y-*`, a bottom margin or padding. Between a label and the one thing it names (a form label and its field, the "Size" line and its buttons, the house's name and the address in the footer): `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, or `mt-8` where this brief says so. Between blocks inside a section (a paragraph and a button, two figures, a form and the text above it): `mt-8`. Between a picture and its caption: `mt-2`. 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 caption line, a group of buttons) the space is the row's `gap-*` as this brief states it.
- **Sections:** `<main>` holds a stack of `<section>` elements. The first has `mt-16`. Every other section has `mt-16 md:mt-32`: 128px between sections on a large screen, 64px on a phone. That top margin is the whole gap; sections have no padding and no borders unless this brief gives one.
- **Header:** the `<header>` is itself the row, with `py-8`: `flex flex-col items-start gap-4 md:flex-row md:items-baseline md:justify-between`. On the left the house's name, 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: upright, with 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-8 gap-y-2` of three links as labels, in this order: "Collection", "House" and "Appointment", each to the page of that name. Each brief names the current one, if any. Below `md` the links sit under the name; there is no "Menu" button. The header has no border.
- **Footer:** a `<footer>` with `mt-16 md:mt-32`, a 1px `border-border` top border and `py-8`. Inside it a grid like the one above. Columns 1–4 (`md:col-span-4`): the house's name as plain text in `font-heading text-h2`, then the atelier's address: one `<div>` with `mt-2 font-mono text-label uppercase text-muted-foreground` holding two `<p>` lines with no margin between them, `7 rue Belvèze` and `69001 Lyon`. 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 three 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` with the copyright, e.g. `© 2026 Maison Ardelle`.
- **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 weight their type class gives them (500); add no weight class. Sentence case, no full stop.
- **Italic:** the heading font's italic is the one flourish. Put it on one word of a heading, or on one title (a collection's name), as an `<em>` inside the heading; `<em>` is italic on its own. At most one italic per heading and at most two per page. Never set a paragraph, a label or body text in italics: a collection's name in a paragraph, a caption or a label line stays upright, even where the sample italicizes it.
- **Labels:** nav links, captions, credits, dates, sizes, look numbers, 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. A unit keeps its case: put `cm` in a `<span>` with `normal-case`.
- **Label headings:** a heading that is styled as a label (Details, Care, Hours) 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 and form labels are `text-foreground`. A button's or a badge's text color comes from its variant or its classes. Caption lines after the first, credits, dates, `<dt>` terms and helper text are `text-muted-foreground`. Paragraphs are never muted. No other text colors; blush (`tertiary`) is never a text color.
- **Measure:** a paragraph of body text is at most `max-w-md` wide (448px), however many columns it sits in. A heading may run wider.
- **Figures and prices:** a price, a size, a time or a count that stands on its own is `font-mono tabular-nums`, in `text-label` as part of a caption and in `text-body` where this brief says so. Prices are written with the currency sign before the number and a comma for thousands: `€1,480`. A date or a time that stands on its own is a `<time>` with a `dateTime`: the opening hours and the years of a timeline and of a "Since" line. A `<time>` inside a sentence keeps the sentence's font. A date is written `Thursday 19 November 2026` (`dateTime="2026-11-19"`), a time `11:30`, a year `2014`. A year inside a name or a credit (`Autumn–Winter 2026`, `Tomas Reyl, 2026`, the copyright) is plain text.
- **Links:** a link in running text or in a caption 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 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`. Every link to a page of this site, a piece's page and the current page's own nav link included, points to `#`, unless the project has routes for these pages; the house's name always points to `/`. Any other link with no destination named is `#`.
- **Buttons:** the shadcn `Button` with `className="h-auto px-8 py-4 font-mono text-label font-normal uppercase"`: `font-normal` because the mono font has one weight. The default variant (black, ivory in dark mode) is for the page's main action. A second action is `variant="secondary"` (stone). 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. A quiet action is `variant="outline"`. 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 }), "…")`, because `buttonVariants` alone does not merge classes. Do not use the `xs`, `sm` or icon sizes.
- **Button groups:** a set of buttons where one is chosen (a size) is a `role="group"` with `flex flex-wrap gap-2`, named by `aria-labelledby` pointing at the visible label above it. Each button has `className="h-auto min-w-16 px-4 py-2 font-mono text-label font-normal uppercase tabular-nums"`. The chosen one has `aria-pressed="true"`, `variant="secondary"` and `border-foreground`; the rest have `aria-pressed="false"` and `variant="outline"`. The chosen value is also written out in words in the label line above the group, so the choice is never shown by fill alone.
- **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"`. They keep the fill they ship with, in dark mode too, 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: `Note (optional)`.
- **Selects:** a choice from a short list is a native `<select>` with `h-auto w-full 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 (`Choose a time`).
- **Date fields:** an `Input` of type `date`, with the Inputs classes. Its picker and the `<select>`'s list follow dark mode through the rules' `color-scheme` lines; add no class for it.
- **Errors:** an error is a line under its field with `mt-2`: `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`. 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. An error line and its field's `aria-invalid` are removed when that field changes (on `input` or `change`); an error that depends on a second field (a Saturday and its time) is also removed when that second field changes. Every other error stays until the next send.
- **Forms:** a form sits on the deeper paper: the `<form>` is `bg-card p-8`, with no border. The form and its confirmation are the only filled boxes on a page. 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, 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 `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`. 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:** at most one badge on a page, and only where this brief asks for it: the shadcn `Badge` (`npx shadcn@latest add badge`) with `className="h-auto rounded-none border-transparent bg-tertiary px-2 py-1 font-mono text-label font-normal uppercase text-tertiary-foreground"`. One or two words, no icon.
- **Pictures:** there are no image files. A picture is a plain `<div>` with an aspect ratio (`aspect-3/4`, `aspect-4/5`, `aspect-2/3`, `aspect-4/3`: Tailwind's ratio classes, not arbitrary values), `w-full`, a fill of `bg-secondary` unless this brief says `bg-card`, `role="img"` and an `aria-label` that describes the photograph in a sentence. Nothing is drawn inside it: no icon, no text, no gradient, no border. Every picture sits in a `<figure>` with a `<figcaption>`.
- **Caption below:** the `<figcaption>` comes under the picture with `mt-2`. It holds label lines: the first in `text-foreground` (a look number, a piece's name), the others in `text-muted-foreground` (materials, credits), each line a `<p>`, with `mt-1` between lines.
- **Caption beside:** where this brief puts a caption beside a picture, the `<figure>` spans all twelve columns (`md:col-span-12`) and is itself a grid from `md` up: `md:grid md:grid-cols-12 md:gap-x-8`. The picture and the caption take the columns this brief gives them, and the caption has `md:self-end md:mt-0`, so it sits at the foot of the picture. Where the caption is to the left of the picture, give both `md:row-start-1`. Below `md` the caption sits under the picture with `mt-2`, as any caption.
- **Lists of entries:** a list where this brief gives each item a 1px top border (a timeline, a list of workshops) is a `<ul>` or `<ol>` with `list-none`, no padding, and no space between its items; each item 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 `<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.
- **Rules:** every line is 1px and `border-border`. No shadows, no blur, no gradients, no colored backgrounds behind sections.
- **Corners:** square everywhere. The stock components are already square in this theme; `rounded-none` is the only corner class, and it is needed only where these notes give it.
- **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.
- **Motion:** in the code you write, the only transition is color. Nothing moves on scroll.

## The page

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

Sample house, if the user gave none: **Maison Ardelle**, a small fashion house in Lyon founded in 2014 by Inès Ardelle. This page is its lookbook for Autumn–Winter 2026, called *Still Water*: seven looks in undyed wool, black linen and silk the color of skin, shot in the empty Bains du Rhône swimming baths in Lyon in September 2026. Photography by Tomas Reyl, styling by Anouk Delmas, casting by Petra Laine, hair and make-up by Sami Okafor. The models are Yara Benali, Marit Ekwall and Joon Seo. Pieces include the Lune wrap coat (€1,480), the Ilse pleated skirt (€640), the Mira silk shirt (€520), the Sorel wide trousers (€590), the Nadia knitted dress (€760), the Tove belted cardigan (€540) and the Ren linen shirt-dress (€610).

Build these parts in order.

### 1. Header

The header from the layout notes. "Collection" is the current page.

### 2. The season

The first section, in the grid:

- A label line in `text-muted-foreground`, across columns 1–12 (`md:col-span-12`): `Autumn–Winter 2026 · 7 looks`.
- With `mt-4`, the `<h1>` in `text-h1 md:text-display`, columns 1–8 (`md:col-span-8`): the collection's name, "*Still Water*", the whole name in an `<em>`. This is the page's one italic.
- With `mt-8`, the season's text, one `<div>` in columns 5–9 (`md:col-start-5 md:col-span-5`), which overlap the `<h1>`'s, so it starts a row under it; each paragraph at most `max-w-md`: two short paragraphs in `text-body`, the second with `mt-4`. Say where the clothes were shot, what they are made of and one thing that is new this season.

### 3. The looks

A section whose content is an `<ol>` with `list-none` that is itself the grid (`grid grid-cols-1 md:grid-cols-12 md:gap-x-8`). Each look is an `<li>` holding a `<figure>`; the margin is on the `<li>`, and the `<figure>` has none. Look 01 has no top margin at any width: the section's own margin is the gap above it. Every look after it has `mt-16` on a phone; the margin from `md` up is given for each. Lay them out exactly like this:

| Look | Columns of the `<li>` | Top margin from `md` | Picture | Caption |
|---|---|---|---|---|
| 01 | `md:col-span-12` | none | columns 1–8, `aspect-4/5` | beside, columns 9–11 |
| 02 | `md:col-start-2 md:col-span-4` | `md:mt-32` | `aspect-3/4` | below |
| 03 | `md:col-start-8 md:col-span-5` | `md:mt-16` | `aspect-2/3`, `bg-card` | below |
| 04 | `md:col-span-12` | `md:mt-32` | columns 5–12, `aspect-4/3` | beside, columns 2–4, on the left |
| 05 | `md:col-start-1 md:col-span-5` | `md:mt-32` | `aspect-3/4` | below |
| 06 | `md:col-start-7 md:col-span-3` | `md:mt-32` | `aspect-2/3`, `bg-card` | below |
| 07 | `md:col-start-10 md:col-span-3` | `md:mt-16` | `aspect-3/4` | below |

This table is for you, not for the page. Looks 01 and 04 use the caption-beside layout from the layout notes, where the `<figure>` is itself a 12-column grid: for look 01 the picture is `md:col-span-8` and the caption `md:col-start-9 md:col-span-3`; for look 04 the picture is `md:col-start-5 md:col-span-8` and the caption `md:col-start-2 md:col-span-3`, both with `md:row-start-1`. Looks 02 and 03 share a row, and so do 05, 06 and 07: the top margins make each row uneven. A margin counts from the foot of the row above, which ends at the foot of its tallest look, and it is the whole gap: under look 01, look 02 starts 128px lower and look 03 64px lower. Pictures are `bg-secondary` unless the table says `bg-card`.

Each caption has three or four label lines, as the layout notes describe:

- The look number in `text-foreground`: `Look 03`.
- The pieces it shows in `text-muted-foreground`, joined with ` · `: `Lune wrap coat · Mira silk shirt`. In look 03, "Lune wrap coat" is a link to the piece page (`#`, as the Links note says), styled as a link in a caption and kept in `text-foreground`. The other pieces are plain text.
- The credit in `text-muted-foreground`: `Worn by Marit Ekwall`.
- For the two looks with the caption beside the picture, a fourth line in `text-muted-foreground` with what the pieces are made of. Look 01: `Lune wrap coat · Sorel wide trousers`, `Worn by Yara Benali`, `Undyed wool · Black linen`. Look 04: `Ren linen shirt-dress · Tove belted cardigan`, `Worn by Joon Seo`, `Black linen · Undyed wool`.

Each picture's `aria-label` describes that photograph in one sentence: the clothes, the pose, the room. The look number is in it too ("Look 03: …").

### 4. Credits

A section in the grid, with its content in columns 5–9 (`md:col-start-5 md:col-span-5`): a label heading "Credits", an `<h2>`, then with `mt-4` a `<dl>` as the layout notes describe it, with six rows: Photography, Styling, Casting, Hair and make-up, Models, Location (`Bains du Rhône, Lyon`). The models' names are one `<dd>`, joined with commas. Then with `mt-8` a `<p>` holding a link "Book a fitting" to the appointment page, in `font-mono text-label uppercase` and styled as a link in running text (the margin is on the `<p>`: a link is inline).

### 5. Footer

The footer from the layout notes.

### On a phone

Every look is one column at the full width, `mt-16` under the one before, with its caption under its picture. The pictures keep their ratios, so the page is long and the rhythm comes from their different heights. The season's text and the credits take the full width.

### Behavior

Nothing on this page has state. Links only.

## 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`, `16`, `32`) 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 give one image or one heading the whole screen.
- Do set captions and credits small, in the mono font, and let them be found.
- Do use the italic for one word or one title at a time.
- Don't round a corner, add a shadow or tint a background to make something "pop".
- Don't set long text in Bodoni: below 28px its hairlines break up.
- 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 looks are numbered in order, `Look 01` to `Look 07`, and no two pictures that share a row from `md` up have the same width and the same top edge.
- [ ] Two looks have their caption beside the picture from `md` up, one on the right and one on the left; on a phone every caption sits under its picture.
- [ ] Every picture has an `aria-label` that describes the photograph, and every look's caption names the pieces and gives a credit.
- [ ] The credits for the whole shoot are a `<dl>`, and the season's text is at most `max-w-md` wide.
- [ ] 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.
