Spaces:
Configuration error
Configuration error
merge ux/findings-v0.4.4: ship the v0.4.4 + v0.4.5 design pass
Browse files19 commits land the full v0.4.4 Findings region (5 Stones, 12 card
variants, FSM-state-driven streaming, smart provenance) on top of the
Stones architecture, then the nine v0.4.5 polish deltas (status-state
split, Capstone meta-card plumbing, full provenance roster, TerraMind
LULC + fine-tuned TTM cards, Stone-tinted accents, hover linking,
Stone-grouped LAYERS panel) and the new marketing landing at /. The
legacy custom-element fallback routes are retired.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This view is limited to 50 files because it contains too many changes. See raw diff
- docs/design_handoff/CLAUDE_CODE_PROMPT.md +43 -0
- docs/design_handoff/README.md +401 -0
- docs/design_handoff/V0.4.5_SPEC.md +313 -0
- docs/design_handoff/design_files/Riprap Landing Variants.html +34 -0
- docs/design_handoff/design_files/Riprap Landing.html +339 -0
- docs/design_handoff/design_files/Riprap Stone-Grouped UI v0.4.4.html +369 -0
- docs/design_handoff/design_files/Riprap Stone-Grouped UI v0.4.5.html +369 -0
- docs/design_handoff/design_files/briefing.jsx +312 -0
- docs/design_handoff/design_files/design-canvas.jsx +936 -0
- docs/design_handoff/design_files/evidence.jsx +257 -0
- docs/design_handoff/design_files/findings.jsx +883 -0
- docs/design_handoff/design_files/glyphs.jsx +126 -0
- docs/design_handoff/design_files/landing-variants.css +89 -0
- docs/design_handoff/design_files/landing-variants.jsx +266 -0
- docs/design_handoff/design_files/map.jsx +228 -0
- docs/design_handoff/design_files/shell.jsx +142 -0
- docs/design_handoff/design_files/stone-evidence.jsx +140 -0
- docs/design_handoff/design_files/stones-trace.jsx +219 -0
- docs/design_handoff/design_files/styles.css +1274 -0
- docs/design_handoff/design_files/tokens.css +171 -0
- docs/design_handoff/design_files/trace.jsx +166 -0
- docs/design_handoff/design_files/tweaks-panel.jsx +425 -0
- web/main.py +17 -39
- web/sveltekit/build/200.html +15 -15
- web/sveltekit/build/_app/immutable/assets/0.KpTzaSsX.css +0 -0
- web/sveltekit/build/_app/immutable/assets/0.MHa9rmVP.css +0 -0
- web/sveltekit/build/_app/immutable/assets/2.CWtxLGO7.css +1 -0
- web/sveltekit/build/_app/immutable/assets/4.BIuIAgmk.css +1 -0
- web/sveltekit/build/_app/immutable/assets/4.CPUwsEjs.css +0 -1
- web/sveltekit/build/_app/immutable/assets/{Briefing.Cg0TTl7h.css → Briefing.Dmn9LgiV.css} +1 -1
- web/sveltekit/build/_app/immutable/assets/MapLegend.DvDgr167.css +0 -1
- web/sveltekit/build/_app/immutable/assets/stoneRegistry.bHiraU77.css +1 -0
- web/sveltekit/build/_app/immutable/chunks/5ANqR9hF.js +1 -0
- web/sveltekit/build/_app/immutable/chunks/B4YyPI_Q.js +1 -0
- web/sveltekit/build/_app/immutable/chunks/B7gjWklj.js +0 -1
- web/sveltekit/build/_app/immutable/chunks/BClW_8sh.js +1 -0
- web/sveltekit/build/_app/immutable/chunks/BLULdth_.js +0 -2
- web/sveltekit/build/_app/immutable/chunks/BPaqPo1M.js +0 -27
- web/sveltekit/build/_app/immutable/chunks/BkOwEZvC.js +1 -0
- web/sveltekit/build/_app/immutable/chunks/{DO0D806X.js → BzAi_hbj.js} +1 -1
- web/sveltekit/build/_app/immutable/chunks/C8fGhQJC.js +27 -0
- web/sveltekit/build/_app/immutable/chunks/CW0UkEuV.js +0 -2
- web/sveltekit/build/_app/immutable/chunks/CYuHyzh3.js +0 -1
- web/sveltekit/build/_app/immutable/chunks/Chht4iZ-.js +2 -0
- web/sveltekit/build/_app/immutable/chunks/CpEmpa3I.js +0 -1
- web/sveltekit/build/_app/immutable/chunks/{DwPgZwgo.js → DM0dVcpA.js} +1 -1
- web/sveltekit/build/_app/immutable/chunks/DOnKrFbX.js +0 -1
- web/sveltekit/build/_app/immutable/chunks/Dr93mMbz.js +1 -0
- web/sveltekit/build/_app/immutable/chunks/{CdmpEGnB.js → LdqJVpAk.js} +1 -1
- web/sveltekit/build/_app/immutable/chunks/WYoL_k1G.js +2 -0
docs/design_handoff/CLAUDE_CODE_PROMPT.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Prompt for Claude Code · v0.4.5 polish session
|
| 2 |
+
|
| 3 |
+
Copy-paste this whole block into Claude Code as your opening message.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
v0.4.4 has shipped to the local development environment. The Findings region with the Five Stones, evidence cards, and Capstone meta-card are live and rendering against real queries via local uvicorn (`uvicorn web.main:app --host 127.0.0.1 --port 7860`). Screenshots from 80 Pioneer Street, Red Hook confirm the architecture working: four Stones producing genuinely different evidence-card kinds, each carrying its tier badge, the briefing prose with citations resolving cleanly, and the map with three tier-encoded layers.
|
| 8 |
+
|
| 9 |
+
**v0.4.5 is polish.** Nine specific issues observed in production-shaped local runs, plus a new Stone-tinted light-theming layer. **None of the fixes are structural. Don't rebuild anything.** Read the current implementations of `src/lib/components/findings/StoneRegion.svelte`, `src/lib/components/findings/EvidenceCard.svelte`, and `src/lib/components/findings/CapstoneCard.svelte` and apply the deltas described in `V0.4.5_SPEC.md`.
|
| 10 |
+
|
| 11 |
+
**Important context.**
|
| 12 |
+
|
| 13 |
+
- Public mirrors (GitHub, HF Space) were deleted pre-hackathon for caution and re-publish during the hackathon window. **The HF Space link is currently delisted; do not reference it.** All references in this work target local development.
|
| 14 |
+
- The codebase is **SvelteKit (Svelte 5 with runes)**. Stay in idiomatic Svelte: `$state`, `$derived`, `$props`, scoped `<style>` blocks. No React. No Tailwind. No animation libraries.
|
| 15 |
+
- The card grammar (header / body / footer / tier badge / source link), the four-section briefing prose, the Mellea reroll status strip, the four-tier color palette and glyphs, the cold-start state, the trust-signal footer, and the PDF template's core layout are **not changing**. Don't touch them.
|
| 16 |
+
|
| 17 |
+
**Read these files first, in order:**
|
| 18 |
+
|
| 19 |
+
1. `V0.4.5_SPEC.md` — the nine fixes, with file-level deltas, expected outputs, and acceptance criteria.
|
| 20 |
+
2. `README.md` — v0.4.4 spec for context (card grammar, tier system, data schema, hover-link contract). Reference, not rework.
|
| 21 |
+
3. `design_files/Riprap Stone-Grouped UI v0.4.5.html` — open in browser; this is the **visual target** with all nine v0.4.5 deltas applied.
|
| 22 |
+
4. `design_files/Riprap Stone-Grouped UI v0.4.4.html` — kept only for diff/reference against the previously shipped state.
|
| 23 |
+
|
| 24 |
+
**Order of work** (matches `V0.4.5_SPEC.md` priority):
|
| 25 |
+
|
| 26 |
+
1. **Status semantics first.** Split the FSM specialist status into `fired / silent_by_design / warned / errored / not_invoked`. Update the per-Stone summary and top tally aggregate counts to render the breakdown. Update provenance row visual treatment per status. This is the most important fix — it's the one actively misrepresenting system integrity.
|
| 27 |
+
2. **Capstone meta-card field plumbing.** Wire the four metrics to the reconciler's actual state fields. Acceptance: a clean Red Hook run shows `1 reroll · 4/4 grounding · 4 citations · 24.0s`.
|
| 28 |
+
3. **Provenance roster completeness.** Each Stone's expander always shows the full specialist inventory, never a filtered subset. Missing specialists render as `not_invoked` with one-line reasons.
|
| 29 |
+
4. **Touchstone card additions** (TerraMind LULC, Prithvi-NYC-Pluvial). If the specialists aren't firing yet, land the card components anyway so they're ready when the data is.
|
| 30 |
+
5. **Lodestone fine-tuned TTM card.** Add alongside the existing zero-shot card. Footer must include the HF model-card link, RMSE, and AMD MI300X badge.
|
| 31 |
+
6. **Drop the "anomaly" tag.** The new status counts make it redundant.
|
| 32 |
+
7. **LAYERS panel restructure.** Group by Stone, add the four new raster layers (default-off), include the explicit "no map layers — see Findings cards" label under Lodestone.
|
| 33 |
+
8. **Card-to-map hover linking.** Verify and ship the connection. Click-to-fitBounds() on register cards is new in v0.4.5.
|
| 34 |
+
9. **Stone-tinted accent colors.** Add the five `--stone-*` tokens with the proposed values (designer can adjust within constraints). Apply at the recommended placements: 3px left-rule on Stone region headers, 6px dot beside Stone names in the cold-start list, optional row tint on the methodology matrix. Print-media override drops all five to `#999`.
|
| 35 |
+
|
| 36 |
+
**What to verify before reporting done.**
|
| 37 |
+
|
| 38 |
+
- A query at 80 Pioneer Street, Red Hook produces a Findings region matching the "v0.4.5 ready looks like" section at the bottom of `V0.4.5_SPEC.md`.
|
| 39 |
+
- No regressions in the briefing prose, the Mellea status strip, the cold-start, the footer, the PDF template, or the existing map layers.
|
| 40 |
+
- New status messages match the engineering-honest voice in `V0.4.5_SPEC.md` §1 (no euphemism — `"no entrances within radius"`, not "no data found").
|
| 41 |
+
- Print stylesheet drops Stone tints to neutral gray.
|
| 42 |
+
|
| 43 |
+
A v0.4.5 implementation session is a few hours of focused polish, not a day. The structural work is already done.
|
docs/design_handoff/README.md
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Handoff: Riprap Findings Region
|
| 2 |
+
|
| 3 |
+
**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.
|
| 4 |
+
|
| 5 |
+
**Read order for the implementer:**
|
| 6 |
+
1. `CLAUDE_CODE_PROMPT.md` — paste into Claude Code
|
| 7 |
+
2. `V0.4.5_SPEC.md` — the nine fixes (now realized in `v0.4.5.html`)
|
| 8 |
+
3. `README.md` (this file) — v0.4.4 reference
|
| 9 |
+
4. `design_files/` — prototypes (both v0.4.4 and v0.4.5 HTMLs are present)
|
| 10 |
+
|
| 11 |
+
## Overview
|
| 12 |
+
|
| 13 |
+
**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.).
|
| 14 |
+
|
| 15 |
+
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.
|
| 16 |
+
|
| 17 |
+
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.
|
| 18 |
+
|
| 19 |
+
## About the Design Files
|
| 20 |
+
|
| 21 |
+
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**.
|
| 22 |
+
|
| 23 |
+
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.
|
| 24 |
+
|
| 25 |
+
**File pairing** (each prototype area has one or two source files):
|
| 26 |
+
|
| 27 |
+
- `Riprap Stone-Grouped UI v0.4.4.html` — main prototype, the v0.4.4 Findings region in context
|
| 28 |
+
- `findings.jsx` — Findings region: stones, cards, run-health, grammar reference (the centerpiece of this handoff)
|
| 29 |
+
- `briefing.jsx` — long-form briefing prose with inline citations
|
| 30 |
+
- `map.jsx` — mini-map with FEMA AE / HWM / FloodNet / 311 / address layers and link highlighting
|
| 31 |
+
- `trace.jsx`, `stones-trace.jsx`, `stone-evidence.jsx` — provenance trace variants
|
| 32 |
+
- `shell.jsx` — app header, footer, cold-start state
|
| 33 |
+
- `glyphs.jsx` — four tier glyphs as inline SVG
|
| 34 |
+
- `tokens.css` — design tokens (colors, type, spacing). Port verbatim.
|
| 35 |
+
- `styles.css` — component CSS. Reference only; rewrite as scoped Svelte styles.
|
| 36 |
+
- `tweaks-panel.jsx`, `design-canvas.jsx`, `landing-variants.*` — prototype-time tooling. Ignore.
|
| 37 |
+
|
| 38 |
+
## Fidelity
|
| 39 |
+
|
| 40 |
+
**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.
|
| 41 |
+
|
| 42 |
+
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.
|
| 43 |
+
|
| 44 |
+
## Screens / Views
|
| 45 |
+
|
| 46 |
+
The product is a single-page app with two states: **cold-start** (no query) and **briefing** (query active).
|
| 47 |
+
|
| 48 |
+
### 1. Cold-start
|
| 49 |
+
|
| 50 |
+
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>`.
|
| 51 |
+
|
| 52 |
+
- **Layout**: centered single column, max-width ~720px, paper background (`--paper`)
|
| 53 |
+
- **Wordmark**: `riprap` lowercase, IBM Plex Mono 14px / 600, with a 0.85em accent bar `▌` prefix
|
| 54 |
+
- **Deck**: serif paragraph, 18px, line-height 1.55, ink-secondary
|
| 55 |
+
- **Query input**: full width, 1px ink border, mono placeholder, 16px
|
| 56 |
+
- **Submit**: ink fill, paper text, mono caps, 13px
|
| 57 |
+
- **Sample queries**: three buttons in a column, each shows mode (caps mono) / query (sans 16px) / sub (mono 12px tertiary)
|
| 58 |
+
- **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
|
| 59 |
+
|
| 60 |
+
### 2. Briefing (active query)
|
| 61 |
+
|
| 62 |
+
Three-region layout, vertical stack:
|
| 63 |
+
|
| 64 |
+
1. **App header** (sticky top): wordmark · context · query pill · methodology / export PDF / live status
|
| 65 |
+
2. **Briefing prose region**: long-form text with inline `[N]` citations, three side-by-side panes (excerpt · evidence cards · mini map)
|
| 66 |
+
3. **Findings region**: the v0.4.4 Stone-grouped card stack (this handoff's focus)
|
| 67 |
+
4. **Footer**: tier legend + build line
|
| 68 |
+
|
| 69 |
+
The Findings region is the substantial new surface. Everything below documents it.
|
| 70 |
+
|
| 71 |
+
## Findings Region (v0.4.4) — detailed spec
|
| 72 |
+
|
| 73 |
+
### Composition
|
| 74 |
+
|
| 75 |
+
```
|
| 76 |
+
<FindingsRegion>
|
| 77 |
+
<RunHealthStrip /> ← 1 row, top, summarizes all 25 model calls
|
| 78 |
+
<StoneRegion stone="cornerstone" />
|
| 79 |
+
<StoneRegion stone="keystone" />
|
| 80 |
+
<StoneRegion stone="touchstone" />
|
| 81 |
+
<StoneRegion stone="lodestone" />
|
| 82 |
+
<StoneRegion stone="capstone" />
|
| 83 |
+
<CardGrammarReference /> ← optional, on by default; one stub per variant
|
| 84 |
+
</FindingsRegion>
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
### Stones
|
| 88 |
+
|
| 89 |
+
Five fixed roles, in order:
|
| 90 |
+
|
| 91 |
+
| key | name | role | tag |
|
| 92 |
+
|---|---|---|---|
|
| 93 |
+
| `cornerstone` | Cornerstone | the hazard reader | what NYC's ground remembers |
|
| 94 |
+
| `keystone` | Keystone | the asset register | what's exposed |
|
| 95 |
+
| `touchstone` | Touchstone | the present-tense witness | what's happening now |
|
| 96 |
+
| `lodestone` | Lodestone | the future-pointer | what's coming |
|
| 97 |
+
| `capstone` | Capstone | the writer | what we say, with citations |
|
| 98 |
+
|
| 99 |
+
Each `<StoneRegion>` has:
|
| 100 |
+
|
| 101 |
+
- **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
|
| 102 |
+
- **Provenance trace**: smart-default expansion (see below). Renders specialist tree with status pips.
|
| 103 |
+
- **Card grid**: 12-column grid, each card spans 4 cols by default (3 per row); register/timeseries/raster cards may span 6.
|
| 104 |
+
|
| 105 |
+
### Card data schema
|
| 106 |
+
|
| 107 |
+
```ts
|
| 108 |
+
type Card = {
|
| 109 |
+
stone: "cornerstone" | "keystone" | "touchstone" | "lodestone" | "capstone";
|
| 110 |
+
tier: "empirical" | "modeled" | "proxy" | "synthetic";
|
| 111 |
+
variant: CardVariant; // see below
|
| 112 |
+
source: string; // short label, e.g. "FEMA"
|
| 113 |
+
agency: string; // long form, e.g. "FEMA preliminary FIRM, panel 36047C..."
|
| 114 |
+
vintage: string; // e.g. "2024-Q3" or "2007–present"
|
| 115 |
+
title: string; // card title
|
| 116 |
+
// variant-specific fields:
|
| 117 |
+
headline?: string; subhead?: string;
|
| 118 |
+
columns?: string[]; rows?: (string | number)[][];
|
| 119 |
+
scalars?: { label: string; value: string; unit?: string }[];
|
| 120 |
+
spark?: number[]; histogram?: number[];
|
| 121 |
+
timeseries?: { hours: number[]; values: number[]; threshold?: number };
|
| 122 |
+
forecast?: { years: number[]; p10: number[]; p50: number[]; p90: number[] };
|
| 123 |
+
raster?: "stormwater" | "fema-ae" | "hwm" | "floodnet-density" | ...;
|
| 124 |
+
register?: { tag: string; label: string; sourceId: string; detail: string }[];
|
| 125 |
+
comparison?: { left: ScalarSet; right: ScalarSet; delta: string };
|
| 126 |
+
meta?: Record<string, string>;
|
| 127 |
+
// citation fan-out:
|
| 128 |
+
cites?: { id: string; label: string; href?: string }[];
|
| 129 |
+
// map link:
|
| 130 |
+
mapLayer?: "fema-ae" | "hwm" | "floodnet" | "nycha" | "address" | ...;
|
| 131 |
+
};
|
| 132 |
+
```
|
| 133 |
+
|
| 134 |
+
See `findings.jsx` lines 12–230 for the canonical `CARDS` table populated for the Red Hook query.
|
| 135 |
+
|
| 136 |
+
### Card grammar (12 variants)
|
| 137 |
+
|
| 138 |
+
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:
|
| 139 |
+
|
| 140 |
+
| variant | shape | use |
|
| 141 |
+
|---|---|---|
|
| 142 |
+
| `headline` | one big number/label, scenario-tagged subhead | single-fact cards: "Zone AE" |
|
| 143 |
+
| `tabular` | small N×3 table, mono | observation lists: HWM marks |
|
| 144 |
+
| `scalars` | 2–3 labeled scalars in a row | "1.2 m · 0.18 mi · 2012" |
|
| 145 |
+
| `spark` | 60×24 inline sparkline, no axes | trend at a glance |
|
| 146 |
+
| `histogram` | 8–12 bar histogram, mono labels | distributions |
|
| 147 |
+
| `timeseries` | 240×84 line chart with threshold rule | hourly water level, 311 calls |
|
| 148 |
+
| `forecast` | 240×88 fan chart (p10/p50/p90) | 2050/2080 SLR, surge |
|
| 149 |
+
| `raster` | 240×120 stylized raster thumbnail | FEMA AE polygon, stormwater extent, HWM contour |
|
| 150 |
+
| `raster-pred` | same shape with dashed top-rule (synthetic tier) | TerraMind 2050 prediction |
|
| 151 |
+
| `register` | 3-col dense list (tag · label · sourceId), detail in `title=` tooltip | NYCHA buildings, schools |
|
| 152 |
+
| `comparison` | side-by-side scalar columns with delta | FEMA-AE vs Prithvi-2050 |
|
| 153 |
+
| `meta` | definition list of run metadata | model id, prompt hash, latency |
|
| 154 |
+
|
| 155 |
+
**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.
|
| 156 |
+
|
| 157 |
+
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).
|
| 158 |
+
|
| 159 |
+
### Tier system
|
| 160 |
+
|
| 161 |
+
Four epistemic tiers, encoded redundantly:
|
| 162 |
+
|
| 163 |
+
| tier | color | glyph | badge | meaning |
|
| 164 |
+
|---|---|---|---|---|
|
| 165 |
+
| `empirical` | `#0B5394` (8.59:1) | filled square | EMP | observed, ground-truth |
|
| 166 |
+
| `modeled` | `#2A6FA8` (5.41:1) | open square | MOD | computed from observations |
|
| 167 |
+
| `proxy` | `#6B6B6B` (5.74:1) | dotted ring | PRX | indirect signal, e.g. 311 calls |
|
| 168 |
+
| `synthetic` | `#2A6FA8` + dashed | hatched square | SYN | model prior, no observation |
|
| 169 |
+
|
| 170 |
+
Glyphs are inline SVG, 12×12, black-stroke. See `glyphs.jsx` for the four shapes.
|
| 171 |
+
|
| 172 |
+
**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.
|
| 173 |
+
|
| 174 |
+
### Provenance trace
|
| 175 |
+
|
| 176 |
+
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`.
|
| 177 |
+
|
| 178 |
+
**Smart-default rule** (`provenanceMode = "smart"`):
|
| 179 |
+
|
| 180 |
+
- All-`ok` Stone → **collapsed** by default, single-line summary "12/12 specialists fired clean"
|
| 181 |
+
- Any `warn` or `error` → **expanded**, full tree visible
|
| 182 |
+
- All `silent` → collapsed, single-line "no firings (section omitted from briefing)"
|
| 183 |
+
|
| 184 |
+
`provenanceMode = "expanded"` forces all expanded; `"collapsed"` forces all collapsed (warn/error still get a count chip).
|
| 185 |
+
|
| 186 |
+
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.
|
| 187 |
+
|
| 188 |
+
### Run-health strip
|
| 189 |
+
|
| 190 |
+
Single row above the Stones, full-width. Shows:
|
| 191 |
+
|
| 192 |
+
- Total specialists fired / total specialists registered (e.g. `83/100`)
|
| 193 |
+
- Per-tier breakdown as four chips: `EMP 41 · MOD 28 · PRX 12 · SYN 2`
|
| 194 |
+
- Total runtime (e.g. `3.4s`)
|
| 195 |
+
- Cache-hit ratio (e.g. `92%`)
|
| 196 |
+
|
| 197 |
+
Mono throughout. Background `--paper-deep`, 1px ink-soft top + bottom rule.
|
| 198 |
+
|
| 199 |
+
### Hover linking
|
| 200 |
+
|
| 201 |
+
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:
|
| 202 |
+
|
| 203 |
+
- Adds `is-link-{layer}` class to its root, which lights up that layer (see `map.jsx` for the CSS rules)
|
| 204 |
+
- Renders a small label badge bottom-right: "linked: {layer}"
|
| 205 |
+
|
| 206 |
+
The same applies in reverse: hovering a layer in the map sets `linkedKey` to the corresponding card key, which gets a 2px accent outline.
|
| 207 |
+
|
| 208 |
+
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`.
|
| 209 |
+
|
| 210 |
+
### Density
|
| 211 |
+
|
| 212 |
+
`density: "compact" | "comfortable"` (default `comfortable`).
|
| 213 |
+
|
| 214 |
+
- **Comfortable**: 16px card padding, 14px line-height multiplier 1.4
|
| 215 |
+
- **Compact**: 10px card padding, 12px line-height multiplier 1.25, register-card row height 18px (vs 24px)
|
| 216 |
+
|
| 217 |
+
Pass through to all card bodies; only register/tabular/meta visibly change.
|
| 218 |
+
|
| 219 |
+
## Interactions & Behavior
|
| 220 |
+
|
| 221 |
+
- **Card hover**: 200ms `background-color` transition to `--paper-deep`, 2px accent outline if linked
|
| 222 |
+
- **Cite button**: opens a small popover with the full citation list (`cites[]`), each row a link to the source PDF/page
|
| 223 |
+
- **Provenance toggle**: button with `aria-expanded`, animates the tree open/closed via `details`/`summary` or scoped `max-height` transition (≤200ms)
|
| 224 |
+
- **Map layer hover**: sets `linkedKey`, 100ms layer fill opacity transition
|
| 225 |
+
- **Reduced motion**: all transitions become 0.01ms via the global rule in `tokens.css`. Don't add motion on top.
|
| 226 |
+
- **Keyboard**: every card is `tabindex=0` with `aria-label="{tier} card · {title} · {source}"`. Cite button is real `<button>`. Provenance toggle is real `<button>`.
|
| 227 |
+
- **Loading**: cards show a 1px ink-soft skeleton (no spinner); replace with content when the specialist returns
|
| 228 |
+
- **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)
|
| 229 |
+
|
| 230 |
+
## State Management
|
| 231 |
+
|
| 232 |
+
Svelte 5 runes; lift to `+page.svelte`:
|
| 233 |
+
|
| 234 |
+
```svelte
|
| 235 |
+
<script>
|
| 236 |
+
let query = $state(null);
|
| 237 |
+
let density = $state("comfortable");
|
| 238 |
+
let provenanceMode = $state("smart");
|
| 239 |
+
let showComparison = $state(false);
|
| 240 |
+
let showGrammar = $state(false); // dev-only toggle
|
| 241 |
+
let linkedKey = $state(null);
|
| 242 |
+
|
| 243 |
+
// Data: load once per query, hydrate from server
|
| 244 |
+
let cardsByStone = $derived(loadCards(query));
|
| 245 |
+
let runHealth = $derived(summarize(cardsByStone));
|
| 246 |
+
</script>
|
| 247 |
+
```
|
| 248 |
+
|
| 249 |
+
Children take props via `$props()`. No Svelte stores unless cross-route.
|
| 250 |
+
|
| 251 |
+
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.
|
| 252 |
+
|
| 253 |
+
## Design Tokens
|
| 254 |
+
|
| 255 |
+
Port `tokens.css` verbatim. Key values:
|
| 256 |
+
|
| 257 |
+
**Tier colors** (all WCAG AA on white):
|
| 258 |
+
- `--tier-empirical: #0B5394` (8.59:1)
|
| 259 |
+
- `--tier-modeled: #2A6FA8` (5.41:1)
|
| 260 |
+
- `--tier-proxy: #6B6B6B` (5.74:1)
|
| 261 |
+
- `--tier-synthetic: #2A6FA8` (pattern-differentiated)
|
| 262 |
+
|
| 263 |
+
**Neutrals**:
|
| 264 |
+
- `--paper: #FAFAF7` (warm near-white, USGS-report register)
|
| 265 |
+
- `--paper-deep: #F2F2EE`
|
| 266 |
+
- `--ink: #1A1A1A` · `--ink-secondary: #4A4A4A` · `--ink-tertiary: #6B6B6B`
|
| 267 |
+
- `--rule: #1A1A1A` · `--rule-soft: #C9C9C5`
|
| 268 |
+
|
| 269 |
+
**Accent**:
|
| 270 |
+
- `--accent: #B8620A` (for text, AA)
|
| 271 |
+
- `--accent-graphical: #D17C00` (for shapes/lines, ≥3:1)
|
| 272 |
+
|
| 273 |
+
**Type**:
|
| 274 |
+
- `--font-sans: "IBM Plex Sans"` (UI, body)
|
| 275 |
+
- `--font-serif: "IBM Plex Serif"` (Stone names, hero italic emphasis, oversized stone numerals)
|
| 276 |
+
- `--font-mono: "IBM Plex Mono"` (labels, source ids, badges, table cells)
|
| 277 |
+
|
| 278 |
+
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.
|
| 279 |
+
|
| 280 |
+
**Spacing** (`--s-1` through `--s-9`): 4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 / 96 px. Use these tokens; don't hand-roll values.
|
| 281 |
+
|
| 282 |
+
**Type scale** (suggested, not enforced):
|
| 283 |
+
- 11px mono labels (`section-label`)
|
| 284 |
+
- 12–13px mono row text
|
| 285 |
+
- 14px sans card titles (600)
|
| 286 |
+
- 16px sans body
|
| 287 |
+
- 18px sans deck text
|
| 288 |
+
- 26px serif italic Stone names
|
| 289 |
+
- 36–52px serif headlines (italic for emphasis)
|
| 290 |
+
|
| 291 |
+
**Radius**: 0 throughout (this is a civic-tech-clean, USGS-report register; no rounded corners except `1px` on focus rings).
|
| 292 |
+
|
| 293 |
+
**Shadows**: none. Differentiation by 1px rules and `--paper-deep` fills only.
|
| 294 |
+
|
| 295 |
+
## Assets
|
| 296 |
+
|
| 297 |
+
- **Fonts**: IBM Plex Sans / Serif / Mono. Self-host woff2 or Google Fonts.
|
| 298 |
+
- **Wordmark**: text + accent bar `▌` prefix, no logo file
|
| 299 |
+
- **Tier glyphs**: inline SVG, see `glyphs.jsx`
|
| 300 |
+
- **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.
|
| 301 |
+
- **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.
|
| 302 |
+
- **No icon library.** No Lucide, no Heroicons. SVG glyphs for tiers, mono characters (⌕, ↗, ▌) for chrome.
|
| 303 |
+
- **No emoji.**
|
| 304 |
+
|
| 305 |
+
## Files in this bundle
|
| 306 |
+
|
| 307 |
+
```
|
| 308 |
+
design_handoff_riprap_findings/
|
| 309 |
+
├── CLAUDE_CODE_PROMPT.md ← paste this into Claude Code first
|
| 310 |
+
├── README.md ← you are here
|
| 311 |
+
└── design_files/
|
| 312 |
+
├── Riprap Stone-Grouped UI v0.4.4.html ← main prototype, open in browser
|
| 313 |
+
├── Riprap Landing.html
|
| 314 |
+
├── Riprap Landing Variants.html
|
| 315 |
+
├── tokens.css ← port verbatim
|
| 316 |
+
├── styles.css ← reference only
|
| 317 |
+
├── findings.jsx ← Findings region (this handoff's centerpiece)
|
| 318 |
+
├── briefing.jsx ← long-form prose
|
| 319 |
+
├── evidence.jsx ← evidence card stack used in briefing
|
| 320 |
+
├── map.jsx ← mini-map with link highlighting
|
| 321 |
+
├── trace.jsx
|
| 322 |
+
├── stones-trace.jsx
|
| 323 |
+
├── stone-evidence.jsx
|
| 324 |
+
├── shell.jsx ← header, footer, cold-start
|
| 325 |
+
├── glyphs.jsx ← four tier glyphs
|
| 326 |
+
├── tweaks-panel.jsx ← prototype tooling, ignore
|
| 327 |
+
├── design-canvas.jsx ← prototype tooling, ignore
|
| 328 |
+
├── landing-variants.css ← marketing-page exploration, ignore unless asked
|
| 329 |
+
└── landing-variants.jsx ← marketing-page exploration, ignore unless asked
|
| 330 |
+
```
|
| 331 |
+
|
| 332 |
+
## Scope
|
| 333 |
+
|
| 334 |
+
**In scope** for this handoff:
|
| 335 |
+
|
| 336 |
+
- The Findings region (5 Stones, 12 card variants, run-health strip, smart provenance, hover linking, card grammar reference)
|
| 337 |
+
- Briefing prose and map, to the extent they connect to Findings via `linkedKey`
|
| 338 |
+
- The marketing **landing page** (see §"Landing page" below)
|
| 339 |
+
- v0.4.5 deltas in `V0.4.5_SPEC.md`
|
| 340 |
+
|
| 341 |
+
**Out of scope**: the methodology PDF, the export-PDF flow.
|
| 342 |
+
|
| 343 |
+
## Landing page
|
| 344 |
+
|
| 345 |
+
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/`:
|
| 346 |
+
|
| 347 |
+
- `Riprap Landing.html` — final shipping landing page
|
| 348 |
+
- `Riprap Landing Variants.html` — three exploratory variants on a design canvas (kept for reference; do not port)
|
| 349 |
+
|
| 350 |
+
### Landing structure (port `Riprap Landing.html`)
|
| 351 |
+
|
| 352 |
+
Four-section vertical scroll, max-width 1200px, paper background:
|
| 353 |
+
|
| 354 |
+
1. **Hero**
|
| 355 |
+
- Wordmark top-left (with `▌` accent prefix)
|
| 356 |
+
- Headline (52px serif, italic emphasis on *any place*, line-broken into two lines)
|
| 357 |
+
- Deck (18px sans, max 70ch, ink-secondary)
|
| 358 |
+
- Big query box: "Try:" label + cycling example queries on a fixed dotted-underline rail (rail width pinned, ellipsis fallback for overflow)
|
| 359 |
+
- Submit button (ink fill, paper text, mono caps)
|
| 360 |
+
|
| 361 |
+
2. **"What you'll get back" preview** — 3-pane grid (1.4fr / 1fr / 1fr), bottoms equalized:
|
| 362 |
+
- **Excerpt pane**: serif briefing snippet with inline `[N]` citation pins; compact source list at bottom (tier-coded)
|
| 363 |
+
- **Evidence cards pane**: 2x2 grid of compact evidence cards, each with tier-coded left rule (2px), claim, source
|
| 364 |
+
- **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
|
| 365 |
+
|
| 366 |
+
3. **Five Stones strip** — explanation grid: 5 columns (`repeat(5, 1fr)`), one cell per Stone:
|
| 367 |
+
- Oversized italic-serif numerals `01..05` top-right of each cell (in `--rule-soft`, decorative)
|
| 368 |
+
- Stone name (serif 22px / 500)
|
| 369 |
+
- Role tagline (sans 13px / ink-secondary)
|
| 370 |
+
- Italic-serif tag ("what NYC's ground remembers" etc.)
|
| 371 |
+
- Dashed rule + mono source list at bottom
|
| 372 |
+
|
| 373 |
+
4. **Footer** — earns its keep:
|
| 374 |
+
- Tier legend (4 swatches: empirical / modeled / proxy / synthetic)
|
| 375 |
+
- Build line (mono, ink-tertiary)
|
| 376 |
+
|
| 377 |
+
### Landing — what's settled (don't redesign)
|
| 378 |
+
|
| 379 |
+
- The hero dropped the redundant "Riprap" eyebrow; the wordmark already says it. Headline carries the lede.
|
| 380 |
+
- Cycling examples ride a single fixed dotted underline (no jump on length change). Pin the rail width; ellipsis-truncate overflow.
|
| 381 |
+
- Pane heights are equalized via a flex-1 spacer in the cards pane and `flex: 1` on the map pane.
|
| 382 |
+
- Map texture is a subtle 8px paper-grain grid behind the colored layers — gives it a map register, not a diagram register.
|
| 383 |
+
- Stones strip uses oversized italic numerals as a typographic register (decorative, in soft-rule color).
|
| 384 |
+
- Italic serif is intentional: hero emphasis + Stone tags + Stone numerals. Reads as a deliberate third voice.
|
| 385 |
+
|
| 386 |
+
### Landing — em-dashes
|
| 387 |
+
|
| 388 |
+
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.
|
| 389 |
+
|
| 390 |
+
### Landing — out of scope variants
|
| 391 |
+
|
| 392 |
+
`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).
|
| 393 |
+
|
| 394 |
+
## Open questions for the design team
|
| 395 |
+
|
| 396 |
+
These are deliberately *not* resolved in the prototype; raise them with the designer before locking implementation:
|
| 397 |
+
|
| 398 |
+
1. Should `register` cards paginate when N > 20, or stay scrollable in a fixed-height card?
|
| 399 |
+
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)?
|
| 400 |
+
3. Run-health cache-hit ratio — is this surfaced to end users, or is it dev-mode only?
|
| 401 |
+
4. Provenance trace expansion: should expanded state survive across query changes, or reset?
|
docs/design_handoff/V0.4.5_SPEC.md
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Riprap v0.4.5 · Polish on v0.4.4 (Findings region)
|
| 2 |
+
|
| 3 |
+
**Status**: design spec, ready for Claude Code implementation.
|
| 4 |
+
**Frame**: surgical polish on the live local SvelteKit app. Nothing structural changes. Reuse v0.4.4 components; apply nine deltas.
|
| 5 |
+
|
| 6 |
+
The v0.4.4 Findings region (five Stones, evidence cards, raster thumbnails, time-series, Capstone meta-card) is shipping correctly. Real queries (e.g., 80 Pioneer Street, Red Hook) render Stones producing genuinely different evidence-card kinds, with provenance collapsing under each Stone, the briefing prose with citations resolving cleanly, and the map with three tier-encoded layers and the address pin. The work below corrects nine specific issues observed in production-shaped local runs.
|
| 7 |
+
|
| 8 |
+
## What v0.4.5 must NOT change
|
| 9 |
+
|
| 10 |
+
- Card grammar (header / body / footer / tier badge / source link)
|
| 11 |
+
- The four-section briefing prose structure
|
| 12 |
+
- The Mellea reroll status strip ("Regenerating to satisfy citation grounding · attempt N of N · previous draft dimmed below")
|
| 13 |
+
- The four-tier color palette and glyphs
|
| 14 |
+
- The cold-start state with the v0.4.3 Stones one-liner
|
| 15 |
+
- The trust-signal footer
|
| 16 |
+
- The PDF template's core layout (Stone color print handling is the only new consideration)
|
| 17 |
+
- The map's base style and the existing layer rendering for Sandy / FEMA / 311
|
| 18 |
+
|
| 19 |
+
## What v0.4.5 must NOT introduce
|
| 20 |
+
|
| 21 |
+
- New card body variants beyond what v0.4.4 specified
|
| 22 |
+
- Card-level decoration competing with tier badges (Stone color hints are at the Stone region level — at the card level only as a designer-chosen subtle touch like a tinted left-border)
|
| 23 |
+
- Animations on card render
|
| 24 |
+
- New typography or spacing tokens unless something genuinely new emerges from the fixes
|
| 25 |
+
- Mascots, icons, or thematic visual associations for individual Stones beyond their color hint
|
| 26 |
+
|
| 27 |
+
---
|
| 28 |
+
|
| 29 |
+
## 1. Status semantics: split conflated states
|
| 30 |
+
|
| 31 |
+
**Problem.** The top tally currently reads "5 Stones · 15/18 functions fired · 9 evidence cards · 24.0s · 3 error" on a query where Mellea grounding passed 4/4 and the briefing came out clean. Two of those three "errors" are not errors at all:
|
| 32 |
+
|
| 33 |
+
- Keystone's `mta_entrance_exposure` returned "no entrances within radius" — that's the specialist working correctly. Absence of nearby subway entrances at 80 Pioneer is a true and useful finding, not a failure.
|
| 34 |
+
- Lodestone's `floodnet_forecast` hit its silent-floor (sensor has only 2 historical events, <5 required) — the four-tier discipline working as intended.
|
| 35 |
+
- Lodestone's `ttm_311_forecast` actually failed (311 history fetch error). That is the only real error.
|
| 36 |
+
|
| 37 |
+
Three different epistemic states are being rendered as one red square.
|
| 38 |
+
|
| 39 |
+
**Fix.** Split the FSM specialist status into five values:
|
| 40 |
+
|
| 41 |
+
| status | meaning | provenance row treatment | counts toward |
|
| 42 |
+
|---|---|---|---|
|
| 43 |
+
| `fired` | completed and produced output the reconciler used | tier-colored square | "fired" tally |
|
| 44 |
+
| `silent_by_design` | completed and correctly produced no output | open square in neutral tone, italicized message | "silent" tally |
|
| 45 |
+
| `warned` | output produced with a non-fatal warning | tier-colored square + small warn sidemark | "fired" tally + "warnings" count |
|
| 46 |
+
| `errored` | failed to complete, no usable output | red square; 1-line collapsed summary; full trace behind click-to-expand (v0.4.2 drilldown pattern) | "errored" tally |
|
| 47 |
+
| `not_invoked` | FSM skipped the specialist (precondition unmet) | hollow gray square; one-line reason | "not invoked" count |
|
| 48 |
+
|
| 49 |
+
**Aggregate count rules.** Top tally:
|
| 50 |
+
```
|
| 51 |
+
5 Stones · 15 fired · 5 silent · 1 errored · 24.0s
|
| 52 |
+
```
|
| 53 |
+
(no more rolled-up "errors" number)
|
| 54 |
+
|
| 55 |
+
Per-Stone summary (replaces "0 cards · 0 fired · 1 error · 30ms"):
|
| 56 |
+
```
|
| 57 |
+
Keystone · 0 cards · 5 silent · 30ms
|
| 58 |
+
Lodestone · 1 card · 3 fired · 1 silent · 1 errored · 1.5s
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
**Status messages — voice.** Engineering-honest, no euphemism. Examples to copy:
|
| 62 |
+
- `"no entrances within radius"`
|
| 63 |
+
- `"sensor has only 2 historical events; forecast omitted (silent-floor: 5)"`
|
| 64 |
+
- `"PLUTO join skipped: queried address not in NYC PLUTO dataset"`
|
| 65 |
+
- `"311 history fetch failed: HTTP 503 at NYC OpenData (3 retries)"`
|
| 66 |
+
|
| 67 |
+
Match v0.4.1–v0.4.4 voice — precise, slightly understated, comfortable with technical detail, engineer-to-engineer.
|
| 68 |
+
|
| 69 |
+
**This is the most important fix in v0.4.5.** It is the one actively misrepresenting system integrity. Do this first.
|
| 70 |
+
|
| 71 |
+
---
|
| 72 |
+
|
| 73 |
+
## 2. Capstone meta-card field-mapping
|
| 74 |
+
|
| 75 |
+
**Problem.** The Capstone meta-card displays:
|
| 76 |
+
```
|
| 77 |
+
mellea reroll: 0 attempts
|
| 78 |
+
grounding checks: 0/4 passed
|
| 79 |
+
citations resolved: 0
|
| 80 |
+
wall-clock: 24.0 s
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
But the briefing has 4 resolved citations ([1] Sandy, [2] NYC311, [3] Ida, [4] Microtopo); the Mellea status strip explicitly shows "attempt 2 of 2"; the briefing rendered clean (so grounding passed). Three of four metrics are showing zero on a clean run. State plumbing, not logic.
|
| 84 |
+
|
| 85 |
+
**Fix.** Wire the four metrics to the reconciler's actual state fields:
|
| 86 |
+
|
| 87 |
+
| display | reads from | expected for this query |
|
| 88 |
+
|---|---|---|
|
| 89 |
+
| `mellea reroll: N attempts` | per-query reroll count from Mellea (likely `melleaState.rerollCount`) | `1` (one reroll on top of initial) |
|
| 90 |
+
| `grounding checks: N/4 passed` | per-query grounding-check results array → count true | `4/4` |
|
| 91 |
+
| `citations resolved: N` | count of resolved citations in the final briefing payload | `4` |
|
| 92 |
+
| `wall-clock: NN.N s` | already correct — no change | `24.0 s` |
|
| 93 |
+
|
| 94 |
+
**Acceptance.** The Capstone meta-card on a clean Red Hook run shows `1 / 4/4 / 4 / 24.0 s`. On a failed-grounding run it shows the actual numbers (e.g. `2 / 3/4 / 4 / 31.0 s`) so the meta-card honestly reports what happened.
|
| 95 |
+
|
| 96 |
+
The Capstone meta-card is the integrity-narration UI for the entire pipeline. Zero on a clean run undersells the system's integrity. Accurate numbers turn it into a proof point — "this query went through 1 reroll to satisfy 4/4 grounding checks, resolved 4 citations to primary sources, and took 24 seconds end-to-end."
|
| 97 |
+
|
| 98 |
+
---
|
| 99 |
+
|
| 100 |
+
## 3. Provenance roster: always show full inventory
|
| 101 |
+
|
| 102 |
+
**Problem.** Expanded Keystone provenance shows only `step-16 · mta_entrance_exposure — no entrances within radius (30ms)`. Keystone fires five specialists (MTA, NYCHA, DOE, DOH, PLUTO). Four are missing from the UI.
|
| 103 |
+
|
| 104 |
+
**Fix.** Each Stone's provenance expander shows the **complete roster** of specialists the Stone *could have fired*, with one row per specialist and its status from §1. A reader who expands a Stone sees the full inventory — never a filtered subset. This is the auditability contract.
|
| 105 |
+
|
| 106 |
+
If a specialist genuinely didn't run, that's `not_invoked` with a one-line reason. The reader sees the intended roster and understands every absence.
|
| 107 |
+
|
| 108 |
+
**Implementation.** The Stone definition (in `src/lib/data/stones.ts` or wherever the registry lives) should list its full specialist roster. The provenance component renders one row per registry entry, joining against the run's actual specialist outputs. Specialists missing from the run output → `not_invoked` status, one-line reason from the FSM's skip log.
|
| 109 |
+
|
| 110 |
+
---
|
| 111 |
+
|
| 112 |
+
## 4. Touchstone — five cards, not three
|
| 113 |
+
|
| 114 |
+
**Problem.** Touchstone renders three cards (FloodNet, NYC 311, NOAA CO-OPS). v0.4.4 specified five: those three plus **TerraMind LULC** (SYN tier) and **Prithvi-NYC-Pluvial** (MOD tier).
|
| 115 |
+
|
| 116 |
+
**Fix.** Wire the structured outputs of `step_terramind_lulc` and `step_prithvi_live` (now Prithvi v2) to Touchstone cards.
|
| 117 |
+
|
| 118 |
+
**TerraMind LULC card** (Touchstone, SYN tier — synthetic prior, dashed top-rule):
|
| 119 |
+
- Title: `"Land use / land cover · TerraMind v1.2"`
|
| 120 |
+
- Body variant: existing `raster` thumbnail (240×120, segmentation overlay) + a **horizontal stacked class-mix bar** below the thumbnail showing percentage by LULC class. Use the conventional LULC palette (urban / water / vegetation / barren / wetland) — those colors are visual conventions for the layer itself, not new tier signals.
|
| 121 |
+
- Source: `TerraMind v1.2`
|
| 122 |
+
- Agency: `IBM TerraMind v1.2 · Sentinel-2 inputs`
|
| 123 |
+
- Vintage: latest Sentinel scene date for the AOI (e.g. `Sentinel-2 · 2024-09-18`)
|
| 124 |
+
- `mapLayer: "terramind-lulc"`
|
| 125 |
+
|
| 126 |
+
**Prithvi-NYC-Pluvial card** (Touchstone, MOD tier):
|
| 127 |
+
- Title: `"Pluvial flood prediction · Prithvi-NYC-Pluvial"`
|
| 128 |
+
- Body variant: `raster` thumbnail (flood-mask overlay) **plus a headline scalar above** showing flood percentage of AOI.
|
| 129 |
+
- Source: `Prithvi-NYC-Pluvial`
|
| 130 |
+
- Agency: `NASA-IBM Prithvi v2 · NYC fine-tune`
|
| 131 |
+
- Vintage: prediction time + Sentinel scene date
|
| 132 |
+
- `mapLayer: "prithvi-pluvial"`
|
| 133 |
+
|
| 134 |
+
**Layout.** v0.4.4's horizontal scrolling treatment handles five cards on wide viewports; on narrow viewports they stack vertically as designed. No new layout work.
|
| 135 |
+
|
| 136 |
+
**If the specialists aren't firing yet** (UPDATE_STONES.md commits 4 + 5 may still be in flight): surface that as a backend issue, but the card definitions land in v0.4.5 so they're ready when the data is.
|
| 137 |
+
|
| 138 |
+
---
|
| 139 |
+
|
| 140 |
+
## 5. Lodestone — both TTM cards, not just the zero-shot
|
| 141 |
+
|
| 142 |
+
**Problem.** Lodestone shows one time-series card: zero-shot Granite TTM r2 surge nowcast (9.6h, 6-min cadence). The footer correctly notes "Distinct from the fine-tuned Battery surge nowcast" — but that fine-tuned card is missing.
|
| 143 |
+
|
| 144 |
+
**Fix.** Add the fine-tuned TTM card. Both cards live in Lodestone:
|
| 145 |
+
|
| 146 |
+
| | Zero-shot card (existing) | Fine-tuned card (new) |
|
| 147 |
+
|---|---|---|
|
| 148 |
+
| Title | `"Storm surge nowcast at The Battery — 9.6 h horizon (regional)"` | `"Storm surge nowcast at The Battery — 96 h horizon (NYC-specialized fine-tune)"` |
|
| 149 |
+
| Horizon | 9.6 h | 96 h |
|
| 150 |
+
| Cadence | 6-min | hourly |
|
| 151 |
+
| Tier | MOD | MOD |
|
| 152 |
+
| Body | timeseries (existing) | timeseries (96-h forecast) |
|
| 153 |
+
| Source label | `Granite TTM r2 (zero-shot)` | `msradam/Granite-TTM-r2-Battery-Surge` |
|
| 154 |
+
| Footer extras | "regional disclosure" tag | model-card link to HF artifact: `huggingface.co/msradam/Granite-TTM-r2-Battery-Surge`, RMSE `0.157 m`, `−35% vs persistence`, AMD MI300X badge |
|
| 155 |
+
|
| 156 |
+
The fine-tuned card is a load-bearing piece of the Riprap-on-AMD story for the hackathon submission. Its presence as a Lodestone card alongside the zero-shot card is the visible "this system uses NYC-specialized fine-tuned models you can verify on HuggingFace" claim that closes the loop.
|
| 157 |
+
|
| 158 |
+
`step_ttm_battery_surge` in production trace fires at ~1.5s with `context_h=1024 · horizon_h=96`. If it's firing but not producing a structured output the card layer can render, surface that as a backend issue before v0.4.5 implementation.
|
| 159 |
+
|
| 160 |
+
---
|
| 161 |
+
|
| 162 |
+
## 6. The "anomaly" tag
|
| 163 |
+
|
| 164 |
+
**Problem.** Provenance expanders show `Hide provenance · 1 function · anomaly` (Keystone) and `Hide provenance · 5 functions · anomaly` (Lodestone). Overloaded label: doing different work in each context, partially duplicating row-level info.
|
| 165 |
+
|
| 166 |
+
**Decision: drop the "anomaly" tag.**
|
| 167 |
+
|
| 168 |
+
After §1 lands, Stone summaries say `Keystone · 0 cards · 5 silent · 30ms` (expected behavior — no exposed assets at this address) or `Lodestone · 1 card · 3 fired · 1 silent · 1 errored · 1.5s` (one specialist errored, visible in the count). The status counts make "anomaly" redundant. Less is more.
|
| 169 |
+
|
| 170 |
+
(Alternative — retained for record but not recommended: keep "anomaly" and define it as "Stone-level outcome differs from typical: 0 cards landed despite specialists firing, OR ≥1 specialist errored, OR Stone retried." If chosen, document the rule and apply mechanically. But §1's count breakdown carries the same information without the extra label.)
|
| 171 |
+
|
| 172 |
+
---
|
| 173 |
+
|
| 174 |
+
## 7. LAYERS panel — restructure to mirror Stones
|
| 175 |
+
|
| 176 |
+
**Problem.** Production shows the existing three layers (Sandy / FEMA-DEP / 311) in a flat LAYERS panel without Stone grouping. The four new raster layers (TerraMind LULC, TerraMind Buildings, Prithvi-NYC-Pluvial) don't have a home.
|
| 177 |
+
|
| 178 |
+
**Fix.** Restructure the LAYERS panel to mirror the Findings Stones structure:
|
| 179 |
+
|
| 180 |
+
```
|
| 181 |
+
LAYERS
|
| 182 |
+
|
| 183 |
+
▾ Cornerstone — what NYC's ground remembers
|
| 184 |
+
◧ Sandy Inundation Zone (2012) EMP [on]
|
| 185 |
+
◨ FEMA / DEP scenarios MOD [on]
|
| 186 |
+
◧ Ida HWM points (2021) EMP [off]
|
| 187 |
+
▥ Microtopography (HAND/TWI) PRX [off]
|
| 188 |
+
|
| 189 |
+
▾ Keystone — what's exposed
|
| 190 |
+
◉ MTA subway entrances EMP [on]
|
| 191 |
+
▭ NYCHA developments EMP [on]
|
| 192 |
+
✕ DOE schools EMP [on]
|
| 193 |
+
● DOH hospitals EMP [on]
|
| 194 |
+
▦ TerraMind Buildings (current) SYN [off]
|
| 195 |
+
|
| 196 |
+
▾ Touchstone — what's happening now
|
| 197 |
+
● 311 flood complaints PRX [on]
|
| 198 |
+
◉ FloodNet sensors EMP [on]
|
| 199 |
+
▥ TerraMind LULC (current) SYN [off]
|
| 200 |
+
▥ Prithvi-NYC-Pluvial flood pred. MOD [off]
|
| 201 |
+
|
| 202 |
+
▾ Lodestone — what's coming
|
| 203 |
+
(no map layers — see Findings cards)
|
| 204 |
+
|
| 205 |
+
▾ Capstone — synthesis
|
| 206 |
+
(not a map layer)
|
| 207 |
+
```
|
| 208 |
+
|
| 209 |
+
**New layer specs**:
|
| 210 |
+
- **TerraMind LULC** (SYN tier): conventional LULC palette (urban/water/vegetation/barren/wetland). Default off. Label includes Sentinel scene date.
|
| 211 |
+
- **TerraMind Buildings** (SYN tier): synthetic-prior glyph. Default off.
|
| 212 |
+
- **Prithvi-NYC-Pluvial** (MOD tier): Modeled-tier color treatment. Label includes Sentinel scene date.
|
| 213 |
+
|
| 214 |
+
The "no map layers — see Findings cards" label under Lodestone is explicit by design: the TTM Battery Surge is a Lodestone card, not a map layer. Naming the absence prevents the reader from looking for it.
|
| 215 |
+
|
| 216 |
+
---
|
| 217 |
+
|
| 218 |
+
## 8. Card-to-map hover linking
|
| 219 |
+
|
| 220 |
+
**Problem.** v0.4.4 specified hover-to-highlight from card → map element. Production may or may not have this wired.
|
| 221 |
+
|
| 222 |
+
**Fix.** Verify and ship the connection:
|
| 223 |
+
|
| 224 |
+
| Card type | Map link | Hover treatment |
|
| 225 |
+
|---|---|---|
|
| 226 |
+
| FEMA card | FEMA AE polygon | layer fill opacity bump (0.4 → 0.6), 100ms |
|
| 227 |
+
| HWM tabular | HWM contour + points | line weight 1px → 2px |
|
| 228 |
+
| Register cards (NYCHA, schools, etc.) | corresponding pins | pins gain 2px accent ring; on click, fitBounds() |
|
| 229 |
+
| FloodNet sensor card | sensor pin | pin glow + accent outline |
|
| 230 |
+
| Raster prediction (TerraMind, Prithvi) | the matching raster layer | layer fill opacity bump |
|
| 231 |
+
| Address card | address pin | pin pulse (single, 200ms) |
|
| 232 |
+
| TTM Battery Surge | (none — not spatial) | no map behavior |
|
| 233 |
+
| Capstone meta-card | (none) | no map behavior |
|
| 234 |
+
|
| 235 |
+
**Implementation.** Page-level `linkedKey` state (already specced in v0.4.4 README). Cards set it on `pointerenter` / `focus`, clear on `pointerleave` / `blur`. Map watches `$derived` of `linkedKey` and applies layer-specific class + label badge ("linked: {layer}") bottom-right.
|
| 236 |
+
|
| 237 |
+
Click-to-fit-bounds for register cards is new in v0.4.5: when clicking a register row inside a register card, the map calls `fitBounds()` on the affected feature(s) with 80px padding, 400ms easing.
|
| 238 |
+
|
| 239 |
+
---
|
| 240 |
+
|
| 241 |
+
## 9. Stone-tinted accent colors — light theming
|
| 242 |
+
|
| 243 |
+
**Problem.** v0.4.3/v0.4.4 prohibited per-Stone color coding to avoid competing with the four-tier epistemic palette. That prohibition is being relaxed: each Stone gets a single muted accent color the design system can apply as a hint.
|
| 244 |
+
|
| 245 |
+
**Five Stone accent tokens** (proposed values — designer can adjust within the constraints below):
|
| 246 |
+
|
| 247 |
+
| token | hex | name | rationale |
|
| 248 |
+
|---|---|---|---|
|
| 249 |
+
| `--stone-cornerstone` | `#7C6F5E` | warm taupe | grounded, soil-adjacent without being literally brown |
|
| 250 |
+
| `--stone-keystone` | `#5E6E7C` | cool slate | structural / architectural reading |
|
| 251 |
+
| `--stone-touchstone` | `#6B7C66` | muted sage | present-tense, alive without being signal-green |
|
| 252 |
+
| `--stone-lodestone` | `#7C6E5E` | softened ochre | forward-pointing warmth without competing with `--accent` |
|
| 253 |
+
| `--stone-capstone` | `#5E5E6E` | neutral indigo-gray | synthetic, cool, narrative |
|
| 254 |
+
|
| 255 |
+
All five sit at L≈45 in OKLCH, chroma ≤0.04. Verified non-overlap with the four tier hues (which sit at chroma 0–0.10 in different L regions).
|
| 256 |
+
|
| 257 |
+
### Constraints (non-negotiable)
|
| 258 |
+
|
| 259 |
+
1. **One color per Stone, five total.**
|
| 260 |
+
2. **Hint-level, not feature-level.** These are not signal-carrying like the tier palette. They are decoration that helps a reader navigate the five-region structure on first encounter.
|
| 261 |
+
3. **Must not compete with the four-tier palette.** Tier badges (filled/open square / dotted ring / hatched square in `#0B5394` / `#2A6FA8` / `#6B6B6B` / `#2A6FA8+stripe`) are the load-bearing epistemic signal. Stone colors must be muted enough or applied lightly enough that they read as decoration. If a reader could mistake a Stone color for a tier badge, it's wrong.
|
| 262 |
+
4. **Not technicolor.** Carto Positron base + IBM Plex + restrained ink palette is quiet and serious. Five primary-bright colors would shatter that.
|
| 263 |
+
5. **Not mascot.** No Stone gets an icon paired with its color. No "Cornerstone is brown because earthy" thematic mapping. The colors differentiate five regions; they don't express character.
|
| 264 |
+
6. **Print/PDF**: all five degrade to neutral gray (`#999`) in `@media print`. Each token has a print-media override.
|
| 265 |
+
7. **WCAG**: any text in or on a Stone color passes contrast. As tints (low-opacity backgrounds), body text stays in standard `--ink`. As foreground accents, contrast against `--paper` is verified.
|
| 266 |
+
|
| 267 |
+
### Recommended placement (one to three locations — pick conservatively)
|
| 268 |
+
|
| 269 |
+
**Primary** (recommend): a **3px left-rule** on each `<StoneRegion>` header strip, in that Stone's color. Subtle, navigational, hard to confuse with tier badges (which are inside cards, not on region headers).
|
| 270 |
+
|
| 271 |
+
**Secondary** (recommend): a **6px colored dot** beside each Stone name in the cold-start "How Riprap is built" five-line list. Helps recognition when the reader later sees the Stones in a query result.
|
| 272 |
+
|
| 273 |
+
**Tertiary** (optional, designer's call): a Stone-tinted **2px left-border on cards within that Stone's strip**. Risk: this brings color to the card chrome, which is where tier badges live. Apply only if the designer judges the chroma low enough that the tint reads as a region cue, not a tier cue. If in doubt, skip.
|
| 274 |
+
|
| 275 |
+
**Methodology page** (recommend): the 5×4 tier-Stone matrix uses Stone tints on its row labels (background tint at ~10% opacity, body text in standard ink). The matrix is where Stones-as-architecture is most visually present, and tints help readers parse the rows.
|
| 276 |
+
|
| 277 |
+
### Designer's veto
|
| 278 |
+
|
| 279 |
+
If the designer judges that adding Stone colors anywhere makes the UI busier without making it more legible, they may recommend keeping the existing zero-color Stone treatment with documented rationale. This is permission, not requirement.
|
| 280 |
+
|
| 281 |
+
### Print-media override
|
| 282 |
+
|
| 283 |
+
```css
|
| 284 |
+
@media print {
|
| 285 |
+
:root {
|
| 286 |
+
--stone-cornerstone: #999;
|
| 287 |
+
--stone-keystone: #999;
|
| 288 |
+
--stone-touchstone: #999;
|
| 289 |
+
--stone-lodestone: #999;
|
| 290 |
+
--stone-capstone: #999;
|
| 291 |
+
}
|
| 292 |
+
}
|
| 293 |
+
```
|
| 294 |
+
|
| 295 |
+
The PDF template's hierarchy is preserved by structure (Stone headings, type scale, rules), not by color. Stone tints are ignored in print.
|
| 296 |
+
|
| 297 |
+
---
|
| 298 |
+
|
| 299 |
+
## "v0.4.5 ready" looks like
|
| 300 |
+
|
| 301 |
+
A query at 80 Pioneer Street, Red Hook produces a Findings region where:
|
| 302 |
+
|
| 303 |
+
- Top tally reads `5 Stones · 15 fired · 5 silent · 1 errored · 24.0s` (no more 3-error mismatch).
|
| 304 |
+
- Capstone meta-card shows `1 reroll · 4/4 grounding · 4 citations · 24.0s` (no more zeroes).
|
| 305 |
+
- Every Stone's expanded provenance shows its full specialist roster, with each row carrying one of the five status states.
|
| 306 |
+
- Touchstone shows five cards (FloodNet, NYC 311, NOAA CO-OPS, TerraMind LULC, Prithvi-NYC-Pluvial).
|
| 307 |
+
- Lodestone shows two TTM time-series cards (zero-shot 9.6h, fine-tuned 96h with HF model-card link).
|
| 308 |
+
- LAYERS panel is grouped by Stone, with the four new raster layers wired and default-off.
|
| 309 |
+
- Hovering any spatial card lights up the corresponding map element; hovering a map layer outlines the corresponding card.
|
| 310 |
+
- "anomaly" tag is gone.
|
| 311 |
+
- Each Stone region carries a 3px left-rule in its accent color; the cold-start list shows colored dots beside Stone names. No card-level chrome shouts color.
|
| 312 |
+
|
| 313 |
+
The hackathon submission goes in (when public re-hosting completes) with a system that's not just impressive in capability but **transparent in operational state and visually composed as a deliberate architecture**.
|
docs/design_handoff/design_files/Riprap Landing Variants.html
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 6 |
+
<title>Riprap , Landing variants</title>
|
| 7 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
| 8 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
| 9 |
+
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400;1,500&family=IBM+Plex+Serif:wght@400;500;600&display=swap" rel="stylesheet" />
|
| 10 |
+
<link rel="stylesheet" href="tokens.css" />
|
| 11 |
+
<link rel="stylesheet" href="landing-variants.css" />
|
| 12 |
+
</head>
|
| 13 |
+
<body>
|
| 14 |
+
<template id="__bundler_thumbnail">
|
| 15 |
+
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
| 16 |
+
<rect width="100" height="100" fill="#FAFAF7"/>
|
| 17 |
+
<g transform="translate(20 30)" fill="#0B5394">
|
| 18 |
+
<rect x="0" y="0" width="60" height="6"/>
|
| 19 |
+
<rect x="0" y="12" width="60" height="3"/>
|
| 20 |
+
<rect x="0" y="20" width="42" height="3"/>
|
| 21 |
+
<rect x="0" y="30" width="60" height="10"/>
|
| 22 |
+
</g>
|
| 23 |
+
</svg>
|
| 24 |
+
</template>
|
| 25 |
+
<div id="root"></div>
|
| 26 |
+
|
| 27 |
+
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
| 28 |
+
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
| 29 |
+
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
| 30 |
+
|
| 31 |
+
<script type="text/babel" src="design-canvas.jsx"></script>
|
| 32 |
+
<script type="text/babel" src="landing-variants.jsx"></script>
|
| 33 |
+
</body>
|
| 34 |
+
</html>
|
docs/design_handoff/design_files/Riprap Landing.html
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 6 |
+
<title>Riprap · Flood Exposure Briefing for NYC</title>
|
| 7 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
| 8 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
| 9 |
+
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400;1,500&family=IBM+Plex+Serif:wght@400;500;600&display=swap" rel="stylesheet" />
|
| 10 |
+
<link rel="stylesheet" href="tokens.css" />
|
| 11 |
+
<link rel="stylesheet" href="landing-variants.css" />
|
| 12 |
+
<style>
|
| 13 |
+
.land-page { max-width: 1200px; margin: 0 auto; }
|
| 14 |
+
.land-section-stones-detail { background: var(--paper-deep); padding: 56px 32px; border-top: 1px solid var(--rule-soft); }
|
| 15 |
+
.land-stones-detail { display: grid; grid-template-columns: repeat(5, 1fr); gap: 0; background: white; border: 1px solid var(--rule-soft); border-bottom: 2px solid var(--ink); }
|
| 16 |
+
.land-stones-detail-cell { position: relative; padding: 28px 18px 22px; border-right: 1px solid var(--rule-soft); display: flex; flex-direction: column; gap: 8px; overflow: hidden; }
|
| 17 |
+
.land-stones-detail-cell:last-child { border-right: none; }
|
| 18 |
+
.land-stones-detail-num { position: absolute; top: 6px; right: 10px; font-family: var(--font-serif); font-style: italic; font-weight: 400; font-size: 38px; line-height: 1; color: var(--rule-soft); letter-spacing: -0.02em; pointer-events: none; }
|
| 19 |
+
.land-stones-detail-name { font-family: var(--font-serif); font-size: 22px; font-weight: 500; margin: 0; color: var(--ink); }
|
| 20 |
+
.land-stones-detail-role { font-family: var(--font-sans); font-size: 13px; color: var(--ink-secondary); }
|
| 21 |
+
.land-stones-detail-tag { font-family: var(--font-serif); font-style: italic; font-size: 14px; color: var(--ink-tertiary); margin: 0 0 6px; line-height: 1.45; }
|
| 22 |
+
.land-stones-detail-sources { margin-top: auto; padding-top: 10px; border-top: 1px dashed var(--rule-soft); font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); line-height: 1.55; }
|
| 23 |
+
.land-stones-deck { font-family: var(--font-serif); font-size: 17px; line-height: 1.6; color: var(--ink-secondary); max-width: 70ch; margin: 0 0 22px; }
|
| 24 |
+
|
| 25 |
+
/* Compact "What you'll get back" · three panes (excerpt + evidence + map) */
|
| 26 |
+
.land-preview-grid { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr); gap: 14px; align-items: stretch; }
|
| 27 |
+
.land-preview-pane { background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); padding: 16px 18px; display: flex; flex-direction: column; gap: 10px; min-width: 0; }
|
| 28 |
+
.land-preview-pane .land-preview-eyebrow { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-tertiary); margin: 0; }
|
| 29 |
+
.land-preview-pane-excerpt .land-preview-body { font-family: var(--font-serif); font-size: 15px; line-height: 1.55; color: var(--ink); margin: 0; }
|
| 30 |
+
.land-preview-pane-excerpt .land-preview-cites { display: flex; flex-direction: column; gap: 4px; padding-top: 10px; border-top: 1px dashed var(--rule-soft); }
|
| 31 |
+
.land-preview-pane-excerpt .land-preview-cite-row { grid-template-columns: 30px 1fr 70px; gap: 8px; font-size: 11px; }
|
| 32 |
+
|
| 33 |
+
.land-preview-pane-cards { gap: 8px; }
|
| 34 |
+
.land-evcard { background: var(--paper); border: 1px solid var(--rule-soft); padding: 8px 10px; display: flex; flex-direction: column; gap: 3px; }
|
| 35 |
+
.land-evcard-empirical { border-left: 2px solid var(--tier-empirical); }
|
| 36 |
+
.land-evcard-modeled { border-left: 2px solid var(--tier-modeled); }
|
| 37 |
+
.land-evcard-head { display: flex; justify-content: space-between; align-items: baseline; font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.06em; }
|
| 38 |
+
.land-evcard-tier { color: var(--ink-secondary); text-transform: uppercase; }
|
| 39 |
+
.land-evcard-empirical .land-evcard-tier { color: var(--tier-empirical); }
|
| 40 |
+
.land-evcard-modeled .land-evcard-tier { color: var(--tier-modeled); }
|
| 41 |
+
.land-evcard-id { color: var(--ink-tertiary); }
|
| 42 |
+
.land-evcard-claim { font-family: var(--font-sans); font-size: 12.5px; line-height: 1.35; color: var(--ink); }
|
| 43 |
+
.land-evcard-source { font-family: var(--font-mono); font-size: 10.5px; color: var(--ink-tertiary); }
|
| 44 |
+
|
| 45 |
+
.land-preview-pane-map { padding: 16px 18px; }
|
| 46 |
+
.land-mapmini { position: relative; aspect-ratio: 6 / 5; border: 1px solid var(--rule-soft); overflow: hidden; }
|
| 47 |
+
.land-mapmini-legend { position: absolute; left: 6px; bottom: 6px; right: 6px; display: flex; gap: 10px; padding: 4px 6px; background: rgba(255,255,255,0.92); font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.04em; color: var(--ink-secondary); }
|
| 48 |
+
.land-mapmini-legend span { display: inline-flex; align-items: center; gap: 4px; }
|
| 49 |
+
.lm-sw { display: inline-block; width: 8px; height: 8px; }
|
| 50 |
+
.lm-sw-emp { background: var(--tier-empirical); }
|
| 51 |
+
.lm-sw-mod { background: rgba(42,111,168,0.4); border: 1px dashed var(--tier-modeled); }
|
| 52 |
+
.lm-sw-prx { background: transparent; border: 1px solid #6B6B6B; border-radius: 50%; }
|
| 53 |
+
.land-preview-mapmeta { font-family: var(--font-mono); font-size: 10.5px; color: var(--ink-tertiary); }
|
| 54 |
+
|
| 55 |
+
@media (max-width: 1000px) {
|
| 56 |
+
.land-preview-grid { grid-template-columns: 1fr 1fr; }
|
| 57 |
+
.land-preview-pane-excerpt { grid-column: 1 / -1; }
|
| 58 |
+
}
|
| 59 |
+
@media (max-width: 640px) {
|
| 60 |
+
.land-preview-grid { grid-template-columns: 1fr; }
|
| 61 |
+
.land-preview-pane-excerpt { grid-column: auto; }
|
| 62 |
+
}
|
| 63 |
+
@media (max-width: 880px) {
|
| 64 |
+
.land-stones-detail { grid-template-columns: 1fr; }
|
| 65 |
+
.land-stones-detail-cell { border-right: none; border-bottom: 1px solid var(--rule-soft); }
|
| 66 |
+
.land-stones-detail-cell:last-child { border-bottom: none; }
|
| 67 |
+
}
|
| 68 |
+
</style>
|
| 69 |
+
</head>
|
| 70 |
+
<body>
|
| 71 |
+
<template id="__bundler_thumbnail">
|
| 72 |
+
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
| 73 |
+
<rect width="100" height="100" fill="#FAFAF7"/>
|
| 74 |
+
<g transform="translate(20 30)" fill="#0B5394">
|
| 75 |
+
<rect x="0" y="0" width="60" height="6"/>
|
| 76 |
+
<rect x="0" y="12" width="60" height="3"/>
|
| 77 |
+
<rect x="0" y="20" width="42" height="3"/>
|
| 78 |
+
<rect x="0" y="30" width="60" height="10"/>
|
| 79 |
+
</g>
|
| 80 |
+
</svg>
|
| 81 |
+
</template>
|
| 82 |
+
<div id="root"></div>
|
| 83 |
+
|
| 84 |
+
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
| 85 |
+
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
| 86 |
+
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
| 87 |
+
|
| 88 |
+
<script type="text/babel">
|
| 89 |
+
const { useState, useEffect } = React;
|
| 90 |
+
|
| 91 |
+
const SAMPLE_QUERIES = [
|
| 92 |
+
"80 Pioneer Street, Red Hook",
|
| 93 |
+
"Coney Island Hospital",
|
| 94 |
+
"PS 188, Lower East Side",
|
| 95 |
+
"Hammels Houses, Rockaway",
|
| 96 |
+
"Bowling Green station",
|
| 97 |
+
"555 W 57th Street",
|
| 98 |
+
];
|
| 99 |
+
|
| 100 |
+
const STONE_FRIEZE = [
|
| 101 |
+
{ name: "Cornerstone", role: "the hazard reader", tag: "what NYC's ground remembers", sources: "USGS HWMs · FEMA NFHL · DEP stormwater · Prithvi historical" },
|
| 102 |
+
{ name: "Keystone", role: "the asset register", tag: "what's exposed", sources: "MTA · NYCHA · DOE · DOH · PLUTO" },
|
| 103 |
+
{ name: "Touchstone", role: "the live observer", tag: "what's happening now", sources: "FloodNet sensors · 311 complaints · tidal gauges" },
|
| 104 |
+
{ name: "Lodestone", role: "the projector", tag: "what's coming", sources: "NPCC4 · TTM foundation model · TerraMind synthetic SAR · NFIP" },
|
| 105 |
+
{ name: "Capstone", role: "the synthesizer", tag: "writes it all down", sources: "Granite composer · Mellea grounding-check · WeasyPrint" },
|
| 106 |
+
];
|
| 107 |
+
|
| 108 |
+
const QueryBox = () => (
|
| 109 |
+
<form className="land-query land-query-lg" onSubmit={(e) => e.preventDefault()}>
|
| 110 |
+
<span className="land-query-prompt" aria-hidden="true">›</span>
|
| 111 |
+
<input
|
| 112 |
+
type="text"
|
| 113 |
+
placeholder="Address, neighborhood, or BBL. e.g. 80 Pioneer Street, Red Hook"
|
| 114 |
+
className="land-query-input"
|
| 115 |
+
aria-label="Query an address, neighborhood, or BBL"
|
| 116 |
+
/>
|
| 117 |
+
<button type="submit" className="land-query-submit">Brief this place →</button>
|
| 118 |
+
</form>
|
| 119 |
+
);
|
| 120 |
+
|
| 121 |
+
const CyclingExamples = () => {
|
| 122 |
+
const [i, setI] = useState(0);
|
| 123 |
+
useEffect(() => {
|
| 124 |
+
const t = setInterval(() => setI((x) => (x + 1) % SAMPLE_QUERIES.length), 2200);
|
| 125 |
+
return () => clearInterval(t);
|
| 126 |
+
}, []);
|
| 127 |
+
return (
|
| 128 |
+
<div className="land-cycling" aria-live="polite">
|
| 129 |
+
<span className="land-cycling-label">Try:</span>
|
| 130 |
+
<span className="land-cycling-rail">
|
| 131 |
+
{SAMPLE_QUERIES.map((q, idx) => (
|
| 132 |
+
<span key={q} className={`land-cycling-item ${idx === i ? "is-active" : ""}`} aria-hidden={idx !== i}>
|
| 133 |
+
{q}
|
| 134 |
+
</span>
|
| 135 |
+
))}
|
| 136 |
+
</span>
|
| 137 |
+
</div>
|
| 138 |
+
);
|
| 139 |
+
};
|
| 140 |
+
|
| 141 |
+
const GroundedOutputPreview = () => (
|
| 142 |
+
<div className="land-preview-grid">
|
| 143 |
+
{/* Pane 1 · Excerpt */}
|
| 144 |
+
<div className="land-preview-pane land-preview-pane-excerpt">
|
| 145 |
+
<div className="land-preview-eyebrow">Briefing excerpt</div>
|
| 146 |
+
<p className="land-preview-body">
|
| 147 |
+
The lot sits inside the FEMA <span className="land-preview-cite">1% AE flood zone <sup>[c3]</sup></span>,
|
| 148 |
+
with Sandy high-water marks recorded
|
| 149 |
+
<span className="land-preview-cite"> 4.7 ft above grade <sup>[c1]</sup></span>.
|
| 150 |
+
FloodNet FN-BK-018 has logged
|
| 151 |
+
<span className="land-preview-cite"> 14 nuisance floods since 2023 <sup>[c2]</sup></span>.
|
| 152 |
+
</p>
|
| 153 |
+
<div className="land-preview-cites">
|
| 154 |
+
<div className="land-preview-cite-row">
|
| 155 |
+
<span className="land-preview-cite-pin">[c1]</span>
|
| 156 |
+
<span className="land-preview-cite-src">USGS HWM · Sandy 2012</span>
|
| 157 |
+
<span className="land-preview-cite-tier">empirical</span>
|
| 158 |
+
</div>
|
| 159 |
+
<div className="land-preview-cite-row">
|
| 160 |
+
<span className="land-preview-cite-pin">[c2]</span>
|
| 161 |
+
<span className="land-preview-cite-src">FloodNet FN-BK-018</span>
|
| 162 |
+
<span className="land-preview-cite-tier">empirical</span>
|
| 163 |
+
</div>
|
| 164 |
+
<div className="land-preview-cite-row">
|
| 165 |
+
<span className="land-preview-cite-pin">[c3]</span>
|
| 166 |
+
<span className="land-preview-cite-src">FEMA NFHL · 36047C0207</span>
|
| 167 |
+
<span className="land-preview-cite-tier">modeled</span>
|
| 168 |
+
</div>
|
| 169 |
+
</div>
|
| 170 |
+
</div>
|
| 171 |
+
|
| 172 |
+
{/* Pane 2 · Evidence cards (2x2 grid) */}
|
| 173 |
+
<div className="land-preview-pane land-preview-pane-cards">
|
| 174 |
+
<div className="land-preview-eyebrow">Evidence cards</div>
|
| 175 |
+
<div className="land-evcard-grid">
|
| 176 |
+
<article className="land-evcard land-evcard-empirical">
|
| 177 |
+
<header className="land-evcard-head">
|
| 178 |
+
<span className="land-evcard-tier">empirical</span>
|
| 179 |
+
<span className="land-evcard-id">e1</span>
|
| 180 |
+
</header>
|
| 181 |
+
<div className="land-evcard-claim">4.7 ft Sandy storm-surge HWM at address</div>
|
| 182 |
+
<div className="land-evcard-source">USGS High-Water Mark database · 2012</div>
|
| 183 |
+
</article>
|
| 184 |
+
<article className="land-evcard land-evcard-empirical">
|
| 185 |
+
<header className="land-evcard-head">
|
| 186 |
+
<span className="land-evcard-tier">empirical</span>
|
| 187 |
+
<span className="land-evcard-id">e2</span>
|
| 188 |
+
</header>
|
| 189 |
+
<div className="land-evcard-claim">14 nuisance-flood events, 2023–2026</div>
|
| 190 |
+
<div className="land-evcard-source">FloodNet FN-BK-018 · 2 blocks north</div>
|
| 191 |
+
</article>
|
| 192 |
+
<article className="land-evcard land-evcard-modeled">
|
| 193 |
+
<header className="land-evcard-head">
|
| 194 |
+
<span className="land-evcard-tier">modeled</span>
|
| 195 |
+
<span className="land-evcard-id">e3</span>
|
| 196 |
+
</header>
|
| 197 |
+
<div className="land-evcard-claim">FEMA 1% annual-chance (AE) flood zone</div>
|
| 198 |
+
<div className="land-evcard-source">FEMA NFHL · panel 36047C0207</div>
|
| 199 |
+
</article>
|
| 200 |
+
<article className="land-evcard land-evcard-modeled">
|
| 201 |
+
<header className="land-evcard-head">
|
| 202 |
+
<span className="land-evcard-tier">modeled</span>
|
| 203 |
+
<span className="land-evcard-id">e5</span>
|
| 204 |
+
</header>
|
| 205 |
+
<div className="land-evcard-claim">+30 in MSL by 2070 (NPCC4 high)</div>
|
| 206 |
+
<div className="land-evcard-source">NPCC4 SLR projection · 2024</div>
|
| 207 |
+
</article>
|
| 208 |
+
</div>
|
| 209 |
+
<div className="land-preview-spacer"/>
|
| 210 |
+
</div>
|
| 211 |
+
|
| 212 |
+
{/* Pane 3 · Mini map */}
|
| 213 |
+
<div className="land-preview-pane land-preview-pane-map">
|
| 214 |
+
<div className="land-preview-eyebrow">Map</div>
|
| 215 |
+
<div className="land-mapmini" role="img" aria-label="Sample exposure map of Red Hook">
|
| 216 |
+
<svg viewBox="0 0 240 200" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" style={{ display: "block", width: "100%", height: "100%" }}>
|
| 217 |
+
{/* base */}
|
| 218 |
+
<rect width="240" height="200" fill="#F2F2EE"/>
|
| 219 |
+
{/* water */}
|
| 220 |
+
<path d="M0,150 Q60,140 120,148 T240,140 L240,200 L0,200 Z" fill="#D6DDE0"/>
|
| 221 |
+
<path d="M0,90 Q40,82 90,88 L120,86 L120,150 L0,150 Z" fill="#D6DDE0"/>
|
| 222 |
+
{/* AE zone (modeled) · translucent fill */}
|
| 223 |
+
<path d="M30,80 L130,76 L160,140 L40,148 Z" fill="rgba(42,111,168,0.22)" stroke="#2A6FA8" strokeWidth="0.8" strokeDasharray="3 2"/>
|
| 224 |
+
{/* HWM contour (empirical) */}
|
| 225 |
+
<path d="M50,90 Q90,84 130,90 T200,108" fill="none" stroke="#0B5394" strokeWidth="1.2"/>
|
| 226 |
+
{/* streets */}
|
| 227 |
+
<g stroke="#B8B5AE" strokeWidth="0.5" fill="none">
|
| 228 |
+
<path d="M0,60 L240,52"/>
|
| 229 |
+
<path d="M0,110 L240,102"/>
|
| 230 |
+
<path d="M60,0 L72,200"/>
|
| 231 |
+
<path d="M120,0 L132,200"/>
|
| 232 |
+
<path d="M180,0 L192,200"/>
|
| 233 |
+
</g>
|
| 234 |
+
{/* FloodNet sensor (empirical · square) */}
|
| 235 |
+
<g transform="translate(108 92)">
|
| 236 |
+
<rect x="-3" y="-3" width="6" height="6" fill="#0B5394" stroke="white" strokeWidth="0.8"/>
|
| 237 |
+
</g>
|
| 238 |
+
{/* 311 cluster (proxy · open circles) */}
|
| 239 |
+
<g fill="none" stroke="#6B6B6B" strokeWidth="0.8">
|
| 240 |
+
<circle cx="80" cy="120" r="3"/>
|
| 241 |
+
<circle cx="86" cy="124" r="3"/>
|
| 242 |
+
<circle cx="92" cy="118" r="3"/>
|
| 243 |
+
</g>
|
| 244 |
+
{/* queried address pin */}
|
| 245 |
+
<g transform="translate(118 112)">
|
| 246 |
+
<circle r="8" fill="none" stroke="#1A1A1A" strokeWidth="1.2"/>
|
| 247 |
+
<circle r="2.4" fill="#1A1A1A"/>
|
| 248 |
+
</g>
|
| 249 |
+
</svg>
|
| 250 |
+
<div className="land-mapmini-legend">
|
| 251 |
+
<span><span className="lm-sw lm-sw-emp"/>empirical</span>
|
| 252 |
+
<span><span className="lm-sw lm-sw-mod"/>modeled</span>
|
| 253 |
+
<span><span className="lm-sw lm-sw-prx"/>proxy</span>
|
| 254 |
+
</div>
|
| 255 |
+
</div>
|
| 256 |
+
<div className="land-preview-mapmeta">Red Hook · z16 · Carto Positron</div>
|
| 257 |
+
</div>
|
| 258 |
+
</div>
|
| 259 |
+
);
|
| 260 |
+
|
| 261 |
+
const StonesSection = () => (
|
| 262 |
+
<section className="land-section-stones-detail" id="methodology">
|
| 263 |
+
<div className="land-page">
|
| 264 |
+
<div className="land-section-head">
|
| 265 |
+
<span className="section-label">How Riprap reads a place</span>
|
| 266 |
+
<span className="land-section-meta">Five Stones · one taxonomy · every briefing</span>
|
| 267 |
+
</div>
|
| 268 |
+
<p className="land-stones-deck">
|
| 269 |
+
Each briefing routes through a fixed taxonomy of public-record specialists. Each Stone is a
|
| 270 |
+
class of evidence. Together they form the briefing, and every claim in the output traces
|
| 271 |
+
back to the Stone that produced it.
|
| 272 |
+
</p>
|
| 273 |
+
<div className="land-stones-detail">
|
| 274 |
+
{STONE_FRIEZE.map((s, i) => (
|
| 275 |
+
<article key={s.name} className="land-stones-detail-cell">
|
| 276 |
+
<div className="land-stones-detail-num">{String(i + 1).padStart(2, "0")}</div>
|
| 277 |
+
<h3 className="land-stones-detail-name">{s.name}</h3>
|
| 278 |
+
<div className="land-stones-detail-role">{s.role}</div>
|
| 279 |
+
<p className="land-stones-detail-tag">{s.tag}</p>
|
| 280 |
+
<div className="land-stones-detail-sources">{s.sources}</div>
|
| 281 |
+
</article>
|
| 282 |
+
))}
|
| 283 |
+
</div>
|
| 284 |
+
</div>
|
| 285 |
+
</section>
|
| 286 |
+
);
|
| 287 |
+
|
| 288 |
+
const Landing = () => (
|
| 289 |
+
<div className="land land-v1">
|
| 290 |
+
<header className="land-header">
|
| 291 |
+
<span className="riprap-wordmark">riprap</span>
|
| 292 |
+
<span className="land-header-sep">/</span>
|
| 293 |
+
<span className="land-header-context">Flood Exposure Briefing · NYC</span>
|
| 294 |
+
<nav className="land-header-nav">
|
| 295 |
+
<a href="#methodology">Methodology</a>
|
| 296 |
+
<a href="#sources">Sources</a>
|
| 297 |
+
<a href="#about">About</a>
|
| 298 |
+
</nav>
|
| 299 |
+
</header>
|
| 300 |
+
|
| 301 |
+
<div className="land-page">
|
| 302 |
+
<main className="land-hero land-hero-v1">
|
| 303 |
+
<h1 className="land-hero-h1">
|
| 304 |
+
<span className="land-hero-headline">A flood exposure briefing<br/>for <em>any place</em> in New York City.</span>
|
| 305 |
+
<span className="land-hero-deck">
|
| 306 |
+
Type an address. Get a written briefing where every numeric claim
|
| 307 |
+
links to its primary public-record source.
|
| 308 |
+
</span>
|
| 309 |
+
</h1>
|
| 310 |
+
<QueryBox/>
|
| 311 |
+
<CyclingExamples/>
|
| 312 |
+
</main>
|
| 313 |
+
<section className="land-section land-section-v1">
|
| 314 |
+
<div className="land-section-head">
|
| 315 |
+
<span className="section-label">What you'll get back</span>
|
| 316 |
+
<span className="land-section-meta">A grounded paragraph with citations, not a chatbot answer.</span>
|
| 317 |
+
</div>
|
| 318 |
+
<GroundedOutputPreview/>
|
| 319 |
+
</section>
|
| 320 |
+
</div>
|
| 321 |
+
|
| 322 |
+
<StonesSection/>
|
| 323 |
+
|
| 324 |
+
<footer className="land-footer">
|
| 325 |
+
<span className="land-footer-tiers">
|
| 326 |
+
<span className="land-footer-tier"><span className="lm-sw lm-sw-emp"/>empirical</span>
|
| 327 |
+
<span className="land-footer-tier"><span className="lm-sw lm-sw-mod"/>modeled</span>
|
| 328 |
+
<span className="land-footer-tier"><span className="lm-sw lm-sw-prx"/>proxy</span>
|
| 329 |
+
<span className="land-footer-tier"><span className="lm-sw lm-sw-syn"/>synthetic</span>
|
| 330 |
+
</span>
|
| 331 |
+
<span className="land-footer-build">Riprap v0.4.4 · NYC OpenData · FEMA NFHL · USGS · NPCC4</span>
|
| 332 |
+
</footer>
|
| 333 |
+
</div>
|
| 334 |
+
);
|
| 335 |
+
|
| 336 |
+
ReactDOM.createRoot(document.getElementById("root")).render(<Landing/>);
|
| 337 |
+
</script>
|
| 338 |
+
</body>
|
| 339 |
+
</html>
|
docs/design_handoff/design_files/Riprap Stone-Grouped UI v0.4.4.html
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 6 |
+
<title>Riprap , Stone-grouped UI mockup v0.4.4</title>
|
| 7 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
| 8 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
| 9 |
+
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400;1,500&family=IBM+Plex+Serif:wght@400;500;600&display=swap" rel="stylesheet" />
|
| 10 |
+
<link rel="stylesheet" href="tokens.css" />
|
| 11 |
+
<link rel="stylesheet" href="styles.css" />
|
| 12 |
+
<style>
|
| 13 |
+
.stone-mock-page { background: var(--paper); padding: 24px 32px 64px; }
|
| 14 |
+
.stone-mock-page > .stone-mock-section + .stone-mock-section { margin-top: 32px; }
|
| 15 |
+
.stone-mock-head { display: flex; justify-content: space-between; align-items: baseline; padding-bottom: 12px; border-bottom: 2px solid var(--ink); margin-bottom: 18px; }
|
| 16 |
+
.stone-mock-head h1 { font-family: var(--font-serif); font-size: 26px; font-weight: 600; margin: 0; }
|
| 17 |
+
.stone-mock-head .meta { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.06em; }
|
| 18 |
+
|
| 19 |
+
/* Stone-grouped evidence layout */
|
| 20 |
+
.stone-ev-layout-head { display: flex; justify-content: space-between; align-items: baseline; padding-bottom: 10px; margin-bottom: 14px; border-bottom: 1px solid var(--rule-soft); }
|
| 21 |
+
.stone-ev-layout-meta { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); }
|
| 22 |
+
|
| 23 |
+
.stone-ev-group { background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); margin-bottom: 14px; }
|
| 24 |
+
.stone-ev-head { display: grid; grid-template-columns: 1fr auto; gap: 12px; padding: 12px 18px; background: var(--stone-band-bg); border-bottom: 1px solid var(--rule-soft); align-items: baseline; }
|
| 25 |
+
.stone-ev-name-row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
|
| 26 |
+
.stone-ev-name { font-family: var(--font-sans); font-size: 16px; font-weight: 600; margin: 0; color: var(--ink); }
|
| 27 |
+
.stone-ev-role { font-size: 13px; color: var(--ink-secondary); }
|
| 28 |
+
.stone-ev-tag { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); }
|
| 29 |
+
.stone-ev-meta { display: flex; gap: 10px; align-items: baseline; font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); }
|
| 30 |
+
.stone-ev-count { font-weight: 600; color: var(--ink); }
|
| 31 |
+
.stone-ev-tally { display: inline-flex; align-items: center; gap: 4px; }
|
| 32 |
+
.stone-ev-rail { padding: 12px 14px; display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; align-items: start; }
|
| 33 |
+
.stone-ev-rail > * { min-width: 0; }
|
| 34 |
+
.stone-ev-empty { padding: 16px 18px; color: var(--ink-tertiary); font-style: italic; font-size: 13px; max-width: 60ch; }
|
| 35 |
+
.stone-ev-empty p { margin: 6px 0 0; line-height: 1.55; }
|
| 36 |
+
|
| 37 |
+
/* Mockup-specific: kill sticky map within the long single-page v0.4.4 layout */
|
| 38 |
+
.stone-mock-page .app-region-map { position: static; max-height: none; }
|
| 39 |
+
|
| 40 |
+
/* Trace row primitives (used inside unified bands) */
|
| 41 |
+
.trace-row { display: grid; grid-template-columns: 16px 1fr 70px 90px 70px; gap: 12px; align-items: baseline; padding: 5px 18px; font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); }
|
| 42 |
+
.trace-row-group > summary { display: grid; grid-template-columns: 16px 1fr 70px 90px 70px; gap: 12px; align-items: baseline; cursor: pointer; list-style: none; }
|
| 43 |
+
.trace-row-group > summary::-webkit-details-marker { display: none; }
|
| 44 |
+
.trace-name { color: var(--ink); }
|
| 45 |
+
.trace-status { color: var(--ink-tertiary); font-size: 11px; }
|
| 46 |
+
.trace-status-err { color: var(--status-error); }
|
| 47 |
+
.trace-tier { font-size: 11px; }
|
| 48 |
+
.trace-ms { text-align: right; color: var(--ink); }
|
| 49 |
+
.trace-row-error { background: var(--status-error-soft); }
|
| 50 |
+
.trace-row-warn .trace-bullet { color: #B26500; font-weight: 700; }
|
| 51 |
+
.trace-row-silent { color: var(--ink-tertiary); }
|
| 52 |
+
.trace-warn-note, .trace-note, .trace-error-summary { grid-column: 2 / -1; font-size: 11px; color: var(--ink-tertiary); font-style: italic; padding-top: 2px; }
|
| 53 |
+
.trace-error-summary { color: var(--status-error); font-style: normal; }
|
| 54 |
+
|
| 55 |
+
/* Run-health strip */
|
| 56 |
+
.run-health { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; padding: 10px 18px; margin-bottom: 14px; background: var(--paper-deep); border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); }
|
| 57 |
+
.run-health-item strong { color: var(--ink); font-weight: 600; }
|
| 58 |
+
.run-health-sep { color: var(--ink-tertiary); }
|
| 59 |
+
.run-health-silent { color: var(--ink-tertiary); }
|
| 60 |
+
.run-health-warn { color: #B26500; }
|
| 61 |
+
.run-health-error { color: var(--status-error); font-weight: 600; }
|
| 62 |
+
|
| 63 |
+
/* Unified Stone band */
|
| 64 |
+
.stone-uni { background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); margin-bottom: 14px; }
|
| 65 |
+
.stone-uni-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 12px 18px; background: var(--stone-band-bg); border-bottom: 1px solid var(--rule-soft); }
|
| 66 |
+
.stone-uni-head-left { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
|
| 67 |
+
.stone-uni-name { font-family: var(--font-sans); font-size: 16px; font-weight: 600; margin: 0; color: var(--ink); }
|
| 68 |
+
.stone-uni-role { font-size: 13px; color: var(--ink-secondary); }
|
| 69 |
+
.stone-uni-tag { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); }
|
| 70 |
+
.stone-uni-agg { font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); display: inline-flex; gap: 4px; flex-wrap: wrap; align-items: baseline; }
|
| 71 |
+
.stone-uni-agg-cards { color: var(--ink); font-weight: 600; }
|
| 72 |
+
.stone-uni-agg-num { color: var(--ink); font-weight: 600; }
|
| 73 |
+
.stone-uni-agg-warn { color: #B26500; font-weight: 600; }
|
| 74 |
+
.stone-uni-agg-err { color: var(--status-error); font-weight: 600; }
|
| 75 |
+
.stone-uni-agg-ms { color: var(--ink); font-weight: 600; }
|
| 76 |
+
.stone-uni-agg-sep { color: var(--ink-tertiary); margin: 0 2px; }
|
| 77 |
+
.stone-uni-rail { padding: 12px 14px; display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; align-items: start; }
|
| 78 |
+
.stone-uni-rail > * { min-width: 0; }
|
| 79 |
+
.stone-uni-empty { padding: 14px 18px; color: var(--ink-tertiary); font-size: 13px; max-width: 70ch; }
|
| 80 |
+
.stone-uni-empty p { margin: 6px 0 0; line-height: 1.55; font-style: italic; }
|
| 81 |
+
.stone-uni-trace { border-top: 1px dashed var(--rule-soft); }
|
| 82 |
+
.stone-uni-trace-toggle { width: 100%; display: flex; align-items: baseline; gap: 8px; padding: 8px 18px; background: transparent; border: none; cursor: pointer; text-align: left; font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.04em; }
|
| 83 |
+
.stone-uni-trace-toggle:hover { background: var(--paper-deep); color: var(--ink-secondary); }
|
| 84 |
+
.stone-uni-trace-caret { display: inline-block; width: 10px; }
|
| 85 |
+
.stone-uni-trace-body { padding: 4px 0 8px; background: var(--paper); border-top: 1px solid var(--rule-soft); }
|
| 86 |
+
|
| 87 |
+
/* ════════════════════════════════════════════════════════════════════
|
| 88 |
+
v0.4.4 · Findings region
|
| 89 |
+
════════════════════════════════════════════════════════════════════ */
|
| 90 |
+
.findings { background: var(--paper); }
|
| 91 |
+
.findings-head { display: flex; justify-content: space-between; align-items: baseline; padding-bottom: 12px; border-bottom: 2px solid var(--ink); margin-bottom: 18px; }
|
| 92 |
+
.findings-h2 { font-family: var(--font-serif); font-size: 26px; font-weight: 600; margin: 0; }
|
| 93 |
+
.findings-tagline { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.04em; }
|
| 94 |
+
|
| 95 |
+
.f-runhealth { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; padding: 10px 18px; margin-bottom: 14px; background: var(--paper-deep); border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); }
|
| 96 |
+
.f-rh-item strong { color: var(--ink); font-weight: 600; }
|
| 97 |
+
.f-rh-sep { color: var(--ink-tertiary); }
|
| 98 |
+
.f-rh-silent { color: var(--ink-tertiary); }
|
| 99 |
+
.f-rh-warn { color: #B26500; }
|
| 100 |
+
.f-rh-err { color: var(--status-error); font-weight: 600; }
|
| 101 |
+
|
| 102 |
+
.f-region { background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); margin-bottom: 14px; }
|
| 103 |
+
.f-region-head { display: flex; justify-content: space-between; gap: 12px; padding: 12px 18px; background: var(--stone-band-bg); border-bottom: 1px solid var(--rule-soft); align-items: baseline; flex-wrap: wrap; }
|
| 104 |
+
.f-region-head-left { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; min-width: 0; }
|
| 105 |
+
.f-region-num { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.06em; }
|
| 106 |
+
.f-region-name { font-family: var(--font-sans); font-size: 16px; font-weight: 600; margin: 0; color: var(--ink); }
|
| 107 |
+
.f-region-role { font-size: 13px; color: var(--ink-secondary); }
|
| 108 |
+
.f-region-tag { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); }
|
| 109 |
+
|
| 110 |
+
.f-tally { font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); display: inline-flex; gap: 6px; flex-wrap: wrap; align-items: baseline; }
|
| 111 |
+
.f-tally-strong { color: var(--ink); font-weight: 600; }
|
| 112 |
+
.f-tally-cards { color: var(--ink); font-weight: 600; }
|
| 113 |
+
.f-tally-sep { color: var(--ink-tertiary); }
|
| 114 |
+
.f-tally-warn { color: #B26500; }
|
| 115 |
+
.f-tally-err { color: var(--status-error); }
|
| 116 |
+
.f-tally-silent { color: var(--ink-tertiary); }
|
| 117 |
+
|
| 118 |
+
.f-rail { padding: 14px; display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; align-items: start; }
|
| 119 |
+
.f-rail > * { min-width: 0; }
|
| 120 |
+
.f-rail-capstone { grid-template-columns: minmax(360px, 480px); }
|
| 121 |
+
|
| 122 |
+
.f-silent { padding: 14px 18px; display: flex; flex-direction: column; gap: 6px; max-width: 70ch; }
|
| 123 |
+
.f-silent-tag { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-tertiary); align-self: flex-start; padding: 2px 6px; border: 1px solid var(--rule-soft); }
|
| 124 |
+
.f-silent-prose { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); margin: 0; line-height: 1.55; }
|
| 125 |
+
|
| 126 |
+
.f-prov { border-top: 1px dashed var(--rule-soft); }
|
| 127 |
+
.f-prov-toggle { width: 100%; display: flex; align-items: baseline; gap: 8px; padding: 8px 18px; background: transparent; border: none; cursor: pointer; text-align: left; font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.04em; }
|
| 128 |
+
.f-prov-toggle:hover { background: var(--paper-deep); color: var(--ink-secondary); }
|
| 129 |
+
.f-prov-caret { display: inline-block; width: 10px; }
|
| 130 |
+
.f-prov-meta { color: var(--ink-tertiary); }
|
| 131 |
+
.f-prov-body { padding: 4px 0 8px; background: var(--paper); border-top: 1px solid var(--rule-soft); }
|
| 132 |
+
|
| 133 |
+
/* ─── Card frame ─── */
|
| 134 |
+
.fc { border: 1px solid var(--rule-soft); background: var(--paper); padding: 12px 14px 10px; display: flex; flex-direction: column; gap: 8px; position: relative; border-top: 2px solid var(--ink); transition: border-color 120ms, box-shadow 120ms; cursor: pointer; }
|
| 135 |
+
.fc-tier-empirical { border-top-color: var(--tier-empirical); }
|
| 136 |
+
.fc-tier-modeled { border-top-color: var(--tier-modeled); }
|
| 137 |
+
.fc-tier-proxy { border-top-color: var(--tier-proxy); }
|
| 138 |
+
.fc-tier-synthetic { border-top-color: var(--tier-synthetic); border-top-style: dashed; }
|
| 139 |
+
.fc:hover, .fc.is-linked { box-shadow: 0 1px 0 var(--ink), 0 0 0 1px var(--ink); }
|
| 140 |
+
.fc.is-compact { padding: 8px 12px 8px; gap: 6px; }
|
| 141 |
+
|
| 142 |
+
.fc-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
|
| 143 |
+
.fc-head-source { display: inline-flex; align-items: center; gap: 6px; font-family: var(--font-sans); font-size: 12px; font-weight: 600; color: var(--ink); }
|
| 144 |
+
.fc-head-source-label { letter-spacing: 0.01em; }
|
| 145 |
+
.fc-head-vintage { font-family: var(--font-mono); font-size: 10.5px; color: var(--ink-tertiary); letter-spacing: 0.02em; }
|
| 146 |
+
|
| 147 |
+
.fc-title { font-family: var(--font-sans); font-size: 13.5px; font-weight: 600; line-height: 1.3; margin: 0; color: var(--ink); text-wrap: pretty; }
|
| 148 |
+
.fc.is-compact .fc-title { font-size: 13px; }
|
| 149 |
+
|
| 150 |
+
.fc-body { display: flex; flex-direction: column; gap: 6px; padding: 2px 0; }
|
| 151 |
+
.fc-body-prose { font-size: 12px; line-height: 1.5; color: var(--ink-secondary); margin: 4px 0 0; }
|
| 152 |
+
.fc-body-sub { font-size: 11px; line-height: 1.5; color: var(--ink-tertiary); font-style: italic; }
|
| 153 |
+
.fc.is-compact .fc-body-prose, .fc.is-compact .fc-body-sub { font-size: 11px; }
|
| 154 |
+
|
| 155 |
+
.fc-headline { font-family: var(--font-serif); font-size: 22px; font-weight: 600; line-height: 1.1; }
|
| 156 |
+
.fc.is-compact .fc-headline { font-size: 19px; }
|
| 157 |
+
.fc-subhead { font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); letter-spacing: 0.02em; }
|
| 158 |
+
|
| 159 |
+
.fc-table { width: 100%; border-collapse: collapse; font-family: var(--font-mono); font-size: 11px; }
|
| 160 |
+
.fc-table th, .fc-table td { text-align: left; padding: 3px 6px; border-bottom: 1px solid var(--rule-soft); }
|
| 161 |
+
.fc-table th { color: var(--ink-tertiary); font-weight: 500; text-transform: uppercase; letter-spacing: 0.08em; font-size: 9px; }
|
| 162 |
+
|
| 163 |
+
.fc-body-scalars .fc-scalars-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; padding: 4px 0; }
|
| 164 |
+
.fc-scalar-cell { display: flex; flex-direction: column; gap: 2px; padding: 6px 4px; background: var(--paper-deep); border: 1px solid var(--rule-soft); }
|
| 165 |
+
.fc-scalar-value { font-family: var(--font-serif); font-size: 18px; font-weight: 600; line-height: 1.05; }
|
| 166 |
+
.fc-scalar-label { font-family: var(--font-mono); font-size: 9.5px; color: var(--ink-tertiary); letter-spacing: 0.04em; }
|
| 167 |
+
|
| 168 |
+
.fc-raster-frame { position: relative; border: 1px solid var(--rule-soft); }
|
| 169 |
+
.fc-illustrative { position: absolute; top: 4px; right: 4px; font-family: var(--font-mono); font-size: 8.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--paper); background: rgba(26,26,26,0.7); padding: 1px 5px; }
|
| 170 |
+
.fc-raster-headline { font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); }
|
| 171 |
+
.fc-raster-headline span { font-family: var(--font-serif); font-size: 14px; font-weight: 600; }
|
| 172 |
+
|
| 173 |
+
.fc-body-timeseries { gap: 4px; }
|
| 174 |
+
.fc-ts-header { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
|
| 175 |
+
.fc-spatial-note { display: inline-block; font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); border: 1px solid var(--accent); padding: 1px 5px; margin-right: 6px; vertical-align: middle; }
|
| 176 |
+
|
| 177 |
+
/* Register composite, dense row layout (v0.4.4 follow-up: cut card height) */
|
| 178 |
+
.fc-body-register .fc-reg-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0; border: 1px solid var(--rule-soft); }
|
| 179 |
+
.fc-reg-row { display: grid; grid-template-columns: 44px minmax(0, 1fr) auto; gap: 6px 10px; align-items: baseline; padding: 3px 8px; border-bottom: 1px solid var(--rule-soft); font-size: 12px; line-height: 1.35; }
|
| 180 |
+
.fc-reg-row:last-child { border-bottom: none; }
|
| 181 |
+
.fc-reg-row.is-silent { grid-template-columns: 44px minmax(0, 1fr); color: var(--ink-tertiary); font-style: italic; background: var(--paper-deep); padding: 2px 8px; }
|
| 182 |
+
.fc-reg-tag { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.08em; color: var(--ink-tertiary); align-self: center; }
|
| 183 |
+
.fc-reg-label { font-family: var(--font-sans); font-size: 12px; font-weight: 500; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
| 184 |
+
.fc-reg-detail { display: none; }
|
| 185 |
+
.fc-reg-tier { display: inline-flex; align-self: center; }
|
| 186 |
+
.fc-reg-source { font-family: var(--font-mono); font-size: 9.5px; color: var(--ink-tertiary); align-self: center; white-space: nowrap; }
|
| 187 |
+
.fc-reg-silent { font-family: var(--font-serif); font-size: 11.5px; color: var(--ink-tertiary); }
|
| 188 |
+
.fc.is-compact .fc-reg-row { padding: 2px 8px; font-size: 11.5px; }
|
| 189 |
+
.fc.is-compact .fc-reg-label { font-size: 11.5px; }
|
| 190 |
+
|
| 191 |
+
/* Comparison */
|
| 192 |
+
.fc-body-comparison .fc-cmp-grid { display: grid; grid-template-columns: 1fr auto 1fr; gap: 8px; align-items: stretch; padding: 4px 0; }
|
| 193 |
+
.fc-cmp-cell { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; background: var(--paper-deep); border: 1px solid var(--rule-soft); }
|
| 194 |
+
.fc-cmp-cell-tier { display: inline-flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: 10px; color: var(--ink-tertiary); letter-spacing: 0.04em; }
|
| 195 |
+
.fc-cmp-cell-label { color: var(--ink-secondary); }
|
| 196 |
+
.fc-cmp-cell-value { font-family: var(--font-serif); font-size: 22px; font-weight: 600; line-height: 1.05; }
|
| 197 |
+
.fc-cmp-cell-aux { font-family: var(--font-mono); font-size: 10px; color: var(--ink-tertiary); }
|
| 198 |
+
.fc-cmp-divider { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); align-self: center; padding: 0 4px; }
|
| 199 |
+
.fc-cmp-delta { font-family: var(--font-mono); font-size: 11px; color: var(--accent); padding: 4px 0; border-top: 1px dashed var(--rule-soft); border-bottom: 1px dashed var(--rule-soft); margin: 4px 0; }
|
| 200 |
+
|
| 201 |
+
/* Meta */
|
| 202 |
+
.fc-body-meta .fc-meta-list { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 12px; margin: 0; }
|
| 203 |
+
.fc-meta-row { display: flex; flex-direction: column; gap: 1px; padding: 4px 6px; background: var(--paper-deep); border-left: 2px solid var(--rule-soft); }
|
| 204 |
+
.fc-meta-row dt { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-tertiary); margin: 0; }
|
| 205 |
+
.fc-meta-row dd { font-family: var(--font-sans); font-size: 13px; font-weight: 500; color: var(--ink); margin: 0; }
|
| 206 |
+
|
| 207 |
+
/* Card foot */
|
| 208 |
+
.fc-foot { display: flex; justify-content: space-between; align-items: center; padding-top: 6px; margin-top: auto; border-top: 1px solid var(--rule-soft); }
|
| 209 |
+
.fc-foot-cite { background: transparent; border: 0; padding: 2px 0; cursor: pointer; display: inline-flex; align-items: center; gap: 5px; color: var(--ink-secondary); }
|
| 210 |
+
.fc-foot-cite:hover { color: var(--accent); }
|
| 211 |
+
.fc-foot-docid { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.02em; }
|
| 212 |
+
.fc-foot-docid-mute { color: var(--ink-tertiary); }
|
| 213 |
+
.fc-foot-arrow { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
| 214 |
+
|
| 215 |
+
.fc-tier-badge { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.08em; padding: 2px 5px; border: 1px solid var(--rule-soft); color: var(--ink-secondary); background: var(--paper); }
|
| 216 |
+
.fc-tier-badge-empirical { border-color: var(--tier-empirical); color: var(--tier-empirical); }
|
| 217 |
+
.fc-tier-badge-modeled { border-color: var(--tier-modeled); color: var(--tier-modeled); }
|
| 218 |
+
.fc-tier-badge-proxy { border-color: var(--tier-proxy); color: var(--tier-proxy); }
|
| 219 |
+
.fc-tier-badge-synthetic { border-color: var(--tier-synthetic); color: var(--tier-synthetic); border-style: dashed; }
|
| 220 |
+
|
| 221 |
+
/* Map highlight on hover-link */
|
| 222 |
+
.map-frame.is-link-floodnet::after,
|
| 223 |
+
.map-frame.is-link-fema-ae::after,
|
| 224 |
+
.map-frame.is-link-hwm::after,
|
| 225 |
+
.map-frame.is-link-stormwater::after,
|
| 226 |
+
.map-frame.is-link-prithvi::after,
|
| 227 |
+
.map-frame.is-link-buildings::after,
|
| 228 |
+
.map-frame.is-link-complaints::after,
|
| 229 |
+
.map-frame.is-link-registers::after {
|
| 230 |
+
content: attr(data-link-label); position: absolute; bottom: 8px; right: 8px;
|
| 231 |
+
background: var(--ink); color: var(--paper); font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
|
| 232 |
+
padding: 4px 8px; pointer-events: none; z-index: 5;
|
| 233 |
+
}
|
| 234 |
+
.map-frame.is-linked { outline: 2px solid var(--accent-graphical); outline-offset: -2px; }
|
| 235 |
+
|
| 236 |
+
</style>
|
| 237 |
+
</head>
|
| 238 |
+
<body>
|
| 239 |
+
<template id="__bundler_thumbnail">
|
| 240 |
+
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
| 241 |
+
<rect width="100" height="100" fill="#FAFAF7"/>
|
| 242 |
+
<g transform="translate(20 28)">
|
| 243 |
+
<rect x="0" y="0" width="60" height="9" fill="#0B5394"/>
|
| 244 |
+
<rect x="0" y="13" width="48" height="9" fill="#2A6FA8"/>
|
| 245 |
+
<rect x="0" y="26" width="38" height="9" fill="#6B6B6B"/>
|
| 246 |
+
<rect x="0" y="39" width="52" height="9" fill="#D17C00"/>
|
| 247 |
+
</g>
|
| 248 |
+
</svg>
|
| 249 |
+
</template>
|
| 250 |
+
<div id="root"></div>
|
| 251 |
+
|
| 252 |
+
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
| 253 |
+
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
| 254 |
+
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
| 255 |
+
|
| 256 |
+
<script type="text/babel" src="tweaks-panel.jsx"></script>
|
| 257 |
+
<script type="text/babel" src="glyphs.jsx"></script>
|
| 258 |
+
<script type="text/babel" src="briefing.jsx"></script>
|
| 259 |
+
<script type="text/babel" src="map.jsx"></script>
|
| 260 |
+
<script type="text/babel" src="trace.jsx"></script>
|
| 261 |
+
<script type="text/babel" src="evidence.jsx"></script>
|
| 262 |
+
<script type="text/babel" src="shell.jsx"></script>
|
| 263 |
+
<script type="text/babel" src="stones-trace.jsx"></script>
|
| 264 |
+
<script type="text/babel" src="stone-evidence.jsx"></script>
|
| 265 |
+
<script type="text/babel" src="findings.jsx"></script>
|
| 266 |
+
<script type="text/babel">
|
| 267 |
+
const { useState: useM } = React;
|
| 268 |
+
|
| 269 |
+
const V44_DEFAULTS = /*EDITMODE-BEGIN*/{
|
| 270 |
+
"density": "comfortable",
|
| 271 |
+
"provenance": "smart",
|
| 272 |
+
"query": "redhook",
|
| 273 |
+
"showComparison": false,
|
| 274 |
+
"showGrammar": true
|
| 275 |
+
}/*EDITMODE-END*/;
|
| 276 |
+
|
| 277 |
+
function StoneMock() {
|
| 278 |
+
const [activeCite, setActiveCite] = useM(null);
|
| 279 |
+
const [linkedKey, setLinkedKey] = useM(null);
|
| 280 |
+
const [tweaks, setTweak] = window.useTweaks(V44_DEFAULTS);
|
| 281 |
+
const handleCite = (id) => setActiveCite(id);
|
| 282 |
+
const handleHover = (key) => setLinkedKey(key);
|
| 283 |
+
/* Reflect linkedKey into map-frame data attribute */
|
| 284 |
+
React.useEffect(() => {
|
| 285 |
+
const frame = document.querySelector(".stone-mock-page .map-frame");
|
| 286 |
+
if (!frame) return;
|
| 287 |
+
/* clear */
|
| 288 |
+
["floodnet","fema-ae","hwm","stormwater","prithvi","buildings","complaints","registers"].forEach(k => frame.classList.remove("is-link-" + k));
|
| 289 |
+
frame.classList.toggle("is-linked", !!linkedKey);
|
| 290 |
+
if (linkedKey) {
|
| 291 |
+
frame.classList.add("is-link-" + linkedKey);
|
| 292 |
+
const labels = { floodnet: "FloodNet sensor", "fema-ae": "FEMA Zone AE", hwm: "USGS HWM points", stormwater: "DEP stormwater", prithvi: "Prithvi flood pred.", buildings: "TerraMind buildings", complaints: "311 complaints", registers: "Asset registers" };
|
| 293 |
+
frame.dataset.linkLabel = labels[linkedKey] || linkedKey;
|
| 294 |
+
}
|
| 295 |
+
}, [linkedKey]);
|
| 296 |
+
|
| 297 |
+
return (
|
| 298 |
+
<div className="riprap-root stone-mock-page">
|
| 299 |
+
<window.AppHeader query={"80 Pioneer Street, Red Hook, Brooklyn"} onResetCold={() => {}} onOpenMethodology={() => {}}/>
|
| 300 |
+
|
| 301 |
+
{/* §1 · Briefing + map */}
|
| 302 |
+
<section className="stone-mock-section" data-screen-label="01 · Briefing + map">
|
| 303 |
+
<div className="stone-mock-head">
|
| 304 |
+
<h1>Riprap Flood Exposure Briefing , 80 Pioneer Street</h1>
|
| 305 |
+
<span className="meta">v0.4.4 · Stone-grouped UI mockup</span>
|
| 306 |
+
</div>
|
| 307 |
+
<div className="app-shell app-shell-desktop">
|
| 308 |
+
<main id="region-briefing" className="app-region app-region-brief">
|
| 309 |
+
<header className="region-head"><span className="section-label">Briefing</span></header>
|
| 310 |
+
<h1 className="brief-h1">
|
| 311 |
+
<span className="brief-h1-eyebrow">Riprap Flood Exposure Briefing</span>
|
| 312 |
+
<span className="brief-h1-addr">80 Pioneer Street</span>
|
| 313 |
+
<span className="brief-h1-meta">
|
| 314 |
+
<span className="brief-h1-meta-row"><span className="brief-h1-meta-key">borough</span><span className="brief-h1-meta-val">Brooklyn · CB6</span></span>
|
| 315 |
+
<span className="brief-h1-meta-row"><span className="brief-h1-meta-key">tract</span><span className="brief-h1-meta-val">36047008500</span></span>
|
| 316 |
+
<span className="brief-h1-meta-row"><span className="brief-h1-meta-key">generated</span><span className="brief-h1-meta-val">2026-05-05 14:22 ET</span></span>
|
| 317 |
+
</span>
|
| 318 |
+
</h1>
|
| 319 |
+
<window.StreamingBriefing onCite={handleCite} replayKey={0}/>
|
| 320 |
+
</main>
|
| 321 |
+
<aside className="app-region app-region-map" aria-label="Map">
|
| 322 |
+
<header className="region-head">
|
| 323 |
+
<span className="section-label">Map</span>
|
| 324 |
+
<span className="region-head-meta">Carto Positron · z16</span>
|
| 325 |
+
</header>
|
| 326 |
+
<div className="map-frame">
|
| 327 |
+
<window.RedHookMapMock activeLayers={{ empirical: true, modeled: true, synthetic: true, proxy: true }} queriedAddress="80 Pioneer Street"/>
|
| 328 |
+
<window.MapLegend activeLayers={{ empirical: true, modeled: true, synthetic: true, proxy: true }} onToggle={() => {}}/>
|
| 329 |
+
</div>
|
| 330 |
+
</aside>
|
| 331 |
+
<aside className="app-region app-region-cites" aria-label="Citations">
|
| 332 |
+
<window.CitationDrawer activeId={activeCite}/>
|
| 333 |
+
</aside>
|
| 334 |
+
</div>
|
| 335 |
+
</section>
|
| 336 |
+
|
| 337 |
+
{/* §2 �� Findings region · v0.4.4 */}
|
| 338 |
+
<section className="stone-mock-section" data-screen-label="02 Findings">
|
| 339 |
+
<window.FindingsRegion
|
| 340 |
+
density={tweaks.density}
|
| 341 |
+
provenanceMode={tweaks.provenance}
|
| 342 |
+
queryKey={tweaks.query}
|
| 343 |
+
showComparison={tweaks.showComparison}
|
| 344 |
+
showGrammar={tweaks.showGrammar}
|
| 345 |
+
onCite={handleCite}
|
| 346 |
+
onHover={handleHover}
|
| 347 |
+
linkedKey={linkedKey}
|
| 348 |
+
/>
|
| 349 |
+
</section>
|
| 350 |
+
|
| 351 |
+
{/* Tweaks panel */}
|
| 352 |
+
<window.TweaksPanel title="Tweaks">
|
| 353 |
+
<window.TweakSection label="Display"/>
|
| 354 |
+
<window.TweakRadio label="Density" value={tweaks.density} onChange={(v) => setTweak("density", v)} options={["comfortable", "compact"]}/>
|
| 355 |
+
<window.TweakSelect label="Query" value={tweaks.query} onChange={(v) => setTweak("query", v)} options={["redhook", "bronx"]}/>
|
| 356 |
+
<window.TweakSection label="Provenance"/>
|
| 357 |
+
<window.TweakSelect label="Default" value={tweaks.provenance} onChange={(v) => setTweak("provenance", v)} options={["smart", "expanded", "collapsed"]}/>
|
| 358 |
+
<window.TweakSection label="Card grammar"/>
|
| 359 |
+
<window.TweakToggle label="Show grammar reference" value={tweaks.showGrammar} onChange={(v) => setTweak("showGrammar", v)}/>
|
| 360 |
+
<window.TweakToggle label="Show comparison card" value={tweaks.showComparison} onChange={(v) => setTweak("showComparison", v)}/>
|
| 361 |
+
</window.TweaksPanel>
|
| 362 |
+
</div>
|
| 363 |
+
);
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
ReactDOM.createRoot(document.getElementById("root")).render(<StoneMock/>);
|
| 367 |
+
</script>
|
| 368 |
+
</body>
|
| 369 |
+
</html>
|
docs/design_handoff/design_files/Riprap Stone-Grouped UI v0.4.5.html
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 6 |
+
<title>Riprap · Stone-grouped UI mockup v0.4.5</title>
|
| 7 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
| 8 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
| 9 |
+
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400;1,500&family=IBM+Plex+Serif:wght@400;500;600&display=swap" rel="stylesheet" />
|
| 10 |
+
<link rel="stylesheet" href="tokens.css" />
|
| 11 |
+
<link rel="stylesheet" href="styles.css" />
|
| 12 |
+
<style>
|
| 13 |
+
.stone-mock-page { background: var(--paper); padding: 24px 32px 64px; }
|
| 14 |
+
.stone-mock-page > .stone-mock-section + .stone-mock-section { margin-top: 32px; }
|
| 15 |
+
.stone-mock-head { display: flex; justify-content: space-between; align-items: baseline; padding-bottom: 12px; border-bottom: 2px solid var(--ink); margin-bottom: 18px; }
|
| 16 |
+
.stone-mock-head h1 { font-family: var(--font-serif); font-size: 26px; font-weight: 600; margin: 0; }
|
| 17 |
+
.stone-mock-head .meta { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.06em; }
|
| 18 |
+
|
| 19 |
+
/* Stone-grouped evidence layout */
|
| 20 |
+
.stone-ev-layout-head { display: flex; justify-content: space-between; align-items: baseline; padding-bottom: 10px; margin-bottom: 14px; border-bottom: 1px solid var(--rule-soft); }
|
| 21 |
+
.stone-ev-layout-meta { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); }
|
| 22 |
+
|
| 23 |
+
.stone-ev-group { background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); margin-bottom: 14px; }
|
| 24 |
+
.stone-ev-head { display: grid; grid-template-columns: 1fr auto; gap: 12px; padding: 12px 18px; background: var(--stone-band-bg); border-bottom: 1px solid var(--rule-soft); align-items: baseline; }
|
| 25 |
+
.stone-ev-name-row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
|
| 26 |
+
.stone-ev-name { font-family: var(--font-sans); font-size: 16px; font-weight: 600; margin: 0; color: var(--ink); }
|
| 27 |
+
.stone-ev-role { font-size: 13px; color: var(--ink-secondary); }
|
| 28 |
+
.stone-ev-tag { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); }
|
| 29 |
+
.stone-ev-meta { display: flex; gap: 10px; align-items: baseline; font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); }
|
| 30 |
+
.stone-ev-count { font-weight: 600; color: var(--ink); }
|
| 31 |
+
.stone-ev-tally { display: inline-flex; align-items: center; gap: 4px; }
|
| 32 |
+
.stone-ev-rail { padding: 12px 14px; display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; align-items: start; }
|
| 33 |
+
.stone-ev-rail > * { min-width: 0; }
|
| 34 |
+
.stone-ev-empty { padding: 16px 18px; color: var(--ink-tertiary); font-style: italic; font-size: 13px; max-width: 60ch; }
|
| 35 |
+
.stone-ev-empty p { margin: 6px 0 0; line-height: 1.55; }
|
| 36 |
+
|
| 37 |
+
/* Mockup-specific: kill sticky map within the long single-page v0.4.4 layout */
|
| 38 |
+
.stone-mock-page .app-region-map { position: static; max-height: none; }
|
| 39 |
+
|
| 40 |
+
/* Trace row primitives (used inside unified bands) */
|
| 41 |
+
.trace-row { display: grid; grid-template-columns: 16px 1fr 70px 90px 70px; gap: 12px; align-items: baseline; padding: 5px 18px; font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); }
|
| 42 |
+
.trace-row-group > summary { display: grid; grid-template-columns: 16px 1fr 70px 90px 70px; gap: 12px; align-items: baseline; cursor: pointer; list-style: none; }
|
| 43 |
+
.trace-row-group > summary::-webkit-details-marker { display: none; }
|
| 44 |
+
.trace-name { color: var(--ink); }
|
| 45 |
+
.trace-status { color: var(--ink-tertiary); font-size: 11px; }
|
| 46 |
+
.trace-status-err { color: var(--status-error); }
|
| 47 |
+
.trace-tier { font-size: 11px; }
|
| 48 |
+
.trace-ms { text-align: right; color: var(--ink); }
|
| 49 |
+
.trace-row-error { background: var(--status-error-soft); }
|
| 50 |
+
.trace-row-warn .trace-bullet { color: #B26500; font-weight: 700; }
|
| 51 |
+
.trace-row-silent { color: var(--ink-tertiary); }
|
| 52 |
+
.trace-warn-note, .trace-note, .trace-error-summary { grid-column: 2 / -1; font-size: 11px; color: var(--ink-tertiary); font-style: italic; padding-top: 2px; }
|
| 53 |
+
.trace-error-summary { color: var(--status-error); font-style: normal; }
|
| 54 |
+
|
| 55 |
+
/* Run-health strip */
|
| 56 |
+
.run-health { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; padding: 10px 18px; margin-bottom: 14px; background: var(--paper-deep); border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); }
|
| 57 |
+
.run-health-item strong { color: var(--ink); font-weight: 600; }
|
| 58 |
+
.run-health-sep { color: var(--ink-tertiary); }
|
| 59 |
+
.run-health-silent { color: var(--ink-tertiary); }
|
| 60 |
+
.run-health-warn { color: #B26500; }
|
| 61 |
+
.run-health-error { color: var(--status-error); font-weight: 600; }
|
| 62 |
+
|
| 63 |
+
/* Unified Stone band */
|
| 64 |
+
.stone-uni { background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); margin-bottom: 14px; }
|
| 65 |
+
.stone-uni-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 12px 18px; background: var(--stone-band-bg); border-bottom: 1px solid var(--rule-soft); }
|
| 66 |
+
.stone-uni-head-left { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
|
| 67 |
+
.stone-uni-name { font-family: var(--font-sans); font-size: 16px; font-weight: 600; margin: 0; color: var(--ink); }
|
| 68 |
+
.stone-uni-role { font-size: 13px; color: var(--ink-secondary); }
|
| 69 |
+
.stone-uni-tag { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); }
|
| 70 |
+
.stone-uni-agg { font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); display: inline-flex; gap: 4px; flex-wrap: wrap; align-items: baseline; }
|
| 71 |
+
.stone-uni-agg-cards { color: var(--ink); font-weight: 600; }
|
| 72 |
+
.stone-uni-agg-num { color: var(--ink); font-weight: 600; }
|
| 73 |
+
.stone-uni-agg-warn { color: #B26500; font-weight: 600; }
|
| 74 |
+
.stone-uni-agg-err { color: var(--status-error); font-weight: 600; }
|
| 75 |
+
.stone-uni-agg-ms { color: var(--ink); font-weight: 600; }
|
| 76 |
+
.stone-uni-agg-sep { color: var(--ink-tertiary); margin: 0 2px; }
|
| 77 |
+
.stone-uni-rail { padding: 12px 14px; display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; align-items: start; }
|
| 78 |
+
.stone-uni-rail > * { min-width: 0; }
|
| 79 |
+
.stone-uni-empty { padding: 14px 18px; color: var(--ink-tertiary); font-size: 13px; max-width: 70ch; }
|
| 80 |
+
.stone-uni-empty p { margin: 6px 0 0; line-height: 1.55; font-style: italic; }
|
| 81 |
+
.stone-uni-trace { border-top: 1px dashed var(--rule-soft); }
|
| 82 |
+
.stone-uni-trace-toggle { width: 100%; display: flex; align-items: baseline; gap: 8px; padding: 8px 18px; background: transparent; border: none; cursor: pointer; text-align: left; font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.04em; }
|
| 83 |
+
.stone-uni-trace-toggle:hover { background: var(--paper-deep); color: var(--ink-secondary); }
|
| 84 |
+
.stone-uni-trace-caret { display: inline-block; width: 10px; }
|
| 85 |
+
.stone-uni-trace-body { padding: 4px 0 8px; background: var(--paper); border-top: 1px solid var(--rule-soft); }
|
| 86 |
+
|
| 87 |
+
/* ════════════════════════════════════════════════════════════════════
|
| 88 |
+
v0.4.4 · Findings region
|
| 89 |
+
════════════════════════════════════════════════════════════════════ */
|
| 90 |
+
.findings { background: var(--paper); }
|
| 91 |
+
.findings-head { display: flex; justify-content: space-between; align-items: baseline; padding-bottom: 12px; border-bottom: 2px solid var(--ink); margin-bottom: 18px; }
|
| 92 |
+
.findings-h2 { font-family: var(--font-serif); font-size: 26px; font-weight: 600; margin: 0; }
|
| 93 |
+
.findings-tagline { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.04em; }
|
| 94 |
+
|
| 95 |
+
.f-runhealth { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; padding: 10px 18px; margin-bottom: 14px; background: var(--paper-deep); border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); }
|
| 96 |
+
.f-rh-item strong { color: var(--ink); font-weight: 600; }
|
| 97 |
+
.f-rh-sep { color: var(--ink-tertiary); }
|
| 98 |
+
.f-rh-silent { color: var(--ink-tertiary); }
|
| 99 |
+
.f-rh-warn { color: #B26500; }
|
| 100 |
+
.f-rh-err { color: var(--status-error); font-weight: 600; }
|
| 101 |
+
|
| 102 |
+
.f-region { background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); margin-bottom: 14px; }
|
| 103 |
+
.f-region-head { display: flex; justify-content: space-between; gap: 12px; padding: 12px 18px; background: var(--stone-band-bg); border-bottom: 1px solid var(--rule-soft); align-items: baseline; flex-wrap: wrap; }
|
| 104 |
+
.f-region-head-left { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; min-width: 0; }
|
| 105 |
+
.f-region-num { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.06em; }
|
| 106 |
+
.f-region-name { font-family: var(--font-sans); font-size: 16px; font-weight: 600; margin: 0; color: var(--ink); }
|
| 107 |
+
.f-region-role { font-size: 13px; color: var(--ink-secondary); }
|
| 108 |
+
.f-region-tag { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); }
|
| 109 |
+
|
| 110 |
+
.f-tally { font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); display: inline-flex; gap: 6px; flex-wrap: wrap; align-items: baseline; }
|
| 111 |
+
.f-tally-strong { color: var(--ink); font-weight: 600; }
|
| 112 |
+
.f-tally-cards { color: var(--ink); font-weight: 600; }
|
| 113 |
+
.f-tally-sep { color: var(--ink-tertiary); }
|
| 114 |
+
.f-tally-warn { color: #B26500; }
|
| 115 |
+
.f-tally-err { color: var(--status-error); }
|
| 116 |
+
.f-tally-silent { color: var(--ink-tertiary); }
|
| 117 |
+
|
| 118 |
+
.f-rail { padding: 14px; display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; align-items: start; }
|
| 119 |
+
.f-rail > * { min-width: 0; }
|
| 120 |
+
.f-rail-capstone { grid-template-columns: minmax(360px, 480px); }
|
| 121 |
+
|
| 122 |
+
.f-silent { padding: 14px 18px; display: flex; flex-direction: column; gap: 6px; max-width: 70ch; }
|
| 123 |
+
.f-silent-tag { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-tertiary); align-self: flex-start; padding: 2px 6px; border: 1px solid var(--rule-soft); }
|
| 124 |
+
.f-silent-prose { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); margin: 0; line-height: 1.55; }
|
| 125 |
+
|
| 126 |
+
.f-prov { border-top: 1px dashed var(--rule-soft); }
|
| 127 |
+
.f-prov-toggle { width: 100%; display: flex; align-items: baseline; gap: 8px; padding: 8px 18px; background: transparent; border: none; cursor: pointer; text-align: left; font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.04em; }
|
| 128 |
+
.f-prov-toggle:hover { background: var(--paper-deep); color: var(--ink-secondary); }
|
| 129 |
+
.f-prov-caret { display: inline-block; width: 10px; }
|
| 130 |
+
.f-prov-meta { color: var(--ink-tertiary); }
|
| 131 |
+
.f-prov-body { padding: 4px 0 8px; background: var(--paper); border-top: 1px solid var(--rule-soft); }
|
| 132 |
+
|
| 133 |
+
/* ─── Card frame ─── */
|
| 134 |
+
.fc { border: 1px solid var(--rule-soft); background: var(--paper); padding: 12px 14px 10px; display: flex; flex-direction: column; gap: 8px; position: relative; border-top: 2px solid var(--ink); transition: border-color 120ms, box-shadow 120ms; cursor: pointer; }
|
| 135 |
+
.fc-tier-empirical { border-top-color: var(--tier-empirical); }
|
| 136 |
+
.fc-tier-modeled { border-top-color: var(--tier-modeled); }
|
| 137 |
+
.fc-tier-proxy { border-top-color: var(--tier-proxy); }
|
| 138 |
+
.fc-tier-synthetic { border-top-color: var(--tier-synthetic); border-top-style: dashed; }
|
| 139 |
+
.fc:hover, .fc.is-linked { box-shadow: 0 1px 0 var(--ink), 0 0 0 1px var(--ink); }
|
| 140 |
+
.fc.is-compact { padding: 8px 12px 8px; gap: 6px; }
|
| 141 |
+
|
| 142 |
+
.fc-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
|
| 143 |
+
.fc-head-source { display: inline-flex; align-items: center; gap: 6px; font-family: var(--font-sans); font-size: 12px; font-weight: 600; color: var(--ink); }
|
| 144 |
+
.fc-head-source-label { letter-spacing: 0.01em; }
|
| 145 |
+
.fc-head-vintage { font-family: var(--font-mono); font-size: 10.5px; color: var(--ink-tertiary); letter-spacing: 0.02em; }
|
| 146 |
+
|
| 147 |
+
.fc-title { font-family: var(--font-sans); font-size: 13.5px; font-weight: 600; line-height: 1.3; margin: 0; color: var(--ink); text-wrap: pretty; }
|
| 148 |
+
.fc.is-compact .fc-title { font-size: 13px; }
|
| 149 |
+
|
| 150 |
+
.fc-body { display: flex; flex-direction: column; gap: 6px; padding: 2px 0; }
|
| 151 |
+
.fc-body-prose { font-size: 12px; line-height: 1.5; color: var(--ink-secondary); margin: 4px 0 0; }
|
| 152 |
+
.fc-body-sub { font-size: 11px; line-height: 1.5; color: var(--ink-tertiary); font-style: italic; }
|
| 153 |
+
.fc.is-compact .fc-body-prose, .fc.is-compact .fc-body-sub { font-size: 11px; }
|
| 154 |
+
|
| 155 |
+
.fc-headline { font-family: var(--font-serif); font-size: 22px; font-weight: 600; line-height: 1.1; }
|
| 156 |
+
.fc.is-compact .fc-headline { font-size: 19px; }
|
| 157 |
+
.fc-subhead { font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); letter-spacing: 0.02em; }
|
| 158 |
+
|
| 159 |
+
.fc-table { width: 100%; border-collapse: collapse; font-family: var(--font-mono); font-size: 11px; }
|
| 160 |
+
.fc-table th, .fc-table td { text-align: left; padding: 3px 6px; border-bottom: 1px solid var(--rule-soft); }
|
| 161 |
+
.fc-table th { color: var(--ink-tertiary); font-weight: 500; text-transform: uppercase; letter-spacing: 0.08em; font-size: 9px; }
|
| 162 |
+
|
| 163 |
+
.fc-body-scalars .fc-scalars-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; padding: 4px 0; }
|
| 164 |
+
.fc-scalar-cell { display: flex; flex-direction: column; gap: 2px; padding: 6px 4px; background: var(--paper-deep); border: 1px solid var(--rule-soft); }
|
| 165 |
+
.fc-scalar-value { font-family: var(--font-serif); font-size: 18px; font-weight: 600; line-height: 1.05; }
|
| 166 |
+
.fc-scalar-label { font-family: var(--font-mono); font-size: 9.5px; color: var(--ink-tertiary); letter-spacing: 0.04em; }
|
| 167 |
+
|
| 168 |
+
.fc-raster-frame { position: relative; border: 1px solid var(--rule-soft); }
|
| 169 |
+
.fc-illustrative { position: absolute; top: 4px; right: 4px; font-family: var(--font-mono); font-size: 8.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--paper); background: rgba(26,26,26,0.7); padding: 1px 5px; }
|
| 170 |
+
.fc-raster-headline { font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); }
|
| 171 |
+
.fc-raster-headline span { font-family: var(--font-serif); font-size: 14px; font-weight: 600; }
|
| 172 |
+
|
| 173 |
+
.fc-body-timeseries { gap: 4px; }
|
| 174 |
+
.fc-ts-header { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
|
| 175 |
+
.fc-spatial-note { display: inline-block; font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); border: 1px solid var(--accent); padding: 1px 5px; margin-right: 6px; vertical-align: middle; }
|
| 176 |
+
|
| 177 |
+
/* Register composite, dense row layout (v0.4.4 follow-up: cut card height) */
|
| 178 |
+
.fc-body-register .fc-reg-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0; border: 1px solid var(--rule-soft); }
|
| 179 |
+
.fc-reg-row { display: grid; grid-template-columns: 44px minmax(0, 1fr) auto; gap: 6px 10px; align-items: baseline; padding: 3px 8px; border-bottom: 1px solid var(--rule-soft); font-size: 12px; line-height: 1.35; }
|
| 180 |
+
.fc-reg-row:last-child { border-bottom: none; }
|
| 181 |
+
.fc-reg-row.is-silent { grid-template-columns: 44px minmax(0, 1fr); color: var(--ink-tertiary); font-style: italic; background: var(--paper-deep); padding: 2px 8px; }
|
| 182 |
+
.fc-reg-tag { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.08em; color: var(--ink-tertiary); align-self: center; }
|
| 183 |
+
.fc-reg-label { font-family: var(--font-sans); font-size: 12px; font-weight: 500; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
| 184 |
+
.fc-reg-detail { display: none; }
|
| 185 |
+
.fc-reg-tier { display: inline-flex; align-self: center; }
|
| 186 |
+
.fc-reg-source { font-family: var(--font-mono); font-size: 9.5px; color: var(--ink-tertiary); align-self: center; white-space: nowrap; }
|
| 187 |
+
.fc-reg-silent { font-family: var(--font-serif); font-size: 11.5px; color: var(--ink-tertiary); }
|
| 188 |
+
.fc.is-compact .fc-reg-row { padding: 2px 8px; font-size: 11.5px; }
|
| 189 |
+
.fc.is-compact .fc-reg-label { font-size: 11.5px; }
|
| 190 |
+
|
| 191 |
+
/* Comparison */
|
| 192 |
+
.fc-body-comparison .fc-cmp-grid { display: grid; grid-template-columns: 1fr auto 1fr; gap: 8px; align-items: stretch; padding: 4px 0; }
|
| 193 |
+
.fc-cmp-cell { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; background: var(--paper-deep); border: 1px solid var(--rule-soft); }
|
| 194 |
+
.fc-cmp-cell-tier { display: inline-flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: 10px; color: var(--ink-tertiary); letter-spacing: 0.04em; }
|
| 195 |
+
.fc-cmp-cell-label { color: var(--ink-secondary); }
|
| 196 |
+
.fc-cmp-cell-value { font-family: var(--font-serif); font-size: 22px; font-weight: 600; line-height: 1.05; }
|
| 197 |
+
.fc-cmp-cell-aux { font-family: var(--font-mono); font-size: 10px; color: var(--ink-tertiary); }
|
| 198 |
+
.fc-cmp-divider { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); align-self: center; padding: 0 4px; }
|
| 199 |
+
.fc-cmp-delta { font-family: var(--font-mono); font-size: 11px; color: var(--accent); padding: 4px 0; border-top: 1px dashed var(--rule-soft); border-bottom: 1px dashed var(--rule-soft); margin: 4px 0; }
|
| 200 |
+
|
| 201 |
+
/* Meta */
|
| 202 |
+
.fc-body-meta .fc-meta-list { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 12px; margin: 0; }
|
| 203 |
+
.fc-meta-row { display: flex; flex-direction: column; gap: 1px; padding: 4px 6px; background: var(--paper-deep); border-left: 2px solid var(--rule-soft); }
|
| 204 |
+
.fc-meta-row dt { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-tertiary); margin: 0; }
|
| 205 |
+
.fc-meta-row dd { font-family: var(--font-sans); font-size: 13px; font-weight: 500; color: var(--ink); margin: 0; }
|
| 206 |
+
|
| 207 |
+
/* Card foot */
|
| 208 |
+
.fc-foot { display: flex; justify-content: space-between; align-items: center; padding-top: 6px; margin-top: auto; border-top: 1px solid var(--rule-soft); }
|
| 209 |
+
.fc-foot-cite { background: transparent; border: 0; padding: 2px 0; cursor: pointer; display: inline-flex; align-items: center; gap: 5px; color: var(--ink-secondary); }
|
| 210 |
+
.fc-foot-cite:hover { color: var(--accent); }
|
| 211 |
+
.fc-foot-docid { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.02em; }
|
| 212 |
+
.fc-foot-docid-mute { color: var(--ink-tertiary); }
|
| 213 |
+
.fc-foot-arrow { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
| 214 |
+
|
| 215 |
+
.fc-tier-badge { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.08em; padding: 2px 5px; border: 1px solid var(--rule-soft); color: var(--ink-secondary); background: var(--paper); }
|
| 216 |
+
.fc-tier-badge-empirical { border-color: var(--tier-empirical); color: var(--tier-empirical); }
|
| 217 |
+
.fc-tier-badge-modeled { border-color: var(--tier-modeled); color: var(--tier-modeled); }
|
| 218 |
+
.fc-tier-badge-proxy { border-color: var(--tier-proxy); color: var(--tier-proxy); }
|
| 219 |
+
.fc-tier-badge-synthetic { border-color: var(--tier-synthetic); color: var(--tier-synthetic); border-style: dashed; }
|
| 220 |
+
|
| 221 |
+
/* Map highlight on hover-link */
|
| 222 |
+
.map-frame.is-link-floodnet::after,
|
| 223 |
+
.map-frame.is-link-fema-ae::after,
|
| 224 |
+
.map-frame.is-link-hwm::after,
|
| 225 |
+
.map-frame.is-link-stormwater::after,
|
| 226 |
+
.map-frame.is-link-prithvi::after,
|
| 227 |
+
.map-frame.is-link-buildings::after,
|
| 228 |
+
.map-frame.is-link-complaints::after,
|
| 229 |
+
.map-frame.is-link-registers::after {
|
| 230 |
+
content: attr(data-link-label); position: absolute; bottom: 8px; right: 8px;
|
| 231 |
+
background: var(--ink); color: var(--paper); font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
|
| 232 |
+
padding: 4px 8px; pointer-events: none; z-index: 5;
|
| 233 |
+
}
|
| 234 |
+
.map-frame.is-linked { outline: 2px solid var(--accent-graphical); outline-offset: -2px; }
|
| 235 |
+
|
| 236 |
+
</style>
|
| 237 |
+
</head>
|
| 238 |
+
<body>
|
| 239 |
+
<template id="__bundler_thumbnail">
|
| 240 |
+
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
| 241 |
+
<rect width="100" height="100" fill="#FAFAF7"/>
|
| 242 |
+
<g transform="translate(20 28)">
|
| 243 |
+
<rect x="0" y="0" width="60" height="9" fill="#0B5394"/>
|
| 244 |
+
<rect x="0" y="13" width="48" height="9" fill="#2A6FA8"/>
|
| 245 |
+
<rect x="0" y="26" width="38" height="9" fill="#6B6B6B"/>
|
| 246 |
+
<rect x="0" y="39" width="52" height="9" fill="#D17C00"/>
|
| 247 |
+
</g>
|
| 248 |
+
</svg>
|
| 249 |
+
</template>
|
| 250 |
+
<div id="root"></div>
|
| 251 |
+
|
| 252 |
+
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
| 253 |
+
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
| 254 |
+
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
| 255 |
+
|
| 256 |
+
<script type="text/babel" src="tweaks-panel.jsx"></script>
|
| 257 |
+
<script type="text/babel" src="glyphs.jsx"></script>
|
| 258 |
+
<script type="text/babel" src="briefing.jsx"></script>
|
| 259 |
+
<script type="text/babel" src="map.jsx"></script>
|
| 260 |
+
<script type="text/babel" src="trace.jsx"></script>
|
| 261 |
+
<script type="text/babel" src="evidence.jsx"></script>
|
| 262 |
+
<script type="text/babel" src="shell.jsx"></script>
|
| 263 |
+
<script type="text/babel" src="stones-trace.jsx"></script>
|
| 264 |
+
<script type="text/babel" src="stone-evidence.jsx"></script>
|
| 265 |
+
<script type="text/babel" src="findings.jsx"></script>
|
| 266 |
+
<script type="text/babel">
|
| 267 |
+
const { useState: useM } = React;
|
| 268 |
+
|
| 269 |
+
const V44_DEFAULTS = /*EDITMODE-BEGIN*/{
|
| 270 |
+
"density": "comfortable",
|
| 271 |
+
"provenance": "smart",
|
| 272 |
+
"query": "redhook",
|
| 273 |
+
"showComparison": false,
|
| 274 |
+
"showGrammar": true
|
| 275 |
+
}/*EDITMODE-END*/;
|
| 276 |
+
|
| 277 |
+
function StoneMock() {
|
| 278 |
+
const [activeCite, setActiveCite] = useM(null);
|
| 279 |
+
const [linkedKey, setLinkedKey] = useM(null);
|
| 280 |
+
const [tweaks, setTweak] = window.useTweaks(V44_DEFAULTS);
|
| 281 |
+
const handleCite = (id) => setActiveCite(id);
|
| 282 |
+
const handleHover = (key) => setLinkedKey(key);
|
| 283 |
+
/* Reflect linkedKey into map-frame data attribute */
|
| 284 |
+
React.useEffect(() => {
|
| 285 |
+
const frame = document.querySelector(".stone-mock-page .map-frame");
|
| 286 |
+
if (!frame) return;
|
| 287 |
+
/* clear */
|
| 288 |
+
["floodnet","fema-ae","hwm","stormwater","prithvi","buildings","complaints","registers"].forEach(k => frame.classList.remove("is-link-" + k));
|
| 289 |
+
frame.classList.toggle("is-linked", !!linkedKey);
|
| 290 |
+
if (linkedKey) {
|
| 291 |
+
frame.classList.add("is-link-" + linkedKey);
|
| 292 |
+
const labels = { floodnet: "FloodNet sensor", "fema-ae": "FEMA Zone AE", hwm: "USGS HWM points", stormwater: "DEP stormwater", prithvi: "Prithvi flood pred.", buildings: "TerraMind buildings", complaints: "311 complaints", registers: "Asset registers" };
|
| 293 |
+
frame.dataset.linkLabel = labels[linkedKey] || linkedKey;
|
| 294 |
+
}
|
| 295 |
+
}, [linkedKey]);
|
| 296 |
+
|
| 297 |
+
return (
|
| 298 |
+
<div className="riprap-root stone-mock-page">
|
| 299 |
+
<window.AppHeader query={"80 Pioneer Street, Red Hook, Brooklyn"} onResetCold={() => {}} onOpenMethodology={() => {}}/>
|
| 300 |
+
|
| 301 |
+
{/* §1 · Briefing + map */}
|
| 302 |
+
<section className="stone-mock-section" data-screen-label="01 · Briefing + map">
|
| 303 |
+
<div className="stone-mock-head">
|
| 304 |
+
<h1>Riprap Flood Exposure Briefing , 80 Pioneer Street</h1>
|
| 305 |
+
<span className="meta">v0.4.5 · Stone-grouped UI mockup</span>
|
| 306 |
+
</div>
|
| 307 |
+
<div className="app-shell app-shell-desktop">
|
| 308 |
+
<main id="region-briefing" className="app-region app-region-brief">
|
| 309 |
+
<header className="region-head"><span className="section-label">Briefing</span></header>
|
| 310 |
+
<h1 className="brief-h1">
|
| 311 |
+
<span className="brief-h1-eyebrow">Riprap Flood Exposure Briefing</span>
|
| 312 |
+
<span className="brief-h1-addr">80 Pioneer Street</span>
|
| 313 |
+
<span className="brief-h1-meta">
|
| 314 |
+
<span className="brief-h1-meta-row"><span className="brief-h1-meta-key">borough</span><span className="brief-h1-meta-val">Brooklyn · CB6</span></span>
|
| 315 |
+
<span className="brief-h1-meta-row"><span className="brief-h1-meta-key">tract</span><span className="brief-h1-meta-val">36047008500</span></span>
|
| 316 |
+
<span className="brief-h1-meta-row"><span className="brief-h1-meta-key">generated</span><span className="brief-h1-meta-val">2026-05-05 14:22 ET</span></span>
|
| 317 |
+
</span>
|
| 318 |
+
</h1>
|
| 319 |
+
<window.StreamingBriefing onCite={handleCite} replayKey={0}/>
|
| 320 |
+
</main>
|
| 321 |
+
<aside className="app-region app-region-map" aria-label="Map">
|
| 322 |
+
<header className="region-head">
|
| 323 |
+
<span className="section-label">Map</span>
|
| 324 |
+
<span className="region-head-meta">Carto Positron · z16</span>
|
| 325 |
+
</header>
|
| 326 |
+
<div className="map-frame">
|
| 327 |
+
<window.RedHookMapMock activeLayers={{ empirical: true, modeled: true, synthetic: true, proxy: true, "prithvi-pluvial": false }} queriedAddress="80 Pioneer Street"/>
|
| 328 |
+
<window.MapLegend activeLayers={{ empirical: true, modeled: true, synthetic: true, proxy: true, "prithvi-pluvial": false }} onToggle={() => {}}/>
|
| 329 |
+
</div>
|
| 330 |
+
</aside>
|
| 331 |
+
<aside className="app-region app-region-cites" aria-label="Citations">
|
| 332 |
+
<window.CitationDrawer activeId={activeCite}/>
|
| 333 |
+
</aside>
|
| 334 |
+
</div>
|
| 335 |
+
</section>
|
| 336 |
+
|
| 337 |
+
{/* §2 · Findings region · v0.4.4 */}
|
| 338 |
+
<section className="stone-mock-section" data-screen-label="02 Findings">
|
| 339 |
+
<window.FindingsRegion
|
| 340 |
+
density={tweaks.density}
|
| 341 |
+
provenanceMode={tweaks.provenance}
|
| 342 |
+
queryKey={tweaks.query}
|
| 343 |
+
showComparison={tweaks.showComparison}
|
| 344 |
+
showGrammar={tweaks.showGrammar}
|
| 345 |
+
onCite={handleCite}
|
| 346 |
+
onHover={handleHover}
|
| 347 |
+
linkedKey={linkedKey}
|
| 348 |
+
/>
|
| 349 |
+
</section>
|
| 350 |
+
|
| 351 |
+
{/* Tweaks panel */}
|
| 352 |
+
<window.TweaksPanel title="Tweaks">
|
| 353 |
+
<window.TweakSection label="Display"/>
|
| 354 |
+
<window.TweakRadio label="Density" value={tweaks.density} onChange={(v) => setTweak("density", v)} options={["comfortable", "compact"]}/>
|
| 355 |
+
<window.TweakSelect label="Query" value={tweaks.query} onChange={(v) => setTweak("query", v)} options={["redhook", "bronx"]}/>
|
| 356 |
+
<window.TweakSection label="Provenance"/>
|
| 357 |
+
<window.TweakSelect label="Default" value={tweaks.provenance} onChange={(v) => setTweak("provenance", v)} options={["smart", "expanded", "collapsed"]}/>
|
| 358 |
+
<window.TweakSection label="Card grammar"/>
|
| 359 |
+
<window.TweakToggle label="Show grammar reference" value={tweaks.showGrammar} onChange={(v) => setTweak("showGrammar", v)}/>
|
| 360 |
+
<window.TweakToggle label="Show comparison card" value={tweaks.showComparison} onChange={(v) => setTweak("showComparison", v)}/>
|
| 361 |
+
</window.TweaksPanel>
|
| 362 |
+
</div>
|
| 363 |
+
);
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
ReactDOM.createRoot(document.getElementById("root")).render(<StoneMock/>);
|
| 367 |
+
</script>
|
| 368 |
+
</body>
|
| 369 |
+
</html>
|
docs/design_handoff/design_files/briefing.jsx
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Briefing prose with epistemic-tier glyph margin.
|
| 2 |
+
Each <Claim tier=... cite=...> renders the glyph in the left margin
|
| 3 |
+
and a hoverable superscript citation that scrolls the citation drawer.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
const { useState, useRef, useEffect, useMemo } = React;
|
| 7 |
+
|
| 8 |
+
/* ── Citation registry for the sample briefing ───────────────────── */
|
| 9 |
+
const CITATIONS = {
|
| 10 |
+
c1: {
|
| 11 |
+
n: 1,
|
| 12 |
+
tier: "empirical",
|
| 13 |
+
source: "USGS",
|
| 14 |
+
title: "Hurricane Sandy storm tide elevations, NY-NJ Harbor",
|
| 15 |
+
docId: "USGS-OFR-2013-1234",
|
| 16 |
+
url: "https://pubs.usgs.gov/of/2013/1234/",
|
| 17 |
+
vintage: "2013-05",
|
| 18 |
+
retrieved: "2026-04-28",
|
| 19 |
+
},
|
| 20 |
+
c2: {
|
| 21 |
+
n: 2,
|
| 22 |
+
tier: "empirical",
|
| 23 |
+
source: "NYC OEM",
|
| 24 |
+
title: "Hurricane Sandy Inundation Zone (2012)",
|
| 25 |
+
docId: "NYCOEM-SIZ-2013",
|
| 26 |
+
url: "https://data.cityofnewyork.us/dataset/sandy-inundation-zone",
|
| 27 |
+
vintage: "2013-01",
|
| 28 |
+
retrieved: "2026-04-28",
|
| 29 |
+
},
|
| 30 |
+
c3: {
|
| 31 |
+
n: 3,
|
| 32 |
+
tier: "empirical",
|
| 33 |
+
source: "FloodNet NYC",
|
| 34 |
+
title: "Sensor BK-RH-002 , Coffey Park, monthly exceedance",
|
| 35 |
+
docId: "FN-BK-RH-002",
|
| 36 |
+
url: "https://floodnet.nyc/sensor/BK-RH-002",
|
| 37 |
+
vintage: "2026-04",
|
| 38 |
+
retrieved: "2026-05-02",
|
| 39 |
+
},
|
| 40 |
+
c4: {
|
| 41 |
+
n: 4,
|
| 42 |
+
tier: "modeled",
|
| 43 |
+
source: "FEMA",
|
| 44 |
+
title: "Preliminary Flood Insurance Rate Map, panel 36047C0207G",
|
| 45 |
+
docId: "FEMA-FIRM-36047C0207G",
|
| 46 |
+
url: "https://msc.fema.gov/portal/search",
|
| 47 |
+
vintage: "2024-09",
|
| 48 |
+
retrieved: "2026-04-28",
|
| 49 |
+
},
|
| 50 |
+
c5: {
|
| 51 |
+
n: 5,
|
| 52 |
+
tier: "modeled",
|
| 53 |
+
source: "NYC DEP",
|
| 54 |
+
title: "Stormwater Flood Map , Moderate Stormwater Scenario",
|
| 55 |
+
docId: "NYCDEP-SWFM-2024",
|
| 56 |
+
url: "https://nyc.gov/stormwater-map",
|
| 57 |
+
vintage: "2024-06",
|
| 58 |
+
retrieved: "2026-04-28",
|
| 59 |
+
},
|
| 60 |
+
c6: {
|
| 61 |
+
n: 6,
|
| 62 |
+
tier: "modeled",
|
| 63 |
+
source: "NPCC4",
|
| 64 |
+
title: "Sea-level rise projections, 2050 90th percentile",
|
| 65 |
+
docId: "NPCC4-Ch3-Tbl3.2",
|
| 66 |
+
url: "https://nyas.org/npcc4",
|
| 67 |
+
vintage: "2024-03",
|
| 68 |
+
retrieved: "2026-04-28",
|
| 69 |
+
},
|
| 70 |
+
c7: {
|
| 71 |
+
n: 7,
|
| 72 |
+
tier: "proxy",
|
| 73 |
+
source: "NYC 311",
|
| 74 |
+
title: "Flooding service requests, BK CB6 2019–2025",
|
| 75 |
+
docId: "NYC311-FLD-CB6",
|
| 76 |
+
url: "https://data.cityofnewyork.us/311",
|
| 77 |
+
vintage: "2025-12",
|
| 78 |
+
retrieved: "2026-05-01",
|
| 79 |
+
},
|
| 80 |
+
c8: {
|
| 81 |
+
n: 8,
|
| 82 |
+
tier: "proxy",
|
| 83 |
+
source: "FEMA NFIP",
|
| 84 |
+
title: "National Flood Insurance Program claims, tract 36047008500",
|
| 85 |
+
docId: "NFIP-T36047008500",
|
| 86 |
+
url: "https://www.fema.gov/openfema",
|
| 87 |
+
vintage: "2024-12",
|
| 88 |
+
retrieved: "2026-04-28",
|
| 89 |
+
},
|
| 90 |
+
c9: {
|
| 91 |
+
n: 9,
|
| 92 |
+
tier: "synthetic",
|
| 93 |
+
source: "TerraMind v1.2",
|
| 94 |
+
title: "Synthetic SAR backscatter for 2025-09-14 (Sentinel-1 cloud-occluded)",
|
| 95 |
+
docId: "RIPRAP-SYN-20250914",
|
| 96 |
+
url: "#methodology-synthetic",
|
| 97 |
+
vintage: "2025-09",
|
| 98 |
+
retrieved: "2026-05-02",
|
| 99 |
+
},
|
| 100 |
+
c10: {
|
| 101 |
+
n: 10,
|
| 102 |
+
tier: "modeled",
|
| 103 |
+
source: "NYC DCP",
|
| 104 |
+
title: "Waterfront Revitalization Program , Coastal Risk Area",
|
| 105 |
+
docId: "NYCDCP-WRP-2022",
|
| 106 |
+
url: "https://nyc.gov/dcp/wrp",
|
| 107 |
+
vintage: "2022-11",
|
| 108 |
+
retrieved: "2026-04-28",
|
| 109 |
+
},
|
| 110 |
+
};
|
| 111 |
+
|
| 112 |
+
const Cite = ({ id, onActivate }) => {
|
| 113 |
+
const c = CITATIONS[id];
|
| 114 |
+
if (!c) return null;
|
| 115 |
+
return (
|
| 116 |
+
<a
|
| 117 |
+
href={`#cite-${id}`}
|
| 118 |
+
className="inline-cite"
|
| 119 |
+
data-cite={id}
|
| 120 |
+
onClick={(e) => {
|
| 121 |
+
e.preventDefault();
|
| 122 |
+
onActivate?.(id);
|
| 123 |
+
}}
|
| 124 |
+
aria-label={`Citation ${c.n}: ${c.source}, ${c.title}`}
|
| 125 |
+
>
|
| 126 |
+
<sup>[{c.n}]</sup>
|
| 127 |
+
</a>
|
| 128 |
+
);
|
| 129 |
+
};
|
| 130 |
+
|
| 131 |
+
const Claim = ({ tier, children }) => (
|
| 132 |
+
<span className={`claim claim-${tier}`} data-tier={tier}>
|
| 133 |
+
<span className="claim-glyph" aria-hidden="false">
|
| 134 |
+
<TierGlyph tier={tier} size={11} color={`var(--tier-${tier})`} />
|
| 135 |
+
</span>
|
| 136 |
+
<span className="claim-body">{children}</span>
|
| 137 |
+
</span>
|
| 138 |
+
);
|
| 139 |
+
|
| 140 |
+
const SectionHead = ({ n, label, tier, children }) => (
|
| 141 |
+
<h3 className="briefing-section-head">
|
| 142 |
+
<span className="briefing-section-num">{n}</span>
|
| 143 |
+
<span className="briefing-section-label">{label}</span>
|
| 144 |
+
{tier && (
|
| 145 |
+
<span className="briefing-section-tier">
|
| 146 |
+
<TierBadge tier={tier} compact />
|
| 147 |
+
</span>
|
| 148 |
+
)}
|
| 149 |
+
{children && <span className="briefing-section-title">{children}</span>}
|
| 150 |
+
</h3>
|
| 151 |
+
);
|
| 152 |
+
|
| 153 |
+
/* ── Sample briefing: 80 Pioneer St, Red Hook, Brooklyn ─────────── */
|
| 154 |
+
const BRIEFING_BLOCKS = [
|
| 155 |
+
{ kind: "status", html: `
|
| 156 |
+
<p class="briefing-deck">
|
| 157 |
+
<strong>80 Pioneer Street, Red Hook, Brooklyn 11231.</strong>
|
| 158 |
+
Block 597, Lot 30. Industrial Business Zone (IBZ-RH).
|
| 159 |
+
Queried 2026-05-05 14:22 ET. <span class="briefing-meta">Briefing v0.4.4 · 5 Stones engaged · Keystone silent (no register joins matched)</span>
|
| 160 |
+
</p>
|
| 161 |
+
` },
|
| 162 |
+
|
| 163 |
+
{ kind: "head", n: "01", label: "Status", title: "Coastal-edge, post-Sandy, multi-hazard" },
|
| 164 |
+
{ kind: "prose", parts: [
|
| 165 |
+
{ tier: "empirical", text: "The address sits 380 ft inland of the Erie Basin bulkhead, at a ground elevation of 6.2 ft NAVD88", cite: "c1" },
|
| 166 |
+
{ text: " , within the " },
|
| 167 |
+
{ tier: "empirical", text: "2012 Sandy Inundation Zone, which recorded a peak storm tide of 11.4 ft NAVD88 at the Battery", cite: "c2" },
|
| 168 |
+
{ text: " 2.4 mi to the northwest. " },
|
| 169 |
+
{ tier: "modeled", text: "FEMA's preliminary FIRM places the parcel in Zone AE (BFE 11 ft NAVD88)", cite: "c4" },
|
| 170 |
+
{ text: ", a 4.8 ft freeboard above current grade. The site is upgradient of two FloodNet sensors and three blocks from a chronic 311 cluster." },
|
| 171 |
+
]},
|
| 172 |
+
|
| 173 |
+
{ kind: "head", n: "02", label: "Empirical evidence", tier: "empirical" },
|
| 174 |
+
{ kind: "prose", parts: [
|
| 175 |
+
{ tier: "empirical", text: "FloodNet sensor BK-RH-002 (Coffey Park, 1,200 ft south) recorded 7 above-curb events between 2024-06 and 2026-04", cite: "c3" },
|
| 176 |
+
{ text: ", with a peak depth of 14.3 cm during the 2025-09-29 nor'easter. " },
|
| 177 |
+
{ tier: "empirical", text: "USGS post-Sandy high-water marks within 500 ft cluster between 6.8 and 8.1 ft NAVD88", cite: "c1" },
|
| 178 |
+
{ text: ", consistent with 0.6–1.9 ft of standing water at the queried address during the storm." },
|
| 179 |
+
]},
|
| 180 |
+
|
| 181 |
+
{ kind: "head", n: "03", label: "Modeled scenarios", tier: "modeled" },
|
| 182 |
+
{ kind: "prose", parts: [
|
| 183 |
+
{ tier: "modeled", text: "DEP's Moderate Stormwater Scenario (2.13 in/hr design storm) shows ponding ≥4 in across the western half of the lot", cite: "c5" },
|
| 184 |
+
{ text: ", routed by the 1.2% slope toward Imlay St. " },
|
| 185 |
+
{ tier: "modeled", text: "Under NPCC4's 2050 90th-percentile sea-level rise (30 in)", cite: "c6" },
|
| 186 |
+
{ text: ", the parcel falls within the projected daily-tidal floodplain by mid-century. " },
|
| 187 |
+
{ tier: "synthetic", text: "Synthetic SAR backscatter for 2025-09-14 (Sentinel-1 cloud-occluded) was generated by TerraMind v1.2 and is presented as a prior, not an observation", cite: "c9" },
|
| 188 |
+
{ text: "; treat with appropriate caution." },
|
| 189 |
+
]},
|
| 190 |
+
|
| 191 |
+
{ kind: "head", n: "04", label: "Policy context" },
|
| 192 |
+
{ kind: "prose", parts: [
|
| 193 |
+
{ tier: "proxy", text: "311 flood complaints within the surrounding census tract total 89 calls over 2019–2025, with seasonal clustering in Aug–Oct", cite: "c7" },
|
| 194 |
+
{ text: ". " },
|
| 195 |
+
{ tier: "proxy", text: "NFIP claims aggregated to tract 36047008500 total $4.1M across 47 paid losses since 2000", cite: "c8" },
|
| 196 |
+
{ text: ". " },
|
| 197 |
+
{ tier: "modeled", text: "The site lies within the NYC Waterfront Revitalization Program Coastal Risk Area; CEQR §817 review applies to any discretionary action", cite: "c10" },
|
| 198 |
+
{ text: "." },
|
| 199 |
+
]},
|
| 200 |
+
];
|
| 201 |
+
|
| 202 |
+
/* ── Streaming renderer ───────────────────────────────────────────
|
| 203 |
+
Uses CSS reveal (token-by-token via animation-delay) instead of
|
| 204 |
+
recomputing innerText, to avoid layout shift.
|
| 205 |
+
*/
|
| 206 |
+
const StreamingBriefing = ({ onCite, replayKey }) => {
|
| 207 |
+
const [visibleCount, setVisibleCount] = useState(0);
|
| 208 |
+
const totalBlocks = BRIEFING_BLOCKS.length;
|
| 209 |
+
|
| 210 |
+
useEffect(() => {
|
| 211 |
+
setVisibleCount(0);
|
| 212 |
+
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
| 213 |
+
if (reduce) {
|
| 214 |
+
setVisibleCount(totalBlocks);
|
| 215 |
+
return;
|
| 216 |
+
}
|
| 217 |
+
let i = 0;
|
| 218 |
+
const tick = () => {
|
| 219 |
+
i++;
|
| 220 |
+
setVisibleCount(i);
|
| 221 |
+
if (i < totalBlocks) {
|
| 222 |
+
setTimeout(tick, i < 2 ? 280 : 420);
|
| 223 |
+
}
|
| 224 |
+
};
|
| 225 |
+
const t = setTimeout(tick, 240);
|
| 226 |
+
return () => clearTimeout(t);
|
| 227 |
+
}, [replayKey]);
|
| 228 |
+
|
| 229 |
+
return (
|
| 230 |
+
<div
|
| 231 |
+
className="briefing-prose"
|
| 232 |
+
role="log"
|
| 233 |
+
aria-live="polite"
|
| 234 |
+
aria-atomic="false"
|
| 235 |
+
aria-label="Streaming flood-exposure briefing"
|
| 236 |
+
>
|
| 237 |
+
{BRIEFING_BLOCKS.slice(0, visibleCount).map((b, i) => {
|
| 238 |
+
if (b.kind === "status") {
|
| 239 |
+
return <div key={i} className="briefing-status" dangerouslySetInnerHTML={{ __html: b.html }} />;
|
| 240 |
+
}
|
| 241 |
+
if (b.kind === "head") {
|
| 242 |
+
return <SectionHead key={i} n={b.n} label={b.label} tier={b.tier}>{b.title}</SectionHead>;
|
| 243 |
+
}
|
| 244 |
+
if (b.kind === "prose") {
|
| 245 |
+
return (
|
| 246 |
+
<p key={i} className="briefing-para">
|
| 247 |
+
{b.parts.map((p, j) => {
|
| 248 |
+
if (p.tier) {
|
| 249 |
+
return (
|
| 250 |
+
<React.Fragment key={j}>
|
| 251 |
+
<Claim tier={p.tier}>{p.text}</Claim>
|
| 252 |
+
{p.cite && <Cite id={p.cite} onActivate={onCite} />}
|
| 253 |
+
</React.Fragment>
|
| 254 |
+
);
|
| 255 |
+
}
|
| 256 |
+
return <span key={j}>{p.text}</span>;
|
| 257 |
+
})}
|
| 258 |
+
</p>
|
| 259 |
+
);
|
| 260 |
+
}
|
| 261 |
+
return null;
|
| 262 |
+
})}
|
| 263 |
+
{visibleCount < totalBlocks && (
|
| 264 |
+
<span className="streaming-caret" aria-hidden="true">▍</span>
|
| 265 |
+
)}
|
| 266 |
+
</div>
|
| 267 |
+
);
|
| 268 |
+
};
|
| 269 |
+
|
| 270 |
+
const CitationDrawer = ({ activeId, onClose }) => {
|
| 271 |
+
const items = Object.entries(CITATIONS);
|
| 272 |
+
return (
|
| 273 |
+
<aside className="citation-drawer" aria-label="Citations">
|
| 274 |
+
<div className="citation-drawer-head">
|
| 275 |
+
<span className="section-label">Citations · {items.length}</span>
|
| 276 |
+
<span className="citation-drawer-meta">live · primary sources</span>
|
| 277 |
+
</div>
|
| 278 |
+
<ol className="citation-list">
|
| 279 |
+
{items.map(([id, c]) => (
|
| 280 |
+
<li
|
| 281 |
+
key={id}
|
| 282 |
+
id={`cite-${id}`}
|
| 283 |
+
className={`citation-item ${activeId === id ? "is-active" : ""}`}
|
| 284 |
+
>
|
| 285 |
+
<span className="citation-num">[{c.n}]</span>
|
| 286 |
+
<div className="citation-body">
|
| 287 |
+
<div className="citation-line-1">
|
| 288 |
+
<TierGlyph tier={c.tier} size={10} color={`var(--tier-${c.tier})`} />
|
| 289 |
+
<span className="citation-source">{c.source}</span>
|
| 290 |
+
<span className="citation-vintage">v. {c.vintage}</span>
|
| 291 |
+
</div>
|
| 292 |
+
<div className="citation-title">{c.title}</div>
|
| 293 |
+
<div className="citation-meta">
|
| 294 |
+
<span className="citation-docid">{c.docId}</span>
|
| 295 |
+
<span className="citation-retrieved">retr. {c.retrieved}</span>
|
| 296 |
+
</div>
|
| 297 |
+
</div>
|
| 298 |
+
</li>
|
| 299 |
+
))}
|
| 300 |
+
</ol>
|
| 301 |
+
<div className="citation-drawer-foot">
|
| 302 |
+
<span className="section-label">Trust signals</span>
|
| 303 |
+
<p className="citation-foot-copy">
|
| 304 |
+
All foundation models Apache-2.0. All data from public-record federal,
|
| 305 |
+
state, and city sources. No commercial APIs contacted at runtime.
|
| 306 |
+
</p>
|
| 307 |
+
</div>
|
| 308 |
+
</aside>
|
| 309 |
+
);
|
| 310 |
+
};
|
| 311 |
+
|
| 312 |
+
Object.assign(window, { StreamingBriefing, CitationDrawer, CITATIONS, Cite, Claim });
|
docs/design_handoff/design_files/design-canvas.jsx
ADDED
|
@@ -0,0 +1,936 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
// DesignCanvas.jsx — Figma-ish design canvas wrapper
|
| 3 |
+
// Warm gray grid bg + Sections + Artboards + PostIt notes.
|
| 4 |
+
// Artboards are reorderable (grip-drag), deletable, labels/titles are
|
| 5 |
+
// inline-editable, and any artboard can be opened in a fullscreen focus
|
| 6 |
+
// overlay (←/→/Esc). State persists to a .design-canvas.state.json sidecar
|
| 7 |
+
// via the host bridge. No assets, no deps.
|
| 8 |
+
//
|
| 9 |
+
// Usage:
|
| 10 |
+
// <DesignCanvas>
|
| 11 |
+
// <DCSection id="onboarding" title="Onboarding" subtitle="First-run variants">
|
| 12 |
+
// <DCArtboard id="a" label="A · Dusk" width={260} height={480}>…</DCArtboard>
|
| 13 |
+
// <DCArtboard id="b" label="B · Minimal" width={260} height={480}>…</DCArtboard>
|
| 14 |
+
// </DCSection>
|
| 15 |
+
// </DesignCanvas>
|
| 16 |
+
|
| 17 |
+
const DC = {
|
| 18 |
+
bg: '#f0eee9',
|
| 19 |
+
grid: 'rgba(0,0,0,0.06)',
|
| 20 |
+
label: 'rgba(60,50,40,0.7)',
|
| 21 |
+
title: 'rgba(40,30,20,0.85)',
|
| 22 |
+
subtitle: 'rgba(60,50,40,0.6)',
|
| 23 |
+
postitBg: '#fef4a8',
|
| 24 |
+
postitText: '#5a4a2a',
|
| 25 |
+
font: '-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',
|
| 26 |
+
};
|
| 27 |
+
|
| 28 |
+
// One-time CSS injection (classes are dc-prefixed so they don't collide with
|
| 29 |
+
// the hosted design's own styles).
|
| 30 |
+
if (typeof document !== 'undefined' && !document.getElementById('dc-styles')) {
|
| 31 |
+
const s = document.createElement('style');
|
| 32 |
+
s.id = 'dc-styles';
|
| 33 |
+
s.textContent = [
|
| 34 |
+
'.dc-editable{cursor:text;outline:none;white-space:nowrap;border-radius:3px;padding:0 2px;margin:0 -2px}',
|
| 35 |
+
'.dc-editable:focus{background:#fff;box-shadow:0 0 0 1.5px #c96442}',
|
| 36 |
+
'[data-dc-slot]{transition:transform .18s cubic-bezier(.2,.7,.3,1)}',
|
| 37 |
+
'[data-dc-slot].dc-dragging{transition:none;z-index:10;pointer-events:none}',
|
| 38 |
+
'[data-dc-slot].dc-dragging .dc-card{box-shadow:0 12px 40px rgba(0,0,0,.25),0 0 0 2px #c96442;transform:scale(1.02)}',
|
| 39 |
+
// isolation:isolate contains artboard content's z-indexes so a
|
| 40 |
+
// z-indexed child (sticky navbar etc.) can't paint over .dc-header or
|
| 41 |
+
// the .dc-menu popover that drops into the top of the card.
|
| 42 |
+
'.dc-card{isolation:isolate;transition:box-shadow .15s,transform .15s}',
|
| 43 |
+
'.dc-card *{scrollbar-width:none}',
|
| 44 |
+
'.dc-card *::-webkit-scrollbar{display:none}',
|
| 45 |
+
// Per-artboard header: grip + label on the left, delete/expand on the
|
| 46 |
+
// right. Single flex row; when the artboard's on-screen width is too
|
| 47 |
+
// narrow for both the label yields (ellipsis, then hidden entirely below
|
| 48 |
+
// ~4ch via the container query) and the buttons stay on the row.
|
| 49 |
+
'.dc-header{position:absolute;bottom:100%;left:-4px;margin-bottom:calc(4px * var(--dc-inv-zoom,1));z-index:2;',
|
| 50 |
+
' display:flex;align-items:center;container-type:inline-size}',
|
| 51 |
+
'.dc-labelrow{display:flex;align-items:center;gap:4px;height:24px;flex:1 1 auto;min-width:0}',
|
| 52 |
+
'.dc-grip{flex:0 0 auto;cursor:grab;display:flex;align-items:center;padding:5px 4px;border-radius:4px;transition:background .12s,opacity .12s}',
|
| 53 |
+
'.dc-grip:hover{background:rgba(0,0,0,.08)}',
|
| 54 |
+
'.dc-grip:active{cursor:grabbing}',
|
| 55 |
+
'.dc-labeltext{flex:1 1 auto;min-width:0;cursor:pointer;border-radius:4px;padding:3px 6px;',
|
| 56 |
+
' display:flex;align-items:center;transition:background .12s;overflow:hidden}',
|
| 57 |
+
// Below ~4ch of label room: hide the label entirely, and drop the grip to
|
| 58 |
+
// hover-only (same reveal rule as .dc-btns) so a narrow header is clean
|
| 59 |
+
// until the card is moused.
|
| 60 |
+
'@container (max-width: 110px){',
|
| 61 |
+
' .dc-labeltext{display:none}',
|
| 62 |
+
' .dc-grip{opacity:0}',
|
| 63 |
+
' [data-dc-slot]:hover .dc-grip{opacity:1}',
|
| 64 |
+
'}',
|
| 65 |
+
'.dc-labeltext:hover{background:rgba(0,0,0,.05)}',
|
| 66 |
+
'.dc-labeltext .dc-editable{overflow:hidden;text-overflow:ellipsis;max-width:100%}',
|
| 67 |
+
'.dc-labeltext .dc-editable:focus{overflow:visible;text-overflow:clip}',
|
| 68 |
+
'.dc-btns{flex:0 0 auto;margin-left:auto;display:flex;gap:2px;opacity:0;transition:opacity .12s}',
|
| 69 |
+
'[data-dc-slot]:hover .dc-btns,.dc-btns:has(.dc-menu){opacity:1}',
|
| 70 |
+
'.dc-expand,.dc-kebab{width:22px;height:22px;border-radius:5px;border:none;cursor:pointer;padding:0;',
|
| 71 |
+
' background:transparent;color:rgba(60,50,40,.7);display:flex;align-items:center;justify-content:center;',
|
| 72 |
+
' font:inherit;transition:background .12s,color .12s}',
|
| 73 |
+
'.dc-expand:hover,.dc-kebab:hover{background:rgba(0,0,0,.06);color:#2a251f}',
|
| 74 |
+
// Slot hosting an open menu floats above later siblings (which otherwise
|
| 75 |
+
// paint on top — same z-index:auto, later DOM order) so the popup isn't
|
| 76 |
+
// clipped by the next card.
|
| 77 |
+
'[data-dc-slot]:has(.dc-menu){z-index:10}',
|
| 78 |
+
'.dc-menu{position:absolute;top:100%;right:0;margin-top:4px;background:#fff;border-radius:8px;',
|
| 79 |
+
' box-shadow:0 8px 28px rgba(0,0,0,.18),0 0 0 1px rgba(0,0,0,.05);padding:4px;min-width:160px;z-index:10}',
|
| 80 |
+
'.dc-menu button{display:block;width:100%;padding:7px 10px;border:0;background:transparent;',
|
| 81 |
+
' border-radius:5px;font-family:inherit;font-size:13px;font-weight:500;line-height:1.2;',
|
| 82 |
+
' color:#29261b;cursor:pointer;text-align:left;transition:background .12s;white-space:nowrap}',
|
| 83 |
+
'.dc-menu button:hover{background:rgba(0,0,0,.05)}',
|
| 84 |
+
'.dc-menu hr{border:0;border-top:1px solid rgba(0,0,0,.08);margin:4px 2px}',
|
| 85 |
+
'.dc-menu .dc-danger{color:#c96442}',
|
| 86 |
+
'.dc-menu .dc-danger:hover{background:rgba(201,100,66,.1)}',
|
| 87 |
+
// Chrome (titles / labels / buttons) counter-scales against the viewport
|
| 88 |
+
// zoom so it stays a constant on-screen size. --dc-inv-zoom is set by
|
| 89 |
+
// DCViewport on every transform update and inherits to all descendants —
|
| 90 |
+
// any overlay inside the world (e.g. a TweaksPanel on an artboard) can use
|
| 91 |
+
// it the same way.
|
| 92 |
+
//
|
| 93 |
+
// The header uses transform:scale (out-of-flow, so layout impact doesn't
|
| 94 |
+
// matter) with its world-space width set to card-width / inv-zoom so that
|
| 95 |
+
// after counter-scaling its on-screen width exactly matches the card's —
|
| 96 |
+
// that's what lets the container query + text-overflow behave against the
|
| 97 |
+
// card's visible edge at every zoom level.
|
| 98 |
+
//
|
| 99 |
+
// The section head uses CSS zoom instead of transform so its layout box
|
| 100 |
+
// grows with the counter-scale, pushing the card row down — otherwise the
|
| 101 |
+
// constant-screen-size title would overflow into the (shrinking) world-
|
| 102 |
+
// space gap and overlap the artboard headers at low zoom.
|
| 103 |
+
'.dc-header{width:calc((100% + 4px) / var(--dc-inv-zoom,1));',
|
| 104 |
+
' transform:scale(var(--dc-inv-zoom,1));transform-origin:bottom left}',
|
| 105 |
+
'.dc-sectionhead{zoom:var(--dc-inv-zoom,1)}',
|
| 106 |
+
].join('\n');
|
| 107 |
+
document.head.appendChild(s);
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
const DCCtx = React.createContext(null);
|
| 111 |
+
|
| 112 |
+
// ─────────────────────────────────────────────────────────────
|
| 113 |
+
// DesignCanvas — stateful wrapper around the pan/zoom viewport.
|
| 114 |
+
// Owns runtime state (per-section order, renamed titles/labels, hidden
|
| 115 |
+
// artboards, focused artboard). Order/titles/labels/hidden persist to a
|
| 116 |
+
// .design-canvas.state.json
|
| 117 |
+
// sidecar next to the HTML. Reads go via plain fetch() so the saved
|
| 118 |
+
// arrangement is visible anywhere the HTML + sidecar are served together
|
| 119 |
+
// (omelette preview, direct link, downloaded zip). Writes go through the
|
| 120 |
+
// host's window.omelette bridge — editing requires the omelette runtime.
|
| 121 |
+
// Focus is ephemeral.
|
| 122 |
+
// ─────────────────────────────────────────────────────────────
|
| 123 |
+
const DC_STATE_FILE = '.design-canvas.state.json';
|
| 124 |
+
|
| 125 |
+
function DesignCanvas({ children, minScale, maxScale, style }) {
|
| 126 |
+
const [state, setState] = React.useState({ sections: {}, focus: null });
|
| 127 |
+
// Hold rendering until the sidecar read settles so the saved order/titles
|
| 128 |
+
// appear on first paint (no source-order flash). didRead gates writes until
|
| 129 |
+
// the read settles so the empty initial state can't clobber a slow read;
|
| 130 |
+
// skipNextWrite suppresses the one echo-write that would otherwise follow
|
| 131 |
+
// hydration.
|
| 132 |
+
const [ready, setReady] = React.useState(false);
|
| 133 |
+
const didRead = React.useRef(false);
|
| 134 |
+
const skipNextWrite = React.useRef(false);
|
| 135 |
+
|
| 136 |
+
React.useEffect(() => {
|
| 137 |
+
let off = false;
|
| 138 |
+
fetch('./' + DC_STATE_FILE)
|
| 139 |
+
.then((r) => (r.ok ? r.json() : null))
|
| 140 |
+
.then((saved) => {
|
| 141 |
+
if (off || !saved || !saved.sections) return;
|
| 142 |
+
skipNextWrite.current = true;
|
| 143 |
+
setState((s) => ({ ...s, sections: saved.sections }));
|
| 144 |
+
})
|
| 145 |
+
.catch(() => {})
|
| 146 |
+
.finally(() => { didRead.current = true; if (!off) setReady(true); });
|
| 147 |
+
const t = setTimeout(() => { if (!off) setReady(true); }, 150);
|
| 148 |
+
return () => { off = true; clearTimeout(t); };
|
| 149 |
+
}, []);
|
| 150 |
+
|
| 151 |
+
React.useEffect(() => {
|
| 152 |
+
if (!didRead.current) return;
|
| 153 |
+
if (skipNextWrite.current) { skipNextWrite.current = false; return; }
|
| 154 |
+
const t = setTimeout(() => {
|
| 155 |
+
window.omelette?.writeFile(DC_STATE_FILE, JSON.stringify({ sections: state.sections })).catch(() => {});
|
| 156 |
+
}, 250);
|
| 157 |
+
return () => clearTimeout(t);
|
| 158 |
+
}, [state.sections]);
|
| 159 |
+
|
| 160 |
+
// Build registries synchronously from children so FocusOverlay can read
|
| 161 |
+
// them in the same render. Only direct DCSection > DCArtboard children are
|
| 162 |
+
// walked — wrapping them in other elements opts out of focus/reorder.
|
| 163 |
+
const registry = {}; // slotId -> { sectionId, artboard }
|
| 164 |
+
const sectionMeta = {}; // sectionId -> { title, subtitle, slotIds[] }
|
| 165 |
+
const sectionOrder = [];
|
| 166 |
+
React.Children.forEach(children, (sec) => {
|
| 167 |
+
if (!sec || sec.type !== DCSection) return;
|
| 168 |
+
const sid = sec.props.id ?? sec.props.title;
|
| 169 |
+
if (!sid) return;
|
| 170 |
+
sectionOrder.push(sid);
|
| 171 |
+
const persisted = state.sections[sid] || {};
|
| 172 |
+
const abs = [];
|
| 173 |
+
React.Children.forEach(sec.props.children, (ab) => {
|
| 174 |
+
if (!ab || ab.type !== DCArtboard) return;
|
| 175 |
+
const aid = ab.props.id ?? ab.props.label;
|
| 176 |
+
if (aid) abs.push([aid, ab]);
|
| 177 |
+
});
|
| 178 |
+
// hidden is scoped to one source revision — when the agent regenerates
|
| 179 |
+
// (artboard-ID set changes), prior deletes don't apply to new content.
|
| 180 |
+
const srcKey = abs.map(([k]) => k).join('\x1f');
|
| 181 |
+
const hidden = persisted.srcKey === srcKey ? (persisted.hidden || []) : [];
|
| 182 |
+
const srcIds = [];
|
| 183 |
+
abs.forEach(([aid, ab]) => {
|
| 184 |
+
if (hidden.includes(aid)) return;
|
| 185 |
+
registry[`${sid}/${aid}`] = { sectionId: sid, artboard: ab };
|
| 186 |
+
srcIds.push(aid);
|
| 187 |
+
});
|
| 188 |
+
const kept = (persisted.order || []).filter((k) => srcIds.includes(k));
|
| 189 |
+
sectionMeta[sid] = {
|
| 190 |
+
title: persisted.title ?? sec.props.title,
|
| 191 |
+
subtitle: sec.props.subtitle,
|
| 192 |
+
slotIds: [...kept, ...srcIds.filter((k) => !kept.includes(k))],
|
| 193 |
+
};
|
| 194 |
+
});
|
| 195 |
+
|
| 196 |
+
const api = React.useMemo(() => ({
|
| 197 |
+
state,
|
| 198 |
+
section: (id) => state.sections[id] || {},
|
| 199 |
+
patchSection: (id, p) => setState((s) => ({
|
| 200 |
+
...s,
|
| 201 |
+
sections: { ...s.sections, [id]: { ...s.sections[id], ...(typeof p === 'function' ? p(s.sections[id] || {}) : p) } },
|
| 202 |
+
})),
|
| 203 |
+
setFocus: (slotId) => setState((s) => ({ ...s, focus: slotId })),
|
| 204 |
+
}), [state]);
|
| 205 |
+
|
| 206 |
+
// Esc exits focus; any outside pointerdown commits an in-progress rename.
|
| 207 |
+
React.useEffect(() => {
|
| 208 |
+
const onKey = (e) => { if (e.key === 'Escape') api.setFocus(null); };
|
| 209 |
+
const onPd = (e) => {
|
| 210 |
+
const ae = document.activeElement;
|
| 211 |
+
if (ae && ae.isContentEditable && !ae.contains(e.target)) ae.blur();
|
| 212 |
+
};
|
| 213 |
+
document.addEventListener('keydown', onKey);
|
| 214 |
+
document.addEventListener('pointerdown', onPd, true);
|
| 215 |
+
return () => {
|
| 216 |
+
document.removeEventListener('keydown', onKey);
|
| 217 |
+
document.removeEventListener('pointerdown', onPd, true);
|
| 218 |
+
};
|
| 219 |
+
}, [api]);
|
| 220 |
+
|
| 221 |
+
return (
|
| 222 |
+
<DCCtx.Provider value={api}>
|
| 223 |
+
<DCViewport minScale={minScale} maxScale={maxScale} style={style}>{ready && children}</DCViewport>
|
| 224 |
+
{state.focus && registry[state.focus] && (
|
| 225 |
+
<DCFocusOverlay entry={registry[state.focus]} sectionMeta={sectionMeta} sectionOrder={sectionOrder} />
|
| 226 |
+
)}
|
| 227 |
+
</DCCtx.Provider>
|
| 228 |
+
);
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
// ─────────────────────────────────────────────────────────────
|
| 232 |
+
// DCViewport — transform-based pan/zoom (internal)
|
| 233 |
+
//
|
| 234 |
+
// Input mapping (Figma-style):
|
| 235 |
+
// • trackpad pinch → zoom (ctrlKey wheel; Safari gesture* events)
|
| 236 |
+
// • trackpad scroll → pan (two-finger)
|
| 237 |
+
// • mouse wheel → zoom (notched; distinguished from trackpad scroll)
|
| 238 |
+
// • middle-drag / primary-drag-on-bg → pan
|
| 239 |
+
//
|
| 240 |
+
// Transform state lives in a ref and is written straight to the DOM
|
| 241 |
+
// (translate3d + will-change) so wheel ticks don't go through React —
|
| 242 |
+
// keeps pans at 60fps on dense canvases.
|
| 243 |
+
// ─────────────────────────────────────────────────────────────
|
| 244 |
+
function DCViewport({ children, minScale = 0.1, maxScale = 8, style = {} }) {
|
| 245 |
+
const vpRef = React.useRef(null);
|
| 246 |
+
const worldRef = React.useRef(null);
|
| 247 |
+
const tf = React.useRef({ x: 0, y: 0, scale: 1 });
|
| 248 |
+
// Persist viewport across reloads so the user lands back where they were
|
| 249 |
+
// after an agent edit or browser refresh. The sandbox origin is already
|
| 250 |
+
// per-project; pathname keeps multiple canvas files in one project apart.
|
| 251 |
+
const tfKey = 'dc-viewport:' + location.pathname;
|
| 252 |
+
const saveT = React.useRef(0);
|
| 253 |
+
|
| 254 |
+
const lastPostedScale = React.useRef();
|
| 255 |
+
const apply = React.useCallback(() => {
|
| 256 |
+
const { x, y, scale } = tf.current;
|
| 257 |
+
const el = worldRef.current;
|
| 258 |
+
if (!el) return;
|
| 259 |
+
el.style.transform = `translate3d(${x}px, ${y}px, 0) scale(${scale})`;
|
| 260 |
+
// Exposed for zoom-invariant chrome (labels, buttons, TweaksPanel).
|
| 261 |
+
el.style.setProperty('--dc-inv-zoom', String(1 / scale));
|
| 262 |
+
// Keep the host toolbar's % readout in sync with the canvas scale. Pan
|
| 263 |
+
// ticks leave scale unchanged — skip the cross-frame post for those.
|
| 264 |
+
if (lastPostedScale.current !== scale) {
|
| 265 |
+
lastPostedScale.current = scale;
|
| 266 |
+
window.parent.postMessage({ type: '__dc_zoom', scale }, '*');
|
| 267 |
+
}
|
| 268 |
+
clearTimeout(saveT.current);
|
| 269 |
+
saveT.current = setTimeout(() => {
|
| 270 |
+
try { localStorage.setItem(tfKey, JSON.stringify(tf.current)); } catch {}
|
| 271 |
+
}, 200);
|
| 272 |
+
}, [tfKey]);
|
| 273 |
+
|
| 274 |
+
React.useLayoutEffect(() => {
|
| 275 |
+
const flush = () => {
|
| 276 |
+
clearTimeout(saveT.current);
|
| 277 |
+
try { localStorage.setItem(tfKey, JSON.stringify(tf.current)); } catch {}
|
| 278 |
+
};
|
| 279 |
+
try {
|
| 280 |
+
const s = JSON.parse(localStorage.getItem(tfKey) || 'null');
|
| 281 |
+
if (s && Number.isFinite(s.x) && Number.isFinite(s.y) && Number.isFinite(s.scale)) {
|
| 282 |
+
tf.current = { x: s.x, y: s.y, scale: Math.min(maxScale, Math.max(minScale, s.scale)) };
|
| 283 |
+
apply();
|
| 284 |
+
}
|
| 285 |
+
} catch {}
|
| 286 |
+
// Flush on pagehide and unmount so a reload within the 200ms debounce
|
| 287 |
+
// window doesn't drop the last pan/zoom.
|
| 288 |
+
window.addEventListener('pagehide', flush);
|
| 289 |
+
return () => { window.removeEventListener('pagehide', flush); flush(); };
|
| 290 |
+
}, []);
|
| 291 |
+
|
| 292 |
+
React.useEffect(() => {
|
| 293 |
+
const vp = vpRef.current;
|
| 294 |
+
if (!vp) return;
|
| 295 |
+
|
| 296 |
+
const zoomAt = (cx, cy, factor) => {
|
| 297 |
+
const r = vp.getBoundingClientRect();
|
| 298 |
+
const px = cx - r.left, py = cy - r.top;
|
| 299 |
+
const t = tf.current;
|
| 300 |
+
const next = Math.min(maxScale, Math.max(minScale, t.scale * factor));
|
| 301 |
+
const k = next / t.scale;
|
| 302 |
+
// keep the world point under the cursor fixed
|
| 303 |
+
t.x = px - (px - t.x) * k;
|
| 304 |
+
t.y = py - (py - t.y) * k;
|
| 305 |
+
t.scale = next;
|
| 306 |
+
apply();
|
| 307 |
+
};
|
| 308 |
+
|
| 309 |
+
// Mouse-wheel vs trackpad-scroll heuristic. A physical wheel sends
|
| 310 |
+
// line-mode deltas (Firefox) or large integer pixel deltas with no X
|
| 311 |
+
// component (Chrome/Safari, typically multiples of 100/120). Trackpad
|
| 312 |
+
// two-finger scroll sends small/fractional pixel deltas, often with
|
| 313 |
+
// non-zero deltaX. ctrlKey is set by the browser for trackpad pinch.
|
| 314 |
+
const isMouseWheel = (e) =>
|
| 315 |
+
e.deltaMode !== 0 ||
|
| 316 |
+
(e.deltaX === 0 && Number.isInteger(e.deltaY) && Math.abs(e.deltaY) >= 40);
|
| 317 |
+
|
| 318 |
+
const onWheel = (e) => {
|
| 319 |
+
e.preventDefault();
|
| 320 |
+
if (isGesturing) return; // Safari: gesture* owns the pinch — discard concurrent wheels
|
| 321 |
+
if ((e.ctrlKey || e.metaKey) && !isMouseWheel(e)) {
|
| 322 |
+
// trackpad pinch, or ctrl/cmd + smooth-scroll mouse. Notched
|
| 323 |
+
// wheels fall through to the fixed-step branch below.
|
| 324 |
+
zoomAt(e.clientX, e.clientY, Math.exp(-e.deltaY * 0.01));
|
| 325 |
+
} else if (isMouseWheel(e)) {
|
| 326 |
+
// notched mouse wheel — fixed-ratio step per click
|
| 327 |
+
zoomAt(e.clientX, e.clientY, Math.exp(-Math.sign(e.deltaY) * 0.18));
|
| 328 |
+
} else {
|
| 329 |
+
// trackpad two-finger scroll — pan
|
| 330 |
+
tf.current.x -= e.deltaX;
|
| 331 |
+
tf.current.y -= e.deltaY;
|
| 332 |
+
apply();
|
| 333 |
+
}
|
| 334 |
+
};
|
| 335 |
+
|
| 336 |
+
// Safari sends native gesture* events for trackpad pinch with a smooth
|
| 337 |
+
// e.scale; preferring these over the ctrl+wheel fallback gives a much
|
| 338 |
+
// better feel there. No-ops on other browsers. Safari also fires
|
| 339 |
+
// ctrlKey wheel events during the same pinch — isGesturing makes
|
| 340 |
+
// onWheel drop those entirely so they neither zoom nor pan.
|
| 341 |
+
let gsBase = 1;
|
| 342 |
+
let isGesturing = false;
|
| 343 |
+
const onGestureStart = (e) => { e.preventDefault(); isGesturing = true; gsBase = tf.current.scale; };
|
| 344 |
+
const onGestureChange = (e) => {
|
| 345 |
+
e.preventDefault();
|
| 346 |
+
zoomAt(e.clientX, e.clientY, (gsBase * e.scale) / tf.current.scale);
|
| 347 |
+
};
|
| 348 |
+
const onGestureEnd = (e) => { e.preventDefault(); isGesturing = false; };
|
| 349 |
+
|
| 350 |
+
// Drag-pan: middle button anywhere, or primary button on canvas
|
| 351 |
+
// background (anything that isn't an artboard or an inline editor).
|
| 352 |
+
let drag = null;
|
| 353 |
+
const onPointerDown = (e) => {
|
| 354 |
+
const onBg = !e.target.closest('[data-dc-slot], .dc-editable');
|
| 355 |
+
if (!(e.button === 1 || (e.button === 0 && onBg))) return;
|
| 356 |
+
e.preventDefault();
|
| 357 |
+
vp.setPointerCapture(e.pointerId);
|
| 358 |
+
drag = { id: e.pointerId, lx: e.clientX, ly: e.clientY };
|
| 359 |
+
vp.style.cursor = 'grabbing';
|
| 360 |
+
};
|
| 361 |
+
const onPointerMove = (e) => {
|
| 362 |
+
if (!drag || e.pointerId !== drag.id) return;
|
| 363 |
+
tf.current.x += e.clientX - drag.lx;
|
| 364 |
+
tf.current.y += e.clientY - drag.ly;
|
| 365 |
+
drag.lx = e.clientX; drag.ly = e.clientY;
|
| 366 |
+
apply();
|
| 367 |
+
};
|
| 368 |
+
const onPointerUp = (e) => {
|
| 369 |
+
if (!drag || e.pointerId !== drag.id) return;
|
| 370 |
+
vp.releasePointerCapture(e.pointerId);
|
| 371 |
+
drag = null;
|
| 372 |
+
vp.style.cursor = '';
|
| 373 |
+
};
|
| 374 |
+
|
| 375 |
+
// Host-driven zoom (toolbar % menu). Zooms around viewport centre so the
|
| 376 |
+
// visible midpoint stays fixed — matching the host's iframe-zoom feel.
|
| 377 |
+
const onHostMsg = (e) => {
|
| 378 |
+
const d = e.data;
|
| 379 |
+
if (d && d.type === '__dc_set_zoom' && typeof d.scale === 'number') {
|
| 380 |
+
const r = vp.getBoundingClientRect();
|
| 381 |
+
zoomAt(r.left + r.width / 2, r.top + r.height / 2, d.scale / tf.current.scale);
|
| 382 |
+
} else if (d && d.type === '__dc_probe') {
|
| 383 |
+
// Host's [readyGen] reset asks whether a canvas is present; it
|
| 384 |
+
// fires on the iframe's native 'load', which for canvases with
|
| 385 |
+
// images/fonts is after our mount-time announce, so re-announce.
|
| 386 |
+
// Clear the pan-tick guard so apply() re-posts the current scale
|
| 387 |
+
// even if it's unchanged — the host just reset dcScale to 1.
|
| 388 |
+
window.parent.postMessage({ type: '__dc_present' }, '*');
|
| 389 |
+
lastPostedScale.current = undefined;
|
| 390 |
+
apply();
|
| 391 |
+
}
|
| 392 |
+
};
|
| 393 |
+
window.addEventListener('message', onHostMsg);
|
| 394 |
+
// Announce canvas mode so the host toolbar proxies its % control here
|
| 395 |
+
// instead of scaling the iframe element (which would just shrink the
|
| 396 |
+
// viewport window of an infinite canvas). The apply() that follows emits
|
| 397 |
+
// the initial __dc_zoom so the toolbar % is correct before first pinch.
|
| 398 |
+
// lastPostedScale reset mirrors the __dc_probe handler: the layout
|
| 399 |
+
// effect's restore-path apply() may already have posted the restored
|
| 400 |
+
// scale (before __dc_present), so clear the guard to re-post it in order.
|
| 401 |
+
window.parent.postMessage({ type: '__dc_present' }, '*');
|
| 402 |
+
lastPostedScale.current = undefined;
|
| 403 |
+
apply();
|
| 404 |
+
|
| 405 |
+
vp.addEventListener('wheel', onWheel, { passive: false });
|
| 406 |
+
vp.addEventListener('gesturestart', onGestureStart, { passive: false });
|
| 407 |
+
vp.addEventListener('gesturechange', onGestureChange, { passive: false });
|
| 408 |
+
vp.addEventListener('gestureend', onGestureEnd, { passive: false });
|
| 409 |
+
vp.addEventListener('pointerdown', onPointerDown);
|
| 410 |
+
vp.addEventListener('pointermove', onPointerMove);
|
| 411 |
+
vp.addEventListener('pointerup', onPointerUp);
|
| 412 |
+
vp.addEventListener('pointercancel', onPointerUp);
|
| 413 |
+
return () => {
|
| 414 |
+
window.removeEventListener('message', onHostMsg);
|
| 415 |
+
vp.removeEventListener('wheel', onWheel);
|
| 416 |
+
vp.removeEventListener('gesturestart', onGestureStart);
|
| 417 |
+
vp.removeEventListener('gesturechange', onGestureChange);
|
| 418 |
+
vp.removeEventListener('gestureend', onGestureEnd);
|
| 419 |
+
vp.removeEventListener('pointerdown', onPointerDown);
|
| 420 |
+
vp.removeEventListener('pointermove', onPointerMove);
|
| 421 |
+
vp.removeEventListener('pointerup', onPointerUp);
|
| 422 |
+
vp.removeEventListener('pointercancel', onPointerUp);
|
| 423 |
+
};
|
| 424 |
+
}, [apply, minScale, maxScale]);
|
| 425 |
+
|
| 426 |
+
const gridSvg = `url("data:image/svg+xml,%3Csvg width='120' height='120' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M120 0H0v120' fill='none' stroke='${encodeURIComponent(DC.grid)}' stroke-width='1'/%3E%3C/svg%3E")`;
|
| 427 |
+
return (
|
| 428 |
+
<div
|
| 429 |
+
ref={vpRef}
|
| 430 |
+
className="design-canvas"
|
| 431 |
+
style={{
|
| 432 |
+
height: '100vh', width: '100vw',
|
| 433 |
+
background: DC.bg,
|
| 434 |
+
overflow: 'hidden',
|
| 435 |
+
overscrollBehavior: 'none',
|
| 436 |
+
touchAction: 'none',
|
| 437 |
+
position: 'relative',
|
| 438 |
+
fontFamily: DC.font,
|
| 439 |
+
boxSizing: 'border-box',
|
| 440 |
+
...style,
|
| 441 |
+
}}
|
| 442 |
+
>
|
| 443 |
+
<div
|
| 444 |
+
ref={worldRef}
|
| 445 |
+
style={{
|
| 446 |
+
position: 'absolute', top: 0, left: 0,
|
| 447 |
+
transformOrigin: '0 0',
|
| 448 |
+
willChange: 'transform',
|
| 449 |
+
width: 'max-content', minWidth: '100%',
|
| 450 |
+
minHeight: '100%',
|
| 451 |
+
padding: '60px 0 80px',
|
| 452 |
+
}}
|
| 453 |
+
>
|
| 454 |
+
<div style={{ position: 'absolute', inset: -6000, backgroundImage: gridSvg, backgroundSize: '120px 120px', pointerEvents: 'none', zIndex: -1 }} />
|
| 455 |
+
{children}
|
| 456 |
+
</div>
|
| 457 |
+
</div>
|
| 458 |
+
);
|
| 459 |
+
}
|
| 460 |
+
|
| 461 |
+
// ─────────────────────────────────────────────────────────────
|
| 462 |
+
// DCSection — editable title + h-row of artboards in persisted order
|
| 463 |
+
// ─────────────────────────────────────────────────────────────
|
| 464 |
+
function DCSection({ id, title, subtitle, children, gap = 48 }) {
|
| 465 |
+
const ctx = React.useContext(DCCtx);
|
| 466 |
+
const sid = id ?? title;
|
| 467 |
+
const all = React.Children.toArray(children);
|
| 468 |
+
const artboards = all.filter((c) => c && c.type === DCArtboard);
|
| 469 |
+
const rest = all.filter((c) => !(c && c.type === DCArtboard));
|
| 470 |
+
const sec = (ctx && sid && ctx.section(sid)) || {};
|
| 471 |
+
// Must match DesignCanvas's srcKey computation exactly (it filters falsy
|
| 472 |
+
// IDs), or onDelete persists a srcKey that DesignCanvas never recognizes.
|
| 473 |
+
const allIds = artboards.map((a) => a.props.id ?? a.props.label).filter(Boolean);
|
| 474 |
+
const srcKey = allIds.join('\x1f');
|
| 475 |
+
const hidden = sec.srcKey === srcKey ? (sec.hidden || []) : [];
|
| 476 |
+
const srcOrder = allIds.filter((k) => !hidden.includes(k));
|
| 477 |
+
|
| 478 |
+
const order = React.useMemo(() => {
|
| 479 |
+
const kept = (sec.order || []).filter((k) => srcOrder.includes(k));
|
| 480 |
+
return [...kept, ...srcOrder.filter((k) => !kept.includes(k))];
|
| 481 |
+
}, [sec.order, srcOrder.join('|')]);
|
| 482 |
+
|
| 483 |
+
const byId = Object.fromEntries(artboards.map((a) => [a.props.id ?? a.props.label, a]));
|
| 484 |
+
|
| 485 |
+
// marginBottom counter-scales so the on-screen gap between sections stays
|
| 486 |
+
// constant — otherwise at low zoom the (world-space) gap collapses while
|
| 487 |
+
// the screen-constant sectionhead below it doesn't, and the title reads as
|
| 488 |
+
// belonging to the section above. paddingBottom below is just enough for
|
| 489 |
+
// the 24px artboard-header (abs-positioned above each card) plus ~8px, so
|
| 490 |
+
// the title sits tight against its own row at every zoom.
|
| 491 |
+
return (
|
| 492 |
+
<div data-dc-section={sid}
|
| 493 |
+
style={{ marginBottom: 'calc(80px * var(--dc-inv-zoom, 1))', position: 'relative' }}>
|
| 494 |
+
<div style={{ padding: '0 60px' }}>
|
| 495 |
+
<div className="dc-sectionhead" style={{ paddingBottom: 36 }}>
|
| 496 |
+
<DCEditable tag="div" value={sec.title ?? title}
|
| 497 |
+
onChange={(v) => ctx && sid && ctx.patchSection(sid, { title: v })}
|
| 498 |
+
style={{ fontSize: 28, fontWeight: 600, color: DC.title, letterSpacing: -0.4, marginBottom: 6, display: 'inline-block' }} />
|
| 499 |
+
{subtitle && <div style={{ fontSize: 16, color: DC.subtitle }}>{subtitle}</div>}
|
| 500 |
+
</div>
|
| 501 |
+
</div>
|
| 502 |
+
<div style={{ display: 'flex', gap, padding: '0 60px', alignItems: 'flex-start', width: 'max-content' }}>
|
| 503 |
+
{order.map((k) => (
|
| 504 |
+
<DCArtboardFrame key={k} sectionId={sid} artboard={byId[k]} order={order}
|
| 505 |
+
label={(sec.labels || {})[k] ?? byId[k].props.label}
|
| 506 |
+
onRename={(v) => ctx && ctx.patchSection(sid, (x) => ({ labels: { ...x.labels, [k]: v } }))}
|
| 507 |
+
onReorder={(next) => ctx && ctx.patchSection(sid, { order: next })}
|
| 508 |
+
onDelete={() => ctx && ctx.patchSection(sid, (x) => ({
|
| 509 |
+
hidden: [...(x.srcKey === srcKey ? (x.hidden || []) : []), k],
|
| 510 |
+
srcKey,
|
| 511 |
+
}))}
|
| 512 |
+
onFocus={() => ctx && ctx.setFocus(`${sid}/${k}`)} />
|
| 513 |
+
))}
|
| 514 |
+
</div>
|
| 515 |
+
{rest}
|
| 516 |
+
</div>
|
| 517 |
+
);
|
| 518 |
+
}
|
| 519 |
+
|
| 520 |
+
// DCArtboard — marker; rendered by DCArtboardFrame via DCSection.
|
| 521 |
+
function DCArtboard() { return null; }
|
| 522 |
+
|
| 523 |
+
// Per-artboard export (kind: 'png' | 'html'). Both paths share the same
|
| 524 |
+
// self-contained clone: computed styles baked in, @font-face / <img> /
|
| 525 |
+
// inline-style background-image urls inlined as data URIs. PNG wraps the
|
| 526 |
+
// clone in foreignObject→canvas at 3× the artboard's natural width×height
|
| 527 |
+
// (same pipeline the host uses for page captures); HTML wraps it in a
|
| 528 |
+
// minimal standalone document. Both are independent of viewport zoom.
|
| 529 |
+
async function dcExport(node, w, h, name, kind) {
|
| 530 |
+
try { await document.fonts.ready; } catch {}
|
| 531 |
+
const toDataURL = (url) => fetch(url).then((r) => r.blob()).then((b) => new Promise((res) => {
|
| 532 |
+
const fr = new FileReader(); fr.onload = () => res(fr.result); fr.onerror = () => res(url); fr.readAsDataURL(b);
|
| 533 |
+
})).catch(() => url);
|
| 534 |
+
|
| 535 |
+
// Collect @font-face rules. ss.cssRules throws SecurityError on
|
| 536 |
+
// cross-origin sheets (e.g. fonts.googleapis.com) — in that case fetch
|
| 537 |
+
// the CSS text directly (those endpoints send ACAO:*) and regex-extract
|
| 538 |
+
// the blocks. @import and @media/@supports are walked so nested
|
| 539 |
+
// @font-face rules aren't missed.
|
| 540 |
+
const fontRules = [], pending = [], seen = new Set();
|
| 541 |
+
const scrapeCss = (href) => {
|
| 542 |
+
if (seen.has(href)) return; seen.add(href);
|
| 543 |
+
pending.push(fetch(href).then((r) => r.text()).then((css) => {
|
| 544 |
+
for (const m of css.match(/@font-face\s*{[^}]*}/g) || []) fontRules.push({ css: m, base: href });
|
| 545 |
+
for (const m of css.matchAll(/@import\s+(?:url\()?['"]?([^'")\s;]+)/g))
|
| 546 |
+
scrapeCss(new URL(m[1], href).href);
|
| 547 |
+
}).catch(() => {}));
|
| 548 |
+
};
|
| 549 |
+
const walk = (rules, base) => {
|
| 550 |
+
for (const r of rules) {
|
| 551 |
+
if (r.type === CSSRule.FONT_FACE_RULE) fontRules.push({ css: r.cssText, base });
|
| 552 |
+
else if (r.type === CSSRule.IMPORT_RULE && r.styleSheet) {
|
| 553 |
+
const ibase = r.styleSheet.href || base;
|
| 554 |
+
try { walk(r.styleSheet.cssRules, ibase); } catch { scrapeCss(ibase); }
|
| 555 |
+
} else if (r.cssRules) walk(r.cssRules, base);
|
| 556 |
+
}
|
| 557 |
+
};
|
| 558 |
+
for (const ss of document.styleSheets) {
|
| 559 |
+
const base = ss.href || location.href;
|
| 560 |
+
try { walk(ss.cssRules, base); } catch { if (ss.href) scrapeCss(ss.href); }
|
| 561 |
+
}
|
| 562 |
+
while (pending.length) await pending.shift();
|
| 563 |
+
const fontCss = (await Promise.all(fontRules.map(async (rule) => {
|
| 564 |
+
let out = rule.css, m; const re = /url\((['"]?)([^'")]+)\1\)/g;
|
| 565 |
+
while ((m = re.exec(rule.css))) {
|
| 566 |
+
if (m[2].indexOf('data:') === 0) continue;
|
| 567 |
+
let abs; try { abs = new URL(m[2], rule.base).href; } catch { continue; }
|
| 568 |
+
out = out.split(m[0]).join('url("' + await toDataURL(abs) + '")');
|
| 569 |
+
}
|
| 570 |
+
return out;
|
| 571 |
+
}))).join('\n');
|
| 572 |
+
|
| 573 |
+
const cloneStyled = (src) => {
|
| 574 |
+
if (src.nodeType === 8 || (src.nodeType === 1 && src.tagName === 'SCRIPT')) return document.createTextNode('');
|
| 575 |
+
const dst = src.cloneNode(false);
|
| 576 |
+
if (src.nodeType === 1) {
|
| 577 |
+
const cs = getComputedStyle(src); let txt = '';
|
| 578 |
+
for (let i = 0; i < cs.length; i++) txt += cs[i] + ':' + cs.getPropertyValue(cs[i]) + ';';
|
| 579 |
+
dst.setAttribute('style', txt + 'animation:none;transition:none;');
|
| 580 |
+
if (src.tagName === 'CANVAS') try { const im = document.createElement('img'); im.src = src.toDataURL(); im.setAttribute('style', txt); return im; } catch {}
|
| 581 |
+
}
|
| 582 |
+
for (let c = src.firstChild; c; c = c.nextSibling) dst.appendChild(cloneStyled(c));
|
| 583 |
+
return dst;
|
| 584 |
+
};
|
| 585 |
+
const clone = cloneStyled(node);
|
| 586 |
+
clone.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
|
| 587 |
+
// Drop the card's own shadow/radius so the export is a flush w×h rect;
|
| 588 |
+
// the artboard's own background (if any) is already in the computed style.
|
| 589 |
+
clone.style.boxShadow = 'none'; clone.style.borderRadius = '0';
|
| 590 |
+
|
| 591 |
+
const jobs = [];
|
| 592 |
+
clone.querySelectorAll('img').forEach((el) => {
|
| 593 |
+
const s = el.getAttribute('src');
|
| 594 |
+
if (s && s.indexOf('data:') !== 0) jobs.push(toDataURL(el.src).then((d) => el.setAttribute('src', d)));
|
| 595 |
+
});
|
| 596 |
+
[clone, ...clone.querySelectorAll('*')].forEach((el) => {
|
| 597 |
+
const bg = el.style.backgroundImage; if (!bg) return;
|
| 598 |
+
let m; const re = /url\(["']?([^"')]+)["']?\)/g;
|
| 599 |
+
while ((m = re.exec(bg))) {
|
| 600 |
+
const tok = m[0], url = m[1];
|
| 601 |
+
if (url.indexOf('data:') === 0) continue;
|
| 602 |
+
jobs.push(toDataURL(url).then((d) => { el.style.backgroundImage = el.style.backgroundImage.split(tok).join('url("' + d + '")'); }));
|
| 603 |
+
}
|
| 604 |
+
});
|
| 605 |
+
await Promise.all(jobs);
|
| 606 |
+
|
| 607 |
+
const xml = new XMLSerializer().serializeToString(clone);
|
| 608 |
+
const save = (blob, ext) => {
|
| 609 |
+
if (!blob) return;
|
| 610 |
+
const a = document.createElement('a');
|
| 611 |
+
a.href = URL.createObjectURL(blob); a.download = name + '.' + ext; a.click();
|
| 612 |
+
setTimeout(() => URL.revokeObjectURL(a.href), 1000);
|
| 613 |
+
};
|
| 614 |
+
|
| 615 |
+
if (kind === 'html') {
|
| 616 |
+
const html = '<!doctype html><html><head><meta charset="utf-8"><title>' + name + '</title>' +
|
| 617 |
+
(fontCss ? '<style>' + fontCss + '</style>' : '') +
|
| 618 |
+
'</head><body style="margin:0">' + xml + '</body></html>';
|
| 619 |
+
return save(new Blob([html], { type: 'text/html' }), 'html');
|
| 620 |
+
}
|
| 621 |
+
|
| 622 |
+
// PNG: the SVG's own width/height must be the output resolution — an
|
| 623 |
+
// <img>-loaded SVG rasterizes at its intrinsic size, so sizing it at 1×
|
| 624 |
+
// and ctx.scale()-ing up would just upscale a 1× bitmap. viewBox maps the
|
| 625 |
+
// w×h foreignObject onto the px·w × px·h SVG canvas so the browser renders
|
| 626 |
+
// the HTML at full resolution.
|
| 627 |
+
const px = 3;
|
| 628 |
+
const svg = '<svg xmlns="http://www.w3.org/2000/svg" width="' + w * px + '" height="' + h * px +
|
| 629 |
+
'" viewBox="0 0 ' + w + ' ' + h + '"><foreignObject width="' + w + '" height="' + h + '">' +
|
| 630 |
+
(fontCss ? '<style><![CDATA[' + fontCss + ']]></style>' : '') + xml + '</foreignObject></svg>';
|
| 631 |
+
const img = new Image();
|
| 632 |
+
await new Promise((res, rej) => {
|
| 633 |
+
img.onload = res; img.onerror = () => rej(new Error('svg load failed'));
|
| 634 |
+
img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
|
| 635 |
+
});
|
| 636 |
+
const cv = document.createElement('canvas');
|
| 637 |
+
cv.width = w * px; cv.height = h * px;
|
| 638 |
+
cv.getContext('2d').drawImage(img, 0, 0);
|
| 639 |
+
cv.toBlob((blob) => save(blob, 'png'), 'image/png');
|
| 640 |
+
}
|
| 641 |
+
|
| 642 |
+
function DCArtboardFrame({ sectionId, artboard, label, order, onRename, onReorder, onFocus, onDelete }) {
|
| 643 |
+
const { id: rawId, label: rawLabel, width = 260, height = 480, children, style = {} } = artboard.props;
|
| 644 |
+
const id = rawId ?? rawLabel;
|
| 645 |
+
const ref = React.useRef(null);
|
| 646 |
+
const cardRef = React.useRef(null);
|
| 647 |
+
const menuRef = React.useRef(null);
|
| 648 |
+
const [menuOpen, setMenuOpen] = React.useState(false);
|
| 649 |
+
const [confirming, setConfirming] = React.useState(false);
|
| 650 |
+
|
| 651 |
+
// ⋯ menu: close on any outside pointerdown. Two-click delete lives inside
|
| 652 |
+
// the menu — first click arms the row, second commits; closing disarms.
|
| 653 |
+
React.useEffect(() => {
|
| 654 |
+
if (!menuOpen) { setConfirming(false); return; }
|
| 655 |
+
const off = (e) => { if (!menuRef.current || !menuRef.current.contains(e.target)) setMenuOpen(false); };
|
| 656 |
+
document.addEventListener('pointerdown', off, true);
|
| 657 |
+
return () => document.removeEventListener('pointerdown', off, true);
|
| 658 |
+
}, [menuOpen]);
|
| 659 |
+
|
| 660 |
+
const doExport = (kind) => {
|
| 661 |
+
setMenuOpen(false);
|
| 662 |
+
if (!cardRef.current) return;
|
| 663 |
+
const name = String(label || id || 'artboard').replace(/[^\w\s.-]+/g, '_');
|
| 664 |
+
dcExport(cardRef.current, width, height, name, kind)
|
| 665 |
+
.catch((e) => console.error('[design-canvas] export failed:', e));
|
| 666 |
+
};
|
| 667 |
+
|
| 668 |
+
// Live drag-reorder: dragged card sticks to cursor; siblings slide into
|
| 669 |
+
// their would-be slots in real time via transforms. DOM order only
|
| 670 |
+
// changes on drop.
|
| 671 |
+
const onGripDown = (e) => {
|
| 672 |
+
e.preventDefault(); e.stopPropagation();
|
| 673 |
+
const me = ref.current;
|
| 674 |
+
// translateX is applied in local (pre-scale) space but pointer deltas and
|
| 675 |
+
// getBoundingClientRect().left are screen-space — divide by the viewport's
|
| 676 |
+
// current scale so the dragged card tracks the cursor at any zoom level.
|
| 677 |
+
const scale = me.getBoundingClientRect().width / me.offsetWidth || 1;
|
| 678 |
+
const peers = Array.from(document.querySelectorAll(`[data-dc-section="${sectionId}"] [data-dc-slot]`));
|
| 679 |
+
const homes = peers.map((el) => ({ el, id: el.dataset.dcSlot, x: el.getBoundingClientRect().left }));
|
| 680 |
+
const slotXs = homes.map((h) => h.x);
|
| 681 |
+
const startIdx = order.indexOf(id);
|
| 682 |
+
const startX = e.clientX;
|
| 683 |
+
let liveOrder = order.slice();
|
| 684 |
+
me.classList.add('dc-dragging');
|
| 685 |
+
|
| 686 |
+
const layout = () => {
|
| 687 |
+
for (const h of homes) {
|
| 688 |
+
if (h.id === id) continue;
|
| 689 |
+
const slot = liveOrder.indexOf(h.id);
|
| 690 |
+
h.el.style.transform = `translateX(${(slotXs[slot] - h.x) / scale}px)`;
|
| 691 |
+
}
|
| 692 |
+
};
|
| 693 |
+
|
| 694 |
+
const move = (ev) => {
|
| 695 |
+
const dx = ev.clientX - startX;
|
| 696 |
+
me.style.transform = `translateX(${dx / scale}px)`;
|
| 697 |
+
const cur = homes[startIdx].x + dx;
|
| 698 |
+
let nearest = 0, best = Infinity;
|
| 699 |
+
for (let i = 0; i < slotXs.length; i++) {
|
| 700 |
+
const d = Math.abs(slotXs[i] - cur);
|
| 701 |
+
if (d < best) { best = d; nearest = i; }
|
| 702 |
+
}
|
| 703 |
+
if (liveOrder.indexOf(id) !== nearest) {
|
| 704 |
+
liveOrder = order.filter((k) => k !== id);
|
| 705 |
+
liveOrder.splice(nearest, 0, id);
|
| 706 |
+
layout();
|
| 707 |
+
}
|
| 708 |
+
};
|
| 709 |
+
|
| 710 |
+
const up = () => {
|
| 711 |
+
document.removeEventListener('pointermove', move);
|
| 712 |
+
document.removeEventListener('pointerup', up);
|
| 713 |
+
const finalSlot = liveOrder.indexOf(id);
|
| 714 |
+
me.classList.remove('dc-dragging');
|
| 715 |
+
me.style.transform = `translateX(${(slotXs[finalSlot] - homes[startIdx].x) / scale}px)`;
|
| 716 |
+
// After the settle transition, kill transitions + clear transforms +
|
| 717 |
+
// commit the reorder in the same frame so there's no visual snap-back.
|
| 718 |
+
setTimeout(() => {
|
| 719 |
+
for (const h of homes) { h.el.style.transition = 'none'; h.el.style.transform = ''; }
|
| 720 |
+
if (liveOrder.join('|') !== order.join('|')) onReorder(liveOrder);
|
| 721 |
+
requestAnimationFrame(() => requestAnimationFrame(() => {
|
| 722 |
+
for (const h of homes) h.el.style.transition = '';
|
| 723 |
+
}));
|
| 724 |
+
}, 180);
|
| 725 |
+
};
|
| 726 |
+
document.addEventListener('pointermove', move);
|
| 727 |
+
document.addEventListener('pointerup', up);
|
| 728 |
+
};
|
| 729 |
+
|
| 730 |
+
return (
|
| 731 |
+
<div ref={ref} data-dc-slot={id} style={{ position: 'relative', flexShrink: 0 }}>
|
| 732 |
+
<div className="dc-header" style={{ color: DC.label }} onPointerDown={(e) => e.stopPropagation()}>
|
| 733 |
+
<div className="dc-labelrow">
|
| 734 |
+
<div className="dc-grip" onPointerDown={onGripDown} title="Drag to reorder">
|
| 735 |
+
<svg width="9" height="13" viewBox="0 0 9 13" fill="currentColor"><circle cx="2" cy="2" r="1.1"/><circle cx="7" cy="2" r="1.1"/><circle cx="2" cy="6.5" r="1.1"/><circle cx="7" cy="6.5" r="1.1"/><circle cx="2" cy="11" r="1.1"/><circle cx="7" cy="11" r="1.1"/></svg>
|
| 736 |
+
</div>
|
| 737 |
+
<div className="dc-labeltext" onClick={onFocus} title="Click to focus">
|
| 738 |
+
<DCEditable value={label} onChange={onRename} onClick={(e) => e.stopPropagation()}
|
| 739 |
+
style={{ fontSize: 15, fontWeight: 500, color: DC.label, lineHeight: 1 }} />
|
| 740 |
+
</div>
|
| 741 |
+
</div>
|
| 742 |
+
<div className="dc-btns">
|
| 743 |
+
<div ref={menuRef} style={{ position: 'relative' }}>
|
| 744 |
+
<button className="dc-kebab" title="More" onClick={() => setMenuOpen((o) => !o)}>
|
| 745 |
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor"><circle cx="2.5" cy="6" r="1.1"/><circle cx="6" cy="6" r="1.1"/><circle cx="9.5" cy="6" r="1.1"/></svg>
|
| 746 |
+
</button>
|
| 747 |
+
{menuOpen && (
|
| 748 |
+
<div className="dc-menu" onPointerDown={(e) => e.stopPropagation()}>
|
| 749 |
+
<button onClick={() => doExport('png')}>Download PNG</button>
|
| 750 |
+
<button onClick={() => doExport('html')}>Download HTML</button>
|
| 751 |
+
<hr />
|
| 752 |
+
<button className="dc-danger"
|
| 753 |
+
onClick={() => { if (confirming) { setMenuOpen(false); onDelete(); } else setConfirming(true); }}>
|
| 754 |
+
{confirming ? 'Click again to delete' : 'Delete'}
|
| 755 |
+
</button>
|
| 756 |
+
</div>
|
| 757 |
+
)}
|
| 758 |
+
</div>
|
| 759 |
+
<button className="dc-expand" onClick={onFocus} title="Focus">
|
| 760 |
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"><path d="M7 1h4v4M5 11H1V7M11 1L7.5 4.5M1 11l3.5-3.5"/></svg>
|
| 761 |
+
</button>
|
| 762 |
+
</div>
|
| 763 |
+
</div>
|
| 764 |
+
<div ref={cardRef} className="dc-card"
|
| 765 |
+
style={{ borderRadius: 2, boxShadow: '0 1px 3px rgba(0,0,0,.08),0 4px 16px rgba(0,0,0,.06)', overflow: 'hidden', width, height, background: '#fff', ...style }}>
|
| 766 |
+
{children || <div style={{ height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#bbb', fontSize: 13, fontFamily: DC.font }}>{id}</div>}
|
| 767 |
+
</div>
|
| 768 |
+
</div>
|
| 769 |
+
);
|
| 770 |
+
}
|
| 771 |
+
|
| 772 |
+
// Inline rename — commits on blur or Enter.
|
| 773 |
+
function DCEditable({ value, onChange, style, tag = 'span', onClick }) {
|
| 774 |
+
const T = tag;
|
| 775 |
+
return (
|
| 776 |
+
<T className="dc-editable" contentEditable suppressContentEditableWarning
|
| 777 |
+
onClick={onClick}
|
| 778 |
+
onPointerDown={(e) => e.stopPropagation()}
|
| 779 |
+
onBlur={(e) => onChange && onChange(e.currentTarget.textContent)}
|
| 780 |
+
onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); e.currentTarget.blur(); } }}
|
| 781 |
+
style={style}>{value}</T>
|
| 782 |
+
);
|
| 783 |
+
}
|
| 784 |
+
|
| 785 |
+
// ─────────────────────────────────────────────────────────────
|
| 786 |
+
// Focus mode — overlay one artboard; ←/→ within section, ↑/↓ across
|
| 787 |
+
// sections, Esc or backdrop click to exit.
|
| 788 |
+
// ─────────────────────────────────────────────────────────────
|
| 789 |
+
function DCFocusOverlay({ entry, sectionMeta, sectionOrder }) {
|
| 790 |
+
const ctx = React.useContext(DCCtx);
|
| 791 |
+
const { sectionId, artboard } = entry;
|
| 792 |
+
const sec = ctx.section(sectionId);
|
| 793 |
+
const meta = sectionMeta[sectionId];
|
| 794 |
+
const peers = meta.slotIds;
|
| 795 |
+
const aid = artboard.props.id ?? artboard.props.label;
|
| 796 |
+
const idx = peers.indexOf(aid);
|
| 797 |
+
const secIdx = sectionOrder.indexOf(sectionId);
|
| 798 |
+
|
| 799 |
+
const go = (d) => { const n = peers[(idx + d + peers.length) % peers.length]; if (n) ctx.setFocus(`${sectionId}/${n}`); };
|
| 800 |
+
const goSection = (d) => {
|
| 801 |
+
// Sections whose artboards are all deleted have slotIds:[] — step past
|
| 802 |
+
// them to the next non-empty section so ↑/↓ doesn't dead-end.
|
| 803 |
+
const n = sectionOrder.length;
|
| 804 |
+
for (let i = 1; i < n; i++) {
|
| 805 |
+
const ns = sectionOrder[(((secIdx + d * i) % n) + n) % n];
|
| 806 |
+
const first = sectionMeta[ns] && sectionMeta[ns].slotIds[0];
|
| 807 |
+
if (first) { ctx.setFocus(`${ns}/${first}`); return; }
|
| 808 |
+
}
|
| 809 |
+
};
|
| 810 |
+
|
| 811 |
+
React.useEffect(() => {
|
| 812 |
+
const k = (e) => {
|
| 813 |
+
if (e.key === 'ArrowLeft') { e.preventDefault(); go(-1); }
|
| 814 |
+
if (e.key === 'ArrowRight') { e.preventDefault(); go(1); }
|
| 815 |
+
if (e.key === 'ArrowUp') { e.preventDefault(); goSection(-1); }
|
| 816 |
+
if (e.key === 'ArrowDown') { e.preventDefault(); goSection(1); }
|
| 817 |
+
};
|
| 818 |
+
document.addEventListener('keydown', k);
|
| 819 |
+
return () => document.removeEventListener('keydown', k);
|
| 820 |
+
});
|
| 821 |
+
|
| 822 |
+
const { width = 260, height = 480, children } = artboard.props;
|
| 823 |
+
const [vp, setVp] = React.useState({ w: window.innerWidth, h: window.innerHeight });
|
| 824 |
+
React.useEffect(() => { const r = () => setVp({ w: window.innerWidth, h: window.innerHeight }); window.addEventListener('resize', r); return () => window.removeEventListener('resize', r); }, []);
|
| 825 |
+
const scale = Math.max(0.1, Math.min((vp.w - 200) / width, (vp.h - 260) / height, 2));
|
| 826 |
+
|
| 827 |
+
const [ddOpen, setDd] = React.useState(false);
|
| 828 |
+
const Arrow = ({ dir, onClick }) => (
|
| 829 |
+
<button onClick={(e) => { e.stopPropagation(); onClick(); }}
|
| 830 |
+
style={{ position: 'absolute', top: '50%', [dir]: 28, transform: 'translateY(-50%)',
|
| 831 |
+
border: 'none', background: 'rgba(255,255,255,.08)', color: 'rgba(255,255,255,.9)',
|
| 832 |
+
width: 44, height: 44, borderRadius: 22, fontSize: 18, cursor: 'pointer',
|
| 833 |
+
display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'background .15s' }}
|
| 834 |
+
onMouseEnter={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,.18)')}
|
| 835 |
+
onMouseLeave={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,.08)')}>
|
| 836 |
+
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
| 837 |
+
<path d={dir === 'left' ? 'M11 3L5 9l6 6' : 'M7 3l6 6-6 6'} /></svg>
|
| 838 |
+
</button>
|
| 839 |
+
);
|
| 840 |
+
|
| 841 |
+
// Portal to body so position:fixed is the real viewport regardless of any
|
| 842 |
+
// transform on DesignCanvas's ancestors (including the canvas zoom itself).
|
| 843 |
+
return ReactDOM.createPortal(
|
| 844 |
+
<div onClick={() => ctx.setFocus(null)}
|
| 845 |
+
onWheel={(e) => e.preventDefault()}
|
| 846 |
+
style={{ position: 'fixed', inset: 0, zIndex: 100, background: 'rgba(24,20,16,.6)', backdropFilter: 'blur(14px)',
|
| 847 |
+
fontFamily: DC.font, color: '#fff' }}>
|
| 848 |
+
|
| 849 |
+
{/* top bar: section dropdown (left) · close (right) */}
|
| 850 |
+
<div onClick={(e) => e.stopPropagation()}
|
| 851 |
+
style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 72, display: 'flex', alignItems: 'flex-start', padding: '16px 20px 0', gap: 16 }}>
|
| 852 |
+
<div style={{ position: 'relative' }}>
|
| 853 |
+
<button onClick={() => setDd((o) => !o)}
|
| 854 |
+
style={{ border: 'none', background: 'transparent', color: '#fff', cursor: 'pointer', padding: '6px 8px',
|
| 855 |
+
borderRadius: 6, textAlign: 'left', fontFamily: 'inherit' }}>
|
| 856 |
+
<span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
| 857 |
+
<span style={{ fontSize: 18, fontWeight: 600, letterSpacing: -0.3 }}>{meta.title}</span>
|
| 858 |
+
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" style={{ opacity: .7 }}><path d="M2 4l3.5 3.5L9 4"/></svg>
|
| 859 |
+
</span>
|
| 860 |
+
{meta.subtitle && <span style={{ display: 'block', fontSize: 13, opacity: .6, fontWeight: 400, marginTop: 2 }}>{meta.subtitle}</span>}
|
| 861 |
+
</button>
|
| 862 |
+
{ddOpen && (
|
| 863 |
+
<div style={{ position: 'absolute', top: '100%', left: 0, marginTop: 4, background: '#2a251f', borderRadius: 8,
|
| 864 |
+
boxShadow: '0 8px 32px rgba(0,0,0,.4)', padding: 4, minWidth: 200, zIndex: 10 }}>
|
| 865 |
+
{sectionOrder.filter((sid) => sectionMeta[sid].slotIds.length).map((sid) => (
|
| 866 |
+
<button key={sid} onClick={() => { setDd(false); const f = sectionMeta[sid].slotIds[0]; if (f) ctx.setFocus(`${sid}/${f}`); }}
|
| 867 |
+
style={{ display: 'block', width: '100%', textAlign: 'left', border: 'none', cursor: 'pointer',
|
| 868 |
+
background: sid === sectionId ? 'rgba(255,255,255,.1)' : 'transparent', color: '#fff',
|
| 869 |
+
padding: '8px 12px', borderRadius: 5, fontSize: 14, fontWeight: sid === sectionId ? 600 : 400, fontFamily: 'inherit' }}>
|
| 870 |
+
{sectionMeta[sid].title}
|
| 871 |
+
</button>
|
| 872 |
+
))}
|
| 873 |
+
</div>
|
| 874 |
+
)}
|
| 875 |
+
</div>
|
| 876 |
+
<div style={{ flex: 1 }} />
|
| 877 |
+
<button onClick={() => ctx.setFocus(null)}
|
| 878 |
+
onMouseEnter={(e) => (e.currentTarget.style.background = 'rgba(255,255,255,.12)')}
|
| 879 |
+
onMouseLeave={(e) => (e.currentTarget.style.background = 'transparent')}
|
| 880 |
+
style={{ border: 'none', background: 'transparent', color: 'rgba(255,255,255,.7)', width: 32, height: 32,
|
| 881 |
+
borderRadius: 16, fontSize: 20, cursor: 'pointer', lineHeight: 1, transition: 'background .12s' }}>×</button>
|
| 882 |
+
</div>
|
| 883 |
+
|
| 884 |
+
{/* card centered, label + index below — only the card itself stops
|
| 885 |
+
propagation so any backdrop click (including the margins around
|
| 886 |
+
the card) exits focus */}
|
| 887 |
+
<div
|
| 888 |
+
style={{ position: 'absolute', top: 64, bottom: 56, left: 100, right: 100, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 16 }}>
|
| 889 |
+
<div onClick={(e) => e.stopPropagation()} style={{ width: width * scale, height: height * scale, position: 'relative' }}>
|
| 890 |
+
<div style={{ width, height, transform: `scale(${scale})`, transformOrigin: 'top left', background: '#fff', borderRadius: 2, overflow: 'hidden',
|
| 891 |
+
boxShadow: '0 20px 80px rgba(0,0,0,.4)' }}>
|
| 892 |
+
{children || <div style={{ height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#bbb' }}>{aid}</div>}
|
| 893 |
+
</div>
|
| 894 |
+
</div>
|
| 895 |
+
<div onClick={(e) => e.stopPropagation()} style={{ fontSize: 14, fontWeight: 500, opacity: .85, textAlign: 'center' }}>
|
| 896 |
+
{(sec.labels || {})[aid] ?? artboard.props.label}
|
| 897 |
+
<span style={{ opacity: .5, marginLeft: 10, fontVariantNumeric: 'tabular-nums' }}>{idx + 1} / {peers.length}</span>
|
| 898 |
+
</div>
|
| 899 |
+
</div>
|
| 900 |
+
|
| 901 |
+
<Arrow dir="left" onClick={() => go(-1)} />
|
| 902 |
+
<Arrow dir="right" onClick={() => go(1)} />
|
| 903 |
+
|
| 904 |
+
{/* dots */}
|
| 905 |
+
<div onClick={(e) => e.stopPropagation()}
|
| 906 |
+
style={{ position: 'absolute', bottom: 20, left: '50%', transform: 'translateX(-50%)', display: 'flex', gap: 8 }}>
|
| 907 |
+
{peers.map((p, i) => (
|
| 908 |
+
<button key={p} onClick={() => ctx.setFocus(`${sectionId}/${p}`)}
|
| 909 |
+
style={{ border: 'none', padding: 0, cursor: 'pointer', width: 6, height: 6, borderRadius: 3,
|
| 910 |
+
background: i === idx ? '#fff' : 'rgba(255,255,255,.3)' }} />
|
| 911 |
+
))}
|
| 912 |
+
</div>
|
| 913 |
+
</div>,
|
| 914 |
+
document.body,
|
| 915 |
+
);
|
| 916 |
+
}
|
| 917 |
+
|
| 918 |
+
// ─────────────────────────────────────────────────────────────
|
| 919 |
+
// Post-it — absolute-positioned sticky note
|
| 920 |
+
// ─────────────────────────────────────────────────────────────
|
| 921 |
+
function DCPostIt({ children, top, left, right, bottom, rotate = -2, width = 180 }) {
|
| 922 |
+
return (
|
| 923 |
+
<div style={{
|
| 924 |
+
position: 'absolute', top, left, right, bottom, width,
|
| 925 |
+
background: DC.postitBg, padding: '14px 16px',
|
| 926 |
+
fontFamily: '"Comic Sans MS", "Marker Felt", "Segoe Print", cursive',
|
| 927 |
+
fontSize: 14, lineHeight: 1.4, color: DC.postitText,
|
| 928 |
+
boxShadow: '0 2px 8px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.08)',
|
| 929 |
+
transform: `rotate(${rotate}deg)`,
|
| 930 |
+
zIndex: 5,
|
| 931 |
+
}}>{children}</div>
|
| 932 |
+
);
|
| 933 |
+
}
|
| 934 |
+
|
| 935 |
+
Object.assign(window, { DesignCanvas, DCSection, DCArtboard, DCPostIt });
|
| 936 |
+
|
docs/design_handoff/design_files/evidence.jsx
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Evidence cards: stack below the map. One card per specialist that fired.
|
| 2 |
+
Each card carries source label, formatted output, tier badge, doc_id,
|
| 3 |
+
and prominent vintage. Mobile: horizontal swipe; desktop: 2-col grid.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
const EVIDENCE = [
|
| 7 |
+
{
|
| 8 |
+
id: "e1", citeId: "c1", tier: "empirical",
|
| 9 |
+
source: "USGS",
|
| 10 |
+
title: "Post-Sandy high-water marks within 500ft",
|
| 11 |
+
fmt: "table",
|
| 12 |
+
table: [
|
| 13 |
+
["HWM-NY-3081", "7.4 ft NAVD88", "0.18 mi"],
|
| 14 |
+
["HWM-NY-3082", "8.1 ft NAVD88", "0.22 mi"],
|
| 15 |
+
["HWM-NY-3105", "6.8 ft NAVD88", "0.31 mi"],
|
| 16 |
+
],
|
| 17 |
+
docId: "USGS-OFR-2013-1234",
|
| 18 |
+
vintage: "2013-05",
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
id: "e2", citeId: "c3", tier: "empirical",
|
| 22 |
+
source: "FloodNet NYC",
|
| 23 |
+
title: "Sensor BK-RH-002 , monthly above-curb events",
|
| 24 |
+
fmt: "spark",
|
| 25 |
+
spark: [0,0,1,0,2,1,0,0,3,0,1,0,0,0,2,1,0,0,1,0,2,4,1,1],
|
| 26 |
+
headline: "7 events", sub: "Jun 2024 → Apr 2026 · peak 14.3 cm",
|
| 27 |
+
docId: "FN-BK-RH-002",
|
| 28 |
+
vintage: "2026-04",
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
id: "e3", citeId: "c4", tier: "modeled",
|
| 32 |
+
source: "FEMA",
|
| 33 |
+
title: "Preliminary FIRM, panel 36047C0207G",
|
| 34 |
+
fmt: "scalar",
|
| 35 |
+
scalar: { value: "Zone AE", unit: "BFE 11 ft NAVD88", aux: "freeboard +4.8 ft" },
|
| 36 |
+
docId: "FEMA-FIRM-36047C0207G",
|
| 37 |
+
vintage: "2024-09",
|
| 38 |
+
},
|
| 39 |
+
{
|
| 40 |
+
id: "e4", citeId: "c5", tier: "modeled",
|
| 41 |
+
source: "NYC DEP",
|
| 42 |
+
title: "Stormwater Flood Map , moderate scenario",
|
| 43 |
+
fmt: "thumb",
|
| 44 |
+
thumb: "stormwater",
|
| 45 |
+
sub: "2.13 in/hr · ponding ≥4 in W half of lot · routed toward Imlay St",
|
| 46 |
+
docId: "NYCDEP-SWFM-2024",
|
| 47 |
+
vintage: "2024-06",
|
| 48 |
+
},
|
| 49 |
+
{
|
| 50 |
+
id: "e5", citeId: "c6", tier: "modeled",
|
| 51 |
+
source: "NPCC4",
|
| 52 |
+
title: "Sea-level rise projections for Lower NY Harbor",
|
| 53 |
+
fmt: "forecast",
|
| 54 |
+
forecast: [
|
| 55 |
+
{ year: 2030, low: 4, mid: 6, high: 9 },
|
| 56 |
+
{ year: 2050, low: 13, mid: 22, high: 30 },
|
| 57 |
+
{ year: 2080, low: 28, mid: 49, high: 75 },
|
| 58 |
+
{ year: 2100, low: 38, mid: 71, high: 114 },
|
| 59 |
+
],
|
| 60 |
+
docId: "NPCC4-Ch3-Tbl3.2",
|
| 61 |
+
vintage: "2024-03",
|
| 62 |
+
},
|
| 63 |
+
{
|
| 64 |
+
id: "e6", citeId: "c9", tier: "synthetic",
|
| 65 |
+
source: "TerraMind v1.2",
|
| 66 |
+
title: "Synthetic SAR for 2025-09-14 (Sentinel-1 cloud-occluded)",
|
| 67 |
+
fmt: "thumb",
|
| 68 |
+
thumb: "synthetic",
|
| 69 |
+
sub: "Generated, not observed. Confidence 0.71. Provided as prior for downstream models; do not cite as observation.",
|
| 70 |
+
docId: "RIPRAP-SYN-20250914",
|
| 71 |
+
vintage: "2025-09",
|
| 72 |
+
},
|
| 73 |
+
{
|
| 74 |
+
id: "e7", citeId: "c7", tier: "proxy",
|
| 75 |
+
source: "NYC 311",
|
| 76 |
+
title: "Flood complaints, BK CB6 (2019–2025)",
|
| 77 |
+
fmt: "histogram",
|
| 78 |
+
months: [3,2,1,0,1,4,7,12,18,11,5,3,4,2,1,0,2,3,8,9,4,2,1,0],
|
| 79 |
+
headline: "89 calls", sub: "seasonal cluster Aug–Oct",
|
| 80 |
+
docId: "NYC311-FLD-CB6",
|
| 81 |
+
vintage: "2025-12",
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
id: "e8", citeId: "c8", tier: "proxy",
|
| 85 |
+
source: "FEMA NFIP",
|
| 86 |
+
title: "NFIP claims, tract 36047008500",
|
| 87 |
+
fmt: "scalar",
|
| 88 |
+
scalar: { value: "$4.1M", unit: "47 paid losses", aux: "since 2000-01-01" },
|
| 89 |
+
docId: "NFIP-T36047008500",
|
| 90 |
+
vintage: "2024-12",
|
| 91 |
+
},
|
| 92 |
+
];
|
| 93 |
+
|
| 94 |
+
const Spark = ({ data, color }) => {
|
| 95 |
+
const max = Math.max(...data, 1);
|
| 96 |
+
const w = 180, h = 36, n = data.length;
|
| 97 |
+
return (
|
| 98 |
+
<svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} preserveAspectRatio="none" aria-hidden="true">
|
| 99 |
+
{data.map((v, i) => (
|
| 100 |
+
<rect
|
| 101 |
+
key={i}
|
| 102 |
+
x={(i / n) * w + 0.5}
|
| 103 |
+
y={h - (v / max) * h}
|
| 104 |
+
width={Math.max(2, w / n - 1.5)}
|
| 105 |
+
height={(v / max) * h}
|
| 106 |
+
fill={color}
|
| 107 |
+
/>
|
| 108 |
+
))}
|
| 109 |
+
</svg>
|
| 110 |
+
);
|
| 111 |
+
};
|
| 112 |
+
|
| 113 |
+
const Histogram = ({ data, color }) => (
|
| 114 |
+
<Spark data={data} color={color} />
|
| 115 |
+
);
|
| 116 |
+
|
| 117 |
+
const ForecastChart = ({ data, color }) => {
|
| 118 |
+
const w = 220, h = 80, pad = 4;
|
| 119 |
+
const xs = data.map((d, i) => pad + (i / (data.length - 1)) * (w - pad * 2));
|
| 120 |
+
const max = Math.max(...data.map(d => d.high));
|
| 121 |
+
const y = (v) => h - pad - (v / max) * (h - pad * 2);
|
| 122 |
+
const path = (key) => xs.map((x, i) => `${i ? "L" : "M"} ${x} ${y(data[i][key])}`).join(" ");
|
| 123 |
+
const range = xs.map((x, i) => ({ x, lo: y(data[i].low), hi: y(data[i].high) }));
|
| 124 |
+
const areaD = `M ${range.map(r => `${r.x} ${r.lo}`).join(" L ")} L ${[...range].reverse().map(r => `${r.x} ${r.hi}`).join(" L ")} Z`;
|
| 125 |
+
return (
|
| 126 |
+
<svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} aria-hidden="true">
|
| 127 |
+
<path d={areaD} fill={color} fillOpacity="0.18" />
|
| 128 |
+
<path d={path("mid")} fill="none" stroke={color} strokeWidth="1.5"/>
|
| 129 |
+
{data.map((d, i) => (
|
| 130 |
+
<g key={i}>
|
| 131 |
+
<circle cx={xs[i]} cy={y(d.mid)} r="2" fill={color}/>
|
| 132 |
+
<text x={xs[i]} y={h - 1} fontSize="9" fontFamily="IBM Plex Mono" textAnchor="middle" fill="#6B6B6B">{d.year}</text>
|
| 133 |
+
</g>
|
| 134 |
+
))}
|
| 135 |
+
</svg>
|
| 136 |
+
);
|
| 137 |
+
};
|
| 138 |
+
|
| 139 |
+
const ThumbStripe = ({ kind }) => (
|
| 140 |
+
<svg viewBox="0 0 220 110" width="100%" height="110" aria-hidden="true" style={{ display: "block", background: "#F2F2EE" }}>
|
| 141 |
+
<defs>
|
| 142 |
+
<pattern id={`thumb-${kind}`} width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
|
| 143 |
+
<rect width="6" height="6" fill={kind === "synthetic" ? "rgba(42,111,168,0.18)" : "rgba(42,111,168,0.30)"}/>
|
| 144 |
+
<line x1="0" y1="0" x2="0" y2="6" stroke={kind === "synthetic" ? "#2A6FA8" : "#0B5394"} strokeWidth="0.8"/>
|
| 145 |
+
</pattern>
|
| 146 |
+
</defs>
|
| 147 |
+
{kind === "stormwater" ? (
|
| 148 |
+
<>
|
| 149 |
+
<rect x="0" y="0" width="220" height="110" fill="#FAFAF7"/>
|
| 150 |
+
<path d="M0 60 L60 55 L120 70 L180 80 L220 78 L220 110 L0 110 Z" fill="rgba(42,111,168,0.30)" stroke="#2A6FA8" strokeWidth="1"/>
|
| 151 |
+
<path d="M0 75 L60 72 L120 85 L180 92 L220 90 L220 110 L0 110 Z" fill="rgba(42,111,168,0.20)" stroke="#2A6FA8" strokeWidth="0.8"/>
|
| 152 |
+
<circle cx="100" cy="68" r="4" fill="#D17C00" stroke="#FAFAF7" strokeWidth="1.5"/>
|
| 153 |
+
</>
|
| 154 |
+
) : (
|
| 155 |
+
<>
|
| 156 |
+
<rect x="0" y="0" width="220" height="110" fill={`url(#thumb-${kind})`}/>
|
| 157 |
+
<text x="8" y="100" fontFamily="IBM Plex Mono" fontSize="9" fill="#2A6FA8">SYN · 2025-09-14</text>
|
| 158 |
+
</>
|
| 159 |
+
)}
|
| 160 |
+
</svg>
|
| 161 |
+
);
|
| 162 |
+
|
| 163 |
+
const EvidenceCard = ({ ev, onCite }) => {
|
| 164 |
+
const tierColor = `var(--tier-${ev.tier})`;
|
| 165 |
+
return (
|
| 166 |
+
<article className={`evidence-card evidence-card-${ev.tier}`} aria-labelledby={`ec-${ev.id}-title`}>
|
| 167 |
+
<header className="evidence-card-head">
|
| 168 |
+
<div className="evidence-card-source">
|
| 169 |
+
<TierGlyph tier={ev.tier} size={11} color={tierColor} />
|
| 170 |
+
<span className="evidence-card-source-label">{ev.source}</span>
|
| 171 |
+
</div>
|
| 172 |
+
<span className="evidence-card-vintage" title="Data vintage">v. {ev.vintage}</span>
|
| 173 |
+
</header>
|
| 174 |
+
<h4 id={`ec-${ev.id}-title`} className="evidence-card-title">{ev.title}</h4>
|
| 175 |
+
|
| 176 |
+
<div className="evidence-card-body">
|
| 177 |
+
{ev.fmt === "scalar" && (
|
| 178 |
+
<div className="evidence-scalar">
|
| 179 |
+
<div className="evidence-scalar-value" style={{ color: tierColor }}>{ev.scalar.value}</div>
|
| 180 |
+
<div className="evidence-scalar-unit">{ev.scalar.unit}</div>
|
| 181 |
+
{ev.scalar.aux && <div className="evidence-scalar-aux">{ev.scalar.aux}</div>}
|
| 182 |
+
</div>
|
| 183 |
+
)}
|
| 184 |
+
{ev.fmt === "table" && (
|
| 185 |
+
<table className="evidence-table">
|
| 186 |
+
<thead><tr><th>id</th><th>elev.</th><th>dist.</th></tr></thead>
|
| 187 |
+
<tbody>
|
| 188 |
+
{ev.table.map((row, i) => (
|
| 189 |
+
<tr key={i}>{row.map((c, j) => <td key={j}>{c}</td>)}</tr>
|
| 190 |
+
))}
|
| 191 |
+
</tbody>
|
| 192 |
+
</table>
|
| 193 |
+
)}
|
| 194 |
+
{ev.fmt === "spark" && (
|
| 195 |
+
<div className="evidence-spark">
|
| 196 |
+
<div className="evidence-spark-headline" style={{ color: tierColor }}>{ev.headline}</div>
|
| 197 |
+
<Spark data={ev.spark} color={tierColor}/>
|
| 198 |
+
<div className="evidence-scalar-aux">{ev.sub}</div>
|
| 199 |
+
</div>
|
| 200 |
+
)}
|
| 201 |
+
{ev.fmt === "histogram" && (
|
| 202 |
+
<div className="evidence-spark">
|
| 203 |
+
<div className="evidence-spark-headline" style={{ color: tierColor }}>{ev.headline}</div>
|
| 204 |
+
<Histogram data={ev.months} color={tierColor}/>
|
| 205 |
+
<div className="evidence-scalar-aux">{ev.sub}</div>
|
| 206 |
+
</div>
|
| 207 |
+
)}
|
| 208 |
+
{ev.fmt === "forecast" && (
|
| 209 |
+
<div className="evidence-spark">
|
| 210 |
+
<ForecastChart data={ev.forecast} color={tierColor}/>
|
| 211 |
+
<div className="evidence-scalar-aux">inches MSL · 17th–83rd %ile range, median line</div>
|
| 212 |
+
</div>
|
| 213 |
+
)}
|
| 214 |
+
{ev.fmt === "thumb" && (
|
| 215 |
+
<div className="evidence-thumb">
|
| 216 |
+
<ThumbStripe kind={ev.thumb}/>
|
| 217 |
+
<div className="evidence-scalar-aux">{ev.sub}</div>
|
| 218 |
+
</div>
|
| 219 |
+
)}
|
| 220 |
+
</div>
|
| 221 |
+
|
| 222 |
+
<footer className="evidence-card-foot">
|
| 223 |
+
<button
|
| 224 |
+
type="button"
|
| 225 |
+
className="evidence-card-cite"
|
| 226 |
+
onClick={() => onCite?.(ev.citeId)}
|
| 227 |
+
title={`Open citation ${ev.citeId} in drawer`}
|
| 228 |
+
>
|
| 229 |
+
<span className="evidence-card-docid">{ev.docId}</span>
|
| 230 |
+
<span className="evidence-card-cite-arrow" aria-hidden="true">→</span>
|
| 231 |
+
</button>
|
| 232 |
+
<TierBadge tier={ev.tier} compact />
|
| 233 |
+
</footer>
|
| 234 |
+
</article>
|
| 235 |
+
);
|
| 236 |
+
};
|
| 237 |
+
|
| 238 |
+
const EvidenceGrid = ({ onCite }) => (
|
| 239 |
+
<section className="evidence-grid" aria-label="Evidence cards">
|
| 240 |
+
<div className="evidence-grid-head">
|
| 241 |
+
<span className="section-label">Evidence · 8 cards</span>
|
| 242 |
+
<span className="evidence-grid-meta">
|
| 243 |
+
<span className="evidence-grid-tally"><TierGlyph tier="empirical" size={9}/> 3</span>
|
| 244 |
+
<span className="evidence-grid-tally"><TierGlyph tier="modeled" size={9}/> 3</span>
|
| 245 |
+
<span className="evidence-grid-tally"><TierGlyph tier="proxy" size={9}/> 2</span>
|
| 246 |
+
<span className="evidence-grid-tally"><TierGlyph tier="synthetic" size={9}/> 1</span>
|
| 247 |
+
</span>
|
| 248 |
+
</div>
|
| 249 |
+
<div className="evidence-grid-rail">
|
| 250 |
+
{EVIDENCE.map((ev) => (
|
| 251 |
+
<EvidenceCard key={ev.id} ev={ev} onCite={onCite}/>
|
| 252 |
+
))}
|
| 253 |
+
</div>
|
| 254 |
+
</section>
|
| 255 |
+
);
|
| 256 |
+
|
| 257 |
+
Object.assign(window, { EvidenceGrid, EvidenceCard });
|
docs/design_handoff/design_files/findings.jsx
ADDED
|
@@ -0,0 +1,883 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Riprap v0.4.4 · Findings region.
|
| 2 |
+
Card grammar: a small set of body variants any Stone's specialists render into.
|
| 3 |
+
Variants: tabular, headline, visualization, raster-thumbnail, time-series,
|
| 4 |
+
composite-register (novel), comparison (novel · EMP vs SYN), text-headline.
|
| 5 |
+
Common chrome: header (source badge + tier glyph + vintage) → title → body → footer (source ID + tier badge).
|
| 6 |
+
*/
|
| 7 |
+
|
| 8 |
+
const { useState: useFi, useMemo: useFiMemo } = React;
|
| 9 |
+
|
| 10 |
+
/* ─── Card data ─── */
|
| 11 |
+
|
| 12 |
+
const CARDS_BY_QUERY = {
|
| 13 |
+
redhook: {
|
| 14 |
+
cornerstone: ["fc-fema", "fc-hwm", "fc-stormwater"],
|
| 15 |
+
keystone: ["fc-register-rh"],
|
| 16 |
+
touchstone: ["fc-floodnet", "fc-311", "fc-nws", "fc-terramind-lulc", "fc-prithvi-pluvial"],
|
| 17 |
+
lodestone: ["fc-ttm-surge", "fc-ttm-surge-ft", "fc-npcc4"],
|
| 18 |
+
capstone: ["fc-mellea-meta"],
|
| 19 |
+
},
|
| 20 |
+
bronx: {
|
| 21 |
+
cornerstone: ["fc-fema-x", "fc-stormwater-bx"],
|
| 22 |
+
keystone: ["fc-register-bx"],
|
| 23 |
+
touchstone: ["fc-311-bx", "fc-nws"],
|
| 24 |
+
lodestone: [], /* full-Stone silence: address is inland, no Battery surge relevance */
|
| 25 |
+
capstone: ["fc-mellea-meta-bx"],
|
| 26 |
+
},
|
| 27 |
+
};
|
| 28 |
+
|
| 29 |
+
const CARDS = {
|
| 30 |
+
/* ── Cornerstone ── */
|
| 31 |
+
"fc-fema": {
|
| 32 |
+
stone: "cornerstone", tier: "modeled", variant: "headline",
|
| 33 |
+
source: "FEMA", agency: "Federal Emergency Management Agency",
|
| 34 |
+
title: "Preliminary FIRM, panel 36047C0207G",
|
| 35 |
+
headline: "Zone AE", subhead: "BFE 11 ft NAVD88 · freeboard +4.8 ft",
|
| 36 |
+
body: "Address sits within the regulatory 1% annual-chance floodplain. Base Flood Elevation 11.0 ft NAVD88; first floor must be at or above this datum for NFIP rating.",
|
| 37 |
+
docId: "FEMA-FIRM-36047C0207G", vintage: "2024-09", citeId: "c4",
|
| 38 |
+
mapKey: "fema-ae",
|
| 39 |
+
},
|
| 40 |
+
"fc-hwm": {
|
| 41 |
+
stone: "cornerstone", tier: "empirical", variant: "tabular",
|
| 42 |
+
source: "USGS", agency: "U.S. Geological Survey",
|
| 43 |
+
title: "Post-Sandy high-water marks within 500 ft",
|
| 44 |
+
columns: ["id", "elev.", "dist."],
|
| 45 |
+
rows: [
|
| 46 |
+
["HWM-NY-3081", "7.4 ft NAVD88", "0.18 mi"],
|
| 47 |
+
["HWM-NY-3082", "8.1 ft NAVD88", "0.22 mi"],
|
| 48 |
+
["HWM-NY-3105", "6.8 ft NAVD88", "0.31 mi"],
|
| 49 |
+
],
|
| 50 |
+
sub: "3 marks · max 8.1 ft · surveyed Nov 2012",
|
| 51 |
+
docId: "USGS-OFR-2013-1234", vintage: "2013-05", citeId: "c1",
|
| 52 |
+
mapKey: "hwm",
|
| 53 |
+
},
|
| 54 |
+
"fc-stormwater": {
|
| 55 |
+
stone: "cornerstone", tier: "modeled", variant: "raster",
|
| 56 |
+
source: "NYC DEP", agency: "NYC Dept. of Environmental Protection",
|
| 57 |
+
title: "Stormwater Flood Map · moderate scenario",
|
| 58 |
+
rasterKind: "stormwater",
|
| 59 |
+
sub: "2.13 in/hr · ponding ≥4 in W half of lot · routed toward Imlay St",
|
| 60 |
+
docId: "NYCDEP-SWFM-2024", vintage: "2024-06", citeId: "c5",
|
| 61 |
+
mapKey: "stormwater",
|
| 62 |
+
},
|
| 63 |
+
"fc-fema-x": {
|
| 64 |
+
stone: "cornerstone", tier: "modeled", variant: "headline",
|
| 65 |
+
source: "FEMA", agency: "Federal Emergency Management Agency",
|
| 66 |
+
title: "Preliminary FIRM, panel 36005C0152F",
|
| 67 |
+
headline: "Zone X", subhead: "outside the 1% annual-chance floodplain",
|
| 68 |
+
body: "Address is in Zone X, the unshaded 0.2% annual-chance area or higher ground. NFIP coverage optional; insurance not mandated.",
|
| 69 |
+
docId: "FEMA-FIRM-36005C0152F", vintage: "2024-09", citeId: "cx1",
|
| 70 |
+
mapKey: "fema-x",
|
| 71 |
+
},
|
| 72 |
+
"fc-stormwater-bx": {
|
| 73 |
+
stone: "cornerstone", tier: "modeled", variant: "raster",
|
| 74 |
+
source: "NYC DEP", agency: "NYC Dept. of Environmental Protection",
|
| 75 |
+
title: "Stormwater Flood Map · moderate scenario",
|
| 76 |
+
rasterKind: "stormwater-dry",
|
| 77 |
+
sub: "2.13 in/hr · no ponding ≥4 in within parcel · upslope grade 3.4%",
|
| 78 |
+
docId: "NYCDEP-SWFM-2024", vintage: "2024-06", citeId: "cx2",
|
| 79 |
+
mapKey: "stormwater",
|
| 80 |
+
},
|
| 81 |
+
|
| 82 |
+
/* ── Keystone (composite register) ── */
|
| 83 |
+
"fc-register-rh": {
|
| 84 |
+
stone: "keystone", tier: "empirical", variant: "register",
|
| 85 |
+
source: "NYC OpenData", agency: "NYC OpenData · multi-agency join",
|
| 86 |
+
title: "Nearby exposed assets",
|
| 87 |
+
registers: [
|
| 88 |
+
{ reg: "MTA", tier: "empirical", label: null, detail: null, sourceId: null, vintage: null, note: "no entrances within radius" },
|
| 89 |
+
{ reg: "NYCHA", tier: "empirical", label: null, detail: null, sourceId: null, vintage: null, note: "no NYCHA developments within 1.0 mi" },
|
| 90 |
+
{ reg: "DOE", tier: "empirical", label: null, detail: null, sourceId: null, vintage: null, note: "no DOE schools within 1.0 mi" },
|
| 91 |
+
{ reg: "DOH", tier: "empirical", label: null, detail: null, sourceId: null, vintage: null, note: "no acute-care hospitals within 1.0 mi" },
|
| 92 |
+
{ reg: "PLUTO", tier: "empirical", label: null, detail: null, sourceId: null, vintage: null, note: "PLUTO join skipped: queried address not in NYC PLUTO dataset" },
|
| 93 |
+
],
|
| 94 |
+
sub: "5 specialists · 5 silent_by_design · 0 cards landed (full inventory shown)",
|
| 95 |
+
docId: "RIPRAP-EXP-RH80", vintage: "2026-05", citeId: "c-reg-rh",
|
| 96 |
+
mapKey: "registers",
|
| 97 |
+
},
|
| 98 |
+
"fc-register-bx": {
|
| 99 |
+
stone: "keystone", tier: "empirical", variant: "register",
|
| 100 |
+
source: "NYC OpenData", agency: "NYC OpenData · multi-agency join",
|
| 101 |
+
title: "Nearby exposed assets",
|
| 102 |
+
registers: [
|
| 103 |
+
{ reg: "MTA", tier: "empirical", label: "Pelham Pkwy 5 station", detail: "0.18 mi · 5", sourceId: "MTA-ENT-N122", vintage: "2025-11", note: null },
|
| 104 |
+
{ reg: "NYCHA", tier: "empirical", label: null, detail: null, sourceId: null, vintage: null, note: "no NYCHA developments within 1.0 mi (silent)" },
|
| 105 |
+
{ reg: "DOE", tier: "empirical", label: "PS 89 Cinco Estrellas", detail: "0.22 mi · 612 K-5", sourceId: "DOE-X089", vintage: "2024-25", note: null },
|
| 106 |
+
{ reg: "DOH", tier: "empirical", label: "Jacobi Medical Center", detail: "0.51 mi · 457 beds", sourceId: "DOH-JMC", vintage: "2025-Q1", note: null },
|
| 107 |
+
{ reg: "PLUTO", tier: "empirical", label: "Lot 36005 / 4382 / 18", detail: "BIN 2098441 · R5", sourceId: "PLUTO-2024v2", vintage: "2024-12", note: null },
|
| 108 |
+
],
|
| 109 |
+
sub: "4 of 5 registers fired · 1 silent · joined within 1.0 mi",
|
| 110 |
+
docId: "RIPRAP-EXP-BX12", vintage: "2026-05", citeId: "cx-reg",
|
| 111 |
+
mapKey: "registers",
|
| 112 |
+
},
|
| 113 |
+
|
| 114 |
+
/* ── Touchstone ── */
|
| 115 |
+
"fc-floodnet": {
|
| 116 |
+
stone: "touchstone", tier: "empirical", variant: "spark",
|
| 117 |
+
source: "FloodNet", agency: "FloodNet NYC sensor network",
|
| 118 |
+
title: "Sensor BK-RH-002, monthly above-curb events",
|
| 119 |
+
headline: "7 events", subhead: "Jun 2024 → Apr 2026 · peak 14.3 cm",
|
| 120 |
+
spark: [0,0,1,0,2,1,0,0,3,0,1,0,0,0,2,1,0,0,1,0,2,4,1,1],
|
| 121 |
+
sparkSub: "Sensor located 0.21 mi N at Coffey & Van Brunt. Above-curb depth in cm; events ≥2 cm.",
|
| 122 |
+
docId: "FN-BK-RH-002", vintage: "2026-04", citeId: "c3",
|
| 123 |
+
mapKey: "floodnet",
|
| 124 |
+
},
|
| 125 |
+
"fc-311": {
|
| 126 |
+
stone: "touchstone", tier: "proxy", variant: "histogram",
|
| 127 |
+
source: "NYC 311", agency: "NYC 311 service requests",
|
| 128 |
+
title: "Recent 311 flood complaints, BK CB6",
|
| 129 |
+
headline: "89 calls", subhead: "2019–2025 · seasonal cluster Aug–Oct",
|
| 130 |
+
histogram: [3,2,1,0,1,4,7,12,18,11,5,3,4,2,1,0,2,3,8,9,4,2,1,0],
|
| 131 |
+
sparkSub: "Filtered to complaint types: Sewer (Backup), Street Flooding, Catch Basin Clogged. Within 200 m of address.",
|
| 132 |
+
docId: "NYC311-FLD-CB6", vintage: "2025-12", citeId: "c7",
|
| 133 |
+
mapKey: "complaints",
|
| 134 |
+
},
|
| 135 |
+
"fc-311-bx": {
|
| 136 |
+
stone: "touchstone", tier: "proxy", variant: "histogram",
|
| 137 |
+
source: "NYC 311", agency: "NYC 311 service requests",
|
| 138 |
+
title: "Recent 311 flood complaints, BX CB11",
|
| 139 |
+
headline: "12 calls", subhead: "2019–2025 · sparse · no seasonal cluster",
|
| 140 |
+
histogram: [0,0,1,0,0,1,0,1,2,0,0,0,1,0,0,0,1,0,2,1,1,0,0,1],
|
| 141 |
+
sparkSub: "Filtered to complaint types: Sewer (Backup), Street Flooding, Catch Basin Clogged. Within 200 m of address.",
|
| 142 |
+
docId: "NYC311-FLD-CB11", vintage: "2025-12", citeId: "cx7",
|
| 143 |
+
mapKey: "complaints",
|
| 144 |
+
},
|
| 145 |
+
"fc-prithvi-pluvial": {
|
| 146 |
+
stone: "touchstone", tier: "modeled", variant: "raster-pred",
|
| 147 |
+
source: "Prithvi-NYC-Pluvial", agency: "NASA-IBM Prithvi v2 · NYC fine-tune",
|
| 148 |
+
title: "Pluvial flood prediction · Prithvi-NYC-Pluvial",
|
| 149 |
+
rasterKind: "prithvi",
|
| 150 |
+
headline: "0.3% flooded", subhead: "no flooding apparent · scene 2026-05-02",
|
| 151 |
+
sub: "Model interpretation of imagery, not real-time observation. Confidence-mean 0.84 across non-flooded pixels.",
|
| 152 |
+
docId: "PRITHVI-NYC-PLUV-V2-20260502", vintage: "2026-05-02 · Sentinel-2",
|
| 153 |
+
illustrative: true, citeId: "c-prithvi",
|
| 154 |
+
mapKey: "prithvi-pluvial",
|
| 155 |
+
},
|
| 156 |
+
"fc-terramind-lulc": {
|
| 157 |
+
stone: "touchstone", tier: "synthetic", variant: "lulc",
|
| 158 |
+
source: "TerraMind v1.2", agency: "IBM TerraMind v1.2 · Sentinel-2 inputs",
|
| 159 |
+
title: "Land use / land cover · TerraMind v1.2",
|
| 160 |
+
rasterKind: "lulc",
|
| 161 |
+
classMix: [
|
| 162 |
+
{ k: "urban", pct: 62, color: "#C66" },
|
| 163 |
+
{ k: "water", pct: 18, color: "#5B7FB4" },
|
| 164 |
+
{ k: "vegetation", pct: 12, color: "#5B8A4A" },
|
| 165 |
+
{ k: "barren", pct: 6, color: "#A89A78" },
|
| 166 |
+
{ k: "wetland", pct: 2, color: "#D9C75A" },
|
| 167 |
+
],
|
| 168 |
+
sub: "Synthetic prior. LULC palette is a layer convention, not a tier signal.",
|
| 169 |
+
docId: "TERRAMIND-LULC-20240918", vintage: "Sentinel-2 · 2024-09-18",
|
| 170 |
+
citeId: "c-tm-lulc",
|
| 171 |
+
mapKey: "terramind-lulc",
|
| 172 |
+
},
|
| 173 |
+
"fc-nws": {
|
| 174 |
+
stone: "touchstone", tier: "empirical", variant: "scalars",
|
| 175 |
+
source: "NWS KNYC", agency: "NOAA · National Weather Service",
|
| 176 |
+
title: "Current weather, station KNYC",
|
| 177 |
+
scalars: [
|
| 178 |
+
{ value: "0.02 in", label: "precip · last 24h" },
|
| 179 |
+
{ value: "67°F", label: "temp · current" },
|
| 180 |
+
{ value: "PC", label: "conditions" },
|
| 181 |
+
],
|
| 182 |
+
sub: "Observation timestamp 2026-05-05 14:18 ET. Central Park station; not point-of-query.",
|
| 183 |
+
docId: "NWS-KNYC", vintage: "2026-05-05", citeId: "c-nws",
|
| 184 |
+
mapKey: "nws",
|
| 185 |
+
},
|
| 186 |
+
|
| 187 |
+
/* ── Lodestone ── */
|
| 188 |
+
"fc-ttm-surge": {
|
| 189 |
+
stone: "lodestone", tier: "modeled", variant: "timeseries",
|
| 190 |
+
source: "Granite TTM r2 (zero-shot)", agency: "IBM Granite-TimeSeries · regional",
|
| 191 |
+
title: "Storm surge nowcast at The Battery — 9.6 h horizon (regional)",
|
| 192 |
+
timeseries: { hours: 96, peak: { x: 38, y: 47 }, peakLabel: "+47 cm @ +38h" },
|
| 193 |
+
headline: "+47 cm", subhead: "peak surge residual · 9.6h horizon · 6-min cadence",
|
| 194 |
+
sub: "Regional disclosure. Nowcast applies city-wide via NOAA station 8518750. Distinct from the fine-tuned Battery surge nowcast.",
|
| 195 |
+
docId: "ttm_battery_surge_zeroshot", vintage: "2026-05-05 12:00 ET",
|
| 196 |
+
spatialNote: "regional · The Battery, not point-of-query",
|
| 197 |
+
citeId: "c-ttm",
|
| 198 |
+
mapKey: null,
|
| 199 |
+
},
|
| 200 |
+
"fc-ttm-surge-ft": {
|
| 201 |
+
stone: "lodestone", tier: "modeled", variant: "timeseries-ft",
|
| 202 |
+
source: "msradam/Granite-TTM-r2-Battery-Surge", agency: "Granite TTM r2 · NYC-specialized fine-tune",
|
| 203 |
+
title: "Storm surge nowcast at The Battery — 96 h horizon (NYC-specialized fine-tune)",
|
| 204 |
+
timeseries: { hours: 96, peak: { x: 38, y: 53 }, peakLabel: "+53 cm @ +38h" },
|
| 205 |
+
headline: "+53 cm", subhead: "peak surge · 96h horizon · hourly cadence",
|
| 206 |
+
sub: "Fine-tuned on NYC tide-gauge history. Trained on AMD MI300X.",
|
| 207 |
+
docId: "ttm_battery_surge_finetune", vintage: "2026-05-05 12:00 ET",
|
| 208 |
+
spatialNote: "regional · The Battery, not point-of-query",
|
| 209 |
+
hfModelCard: "huggingface.co/msradam/Granite-TTM-r2-Battery-Surge",
|
| 210 |
+
rmse: "0.157 m",
|
| 211 |
+
skillVsPersistence: "−35% vs persistence",
|
| 212 |
+
hardwareBadge: "MI300X",
|
| 213 |
+
citeId: "c-ttm-ft",
|
| 214 |
+
mapKey: null,
|
| 215 |
+
},
|
| 216 |
+
"fc-npcc4": {
|
| 217 |
+
stone: "lodestone", tier: "modeled", variant: "forecast",
|
| 218 |
+
source: "NPCC4", agency: "NYC Panel on Climate Change, 4th Assessment",
|
| 219 |
+
title: "Sea-level rise projections, Lower NY Harbor",
|
| 220 |
+
forecast: [
|
| 221 |
+
{ year: 2030, low: 4, mid: 6, high: 9 },
|
| 222 |
+
{ year: 2050, low: 13, mid: 22, high: 30 },
|
| 223 |
+
{ year: 2080, low: 28, mid: 49, high: 75 },
|
| 224 |
+
{ year: 2100, low: 38, mid: 71, high: 114 },
|
| 225 |
+
],
|
| 226 |
+
sub: "inches MSL · 17th–83rd %ile range, median line. Battery tide-gauge baseline.",
|
| 227 |
+
docId: "NPCC4-Ch3-Tbl3.2", vintage: "2024-03", citeId: "c6",
|
| 228 |
+
mapKey: null,
|
| 229 |
+
},
|
| 230 |
+
|
| 231 |
+
/* ── Capstone meta ── */
|
| 232 |
+
"fc-mellea-meta": {
|
| 233 |
+
stone: "capstone", tier: "modeled", variant: "meta",
|
| 234 |
+
source: "Mellea", agency: "Capstone synthesis · grounding check",
|
| 235 |
+
title: "Briefing reconciliation",
|
| 236 |
+
metaRows: [
|
| 237 |
+
{ k: "Mellea reroll", v: "1 reroll" },
|
| 238 |
+
{ k: "Grounding checks", v: "4 / 4 passed" },
|
| 239 |
+
{ k: "Citations resolved", v: "4" },
|
| 240 |
+
{ k: "Wall-clock", v: "24.0 s" },
|
| 241 |
+
],
|
| 242 |
+
sub: "Capstone produces prose, not cards. This meta-card summarizes the reconciler chain that wrote the four-section briefing above.",
|
| 243 |
+
docId: "RIPRAP-CAP-RH80", vintage: "2026-05-05 14:22 ET", citeId: null,
|
| 244 |
+
mapKey: null,
|
| 245 |
+
},
|
| 246 |
+
"fc-mellea-meta-bx": {
|
| 247 |
+
stone: "capstone", tier: "modeled", variant: "meta",
|
| 248 |
+
source: "Mellea", agency: "Capstone synthesis · grounding check",
|
| 249 |
+
title: "Briefing reconciliation",
|
| 250 |
+
metaRows: [
|
| 251 |
+
{ k: "Mellea reroll", v: "1 attempt" },
|
| 252 |
+
{ k: "Grounding checks", v: "4 / 4 passed" },
|
| 253 |
+
{ k: "Citations resolved",v: "6 / 6" },
|
| 254 |
+
{ k: "RAG → GLiNER", v: "5 entities · 0 unresolved" },
|
| 255 |
+
],
|
| 256 |
+
sub: "Capstone produces prose, not cards. This meta-card summarizes the reconciler chain.",
|
| 257 |
+
docId: "RIPRAP-CAP-BX12", vintage: "2026-05-05 14:24 ET", citeId: null,
|
| 258 |
+
mapKey: null,
|
| 259 |
+
},
|
| 260 |
+
};
|
| 261 |
+
|
| 262 |
+
/* Comparison card · only included when "showComparison" is on (novel variant the brief flags as v1.1 idea). */
|
| 263 |
+
const COMPARISON_CARD = {
|
| 264 |
+
stone: "keystone", tier: "synthetic", variant: "comparison",
|
| 265 |
+
source: "TerraMind × DOITT", agency: "TerraMind v1.2 Buildings × NYC DOITT footprints",
|
| 266 |
+
title: "Building footprint · documented vs. interpreted",
|
| 267 |
+
left: { tier: "empirical", label: "DOITT (documented)", value: "31.4%", aux: "112 building polygons in chip" },
|
| 268 |
+
right: { tier: "synthetic", label: "TerraMind (interpreted)", value: "36.2%", aux: "126 components · Sentinel-2 2026-05-02" },
|
| 269 |
+
delta: "+4.8 pp · model sees ~14 unrecorded structures",
|
| 270 |
+
sub: "Difference layer. v1.1 idea: surface where the foundation model sees buildings the catalogue doesn't, or vice versa. Illustrative — not part of v0.4.4 production output.",
|
| 271 |
+
docId: "RIPRAP-CMP-RH80-BLDG", vintage: "2026-05-02", citeId: null,
|
| 272 |
+
illustrative: true, mapKey: "buildings",
|
| 273 |
+
};
|
| 274 |
+
|
| 275 |
+
/* ─── Stone metadata ─── */
|
| 276 |
+
|
| 277 |
+
const STONE_META = {
|
| 278 |
+
cornerstone: { name: "Cornerstone", role: "the hazard reader", tag: "what NYC's ground remembers" },
|
| 279 |
+
keystone: { name: "Keystone", role: "the asset register", tag: "what's exposed" },
|
| 280 |
+
touchstone: { name: "Touchstone", role: "the live observer", tag: "what's happening now" },
|
| 281 |
+
lodestone: { name: "Lodestone", role: "the projector", tag: "what's coming" },
|
| 282 |
+
capstone: { name: "Capstone", role: "the synthesizer", tag: "writes it all down with citations" },
|
| 283 |
+
};
|
| 284 |
+
|
| 285 |
+
const STONE_ORDER = ["cornerstone", "keystone", "touchstone", "lodestone", "capstone"];
|
| 286 |
+
|
| 287 |
+
/* ─── Tier badge (footer) ─── */
|
| 288 |
+
|
| 289 |
+
const FiTierBadge = ({ tier }) => {
|
| 290 |
+
const map = { empirical: "EMP", modeled: "MOD", proxy: "PRX", synthetic: "SYN" };
|
| 291 |
+
return (
|
| 292 |
+
<span className={`fc-tier-badge fc-tier-badge-${tier}`} aria-label={`epistemic tier ${map[tier]}`}>
|
| 293 |
+
<window.TierGlyph tier={tier} size={9}/>
|
| 294 |
+
<span>{map[tier]}</span>
|
| 295 |
+
</span>
|
| 296 |
+
);
|
| 297 |
+
};
|
| 298 |
+
|
| 299 |
+
/* ─── Body variants ─── */
|
| 300 |
+
|
| 301 |
+
const BodyHeadline = ({ c }) => (
|
| 302 |
+
<div className="fc-body fc-body-headline">
|
| 303 |
+
<div className="fc-headline" style={{ color: `var(--tier-${c.tier})` }}>{c.headline}</div>
|
| 304 |
+
<div className="fc-subhead">{c.subhead}</div>
|
| 305 |
+
{c.body && <p className="fc-body-prose">{c.body}</p>}
|
| 306 |
+
</div>
|
| 307 |
+
);
|
| 308 |
+
|
| 309 |
+
const BodyTabular = ({ c }) => (
|
| 310 |
+
<div className="fc-body fc-body-tabular">
|
| 311 |
+
<table className="fc-table">
|
| 312 |
+
<thead><tr>{c.columns.map((h, i) => <th key={i}>{h}</th>)}</tr></thead>
|
| 313 |
+
<tbody>
|
| 314 |
+
{c.rows.map((row, i) => (
|
| 315 |
+
<tr key={i}>{row.map((cell, j) => <td key={j}>{cell}</td>)}</tr>
|
| 316 |
+
))}
|
| 317 |
+
</tbody>
|
| 318 |
+
</table>
|
| 319 |
+
{c.sub && <div className="fc-body-sub">{c.sub}</div>}
|
| 320 |
+
</div>
|
| 321 |
+
);
|
| 322 |
+
|
| 323 |
+
const BodySpark = ({ c }) => {
|
| 324 |
+
const data = c.spark || c.histogram;
|
| 325 |
+
const max = Math.max(...data, 1);
|
| 326 |
+
const w = 240, h = 38, n = data.length;
|
| 327 |
+
return (
|
| 328 |
+
<div className="fc-body fc-body-spark">
|
| 329 |
+
<div className="fc-headline" style={{ color: `var(--tier-${c.tier})` }}>{c.headline}</div>
|
| 330 |
+
<div className="fc-subhead">{c.subhead}</div>
|
| 331 |
+
<svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} preserveAspectRatio="none" aria-hidden="true">
|
| 332 |
+
{data.map((v, i) => (
|
| 333 |
+
<rect
|
| 334 |
+
key={i}
|
| 335 |
+
x={(i / n) * w + 0.5}
|
| 336 |
+
y={h - (v / max) * h}
|
| 337 |
+
width={Math.max(2, w / n - 1.5)}
|
| 338 |
+
height={(v / max) * h}
|
| 339 |
+
fill={`var(--tier-${c.tier})`}
|
| 340 |
+
/>
|
| 341 |
+
))}
|
| 342 |
+
</svg>
|
| 343 |
+
{c.sparkSub && <div className="fc-body-sub">{c.sparkSub}</div>}
|
| 344 |
+
</div>
|
| 345 |
+
);
|
| 346 |
+
};
|
| 347 |
+
|
| 348 |
+
const BodyForecast = ({ c }) => {
|
| 349 |
+
const data = c.forecast;
|
| 350 |
+
const w = 240, h = 88, pad = 6;
|
| 351 |
+
const xs = data.map((_, i) => pad + (i / (data.length - 1)) * (w - pad * 2));
|
| 352 |
+
const max = Math.max(...data.map(d => d.high));
|
| 353 |
+
const y = (v) => h - pad - (v / max) * (h - pad * 2 - 12);
|
| 354 |
+
const path = (key) => xs.map((x, i) => `${i ? "L" : "M"} ${x} ${y(data[i][key])}`).join(" ");
|
| 355 |
+
const range = xs.map((x, i) => ({ x, lo: y(data[i].low), hi: y(data[i].high) }));
|
| 356 |
+
const areaD = `M ${range.map(r => `${r.x} ${r.lo}`).join(" L ")} L ${[...range].reverse().map(r => `${r.x} ${r.hi}`).join(" L ")} Z`;
|
| 357 |
+
const color = `var(--tier-${c.tier})`;
|
| 358 |
+
return (
|
| 359 |
+
<div className="fc-body fc-body-forecast">
|
| 360 |
+
<svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} aria-hidden="true">
|
| 361 |
+
<path d={areaD} fill={color} fillOpacity="0.18"/>
|
| 362 |
+
<path d={path("mid")} fill="none" stroke={color} strokeWidth="1.5"/>
|
| 363 |
+
{data.map((d, i) => (
|
| 364 |
+
<g key={i}>
|
| 365 |
+
<circle cx={xs[i]} cy={y(d.mid)} r="2.2" fill={color}/>
|
| 366 |
+
<text x={xs[i]} y={h - 1} fontSize="9" fontFamily="IBM Plex Mono" textAnchor="middle" fill="#6B6B6B">{d.year}</text>
|
| 367 |
+
</g>
|
| 368 |
+
))}
|
| 369 |
+
</svg>
|
| 370 |
+
{c.sub && <div className="fc-body-sub">{c.sub}</div>}
|
| 371 |
+
</div>
|
| 372 |
+
);
|
| 373 |
+
};
|
| 374 |
+
|
| 375 |
+
const BodyTimeseries = ({ c }) => {
|
| 376 |
+
const w = 240, h = 84, pad = 6;
|
| 377 |
+
const hours = c.timeseries.hours;
|
| 378 |
+
/* Synthetic surge curve: harmonic baseline + storm pulse around peak */
|
| 379 |
+
const points = Array.from({ length: hours + 1 }, (_, i) => {
|
| 380 |
+
const t = i;
|
| 381 |
+
const harmonic = 6 * Math.sin((t / 12.42) * Math.PI * 2);
|
| 382 |
+
const pulse = 38 * Math.exp(-Math.pow((t - c.timeseries.peak.x) / 12, 2));
|
| 383 |
+
return { x: t, y: harmonic + pulse + 4 };
|
| 384 |
+
});
|
| 385 |
+
const maxY = Math.max(...points.map(p => p.y), c.timeseries.peak.y);
|
| 386 |
+
const minY = Math.min(...points.map(p => p.y), -10);
|
| 387 |
+
const sx = (t) => pad + (t / hours) * (w - pad * 2);
|
| 388 |
+
const sy = (v) => h - pad - 14 - ((v - minY) / (maxY - minY)) * (h - pad * 2 - 14);
|
| 389 |
+
const pathD = points.map((p, i) => `${i ? "L" : "M"} ${sx(p.x)} ${sy(p.y)}`).join(" ");
|
| 390 |
+
const color = `var(--tier-${c.tier})`;
|
| 391 |
+
return (
|
| 392 |
+
<div className="fc-body fc-body-timeseries">
|
| 393 |
+
<div className="fc-ts-header">
|
| 394 |
+
<span className="fc-headline" style={{ color }}>{c.headline}</span>
|
| 395 |
+
<span className="fc-subhead">{c.subhead}</span>
|
| 396 |
+
</div>
|
| 397 |
+
<svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} aria-hidden="true">
|
| 398 |
+
<line x1={pad} y1={sy(0)} x2={w - pad} y2={sy(0)} stroke="#C9C9C5" strokeWidth="0.5" strokeDasharray="2 2"/>
|
| 399 |
+
<path d={pathD} fill="none" stroke={color} strokeWidth="1.4"/>
|
| 400 |
+
<circle cx={sx(c.timeseries.peak.x)} cy={sy(c.timeseries.peak.y)} r="3" fill={color}/>
|
| 401 |
+
<text x={sx(c.timeseries.peak.x)} y={sy(c.timeseries.peak.y) - 6} fontSize="9" fontFamily="IBM Plex Mono" textAnchor="middle" fill={color}>{c.timeseries.peakLabel}</text>
|
| 402 |
+
<text x={pad} y={h - 2} fontSize="8" fontFamily="IBM Plex Mono" fill="#6B6B6B">now</text>
|
| 403 |
+
<text x={w - pad} y={h - 2} fontSize="8" fontFamily="IBM Plex Mono" textAnchor="end" fill="#6B6B6B">+96h</text>
|
| 404 |
+
</svg>
|
| 405 |
+
<div className="fc-body-sub">
|
| 406 |
+
<span className="fc-spatial-note">{c.spatialNote}</span>
|
| 407 |
+
<span>{c.sub}</span>
|
| 408 |
+
</div>
|
| 409 |
+
</div>
|
| 410 |
+
);
|
| 411 |
+
};
|
| 412 |
+
|
| 413 |
+
const BodyScalars = ({ c }) => (
|
| 414 |
+
<div className="fc-body fc-body-scalars">
|
| 415 |
+
<div className="fc-scalars-row">
|
| 416 |
+
{c.scalars.map((s, i) => (
|
| 417 |
+
<div key={i} className="fc-scalar-cell">
|
| 418 |
+
<div className="fc-scalar-value" style={{ color: `var(--tier-${c.tier})` }}>{s.value}</div>
|
| 419 |
+
<div className="fc-scalar-label">{s.label}</div>
|
| 420 |
+
</div>
|
| 421 |
+
))}
|
| 422 |
+
</div>
|
| 423 |
+
{c.sub && <div className="fc-body-sub">{c.sub}</div>}
|
| 424 |
+
</div>
|
| 425 |
+
);
|
| 426 |
+
|
| 427 |
+
/* Raster thumbnail · hand-drawn SVG approximations using each layer's conventional palette. */
|
| 428 |
+
const RasterThumb = ({ kind }) => {
|
| 429 |
+
const w = 240, h = 120;
|
| 430 |
+
if (kind === "stormwater") {
|
| 431 |
+
return (
|
| 432 |
+
<svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} aria-hidden="true" style={{ display: "block" }}>
|
| 433 |
+
<rect width={w} height={h} fill="#F2F2EE"/>
|
| 434 |
+
{/* street grid */}
|
| 435 |
+
<g stroke="#D9D6CC" strokeWidth="0.6">
|
| 436 |
+
<line x1="0" y1="40" x2={w} y2="40"/><line x1="0" y1="80" x2={w} y2="80"/>
|
| 437 |
+
<line x1="60" y1="0" x2="60" y2={h}/><line x1="160" y1="0" x2="160" y2={h}/>
|
| 438 |
+
</g>
|
| 439 |
+
{/* ponding */}
|
| 440 |
+
<path d="M20 50 Q 60 38 90 56 Q 120 76 150 64 Q 180 50 180 86 Q 130 100 70 96 Q 30 92 20 76 Z" fill="rgba(42,111,168,0.32)" stroke="#2A6FA8" strokeWidth="0.7"/>
|
| 441 |
+
<path d="M40 60 Q 80 54 110 70 Q 140 84 160 78 Q 165 90 130 92 Q 80 90 50 82 Z" fill="rgba(11,83,148,0.36)" stroke="#0B5394" strokeWidth="0.6"/>
|
| 442 |
+
<circle cx="120" cy="74" r="3.2" fill="#D17C00" stroke="#FAFAF7" strokeWidth="1.3"/>
|
| 443 |
+
<text x={w - 6} y={h - 5} fontSize="8" fontFamily="IBM Plex Mono" textAnchor="end" fill="#6B6B6B">2.13 in/hr · MOD</text>
|
| 444 |
+
</svg>
|
| 445 |
+
);
|
| 446 |
+
}
|
| 447 |
+
if (kind === "stormwater-dry") {
|
| 448 |
+
return (
|
| 449 |
+
<svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} aria-hidden="true" style={{ display: "block" }}>
|
| 450 |
+
<rect width={w} height={h} fill="#F2F2EE"/>
|
| 451 |
+
<g stroke="#D9D6CC" strokeWidth="0.6">
|
| 452 |
+
<line x1="0" y1="40" x2={w} y2="40"/><line x1="0" y1="80" x2={w} y2="80"/>
|
| 453 |
+
<line x1="60" y1="0" x2="60" y2={h}/><line x1="160" y1="0" x2="160" y2={h}/>
|
| 454 |
+
</g>
|
| 455 |
+
<path d="M180 92 Q 200 88 215 96 Q 220 105 200 104 Q 185 102 180 96 Z" fill="rgba(42,111,168,0.18)" stroke="#2A6FA8" strokeWidth="0.5" strokeDasharray="2 2"/>
|
| 456 |
+
<circle cx="120" cy="60" r="3.2" fill="#D17C00" stroke="#FAFAF7" strokeWidth="1.3"/>
|
| 457 |
+
<text x={w - 6} y={h - 5} fontSize="8" fontFamily="IBM Plex Mono" textAnchor="end" fill="#6B6B6B">no ponding · MOD</text>
|
| 458 |
+
</svg>
|
| 459 |
+
);
|
| 460 |
+
}
|
| 461 |
+
if (kind === "prithvi") {
|
| 462 |
+
/* Prithvi: 50% Sentinel RGB · 50% pluvial mask. Mostly dry → speckle, no flood polygons. */
|
| 463 |
+
return (
|
| 464 |
+
<svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} aria-hidden="true" style={{ display: "block" }}>
|
| 465 |
+
<defs>
|
| 466 |
+
<pattern id="s2-rgb" x="0" y="0" width="6" height="6" patternUnits="userSpaceOnUse">
|
| 467 |
+
<rect width="6" height="6" fill="#7A8E6A"/>
|
| 468 |
+
<rect x="0" y="0" width="3" height="3" fill="#8D9C7A"/>
|
| 469 |
+
<rect x="3" y="3" width="3" height="3" fill="#69795D"/>
|
| 470 |
+
</pattern>
|
| 471 |
+
</defs>
|
| 472 |
+
<rect width={w} height={h} fill="url(#s2-rgb)"/>
|
| 473 |
+
{/* roads / impervious */}
|
| 474 |
+
<rect x="0" y="55" width={w} height="6" fill="#A8A496"/>
|
| 475 |
+
<rect x="115" y="0" width="8" height={h} fill="#A8A496"/>
|
| 476 |
+
{/* tiny flood blob (0.3%) */}
|
| 477 |
+
<ellipse cx="50" cy="92" rx="6" ry="3" fill="#2A6FA8" fillOpacity="0.65"/>
|
| 478 |
+
<text x="6" y="14" fontSize="9" fontFamily="IBM Plex Mono" fill="#FAFAF7">PRITHVI · 0.3%</text>
|
| 479 |
+
<text x={w - 6} y={h - 5} fontSize="8" fontFamily="IBM Plex Mono" textAnchor="end" fill="#FAFAF7">scene 2026-05-02</text>
|
| 480 |
+
</svg>
|
| 481 |
+
);
|
| 482 |
+
}
|
| 483 |
+
if (kind === "lulc") {
|
| 484 |
+
return (
|
| 485 |
+
<svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} aria-hidden="true" style={{ display: "block" }}>
|
| 486 |
+
<rect width={w} height={h} fill="#F2F2EE"/>
|
| 487 |
+
{/* developed (red) blocks */}
|
| 488 |
+
<rect x="0" y="0" width="80" height="60" fill="#C66"/>
|
| 489 |
+
<rect x="80" y="0" width="60" height="60" fill="#C66"/>
|
| 490 |
+
<rect x="140" y="0" width="100" height="38" fill="#C66"/>
|
| 491 |
+
{/* water */}
|
| 492 |
+
<rect x="140" y="38" width="100" height="22" fill="#5B7FB4"/>
|
| 493 |
+
{/* developed lower */}
|
| 494 |
+
<rect x="0" y="60" width="100" height="60" fill="#C66"/>
|
| 495 |
+
{/* forest patch */}
|
| 496 |
+
<rect x="100" y="60" width="50" height="40" fill="#5B8A4A"/>
|
| 497 |
+
{/* herbaceous */}
|
| 498 |
+
<rect x="150" y="60" width="50" height="60" fill="#D9C75A"/>
|
| 499 |
+
<rect x="200" y="60" width="40" height="60" fill="#C66"/>
|
| 500 |
+
<rect x="100" y="100" width="50" height="20" fill="#A89A78"/>
|
| 501 |
+
<text x="6" y="14" fontSize="9" fontFamily="IBM Plex Mono" fill="#FAFAF7">LULC · TerraMind</text>
|
| 502 |
+
<text x={w - 6} y={h - 5} fontSize="8" fontFamily="IBM Plex Mono" textAnchor="end" fill="#FAFAF7">scene 2026-05-02</text>
|
| 503 |
+
</svg>
|
| 504 |
+
);
|
| 505 |
+
}
|
| 506 |
+
if (kind === "buildings") {
|
| 507 |
+
return (
|
| 508 |
+
<svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} aria-hidden="true" style={{ display: "block" }}>
|
| 509 |
+
<rect width={w} height={h} fill="#3A3A38"/>
|
| 510 |
+
{/* building polygons */}
|
| 511 |
+
{[
|
| 512 |
+
[10,10,28,18],[42,10,30,16],[78,10,40,22],[124,10,32,18],[162,10,30,18],[198,10,32,18],
|
| 513 |
+
[10,32,28,16],[42,30,30,18],[124,32,32,16],[162,32,30,16],[198,32,32,16],
|
| 514 |
+
[10,55,28,18],[42,55,30,18],[78,55,40,18],[124,55,32,18],[162,55,30,18],[198,55,32,18],
|
| 515 |
+
[10,80,28,16],[42,80,30,16],[78,80,40,16],[124,80,32,16],[162,80,30,16],
|
| 516 |
+
[10,100,28,12],[42,100,30,12],[78,100,40,12],
|
| 517 |
+
].map(([x,y,bw,bh], i) => (
|
| 518 |
+
<rect key={i} x={x} y={y} width={bw} height={bh} fill="rgba(42,111,168,0.55)" stroke="#2A6FA8" strokeWidth="0.4"/>
|
| 519 |
+
))}
|
| 520 |
+
<text x="6" y="14" fontSize="9" fontFamily="IBM Plex Mono" fill="#FAFAF7">BLDG · TerraMind</text>
|
| 521 |
+
<text x={w - 6} y={h - 5} fontSize="8" fontFamily="IBM Plex Mono" textAnchor="end" fill="#FAFAF7">36.2% built</text>
|
| 522 |
+
</svg>
|
| 523 |
+
);
|
| 524 |
+
}
|
| 525 |
+
return <div className="fc-thumb-placeholder">raster preview</div>;
|
| 526 |
+
};
|
| 527 |
+
|
| 528 |
+
const BodyRaster = ({ c }) => (
|
| 529 |
+
<div className="fc-body fc-body-raster">
|
| 530 |
+
<div className="fc-raster-frame">
|
| 531 |
+
<RasterThumb kind={c.rasterKind}/>
|
| 532 |
+
{c.illustrative && <span className="fc-illustrative" title="Illustrative rendering, not source pixels">illustrative</span>}
|
| 533 |
+
</div>
|
| 534 |
+
{c.headline && <div className="fc-raster-headline"><span style={{ color: `var(--tier-${c.tier})` }}>{c.headline}</span> · {c.subhead}</div>}
|
| 535 |
+
{c.sub && <div className="fc-body-sub">{c.sub}</div>}
|
| 536 |
+
</div>
|
| 537 |
+
);
|
| 538 |
+
|
| 539 |
+
const BodyRegister = ({ c, density }) => (
|
| 540 |
+
<div className="fc-body fc-body-register">
|
| 541 |
+
<ul className="fc-reg-list">
|
| 542 |
+
{c.registers.map((r, i) => (
|
| 543 |
+
<li key={i} className={`fc-reg-row ${r.label ? "" : "is-silent"}`}>
|
| 544 |
+
<span className="fc-reg-tag">{r.reg}</span>
|
| 545 |
+
{r.label ? (
|
| 546 |
+
<>
|
| 547 |
+
<span className="fc-reg-label" title={r.detail ? `${r.label} , ${r.detail}` : r.label}>{r.label}</span>
|
| 548 |
+
<span className="fc-reg-source">{r.sourceId}</span>
|
| 549 |
+
</>
|
| 550 |
+
) : (
|
| 551 |
+
<span className="fc-reg-silent">{r.note}</span>
|
| 552 |
+
)}
|
| 553 |
+
</li>
|
| 554 |
+
))}
|
| 555 |
+
</ul>
|
| 556 |
+
{c.sub && <div className="fc-body-sub">{c.sub}</div>}
|
| 557 |
+
</div>
|
| 558 |
+
);
|
| 559 |
+
|
| 560 |
+
/* ─── Card-grammar reference: one stub per variant ─── */
|
| 561 |
+
const GRAMMAR_STUBS = [
|
| 562 |
+
{ variant: "headline", tier: "modeled", source: "FEMA", title: "Single big number, scenario-tagged", headline: "Zone AE", subhead: "preliminary FIRM, panel ID", sub: "Use when the answer is one categorical state.", docId: "DS-HEADLINE", vintage: "spec" },
|
| 563 |
+
{ variant: "tabular", tier: "empirical", source: "USGS", title: "Small table of observations", columns: ["id", "value", "dist."], rows: [["ROW-001", "1.2 m", "0.18 mi"], ["ROW-002", "0.9 m", "0.32 mi"], ["ROW-003", "0.7 m", "0.41 mi"]], sub: "Use when 3,8 records each carry the same fields.", docId: "DS-TABULAR", vintage: "spec" },
|
| 564 |
+
{ variant: "scalars", tier: "empirical", source: "NWS", title: "Trio of scalar readings", scalars: [{ value: "0.02 in", label: "precip · 24h" }, { value: "11 mph", label: "wind" }, { value: "63°F", label: "temp" }], sub: "Use for current-state dashboards.", docId: "DS-SCALARS", vintage: "spec" },
|
| 565 |
+
{ variant: "spark", tier: "empirical", source: "FloodNet", title: "Sparkline of recent events", headline: "n events", subhead: "window · peak", spark: [1,2,4,3,7,12,8,5,3,2,4,9,6], docId: "DS-SPARK", vintage: "spec" },
|
| 566 |
+
{ variant: "histogram", tier: "proxy", source: "NYC 311", title: "Histogram of binned counts", headline: "n calls", subhead: "window · seasonal note", histogram: [3,2,1,0,1,4,7,12,18,11,5,3,4,2,1,0,2,3,8,9,4,2,1,0], docId: "DS-HIST", vintage: "spec" },
|
| 567 |
+
{ variant: "timeseries", tier: "modeled", source: "Granite TTM", title: "Forecast curve with horizon", headline: "+0.41 m peak", subhead: "+38h · 90% CI", timeseries: { hours: 96, peak: { x: 38, y: 41 }, peakLabel: "+0.41 m" }, spatialNote: "regional", sub: "Spatial-index callout when station ≠ point-of-query.", docId: "DS-TS", vintage: "spec" },
|
| 568 |
+
{ variant: "forecast", tier: "modeled", source: "NPCC4", title: "Long-horizon scenario projections", forecast: [{ year: 2030, low: 4, mid: 6, high: 9 }, { year: 2050, low: 13, mid: 22, high: 30 }, { year: 2100, low: 38, mid: 71, high: 114 }], sub: "Use for decadal+ uncertainty cones.", docId: "DS-FCST", vintage: "spec" },
|
| 569 |
+
{ variant: "raster", tier: "modeled", source: "NYC DEP", title: "Raster snapshot, mapped layer", rasterKind: "stormwater", headline: "ponding", subhead: "scenario · pixel summary", sub: "Use for any 2D model output.", docId: "DS-RASTER", vintage: "spec" },
|
| 570 |
+
{ variant: "raster-pred", tier: "modeled", source: "Prithvi-NYC", title: "Raster prediction, illustrative", rasterKind: "prithvi", headline: "n% flooded", subhead: "model · scene id", illustrative: true, sub: "Same chrome as raster + illustrative tag.", docId: "DS-RASTERPRED", vintage: "spec" },
|
| 571 |
+
{ variant: "register", tier: "empirical", source: "NYC OpenData", title: "Composite register list", registers: [
|
| 572 |
+
{ reg: "MTA", tier: "empirical", label: "Station entrance", sourceId: "MTA-X", note: null },
|
| 573 |
+
{ reg: "NYCHA", tier: "empirical", label: "Development", sourceId: "NYCHA-Y", note: null },
|
| 574 |
+
{ reg: "DOH", tier: "empirical", label: null, sourceId: null, note: "no acute-care hospital within 1.0 mi" },
|
| 575 |
+
], sub: "Use when many specialists join into one Stone.", docId: "DS-REGISTER", vintage: "spec" },
|
| 576 |
+
{ variant: "comparison", tier: "synthetic", source: "EMP × SYN", title: "Documented vs. interpreted", left: { tier: "empirical", label: "documented", value: "31.4%", aux: "n polygons" }, right: { tier: "synthetic", label: "interpreted", value: "29.8%", aux: "n polygons" }, delta: "Δ = , 1.6 pp · agreement strong", sub: "Use to surface model , ground-truth deltas.", docId: "DS-CMP", vintage: "spec" },
|
| 577 |
+
{ variant: "meta", tier: "modeled", source: "Mellea", title: "Capstone reconciliation", metaRows: [{ k: "claims", v: "12 / 12 grounded" }, { k: "tier mix", v: "EMP 5 · MOD 4 · PRX 2 · SYN 1" }, { k: "tier-1 freshness", v: "median 38 d" }, { k: "warnings", v: "0" }], sub: "Use to expose the synthesis layer's audit.", docId: "DS-META", vintage: "spec" },
|
| 578 |
+
];
|
| 579 |
+
|
| 580 |
+
const CardGrammarReference = ({ density }) => (
|
| 581 |
+
<section className="f-region f-region-grammar" aria-label="Card grammar reference">
|
| 582 |
+
<header className="f-region-head">
|
| 583 |
+
<div className="f-region-head-left">
|
| 584 |
+
<span className="f-region-num">SPEC</span>
|
| 585 |
+
<h3 className="f-region-name">Card grammar</h3>
|
| 586 |
+
<span className="f-region-role">every body variant in the system</span>
|
| 587 |
+
<span className="f-region-tag">stubs, not findings</span>
|
| 588 |
+
</div>
|
| 589 |
+
<span className="f-tally"><span className="f-tally-strong">{GRAMMAR_STUBS.length}</span> variants</span>
|
| 590 |
+
</header>
|
| 591 |
+
<div className="f-rail">
|
| 592 |
+
{GRAMMAR_STUBS.map((c) => (
|
| 593 |
+
<article key={c.variant} className={`fc fc-${c.variant} fc-tier-${c.tier} ${density === "compact" ? "is-compact" : ""}`}>
|
| 594 |
+
<header className="fc-head">
|
| 595 |
+
<div className="fc-head-source">
|
| 596 |
+
<window.TierGlyph tier={c.tier} size={11}/>
|
| 597 |
+
<span className="fc-head-source-label">{c.source}</span>
|
| 598 |
+
</div>
|
| 599 |
+
<span className="fc-head-vintage">{c.variant}</span>
|
| 600 |
+
</header>
|
| 601 |
+
<h4 className="fc-title">{c.title}</h4>
|
| 602 |
+
{renderBody(c, density)}
|
| 603 |
+
<footer className="fc-foot">
|
| 604 |
+
<span className="fc-foot-docid fc-foot-docid-mute">{c.docId}</span>
|
| 605 |
+
<FiTierBadge tier={c.tier}/>
|
| 606 |
+
</footer>
|
| 607 |
+
</article>
|
| 608 |
+
))}
|
| 609 |
+
</div>
|
| 610 |
+
</section>
|
| 611 |
+
);
|
| 612 |
+
|
| 613 |
+
Object.assign(window, { CardGrammarReference });
|
| 614 |
+
|
| 615 |
+
const BodyComparison = ({ c }) => (
|
| 616 |
+
<div className="fc-body fc-body-comparison">
|
| 617 |
+
<div className="fc-cmp-grid">
|
| 618 |
+
<div className="fc-cmp-cell">
|
| 619 |
+
<div className="fc-cmp-cell-tier">
|
| 620 |
+
<window.TierGlyph tier={c.left.tier} size={10}/>
|
| 621 |
+
<span className="fc-cmp-cell-label">{c.left.label}</span>
|
| 622 |
+
</div>
|
| 623 |
+
<div className="fc-cmp-cell-value" style={{ color: `var(--tier-${c.left.tier})` }}>{c.left.value}</div>
|
| 624 |
+
<div className="fc-cmp-cell-aux">{c.left.aux}</div>
|
| 625 |
+
</div>
|
| 626 |
+
<div className="fc-cmp-divider" aria-hidden="true">vs</div>
|
| 627 |
+
<div className="fc-cmp-cell">
|
| 628 |
+
<div className="fc-cmp-cell-tier">
|
| 629 |
+
<window.TierGlyph tier={c.right.tier} size={10}/>
|
| 630 |
+
<span className="fc-cmp-cell-label">{c.right.label}</span>
|
| 631 |
+
</div>
|
| 632 |
+
<div className="fc-cmp-cell-value" style={{ color: `var(--tier-${c.right.tier})` }}>{c.right.value}</div>
|
| 633 |
+
<div className="fc-cmp-cell-aux">{c.right.aux}</div>
|
| 634 |
+
</div>
|
| 635 |
+
</div>
|
| 636 |
+
<div className="fc-cmp-delta">{c.delta}</div>
|
| 637 |
+
{c.sub && <div className="fc-body-sub">{c.sub}</div>}
|
| 638 |
+
</div>
|
| 639 |
+
);
|
| 640 |
+
|
| 641 |
+
const BodyMeta = ({ c }) => (
|
| 642 |
+
<div className="fc-body fc-body-meta">
|
| 643 |
+
<dl className="fc-meta-list">
|
| 644 |
+
{c.metaRows.map((r, i) => (
|
| 645 |
+
<div key={i} className="fc-meta-row">
|
| 646 |
+
<dt>{r.k}</dt>
|
| 647 |
+
<dd>{r.v}</dd>
|
| 648 |
+
</div>
|
| 649 |
+
))}
|
| 650 |
+
</dl>
|
| 651 |
+
{c.sub && <div className="fc-body-sub">{c.sub}</div>}
|
| 652 |
+
</div>
|
| 653 |
+
);
|
| 654 |
+
|
| 655 |
+
const renderBody = (c, density) => {
|
| 656 |
+
switch (c.variant) {
|
| 657 |
+
case "headline": return <BodyHeadline c={c}/>;
|
| 658 |
+
case "tabular": return <BodyTabular c={c}/>;
|
| 659 |
+
case "spark": return <BodySpark c={c}/>;
|
| 660 |
+
case "histogram": return <BodySpark c={c}/>;
|
| 661 |
+
case "forecast": return <BodyForecast c={c}/>;
|
| 662 |
+
case "timeseries": return <BodyTimeseries c={c}/>;
|
| 663 |
+
case "scalars": return <BodyScalars c={c}/>;
|
| 664 |
+
case "raster": return <BodyRaster c={c}/>;
|
| 665 |
+
case "raster-pred": return <BodyRaster c={c}/>;
|
| 666 |
+
case "register": return <BodyRegister c={c} density={density}/>;
|
| 667 |
+
case "comparison": return <BodyComparison c={c}/>;
|
| 668 |
+
case "meta": return <BodyMeta c={c}/>;
|
| 669 |
+
default: return null;
|
| 670 |
+
}
|
| 671 |
+
};
|
| 672 |
+
|
| 673 |
+
/* ─── Card frame ─── */
|
| 674 |
+
|
| 675 |
+
const FindingCard = ({ c, density, onCite, onHover, onClick, isLinked }) => {
|
| 676 |
+
return (
|
| 677 |
+
<article
|
| 678 |
+
className={`fc fc-${c.variant} fc-tier-${c.tier} ${density === "compact" ? "is-compact" : ""} ${isLinked ? "is-linked" : ""}`}
|
| 679 |
+
aria-labelledby={`fc-${c.docId}-title`}
|
| 680 |
+
onMouseEnter={() => onHover?.(c.mapKey)}
|
| 681 |
+
onMouseLeave={() => onHover?.(null)}
|
| 682 |
+
onClick={() => onClick?.(c)}
|
| 683 |
+
>
|
| 684 |
+
<header className="fc-head">
|
| 685 |
+
<div className="fc-head-source">
|
| 686 |
+
<window.TierGlyph tier={c.tier} size={11}/>
|
| 687 |
+
<span className="fc-head-source-label" title={c.agency}>{c.source}</span>
|
| 688 |
+
</div>
|
| 689 |
+
<span className="fc-head-vintage">v. {c.vintage}</span>
|
| 690 |
+
</header>
|
| 691 |
+
<h4 id={`fc-${c.docId}-title`} className="fc-title">{c.title}</h4>
|
| 692 |
+
{renderBody(c, density)}
|
| 693 |
+
<footer className="fc-foot">
|
| 694 |
+
{c.citeId ? (
|
| 695 |
+
<button type="button" className="fc-foot-cite" onClick={(e) => { e.stopPropagation(); onCite?.(c.citeId); }} title={`Open ${c.docId} in citation drawer`}>
|
| 696 |
+
<span className="fc-foot-docid">{c.docId}</span>
|
| 697 |
+
<span className="fc-foot-arrow" aria-hidden="true">→</span>
|
| 698 |
+
</button>
|
| 699 |
+
) : (
|
| 700 |
+
<span className="fc-foot-docid fc-foot-docid-mute">{c.docId}</span>
|
| 701 |
+
)}
|
| 702 |
+
<FiTierBadge tier={c.tier}/>
|
| 703 |
+
</footer>
|
| 704 |
+
</article>
|
| 705 |
+
);
|
| 706 |
+
};
|
| 707 |
+
|
| 708 |
+
/* ─── Stone region ─── */
|
| 709 |
+
|
| 710 |
+
const flatten = (members) => members.flatMap((m) => (m.children ? [m, ...flatten(m.children)] : [m]));
|
| 711 |
+
|
| 712 |
+
const StoneTally44 = ({ cardCount, members }) => {
|
| 713 |
+
const flat = flatten(members);
|
| 714 |
+
const fired = flat.filter((m) => m.status === "fired" || m.status === "warned").length;
|
| 715 |
+
const silent = flat.filter((m) => m.status === "silent_by_design").length;
|
| 716 |
+
const warn = flat.filter((m) => m.status === "warned").length;
|
| 717 |
+
const error = flat.filter((m) => m.status === "errored").length;
|
| 718 |
+
const notInvoked = flat.filter((m) => m.status === "not_invoked").length;
|
| 719 |
+
const ms = members.reduce((acc, m) => Math.max(acc, m.ms || 0), 0);
|
| 720 |
+
const fmtMs = (x) => (x === 0 ? "—" : x < 1000 ? x + "ms" : (x / 1000).toFixed(1) + "s");
|
| 721 |
+
return (
|
| 722 |
+
<span className="f-tally">
|
| 723 |
+
<span className="f-tally-cards">{cardCount} card{cardCount === 1 ? "" : "s"}</span>
|
| 724 |
+
<span className="f-tally-sep">·</span>
|
| 725 |
+
<span className="f-tally-fired"><span className="f-tally-strong">{fired}</span> fired</span>
|
| 726 |
+
{silent > 0 && <><span className="f-tally-sep">·</span><span className="f-tally-silent"><span className="f-tally-strong">{silent}</span> silent</span></>}
|
| 727 |
+
{warn > 0 && <><span className="f-tally-sep">·</span><span className="f-tally-warn"><span className="f-tally-strong">{warn}</span> warn</span></>}
|
| 728 |
+
{error > 0 && <><span className="f-tally-sep">·</span><span className="f-tally-err"><span className="f-tally-strong">{error}</span> errored</span></>}
|
| 729 |
+
{notInvoked > 0 && <><span className="f-tally-sep">·</span><span className="f-tally-notinvoked"><span className="f-tally-strong">{notInvoked}</span> not invoked</span></>}
|
| 730 |
+
<span className="f-tally-sep">·</span>
|
| 731 |
+
<span className="f-tally-ms"><span className="f-tally-strong">{fmtMs(ms)}</span></span>
|
| 732 |
+
</span>
|
| 733 |
+
);
|
| 734 |
+
};
|
| 735 |
+
|
| 736 |
+
const StoneRegion = ({ stone, cardIds, density, provenanceMode, onCite, onHover, linkedKey }) => {
|
| 737 |
+
const meta = STONE_META[stone.key];
|
| 738 |
+
const cards = cardIds.map((id) => CARDS[id]).filter(Boolean);
|
| 739 |
+
const traceCount = flatten(stone.members).length;
|
| 740 |
+
const flat = flatten(stone.members);
|
| 741 |
+
const hasError = flat.some((m) => m.status === "errored");
|
| 742 |
+
const hasWarn = flat.some((m) => m.status === "warned");
|
| 743 |
+
const defaultOpen =
|
| 744 |
+
provenanceMode === "all-expanded" ? true :
|
| 745 |
+
provenanceMode === "all-collapsed" ? false :
|
| 746 |
+
/* smart */ hasError || hasWarn;
|
| 747 |
+
const [traceOpen, setTraceOpen] = useFi(defaultOpen);
|
| 748 |
+
/* Re-sync if user toggles tweak */
|
| 749 |
+
useFiMemo(() => setTraceOpen(defaultOpen), [provenanceMode]);
|
| 750 |
+
|
| 751 |
+
const isCapstone = stone.key === "capstone";
|
| 752 |
+
|
| 753 |
+
return (
|
| 754 |
+
<section className={`f-region f-region-${stone.key}`} aria-labelledby={`f-h-${stone.key}`} data-stone={stone.key}>
|
| 755 |
+
<header className="f-region-head">
|
| 756 |
+
<div className="f-region-head-left">
|
| 757 |
+
<span className="f-region-num">{(STONE_ORDER.indexOf(stone.key) + 1).toString().padStart(2, "0")}</span>
|
| 758 |
+
<h3 id={`f-h-${stone.key}`} className="f-region-name">{meta.name}</h3>
|
| 759 |
+
<span className="f-region-role">· {meta.role}</span>
|
| 760 |
+
<span className="f-region-tag">{meta.tag}</span>
|
| 761 |
+
</div>
|
| 762 |
+
<StoneTally44 cardCount={cards.length} members={stone.members}/>
|
| 763 |
+
</header>
|
| 764 |
+
|
| 765 |
+
{/* Findings · primary surface */}
|
| 766 |
+
{cards.length === 0 ? (
|
| 767 |
+
<div className="f-silent">
|
| 768 |
+
<span className="f-silent-tag">silent</span>
|
| 769 |
+
<p className="f-silent-prose">
|
| 770 |
+
{stone.key === "lodestone"
|
| 771 |
+
? "No projection cards landed for this query. The address is inland (Pelham Pkwy, Bronx); NPCC4 SLR and TTM Battery surge are coastal projections and do not localize here. Atomic functions still ran (see provenance) and returned silence rather than confabulation."
|
| 772 |
+
: "No cards for this Stone on this query."}
|
| 773 |
+
</p>
|
| 774 |
+
</div>
|
| 775 |
+
) : (
|
| 776 |
+
<div className={`f-rail ${isCapstone ? "f-rail-capstone" : ""}`}>
|
| 777 |
+
{cards.map((c) => (
|
| 778 |
+
<FindingCard
|
| 779 |
+
key={c.docId}
|
| 780 |
+
c={c}
|
| 781 |
+
density={density}
|
| 782 |
+
onCite={onCite}
|
| 783 |
+
onHover={onHover}
|
| 784 |
+
onClick={(card) => onHover?.(card.mapKey, true)}
|
| 785 |
+
isLinked={linkedKey && c.mapKey === linkedKey}
|
| 786 |
+
/>
|
| 787 |
+
))}
|
| 788 |
+
</div>
|
| 789 |
+
)}
|
| 790 |
+
|
| 791 |
+
{/* Provenance */}
|
| 792 |
+
<div className="f-prov">
|
| 793 |
+
<button
|
| 794 |
+
type="button"
|
| 795 |
+
className="f-prov-toggle"
|
| 796 |
+
aria-expanded={traceOpen}
|
| 797 |
+
onClick={() => setTraceOpen((o) => !o)}
|
| 798 |
+
>
|
| 799 |
+
<span className="f-prov-caret" aria-hidden="true">{traceOpen ? "▾" : "▸"}</span>
|
| 800 |
+
<span className="f-prov-label">{traceOpen ? "Hide" : "Show"} provenance</span>
|
| 801 |
+
<span className="f-prov-meta">· {traceCount} function{traceCount === 1 ? "" : "s"}{hasError ? " · errored" : hasWarn ? " · warned" : ""}</span>
|
| 802 |
+
</button>
|
| 803 |
+
{traceOpen && (
|
| 804 |
+
<div className="f-prov-body">
|
| 805 |
+
{stone.members.map((m) => <window.TraceRow key={m.id} m={m}/>)}
|
| 806 |
+
</div>
|
| 807 |
+
)}
|
| 808 |
+
</div>
|
| 809 |
+
</section>
|
| 810 |
+
);
|
| 811 |
+
};
|
| 812 |
+
|
| 813 |
+
/* ─── Run-health strip ─── */
|
| 814 |
+
|
| 815 |
+
const RunHealth44 = ({ totalCards }) => {
|
| 816 |
+
const all = window.STONES.flatMap((s) => flatten(s.members));
|
| 817 |
+
const fired = all.filter((m) => m.status === "fired" || m.status === "warned").length;
|
| 818 |
+
const total = all.length;
|
| 819 |
+
const silent = all.filter((m) => m.status === "silent_by_design").length;
|
| 820 |
+
const warn = all.filter((m) => m.status === "warned").length;
|
| 821 |
+
const error = all.filter((m) => m.status === "errored").length;
|
| 822 |
+
return (
|
| 823 |
+
<div className="f-runhealth">
|
| 824 |
+
<span className="f-rh-item"><strong>5</strong> Stones</span>
|
| 825 |
+
<span className="f-rh-sep">·</span>
|
| 826 |
+
<span className="f-rh-item"><strong>{fired}/{total}</strong> functions fired</span>
|
| 827 |
+
<span className="f-rh-sep">·</span>
|
| 828 |
+
<span className="f-rh-item"><strong>{totalCards}</strong> evidence cards</span>
|
| 829 |
+
<span className="f-rh-sep">·</span>
|
| 830 |
+
<span className="f-rh-item"><strong>24.0s</strong> wall-clock</span>
|
| 831 |
+
{silent > 0 && <><span className="f-rh-sep">·</span><span className="f-rh-item f-rh-silent">{silent} silent</span></>}
|
| 832 |
+
{warn > 0 && <><span className="f-rh-sep">·</span><span className="f-rh-item f-rh-warn">{warn} warned</span></>}
|
| 833 |
+
{error > 0 && <><span className="f-rh-sep">·</span><span className="f-rh-item f-rh-err">{error} errored</span></>}
|
| 834 |
+
</div>
|
| 835 |
+
);
|
| 836 |
+
};
|
| 837 |
+
|
| 838 |
+
/* ─── Findings region ─── */
|
| 839 |
+
|
| 840 |
+
const FindingsRegion = ({ density, provenanceMode, queryKey, showComparison, showGrammar, onCite, onHover, linkedKey }) => {
|
| 841 |
+
const map = CARDS_BY_QUERY[queryKey] || CARDS_BY_QUERY.redhook;
|
| 842 |
+
/* Inject the comparison card into Keystone after the register card if showComparison is on */
|
| 843 |
+
const adjusted = useFiMemo(() => {
|
| 844 |
+
if (!showComparison) return map;
|
| 845 |
+
if (queryKey !== "redhook") return map;
|
| 846 |
+
/* Add a synthetic ID reference to the in-memory comparison card */
|
| 847 |
+
return { ...map, keystone: [...(map.keystone || []), "__comparison__"] };
|
| 848 |
+
}, [map, showComparison, queryKey]);
|
| 849 |
+
|
| 850 |
+
const totalCards = STONE_ORDER.reduce((n, k) => n + (adjusted[k] || []).length, 0);
|
| 851 |
+
|
| 852 |
+
return (
|
| 853 |
+
<section className="findings" aria-label="Findings, grouped by Stone">
|
| 854 |
+
<header className="findings-head">
|
| 855 |
+
<h2 className="findings-h2">Findings · grouped by Stone</h2>
|
| 856 |
+
<span className="findings-tagline">cards = what each Stone found · provenance collapses below</span>
|
| 857 |
+
</header>
|
| 858 |
+
<RunHealth44 totalCards={totalCards}/>
|
| 859 |
+
{STONE_ORDER.map((key) => {
|
| 860 |
+
const stone = window.STONES.find((s) => s.key === key);
|
| 861 |
+
const ids = adjusted[key] || [];
|
| 862 |
+
return (
|
| 863 |
+
<StoneRegion
|
| 864 |
+
key={key}
|
| 865 |
+
stone={stone}
|
| 866 |
+
cardIds={ids}
|
| 867 |
+
density={density}
|
| 868 |
+
provenanceMode={provenanceMode}
|
| 869 |
+
onCite={onCite}
|
| 870 |
+
onHover={onHover}
|
| 871 |
+
linkedKey={linkedKey}
|
| 872 |
+
/>
|
| 873 |
+
);
|
| 874 |
+
})}
|
| 875 |
+
{showGrammar && <CardGrammarReference density={density}/>}
|
| 876 |
+
</section>
|
| 877 |
+
);
|
| 878 |
+
};
|
| 879 |
+
|
| 880 |
+
/* Fold the comparison card into CARDS lookup at module load */
|
| 881 |
+
CARDS["__comparison__"] = COMPARISON_CARD;
|
| 882 |
+
|
| 883 |
+
Object.assign(window, { FindingsRegion, CARDS, CARDS_BY_QUERY });
|
docs/design_handoff/design_files/glyphs.jsx
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Epistemic-tier glyphs.
|
| 2 |
+
12×12 monochrome SVGs. Distinguishable by shape (filled square,
|
| 3 |
+
open square, filled circle, striped square) , never by color alone.
|
| 4 |
+
Used in: briefing prose left margin, evidence card badge, trace tier
|
| 5 |
+
column, PDF body, MapLibre legend.
|
| 6 |
+
*/
|
| 7 |
+
|
| 8 |
+
const TierGlyph = ({ tier, size = 12, color = "currentColor", title }) => {
|
| 9 |
+
const s = size;
|
| 10 |
+
const stroke = Math.max(1, Math.round(size / 9));
|
| 11 |
+
const ariaTitle = title || ({
|
| 12 |
+
empirical: "Empirical: directly measured or observed",
|
| 13 |
+
modeled: "Modeled: scenario-based prediction",
|
| 14 |
+
proxy: "Proxy: indirect indicator",
|
| 15 |
+
synthetic: "Synthetic prior: generated, not observed",
|
| 16 |
+
})[tier];
|
| 17 |
+
|
| 18 |
+
const patternId = `rip-stripe-${tier}-${size}`;
|
| 19 |
+
|
| 20 |
+
return (
|
| 21 |
+
<svg
|
| 22 |
+
width={s}
|
| 23 |
+
height={s}
|
| 24 |
+
viewBox={`0 0 ${s} ${s}`}
|
| 25 |
+
role="img"
|
| 26 |
+
aria-label={ariaTitle}
|
| 27 |
+
style={{ flex: "none", display: "inline-block", verticalAlign: "-0.12em" }}
|
| 28 |
+
>
|
| 29 |
+
<title>{ariaTitle}</title>
|
| 30 |
+
{tier === "empirical" && (
|
| 31 |
+
<rect x="0" y="0" width={s} height={s} fill={color} />
|
| 32 |
+
)}
|
| 33 |
+
{tier === "modeled" && (
|
| 34 |
+
<rect
|
| 35 |
+
x={stroke / 2}
|
| 36 |
+
y={stroke / 2}
|
| 37 |
+
width={s - stroke}
|
| 38 |
+
height={s - stroke}
|
| 39 |
+
fill="none"
|
| 40 |
+
stroke={color}
|
| 41 |
+
strokeWidth={stroke}
|
| 42 |
+
/>
|
| 43 |
+
)}
|
| 44 |
+
{tier === "proxy" && (
|
| 45 |
+
<circle cx={s / 2} cy={s / 2} r={s / 2 - 0.5} fill={color} />
|
| 46 |
+
)}
|
| 47 |
+
{tier === "synthetic" && (
|
| 48 |
+
<>
|
| 49 |
+
<defs>
|
| 50 |
+
<pattern
|
| 51 |
+
id={patternId}
|
| 52 |
+
width="3"
|
| 53 |
+
height="3"
|
| 54 |
+
patternUnits="userSpaceOnUse"
|
| 55 |
+
patternTransform="rotate(45)"
|
| 56 |
+
>
|
| 57 |
+
<line x1="0" y1="0" x2="0" y2="3" stroke={color} strokeWidth="1.5" />
|
| 58 |
+
</pattern>
|
| 59 |
+
</defs>
|
| 60 |
+
<rect
|
| 61 |
+
x={stroke / 2}
|
| 62 |
+
y={stroke / 2}
|
| 63 |
+
width={s - stroke}
|
| 64 |
+
height={s - stroke}
|
| 65 |
+
fill={`url(#${patternId})`}
|
| 66 |
+
stroke={color}
|
| 67 |
+
strokeWidth={stroke}
|
| 68 |
+
/>
|
| 69 |
+
</>
|
| 70 |
+
)}
|
| 71 |
+
</svg>
|
| 72 |
+
);
|
| 73 |
+
};
|
| 74 |
+
|
| 75 |
+
const TIER_META = {
|
| 76 |
+
empirical: {
|
| 77 |
+
label: "Empirical",
|
| 78 |
+
short: "EMP",
|
| 79 |
+
desc: "Directly measured or observed",
|
| 80 |
+
examples: "USGS high-water marks · FloodNet sensors · Sandy Inundation Zone",
|
| 81 |
+
},
|
| 82 |
+
modeled: {
|
| 83 |
+
label: "Modeled",
|
| 84 |
+
short: "MOD",
|
| 85 |
+
desc: "Scenario-based prediction",
|
| 86 |
+
examples: "FEMA flood zones · DEP stormwater scenarios · NPCC4 SLR",
|
| 87 |
+
},
|
| 88 |
+
proxy: {
|
| 89 |
+
label: "Proxy",
|
| 90 |
+
short: "PRX",
|
| 91 |
+
desc: "Indirect indicator",
|
| 92 |
+
examples: "311 flood complaints · NFIP claims · terrain indices",
|
| 93 |
+
},
|
| 94 |
+
synthetic: {
|
| 95 |
+
label: "Synthetic prior",
|
| 96 |
+
short: "SYN",
|
| 97 |
+
desc: "Generated, not observed",
|
| 98 |
+
examples: "TerraMind land-cover · synthetic SAR for occluded days",
|
| 99 |
+
},
|
| 100 |
+
};
|
| 101 |
+
|
| 102 |
+
const TierBadge = ({ tier, compact = false }) => {
|
| 103 |
+
const meta = TIER_META[tier];
|
| 104 |
+
return (
|
| 105 |
+
<span
|
| 106 |
+
className={`tier-badge tier-badge-${tier}`}
|
| 107 |
+
style={{
|
| 108 |
+
display: "inline-flex",
|
| 109 |
+
alignItems: "center",
|
| 110 |
+
gap: 6,
|
| 111 |
+
fontFamily: "var(--font-mono)",
|
| 112 |
+
fontSize: 11,
|
| 113 |
+
letterSpacing: "0.08em",
|
| 114 |
+
textTransform: "uppercase",
|
| 115 |
+
color: `var(--tier-${tier})`,
|
| 116 |
+
fontWeight: 500,
|
| 117 |
+
}}
|
| 118 |
+
title={meta.desc}
|
| 119 |
+
>
|
| 120 |
+
<TierGlyph tier={tier} size={10} color={`var(--tier-${tier})`} />
|
| 121 |
+
{compact ? meta.short : meta.label}
|
| 122 |
+
</span>
|
| 123 |
+
);
|
| 124 |
+
};
|
| 125 |
+
|
| 126 |
+
Object.assign(window, { TierGlyph, TierBadge, TIER_META });
|
docs/design_handoff/design_files/landing-variants.css
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Riprap landing-page variants , shared styles */
|
| 2 |
+
|
| 3 |
+
/* Chrome */
|
| 4 |
+
.land { background: var(--paper); color: var(--ink); font-family: var(--font-sans); display: flex; flex-direction: column; min-height: 100%; }
|
| 5 |
+
.land-header { display: flex; align-items: baseline; gap: 12px; padding: 20px 32px; border-bottom: 1px solid var(--rule-soft); }
|
| 6 |
+
.land-header .riprap-wordmark { font-family: var(--font-serif); font-weight: 600; font-size: 18px; letter-spacing: 0.02em; }
|
| 7 |
+
.land-header-sep { color: var(--ink-tertiary); }
|
| 8 |
+
.land-header-context { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-secondary); }
|
| 9 |
+
.land-header-nav { margin-left: auto; display: flex; gap: 18px; font-family: var(--font-mono); font-size: 12px; }
|
| 10 |
+
.land-header-nav a { color: var(--ink-secondary); text-decoration: none; border-bottom: 1px dotted transparent; }
|
| 11 |
+
.land-header-nav a:hover { border-bottom-color: var(--ink-secondary); }
|
| 12 |
+
|
| 13 |
+
.land-footer { margin-top: auto; display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap; padding: 16px 32px; border-top: 1px solid var(--rule-soft); font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.02em; }
|
| 14 |
+
.land-footer-tiers { display: flex; gap: 16px; flex-wrap: wrap; }
|
| 15 |
+
.land-footer-tier { display: inline-flex; align-items: center; gap: 5px; }
|
| 16 |
+
.lm-sw-syn { background: rgba(42,111,168,0.25); border: 1px dashed var(--tier-modeled); }
|
| 17 |
+
|
| 18 |
+
/* Hero common */
|
| 19 |
+
.land-hero { padding: 64px 32px 48px; }
|
| 20 |
+
.land-hero-h1 { display: flex; flex-direction: column; gap: 18px; margin: 0 0 30px; max-width: 880px; }
|
| 21 |
+
.land-hero-headline { font-family: var(--font-serif); font-weight: 500; font-size: 52px; line-height: 1.08; color: var(--ink); letter-spacing: -0.015em; }
|
| 22 |
+
.land-hero-headline em { font-style: italic; font-weight: 500; }
|
| 23 |
+
.land-hero-deck { font-family: var(--font-serif); font-size: 18px; line-height: 1.55; color: var(--ink-secondary); max-width: 64ch; }
|
| 24 |
+
|
| 25 |
+
/* Query box */
|
| 26 |
+
.land-query { display: flex; align-items: stretch; gap: 0; max-width: 760px; border: 1px solid var(--ink); background: white; }
|
| 27 |
+
.land-query-lg { font-size: 18px; }
|
| 28 |
+
.land-query-md { font-size: 16px; max-width: 640px; }
|
| 29 |
+
.land-query-prompt { display: flex; align-items: center; padding: 0 14px; font-family: var(--font-mono); font-size: 22px; color: var(--ink-tertiary); background: var(--paper-deep); border-right: 1px solid var(--rule-soft); }
|
| 30 |
+
.land-query-input { flex: 1; min-width: 0; padding: 18px 16px; font: inherit; font-family: var(--font-sans); border: none; outline: none; background: white; color: var(--ink); }
|
| 31 |
+
.land-query-input::placeholder { color: var(--ink-tertiary); }
|
| 32 |
+
.land-query-submit { padding: 0 22px; font-family: var(--font-sans); font-weight: 600; font-size: 14px; background: var(--ink); color: var(--paper); border: none; cursor: pointer; white-space: nowrap; letter-spacing: 0.02em; }
|
| 33 |
+
.land-query-submit:hover { background: #000; }
|
| 34 |
+
|
| 35 |
+
/* Sections */
|
| 36 |
+
.land-section { padding: 48px 32px; border-top: 1px solid var(--rule-soft); }
|
| 37 |
+
.land-section-head { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; margin-bottom: 22px; padding-bottom: 10px; border-bottom: 1px solid var(--rule-soft); }
|
| 38 |
+
.land-section-meta { font-family: var(--font-serif); font-style: italic; font-size: 14px; color: var(--ink-tertiary); }
|
| 39 |
+
.land-section-link { font-family: var(--font-mono); font-size: 12px; color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--ink); }
|
| 40 |
+
|
| 41 |
+
/* ── v1 ── */
|
| 42 |
+
.land-cycling { margin-top: 18px; display: grid; grid-template-columns: auto 1fr; align-items: baseline; column-gap: 10px; font-family: var(--font-mono); font-size: 13px; color: var(--ink-tertiary); max-width: 760px; }
|
| 43 |
+
.land-cycling-label { letter-spacing: 0.06em; text-transform: uppercase; font-size: 11px; line-height: 1.4em; }
|
| 44 |
+
.land-cycling-rail { position: relative; min-width: 0; height: 1.4em; line-height: 1.4em; }
|
| 45 |
+
.land-cycling-item { position: absolute; inset: 0; line-height: 1.4em; opacity: 0; transition: opacity 240ms ease; color: var(--ink); border-bottom: 1px dotted var(--rule-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
| 46 |
+
.land-cycling-item.is-active { opacity: 1; }
|
| 47 |
+
|
| 48 |
+
.land-preview { display: flex; justify-content: flex-start; }
|
| 49 |
+
.land-preview-frame { background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); padding: 22px 26px; max-width: 760px; }
|
| 50 |
+
.land-preview-eyebrow { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-tertiary); margin-bottom: 12px; }
|
| 51 |
+
.land-preview-body { font-family: var(--font-serif); font-size: 17px; line-height: 1.65; color: var(--ink); margin: 0 0 18px; }
|
| 52 |
+
.land-preview-cite { background: linear-gradient(transparent 60%, rgba(11,83,148,0.14) 60%); }
|
| 53 |
+
.land-preview-cite sup { font-family: var(--font-mono); font-size: 10px; color: var(--tier-empirical); margin-left: 2px; vertical-align: super; }
|
| 54 |
+
.land-preview-cites { display: flex; flex-direction: column; gap: 6px; padding-top: 14px; border-top: 1px dashed var(--rule-soft); }
|
| 55 |
+
.land-preview-cite-row { display: grid; grid-template-columns: 36px 1fr 90px; gap: 10px; align-items: baseline; font-family: var(--font-mono); font-size: 12px; }
|
| 56 |
+
.land-preview-cite-pin { color: var(--tier-empirical); font-weight: 600; }
|
| 57 |
+
.land-preview-cite-src { color: var(--ink); }
|
| 58 |
+
.land-preview-cite-tier { color: var(--ink-tertiary); font-size: 11px; text-align: right; letter-spacing: 0.04em; }
|
| 59 |
+
|
| 60 |
+
/* ── v2 ── */
|
| 61 |
+
.land-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 14px; }
|
| 62 |
+
.land-gallery-card { display: flex; flex-direction: column; gap: 6px; padding: 18px 20px; background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); text-decoration: none; color: inherit; transition: background 120ms; }
|
| 63 |
+
.land-gallery-card:hover { background: var(--paper-deep); }
|
| 64 |
+
.land-gallery-kind { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-tertiary); }
|
| 65 |
+
.land-gallery-title { font-family: var(--font-serif); font-size: 22px; font-weight: 500; margin: 0; color: var(--ink); }
|
| 66 |
+
.land-gallery-sub { font-family: var(--font-serif); font-size: 14px; font-style: italic; color: var(--ink-secondary); margin: 0; line-height: 1.45; }
|
| 67 |
+
.land-gallery-tally { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--rule-soft); font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); }
|
| 68 |
+
.land-gallery-arrow { margin-top: 6px; font-family: var(--font-mono); font-size: 11px; color: var(--ink); letter-spacing: 0.04em; }
|
| 69 |
+
|
| 70 |
+
.land-section-stones { background: var(--paper-deep); }
|
| 71 |
+
.land-stones-strip { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; }
|
| 72 |
+
.land-stone-pill { padding: 14px 16px; background: white; border: 1px solid var(--rule-soft); font-family: var(--font-sans); font-size: 13px; color: var(--ink-secondary); }
|
| 73 |
+
.land-stone-pill strong { display: block; font-family: var(--font-sans); font-weight: 600; font-size: 15px; color: var(--ink); margin-bottom: 4px; }
|
| 74 |
+
|
| 75 |
+
/* ── v3 ── */
|
| 76 |
+
.land-hero-v3 .land-hero-h1 { margin-bottom: 36px; }
|
| 77 |
+
.land-frieze { display: grid; grid-template-columns: repeat(5, 1fr); gap: 0; border: 1px solid var(--rule-soft); border-bottom: 2px solid var(--ink); background: white; margin-bottom: 36px; }
|
| 78 |
+
.land-frieze-stone { padding: 22px 18px 24px; border-right: 1px solid var(--rule-soft); display: flex; flex-direction: column; gap: 8px; }
|
| 79 |
+
.land-frieze-stone:last-child { border-right: none; }
|
| 80 |
+
.land-frieze-num { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.08em; }
|
| 81 |
+
.land-frieze-name { font-family: var(--font-serif); font-size: 22px; font-weight: 500; margin: 0; color: var(--ink); }
|
| 82 |
+
.land-frieze-role { font-family: var(--font-sans); font-size: 13px; color: var(--ink-secondary); }
|
| 83 |
+
.land-frieze-tag { font-family: var(--font-serif); font-style: italic; font-size: 14px; color: var(--ink-tertiary); margin: 0 0 6px; line-height: 1.4; }
|
| 84 |
+
.land-frieze-sources { margin-top: auto; padding-top: 10px; border-top: 1px dashed var(--rule-soft); font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); line-height: 1.5; }
|
| 85 |
+
|
| 86 |
+
.land-frieze-query { display: flex; flex-direction: column; gap: 12px; }
|
| 87 |
+
.land-frieze-query-meta { font-family: var(--font-mono); font-size: 12px; color: var(--ink-tertiary); display: flex; gap: 8px; flex-wrap: wrap; align-items: baseline; }
|
| 88 |
+
.land-link { background: none; border: none; padding: 0; font: inherit; color: var(--ink); border-bottom: 1px dotted var(--ink-secondary); cursor: pointer; }
|
| 89 |
+
.land-link:hover { border-bottom-style: solid; }
|
docs/design_handoff/design_files/landing-variants.jsx
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Riprap landing-page variants — three artboards on a design canvas.
|
| 2 |
+
v1: Minimal pushed harder (cycling example queries + tiny grounded-output preview)
|
| 3 |
+
v2: Example gallery (query box + 6 pre-baked NYC archetype briefings)
|
| 4 |
+
v3: Methodology-forward (5 Stones frieze leads, query box below)
|
| 5 |
+
*/
|
| 6 |
+
|
| 7 |
+
const { useState: useLand, useEffect: useLandFx } = React;
|
| 8 |
+
|
| 9 |
+
/* ═══════════════════════════════════════════════════════════════════════
|
| 10 |
+
Shared chrome — wordmark + footer methodology link
|
| 11 |
+
═══════════════════════════════════════════════════════════════════════ */
|
| 12 |
+
const LandingChrome = ({ children, board }) => (
|
| 13 |
+
<div className={`land land-${board}`}>
|
| 14 |
+
<header className="land-header">
|
| 15 |
+
<span className="riprap-wordmark">riprap</span>
|
| 16 |
+
<span className="land-header-sep">/</span>
|
| 17 |
+
<span className="land-header-context">Flood Exposure Briefing · NYC</span>
|
| 18 |
+
<nav className="land-header-nav">
|
| 19 |
+
<a href="#methodology">Methodology</a>
|
| 20 |
+
<a href="#sources">Sources</a>
|
| 21 |
+
<a href="#about">About</a>
|
| 22 |
+
</nav>
|
| 23 |
+
</header>
|
| 24 |
+
{children}
|
| 25 |
+
<footer className="land-footer">
|
| 26 |
+
<span>Riprap v0.4.4 — built on NYC OpenData, FEMA NFHL, USGS, NPCC4</span>
|
| 27 |
+
<span>Each briefing is a citation graph. Every claim links to its primary source.</span>
|
| 28 |
+
</footer>
|
| 29 |
+
</div>
|
| 30 |
+
);
|
| 31 |
+
|
| 32 |
+
const QueryBox = ({ size = "md", placeholder = "Address, neighborhood, or BBL — e.g. 80 Pioneer Street, Red Hook" }) => (
|
| 33 |
+
<form className={`land-query land-query-${size}`} onSubmit={(e) => e.preventDefault()}>
|
| 34 |
+
<span className="land-query-prompt" aria-hidden="true">›</span>
|
| 35 |
+
<input
|
| 36 |
+
type="text"
|
| 37 |
+
placeholder={placeholder}
|
| 38 |
+
className="land-query-input"
|
| 39 |
+
aria-label="Query an address, neighborhood, or BBL"
|
| 40 |
+
/>
|
| 41 |
+
<button type="submit" className="land-query-submit">
|
| 42 |
+
Brief this place →
|
| 43 |
+
</button>
|
| 44 |
+
</form>
|
| 45 |
+
);
|
| 46 |
+
|
| 47 |
+
/* ═══════════════════════════════════════════════════════════════════════
|
| 48 |
+
v1 · Minimal pushed harder
|
| 49 |
+
═══════════════════════════════════════════════════════════════════════ */
|
| 50 |
+
const SAMPLE_QUERIES = [
|
| 51 |
+
"80 Pioneer Street, Red Hook",
|
| 52 |
+
"Coney Island Hospital",
|
| 53 |
+
"PS 188, Lower East Side",
|
| 54 |
+
"Hammels Houses, Rockaway",
|
| 55 |
+
"Bowling Green station",
|
| 56 |
+
"555 W 57th Street",
|
| 57 |
+
];
|
| 58 |
+
|
| 59 |
+
const CyclingExamples = () => {
|
| 60 |
+
const [i, setI] = useLand(0);
|
| 61 |
+
useLandFx(() => {
|
| 62 |
+
const t = setInterval(() => setI((x) => (x + 1) % SAMPLE_QUERIES.length), 2200);
|
| 63 |
+
return () => clearInterval(t);
|
| 64 |
+
}, []);
|
| 65 |
+
return (
|
| 66 |
+
<div className="land-cycling" aria-live="polite">
|
| 67 |
+
<span className="land-cycling-label">Try:</span>
|
| 68 |
+
<span className="land-cycling-rail">
|
| 69 |
+
{SAMPLE_QUERIES.map((q, idx) => (
|
| 70 |
+
<span
|
| 71 |
+
key={q}
|
| 72 |
+
className={`land-cycling-item ${idx === i ? "is-active" : ""}`}
|
| 73 |
+
aria-hidden={idx !== i}
|
| 74 |
+
>
|
| 75 |
+
{q}
|
| 76 |
+
</span>
|
| 77 |
+
))}
|
| 78 |
+
</span>
|
| 79 |
+
</div>
|
| 80 |
+
);
|
| 81 |
+
};
|
| 82 |
+
|
| 83 |
+
const GroundedOutputPreview = () => (
|
| 84 |
+
<div className="land-preview" aria-label="Sample of grounded output">
|
| 85 |
+
<div className="land-preview-frame">
|
| 86 |
+
<div className="land-preview-eyebrow">Excerpt — 80 Pioneer Street briefing</div>
|
| 87 |
+
<p className="land-preview-body">
|
| 88 |
+
The lot sits inside the FEMA <span className="land-preview-cite">1% AE flood zone <sup>[c3]</sup></span>,
|
| 89 |
+
with Hurricane Sandy storm-surge high-water marks recorded
|
| 90 |
+
<span className="land-preview-cite"> 4.7 ft above grade <sup>[c1]</sup></span> at the address.
|
| 91 |
+
FloodNet sensor FN-BK-018, two blocks north, has logged
|
| 92 |
+
<span className="land-preview-cite"> 14 nuisance-flood events since 2023 <sup>[c2]</sup></span>.
|
| 93 |
+
</p>
|
| 94 |
+
<div className="land-preview-cites">
|
| 95 |
+
<div className="land-preview-cite-row">
|
| 96 |
+
<span className="land-preview-cite-pin">[c1]</span>
|
| 97 |
+
<span className="land-preview-cite-src">USGS High-Water Mark · Sandy 2012</span>
|
| 98 |
+
<span className="land-preview-cite-tier">empirical</span>
|
| 99 |
+
</div>
|
| 100 |
+
<div className="land-preview-cite-row">
|
| 101 |
+
<span className="land-preview-cite-pin">[c2]</span>
|
| 102 |
+
<span className="land-preview-cite-src">FloodNet sensor FN-BK-018 · 2023–2026</span>
|
| 103 |
+
<span className="land-preview-cite-tier">empirical</span>
|
| 104 |
+
</div>
|
| 105 |
+
<div className="land-preview-cite-row">
|
| 106 |
+
<span className="land-preview-cite-pin">[c3]</span>
|
| 107 |
+
<span className="land-preview-cite-src">FEMA NFHL · panel 36047C0207</span>
|
| 108 |
+
<span className="land-preview-cite-tier">modeled</span>
|
| 109 |
+
</div>
|
| 110 |
+
</div>
|
| 111 |
+
</div>
|
| 112 |
+
</div>
|
| 113 |
+
);
|
| 114 |
+
|
| 115 |
+
const LandingV1 = () => (
|
| 116 |
+
<LandingChrome board="v1">
|
| 117 |
+
<main className="land-hero land-hero-v1">
|
| 118 |
+
<h1 className="land-hero-h1">
|
| 119 |
+
<span className="land-hero-eyebrow">Riprap</span>
|
| 120 |
+
<span className="land-hero-headline">A flood exposure briefing for any place in New York City.</span>
|
| 121 |
+
<span className="land-hero-deck">
|
| 122 |
+
Type an address. Get a written briefing where every numeric claim
|
| 123 |
+
links to its primary public-record source.
|
| 124 |
+
</span>
|
| 125 |
+
</h1>
|
| 126 |
+
<QueryBox size="lg"/>
|
| 127 |
+
<CyclingExamples/>
|
| 128 |
+
</main>
|
| 129 |
+
<section className="land-section land-section-v1">
|
| 130 |
+
<div className="land-section-head">
|
| 131 |
+
<span className="section-label">What you'll get back</span>
|
| 132 |
+
<span className="land-section-meta">A grounded paragraph with citations — not a chatbot answer.</span>
|
| 133 |
+
</div>
|
| 134 |
+
<GroundedOutputPreview/>
|
| 135 |
+
</section>
|
| 136 |
+
</LandingChrome>
|
| 137 |
+
);
|
| 138 |
+
|
| 139 |
+
/* ═══════════════════════════════════════════════════════════════════════
|
| 140 |
+
v2 · Example gallery
|
| 141 |
+
═══════════════════════════════════════════════════════════════════════ */
|
| 142 |
+
const GALLERY = [
|
| 143 |
+
{ kind: "Address", title: "80 Pioneer Street", sub: "Red Hook · industrial loft on Sandy inundation footprint", tally: "8 cards · 1% AE zone · 4.7ft Sandy HWM" },
|
| 144 |
+
{ kind: "Hospital", title: "Coney Island Hospital", sub: "Brooklyn · NYC Health+Hospitals · coastal AE-zone facility", tally: "11 cards · evacuated 2012 · NPCC4 +30in by 2070" },
|
| 145 |
+
{ kind: "School", title: "PS 188", sub: "Lower East Side · K-5 · 1.3mi from East River shore", tally: "6 cards · 0.2% shaded-X zone · DEP CSO outfall 200ft" },
|
| 146 |
+
{ kind: "NYCHA", title: "Hammels Houses", sub: "Rockaway · 712 units · ocean-side public housing", tally: "13 cards · multi-event flooding · TerraMind synthetic SAR" },
|
| 147 |
+
{ kind: "Transit", title: "Bowling Green station", sub: "Lower Manhattan · 4/5 line · 2012 inundation, post-Sandy hardened", tally: "9 cards · MTA flood-resilience capital plan referenced" },
|
| 148 |
+
{ kind: "Address", title: "555 W 57th Street", sub: "Hell's Kitchen · inland · low-exposure control case", tally: "4 cards · X-zone · cited for context comparison" },
|
| 149 |
+
];
|
| 150 |
+
|
| 151 |
+
const GalleryCard = ({ item }) => (
|
| 152 |
+
<a className="land-gallery-card" href="#" onClick={(e) => e.preventDefault()}>
|
| 153 |
+
<div className="land-gallery-kind">{item.kind}</div>
|
| 154 |
+
<h3 className="land-gallery-title">{item.title}</h3>
|
| 155 |
+
<p className="land-gallery-sub">{item.sub}</p>
|
| 156 |
+
<div className="land-gallery-tally">{item.tally}</div>
|
| 157 |
+
<span className="land-gallery-arrow" aria-hidden="true">Open briefing →</span>
|
| 158 |
+
</a>
|
| 159 |
+
);
|
| 160 |
+
|
| 161 |
+
const LandingV2 = () => (
|
| 162 |
+
<LandingChrome board="v2">
|
| 163 |
+
<main className="land-hero land-hero-v2">
|
| 164 |
+
<h1 className="land-hero-h1">
|
| 165 |
+
<span className="land-hero-eyebrow">Riprap · Flood Exposure Briefing</span>
|
| 166 |
+
<span className="land-hero-headline">What does flood mean for this place in New York?</span>
|
| 167 |
+
<span className="land-hero-deck">
|
| 168 |
+
Riprap reads a place across hazard, exposure, observation, and projection —
|
| 169 |
+
and writes it down with citations.
|
| 170 |
+
</span>
|
| 171 |
+
</h1>
|
| 172 |
+
<QueryBox size="lg"/>
|
| 173 |
+
</main>
|
| 174 |
+
<section className="land-section land-section-v2">
|
| 175 |
+
<div className="land-section-head">
|
| 176 |
+
<span className="section-label">Or open a sample briefing</span>
|
| 177 |
+
<span className="land-section-meta">Six NYC archetypes · pre-computed · click to read</span>
|
| 178 |
+
</div>
|
| 179 |
+
<div className="land-gallery">
|
| 180 |
+
{GALLERY.map((it) => <GalleryCard key={it.title} item={it}/>)}
|
| 181 |
+
</div>
|
| 182 |
+
</section>
|
| 183 |
+
<section className="land-section land-section-stones">
|
| 184 |
+
<div className="land-section-head">
|
| 185 |
+
<span className="section-label">How Riprap reads a place</span>
|
| 186 |
+
<a className="land-section-link" href="#methodology">See methodology →</a>
|
| 187 |
+
</div>
|
| 188 |
+
<div className="land-stones-strip">
|
| 189 |
+
<div className="land-stone-pill"><strong>Cornerstone</strong> hazard reader</div>
|
| 190 |
+
<div className="land-stone-pill"><strong>Keystone</strong> asset register</div>
|
| 191 |
+
<div className="land-stone-pill"><strong>Touchstone</strong> live observer</div>
|
| 192 |
+
<div className="land-stone-pill"><strong>Lodestone</strong> projector</div>
|
| 193 |
+
<div className="land-stone-pill"><strong>Capstone</strong> synthesizer</div>
|
| 194 |
+
</div>
|
| 195 |
+
</section>
|
| 196 |
+
</LandingChrome>
|
| 197 |
+
);
|
| 198 |
+
|
| 199 |
+
/* ══════════��════════════════════════════════════════════════════════════
|
| 200 |
+
v3 · Methodology-forward
|
| 201 |
+
═══════════════════════════════════════════════════════════════════════ */
|
| 202 |
+
const STONE_FRIEZE = [
|
| 203 |
+
{ name: "Cornerstone", role: "the hazard reader", tag: "what NYC's ground remembers", sources: "USGS HWMs · FEMA NFHL · DEP stormwater · Prithvi historical" },
|
| 204 |
+
{ name: "Keystone", role: "the asset register", tag: "what's exposed", sources: "MTA · NYCHA · DOE · DOH · PLUTO" },
|
| 205 |
+
{ name: "Touchstone", role: "the live observer", tag: "what's happening now", sources: "FloodNet sensors · 311 complaints · tidal gauges" },
|
| 206 |
+
{ name: "Lodestone", role: "the projector", tag: "what's coming", sources: "NPCC4 · TTM foundation model · TerraMind synthetic SAR · NFIP" },
|
| 207 |
+
{ name: "Capstone", role: "the synthesizer", tag: "writes it all down", sources: "Granite composer · Mellea grounding-check · WeasyPrint" },
|
| 208 |
+
];
|
| 209 |
+
|
| 210 |
+
const LandingV3 = () => (
|
| 211 |
+
<LandingChrome board="v3">
|
| 212 |
+
<main className="land-hero land-hero-v3">
|
| 213 |
+
<h1 className="land-hero-h1">
|
| 214 |
+
<span className="land-hero-eyebrow">Riprap · Flood Exposure Briefing</span>
|
| 215 |
+
<span className="land-hero-headline">Five Stones read every place.</span>
|
| 216 |
+
<span className="land-hero-deck">
|
| 217 |
+
Each briefing routes through a fixed taxonomy of public-record specialists.
|
| 218 |
+
Each Stone is a class of evidence; together they form the briefing.
|
| 219 |
+
</span>
|
| 220 |
+
</h1>
|
| 221 |
+
<div className="land-frieze">
|
| 222 |
+
{STONE_FRIEZE.map((s, i) => (
|
| 223 |
+
<article key={s.name} className="land-frieze-stone">
|
| 224 |
+
<div className="land-frieze-num">{String(i + 1).padStart(2, "0")}</div>
|
| 225 |
+
<h3 className="land-frieze-name">{s.name}</h3>
|
| 226 |
+
<div className="land-frieze-role">{s.role}</div>
|
| 227 |
+
<p className="land-frieze-tag">{s.tag}</p>
|
| 228 |
+
<div className="land-frieze-sources">{s.sources}</div>
|
| 229 |
+
</article>
|
| 230 |
+
))}
|
| 231 |
+
</div>
|
| 232 |
+
<div className="land-frieze-query">
|
| 233 |
+
<QueryBox size="lg"/>
|
| 234 |
+
<span className="land-frieze-query-meta">
|
| 235 |
+
Try <button className="land-link" type="button">80 Pioneer Street, Red Hook</button> ·
|
| 236 |
+
<button className="land-link" type="button"> Coney Island Hospital</button> ·
|
| 237 |
+
<button className="land-link" type="button"> Hammels Houses</button>
|
| 238 |
+
</span>
|
| 239 |
+
</div>
|
| 240 |
+
</main>
|
| 241 |
+
</LandingChrome>
|
| 242 |
+
);
|
| 243 |
+
|
| 244 |
+
/* ═══════════════════════════════════════════════════════════════════════
|
| 245 |
+
Canvas mount
|
| 246 |
+
═══════════════════════════════════════════════════════════════════════ */
|
| 247 |
+
const App = () => (
|
| 248 |
+
<DesignCanvas
|
| 249 |
+
title="Riprap landing — three directions"
|
| 250 |
+
subtitle="Compare side-by-side. Click any artboard to focus."
|
| 251 |
+
>
|
| 252 |
+
<DCSection id="landings" title="Landing page · directions">
|
| 253 |
+
<DCArtboard id="v1" label="v1 · Minimal pushed harder" width={1200} height={1500}>
|
| 254 |
+
<LandingV1/>
|
| 255 |
+
</DCArtboard>
|
| 256 |
+
<DCArtboard id="v2" label="v2 · Example gallery" width={1200} height={1700}>
|
| 257 |
+
<LandingV2/>
|
| 258 |
+
</DCArtboard>
|
| 259 |
+
<DCArtboard id="v3" label="v3 · Methodology-forward" width={1200} height={1500}>
|
| 260 |
+
<LandingV3/>
|
| 261 |
+
</DCArtboard>
|
| 262 |
+
</DCSection>
|
| 263 |
+
</DesignCanvas>
|
| 264 |
+
);
|
| 265 |
+
|
| 266 |
+
ReactDOM.createRoot(document.getElementById("root")).render(<App/>);
|
docs/design_handoff/design_files/map.jsx
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Static SVG mock of the MapLibre map for 80 Pioneer St, Red Hook.
|
| 2 |
+
No network dependency. Encodes all four evidence-tier styles
|
| 3 |
+
per the brief: empirical solid + 0.4 fill, modeled solid + 0.25,
|
| 4 |
+
synthetic dashed + 0.25 with stripe, proxy graduated dots no fill.
|
| 5 |
+
*/
|
| 6 |
+
|
| 7 |
+
const RedHookMapMock = ({ activeLayers, queriedAddress }) => {
|
| 8 |
+
return (
|
| 9 |
+
<svg
|
| 10 |
+
viewBox="0 0 800 560"
|
| 11 |
+
width="100%"
|
| 12 |
+
height="100%"
|
| 13 |
+
role="application"
|
| 14 |
+
aria-label={`NYC flood-exposure map for ${queriedAddress}`}
|
| 15 |
+
style={{ display: "block", background: "#F2F2EE" }}
|
| 16 |
+
preserveAspectRatio="xMidYMid slice"
|
| 17 |
+
>
|
| 18 |
+
<defs>
|
| 19 |
+
{/* Diagonal stripe pattern for synthetic-prior fill */}
|
| 20 |
+
<pattern id="syn-stripe" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
|
| 21 |
+
<rect width="6" height="6" fill="rgba(42,111,168,0.18)"/>
|
| 22 |
+
<line x1="0" y1="0" x2="0" y2="6" stroke="#2A6FA8" strokeWidth="1" />
|
| 23 |
+
</pattern>
|
| 24 |
+
{/* Halo for label readability */}
|
| 25 |
+
<filter id="label-halo" x="-10%" y="-10%" width="120%" height="120%">
|
| 26 |
+
<feMorphology in="SourceAlpha" radius="1.5" operator="dilate" result="halo"/>
|
| 27 |
+
<feFlood floodColor="#FAFAF7"/>
|
| 28 |
+
<feComposite in2="halo" operator="in"/>
|
| 29 |
+
<feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
|
| 30 |
+
</filter>
|
| 31 |
+
</defs>
|
| 32 |
+
|
| 33 |
+
{/* ── Basemap: Carto Positron register ── */}
|
| 34 |
+
{/* Water (Erie Basin / Buttermilk Channel) */}
|
| 35 |
+
<path d="M 0 380 L 220 360 L 360 410 L 520 470 L 800 500 L 800 560 L 0 560 Z" fill="#DCE6EC"/>
|
| 36 |
+
<path d="M 540 0 L 580 0 L 600 90 L 640 180 L 700 240 L 800 280 L 800 0 Z" fill="#DCE6EC"/>
|
| 37 |
+
|
| 38 |
+
{/* Park (Coffey Park) */}
|
| 39 |
+
<rect x="380" y="240" width="90" height="60" fill="#E2E8DA"/>
|
| 40 |
+
|
| 41 |
+
{/* Parcels (reference layer #E5E5E5) */}
|
| 42 |
+
<g stroke="#C9C9C5" strokeWidth="0.5" fill="#FAFAF7">
|
| 43 |
+
{Array.from({ length: 8 }).map((_, r) =>
|
| 44 |
+
Array.from({ length: 14 }).map((_, c) => (
|
| 45 |
+
<rect key={`p-${r}-${c}`} x={60 + c * 50} y={60 + r * 38} width="46" height="34" />
|
| 46 |
+
))
|
| 47 |
+
)}
|
| 48 |
+
</g>
|
| 49 |
+
|
| 50 |
+
{/* Streets */}
|
| 51 |
+
<g stroke="#FAFAF7" strokeWidth="6" fill="none">
|
| 52 |
+
<path d="M 0 100 L 800 90"/>
|
| 53 |
+
<path d="M 0 200 L 800 190"/>
|
| 54 |
+
<path d="M 0 300 L 800 290"/>
|
| 55 |
+
<path d="M 60 0 L 50 380"/>
|
| 56 |
+
<path d="M 200 0 L 190 380"/>
|
| 57 |
+
<path d="M 340 0 L 330 380"/>
|
| 58 |
+
<path d="M 480 0 L 470 380"/>
|
| 59 |
+
<path d="M 620 0 L 610 380"/>
|
| 60 |
+
</g>
|
| 61 |
+
<g stroke="#C9C9C5" strokeWidth="6.5" fill="none" opacity="0.0"/>
|
| 62 |
+
|
| 63 |
+
{/* ── Empirical layer: Sandy Inundation Zone ── */}
|
| 64 |
+
{activeLayers.empirical && (
|
| 65 |
+
<g aria-label="Sandy Inundation Zone">
|
| 66 |
+
<path
|
| 67 |
+
d="M 0 380 L 220 360 L 360 410 L 520 470 L 800 500 L 800 560 L 0 560 Z
|
| 68 |
+
M 0 360 L 240 340 L 380 390 L 540 450 L 800 480
|
| 69 |
+
L 800 380 L 600 360 L 420 320 L 240 300 L 0 320 Z"
|
| 70 |
+
fill="rgba(11,83,148,0.40)"
|
| 71 |
+
stroke="#0B5394"
|
| 72 |
+
strokeWidth="1.5"
|
| 73 |
+
fillRule="evenodd"
|
| 74 |
+
/>
|
| 75 |
+
</g>
|
| 76 |
+
)}
|
| 77 |
+
|
| 78 |
+
{/* ── Modeled layer: FEMA AE zone (solid line, 0.25 fill) ── */}
|
| 79 |
+
{activeLayers.modeled && (
|
| 80 |
+
<g aria-label="FEMA Zone AE">
|
| 81 |
+
<path
|
| 82 |
+
d="M 40 340 L 280 320 L 440 360 L 600 420 L 800 440 L 800 560 L 0 560 L 0 350 Z"
|
| 83 |
+
fill="rgba(42,111,168,0.25)"
|
| 84 |
+
stroke="#2A6FA8"
|
| 85 |
+
strokeWidth="1.5"
|
| 86 |
+
strokeDasharray="0"
|
| 87 |
+
/>
|
| 88 |
+
</g>
|
| 89 |
+
)}
|
| 90 |
+
|
| 91 |
+
{/* ── Synthetic-prior: dashed line + stripe pattern ── */}
|
| 92 |
+
{activeLayers.synthetic && (
|
| 93 |
+
<g aria-label="Synthetic SAR backscatter (TerraMind, 2025-09-14)">
|
| 94 |
+
<path
|
| 95 |
+
d="M 100 380 L 260 360 L 380 390 L 480 420 L 600 440 L 720 460 L 720 500 L 100 500 Z"
|
| 96 |
+
fill="url(#syn-stripe)"
|
| 97 |
+
stroke="#2A6FA8"
|
| 98 |
+
strokeWidth="1.5"
|
| 99 |
+
strokeDasharray="4 3"
|
| 100 |
+
/>
|
| 101 |
+
</g>
|
| 102 |
+
)}
|
| 103 |
+
|
| 104 |
+
{/* ── Proxy: 311 flood complaints (graduated dots, no fill) ── */}
|
| 105 |
+
{activeLayers.proxy && (
|
| 106 |
+
<g aria-label="311 flood complaints, 2019-2025">
|
| 107 |
+
{[
|
| 108 |
+
[120, 320, 5], [180, 350, 8], [220, 280, 4], [280, 330, 11],
|
| 109 |
+
[340, 360, 6], [240, 240, 3], [380, 320, 9], [440, 350, 7],
|
| 110 |
+
[200, 220, 4], [160, 280, 5], [340, 240, 3], [420, 280, 4],
|
| 111 |
+
[500, 360, 6], [540, 400, 8], [180, 380, 5],
|
| 112 |
+
].map(([x, y, r], i) => (
|
| 113 |
+
<circle key={i} cx={x} cy={y} r={r} fill="none" stroke="#6B6B6B" strokeWidth="1.25" />
|
| 114 |
+
))}
|
| 115 |
+
</g>
|
| 116 |
+
)}
|
| 117 |
+
|
| 118 |
+
{/* ── Asset pins for register specialists ── */}
|
| 119 |
+
{/* Subway entrance , square */}
|
| 120 |
+
<g transform="translate(580 260)">
|
| 121 |
+
<rect x="-5" y="-5" width="10" height="10" fill="#1A1A1A" />
|
| 122 |
+
<text x="0" y="-9" fontSize="9" fontFamily="IBM Plex Sans" textAnchor="middle" fill="#1A1A1A" filter="url(#label-halo)">Smith–9 St</text>
|
| 123 |
+
</g>
|
| 124 |
+
{/* NYCHA , open square */}
|
| 125 |
+
<g transform="translate(420 200)">
|
| 126 |
+
<rect x="-5" y="-5" width="10" height="10" fill="none" stroke="#1A1A1A" strokeWidth="1.5"/>
|
| 127 |
+
<text x="0" y="-9" fontSize="9" fontFamily="IBM Plex Sans" textAnchor="middle" fill="#1A1A1A" filter="url(#label-halo)">Red Hook Houses</text>
|
| 128 |
+
</g>
|
| 129 |
+
{/* School , cross */}
|
| 130 |
+
<g transform="translate(360 280)" stroke="#1A1A1A" strokeWidth="1.75" fill="none">
|
| 131 |
+
<line x1="-5" y1="0" x2="5" y2="0"/><line x1="0" y1="-5" x2="0" y2="5"/>
|
| 132 |
+
<text x="8" y="3" fontSize="9" fontFamily="IBM Plex Sans" fill="#1A1A1A" filter="url(#label-halo)">PS 15</text>
|
| 133 |
+
</g>
|
| 134 |
+
{/* Hospital , circle */}
|
| 135 |
+
<g transform="translate(680 160)">
|
| 136 |
+
<circle r="5" fill="#1A1A1A"/>
|
| 137 |
+
<text x="8" y="3" fontSize="9" fontFamily="IBM Plex Sans" fill="#1A1A1A" filter="url(#label-halo)">NYU Cobble Hill</text>
|
| 138 |
+
</g>
|
| 139 |
+
|
| 140 |
+
{/* ── Queried-address pin (warm orange, dominant at z14+) ── */}
|
| 141 |
+
<g transform="translate(300 320)">
|
| 142 |
+
<circle r="14" fill="rgba(209,124,0,0.20)"/>
|
| 143 |
+
<circle r="6" fill="#D17C00" stroke="#FAFAF7" strokeWidth="2"/>
|
| 144 |
+
<line x1="0" y1="6" x2="0" y2="22" stroke="#D17C00" strokeWidth="2"/>
|
| 145 |
+
<text x="0" y="-12" fontSize="11" fontWeight="600" fontFamily="IBM Plex Sans" textAnchor="middle" fill="#1A1A1A" filter="url(#label-halo)">80 Pioneer St</text>
|
| 146 |
+
</g>
|
| 147 |
+
|
| 148 |
+
{/* ── Map labels (Imhof hierarchy: water italic, neighborhoods regular caps) ── */}
|
| 149 |
+
<text x="640" y="490" fontSize="13" fontStyle="italic" fontFamily="IBM Plex Sans" fill="#5A7B8A" filter="url(#label-halo)">Buttermilk Channel</text>
|
| 150 |
+
<text x="120" y="450" fontSize="13" fontStyle="italic" fontFamily="IBM Plex Sans" fill="#5A7B8A" filter="url(#label-halo)">Erie Basin</text>
|
| 151 |
+
<text x="180" y="40" fontSize="14" fontFamily="IBM Plex Sans" fontWeight="500" letterSpacing="0.18em" fill="#4A4A4A" filter="url(#label-halo)">RED HOOK</text>
|
| 152 |
+
<text x="600" y="40" fontSize="14" fontFamily="IBM Plex Sans" fontWeight="500" letterSpacing="0.18em" fill="#4A4A4A" filter="url(#label-halo)">CARROLL GARDENS</text>
|
| 153 |
+
<text x="425" y="265" fontSize="11" fontFamily="IBM Plex Sans" fill="#4A6B4A" filter="url(#label-halo)">Coffey Park</text>
|
| 154 |
+
|
| 155 |
+
{/* Street labels at z15+ */}
|
| 156 |
+
<text x="120" y="195" fontSize="10" fontFamily="IBM Plex Sans" fill="#6B6B6B" filter="url(#label-halo)">Van Brunt St</text>
|
| 157 |
+
<text x="120" y="295" fontSize="10" fontFamily="IBM Plex Sans" fill="#6B6B6B" filter="url(#label-halo)">Pioneer St</text>
|
| 158 |
+
<text x="120" y="345" fontSize="10" fontFamily="IBM Plex Sans" fill="#6B6B6B" filter="url(#label-halo)">Imlay St</text>
|
| 159 |
+
|
| 160 |
+
{/* ── Scale bar + zoom indicator (corners, like USGS quad) ── */}
|
| 161 |
+
<g transform="translate(20 530)" fontFamily="IBM Plex Mono" fontSize="10" fill="#4A4A4A">
|
| 162 |
+
<line x1="0" y1="-2" x2="80" y2="-2" stroke="#1A1A1A" strokeWidth="1.5"/>
|
| 163 |
+
<line x1="0" y1="-5" x2="0" y2="1" stroke="#1A1A1A" strokeWidth="1"/>
|
| 164 |
+
<line x1="40" y1="-5" x2="40" y2="1" stroke="#1A1A1A" strokeWidth="1"/>
|
| 165 |
+
<line x1="80" y1="-5" x2="80" y2="1" stroke="#1A1A1A" strokeWidth="1"/>
|
| 166 |
+
<text x="0" y="14">0</text>
|
| 167 |
+
<text x="40" y="14" textAnchor="middle">200</text>
|
| 168 |
+
<text x="80" y="14" textAnchor="middle">400 ft</text>
|
| 169 |
+
</g>
|
| 170 |
+
<g transform="translate(720 28)" fontFamily="IBM Plex Mono" fontSize="10" fill="#4A4A4A">
|
| 171 |
+
<text x="0" y="0">z 16 · 40.6776°N 74.0096°W</text>
|
| 172 |
+
</g>
|
| 173 |
+
</svg>
|
| 174 |
+
);
|
| 175 |
+
};
|
| 176 |
+
|
| 177 |
+
const MapLegend = ({ activeLayers, onToggle }) => {
|
| 178 |
+
/* v0.4.5: restructured by Stone. Each row carries its source-Stone so the
|
| 179 |
+
panel visually mirrors the Findings stack. The tier swatch is unchanged. */
|
| 180 |
+
const layers = [
|
| 181 |
+
{ key: "empirical", tier: "empirical", stone: "cornerstone", label: "Sandy Inundation Zone (2012)", source: "NYC OEM" },
|
| 182 |
+
{ key: "modeled", tier: "modeled", stone: "cornerstone", label: "FEMA Zone AE · preliminary FIRM", source: "FEMA" },
|
| 183 |
+
{ key: "proxy", tier: "proxy", stone: "touchstone", label: "311 flood complaints, 2019–25", source: "NYC 311" },
|
| 184 |
+
{ key: "synthetic", tier: "synthetic", stone: "touchstone", label: "Synthetic LULC (2025-09-14)", source: "TerraMind v1.2" },
|
| 185 |
+
{ key: "prithvi-pluvial", tier: "modeled", stone: "touchstone", label: "Prithvi pluvial prediction", source: "Prithvi-NYC v2" },
|
| 186 |
+
];
|
| 187 |
+
const stoneOrder = ["cornerstone", "touchstone"];
|
| 188 |
+
const stoneMeta = {
|
| 189 |
+
cornerstone: { name: "Cornerstone", role: "what NYC's ground remembers" },
|
| 190 |
+
touchstone: { name: "Touchstone", role: "what's happening now" },
|
| 191 |
+
};
|
| 192 |
+
return (
|
| 193 |
+
<div className="map-legend" role="group" aria-label="Map layer toggles, grouped by Stone">
|
| 194 |
+
<div className="map-legend-head">
|
| 195 |
+
<span className="section-label">Layers · by Stone</span>
|
| 196 |
+
</div>
|
| 197 |
+
{stoneOrder.map((sk) => (
|
| 198 |
+
<div key={sk} className={`map-legend-stone map-legend-stone-${sk}`} data-stone={sk}>
|
| 199 |
+
<div className="map-legend-stone-head">
|
| 200 |
+
<span className={`map-legend-stone-dot map-legend-stone-dot-${sk}`} aria-hidden="true"></span>
|
| 201 |
+
<span className="map-legend-stone-name">{stoneMeta[sk].name}</span>
|
| 202 |
+
<span className="map-legend-stone-role">· {stoneMeta[sk].role}</span>
|
| 203 |
+
</div>
|
| 204 |
+
{layers.filter((l) => l.stone === sk).map((l) => (
|
| 205 |
+
<button
|
| 206 |
+
key={l.key}
|
| 207 |
+
type="button"
|
| 208 |
+
className={`map-legend-item ${activeLayers[l.key] ? "is-on" : "is-off"}`}
|
| 209 |
+
onClick={() => onToggle(l.key)}
|
| 210 |
+
aria-pressed={activeLayers[l.key]}
|
| 211 |
+
>
|
| 212 |
+
<span className="map-legend-swatch" aria-hidden="true">
|
| 213 |
+
<TierGlyph tier={l.tier} size={11} color={`var(--tier-${l.tier})`} />
|
| 214 |
+
</span>
|
| 215 |
+
<span className="map-legend-text">
|
| 216 |
+
<span className="map-legend-label">{l.label}</span>
|
| 217 |
+
<span className="map-legend-source">{l.source} · <TierBadge tier={l.tier} compact /></span>
|
| 218 |
+
</span>
|
| 219 |
+
<span className="map-legend-toggle" aria-hidden="true">{activeLayers[l.key] ? "ON" : "OFF"}</span>
|
| 220 |
+
</button>
|
| 221 |
+
))}
|
| 222 |
+
</div>
|
| 223 |
+
))}
|
| 224 |
+
</div>
|
| 225 |
+
);
|
| 226 |
+
};
|
| 227 |
+
|
| 228 |
+
Object.assign(window, { RedHookMapMock, MapLegend });
|
docs/design_handoff/design_files/shell.jsx
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* App shell + cold-start state + spec sections that live below the
|
| 2 |
+
prototype: typography spec, palette spec, glyph spec, MapLibre style.json
|
| 3 |
+
fragments, layout grids, PDF template preview, accessibility checklist,
|
| 4 |
+
design rationale, reference register sketches.
|
| 5 |
+
*/
|
| 6 |
+
|
| 7 |
+
const SAMPLE_QUERIES = [
|
| 8 |
+
{ mode: "address", q: "80 Pioneer Street, Red Hook, Brooklyn",
|
| 9 |
+
sub: "Address-mode · Sandy edge · IBZ · NYCHA proximity" },
|
| 10 |
+
{ mode: "neighborhood", q: "Far Rockaway flood exposure briefing",
|
| 11 |
+
sub: "Neighborhood-mode · chronic stormwater · 2050 SLR" },
|
| 12 |
+
{ mode: "development", q: "Hunts Point proposed rezoning , flood-context check",
|
| 13 |
+
sub: "Development-check · CEQR §817 · 311 proxy density" },
|
| 14 |
+
];
|
| 15 |
+
|
| 16 |
+
const ColdStart = ({ onPick, onSubmit }) => {
|
| 17 |
+
const [v, setV] = useState("");
|
| 18 |
+
return (
|
| 19 |
+
<section className="cold-start" aria-label="Empty query state">
|
| 20 |
+
<div className="cold-start-band">
|
| 21 |
+
<p className="cold-start-deck">
|
| 22 |
+
<strong>Riprap</strong> is a citation-grounded Flood Exposure Briefing tool for New York City.
|
| 23 |
+
Type an address, neighborhood, or proposed development , Riprap returns a written briefing
|
| 24 |
+
where every numeric claim links to its primary public-record source.
|
| 25 |
+
</p>
|
| 26 |
+
<p className="cold-start-deck cold-start-deck-secondary">
|
| 27 |
+
Built for agency analysts, planners, journalists, community boards, and researchers.
|
| 28 |
+
<strong> Not for individual residents making personal property decisions.</strong>
|
| 29 |
+
{" "}For residents seeking flood guidance, see <a href="https://www.floodhelpny.org" className="cold-start-redir">FloodHelpNY</a>.
|
| 30 |
+
For real-time conditions, see <a href="https://www.floodnet.nyc" className="cold-start-redir">FloodNet NYC</a>.
|
| 31 |
+
</p>
|
| 32 |
+
</div>
|
| 33 |
+
|
| 34 |
+
<form
|
| 35 |
+
className="cold-start-form"
|
| 36 |
+
onSubmit={(e) => { e.preventDefault(); onSubmit?.(v); }}
|
| 37 |
+
role="search"
|
| 38 |
+
>
|
| 39 |
+
<label htmlFor="riprap-query" className="cold-start-label section-label">Query</label>
|
| 40 |
+
<div className="cold-start-input-row">
|
| 41 |
+
<input
|
| 42 |
+
id="riprap-query"
|
| 43 |
+
type="text"
|
| 44 |
+
value={v}
|
| 45 |
+
onChange={(e) => setV(e.target.value)}
|
| 46 |
+
placeholder="address · neighborhood · proposed development"
|
| 47 |
+
className="cold-start-input"
|
| 48 |
+
autoComplete="off"
|
| 49 |
+
/>
|
| 50 |
+
<button type="submit" className="cold-start-submit">Generate briefing →</button>
|
| 51 |
+
</div>
|
| 52 |
+
</form>
|
| 53 |
+
|
| 54 |
+
<div className="cold-start-samples">
|
| 55 |
+
<span className="section-label cold-start-samples-label">Sample queries</span>
|
| 56 |
+
<div className="cold-start-samples-grid">
|
| 57 |
+
{SAMPLE_QUERIES.map((s, i) => (
|
| 58 |
+
<button
|
| 59 |
+
key={i}
|
| 60 |
+
type="button"
|
| 61 |
+
className="cold-start-sample"
|
| 62 |
+
onClick={() => onPick?.(s)}
|
| 63 |
+
>
|
| 64 |
+
<span className="cold-start-sample-mode">{s.mode}</span>
|
| 65 |
+
<span className="cold-start-sample-q">{s.q}</span>
|
| 66 |
+
<span className="cold-start-sample-sub">{s.sub}</span>
|
| 67 |
+
<span className="cold-start-sample-arrow" aria-hidden="true">↗</span>
|
| 68 |
+
</button>
|
| 69 |
+
))}
|
| 70 |
+
</div>
|
| 71 |
+
</div>
|
| 72 |
+
|
| 73 |
+
<div className="cold-start-trust">
|
| 74 |
+
<span className="section-label">How Riprap is built</span>
|
| 75 |
+
<p className="cold-start-thesis">
|
| 76 |
+
<span className="cold-start-thesis-stone-dot cold-start-thesis-stone-dot-cornerstone" aria-hidden="true"></span><strong>Cornerstone</strong> remembers.{" "}
|
| 77 |
+
<span className="cold-start-thesis-stone-dot cold-start-thesis-stone-dot-keystone" aria-hidden="true"></span><strong>Keystone</strong> tallies.
|
| 78 |
+
{" "}<span className="cold-start-thesis-stone-dot cold-start-thesis-stone-dot-touchstone" aria-hidden="true"></span><strong>Touchstone</strong> watches.{" "}
|
| 79 |
+
<span className="cold-start-thesis-stone-dot cold-start-thesis-stone-dot-lodestone" aria-hidden="true"></span><strong>Lodestone</strong> projects.
|
| 80 |
+
{" "}<span className="cold-start-thesis-stone-dot cold-start-thesis-stone-dot-capstone" aria-hidden="true"></span><strong>Capstone</strong> writes it all down with citations.
|
| 81 |
+
</p>
|
| 82 |
+
<ul className="cold-start-trust-list">
|
| 83 |
+
<li>Five named cognitive roles compose ~25 atomic specialists. <a href="#spec-stones">Architecture →</a></li>
|
| 84 |
+
<li>All foundation models <strong>Apache-2.0</strong>; no commercial APIs at runtime.</li>
|
| 85 |
+
<li>All data from public-record federal, state, and city sources.</li>
|
| 86 |
+
<li>Four epistemic tiers , empirical, modeled, proxy, synthetic prior , visible in the briefing margin and the trace.</li>
|
| 87 |
+
<li>Sections without supporting documents are omitted entirely. Silence over confabulation.</li>
|
| 88 |
+
</ul>
|
| 89 |
+
<a href="#methodology" className="cold-start-method-link">Methodology paper →</a>
|
| 90 |
+
</div>
|
| 91 |
+
</section>
|
| 92 |
+
);
|
| 93 |
+
};
|
| 94 |
+
|
| 95 |
+
const AppHeader = ({ query, onResetCold, onOpenMethodology }) => (
|
| 96 |
+
<header className="app-header" data-screen-label="App header">
|
| 97 |
+
<div className="app-header-inner">
|
| 98 |
+
<div className="app-header-left">
|
| 99 |
+
<span className="riprap-wordmark" aria-label="Riprap">riprap</span>
|
| 100 |
+
<span className="app-header-sep">/</span>
|
| 101 |
+
<span className="app-header-context">Flood Exposure Briefing</span>
|
| 102 |
+
</div>
|
| 103 |
+
<div className="app-header-mid">
|
| 104 |
+
<button
|
| 105 |
+
type="button"
|
| 106 |
+
className="app-header-query"
|
| 107 |
+
onClick={onResetCold}
|
| 108 |
+
aria-label="Edit query"
|
| 109 |
+
>
|
| 110 |
+
<span className="app-header-query-icon" aria-hidden="true">⌕</span>
|
| 111 |
+
<span className="app-header-query-text">{query}</span>
|
| 112 |
+
<span className="app-header-query-edit">edit</span>
|
| 113 |
+
</button>
|
| 114 |
+
</div>
|
| 115 |
+
<div className="app-header-right">
|
| 116 |
+
<a className="app-header-link" href="#methodology" onClick={(e) => { e.preventDefault(); onOpenMethodology?.(); }}>methodology</a>
|
| 117 |
+
<a className="app-header-link" href="#export-pdf">export PDF</a>
|
| 118 |
+
<span className="app-header-status" aria-live="polite">
|
| 119 |
+
<span className="app-header-status-dot" aria-hidden="true"></span>
|
| 120 |
+
live
|
| 121 |
+
</span>
|
| 122 |
+
</div>
|
| 123 |
+
</div>
|
| 124 |
+
</header>
|
| 125 |
+
);
|
| 126 |
+
|
| 127 |
+
const AppFooter = () => (
|
| 128 |
+
<footer className="app-footer">
|
| 129 |
+
<div className="app-footer-inner">
|
| 130 |
+
<p className="app-footer-guard">
|
| 131 |
+
<strong>Riprap does not predict damage.</strong>
|
| 132 |
+
{" "}This tool is for professional analytical work, not personal property decisions.
|
| 133 |
+
For residents, see <a href="https://www.floodhelpny.org">FloodHelpNY</a> · <a href="https://www.floodnet.nyc">FloodNet NYC</a>.
|
| 134 |
+
</p>
|
| 135 |
+
<p className="app-footer-build">
|
| 136 |
+
All foundation models Apache-2.0 · All data from public-record federal, state, and city sources · No commercial APIs contacted at runtime · Riprap v0.4.3 · build 2026-05-05
|
| 137 |
+
</p>
|
| 138 |
+
</div>
|
| 139 |
+
</footer>
|
| 140 |
+
);
|
| 141 |
+
|
| 142 |
+
Object.assign(window, { ColdStart, AppHeader, AppFooter });
|
docs/design_handoff/design_files/stone-evidence.jsx
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Riprap v0.4.4 , Unified Stone bands.
|
| 2 |
+
Each Stone holds: header (name + aggregate) → evidence cards → collapsed trace.
|
| 3 |
+
This replaces the duplicated "evidence grouped by stone" + "trace grouped by stone".
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
const { useState: useSEv44 } = React;
|
| 7 |
+
|
| 8 |
+
const EVIDENCE_BY_STONE = {
|
| 9 |
+
cornerstone: ["e1", "e3", "e4"], // USGS HWMs · FEMA FIRM · DEP stormwater
|
| 10 |
+
keystone: [], // (no exposure-register cards in current EVIDENCE , Keystone-silent)
|
| 11 |
+
touchstone: ["e2", "e7"], // FloodNet sensor · 311 complaints
|
| 12 |
+
lodestone: ["e5"], // NPCC4 SLR projection
|
| 13 |
+
capstone: ["e6", "e8"], // synthesis-tier outputs
|
| 14 |
+
};
|
| 15 |
+
|
| 16 |
+
const STONE_LOOKUP_V44 = {
|
| 17 |
+
cornerstone: { name: "Cornerstone", role: "the hazard reader", tag: "what NYC's ground remembers" },
|
| 18 |
+
keystone: { name: "Keystone", role: "the asset register", tag: "what's exposed" },
|
| 19 |
+
touchstone: { name: "Touchstone", role: "the live observer", tag: "what's happening now" },
|
| 20 |
+
lodestone: { name: "Lodestone", role: "the projector", tag: "what's coming" },
|
| 21 |
+
capstone: { name: "Capstone", role: "the synthesizer", tag: "writes it all down with citations" },
|
| 22 |
+
};
|
| 23 |
+
|
| 24 |
+
/* Walk a Stone's trace members (incl. nested children) into a flat list for tally. */
|
| 25 |
+
const flattenMembers = (members) =>
|
| 26 |
+
members.flatMap((m) => (m.children ? [m, ...flattenMembers(m.children)] : [m]));
|
| 27 |
+
|
| 28 |
+
const StoneTally = ({ cards, members }) => {
|
| 29 |
+
const flat = flattenMembers(members);
|
| 30 |
+
const fired = flat.filter((m) => m.status === "ok").length;
|
| 31 |
+
const silent = flat.filter((m) => m.status === "silent").length;
|
| 32 |
+
const warn = flat.filter((m) => m.status === "warn").length;
|
| 33 |
+
const error = flat.filter((m) => m.status === "error").length;
|
| 34 |
+
const ms = members.reduce((acc, m) => Math.max(acc, m.ms || 0), 0);
|
| 35 |
+
const fmt = (x) => (x === 0 ? ", " : x < 1000 ? x + "ms" : (x / 1000).toFixed(1) + "s");
|
| 36 |
+
return (
|
| 37 |
+
<span className="stone-uni-agg">
|
| 38 |
+
<span className="stone-uni-agg-cards">{cards.length} card{cards.length === 1 ? "" : "s"}</span>
|
| 39 |
+
<span className="stone-uni-agg-sep">·</span>
|
| 40 |
+
<span className="stone-uni-agg-num">{fired}</span> fired
|
| 41 |
+
{silent > 0 && <>{" · "}<span className="stone-uni-agg-num">{silent}</span> silent</>}
|
| 42 |
+
{warn > 0 && <>{" · "}<span className="stone-uni-agg-warn">{warn} warn</span></>}
|
| 43 |
+
{error > 0 && <>{" · "}<span className="stone-uni-agg-err">{error} error</span></>}
|
| 44 |
+
<span className="stone-uni-agg-sep">·</span>
|
| 45 |
+
<span className="stone-uni-agg-ms">{fmt(ms)}</span>
|
| 46 |
+
</span>
|
| 47 |
+
);
|
| 48 |
+
};
|
| 49 |
+
|
| 50 |
+
const UnifiedStoneBand = ({ stone, cardIds, onCite }) => {
|
| 51 |
+
const meta = STONE_LOOKUP_V44[stone.key];
|
| 52 |
+
const cards = cardIds.map((id) => EVIDENCE.find((e) => e.id === id)).filter(Boolean);
|
| 53 |
+
const traceCount = flattenMembers(stone.members).length;
|
| 54 |
+
const [traceOpen, setTraceOpen] = useSEv44(false);
|
| 55 |
+
|
| 56 |
+
return (
|
| 57 |
+
<section className={`stone-uni stone-uni-${stone.key}`} aria-labelledby={`stone-uni-h-${stone.key}`}>
|
| 58 |
+
<header className="stone-uni-head">
|
| 59 |
+
<div className="stone-uni-head-left">
|
| 60 |
+
<h3 id={`stone-uni-h-${stone.key}`} className="stone-uni-name">{meta.name}</h3>
|
| 61 |
+
<span className="stone-uni-role">, {meta.role}</span>
|
| 62 |
+
<span className="stone-uni-tag">{meta.tag}</span>
|
| 63 |
+
</div>
|
| 64 |
+
<StoneTally cards={cards} members={stone.members}/>
|
| 65 |
+
</header>
|
| 66 |
+
|
| 67 |
+
{/* Findings , primary surface */}
|
| 68 |
+
{cards.length === 0 ? (
|
| 69 |
+
<div className="stone-uni-empty">
|
| 70 |
+
<span className="section-label">silent</span>
|
| 71 |
+
<p>No exposure-register cards landed for this query , Keystone's atomic functions all fired (5 joins, see trace) but none of the asset registers (MTA, NYCHA, DOE, DOH, PLUTO) returned a hit at this address.</p>
|
| 72 |
+
</div>
|
| 73 |
+
) : (
|
| 74 |
+
<div className="stone-uni-rail">
|
| 75 |
+
{cards.map((ev) => <EvidenceCard key={ev.id} ev={ev} onCite={onCite}/>)}
|
| 76 |
+
</div>
|
| 77 |
+
)}
|
| 78 |
+
|
| 79 |
+
{/* Provenance , collapsed by default */}
|
| 80 |
+
<div className="stone-uni-trace">
|
| 81 |
+
<button
|
| 82 |
+
className="stone-uni-trace-toggle"
|
| 83 |
+
aria-expanded={traceOpen}
|
| 84 |
+
onClick={() => setTraceOpen((o) => !o)}
|
| 85 |
+
>
|
| 86 |
+
<span className="stone-uni-trace-caret" aria-hidden="true">{traceOpen ? "▾" : "▸"}</span>
|
| 87 |
+
<span className="stone-uni-trace-label">
|
| 88 |
+
{traceOpen ? "Hide" : "Show"} provenance · {traceCount} function{traceCount === 1 ? "" : "s"}
|
| 89 |
+
</span>
|
| 90 |
+
</button>
|
| 91 |
+
{traceOpen && (
|
| 92 |
+
<div className="stone-uni-trace-body">
|
| 93 |
+
{stone.members.map((m) => <window.TraceRow key={m.id} m={m}/>)}
|
| 94 |
+
</div>
|
| 95 |
+
)}
|
| 96 |
+
</div>
|
| 97 |
+
</section>
|
| 98 |
+
);
|
| 99 |
+
};
|
| 100 |
+
|
| 101 |
+
/* Global tally strip , at-a-glance run health */
|
| 102 |
+
const RunHealthStrip = () => {
|
| 103 |
+
const allMembers = STONES.flatMap((s) => flattenMembers(s.members));
|
| 104 |
+
const fired = allMembers.filter((m) => m.status === "ok").length;
|
| 105 |
+
const total = allMembers.length;
|
| 106 |
+
const silent = allMembers.filter((m) => m.status === "silent").length;
|
| 107 |
+
const warn = allMembers.filter((m) => m.status === "warn").length;
|
| 108 |
+
const error = allMembers.filter((m) => m.status === "error").length;
|
| 109 |
+
const totalCards = Object.values(EVIDENCE_BY_STONE).reduce((n, ids) => n + ids.length, 0);
|
| 110 |
+
return (
|
| 111 |
+
<div className="run-health">
|
| 112 |
+
<span className="run-health-item"><strong>5</strong> Stones</span>
|
| 113 |
+
<span className="run-health-sep">·</span>
|
| 114 |
+
<span className="run-health-item"><strong>{fired}/{total}</strong> functions fired</span>
|
| 115 |
+
<span className="run-health-sep">·</span>
|
| 116 |
+
<span className="run-health-item"><strong>{totalCards}</strong> evidence cards</span>
|
| 117 |
+
<span className="run-health-sep">·</span>
|
| 118 |
+
<span className="run-health-item run-health-time"><strong>14.0s</strong> wall-clock</span>
|
| 119 |
+
{silent > 0 && <><span className="run-health-sep">·</span><span className="run-health-item run-health-silent">{silent} silent</span></>}
|
| 120 |
+
{warn > 0 && <><span className="run-health-sep">·</span><span className="run-health-item run-health-warn">{warn} warn</span></>}
|
| 121 |
+
{error > 0 && <><span className="run-health-sep">·</span><span className="run-health-item run-health-error">{error} error</span></>}
|
| 122 |
+
</div>
|
| 123 |
+
);
|
| 124 |
+
};
|
| 125 |
+
|
| 126 |
+
const UnifiedStoneLayout = ({ onCite }) => (
|
| 127 |
+
<section className="stone-uni-layout" aria-label="Findings and provenance, grouped by Stone">
|
| 128 |
+
<RunHealthStrip/>
|
| 129 |
+
{STONES.map((stone) => (
|
| 130 |
+
<UnifiedStoneBand
|
| 131 |
+
key={stone.key}
|
| 132 |
+
stone={stone}
|
| 133 |
+
cardIds={EVIDENCE_BY_STONE[stone.key] || []}
|
| 134 |
+
onCite={onCite}
|
| 135 |
+
/>
|
| 136 |
+
))}
|
| 137 |
+
</section>
|
| 138 |
+
);
|
| 139 |
+
|
| 140 |
+
Object.assign(window, { UnifiedStoneLayout });
|
docs/design_handoff/design_files/stones-trace.jsx
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Riprap v0.4.5 · Stone-banded trace.
|
| 2 |
+
Status enum (v0.4.5): fired / silent_by_design / warned / errored / not_invoked.
|
| 3 |
+
See V0.4.5_SPEC.md §1 for the rationale.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
const { useState: useStV44, useEffect: useEfV44 } = React;
|
| 7 |
+
|
| 8 |
+
const STONES = [
|
| 9 |
+
{
|
| 10 |
+
key: "cornerstone", name: "Cornerstone", role: "the hazard reader",
|
| 11 |
+
tag: "what NYC's ground remembers",
|
| 12 |
+
members: [
|
| 13 |
+
{ id: "c1", name: "sandy_inundation.lookup", status: "fired", ms: 380, tier: "empirical" },
|
| 14 |
+
{ id: "c2", name: "usgs_hwm.spatial_join", status: "fired", ms: 460, tier: "empirical" },
|
| 15 |
+
{ id: "c3", name: "fema_firm.lookup", status: "fired", ms: 290, tier: "modeled" },
|
| 16 |
+
{ id: "c4", name: "dep_stormwater.lookup", status: "fired", ms: 540, tier: "modeled" },
|
| 17 |
+
{ id: "c5", name: "prithvi.historical_segment",status: "warned", ms: 1240, tier: "modeled",
|
| 18 |
+
warning: "deprecation: Prithvi-100M v1 → v2 migration scheduled 2026-Q3" },
|
| 19 |
+
],
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
key: "keystone", name: "Keystone", role: "the asset register",
|
| 23 |
+
tag: "what's exposed",
|
| 24 |
+
members: [
|
| 25 |
+
{ id: "k1", name: "mta_entrance_exposure", status: "silent_by_design", ms: 30, tier: "empirical",
|
| 26 |
+
note: "no entrances within radius" },
|
| 27 |
+
{ id: "k2", name: "nycha.development_join", status: "silent_by_design", ms: 28, tier: "empirical",
|
| 28 |
+
note: "no NYCHA developments within 1.0 mi" },
|
| 29 |
+
{ id: "k3", name: "doe.school_join", status: "silent_by_design", ms: 24, tier: "empirical",
|
| 30 |
+
note: "no DOE schools within 1.0 mi" },
|
| 31 |
+
{ id: "k4", name: "doh.facility_join", status: "silent_by_design", ms: 22, tier: "empirical",
|
| 32 |
+
note: "no acute-care hospitals within 1.0 mi" },
|
| 33 |
+
{ id: "k5", name: "pluto.lot_lookup", status: "silent_by_design", ms: 18, tier: "empirical",
|
| 34 |
+
note: "PLUTO join skipped: queried address not in NYC PLUTO dataset" },
|
| 35 |
+
],
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
key: "touchstone", name: "Touchstone", role: "the live observer",
|
| 39 |
+
tag: "what's happening now",
|
| 40 |
+
members: [
|
| 41 |
+
{ id: "t1", name: "floodnet.history", status: "fired", ms: 1240, tier: "empirical" },
|
| 42 |
+
{ id: "t2", name: "nyc311.flood_complaints", status: "fired", ms: 880, tier: "proxy" },
|
| 43 |
+
{ id: "t3", name: "noaa_coops.recent", status: "fired", ms: 410, tier: "empirical" },
|
| 44 |
+
{ id: "t4", name: "terramind.lulc", status: "fired", ms: 2100, tier: "synthetic" },
|
| 45 |
+
{ id: "t5", name: "prithvi_nyc_pluvial", status: "fired", ms: 1820, tier: "modeled" },
|
| 46 |
+
],
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
key: "lodestone", name: "Lodestone", role: "the projector",
|
| 50 |
+
tag: "what's coming",
|
| 51 |
+
members: [
|
| 52 |
+
{ id: "l1", name: "npcc4.slr_projection", status: "fired", ms: 320, tier: "modeled" },
|
| 53 |
+
{ id: "l2", name: "ttm_battery_surge.zero_shot",status: "fired", ms: 1500, tier: "modeled" },
|
| 54 |
+
{ id: "l3", name: "ttm_battery_surge.fine_tune",status: "fired", ms: 1480, tier: "modeled" },
|
| 55 |
+
{ id: "l4", name: "floodnet_forecast", status: "silent_by_design", ms: 14, tier: "modeled",
|
| 56 |
+
note: "sensor has only 2 historical events; forecast omitted (silent-floor: 5)" },
|
| 57 |
+
{ id: "l5", name: "ttm_311_forecast", status: "errored", ms: 0, tier: "modeled",
|
| 58 |
+
error: "311 history fetch failed: HTTP 503 at NYC OpenData (3 retries)" },
|
| 59 |
+
],
|
| 60 |
+
},
|
| 61 |
+
{
|
| 62 |
+
key: "capstone", name: "Capstone", role: "the synthesizer",
|
| 63 |
+
tag: "writes it all down with citations",
|
| 64 |
+
members: [
|
| 65 |
+
{ id: "p1", name: "granite.compose_briefing", status: "fired", ms: 3200, tier: "modeled" },
|
| 66 |
+
{ id: "p2", name: "mellea.grounding_check", status: "fired", ms: 480, tier: "modeled" },
|
| 67 |
+
{ id: "p3", name: "weasyprint.render_artifact",status: "fired", ms: 920, tier: null },
|
| 68 |
+
],
|
| 69 |
+
},
|
| 70 |
+
];
|
| 71 |
+
|
| 72 |
+
const fmtMs = (ms) => ms === 0 ? "—" : ms < 1000 ? ms + "ms" : (ms / 1000).toFixed(1) + "s";
|
| 73 |
+
const tierColor = (t) => t ? `var(--tier-${t})` : "var(--ink-tertiary)";
|
| 74 |
+
|
| 75 |
+
const flat04 = (members) => members.flatMap(m => m.children ? [m, ...flat04(m.children)] : [m]);
|
| 76 |
+
|
| 77 |
+
const StoneAggregate = ({ stone }) => {
|
| 78 |
+
const all = flat04(stone.members);
|
| 79 |
+
const fired = all.filter(m => m.status === "fired" || m.status === "warned").length;
|
| 80 |
+
const silent = all.filter(m => m.status === "silent_by_design").length;
|
| 81 |
+
const warn = all.filter(m => m.status === "warned").length;
|
| 82 |
+
const error = all.filter(m => m.status === "errored").length;
|
| 83 |
+
const notInvoked = all.filter(m => m.status === "not_invoked").length;
|
| 84 |
+
const ms = stone.members.reduce((acc, m) => Math.max(acc, m.ms || 0), 0);
|
| 85 |
+
return (
|
| 86 |
+
<span className="stone-band-agg">
|
| 87 |
+
<span className="stone-band-agg-num">{fired}</span> fired
|
| 88 |
+
{silent > 0 && <> · <span className="stone-band-agg-num">{silent}</span> silent</>}
|
| 89 |
+
{warn > 0 && <> · <span className="stone-band-agg-warn">{warn} warn</span></>}
|
| 90 |
+
{error > 0 && <> · <span className="stone-band-agg-err">{error} errored</span></>}
|
| 91 |
+
{notInvoked > 0 && <> · <span className="stone-band-agg-num">{notInvoked}</span> not invoked</>}
|
| 92 |
+
{" · "}<span className="stone-band-agg-ms">{fmtMs(ms)}</span>
|
| 93 |
+
</span>
|
| 94 |
+
);
|
| 95 |
+
};
|
| 96 |
+
|
| 97 |
+
const TraceRow = ({ m, indent = 16 }) => {
|
| 98 |
+
if (m.children) {
|
| 99 |
+
return (
|
| 100 |
+
<details className="trace-row trace-row-group" style={{ paddingLeft: indent }} open>
|
| 101 |
+
<summary>
|
| 102 |
+
<span className="trace-bullet">▸</span>
|
| 103 |
+
<span className="trace-name">{m.name}</span>
|
| 104 |
+
<span className="trace-status">{m.status}</span>
|
| 105 |
+
<span className="trace-tier" style={{ color: tierColor(m.tier) }}>{m.tier || ""}</span>
|
| 106 |
+
<span className="trace-ms">{fmtMs(m.ms)}</span>
|
| 107 |
+
</summary>
|
| 108 |
+
{m.children.map(c => <TraceRow key={c.id} m={c} indent={indent + 16}/>)}
|
| 109 |
+
</details>
|
| 110 |
+
);
|
| 111 |
+
}
|
| 112 |
+
if (m.status === "errored") {
|
| 113 |
+
return (
|
| 114 |
+
<details className="trace-row trace-row-error trace-row-errored" style={{ paddingLeft: indent }}>
|
| 115 |
+
<summary>
|
| 116 |
+
<span className="trace-bullet trace-bullet-errored">■</span>
|
| 117 |
+
<span className="trace-name">{m.name}</span>
|
| 118 |
+
<span className="trace-status trace-status-err">errored</span>
|
| 119 |
+
<span className="trace-tier" style={{ color: tierColor(m.tier) }}>{m.tier || ""}</span>
|
| 120 |
+
<span className="trace-ms">{fmtMs(m.ms)}</span>
|
| 121 |
+
<span className="trace-error-summary">{m.error}</span>
|
| 122 |
+
<span className="trace-error-expand" aria-hidden="true">click to expand</span>
|
| 123 |
+
</summary>
|
| 124 |
+
<div className="trace-error-body">
|
| 125 |
+
<div className="trace-error-line"><span className="trace-error-k">error</span><span>{m.error}</span></div>
|
| 126 |
+
<div className="trace-error-line"><span className="trace-error-k">retries</span><span>3</span></div>
|
| 127 |
+
<div className="trace-error-line"><span className="trace-error-k">elapsed</span><span>{fmtMs(m.ms)}</span></div>
|
| 128 |
+
</div>
|
| 129 |
+
</details>
|
| 130 |
+
);
|
| 131 |
+
}
|
| 132 |
+
if (m.status === "silent_by_design") {
|
| 133 |
+
return (
|
| 134 |
+
<div className="trace-row trace-row-silent-bd" style={{ paddingLeft: indent }}>
|
| 135 |
+
<span className="trace-bullet trace-bullet-silent">▢</span>
|
| 136 |
+
<span className="trace-name">{m.name}</span>
|
| 137 |
+
<span className="trace-status">silent</span>
|
| 138 |
+
<span className="trace-tier" style={{ color: tierColor(m.tier) }}>{m.tier || ""}</span>
|
| 139 |
+
<span className="trace-ms">{fmtMs(m.ms)}</span>
|
| 140 |
+
{m.note && <span className="trace-silent-note">{m.note}</span>}
|
| 141 |
+
</div>
|
| 142 |
+
);
|
| 143 |
+
}
|
| 144 |
+
if (m.status === "not_invoked") {
|
| 145 |
+
return (
|
| 146 |
+
<div className="trace-row trace-row-not-invoked" style={{ paddingLeft: indent }}>
|
| 147 |
+
<span className="trace-bullet trace-bullet-notinvoked">▫</span>
|
| 148 |
+
<span className="trace-name">{m.name}</span>
|
| 149 |
+
<span className="trace-status">not invoked</span>
|
| 150 |
+
<span className="trace-tier" style={{ color: tierColor(m.tier) }}>{m.tier || ""}</span>
|
| 151 |
+
<span className="trace-ms">—</span>
|
| 152 |
+
{m.note && <span className="trace-note">{m.note}</span>}
|
| 153 |
+
</div>
|
| 154 |
+
);
|
| 155 |
+
}
|
| 156 |
+
if (m.status === "warned") {
|
| 157 |
+
return (
|
| 158 |
+
<div className="trace-row trace-row-warned" style={{ paddingLeft: indent }}>
|
| 159 |
+
<span className="trace-bullet trace-bullet-warned" style={{ color: tierColor(m.tier) }}>■</span>
|
| 160 |
+
<span className="trace-name">{m.name}</span>
|
| 161 |
+
<span className="trace-status trace-status-warn">warned</span>
|
| 162 |
+
<span className="trace-tier" style={{ color: tierColor(m.tier) }}>{m.tier || ""}</span>
|
| 163 |
+
<span className="trace-ms">{fmtMs(m.ms)}</span>
|
| 164 |
+
<span className="trace-warn-sidemark" aria-hidden="true">!</span>
|
| 165 |
+
{m.warning && <span className="trace-warn-note">{m.warning}</span>}
|
| 166 |
+
</div>
|
| 167 |
+
);
|
| 168 |
+
}
|
| 169 |
+
/* fired */
|
| 170 |
+
return (
|
| 171 |
+
<div className="trace-row trace-row-fired" style={{ paddingLeft: indent }}>
|
| 172 |
+
<span className="trace-bullet trace-bullet-fired" style={{ color: tierColor(m.tier), background: tierColor(m.tier) }}>■</span>
|
| 173 |
+
<span className="trace-name">{m.name}</span>
|
| 174 |
+
<span className="trace-status">fired</span>
|
| 175 |
+
<span className="trace-tier" style={{ color: tierColor(m.tier) }}>{m.tier || ""}</span>
|
| 176 |
+
<span className="trace-ms">{fmtMs(m.ms)}</span>
|
| 177 |
+
{m.note && <span className="trace-note">{m.note}</span>}
|
| 178 |
+
</div>
|
| 179 |
+
);
|
| 180 |
+
};
|
| 181 |
+
|
| 182 |
+
const StoneBand = ({ stone }) => {
|
| 183 |
+
const [open, setOpen] = useStV44(true);
|
| 184 |
+
return (
|
| 185 |
+
<section className={`stone-band stone-band-${stone.key}`} aria-labelledby={`band-h-${stone.key}`} data-stone={stone.key}>
|
| 186 |
+
<button className="stone-band-head" aria-expanded={open} onClick={() => setOpen(o => !o)}>
|
| 187 |
+
<span className="stone-band-head-left">
|
| 188 |
+
<span id={`band-h-${stone.key}`} className="stone-band-name">{stone.name}</span>
|
| 189 |
+
<span className="stone-band-role"> · {stone.role}</span>
|
| 190 |
+
<span className="stone-band-tag">{stone.tag}</span>
|
| 191 |
+
</span>
|
| 192 |
+
<StoneAggregate stone={stone}/>
|
| 193 |
+
</button>
|
| 194 |
+
{open && (
|
| 195 |
+
<div className="stone-band-body">
|
| 196 |
+
{stone.members.map(m => <TraceRow key={m.id} m={m}/>)}
|
| 197 |
+
</div>
|
| 198 |
+
)}
|
| 199 |
+
</section>
|
| 200 |
+
);
|
| 201 |
+
};
|
| 202 |
+
|
| 203 |
+
const StoneTrace = () => {
|
| 204 |
+
const all = STONES.flatMap(s => flat04(s.members));
|
| 205 |
+
const fired = all.filter(m => m.status === "fired" || m.status === "warned").length;
|
| 206 |
+
const silent = all.filter(m => m.status === "silent_by_design").length;
|
| 207 |
+
const error = all.filter(m => m.status === "errored").length;
|
| 208 |
+
return (
|
| 209 |
+
<div className="trace-ui-v44">
|
| 210 |
+
<header className="stone-trace-head">
|
| 211 |
+
<span className="section-label">Run trace · 5 Stones</span>
|
| 212 |
+
<span className="stone-trace-tally">{fired} fired · {silent} silent · {error} errored · 24.0s</span>
|
| 213 |
+
</header>
|
| 214 |
+
{STONES.map(s => <StoneBand key={s.key} stone={s}/>)}
|
| 215 |
+
</div>
|
| 216 |
+
);
|
| 217 |
+
};
|
| 218 |
+
|
| 219 |
+
Object.assign(window, { StoneTrace, STONES, TraceRow, fmtMs, tierColor });
|
docs/design_handoff/design_files/styles.css
ADDED
|
@@ -0,0 +1,1274 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Riprap component styles. Civic-tech-clean. */
|
| 2 |
+
|
| 3 |
+
/* ── App header ────────────────────────────────────────── */
|
| 4 |
+
.app-header {
|
| 5 |
+
position: sticky;
|
| 6 |
+
top: 0;
|
| 7 |
+
z-index: 50;
|
| 8 |
+
background: var(--paper);
|
| 9 |
+
border-bottom: 2px solid var(--ink);
|
| 10 |
+
}
|
| 11 |
+
.app-header-inner {
|
| 12 |
+
max-width: 1600px;
|
| 13 |
+
margin: 0 auto;
|
| 14 |
+
padding: 14px 28px;
|
| 15 |
+
display: grid;
|
| 16 |
+
grid-template-columns: 1fr auto 1fr;
|
| 17 |
+
align-items: center;
|
| 18 |
+
gap: 24px;
|
| 19 |
+
}
|
| 20 |
+
.app-header-left { display: flex; align-items: center; gap: 12px; font-family: var(--font-mono); font-size: 13px; }
|
| 21 |
+
.app-header-sep { color: var(--ink-tertiary); }
|
| 22 |
+
.app-header-context { color: var(--ink-secondary); letter-spacing: 0.04em; }
|
| 23 |
+
.app-header-mid { display: flex; justify-content: center; }
|
| 24 |
+
.app-header-query {
|
| 25 |
+
background: var(--paper-deep);
|
| 26 |
+
border: 1px solid var(--rule-soft);
|
| 27 |
+
padding: 8px 14px;
|
| 28 |
+
font-family: var(--font-mono);
|
| 29 |
+
font-size: 13px;
|
| 30 |
+
color: var(--ink);
|
| 31 |
+
cursor: pointer;
|
| 32 |
+
display: inline-flex;
|
| 33 |
+
align-items: center;
|
| 34 |
+
gap: 10px;
|
| 35 |
+
min-width: 360px;
|
| 36 |
+
text-align: left;
|
| 37 |
+
}
|
| 38 |
+
.app-header-query-icon { color: var(--ink-tertiary); }
|
| 39 |
+
.app-header-query-edit {
|
| 40 |
+
margin-left: auto;
|
| 41 |
+
font-size: 11px;
|
| 42 |
+
letter-spacing: 0.1em;
|
| 43 |
+
text-transform: uppercase;
|
| 44 |
+
color: var(--ink-tertiary);
|
| 45 |
+
}
|
| 46 |
+
.app-header-right { display: flex; align-items: center; justify-content: flex-end; gap: 18px; font-family: var(--font-mono); font-size: 12px; }
|
| 47 |
+
.app-header-link { color: var(--ink-secondary); text-decoration: none; border-bottom: 1px solid transparent; }
|
| 48 |
+
.app-header-link:hover { border-bottom-color: var(--ink); color: var(--ink); }
|
| 49 |
+
.app-header-status { display: inline-flex; align-items: center; gap: 6px; color: var(--ink-tertiary); text-transform: uppercase; letter-spacing: 0.1em; font-size: 11px; }
|
| 50 |
+
.app-header-status-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent-graphical); display: inline-block; }
|
| 51 |
+
|
| 52 |
+
/* ── Hero band (prototype) ───────────────────────────────── */
|
| 53 |
+
.hero-band { background: var(--paper); border-bottom: 1px solid var(--rule-soft); }
|
| 54 |
+
.hero-band-inner { max-width: 1600px; margin: 0 auto; padding: 32px 28px 56px; }
|
| 55 |
+
|
| 56 |
+
/* ── App shell layout ────────────────────────────────────── */
|
| 57 |
+
.app-shell {
|
| 58 |
+
display: grid;
|
| 59 |
+
gap: 24px;
|
| 60 |
+
}
|
| 61 |
+
.app-shell-desktop {
|
| 62 |
+
grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
|
| 63 |
+
grid-template-areas:
|
| 64 |
+
"brief map"
|
| 65 |
+
"brief cites"
|
| 66 |
+
"evidence evidence"
|
| 67 |
+
"trace trace";
|
| 68 |
+
}
|
| 69 |
+
.app-shell-tablet {
|
| 70 |
+
grid-template-columns: 1fr;
|
| 71 |
+
grid-template-areas: "map" "brief" "cites" "evidence" "trace";
|
| 72 |
+
}
|
| 73 |
+
.app-shell-mobile {
|
| 74 |
+
grid-template-columns: 1fr;
|
| 75 |
+
grid-template-areas: "brief" "map" "cites" "evidence" "trace";
|
| 76 |
+
}
|
| 77 |
+
.app-region-brief { grid-area: brief; }
|
| 78 |
+
.app-region-map { grid-area: map; position: sticky; top: 80px; align-self: start; max-height: calc(100vh - 96px); display: flex; flex-direction: column; }
|
| 79 |
+
.app-region-map .map-frame { flex: 1; min-height: 0; }
|
| 80 |
+
.app-region-cites { grid-area: cites; }
|
| 81 |
+
.app-region-evidence { grid-area: evidence; }
|
| 82 |
+
.app-region-trace { grid-area: trace; }
|
| 83 |
+
|
| 84 |
+
@media (max-width: 1099px) {
|
| 85 |
+
.app-region-map { position: static; }
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
.region-head {
|
| 89 |
+
display: flex;
|
| 90 |
+
align-items: center;
|
| 91 |
+
justify-content: space-between;
|
| 92 |
+
border-bottom: 1px solid var(--rule-soft);
|
| 93 |
+
padding-bottom: 8px;
|
| 94 |
+
margin-bottom: 16px;
|
| 95 |
+
}
|
| 96 |
+
.region-head-meta { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
| 97 |
+
.region-action {
|
| 98 |
+
background: transparent;
|
| 99 |
+
border: 1px solid var(--rule-soft);
|
| 100 |
+
font-family: var(--font-mono);
|
| 101 |
+
font-size: 11px;
|
| 102 |
+
padding: 4px 10px;
|
| 103 |
+
cursor: pointer;
|
| 104 |
+
color: var(--ink-secondary);
|
| 105 |
+
}
|
| 106 |
+
.region-action:hover { border-color: var(--ink); color: var(--ink); }
|
| 107 |
+
|
| 108 |
+
.brief-h1 {
|
| 109 |
+
font-size: 13px;
|
| 110 |
+
line-height: 1.2;
|
| 111 |
+
font-weight: 500;
|
| 112 |
+
margin: 0 0 22px;
|
| 113 |
+
letter-spacing: 0.06em;
|
| 114 |
+
text-transform: uppercase;
|
| 115 |
+
color: var(--ink-tertiary);
|
| 116 |
+
font-family: var(--font-mono);
|
| 117 |
+
display: grid;
|
| 118 |
+
grid-template-columns: 1fr auto;
|
| 119 |
+
gap: 14px 18px;
|
| 120 |
+
align-items: end;
|
| 121 |
+
padding-bottom: 14px;
|
| 122 |
+
border-bottom: 2px solid var(--ink);
|
| 123 |
+
}
|
| 124 |
+
.brief-h1-addr {
|
| 125 |
+
display: block;
|
| 126 |
+
grid-column: 1;
|
| 127 |
+
grid-row: 1;
|
| 128 |
+
font-family: var(--font-serif);
|
| 129 |
+
font-size: 32px;
|
| 130 |
+
font-weight: 600;
|
| 131 |
+
letter-spacing: -0.01em;
|
| 132 |
+
text-transform: none;
|
| 133 |
+
color: var(--ink);
|
| 134 |
+
line-height: 1.1;
|
| 135 |
+
margin-top: 4px;
|
| 136 |
+
}
|
| 137 |
+
.brief-h1-eyebrow {
|
| 138 |
+
grid-column: 1;
|
| 139 |
+
grid-row: 1;
|
| 140 |
+
align-self: start;
|
| 141 |
+
display: block;
|
| 142 |
+
}
|
| 143 |
+
.brief-h1-meta {
|
| 144 |
+
grid-column: 2;
|
| 145 |
+
grid-row: 1;
|
| 146 |
+
align-self: end;
|
| 147 |
+
display: flex;
|
| 148 |
+
flex-direction: column;
|
| 149 |
+
gap: 4px;
|
| 150 |
+
text-align: right;
|
| 151 |
+
font-family: var(--font-mono);
|
| 152 |
+
font-size: 11px;
|
| 153 |
+
color: var(--ink-tertiary);
|
| 154 |
+
text-transform: none;
|
| 155 |
+
letter-spacing: 0.04em;
|
| 156 |
+
}
|
| 157 |
+
.brief-h1-meta-row { display: flex; gap: 6px; justify-content: flex-end; }
|
| 158 |
+
.brief-h1-meta-key { color: var(--ink-tertiary); }
|
| 159 |
+
.brief-h1-meta-val { color: var(--ink); font-weight: 500; }
|
| 160 |
+
|
| 161 |
+
/* ── Briefing prose ──────────────────────────────────────── */
|
| 162 |
+
.briefing-prose {
|
| 163 |
+
font-size: 16px;
|
| 164 |
+
line-height: var(--leading-prose);
|
| 165 |
+
max-width: 70ch;
|
| 166 |
+
position: relative;
|
| 167 |
+
}
|
| 168 |
+
.briefing-status {
|
| 169 |
+
border-left: 2px solid var(--ink);
|
| 170 |
+
padding: 4px 0 4px 14px;
|
| 171 |
+
margin-bottom: 24px;
|
| 172 |
+
font-size: 14px;
|
| 173 |
+
color: var(--ink-secondary);
|
| 174 |
+
}
|
| 175 |
+
.briefing-deck strong { color: var(--ink); font-weight: 600; }
|
| 176 |
+
.briefing-meta { display: block; font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); margin-top: 6px; letter-spacing: 0.04em; }
|
| 177 |
+
|
| 178 |
+
.briefing-section-head {
|
| 179 |
+
display: flex;
|
| 180 |
+
align-items: baseline;
|
| 181 |
+
gap: 12px;
|
| 182 |
+
font-size: 22px;
|
| 183 |
+
font-weight: 600;
|
| 184 |
+
margin: 32px 0 12px;
|
| 185 |
+
border-top: 2px solid var(--ink);
|
| 186 |
+
padding-top: 16px;
|
| 187 |
+
flex-wrap: wrap;
|
| 188 |
+
}
|
| 189 |
+
.briefing-section-num {
|
| 190 |
+
font-family: var(--font-mono);
|
| 191 |
+
font-size: 13px;
|
| 192 |
+
color: var(--ink-tertiary);
|
| 193 |
+
letter-spacing: 0.06em;
|
| 194 |
+
font-weight: 500;
|
| 195 |
+
}
|
| 196 |
+
.briefing-section-label { font-family: var(--font-sans); font-weight: 600; }
|
| 197 |
+
.briefing-section-title {
|
| 198 |
+
font-family: var(--font-sans);
|
| 199 |
+
font-size: 14px;
|
| 200 |
+
font-weight: 400;
|
| 201 |
+
color: var(--ink-secondary);
|
| 202 |
+
font-style: italic;
|
| 203 |
+
}
|
| 204 |
+
.briefing-section-tier { font-size: 11px; }
|
| 205 |
+
|
| 206 |
+
.briefing-para {
|
| 207 |
+
margin: 0 0 18px;
|
| 208 |
+
padding-left: 22px;
|
| 209 |
+
position: relative;
|
| 210 |
+
text-wrap: pretty;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
.claim {
|
| 214 |
+
position: relative;
|
| 215 |
+
}
|
| 216 |
+
.claim-glyph {
|
| 217 |
+
position: absolute;
|
| 218 |
+
left: -22px;
|
| 219 |
+
top: 6px;
|
| 220 |
+
display: inline-block;
|
| 221 |
+
}
|
| 222 |
+
.claim-empirical .claim-body { /* default */ }
|
| 223 |
+
.claim-modeled .claim-body { /* default */ }
|
| 224 |
+
.claim-proxy .claim-body { color: var(--ink-secondary); }
|
| 225 |
+
.claim-synthetic .claim-body {
|
| 226 |
+
background: linear-gradient(transparent 60%, rgba(42,111,168,0.15) 60%);
|
| 227 |
+
padding: 0 1px;
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
.inline-cite {
|
| 231 |
+
color: var(--tier-empirical);
|
| 232 |
+
font-family: var(--font-sans);
|
| 233 |
+
font-weight: 500;
|
| 234 |
+
text-decoration: none;
|
| 235 |
+
font-size: 14px;
|
| 236 |
+
}
|
| 237 |
+
.inline-cite sup { font-size: 0.78em; }
|
| 238 |
+
.inline-cite:hover { background: rgba(11,83,148,0.08); }
|
| 239 |
+
|
| 240 |
+
.is-dense .briefing-section-head { margin: 18px 0 10px; padding-top: 12px; }
|
| 241 |
+
.is-dense .briefing-para { margin-bottom: 12px; }
|
| 242 |
+
|
| 243 |
+
.streaming-caret {
|
| 244 |
+
display: inline-block;
|
| 245 |
+
color: var(--accent-graphical);
|
| 246 |
+
animation: blink 1s steps(2) infinite;
|
| 247 |
+
margin-left: 2px;
|
| 248 |
+
}
|
| 249 |
+
@keyframes blink { 50% { opacity: 0; } }
|
| 250 |
+
|
| 251 |
+
/* ── Citation drawer ─────────────────────────────────────── */
|
| 252 |
+
.citation-drawer {
|
| 253 |
+
border-top: 1px solid var(--rule-soft);
|
| 254 |
+
border-bottom: 1px solid var(--rule-soft);
|
| 255 |
+
padding: 16px 0;
|
| 256 |
+
font-size: 13px;
|
| 257 |
+
}
|
| 258 |
+
.citation-drawer-head {
|
| 259 |
+
display: flex;
|
| 260 |
+
justify-content: space-between;
|
| 261 |
+
align-items: baseline;
|
| 262 |
+
border-bottom: 1px solid var(--rule-soft);
|
| 263 |
+
padding-bottom: 8px;
|
| 264 |
+
margin-bottom: 12px;
|
| 265 |
+
}
|
| 266 |
+
.citation-drawer-meta { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
| 267 |
+
.citation-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
|
| 268 |
+
.citation-item {
|
| 269 |
+
display: grid;
|
| 270 |
+
grid-template-columns: 32px 1fr;
|
| 271 |
+
gap: 8px;
|
| 272 |
+
padding: 10px 12px;
|
| 273 |
+
border-left: 2px solid var(--rule-soft);
|
| 274 |
+
transition: background 200ms;
|
| 275 |
+
}
|
| 276 |
+
.citation-item.is-active {
|
| 277 |
+
border-left-color: var(--accent-graphical);
|
| 278 |
+
background: rgba(209,124,0,0.06);
|
| 279 |
+
}
|
| 280 |
+
.citation-num { font-family: var(--font-mono); color: var(--ink-tertiary); font-size: 12px; }
|
| 281 |
+
.citation-line-1 { display: flex; align-items: center; gap: 8px; }
|
| 282 |
+
.citation-source { font-weight: 600; }
|
| 283 |
+
.citation-vintage { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); margin-left: auto; }
|
| 284 |
+
.citation-title { font-size: 13px; line-height: 1.4; margin: 4px 0; color: var(--ink-secondary); }
|
| 285 |
+
.citation-meta { display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
| 286 |
+
.citation-drawer-foot { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--rule-soft); }
|
| 287 |
+
.citation-foot-copy { font-size: 12px; color: var(--ink-tertiary); margin: 6px 0 0; max-width: 60ch; }
|
| 288 |
+
|
| 289 |
+
/* ── Map ─────────────────────────────────────────────────── */
|
| 290 |
+
.map-frame {
|
| 291 |
+
position: relative;
|
| 292 |
+
border: 1px solid var(--ink);
|
| 293 |
+
background: var(--paper-deep);
|
| 294 |
+
aspect-ratio: 8 / 5.6;
|
| 295 |
+
overflow: hidden;
|
| 296 |
+
}
|
| 297 |
+
.map-legend {
|
| 298 |
+
position: absolute;
|
| 299 |
+
top: 12px;
|
| 300 |
+
left: 12px;
|
| 301 |
+
background: rgba(250, 250, 247, 0.96);
|
| 302 |
+
border: 1px solid var(--ink);
|
| 303 |
+
padding: 10px 12px 12px;
|
| 304 |
+
width: 280px;
|
| 305 |
+
display: flex;
|
| 306 |
+
flex-direction: column;
|
| 307 |
+
gap: 4px;
|
| 308 |
+
backdrop-filter: blur(4px);
|
| 309 |
+
}
|
| 310 |
+
.map-legend-head { padding-bottom: 6px; border-bottom: 1px solid var(--rule-soft); margin-bottom: 4px; }
|
| 311 |
+
.map-legend-item {
|
| 312 |
+
display: grid;
|
| 313 |
+
grid-template-columns: 16px 1fr auto;
|
| 314 |
+
gap: 10px;
|
| 315 |
+
align-items: center;
|
| 316 |
+
background: transparent;
|
| 317 |
+
border: 0;
|
| 318 |
+
padding: 6px 4px;
|
| 319 |
+
text-align: left;
|
| 320 |
+
cursor: pointer;
|
| 321 |
+
font-family: var(--font-sans);
|
| 322 |
+
border-bottom: 1px solid transparent;
|
| 323 |
+
min-height: 44px;
|
| 324 |
+
}
|
| 325 |
+
.map-legend-item:hover { background: rgba(0,0,0,0.03); }
|
| 326 |
+
.map-legend-item.is-off { opacity: 0.45; }
|
| 327 |
+
.map-legend-text { display: flex; flex-direction: column; gap: 2px; }
|
| 328 |
+
.map-legend-label { font-size: 12px; line-height: 1.3; color: var(--ink); }
|
| 329 |
+
.map-legend-source { font-family: var(--font-mono); font-size: 10px; color: var(--ink-tertiary); display: inline-flex; gap: 6px; align-items: center; }
|
| 330 |
+
.map-legend-toggle { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; color: var(--ink-tertiary); }
|
| 331 |
+
.is-on .map-legend-toggle { color: var(--accent); }
|
| 332 |
+
|
| 333 |
+
/* ── Trace UI ────────────────────────────────────────────── */
|
| 334 |
+
.trace-ui {
|
| 335 |
+
border: 1px solid var(--rule-soft);
|
| 336 |
+
background: var(--paper-deep);
|
| 337 |
+
}
|
| 338 |
+
.trace-head {
|
| 339 |
+
display: flex;
|
| 340 |
+
justify-content: space-between;
|
| 341 |
+
align-items: center;
|
| 342 |
+
padding: 10px 16px;
|
| 343 |
+
border-bottom: 1px solid var(--rule-soft);
|
| 344 |
+
}
|
| 345 |
+
.trace-head-left { display: flex; align-items: center; gap: 14px; }
|
| 346 |
+
.trace-head-meta { display: flex; gap: 6px; font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); }
|
| 347 |
+
.trace-head-sep { color: var(--ink-tertiary); }
|
| 348 |
+
.trace-head-silent { color: var(--accent); }
|
| 349 |
+
.trace-collapse-btn {
|
| 350 |
+
background: transparent;
|
| 351 |
+
border: 1px solid var(--rule-soft);
|
| 352 |
+
font-family: var(--font-mono);
|
| 353 |
+
font-size: 11px;
|
| 354 |
+
padding: 4px 10px;
|
| 355 |
+
cursor: pointer;
|
| 356 |
+
color: var(--ink-secondary);
|
| 357 |
+
}
|
| 358 |
+
.trace-col-heads {
|
| 359 |
+
display: grid;
|
| 360 |
+
grid-template-columns: 28px 24px 1fr 80px 140px;
|
| 361 |
+
gap: 8px;
|
| 362 |
+
padding: 8px 16px 6px;
|
| 363 |
+
border-bottom: 1px solid var(--rule-soft);
|
| 364 |
+
font-family: var(--font-mono);
|
| 365 |
+
font-size: 10px;
|
| 366 |
+
letter-spacing: 0.12em;
|
| 367 |
+
text-transform: uppercase;
|
| 368 |
+
color: var(--ink-tertiary);
|
| 369 |
+
}
|
| 370 |
+
.trace-tree { font-family: var(--font-mono); font-size: 13px; }
|
| 371 |
+
.trace-row { border-bottom: 1px solid var(--rule-soft); }
|
| 372 |
+
.trace-row-toggle {
|
| 373 |
+
width: 100%;
|
| 374 |
+
display: grid;
|
| 375 |
+
grid-template-columns: 16px 24px 1fr 80px 140px;
|
| 376 |
+
gap: 8px;
|
| 377 |
+
align-items: center;
|
| 378 |
+
background: transparent;
|
| 379 |
+
border: 0;
|
| 380 |
+
padding: 6px 16px 6px 0;
|
| 381 |
+
text-align: left;
|
| 382 |
+
cursor: pointer;
|
| 383 |
+
color: var(--ink);
|
| 384 |
+
min-height: 36px;
|
| 385 |
+
}
|
| 386 |
+
.trace-row-toggle[disabled] { cursor: default; }
|
| 387 |
+
.trace-row-toggle:hover:not([disabled]) { background: rgba(0,0,0,0.025); }
|
| 388 |
+
.trace-tree-glyph { color: var(--ink-tertiary); }
|
| 389 |
+
.trace-name { color: var(--ink); }
|
| 390 |
+
.trace-note { color: var(--ink-tertiary); }
|
| 391 |
+
.trace-ms-col { color: var(--ink-secondary); }
|
| 392 |
+
.trace-tier-col { display: inline-flex; align-items: center; gap: 6px; }
|
| 393 |
+
.trace-row-silent .trace-name { color: var(--ink-tertiary); }
|
| 394 |
+
.trace-silent-tag {
|
| 395 |
+
font-size: 10px;
|
| 396 |
+
letter-spacing: 0.1em;
|
| 397 |
+
text-transform: uppercase;
|
| 398 |
+
color: var(--accent);
|
| 399 |
+
border: 1px solid var(--accent);
|
| 400 |
+
padding: 1px 5px;
|
| 401 |
+
}
|
| 402 |
+
.trace-status-glyph { color: var(--ink-tertiary); font-size: 13px; }
|
| 403 |
+
.trace-output {
|
| 404 |
+
padding: 4px 16px 8px 0;
|
| 405 |
+
font-size: 12px;
|
| 406 |
+
color: var(--ink-secondary);
|
| 407 |
+
display: flex;
|
| 408 |
+
gap: 10px;
|
| 409 |
+
align-items: baseline;
|
| 410 |
+
border-top: 1px dashed var(--rule-soft);
|
| 411 |
+
background: rgba(0,0,0,0.015);
|
| 412 |
+
}
|
| 413 |
+
.trace-output-prefix { color: var(--ink-tertiary); }
|
| 414 |
+
.trace-output-claims { margin-left: auto; padding-right: 16px; font-family: var(--font-mono); font-size: 11px; color: var(--accent); }
|
| 415 |
+
|
| 416 |
+
/* ── Evidence cards ──────────────────────────────────────── */
|
| 417 |
+
.evidence-grid-head {
|
| 418 |
+
display: flex;
|
| 419 |
+
justify-content: space-between;
|
| 420 |
+
align-items: baseline;
|
| 421 |
+
border-bottom: 1px solid var(--rule-soft);
|
| 422 |
+
padding-bottom: 8px;
|
| 423 |
+
margin-bottom: 16px;
|
| 424 |
+
}
|
| 425 |
+
.evidence-grid-meta { display: flex; gap: 12px; font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); }
|
| 426 |
+
.evidence-grid-tally { display: inline-flex; align-items: center; gap: 4px; }
|
| 427 |
+
.evidence-grid-rail {
|
| 428 |
+
display: grid;
|
| 429 |
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
| 430 |
+
gap: 16px;
|
| 431 |
+
}
|
| 432 |
+
|
| 433 |
+
.evidence-card {
|
| 434 |
+
border: 1px solid var(--rule-soft);
|
| 435 |
+
background: var(--paper);
|
| 436 |
+
padding: 14px;
|
| 437 |
+
display: flex;
|
| 438 |
+
flex-direction: column;
|
| 439 |
+
gap: 10px;
|
| 440 |
+
position: relative;
|
| 441 |
+
border-top: 3px solid var(--ink);
|
| 442 |
+
}
|
| 443 |
+
.evidence-card-empirical { border-top-color: var(--tier-empirical); }
|
| 444 |
+
.evidence-card-modeled { border-top-color: var(--tier-modeled); }
|
| 445 |
+
.evidence-card-proxy { border-top-color: var(--tier-proxy); }
|
| 446 |
+
.evidence-card-synthetic { border-top-color: var(--tier-synthetic); border-top-style: dashed; }
|
| 447 |
+
|
| 448 |
+
.evidence-card-head { display: flex; justify-content: space-between; align-items: center; }
|
| 449 |
+
.evidence-card-source { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; font-size: 13px; }
|
| 450 |
+
.evidence-card-vintage { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
| 451 |
+
.evidence-card-title { font-size: 14px; font-weight: 600; line-height: 1.3; margin: 0; }
|
| 452 |
+
.evidence-card-body { padding: 4px 0; }
|
| 453 |
+
.evidence-scalar-value { font-size: 24px; font-weight: 600; line-height: 1.1; font-family: var(--font-serif); }
|
| 454 |
+
.evidence-scalar-unit { font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); margin-top: 2px; }
|
| 455 |
+
.evidence-scalar-aux { font-size: 12px; color: var(--ink-tertiary); margin-top: 4px; line-height: 1.4; }
|
| 456 |
+
.evidence-spark-headline { font-size: 18px; font-weight: 600; font-family: var(--font-serif); margin-bottom: 4px; }
|
| 457 |
+
.evidence-table { width: 100%; border-collapse: collapse; font-family: var(--font-mono); font-size: 11px; }
|
| 458 |
+
.evidence-table th, .evidence-table td { text-align: left; padding: 4px 6px; border-bottom: 1px solid var(--rule-soft); }
|
| 459 |
+
.evidence-table th { color: var(--ink-tertiary); font-weight: 500; text-transform: uppercase; letter-spacing: 0.08em; font-size: 9px; }
|
| 460 |
+
.evidence-thumb { display: flex; flex-direction: column; gap: 6px; }
|
| 461 |
+
.evidence-card-foot {
|
| 462 |
+
display: flex;
|
| 463 |
+
justify-content: space-between;
|
| 464 |
+
align-items: center;
|
| 465 |
+
padding-top: 8px;
|
| 466 |
+
border-top: 1px solid var(--rule-soft);
|
| 467 |
+
}
|
| 468 |
+
.evidence-card-cite {
|
| 469 |
+
background: transparent;
|
| 470 |
+
border: 0;
|
| 471 |
+
font-family: var(--font-mono);
|
| 472 |
+
font-size: 11px;
|
| 473 |
+
color: var(--ink-secondary);
|
| 474 |
+
cursor: pointer;
|
| 475 |
+
padding: 4px 0;
|
| 476 |
+
display: inline-flex;
|
| 477 |
+
align-items: center;
|
| 478 |
+
gap: 6px;
|
| 479 |
+
}
|
| 480 |
+
.evidence-card-cite:hover { color: var(--accent); }
|
| 481 |
+
.evidence-card-cite-arrow { color: var(--ink-tertiary); }
|
| 482 |
+
|
| 483 |
+
/* ── Cold start ─────────────────────────────────────────── */
|
| 484 |
+
.cold-start { max-width: 920px; margin: 0 auto; padding: 32px 0; }
|
| 485 |
+
.cold-start-band { border-top: 2px solid var(--ink); border-bottom: 1px solid var(--rule-soft); padding: 24px 0; margin-bottom: 32px; }
|
| 486 |
+
.cold-start-deck { font-size: 18px; line-height: 1.5; max-width: 70ch; margin: 0 0 12px; text-wrap: pretty; }
|
| 487 |
+
.cold-start-deck-secondary { font-size: 14px; color: var(--ink-secondary); }
|
| 488 |
+
.cold-start-redir { color: var(--accent); border-bottom: 1px solid var(--accent); text-decoration: none; }
|
| 489 |
+
.cold-start-form { margin-bottom: 32px; }
|
| 490 |
+
.cold-start-label { display: block; margin-bottom: 8px; }
|
| 491 |
+
.cold-start-input-row { display: grid; grid-template-columns: 1fr auto; gap: 0; border: 2px solid var(--ink); }
|
| 492 |
+
.cold-start-input { padding: 14px 16px; font-family: var(--font-mono); font-size: 14px; border: 0; background: var(--paper); color: var(--ink); }
|
| 493 |
+
.cold-start-input:focus { outline: 0; background: var(--paper-deep); }
|
| 494 |
+
.cold-start-submit { background: var(--ink); color: var(--paper); border: 0; padding: 0 20px; font-family: var(--font-mono); font-size: 13px; cursor: pointer; letter-spacing: 0.04em; }
|
| 495 |
+
.cold-start-submit:hover { background: var(--accent); }
|
| 496 |
+
.cold-start-samples { margin-bottom: 32px; }
|
| 497 |
+
.cold-start-samples-label { display: block; margin-bottom: 12px; }
|
| 498 |
+
.cold-start-samples-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; }
|
| 499 |
+
.cold-start-sample {
|
| 500 |
+
text-align: left;
|
| 501 |
+
background: var(--paper-deep);
|
| 502 |
+
border: 1px solid var(--rule-soft);
|
| 503 |
+
padding: 14px 16px;
|
| 504 |
+
font-family: var(--font-sans);
|
| 505 |
+
cursor: pointer;
|
| 506 |
+
display: grid;
|
| 507 |
+
gap: 4px;
|
| 508 |
+
position: relative;
|
| 509 |
+
min-height: 80px;
|
| 510 |
+
}
|
| 511 |
+
.cold-start-sample:hover { border-color: var(--ink); }
|
| 512 |
+
.cold-start-sample-mode { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent); }
|
| 513 |
+
.cold-start-sample-q { font-size: 14px; font-weight: 500; line-height: 1.3; color: var(--ink); }
|
| 514 |
+
.cold-start-sample-sub { font-size: 11px; color: var(--ink-tertiary); font-family: var(--font-mono); }
|
| 515 |
+
.cold-start-sample-arrow { position: absolute; top: 12px; right: 14px; color: var(--ink-tertiary); }
|
| 516 |
+
.cold-start-trust { border-top: 1px solid var(--rule-soft); padding-top: 16px; }
|
| 517 |
+
.cold-start-trust-list { font-size: 13px; line-height: 1.5; color: var(--ink-secondary); padding-left: 18px; margin: 8px 0; }
|
| 518 |
+
.cold-start-method-link { font-family: var(--font-mono); font-size: 13px; color: var(--ink); border-bottom: 1px solid var(--ink); text-decoration: none; }
|
| 519 |
+
|
| 520 |
+
/* ── Spec band ──────────────────────────────────────────── */
|
| 521 |
+
.spec-band { background: var(--paper-deep); border-top: 2px solid var(--ink); }
|
| 522 |
+
.spec-band-inner { max-width: 1600px; margin: 0 auto; padding: 56px 28px 80px; }
|
| 523 |
+
.spec-band-head { max-width: 70ch; margin-bottom: 56px; }
|
| 524 |
+
.spec-band-title { font-size: 42px; line-height: 1.1; font-weight: 600; margin: 12px 0 16px; letter-spacing: -0.02em; font-family: var(--font-serif); }
|
| 525 |
+
.spec-band-deck { font-size: 17px; line-height: 1.5; color: var(--ink-secondary); }
|
| 526 |
+
.spec-toc { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 0; margin-top: 32px; border-top: 1px solid var(--rule-soft); border-left: 1px solid var(--rule-soft); }
|
| 527 |
+
.spec-toc-item { display: flex; gap: 8px; padding: 10px 14px; border-right: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft); text-decoration: none; color: var(--ink); background: var(--paper); font-family: var(--font-mono); font-size: 12px; align-items: baseline; }
|
| 528 |
+
.spec-toc-item:hover { background: var(--paper-deep); color: var(--accent); }
|
| 529 |
+
.spec-toc-n { color: var(--ink-tertiary); }
|
| 530 |
+
|
| 531 |
+
.spec-section { padding: 48px 0; border-top: 2px solid var(--ink); }
|
| 532 |
+
.spec-section-head { max-width: 70ch; margin-bottom: 32px; display: flex; flex-direction: column; gap: 6px; }
|
| 533 |
+
.spec-section-title { font-size: 32px; line-height: 1.15; font-weight: 600; margin: 4px 0 12px; font-family: var(--font-serif); }
|
| 534 |
+
.spec-section-deck { font-size: 15px; line-height: 1.55; color: var(--ink-secondary); margin: 0; }
|
| 535 |
+
|
| 536 |
+
/* Overview */
|
| 537 |
+
.overview-tiers { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 0; border-top: 1px solid var(--rule-soft); border-left: 1px solid var(--rule-soft); }
|
| 538 |
+
.overview-tier { padding: 20px; border-right: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft); background: var(--paper); display: flex; flex-direction: column; gap: 10px; }
|
| 539 |
+
.overview-tier-head { display: flex; align-items: center; gap: 10px; }
|
| 540 |
+
.overview-tier-name { font-size: 16px; font-weight: 600; }
|
| 541 |
+
.overview-tier-desc { font-size: 13px; color: var(--ink-secondary); margin: 0; line-height: 1.5; }
|
| 542 |
+
.overview-tier-ex { font-size: 11px; font-family: var(--font-mono); color: var(--ink-tertiary); margin: 0; line-height: 1.5; }
|
| 543 |
+
|
| 544 |
+
/* Palette */
|
| 545 |
+
.palette-grid { display: grid; gap: 0; border-top: 1px solid var(--rule-soft); }
|
| 546 |
+
.palette-row {
|
| 547 |
+
display: grid;
|
| 548 |
+
grid-template-columns: 60px 1.6fr 1.2fr 1.4fr 2fr;
|
| 549 |
+
gap: 16px;
|
| 550 |
+
align-items: center;
|
| 551 |
+
padding: 14px 0;
|
| 552 |
+
border-bottom: 1px solid var(--rule-soft);
|
| 553 |
+
font-size: 13px;
|
| 554 |
+
}
|
| 555 |
+
.palette-swatch { width: 48px; height: 48px; border: 1px solid var(--rule-soft); }
|
| 556 |
+
.palette-swatch.is-syn { background-image: repeating-linear-gradient(45deg, transparent 0, transparent 3px, #2A6FA8 3px, #2A6FA8 4px); }
|
| 557 |
+
.palette-name { font-weight: 600; font-size: 14px; }
|
| 558 |
+
.palette-token { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
| 559 |
+
.palette-hex { font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); }
|
| 560 |
+
.palette-contrast-ratio { font-family: var(--font-mono); font-size: 14px; font-weight: 600; }
|
| 561 |
+
.palette-contrast-grade { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; }
|
| 562 |
+
.palette-contrast-aaa, .palette-contrast-aa { color: var(--tier-empirical); }
|
| 563 |
+
.palette-contrast-decorative { color: var(--ink-tertiary); }
|
| 564 |
+
.palette-note { font-size: 12px; color: var(--ink-secondary); line-height: 1.4; }
|
| 565 |
+
.cvd-strip { margin-top: 24px; padding: 16px; background: var(--paper); border: 1px solid var(--rule-soft); }
|
| 566 |
+
.cvd-copy { font-size: 13px; line-height: 1.55; color: var(--ink-secondary); margin: 8px 0 0; max-width: 70ch; }
|
| 567 |
+
|
| 568 |
+
/* Type spec */
|
| 569 |
+
.type-spec-table { border-top: 1px solid var(--rule-soft); margin-bottom: 32px; }
|
| 570 |
+
.type-spec-row { display: grid; grid-template-columns: 1.4fr 1.2fr 1.4fr 2fr; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--rule-soft); font-size: 13px; align-items: baseline; }
|
| 571 |
+
.type-spec-head { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-tertiary); }
|
| 572 |
+
.type-spec-surface { font-weight: 500; }
|
| 573 |
+
.type-spec-family { font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); }
|
| 574 |
+
.type-spec-size { font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); }
|
| 575 |
+
.type-spec-use { color: var(--ink-secondary); font-size: 12px; }
|
| 576 |
+
.type-spec-samples { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; padding: 20px; background: var(--paper); border: 1px solid var(--rule-soft); }
|
| 577 |
+
.type-sample { display: flex; flex-direction: column; }
|
| 578 |
+
|
| 579 |
+
/* Glyphs */
|
| 580 |
+
.glyph-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 0; border-top: 1px solid var(--rule-soft); border-left: 1px solid var(--rule-soft); }
|
| 581 |
+
.glyph-cell { padding: 24px; border-right: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft); display: flex; flex-direction: column; gap: 14px; background: var(--paper); }
|
| 582 |
+
.glyph-display { width: 96px; height: 96px; background: var(--paper-deep); display: flex; align-items: center; justify-content: center; border: 1px solid var(--rule-soft); }
|
| 583 |
+
.glyph-anatomy { display: flex; flex-direction: column; gap: 4px; }
|
| 584 |
+
.glyph-anatomy-title { font-size: 16px; font-weight: 600; }
|
| 585 |
+
.glyph-anatomy-shape { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
| 586 |
+
.glyph-anatomy-desc { font-size: 13px; color: var(--ink-secondary); line-height: 1.5; }
|
| 587 |
+
.glyph-anatomy-ex { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); line-height: 1.5; }
|
| 588 |
+
.glyph-sizes, .glyph-mono { display: flex; align-items: center; gap: 10px; font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); border-top: 1px dashed var(--rule-soft); padding-top: 10px; }
|
| 589 |
+
.glyph-sizes-label { min-width: 80px; }
|
| 590 |
+
.glyph-sizes-mono { color: var(--ink-tertiary); margin-left: auto; }
|
| 591 |
+
.glyph-mono > svg:last-of-type { background: #1A1A1A; padding: 2px; border-radius: 1px; }
|
| 592 |
+
|
| 593 |
+
/* Map spec */
|
| 594 |
+
.map-spec-grid { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: 24px; align-items: stretch; }
|
| 595 |
+
.map-spec-preview { border: 1px solid var(--ink); aspect-ratio: 8 / 5.6; position: relative; overflow: hidden; }
|
| 596 |
+
.map-spec-caption { position: absolute; bottom: 0; left: 0; right: 0; background: rgba(250,250,247,0.92); padding: 6px 12px; font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); border-top: 1px solid var(--rule-soft); }
|
| 597 |
+
.map-spec-code { font-family: var(--font-mono); font-size: 11px; line-height: 1.55; background: var(--ink); color: var(--paper); padding: 16px; margin: 0; overflow-x: auto; max-height: 560px; overflow-y: auto; }
|
| 598 |
+
|
| 599 |
+
@media (max-width: 900px) { .map-spec-grid { grid-template-columns: 1fr; } }
|
| 600 |
+
|
| 601 |
+
/* Layouts */
|
| 602 |
+
.layout-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; }
|
| 603 |
+
.layout-fig { margin: 0; }
|
| 604 |
+
.layout-fig-cap { display: flex; justify-content: space-between; align-items: baseline; padding-bottom: 8px; border-bottom: 1px solid var(--rule-soft); margin-bottom: 12px; }
|
| 605 |
+
.layout-fig-meta { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
| 606 |
+
.layout-canvas { background: var(--paper); border: 1px solid var(--ink); display: grid; gap: 4px; padding: 8px; }
|
| 607 |
+
.layout-canvas-desktop { grid-template-columns: 5fr 7fr; grid-template-rows: 220px 80px 60px; aspect-ratio: 16/10; }
|
| 608 |
+
.layout-canvas-tablet { grid-template-rows: 160px 120px 80px; aspect-ratio: 4/5; }
|
| 609 |
+
.layout-canvas-mobile { grid-template-rows: 180px 100px 100px 60px; aspect-ratio: 9/16; max-height: 540px; }
|
| 610 |
+
.layout-region { background: var(--paper-deep); border: 1px dashed var(--rule-soft); padding: 10px; font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); display: flex; flex-direction: column; justify-content: space-between; }
|
| 611 |
+
.layout-region-w { color: var(--ink-tertiary); font-size: 10px; }
|
| 612 |
+
.layout-canvas-desktop .layout-region-brief { grid-row: 1 / span 2; }
|
| 613 |
+
.layout-canvas-desktop .layout-region-map { grid-column: 2; }
|
| 614 |
+
.layout-canvas-desktop .layout-region-evidence { grid-column: 1 / span 2; }
|
| 615 |
+
.layout-canvas-desktop .layout-region-trace { grid-column: 1 / span 2; background: var(--paper); }
|
| 616 |
+
.layout-region-brief { background: rgba(11,83,148,0.08); border-color: var(--tier-empirical); }
|
| 617 |
+
.layout-region-map { background: rgba(42,111,168,0.08); border-color: var(--tier-modeled); }
|
| 618 |
+
.layout-region-evidence { background: rgba(107,107,107,0.08); }
|
| 619 |
+
.layout-region-tabs { background: rgba(11,83,148,0.06); }
|
| 620 |
+
.layout-region-trace { background: var(--paper-deep); border-style: solid; }
|
| 621 |
+
|
| 622 |
+
/* PDF spec */
|
| 623 |
+
.pdf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 24px; align-items: start; }
|
| 624 |
+
.pdf-page { aspect-ratio: 210 / 297; background: var(--paper); border: 1px solid var(--rule-soft); box-shadow: 0 12px 32px -16px rgba(0,0,0,0.18); position: relative; overflow: hidden; }
|
| 625 |
+
.pdf-page-inner { position: absolute; inset: 0; padding: 28px 28px; display: flex; flex-direction: column; gap: 14px; font-size: 10px; line-height: 1.45; }
|
| 626 |
+
.pdf-cover-band { display: flex; justify-content: space-between; padding-bottom: 12px; border-bottom: 2px solid var(--ink); font-family: var(--font-mono); font-size: 9px; color: var(--ink-secondary); }
|
| 627 |
+
.pdf-cover-band-meta { letter-spacing: 0.06em; }
|
| 628 |
+
.pdf-cover-headline { padding: 24px 0; }
|
| 629 |
+
.pdf-cover-eyebrow { font-family: var(--font-mono); font-size: 9px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-tertiary); }
|
| 630 |
+
.pdf-cover-title { font-family: var(--font-serif); font-size: 28px; font-weight: 600; line-height: 1.1; margin: 8px 0 6px; letter-spacing: -0.01em; }
|
| 631 |
+
.pdf-cover-deck { font-family: var(--font-mono); font-size: 10px; color: var(--ink-secondary); margin: 0; }
|
| 632 |
+
.pdf-cover-meta { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 16px; border-top: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft); padding: 12px 0; font-size: 10px; }
|
| 633 |
+
.pdf-cover-meta div { display: flex; gap: 6px; }
|
| 634 |
+
.pdf-cover-meta dt { font-family: var(--font-mono); font-size: 9px; color: var(--ink-tertiary); text-transform: uppercase; letter-spacing: 0.08em; min-width: 88px; margin: 0; }
|
| 635 |
+
.pdf-cover-meta dd { margin: 0; font-size: 10px; }
|
| 636 |
+
.pdf-cover-models ul { margin: 6px 0 0; padding-left: 14px; font-family: var(--font-mono); font-size: 9px; line-height: 1.6; color: var(--ink-secondary); }
|
| 637 |
+
.pdf-cover-foot { margin-top: auto; display: flex; justify-content: space-between; padding-top: 12px; border-top: 1px solid var(--ink); font-family: var(--font-mono); font-size: 9px; color: var(--ink-secondary); }
|
| 638 |
+
.pdf-cover-foot a { color: var(--ink); }
|
| 639 |
+
|
| 640 |
+
.pdf-running-head { display: flex; justify-content: space-between; padding-bottom: 8px; border-bottom: 1px solid var(--rule-soft); font-family: var(--font-mono); font-size: 9px; color: var(--ink-tertiary); }
|
| 641 |
+
.pdf-running-foot { margin-top: auto; padding-top: 8px; border-top: 1px solid var(--rule-soft); font-family: var(--font-mono); font-size: 8px; color: var(--ink-tertiary); }
|
| 642 |
+
.pdf-h2 { font-family: var(--font-sans); font-size: 14px; font-weight: 600; margin: 8px 0 6px; }
|
| 643 |
+
.pdf-h3 { font-family: var(--font-sans); font-size: 11px; font-weight: 600; margin: 8px 0 4px; }
|
| 644 |
+
.pdf-prose { font-family: var(--font-serif); font-size: 10px; line-height: 1.5; }
|
| 645 |
+
.pdf-prose p { margin: 0 0 8px; padding-left: 16px; position: relative; }
|
| 646 |
+
.pdf-margin-glyph { position: absolute; left: 0; top: 4px; }
|
| 647 |
+
.pdf-prose sup { color: var(--tier-empirical); font-family: var(--font-sans); font-weight: 500; }
|
| 648 |
+
.pdf-cite-list { font-family: var(--font-serif); font-size: 9px; line-height: 1.55; padding-left: 16px; margin: 8px 0; }
|
| 649 |
+
.pdf-cite-list li { margin-bottom: 6px; }
|
| 650 |
+
.pdf-cite-list span { font-family: var(--font-mono); font-size: 8px; color: var(--ink-tertiary); }
|
| 651 |
+
|
| 652 |
+
/* A11y */
|
| 653 |
+
.a11y-grid { display: grid; gap: 0; border-top: 1px solid var(--rule-soft); }
|
| 654 |
+
.a11y-row { display: grid; grid-template-columns: 200px 1fr; gap: 24px; padding: 12px 0; border-bottom: 1px solid var(--rule-soft); font-size: 13px; align-items: baseline; }
|
| 655 |
+
.a11y-key { padding-top: 2px; }
|
| 656 |
+
.a11y-val { color: var(--ink-secondary); line-height: 1.55; }
|
| 657 |
+
|
| 658 |
+
/* Refs */
|
| 659 |
+
.ref-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px; }
|
| 660 |
+
.ref-card { background: var(--paper); border: 1px solid var(--rule-soft); display: flex; flex-direction: column; }
|
| 661 |
+
.ref-card-thumb { aspect-ratio: 200/140; border-bottom: 1px solid var(--rule-soft); }
|
| 662 |
+
.ref-card-body { padding: 14px; display: flex; flex-direction: column; gap: 6px; }
|
| 663 |
+
.ref-card-name { margin: 0; font-size: 14px; font-weight: 600; }
|
| 664 |
+
.ref-card-borrow { font-size: 12px; color: var(--ink-secondary); line-height: 1.5; margin: 0; }
|
| 665 |
+
.ref-card-url { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
| 666 |
+
|
| 667 |
+
/* Rationale */
|
| 668 |
+
.spec-section-quote { background: var(--paper); }
|
| 669 |
+
.rationale-quote { margin: 0; max-width: 70ch; border-left: 3px solid var(--ink); padding: 16px 0 16px 24px; }
|
| 670 |
+
.rationale-quote p { font-family: var(--font-serif); font-size: 19px; line-height: 1.55; margin: 0 0 16px; text-wrap: pretty; }
|
| 671 |
+
.rationale-cite { display: block; font-family: var(--font-mono); font-size: 12px; color: var(--ink-tertiary); font-style: normal; }
|
| 672 |
+
|
| 673 |
+
/* Footer */
|
| 674 |
+
.app-footer { background: var(--ink); color: var(--paper); }
|
| 675 |
+
.app-footer-inner { max-width: 1600px; margin: 0 auto; padding: 32px 28px; display: flex; flex-direction: column; gap: 12px; }
|
| 676 |
+
.app-footer-guard { font-size: 14px; line-height: 1.5; max-width: 70ch; margin: 0; }
|
| 677 |
+
.app-footer-guard a { color: var(--accent-graphical); border-bottom: 1px solid var(--accent-graphical); text-decoration: none; }
|
| 678 |
+
.app-footer-build { font-family: var(--font-mono); font-size: 11px; color: rgba(250,250,247,0.55); margin: 0; letter-spacing: 0.04em; }
|
| 679 |
+
|
| 680 |
+
/* Dark mode , deferred to v0.5 (see §17). Partial styles removed in v0.4.2. */
|
| 681 |
+
|
| 682 |
+
/* ════════════════════════════════════════════════════════════════════
|
| 683 |
+
v0.4.2 APPENDIX STYLES
|
| 684 |
+
§11 loading · §12 errors · §13 guardian · §14 stripe · §15 register
|
| 685 |
+
§16 caveats · §17 dark mode · §18 print · §19 changelog
|
| 686 |
+
════════════════════════════════════════════════════════════════════ */
|
| 687 |
+
|
| 688 |
+
/* v0.4.2 banner */
|
| 689 |
+
.v042-band { background: var(--paper); border-top: 1px solid var(--rule-soft); }
|
| 690 |
+
.v042-banner { background: linear-gradient(180deg, #F4EFE5 0%, var(--paper) 100%); border: 1px solid var(--rule-soft); padding: 36px 32px; margin-bottom: 48px; }
|
| 691 |
+
.v042-banner-inner { display: grid; grid-template-columns: 1.4fr 1fr; gap: 40px; align-items: start; }
|
| 692 |
+
.v042-banner-title { font-family: var(--font-serif); font-size: 30px; font-weight: 600; line-height: 1.18; margin: 8px 0 12px; letter-spacing: -0.01em; }
|
| 693 |
+
.v042-banner-deck { font-size: 15px; line-height: 1.6; color: var(--ink-secondary); max-width: 60ch; }
|
| 694 |
+
.v042-toc { display: grid; grid-template-columns: 1fr 1fr; gap: 0; border: 1px solid var(--rule-soft); }
|
| 695 |
+
.v042-toc-item { display: flex; gap: 10px; padding: 10px 14px; border-right: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft); font-size: 13px; color: var(--ink); text-decoration: none; align-items: baseline; background: white; }
|
| 696 |
+
.v042-toc-item:nth-child(2n) { border-right: none; }
|
| 697 |
+
.v042-toc-item:hover { background: #F8F4EA; }
|
| 698 |
+
.v042-toc-n { font-family: var(--font-mono); font-size: 10px; color: var(--ink-tertiary); letter-spacing: 0.06em; min-width: 28px; }
|
| 699 |
+
|
| 700 |
+
/* §11 Loading + skeleton + reroll */
|
| 701 |
+
.loading-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-bottom: 28px; }
|
| 702 |
+
.loading-fig { display: flex; flex-direction: column; gap: 10px; }
|
| 703 |
+
.loading-cap { display: flex; flex-direction: column; gap: 4px; }
|
| 704 |
+
.loading-cap-meta { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.04em; }
|
| 705 |
+
.loading-frame { background: white; border: 1px solid var(--rule-soft); padding: 20px; min-height: 320px; }
|
| 706 |
+
|
| 707 |
+
.skeleton-brief { display: flex; flex-direction: column; gap: 18px; }
|
| 708 |
+
.skeleton-status { display: flex; flex-direction: column; gap: 6px; padding-bottom: 12px; border-bottom: 1px solid var(--rule-soft); }
|
| 709 |
+
.skeleton-section { display: flex; flex-direction: column; gap: 8px; }
|
| 710 |
+
.skeleton-head { display: flex; align-items: baseline; gap: 12px; padding-bottom: 4px; }
|
| 711 |
+
.skeleton-num { font-family: var(--font-mono); font-size: 10px; color: var(--ink-tertiary); letter-spacing: 0.08em; }
|
| 712 |
+
.skeleton-label { font-family: var(--font-serif); font-size: 16px; font-weight: 600; color: var(--ink); }
|
| 713 |
+
.skeleton-spinner { font-family: var(--font-mono); color: var(--tier-modeled); font-size: 12px; animation: skeletonBlink 1.1s ease-in-out infinite; margin-left: auto; }
|
| 714 |
+
.skeleton-pulse { display: block; height: 12px; background: linear-gradient(90deg, #ECE8DD 0%, #DAD4C5 50%, #ECE8DD 100%); background-size: 200% 100%; animation: skeletonShimmer 1.6s ease-in-out infinite; border-radius: 1px; }
|
| 715 |
+
.skeleton-pulse-meta { height: 9px; }
|
| 716 |
+
@keyframes skeletonShimmer { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }
|
| 717 |
+
@keyframes skeletonBlink { 0%, 100% { opacity: 0.3; } 50% { opacity: 1; } }
|
| 718 |
+
@media (prefers-reduced-motion: reduce) {
|
| 719 |
+
.skeleton-pulse { animation: none; background: #E2DCCC; }
|
| 720 |
+
.skeleton-spinner { animation: none; opacity: 0.6; }
|
| 721 |
+
}
|
| 722 |
+
|
| 723 |
+
.reroll-banner { display: flex; align-items: center; gap: 12px; padding: 12px 16px; background: rgba(42,111,168,0.08); border-left: 3px solid var(--tier-modeled); margin-bottom: 14px; }
|
| 724 |
+
.reroll-body { display: flex; flex-direction: column; gap: 2px; flex: 1; }
|
| 725 |
+
.reroll-head { font-family: var(--font-sans); font-size: 14px; font-weight: 500; color: var(--ink); }
|
| 726 |
+
.reroll-sub { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.03em; }
|
| 727 |
+
.reroll-spinner { font-size: 16px; color: var(--tier-modeled); animation: rerollSpin 2s linear infinite; }
|
| 728 |
+
@keyframes rerollSpin { 100% { transform: rotate(360deg); } }
|
| 729 |
+
.reroll-prev { opacity: 0.4; pointer-events: none; }
|
| 730 |
+
.reroll-prev-line { font-family: var(--font-serif); font-size: 14px; line-height: 1.55; color: var(--ink); margin-bottom: 8px; }
|
| 731 |
+
|
| 732 |
+
.loading-rules { padding: 18px 22px; background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--tier-empirical); }
|
| 733 |
+
.loading-rules ul { margin: 8px 0 0 18px; }
|
| 734 |
+
.loading-rules li { font-size: 13px; line-height: 1.6; margin-bottom: 4px; color: var(--ink-secondary); }
|
| 735 |
+
|
| 736 |
+
/* §12 Errors */
|
| 737 |
+
.error-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
|
| 738 |
+
.error-card { background: white; border: 1px solid var(--rule-soft); padding: 22px 24px; display: flex; flex-direction: column; gap: 10px; }
|
| 739 |
+
.error-card-head { display: flex; align-items: center; gap: 8px; padding-bottom: 8px; border-bottom: 1px solid var(--rule-soft); }
|
| 740 |
+
.error-card-eyebrow { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.06em; text-transform: uppercase; }
|
| 741 |
+
.error-card-headline { font-family: var(--font-serif); font-size: 19px; font-weight: 600; line-height: 1.3; margin: 0; }
|
| 742 |
+
.error-card-body { font-size: 14px; line-height: 1.55; color: var(--ink-secondary); margin: 0; }
|
| 743 |
+
.error-card-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
|
| 744 |
+
.error-card-action { font-family: var(--font-sans); font-size: 12px; font-weight: 500; padding: 7px 14px; border: 1px solid var(--ink); background: white; cursor: pointer; letter-spacing: 0.02em; }
|
| 745 |
+
.error-card-action.is-primary { background: var(--ink); color: var(--paper); border-color: var(--ink); }
|
| 746 |
+
.error-card-action:hover { background: #F4EFE5; }
|
| 747 |
+
.error-card-action.is-primary:hover { background: #2A2A2A; }
|
| 748 |
+
.error-card-foot { display: flex; flex-direction: column; gap: 2px; padding-top: 10px; margin-top: 4px; border-top: 1px dashed var(--rule-soft); }
|
| 749 |
+
.error-card-foot-copy { font-family: var(--font-mono); font-size: 10px; color: var(--ink-tertiary); letter-spacing: 0.03em; }
|
| 750 |
+
|
| 751 |
+
/* §13 Guardian */
|
| 752 |
+
.guardian-tabs { display: flex; gap: 0; border: 1px solid var(--rule-soft); margin-bottom: 0; flex-wrap: wrap; }
|
| 753 |
+
.guardian-tab { font-family: var(--font-sans); font-size: 12px; font-weight: 500; padding: 11px 16px; background: white; border: none; border-right: 1px solid var(--rule-soft); cursor: pointer; flex: 1; min-width: 180px; text-align: left; color: var(--ink-secondary); letter-spacing: 0.01em; }
|
| 754 |
+
.guardian-tab:last-child { border-right: none; }
|
| 755 |
+
.guardian-tab.is-active { background: var(--ink); color: var(--paper); }
|
| 756 |
+
.guardian-tab:hover:not(.is-active) { background: #F8F4EA; color: var(--ink); }
|
| 757 |
+
|
| 758 |
+
.guardian-card { background: white; border: 1px solid var(--rule-soft); border-top: none; padding: 32px 36px; display: flex; flex-direction: column; gap: 14px; }
|
| 759 |
+
.guardian-head { display: flex; align-items: baseline; gap: 8px; }
|
| 760 |
+
.guardian-eyebrow { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.06em; text-transform: uppercase; }
|
| 761 |
+
.guardian-title { font-family: var(--font-serif); font-size: 24px; font-weight: 600; line-height: 1.25; margin: 0; }
|
| 762 |
+
.guardian-body { font-family: var(--font-serif); font-size: 16px; line-height: 1.6; color: var(--ink); max-width: 60ch; margin: 0; }
|
| 763 |
+
.guardian-redirect { display: flex; flex-direction: column; gap: 4px; padding: 14px 18px; border: 1px solid var(--ink); background: #FBF8EF; text-decoration: none; color: var(--ink); margin: 6px 0; max-width: 480px; }
|
| 764 |
+
.guardian-redirect:hover { background: #F4EFE5; }
|
| 765 |
+
.guardian-redirect-label { font-family: var(--font-sans); font-size: 14px; font-weight: 600; }
|
| 766 |
+
.guardian-redirect-url { font-family: var(--font-mono); font-size: 12px; color: var(--ink-secondary); letter-spacing: 0.02em; }
|
| 767 |
+
.guardian-no-redirect { padding: 14px 18px; border-left: 2px solid var(--ink-tertiary); background: #FBF8EF; max-width: 540px; }
|
| 768 |
+
.guardian-no-redirect p { font-size: 13px; line-height: 1.55; color: var(--ink-secondary); margin: 4px 0 0; }
|
| 769 |
+
.guardian-foot { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.03em; padding-top: 12px; border-top: 1px dashed var(--rule-soft); }
|
| 770 |
+
|
| 771 |
+
.guardian-a11y { padding: 18px 22px; background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--tier-empirical); margin-top: 22px; }
|
| 772 |
+
.guardian-a11y p { font-size: 13px; line-height: 1.6; color: var(--ink-secondary); margin: 6px 0 0; }
|
| 773 |
+
.guardian-a11y em { font-family: var(--font-serif); font-style: italic; color: var(--ink); }
|
| 774 |
+
|
| 775 |
+
/* §14 syn-stripe */
|
| 776 |
+
.stripe-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-bottom: 22px; }
|
| 777 |
+
.stripe-cell { display: flex; flex-direction: column; gap: 8px; padding: 18px; background: white; border: 1px solid var(--rule-soft); }
|
| 778 |
+
.stripe-preview { width: 100%; height: 180px; border: 1px solid var(--rule-soft); background-color: #FAFAF7; overflow: hidden; }
|
| 779 |
+
.stripe-preview > div { background-repeat: repeat; background-size: 12px 12px !important; }
|
| 780 |
+
.stripe-code { font-family: var(--font-mono); font-size: 10px; line-height: 1.5; color: var(--ink-secondary); background: #FBF8EF; padding: 10px 12px; border: 1px solid var(--rule-soft); white-space: pre-wrap; overflow-x: auto; max-height: 180px; overflow-y: auto; }
|
| 781 |
+
.stripe-code-wide { max-height: 360px; }
|
| 782 |
+
.stripe-reg { padding: 18px; background: white; border: 1px solid var(--rule-soft); margin-bottom: 18px; }
|
| 783 |
+
.stripe-reg .section-label { display: block; margin-bottom: 8px; }
|
| 784 |
+
.stripe-data { padding: 14px 18px; background: white; border: 1px solid var(--rule-soft); }
|
| 785 |
+
.stripe-data .section-label { display: block; margin-bottom: 6px; }
|
| 786 |
+
.stripe-data-uri { display: block; font-family: var(--font-mono); font-size: 10px; color: var(--ink-secondary); word-break: break-all; line-height: 1.5; max-height: 80px; overflow-y: auto; }
|
| 787 |
+
|
| 788 |
+
/* §15 Register card */
|
| 789 |
+
.register-frame { padding: 28px; background: var(--paper); border: 1px solid var(--rule-soft); margin-bottom: 22px; }
|
| 790 |
+
.register-card { background: white; border: 1px solid var(--rule-soft); padding: 22px 26px; max-width: 760px; }
|
| 791 |
+
.register-card-head { display: flex; justify-content: space-between; align-items: center; padding-bottom: 10px; border-bottom: 1px solid var(--rule-soft); margin-bottom: 14px; }
|
| 792 |
+
.register-card-source { display: flex; align-items: center; gap: 6px; }
|
| 793 |
+
.register-card-source-label { font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); letter-spacing: 0.04em; }
|
| 794 |
+
.register-card-vintage { font-family: var(--font-mono); font-size: 10px; color: var(--ink-tertiary); letter-spacing: 0.05em; }
|
| 795 |
+
.register-card-title { font-family: var(--font-serif); margin: 0 0 16px; display: flex; align-items: baseline; gap: 10px; line-height: 1.2; }
|
| 796 |
+
.register-card-count { font-size: 32px; font-weight: 600; color: var(--ink); }
|
| 797 |
+
.register-card-type { font-size: 16px; font-weight: 400; color: var(--ink-secondary); }
|
| 798 |
+
.register-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
| 799 |
+
.register-table thead th { font-family: var(--font-mono); font-size: 10px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-tertiary); padding: 6px 8px; text-align: left; border-bottom: 1px solid var(--rule-soft); }
|
| 800 |
+
.register-row { border-bottom: 1px solid #ECE8DD; cursor: pointer; transition: background 0.12s; }
|
| 801 |
+
.register-row:hover { background: #FBF8EF; }
|
| 802 |
+
.register-row.is-open { background: #F4EFE5; }
|
| 803 |
+
.register-row td { padding: 10px 8px; vertical-align: middle; }
|
| 804 |
+
.register-row-glyph { display: flex; align-items: center; gap: 4px; }
|
| 805 |
+
.register-row-name { font-family: var(--font-sans); font-weight: 500; color: var(--ink); }
|
| 806 |
+
.register-yes { color: var(--tier-empirical); font-weight: 600; }
|
| 807 |
+
.register-no { color: var(--ink-tertiary); }
|
| 808 |
+
.register-detail td { padding: 0 8px 14px; background: #F4EFE5; border-bottom: 1px solid var(--rule-soft); }
|
| 809 |
+
.register-detail-grid { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 14px; padding: 14px 0; }
|
| 810 |
+
.register-detail-grid > div { display: flex; flex-direction: column; gap: 4px; }
|
| 811 |
+
.register-detail-grid p { font-size: 12px; line-height: 1.45; color: var(--ink-secondary); margin: 0; }
|
| 812 |
+
.register-card-foot { display: flex; justify-content: space-between; align-items: center; padding-top: 12px; margin-top: 14px; border-top: 1px dashed var(--rule-soft); }
|
| 813 |
+
.register-foot-note { font-size: 12px; color: var(--ink-tertiary); font-style: italic; max-width: 50ch; }
|
| 814 |
+
|
| 815 |
+
.register-rules { padding: 18px 22px; background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--tier-empirical); }
|
| 816 |
+
.register-rules ul { margin: 8px 0 0 18px; }
|
| 817 |
+
.register-rules li { font-size: 13px; line-height: 1.6; margin-bottom: 4px; color: var(--ink-secondary); }
|
| 818 |
+
|
| 819 |
+
/* §16 caveats */
|
| 820 |
+
.caveat-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0; }
|
| 821 |
+
.caveat-list li { padding: 14px 18px; background: white; border: 1px solid var(--rule-soft); border-bottom: none; font-size: 14px; line-height: 1.55; color: var(--ink-secondary); }
|
| 822 |
+
.caveat-list li:last-child { border-bottom: 1px solid var(--rule-soft); }
|
| 823 |
+
.caveat-list strong { color: var(--ink); font-weight: 600; }
|
| 824 |
+
|
| 825 |
+
/* §17 dark mode */
|
| 826 |
+
.darkmode-rules { padding: 18px 22px; background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--tier-proxy); }
|
| 827 |
+
.darkmode-rules ul { margin: 8px 0 0 18px; }
|
| 828 |
+
.darkmode-rules li { font-size: 13px; line-height: 1.6; margin-bottom: 4px; color: var(--ink-secondary); }
|
| 829 |
+
|
| 830 |
+
/* §18 print */
|
| 831 |
+
.print-css-block { font-family: var(--font-mono); font-size: 11px; line-height: 1.55; color: var(--ink); background: #FBF8EF; padding: 18px 22px; border: 1px solid var(--rule-soft); white-space: pre; overflow-x: auto; }
|
| 832 |
+
|
| 833 |
+
/* §19 changelog */
|
| 834 |
+
.spec-section-changelog .changelog-list { list-style: none; padding: 0; margin: 0; }
|
| 835 |
+
.changelog-row { display: grid; grid-template-columns: 60px 1fr 90px; gap: 16px; padding: 12px 16px; border-bottom: 1px solid var(--rule-soft); font-size: 13px; align-items: center; }
|
| 836 |
+
.changelog-row:last-child { border-bottom: none; }
|
| 837 |
+
.changelog-n { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.06em; }
|
| 838 |
+
.changelog-label { color: var(--ink); }
|
| 839 |
+
.changelog-status { font-family: var(--font-mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--tier-empirical); text-align: right; }
|
| 840 |
+
|
| 841 |
+
/* ════════════════════════════════════════════════════════════════════
|
| 842 |
+
v0.4.3 , The Five Stones UI
|
| 843 |
+
§20 taxonomy · §21 trace rework · §22 cold-start · §23 methodology
|
| 844 |
+
§24 reusability · §25 a11y · §26 CSS deltas · §27 rationale
|
| 845 |
+
════════════════════════════════════════════════════════════════════ */
|
| 846 |
+
|
| 847 |
+
/* New tokens (Stones-band + warning status) */
|
| 848 |
+
:root {
|
| 849 |
+
--status-warning: var(--accent-graphical);
|
| 850 |
+
--status-warning-soft: rgba(209, 124, 0, 0.10);
|
| 851 |
+
--status-error: #B8620A;
|
| 852 |
+
--status-error-soft: rgba(184, 98, 10, 0.08);
|
| 853 |
+
--stone-band-rule: var(--ink);
|
| 854 |
+
--stone-band-bg: #FBF8EF;
|
| 855 |
+
--stone-band-bg-active: #F4EFE5;
|
| 856 |
+
}
|
| 857 |
+
|
| 858 |
+
/* §20 Banner + Stone strip */
|
| 859 |
+
.v043-banner-frame { padding: 0 0 32px; border-top: 1px solid var(--rule-soft); }
|
| 860 |
+
.v043-banner { background: linear-gradient(180deg, #F4EFE5 0%, var(--paper) 100%); border: 1px solid var(--rule-soft); padding: 36px 32px; display: grid; grid-template-columns: 1.1fr 1.4fr; gap: 36px; align-items: start; }
|
| 861 |
+
.v043-banner-title { font-family: var(--font-serif); font-size: 30px; font-weight: 600; line-height: 1.18; margin: 8px 0 12px; letter-spacing: -0.01em; }
|
| 862 |
+
.v043-banner-deck { font-size: 15px; line-height: 1.62; color: var(--ink-secondary); max-width: 60ch; }
|
| 863 |
+
.v043-banner-deck em { font-family: var(--font-serif); font-style: italic; color: var(--ink); }
|
| 864 |
+
|
| 865 |
+
.v043-stones-strip { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 0; border: 1px solid var(--rule-soft); background: white; counter-reset: stone; }
|
| 866 |
+
.v043-stones-strip li:nth-child(5) { grid-column: span 2; }
|
| 867 |
+
.v043-stone-chip { padding: 14px 16px; border-right: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft); display: flex; flex-direction: column; gap: 3px; counter-increment: stone; position: relative; }
|
| 868 |
+
.v043-stone-chip:nth-child(2n) { border-right: none; }
|
| 869 |
+
.v043-stone-chip:nth-last-child(-n+1) { border-bottom: none; }
|
| 870 |
+
.v043-stone-chip::before { content: counter(stone, decimal-leading-zero); position: absolute; top: 12px; right: 16px; font-family: var(--font-mono); font-size: 10px; color: var(--ink-tertiary); letter-spacing: 0.06em; }
|
| 871 |
+
.v043-stone-name { font-family: var(--font-sans); font-size: 16px; font-weight: 600; color: var(--ink); }
|
| 872 |
+
.v043-stone-role { font-family: var(--font-sans); font-size: 12px; color: var(--ink-secondary); letter-spacing: 0.01em; }
|
| 873 |
+
.v043-stone-tag { font-family: var(--font-serif); font-style: italic; font-size: 13px; color: var(--ink-tertiary); }
|
| 874 |
+
|
| 875 |
+
.v043-toc { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; border: 1px solid var(--rule-soft); margin-bottom: 36px; }
|
| 876 |
+
.v043-toc-item { display: flex; gap: 10px; padding: 10px 14px; border-right: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft); font-size: 13px; color: var(--ink); text-decoration: none; align-items: baseline; background: white; }
|
| 877 |
+
.v043-toc-item:nth-child(4n) { border-right: none; }
|
| 878 |
+
.v043-toc-item:nth-last-child(-n+4):nth-child(n+5) { border-bottom: none; }
|
| 879 |
+
.v043-toc-item:hover { background: #F8F4EA; }
|
| 880 |
+
.v043-toc-n { font-family: var(--font-mono); font-size: 10px; color: var(--ink-tertiary); letter-spacing: 0.06em; min-width: 28px; }
|
| 881 |
+
|
| 882 |
+
/* §20 Stones taxonomy table */
|
| 883 |
+
.stones-table { width: 100%; border-collapse: collapse; font-size: 13px; background: white; border: 1px solid var(--rule-soft); }
|
| 884 |
+
.stones-table thead th { font-family: var(--font-mono); font-size: 10px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-tertiary); padding: 10px 12px; text-align: left; border-bottom: 1px solid var(--rule-soft); background: #FBF8EF; }
|
| 885 |
+
.stones-row td { padding: 14px 12px; vertical-align: top; border-bottom: 1px solid #ECE8DD; }
|
| 886 |
+
.stones-row:last-child td { border-bottom: none; }
|
| 887 |
+
.stones-row-stone { display: block; font-family: var(--font-sans); font-size: 15px; font-weight: 600; color: var(--ink); }
|
| 888 |
+
.stones-row-tag { display: block; font-family: var(--font-serif); font-style: italic; font-size: 12px; color: var(--ink-tertiary); margin-top: 2px; }
|
| 889 |
+
.stones-row-role { font-family: var(--font-sans); color: var(--ink-secondary); white-space: nowrap; }
|
| 890 |
+
.stones-row-posture { line-height: 1.55; color: var(--ink); }
|
| 891 |
+
.stones-row-count { white-space: nowrap; }
|
| 892 |
+
.stones-row-num { font-family: var(--font-serif); font-size: 22px; font-weight: 600; color: var(--ink); margin-right: 6px; }
|
| 893 |
+
.stones-row-numlabel { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
| 894 |
+
.stones-row-reuse { font-size: 12px; color: var(--ink-secondary); line-height: 1.5; max-width: 28ch; }
|
| 895 |
+
.stones-row-reuse-muted { color: var(--ink-tertiary); font-style: italic; }
|
| 896 |
+
|
| 897 |
+
.stones-pullquote { font-family: var(--font-serif); font-style: italic; font-size: 22px; line-height: 1.45; color: var(--ink); padding: 28px 32px; margin: 28px 0 0; border-left: 3px solid var(--ink); background: #FBF8EF; max-width: 64ch; }
|
| 898 |
+
|
| 899 |
+
/* §21 Treatment tabs */
|
| 900 |
+
.treatment-tabs { display: flex; border: 1px solid var(--rule-soft); margin-bottom: 0; }
|
| 901 |
+
.treatment-tab { font-family: var(--font-sans); font-size: 12px; font-weight: 500; padding: 12px 18px; background: white; border: none; border-right: 1px solid var(--rule-soft); cursor: pointer; flex: 1; color: var(--ink-secondary); letter-spacing: 0.02em; text-align: left; }
|
| 902 |
+
.treatment-tab:last-child { border-right: none; }
|
| 903 |
+
.treatment-tab.is-active { background: var(--ink); color: var(--paper); }
|
| 904 |
+
.treatment-tab:hover:not(.is-active) { background: #F8F4EA; color: var(--ink); }
|
| 905 |
+
|
| 906 |
+
.treatment-frame { background: white; border: 1px solid var(--rule-soft); padding: 28px 32px; display: grid; grid-template-columns: 1fr 1.4fr; gap: 32px; align-items: start; }
|
| 907 |
+
.treatment-frame-stacked { grid-template-columns: 1fr; gap: 24px; margin-bottom: 18px; }
|
| 908 |
+
.treatment-meta { display: flex; flex-direction: column; gap: 12px; }
|
| 909 |
+
.treatment-deck { font-size: 14px; line-height: 1.6; color: var(--ink-secondary); }
|
| 910 |
+
.treatment-deck code { font-family: var(--font-mono); font-size: 12px; background: #FBF8EF; padding: 1px 4px; border: 1px solid var(--rule-soft); }
|
| 911 |
+
.treatment-tradeoffs { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; font-size: 13px; line-height: 1.55; }
|
| 912 |
+
.treatment-tradeoffs li { color: var(--ink-secondary); padding-left: 18px; position: relative; }
|
| 913 |
+
.treatment-tradeoffs strong { position: absolute; left: 0; font-family: var(--font-mono); color: var(--ink); }
|
| 914 |
+
|
| 915 |
+
.treatment-rec { background: white; border: 1px solid var(--rule-soft); padding: 32px 36px; }
|
| 916 |
+
.treatment-rec-title { font-family: var(--font-serif); font-size: 26px; font-weight: 600; margin: 8px 0 14px; }
|
| 917 |
+
.treatment-rec p { font-family: var(--font-serif); font-size: 15px; line-height: 1.6; color: var(--ink); max-width: 64ch; margin: 0 0 12px; }
|
| 918 |
+
.treatment-rec p code { font-family: var(--font-mono); font-size: 12px; background: #FBF8EF; padding: 1px 5px; border: 1px solid var(--rule-soft); font-weight: 500; }
|
| 919 |
+
.treatment-rec p em { font-family: var(--font-serif); font-style: italic; color: var(--ink-secondary); }
|
| 920 |
+
.treatment-rec-rules { list-style: none; padding: 16px 20px; margin: 16px 0 0; background: #FBF8EF; border-left: 3px solid var(--ink); display: flex; flex-direction: column; gap: 8px; max-width: 64ch; }
|
| 921 |
+
.treatment-rec-rules li { font-size: 13px; line-height: 1.55; color: var(--ink-secondary); }
|
| 922 |
+
|
| 923 |
+
/* §21 Stone band (Treatment A) */
|
| 924 |
+
.trace-ui-v43-a { background: var(--paper); border: 1px solid var(--rule-soft); }
|
| 925 |
+
.trace-ui-v43-a .trace-head { padding: 14px 18px; border-bottom: 1px solid var(--rule-soft); display: flex; justify-content: space-between; align-items: center; }
|
| 926 |
+
.trace-head-grouping { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.06em; }
|
| 927 |
+
.trace-head-warning { color: var(--status-warning); }
|
| 928 |
+
.trace-head-error { color: var(--status-error); }
|
| 929 |
+
|
| 930 |
+
.stone-band { border-top: 1.5px solid var(--stone-band-rule); background: white; }
|
| 931 |
+
.stone-band:first-of-type { border-top: none; }
|
| 932 |
+
.stone-band-head { display: grid; grid-template-columns: 18px minmax(180px, auto) 1fr auto; gap: 14px; padding: 14px 18px; background: var(--stone-band-bg); font-family: var(--font-sans); border: none; width: 100%; text-align: left; cursor: pointer; align-items: baseline; }
|
| 933 |
+
.stone-band.is-open .stone-band-head { background: var(--stone-band-bg-active); }
|
| 934 |
+
.stone-band-head:hover { background: #EFE9DA; }
|
| 935 |
+
.stone-band-toggle { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); }
|
| 936 |
+
.stone-band-name { display: flex; align-items: baseline; gap: 4px; }
|
| 937 |
+
.stone-band-stone { font-weight: 600; font-size: 15px; color: var(--ink); }
|
| 938 |
+
.stone-band-role { color: var(--ink-secondary); font-size: 13px; }
|
| 939 |
+
.stone-band-tag { font-family: var(--font-serif); font-style: italic; color: var(--ink-tertiary); font-size: 13px; }
|
| 940 |
+
.stone-band-agg { font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); letter-spacing: 0.04em; white-space: nowrap; }
|
| 941 |
+
.stone-agg-fired { color: var(--ink); }
|
| 942 |
+
.stone-agg-silent { color: var(--ink-tertiary); }
|
| 943 |
+
.stone-agg-warn { color: var(--status-warning); font-weight: 600; }
|
| 944 |
+
.stone-agg-error { color: var(--status-error); font-weight: 600; }
|
| 945 |
+
.stone-agg-ms { color: var(--ink-tertiary); }
|
| 946 |
+
.stone-agg-sep { color: var(--ink-tertiary); margin: 0 6px; }
|
| 947 |
+
.stone-band-body { padding: 8px 0 14px; }
|
| 948 |
+
.stone-band-body .trace-row { padding-top: 6px; padding-bottom: 6px; }
|
| 949 |
+
|
| 950 |
+
/* shared specialist row in v0.4.3 */
|
| 951 |
+
.trace-ui-v43-a .trace-row, .trace-ui-v43-b .trace-row {
|
| 952 |
+
display: grid; grid-template-columns: 16px 16px 1fr auto auto; gap: 10px; align-items: baseline;
|
| 953 |
+
font-family: var(--font-sans); font-size: 13px;
|
| 954 |
+
}
|
| 955 |
+
.trace-ui-v43-a .trace-row .trace-name, .trace-ui-v43-b .trace-row .trace-name {
|
| 956 |
+
font-family: var(--font-mono); font-size: 12px; color: var(--ink);
|
| 957 |
+
}
|
| 958 |
+
.trace-row-warning { background: var(--status-warning-soft); border-left: 2px solid var(--status-warning); }
|
| 959 |
+
.trace-row-error { background: var(--status-error-soft); border-left: 2px solid var(--status-error); }
|
| 960 |
+
.trace-warning-tag { color: var(--status-warning); font-weight: 600; font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; }
|
| 961 |
+
.trace-error-tag { color: var(--status-error); font-weight: 600; font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; }
|
| 962 |
+
.trace-warning-summary { color: var(--status-warning); font-style: italic; font-family: var(--font-sans); font-size: 12px; }
|
| 963 |
+
.trace-error-summary { color: var(--status-error); font-style: italic; font-family: var(--font-sans); font-size: 12px; }
|
| 964 |
+
.trace-error-trace { font-family: var(--font-mono); font-size: 11px; line-height: 1.55; color: var(--status-error); background: var(--status-error-soft); padding: 12px 16px; margin: 4px 18px 8px; border-left: 2px solid var(--status-error); white-space: pre; overflow-x: auto; }
|
| 965 |
+
|
| 966 |
+
.trace-ttm-group { padding: 6px 0; }
|
| 967 |
+
.trace-ttm-group > summary { display: grid; grid-template-columns: 16px 16px 1fr auto; gap: 10px; align-items: baseline; cursor: pointer; padding: 4px 0; list-style: none; }
|
| 968 |
+
.trace-ttm-group > summary::-webkit-details-marker { display: none; }
|
| 969 |
+
.trace-ttm-group > summary .trace-name { font-family: var(--font-mono); font-size: 12px; }
|
| 970 |
+
|
| 971 |
+
/* §21 Treatment B , rule-marked groups */
|
| 972 |
+
.trace-ui-v43-b .trace-body-flat { padding: 14px 18px; }
|
| 973 |
+
.trace-ui-v43-b .trace-col-heads { display: grid; grid-template-columns: 1fr auto auto; gap: 12px; padding: 6px 0 8px; border-bottom: 1px solid var(--rule-soft); font-family: var(--font-mono); font-size: 10px; color: var(--ink-tertiary); letter-spacing: 0.06em; }
|
| 974 |
+
.stone-rule-group { border-top: 2px solid var(--ink); padding-top: 10px; margin-top: 14px; }
|
| 975 |
+
.stone-rule-group:first-of-type { border-top: 1px solid var(--rule-soft); margin-top: 6px; padding-top: 8px; }
|
| 976 |
+
.stone-rule-marker { display: flex; align-items: baseline; gap: 10px; padding: 4px 0 8px; flex-wrap: wrap; }
|
| 977 |
+
.stone-rule-name { font-family: var(--font-sans); font-size: 14px; font-weight: 600; color: var(--ink); }
|
| 978 |
+
.stone-rule-role { font-family: var(--font-serif); font-style: italic; color: var(--ink-tertiary); font-size: 12px; }
|
| 979 |
+
.stone-rule-agg { font-family: var(--font-mono); font-size: 10px; color: var(--ink-secondary); letter-spacing: 0.04em; margin-left: auto; }
|
| 980 |
+
|
| 981 |
+
/* §22 cold-start (in-app) thesis line */
|
| 982 |
+
.cold-start-thesis { font-family: var(--font-sans); font-size: 15px; line-height: 1.55; color: var(--ink); margin: 6px 0 14px; padding-bottom: 12px; border-bottom: 1px solid var(--rule-soft); }
|
| 983 |
+
.cold-start-thesis strong { font-weight: 600; }
|
| 984 |
+
|
| 985 |
+
/* §22 cold-start v43 spec mock */
|
| 986 |
+
.cold-v43-frame { padding: 28px; background: var(--paper); border: 1px solid var(--rule-soft); margin-bottom: 18px; }
|
| 987 |
+
.cold-v43-mock { background: white; border: 1px solid var(--rule-soft); padding: 24px 28px; max-width: 600px; }
|
| 988 |
+
.cold-v43-thesis { font-family: var(--font-sans); font-size: 16px; line-height: 1.55; color: var(--ink); margin: 8px 0 14px; padding: 14px 0; border-top: 1px solid var(--rule-soft); border-bottom: 1px solid var(--rule-soft); }
|
| 989 |
+
.cold-v43-thesis strong { font-weight: 600; }
|
| 990 |
+
.cold-v43-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; }
|
| 991 |
+
.cold-v43-list li { font-size: 13px; line-height: 1.55; color: var(--ink-secondary); padding-left: 14px; position: relative; }
|
| 992 |
+
.cold-v43-list li::before { content: "·"; position: absolute; left: 0; color: var(--ink); font-weight: 700; }
|
| 993 |
+
.cold-v43-list a { color: var(--accent-graphical); text-decoration: none; border-bottom: 1px solid var(--accent-graphical); }
|
| 994 |
+
.cold-v43-rules { padding: 18px 22px; background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--tier-empirical); }
|
| 995 |
+
.cold-v43-rules ul { margin: 8px 0 0 18px; }
|
| 996 |
+
.cold-v43-rules li { font-size: 13px; line-height: 1.6; margin-bottom: 4px; color: var(--ink-secondary); }
|
| 997 |
+
|
| 998 |
+
/* §23 methodology + 5×4 matrix */
|
| 999 |
+
.methodology-toc { list-style: none; padding: 0; margin: 0 0 36px; counter-reset: section; }
|
| 1000 |
+
.methodology-toc li { padding: 12px 16px; background: white; border: 1px solid var(--rule-soft); border-bottom: none; font-size: 14px; line-height: 1.55; color: var(--ink-secondary); }
|
| 1001 |
+
.methodology-toc li:last-child { border-bottom: 1px solid var(--rule-soft); }
|
| 1002 |
+
.methodology-toc strong { color: var(--ink); }
|
| 1003 |
+
|
| 1004 |
+
.tier-stone-matrix { background: white; border: 1px solid var(--rule-soft); padding: 24px 28px; margin: 0; }
|
| 1005 |
+
.tier-stone-matrix table { width: 100%; border-collapse: collapse; }
|
| 1006 |
+
.tier-stone-matrix thead th { font-family: var(--font-mono); font-size: 10px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-tertiary); padding: 12px 8px; text-align: center; border-bottom: 1px solid var(--ink); }
|
| 1007 |
+
.tier-stone-matrix thead th:first-child { text-align: left; }
|
| 1008 |
+
.tier-stone-matrix tbody th { text-align: left; padding: 14px 12px; border-bottom: 1px solid #ECE8DD; vertical-align: top; }
|
| 1009 |
+
.matrix-stone-name { display: block; font-family: var(--font-sans); font-size: 14px; font-weight: 600; color: var(--ink); }
|
| 1010 |
+
.matrix-stone-role { display: block; font-family: var(--font-serif); font-style: italic; font-size: 11px; color: var(--ink-tertiary); margin-top: 2px; }
|
| 1011 |
+
.matrix-cell { text-align: center; padding: 14px 8px; border-bottom: 1px solid #ECE8DD; vertical-align: middle; }
|
| 1012 |
+
.matrix-cell-mark { display: flex; align-items: center; justify-content: center; min-height: 18px; }
|
| 1013 |
+
.matrix-cell-label { display: block; font-family: var(--font-mono); font-size: 9px; color: var(--ink-tertiary); letter-spacing: 0.06em; text-transform: uppercase; margin-top: 4px; }
|
| 1014 |
+
.matrix-cell-empty .matrix-cell-label { color: #C9C5B6; }
|
| 1015 |
+
.matrix-cell-passthrough { background: #FBF8EF; }
|
| 1016 |
+
.matrix-empty { font-family: var(--font-serif); font-size: 16px; color: #C9C5B6; }
|
| 1017 |
+
.matrix-passthrough { font-family: var(--font-mono); font-size: 14px; color: var(--ink-tertiary); }
|
| 1018 |
+
.tier-stone-matrix-cap { font-family: var(--font-serif); font-size: 13px; line-height: 1.55; color: var(--ink-secondary); padding-top: 16px; margin-top: 16px; border-top: 1px solid var(--rule-soft); max-width: 80ch; }
|
| 1019 |
+
|
| 1020 |
+
/* §24 reusability */
|
| 1021 |
+
.reuse-statement { font-family: var(--font-serif); font-style: italic; font-size: 18px; line-height: 1.55; color: var(--ink); padding: 24px 28px; margin: 0 0 18px; background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); max-width: 70ch; }
|
| 1022 |
+
.reuse-rules { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
|
| 1023 |
+
.reuse-rules li { padding: 12px 16px; background: white; border: 1px solid var(--rule-soft); font-size: 13px; line-height: 1.55; color: var(--ink-secondary); }
|
| 1024 |
+
.reuse-rules strong { color: var(--ink); }
|
| 1025 |
+
|
| 1026 |
+
/* §25 a11y */
|
| 1027 |
+
.a11y-v43-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
|
| 1028 |
+
.a11y-v43-list li { padding: 14px 18px; background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--tier-empirical); font-size: 13px; line-height: 1.6; color: var(--ink-secondary); }
|
| 1029 |
+
.a11y-v43-list strong { color: var(--ink); }
|
| 1030 |
+
.a11y-v43-list code { font-family: var(--font-mono); font-size: 11px; background: #FBF8EF; padding: 1px 4px; border: 1px solid var(--rule-soft); }
|
| 1031 |
+
.a11y-v43-list em { font-family: var(--font-serif); font-style: italic; color: var(--ink); }
|
| 1032 |
+
|
| 1033 |
+
/* §26 css deltas */
|
| 1034 |
+
.css-delta-block { font-family: var(--font-mono); font-size: 11px; line-height: 1.55; color: var(--ink); background: #FBF8EF; padding: 18px 22px; border: 1px solid var(--rule-soft); white-space: pre; overflow-x: auto; }
|
| 1035 |
+
|
| 1036 |
+
/* §27 rationale (reuses existing if present, else light variant) */
|
| 1037 |
+
.spec-section-rationale .rationale-quote { font-family: var(--font-serif); font-size: 16px; line-height: 1.65; color: var(--ink); padding: 28px 32px; background: white; border: 1px solid var(--rule-soft); border-left: 3px solid var(--ink); margin: 0 0 12px; max-width: 70ch; }
|
| 1038 |
+
.spec-section-rationale .rationale-quote p { margin: 0 0 14px; }
|
| 1039 |
+
.spec-section-rationale .rationale-quote p:last-child { margin: 0; }
|
| 1040 |
+
.spec-section-rationale .rationale-cite { font-family: var(--font-mono); font-size: 11px; color: var(--ink-tertiary); letter-spacing: 0.04em; }
|
| 1041 |
+
|
| 1042 |
+
/* PDF artifact link , pre-rendered, not a client export action */
|
| 1043 |
+
.app-header-pdf {
|
| 1044 |
+
display: inline-flex; align-items: baseline; gap: 6px;
|
| 1045 |
+
font-family: var(--font-mono); font-size: 11px;
|
| 1046 |
+
color: var(--ink); padding: 4px 10px;
|
| 1047 |
+
border: 1px solid var(--rule-soft); background: white;
|
| 1048 |
+
letter-spacing: 0.02em;
|
| 1049 |
+
}
|
| 1050 |
+
.app-header-pdf-icon { font-size: 12px; color: var(--accent-graphical); }
|
| 1051 |
+
.app-header-pdf-meta { font-size: 9px; color: var(--ink-tertiary); letter-spacing: 0.06em; text-transform: uppercase; }
|
| 1052 |
+
|
| 1053 |
+
/* Mobile brief title fallback */
|
| 1054 |
+
@media (max-width: 720px) {
|
| 1055 |
+
.brief-h1 { grid-template-columns: 1fr; }
|
| 1056 |
+
.brief-h1-meta { grid-column: 1; grid-row: 3; text-align: left; align-items: flex-start; }
|
| 1057 |
+
.brief-h1-meta-row { justify-content: flex-start; }
|
| 1058 |
+
.brief-h1-addr { font-size: 24px; }
|
| 1059 |
+
}
|
| 1060 |
+
.v043-stones-strip { grid-template-columns: 1fr; }
|
| 1061 |
+
.v043-stones-strip li:nth-child(5) { grid-column: span 1; }
|
| 1062 |
+
.v043-stone-chip:nth-child(2n) { border-right: none; border-bottom: 1px solid var(--rule-soft); }
|
| 1063 |
+
.v043-toc { grid-template-columns: 1fr 1fr; }
|
| 1064 |
+
.stones-table thead { display: none; }
|
| 1065 |
+
.stones-table tbody td { display: block; padding: 6px 12px; }
|
| 1066 |
+
.stones-row td { border-bottom: none; }
|
| 1067 |
+
.stones-row { display: block; padding: 14px 0; border-bottom: 1px solid #ECE8DD; }
|
| 1068 |
+
.treatment-tabs { flex-direction: column; }
|
| 1069 |
+
.treatment-tab { border-right: none; border-bottom: 1px solid var(--rule-soft); }
|
| 1070 |
+
.treatment-frame { grid-template-columns: 1fr; padding: 18px; }
|
| 1071 |
+
.stone-band-head { grid-template-columns: 16px 1fr; gap: 8px; }
|
| 1072 |
+
.stone-band-tag { grid-column: 2; }
|
| 1073 |
+
.stone-band-agg { grid-column: 2; }
|
| 1074 |
+
.tier-stone-matrix { padding: 16px; }
|
| 1075 |
+
.tier-stone-matrix thead th { font-size: 9px; padding: 6px 4px; }
|
| 1076 |
+
.tier-stone-matrix tbody th { padding: 10px 6px; }
|
| 1077 |
+
.matrix-cell { padding: 10px 4px; }
|
| 1078 |
+
.matrix-stone-name { font-size: 12px; }
|
| 1079 |
+
.stones-pullquote { font-size: 17px; padding: 18px 20px; }
|
| 1080 |
+
}
|
| 1081 |
+
|
| 1082 |
+
@media (prefers-reduced-motion: reduce) {
|
| 1083 |
+
.stone-band, .stone-band-head, .treatment-tab { transition: none; }
|
| 1084 |
+
}
|
| 1085 |
+
|
| 1086 |
+
/* ============================================================
|
| 1087 |
+
v0.4.5 — Stone accent layer + new status enum rules
|
| 1088 |
+
Hint-only treatment: a 3-px left-rule per Stone, scoped to
|
| 1089 |
+
.stone-band[data-stone] and .f-region[data-stone]. Tier
|
| 1090 |
+
swatches inside cards/legend rows are unchanged.
|
| 1091 |
+
See V0.4.5_SPEC.md §2 for the full rationale.
|
| 1092 |
+
============================================================ */
|
| 1093 |
+
|
| 1094 |
+
/* 3-px left rule on Stone-banded sections */
|
| 1095 |
+
.stone-band[data-stone="cornerstone"] { border-left: 3px solid var(--stone-cornerstone); }
|
| 1096 |
+
.stone-band[data-stone="keystone"] { border-left: 3px solid var(--stone-keystone); }
|
| 1097 |
+
.stone-band[data-stone="touchstone"] { border-left: 3px solid var(--stone-touchstone); }
|
| 1098 |
+
.stone-band[data-stone="lodestone"] { border-left: 3px solid var(--stone-lodestone); }
|
| 1099 |
+
.stone-band[data-stone="capstone"] { border-left: 3px solid var(--stone-capstone); }
|
| 1100 |
+
|
| 1101 |
+
.f-region[data-stone="cornerstone"] { border-left: 3px solid var(--stone-cornerstone); }
|
| 1102 |
+
.f-region[data-stone="keystone"] { border-left: 3px solid var(--stone-keystone); }
|
| 1103 |
+
.f-region[data-stone="touchstone"] { border-left: 3px solid var(--stone-touchstone); }
|
| 1104 |
+
.f-region[data-stone="lodestone"] { border-left: 3px solid var(--stone-lodestone); }
|
| 1105 |
+
.f-region[data-stone="capstone"] { border-left: 3px solid var(--stone-capstone); }
|
| 1106 |
+
|
| 1107 |
+
/* Map Layers panel — Stone group dots + soft inset rule */
|
| 1108 |
+
.map-legend-stone {
|
| 1109 |
+
border-left: 2px solid transparent;
|
| 1110 |
+
padding-left: 10px;
|
| 1111 |
+
margin-bottom: 8px;
|
| 1112 |
+
}
|
| 1113 |
+
.map-legend-stone-cornerstone { border-left-color: var(--stone-cornerstone); }
|
| 1114 |
+
.map-legend-stone-keystone { border-left-color: var(--stone-keystone); }
|
| 1115 |
+
.map-legend-stone-touchstone { border-left-color: var(--stone-touchstone); }
|
| 1116 |
+
.map-legend-stone-lodestone { border-left-color: var(--stone-lodestone); }
|
| 1117 |
+
.map-legend-stone-capstone { border-left-color: var(--stone-capstone); }
|
| 1118 |
+
.map-legend-stone-head {
|
| 1119 |
+
display: flex; align-items: baseline; gap: 6px;
|
| 1120 |
+
margin: 8px 0 4px;
|
| 1121 |
+
font-family: var(--font-sans); font-size: 11px; letter-spacing: 0.04em;
|
| 1122 |
+
}
|
| 1123 |
+
.map-legend-stone-dot {
|
| 1124 |
+
width: 8px; height: 8px; border-radius: 50%;
|
| 1125 |
+
display: inline-block; align-self: center;
|
| 1126 |
+
}
|
| 1127 |
+
.map-legend-stone-dot-cornerstone { background: var(--stone-cornerstone); }
|
| 1128 |
+
.map-legend-stone-dot-keystone { background: var(--stone-keystone); }
|
| 1129 |
+
.map-legend-stone-dot-touchstone { background: var(--stone-touchstone); }
|
| 1130 |
+
.map-legend-stone-dot-lodestone { background: var(--stone-lodestone); }
|
| 1131 |
+
.map-legend-stone-dot-capstone { background: var(--stone-capstone); }
|
| 1132 |
+
.map-legend-stone-name { font-weight: 600; color: var(--ink); text-transform: uppercase; letter-spacing: 0.06em; }
|
| 1133 |
+
.map-legend-stone-role { color: var(--ink-tertiary); font-size: 11px; font-style: italic; font-family: var(--font-serif); }
|
| 1134 |
+
|
| 1135 |
+
/* Cold-start thesis row — colored dots beside Stone names */
|
| 1136 |
+
.cold-start-thesis-stone-dot {
|
| 1137 |
+
width: 7px; height: 7px; border-radius: 50%;
|
| 1138 |
+
display: inline-block; vertical-align: middle;
|
| 1139 |
+
margin-right: 5px; transform: translateY(-1px);
|
| 1140 |
+
}
|
| 1141 |
+
.cold-start-thesis-stone-dot-cornerstone { background: var(--stone-cornerstone); }
|
| 1142 |
+
.cold-start-thesis-stone-dot-keystone { background: var(--stone-keystone); }
|
| 1143 |
+
.cold-start-thesis-stone-dot-touchstone { background: var(--stone-touchstone); }
|
| 1144 |
+
.cold-start-thesis-stone-dot-lodestone { background: var(--stone-lodestone); }
|
| 1145 |
+
.cold-start-thesis-stone-dot-capstone { background: var(--stone-capstone); }
|
| 1146 |
+
|
| 1147 |
+
/* New status enum — replaces 0.4.4 anomaly/silent/warn/error rules.
|
| 1148 |
+
v0.4.5 statuses: fired, silent_by_design, warned, errored, not_invoked. */
|
| 1149 |
+
.trace-row { padding: 8px 0; font-family: var(--font-mono); font-size: 12px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
| 1150 |
+
.trace-row .trace-name { color: var(--ink); }
|
| 1151 |
+
.trace-row .trace-status { font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-tertiary); }
|
| 1152 |
+
.trace-row .trace-tier { font-size: 10px; letter-spacing: 0.04em; }
|
| 1153 |
+
.trace-row .trace-ms { color: var(--ink-tertiary); margin-left: auto; }
|
| 1154 |
+
.trace-row .trace-bullet { width: 12px; display: inline-block; text-align: center; }
|
| 1155 |
+
|
| 1156 |
+
.trace-row-fired { /* default — bullet color carries tier */ }
|
| 1157 |
+
|
| 1158 |
+
.trace-row-silent-bd .trace-name { color: var(--ink-secondary); }
|
| 1159 |
+
.trace-row-silent-bd .trace-bullet-silent { color: var(--ink-tertiary); }
|
| 1160 |
+
.trace-row-silent-bd .trace-silent-note { color: var(--ink-tertiary); font-style: italic; font-family: var(--font-serif); font-size: 12px; }
|
| 1161 |
+
|
| 1162 |
+
.trace-row-not-invoked .trace-name { color: var(--ink-tertiary); }
|
| 1163 |
+
.trace-row-not-invoked .trace-bullet-notinvoked { color: var(--ink-tertiary); }
|
| 1164 |
+
|
| 1165 |
+
.trace-row-warned {
|
| 1166 |
+
background: var(--status-warning-soft);
|
| 1167 |
+
/* keep stone left-rule; do NOT set border-left here */
|
| 1168 |
+
position: relative;
|
| 1169 |
+
padding-left: 8px;
|
| 1170 |
+
}
|
| 1171 |
+
.trace-row-warned .trace-status-warn { color: var(--status-warning); font-weight: 600; }
|
| 1172 |
+
.trace-row-warned .trace-warn-sidemark {
|
| 1173 |
+
color: var(--status-warning);
|
| 1174 |
+
font-weight: 700;
|
| 1175 |
+
margin-left: 4px;
|
| 1176 |
+
}
|
| 1177 |
+
.trace-row-warned .trace-warn-note { color: var(--ink-secondary); font-family: var(--font-serif); font-style: italic; font-size: 12px; }
|
| 1178 |
+
|
| 1179 |
+
.trace-row-errored {
|
| 1180 |
+
background: var(--status-error-soft);
|
| 1181 |
+
padding-left: 8px;
|
| 1182 |
+
}
|
| 1183 |
+
.trace-row-errored > summary { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; cursor: pointer; list-style: none; }
|
| 1184 |
+
.trace-row-errored > summary::-webkit-details-marker { display: none; }
|
| 1185 |
+
.trace-row-errored .trace-status-err { color: var(--status-error); font-weight: 600; }
|
| 1186 |
+
.trace-row-errored .trace-error-summary { color: var(--ink-secondary); font-style: italic; font-family: var(--font-serif); font-size: 12px; }
|
| 1187 |
+
.trace-row-errored .trace-error-expand { color: var(--ink-tertiary); font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase; }
|
| 1188 |
+
.trace-row-errored[open] .trace-error-expand { display: none; }
|
| 1189 |
+
.trace-row-errored .trace-error-body {
|
| 1190 |
+
margin-top: 6px;
|
| 1191 |
+
padding: 8px 10px;
|
| 1192 |
+
background: rgba(255,255,255,0.6);
|
| 1193 |
+
border-left: 2px solid var(--status-error);
|
| 1194 |
+
font-size: 11px;
|
| 1195 |
+
display: grid; gap: 4px;
|
| 1196 |
+
}
|
| 1197 |
+
.trace-error-line { display: grid; grid-template-columns: 80px 1fr; gap: 8px; }
|
| 1198 |
+
.trace-error-k { color: var(--ink-tertiary); }
|
| 1199 |
+
|
| 1200 |
+
/* Tally text colors */
|
| 1201 |
+
.f-tally-warn { color: var(--status-warning); }
|
| 1202 |
+
.f-tally-err { color: var(--status-error); }
|
| 1203 |
+
.f-tally-notinvoked { color: var(--ink-tertiary); }
|
| 1204 |
+
|
| 1205 |
+
/* Hover link — when an evidence card is "linked" to a hovered map glyph */
|
| 1206 |
+
.f-card.is-linked,
|
| 1207 |
+
.finding-card.is-linked {
|
| 1208 |
+
outline: 2px solid var(--ink);
|
| 1209 |
+
outline-offset: 2px;
|
| 1210 |
+
}
|
| 1211 |
+
|
| 1212 |
+
/* TerraMind LULC class-mix bar (Touchstone synthetic-prior card) */
|
| 1213 |
+
.lulc-bar { display: flex; height: 12px; border-radius: 2px; overflow: hidden; margin-top: 6px; }
|
| 1214 |
+
.lulc-bar-seg { height: 100%; }
|
| 1215 |
+
.lulc-legend { display: flex; flex-wrap: wrap; gap: 8px 14px; margin-top: 8px; font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); }
|
| 1216 |
+
.lulc-legend-swatch { display: inline-block; width: 8px; height: 8px; margin-right: 4px; border-radius: 1px; vertical-align: middle; }
|
| 1217 |
+
|
| 1218 |
+
/* Fine-tuned TTM trim line + hardware badge */
|
| 1219 |
+
.ttm-ft-trim { border-top: 1px solid var(--rule-soft); margin-top: 10px; padding-top: 8px; display: grid; gap: 4px; font-family: var(--font-mono); font-size: 11px; color: var(--ink-secondary); }
|
| 1220 |
+
.ttm-ft-row { display: grid; grid-template-columns: 110px 1fr; gap: 8px; }
|
| 1221 |
+
.ttm-ft-k { color: var(--ink-tertiary); }
|
| 1222 |
+
.ttm-hw-badge {
|
| 1223 |
+
display: inline-block; padding: 1px 6px; border: 1px solid var(--rule-soft);
|
| 1224 |
+
border-radius: 2px; font-size: 10px; letter-spacing: 0.04em;
|
| 1225 |
+
font-family: var(--font-mono); color: var(--ink-secondary); background: white;
|
| 1226 |
+
}
|
| 1227 |
+
|
| 1228 |
+
/* Print: drop accents to neutral, drop hover/error tints */
|
| 1229 |
+
@media print {
|
| 1230 |
+
.stone-band[data-stone],
|
| 1231 |
+
.f-region[data-stone],
|
| 1232 |
+
.map-legend-stone { border-left-color: #999 !important; }
|
| 1233 |
+
.cold-start-thesis-stone-dot,
|
| 1234 |
+
.map-legend-stone-dot { background: #999 !important; }
|
| 1235 |
+
.trace-row-warned, .trace-row-errored { background: transparent !important; }
|
| 1236 |
+
}
|
| 1237 |
+
|
| 1238 |
+
/* ─────────── original v0.4.2 mobile rules ────────── */
|
| 1239 |
+
@media (max-width: 720px) {
|
| 1240 |
+
.app-header-inner { grid-template-columns: 1fr; gap: 8px; }
|
| 1241 |
+
.app-header-mid, .app-header-right { justify-content: flex-start; }
|
| 1242 |
+
.app-header-query { min-width: 0; width: 100%; }
|
| 1243 |
+
.hero-band-inner { padding: 20px 16px 32px; }
|
| 1244 |
+
.spec-band-inner { padding: 40px 16px 60px; }
|
| 1245 |
+
.spec-band-title { font-size: 28px; }
|
| 1246 |
+
.spec-section-title { font-size: 24px; }
|
| 1247 |
+
.brief-h1 { font-size: 26px; }
|
| 1248 |
+
.map-legend { width: calc(100% - 24px); }
|
| 1249 |
+
.palette-row { grid-template-columns: 48px 1fr; gap: 12px; row-gap: 4px; }
|
| 1250 |
+
.palette-row > *:nth-child(n+3) { grid-column: 2; }
|
| 1251 |
+
.a11y-row { grid-template-columns: 1fr; gap: 4px; }
|
| 1252 |
+
|
| 1253 |
+
/* v0.4.2 mobile */
|
| 1254 |
+
.v042-banner-inner { grid-template-columns: 1fr; gap: 24px; }
|
| 1255 |
+
.v042-toc { grid-template-columns: 1fr; }
|
| 1256 |
+
.v042-toc-item { border-right: none !important; }
|
| 1257 |
+
.v042-banner-title { font-size: 24px; }
|
| 1258 |
+
.loading-grid { grid-template-columns: 1fr; }
|
| 1259 |
+
.error-grid { grid-template-columns: 1fr; }
|
| 1260 |
+
.stripe-grid { grid-template-columns: 1fr; }
|
| 1261 |
+
.guardian-tabs { flex-direction: column; }
|
| 1262 |
+
.guardian-tab { border-right: none !important; border-bottom: 1px solid var(--rule); flex: none; }
|
| 1263 |
+
.guardian-tab:last-child { border-bottom: none; }
|
| 1264 |
+
.guardian-card { padding: 24px 22px; }
|
| 1265 |
+
.guardian-title { font-size: 20px; }
|
| 1266 |
+
.register-card { padding: 18px; }
|
| 1267 |
+
.register-table { font-size: 11px; }
|
| 1268 |
+
.register-table thead th { padding: 4px; font-size: 9px; }
|
| 1269 |
+
.register-row td { padding: 8px 4px; }
|
| 1270 |
+
.register-detail-grid { grid-template-columns: 1fr 1fr; }
|
| 1271 |
+
.register-card-count { font-size: 26px; }
|
| 1272 |
+
.changelog-row { grid-template-columns: 50px 1fr; }
|
| 1273 |
+
.changelog-status { grid-column: 2; text-align: left; padding-top: 2px; }
|
| 1274 |
+
}
|
docs/design_handoff/design_files/tokens.css
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Riprap design tokens
|
| 2 |
+
Civic-tech-clean. SIL OFL / Apache fonts only. WCAG 2.2 AA verified.
|
| 3 |
+
*/
|
| 4 |
+
|
| 5 |
+
:root {
|
| 6 |
+
/* ── Epistemic tier colors ─────────────────────────────────────────
|
| 7 |
+
Refined from starter palette to meet 4.5:1 against white for body
|
| 8 |
+
text where the tier color is also used as inline citation/label color.
|
| 9 |
+
Originals: #3D85C6 = 3.04:1 (fail body), #999999 = 2.85:1 (fail).
|
| 10 |
+
Refined values verified via WebAIM contrast checker, deutan/protan/
|
| 11 |
+
tritan colorblind-safe (tier is also encoded by glyph + label).
|
| 12 |
+
*/
|
| 13 |
+
--tier-empirical: #0B5394; /* 8.59:1 vs white ✓ AAA */
|
| 14 |
+
--tier-empirical-fill: rgba(11, 83, 148, 0.40);
|
| 15 |
+
--tier-empirical-line: #0B5394;
|
| 16 |
+
|
| 17 |
+
--tier-modeled: #2A6FA8; /* 5.41:1 vs white ✓ AA was #3D85C6 (3.04:1 fail) */
|
| 18 |
+
--tier-modeled-fill: rgba(42, 111, 168, 0.25);
|
| 19 |
+
--tier-modeled-line: #2A6FA8;
|
| 20 |
+
|
| 21 |
+
--tier-proxy: #6B6B6B; /* 5.74:1 vs white ✓ AA was #999999 (2.85:1 fail) */
|
| 22 |
+
--tier-proxy-fill: transparent;
|
| 23 |
+
--tier-proxy-line: #6B6B6B;
|
| 24 |
+
|
| 25 |
+
--tier-synthetic: #2A6FA8; /* same hue as modeled , pattern carries the difference */
|
| 26 |
+
--tier-synthetic-fill: rgba(42, 111, 168, 0.25);
|
| 27 |
+
--tier-synthetic-line: #2A6FA8;
|
| 28 |
+
|
| 29 |
+
/* ── Reference + accent ── */
|
| 30 |
+
--reference-bg: #E8E8E6;
|
| 31 |
+
--reference-line: #C9C9C5;
|
| 32 |
+
--accent: #B8620A; /* 4.93:1 vs white ✓ AA adjusted from #D17C00 (3.36:1) */
|
| 33 |
+
--accent-graphical: #D17C00; /* keep original for graphical use only (3.36:1 ≥ 3:1) */
|
| 34 |
+
|
| 35 |
+
/* ── Neutrals (paper register) ── */
|
| 36 |
+
--paper: #FAFAF7; /* warm near-white, USGS report register */
|
| 37 |
+
--paper-deep: #F2F2EE;
|
| 38 |
+
--ink: #1A1A1A; /* 18.5:1 ✓ AAA */
|
| 39 |
+
--ink-secondary: #4A4A4A; /* 9.7:1 ✓ AAA */
|
| 40 |
+
--ink-tertiary: #6B6B6B; /* 5.74:1 ✓ AA */
|
| 41 |
+
--rule: #1A1A1A;
|
| 42 |
+
--rule-soft: #C9C9C5;
|
| 43 |
+
|
| 44 |
+
/* ── Type ── */
|
| 45 |
+
--font-sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
| 46 |
+
--font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
|
| 47 |
+
--font-serif: "IBM Plex Serif", Georgia, "Times New Roman", serif;
|
| 48 |
+
|
| 49 |
+
/* ── Scale ── */
|
| 50 |
+
--measure: 70ch;
|
| 51 |
+
--leading-prose: 1.55;
|
| 52 |
+
--leading-tight: 1.25;
|
| 53 |
+
|
| 54 |
+
/* ── Stone accent tokens (v0.4.5) ──
|
| 55 |
+
Five muted hint-colors keyed to Stones. L≈45 OKLCH, chroma ≤0.04.
|
| 56 |
+
Hint-level decoration; never competes with the four-tier epistemic palette.
|
| 57 |
+
All five degrade to neutral gray in @media print (see below). */
|
| 58 |
+
--stone-cornerstone: #7C6F5E; /* warm taupe */
|
| 59 |
+
--stone-keystone: #5E6E7C; /* cool slate */
|
| 60 |
+
--stone-touchstone: #6B7C66; /* muted sage */
|
| 61 |
+
--stone-lodestone: #7C6E5E; /* softened ochre */
|
| 62 |
+
--stone-capstone: #5E5E6E; /* neutral indigo-gray */
|
| 63 |
+
|
| 64 |
+
/* ── Spacing ── */
|
| 65 |
+
--s-1: 4px;
|
| 66 |
+
--s-2: 8px;
|
| 67 |
+
--s-3: 12px;
|
| 68 |
+
--s-4: 16px;
|
| 69 |
+
--s-5: 24px;
|
| 70 |
+
--s-6: 32px;
|
| 71 |
+
--s-7: 48px;
|
| 72 |
+
--s-8: 64px;
|
| 73 |
+
--s-9: 96px;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
* { box-sizing: border-box; }
|
| 77 |
+
|
| 78 |
+
html, body {
|
| 79 |
+
margin: 0;
|
| 80 |
+
padding: 0;
|
| 81 |
+
background: var(--paper);
|
| 82 |
+
color: var(--ink);
|
| 83 |
+
font-family: var(--font-sans);
|
| 84 |
+
font-size: 16px;
|
| 85 |
+
line-height: var(--leading-prose);
|
| 86 |
+
-webkit-font-smoothing: antialiased;
|
| 87 |
+
text-rendering: optimizeLegibility;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
/* High-contrast focus rings, brief-spec'd */
|
| 91 |
+
:focus-visible {
|
| 92 |
+
outline: 3px solid var(--accent-graphical);
|
| 93 |
+
outline-offset: 2px;
|
| 94 |
+
border-radius: 1px;
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
@media print {
|
| 98 |
+
:root {
|
| 99 |
+
--stone-cornerstone: #999;
|
| 100 |
+
--stone-keystone: #999;
|
| 101 |
+
--stone-touchstone: #999;
|
| 102 |
+
--stone-lodestone: #999;
|
| 103 |
+
--stone-capstone: #999;
|
| 104 |
+
}
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
@media (prefers-reduced-motion: reduce) {
|
| 108 |
+
*, *::before, *::after {
|
| 109 |
+
animation-duration: 0.01ms !important;
|
| 110 |
+
animation-iteration-count: 1 !important;
|
| 111 |
+
transition-duration: 0.01ms !important;
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
/* ── Wordmark ─────────────────────────────────────────────────────── */
|
| 116 |
+
.riprap-wordmark {
|
| 117 |
+
font-family: var(--font-mono);
|
| 118 |
+
font-weight: 600;
|
| 119 |
+
font-size: 14px;
|
| 120 |
+
letter-spacing: 0.06em;
|
| 121 |
+
text-transform: lowercase;
|
| 122 |
+
color: var(--ink);
|
| 123 |
+
display: inline-flex;
|
| 124 |
+
align-items: baseline;
|
| 125 |
+
gap: 0;
|
| 126 |
+
}
|
| 127 |
+
.riprap-wordmark::before {
|
| 128 |
+
content: "▌";
|
| 129 |
+
color: var(--accent-graphical);
|
| 130 |
+
margin-right: 4px;
|
| 131 |
+
font-size: 0.85em;
|
| 132 |
+
position: relative;
|
| 133 |
+
top: 1px;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
/* ── Skip link ── */
|
| 137 |
+
.skip-link {
|
| 138 |
+
position: absolute;
|
| 139 |
+
left: -9999px;
|
| 140 |
+
top: 8px;
|
| 141 |
+
padding: 8px 12px;
|
| 142 |
+
background: var(--ink);
|
| 143 |
+
color: var(--paper);
|
| 144 |
+
font-family: var(--font-mono);
|
| 145 |
+
font-size: 13px;
|
| 146 |
+
z-index: 1000;
|
| 147 |
+
}
|
| 148 |
+
.skip-link:focus {
|
| 149 |
+
left: 8px;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
/* ── Generic section labels ── */
|
| 153 |
+
.section-label {
|
| 154 |
+
font-family: var(--font-mono);
|
| 155 |
+
font-size: 11px;
|
| 156 |
+
font-weight: 500;
|
| 157 |
+
letter-spacing: 0.12em;
|
| 158 |
+
text-transform: uppercase;
|
| 159 |
+
color: var(--ink-tertiary);
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
.rule-thin {
|
| 163 |
+
border: 0;
|
| 164 |
+
border-top: 1px solid var(--rule-soft);
|
| 165 |
+
margin: 0;
|
| 166 |
+
}
|
| 167 |
+
.rule-heavy {
|
| 168 |
+
border: 0;
|
| 169 |
+
border-top: 2px solid var(--ink);
|
| 170 |
+
margin: 0;
|
| 171 |
+
}
|
docs/design_handoff/design_files/trace.jsx
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Trace UI: <details>-based tree of the Burr FSM run.
|
| 2 |
+
Three columns: action name · elapsed ms · epistemic-tier badge.
|
| 3 |
+
Parallel branches shown as sibling rows under a "fan-out" parent;
|
| 4 |
+
convergence step shown as a "merge" node. Reference: Postgres
|
| 5 |
+
EXPLAIN ANALYZE viewers, Apache Airflow DAG.
|
| 6 |
+
*/
|
| 7 |
+
|
| 8 |
+
const TRACE = {
|
| 9 |
+
id: "root",
|
| 10 |
+
name: "briefing.run",
|
| 11 |
+
status: "ok",
|
| 12 |
+
ms: 14820,
|
| 13 |
+
tier: null,
|
| 14 |
+
children: [
|
| 15 |
+
{ id: "n1", name: "geocode.address", status: "ok", ms: 142, tier: null,
|
| 16 |
+
output: { lat: 40.6776, lon: -74.0096, bbl: "3005970030" } },
|
| 17 |
+
{ id: "n2", name: "fan_out.stones", status: "fan", ms: 0, tier: null,
|
| 18 |
+
note: "5 Stones engaged in parallel",
|
| 19 |
+
children: [
|
| 20 |
+
{ id: "s1", name: "sandy_inundation.lookup", status: "ok", ms: 380, tier: "empirical",
|
| 21 |
+
claims: 2, output: "polygon: contains; nearest HWM 0.4mi" },
|
| 22 |
+
{ id: "s2", name: "floodnet.history", status: "ok", ms: 1240, tier: "empirical",
|
| 23 |
+
claims: 1, output: "BK-RH-002: 7 events, peak 14.3cm" },
|
| 24 |
+
{ id: "s3", name: "usgs.high_water_marks", status: "ok", ms: 612, tier: "empirical",
|
| 25 |
+
claims: 1, output: "9 marks within 500ft" },
|
| 26 |
+
{ id: "s4", name: "fema.firm.preliminary", status: "ok", ms: 488, tier: "modeled",
|
| 27 |
+
claims: 1, output: "Zone AE, BFE 11ft NAVD88" },
|
| 28 |
+
{ id: "s5", name: "dep.stormwater.scenario", status: "ok", ms: 2104, tier: "modeled",
|
| 29 |
+
claims: 1, output: "moderate: ponding ≥4in W half" },
|
| 30 |
+
{ id: "s6", name: "npcc4.slr.projection", status: "ok", ms: 320, tier: "modeled",
|
| 31 |
+
claims: 1, output: "2050 90th: +30in" },
|
| 32 |
+
{ id: "s7", name: "nyc311.flood_complaints", status: "ok", ms: 980, tier: "proxy",
|
| 33 |
+
claims: 1, output: "89 calls / tract / 2019–25" },
|
| 34 |
+
{ id: "s8", name: "nfip.claims_aggregate", status: "ok", ms: 540, tier: "proxy",
|
| 35 |
+
claims: 1, output: "$4.1M / 47 paid losses" },
|
| 36 |
+
{ id: "s9", name: "terramind.synthetic_sar", status: "ok", ms: 6840, tier: "synthetic",
|
| 37 |
+
claims: 1, output: "synthesis confidence 0.71" },
|
| 38 |
+
{ id: "s10", name: "tidal_gauge.range", status: "silent", ms: 18, tier: null,
|
| 39 |
+
claims: 0, output: "out of range: nearest gauge >2mi" },
|
| 40 |
+
{ id: "s11", name: "wrp.coastal_risk_area", status: "ok", ms: 210, tier: "modeled",
|
| 41 |
+
claims: 1, output: "within Coastal Risk Area" },
|
| 42 |
+
]
|
| 43 |
+
},
|
| 44 |
+
{ id: "n3", name: "merge.evidence", status: "merge", ms: 92, tier: null,
|
| 45 |
+
note: "10 cards · 1 silent · 0 errors" },
|
| 46 |
+
{ id: "n4", name: "compose.briefing", status: "ok", ms: 1380, tier: null,
|
| 47 |
+
output: "4 sections · 11 claims · 10 citations" },
|
| 48 |
+
{ id: "n5", name: "stream.sse", status: "ok", ms: 4940, tier: null,
|
| 49 |
+
output: "1812 tokens · 11 sentence chunks" },
|
| 50 |
+
]
|
| 51 |
+
};
|
| 52 |
+
|
| 53 |
+
const StatusGlyph = ({ status }) => {
|
| 54 |
+
const map = {
|
| 55 |
+
ok: { fill: "#0B5394", char: "" },
|
| 56 |
+
silent: { fill: "transparent", stroke: "#6B6B6B", char: "" },
|
| 57 |
+
error: { fill: "#B8620A", char: "!" },
|
| 58 |
+
fan: { char: "⤳" },
|
| 59 |
+
merge: { char: "⤺" },
|
| 60 |
+
};
|
| 61 |
+
const m = map[status];
|
| 62 |
+
if (status === "fan" || status === "merge") {
|
| 63 |
+
return <span className="trace-status-glyph" aria-label={status}>{m.char}</span>;
|
| 64 |
+
}
|
| 65 |
+
return (
|
| 66 |
+
<svg width="9" height="9" viewBox="0 0 9 9" aria-label={status}>
|
| 67 |
+
<rect x="0.75" y="0.75" width="7.5" height="7.5" fill={m.fill} stroke={m.stroke || m.fill} strokeWidth="1.5"/>
|
| 68 |
+
</svg>
|
| 69 |
+
);
|
| 70 |
+
};
|
| 71 |
+
|
| 72 |
+
const TraceRow = ({ node, depth = 0, defaultOpen = false }) => {
|
| 73 |
+
const hasChildren = node.children && node.children.length > 0;
|
| 74 |
+
const [open, setOpen] = useState(defaultOpen);
|
| 75 |
+
const indent = depth * 16;
|
| 76 |
+
|
| 77 |
+
return (
|
| 78 |
+
<>
|
| 79 |
+
<div className={`trace-row trace-row-${node.status}`} style={{ paddingLeft: indent + 12 }}>
|
| 80 |
+
<button
|
| 81 |
+
type="button"
|
| 82 |
+
className="trace-row-toggle"
|
| 83 |
+
onClick={() => hasChildren && setOpen(!open)}
|
| 84 |
+
aria-expanded={hasChildren ? open : undefined}
|
| 85 |
+
aria-label={`${node.name}, ${node.ms}ms, ${node.status}`}
|
| 86 |
+
disabled={!hasChildren}
|
| 87 |
+
>
|
| 88 |
+
<span className="trace-tree-glyph" aria-hidden="true">
|
| 89 |
+
{hasChildren ? (open ? "▾" : "▸") : "·"}
|
| 90 |
+
</span>
|
| 91 |
+
<span className="trace-status-col">
|
| 92 |
+
<StatusGlyph status={node.status} />
|
| 93 |
+
</span>
|
| 94 |
+
<span className="trace-name-col">
|
| 95 |
+
<span className="trace-name">{node.name}</span>
|
| 96 |
+
{node.note && <span className="trace-note"> · {node.note}</span>}
|
| 97 |
+
</span>
|
| 98 |
+
<span className="trace-ms-col">{node.ms}ms</span>
|
| 99 |
+
<span className="trace-tier-col">
|
| 100 |
+
{node.tier && <TierBadge tier={node.tier} compact />}
|
| 101 |
+
{node.status === "silent" && (
|
| 102 |
+
<span className="trace-silent-tag">silent</span>
|
| 103 |
+
)}
|
| 104 |
+
</span>
|
| 105 |
+
</button>
|
| 106 |
+
{open && node.output && (
|
| 107 |
+
<div className="trace-output" style={{ paddingLeft: indent + 44 }}>
|
| 108 |
+
<span className="trace-output-prefix">→</span>
|
| 109 |
+
<span className="trace-output-text">
|
| 110 |
+
{typeof node.output === "string" ? node.output : JSON.stringify(node.output)}
|
| 111 |
+
</span>
|
| 112 |
+
{node.claims != null && (
|
| 113 |
+
<span className="trace-output-claims">{node.claims} claim{node.claims === 1 ? "" : "s"} cited</span>
|
| 114 |
+
)}
|
| 115 |
+
</div>
|
| 116 |
+
)}
|
| 117 |
+
</div>
|
| 118 |
+
{open && hasChildren && node.children.map((c) => (
|
| 119 |
+
<TraceRow key={c.id} node={c} depth={depth + 1} defaultOpen={c.status === "fan"} />
|
| 120 |
+
))}
|
| 121 |
+
</>
|
| 122 |
+
);
|
| 123 |
+
};
|
| 124 |
+
|
| 125 |
+
const TraceUI = ({ collapsed, onToggleCollapsed }) => {
|
| 126 |
+
return (
|
| 127 |
+
<section className={`trace-ui ${collapsed ? "is-collapsed" : ""}`} aria-label="Run trace">
|
| 128 |
+
<header className="trace-head">
|
| 129 |
+
<div className="trace-head-left">
|
| 130 |
+
<span className="section-label">Run trace</span>
|
| 131 |
+
<span className="trace-head-meta">
|
| 132 |
+
<span className="trace-head-stat">14.82s total</span>
|
| 133 |
+
<span className="trace-head-sep">·</span>
|
| 134 |
+
<span className="trace-head-stat">10 fired</span>
|
| 135 |
+
<span className="trace-head-sep">·</span>
|
| 136 |
+
<span className="trace-head-stat trace-head-silent">1 silent</span>
|
| 137 |
+
<span className="trace-head-sep">·</span>
|
| 138 |
+
<span className="trace-head-stat">0 errors</span>
|
| 139 |
+
</span>
|
| 140 |
+
</div>
|
| 141 |
+
<button
|
| 142 |
+
type="button"
|
| 143 |
+
className="trace-collapse-btn"
|
| 144 |
+
onClick={onToggleCollapsed}
|
| 145 |
+
aria-expanded={!collapsed}
|
| 146 |
+
>
|
| 147 |
+
{collapsed ? "Expand ▾" : "Collapse ▴"}
|
| 148 |
+
</button>
|
| 149 |
+
</header>
|
| 150 |
+
{!collapsed && (
|
| 151 |
+
<div className="trace-body">
|
| 152 |
+
<div className="trace-col-heads">
|
| 153 |
+
<span className="trace-col-head trace-col-head-name">action</span>
|
| 154 |
+
<span className="trace-col-head trace-col-head-ms">elapsed</span>
|
| 155 |
+
<span className="trace-col-head trace-col-head-tier">tier</span>
|
| 156 |
+
</div>
|
| 157 |
+
<div className="trace-tree" role="tree">
|
| 158 |
+
<TraceRow node={TRACE} defaultOpen={true} />
|
| 159 |
+
</div>
|
| 160 |
+
</div>
|
| 161 |
+
)}
|
| 162 |
+
</section>
|
| 163 |
+
);
|
| 164 |
+
};
|
| 165 |
+
|
| 166 |
+
Object.assign(window, { TraceUI });
|
docs/design_handoff/design_files/tweaks-panel.jsx
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
// tweaks-panel.jsx
|
| 3 |
+
// Reusable Tweaks shell + form-control helpers.
|
| 4 |
+
//
|
| 5 |
+
// Owns the host protocol (listens for __activate_edit_mode / __deactivate_edit_mode,
|
| 6 |
+
// posts __edit_mode_available / __edit_mode_set_keys / __edit_mode_dismissed) so
|
| 7 |
+
// individual prototypes don't re-roll it. Ships a consistent set of controls so you
|
| 8 |
+
// don't hand-draw <input type="range">, segmented radios, steppers, etc.
|
| 9 |
+
//
|
| 10 |
+
// Usage (in an HTML file that loads React + Babel):
|
| 11 |
+
//
|
| 12 |
+
// const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
|
| 13 |
+
// "primaryColor": "#D97757",
|
| 14 |
+
// "fontSize": 16,
|
| 15 |
+
// "density": "regular",
|
| 16 |
+
// "dark": false
|
| 17 |
+
// }/*EDITMODE-END*/;
|
| 18 |
+
//
|
| 19 |
+
// function App() {
|
| 20 |
+
// const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
|
| 21 |
+
// return (
|
| 22 |
+
// <div style={{ fontSize: t.fontSize, color: t.primaryColor }}>
|
| 23 |
+
// Hello
|
| 24 |
+
// <TweaksPanel>
|
| 25 |
+
// <TweakSection label="Typography" />
|
| 26 |
+
// <TweakSlider label="Font size" value={t.fontSize} min={10} max={32} unit="px"
|
| 27 |
+
// onChange={(v) => setTweak('fontSize', v)} />
|
| 28 |
+
// <TweakRadio label="Density" value={t.density}
|
| 29 |
+
// options={['compact', 'regular', 'comfy']}
|
| 30 |
+
// onChange={(v) => setTweak('density', v)} />
|
| 31 |
+
// <TweakSection label="Theme" />
|
| 32 |
+
// <TweakColor label="Primary" value={t.primaryColor}
|
| 33 |
+
// onChange={(v) => setTweak('primaryColor', v)} />
|
| 34 |
+
// <TweakToggle label="Dark mode" value={t.dark}
|
| 35 |
+
// onChange={(v) => setTweak('dark', v)} />
|
| 36 |
+
// </TweaksPanel>
|
| 37 |
+
// </div>
|
| 38 |
+
// );
|
| 39 |
+
// }
|
| 40 |
+
//
|
| 41 |
+
// ─────────────────────────────────────────────────────────────────────────────
|
| 42 |
+
|
| 43 |
+
const __TWEAKS_STYLE = `
|
| 44 |
+
.twk-panel{position:fixed;right:16px;bottom:16px;z-index:2147483646;width:280px;
|
| 45 |
+
max-height:calc(100vh - 32px);display:flex;flex-direction:column;
|
| 46 |
+
background:rgba(250,249,247,.78);color:#29261b;
|
| 47 |
+
-webkit-backdrop-filter:blur(24px) saturate(160%);backdrop-filter:blur(24px) saturate(160%);
|
| 48 |
+
border:.5px solid rgba(255,255,255,.6);border-radius:14px;
|
| 49 |
+
box-shadow:0 1px 0 rgba(255,255,255,.5) inset,0 12px 40px rgba(0,0,0,.18);
|
| 50 |
+
font:11.5px/1.4 ui-sans-serif,system-ui,-apple-system,sans-serif;overflow:hidden}
|
| 51 |
+
.twk-hd{display:flex;align-items:center;justify-content:space-between;
|
| 52 |
+
padding:10px 8px 10px 14px;cursor:move;user-select:none}
|
| 53 |
+
.twk-hd b{font-size:12px;font-weight:600;letter-spacing:.01em}
|
| 54 |
+
.twk-x{appearance:none;border:0;background:transparent;color:rgba(41,38,27,.55);
|
| 55 |
+
width:22px;height:22px;border-radius:6px;cursor:default;font-size:13px;line-height:1}
|
| 56 |
+
.twk-x:hover{background:rgba(0,0,0,.06);color:#29261b}
|
| 57 |
+
.twk-body{padding:2px 14px 14px;display:flex;flex-direction:column;gap:10px;
|
| 58 |
+
overflow-y:auto;overflow-x:hidden;min-height:0;
|
| 59 |
+
scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.15) transparent}
|
| 60 |
+
.twk-body::-webkit-scrollbar{width:8px}
|
| 61 |
+
.twk-body::-webkit-scrollbar-track{background:transparent;margin:2px}
|
| 62 |
+
.twk-body::-webkit-scrollbar-thumb{background:rgba(0,0,0,.15);border-radius:4px;
|
| 63 |
+
border:2px solid transparent;background-clip:content-box}
|
| 64 |
+
.twk-body::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.25);
|
| 65 |
+
border:2px solid transparent;background-clip:content-box}
|
| 66 |
+
.twk-row{display:flex;flex-direction:column;gap:5px}
|
| 67 |
+
.twk-row-h{flex-direction:row;align-items:center;justify-content:space-between;gap:10px}
|
| 68 |
+
.twk-lbl{display:flex;justify-content:space-between;align-items:baseline;
|
| 69 |
+
color:rgba(41,38,27,.72)}
|
| 70 |
+
.twk-lbl>span:first-child{font-weight:500}
|
| 71 |
+
.twk-val{color:rgba(41,38,27,.5);font-variant-numeric:tabular-nums}
|
| 72 |
+
|
| 73 |
+
.twk-sect{font-size:10px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
|
| 74 |
+
color:rgba(41,38,27,.45);padding:10px 0 0}
|
| 75 |
+
.twk-sect:first-child{padding-top:0}
|
| 76 |
+
|
| 77 |
+
.twk-field{appearance:none;width:100%;height:26px;padding:0 8px;
|
| 78 |
+
border:.5px solid rgba(0,0,0,.1);border-radius:7px;
|
| 79 |
+
background:rgba(255,255,255,.6);color:inherit;font:inherit;outline:none}
|
| 80 |
+
.twk-field:focus{border-color:rgba(0,0,0,.25);background:rgba(255,255,255,.85)}
|
| 81 |
+
select.twk-field{padding-right:22px;
|
| 82 |
+
background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='rgba(0,0,0,.5)' d='M0 0h10L5 6z'/></svg>");
|
| 83 |
+
background-repeat:no-repeat;background-position:right 8px center}
|
| 84 |
+
|
| 85 |
+
.twk-slider{appearance:none;-webkit-appearance:none;width:100%;height:4px;margin:6px 0;
|
| 86 |
+
border-radius:999px;background:rgba(0,0,0,.12);outline:none}
|
| 87 |
+
.twk-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
|
| 88 |
+
width:14px;height:14px;border-radius:50%;background:#fff;
|
| 89 |
+
border:.5px solid rgba(0,0,0,.12);box-shadow:0 1px 3px rgba(0,0,0,.2);cursor:default}
|
| 90 |
+
.twk-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;
|
| 91 |
+
background:#fff;border:.5px solid rgba(0,0,0,.12);box-shadow:0 1px 3px rgba(0,0,0,.2);cursor:default}
|
| 92 |
+
|
| 93 |
+
.twk-seg{position:relative;display:flex;padding:2px;border-radius:8px;
|
| 94 |
+
background:rgba(0,0,0,.06);user-select:none}
|
| 95 |
+
.twk-seg-thumb{position:absolute;top:2px;bottom:2px;border-radius:6px;
|
| 96 |
+
background:rgba(255,255,255,.9);box-shadow:0 1px 2px rgba(0,0,0,.12);
|
| 97 |
+
transition:left .15s cubic-bezier(.3,.7,.4,1),width .15s}
|
| 98 |
+
.twk-seg.dragging .twk-seg-thumb{transition:none}
|
| 99 |
+
.twk-seg button{appearance:none;position:relative;z-index:1;flex:1;border:0;
|
| 100 |
+
background:transparent;color:inherit;font:inherit;font-weight:500;min-height:22px;
|
| 101 |
+
border-radius:6px;cursor:default;padding:4px 6px;line-height:1.2;
|
| 102 |
+
overflow-wrap:anywhere}
|
| 103 |
+
|
| 104 |
+
.twk-toggle{position:relative;width:32px;height:18px;border:0;border-radius:999px;
|
| 105 |
+
background:rgba(0,0,0,.15);transition:background .15s;cursor:default;padding:0}
|
| 106 |
+
.twk-toggle[data-on="1"]{background:#34c759}
|
| 107 |
+
.twk-toggle i{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;
|
| 108 |
+
background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:transform .15s}
|
| 109 |
+
.twk-toggle[data-on="1"] i{transform:translateX(14px)}
|
| 110 |
+
|
| 111 |
+
.twk-num{display:flex;align-items:center;height:26px;padding:0 0 0 8px;
|
| 112 |
+
border:.5px solid rgba(0,0,0,.1);border-radius:7px;background:rgba(255,255,255,.6)}
|
| 113 |
+
.twk-num-lbl{font-weight:500;color:rgba(41,38,27,.6);cursor:ew-resize;
|
| 114 |
+
user-select:none;padding-right:8px}
|
| 115 |
+
.twk-num input{flex:1;min-width:0;height:100%;border:0;background:transparent;
|
| 116 |
+
font:inherit;font-variant-numeric:tabular-nums;text-align:right;padding:0 8px 0 0;
|
| 117 |
+
outline:none;color:inherit;-moz-appearance:textfield}
|
| 118 |
+
.twk-num input::-webkit-inner-spin-button,.twk-num input::-webkit-outer-spin-button{
|
| 119 |
+
-webkit-appearance:none;margin:0}
|
| 120 |
+
.twk-num-unit{padding-right:8px;color:rgba(41,38,27,.45)}
|
| 121 |
+
|
| 122 |
+
.twk-btn{appearance:none;height:26px;padding:0 12px;border:0;border-radius:7px;
|
| 123 |
+
background:rgba(0,0,0,.78);color:#fff;font:inherit;font-weight:500;cursor:default}
|
| 124 |
+
.twk-btn:hover{background:rgba(0,0,0,.88)}
|
| 125 |
+
.twk-btn.secondary{background:rgba(0,0,0,.06);color:inherit}
|
| 126 |
+
.twk-btn.secondary:hover{background:rgba(0,0,0,.1)}
|
| 127 |
+
|
| 128 |
+
.twk-swatch{appearance:none;-webkit-appearance:none;width:56px;height:22px;
|
| 129 |
+
border:.5px solid rgba(0,0,0,.1);border-radius:6px;padding:0;cursor:default;
|
| 130 |
+
background:transparent;flex-shrink:0}
|
| 131 |
+
.twk-swatch::-webkit-color-swatch-wrapper{padding:0}
|
| 132 |
+
.twk-swatch::-webkit-color-swatch{border:0;border-radius:5.5px}
|
| 133 |
+
.twk-swatch::-moz-color-swatch{border:0;border-radius:5.5px}
|
| 134 |
+
`;
|
| 135 |
+
|
| 136 |
+
// ── useTweaks ───────────────────────────────────────────────────────────────
|
| 137 |
+
// Single source of truth for tweak values. setTweak persists via the host
|
| 138 |
+
// (__edit_mode_set_keys → host rewrites the EDITMODE block on disk).
|
| 139 |
+
function useTweaks(defaults) {
|
| 140 |
+
const [values, setValues] = React.useState(defaults);
|
| 141 |
+
// Accepts either setTweak('key', value) or setTweak({ key: value, ... }) so a
|
| 142 |
+
// useState-style call doesn't write a "[object Object]" key into the persisted
|
| 143 |
+
// JSON block.
|
| 144 |
+
const setTweak = React.useCallback((keyOrEdits, val) => {
|
| 145 |
+
const edits = typeof keyOrEdits === 'object' && keyOrEdits !== null
|
| 146 |
+
? keyOrEdits : { [keyOrEdits]: val };
|
| 147 |
+
setValues((prev) => ({ ...prev, ...edits }));
|
| 148 |
+
window.parent.postMessage({ type: '__edit_mode_set_keys', edits }, '*');
|
| 149 |
+
}, []);
|
| 150 |
+
return [values, setTweak];
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
// ── TweaksPanel ─────────────────────────────────────────────────────────────
|
| 154 |
+
// Floating shell. Registers the protocol listener BEFORE announcing
|
| 155 |
+
// availability — if the announce ran first, the host's activate could land
|
| 156 |
+
// before our handler exists and the toolbar toggle would silently no-op.
|
| 157 |
+
// The close button posts __edit_mode_dismissed so the host's toolbar toggle
|
| 158 |
+
// flips off in lockstep; the host echoes __deactivate_edit_mode back which
|
| 159 |
+
// is what actually hides the panel.
|
| 160 |
+
function TweaksPanel({ title = 'Tweaks', children }) {
|
| 161 |
+
const [open, setOpen] = React.useState(false);
|
| 162 |
+
const dragRef = React.useRef(null);
|
| 163 |
+
const offsetRef = React.useRef({ x: 16, y: 16 });
|
| 164 |
+
const PAD = 16;
|
| 165 |
+
|
| 166 |
+
const clampToViewport = React.useCallback(() => {
|
| 167 |
+
const panel = dragRef.current;
|
| 168 |
+
if (!panel) return;
|
| 169 |
+
const w = panel.offsetWidth, h = panel.offsetHeight;
|
| 170 |
+
const maxRight = Math.max(PAD, window.innerWidth - w - PAD);
|
| 171 |
+
const maxBottom = Math.max(PAD, window.innerHeight - h - PAD);
|
| 172 |
+
offsetRef.current = {
|
| 173 |
+
x: Math.min(maxRight, Math.max(PAD, offsetRef.current.x)),
|
| 174 |
+
y: Math.min(maxBottom, Math.max(PAD, offsetRef.current.y)),
|
| 175 |
+
};
|
| 176 |
+
panel.style.right = offsetRef.current.x + 'px';
|
| 177 |
+
panel.style.bottom = offsetRef.current.y + 'px';
|
| 178 |
+
}, []);
|
| 179 |
+
|
| 180 |
+
React.useEffect(() => {
|
| 181 |
+
if (!open) return;
|
| 182 |
+
clampToViewport();
|
| 183 |
+
if (typeof ResizeObserver === 'undefined') {
|
| 184 |
+
window.addEventListener('resize', clampToViewport);
|
| 185 |
+
return () => window.removeEventListener('resize', clampToViewport);
|
| 186 |
+
}
|
| 187 |
+
const ro = new ResizeObserver(clampToViewport);
|
| 188 |
+
ro.observe(document.documentElement);
|
| 189 |
+
return () => ro.disconnect();
|
| 190 |
+
}, [open, clampToViewport]);
|
| 191 |
+
|
| 192 |
+
React.useEffect(() => {
|
| 193 |
+
const onMsg = (e) => {
|
| 194 |
+
const t = e?.data?.type;
|
| 195 |
+
if (t === '__activate_edit_mode') setOpen(true);
|
| 196 |
+
else if (t === '__deactivate_edit_mode') setOpen(false);
|
| 197 |
+
};
|
| 198 |
+
window.addEventListener('message', onMsg);
|
| 199 |
+
window.parent.postMessage({ type: '__edit_mode_available' }, '*');
|
| 200 |
+
return () => window.removeEventListener('message', onMsg);
|
| 201 |
+
}, []);
|
| 202 |
+
|
| 203 |
+
const dismiss = () => {
|
| 204 |
+
setOpen(false);
|
| 205 |
+
window.parent.postMessage({ type: '__edit_mode_dismissed' }, '*');
|
| 206 |
+
};
|
| 207 |
+
|
| 208 |
+
const onDragStart = (e) => {
|
| 209 |
+
const panel = dragRef.current;
|
| 210 |
+
if (!panel) return;
|
| 211 |
+
const r = panel.getBoundingClientRect();
|
| 212 |
+
const sx = e.clientX, sy = e.clientY;
|
| 213 |
+
const startRight = window.innerWidth - r.right;
|
| 214 |
+
const startBottom = window.innerHeight - r.bottom;
|
| 215 |
+
const move = (ev) => {
|
| 216 |
+
offsetRef.current = {
|
| 217 |
+
x: startRight - (ev.clientX - sx),
|
| 218 |
+
y: startBottom - (ev.clientY - sy),
|
| 219 |
+
};
|
| 220 |
+
clampToViewport();
|
| 221 |
+
};
|
| 222 |
+
const up = () => {
|
| 223 |
+
window.removeEventListener('mousemove', move);
|
| 224 |
+
window.removeEventListener('mouseup', up);
|
| 225 |
+
};
|
| 226 |
+
window.addEventListener('mousemove', move);
|
| 227 |
+
window.addEventListener('mouseup', up);
|
| 228 |
+
};
|
| 229 |
+
|
| 230 |
+
if (!open) return null;
|
| 231 |
+
return (
|
| 232 |
+
<>
|
| 233 |
+
<style>{__TWEAKS_STYLE}</style>
|
| 234 |
+
<div ref={dragRef} className="twk-panel" data-noncommentable=""
|
| 235 |
+
style={{ right: offsetRef.current.x, bottom: offsetRef.current.y }}>
|
| 236 |
+
<div className="twk-hd" onMouseDown={onDragStart}>
|
| 237 |
+
<b>{title}</b>
|
| 238 |
+
<button className="twk-x" aria-label="Close tweaks"
|
| 239 |
+
onMouseDown={(e) => e.stopPropagation()}
|
| 240 |
+
onClick={dismiss}>✕</button>
|
| 241 |
+
</div>
|
| 242 |
+
<div className="twk-body">{children}</div>
|
| 243 |
+
</div>
|
| 244 |
+
</>
|
| 245 |
+
);
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
// ── Layout helpers ──────────────────────────────────────────────────────────
|
| 249 |
+
|
| 250 |
+
function TweakSection({ label, children }) {
|
| 251 |
+
return (
|
| 252 |
+
<>
|
| 253 |
+
<div className="twk-sect">{label}</div>
|
| 254 |
+
{children}
|
| 255 |
+
</>
|
| 256 |
+
);
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
function TweakRow({ label, value, children, inline = false }) {
|
| 260 |
+
return (
|
| 261 |
+
<div className={inline ? 'twk-row twk-row-h' : 'twk-row'}>
|
| 262 |
+
<div className="twk-lbl">
|
| 263 |
+
<span>{label}</span>
|
| 264 |
+
{value != null && <span className="twk-val">{value}</span>}
|
| 265 |
+
</div>
|
| 266 |
+
{children}
|
| 267 |
+
</div>
|
| 268 |
+
);
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
// ── Controls ────────────────────────────────────────────────────────────────
|
| 272 |
+
|
| 273 |
+
function TweakSlider({ label, value, min = 0, max = 100, step = 1, unit = '', onChange }) {
|
| 274 |
+
return (
|
| 275 |
+
<TweakRow label={label} value={`${value}${unit}`}>
|
| 276 |
+
<input type="range" className="twk-slider" min={min} max={max} step={step}
|
| 277 |
+
value={value} onChange={(e) => onChange(Number(e.target.value))} />
|
| 278 |
+
</TweakRow>
|
| 279 |
+
);
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
function TweakToggle({ label, value, onChange }) {
|
| 283 |
+
return (
|
| 284 |
+
<div className="twk-row twk-row-h">
|
| 285 |
+
<div className="twk-lbl"><span>{label}</span></div>
|
| 286 |
+
<button type="button" className="twk-toggle" data-on={value ? '1' : '0'}
|
| 287 |
+
role="switch" aria-checked={!!value}
|
| 288 |
+
onClick={() => onChange(!value)}><i /></button>
|
| 289 |
+
</div>
|
| 290 |
+
);
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
function TweakRadio({ label, value, options, onChange }) {
|
| 294 |
+
const trackRef = React.useRef(null);
|
| 295 |
+
const [dragging, setDragging] = React.useState(false);
|
| 296 |
+
const opts = options.map((o) => (typeof o === 'object' ? o : { value: o, label: o }));
|
| 297 |
+
const idx = Math.max(0, opts.findIndex((o) => o.value === value));
|
| 298 |
+
const n = opts.length;
|
| 299 |
+
|
| 300 |
+
// The active value is read by pointer-move handlers attached for the lifetime
|
| 301 |
+
// of a drag — ref it so a stale closure doesn't fire onChange for every move.
|
| 302 |
+
const valueRef = React.useRef(value);
|
| 303 |
+
valueRef.current = value;
|
| 304 |
+
|
| 305 |
+
const segAt = (clientX) => {
|
| 306 |
+
const r = trackRef.current.getBoundingClientRect();
|
| 307 |
+
const inner = r.width - 4;
|
| 308 |
+
const i = Math.floor(((clientX - r.left - 2) / inner) * n);
|
| 309 |
+
return opts[Math.max(0, Math.min(n - 1, i))].value;
|
| 310 |
+
};
|
| 311 |
+
|
| 312 |
+
const onPointerDown = (e) => {
|
| 313 |
+
setDragging(true);
|
| 314 |
+
const v0 = segAt(e.clientX);
|
| 315 |
+
if (v0 !== valueRef.current) onChange(v0);
|
| 316 |
+
const move = (ev) => {
|
| 317 |
+
if (!trackRef.current) return;
|
| 318 |
+
const v = segAt(ev.clientX);
|
| 319 |
+
if (v !== valueRef.current) onChange(v);
|
| 320 |
+
};
|
| 321 |
+
const up = () => {
|
| 322 |
+
setDragging(false);
|
| 323 |
+
window.removeEventListener('pointermove', move);
|
| 324 |
+
window.removeEventListener('pointerup', up);
|
| 325 |
+
};
|
| 326 |
+
window.addEventListener('pointermove', move);
|
| 327 |
+
window.addEventListener('pointerup', up);
|
| 328 |
+
};
|
| 329 |
+
|
| 330 |
+
return (
|
| 331 |
+
<TweakRow label={label}>
|
| 332 |
+
<div ref={trackRef} role="radiogroup" onPointerDown={onPointerDown}
|
| 333 |
+
className={dragging ? 'twk-seg dragging' : 'twk-seg'}>
|
| 334 |
+
<div className="twk-seg-thumb"
|
| 335 |
+
style={{ left: `calc(2px + ${idx} * (100% - 4px) / ${n})`,
|
| 336 |
+
width: `calc((100% - 4px) / ${n})` }} />
|
| 337 |
+
{opts.map((o) => (
|
| 338 |
+
<button key={o.value} type="button" role="radio" aria-checked={o.value === value}>
|
| 339 |
+
{o.label}
|
| 340 |
+
</button>
|
| 341 |
+
))}
|
| 342 |
+
</div>
|
| 343 |
+
</TweakRow>
|
| 344 |
+
);
|
| 345 |
+
}
|
| 346 |
+
|
| 347 |
+
function TweakSelect({ label, value, options, onChange }) {
|
| 348 |
+
return (
|
| 349 |
+
<TweakRow label={label}>
|
| 350 |
+
<select className="twk-field" value={value} onChange={(e) => onChange(e.target.value)}>
|
| 351 |
+
{options.map((o) => {
|
| 352 |
+
const v = typeof o === 'object' ? o.value : o;
|
| 353 |
+
const l = typeof o === 'object' ? o.label : o;
|
| 354 |
+
return <option key={v} value={v}>{l}</option>;
|
| 355 |
+
})}
|
| 356 |
+
</select>
|
| 357 |
+
</TweakRow>
|
| 358 |
+
);
|
| 359 |
+
}
|
| 360 |
+
|
| 361 |
+
function TweakText({ label, value, placeholder, onChange }) {
|
| 362 |
+
return (
|
| 363 |
+
<TweakRow label={label}>
|
| 364 |
+
<input className="twk-field" type="text" value={value} placeholder={placeholder}
|
| 365 |
+
onChange={(e) => onChange(e.target.value)} />
|
| 366 |
+
</TweakRow>
|
| 367 |
+
);
|
| 368 |
+
}
|
| 369 |
+
|
| 370 |
+
function TweakNumber({ label, value, min, max, step = 1, unit = '', onChange }) {
|
| 371 |
+
const clamp = (n) => {
|
| 372 |
+
if (min != null && n < min) return min;
|
| 373 |
+
if (max != null && n > max) return max;
|
| 374 |
+
return n;
|
| 375 |
+
};
|
| 376 |
+
const startRef = React.useRef({ x: 0, val: 0 });
|
| 377 |
+
const onScrubStart = (e) => {
|
| 378 |
+
e.preventDefault();
|
| 379 |
+
startRef.current = { x: e.clientX, val: value };
|
| 380 |
+
const decimals = (String(step).split('.')[1] || '').length;
|
| 381 |
+
const move = (ev) => {
|
| 382 |
+
const dx = ev.clientX - startRef.current.x;
|
| 383 |
+
const raw = startRef.current.val + dx * step;
|
| 384 |
+
const snapped = Math.round(raw / step) * step;
|
| 385 |
+
onChange(clamp(Number(snapped.toFixed(decimals))));
|
| 386 |
+
};
|
| 387 |
+
const up = () => {
|
| 388 |
+
window.removeEventListener('pointermove', move);
|
| 389 |
+
window.removeEventListener('pointerup', up);
|
| 390 |
+
};
|
| 391 |
+
window.addEventListener('pointermove', move);
|
| 392 |
+
window.addEventListener('pointerup', up);
|
| 393 |
+
};
|
| 394 |
+
return (
|
| 395 |
+
<div className="twk-num">
|
| 396 |
+
<span className="twk-num-lbl" onPointerDown={onScrubStart}>{label}</span>
|
| 397 |
+
<input type="number" value={value} min={min} max={max} step={step}
|
| 398 |
+
onChange={(e) => onChange(clamp(Number(e.target.value)))} />
|
| 399 |
+
{unit && <span className="twk-num-unit">{unit}</span>}
|
| 400 |
+
</div>
|
| 401 |
+
);
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
function TweakColor({ label, value, onChange }) {
|
| 405 |
+
return (
|
| 406 |
+
<div className="twk-row twk-row-h">
|
| 407 |
+
<div className="twk-lbl"><span>{label}</span></div>
|
| 408 |
+
<input type="color" className="twk-swatch" value={value}
|
| 409 |
+
onChange={(e) => onChange(e.target.value)} />
|
| 410 |
+
</div>
|
| 411 |
+
);
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
function TweakButton({ label, onClick, secondary = false }) {
|
| 415 |
+
return (
|
| 416 |
+
<button type="button" className={secondary ? 'twk-btn secondary' : 'twk-btn'}
|
| 417 |
+
onClick={onClick}>{label}</button>
|
| 418 |
+
);
|
| 419 |
+
}
|
| 420 |
+
|
| 421 |
+
Object.assign(window, {
|
| 422 |
+
useTweaks, TweaksPanel, TweakSection, TweakRow,
|
| 423 |
+
TweakSlider, TweakToggle, TweakRadio, TweakSelect,
|
| 424 |
+
TweakText, TweakNumber, TweakColor, TweakButton,
|
| 425 |
+
});
|
web/main.py
CHANGED
|
@@ -26,12 +26,17 @@ from app.stones import capstone as _capstone_stone # noqa: E402
|
|
| 26 |
# nta_resolve and friends) don't open a Stone boundary — they're
|
| 27 |
# orientation / policy infrastructure shared across Stones.
|
| 28 |
_STEP_TO_STONE: dict[str, str] = {
|
| 29 |
-
# Cornerstone
|
| 30 |
"sandy_inundation": "Cornerstone",
|
| 31 |
"dep_stormwater": "Cornerstone",
|
| 32 |
"ida_hwm_2021": "Cornerstone",
|
| 33 |
"prithvi_eo_v2": "Cornerstone",
|
| 34 |
"microtopo_lidar": "Cornerstone",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
# Keystone (the chip fetch is infrastructure for the LoRA pair, but
|
| 36 |
# it's logically Keystone-adjacent and we surface it under that
|
| 37 |
# banner so the trace doesn't show a phantom orphan step).
|
|
@@ -49,6 +54,7 @@ _STEP_TO_STONE: dict[str, str] = {
|
|
| 49 |
"noaa_tides": "Touchstone",
|
| 50 |
"prithvi_eo_live": "Touchstone",
|
| 51 |
"terramind_lulc": "Touchstone",
|
|
|
|
| 52 |
# Lodestone
|
| 53 |
"nws_alerts": "Lodestone",
|
| 54 |
"ttm_forecast": "Lodestone",
|
|
@@ -306,14 +312,14 @@ async def api_backend():
|
|
| 306 |
|
| 307 |
@app.get("/")
|
| 308 |
def index():
|
| 309 |
-
"""SvelteKit
|
| 310 |
-
the legacy custom-element agent.html if the SvelteKit build hasn't been
|
| 311 |
-
compiled yet — that lets `uvicorn` boot in a fresh checkout without a
|
| 312 |
-
Node toolchain present."""
|
| 313 |
sk = SVELTEKIT_BUILD / "index.html"
|
| 314 |
if sk.exists():
|
| 315 |
return FileResponse(sk)
|
| 316 |
-
return
|
|
|
|
|
|
|
|
|
|
| 317 |
|
| 318 |
|
| 319 |
@app.get("/q/sample")
|
|
@@ -346,39 +352,11 @@ def print_page(query_id: str): # noqa: ARG001 — captured by the SPA router
|
|
| 346 |
return JSONResponse({"error": "sveltekit build not present"}, status_code=503)
|
| 347 |
|
| 348 |
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
@app.get("/single")
|
| 356 |
-
def single_address_page():
|
| 357 |
-
return FileResponse(STATIC / "index.html")
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
@app.get("/compare")
|
| 361 |
-
def compare_page():
|
| 362 |
-
return FileResponse(STATIC / "compare.html")
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
@app.get("/agent")
|
| 366 |
-
def agent_page():
|
| 367 |
-
return FileResponse(STATIC / "agent.html")
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
@app.get("/report")
|
| 371 |
-
def report_page():
|
| 372 |
-
"""Print-ready auditable report. Reads the prior agent run from
|
| 373 |
-
the browser's sessionStorage; fully client-side render."""
|
| 374 |
-
return FileResponse(STATIC / "report.html")
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
@app.get("/register/{asset_class}")
|
| 378 |
-
def register_page(asset_class: str):
|
| 379 |
-
if asset_class not in ("schools", "nycha", "mta_entrances"):
|
| 380 |
-
return JSONResponse({"error": f"unknown asset class {asset_class!r}"}, status_code=404)
|
| 381 |
-
return FileResponse(STATIC / "register.html")
|
| 382 |
|
| 383 |
|
| 384 |
@app.get("/api/register/{asset_class}")
|
|
|
|
| 26 |
# nta_resolve and friends) don't open a Stone boundary — they're
|
| 27 |
# orientation / policy infrastructure shared across Stones.
|
| 28 |
_STEP_TO_STONE: dict[str, str] = {
|
| 29 |
+
# Cornerstone — single_address + polygon-aggregated (neighborhood)
|
| 30 |
"sandy_inundation": "Cornerstone",
|
| 31 |
"dep_stormwater": "Cornerstone",
|
| 32 |
"ida_hwm_2021": "Cornerstone",
|
| 33 |
"prithvi_eo_v2": "Cornerstone",
|
| 34 |
"microtopo_lidar": "Cornerstone",
|
| 35 |
+
"sandy_nta": "Cornerstone",
|
| 36 |
+
"dep_extreme_2080_nta": "Cornerstone",
|
| 37 |
+
"dep_moderate_2050_nta": "Cornerstone",
|
| 38 |
+
"dep_moderate_current_nta": "Cornerstone",
|
| 39 |
+
"microtopo_nta": "Cornerstone",
|
| 40 |
# Keystone (the chip fetch is infrastructure for the LoRA pair, but
|
| 41 |
# it's logically Keystone-adjacent and we surface it under that
|
| 42 |
# banner so the trace doesn't show a phantom orphan step).
|
|
|
|
| 54 |
"noaa_tides": "Touchstone",
|
| 55 |
"prithvi_eo_live": "Touchstone",
|
| 56 |
"terramind_lulc": "Touchstone",
|
| 57 |
+
"nyc311_nta": "Touchstone",
|
| 58 |
# Lodestone
|
| 59 |
"nws_alerts": "Lodestone",
|
| 60 |
"ttm_forecast": "Lodestone",
|
|
|
|
| 312 |
|
| 313 |
@app.get("/")
|
| 314 |
def index():
|
| 315 |
+
"""SvelteKit landing page (the new design-system UI)."""
|
|
|
|
|
|
|
|
|
|
| 316 |
sk = SVELTEKIT_BUILD / "index.html"
|
| 317 |
if sk.exists():
|
| 318 |
return FileResponse(sk)
|
| 319 |
+
return JSONResponse(
|
| 320 |
+
{"error": "sveltekit build not present — run `cd web/sveltekit && npm run build`"},
|
| 321 |
+
status_code=503,
|
| 322 |
+
)
|
| 323 |
|
| 324 |
|
| 325 |
@app.get("/q/sample")
|
|
|
|
| 352 |
return JSONResponse({"error": "sveltekit build not present"}, status_code=503)
|
| 353 |
|
| 354 |
|
| 355 |
+
# Legacy custom-element bundle routes (/legacy, /single, /compare, /agent,
|
| 356 |
+
# /report, /register/*) were retired in v0.4.5 — the SvelteKit UI fully
|
| 357 |
+
# subsumes them. Static assets at /static/* still mount in case anything
|
| 358 |
+
# external embeds them, but the page-level routes are gone. Hitting them
|
| 359 |
+
# now returns the framework default 404.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 360 |
|
| 361 |
|
| 362 |
@app.get("/api/register/{asset_class}")
|
web/sveltekit/build/200.html
CHANGED
|
@@ -6,33 +6,33 @@
|
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 7 |
<meta name="description" content="Riprap — citation-grounded NYC flood-exposure briefings." />
|
| 8 |
<title>Riprap — flood-exposure briefing</title>
|
| 9 |
-
<link href="/_app/immutable/entry/start.
|
| 10 |
-
<link href="/_app/immutable/chunks/
|
| 11 |
-
<link href="/_app/immutable/chunks/
|
| 12 |
-
<link href="/_app/immutable/entry/app.
|
| 13 |
-
<link href="/_app/immutable/chunks/
|
| 14 |
-
<link href="/_app/immutable/chunks/
|
| 15 |
-
<link href="/_app/immutable/chunks/
|
| 16 |
-
<link href="/_app/immutable/nodes/0.
|
| 17 |
-
<link href="/_app/immutable/chunks/
|
| 18 |
-
<link href="/_app/immutable/chunks/
|
| 19 |
-
<link href="/_app/immutable/chunks/
|
| 20 |
|
| 21 |
-
<link href="/_app/immutable/assets/0.
|
| 22 |
</head>
|
| 23 |
<body data-sveltekit-preload-data="hover">
|
| 24 |
<div style="display: contents">
|
| 25 |
<script>
|
| 26 |
{
|
| 27 |
-
|
| 28 |
base: ""
|
| 29 |
};
|
| 30 |
|
| 31 |
const element = document.currentScript.parentElement;
|
| 32 |
|
| 33 |
Promise.all([
|
| 34 |
-
import("/_app/immutable/entry/start.
|
| 35 |
-
import("/_app/immutable/entry/app.
|
| 36 |
]).then(([kit, app]) => {
|
| 37 |
kit.start(app, element);
|
| 38 |
});
|
|
|
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 7 |
<meta name="description" content="Riprap — citation-grounded NYC flood-exposure briefings." />
|
| 8 |
<title>Riprap — flood-exposure briefing</title>
|
| 9 |
+
<link href="/_app/immutable/entry/start.Cq4ZNiwW.js" rel="modulepreload">
|
| 10 |
+
<link href="/_app/immutable/chunks/BkOwEZvC.js" rel="modulepreload">
|
| 11 |
+
<link href="/_app/immutable/chunks/Dr93mMbz.js" rel="modulepreload">
|
| 12 |
+
<link href="/_app/immutable/entry/app.DhBN73ay.js" rel="modulepreload">
|
| 13 |
+
<link href="/_app/immutable/chunks/DM0dVcpA.js" rel="modulepreload">
|
| 14 |
+
<link href="/_app/immutable/chunks/WYoL_k1G.js" rel="modulepreload">
|
| 15 |
+
<link href="/_app/immutable/chunks/kFXL_I1K.js" rel="modulepreload">
|
| 16 |
+
<link href="/_app/immutable/nodes/0.CN6HhEhP.js" rel="modulepreload">
|
| 17 |
+
<link href="/_app/immutable/chunks/LdqJVpAk.js" rel="modulepreload">
|
| 18 |
+
<link href="/_app/immutable/chunks/BzAi_hbj.js" rel="modulepreload">
|
| 19 |
+
<link href="/_app/immutable/chunks/5ANqR9hF.js" rel="modulepreload">
|
| 20 |
|
| 21 |
+
<link href="/_app/immutable/assets/0.MHa9rmVP.css" rel="stylesheet">
|
| 22 |
</head>
|
| 23 |
<body data-sveltekit-preload-data="hover">
|
| 24 |
<div style="display: contents">
|
| 25 |
<script>
|
| 26 |
{
|
| 27 |
+
__sveltekit_ihrl7b = {
|
| 28 |
base: ""
|
| 29 |
};
|
| 30 |
|
| 31 |
const element = document.currentScript.parentElement;
|
| 32 |
|
| 33 |
Promise.all([
|
| 34 |
+
import("/_app/immutable/entry/start.Cq4ZNiwW.js"),
|
| 35 |
+
import("/_app/immutable/entry/app.DhBN73ay.js")
|
| 36 |
]).then(([kit, app]) => {
|
| 37 |
kit.start(app, element);
|
| 38 |
});
|
web/sveltekit/build/_app/immutable/assets/0.KpTzaSsX.css
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
web/sveltekit/build/_app/immutable/assets/0.MHa9rmVP.css
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
web/sveltekit/build/_app/immutable/assets/2.CWtxLGO7.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
.land-header.svelte-1ct2rgk{display:flex;align-items:baseline;gap:12px;padding:20px 32px;border-bottom:1px solid var(--rule-soft)}.land-header.svelte-1ct2rgk .riprap-wordmark{font-family:var(--font-serif);font-weight:600;font-size:18px;letter-spacing:.02em}.land-header-sep.svelte-1ct2rgk{color:var(--ink-tertiary)}.land-header-context.svelte-1ct2rgk{font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-secondary)}.land-header-nav.svelte-1ct2rgk{margin-left:auto;display:flex;gap:18px;font-family:var(--font-mono);font-size:12px}.land-header-nav.svelte-1ct2rgk a:where(.svelte-1ct2rgk){color:var(--ink-secondary);text-decoration:none;border-bottom:1px dotted transparent}.land-header-nav.svelte-1ct2rgk a:where(.svelte-1ct2rgk):hover{border-bottom-color:var(--ink-secondary)}.land-hero.svelte-drzq4r{padding:64px 32px 48px}.land-hero-h1.svelte-drzq4r{display:flex;flex-direction:column;gap:18px;margin:0 0 30px;max-width:880px}.land-hero-headline.svelte-drzq4r{font-family:var(--font-serif);font-weight:500;font-size:52px;line-height:1.08;color:var(--ink);letter-spacing:-.015em}.land-hero-headline.svelte-drzq4r em:where(.svelte-drzq4r){font-style:italic;font-weight:500}.land-hero-deck.svelte-drzq4r{font-family:var(--font-serif);font-size:18px;line-height:1.55;color:var(--ink-secondary);max-width:64ch}.land-query.svelte-drzq4r{display:flex;align-items:stretch;gap:0;max-width:760px;border:1px solid var(--ink);background:#fff;font-size:18px}.land-query-prompt.svelte-drzq4r{display:flex;align-items:center;padding:0 14px;font-family:var(--font-mono);font-size:22px;color:var(--ink-tertiary);background:var(--paper-deep);border-right:1px solid var(--rule-soft)}.land-query-input.svelte-drzq4r{flex:1;min-width:0;padding:18px 16px;font:inherit;font-family:var(--font-sans);border:none;outline:none;background:#fff;color:var(--ink)}.land-query-input.svelte-drzq4r::placeholder{color:var(--ink-tertiary)}.land-query-submit.svelte-drzq4r{padding:0 22px;font-family:var(--font-sans);font-weight:600;font-size:14px;background:var(--ink);color:var(--paper);border:none;cursor:pointer;white-space:nowrap;letter-spacing:.02em}.land-query-submit.svelte-drzq4r:hover{background:#000}.land-cycling.svelte-drzq4r{margin-top:18px;display:grid;grid-template-columns:auto 1fr;align-items:baseline;column-gap:10px;font-family:var(--font-mono);font-size:13px;color:var(--ink-tertiary);max-width:760px}.land-cycling-label.svelte-drzq4r{letter-spacing:.06em;text-transform:uppercase;font-size:11px;line-height:1.4em}.land-cycling-rail.svelte-drzq4r{position:relative;min-width:0;height:1.4em;line-height:1.4em;background:transparent;border:0;padding:0;cursor:pointer;text-align:left}.land-cycling-item.svelte-drzq4r{position:absolute;top:0;right:0;bottom:0;left:0;line-height:1.4em;opacity:0;transition:opacity .24s ease;color:var(--ink);border-bottom:1px dotted var(--rule-soft);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-family:var(--font-mono);font-size:13px}.land-cycling-item.is-active.svelte-drzq4r{opacity:1}@media(max-width:640px){.land-hero-headline.svelte-drzq4r{font-size:38px}.land-hero.svelte-drzq4r{padding:40px 24px 32px}}.land-section.svelte-1anw2jf{padding:48px 32px;border-top:1px solid var(--rule-soft)}.land-section-head.svelte-1anw2jf{display:flex;justify-content:space-between;align-items:baseline;gap:16px;margin-bottom:22px;padding-bottom:10px;border-bottom:1px solid var(--rule-soft)}.land-section-meta.svelte-1anw2jf{font-family:var(--font-serif);font-style:italic;font-size:14px;color:var(--ink-tertiary)}.land-preview-grid.svelte-1anw2jf{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) minmax(0,1fr);gap:14px;align-items:stretch}.land-preview-pane.svelte-1anw2jf{background:#fff;border:1px solid var(--rule-soft);border-left:3px solid var(--ink);padding:16px 18px;display:flex;flex-direction:column;gap:10px;min-width:0}.land-preview-eyebrow.svelte-1anw2jf{font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-tertiary);margin:0}.land-preview-pane-excerpt.svelte-1anw2jf .land-preview-body:where(.svelte-1anw2jf){font-family:var(--font-serif);font-size:15px;line-height:1.55;color:var(--ink);margin:0}.land-preview-cite.svelte-1anw2jf{background:linear-gradient(transparent 60%,#0b539424 60%)}.land-preview-cite.svelte-1anw2jf sup:where(.svelte-1anw2jf){font-family:var(--font-mono);font-size:10px;color:var(--tier-empirical);margin-left:2px;vertical-align:super}.land-preview-cites.svelte-1anw2jf{display:flex;flex-direction:column;gap:4px;padding-top:10px;border-top:1px dashed var(--rule-soft)}.land-preview-cite-row.svelte-1anw2jf{display:grid;grid-template-columns:30px 1fr 70px;gap:8px;align-items:baseline;font-family:var(--font-mono);font-size:11px}.land-preview-cite-pin.svelte-1anw2jf{color:var(--tier-empirical);font-weight:600}.land-preview-cite-src.svelte-1anw2jf{color:var(--ink)}.land-preview-cite-tier.svelte-1anw2jf{color:var(--ink-tertiary);text-align:right;letter-spacing:.04em}.land-preview-pane-cards.svelte-1anw2jf{gap:8px}.land-evcard-grid.svelte-1anw2jf{display:grid;grid-template-columns:1fr 1fr;gap:6px;flex:1}.land-evcard.svelte-1anw2jf{background:var(--paper);border:1px solid var(--rule-soft);padding:8px 10px;display:flex;flex-direction:column;gap:3px}.land-evcard-empirical.svelte-1anw2jf{border-left:2px solid var(--tier-empirical)}.land-evcard-modeled.svelte-1anw2jf{border-left:2px solid var(--tier-modeled)}.land-evcard-head.svelte-1anw2jf{display:flex;justify-content:space-between;align-items:baseline;font-family:var(--font-mono);font-size:10px;letter-spacing:.06em}.land-evcard-tier.svelte-1anw2jf{color:var(--ink-secondary);text-transform:uppercase}.land-evcard-empirical.svelte-1anw2jf .land-evcard-tier:where(.svelte-1anw2jf){color:var(--tier-empirical)}.land-evcard-modeled.svelte-1anw2jf .land-evcard-tier:where(.svelte-1anw2jf){color:var(--tier-modeled)}.land-evcard-id.svelte-1anw2jf{color:var(--ink-tertiary)}.land-evcard-claim.svelte-1anw2jf{font-family:var(--font-sans);font-size:12.5px;line-height:1.35;color:var(--ink)}.land-evcard-source.svelte-1anw2jf{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-tertiary)}.land-preview-pane-map.svelte-1anw2jf{padding:16px 18px}.land-mapmini.svelte-1anw2jf{position:relative;aspect-ratio:6 / 5;border:1px solid var(--rule-soft);overflow:hidden}.land-mapmini-legend.svelte-1anw2jf{position:absolute;left:6px;bottom:6px;right:6px;display:flex;gap:10px;padding:4px 6px;background:#ffffffeb;font-family:var(--font-mono);font-size:9.5px;letter-spacing:.04em;color:var(--ink-secondary)}.land-mapmini-legend.svelte-1anw2jf span:where(.svelte-1anw2jf){display:inline-flex;align-items:center;gap:4px}.lm-sw.svelte-1anw2jf{display:inline-block;width:8px;height:8px}.lm-sw-emp.svelte-1anw2jf{background:var(--tier-empirical)}.lm-sw-mod.svelte-1anw2jf{background:#2a6fa866;border:1px dashed var(--tier-modeled)}.lm-sw-prx.svelte-1anw2jf{background:transparent;border:1px solid #6B6B6B;border-radius:50%}.land-preview-mapmeta.svelte-1anw2jf{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-tertiary)}@media(max-width:1000px){.land-preview-grid.svelte-1anw2jf{grid-template-columns:1fr 1fr}.land-preview-pane-excerpt.svelte-1anw2jf{grid-column:1 / -1}}@media(max-width:640px){.land-preview-grid.svelte-1anw2jf{grid-template-columns:1fr}.land-preview-pane-excerpt.svelte-1anw2jf{grid-column:auto}}.land-section-stones-detail.svelte-1v6nt1t{background:var(--paper-deep);padding:56px 32px;border-top:1px solid var(--rule-soft)}.land-page.svelte-1v6nt1t{max-width:1200px;margin:0 auto}.land-section-head.svelte-1v6nt1t{display:flex;justify-content:space-between;align-items:baseline;gap:16px;margin-bottom:22px;padding-bottom:10px;border-bottom:1px solid var(--rule-soft)}.land-section-meta.svelte-1v6nt1t{font-family:var(--font-serif);font-style:italic;font-size:14px;color:var(--ink-tertiary)}.land-stones-deck.svelte-1v6nt1t{font-family:var(--font-serif);font-size:17px;line-height:1.6;color:var(--ink-secondary);max-width:70ch;margin:0 0 22px}.land-stones-detail.svelte-1v6nt1t{display:grid;grid-template-columns:repeat(5,1fr);gap:0;background:#fff;border:1px solid var(--rule-soft);border-bottom:2px solid var(--ink)}.land-stones-detail-cell.svelte-1v6nt1t{position:relative;padding:28px 18px 22px;border-right:1px solid var(--rule-soft);display:flex;flex-direction:column;gap:8px;overflow:hidden;border-left:3px solid var(--stone-tint, var(--rule-soft))}.land-stones-detail-cell.svelte-1v6nt1t:last-child{border-right:none}.land-stones-detail-num.svelte-1v6nt1t{position:absolute;top:6px;right:10px;font-family:var(--font-serif);font-style:italic;font-weight:400;font-size:38px;line-height:1;color:var(--rule-soft);letter-spacing:-.02em;pointer-events:none}.land-stones-detail-name.svelte-1v6nt1t{font-family:var(--font-serif);font-size:22px;font-weight:500;margin:0;color:var(--ink)}.land-stones-detail-role.svelte-1v6nt1t{font-family:var(--font-sans);font-size:13px;color:var(--ink-secondary)}.land-stones-detail-tag.svelte-1v6nt1t{font-family:var(--font-serif);font-style:italic;font-size:14px;color:var(--ink-tertiary);margin:0 0 6px;line-height:1.45}.land-stones-detail-sources.svelte-1v6nt1t{margin-top:auto;padding-top:10px;border-top:1px dashed var(--rule-soft);font-family:var(--font-mono);font-size:11px;color:var(--ink-secondary);line-height:1.55}@media(max-width:880px){.land-stones-detail.svelte-1v6nt1t{grid-template-columns:1fr}.land-stones-detail-cell.svelte-1v6nt1t{border-right:none;border-bottom:1px solid var(--rule-soft)}.land-stones-detail-cell.svelte-1v6nt1t:last-child{border-bottom:none}}.land-footer.svelte-1dcj612{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;padding:16px 32px;border-top:1px solid var(--rule-soft);font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);letter-spacing:.02em}.land-footer-tiers.svelte-1dcj612{display:flex;gap:16px;flex-wrap:wrap}.land-footer-tier.svelte-1dcj612{display:inline-flex;align-items:center;gap:5px}.lm-sw.svelte-1dcj612{display:inline-block;width:8px;height:8px}.lm-sw-emp.svelte-1dcj612{background:var(--tier-empirical)}.lm-sw-mod.svelte-1dcj612{background:#2a6fa866;border:1px dashed var(--tier-modeled)}.lm-sw-prx.svelte-1dcj612{background:transparent;border:1px solid #6B6B6B;border-radius:50%}.lm-sw-syn.svelte-1dcj612{background:#2a6fa840;background-image:repeating-linear-gradient(45deg,transparent 0,transparent 2px,var(--tier-synthetic) 2px,var(--tier-synthetic) 3px);border:1px solid var(--tier-synthetic)}.land.svelte-1uha8ag{min-height:100vh;display:flex;flex-direction:column;background:var(--paper);color:var(--ink)}.land-page.svelte-1uha8ag{max-width:1200px;margin:0 auto;width:100%}
|
web/sveltekit/build/_app/immutable/assets/4.BIuIAgmk.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
.plan-details.svelte-1q8jizq{border:1px solid var(--rule-soft);background:var(--paper-deep);margin-bottom:16px}.plan-details.svelte-1q8jizq summary:where(.svelte-1q8jizq){padding:10px 14px;cursor:pointer;font-family:var(--font-mono);font-size:12px;color:var(--ink-secondary)}.plan-stream.svelte-1q8jizq{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);white-space:pre-wrap;padding:0 14px 12px;margin:0;max-height:240px;overflow:auto}.generating-status.svelte-1q8jizq{display:flex;align-items:center;gap:12px;padding:12px 0;font-family:var(--font-mono);font-size:13px;color:var(--ink-secondary);flex-wrap:wrap}.pulse.svelte-1q8jizq{width:8px;height:8px;border-radius:50%;background:var(--accent-graphical);animation:svelte-1q8jizq-pulse 1.4s ease-in-out infinite}@keyframes svelte-1q8jizq-pulse{0%,to{opacity:.3;transform:scale(.85)}50%{opacity:1;transform:scale(1.1)}}@media(prefers-reduced-motion:reduce){.pulse.svelte-1q8jizq{animation:none;opacity:.7}}
|
web/sveltekit/build/_app/immutable/assets/4.CPUwsEjs.css
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
.register-grid.svelte-1q8jizq{display:grid;gap:16px;grid-template-columns:1fr}@media(min-width:1100px){.register-grid.svelte-1q8jizq{grid-template-columns:1fr 1fr}}.plan-details.svelte-1q8jizq{border:1px solid var(--rule-soft);background:var(--paper-deep);margin-bottom:16px}.plan-details.svelte-1q8jizq summary:where(.svelte-1q8jizq){padding:10px 14px;cursor:pointer;font-family:var(--font-mono);font-size:12px;color:var(--ink-secondary)}.plan-stream.svelte-1q8jizq{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);white-space:pre-wrap;padding:0 14px 12px;margin:0;max-height:240px;overflow:auto}.generating-status.svelte-1q8jizq{display:flex;align-items:center;gap:12px;padding:12px 0;font-family:var(--font-mono);font-size:13px;color:var(--ink-secondary);flex-wrap:wrap}.pulse.svelte-1q8jizq{width:8px;height:8px;border-radius:50%;background:var(--accent-graphical);animation:svelte-1q8jizq-pulse 1.4s ease-in-out infinite}@keyframes svelte-1q8jizq-pulse{0%,to{opacity:.3;transform:scale(.85)}50%{opacity:1;transform:scale(1.1)}}@media(prefers-reduced-motion:reduce){.pulse.svelte-1q8jizq{animation:none;opacity:.7}}
|
|
|
|
|
|
web/sveltekit/build/_app/immutable/assets/{Briefing.Cg0TTl7h.css → Briefing.Dmn9LgiV.css}
RENAMED
|
@@ -1 +1 @@
|
|
| 1 |
-
.tier-badge.svelte-1acpjpp{display:inline-flex;align-items:center;gap:6px;font-family:var(--font-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;font-weight:500}
|
|
|
|
| 1 |
+
.tier-badge.svelte-1acpjpp{display:inline-flex;align-items:center;gap:6px;font-family:var(--font-mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;font-weight:500}.briefing-fade-in.svelte-cc2m0h{animation:svelte-cc2m0h-briefing-fade .32s ease-out both}@keyframes svelte-cc2m0h-briefing-fade{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}
|
web/sveltekit/build/_app/immutable/assets/MapLegend.DvDgr167.css
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
.citation-drawer.svelte-1p339fd a{color:inherit;border-bottom:1px solid var(--rule-soft);text-decoration:none}.citation-drawer.svelte-1p339fd a:hover{border-bottom-color:var(--accent);color:var(--accent)}.rip-map-container.svelte-wk2bu4{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%}.map-frame.svelte-wk2bu4{aspect-ratio:8 / 5.6;position:relative}
|
|
|
|
|
|
web/sveltekit/build/_app/immutable/assets/stoneRegistry.bHiraU77.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
.citation-drawer.svelte-1p339fd a{color:inherit;border-bottom:1px solid var(--rule-soft);text-decoration:none}.citation-drawer.svelte-1p339fd a:hover{border-bottom-color:var(--accent);color:var(--accent)}.rip-map-container.svelte-wk2bu4{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%}.map-frame.svelte-wk2bu4{aspect-ratio:8 / 5.6;position:relative;transition:outline-color .2s ease;outline:0 solid transparent;outline-offset:0}.map-frame[data-linked].svelte-wk2bu4:not([data-linked=""]){outline:2px solid var(--accent-graphical)}.link-badge.svelte-wk2bu4{position:absolute;bottom:8px;right:8px;padding:3px 8px;background:var(--ink);color:var(--paper);font-family:var(--font-mono);font-size:10px;letter-spacing:.06em;text-transform:lowercase;z-index:5;pointer-events:none}.layers-panel.svelte-1g2dety{background:var(--paper);border:1px solid var(--rule-soft);padding:var(--s-3) var(--s-4) var(--s-4);display:flex;flex-direction:column;gap:var(--s-3);font-family:var(--font-sans)}.layers-head.svelte-1g2dety{padding-bottom:4px}.layers-group.svelte-1g2dety{border-top:1px solid var(--rule-soft);padding-top:var(--s-2);border-left:3px solid var(--stone-tint, var(--rule-soft));padding-left:var(--s-3)}.layers-group.region-cornerstone.svelte-1g2dety{--stone-tint: var(--stone-cornerstone)}.layers-group.region-keystone.svelte-1g2dety{--stone-tint: var(--stone-keystone)}.layers-group.region-touchstone.svelte-1g2dety{--stone-tint: var(--stone-touchstone)}.layers-group.region-lodestone.svelte-1g2dety{--stone-tint: var(--stone-lodestone)}.layers-group.region-capstone.svelte-1g2dety{--stone-tint: var(--stone-capstone)}.layers-group.svelte-1g2dety summary:where(.svelte-1g2dety){cursor:pointer;list-style:none;display:flex;align-items:baseline;gap:var(--s-2);padding:4px 0}.layers-group.svelte-1g2dety summary:where(.svelte-1g2dety)::-webkit-details-marker{display:none}.layers-caret.svelte-1g2dety{font-size:10px;color:var(--ink-tertiary);transition:transform .2s ease}.layers-group.svelte-1g2dety:not([open]) .layers-caret:where(.svelte-1g2dety){transform:rotate(-90deg)}.layers-stone-name.svelte-1g2dety{font-family:var(--font-serif);font-style:italic;font-size:16px;color:var(--ink)}.layers-stone-tag.svelte-1g2dety{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);letter-spacing:.04em}.layers-count.svelte-1g2dety{margin-left:auto;font-family:var(--font-mono);font-size:10px;color:var(--ink-tertiary);letter-spacing:.05em;text-transform:lowercase}.layers-list.svelte-1g2dety{list-style:none;margin:4px 0 var(--s-2);padding:0;display:flex;flex-direction:column}.layers-row.svelte-1g2dety{display:grid;grid-template-columns:16px 1fr auto;gap:var(--s-2);align-items:center;padding:4px 0;font-family:var(--font-mono);font-size:11px;color:var(--ink);border-bottom:1px dotted var(--rule-soft)}.layers-row.svelte-1g2dety:last-child{border-bottom:0}.layers-row.dim.svelte-1g2dety{opacity:.7}.layers-glyph.svelte-1g2dety{display:inline-flex;align-items:center}.layers-text.svelte-1g2dety{display:flex;flex-direction:column;gap:2px}.layers-label.svelte-1g2dety{color:var(--ink);font-family:var(--font-sans);font-size:12px}.layers-meta.svelte-1g2dety{font-family:var(--font-mono);font-size:10px;color:var(--ink-tertiary);display:inline-flex;align-items:center;gap:4px}.layers-state.svelte-1g2dety{font-family:var(--font-mono);font-size:10px;letter-spacing:.05em;color:var(--ink);text-transform:uppercase}.layers-state-dim.svelte-1g2dety{color:var(--ink-tertiary);text-transform:lowercase;font-style:italic}.layers-row-empty.svelte-1g2dety .layers-empty-text:where(.svelte-1g2dety){grid-column:1 / -1;color:var(--ink-tertiary);font-style:italic;font-family:var(--font-mono);font-size:11px}.layers-masters.svelte-1g2dety{border-top:1px solid var(--rule-soft);padding-top:var(--s-2)}.layers-master-row.svelte-1g2dety{margin-top:4px;display:flex;flex-wrap:wrap;gap:6px}.layers-master.svelte-1g2dety{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:var(--paper);border:1px solid var(--rule-soft);cursor:pointer;font-family:var(--font-mono);font-size:10px;letter-spacing:.05em;color:var(--ink)}.layers-master.is-on.svelte-1g2dety{background:var(--paper-deep);border-color:var(--ink)}.layers-master-state.svelte-1g2dety{margin-left:4px;color:var(--ink-tertiary);font-size:9px}.layers-master.is-on.svelte-1g2dety .layers-master-state:where(.svelte-1g2dety){color:var(--ink)}.rh.svelte-1vuwkv4{display:flex;align-items:center;flex-wrap:wrap;gap:var(--s-2);padding:var(--s-2) var(--s-4);background:var(--paper-deep);border-top:1px solid var(--rule-soft);border-bottom:1px solid var(--rule-soft);font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);letter-spacing:.04em}.rh-item.svelte-1vuwkv4 strong:where(.svelte-1vuwkv4){font-weight:600;color:var(--ink);margin-right:2px}.rh-sep.svelte-1vuwkv4{opacity:.5}.rh-silent.svelte-1vuwkv4{color:var(--ink-tertiary)}.rh-warn.svelte-1vuwkv4{color:#b7791f}.rh-err.svelte-1vuwkv4{color:#b91c1c}.rh-notinvoked.svelte-1vuwkv4{color:var(--ink-tertiary);font-style:italic}.rh-total.svelte-1vuwkv4 strong:where(.svelte-1vuwkv4){color:var(--ink-tertiary)}.body-headline.svelte-lygj3j{padding:var(--s-3) var(--s-4) var(--s-2);display:flex;flex-direction:column;gap:var(--s-1)}.fc.is-compact .body-headline.svelte-lygj3j{padding:var(--s-2) var(--s-3)}.headline.svelte-lygj3j{font-family:var(--font-serif);font-style:italic;font-size:28px;font-weight:500;line-height:1.1;letter-spacing:-.01em}.fc.is-compact .headline.svelte-lygj3j{font-size:22px}.subhead.svelte-lygj3j{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);letter-spacing:.04em}.body-prose.svelte-lygj3j{margin:var(--s-2) 0 0;font-size:13px;line-height:1.45;color:var(--ink-secondary)}.body-tabular.svelte-1nlkuao{padding:var(--s-2) var(--s-4) var(--s-3)}.fc.is-compact .body-tabular.svelte-1nlkuao{padding:var(--s-2) var(--s-3)}.t.svelte-1nlkuao{width:100%;border-collapse:collapse;font-family:var(--font-mono);font-size:12px}.t.svelte-1nlkuao th:where(.svelte-1nlkuao){text-align:left;font-weight:500;color:var(--ink-tertiary);padding:4px 8px 4px 0;border-bottom:1px solid var(--rule-soft);text-transform:lowercase;letter-spacing:.04em}.t.svelte-1nlkuao td:where(.svelte-1nlkuao){padding:4px 8px 4px 0;border-bottom:1px solid var(--rule-soft);color:var(--ink)}.t.svelte-1nlkuao tr:where(.svelte-1nlkuao):last-child td:where(.svelte-1nlkuao){border-bottom:0}.fc.is-compact .t.svelte-1nlkuao th:where(.svelte-1nlkuao),.fc.is-compact .t.svelte-1nlkuao td:where(.svelte-1nlkuao){padding:2px 6px 2px 0;font-size:11px}.body-sub.svelte-1nlkuao{margin-top:var(--s-2);font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);line-height:1.5}.body-scalars.svelte-stf9c8{padding:var(--s-3) var(--s-4) var(--s-3)}.fc.is-compact .body-scalars.svelte-stf9c8{padding:var(--s-2) var(--s-3)}.row.svelte-stf9c8{display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr));gap:var(--s-3)}.cell.svelte-stf9c8{display:flex;flex-direction:column;gap:2px}.value.svelte-stf9c8{font-family:var(--font-serif);font-style:italic;font-size:22px;font-weight:500;line-height:1.1}.label.svelte-stf9c8{font-family:var(--font-mono);font-size:10px;color:var(--ink-tertiary);letter-spacing:.06em;text-transform:lowercase}.body-sub.svelte-stf9c8{margin-top:var(--s-3);font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);line-height:1.5}.body-spark.svelte-jrppts{padding:var(--s-3) var(--s-4) var(--s-3);display:flex;flex-direction:column;gap:var(--s-1)}.fc.is-compact .body-spark.svelte-jrppts{padding:var(--s-2) var(--s-3)}.headline.svelte-jrppts{font-family:var(--font-serif);font-style:italic;font-size:22px;font-weight:500;line-height:1.1}.subhead.svelte-jrppts{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);letter-spacing:.04em;margin-bottom:var(--s-1)}svg.svelte-jrppts{display:block}.body-sub.svelte-jrppts{margin-top:var(--s-2);font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);line-height:1.5}.body-timeseries.svelte-48vbub{padding:var(--s-3) var(--s-4) var(--s-3);display:flex;flex-direction:column;gap:var(--s-2)}.fc.is-compact .body-timeseries.svelte-48vbub{padding:var(--s-2) var(--s-3)}.ts-header.svelte-48vbub{display:flex;align-items:baseline;flex-wrap:wrap;gap:var(--s-2)}.headline.svelte-48vbub{font-family:var(--font-serif);font-style:italic;font-size:22px;font-weight:500;line-height:1.1}.subhead.svelte-48vbub{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);letter-spacing:.04em}svg.svelte-48vbub{display:block}.body-sub.svelte-48vbub{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);line-height:1.5;display:flex;flex-direction:column;gap:2px}.spatial-note.svelte-48vbub{color:var(--accent);font-style:italic}.ft-footer.svelte-1y25lfh{margin:var(--s-2) var(--s-4) var(--s-3);padding-top:var(--s-2);border-top:1px dashed var(--rule-soft);display:flex;flex-wrap:wrap;gap:var(--s-3);align-items:baseline;font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary)}.fc.is-compact .ft-footer.svelte-1y25lfh{margin:var(--s-2) var(--s-3)}.ft-stat.svelte-1y25lfh{display:inline-flex;align-items:baseline;gap:4px;color:var(--ink)}.ft-stat-k.svelte-1y25lfh{font-size:10px;color:var(--ink-tertiary);letter-spacing:.05em;text-transform:uppercase}.ft-skill.svelte-1y25lfh{color:var(--tier-modeled);font-weight:500}.ft-badge.svelte-1y25lfh{border:1px solid var(--ink);color:var(--ink);padding:1px 6px;font-size:10px;letter-spacing:.06em;text-transform:uppercase;background:var(--paper)}.ft-link.svelte-1y25lfh{margin-left:auto;color:var(--accent);text-decoration:none}.ft-link.svelte-1y25lfh:hover{text-decoration:underline}.body-forecast.svelte-gqkhpe{padding:var(--s-3) var(--s-4) var(--s-3)}.fc.is-compact .body-forecast.svelte-gqkhpe{padding:var(--s-2) var(--s-3)}svg.svelte-gqkhpe{display:block}.body-sub.svelte-gqkhpe{margin-top:var(--s-2);font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);line-height:1.5}svg.svelte-1lx1psy{display:block}.thumb-placeholder.svelte-1lx1psy{height:120px;background:var(--paper-deep);display:flex;align-items:center;justify-content:center;color:var(--ink-tertiary);font-family:var(--font-mono);font-size:11px;border:1px dashed var(--rule-soft)}.body-raster.svelte-1m43x1m{padding:var(--s-2) var(--s-4) var(--s-3);display:flex;flex-direction:column;gap:var(--s-2)}.fc.is-compact .body-raster.svelte-1m43x1m{padding:var(--s-2) var(--s-3)}.frame.svelte-1m43x1m{position:relative;border:1px solid var(--rule-soft)}.illustrative.svelte-1m43x1m{position:absolute;top:6px;right:6px;background:#1a1a1ab3;color:var(--paper);font-family:var(--font-mono);font-size:9px;padding:2px 6px;letter-spacing:.05em;text-transform:lowercase}.raster-headline.svelte-1m43x1m{font-family:var(--font-mono);font-size:12px;color:var(--ink)}.raster-headline.svelte-1m43x1m span:where(.svelte-1m43x1m):first-child{font-family:var(--font-serif);font-style:italic;font-size:18px;font-weight:500}.body-sub.svelte-1m43x1m{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);line-height:1.5}.body-lulc.svelte-1td276x{padding:var(--s-2) var(--s-4) var(--s-3);display:flex;flex-direction:column;gap:var(--s-2)}.fc.is-compact .body-lulc.svelte-1td276x{padding:var(--s-2) var(--s-3)}.frame.svelte-1td276x{position:relative;border:1px solid var(--rule-soft)}.illustrative.svelte-1td276x{position:absolute;top:6px;right:6px;background:#1a1a1ab3;color:var(--paper);font-family:var(--font-mono);font-size:9px;padding:2px 6px;letter-spacing:.05em;text-transform:lowercase}.bar.svelte-1td276x{display:flex;height:10px;border:1px solid var(--rule-soft);overflow:hidden}.bar-seg.svelte-1td276x{flex-shrink:1;flex-basis:0}.legend.svelte-1td276x{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:4px var(--s-3);font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary)}.legend.svelte-1td276x li:where(.svelte-1td276x){display:inline-flex;align-items:center;gap:6px}.swatch.svelte-1td276x{display:inline-block;width:10px;height:10px;border:1px solid var(--rule-soft);flex:none}.legend-k.svelte-1td276x{color:var(--ink);text-transform:lowercase;letter-spacing:.04em}.legend-pct.svelte-1td276x{color:var(--ink-tertiary)}.body-sub.svelte-1td276x{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);line-height:1.5}.body-register.svelte-1iup6im{padding:var(--s-2) var(--s-4) var(--s-3)}.fc.is-compact .body-register.svelte-1iup6im{padding:var(--s-2) var(--s-3)}.reg-list.svelte-1iup6im{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}.reg-row.svelte-1iup6im{display:grid;grid-template-columns:70px 1fr auto;gap:var(--s-2);align-items:baseline;padding:5px 0;border-bottom:1px solid var(--rule-soft);font-family:var(--font-mono);font-size:12px;line-height:1.4}.reg-row.svelte-1iup6im:last-child{border-bottom:0}.fc.is-compact .reg-row.svelte-1iup6im{padding:3px 0;font-size:11px}.reg-tag.svelte-1iup6im{display:inline-flex;gap:4px;align-items:center;color:var(--ink-tertiary);text-transform:uppercase;letter-spacing:.05em;font-weight:500}.reg-label.svelte-1iup6im{color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.reg-source.svelte-1iup6im{color:var(--ink-tertiary);font-size:10px;letter-spacing:.05em}.reg-silent.svelte-1iup6im{grid-column:2 / span 2;color:var(--ink-tertiary);font-style:italic}.reg-row.silent.svelte-1iup6im{opacity:.65}.body-sub.svelte-1iup6im{margin-top:var(--s-2);font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);line-height:1.5}.body-comparison.svelte-1swqabu{padding:var(--s-3) var(--s-4) var(--s-3);display:flex;flex-direction:column;gap:var(--s-2)}.fc.is-compact .body-comparison.svelte-1swqabu{padding:var(--s-2) var(--s-3)}.cmp-grid.svelte-1swqabu{display:grid;grid-template-columns:1fr auto 1fr;gap:var(--s-3);align-items:stretch}.cell.svelte-1swqabu{display:flex;flex-direction:column;gap:4px}.cell-tier.svelte-1swqabu{display:inline-flex;align-items:center;gap:4px;font-family:var(--font-mono);font-size:10px;color:var(--ink-tertiary);letter-spacing:.05em;text-transform:lowercase}.cell-value.svelte-1swqabu{font-family:var(--font-serif);font-style:italic;font-size:22px;font-weight:500}.cell-aux.svelte-1swqabu{font-family:var(--font-mono);font-size:10px;color:var(--ink-tertiary)}.divider.svelte-1swqabu{align-self:center;font-family:var(--font-serif);font-style:italic;font-size:14px;color:var(--ink-tertiary);padding-top:18px}.cmp-delta.svelte-1swqabu{font-family:var(--font-mono);font-size:11px;color:var(--ink);border-top:1px solid var(--rule-soft);padding-top:var(--s-2)}.body-sub.svelte-1swqabu{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);line-height:1.5}.body-meta.svelte-e40scu{padding:var(--s-2) var(--s-4) var(--s-3)}.fc.is-compact .body-meta.svelte-e40scu{padding:var(--s-2) var(--s-3)}.meta-list.svelte-e40scu{margin:0;display:grid;grid-template-columns:1fr;gap:4px}.meta-row.svelte-e40scu{display:grid;grid-template-columns:minmax(110px,max-content) 1fr;gap:var(--s-3);padding:3px 0;border-bottom:1px solid var(--rule-soft);align-items:baseline}.meta-row.svelte-e40scu:last-child{border-bottom:0}dt.svelte-e40scu{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);text-transform:lowercase;letter-spacing:.04em}dd.svelte-e40scu{margin:0;font-family:var(--font-mono);font-size:12px;color:var(--ink)}.body-sub.svelte-e40scu{margin-top:var(--s-2);font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);line-height:1.5}.unknown.svelte-1x6xqhh{padding:var(--s-3) var(--s-4);font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary)}.fc.svelte-1nuvnzu{background:var(--paper);border:1px solid var(--rule-soft);display:flex;flex-direction:column;transition:background-color .2s ease,border-color .2s ease,outline-color .2s ease;outline:0 solid transparent;outline-offset:0;color:inherit;text-align:left;font:inherit;padding:0;width:100%;animation:svelte-1nuvnzu-fc-fade-in .36s ease-out both}@keyframes svelte-1nuvnzu-fc-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.fc.is-interactive.svelte-1nuvnzu{cursor:pointer}.fc.svelte-1nuvnzu:hover{background:var(--paper-deep)}.fc.is-linked.svelte-1nuvnzu{outline:2px solid var(--accent-graphical);outline-offset:0}.has-illustrative.svelte-1nuvnzu,.fc-tier-synthetic.svelte-1nuvnzu{border-top:1px dashed var(--tier-synthetic-line)}.fc-head.svelte-1nuvnzu{display:flex;justify-content:space-between;align-items:center;padding:var(--s-2) var(--s-4);border-bottom:1px solid var(--rule-soft);background:var(--paper-deep)}.fc.is-compact .fc-head.svelte-1nuvnzu{padding:6px var(--s-3)}.fc-head-source.svelte-1nuvnzu{display:inline-flex;align-items:center;gap:var(--s-2);font-family:var(--font-mono);font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--ink)}.fc-head-source-label.svelte-1nuvnzu{cursor:help}.fc-head-vintage.svelte-1nuvnzu{font-family:var(--font-mono);font-size:10px;color:var(--ink-tertiary);letter-spacing:.05em}.fc-title.svelte-1nuvnzu{margin:0;padding:var(--s-3) var(--s-4) 0;font-family:var(--font-sans);font-size:14px;font-weight:600;line-height:1.35;color:var(--ink)}.fc.is-compact .fc-title.svelte-1nuvnzu{padding:var(--s-2) var(--s-3) 0;font-size:13px}.fc-foot.svelte-1nuvnzu{display:flex;justify-content:space-between;align-items:center;padding:var(--s-2) var(--s-4);border-top:1px solid var(--rule-soft);background:var(--paper-deep);gap:var(--s-3);margin-top:auto}.fc.is-compact .fc-foot.svelte-1nuvnzu{padding:6px var(--s-3)}.fc-foot-cite.svelte-1nuvnzu{display:inline-flex;align-items:center;gap:4px;background:transparent;border:0;padding:0;cursor:pointer;font-family:var(--font-mono);font-size:10px;letter-spacing:.05em;color:var(--accent)}.fc-foot-cite.svelte-1nuvnzu:hover{color:var(--ink)}.fc-foot-docid.svelte-1nuvnzu{text-transform:uppercase}.fc-foot-docid-mute.svelte-1nuvnzu{font-family:var(--font-mono);font-size:10px;color:var(--ink-tertiary);letter-spacing:.05em;text-transform:uppercase}.fc-foot-arrow.svelte-1nuvnzu{font-family:var(--font-mono);font-size:11px}.fc-tier-badge.svelte-1nuvnzu{display:inline-flex;align-items:center;gap:4px;font-family:var(--font-mono);font-size:10px;font-weight:500;letter-spacing:.08em;text-transform:uppercase}.fc-tier-badge-empirical.svelte-1nuvnzu{color:var(--tier-empirical)}.fc-tier-badge-modeled.svelte-1nuvnzu{color:var(--tier-modeled)}.fc-tier-badge-proxy.svelte-1nuvnzu{color:var(--tier-proxy)}.fc-tier-badge-synthetic.svelte-1nuvnzu{color:var(--tier-synthetic)}.tally.svelte-1qqbvs2{display:inline-flex;align-items:center;gap:4px;font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);letter-spacing:.04em;flex-wrap:wrap}strong.svelte-1qqbvs2{font-weight:600;color:var(--ink)}.silent.svelte-1qqbvs2 strong:where(.svelte-1qqbvs2){color:var(--ink-tertiary)}.warn.svelte-1qqbvs2 strong:where(.svelte-1qqbvs2){color:#b7791f}.err.svelte-1qqbvs2 strong:where(.svelte-1qqbvs2){color:#b91c1c}.notinvoked.svelte-1qqbvs2 strong:where(.svelte-1qqbvs2){color:var(--ink-tertiary);font-style:italic}.sep.svelte-1qqbvs2{color:var(--ink-tertiary);opacity:.6}.prov-tree.svelte-qakefz{list-style:none;margin:0;padding:0;padding-left:calc(var(--depth, 0) * 16px)}.prov-row.svelte-qakefz{display:grid;grid-template-columns:14px max-content max-content 1fr auto;gap:var(--s-2);align-items:baseline;padding:3px 0;font-family:var(--font-mono);font-size:11px;border-bottom:1px dotted var(--rule-soft)}.prov-row.svelte-qakefz:last-child{border-bottom:0}.prov-pip.svelte-qakefz{text-align:center;font-size:10px;line-height:1}.prov-id.svelte-qakefz{color:var(--ink);letter-spacing:.04em;text-transform:lowercase}.prov-tier.svelte-qakefz{display:inline-flex;align-items:center}.prov-name.svelte-qakefz{font-family:var(--font-serif);font-style:italic;font-size:13px;color:var(--ink)}.prov-note.svelte-qakefz{font-family:var(--font-sans);font-size:12px;color:var(--ink-tertiary)}.prov-ms.svelte-qakefz{font-family:var(--font-mono);font-size:10px;color:var(--ink-tertiary)}.prov-status-silent_by_design.svelte-qakefz .prov-name:where(.svelte-qakefz){color:var(--ink-tertiary);font-style:italic}.prov-status-warned.svelte-qakefz .prov-name:where(.svelte-qakefz){color:#b7791f}.prov-status-errored.svelte-qakefz .prov-name:where(.svelte-qakefz){color:#b91c1c}.prov-status-errored.svelte-qakefz .prov-pip:where(.svelte-qakefz){font-weight:700}.prov-status-not_invoked.svelte-qakefz .prov-name:where(.svelte-qakefz){color:var(--ink-tertiary);font-style:italic}.prov-status-not_invoked.svelte-qakefz .prov-id:where(.svelte-qakefz){color:var(--ink-tertiary);opacity:.6}.prov-children.svelte-qakefz{padding:0}.region.svelte-16iv0n8{border-top:1px solid var(--rule-soft);padding:var(--s-5) 0 var(--s-5);background:transparent}.region.svelte-16iv0n8:first-of-type{border-top:0}.region-head.svelte-16iv0n8{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-4);margin-bottom:var(--s-3);flex-wrap:wrap;border-left:3px solid var(--stone-tint, var(--rule-soft));padding-left:var(--s-3)}.region-cornerstone.svelte-16iv0n8 .region-head:where(.svelte-16iv0n8){--stone-tint: var(--stone-cornerstone)}.region-keystone.svelte-16iv0n8 .region-head:where(.svelte-16iv0n8){--stone-tint: var(--stone-keystone)}.region-touchstone.svelte-16iv0n8 .region-head:where(.svelte-16iv0n8){--stone-tint: var(--stone-touchstone)}.region-lodestone.svelte-16iv0n8 .region-head:where(.svelte-16iv0n8){--stone-tint: var(--stone-lodestone)}.region-capstone.svelte-16iv0n8 .region-head:where(.svelte-16iv0n8){--stone-tint: var(--stone-capstone)}.region-head-left.svelte-16iv0n8{display:flex;align-items:baseline;gap:var(--s-2);flex-wrap:wrap}.region-num.svelte-16iv0n8{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);letter-spacing:.1em}.region-name.svelte-16iv0n8{margin:0;font-family:var(--font-serif);font-style:italic;font-size:26px;font-weight:500;color:var(--ink);letter-spacing:-.005em;line-height:1.1}.region-role.svelte-16iv0n8{font-family:var(--font-sans);font-size:14px;color:var(--ink-secondary)}.region-tag.svelte-16iv0n8{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);letter-spacing:.05em;margin-left:var(--s-2)}.rail.svelte-16iv0n8{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--s-3)}.rail.svelte-16iv0n8>.fc{grid-column:span 4}.rail.svelte-16iv0n8>.fc.fc-register,.rail.svelte-16iv0n8>.fc.fc-timeseries,.rail.svelte-16iv0n8>.fc.fc-timeseries-ft,.rail.svelte-16iv0n8>.fc.fc-forecast,.rail.svelte-16iv0n8>.fc.fc-raster,.rail.svelte-16iv0n8>.fc.fc-raster-pred,.rail.svelte-16iv0n8>.fc.fc-lulc,.rail.svelte-16iv0n8>.fc.fc-comparison{grid-column:span 6}.rail-capstone.svelte-16iv0n8>.fc{grid-column:span 6}@media(max-width:920px){.rail.svelte-16iv0n8{grid-template-columns:repeat(6,1fr)}.rail.svelte-16iv0n8>.fc{grid-column:span 6}}.silent.svelte-16iv0n8{border:1px dashed var(--rule-soft);padding:var(--s-4);display:flex;flex-direction:column;gap:var(--s-2);background:var(--paper-deep)}.silent-tag.svelte-16iv0n8{font-family:var(--font-mono);font-size:10px;color:var(--ink-tertiary);letter-spacing:.1em;text-transform:uppercase}.silent-prose.svelte-16iv0n8{margin:0;font-size:13px;color:var(--ink-secondary);line-height:1.5;max-width:var(--measure)}.prov.svelte-16iv0n8{margin-top:var(--s-3)}.prov-toggle.svelte-16iv0n8{background:transparent;border:0;padding:4px 0;cursor:pointer;display:inline-flex;align-items:baseline;gap:var(--s-1);font-family:var(--font-mono);font-size:11px;color:var(--ink-secondary);letter-spacing:.05em}.prov-toggle.svelte-16iv0n8:hover{color:var(--ink)}.prov-caret.svelte-16iv0n8{font-size:10px;color:var(--ink-tertiary)}.prov-meta.svelte-16iv0n8{color:var(--ink-tertiary)}.prov-body.svelte-16iv0n8{margin-top:var(--s-2);padding:var(--s-2) 0;border-top:1px solid var(--rule-soft);border-bottom:1px solid var(--rule-soft)}.region-grammar.svelte-gwg123{border-top:2px solid var(--ink);padding:var(--s-5) 0}.region-head.svelte-gwg123{display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap;gap:var(--s-3);margin-bottom:var(--s-3)}.region-head-left.svelte-gwg123{display:flex;align-items:baseline;gap:var(--s-2);flex-wrap:wrap}.region-num.svelte-gwg123{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);letter-spacing:.1em}.region-name.svelte-gwg123{margin:0;font-family:var(--font-serif);font-style:italic;font-size:22px;font-weight:500}.region-role.svelte-gwg123{font-family:var(--font-sans);font-size:13px;color:var(--ink-secondary)}.region-tag.svelte-gwg123{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);letter-spacing:.05em}.grammar-count.svelte-gwg123{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary)}.rail.svelte-gwg123{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--s-3)}.rail.svelte-gwg123>.fc{grid-column:span 4}.rail.svelte-gwg123>.fc.fc-register,.rail.svelte-gwg123>.fc.fc-timeseries,.rail.svelte-gwg123>.fc.fc-forecast,.rail.svelte-gwg123>.fc.fc-raster,.rail.svelte-gwg123>.fc.fc-raster-pred,.rail.svelte-gwg123>.fc.fc-comparison{grid-column:span 6}@media(max-width:920px){.rail.svelte-gwg123{grid-template-columns:repeat(6,1fr)}.rail.svelte-gwg123>.fc{grid-column:span 6}}.findings.svelte-ci42t5{background:var(--paper);color:var(--ink)}.findings-head.svelte-ci42t5{display:flex;align-items:baseline;justify-content:space-between;flex-wrap:wrap;gap:var(--s-3);padding:var(--s-3) 0 var(--s-2)}.findings-h2.svelte-ci42t5{margin:0;font-family:var(--font-serif);font-style:italic;font-size:22px;font-weight:500;color:var(--ink)}.findings-tagline.svelte-ci42t5{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);letter-spacing:.05em}
|
web/sveltekit/build/_app/immutable/chunks/5ANqR9hF.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
import{l as a}from"./Dr93mMbz.js";a();
|
web/sveltekit/build/_app/immutable/chunks/B4YyPI_Q.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
var Q=i=>{throw TypeError(i)};var dt=(i,t,e)=>t.has(i)||Q("Cannot "+e);var N=(i,t,e)=>(dt(i,t,"read from private field"),e?e.call(i):t.get(i)),Y=(i,t,e)=>t.has(i)?Q("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(i):t.set(i,e);import{j as q,s as k,a as v,k as z,f as W,d as ht,b as vt,c as $,t as ft}from"./WYoL_k1G.js";import{h as H,i as tt,k as B,t as x,P as et,q as ut,b6 as mt,j as rt,C as _t,g as gt,b5 as yt,b2 as bt,aW as pt,b7 as wt,b8 as xt,c as _,r as f,s as C,x as r,B as j,f as U,w as K,y as E,p as X,a as Z,G as Wt,F as at,ab as Mt}from"./Dr93mMbz.js";import{p as F,i as P}from"./kFXL_I1K.js";import{s,a as nt,b as St,e as it,i as st}from"./Chht4iZ-.js";import{s as kt}from"./LdqJVpAk.js";function Ct(i,t,e=!1,a=!1,l=!1,c=!1){var h=i,d="";if(e){var o=i;H&&(h=tt(B(o)))}x(()=>{var u=ut;if(d===(d=t()??"")){H&&et();return}if(e&&!H){u.nodes=null,o.innerHTML=d,d!==""&&q(B(o),o.lastChild);return}if(u.nodes!==null&&(mt(u.nodes.start,u.nodes.end),u.nodes=null),d!==""){if(H){rt.data;for(var y=et(),b=y;y!==null&&(y.nodeType!==_t||y.data!=="");)b=y,y=gt(y);if(y===null)throw yt(),bt;q(rt,b),h=tt(y);return}var p=a?wt:l?xt:void 0,M=pt(a?"svg":l?"math":"template",p);M.innerHTML=d;var m=a||l?M:M.content;if(q(B(m),m.lastChild),a||l)for(;B(m);)h.before(B(m));else h.before(m)}})}var Et=z('<rect x="0" y="0"></rect>'),Tt=z('<rect fill="none"></rect>'),It=z("<circle></circle>"),Dt=z('<defs><pattern width="3" height="3" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="3" stroke-width="1.5"></line></pattern></defs><rect></rect>',1),Pt=z('<svg role="img" style="flex: none; display: inline-block; vertical-align: -0.12em;"><title> </title><!></svg>');function ot(i,t){let e=F(t,"size",3,12),a=F(t,"color",3,"currentColor");const l={empirical:"Empirical: directly measured or observed",modeled:"Modeled: scenario-based prediction",proxy:"Proxy: indirect indicator",synthetic:"Synthetic prior: generated, not observed"};let c=j(()=>Math.max(1,Math.round(e()/9))),h=j(()=>t.title??l[t.tier]),d=j(()=>`rip-stripe-${t.tier}-${e()}`);var o=Pt(),u=_(o),y=_(u,!0);f(u);var b=C(u);{var p=g=>{var n=Et();x(()=>{s(n,"width",e()),s(n,"height",e()),s(n,"fill",a())}),v(g,n)},M=g=>{var n=Tt();x(()=>{s(n,"x",r(c)/2),s(n,"y",r(c)/2),s(n,"width",e()-r(c)),s(n,"height",e()-r(c)),s(n,"stroke",a()),s(n,"stroke-width",r(c))}),v(g,n)},m=g=>{var n=It();x(()=>{s(n,"cx",e()/2),s(n,"cy",e()/2),s(n,"r",e()/2-.5),s(n,"fill",a())}),v(g,n)},w=g=>{var n=Dt(),T=U(n),R=_(T),V=_(R);f(R),f(T);var S=C(T);x(()=>{s(R,"id",r(d)),s(V,"stroke",a()),s(S,"x",r(c)/2),s(S,"y",r(c)/2),s(S,"width",e()-r(c)),s(S,"height",e()-r(c)),s(S,"fill",`url(#${r(d)??""})`),s(S,"stroke",a()),s(S,"stroke-width",r(c))}),v(g,n)};P(b,g=>{t.tier==="empirical"?g(p):t.tier==="modeled"?g(M,1):t.tier==="proxy"?g(m,2):g(w,-1)})}f(o),x(()=>{s(o,"width",e()),s(o,"height",e()),s(o,"viewBox",`0 0 ${e()??""} ${e()??""}`),s(o,"aria-label",r(h)),k(y,r(h))}),v(i,o)}var Rt=W('<span><span class="claim-glyph" aria-hidden="false"><!></span> <span class="claim-body"><!></span></span>');function At(i,t){var e=Rt(),a=_(e),l=_(a);ot(l,{get tier(){return t.tier},size:11,get color(){return`var(--tier-${t.tier??""})`}}),f(a);var c=C(a,2),h=_(c);kt(h,()=>t.children),f(c),f(e),x(()=>{nt(e,1,`claim claim-${t.tier??""}`),s(e,"data-tier",t.tier)}),v(i,e)}var L,O;class Nt{constructor(){Y(this,L,K(null));Y(this,O,K(null))}get active(){return r(N(this,L))}set active(t){E(N(this,L),t,!0)}get highlightDocId(){return r(N(this,O))}set highlightDocId(t){E(N(this,O),t,!0)}}L=new WeakMap,O=new WeakMap;const Bt=new Nt;var Ft=W('<a class="inline-cite"><sup> </sup></a>');function Lt(i,t){X(t,!0);function e(h){h.preventDefault(),Bt.active=t.c.id;const d=document.getElementById(`cite-${t.c.id}`);d==null||d.scrollIntoView({block:"center",behavior:"smooth"})}var a=Ft(),l=_(a),c=_(l);f(l),f(a),x(()=>{s(a,"href",`#cite-${t.c.id??""}`),s(a,"data-cite",t.c.id),s(a,"aria-label",`Citation ${t.c.n??""}: ${t.c.source??""}, ${t.c.title??""}`),k(c,`[${t.c.n??""}]`)}),vt("click",a,e),v(i,a),Z()}ht(["click"]);const Ot={empirical:{label:"Empirical",short:"EMP",desc:"Directly measured or observed",examples:"USGS high-water marks · FloodNet sensors · Sandy Inundation Zone"},modeled:{label:"Modeled",short:"MOD",desc:"Scenario-based prediction",examples:"FEMA flood zones · DEP stormwater scenarios · NPCC4 SLR"},proxy:{label:"Proxy",short:"PRX",desc:"Indirect indicator",examples:"311 flood complaints · NFIP claims · terrain indices"},synthetic:{label:"Synthetic prior",short:"SYN",desc:"Generated, not observed",examples:"TerraMind land-cover · synthetic SAR for occluded days"}};function se(i){const t=i.toLowerCase();return t.startsWith("syn")||t.startsWith("terramind")||t.includes("synthetic")?"synthetic":t.startsWith("sandy")||t.startsWith("floodnet")||t.startsWith("usgs")||t.startsWith("mta_entrance")||t.startsWith("nycha_dev")||t.startsWith("doe_school")||t.startsWith("doh_hospital")||t.startsWith("ida_hwm")||t.startsWith("hwm")||t.startsWith("noaa")||t.startsWith("nws_obs")||t.startsWith("prithvi_eo")?"empirical":t.startsWith("dep")||t.startsWith("fema_firm")||t.startsWith("npcc")||t.startsWith("wrp")||t.includes("scenario")||t.includes("forecast")||t.startsWith("prithvi")||t.startsWith("ttm")||t.startsWith("nws_alert")?"modeled":(t.startsWith("nyc311")||t.startsWith("311")||t.startsWith("nfip")||t.startsWith("rag")||t.startsWith("dob")||t.startsWith("hand")||t.startsWith("twi")||t.startsWith("microtopo"),"proxy")}function ne(i){const t=i.toLowerCase();return t==="geocode"||t.startsWith("fan")||t.startsWith("merge")||t==="plan"||t==="compose"||t==="reconcile"||t==="stream"?null:t==="sandy"||t==="sandy_inundation"||t==="floodnet"||t==="ida_hwm"||t==="noaa_tides"||t==="nws_obs"||t==="prithvi_eo_v2"||t==="prithvi_eo_live"||t==="mta_entrance_exposure"||t==="mta_entrances"||t==="nycha_developments"||t==="doe_school_exposure"||t==="doe_schools"||t==="doh_hospital_exposure"||t==="doh_hospitals"?"empirical":t==="dep"||t==="dep_stormwater"||t==="ttm_forecast"||t==="ttm_311_forecast"||t==="floodnet_forecast"||t==="nws_alerts"||t==="prithvi_water"?"modeled":t==="nyc311"||t==="microtopo"||t==="microtopo_lidar"||t==="rag"||t==="rag_mta"?"proxy":t==="terramind"||t==="terramind_synthesis"?"synthetic":null}var zt=W("<span><!> </span>");function Gt(i,t){X(t,!0);let e=F(t,"compact",3,!1),a=j(()=>Ot[t.tier]);var l=zt();let c;var h=_(l);ot(h,{get tier(){return t.tier},size:10,get color(){return`var(--tier-${t.tier??""})`}});var d=C(h);f(l),x(()=>{nt(l,1,`tier-badge tier-badge-${t.tier??""}`,"svelte-1acpjpp"),s(l,"title",r(a).desc),c=St(l,"",c,{color:`var(--tier-${t.tier??""})`}),k(d,` ${(e()?r(a).short:r(a).label)??""}`)}),v(i,l),Z()}var Ht=W('<span class="briefing-section-tier"><!></span>'),jt=W('<span class="briefing-section-title"> </span>'),Ut=W('<h3 class="briefing-section-head"><span class="briefing-section-num"> </span> <span class="briefing-section-label"> </span> <!> <!></h3>');function Kt(i,t){var e=Ut(),a=_(e),l=_(a,!0);f(a);var c=C(a,2),h=_(c,!0);f(c);var d=C(c,2);{var o=b=>{var p=Ht(),M=_(p);Gt(M,{get tier(){return t.tier},compact:!0}),f(p),v(b,p)};P(d,b=>{t.tier&&b(o)})}var u=C(d,2);{var y=b=>{var p=jt(),M=_(p,!0);f(p),x(()=>k(M,t.title)),v(b,p)};P(u,b=>{t.title&&b(y)})}f(e),x(()=>{k(l,t.n),k(h,t.label)}),v(i,e)}var Vt=W('<div class="briefing-status briefing-fade-in svelte-cc2m0h"></div>'),Yt=W('<div class="briefing-fade-in svelte-cc2m0h"><!></div>'),qt=W("<!><!>",1),Xt=W("<span> </span>"),Zt=W('<p class="briefing-para briefing-fade-in svelte-cc2m0h"></p>'),Jt=W('<div class="briefing-prose" role="log" aria-live="polite" aria-atomic="false" aria-label="Streaming flood-exposure briefing"></div>');function oe(i,t){X(t,!0);let e=F(t,"streaming",3,!1),a=F(t,"replayKey",3,0),l=K(Wt(t.blocks.length)),c=K(!1);at(()=>{typeof window>"u"||E(c,window.matchMedia("(prefers-reduced-motion: reduce)").matches,!0)}),at(()=>{if(a(),!e()){E(l,t.blocks.length,!0);return}if(r(c)){E(l,t.blocks.length,!0);return}E(l,0);let d=0,o;const u=()=>{d++,E(l,d,!0),d<t.blocks.length&&(o=setTimeout(u,d<2?280:420))};return o=setTimeout(u,240),()=>clearTimeout(o)});var h=Jt();it(h,21,()=>t.blocks.slice(0,r(l)),st,(d,o)=>{var u=$(),y=U(u);{var b=m=>{var w=Vt();Ct(w,()=>r(o).html,!0),f(w),v(m,w)},p=m=>{var w=Yt(),g=_(w);Kt(g,{get n(){return r(o).n},get label(){return r(o).label},get tier(){return r(o).tier},get title(){return r(o).title}}),f(w),v(m,w)},M=m=>{var w=Zt();it(w,21,()=>r(o).parts,st,(g,n)=>{var T=$(),R=U(T);{var V=I=>{var D=qt(),G=U(D);At(G,{get tier(){return r(n).tier},children:(A,Qt)=>{Mt();var J=ft();x(()=>k(J,r(n).text)),v(A,J)}});var lt=C(G);{var ct=A=>{Lt(A,{get c(){return t.citations[r(n).cite]}})};P(lt,A=>{r(n).cite&&t.citations[r(n).cite]&&A(ct)})}v(I,D)},S=I=>{var D=Xt(),G=_(D,!0);f(D),x(()=>k(G,r(n).text)),v(I,D)};P(R,I=>{r(n).tier?I(V):I(S,-1)})}v(g,T)}),f(w),v(m,w)};P(y,m=>{r(o).kind==="status"?m(b):r(o).kind==="head"?m(p,1):m(M,-1)})}v(d,u)}),f(h),v(i,h),Z()}export{oe as B,ot as T,ne as a,Gt as b,Bt as c,Ot as d,se as t};
|
web/sveltekit/build/_app/immutable/chunks/B7gjWklj.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
import{m as z,i as oe,n as fe,o as C,q as Y,v as le,w as ue,k as W,x as te,H as de,y as Z,z as B,A as y,C as ce,B as ve,F as $,G as _e,I as m,J as q,K as pe,L as ge,M as he,N as Ee,O as K,P as Ae,Q as Te,R as me,T as j,U as Se,V as Ne,W as ae,X as ie,Y as U,Z as se,_ as Ie,$ as we,a0 as Me,a1 as Ce,a2 as ke,a3 as Le,a4 as Re,a5 as He,a6 as Oe,a7 as xe,a8 as De,a9 as be}from"./CYuHyzh3.js";function Ve(e,r){return r}function Fe(e,r,f){for(var o=[],l=r.length,u,a=r.length,_=0;_<l;_++){let E=r[_];ie(E,()=>{if(u){if(u.pending.delete(E),u.done.add(E),u.pending.size===0){var d=e.outrogroups;G(e,K(u.done)),d.delete(u),d.size===0&&(e.outrogroups=null)}}else a-=1},!1)}if(a===0){var s=o.length===0&&f!==null;if(s){var v=f,i=v.parentNode;Me(i),i.append(v),e.items.clear()}G(e,r,!s)}else u={pending:new Set(r),done:new Set},(e.outrogroups??(e.outrogroups=new Set)).add(u)}function G(e,r,f=!0){var o;if(e.pending.size>0){o=new Set;for(const a of e.pending.values())for(const _ of a)o.add(e.items.get(_).e)}for(var l=0;l<r.length;l++){var u=r[l];if(o!=null&&o.has(u)){u.f|=m;const a=document.createDocumentFragment();Ce(u,a)}else ke(r[l],f)}}var ee;function Xe(e,r,f,o,l,u=null){var a=e,_=new Map,s=(r&fe)!==0;if(s){var v=e;a=C?Y(le(v)):v.appendChild(z())}C&&ue();var i=null,E=he(()=>{var c=f();return Ee(c)?c:c==null?[]:K(c)}),d,A=new Map,T=!0;function k(c){(N.effect.f&Ne)===0&&(N.pending.delete(c),N.fallback=i,ye(N,d,a,r,o),i!==null&&(d.length===0?(i.f&m)===0?ae(i):(i.f^=m,x(i,null,a)):ie(i,()=>{i=null})))}function n(c){N.pending.delete(c)}var t=oe(()=>{d=W(E);var c=d.length;let g=!1;if(C){var D=te(a)===de;D!==(c===0)&&(a=Z(),Y(a),B(!1),g=!0)}for(var I=new Set,p=_e,R=ge(),w=0;w<c;w+=1){C&&y.nodeType===ce&&y.data===ve&&(a=y,g=!0,B(!1));var H=d[w],L=o(H,w),h=T?null:_.get(L);h?(h.v&&$(h.v,H),h.i&&$(h.i,w),R&&p.unskip_effect(h.e)):(h=ze(_,T?a:ee??(ee=z()),H,L,w,l,r,f),T||(h.e.f|=m),_.set(L,h)),I.add(L)}if(c===0&&u&&!i&&(T?i=q(()=>u(a)):(i=q(()=>u(ee??(ee=z()))),i.f|=m)),c>I.size&&pe(),C&&c>0&&Y(Z()),!T)if(A.set(p,I),R){for(const[b,F]of _)I.has(b)||p.skip_effect(F.e);p.oncommit(k),p.ondiscard(n)}else k(p);g&&B(!0),W(E)}),N={effect:t,items:_,pending:A,outrogroups:null,fallback:i};T=!1,C&&(a=y)}function O(e){for(;e!==null&&(e.f&Ie)===0;)e=e.next;return e}function ye(e,r,f,o,l){var H,L,h,b,F,V,X,P,J;var u=(o&we)!==0,a=r.length,_=e.items,s=O(e.effect.first),v,i=null,E,d=[],A=[],T,k,n,t;if(u)for(t=0;t<a;t+=1)T=r[t],k=l(T,t),n=_.get(k).e,(n.f&m)===0&&((L=(H=n.nodes)==null?void 0:H.a)==null||L.measure(),(E??(E=new Set)).add(n));for(t=0;t<a;t+=1){if(T=r[t],k=l(T,t),n=_.get(k).e,e.outrogroups!==null)for(const S of e.outrogroups)S.pending.delete(n),S.done.delete(n);if((n.f&U)!==0&&(ae(n),u&&((b=(h=n.nodes)==null?void 0:h.a)==null||b.unfix(),(E??(E=new Set)).delete(n))),(n.f&m)!==0)if(n.f^=m,n===s)x(n,null,f);else{var N=i?i.next:s;n===e.effect.last&&(e.effect.last=n.prev),n.prev&&(n.prev.next=n.next),n.next&&(n.next.prev=n.prev),M(e,i,n),M(e,n,N),x(n,N,f),i=n,d=[],A=[],s=O(i.next);continue}if(n!==s){if(v!==void 0&&v.has(n)){if(d.length<A.length){var c=A[0],g;i=c.prev;var D=d[0],I=d[d.length-1];for(g=0;g<d.length;g+=1)x(d[g],c,f);for(g=0;g<A.length;g+=1)v.delete(A[g]);M(e,D.prev,I.next),M(e,i,D),M(e,I,c),s=c,i=I,t-=1,d=[],A=[]}else v.delete(n),x(n,s,f),M(e,n.prev,n.next),M(e,n,i===null?e.effect.first:i.next),M(e,i,n),i=n;continue}for(d=[],A=[];s!==null&&s!==n;)(v??(v=new Set)).add(s),A.push(s),s=O(s.next);if(s===null)continue}(n.f&m)===0&&d.push(n),i=n,s=O(n.next)}if(e.outrogroups!==null){for(const S of e.outrogroups)S.pending.size===0&&(G(e,K(S.done)),(F=e.outrogroups)==null||F.delete(S));e.outrogroups.size===0&&(e.outrogroups=null)}if(s!==null||v!==void 0){var p=[];if(v!==void 0)for(n of v)(n.f&U)===0&&p.push(n);for(;s!==null;)(s.f&U)===0&&s!==e.fallback&&p.push(s),s=O(s.next);var R=p.length;if(R>0){var w=(o&fe)!==0&&a===0?f:null;if(u){for(t=0;t<R;t+=1)(X=(V=p[t].nodes)==null?void 0:V.a)==null||X.measure();for(t=0;t<R;t+=1)(J=(P=p[t].nodes)==null?void 0:P.a)==null||J.fix()}Fe(e,p,w)}}u&&se(()=>{var S,Q;if(E!==void 0)for(n of E)(Q=(S=n.nodes)==null?void 0:S.a)==null||Q.apply()})}function ze(e,r,f,o,l,u,a,_){var s=(a&Ae)!==0?(a&Te)===0?me(f,!1,!1):j(f):null,v=(a&Se)!==0?j(l):null;return{v:s,i:v,e:q(()=>(u(r,s??f,v??l,_),()=>{e.delete(o)}))}}function x(e,r,f){if(e.nodes)for(var o=e.nodes.start,l=e.nodes.end,u=r&&(r.f&m)===0?r.nodes.start:f;o!==null;){var a=Le(o);if(u.before(o),o===l)return;o=a}}function M(e,r,f){r===null?e.effect.first=f:r.next=f,f===null?e.effect.last=r:f.prev=r}const Ye=Symbol("is custom element"),Be=Symbol("is html"),Ue=xe?"link":"LINK";function Pe(e){if(C){var r=!1,f=()=>{if(!r){if(r=!0,e.hasAttribute("value")){var o=e.value;re(e,"value",null),e.value=o}if(e.hasAttribute("checked")){var l=e.checked;re(e,"checked",null),e.checked=l}}};e.__on_r=f,se(f),Re()}}function re(e,r,f,o){var l=qe(e);C&&(l[r]=e.getAttribute(r),r==="src"||r==="srcset"||r==="href"&&e.nodeName===Ue)||l[r]!==(l[r]=f)&&(r==="loading"&&(e[He]=f),f==null?e.removeAttribute(r):typeof f!="string"&&Ge(e).includes(r)?e[r]=f:e.setAttribute(r,f))}function qe(e){return e.__attributes??(e.__attributes={[Ye]:e.nodeName.includes("-"),[Be]:e.namespaceURI===Oe})}var ne=new Map;function Ge(e){var r=e.getAttribute("is")||e.nodeName,f=ne.get(r);if(f)return f;ne.set(r,f=[]);for(var o,l=e,u=Element.prototype;u!==l;){o=be(l);for(var a in o)o[a].set&&f.push(a);l=De(l)}return f}export{Xe as e,Ve as i,Pe as r,re as s};
|
|
|
|
|
|
web/sveltekit/build/_app/immutable/chunks/BClW_8sh.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
import{b as y,d as u,H as _,E as g,h as t,C as o,g as i,e as l,i as d,j as p,k as m}from"./Dr93mMbz.js";function C(n,r){let s=null,E=t;var a;if(t){s=p;for(var e=m(document.head);e!==null&&(e.nodeType!==o||e.data!==n);)e=i(e);if(e===null)l(!1);else{var f=i(e);e.remove(),d(f)}}t||(a=document.head.appendChild(y()));try{u(()=>r(a),_|g)}finally{E&&(l(!0),d(s))}}export{C as h};
|
web/sveltekit/build/_app/immutable/chunks/BLULdth_.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
var Be=Object.defineProperty;var me=t=>{throw TypeError(t)};var We=(t,e,r)=>e in t?Be(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var j=(t,e,r)=>We(t,typeof e!="symbol"?e+"":e,r),ne=(t,e,r)=>e.has(t)||me("Cannot "+r);var s=(t,e,r)=>(ne(t,e,"read from private field"),r?r.call(t):e.get(t)),c=(t,e,r)=>e.has(t)?me("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,r),o=(t,e,r,a)=>(ne(t,e,"write to private field"),a?a.call(t,r):e.set(t,r),r),_=(t,e,r)=>(ne(t,e,"access private method"),r);import{aA as $e,k as Se,g as Ve,u as Ye,aB as ye,Z as q,T as Ae,A as v,o as S,h as C,aC as Te,i as qe,w as Oe,H as Ue,aD as Ee,J as x,m as U,X as fe,G as B,a1 as ze,aE as Ge,aF as re,aG as se,aH as we,aI as Je,aJ as Xe,aK as De,b as Me,F as je,a2 as oe,q as z,ac as Ze,y as Ke,aL as Z,E as Qe,ae as et,aM as tt,aN as rt,aO as st,aP as it,ax as at,aQ as nt,v as F,aR as ft,aS as ke,aT as ot,aU as ht,aV as lt,aW as ut,aX as le,C as Fe,av as dt,a3 as ct,aY as ue,z as K,aZ as _t,a0 as pt,a_ as vt,O as gt,p as mt,B as yt,a$ as Tt,a as Et}from"./CYuHyzh3.js";function wt(t){let e=0,r=Ae(0),a;return()=>{$e()&&(Se(r),Ve(()=>(e===0&&(a=Ye(()=>t(()=>ye(r)))),e+=1,()=>{q(()=>{e-=1,e===0&&(a==null||a(),a=void 0,ye(r))})})))}}var bt=Qe|et;function Nt(t,e,r,a){new Rt(t,e,r,a)}var w,G,N,$,m,R,p,b,O,V,L,Y,J,X,D,ie,h,Le,Ce,Ie,de,ee,te,ce,_e;class Rt{constructor(e,r,a,f){c(this,h);j(this,"parent");j(this,"is_pending",!1);j(this,"transform_error");c(this,w);c(this,G,S?v:null);c(this,N);c(this,$);c(this,m);c(this,R,null);c(this,p,null);c(this,b,null);c(this,O,null);c(this,V,0);c(this,L,0);c(this,Y,!1);c(this,J,new Set);c(this,X,new Set);c(this,D,null);c(this,ie,wt(()=>(o(this,D,Ae(s(this,V))),()=>{o(this,D,null)})));var i;o(this,w,e),o(this,N,r),o(this,$,n=>{var d=C;d.b=this,d.f|=Te,a(n)}),this.parent=C.b,this.transform_error=f??((i=this.parent)==null?void 0:i.transform_error)??(n=>n),o(this,m,qe(()=>{if(S){const n=s(this,G);Oe();const d=n.data===Ue;if(n.data.startsWith(Ee)){const u=JSON.parse(n.data.slice(Ee.length));_(this,h,Ce).call(this,u)}else d?_(this,h,Ie).call(this):_(this,h,Le).call(this)}else _(this,h,de).call(this)},bt)),S&&o(this,w,v)}defer_effect(e){Ge(e,s(this,J),s(this,X))}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!s(this,N).pending}update_pending_count(e,r){_(this,h,ce).call(this,e,r),o(this,V,s(this,V)+e),!(!s(this,D)||s(this,Y))&&(o(this,Y,!0),q(()=>{o(this,Y,!1),s(this,D)&&je(s(this,D),s(this,V))}))}get_effect_pending(){return s(this,ie).call(this),Se(s(this,D))}error(e){var r;if(!s(this,N).onerror&&!s(this,N).failed)throw e;(r=B)!=null&&r.is_fork?(s(this,R)&&B.skip_effect(s(this,R)),s(this,p)&&B.skip_effect(s(this,p)),s(this,b)&&B.skip_effect(s(this,b)),B.on_fork_commit(()=>{_(this,h,_e).call(this,e)})):_(this,h,_e).call(this,e)}}w=new WeakMap,G=new WeakMap,N=new WeakMap,$=new WeakMap,m=new WeakMap,R=new WeakMap,p=new WeakMap,b=new WeakMap,O=new WeakMap,V=new WeakMap,L=new WeakMap,Y=new WeakMap,J=new WeakMap,X=new WeakMap,D=new WeakMap,ie=new WeakMap,h=new WeakSet,Le=function(){try{o(this,R,x(()=>s(this,$).call(this,s(this,w))))}catch(e){this.error(e)}},Ce=function(e){const r=s(this,N).failed;r&&o(this,b,x(()=>{r(s(this,w),()=>e,()=>()=>{})}))},Ie=function(){const e=s(this,N).pending;e&&(this.is_pending=!0,o(this,p,x(()=>e(s(this,w)))),q(()=>{var r=o(this,O,document.createDocumentFragment()),a=U();r.append(a),o(this,R,_(this,h,te).call(this,()=>x(()=>s(this,$).call(this,a)))),s(this,L)===0&&(s(this,w).before(r),o(this,O,null),fe(s(this,p),()=>{o(this,p,null)}),_(this,h,ee).call(this,B))}))},de=function(){try{if(this.is_pending=this.has_pending_snippet(),o(this,L,0),o(this,V,0),o(this,R,x(()=>{s(this,$).call(this,s(this,w))})),s(this,L)>0){var e=o(this,O,document.createDocumentFragment());ze(s(this,R),e);const r=s(this,N).pending;o(this,p,x(()=>r(s(this,w))))}else _(this,h,ee).call(this,B)}catch(r){this.error(r)}},ee=function(e){this.is_pending=!1,e.transfer_effects(s(this,J),s(this,X))},te=function(e){var r=C,a=De,f=Me;re(s(this,m)),se(s(this,m)),we(s(this,m).ctx);try{return Je.ensure(),e()}catch(i){return Xe(i),null}finally{re(r),se(a),we(f)}},ce=function(e,r){var a;if(!this.has_pending_snippet()){this.parent&&_(a=this.parent,h,ce).call(a,e,r);return}o(this,L,s(this,L)+e),s(this,L)===0&&(_(this,h,ee).call(this,r),s(this,p)&&fe(s(this,p),()=>{o(this,p,null)}),s(this,O)&&(s(this,w).before(s(this,O)),o(this,O,null)))},_e=function(e){s(this,R)&&(oe(s(this,R)),o(this,R,null)),s(this,p)&&(oe(s(this,p)),o(this,p,null)),s(this,b)&&(oe(s(this,b)),o(this,b,null)),S&&(z(s(this,G)),Ze(),z(Ke()));var r=s(this,N).onerror;let a=s(this,N).failed;var f=!1,i=!1;const n=()=>{if(f){rt();return}f=!0,i&&tt(),s(this,b)!==null&&fe(s(this,b),()=>{o(this,b,null)}),_(this,h,te).call(this,()=>{_(this,h,de).call(this)})},d=l=>{try{i=!0,r==null||r(l,n),i=!1}catch(u){Z(u,s(this,m)&&s(this,m).parent)}a&&o(this,b,_(this,h,te).call(this,()=>{try{return x(()=>{var u=C;u.b=this,u.f|=Te,a(s(this,w),()=>l,()=>n)})}catch(u){return Z(u,s(this,m).parent),null}}))};q(()=>{var l;try{l=this.transform_error(e)}catch(u){Z(u,s(this,m)&&s(this,m).parent);return}l!==null&&typeof l=="object"&&typeof l.then=="function"?l.then(d,u=>Z(u,s(this,m)&&s(this,m).parent)):d(l)})};const St=["touchstart","touchmove"];function At(t){return St.includes(t)}const W=Symbol("events"),He=new Set,pe=new Set;function Ot(t,e,r,a={}){function f(i){if(a.capture||ve.call(e,i),!i.cancelBubble)return it(()=>r==null?void 0:r.call(this,i))}return t.startsWith("pointer")||t.startsWith("touch")||t==="wheel"?q(()=>{e.addEventListener(t,f,a)}):e.addEventListener(t,f,a),f}function It(t,e,r,a,f){var i={capture:a,passive:f},n=Ot(t,e,r,i);(e===document.body||e===window||e===document||e instanceof HTMLMediaElement)&&st(()=>{e.removeEventListener(t,n,i)})}function Ht(t,e,r){(e[W]??(e[W]={}))[t]=r}function Pt(t){for(var e=0;e<t.length;e++)He.add(t[e]);for(var r of pe)r(t)}let be=null;function ve(t){var H,E;var e=this,r=e.ownerDocument,a=t.type,f=((H=t.composedPath)==null?void 0:H.call(t))||[],i=f[0]||t.target;be=t;var n=0,d=be===t&&t[W];if(d){var l=f.indexOf(d);if(l!==-1&&(e===document||e===window)){t[W]=e;return}var u=f.indexOf(e);if(u===-1)return;l<=u&&(n=l)}if(i=f[n]||t.target,i!==e){at(t,"currentTarget",{configurable:!0,get(){return i||r}});var M=De,I=C;se(null),re(null);try{for(var k,y=[];i!==null;){var g=i.assignedSlot||i.parentNode||i.host||null;try{var T=(E=i[W])==null?void 0:E[a];T!=null&&(!i.disabled||t.target===i)&&T.call(i,t)}catch(P){k?y.push(P):k=P}if(t.cancelBubble||g===e||g===null)break;i=g}if(k){for(let P of y)queueMicrotask(()=>{throw P});throw k}}finally{t[W]=e,delete t.currentTarget,se(M),re(I)}}}var Ne;const he=((Ne=globalThis==null?void 0:globalThis.window)==null?void 0:Ne.trustedTypes)&&globalThis.window.trustedTypes.createPolicy("svelte-trusted-html",{createHTML:t=>t});function Dt(t){return(he==null?void 0:he.createHTML(t))??t}function Pe(t){var e=nt("template");return e.innerHTML=Dt(t.replaceAll("<!>","<!---->")),e.content}function A(t,e){var r=C;r.nodes===null&&(r.nodes={start:t,end:e,a:null,t:null})}function xt(t,e){var r=(e&ke)!==0,a=(e&ot)!==0,f,i=!t.startsWith("<!>");return()=>{if(S)return A(v,null),v;f===void 0&&(f=Pe(i?t:"<!>"+t),r||(f=F(f)));var n=a||ft?document.importNode(f,!0):f.cloneNode(!0);if(r){var d=F(n),l=n.lastChild;A(d,l)}else A(n,n);return n}}function Mt(t,e,r="svg"){var a=!t.startsWith("<!>"),f=(e&ke)!==0,i=`<${r}>${a?t:"<!>"+t}</${r}>`,n;return()=>{if(S)return A(v,null),v;if(!n){var d=Pe(i),l=F(d);if(f)for(n=document.createDocumentFragment();F(l);)n.appendChild(F(l));else n=F(l)}var u=n.cloneNode(!0);if(f){var M=F(u),I=u.lastChild;A(M,I)}else A(u,u);return u}}function Bt(t,e){return Mt(t,e,"svg")}function Wt(t=""){if(!S){var e=U(t+"");return A(e,e),e}var r=v;return r.nodeType!==lt?(r.before(r=U()),z(r)):ut(r),A(r,r),r}function $t(){if(S)return A(v,null),v;var t=document.createDocumentFragment(),e=document.createComment(""),r=U();return t.append(e,r),A(e,r),t}function Vt(t,e){if(S){var r=C;((r.f&ht)===0||r.nodes.end===null)&&(r.nodes.end=v),Oe();return}t!==null&&t.before(e)}function Yt(t,e){var r=e==null?"":typeof e=="object"?`${e}`:e;r!==(t.__t??(t.__t=t.nodeValue))&&(t.__t=r,t.nodeValue=`${r}`)}function kt(t,e){return xe(t,e)}function qt(t,e){le(),e.intro=e.intro??!1;const r=e.target,a=S,f=v;try{for(var i=F(r);i&&(i.nodeType!==Fe||i.data!==dt);)i=ct(i);if(!i)throw ue;K(!0),z(i);const n=xe(t,{...e,anchor:i});return K(!1),n}catch(n){if(n instanceof Error&&n.message.split(`
|
| 2 |
-
`).some(d=>d.startsWith("https://svelte.dev/e/")))throw n;return n!==ue&&console.warn("Failed to hydrate: ",n),e.recover===!1&&_t(),le(),pt(r),K(!1),kt(t,e)}finally{K(a),z(f)}}const Q=new Map;function xe(t,{target:e,anchor:r,props:a={},events:f,context:i,intro:n=!0,transformError:d}){le();var l=void 0,u=vt(()=>{var M=r??e.appendChild(U());Nt(M,{pending:()=>{}},y=>{mt({});var g=Me;if(i&&(g.c=i),f&&(a.$$events=f),S&&A(y,null),l=t(y,a)||{},S&&(C.nodes.end=v,v===null||v.nodeType!==Fe||v.data!==yt))throw Tt(),ue;Et()},d);var I=new Set,k=y=>{for(var g=0;g<y.length;g++){var T=y[g];if(!I.has(T)){I.add(T);var H=At(T);for(const ae of[e,document]){var E=Q.get(ae);E===void 0&&(E=new Map,Q.set(ae,E));var P=E.get(T);P===void 0?(ae.addEventListener(T,ve,{passive:H}),E.set(T,1)):E.set(T,P+1)}}}};return k(gt(He)),pe.add(k),()=>{var H;for(var y of I)for(const E of[e,document]){var g=Q.get(E),T=g.get(y);--T==0?(E.removeEventListener(y,ve),g.delete(y),g.size===0&&Q.delete(E)):g.set(y,T)}pe.delete(k),M!==r&&((H=M.parentNode)==null||H.removeChild(M))}});return ge.set(l,u),l}let ge=new WeakMap;function Ut(t,e){const r=ge.get(t);return r?(ge.delete(t),r(e)):Promise.resolve()}const Ft="5";var Re;typeof window<"u"&&((Re=window.__svelte??(window.__svelte={})).v??(Re.v=new Set)).add(Ft);export{Vt as a,Ht as b,$t as c,Pt as d,It as e,xt as f,Bt as g,qt as h,A as i,kt as m,Yt as s,Wt as t,Ut as u};
|
|
|
|
|
|
|
|
|
web/sveltekit/build/_app/immutable/chunks/BPaqPo1M.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
| 1 |
-
import{a as y,f as w,s as v,t as Re,c as ue,g as ve,d as pe,b as ne}from"./BLULdth_.js";import{p as ae,t as C,a as re,s as n,c as i,r,ac as ge,k as t,ah as L,f as oe,j as ee,am as je,l as $,az as Z,af as Ne,b0 as ze}from"./CYuHyzh3.js";import{i as j,p as de}from"./vWNuMvXT.js";import{e as ye,s as V}from"./B7gjWklj.js";import{T as he,s as te,c as De,b as me,a as xe}from"./CW0UkEuV.js";import{b as Ye,_ as qe}from"./DwPgZwgo.js";var Ue=w('<a target="_blank" rel="noopener noreferrer"> </a>'),Ge=w('<li><span class="citation-num"> </span> <div class="citation-body"><div class="citation-line-1"><!> <span class="citation-source"> </span> <span class="citation-vintage"> </span></div> <div class="citation-title"><!></div> <div class="citation-meta"><span class="citation-docid"> </span> <span class="citation-retrieved"> </span></div></div></li>'),Ve=w(`<aside class="citation-drawer svelte-1p339fd" aria-label="Citations"><div class="citation-drawer-head"><span class="section-label"> </span> <span class="citation-drawer-meta">live · primary sources</span></div> <ol class="citation-list"></ol> <div class="citation-drawer-foot"><span class="section-label">Trust signals</span> <p class="citation-foot-copy">All foundation models Apache-2.0. All data from public-record federal,
|
| 2 |
-
state, and city sources. No commercial APIs contacted at runtime.</p></div></aside>`);function kt(F,e){ae(e,!0);let o=L(()=>Object.values(e.citations).sort((l,s)=>l.n-s.n));var g=Ve(),a=i(g),m=i(a),T=i(m);r(m),ge(2),r(a);var c=n(a,2);ye(c,21,()=>t(o),l=>l.id,(l,s)=>{var d=Ge();let h;var p=i(d),k=i(p);r(p);var x=n(p,2),_=i(x),I=i(_);he(I,{get tier(){return t(s).tier},size:10,get color(){return`var(--tier-${t(s).tier??""})`}});var P=n(I,2),M=i(P,!0);r(P);var A=n(P,2),N=i(A);r(A),r(_);var R=n(_,2),D=i(R);{var O=H=>{var Y=Ue(),ie=i(Y,!0);r(Y),C(()=>{V(Y,"href",t(s).url),v(ie,t(s).title)}),y(H,Y)},U=L(()=>t(s).url&&t(s).url.startsWith("http")),W=H=>{var Y=Re();C(()=>v(Y,t(s).title)),y(H,Y)};j(D,H=>{t(U)?H(O):H(W,-1)})}r(R);var G=n(R,2),f=i(G),E=i(f,!0);r(f);var z=n(f,2),K=i(z);r(z),r(G),r(x),r(d),C(()=>{V(d,"id",`cite-${t(s).id??""}`),h=te(d,1,"citation-item",null,h,{"is-active":De.active===t(s).id}),v(k,`[${t(s).n??""}]`),v(M,t(s).source),v(N,`v. ${t(s).vintage??""}`),v(E,t(s).docId),v(K,`retr. ${t(s).retrieved??""}`)}),y(l,d)}),r(c),ge(2),r(g),C(()=>v(T,`Citations · ${t(o).length??""}`)),y(F,g),re()}var We=w('<span class="trace-status-glyph" aria-label="fan-out">⤳</span>'),He=w('<span class="trace-status-glyph" aria-label="merge">⤺</span>'),Je=ve('<svg width="9" height="9" viewBox="0 0 9 9" aria-label="silent"><rect x="0.75" y="0.75" width="7.5" height="7.5" fill="transparent" stroke="#6B6B6B" stroke-width="1.5"></rect></svg>'),Ze=ve('<svg width="9" height="9" viewBox="0 0 9 9" aria-label="error"><rect x="0.75" y="0.75" width="7.5" height="7.5" fill="#B8620A"></rect></svg>'),Ke=ve('<svg width="9" height="9" viewBox="0 0 9 9" aria-label="ok"><rect x="0.75" y="0.75" width="7.5" height="7.5" fill="#0B5394"></rect></svg>');function Qe(F,e){var o=ue(),g=oe(o);{var a=s=>{var d=We();y(s,d)},m=s=>{var d=He();y(s,d)},T=s=>{var d=Je();y(s,d)},c=s=>{var d=Ze();y(s,d)},l=s=>{var d=Ke();y(s,d)};j(g,s=>{e.status==="fan"?s(a):e.status==="merge"?s(m,1):e.status==="silent"?s(T,2):e.status==="error"?s(c,3):s(l,-1)})}y(F,o)}var Xe=w('<span class="trace-note"> </span>'),$e=w('<span class="trace-doc-id"> </span>'),et=w('<span class="trace-silent-tag">silent</span>'),tt=w('<span class="trace-output-model">model: <code> </code></span>'),at=w('<span class="trace-output-claims-count"> </span>'),rt=w('<button type="button" class="trace-output-copy"> </button>'),it=w('<pre class="trace-output-pre"> </pre>'),st=w('<p class="trace-output-text"> </p>'),nt=w('<div class="trace-output-panel"><div class="trace-output-head"><span> </span> <!> <!> <!></div> <!></div>'),ot=w('<div><button type="button" class="trace-row-toggle"><span class="trace-tree-glyph" aria-hidden="true"> </span> <span class="trace-status-col"><!></span> <span class="trace-name-col"><span class="trace-name"> </span> <!> <!></span> <span class="trace-ms-col"> </span> <span class="trace-tier-col"><!> <!></span></button> <!></div> <!>',1);function be(F,e){ae(e,!0);let o=de(e,"depth",3,0),g=de(e,"defaultOpen",3,!1),a=ee(je(g())),m=ee(!1),T=L(()=>{var u;return!!((u=e.node.children)!=null&&u.length)}),c=L(()=>e.node.output!=null||!!e.node.error),l=L(()=>t(T)||t(c)),s=L(()=>o()*16);function d(){t(l)&&$(a,!t(a))}let h=L(()=>e.node.output!=null&&typeof e.node.output=="object"),p=L(()=>{if(e.node.error)return e.node.error;if(e.node.output==null)return"";if(typeof e.node.output=="string")return e.node.output;try{return JSON.stringify(e.node.output,null,2)}catch{return String(e.node.output)}}),k=L(()=>e.node.status==="error"?"Error":e.node.status==="silent"?"Silent reason":"Output");async function x(u){u.stopPropagation();try{await navigator.clipboard.writeText(t(p)),$(m,!0),setTimeout(()=>$(m,!1),1500)}catch{}}var _=ot(),I=oe(_);let P;var M=i(I),A=i(M),N=i(A,!0);r(A);var R=n(A,2),D=i(R);Qe(D,{get status(){return e.node.status}}),r(R);var O=n(R,2),U=i(O),W=i(U,!0);r(U);var G=n(U,2);{var f=u=>{var S=Xe(),J=i(S);r(S),C(()=>v(J,`· ${e.node.note??""}`)),y(u,S)};j(G,u=>{e.node.note&&u(f)})}var E=n(G,2);{var z=u=>{var S=$e(),J=i(S);r(S),C(()=>{V(S,"title",`cited in briefing as [${e.node.docId??""}]`),v(J,`[${e.node.docId??""}]`)}),y(u,S)};j(E,u=>{e.node.docId&&u(z)})}r(O);var K=n(O,2),H=i(K);r(K);var Y=n(K,2),ie=i(Y);{var we=u=>{xe(u,{get tier(){return e.node.tier},compact:!0})};j(ie,u=>{e.node.tier&&u(we)})}var ke=n(ie,2);{var Se=u=>{var S=et();y(u,S)};j(ke,u=>{e.node.status==="silent"&&u(Se)})}r(Y),r(M);var Be=n(M,2);{var Le=u=>{var S=nt();let J;var Q=i(S),X=i(Q),le=i(X,!0);r(X);var se=n(X,2);{var Fe=B=>{var b=tt(),q=n(i(b)),ce=i(q,!0);r(q),r(b),C(()=>v(ce,e.node.model)),y(B,b)};j(se,B=>{e.node.model&&B(Fe)})}var fe=n(se,2);{var Pe=B=>{var b=at(),q=i(b);r(b),C(()=>v(q,`${e.node.claims??""} claim${e.node.claims===1?"":"s"} cited`)),y(B,b)};j(fe,B=>{e.node.claims!=null&&B(Pe)})}var Me=n(fe,2);{var Oe=B=>{var b=rt(),q=i(b,!0);r(b),C(ce=>{V(b,"aria-label",`Copy ${ce??""} to clipboard`),v(q,t(m)?"Copied":"Copy")},[()=>t(k).toLowerCase()]),ne("click",b,x),y(B,b)};j(Me,B=>{t(p)&&B(Oe)})}r(Q);var Te=n(Q,2);{var Ie=B=>{var b=it(),q=i(b,!0);r(b),C(()=>v(q,t(p))),y(B,b)},Ee=B=>{var b=st(),q=i(b,!0);r(b),C(()=>v(q,t(p))),y(B,b)};j(Te,B=>{t(h)?B(Ie):B(Ee,-1)})}r(S),C(()=>{J=me(S,"",J,{"margin-left":`${t(s)+44}px`}),te(X,1,`trace-output-label trace-output-label-${e.node.status??""}`),v(le,t(k))}),y(u,S)};j(Be,u=>{t(a)&&t(c)&&u(Le)})}r(I);var Ae=n(I,2);{var Ce=u=>{var S=ue(),J=oe(S);ye(J,17,()=>e.node.children,Q=>Q.id,(Q,X)=>{{let le=L(()=>o()+1),se=L(()=>t(X).status==="fan");be(Q,{get node(){return t(X)},get depth(){return t(le)},get defaultOpen(){return t(se)}})}}),y(u,S)};j(Ae,u=>{t(a)&&t(T)&&e.node.children&&u(Ce)})}C(()=>{te(I,1,`trace-row trace-row-${e.node.status??""}`),P=me(I,"",P,{"padding-left":`${t(s)+12}px`}),V(M,"aria-expanded",t(l)?t(a):void 0),V(M,"aria-label",`${e.node.name??""}, ${e.node.ms??""}ms, ${e.node.status??""}${e.node.note?", "+e.node.note:""}`),M.disabled=!t(l),v(N,t(T)?t(a)?"▾":"▸":t(c)?t(a)?"▾":"▸":"·"),v(W,e.node.name),v(H,`${e.node.ms??""}ms`)}),ne("click",M,d),y(F,_),re()}pe(["click"]);var lt=w('<div class="trace-body"><div class="trace-col-heads"><span></span><span></span> <span class="trace-col-head">action</span> <span class="trace-col-head">elapsed</span> <span class="trace-col-head">tier</span></div> <div class="trace-tree" role="tree"><!></div></div>'),ct=w('<section aria-label="Run trace"><header class="trace-head"><div class="trace-head-left"><span class="section-label">Run trace</span> <span class="trace-head-meta"><span class="trace-head-stat"> </span> <span class="trace-head-sep">·</span> <span class="trace-head-stat"> </span> <span class="trace-head-sep">·</span> <span class="trace-head-stat trace-head-silent"> </span> <span class="trace-head-sep">·</span> <span class="trace-head-stat"> </span></span></div> <button type="button" class="trace-collapse-btn"> </button></header> <!></section>');function St(F,e){ae(e,!0);let o=ee(!1);function g(f,E){if(f.status==="fan"||f.status==="merge"){for(const z of f.children??[])g(z,E);return}E.push(f);for(const z of f.children??[])g(z,E)}let a=L(()=>{const f=[];for(const E of e.root.children??[])g(E,f);return f}),m=L(()=>e.root.ms>0?e.root.ms:t(a).reduce((f,E)=>f+(E.ms||0),0)),T=L(()=>(t(m)/1e3).toFixed(2)),c=L(()=>t(a).filter(f=>f.status==="ok").length),l=L(()=>t(a).filter(f=>f.status==="silent").length),s=L(()=>t(a).filter(f=>f.status==="error").length);var d=ct();let h;var p=i(d),k=i(p),x=n(i(k),2),_=i(x),I=i(_);r(_);var P=n(_,4),M=i(P);r(P);var A=n(P,4),N=i(A);r(A);var R=n(A,4),D=i(R);r(R),r(x),r(k);var O=n(k,2),U=i(O,!0);r(O),r(p);var W=n(p,2);{var G=f=>{var E=lt(),z=n(i(E),2),K=i(z);be(K,{get node(){return e.root},defaultOpen:!0}),r(z),r(E),y(f,E)};j(W,f=>{t(o)||f(G)})}r(d),C(()=>{h=te(d,1,"trace-ui",null,h,{"is-collapsed":t(o)}),v(I,`${t(T)??""}s total`),v(M,`${t(c)??""} fired`),v(N,`${t(l)??""} silent`),v(D,`${t(s)??""} errors`),V(O,"aria-expanded",!t(o)),v(U,t(o)?"Expand ▾":"Collapse ▴")}),ne("click",O,()=>$(o,!t(o))),y(F,d),re()}pe(["click"]);const dt="https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json",_e=`<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
|
| 3 |
-
<rect width="12" height="12" fill="rgba(42,111,168,0.18)"/>
|
| 4 |
-
<g stroke="#2A6FA8" stroke-width="1.4">
|
| 5 |
-
<line x1="-2" y1="2" x2="14" y2="-14"/>
|
| 6 |
-
<line x1="-2" y1="8" x2="14" y2="-8"/>
|
| 7 |
-
<line x1="-2" y1="14" x2="14" y2="-2"/>
|
| 8 |
-
<line x1="-2" y1="20" x2="14" y2="4"/>
|
| 9 |
-
</g>
|
| 10 |
-
</svg>`,ut=`<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
|
| 11 |
-
<rect width="12" height="12" fill="rgba(42,111,168,0.16)"/>
|
| 12 |
-
<g stroke="#2A6FA8" stroke-width="1.1">
|
| 13 |
-
<line x1="-2" y1="6" x2="14" y2="-10"/>
|
| 14 |
-
<line x1="-2" y1="14" x2="14" y2="-2"/>
|
| 15 |
-
<line x1="-2" y1="22" x2="14" y2="6"/>
|
| 16 |
-
</g>
|
| 17 |
-
</svg>`;async function vt(F,e){const o=new Blob([F],{type:"image/svg+xml"}),g=URL.createObjectURL(o);try{return await new Promise((m,T)=>{const c=new Image(e,e);c.onload=()=>m(c),c.onerror=l=>T(l),c.src=g})}finally{URL.revokeObjectURL(g)}}async function pt(F){const e=[["syn-stripe-45",_e,12],["syn-stripe-45-2x",_e,24],["syn-stripe-45-low",ut,12]];for(const[o,g,a]of e)if(!F.hasImage(o))try{const m=await vt(g,a);F.addImage(o,m,{pixelRatio:a/12})}catch(m){console.warn(`syn-stripe registration failed for ${o}`,m)}}var yt=w('<div class="map-frame svelte-wk2bu4"><div role="application" class="rip-map-container svelte-wk2bu4"></div></div>');function Bt(F,e){ae(e,!0);let o=de(e,"activeLayers",19,()=>({empirical:!0,modeled:!0,synthetic:!0,proxy:!0})),g=ee(null),a=null,m=ee(!1);const T={type:"FeatureCollection",features:[]};function c(h,p){if(!a||!t(m))return;const k=a.getSource(h);k&&k.setData(p??T)}function l(h,p){!a||!t(m)||a.getLayer(h)&&a.setLayoutProperty(h,"visibility",p?"visible":"none")}Z(()=>{c("sandy-empirical",e.sandyEmpirical)}),Z(()=>{c("dep-modeled",e.depModeled)}),Z(()=>{c("syn-prior",e.syntheticPrior)}),Z(()=>{c("proxy-311",e.proxy311)}),Z(()=>{c("register-points",e.registerPoints)}),Z(()=>{c("register-polygons",e.registerPolygons)}),Z(()=>{l("tier-empirical-fill",o().empirical),l("tier-empirical-line",o().empirical),l("tier-modeled-fill",o().modeled),l("tier-modeled-line",o().modeled),l("tier-synthetic-fill",o().synthetic),l("tier-synthetic-line",o().synthetic),l("tier-proxy-dots",o().proxy)}),Z(()=>{!a||!t(m)||a.flyTo({center:[e.address.lon,e.address.lat],zoom:15,essential:!0})}),Ne(async()=>{if(!t(g))return;const h=await qe(()=>import("./D4L2lGt1.js").then(p=>p.m),[],import.meta.url);a=new h.Map({container:t(g),style:dt,center:[e.address.lon,e.address.lat],zoom:15,attributionControl:{compact:!0}}),a.addControl(new h.NavigationControl({visualizePitch:!1}),"top-right"),a.addControl(new h.ScaleControl({maxWidth:100,unit:"imperial"}),"bottom-left"),a.on("load",()=>{if(!a)return;window.__riprapMap=a,pt(a);const p=()=>({type:"FeatureCollection",features:[]});a.addSource("sandy-empirical",{type:"geojson",data:e.sandyEmpirical??p()}),a.addSource("dep-modeled",{type:"geojson",data:e.depModeled??p()}),a.addSource("syn-prior",{type:"geojson",data:e.syntheticPrior??p()}),a.addSource("proxy-311",{type:"geojson",data:e.proxy311??p()}),a.addSource("register-points",{type:"geojson",data:e.registerPoints??p()}),a.addSource("register-polygons",{type:"geojson",data:e.registerPolygons??p()}),a.addSource("queried-address",{type:"geojson",data:{type:"FeatureCollection",features:[{type:"Feature",geometry:{type:"Point",coordinates:[e.address.lon,e.address.lat]},properties:{label:e.address.label}}]}}),a.addLayer({id:"tier-empirical-fill",type:"fill",source:"sandy-empirical",paint:{"fill-color":"#0B5394","fill-opacity":.4}}),a.addLayer({id:"tier-empirical-line",type:"line",source:"sandy-empirical",paint:{"line-color":"#0B5394","line-width":1.5}}),a.addLayer({id:"tier-modeled-fill",type:"fill",source:"dep-modeled",paint:{"fill-color":"#2A6FA8","fill-opacity":.25}}),a.addLayer({id:"tier-modeled-line",type:"line",source:"dep-modeled",paint:{"line-color":"#2A6FA8","line-width":1.5}}),a.addLayer({id:"tier-synthetic-fill",type:"fill",source:"syn-prior",paint:{"fill-pattern":"syn-stripe-45","fill-opacity":.65}}),a.addLayer({id:"tier-synthetic-line",type:"line",source:"syn-prior",paint:{"line-color":"#2A6FA8","line-width":1.5,"line-dasharray":[4,3]}}),a.addLayer({id:"tier-proxy-dots",type:"circle",source:"proxy-311",paint:{"circle-color":"transparent","circle-stroke-color":"#6B6B6B","circle-stroke-width":1.25,"circle-radius":["interpolate",["linear"],["coalesce",["get","count"],1],1,3,5,6,15,9,30,12]}}),a.addLayer({id:"register-polygons-fill",type:"fill",source:"register-polygons",paint:{"fill-color":"#0B5394","fill-opacity":["interpolate",["linear"],["coalesce",["get","pct_inside_sandy"],0],0,.1,25,.2,50,.32,75,.45]}}),a.addLayer({id:"register-polygons-line",type:"line",source:"register-polygons",paint:{"line-color":"#0B5394","line-width":1,"line-opacity":.85}}),a.addLayer({id:"register-points-circle",type:"circle",source:"register-points",paint:{"circle-color":["case",["==",["get","inside_sandy_2012"],!0],"#0B5394","#6B6B6B"],"circle-stroke-color":"#FAFAF7","circle-stroke-width":1.25,"circle-radius":["match",["get","kind"],"subway",4,"school",5,"hospital",6,"nycha",7,4],"circle-opacity":.9}}),a.on("mouseenter","register-points-circle",()=>{a&&(a.getCanvas().style.cursor="pointer")}),a.on("mouseleave","register-points-circle",()=>{a&&(a.getCanvas().style.cursor="")}),a.on("click","register-points-circle",k=>{var O;if(!a||!((O=k.features)!=null&&O.length))return;const x=k.features[0],_=x.properties??{},I=String(_.name??"?"),P=String(_.kind??"?"),M=_.inside_sandy_2012===!0||_.inside_sandy_2012==="true",A=String(_.doc_id??""),N=`
|
| 18 |
-
<div style="font-family: 'IBM Plex Sans', system-ui; font-size: 12px;">
|
| 19 |
-
<div style="font-weight: 600; color: #1A1A1A;">${I}</div>
|
| 20 |
-
<div style="color: #6B6B6B; font-size: 11px; margin-top: 2px;">${P}</div>
|
| 21 |
-
<div style="margin-top: 6px;">
|
| 22 |
-
<span style="font-family: 'IBM Plex Mono', monospace; font-size: 10.5px; color: ${M?"#0B5394":"#6B6B6B"};">
|
| 23 |
-
inside_sandy_2012=${M}
|
| 24 |
-
</span>
|
| 25 |
-
</div>
|
| 26 |
-
${A?`<div style="margin-top: 4px; font-family: 'IBM Plex Mono', monospace; font-size: 10.5px; color: #B8620A;">[${A}]</div>`:""}
|
| 27 |
-
</div>`,R=new h.Popup({closeButton:!0,offset:12}),D=x.geometry.coordinates;R.setLngLat(D).setHTML(N).addTo(a)}),a.addLayer({id:"queried-halo",type:"circle",source:"queried-address",paint:{"circle-color":"rgba(209, 124, 0, 0.20)","circle-radius":16}}),a.addLayer({id:"queried-pin",type:"circle",source:"queried-address",paint:{"circle-color":"#D17C00","circle-stroke-color":"#FAFAF7","circle-stroke-width":2,"circle-radius":7}}),a.addLayer({id:"queried-label",type:"symbol",source:"queried-address",layout:{"text-field":["get","label"],"text-font":["Open Sans Semibold","Arial Unicode MS Bold"],"text-size":12,"text-offset":[0,-1.6],"text-anchor":"bottom"},paint:{"text-color":"#1A1A1A","text-halo-color":"#FAFAF7","text-halo-width":1.5}}),$(m,!0)})}),ze(()=>{a==null||a.remove(),a=null});var s=yt(),d=i(s);Ye(d,h=>$(g,h),()=>t(g)),r(s),C(()=>V(d,"aria-label",`Flood-exposure map for ${e.address.label??""}`)),y(F,s),re()}var ft=w('<button type="button"><span class="map-legend-swatch" aria-hidden="true"><!></span> <span class="map-legend-text"><span class="map-legend-label"> </span> <span class="map-legend-source"> <!></span></span> <span class="map-legend-toggle" aria-hidden="true"> </span></button>'),gt=w('<div class="map-legend" role="group" aria-label="Map layer toggles"><div class="map-legend-head"><span class="section-label"> </span></div> <!></div>');function Lt(F,e){ae(e,!0);const o=[{key:"empirical",tier:"empirical",label:"Sandy Inundation Zone (2012)",source:"NYC OEM"},{key:"modeled",tier:"modeled",label:"FEMA / DEP scenarios",source:"FEMA · NYC DEP"},{key:"synthetic",tier:"synthetic",label:"Synthetic SAR (TerraMind)",source:"TerraMind v1.2"},{key:"proxy",tier:"proxy",label:"311 flood complaints",source:"NYC 311"}];let g=L(()=>e.featureCounts===null?o:o.filter(c=>{var s;const l=(s=e.featureCounts)==null?void 0:s[c.key];return l===void 0||l>0}));var a=ue(),m=oe(a);{var T=c=>{var l=gt(),s=i(l),d=i(s),h=i(d);r(d),r(s);var p=n(s,2);ye(p,17,()=>t(g),k=>k.key,(k,x)=>{var _=ft();let I;var P=i(_),M=i(P);he(M,{get tier(){return t(x).tier},size:11,get color(){return`var(--tier-${t(x).tier??""})`}}),r(P);var A=n(P,2),N=i(A),R=i(N,!0);r(N);var D=n(N,2),O=i(D),U=n(O);xe(U,{get tier(){return t(x).tier},compact:!0}),r(D),r(A);var W=n(A,2),G=i(W,!0);r(W),r(_),C(()=>{I=te(_,1,"map-legend-item",null,I,{"is-on":e.active[t(x).key],"is-off":!e.active[t(x).key]}),V(_,"aria-pressed",e.active[t(x).key]),v(R,t(x).label),v(O,`${t(x).source??""} · `),v(G,e.active[t(x).key]?"ON":"OFF")}),ne("click",_,()=>e.onToggle(t(x).key)),y(k,_)}),r(l),C(()=>v(h,`Layers · ${t(g).length??""}`)),y(c,l)};j(m,c=>{t(g).length&&c(T)})}y(F,a),re()}pe(["click"]);export{kt as C,Lt as M,Bt as R,St as T};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
web/sveltekit/build/_app/immutable/chunks/BkOwEZvC.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
var rt=e=>{throw TypeError(e)};var Dt=(e,t,n)=>t.has(e)||rt("Cannot "+n);var y=(e,t,n)=>(Dt(e,t,"read from private field"),n?n.call(e):t.get(e)),A=(e,t,n)=>t.has(e)?rt("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n);import{bf as Pe,bg as Vt,z as at,w as T,x as I,y as O,aa as we,bh as Bt}from"./Dr93mMbz.js";const M=[];function Ke(e,t=Pe){let n=null;const a=new Set;function r(o){if(Vt(e,o)&&(e=o,n)){const l=!M.length;for(const c of a)c[1](),M.push(c,e);if(l){for(let c=0;c<M.length;c+=2)M[c][0](M[c+1]);M.length=0}}}function i(o){r(o(e))}function s(o,l=Pe){const c=[o,l];return a.add(c),a.size===1&&(n=t(r,i)||Pe),o(e),()=>{a.delete(c),a.size===0&&n&&(n(),n=null)}}return{set:r,update:i,subscribe:s}}class Me{constructor(t,n){this.status=t,typeof n=="string"?this.body={message:n}:n?this.body=n:this.body={message:`Error: ${t}`}}toString(){return JSON.stringify(this.body)}}class ze{constructor(t,n){try{new Headers({location:n})}catch{throw new Error(`Invalid redirect location ${JSON.stringify(n)}: this string contains characters that cannot be used in HTTP headers`)}this.status=t,this.location=n}}class Fe extends Error{constructor(t,n,a){super(a),this.status=t,this.text=n}}new URL("sveltekit-internal://");function Kt(e,t){return e==="/"||t==="ignore"?e:t==="never"?e.endsWith("/")?e.slice(0,-1):e:t==="always"&&!e.endsWith("/")?e+"/":e}function Mt(e){return e.split("%25").map(decodeURI).join("%25")}function zt(e){for(const t in e)e[t]=decodeURIComponent(e[t]);return e}function $e({href:e}){return e.split("#")[0]}function C(){}function Ft(...e){let t=5381;for(const n of e)if(typeof n=="string"){let a=n.length;for(;a;)t=t*33^n.charCodeAt(--a)}else if(ArrayBuffer.isView(n)){const a=new Uint8Array(n.buffer,n.byteOffset,n.byteLength);let r=a.length;for(;r;)t=t*33^a[--r]}else throw new TypeError("value must be a string or TypedArray");return(t>>>0).toString(36)}new TextEncoder;function Gt(e){const t=atob(e),n=new Uint8Array(t.length);for(let a=0;a<t.length;a++)n[a]=t.charCodeAt(a);return n}const Ht=window.fetch;window.fetch=(e,t)=>((e instanceof Request?e.method:(t==null?void 0:t.method)||"GET")!=="GET"&&X.delete(Ge(e)),Ht(e,t));const X=new Map;function Wt(e,t){const n=Ge(e,t),a=document.querySelector(n);if(a!=null&&a.textContent){a.remove();let{body:r,...i}=JSON.parse(a.textContent);const s=a.getAttribute("data-ttl");return s&&X.set(n,{body:r,init:i,ttl:1e3*Number(s)}),a.getAttribute("data-b64")!==null&&(r=Gt(r)),Promise.resolve(new Response(r,i))}return window.fetch(e,t)}function Jt(e,t,n){if(X.size>0){const a=Ge(e,n),r=X.get(a);if(r){if(performance.now()<r.ttl&&["default","force-cache","only-if-cached",void 0].includes(n==null?void 0:n.cache))return new Response(r.body,r.init);X.delete(a)}}return window.fetch(t,n)}function Ge(e,t){let a=`script[data-sveltekit-fetched][data-url=${JSON.stringify(e instanceof Request?e.url:e)}]`;if(t!=null&&t.headers||t!=null&&t.body){const r=[];t.headers&&r.push([...new Headers(t.headers)].join(",")),t.body&&(typeof t.body=="string"||ArrayBuffer.isView(t.body))&&r.push(t.body),a+=`[data-hash="${Ft(...r)}"]`}return a}const Yt=/^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;function Xt(e){const t=[];return{pattern:e==="/"?/^\/$/:new RegExp(`^${Zt(e).map(a=>{const r=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(a);if(r)return t.push({name:r[1],matcher:r[2],optional:!1,rest:!0,chained:!0}),"(?:/([^]*))?";const i=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(a);if(i)return t.push({name:i[1],matcher:i[2],optional:!0,rest:!1,chained:!0}),"(?:/([^/]+))?";if(!a)return;const s=a.split(/\[(.+?)\](?!\])/);return"/"+s.map((l,c)=>{if(c%2){if(l.startsWith("x+"))return Ce(String.fromCharCode(parseInt(l.slice(2),16)));if(l.startsWith("u+"))return Ce(String.fromCharCode(...l.slice(2).split("-").map(m=>parseInt(m,16))));const d=Yt.exec(l),[,u,w,p,f]=d;return t.push({name:p,matcher:f,optional:!!u,rest:!!w,chained:w?c===1&&s[0]==="":!1}),w?"([^]*?)":u?"([^/]*)?":"([^/]+?)"}return Ce(l)}).join("")}).join("")}/?$`),params:t}}function Qt(e){return e!==""&&!/^\([^)]+\)$/.test(e)}function Zt(e){return e.slice(1).split("/").filter(Qt)}function en(e,t,n){const a={},r=e.slice(1),i=r.filter(o=>o!==void 0);let s=0;for(let o=0;o<t.length;o+=1){const l=t[o];let c=r[o-s];if(l.chained&&l.rest&&s&&(c=r.slice(o-s,o+1).filter(d=>d).join("/"),s=0),c===void 0)if(l.rest)c="";else continue;if(!l.matcher||n[l.matcher](c)){a[l.name]=c;const d=t[o+1],u=r[o+1];d&&!d.rest&&d.optional&&u&&l.chained&&(s=0),!d&&!u&&Object.keys(a).length===i.length&&(s=0);continue}if(l.optional&&l.chained){s++;continue}return}if(!s)return a}function Ce(e){return e.normalize().replace(/[[\]]/g,"\\$&").replace(/%/g,"%25").replace(/\//g,"%2[Ff]").replace(/\?/g,"%3[Ff]").replace(/#/g,"%23").replace(/[.*+?^${}()|\\]/g,"\\$&")}function tn({nodes:e,server_loads:t,dictionary:n,matchers:a}){const r=new Set(t);return Object.entries(n).map(([o,[l,c,d]])=>{const{pattern:u,params:w}=Xt(o),p={id:o,exec:f=>{const m=u.exec(f);if(m)return en(m,w,a)},errors:[1,...d||[]].map(f=>e[f]),layouts:[0,...c||[]].map(s),leaf:i(l)};return p.errors.length=p.layouts.length=Math.max(p.errors.length,p.layouts.length),p});function i(o){const l=o<0;return l&&(o=~o),[l,e[o]]}function s(o){return o===void 0?o:[r.has(o),e[o]]}}function wt(e,t=JSON.parse){try{return t(sessionStorage[e])}catch{}}function ot(e,t,n=JSON.stringify){const a=n(t);try{sessionStorage[e]=a}catch{}}var ht;const U=((ht=globalThis.__sveltekit_ihrl7b)==null?void 0:ht.base)??"";var pt;const nn=((pt=globalThis.__sveltekit_ihrl7b)==null?void 0:pt.assets)??U??"",rn="1778024768646",vt="sveltekit:snapshot",yt="sveltekit:scroll",bt="sveltekit:states",an="sveltekit:pageurl",F="sveltekit:history",Z="sveltekit:navigation",D={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},Ue=location.origin;function He(e){if(e instanceof URL)return e;let t=document.baseURI;if(!t){const n=document.getElementsByTagName("base");t=n.length?n[0].href:document.URL}return new URL(e,t)}function B(){return{x:pageXOffset,y:pageYOffset}}function z(e,t){return e.getAttribute(`data-sveltekit-${t}`)}const st={...D,"":D.hover};function kt(e){let t=e.assignedSlot??e.parentNode;return(t==null?void 0:t.nodeType)===11&&(t=t.host),t}function St(e,t){for(;e&&e!==t;){if(e.nodeName.toUpperCase()==="A"&&e.hasAttribute("href"))return e;e=kt(e)}}function qe(e,t,n){let a;try{if(a=new URL(e instanceof SVGAElement?e.href.baseVal:e.href,document.baseURI),n&&a.hash.match(/^#[^/]/)){const o=location.hash.split("#")[1]||"/";a.hash=`#${o}${a.hash}`}}catch{}const r=e instanceof SVGAElement?e.target.baseVal:e.target,i=!a||!!r||Ae(a,t,n)||(e.getAttribute("rel")||"").split(/\s+/).includes("external"),s=(a==null?void 0:a.origin)===Ue&&e.hasAttribute("download");return{url:a,external:i,target:r,download:s}}function ve(e){let t=null,n=null,a=null,r=null,i=null,s=null,o=e;for(;o&&o!==document.documentElement;)a===null&&(a=z(o,"preload-code")),r===null&&(r=z(o,"preload-data")),t===null&&(t=z(o,"keepfocus")),n===null&&(n=z(o,"noscroll")),i===null&&(i=z(o,"reload")),s===null&&(s=z(o,"replacestate")),o=kt(o);function l(c){switch(c){case"":case"true":return!0;case"off":case"false":return!1;default:return}}return{preload_code:st[a??"off"],preload_data:st[r??"off"],keepfocus:l(t),noscroll:l(n),reload:l(i),replace_state:l(s)}}function it(e){const t=Ke(e);let n=!0;function a(){n=!0,t.update(s=>s)}function r(s){n=!1,t.set(s)}function i(s){let o;return t.subscribe(l=>{(o===void 0||n&&l!==o)&&s(o=l)})}return{notify:a,set:r,subscribe:i}}const Et={v:C};function on(){const{set:e,subscribe:t}=Ke(!1);let n;async function a(){clearTimeout(n);try{const r=await fetch(`${nn}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(!r.ok)return!1;const s=(await r.json()).version!==rn;return s&&(e(!0),Et.v(),clearTimeout(n)),s}catch{return!1}}return{subscribe:t,check:a}}function Ae(e,t,n){return e.origin!==Ue||!e.pathname.startsWith(t)?!0:n?e.pathname!==location.pathname:!1}function Pn(e){}const Rt=new Set(["load","prerender","csr","ssr","trailingSlash","config"]);[...Rt];const sn=new Set([...Rt]);[...sn];function ln(e){return e.filter(t=>t!=null)}function me(e,t){return e+"/"+t}function We(e){return e instanceof Me||e instanceof Fe?e.status:500}function cn(e){return e instanceof Fe?e.text:"Internal Error"}let R,ee,je;const fn=at.toString().includes("$$")||/function \w+\(\) \{\}/.test(at.toString()),lt="a:";var oe,se,ie,le,ce,fe,ue,de,gt,he,mt,pe,_t;fn?(R={data:{},form:null,error:null,params:{},route:{id:null},state:{},status:-1,url:new URL(lt)},ee={current:null},je={current:!1}):(R=new(gt=class{constructor(){A(this,oe,T({}));A(this,se,T(null));A(this,ie,T(null));A(this,le,T({}));A(this,ce,T({id:null}));A(this,fe,T({}));A(this,ue,T(-1));A(this,de,T(new URL(lt)))}get data(){return I(y(this,oe))}set data(t){O(y(this,oe),t)}get form(){return I(y(this,se))}set form(t){O(y(this,se),t)}get error(){return I(y(this,ie))}set error(t){O(y(this,ie),t)}get params(){return I(y(this,le))}set params(t){O(y(this,le),t)}get route(){return I(y(this,ce))}set route(t){O(y(this,ce),t)}get state(){return I(y(this,fe))}set state(t){O(y(this,fe),t)}get status(){return I(y(this,ue))}set status(t){O(y(this,ue),t)}get url(){return I(y(this,de))}set url(t){O(y(this,de),t)}},oe=new WeakMap,se=new WeakMap,ie=new WeakMap,le=new WeakMap,ce=new WeakMap,fe=new WeakMap,ue=new WeakMap,de=new WeakMap,gt),ee=new(mt=class{constructor(){A(this,he,T(null))}get current(){return I(y(this,he))}set current(t){O(y(this,he),t)}},he=new WeakMap,mt),je=new(_t=class{constructor(){A(this,pe,T(!1))}get current(){return I(y(this,pe))}set current(t){O(y(this,pe),t)}},pe=new WeakMap,_t),Et.v=()=>je.current=!0);function un(e){Object.assign(R,e)}const dn=new Set(["icon","shortcut icon","apple-touch-icon"]);let J=null;const N=wt(yt)??{},te=wt(vt)??{},j={url:it({}),page:it({}),navigating:Ke(null),updated:on()};function Je(e){N[e]=B()}function hn(e,t){let n=e+1;for(;N[n];)delete N[n],n+=1;for(n=t+1;te[n];)delete te[n],n+=1}function ne(e,t=!1){return t?location.replace(e.href):location.href=e.href,new Promise(C)}async function xt(){if("serviceWorker"in navigator){const e=await navigator.serviceWorker.getRegistration(U||"/");e&&await e.update()}}let Ye,De,ye,P,Ve,S;const be=[],ke=[];let v=null;function Se(){var e;(e=v==null?void 0:v.fork)==null||e.then(t=>t==null?void 0:t.discard()),v=null}const _e=new Map,Lt=new Set,pn=new Set,Q=new Set;let _={branch:[],error:null,url:null},Ut=!1,Ee=!1,ct=!0,re=!1,Y=!1,At=!1,Xe=!1,Tt,k,L,V;const Re=new Set,ft=new Map,ut=new Map;async function Nn(e,t,n){var i,s,o,l;globalThis.__sveltekit_ihrl7b&&(globalThis.__sveltekit_ihrl7b.query,globalThis.__sveltekit_ihrl7b.prerender),document.URL!==location.href&&(location.href=location.href),S=e,await((s=(i=e.hooks).init)==null?void 0:s.call(i)),Ye=tn(e),P=document.documentElement,Ve=t,De=e.nodes[0],ye=e.nodes[1],De(),ye(),k=(o=history.state)==null?void 0:o[F],L=(l=history.state)==null?void 0:l[Z],k||(k=L=Date.now(),history.replaceState({...history.state,[F]:k,[Z]:L},""));const a=N[k];function r(){a&&(history.scrollRestoration="manual",scrollTo(a.x,a.y))}n?(r(),await Ln(Ve,n)):(await G({type:"enter",url:He(S.hash?Tn(new URL(location.href)):location.href),replace_state:!0}),r()),xn()}function gn(){be.length=0,Xe=!1}function It(e){ke.some(t=>t==null?void 0:t.snapshot)&&(te[e]=ke.map(t=>{var n;return(n=t==null?void 0:t.snapshot)==null?void 0:n.capture()}))}function Ot(e){var t;(t=te[e])==null||t.forEach((n,a)=>{var r,i;(i=(r=ke[a])==null?void 0:r.snapshot)==null||i.restore(n)})}function dt(){Je(k),ot(yt,N),It(L),ot(vt,te)}async function Pt(e,t,n,a){let r,i;t.invalidateAll&&Se(),await G({type:"goto",url:He(e),keepfocus:t.keepFocus,noscroll:t.noScroll,replace_state:t.replaceState,state:t.state,redirect_count:n,nav_token:a,accept:()=>{if(t.invalidateAll){Xe=!0,r=new Set;for(const[s,o]of ft)for(const l of o.keys())r.add(me(s,l));i=new Set;for(const[s,o]of ut)for(const l of o.keys())i.add(me(s,l))}t.invalidate&&t.invalidate.forEach(Rn)}}),t.invalidateAll&&we().then(we).then(()=>{for(const[s,o]of ft)for(const[l,{resource:c}]of o)r!=null&&r.has(me(s,l))&&c.refresh();for(const[s,o]of ut)for(const[l,{resource:c}]of o)i!=null&&i.has(me(s,l))&&c.reconnect()})}async function mn(e){if(e.id!==(v==null?void 0:v.id)){Se();const t={};Re.add(t),v={id:e.id,token:t,promise:Ct({...e,preload:t}).then(n=>(Re.delete(t),n.type==="loaded"&&n.state.error&&Se(),n)),fork:null}}return v.promise}async function Ne(e){var n;const t=(n=await Te(e,!1))==null?void 0:n.route;t&&await Promise.all([...t.layouts,t.leaf].filter(Boolean).map(a=>a[1]()))}async function $t(e,t,n){var i;const a={params:_.params,route:{id:((i=_.route)==null?void 0:i.id)??null},url:new URL(location.href)};_={...e.state,nav:a};const r=document.querySelector("style[data-sveltekit]");if(r&&r.remove(),Object.assign(R,e.props.page),Tt=new S.root({target:t,props:{...e.props,stores:j,components:ke},hydrate:n,sync:!1,transformError:void 0}),await Promise.resolve(),Ot(L),n){const s={from:null,to:{...a,scroll:N[k]??B()},willUnload:!1,type:"enter",complete:Promise.resolve()};Q.forEach(o=>o(s))}Ee=!0}async function xe({url:e,params:t,branch:n,errors:a,status:r,error:i,route:s,form:o}){let l="never";if(U&&(e.pathname===U||e.pathname===U+"/"))l="always";else for(const f of n)(f==null?void 0:f.slash)!==void 0&&(l=f.slash);e.pathname=Kt(e.pathname,l),e.search=e.search;const c={type:"loaded",state:{url:e,params:t,branch:n,error:i,route:s},props:{constructors:ln(n).map(f=>f.node.component),page:nt(R)}};o!==void 0&&(c.props.form=o);let d={},u=!R,w=0;for(let f=0;f<Math.max(n.length,_.branch.length);f+=1){const m=n[f],h=_.branch[f];(m==null?void 0:m.data)!==(h==null?void 0:h.data)&&(u=!0),m&&(d={...d,...m.data},u&&(c.props[`data_${w}`]=d),w+=1)}return(!_.url||e.href!==_.url.href||_.error!==i||o!==void 0&&o!==R.form||u)&&(c.props.page={error:i,params:t,route:{id:(s==null?void 0:s.id)??null},state:{},status:r,url:new URL(e),form:o??null,data:u?d:R.data}),c}async function Qe({loader:e,parent:t,url:n,params:a,route:r,server_data_node:i}){var c,d;let s=null;const o={dependencies:new Set,params:new Set,parent:!1,route:!1,url:!1,search_params:new Set},l=await e();return{node:l,loader:e,server:i,universal:(c=l.universal)!=null&&c.load?{type:"data",data:s,uses:o}:null,data:s??(i==null?void 0:i.data)??null,slash:((d=l.universal)==null?void 0:d.trailingSlash)??(i==null?void 0:i.slash)}}function _n(e,t,n){let a=e instanceof Request?e.url:e;const r=new URL(a,n);r.origin===n.origin&&(a=r.href.slice(n.origin.length));const i=Ee?Jt(a,r.href,t):Wt(a,t);return{resolved:r,promise:i}}function wn(e,t,n,a,r,i){if(Xe)return!0;if(!r)return!1;if(r.parent&&e||r.route&&t||r.url&&n)return!0;for(const s of r.search_params)if(a.has(s))return!0;for(const s of r.params)if(i[s]!==_.params[s])return!0;for(const s of r.dependencies)if(be.some(o=>o(new URL(s))))return!0;return!1}function Ze(e,t){return(e==null?void 0:e.type)==="data"?e:(e==null?void 0:e.type)==="skip"?t??null:null}function vn(e,t){if(!e)return new Set(t.searchParams.keys());const n=new Set([...e.searchParams.keys(),...t.searchParams.keys()]);for(const a of n){const r=e.searchParams.getAll(a),i=t.searchParams.getAll(a);r.every(s=>i.includes(s))&&i.every(s=>r.includes(s))&&n.delete(a)}return n}function yn({error:e,url:t,route:n,params:a}){return{type:"loaded",state:{error:e,url:t,route:n,params:a,branch:[]},props:{page:nt(R),constructors:[]}}}async function Ct({id:e,invalidating:t,url:n,params:a,route:r,preload:i}){if((v==null?void 0:v.id)===e)return Re.delete(v.token),v.promise;const{errors:s,layouts:o,leaf:l}=r,c=[...o,l];s.forEach(h=>h==null?void 0:h().catch(C)),c.forEach(h=>h==null?void 0:h[1]().catch(C));const d=_.url?e!==Le(_.url):!1,u=_.route?r.id!==_.route.id:!1,w=vn(_.url,n);let p=!1;const f=c.map(async(h,g)=>{var $;if(!h)return;const b=_.branch[g];return h[1]===(b==null?void 0:b.loader)&&!wn(p,u,d,w,($=b.universal)==null?void 0:$.uses,a)?b:(p=!0,Qe({loader:h[1],url:n,params:a,route:r,parent:async()=>{var ge;const q={};for(let K=0;K<g;K+=1)Object.assign(q,(ge=await f[K])==null?void 0:ge.data);return q},server_data_node:Ze(h[0]?{type:"skip"}:null,h[0]?b==null?void 0:b.server:void 0)}))});for(const h of f)h.catch(C);const m=[];for(let h=0;h<c.length;h+=1)if(c[h])try{m.push(await f[h])}catch(g){if(g instanceof ze)return{type:"redirect",location:g.location};if(Re.has(i))return yn({error:await ae(g,{params:a,url:n,route:{id:r.id}}),url:n,params:a,route:r});let b=We(g),x;if(g instanceof Me)x=g.body;else{if(await j.updated.check())return await xt(),await ne(n);x=await ae(g,{params:a,url:n,route:{id:r.id}})}const $=await bn(h,m,s);return $?xe({url:n,params:a,branch:m.slice(0,$.idx).concat($.node),errors:s,status:b,error:x,route:r}):await Nt(n,{id:r.id},x,b)}else m.push(void 0);return xe({url:n,params:a,branch:m,errors:s,status:200,error:null,route:r,form:t?void 0:null})}async function bn(e,t,n){for(;e--;)if(n[e]){let a=e;for(;!t[a];)a-=1;try{return{idx:a+1,node:{node:await n[e](),loader:n[e],data:{},server:null,universal:null}}}catch{continue}}}async function et({status:e,error:t,url:n,route:a}){const r={};let i=null;try{const s=await Qe({loader:De,url:n,params:r,route:a,parent:()=>Promise.resolve({}),server_data_node:Ze(i)}),o={node:await ye(),loader:ye,universal:null,server:null,data:null};return xe({url:n,params:r,branch:[s,o],status:e,error:t,errors:[],route:null})}catch(s){if(s instanceof ze)return Pt(new URL(s.location,location.href),{},0);throw s}}async function kn(e){const t=e.href;if(_e.has(t))return _e.get(t);let n;try{const a=(async()=>{let r=await S.hooks.reroute({url:new URL(e),fetch:async(i,s)=>_n(i,s,e).promise})??e;if(typeof r=="string"){const i=new URL(e);S.hash?i.hash=r:i.pathname=r,r=i}return r})();_e.set(t,a),n=await a}catch{_e.delete(t);return}return n}async function Te(e,t){if(e&&!Ae(e,U,S.hash)){const n=await kn(e);if(!n)return;const a=Sn(n);for(const r of Ye){const i=r.exec(a);if(i)return{id:Le(e),invalidating:t,route:r,params:zt(i),url:e}}}}function Sn(e){return Mt(S.hash?e.hash.replace(/^#/,"").replace(/[?#].+/,""):e.pathname.slice(U.length))||"/"}function Le(e){return(S.hash?e.hash.replace(/^#/,""):e.pathname)+e.search}function jt({url:e,type:t,intent:n,delta:a,event:r,scroll:i}){let s=!1;const o=tt(_,n,e,t,i??null);a!==void 0&&(o.navigation.delta=a),r!==void 0&&(o.navigation.event=r);const l={...o.navigation,cancel:()=>{s=!0,o.reject(new Error("navigation cancelled"))}};return re||Lt.forEach(c=>c(l)),s?null:o}async function G({type:e,url:t,popped:n,keepfocus:a,noscroll:r,replace_state:i,state:s={},redirect_count:o=0,nav_token:l={},accept:c=C,block:d=C,event:u}){var K;const w=V;V=l;const p=await Te(t,!1),f=e==="enter"?tt(_,p,t,e):jt({url:t,type:e,delta:n==null?void 0:n.delta,intent:p,scroll:n==null?void 0:n.scroll,event:u});if(!f){d(),V===l&&(V=w);return}const m=k,h=L;c(),re=!0,Ee&&f.navigation.type!=="enter"&&j.navigating.set(ee.current=f.navigation);let g=p&&await Ct(p);if(!g){if(Ae(t,U,S.hash))return await ne(t,i);g=await Nt(t,{id:null},await ae(new Fe(404,"Not Found",`Not found: ${t.pathname}`),{url:t,params:{},route:{id:null}}),404,i)}if(t=(p==null?void 0:p.url)||t,V!==l)return f.reject(new Error("navigation aborted")),!1;if(g.type==="redirect"){if(o<20){await G({type:e,url:new URL(g.location,t),popped:n,keepfocus:a,noscroll:r,replace_state:i,state:s,redirect_count:o+1,nav_token:l}),f.fulfil(void 0);return}g=await et({status:500,error:await ae(new Error("Redirect loop"),{url:t,params:{},route:{id:null}}),url:t,route:{id:null}})}else g.props.page.status>=400&&await j.updated.check()&&(await xt(),await ne(t,i));if(gn(),Je(m),It(h),g.props.page.url.pathname!==t.pathname&&(t.pathname=g.props.page.url.pathname),s=n?n.state:s,!n){const E=i?0:1,H={[F]:k+=E,[Z]:L+=E,[bt]:s};(i?history.replaceState:history.pushState).call(history,H,"",t),i||hn(k,L)}const b=p&&(v==null?void 0:v.id)===p.id?v.fork:null;v!=null&&v.fork&&!b&&Se(),v=null,g.props.page.state=s;let x;if(Ee){const E=(await Promise.all(Array.from(pn,W=>W(f.navigation)))).filter(W=>typeof W=="function");if(E.length>0){let W=function(){E.forEach(Oe=>{Q.delete(Oe)})};E.push(W),E.forEach(Oe=>{Q.add(Oe)})}const H=f.navigation.to;_={...g.state,nav:{params:H.params,route:H.route,url:H.url}},g.props.page&&(g.props.page.url=t);const Ie=b&&await b;Ie?x=Ie.commit():(J=null,Tt.$set(g.props),J&&Object.assign(g.props.page,J),un(g.props.page),x=(K=Bt)==null?void 0:K()),At=!0}else await $t(g,Ve,!1);const{activeElement:$}=document;await x,await we(),await we();let q=null;if(ct){const E=n?n.scroll:r?B():null;E?scrollTo(E.x,E.y):(q=t.hash&&document.getElementById(qt(t)))?q.scrollIntoView():scrollTo(0,0)}const ge=document.activeElement!==$&&document.activeElement!==document.body;!a&&!ge&&An(t,!q),ct=!0,g.props.page&&(J&&Object.assign(g.props.page,J),Object.assign(R,g.props.page)),re=!1,e==="popstate"&&Ot(L),f.fulfil(void 0),f.navigation.to&&(f.navigation.to.scroll=B()),Q.forEach(E=>E(f.navigation)),j.navigating.set(ee.current=null)}async function Nt(e,t,n,a,r){return e.origin===Ue&&e.pathname===location.pathname&&!Ut?await et({status:a,error:n,url:e,route:t}):await ne(e,r)}function En(){let e,t={element:void 0,href:void 0},n;P.addEventListener("mousemove",o=>{const l=o.target;clearTimeout(e),e=setTimeout(()=>{i(l,D.hover)},20)});function a(o){o.defaultPrevented||i(o.composedPath()[0],D.tap)}P.addEventListener("mousedown",a),P.addEventListener("touchstart",a,{passive:!0});const r=new IntersectionObserver(o=>{for(const l of o)l.isIntersecting&&(Ne(new URL(l.target.href)),r.unobserve(l.target))},{threshold:0});async function i(o,l){const c=St(o,P),d=c===t.element&&(c==null?void 0:c.href)===t.href&&l>=n;if(!c||d)return;const{url:u,external:w,download:p}=qe(c,U,S.hash);if(w||p)return;const f=ve(c),m=u&&Le(_.url)===Le(u);if(!(f.reload||m))if(l<=f.preload_data){t={element:c,href:c.href},n=D.tap;const h=await Te(u,!1);if(!h)return;mn(h)}else l<=f.preload_code&&(t={element:c,href:c.href},n=l,Ne(u))}function s(){r.disconnect();for(const o of P.querySelectorAll("a")){const{url:l,external:c,download:d}=qe(o,U,S.hash);if(c||d)continue;const u=ve(o);u.reload||(u.preload_code===D.viewport&&r.observe(o),u.preload_code===D.eager&&Ne(l))}}Q.add(s),s()}function ae(e,t){if(e instanceof Me)return e.body;const n=We(e),a=cn(e);return S.hooks.handleError({error:e,event:t,status:n,message:a})??{message:a}}function qn(e,t={}){return e=new URL(He(e)),e.origin!==Ue?Promise.reject(new Error("goto: invalid URL")):Pt(e,t,0)}function Rn(e){if(typeof e=="function")be.push(e);else{const{href:t}=new URL(e,location.href);be.push(n=>n.href===t)}}function xn(){var t;history.scrollRestoration="manual",addEventListener("beforeunload",n=>{let a=!1;if(dt(),!re){const r=tt(_,void 0,null,"leave"),i={...r.navigation,cancel:()=>{a=!0,r.reject(new Error("navigation cancelled"))}};Lt.forEach(s=>s(i))}a?(n.preventDefault(),n.returnValue=""):history.scrollRestoration="auto"}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&dt()}),(t=navigator.connection)!=null&&t.saveData||En(),P.addEventListener("click",async n=>{if(n.button||n.which!==1||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||n.defaultPrevented)return;const a=St(n.composedPath()[0],P);if(!a)return;const{url:r,external:i,target:s,download:o}=qe(a,U,S.hash);if(!r)return;if(s==="_parent"||s==="_top"){if(window.parent!==window)return}else if(s&&s!=="_self")return;const l=ve(a);if(!(a instanceof SVGAElement)&&r.protocol!==location.protocol&&!(r.protocol==="https:"||r.protocol==="http:")||o)return;const[d,u]=(S.hash?r.hash.replace(/^#/,""):r.href).split("#"),w=d===$e(location);if(i||l.reload&&(!w||!u)){jt({url:r,type:"link",event:n})?re=!0:n.preventDefault();return}if(u!==void 0&&w){const[,p]=_.url.href.split("#");if(p===u){if(n.preventDefault(),u===""||u==="top"&&a.ownerDocument.getElementById("top")===null)scrollTo({top:0});else{const f=a.ownerDocument.getElementById(decodeURIComponent(u));f&&(f.scrollIntoView(),f.focus())}return}if(Y=!0,Je(k),e(r),!l.replace_state)return;Y=!1}n.preventDefault(),await new Promise(p=>{requestAnimationFrame(()=>{setTimeout(p,0)}),setTimeout(p,100)}),await G({type:"link",url:r,keepfocus:l.keepfocus,noscroll:l.noscroll,replace_state:l.replace_state??r.href===location.href,event:n})}),P.addEventListener("submit",n=>{if(n.defaultPrevented)return;const a=HTMLFormElement.prototype.cloneNode.call(n.target),r=n.submitter;if(((r==null?void 0:r.formTarget)||a.target)==="_blank"||((r==null?void 0:r.formMethod)||a.method)!=="get")return;const o=new URL((r==null?void 0:r.hasAttribute("formaction"))&&(r==null?void 0:r.formAction)||a.action);if(Ae(o,U,!1))return;const l=n.target,c=ve(l);if(c.reload)return;n.preventDefault(),n.stopPropagation();const d=new FormData(l,r);o.search=new URLSearchParams(d).toString(),G({type:"form",url:o,keepfocus:c.keepfocus,noscroll:c.noscroll,replace_state:c.replace_state??o.href===location.href,event:n})}),addEventListener("popstate",async n=>{var a;if(!Be){if((a=n.state)!=null&&a[F]){const r=n.state[F];if(V={},r===k)return;const i=N[r],s=n.state[bt]??{},o=new URL(n.state[an]??location.href),l=n.state[Z],c=_.url?$e(location)===$e(_.url):!1;if(l===L&&(At||c)){s!==R.state&&(R.state=s),e(o),N[k]=B(),i&&scrollTo(i.x,i.y),k=r;return}const u=r-k;await G({type:"popstate",url:o,popped:{state:s,scroll:i,delta:u},accept:()=>{k=r,L=l},block:()=>{history.go(-u)},nav_token:V,event:n})}else if(!Y){const r=new URL(location.href);e(r),S.hash&&location.reload()}}}),addEventListener("hashchange",()=>{Y&&(Y=!1,history.replaceState({...history.state,[F]:++k,[Z]:L},"",location.href))});for(const n of document.querySelectorAll("link"))dn.has(n.rel)&&(n.href=n.href);addEventListener("pageshow",n=>{n.persisted&&j.navigating.set(ee.current=null)});function e(n){_.url=R.url=n,j.page.set(nt(R)),j.page.notify()}}async function Ln(e,{status:t=200,error:n,node_ids:a,params:r,route:i,server_route:s,data:o,form:l}){Ut=!0;const c=new URL(location.href);let d;({params:r={},route:i={id:null}}=await Te(c,!1)||{}),d=Ye.find(({id:p})=>p===i.id);let u,w=!0;try{const p=a.map(async(m,h)=>{const g=o[h];return g!=null&&g.uses&&(g.uses=Un(g.uses)),Qe({loader:S.nodes[m],url:c,params:r,route:i,parent:async()=>{const b={};for(let x=0;x<h;x+=1)Object.assign(b,(await p[x]).data);return b},server_data_node:Ze(g)})}),f=await Promise.all(p);if(d){const m=d.layouts;for(let h=0;h<m.length;h++)m[h]||f.splice(h,0,void 0)}u=await xe({url:c,params:r,branch:f,status:t,error:n,errors:d==null?void 0:d.errors,form:l,route:d??null})}catch(p){if(p instanceof ze){await ne(new URL(p.location,location.href));return}u=await et({status:We(p),error:await ae(p,{url:c,params:r,route:i}),url:c,route:i}),e.textContent="",w=!1}finally{}u.props.page&&(u.props.page.state={}),await $t(u,e,w)}function Un(e){return{dependencies:new Set((e==null?void 0:e.dependencies)??[]),params:new Set((e==null?void 0:e.params)??[]),parent:!!(e!=null&&e.parent),route:!!(e!=null&&e.route),url:!!(e!=null&&e.url),search_params:new Set((e==null?void 0:e.search_params)??[])}}let Be=!1;function An(e,t=!0){const n=document.querySelector("[autofocus]");if(n)n.focus();else{const a=qt(e);if(a&&document.getElementById(a)){const{x:i,y:s}=B();setTimeout(()=>{const o=history.state;Be=!0,location.replace(new URL(`#${a}`,location.href)),history.replaceState(o,"",e),t&&scrollTo(i,s),Be=!1})}else{const i=document.body,s=i.getAttribute("tabindex");i.tabIndex=-1,i.focus({preventScroll:!0,focusVisible:!1}),s!==null?i.setAttribute("tabindex",s):i.removeAttribute("tabindex")}const r=getSelection();if(r&&r.type!=="None"){const i=[];for(let s=0;s<r.rangeCount;s+=1)i.push(r.getRangeAt(s));setTimeout(()=>{if(r.rangeCount===i.length){for(let s=0;s<r.rangeCount;s+=1){const o=i[s],l=r.getRangeAt(s);if(o.commonAncestorContainer!==l.commonAncestorContainer||o.startContainer!==l.startContainer||o.endContainer!==l.endContainer||o.startOffset!==l.startOffset||o.endOffset!==l.endOffset)return}r.removeAllRanges()}})}}}function tt(e,t,n,a,r=null){var c,d;let i,s;const o=new Promise((u,w)=>{i=u,s=w});return o.catch(C),{navigation:{from:{params:e.params,route:{id:((c=e.route)==null?void 0:c.id)??null},url:e.url,scroll:B()},to:n&&{params:(t==null?void 0:t.params)??null,route:{id:((d=t==null?void 0:t.route)==null?void 0:d.id)??null},url:n,scroll:r},willUnload:!t,type:a,complete:o},fulfil:i,reject:s}}function nt(e){return{data:e.data,error:e.error,form:e.form,params:e.params,route:e.route,state:e.state,status:e.status,url:e.url}}function Tn(e){const t=new URL(e);return t.hash=decodeURIComponent(e.hash),t}function qt(e){let t;if(S.hash){const[,,n]=e.hash.split("#",3);t=n??""}else t=e.hash.slice(1);return decodeURIComponent(t)}export{Nn as a,qn as g,Pn as l,R as p,j as s};
|
web/sveltekit/build/_app/immutable/chunks/{DO0D806X.js → BzAi_hbj.js}
RENAMED
|
@@ -1 +1 @@
|
|
| 1 |
-
import{s as e,p as r}from"./
|
|
|
|
| 1 |
+
import{s as e,p as r}from"./BkOwEZvC.js";const t={get error(){return r.error},get params(){return r.params},get status(){return r.status},get url(){return r.url}};e.updated.check;const a=t;export{a as p};
|
web/sveltekit/build/_app/immutable/chunks/C8fGhQJC.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import{j as Ie,o as De,a as v,f as p,s as f,t as ye,d as Ce,c as xe,b as Ae,k as _e}from"./WYoL_k1G.js";import{h as ve,P as Be,j as we,bd as Oe,d as ze,v as He,aW as Ye,b7 as je,k as We,b as Ue,e as Re,i as ke,q as Ke,aw as Ge,p as X,t as C,a as J,s as i,c as r,r as t,ab as re,x as a,B as q,F as ce,z as Qe,be as Ve,y as pe,w as Se,f as ee}from"./Dr93mMbz.js";import{B as Ze,i as N,p as ie}from"./kFXL_I1K.js";import{e as te,s as m,a as ue,i as oe,b as le,c as Xe,C as Je}from"./Chht4iZ-.js";import{T as ge,c as $e,b as et,d as Fe}from"./B4YyPI_Q.js";import{b as tt,_ as at}from"./DM0dVcpA.js";function rt(F,e,S,_,M,n){let u=ve;ve&&Be();var d=null;ve&&we.nodeType===Oe&&(d=we,Be());var x=ve?we:F,h=new Ze(x,!1);ze(()=>{const w=e()||null;var y=w==="svg"?je:void 0;if(w===null){h.ensure(null,null);return}return h.ensure(w,o=>{if(w){if(d=ve?d:Ye(w,y),Ie(d,d),_){ve&&De(w)&&d.append(document.createComment(""));var s=ve?We(d):d.appendChild(Ue());ve&&(s===null?Re(!1):ke(s)),_(d,s)}Ke.nodes.end=d,o.before(d)}ve&&ke(o)}),()=>{}},He),Ge(()=>{}),u&&(Re(!0),ke(x))}var st=p('<a target="_blank" rel="noopener noreferrer"> </a>'),it=p('<li><span class="citation-num"> </span> <div class="citation-body"><div class="citation-line-1"><!> <span class="citation-source"> </span> <span class="citation-vintage"> </span></div> <div class="citation-title"><!></div> <div class="citation-meta"><span class="citation-docid"> </span> <span class="citation-retrieved"> </span></div></div></li>'),nt=p(`<aside class="citation-drawer svelte-1p339fd" aria-label="Citations"><div class="citation-drawer-head"><span class="section-label"> </span> <span class="citation-drawer-meta">live · primary sources</span></div> <ol class="citation-list"></ol> <div class="citation-drawer-foot"><span class="section-label">Trust signals</span> <p class="citation-foot-copy">All foundation models Apache-2.0. All data from public-record federal,
|
| 2 |
+
state, and city sources. No commercial APIs contacted at runtime.</p></div></aside>`);function Ar(F,e){X(e,!0);let S=q(()=>Object.values(e.citations).sort((x,h)=>x.n-h.n));var _=nt(),M=r(_),n=r(M),u=r(n);t(n),re(2),t(M);var d=i(M,2);te(d,21,()=>a(S),x=>x.id,(x,h)=>{var w=it();let y;var o=r(w),s=r(o);t(o);var l=i(o,2),b=r(l),A=r(b);ge(A,{get tier(){return a(h).tier},size:10,get color(){return`var(--tier-${a(h).tier??""})`}});var g=i(A,2),c=r(g,!0);t(g);var k=i(g,2),R=r(k);t(k),t(b);var P=i(b,2),D=r(P);{var j=L=>{var Y=st(),G=r(Y,!0);t(Y),C(()=>{m(Y,"href",a(h).url),f(G,a(h).title)}),v(L,Y)},I=q(()=>a(h).url&&a(h).url.startsWith("http")),O=L=>{var Y=ye();C(()=>f(Y,a(h).title)),v(L,Y)};N(D,L=>{a(I)?L(j):L(O,-1)})}t(P);var z=i(P,2),U=r(z),T=r(U,!0);t(U);var E=i(U,2),B=r(E);t(E),t(z),t(l),t(w),C(()=>{m(w,"id",`cite-${a(h).id??""}`),y=ue(w,1,"citation-item",null,y,{"is-active":$e.active===a(h).id}),f(s,`[${a(h).n??""}]`),f(c,a(h).source),f(R,`v. ${a(h).vintage??""}`),f(T,a(h).docId),f(B,`retr. ${a(h).retrieved??""}`)}),v(x,w)}),t(d),re(2),t(_),C(()=>f(u,`Citations · ${a(S).length??""}`)),v(F,_),J()}const lt="https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json",Le=`<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
|
| 3 |
+
<rect width="12" height="12" fill="rgba(42,111,168,0.18)"/>
|
| 4 |
+
<g stroke="#2A6FA8" stroke-width="1.4">
|
| 5 |
+
<line x1="-2" y1="2" x2="14" y2="-14"/>
|
| 6 |
+
<line x1="-2" y1="8" x2="14" y2="-8"/>
|
| 7 |
+
<line x1="-2" y1="14" x2="14" y2="-2"/>
|
| 8 |
+
<line x1="-2" y1="20" x2="14" y2="4"/>
|
| 9 |
+
</g>
|
| 10 |
+
</svg>`,ot=`<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
|
| 11 |
+
<rect width="12" height="12" fill="rgba(42,111,168,0.16)"/>
|
| 12 |
+
<g stroke="#2A6FA8" stroke-width="1.1">
|
| 13 |
+
<line x1="-2" y1="6" x2="14" y2="-10"/>
|
| 14 |
+
<line x1="-2" y1="14" x2="14" y2="-2"/>
|
| 15 |
+
<line x1="-2" y1="22" x2="14" y2="6"/>
|
| 16 |
+
</g>
|
| 17 |
+
</svg>`;async function dt(F,e){const S=new Blob([F],{type:"image/svg+xml"}),_=URL.createObjectURL(S);try{return await new Promise((n,u)=>{const d=new Image(e,e);d.onload=()=>n(d),d.onerror=x=>u(x),d.src=_})}finally{URL.revokeObjectURL(_)}}async function vt(F){const e=[["syn-stripe-45",Le,12],["syn-stripe-45-2x",Le,24],["syn-stripe-45-low",ot,12]];for(const[S,_,M]of e)if(!F.hasImage(S))try{const n=await dt(_,M);F.addImage(S,n,{pixelRatio:M/12})}catch(n){console.warn(`syn-stripe registration failed for ${S}`,n)}}var ct=p('<span class="link-badge svelte-wk2bu4" aria-hidden="true"> </span>'),ut=p('<div class="map-frame svelte-wk2bu4"><div role="application" class="rip-map-container svelte-wk2bu4"></div> <!></div>');function Nr(F,e){X(e,!0);let S=ie(e,"activeLayers",19,()=>({empirical:!0,modeled:!0,synthetic:!0,proxy:!0})),_=ie(e,"linkedKey",3,null),M=Se(null),n=null,u=Se(!1);const d={type:"FeatureCollection",features:[]};function x(l,b){if(!n||!a(u))return;const A=n.getSource(l);A&&A.setData(b??d)}function h(l,b){!n||!a(u)||n.getLayer(l)&&n.setLayoutProperty(l,"visibility",b?"visible":"none")}ce(()=>{x("sandy-empirical",e.sandyEmpirical)}),ce(()=>{x("dep-modeled",e.depModeled)}),ce(()=>{x("syn-prior",e.syntheticPrior)}),ce(()=>{x("proxy-311",e.proxy311)}),ce(()=>{x("register-points",e.registerPoints)}),ce(()=>{x("register-polygons",e.registerPolygons)}),ce(()=>{h("tier-empirical-fill",S().empirical),h("tier-empirical-line",S().empirical),h("tier-modeled-fill",S().modeled),h("tier-modeled-line",S().modeled),h("tier-synthetic-fill",S().synthetic),h("tier-synthetic-line",S().synthetic),h("tier-proxy-dots",S().proxy)}),ce(()=>{!n||!a(u)||n.flyTo({center:[e.address.lon,e.address.lat],zoom:15,essential:!0})}),Qe(async()=>{if(!a(M))return;const l=await at(()=>import("./D4L2lGt1.js").then(b=>b.m),[],import.meta.url);n=new l.Map({container:a(M),style:lt,center:[e.address.lon,e.address.lat],zoom:15,attributionControl:{compact:!0}}),n.addControl(new l.NavigationControl({visualizePitch:!1}),"top-right"),n.addControl(new l.ScaleControl({maxWidth:100,unit:"imperial"}),"bottom-left"),n.on("load",()=>{if(!n)return;window.__riprapMap=n,vt(n);const b=()=>({type:"FeatureCollection",features:[]});n.addSource("sandy-empirical",{type:"geojson",data:e.sandyEmpirical??b()}),n.addSource("dep-modeled",{type:"geojson",data:e.depModeled??b()}),n.addSource("syn-prior",{type:"geojson",data:e.syntheticPrior??b()}),n.addSource("proxy-311",{type:"geojson",data:e.proxy311??b()}),n.addSource("register-points",{type:"geojson",data:e.registerPoints??b()}),n.addSource("register-polygons",{type:"geojson",data:e.registerPolygons??b()}),n.addSource("queried-address",{type:"geojson",data:{type:"FeatureCollection",features:[{type:"Feature",geometry:{type:"Point",coordinates:[e.address.lon,e.address.lat]},properties:{label:e.address.label}}]}}),n.addLayer({id:"tier-empirical-fill",type:"fill",source:"sandy-empirical",paint:{"fill-color":"#0B5394","fill-opacity":.4}}),n.addLayer({id:"tier-empirical-line",type:"line",source:"sandy-empirical",paint:{"line-color":"#0B5394","line-width":1.5}}),n.addLayer({id:"tier-modeled-fill",type:"fill",source:"dep-modeled",paint:{"fill-color":"#2A6FA8","fill-opacity":.25}}),n.addLayer({id:"tier-modeled-line",type:"line",source:"dep-modeled",paint:{"line-color":"#2A6FA8","line-width":1.5}}),n.addLayer({id:"tier-synthetic-fill",type:"fill",source:"syn-prior",paint:{"fill-pattern":"syn-stripe-45","fill-opacity":.65}}),n.addLayer({id:"tier-synthetic-line",type:"line",source:"syn-prior",paint:{"line-color":"#2A6FA8","line-width":1.5,"line-dasharray":[4,3]}}),n.addLayer({id:"tier-proxy-dots",type:"circle",source:"proxy-311",paint:{"circle-color":"transparent","circle-stroke-color":"#6B6B6B","circle-stroke-width":1.25,"circle-radius":["interpolate",["linear"],["coalesce",["get","count"],1],1,3,5,6,15,9,30,12]}}),n.addLayer({id:"register-polygons-fill",type:"fill",source:"register-polygons",paint:{"fill-color":"#0B5394","fill-opacity":["interpolate",["linear"],["coalesce",["get","pct_inside_sandy"],0],0,.1,25,.2,50,.32,75,.45]}}),n.addLayer({id:"register-polygons-line",type:"line",source:"register-polygons",paint:{"line-color":"#0B5394","line-width":1,"line-opacity":.85}}),n.addLayer({id:"register-points-circle",type:"circle",source:"register-points",paint:{"circle-color":["case",["==",["get","inside_sandy_2012"],!0],"#0B5394","#6B6B6B"],"circle-stroke-color":"#FAFAF7","circle-stroke-width":1.25,"circle-radius":["match",["get","kind"],"subway",4,"school",5,"hospital",6,"nycha",7,4],"circle-opacity":.9}}),n.on("mouseenter","register-points-circle",()=>{n&&(n.getCanvas().style.cursor="pointer")}),n.on("mouseleave","register-points-circle",()=>{n&&(n.getCanvas().style.cursor="")}),n.on("click","register-points-circle",A=>{var z;if(!n||!((z=A.features)!=null&&z.length))return;const g=A.features[0],c=g.properties??{},k=String(c.name??"?"),R=String(c.kind??"?"),P=c.inside_sandy_2012===!0||c.inside_sandy_2012==="true",D=String(c.doc_id??""),j=`
|
| 18 |
+
<div style="font-family: 'IBM Plex Sans', system-ui; font-size: 12px;">
|
| 19 |
+
<div style="font-weight: 600; color: #1A1A1A;">${k}</div>
|
| 20 |
+
<div style="color: #6B6B6B; font-size: 11px; margin-top: 2px;">${R}</div>
|
| 21 |
+
<div style="margin-top: 6px;">
|
| 22 |
+
<span style="font-family: 'IBM Plex Mono', monospace; font-size: 10.5px; color: ${P?"#0B5394":"#6B6B6B"};">
|
| 23 |
+
inside_sandy_2012=${P}
|
| 24 |
+
</span>
|
| 25 |
+
</div>
|
| 26 |
+
${D?`<div style="margin-top: 4px; font-family: 'IBM Plex Mono', monospace; font-size: 10.5px; color: #B8620A;">[${D}]</div>`:""}
|
| 27 |
+
</div>`,I=new l.Popup({closeButton:!0,offset:12}),O=g.geometry.coordinates;I.setLngLat(O).setHTML(j).addTo(n)}),n.addLayer({id:"queried-halo",type:"circle",source:"queried-address",paint:{"circle-color":"rgba(209, 124, 0, 0.20)","circle-radius":16}}),n.addLayer({id:"queried-pin",type:"circle",source:"queried-address",paint:{"circle-color":"#D17C00","circle-stroke-color":"#FAFAF7","circle-stroke-width":2,"circle-radius":7}}),n.addLayer({id:"queried-label",type:"symbol",source:"queried-address",layout:{"text-field":["get","label"],"text-font":["Open Sans Semibold","Arial Unicode MS Bold"],"text-size":12,"text-offset":[0,-1.6],"text-anchor":"bottom"},paint:{"text-color":"#1A1A1A","text-halo-color":"#FAFAF7","text-halo-width":1.5}}),pe(u,!0)})}),Ve(()=>{n==null||n.remove(),n=null});var w=ut(),y=r(w);tt(y,l=>pe(M,l),()=>a(M));var o=i(y,2);{var s=l=>{var b=ct(),A=r(b);t(b),C(()=>f(A,`linked: ${_()??""}`)),v(l,b)};N(o,l=>{_()&&l(s)})}t(w),C(()=>{m(w,"data-linked",_()??""),m(y,"aria-label",`Flood-exposure map for ${e.address.label??""}`)}),v(F,w),J()}const Ne=["cornerstone","keystone","touchstone","lodestone","capstone"],Me={cornerstone:{name:"Cornerstone",role:"the hazard reader",tag:"what NYC's ground remembers"},keystone:{name:"Keystone",role:"the asset register",tag:"what's exposed"},touchstone:{name:"Touchstone",role:"the live observer",tag:"what's happening now"},lodestone:{name:"Lodestone",role:"the projector",tag:"what's coming"},capstone:{name:"Capstone",role:"the synthesizer",tag:"writes it all down with citations"}};var gt=p('<span class="layers-count svelte-1g2dety"> </span>'),ht=p('<li class="layers-row layers-row-empty svelte-1g2dety"><span class="layers-empty-text svelte-1g2dety">no map layers — see Findings cards</span></li>'),_t=p('<li class="layers-row layers-row-empty svelte-1g2dety"><span class="layers-empty-text svelte-1g2dety">not a map layer</span></li>'),ft=p('<span class="layers-state-dim svelte-1g2dety" title="Not yet wired to map source">off · catalog</span>'),mt=p('<li><span class="layers-glyph svelte-1g2dety" aria-hidden="true"><!></span> <span class="layers-text svelte-1g2dety"><span class="layers-label svelte-1g2dety"> </span> <span class="layers-meta svelte-1g2dety"> <!></span></span> <span class="layers-state svelte-1g2dety"><!></span></li>'),yt=p('<details open=""><summary class="svelte-1g2dety"><span class="layers-caret svelte-1g2dety" aria-hidden="true">▾</span> <span class="layers-stone-name svelte-1g2dety"> </span> <span class="layers-stone-tag svelte-1g2dety"> </span> <!></summary> <ul class="layers-list svelte-1g2dety"><!></ul></details>'),pt=p('<button type="button"><!> <span> </span> <span class="layers-master-state svelte-1g2dety"> </span></button>'),xt=p('<aside class="layers-panel svelte-1g2dety" aria-label="Map layers grouped by Stone"><div class="layers-head svelte-1g2dety"><span class="section-label">Layers · grouped by Stone</span></div> <!> <div class="layers-masters svelte-1g2dety" role="group" aria-label="Master tier toggles"><span class="section-label">Tier toggles</span> <div class="layers-master-row svelte-1g2dety"></div></div></aside>');function Br(F,e){X(e,!0);const S={cornerstone:[{label:"Sandy Inundation Zone (2012)",source:"NYC OEM",tier:"empirical",wired:!0},{label:"FEMA / DEP scenarios",source:"FEMA · NYC DEP",tier:"modeled",wired:!0},{label:"Ida HWM points (2021)",source:"USGS STN",tier:"empirical",wired:!1},{label:"Microtopography (HAND/TWI)",source:"USGS 3DEP",tier:"proxy",wired:!1}],keystone:[{label:"MTA subway entrances",source:"MTA Open Data",tier:"empirical",wired:!0},{label:"NYCHA developments",source:"NYC OD phvi-damg",tier:"empirical",wired:!0},{label:"DOE schools",source:"NYC DOE Locations",tier:"empirical",wired:!0},{label:"DOH hospitals",source:"NYS DOH vn5v-hh5r",tier:"empirical",wired:!0},{label:"TerraMind Buildings (current)",source:"msradam/TerraMind-NYC-Adapters",tier:"synthetic",wired:!1}],touchstone:[{label:"311 flood complaints",source:"NYC 311",tier:"proxy",wired:!0},{label:"FloodNet sensors",source:"FloodNet NYC",tier:"empirical",wired:!1},{label:"TerraMind LULC (current)",source:"msradam/TerraMind-NYC-Adapters",tier:"synthetic",wired:!1},{label:"Prithvi-NYC-Pluvial flood pred.",source:"msradam/Prithvi-EO-2.0-NYC-Pluvial",tier:"modeled",wired:!1}],lodestone:[],capstone:[]};function _(w){return!!e.active[w.tier]}function M(w){return S[w].length}const n=[{k:"empirical",tier:"empirical",label:"EMP"},{k:"modeled",tier:"modeled",label:"MOD"},{k:"proxy",tier:"proxy",label:"PRX"},{k:"synthetic",tier:"synthetic",label:"SYN"}];var u=xt(),d=i(r(u),2);te(d,16,()=>Ne,w=>w,(w,y)=>{var o=yt(),s=r(o),l=i(r(s),2),b=r(l,!0);t(l);var A=i(l,2),g=r(A);t(A);var c=i(A,2);{var k=z=>{var U=gt(),T=r(U,!0);t(U),C(E=>f(T,E),[()=>M(y)]),v(z,U)},R=q(()=>M(y)>0);N(c,z=>{a(R)&&z(k)})}t(s);var P=i(s,2),D=r(P);{var j=z=>{var U=ht();v(z,U)},I=z=>{var U=_t();v(z,U)},O=z=>{var U=xe(),T=ee(U);te(T,17,()=>S[y],oe,(E,B)=>{var L=mt();let Y;var G=r(L),ae=r(G);ge(ae,{get tier(){return a(B).tier},size:11,get color(){return`var(--tier-${a(B).tier??""})`}}),t(G);var Q=i(G,2),Z=r(Q),ne=r(Z,!0);t(Z);var H=i(Z,2),V=r(H),K=i(V);et(K,{get tier(){return a(B).tier},compact:!0}),t(H),t(Q);var W=i(Q,2),$=r(W);{var de=se=>{var me=ft();v(se,me)},fe=se=>{var me=ye("on");v(se,me)},be=q(()=>_(a(B))),he=se=>{var me=ye("off");v(se,me)};N($,se=>{a(B).wired?a(be)?se(fe,1):se(he,-1):se(de)})}t(W),t(L),C(()=>{Y=ue(L,1,"layers-row svelte-1g2dety",null,Y,{dim:!a(B).wired}),f(ne,a(B).label),f(V,`${a(B).source??""} · `)}),v(E,L)}),v(z,U)};N(D,z=>{y==="lodestone"?z(j):y==="capstone"?z(I,1):z(O,-1)})}t(P),t(o),C(()=>{ue(o,1,`layers-group region-${y??""}`,"svelte-1g2dety"),f(b,Me[y].name),f(g,`— ${Me[y].tag??""}`)}),v(w,o)});var x=i(d,2),h=i(r(x),2);te(h,21,()=>n,w=>w.k,(w,y)=>{var o=pt();let s;var l=r(o);ge(l,{get tier(){return a(y).tier},size:11,get color(){return`var(--tier-${a(y).tier??""})`}});var b=i(l,2),A=r(b,!0);t(b);var g=i(b,2),c=r(g,!0);t(g),t(o),C(()=>{s=ue(o,1,"layers-master svelte-1g2dety",null,s,{"is-on":e.active[a(y).k]}),m(o,"aria-pressed",e.active[a(y).k]),f(A,a(y).label),f(c,e.active[a(y).k]?"ON":"OFF")}),Ae("click",o,()=>e.onToggle(a(y).k)),v(w,o)}),t(h),t(x),t(u),v(F,u),J()}Ce(["click"]);var bt=p('<span class="rh-sep svelte-1vuwkv4">·</span> <span class="rh-item rh-silent svelte-1vuwkv4"><strong class="svelte-1vuwkv4"> </strong> silent</span>',1),wt=p('<span class="rh-sep svelte-1vuwkv4">·</span> <span class="rh-item rh-warn svelte-1vuwkv4"><strong class="svelte-1vuwkv4"> </strong> warned</span>',1),kt=p('<span class="rh-sep svelte-1vuwkv4">·</span> <span class="rh-item rh-err svelte-1vuwkv4"><strong class="svelte-1vuwkv4"> </strong> errored</span>',1),St=p('<span class="rh-sep svelte-1vuwkv4">·</span> <span class="rh-item rh-notinvoked svelte-1vuwkv4"><strong class="svelte-1vuwkv4"> </strong> not invoked</span>',1),Mt=p('<span class="rh-sep svelte-1vuwkv4">·</span> <span class="rh-item svelte-1vuwkv4"><strong class="svelte-1vuwkv4"> </strong> cache</span>',1),Ct=p('<div class="rh svelte-1vuwkv4"><span class="rh-item svelte-1vuwkv4"><strong class="svelte-1vuwkv4"> </strong> Stones</span> <span class="rh-sep svelte-1vuwkv4">·</span> <span class="rh-item svelte-1vuwkv4"><strong class="svelte-1vuwkv4"> </strong> fired</span> <!> <!> <!> <!> <span class="rh-sep svelte-1vuwkv4">·</span> <span class="rh-item svelte-1vuwkv4"><strong class="svelte-1vuwkv4"> </strong> </span> <span class="rh-sep svelte-1vuwkv4">·</span> <span class="rh-item svelte-1vuwkv4"><strong class="svelte-1vuwkv4"> </strong> wall-clock</span> <!> <span class="rh-sep svelte-1vuwkv4">·</span> <span class="rh-item rh-total svelte-1vuwkv4"><strong class="svelte-1vuwkv4"> </strong> registered</span></div>');function At(F,e){X(e,!0);function S(H){return H.flatMap(V=>V.children?[V,...S(V.children)]:[V])}let _=q(()=>e.stones.flatMap(H=>S(H.members))),M=q(()=>a(_).length),n=q(()=>a(_).filter(H=>H.status==="fired"||H.status==="warned").length),u=q(()=>a(_).filter(H=>H.status==="silent_by_design").length),d=q(()=>a(_).filter(H=>H.status==="warned").length),x=q(()=>a(_).filter(H=>H.status==="errored").length),h=q(()=>a(_).filter(H=>H.status==="not_invoked").length),w=q(()=>e.wallSeconds==null?"—":e.wallSeconds<1?`${Math.round(e.wallSeconds*1e3)}ms`:`${e.wallSeconds.toFixed(1)}s`);var y=Ct(),o=r(y),s=r(o),l=r(s,!0);t(s),re(),t(o);var b=i(o,4),A=r(b),g=r(A,!0);t(A),re(),t(b);var c=i(b,2);{var k=H=>{var V=bt(),K=i(ee(V),2),W=r(K),$=r(W,!0);t(W),re(),t(K),C(()=>f($,a(u))),v(H,V)};N(c,H=>{a(u)>0&&H(k)})}var R=i(c,2);{var P=H=>{var V=wt(),K=i(ee(V),2),W=r(K),$=r(W,!0);t(W),re(),t(K),C(()=>f($,a(d))),v(H,V)};N(R,H=>{a(d)>0&&H(P)})}var D=i(R,2);{var j=H=>{var V=kt(),K=i(ee(V),2),W=r(K),$=r(W,!0);t(W),re(),t(K),C(()=>f($,a(x))),v(H,V)};N(D,H=>{a(x)>0&&H(j)})}var I=i(D,2);{var O=H=>{var V=St(),K=i(ee(V),2),W=r(K),$=r(W,!0);t(W),re(),t(K),C(()=>f($,a(h))),v(H,V)};N(I,H=>{a(h)>0&&H(O)})}var z=i(I,4),U=r(z),T=r(U,!0);t(U);var E=i(U);t(z);var B=i(z,4),L=r(B),Y=r(L,!0);t(L),re(),t(B);var G=i(B,2);{var ae=H=>{var V=Mt(),K=i(ee(V),2),W=r(K),$=r(W);t(W),re(),t(K),C(de=>f($,`${de??""}%`),[()=>Math.round(e.cacheHit*100)]),v(H,V)};N(G,H=>{e.cacheHit!=null&&H(ae)})}var Q=i(G,4),Z=r(Q),ne=r(Z,!0);t(Z),re(),t(Q),t(y),C(()=>{f(l,e.stones.length),f(g,a(n)),f(T,e.cards.length),f(E,` evidence card${e.cards.length===1?"":"s"}`),f(Y,a(w)),f(ne,a(M))}),v(F,y),J()}var Nt=p('<div class="subhead svelte-lygj3j"> </div>'),Bt=p('<p class="body-prose svelte-lygj3j"> </p>'),Rt=p('<div class="body body-headline svelte-lygj3j"><div class="headline svelte-lygj3j"> </div> <!> <!></div>');function Ft(F,e){X(e,!0);var S=Rt(),_=r(S),M=r(_,!0);t(_);var n=i(_,2);{var u=h=>{var w=Nt(),y=r(w,!0);t(w),C(()=>f(y,e.card.subhead)),v(h,w)};N(n,h=>{e.card.subhead&&h(u)})}var d=i(n,2);{var x=h=>{var w=Bt(),y=r(w,!0);t(w),C(()=>f(y,e.card.body)),v(h,w)};N(d,h=>{e.card.body&&h(x)})}t(S),C(()=>{le(_,`color: var(--tier-${e.card.tier??""});`),f(M,e.card.headline??"")}),v(F,S),J()}var Lt=p('<th class="svelte-1nlkuao"> </th>'),Et=p('<td class="svelte-1nlkuao"> </td>'),Pt=p('<tr class="svelte-1nlkuao"></tr>'),Tt=p('<div class="body-sub svelte-1nlkuao"> </div>'),qt=p('<div class="body body-tabular svelte-1nlkuao"><table class="t svelte-1nlkuao"><thead><tr></tr></thead><tbody></tbody></table> <!></div>');function It(F,e){X(e,!0);var S=qt(),_=r(S),M=r(_),n=r(M);te(n,21,()=>e.card.columns??[],oe,(h,w)=>{var y=Lt(),o=r(y,!0);t(y),C(()=>f(o,a(w))),v(h,y)}),t(n),t(M);var u=i(M);te(u,21,()=>e.card.rows??[],oe,(h,w)=>{var y=Pt();te(y,21,()=>a(w),oe,(o,s)=>{var l=Et(),b=r(l,!0);t(l),C(()=>f(b,a(s))),v(o,l)}),t(y),v(h,y)}),t(u),t(_);var d=i(_,2);{var x=h=>{var w=Tt(),y=r(w,!0);t(w),C(()=>f(y,e.card.sub)),v(h,w)};N(d,h=>{e.card.sub&&h(x)})}t(S),v(F,S),J()}var Dt=p('<div class="cell svelte-stf9c8"><div class="value svelte-stf9c8"> </div> <div class="label svelte-stf9c8"> </div></div>'),Ot=p('<div class="body-sub svelte-stf9c8"> </div>'),zt=p('<div class="body body-scalars svelte-stf9c8"><div class="row svelte-stf9c8"></div> <!></div>');function Ht(F,e){X(e,!0);var S=zt(),_=r(S);te(_,21,()=>e.card.scalars??[],oe,(u,d)=>{var x=Dt(),h=r(x),w=r(h,!0);t(h);var y=i(h,2),o=r(y,!0);t(y),t(x),C(()=>{le(h,`color: var(--tier-${e.card.tier??""});`),f(w,a(d).value),f(o,a(d).label)}),v(u,x)}),t(_);var M=i(_,2);{var n=u=>{var d=Ot(),x=r(d,!0);t(d),C(()=>f(x,e.card.sub)),v(u,d)};N(M,u=>{e.card.sub&&u(n)})}t(S),v(F,S),J()}var Yt=p('<div class="headline svelte-jrppts"> </div>'),jt=p('<div class="subhead svelte-jrppts"> </div>'),Wt=_e("<rect></rect>"),Ut=p('<div class="body-sub svelte-jrppts"> </div>'),Kt=p('<div class="body-sub svelte-jrppts"> </div>'),Gt=p('<div class="body body-spark svelte-jrppts"><!> <!> <svg width="100%" preserveAspectRatio="none" aria-hidden="true" class="svelte-jrppts"></svg> <!> <!></div>');function Qt(F,e){X(e,!0);const S=240,_=38;let M=q(()=>e.card.spark??e.card.histogram??[]),n=q(()=>Math.max(...a(M),1)),u=q(()=>a(M).length),d=q(()=>Math.max(2,S/Math.max(a(u),1)-1.5));var x=Gt(),h=r(x);{var w=c=>{var k=Yt(),R=r(k,!0);t(k),C(()=>{le(k,`color: var(--tier-${e.card.tier??""});`),f(R,e.card.headline)}),v(c,k)};N(h,c=>{e.card.headline&&c(w)})}var y=i(h,2);{var o=c=>{var k=jt(),R=r(k,!0);t(k),C(()=>f(R,e.card.subhead)),v(c,k)};N(y,c=>{e.card.subhead&&c(o)})}var s=i(y,2);m(s,"viewBox","0 0 240 38"),m(s,"height",_),te(s,21,()=>a(M),oe,(c,k,R)=>{var P=Wt();C(()=>{m(P,"x",R/a(u)*S+.5),m(P,"y",_-a(k)/a(n)*_),m(P,"width",a(d)),m(P,"height",a(k)/a(n)*_),m(P,"fill",`var(--tier-${e.card.tier??""})`)}),v(c,P)}),t(s);var l=i(s,2);{var b=c=>{var k=Ut(),R=r(k,!0);t(k),C(()=>f(R,e.card.sparkSub)),v(c,k)};N(l,c=>{e.card.sparkSub&&c(b)})}var A=i(l,2);{var g=c=>{var k=Kt(),R=r(k,!0);t(k),C(()=>f(R,e.card.sub)),v(c,k)};N(A,c=>{!e.card.sparkSub&&e.card.sub&&c(g)})}t(x),v(F,x),J()}var Vt=p('<span class="headline svelte-48vbub"> </span>'),Zt=p('<span class="subhead svelte-48vbub"> </span>'),Xt=p('<span class="spatial-note svelte-48vbub"> </span>'),Jt=p("<span> </span>"),$t=p('<div class="body-sub svelte-48vbub"><!> <!></div>'),ea=p('<div class="body body-timeseries svelte-48vbub"><div class="ts-header svelte-48vbub"><!> <!></div> <svg width="100%" aria-hidden="true" class="svelte-48vbub"><line stroke="#C9C9C5" stroke-width="0.5" stroke-dasharray="2 2"></line><path fill="none" stroke-width="1.4"></path><circle r="3"></circle><text font-size="9" font-family="IBM Plex Mono" text-anchor="middle"> </text><text font-size="8" font-family="IBM Plex Mono" fill="#6B6B6B">now</text><text font-size="8" font-family="IBM Plex Mono" text-anchor="end" fill="#6B6B6B"> </text></svg> <!></div>');function Ee(F,e){X(e,!0);const S=240,_=84,M=6,n=q(()=>e.card.timeseries??{hours:96,peak:{x:38,y:47},peakLabel:""}),u=q(()=>h(a(n))),d=q(()=>w(a(u),a(n))),x=q(()=>y(a(u),a(d)));function h(E){const B=[];for(let L=0;L<=E.hours;L++){const Y=6*Math.sin(L/12.42*Math.PI*2),G=38*Math.exp(-Math.pow((L-E.peak.x)/12,2));B.push({x:L,y:Y+G+4})}return B}function w(E,B){const L=Math.max(...E.map(G=>G.y),B.peak.y),Y=Math.min(...E.map(G=>G.y),-10);return{sx:G=>M+G/B.hours*(S-M*2),sy:G=>_-M-14-(G-Y)/(L-Y)*(_-M*2-14)}}function y(E,B){return E.map((L,Y)=>`${Y?"L":"M"} ${B.sx(L.x)} ${B.sy(L.y)}`).join(" ")}var o=ea(),s=r(o),l=r(s);{var b=E=>{var B=Vt(),L=r(B,!0);t(B),C(()=>{le(B,`color: var(--tier-${e.card.tier??""});`),f(L,e.card.headline)}),v(E,B)};N(l,E=>{e.card.headline&&E(b)})}var A=i(l,2);{var g=E=>{var B=Zt(),L=r(B,!0);t(B),C(()=>f(L,e.card.subhead)),v(E,B)};N(A,E=>{e.card.subhead&&E(g)})}t(s);var c=i(s,2);m(c,"viewBox","0 0 240 84"),m(c,"height",_);var k=r(c);m(k,"x1",M),m(k,"x2",S-M);var R=i(k),P=i(R),D=i(P),j=r(D,!0);t(D);var I=i(D);m(I,"x",M),m(I,"y",_-2);var O=i(I);m(O,"x",S-M),m(O,"y",_-2);var z=r(O);t(O),t(c);var U=i(c,2);{var T=E=>{var B=$t(),L=r(B);{var Y=Q=>{var Z=Xt(),ne=r(Z,!0);t(Z),C(()=>f(ne,e.card.spatialNote)),v(Q,Z)};N(L,Q=>{e.card.spatialNote&&Q(Y)})}var G=i(L,2);{var ae=Q=>{var Z=Jt(),ne=r(Z,!0);t(Z),C(()=>f(ne,e.card.sub)),v(Q,Z)};N(G,Q=>{e.card.sub&&Q(ae)})}t(B),v(E,B)};N(U,E=>{(e.card.spatialNote||e.card.sub)&&E(T)})}t(o),C((E,B,L,Y,G,ae)=>{m(k,"y1",E),m(k,"y2",B),m(R,"d",a(x)),m(R,"stroke",`var(--tier-${e.card.tier??""})`),m(P,"cx",L),m(P,"cy",Y),m(P,"fill",`var(--tier-${e.card.tier??""})`),m(D,"x",G),m(D,"y",ae),m(D,"fill",`var(--tier-${e.card.tier??""})`),f(j,a(n).peakLabel),f(z,`+${a(n).hours??""}h`)},[()=>a(d).sy(0),()=>a(d).sy(0),()=>a(d).sx(a(n).peak.x),()=>a(d).sy(a(n).peak.y),()=>a(d).sx(a(n).peak.x),()=>a(d).sy(a(n).peak.y)-6]),v(F,o),J()}var ta=p('<span class="ft-stat svelte-1y25lfh"><span class="ft-stat-k svelte-1y25lfh">RMSE</span> </span>'),aa=p('<span class="ft-stat ft-skill svelte-1y25lfh"> </span>'),ra=p('<span class="ft-badge svelte-1y25lfh" title="Trained on this hardware"> </span>'),sa=p('<a class="ft-link svelte-1y25lfh" target="_blank" rel="noopener noreferrer">Model card ↗</a>'),ia=p('<!> <div class="ft-footer svelte-1y25lfh"><!> <!> <!> <!></div>',1);function na(F,e){X(e,!0);var S=ia(),_=ee(S);Ee(_,{get card(){return e.card}});var M=i(_,2),n=r(M);{var u=s=>{var l=ta(),b=i(r(l));t(l),C(()=>f(b,` ${e.card.rmse??""}`)),v(s,l)};N(n,s=>{e.card.rmse&&s(u)})}var d=i(n,2);{var x=s=>{var l=aa(),b=r(l,!0);t(l),C(()=>f(b,e.card.skillVsPersistence)),v(s,l)};N(d,s=>{e.card.skillVsPersistence&&s(x)})}var h=i(d,2);{var w=s=>{var l=ra(),b=r(l,!0);t(l),C(()=>f(b,e.card.hardwareBadge)),v(s,l)};N(h,s=>{e.card.hardwareBadge&&s(w)})}var y=i(h,2);{var o=s=>{var l=sa();C(b=>m(l,"href",b),[()=>e.card.hfModelCard.startsWith("http")?e.card.hfModelCard:`https://${e.card.hfModelCard}`]),v(s,l)};N(y,s=>{e.card.hfModelCard&&s(o)})}t(M),v(F,S),J()}var la=_e('<circle r="2.2"></circle><text font-size="9" font-family="IBM Plex Mono" text-anchor="middle" fill="#6B6B6B"> </text>',1),oa=p('<div class="body-sub svelte-gqkhpe"> </div>'),da=p('<div class="body body-forecast svelte-gqkhpe"><svg width="100%" aria-hidden="true" class="svelte-gqkhpe"><path fill-opacity="0.18"></path><path fill="none" stroke-width="1.5"></path><!></svg> <!></div>');function va(F,e){X(e,!0);const S=240,_=88,M=6;let n=q(()=>e.card.forecast??[]),u=q(()=>a(n).map((c,k)=>M+k/Math.max(a(n).length-1,1)*(S-M*2))),d=q(()=>Math.max(...a(n).map(c=>c.high),1));function x(c){return _-M-c/a(d)*(_-M*2-12)}let h=q(()=>a(u).map((c,k)=>`${k?"L":"M"} ${c} ${x(a(n)[k].mid)}`).join(" ")),w=q(()=>{if(!a(n).length)return"";const c=a(u).map((R,P)=>`${R} ${x(a(n)[P].low)}`).join(" L "),k=[...a(u)].reverse().map((R,P)=>`${R} ${x(a(n)[a(n).length-1-P].high)}`).join(" L ");return`M ${c} L ${k} Z`});var y=da(),o=r(y);m(o,"viewBox","0 0 240 88"),m(o,"height",_);var s=r(o),l=i(s),b=i(l);te(b,17,()=>a(n),oe,(c,k,R)=>{var P=la(),D=ee(P),j=i(D);m(j,"y",_-1);var I=r(j,!0);t(j),C(O=>{m(D,"cx",a(u)[R]),m(D,"cy",O),m(D,"fill",`var(--tier-${e.card.tier??""})`),m(j,"x",a(u)[R]),f(I,a(k).year)},[()=>x(a(k).mid)]),v(c,P)}),t(o);var A=i(o,2);{var g=c=>{var k=oa(),R=r(k,!0);t(k),C(()=>f(R,e.card.sub)),v(c,k)};N(A,c=>{e.card.sub&&c(g)})}t(y),C(()=>{m(s,"d",a(w)),m(s,"fill",`var(--tier-${e.card.tier??""})`),m(l,"d",a(h)),m(l,"stroke",`var(--tier-${e.card.tier??""})`)}),v(F,y),J()}var ca=_e('<svg width="100%" aria-hidden="true" class="svelte-1lx1psy"><rect fill="#F2F2EE"></rect><g stroke="#D9D6CC" stroke-width="0.6"><line x1="0" y1="40" y2="40"></line><line x1="0" y1="80" y2="80"></line><line x1="60" y1="0" x2="60"></line><line x1="160" y1="0" x2="160"></line></g><path d="M20 50 Q 60 38 90 56 Q 120 76 150 64 Q 180 50 180 86 Q 130 100 70 96 Q 30 92 20 76 Z" fill="rgba(42,111,168,0.32)" stroke="#2A6FA8" stroke-width="0.7"></path><path d="M40 60 Q 80 54 110 70 Q 140 84 160 78 Q 165 90 130 92 Q 80 90 50 82 Z" fill="rgba(11,83,148,0.36)" stroke="#0B5394" stroke-width="0.6"></path><circle cx="120" cy="74" r="3.2" fill="#D17C00" stroke="#FAFAF7" stroke-width="1.3"></circle><text font-size="8" font-family="IBM Plex Mono" text-anchor="end" fill="#6B6B6B">2.13 in/hr · MOD</text></svg>'),ua=_e('<svg width="100%" aria-hidden="true" class="svelte-1lx1psy"><rect fill="#F2F2EE"></rect><g stroke="#D9D6CC" stroke-width="0.6"><line x1="0" y1="40" y2="40"></line><line x1="0" y1="80" y2="80"></line><line x1="60" y1="0" x2="60"></line><line x1="160" y1="0" x2="160"></line></g><path d="M180 92 Q 200 88 215 96 Q 220 105 200 104 Q 185 102 180 96 Z" fill="rgba(42,111,168,0.18)" stroke="#2A6FA8" stroke-width="0.5" stroke-dasharray="2 2"></path><circle cx="120" cy="60" r="3.2" fill="#D17C00" stroke="#FAFAF7" stroke-width="1.3"></circle><text font-size="8" font-family="IBM Plex Mono" text-anchor="end" fill="#6B6B6B">no ponding · MOD</text></svg>'),ga=_e('<svg width="100%" aria-hidden="true" class="svelte-1lx1psy"><defs><pattern id="rt-s2-rgb" x="0" y="0" width="6" height="6" patternUnits="userSpaceOnUse"><rect width="6" height="6" fill="#7A8E6A"></rect><rect x="0" y="0" width="3" height="3" fill="#8D9C7A"></rect><rect x="3" y="3" width="3" height="3" fill="#69795D"></rect></pattern></defs><rect fill="url(#rt-s2-rgb)"></rect><rect x="0" y="55" height="6" fill="#A8A496"></rect><rect x="115" y="0" width="8" fill="#A8A496"></rect><ellipse cx="50" cy="92" rx="6" ry="3" fill="#2A6FA8" fill-opacity="0.65"></ellipse><text x="6" y="14" font-size="9" font-family="IBM Plex Mono" fill="#FAFAF7">PRITHVI · 0.3%</text><text font-size="8" font-family="IBM Plex Mono" text-anchor="end" fill="#FAFAF7">scene 2026-05-02</text></svg>'),ha=_e('<svg width="100%" aria-hidden="true" class="svelte-1lx1psy"><rect fill="#F2F2EE"></rect><rect x="0" y="0" width="80" height="60" fill="#C66"></rect><rect x="80" y="0" width="60" height="60" fill="#C66"></rect><rect x="140" y="0" width="100" height="38" fill="#C66"></rect><rect x="140" y="38" width="100" height="22" fill="#5B7FB4"></rect><rect x="0" y="60" width="100" height="60" fill="#C66"></rect><rect x="100" y="60" width="50" height="40" fill="#5B8A4A"></rect><rect x="150" y="60" width="50" height="60" fill="#D9C75A"></rect><rect x="200" y="60" width="40" height="60" fill="#C66"></rect><rect x="100" y="100" width="50" height="20" fill="#A89A78"></rect><text x="6" y="14" font-size="9" font-family="IBM Plex Mono" fill="#FAFAF7">LULC · TerraMind</text><text font-size="8" font-family="IBM Plex Mono" text-anchor="end" fill="#FAFAF7">scene 2026-05-02</text></svg>'),_a=_e('<rect fill="rgba(42,111,168,0.55)" stroke="#2A6FA8" stroke-width="0.4"></rect>'),fa=_e('<svg width="100%" aria-hidden="true" class="svelte-1lx1psy"><rect fill="#3A3A38"></rect><!><text x="6" y="14" font-size="9" font-family="IBM Plex Mono" fill="#FAFAF7">BLDG · TerraMind</text><text font-size="8" font-family="IBM Plex Mono" text-anchor="end" fill="#FAFAF7">36.2% built</text></svg>'),ma=p('<div class="thumb-placeholder svelte-1lx1psy">raster preview</div>');function Pe(F,e){var M=xe(),n=ee(M);{var u=o=>{var s=ca();m(s,"viewBox","0 0 240 120"),m(s,"height",120);var l=r(s);m(l,"width",240),m(l,"height",120);var b=i(l),A=r(b);m(A,"x2",240);var g=i(A);m(g,"x2",240);var c=i(g);m(c,"y2",120);var k=i(c);m(k,"y2",120),t(b);var R=i(b,4);m(R,"x",234),m(R,"y",115),t(s),v(o,s)},d=o=>{var s=ua();m(s,"viewBox","0 0 240 120"),m(s,"height",120);var l=r(s);m(l,"width",240),m(l,"height",120);var b=i(l),A=r(b);m(A,"x2",240);var g=i(A);m(g,"x2",240);var c=i(g);m(c,"y2",120);var k=i(c);m(k,"y2",120),t(b);var R=i(b,3);m(R,"x",234),m(R,"y",115),t(s),v(o,s)},x=o=>{var s=ga();m(s,"viewBox","0 0 240 120"),m(s,"height",120);var l=i(r(s));m(l,"width",240),m(l,"height",120);var b=i(l);m(b,"width",240);var A=i(b);m(A,"height",120);var g=i(A,3);m(g,"x",234),m(g,"y",115),t(s),v(o,s)},h=o=>{var s=ha();m(s,"viewBox","0 0 240 120"),m(s,"height",120);var l=r(s);m(l,"width",240),m(l,"height",120);var b=i(l,11);m(b,"x",234),m(b,"y",115),t(s),v(o,s)},w=o=>{var s=fa();m(s,"viewBox","0 0 240 120"),m(s,"height",120);var l=r(s);m(l,"width",240),m(l,"height",120);var b=i(l);te(b,16,()=>[[10,10,28,18],[42,10,30,16],[78,10,40,22],[124,10,32,18],[162,10,30,18],[198,10,32,18],[10,32,28,16],[42,30,30,18],[124,32,32,16],[162,32,30,16],[198,32,32,16],[10,55,28,18],[42,55,30,18],[78,55,40,18],[124,55,32,18],[162,55,30,18],[198,55,32,18],[10,80,28,16],[42,80,30,16],[78,80,40,16],[124,80,32,16],[162,80,30,16],[10,100,28,12],[42,100,30,12],[78,100,40,12]],oe,(g,c)=>{var k=_a();C(()=>{m(k,"x",c[0]),m(k,"y",c[1]),m(k,"width",c[2]),m(k,"height",c[3])}),v(g,k)});var A=i(b,2);m(A,"x",234),m(A,"y",115),t(s),v(o,s)},y=o=>{var s=ma();v(o,s)};N(n,o=>{e.kind==="stormwater"?o(u):e.kind==="stormwater-dry"?o(d,1):e.kind==="prithvi"?o(x,2):e.kind==="lulc"?o(h,3):e.kind==="buildings"?o(w,4):o(y,-1)})}v(F,M)}var ya=p('<span class="illustrative svelte-1m43x1m" title="Illustrative rendering, not source pixels">illustrative</span>'),pa=p('<span class="svelte-1m43x1m"> </span>'),xa=p('<div class="raster-headline svelte-1m43x1m"><span class="svelte-1m43x1m"> </span> <!></div>'),ba=p('<div class="body-sub svelte-1m43x1m"> </div>'),wa=p('<div class="body body-raster svelte-1m43x1m"><div class="frame svelte-1m43x1m"><!> <!></div> <!> <!></div>');function ka(F,e){X(e,!0);var S=wa(),_=r(S),M=r(_);Pe(M,{get kind(){return e.card.rasterKind}});var n=i(M,2);{var u=y=>{var o=ya();v(y,o)};N(n,y=>{(e.card.illustrative||e.card.tier==="synthetic")&&y(u)})}t(_);var d=i(_,2);{var x=y=>{var o=xa(),s=r(o),l=r(s,!0);t(s);var b=i(s,2);{var A=g=>{var c=pa(),k=r(c);t(c),C(()=>f(k,`· ${e.card.subhead??""}`)),v(g,c)};N(b,g=>{e.card.subhead&&g(A)})}t(o),C(()=>{le(s,`color: var(--tier-${e.card.tier??""});`),f(l,e.card.headline)}),v(y,o)};N(d,y=>{e.card.headline&&y(x)})}var h=i(d,2);{var w=y=>{var o=ba(),s=r(o,!0);t(o),C(()=>f(s,e.card.sub)),v(y,o)};N(h,y=>{e.card.sub&&y(w)})}t(S),v(F,S),J()}var Sa=p('<span class="illustrative svelte-1td276x" title="Illustrative rendering, not source pixels">illustrative</span>'),Ma=p('<span class="bar-seg svelte-1td276x"></span>'),Ca=p('<li class="svelte-1td276x"><span class="swatch svelte-1td276x"></span> <span class="legend-k svelte-1td276x"> </span> <span class="legend-pct svelte-1td276x"> </span></li>'),Aa=p('<div class="bar svelte-1td276x" role="img" aria-label="LULC class mix"></div> <ul class="legend svelte-1td276x"></ul>',1),Na=p('<div class="body-sub svelte-1td276x"> </div>'),Ba=p('<div class="body body-lulc svelte-1td276x"><div class="frame svelte-1td276x"><!> <!></div> <!> <!></div>');function Ra(F,e){X(e,!0);let S=q(()=>(e.card.classMix??[]).reduce((o,s)=>o+(s.pct||0),0)||1);var _=Ba(),M=r(_),n=r(M);{let o=q(()=>e.card.rasterKind??"lulc");Pe(n,{get kind(){return a(o)}})}var u=i(n,2);{var d=o=>{var s=Sa();v(o,s)};N(u,o=>{(e.card.illustrative||e.card.tier==="synthetic")&&o(d)})}t(M);var x=i(M,2);{var h=o=>{var s=Aa(),l=ee(s);te(l,21,()=>e.card.classMix,A=>A.k,(A,g)=>{var c=Ma();let k;C(()=>{m(c,"title",`${a(g).k??""}: ${a(g).pct??""}%`),k=le(c,"",k,{"flex-grow":a(g).pct/a(S),background:a(g).color})}),v(A,c)}),t(l);var b=i(l,2);te(b,21,()=>e.card.classMix,A=>A.k,(A,g)=>{var c=Ca(),k=r(c);let R;var P=i(k,2),D=r(P,!0);t(P);var j=i(P,2),I=r(j);t(j),t(c),C(()=>{R=le(k,"",R,{background:a(g).color}),f(D,a(g).k),f(I,`${a(g).pct??""}%`)}),v(A,c)}),t(b),v(o,s)};N(x,o=>{var s;(s=e.card.classMix)!=null&&s.length&&o(h)})}var w=i(x,2);{var y=o=>{var s=Na(),l=r(s,!0);t(s),C(()=>f(l,e.card.sub)),v(o,s)};N(w,o=>{e.card.sub&&o(y)})}t(_),v(F,_),J()}var Fa=p('<span class="reg-label svelte-1iup6im"> </span> <span class="reg-source svelte-1iup6im"> </span>',1),La=p('<span class="reg-silent svelte-1iup6im"> </span>'),Ea=p('<li><span class="reg-tag svelte-1iup6im"><!> <span> </span></span> <!></li>'),Pa=p('<div class="body-sub svelte-1iup6im"> </div>'),Ta=p('<div class="body body-register svelte-1iup6im"><ul class="reg-list svelte-1iup6im"></ul> <!></div>');function qa(F,e){X(e,!0);var S=Ta(),_=r(S);te(_,21,()=>e.card.registers??[],oe,(u,d)=>{var x=Ea();let h;var w=r(x),y=r(w);ge(y,{get tier(){return a(d).tier},size:9,get color(){return`var(--tier-${a(d).tier??""})`}});var o=i(y,2),s=r(o,!0);t(o),t(w);var l=i(w,2);{var b=g=>{var c=Fa(),k=ee(c),R=r(k,!0);t(k);var P=i(k,2),D=r(P,!0);t(P),C(()=>{m(k,"title",a(d).detail?`${a(d).label} — ${a(d).detail}`:a(d).label),f(R,a(d).label),f(D,a(d).sourceId??"")}),v(g,c)},A=g=>{var c=La(),k=r(c,!0);t(c),C(()=>f(k,a(d).note)),v(g,c)};N(l,g=>{a(d).label?g(b):g(A,-1)})}t(x),C(()=>{h=ue(x,1,"reg-row svelte-1iup6im",null,h,{silent:!a(d).label}),m(w,"title",a(d).tier),f(s,a(d).reg)}),v(u,x)}),t(_);var M=i(_,2);{var n=u=>{var d=Pa(),x=r(d,!0);t(d),C(()=>f(x,e.card.sub)),v(u,d)};N(M,u=>{e.card.sub&&u(n)})}t(S),v(F,S),J()}var Ia=p('<div class="cell-aux svelte-1swqabu"> </div>'),Da=p('<div class="cell svelte-1swqabu"><div class="cell-tier svelte-1swqabu"><!> <span class="cell-label"> </span></div> <div class="cell-value svelte-1swqabu"> </div> <!></div>'),Oa=p('<div class="cell-aux svelte-1swqabu"> </div>'),za=p('<div class="cell svelte-1swqabu"><div class="cell-tier svelte-1swqabu"><!> <span class="cell-label"> </span></div> <div class="cell-value svelte-1swqabu"> </div> <!></div>'),Ha=p('<div class="cmp-delta svelte-1swqabu"> </div>'),Ya=p('<div class="body-sub svelte-1swqabu"> </div>'),ja=p('<div class="body body-comparison svelte-1swqabu"><div class="cmp-grid svelte-1swqabu"><!> <div class="divider svelte-1swqabu" aria-hidden="true">vs</div> <!></div> <!> <!></div>');function Wa(F,e){X(e,!0);var S=ja(),_=r(S),M=r(_);{var n=o=>{var s=Da(),l=r(s),b=r(l);ge(b,{get tier(){return e.card.left.tier},size:10,get color(){return`var(--tier-${e.card.left.tier??""})`}});var A=i(b,2),g=r(A,!0);t(A),t(l);var c=i(l,2),k=r(c,!0);t(c);var R=i(c,2);{var P=D=>{var j=Ia(),I=r(j,!0);t(j),C(()=>f(I,e.card.left.aux)),v(D,j)};N(R,D=>{e.card.left.aux&&D(P)})}t(s),C(()=>{f(g,e.card.left.label),le(c,`color: var(--tier-${e.card.left.tier??""});`),f(k,e.card.left.value)}),v(o,s)};N(M,o=>{e.card.left&&o(n)})}var u=i(M,4);{var d=o=>{var s=za(),l=r(s),b=r(l);ge(b,{get tier(){return e.card.right.tier},size:10,get color(){return`var(--tier-${e.card.right.tier??""})`}});var A=i(b,2),g=r(A,!0);t(A),t(l);var c=i(l,2),k=r(c,!0);t(c);var R=i(c,2);{var P=D=>{var j=Oa(),I=r(j,!0);t(j),C(()=>f(I,e.card.right.aux)),v(D,j)};N(R,D=>{e.card.right.aux&&D(P)})}t(s),C(()=>{f(g,e.card.right.label),le(c,`color: var(--tier-${e.card.right.tier??""});`),f(k,e.card.right.value)}),v(o,s)};N(u,o=>{e.card.right&&o(d)})}t(_);var x=i(_,2);{var h=o=>{var s=Ha(),l=r(s,!0);t(s),C(()=>f(l,e.card.delta)),v(o,s)};N(x,o=>{e.card.delta&&o(h)})}var w=i(x,2);{var y=o=>{var s=Ya(),l=r(s,!0);t(s),C(()=>f(l,e.card.sub)),v(o,s)};N(w,o=>{e.card.sub&&o(y)})}t(S),v(F,S),J()}var Ua=p('<div class="meta-row svelte-e40scu"><dt class="svelte-e40scu"> </dt> <dd class="svelte-e40scu"> </dd></div>'),Ka=p('<div class="body-sub svelte-e40scu"> </div>'),Ga=p('<div class="body body-meta svelte-e40scu"><dl class="meta-list svelte-e40scu"></dl> <!></div>');function Qa(F,e){X(e,!0);var S=Ga(),_=r(S);te(_,21,()=>e.card.metaRows??[],oe,(u,d)=>{var x=Ua(),h=r(x),w=r(h,!0);t(h);var y=i(h,2),o=r(y,!0);t(y),t(x),C(()=>{f(w,a(d).k),f(o,a(d).v)}),v(u,x)}),t(_);var M=i(_,2);{var n=u=>{var d=Ka(),x=r(d,!0);t(d),C(()=>f(x,e.card.sub)),v(u,d)};N(M,u=>{e.card.sub&&u(n)})}t(S),v(F,S),J()}var Va=p('<div class="unknown svelte-1x6xqhh"> </div>');function Za(F,e){X(e,!0);var S=xe(),_=ee(S);{var M=g=>{Ft(g,{get card(){return e.card}})},n=g=>{It(g,{get card(){return e.card}})},u=g=>{Ht(g,{get card(){return e.card}})},d=g=>{Qt(g,{get card(){return e.card}})},x=g=>{Ee(g,{get card(){return e.card}})},h=g=>{na(g,{get card(){return e.card}})},w=g=>{va(g,{get card(){return e.card}})},y=g=>{ka(g,{get card(){return e.card}})},o=g=>{Ra(g,{get card(){return e.card}})},s=g=>{qa(g,{get card(){return e.card}})},l=g=>{Wa(g,{get card(){return e.card}})},b=g=>{Qa(g,{get card(){return e.card}})},A=g=>{var c=Va(),k=r(c);t(c),C(()=>f(k,`unknown variant: ${e.card.variant??""}`)),v(g,c)};N(_,g=>{e.card.variant==="headline"?g(M):e.card.variant==="tabular"?g(n,1):e.card.variant==="scalars"?g(u,2):e.card.variant==="spark"||e.card.variant==="histogram"?g(d,3):e.card.variant==="timeseries"?g(x,4):e.card.variant==="timeseries-ft"?g(h,5):e.card.variant==="forecast"?g(w,6):e.card.variant==="raster"||e.card.variant==="raster-pred"?g(y,7):e.card.variant==="lulc"?g(o,8):e.card.variant==="register"?g(s,9):e.card.variant==="comparison"?g(l,10):e.card.variant==="meta"?g(b,11):g(A,-1)})}v(F,S),J()}var Xa=p('<button type="button" class="fc-foot-cite svelte-1nuvnzu"><span class="fc-foot-docid svelte-1nuvnzu"> </span> <span class="fc-foot-arrow svelte-1nuvnzu" aria-hidden="true">→</span></button>'),Ja=p('<span class="fc-foot-docid fc-foot-docid-mute svelte-1nuvnzu"> </span>'),$a=p('<header class="fc-head svelte-1nuvnzu"><div class="fc-head-source svelte-1nuvnzu"><!> <span class="fc-head-source-label svelte-1nuvnzu"> </span></div> <span class="fc-head-vintage svelte-1nuvnzu"> </span></header> <h4 class="fc-title svelte-1nuvnzu"> </h4> <!> <footer class="fc-foot svelte-1nuvnzu"><!> <span><!> <span> </span></span></footer>',1);function Te(F,e){X(e,!0);let S=ie(e,"density",3,"comfortable"),_=ie(e,"linkedKey",3,null),M=q(()=>_()!=null&&e.card.mapLayer!=null&&e.card.mapLayer===_()),n=q(()=>Fe[e.card.tier].short),u=q(()=>e.card.mapLayer!=null);function d(){var s;e.card.mapLayer&&((s=e.onLink)==null||s.call(e,e.card.mapLayer))}function x(){var s;e.card.mapLayer&&((s=e.onLink)==null||s.call(e,null))}function h(s){var l;s.stopPropagation(),e.card.citeId&&((l=e.onCite)==null||l.call(e,e.card.citeId))}function w(s){var l;a(u)&&(s.key==="Enter"||s.key===" ")&&(s.preventDefault(),(l=e.onLink)==null||l.call(e,e.card.mapLayer??null))}var y=xe(),o=ee(y);rt(o,()=>a(u)?"button":"article",!1,(s,l)=>{Xe(s,()=>({type:a(u)?"button":void 0,role:a(u)?"button":"article",class:`fc fc-${e.card.variant??""} fc-tier-${e.card.tier??""}`,"aria-labelledby":`fc-${e.card.id}-title`,"aria-label":`${Fe[e.card.tier].label} card · ${e.card.title} · ${e.card.source}`,onpointerenter:d,onpointerleave:x,onfocus:d,onblur:x,onkeydown:w,[Je]:{"is-compact":S()==="compact","is-linked":a(M),"is-interactive":a(u),"has-illustrative":e.card.illustrative||e.card.tier==="synthetic"||e.card.variant==="comparison"}}),void 0,void 0,void 0,"svelte-1nuvnzu");var b=$a(),A=ee(b),g=r(A),c=r(g);ge(c,{get tier(){return e.card.tier},size:11,get color(){return`var(--tier-${e.card.tier??""})`}});var k=i(c,2),R=r(k,!0);t(k),t(g);var P=i(g,2),D=r(P);t(P),t(A);var j=i(A,2),I=r(j,!0);t(j);var O=i(j,2);Za(O,{get card(){return e.card}});var z=i(O,2),U=r(z);{var T=ae=>{var Q=Xa(),Z=r(Q),ne=r(Z,!0);t(Z),re(2),t(Q),C(()=>{m(Q,"title",`Open ${e.card.docId} in citation drawer`),f(ne,e.card.docId)}),Ae("click",Q,h),v(ae,Q)},E=ae=>{var Q=Ja(),Z=r(Q,!0);t(Q),C(()=>f(Z,e.card.docId)),v(ae,Q)};N(U,ae=>{e.card.citeId?ae(T):ae(E,-1)})}var B=i(U,2),L=r(B);ge(L,{get tier(){return e.card.tier},size:9,get color(){return`var(--tier-${e.card.tier??""})`}});var Y=i(L,2),G=r(Y,!0);t(Y),t(B),t(z),C(()=>{m(k,"title",e.card.agency),f(R,e.card.source),f(D,`v. ${e.card.vintage??""}`),m(j,"id",`fc-${e.card.id}-title`),f(I,e.card.title),ue(B,1,`fc-tier-badge fc-tier-badge-${e.card.tier??""}`,"svelte-1nuvnzu"),m(B,"aria-label",`epistemic tier ${a(n)}`),f(G,a(n))}),v(l,b)}),v(F,y),J()}Ce(["click"]);var er=p('<span class="sep svelte-1qqbvs2">·</span> <span class="silent svelte-1qqbvs2"><strong class="svelte-1qqbvs2"> </strong> silent</span>',1),tr=p('<span class="sep svelte-1qqbvs2">·</span> <span class="warn svelte-1qqbvs2"><strong class="svelte-1qqbvs2"> </strong> warn</span>',1),ar=p('<span class="sep svelte-1qqbvs2">·</span> <span class="err svelte-1qqbvs2"><strong class="svelte-1qqbvs2"> </strong> errored</span>',1),rr=p('<span class="sep svelte-1qqbvs2">·</span> <span class="notinvoked svelte-1qqbvs2"><strong class="svelte-1qqbvs2"> </strong> not invoked</span>',1),sr=p('<span class="tally svelte-1qqbvs2"><span class="cards"> </span> <span class="sep svelte-1qqbvs2">·</span> <span class="fired"><strong class="svelte-1qqbvs2"> </strong> fired</span> <!> <!> <!> <!> <span class="sep svelte-1qqbvs2">·</span> <span class="ms"><strong class="svelte-1qqbvs2"> </strong></span></span>');function ir(F,e){X(e,!0);function S(T){return T.flatMap(E=>E.children?[E,...S(E.children)]:[E])}let _=q(()=>S(e.members)),M=q(()=>a(_).filter(T=>T.status==="fired"||T.status==="warned").length),n=q(()=>a(_).filter(T=>T.status==="silent_by_design").length),u=q(()=>a(_).filter(T=>T.status==="warned").length),d=q(()=>a(_).filter(T=>T.status==="errored").length),x=q(()=>a(_).filter(T=>T.status==="not_invoked").length),h=q(()=>e.members.reduce((T,E)=>Math.max(T,E.ms??0),0));function w(T){return T===0?"—":T<1e3?`${T}ms`:`${(T/1e3).toFixed(1)}s`}var y=sr(),o=r(y),s=r(o);t(o);var l=i(o,4),b=r(l),A=r(b,!0);t(b),re(),t(l);var g=i(l,2);{var c=T=>{var E=er(),B=i(ee(E),2),L=r(B),Y=r(L,!0);t(L),re(),t(B),C(()=>f(Y,a(n))),v(T,E)};N(g,T=>{a(n)>0&&T(c)})}var k=i(g,2);{var R=T=>{var E=tr(),B=i(ee(E),2),L=r(B),Y=r(L,!0);t(L),re(),t(B),C(()=>f(Y,a(u))),v(T,E)};N(k,T=>{a(u)>0&&T(R)})}var P=i(k,2);{var D=T=>{var E=ar(),B=i(ee(E),2),L=r(B),Y=r(L,!0);t(L),re(),t(B),C(()=>f(Y,a(d))),v(T,E)};N(P,T=>{a(d)>0&&T(D)})}var j=i(P,2);{var I=T=>{var E=rr(),B=i(ee(E),2),L=r(B),Y=r(L,!0);t(L),re(),t(B),C(()=>f(Y,a(x))),v(T,E)};N(j,T=>{a(x)>0&&T(I)})}var O=i(j,4),z=r(O),U=r(z,!0);t(z),t(O),t(y),C(T=>{f(s,`${e.cardCount??""} card${e.cardCount===1?"":"s"}`),f(A,a(M)),f(U,T)},[()=>w(a(h))]),v(F,y),J()}var nr=p('<span class="prov-tier svelte-qakefz"><!></span>'),lr=p('<span class="prov-note svelte-qakefz"> </span>'),or=p('<span class="prov-ms svelte-qakefz"> </span>'),dr=p('<li class="prov-children svelte-qakefz"><!></li>'),vr=p('<li><span class="prov-pip svelte-qakefz" aria-hidden="true"> </span> <span class="prov-id svelte-qakefz"> </span> <!> <span class="prov-name svelte-qakefz"> </span> <!> <!></li> <!>',1),cr=p('<ul class="prov-tree svelte-qakefz"></ul>');function qe(F,e){X(e,!0);let S=ie(e,"depth",3,0);function _(u){return{fired:"●",silent_by_design:"○",warned:"▲",errored:"■",not_invoked:"□"}[u]}function M(u){return u.status==="warned"?"#B7791F":u.status==="errored"?"#B91C1C":u.status==="silent_by_design"||u.status==="not_invoked"?"var(--ink-tertiary)":u.tier?`var(--tier-${u.tier})`:"var(--ink)"}var n=cr();te(n,21,()=>e.members,u=>u.id,(u,d)=>{var x=vr(),h=ee(x),w=r(h),y=r(w,!0);t(w);var o=i(w,2),s=r(o,!0);t(o);var l=i(o,2);{var b=I=>{var O=nr(),z=r(O);{let U=q(()=>`var(--tier-${a(d).tier})`);ge(z,{get tier(){return a(d).tier},size:9,get color(){return a(U)}})}t(O),v(I,O)};N(l,I=>{a(d).tier&&I(b)})}var A=i(l,2),g=r(A,!0);t(A);var c=i(A,2);{var k=I=>{var O=lr(),z=r(O);t(O),C(()=>f(z,`— ${a(d).note??""}`)),v(I,O)};N(c,I=>{a(d).note&&I(k)})}var R=i(c,2);{var P=I=>{var O=or(),z=r(O,!0);t(O),C(U=>f(z,U),[()=>a(d).ms<1e3?`${a(d).ms}ms`:`${(a(d).ms/1e3).toFixed(1)}s`]),v(I,O)};N(R,I=>{a(d).ms!=null&&I(P)})}t(h);var D=i(h,2);{var j=I=>{var O=dr(),z=r(O);{let U=q(()=>S()+1);qe(z,{get members(){return a(d).children},get depth(){return a(U)}})}t(O),v(I,O)};N(D,I=>{var O;(O=a(d).children)!=null&&O.length&&I(j)})}C((I,O)=>{ue(h,1,`prov-row prov-status-${a(d).status??""}`,"svelte-qakefz"),le(w,`color: ${I??""};`),f(y,O),f(s,a(d).id),f(g,a(d).name)},[()=>M(a(d)),()=>_(a(d).status)]),v(u,x)}),t(n),C(()=>le(n,`--depth: ${S()??""};`)),v(F,n),J()}var ur=p('<div class="silent svelte-16iv0n8"><span class="silent-tag svelte-16iv0n8">silent</span> <p class="silent-prose svelte-16iv0n8"><!></p></div>'),gr=p("<div></div>"),hr=p('<div class="prov-body svelte-16iv0n8"><!></div>'),_r=p('<section><header class="region-head svelte-16iv0n8"><div class="region-head-left svelte-16iv0n8"><span class="region-num svelte-16iv0n8"> </span> <h3 class="region-name svelte-16iv0n8"> </h3> <span class="region-role svelte-16iv0n8"> </span> <span class="region-tag svelte-16iv0n8"> </span></div> <!></header> <!> <div class="prov svelte-16iv0n8"><button type="button" class="prov-toggle svelte-16iv0n8"><span class="prov-caret svelte-16iv0n8" aria-hidden="true"> </span> <span class="prov-label"> </span> <span class="prov-meta svelte-16iv0n8"> </span></button> <!></div></section>');function fr(F,e){X(e,!0);let S=ie(e,"density",3,"comfortable"),_=ie(e,"provenanceMode",3,"smart"),M=ie(e,"linkedKey",3,null),n=q(()=>Me[e.stone]),u=q(()=>`${Ne.indexOf(e.stone)+1}`.padStart(2,"0")),d=q(()=>e.stone==="capstone");function x(K){return K.flatMap(W=>W.children?[W,...x(W.children)]:[W])}let h=q(()=>x(e.trace.members)),w=q(()=>a(h).length),y=q(()=>a(h).some(K=>K.status==="warned"||K.status==="errored"||K.status==="not_invoked")),o=q(()=>_()==="all-expanded"?!0:_()==="all-collapsed"?!1:a(y)),s=Se(null),l=q(()=>a(s)??a(o));ce(()=>{_(),pe(s,null)});var b=_r(),A=r(b),g=r(A),c=r(g),k=r(c,!0);t(c);var R=i(c,2),P=r(R,!0);t(R);var D=i(R,2),j=r(D);t(D);var I=i(D,2),O=r(I,!0);t(I),t(g);var z=i(g,2);ir(z,{get cardCount(){return e.cards.length},get members(){return e.trace.members}}),t(A);var U=i(A,2);{var T=K=>{var W=ur(),$=i(r(W),2),de=r($);{var fe=he=>{var se=ye("No projection cards landed for this query. Atomic functions still ran (see provenance) and returned silence rather than confabulation.");v(he,se)},be=he=>{var se=ye("No cards for this Stone on this query.");v(he,se)};N(de,he=>{e.stone==="lodestone"?he(fe):he(be,-1)})}t($),t(W),v(K,W)},E=K=>{var W=gr();let $;te(W,21,()=>e.cards,de=>de.id,(de,fe)=>{Te(de,{get card(){return a(fe)},get density(){return S()},get linkedKey(){return M()},get onCite(){return e.onCite},get onLink(){return e.onLink}})}),t(W),C(()=>$=ue(W,1,"rail svelte-16iv0n8",null,$,{"rail-capstone":a(d)})),v(K,W)};N(U,K=>{e.cards.length===0?K(T):K(E,-1)})}var B=i(U,2),L=r(B),Y=r(L),G=r(Y,!0);t(Y);var ae=i(Y,2),Q=r(ae);t(ae);var Z=i(ae,2),ne=r(Z);t(Z),t(L);var H=i(L,2);{var V=K=>{var W=hr(),$=r(W);qe($,{get members(){return e.trace.members}}),t(W),C(()=>m(W,"id",`prov-body-${e.stone}`)),v(K,W)};N(H,K=>{a(l)&&K(V)})}t(B),t(b),C(()=>{ue(b,1,`region region-${e.stone??""}`,"svelte-16iv0n8"),m(b,"aria-labelledby",`region-h-${e.stone}`),m(b,"data-stone",e.stone),f(k,a(u)),m(R,"id",`region-h-${e.stone}`),f(P,a(n).name),f(j,`· ${a(n).role??""}`),f(O,a(n).tag),m(L,"aria-expanded",a(l)),m(L,"aria-controls",`prov-body-${e.stone}`),f(G,a(l)?"▾":"▸"),f(Q,`${a(l)?"Hide":"Show"} provenance`),f(ne,`· ${a(w)??""} function${a(w)===1?"":"s"}`)}),Ae("click",L,()=>pe(s,!a(l))),v(F,b),J()}Ce(["click"]);var mr=p('<section class="region region-grammar svelte-gwg123" aria-label="Card grammar reference"><header class="region-head svelte-gwg123"><div class="region-head-left svelte-gwg123"><span class="region-num svelte-gwg123">SPEC</span> <h3 class="region-name svelte-gwg123">Card grammar</h3> <span class="region-role svelte-gwg123">· every body variant in the system</span> <span class="region-tag svelte-gwg123">stubs, not findings</span></div> <span class="grammar-count svelte-gwg123"> </span></header> <div class="rail svelte-gwg123"></div></section>');function yr(F,e){let S=ie(e,"density",3,"comfortable");const _=[{id:"grm-headline",stone:"cornerstone",tier:"modeled",variant:"headline",source:"FEMA",agency:"spec",vintage:"spec",title:"Single big number, scenario-tagged",headline:"Zone AE",subhead:"preliminary FIRM, panel ID",sub:"Use when the answer is one categorical state.",docId:"DS-HEADLINE"},{id:"grm-tabular",stone:"cornerstone",tier:"empirical",variant:"tabular",source:"USGS",agency:"spec",vintage:"spec",title:"Small table of observations",columns:["id","value","dist."],rows:[["ROW-001","1.2 m","0.18 mi"],["ROW-002","0.9 m","0.32 mi"],["ROW-003","0.7 m","0.41 mi"]],sub:"Use when 3–8 records each carry the same fields.",docId:"DS-TABULAR"},{id:"grm-scalars",stone:"touchstone",tier:"empirical",variant:"scalars",source:"NWS",agency:"spec",vintage:"spec",title:"Trio of scalar readings",scalars:[{value:"0.02 in",label:"precip · 24h"},{value:"11 mph",label:"wind"},{value:"63°F",label:"temp"}],sub:"Use for current-state dashboards.",docId:"DS-SCALARS"},{id:"grm-spark",stone:"touchstone",tier:"empirical",variant:"spark",source:"FloodNet",agency:"spec",vintage:"spec",title:"Sparkline of recent events",headline:"n events",subhead:"window · peak",spark:[1,2,4,3,7,12,8,5,3,2,4,9,6],docId:"DS-SPARK"},{id:"grm-histogram",stone:"touchstone",tier:"proxy",variant:"histogram",source:"NYC 311",agency:"spec",vintage:"spec",title:"Histogram of binned counts",headline:"n calls",subhead:"window · seasonal note",histogram:[3,2,1,0,1,4,7,12,18,11,5,3,4,2,1,0,2,3,8,9,4,2,1,0],docId:"DS-HIST"},{id:"grm-timeseries",stone:"lodestone",tier:"modeled",variant:"timeseries",source:"Granite TTM",agency:"spec",vintage:"spec",title:"Forecast curve with horizon",headline:"+0.41 m peak",subhead:"+38h · 90% CI",timeseries:{hours:96,peak:{x:38,y:41},peakLabel:"+0.41 m"},spatialNote:"regional",sub:"Spatial-index callout when station ≠ point-of-query.",docId:"DS-TS"},{id:"grm-forecast",stone:"lodestone",tier:"modeled",variant:"forecast",source:"NPCC4",agency:"spec",vintage:"spec",title:"Long-horizon scenario projections",forecast:[{year:2030,low:4,mid:6,high:9},{year:2050,low:13,mid:22,high:30},{year:2100,low:38,mid:71,high:114}],sub:"Use for decadal+ uncertainty cones.",docId:"DS-FCST"},{id:"grm-raster",stone:"cornerstone",tier:"modeled",variant:"raster",source:"NYC DEP",agency:"spec",vintage:"spec",title:"Raster snapshot, mapped layer",rasterKind:"stormwater",headline:"ponding",subhead:"scenario · pixel summary",sub:"Use for any 2D model output.",docId:"DS-RASTER"},{id:"grm-rasterpred",stone:"touchstone",tier:"modeled",variant:"raster-pred",source:"Prithvi-NYC",agency:"spec",vintage:"spec",title:"Raster prediction, illustrative",rasterKind:"prithvi",headline:"n% flooded",subhead:"model · scene id",illustrative:!0,sub:"Same chrome as raster + illustrative tag.",docId:"DS-RASTERPRED"},{id:"grm-register",stone:"keystone",tier:"empirical",variant:"register",source:"NYC OpenData",agency:"spec",vintage:"spec",title:"Composite register list",registers:[{reg:"MTA",tier:"empirical",label:"Station entrance",detail:"0.18 mi · 5",sourceId:"MTA-X",note:null},{reg:"NYCHA",tier:"empirical",label:"Development",detail:"0.41 mi · 2,878 res.",sourceId:"NYCHA-Y",note:null},{reg:"DOH",tier:"empirical",label:null,detail:null,sourceId:null,note:"no acute-care hospital within 1.0 mi"}],sub:"Use when many specialists join into one Stone.",docId:"DS-REGISTER"},{id:"grm-comparison",stone:"keystone",tier:"synthetic",variant:"comparison",source:"EMP × SYN",agency:"spec",vintage:"spec",title:"Documented vs. interpreted",left:{tier:"empirical",label:"documented",value:"31.4%",aux:"n polygons"},right:{tier:"synthetic",label:"interpreted",value:"29.8%",aux:"n polygons"},delta:"Δ = −1.6 pp · agreement strong",sub:"Use to surface model–ground-truth deltas.",docId:"DS-CMP"},{id:"grm-meta",stone:"capstone",tier:"modeled",variant:"meta",source:"Mellea",agency:"spec",vintage:"spec",title:"Capstone reconciliation",metaRows:[{k:"claims",v:"12 / 12 grounded"},{k:"tier mix",v:"EMP 5 · MOD 4 · PRX 2 · SYN 1"},{k:"tier-1 freshness",v:"median 38 d"},{k:"warnings",v:"0"}],sub:"Use to expose the synthesis layer's audit.",docId:"DS-META"}];var M=mr(),n=r(M),u=i(r(n),2),d=r(u);t(u),t(n);var x=i(n,2);te(x,21,()=>_,h=>h.id,(h,w)=>{Te(h,{get card(){return a(w)},get density(){return S()}})}),t(x),t(M),C(()=>f(d,`${_.length??""} variants`)),v(F,M)}var pr=p('<section class="findings svelte-ci42t5" aria-label="Findings, grouped by Stone"><header class="findings-head svelte-ci42t5"><h2 class="findings-h2 svelte-ci42t5">Findings · grouped by Stone</h2> <span class="findings-tagline svelte-ci42t5">cards = what each Stone found · provenance collapses below</span></header> <!> <!> <!></section>');function Rr(F,e){X(e,!0);let S=ie(e,"density",3,"comfortable"),_=ie(e,"provenanceMode",3,"smart"),M=ie(e,"showGrammar",3,!1),n=ie(e,"linkedKey",3,null),u=q(()=>{const s={cornerstone:[],keystone:[],touchstone:[],lodestone:[],capstone:[]};for(const l of e.data.cards)s[l.stone].push(l);return s}),d=q(()=>{const s={cornerstone:{key:"cornerstone",members:[]},keystone:{key:"keystone",members:[]},touchstone:{key:"touchstone",members:[]},lodestone:{key:"lodestone",members:[]},capstone:{key:"capstone",members:[]}};for(const l of e.data.stones)s[l.key]=l;return s});var x=pr(),h=i(r(x),2);At(h,{get cards(){return e.data.cards},get stones(){return e.data.stones},get wallSeconds(){return e.data.wallSeconds},get cacheHit(){return e.data.cacheHit}});var w=i(h,2);te(w,16,()=>Ne,s=>s,(s,l)=>{fr(s,{get stone(){return l},get cards(){return a(u)[l]},get trace(){return a(d)[l]},get density(){return S()},get provenanceMode(){return _()},get linkedKey(){return n()},get onCite(){return e.onCite},get onLink(){return e.onLink}})});var y=i(w,2);{var o=s=>{yr(s,{get density(){return S()}})};N(y,s=>{M()&&s(o)})}t(x),v(F,x),J()}const xr={cornerstone:[{id:"CORN-001",name:"sandy_inundation.lookup",stepNames:["sandy_inundation","sandy_nta"],tier:"empirical",skipReason:"Sandy 2012 inundation: query outside NYC bounds"},{id:"CORN-002",name:"dep_stormwater.lookup",stepNames:["dep_stormwater","dep_extreme_2080_nta","dep_moderate_2050_nta","dep_moderate_current_nta"],tier:"modeled",skipReason:"NYC DEP stormwater scenarios: query outside NYC bounds"},{id:"CORN-003",name:"usgs_hwm.spatial_join",stepNames:["ida_hwm_2021"],tier:"empirical",skipReason:"USGS Ida HWMs: no marks within 800 m of address"},{id:"CORN-004",name:"prithvi_water.lookup",stepNames:["prithvi_eo_v2"],tier:"modeled",skipReason:"Prithvi-EO Ida polygons: no polygons within 500 m"},{id:"CORN-005",name:"microtopo.dem_hand_twi",stepNames:["microtopo_lidar","microtopo_nta"],tier:"proxy",skipReason:"USGS 3DEP DEM: query outside NYC raster coverage"}],keystone:[{id:"KEY-001",name:"mta_entrance_exposure",stepNames:["mta_entrance_exposure"],tier:"empirical",skipReason:"no entrances within radius"},{id:"KEY-002",name:"nycha.development_join",stepNames:["nycha_development_exposure"],tier:"empirical",skipReason:"no NYCHA developments within 1.0 mi"},{id:"KEY-003",name:"doe.school_join",stepNames:["doe_school_exposure"],tier:"empirical",skipReason:"no DOE schools within 1.0 mi"},{id:"KEY-004",name:"doh.facility_join",stepNames:["doh_hospital_exposure"],tier:"empirical",skipReason:"no acute-care hospitals within 1.0 mi"},{id:"KEY-005",name:"pluto.lot_lookup",stepNames:["pluto_lookup"],tier:"empirical",skipReason:"PLUTO join skipped: queried address not in NYC PLUTO dataset"},{id:"KEY-006",name:"terramind.buildings",stepNames:["terramind_buildings","terramind_synthesis"],tier:"modeled",skipReason:"TerraMind Buildings adapter: heavy specialist disabled (RIPRAP_HEAVY_SPECIALISTS=0)"}],touchstone:[{id:"TCH-001",name:"floodnet.history",stepNames:["floodnet"],tier:"empirical",skipReason:"FloodNet sensor: no deployments within 600 m"},{id:"TCH-002",name:"nyc311.flood_complaints",stepNames:["nyc311","nyc311_nta"],tier:"proxy",skipReason:"NYC 311: no flood-relevant complaints within 200 m"},{id:"TCH-003",name:"nws_obs.metar",stepNames:["nws_obs"],tier:"empirical",skipReason:"NWS hourly METAR: nearest ASOS reports no precipitation"},{id:"TCH-004",name:"noaa_coops.recent",stepNames:["noaa_tides"],tier:"empirical",skipReason:"NOAA tide gauge: nearest station >25 km from address"},{id:"TCH-005",name:"prithvi_nyc_pluvial",stepNames:["prithvi_eo_live"],tier:"modeled",skipReason:"Prithvi-NYC-Pluvial: live segmentation specialist disabled or no <30% cloud Sentinel-2 in last 120 d"},{id:"TCH-006",name:"terramind.lulc",stepNames:["terramind_lulc"],tier:"synthetic",skipReason:"TerraMind LULC adapter: heavy specialist disabled or eo_chip fetch silent"}],lodestone:[{id:"LOD-001",name:"nws_alerts.flood_relevant",stepNames:["nws_alerts"],tier:"modeled",skipReason:"NWS public alerts: no active flood-relevant alerts at this address"},{id:"LOD-002",name:"ttm_battery_surge.zero_shot",stepNames:["ttm_forecast"],tier:"modeled",skipReason:"Granite TTM r2 zero-shot: forecast not interesting (peak |residual| < 0.3 ft)"},{id:"LOD-003",name:"ttm_battery_surge.fine_tune",stepNames:["ttm_battery_surge"],tier:"modeled",skipReason:"Granite TTM Battery fine-tune: forecast not interesting (peak |residual| < 0.3 m)"},{id:"LOD-004",name:"ttm_311_forecast",stepNames:["ttm_311_forecast"],tier:"modeled",skipReason:"NYC 311 weekly forecast: no per-address history to extrapolate"},{id:"LOD-005",name:"floodnet_forecast",stepNames:["floodnet_forecast"],tier:"modeled",skipReason:"FloodNet sensor recurrence: sensor has < silent-floor historical events; forecast omitted"},{id:"LOD-006",name:"npcc4.slr_projection",stepNames:["npcc4_projection"],tier:"modeled",skipReason:"NPCC4 SLR projection: not yet wired into FSM (static reference card on hold)"}],capstone:[{id:"CAP-001",name:"rag.granite_embedding",stepNames:["rag_granite_embedding"],tier:"proxy",skipReason:"Granite Embedding RAG: no policy retrieval (out-of-NYC scope)"},{id:"CAP-002",name:"gliner.typed_extraction",stepNames:["gliner_extract"],tier:"proxy",skipReason:"GLiNER typed extraction: no RAG hits to extract over"},{id:"CAP-003",name:"granite41.compose_briefing",stepNames:["reconcile_granite41","mellea_reconcile_address","reconcile_neighborhood","reconcile_development","reconcile_live_now"],tier:"modeled",skipReason:"Reconciler did not run (no grounded data available)"},{id:"CAP-004",name:"mellea.grounding_check",stepNames:["mellea_grounding"],tier:"modeled",skipReason:"Mellea grounding-check: rolled into reconcile step on this run"}]};function Fr(F,e){const S=xr[F]??[],_=new Map;for(const u of e)_.set(u.name,u);const M=[],n=new Set;for(const u of S){let d;for(const x of u.stepNames){const h=_.get(x);if(h){d=h,n.add(x);break}}d?M.push({...d,id:u.id,name:u.name,tier:d.tier??u.tier??null}):M.push({id:u.id,name:u.name,status:"not_invoked",tier:u.tier??null,note:u.skipReason})}for(const u of e)n.has(u.name)||M.push(u);return M}export{Ar as C,Rr as F,Br as M,Nr as R,Fr as f};
|
web/sveltekit/build/_app/immutable/chunks/CW0UkEuV.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
var $=a=>{throw TypeError(a)};var _t=(a,t,r)=>t.has(a)||$("Cannot "+r);var k=(a,t,r)=>(_t(a,t,"read from private field"),r?r.call(a):t.get(a)),V=(a,t,r)=>t.has(a)?$("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(a):t.set(a,r);import{i as q,a as v,g as F,s as E,f as w,d as bt,b as pt,c as tt,t as yt}from"./BLULdth_.js";import{o as C,q as et,v as L,t as y,w as rt,h as wt,b1 as Wt,A as at,C as xt,a3 as At,a$ as Mt,aY as St,aQ as Tt,b2 as Ct,b3 as Et,c as _,r as m,s as T,k as n,ah as G,f as U,j as Y,l as I,p as X,a as Z,am as It,az as it,ac as Nt}from"./CYuHyzh3.js";import{p as j,i as N}from"./vWNuMvXT.js";import{s as c,e as st,i as nt}from"./B7gjWklj.js";import{s as Pt}from"./CdmpEGnB.js";function Dt(a,t,r=!1,e=!1,i=!1,s=!1){var o=a,f="";if(r){var u=a;C&&(o=et(L(u)))}y(()=>{var b=wt;if(f===(f=t()??"")){C&&rt();return}if(r&&!C){b.nodes=null,u.innerHTML=f,f!==""&&q(L(u),u.lastChild);return}if(b.nodes!==null&&(Wt(b.nodes.start,b.nodes.end),b.nodes=null),f!==""){if(C){at.data;for(var h=rt(),d=h;h!==null&&(h.nodeType!==xt||h.data!=="");)d=h,h=At(h);if(h===null)throw Mt(),St;q(at,d),o=et(h);return}var g=e?Ct:i?Et:void 0,W=Tt(e?"svg":i?"math":"template",g);W.innerHTML=f;var M=e||i?W:W.content;if(q(L(M),M.lastChild),e||i)for(;L(M);)o.before(L(M));else o.before(M)}})}function ct(a){var t,r,e="";if(typeof a=="string"||typeof a=="number")e+=a;else if(typeof a=="object")if(Array.isArray(a)){var i=a.length;for(t=0;t<i;t++)a[t]&&(r=ct(a[t]))&&(e&&(e+=" "),e+=r)}else for(r in a)a[r]&&(e&&(e+=" "),e+=r);return e}function Rt(){for(var a,t,r=0,e="",i=arguments.length;r<i;r++)(a=arguments[r])&&(t=ct(a))&&(e&&(e+=" "),e+=t);return e}function me(a){return typeof a=="object"?Rt(a):a??""}const ot=[...`
|
| 2 |
-
\r\f \v\uFEFF`];function Ot(a,t,r){var e=a==null?"":""+a;if(t&&(e=e?e+" "+t:t),r){for(var i of Object.keys(r))if(r[i])e=e?e+" "+i:i;else if(e.length)for(var s=i.length,o=0;(o=e.indexOf(i,o))>=0;){var f=o+s;(o===0||ot.includes(e[o-1]))&&(f===e.length||ot.includes(e[f]))?e=(o===0?"":e.substring(0,o))+e.substring(f+1):o=f}}return e===""?null:e}function lt(a,t=!1){var r=t?" !important;":";",e="";for(var i of Object.keys(a)){var s=a[i];s!=null&&s!==""&&(e+=" "+i+": "+s+r)}return e}function kt(a,t){if(t){var r="",e,i;return Array.isArray(t)?(e=t[0],i=t[1]):e=t,e&&(r+=lt(e)),i&&(r+=lt(i,!0)),r=r.trim(),r===""?null:r}return String(a)}function ft(a,t,r,e,i,s){var o=a.__className;if(C||o!==r||o===void 0){var f=Ot(r,e,s);(!C||f!==a.getAttribute("class"))&&(f==null?a.removeAttribute("class"):a.className=f),a.__className=r}else if(s&&i!==s)for(var u in s){var b=!!s[u];(i==null||b!==!!i[u])&&a.classList.toggle(u,b)}return s}function Q(a,t={},r,e){for(var i in r){var s=r[i];t[i]!==s&&(r[i]==null?a.style.removeProperty(i):a.style.setProperty(i,s,e))}}function Lt(a,t,r,e){var i=a.__style;if(C||i!==t){var s=kt(t,e);(!C||s!==a.getAttribute("style"))&&(s==null?a.removeAttribute("style"):a.style.cssText=s),a.__style=t}else e&&(Array.isArray(e)?(Q(a,r==null?void 0:r[0],e[0]),Q(a,r==null?void 0:r[1],e[1],"important")):Q(a,r,e));return e}var jt=F('<rect x="0" y="0"></rect>'),zt=F('<rect fill="none"></rect>'),Bt=F("<circle></circle>"),Ft=F('<defs><pattern width="3" height="3" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="3" stroke-width="1.5"></line></pattern></defs><rect></rect>',1),Ht=F('<svg role="img" style="flex: none; display: inline-block; vertical-align: -0.12em;"><title> </title><!></svg>');function dt(a,t){let r=j(t,"size",3,12),e=j(t,"color",3,"currentColor");const i={empirical:"Empirical: directly measured or observed",modeled:"Modeled: scenario-based prediction",proxy:"Proxy: indirect indicator",synthetic:"Synthetic prior: generated, not observed"};let s=G(()=>Math.max(1,Math.round(r()/9))),o=G(()=>t.title??i[t.tier]),f=G(()=>`rip-stripe-${t.tier}-${r()}`);var u=Ht(),b=_(u),h=_(b,!0);m(b);var d=T(b);{var g=p=>{var l=jt();y(()=>{c(l,"width",r()),c(l,"height",r()),c(l,"fill",e())}),v(p,l)},W=p=>{var l=zt();y(()=>{c(l,"x",n(s)/2),c(l,"y",n(s)/2),c(l,"width",r()-n(s)),c(l,"height",r()-n(s)),c(l,"stroke",e()),c(l,"stroke-width",n(s))}),v(p,l)},M=p=>{var l=Bt();y(()=>{c(l,"cx",r()/2),c(l,"cy",r()/2),c(l,"r",r()/2-.5),c(l,"fill",e())}),v(p,l)},K=p=>{var l=Ft(),x=U(l),R=_(x),S=_(R);m(R),m(x);var A=T(x);y(()=>{c(R,"id",n(f)),c(S,"stroke",e()),c(A,"x",n(s)/2),c(A,"y",n(s)/2),c(A,"width",r()-n(s)),c(A,"height",r()-n(s)),c(A,"fill",`url(#${n(f)??""})`),c(A,"stroke",e()),c(A,"stroke-width",n(s))}),v(p,l)};N(d,p=>{t.tier==="empirical"?p(g):t.tier==="modeled"?p(W,1):t.tier==="proxy"?p(M,2):p(K,-1)})}m(u),y(()=>{c(u,"width",r()),c(u,"height",r()),c(u,"viewBox",`0 0 ${r()??""} ${r()??""}`),c(u,"aria-label",n(o)),E(h,n(o))}),v(a,u)}var Gt=w('<span><span class="claim-glyph" aria-hidden="false"><!></span> <span class="claim-body"><!></span></span>');function Ut(a,t){var r=Gt(),e=_(r),i=_(e);dt(i,{get tier(){return t.tier},size:11,get color(){return`var(--tier-${t.tier??""})`}}),m(e);var s=T(e,2),o=_(s);Pt(o,()=>t.children),m(s),m(r),y(()=>{ft(r,1,`claim claim-${t.tier??""}`),c(r,"data-tier",t.tier)}),v(a,r)}var z,B;class Yt{constructor(){V(this,z,Y(null));V(this,B,Y(null))}get active(){return n(k(this,z))}set active(t){I(k(this,z),t,!0)}get highlightDocId(){return n(k(this,B))}set highlightDocId(t){I(k(this,B),t,!0)}}z=new WeakMap,B=new WeakMap;const Kt=new Yt;var Vt=w('<a class="inline-cite"><sup> </sup></a>');function qt(a,t){X(t,!0);function r(o){o.preventDefault(),Kt.active=t.c.id;const f=document.getElementById(`cite-${t.c.id}`);f==null||f.scrollIntoView({block:"center",behavior:"smooth"})}var e=Vt(),i=_(e),s=_(i);m(i),m(e),y(()=>{c(e,"href",`#cite-${t.c.id??""}`),c(e,"data-cite",t.c.id),c(e,"aria-label",`Citation ${t.c.n??""}: ${t.c.source??""}, ${t.c.title??""}`),E(s,`[${t.c.n??""}]`)}),pt("click",e,r),v(a,e),Z()}bt(["click"]);const Qt={empirical:{label:"Empirical",short:"EMP",desc:"Directly measured or observed",examples:"USGS high-water marks · FloodNet sensors · Sandy Inundation Zone"},modeled:{label:"Modeled",short:"MOD",desc:"Scenario-based prediction",examples:"FEMA flood zones · DEP stormwater scenarios · NPCC4 SLR"},proxy:{label:"Proxy",short:"PRX",desc:"Indirect indicator",examples:"311 flood complaints · NFIP claims · terrain indices"},synthetic:{label:"Synthetic prior",short:"SYN",desc:"Generated, not observed",examples:"TerraMind land-cover · synthetic SAR for occluded days"}};function ge(a){const t=a.toLowerCase();return t.startsWith("syn")||t.startsWith("terramind")||t.includes("synthetic")?"synthetic":t.startsWith("sandy")||t.startsWith("floodnet")||t.startsWith("usgs")||t.startsWith("mta_entrance")||t.startsWith("nycha_dev")||t.startsWith("doe_school")||t.startsWith("doh_hospital")||t.startsWith("ida_hwm")||t.startsWith("hwm")||t.startsWith("noaa")||t.startsWith("nws_obs")||t.startsWith("prithvi_eo")?"empirical":t.startsWith("dep")||t.startsWith("fema_firm")||t.startsWith("npcc")||t.startsWith("wrp")||t.includes("scenario")||t.includes("forecast")||t.startsWith("prithvi")||t.startsWith("ttm")||t.startsWith("nws_alert")?"modeled":(t.startsWith("nyc311")||t.startsWith("311")||t.startsWith("nfip")||t.startsWith("rag")||t.startsWith("dob")||t.startsWith("hand")||t.startsWith("twi")||t.startsWith("microtopo"),"proxy")}function _e(a){const t=a.toLowerCase();return t==="geocode"||t.startsWith("fan")||t.startsWith("merge")||t==="plan"||t==="compose"||t==="reconcile"||t==="stream"?null:t==="sandy"||t==="sandy_inundation"||t==="floodnet"||t==="ida_hwm"||t==="noaa_tides"||t==="nws_obs"||t==="prithvi_eo_v2"||t==="prithvi_eo_live"||t==="mta_entrance_exposure"||t==="mta_entrances"||t==="nycha_developments"||t==="doe_school_exposure"||t==="doe_schools"||t==="doh_hospital_exposure"||t==="doh_hospitals"?"empirical":t==="dep"||t==="dep_stormwater"||t==="ttm_forecast"||t==="ttm_311_forecast"||t==="floodnet_forecast"||t==="nws_alerts"||t==="prithvi_water"?"modeled":t==="nyc311"||t==="microtopo"||t==="microtopo_lidar"||t==="rag"||t==="rag_mta"?"proxy":t==="terramind"||t==="terramind_synthesis"?"synthetic":null}var Xt=w("<span><!> </span>");function Zt(a,t){X(t,!0);let r=j(t,"compact",3,!1),e=G(()=>Qt[t.tier]);var i=Xt();let s;var o=_(i);dt(o,{get tier(){return t.tier},size:10,get color(){return`var(--tier-${t.tier??""})`}});var f=T(o);m(i),y(()=>{ft(i,1,`tier-badge tier-badge-${t.tier??""}`,"svelte-1acpjpp"),c(i,"title",n(e).desc),s=Lt(i,"",s,{color:`var(--tier-${t.tier??""})`}),E(f,` ${(r()?n(e).short:n(e).label)??""}`)}),v(a,i),Z()}var Jt=w('<span class="briefing-section-tier"><!></span>'),$t=w('<span class="briefing-section-title"> </span>'),te=w('<h3 class="briefing-section-head"><span class="briefing-section-num"> </span> <span class="briefing-section-label"> </span> <!> <!></h3>');function ee(a,t){var r=te(),e=_(r),i=_(e,!0);m(e);var s=T(e,2),o=_(s,!0);m(s);var f=T(s,2);{var u=d=>{var g=Jt(),W=_(g);Zt(W,{get tier(){return t.tier},compact:!0}),m(g),v(d,g)};N(f,d=>{t.tier&&d(u)})}var b=T(f,2);{var h=d=>{var g=$t(),W=_(g,!0);m(g),y(()=>E(W,t.title)),v(d,g)};N(b,d=>{t.title&&d(h)})}m(r),y(()=>{E(i,t.n),E(o,t.label)}),v(a,r)}var re=w('<div class="briefing-status"></div>'),ae=w("<!><!>",1),ie=w("<span> </span>"),se=w('<p class="briefing-para"></p>'),ne=w('<span class="streaming-caret" aria-hidden="true">▍</span>'),oe=w('<div class="briefing-prose" role="log" aria-live="polite" aria-atomic="false" aria-label="Streaming flood-exposure briefing"><!> <!></div>');function be(a,t){X(t,!0);let r=j(t,"streaming",3,!1),e=j(t,"replayKey",3,0),i=Y(It(t.blocks.length)),s=Y(!1);it(()=>{typeof window>"u"||I(s,window.matchMedia("(prefers-reduced-motion: reduce)").matches,!0)}),it(()=>{if(e(),!r()){I(i,t.blocks.length,!0);return}if(n(s)){I(i,t.blocks.length,!0);return}I(i,0);let h=0,d;const g=()=>{h++,I(i,h,!0),h<t.blocks.length&&(d=setTimeout(g,h<2?280:420))};return d=setTimeout(g,240),()=>clearTimeout(d)});var o=oe(),f=_(o);st(f,17,()=>t.blocks.slice(0,n(i)),nt,(h,d)=>{var g=tt(),W=U(g);{var M=l=>{var x=re();Dt(x,()=>n(d).html,!0),m(x),v(l,x)},K=l=>{ee(l,{get n(){return n(d).n},get label(){return n(d).label},get tier(){return n(d).tier},get title(){return n(d).title}})},p=l=>{var x=se();st(x,21,()=>n(d).parts,nt,(R,S)=>{var A=tt(),ht=U(A);{var ut=P=>{var D=ae(),H=U(D);Ut(H,{get tier(){return n(S).tier},children:(O,le)=>{Nt();var J=yt();y(()=>E(J,n(S).text)),v(O,J)}});var mt=T(H);{var gt=O=>{qt(O,{get c(){return t.citations[n(S).cite]}})};N(mt,O=>{n(S).cite&&t.citations[n(S).cite]&&O(gt)})}v(P,D)},vt=P=>{var D=ie(),H=_(D,!0);m(D),y(()=>E(H,n(S).text)),v(P,D)};N(ht,P=>{n(S).tier?P(ut):P(vt,-1)})}v(R,A)}),m(x),v(l,x)};N(W,l=>{n(d).kind==="status"?l(M):n(d).kind==="head"?l(K,1):l(p,-1)})}v(h,g)});var u=T(f,2);{var b=h=>{var d=ne();v(h,d)};N(u,h=>{n(i)<t.blocks.length&&h(b)})}m(o),v(a,o),Z()}export{be as B,dt as T,Zt as a,Lt as b,Kt as c,me as d,_e as e,ft as s,ge as t};
|
|
|
|
|
|
|
|
|
web/sveltekit/build/_app/immutable/chunks/CYuHyzh3.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
var gt=Object.defineProperty;var kn=e=>{throw TypeError(e)};var yt=(e,n,t)=>n in e?gt(e,n,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[n]=t;var se=(e,n,t)=>yt(e,typeof n!="symbol"?n+"":n,t),rn=(e,n,t)=>n.has(e)||kn("Cannot "+t);var u=(e,n,t)=>(rn(e,n,"read from private field"),t?t.call(e):n.get(e)),N=(e,n,t)=>n.has(e)?kn("Cannot add the same private member more than once"):n instanceof WeakSet?n.add(e):n.set(e,t),ge=(e,n,t,r)=>(rn(e,n,"write to private field"),r?r.call(e,t):n.set(e,t),t),D=(e,n,t)=>(rn(e,n,"access private method"),t);var mt=Array.isArray,Tt=Array.prototype.indexOf,Re=Array.prototype.includes,gr=Array.from,yr=Object.defineProperty,Pe=Object.getOwnPropertyDescriptor,mr=Object.getOwnPropertyDescriptors,bt=Object.prototype,At=Array.prototype,St=Object.getPrototypeOf,Nn=Object.isExtensible;const Rt=()=>{};function kt(e){for(var n=0;n<e.length;n++)e[n]()}function jn(){var e,n,t=new Promise((r,s)=>{e=r,n=s});return{promise:t,resolve:e,reject:n}}const x=2,ve=4,Ye=8,Yn=1<<24,G=16,z=32,ee=64,Nt=128,F=512,T=1024,O=2048,K=4096,L=8192,q=16384,Ee=32768,On=1<<25,Xe=65536,an=1<<17,Ot=1<<18,He=1<<19,Hn=1<<20,Tr=1<<25,de=65536,Ze=1<<21,Ce=1<<22,Q=1<<23,sn=Symbol("$state"),br=Symbol("legacy props"),Ar=Symbol(""),$=new class extends Error{constructor(){super(...arguments);se(this,"name","StaleReactionError");se(this,"message","The reaction that called `getAbortSignal()` was re-run or destroyed")}};var Ln;const Rr=!!((Ln=globalThis.document)!=null&&Ln.contentType)&&globalThis.document.contentType.includes("xml"),tn=3,qn=8;function Un(e){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}function xt(){throw new Error("https://svelte.dev/e/async_derived_orphan")}function kr(e,n,t){throw new Error("https://svelte.dev/e/each_key_duplicate")}function It(e){throw new Error("https://svelte.dev/e/effect_in_teardown")}function Dt(){throw new Error("https://svelte.dev/e/effect_in_unowned_derived")}function Pt(e){throw new Error("https://svelte.dev/e/effect_orphan")}function Mt(){throw new Error("https://svelte.dev/e/effect_update_depth_exceeded")}function Nr(){throw new Error("https://svelte.dev/e/hydration_failed")}function Or(e){throw new Error("https://svelte.dev/e/props_invalid_value")}function Ct(){throw new Error("https://svelte.dev/e/state_descriptors_fixed")}function Ft(){throw new Error("https://svelte.dev/e/state_prototype_fixed")}function Lt(){throw new Error("https://svelte.dev/e/state_unsafe_mutation")}function xr(){throw new Error("https://svelte.dev/e/svelte_boundary_reset_onerror")}const Ir=1,Dr=2,Pr=4,Mr=8,Cr=16,Fr=1,Lr=2,jr=4,Yr=8,Hr=16,qr=1,Ur=2,jt="[",Yt="[!",Vr="[?",Ht="]",dn={},S=Symbol(),qt="http://www.w3.org/1999/xhtml",Br="http://www.w3.org/2000/svg",Gr="http://www.w3.org/1998/Math/MathML";function Ut(){console.warn("https://svelte.dev/e/derived_inert")}function hn(e){console.warn("https://svelte.dev/e/hydration_mismatch")}function zr(){console.warn("https://svelte.dev/e/svelte_boundary_reset_noop")}let he=!1;function Kr(e){he=e}let R;function ke(e){if(e===null)throw hn(),dn;return R=e}function $r(){return ke(re(R))}function Xr(e){if(he){if(re(R)!==null)throw hn(),dn;R=e}}function Zr(e=1){if(he){for(var n=e,t=R;n--;)t=re(t);R=t}}function Wr(e=!0){for(var n=0,t=R;;){if(t.nodeType===qn){var r=t.data;if(r===Ht){if(n===0)return t;n-=1}else(r===jt||r===Yt||r[0]==="["&&!isNaN(Number(r.slice(1))))&&(n+=1)}var s=re(t);e&&t.remove(),t=s}}function Jr(e){if(!e||e.nodeType!==qn)throw hn(),dn;return e.data}function Vn(e){return e===this.v}function Vt(e,n){return e!=e?n==n:e!==n||e!==null&&typeof e=="object"||typeof e=="function"}function Bn(e){return!Vt(e,this.v)}let qe=!1;function Qr(){qe=!0}let b=null;function We(e){b=e}function es(e,n=!1,t){b={p:b,i:!1,c:null,e:null,s:e,x:null,r:E,l:qe&&!n?{s:null,u:null,$:[]}:null}}function ns(e){var n=b,t=n.e;if(t!==null){n.e=null;for(var r of t)it(r)}return n.i=!0,b=n.p,{}}function Ue(){return!qe||b!==null&&b.l===null}let fe=[];function Gn(){var e=fe;fe=[],kt(e)}function xn(e){if(fe.length===0&&!Me){var n=fe;queueMicrotask(()=>{n===fe&&Gn()})}fe.push(e)}function Bt(){for(;fe.length>0;)Gn()}function Gt(e){var n=E;if(n===null)return w.f|=Q,e;if((n.f&Ee)===0&&(n.f&ve)===0)throw e;Je(e,n)}function Je(e,n){for(;n!==null;){if((n.f&Nt)!==0){if((n.f&Ee)===0)throw e;try{n.b.error(e);return}catch(t){e=t}}n=n.parent}throw e}const zt=-7169;function g(e,n){e.f=e.f&zt|n}function pn(e){(e.f&F)!==0||e.deps===null?g(e,T):g(e,K)}function zn(e){if(e!==null)for(const n of e)(n.f&x)===0||(n.f&de)===0||(n.f^=de,zn(n.deps))}function Kt(e,n,t){(e.f&O)!==0?n.add(e):(e.f&K)!==0&&t.add(e),zn(e.deps),g(e,T)}const le=new Set;let p=null,k=null,un=null,Me=!1,ln=!1,ye=null,Be=null;var In=0;let $t=1;var Te,be,ue,X,V,Le,M,je,J,Z,B,Ae,Se,oe,y,Ge,Kn,ze,on,Ke,Xt;const nn=class nn{constructor(){N(this,y);se(this,"id",$t++);se(this,"current",new Map);se(this,"previous",new Map);N(this,Te,new Set);N(this,be,new Set);N(this,ue,new Set);N(this,X,new Map);N(this,V,new Map);N(this,Le,null);N(this,M,[]);N(this,je,[]);N(this,J,new Set);N(this,Z,new Set);N(this,B,new Map);N(this,Ae,new Set);se(this,"is_fork",!1);N(this,Se,!1);N(this,oe,new Set)}skip_effect(n){u(this,B).has(n)||u(this,B).set(n,{d:[],m:[]}),u(this,Ae).delete(n)}unskip_effect(n,t=r=>this.schedule(r)){var r=u(this,B).get(n);if(r){u(this,B).delete(n);for(var s of r.d)g(s,O),t(s);for(s of r.m)g(s,K),t(s)}u(this,Ae).add(n)}capture(n,t,r=!1){n.v!==S&&!this.previous.has(n)&&this.previous.set(n,n.v),(n.f&Q)===0&&(this.current.set(n,[t,r]),k==null||k.set(n,t)),this.is_fork||(n.v=t)}activate(){p=this}deactivate(){p=null,k=null}flush(){try{ln=!0,p=this,D(this,y,ze).call(this)}finally{In=0,un=null,ye=null,Be=null,ln=!1,p=null,k=null,ce.clear()}}discard(){for(const n of u(this,be))n(this);u(this,be).clear(),u(this,ue).clear(),le.delete(this)}register_created_effect(n){u(this,je).push(n)}increment(n,t){let r=u(this,X).get(t)??0;if(u(this,X).set(t,r+1),n){let s=u(this,V).get(t)??0;u(this,V).set(t,s+1)}}decrement(n,t,r){let s=u(this,X).get(t)??0;if(s===1?u(this,X).delete(t):u(this,X).set(t,s-1),n){let l=u(this,V).get(t)??0;l===1?u(this,V).delete(t):u(this,V).set(t,l-1)}u(this,Se)||r||(ge(this,Se,!0),xn(()=>{ge(this,Se,!1),this.flush()}))}transfer_effects(n,t){for(const r of n)u(this,J).add(r);for(const r of t)u(this,Z).add(r);n.clear(),t.clear()}oncommit(n){u(this,Te).add(n)}ondiscard(n){u(this,be).add(n)}on_fork_commit(n){u(this,ue).add(n)}run_fork_commit_callbacks(){for(const n of u(this,ue))n(this);u(this,ue).clear()}settled(){return(u(this,Le)??ge(this,Le,jn())).promise}static ensure(){if(p===null){const n=p=new nn;ln||(le.add(p),Me||xn(()=>{p===n&&n.flush()}))}return p}apply(){{k=null;return}}schedule(n){var s;if(un=n,(s=n.b)!=null&&s.is_pending&&(n.f&(ve|Ye|Yn))!==0&&(n.f&Ee)===0){n.b.defer_effect(n);return}for(var t=n;t.parent!==null;){t=t.parent;var r=t.f;if(ye!==null&&t===E&&(w===null||(w.f&x)===0))return;if((r&(ee|z))!==0){if((r&T)===0)return;t.f^=T}}u(this,M).push(t)}};Te=new WeakMap,be=new WeakMap,ue=new WeakMap,X=new WeakMap,V=new WeakMap,Le=new WeakMap,M=new WeakMap,je=new WeakMap,J=new WeakMap,Z=new WeakMap,B=new WeakMap,Ae=new WeakMap,Se=new WeakMap,oe=new WeakMap,y=new WeakSet,Ge=function(){return this.is_fork||u(this,V).size>0},Kn=function(){for(const r of u(this,oe))for(const s of u(r,V).keys()){for(var n=!1,t=s;t.parent!==null;){if(u(this,B).has(t)){n=!0;break}t=t.parent}if(!n)return!0}return!1},ze=function(){var a,f;if(In++>1e3&&(le.delete(this),Wt()),!D(this,y,Ge).call(this)){for(const i of u(this,J))u(this,Z).delete(i),g(i,O),this.schedule(i);for(const i of u(this,Z))g(i,K),this.schedule(i)}const n=u(this,M);ge(this,M,[]),this.apply();var t=ye=[],r=[],s=Be=[];for(const i of n)try{D(this,y,on).call(this,i,t,r)}catch(c){throw Zn(i),c}if(p=null,s.length>0){var l=nn.ensure();for(const i of s)l.schedule(i)}if(ye=null,Be=null,D(this,y,Ge).call(this)||D(this,y,Kn).call(this)){D(this,y,Ke).call(this,r),D(this,y,Ke).call(this,t);for(const[i,c]of u(this,B))Xn(i,c)}else{u(this,X).size===0&&le.delete(this),u(this,J).clear(),u(this,Z).clear();for(const i of u(this,Te))i(this);u(this,Te).clear(),Dn(r),Dn(t),(a=u(this,Le))==null||a.resolve()}var o=p;if(u(this,M).length>0){const i=o??(o=this);u(i,M).push(...u(this,M).filter(c=>!u(i,M).includes(c)))}o!==null&&(le.add(o),D(f=o,y,ze).call(f))},on=function(n,t,r){n.f^=T;for(var s=n.first;s!==null;){var l=s.f,o=(l&(z|ee))!==0,a=o&&(l&T)!==0,f=a||(l&L)!==0||u(this,B).has(s);if(!f&&s.fn!==null){o?s.f^=T:(l&ve)!==0?t.push(s):Ve(s)&&((l&G)!==0&&u(this,Z).add(s),Oe(s));var i=s.first;if(i!==null){s=i;continue}}for(;s!==null;){var c=s.next;if(c!==null){s=c;break}s=s.parent}}},Ke=function(n){for(var t=0;t<n.length;t+=1)Kt(n[t],u(this,J),u(this,Z))},Xt=function(){var c,h,d;for(const v of le){var n=v.id<this.id,t=[];for(const[_,[A,m]]of this.current){if(v.current.has(_)){var r=v.current.get(_)[0];if(n&&A!==r)v.current.set(_,[A,m]);else continue}t.push(_)}var s=[...v.current.keys()].filter(_=>!this.current.has(_));if(s.length===0)n&&v.discard();else if(t.length>0){if(n)for(const _ of u(this,Ae))v.unskip_effect(_,A=>{var m;(A.f&(G|Ce))!==0?v.schedule(A):D(m=v,y,Ke).call(m,[A])});v.activate();var l=new Set,o=new Map;for(var a of t)$n(a,s,l,o);o=new Map;var f=[...v.current.keys()].filter(_=>this.current.has(_)?this.current.get(_)[0]!==_:!0);for(const _ of u(this,je))(_.f&(q|L|an))===0&&wn(_,f,o)&&((_.f&(Ce|G))!==0?(g(_,O),v.schedule(_)):u(v,J).add(_));if(u(v,M).length>0){v.apply();for(var i of u(v,M))D(c=v,y,on).call(c,i,[],[]);ge(v,M,[])}v.deactivate()}}for(const v of le)u(v,oe).has(this)&&(u(v,oe).delete(this),u(v,oe).size===0&&!D(h=v,y,Ge).call(h)&&(v.activate(),D(d=v,y,ze).call(d)))};let pe=nn;function Zt(e){var n=Me;Me=!0;try{for(var t;;){if(Bt(),p===null)return t;p.flush()}}finally{Me=n}}function Wt(){try{Mt()}catch(e){Je(e,un)}}let Y=null;function Dn(e){var n=e.length;if(n!==0){for(var t=0;t<n;){var r=e[t++];if((r.f&(q|L))===0&&Ve(r)&&(Y=new Set,Oe(r),r.deps===null&&r.first===null&&r.nodes===null&&r.teardown===null&&r.ac===null&&at(r),(Y==null?void 0:Y.size)>0)){ce.clear();for(const s of Y){if((s.f&(q|L))!==0)continue;const l=[s];let o=s.parent;for(;o!==null;)Y.has(o)&&(Y.delete(o),l.push(o)),o=o.parent;for(let a=l.length-1;a>=0;a--){const f=l[a];(f.f&(q|L))===0&&Oe(f)}}Y.clear()}}Y=null}}function $n(e,n,t,r){if(!t.has(e)&&(t.add(e),e.reactions!==null))for(const s of e.reactions){const l=s.f;(l&x)!==0?$n(s,n,t,r):(l&(Ce|G))!==0&&(l&O)===0&&wn(s,n,r)&&(g(s,O),En(s))}}function wn(e,n,t){const r=t.get(e);if(r!==void 0)return r;if(e.deps!==null)for(const s of e.deps){if(Re.call(n,s))return!0;if((s.f&x)!==0&&wn(s,n,t))return t.set(s,!0),!0}return t.set(e,!1),!1}function En(e){p.schedule(e)}function Xn(e,n){if(!((e.f&z)!==0&&(e.f&T)!==0)){(e.f&O)!==0?n.d.push(e):(e.f&K)!==0&&n.m.push(e),g(e,T);for(var t=e.first;t!==null;)Xn(t,n),t=t.next}}function Zn(e){g(e,T);for(var n=e.first;n!==null;)Zn(n),n=n.next}function Wn(e,n,t,r){const s=Ue()?yn:er;var l=e.filter(d=>!d.settled);if(t.length===0&&l.length===0){r(n.map(s));return}var o=E,a=Jt(),f=l.length===1?l[0].promise:l.length>1?Promise.all(l.map(d=>d.promise)):null;function i(d){a();try{r(d)}catch(v){(o.f&q)===0&&Je(v,o)}Qe()}if(t.length===0){f.then(()=>i(n.map(s)));return}var c=gn();function h(){Promise.all(t.map(d=>Qt(d))).then(d=>i([...n.map(s),...d])).catch(d=>Je(d,o)).finally(()=>c())}f?f.then(()=>{a(),h(),Qe()}):h()}function Jt(){var e=E,n=w,t=b,r=p;return function(l=!0){Ne(e),te(n),We(t),l&&(e.f&q)===0&&(r==null||r.activate(),r==null||r.apply())}}function Qe(e=!0){Ne(null),te(null),We(null),e&&(p==null||p.deactivate())}function gn(){var e=E,n=e.b,t=p,r=n.is_rendered();return n.update_pending_count(1,t),t.increment(r,e),(s=!1)=>{n.update_pending_count(-1,t),t.decrement(r,e,s)}}function yn(e){var n=x|O;return E!==null&&(E.f|=He),{ctx:b,deps:null,effects:null,equals:Vn,f:n,fn:e,reactions:null,rv:0,v:S,wv:0,parent:E,ac:null}}function Qt(e,n,t){let r=E;r===null&&xt();var s=void 0,l=Tn(S),o=!w,a=new Map;return or(()=>{var v;var f=E,i=jn();s=i.promise;try{Promise.resolve(e()).then(i.resolve,i.reject).finally(Qe)}catch(_){i.reject(_),Qe()}var c=p;if(o){if((f.f&Ee)!==0)var h=gn();if(r.b.is_rendered())(v=a.get(c))==null||v.reject($),a.delete(c);else{for(const _ of a.values())_.reject($);a.clear()}a.set(c,i)}const d=(_,A=void 0)=>{if(h){var m=A===$;h(m)}if(!(A===$||(f.f&q)!==0)){if(c.activate(),A)l.f|=Q,_n(l,A);else{(l.f&Q)!==0&&(l.f^=Q),_n(l,_);for(const[xe,Ie]of a){if(a.delete(xe),xe===c)break;Ie.reject($)}}c.deactivate()}};i.promise.then(d,_=>d(null,_||"unknown"))}),ar(()=>{for(const f of a.values())f.reject($)}),new Promise(f=>{function i(c){function h(){c===s?f(l):i(s)}c.then(h,h)}i(s)})}function ts(e){const n=yn(e);return ct(n),n}function er(e){const n=yn(e);return n.equals=Bn,n}function nr(e){var n=e.effects;if(n!==null){e.effects=null;for(var t=0;t<n.length;t+=1)we(n[t])}}function mn(e){var n,t=E,r=e.parent;if(!ne&&r!==null&&(r.f&(q|L))!==0)return Ut(),e.v;Ne(r);try{e.f&=~de,nr(e),n=ht(e)}finally{Ne(t)}return n}function Jn(e){var n=mn(e);if(!e.equals(n)&&(e.wv=vt(),(!(p!=null&&p.is_fork)||e.deps===null)&&(p!==null?p.capture(e,n,!0):e.v=n,e.deps===null))){g(e,T);return}ne||(k!==null?(lt()||p!=null&&p.is_fork)&&k.set(e,n):pn(e))}function tr(e){var n,t;if(e.effects!==null)for(const r of e.effects)(r.teardown||r.ac)&&((n=r.teardown)==null||n.call(r),(t=r.ac)==null||t.abort($),r.teardown=Rt,r.ac=null,Fe(r,0),Sn(r))}function Qn(e){if(e.effects!==null)for(const n of e.effects)n.teardown&&Oe(n)}let cn=new Set;const ce=new Map;let et=!1;function Tn(e,n){var t={f:0,v:e,reactions:null,equals:Vn,rv:0,wv:0};return t}function W(e,n){const t=Tn(e);return ct(t),t}function rs(e,n=!1,t=!0){var s;const r=Tn(e);return n||(r.equals=Bn),qe&&t&&b!==null&&b.l!==null&&((s=b.l).s??(s.s=[])).push(r),r}function ie(e,n,t=!1){w!==null&&(!H||(w.f&an)!==0)&&Ue()&&(w.f&(x|G|Ce|an))!==0&&(j===null||!Re.call(j,e))&&Lt();let r=t?De(n):n;return _n(e,r,Be)}function _n(e,n,t=null){if(!e.equals(n)){ce.set(e,ne?n:e.v);var r=pe.ensure();if(r.capture(e,n),(e.f&x)!==0){const s=e;(e.f&O)!==0&&mn(s),k===null&&pn(s)}e.wv=vt(),nt(e,O,t),Ue()&&E!==null&&(E.f&T)!==0&&(E.f&(z|ee))===0&&(C===null?dr([e]):C.push(e)),!r.is_fork&&cn.size>0&&!et&&rr()}return n}function rr(){et=!1;for(const e of cn)(e.f&T)!==0&&g(e,K),Ve(e)&&Oe(e);cn.clear()}function fn(e){ie(e,e.v+1)}function nt(e,n,t){var r=e.reactions;if(r!==null)for(var s=Ue(),l=r.length,o=0;o<l;o++){var a=r[o],f=a.f;if(!(!s&&a===E)){var i=(f&O)===0;if(i&&g(a,n),(f&x)!==0){var c=a;k==null||k.delete(c),(f&de)===0&&(f&F&&(E===null||(E.f&Ze)===0)&&(a.f|=de),nt(c,K,t))}else if(i){var h=a;(f&G)!==0&&Y!==null&&Y.add(h),t!==null?t.push(h):En(h)}}}}function De(e){if(typeof e!="object"||e===null||sn in e)return e;const n=St(e);if(n!==bt&&n!==At)return e;var t=new Map,r=mt(e),s=W(0),l=_e,o=a=>{if(_e===l)return a();var f=w,i=_e;te(null),Fn(l);var c=a();return te(f),Fn(i),c};return r&&t.set("length",W(e.length)),new Proxy(e,{defineProperty(a,f,i){(!("value"in i)||i.configurable===!1||i.enumerable===!1||i.writable===!1)&&Ct();var c=t.get(f);return c===void 0?o(()=>{var h=W(i.value);return t.set(f,h),h}):ie(c,i.value,!0),!0},deleteProperty(a,f){var i=t.get(f);if(i===void 0){if(f in a){const c=o(()=>W(S));t.set(f,c),fn(s)}}else ie(i,S),fn(s);return!0},get(a,f,i){var v;if(f===sn)return e;var c=t.get(f),h=f in a;if(c===void 0&&(!h||(v=Pe(a,f))!=null&&v.writable)&&(c=o(()=>{var _=De(h?a[f]:S),A=W(_);return A}),t.set(f,c)),c!==void 0){var d=me(c);return d===S?void 0:d}return Reflect.get(a,f,i)},getOwnPropertyDescriptor(a,f){var i=Reflect.getOwnPropertyDescriptor(a,f);if(i&&"value"in i){var c=t.get(f);c&&(i.value=me(c))}else if(i===void 0){var h=t.get(f),d=h==null?void 0:h.v;if(h!==void 0&&d!==S)return{enumerable:!0,configurable:!0,value:d,writable:!0}}return i},has(a,f){var d;if(f===sn)return!0;var i=t.get(f),c=i!==void 0&&i.v!==S||Reflect.has(a,f);if(i!==void 0||E!==null&&(!c||(d=Pe(a,f))!=null&&d.writable)){i===void 0&&(i=o(()=>{var v=c?De(a[f]):S,_=W(v);return _}),t.set(f,i));var h=me(i);if(h===S)return!1}return c},set(a,f,i,c){var Rn;var h=t.get(f),d=f in a;if(r&&f==="length")for(var v=i;v<h.v;v+=1){var _=t.get(v+"");_!==void 0?ie(_,S):v in a&&(_=o(()=>W(S)),t.set(v+"",_))}if(h===void 0)(!d||(Rn=Pe(a,f))!=null&&Rn.writable)&&(h=o(()=>W(void 0)),ie(h,De(i)),t.set(f,h));else{d=h.v!==S;var A=o(()=>De(i));ie(h,A)}var m=Reflect.getOwnPropertyDescriptor(a,f);if(m!=null&&m.set&&m.set.call(c,i),!d){if(r&&typeof f=="string"){var xe=t.get("length"),Ie=Number(f);Number.isInteger(Ie)&&Ie>=xe.v&&ie(xe,Ie+1)}fn(s)}return!0},ownKeys(a){me(s);var f=Reflect.ownKeys(a).filter(h=>{var d=t.get(h);return d===void 0||d.v!==S});for(var[i,c]of t)c.v!==S&&!(i in a)&&f.push(i);return f},setPrototypeOf(){Ft()}})}var Pn,sr,lr,tt,rt;function ss(){if(Pn===void 0){Pn=window,sr=document,lr=/Firefox/.test(navigator.userAgent);var e=Element.prototype,n=Node.prototype,t=Text.prototype;tt=Pe(n,"firstChild").get,rt=Pe(n,"nextSibling").get,Nn(e)&&(e.__click=void 0,e.__className=void 0,e.__attributes=null,e.__style=void 0,e.__e=void 0),Nn(t)&&(t.__t=void 0)}}function en(e=""){return document.createTextNode(e)}function vn(e){return tt.call(e)}function re(e){return rt.call(e)}function ls(e,n){if(!he)return vn(e);var t=vn(R);if(t===null)t=R.appendChild(en());else if(n&&t.nodeType!==tn){var r=en();return t==null||t.before(r),ke(r),r}return n&&bn(t),ke(t),t}function is(e,n=!1){if(!he){var t=vn(e);return t instanceof Comment&&t.data===""?re(t):t}if(n){if((R==null?void 0:R.nodeType)!==tn){var r=en();return R==null||R.before(r),ke(r),r}bn(R)}return R}function fs(e,n=1,t=!1){let r=he?R:e;for(var s;n--;)s=r,r=re(r);if(!he)return r;if(t){if((r==null?void 0:r.nodeType)!==tn){var l=en();return r===null?s==null||s.after(l):r.before(l),ke(l),l}bn(r)}return ke(r),r}function as(e){e.textContent=""}function us(){return!1}function os(e,n,t){return document.createElementNS(n??qt,e,void 0)}function bn(e){if(e.nodeValue.length<65536)return;let n=e.nextSibling;for(;n!==null&&n.nodeType===tn;)n.remove(),e.nodeValue+=n.nodeValue,n=e.nextSibling}let Mn=!1;function ir(){Mn||(Mn=!0,document.addEventListener("reset",e=>{Promise.resolve().then(()=>{var n;if(!e.defaultPrevented)for(const t of e.target.elements)(n=t.__on_r)==null||n.call(t)})},{capture:!0}))}function An(e){var n=w,t=E;te(null),Ne(null);try{return e()}finally{te(n),Ne(t)}}function cs(e,n,t,r=t){e.addEventListener(n,()=>An(t));const s=e.__on_r;s?e.__on_r=()=>{s(),r(!0)}:e.__on_r=()=>r(!0),ir()}function st(e){E===null&&(w===null&&Pt(),Dt()),ne&&It()}function fr(e,n){var t=n.last;t===null?n.last=n.first=e:(t.next=e,e.prev=t,n.last=e)}function U(e,n){var t=E;t!==null&&(t.f&L)!==0&&(e|=L);var r={ctx:b,deps:null,nodes:null,f:e|O|F,first:null,fn:n,last:null,next:null,parent:t,b:t&&t.b,prev:null,teardown:null,wv:0,ac:null};p==null||p.register_created_effect(r);var s=r;if((e&ve)!==0)ye!==null?ye.push(r):pe.ensure().schedule(r);else if(n!==null){try{Oe(r)}catch(o){throw we(r),o}s.deps===null&&s.teardown===null&&s.nodes===null&&s.first===s.last&&(s.f&He)===0&&(s=s.first,(e&G)!==0&&(e&Xe)!==0&&s!==null&&(s.f|=Xe))}if(s!==null&&(s.parent=t,t!==null&&fr(s,t),w!==null&&(w.f&x)!==0&&(e&ee)===0)){var l=w;(l.effects??(l.effects=[])).push(s)}return r}function lt(){return w!==null&&!H}function ar(e){const n=U(Ye,null);return g(n,T),n.teardown=e,n}function ur(e){st();var n=E.f,t=!w&&(n&z)!==0&&(n&Ee)===0;if(t){var r=b;(r.e??(r.e=[])).push(e)}else return it(e)}function it(e){return U(ve|Hn,e)}function _s(e){return st(),U(Ye|Hn,e)}function vs(e){pe.ensure();const n=U(ee|He,e);return(t={})=>new Promise(r=>{t.outro?vr(n,()=>{we(n),r(void 0)}):(we(n),r(void 0))})}function ds(e){return U(ve,e)}function or(e){return U(Ce|He,e)}function hs(e,n=0){return U(Ye|n,e)}function ps(e,n=[],t=[],r=[]){Wn(r,n,t,s=>{U(Ye,()=>e(...s.map(me)))})}function ws(e,n=[],t=[],r=[]){if(t.length>0||r.length>0)var s=gn();Wn(r,n,t,l=>{U(ve,()=>e(...l.map(me))),s&&s()})}function Es(e,n=0){var t=U(G|n,e);return t}function gs(e){return U(z|He,e)}function ft(e){var n=e.teardown;if(n!==null){const t=ne,r=w;Cn(!0),te(null);try{n.call(null)}finally{Cn(t),te(r)}}}function Sn(e,n=!1){var t=e.first;for(e.first=e.last=null;t!==null;){const s=t.ac;s!==null&&An(()=>{s.abort($)});var r=t.next;(t.f&ee)!==0?t.parent=null:we(t,n),t=r}}function cr(e){for(var n=e.first;n!==null;){var t=n.next;(n.f&z)===0&&we(n),n=t}}function we(e,n=!0){var t=!1;(n||(e.f&Ot)!==0)&&e.nodes!==null&&e.nodes.end!==null&&(_r(e.nodes.start,e.nodes.end),t=!0),g(e,On),Sn(e,n&&!t),Fe(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(const l of r)l.stop();ft(e),e.f^=On,e.f|=q;var s=e.parent;s!==null&&s.first!==null&&at(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function _r(e,n){for(;e!==null;){var t=e===n?null:re(e);e.remove(),e=t}}function at(e){var n=e.parent,t=e.prev,r=e.next;t!==null&&(t.next=r),r!==null&&(r.prev=t),n!==null&&(n.first===e&&(n.first=r),n.last===e&&(n.last=t))}function vr(e,n,t=!0){var r=[];ut(e,r,!0);var s=()=>{t&&we(e),n&&n()},l=r.length;if(l>0){var o=()=>--l||s();for(var a of r)a.out(o)}else s()}function ut(e,n,t){if((e.f&L)===0){e.f^=L;var r=e.nodes&&e.nodes.t;if(r!==null)for(const a of r)(a.is_global||t)&&n.push(a);for(var s=e.first;s!==null;){var l=s.next;if((s.f&ee)===0){var o=(s.f&Xe)!==0||(s.f&z)!==0&&(e.f&G)!==0;ut(s,n,o?t:!1)}s=l}}}function ys(e){ot(e,!0)}function ot(e,n){if((e.f&L)!==0){e.f^=L,(e.f&T)===0&&(g(e,O),pe.ensure().schedule(e));for(var t=e.first;t!==null;){var r=t.next,s=(t.f&Xe)!==0||(t.f&z)!==0;ot(t,s?n:!1),t=r}var l=e.nodes&&e.nodes.t;if(l!==null)for(const o of l)(o.is_global||n)&&o.in()}}function ms(e,n){if(e.nodes)for(var t=e.nodes.start,r=e.nodes.end;t!==null;){var s=t===r?null:re(t);n.append(t),t=s}}let $e=!1,ne=!1;function Cn(e){ne=e}let w=null,H=!1;function te(e){w=e}let E=null;function Ne(e){E=e}let j=null;function ct(e){w!==null&&(j===null?j=[e]:j.push(e))}let I=null,P=0,C=null;function dr(e){C=e}let _t=1,ae=0,_e=ae;function Fn(e){_e=e}function vt(){return++_t}function Ve(e){var n=e.f;if((n&O)!==0)return!0;if(n&x&&(e.f&=~de),(n&K)!==0){for(var t=e.deps,r=t.length,s=0;s<r;s++){var l=t[s];if(Ve(l)&&Jn(l),l.wv>e.wv)return!0}(n&F)!==0&&k===null&&g(e,T)}return!1}function dt(e,n,t=!0){var r=e.reactions;if(r!==null&&!(j!==null&&Re.call(j,e)))for(var s=0;s<r.length;s++){var l=r[s];(l.f&x)!==0?dt(l,n,!1):n===l&&(t?g(l,O):(l.f&T)!==0&&g(l,K),En(l))}}function ht(e){var A;var n=I,t=P,r=C,s=w,l=j,o=b,a=H,f=_e,i=e.f;I=null,P=0,C=null,w=(i&(z|ee))===0?e:null,j=null,We(e.ctx),H=!1,_e=++ae,e.ac!==null&&(An(()=>{e.ac.abort($)}),e.ac=null);try{e.f|=Ze;var c=e.fn,h=c();e.f|=Ee;var d=e.deps,v=p==null?void 0:p.is_fork;if(I!==null){var _;if(v||Fe(e,P),d!==null&&P>0)for(d.length=P+I.length,_=0;_<I.length;_++)d[P+_]=I[_];else e.deps=d=I;if(lt()&&(e.f&F)!==0)for(_=P;_<d.length;_++)((A=d[_]).reactions??(A.reactions=[])).push(e)}else!v&&d!==null&&P<d.length&&(Fe(e,P),d.length=P);if(Ue()&&C!==null&&!H&&d!==null&&(e.f&(x|K|O))===0)for(_=0;_<C.length;_++)dt(C[_],e);if(s!==null&&s!==e){if(ae++,s.deps!==null)for(let m=0;m<t;m+=1)s.deps[m].rv=ae;if(n!==null)for(const m of n)m.rv=ae;C!==null&&(r===null?r=C:r.push(...C))}return(e.f&Q)!==0&&(e.f^=Q),h}catch(m){return Gt(m)}finally{e.f^=Ze,I=n,P=t,C=r,w=s,j=l,We(o),H=a,_e=f}}function hr(e,n){let t=n.reactions;if(t!==null){var r=Tt.call(t,e);if(r!==-1){var s=t.length-1;s===0?t=n.reactions=null:(t[r]=t[s],t.pop())}}if(t===null&&(n.f&x)!==0&&(I===null||!Re.call(I,n))){var l=n;(l.f&F)!==0&&(l.f^=F,l.f&=~de),l.v!==S&&pn(l),tr(l),Fe(l,0)}}function Fe(e,n){var t=e.deps;if(t!==null)for(var r=n;r<t.length;r++)hr(e,t[r])}function Oe(e){var n=e.f;if((n&q)===0){g(e,T);var t=E,r=$e;E=e,$e=!0;try{(n&(G|Yn))!==0?cr(e):Sn(e),ft(e);var s=ht(e);e.teardown=typeof s=="function"?s:null,e.wv=_t;var l}finally{$e=r,E=t}}}async function Ts(){await Promise.resolve(),Zt()}function bs(){return pe.ensure().settled()}function me(e){var n=e.f,t=(n&x)!==0;if(w!==null&&!H){var r=E!==null&&(E.f&q)!==0;if(!r&&(j===null||!Re.call(j,e))){var s=w.deps;if((w.f&Ze)!==0)e.rv<ae&&(e.rv=ae,I===null&&s!==null&&s[P]===e?P++:I===null?I=[e]:I.push(e));else{(w.deps??(w.deps=[])).push(e);var l=e.reactions;l===null?e.reactions=[w]:Re.call(l,w)||l.push(w)}}}if(ne&&ce.has(e))return ce.get(e);if(t){var o=e;if(ne){var a=o.v;return((o.f&T)===0&&o.reactions!==null||wt(o))&&(a=mn(o)),ce.set(o,a),a}var f=(o.f&F)===0&&!H&&w!==null&&($e||(w.f&F)!==0),i=(o.f&Ee)===0;Ve(o)&&(f&&(o.f|=F),Jn(o)),f&&!i&&(Qn(o),pt(o))}if(k!=null&&k.has(e))return k.get(e);if((e.f&Q)!==0)throw e.v;return e.v}function pt(e){if(e.f|=F,e.deps!==null)for(const n of e.deps)(n.reactions??(n.reactions=[])).push(e),(n.f&x)!==0&&(n.f&F)===0&&(Qn(n),pt(n))}function wt(e){if(e.v===S)return!0;if(e.deps===null)return!1;for(const n of e.deps)if(ce.has(n)||(n.f&x)!==0&&wt(n))return!0;return!1}function Et(e){var n=H;try{return H=!0,e()}finally{H=n}}function pr(e){b===null&&Un(),qe&&b.l!==null?wr(b).m.push(e):ur(()=>{const n=Et(e);if(typeof n=="function")return n})}function As(e){b===null&&Un(),pr(()=>()=>Et(e))}function wr(e){var n=e.l;return n.u??(n.u={a:[],b:[],m:[]})}export{Mr as $,R as A,Ht as B,qn as C,On as D,Xe as E,_n as F,p as G,Yt as H,Tr as I,gs as J,kr as K,us as L,er as M,mt as N,gr as O,Ir as P,Cr as Q,rs as R,sn as S,Tn as T,Dr as U,q as V,ys as W,vr as X,L as Y,xn as Z,z as _,ns as a,hn as a$,as as a0,ms as a1,we as a2,re as a3,ir as a4,Ar as a5,qt as a6,Rr as a7,St as a8,mr as a9,lt as aA,fn as aB,Nt as aC,Vr as aD,Kt as aE,Ne as aF,te as aG,We as aH,pe as aI,Gt as aJ,w as aK,Je as aL,xr as aM,zr as aN,ar as aO,An as aP,os as aQ,lr as aR,qr as aS,Ur as aT,Ee as aU,tn as aV,bn as aW,ss as aX,dn as aY,Nr as aZ,vs as a_,cs as aa,Ts as ab,Zr as ac,Ot as ad,He as ae,pr as af,ws as ag,ts as ah,sr as ai,Pe as aj,Or as ak,jr as al,De as am,ne as an,Yr as ao,qe as ap,Lr as aq,Fr as ar,yn as as,Hr as at,br as au,jt as av,Zt as aw,yr as ax,_s as ay,ur as az,b,As as b0,_r as b1,Br as b2,Gr as b3,Rt as b4,Vt as b5,bs as b6,ls as c,ds as d,Qr as e,is as f,hs as g,E as h,Es as i,W as j,me as k,ie as l,en as m,Pr as n,he as o,es as p,ke as q,Xr as r,fs as s,ps as t,Et as u,vn as v,$r as w,Jr as x,Wr as y,Kr as z};
|
|
|
|
|
|
web/sveltekit/build/_app/immutable/chunks/Chht4iZ-.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import{b as q,d as Cr,ac as dr,h as O,i as X,k as Or,P as wr,x as Z,Q as Lr,ad as Mr,R as nr,e as z,j as Y,C as kr,ae as Rr,af as ur,M as Dr,ag as L,L as U,ah as Hr,O as Pr,a2 as Fr,ai as gr,aj as x,ak as zr,al as Ur,a8 as Gr,am as sr,an as Vr,X as Yr,I as hr,K as pr,ao as y,ap as _r,aq as Br,ar as $r,as as Kr,N as jr,J as $,g as qr,at as br,n as Ar,au as Xr,av as yr,aw as Jr,ax as Qr,ay as Zr,az as Wr,aA as mr,aB as xr,aC as j,aD as re,aE as ee,aF as fe,aG as ie}from"./Dr93mMbz.js";import{i as ae,b as ne,d as ue,e as se,n as te,g as le}from"./WYoL_k1G.js";function ke(r,f){return f}function oe(r,f,e){for(var i=[],a=f.length,n,s=f.length,v=0;v<a;v++){let p=f[v];pr(p,()=>{if(n){if(n.pending.delete(p),n.done.add(p),n.pending.size===0){var t=r.outrogroups;W(r,x(n.done)),t.delete(n),t.size===0&&(r.outrogroups=null)}}else s-=1},!1)}if(s===0){var l=i.length===0&&e!==null;if(l){var d=e,o=d.parentNode;Kr(o),o.append(d),r.items.clear()}W(r,f,!l)}else n={pending:new Set(f),done:new Set},(r.outrogroups??(r.outrogroups=new Set)).add(n)}function W(r,f,e=!0){var i;if(r.pending.size>0){i=new Set;for(const s of r.pending.values())for(const v of s)i.add(r.items.get(v).e)}for(var a=0;a<f.length;a++){var n=f[a];if(i!=null&&i.has(n)){n.f|=L;const s=document.createDocumentFragment();jr(n,s)}else $(f[a],e)}}var tr;function Re(r,f,e,i,a,n=null){var s=r,v=new Map,l=(f&dr)!==0;if(l){var d=r;s=O?X(Or(d)):d.appendChild(q())}O&&wr();var o=null,p=Fr(()=>{var A=e();return gr(A)?A:A==null?[]:x(A)}),t,N=new Map,b=!0;function S(A){(w.effect.f&Yr)===0&&(w.pending.delete(A),w.fallback=o,ce(w,t,s,f,i),o!==null&&(t.length===0?(o.f&L)===0?hr(o):(o.f^=L,F(o,null,s)):pr(o,()=>{o=null})))}function u(A){w.pending.delete(A)}var g=Cr(()=>{t=Z(p);var A=t.length;let _=!1;if(O){var D=Lr(s)===Mr;D!==(A===0)&&(s=nr(),X(s),z(!1),_=!0)}for(var h=new Set,c=Dr,T=Pr(),I=0;I<A;I+=1){O&&Y.nodeType===kr&&Y.data===Rr&&(s=Y,_=!0,z(!1));var E=t[I],M=i(E,I),C=b?null:v.get(M);C?(C.v&&ur(C.v,E),C.i&&ur(C.i,I),T&&c.unskip_effect(C.e)):(C=ve(v,b?s:tr??(tr=q()),E,M,I,a,f,e),b||(C.e.f|=L),v.set(M,C)),h.add(M)}if(A===0&&n&&!o&&(b?o=U(()=>n(s)):(o=U(()=>n(tr??(tr=q()))),o.f|=L)),A>h.size&&Hr(),O&&A>0&&X(nr()),!b)if(N.set(c,h),T){for(const[G,V]of v)h.has(G)||c.skip_effect(V.e);c.oncommit(S),c.ondiscard(u)}else S(c);_&&z(!0),Z(p)}),w={effect:g,items:v,pending:N,outrogroups:null,fallback:o};b=!1,O&&(s=Y)}function H(r){for(;r!==null&&(r.f&Br)===0;)r=r.next;return r}function ce(r,f,e,i,a){var E,M,C,G,V,rr,er,fr,ir;var n=(i&$r)!==0,s=f.length,v=r.items,l=H(r.effect.first),d,o=null,p,t=[],N=[],b,S,u,g;if(n)for(g=0;g<s;g+=1)b=f[g],S=a(b,g),u=v.get(S).e,(u.f&L)===0&&((M=(E=u.nodes)==null?void 0:E.a)==null||M.measure(),(p??(p=new Set)).add(u));for(g=0;g<s;g+=1){if(b=f[g],S=a(b,g),u=v.get(S).e,r.outrogroups!==null)for(const k of r.outrogroups)k.pending.delete(u),k.done.delete(u);if((u.f&y)!==0&&(hr(u),n&&((G=(C=u.nodes)==null?void 0:C.a)==null||G.unfix(),(p??(p=new Set)).delete(u))),(u.f&L)!==0)if(u.f^=L,u===l)F(u,null,e);else{var w=o?o.next:l;u===r.effect.last&&(r.effect.last=u.prev),u.prev&&(u.prev.next=u.next),u.next&&(u.next.prev=u.prev),R(r,o,u),R(r,u,w),F(u,w,e),o=u,t=[],N=[],l=H(o.next);continue}if(u!==l){if(d!==void 0&&d.has(u)){if(t.length<N.length){var A=N[0],_;o=A.prev;var D=t[0],h=t[t.length-1];for(_=0;_<t.length;_+=1)F(t[_],A,e);for(_=0;_<N.length;_+=1)d.delete(N[_]);R(r,D.prev,h.next),R(r,o,D),R(r,h,A),l=A,o=h,g-=1,t=[],N=[]}else d.delete(u),F(u,l,e),R(r,u.prev,u.next),R(r,u,o===null?r.effect.first:o.next),R(r,o,u),o=u;continue}for(t=[],N=[];l!==null&&l!==u;)(d??(d=new Set)).add(l),N.push(l),l=H(l.next);if(l===null)continue}(u.f&L)===0&&t.push(u),o=u,l=H(u.next)}if(r.outrogroups!==null){for(const k of r.outrogroups)k.pending.size===0&&(W(r,x(k.done)),(V=r.outrogroups)==null||V.delete(k));r.outrogroups.size===0&&(r.outrogroups=null)}if(l!==null||d!==void 0){var c=[];if(d!==void 0)for(u of d)(u.f&y)===0&&c.push(u);for(;l!==null;)(l.f&y)===0&&l!==r.fallback&&c.push(l),l=H(l.next);var T=c.length;if(T>0){var I=(i&dr)!==0&&s===0?e:null;if(n){for(g=0;g<T;g+=1)(er=(rr=c[g].nodes)==null?void 0:rr.a)==null||er.measure();for(g=0;g<T;g+=1)(ir=(fr=c[g].nodes)==null?void 0:fr.a)==null||ir.fix()}oe(r,c,I)}}n&&_r(()=>{var k,ar;if(p!==void 0)for(u of p)(ar=(k=u.nodes)==null?void 0:k.a)==null||ar.apply()})}function ve(r,f,e,i,a,n,s,v){var l=(s&zr)!==0?(s&Ur)===0?Gr(e,!1,!1):sr(e):null,d=(s&Vr)!==0?sr(a):null;return{v:l,i:d,e:U(()=>(n(f,l??e,d??a,v),()=>{r.delete(i)}))}}function F(r,f,e){if(r.nodes)for(var i=r.nodes.start,a=r.nodes.end,n=f&&(f.f&L)===0?f.nodes.start:e;i!==null;){var s=qr(i);if(n.before(i),i===a)return;i=s}}function R(r,f,e){f===null?r.effect.first=e:f.next=e,e===null?r.effect.last=f:e.prev=f}function de(r,f){var e=void 0,i;br(()=>{e!==(e=f())&&(i&&($(i),i=null),e&&(i=U(()=>{Ar(()=>e(r))})))})}function Er(r){var f,e,i="";if(typeof r=="string"||typeof r=="number")i+=r;else if(typeof r=="object")if(Array.isArray(r)){var a=r.length;for(f=0;f<a;f++)r[f]&&(e=Er(r[f]))&&(i&&(i+=" "),i+=e)}else for(e in r)r[e]&&(i&&(i+=" "),i+=e);return i}function ge(){for(var r,f,e=0,i="",a=arguments.length;e<a;e++)(r=arguments[e])&&(f=Er(r))&&(i&&(i+=" "),i+=f);return i}function he(r){return typeof r=="object"?ge(r):r??""}const lr=[...`
|
| 2 |
+
\r\f \v\uFEFF`];function pe(r,f,e){var i=r==null?"":""+r;if(f&&(i=i?i+" "+f:f),e){for(var a of Object.keys(e))if(e[a])i=i?i+" "+a:a;else if(i.length)for(var n=a.length,s=0;(s=i.indexOf(a,s))>=0;){var v=s+n;(s===0||lr.includes(i[s-1]))&&(v===i.length||lr.includes(i[v]))?i=(s===0?"":i.substring(0,s))+i.substring(v+1):s=v}}return i===""?null:i}function or(r,f=!1){var e=f?" !important;":";",i="";for(var a of Object.keys(r)){var n=r[a];n!=null&&n!==""&&(i+=" "+a+": "+n+e)}return i}function J(r){return r[0]!=="-"||r[1]!=="-"?r.toLowerCase():r}function _e(r,f){if(f){var e="",i,a;if(Array.isArray(f)?(i=f[0],a=f[1]):i=f,r){r=String(r).replaceAll(/\s*\/\*.*?\*\/\s*/g,"").trim();var n=!1,s=0,v=!1,l=[];i&&l.push(...Object.keys(i).map(J)),a&&l.push(...Object.keys(a).map(J));var d=0,o=-1;const S=r.length;for(var p=0;p<S;p++){var t=r[p];if(v?t==="/"&&r[p-1]==="*"&&(v=!1):n?n===t&&(n=!1):t==="/"&&r[p+1]==="*"?v=!0:t==='"'||t==="'"?n=t:t==="("?s++:t===")"&&s--,!v&&n===!1&&s===0){if(t===":"&&o===-1)o=p;else if(t===";"||p===S-1){if(o!==-1){var N=J(r.substring(d,o).trim());if(!l.includes(N)){t!==";"&&p++;var b=r.substring(d,p).trim();e+=" "+b+";"}}d=p+1,o=-1}}}}return i&&(e+=or(i)),a&&(e+=or(a,!0)),e=e.trim(),e===""?null:e}return r==null?null:String(r)}function be(r,f,e,i,a,n){var s=r.__className;if(O||s!==e||s===void 0){var v=pe(e,i,n);(!O||v!==r.getAttribute("class"))&&(v==null?r.removeAttribute("class"):f?r.className=v:r.setAttribute("class",v)),r.__className=e}else if(n&&a!==n)for(var l in n){var d=!!n[l];(a==null||d!==!!a[l])&&r.classList.toggle(l,d)}return n}function Q(r,f={},e,i){for(var a in e){var n=e[a];f[a]!==n&&(e[a]==null?r.style.removeProperty(a):r.style.setProperty(a,n,i))}}function Ae(r,f,e,i){var a=r.__style;if(O||a!==f){var n=_e(f,i);(!O||n!==r.getAttribute("style"))&&(n==null?r.removeAttribute("style"):r.style.cssText=n),r.__style=f}else i&&(Array.isArray(i)?(Q(r,e==null?void 0:e[0],i[0]),Q(r,e==null?void 0:e[1],i[1],"important")):Q(r,e,i));return i}function m(r,f,e=!1){if(r.multiple){if(f==null)return;if(!gr(f))return Xr();for(var i of r.options)i.selected=f.includes(cr(i));return}for(i of r.options){var a=cr(i);if(yr(a,f)){i.selected=!0;return}}(!e||f!==void 0)&&(r.selectedIndex=-1)}function Ee(r){var f=new MutationObserver(()=>{m(r,r.__value)});f.observe(r,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["value"]}),Jr(()=>{f.disconnect()})}function cr(r){return"__value"in r?r.__value:r.value}const B=Symbol("class"),P=Symbol("style"),Tr=Symbol("is custom element"),Nr=Symbol("is html"),Te=j?"link":"LINK",Ne=j?"input":"INPUT",Se=j?"option":"OPTION",Ie=j?"select":"SELECT";function Ce(r){if(O){var f=!1,e=()=>{if(!f){if(f=!0,r.hasAttribute("value")){var i=r.value;K(r,"value",null),r.value=i}if(r.hasAttribute("checked")){var a=r.checked;K(r,"checked",null),r.checked=a}}};r.__on_r=e,_r(e),Qr()}}function Oe(r,f){f?r.hasAttribute("selected")||r.setAttribute("selected",""):r.removeAttribute("selected")}function K(r,f,e,i){var a=Sr(r);O&&(a[f]=r.getAttribute(f),f==="src"||f==="srcset"||f==="href"&&r.nodeName===Te)||a[f]!==(a[f]=e)&&(f==="loading"&&(r[mr]=e),e==null?r.removeAttribute(f):typeof e!="string"&&Ir(r).includes(f)?r[f]=e:r.setAttribute(f,e))}function we(r,f,e,i,a=!1,n=!1){if(O&&a&&r.nodeName===Ne){var s=r,v=s.type==="checkbox"?"defaultChecked":"defaultValue";v in e||Ce(s)}var l=Sr(r),d=l[Tr],o=!l[Nr];let p=O&&d;p&&z(!1);var t=f||{},N=r.nodeName===Se;for(var b in f)b in e||(e[b]=null);e.class?e.class=he(e.class):e.class=null,e[P]&&(e.style??(e.style=null));var S=Ir(r);for(const h in e){let c=e[h];if(N&&h==="value"&&c==null){r.value=r.__value="",t[h]=c;continue}if(h==="class"){var u=r.namespaceURI==="http://www.w3.org/1999/xhtml";be(r,u,c,i,f==null?void 0:f[B],e[B]),t[h]=c,t[B]=e[B];continue}if(h==="style"){Ae(r,c,f==null?void 0:f[P],e[P]),t[h]=c,t[P]=e[P];continue}var g=t[h];if(!(c===g&&!(c===void 0&&r.hasAttribute(h)))){t[h]=c;var w=h[0]+h[1];if(w!=="$$")if(w==="on"){const T={},I="$$"+h;let E=h.slice(2);var A=le(E);if(ae(E)&&(E=E.slice(0,-7),T.capture=!0),!A&&g){if(c!=null)continue;r.removeEventListener(E,t[I],T),t[I]=null}if(A)ne(E,r,c),ue([E]);else if(c!=null){let M=function(C){t[h].call(this,C)};t[I]=se(E,r,M,T)}}else if(h==="style")K(r,h,c);else if(h==="autofocus")fe(r,!!c);else if(!d&&(h==="__value"||h==="value"&&c!=null))r.value=r.__value=c;else if(h==="selected"&&N)Oe(r,c);else{var _=h;o||(_=te(_));var D=_==="defaultValue"||_==="defaultChecked";if(c==null&&!d&&!D)if(l[h]=null,_==="value"||_==="checked"){let T=r;const I=f===void 0;if(_==="value"){let E=T.defaultValue;T.removeAttribute(_),T.defaultValue=E,T.value=T.__value=I?E:null}else{let E=T.defaultChecked;T.removeAttribute(_),T.defaultChecked=E,T.checked=I?E:!1}}else r.removeAttribute(h);else D||S.includes(_)&&(d||typeof c!="string")?(r[_]=c,_ in l&&(l[_]=ie)):typeof c!="function"&&K(r,_,c)}}}return p&&z(!0),t}function De(r,f,e=[],i=[],a=[],n,s=!1,v=!1){re(a,e,i,l=>{var d=void 0,o={},p=r.nodeName===Ie,t=!1;if(br(()=>{var b=f(...l.map(Z)),S=we(r,d,b,n,s,v);t&&p&&"value"in b&&m(r,b.value);for(let g of Object.getOwnPropertySymbols(o))b[g]||$(o[g]);for(let g of Object.getOwnPropertySymbols(b)){var u=b[g];g.description===ee&&(!d||u!==d[g])&&(o[g]&&$(o[g]),o[g]=U(()=>de(r,()=>u))),S[g]=u}d=S}),p){var N=r;Ar(()=>{m(N,d.value,!0),Ee(N)})}t=!0})}function Sr(r){return r.__attributes??(r.__attributes={[Tr]:r.nodeName.includes("-"),[Nr]:r.namespaceURI===Zr})}var vr=new Map;function Ir(r){var f=r.getAttribute("is")||r.nodeName,e=vr.get(f);if(e)return e;vr.set(f,e=[]);for(var i,a=r,n=Element.prototype;n!==a;){i=xr(a);for(var s in i)i[s].set&&e.push(s);a=Wr(a)}return e}export{B as C,be as a,Ae as b,De as c,Re as e,ke as i,Ce as r,K as s};
|
web/sveltekit/build/_app/immutable/chunks/CpEmpa3I.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
import{e}from"./CYuHyzh3.js";e();
|
|
|
|
|
|
web/sveltekit/build/_app/immutable/chunks/{DwPgZwgo.js → DM0dVcpA.js}
RENAMED
|
@@ -1 +1 @@
|
|
| 1 |
-
import{
|
|
|
|
| 1 |
+
import{m as v,n as p,o as y,u as k,q as P,D as R,S as L}from"./Dr93mMbz.js";function w(n,i){return n===i||(n==null?void 0:n[L])===i}function A(n={},i,a,E){var u=v.r,h=P;return p(()=>{var s,e;return y(()=>{s=e,e=[],k(()=>{n!==a(...e)&&(i(n,...e),s&&w(a(...s),n)&&i(null,...s))})}),()=>{let r=h;for(;r!==u&&r.parent!==null&&r.parent.f&R;)r=r.parent;const d=()=>{e&&w(a(...e),n)&&i(null,...e)},t=r.teardown;r.teardown=()=>{d(),t==null||t()}}}),n}const g="modulepreload",B=function(n,i){return new URL(n,i).href},S={},C=function(i,a,E){let u=Promise.resolve();if(a&&a.length>0){let s=function(t){return Promise.all(t.map(c=>Promise.resolve(c).then(l=>({status:"fulfilled",value:l}),l=>({status:"rejected",reason:l}))))};const e=document.getElementsByTagName("link"),r=document.querySelector("meta[property=csp-nonce]"),d=(r==null?void 0:r.nonce)||(r==null?void 0:r.getAttribute("nonce"));u=s(a.map(t=>{if(t=B(t,E),t in S)return;S[t]=!0;const c=t.endsWith(".css"),l=c?'[rel="stylesheet"]':"";if(!!E)for(let f=e.length-1;f>=0;f--){const m=e[f];if(m.href===t&&(!c||m.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${t}"]${l}`))return;const o=document.createElement("link");if(o.rel=c?"stylesheet":g,c||(o.as="script"),o.crossOrigin="",o.href=t,d&&o.setAttribute("nonce",d),document.head.appendChild(o),c)return new Promise((f,m)=>{o.addEventListener("load",f),o.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${t}`)))})}))}function h(s){const e=new Event("vite:preloadError",{cancelable:!0});if(e.payload=s,window.dispatchEvent(e),!e.defaultPrevented)throw s}return u.then(s=>{for(const e of s||[])e.status==="rejected"&&h(e.reason);return i().catch(h)})};export{C as _,A as b};
|
web/sveltekit/build/_app/immutable/chunks/DOnKrFbX.js
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
var rt=e=>{throw TypeError(e)};var Dt=(e,t,n)=>t.has(e)||rt("Cannot "+n);var v=(e,t,n)=>(Dt(e,t,"read from private field"),n?n.call(e):t.get(e)),A=(e,t,n)=>t.has(e)?rt("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n);import{b4 as Pe,b5 as Vt,af as at,j as T,k as I,l as O,ab as we,b6 as Bt}from"./CYuHyzh3.js";const M=[];function Ke(e,t=Pe){let n=null;const a=new Set;function r(o){if(Vt(e,o)&&(e=o,n)){const l=!M.length;for(const c of a)c[1](),M.push(c,e);if(l){for(let c=0;c<M.length;c+=2)M[c][0](M[c+1]);M.length=0}}}function i(o){r(o(e))}function s(o,l=Pe){const c=[o,l];return a.add(c),a.size===1&&(n=t(r,i)||Pe),o(e),()=>{a.delete(c),a.size===0&&n&&(n(),n=null)}}return{set:r,update:i,subscribe:s}}class Me{constructor(t,n){this.status=t,typeof n=="string"?this.body={message:n}:n?this.body=n:this.body={message:`Error: ${t}`}}toString(){return JSON.stringify(this.body)}}class ze{constructor(t,n){try{new Headers({location:n})}catch{throw new Error(`Invalid redirect location ${JSON.stringify(n)}: this string contains characters that cannot be used in HTTP headers`)}this.status=t,this.location=n}}class Fe extends Error{constructor(t,n,a){super(a),this.status=t,this.text=n}}new URL("sveltekit-internal://");function Kt(e,t){return e==="/"||t==="ignore"?e:t==="never"?e.endsWith("/")?e.slice(0,-1):e:t==="always"&&!e.endsWith("/")?e+"/":e}function Mt(e){return e.split("%25").map(decodeURI).join("%25")}function zt(e){for(const t in e)e[t]=decodeURIComponent(e[t]);return e}function $e({href:e}){return e.split("#")[0]}function j(){}function Ft(...e){let t=5381;for(const n of e)if(typeof n=="string"){let a=n.length;for(;a;)t=t*33^n.charCodeAt(--a)}else if(ArrayBuffer.isView(n)){const a=new Uint8Array(n.buffer,n.byteOffset,n.byteLength);let r=a.length;for(;r;)t=t*33^a[--r]}else throw new TypeError("value must be a string or TypedArray");return(t>>>0).toString(36)}new TextEncoder;function Gt(e){const t=atob(e),n=new Uint8Array(t.length);for(let a=0;a<t.length;a++)n[a]=t.charCodeAt(a);return n}const Ht=window.fetch;window.fetch=(e,t)=>((e instanceof Request?e.method:(t==null?void 0:t.method)||"GET")!=="GET"&&X.delete(Ge(e)),Ht(e,t));const X=new Map;function Wt(e,t){const n=Ge(e,t),a=document.querySelector(n);if(a!=null&&a.textContent){a.remove();let{body:r,...i}=JSON.parse(a.textContent);const s=a.getAttribute("data-ttl");return s&&X.set(n,{body:r,init:i,ttl:1e3*Number(s)}),a.getAttribute("data-b64")!==null&&(r=Gt(r)),Promise.resolve(new Response(r,i))}return window.fetch(e,t)}function Jt(e,t,n){if(X.size>0){const a=Ge(e,n),r=X.get(a);if(r){if(performance.now()<r.ttl&&["default","force-cache","only-if-cached",void 0].includes(n==null?void 0:n.cache))return new Response(r.body,r.init);X.delete(a)}}return window.fetch(t,n)}function Ge(e,t){let a=`script[data-sveltekit-fetched][data-url=${JSON.stringify(e instanceof Request?e.url:e)}]`;if(t!=null&&t.headers||t!=null&&t.body){const r=[];t.headers&&r.push([...new Headers(t.headers)].join(",")),t.body&&(typeof t.body=="string"||ArrayBuffer.isView(t.body))&&r.push(t.body),a+=`[data-hash="${Ft(...r)}"]`}return a}const Yt=/^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;function Xt(e){const t=[];return{pattern:e==="/"?/^\/$/:new RegExp(`^${Zt(e).map(a=>{const r=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(a);if(r)return t.push({name:r[1],matcher:r[2],optional:!1,rest:!0,chained:!0}),"(?:/([^]*))?";const i=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(a);if(i)return t.push({name:i[1],matcher:i[2],optional:!0,rest:!1,chained:!0}),"(?:/([^/]+))?";if(!a)return;const s=a.split(/\[(.+?)\](?!\])/);return"/"+s.map((l,c)=>{if(c%2){if(l.startsWith("x+"))return je(String.fromCharCode(parseInt(l.slice(2),16)));if(l.startsWith("u+"))return je(String.fromCharCode(...l.slice(2).split("-").map(m=>parseInt(m,16))));const d=Yt.exec(l),[,u,w,p,f]=d;return t.push({name:p,matcher:f,optional:!!u,rest:!!w,chained:w?c===1&&s[0]==="":!1}),w?"([^]*?)":u?"([^/]*)?":"([^/]+?)"}return je(l)}).join("")}).join("")}/?$`),params:t}}function Qt(e){return e!==""&&!/^\([^)]+\)$/.test(e)}function Zt(e){return e.slice(1).split("/").filter(Qt)}function en(e,t,n){const a={},r=e.slice(1),i=r.filter(o=>o!==void 0);let s=0;for(let o=0;o<t.length;o+=1){const l=t[o];let c=r[o-s];if(l.chained&&l.rest&&s&&(c=r.slice(o-s,o+1).filter(d=>d).join("/"),s=0),c===void 0)if(l.rest)c="";else continue;if(!l.matcher||n[l.matcher](c)){a[l.name]=c;const d=t[o+1],u=r[o+1];d&&!d.rest&&d.optional&&u&&l.chained&&(s=0),!d&&!u&&Object.keys(a).length===i.length&&(s=0);continue}if(l.optional&&l.chained){s++;continue}return}if(!s)return a}function je(e){return e.normalize().replace(/[[\]]/g,"\\$&").replace(/%/g,"%25").replace(/\//g,"%2[Ff]").replace(/\?/g,"%3[Ff]").replace(/#/g,"%23").replace(/[.*+?^${}()|\\]/g,"\\$&")}function tn({nodes:e,server_loads:t,dictionary:n,matchers:a}){const r=new Set(t);return Object.entries(n).map(([o,[l,c,d]])=>{const{pattern:u,params:w}=Xt(o),p={id:o,exec:f=>{const m=u.exec(f);if(m)return en(m,w,a)},errors:[1,...d||[]].map(f=>e[f]),layouts:[0,...c||[]].map(s),leaf:i(l)};return p.errors.length=p.layouts.length=Math.max(p.errors.length,p.layouts.length),p});function i(o){const l=o<0;return l&&(o=~o),[l,e[o]]}function s(o){return o===void 0?o:[r.has(o),e[o]]}}function wt(e,t=JSON.parse){try{return t(sessionStorage[e])}catch{}}function ot(e,t,n=JSON.stringify){const a=n(t);try{sessionStorage[e]=a}catch{}}var ht;const U=((ht=globalThis.__sveltekit_yhur1t)==null?void 0:ht.base)??"";var pt;const nn=((pt=globalThis.__sveltekit_yhur1t)==null?void 0:pt.assets)??U??"",rn="1777858715422",yt="sveltekit:snapshot",vt="sveltekit:scroll",bt="sveltekit:states",an="sveltekit:pageurl",F="sveltekit:history",Z="sveltekit:navigation",D={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},Ue=location.origin;function He(e){if(e instanceof URL)return e;let t=document.baseURI;if(!t){const n=document.getElementsByTagName("base");t=n.length?n[0].href:document.URL}return new URL(e,t)}function B(){return{x:pageXOffset,y:pageYOffset}}function z(e,t){return e.getAttribute(`data-sveltekit-${t}`)}const st={...D,"":D.hover};function kt(e){let t=e.assignedSlot??e.parentNode;return(t==null?void 0:t.nodeType)===11&&(t=t.host),t}function St(e,t){for(;e&&e!==t;){if(e.nodeName.toUpperCase()==="A"&&e.hasAttribute("href"))return e;e=kt(e)}}function qe(e,t,n){let a;try{if(a=new URL(e instanceof SVGAElement?e.href.baseVal:e.href,document.baseURI),n&&a.hash.match(/^#[^/]/)){const o=location.hash.split("#")[1]||"/";a.hash=`#${o}${a.hash}`}}catch{}const r=e instanceof SVGAElement?e.target.baseVal:e.target,i=!a||!!r||Ae(a,t,n)||(e.getAttribute("rel")||"").split(/\s+/).includes("external"),s=(a==null?void 0:a.origin)===Ue&&e.hasAttribute("download");return{url:a,external:i,target:r,download:s}}function ye(e){let t=null,n=null,a=null,r=null,i=null,s=null,o=e;for(;o&&o!==document.documentElement;)a===null&&(a=z(o,"preload-code")),r===null&&(r=z(o,"preload-data")),t===null&&(t=z(o,"keepfocus")),n===null&&(n=z(o,"noscroll")),i===null&&(i=z(o,"reload")),s===null&&(s=z(o,"replacestate")),o=kt(o);function l(c){switch(c){case"":case"true":return!0;case"off":case"false":return!1;default:return}}return{preload_code:st[a??"off"],preload_data:st[r??"off"],keepfocus:l(t),noscroll:l(n),reload:l(i),replace_state:l(s)}}function it(e){const t=Ke(e);let n=!0;function a(){n=!0,t.update(s=>s)}function r(s){n=!1,t.set(s)}function i(s){let o;return t.subscribe(l=>{(o===void 0||n&&l!==o)&&s(o=l)})}return{notify:a,set:r,subscribe:i}}const Et={v:j};function on(){const{set:e,subscribe:t}=Ke(!1);let n;async function a(){clearTimeout(n);try{const r=await fetch(`${nn}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(!r.ok)return!1;const s=(await r.json()).version!==rn;return s&&(e(!0),Et.v(),clearTimeout(n)),s}catch{return!1}}return{subscribe:t,check:a}}function Ae(e,t,n){return e.origin!==Ue||!e.pathname.startsWith(t)?!0:n?e.pathname!==location.pathname:!1}function Pn(e){}const Rt=new Set(["load","prerender","csr","ssr","trailingSlash","config"]);[...Rt];const sn=new Set([...Rt]);[...sn];function ln(e){return e.filter(t=>t!=null)}function me(e,t){return e+"/"+t}function We(e){return e instanceof Me||e instanceof Fe?e.status:500}function cn(e){return e instanceof Fe?e.text:"Internal Error"}let R,ee,Ce;const fn=at.toString().includes("$$")||/function \w+\(\) \{\}/.test(at.toString()),lt="a:";var oe,se,ie,le,ce,fe,ue,de,gt,he,mt,pe,_t;fn?(R={data:{},form:null,error:null,params:{},route:{id:null},state:{},status:-1,url:new URL(lt)},ee={current:null},Ce={current:!1}):(R=new(gt=class{constructor(){A(this,oe,T({}));A(this,se,T(null));A(this,ie,T(null));A(this,le,T({}));A(this,ce,T({id:null}));A(this,fe,T({}));A(this,ue,T(-1));A(this,de,T(new URL(lt)))}get data(){return I(v(this,oe))}set data(t){O(v(this,oe),t)}get form(){return I(v(this,se))}set form(t){O(v(this,se),t)}get error(){return I(v(this,ie))}set error(t){O(v(this,ie),t)}get params(){return I(v(this,le))}set params(t){O(v(this,le),t)}get route(){return I(v(this,ce))}set route(t){O(v(this,ce),t)}get state(){return I(v(this,fe))}set state(t){O(v(this,fe),t)}get status(){return I(v(this,ue))}set status(t){O(v(this,ue),t)}get url(){return I(v(this,de))}set url(t){O(v(this,de),t)}},oe=new WeakMap,se=new WeakMap,ie=new WeakMap,le=new WeakMap,ce=new WeakMap,fe=new WeakMap,ue=new WeakMap,de=new WeakMap,gt),ee=new(mt=class{constructor(){A(this,he,T(null))}get current(){return I(v(this,he))}set current(t){O(v(this,he),t)}},he=new WeakMap,mt),Ce=new(_t=class{constructor(){A(this,pe,T(!1))}get current(){return I(v(this,pe))}set current(t){O(v(this,pe),t)}},pe=new WeakMap,_t),Et.v=()=>Ce.current=!0);function un(e){Object.assign(R,e)}const dn=new Set(["icon","shortcut icon","apple-touch-icon"]);let J=null;const N=wt(vt)??{},te=wt(yt)??{},C={url:it({}),page:it({}),navigating:Ke(null),updated:on()};function Je(e){N[e]=B()}function hn(e,t){let n=e+1;for(;N[n];)delete N[n],n+=1;for(n=t+1;te[n];)delete te[n],n+=1}function ne(e,t=!1){return t?location.replace(e.href):location.href=e.href,new Promise(j)}async function xt(){if("serviceWorker"in navigator){const e=await navigator.serviceWorker.getRegistration(U||"/");e&&await e.update()}}let Ye,De,ve,P,Ve,S;const be=[],ke=[];let y=null;function Se(){var e;(e=y==null?void 0:y.fork)==null||e.then(t=>t==null?void 0:t.discard()),y=null}const _e=new Map,Lt=new Set,pn=new Set,Q=new Set;let _={branch:[],error:null,url:null},Ut=!1,Ee=!1,ct=!0,re=!1,Y=!1,At=!1,Xe=!1,Tt,k,L,V;const Re=new Set,ft=new Map,ut=new Map;async function Nn(e,t,n){var i,s,o,l;globalThis.__sveltekit_yhur1t&&(globalThis.__sveltekit_yhur1t.query,globalThis.__sveltekit_yhur1t.prerender),document.URL!==location.href&&(location.href=location.href),S=e,await((s=(i=e.hooks).init)==null?void 0:s.call(i)),Ye=tn(e),P=document.documentElement,Ve=t,De=e.nodes[0],ve=e.nodes[1],De(),ve(),k=(o=history.state)==null?void 0:o[F],L=(l=history.state)==null?void 0:l[Z],k||(k=L=Date.now(),history.replaceState({...history.state,[F]:k,[Z]:L},""));const a=N[k];function r(){a&&(history.scrollRestoration="manual",scrollTo(a.x,a.y))}n?(r(),await Ln(Ve,n)):(await G({type:"enter",url:He(S.hash?Tn(new URL(location.href)):location.href),replace_state:!0}),r()),xn()}function gn(){be.length=0,Xe=!1}function It(e){ke.some(t=>t==null?void 0:t.snapshot)&&(te[e]=ke.map(t=>{var n;return(n=t==null?void 0:t.snapshot)==null?void 0:n.capture()}))}function Ot(e){var t;(t=te[e])==null||t.forEach((n,a)=>{var r,i;(i=(r=ke[a])==null?void 0:r.snapshot)==null||i.restore(n)})}function dt(){Je(k),ot(vt,N),It(L),ot(yt,te)}async function Pt(e,t,n,a){let r,i;t.invalidateAll&&Se(),await G({type:"goto",url:He(e),keepfocus:t.keepFocus,noscroll:t.noScroll,replace_state:t.replaceState,state:t.state,redirect_count:n,nav_token:a,accept:()=>{if(t.invalidateAll){Xe=!0,r=new Set;for(const[s,o]of ft)for(const l of o.keys())r.add(me(s,l));i=new Set;for(const[s,o]of ut)for(const l of o.keys())i.add(me(s,l))}t.invalidate&&t.invalidate.forEach(Rn)}}),t.invalidateAll&&we().then(we).then(()=>{for(const[s,o]of ft)for(const[l,{resource:c}]of o)r!=null&&r.has(me(s,l))&&c.refresh();for(const[s,o]of ut)for(const[l,{resource:c}]of o)i!=null&&i.has(me(s,l))&&c.reconnect()})}async function mn(e){if(e.id!==(y==null?void 0:y.id)){Se();const t={};Re.add(t),y={id:e.id,token:t,promise:jt({...e,preload:t}).then(n=>(Re.delete(t),n.type==="loaded"&&n.state.error&&Se(),n)),fork:null}}return y.promise}async function Ne(e){var n;const t=(n=await Te(e,!1))==null?void 0:n.route;t&&await Promise.all([...t.layouts,t.leaf].filter(Boolean).map(a=>a[1]()))}async function $t(e,t,n){var i;const a={params:_.params,route:{id:((i=_.route)==null?void 0:i.id)??null},url:new URL(location.href)};_={...e.state,nav:a};const r=document.querySelector("style[data-sveltekit]");if(r&&r.remove(),Object.assign(R,e.props.page),Tt=new S.root({target:t,props:{...e.props,stores:C,components:ke},hydrate:n,sync:!1,transformError:void 0}),await Promise.resolve(),Ot(L),n){const s={from:null,to:{...a,scroll:N[k]??B()},willUnload:!1,type:"enter",complete:Promise.resolve()};Q.forEach(o=>o(s))}Ee=!0}async function xe({url:e,params:t,branch:n,errors:a,status:r,error:i,route:s,form:o}){let l="never";if(U&&(e.pathname===U||e.pathname===U+"/"))l="always";else for(const f of n)(f==null?void 0:f.slash)!==void 0&&(l=f.slash);e.pathname=Kt(e.pathname,l),e.search=e.search;const c={type:"loaded",state:{url:e,params:t,branch:n,error:i,route:s},props:{constructors:ln(n).map(f=>f.node.component),page:nt(R)}};o!==void 0&&(c.props.form=o);let d={},u=!R,w=0;for(let f=0;f<Math.max(n.length,_.branch.length);f+=1){const m=n[f],h=_.branch[f];(m==null?void 0:m.data)!==(h==null?void 0:h.data)&&(u=!0),m&&(d={...d,...m.data},u&&(c.props[`data_${w}`]=d),w+=1)}return(!_.url||e.href!==_.url.href||_.error!==i||o!==void 0&&o!==R.form||u)&&(c.props.page={error:i,params:t,route:{id:(s==null?void 0:s.id)??null},state:{},status:r,url:new URL(e),form:o??null,data:u?d:R.data}),c}async function Qe({loader:e,parent:t,url:n,params:a,route:r,server_data_node:i}){var c,d;let s=null;const o={dependencies:new Set,params:new Set,parent:!1,route:!1,url:!1,search_params:new Set},l=await e();return{node:l,loader:e,server:i,universal:(c=l.universal)!=null&&c.load?{type:"data",data:s,uses:o}:null,data:s??(i==null?void 0:i.data)??null,slash:((d=l.universal)==null?void 0:d.trailingSlash)??(i==null?void 0:i.slash)}}function _n(e,t,n){let a=e instanceof Request?e.url:e;const r=new URL(a,n);r.origin===n.origin&&(a=r.href.slice(n.origin.length));const i=Ee?Jt(a,r.href,t):Wt(a,t);return{resolved:r,promise:i}}function wn(e,t,n,a,r,i){if(Xe)return!0;if(!r)return!1;if(r.parent&&e||r.route&&t||r.url&&n)return!0;for(const s of r.search_params)if(a.has(s))return!0;for(const s of r.params)if(i[s]!==_.params[s])return!0;for(const s of r.dependencies)if(be.some(o=>o(new URL(s))))return!0;return!1}function Ze(e,t){return(e==null?void 0:e.type)==="data"?e:(e==null?void 0:e.type)==="skip"?t??null:null}function yn(e,t){if(!e)return new Set(t.searchParams.keys());const n=new Set([...e.searchParams.keys(),...t.searchParams.keys()]);for(const a of n){const r=e.searchParams.getAll(a),i=t.searchParams.getAll(a);r.every(s=>i.includes(s))&&i.every(s=>r.includes(s))&&n.delete(a)}return n}function vn({error:e,url:t,route:n,params:a}){return{type:"loaded",state:{error:e,url:t,route:n,params:a,branch:[]},props:{page:nt(R),constructors:[]}}}async function jt({id:e,invalidating:t,url:n,params:a,route:r,preload:i}){if((y==null?void 0:y.id)===e)return Re.delete(y.token),y.promise;const{errors:s,layouts:o,leaf:l}=r,c=[...o,l];s.forEach(h=>h==null?void 0:h().catch(j)),c.forEach(h=>h==null?void 0:h[1]().catch(j));const d=_.url?e!==Le(_.url):!1,u=_.route?r.id!==_.route.id:!1,w=yn(_.url,n);let p=!1;const f=c.map(async(h,g)=>{var $;if(!h)return;const b=_.branch[g];return h[1]===(b==null?void 0:b.loader)&&!wn(p,u,d,w,($=b.universal)==null?void 0:$.uses,a)?b:(p=!0,Qe({loader:h[1],url:n,params:a,route:r,parent:async()=>{var ge;const q={};for(let K=0;K<g;K+=1)Object.assign(q,(ge=await f[K])==null?void 0:ge.data);return q},server_data_node:Ze(h[0]?{type:"skip"}:null,h[0]?b==null?void 0:b.server:void 0)}))});for(const h of f)h.catch(j);const m=[];for(let h=0;h<c.length;h+=1)if(c[h])try{m.push(await f[h])}catch(g){if(g instanceof ze)return{type:"redirect",location:g.location};if(Re.has(i))return vn({error:await ae(g,{params:a,url:n,route:{id:r.id}}),url:n,params:a,route:r});let b=We(g),x;if(g instanceof Me)x=g.body;else{if(await C.updated.check())return await xt(),await ne(n);x=await ae(g,{params:a,url:n,route:{id:r.id}})}const $=await bn(h,m,s);return $?xe({url:n,params:a,branch:m.slice(0,$.idx).concat($.node),errors:s,status:b,error:x,route:r}):await Nt(n,{id:r.id},x,b)}else m.push(void 0);return xe({url:n,params:a,branch:m,errors:s,status:200,error:null,route:r,form:t?void 0:null})}async function bn(e,t,n){for(;e--;)if(n[e]){let a=e;for(;!t[a];)a-=1;try{return{idx:a+1,node:{node:await n[e](),loader:n[e],data:{},server:null,universal:null}}}catch{continue}}}async function et({status:e,error:t,url:n,route:a}){const r={};let i=null;try{const s=await Qe({loader:De,url:n,params:r,route:a,parent:()=>Promise.resolve({}),server_data_node:Ze(i)}),o={node:await ve(),loader:ve,universal:null,server:null,data:null};return xe({url:n,params:r,branch:[s,o],status:e,error:t,errors:[],route:null})}catch(s){if(s instanceof ze)return Pt(new URL(s.location,location.href),{},0);throw s}}async function kn(e){const t=e.href;if(_e.has(t))return _e.get(t);let n;try{const a=(async()=>{let r=await S.hooks.reroute({url:new URL(e),fetch:async(i,s)=>_n(i,s,e).promise})??e;if(typeof r=="string"){const i=new URL(e);S.hash?i.hash=r:i.pathname=r,r=i}return r})();_e.set(t,a),n=await a}catch{_e.delete(t);return}return n}async function Te(e,t){if(e&&!Ae(e,U,S.hash)){const n=await kn(e);if(!n)return;const a=Sn(n);for(const r of Ye){const i=r.exec(a);if(i)return{id:Le(e),invalidating:t,route:r,params:zt(i),url:e}}}}function Sn(e){return Mt(S.hash?e.hash.replace(/^#/,"").replace(/[?#].+/,""):e.pathname.slice(U.length))||"/"}function Le(e){return(S.hash?e.hash.replace(/^#/,""):e.pathname)+e.search}function Ct({url:e,type:t,intent:n,delta:a,event:r,scroll:i}){let s=!1;const o=tt(_,n,e,t,i??null);a!==void 0&&(o.navigation.delta=a),r!==void 0&&(o.navigation.event=r);const l={...o.navigation,cancel:()=>{s=!0,o.reject(new Error("navigation cancelled"))}};return re||Lt.forEach(c=>c(l)),s?null:o}async function G({type:e,url:t,popped:n,keepfocus:a,noscroll:r,replace_state:i,state:s={},redirect_count:o=0,nav_token:l={},accept:c=j,block:d=j,event:u}){var K;const w=V;V=l;const p=await Te(t,!1),f=e==="enter"?tt(_,p,t,e):Ct({url:t,type:e,delta:n==null?void 0:n.delta,intent:p,scroll:n==null?void 0:n.scroll,event:u});if(!f){d(),V===l&&(V=w);return}const m=k,h=L;c(),re=!0,Ee&&f.navigation.type!=="enter"&&C.navigating.set(ee.current=f.navigation);let g=p&&await jt(p);if(!g){if(Ae(t,U,S.hash))return await ne(t,i);g=await Nt(t,{id:null},await ae(new Fe(404,"Not Found",`Not found: ${t.pathname}`),{url:t,params:{},route:{id:null}}),404,i)}if(t=(p==null?void 0:p.url)||t,V!==l)return f.reject(new Error("navigation aborted")),!1;if(g.type==="redirect"){if(o<20){await G({type:e,url:new URL(g.location,t),popped:n,keepfocus:a,noscroll:r,replace_state:i,state:s,redirect_count:o+1,nav_token:l}),f.fulfil(void 0);return}g=await et({status:500,error:await ae(new Error("Redirect loop"),{url:t,params:{},route:{id:null}}),url:t,route:{id:null}})}else g.props.page.status>=400&&await C.updated.check()&&(await xt(),await ne(t,i));if(gn(),Je(m),It(h),g.props.page.url.pathname!==t.pathname&&(t.pathname=g.props.page.url.pathname),s=n?n.state:s,!n){const E=i?0:1,H={[F]:k+=E,[Z]:L+=E,[bt]:s};(i?history.replaceState:history.pushState).call(history,H,"",t),i||hn(k,L)}const b=p&&(y==null?void 0:y.id)===p.id?y.fork:null;y!=null&&y.fork&&!b&&Se(),y=null,g.props.page.state=s;let x;if(Ee){const E=(await Promise.all(Array.from(pn,W=>W(f.navigation)))).filter(W=>typeof W=="function");if(E.length>0){let W=function(){E.forEach(Oe=>{Q.delete(Oe)})};E.push(W),E.forEach(Oe=>{Q.add(Oe)})}const H=f.navigation.to;_={...g.state,nav:{params:H.params,route:H.route,url:H.url}},g.props.page&&(g.props.page.url=t);const Ie=b&&await b;Ie?x=Ie.commit():(J=null,Tt.$set(g.props),J&&Object.assign(g.props.page,J),un(g.props.page),x=(K=Bt)==null?void 0:K()),At=!0}else await $t(g,Ve,!1);const{activeElement:$}=document;await x,await we(),await we();let q=null;if(ct){const E=n?n.scroll:r?B():null;E?scrollTo(E.x,E.y):(q=t.hash&&document.getElementById(qt(t)))?q.scrollIntoView():scrollTo(0,0)}const ge=document.activeElement!==$&&document.activeElement!==document.body;!a&&!ge&&An(t,!q),ct=!0,g.props.page&&(J&&Object.assign(g.props.page,J),Object.assign(R,g.props.page)),re=!1,e==="popstate"&&Ot(L),f.fulfil(void 0),f.navigation.to&&(f.navigation.to.scroll=B()),Q.forEach(E=>E(f.navigation)),C.navigating.set(ee.current=null)}async function Nt(e,t,n,a,r){return e.origin===Ue&&e.pathname===location.pathname&&!Ut?await et({status:a,error:n,url:e,route:t}):await ne(e,r)}function En(){let e,t={element:void 0,href:void 0},n;P.addEventListener("mousemove",o=>{const l=o.target;clearTimeout(e),e=setTimeout(()=>{i(l,D.hover)},20)});function a(o){o.defaultPrevented||i(o.composedPath()[0],D.tap)}P.addEventListener("mousedown",a),P.addEventListener("touchstart",a,{passive:!0});const r=new IntersectionObserver(o=>{for(const l of o)l.isIntersecting&&(Ne(new URL(l.target.href)),r.unobserve(l.target))},{threshold:0});async function i(o,l){const c=St(o,P),d=c===t.element&&(c==null?void 0:c.href)===t.href&&l>=n;if(!c||d)return;const{url:u,external:w,download:p}=qe(c,U,S.hash);if(w||p)return;const f=ye(c),m=u&&Le(_.url)===Le(u);if(!(f.reload||m))if(l<=f.preload_data){t={element:c,href:c.href},n=D.tap;const h=await Te(u,!1);if(!h)return;mn(h)}else l<=f.preload_code&&(t={element:c,href:c.href},n=l,Ne(u))}function s(){r.disconnect();for(const o of P.querySelectorAll("a")){const{url:l,external:c,download:d}=qe(o,U,S.hash);if(c||d)continue;const u=ye(o);u.reload||(u.preload_code===D.viewport&&r.observe(o),u.preload_code===D.eager&&Ne(l))}}Q.add(s),s()}function ae(e,t){if(e instanceof Me)return e.body;const n=We(e),a=cn(e);return S.hooks.handleError({error:e,event:t,status:n,message:a})??{message:a}}function qn(e,t={}){return e=new URL(He(e)),e.origin!==Ue?Promise.reject(new Error("goto: invalid URL")):Pt(e,t,0)}function Rn(e){if(typeof e=="function")be.push(e);else{const{href:t}=new URL(e,location.href);be.push(n=>n.href===t)}}function xn(){var t;history.scrollRestoration="manual",addEventListener("beforeunload",n=>{let a=!1;if(dt(),!re){const r=tt(_,void 0,null,"leave"),i={...r.navigation,cancel:()=>{a=!0,r.reject(new Error("navigation cancelled"))}};Lt.forEach(s=>s(i))}a?(n.preventDefault(),n.returnValue=""):history.scrollRestoration="auto"}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&dt()}),(t=navigator.connection)!=null&&t.saveData||En(),P.addEventListener("click",async n=>{if(n.button||n.which!==1||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||n.defaultPrevented)return;const a=St(n.composedPath()[0],P);if(!a)return;const{url:r,external:i,target:s,download:o}=qe(a,U,S.hash);if(!r)return;if(s==="_parent"||s==="_top"){if(window.parent!==window)return}else if(s&&s!=="_self")return;const l=ye(a);if(!(a instanceof SVGAElement)&&r.protocol!==location.protocol&&!(r.protocol==="https:"||r.protocol==="http:")||o)return;const[d,u]=(S.hash?r.hash.replace(/^#/,""):r.href).split("#"),w=d===$e(location);if(i||l.reload&&(!w||!u)){Ct({url:r,type:"link",event:n})?re=!0:n.preventDefault();return}if(u!==void 0&&w){const[,p]=_.url.href.split("#");if(p===u){if(n.preventDefault(),u===""||u==="top"&&a.ownerDocument.getElementById("top")===null)scrollTo({top:0});else{const f=a.ownerDocument.getElementById(decodeURIComponent(u));f&&(f.scrollIntoView(),f.focus())}return}if(Y=!0,Je(k),e(r),!l.replace_state)return;Y=!1}n.preventDefault(),await new Promise(p=>{requestAnimationFrame(()=>{setTimeout(p,0)}),setTimeout(p,100)}),await G({type:"link",url:r,keepfocus:l.keepfocus,noscroll:l.noscroll,replace_state:l.replace_state??r.href===location.href,event:n})}),P.addEventListener("submit",n=>{if(n.defaultPrevented)return;const a=HTMLFormElement.prototype.cloneNode.call(n.target),r=n.submitter;if(((r==null?void 0:r.formTarget)||a.target)==="_blank"||((r==null?void 0:r.formMethod)||a.method)!=="get")return;const o=new URL((r==null?void 0:r.hasAttribute("formaction"))&&(r==null?void 0:r.formAction)||a.action);if(Ae(o,U,!1))return;const l=n.target,c=ye(l);if(c.reload)return;n.preventDefault(),n.stopPropagation();const d=new FormData(l,r);o.search=new URLSearchParams(d).toString(),G({type:"form",url:o,keepfocus:c.keepfocus,noscroll:c.noscroll,replace_state:c.replace_state??o.href===location.href,event:n})}),addEventListener("popstate",async n=>{var a;if(!Be){if((a=n.state)!=null&&a[F]){const r=n.state[F];if(V={},r===k)return;const i=N[r],s=n.state[bt]??{},o=new URL(n.state[an]??location.href),l=n.state[Z],c=_.url?$e(location)===$e(_.url):!1;if(l===L&&(At||c)){s!==R.state&&(R.state=s),e(o),N[k]=B(),i&&scrollTo(i.x,i.y),k=r;return}const u=r-k;await G({type:"popstate",url:o,popped:{state:s,scroll:i,delta:u},accept:()=>{k=r,L=l},block:()=>{history.go(-u)},nav_token:V,event:n})}else if(!Y){const r=new URL(location.href);e(r),S.hash&&location.reload()}}}),addEventListener("hashchange",()=>{Y&&(Y=!1,history.replaceState({...history.state,[F]:++k,[Z]:L},"",location.href))});for(const n of document.querySelectorAll("link"))dn.has(n.rel)&&(n.href=n.href);addEventListener("pageshow",n=>{n.persisted&&C.navigating.set(ee.current=null)});function e(n){_.url=R.url=n,C.page.set(nt(R)),C.page.notify()}}async function Ln(e,{status:t=200,error:n,node_ids:a,params:r,route:i,server_route:s,data:o,form:l}){Ut=!0;const c=new URL(location.href);let d;({params:r={},route:i={id:null}}=await Te(c,!1)||{}),d=Ye.find(({id:p})=>p===i.id);let u,w=!0;try{const p=a.map(async(m,h)=>{const g=o[h];return g!=null&&g.uses&&(g.uses=Un(g.uses)),Qe({loader:S.nodes[m],url:c,params:r,route:i,parent:async()=>{const b={};for(let x=0;x<h;x+=1)Object.assign(b,(await p[x]).data);return b},server_data_node:Ze(g)})}),f=await Promise.all(p);if(d){const m=d.layouts;for(let h=0;h<m.length;h++)m[h]||f.splice(h,0,void 0)}u=await xe({url:c,params:r,branch:f,status:t,error:n,errors:d==null?void 0:d.errors,form:l,route:d??null})}catch(p){if(p instanceof ze){await ne(new URL(p.location,location.href));return}u=await et({status:We(p),error:await ae(p,{url:c,params:r,route:i}),url:c,route:i}),e.textContent="",w=!1}finally{}u.props.page&&(u.props.page.state={}),await $t(u,e,w)}function Un(e){return{dependencies:new Set((e==null?void 0:e.dependencies)??[]),params:new Set((e==null?void 0:e.params)??[]),parent:!!(e!=null&&e.parent),route:!!(e!=null&&e.route),url:!!(e!=null&&e.url),search_params:new Set((e==null?void 0:e.search_params)??[])}}let Be=!1;function An(e,t=!0){const n=document.querySelector("[autofocus]");if(n)n.focus();else{const a=qt(e);if(a&&document.getElementById(a)){const{x:i,y:s}=B();setTimeout(()=>{const o=history.state;Be=!0,location.replace(new URL(`#${a}`,location.href)),history.replaceState(o,"",e),t&&scrollTo(i,s),Be=!1})}else{const i=document.body,s=i.getAttribute("tabindex");i.tabIndex=-1,i.focus({preventScroll:!0,focusVisible:!1}),s!==null?i.setAttribute("tabindex",s):i.removeAttribute("tabindex")}const r=getSelection();if(r&&r.type!=="None"){const i=[];for(let s=0;s<r.rangeCount;s+=1)i.push(r.getRangeAt(s));setTimeout(()=>{if(r.rangeCount===i.length){for(let s=0;s<r.rangeCount;s+=1){const o=i[s],l=r.getRangeAt(s);if(o.commonAncestorContainer!==l.commonAncestorContainer||o.startContainer!==l.startContainer||o.endContainer!==l.endContainer||o.startOffset!==l.startOffset||o.endOffset!==l.endOffset)return}r.removeAllRanges()}})}}}function tt(e,t,n,a,r=null){var c,d;let i,s;const o=new Promise((u,w)=>{i=u,s=w});return o.catch(j),{navigation:{from:{params:e.params,route:{id:((c=e.route)==null?void 0:c.id)??null},url:e.url,scroll:B()},to:n&&{params:(t==null?void 0:t.params)??null,route:{id:((d=t==null?void 0:t.route)==null?void 0:d.id)??null},url:n,scroll:r},willUnload:!t,type:a,complete:o},fulfil:i,reject:s}}function nt(e){return{data:e.data,error:e.error,form:e.form,params:e.params,route:e.route,state:e.state,status:e.status,url:e.url}}function Tn(e){const t=new URL(e);return t.hash=decodeURIComponent(e.hash),t}function qt(e){let t;if(S.hash){const[,,n]=e.hash.split("#",3);t=n??""}else t=e.hash.slice(1);return decodeURIComponent(t)}export{Nn as a,qn as g,Pn as l,R as p,C as s};
|
|
|
|
|
|
web/sveltekit/build/_app/immutable/chunks/Dr93mMbz.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
var bn=Object.defineProperty;var Ot=e=>{throw TypeError(e)};var Tn=(e,t,n)=>t in e?bn(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var se=(e,t,n)=>Tn(e,typeof t!="symbol"?t+"":t,n),st=(e,t,n)=>t.has(e)||Ot("Cannot "+n);var u=(e,t,n)=>(st(e,t,"read from private field"),n?n.call(e):t.get(e)),N=(e,t,n)=>t.has(e)?Ot("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n),ge=(e,t,n,r)=>(st(e,t,"write to private field"),r?r.call(e,n):t.set(e,n),n),I=(e,t,n)=>(st(e,t,"access private method"),n);var An=Array.isArray,Sn=Array.prototype.indexOf,ke=Array.prototype.includes,br=Array.from,Tr=Object.defineProperty,Pe=Object.getOwnPropertyDescriptor,Rn=Object.getOwnPropertyDescriptors,kn=Object.prototype,Nn=Array.prototype,Ht=Object.getPrototypeOf,xt=Object.isExtensible;const On=()=>{};function Ar(e){return e()}function xn(e){for(var t=0;t<e.length;t++)e[t]()}function qt(){var e,t,n=new Promise((r,s)=>{e=r,t=s});return{promise:n,resolve:e,reject:t}}const x=2,de=4,He=8,ht=1<<24,G=16,z=32,ee=64,Dn=128,F=512,b=1024,O=2048,K=4096,L=8192,U=16384,Ee=32768,Dt=1<<25,Ze=65536,ft=1<<17,In=1<<18,qe=1<<19,Ut=1<<20,Sr=1<<25,he=65536,We=1<<21,Fe=1<<22,Q=1<<23,ce=Symbol("$state"),Rr=Symbol("legacy props"),kr=Symbol(""),$=new class extends Error{constructor(){super(...arguments);se(this,"name","StaleReactionError");se(this,"message","The reaction that called `getAbortSignal()` was re-run or destroyed")}};var Yt;const Or=!!((Yt=globalThis.document)!=null&&Yt.contentType)&&globalThis.document.contentType.includes("xml"),xr=1,rt=3,Vt=8;function Bt(e){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}function Mn(){throw new Error("https://svelte.dev/e/async_derived_orphan")}function Dr(e,t,n){throw new Error("https://svelte.dev/e/each_key_duplicate")}function Pn(e){throw new Error("https://svelte.dev/e/effect_in_teardown")}function Cn(){throw new Error("https://svelte.dev/e/effect_in_unowned_derived")}function Fn(e){throw new Error("https://svelte.dev/e/effect_orphan")}function Ln(){throw new Error("https://svelte.dev/e/effect_update_depth_exceeded")}function Ir(){throw new Error("https://svelte.dev/e/hydration_failed")}function Mr(e){throw new Error("https://svelte.dev/e/props_invalid_value")}function jn(){throw new Error("https://svelte.dev/e/state_descriptors_fixed")}function Yn(){throw new Error("https://svelte.dev/e/state_prototype_fixed")}function Hn(){throw new Error("https://svelte.dev/e/state_unsafe_mutation")}function Pr(){throw new Error("https://svelte.dev/e/svelte_boundary_reset_onerror")}const Cr=1,Fr=2,Lr=4,jr=8,Yr=16,Hr=1,qr=2,Ur=4,Vr=8,Br=16,Gr=1,zr=2,qn="[",Un="[!",Kr="[?",Vn="]",pt={},S=Symbol(),Bn="http://www.w3.org/1999/xhtml",$r="http://www.w3.org/2000/svg",Xr="http://www.w3.org/1998/Math/MathML",Zr="@attach";function Gn(){console.warn("https://svelte.dev/e/derived_inert")}function wt(e){console.warn("https://svelte.dev/e/hydration_mismatch")}function Wr(){console.warn("https://svelte.dev/e/select_multiple_invalid_value")}function Jr(){console.warn("https://svelte.dev/e/svelte_boundary_reset_noop")}let pe=!1;function Qr(e){pe=e}let R;function Ne(e){if(e===null)throw wt(),pt;return R=e}function es(){return Ne(re(R))}function ts(e){if(pe){if(re(R)!==null)throw wt(),pt;R=e}}function ns(e=1){if(pe){for(var t=e,n=R;t--;)n=re(n);R=n}}function rs(e=!0){for(var t=0,n=R;;){if(n.nodeType===Vt){var r=n.data;if(r===Vn){if(t===0)return n;t-=1}else(r===qn||r===Un||r[0]==="["&&!isNaN(Number(r.slice(1))))&&(t+=1)}var s=re(n);e&&n.remove(),n=s}}function ss(e){if(!e||e.nodeType!==Vt)throw wt(),pt;return e.data}function Gt(e){return e===this.v}function zn(e,t){return e!=e?t==t:e!==t||e!==null&&typeof e=="object"||typeof e=="function"}function zt(e){return!zn(e,this.v)}let Ue=!1;function is(){Ue=!0}let T=null;function Je(e){T=e}function ls(e,t=!1,n){T={p:T,i:!1,c:null,e:null,s:e,x:null,r:y,l:Ue&&!t?{s:null,u:null,$:[]}:null}}function fs(e){var t=T,n=t.e;if(n!==null){t.e=null;for(var r of n)on(r)}return t.i=!0,T=t.p,{}}function Ve(){return!Ue||T!==null&&T.l===null}let fe=[];function Kt(){var e=fe;fe=[],xn(e)}function at(e){if(fe.length===0&&!Ce){var t=fe;queueMicrotask(()=>{t===fe&&Kt()})}fe.push(e)}function Kn(){for(;fe.length>0;)Kt()}function $n(e){var t=y;if(t===null)return w.f|=Q,e;if((t.f&Ee)===0&&(t.f&de)===0)throw e;Qe(e,t)}function Qe(e,t){for(;t!==null;){if((t.f&Dn)!==0){if((t.f&Ee)===0)throw e;try{t.b.error(e);return}catch(n){e=n}}t=t.parent}throw e}const Xn=-7169;function E(e,t){e.f=e.f&Xn|t}function yt(e){(e.f&F)!==0||e.deps===null?E(e,b):E(e,K)}function $t(e){if(e!==null)for(const t of e)(t.f&x)===0||(t.f&he)===0||(t.f^=he,$t(t.deps))}function Zn(e,t,n){(e.f&O)!==0?t.add(e):(e.f&K)!==0&&n.add(e),$t(e.deps),E(e,b)}const ie=new Set;let p=null,k=null,ot=null,Ce=!1,it=!1,me=null,Ge=null;var It=0;let Wn=1;var Te,Ae,oe,X,V,je,P,Ye,J,Z,B,Se,Re,ue,g,ze,Xt,Ke,ut,$e,Jn;const nt=class nt{constructor(){N(this,g);se(this,"id",Wn++);se(this,"current",new Map);se(this,"previous",new Map);N(this,Te,new Set);N(this,Ae,new Set);N(this,oe,new Set);N(this,X,new Map);N(this,V,new Map);N(this,je,null);N(this,P,[]);N(this,Ye,[]);N(this,J,new Set);N(this,Z,new Set);N(this,B,new Map);N(this,Se,new Set);se(this,"is_fork",!1);N(this,Re,!1);N(this,ue,new Set)}skip_effect(t){u(this,B).has(t)||u(this,B).set(t,{d:[],m:[]}),u(this,Se).delete(t)}unskip_effect(t,n=r=>this.schedule(r)){var r=u(this,B).get(t);if(r){u(this,B).delete(t);for(var s of r.d)E(s,O),n(s);for(s of r.m)E(s,K),n(s)}u(this,Se).add(t)}capture(t,n,r=!1){t.v!==S&&!this.previous.has(t)&&this.previous.set(t,t.v),(t.f&Q)===0&&(this.current.set(t,[n,r]),k==null||k.set(t,n)),this.is_fork||(t.v=n)}activate(){p=this}deactivate(){p=null,k=null}flush(){try{it=!0,p=this,I(this,g,Ke).call(this)}finally{It=0,ot=null,me=null,Ge=null,it=!1,p=null,k=null,_e.clear()}}discard(){for(const t of u(this,Ae))t(this);u(this,Ae).clear(),u(this,oe).clear(),ie.delete(this)}register_created_effect(t){u(this,Ye).push(t)}increment(t,n){let r=u(this,X).get(n)??0;if(u(this,X).set(n,r+1),t){let s=u(this,V).get(n)??0;u(this,V).set(n,s+1)}}decrement(t,n,r){let s=u(this,X).get(n)??0;if(s===1?u(this,X).delete(n):u(this,X).set(n,s-1),t){let i=u(this,V).get(n)??0;i===1?u(this,V).delete(n):u(this,V).set(n,i-1)}u(this,Re)||r||(ge(this,Re,!0),at(()=>{ge(this,Re,!1),this.flush()}))}transfer_effects(t,n){for(const r of t)u(this,J).add(r);for(const r of n)u(this,Z).add(r);t.clear(),n.clear()}oncommit(t){u(this,Te).add(t)}ondiscard(t){u(this,Ae).add(t)}on_fork_commit(t){u(this,oe).add(t)}run_fork_commit_callbacks(){for(const t of u(this,oe))t(this);u(this,oe).clear()}settled(){return(u(this,je)??ge(this,je,qt())).promise}static ensure(){if(p===null){const t=p=new nt;it||(ie.add(p),Ce||at(()=>{p===t&&t.flush()}))}return p}apply(){{k=null;return}}schedule(t){var s;if(ot=t,(s=t.b)!=null&&s.is_pending&&(t.f&(de|He|ht))!==0&&(t.f&Ee)===0){t.b.defer_effect(t);return}for(var n=t;n.parent!==null;){n=n.parent;var r=n.f;if(me!==null&&n===y&&(w===null||(w.f&x)===0))return;if((r&(ee|z))!==0){if((r&b)===0)return;n.f^=b}}u(this,P).push(n)}};Te=new WeakMap,Ae=new WeakMap,oe=new WeakMap,X=new WeakMap,V=new WeakMap,je=new WeakMap,P=new WeakMap,Ye=new WeakMap,J=new WeakMap,Z=new WeakMap,B=new WeakMap,Se=new WeakMap,Re=new WeakMap,ue=new WeakMap,g=new WeakSet,ze=function(){return this.is_fork||u(this,V).size>0},Xt=function(){for(const r of u(this,ue))for(const s of u(r,V).keys()){for(var t=!1,n=s;n.parent!==null;){if(u(this,B).has(n)){t=!0;break}n=n.parent}if(!t)return!0}return!1},Ke=function(){var a,f;if(It++>1e3&&(ie.delete(this),er()),!I(this,g,ze).call(this)){for(const l of u(this,J))u(this,Z).delete(l),E(l,O),this.schedule(l);for(const l of u(this,Z))E(l,K),this.schedule(l)}const t=u(this,P);ge(this,P,[]),this.apply();var n=me=[],r=[],s=Ge=[];for(const l of t)try{I(this,g,ut).call(this,l,n,r)}catch(c){throw Jt(l),c}if(p=null,s.length>0){var i=nt.ensure();for(const l of s)i.schedule(l)}if(me=null,Ge=null,I(this,g,ze).call(this)||I(this,g,Xt).call(this)){I(this,g,$e).call(this,r),I(this,g,$e).call(this,n);for(const[l,c]of u(this,B))Wt(l,c)}else{u(this,X).size===0&&ie.delete(this),u(this,J).clear(),u(this,Z).clear();for(const l of u(this,Te))l(this);u(this,Te).clear(),Mt(r),Mt(n),(a=u(this,je))==null||a.resolve()}var o=p;if(u(this,P).length>0){const l=o??(o=this);u(l,P).push(...u(this,P).filter(c=>!u(l,P).includes(c)))}o!==null&&(ie.add(o),I(f=o,g,Ke).call(f))},ut=function(t,n,r){t.f^=b;for(var s=t.first;s!==null;){var i=s.f,o=(i&(z|ee))!==0,a=o&&(i&b)!==0,f=a||(i&L)!==0||u(this,B).has(s);if(!f&&s.fn!==null){o?s.f^=b:(i&de)!==0?n.push(s):Be(s)&&((i&G)!==0&&u(this,Z).add(s),xe(s));var l=s.first;if(l!==null){s=l;continue}}for(;s!==null;){var c=s.next;if(c!==null){s=c;break}s=s.parent}}},$e=function(t){for(var n=0;n<t.length;n+=1)Zn(t[n],u(this,J),u(this,Z))},Jn=function(){var c,h,d;for(const v of ie){var t=v.id<this.id,n=[];for(const[_,[A,m]]of this.current){if(v.current.has(_)){var r=v.current.get(_)[0];if(t&&A!==r)v.current.set(_,[A,m]);else continue}n.push(_)}var s=[...v.current.keys()].filter(_=>!this.current.has(_));if(s.length===0)t&&v.discard();else if(n.length>0){if(t)for(const _ of u(this,Se))v.unskip_effect(_,A=>{var m;(A.f&(G|Fe))!==0?v.schedule(A):I(m=v,g,$e).call(m,[A])});v.activate();var i=new Set,o=new Map;for(var a of n)Zt(a,s,i,o);o=new Map;var f=[...v.current.keys()].filter(_=>this.current.has(_)?this.current.get(_)[0]!==_:!0);for(const _ of u(this,Ye))(_.f&(U|L|ft))===0&&Et(_,f,o)&&((_.f&(Fe|G))!==0?(E(_,O),v.schedule(_)):u(v,J).add(_));if(u(v,P).length>0){v.apply();for(var l of u(v,P))I(c=v,g,ut).call(c,l,[],[]);ge(v,P,[])}v.deactivate()}}for(const v of ie)u(v,ue).has(this)&&(u(v,ue).delete(this),u(v,ue).size===0&&!I(h=v,g,ze).call(h)&&(v.activate(),I(d=v,g,Ke).call(d)))};let we=nt;function Qn(e){var t=Ce;Ce=!0;try{for(var n;;){if(Kn(),p===null)return n;p.flush()}}finally{Ce=t}}function er(){try{Ln()}catch(e){Qe(e,ot)}}let H=null;function Mt(e){var t=e.length;if(t!==0){for(var n=0;n<t;){var r=e[n++];if((r.f&(U|L))===0&&Be(r)&&(H=new Set,xe(r),r.deps===null&&r.first===null&&r.nodes===null&&r.teardown===null&&r.ac===null&&cn(r),(H==null?void 0:H.size)>0)){_e.clear();for(const s of H){if((s.f&(U|L))!==0)continue;const i=[s];let o=s.parent;for(;o!==null;)H.has(o)&&(H.delete(o),i.push(o)),o=o.parent;for(let a=i.length-1;a>=0;a--){const f=i[a];(f.f&(U|L))===0&&xe(f)}}H.clear()}}H=null}}function Zt(e,t,n,r){if(!n.has(e)&&(n.add(e),e.reactions!==null))for(const s of e.reactions){const i=s.f;(i&x)!==0?Zt(s,t,n,r):(i&(Fe|G))!==0&&(i&O)===0&&Et(s,t,r)&&(E(s,O),gt(s))}}function Et(e,t,n){const r=n.get(e);if(r!==void 0)return r;if(e.deps!==null)for(const s of e.deps){if(ke.call(t,s))return!0;if((s.f&x)!==0&&Et(s,t,n))return n.set(s,!0),!0}return n.set(e,!1),!1}function gt(e){p.schedule(e)}function Wt(e,t){if(!((e.f&z)!==0&&(e.f&b)!==0)){(e.f&O)!==0?t.d.push(e):(e.f&K)!==0&&t.m.push(e),E(e,b);for(var n=e.first;n!==null;)Wt(n,t),n=n.next}}function Jt(e){E(e,b);for(var t=e.first;t!==null;)Jt(t),t=t.next}function Qt(e,t,n,r){const s=Ve()?bt:rr;var i=e.filter(d=>!d.settled);if(n.length===0&&i.length===0){r(t.map(s));return}var o=y,a=tr(),f=i.length===1?i[0].promise:i.length>1?Promise.all(i.map(d=>d.promise)):null;function l(d){a();try{r(d)}catch(v){(o.f&U)===0&&Qe(v,o)}et()}if(n.length===0){f.then(()=>l(t.map(s)));return}var c=mt();function h(){Promise.all(n.map(d=>nr(d))).then(d=>l([...t.map(s),...d])).catch(d=>Qe(d,o)).finally(()=>c())}f?f.then(()=>{a(),h(),et()}):h()}function tr(){var e=y,t=w,n=T,r=p;return function(i=!0){Oe(e),ne(t),Je(n),i&&(e.f&U)===0&&(r==null||r.activate(),r==null||r.apply())}}function et(e=!0){Oe(null),ne(null),Je(null),e&&(p==null||p.deactivate())}function mt(){var e=y,t=e.b,n=p,r=t.is_rendered();return t.update_pending_count(1,n),n.increment(r,e),(s=!1)=>{t.update_pending_count(-1,n),n.decrement(r,e,s)}}function bt(e){var t=x|O;return y!==null&&(y.f|=qe),{ctx:T,deps:null,effects:null,equals:Gt,f:t,fn:e,reactions:null,rv:0,v:S,wv:0,parent:y,ac:null}}function nr(e,t,n){let r=y;r===null&&Mn();var s=void 0,i=At(S),o=!w,a=new Map;return vr(()=>{var v;var f=y,l=qt();s=l.promise;try{Promise.resolve(e()).then(l.resolve,l.reject).finally(et)}catch(_){l.reject(_),et()}var c=p;if(o){if((f.f&Ee)!==0)var h=mt();if(r.b.is_rendered())(v=a.get(c))==null||v.reject($),a.delete(c);else{for(const _ of a.values())_.reject($);a.clear()}a.set(c,l)}const d=(_,A=void 0)=>{if(h){var m=A===$;h(m)}if(!(A===$||(f.f&U)!==0)){if(c.activate(),A)i.f|=Q,_t(i,A);else{(i.f&Q)!==0&&(i.f^=Q),_t(i,_);for(const[De,Ie]of a){if(a.delete(De),De===c)break;Ie.reject($)}}c.deactivate()}};l.promise.then(d,_=>d(null,_||"unknown"))}),cr(()=>{for(const f of a.values())f.reject($)}),new Promise(f=>{function l(c){function h(){c===s?f(i):l(s)}c.then(h,h)}l(s)})}function as(e){const t=bt(e);return dn(t),t}function rr(e){const t=bt(e);return t.equals=zt,t}function sr(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n<t.length;n+=1)ye(t[n])}}function Tt(e){var t,n=y,r=e.parent;if(!te&&r!==null&&(r.f&(U|L))!==0)return Gn(),e.v;Oe(r);try{e.f&=~he,sr(e),t=yn(e)}finally{Oe(n)}return t}function en(e){var t=Tt(e);if(!e.equals(t)&&(e.wv=pn(),(!(p!=null&&p.is_fork)||e.deps===null)&&(p!==null?p.capture(e,t,!0):e.v=t,e.deps===null))){E(e,b);return}te||(k!==null?(an()||p!=null&&p.is_fork)&&k.set(e,t):yt(e))}function ir(e){var t,n;if(e.effects!==null)for(const r of e.effects)(r.teardown||r.ac)&&((t=r.teardown)==null||t.call(r),(n=r.ac)==null||n.abort($),r.teardown=On,r.ac=null,Le(r,0),kt(r))}function tn(e){if(e.effects!==null)for(const t of e.effects)t.teardown&&xe(t)}let ct=new Set;const _e=new Map;let nn=!1;function At(e,t){var n={f:0,v:e,reactions:null,equals:Gt,rv:0,wv:0};return n}function W(e,t){const n=At(e);return dn(n),n}function os(e,t=!1,n=!0){var s;const r=At(e);return t||(r.equals=zt),Ue&&n&&T!==null&&T.l!==null&&((s=T.l).s??(s.s=[])).push(r),r}function le(e,t,n=!1){w!==null&&(!q||(w.f&ft)!==0)&&Ve()&&(w.f&(x|G|Fe|ft))!==0&&(j===null||!ke.call(j,e))&&Hn();let r=n?Me(t):t;return _t(e,r,Ge)}function _t(e,t,n=null){if(!e.equals(t)){_e.set(e,te?t:e.v);var r=we.ensure();if(r.capture(e,t),(e.f&x)!==0){const s=e;(e.f&O)!==0&&Tt(s),k===null&&yt(s)}e.wv=pn(),rn(e,O,n),Ve()&&y!==null&&(y.f&b)!==0&&(y.f&(z|ee))===0&&(C===null?wr([e]):C.push(e)),!r.is_fork&&ct.size>0&&!nn&&lr()}return t}function lr(){nn=!1;for(const e of ct)(e.f&b)!==0&&E(e,K),Be(e)&&xe(e);ct.clear()}function lt(e){le(e,e.v+1)}function rn(e,t,n){var r=e.reactions;if(r!==null)for(var s=Ve(),i=r.length,o=0;o<i;o++){var a=r[o],f=a.f;if(!(!s&&a===y)){var l=(f&O)===0;if(l&&E(a,t),(f&x)!==0){var c=a;k==null||k.delete(c),(f&he)===0&&(f&F&&(y===null||(y.f&We)===0)&&(a.f|=he),rn(c,K,n))}else if(l){var h=a;(f&G)!==0&&H!==null&&H.add(h),n!==null?n.push(h):gt(h)}}}}function Me(e){if(typeof e!="object"||e===null||ce in e)return e;const t=Ht(e);if(t!==kn&&t!==Nn)return e;var n=new Map,r=An(e),s=W(0),i=ve,o=a=>{if(ve===i)return a();var f=w,l=ve;ne(null),jt(i);var c=a();return ne(f),jt(l),c};return r&&n.set("length",W(e.length)),new Proxy(e,{defineProperty(a,f,l){(!("value"in l)||l.configurable===!1||l.enumerable===!1||l.writable===!1)&&jn();var c=n.get(f);return c===void 0?o(()=>{var h=W(l.value);return n.set(f,h),h}):le(c,l.value,!0),!0},deleteProperty(a,f){var l=n.get(f);if(l===void 0){if(f in a){const c=o(()=>W(S));n.set(f,c),lt(s)}}else le(l,S),lt(s);return!0},get(a,f,l){var v;if(f===ce)return e;var c=n.get(f),h=f in a;if(c===void 0&&(!h||(v=Pe(a,f))!=null&&v.writable)&&(c=o(()=>{var _=Me(h?a[f]:S),A=W(_);return A}),n.set(f,c)),c!==void 0){var d=be(c);return d===S?void 0:d}return Reflect.get(a,f,l)},getOwnPropertyDescriptor(a,f){var l=Reflect.getOwnPropertyDescriptor(a,f);if(l&&"value"in l){var c=n.get(f);c&&(l.value=be(c))}else if(l===void 0){var h=n.get(f),d=h==null?void 0:h.v;if(h!==void 0&&d!==S)return{enumerable:!0,configurable:!0,value:d,writable:!0}}return l},has(a,f){var d;if(f===ce)return!0;var l=n.get(f),c=l!==void 0&&l.v!==S||Reflect.has(a,f);if(l!==void 0||y!==null&&(!c||(d=Pe(a,f))!=null&&d.writable)){l===void 0&&(l=o(()=>{var v=c?Me(a[f]):S,_=W(v);return _}),n.set(f,l));var h=be(l);if(h===S)return!1}return c},set(a,f,l,c){var Nt;var h=n.get(f),d=f in a;if(r&&f==="length")for(var v=l;v<h.v;v+=1){var _=n.get(v+"");_!==void 0?le(_,S):v in a&&(_=o(()=>W(S)),n.set(v+"",_))}if(h===void 0)(!d||(Nt=Pe(a,f))!=null&&Nt.writable)&&(h=o(()=>W(void 0)),le(h,Me(l)),n.set(f,h));else{d=h.v!==S;var A=o(()=>Me(l));le(h,A)}var m=Reflect.getOwnPropertyDescriptor(a,f);if(m!=null&&m.set&&m.set.call(c,l),!d){if(r&&typeof f=="string"){var De=n.get("length"),Ie=Number(f);Number.isInteger(Ie)&&Ie>=De.v&&le(De,Ie+1)}lt(s)}return!0},ownKeys(a){be(s);var f=Reflect.ownKeys(a).filter(h=>{var d=n.get(h);return d===void 0||d.v!==S});for(var[l,c]of n)c.v!==S&&!(l in a)&&f.push(l);return f},setPrototypeOf(){Yn()}})}function Pt(e){try{if(e!==null&&typeof e=="object"&&ce in e)return e[ce]}catch{}return e}function us(e,t){return Object.is(Pt(e),Pt(t))}var Ct,fr,ar,sn,ln;function cs(){if(Ct===void 0){Ct=window,fr=document,ar=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;sn=Pe(t,"firstChild").get,ln=Pe(t,"nextSibling").get,xt(e)&&(e.__click=void 0,e.__className=void 0,e.__attributes=null,e.__style=void 0,e.__e=void 0),xt(n)&&(n.__t=void 0)}}function tt(e=""){return document.createTextNode(e)}function vt(e){return sn.call(e)}function re(e){return ln.call(e)}function _s(e,t){if(!pe)return vt(e);var n=vt(R);if(n===null)n=R.appendChild(tt());else if(t&&n.nodeType!==rt){var r=tt();return n==null||n.before(r),Ne(r),r}return t&&St(n),Ne(n),n}function vs(e,t=!1){if(!pe){var n=vt(e);return n instanceof Comment&&n.data===""?re(n):n}if(t){if((R==null?void 0:R.nodeType)!==rt){var r=tt();return R==null||R.before(r),Ne(r),r}St(R)}return R}function ds(e,t=1,n=!1){let r=pe?R:e;for(var s;t--;)s=r,r=re(r);if(!pe)return r;if(n){if((r==null?void 0:r.nodeType)!==rt){var i=tt();return r===null?s==null||s.after(i):r.before(i),Ne(i),i}St(r)}return Ne(r),r}function hs(e){e.textContent=""}function ps(){return!1}function ws(e,t,n){return document.createElementNS(t??Bn,e,void 0)}function St(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===rt;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function ys(e,t){if(t){const n=document.body;e.autofocus=!0,at(()=>{document.activeElement===n&&e.focus()})}}let Ft=!1;function or(){Ft||(Ft=!0,document.addEventListener("reset",e=>{Promise.resolve().then(()=>{var t;if(!e.defaultPrevented)for(const n of e.target.elements)(t=n.__on_r)==null||t.call(n)})},{capture:!0}))}function Rt(e){var t=w,n=y;ne(null),Oe(null);try{return e()}finally{ne(t),Oe(n)}}function Es(e,t,n,r=n){e.addEventListener(t,()=>Rt(n));const s=e.__on_r;s?e.__on_r=()=>{s(),r(!0)}:e.__on_r=()=>r(!0),or()}function fn(e){y===null&&(w===null&&Fn(),Cn()),te&&Pn()}function ur(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function Y(e,t){var n=y;n!==null&&(n.f&L)!==0&&(e|=L);var r={ctx:T,deps:null,nodes:null,f:e|O|F,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};p==null||p.register_created_effect(r);var s=r;if((e&de)!==0)me!==null?me.push(r):we.ensure().schedule(r);else if(t!==null){try{xe(r)}catch(o){throw ye(r),o}s.deps===null&&s.teardown===null&&s.nodes===null&&s.first===s.last&&(s.f&qe)===0&&(s=s.first,(e&G)!==0&&(e&Ze)!==0&&s!==null&&(s.f|=Ze))}if(s!==null&&(s.parent=n,n!==null&&ur(s,n),w!==null&&(w.f&x)!==0&&(e&ee)===0)){var i=w;(i.effects??(i.effects=[])).push(s)}return r}function an(){return w!==null&&!q}function cr(e){const t=Y(He,null);return E(t,b),t.teardown=e,t}function _r(e){fn();var t=y.f,n=!w&&(t&z)!==0&&(t&Ee)===0;if(n){var r=T;(r.e??(r.e=[])).push(e)}else return on(e)}function on(e){return Y(de|Ut,e)}function gs(e){return fn(),Y(He|Ut,e)}function ms(e){we.ensure();const t=Y(ee|qe,e);return(n={})=>new Promise(r=>{n.outro?pr(t,()=>{ye(t),r(void 0)}):(ye(t),r(void 0))})}function bs(e){return Y(de,e)}function vr(e){return Y(Fe|qe,e)}function Ts(e,t=0){return Y(He|t,e)}function As(e,t=[],n=[],r=[]){Qt(r,t,n,s=>{Y(He,()=>e(...s.map(be)))})}function Ss(e,t=[],n=[],r=[]){if(n.length>0||r.length>0)var s=mt();Qt(r,t,n,i=>{Y(de,()=>e(...i.map(be))),s&&s()})}function Rs(e,t=0){var n=Y(G|t,e);return n}function ks(e,t=0){var n=Y(ht|t,e);return n}function Ns(e){return Y(z|qe,e)}function un(e){var t=e.teardown;if(t!==null){const n=te,r=w;Lt(!0),ne(null);try{t.call(null)}finally{Lt(n),ne(r)}}}function kt(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){const s=n.ac;s!==null&&Rt(()=>{s.abort($)});var r=n.next;(n.f&ee)!==0?n.parent=null:ye(n,t),n=r}}function dr(e){for(var t=e.first;t!==null;){var n=t.next;(t.f&z)===0&&ye(t),t=n}}function ye(e,t=!0){var n=!1;(t||(e.f&In)!==0)&&e.nodes!==null&&e.nodes.end!==null&&(hr(e.nodes.start,e.nodes.end),n=!0),E(e,Dt),kt(e,t&&!n),Le(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(const i of r)i.stop();un(e),e.f^=Dt,e.f|=U;var s=e.parent;s!==null&&s.first!==null&&cn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function hr(e,t){for(;e!==null;){var n=e===t?null:re(e);e.remove(),e=n}}function cn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function pr(e,t,n=!0){var r=[];_n(e,r,!0);var s=()=>{n&&ye(e),t&&t()},i=r.length;if(i>0){var o=()=>--i||s();for(var a of r)a.out(o)}else s()}function _n(e,t,n){if((e.f&L)===0){e.f^=L;var r=e.nodes&&e.nodes.t;if(r!==null)for(const a of r)(a.is_global||n)&&t.push(a);for(var s=e.first;s!==null;){var i=s.next;if((s.f&ee)===0){var o=(s.f&Ze)!==0||(s.f&z)!==0&&(e.f&G)!==0;_n(s,t,o?n:!1)}s=i}}}function Os(e){vn(e,!0)}function vn(e,t){if((e.f&L)!==0){e.f^=L,(e.f&b)===0&&(E(e,O),we.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,s=(n.f&Ze)!==0||(n.f&z)!==0;vn(n,s?t:!1),n=r}var i=e.nodes&&e.nodes.t;if(i!==null)for(const o of i)(o.is_global||t)&&o.in()}}function xs(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var s=n===r?null:re(n);t.append(n),n=s}}let Xe=!1,te=!1;function Lt(e){te=e}let w=null,q=!1;function ne(e){w=e}let y=null;function Oe(e){y=e}let j=null;function dn(e){w!==null&&(j===null?j=[e]:j.push(e))}let D=null,M=0,C=null;function wr(e){C=e}let hn=1,ae=0,ve=ae;function jt(e){ve=e}function pn(){return++hn}function Be(e){var t=e.f;if((t&O)!==0)return!0;if(t&x&&(e.f&=~he),(t&K)!==0){for(var n=e.deps,r=n.length,s=0;s<r;s++){var i=n[s];if(Be(i)&&en(i),i.wv>e.wv)return!0}(t&F)!==0&&k===null&&E(e,b)}return!1}function wn(e,t,n=!0){var r=e.reactions;if(r!==null&&!(j!==null&&ke.call(j,e)))for(var s=0;s<r.length;s++){var i=r[s];(i.f&x)!==0?wn(i,t,!1):t===i&&(n?E(i,O):(i.f&b)!==0&&E(i,K),gt(i))}}function yn(e){var A;var t=D,n=M,r=C,s=w,i=j,o=T,a=q,f=ve,l=e.f;D=null,M=0,C=null,w=(l&(z|ee))===0?e:null,j=null,Je(e.ctx),q=!1,ve=++ae,e.ac!==null&&(Rt(()=>{e.ac.abort($)}),e.ac=null);try{e.f|=We;var c=e.fn,h=c();e.f|=Ee;var d=e.deps,v=p==null?void 0:p.is_fork;if(D!==null){var _;if(v||Le(e,M),d!==null&&M>0)for(d.length=M+D.length,_=0;_<D.length;_++)d[M+_]=D[_];else e.deps=d=D;if(an()&&(e.f&F)!==0)for(_=M;_<d.length;_++)((A=d[_]).reactions??(A.reactions=[])).push(e)}else!v&&d!==null&&M<d.length&&(Le(e,M),d.length=M);if(Ve()&&C!==null&&!q&&d!==null&&(e.f&(x|K|O))===0)for(_=0;_<C.length;_++)wn(C[_],e);if(s!==null&&s!==e){if(ae++,s.deps!==null)for(let m=0;m<n;m+=1)s.deps[m].rv=ae;if(t!==null)for(const m of t)m.rv=ae;C!==null&&(r===null?r=C:r.push(...C))}return(e.f&Q)!==0&&(e.f^=Q),h}catch(m){return $n(m)}finally{e.f^=We,D=t,M=n,C=r,w=s,j=i,Je(o),q=a,ve=f}}function yr(e,t){let n=t.reactions;if(n!==null){var r=Sn.call(n,e);if(r!==-1){var s=n.length-1;s===0?n=t.reactions=null:(n[r]=n[s],n.pop())}}if(n===null&&(t.f&x)!==0&&(D===null||!ke.call(D,t))){var i=t;(i.f&F)!==0&&(i.f^=F,i.f&=~he),i.v!==S&&yt(i),ir(i),Le(i,0)}}function Le(e,t){var n=e.deps;if(n!==null)for(var r=t;r<n.length;r++)yr(e,n[r])}function xe(e){var t=e.f;if((t&U)===0){E(e,b);var n=y,r=Xe;y=e,Xe=!0;try{(t&(G|ht))!==0?dr(e):kt(e),un(e);var s=yn(e);e.teardown=typeof s=="function"?s:null,e.wv=hn;var i}finally{Xe=r,y=n}}}async function Ds(){await Promise.resolve(),Qn()}function Is(){return we.ensure().settled()}function be(e){var t=e.f,n=(t&x)!==0;if(w!==null&&!q){var r=y!==null&&(y.f&U)!==0;if(!r&&(j===null||!ke.call(j,e))){var s=w.deps;if((w.f&We)!==0)e.rv<ae&&(e.rv=ae,D===null&&s!==null&&s[M]===e?M++:D===null?D=[e]:D.push(e));else{(w.deps??(w.deps=[])).push(e);var i=e.reactions;i===null?e.reactions=[w]:ke.call(i,w)||i.push(w)}}}if(te&&_e.has(e))return _e.get(e);if(n){var o=e;if(te){var a=o.v;return((o.f&b)===0&&o.reactions!==null||gn(o))&&(a=Tt(o)),_e.set(o,a),a}var f=(o.f&F)===0&&!q&&w!==null&&(Xe||(w.f&F)!==0),l=(o.f&Ee)===0;Be(o)&&(f&&(o.f|=F),en(o)),f&&!l&&(tn(o),En(o))}if(k!=null&&k.has(e))return k.get(e);if((e.f&Q)!==0)throw e.v;return e.v}function En(e){if(e.f|=F,e.deps!==null)for(const t of e.deps)(t.reactions??(t.reactions=[])).push(e),(t.f&x)!==0&&(t.f&F)===0&&(tn(t),En(t))}function gn(e){if(e.v===S)return!0;if(e.deps===null)return!1;for(const t of e.deps)if(_e.has(t)||(t.f&x)!==0&&gn(t))return!0;return!1}function mn(e){var t=q;try{return q=!0,e()}finally{q=t}}function Ms(e){if(!(typeof e!="object"||!e||e instanceof EventTarget)){if(ce in e)dt(e);else if(!Array.isArray(e))for(let t in e){const n=e[t];typeof n=="object"&&n&&ce in n&&dt(n)}}}function dt(e,t=new Set){if(typeof e=="object"&&e!==null&&!(e instanceof EventTarget)&&!t.has(e)){t.add(e),e instanceof Date&&e.getTime();for(let r in e)try{dt(e[r],t)}catch{}const n=Ht(e);if(n!==Object.prototype&&n!==Array.prototype&&n!==Map.prototype&&n!==Set.prototype&&n!==Date.prototype){const r=Rn(n);for(let s in r){const i=r[s].get;if(i)try{i.call(e)}catch{}}}}}function Er(e){T===null&&Bt(),Ue&&T.l!==null?gr(T).m.push(e):_r(()=>{const t=mn(e);if(typeof t=="function")return t})}function Ps(e){T===null&&Bt(),Er(()=>()=>mn(e))}function gr(e){var t=e.l;return t.u??(t.u={a:[],b:[],m:[]})}export{fr as $,Ss as A,as as B,Vt as C,Dt as D,qe as E,_r as F,Me as G,In as H,Os as I,ye as J,pr as K,Ns as L,p as M,xs as N,ps as O,es as P,ss as Q,rs as R,ce as S,Pe as T,Mr as U,Ur as V,te as W,U as X,Vr as Y,Ue as Z,qr as _,fs as a,rt as a$,Hr as a0,bt as a1,rr as a2,Br as a3,Rr as a4,qn as a5,Qn as a6,Tr as a7,os as a8,gs as a9,kr as aA,Rn as aB,Or as aC,Qt as aD,Zr as aE,ys as aF,S as aG,an as aH,lt as aI,Dn as aJ,Kr as aK,Zn as aL,Oe as aM,ne as aN,Je as aO,we as aP,$n as aQ,w as aR,Qe as aS,Pr as aT,Jr as aU,Rt as aV,ws as aW,ar as aX,Gr as aY,zr as aZ,Ee as a_,Ds as aa,ns as ab,Lr as ac,Un as ad,Vn as ae,_t as af,Sr as ag,Dr as ah,An as ai,br as aj,Cr as ak,Yr as al,At as am,Fr as an,L as ao,at as ap,z as aq,jr as ar,hs as as,ks as at,Wr as au,us as av,cr as aw,or as ax,Bn as ay,Ht as az,tt as b,St as b0,cs as b1,pt as b2,Ir as b3,ms as b4,wt as b5,hr as b6,$r as b7,Xr as b8,Es as b9,xn as ba,Ar as bb,Ms as bc,xr as bd,Ps as be,On as bf,zn as bg,Is as bh,_s as c,Rs as d,Qr as e,vs as f,re as g,pe as h,Ne as i,R as j,vt as k,is as l,T as m,bs as n,Ts as o,ls as p,y as q,ts as r,ds as s,As as t,mn as u,Ze as v,W as w,be as x,le as y,Er as z};
|
web/sveltekit/build/_app/immutable/chunks/{CdmpEGnB.js → LdqJVpAk.js}
RENAMED
|
@@ -1 +1 @@
|
|
| 1 |
-
var i=t=>{throw TypeError(t)};var f=(t,e,r)=>e.has(t)||i("Cannot "+r);var s=(t,e,r)=>(f(t,e,"read from private field"),r?r.call(t):e.get(t)),o=(t,e,r)=>e.has(t)?i("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,r);import{
|
|
|
|
| 1 |
+
var i=t=>{throw TypeError(t)};var f=(t,e,r)=>e.has(t)||i("Cannot "+r);var s=(t,e,r)=>(f(t,e,"read from private field"),r?r.call(t):e.get(t)),o=(t,e,r)=>e.has(t)?i("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,r);import{d as p,v as d,w as y,x as h,y as S}from"./Dr93mMbz.js";import{B as g}from"./kFXL_I1K.js";function I(t,e,...r){var c=new g(t);p(()=>{const a=e()??null;c.ensure(a,a&&(l=>a(l,...r)))},d)}var n;class w{constructor(){o(this,n,y(!1))}get ready(){return h(s(this,n))}set ready(e){S(s(this,n),e,!0)}reset(){this.ready=!1}markReady(){this.ready=!0}}n=new WeakMap;const N=new w,m="riprap:print:";function u(t){return m+t}function T(t){if(!(typeof window>"u"))try{localStorage.setItem(u(t.queryId),JSON.stringify(t))}catch{}}function A(t){if(typeof window>"u")return null;try{const e=localStorage.getItem(u(t));return e?JSON.parse(e):null}catch{return null}}export{N as b,A as l,T as p,I as s};
|
web/sveltekit/build/_app/immutable/chunks/WYoL_k1G.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
var He=Object.defineProperty;var me=t=>{throw TypeError(t)};var We=(t,e,r)=>e in t?He(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var G=(t,e,r)=>We(t,typeof e!="symbol"?e+"":e,r),ae=(t,e,r)=>e.has(t)||me("Cannot "+r);var s=(t,e,r)=>(ae(t,e,"read from private field"),r?r.call(t):e.get(t)),h=(t,e,r)=>e.has(t)?me("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,r),f=(t,e,r,n)=>(ae(t,e,"write to private field"),n?n.call(t,r):e.set(t,r),r),_=(t,e,r)=>(ae(t,e,"access private method"),r);import{aH as $e,x as Se,o as je,u as Be,aI as ye,ap as Y,am as ke,j as v,h as S,q as C,aJ as be,d as Ye,P as Ae,ad as Ue,aK as Ee,L as V,b as U,K as oe,M as H,N as qe,aL as ze,aM as re,aN as se,aO as Te,aP as Je,aQ as Xe,aR as Le,m as Me,af as Ge,J as fe,i as q,ab as Ke,R as Qe,aS as K,v as Ze,E as et,aT as tt,aU as rt,aw as st,aV as it,a7 as nt,aW as at,k as D,aX as ot,aY as Oe,aZ as ft,a_ as lt,a$ as ut,b0 as ct,b1 as ue,C as De,a5 as dt,g as ht,b2 as ce,e as Q,b3 as _t,as as pt,b4 as vt,aj as gt,p as mt,ae as yt,b5 as bt,a as Et}from"./Dr93mMbz.js";function Tt(t){let e=0,r=ke(0),n;return()=>{$e()&&(Se(r),je(()=>(e===0&&(n=Be(()=>t(()=>ye(r)))),e+=1,()=>{Y(()=>{e-=1,e===0&&(n==null||n(),n=void 0,ye(r))})})))}}var wt=Ze|et;function Nt(t,e,r,n){new Rt(t,e,r,n)}var T,z,N,$,m,R,p,w,A,j,I,B,J,X,L,ie,l,Ie,Ce,Pe,de,ee,te,he,_e;class Rt{constructor(e,r,n,o){h(this,l);G(this,"parent");G(this,"is_pending",!1);G(this,"transform_error");h(this,T);h(this,z,S?v:null);h(this,N);h(this,$);h(this,m);h(this,R,null);h(this,p,null);h(this,w,null);h(this,A,null);h(this,j,0);h(this,I,0);h(this,B,!1);h(this,J,new Set);h(this,X,new Set);h(this,L,null);h(this,ie,Tt(()=>(f(this,L,ke(s(this,j))),()=>{f(this,L,null)})));var i;f(this,T,e),f(this,N,r),f(this,$,a=>{var d=C;d.b=this,d.f|=be,n(a)}),this.parent=C.b,this.transform_error=o??((i=this.parent)==null?void 0:i.transform_error)??(a=>a),f(this,m,Ye(()=>{if(S){const a=s(this,z);Ae();const d=a.data===Ue;if(a.data.startsWith(Ee)){const c=JSON.parse(a.data.slice(Ee.length));_(this,l,Ce).call(this,c)}else d?_(this,l,Pe).call(this):_(this,l,Ie).call(this)}else _(this,l,de).call(this)},wt)),S&&f(this,T,v)}defer_effect(e){ze(e,s(this,J),s(this,X))}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!s(this,N).pending}update_pending_count(e,r){_(this,l,he).call(this,e,r),f(this,j,s(this,j)+e),!(!s(this,L)||s(this,B))&&(f(this,B,!0),Y(()=>{f(this,B,!1),s(this,L)&&Ge(s(this,L),s(this,j))}))}get_effect_pending(){return s(this,ie).call(this),Se(s(this,L))}error(e){var r;if(!s(this,N).onerror&&!s(this,N).failed)throw e;(r=H)!=null&&r.is_fork?(s(this,R)&&H.skip_effect(s(this,R)),s(this,p)&&H.skip_effect(s(this,p)),s(this,w)&&H.skip_effect(s(this,w)),H.on_fork_commit(()=>{_(this,l,_e).call(this,e)})):_(this,l,_e).call(this,e)}}T=new WeakMap,z=new WeakMap,N=new WeakMap,$=new WeakMap,m=new WeakMap,R=new WeakMap,p=new WeakMap,w=new WeakMap,A=new WeakMap,j=new WeakMap,I=new WeakMap,B=new WeakMap,J=new WeakMap,X=new WeakMap,L=new WeakMap,ie=new WeakMap,l=new WeakSet,Ie=function(){try{f(this,R,V(()=>s(this,$).call(this,s(this,T))))}catch(e){this.error(e)}},Ce=function(e){const r=s(this,N).failed;r&&f(this,w,V(()=>{r(s(this,T),()=>e,()=>()=>{})}))},Pe=function(){const e=s(this,N).pending;e&&(this.is_pending=!0,f(this,p,V(()=>e(s(this,T)))),Y(()=>{var r=f(this,A,document.createDocumentFragment()),n=U();r.append(n),f(this,R,_(this,l,te).call(this,()=>V(()=>s(this,$).call(this,n)))),s(this,I)===0&&(s(this,T).before(r),f(this,A,null),oe(s(this,p),()=>{f(this,p,null)}),_(this,l,ee).call(this,H))}))},de=function(){try{if(this.is_pending=this.has_pending_snippet(),f(this,I,0),f(this,j,0),f(this,R,V(()=>{s(this,$).call(this,s(this,T))})),s(this,I)>0){var e=f(this,A,document.createDocumentFragment());qe(s(this,R),e);const r=s(this,N).pending;f(this,p,V(()=>r(s(this,T))))}else _(this,l,ee).call(this,H)}catch(r){this.error(r)}},ee=function(e){this.is_pending=!1,e.transfer_effects(s(this,J),s(this,X))},te=function(e){var r=C,n=Le,o=Me;re(s(this,m)),se(s(this,m)),Te(s(this,m).ctx);try{return Je.ensure(),e()}catch(i){return Xe(i),null}finally{re(r),se(n),Te(o)}},he=function(e,r){var n;if(!this.has_pending_snippet()){this.parent&&_(n=this.parent,l,he).call(n,e,r);return}f(this,I,s(this,I)+e),s(this,I)===0&&(_(this,l,ee).call(this,r),s(this,p)&&oe(s(this,p),()=>{f(this,p,null)}),s(this,A)&&(s(this,T).before(s(this,A)),f(this,A,null)))},_e=function(e){s(this,R)&&(fe(s(this,R)),f(this,R,null)),s(this,p)&&(fe(s(this,p)),f(this,p,null)),s(this,w)&&(fe(s(this,w)),f(this,w,null)),S&&(q(s(this,z)),Ke(),q(Qe()));var r=s(this,N).onerror;let n=s(this,N).failed;var o=!1,i=!1;const a=()=>{if(o){rt();return}o=!0,i&&tt(),s(this,w)!==null&&oe(s(this,w),()=>{f(this,w,null)}),_(this,l,te).call(this,()=>{_(this,l,de).call(this)})},d=u=>{try{i=!0,r==null||r(u,a),i=!1}catch(c){K(c,s(this,m)&&s(this,m).parent)}n&&f(this,w,_(this,l,te).call(this,()=>{try{return V(()=>{var c=C;c.b=this,c.f|=be,n(s(this,T),()=>u,()=>a)})}catch(c){return K(c,s(this,m).parent),null}}))};Y(()=>{var u;try{u=this.transform_error(e)}catch(c){K(c,s(this,m)&&s(this,m).parent);return}u!==null&&typeof u=="object"&&typeof u.then=="function"?u.then(d,c=>K(c,s(this,m)&&s(this,m).parent)):d(u)})};function Vt(t){return t.endsWith("capture")&&t!=="gotpointercapture"&&t!=="lostpointercapture"}const St=["beforeinput","click","change","dblclick","contextmenu","focusin","focusout","input","keydown","keyup","mousedown","mousemove","mouseout","mouseover","mouseup","pointerdown","pointermove","pointerout","pointerover","pointerup","touchend","touchmove","touchstart"];function Ht(t){return St.includes(t)}const kt={formnovalidate:"formNoValidate",ismap:"isMap",nomodule:"noModule",playsinline:"playsInline",readonly:"readOnly",defaultvalue:"defaultValue",defaultchecked:"defaultChecked",srcobject:"srcObject",novalidate:"noValidate",allowfullscreen:"allowFullscreen",disablepictureinpicture:"disablePictureInPicture",disableremoteplayback:"disableRemotePlayback"};function Wt(t){return t=t.toLowerCase(),kt[t]??t}const At=["touchstart","touchmove"];function Lt(t){return At.includes(t)}const Mt=["textarea","script","style","title"];function $t(t){return Mt.includes(t)}const W=Symbol("events"),Fe=new Set,pe=new Set;function Ot(t,e,r,n={}){function o(i){if(n.capture||ve.call(e,i),!i.cancelBubble)return it(()=>r==null?void 0:r.call(this,i))}return t.startsWith("pointer")||t.startsWith("touch")||t==="wheel"?Y(()=>{e.addEventListener(t,o,n)}):e.addEventListener(t,o,n),o}function jt(t,e,r,n,o){var i={capture:n,passive:o},a=Ot(t,e,r,i);(e===document.body||e===window||e===document||e instanceof HTMLMediaElement)&&st(()=>{e.removeEventListener(t,a,i)})}function Bt(t,e,r){(e[W]??(e[W]={}))[t]=r}function Yt(t){for(var e=0;e<t.length;e++)Fe.add(t[e]);for(var r of pe)r(t)}let we=null;function ve(t){var F,E;var e=this,r=e.ownerDocument,n=t.type,o=((F=t.composedPath)==null?void 0:F.call(t))||[],i=o[0]||t.target;we=t;var a=0,d=we===t&&t[W];if(d){var u=o.indexOf(d);if(u!==-1&&(e===document||e===window)){t[W]=e;return}var c=o.indexOf(e);if(c===-1)return;u<=c&&(a=u)}if(i=o[a]||t.target,i!==e){nt(t,"currentTarget",{configurable:!0,get(){return i||r}});var M=Le,P=C;se(null),re(null);try{for(var O,y=[];i!==null;){var g=i.assignedSlot||i.parentNode||i.host||null;try{var b=(E=i[W])==null?void 0:E[n];b!=null&&(!i.disabled||t.target===i)&&b.call(i,t)}catch(x){O?y.push(x):O=x}if(t.cancelBubble||g===e||g===null)break;i=g}if(O){for(let x of y)queueMicrotask(()=>{throw x});throw O}}finally{t[W]=e,delete t.currentTarget,se(M),re(P)}}}var Ne;const le=((Ne=globalThis==null?void 0:globalThis.window)==null?void 0:Ne.trustedTypes)&&globalThis.window.trustedTypes.createPolicy("svelte-trusted-html",{createHTML:t=>t});function Dt(t){return(le==null?void 0:le.createHTML(t))??t}function xe(t){var e=at("template");return e.innerHTML=Dt(t.replaceAll("<!>","<!---->")),e.content}function k(t,e){var r=C;r.nodes===null&&(r.nodes={start:t,end:e,a:null,t:null})}function Ut(t,e){var r=(e&Oe)!==0,n=(e&ft)!==0,o,i=!t.startsWith("<!>");return()=>{if(S)return k(v,null),v;o===void 0&&(o=xe(i?t:"<!>"+t),r||(o=D(o)));var a=n||ot?document.importNode(o,!0):o.cloneNode(!0);if(r){var d=D(a),u=a.lastChild;k(d,u)}else k(a,a);return a}}function It(t,e,r="svg"){var n=!t.startsWith("<!>"),o=(e&Oe)!==0,i=`<${r}>${n?t:"<!>"+t}</${r}>`,a;return()=>{if(S)return k(v,null),v;if(!a){var d=xe(i),u=D(d);if(o)for(a=document.createDocumentFragment();D(u);)a.appendChild(D(u));else a=D(u)}var c=a.cloneNode(!0);if(o){var M=D(c),P=c.lastChild;k(M,P)}else k(c,c);return c}}function qt(t,e){return It(t,e,"svg")}function zt(t=""){if(!S){var e=U(t+"");return k(e,e),e}var r=v;return r.nodeType!==ut?(r.before(r=U()),q(r)):ct(r),k(r,r),r}function Jt(){if(S)return k(v,null),v;var t=document.createDocumentFragment(),e=document.createComment(""),r=U();return t.append(e,r),k(e,r),t}function Xt(t,e){if(S){var r=C;((r.f<)===0||r.nodes.end===null)&&(r.nodes.end=v),Ae();return}t!==null&&t.before(e)}function Gt(t,e){var r=e==null?"":typeof e=="object"?`${e}`:e;r!==(t.__t??(t.__t=t.nodeValue))&&(t.__t=r,t.nodeValue=`${r}`)}function Ct(t,e){return Ve(t,e)}function Kt(t,e){ue(),e.intro=e.intro??!1;const r=e.target,n=S,o=v;try{for(var i=D(r);i&&(i.nodeType!==De||i.data!==dt);)i=ht(i);if(!i)throw ce;Q(!0),q(i);const a=Ve(t,{...e,anchor:i});return Q(!1),a}catch(a){if(a instanceof Error&&a.message.split(`
|
| 2 |
+
`).some(d=>d.startsWith("https://svelte.dev/e/")))throw a;return a!==ce&&console.warn("Failed to hydrate: ",a),e.recover===!1&&_t(),ue(),pt(r),Q(!1),Ct(t,e)}finally{Q(n),q(o)}}const Z=new Map;function Ve(t,{target:e,anchor:r,props:n={},events:o,context:i,intro:a=!0,transformError:d}){ue();var u=void 0,c=vt(()=>{var M=r??e.appendChild(U());Nt(M,{pending:()=>{}},y=>{mt({});var g=Me;if(i&&(g.c=i),o&&(n.$$events=o),S&&k(y,null),u=t(y,n)||{},S&&(C.nodes.end=v,v===null||v.nodeType!==De||v.data!==yt))throw bt(),ce;Et()},d);var P=new Set,O=y=>{for(var g=0;g<y.length;g++){var b=y[g];if(!P.has(b)){P.add(b);var F=Lt(b);for(const ne of[e,document]){var E=Z.get(ne);E===void 0&&(E=new Map,Z.set(ne,E));var x=E.get(b);x===void 0?(ne.addEventListener(b,ve,{passive:F}),E.set(b,1)):E.set(b,x+1)}}}};return O(gt(Fe)),pe.add(O),()=>{var F;for(var y of P)for(const E of[e,document]){var g=Z.get(E),b=g.get(y);--b==0?(E.removeEventListener(y,ve),g.delete(y),g.size===0&&Z.delete(E)):g.set(y,b)}pe.delete(O),M!==r&&((F=M.parentNode)==null||F.removeChild(M))}});return ge.set(u,c),u}let ge=new WeakMap;function Qt(t,e){const r=ge.get(t);return r?(ge.delete(t),r(e)):Promise.resolve()}const Pt="5";var Re;typeof window<"u"&&((Re=window.__svelte??(window.__svelte={})).v??(Re.v=new Set)).add(Pt);export{Xt as a,Bt as b,Jt as c,Yt as d,Ot as e,Ut as f,Ht as g,Kt as h,Vt as i,k as j,qt as k,jt as l,Ct as m,Wt as n,$t as o,Gt as s,zt as t,Qt as u};
|