# desktop-mode/1.1.2/assets/css/my-wordpress.css

OpenStation: Desktop Windows, Dock &amp; Virtual Desktops for WP Admin, version 1.1.2. 2,446 lines.

- Page: https://pluginprobe.com/plugins/desktop-mode/1.1.2/code/assets/css/my-wordpress.css
- Raw: https://pluginprobe.com/plugins/desktop-mode/1.1.2/raw/assets/css/my-wordpress.css
- Modified: 2026-08-19T14:51:18+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.2/code/assets/css/my-wordpress.css#L10-L20`.

```css
/**
 * My WordPress — window-local layout.
 *
 * Two-pane file-explorer for browsing WordPress entities. Mirrors the
 * Recycle Bin and Posts window CSS conventions (`os-*`
 * BEM-ish prefix, container-relative geometry, theming via the
 * os-variables CSS custom properties).
 *
 */

.desktop-mode-my-wordpress {
	display: flex;
	flex-direction: column;
	width: 100%;
	height: 100%;
	min-height: 0;
	font-family: var(
		--os-font,
		-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif
	);
	color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
	/* Window background — separate from the wallpaper bg so the
	 * folder window reads as a real OS window even when the desktop
	 * gradient is dark. Themes / plugins can override
	 * `--os-my-wordpress-bg` to repaint just this surface
	 * without touching every other window. */
	background: var(
		--os-my-wordpress-bg,
		var( --os-window-bg, #fff )
	);
	/* Tile color tokens — same recipe as the folder window. Both
	 * chain through the palette so the surface follows the active
	 * desktop theme, with the pre-brand WordPress-admin literal as
	 * the floor if no stylesheet declares one.
	 *
	 * `--os-tile-fg-muted` used to be the bare literal below, which
	 * outranked the palette's own value and painted every tile's
	 * secondary line near-black — invisible on a dark station, while
	 * the label right beside it followed the theme correctly. A
	 * colour declared next to the thing it paints is out of reach of
	 * the palette AND of every desktop theme. */
	--os-tile-fg: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
	--os-tile-fg-muted: var(
		--os-my-wordpress-fg-muted,
		var( --os-ui-fg-muted, rgba( 0, 0, 0, 0.55 ) )
	);
	--os-tile-hover-bg: var(
		--os-hover,
		rgba( 0, 0, 0, 0.06 )
	);
	/* Light-context label rendering — same recipe as the folder
	 * window. Defined as tokens in `variables.css`; the single
	 * `.os-file-tile__label` rule reads them for every
	 * surface. */
	--os-tile-label-shadow: none;
	--os-tile-label-weight: 500;
	--os-tile-label-smoothing: antialiased;
	--os-tile-label-color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
}

/* Pinned-icon visual cue — used by `os-icon--pinned` (the
 * generic flag added in 0.8.0). The icon-rail itself owns the
 * non-draggable invariant; this rule just removes the grab affordance
 * if the framework ever attaches one in a later phase.
 */
.os-icon--pinned {
	cursor: pointer;
}

/* Breadcrumb header chrome — owned by the shared
 * `.os-breadcrumbs` rules in `desktop-files.css`. No
 * per-surface duplication. */

/* ----- Body host ----- */

.os-my-wordpress__body {
	flex: 1 1 auto;
	min-height: 0;
	overflow: hidden;
	position: relative;
	display: flex;
	flex-direction: column;
}

/* List-view toolbar — sits between the breadcrumb header and the
 * split pane in every entity list view (Posts, Pages, Users, Media).
 * Holds the server-side search input today; reserved for future
 * filter / sort controls. Visual treatment mirrors the Content
 * Graph toolbar's search row so the two surfaces read as the same
 * control. */
.os-my-wordpress__list-toolbar {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 8px 12px;
	border-bottom: 1px solid var( --os-ui-border, #dcdcde );
	/* Opaque and above the canvas: the tile canvas scrolls beneath
	 * this row, and a transparent toolbar let tiles read through it. */
	position: relative;
	z-index: 4;
	background: var( --os-ui-bg, #fff );
}

.os-my-wordpress__list-toolbar-search {
	position: relative;
	flex: 1 1 auto;
	min-width: 0;
	max-width: 360px;
}

.os-my-wordpress__list-toolbar-search-input {
	width: 100%;
	padding: 6px 10px;
	border: 1px solid var( --os-ui-border, #d8dde4 );
	border-radius: 6px;
	background: var( --os-ui-surface, #fff );
	color: inherit;
	font-size: 13px;
	box-sizing: border-box;
}

.os-my-wordpress__list-toolbar-search-input:focus {
	outline: none;
	border-color: var( --os-ui-accent, #2c6be5 );
	box-shadow: 0 0 0 3px var( --os-ui-accent-soft, rgba( 44, 107, 229, 0.18 ) );
}

/* When the list-toolbar mounts, the split pane needs to claim the
 * remaining vertical space inside the flex column body — without
 * this rule it would size to its content (which, for an empty list,
 * is zero) and the tile canvas would collapse. */
.os-my-wordpress__body > .os-my-wordpress__split {
	flex: 1 1 auto;
	min-height: 0;
}

/* Search-in-progress dimming. The previous result set stays in place
 * (no skeleton flash, no empty intermediate page) but reads as
 * "stale" until the new page lands and the renderer atomically swaps
 * the tiles. `pointer-events: none` blocks the user from clicking
 * a tile that's about to disappear. */
.os-my-wordpress__tiles--searching,
.os-my-wordpress__media-grid--searching {
	opacity: 0.45;
	pointer-events: none;
	transition: opacity 0.12s ease-out;
}

/* Reuse the file-system folder window's status-bar element
 * (`.os-folder-status-bar`) so segments render with the
 * exact same DOM the file-system uses — but retone it for the
 * white window background. The dark default (white-on-dark) reads
 * as invisible against `--os-window-bg`. */
.desktop-mode-my-wordpress > .os-folder-status-bar {
	border-top: 1px solid var( --os-ui-border, #dcdcde );
	background: transparent;
	color: var( --os-ui-fg-muted, #50575e );
}

.desktop-mode-my-wordpress
	> .os-folder-status-bar
	button.os-folder-status-bar__segment:hover {
	background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) ) );
}

/* ----- Tile canvas (root + entity list) -----
 *
 * Both views use the SAME tile element (`.os-file-tile`)
 * the wallpaper uses, so visuals stay 1:1 with the desktop. The
 * canvas is `position: relative`; tiles are absolute-positioned and
 * draggable — same interaction pattern as the wallpaper, just
 * scoped to the window. Positions persist per-window in
 * `localStorage` (see `createTileLayout` in src/my-wordpress).
 */

.os-my-wordpress__canvas {
	position: relative;
	min-height: 100%;
	box-sizing: border-box;
	/* Note — DO NOT add `overflow: auto` here. Scroll is owned by
	 * the surrounding pane (`__list` for entity views, `__body` for
	 * the root view via `__grid` below). A second scroller here
	 * would steal the wheel from the outer pane and break the
	 * IntersectionObserver-based infinite scroll. */
}

.os-my-wordpress__grid {
	height: 100%;
	overflow-y: scroll;
}

/* Override the wallpaper file-tile palette for in-window context.
 * On the wallpaper the file tile sits on a dark gradient and uses
 * white text + a 12% white hover — both of which would be invisible
 * on a white window background. Inside My WordPress we re-tone the
 * same element to read on light surfaces. The dimensions / tile
 * structure are inherited from `.os-file-tile`.
 */
.desktop-mode-my-wordpress .os-file-tile {
	/* Color + hover come from `--os-tile-*` — see the
	 * scope-level variable overrides on `.desktop-mode-my-wordpress`
	 * above. We only override structural / animation bits here.
	 *
	 * Drop the wallpaper's `transition: transform` — it hints at
	 * GPU compositing in Chrome, and the moment the layer is
	 * composited the label rasterizes against fractional device
	 * pixels (sub-pixel blur on HiDPI). We don't animate transform
	 * here, so the transition is dead code anyway. */
	transition: background-color 0.15s ease;
	transform: none;
}

/* Selected state for every tile in My WordPress is rendered by
 * the canonical `.os-file-tile--selected` rule in
 * `desktop-files.css` — same accent border + tinted background
 * the folder windows + wallpaper tiles use. No section-specific
 * override here — visual parity across the whole shell.
 */

/* Large tiles — sections that opt in with `tileSize: 'large'`.
 *
 * A section whose rows carry a photograph (a shop's products) reads
 * as a catalogue, not a file list: the picture is what's being
 * scanned, and at 48px it's a thumbnail of a thumbnail. Roughly
 * doubling the icon well also gives a corner ribbon room to be a
 * corner flash instead of covering the subject.
 *
 * The cell pitch lives in `TILE_METRICS_LARGE` (src/my-wordpress/
 * index.ts) and the two MUST move together — a wider tile in a cell
 * that didn't grow overlaps its neighbour. */
/*
 * Image-led sections re-point the tile tokens for their whole
 * subtree rather than overriding the tile's width directly. The base
 * `.os-file-tile` rule then sizes itself from them, and so does
 * everything derived from them (the label's max-width, the loading
 * skeletons) — one declaration instead of a chain of overrides that
 * have to be kept in step.
 */
.os-my-wordpress__tiles--large {
	--os-tile-w: var( --os-tile-w-large, 132px );
	--os-tile-h: var( --os-tile-h-large, 160px );
}

.os-my-wordpress__tiles--large .os-file-tile__visual,
.os-my-wordpress__tiles--large .os-file-tile__icon {
	width: 104px;
	height: 104px;
	font-size: 64px;
}

.os-my-wordpress__tiles--large .os-file-tile__preview {
	width: 104px;
	height: 104px;
	border-radius: 8px;
}

/* Three lines of title at this width instead of two — a product name
 * is long and the extra width earns the room. Matches the taller
 * cell in `TILE_METRICS_LARGE`. */
.os-my-wordpress__tiles--large .os-file-tile__label {
	-webkit-line-clamp: 3;
}

/* Featured-image tiles. Entries that have a featured image render it
 * in the icon well instead of the section dashicon — decisive for
 * image-led types like WooCommerce products. The base
 * `.os-file-tile__preview` rule in `desktop-files.css`
 * already sizes and crops it; all that's added here is a hairline
 * border so a light-on-light photo still reads as a distinct object
 * against the wallpaper, plus a neutral well behind it so a
 * transparent PNG or a slow-loading image doesn't flash the
 * wallpaper through. */
.os-my-wordpress__tile--entry .os-file-tile__preview {
	background: var(
		--os-media-tile-bg,
		rgba( 0, 0, 0, 0.05 )
	);
	/* The hairline was a flat 12%-alpha black — right on the pre-brand
	 * white canvas, and nothing at all on a dark one, where a black
	 * line over a near-black surface leaves the photo edgeless. It
	 * follows the border token now, so it inverts with the theme. */
	box-shadow: inset 0 0 0 1px var( --os-ui-border, rgba( 0, 0, 0, 0.12 ) );
}

/* Banded list views. A section can split its tiles into labelled
 * bands (WooCommerce Orders groups by status, needs-attention first)
 * instead of one flat canvas. The root container stops being a
 * positioned canvas and becomes a plain stack; each band owns its
 * own canvas below its heading. */
.os-my-wordpress__tiles--banded {
	position: static;
	min-height: 0;
}

.os-my-wordpress__band + .os-my-wordpress__band {
	margin-block-start: 8px;
}

/* Bands are laid out in order before the first page arrives so none
 * appears mid-scroll, but one that hasn't received a row yet stays
 * out of the way rather than showing a heading over nothing. */
.os-my-wordpress__band--empty {
	display: none;
}

.os-my-wordpress__band-title {
	display: flex;
	align-items: center;
	gap: 8px;
	position: sticky;
	/* Keeps the band label visible while scrolling a long band —
	 * the whole point of banding is knowing what you're looking at. */
	top: 0;
	/* Above the absolutely-positioned tiles of every band, including
	 * the ones that come later in the DOM. */
	z-index: 3;
	margin: 0;
	padding: 6px 12px;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var( --os-ui-fg-muted, #787c82 );
	/* Near-opaque on purpose. Tiles scrolling underneath are behind
	 * the bar, but at 85% they still read as ghostly blurred shapes
	 * through it, which looks like a rendering fault rather than
	 * frosted glass. The blur + saturate keeps the frosted feel while
	 * the alpha stops the content beneath resolving into shapes. */
	background: color-mix(
		in srgb,
		var( --os-ui-bg, #fff ) 94%,
		transparent
	);
	backdrop-filter: blur( 14px ) saturate( 180% );
	border-block-end: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) );
}

/* Browsers without `color-mix()` fall back to the flat surface —
 * still opaque, just without the translucency. */
@supports not ( background: color-mix( in srgb, red 50%, transparent ) ) {
	.os-my-wordpress__band-title {
		background: var( --os-ui-bg, #fff );
	}
}

/* Attention-carrying bands (WooCommerce's "Needs attention",
 * "Problems") tint their bar so the eye lands there first instead of
 * having to read every heading. */
.os-my-wordpress__band-title--warn {
	color: var( --os-ui-warning, #8a6116 );
	background: color-mix(
		in srgb,
		var( --os-ui-warning, #8a6116 ) 12%,
		var( --os-ui-bg, #fff )
	);
	border-block-end-color: color-mix(
		in srgb,
		var( --os-ui-warning, #8a6116 ) 35%,
		transparent
	);
}

.os-my-wordpress__band-title--danger {
	color: var( --os-ui-danger, #b32d2e );
	background: color-mix(
		in srgb,
		var( --os-ui-danger, #b32d2e ) 12%,
		var( --os-ui-bg, #fff )
	);
	border-block-end-color: color-mix(
		in srgb,
		var( --os-ui-danger, #b32d2e ) 35%,
		transparent
	);
}

.os-my-wordpress__band-count {
	padding: 0 6px;
	border-radius: 999px;
	font-size: 11px;
	line-height: 1.7;
	background: color-mix( in srgb, currentColor 16%, transparent );
	color: inherit;
}

/* Lock state — another user is currently editing this post. We dim
 * the tile a touch and overlay a small dashicon-lock badge on the
 * top-right of the icon. The whole tile stays clickable; the lock
 * is just a visual heads-up so the user knows the takeover dialog
 * is coming if they double-click. */
.os-my-wordpress__tile--locked {
	opacity: 0.85;
}

.os-my-wordpress__tile-lock {
	position: absolute;
	top: 4px;
	inset-inline-end: 14px;
	width: 18px;
	height: 18px;
	font-size: 12px;
	line-height: 18px;
	text-align: center;
	border-radius: 50%;
	background: var( --os-danger, var( --os-ui-danger, #b32d2e ) );
	color: var( --os-ui-fg-on-accent, #fff );
	box-shadow:
		0 0 0 1.5px rgba( 0, 0, 0, 0.4 ),
		0 1px 3px rgba( 0, 0, 0, 0.35 );
	pointer-events: none;
}

/* Hover-card lock banner — shown above the excerpt when present. The
 * wash was a fixed 8%-alpha red, which on a dark card was a smear too
 * faint to read as a banner at all; it follows the badge-danger token
 * now, so it lands at the alpha the active theme picked. */
.os-my-wordpress__tooltip-lock {
	display: flex;
	align-items: center;
	gap: 6px;
	margin: 0 0 8px;
	padding: 6px 8px;
	border-radius: 4px;
	background: var(
		--os-my-wordpress-card-lock-bg,
		rgba( 179, 45, 46, 0.08 )
	);
	color: var( --os-danger, var( --os-ui-danger, #b32d2e ) );
	font-size: 12px;
	font-weight: 600;
}

.os-my-wordpress__tooltip-lock .dashicons {
	font-size: 14px;
	width: 14px;
	height: 14px;
}

/* Plain two-line clamp without `-webkit-line-clamp`. The webkit
 * variant promotes the label to its own composited layer, which on
 * HiDPI screens snaps text to fractional device pixels and renders
 * blurry. `max-height: 2 × line-height` + `overflow: hidden` looks
 * the same and stays sharp.
 */
.os-my-wordpress__tile .os-file-tile__label {
	font-size: 12px;
	line-height: 1.3;
	max-height: calc( 1.3em * 2 );
	text-align: center;
	overflow: hidden;
	max-width: 100%;
	word-break: break-word;
}

/* ----- Entity list: two-pane split ----- */

.os-my-wordpress__split {
	display: grid;
	grid-template-columns: minmax( 240px, 60% ) minmax( 0, 1fr );
	height: 100%;
	min-height: 0;
}

.os-my-wordpress__list {
	overflow-y: scroll;
	border-inline-end: 1px solid var( --os-ui-border, #dcdcde );
	min-width: 0;
}

/* Always-visible scrollbar styling for the icon canvases. macOS
 * defaults to overlay scrollbars that hide until a wheel/scroll
 * event — fine for documents but actively confusing on a tile
 * canvas where the user can't tell whether more icons live below
 * or to the right. We force the scrollbar to render at all times
 * via `::-webkit-scrollbar` (works in WebKit / Blink / Edge), and
 * the explicit `scrollbar-width: thin` covers Firefox. */
.os-my-wordpress__list,
.os-my-wordpress__grid {
	scrollbar-width: thin;
	scrollbar-color: rgba( 0, 0, 0, 0.25 ) transparent;
}

.os-my-wordpress__list::-webkit-scrollbar,
.os-my-wordpress__grid::-webkit-scrollbar {
	width: 12px;
	height: 12px;
}

.os-my-wordpress__list::-webkit-scrollbar-thumb,
.os-my-wordpress__grid::-webkit-scrollbar-thumb {
	background: var( --os-ui-scrim, rgba( 0, 0, 0, 0.25 ) );
	border-radius: 6px;
	border: 3px solid transparent;
	background-clip: padding-box;
}

.os-my-wordpress__list::-webkit-scrollbar-thumb:hover,
.os-my-wordpress__grid::-webkit-scrollbar-thumb:hover {
	background: var( --os-ui-scrim, rgba( 0, 0, 0, 0.4 ) );
	background-clip: padding-box;
}

.os-my-wordpress__list::-webkit-scrollbar-track,
.os-my-wordpress__grid::-webkit-scrollbar-track {
	background: transparent;
}

.os-my-wordpress__sentinel {
	height: 1px;
}

/* Tile-grid loading skeleton — placeholder tiles that mimic the
 * icon + label shape of what's about to load. We dropped the
 * spinning WordPress mark here because it read as an alien
 * affordance inside the file-explorer surface; a skeleton signals
 * "tiles are landing in these slots" instead of "the system is
 * busy doing something opaque."
 *
 * Each placeholder is parented directly to the positioned canvas
 * (`__canvas--positioned`) and `position: absolute` at the
 * (left, top) the next real tile will occupy — see
 * `layout.peekNextCells()` and `showLoadingSkeleton()` in
 * `src/my-wordpress/index.ts`. Per-tile shimmer delay and label
 * width are inlined so a row of placeholders reads as a cascade,
 * not a uniform pulse.
 *
 * Tile dimensions mirror the real `.os-file-tile` width
 * so a placeholder sits cleanly in the cell its successor will
 * inherit.
 */
.os-my-wordpress__skeleton-tile {
	position: absolute;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	/* Same box as the real tile it stands in for — a skeleton that
	 * doesn't match the grid makes the list jump when rows land. */
	width: var( --os-tile-w, 88px );
	height: var( --os-tile-h, 104px );
	padding: 8px 4px;
	box-sizing: border-box;
	pointer-events: none;
}

.os-my-wordpress__skeleton-icon,
.os-my-wordpress__skeleton-label {
	background: linear-gradient(
		90deg,
		var( --os-skeleton-low, var( --os-ui-hover, rgba( 0, 0, 0, 0.05 ) ) ) 0%,
		var( --os-skeleton-high, var( --os-ui-hover, rgba( 0, 0, 0, 0.13 ) ) ) 50%,
		var( --os-skeleton-low, var( --os-ui-hover, rgba( 0, 0, 0, 0.05 ) ) ) 100%
	);
	background-size: 200% 100%;
	animation: os-my-wordpress-skeleton-shimmer 1.4s ease-in-out
		var( --os-skeleton-delay, 0s ) infinite;
}

.os-my-wordpress__skeleton-icon {
	width: 44px;
	height: 44px;
	border-radius: 10px;
}

.os-my-wordpress__skeleton-label {
	width: 72%;
	height: 10px;
	border-radius: 4px;
}

@keyframes os-my-wordpress-skeleton-shimmer {
	from {
		background-position: 200% 0;
	}
	to {
		background-position: -200% 0;
	}
}

@media ( prefers-reduced-motion: reduce ) {
	.os-my-wordpress__skeleton-icon,
	.os-my-wordpress__skeleton-label {
		animation: none;
	}
}

/* ----- Right pane: preview ----- */

.os-my-wordpress__preview {
	overflow: auto;
	min-width: 0;
	background: transparent;
}

.os-my-wordpress__preview-empty {
	display: flex;
	flex-direction: column;
	gap: 6px;
	align-items: center;
	justify-content: center;
	height: 100%;
	color: var( --os-ui-fg-muted, #787c82 );
	font-size: 13px;
	padding: 24px;
	text-align: center;
}

/* Type / status breakdown under a multi-selection's count. */
.os-my-wordpress__preview-selection-breakdown {
	font-size: 12px;
	opacity: 0.75;
}

/* One labelled control in the bulk-edit modal. Label above field,
 * because the fields are wide (a category tree, a tag row) and a
 * two-column grid would put a one-word label beside a control four
 * times its height. */
.os-my-wordpress__bulk-row {
	display: flex;
	flex-direction: column;
	gap: 6px;
	margin-block-end: 16px;
}

.os-my-wordpress__bulk-label {
	font-size: 12px;
	font-weight: 600;
	color: var( --os-ui-fg-muted, #787c82 );
}

.os-my-wordpress__preview-loading {
	display: flex;
	align-items: center;
	justify-content: center;
	height: 100%;
	min-height: 320px;
	padding: 32px;
	box-sizing: border-box;
}

/* Preview-pane loader — same scale curve as the tile-grid first
 * spinner and the window-loading overlay. The JS used to hard-code
 * `size="128"`, which set an inline `--os-ui-spinner-size` that
 * overrode any CSS knob; the size attribute is now omitted so this
 * rule is the single source of truth for both the My WordPress
 * preview and the wallpaper file-preview pane (which reuses this
 * class deliberately).
 */
.os-my-wordpress__preview-loading os-spinner {
	--os-ui-spinner-size: clamp( 96px, 14vw, 192px );
}

.os-my-wordpress__article {
	padding: 24px 32px;
	max-width: 720px;
	margin: 0 auto;
}

.os-my-wordpress__article-title {
	font-size: 22px;
	margin: 0 0 8px;
	line-height: 1.3;
}

.os-my-wordpress__article-meta {
	margin: 0 0 16px;
	color: var( --os-ui-fg-muted, #50575e );
	font-size: 12px;
}

.os-my-wordpress__article-hero {
	width: 100%;
	height: auto;
	border-radius: 6px;
	margin-bottom: 16px;
	display: block;
}

.os-my-wordpress__article-content {
	font-size: 14px;
	line-height: 1.6;
}

.os-my-wordpress__article-content img {
	max-width: 100%;
	height: auto;
}

.os-my-wordpress__article-footer {
	margin-top: 24px;
	padding-top: 16px;
	border-top: 1px solid var( --os-ui-border, #dcdcde );
	display: flex;
	justify-content: flex-end;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
}

/* ----- Empty / error states ----- */

.os-my-wordpress__empty,
.os-my-wordpress__error {
	padding: 32px 24px;
	text-align: center;
	color: var( --os-ui-fg-muted, #787c82 );
	font-size: 13px;
}

.os-my-wordpress__error {
	color: var( --os-danger, var( --os-ui-danger, #b32d2e ) );
}

/* ----- Hover card (floats over body) -----
 *
 * The card is summoned from a tile and belongs to the window that
 * tile is in, so it follows the active desktop theme rather than the
 * shell's tooltip chip. Every colour chains
 * `--os-my-wordpress-card-*` first — derived in `variables.css` from
 * the window family, so a theme that repaints the window repaints the
 * card with it — then the chip tokens, then the pre-brand literal.
 *
 * It is appended to `document.body`, not to the window, because it is
 * `position: fixed` and a window with `overflow: hidden` would clip
 * it. Theme tokens still reach it: a desktop theme declares on
 * `body.os-desktop-theme-<slug>`, an ancestor of both. What does NOT
 * reach it is anything scoped to `.desktop-mode-my-wordpress`, which
 * is why the card family is declared at palette level. */

.os-my-wordpress__tooltip {
	position: fixed;
	z-index: 100000;
	max-width: 320px;
	background: var(
		--os-my-wordpress-card-bg,
		var(
			--os-tooltip-bg,
			var( --os-surface, var( --os-ui-surface, #fff ) )
		)
	);
	color: var(
		--os-my-wordpress-card-fg,
		var( --os-tooltip-fg, var( --os-fg, #1d2327 ) )
	);
	/* The card lands on the same colour as the window under it — the
	 * border and the shadow are the only reason it reads as a separate
	 * object, so neither is decoration. */
	border: 1px solid var( --os-my-wordpress-card-border, #dcdcde );
	border-radius: 6px;
	padding: 12px;
	box-shadow: var(
		--os-my-wordpress-card-shadow,
		0 6px 24px rgba( 0, 0, 0, 0.18 )
	);
	font-size: 12px;
	pointer-events: none;
}

.os-my-wordpress__tooltip-title {
	font-weight: 600;
	margin-bottom: 6px;
	font-size: 13px;
}

/* Same neutral well the entry and media tiles give their thumbnails:
 * a transparent PNG or a still-loading image would otherwise show the
 * card straight through, and a photo whose edge matches the card
 * would have no boundary. */
.os-my-wordpress__tooltip-thumb {
	display: block;
	max-width: 100%;
	height: auto;
	border-radius: 4px;
	margin: 6px 0;
	background: var( --os-my-wordpress-card-thumb-bg, rgba( 0, 0, 0, 0.05 ) );
	box-shadow: inset 0 0 0 1px var(
		--os-my-wordpress-card-border,
		#dcdcde
	);
}

.os-my-wordpress__tooltip-excerpt {
	margin: 0;
	color: var( --os-my-wordpress-card-fg-muted, #50575e );
	line-height: 1.4;
}

/* ----- Context menu host ----- */

.os-my-wordpress__menu {
	position: fixed;
	z-index: 100001;
}

/* ---------------------------------------------------------------
 * User dossier — right-pane preview when a person is selected.
 * Header (avatar + name + roles + links), bio, stat cards row,
 * 12-month activity sparkline, milestones, recent posts, top
 * categories. Designed to fit comfortably on a 360px-wide pane
 * but to stretch nicely up to ~720px before the line-length stops
 * helping readability.
 * --------------------------------------------------------------- */

.os-my-wordpress__user {
	max-width: 720px;
}

.os-my-wordpress__user-header {
	display: flex;
	gap: 16px;
	align-items: center;
	margin-bottom: 16px;
}

.os-my-wordpress__user-avatar {
	width: 96px;
	height: 96px;
	border-radius: 50%;
	flex-shrink: 0;
	box-shadow: 0 2px 8px rgba( 0, 0, 0, 0.12 );
}

.os-my-wordpress__user-headline {
	flex: 1 1 auto;
	min-width: 0;
}

.os-my-wordpress__user
	.os-my-wordpress__article-title {
	margin: 0 0 4px;
}

.os-my-wordpress__user-roles {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin-bottom: 6px;
}

.os-my-wordpress__user-role {
	display: inline-flex;
	align-items: center;
	padding: 2px 8px;
	border-radius: 10px;
	font-size: 11px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	background: rgba( 34, 113, 177, 0.12 );
	color: var( --wp-admin-theme-color, #2271b1 );
}

.os-my-wordpress__user-links {
	display: flex;
	gap: 12px;
	font-size: 12px;
}

.os-my-wordpress__user-links a {
	color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
	text-decoration: none;
}

.os-my-wordpress__user-links a:hover {
	text-decoration: underline;
}

.os-my-wordpress__user-bio {
	font-size: 14px;
	line-height: 1.5;
	color: var( --os-fg, #1d2327 );
	margin: 0 0 16px;
	padding-left: 12px;
	border-left: 3px solid var( --os-ui-border, #dcdcde );
}

/* ----- Stat cards row ----- */

.os-my-wordpress__user-stats {
	display: grid;
	grid-template-columns: repeat( auto-fit, minmax( 120px, 1fr ) );
	gap: 8px;
	margin-bottom: 20px;
}

.os-my-wordpress__user-stat {
	display: flex;
	flex-direction: column;
	gap: 2px;
	padding: 12px 14px;
	border-radius: 8px;
	background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ) );
	border: 1px solid var( --os-ui-border, #dcdcde );
}

.os-my-wordpress__user-stat-value {
	font-size: 26px;
	font-weight: 700;
	line-height: 1.1;
	color: var( --wp-admin-theme-color, #2271b1 );
	font-variant-numeric: tabular-nums;
}

.os-my-wordpress__user-stat-label {
	font-size: 12px;
	color: var( --os-ui-fg-muted, #50575e );
	text-transform: uppercase;
	letter-spacing: 0.04em;
}

.os-my-wordpress__user-stat-caption {
	font-size: 11px;
	color: var( --os-ui-fg-muted, #787c82 );
}

/* ----- 12-month activity sparkline ----- */

.os-my-wordpress__user-section {
	margin-bottom: 20px;
}

.os-my-wordpress__user-section h3 {
	font-size: 13px;
	font-weight: 600;
	color: var( --os-ui-fg-muted, #50575e );
	text-transform: uppercase;
	letter-spacing: 0.04em;
	margin: 0 0 8px;
}

.os-my-wordpress__user-spark-chart {
	display: grid;
	grid-template-columns: repeat( 12, 1fr );
	gap: 4px;
	height: 80px;
	align-items: end;
	padding: 6px 0;
}

.os-my-wordpress__user-spark-col {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	justify-content: flex-end;
	gap: 4px;
	height: 100%;
	min-height: 0;
}

.os-my-wordpress__user-spark-bar {
	background: linear-gradient(
		180deg,
		var( --wp-admin-theme-color, #2271b1 ) 0%,
		rgba( 34, 113, 177, 0.55 ) 100%
	);
	border-radius: 3px 3px 0 0;
	min-height: 2px;
	transition: height 0.3s ease;
}

.os-my-wordpress__user-spark-bar--empty {
	background: var( --os-ui-border, #dcdcde );
	min-height: 2px;
	height: 2px !important;
}

.os-my-wordpress__user-spark-label {
	font-size: 10px;
	text-align: center;
	color: var( --os-ui-fg-muted, #787c82 );
}

/* ----- Milestones ----- */

.os-my-wordpress__user-milestones {
	display: grid;
	grid-template-columns: repeat( auto-fit, minmax( 140px, 1fr ) );
	gap: 12px;
	margin: 0 0 20px;
}

.os-my-wordpress__user-milestones dt {
	font-size: 11px;
	color: var( --os-ui-fg-muted, #50575e );
	text-transform: uppercase;
	letter-spacing: 0.04em;
	margin-bottom: 2px;
}

.os-my-wordpress__user-milestones dd {
	font-size: 14px;
	font-weight: 600;
	margin: 0;
}

/* ----- Recent posts list ----- */

.os-my-wordpress__user-recent {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 6px;
}

.os-my-wordpress__user-recent li {
	display: flex;
	flex-direction: column;
	gap: 2px;
	padding: 8px 10px;
	border-radius: 6px;
	background: var( --os-ui-hover, rgba( 0, 0, 0, 0.02 ) );
}

.os-my-wordpress__user-recent a {
	font-weight: 600;
	color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
	text-decoration: none;
}

.os-my-wordpress__user-recent a:hover {
	text-decoration: underline;
}

.os-my-wordpress__user-recent-meta {
	font-size: 11px;
	color: var( --os-ui-fg-muted, #787c82 );
}

/* ----- Top categories chips ----- */

.os-my-wordpress__user-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}

.os-my-wordpress__user-chip {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 4px 10px;
	border-radius: 999px;
	font-size: 12px;
	background: var( --os-ui-hover, rgba( 0, 0, 0, 0.05 ) );
	border: 1px solid var( --os-ui-border, #dcdcde );
}

.os-my-wordpress__user-chip--tag {
	background: rgba( 124, 58, 237, 0.08 );
	border-color: rgba( 124, 58, 237, 0.25 );
}

.os-my-wordpress__user-chip-count {
	font-weight: 700;
	color: var( --wp-admin-theme-color, #2271b1 );
	font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------
 * Term dossier — same skeleton as the user dossier (header, stat
 * cards, sparkline, milestones, recent list, chips) plus a few
 * term-specific bits below: a colored taxonomy icon medallion in
 * the header, role badge variants for category vs tag, and a
 * "Top contributors" grid that pairs an author avatar with a
 * post count.
 * --------------------------------------------------------------- */

.os-my-wordpress__term-header {
	display: flex;
	align-items: center;
	gap: 16px;
	margin-bottom: 16px;
}

.os-my-wordpress__term-icon {
	width: 64px;
	height: 64px;
	border-radius: 12px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	color: var( --os-ui-fg-on-accent, #fff );
}

.os-my-wordpress__term-icon--category {
	background: linear-gradient( 135deg, var( --os-ui-accent, #2271b1 ) 0%, var( --os-ui-accent-strong, #135e96 ) 100% );
	box-shadow: 0 4px 12px rgba( 34, 113, 177, 0.32 );
}

.os-my-wordpress__term-icon--tag {
	background: linear-gradient( 135deg, #7c3aed 0%, #5b21b6 100% );
	box-shadow: 0 4px 12px rgba( 124, 58, 237, 0.32 );
}

.os-my-wordpress__user-role--category {
	background: rgba( 34, 113, 177, 0.12 );
	color: var( --os-ui-accent-strong, #135e96 );
}

.os-my-wordpress__user-role--tag {
	background: rgba( 124, 58, 237, 0.12 );
	color: #6d28d9;
}

/* Top contributors row — paired avatar + name + post count. */
.os-my-wordpress__term-authors {
	display: grid;
	grid-template-columns: repeat( auto-fit, minmax( 180px, 1fr ) );
	gap: 8px;
}

.os-my-wordpress__term-author {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 8px 12px;
	border-radius: 8px;
	background: var( --os-ui-hover, rgba( 0, 0, 0, 0.03 ) );
	border: 1px solid var( --os-ui-border, #dcdcde );
}

.os-my-wordpress__term-author-avatar {
	width: 36px;
	height: 36px;
	border-radius: 50%;
	flex-shrink: 0;
}

.os-my-wordpress__term-author-text {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.os-my-wordpress__term-author-name {
	font-weight: 600;
	font-size: 13px;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.os-my-wordpress__term-author-count {
	font-size: 11px;
	color: var( --os-ui-fg-muted, #50575e );
}

/* ---------------------------------------------------------------
 * Comment dossier — header (avatar + name + status / date / count
 * badges + author archive / website / moderate links), optional
 * "in reply to" quote, the comment body, parent-post card,
 * direct-reply thread, and a moderation strip (IP + user agent)
 * for users with moderate_comments.
 * --------------------------------------------------------------- */

.os-my-wordpress__comment-status--approved {
	background: rgba( 34, 113, 177, 0.12 );
	color: var( --os-ui-accent-strong, #135e96 );
}

.os-my-wordpress__comment-status--pending {
	background: rgba( 217, 119, 6, 0.16 );
	color: #b45309;
}

.os-my-wordpress__comment-status--spam {
	background: rgba( 179, 45, 46, 0.14 );
	color: var( --os-ui-danger-hover, #b32d2e );
}

.os-my-wordpress__comment-status--trash {
	background: var( --os-ui-hover, rgba( 0, 0, 0, 0.08 ) );
	color: var( --os-ui-fg-muted, #50575e );
}

.os-my-wordpress__comment-date-badge {
	background: transparent;
	color: var( --os-ui-fg-muted, #50575e );
	border: 1px solid var( --os-ui-border, #dcdcde );
	text-transform: none;
	letter-spacing: normal;
}

/* "In reply to ..." quote shown above the comment body when this
 * is a thread reply. */
.os-my-wordpress__comment-quote {
	margin: 0 0 16px;
	padding: 10px 14px;
	border-left: 3px solid var( --wp-admin-theme-color, #2271b1 );
	background: rgba( 34, 113, 177, 0.06 );
	border-radius: 0 6px 6px 0;
	font-size: 13px;
}

.os-my-wordpress__comment-quote-lead {
	font-size: 11px;
	font-weight: 600;
	color: var( --os-ui-fg-muted, #50575e );
	text-transform: uppercase;
	letter-spacing: 0.04em;
	margin-bottom: 4px;
}

.os-my-wordpress__comment-quote p {
	margin: 0;
	color: var( --os-fg, #1d2327 );
}

/* Comment body — same style as article-content but with a soft
 * card background so it reads as the focal element. */
.os-my-wordpress__comment-body {
	padding: 14px 16px;
	background: var( --os-ui-hover, rgba( 0, 0, 0, 0.03 ) );
	border-radius: 8px;
	border: 1px solid var( --os-ui-border, #dcdcde );
}

/* Parent-post card under the comment body. */
.os-my-wordpress__comment-post {
	display: flex;
	flex-direction: column;
	gap: 4px;
	padding: 10px 14px;
	border-radius: 8px;
	background: var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) );
	border: 1px solid var( --os-ui-border, #dcdcde );
}

.os-my-wordpress__comment-post-title {
	font-weight: 600;
	color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
	text-decoration: none;
}

.os-my-wordpress__comment-post-title:hover {
	text-decoration: underline;
}

.os-my-wordpress__comment-post-meta {
	font-size: 11px;
	color: var( --os-ui-fg-muted, #787c82 );
}

/* Replies list. */
.os-my-wordpress__comment-replies {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.os-my-wordpress__comment-reply {
	display: flex;
	gap: 10px;
	align-items: flex-start;
	padding: 10px 12px;
	border-radius: 8px;
	background: var( --os-ui-hover, rgba( 0, 0, 0, 0.02 ) );
	border: 1px solid var( --os-ui-border, #dcdcde );
}

.os-my-wordpress__comment-reply-avatar {
	width: 32px;
	height: 32px;
	border-radius: 50%;
	flex-shrink: 0;
}

.os-my-wordpress__comment-reply-text {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.os-my-wordpress__comment-reply-head {
	display: flex;
	align-items: baseline;
	gap: 8px;
	flex-wrap: wrap;
}

.os-my-wordpress__comment-reply-name {
	font-weight: 600;
	font-size: 13px;
}

.os-my-wordpress__comment-reply-when {
	font-size: 11px;
	color: var( --os-ui-fg-muted, #787c82 );
}

.os-my-wordpress__comment-reply-excerpt {
	margin: 0;
	font-size: 13px;
	color: var( --os-fg, #1d2327 );
	line-height: 1.45;
}

/* =================================================================== *
 *  USERS — list tiles + activity footprint surface.
 *
 *  The Users folder reuses the same canvas + 96×92 tile grid the
 *  post tiles use; the user-tile content (avatar circle + label +
 *  small sub-line) fits inside the cell. The "View activity
 *  footprint" surface is a full-width body view (no split pane)
 *  with hero / stats / heatmap / rhythm / timeline.
 *
 *  Since 0.20.0.
 * =================================================================== */

/* User tile — replaces the dashicon glyph with a real avatar */
.os-my-wordpress__tile--user {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: flex-start;
	gap: 4px;
	padding: 8px 4px 6px;
	box-sizing: border-box;
}

.os-my-wordpress__user-tile-avatar {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	overflow: hidden;
	background: linear-gradient(
		135deg,
		rgba( 34, 113, 177, 0.18 ),
		rgba( 34, 113, 177, 0.04 )
	);
	box-shadow:
		inset 0 0 0 1px rgba( 0, 0, 0, 0.08 ),
		0 2px 6px rgba( 0, 0, 0, 0.08 );
	flex: 0 0 auto;
}

.os-my-wordpress__user-tile-avatar img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.os-my-wordpress__user-tile-initials {
	font-weight: 600;
	font-size: 14px;
	letter-spacing: 0.02em;
	color: var( --wp-admin-theme-color, #2271b1 );
	text-transform: uppercase;
}

/* Single-line label for user tiles — we already have a generic
 * label clamp, but the user variant gets only ONE line so the
 * sub-line ("Editor · 12 posts") can fit beneath it. */
.os-my-wordpress__tile--user .os-file-tile__label {
	font-size: 11px;
	font-weight: 500;
	line-height: 1.25;
	max-height: 1.25em;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	width: 100%;
	padding: 0 4px;
	box-sizing: border-box;
}

.os-my-wordpress__user-tile-sub {
	font-size: 10px;
	line-height: 1.2;
	color: var( --os-tile-fg-muted, rgba( 0, 0, 0, 0.55 ) );
	max-height: 1.2em;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	width: 100%;
	text-align: center;
	padding: 0 4px;
	box-sizing: border-box;
}

/* Selected/hover user tiles inherit the same chrome the post tiles
 * use — see `.os-my-wordpress__tile--selected` above. */

/* ----- Activity footprint — full-width body surface -----
 *
 * Opens via the right-click context menu on a user tile (or the
 * dossier's "View activity footprint" button). Replaces the split
 * list/preview with a wide canvas so each section gets enough room
 * to read.
 */

.os-my-wordpress__footprint {
	display: flex;
	flex-direction: column;
	gap: 20px;
	padding: 24px 28px 32px;
	box-sizing: border-box;
	overflow-y: auto;
	height: 100%;
	/* Scroll container spans the full window width so wheel + touch
	 * scroll work no matter where the pointer is — including in the
	 * empty margins to the left / right of the content. The 1080px
	 * cap is applied to each immediate child instead (below). */
	align-items: center;
}

.os-my-wordpress__footprint > * {
	width: 100%;
	max-width: 1080px;
}

.os-my-wordpress__footprint-section {
	border: 1px solid var( --os-ui-border, #dcdcde );
	border-radius: 12px;
	padding: 18px 20px;
	background: var(
		--os-my-wordpress-surface,
		var( --os-surface, var( --os-ui-surface, rgba( 255, 255, 255, 0.6 ) ) )
	);
	box-shadow: 0 1px 2px rgba( 0, 0, 0, 0.04 );
	/* The parent `.__footprint` is a flex column with `overflow-y:
	 * auto`. Flex items default to `flex-shrink: 1`, so each section
	 * would compress along the column axis when the combined natural
	 * height exceeds the container — clipping the calendar heatmap
	 * (the calendar section also sets `overflow-x: auto`, which
	 * doesn't surface a vertical scrollbar). Lock the intrinsic
	 * height so the parent scroller, not the children, handles the
	 * overflow. */
	flex: 0 0 auto;
}

.os-my-wordpress__footprint-section h3 {
	margin: 0 0 12px;
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var( --os-ui-fg-muted, #50575e );
}

/* ----- Hero ----- */

.os-my-wordpress__footprint-hero {
	display: flex;
	gap: 20px;
	align-items: center;
	padding: 24px;
	border-radius: 16px;
	background: linear-gradient(
		135deg,
		rgba( 34, 113, 177, 0.10 ) 0%,
		rgba( 34, 113, 177, 0.02 ) 60%,
		transparent 100%
	);
	border: 1px solid var( --os-ui-border, #dcdcde );
}

.os-my-wordpress__footprint-avatar {
	width: 112px;
	height: 112px;
	border-radius: 50%;
	overflow: hidden;
	flex: 0 0 auto;
	box-shadow:
		inset 0 0 0 2px rgba( 255, 255, 255, 0.8 ),
		0 6px 24px rgba( 0, 0, 0, 0.14 );
	background: linear-gradient(
		135deg,
		rgba( 34, 113, 177, 0.18 ),
		rgba( 34, 113, 177, 0.04 )
	);
	display: inline-flex;
	align-items: center;
	justify-content: center;
}

.os-my-wordpress__footprint-avatar img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.os-my-wordpress__footprint-avatar
	.os-my-wordpress__user-tile-initials {
	font-size: 36px;
}

.os-my-wordpress__footprint-headline {
	min-width: 0;
	flex: 1 1 auto;
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.os-my-wordpress__footprint-title {
	margin: 0;
	font-size: 28px;
	line-height: 1.15;
	font-weight: 700;
	color: var( --os-fg, #1d2327 );
}

.os-my-wordpress__footprint-meta {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	align-items: center;
}

.os-my-wordpress__footprint-since {
	background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) );
	color: var( --os-ui-fg-muted, #50575e );
}

/* ----- Headline stats row (4 cards) ----- */

.os-my-wordpress__footprint-stats-row {
	display: grid;
	grid-template-columns: repeat( auto-fit, minmax( 160px, 1fr ) );
	gap: 12px;
	padding: 16px 18px;
}

.os-my-wordpress__footprint-stats-row
	.os-my-wordpress__user-stat {
	background: var( --os-surface, var( --os-ui-surface, rgba( 255, 255, 255, 0.9 ) ) );
	box-shadow: 0 1px 4px rgba( 0, 0, 0, 0.04 );
}

/* ----- Calendar heatmap (GitHub-style) ----- */

.os-my-wordpress__footprint-calendar-section {
	overflow-x: auto;
}

.os-my-wordpress__footprint-calendar {
	display: flex;
	flex-direction: column;
	gap: 8px;
	/* Size to the grid itself so the legend (justify-content: flex-end
	 * inside this same column) anchors to the grid's right edge instead
	 * of the section's. Centered within the wider section by
	 * `margin-inline: auto`. */
	width: max-content;
	max-width: 100%;
	margin-inline: auto;
}

.os-my-wordpress__footprint-grid {
	display: grid;
	/* Row 1 holds month labels (auto height); rows 2–8 are the 7
	 * weekdays at fixed 11px. Col 1 holds weekday labels (auto width);
	 * subsequent cols are 11px-wide week tracks, set per-cell via
	 * `grid-column` so the JS can place items explicitly. */
	grid-template-rows: auto repeat( 7, 11px );
	grid-template-columns: auto;
	grid-auto-columns: 11px;
	column-gap: 3px;
	row-gap: 3px;
	width: max-content;
}

.os-my-wordpress__footprint-month,
.os-my-wordpress__footprint-weekday {
	font-size: 10px;
	line-height: 11px;
	color: var( --os-ui-fg-muted, #50575e );
	white-space: nowrap;
}

.os-my-wordpress__footprint-weekday {
	/* Sit flush to the right of the label gutter so each label reads
	 * adjacent to its row. The 6px breathing room is in addition to
	 * the grid's column-gap. */
	padding-inline-end: 6px;
	align-self: center;
	justify-self: end;
}

.os-my-wordpress__footprint-month {
	padding-bottom: 2px;
}

.os-my-wordpress__footprint-cell {
	width: 11px;
	height: 11px;
	border-radius: 2px;
	background: var( --os-ui-border, #ebedf0 );
	display: inline-block;
	transition: transform 0.12s ease;
}

.os-my-wordpress__footprint-cell--pad {
	background: transparent;
}

.os-my-wordpress__footprint-cell--l0 {
	background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) );
}

.os-my-wordpress__footprint-cell--l1 {
	background: rgba( 34, 113, 177, 0.25 );
}

.os-my-wordpress__footprint-cell--l2 {
	background: rgba( 34, 113, 177, 0.5 );
}

.os-my-wordpress__footprint-cell--l3 {
	background: rgba( 34, 113, 177, 0.75 );
}

.os-my-wordpress__footprint-cell--l4 {
	background: var( --wp-admin-theme-color, #2271b1 );
}

.os-my-wordpress__footprint-cell:hover {
	transform: scale( 1.4 );
	z-index: 1;
}

.os-my-wordpress__footprint-legend {
	display: flex;
	gap: 4px;
	align-items: center;
	justify-content: flex-end;
	font-size: 11px;
	color: var( --os-ui-fg-muted, #50575e );
}

.os-my-wordpress__footprint-legend-label {
	font-size: 11px;
	color: var( --os-ui-fg-muted, #50575e );
	padding: 0 4px;
}

/* ----- Rhythm (weekday + hour bar charts) ----- */

.os-my-wordpress__footprint-rhythm-grid {
	display: grid;
	grid-template-columns: minmax( 0, 1fr ) minmax( 0, 1.7fr );
	gap: 24px;
}

@container ( max-width: 720px ) {
	.os-my-wordpress__footprint-rhythm-grid {
		grid-template-columns: 1fr;
	}
}

.os-my-wordpress__footprint-chart-caption {
	font-size: 12px;
	color: var( --os-ui-fg-muted, #50575e );
	margin-bottom: 6px;
}

.os-my-wordpress__footprint-bars {
	display: flex;
	align-items: end;
	gap: 4px;
	height: 100px;
	padding-bottom: 18px;
	position: relative;
}

.os-my-wordpress__footprint-bar-col {
	flex: 1 1 0;
	min-width: 0;
	display: flex;
	flex-direction: column;
	align-items: stretch;
	justify-content: flex-end;
	gap: 4px;
	height: 100%;
	position: relative;
}

.os-my-wordpress__footprint-bar {
	background: linear-gradient(
		180deg,
		var( --wp-admin-theme-color, #2271b1 ) 0%,
		rgba( 34, 113, 177, 0.55 ) 100%
	);
	border-radius: 3px 3px 0 0;
	min-height: 2px;
	transition: opacity 0.2s ease;
}

.os-my-wordpress__footprint-bar--empty {
	background: var( --os-ui-border, #dcdcde );
	height: 2px !important;
	min-height: 2px;
}

.os-my-wordpress__footprint-bar-label {
	position: absolute;
	bottom: -16px;
	left: 0;
	right: 0;
	text-align: center;
	font-size: 10px;
	color: var( --os-ui-fg-muted, #787c82 );
}

/* ----- Most-prolific-month callout ----- */

.os-my-wordpress__footprint-callout {
	display: flex;
	flex-direction: column;
	gap: 4px;
	align-items: center;
	text-align: center;
	padding: 20px 24px;
	background: linear-gradient(
		135deg,
		rgba( 34, 113, 177, 0.10 ),
		rgba( 34, 113, 177, 0.02 )
	);
}

.os-my-wordpress__footprint-callout-label {
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var( --os-ui-fg-muted, #50575e );
	font-weight: 600;
}

.os-my-wordpress__footprint-callout-value {
	margin: 2px 0;
	font-size: 24px;
	font-weight: 700;
	color: var( --os-fg, #1d2327 );
}

.os-my-wordpress__footprint-callout-detail {
	margin: 0;
	font-size: 13px;
	color: var( --os-ui-fg-muted, #50575e );
	max-width: 480px;
}

/* ----- Recent-activity timeline ----- */

.os-my-wordpress__footprint-timeline {
	list-style: none;
	margin: 0;
	padding: 0;
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.os-my-wordpress__footprint-timeline::before {
	content: '';
	position: absolute;
	inset-inline-start: 13px;
	top: 4px;
	bottom: 4px;
	width: 2px;
	background: var( --os-ui-border, #dcdcde );
	border-radius: 1px;
}

.os-my-wordpress__footprint-event {
	display: flex;
	gap: 12px;
	align-items: flex-start;
	position: relative;
	padding-inline-start: 36px;
	min-height: 28px;
}

.os-my-wordpress__footprint-dot {
	position: absolute;
	inset-inline-start: 0;
	top: 0;
	width: 28px;
	height: 28px;
	border-radius: 50%;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	background: var( --os-surface, var( --os-ui-surface, #fff ) );
	border: 2px solid var( --wp-admin-theme-color, #2271b1 );
	color: var( --wp-admin-theme-color, #2271b1 );
}

.os-my-wordpress__footprint-event--comment
	.os-my-wordpress__footprint-dot {
	border-color: var( --os-ui-fg-muted, #787c82 );
	color: var( --os-ui-fg-muted, #787c82 );
}

.os-my-wordpress__footprint-dot .dashicons {
	font-size: 14px;
	width: 14px;
	height: 14px;
	line-height: 1;
}

.os-my-wordpress__footprint-event-body {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
	flex: 1 1 auto;
}

.os-my-wordpress__footprint-event-title {
	font-weight: 600;
	font-size: 13px;
	color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
	text-decoration: none;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

a.os-my-wordpress__footprint-event-title:hover {
	text-decoration: underline;
}

.os-my-wordpress__footprint-event-meta {
	font-size: 11px;
	color: var( --os-ui-fg-muted, #787c82 );
}

/* ----- Footer actions ----- */

.os-my-wordpress__footprint-footer {
	display: flex;
	justify-content: flex-end;
	align-items: center;
	gap: 8px;
	flex-wrap: wrap;
	background: transparent;
	border-color: transparent;
	box-shadow: none;
	padding: 0;
}

/* ============================================================ *
 *  Status ribbon — diagonal corner banner for non-published tiles
 *  (since 0.21.0)
 *
 *  Classic 45° corner ribbon — same affordance as a stamp on a
 *  document. Applied to every tile whose post status isn't
 *  `publish` across every My WordPress section (Posts, Pages,
 *  Media drill-in, plugin kinds).
 *
 *  Implementation:
 *   1. Tiles with a status modifier get `overflow: hidden` so the
 *      ribbon's overshoot clips to the tile bounds.
 *   2. Ribbon is a wide strip (`width: 110px`) anchored to the
 *      top-right corner, rotated +45° so it forms a diagonal
 *      banner across that corner. The strip extends past the
 *      tile edges; `overflow: hidden` on the parent crops it
 *      into a clean triangle.
 *
 *  The status tiles need a positioning context. `.os-
 *  file-tile` is `position: absolute` (canvas layout) on the
 *  Posts/Pages browse views — that satisfies the positioning-
 *  context requirement. For the Media drill-in usage tiles we
 *  swap from `static` to `relative` via the per-status rule
 *  below so the ribbon stays anchored to its own tile.
 * ============================================================ */

/* Status ribbon — the diagonal corner banner — is now drawn by
 * the `<os-ribbon>` web component slotted inside `<os-tile>`.
 * The component's own shadow-DOM styles own placement, rotation,
 * background, lettering, and the WP admin-theme tint. The only
 * surface-specific rule left here is dimming the icon on non-
 * published tiles so the unpublished signal carries even when
 * the ribbon is hidden via the OS-settings toggle. */

.os-my-wordpress__tile--draft,
.os-my-wordpress__tile--pending,
.os-my-wordpress__tile--private,
.os-my-wordpress__tile--future {
	overflow: hidden;
}

os-tile[ status='draft' ] .os-file-tile__icon,
os-tile[ status='pending' ] .os-file-tile__icon,
os-tile[ status='private' ] .os-file-tile__icon,
os-tile[ status='future' ] .os-file-tile__icon {
	opacity: 0.7;
}

/* ============================================================ *
 *  Media section (since 0.21.0)
 * ============================================================ */

.os-my-wordpress__media-grid {
	display: grid;
	grid-template-columns: repeat( auto-fill, minmax( 120px, 1fr ) );
	/* A flow grid rather than the absolute canvas, but the air
	 * between two icons should not depend on which window you are
	 * looking at — so the gaps and the gutter come from the same
	 * tokens the canvas pitch is built from. */
	gap: var( --os-grid-gap-y, 16px ) var( --os-grid-gap-x, 20px );
	padding: var( --os-grid-padding, 16px );
	overflow-y: auto;
	align-content: start;
}

.os-my-wordpress__media-tile {
	/* Override the base `.os-file-tile` (position: absolute,
	 * width: 88px) so media tiles flow inside the CSS Grid instead of
	 * stacking at (0,0) like the absolute-positioned post/user tiles.
	 *
	 * `relative` (not `static`) so the tile remains a containing
	 * block for absolutely-positioned descendants — the `::after`
	 * shortcut-arrow badge on attachment tiles, and the
	 * `<os-ribbon>` post-status corner ribbon — both of which
	 * would otherwise escape to the nearest positioned ancestor
	 * (the window, the desktop, the viewport) and render way off
	 * the tile. */
	position: relative;
	width: auto;
	/* …and out of the fixed tile height for the same reason: a media
	 * tile is a square thumbnail plus a caption, sized by the grid
	 * column it lands in, not by the icon-canvas cell. */
	height: auto;
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 6px;
	padding: 8px;
	background: transparent;
	border: 1px solid transparent;
	border-radius: 8px;
	cursor: pointer;
	text-align: center;
	min-width: 0;
}

.os-my-wordpress__media-tile:hover {
	background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ) );
}

/* Media tiles inherit the canonical `.os-file-tile--selected`
 * rule from `desktop-files.css` — accent inset border + tinted
 * background — same as every other tile in the shell. */

/* Override the canonical 48×48 visual/preview sizing — media tiles
 * want the visual to fill the full grid-cell width as a square
 * thumbnail (or icon-fallback square). Photos browser look, not
 * folder-icon look. Applies to BOTH the `<img>` (real thumbnail)
 * and the dashicon fallback (non-image MIME), so the two tile
 * shapes are visually consistent in the grid. */
.os-my-wordpress__media-tile .os-file-tile__visual {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: auto;
	aspect-ratio: 1 / 1;
	overflow: hidden;
	border-radius: 6px;
	background: var(
		--os-media-tile-bg,
		rgba( 0, 0, 0, 0.05 )
	);
	/* Reset the base 32px font + 48px size since we're sizing the
	 * inner img/icon explicitly below. */
	font-size: 0;
	line-height: 1;
}

.os-my-wordpress__media-tile .os-file-tile__preview {
	width: 100%;
	height: 100%;
	object-fit: cover;
	border-radius: 0;
}

.os-my-wordpress__media-tile .os-file-tile__icon {
	/* Centered dashicon fallback — proportional to the larger
	 * thumbnail well so it doesn't look like a postage stamp. */
	width: auto;
	height: auto;
	font-size: 36px;
	color: var( --os-ui-fg-muted, #787c82 );
}

.os-my-wordpress__media-tile .os-file-tile__label {
	font-size: 12px;
	line-height: 1.3;
	overflow: hidden;
	text-overflow: ellipsis;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
}

/* ----- Media preview pane ----- */

.os-my-wordpress__media-pane {
	display: flex;
	flex-direction: column;
	gap: 16px;
	padding: 16px;
}

.os-my-wordpress__media-title {
	font-size: 16px;
	font-weight: 600;
	margin: 0;
}

.os-my-wordpress__media-visual {
	display: flex;
	align-items: center;
	justify-content: center;
	background: var( --os-media-visual-bg, var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ) );
	border-radius: 8px;
	min-height: 200px;
	max-height: 480px;
	overflow: hidden;
}

.os-my-wordpress__media-image,
.os-my-wordpress__media-video {
	max-width: 100%;
	max-height: 480px;
	object-fit: contain;
}

.os-my-wordpress__media-fallback-icon {
	font-size: 96px;
	width: 96px;
	height: 96px;
	color: var( --os-ui-fg-muted, #787c82 );
}

.os-my-wordpress__media-doc-link {
	margin-inline-start: 16px;
	align-self: center;
}

.os-my-wordpress__media-audio-stack {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 12px;
	padding: 24px;
}

.os-my-wordpress__media-meta {
	display: grid;
	grid-template-columns: max-content 1fr;
	gap: 4px 16px;
	font-size: 13px;
	margin: 0;
}

.os-my-wordpress__media-meta-term {
	color: var( --os-ui-fg-muted, #787c82 );
	margin: 0;
}

.os-my-wordpress__media-meta-value {
	color: var( --os-ui-fg, #1d2327 );
	word-break: break-word;
	margin: 0;
}

.os-my-wordpress__media-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	padding-top: 8px;
	border-top: 1px solid var( --os-ui-border, #dcdcde );
}

/* ----- Media drill-in ----- */

/* Drill-in: referencing-posts list. Visually identical to the
 * Posts / Pages tiles — same `os-file-tile` chrome (48px
 * dashicon + 88px-wide two-line label). We can't reuse the
 * absolute-positioning canvas layout from `renderEntityList` here
 * (those internals don't export), so this surface uses a flex-wrap
 * of fixed-width tiles to reproduce the look.
 *
 * NOTE: this rule INTENTIONALLY overrides the `__media-grid`
 * defaults (which target square thumbnails on 130px columns) so
 * usage tiles match the canvas-flowed post tiles instead.
 */
.os-my-wordpress__usage-grid {
	display: flex;
	flex-wrap: wrap;
	gap: var( --os-grid-gap-y, 16px ) var( --os-grid-gap-x, 20px );
	padding: var( --os-grid-padding, 16px );
	align-content: flex-start;
}

/* Reset the `__media-tile` overrides for usage tiles so they
 * inherit the unmodified `.os-file-tile` chrome the
 * canvas-positioned post tiles use — except for `position: static`
 * so they participate in the flex layout. */
.os-my-wordpress__tile--usage {
	width: var( --os-tile-w, 88px );
	align-items: center;
	padding: 8px 4px;
	gap: 6px;
}

/* Right-pane summary bar sits ABOVE the inline media preview. */
.os-my-wordpress__media-detail-summary-bar {
	padding: 12px 16px;
	border-bottom: 1px solid var( --os-ui-border, #dcdcde );
}

.os-my-wordpress__media-detail-summary {
	margin: 0;
	color: var( --os-ui-fg-muted, #787c82 );
	font-size: 13px;
}

.os-my-wordpress__media-detail-preview {
	/* Lets the embedded `__media-pane` keep its own padding without
	 * fighting the summary bar's spacing. */
	display: block;
}

/* ------------------------------------------------------------------ */
/* Agents section (`agent` entity kind — src/my-wordpress/agents-renderer.ts) */
/* ------------------------------------------------------------------ */

.dm-agents {
	display: flex;
	flex-direction: column;
	height: 100%;
	min-height: 0;
	padding: 12px;
	gap: 10px;
	overflow: hidden;
	box-sizing: border-box;
}

/* The framework is opt-in but the section is always listed, so with
   the option off the surface paints inert rather than vanishing.
   Only the sidebar dims: the detail pane holds the empty state
   explaining why, and the one button that undoes it — dimming the way
   out along with everything else is how a disabled screen becomes a
   dead end. */
.dm-agents.is-disabled .dm-agents__sidebar {
	opacity: 0.6;
}

/* Section-level loading — same scale curve as the My WordPress
   preview loader and the window-loading overlay, instead of the bare
   48px component default. */
.dm-agents__loading {
	display: flex;
	align-items: center;
	justify-content: center;
	height: 100%;
	min-height: 320px;
}

.dm-agents__loading os-spinner {
	--os-ui-spinner-size: clamp( 96px, 14vw, 192px );
}

.dm-agents__layout {
	display: flex;
	flex: 1;
	min-height: 0;
	gap: 12px;
}

/* Sidebar column: a fixed "Create agent" header over the scrolling
   list. The button used to be the list's last child, which put it out
   of reach on any site with enough agents to need it. */
.dm-agents__sidebar {
	display: flex;
	flex-direction: column;
	flex: 0 0 260px;
	/* `min-width: auto` on a flex item resolves to its min-content
	   width, and the rows carry long `white-space: nowrap` descriptions
	   — without this the column ignores its 260px basis and eats the
	   detail pane. The scroll container used to be this item, whose
	   non-visible overflow zeroed the minimum for free. */
	min-width: 0;
	min-height: 0;
	border-inline-end: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
}

.dm-agents__list-toolbar {
	flex: none;
	display: flex;
	padding-block-end: 8px;
	padding-inline-end: 8px;
}

.dm-agents__list-toolbar .dm-agents__create {
	flex: 1;
}

.dm-agents__list {
	display: flex;
	flex-direction: column;
	gap: 4px;
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	padding-inline-end: 4px;
}

.dm-agents__row {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 8px;
	border-radius: 8px;
	cursor: pointer;
}

.dm-agents__row:hover {
	background: var( --os-hover, rgba( 0, 0, 0, 0.06 ) );
}

.dm-agents__row.is-selected {
	background: var( --os-selected, rgba( 0, 0, 0, 0.1 ) );
}

.dm-agents__row-avatar {
	width: 32px;
	height: 32px;
	border-radius: 50%;
	/* Placeholder behind the avatar image while it loads — follows the
	   palette so it isn't a white disc on a dark station. */
	background: var( --os-ui-surface-elevated, #fff );
	flex: none;
}

.dm-agents__row-text {
	display: flex;
	flex-direction: column;
	min-width: 0;
	flex: 1;
}

.dm-agents__row-name {
	font-weight: 600;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.dm-agents__row-desc {
	font-size: 12px;
	opacity: 0.65;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}


.dm-agents__detail {
	flex: 1;
	min-width: 0;
	overflow-y: auto;
	display: flex;
	flex-direction: column;
	gap: 10px;
}

/* `<os-empty-state>` owns the icon + copy and centres them within
   itself; the host is still a flex item that shrinks to its content,
   so without this it sits at the top of a pane that is mostly empty
   space. Auto margins on both axes absorb the slack — placement only,
   same as the comments window. Covers the framework-off state and
   "No agents yet" alike. */
.dm-agents__detail > os-empty-state {
	margin: auto;
}

.dm-agents__detail-head {
	display: flex;
	align-items: center;
	gap: 10px;
}

.dm-agents__detail-avatar {
	width: 48px;
	height: 48px;
	border-radius: 50%;
	/* Placeholder behind the avatar image while it loads — follows the
	   palette so it isn't a white disc on a dark station. */
	background: var( --os-ui-surface-elevated, #fff );
	flex: none;
}

.dm-agents__detail-title {
	flex: 1;
	min-width: 0;
}

.dm-agents__detail-title h3 {
	margin: 0;
	font-size: 16px;
}

.dm-agents__detail-slug {
	font-size: 12px;
	opacity: 0.6;
}

/* Verbs get their own row under the identity block: the set grows
   (profile, contributions, desktop, chat, delete) and squeezing them
   beside the name truncated it first. */
.dm-agents__detail-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.dm-agents__tabs {
	display: flex;
	gap: 4px;
	border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
}

.dm-agents__tab {
	appearance: none;
	background: none;
	border: none;
	border-bottom: 2px solid transparent;
	padding: 6px 10px;
	font: inherit;
	color: inherit;
	cursor: pointer;
	opacity: 0.7;
}

.dm-agents__tab.is-active {
	border-bottom-color: var( --os-accent, #2271b1 );
	opacity: 1;
	font-weight: 600;
}

.dm-agents__pane {
	display: flex;
	flex-direction: column;
	gap: 10px;
	padding-bottom: 12px;
}

/* Pane-level loading: same big, centered WordPress mark the section
   loader uses, instead of a bare 48px spinner pinned to the top-left
   of the pane. */
.dm-agents__pane--loading {
	align-items: center;
	justify-content: center;
	flex: 1;
	min-height: 260px;
}

.dm-agents__pane--loading os-spinner {
	--os-ui-spinner-size: clamp( 96px, 12vw, 160px );
}

.dm-agents__hint {
	margin: 0;
	font-size: 12px;
	opacity: 0.7;
}

.dm-agents__category {
	margin: 8px 0 0;
	font-size: 12px;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	opacity: 0.6;
}

.dm-agents__ability {
	display: grid;
	grid-template-columns: 1fr auto;
	gap: 2px 8px;
	align-items: center;
	padding: 4px 0;
}

.dm-agents__ability-desc {
	grid-column: 1 / -1;
	margin: 0;
	font-size: 12px;
	opacity: 0.6;
}

.dm-agents__trigger {
	border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
	border-radius: 8px;
	padding: 8px 10px;
	display: flex;
	flex-direction: column;
	gap: 6px;
}

.dm-agents__trigger-head {
	display: flex;
	align-items: center;
	gap: 8px;
}

.dm-agents__trigger-summary {
	flex: 1;
	font-size: 12px;
	opacity: 0.65;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.dm-agents__trigger-config {
	display: flex;
	flex-wrap: wrap;
	gap: 4px 14px;
}

.dm-agents__actions {
	display: flex;
	gap: 8px;
}

```
