@@ -124,6 +124,10 @@ const FOCUS_LOFI_OPTIONS: readonly {
124124const FOCUS_MODE_TRANSITION_MS = 1100 ;
125125const FOCUS_MODE_TRANSITION_MS_REDUCED = 280 ;
126126
127+ /** Same expression as `focus-mode-mobile.css` — narrow portrait or phone landscape */
128+ const PORTABLE_UI_MEDIA_QUERY =
129+ '(max-width: 640px), (orientation: landscape) and (max-height: 520px)' ;
130+
127131@Component ( {
128132 selector : 'app-root' ,
129133 imports : [ FocusPomodoroComponent , GlobeViewComponent , StreamSidebarComponent ] ,
@@ -135,6 +139,25 @@ export class AppComponent implements OnInit, OnDestroy {
135139
136140 private focusTransitionClearId : ReturnType < typeof setTimeout > | null = null ;
137141
142+ private portableUiMql : MediaQueryList | null = null ;
143+
144+ private readonly onPortableUiChange = ( ) : void => {
145+ if ( ! this . portableUiMql ) {
146+ return ;
147+ }
148+ this . portableUiActive = this . portableUiMql . matches ;
149+ this . cdr . markForCheck ( ) ;
150+ } ;
151+
152+ /**
153+ * True when compact portable layout applies (matches CSS portable breakpoint).
154+ * Used to hide the Focus entry control while the stream sidebar is fullscreen.
155+ */
156+ portableUiActive =
157+ typeof globalThis !== 'undefined' &&
158+ typeof matchMedia !== 'undefined' &&
159+ matchMedia ( PORTABLE_UI_MEDIA_QUERY ) . matches ;
160+
138161 /**
139162 * First focus embed after a cold load with `?focus=` in the URL — use muted autoplay so
140163 * playback can start without a user gesture; cleared after the first successful embed URL.
@@ -175,6 +198,8 @@ export class AppComponent implements OnInit, OnDestroy {
175198 clearTimeout ( this . focusTransitionClearId ) ;
176199 this . focusTransitionClearId = null ;
177200 }
201+ this . portableUiMql ?. removeEventListener ( 'change' , this . onPortableUiChange ) ;
202+ this . portableUiMql = null ;
178203 }
179204
180205 /** Full-screen veil while toggling Focus mode (hides WebGL/layout churn). */
@@ -213,6 +238,11 @@ export class AppComponent implements OnInit, OnDestroy {
213238 ) ;
214239 }
215240
241+ /** Hide on portable while sidebar is open — panel covers the canvas */
242+ get focusModeLaunchVisible ( ) : boolean {
243+ return ! this . focusMode && ( ! this . sidebarOpen || ! this . portableUiActive ) ;
244+ }
245+
216246 /** X vs magnifier: updates on close *start*, not when the panel unmounts. */
217247 get searchLaunchShowsClose ( ) : boolean {
218248 return this . sidebarOpen && ! this . panelCloseAnimationStarted ;
@@ -307,6 +337,11 @@ export class AppComponent implements OnInit, OnDestroy {
307337
308338 ngOnInit ( ) : void {
309339 if ( typeof window === 'undefined' ) return ;
340+ if ( typeof matchMedia !== 'undefined' ) {
341+ this . portableUiMql = matchMedia ( PORTABLE_UI_MEDIA_QUERY ) ;
342+ this . portableUiActive = this . portableUiMql . matches ;
343+ this . portableUiMql . addEventListener ( 'change' , this . onPortableUiChange ) ;
344+ }
310345 const u = new URL ( window . location . href ) ;
311346 const rawStream = u . searchParams . get ( 'stream' ) ;
312347 const rawRadio = u . searchParams . get ( 'radio' ) ;
0 commit comments