Skip to main content

max / goingson

chore: prune todo.md, collapse CSS dedup to one-line summaries Migration sweeps A-K are all closed; per-row dates and callsite notes live in git history. Keeps launch readiness intact and §K Phase 6 as the only open CSS item. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-06-03 15:23 UTC
Commit: 5ddd99253da4f1f4fe86c80cfc33b4096e876760
Parent: a7f0f3f
1 file changed, +29 insertions, -324 deletions
M todo.md +29 -324
@@ -1,339 +1,44 @@
1 1 # GoingsOn TODO
2 2
3 - ## Launch readiness (Monday 2026-06-01) — IN PROGRESS
3 + ## Launch readiness — IN PROGRESS (target 2026-06-01 slipped; correctness over deadline)
4 4
5 - ### §3.4 fuzz pass — DONE 2026-05-31
6 - Four-axis parallel audit (rust-fuzz + use-fuzz + creator-fuzz + ux-audit) complete. 10 launch-critical fixes committed (emoji sweep in 3 empty-state strings, About modal + Settings → About both populated with publisher/license/contact/source/privacy/copyright, hard-fail error copy humanized, `questions@` → `info@` in privacy policy, README trust block + Your Data section, bulk-bar CSS color contradiction removed, four bulk Delete buttons given `.btn-danger`). CSS rebuilt via `build-css.sh`.
7 -
8 - ### Deferred to post-launch (audited but not blocking)
9 -
10 - **rust-fuzz**
11 - - ~~`email.rs:598` blocking `fs::read` in async.~~ Done 2026-06-02. Switched to `tokio::fs::read(...).await` — simpler than the `spawn_blocking` recipe and matches the existing async-IO style for tokio::fs::write a few lines above.
12 - - ~~`smtp_client.rs:291` `ContentType::parse(...).unwrap()`.~~ Done 2026-06-02. Replaced unwrap with `or_else` parse + `map_err(...)` to propagate as a real error. `lettre::ContentType` has no public const for octet-stream; this is the closest no-panic path.
13 - - ~~Mutex `.expect("poisoned")` sweep.~~ Done 2026-06-02. All 7 callsites (`state.rs:173`, `sync.rs:{75,177,203}`, `oauth.rs:{131,166}`, `email_sync.rs:49`) switched to `unwrap_or_else(|e| e.into_inner())`. `sync.rs:75` is an `RwLock::read`; the same pattern works.
14 - - `db_watcher.rs:61` — shutdown latency = `MIN_EVENT_INTERVAL_MS`, thread unjoinable. Cosmetic. Leaving.
15 -
16 - **use-fuzz**
17 - - ~~`seed-data.js:393` prod `console.log`.~~ Done 2026-06-02. Removed.
18 - - ~~`app.js` chatty launch logs.~~ Done 2026-06-02. Stripped the four informational `console.log` calls (Desktop loaded, Loaded N projects, Loaded N accounts, external-change/sync-changes/modal-skip/refresh-success). All `console.error` retained.
19 - - ~~`{emails,tasks,task-board}.js` bare "Failed to load".~~ Done 2026-06-02. All four inline error blocks (tasks.js:126, emails.js:105, events.js:419, task-board.js:92) now embed a "Try again" `.btn-link` that calls the corresponding load fn. The toast-side Retry action already existed; this is the parallel in-place recovery.
20 - - ~~`settings.js:196` Linux plugin path.~~ Done 2026-06-02. Replaced `~/.config/goingson/plugins/` with neutral OS-agnostic phrasing ("inside your OS app-data location").
21 - - ~~`index.html:603` literal `x` close button.~~ Done 2026-06-02. Now `&times;`.
22 - - `index.html:18-57` — `?ui=mobile` prod lockdown. Deferred — same item as §K Phase 6, blocks on a desktop-mobile distribution channel existing.
23 -
24 - **creator-fuzz**
25 - - ~~`CHANGELOG.md:24-26` audit-runs language.~~ Done 2026-06-02. Replaced "all issues identified in audit runs 1–12" with six concrete user-visible fixes (empty-state copy, About/Settings populated, hard-fail copy, bulk-action danger color, mutex poisoning recovery, SMTP unwrap fix).
26 - - ~~`docs/data-export.md`.~~ Done 2026-06-02. Written. Covers all five export formats (JSON, gzipped backup, CSV, ICS, raw SQLite), the top-level JSON schema with version + `exportedAt`, per-entity field summaries pointing at `crates/core/src/` for full shapes, denormalized-field caveats, restore semantics (replace vs merge), a verification recipe a user can run with `gunzip` + a text editor, and the not-exported list (OAuth tokens, TOTP secrets, sync state, window state). Source-of-truth pointer at the bottom.
27 - - ~~Settings "Check for updates on launch" toggle.~~ Done 2026-06-02. New `commands::preferences` module persists a tiny JSON pref file in the Tauri app config dir (`preferences.json`, `update_check_on_launch: bool`, default true). Two commands wired: `get_preferences`, `set_update_check_on_launch`. `main.rs` setup hook gates the 10s-delay updater check on the preference. Settings → About now shows a toggle row (new `.settings-toggle-row` primitive) replacing the static "Updates are checked automatically on launch" line; toast confirms the save. API wrapper: `GoingsOn.api.preferences.{get, setUpdateCheckOnLaunch}`. Tauri config (`createUpdaterArtifacts`, endpoints, pubkey) untouched — the toggle gates the check, not the plugin.
28 - - ~~README PolyForm-NC commercial contact.~~ Done 2026-06-02. License section now invites commercial-use inquiries at `info@makenot.work`.
29 -
30 - **ux-audit**
31 - - ~~`styles.css:590-660` — `.btn` offset-shadow brand cue.~~ Done 2026-06-02. `.btn` base now carries `--shadow-brutal-xs` (1px). Hover lifts to 3px shadow + `translate(-1px,-1px)`; active presses to `translate(1px,1px)` + no shadow. Transition extended to include `transform` and `box-shadow`. `.btn-primary` / `.btn-danger` inherit. `.btn-link` and `.btn-icon` are sibling primitives (not `.btn` extenders) so unaffected. `.bulk-actions-bar .btn` inherits cleanly.
32 - - ~~`styles.css:615` — disabled-button contrast.~~ Done 2026-06-02. Opacity dropped; now `transparent bg + muted color + muted border + no shadow`.
33 - - `.pill.active` / `.tab.active` lift — Done 2026-06-02. Both gained `--shadow-brutal-xs` to distinguish active from inactive chrome.
34 - - ~~`styles.css:4712` — `.sync-label` mobile shape variation.~~ Done 2026-06-02. Shape now encodes state on the dot itself (works in mobile + monochrome + high-contrast): `connected`/`syncing` stay circular; `warn` is a rotated square (diamond); `error` is a square. Label-hiding behavior unchanged.
35 - - ~~Single-item destructive `.btn-loading`.~~ Reviewed 2026-06-02 — not applicable. Audit cited `tasks.js:657` but every single-item destructive flow (`tasks.js`, `projects.js`, `events.js`, `emails.js`) is optimistic with `showUndoToast`: the UI removes the item the instant the confirm modal closes, so there is no button still mounted to carry a loading state. Optimism + undo is a stronger UX than a spinner. Closing without code change.
36 - - ~~`index.html:466` straggler `row-flex-2`.~~ Done 2026-06-02. Now matches neighbors as `row-flex row-flex-2`.
37 - - ~~Back-arrow consistency.~~ Done 2026-06-02. `index.html:270` literal `← Back` → `&larr; Back` matching the other three back-arrow buttons.
5 + ### §3.2 distribution — NEXT, blocks launch
6 + - Signed builds + notarization stapled + DMG smoke test on a clean macOS account.
7 + - Build on pop-os (x86_64) and astra (aarch64) natively per the no-cross-compile rule.
8 + - macOS notarization via altool key in `_private/`.
9 + - Smoke test: install DMG on a clean macOS user account, launch, verify Gatekeeper passes, run the welcome flow, exit, relaunch.
10 + - iOS TestFlight: re-check the appiconset alpha-flatten step (required every iOS build per memory).
38 11
39 - ### §3.1 — DONE for emoji/About/error-copy. Remaining:
40 - - Mobile UI mode override hardening (see use-fuzz deferral above).
12 + ### §3.1 mobile UI mode hardening — DEFERRED
13 + - `index.html:18-57` `?ui=mobile` prod lockdown. Same item as §K Phase 6; blocks on a desktop-mobile distribution channel existing.
41 14
42 - ### §3.2 distribution — NOT STARTED
43 - - Signed builds + notarization stapled + DMG smoke test on a clean macOS account.
15 + ### §3.4 fuzz pass — DONE 2026-05-31
16 + Four-axis parallel audit (rust-fuzz + use-fuzz + creator-fuzz + ux-audit). Launch-critical fixes committed pre-launch; deferred items closed 2026-06-02 (commits `7421ead`, `9719e28`). One cosmetic item left: `db_watcher.rs:61` shutdown latency = `MIN_EVENT_INTERVAL_MS`, thread unjoinable. Not worth fixing.
44 17
45 18 ### §3.3 repo hygiene — DONE 2026-06-02
46 - - Stray root planning files: already moved to `docs/_archive/` pre-session.
47 - - `generate_pitch_pdf.py` + `GoingsOn-Pitch.pdf` deleted (regenerable; git preserves).
48 - - Root `_archive/` (pre-Tauri Rust HTTP server) deleted; migration shipped 2026-06-01, git history is the right home for pre-migration reference.
49 -
50 - CSS dedup work below is the active in-progress stream; launch readiness is its own track.
51 19
52 20 ---
53 21
54 - # GoingsOn CSS Dedup TODO
22 + # GoingsOn CSS Dedup — DONE (2026-05-24)
55 23
56 - Charter + primitive vocabulary lives at the top of
57 - `src-tauri/frontend/css/styles.css`. The constraint that "visual output
58 - must not change" has been relaxed: visual *changes* are OK as long as
59 - they're not worse. Prefer one consistent visual per element family.
60 -
61 - Always rebuild after changes:
24 + Charter + primitive vocabulary lives at the top of `src-tauri/frontend/css/styles.css`. Always rebuild after changes:
62 25 ```
63 26 cd src-tauri/frontend && ./build-css.sh
64 27 ```
65 28
66 - ---
67 -
68 - ## A. Buttons — consolidate to one family
69 -
70 - 53 button-related selectors live in `styles.css`. They cluster around 4
71 - canonical primitives (`.btn`, `.btn-icon`, `.btn-link`, `.btn-loading`)
72 - plus ~15 bespoke `*-btn` classes that each invented their own padding /
73 - font / hover.
74 -
75 - ### Canonical button family (target)
76 - - `.btn` — base. `inline-flex`, padding `0.625rem 1.25rem`, border, radius-sm, font 0.9rem/600.
77 - - Intents: `.btn-primary`, `.btn-secondary`, `.btn-danger`.
78 - - Sizes: `.btn-sm` (+ new `.btn-lg` if any of the bespoke ones genuinely need it).
79 - - Variants: `.btn-icon` (chrome icon button, no border), `.btn-link` (text-link button).
80 - - States: `:hover`, `:active`, `:disabled`, `.btn-loading`.
81 -
82 - ### Bespoke buttons to migrate
83 - For each, find the closest canonical and migrate. Visual deltas are
84 - acceptable when they bring the button into the system.
85 -
86 - | Bespoke class | Maps to | Notes |
87 - |---------------|---------|-------|
88 - | ~~`.settings-btn`~~ | `.btn` | Done 2026-05-24. Structural styling (bg-card, border, radius-sm, hover/active) already matched `.btn`; only deltas were font-size (1.25rem → 0.9rem) and padding (tighter horizontal). Accepted smaller per charter. `margin-left` dropped — `.header-actions` parent already sets `gap: 0.5rem`. Mobile path unaffected (`.app-header` is `display:none` on `.ui-mode-mobile`). |
89 - | ~~`.shortcut-hint-btn`~~ | `.btn.shortcut-hint-btn` | Done 2026-05-24. Marker class retained for mono font + min-width + tighter padding override (single-glyph `?` button) and for the touch/mobile `display:none` rule. Bespoke chrome (border/bg/hover) dropped — now inherited from `.btn`. |
90 - | ~~`.settings-back-btn`~~ | `.btn-link.mb-1.settings-back` | Done 2026-05-24. Marker class kept for mobile-pass override. |
91 - | `.settings-nav-item` | New `.btn-nav` *or* not-a-button | It's a nav item, not really a button. Keep as is. |
92 - | ~~`.undo-btn`~~ | `.btn.btn-sm.btn-primary` | Done 2026-05-24. Bespoke was custom accent-blue; .btn-primary matches. |
93 - | ~~`.account-delete-btn`~~ | `.btn.btn-sm.btn-danger` | Done 2026-05-24. |
94 - | ~~`.bulk-modal-option-btn`~~ | `.btn.btn-sm.text-left.w-full` + marker class | Done 2026-05-24. New `.w-full` utility; `.bulk-modal-scroll` now flex column with gap; marker class retained for mobile padding override. Stale `btn-ghost` reference removed from callsites. |
95 - | ~~`.email-attachment-remove-btn`~~ | (dead code) | Done 2026-05-24: rule deleted, no callsites. |
96 - | ~~`.toggle-cc-btn`~~ | `.btn-link` | Done 2026-05-24. Accepts underline visual delta. |
97 - | ~~`.attachment-delete-btn`~~ | `.text-accent-red` | Done 2026-05-24: already had `.btn.btn-sm.btn-secondary`; bespoke was only the red color. |
98 - | ~~`.welcome-step-btn`~~ | `.text-left` | Done 2026-05-24: already had `.btn.btn-secondary`; new `.text-left` utility added next to `.text-center`. |
99 - | ~~`.kebab-btn`~~ | `.btn-icon` + slim bespoke for hover-reveal/font-override | Done 2026-05-24. 5 callsites now `btn-icon kebab-btn`. |
100 - | ~~`.schedule-task-btn`~~ | (dead code) | Done 2026-05-24: rule deleted, no callsites. |
101 - | ~~`.time-block-quick-btn`~~ | `.btn.btn-sm.btn-secondary` + marker class | Done 2026-05-24. Bespoke base styles dropped; only `.selected` state override retained. `.selected` JS class kept (not renamed to `.is-selected` — out of scope). |
102 - | ~~`.month-goal-status-btn`~~ | `.btn-icon` + slim bespoke for color/sizing | Done 2026-05-24. |
103 - | ~~`.month-goal-delete-btn`~~ | `.btn-icon` + slim bespoke for hover-reveal/color | Done 2026-05-24. |
104 - | `.view-toggle-btn` (+active) | Keep as-is | Reviewed 2026-05-24. Already its own primitive (joined borders, zero-gap segmented look); not duplication of `.btn`. Rename to `.segmented-control` deferred — cosmetic only. |
105 - | ~~`.btn-danger-text`~~ | `.text-accent-red` (generic utility) | Done 2026-05-24. Kept the secondary chrome on Delete buttons — button-shaped affordance matters for destructive actions; the red text is enough to signal intent. 5 callsites (emails.js, events.js, contact-dashboard.js, task-overview.js, tasks.js) now read `.btn.btn-secondary.text-accent-red`. Bespoke 1-line rule deleted; the existing `.text-accent-red` utility already provided the color. |
106 -
107 - Expected savings: ~80-120 source lines, ~2-3 KB min.css.
108 -
109 - ### Migration approach
110 - 1. Audit each bespoke class's callsites: `grep -rn "<class>" js/ *.html`.
111 - 2. Edit one bespoke class at a time:
112 - a. Decide the canonical mapping.
113 - b. Update callsites to use canonical class(es).
114 - c. Delete bespoke rule from styles.css.
115 - 3. After each migration, rebuild + eyeball the affected surface.
116 - 4. If migration reveals a missing modifier (e.g. `.btn-icon--lg`), add
117 - it next to `.btn-icon` and document in the charter inventory.
118 -
119 - ---
120 -
121 - ## B. Form inputs & selects — one input family
122 -
123 - ### Canonical (target)
124 - - `.form-input` — base text input.
125 - - `.form-select` — base select.
126 - - Sizes: `.form-input--w-200`, `.form-select--compact` (already in place).
127 - - Variants: new `.form-input--ghost` (borderless, used by compose
128 - header rows), new `.form-input--search` (search affordance).
129 -
130 - ### Migrate
131 - - ~~`.quick-add-input`~~ — Done 2026-05-24: dead code. `.quick-add` parent + `.quick-add-input` rules deleted (~28 lines). The actual quick-add modal at `keyboard.js:openQuickAddModal` uses `.form-input`. §10 section header vacated; navigation numbering preserved.
132 - - `.filter-select` → **Skip.** Reviewed 2026-05-24: not duplication. Bespoke focus styling (fills with accent-blue) is a distinctive UX cue, and the smaller padding/font-weight differ enough from `.form-select.form-select--compact` that the migration would lose visual identity for a ~5-line CSS win. Leave as-is.
133 - - ~~`.header-input`~~ → `.form-input.form-input--ghost.flex-1`. Done 2026-05-24. New `.form-input--ghost` modifier added next to base in §20.
134 - - ~~`.header-select`~~ → `.form-select.form-select--ghost.flex-1`. Done 2026-05-24. New `.form-select--ghost` modifier added.
135 -
136 - Savings achieved: ~29 lines (29/(40-60) of estimate).
137 -
138 - ---
139 -
140 - ## C. Cards — one card family with shell + muted variants
141 -
142 - ### Canonical
143 - - `.card` — interactive tile (current).
144 - - `.card--list-item` — compact list row (already added).
145 - - New `.card--muted` — bg-secondary, no shadow/hover, no cursor (mini
146 - info card).
147 - - New `.card--shell` — container shell: no padding, no cursor, no
148 - hover, flex column, overflow handling. For list shells like
149 - `.email-list` and `.dashboard-column`.
150 -
151 - ### Migrate
152 - - ~~`.email-list`~~ → `.card.card--shell.email-list`. Done 2026-05-24. Slim feature rule keeps only `flex: 1; min-height: 0;`.
153 - - `.dashboard-column` → **Skip.** Has no shadow + thinner border, doesn't actually match `.card`'s look; migrating would change visuals across project dashboard. Leave page-scoped.
154 - - ~~`.contact-info-section`~~ → `.card.card--muted.contact-info-section`. Done 2026-05-24. Slim feature rule keeps gap + margin + tighter padding.
155 - - `.contact-summary-stat` → **Skip.** Page-scoped is appropriate (feature-specific stat tile with min-width 80px); not duplication of `.card`.
156 - - ~~`.review-card`~~ → `.card.card--static.review-card`. Done 2026-05-24. New `.card--static` variant added next to `--shell` and `--muted` in §11 (cursor default + no-op hover, full card chrome retained). `.review-card` base slimmed to a single `padding: 1.5rem` declaration; descendants (`.card-header`, `.card-title`, `.card-icon`, `.card-badge`) trimmed to only their review-specific deltas (font-family/font-size/font-weight all inherit from the canonical `.card-*` descendants now). 14 callsites migrated across `plan-review-toggle.js`, `weekly-review-render.js`, `monthly-review-render.js`. Eyeball: weekly + monthly review screens.
157 - - ~~`.kanban-card`~~ → `.card.kanban-card`. Done 2026-05-24. Bespoke bg/border/box-shadow/transition/hover declarations dropped — all inherited from `.card`. Marker class slimmed to its deltas: `border-width-sm` (thinner), `border-radius: 0` (square), `padding: 0.75rem` (tighter), `cursor: grab`, `border-left: 4px solid transparent` (priority hook). Hover transform now inherited (was duplicate). 1 callsite in `tasks-kanban.js`. Eyeball: kanban board view + drag interaction.
158 -
159 - Savings achieved: ~30 net source lines (~17 lines from .review-card bespoke chrome + ~10 lines from .kanban-card duplicate hover/chrome; ~10 lines added for .card--static infra). min.css 152970 → 152465 (-505 bytes).
160 -
161 - ---
162 -
163 - ## D. Badges & tags — unify the chip family
164 -
165 - ### Canonical
166 - - `.tag, .badge` — bordered chip primitive (current).
167 - - Color variants via `[data-color="green|yellow|red|cyan|purple|muted"]`
168 - (already exists).
169 - - New size modifiers: `.badge--sm`, `.badge--xs`.
170 - - New intent modifier: `.badge--filled` (solid accent fill, no border).
171 -
172 - ### Migrate
173 - - `.thread-badge` → **Skip.** Already audited 2026-05-21 as net-zero dedup; composing `.badge` would require overriding every declaration. Leave standalone.
174 - - ~~`.email-label-badge`~~ → `.badge.badge--xs.badge--filled[data-color="blue"]`. Done 2026-05-24. 2 callsites in `emails.js`. Visual delta: text color now white-on-blue instead of lavender-on-blue (acceptable per charter).
175 - - `.subtask-linked-tag` → **Skip.** Tiny 2-property rule (color + font-size), 1 callsite, not really a chip — it's an inline "[Linked]" label. Page-scoped is appropriate.
176 - - `.card-badge--success/-warning/-info/-danger` → **Skip.** Self-contained BEM system inside `.review-card` context; base lives in parent-scoped rule. Already minimal; not duplication of `.badge`.
177 - - ~~`.badge-completed`, `.badge-started`, `.badge-pending`, `.badge-priority-h/m/l`, `.badge-focus`, `.badge-overdue`, `.badge-snoozed`~~ → Done 2026-05-24. All 9 bespoke rules deleted. `task-overview.js:322-326` rewritten with a `chip(color, text)` helper that emits `.badge.badge--xs.badge--filled[data-color="X"]`. New modifiers added in §12: `.badge--xs`, `.badge--sm`, `.badge--filled` (with per-color overrides), plus a new `data-color="blue"` variant on the base.
178 -
179 - Savings achieved: ~4 net source lines (added 22 of modifier infra to delete 22 of bespoke + 9 lines deleted from emails.js + task-overview.js path). Real win is qualitative: one composable badge system, no more invent-a-badge-per-feature.
180 -
181 - ---
182 -
183 - ## E. Avatars — DONE (2026-05-24)
184 -
185 - Promoted `.avatar` primitive in §47 Contacts with `.avatar--sm` (32px, no border), `.avatar--lg` (60px), `.avatar--unknown` (muted bg). All 5 callsites migrated: emails.js (×2), contact-dashboard.js (×1), contacts-render.js (×2). Bespoke rules deleted: `.contact-avatar`, `.contact-avatar-lg`, `.contact-avatar-large`, `.contact-avatar-sm`, `.contact-avatar-unknown`. Net ~30 source lines removed. `.contact-avatar-large` was 64px; new `.avatar--lg` is 60px (4px visual delta, accepted).
186 -
187 - ---
188 -
189 - ## F. Status dots — REVIEWED, SKIP (2026-05-24)
190 -
191 - Audited 2026-05-24. Three dot patterns exist: `.sync-dot` (8px, connect/sync/warn/error), `.sync-status-dot` (10px, fixed green), `.tab-status-dot` (8px, none/green/yellow/red with pulse animations). Variation in animations and positioning is real; unifying under `.status-dot` would either lose animation timing or require many overrides. Combined surface is ~25 lines; consolidation savings would be <10 lines after adding the primitive. Leave as-is.
192 -
193 - ---
194 -
195 - ## G. Toggles — REVIEWED, CLEAN (2026-05-24)
196 -
197 - `.toggle-switch` is the only on/off switch primitive in the codebase. `.past-events-toggle` and `.form-more-toggle` are `<summary>`-style disclosure controls (different pattern), not duplicates. Nothing to consolidate.
198 -
199 - ---
200 -
201 - ## H. Empty states — REVIEWED, CLEAN (2026-05-24)
202 -
203 - Audited 2026-05-24. No ad-hoc "No X yet" / "Nothing X" markup found in js/ or html. The `.empty-state` primitive + variants (`--compact`, `--dashboard`, `--error`) are the only empty-state surface. Nothing to migrate.
204 -
205 - ---
206 -
207 - ## I. Misplaced / global utilities to relocate — DONE (2026-05-24)
208 -
209 - - ~~`.kbd-hint`~~ relocated from §41 Settings to §12 Tags & Badges (right after the `.badge--filled` color overrides). Comment added explaining the move.
210 - - `.visually-hidden` was already deleted previously.
211 - - Audited `.address-highlight-mirror` and `.addr-*` color classes: tightly coupled to the address-highlighter overlay element (positional, feature-specific). Not generic text-color utilities. Leave page-scoped.
212 -
213 - ---
214 -
215 - ## J. Tier 3 (deferred, regression-prone)
216 -
217 - These were called out earlier as needing the dev server running for
218 - visual diffs. Do AFTER A–F so the consolidated primitives reduce
219 - surface area first.
220 -
221 - - ~~**Consolidate the 3 mobile sections** (§25 / §59 / §60) into one
222 - RESPONSIVE band at end-of-file.~~ Superseded by **K. UI Mode
223 - Separation** (2026-05-22). Mobile rules now live behind
224 - `.ui-mode-mobile` class selectors, not media queries — the original
225 - "one RESPONSIVE band" goal is moot.
226 - - ~~**Resolve V1/V2 Weekly Review.**~~ Done 2026-05-24. Audited every
227 - §49 class against actual JS/HTML usage: 20+ V1 classes had zero
228 - callsites (`.stat-card*`, `.review-section`, `.section-title`,
229 - `.review-details*`, `.review-task-list`, `.review-event-list`,
230 - `.review-task-item*`, `.review-event-item*`, `.project-badge`,
231 - `.due-badge*`, `.focus-task-list*`, `.focus-toggle*`,
232 - `.no-focus-message`, `.focused-projects`, `.project-tag`,
233 - `.notes-section`, `.review-notes-input*`, `.review-actions`,
234 - `.week-info`). Deleted all. Kept the surviving chrome still composed
235 - by `weekly-review.js` (header, status pill, week-dates, event-time,
236 - focus-section gradient) and tab indicators (`.tab-badge`,
237 - `.tab-status-dot`) used by tab nav + events.js. §49 went from 326
238 - lines to 106. `mobile.js` had no V1 weekly-review classes — only a
239 - `getElementById('weekly-review-content')` lookup, still valid.
240 - Section header renamed to "Weekly Review — chrome around the V2 grid
241 - + tab indicators."
242 - - ~~**Print consolidation** (§48 + §52).~~ Done 2026-05-24. §52 (152
243 - lines, weekly-review-specific `@media print`) folded into §48 inside
244 - a single consolidated `@media print` block. Organized with three
245 - subsection comments: "Global chrome", "Tables", "Weekly review".
246 - Hidden-selector list deduplicated (e.g. one `display: none` block
247 - now lists global + weekly-review hide targets together). §52
248 - vacated; only one `@media print` block remains in the file.
249 -
250 - ---
251 -
252 - ## K. UI Mode Separation — DONE (2026-05-22)
253 -
254 - Architectural refactor: mobile vs desktop UI is no longer selected by
255 - viewport width. Set once at boot via `<html class="ui-mode-mobile|desktop">`
256 - from the inline detection script in `index.html`. Desktop binaries stay
257 - desktop even when the window is narrowed.
258 -
259 - Phases 1–5 complete (~3 hours total). Full plan and post-audit live at:
260 - - `ui_mode_separation_plan.md` (the plan)
261 - - `css_state_audit.md` (where things landed)
262 -
263 - ### Key outcomes
264 - - 8 `@media (max-width: 768px)` blocks → 0. 228 selectors prefixed `.ui-mode-mobile`.
265 - - 6 stray non-768 mobile breakpoints (600/640/900/1100) → converted to `.ui-mode-mobile` rules.
266 - - Remaining `@media` queries: 1 `(hover: none)` (hover suppression),
267 - 1 `(max-width: 1024px)` + 1 `(min-width: 1400px)` (both intra-desktop,
268 - selectors `.ui-mode-desktop`-prefixed), 2 `print`. Zero mode-switching queries.
269 - - `[style*="display:none"]` selector hack removed.
270 - - JS layout-mode checks (`innerWidth <= 768`/`<= 600`) → `GoingsOn.viewport.isMobile()`.
271 - - New `js/viewport.js` module; new inline detection script in `index.html` head.
272 - - Section header at `styles.css:159–185` rewritten to document the new model.
273 - - `#task-view-toggle { display: none }` moved out of `@media (hover: none)`
274 - (was wrong axis — input capability instead of UI mode).
275 - - Latent bug fixed: mobile `@keyframes modalSlideIn/Out` were globally
276 - overriding the desktop variants (last-keyframes-wins). Renamed to
277 - `*Mobile` and applied via `animation-name` override.
278 -
279 - ### Phase 6 — remaining (not blocking)
280 - model for new contributors. Done 2026-05-24. Placed after "CSS
281 - Workflow"; covers CSS/JS surfaces, detection precedence, dev preview,
282 - the "mode is a build property, not viewport size" invariant, and the
283 - separate input-capability axis.
284 - detection script in `index.html` to read `window.__TAURI__.platform`
285 - before the UA fallback. Done 2026-05-24. Tauri 2's platform plugin
286 - is async (no sync `window.__TAURI__.platform`), which doesn't fit an
287 - inline-head script that must set the class before the stylesheet
288 - evaluates. Substituted: `navigator.userAgentData.mobile` (UA Client
289 - Hints) as a third tier, plus iPad-as-Mac detection
290 - (`navigator.maxTouchPoints > 1` on a Mac-reporting platform) in the
291 - UA regex tier. Works for WKWebView (iOS), Android WebView, and
292 - catches Safari 13+ iPads that report as Mac. Detail in
293 - `css_state_audit.md` "Detection script — updated 2026-05-24".
294 - - [ ] For production lockdown of the override (so users can't set
295 - `?ui=mobile` on a desktop build and break things): inject
296 - `__GO_BUILD_MODE__` at build time and gate the override branches.
297 - Defer until a production mobile-only desktop-binary distribution
298 - channel exists. (TestFlight iOS is its own binary; no override
299 - risk there.)
300 -
301 - ### Divergences from the plan
302 - - Kept `.mobile-hide` utility class (plan called for removing it).
303 - Rationale: it's one rule, the marker class makes intent visible in
304 - markup, and removing it would shift verbosity from CSS to HTML.
305 - - Phase 4 scope: prefixed only the *layout* rules that mobile UI
306 - hides entirely (header, top tab nav, pills, sidebars). Did not
307 - prefix shared component rules (buttons, forms, modals, cards) —
308 - they correctly stay in the base scope.
309 -
310 - ---
311 -
312 - ## Suggested order
313 -
314 - 1. **A. Buttons** — biggest leverage, sets the design language.
315 - 2. **B. Form inputs** — closely related (form chrome consistency).
316 - 3. **D. Badges** — small surface, quick win once button pattern is set.
317 - 4. **C. Cards** — falls into place once buttons + inputs unified.
318 - 5. **E. Avatars / F. Status dots / G. Toggles / H. Empty states** — cleanup.
319 - 6. **I. Relocate globals** — janitorial, do last.
320 - 7. **J. Tier 3** — only when surface area is reduced.
321 -
322 - ---
323 -
324 - ## Process notes
325 -
326 - - Rebuild `styles.min.css` via `./build-css.sh` after every batch.
327 - - The composition charter at the top of `styles.css` is the source of
328 - truth for what primitives exist. Update the EXISTING VOCABULARY
329 - inventory there whenever you add or rename a primitive/modifier.
330 - - Don't ship two ways to do the same thing during a migration; if A
331 - half-done blocks B, finish A first.
332 - - Spot-check after each surface migration:
333 - - Settings overlay (gear button, back button, nav)
334 - - Email list, email reader, compose modal, compose window
335 - - Project dashboard tiles
336 - - Task table action buttons
337 - - Toast undo
338 - - Bulk-select modal
339 - - Welcome wizard
29 + All consolidation sweeps closed. Outcomes:
30 +
31 + - **A. Buttons** — 14 bespoke `*-btn` classes migrated to `.btn` family (intents/sizes/variants); 2 deleted as dead code. Remaining bespoke (`.view-toggle-btn`, `.kebab-btn`, `.shortcut-hint-btn`, etc.) are slim markers on top of canonical `.btn` and are correct as-is.
32 + - **B. Form inputs** — `.header-input`/`.header-select` migrated to `.form-input--ghost`/`.form-select--ghost`; `.quick-add-input` was dead code, deleted. `.filter-select` kept (distinctive UX cue).
33 + - **C. Cards** — `.email-list`, `.contact-info-section`, `.review-card`, `.kanban-card` migrated to `.card` + variants (`--shell`/`--muted`/`--static`). `.dashboard-column` and `.contact-summary-stat` kept page-scoped.
34 + - **D. Badges** — 9 task-status bespoke badges replaced with composable `chip(color, text)` helper emitting `.badge.badge--xs.badge--filled[data-color="X"]`. New `--xs`/`--sm`/`--filled` modifiers.
35 + - **E. Avatars** — `.avatar` primitive promoted with `--sm`/`--lg`/`--unknown`; all 5 bespoke contact-avatar rules deleted.
36 + - **F. Status dots** — reviewed, skip. Three variants (`.sync-dot`, `.sync-status-dot`, `.tab-status-dot`) have meaningfully different animation timing; unifying would lose nuance for <10 lines saved.
37 + - **G. Toggles** — clean. `.toggle-switch` is the only on/off primitive; `<summary>`-style disclosures are a different pattern.
38 + - **H. Empty states** — clean. `.empty-state` + variants are the only surface.
39 + - **I. Globals relocation** — `.kbd-hint` moved into §12; `.visually-hidden` already removed.
40 + - **J. Tier 3** — mobile section consolidation superseded by §K; V1/V2 weekly review resolved (§49 trimmed 326→106 lines); print consolidation done (§52 folded into §48).
41 + - **K. UI Mode Separation** — set once at boot via `<html class="ui-mode-mobile|desktop">`. 8 viewport media queries → 0. Phases 1–5 complete; full notes in `ui_mode_separation_plan.md` + `css_state_audit.md`.
42 +
43 + ### §K Phase 6 — open, blocked
44 + - [ ] Inject `__GO_BUILD_MODE__` at build time to gate `?ui=mobile` override branches against production tampering. Defer until a production mobile-only desktop-binary distribution channel exists. (TestFlight iOS is its own binary; no override risk there.)