- Header.css: mobile padding-top is additive (var(--space-md) + env(safe-area-inset-top)) so the brand row keeps its 12px breathing room below the Android status bar instead of having it replaced by the inset.
- ExecutorStatusBar.css: bottom offset now uses max(env(safe-area-inset-bottom), 12px) to match the floor MobileNavBar already applies, so the footer lands flush on top of the nav instead of inside its padding band.
- useToast.ts: silently drop bare "Failed to fetch" error toasts (from fetch() aborts on tab background/resume); toasts with additional context still pass through.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Some Android Chrome builds (multi-window / split-screen / certain WebViews)
leave window.innerWidth/Height stuck larger than the actual rendered canvas.
DOM, body, and visualViewport report the true dimensions, but position:fixed
uses the ICB, pinning fixed-bottom elements offscreen below the visible area.
JS-side meta override (setAttribute and full replacement) does not force
Chrome to recompute the ICB on those builds.
index.html now publishes the ICB→visualViewport delta as CSS variables
(--icb-bottom-offset, --icb-right-offset) on <html>. MobileNavBar.css and
ExecutorStatusBar.css consume them so the bars pin to the visible viewport
edge regardless of ICB drift. Math is visualViewport-relative so it also
handles pinch-zoom in (offsets compensate) and pinch-zoom out (clamp at 0).
Healthy browsers see 0px and behave unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Broaden mobile media query to include (max-height: 480px) so landscape
phones (which exceed 768 CSS px wide) still render the bottom nav and
mobile board layout instead of desktop horizontally-scrollable columns.
- Guard useMobileKeyboard against pinch-zoom (vv.scale > 1) — Android
Chrome ignores user-scalable=no, and a focused textarea + zoom was
false-positiving keyboard-open and hiding MobileNavBar.
- Read documentElement.clientHeight instead of stale window.innerHeight
when computing keyboard overlap (Android multi-window can leave
innerHeight cached at a wildly different value than the actual layout
viewport — observed 2848 while html was 797).
- Add interactive-widget=resizes-content to the viewport meta so Android
Chrome shrinks the layout viewport with the soft keyboard, matching iOS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merges FN-3954: adds a blocker fanout system where task staleness automatically escalates to upstream blocking tasks, with a new `blocker-fanout` core module, dashboard hooks wiring that surfaces escalation status on the board and executor status bar, and a new settings toggle to disable escalation.
Fusion-Task-Id: FN-3954
Adds a "high fan-out" indicator that surfaces when a task blocks an unusually large number of downstream tasks, displayed as a warning badge on TaskCards and in the ExecutorStatusBar. The feature includes core type definitions, dashboard UI updates to render the warning with appropriate styling and
Fusion-Task-Id: FN-3942
This merge bundles six feature steps across mobile PWA spacing, test infrastructure, MissionManager tokenization, and Insights coverage. Key changes include restoring the `--standalone-bottom-gap` CSS token contract for iOS home indicator spacing, a refactored `test-changed.mjs` script with workflow
Fusion-Task-Id: FN-3232
- Improve automation startup diagnostics and route handling for manual execution steps
- Add support for full manual automation step execution in dashboard and engine flows
- Expand due-schedule coverage in automation store and dashboard route tests
- Add cron runner regression tests for edge cases and document the automation execution fix via changeset
Apply a 2px-left / 1px-up transform to the bare folder toggle so it sits
flush with the surrounding status-bar text on desktop. Touch-target sizing
on coarse pointers / mobile is unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removed the always-on 40px touch-target sizing from the folder toggle so the
icon sits at the same baseline as adjacent status-bar text. Touch targets
are still applied via @media (pointer: coarse) and (max-width: 768px) so
mobile keeps a tappable hit area. line-height: 1 prevents the icon's box
from inheriting status-bar text leading.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The status-bar folder-toggle button had a 1px border + radius that made it
look like a chip. Per request, render it as a bare icon — transparent
background, no border. Hover now only shifts the icon color, the active
state drops to color only, and focus-visible still draws a focus ring with
border-radius applied so it remains a clean halo.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Split app/styles.css from ~40k lines down to ~4.5k. Created 56 co-located
component CSS files in app/components/, each imported by its owning .tsx.
The remainder of styles.css holds genuinely global rules (design tokens,
.btn/.card/.modal/.form-input primitives, cross-component @media overrides).
- Lazy-load 13 heavy views (AgentsView, RoadmapsView, NodesView, etc.) via
React.lazy + Suspense; prefetch all chunks on idle so first navigation is
instant. Initial JS bundle: 1.58 MB → 1.16 MB (-26%). Initial CSS bundle:
635 kB → 471 kB (-26%); the rest splits into 13 per-view chunks.
- Add app/test/cssFixture.ts exposing loadAllAppCss() + loadAllAppCssBaseOnly()
so CSS regression tests load the full per-component bundle (mirroring Vite
source order). Migrate 30+ tests off direct readFileSync('../styles.css').
- Enable test.css: { include: [/.+/] } in vitest.config.ts so component CSS
imports actually inject styles in jsdom (fixes getComputedStyle assertions).
- Add ESLint rule (no-restricted-syntax) banning direct styles.css reads in
dashboard test files; points at loadAllAppCss() instead.
- Restore lost utility classes (.text-muted, .text-secondary, .text-dim,
.form-input) and rescue dropped chat tool-call rules into QuickChatFAB.css.
- Mobile fixes along the way: scroll containment for view containers
(min-height:0 + -webkit-overflow-scrolling), QuickChatFAB full-screen on
mobile (with safe-area-inset for iOS home bar), AgentsView single-row
header layout, ActivityLogModal close button on right, model-combobox
z-index above the mobile quick-chat panel.
- Bug fix: SkillsView toggle was display:none which hid the input from the
accessibility tree; replaced with the visually-hidden pattern so screen
readers + getByRole still find the checkbox.
- Bug fix: standalone Delete button in TaskDetailModal for triage-column
tasks (Actions dropdown is hidden in triage state, so previously no way
to delete a freshly-created task without status change first).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>