diff --git a/docs/superpowers/specs/2026-09-01-delivery-row-info-and-order-age-badge-design.md b/docs/superpowers/specs/2026-09-01-delivery-row-info-and-order-age-badge-design.md
new file mode 100644
index 0000000..c2a131a
--- /dev/null
+++ b/docs/superpowers/specs/2026-09-01-delivery-row-info-and-order-age-badge-design.md
@@ -0,0 +1,305 @@
+# Compact DN Row + Order Age Badge — Design
+
+## Purpose
+
+Two small, independent rendering improvements for two pages the
+operator uses every day:
+
+1. **`/deliveries`** — each delivery-note row currently renders only
+ `DN {number}` + a one-line subline (delivery date or PO ref). The
+ operator can't tell *who* a delivery note is for or whether the
+ signed copy is back without scrolling up to the customer/order
+ group header. This spec adds a compact subline with customer name,
+ company, parent order number, and a signed-copy indicator — all
+ data the service already returns, no new queries.
+
+2. **`/orders`** — the order card subtitle already shows
+ `{formatDate(order.created)}` but as a bare date string. The
+ operator wants to triage by **age** ("how long has this been on my
+ board") at a glance, not parse dates. This spec adds a colour-coded
+ age badge (`today`, `3d old`, `7d old`, etc.) replacing the bare
+ date string in the right-aligned metadata slot.
+
+Both features share no code; they're bundled in one spec only because
+they were brainstormed together.
+
+## Scope decisions (confirmed during brainstorming)
+
+- **DN row enrichment** — compact option from the brainstorm:
+ customer name, company name, parent order number, signed-copy
+ indicator. No item count, no total value (those would require a
+ server-side aggregate; rejected for now).
+- **Age surfacing** — colour-coded badge, not auto-sort, not
+ raw-age-only. Replaces the existing date string; date is dropped
+ because the badge text already conveys the date implicitly ("3d old
+ on 2026-08-29" is redundant).
+- **Age thresholds** — `<24h → today (green)`, `1–2d → green`,
+ `3–7d → amber`, `8–14d → orange`, `>14d → red`. Tunable later.
+- **No server changes** — both features consume data already
+ returned by `/api/v1/deliveries` and `/api/v1/orders` (or, in
+ `/orders`'s case, the page-level fetcher).
+- **No PB schema changes** — purely UI/rendering work.
+
+## Architecture
+
+**New files:**
+```
+src/lib/order-age.ts — pure age math + bucket → tailwind class
+src/lib/order-age.test.ts — unit tests for the util
+```
+
+**Modified files:**
+```
+src/app/(app)/deliveries/page.tsx — DeliveryNoteRowComponent only
+src/app/(app)/orders/page.tsx — order card right-aligned metadata slot
+```
+
+No service-layer or `/api/v1` route changes. No MCP tool changes.
+
+---
+
+## Feature 1: Compact info on delivery-note rows
+
+### File: `src/app/(app)/deliveries/page.tsx`
+
+Scope: **only** the `DeliveryNoteRowComponent` function (lines
+~1475-1588). No change to the grouping logic, the search filter, the
+loading endpoint, or any other component on the page.
+
+### Target row rendering
+
+```
+DN DN26083101
+Acme Mining (Pty) Ltd · Master Drilling Co · ORD-2026-08-058 · 📎 signed copy
+[status badge] [Mark loaded] [Signed copy]
+```
+
+The existing `
` subline at line 1537-1547 is replaced with a new
+piece list:
+
+1. **Customer name** — `dn.order?.customer_name` ?? `"Counter pickup"`.
+ Always rendered (counter-pickup is the explicit no-order case).
+2. **Company name** — `dn.order?.company_name`. Hidden if falsy.
+3. **Order number** — `dn.order?.order_number`. Hidden if falsy.
+4. **Signed-copy indicator** — `` icon + `"signed copy"`
+ text. Hidden if `!dn.signed_copy || dn.signed_copy.length === 0`.
+ The whole indicator carries `title={dn.signed_copy.join(", ")}` so
+ the operator can see the filenames on hover.
+
+Joined by `·` (same separator convention as the order card subtitle),
+in `text-muted-foreground` (matches the existing subline colour).
+
+### Edge cases
+
+- **Counter-pickup DN** (`dn.order === null`): customer/company/order
+ pieces all collapse to a single `"Counter pickup"` label (no
+ separator emitted for the missing pieces). Signed-copy indicator
+ still appears if files were uploaded directly to the DN.
+- **Long customer names**: the customer name piece carries
+ `truncate max-w-[40ch]` + `title` to preserve hover-access. Other
+ pieces are short and don't need truncation.
+- **Multi-DN under one order**: each DN row still renders its own
+ compact subline. The group header (customer → order → list of rows)
+ is unchanged.
+
+### Acceptance
+
+- A DN with order attached shows: `DN {n}`, customer name, company
+ name, order number, paperclip if signed copy uploaded.
+- A counter-pickup DN shows: `DN {n}`, `Counter pickup`, paperclip if
+ signed copy uploaded.
+- A DN with no signed copy shows no paperclip.
+- The page's existing search, grouping, and tab logic are unaffected.
+- No new network request is made when rendering a DN row.
+
+---
+
+## Feature 2: Order age badge
+
+### New file: `src/lib/order-age.ts`
+
+```typescript
+export type OrderAgeBucket = "fresh" | "recent" | "slipping" | "stale";
+
+export interface OrderAge {
+ /** Integer calendar-day delta. 0 means "today". Never negative. */
+ daysOld: number;
+ bucket: OrderAgeBucket;
+ /** Human-readable badge label. */
+ label: string;
+}
+
+/**
+ * Compute the age of an order relative to `now` (defaults to
+ * `new Date()`). The `daysOld` value is a **calendar-day** delta in
+ * the office's local timezone (Africa/Johannesburg, hardcoded for
+ * now) — an order created at 23:00 yesterday read at 01:00 today is
+ * `daysOld = 0` ("today"), not 1. The day delta is computed as
+ * `floor((localEndOfDay - localStartOfCreatedDay) / 1 day)` and is
+ * always returned as a non-negative integer.
+ *
+ * Future-dated `created` values (clock skew, bad data) clamp to 0
+ * to avoid showing "−1d old" or similar nonsense.
+ *
+ * Implementation note: use `Intl.DateTimeFormat` with the
+ * Africa/Johannesburg timezone to get the local YYYY-MM-DD for both
+ * dates, then subtract. Don't divide millisecond deltas by
+ * 86_400_000 — that fails across DST boundaries (SA doesn't observe
+ * DST but the pattern is fragile).
+ */
+export function getOrderAge(
+ created: string | Date,
+ now?: Date,
+): OrderAge;
+
+/**
+ * Map an age bucket to a tailwind className for the badge.
+ * Centralised so a future palette tweak is a one-line change.
+ */
+export function bucketToBadgeClass(bucket: OrderAgeBucket): string;
+```
+
+**Bucket thresholds:**
+
+| `daysOld` | bucket | label |
+|-----------|--------|-------|
+| 0 | `fresh` | `today` |
+| 1–2 | `fresh` | `{daysOld}d old` |
+| 3–7 | `recent` | `{daysOld}d old` |
+| 8–14 | `slipping` | `{daysOld}d old` |
+| ≥ 15 | `stale` | `{daysOld}d old` |
+
+**Badge class names** (initial palette, tunable):
+- `fresh` → `bg-emerald-100 text-emerald-800 dark:bg-emerald-950 dark:text-emerald-300`
+- `recent` → `bg-amber-100 text-amber-800 dark:bg-amber-950 dark:text-amber-300`
+- `slipping` → `bg-orange-100 text-orange-800 dark:bg-orange-950 dark:text-orange-300`
+- `stale` → `bg-red-100 text-red-800 dark:bg-red-950 dark:text-red-300`
+
+### New file: `src/lib/order-age.test.ts`
+
+Unit tests, all pure-function:
+- `daysOld = 0` (created today) → `fresh`, label `"today"`
+- `daysOld = 1, 2` → `fresh`, label `"1d old"`, `"2d old"`
+- `daysOld = 3, 7` → `recent`, label `"3d old"`, `"7d old"`
+- `daysOld = 8, 14` → `slipping`, label `"8d old"`, `"14d old"`
+- `daysOld = 15, 30, 365` → `stale`, label `"15d old"`, `"30d old"`,
+ `"365d old"`
+- `daysOld = -1` (future date) → clamps to `fresh`, label `"today"`
+- **Local-timezone edge case**: an order created at 23:00 JNB yesterday
+ read at 01:00 JNB today must return `daysOld = 0` (calendar-day delta,
+ not millisecond delta). Test this with `now` injected explicitly.
+- All four `bucketToBadgeClass` outputs return non-empty strings
+- `bucketToBadgeClass` is stable (same input → same output)
+
+No React rendering tests for the badge — that would be a separate
+playwright/e2e concern and is out of scope.
+
+### Modified file: `src/app/(app)/orders/page.tsx`
+
+Scope: the right-aligned metadata line in the order card (current line
+~865-867). The exact JSX currently is:
+
+```tsx
+
+ {formatDate(order.created)}
+
+```
+
+Replace with:
+
+```tsx
+
+```
+
+Where `OrderAgeBadge` is a tiny new inline component in the same
+file (kept local — used only here for now; if a second page needs it,
+it can be hoisted to `src/components/order-age-badge.tsx`):
+
+```tsx
+function OrderAgeBadge({
+ created,
+ className,
+}: {
+ created: string;
+ className?: string;
+}) {
+ const age = getOrderAge(created);
+ return (
+
+ {age.label}
+
+ );
+}
+```
+
+The `title=` attribute preserves the absolute date for operators who
+want to know the exact day — addresses the brainstorm's "drop raw
+date" option concern without keeping it as primary UI.
+
+The existing `formatDate` import (currently from
+`@/lib/pb-helpers`, line ~1596 — `formatDate(dateStr: string)`) is
+kept on the page. `getOrderAge` itself does **not** import
+`formatDate`; it returns the age label only.
+
+### Edge cases
+
+- **Sorting**: not changing. The badge surfaces age; the operator
+ still sorts by `created` if they want.
+- **Rep filtering**: the recent commit
+ `feat(invoicing): filter rows to the logged-in rep's own orders` is
+ orthogonal. Age badge fires regardless of who the rep is.
+- **Long lists**: each card gets one badge. No batched summary view
+ changes.
+- **Existing `formatDate` call**: `formatDate(order.created)` moves
+ from the JSX into the badge's `title=` tooltip so the absolute
+ date is still accessible on hover.
+
+### Acceptance
+
+- An order created today shows a green `today` badge in the
+ right-aligned slot where the date used to live.
+- An order 5 days old shows an amber `5d old` badge.
+- An order 20 days old shows a red `20d old` badge.
+- Hovering the badge reveals the full `Created YYYY-MM-DD` tooltip.
+- The order card layout is otherwise unchanged — the badge slot has
+ the same `ml-auto hidden sm:inline` styling hooks.
+
+---
+
+## What's NOT in scope
+
+- **No new server endpoints.** Both features use existing data.
+- **No PB schema changes.** Purely UI.
+- **No item count / total value on DN rows.** Rejected during
+ brainstorm (would need a server-side aggregate).
+- **No sorting changes on /orders.** Badge only.
+- **No changes to MCP tools.**
+- **No changes to /api/v1 routes.**
+- **No changes to other delivery-note pages** (`/delivery-notes`,
+ `/delivery-notes/[id]`, `/delivery-note-print`). Only
+ `/deliveries`'s row component.
+
+## Testing
+
+- `src/lib/order-age.test.ts` — 6+ unit tests covering all bucket
+ thresholds + future-date clamp + `bucketToBadgeClass` stability.
+- Manual smoke: open `/orders`, verify badges appear with correct
+ colours for orders of various ages; open `/deliveries`, verify the
+ DN row subline shows customer/company/order/paperclip as expected.
+- Existing page tests (if any) for `/orders` and `/deliveries`
+ should continue to pass — no layout regressions.
+
+## Risk assessment
+
+**Low.** Both features are additive rendering changes against
+existing, well-typed payloads. The age util is a pure function and
+the only file with logic worth testing. The DN row change touches
+one component and doesn't change any user-facing interaction
+(no new buttons, no new fields).
\ No newline at end of file