diff --git a/.changeset/planning-mobile-scroll.md b/.changeset/planning-mobile-scroll.md new file mode 100644 index 0000000000..3ca9cba525 --- /dev/null +++ b/.changeset/planning-mobile-scroll.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Fix Planning Mode not scrolling on mobile so action buttons stay reachable. +category: fix +dev: The global mobile `.modal-lg`/`.modal:not(.confirm-dialog)` 100dvh rule was matching the embedded Planning shell (`.planning-modal--embedded`) and stretching it past its bounded `.planning-view` pane, clipping the footer under `overflow:hidden`. Mobile rule now qualifies as `.planning-view.open .planning-modal--embedded` (specificity 0,3,0) and re-pins `max-height:100%` so the inner flex scroll chain works. diff --git a/packages/dashboard/app/components/PlanningModeModal.css b/packages/dashboard/app/components/PlanningModeModal.css index c2962178ba..5c6ecef699 100644 --- a/packages/dashboard/app/components/PlanningModeModal.css +++ b/packages/dashboard/app/components/PlanningModeModal.css @@ -480,9 +480,14 @@ The New session button must look EXACTLY like Missions' primary sidebar create b .planning-view { padding: var(--space-sm); } - .planning-view .planning-modal--embedded { + /* + FNXC:PlanningMode 2026-06-25-13:10: + The global mobile modal rule in styles.css (`@media (max-width:768px) .modal:not(.confirm-dialog), .modal-lg, ...`) forces every full-screen modal to height/max-height:100dvh. The embedded Planning shell carries `.modal-lg`, so it matched that rule and was stretched to the full 100dvh viewport even though its parent `.planning-view` is bounded between the header and the bottom nav (≈688px). Because `.modal:not(.confirm-dialog)` resolves to specificity (0,2,0) — equal to `.planning-view .planning-modal--embedded` — the global rule won on source order, so the embedded shell overflowed past the viewport, `.planning-view { overflow:hidden }` clipped the footer action buttons, and the summary/question panes could not scroll on mobile. Qualify with `.planning-view.open` (0,3,0) and re-pin max-height so the embedded view stays inside its bounded pane and the inner flex scroll chain works. + */ + .planning-view.open .planning-modal--embedded { width: 100%; height: 100%; + max-height: 100%; } .planning-modal-body--split { flex-direction: column; diff --git a/packages/dashboard/app/components/__tests__/PlanningModeModal.css.test.ts b/packages/dashboard/app/components/__tests__/PlanningModeModal.css.test.ts index 96f5cd0ddd..c78b77f632 100644 --- a/packages/dashboard/app/components/__tests__/PlanningModeModal.css.test.ts +++ b/packages/dashboard/app/components/__tests__/PlanningModeModal.css.test.ts @@ -49,4 +49,22 @@ describe("PlanningModeModal CSS responsive action contract", () => { expectSomeRule(mobileCss, ".planning-summary-actions-right", /flex-direction\s*:\s*column\s*;/); expectSomeRule(mobileCss, ".planning-summary-actions-right", /width\s*:\s*100%\s*;/); }); + + // FNXC:PlanningMode 2026-06-25-13:10: regression for the embedded Planning view not + // scrolling on mobile. The global mobile `.modal:not(.confirm-dialog), .modal-lg, ...` + // 100dvh rule (specificity 0,2,0) matched the embedded shell and stretched it past its + // bounded `.planning-view` pane, so `.planning-view { overflow:hidden }` clipped the + // footer action buttons. The mobile embedded override must (a) qualify with + // `.planning-view.open` so it outranks (0,3,0 > 0,2,0) that global rule, and (b) re-pin + // `max-height` so the embedded shell cannot exceed its pane and the inner flex scroll + // chain works. + it("pins the embedded view to its bounded pane height on mobile so the footer scrolls into reach", () => { + const css = loadPlanningCss(); + const mobileCss = getMediaBlocks(css, MOBILE_ACTIONS_QUERY).join("\n"); + + const embeddedRule = findRule(mobileCss, ".planning-view.open .planning-modal--embedded"); + expect(embeddedRule).toBeTruthy(); + expect(embeddedRule).toMatch(/height\s*:\s*100%\s*;/); + expect(embeddedRule).toMatch(/max-height\s*:\s*100%\s*;/); + }); });