# Design Philosophy — Agent Instructions

This file defines the high-level aesthetic decisions that govern every UI element in the system. It is the **root of the cascade** — when a decision here changes, every component pattern that references it must be updated to match.

Component pattern files (`design/components/*.md`) explicitly declare which philosophy sections they follow. When you change a section below, find all component patterns that reference it and rewrite them accordingly.

---

## Surface Treatment

The system uses a **border-first** aesthetic. Surfaces are distinguished from their surroundings primarily through subtle borders, with shadows providing supplementary depth.

### Rules

- **Borders are the primary delimiter.** Every distinct surface (card, panel, input, tile) has a 1px solid border. The border token varies by emphasis:
  - `--border-subtle` — default for cards, tiles, dividers, and resting containers
  - `--border-default` — inputs and interactive containers at rest
  - `--border-strong` — when a boundary must be clearly visible (e.g., active selection borders)
- **Shadows provide static depth and elevation only.** The shadow scale (`--shadow-xs` through `--shadow-xl` in `layout.css`) communicates visual hierarchy but never changes on interaction:
  - Resting cards/tiles: `--shadow-xs` or `--shadow-sm`
  - Floating panels (dropdowns, tooltips): `--shadow-md` or `--shadow-lg`
  - Modals: `--shadow-xl`
  - Shadows do not change on hover, active, or any other interaction state
  - Use shadows purposefully — not every surface needs one
- **Backgrounds are near-white and layered.** Surface depth is also communicated through background token steps:
  - Page canvas: `--bg-canvas` (grey-0)
  - First surface (sidebar, card header/footer): `--bg-surface-1` (grey-50)
  - Hover / secondary surface: `--bg-surface-2` (grey-200)
- **No gradients on surfaces.** Gradients are reserved exclusively for brand/decorative elements (logo shimmer, loading animations). Functional surfaces are always flat solid fills.

### Status accent pattern

Status-colored blocks (alerts, terminal messages, chat bubbles) use a consistent formula:
- Background: the `50` stop of the status color
- Border: `1px solid` using the `200` stop
- Optional left accent: `2px solid` left border using the `500` stop
- Text: the `500` stop for emphasis text, `--fg-secondary` for body

This applies to: success (green), error (red), warning (yellow), info (blue), and action (pink).

---

## Corner Style

Corner radii are intentionally constrained to a small set of values. Do not invent new radii.

| Context | Token | Value | Examples |
|---------|-------|-------|----------|
| Small controls | `--radius-sm` | 4px | Close buttons, inline code, checkboxes |
| Default interactive | `--radius-md` | 6px | Inputs, small buttons, command blocks |
| Containers | `--radius-lg` | 8px | Cards, tiles, modals, primary buttons, panels |
| Large containers | `--radius-xl` | 12px | Hero elements, large panels |
| Pills | `--radius-full` | 9999px | Badges, tags, toggle tracks |

### Rules

- Buttons always use `--radius-lg` (8px).
- Inputs and dropdowns use `--radius-md` (6px) for triggers, `--radius-md` for panels.
- Cards, tiles, and modals use `--radius-lg` (8px).
- Badges and toggle tracks use `--radius-full` (pill).
- Never mix radii on the same element (e.g., don't round only the top corners of a card unless it's a header/footer split where the parent clips overflow).

---

## Interaction States

### Hover

Hover is communicated through **background color shift only**. No scale transforms, no shadow changes, no translateY on hover.

The hover direction depends on **perceptual weight**, not whether the element is colored or neutral:

- **Heavy fills (saturated, dark)** lighten on hover — e.g., primary buttons go `600 → 500`. This is the Stripe-style "breath" where surfaces inhale (lighter) on hover and exhale (darker) on press.
- **Light surfaces (pastel, near-white)** darken on hover — e.g., colored surfaces go `50 → 100`, neutral surfaces go `canvas → surface-1`. A near-white tinted surface has nowhere meaningful to go lighter, same as pure white.

The dividing line is roughly **stop 300**: stops 0–300 are light enough that hover should deepen; stops 400+ are heavy enough that hover should lighten.

