# AI Radar — Visual & UX Direction

Locked before Phase 3 implementation. Every UI decision must trace back to
this document.

---

## 1. The governing rule

From the brief, in priority order:

1. Easier to read beats more advanced.
2. Clearer hierarchy beats more content visible.
3. Less UI beats more UI.

When any two of these conflict with each other, the lower number wins.

The target reaction is: *"This looks modern and intelligent, but I immediately
understand how to use it."* Not *"look at this interface."*

### What this rules out

Named explicitly so it cannot creep back in:

- Cards on everything. Border + fill + radius + shadow each announce "separate
  object". Spending all four on every block flattens hierarchy instead of
  creating it.
- A grid of big-number stat tiles. Those belong on a dashboard where the
  figures are the point. Here the *stories* are the point.
- Accent colour as decoration. Green is a signal, not a theme.
- Coloured category backgrounds. Category is a small label, never a fill.
- Emoji as section markers.
- Gradient anything.
- More than one ambient animation.
- Inter as the display face. It is the current default-by-reflex and reads as
  unconsidered.

---

## 2. Type

The instrument feel comes from typography, not chrome. Three roles:

| Role | Face | Why |
|---|---|---|
| Display / headlines | **Newsreader** (serif) | Editorial authority. A serif at headline size is what separates a publication from a SaaS dashboard, and it is the single strongest signal that this is something to *read*. Optical sizing keeps it sharp small. |
| UI / body | **Söhne**, fallback stack → system | Neutral, high legibility at 15–17px, no personality fighting the serif. |
| Data / metadata | **JetBrains Mono** | Timestamps, source names, category labels, counts. Mono on metadata is the "live instrument" cue — and it makes secondary text feel deliberate rather than merely small. |

Söhne is not on Google Fonts. Practical substitution, in order:
`Söhne` → `Inter Tight` → system UI stack. Inter Tight is narrower and less
recognisable than plain Inter, so it does not read as the default.

Google Fonts is the only permitted host, so: **Newsreader** + **Inter Tight**
+ **JetBrains Mono**, one stylesheet link, `display=swap`, real fallbacks.

### Scale

A 1.25 ratio, rounded to whole pixels. Nothing off this scale.

| Token | Size / line-height | Weight | Use |
|---|---|---|---|
| `hero` | 38 / 44 | 500 serif | Hero story title only |
| `display` | 30 / 36 | 500 serif | Daily overview headline |
| `h1` | 26 / 32 | 500 serif | Story page title |
| `h2` | 19 / 26 | 600 sans | Section headers |
| `card-lg` | 20 / 28 | 500 serif | Important-story card titles |
| `card` | 16.5 / 24 | 550 sans | Standard card titles |
| `row` | 15.5 / 22 | 500 sans | List-row titles |
| `body` | 16 / 27 | 400 sans | Summaries, prose |
| `body-sm` | 14.5 / 23 | 400 sans | Card summaries |
| `meta` | 12.5 / 16 | 500 mono | Source, time |
| `label` | 10.5 / 14 | 600 mono, 0.08em, caps | Category |

Line height on reading text is 1.65–1.7 — generous, per the brief.
Measure capped at **66ch** for prose, **62ch** for card summaries.
**No text below 12.5px anywhere.** No grey lighter than `--text-2` on body copy.

Headlines get `text-wrap: balance`; summaries get `text-wrap: pretty`.

---

## 3. Colour

Dark-first, as specified. The neutral is biased slightly green-cyan so it
relates to the accent instead of being stock grey.

```
DARK (primary)
--bg           #0B0D11   ground, near-black with a faint cool cast
--surface      #12151B   raised: hero card, sidebar panels
--surface-2    #181C23   hover only
--border       #222731   hairlines
--border-lift  #2E353F   hover border
--text         #E9ECEF   primary
--text-2       #9AA3AE   metadata, summaries — AA on --bg
--text-3       #6E7681   least emphasis, never body copy
--accent       #3DDC97   Radar Green (specified)
--accent-quiet #1C5240   accent fill at low emphasis
--warn         #E8A33D
--danger       #E5565B

LIGHT
--bg           #FFFFFF
--surface      #F8F9FA
--surface-2    #F1F3F5
--border       #E4E7EB
--border-lift  #CFD5DC
--text         #101418
--text-2       #5B6570
--text-3       #7C8590
--accent       #0B8E5C   darkened — #3DDC97 fails contrast on white
--accent-quiet #D7F2E6
```

### Accent budget

Green appears in **at most five places per screen**. Permitted uses only:

- LIVE indicator dot
- active nav item
- links on hover
- "Worth trying" tick
- the hero story's left edge rule (1px, not a 4px bar)

Everything else is greyscale. If green appears anywhere not on that list, it is
a bug.

### Category colour

Categories are distinguished by **label text**, not colour fills. Each category
gets a muted hue applied *only* to its 10.5px mono label:

`model` violet · `tool` cyan · `update` blue · `pricing` amber ·
`research` slate · `news` green

