# Lotus Chat — Bug Report & Technical Audit

**Date:** June 2026

This document tracks identified bugs, edge cases, and architectural discrepancies found during the audit of the Lotus Chat codebase. Recommended fixes are provided for each item.

---

## 🚩 Critical & UI Bugs

### 12. PiP Mute Icon Misidentifies Whose Mic Is Muted

- **File:** `cinny/src/app/components/CallEmbedProvider.tsx`
- **Status:** **FIXED ⚠️ UNTESTED** — needs verification in a live call with at least one other participant who mutes/unmutes
- **Issue:** The muted-mic badge in the Picture-in-Picture window used `useRemoteAllMuted` (fires when ANY remote participant is muted) and rendered in the bottom-left corner — the conventional position for "YOUR" mic status. Users read it as their own mic being muted.
- **Root Cause:** `PipMuteOverlay` was triggering on remote-mute events while displaying in a position that implies local-user status.
- **Fix Applied:**
  - **Bottom-left badge** now shows only when the LOCAL user's mic is muted (checked via `!controlState.microphone` from `useCallControlState`). Includes "You" label to make it unambiguous. Uses `color.Critical.Main`.
  - **Top-right badge** (new) shows "All muted" in `color.Warning.Main` when all remote participants are muted — positioned and labeled so it's clearly about other people, not the local user.
  - Both badges use `aria-label` / `title` for accessibility.

---

### 1. No Camera Focus During Screenshare

- **File:** `cinny/src/app/features/call/CallControls.tsx`
- **Status:** **OPEN**
- **Issue:** Automatic screenshare spotlighting forces primary display override, preventing users from manually focusing on camera feeds.
- **Root Cause:** Current spotlighting logic prioritizes active screenshare streams over manual participant selections, effectively ignoring or overriding user-initiated focus states.
- **Proposed Fix:** Introduce a manual 'Focus' state that takes precedence over automatic screenshare spotlighting, implemented via a toggle/click UI on participant tiles. Update the video renderer to respect this manual override.

### 2. Chat Background Animation Flickering

- **File:** `cinny/src/app/features/lotus/chatBackground.ts`
- **Status:** **FIXED ⚠️ UNTESTED** — needs verification on a real device with an animated background active
- **Issue:** Animated background properties cause visible flickering on message text and the composer area, particularly on browsers/GPUs susceptible to repaint-induced artifacts.
- **Root Cause:** Animation triggers excessive repaints or layout recalculations on descendant elements, likely due to animating non-GPU accelerated properties on parent containers without proper rendering context isolation.
- **Fix Applied:** `getChatBg()` now injects `willChange: 'background-position'` and `contain: 'paint'` for any animated variant. This promotes the element to its own compositor layer and isolates repaints from descendants. Background-position animation is already GPU-hinted on modern browsers; `contain: paint` prevents descendant elements from being invalidated during each frame.

### 3. Avatar Decorations in Element Call

- **File:** `cinny/src/app/components/avatar-decoration/AvatarDecoration.tsx`
- **Status:** **OPEN**
- **Issue:** Avatar decorations are failing to render within the call/room interface member lists.
- **Root Cause:** Likely a mismatch between the expected `member` object structure required by the `AvatarDecoration` component and the data actually provided by the call/room UI components. Matrix event data for decorations might not be propagating correctly to these UI member objects.
- **Proposed Fix:** Analyze the data propagation chain from Matrix events to the member object in `cinny/src/app/components/call` and `room`, ensuring that decoration-related properties are correctly mapped and passed to the `AvatarDecoration` component.

### 4. DM and Group Message Calls

- **File:** `cinny/src/app/components/CallEmbedProvider.tsx`
- **Status:** **PARTIALLY FIXED ⚠️ UNTESTED** — Volume control added. Remaining: ringtone selection, suppression during active calls.
- **Issue:** Incoming call ringtone is hardcoded, lacks volume control, and is suppressed if the user is already in an active call.
- **Root Cause:** Ringing logic is tightly coupled to `RTCNotification` events in `CallEmbedProvider.tsx`, using a hardcoded audio file path. It lacks an abstraction for sound management or user-configurable settings for ringtones/volumes.
- **Fix Applied:** Added `ringtoneVolume` setting (0–100, default 70). `IncomingCall` reads this setting and applies `audioElement.volume = ringtoneVolume / 100` before `play()`. Slider added to Settings → General → Calls section.
- **Remaining:** (a) Ringtone selection (still hardcoded to `call.ogg`); (b) Suppression during active calls — not investigated.

### 5. Seasonal Themes and Chat Backgrounds Design