- On primary buttons (solid fill): lighten one stop (`600` -> `500`)
- On danger buttons: lighten via `--error-hover` (matches primary behavior)
- On secondary buttons (neutral bordered): darken one step (`--bg-canvas` -> `--bg-surface-1`)
- On ghost/text elements: add a subtle background (`bg-surface-1` or `bg-surface-2`)
- On cards/tiles: shift background one surface level deeper; no shadow change
- On neutral surfaces: shift one surface level deeper (`bg-canvas` -> `bg-surface-1` -> `bg-surface-2`)
- On colored surfaces (50-stop): darken one stop (`--{color}-surface` → `--{color}-surface-hover`, i.e. `50 → 100`)
- Transition: `0.15s ease` for color changes

The interaction arc for heavy fills is: **rest → lighter (hover) → darker (pressed)**. This creates a "breath" feel — surfaces inhale on hover, exhale on press. For light surfaces the arc is simply: **rest → deeper (hover) → deeper still or rest (pressed)**.

### Focus

- All focusable elements get a **2px ring** using `--border-focus` (blue-500) with a **2px offset** from the element edge.
- Ring is applied via `focus-visible`, not `focus`, so it only appears for keyboard navigation.
- The ring offset background matches the surrounding surface (usually `--bg-canvas`).

### Active / Pressed

**Single-step interactions** (click completes the action — buttons, clickable cards):
- **Primary and danger buttons:** `transform: scale(0.97)` on mousedown — a subtle physical press feel. `120ms ease-out` per motion guide. Background stays at the hover color — the shrink is the feedback, not a color change.
- **Secondary / ghost buttons:** darken background on press (no scale transform). Use `bg-surface-2`.
- Text links do NOT scale — they are text, not interactive surfaces. A button that navigates is still a button.

**Multi-step interactions** (click opens an intermediate step — dropdowns, menus, popovers):
- No scale transform on the trigger. The opening of the panel *is* the feedback.
- The **final step** in the chain (e.g., clicking a menu item) may optionally use `scale(0.97)` to signal completion, but it is not required.

No shadow changes on press for either type.

### Disabled

- `opacity: 0.5` on the entire element
- `pointer-events: none`
- `cursor: not-allowed` (set before pointer-events disables it, for the visual cue)
- For outlined/bordered buttons specifically: switch to `transparent` background + `1px solid --fg-disabled` border + `--fg-disabled` text

### Selected / Active (navigation)

Navigation items and tabs use dedicated tokens:
- Resting: `transparent` background
- Hover: `--nav-hover`
- Selected: `--nav-selected`
- These are distinct from surface tokens — navigation has its own visual lane.

---

## Color Application

### Button color model

The system uses a **four-variant** button model: Primary → Secondary → Ghost → Danger. Primary buttons use solid dark fills (600 stop) for maximum prominence. Secondary buttons are neutral (white bg, subtle grey border, dark text). Ghost buttons are transparent with no border. Danger buttons are a red variant of primary.

| Button type | Background (rest) | Background (hover) | Background (pressed) | Border | Text |
|-------------|-------------------|---------------------|----------------------|--------|------|
| Primary | `{color}-600` | `{color}-500` | `{color}-500` | none | `--fg-inverse` |
| Secondary | `--bg-canvas` | `--bg-surface-1` | `--bg-surface-2` | `1px solid --border-subtle` | `--fg-primary` |
| Ghost | `transparent` | `--bg-surface-1` | `--bg-surface-2` | none | `--fg-primary` |
| Danger | `--red-600` | `--error-hover` | `--error-hover` | none | `--fg-inverse` |

Primary is the single most important action on a screen — one per section. The `{color}-0` (bg) stop is **not** used for buttons; it is reserved for non-interactive reading surfaces like message bubbles and status banners.

### Accent hierarchy

The interface is **predominantly greyscale**. Chromatic color is used as targeted *highlights*, never as the dominant tone of a screen. A page that reads as colorful is wrong; a page that reads as quiet greyscale punctuated by a few small chromatic moments is right. If a UI element does not need to draw the eye, it should be greyscale — reach for a chromatic accent only when there is a specific reason for the user to look at that thing.

Highlight roles:

- **Blue** is the **primary highlight** (links, focus rings, default interactive color — the "where to click" cue)
- **Orange** is the **secondary highlight** (callouts, accents that need contrast alongside blue, complementary tags, chart accents)
- **Pink** is reserved for **brand expression** (logo, brand gradient, welcome moments). Do not use it for general UI accents.
- **Green** is success/completion
- **Red** is error/danger/destructive
- **Yellow** is warning

Status colors (green/red/yellow) are orthogonal to the highlight hierarchy — they communicate state, not emphasis, and should be used only when their meaning applies.

