# Icons: agent spec

Twin of `/icons`. Source wins: `app/assets/icons/motive/`, the components below.

## Set

22 icons in `app/assets/icons/motive/`, the `motive` icon collection (`nuxt.config.ts`). Each is on a `0 0 512 512` viewBox, except `motive-unknown` (a square around the mark, see `/agent/logo.md`). Render with `BrandIcon` (`::brand-icon{name}`) or the icon string `i-motive-<name>`.

`/icons` shows two grids. Do not sub-divide either.

| Grid | Icons, in page order |
|---|---|
| Mark and verticals (4) | `motive-unknown`, `recorded-music`, `music-tech`, `retail` |
| Actions and services (18) | `paid-media`, `crm`, `ecommerce`, `project-management`, `positioning`, `strategy`, `creative-messaging`, `seo-geo`, `consulting`, `conversion`, `reporting`, `social-media`, `distribution`, `artist`, `label`, `saas`, `creator-tools`, `network` |

- The 18 are tags: the work Motive Unknown does, and the kinds of client it does it for.
- `motive-unknown` is the mark. `pnpm marks:import` writes it, not the icon import (see `/agent/logo.md`).

## Components

| Component | Props | Render |
|---|---|---|
| `IconTile` / `:::icon-tile` | `name`, `label`, `optical` (scales the icon 160%, so it reads as large as the verticals; for `motive-unknown`: its square holds more air, so it reads small beside the verticals) | One icon on a flat `bg-elevated` `aspect-square` tile, `p-8`, icon `size="full"` in `text-highlighted`. |
| `IconPairTile` / `:::icon-pair-tile` | `first`, `second`, `label` | Two icons side by side (`grid-cols-2`, no gap, each moved 8px inward) on one `aspect-square` tile, `p-8`, one grid column. |
| `BrandIcon` / `::brand-icon` | `name`, `size?: sm\|md\|lg\|full\|auto` | `sm` `size-5`, `md` `size-8` (default), `lg` `size-12`, `full` `size-full!`, `auto` `h-full! w-auto! aspect-square`. |

Both tiles render through `TileFrame`: no border, the label as body text under the tile. Put them in `::grid{columns="4"}`.

## Rules

1. Every icon is one solid shape in one colour (`brand/ink`). No second colour, no gradient, no outline variant.
2. Import icons with `scripts/import-icons.py`, not by hand. Its source is `brand-assets/icons/svg/` in the Production Dropbox. It removes the exporter’s `<g id="Grid">` guides and adds `fill="currentColor"`.
3. Every SVG in `app/assets/icons/motive/` has `fill="currentColor"` on its root, so Nuxt Icon can recolour it.
4. `motive-unknown` is not a tag. In advertising it may pair with another icon. In B2B and client work, never pair it: there it sits in the fixed parts of the report, presentation or website (header, footer, cover). Never use it to label a client type or a service.
5. Always show the label. The icon takes `aria-hidden`, and the caption carries the name.
6. Keep every icon box `aspect-square`, so a two-line caption does not move the row’s boxes.

Suggestion, not a rule: a pair reads best as one service with one vertical or kind of client, and two icons usually say enough. Do not pair two services (the page's Don't).

## Craft rules (from `better-ui`)

1. Use one SVG per icon, recoloured by CSS for each state. Never a second asset for a state.
2. Beside text at weight 500, use a 2px stroke. This applies to Lucide icons; the `motive` set is solid.
3. Use Lucide for interface icons and `motive` for brand subjects. Do not mix two stroke styles in one row.
4. For a set with both variants, outline is the default and fill marks the active state.
5. Align asymmetric glyphs optically, not geometrically.
6. Animate an icon change with scale `0.25 → 1`, opacity `0 → 1`, blur `4px → 0`. Use `springBase` from `motion-tokens.ts`, or a cross-fade on `--ease-standard` without the library.
7. Give an icon-only control an `aria-label`. Set an icon that repeats adjacent text `aria-hidden="true"`.

## Related

- `/agent/logo.md`: the mark.
- `/agent/layout.md`: tiles and the `better-accessibility` floor.
- `/agent/colour.md`: `brand/ink`.
- `/agent/motion.md`: icon state transitions.
