From e36aef8aa3c64aa040147c1139be09cb504365da Mon Sep 17 00:00:00 2001 From: Lotus CI Date: Sun, 19 Jul 2026 01:08:43 -0400 Subject: [PATCH] fix(lotus): in-call mobile UI fixes (EC audit wave-2) 3-agent survey of the in-call UI at phone width + 2-agent review of the staged diff. All changes are mobile-gated CSS (mobile-first @media on Compound tokens), so desktop rendering is provably unchanged. - CallFooter: wrap the control row and tighten gap/padding at <=500px so the buttons (incl. the destructive hangup) can never be clipped by the grid's overflow-x:hidden. A loudspeaker button was added to the bar without a shed rule, pushing 6-7 lg buttons past the viewport at 320-500px. - OneOnOnePortraitLayout: give the 1:1 self-view the same safe-area-aware inset as SpotlightExpandedLayout so it clears the home indicator / floating footer (was a bare 16px inset). Phone-only layout. - GridTile: enlarge the always-visible camera-flip control to a 44px touch target on coarse pointers (was ~28px). - ReactionToggleButton: enlarge reaction-picker emoji buttons to 44px at <=420px (were ~32px); five still fit a 360px drawer row. - Tabs: scroll the horizontal tab row on the inline axis (overflow-x) so the Settings tabs don't clip in a phone drawer. - SpotlightLandscapeLayout: shrink the 180px filmstrip rail to 132px on short landscape phones (max-height:400px) so the spotlight isn't a sliver. Gates: tsc 0, prettier clean, affected vitest pass. No TS changed. Co-Authored-By: Claude Opus 4.8 --- src/button/ReactionToggleButton.module.css | 6 ++++-- src/components/CallFooter.module.css | 14 ++++++++++++++ src/grid/OneOnOnePortraitLayout.module.css | 9 ++++++++- src/grid/SpotlightLandscapeLayout.module.css | 14 ++++++++++++++ src/tabs/Tabs.module.css | 8 +++++++- src/tile/GridTile.module.css | 8 ++++++++ 6 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/button/ReactionToggleButton.module.css b/src/button/ReactionToggleButton.module.css index 90c6af02..96e064c6 100644 --- a/src/button/ReactionToggleButton.module.css +++ b/src/button/ReactionToggleButton.module.css @@ -12,8 +12,10 @@ @media (max-width: 420px) { .reactionPopupMenu { - --reaction-button-padding: 8px; - --reaction-button-fontsize: 16px; + /* 20px glyph + 2×12px padding = a 44px square touch target. Five buttons + still fit a ~360px drawer row; previously these were ~32px. */ + --reaction-button-padding: 12px; + --reaction-button-fontsize: 20px; --reaction-button-gap: 6px; } } diff --git a/src/components/CallFooter.module.css b/src/components/CallFooter.module.css index adff99d5..c25b21ef 100644 --- a/src/components/CallFooter.module.css +++ b/src/components/CallFooter.module.css @@ -102,6 +102,20 @@ Once we exceed 500 we hide everything except the buttons. @media (max-width: 500px) { .footer { grid-template-areas: "buttons buttons buttons"; + /* Reclaim horizontal budget so the control row fits on phones. */ + padding-left: calc(env(safe-area-inset-left) + var(--cpd-space-3x)); + padding-right: calc(env(safe-area-inset-right) + var(--cpd-space-3x)); + } + + .buttons { + /* Fill the row and wrap instead of overflowing: on the narrowest phones the + control set (mic/video/loudspeaker/hangup) exceeds the viewport, and the + parent's overflow-x: hidden would otherwise clip the outer buttons — + including the destructive hangup. Wrapping keeps every control reachable. */ + justify-self: stretch; + flex-wrap: wrap; + justify-content: center; + gap: var(--cpd-space-2x); } .settingsOnlyShowNarrow { diff --git a/src/grid/OneOnOnePortraitLayout.module.css b/src/grid/OneOnOnePortraitLayout.module.css index 999f504d..cfe355c3 100644 --- a/src/grid/OneOnOnePortraitLayout.module.css +++ b/src/grid/OneOnOnePortraitLayout.module.css @@ -16,7 +16,14 @@ Please see LICENSE in the repository root for full details. .pip { position: absolute; - inset: var(--cpd-space-4x); + /* Keep the self-view within the safe area (home indicator) and the inline + content insets, and clear of the floating footer — matching the + reference-correct SpotlightExpandedLayout. A bare 16px inset let the + bottom-right self-tile sit under the controls / device chrome on phones. */ + inset: calc(env(safe-area-inset-top) + var(--cpd-space-4x)) + var(--content-inset-right) + calc(env(safe-area-inset-bottom) + var(--cpd-space-4x)) + var(--content-inset-left); } .pip[data-size="sm"] { diff --git a/src/grid/SpotlightLandscapeLayout.module.css b/src/grid/SpotlightLandscapeLayout.module.css index ddc4a395..c2375242 100644 --- a/src/grid/SpotlightLandscapeLayout.module.css +++ b/src/grid/SpotlightLandscapeLayout.module.css @@ -43,3 +43,17 @@ unconditionally select the container so we can use cq units */ inline-size: 180px; block-size: 135px; } + +/* On a landscape phone the fixed 180px filmstrip rail squeezes the spotlight to + a sliver; shrink the rail (keeping the 4:3 tile ratio) to give the spotlight + room. Desktop landscape (height > 400px) is unaffected. */ +@media (max-height: 400px) { + .layer { + --grid-slot-width: 132px; + } + + .grid > .slot { + inline-size: 132px; + block-size: 99px; + } +} diff --git a/src/tabs/Tabs.module.css b/src/tabs/Tabs.module.css index 1894dd98..08e65c31 100644 --- a/src/tabs/Tabs.module.css +++ b/src/tabs/Tabs.module.css @@ -12,7 +12,12 @@ Please see LICENSE in the repository root for full details. } .tabList { - overflow-y: auto; + /* The tab row is horizontal, so scroll along the inline axis when the tabs + exceed the width (e.g. the Settings modal as a phone drawer). The prior + overflow-y:auto expressed the wrong axis; make the horizontal intent + explicit and hide the scrollbar. */ + overflow-x: auto; + overflow-y: hidden; max-width: 100%; /*no scrollbars*/ @@ -24,4 +29,5 @@ Please see LICENSE in the repository root for full details. /*no scrollbars*/ background: transparent; /* Chrome/Safari/Webkit */ width: 0px; + height: 0px; } diff --git a/src/tile/GridTile.module.css b/src/tile/GridTile.module.css index 7ffe67d4..45539fa7 100644 --- a/src/tile/GridTile.module.css +++ b/src/tile/GridTile.module.css @@ -98,6 +98,14 @@ borders don't support gradients */ color: var(--cpd-color-icon-primary); } +/* The camera-flip control is always visible and primarily used on phones; + its inherited 4px padding yields a ~28px box, below the touch minimum. */ +@media (hover: none), (pointer: coarse) { + .tile .switchCamera { + padding: var(--cpd-space-3x); + } +} + @media (hover) { .tile .switchCamera:hover { background: var(--cpd-color-bg-subtle-secondary);