File size: 22,243 Bytes
79cf005
 
 
 
 
 
 
 
 
85dca58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79cf005
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85dca58
79cf005
85dca58
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# Handoff: Riprap Findings Region

**Current target version: v0.4.5** (polish on v0.4.4 β€” now applied in `Riprap Stone-Grouped UI v0.4.5.html`). See `V0.4.5_SPEC.md` for the nine deltas. This README is the v0.4.4 specification and remains the reference for everything *not* changing in v0.4.5.

**Read order for the implementer:**
1. `CLAUDE_CODE_PROMPT.md` β€” paste into Claude Code
2. `V0.4.5_SPEC.md` β€” the nine fixes (now realized in `v0.4.5.html`)
3. `README.md` (this file) β€” v0.4.4 reference
4. `design_files/` β€” prototypes (both v0.4.4 and v0.4.5 HTMLs are present)

## Overview

**Riprap** is a citation-grounded Flood Exposure Briefing tool for New York City. A user enters an address, neighborhood, or proposed development; Riprap returns a written briefing where every numeric claim links to its primary public-record source (FEMA, USGS, NYC DOITT, FloodNet, NYC OpenData, etc.).

This handoff covers the **Findings region** β€” the structured-data sibling of the briefing prose. It groups model outputs into five named "Stones" (cognitive roles), each rendered as a card stack with explicit epistemic tiering, smart provenance traces, and cross-linking to the map.

The target codebase is **SvelteKit** (Svelte 5 with runes). The files in `design_files/` are React-based prototypes β€” design references, not production code. Recreate them as Svelte components.

## About the Design Files

The files in `design_files/` are **design references created in HTML + React** β€” high-fidelity prototypes showing the intended look and behavior. They are **not production code to copy directly**.