### Brand expression

Blue and pink together form the brand gradient, used exclusively in decorative/animated contexts:
- Loading shimmer: `linear-gradient(135deg, blue-500, pink-500)`
- Logo animations, welcome screens
- Never on functional UI surfaces or buttons

---

## Density & Spacing

### Spacing values

The system does not use a formal spacing scale yet (see Fractal `layout.css`). In practice, these values recur consistently:

| Context | Value | Usage |
|---------|-------|-------|
| Tight | 4px | Gap between icon-only buttons, inline elements |
| Default | 8px | Gap between related elements (icon + label, stacked items) |
| Comfortable | 12px | Padding inside small components (buttons, badges) |
| Roomy | 16px | Padding inside containers, gap between sections |
| Generous | 20-24px | Card/modal padding, page margins |

### Layout constants

- Sidebar width: `--panel-width` (256px)
- Modal small: 420px, Modal medium: 756px
- Max content width: 580px (terminal), ~2xl for settings

### Density philosophy

The app leans toward **comfortable density** — not as tight as a data-heavy dashboard, not as airy as a marketing page. There should always be visible breathing room between elements, but no wasted space. When in doubt, use `8px` gaps and `16px` padding.

---

## Motion & Transitions

### Duration

- **Fast** (0.15s): Color changes, background shifts, opacity — anything that responds to hover/press
- **Normal** (0.2s): Layout transitions, sidebar collapse/expand, element appearance
- **None**: No transitions on text content changes, scroll position, or data updates

### Easing

- `ease` for most interactions (CSS default ease)
- `ease-in-out` for animations that loop (shimmer, pulse, loading)
- `cubic-bezier(0.4, 0, 0.6, 1)` for the pulse animation specifically
- Never use `linear` for UI transitions (it feels mechanical)

### What moves

- Background color on hover/press
- Border color on focus
- Sidebar panel position (translateX)
- Opacity on disabled state and loading
- Toggle knob position

### What does not move

- No scale transforms on hover (elements don't grow/shrink). Single-step interactions use `scale(0.97)` on active/pressed only — see Active / Pressed section above.
- No rotation on icons
- No bounce/spring easing

---

## Scrollbar Style

Scrollbars are thin and auto-hiding:
- Width: 8px
- Track: transparent
- Thumb: invisible at rest, `rgba(0,0,0,0.15)` on container hover, `rgba(0,0,0,0.25)` on thumb hover
- Border-radius: 4px on thumb

---

## Typography Application

(See `typography/typography-guide.md` for the full token reference. This section covers how typography is applied in the UI philosophy.)

- **Default body text** in the app is `text-sm` (0.875rem / 14px) — tier 02.
- **Section labels** use `label-01` (uppercase, 0.75rem, semibold, letter-spaced).
- **Headers** use `font-medium` (500) at the same size as their body tier — differentiated by weight, not size.
- **Metadata and secondary info** uses `text-xs` (0.75rem / 12px).
- **Chat content** is the only context that uses tier 01 (1rem / 16px).

---

## Iconography

- Icon library: **Carbon Icons** (`@carbon/icons-react`)
- Default icon size: 16px for inline/sidebar, 20px for standalone buttons
- Icon color follows the foreground color of its context (`--fg-primary`, `--fg-secondary`, or the relevant accent `500` stop)
- Icons in buttons sit to the left of text with an 8px gap, unless `iconPosition: 'right'` is specified
- Icon-only buttons are square, sized by button size tier (sm: 20px, md: 38px, lg: 44px)

---

## Z-Index

In practice, these layers exist:

| Layer | z-index | Usage |
|-------|---------|-------|
| Base content | 0 | Page content, cards, tiles |
| Sticky headers | 1 | Sticky project row in sidebar |
| Dropdowns | 999999 | Dropdown panels (high to escape any stacking context) |
| Modals / overlays | 9998 | Modal backdrop |
| Tooltips | 10000+ | Ketcher and other third-party overlays |

---

## Summary of Aesthetic Identity

The visual identity can be described in one sentence: **A flat, border-defined, light-palette UI where color is applied softly through pastel fills and precise accent text, not through heavy solid blocks or shadows.**

Key adjectives: clean, light, precise, pastel, quiet. The interface should feel like it recedes behind the content — never loud, never heavy, never decorative except in explicit brand moments.
