docs: apply the v1.1 UI-SPEC amendments (A-I) from V1.1-BUILD.md
§2 card geometry contain/blur bullet · §2 loud empty state · §7.3 header pill fresh counter · §10.1 batch-formula-v2 composition sentence · §10.2 dry-catalog note · §10.3 violet SECOND LOOK band + dossier mirror · §10.5 minimal rubric + rubric_debug visibility · §11.3/§11.4 info-first doors and the viewer walk-back chain.
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
### Card geometry
|
||||
- `.card{position:absolute;left:14px;right:14px;top:6px;bottom:34px;border-radius:18px;overflow:hidden;background:var(--card);box-shadow:0 0 0 1px rgba(255,255,255,.07),0 18px 40px rgba(0,0,0,.5);will-change:transform;touch-action:none}`
|
||||
- Photo full-bleed (`object-fit:cover`); bottom gradient 52% tall (transparent → rgba(0,0,0,.55) @55% → .88). Info block at bottom:14px: chips row → title (23px/800, clamp 2 lines) → price (20px/600) → sub (13px, var(--mut)).
|
||||
- Photo fit is **contain** (v1.1): the whole photo is shown uncropped over a soft blurred backdrop of the same image (option C). The backdrop is decorative (blurred, dimmed, overflow-clipped); the sharp photo sits on top with a gentle shadow. The fullscreen viewer already fits; list thumbs stay `cover` (56px).
|
||||
- No photo → placeholder panel: 45° repeating stripes (#141419/#18181f), centered copy: "Photo pending — thumbnail backfill queued".
|
||||
- ⓘ button: 32px circle, top-right of card (rgba(0,0,0,.45), 1px rgba(255,255,255,.25) border) → opens the dossier for that card.
|
||||
- Chips: 11px/600, rgba(255,255,255,.14) bg, radius 999px. Production content: max 3 = seller chip ("Dealer" only when seller_type=dealer) + first 2 extras.
|
||||
@@ -51,6 +52,8 @@
|
||||
### Empty state
|
||||
Dashed panel, copy exactly: **"You're all caught up."** / "New bikes arrive nightly — next import 08:30." / "↺ brings the last card back." Undo restores the last card.
|
||||
|
||||
§2's quiet "You're all caught up." remains only for legacy (pre-section) mode. In section mode, when the deck can serve nothing at all — no fresh listings, no eligible second looks, no wildcards — the deck shows a prominent **"No more new bikes!"** panel with "New bikes arrive nightly — next import 08:30." and, when true, "↺ brings the last card back." This panel supersedes the report card in that state only; the report card remains the end-of-section screen whenever more cards can still be served (v1.1).
|
||||
|
||||
## 3. Dossier (fullscreen sheet) — one sheet, both doors
|
||||
|
||||
Opened from deck ⓘ (or tap) AND list row tap. iOS-sheet geometry: top gap 26px; `border-radius:18px 18px 0 0`; bg #0f0f13; slide-in `.3s cubic-bezier(.2,.8,.3,1)` from `translateY(103%)`. Backdrop: rgba(0,0,0,.38), z 69/70.
|
||||
@@ -87,7 +90,7 @@ Opened from deck ⓘ (or tap) AND list row tap. iOS-sheet geometry: top gap 26px
|
||||
|
||||
1. **Data wiring.** Deck buffer from `GET /api/deck` (limit 40, opaque cursor); refill when buffer <5 and a cursor exists. List from `GET /api/list`. Dossier from the row/card payload. Header from `GET /api/stats`.
|
||||
2. **Swipe persistence.** Commits fire `POST /api/swipe` optimistically — the UI never waits. Failures go to a retry queue (attempts at ~2s/5s/10s, then every 15s while the tab lives); toast "Couldn't save — retrying" on first failure. Undo fires `DELETE /api/swipe/{id}` through the same queue.
|
||||
3. **Badge.** `POST /api/badge/seen` once per session when the deck first renders. Header shows "{live} live" always; "· {new} new" only when new>0.
|
||||
3. **Badge.** `POST /api/badge/seen` once per session when the deck first renders. Header pill shows `{live} live · {fresh} fresh` always, and `· {new} new` only when new > 0. `fresh` = active listings with no swipe (the deck's new-pool predicate) — v1.1.
|
||||
4. **List filters + search** per §4 (mockup had placeholder chips only).
|
||||
5. **Matches real (§5); Trends stays stub.**
|
||||
6. **Images.** Every card/thumb references `/img/{id}` (server: cached file → 302 CDN → 404 → placeholder). The client never hotlinks directly.
|
||||
@@ -119,7 +122,7 @@ Human (parent-run after build): Glenn swipes a real deck from his phone and edit
|
||||
|
||||
- The deck serves ONE section at a time: 25 cards (default). A section is the unit of a swipe burst; when it is exhausted, the next one is forged (with an agent pass in between — §10.2).
|
||||
- Header right side, next to the stats: section progress "7/25" (12px/650, muted; turns `--accent1` at 25/25; fits within the existing header, no layout shift).
|
||||
- Section composition (engine side — RANKING.md): ≤5 SECOND LOOK cards + 2 WILDCARD cards + the remainder new.
|
||||
- Section composition (engine side — RANKING.md): every batch targets 25 cards = fresh first (up to 23 slots) + ≥2 SECOND LOOK cards whenever eligible stock exists + 2 WILDCARD cards; when fresh < 23, second looks fill the remainder up to 25. Re-nope returns stay ≤2 per batch; fresh shortfalls are never padded (v1.1).
|
||||
- ALL §2 mechanics apply unchanged inside a section: 5-deep stack, `.topcard` invariant, no-lock commit, stamps, undo; the buffer refills from the pinned section order.
|
||||
|
||||
### 10.2 Section report + forge
|
||||
@@ -133,13 +136,14 @@ Human (parent-run after build): Glenn swipes a real deck from his phone and edit
|
||||
- **Forge mode `ondemand` (default):** primary button **"Forge next 25"**. Tap → inline status "The agent is reading your strikes…" → new section hydrates. If the pass fails: "Agent offline — forging on the last read." and the forge still lands (fallback profile).
|
||||
- **Forge mode `strict`:** button disabled; caption "Waiting for the nightly read — next section after the agent runs."
|
||||
- Both modes: small toggle row under the actions — "Forge: on demand ⇄ strict" (persists `prefs['forge_mode']`).
|
||||
- When a forged batch reports `new: 0` the report shows: "Catalog fully reviewed — fresh listings arrive with the next 08:30 import; showing second looks." and the forge reads the same note in its status line (v1.1).
|
||||
- Undo from the report screen re-opens the section with the last card (§2 undo semantics).
|
||||
|
||||
### 10.3 Card bands (the ONLY card-face additions)
|
||||
|
||||
- Position: top-left inset (12px); `pointer-events:none`; no layout shift; no effect on stack metrics or drag. 11px/650, letter-spacing .4px, radius 999px, padding 4px 9px, max-width 78%; may wrap to 2 lines.
|
||||
- **WILDCARD — maybe you'll like this one instead** — color `#ffb43a`; bg `rgba(255,180,58,.14)`; border `1px solid rgba(255,180,58,.35)`.
|
||||
- **SECOND LOOK — you {liked|maybed|noped} this** — color `rgba(255,255,255,.85)`; bg `rgba(255,255,255,.10)`; border `1px solid rgba(255,255,255,.22)`.
|
||||
- **SECOND LOOK — you {liked|maybed|noped} this** — color `#c4b5fd`; bg `rgba(124,58,237,.22)`; border `1px solid rgba(167,139,250,.65)`; font-weight 800. Wildcard stays amber (`#ffb43a` family values unchanged) for contrast. The same band renders in the dossier ("info view") when the listing's slot is `return` or `wildcard` (v1.1).
|
||||
- No other text may sit on the card face in v3.
|
||||
|
||||
### 10.4 Tank gauge (match points, non-numeric)
|
||||
@@ -151,9 +155,10 @@ Human (parent-run after build): Glenn swipes a real deck from his phone and edit
|
||||
### 10.5 Dossier — "Why this rank"
|
||||
|
||||
- New block after chips, before "The machine":
|
||||
- Gauge + "{points}/100" (17px/700).
|
||||
- Gauge + "{points}%" (17px/700).
|
||||
- Up to 4 breakdown lines, strongest first, muted: "· {feature} {±n}"; agent contributions render as "· agent: {why} {±n}".
|
||||
- Footer: "from {N} strikes · read {relative}" (N = events count; time = profile version time).
|
||||
- Hidden-by-default detail: the breakdown lines and the "from N strikes" footer render only when `prefs['rubric_debug'] === 'on'` (default `'off'`). With the pref off the block shows **the tank gauge + "{points}%"** only — the minimal rubric display. The toggle lives in the Trends "Actions" area (why-breakdown on ⇄ off, same control style as the forge-mode toggle) — v1.1.
|
||||
- This is the only numeric display in the app (accepted — it exists for audit).
|
||||
|
||||
### 10.6 Trends tab (real)
|
||||
@@ -192,14 +197,15 @@ Top→bottom:
|
||||
|
||||
### 11.3 Fullscreen viewer
|
||||
|
||||
- Tap the CENTER third → fullscreen viewer opens on the photo currently shown.
|
||||
- Tap the photo in the dossier hero → fullscreen viewer opens (starts at photo 0). Direct card→viewer is retired (see §11.4); the viewer is reached from the dossier (v1.1).
|
||||
- Viewer: horizontal swipe = prev/next (clamp); pinch-zoom + double-tap = zoom (1×–~4×); segment bars stay visible; close via ✕ (36px, top-left) or pull-down (grabber or photo, `dy > 110` — the §3 idiom). Closing returns to the same card, same photo position; deck state untouched.
|
||||
- Also opens from the dossier hero (starts at photo 0). Same sheet geometry as the dossier (z-band 69/70), photo-first: no body, no actions row.
|
||||
- Viewer close (✕, pull-down, or Esc) returns to the dossier; the next Esc returns to the deck (v1.1).
|
||||
|
||||
### 11.4 Doors (supersedes the old tap→dossier)
|
||||
|
||||
- ⓘ (top-right of card) → dossier (unchanged). The card face now owns: **sides = flip, center = fullscreen**; the "tap anywhere → dossier" gesture is RETIRED.
|
||||
- The dossier stays reachable from ⓘ and from list rows (§3/§4 unchanged).
|
||||
- ⓘ (top-right of card) → dossier (unchanged). The card face now owns: **sides = flip, center = dossier (bike data first)**; the "tap anywhere → dossier" gesture is RETIRED and the B1 center-tap→fullscreen gesture is RETIRED in favour of it (v1.1).
|
||||
- Tapping the photo in the dossier hero opens the fullscreen image viewer (§11.3). Viewer close (✕, pull-down, or Esc) returns to the dossier; the next Esc returns to the deck. The dossier stays reachable from ⓘ and from list rows (§3/§4 unchanged).
|
||||
|
||||
### 11.5 Repeat rotation
|
||||
|
||||
|
||||
Reference in New Issue
Block a user