# 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` → ``. - **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 ``` ← 1 row, top, summarizes all 25 model calls ← optional, on by default; one stub per variant ``` ### 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 `` has: - **Header**: Stone name (IBM Plex Serif 26px italic for the name, sans for the role) · role tagline · `` 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; // 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 `` 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 `