diff --git a/.agents/skills/animate/RECIPES.md b/.agents/skills/animate/RECIPES.md
new file mode 100644
index 0000000..6744891
--- /dev/null
+++ b/.agents/skills/animate/RECIPES.md
@@ -0,0 +1,324 @@
+# Animation Recipes
+
+Ready-to-build implementations for the cases that come up most. Start from the recipe, then adapt — don't rebuild from scratch.
+
+Curves are the `--ease-out`, `--ease-in-out`, and `--ease-drawer` tokens defined in SKILL.md.
+
+---
+
+## Button press
+
+Any pressable element. Instant feedback that the interface heard the user.
+
+```css
+.button {
+ transition: transform 160ms var(--ease-out);
+}
+
+.button:active {
+ transform: scale(0.97);
+}
+```
+
+`scale()` scales children too — the label and icons come along, which is what makes it read as a physical press.
+
+No hover gating needed here: `:active` is a real press on touch. Gate any `:hover` styling separately.
+
+---
+
+## Dropdown, popover, menu, select
+
+Scales out of its trigger, not out of thin air.
+
+```css
+.popover {
+ transform-origin: var(--transform-origin); /* Base UI supplies this */
+ transition:
+ opacity 200ms var(--ease-out),
+ transform 200ms var(--ease-out);
+}
+
+.popover[data-starting-style],
+.popover[data-ending-style] {
+ opacity: 0;
+ transform: scale(0.95);
+}
+```
+
+The `transform-origin` is the whole point — the panel should look like it came out of the thing you clicked.
+
+---
+
+## Tooltip
+
+Same shape as a popover, faster, plus the detail most implementations miss.
+
+```css
+.tooltip {
+ transform-origin: var(--transform-origin);
+ transition:
+ transform 125ms var(--ease-out),
+ opacity 125ms var(--ease-out);
+}
+
+.tooltip[data-starting-style],
+.tooltip[data-ending-style] {
+ opacity: 0;
+ transform: scale(0.97);
+}
+
+/* Once one tooltip is open, neighbours open instantly */
+.tooltip[data-instant] {
+ transition-duration: 0ms;
+}
+```
+
+The initial delay prevents accidental activation. After that, skipping both the delay and the animation makes the whole toolbar feel faster.
+
+---
+
+## Modal
+
+The one popover that stays centered.
+
+```css
+.modal {
+ transform-origin: center; /* exempt — not anchored to a trigger */
+ transition:
+ opacity 250ms var(--ease-out),
+ transform 250ms var(--ease-out);
+}
+
+.modal[data-starting-style],
+.modal[data-ending-style] {
+ opacity: 0;
+ transform: scale(0.96);
+}
+
+.backdrop {
+ transition: opacity 250ms var(--ease-out);
+}
+```
+
+Animate the backdrop's opacity alongside it so they read as one surface.
+
+---
+
+## Drawer / sheet
+
+```css
+.drawer {
+ transform: translateY(0);
+ transition: transform 500ms var(--ease-drawer);
+}
+
+.drawer[data-closed] {
+ transform: translateY(100%);
+}
+```
+
+This is how Vaul hides a drawer before animating it in.
+
+Add drag and it becomes a gesture problem — see **Drag to dismiss** below.
+
+---
+
+## Toast
+
+```css
+.toast {
+ opacity: 1;
+ transform: translateY(0);
+ transition:
+ opacity 400ms ease,
+ transform 400ms ease;
+
+ @starting-style {
+ opacity: 0;
+ transform: translateY(100%);
+ }
+}
+```
+
+- `ease` rather than `ease-out`, slightly slower than typical UI: Sonner reads as elegant partly because its motion is tuned to the component's personality rather than to the generic UI budget.
+- If `@starting-style` isn't available, fall back to the mount flag:
+
+```jsx
+useEffect(() => { setMounted(true); }, []);
+//
+```
+
+When toasts stack and the list reflows, the opacity change has to work against the height change. There's no formula for that pair — adjust until it feels right, then check it again the next day.
+
+---
+
+## Accordion / collapse
+
+```css
+.content {
+ overflow: hidden;
+ transition:
+ height 200ms var(--ease-out),
+ opacity 200ms var(--ease-out);
+}
+```
+
+Keep it short — this is one of the few animations that costs layout on every frame, so a long duration is expensive as well as sluggish. Measure the content height in JS (or use a headless primitive that supplies it) rather than animating to `auto`.
+
+---
+
+## Stagger a group entrance
+
+For a list or grid the user sees occasionally — not for a list they scroll past all day.
+
+```css
+.item {
+ opacity: 0;
+ transform: translateY(8px);
+ animation: fadeIn 300ms var(--ease-out) forwards;
+}
+
+.item:nth-child(2) { animation-delay: 50ms; }
+.item:nth-child(3) { animation-delay: 100ms; }
+.item:nth-child(4) { animation-delay: 150ms; }
+
+@keyframes fadeIn {
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+```
+
+Stagger is decorative — it must never block interaction while it plays.
+
+---
+
+## Hold to confirm
+
+For destructive actions where a plain click is too easy to fire by accident.
+
+```css
+.overlay {
+ clip-path: inset(0 100% 0 0);
+ transition: clip-path 200ms var(--ease-out); /* release: snappy */
+}
+
+.button:active .overlay {
+ clip-path: inset(0 0 0 0);
+ transition: clip-path 2s linear; /* press: slow and deliberate */
+}
+
+.button:active {
+ transform: scale(0.97);
+}
+```
+
+`linear` is correct here — the fill is a progress indicator, and progress shouldn't ease.
+
+---
+
+## Tab indicator with a color transition
+
+Timing individual color transitions across a tab list never quite lands. Clip instead.
+
+Duplicate the tab list. Style the copy as the active state — different background, different text color. Clip the copy so only the active tab shows, and animate the clip on change:
+
+```css
+.tabs-active-copy {
+ clip-path: inset(0 60% 0 20%); /* driven by the active tab's position */
+ transition: clip-path 250ms var(--ease-in-out);
+}
+```
+
+The text and background change together, in perfect sync, because they're one element being revealed rather than two colors being interpolated.
+
+---
+
+## Scroll reveal
+
+Marketing surfaces only. Don't do this to functional UI a user visits daily.
+
+```css
+.reveal {
+ clip-path: inset(0 0 100% 0);
+ transition: clip-path 600ms var(--ease-in-out);
+}
+
+.reveal[data-visible] {
+ clip-path: inset(0 0 0 0);
+}
+```
+
+Trigger with `IntersectionObserver`, or Motion's `useInView` with `{ once: true, margin: "-100px" }`. Fire it once — re-animating on every scroll-by is an interface fighting its reader.
+
+---
+
+## Drag to dismiss
+
+The gesture recipe. Springs, not durations, because the user can reverse mid-motion.
+
+```js
+// Dismiss on a flick, not just on distance
+const timeTaken = Date.now() - dragStartTime.current;
+const velocity = Math.abs(swipeAmount) / timeTaken;
+
+if (Math.abs(swipeAmount) >= SWIPE_THRESHOLD || velocity > 0.11) {
+ dismiss();
+}
+```
+
+```js
+// Set transform on the dragged element directly.
+// Driving it through a CSS variable on the parent recalcs styles for every child.
+element.style.transform = `translateY(${distance}px)`;
+```
+
+Four details that separate a good drag from a bad one:
+
+- **Pointer capture** once the drag starts, so it continues when the pointer leaves the element's bounds.
+- **Multi-touch protection** — `if (isDragging) return` on new touch points, or switching fingers mid-drag makes the element jump.
+- **Damping past boundaries** — dragging beyond a natural edge moves the element less the further it goes. Real things slow before they stop.
+- **Friction, not a wall** — allow the over-drag with rising resistance rather than refusing it.
+
+Settle with a spring so an interrupted drag keeps its velocity:
+
+```js
+{ type: "spring", duration: 0.5, bounce: 0.2 }
+```
+
+---
+
+## Masking a crossfade that won't settle
+
+When two states overlap visibly during a transition and no amount of easing or duration tuning fixes it, blur the seam:
+
+```css
+.content {
+ transition:
+ filter 200ms ease,
+ opacity 200ms ease;
+}
+
+.content.transitioning {
+ filter: blur(2px);
+ opacity: 0.7;
+}
+```
+
+Without blur the eye reads two distinct objects swapping. Blur blends them into one perceived transformation. Keep it under 20px — heavy blur is expensive, especially in Safari.
+
+---
+
+## Programmatic, without a library
+
+When the motion needs JS control but not a dependency, WAAPI gives you CSS-grade performance:
+
+```js
+element.animate(
+ [{ clipPath: 'inset(0 0 100% 0)' }, { clipPath: 'inset(0 0 0 0)' }],
+ { duration: 1000, fill: 'forwards', easing: 'cubic-bezier(0.77, 0, 0.175, 1)' }
+);
+```
+
+Hardware-accelerated, interruptible, no bundle cost.
diff --git a/.agents/skills/animate/SKILL.md b/.agents/skills/animate/SKILL.md
new file mode 100644
index 0000000..159fe07
--- /dev/null
+++ b/.agents/skills/animate/SKILL.md
@@ -0,0 +1,199 @@
+---
+name: animate
+description: Build an animation from scratch, making the decisions in the order that determines whether it feels right — should it animate at all, what purpose, which tool, which properties, which curve and duration, how it interrupts, how it exits. Writes the implementation. Use when asked to animate something, add motion, make a component feel alive, or build a transition. For critiquing existing motion use review-animations; for auditing a whole codebase use improve-animations.
+---
+
+# Building Animations
+
+A construction skill. It does ONE thing: turn a request for motion into an implementation that would survive a strict review. It does not audit a codebase (that's `improve-animations`), critique a diff (that's `review-animations`), hunt for places that could animate (that's `find-animation-opportunities`), or build for React Native (that's `animate-expo`).
+
+## Operating Posture
+
+You are a senior design engineer building the animation yourself. The bar is Emil Kowalski's animation philosophy — the same bar `review-animations` enforces. Write it so it passes that review the first time.
+
+Two failure modes, and the first is worse:
+
+1. **Animating something that shouldn't animate.** The gate below exists to produce zero lines of code sometimes. That's a success, not a dodge.
+2. **Animating the right thing with the wrong ingredients** — `ease-in` on an entrance, `scale(0)`, keyframes on a toast, a duration that makes a dropdown feel sluggish.
+
+Never present motion options as a menu. Make the call, state the reasoning in one line, write the code.
+
+## Hard Rules
+
+1. **Run the sequence in order.** Steps 1 and 2 gate everything. Don't reach for a curve before you know whether it animates at all.
+2. **No approximated values.** Every curve, duration, and spring config comes from the tables below. Never invent `cubic-bezier(0.4, 0, 0.2, 1)` because it looks familiar.
+3. **Extend the codebase's tokens, don't fork them.** If `--ease-out` or a duration scale already exists, use it. Adding a parallel system is a defect.
+4. **Reduced motion and hover gating ship with the animation**, not as a follow-up.
+5. **Cheapest tool that works.** Don't install a motion library for a fade.
+
+## The Build Sequence
+
+### 1. Should this animate at all?
+
+| Frequency | Decision |
+| --- | --- |
+| 100+ times/day (keyboard shortcuts, command palette toggle) | **No animation. Ever.** Stop here. |
+| Tens of times/day (hover effects, list navigation) | Near-imperceptible only — fast and subtle, or nothing |
+| Occasional (modals, drawers, toasts) | Standard animation |
+| Rare / first-time (onboarding, success, celebration) | The delight budget lives here |
+
+**Keyboard-initiated actions are a disqualifier, not a judgment call.** Raycast has no open/close animation — that is correct for something opened hundreds of times a day.
+
+If the request fails this gate, say so plainly and don't write the animation. Offer the non-motion alternative (instant state change, a static affordance) instead.
+
+### 2. What is the purpose?
+
+Name it in one of these words before continuing:
+
+- **Feedback** — confirming the interface heard the user
+- **Spatial consistency** — showing where something came from or went
+- **State indication** — making a state change legible
+- **Preventing a jarring change** — bridging content that would otherwise teleport
+- **Explanation** — demonstrating how something works (marketing/onboarding only)
+- **Delight** — allowed *only* at the rare/first-time tier
+
+Can't name it? Don't build it. "It looks cool" on a frequently-seen element is a reason to stop.
+
+Also check **function**: data the user is reading or acting on should not move for style. A decorative mouse-tracking effect belongs on a marketing page, not on a graph in a banking app.
+
+### 3. Pick the tool — cheapest that works
+
+Walk down; stop at the first that fits.
+
+| Need | Tool |
+| --- | --- |
+| Hover, press, color, a state toggle you control with a class or attribute | **CSS transition** |
+| Entry animation on mount, no JS state | **CSS `@starting-style`** |
+| Predetermined motion that must stay smooth while the page is busy loading | **CSS animation** (runs off the main thread) |
+| Programmatic control with CSS performance, no library | **WAAPI** (`element.animate()`) |
+| Springs, layout animations, exit animations, gesture-driven values | **Motion** (`motion.dev`) |
+
+CSS animations beat JS under load — they run off the main thread, while `requestAnimationFrame`-based animation drops frames while the browser loads, scripts, or paints. Use CSS for predetermined motion, JS for dynamic and interruptible motion.
+
+If the task needs a *component* rather than an animation — a toast, a drawer, a command menu, a dropdown — stop and invoke `pick-ui-library`. Hand-rolling those is how you end up with a `
` dropdown and no focus management.
+
+### 4. Pick the properties
+
+- **`transform` and `opacity` only.** They skip layout and paint and run on the GPU. `width`/`height`/`margin`/`padding`/`top`/`left` trigger all three. (`clip-path` is the sanctioned fourth — see RECIPES.md. `height` is tolerated only for accordions, where there's no transform equivalent.)
+- **Never `scale(0)`.** Start from `scale(0.9–0.97)` + `opacity: 0`. Nothing in the real world appears from nothing.
+- **`transform-origin` at the trigger** for popovers, dropdowns, menus, tooltips — `var(--transform-origin)` in Base UI. **Modals are exempt**; they're not anchored to a trigger, so they stay centered.
+- **Percentages in `translate()`** are relative to the element's own size — `translateY(100%)` moves by its own height whatever the content. Prefer over hardcoded pixels.
+- **In Motion, use the full transform string.** `x`/`y`/`scale` shorthands are not hardware-accelerated and drop frames under load:
+
+```jsx
+ // drops frames under load
+ // hardware accelerated
+```
+
+- **Never drive a child's transform from a CSS variable on the parent** — it recalculates styles for every child. Set `transform` on the element directly.
+
+### 5. Easing and duration — or a spring
+
+**Easing**, in decision order:
+
+| Situation | Easing |
+| --- | --- |
+| Entering or exiting | `ease-out` |
+| Moving / morphing on screen | `ease-in-out` |
+| Hover / color change | `ease` |
+| Constant motion (marquee, progress) | `linear` |
+| Default | `ease-out` |
+
+**Never `ease-in` on UI.** It starts slow, delaying the exact moment the user is watching. `ease-out` at 200ms *feels* faster than `ease-in` at 200ms.
+
+Built-in CSS easings are too weak. Use these:
+
+```css
+--ease-out: cubic-bezier(0.23, 1, 0.32, 1); /* strong ease-out for UI */
+--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1); /* strong ease-in-out for on-screen movement */
+--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* iOS-like drawer curve (Ionic) */
+```
+
+Need a curve that isn't here? Take it from [easing.dev](https://easing.dev/) or [easings.co](https://easings.co/). Don't hand-roll one.
+
+**Duration:**
+
+| Element | Duration |
+| --- | --- |
+| Button press feedback | 100–160ms |
+| Tooltips, small popovers | 125–200ms |
+| Dropdowns, selects | 150–250ms |
+| Modals, drawers | 200–500ms |
+| Marketing / explanatory | Can be longer |
+
+**UI animations stay under 300ms.** A 180ms dropdown feels more responsive than a 400ms one.
+
+**Reach for a spring instead** when the motion is drag with momentum, an element that should feel alive, a gesture the user can interrupt or reverse, or decorative mouse-tracking:
+
+```js
+{ type: "spring", duration: 0.5, bounce: 0.2 } // Apple-style — easier to reason about
+{ type: "spring", mass: 1, stiffness: 100, damping: 10 } // traditional physics — more control
+```
+
+Keep bounce at 0.1–0.3, and avoid bounce in most UI — reserve it for drag-to-dismiss and playful interactions.
+
+### 6. Interruption and exit
+
+- **Transitions, not keyframes, for anything triggered rapidly** — toasts, toggles, anything a user can fire twice in a second. Transitions retarget from the current value; keyframes restart from zero.
+- **Springs for gestures**, because they carry velocity through an interruption.
+- **Exit the way it entered.** A toast that slides in from the bottom leaves through the bottom. Symmetric paths are what make swipe-to-dismiss feel obvious.
+- **Asymmetric timing where the user is deciding.** Slow on the deliberate phase (a hold-to-confirm press: 2s linear), snappy on the system response (release: 200ms ease-out).
+
+### 7. Reduced motion and pointer gating
+
+Ships with the animation, every time.
+
+```css
+@media (prefers-reduced-motion: reduce) {
+ .element { animation: fade 0.2s ease; } /* keep opacity/color, drop transform-based motion */
+}
+
+@media (hover: hover) and (pointer: fine) {
+ .element:hover { transform: scale(1.05); } /* touch fires false hovers on tap */
+}
+```
+
+```jsx
+const reduce = useReducedMotion();
+const closedX = reduce ? 0 : '-100%';
+```
+
+Reduced motion means **fewer and gentler** animations, not zero — keep transitions that aid comprehension, remove movement and position changes.
+
+## Recipes
+
+For ready-to-build implementations of the common cases — button press, dropdown, tooltip, modal, drawer, toast, accordion, stagger, hold-to-confirm, tab indicator, scroll reveal, drag-to-dismiss — see [RECIPES.md](RECIPES.md). Load it whenever the request matches one of those components; start from the recipe rather than from a blank file.
+
+## Never Ship
+
+Self-check before you finish. Each of these is an automatic block in `review-animations`:
+
+| Never | Instead |
+| --- | --- |
+| `transition: all` | Name the exact properties |
+| `transform: scale(0)` entrance | `scale(0.95)` + `opacity: 0` |
+| `ease-in` on a UI element | `ease-out` or a strong custom curve |
+| Built-in `ease-out` on a deliberate animation | `cubic-bezier(0.23, 1, 0.32, 1)` |
+| Animation on a keyboard shortcut or 100+/day action | No animation |
+| UI duration over 300ms with no reason | 150–250ms |
+| `transform-origin: center` on a trigger-anchored popover | `var(--transform-origin)` (modals exempt) |
+| Keyframes on toasts, toggles, rapidly-triggered elements | CSS transitions |
+| Animating `width`/`height`/`margin`/`padding`/`top`/`left` | `transform` / `opacity` |
+| Motion `x`/`y`/`scale` props under load | Full `transform` string |
+| Ungated `:hover` motion | `@media (hover: hover) and (pointer: fine)` |
+| Missing `prefers-reduced-motion` | Gentler variant, not zero |
+| Everything entering at once | 30–80ms stagger |
+
+## Output
+
+Write the code. Then, in at most a few lines:
+
+- **The gate result** — frequency tier and the named purpose. If something in the request was rejected, say which and why.
+- **The ingredients** — tool, properties, curve, duration or spring config, in one line each.
+- **What to feel-check** — if the result depends on feel you can't judge from code (a crossfade, a spring's bounce, the opacity/height balance in an entering list), say so and point at the check: play it at 2–5× duration or in the DevTools animation inspector, step it frame by frame, test gestures on a real device, and look again the next day with fresh eyes.
+
+Don't pad this into a report. The code is the deliverable.
+
+## Tone
+
+Opinionated and brief. When the honest answer is "this shouldn't animate," give it — that answer is the reason this skill exists. When feel genuinely can't be settled from code, say so instead of guessing at a value.
diff --git a/.agents/skills/design-taste-frontend/SKILL.md b/.agents/skills/design-taste-frontend/SKILL.md
new file mode 100644
index 0000000..b72132f
--- /dev/null
+++ b/.agents/skills/design-taste-frontend/SKILL.md
@@ -0,0 +1,1206 @@
+---
+name: design-taste-frontend
+description: Anti-slop frontend skill for landing pages, portfolios, and redesigns. The agent reads the brief, infers the right design direction, and ships interfaces that do not look templated. Real design systems when applicable, audit-first on redesigns, strict pre-flight check.
+---
+
+# tasteskill: Anti-Slop Frontend Skill
+
+> Landing pages, portfolios, and redesigns. Not dashboards, not data tables, not multi-step product UI.
+> Every rule below is **contextual**. None of it fires automatically. First read the brief, then pull only what fits.
+
+---
+
+## 0. BRIEF INFERENCE (Read the Room Before Anything Else)
+
+Before touching code or tweaking dials, **infer what the user actually wants**. Most LLM design output is bad because the model jumps to a default aesthetic instead of reading the room.
+
+### 0.A Read these signals first
+1. **Page kind** - landing (SaaS / consumer / agency / event), portfolio (dev / designer / creative studio), redesign (preserve vs overhaul), editorial / blog.
+2. **Vibe words** the user used - "minimalist", "calm", "Linear-style", "Awwwards", "brutalist", "premium consumer", "Apple-y", "playful", "serious B2B", "editorial", "agency-y", "glassy", "dark tech".
+3. **Reference signals** - URLs they linked, screenshots they pasted, products they named, brands they're competing with.
+4. **Audience** - B2B procurement panel vs. design-conscious consumer vs. recruiter scanning a portfolio. The audience picks the aesthetic, not your taste.
+5. **Brand assets that already exist** - logo, color, type, photography. For redesigns, these are starting material, not optional input (see Section 11).
+6. **Quiet constraints** - accessibility-first audiences, public-sector, regulated industries, trust-first commerce, kids' products. These constraints OVERRIDE aesthetic preference.
+
+### 0.B Output a one-line "Design Read" before generating
+Before any code, state in one line: **"Reading this as: \ for \, with a \ language, leaning toward \."**
+
+Example reads:
+- *"Reading this as: B2B SaaS landing for technical buyers, with a Linear-style minimalist language, leaning toward Tailwind utilities + Geist + restrained motion."*
+- *"Reading this as: solo designer portfolio for hiring managers, with an editorial / kinetic-type language, leaning toward native CSS + scroll-driven animation + custom typography."*
+- *"Reading this as: redesign of a public-sector service site, with a trust-first language, leaning toward GOV.UK Frontend or USWDS."*
+
+### 0.C If the brief is ambiguous, ask one question, do not guess
+Ask exactly **one** clarifying question - never a multi-question dump - and only when the design read genuinely diverges. Example: *"Should this feel closer to Linear-clean or Awwwards-experimental?"*
+
+If you can confidently infer from context, **do not ask**. Just declare the design read and proceed.
+
+### 0.D Anti-Default Discipline
+Do not default to: AI-purple gradients, centered hero over dark mesh, three equal feature cards, generic glassmorphism on everything, infinite-loop micro-animations everywhere, Inter + slate-900. These are the LLM defaults. Reach past them deliberately based on the design read.
+
+---
+
+## 1. THE THREE DIALS (Core Configuration)
+
+After the design read, set three dials. Every layout, motion, and density decision below is gated by these.
+
+* **`DESIGN_VARIANCE: 8`** - 1 = Perfect Symmetry, 10 = Artsy Chaos
+* **`MOTION_INTENSITY: 6`** - 1 = Static, 10 = Cinematic / Physics
+* **`VISUAL_DENSITY: 4`** - 1 = Art Gallery / Airy, 10 = Cockpit / Packed Data
+
+**Baseline:** `8 / 6 / 4`. Use these unless the design read overrides them. Do not ask the user to edit this file - overrides happen conversationally.
+
+### 1.A Dial Inference (design read → dial values)
+| Signal | VARIANCE | MOTION | DENSITY |
+|---|---|---|---|
+| "minimalist / clean / calm / editorial / Linear-style" | 5-6 | 3-4 | 2-3 |
+| "premium consumer / Apple-y / luxury / brand" | 7-8 | 5-7 | 3-4 |
+| "playful / wild / Dribbble / Awwwards / experimental / agency" | 9-10 | 8-10 | 3-4 |
+| "landing page / portfolio / marketing site (default)" | 7-9 | 6-8 | 3-5 |
+| "trust-first / public-sector / regulated / accessibility-critical" | 3-4 | 2-3 | 4-5 |
+| "redesign - preserve" | match existing | +1 | match existing |
+| "redesign - overhaul" | +2 | +2 | match existing |
+
+### 1.B Use-Case Presets
+| Use case | VARIANCE | MOTION | DENSITY |
+|---|---|---|---|
+| Landing (SaaS, mainstream) | 7 | 6 | 4 |
+| Landing (Agency / creative) | 9 | 8 | 3 |
+| Landing (Premium consumer) | 7 | 6 | 3 |
+| Portfolio (Designer / studio) | 8 | 7 | 3 |
+| Portfolio (Developer) | 6 | 5 | 4 |
+| Editorial / Blog | 6 | 4 | 3 |
+| Public-sector service | 3 | 2 | 5 |
+| Redesign - preserve | match | match+1 | match |
+| Redesign - overhaul | +2 | +2 | match |
+
+### 1.C How the Dials Drive Output
+Use these (or user-overridden values) as global variables. Cross-references throughout this document refer to these exact variable names - never invent aliases like `LAYOUT_VARIANCE` or `ANIM_LEVEL`.
+
+---
+
+## 2. BRIEF → DESIGN SYSTEM MAP
+
+Once you have the design read (Section 0) and dials (Section 1), pick the right foundation. Do not invent CSS for things that have an official package. Do not pretend an aesthetic trend is an official system.
+
+### 2.A When to reach for a real design system (use official packages)
+| Brief reads as… | Reach for | Why |
+|---|---|---|
+| Microsoft / enterprise SaaS / dashboards | `@fluentui/react-components` or `@fluentui/web-components` | Official Fluent UI, Microsoft tokens, accessibility done |
+| Google-ish UI, Material-flavored product | `@material/web` + Material 3 tokens | Official, theme-able via Material Theming |
+| IBM-style B2B / enterprise analytics | `@carbon/react` + `@carbon/styles` | Official Carbon, mature data-density patterns |
+| Shopify app surfaces | `polaris.js` web components / Polaris React | Required for Shopify admin UI |
+| Atlassian / Jira-style product | `@atlaskit/*` + `@atlaskit/tokens` | Official Atlassian DS |
+| GitHub-style devtool / community page | `@primer/css` or `@primer/react-brand` | Official Primer; Brand variant for marketing |
+| Public-sector UK service | `govuk-frontend` | Legally / regulatorily expected |
+| US public-sector / trust-first | `uswds` | Same |
+| Fast local-business / agency MVP | Bootstrap 5.3 | Boring, fast, works |
+| Modern accessible React foundation | `@radix-ui/themes` | Primitives + polished theme |
+| Modern SaaS where you own the components | shadcn/ui (`npx shadcn@latest add ...`) | You own the code, easy to customise; never ship default state |
+| Tailwind-based modern SaaS / AI marketing | Tailwind v4 utilities + `dark:` variant | Default for indie + small team builds |
+
+**Honesty rule:** if the brief reads as one of the systems above, install and use the **official** package. Do not recreate its CSS by hand. Do not import a system's tokens but then override 90% of them.
+
+**One system per project.** Do not mix Fluent React with Carbon in the same tree. Do not import shadcn/ui components into a Material 3 app.
+
+### 2.B When the brief is an aesthetic, not a system
+For these directions, there is **no single official package**. Build with native CSS + Tailwind + a maintained component library. Be honest in code comments about what is borrowed inspiration vs. official material.
+
+| Aesthetic | Honest implementation |
+|---|---|
+| Glassmorphism / "frosted glass" | `backdrop-filter`, layered borders, highlight overlays. Provide solid-fill fallback for `prefers-reduced-transparency`. |
+| Bento (Apple-style tile grids) | CSS Grid with mixed cell sizes. No single library owns this. |
+| Brutalism | Native CSS, monospace, raw borders. No library. |
+| Editorial / magazine | Serif type, asymmetric grid, generous whitespace. No library. |
+| Dark tech / hacker | Mono + accent neon, terminal motifs. No library. |
+| Aurora / mesh gradients | SVG or layered radial gradients. No library. |
+| Kinetic typography | Native CSS animations, scroll-driven animations, GSAP for hijacks. No library. |
+| **Apple Liquid Glass** | Apple documents this for Apple platforms only. **There is no official `liquid-glass.css`.** Web implementations are approximations using `backdrop-filter` + layered borders + highlights. Label clearly as approximation. |
+
+---
+
+## 3. DEFAULT ARCHITECTURE & CONVENTIONS
+
+Unless the design read picks a real design system (Section 2.A), these are the defaults:
+
+### 3.A Stack
+* **Framework:** React or Next.js. Default to Server Components (RSC).
+ * **RSC SAFETY:** Global state works ONLY in Client Components. In Next.js, wrap providers in a `"use client"` component.
+ * **INTERACTIVITY ISOLATION:** Any component using Motion, scroll listeners, or pointer physics MUST be an isolated leaf with `'use client'` at the top. Server Components render static layouts only.
+* **Styling:** **Tailwind v4** (default). Tailwind v3 only if the existing project demands it.
+ * For v4: do NOT use `tailwindcss` plugin in `postcss.config.js`. Use `@tailwindcss/postcss` or the Vite plugin.
+* **Animation:** **Motion** (the library formerly known as Framer Motion). Import from `motion/react` (`import { motion } from "motion/react"`). The `framer-motion` package still works as a legacy alias - prefer `motion/react` in new code.
+* **Fonts:** Always use `next/font` (Next.js) or self-host with `@font-face` + `font-display: swap`. Never link Google Fonts via `` in production.
+
+### 3.B State
+* Local `useState` / `useReducer` for isolated UI.
+* Global state ONLY for deep prop-drilling avoidance - Zustand, Jotai, or React context.
+* **NEVER** use `useState` to track continuous values driven by user input (mouse position, scroll progress, pointer physics, magnetic hover). Use Motion's `useMotionValue` / `useTransform` / `useScroll`. `useState` re-renders the React tree on every change and collapses on mobile.
+
+### 3.C Icons
+* **Allowed libraries (priority order):** `@phosphor-icons/react`, `hugeicons-react`, `@radix-ui/react-icons`, `@tabler/icons-react`.
+* **Discouraged:** `lucide-react`. Acceptable only when the user explicitly asks for it or the project already depends on it.
+* **NEVER hand-roll SVG icons.** If a glyph is missing, install a second library or compose from primitives - do not draw icon paths from scratch.
+* **One family per project.** Do not mix Phosphor with Lucide in the same component tree.
+* **Standardize `strokeWidth` globally** (e.g. `1.5` or `2.0`).
+
+### 3.D Emoji Policy
+Discouraged by default in code, markup, and visible text. Replace symbols with icon-library glyphs. **Override:** allow emojis only when the user explicitly asks for a playful / chat-style / social-native vibe - and even then use them sparingly with intent.
+
+### 3.E Responsiveness & Layout Mechanics
+* Standardize breakpoints (`sm 640`, `md 768`, `lg 1024`, `xl 1280`, `2xl 1536`).
+* Contain page layouts using `max-w-[1400px] mx-auto` or `max-w-7xl`.
+* **Viewport Stability:** NEVER use `h-screen` for full-height Hero sections. ALWAYS use `min-h-[100dvh]` to prevent layout jumping on mobile (iOS Safari address bar).
+* **Grid over Flex-Math:** NEVER use complex flexbox percentage math (`w-[calc(33%-1rem)]`). ALWAYS use CSS Grid (`grid grid-cols-1 md:grid-cols-3 gap-6`).
+
+### 3.F Dependency Verification (mandatory)
+Before importing ANY 3rd-party library, check `package.json`. If the package is missing, output the install command first. **Never** assume a library exists.
+
+---
+
+## 4. DESIGN ENGINEERING DIRECTIVES (Bias Correction)
+
+LLMs default to clichés. Override these defaults proactively. Each rule has a context-aware override path.
+
+### 4.1 Typography
+* **Display / Headlines:** Default `text-4xl md:text-6xl tracking-tighter leading-none`.
+* **Body / Paragraphs:** Default `text-base text-gray-600 leading-relaxed max-w-[65ch]`.
+* **Sans font choice:**
+ * **Discouraged as default:** `Inter`. Pick `Geist`, `Outfit`, `Cabinet Grotesk`, `Satoshi`, or a brand-appropriate serif first.
+ * **Override:** Inter is acceptable when the user explicitly asks for a neutral / standard / Linear-style feel, or when the brief is a public-sector / accessibility-first site.
+* **Pairings to know:** `Geist` + `Geist Mono`, `Satoshi` + `JetBrains Mono`, `Cabinet Grotesk` + `Inter Tight`, `GT America` + `IBM Plex Mono`.
+
+* **SERIF DISCIPLINE (VERY DISCOURAGED AS DEFAULT):**
+ * Serif is **very discouraged as the default font for any project.** "It feels creative / premium / editorial" is NOT a reason to reach for serif. The agent's default mental model that "creative brief = serif" is the single most-tested AI tell in production rounds.
+ * **Serif is only acceptable when ONE of these is explicitly true:**
+ - The brand brief literally names a serif font, OR
+ - The aesthetic family is genuinely editorial / luxury / publication / manuscript / heritage / vintage AND you can articulate why this specific serif fits this specific brand
+ * For everything else (creative agency, design studio, modern brand, premium consumer, portfolio, lifestyle), **default sans-serif display** (Geist Display, ABC Diatype, Söhne Breit, Cabinet Grotesk Display, Migra Sans, GT Walsheim, Inter Display, PP Neue Montreal). Sans display fonts are not "boring" — they are the default for the same reason black is the default in fashion.
+ * **EMPHASIS RULE (related):** When you want to emphasize a word within a headline (the kinetic "and `spatial` design" type move), use **italic or bold of the SAME font**. Do NOT inject a random serif word into a sans headline (or vice versa) just to add visual interest. Mixed-family emphasis is amateur. Italic/bold emphasis in the same family is the right move.
+ * **Specifically BANNED as defaults:** `Fraunces` and `Instrument_Serif` (the two LLM-favorite display serifs).
+ * **If a serif is justified** (rare, per the above), rotate from this pool, do NOT reuse the same serif across consecutive projects: PP Editorial New, GT Sectra Display, Cardinal Grotesque, Reckless Neue, Tiempos Headline, Recoleta, Cormorant Garamond, Playfair Display, EB Garamond, IvyPresto, Migra, Editorial Old, Saol Display, Söhne Breit Kursiv, Domaine Display, Canela, Schnyder, Tobias, NB Architekt, ITC Galliard.
+
+* **ITALIC DESCENDER CLEARANCE (mandatory):** When italic is used in display type and the word contains a descender letter (`y g j p q`), `leading-[1]` or `leading-none` will clip the descender. Use `leading-[1.1]` minimum and add `pb-1` or `mb-1` reserve on the wrapping element. Audit every italic word in display headlines before shipping.
+
+### 4.2 Color Calibration
+* Max 1 accent color. Saturation < 80% by default.
+* **THE LILA RULE:** The "AI Purple / Blue glow" aesthetic is discouraged as a default. No automatic purple button glows, no random neon gradients. Use neutral bases (Zinc / Slate / Stone) with high-contrast singular accents (Emerald, Electric Blue, Deep Rose, Burnt Orange, etc.).
+* **Override:** if the brand or brief explicitly asks for purple / violet / lila, embrace it. But execute with intent: consistent palette, harmonised neutrals, restrained gradients. Not generic AI gradient slop.
+* **One palette per project.** Do not fluctuate between warm and cool grays within the same project.
+* **COLOR CONSISTENCY LOCK (mandatory):** Once an accent color is chosen for a page, it is used on the WHOLE page. A warm-grey site does not suddenly get a blue CTA in section 7. A rose-accented site does not get a teal status badge in the footer. Pick one accent, lock it, audit every component before shipping.
+
+* **PREMIUM-CONSUMER PALETTE BAN (mandatory, second-most-recurring AI-tell):**
+ * For premium-consumer briefs (cookware, wellness, artisan, luxury, heritage craft, DTC home goods, etc.) the LLM default is **warm beige/cream + brass/clay/oxblood/ochre + espresso/ink dark text**. Concretely banned hex families as default backgrounds and accents:
+ - Backgrounds: `#f5f1ea`, `#f7f5f1`, `#fbf8f1`, `#efeae0`, `#ece6db`, `#faf7f1`, `#e8dfcb` (all "warm paper / cream / chalk / bone")
+ - Accents: `#b08947`, `#b6553a`, `#9a2436`, `#9c6e2a`, `#bc7c3a`, `#7d5621` (all "brass / clay / oxblood / ochre")
+ - Text: `#1a1714`, `#1a1814`, `#1b1814` (all "espresso / warm near-black")
+ * This palette is BANNED as the default reach for premium-consumer briefs. Every premium-consumer site you have ever shipped uses this exact palette. The brand becomes invisible.
+ * **Default alternatives (rotate, do not reuse):**
+ - **Cold Luxury:** silver-grey + chrome + smoke (think Tesla, Apple Watch Hermes-without-the-leather)
+ - **Forest:** deep green + bone + amber accent (think Filson, Patagonia premium)
+ - **Black and Tan:** true off-black + warm tan, sharp contrast, no beige
+ - **Cobalt + Cream:** saturated blue against a single neutral, no brass
+ - **Terracotta + Slate:** warm rust against cool grey, no brass
+ - **Olive + Brick + Paper:** muted olive plus brick-red accent
+ - **Pure monochrome + single saturated pop:** off-white + off-black + one bright accent (electric blue, emerald, hot pink, etc.)
+ * **Palette-rotation rule:** if the previous premium-consumer project you generated used the beige+brass family, this one MUST use a different family. Do not ship the same warm-craft palette twice in a row.
+ * **Override:** the beige+brass+espresso palette is acceptable ONLY when the brand brief explicitly names those colors, or when the brand identity is genuinely vintage / artisan / warm-craft AND you can articulate why this specific palette fits this specific brand. Default-reaching for it because "this is a cookware brief" is banned.
+
+### 4.3 Layout Diversification
+* **ANTI-CENTER BIAS:** Centered Hero / H1 sections are avoided when `DESIGN_VARIANCE > 4`. Force "Split Screen" (50/50), "Left-aligned content / right-aligned asset", "Asymmetric white-space", or scroll-pinned structures.
+* **Override:** centered hero is OK for editorial / manifesto / launch-announcement briefs where the message itself is the design.
+
+### 4.4 Materiality, Shadows, Cards
+* Use cards ONLY when elevation communicates real hierarchy. Otherwise group with `border-t`, `divide-y`, or negative space.
+* When a shadow is used, tint it to the background hue. No pure-black drop shadows on light backgrounds.
+* For `VISUAL_DENSITY > 7`: generic card containers are banned. Data metrics breathe in plain layout.
+* **SHAPE CONSISTENCY LOCK (mandatory):** Pick ONE corner-radius scale for the page and stick to it. Options: all-sharp (radius 0), all-soft (radius 12-16px), all-pill (full radius for interactive). Mixed systems are allowed only when there is a documented rule (e.g. "buttons are full-pill, cards are 16px, inputs are 8px") and that rule is followed everywhere. Round buttons in a square layout, or square cards on a pill-button page, is broken design.
+
+### 4.5 Interactive UI States
+LLMs default to "static successful state only." Always implement full cycles:
+* **Loading:** Skeletal loaders matching the final layout's shape. Avoid generic circular spinners.
+* **Empty States:** Beautifully composed; indicate how to populate.
+* **Error States:** Clear, inline (forms), or contextual (toasts only for transient).
+* **Tactile Feedback:** On `:active`, use `-translate-y-[1px]` or `scale-[0.98]` to simulate a physical push.
+* **BUTTON CONTRAST CHECK (mandatory, a11y):** Before shipping any button, verify the button text is readable against the button background. White button + white text, `bg-white` CTA with `text-white` label, transparent button against the page background with no border → all banned. Audit every CTA: contrast ratio WCAG AA min (4.5:1 for body, 3:1 for large text 18px+). Same rule applies to ghost buttons over photographic backgrounds (use a backdrop, scrim, or stroke).
+* **CTA BUTTON WRAP BAN (mandatory):** Button text MUST fit on one line at desktop. If a label like "VIEW SELECTED WORK" wraps to 2 or 3 lines, the button is broken. Fix by EITHER shortening the label (3 words max for primary CTAs, ideally 1-2) OR widening the button (do not artificially constrain `max-width` on CTAs). Wrapped CTAs at desktop are a Pre-Flight Fail.
+* **NO DUPLICATE CTA INTENT (mandatory):** Two CTAs with the same intent on one page is a Pre-Flight Fail. Examples of same intent: "Get in touch" + "Contact us" + "Let's talk" + "Start a project" + "Start something" + "Reach out" = all "contact" intent → pick ONE label and use it everywhere on the page (nav, hero, footer). Same for "Try free" + "Get started" + "Sign up free" (all "signup" intent) and "View work" + "See selected work" + "Browse projects" (all "portfolio" intent). One label per intent.
+* **FORM CONTRAST CHECK (mandatory, a11y):** Form inputs, placeholder text, focus rings, helper text, and error text all pass WCAG AA contrast against the section background. Light placeholders on a near-white form, white form on white page section, form labels grayer than 4.5:1 contrast → all banned. Audit every form before shipping.
+
+### 4.6 Data & Form Patterns
+* Label ABOVE input. Helper text optional but present in markup. Error text BELOW input. Standard `gap-2` for input blocks.
+* No placeholder-as-label. Ever.
+
+### 4.7 Layout Discipline (Hard Rules. Failing any of these is shipping broken work)
+
+* **Hero MUST fit in the initial viewport.** Headline max 2 lines on desktop, subtext max **20 words** AND max 3-4 lines, CTAs visible without scroll. If the copy is too long: reduce font scale OR cut copy. If you cannot describe the value-prop in 20 words of subtext, the value-prop is unclear, not the rule too tight. Never let the hero overflow and force scroll to find the CTA.
+* **Hero font-scale discipline.** Plan font size and image size *together*. If the hero asset is large and the headline is more than 6 words, do not start at `text-7xl/text-8xl`. Default sensible range: `text-4xl md:text-5xl lg:text-6xl` for most heroes; `text-6xl md:text-7xl` only when the headline is 3-5 words. A 4-line hero headline is always a font-size error, never a copy-length error.
+* **HERO TOP PADDING CAP (mandatory):** Hero top padding max `pt-24` (≈6rem) at desktop. More than that means the hero content floats halfway down the viewport and reads as a layout bug, not as intentional space. If your hero needs more breathing room, increase font scale or asset size, not top padding.
+* **HERO STACK DISCIPLINE (max 4 text elements).** The hero is a single moment, not a feature list. Allowed text elements, max 4 in total:
+ 1. Eyebrow (small uppercase label) OR brand strip OR neither - pick zero or one
+ 2. Headline (max 2 lines, see above)
+ 3. Subtext (max 20 words, max 4 lines)
+ 4. CTAs (1 primary + max 1 secondary)
+ - **BANNED in the hero:** tiny tagline below CTAs ("Works with GitHub, GitLab, and self-hosted Git"), trust micro-strip ("Used by engineering teams at..."), pricing teaser ("Free for solo, $10/user for teams"), feature bullet list, social-proof avatar row. All of those move to dedicated sections directly below the hero.
+ - If you have an eyebrow AND a tagline below CTAs in the same hero, drop the tagline. If you have a brand strip AND a tagline, drop the tagline. One small text element per hero, max.
+* **"Used by" / "Trusted by" logo wall belongs UNDER the hero, never inside it.** The hero is for the value prop and primary CTA. The logo wall is a separate section directly below. Do not stuff trust logos into the same flex row as the hero copy.
+* **Navigation MUST render on a single line on desktop.** If items don't fit at `lg` (1024px), condense labels, drop secondary items, or move to a hamburger. A two-line nav at desktop is broken design.
+* **Navigation height cap: 80px max desktop, default 64-72px.** No huge "agency" nav bars that eat 15% of the viewport.
+* **Bento grids MUST have rhythm, not one-sided repetition.** Do not stack 6 left-image / right-text rows. Vary the composition: alternate full-width feature rows, asymmetric tile sizes, vertical breaks.
+* **BENTO CELL COUNT RULE (mandatory):** A bento grid has EXACTLY as many cells as you have content for. 3 items → 3 cells (1+2 split, or 2+1, or asymmetric trio). 5 items → 5 cells (2+3, 3+2, hero+4, etc.). If your grid has an empty cell in the middle or at the end, you planned wrong. Re-shape the grid; do not paste a blank tile.
+* **Section-Layout-Repetition Ban.** Once you use a layout family for a section (e.g., 3-column-image-cards, full-width-quote, split-text-image), that family can appear at most ONCE on the page. "Selected commissions" must not look like "What we do." A landing page with 8 sections must use at least 4 different layout families.
+* **ZIGZAG ALTERNATION CAP (mandatory).** Alternating "left-image + right-text" then "left-text + right-image" zigzag layout = banal. Max 2 sections in a row with this image+text-split pattern. The 3rd consecutive image+text split is a Pre-Flight Fail. Break the pattern with a full-width section, a vertical-stack section, a bento grid, a marquee, or a different layout family.
+* **EYEBROW RESTRAINT (mandatory, the #1 violated rule in production tests).** An "eyebrow" is the small uppercase wide-tracking label sitting above a section headline (e.g. `FOUR COLORWAYS`, `SELECTED WORK`, `THE HARDWARE`, `Git-native task management`). Typical CSS signature: `text-[11px] uppercase tracking-[0.18em]`, `font-mono text-[10.5px] uppercase tracking-[0.22em]`. Every AI-built site puts an eyebrow above EVERY section header, producing the same templated rhythm. Hard rule:
+ - **Maximum 1 eyebrow per 3 sections.** Hero counts as 1. So a page with 9 sections may use at most 3 eyebrows total.
+ - If section A has an eyebrow, the next 2 sections cannot have one.
+ - **Pre-Flight Check is mechanical:** count instances of `uppercase tracking` (or similar small-caps mono labels above headlines) across all section components. If count > ceil(sectionCount / 3), the output fails.
+ - **What to do instead of an eyebrow:** drop it entirely. The headline alone is enough. If you need to categorize a section, the section's location on the page already categorizes it; no label needed.
+* **SPLIT-HEADER BAN (mandatory).** The pattern "left big headline + right small explainer paragraph" as a section header (left col-span-7/8, right col-span-4/5 with a small body paragraph floating in the right column) is **banned as default**. Sections should have ONE focused message. If you genuinely need both a headline and an explainer paragraph, stack them vertically (headline on top, body below, max-width 65ch). Reach for the split-header pattern only when there is a real compositional reason (e.g., the right column carries a visual or interactive element, not just filler text).
+* **Bento Background Diversity (mandatory).** Bento and feature-grid sections cannot be 6 white-on-white cards with text inside. At least 2-3 cells in any multi-cell grid need real visual variation: a real image, a brand-appropriate gradient (not AI-purple), a pattern, a tinted background. A cream-on-cream bento with only typography inside reads as boring AI default, even when the rest of the page is good.
+* **Mobile collapse must be explicit per section.** For every multi-column layout, declare the `< 768px` fallback in the same component. No "it'll work, Tailwind handles it" assumptions.
+
+### 4.8 Image & Visual Asset Strategy
+
+Landing pages and portfolios are **visual products**. Text-only pages with fake-screenshot divs are slop.
+
+**Priority order for visual assets:**
+1. **Image-generation tool first.** If ANY image-gen tool is available in the environment (`generate_image`, MCP image tool, IDE-integrated gen, OpenAI image tools, etc.) you MUST use it to create section-specific assets: hero photography, product shots, texture backgrounds, mood images. Generate at the right aspect ratio for the section. Do not skip this step because hand-rolled CSS feels faster.
+2. **Real web images second.** When no gen tool is available, use real photography sources. Acceptable defaults:
+ * `https://picsum.photos/seed/{descriptive-seed}/{w}/{h}` for placeholder photography (seed should describe the section, e.g. `marrow-cookware-kitchen`)
+ * Actual stock or brand URLs when the brief provides them
+ * Open-license sources (Unsplash via direct URL, Pexels) if explicitly allowed
+3. **Last resort: tell the user.** If neither is possible, do NOT fill the page with hand-rolled SVG illustrations or div-based "fake screenshots." Instead, leave clearly-labeled placeholder slots (``) and at the end of the response say: *"This page needs real images at: \[list of placements\]. Please generate or provide them."*
+
+**Even minimalist sites need real images.** A pure-text page is not minimalism. It is incomplete work. Even an editorial Linear-style site needs at least 2-3 real images (hero, one product/lifestyle shot, one supporting image). Generate B&W minimalist photography if the brief is restrained; do not skip images entirely because the dial is low.
+
+**Real company logos for social proof.** When the brief calls for a "Trusted by / Used by / Customers" logo wall, do NOT default to plain text wordmarks (`Acme Co` styled in a row). Use real SVG logos:
+* **Source: Simple Icons** (`https://cdn.simpleicons.org/{slug}/ffffff` for any color, or `simple-icons` npm package). Covers most known brands.
+* **Alternative: devicon** for tech-stack logos (`@svgr/cli` or CDN).
+* **Make-up the brand name? Then make-up an SVG mark too.** Generate a simple monogram (one letter in a circle, two-letter ligature, abstract glyph) rendered as an inline `