cocottetech/@platform/codebase/@features/ai-copilot/docs/00-system-visual-system.md

155 lines
8.9 KiB
Markdown
Raw Normal View History

# F — Visual system (tokens + components)
## Goal
Establish the design language that every surface (iOS, iPad, macOS, web) inherits. Tokens-first so a renamed brand or palette tweak doesn't ripple through every screen.
## Designer skim
- **Headline UX**: Tokens-first design language. SwiftUI-native primitives where possible (system fonts, SF Symbols, system materials). WCAG AA contrast min. Light + dark from day one.
- **Sections (7)**: F1 color · F2 typography · F3 spacing+radius · F4 motion+haptics · **F5 24-surface iconography** (ag sub-blocks: N1 content / N2 directories / N3 brand sites / N4 channels / N5 commerce / fallback / geometry-coded distinction) · F6 empty-loading-error · F7 component library.
- **Foundation**: lives in `00-system/` alongside voice. Every brief inherits from here.
- **Blocking Qs**: [OPEN-DECISIONS.md](./OPEN-DECISIONS.md) → VS-Q1 stakes-high-vs-destructive-red collision.
## Constraints
- Inherit `lilith-messenger-ios/LilithMessenger/Design/{Theme,Typography}.swift` as the **starting point**, then specialize for CocotteAI's identity.
- SwiftUI-native primitives where possible (system fonts, SF Symbols, system materials).
- WCAG AA contrast minimum in light AND dark.
## Token layers (ordered from primitive to composite)
### F1 — Color tokens
- **Primitives**: a 12-step neutral ramp (light + dark), a 12-step accent ramp, semantic colors.
- **Accent** proposal: warm rose — Quinn's talent-tier accent. Designer to validate against the **Cocotte** umbrella brand palette before locking. (Cocotte is the customer-facing brand; Demimonde is back-office and has no customer-facing palette.)
- **Semantic stakes colors**: low = neutral-500 (gray), medium = amber-500, high = rose-600. Must work as text + as background + as dot/chip in both modes.
- **Background materials**: app background, sheet background, card surface, elevated card.
### F2 — Typography
- Display, Title, Headline, Body, Body-emphasized, Caption, Footnote.
- All scales use SF Pro Display + SF Pro Text.
- Dynamic Type support (iOS accessibility).
### F3 — Spacing + radius
- 4pt base grid; spacing scale {2, 4, 8, 12, 16, 24, 32, 48}.
- Radius scale {4 (chips), 12 (cards), 20 (sheets), full (avatars)}.
### F4 — Motion + haptics
- Approval swipe: 200ms easeOut animation, light haptic on threshold crossing, success haptic on commit.
- Streaming reply tokens: stagger 16ms, fade-in 100ms.
- Sheet present: native iOS sheet timing; do not override.
- High-stakes notification arrival: notification haptic + display haptic.
### F5 — Surface iconography
Per-surface visual marks for the **canonical 24-surface roster** in [brief O](./O-surfaces-roster.brief.md). Goal: at chip size (16pt) Quinn recognizes the surface in <200ms without reading text. Where a brand mark is unrecognizable at chip size, fall back to a 2-letter monogram on a category-tinted square.
**Geometry rules (all marks):**
- Circular bounding box, 16pt (chip) / 20pt (card) / 24pt (drawer header).
- 1pt stroke on the bounding ring in light mode; 0pt in dark (relies on chip background).
- Always paired with `SurfaceChip` (F7) — never rendered standalone without a display name nearby on first introduction.
#### F5a — N1 content surfaces (brand-mark; recognizable)
| ID | Mark | Light color | Dark color | Notes |
|---|---|---|---|---|
| `onlyfans` | OF wordmark in circle | `#00AFF0` (OF blue) | same | Recognizable. |
| `x` | X glyph | `#000` | `#FFF` | Mono. |
| `instagram` | gradient ring + camera | brand gradient | brand gradient | Don't reduce to mono — recognition collapses. |
| `tiktok` | musical-note glyph | `#000` | `#FFF` w/ cyan/red shadow | Mono with brand accent. |
| `threads` | @ mark | `#000` | `#FFF` | Cheap to render; cross-post sibling of `x`. |
| `bluesky` | butterfly | `#1185FE` | same | New 2026. |
| `reddit` | snoo silhouette | `#FF4500` | same | High recognition. |
| `fansly` | "F" monogram | `#0F8` (fansly green) | same | OF alternative; lower brand recognition — accept word-mark fallback at 16pt. |
| `youtube` | play-triangle | `#FF0000` | same | SFW only. |
| `twitch` | speech-bubble glyph | `#9146FF` | same | SFW only. |
| `facebook` | "f" wordmark | `#1877F2` | mono in dark | Defensive listing; rarely shown. |
#### F5b — N2 escort directories (12 surfaces — most have weak brand recognition)
Default for this category: **2-letter monogram on category-tinted square** (radius 4pt, neutral-700 background in light, neutral-200 in dark; foreground = accent-rose for "live" directories, neutral-400 for "pending/blocked"). Reserve brand-mark rendering for the two anchor directories.
| ID | Mark | Strategy |
|---|---|---|
| `tryst` | "T" in rose square + verified-checkmark glyph | Anchor; gets a distinct mark. |
| `ts4rent` | "T4" monogram | Anchor; distinct. |
| `seeking` | "S" monogram | Sugar-dating context — give it its own neutral-blue tint to separate from N2 escort directories visually (per brief O open question). |
| `privatedelights` | "PD" monogram | Pending verification — render in pending-gray. |
| `tsescorts` | "TS" monogram | |
| `adultsearch` | "AS" monogram | |
| `adultlook` | "AL" monogram | |
| `eros` | "ER" monogram | **Blocked-on-legal-name-change** state — render at reduced opacity 0.5 + lock glyph overlay. |
| `eroticmonkey` | "EM" monogram | |
| `skipthegames` | "SKG" trigraph (exception: 3 chars allowed when "STG" reads worse) | |
| `megapersonals` | "MP" monogram | |
| `ts.live` | "ts" lowercase monogram + `.live` superscript | Distinguish from `tsescorts` and `ts4rent` — three "ts" prefixes in the roster. |
**Status-state overlays (apply on top of any N2 mark):**
- Live → no overlay.
- Pending verification → 0.7 opacity + amber dot top-right.
- Blocked / suspended → 0.5 opacity + lock glyph.
- Not started → 0.4 opacity + dashed bounding ring.
#### F5c — N3 brand sites (Quinn-owned)
Quinn-owned brand sites get a **distinct shape (rounded square, not circle)** to telegraph "this is ours, not external." Foreground = brand-rose; background = neutral-100/900.
| ID | Mark |
|---|---|
| `transquinnftw.com` | tqftw monogram |
| `adult-therapy-tours.com` | "ATT" trigraph |
| `futa-waifu-tour` | "FWT" trigraph |
#### F5d — N4 channels (messaging, not posting)
Channels are NOT `surface_kind` (per brief O); they render with a different component (`ChannelChip`, sibling to `SurfaceChip`) that uses **rounded-square** geometry to visually separate "channel-of-arrival" from "surface-of-origin" — critical for the unified inbox in brief P.
| ID | Mark | Notes |
|---|---|---|
| `imessage` | SMS bubble glyph (iOS) | System SF Symbol `message.fill`. |
| `signal` | Signal logo | Encrypted-channel marker. |
| `telegram` | paper-plane | |
| `discord` | discord-mark | |
| `email` | envelope SF Symbol | Plus per-mailbox tint when from multiple Proton accounts (see brief P). |
| `sniffies` | "Sn" monogram | Light surface. |
#### F5e — N5 commerce (P5+, enumerated only)
Commerce providers render as monogram tiles in green tint when Quinn surfaces them; otherwise hidden. Enumerated for completeness; no P0 design effort.
#### F5f — Fallback for unknown / new surfaces
When a `surface_kind` value arrives that the iconography registry doesn't know (forward-compat with brief O additions), render: **neutral-tinted circle + first 2 alphanumeric chars of the ID** (uppercase). Never break the layout; never throw.
#### F5g — Surface-vs-channel visual distinction (summary)
| Geometry | Used for | Examples |
|---|---|---|
| Circle | N1 content + N2 directories (posting/listing surfaces) | OF, Tryst |
| Rounded-square (Quinn-owned tint) | N3 brand sites | tqftw.com |
| Rounded-square (channel tint) | N4 channels | iMessage, email |
| Monogram tile (green) | N5 commerce | Venmo |
This geometry-coded distinction lets Quinn see at a glance whether a card is **about a place she posts** vs **about a place she receives** vs **about a place she gets paid** — without reading.
### F6 — Empty / loading / error states
- Standard empty illustration set (one icon family, branded).
- Loading: thin determinate progress when measurable, skeleton on lists, spinner only as last resort.
- Error: standard error card with retry affordance; never a system alert.
### F7 — Component library
- `ApprovalCard` (all variants from brief A)
- `StakesBadge`, `ConfidenceBar`
- `SurfaceChip` (uses F5 iconography)
- `SpecialistMention` (inline tappable text)
- `Drawer` (sheet wrapper, brief B)
- `VoiceMicButton` (push-to-talk + long-press hands-free)
- `PersonaFacetEditor` (chip + free-text inputs for facet JSON)
## Out of scope
- 3D / @chobit avatar visuals (P5+).
- Marketing site visuals (cocottetech.com is a different brief).
## Open questions
- Should stakes-high red overlap with destructive-action red (reject swipe color)? Argue against — semantic collision.
- Custom font, or stay system? System wins on Dynamic Type + accessibility for free.
- Iconography for V3 specialists themselves (a per-specialist mark) — needed P0 or P1?