# 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** (a–g 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?