Desaturated so six of them on one page do not compete. Never a background,
never a border, never a dot.

---

## 4. Layout

### Desktop (≥1080px)

A **two-column asymmetric** layout: 1fr content / 300px rail, 40px gutter,
max 1200px. Not a three-column dashboard.

The rail holds Trending, Worth Trying, and one ad slot — secondary by
construction. It starts *below* the daily overview so the overview runs full
width and clearly reads as the page's answer to "what happened today?"

### Hierarchy, top to bottom

Each tier is visually weaker than the one above. That progression *is* the
design.

| Tier | Treatment |
|---|---|
| 1. Daily overview | Full width. Serif display headline. Numbered story list. No card. |
| 2. Hero story | Raised surface, 1px accent rule on the left edge, serif title at 38px. The only element with a surface *and* an accent. |
| 3. Important stories | Two up. Surface, no accent. Serif title at 20px. |
| 4. Tools / Models | Compact cards, three or four up. Sans titles. Border only, no fill. |
| 5. Updates / Pricing | **Editorial rows.** No card at all — time, label, title, source on one line, separated by hairlines. |

Five tiers, four card treatments, and the densest tier has no card. That
enforces the brief's "not every item needs a heavy box".

### Mobile (<720px)

Single column. The tier order is preserved exactly — that is what keeps the
hierarchy legible on a phone.

- Hero keeps its serif title, drops to 28px
- Tiers 3–4 become full-width rows with a tighter summary
- Tier 5 is unchanged (rows already work)
- Rail content moves to the bottom, minus the ad
- Tap targets ≥44px
- Sticky 52px header, title only

### Spacing

4px base. Section gap **56px** desktop / 40px mobile — the main instrument of
"calm". Card padding 20px. Row padding 14px vertical.

Sections separate by **space plus a small caps header**, not by boxes or heavy
rules. One hairline above each section header, nothing else.

---

## 5. Cards

Exactly four, and no fifth may be added.

**Hero** — surface fill, 1px border, 1px accent left rule, 24px padding.
Category label · title (serif 38) · summary (16/27) · why-it-matters ·
worth-trying · source line.

**Standard** — surface fill, 1px border, 20px padding, serif title 20px.
Category · title · summary · why-it-matters · source.

**Compact** — no fill, 1px border, 16px padding, sans title 16.5px.
Category · title · one-line summary · worth-trying tick if set.

**Row** — no fill, no border, hairline separator only.
`time · LABEL · title · source` on one line, wrapping to two on mobile.

### Why-it-matters

This is the product's differentiator, so it gets a distinct but quiet
treatment: a 10.5px mono `WHY IT MATTERS` label above the text, with the text
at `--text` while the summary sits at `--text-2`. No icon, no coloured box, no
left border. The label does the work.

---

## 6. Motion

One ambient animation on the whole site: the LIVE dot, 6px, 2s opacity loop
between 1 and 0.35. That is the entire radar motif.

Everything else is state-change only:
- Card hover: border → `--border-lift`, 150ms ease-out. No lift, no scale, no
  shadow in dark (shadow does nothing on near-black).
- Link hover: colour → accent, 120ms.
- Theme change: 200ms on background and colour.

All of it behind `prefers-reduced-motion`.

Loading states are skeletons matching the real block's dimensions — never
spinners, never layout shift. Empty states are written sentences, not
illustrations: *"A quiet day in AI — 3 things worth knowing."* A quiet day is a
valid outcome the design has to handle gracefully, since publishing volume
follows quality.

---

## 7. Ads

Approved inventory, unchanged: desktop 3 (rail, top-feed, between-sections),
mobile 2 (feed, between-sections), story page 2 (middle, bottom).

Rules enforced in the component:

- 56px clear space above and below. Ads are never crowded against content.
- A 10.5px mono `ADVERTISEMENT` label, `--text-3`, above every slot.
- Renders `null` when disabled or unfilled. No empty boxes, no reserved
  collapse.
- First homepage ad sits after the hero and important stories — the reader has
  already had the value.
- On a story page, the first ad is after `why_it_matters`. **Never** between
  title and summary.
- No sticky or anchor ads in v1.

Sponsored content, when it exists: `SPONSORED` in the category slot, same
muted treatment, and excluded from importance ranking entirely.

---

## 8. Component inventory

Deliberately small. Anything not on this list needs a reason.

```
layout/    Header  Nav  Footer  ThemeToggle  LiveDot  Container
digest/    DailyOverview  TopStoryList  SectionCounts
cards/     HeroCard  StandardCard  CompactCard  StoryRow
story/     StoryHeader  FieldBlock  WorthTrying  SourceBlock  RelatedStories
sections/  SectionHeader  CardGrid  RowList
rail/      TrendingList  WorthTryingList
ads/       AdSlot
ui/        CategoryLabel  TimeAgo  Skeleton  EmptyState
```

`CategoryLabel`, `TimeAgo` and `AdSlot` are each used everywhere, which is how
consistency gets enforced structurally rather than by discipline.

---