- **File:** `cinny/src/app/hooks/useTheme.ts`, `cinny/src/app/features/lotus/chatBackground.ts`
- **Status:** **OPEN**
- **Issue:** Basic CSS or random moving lines are insufficient for high-fidelity wallpaper/theming. They lack professional design theory, coherence, and aesthetic depth.
- **Root Cause:** Current implementation relies on basic CSS, lacks advanced design theory, and does not leverage modern, performant CSS wallpaper techniques.
- **Proposed Fix (Extreme Depth Redesign):**
  - **Research-Backed Implementation:** Implement advanced design techniques (layered `oklch` gradients, `backdrop-filter` for refractive "liquid glass" effects, GPU-accelerated `transform` animations) to create living, breathing backgrounds.
  - **Performance Optimization:** Ensure all animations strictly use compositor-thread properties (`transform`, `opacity`) and apply `contain: paint` / `will-change: transform` to prevent layout thrashing/flickering.
  - **Design Resources (Examples/Inspiration):**
    - [Uiverse.io Patterns](https://uiverse.io/patterns)
    - [MagicPattern CSS Backgrounds](https://www.magicpattern.design/tools/css-backgrounds)
    - [Prismic Blog: CSS Background Effects](https://prismic.io/blog/css-background-effects)
    - [CSS-Pattern.com](https://css-pattern.com) (Pure CSS pattern library)
    - [BGJar](https://bgjar.com) (Performance-focused generators)
  - **Goal:** Treat each theme/background as a week-long development sprint to ensure professional polish, WCAG AA contrast compliance for overlaying UI, and seamless integration with the Lotus TDS.

### 6. Exclusive Background vs. Seasonal Choice

- **File:** `cinny/src/app/state/settings.ts`
- **Status:** **FIXED ⚠️ UNTESTED** — needs verification: (a) pick a background, confirm seasonal theme auto-clears; (b) pick a seasonal theme, confirm background auto-clears; (c) set both via old localStorage data and reload, confirm SeasonalEffect guard suppresses the overlay
- **Issue:** Concurrent application of both Chat Backgrounds and Seasonal Themes causes visual clutter and high GPU usage.
- **Root Cause:** These are currently handled as independent settings in the `settingsAtom` and applied simultaneously without mutual exclusion.
- **Fix Applied:** Mutual exclusion enforced at two layers: (1) `General.tsx` — ChatBgGrid clears seasonalThemeOverride→'off' when any non-'none' background is picked; SeasonalBgGrid clears chatBackground→'none' when any real seasonal theme is selected. (2) `SeasonalEffect.tsx` — runtime guard returns null if `chatBackground !== 'none'`, protecting against legacy persisted state.

### 7. Tiny Touch Targets in Composer Toolbar

- **File:** `cinny/src/app/features/room/RoomInput.tsx`
- **Status:** **FIXED ⚠️ UNTESTED** — needs verification on a real mobile device: open composer, confirm all toolbar buttons are tappable without mis-taps
- **Issue:** Toolbar buttons have hit areas smaller than the WCAG-recommended 44x44px for touch, hindering mobile accessibility.
- **Fix Applied:** Added `touchTarget = { minWidth: '44px', minHeight: '44px' }` computed from `mobileOrTablet()` and applied as `style={touchTarget}` to all 8 composer toolbar `IconButton` elements (attach, format, sticker, emoji, GIF, location, poll, schedule, send).

### 8. Horizontal Overflow in Room Settings

- **File:** `cinny/src/app/components/page/style.css.ts`
- **Status:** **FIXED ⚠️ UNTESTED** — needs verification: open Room Settings on a narrow mobile screen, confirm nav panel fills full width and no horizontal scrollbar appears
- **Issue:** Wide tables and input elements in room settings cause horizontal overflow on mobile viewports.
- **Fix Applied:** Added `@media (max-width: 750px) { width: '100%' }` to both `'400'` and `'300'` size variants of the `PageNav` vanilla-extract recipe in `style.css.ts`.

### 9. Modal Float-Style Responsiveness

- **File:** Multiple modal files
- **Status:** **FIXED ⚠️ UNTESTED** — needs verification by opening each modal on a real mobile device
- **Issue:** Modals appear as floating boxes on mobile, creating navigation and readability challenges.
- **Fix Applied:** Created `useModalStyle(desktopMaxWidth)` hook (`src/app/hooks/useModalStyle.ts`) that returns fullscreen styles on mobile (no border-radius, no max-width, `height: 100%`) and desktop box styles otherwise. Applied to all 22+ modal files: `LeaveRoomPrompt`, `LeaveSpacePrompt`, `ReportRoomModal`, `ReportUserModal`, `DeviceVerification`, `InviteUserPrompt`, `LogoutDialog`, `DeviceVerificationSetup`, `DeviceVerificationReset`, `JoinAddressPrompt`, `JumpToTime`, `EditHistoryModal`, `ForwardMessageDialog`, `RemindMeDialog`, `CreateRoomModal`, `CreateSpaceModal`, `ScheduleMessageModal`, `PollCreator`, `AddExistingModal`, `RoomEncryption`, `RoomUpgrade`, `Modal500`, `ReadReceiptAvatars`, `RoomTopicViewer`.
- **Note:** `UIAFlowOverlay` already fullscreen via `<Overlay>` — no change needed. `JoinRulesSwitcher`/`RoomNotificationSwitcher` are dropdowns, not modals.

### 10. Composer Keyboard Obscurity

- **File:** `src/index.css`
- **Status:** **FIXED ⚠️ UNTESTED** — needs verification on iOS Safari specifically (the worst offender); on Android Chrome `100dvh` has been standard since Chrome 108
- **Issue:** The chat composer is often partially or fully obscured by the virtual keyboard on mobile.
- **Fix Applied:** Added `height: 100dvh` (dynamic viewport height) to `html` alongside the existing `height: 100%` fallback. `dvh` updates when the software keyboard appears, ensuring the layout shrinks correctly and the composer stays visible.

### 11. Inline Jotai atom creation

- **File:** `cinny/src/app/hooks/useSpaceHierarchy.ts`
- **Status:** **FALSE POSITIVE — CLOSED**
- **Issue:** Inline Jotai atom creation in a hook risks re-rendering components unnecessarily.
- **Resolution:** `useState(() => atom(...))` IS the correct Jotai pattern for local stable atom references. The factory function form of `useState` ensures the atom is created only once per component mount. No change warranted.

---

## 📦 Barrel File Audit

| File Path                                   | Note                       | Status |
| :------------------------------------------ | :------------------------- | :----- |
| `cinny/src/app/plugins/call/index.ts`       | Extensive `export *` usage | OPEN   |
| `cinny/src/app/plugins/text-area/index.ts`  | Extensive `export *` usage | OPEN   |
| `cinny/src/app/components/message/index.ts` | Extensive `export *` usage | OPEN   |

---

## 🔍 Technical & Performance Refinements

| Category                | Issue Description                                                                                                                                        | File Path                                       | Status                                                                                                                                                                                                                                                                                                                |
| :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| State Sync              | Fire-and-forget network call to set offline presence during `pagehide` event may not complete reliably, potentially causing UI drift in presence status. | `cinny/src/app/hooks/usePresenceUpdater.ts`     | OPEN                                                                                                                                                                                                                                                                                                                  |
| State Sync              | Fire-and-forget network call `setPresence().catch(...)` suppresses errors, meaning the app may falsely assume presence update success.                   | `cinny/src/app/hooks/usePresenceUpdater.ts`     | OPEN                                                                                                                                                                                                                                                                                                                  |
| Memory Leak             | Decrypted Media Memory Leak (Gallery & Lightbox) due to missing virtualization and blob revocation.                                                      | `cinny/src/app/features/room/MediaGallery.tsx`  | PARTIALLY FIXED ⚠️ UNTESTED — Blob revocation was already correct; added `enabled` param to `useDecryptedMediaUrl` and `useNearViewport(300px)` to each `GalleryTile` to gate decryption until near-viewport, reducing burst on pagination. True virtualization (windowing) deferred — requires significant refactor. |
| Data Persistence        | Scheduled Messages are ephemeral (lost on refresh) due to fragile `localStorage` parsing.                                                                | `cinny/src/app/state/scheduledMessages.ts`      | FIXED — now uses `atomWithStorage` + `createJSONStorage` (Jotai's built-in persistence with error-safe JSON parsing)                                                                                                                                                                                                  |
| Memory Leak             | Potential memory leak due to uncleaned `handleMouseMove` listener in `usePan`.                                                                           | `cinny/src/app/hooks/usePan.ts`                 | FALSE POSITIVE — `usePan` already uses `attachedRef` to track listeners and cleans them up in an unmount `useEffect`. No change needed.                                                                                                                                                                               |
| Asset Optimization      | Large unoptimized media asset (213KB) found in `public/res`.                                                                                             | `public/res/Lotus.png`                          | OPEN                                                                                                                                                                                                                                                                                                                  |
| Data Persistence        | Non-atomic `localStorage` updates in session management can lead to inconsistent state.                                                                  | `cinny/src/app/state/sessions.ts`               | OPEN                                                                                                                                                                                                                                                                                                                  |
| Data Persistence        | Lack of cross-tab synchronization for `localStorage` updates in session management risks race conditions.                                                | `cinny/src/app/state/sessions.ts`               | OPEN                                                                                                                                                                                                                                                                                                                  |
| Network Resilience      | `uploadContent` lacks retry logic, failing immediately upon network error.                                                                               | `cinny/src/app/utils/matrix.ts`                 | OPEN                                                                                                                                                                                                                                                                                                                  |
| Network Resilience      | `rateLimitedActions` uses basic retry logic without exponential backoff, which may exacerbate 429 issues.                                                | `cinny/src/app/utils/matrix.ts`                 | FIXED — fallback delay now uses capped exponential backoff (`min(1000 * 2^retryCount, 30_000)ms`) when server doesn't send `Retry-After`; server header still takes precedence via `getRetryAfterMs()`.                                                                                                               |
| Matrix Event Robustness | `useMatrixEventRenderer` handles unknown events gracefully by returning `null`, which may hide potentially important unrendered data.                    | `cinny/src/app/hooks/useMatrixEventRenderer.ts` | OPEN                                                                                                                                                                                                                                                                                                                  |
| Data Contract           | `MatrixError` instantiation with `UploadResponse` might be brittle.                                                                                      | `cinny/src/app/utils/matrix.ts`                 | OPEN                                                                                                                                                                                                                                                                                                                  |
| Type Safety             | `addRoomIdToMDirect` uses `as any` cast for `AccountDataEvent.Direct`, bypassing type contract validation.                                               | `cinny/src/app/utils/matrix.ts`                 | OPEN                                                                                                                                                                                                                                                                                                                  |
| Robustness              | `rateLimitedActions` relies on `MatrixError.httpStatus` which might not exist on all error variants.                                                     | `cinny/src/app/utils/matrix.ts`                 | FALSE POSITIVE — `MatrixError.httpStatus` is defined as `readonly httpStatus?: number` in `matrix-js-sdk/lib/http-api/errors.d.ts`. It is optional (not on all instances) but the `?.` optional chain already guards against undefined. No change needed.                                                             |
| Type Contract           | Custom types in `cinny/src/types/matrix` mirror SDK types instead of using them, risking drift and contract mismatches.                                  | `cinny/src/types/matrix/`                       | OPEN                                                                                                                                                                                                                                                                                                                  |

## 🏗️ Architectural & Hygiene Audit

| Category | Issue Description                                                | File Path | Status |
| :------- | :--------------------------------------------------------------- | :-------- | :----- |
| Hygiene  | No stale development notes or TypeScript strictness issues found | N/A       | OPEN   |

---

## 🏗️ TDS Compliance & Styling Issues

| Issue Description                                                                            | File Path                                                                                                                                                                                                                                                                                                                                    |
| :------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Hardcoded inline style `cursor: 'pointer'`                                                   | `cinny/src/app/plugins/react-custom-html-parser.tsx`                                                                                                                                                                                                                                                                                         |
| Hardcoded color `#00D4FF`, `#FFB300` ✅ **VERIFIED COMPLIANT**                               | `cinny/src/app/components/event-readers/EventReaders.tsx`                                                                                                                                                                                                                                                                                    |
| Hardcoded color `#EE1D52`, `#9146ff`, `#ff4500`, `#cb3837`, `#f48024` ⚠️ **BRAND EXCEPTION** | `cinny/src/app/components/url-preview/UrlPreviewCard.tsx` + `UrlPreview.css.tsx` — official third-party brand colors in SVG logos and site badge backgrounds; cannot convert to CSS variables without inventing new tokens (violates TDS rule 3)                                                                                             |
| Massive number of hardcoded `backgroundColor` values ⚠️ **PATTERN CONTENT EXCEPTION**        | `cinny/src/app/features/lotus/chatBackground.ts` — each background's base color is aesthetic content that defines the pattern identity; converting requires inventing 40+ CSS variables (violates TDS rule 3) or using CSS4 `relative-color-syntax` in inline styles (insufficient browser support); these are visual content, not UI chrome |
| Hardcoded colors `#00FF88`, `#FF6B00` ✅ **VERIFIED COMPLIANT**                              | `cinny/src/app/features/call/CallControls.tsx`                                                                                                                                                                                                                                                                                               |
| Hardcoded fallback hexes in toast colors ✅ **FIXED**                                        | `cinny/src/app/features/toast/LotusToastContainer.tsx`                                                                                                                                                                                                                                                                                       |

---

## 🌐 Localization, Accessibility & Performance

| Category      | Issue Description                                                                                                                                                   | File Path                                                      | Status                                                                                                                                                                                                   |
| :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Localization  | Hardcoded UI string: "Chat Room"                                                                                                                                    | `src/app/components/create-room/CreateRoomTypeSelector.tsx`    | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Messages, photos, and videos."                                                                                                                | `src/app/components/create-room/CreateRoomTypeSelector.tsx`    | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Voice Room"                                                                                                                                   | `src/app/components/create-room/CreateRoomTypeSelector.tsx`    | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Live audio and video conversations."                                                                                                          | `src/app/components/create-room/CreateRoomTypeSelector.tsx`    | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Download"                                                                                                                                     | `src/app/components/image-viewer/ImageViewer.tsx`              | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Open Location"                                                                                                                                | `src/app/components/message/MsgTypeRenderers.tsx`              | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Thread"                                                                                                                                       | `src/app/components/message/Reply.tsx`                         | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "View"                                                                                                                                         | `src/app/components/message/content/ImageContent.tsx`          | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Spoiler"                                                                                                                                      | `src/app/components/message/content/ImageContent.tsx`          | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Retry"                                                                                                                                        | `src/app/components/message/content/ImageContent.tsx`          | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Close"                                                                                                                                        | `src/app/components/DeviceVerification.tsx`                    | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Accept"                                                                                                                                       | `src/app/components/DeviceVerification.tsx`                    | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "They Match"                                                                                                                                   | `src/app/components/DeviceVerification.tsx`                    | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Okay"                                                                                                                                         | `src/app/components/DeviceVerification.tsx`                    | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Join Server"                                                                                                                                  | `src/app/components/url-preview/UrlPreviewCard.tsx`            | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Invite"                                                                                                                                       | `src/app/components/invite-user-prompt/InviteUserPrompt.tsx`   | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Files"                                                                                                                                        | `src/app/components/upload-board/UploadBoard.tsx`              | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Send"                                                                                                                                         | `src/app/components/upload-board/UploadBoard.tsx`              | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Upload Failed"                                                                                                                                | `src/app/components/upload-board/UploadBoard.tsx`              | OPEN                                                                                                                                                                                                     |
| Localization  | Hardcoded UI string: "Password"                                                                                                                                     | `src/app/components/uia-stages/PasswordStage.tsx`              | OPEN                                                                                                                                                                                                     |
| Bundle Size   | Large unoptimized media asset (213KB)                                                                                                                               | `public/res/Lotus.png`                                         | OPEN                                                                                                                                                                                                     |
| Matrix Logic  | Inefficient repeated `mx.getRoom()` calls in component render loops                                                                                                 | `src/app/features/lobby/Lobby.tsx`                             | OPEN                                                                                                                                                                                                     |
| Matrix Logic  | Inefficient repeated `mx.getRoom()` calls in component render loops                                                                                                 | `src/app/components/emoji-board/EmojiBoard.tsx`                | OPEN                                                                                                                                                                                                     |
| Performance   | Numerous event handlers (e.g., handleUserClick, handleReplyClick) lack `useCallback`, leading to unnecessary re-renders of message components.                      | `cinny/src/app/features/room/RoomTimeline.tsx`                 | OPEN                                                                                                                                                                                                     |
| Performance   | The `submit` function and file handling callbacks (e.g., handleSendUpload) are re-created on every render, causing re-renders of the editor and toolbar components. | `cinny/src/app/features/room/RoomInput.tsx`                    | OPEN                                                                                                                                                                                                     |
| Accessibility | `button` for edit history lacks `aria-label`                                                                                                                        | `cinny/src/app/components/message/content/FallbackContent.tsx` | FIXED ⚠️ UNTESTED — added `aria-label="View edit history"`                                                                                                                                               |
| Accessibility | `button` for reaction lacks `aria-label`                                                                                                                            | `cinny/src/app/components/message/Reaction.tsx`                | **FIXED ⚠️ UNTESTED** — `Reaction` component now computes `aria-label="{shortcode} reaction, N people"` internally using `getShortcodeFor`; custom (mxc://) emoji falls back to "custom emoji reaction". |
| Accessibility | `button` for ThreadIndicator lacks `aria-label`                                                                                                                     | `cinny/src/app/components/message/Reply.tsx`                   | FIXED ⚠️ UNTESTED — added `aria-label="View thread"`                                                                                                                                                     |
| Accessibility | `button` for ReplyLayout lacks `aria-label`                                                                                                                         | `cinny/src/app/components/message/Reply.tsx`                   | FIXED ⚠️ UNTESTED — added `aria-label="Jump to original message"`                                                                                                                                        |

---

## 🔧 Infrastructure, DevEx & Type Safety

| Category       | Issue Description                                                                                            | File Path                                            | Status |
| :------------- | :----------------------------------------------------------------------------------------------------------- | :--------------------------------------------------- | :----- |
| Dependencies   | `lodash` pinned to non-existent version `4.18.1`                                                             | `cinny/package.json`                                 | OPEN   |
| Dependencies   | Various pinned versions of `@atlaskit`, `matrix-js-sdk`                                                      | `cinny/package.json`                                 | OPEN   |
| Dependencies   | `matrix-js-sdk` pinned to Release Candidate (`41.6.0-rc.0`)                                                  | `cinny/package.json`                                 | OPEN   |
| Dependencies   | Unstable/experimental versions for build tools (`vite` 8.0.14, `typescript` 6.0.3, `eslint` 9.39.4)          | `cinny/package.json`                                 | OPEN   |
| CI/CD          | `package-manager-cache` set to `false`                                                                       | `cinny/.github/workflows/build-pull-request.yml`     | OPEN   |
| CI/CD          | Inefficient sequential execution in deployment                                                               | `cinny/.github/workflows/prod-deploy.yml`            | OPEN   |
| CI/CD          | Aggressive 1-minute timeout for Netlify deploy                                                               | `cinny/.github/workflows/prod-deploy.yml`            | OPEN   |
| DevEx          | Stale upstream bug tracker link/donations/CLA                                                                | `cinny/CONTRIBUTING.md`                              | OPEN   |
| DevEx          | Alignment issue between README and CONTRIBUTING                                                              | `cinny/README.md`                                    | OPEN   |
| Testing        | No evident automated testing configuration/files                                                             | `cinny/src/`                                         | OPEN   |
| Type Safety    | Extensive use of `as any` type assertions                                                                    | `cinny/src/`                                         | OPEN   |
| Security       | Hardcoded public CDN URL; consider moving to environment variable                                            | /root/code/cinny/scripts/syncDecorations.mjs         | OPEN   |
| Architecture   | Modifying node_modules directly is brittle; use patch-package instead                                        | /root/code/cinny/scripts/patch-folds.mjs             | OPEN   |
| Robustness     | Missing security headers (HSTS, CSP, etc.) and inefficient asset serving using rewrites instead of try_files | /root/code/cinny/contrib/nginx/cinny.domain.tld.conf | OPEN   |
| Robustness     | Incomplete documentation/placeholder path in Caddyfile                                                       | /root/code/cinny/contrib/caddy/caddyfile             | OPEN   |
| Matrix SDK     | Inefficient listener management (`setMaxListeners: 150`) and incomplete SDK state transition handling.       | `src/client/initMatrix.ts`                           | OPEN   |
| PWA Robustness | Service worker lacks caching strategy for application assets, resulting in no offline capability.            | `cinny/src/sw.ts`                                    | OPEN   |
| PWA Integrity  | `manifest: false` in `vite.config.js` might prevent correct PWA installation if not handled externally.      | `cinny/vite.config.js`                               | OPEN   |
| PII Leakage    | Potential PII exposure via console.error (parameter e likely contains event data).                           | `cinny/src/app/plugins/call/CallEmbed.ts`            | OPEN   |
| PII Leakage    | Potential PII exposure via console.warn (parameter imgError/videoError/thumbError object).                   | `cinny/src/app/features/room/msgContent.ts`          | OPEN   |
| PII Leakage    | Potential PII exposure via console.error (parameter e likely contains event data).                           | `cinny/src/app/features/room/RoomInput.tsx`          | OPEN   |

## 🏗️ Architectural & Resilience Audit

| Category                 | Issue Description                                                                                                                                                                                            | File Path                                               | Status |
| :----------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------ | :----- |
| Element Call Integration | Lacks robust iframe failure monitoring beyond initial 'preparing' event; can result in a permanently hung 'Loading...' state with no user-visible error or recovery path.                                    | `src/app/plugins/call/CallEmbed.ts`                     | OPEN   |
| Component Resilience     | `RoomTimeline` has no `ErrorBoundary` wrapper — a single malformed event crashing the renderer takes down the entire timeline with no fallback UI.                                                           | `src/app/features/room/RoomTimeline.tsx`                | OPEN   |
| Component Resilience     | `RoomInput` has no `ErrorBoundary` wrapper — a crash in the composer leaves users unable to send messages.                                                                                                   | `src/app/features/room/RoomInput.tsx`                   | OPEN   |
| Fallback Logic           | No explicit empty/error fallback for Matrix SDK data calls in `RoomTimeline`; relies purely on SDK internal error propagation, meaning silent failures show a blank timeline.                                | `src/app/features/room/RoomTimeline.tsx`                | OPEN   |
| Dependency               | Potential for complex dependency chains due to deep nesting in `src/app/features/` and `src/app/hooks/`.                                                                                                     | `src/app/`                                              | OPEN   |
| Hydration/Race Condition | The SyncState listener registered by useSyncState may miss the initial 'PREPARED' event if the client initializes synchronously from IndexedDB before the effect runs, leading to an infinite loading state. | `cinny/src/app/pages/client/ClientRoot.tsx`             | OPEN   |
| Structure                | High number of small, highly coupled utility hooks (`src/app/hooks/`) may obscure dependency graphs.                                                                                                         | `src/app/hooks/`                                        | OPEN   |
| Dead Code                | Potential for unused CSS modules or UI components in `src/app/features/`.                                                                                                                                    | `src/app/`                                              | OPEN   |
| Security                 | Sensitive session data (access tokens, device ID) stored in `localStorage` is vulnerable to XSS.                                                                                                             | `src/app/state/sessions.ts`                             | OPEN   |
| Privacy                  | Sensitive user status messages and expiry timestamps are persisted in `localStorage`.                                                                                                                        | `src/app/features/settings/account/Profile.tsx`         | OPEN   |
| Privacy                  | Unsent composer drafts stored in `localStorage` without encryption could leak info on shared devices.                                                                                                        | `src/app/features/room/RoomInput.tsx`                   | OPEN   |
| Persistence              | Scheduled messages relying on fragile `localStorage` parsing are prone to data loss on session expiry or error.                                                                                              | `src/app/state/scheduledMessages.ts`                    | OPEN   |
| Bundle Bloat             | Inefficient `lodash` import; risks including entire library instead of necessary utilities.                                                                                                                  | `cinny/package.json`                                    | OPEN   |
| Bundle Bloat             | Large `matrix-js-sdk` (RC version) dependency; high potential for tree-shaking overhead.                                                                                                                     | `cinny/package.json`                                    | OPEN   |
| Build-Time Overhead      | `lotusDenoise` plugin performs heavy, sequential `fs` operations during `closeBundle`, significantly slowing build times.                                                                                    | `cinny/vite.config.js`                                  | OPEN   |
| Build-Time Overhead      | Complex manual `viteStaticCopy` configuration requiring multiple renames and path manipulations; risks redundant processing.                                                                                 | `cinny/vite.config.js`                                  | OPEN   |
| Architectural Debt       | Redundant style variant logic in `SpacingVariant` could be simplified.                                                                                                                                       | `cinny/src/app/components/message/layout/layout.css.ts` | OPEN   |
| Overhead Analysis        | Potential CSS bloat from `DropTarget` composition across multiple recipes (`SidebarItem`, `SidebarFolder`).                                                                                                  | `cinny/src/app/components/sidebar/Sidebar.css.ts`       | OPEN   |

## 🏗️ Git Workflow & History Audit

| Category | Issue Description                                                                                       | File Path   | Status |
| :------- | :------------------------------------------------------------------------------------------------------ | :---------- | :----- |
| Workflow | Monolithic "Fix all bugs" commits (e.g., `10f6544e`, `aa48c9ef`) make `git bisect` difficult.           | Git History | OPEN   |
| Workflow | Inconsistent commit message prefixes (e.g., `fix`, `feat`, `docs`, `assets`).                           | Git History | OPEN   |
| Workflow | Use of `fix` or `feat` for large-scale changes affecting multiple disparate systems (e.g., `938ead79`). | Git History | OPEN   |

---

## 🎨 Native UI/UX Consistency — Lotus vs. Cinny Baseline

> Audit of every Lotus-custom UI feature against Cinny's native folds design-system conventions. "Native pattern" means the `folds` component library, vanilla-extract tokens (`color.*`, `config.radii.*`, `config.space.*`), and established Cinny component patterns. 52 findings, organized by severity.

---

### 🔴 Major — Broken Styling / Functional Regressions

#### N1. `ProfileDecoration` Save Button — Undefined `--accent-cyan` Variable (border invisible on all non-TDS themes)

- **File:** `src/app/features/settings/account/ProfileDecoration.tsx`, lines 191–213
- **Status:** **FIXED** — replaced raw `<button>` with `<Button size="400" variant="Success" fill="Solid" radii="300">`, removed undefined `--accent-cyan` reference
- **Issue:** The save button is a raw `<button>` with `border: '1px solid var(--accent-cyan)'` and `color: 'var(--accent-cyan)'`. The variable `--accent-cyan` (without the `--lt-` prefix) is never defined in any theme file — the correct prefixed form is `--lt-accent-cyan`. On all non-TDS themes the border is **invisible** and the text has no color.
- **Root Cause:** Missing `--lt-` prefix. Additionally, the raw `<button>` should be a folds `<Button>` to match every other save button in the same `Profile.tsx` settings panel (e.g., `ProfileDisplayName` save at `Profile.tsx:303`).
- **Fix:** Replace raw `<button>` with `<Button size="400" variant="Success" fill="Solid" radii="300">`. Remove the `--accent-cyan` reference.

#### N2. `UserPrivateNotes` Textarea — Undefined `--border-interactive` Variable (border invisible on all themes)

- **File:** `src/app/components/user-profile/UserRoomProfile.tsx`, lines 246–265
- **Status:** **FIXED** — replaced undefined CSS vars with `color.SurfaceVariant.ContainerLine`, `config.radii.R300`, `config.space.S200/S300`
- **Issue:** The notes textarea sets `border: '1px solid var(--border-interactive)'`. This variable is never defined anywhere in the codebase — the correct equivalents are `--bg-surface-border` (`src/index.css`) or `color.SurfaceVariant.ContainerLine` (folds token). The border is **invisible on all themes**.
- **Root Cause:** Invented CSS variable name. Also uses raw pixel sizing (`borderRadius: '6px'`, `padding: '8px 10px'`, `fontSize: '14px'`) instead of folds tokens.
- **Fix:** Replace inline style with `border: \`1px solid ${color.SurfaceVariant.ContainerLine}\``, `borderRadius: config.radii.R300`, `padding: config.space.S200`.

#### N3. `LotusToastContainer` — Z-Index Places Toasts Below Night Light Overlay and All Modals

- **File:** `src/app/features/toast/LotusToastContainer.tsx`, lines 184–211; `src/app/pages/App.tsx`
- **Status:** **FIXED** — raised toast `zIndex` from `9997` to `10001` (above Night Light at 9998 and modals at 9999)
- **Issue:** The toast container uses hardcoded `zIndex: 9997`. The Night Light overlay is at `z-index: 9998`. The folds `Overlay`/`Dialog` components used for all modals resolve to `z-index: 9999`. Result: (a) toasts render **under** the Night Light tint and take on the warm orange filter; (b) any open modal covers toasts entirely, making notifications invisible.
- **Root Cause:** The toast container does not use the `folds` `OverlayContainerProvider` portal that manages z-index correctly — it is a plain `position: fixed` div injected directly in `App.tsx`.
- **Fix:** Either route the toast portal through `OverlayContainerProvider` (matching how all other floating UI works), or raise `zIndex` above all overlay layers (10001+). Also audit Night Light's z-index (9998) relative to toasts.

#### N4. `PollContent` Vote Buttons — Entirely Outside the Folds Design System

- **File:** `src/app/components/message/content/PollContent.tsx`, lines 250–358
- **Status:** **OPEN**
- **Issue:** Each poll answer is a native `<button>` with ~15 hardcoded inline style properties using undefined CSS variables (`--accent-cyan`, `--accent-cyan-dim`, `--accent-cyan-border`). Checkbox/radio indicators, percentage spans, and the poll label use raw pixel font sizes (`0.68rem`, `0.78rem`, `0.88rem`) and hardcoded `borderRadius: '8px'`. None of these variables exist in any theme — the entire component will render unstyled on non-TDS themes. All other interactive message content (audio, file, image) uses folds `Chip` or `Button` variants.
- **Root Cause:** Custom implementation that bypasses folds primitives entirely.
- **Fix:** Rewrite using folds `Button` or `Chip` for answers; replace `--accent-cyan*` with `color.Secondary.*` folds tokens; use `Text size="T300"` for labels.

---

### 🟠 Moderate — Interaction Pattern or Visual Deviations

| #   | Area                       | File                                      | Lines             | Issue                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Native Pattern                                                                                                                                                                                                 |
| :-- | :------------------------- | :---------------------------------------- | :---------------- | :  | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| N5  | Read Receipts              | `ReadReceiptAvatars.tsx`                  | 62–137            | Trigger button is raw `<button>` with `onMouseEnter`/`onMouseLeave` JS style mutation for hover state                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | All interactive elements use `useHover` from `react-aria` and folds variant system for hover; direct `.style` mutation used nowhere else on buttons                                                            |
| N6  | Read Receipts              | `ReadReceiptAvatars.tsx` & `Message.tsx`  | 32–56 / 268–283   | Two code paths open `EventReaders`: avatar-pill path uses `useModalStyle(360)` for mobile fullscreen; context-menu path (`MessageReadReceiptItem`) does not — on mobile the context menu path opens a fixed-size non-fullscreen modal for the same content                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | All modals that share a layout variant use `useModalStyle` consistently; `MessageReadReceiptItem` was not updated when `useModalStyle` was added                                                               |
| N7  | Delivery Status            | `Message.tsx`                             | 89–148            | `DeliveryStatus` renders Unicode glyphs (`⟳ ✓ ✕`) in a `<span>` with `fontSize: '10px'` instead of folds `<Icon>` components — **FIXED**: replaced with `Icons.Check/Cross/Send` via `<Icon size="100">`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | `Icons.Check`, `Icons.Cross`, etc. are used for all other status glyphs; folds `Text` size tokens for all supplementary text                                                                                   |
| N8  | GIF Picker                 | `GifPicker.tsx`                           | 83–124            | GIF picker container uses fully bespoke inline styles (`borderRadius: '12px'`, `boxShadow: '0 8px 32px rgba(0,0,0,0.4)'`, raw `rgba` border) — two separate style sets for TDS and non-TDS paths                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | `EmojiBoard` has no caller-applied container styling; folds components handle their own surface internally via design tokens                                                                                   |
| N9  | GIF Button                 | `RoomInput.tsx`                           | 1076–1087         | GIF toolbar button renders `<Text size="T200">` with hand-rolled `fontWeight`/`fontSize`/`letterSpacing` instead of `<Icon>` — **WON'T FIX (deliberate)**: folds has no GIF icon, and "GIF" is a widely-recognized text affordance (Slack/Discord/Element all use a text label). Converting to an arbitrary icon would be less clear, not more.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | All 8 other toolbar buttons (`Smile`, `Sticker`, `Location`, `Poll`, etc.) use `<Icon src={...} />` exclusively                                                                                                |
| N10 | Send Animation             | `Message.tsx` + `Animations.css.ts`       | 979–998 / 60–71   | `MsgAppearClass` and `MentionHighlightPulse` both animate `transform: scale` on the same `MessageBase` DOM node — on self-sent mention messages both classes apply simultaneously and fight over the `transform` property                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | Pre-existing `highlightAnime` only animates `backgroundColor`; no prior `transform` animation on `MessageBase`                                                                                                 |
| N11 | AvatarDecoration           | `AvatarDecoration.tsx`                    | 5 / 38–41         | Fixed 8px inset on all sides regardless of avatar size — at folds size `"200"` (~32px) the decoration bleeds 50% of the avatar diameter, clipping against `overflow: hidden` parent containers in member lists. **Inset issue still OPEN.** _Related regression fixed in `useAvatarDecoration.ts`_: the decoration fetch cached **all** failures (including transient 429/5xx) as "no decoration" permanently for the session, so a single rate-limited burst (member list / timeline mount many avatars at once) would make decorations vanish until a full reload. Now only a genuine 404 is cached; transient errors retry on the next mount.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | Folds `Avatar` and `PresenceRingAvatar` do not emit overflow outside their bounding box                                                                                                                        |
| N12 | MediaGallery Drawer        | `MediaGallery.tsx`                        | 651–661           | Drawer uses `position: 'fixed'` with hardcoded `width: '320px'` as inline styles on a `<Box>` — **FIXED**: moved positioning/width into co-located `MediaGallery.css.ts` using `toRem(320)` + a `max-width: 750px` full-screen media query (mirrors `MembersDrawer`); border/header now use `config.borderWidth`/`config.space` tokens. Added Escape-to-close on the panel (previously only the lightbox handled Escape). **Full chrome redesign (round 2)** to match native conventions: panel + header switched from `Surface` to `Background` variant (matching `MembersDrawer`/Saved Messages); header now `Text size="H4"` + plain close `IconButton` (dropped the bespoke tooltip-wrapped button); tabs moved to a bordered toolbar strip with the `variant={active?'Primary':'Secondary'} fill={active?'Solid':'Soft'}` pattern from `PolicyListViewer` and now show per-tab counts; the centered "lines + label" month divider replaced with a left-aligned group label (Cinny group-label pattern); thumbnail tiles moved hover/focus styling to CSS `:hover`/`:focus-visible` (no JS hover state) and into `MediaGallery.css.ts`; file rows + grid tokenized. **Docking fix (round 3)** — the core of the finding: the gallery was a `position: fixed` overlay floating over the timeline, mounted from `RoomViewHeader`. It is now a **docked flex sibling** in the room layout row, exactly like `MembersDrawer`: open state lifted to a `mediaGalleryAtom` (mirrors `bookmarksPanelAtom`), rendered in `Room.tsx` with a vertical `Line` separator on desktop and `key={room.roomId}` to reset per room; the CSS is static-width on desktop and only `position: fixed; inset: 0` full-screen on mobile (identical strategy to `MembersDrawer.css`). It now shares the row with the timeline instead of overlapping it. | `MembersDrawer` uses a vanilla-extract class with `width: toRem(266)` and is placed by the layout system, not `position: fixed`. 54px width discrepancy also breaks visual rhythm if both panels could be open |
| N13 | ScheduledMessagesTray      | `ScheduledMessagesTray.tsx`               | 108–126           | Collapsible tray header is `<Box as="button">` with `cursor: 'pointer'` inline style and no folds variant — no hover state, no focus ring — **FIXED**: replaced with folds `<Button variant="Secondary" fill="None" radii="0">` using `before`/`after` icon props (gains design-system hover/focus)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | All clickable header/toggle elements in the room view use folds `<Button>` or `<IconButton>` with explicit variants for hover/focus; `<Box as="button">` with no variant is used nowhere else                  |
| N14 | ForwardMessageDialog       | `ForwardMessageDialog.tsx`                | 137–154           | Dialog uses `<Modal>` but has no `<Header>` component and no close `<IconButton>` — only way to close is clicking outside                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | Every other modal using `<Modal>` or `<Box role="dialog">` includes a `<Header>` with a close `<IconButton>` in the top-right (EditHistoryModal, LeaveRoomPrompt, ScheduleMessageModal, RemindMeDialog, etc.)  |
| N15 | ScheduleMessageModal       | `ScheduleMessageModal.tsx`                | 180–193           | Modal root is `<Box as="form" role="dialog">` with manually assembled `borderRadius: config.radii.R400`/`boxShadow`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `ForwardMessageDialog` uses folds `<Modal size="400">` with `R500` radius; the R400 vs R500 mismatch is visible when both dialogs appear in the same session                                                   |
| N16 | Presence Picker            | `SettingsTab.tsx`                         | 118–144           | Presence trigger dot is raw `<button>` with `position: absolute; bottom: 2; right: 2` inline and no folds focus ring; no tooltip — **FIXED**: wrapped the trigger in a folds `TooltipProvider` (shows "Status: …"); replaced the undefined `var(--bg-surface)` with `color.Background.Container`. Kept the absolute-positioned `<button>` (it overlays the avatar corner; a full `IconButton` would be too large for the dot).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Every other sidebar icon button uses folds `IconButton` with `SidebarItemTooltip` and `TooltipProvider`                                                                                                        |
| N17 | Presence Picker            | `SettingsTab.tsx`                         | 80–86             | `PresencePicker` `FocusTrap` missing `escapeDeactivates: stopPropagation` and `isKeyForward`/`isKeyBackward` — **FIXED**: added all three options, matching the theme selector / sort menus                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Every other `PopOut`+`FocusTrap`+`Menu` combo supplies both (theme selector `General.tsx:143–160`, `SettingsSelect`, sort menus) — without it Escape bubbles past the trap and arrow-key navigation is absent  |
| N18 | Profile Selects            | `Profile.tsx`                             | 547–575 / 816–848 | `ProfileStatus` auto-clear and `ProfileTimezone` selectors are native `<select>` elements with hardcoded `colorScheme: 'dark'` — will render in dark mode on light themes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | General.tsx uses folds `SettingsSelect<T>` (`Button`+`PopOut`+`Menu`) for all dropdowns; `colorScheme: 'dark'` breaks light/custom theme appearance                                                            |
| N19 | Presence Labels            | `useUserPresence.ts` vs `SettingsTab.tsx` | 55–62 / 36–42     | `PresenceBadge` tooltip shows "Active / Busy / Away"; `PresencePicker` options read "Online / Idle / Do Not Disturb / Invisible" — a DND user shows tooltip "Busy", not "Do Not Disturb" — **FIXED**: aligned `usePresenceLabel` reader vocabulary to the setter (online→"Online", unavailable→"Idle", offline→"Offline")                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | Within the same Lotus feature set the user-facing vocabulary is inconsistent between the setter UI and the reader tooltip                                                                                      |
| N20 | Notification Presets       | `Notifications.tsx`                       | 57–107            | Gaming/Work/Sleep preset buttons are bare `<button>` elements with Lotus-specific CSS vars (`--border-interactive-normal`, `--bg-surface-low`) not defined in all themes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | Grouped preset/action buttons elsewhere use folds `Chip variant="Primary/Secondary" outlined radii="Pill"` (e.g., Composer Toolbar toggles in `General.tsx:1100–1113`)                                         |
| N21 | Notification Sound Selects | `SystemNotification.tsx`                  | 111–305           | Message sound, invite sound, and quiet-hours time pickers are bare `<select>`/`<input type="time">` with `colorScheme: 'dark'` workaround                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | All other dropdowns in settings use the `Button`+`PopOut`+`Menu`+`MenuItem` folds pattern; the native select renders OS-styled on all platforms                                                                |
| N22 | DM Preview Virtualizer     | `RoomNavItem.tsx` / `Direct.tsx`          | 608–627 / 232     | DM preview adds a second text row to each DM item, making it taller than 38px, but `useVirtualizer` in `Direct.tsx` still uses `estimateSize: () => 38` — causes layout jump/overlap on initial render                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | Non-DM rooms in Home.tsx also estimate 38px; DM items with a preview are now a different height, creating two visual densities in the same nav column                                                          |
| N23 | RoomServerACL              | `RoomServerACL.tsx`                       | 100–115 / 298–309 | Server-name text input is a raw `<input type="text">` with inline style object; "Allow IP literal addresses" is a raw `<input type="checkbox">` with `style={{ width: 16, height: 16 }}`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | All other text/boolean controls in room settings use folds `Input` and `Checkbox` components (`RoomAddress.tsx:163`, `RoomAddress.tsx:330`)                                                                    |
| N24 | PolicyListViewer           | `PolicyListViewer.tsx`                    | 245–264           | Room-ID add input is a raw `<input type="text">` with manually replicated folds token values                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | Native pattern: folds `<Input variant="Secondary" size="300" radii="300">` — no inline style needed                                                                                                            |
| N25 | ExportRoomHistory Inputs   | `ExportRoomHistory.tsx`                   | 258–292           | Both date range pickers are raw `<input type="date">` with inline styles                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | Native pattern: folds `Input` component; `<input type="date">` renders OS-native date picker, unstyled relative to the rest of the settings panel                                                              |
| N26 | RoomShareInvite QR         | `RoomShareInvite.tsx`                     | 66–73             | QR code `<img>` has no `onError` handler and no loading state — broken-image placeholder shown when the external API is unreachable                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | Cinny avatar components and MediaGallery use `onError` handlers; this is the only settings element making a request to a third-party server with no graceful degradation                                       |

---

### 🟡 Minor — Cosmetic / Token Discipline

| #       | Area                               | File                                   | Lines             | Issue                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | Native Pattern                                                                                                                                                                     |
| :------ | :--------------------------------- | :------------------------------------- | :---------------- | :  | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| N27     | GIF Picker                         | `GifPicker.tsx`                        | 103–110           | `FocusTrap` omits `returnFocusOnDeactivate: false` — focus returns to GIF button on dismiss instead of staying in the editor                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | `EmojiBoard` in `RoomInput.tsx:978` explicitly sets `returnFocusOnDeactivate={false}`; GIF picker dismiss behaviour is inconsistent with emoji picker                              |
| N28     | Character Counter                  | `RoomInput.tsx`                        | 1159–1174         | Composer character counter rendered with `color: 'var(--tc-surface-low)'` and raw pixel padding — a CSS variable not used anywhere else in the codebase — **FIXED**: removed undefined var and raw opacity; now `<Text priority="300">` with `config.space.S100` padding                                                                                                                                                                                                                                                                                                                                                                                                                    | Use `color.*` folds tokens or `priority="300"` on a `Text` component                                                                                                               |
| N29     | PollCreator Modal                  | `PollCreator.tsx`                      | 103–116           | Modal root is `<Box as="form" role="dialog" aria-modal="true">` with manually assembled surface styles instead of folds `<Dialog variant="Surface">`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | `MessageDeleteItem` and `MessageReportItem` in `Message.tsx:506,635` use `<Dialog variant="Surface">` inside `OverlayCenter > FocusTrap`                                           |
| N30     | Playback Speed Chip                | `AudioContent.tsx`                     | 163–189           | Speed chip uses `variant="SurfaceVariant" radii="Pill"` while adjacent Play/Pause chip uses `variant="Secondary" radii="300"` — mismatched shape and variant within the same `leftControl` row — **FIXED**: changed speed chip to `variant="Secondary" radii="300"`                                                                                                                                                                                                                                                                                                                                                                                                                         | Controls grouped in the same row should share variant and radii                                                                                                                    |
| N31     | Collapsible Message Toggle         | `MsgTypeRenderers.tsx`                 | 97–105            | "Read more ↓" / "Show less ↑" uses `<Button size="300" variant="Secondary" fill="None">` — visually a padded form button — **FIXED**: replaced with the native flush inline-button pattern (`background:none;border:none;padding:0`) + `<Text size="T200">` tinted `color.Primary.Main`, matching `(edited)` in FallbackContent                                                                                                                                                                                                                                                                                                                                                             | Inline text toggles in message content (e.g. `(edited)` in `FallbackContent.tsx:74`) use bare `<button>` with `background: none; border: none; padding: 0` to stay flush with text |
| N32     | ReadReceiptAvatars Pill            | `ReadReceiptAvatars.tsx`               | 95–103            | Pill border is `'1px solid rgba(0,212,255,0.30)'` hardcoded raw rgba string; `borderRadius: '999px'` not a folds radii token; padding in raw pixels — **FIXED**: replaced with `config.borderWidth.B300`, `config.radii.Pill`, `config.space.S100/S200`                                                                                                                                                                                                                                                                                                                                                                                                                                     | Use `color.*` folds tokens and `config.radii.Pill` / `config.space.S*`                                                                                                             |
| ~~N33~~ | ~~ReadReceiptAvatars Class~~       | ~~`ReadReceiptAvatars.tsx`~~           | ~~67~~            | ~~`className="receipt-pill-btn"` references a class never defined~~ — **FIXED**: removed dead className                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | All custom CSS goes through co-located vanilla-extract `*.css.ts` files                                                                                                            |
| N34     | EventReaders Header Size           | `EventReaders.tsx`                     | 70                | `Header size="600"` (56px tall) while all peer message-action modals use `size="500"` (48px) — **FIXED**: changed to `size="500"`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | `EditHistoryModal`, `LeaveRoomPrompt`, `MessageDeleteItem`, `MessageReportItem` all use `size="500"`; `size="600"` is reserved for full-page panel headers                         |
| N35     | EventReaders Close Button          | `EventReaders.tsx`                     | 96                | Close `IconButton` missing explicit `radii="300"` prop — **FIXED**: added `radii="300"`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Every peer modal close button explicitly sets `radii="300"` (EditHistoryModal:184, LeaveRoomPrompt:75, MessageDeleteItem:517)                                                      |
| N36     | EventReaders Header Border         | `EventReaders.tsx`                     | 72–77             | Lotus-mode header sets `borderBottom: '1px solid var(--lt-border-color)'` as a CSS shorthand string — **FIXED**: changed to `borderBottomWidth: config.borderWidth.B300`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | Native modals use `borderBottomWidth: config.borderWidth.B300` to avoid overriding the border-color set by the folds variant system                                                |
| N37     | EventReaders Timestamp             | `EventReaders.tsx`                     | 143–151           | Lotus path sets `fontSize: '0.72rem'` inline — a raw relative unit between folds `T200` and `T100` scale steps — **FIXED**: removed raw `fontSize`, added `priority="300"`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Use folds `Text size="T200" priority="300"` for subdued secondary text                                                                                                             |
| N38     | BookmarksPanel Header              | `BookmarksPanel.tsx`                   | 155–196           | Header uses `variant="Surface"` and close button uses `size="300" radii="300"`; also has a SurfaceVariant search bar strip with no equivalent in any native drawer — **FIXED (full redesign)**: rebuilt the whole "Saved Messages" panel to match the canonical `MembersDrawer` — co-located `BookmarksPanel.css.ts` (`toRem(266)` + `max-width:750px` full-screen media query, replacing the old `position:absolute; zIndex:100` mobile "modal" that had no backdrop/escape), `variant="Background"` header, room **avatars** on each item (was a generic hash icon), `priority` tokens replacing all raw `opacity` hacks, the `borderLeft:3px` accent removed, and Escape-to-close added. | `MembersDrawer` header uses `variant="Background"` and default-size close button; the extra search+count strip creates a structurally different component family                   |
| N39     | Forward Menu Icon                  | `Message.tsx`                          | 1150              | Forward context menu item's `after` icon has no `size="100"` prop — **FIXED**: added `size="100"` to the `ArrowRight` icon                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Every other after-icon in the same menu block explicitly uses `size="100"` (Reply, Reaction, Edit, Remind Me, Bookmark); missing size causes the Forward icon to render larger     |
| N40     | ProfileDecoration Remove Button    | `ProfileDecoration.tsx`                | 185               | "Remove" link is a raw `<button>` with `background: 'none'; color: 'var(--tc-surface-low-contrast)'` — an undefined CSS variable — **FIXED**: replaced with `<Button variant="Critical" fill="None" size="300" radii="300">`                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Use folds `<Button variant="Critical" fill="None">` or a `Text`-styled inline link                                                                                                 |
| N41     | PresenceBadge / UserNotes Saving   | `UserRoomProfile.tsx`                  | 240–244           | "Saving…" indicator is `<Text opacity={0.5}>` without a spinner — **FIXED**: now shows a folds `<Spinner variant="Success" fill="Solid" size="100">` beside the "Saving…" text                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | Every other save operation in `Profile.tsx` shows a folds `<Spinner variant="Success" fill="Solid" size="300">` alongside the save button                                          |
| N42     | Character Counter Convention       | `UserRoomProfile.tsx` vs `Profile.tsx` | 243 / 479–490     | `UserPrivateNotes` shows remaining count `"N left"`, appears only under 100; `ProfileStatus` shows `"current / 64"` always with color progression                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | Two Lotus features in the same settings flow use different counter conventions; neither matches a pre-existing Cinny pattern                                                       |
| N43     | Night Light Slider                 | `General.tsx`                          | 554–565           | Night Light intensity slider is a raw `<input type="range">` with no `accentColor` token — renders in browser-default blue on all themes — **FIXED**: added `accentColor: color.Primary.Main`; the intensity label `opacity` hack also replaced with `priority="300"`                                                                                                                                                                                                                                                                                                                                                                                                                       | The Gate Threshold slider at `General.tsx:1456` at minimum sets `accentColor: 'var(--accent-orange)'`; the Night Light slider does neither                                         |
| N44     | Mention Highlight & Boot Button    | `General.tsx`                          | 597–677           | `<input type="color">` for mention highlight uses raw pixel dimensions (`width: '36px'`, `height: '28px'`, `borderRadius: '4px'`); Reset and Boot buttons are bare `<button>` with Lotus CSS vars — **PARTIALLY FIXED**: the mention-highlight Reset button (renders on all themes) is now a folds `<Button variant="Secondary" fill="Soft">`, removing the undefined `--border-interactive-normal` var. The Boot button is **deliberately kept** as-is: it only renders when `lotusTerminal` is active, i.e. exactly when the `--accent-orange*` TDS vars are defined. The `<input type="color">` itself is tracked separately as N69.                                                     | Adjacent settings controls use folds `IconButton`/`Button`; there is no other `<input type="color">` in the Cinny settings UI                                                      |
| N45     | SettingsSelect vs SelectTheme      | `General.tsx`                          | 126 vs 197        | `SettingsSelect` trigger uses `variant="Secondary"` while `SelectTheme` uses `variant="Primary" outlined fill="Soft"` for the same `Button`+`PopOut` dropdown pattern — adjacent rows in the same Appearance section have different visual weight — **FIXED**: `SelectTheme` trigger changed to `variant="Secondary"` to match `SettingsSelect`                                                                                                                                                                                                                                                                                                                                             | Dropdown triggers should share the same variant within the same settings section                                                                                                   |
| N46     | RoomInsights SectionHeader         | `RoomInsights.tsx`                     | 24–37             | `SectionHeader` adds `textTransform: 'uppercase'`, `letterSpacing: '0.06em'`, `opacity: 0.6` to `Text size="L400"` — **FIXED**: simplified to `<Text size="L400" priority="300">`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | Every other settings panel uses bare `<Text size="L400">Label</Text>` with no transforms (`General.tsx:52–72`, `ExportRoomHistory.tsx:220,246`)                                    |
| N47     | RoomInsights Chart Radii           | `RoomInsights.tsx`                     | 350–356 / 415–436 | Bar chart uses `borderRadius: 3` and histogram bars use `borderRadius: '2px 2px 0 0'` as raw pixel integers — **FIXED**: replaced with `config.radii.R300`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | All other rounded corners use `config.radii.*` tokens                                                                                                                              |
| N48     | RoomInsights Font Size             | `RoomInsights.tsx`                     | 448               | Hour-axis labels set `style={{ fontSize: 9 }}` as a raw pixel integer — overrides the folds `Text size="T200"` applied on the same element — **FIXED**: removed raw `style={{ fontSize: 9 }}`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Use only folds `Text` size props; never override with raw `fontSize`                                                                                                               |
| N49     | RoomInsights Emoji Icons           | `RoomInsights.tsx`                     | 41–65 / 292–295   | `StatTile` uses literal Unicode emoji (`🖼️ 🎬 🎵 📎`) in `<Text size="H4">` as icons — **FIXED**: `StatTile` now takes an `icon: IconSrc` and renders `<Icon>` using `Icons.Photo/VideoCamera/Headphone/File`                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | All other iconographic elements use `<Icon src={Icons.*} />` from folds — emoji rendering varies between Windows/macOS/Linux and cannot be tinted by the theme                     |
| N50     | RoomInsights Warning Banner        | `RoomInsights.tsx`                     | 168–192           | Disclaimer banner uses raw `<Box style={{ border: color.Warning.Main, background: color.Warning.Container }}>` — **FIXED**: replaced with `<SequenceCard variant="SurfaceVariant">` with `<Icon>` colored via `color.Warning.Main`                                                                                                                                                                                                                                                                                                                                                                                                                                                          | Settings panel informational cards use `<SequenceCard variant="SurfaceVariant">` throughout RoomServerACL, ExportRoomHistory, PolicyListViewer                                     |
| N51     | ExportRoomHistory Progress         | `ExportRoomHistory.tsx`                | 311–314           | Export progress shows as a plain `Text` string ("Exporting… N messages") — **WON'T FIX (deliberate)**: unlike `BackupRestore` (which has a known total to drive a determinate `ProgressBar`), export has no known total — it counts messages as they stream. The operation already shows a folds `Spinner` in the button plus a live count, which is the correct affordance for an indeterminate task.                                                                                                                                                                                                                                                                                      | `BackupRestore.tsx:72,90` uses a folds `<ProgressBar variant="Secondary" size="300">` for the same kind of long async operation                                                    |
| N52     | MessageQuickReactions Empty Return | `Message.tsx`                          | 160               | `if (recentEmojis.length === 0) return <span />;` — injects an invisible DOM node into the hover action bar flex container — **FIXED**: changed to `return null`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | Universal convention for empty renders in Cinny is `return null`; 144+ instances across the codebase; the empty `<span>` can affect flex spacing                                   |

---

### Round 2 — Additional Feature Areas

#### 🔴 Additional Major Findings

**N53 — PTT Badge (Lotus Terminal path): Raw `<div>` tree with `--lt-*` CSS vars instead of folds `<Chip>`**

- **File:** `src/app/features/call/CallControls.tsx`, lines 242–282
- **Status:** **OPEN**
- **Issue:** When `lotusTerminal` is true the PTT badge renders as a bare `<Box>` with inline styles referencing `--lt-accent-green-dim`, `--lt-accent-green-border`, `--lt-accent-green` — variables absent outside TDS mode — hardcoded rem padding, `borderRadius: '99px'` (non-token), a raw monospace `fontFamily` string, non-token `letterSpacing`, and a raw `animation:` CSS string for the live-pulse dot. The live `●` dot is a raw `<span>` with inline style.
- **Root Cause:** Two entirely separate component trees for the same badge depending on a theme boolean. The non-terminal path (lines 284–301) uses the correct `<Chip variant="Success"|"Warning" fill="Soft" radii="400" outlined>`.
- **Fix:** Remove the terminal branch. The standard `<Chip>` path already exists and TDS theming can be applied via the CSS variable layer without a separate component tree.

**N54 — PiP Mute Overlay Badges: Raw `<div>` instead of folds `<Badge>`/`<Chip>`**

- **File:** `src/app/components/CallEmbedProvider.tsx`, lines 438–477
- **Status:** **FIXED** — replaced hardcoded `borderRadius`/`padding`/`fontSize` with `config.radii.R300`, `config.space.S100/S200` tokens; replaced raw `<span>` text with folds `<Text size="T200">`; color now applied to the `Icon`/`Text` via `color.Critical/Warning.Main`. The dark translucent scrim (`rgba(0,0,0,0.65)`) is **deliberately retained**: these badges overlay arbitrary video, where a theme `Chip`/`Badge` surface token would not guarantee legibility. They are also non-interactive (`pointerEvents: 'none'`), so an interactive `Chip` (a `<button>`) is semantically wrong.
- **Issue:** Both the "You muted" (bottom-left) and "All muted" (top-right) PiP badges are raw `<div>` elements with hardcoded `background: 'rgba(0,0,0,0.65)'`, `backdropFilter: 'blur(4px)'`, `borderRadius: '6px'`, `padding: '3px 7px'`, `fontSize: '12px'`. Color is set as `color: color.Critical.Main` directly on the wrapper `<div>`, not via a folds `variant` prop. Text is `<span style={{ fontSize: '11px', fontWeight: 600 }}>`.
- **Root Cause:** `CallView.tsx` line 127 uses `<Badge variant="Critical" fill="Solid" size="400">` in the same file for the "N Live" indicator — the native pattern exists and is unused here.

**N55 — Chat Background / Seasonal Theme Selected State Uses `color.Critical.Main` (Error Red)**

- **File:** `src/app/features/settings/general/General.tsx`, lines 1660–1661 and 1726–1728
- **Status:** **FIXED** — replaced all 4 instances of `color.Critical.Main` with `color.Primary.Main` in `General.tsx`
- **Issue:** The selected-state border for both `ChatBgGrid` and `SeasonalBgGrid` is `border: \`2px solid ${color.Critical.Main}\``and the label color is also`color.Critical.Main`. `color.Critical.Main` is the semantic token for **destructive/error states** — it is used for "Leave Room", "Delete Message", "Report Room" in the same file. A normal selection indicator rendered in error red is semantically wrong and visually alarming.
- **Root Cause:** Wrong semantic token for an active/selected state.
- **Fix:** Replace `color.Critical.Main` with `color.Primary.Main` (or `color.Success.Main` to match how other settings selections are styled) for both the border and label color.

**N56 — Report Modal Category Dropdown: Native `<select>` Instead of folds `Chip`+`PopOut`+`Menu`**

- **File:** `src/app/features/room/ReportRoomModal.tsx` lines 138–163; `src/app/features/room/ReportUserModal.tsx` lines 144–169
- **Status:** **OPEN**
- **Issue:** Both report modals render the "Category" field as `<Box as="select">` with hand-rolled inline styles (padding, border, background, color, fontSize, fontFamily). No other selector in the message-action modal context uses `<select>` — the established pattern for all dropdowns in both message modals and search filters is `Chip onClick → setMenuAnchor → PopOut → FocusTrap → Menu → MenuItem` (reference: `OrderButton` in `SearchFilters.tsx` lines 63–114).
- **Fix:** Replace native `<select>` with `<Chip>` trigger + `<PopOut>` + `<Menu>` + `<MenuItem>` pattern.

---

#### 🟠 Additional Moderate Findings

| #   | Area                                                                         | File                                          | Lines                 | Issue                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Native Pattern                                                                                                                                                                                                      |
| :-- | :--------------------------------------------------------------------------- | :-------------------------------------------- | :-------------------- | :  | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| N57 | PiP Fullscreen Button                                                        | `CallEmbedProvider.tsx`                       | 929–951               | PiP fullscreen toggle is a raw `<button>` with `background: 'rgba(0,0,0,0.65)'`, `color: '#fff'`, `fontSize: '13px'`, Unicode ⛶/⊡ glyph — no focus ring, no tooltip — **FIXED (token discipline)**: `borderRadius`/`padding`/gap replaced with `config.radii.R300` + `config.space.*` tokens (also on the "Return to call" label). The dark scrim and `#fff` text are **deliberately kept** for legibility over arbitrary video; the glyph stays because folds has no fullscreen icon. `aria-label`/`title` tooltip already present. | `Controls.tsx` fullscreen button uses `<IconButton variant="Surface" fill="Soft" radii="400" size="400" outlined>` with `<TooltipProvider>`; hardcoded `#fff` fails on light themes                                 |
| N58 | Screenshare Confirm Popup                                                    | `CallControls.tsx`                            | 303–360               | "Share your screen?" popup is a raw `<Box>` with `--bg-surface`/`--bg-surface-border` vars (undefined outside TDS), `borderRadius: '0.75rem'`, `boxShadow: '0 8px 32px rgba(...)'`, no `FocusTrap`                                                                                                                                                                                                                                                                                                                                   | Cinny's confirmation dialogs use folds `<Menu>` + `<FocusTrap>` + `<PopOut>`; the non-FocusTrap popup is not keyboard-accessible                                                                                    |
| N59 | ML Noise Suppression Panel                                                   | `General.tsx`                                 | 1303–1487             | Sub-panel uses `var(--border-color)`, `var(--bg-card)`, `var(--bg-input)` (undefined in folds default theme), raw `<details>`/`<summary>` (UA-styled), `accentColor: 'var(--accent-orange)'` (TDS-only)                                                                                                                                                                                                                                                                                                                              | All other settings sub-sections use `<SettingTile>` rows inside `<SequenceCard>`; no other settings component uses `<details>`                                                                                      |
| N60 | Knock Badge on Members Button                                                | `RoomViewHeader.tsx`                          | 744–782               | Knock count badge wrapped in extra `<div style={{ position: 'relative' }}>` with hardcoded `fontSize: '9px'`, `minWidth: '14px'`, `height: '14px'`, `padding: '0 3px'` overriding folds `size="200"` — **FIXED**: removed wrapper div, put `position: 'relative'` directly on the `IconButton`, `<Badge size="400">` with `toRem(3)` insets and `<Text size="L400">` — now matches the Pinned Messages badge pattern exactly                                                                                                         | Pinned Messages badge (same header, lines 651–677) uses `position: 'relative'` directly on `<IconButton>` + `toRem()` for inset; no extra wrapper div                                                               |
| N61 | Knock Member Rows                                                            | `MembersDrawer.tsx`                           | 441–487               | Knock requester rows use raw `<Box>` with manually duplicated padding; no `<MenuItem>` wrapper → no hover/focus/active states — **WON'T FIX (deliberate)**: unlike a `MemberItem` (a clickable navigation row), a knock row contains two action buttons (Approve / Deny) and is **not itself clickable**. Wrapping it in `<MenuItem>` (a `<button>`) would nest interactive controls inside a button — invalid HTML/ARIA. The row has no interactive state to express.                                                               | Every joined/invited member uses `<MemberItem>` which wraps `<MenuItem variant="Background" radii="400">` with baked-in spacing and all interactive states                                                          |
| N62 | Unverified Device Banner                                                     | `RoomInput.tsx`                               | 860–883               | Warning callout above composer uses inline `background: color.Warning.Container`, `borderLeft: '3px solid color.Warning.Main'` — a custom left-border accent pattern not present anywhere else in the folds system — **FIXED**: replaced the `borderLeft: '3px'` accent with a standard full `border` using `color.Warning.ContainerLine` + `config.borderWidth.B300`; removed the `opacity` hacks (folds `OnContainer` already meets contrast)                                                                                      | Warning indicators in the same codebase use `<Chip variant="Warning">` or `<Badge variant="Warning">`; the 3px left-border card pattern has no folds equivalent                                                     |
| N63 | Report Modals — Box Instead of Dialog                                        | `ReportRoomModal.tsx` / `ReportUserModal.tsx` | 97–110 / 103–116      | Both modals render as `<Box as="form" role="dialog">` with inline `background`/`borderRadius`/`boxShadow`; use `config.radii.R400` (rounder) vs native `Dialog` which uses `R300`                                                                                                                                                                                                                                                                                                                                                    | Native `MessageReportItem` at `Message.tsx:634` and all other Cinny message-action modals use `<Dialog variant="Surface">`                                                                                          |
| N64 | EditHistoryModal — `<Modal>` vs `<Dialog>`                                   | `EditHistoryModal.tsx`                        | 166                   | Uses `<Modal variant="Surface" size="500">` while sibling message-action modals (`DeleteMessageItem:505`, `MessageReportItem:634`) all use `<Dialog variant="Surface">` — different widths and internal padding                                                                                                                                                                                                                                                                                                                      | `<Dialog variant="Surface">` is the established modal shell for all message-triggered dialogs                                                                                                                       |
| N65 | EditHistoryModal — No "Load More"                                            | `EditHistoryModal.tsx`                        | 253–259               | When `hasMore` is true the modal shows passive `<Text>"Showing the 50 most recent edits"</Text>` with no action; older edits are inaccessible — **FIXED**: implemented real pagination — edits accumulate across `next_batch` fetches (de-duped by event id, re-sorted by ts), with a folds `<Button>Load more</Button>` (spinner while loading) replacing the passive text                                                                                                                                                          | `RoomActivityLog.tsx:425` and `MessageSearch.tsx:129` both render a folds `<Button size="300" variant="Secondary">Load more</Button>` to fetch the next page                                                        |
| N66 | DateRangeButton — Native `<input type="date">`                               | `SearchFilters.tsx`                           | 558–589               | "From" and "To" date fields are raw `<input type="date">` with inline style overrides including `fontSize: '0.82rem'` — **FIXED**: replaced both with folds `<Input type="date" variant="SurfaceVariant" size="300" radii="300">`; removed now-unused `color` import                                                                                                                                                                                                                                                                 | `SelectRoomButton` (same file, line 224) and `SelectSenderButton` (line 424) both use folds `<Input size="300" radii="300">`; the date inputs are the only native browser inputs in the search filter row           |
| N67 | SeasonalEffect / NightLight Z-Index Order                                    | `SeasonalEffect.tsx` / `App.tsx`              | 759 / 62–77           | `SeasonalEffect` mounts at `zIndex: 9999`; `NightLightOverlay` at `zIndex: 9998`. Seasonal particles render **above** Night Light so they are never tinted. `SeasonalEffect` also shares `z-index: 9999` with the skip-to-content link in `ClientLayout.tsx` — **FIXED**: lowered `SeasonalEffect` overlay to `zIndex: 9997` (below Night Light at 9998 and modals at 9999), so Night Light now tints the particles and dialogs are never obscured                                                                                   | Expected UX: Night Light tints all visible content including effects; requires either a higher Night Light z-index or a lower SeasonalEffect z-index                                                                |
| N68 | Syntax Highlighting — `--lt-accent-*` Vars in Non-TDS Themes                 | `syntaxHighlight.ts`                          | 313–323               | `tokenStyle()` returns `var(--lt-accent-cyan/green/orange/purple, hardcoded-fallback)` — `--lt-*` vars only exist in TDS mode; fallbacks are Monokai dark colors that have poor contrast on light themes and no relationship to the existing `--prism-*` variables in `ReactPrism.css`                                                                                                                                                                                                                                               | `ReactPrism.css` uses `--prism-keyword`, `--prism-selector` etc. which switch correctly between light and dark palettes; syntax highlighting should use the same variable family                                    |
| N69 | Mention Highlight — `<input type="color">` Instead of `HexColorPickerPopOut` | `General.tsx`                                 | 644–675               | Raw `<input type="color">` with hardcoded pixel dimensions; OS-native color picker chrome renders completely differently from the rest of settings UI                                                                                                                                                                                                                                                                                                                                                                                | `PowersEditor.tsx:125–143` establishes `<HexColorPickerPopOut picker={<HexColorPicker ...>}>` as the codebase's color-picking pattern; Reset button should be `<Button size="300" variant="Secondary" radii="300">` |
| N70 | ChatBgGrid / SeasonalBgGrid — Raw `<button>` Elements                        | `General.tsx`                                 | 1648–1689 / 1711–1742 | Both pickers use raw HTML `<button>` elements with hardcoded `width: toRem(76)`, `height: toRem(50/56)`, `borderRadius: toRem(8)`, `border: 2px solid rgba(...)` — no focus ring via folds, no `variant` prop, no hover state from the design system                                                                                                                                                                                                                                                                                 | Native Cinny theme pickers use folds `<MenuItem>` or `<Chip>` which respond to theme and provide focus/hover states automatically                                                                                   |

---

#### 🟡 Additional Minor Findings

| #   | Area                                          | File                                          | Lines             | Issue                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | Native Pattern                                                                                                                                                                                         |
| :-- | :-------------------------------------------- | :-------------------------------------------- | :---------------- | :  | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| N71 | Call Prescreen Text                           | `CallView.tsx`                                | 63–85             | `ChannelFullMessage` and `AlreadyInCallMessage` use `<Text style={{ color: color.Critical/Warning.Main }}>` inline instead of folds `<Badge variant="Critical/Warning">` — **WON'T FIX (deliberate)**: these are full, centered explanatory **sentences** ("Channel Full (N/M) — Wait for someone to leave…"), not short labels. A `Badge` is for compact chips like "N Live"; wrapping a sentence in one is visually wrong. They already use folds `color.*` tokens. The sibling `LivekitServerMissingMessage`/`NoPermissionMessage` use the same (un-flagged) pattern. | The "N Live" badge directly above (line 127) correctly uses `<Badge variant="Critical" fill="Solid" size="400">`                                                                                       |
| N72 | Mute MenuItem Icon                            | `RoomNavItem.tsx`                             | 454–466           | "Mute" `<MenuItem>` places bell-mute icon as a raw child node instead of using the `before` prop — **FIXED**: moved `Icons.BellMute` to `before` prop                                                                                                                                                                                                                                                                                                                                                                                                                    | Every other `<MenuItem>` in both `RoomNavItemMenu` and `RoomMenu` places its leading icon in the `before` prop                                                                                         |
| N73 | Pending Requests Header                       | `MembersDrawer.tsx`                           | 415–422           | "Pending Requests" section header is bare `<Text>` with inline padding instead of `className={css.MembersGroupLabel}` — **FIXED**: now uses `className={css.MembersGroupLabel}` like every other section header                                                                                                                                                                                                                                                                                                                                                          | Power-level group labels at lines 506–519 use `className={css.MembersGroupLabel}` for all other section headers in the same virtualizer list                                                           |
| N74 | Emoji Prefix Span                             | `RoomNavItem.tsx`                             | 730–736           | Emoji prefix rendered as raw `<span style={{ fontSize: '1.15em', lineHeight: 1 }}>` inside a `<Text>` node — **FIXED**: removed the emoji-splitting span; the room name (including any leading emoji) now renders directly inside `<Text>`                                                                                                                                                                                                                                                                                                                               | All other nav item text uses folds `<Text size="Inherit">` or similar — no raw `<span>` with em-based font-size override exists elsewhere in the sidebar                                               |
| N75 | Room Name Override / Star Indicators          | `RoomNavItem.tsx`                             | 741–757           | Pencil and star indicator icons are embedded inside the name `<Box as="span">`, giving them the same visual baseline as the room name text — **WON'T FIX (deliberate)**: an inline favorite-star / local-name marker adjacent to the name is a deliberate, common design (cf. Element/Slack pinned-name markers). Moving them to the far right would collide with the unread/notification indicators already there and risks layout regressions. Low value, real regression risk.                                                                                        | Native sidebar status indicators (unread count, notification mode icon) are placed to the far right of the item, never inside the name text span group                                                 |
| N76 | Report Modals — Extra Cancel Button           | `ReportRoomModal.tsx` / `ReportUserModal.tsx` | 189–191 / 195–197 | Both custom report modals include a "Cancel" `<Button>` in the footer row                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Native `MessageReportItem` (`Message.tsx:675–691`) has no Cancel button — dismissal is via `×` header button or click-outside only                                                                     |
| N77 | Search Filter Inline Lambdas                  | `SearchFilters.tsx`                           | 480, 625          | `SelectSenderButton` and `DateRangeButton` trigger chips use inline `onClick` arrow functions — **WON'T FIX (deliberate)**: purely a code-style nit with zero user-facing or behavioural impact. Inline arrow handlers are idiomatic React and used throughout this very file; extracting them yields no functional benefit.                                                                                                                                                                                                                                             | `OrderButton` (line 58) and `SelectRoomButton` (line 195) both extract a named `const handleOpenMenu: MouseEventHandler<HTMLButtonElement>` handler — bypassing the type annotation in the inline form |
| N78 | HasLink Chip Active Color                     | `SearchFilters.tsx`                           | 755               | `HasLink` active state uses `variant="Primary"` (blue); all boolean scope-toggle chips in the same bar use `variant="Success"` (green) with `outlined` — **FIXED**: changed to `variant={containsUrl ? 'Success' : 'SurfaceVariant'} outlined={!!containsUrl}`                                                                                                                                                                                                                                                                                                           | `variant="Success" outlined` is the established active-state pattern for boolean toggles in the filter bar                                                                                             |
| N79 | Server Notice Chip Radii                      | `RoomViewHeader.tsx`                          | 570               | `<Chip size="400" radii="Pill">` — `Pill` radii on a room-type label — **FIXED**: changed to `radii="300"`                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Room/space type labels in lobby (`RoomItem.tsx:83`, `SpaceItem.tsx:63`) use `radii="300"`; `radii="Pill"` is for filter/tag chips only                                                                 |
| N80 | Server Support Contact Layout                 | `About.tsx`                                   | 172–239           | Homeserver support contacts rendered as raw `<Box direction="Column">` with `<Text as="a">` pairs — custom label/link layout                                                                                                                                                                                                                                                                                                                                                                                                                                             | All other `<SequenceCard>` content in `About.tsx` and `General.tsx` uses `<SettingTile title="..." description="..." after={...}>` as the content unit                                                 |
| N81 | Background Picker Grid — No Responsive Layout | `General.tsx`                                 | 1707–1742         | Fixed `width: toRem(76)` flex-wrap cells with no `minWidth` floor or CSS grid `auto-fill` — SeasonalBgGrid's 13 items produce a visually lopsided orphan last row at any viewport width                                                                                                                                                                                                                                                                                                                                                                                  | Cinny's native grids use `grid-template-columns: repeat(auto-fill, minmax(N, 1fr))` or equivalent for responsive fill                                                                                  |
| N82 | Join/Leave Sounds Auto-Preview                | `General.tsx`                                 | 1592–1609         | Selecting a sound in the dropdown immediately plays a preview, but no UI affordance (button label, description text, or "▶ Preview" button) communicates this to the user                                                                                                                                                                                                                                                                                                                                                                                                | Settings tiles with side effects on selection (theme picker, chat background) show a live visual preview or a dedicated control explaining the side effect                                             |

---

### Round 3 — Rich Topic Editor, RemindMe Dialog, Composer Toolbar, Voice Recorder, Uploads, Location, Mention Highlight

#### 🔴 Additional Major Findings

**N83 — Rich Topic Formatting Toolbar: Raw `<button>` Elements with Fully Inline Styles**

- **File:** `src/app/features/common-settings/general/RoomProfile.tsx`, lines 335–358
- **Status:** **FIXED** — replaced raw `<button>` elements with `<Button size="300" radii="300" variant="Secondary" fill="Soft">` with styled `<Text>` children for B/I/S/code labels
- **Issue:** The four formatting buttons (B, I, S, `` ` ``) in the room topic editor are plain HTML `<button>` elements with entirely inline styles: manual `border`, `borderRadius`, `background`, `color`, `cursor`, `fontSize`, `fontWeight`, `fontStyle`, `fontFamily`, `lineHeight`. They bypass the folds design token system completely — no `variant`, `size`, or `radii` props, no theme-reactive hover/focus states.
- **Root Cause:** Custom addition without referencing folds primitives.
- **Fix:** Replace with `<IconButton type="button" size="300" radii="300" variant="Surface" fill="Soft">` matching the emoji-picker trigger immediately above them at line 285, which already uses the correct pattern.

**N84 — Topic Preview in Room Settings Renders Plain Text Instead of `formatted_body`**

- **File:** `src/app/features/common-settings/general/RoomProfile.tsx`, lines 457–461
- **Status:** **FIXED** — read-mode topic now checks `topic.format === 'org.matrix.custom.html'` and renders `parse(sanitizeCustomHtml(topic.formatted_body))`, matching `RoomTopicViewer` and all other display sites
- **Issue:** The read-mode topic display wraps `topic.topic` (the plain-text field) in `<Linkify>` and never reads `formatted_body`. However `buildTopicContent()` (lines 82–89) intentionally stores both `topic` and `formatted_body` under `org.matrix.custom.html`. After the user saves a formatted topic, the preview panel immediately shows the stripped plain-text version — the formatting appears to disappear within the same settings panel.
- **Root Cause:** The existing `RoomTopicViewer` component (`src/app/components/room-topic-viewer/RoomTopicViewer.tsx:24–51`) already checks `topic.format === 'org.matrix.custom.html'` and pipes `formatted_body` through `sanitizeCustomHtml`. This component is used everywhere else (`RoomIntro`, `LobbyHero`, `RoomItem`, `Invites`, etc.) but not in Room Settings.
- **Fix:** Replace the inline plain-text render with `<RoomTopicViewer topic={roomTopic}>` to match all other display sites.

---

#### 🟠 Additional Moderate Findings

| #   | Area                               | File                       | Lines              | Issue                                                                                                                                                                                                                                                                                                                                                                                                                                                | Native Pattern                                                                                                                                                                                                                     |
| :-- | :--------------------------------- | :------------------------- | :----------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| N85 | RemindMe Dialog Shell              | `RemindMeDialog.tsx`       | 69–81              | Dialog shell is `<Box role="dialog">` with `background`, `borderRadius`, `boxShadow`, `overflow` all set as inline styles using token lookups. Corner radius is `config.radii.R400` which differs from the `R300` embedded in `<Dialog variant="Surface">` — **FIXED**: shell replaced with `<Dialog variant="Surface" style={modalStyle}>`; removed the inline `background`/`borderRadius`/`boxShadow`/`overflow` and the now-unused `color` import | All small message-action dialogs (`LeaveRoomPrompt`, `LogoutDialog`, `JoinAddressPrompt`, `PowerChip`, `DeleteMessageItem`) use `<Dialog variant="Surface" style={modalStyle}>` as the shell                                       |
| N86 | RemindMe Preset Buttons            | `RemindMeDialog.tsx`       | 111–117            | The four preset time choices (20 min, 1 hr, 3 hr, tomorrow) use `<MenuItem size="300" radii="300">` — `MenuItem` is a navigation primitive tied to `menu`/`menubar` ARIA roles; placing it inside `role="dialog"` is an invalid ARIA combination — **FIXED**: each preset is now a folds `<Button variant="Secondary" fill="Soft" radii="300">`, resolving the invalid `menuitem`-in-`dialog` ARIA                                                   | Dialog action choices use `<Button>` (delete/leave/logout dialogs) or `<Chip>` (selection choices). No other dialog in the codebase uses `MenuItem` for action items                                                               |
| N87 | Composer Toolbar Toggle Pattern    | `General.tsx`              | 1100–1114          | Per-button toolbar toggles (Format, Emoji, Sticker, GIF, Location, Poll, Voice, Schedule) use `<Chip variant="Primary"/"Secondary" radii="Pill">` in a wrap grid — a compact chip-toggle grid inside a `SettingTile`, different from every adjacent row                                                                                                                                                                                              | The three sibling tiles in the same `Editor()` function (ENTER for Newline, Markdown, Formatting Toolbar) all use `<SettingTile after={<Switch variant="Primary">}>`. 15+ other binary settings in the file use the Switch pattern |
| N88 | Voice Recorder Recording State     | `VoiceMessageRecorder.tsx` | 195, 206, 240, 276 | Recording container background is `var(--bg-surface-variant)`, the live pulse dot is `var(--tc-danger-normal)`, waveform bars are `var(--tc-primary-normal)` — custom Lotus CSS vars that may not exist in folds themes, falling back to transparent/black — **FIXED**: replaced with `color.SurfaceVariant.Container`, `color.Critical.Main`, `color.Primary.Main`                                                                                  | Native message components use JS-accessible `color.*` tokens that are always populated regardless of theme class                                                                                                                   |
| N89 | Voice Recorder Preview Audio       | `VoiceMessageRecorder.tsx` | 282–283            | Preview state renders bare `<audio src={previewUrl} controls>` — native browser element with inconsistent cross-browser chrome — **FIXED**: replaced with `<audio ref>` + folds `<IconButton>` play/pause toggle; `onEnded` resets playing state                                                                                                                                                                                                     | Native audio messages use folds `Attachment`/`AttachmentContent` layout wrappers; pre-send preview should use `<IconButton>` play/pause controls                                                                                   |
| N90 | Mention Highlight Contrast Formula | `App.tsx`                  | 36–40              | Auto-computed text color (black/white) uses simplified luma `(0.299r + 0.587g + 0.114b)/255 > 0.5` — not WCAG 2.1 relative luminance (which requires gamma linearization) — **FIXED**: replaced with WCAG 2.1 relative luminance formula using `((c+0.055)/1.055)^2.4` gamma linearization; threshold moved from 0.5 to 0.179                                                                                                                        | Folds `color.*.OnContainer` tokens are manually curated to pass WCAG AA 4.5:1 contrast ratios; custom computation must match this guarantee                                                                                        |

---

#### 🟡 Additional Minor Findings

| #   | Area                               | File                     | Lines   | Issue                                                                                                                                                                                                                | Native Pattern                                                                                                                                                   |
| :-- | :--------------------------------- | :----------------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| N91 | Upload Card Caption Input          | `UploadCardRenderer.tsx` | 356–376 | Caption input is raw `<input type="text">` with hardcoded inline CSS using Lotus-specific vars not in folds — **FIXED**: replaced with folds `<Input variant="Secondary" size="300" radii="300">`                    | Other text inputs in the UI use folds `<Input size="300" radii="300">` with folds-token props for all sizing and color                                           |
| N92 | Location "Open Location" Button    | `MsgTypeRenderers.tsx`   | 534–547 | "Open Location" action link uses `<Chip as="a">` — compact badge-sized element — **FIXED**: replaced with `<Button as="a" variant="Secondary" fill="Solid" radii="300" size="400">` matching FileContent pattern     | `FileContent.tsx` uses `<Button variant="Secondary" fill="Solid" radii="300" size="400">` for "Open File"/"Open PDF"                                             |
| N93 | Location Coordinates Text          | `MsgTypeRenderers.tsx`   | 532     | `<Text size="T300" style={{ opacity: 0.65 }}>` — hardcoded non-standard opacity — **FIXED**: replaced with `<Text size="T300" priority="300">`                                                                       | Secondary text uses folds `priority` prop; `0.65` is outside the token scale                                                                                     |
| N94 | Mention Highlight Border Invisible | `App.tsx`                | 41      | `--mention-highlight-border` is set to the same value as `--mention-highlight-bg` — the border is invisible — **FIXED**: border is now `rgba(r,g,b,0.5)` — same hue as the background at 50% opacity, always visible | In folds, `color.*.ContainerLine` is always a lighter/muted sibling of `color.*.Container`, providing the 1px outline that gives mention chips visual definition |
