# desktop-mode/1.1.10/assets/css/mobile.css

OpenStation: Desktop Windows, Dock &amp; Virtual Desktops for WP Admin, version 1.1.10. 1,077 lines.

- Page: https://pluginprobe.com/plugins/desktop-mode/1.1.10/code/assets/css/mobile.css
- Raw: https://pluginprobe.com/plugins/desktop-mode/1.1.10/raw/assets/css/mobile.css
- Modified: 2026-09-04T15:30:56+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/desktop-mode/1.1.10/code/assets/css/mobile.css#L10-L20`.

```css
/**
 * OpenStation — the phone layer.
 *
 * Every rule is scoped to `html[data-os-mode="mobile"]`, the
 * attribute the PHP head stamp writes before the body parses and the
 * mode controller keeps current. On a desktop this sheet matches
 * nothing; on a phone the first paint is already the phone layer.
 *
 * Three jobs:
 *
 *   1. Retire the desktop chrome — dock, widgets, wallpaper icons,
 *      admin bar, notch, window title bars and resize handles. The
 *      elements stay in the DOM (their listeners and registries keep
 *      working); they just stop painting.
 *   2. Make every window the whole work area, between the top bar
 *      and the tab bar, under the safe areas.
 *   3. Paint the four phone surfaces: top bar, home grid, tab bar,
 *      app switcher.
 *
 * Every colour resolves through a palette token with the pre-brand
 * literal as its floor (see AGENTS.md, "The palette lives in
 * variables.css"). Motion is opt-out under `prefers-reduced-motion`.
 */

/* ------------------------------------------------------------------
 * 1. The desktop chrome steps aside.
 * ---------------------------------------------------------------- */

html[data-os-mode="mobile"] #wpadminbar,
html[data-os-mode="mobile"] #os-dock,
html[data-os-mode="mobile"] #os-side-dock,
html[data-os-mode="mobile"] #os-widgets,
html[data-os-mode="mobile"] .os-icons,
html[data-os-mode="mobile"] .os-files-layer,
html[data-os-mode="mobile"] .os-notes,
html[data-os-mode="mobile"] .os-mio,
html[data-os-mode="mobile"] #os-mio,
html[data-os-mode="mobile"] .os-notch,
html[data-os-mode="mobile"] .os-window__titlebar,
html[data-os-mode="mobile"] .os-window__resize-handle {
	display: none !important;
}

/* The 32px WordPress reserves for the admin bar. */
html[data-os-mode="mobile"].wp-toolbar {
	padding-top: 0 !important;
}

/*
 * The shell starts at the top edge, and the admin-bar height token
 * says so too: anything that still reads it (the notch's offset, a
 * plugin's own chrome) measures against a bar that is not there.
 */
html[data-os-mode="mobile"] .os-shell {
	inset-block-start: 0;
	--wp-admin--admin-bar--height: 0px;
}

/*
 * No page zoom. `pan-x pan-y` keeps every scroll and hands nothing
 * else to the browser: no pinch, no double-tap. The viewport meta
 * says the same (`maximum-scale=1`, `openstation_mode_viewport_meta()`)
 * and `src/mode/zoom-guard.ts` cancels the gesture where a browser
 * ignores both. `overscroll-behavior: none` is the rubber band at the
 * document's edges — the shell is fixed and has no document to pull.
 */
html[data-os-mode="mobile"],
html[data-os-mode="mobile"] body {
	touch-action: pan-x pan-y;
	overscroll-behavior: none;
}

/* The work area is the whole area: no dock band to keep clear of. */
html[data-os-mode="mobile"] .os-area {
	padding: 0 !important;
}

/* ------------------------------------------------------------------
 * Tokens for this layer alone. Private aliases read the palette so a
 * desktop theme that repaints the surfaces repaints the phone too.
 * ---------------------------------------------------------------- */

/*
 * The phone chrome is the DOCK's family, not the window's. The window
 * tokens describe a document surface (white body, dark text under most
 * admin colour schemes) and they are wrong for anything that sits on
 * the wallpaper: a white tile grid with grey labels on a dark desk.
 * The dock tokens describe exactly this situation — translucent dark
 * glass over the wallpaper, light glyphs — so every phone surface
 * reads them, and a desktop theme that reskins the dock reskins the
 * phone with it. Bars and sheets sit on the solid backstop, the same
 * floor the shell paints under everything. What the dock has no word
 * for — the glass a tile is cut from, the shadows, the scrims — is
 * the `--os-mobile-*` family in `variables.css`.
 */
html[data-os-mode="mobile"] .os-shell {
	--_m-surface: var( --os-backstop, #0c0b0f );
	--_m-surface-raised: var( --os-dock-bg, rgba( 12, 11, 15, 0.72 ) );
	--_m-glass: var( --os-mobile-glass, rgba( 255, 255, 255, 0.1 ) );
	--_m-glass-strong: var( --os-mobile-glass-strong, rgba( 255, 255, 255, 0.16 ) );
	--_m-glass-border: var( --os-mobile-glass-border, rgba( 255, 255, 255, 0.14 ) );
	--_m-fg: var( --os-dock-icon-color-hover, #fffbff );
	--_m-fg-muted: var( --os-dock-icon-color, rgba( 255, 251, 255, 0.72 ) );
	--_m-border: var( --os-dock-border, rgba( 255, 251, 255, 0.1 ) );
	--_m-accent: var( --os-ui-accent, #f252fc );
	--_m-accent-dim: var( --os-ui-accent-dim, rgba( 242, 82, 252, 0.35 ) );
	--_m-danger: var( --os-ui-danger, #d63638 );
	--_m-tile-shadow: var( --os-mobile-tile-shadow, 0 8px 20px rgba( 0, 0, 0, 0.35 ) );
	--_m-card-shadow: var( --os-mobile-card-shadow, 0 -8px 24px rgba( 0, 0, 0, 0.4 ) );
	--_m-sheet-shadow: var( --os-mobile-sheet-shadow, 0 -12px 40px rgba( 0, 0, 0, 0.45 ) );
	--_m-window-shadow: var( --os-mobile-window-shadow, 0 12px 32px rgba( 0, 0, 0, 0.45 ) );
	--_m-scrim: var( --os-mobile-scrim, rgba( 0, 0, 0, 0.55 ) );
	--_m-top-scrim: var( --os-mobile-top-scrim, rgba( 0, 0, 0, 0.45 ) );
	--_m-label-shadow: var( --os-mobile-label-shadow, 0 1px 2px rgba( 0, 0, 0, 0.6 ) );
	--_m-radius: 14px;
	--_m-gap: 8px;
	--_m-top-h: 48px;
	--_m-tabs-h: 56px;
	--_m-safe-top: env( safe-area-inset-top, 0px );
	--_m-safe-bottom: env( safe-area-inset-bottom, 0px );
	--_m-safe-left: env( safe-area-inset-left, 0px );
	--_m-safe-right: env( safe-area-inset-right, 0px );
	/* The tab bar's whole footprint: its items, and the home indicator under them. */
	--_m-tabs-total: calc( var( --_m-tabs-h ) + 8px + var( --_m-safe-bottom ) );
	--_m-ease: cubic-bezier( 0.2, 0.8, 0.2, 1 );
	/*
	 * Every kit field on the phone is 16px: below that, iOS zooms the
	 * page into a focused control and never zooms back out.
	 */
	--os-ui-field-font-size: 16px;
	--os-ui-field-font-size-compact: 16px;
}

/*
 * The phone surfaces are DARK surfaces, and the kit components they
 * host (`<os-text-field>` in the home search, `<os-button>` in the
 * switcher footer) read the public `--os-ui-*` tokens, which under a
 * light admin scheme describe the window's white body and near-black
 * text. Re-pointing those public names on the surface itself is the
 * sanctioned move (a document-tree rule the component's `var()`
 * resolves through — see AGENTS.md, "Never declare a themeable token
 * on a component's :host"): every kit control inside reads the phone
 * palette, and a theme that recolours the dock recolours them too.
 * `--_m-*` are the private aliases declared on `.os-shell` above.
 */
html[data-os-mode="mobile"] .os-mobile-home,
html[data-os-mode="mobile"] .os-mobile-top,
html[data-os-mode="mobile"] .os-mobile-tabs,
html[data-os-mode="mobile"] .os-mobile-switcher__sheet {
	--os-ui-fg: var( --_m-fg );
	--os-ui-fg-muted: var( --_m-fg-muted );
	--os-ui-border: var( --_m-border );
	--os-ui-surface: var( --_m-surface );
	--os-window-bg: var( --_m-surface );
	--os-ui-hover: var( --_m-glass );
	--os-ui-button-fg: var( --_m-fg );
	--os-ui-button-bg: var( --_m-glass );
	--os-ui-button-bg-hover: var( --_m-glass-strong );
	--os-ui-button-border: 1px solid var( --_m-border );
}

/* ------------------------------------------------------------------
 * 2. Windows: one at a time, a card on the desk.
 *
 * The window is inset from the edges by `--_m-gap`, rounded and
 * shadowed, so the desk shows around it and the app reads as a card
 * sitting on something (the switcher draws it the same way). `inset`
 * with `!important` beats the inline left/top/width/height that
 * `maximize()` writes; the inline values stay what they were, which
 * is what the session hands back to the desktop.
 * ---------------------------------------------------------------- */

html[data-os-mode="mobile"] .os-window {
	border-radius: var( --_m-radius ) !important;
	border: 1px solid var( --_m-border ) !important;
	box-shadow: var( --_m-window-shadow ) !important;
	overflow: hidden;
}

html[data-os-mode="mobile"] .os-window--maximized {
	inset: var( --_m-gap ) calc( var( --_m-gap ) + var( --_m-safe-right ) ) var( --_m-gap )
		calc( var( --_m-gap ) + var( --_m-safe-left ) ) !important;
	width: auto !important;
	height: auto !important;
}

/*
 * Only the topmost window paints. The rest are still open (their
 * iframes keep their state) but skip layout and paint entirely, so a
 * phone with six apps open composites one.
 */
html[data-os-mode="mobile"] .os-window:not( .os-window--focused ) {
	content-visibility: hidden;
}

/*
 * The tab strip (All Posts / Add New / Categories) stays: on a phone
 * it is the in-app navigation. It scrolls sideways instead of
 * wrapping.
 */
html[data-os-mode="mobile"] .os-window__tabs {
	overflow-x: auto;
	overflow-y: hidden;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
	padding-inline: var( --_m-safe-left ) var( --_m-safe-right );
}

html[data-os-mode="mobile"] .os-window__tabs::-webkit-scrollbar {
	display: none;
}

/* A new app slides up from the tab bar. */
@keyframes os-mobile-app-in {
	from {
		opacity: 0;
		transform: translateY( 24px ) scale( 0.98 );
	}
	to {
		opacity: 1;
		transform: none;
	}
}

html[data-os-mode="mobile"] .os-window.os-mobile-enter {
	animation: os-mobile-app-in 220ms var( --_m-ease ) both;
}

/* On the home screen nothing but the wallpaper and the grid paint. */
html[data-os-mode="mobile"] .os-shell[data-os-mobile-state="home"] .os-window {
	visibility: hidden;
}

/* ------------------------------------------------------------------
 * The edge zones: thin strips the shell owns over both edges of the
 * area, so a pointer that starts there never belongs to the iframe.
 * The start edge is the Back gesture; both cancel the browser's
 * history swipe (`bindHistorySwipeGuard` in `gestures.ts`), so they
 * stay up on the home screen too — a swipe that navigated away would
 * navigate away from home as well. Above the windows and the home
 * grid, below the switcher sheet.
 * ---------------------------------------------------------------- */

html[data-os-mode="mobile"] .os-mobile-edge {
	position: absolute;
	inset-block: 0;
	width: 22px;
	z-index: 2000;
	touch-action: none;
	background: transparent;
}

html[data-os-mode="mobile"] .os-mobile-edge--start {
	inset-inline-start: 0;
}

html[data-os-mode="mobile"] .os-mobile-edge--end {
	inset-inline-end: 0;
}

/* ------------------------------------------------------------------
 * 3a. The top bar.
 * ---------------------------------------------------------------- */

/*
 * The bar sits on the desk, not on the window: no fill and no rule
 * under it, only a scrim so the title reads over any wallpaper, and
 * the window card starts `--_m-gap` below it. Icon and name above a
 * card is the picture the switcher draws too.
 */
html[data-os-mode="mobile"] .os-mobile-top {
	display: flex;
	align-items: center;
	gap: 6px;
	flex: 0 0 auto;
	min-height: var( --_m-top-h );
	padding-block-start: var( --_m-safe-top );
	padding-inline: calc( 4px + var( --_m-safe-left ) ) calc( 4px + var( --_m-safe-right ) );
	background: linear-gradient( to bottom, var( --_m-top-scrim ), transparent );
	color: var( --_m-fg );
	position: relative;
	z-index: 3000;
}

html[data-os-mode="mobile"] .os-mobile-top[hidden] {
	display: none;
}

/* Icon + title, the app's identity. Follows an in-flight back gesture. */
html[data-os-mode="mobile"] .os-mobile-top__identity {
	flex: 1 1 auto;
	min-width: 0;
	display: flex;
	align-items: center;
	gap: 10px;
	padding-inline-start: 10px;
	transition: transform 80ms linear;
}

html[data-os-mode="mobile"] .os-mobile-top--peeking .os-mobile-top__identity {
	transform: translateX( calc( var( --os-mobile-back-progress, 0 ) * 14px ) );
}

/*
 * One control, a round glass ×: the app closes and morphs back into
 * its tile. The bar also answers a flick down by sending the app
 * home, window kept.
 */
html[data-os-mode="mobile"] .os-mobile-top__controls {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	margin-inline-end: 6px;
	flex: 0 0 auto;
}

html[data-os-mode="mobile"] .os-mobile-top__button {
	appearance: none;
	border: 1px solid var( --_m-border );
	background: var( --_m-glass );
	color: inherit;
	width: 34px;
	height: 34px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: 999px;
	flex: 0 0 auto;
	-webkit-tap-highlight-color: transparent;
}

html[data-os-mode="mobile"] .os-mobile-top__button:active {
	background: var( --_m-glass-strong );
}

html[data-os-mode="mobile"] .os-mobile-top__button:focus-visible {
	outline: 2px solid var( --_m-accent );
	outline-offset: 2px;
}

html[data-os-mode="mobile"] .os-mobile-top__icon {
	display: inline-flex;
	width: 24px;
	height: 24px;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	color: var( --_m-fg-muted );
}

html[data-os-mode="mobile"] .os-mobile-top__icon > * {
	width: 22px;
	height: 22px;
	font-size: 22px;
}

html[data-os-mode="mobile"] .os-mobile-top__title {
	flex: 1 1 auto;
	min-width: 0;
	margin: 0;
	font-size: 17px;
	font-weight: 600;
	line-height: 1.2;
	color: inherit;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	text-shadow: var( --_m-label-shadow );
}

/* ------------------------------------------------------------------
 * 3b. Home.
 * ---------------------------------------------------------------- */

html[data-os-mode="mobile"] .os-mobile-home {
	position: absolute;
	inset: 0;
	z-index: 1500;
	display: flex;
	flex-direction: column;
	color: var( --_m-fg );
	padding-block-start: calc( 8px + var( --_m-safe-top ) );
	padding-inline: calc( 12px + var( --_m-safe-left ) ) calc( 12px + var( --_m-safe-right ) );
	animation: os-mobile-fade-in 160ms var( --_m-ease ) both;
}

html[data-os-mode="mobile"] .os-mobile-home[hidden] {
	display: none;
}

@keyframes os-mobile-fade-in {
	from {
		opacity: 0;
	}
	to {
		opacity: 1;
	}
}

/*
 * The search is a phone's search: a rounded glass field, 16px type
 * (the size under which iOS zooms the page into it), no hairline
 * box. The kit field reads the public tokens re-pointed here.
 */
html[data-os-mode="mobile"] .os-mobile-home__search {
	flex: 0 0 auto;
	padding-block: 4px 8px;
	--os-window-bg: var( --_m-glass );
	--os-ui-border: var( --_m-glass-border );
	--os-ui-field-radius: 12px;
}

html[data-os-mode="mobile"] .os-mobile-home__search os-text-field {
	backdrop-filter: blur( 14px ) saturate( 140% );
	-webkit-backdrop-filter: blur( 14px ) saturate( 140% );
	border-radius: 12px;
}

html[data-os-mode="mobile"] .os-mobile-home__scroll {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
	overscroll-behavior: contain;
}

/*
 * The grid always scrolls, by at least one pixel. A phone only rubber-
 * bands a surface that CAN scroll, and a home screen that fits — a
 * dozen tiles on a tall phone — was a dead surface under the thumb.
 * The 1px past the frame makes it a scroller, and the platform then
 * gives it the platform's own bounce at both ends, exactly the drag
 * every native home screen has. The bottom padding is here rather than
 * on the scroller so the last row clears the tab bar inside the same
 * measure.
 */
html[data-os-mode="mobile"] .os-mobile-home__content {
	box-sizing: border-box;
	min-block-size: calc( 100% + 1px );
	padding-block-end: 16px;
}

html[data-os-mode="mobile"] .os-mobile-home__heading {
	margin: 12px 4px 6px;
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var( --_m-fg-muted );
	text-shadow: var( --_m-label-shadow );
}

html[data-os-mode="mobile"] .os-mobile-home__empty {
	margin: 24px 8px;
	color: var( --_m-fg-muted );
	text-align: center;
}

html[data-os-mode="mobile"] .os-mobile-grid {
	display: grid;
	grid-template-columns: repeat( auto-fill, minmax( 78px, 1fr ) );
	gap: 10px 6px;
	margin: 0;
	padding: 0;
	list-style: none;
}

html[data-os-mode="mobile"] .os-mobile-tile {
	appearance: none;
	background: transparent;
	border: 0;
	padding: 6px 2px;
	width: 100%;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	color: var( --_m-fg );
	border-radius: 12px;
	-webkit-tap-highlight-color: transparent;
}

html[data-os-mode="mobile"] .os-mobile-tile:active .os-mobile-tile__icon {
	transform: scale( 0.92 );
}

html[data-os-mode="mobile"] .os-mobile-tile:focus-visible {
	outline: 2px solid var( --_m-accent );
	outline-offset: 2px;
}

html[data-os-mode="mobile"] .os-mobile-tile__icon {
	position: relative;
	width: 58px;
	height: 58px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: 16px;
	background: var( --_m-glass );
	border: 1px solid var( --_m-glass-border );
	box-shadow: var( --_m-tile-shadow );
	backdrop-filter: blur( 14px ) saturate( 140% );
	-webkit-backdrop-filter: blur( 14px ) saturate( 140% );
	transition: transform 120ms var( --_m-ease ), background 120ms var( --_m-ease );
	color: var( --_m-fg );
}

html[data-os-mode="mobile"] .os-mobile-tile:active .os-mobile-tile__icon {
	background: var( --_m-glass-strong );
}

html[data-os-mode="mobile"] .os-mobile-tile__glyph {
	width: 30px;
	height: 30px;
	font-size: 30px;
	line-height: 1;
}

html[data-os-mode="mobile"] .os-mobile-tile__glyph.dashicons {
	width: 30px;
	height: 30px;
	font-size: 30px;
}

html[data-os-mode="mobile"] .os-mobile-tile__badge,
html[data-os-mode="mobile"] .os-mobile-tabs__badge {
	position: absolute;
	inset-block-start: -6px;
	inset-inline-end: -6px;
	min-width: 18px;
	height: 18px;
	padding-inline: 5px;
	border-radius: 9px;
	background: var( --_m-danger );
	color: #fff;
	font-size: 11px;
	font-weight: 700;
	line-height: 18px;
	text-align: center;
	box-shadow: 0 0 0 2px var( --_m-surface );
}

/*
 * The switcher's glyph is the number of open apps inside a rounded
 * square, the browser tab-switcher convention, so it reads as "this
 * many open" and not as a notification (and the accent stays the
 * active tab's alone). With nothing open the windows icon shows.
 */
html[data-os-mode="mobile"] .os-mobile-tabs__count {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* Stroke included: the neighbours' glyphs ink about 19px of their 22px box. */
	box-sizing: border-box;
	width: 20px;
	height: 20px;
	border: 2px solid currentColor;
	border-radius: 5px;
	font-size: 11px;
	font-weight: 700;
	line-height: 1;
	letter-spacing: -0.02em;
	color: inherit;
}

html[data-os-mode="mobile"] .os-mobile-tabs__count[hidden],
html[data-os-mode="mobile"] .os-mobile-tabs__icon--counted > :not( .os-mobile-tabs__count ) {
	display: none;
}

html[data-os-mode="mobile"] .os-mobile-tile__label {
	font-size: 11.5px;
	line-height: 1.2;
	color: var( --_m-fg );
	text-align: center;
	max-width: 100%;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	text-shadow: var( --_m-label-shadow );
}

/* ------------------------------------------------------------------
 * 3c. The tab bar.
 *
 * Anchored to the viewport's bottom edge, not laid out as the last
 * child of the shell. The shell is a viewport-sized fixed box, and a
 * box of that shape is exactly what mobile Safari has, more than
 * once, ended short of the screen's bottom edge (an installed app
 * under one iOS release drew a band of nothing under the bar). A
 * bar fixed to the edge is right whatever the shell's box does; the
 * shell's body keeps the bar's footprint clear below it, so the
 * window card and the home grid end where the bar begins.
 * ---------------------------------------------------------------- */

html[data-os-mode="mobile"] .os-mobile-tabs {
	position: fixed;
	inset-inline: 0;
	inset-block-end: 0;
	display: grid;
	grid-auto-flow: column;
	grid-auto-columns: 1fr;
	align-items: stretch;
	min-height: var( --_m-tabs-h );
	padding-block-end: var( --_m-safe-bottom );
	padding-inline: var( --_m-safe-left ) var( --_m-safe-right );
	background: var( --_m-surface );
	border-block-start: 1px solid var( --_m-border );
	z-index: 3000;
	touch-action: pan-x;
}

html[data-os-mode="mobile"] .os-shell__body {
	padding-block-end: var( --_m-tabs-total );
}

html[data-os-mode="mobile"] .os-mobile-tabs__item {
	appearance: none;
	background: transparent;
	border: 0;
	color: var( --_m-fg-muted );
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 2px;
	min-height: var( --_m-tabs-h );
	padding: 4px 2px;
	font-size: 10.5px;
	line-height: 1.1;
	-webkit-tap-highlight-color: transparent;
}

html[data-os-mode="mobile"] .os-mobile-tabs__item[aria-current="page"] {
	color: var( --_m-accent );
}

html[data-os-mode="mobile"] .os-mobile-tabs__item:focus-visible {
	outline: 2px solid var( --_m-accent );
	outline-offset: -2px;
	border-radius: 10px;
}

html[data-os-mode="mobile"] .os-mobile-tabs__icon {
	position: relative;
	display: inline-flex;
	width: 26px;
	height: 26px;
	align-items: center;
	justify-content: center;
}

html[data-os-mode="mobile"] .os-mobile-tabs__glyph {
	width: 22px;
	height: 22px;
	font-size: 22px;
	line-height: 1;
}

html[data-os-mode="mobile"] .os-mobile-tabs__glyph.dashicons {
	width: 22px;
	height: 22px;
	font-size: 22px;
}

html[data-os-mode="mobile"] .os-mobile-tabs__label {
	max-width: 100%;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* ------------------------------------------------------------------
 * 3d. The switcher.
 * ---------------------------------------------------------------- */

html[data-os-mode="mobile"] .os-mobile-switcher {
	position: fixed;
	inset: 0;
	z-index: 4000;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
}

html[data-os-mode="mobile"] .os-mobile-switcher[hidden] {
	display: none;
}

html[data-os-mode="mobile"] .os-mobile-switcher__backdrop {
	position: absolute;
	inset: 0;
	background: var( --_m-scrim );
	animation: os-mobile-fade-in 160ms var( --_m-ease ) both;
}

@keyframes os-mobile-sheet-in {
	from {
		transform: translateY( 24px );
		opacity: 0;
	}
	to {
		transform: none;
		opacity: 1;
	}
}

html[data-os-mode="mobile"] .os-mobile-switcher__sheet {
	position: relative;
	max-height: min( 82vh, 82dvh );
	display: flex;
	flex-direction: column;
	background: var( --_m-surface );
	color: var( --_m-fg );
	border-start-start-radius: var( --_m-radius );
	border-start-end-radius: var( --_m-radius );
	border: 1px solid var( --_m-border );
	border-block-end: 0;
	padding-block-end: var( --_m-safe-bottom );
	box-shadow: var( --_m-sheet-shadow );
	animation: os-mobile-sheet-in 200ms var( --_m-ease ) both;
}

html[data-os-mode="mobile"] .os-mobile-switcher__header {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 10px 8px 6px 16px;
}

/*
 * Explicit colours on the headings: wp-admin's `common.css` paints
 * every h2/h3 in the admin's heading colour, which is near-black under
 * a light scheme and would sink into the sheet.
 */
html[data-os-mode="mobile"] .os-mobile-switcher__title {
	margin: 0;
	font-size: 15px;
	font-weight: 600;
	color: var( --_m-fg );
}

html[data-os-mode="mobile"] .os-mobile-switcher__close {
	appearance: none;
	background: transparent;
	border: 0;
	color: inherit;
	width: 40px;
	height: 40px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: 10px;
}

html[data-os-mode="mobile"] .os-mobile-switcher__close:focus-visible {
	outline: 2px solid var( --_m-accent );
	outline-offset: -2px;
}

/*
 * The list is laid out bottom-up (`column-reverse`) so the deck sits
 * at the bottom with its front card nearest the thumb, and a scroll
 * container in this direction starts scrolled to the front. The DOM
 * order is unchanged.
 */
html[data-os-mode="mobile"] .os-mobile-switcher__list {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
	overscroll-behavior: contain;
	padding: 4px 12px 12px;
	display: flex;
	flex-direction: column-reverse;
	gap: 16px;
}

html[data-os-mode="mobile"] .os-mobile-deck {
	display: flex;
	flex-direction: column;
}

/*
 * The pile. Cards are laid out bottom-up too, so the first card in
 * the DOM (the most recent, the one in front) is the lowest; each
 * card that follows is pulled down under the one before it by
 * `--_m-card-peek` less than its height, so only its title bar shows.
 * `switcher.ts` sets the z-order, front card highest.
 */
html[data-os-mode="mobile"] .os-mobile-deck__cards {
	display: flex;
	flex-direction: column-reverse;
	padding-block-end: 2px;
}

html[data-os-mode="mobile"] .os-mobile-switcher__empty {
	margin: 24px 8px;
	color: var( --_m-fg-muted );
	text-align: center;
}

html[data-os-mode="mobile"] .os-mobile-switcher__footer {
	display: flex;
	justify-content: center;
	padding: 8px 12px 10px;
	border-block-start: 1px solid var( --_m-border );
}

html[data-os-mode="mobile"] .os-mobile-switcher__footer[hidden] {
	display: none;
}

/*
 * A card is a small window: an opaque surface (glass over the solid
 * backstop, so the card behind never shows through), a title bar,
 * and a lighter body. The shadow is cast upward, onto the card
 * behind.
 */
html[data-os-mode="mobile"] .os-mobile-card {
	--_m-card-h: 132px;
	--_m-card-peek: 58px;
	position: relative;
	display: block;
	height: var( --_m-card-h );
	background: linear-gradient( var( --_m-glass ), var( --_m-glass ) ) var( --_m-surface );
	border: 1px solid var( --_m-border );
	border-radius: var( --_m-radius );
	box-shadow: var( --_m-card-shadow );
	touch-action: pan-y;
	will-change: transform;
	transition: transform 200ms var( --_m-ease ), opacity 200ms var( --_m-ease );
}

html[data-os-mode="mobile"] .os-mobile-card + .os-mobile-card {
	margin-block-end: calc( var( --_m-card-peek ) - var( --_m-card-h ) );
}

html[data-os-mode="mobile"] .os-mobile-card--dragging {
	transition: none;
}

html[data-os-mode="mobile"] .os-mobile-card--out {
	transform: translateX( calc( var( --os-mobile-card-dir, 1 ) * 110% ) ) !important;
	opacity: 0 !important;
	pointer-events: none;
}

/* The app on screen: outlined in the accent, labelled, not a target. */
html[data-os-mode="mobile"] .os-mobile-card--active {
	border-color: var( --_m-accent-dim );
	box-shadow: 0 0 0 1px var( --_m-accent-dim ), var( --_m-card-shadow );
}

html[data-os-mode="mobile"] .os-mobile-card__status {
	display: inline-block;
	margin-inline-start: 8px;
	padding: 1px 7px;
	border-radius: 999px;
	font-size: 10px;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	vertical-align: middle;
	color: var( --_m-accent );
	background: var( --_m-glass );
	border: 1px solid var( --_m-accent-dim );
}

html[data-os-mode="mobile"] .os-mobile-card__body {
	appearance: none;
	background: transparent;
	border: 0;
	color: inherit;
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	padding: 0;
	text-align: start;
	border-radius: inherit;
}

/* The title bar: exactly the strip that shows when the card is behind another. */
html[data-os-mode="mobile"] .os-mobile-card__bar {
	flex: 0 0 auto;
	height: var( --_m-card-peek );
	display: flex;
	align-items: center;
	gap: 12px;
	padding-inline: 12px 52px;
	min-width: 0;
}

/* The body: a lighter page surface, its bottom under the card in front. */
html[data-os-mode="mobile"] .os-mobile-card__preview {
	flex: 1 1 auto;
	margin-inline: 10px;
	border-radius: 10px 10px 0 0;
	background: var( --_m-glass-strong );
	border: 1px solid var( --_m-border );
	border-block-end: 0;
}

html[data-os-mode="mobile"] .os-mobile-card__body:focus-visible {
	outline: 2px solid var( --_m-accent );
	outline-offset: -2px;
}

html[data-os-mode="mobile"] .os-mobile-card__icon {
	flex: 0 0 auto;
	width: 36px;
	height: 36px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: 10px;
	background: var( --_m-glass );
	border: 1px solid var( --_m-border );
	color: var( --_m-fg );
}

html[data-os-mode="mobile"] .os-mobile-card__glyph {
	width: 22px;
	height: 22px;
	font-size: 22px;
	line-height: 1;
}

html[data-os-mode="mobile"] .os-mobile-card__text {
	display: flex;
	flex-direction: column;
	min-width: 0;
	gap: 2px;
}

html[data-os-mode="mobile"] .os-mobile-card__title {
	font-size: 15px;
	font-weight: 600;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

html[data-os-mode="mobile"] .os-mobile-card__subtitle {
	font-size: 12px;
	color: var( --_m-fg-muted );
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

html[data-os-mode="mobile"] .os-mobile-card__close {
	appearance: none;
	background: var( --_m-glass );
	border: 1px solid var( --_m-border );
	color: var( --_m-fg-muted );
	position: absolute;
	inset-block-start: 11px;
	inset-inline-end: 10px;
	width: 36px;
	height: 36px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: 999px;
	-webkit-tap-highlight-color: transparent;
}

html[data-os-mode="mobile"] .os-mobile-card__close:focus-visible {
	outline: 2px solid var( --_m-accent );
	outline-offset: -2px;
}

/* ------------------------------------------------------------------
 * View transitions.
 *
 * The layer runs every open, back and switch through
 * `document.startViewTransition()` (`src/mobile/layer.ts`). The thing
 * the user touched and the thing it becomes share one
 * `view-transition-name` (`os-mobile-hero`), so the browser morphs a
 * tile into its full-screen window and the window back into its
 * tile. The top bar and the tab bar carry their own names so they
 * hold still instead of cross-fading with the rest of the root.
 * ---------------------------------------------------------------- */

html[data-os-mode="mobile"] .os-mobile-top {
	view-transition-name: os-mobile-top;
}

html[data-os-mode="mobile"] .os-mobile-tabs {
	view-transition-name: os-mobile-tabs;
}

::view-transition-group( os-mobile-hero ) {
	animation-duration: 300ms;
	animation-timing-function: cubic-bezier( 0.2, 0.8, 0.2, 1 );
}

::view-transition-old( os-mobile-hero ),
::view-transition-new( os-mobile-hero ) {
	/* The two boxes are different shapes; stretch each to the group
	 * box rather than letting the smaller one letterbox. */
	height: 100%;
	width: 100%;
	object-fit: cover;
	mix-blend-mode: normal;
	animation-duration: 300ms;
}

::view-transition-old( root ),
::view-transition-new( root ) {
	animation-duration: 220ms;
}

::view-transition-old( os-mobile-top ),
::view-transition-new( os-mobile-top ),
::view-transition-old( os-mobile-tabs ),
::view-transition-new( os-mobile-tabs ) {
	animation-duration: 160ms;
}

/*
 * The tab bar's membership changing (a pin added, removed or
 * reordered in Preferences → Mobile). Each tab carries a name and
 * the `os-tab` transition class for the duration: a tab that stays
 * glides to its slot, a new one grows in from the bar, a removed one
 * shrinks out. `:only-child` is how a group with no counterpart —
 * an entry or an exit — is told apart from a morph.
 */
@keyframes os-mobile-tab-in {
	from {
		opacity: 0;
		transform: translateY( 10px ) scale( 0.6 );
	}
}

@keyframes os-mobile-tab-out {
	to {
		opacity: 0;
		transform: translateY( 10px ) scale( 0.6 );
	}
}

::view-transition-group( .os-tab ) {
	animation-duration: 280ms;
	animation-timing-function: cubic-bezier( 0.2, 0.8, 0.2, 1 );
}

::view-transition-new( .os-tab ):only-child {
	animation: os-mobile-tab-in 280ms cubic-bezier( 0.2, 0.8, 0.2, 1 ) both;
}

::view-transition-old( .os-tab ):only-child {
	animation: os-mobile-tab-out 200ms cubic-bezier( 0.2, 0.8, 0.2, 1 ) both;
}

/* ------------------------------------------------------------------
 * Motion opt-out.
 * ---------------------------------------------------------------- */

@media ( prefers-reduced-motion: reduce ) {
	html[data-os-mode="mobile"] .os-window.os-mobile-enter,
	html[data-os-mode="mobile"] .os-mobile-home,
	html[data-os-mode="mobile"] .os-mobile-switcher__backdrop,
	html[data-os-mode="mobile"] .os-mobile-switcher__sheet {
		animation: none;
	}

	html[data-os-mode="mobile"] .os-mobile-card,
	html[data-os-mode="mobile"] .os-mobile-tile__icon {
		transition: none;
	}
}

```