## 9. Self-check against the brief

| Requirement | How this delivers it |
|---|---|
| Extremely easy to read | 66ch measure, 1.65+ line height, nothing under 12.5px, serif headlines |
| Extremely easy to scan | Five tiers of decreasing weight; category label always first |
| Clean | Four card types; hairlines not boxes; whitespace as the separator |
| Modern | Newsreader + Inter Tight is a current pairing, not a default one |
| Professional | Editorial serif, restrained palette, no effects |
| Advanced | Mono metadata, precise spacing, considered hover states |
| Premium | Generous 56px section rhythm; nothing crowded |
| Calm | One animation; five accent uses per screen; no colour fills |
| Organized | One 4px spacing scale, one type scale, no exceptions |
| Visually clear | Hierarchy carried by size and weight, never by colour |
| No dashboard clutter | No stat-tile grid; rail is secondary and starts below the fold |
| Mobile primary | Tier order preserved; 44px targets; rail demoted, not stacked |

---

## 10. Open question

**The serif headline is the one real opinion here.** It is what makes AI Radar
read as a publication rather than a tool, and it is the choice most likely to
be wrong for your taste.

The alternative is all-sans: safer, more "AI product", less distinctive. If you
would rather have that, say so and I will swap the display face — everything
else in this document holds either way, so it is a one-token change rather than
a redesign.

I am building with the serif unless you tell me otherwise.

---

## 11. Bilingual: Arabic + English

Added after the initial spec was locked. Sections 1–10 hold unchanged; this
section says how they apply in Arabic.

### Decisions

| Question | Decision |
|---|---|
| Arabic content | **Translated from verified English.** One Haiku call after a post passes verification. |
| URLs | **`/en/…` and `/ar/…`**, both prefixed. `/` redirects on `Accept-Language`. |
| Slugs | **English slug in both languages.** `/ar/models/claude-opus-5`. |

Translating rather than writing twice is the safety argument, not just the
cost one: Arabic is then a faithful rendering of text that already passed
fact-checking, so the two languages can never state different numbers. Cost is
~$1.40/month at 12 posts/day with Haiku — Sonnet would be $4.19 and push total
spend past the $10 cap.

### Typography in Arabic

The Latin type pairing does not carry over. Arabic needs its own faces:

| Role | Latin | Arabic |
|---|---|---|
| Display | Newsreader | **Noto Kufi Arabic** — geometric, editorial, works at headline weight |
| Body | Inter Tight | **IBM Plex Sans Arabic** — excellent screen legibility, wide weight range |
| Metadata | JetBrains Mono | **IBM Plex Sans Arabic** at 500 |

Arabic has no mono tradition for metadata, and forcing a Latin mono onto Arabic
numerals looks wrong. So in Arabic the mono role is dropped: metadata uses Plex
Sans Arabic with letter-spacing at `0`, since letter-spacing breaks Arabic
letter joining.

Two required adjustments, both real legibility issues rather than preferences:

- **Arabic runs ~1.15× larger.** Arabic letterforms carry more detail at the
  same nominal size. Body goes 16px → 17px, hero 38px → 40px.
- **Line height increases.** Arabic ascenders and descenders need more room:
  1.65 → 1.85 on body text.
- **No uppercase transform.** Arabic has no case. `text-transform: uppercase`
  is a no-op at best and must be removed so labels do not gain stray spacing.
- **Numerals stay Western** (`0-9`). Tech audiences in the Gulf and Levant read
  Western digits for versions and prices; Eastern Arabic numerals would make
  "GPT-5" read oddly.

### RTL layout

`dir="rtl"` on `<html>` for Arabic. Everything already uses logical
properties (`padding-inline`, `inset-inline-start`, `margin-inline`) so the
layout mirrors without per-rule overrides. What does need attention:

- The hero's accent rule uses `inset-inline-start`, so it moves to the right
  edge in Arabic. Correct — it stays on the reading-start side.
- Numbered lists, the rail, and card grids all mirror automatically via grid
  and flex.
- The `↗` on outbound links and `→` on "view all" must flip to `↖` and `←`.
  These are content, not layout, so they need explicit handling.
- Latin product names inside Arabic prose ("Claude Opus 5") stay LTR inside the
  RTL paragraph. The browser bidi algorithm handles this; do not fight it.

### SEO

- `hreflang` pairs on every page, plus `x-default` pointing at `/en`.
- Canonical per language: `/ar/models/x` canonicals to itself, never to `/en`.
- `lang` and `dir` on `<html>` per locale.
- Both languages in the sitemap, with `xhtml:link` alternates.
- The language switcher is a real link, not JavaScript, so crawlers follow it.

### What Arabic does NOT get

- No separate editorial judgement. Importance, category and verification are
  decided once, in English.
- No Arabic-only sources in v1. Adding Arabic-language AI publications is a
  later decision that needs its own trust-tier review.
- No machine-translated UI strings. The interface dictionary is hand-written,
  because a mistranslated nav label is more damaging than a slightly stiff
  story summary.