Your task is to **recreate these designs in the existing Svelte codebase**, using its established patterns (Svelte 5 runes, scoped styles, the project's existing route structure and data layer). Lift visual values, copy, and interaction logic from the references; do not transpile JSX to Svelte.

**File pairing** (each prototype area has one or two source files):

- `Riprap Stone-Grouped UI v0.4.4.html` β€” main prototype, the v0.4.4 Findings region in context
- `findings.jsx` β€” Findings region: stones, cards, run-health, grammar reference (the centerpiece of this handoff)
- `briefing.jsx` β€” long-form briefing prose with inline citations
- `map.jsx` β€” mini-map with FEMA AE / HWM / FloodNet / 311 / address layers and link highlighting
- `trace.jsx`, `stones-trace.jsx`, `stone-evidence.jsx` β€” provenance trace variants
- `shell.jsx` β€” app header, footer, cold-start state
- `glyphs.jsx` β€” four tier glyphs as inline SVG
- `tokens.css` β€” design tokens (colors, type, spacing). Port verbatim.
- `styles.css` β€” component CSS. Reference only; rewrite as scoped Svelte styles.
- `tweaks-panel.jsx`, `design-canvas.jsx`, `landing-variants.*` β€” prototype-time tooling. Ignore.

## Fidelity

**High-fidelity (hifi).** Pixel-perfect mockups with final colors, typography, spacing, glyphs, and interactions. Recreate the UI pixel-perfectly using the codebase's existing libraries and patterns. The tier color values, the IBM Plex font stack, the 4/8/12/16/24/32/48/64/96 spacing scale, and the four-tier glyph system are all final.

The Findings region in particular has been through several iterations (v0.4.0 β†’ v0.4.4) and is settled. Don't redesign card layouts; port them as-is.

## Screens / Views

The product is a single-page app with two states: **cold-start** (no query) and **briefing** (query active).

### 1. Cold-start

Empty state with the wordmark, a deck explaining what Riprap is, a query input, three sample-query buttons, and a "How Riprap is built" trust band. See `shell.jsx` β†’ `<ColdStart>`.

- **Layout**: centered single column, max-width ~720px, paper background (`--paper`)
- **Wordmark**: `riprap` lowercase, IBM Plex Mono 14px / 600, with a 0.85em accent bar `β–Œ` prefix
- **Deck**: serif paragraph, 18px, line-height 1.55, ink-secondary
- **Query input**: full width, 1px ink border, mono placeholder, 16px
- **Submit**: ink fill, paper text, mono caps, 13px
- **Sample queries**: three buttons in a column, each shows mode (caps mono) / query (sans 16px) / sub (mono 12px tertiary)
- **Trust band**: section-label heading, italic-serif "Cornerstone remembers. Keystone tallies. Touchstone watches. Lodestone projects. Capstone writes it all down with citations." then a bullet list

### 2. Briefing (active query)

Three-region layout, vertical stack:

1. **App header** (sticky top): wordmark Β· context Β· query pill Β· methodology / export PDF / live status
2. **Briefing prose region**: long-form text with inline `[N]` citations, three side-by-side panes (excerpt Β· evidence cards Β· mini map)
3. **Findings region**: the v0.4.4 Stone-grouped card stack (this handoff's focus)
4. **Footer**: tier legend + build line

The Findings region is the substantial new surface. Everything below documents it.

## Findings Region (v0.4.4) β€” detailed spec

### Composition

```
<FindingsRegion>
  <RunHealthStrip />               ← 1 row, top, summarizes all 25 model calls
  <StoneRegion stone="cornerstone" />
  <StoneRegion stone="keystone" />
  <StoneRegion stone="touchstone" />
  <StoneRegion stone="lodestone" />
  <StoneRegion stone="capstone" />
  <CardGrammarReference />         ← optional, on by default; one stub per variant
</FindingsRegion>
```

### Stones

Five fixed roles, in order:

| key | name | role | tag |
|---|---|---|---|
| `cornerstone` | Cornerstone | the hazard reader | what NYC's ground remembers |
| `keystone` | Keystone | the asset register | what's exposed |
| `touchstone` | Touchstone | the present-tense witness | what's happening now |
| `lodestone` | Lodestone | the future-pointer | what's coming |
| `capstone` | Capstone | the writer | what we say, with citations |

Each `<StoneRegion>` has:

- **Header**: Stone name (IBM Plex Serif 26px italic for the name, sans for the role) Β· role tagline Β· `<StoneTally>` chip showing `N/M cards fired` Β· provenance toggle button
- **Provenance trace**: smart-default expansion (see below). Renders specialist tree with status pips.
- **Card grid**: 12-column grid, each card spans 4 cols by default (3 per row); register/timeseries/raster cards may span 6.

### Card data schema

```ts
type Card = {
  stone: "cornerstone" | "keystone" | "touchstone" | "lodestone" | "capstone";
  tier: "empirical" | "modeled" | "proxy" | "synthetic";
  variant: CardVariant;       // see below
  source: string;             // short label, e.g. "FEMA"
  agency: string;             // long form, e.g. "FEMA preliminary FIRM, panel 36047C..."
  vintage: string;            // e.g. "2024-Q3" or "2007–present"
  title: string;              // card title
  // variant-specific fields:
  headline?: string; subhead?: string;
  columns?: string[]; rows?: (string | number)[][];
  scalars?: { label: string; value: string; unit?: string }[];
  spark?: number[]; histogram?: number[];
  timeseries?: { hours: number[]; values: number[]; threshold?: number };
  forecast?: { years: number[]; p10: number[]; p50: number[]; p90: number[] };
  raster?: "stormwater" | "fema-ae" | "hwm" | "floodnet-density" | ...;
  register?: { tag: string; label: string; sourceId: string; detail: string }[];
  comparison?: { left: ScalarSet; right: ScalarSet; delta: string };
  meta?: Record<string, string>;
  // citation fan-out:
  cites?: { id: string; label: string; href?: string }[];
  // map link:
  mapLayer?: "fema-ae" | "hwm" | "floodnet" | "nycha" | "address" | ...;
};
```

See `findings.jsx` lines 12–230 for the canonical `CARDS` table populated for the Red Hook query.

### Card grammar (12 variants)

Every card uses the same chrome β€” title row (sans 14/600), source Β· agency Β· vintage row (mono 11/tertiary), a body block, and a footer with the **tier badge** (3-letter caps mono) and a "cite" button. The body block is one of:

| variant | shape | use |
|---|---|---|
| `headline` | one big number/label, scenario-tagged subhead | single-fact cards: "Zone AE" |
| `tabular` | small NΓ—3 table, mono | observation lists: HWM marks |
| `scalars` | 2–3 labeled scalars in a row | "1.2 m Β· 0.18 mi Β· 2012" |
| `spark` | 60Γ—24 inline sparkline, no axes | trend at a glance |
| `histogram` | 8–12 bar histogram, mono labels | distributions |
| `timeseries` | 240Γ—84 line chart with threshold rule | hourly water level, 311 calls |
| `forecast` | 240Γ—88 fan chart (p10/p50/p90) | 2050/2080 SLR, surge |
| `raster` | 240Γ—120 stylized raster thumbnail | FEMA AE polygon, stormwater extent, HWM contour |
| `raster-pred` | same shape with dashed top-rule (synthetic tier) | TerraMind 2050 prediction |
| `register` | 3-col dense list (tag Β· label Β· sourceId), detail in `title=` tooltip | NYCHA buildings, schools |
| `comparison` | side-by-side scalar columns with delta | FEMA-AE vs Prithvi-2050 |
| `meta` | definition list of run metadata | model id, prompt hash, latency |

**Synthetic tier** cards (TerraMind predictions, prior-only Lodestone outputs) get a **dashed top-rule** (1px dashed `--tier-synthetic-line`) to telegraph "no observed data here." Comparison cards always render synthetic.

The `<CardGrammarReference>` region renders one stub per variant in `findings.jsx` lines 529–581 β€” a visual catalog the design team uses to spot-check fidelity. Keep it, gate it on `showGrammar` prop (default true in dev, false in prod).

### Tier system

Four epistemic tiers, encoded redundantly:

| tier | color | glyph | badge | meaning |
|---|---|---|---|---|
| `empirical` | `#0B5394` (8.59:1) | filled square | EMP | observed, ground-truth |
| `modeled` | `#2A6FA8` (5.41:1) | open square | MOD | computed from observations |
| `proxy` | `#6B6B6B` (5.74:1) | dotted ring | PRX | indirect signal, e.g. 311 calls |
| `synthetic` | `#2A6FA8` + dashed | hatched square | SYN | model prior, no observation |

Glyphs are inline SVG, 12Γ—12, black-stroke. See `glyphs.jsx` for the four shapes.

**Accessibility**: tier is *always* encoded by color + glyph + label, never color alone. Modeled and synthetic share a hue; the dashed top-rule and glyph carry the difference.

### Provenance trace

Every Stone has a tree of specialists ("CORN-001: pull FEMA NFHL β†’ CORN-002: parse panel index β†’ ..."). Each specialist has a status: `ok` / `warn` / `error` / `silent`.

**Smart-default rule** (`provenanceMode = "smart"`):

- All-`ok` Stone β†’ **collapsed** by default, single-line summary "12/12 specialists fired clean"
- Any `warn` or `error` β†’ **expanded**, full tree visible
- All `silent` β†’ collapsed, single-line "no firings (section omitted from briefing)"

`provenanceMode = "expanded"` forces all expanded; `"collapsed"` forces all collapsed (warn/error still get a count chip).

The trace UI: indented tree, mono ids, status pip (●/β–²/β– /β—‹) in tier-color or warn/error colors. Specialist names italic-serif. Hovering a specialist row dims the rest. See `trace.jsx` for the leaf and `stones-trace.jsx` for the per-Stone composition.

### Run-health strip

Single row above the Stones, full-width. Shows:

- Total specialists fired / total specialists registered (e.g. `83/100`)
- Per-tier breakdown as four chips: `EMP 41 Β· MOD 28 Β· PRX 12 Β· SYN 2`
- Total runtime (e.g. `3.4s`)
- Cache-hit ratio (e.g. `92%`)

Mono throughout. Background `--paper-deep`, 1px ink-soft top + bottom rule.

### Hover linking

Every card with a `mapLayer` is hoverable. On hover (or focus), the card's `key` becomes the page-level `linkedKey`. The briefing's map frame reads `linkedKey` and:

- Adds `is-link-{layer}` class to its root, which lights up that layer (see `map.jsx` for the CSS rules)
- Renders a small label badge bottom-right: "linked: {layer}"

The same applies in reverse: hovering a layer in the map sets `linkedKey` to the corresponding card key, which gets a 2px accent outline.

In Svelte: lift `linkedKey` to `+page.svelte` as `let linkedKey = $state(null)`, pass it down both branches, and have the cards / layers update it on `pointerenter` / `focus` / `pointerleave` / `blur`.

### Density

`density: "compact" | "comfortable"` (default `comfortable`).

- **Comfortable**: 16px card padding, 14px line-height multiplier 1.4
- **Compact**: 10px card padding, 12px line-height multiplier 1.25, register-card row height 18px (vs 24px)

Pass through to all card bodies; only register/tabular/meta visibly change.

## Interactions & Behavior

- **Card hover**: 200ms `background-color` transition to `--paper-deep`, 2px accent outline if linked
- **Cite button**: opens a small popover with the full citation list (`cites[]`), each row a link to the source PDF/page
- **Provenance toggle**: button with `aria-expanded`, animates the tree open/closed via `details`/`summary` or scoped `max-height` transition (≀200ms)
- **Map layer hover**: sets `linkedKey`, 100ms layer fill opacity transition
- **Reduced motion**: all transitions become 0.01ms via the global rule in `tokens.css`. Don't add motion on top.
- **Keyboard**: every card is `tabindex=0` with `aria-label="{tier} card Β· {title} Β· {source}"`. Cite button is real `<button>`. Provenance toggle is real `<button>`.
- **Loading**: cards show a 1px ink-soft skeleton (no spinner); replace with content when the specialist returns
- **Error / silent**: cards with status `error` render a 1-line "specialist failed: {reason}" in tier-error color and stay; cards with status `silent` are omitted entirely (silence over confabulation β€” design tenet)

## State Management

Svelte 5 runes; lift to `+page.svelte`:

```svelte
<script>
  let query = $state(null);
  let density = $state("comfortable");
  let provenanceMode = $state("smart");
  let showComparison = $state(false);
  let showGrammar = $state(false);   // dev-only toggle
  let linkedKey = $state(null);

  // Data: load once per query, hydrate from server
  let cardsByStone = $derived(loadCards(query));
  let runHealth = $derived(summarize(cardsByStone));
</script>
```

Children take props via `$props()`. No Svelte stores unless cross-route.

Data fetching: assume the existing codebase has a `+page.server.ts` load function that runs the 25 specialists and returns the `Card[]` payload. This handoff doesn't change the data layer; it changes the rendering.

## Design Tokens

Port `tokens.css` verbatim. Key values:

**Tier colors** (all WCAG AA on white):
- `--tier-empirical: #0B5394` (8.59:1)
- `--tier-modeled: #2A6FA8` (5.41:1)
- `--tier-proxy: #6B6B6B` (5.74:1)
- `--tier-synthetic: #2A6FA8` (pattern-differentiated)

**Neutrals**:
- `--paper: #FAFAF7` (warm near-white, USGS-report register)
- `--paper-deep: #F2F2EE`
- `--ink: #1A1A1A` Β· `--ink-secondary: #4A4A4A` Β· `--ink-tertiary: #6B6B6B`
- `--rule: #1A1A1A` Β· `--rule-soft: #C9C9C5`

**Accent**:
- `--accent: #B8620A` (for text, AA)
- `--accent-graphical: #D17C00` (for shapes/lines, β‰₯3:1)

**Type**:
- `--font-sans: "IBM Plex Sans"` (UI, body)
- `--font-serif: "IBM Plex Serif"` (Stone names, hero italic emphasis, oversized stone numerals)
- `--font-mono: "IBM Plex Mono"` (labels, source ids, badges, table cells)

All three are SIL OFL / Apache; self-host or load from Google Fonts. **No system fallbacks for branding** β€” always specify the Plex stack, fall through only on load failure.

**Spacing** (`--s-1` through `--s-9`): 4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 / 96 px. Use these tokens; don't hand-roll values.

**Type scale** (suggested, not enforced):
- 11px mono labels (`section-label`)
- 12–13px mono row text
- 14px sans card titles (600)
- 16px sans body
- 18px sans deck text
- 26px serif italic Stone names
- 36–52px serif headlines (italic for emphasis)

**Radius**: 0 throughout (this is a civic-tech-clean, USGS-report register; no rounded corners except `1px` on focus rings).

**Shadows**: none. Differentiation by 1px rules and `--paper-deep` fills only.

## Assets

- **Fonts**: IBM Plex Sans / Serif / Mono. Self-host woff2 or Google Fonts.
- **Wordmark**: text + accent bar `β–Œ` prefix, no logo file
- **Tier glyphs**: inline SVG, see `glyphs.jsx`
- **Map raster thumbnails**: hand-drawn SVG approximations using each layer's conventional palette. See `findings.jsx` β†’ `RasterThumb`. Replace with real raster previews if/when MapLibre tile snapshots are wired up.
- **Real map**: the production map should use **MapLibre GL** with a custom `style.json` that respects the tier palette. Style fragments are sketched in `shell.jsx` comments.
- **No icon library.** No Lucide, no Heroicons. SVG glyphs for tiers, mono characters (βŒ•, β†—, β–Œ) for chrome.
- **No emoji.**

## Files in this bundle

```
design_handoff_riprap_findings/
β”œβ”€β”€ CLAUDE_CODE_PROMPT.md        ← paste this into Claude Code first
β”œβ”€β”€ README.md                     ← you are here
└── design_files/
    β”œβ”€β”€ Riprap Stone-Grouped UI v0.4.4.html   ← main prototype, open in browser
    β”œβ”€β”€ Riprap Landing.html
    β”œβ”€β”€ Riprap Landing Variants.html
    β”œβ”€β”€ tokens.css                ← port verbatim
    β”œβ”€β”€ styles.css                ← reference only
    β”œβ”€β”€ findings.jsx              ← Findings region (this handoff's centerpiece)
    β”œβ”€β”€ briefing.jsx              ← long-form prose
    β”œβ”€β”€ evidence.jsx              ← evidence card stack used in briefing
    β”œβ”€β”€ map.jsx                   ← mini-map with link highlighting
    β”œβ”€β”€ trace.jsx
    β”œβ”€β”€ stones-trace.jsx
    β”œβ”€β”€ stone-evidence.jsx
    β”œβ”€β”€ shell.jsx                 ← header, footer, cold-start
    β”œβ”€β”€ glyphs.jsx                ← four tier glyphs
    β”œβ”€β”€ tweaks-panel.jsx          ← prototype tooling, ignore
    β”œβ”€β”€ design-canvas.jsx         ← prototype tooling, ignore
    β”œβ”€β”€ landing-variants.css      ← marketing-page exploration, ignore unless asked
    └── landing-variants.jsx      ← marketing-page exploration, ignore unless asked
```

## Scope

**In scope** for this handoff:

- The Findings region (5 Stones, 12 card variants, run-health strip, smart provenance, hover linking, card grammar reference)
- Briefing prose and map, to the extent they connect to Findings via `linkedKey`
- The marketing **landing page** (see Β§"Landing page" below)
- v0.4.5 deltas in `V0.4.5_SPEC.md`

**Out of scope**: the methodology PDF, the export-PDF flow.

## Landing page

The landing page is the public-facing entry point at `/` (separate from the app's cold-start state at `/app`). Two design files in `design_files/`:

- `Riprap Landing.html` β€” final shipping landing page
- `Riprap Landing Variants.html` β€” three exploratory variants on a design canvas (kept for reference; do not port)

### Landing structure (port `Riprap Landing.html`)

Four-section vertical scroll, max-width 1200px, paper background:

1. **Hero**
   - Wordmark top-left (with `β–Œ` accent prefix)
   - Headline (52px serif, italic emphasis on *any place*, line-broken into two lines)
   - Deck (18px sans, max 70ch, ink-secondary)
   - Big query box: "Try:" label + cycling example queries on a fixed dotted-underline rail (rail width pinned, ellipsis fallback for overflow)
   - Submit button (ink fill, paper text, mono caps)

2. **"What you'll get back" preview** β€” 3-pane grid (1.4fr / 1fr / 1fr), bottoms equalized:
   - **Excerpt pane**: serif briefing snippet with inline `[N]` citation pins; compact source list at bottom (tier-coded)
   - **Evidence cards pane**: 2x2 grid of compact evidence cards, each with tier-coded left rule (2px), claim, source
   - **Map pane**: mini SVG map (240x200, 8px paper-grain grid texture) with FEMA-AE polygon fill, HWM contour, FloodNet sensor pin, 311 cluster, address pin, and a compact tier-legend overlay bottom-edge

3. **Five Stones strip** β€” explanation grid: 5 columns (`repeat(5, 1fr)`), one cell per Stone:
   - Oversized italic-serif numerals `01..05` top-right of each cell (in `--rule-soft`, decorative)
   - Stone name (serif 22px / 500)
   - Role tagline (sans 13px / ink-secondary)
   - Italic-serif tag ("what NYC's ground remembers" etc.)
   - Dashed rule + mono source list at bottom

4. **Footer** β€” earns its keep:
   - Tier legend (4 swatches: empirical / modeled / proxy / synthetic)
   - Build line (mono, ink-tertiary)

### Landing β€” what's settled (don't redesign)

- The hero dropped the redundant "Riprap" eyebrow; the wordmark already says it. Headline carries the lede.
- Cycling examples ride a single fixed dotted underline (no jump on length change). Pin the rail width; ellipsis-truncate overflow.
- Pane heights are equalized via a flex-1 spacer in the cards pane and `flex: 1` on the map pane.
- Map texture is a subtle 8px paper-grain grid behind the colored layers β€” gives it a map register, not a diagram register.
- Stones strip uses oversized italic numerals as a typographic register (decorative, in soft-rule color).
- Italic serif is intentional: hero emphasis + Stone tags + Stone numerals. Reads as a deliberate third voice.

### Landing β€” em-dashes

All user-facing em-dashes have been purged from `Riprap Landing.html`, `landing-variants.css`, and `landing-variants.jsx`. Replacement convention: `, ` (comma-space) for em-dashes used as parenthetical breaks. **Maintain this convention** when porting copy. Em-dashes in source-code comments (non-rendered) are fine.

### Landing β€” out of scope variants

`Riprap Landing Variants.html` contains three earlier explorations on a design canvas: v1 (minimal pushed harder), v2 (example gallery), v3 (methodology-forward). Kept for reference only; **do not port**. The shipping landing page is `Riprap Landing.html` (a refinement of v1).

## Open questions for the design team

These are deliberately *not* resolved in the prototype; raise them with the designer before locking implementation:

1. Should `register` cards paginate when N > 20, or stay scrollable in a fixed-height card?
2. The `comparison` card is currently always synthetic (FEMA-AE vs Prithvi-2050). Will there be empirical-vs-empirical comparisons (e.g. FEMA-AE vs HWM observed)?
3. Run-health cache-hit ratio β€” is this surfaced to end users, or is it dev-mode only?
4. Provenance trace expansion: should expanded state survive across query changes, or reset?