PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
← All changes | assets/css/my-wordpress.css +1469 -396 0.8.91.1.10 View file →
@@ -1,13 +1,12 @@
1 1 /**
2 2 * My WordPress — window-local layout.
3 3 *
4 4 * Two-pane file-explorer for browsing WordPress entities. Mirrors the
5 - * Recycle Bin and Posts window CSS conventions (`desktop-mode-*`
5 + * Recycle Bin and Posts window CSS conventions (`os-*`
6 6 * BEM-ish prefix, container-relative geometry, theming via the
7 - * desktop-mode-variables CSS custom properties).
7 + * os-variables CSS custom properties).
8 8 *
9 - * @since 0.8.0
10 9 */
11 10
12 11 .desktop-mode-my-wordpress {
13 12 display: flex;
@@ -15,55 +14,67 @@
15 14 width: 100%;
16 15 height: 100%;
17 16 min-height: 0;
18 17 font-family: var(
19 - --desktop-mode-font,
18 + --os-font,
20 19 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif
21 20 );
22 - color: var( --desktop-mode-my-wordpress-fg, #1d2327 );
21 + color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
23 22 /* Window background — separate from the wallpaper bg so the
24 23 * folder window reads as a real OS window even when the desktop
25 24 * gradient is dark. Themes / plugins can override
26 - * `--desktop-mode-my-wordpress-bg` to repaint just this surface
25 + * `--os-my-wordpress-bg` to repaint just this surface
27 26 * without touching every other window. */
28 27 background: var(
29 - --desktop-mode-my-wordpress-bg,
30 - var( --desktop-mode-window-bg, #fff )
28 + --os-my-wordpress-bg,
29 + var( --os-window-bg, #fff )
31 30 );
32 - /* Light-context tile color tokens — same recipe as the folder
33 - * window. Tiles inside My WordPress paint dark-on-light. */
34 - --desktop-mode-tile-fg: var( --desktop-mode-my-wordpress-fg, #1d2327 );
35 - --desktop-mode-tile-fg-muted: rgba( 0, 0, 0, 0.55 );
36 - --desktop-mode-tile-hover-bg: var(
37 - --desktop-mode-hover,
31 + /* Tile color tokens — same recipe as the folder window. Both
32 + * chain through the palette so the surface follows the active
33 + * desktop theme, with the pre-brand WordPress-admin literal as
34 + * the floor if no stylesheet declares one.
35 + *
36 + * `--os-tile-fg-muted` used to be the bare literal below, which
37 + * outranked the palette's own value and painted every tile's
38 + * secondary line near-black — invisible on a dark station, while
39 + * the label right beside it followed the theme correctly. A
40 + * colour declared next to the thing it paints is out of reach of
41 + * the palette AND of every desktop theme. */
42 + --os-tile-fg: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
43 + --os-tile-fg-muted: var(
44 + --os-my-wordpress-fg-muted,
45 + var( --os-ui-fg-muted, rgba( 0, 0, 0, 0.55 ) )
46 + );
47 + --os-tile-hover-bg: var(
48 + --os-hover,
38 49 rgba( 0, 0, 0, 0.06 )
39 50 );
40 51 /* Light-context label rendering — same recipe as the folder
41 52 * window. Defined as tokens in `variables.css`; the single
42 - * `.desktop-mode-file-tile__label` rule reads them for every
53 + * `.os-file-tile__label` rule reads them for every
43 54 * surface. */
44 - --desktop-mode-tile-label-shadow: none;
45 - --desktop-mode-tile-label-weight: 500;
46 - --desktop-mode-tile-label-smoothing: antialiased;
47 - --desktop-mode-tile-label-color: var( --desktop-mode-my-wordpress-fg, #1d2327 );
55 + --os-tile-label-shadow: none;
56 + --os-tile-label-weight: 500;
57 + --os-tile-label-smoothing: antialiased;
58 + --os-tile-label-color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
48 59 }
49 60
50 -/* Pinned-icon visual cue — used by `desktop-mode-icon--pinned` (the
61 +/* Pinned-icon visual cue — used by `os-icon--pinned` (the
51 62 * generic flag added in 0.8.0). The icon-rail itself owns the
52 63 * non-draggable invariant; this rule just removes the grab affordance
53 64 * if the framework ever attaches one in a later phase.
54 65 */
55 -.desktop-mode-icon--pinned {
66 +.os-icon--pinned {
56 67 cursor: pointer;
57 68 }
58 69
59 70 /* Breadcrumb header chrome — owned by the shared
60 - * `.desktop-mode-breadcrumbs` rules in `desktop-files.css`. No
71 + * `.os-breadcrumbs` rules in `desktop-files.css`. No
61 72 * per-surface duplication. */
62 73
63 74 /* ----- Body host ----- */
64 75
65 -.desktop-mode-my-wordpress__body {
76 +.os-my-wordpress__body {
66 77 flex: 1 1 auto;
67 78 min-height: 0;
68 79 overflow: hidden;
69 80 position: relative;
@@ -76,19 +87,23 @@
76 87 * Holds the server-side search input today; reserved for future
77 88 * filter / sort controls. Visual treatment mirrors the Content
78 89 * Graph toolbar's search row so the two surfaces read as the same
79 90 * control. */
80 -.desktop-mode-my-wordpress__list-toolbar {
91 +.os-my-wordpress__list-toolbar {
81 92 flex: 0 0 auto;
82 93 display: flex;
83 94 align-items: center;
84 95 gap: 8px;
85 96 padding: 8px 12px;
86 - border-bottom: 1px solid var( --desktop-mode-border, #dcdcde );
87 - background: transparent;
97 + border-bottom: 1px solid var( --os-ui-border, #dcdcde );
98 + /* Opaque and above the canvas: the tile canvas scrolls beneath
99 + * this row, and a transparent toolbar let tiles read through it. */
100 + position: relative;
101 + z-index: 4;
102 + background: var( --os-ui-bg, #fff );
88 103 }
89 104
90 -.desktop-mode-my-wordpress__list-toolbar-search {
105 +.os-my-wordpress__list-toolbar-search {
91 106 position: relative;
92 107 flex: 1 1 auto;
93 108 min-width: 0;
94 109 max-width: 360px;
@@ -93,23 +108,23 @@
93 108 min-width: 0;
94 109 max-width: 360px;
95 110 }
96 111
97 -.desktop-mode-my-wordpress__list-toolbar-search-input {
112 +.os-my-wordpress__list-toolbar-search-input {
98 113 width: 100%;
99 114 padding: 6px 10px;
100 - border: 1px solid var( --wpd-border, #d8dde4 );
115 + border: 1px solid var( --os-ui-border, #d8dde4 );
101 116 border-radius: 6px;
102 - background: var( --wpd-surface, #fff );
117 + background: var( --os-ui-surface, #fff );
103 118 color: inherit;
104 119 font-size: 13px;
105 120 box-sizing: border-box;
106 121 }
107 122
108 -.desktop-mode-my-wordpress__list-toolbar-search-input:focus {
123 +.os-my-wordpress__list-toolbar-search-input:focus {
109 124 outline: none;
110 - border-color: var( --wpd-accent, #2c6be5 );
111 - box-shadow: 0 0 0 3px var( --wpd-accent-soft, rgba( 44, 107, 229, 0.18 ) );
125 + border-color: var( --os-ui-accent, #2c6be5 );
126 + box-shadow: 0 0 0 3px var( --os-ui-accent-soft, rgba( 44, 107, 229, 0.18 ) );
112 127 }
113 128
114 129 /* When the list-toolbar mounts, the split pane needs to claim the
115 130 * remaining vertical space inside the flex column body — without
@@ -114,9 +129,9 @@
114 129 /* When the list-toolbar mounts, the split pane needs to claim the
115 130 * remaining vertical space inside the flex column body — without
116 131 * this rule it would size to its content (which, for an empty list,
117 132 * is zero) and the tile canvas would collapse. */
118 -.desktop-mode-my-wordpress__body > .desktop-mode-my-wordpress__split {
133 +.os-my-wordpress__body > .os-my-wordpress__split {
119 134 flex: 1 1 auto;
120 135 min-height: 0;
121 136 }
122 137
@@ -124,10 +139,10 @@
124 139 * (no skeleton flash, no empty intermediate page) but reads as
125 140 * "stale" until the new page lands and the renderer atomically swaps
126 141 * the tiles. `pointer-events: none` blocks the user from clicking
127 142 * a tile that's about to disappear. */
128 -.desktop-mode-my-wordpress__tiles--searching,
129 -.desktop-mode-my-wordpress__media-grid--searching {
143 +.os-my-wordpress__tiles--searching,
144 +.os-my-wordpress__media-grid--searching {
130 145 opacity: 0.45;
131 146 pointer-events: none;
132 147 transition: opacity 0.12s ease-out;
133 148 }
@@ -132,27 +147,27 @@
132 147 transition: opacity 0.12s ease-out;
133 148 }
134 149
135 150 /* Reuse the file-system folder window's status-bar element
136 - * (`.desktop-mode-folder-status-bar`) so segments render with the
151 + * (`.os-folder-status-bar`) so segments render with the
137 152 * exact same DOM the file-system uses — but retone it for the
138 153 * white window background. The dark default (white-on-dark) reads
139 - * as invisible against `--desktop-mode-window-bg`. */
140 -.desktop-mode-my-wordpress > .desktop-mode-folder-status-bar {
141 - border-top: 1px solid var( --desktop-mode-border, #dcdcde );
154 + * as invisible against `--os-window-bg`. */
155 +.desktop-mode-my-wordpress > .os-folder-status-bar {
156 + border-top: 1px solid var( --os-ui-border, #dcdcde );
142 157 background: transparent;
143 - color: var( --desktop-mode-muted, #50575e );
158 + color: var( --os-ui-fg-muted, #50575e );
144 159 }
145 160
146 161 .desktop-mode-my-wordpress
147 - > .desktop-mode-folder-status-bar
148 - button.desktop-mode-folder-status-bar__segment:hover {
149 - background: var( --desktop-mode-hover, rgba( 0, 0, 0, 0.06 ) );
162 + > .os-folder-status-bar
163 + button.os-folder-status-bar__segment:hover {
164 + background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) ) );
150 165 }
151 166
152 167 /* ----- Tile canvas (root + entity list) -----
153 168 *
154 - * Both views use the SAME tile element (`.desktop-mode-file-tile`)
169 + * Both views use the SAME tile element (`.os-file-tile`)
155 170 * the wallpaper uses, so visuals stay 1:1 with the desktop. The
156 171 * canvas is `position: relative`; tiles are absolute-positioned and
157 172 * draggable — same interaction pattern as the wallpaper, just
158 173 * scoped to the window. Positions persist per-window in
@@ -158,9 +173,9 @@
158 173 * scoped to the window. Positions persist per-window in
159 174 * `localStorage` (see `createTileLayout` in src/my-wordpress).
160 175 */
161 176
162 -.desktop-mode-my-wordpress__canvas {
177 +.os-my-wordpress__canvas {
163 178 position: relative;
164 179 min-height: 100%;
165 180 box-sizing: border-box;
166 181 /* Note — DO NOT add `overflow: auto` here. Scroll is owned by
@@ -169,9 +184,9 @@
169 184 * would steal the wheel from the outer pane and break the
170 185 * IntersectionObserver-based infinite scroll. */
171 186 }
172 187
173 -.desktop-mode-my-wordpress__grid {
188 +.os-my-wordpress__grid {
174 189 height: 100%;
175 190 overflow-y: scroll;
176 191 }
177 192
@@ -179,12 +194,12 @@
179 194 * On the wallpaper the file tile sits on a dark gradient and uses
180 195 * white text + a 12% white hover — both of which would be invisible
181 196 * on a white window background. Inside My WordPress we re-tone the
182 197 * same element to read on light surfaces. The dimensions / tile
183 - * structure are inherited from `.desktop-mode-file-tile`.
198 + * structure are inherited from `.os-file-tile`.
184 199 */
185 -.desktop-mode-my-wordpress .desktop-mode-file-tile {
186 - /* Color + hover come from `--desktop-mode-tile-*` — see the
200 +.desktop-mode-my-wordpress .os-file-tile {
201 + /* Color + hover come from `--os-tile-*` — see the
187 202 * scope-level variable overrides on `.desktop-mode-my-wordpress`
188 203 * above. We only override structural / animation bits here.
189 204 *
190 205 * Drop the wallpaper's `transition: transform` — it hints at
@@ -196,24 +211,190 @@
196 211 transform: none;
197 212 }
198 213
199 214 /* Selected state for every tile in My WordPress is rendered by
200 - * the canonical `.desktop-mode-file-tile--selected` rule in
215 + * the canonical `.os-file-tile--selected` rule in
201 216 * `desktop-files.css` — same accent border + tinted background
202 217 * the folder windows + wallpaper tiles use. No section-specific
203 218 * override here — visual parity across the whole shell.
204 219 */
205 220
221 +/* Large tiles — sections that opt in with `tileSize: 'large'`.
222 + *
223 + * A section whose rows carry a photograph (a shop's products) reads
224 + * as a catalogue, not a file list: the picture is what's being
225 + * scanned, and at 48px it's a thumbnail of a thumbnail. Roughly
226 + * doubling the icon well also gives a corner ribbon room to be a
227 + * corner flash instead of covering the subject.
228 + *
229 + * The cell pitch lives in `TILE_METRICS_LARGE` (src/my-wordpress/
230 + * index.ts) and the two MUST move together — a wider tile in a cell
231 + * that didn't grow overlaps its neighbour. */
232 +/*
233 + * Image-led sections re-point the tile tokens for their whole
234 + * subtree rather than overriding the tile's width directly. The base
235 + * `.os-file-tile` rule then sizes itself from them, and so does
236 + * everything derived from them (the label's max-width, the loading
237 + * skeletons) — one declaration instead of a chain of overrides that
238 + * have to be kept in step.
239 + */
240 +.os-my-wordpress__tiles--large {
241 + --os-tile-w: var( --os-tile-w-large, 132px );
242 + --os-tile-h: var( --os-tile-h-large, 160px );
243 +}
244 +
245 +.os-my-wordpress__tiles--large .os-file-tile__visual,
246 +.os-my-wordpress__tiles--large .os-file-tile__icon {
247 + width: 104px;
248 + height: 104px;
249 + font-size: 64px;
250 +}
251 +
252 +.os-my-wordpress__tiles--large .os-file-tile__preview {
253 + width: 104px;
254 + height: 104px;
255 + border-radius: 8px;
256 +}
257 +
258 +/* Three lines of title at this width instead of two — a product name
259 + * is long and the extra width earns the room. Matches the taller
260 + * cell in `TILE_METRICS_LARGE`. */
261 +.os-my-wordpress__tiles--large .os-file-tile__label {
262 + -webkit-line-clamp: 3;
263 +}
264 +
265 +/* Featured-image tiles. Entries that have a featured image render it
266 + * in the icon well instead of the section dashicon — decisive for
267 + * image-led types like WooCommerce products. The base
268 + * `.os-file-tile__preview` rule in `desktop-files.css`
269 + * already sizes and crops it; all that's added here is a hairline
270 + * border so a light-on-light photo still reads as a distinct object
271 + * against the wallpaper, plus a neutral well behind it so a
272 + * transparent PNG or a slow-loading image doesn't flash the
273 + * wallpaper through. */
274 +.os-my-wordpress__tile--entry .os-file-tile__preview {
275 + background: var(
276 + --os-media-tile-bg,
277 + rgba( 0, 0, 0, 0.05 )
278 + );
279 + /* The hairline was a flat 12%-alpha black — right on the pre-brand
280 + * white canvas, and nothing at all on a dark one, where a black
281 + * line over a near-black surface leaves the photo edgeless. It
282 + * follows the border token now, so it inverts with the theme. */
283 + box-shadow: inset 0 0 0 1px var( --os-ui-border, rgba( 0, 0, 0, 0.12 ) );
284 +}
285 +
286 +/* Banded list views. A section can split its tiles into labelled
287 + * bands (WooCommerce Orders groups by status, needs-attention first)
288 + * instead of one flat canvas. The root container stops being a
289 + * positioned canvas and becomes a plain stack; each band owns its
290 + * own canvas below its heading. */
291 +.os-my-wordpress__tiles--banded {
292 + position: static;
293 + min-height: 0;
294 +}
295 +
296 +.os-my-wordpress__band + .os-my-wordpress__band {
297 + margin-block-start: 8px;
298 +}
299 +
300 +/* Bands are laid out in order before the first page arrives so none
301 + * appears mid-scroll, but one that hasn't received a row yet stays
302 + * out of the way rather than showing a heading over nothing. */
303 +.os-my-wordpress__band--empty {
304 + display: none;
305 +}
306 +
307 +.os-my-wordpress__band-title {
308 + display: flex;
309 + align-items: center;
310 + gap: 8px;
311 + position: sticky;
312 + /* Keeps the band label visible while scrolling a long band —
313 + * the whole point of banding is knowing what you're looking at. */
314 + top: 0;
315 + /* Above the absolutely-positioned tiles of every band, including
316 + * the ones that come later in the DOM. */
317 + z-index: 3;
318 + margin: 0;
319 + padding: 6px 12px;
320 + font-size: 11px;
321 + font-weight: 600;
322 + letter-spacing: 0.06em;
323 + text-transform: uppercase;
324 + color: var( --os-ui-fg-muted, #787c82 );
325 + /* Near-opaque on purpose. Tiles scrolling underneath are behind
326 + * the bar, but at 85% they still read as ghostly blurred shapes
327 + * through it, which looks like a rendering fault rather than
328 + * frosted glass. The blur + saturate keeps the frosted feel while
329 + * the alpha stops the content beneath resolving into shapes. */
330 + background: color-mix(
331 + in srgb,
332 + var( --os-ui-bg, #fff ) 94%,
333 + transparent
334 + );
335 + backdrop-filter: blur( 14px ) saturate( 180% );
336 + border-block-end: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) );
337 +}
338 +
339 +/* Browsers without `color-mix()` fall back to the flat surface —
340 + * still opaque, just without the translucency. */
341 +@supports not ( background: color-mix( in srgb, red 50%, transparent ) ) {
342 + .os-my-wordpress__band-title {
343 + background: var( --os-ui-bg, #fff );
344 + }
345 +}
346 +
347 +/* Attention-carrying bands (WooCommerce's "Needs attention",
348 + * "Problems") tint their bar so the eye lands there first instead of
349 + * having to read every heading. */
350 +.os-my-wordpress__band-title--warn {
351 + color: var( --os-ui-warning, #8a6116 );
352 + background: color-mix(
353 + in srgb,
354 + var( --os-ui-warning, #8a6116 ) 12%,
355 + var( --os-ui-bg, #fff )
356 + );
357 + border-block-end-color: color-mix(
358 + in srgb,
359 + var( --os-ui-warning, #8a6116 ) 35%,
360 + transparent
361 + );
362 +}
363 +
364 +.os-my-wordpress__band-title--danger {
365 + color: var( --os-ui-danger, #b32d2e );
366 + background: color-mix(
367 + in srgb,
368 + var( --os-ui-danger, #b32d2e ) 12%,
369 + var( --os-ui-bg, #fff )
370 + );
371 + border-block-end-color: color-mix(
372 + in srgb,
373 + var( --os-ui-danger, #b32d2e ) 35%,
374 + transparent
375 + );
376 +}
377 +
378 +.os-my-wordpress__band-count {
379 + padding: 0 6px;
380 + border-radius: 999px;
381 + font-size: 11px;
382 + line-height: 1.7;
383 + background: color-mix( in srgb, currentColor 16%, transparent );
384 + color: inherit;
385 +}
386 +
206 387 /* Lock state — another user is currently editing this post. We dim
207 388 * the tile a touch and overlay a small dashicon-lock badge on the
208 389 * top-right of the icon. The whole tile stays clickable; the lock
209 390 * is just a visual heads-up so the user knows the takeover dialog
210 391 * is coming if they double-click. */
211 -.desktop-mode-my-wordpress__tile--locked {
392 +.os-my-wordpress__tile--locked {
212 393 opacity: 0.85;
213 394 }
214 395
215 -.desktop-mode-my-wordpress__tile-lock {
396 +.os-my-wordpress__tile-lock {
216 397 position: absolute;
217 398 top: 4px;
218 399 inset-inline-end: 14px;
219 400 width: 18px;
@@ -221,10 +402,10 @@
221 402 font-size: 12px;
222 403 line-height: 18px;
223 404 text-align: center;
224 405 border-radius: 50%;
225 - background: var( --desktop-mode-danger, #b32d2e );
226 - color: #fff;
406 + background: var( --os-danger, var( --os-ui-danger, #b32d2e ) );
407 + color: var( --os-ui-fg-on-accent, #fff );
227 408 box-shadow:
228 409 0 0 0 1.5px rgba( 0, 0, 0, 0.4 ),
229 410 0 1px 3px rgba( 0, 0, 0, 0.35 );
230 411 pointer-events: none;
@@ -229,10 +410,13 @@
229 410 0 1px 3px rgba( 0, 0, 0, 0.35 );
230 411 pointer-events: none;
231 412 }
232 413
233 -/* Tooltip lock banner — shown above the excerpt when present. */
234 -.desktop-mode-my-wordpress__tooltip-lock {
414 +/* Hover-card lock banner — shown above the excerpt when present. The
415 + * wash was a fixed 8%-alpha red, which on a dark card was a smear too
416 + * faint to read as a banner at all; it follows the badge-danger token
417 + * now, so it lands at the alpha the active theme picked. */
418 +.os-my-wordpress__tooltip-lock {
235 419 display: flex;
236 420 align-items: center;
237 421 gap: 6px;
238 422 margin: 0 0 8px;
@@ -237,15 +421,18 @@
237 421 gap: 6px;
238 422 margin: 0 0 8px;
239 423 padding: 6px 8px;
240 424 border-radius: 4px;
241 - background: rgba( 179, 45, 46, 0.08 );
242 - color: var( --desktop-mode-danger, #b32d2e );
425 + background: var(
426 + --os-my-wordpress-card-lock-bg,
427 + rgba( 179, 45, 46, 0.08 )
428 + );
429 + color: var( --os-danger, var( --os-ui-danger, #b32d2e ) );
243 430 font-size: 12px;
244 431 font-weight: 600;
245 432 }
246 433
247 -.desktop-mode-my-wordpress__tooltip-lock .dashicons {
434 +.os-my-wordpress__tooltip-lock .dashicons {
248 435 font-size: 14px;
249 436 width: 14px;
250 437 height: 14px;
251 438 }
@@ -255,9 +442,9 @@
255 442 * HiDPI screens snaps text to fractional device pixels and renders
256 443 * blurry. `max-height: 2 × line-height` + `overflow: hidden` looks
257 444 * the same and stays sharp.
258 445 */
259 -.desktop-mode-my-wordpress__tile .desktop-mode-file-tile__label {
446 +.os-my-wordpress__tile .os-file-tile__label {
260 447 font-size: 12px;
261 448 line-height: 1.3;
262 449 max-height: calc( 1.3em * 2 );
263 450 text-align: center;
@@ -267,9 +454,9 @@
267 454 }
268 455
269 456 /* ----- Entity list: two-pane split ----- */
270 457
271 -.desktop-mode-my-wordpress__split {
458 +.os-my-wordpress__split {
272 459 display: grid;
273 460 grid-template-columns: minmax( 240px, 60% ) minmax( 0, 1fr );
274 461 height: 100%;
275 462 min-height: 0;
@@ -274,11 +461,11 @@
274 461 height: 100%;
275 462 min-height: 0;
276 463 }
277 464
278 -.desktop-mode-my-wordpress__list {
465 +.os-my-wordpress__list {
279 466 overflow-y: scroll;
280 - border-inline-end: 1px solid var( --desktop-mode-border, #dcdcde );
467 + border-inline-end: 1px solid var( --os-ui-border, #dcdcde );
281 468 min-width: 0;
282 469 }
283 470
284 471 /* Always-visible scrollbar styling for the icon canvases. macOS
@@ -287,40 +474,40 @@
287 474 * canvas where the user can't tell whether more icons live below
288 475 * or to the right. We force the scrollbar to render at all times
289 476 * via `::-webkit-scrollbar` (works in WebKit / Blink / Edge), and
290 477 * the explicit `scrollbar-width: thin` covers Firefox. */
291 -.desktop-mode-my-wordpress__list,
292 -.desktop-mode-my-wordpress__grid {
478 +.os-my-wordpress__list,
479 +.os-my-wordpress__grid {
293 480 scrollbar-width: thin;
294 481 scrollbar-color: rgba( 0, 0, 0, 0.25 ) transparent;
295 482 }
296 483
297 -.desktop-mode-my-wordpress__list::-webkit-scrollbar,
298 -.desktop-mode-my-wordpress__grid::-webkit-scrollbar {
484 +.os-my-wordpress__list::-webkit-scrollbar,
485 +.os-my-wordpress__grid::-webkit-scrollbar {
299 486 width: 12px;
300 487 height: 12px;
301 488 }
302 489
303 -.desktop-mode-my-wordpress__list::-webkit-scrollbar-thumb,
304 -.desktop-mode-my-wordpress__grid::-webkit-scrollbar-thumb {
305 - background: rgba( 0, 0, 0, 0.25 );
490 +.os-my-wordpress__list::-webkit-scrollbar-thumb,
491 +.os-my-wordpress__grid::-webkit-scrollbar-thumb {
492 + background: var( --os-ui-scrim, rgba( 0, 0, 0, 0.25 ) );
306 493 border-radius: 6px;
307 494 border: 3px solid transparent;
308 495 background-clip: padding-box;
309 496 }
310 497
311 -.desktop-mode-my-wordpress__list::-webkit-scrollbar-thumb:hover,
312 -.desktop-mode-my-wordpress__grid::-webkit-scrollbar-thumb:hover {
313 - background: rgba( 0, 0, 0, 0.4 );
498 +.os-my-wordpress__list::-webkit-scrollbar-thumb:hover,
499 +.os-my-wordpress__grid::-webkit-scrollbar-thumb:hover {
500 + background: var( --os-ui-scrim, rgba( 0, 0, 0, 0.4 ) );
314 501 background-clip: padding-box;
315 502 }
316 503
317 -.desktop-mode-my-wordpress__list::-webkit-scrollbar-track,
318 -.desktop-mode-my-wordpress__grid::-webkit-scrollbar-track {
504 +.os-my-wordpress__list::-webkit-scrollbar-track,
505 +.os-my-wordpress__grid::-webkit-scrollbar-track {
319 506 background: transparent;
320 507 }
321 508
322 -.desktop-mode-my-wordpress__sentinel {
509 +.os-my-wordpress__sentinel {
323 510 height: 1px;
324 511 }
325 512
326 513 /* Tile-grid loading skeleton — placeholder tiles that mimic the
@@ -337,50 +524,53 @@
337 524 * `src/my-wordpress/index.ts`. Per-tile shimmer delay and label
338 525 * width are inlined so a row of placeholders reads as a cascade,
339 526 * not a uniform pulse.
340 527 *
341 - * Tile dimensions mirror the real `.desktop-mode-file-tile` width
528 + * Tile dimensions mirror the real `.os-file-tile` width
342 529 * so a placeholder sits cleanly in the cell its successor will
343 530 * inherit.
344 531 */
345 -.desktop-mode-my-wordpress__skeleton-tile {
532 +.os-my-wordpress__skeleton-tile {
346 533 position: absolute;
347 534 display: flex;
348 535 flex-direction: column;
349 536 align-items: center;
350 537 gap: 6px;
351 - width: 88px;
538 + /* Same box as the real tile it stands in for — a skeleton that
539 + * doesn't match the grid makes the list jump when rows land. */
540 + width: var( --os-tile-w, 88px );
541 + height: var( --os-tile-h, 104px );
352 542 padding: 8px 4px;
353 543 box-sizing: border-box;
354 544 pointer-events: none;
355 545 }
356 546
357 -.desktop-mode-my-wordpress__skeleton-icon,
358 -.desktop-mode-my-wordpress__skeleton-label {
547 +.os-my-wordpress__skeleton-icon,
548 +.os-my-wordpress__skeleton-label {
359 549 background: linear-gradient(
360 550 90deg,
361 - var( --desktop-mode-skeleton-low, rgba( 0, 0, 0, 0.05 ) ) 0%,
362 - var( --desktop-mode-skeleton-high, rgba( 0, 0, 0, 0.13 ) ) 50%,
363 - var( --desktop-mode-skeleton-low, rgba( 0, 0, 0, 0.05 ) ) 100%
551 + var( --os-skeleton-low, var( --os-ui-hover, rgba( 0, 0, 0, 0.05 ) ) ) 0%,
552 + var( --os-skeleton-high, var( --os-ui-hover, rgba( 0, 0, 0, 0.13 ) ) ) 50%,
553 + var( --os-skeleton-low, var( --os-ui-hover, rgba( 0, 0, 0, 0.05 ) ) ) 100%
364 554 );
365 555 background-size: 200% 100%;
366 - animation: desktop-mode-my-wordpress-skeleton-shimmer 1.4s ease-in-out
367 - var( --desktop-mode-skeleton-delay, 0s ) infinite;
556 + animation: os-my-wordpress-skeleton-shimmer 1.4s ease-in-out
557 + var( --os-skeleton-delay, 0s ) infinite;
368 558 }
369 559
370 -.desktop-mode-my-wordpress__skeleton-icon {
560 +.os-my-wordpress__skeleton-icon {
371 561 width: 44px;
372 562 height: 44px;
373 563 border-radius: 10px;
374 564 }
375 565
376 -.desktop-mode-my-wordpress__skeleton-label {
566 +.os-my-wordpress__skeleton-label {
377 567 width: 72%;
378 568 height: 10px;
379 569 border-radius: 4px;
380 570 }
381 571
382 -@keyframes desktop-mode-my-wordpress-skeleton-shimmer {
572 +@keyframes os-my-wordpress-skeleton-shimmer {
383 573 from {
384 574 background-position: 200% 0;
385 575 }
386 576 to {
@@ -388,10 +578,10 @@
388 578 }
389 579 }
390 580
391 581 @media ( prefers-reduced-motion: reduce ) {
392 - .desktop-mode-my-wordpress__skeleton-icon,
393 - .desktop-mode-my-wordpress__skeleton-label {
582 + .os-my-wordpress__skeleton-icon,
583 + .os-my-wordpress__skeleton-label {
394 584 animation: none;
395 585 }
396 586 }
397 587
@@ -396,27 +586,52 @@
396 586 }
397 587
398 588 /* ----- Right pane: preview ----- */
399 589
400 -.desktop-mode-my-wordpress__preview {
590 +.os-my-wordpress__preview {
401 591 overflow: auto;
402 592 min-width: 0;
403 593 background: transparent;
404 594 }
405 595
406 -.desktop-mode-my-wordpress__preview-empty {
596 +.os-my-wordpress__preview-empty {
407 597 display: flex;
598 + flex-direction: column;
599 + gap: 6px;
408 600 align-items: center;
409 601 justify-content: center;
410 602 height: 100%;
411 - color: var( --desktop-mode-muted, #787c82 );
603 + color: var( --os-ui-fg-muted, #787c82 );
412 604 font-size: 13px;
413 605 padding: 24px;
414 606 text-align: center;
415 607 }
416 608
417 -.desktop-mode-my-wordpress__preview-loading {
609 +/* Type / status breakdown under a multi-selection's count. */
610 +.os-my-wordpress__preview-selection-breakdown {
611 + font-size: 12px;
612 + opacity: 0.75;
613 +}
614 +
615 +/* One labelled control in the bulk-edit modal. Label above field,
616 + * because the fields are wide (a category tree, a tag row) and a
617 + * two-column grid would put a one-word label beside a control four
618 + * times its height. */
619 +.os-my-wordpress__bulk-row {
418 620 display: flex;
621 + flex-direction: column;
622 + gap: 6px;
623 + margin-block-end: 16px;
624 +}
625 +
626 +.os-my-wordpress__bulk-label {
627 + font-size: 12px;
628 + font-weight: 600;
629 + color: var( --os-ui-fg-muted, #787c82 );
630 +}
631 +
632 +.os-my-wordpress__preview-loading {
633 + display: flex;
419 634 align-items: center;
420 635 justify-content: center;
421 636 height: 100%;
422 637 min-height: 320px;
@@ -425,37 +640,37 @@
425 640 }
426 641
427 642 /* Preview-pane loader — same scale curve as the tile-grid first
428 643 * spinner and the window-loading overlay. The JS used to hard-code
429 - * `size="128"`, which set an inline `--wpd-spinner-size` that
644 + * `size="128"`, which set an inline `--os-ui-spinner-size` that
430 645 * overrode any CSS knob; the size attribute is now omitted so this
431 646 * rule is the single source of truth for both the My WordPress
432 647 * preview and the wallpaper file-preview pane (which reuses this
433 648 * class deliberately).
434 649 */
435 -.desktop-mode-my-wordpress__preview-loading wpd-spinner {
436 - --wpd-spinner-size: clamp( 96px, 14vw, 192px );
650 +.os-my-wordpress__preview-loading os-spinner {
651 + --os-ui-spinner-size: clamp( 96px, 14vw, 192px );
437 652 }
438 653
439 -.desktop-mode-my-wordpress__article {
654 +.os-my-wordpress__article {
440 655 padding: 24px 32px;
441 656 max-width: 720px;
442 657 margin: 0 auto;
443 658 }
444 659
445 -.desktop-mode-my-wordpress__article-title {
660 +.os-my-wordpress__article-title {
446 661 font-size: 22px;
447 662 margin: 0 0 8px;
448 663 line-height: 1.3;
449 664 }
450 665
451 -.desktop-mode-my-wordpress__article-meta {
666 +.os-my-wordpress__article-meta {
452 667 margin: 0 0 16px;
453 - color: var( --desktop-mode-muted, #50575e );
668 + color: var( --os-ui-fg-muted, #50575e );
454 669 font-size: 12px;
455 670 }
456 671
457 -.desktop-mode-my-wordpress__article-hero {
672 +.os-my-wordpress__article-hero {
458 673 width: 100%;
459 674 height: auto;
460 675 border-radius: 6px;
461 676 margin-bottom: 16px;
@@ -461,22 +676,22 @@
461 676 margin-bottom: 16px;
462 677 display: block;
463 678 }
464 679
465 -.desktop-mode-my-wordpress__article-content {
680 +.os-my-wordpress__article-content {
466 681 font-size: 14px;
467 682 line-height: 1.6;
468 683 }
469 684
470 -.desktop-mode-my-wordpress__article-content img {
685 +.os-my-wordpress__article-content img {
471 686 max-width: 100%;
472 687 height: auto;
473 688 }
474 689
475 -.desktop-mode-my-wordpress__article-footer {
690 +.os-my-wordpress__article-footer {
476 691 margin-top: 24px;
477 692 padding-top: 16px;
478 - border-top: 1px solid var( --desktop-mode-border, #dcdcde );
693 + border-top: 1px solid var( --os-ui-border, #dcdcde );
479 694 display: flex;
480 695 justify-content: flex-end;
481 696 align-items: center;
482 697 gap: 8px;
@@ -484,59 +699,97 @@
484 699 }
485 700
486 701 /* ----- Empty / error states ----- */
487 702
488 -.desktop-mode-my-wordpress__empty,
489 -.desktop-mode-my-wordpress__error {
703 +.os-my-wordpress__empty,
704 +.os-my-wordpress__error {
490 705 padding: 32px 24px;
491 706 text-align: center;
492 - color: var( --desktop-mode-muted, #787c82 );
707 + color: var( --os-ui-fg-muted, #787c82 );
493 708 font-size: 13px;
494 709 }
495 710
496 -.desktop-mode-my-wordpress__error {
497 - color: var( --desktop-mode-danger, #b32d2e );
711 +.os-my-wordpress__error {
712 + color: var( --os-danger, var( --os-ui-danger, #b32d2e ) );
498 713 }
499 714
500 -/* ----- Hover tooltip (floats over body) ----- */
715 +/* ----- Hover card (floats over body) -----
716 + *
717 + * The card is summoned from a tile and belongs to the window that
718 + * tile is in, so it follows the active desktop theme rather than the
719 + * shell's tooltip chip. Every colour chains
720 + * `--os-my-wordpress-card-*` first — derived in `variables.css` from
721 + * the window family, so a theme that repaints the window repaints the
722 + * card with it — then the chip tokens, then the pre-brand literal.
723 + *
724 + * It is appended to `document.body`, not to the window, because it is
725 + * `position: fixed` and a window with `overflow: hidden` would clip
726 + * it. Theme tokens still reach it: a desktop theme declares on
727 + * `body.os-desktop-theme-<slug>`, an ancestor of both. What does NOT
728 + * reach it is anything scoped to `.desktop-mode-my-wordpress`, which
729 + * is why the card family is declared at palette level. */
501 730
502 -.desktop-mode-my-wordpress__tooltip {
731 +.os-my-wordpress__tooltip {
503 732 position: fixed;
504 733 z-index: 100000;
505 734 max-width: 320px;
506 - background: var( --desktop-mode-surface, #fff );
507 - color: var( --desktop-mode-fg, #1d2327 );
508 - border: 1px solid var( --desktop-mode-border, #dcdcde );
735 + background: var(
736 + --os-my-wordpress-card-bg,
737 + var(
738 + --os-tooltip-bg,
739 + var( --os-surface, var( --os-ui-surface, #fff ) )
740 + )
741 + );
742 + color: var(
743 + --os-my-wordpress-card-fg,
744 + var( --os-tooltip-fg, var( --os-fg, #1d2327 ) )
745 + );
746 + /* The card lands on the same colour as the window under it — the
747 + * border and the shadow are the only reason it reads as a separate
748 + * object, so neither is decoration. */
749 + border: 1px solid var( --os-my-wordpress-card-border, #dcdcde );
509 750 border-radius: 6px;
510 751 padding: 12px;
511 - box-shadow: 0 6px 24px rgba( 0, 0, 0, 0.18 );
752 + box-shadow: var(
753 + --os-my-wordpress-card-shadow,
754 + 0 6px 24px rgba( 0, 0, 0, 0.18 )
755 + );
512 756 font-size: 12px;
513 757 pointer-events: none;
514 758 }
515 759
516 -.desktop-mode-my-wordpress__tooltip-title {
760 +.os-my-wordpress__tooltip-title {
517 761 font-weight: 600;
518 762 margin-bottom: 6px;
519 763 font-size: 13px;
520 764 }
521 765
522 -.desktop-mode-my-wordpress__tooltip-thumb {
766 +/* Same neutral well the entry and media tiles give their thumbnails:
767 + * a transparent PNG or a still-loading image would otherwise show the
768 + * card straight through, and a photo whose edge matches the card
769 + * would have no boundary. */
770 +.os-my-wordpress__tooltip-thumb {
523 771 display: block;
524 772 max-width: 100%;
525 773 height: auto;
526 774 border-radius: 4px;
527 775 margin: 6px 0;
776 + background: var( --os-my-wordpress-card-thumb-bg, rgba( 0, 0, 0, 0.05 ) );
777 + box-shadow: inset 0 0 0 1px var(
778 + --os-my-wordpress-card-border,
779 + #dcdcde
780 + );
528 781 }
529 782
530 -.desktop-mode-my-wordpress__tooltip-excerpt {
783 +.os-my-wordpress__tooltip-excerpt {
531 784 margin: 0;
532 - color: var( --desktop-mode-muted, #50575e );
785 + color: var( --os-my-wordpress-card-fg-muted, #50575e );
533 786 line-height: 1.4;
534 787 }
535 788
536 789 /* ----- Context menu host ----- */
537 790
538 -.desktop-mode-my-wordpress__menu {
791 +.os-my-wordpress__menu {
539 792 position: fixed;
540 793 z-index: 100001;
541 794 }
542 795
@@ -548,13 +801,13 @@
548 801 * but to stretch nicely up to ~720px before the line-length stops
549 802 * helping readability.
550 803 * --------------------------------------------------------------- */
551 804
552 -.desktop-mode-my-wordpress__user {
805 +.os-my-wordpress__user {
553 806 max-width: 720px;
554 807 }
555 808
556 -.desktop-mode-my-wordpress__user-header {
809 +.os-my-wordpress__user-header {
557 810 display: flex;
558 811 gap: 16px;
559 812 align-items: center;
560 813 margin-bottom: 16px;
@@ -559,9 +812,9 @@
559 812 align-items: center;
560 813 margin-bottom: 16px;
561 814 }
562 815
563 -.desktop-mode-my-wordpress__user-avatar {
816 +.os-my-wordpress__user-avatar {
564 817 width: 96px;
565 818 height: 96px;
566 819 border-radius: 50%;
567 820 flex-shrink: 0;
@@ -567,19 +820,19 @@
567 820 flex-shrink: 0;
568 821 box-shadow: 0 2px 8px rgba( 0, 0, 0, 0.12 );
569 822 }
570 823
571 -.desktop-mode-my-wordpress__user-headline {
824 +.os-my-wordpress__user-headline {
572 825 flex: 1 1 auto;
573 826 min-width: 0;
574 827 }
575 828
576 -.desktop-mode-my-wordpress__user
577 - .desktop-mode-my-wordpress__article-title {
829 +.os-my-wordpress__user
830 + .os-my-wordpress__article-title {
578 831 margin: 0 0 4px;
579 832 }
580 833
581 -.desktop-mode-my-wordpress__user-roles {
834 +.os-my-wordpress__user-roles {
582 835 display: flex;
583 836 flex-wrap: wrap;
584 837 gap: 6px;
585 838 margin-bottom: 6px;
@@ -584,9 +837,9 @@
584 837 gap: 6px;
585 838 margin-bottom: 6px;
586 839 }
587 840
588 -.desktop-mode-my-wordpress__user-role {
841 +.os-my-wordpress__user-role {
589 842 display: inline-flex;
590 843 align-items: center;
591 844 padding: 2px 8px;
592 845 border-radius: 10px;
@@ -593,39 +846,39 @@
593 846 font-size: 11px;
594 847 font-weight: 600;
595 848 text-transform: uppercase;
596 849 letter-spacing: 0.04em;
597 - background: rgba( 34, 113, 177, 0.12 );
850 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, transparent );
598 851 color: var( --wp-admin-theme-color, #2271b1 );
599 852 }
600 853
601 -.desktop-mode-my-wordpress__user-links {
854 +.os-my-wordpress__user-links {
602 855 display: flex;
603 856 gap: 12px;
604 857 font-size: 12px;
605 858 }
606 859
607 -.desktop-mode-my-wordpress__user-links a {
608 - color: var( --desktop-mode-link, #2271b1 );
860 +.os-my-wordpress__user-links a {
861 + color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
609 862 text-decoration: none;
610 863 }
611 864
612 -.desktop-mode-my-wordpress__user-links a:hover {
865 +.os-my-wordpress__user-links a:hover {
613 866 text-decoration: underline;
614 867 }
615 868
616 -.desktop-mode-my-wordpress__user-bio {
869 +.os-my-wordpress__user-bio {
617 870 font-size: 14px;
618 871 line-height: 1.5;
619 - color: var( --desktop-mode-fg, #1d2327 );
872 + color: var( --os-fg, #1d2327 );
620 873 margin: 0 0 16px;
621 874 padding-left: 12px;
622 - border-left: 3px solid var( --desktop-mode-border, #dcdcde );
875 + border-left: 3px solid var( --os-ui-border, #dcdcde );
623 876 }
624 877
625 878 /* ----- Stat cards row ----- */
626 879
627 -.desktop-mode-my-wordpress__user-stats {
880 +.os-my-wordpress__user-stats {
628 881 display: grid;
629 882 grid-template-columns: repeat( auto-fit, minmax( 120px, 1fr ) );
630 883 gap: 8px;
631 884 margin-bottom: 20px;
@@ -630,19 +883,19 @@
630 883 gap: 8px;
631 884 margin-bottom: 20px;
632 885 }
633 886
634 -.desktop-mode-my-wordpress__user-stat {
887 +.os-my-wordpress__user-stat {
635 888 display: flex;
636 889 flex-direction: column;
637 890 gap: 2px;
638 891 padding: 12px 14px;
639 892 border-radius: 8px;
640 - background: var( --desktop-mode-hover, rgba( 0, 0, 0, 0.04 ) );
641 - border: 1px solid var( --desktop-mode-border, #dcdcde );
893 + background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ) );
894 + border: 1px solid var( --os-ui-border, #dcdcde );
642 895 }
643 896
644 -.desktop-mode-my-wordpress__user-stat-value {
897 +.os-my-wordpress__user-stat-value {
645 898 font-size: 26px;
646 899 font-weight: 700;
647 900 line-height: 1.1;
648 901 color: var( --wp-admin-theme-color, #2271b1 );
@@ -648,36 +901,36 @@
648 901 color: var( --wp-admin-theme-color, #2271b1 );
649 902 font-variant-numeric: tabular-nums;
650 903 }
651 904
652 -.desktop-mode-my-wordpress__user-stat-label {
905 +.os-my-wordpress__user-stat-label {
653 906 font-size: 12px;
654 - color: var( --desktop-mode-muted, #50575e );
907 + color: var( --os-ui-fg-muted, #50575e );
655 908 text-transform: uppercase;
656 909 letter-spacing: 0.04em;
657 910 }
658 911
659 -.desktop-mode-my-wordpress__user-stat-caption {
912 +.os-my-wordpress__user-stat-caption {
660 913 font-size: 11px;
661 - color: var( --desktop-mode-muted, #787c82 );
914 + color: var( --os-ui-fg-muted, #787c82 );
662 915 }
663 916
664 917 /* ----- 12-month activity sparkline ----- */
665 918
666 -.desktop-mode-my-wordpress__user-section {
919 +.os-my-wordpress__user-section {
667 920 margin-bottom: 20px;
668 921 }
669 922
670 -.desktop-mode-my-wordpress__user-section h3 {
923 +.os-my-wordpress__user-section h3 {
671 924 font-size: 13px;
672 925 font-weight: 600;
673 - color: var( --desktop-mode-muted, #50575e );
926 + color: var( --os-ui-fg-muted, #50575e );
674 927 text-transform: uppercase;
675 928 letter-spacing: 0.04em;
676 929 margin: 0 0 8px;
677 930 }
678 931
679 -.desktop-mode-my-wordpress__user-spark-chart {
932 +.os-my-wordpress__user-spark-chart {
680 933 display: grid;
681 934 grid-template-columns: repeat( 12, 1fr );
682 935 gap: 4px;
683 936 height: 80px;
@@ -684,9 +937,9 @@
684 937 align-items: end;
685 938 padding: 6px 0;
686 939 }
687 940
688 -.desktop-mode-my-wordpress__user-spark-col {
941 +.os-my-wordpress__user-spark-col {
689 942 display: flex;
690 943 flex-direction: column;
691 944 align-items: stretch;
692 945 justify-content: flex-end;
@@ -694,13 +947,13 @@
694 947 height: 100%;
695 948 min-height: 0;
696 949 }
697 950
698 -.desktop-mode-my-wordpress__user-spark-bar {
951 +.os-my-wordpress__user-spark-bar {
699 952 background: linear-gradient(
700 953 180deg,
701 954 var( --wp-admin-theme-color, #2271b1 ) 0%,
702 - rgba( 34, 113, 177, 0.55 ) 100%
955 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 55%, transparent ) 100%
703 956 );
704 957 border-radius: 3px 3px 0 0;
705 958 min-height: 2px;
706 959 transition: height 0.3s ease;
@@ -705,23 +958,23 @@
705 958 min-height: 2px;
706 959 transition: height 0.3s ease;
707 960 }
708 961
709 -.desktop-mode-my-wordpress__user-spark-bar--empty {
710 - background: var( --desktop-mode-border, #dcdcde );
962 +.os-my-wordpress__user-spark-bar--empty {
963 + background: var( --os-ui-border, #dcdcde );
711 964 min-height: 2px;
712 965 height: 2px !important;
713 966 }
714 967
715 -.desktop-mode-my-wordpress__user-spark-label {
968 +.os-my-wordpress__user-spark-label {
716 969 font-size: 10px;
717 970 text-align: center;
718 - color: var( --desktop-mode-muted, #787c82 );
971 + color: var( --os-ui-fg-muted, #787c82 );
719 972 }
720 973
721 974 /* ----- Milestones ----- */
722 975
723 -.desktop-mode-my-wordpress__user-milestones {
976 +.os-my-wordpress__user-milestones {
724 977 display: grid;
725 978 grid-template-columns: repeat( auto-fit, minmax( 140px, 1fr ) );
726 979 gap: 12px;
727 980 margin: 0 0 20px;
@@ -726,17 +979,17 @@
726 979 gap: 12px;
727 980 margin: 0 0 20px;
728 981 }
729 982
730 -.desktop-mode-my-wordpress__user-milestones dt {
983 +.os-my-wordpress__user-milestones dt {
731 984 font-size: 11px;
732 - color: var( --desktop-mode-muted, #50575e );
985 + color: var( --os-ui-fg-muted, #50575e );
733 986 text-transform: uppercase;
734 987 letter-spacing: 0.04em;
735 988 margin-bottom: 2px;
736 989 }
737 990
738 -.desktop-mode-my-wordpress__user-milestones dd {
991 +.os-my-wordpress__user-milestones dd {
739 992 font-size: 14px;
740 993 font-weight: 600;
741 994 margin: 0;
742 995 }
@@ -742,9 +995,9 @@
742 995 }
743 996
744 997 /* ----- Recent posts list ----- */
745 998
746 -.desktop-mode-my-wordpress__user-recent {
999 +.os-my-wordpress__user-recent {
747 1000 list-style: none;
748 1001 margin: 0;
749 1002 padding: 0;
750 1003 display: flex;
@@ -751,41 +1004,41 @@
751 1004 flex-direction: column;
752 1005 gap: 6px;
753 1006 }
754 1007
755 -.desktop-mode-my-wordpress__user-recent li {
1008 +.os-my-wordpress__user-recent li {
756 1009 display: flex;
757 1010 flex-direction: column;
758 1011 gap: 2px;
759 1012 padding: 8px 10px;
760 1013 border-radius: 6px;
761 - background: rgba( 0, 0, 0, 0.02 );
1014 + background: var( --os-ui-hover, rgba( 0, 0, 0, 0.02 ) );
762 1015 }
763 1016
764 -.desktop-mode-my-wordpress__user-recent a {
1017 +.os-my-wordpress__user-recent a {
765 1018 font-weight: 600;
766 - color: var( --desktop-mode-link, #2271b1 );
1019 + color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
767 1020 text-decoration: none;
768 1021 }
769 1022
770 -.desktop-mode-my-wordpress__user-recent a:hover {
1023 +.os-my-wordpress__user-recent a:hover {
771 1024 text-decoration: underline;
772 1025 }
773 1026
774 -.desktop-mode-my-wordpress__user-recent-meta {
1027 +.os-my-wordpress__user-recent-meta {
775 1028 font-size: 11px;
776 - color: var( --desktop-mode-muted, #787c82 );
1029 + color: var( --os-ui-fg-muted, #787c82 );
777 1030 }
778 1031
779 1032 /* ----- Top categories chips ----- */
780 1033
781 -.desktop-mode-my-wordpress__user-chips {
1034 +.os-my-wordpress__user-chips {
782 1035 display: flex;
783 1036 flex-wrap: wrap;
784 1037 gap: 6px;
785 1038 }
786 1039
787 -.desktop-mode-my-wordpress__user-chip {
1040 +.os-my-wordpress__user-chip {
788 1041 display: inline-flex;
789 1042 align-items: center;
790 1043 gap: 6px;
791 1044 padding: 4px 10px;
@@ -790,18 +1043,18 @@
790 1043 gap: 6px;
791 1044 padding: 4px 10px;
792 1045 border-radius: 999px;
793 1046 font-size: 12px;
794 - background: rgba( 0, 0, 0, 0.05 );
795 - border: 1px solid var( --desktop-mode-border, #dcdcde );
1047 + background: var( --os-ui-hover, rgba( 0, 0, 0, 0.05 ) );
1048 + border: 1px solid var( --os-ui-border, #dcdcde );
796 1049 }
797 1050
798 -.desktop-mode-my-wordpress__user-chip--tag {
1051 +.os-my-wordpress__user-chip--tag {
799 1052 background: rgba( 124, 58, 237, 0.08 );
800 1053 border-color: rgba( 124, 58, 237, 0.25 );
801 1054 }
802 1055
803 -.desktop-mode-my-wordpress__user-chip-count {
1056 +.os-my-wordpress__user-chip-count {
804 1057 font-weight: 700;
805 1058 color: var( --wp-admin-theme-color, #2271b1 );
806 1059 font-variant-numeric: tabular-nums;
807 1060 }
@@ -814,9 +1067,9 @@
814 1067 * "Top contributors" grid that pairs an author avatar with a
815 1068 * post count.
816 1069 * --------------------------------------------------------------- */
817 1070
818 -.desktop-mode-my-wordpress__term-header {
1071 +.os-my-wordpress__term-header {
819 1072 display: flex;
820 1073 align-items: center;
821 1074 gap: 16px;
822 1075 margin-bottom: 16px;
@@ -821,9 +1074,9 @@
821 1074 gap: 16px;
822 1075 margin-bottom: 16px;
823 1076 }
824 1077
825 -.desktop-mode-my-wordpress__term-icon {
1078 +.os-my-wordpress__term-icon {
826 1079 width: 64px;
827 1080 height: 64px;
828 1081 border-radius: 12px;
829 1082 display: inline-flex;
@@ -829,49 +1082,49 @@
829 1082 display: inline-flex;
830 1083 align-items: center;
831 1084 justify-content: center;
832 1085 flex-shrink: 0;
833 - color: #fff;
1086 + color: var( --os-ui-fg-on-accent, #fff );
834 1087 }
835 1088
836 -.desktop-mode-my-wordpress__term-icon--category {
837 - background: linear-gradient( 135deg, #2271b1 0%, #135e96 100% );
838 - box-shadow: 0 4px 12px rgba( 34, 113, 177, 0.32 );
1089 +.os-my-wordpress__term-icon--category {
1090 + background: linear-gradient( 135deg, var( --os-ui-accent, #2271b1 ) 0%, var( --os-ui-accent-strong, #135e96 ) 100% );
1091 + box-shadow: 0 4px 12px color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 32%, transparent );
839 1092 }
840 1093
841 -.desktop-mode-my-wordpress__term-icon--tag {
1094 +.os-my-wordpress__term-icon--tag {
842 1095 background: linear-gradient( 135deg, #7c3aed 0%, #5b21b6 100% );
843 1096 box-shadow: 0 4px 12px rgba( 124, 58, 237, 0.32 );
844 1097 }
845 1098
846 -.desktop-mode-my-wordpress__user-role--category {
847 - background: rgba( 34, 113, 177, 0.12 );
848 - color: #135e96;
1099 +.os-my-wordpress__user-role--category {
1100 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, transparent );
1101 + color: var( --os-ui-accent-strong, #135e96 );
849 1102 }
850 1103
851 -.desktop-mode-my-wordpress__user-role--tag {
1104 +.os-my-wordpress__user-role--tag {
852 1105 background: rgba( 124, 58, 237, 0.12 );
853 1106 color: #6d28d9;
854 1107 }
855 1108
856 1109 /* Top contributors row — paired avatar + name + post count. */
857 -.desktop-mode-my-wordpress__term-authors {
1110 +.os-my-wordpress__term-authors {
858 1111 display: grid;
859 1112 grid-template-columns: repeat( auto-fit, minmax( 180px, 1fr ) );
860 1113 gap: 8px;
861 1114 }
862 1115
863 -.desktop-mode-my-wordpress__term-author {
1116 +.os-my-wordpress__term-author {
864 1117 display: flex;
865 1118 align-items: center;
866 1119 gap: 10px;
867 1120 padding: 8px 12px;
868 1121 border-radius: 8px;
869 - background: rgba( 0, 0, 0, 0.03 );
870 - border: 1px solid var( --desktop-mode-border, #dcdcde );
1122 + background: var( --os-ui-hover, rgba( 0, 0, 0, 0.03 ) );
1123 + border: 1px solid var( --os-ui-border, #dcdcde );
871 1124 }
872 1125
873 -.desktop-mode-my-wordpress__term-author-avatar {
1126 +.os-my-wordpress__term-author-avatar {
874 1127 width: 36px;
875 1128 height: 36px;
876 1129 border-radius: 50%;
877 1130 flex-shrink: 0;
@@ -876,9 +1129,9 @@
876 1129 border-radius: 50%;
877 1130 flex-shrink: 0;
878 1131 }
879 1132
880 -.desktop-mode-my-wordpress__term-author-text {
1133 +.os-my-wordpress__term-author-text {
881 1134 display: flex;
882 1135 flex-direction: column;
883 1136 gap: 2px;
884 1137 min-width: 0;
@@ -883,9 +1136,9 @@
883 1136 gap: 2px;
884 1137 min-width: 0;
885 1138 }
886 1139
887 -.desktop-mode-my-wordpress__term-author-name {
1140 +.os-my-wordpress__term-author-name {
888 1141 font-weight: 600;
889 1142 font-size: 13px;
890 1143 overflow: hidden;
891 1144 text-overflow: ellipsis;
@@ -891,11 +1144,11 @@
891 1144 text-overflow: ellipsis;
892 1145 white-space: nowrap;
893 1146 }
894 1147
895 -.desktop-mode-my-wordpress__term-author-count {
1148 +.os-my-wordpress__term-author-count {
896 1149 font-size: 11px;
897 - color: var( --desktop-mode-muted, #50575e );
1150 + color: var( --os-ui-fg-muted, #50575e );
898 1151 }
899 1152
900 1153 /* ---------------------------------------------------------------
901 1154 * Comment dossier — header (avatar + name + status / date / count
@@ -904,32 +1157,32 @@
904 1157 * direct-reply thread, and a moderation strip (IP + user agent)
905 1158 * for users with moderate_comments.
906 1159 * --------------------------------------------------------------- */
907 1160
908 -.desktop-mode-my-wordpress__comment-status--approved {
909 - background: rgba( 34, 113, 177, 0.12 );
910 - color: #135e96;
1161 +.os-my-wordpress__comment-status--approved {
1162 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, transparent );
1163 + color: var( --os-ui-accent-strong, #135e96 );
911 1164 }
912 1165
913 -.desktop-mode-my-wordpress__comment-status--pending {
1166 +.os-my-wordpress__comment-status--pending {
914 1167 background: rgba( 217, 119, 6, 0.16 );
915 1168 color: #b45309;
916 1169 }
917 1170
918 -.desktop-mode-my-wordpress__comment-status--spam {
1171 +.os-my-wordpress__comment-status--spam {
919 1172 background: rgba( 179, 45, 46, 0.14 );
920 - color: #b32d2e;
1173 + color: var( --os-ui-danger-hover, #b32d2e );
921 1174 }
922 1175
923 -.desktop-mode-my-wordpress__comment-status--trash {
924 - background: rgba( 0, 0, 0, 0.08 );
925 - color: var( --desktop-mode-muted, #50575e );
1176 +.os-my-wordpress__comment-status--trash {
1177 + background: var( --os-ui-hover, rgba( 0, 0, 0, 0.08 ) );
1178 + color: var( --os-ui-fg-muted, #50575e );
926 1179 }
927 1180
928 -.desktop-mode-my-wordpress__comment-date-badge {
1181 +.os-my-wordpress__comment-date-badge {
929 1182 background: transparent;
930 - color: var( --desktop-mode-muted, #50575e );
931 - border: 1px solid var( --desktop-mode-border, #dcdcde );
1183 + color: var( --os-ui-fg-muted, #50575e );
1184 + border: 1px solid var( --os-ui-border, #dcdcde );
932 1185 text-transform: none;
933 1186 letter-spacing: normal;
934 1187 }
935 1188
@@ -934,68 +1187,68 @@
934 1187 }
935 1188
936 1189 /* "In reply to ..." quote shown above the comment body when this
937 1190 * is a thread reply. */
938 -.desktop-mode-my-wordpress__comment-quote {
1191 +.os-my-wordpress__comment-quote {
939 1192 margin: 0 0 16px;
940 1193 padding: 10px 14px;
941 1194 border-left: 3px solid var( --wp-admin-theme-color, #2271b1 );
942 - background: rgba( 34, 113, 177, 0.06 );
1195 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 6%, transparent );
943 1196 border-radius: 0 6px 6px 0;
944 1197 font-size: 13px;
945 1198 }
946 1199
947 -.desktop-mode-my-wordpress__comment-quote-lead {
1200 +.os-my-wordpress__comment-quote-lead {
948 1201 font-size: 11px;
949 1202 font-weight: 600;
950 - color: var( --desktop-mode-muted, #50575e );
1203 + color: var( --os-ui-fg-muted, #50575e );
951 1204 text-transform: uppercase;
952 1205 letter-spacing: 0.04em;
953 1206 margin-bottom: 4px;
954 1207 }
955 1208
956 -.desktop-mode-my-wordpress__comment-quote p {
1209 +.os-my-wordpress__comment-quote p {
957 1210 margin: 0;
958 - color: var( --desktop-mode-fg, #1d2327 );
1211 + color: var( --os-fg, #1d2327 );
959 1212 }
960 1213
961 1214 /* Comment body — same style as article-content but with a soft
962 1215 * card background so it reads as the focal element. */
963 -.desktop-mode-my-wordpress__comment-body {
1216 +.os-my-wordpress__comment-body {
964 1217 padding: 14px 16px;
965 - background: rgba( 0, 0, 0, 0.03 );
1218 + background: var( --os-ui-hover, rgba( 0, 0, 0, 0.03 ) );
966 1219 border-radius: 8px;
967 - border: 1px solid var( --desktop-mode-border, #dcdcde );
1220 + border: 1px solid var( --os-ui-border, #dcdcde );
968 1221 }
969 1222
970 1223 /* Parent-post card under the comment body. */
971 -.desktop-mode-my-wordpress__comment-post {
1224 +.os-my-wordpress__comment-post {
972 1225 display: flex;
973 1226 flex-direction: column;
974 1227 gap: 4px;
975 1228 padding: 10px 14px;
976 1229 border-radius: 8px;
977 - background: rgba( 0, 0, 0, 0.04 );
978 - border: 1px solid var( --desktop-mode-border, #dcdcde );
1230 + background: var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) );
1231 + border: 1px solid var( --os-ui-border, #dcdcde );
979 1232 }
980 1233
981 -.desktop-mode-my-wordpress__comment-post-title {
1234 +.os-my-wordpress__comment-post-title {
982 1235 font-weight: 600;
983 - color: var( --desktop-mode-link, #2271b1 );
1236 + color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
984 1237 text-decoration: none;
985 1238 }
986 1239
987 -.desktop-mode-my-wordpress__comment-post-title:hover {
1240 +.os-my-wordpress__comment-post-title:hover {
988 1241 text-decoration: underline;
989 1242 }
990 1243
991 -.desktop-mode-my-wordpress__comment-post-meta {
1244 +.os-my-wordpress__comment-post-meta {
992 1245 font-size: 11px;
993 - color: var( --desktop-mode-muted, #787c82 );
1246 + color: var( --os-ui-fg-muted, #787c82 );
994 1247 }
995 1248
996 1249 /* Replies list. */
997 -.desktop-mode-my-wordpress__comment-replies {
1250 +.os-my-wordpress__comment-replies {
998 1251 list-style: none;
999 1252 margin: 0;
1000 1253 padding: 0;
1001 1254 display: flex;
@@ -1002,19 +1255,19 @@
1002 1255 flex-direction: column;
1003 1256 gap: 10px;
1004 1257 }
1005 1258
1006 -.desktop-mode-my-wordpress__comment-reply {
1259 +.os-my-wordpress__comment-reply {
1007 1260 display: flex;
1008 1261 gap: 10px;
1009 1262 align-items: flex-start;
1010 1263 padding: 10px 12px;
1011 1264 border-radius: 8px;
1012 - background: rgba( 0, 0, 0, 0.02 );
1013 - border: 1px solid var( --desktop-mode-border, #dcdcde );
1265 + background: var( --os-ui-hover, rgba( 0, 0, 0, 0.02 ) );
1266 + border: 1px solid var( --os-ui-border, #dcdcde );
1014 1267 }
1015 1268
1016 -.desktop-mode-my-wordpress__comment-reply-avatar {
1269 +.os-my-wordpress__comment-reply-avatar {
1017 1270 width: 32px;
1018 1271 height: 32px;
1019 1272 border-radius: 50%;
1020 1273 flex-shrink: 0;
@@ -1019,9 +1272,9 @@
1019 1272 border-radius: 50%;
1020 1273 flex-shrink: 0;
1021 1274 }
1022 1275
1023 -.desktop-mode-my-wordpress__comment-reply-text {
1276 +.os-my-wordpress__comment-reply-text {
1024 1277 display: flex;
1025 1278 flex-direction: column;
1026 1279 gap: 2px;
1027 1280 min-width: 0;
@@ -1026,9 +1279,9 @@
1026 1279 gap: 2px;
1027 1280 min-width: 0;
1028 1281 }
1029 1282
1030 -.desktop-mode-my-wordpress__comment-reply-head {
1283 +.os-my-wordpress__comment-reply-head {
1031 1284 display: flex;
1032 1285 align-items: baseline;
1033 1286 gap: 8px;
1034 1287 flex-wrap: wrap;
@@ -1033,22 +1286,22 @@
1033 1286 gap: 8px;
1034 1287 flex-wrap: wrap;
1035 1288 }
1036 1289
1037 -.desktop-mode-my-wordpress__comment-reply-name {
1290 +.os-my-wordpress__comment-reply-name {
1038 1291 font-weight: 600;
1039 1292 font-size: 13px;
1040 1293 }
1041 1294
1042 -.desktop-mode-my-wordpress__comment-reply-when {
1295 +.os-my-wordpress__comment-reply-when {
1043 1296 font-size: 11px;
1044 - color: var( --desktop-mode-muted, #787c82 );
1297 + color: var( --os-ui-fg-muted, #787c82 );
1045 1298 }
1046 1299
1047 -.desktop-mode-my-wordpress__comment-reply-excerpt {
1300 +.os-my-wordpress__comment-reply-excerpt {
1048 1301 margin: 0;
1049 1302 font-size: 13px;
1050 - color: var( --desktop-mode-fg, #1d2327 );
1303 + color: var( --os-fg, #1d2327 );
1051 1304 line-height: 1.45;
1052 1305 }
1053 1306
1054 1307 /* =================================================================== *
@@ -1063,9 +1316,9 @@
1063 1316 * Since 0.20.0.
1064 1317 * =================================================================== */
1065 1318
1066 1319 /* User tile — replaces the dashicon glyph with a real avatar */
1067 -.desktop-mode-my-wordpress__tile--user {
1320 +.os-my-wordpress__tile--user {
1068 1321 display: flex;
1069 1322 flex-direction: column;
1070 1323 align-items: center;
1071 1324 justify-content: flex-start;
@@ -1073,9 +1326,9 @@
1073 1326 padding: 8px 4px 6px;
1074 1327 box-sizing: border-box;
1075 1328 }
1076 1329
1077 -.desktop-mode-my-wordpress__user-tile-avatar {
1330 +.os-my-wordpress__user-tile-avatar {
1078 1331 display: inline-flex;
1079 1332 align-items: center;
1080 1333 justify-content: center;
1081 1334 width: 44px;
@@ -1083,10 +1336,10 @@
1083 1336 border-radius: 50%;
1084 1337 overflow: hidden;
1085 1338 background: linear-gradient(
1086 1339 135deg,
1087 - rgba( 34, 113, 177, 0.18 ),
1088 - rgba( 34, 113, 177, 0.04 )
1340 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 18%, transparent ),
1341 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 4%, transparent )
1089 1342 );
1090 1343 box-shadow:
1091 1344 inset 0 0 0 1px rgba( 0, 0, 0, 0.08 ),
1092 1345 0 2px 6px rgba( 0, 0, 0, 0.08 );
@@ -1092,9 +1345,9 @@
1092 1345 0 2px 6px rgba( 0, 0, 0, 0.08 );
1093 1346 flex: 0 0 auto;
1094 1347 }
1095 1348
1096 -.desktop-mode-my-wordpress__user-tile-avatar img {
1349 +.os-my-wordpress__user-tile-avatar img {
1097 1350 width: 100%;
1098 1351 height: 100%;
1099 1352 object-fit: cover;
1100 1353 display: block;
@@ -1099,9 +1352,9 @@
1099 1352 object-fit: cover;
1100 1353 display: block;
1101 1354 }
1102 1355
1103 -.desktop-mode-my-wordpress__user-tile-initials {
1356 +.os-my-wordpress__user-tile-initials {
1104 1357 font-weight: 600;
1105 1358 font-size: 14px;
1106 1359 letter-spacing: 0.02em;
1107 1360 color: var( --wp-admin-theme-color, #2271b1 );
@@ -1110,9 +1363,9 @@
1110 1363
1111 1364 /* Single-line label for user tiles — we already have a generic
1112 1365 * label clamp, but the user variant gets only ONE line so the
1113 1366 * sub-line ("Editor · 12 posts") can fit beneath it. */
1114 -.desktop-mode-my-wordpress__tile--user .desktop-mode-file-tile__label {
1367 +.os-my-wordpress__tile--user .os-file-tile__label {
1115 1368 font-size: 11px;
1116 1369 font-weight: 500;
1117 1370 line-height: 1.25;
1118 1371 max-height: 1.25em;
@@ -1123,12 +1376,12 @@
1123 1376 padding: 0 4px;
1124 1377 box-sizing: border-box;
1125 1378 }
1126 1379
1127 -.desktop-mode-my-wordpress__user-tile-sub {
1380 +.os-my-wordpress__user-tile-sub {
1128 1381 font-size: 10px;
1129 1382 line-height: 1.2;
1130 - color: var( --desktop-mode-tile-fg-muted, rgba( 0, 0, 0, 0.55 ) );
1383 + color: var( --os-tile-fg-muted, rgba( 0, 0, 0, 0.55 ) );
1131 1384 max-height: 1.2em;
1132 1385 white-space: nowrap;
1133 1386 overflow: hidden;
1134 1387 text-overflow: ellipsis;
@@ -1138,9 +1391,9 @@
1138 1391 box-sizing: border-box;
1139 1392 }
1140 1393
1141 1394 /* Selected/hover user tiles inherit the same chrome the post tiles
1142 - * use — see `.desktop-mode-my-wordpress__tile--selected` above. */
1395 + * use — see `.os-my-wordpress__tile--selected` above. */
1143 1396
1144 1397 /* ----- Activity footprint — full-width body surface -----
1145 1398 *
1146 1399 * Opens via the right-click context menu on a user tile (or the
@@ -1148,9 +1401,9 @@
1148 1401 * list/preview with a wide canvas so each section gets enough room
1149 1402 * to read.
1150 1403 */
1151 1404
1152 -.desktop-mode-my-wordpress__footprint {
1405 +.os-my-wordpress__footprint {
1153 1406 display: flex;
1154 1407 flex-direction: column;
1155 1408 gap: 20px;
1156 1409 padding: 24px 28px 32px;
@@ -1163,20 +1416,20 @@
1163 1416 * cap is applied to each immediate child instead (below). */
1164 1417 align-items: center;
1165 1418 }
1166 1419
1167 -.desktop-mode-my-wordpress__footprint > * {
1420 +.os-my-wordpress__footprint > * {
1168 1421 width: 100%;
1169 1422 max-width: 1080px;
1170 1423 }
1171 1424
1172 -.desktop-mode-my-wordpress__footprint-section {
1173 - border: 1px solid var( --desktop-mode-border, #dcdcde );
1425 +.os-my-wordpress__footprint-section {
1426 + border: 1px solid var( --os-ui-border, #dcdcde );
1174 1427 border-radius: 12px;
1175 1428 padding: 18px 20px;
1176 1429 background: var(
1177 - --desktop-mode-my-wordpress-surface,
1178 - var( --desktop-mode-surface, rgba( 255, 255, 255, 0.6 ) )
1430 + --os-my-wordpress-surface,
1431 + var( --os-surface, var( --os-ui-surface, rgba( 255, 255, 255, 0.6 ) ) )
1179 1432 );
1180 1433 box-shadow: 0 1px 2px rgba( 0, 0, 0, 0.04 );
1181 1434 /* The parent `.__footprint` is a flex column with `overflow-y:
1182 1435 * auto`. Flex items default to `flex-shrink: 1`, so each section
@@ -1188,20 +1441,20 @@
1188 1441 * overflow. */
1189 1442 flex: 0 0 auto;
1190 1443 }
1191 1444
1192 -.desktop-mode-my-wordpress__footprint-section h3 {
1445 +.os-my-wordpress__footprint-section h3 {
1193 1446 margin: 0 0 12px;
1194 1447 font-size: 13px;
1195 1448 font-weight: 600;
1196 1449 letter-spacing: 0.04em;
1197 1450 text-transform: uppercase;
1198 - color: var( --desktop-mode-muted, #50575e );
1451 + color: var( --os-ui-fg-muted, #50575e );
1199 1452 }
1200 1453
1201 1454 /* ----- Hero ----- */
1202 1455
1203 -.desktop-mode-my-wordpress__footprint-hero {
1456 +.os-my-wordpress__footprint-hero {
1204 1457 display: flex;
1205 1458 gap: 20px;
1206 1459 align-items: center;
1207 1460 padding: 24px;
@@ -1207,16 +1460,16 @@
1207 1460 padding: 24px;
1208 1461 border-radius: 16px;
1209 1462 background: linear-gradient(
1210 1463 135deg,
1211 - rgba( 34, 113, 177, 0.10 ) 0%,
1212 - rgba( 34, 113, 177, 0.02 ) 60%,
1464 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 10%, transparent ) 0%,
1465 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 2%, transparent ) 60%,
1213 1466 transparent 100%
1214 1467 );
1215 - border: 1px solid var( --desktop-mode-border, #dcdcde );
1468 + border: 1px solid var( --os-ui-border, #dcdcde );
1216 1469 }
1217 1470
1218 -.desktop-mode-my-wordpress__footprint-avatar {
1471 +.os-my-wordpress__footprint-avatar {
1219 1472 width: 112px;
1220 1473 height: 112px;
1221 1474 border-radius: 50%;
1222 1475 overflow: hidden;
@@ -1225,10 +1478,10 @@
1225 1478 inset 0 0 0 2px rgba( 255, 255, 255, 0.8 ),
1226 1479 0 6px 24px rgba( 0, 0, 0, 0.14 );
1227 1480 background: linear-gradient(
1228 1481 135deg,
1229 - rgba( 34, 113, 177, 0.18 ),
1230 - rgba( 34, 113, 177, 0.04 )
1482 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 18%, transparent ),
1483 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 4%, transparent )
1231 1484 );
1232 1485 display: inline-flex;
1233 1486 align-items: center;
1234 1487 justify-content: center;
@@ -1233,9 +1486,9 @@
1233 1486 align-items: center;
1234 1487 justify-content: center;
1235 1488 }
1236 1489
1237 -.desktop-mode-my-wordpress__footprint-avatar img {
1490 +.os-my-wordpress__footprint-avatar img {
1238 1491 width: 100%;
1239 1492 height: 100%;
1240 1493 object-fit: cover;
1241 1494 display: block;
@@ -1240,14 +1493,14 @@
1240 1493 object-fit: cover;
1241 1494 display: block;
1242 1495 }
1243 1496
1244 -.desktop-mode-my-wordpress__footprint-avatar
1245 - .desktop-mode-my-wordpress__user-tile-initials {
1497 +.os-my-wordpress__footprint-avatar
1498 + .os-my-wordpress__user-tile-initials {
1246 1499 font-size: 36px;
1247 1500 }
1248 1501
1249 -.desktop-mode-my-wordpress__footprint-headline {
1502 +.os-my-wordpress__footprint-headline {
1250 1503 min-width: 0;
1251 1504 flex: 1 1 auto;
1252 1505 display: flex;
1253 1506 flex-direction: column;
@@ -1253,17 +1506,17 @@
1253 1506 flex-direction: column;
1254 1507 gap: 8px;
1255 1508 }
1256 1509
1257 -.desktop-mode-my-wordpress__footprint-title {
1510 +.os-my-wordpress__footprint-title {
1258 1511 margin: 0;
1259 1512 font-size: 28px;
1260 1513 line-height: 1.15;
1261 1514 font-weight: 700;
1262 - color: var( --desktop-mode-fg, #1d2327 );
1515 + color: var( --os-fg, #1d2327 );
1263 1516 }
1264 1517
1265 -.desktop-mode-my-wordpress__footprint-meta {
1518 +.os-my-wordpress__footprint-meta {
1266 1519 display: flex;
1267 1520 flex-wrap: wrap;
1268 1521 gap: 6px;
1269 1522 align-items: center;
@@ -1268,16 +1521,16 @@
1268 1521 gap: 6px;
1269 1522 align-items: center;
1270 1523 }
1271 1524
1272 -.desktop-mode-my-wordpress__footprint-since {
1273 - background: rgba( 0, 0, 0, 0.06 );
1274 - color: var( --desktop-mode-muted, #50575e );
1525 +.os-my-wordpress__footprint-since {
1526 + background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) );
1527 + color: var( --os-ui-fg-muted, #50575e );
1275 1528 }
1276 1529
1277 1530 /* ----- Headline stats row (4 cards) ----- */
1278 1531
1279 -.desktop-mode-my-wordpress__footprint-stats-row {
1532 +.os-my-wordpress__footprint-stats-row {
1280 1533 display: grid;
1281 1534 grid-template-columns: repeat( auto-fit, minmax( 160px, 1fr ) );
1282 1535 gap: 12px;
1283 1536 padding: 16px 18px;
@@ -1282,21 +1535,21 @@
1282 1535 gap: 12px;
1283 1536 padding: 16px 18px;
1284 1537 }
1285 1538
1286 -.desktop-mode-my-wordpress__footprint-stats-row
1287 - .desktop-mode-my-wordpress__user-stat {
1288 - background: var( --desktop-mode-surface, rgba( 255, 255, 255, 0.9 ) );
1539 +.os-my-wordpress__footprint-stats-row
1540 + .os-my-wordpress__user-stat {
1541 + background: var( --os-surface, var( --os-ui-surface, rgba( 255, 255, 255, 0.9 ) ) );
1289 1542 box-shadow: 0 1px 4px rgba( 0, 0, 0, 0.04 );
1290 1543 }
1291 1544
1292 1545 /* ----- Calendar heatmap (GitHub-style) ----- */
1293 1546
1294 -.desktop-mode-my-wordpress__footprint-calendar-section {
1547 +.os-my-wordpress__footprint-calendar-section {
1295 1548 overflow-x: auto;
1296 1549 }
1297 1550
1298 -.desktop-mode-my-wordpress__footprint-calendar {
1551 +.os-my-wordpress__footprint-calendar {
1299 1552 display: flex;
1300 1553 flex-direction: column;
1301 1554 gap: 8px;
1302 1555 /* Size to the grid itself so the legend (justify-content: flex-end
@@ -1307,9 +1560,9 @@
1307 1560 max-width: 100%;
1308 1561 margin-inline: auto;
1309 1562 }
1310 1563
1311 -.desktop-mode-my-wordpress__footprint-grid {
1564 +.os-my-wordpress__footprint-grid {
1312 1565 display: grid;
1313 1566 /* Row 1 holds month labels (auto height); rows 2–8 are the 7
1314 1567 * weekdays at fixed 11px. Col 1 holds weekday labels (auto width);
1315 1568 * subsequent cols are 11px-wide week tracks, set per-cell via
@@ -1321,17 +1574,17 @@
1321 1574 row-gap: 3px;
1322 1575 width: max-content;
1323 1576 }
1324 1577
1325 -.desktop-mode-my-wordpress__footprint-month,
1326 -.desktop-mode-my-wordpress__footprint-weekday {
1578 +.os-my-wordpress__footprint-month,
1579 +.os-my-wordpress__footprint-weekday {
1327 1580 font-size: 10px;
1328 1581 line-height: 11px;
1329 - color: var( --desktop-mode-muted, #50575e );
1582 + color: var( --os-ui-fg-muted, #50575e );
1330 1583 white-space: nowrap;
1331 1584 }
1332 1585
1333 -.desktop-mode-my-wordpress__footprint-weekday {
1586 +.os-my-wordpress__footprint-weekday {
1334 1587 /* Sit flush to the right of the label gutter so each label reads
1335 1588 * adjacent to its row. The 6px breathing room is in addition to
1336 1589 * the grid's column-gap. */
1337 1590 padding-inline-end: 6px;
@@ -1338,68 +1591,68 @@
1338 1591 align-self: center;
1339 1592 justify-self: end;
1340 1593 }
1341 1594
1342 -.desktop-mode-my-wordpress__footprint-month {
1595 +.os-my-wordpress__footprint-month {
1343 1596 padding-bottom: 2px;
1344 1597 }
1345 1598
1346 -.desktop-mode-my-wordpress__footprint-cell {
1599 +.os-my-wordpress__footprint-cell {
1347 1600 width: 11px;
1348 1601 height: 11px;
1349 1602 border-radius: 2px;
1350 - background: var( --desktop-mode-border, #ebedf0 );
1603 + background: var( --os-ui-border, #ebedf0 );
1351 1604 display: inline-block;
1352 1605 transition: transform 0.12s ease;
1353 1606 }
1354 1607
1355 -.desktop-mode-my-wordpress__footprint-cell--pad {
1608 +.os-my-wordpress__footprint-cell--pad {
1356 1609 background: transparent;
1357 1610 }
1358 1611
1359 -.desktop-mode-my-wordpress__footprint-cell--l0 {
1360 - background: rgba( 0, 0, 0, 0.06 );
1612 +.os-my-wordpress__footprint-cell--l0 {
1613 + background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) );
1361 1614 }
1362 1615
1363 -.desktop-mode-my-wordpress__footprint-cell--l1 {
1364 - background: rgba( 34, 113, 177, 0.25 );
1616 +.os-my-wordpress__footprint-cell--l1 {
1617 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 25%, transparent );
1365 1618 }
1366 1619
1367 -.desktop-mode-my-wordpress__footprint-cell--l2 {
1368 - background: rgba( 34, 113, 177, 0.5 );
1620 +.os-my-wordpress__footprint-cell--l2 {
1621 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 50%, transparent );
1369 1622 }
1370 1623
1371 -.desktop-mode-my-wordpress__footprint-cell--l3 {
1372 - background: rgba( 34, 113, 177, 0.75 );
1624 +.os-my-wordpress__footprint-cell--l3 {
1625 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 75%, transparent );
1373 1626 }
1374 1627
1375 -.desktop-mode-my-wordpress__footprint-cell--l4 {
1628 +.os-my-wordpress__footprint-cell--l4 {
1376 1629 background: var( --wp-admin-theme-color, #2271b1 );
1377 1630 }
1378 1631
1379 -.desktop-mode-my-wordpress__footprint-cell:hover {
1632 +.os-my-wordpress__footprint-cell:hover {
1380 1633 transform: scale( 1.4 );
1381 1634 z-index: 1;
1382 1635 }
1383 1636
1384 -.desktop-mode-my-wordpress__footprint-legend {
1637 +.os-my-wordpress__footprint-legend {
1385 1638 display: flex;
1386 1639 gap: 4px;
1387 1640 align-items: center;
1388 1641 justify-content: flex-end;
1389 1642 font-size: 11px;
1390 - color: var( --desktop-mode-muted, #50575e );
1643 + color: var( --os-ui-fg-muted, #50575e );
1391 1644 }
1392 1645
1393 -.desktop-mode-my-wordpress__footprint-legend-label {
1646 +.os-my-wordpress__footprint-legend-label {
1394 1647 font-size: 11px;
1395 - color: var( --desktop-mode-muted, #50575e );
1648 + color: var( --os-ui-fg-muted, #50575e );
1396 1649 padding: 0 4px;
1397 1650 }
1398 1651
1399 1652 /* ----- Rhythm (weekday + hour bar charts) ----- */
1400 1653
1401 -.desktop-mode-my-wordpress__footprint-rhythm-grid {
1654 +.os-my-wordpress__footprint-rhythm-grid {
1402 1655 display: grid;
1403 1656 grid-template-columns: minmax( 0, 1fr ) minmax( 0, 1.7fr );
1404 1657 gap: 24px;
1405 1658 }
@@ -1404,20 +1657,20 @@
1404 1657 gap: 24px;
1405 1658 }
1406 1659
1407 1660 @container ( max-width: 720px ) {
1408 - .desktop-mode-my-wordpress__footprint-rhythm-grid {
1661 + .os-my-wordpress__footprint-rhythm-grid {
1409 1662 grid-template-columns: 1fr;
1410 1663 }
1411 1664 }
1412 1665
1413 -.desktop-mode-my-wordpress__footprint-chart-caption {
1666 +.os-my-wordpress__footprint-chart-caption {
1414 1667 font-size: 12px;
1415 - color: var( --desktop-mode-muted, #50575e );
1668 + color: var( --os-ui-fg-muted, #50575e );
1416 1669 margin-bottom: 6px;
1417 1670 }
1418 1671
1419 -.desktop-mode-my-wordpress__footprint-bars {
1672 +.os-my-wordpress__footprint-bars {
1420 1673 display: flex;
1421 1674 align-items: end;
1422 1675 gap: 4px;
1423 1676 height: 100px;
@@ -1424,9 +1677,9 @@
1424 1677 padding-bottom: 18px;
1425 1678 position: relative;
1426 1679 }
1427 1680
1428 -.desktop-mode-my-wordpress__footprint-bar-col {
1681 +.os-my-wordpress__footprint-bar-col {
1429 1682 flex: 1 1 0;
1430 1683 min-width: 0;
1431 1684 display: flex;
1432 1685 flex-direction: column;
@@ -1436,13 +1689,13 @@
1436 1689 height: 100%;
1437 1690 position: relative;
1438 1691 }
1439 1692
1440 -.desktop-mode-my-wordpress__footprint-bar {
1693 +.os-my-wordpress__footprint-bar {
1441 1694 background: linear-gradient(
1442 1695 180deg,
1443 1696 var( --wp-admin-theme-color, #2271b1 ) 0%,
1444 - rgba( 34, 113, 177, 0.55 ) 100%
1697 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 55%, transparent ) 100%
1445 1698 );
1446 1699 border-radius: 3px 3px 0 0;
1447 1700 min-height: 2px;
1448 1701 transition: opacity 0.2s ease;
@@ -1447,15 +1700,15 @@
1447 1700 min-height: 2px;
1448 1701 transition: opacity 0.2s ease;
1449 1702 }
1450 1703
1451 -.desktop-mode-my-wordpress__footprint-bar--empty {
1452 - background: var( --desktop-mode-border, #dcdcde );
1704 +.os-my-wordpress__footprint-bar--empty {
1705 + background: var( --os-ui-border, #dcdcde );
1453 1706 height: 2px !important;
1454 1707 min-height: 2px;
1455 1708 }
1456 1709
1457 -.desktop-mode-my-wordpress__footprint-bar-label {
1710 +.os-my-wordpress__footprint-bar-label {
1458 1711 position: absolute;
1459 1712 bottom: -16px;
1460 1713 left: 0;
1461 1714 right: 0;
@@ -1460,14 +1713,14 @@
1460 1713 left: 0;
1461 1714 right: 0;
1462 1715 text-align: center;
1463 1716 font-size: 10px;
1464 - color: var( --desktop-mode-muted, #787c82 );
1717 + color: var( --os-ui-fg-muted, #787c82 );
1465 1718 }
1466 1719
1467 1720 /* ----- Most-prolific-month callout ----- */
1468 1721
1469 -.desktop-mode-my-wordpress__footprint-callout {
1722 +.os-my-wordpress__footprint-callout {
1470 1723 display: flex;
1471 1724 flex-direction: column;
1472 1725 gap: 4px;
1473 1726 align-items: center;
@@ -1474,38 +1727,38 @@
1474 1727 text-align: center;
1475 1728 padding: 20px 24px;
1476 1729 background: linear-gradient(
1477 1730 135deg,
1478 - rgba( 34, 113, 177, 0.10 ),
1479 - rgba( 34, 113, 177, 0.02 )
1731 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 10%, transparent ),
1732 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 2%, transparent )
1480 1733 );
1481 1734 }
1482 1735
1483 -.desktop-mode-my-wordpress__footprint-callout-label {
1736 +.os-my-wordpress__footprint-callout-label {
1484 1737 font-size: 11px;
1485 1738 text-transform: uppercase;
1486 1739 letter-spacing: 0.08em;
1487 - color: var( --desktop-mode-muted, #50575e );
1740 + color: var( --os-ui-fg-muted, #50575e );
1488 1741 font-weight: 600;
1489 1742 }
1490 1743
1491 -.desktop-mode-my-wordpress__footprint-callout-value {
1744 +.os-my-wordpress__footprint-callout-value {
1492 1745 margin: 2px 0;
1493 1746 font-size: 24px;
1494 1747 font-weight: 700;
1495 - color: var( --desktop-mode-fg, #1d2327 );
1748 + color: var( --os-fg, #1d2327 );
1496 1749 }
1497 1750
1498 -.desktop-mode-my-wordpress__footprint-callout-detail {
1751 +.os-my-wordpress__footprint-callout-detail {
1499 1752 margin: 0;
1500 1753 font-size: 13px;
1501 - color: var( --desktop-mode-muted, #50575e );
1754 + color: var( --os-ui-fg-muted, #50575e );
1502 1755 max-width: 480px;
1503 1756 }
1504 1757
1505 1758 /* ----- Recent-activity timeline ----- */
1506 1759
1507 -.desktop-mode-my-wordpress__footprint-timeline {
1760 +.os-my-wordpress__footprint-timeline {
1508 1761 list-style: none;
1509 1762 margin: 0;
1510 1763 padding: 0;
1511 1764 position: relative;
@@ -1513,9 +1766,9 @@
1513 1766 flex-direction: column;
1514 1767 gap: 12px;
1515 1768 }
1516 1769
1517 -.desktop-mode-my-wordpress__footprint-timeline::before {
1770 +.os-my-wordpress__footprint-timeline::before {
1518 1771 content: '';
1519 1772 position: absolute;
1520 1773 inset-inline-start: 13px;
1521 1774 top: 4px;
@@ -1520,13 +1773,13 @@
1520 1773 inset-inline-start: 13px;
1521 1774 top: 4px;
1522 1775 bottom: 4px;
1523 1776 width: 2px;
1524 - background: var( --desktop-mode-border, #dcdcde );
1777 + background: var( --os-ui-border, #dcdcde );
1525 1778 border-radius: 1px;
1526 1779 }
1527 1780
1528 -.desktop-mode-my-wordpress__footprint-event {
1781 +.os-my-wordpress__footprint-event {
1529 1782 display: flex;
1530 1783 gap: 12px;
1531 1784 align-items: flex-start;
1532 1785 position: relative;
@@ -1533,9 +1786,9 @@
1533 1786 padding-inline-start: 36px;
1534 1787 min-height: 28px;
1535 1788 }
1536 1789
1537 -.desktop-mode-my-wordpress__footprint-dot {
1790 +.os-my-wordpress__footprint-dot {
1538 1791 position: absolute;
1539 1792 inset-inline-start: 0;
1540 1793 top: 0;
1541 1794 width: 28px;
@@ -1543,20 +1796,20 @@
1543 1796 border-radius: 50%;
1544 1797 display: inline-flex;
1545 1798 align-items: center;
1546 1799 justify-content: center;
1547 - background: var( --desktop-mode-surface, #fff );
1800 + background: var( --os-surface, var( --os-ui-surface, #fff ) );
1548 1801 border: 2px solid var( --wp-admin-theme-color, #2271b1 );
1549 1802 color: var( --wp-admin-theme-color, #2271b1 );
1550 1803 }
1551 1804
1552 -.desktop-mode-my-wordpress__footprint-event--comment
1553 - .desktop-mode-my-wordpress__footprint-dot {
1554 - border-color: var( --desktop-mode-muted, #787c82 );
1555 - color: var( --desktop-mode-muted, #787c82 );
1805 +.os-my-wordpress__footprint-event--comment
1806 + .os-my-wordpress__footprint-dot {
1807 + border-color: var( --os-ui-fg-muted, #787c82 );
1808 + color: var( --os-ui-fg-muted, #787c82 );
1556 1809 }
1557 1810
1558 -.desktop-mode-my-wordpress__footprint-dot .dashicons {
1811 +.os-my-wordpress__footprint-dot .dashicons {
1559 1812 font-size: 14px;
1560 1813 width: 14px;
1561 1814 height: 14px;
1562 1815 line-height: 1;
@@ -1561,9 +1814,9 @@
1561 1814 height: 14px;
1562 1815 line-height: 1;
1563 1816 }
1564 1817
1565 -.desktop-mode-my-wordpress__footprint-event-body {
1818 +.os-my-wordpress__footprint-event-body {
1566 1819 display: flex;
1567 1820 flex-direction: column;
1568 1821 gap: 2px;
1569 1822 min-width: 0;
@@ -1569,12 +1822,12 @@
1569 1822 min-width: 0;
1570 1823 flex: 1 1 auto;
1571 1824 }
1572 1825
1573 -.desktop-mode-my-wordpress__footprint-event-title {
1826 +.os-my-wordpress__footprint-event-title {
1574 1827 font-weight: 600;
1575 1828 font-size: 13px;
1576 - color: var( --desktop-mode-link, #2271b1 );
1829 + color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
1577 1830 text-decoration: none;
1578 1831 overflow: hidden;
1579 1832 text-overflow: ellipsis;
1580 1833 white-space: nowrap;
@@ -1579,20 +1832,20 @@
1579 1832 text-overflow: ellipsis;
1580 1833 white-space: nowrap;
1581 1834 }
1582 1835
1583 -a.desktop-mode-my-wordpress__footprint-event-title:hover {
1836 +a.os-my-wordpress__footprint-event-title:hover {
1584 1837 text-decoration: underline;
1585 1838 }
1586 1839
1587 -.desktop-mode-my-wordpress__footprint-event-meta {
1840 +.os-my-wordpress__footprint-event-meta {
1588 1841 font-size: 11px;
1589 - color: var( --desktop-mode-muted, #787c82 );
1842 + color: var( --os-ui-fg-muted, #787c82 );
1590 1843 }
1591 1844
1592 1845 /* ----- Footer actions ----- */
1593 1846
1594 -.desktop-mode-my-wordpress__footprint-footer {
1847 +.os-my-wordpress__footprint-footer {
1595 1848 display: flex;
1596 1849 justify-content: flex-end;
1597 1850 align-items: center;
1598 1851 gap: 8px;
@@ -1620,9 +1873,9 @@
1620 1873 * banner across that corner. The strip extends past the
1621 1874 * tile edges; `overflow: hidden` on the parent crops it
1622 1875 * into a clean triangle.
1623 1876 *
1624 - * The status tiles need a positioning context. `.desktop-mode-
1877 + * The status tiles need a positioning context. `.os-
1625 1878 * file-tile` is `position: absolute` (canvas layout) on the
1626 1879 * Posts/Pages browse views — that satisfies the positioning-
1627 1880 * context requirement. For the Media drill-in usage tiles we
1628 1881 * swap from `static` to `relative` via the per-status rule
@@ -1629,9 +1882,9 @@
1629 1882 * below so the ribbon stays anchored to its own tile.
1630 1883 * ============================================================ */
1631 1884
1632 1885 /* Status ribbon — the diagonal corner banner — is now drawn by
1633 - * the `<wpd-ribbon>` web component slotted inside `<wpd-tile>`.
1886 + * the `<os-ribbon>` web component slotted inside `<os-tile>`.
1634 1887 * The component's own shadow-DOM styles own placement, rotation,
1635 1888 * background, lettering, and the WP admin-theme tint. The only
1636 1889 * surface-specific rule left here is dimming the icon on non-
1637 1890 * published tiles so the unpublished signal carries even when
@@ -1636,19 +1889,19 @@
1636 1889 * surface-specific rule left here is dimming the icon on non-
1637 1890 * published tiles so the unpublished signal carries even when
1638 1891 * the ribbon is hidden via the OS-settings toggle. */
1639 1892
1640 -.desktop-mode-my-wordpress__tile--draft,
1641 -.desktop-mode-my-wordpress__tile--pending,
1642 -.desktop-mode-my-wordpress__tile--private,
1643 -.desktop-mode-my-wordpress__tile--future {
1893 +.os-my-wordpress__tile--draft,
1894 +.os-my-wordpress__tile--pending,
1895 +.os-my-wordpress__tile--private,
1896 +.os-my-wordpress__tile--future {
1644 1897 overflow: hidden;
1645 1898 }
1646 1899
1647 -wpd-tile[ status='draft' ] .desktop-mode-file-tile__icon,
1648 -wpd-tile[ status='pending' ] .desktop-mode-file-tile__icon,
1649 -wpd-tile[ status='private' ] .desktop-mode-file-tile__icon,
1650 -wpd-tile[ status='future' ] .desktop-mode-file-tile__icon {
1900 +os-tile[ status='draft' ] .os-file-tile__icon,
1901 +os-tile[ status='pending' ] .os-file-tile__icon,
1902 +os-tile[ status='private' ] .os-file-tile__icon,
1903 +os-tile[ status='future' ] .os-file-tile__icon {
1651 1904 opacity: 0.7;
1652 1905 }
1653 1906
1654 1907 /* ============================================================ *
@@ -1654,19 +1907,23 @@
1654 1907 /* ============================================================ *
1655 1908 * Media section (since 0.21.0)
1656 1909 * ============================================================ */
1657 1910
1658 -.desktop-mode-my-wordpress__media-grid {
1911 +.os-my-wordpress__media-grid {
1659 1912 display: grid;
1660 1913 grid-template-columns: repeat( auto-fill, minmax( 120px, 1fr ) );
1661 - gap: 12px;
1662 - padding: 16px;
1914 + /* A flow grid rather than the absolute canvas, but the air
1915 + * between two icons should not depend on which window you are
1916 + * looking at — so the gaps and the gutter come from the same
1917 + * tokens the canvas pitch is built from. */
1918 + gap: var( --os-grid-gap-y, 16px ) var( --os-grid-gap-x, 20px );
1919 + padding: var( --os-grid-padding, 16px );
1663 1920 overflow-y: auto;
1664 1921 align-content: start;
1665 1922 }
1666 1923
1667 -.desktop-mode-my-wordpress__media-tile {
1668 - /* Override the base `.desktop-mode-file-tile` (position: absolute,
1924 +.os-my-wordpress__media-tile {
1925 + /* Override the base `.os-file-tile` (position: absolute,
1669 1926 * width: 88px) so media tiles flow inside the CSS Grid instead of
1670 1927 * stacking at (0,0) like the absolute-positioned post/user tiles.
1671 1928 *
1672 1929 * `relative` (not `static`) so the tile remains a containing
@@ -1671,14 +1928,18 @@
1671 1928 *
1672 1929 * `relative` (not `static`) so the tile remains a containing
1673 1930 * block for absolutely-positioned descendants — the `::after`
1674 1931 * shortcut-arrow badge on attachment tiles, and the
1675 - * `<wpd-ribbon>` post-status corner ribbon — both of which
1932 + * `<os-ribbon>` post-status corner ribbon — both of which
1676 1933 * would otherwise escape to the nearest positioned ancestor
1677 1934 * (the window, the desktop, the viewport) and render way off
1678 1935 * the tile. */
1679 1936 position: relative;
1680 1937 width: auto;
1938 + /* …and out of the fixed tile height for the same reason: a media
1939 + * tile is a square thumbnail plus a caption, sized by the grid
1940 + * column it lands in, not by the icon-canvas cell. */
1941 + height: auto;
1681 1942 display: flex;
1682 1943 flex-direction: column;
1683 1944 align-items: stretch;
1684 1945 gap: 6px;
@@ -1690,13 +1951,13 @@
1690 1951 text-align: center;
1691 1952 min-width: 0;
1692 1953 }
1693 1954
1694 -.desktop-mode-my-wordpress__media-tile:hover {
1695 - background: var( --desktop-mode-hover, rgba( 0, 0, 0, 0.04 ) );
1955 +.os-my-wordpress__media-tile:hover {
1956 + background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ) );
1696 1957 }
1697 1958
1698 -/* Media tiles inherit the canonical `.desktop-mode-file-tile--selected`
1959 +/* Media tiles inherit the canonical `.os-file-tile--selected`
1699 1960 * rule from `desktop-files.css` — accent inset border + tinted
1700 1961 * background — same as every other tile in the shell. */
1701 1962
1702 1963 /* Override the canonical 48×48 visual/preview sizing — media tiles
@@ -1704,9 +1965,9 @@
1704 1965 * thumbnail (or icon-fallback square). Photos browser look, not
1705 1966 * folder-icon look. Applies to BOTH the `<img>` (real thumbnail)
1706 1967 * and the dashicon fallback (non-image MIME), so the two tile
1707 1968 * shapes are visually consistent in the grid. */
1708 -.desktop-mode-my-wordpress__media-tile .desktop-mode-file-tile__visual {
1969 +.os-my-wordpress__media-tile .os-file-tile__visual {
1709 1970 display: flex;
1710 1971 align-items: center;
1711 1972 justify-content: center;
1712 1973 width: 100%;
@@ -1714,9 +1975,9 @@
1714 1975 aspect-ratio: 1 / 1;
1715 1976 overflow: hidden;
1716 1977 border-radius: 6px;
1717 1978 background: var(
1718 - --desktop-mode-media-tile-bg,
1979 + --os-media-tile-bg,
1719 1980 rgba( 0, 0, 0, 0.05 )
1720 1981 );
1721 1982 /* Reset the base 32px font + 48px size since we're sizing the
1722 1983 * inner img/icon explicitly below. */
@@ -1723,9 +1984,9 @@
1723 1984 font-size: 0;
1724 1985 line-height: 1;
1725 1986 }
1726 1987
1727 -.desktop-mode-my-wordpress__media-tile .desktop-mode-file-tile__preview {
1988 +.os-my-wordpress__media-tile .os-file-tile__preview {
1728 1989 width: 100%;
1729 1990 height: 100%;
1730 1991 object-fit: cover;
1731 1992 border-radius: 0;
@@ -1730,18 +1991,18 @@
1730 1991 object-fit: cover;
1731 1992 border-radius: 0;
1732 1993 }
1733 1994
1734 -.desktop-mode-my-wordpress__media-tile .desktop-mode-file-tile__icon {
1995 +.os-my-wordpress__media-tile .os-file-tile__icon {
1735 1996 /* Centered dashicon fallback — proportional to the larger
1736 1997 * thumbnail well so it doesn't look like a postage stamp. */
1737 1998 width: auto;
1738 1999 height: auto;
1739 2000 font-size: 36px;
1740 - color: var( --desktop-mode-muted, #787c82 );
2001 + color: var( --os-ui-fg-muted, #787c82 );
1741 2002 }
1742 2003
1743 -.desktop-mode-my-wordpress__media-tile .desktop-mode-file-tile__label {
2004 +.os-my-wordpress__media-tile .os-file-tile__label {
1744 2005 font-size: 12px;
1745 2006 line-height: 1.3;
1746 2007 overflow: hidden;
1747 2008 text-overflow: ellipsis;
@@ -1751,9 +2012,9 @@
1751 2012 }
1752 2013
1753 2014 /* ----- Media preview pane ----- */
1754 2015
1755 -.desktop-mode-my-wordpress__media-pane {
2016 +.os-my-wordpress__media-pane {
1756 2017 display: flex;
1757 2018 flex-direction: column;
1758 2019 gap: 16px;
1759 2020 padding: 16px;
@@ -1758,19 +2019,19 @@
1758 2019 gap: 16px;
1759 2020 padding: 16px;
1760 2021 }
1761 2022
1762 -.desktop-mode-my-wordpress__media-title {
2023 +.os-my-wordpress__media-title {
1763 2024 font-size: 16px;
1764 2025 font-weight: 600;
1765 2026 margin: 0;
1766 2027 }
1767 2028
1768 -.desktop-mode-my-wordpress__media-visual {
2029 +.os-my-wordpress__media-visual {
1769 2030 display: flex;
1770 2031 align-items: center;
1771 2032 justify-content: center;
1772 - background: var( --desktop-mode-media-visual-bg, rgba( 0, 0, 0, 0.04 ) );
2033 + background: var( --os-media-visual-bg, var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ) );
1773 2034 border-radius: 8px;
1774 2035 min-height: 200px;
1775 2036 max-height: 480px;
1776 2037 overflow: hidden;
@@ -1775,28 +2036,28 @@
1775 2036 max-height: 480px;
1776 2037 overflow: hidden;
1777 2038 }
1778 2039
1779 -.desktop-mode-my-wordpress__media-image,
1780 -.desktop-mode-my-wordpress__media-video {
2040 +.os-my-wordpress__media-image,
2041 +.os-my-wordpress__media-video {
1781 2042 max-width: 100%;
1782 2043 max-height: 480px;
1783 2044 object-fit: contain;
1784 2045 }
1785 2046
1786 -.desktop-mode-my-wordpress__media-fallback-icon {
2047 +.os-my-wordpress__media-fallback-icon {
1787 2048 font-size: 96px;
1788 2049 width: 96px;
1789 2050 height: 96px;
1790 - color: var( --desktop-mode-muted, #787c82 );
2051 + color: var( --os-ui-fg-muted, #787c82 );
1791 2052 }
1792 2053
1793 -.desktop-mode-my-wordpress__media-doc-link {
2054 +.os-my-wordpress__media-doc-link {
1794 2055 margin-inline-start: 16px;
1795 2056 align-self: center;
1796 2057 }
1797 2058
1798 -.desktop-mode-my-wordpress__media-audio-stack {
2059 +.os-my-wordpress__media-audio-stack {
1799 2060 display: flex;
1800 2061 flex-direction: column;
1801 2062 align-items: center;
1802 2063 gap: 12px;
@@ -1802,9 +2063,9 @@
1802 2063 gap: 12px;
1803 2064 padding: 24px;
1804 2065 }
1805 2066
1806 -.desktop-mode-my-wordpress__media-meta {
2067 +.os-my-wordpress__media-meta {
1807 2068 display: grid;
1808 2069 grid-template-columns: max-content 1fr;
1809 2070 gap: 4px 16px;
1810 2071 font-size: 13px;
@@ -1810,31 +2071,31 @@
1810 2071 font-size: 13px;
1811 2072 margin: 0;
1812 2073 }
1813 2074
1814 -.desktop-mode-my-wordpress__media-meta-term {
1815 - color: var( --desktop-mode-muted, #787c82 );
2075 +.os-my-wordpress__media-meta-term {
2076 + color: var( --os-ui-fg-muted, #787c82 );
1816 2077 margin: 0;
1817 2078 }
1818 2079
1819 -.desktop-mode-my-wordpress__media-meta-value {
1820 - color: var( --desktop-mode-text, #1d2327 );
2080 +.os-my-wordpress__media-meta-value {
2081 + color: var( --os-ui-fg, #1d2327 );
1821 2082 word-break: break-word;
1822 2083 margin: 0;
1823 2084 }
1824 2085
1825 -.desktop-mode-my-wordpress__media-actions {
2086 +.os-my-wordpress__media-actions {
1826 2087 display: flex;
1827 2088 flex-wrap: wrap;
1828 2089 gap: 8px;
1829 2090 padding-top: 8px;
1830 - border-top: 1px solid var( --desktop-mode-border, #dcdcde );
2091 + border-top: 1px solid var( --os-ui-border, #dcdcde );
1831 2092 }
1832 2093
1833 2094 /* ----- Media drill-in ----- */
1834 2095
1835 2096 /* Drill-in: referencing-posts list. Visually identical to the
1836 - * Posts / Pages tiles — same `desktop-mode-file-tile` chrome (48px
2097 + * Posts / Pages tiles — same `os-file-tile` chrome (48px
1837 2098 * dashicon + 88px-wide two-line label). We can't reuse the
1838 2099 * absolute-positioning canvas layout from `renderEntityList` here
1839 2100 * (those internals don't export), so this surface uses a flex-wrap
1840 2101 * of fixed-width tiles to reproduce the look.
@@ -1842,22 +2103,22 @@
1842 2103 * NOTE: this rule INTENTIONALLY overrides the `__media-grid`
1843 2104 * defaults (which target square thumbnails on 130px columns) so
1844 2105 * usage tiles match the canvas-flowed post tiles instead.
1845 2106 */
1846 -.desktop-mode-my-wordpress__usage-grid {
2107 +.os-my-wordpress__usage-grid {
1847 2108 display: flex;
1848 2109 flex-wrap: wrap;
1849 - gap: 12px 8px;
1850 - padding: 16px;
2110 + gap: var( --os-grid-gap-y, 16px ) var( --os-grid-gap-x, 20px );
2111 + padding: var( --os-grid-padding, 16px );
1851 2112 align-content: flex-start;
1852 2113 }
1853 2114
1854 2115 /* Reset the `__media-tile` overrides for usage tiles so they
1855 - * inherit the unmodified `.desktop-mode-file-tile` chrome the
2116 + * inherit the unmodified `.os-file-tile` chrome the
1856 2117 * canvas-positioned post tiles use — except for `position: static`
1857 2118 * so they participate in the flex layout. */
1858 -.desktop-mode-my-wordpress__tile--usage {
1859 - width: 88px;
2119 +.os-my-wordpress__tile--usage {
2120 + width: var( --os-tile-w, 88px );
1860 2121 align-items: center;
1861 2122 padding: 8px 4px;
1862 2123 gap: 6px;
1863 2124 }
@@ -1862,20 +2123,832 @@
1862 2123 gap: 6px;
1863 2124 }
1864 2125
1865 2126 /* Right-pane summary bar sits ABOVE the inline media preview. */
1866 -.desktop-mode-my-wordpress__media-detail-summary-bar {
2127 +.os-my-wordpress__media-detail-summary-bar {
1867 2128 padding: 12px 16px;
1868 - border-bottom: 1px solid var( --desktop-mode-border, #dcdcde );
2129 + border-bottom: 1px solid var( --os-ui-border, #dcdcde );
1869 2130 }
1870 2131
1871 -.desktop-mode-my-wordpress__media-detail-summary {
2132 +.os-my-wordpress__media-detail-summary {
1872 2133 margin: 0;
1873 - color: var( --desktop-mode-muted, #787c82 );
2134 + color: var( --os-ui-fg-muted, #787c82 );
1874 2135 font-size: 13px;
1875 2136 }
1876 2137
1877 -.desktop-mode-my-wordpress__media-detail-preview {
2138 +.os-my-wordpress__media-detail-preview {
1878 2139 /* Lets the embedded `__media-pane` keep its own padding without
1879 2140 * fighting the summary bar's spacing. */
1880 2141 display: block;
2142 +}
2143 +
2144 +/* ------------------------------------------------------------------ */
2145 +/* Agents section (`agent` entity kind — src/my-wordpress/agents-renderer.ts) */
2146 +/* ------------------------------------------------------------------ */
2147 +
2148 +.dm-agents {
2149 + display: flex;
2150 + flex-direction: column;
2151 + height: 100%;
2152 + min-height: 0;
2153 + padding: 12px;
2154 + gap: 10px;
2155 + overflow: hidden;
2156 + box-sizing: border-box;
2157 +}
2158 +
2159 +
2160 +/* Section-level loading — same scale curve as the My WordPress
2161 + preview loader and the window-loading overlay, instead of the bare
2162 + 48px component default. */
2163 +.dm-agents__loading {
2164 + display: flex;
2165 + align-items: center;
2166 + justify-content: center;
2167 + height: 100%;
2168 + min-height: 320px;
2169 +}
2170 +
2171 +.dm-agents__loading os-spinner {
2172 + --os-ui-spinner-size: clamp( 96px, 14vw, 192px );
2173 +}
2174 +
2175 +.dm-agents__detail {
2176 + flex: 1;
2177 + min-width: 0;
2178 + overflow-y: auto;
2179 + display: flex;
2180 + flex-direction: column;
2181 + gap: 10px;
2182 +}
2183 +
2184 +/* `<os-empty-state>` owns the icon + copy and centres them within
2185 + itself; the host is still a flex item that shrinks to its content,
2186 + so without this it sits at the top of a pane that is mostly empty
2187 + space. Auto margins on both axes absorb the slack — placement only,
2188 + same as the comments window. Covers the framework-off state and
2189 + "No agents yet" alike. */
2190 +.dm-agents__detail > os-empty-state {
2191 + margin: auto;
2192 +}
2193 +
2194 +.dm-agents__detail-head {
2195 + display: flex;
2196 + align-items: center;
2197 + gap: 10px;
2198 +}
2199 +
2200 +.dm-agents__detail-avatar {
2201 + width: 48px;
2202 + height: 48px;
2203 + border-radius: 50%;
2204 + /* Placeholder behind the avatar image while it loads — follows the
2205 + palette so it isn't a white disc on a dark station. */
2206 + background: var( --os-ui-surface-elevated, #fff );
2207 + flex: none;
2208 +}
2209 +
2210 +.dm-agents__detail-title {
2211 + flex: 1;
2212 + min-width: 0;
2213 +}
2214 +
2215 +.dm-agents__detail-title h3 {
2216 + margin: 0;
2217 + font-size: 16px;
2218 +}
2219 +
2220 +.dm-agents__detail-slug {
2221 + font-size: 12px;
2222 + opacity: 0.6;
2223 +}
2224 +
2225 +/* Verbs get their own row under the identity block: the set grows
2226 + (profile, contributions, desktop, chat, delete) and squeezing them
2227 + beside the name truncated it first. */
2228 +.dm-agents__detail-actions {
2229 + display: flex;
2230 + flex-wrap: wrap;
2231 + gap: 8px;
2232 +}
2233 +
2234 +.dm-agents__tabs {
2235 + display: flex;
2236 + gap: 4px;
2237 + border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2238 +}
2239 +
2240 +.dm-agents__tab {
2241 + appearance: none;
2242 + background: none;
2243 + border: none;
2244 + border-bottom: 2px solid transparent;
2245 + padding: 6px 10px;
2246 + font: inherit;
2247 + color: inherit;
2248 + cursor: pointer;
2249 + opacity: 0.7;
2250 +}
2251 +
2252 +.dm-agents__tab.is-active {
2253 + border-bottom-color: var( --os-accent, #2271b1 );
2254 + opacity: 1;
2255 + font-weight: 600;
2256 +}
2257 +
2258 +.dm-agents__pane {
2259 + display: flex;
2260 + flex-direction: column;
2261 + gap: 10px;
2262 + padding-bottom: 12px;
2263 +}
2264 +
2265 +/* Pane-level loading: same big, centered WordPress mark the section
2266 + loader uses, instead of a bare 48px spinner pinned to the top-left
2267 + of the pane. */
2268 +.dm-agents__pane--loading {
2269 + align-items: center;
2270 + justify-content: center;
2271 + flex: 1;
2272 + min-height: 260px;
2273 +}
2274 +
2275 +.dm-agents__pane--loading os-spinner {
2276 + --os-ui-spinner-size: clamp( 96px, 12vw, 160px );
2277 +}
2278 +
2279 +.dm-agents__hint {
2280 + margin: 0;
2281 + font-size: 12px;
2282 + opacity: 0.7;
2283 +}
2284 +
2285 +/* Role first: least privilege is the decision, so the control holds
2286 + the mockup's width instead of stretching into a landing strip. */
2287 +.dm-agents__role-select {
2288 + max-width: 280px;
2289 +}
2290 +
2291 +/* The ability filter. Sized like the role select rather than the
2292 + full pane: it sits above a list of rules, and a search box as wide
2293 + as the rules reads as another one of them. */
2294 +.dm-agents__ability-search {
2295 + display: block;
2296 + max-width: 320px;
2297 + margin: 12px 0 4px;
2298 +}
2299 +
2300 +/* Each category is a native disclosure. `<details>` because the
2301 + behaviour is exactly what it is for: keyboard, screen-reader
2302 + semantics and the open/closed state all arrive for free, and the
2303 + only thing left to write is the costume. */
2304 +.dm-agents__ability-group {
2305 + border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2306 +}
2307 +
2308 +.dm-agents__ability-group:last-of-type {
2309 + border-bottom: 0;
2310 +}
2311 +
2312 +.dm-agents__category {
2313 + margin: 18px 0 2px;
2314 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2315 + font-size: 11px;
2316 + font-weight: 500;
2317 + text-transform: uppercase;
2318 + letter-spacing: 0.08em;
2319 + color: var( --os-ui-fg-muted, #646970 );
2320 +}
2321 +
2322 +/* A `<summary>` is a button, so it gets a button's affordances: a
2323 + pointer, a hover, and a focus ring that is visible on the dark
2324 + surface the section is drawn on. */
2325 +summary.dm-agents__category {
2326 + display: flex;
2327 + align-items: center;
2328 + gap: 8px;
2329 + margin: 0;
2330 + padding: 10px 0;
2331 + cursor: pointer;
2332 + list-style: none;
2333 + user-select: none;
2334 +}
2335 +
2336 +summary.dm-agents__category::-webkit-details-marker {
2337 + display: none;
2338 +}
2339 +
2340 +/* The marker is drawn here rather than left to the UA so it can be
2341 + the same triangle in every browser and turn with the disclosure.
2342 + Rotation only: a transform, so opening a group of forty rows
2343 + costs no layout. */
2344 +summary.dm-agents__category::before {
2345 + content: '';
2346 + flex: none;
2347 + width: 0;
2348 + height: 0;
2349 + border-inline-start: 5px solid currentcolor;
2350 + border-block: 4px solid transparent;
2351 + transition: transform 120ms ease;
2352 +}
2353 +
2354 +.dm-agents__ability-group[ open ] > summary.dm-agents__category::before {
2355 + transform: rotate( 90deg );
2356 +}
2357 +
2358 +@media ( prefers-reduced-motion: reduce ) {
2359 + summary.dm-agents__category::before {
2360 + transition: none;
2361 + }
2362 +}
2363 +
2364 +summary.dm-agents__category:hover {
2365 + color: var( --os-ui-fg, #1e1e1e );
2366 +}
2367 +
2368 +summary.dm-agents__category:focus-visible {
2369 + outline: 2px solid var( --os-ui-accent, #3858e9 );
2370 + outline-offset: 2px;
2371 + border-radius: 4px;
2372 +}
2373 +
2374 +.dm-agents__category-name {
2375 + flex: 1 1 auto;
2376 +}
2377 +
2378 +/* "3 of 12" when some are ticked, otherwise just the size of the
2379 + group. The count is why a closed group is still informative:
2380 + collapsing must not hide the fact that something in there is
2381 + switched on. */
2382 +.dm-agents__category-count {
2383 + flex: none;
2384 + font-variant-numeric: tabular-nums;
2385 + opacity: 0.75;
2386 +}
2387 +
2388 +/* One ability per row, ruled like the mockup: tick and name on the
2389 + left, badge on the right, the description indented under the name
2390 + (the checkbox column is the control's own 26px). */
2391 +.dm-agents__ability {
2392 + display: grid;
2393 + grid-template-columns: 1fr auto;
2394 + gap: 2px 10px;
2395 + align-items: start;
2396 + padding: 11px 0;
2397 + border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2398 +}
2399 +
2400 +.dm-agents__ability:last-of-type {
2401 + border-bottom: 0;
2402 +}
2403 +
2404 +.dm-agents__ability-desc {
2405 + grid-column: 1 / -1;
2406 + margin: 0;
2407 + padding-inline-start: 26px;
2408 + font-size: 12.5px;
2409 + color: var( --os-ui-fg-muted, #646970 );
2410 +}
2411 +
2412 +/* Access badges in the instrument voice. The palette's own warning
2413 + tokens carry the pale-yellow "can modify"; all this changes is the
2414 + costume: mono, uppercase, an outline instead of a wash. */
2415 +.dm-agents__ability os-badge {
2416 + --os-ui-badge-font: 500 10px/1.6 var( --os-ui-font-mono, ui-monospace, monospace );
2417 + --os-ui-badge-dot-size: 0;
2418 + --os-ui-badge-gap: 0;
2419 + --os-ui-badge-padding: 3px 8px;
2420 + --os-ui-badge-bg: transparent;
2421 + --os-ui-badge-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) );
2422 + text-transform: uppercase;
2423 + letter-spacing: 0.06em;
2424 + white-space: nowrap;
2425 + margin-top: 2px;
2426 +}
2427 +
2428 +.dm-agents__ability os-badge[ tone='warning' ] {
2429 + --os-ui-badge-border: 1px solid
2430 + var( --os-ui-warning-border, rgba( 154, 103, 0, 0.4 ) );
2431 +}
2432 +
2433 +.dm-agents__trigger {
2434 + border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2435 + border-radius: 8px;
2436 + padding: 8px 10px;
2437 + display: flex;
2438 + flex-direction: column;
2439 + gap: 6px;
2440 +}
2441 +
2442 +.dm-agents__trigger-head {
2443 + display: flex;
2444 + align-items: center;
2445 + gap: 8px;
2446 +}
2447 +
2448 +.dm-agents__trigger-desc {
2449 + margin: 0;
2450 + font-size: 12px;
2451 + opacity: 0.75;
2452 +}
2453 +
2454 +.dm-agents__trigger-config {
2455 + display: flex;
2456 + flex-wrap: wrap;
2457 + gap: 4px 14px;
2458 +}
2459 +
2460 +.dm-agents__actions {
2461 + display: flex;
2462 + gap: 8px;
2463 + align-items: center;
2464 + flex-wrap: wrap;
2465 + margin-block-start: 12px;
2466 +}
2467 +
2468 +/* ---------------------------------------------------------------------
2469 + The cast
2470 + ------------------------------------------------------------------ */
2471 +
2472 +/* One view at a time: the grid, an agent, or the wizard. A sidebar of
2473 + faces next to a detail form would leave neither enough room, and the
2474 + whole point of the grid is that the faces are big enough to tell
2475 + apart. */
2476 +.dm-agents__view {
2477 + display: flex;
2478 + flex-direction: column;
2479 + flex: 1;
2480 + min-height: 0;
2481 + overflow-y: auto;
2482 + gap: 12px;
2483 +}
2484 +
2485 +.dm-agents__cast-head {
2486 + display: flex;
2487 + align-items: baseline;
2488 + gap: 8px;
2489 +}
2490 +
2491 +.dm-agents__cast-head h3 {
2492 + margin: 0;
2493 +}
2494 +
2495 +.dm-agents__cast-count {
2496 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2497 + font-size: 12px;
2498 + color: var( --os-ui-fg-muted, #646970 );
2499 +}
2500 +
2501 +.dm-agents__cast {
2502 + display: grid;
2503 + gap: 16px;
2504 + grid-template-columns: repeat( auto-fill, minmax( 288px, 1fr ) );
2505 + align-content: start;
2506 +}
2507 +
2508 +/* The card reacts like the mockup's: a lift and a brighter edge.
2509 + Transform and border-color only — both cheap, and the same pair the
2510 + mockup transitions. Document styles win over the component's own
2511 + :host rules, which is the sanctioned way to restyle a host. */
2512 +.dm-agents__cast-card,
2513 +.dm-agents__cast-new {
2514 + --os-ui-card-padding: 20px 18px 16px;
2515 + transition: border-color 0.18s ease, transform 0.18s ease;
2516 +}
2517 +
2518 +.dm-agents__cast-card:hover,
2519 +.dm-agents__cast-card:focus-within {
2520 + border-color: var( --os-ui-border-strong, #8c8f94 );
2521 + transform: translateY( -2px );
2522 +}
2523 +
2524 +/* The dashed door, same as the mockup's crew-new card. */
2525 +.dm-agents__cast-new {
2526 + border-style: dashed;
2527 + --os-ui-card-bg: var( --os-ui-surface-subtle, transparent );
2528 +}
2529 +
2530 +/* The crew breathes. Transform only, staggered so five faces do not
2531 + bob in lockstep, and gone entirely under reduced motion. */
2532 +@media ( prefers-reduced-motion: no-preference ) {
2533 + .dm-agents__cast-card .dm-agents__cast-face {
2534 + animation: dm-agents-bob 6s ease-in-out infinite;
2535 + }
2536 + .dm-agents__cast-card:nth-child( 2n ) .dm-agents__cast-face {
2537 + animation-duration: 7.2s;
2538 + animation-delay: -1.4s;
2539 + }
2540 + .dm-agents__cast-card:nth-child( 3n ) .dm-agents__cast-face {
2541 + animation-duration: 6.6s;
2542 + animation-delay: -2.8s;
2543 + }
2544 +}
2545 +
2546 +@keyframes dm-agents-bob {
2547 + 0%,
2548 + 100% {
2549 + transform: translateY( 0 );
2550 + }
2551 + 50% {
2552 + transform: translateY( -4px );
2553 + }
2554 +}
2555 +
2556 +/* The column lives on a wrapper we own, not on the host: the slotted
2557 + children sit inside the card's own container, so a flex rule on the
2558 + host never reaches them and the role badges land wherever the
2559 + description's line count leaves them. */
2560 +.dm-agents__cast-inner {
2561 + display: flex;
2562 + flex-direction: column;
2563 + align-items: center;
2564 + text-align: center;
2565 + gap: 2px;
2566 + height: 100%;
2567 +}
2568 +
2569 +/* The faces are the loud part of this surface, and deliberately so:
2570 + agent identity is the one place the station spends the mesh budget
2571 + on something other than a hero CTA. A cast has to be legible at a
2572 + glance, which is exactly what a flat accent cannot do. */
2573 +.dm-agents__cast-face {
2574 + width: 118px;
2575 + height: 118px;
2576 + margin: -10px 0 6px;
2577 + flex: none;
2578 +}
2579 +
2580 +.dm-agents__cast-name {
2581 + font-size: 17px;
2582 + font-weight: 600;
2583 + color: var( --os-ui-fg, #1d2327 );
2584 +}
2585 +
2586 +/* The voice line, in the instrument register: this is metadata about
2587 + the agent, not prose from it. */
2588 +.dm-agents__cast-vibes {
2589 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2590 + font-size: 11.5px;
2591 + color: var( --os-ui-accent-strong, #2271b1 );
2592 +}
2593 +
2594 +.dm-agents__cast-good {
2595 + font-size: 13px;
2596 + color: var( --os-ui-fg-muted, #646970 );
2597 + margin-block: 4px 14px;
2598 + flex: 1;
2599 +}
2600 +
2601 +/* The role pill in the instrument voice: mono, uppercase, no status
2602 + dot — this is a fact about the agent, not a state to monitor. The
2603 + custom properties are the component's own theming hooks; the
2604 + inherited text properties flow into its shadow label. */
2605 +.dm-agents__cast-card os-badge {
2606 + --os-ui-badge-font: 500 11px/1.6 var( --os-ui-font-mono, ui-monospace, monospace );
2607 + --os-ui-badge-dot-size: 0;
2608 + --os-ui-badge-gap: 0;
2609 + --os-ui-badge-padding: 3px 10px;
2610 + text-transform: uppercase;
2611 + letter-spacing: 0.08em;
2612 +}
2613 +
2614 +/* The framework-off bar: the section's status and its one action, on
2615 + one line above the crew it would hire. It is deliberately not an
2616 + `os-empty-state` — that block is centred, icon-led and paragraph-
2617 + shaped, which is exactly the wall of text the cast replaced, and at
2618 + five cards tall it would push the button that undoes the situation
2619 + off the bottom of the window. `flex: none` because `.dm-agents__view`
2620 + is a scrolling flex column and the bar must not absorb the slack. */
2621 +.dm-agents__off-head {
2622 + display: flex;
2623 + align-items: center;
2624 + justify-content: space-between;
2625 + flex-wrap: wrap;
2626 + flex: none;
2627 + gap: 8px 16px;
2628 + padding: 12px 14px;
2629 + border-radius: 10px;
2630 + background: var( --os-ui-surface-elevated, #fff );
2631 + border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2632 +}
2633 +
2634 +.dm-agents__off-copy {
2635 + min-width: 0;
2636 +}
2637 +
2638 +.dm-agents__off-copy h3 {
2639 + margin: 0;
2640 +}
2641 +
2642 +.dm-agents__off-copy p {
2643 + margin: 2px 0 0;
2644 + font-size: 13px;
2645 + color: var( --os-ui-fg-muted, #646970 );
2646 +}
2647 +
2648 +/* The cast the site would get, drawn while the framework is off.
2649 + Dimmed as a whole rather than desaturated: the five hues ARE the
2650 + argument this state is making, and a greyscale filter would flatten
2651 + the crew into five identical blobs on the way to saying "not yet".
2652 + Opacity says inactive and leaves the faces telling each other apart.
2653 +
2654 + `pointer-events` is belt and braces — the cards are rendered without
2655 + `interactive` and without a click handler, so there is nothing to
2656 + press. It exists so a hover state cannot promise otherwise. */
2657 +.dm-agents__cast--preview {
2658 + opacity: 0.6;
2659 + pointer-events: none;
2660 +}
2661 +
2662 +.dm-agents__cast-new .dm-agents__cast-plus {
2663 + display: grid;
2664 + place-items: center;
2665 + width: 118px;
2666 + height: 118px;
2667 + margin: -10px 0 6px;
2668 + border-radius: 50%;
2669 + border: 1.5px dashed var( --os-ui-border-strong, #8c8f94 );
2670 + font-size: 30px;
2671 + font-weight: 300;
2672 + line-height: 1;
2673 + color: var( --os-ui-fg-muted, #646970 );
2674 +}
2675 +
2676 +/* ---------------------------------------------------------------------
2677 + The wizard
2678 + ------------------------------------------------------------------ */
2679 +
2680 +.dm-agents__wizard {
2681 + display: flex;
2682 + flex-direction: column;
2683 + gap: 10px;
2684 +}
2685 +
2686 +.dm-agents__wiz-head {
2687 + display: flex;
2688 + align-items: center;
2689 + justify-content: space-between;
2690 + gap: 8px 12px;
2691 + flex-wrap: wrap;
2692 +}
2693 +
2694 +.dm-agents__wiz-head h3 {
2695 + margin: 0;
2696 +}
2697 +
2698 +/* The trail wears the instrument voice: mono uppercase labels, 22px
2699 + dots, a thin rule between stations. All of it goes through the
2700 + component's own theming hooks; the host elements live in this light
2701 + DOM, so the per-state overrides below can select them directly. */
2702 +.dm-agents__trail {
2703 + margin-block: 6px 14px;
2704 + --os-ui-steps-gap: 10px;
2705 + --os-ui-step-gap: 7px;
2706 + --os-ui-step-chip-size: 22px;
2707 + --os-ui-step-chip-font-size: 11px;
2708 + --os-ui-step-chip-family: var( --os-ui-font-mono, ui-monospace, monospace );
2709 + --os-ui-step-title-size: 11px;
2710 + --os-ui-step-title-family: var( --os-ui-font-mono, ui-monospace, monospace );
2711 + --os-ui-step-title-transform: uppercase;
2712 + --os-ui-step-title-spacing: 0.08em;
2713 +}
2714 +
2715 +/* A station you have not reached is an outline, not a filled dot: the
2716 + mesh chip is where you ARE, and it only reads that way if it is the
2717 + only one lit. */
2718 +.dm-agents__trail os-step:not( [ done ] ):not( [ current ] ) {
2719 + --os-ui-step-chip-bg: transparent;
2720 + --os-ui-step-chip-border: 1px solid var( --os-ui-border-strong, #8c8f94 );
2721 + --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 );
2722 +}
2723 +
2724 +/* Done is quiet: a filled tick on the elevated surface, muted like its
2725 + label. It reads as history, not as a second highlight. */
2726 +.dm-agents__trail os-step[ done ] {
2727 + --os-ui-step-chip-done-bg: var( --os-ui-surface-elevated, #f0f0f1 );
2728 + --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 );
2729 +}
2730 +
2731 +.dm-agents__wiz-heading {
2732 + margin: 12px 0 2px;
2733 + font-size: 16px;
2734 + font-weight: 600;
2735 + color: var( --os-ui-fg, #1d2327 );
2736 +}
2737 +
2738 +/* The door. Five complete agents already ship with the plugin, and
2739 + until now the create flow showed them to nobody. */
2740 +.dm-agents__starters {
2741 + display: grid;
2742 + gap: 12px;
2743 + grid-template-columns: repeat( auto-fill, minmax( 148px, 1fr ) );
2744 + margin-block: 8px 16px;
2745 +}
2746 +
2747 +.dm-agents__starter {
2748 + display: flex;
2749 + flex-direction: column;
2750 + align-items: center;
2751 + text-align: center;
2752 + gap: 3px;
2753 + --os-ui-card-padding: 16px 14px;
2754 + --os-ui-card-radius: 10px;
2755 + --os-ui-card-bg: var( --os-ui-surface-subtle, transparent );
2756 + transition: border-color 0.18s ease;
2757 +}
2758 +
2759 +.dm-agents__starter:hover,
2760 +.dm-agents__starter:focus-within {
2761 + border-color: var( --os-ui-border-strong, #8c8f94 );
2762 +}
2763 +
2764 +.dm-agents__starter-face {
2765 + width: 76px;
2766 + height: 76px;
2767 + margin: -8px 0 4px;
2768 + flex: none;
2769 +}
2770 +
2771 +.dm-agents__starter-name {
2772 + font-size: 14px;
2773 + font-weight: 500;
2774 + color: var( --os-ui-fg, #1d2327 );
2775 +}
2776 +
2777 +.dm-agents__starter-vibes {
2778 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2779 + font-size: 11.5px;
2780 + color: var( --os-ui-fg-muted, #646970 );
2781 +}
2782 +
2783 +/* Meet: the portrait beside the fields, so the thing being named is
2784 + visible while it is being named. */
2785 +.dm-agents__meet {
2786 + display: grid;
2787 + gap: 20px;
2788 + grid-template-columns: 220px 1fr;
2789 + align-items: start;
2790 +}
2791 +
2792 +/* The character card. Sunken rather than subtle: the portrait is the
2793 + hero of this step, and the mockup floats it on the darkest surface
2794 + so the face's own glow does the framing. */
2795 +.dm-agents__portrait {
2796 + display: flex;
2797 + flex-direction: column;
2798 + align-items: center;
2799 + gap: 10px;
2800 + padding: 20px 14px 14px;
2801 + border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2802 + border-radius: var( --os-ui-card-radius, 10px );
2803 + background: var( --os-ui-surface-sunken, #f6f7f7 );
2804 +}
2805 +
2806 +.dm-agents__portrait-face {
2807 + width: 176px;
2808 + height: 176px;
2809 + max-width: 100%;
2810 + margin-top: -12px;
2811 +}
2812 +
2813 +/* What this face is, in two words. */
2814 +.dm-agents__portrait-chips {
2815 + display: flex;
2816 + gap: 6px;
2817 + flex-wrap: wrap;
2818 + justify-content: center;
2819 +}
2820 +
2821 +.dm-agents__faces {
2822 + display: grid;
2823 + grid-template-columns: repeat( 6, 1fr );
2824 + gap: 2px;
2825 + width: 100%;
2826 +}
2827 +
2828 +.dm-agents__face-pick {
2829 + display: grid;
2830 + place-items: center;
2831 + padding: 2px;
2832 + border: 1px solid transparent;
2833 + border-radius: 8px;
2834 + background: none;
2835 + cursor: pointer;
2836 +}
2837 +
2838 +.dm-agents__face-pick img {
2839 + width: 100%;
2840 + height: auto;
2841 + display: block;
2842 +}
2843 +
2844 +.dm-agents__face-pick:hover {
2845 + border-color: var( --os-ui-border-strong, #8c8f94 );
2846 +}
2847 +
2848 +.dm-agents__face-pick.is-picked {
2849 + border-color: var( --os-ui-accent, #2271b1 );
2850 +}
2851 +
2852 +.dm-agents__face-pick:focus-visible {
2853 + outline: var( --os-ui-focus-ring, 2px solid #2271b1 );
2854 + outline-offset: 1px;
2855 +}
2856 +
2857 +.dm-agents__meet-fields {
2858 + display: flex;
2859 + flex-direction: column;
2860 + gap: 10px;
2861 + min-width: 0;
2862 +}
2863 +
2864 +/* Launch: one card, the brand's card radius, floating on the darkest
2865 + surface like the portrait it echoes. */
2866 +.dm-agents__summary {
2867 + display: grid;
2868 + grid-template-columns: 106px 1fr;
2869 + gap: 18px;
2870 + align-items: start;
2871 + --os-ui-card-padding: 18px;
2872 + --os-ui-card-radius: 10px;
2873 + --os-ui-card-bg: var( --os-ui-surface-sunken, #f6f7f7 );
2874 +}
2875 +
2876 +.dm-agents__summary-face {
2877 + width: 106px;
2878 + height: 106px;
2879 + flex: none;
2880 +}
2881 +
2882 +.dm-agents__summary-text h4 {
2883 + margin: 0 0 2px;
2884 + font-size: 17px;
2885 +}
2886 +
2887 +.dm-agents__summary-vibes {
2888 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2889 + font-size: 11.5px;
2890 + color: var( --os-ui-accent-strong, #2271b1 );
2891 + margin: 0 0 6px;
2892 +}
2893 +
2894 +.dm-agents__summary-desc {
2895 + margin: 0;
2896 + font-size: 13px;
2897 + color: var( --os-ui-fg-muted, #646970 );
2898 +}
2899 +
2900 +.dm-agents__chips {
2901 + display: flex;
2902 + flex-wrap: wrap;
2903 + gap: 6px;
2904 + margin-block-start: 10px;
2905 +}
2906 +
2907 +/* What the agent will actually be told, under the chips. Clamped: a
2908 + summary card quotes the instructions, it does not host them. */
2909 +.dm-agents__summary-instr {
2910 + margin: 10px 0 0;
2911 + font-size: 13px;
2912 + color: var( --os-ui-fg-muted, #646970 );
2913 + display: -webkit-box;
2914 + -webkit-line-clamp: 3;
2915 + -webkit-box-orient: vertical;
2916 + overflow: hidden;
2917 +}
2918 +
2919 +/* Identity chips — role, abilities, silhouette, hue — all wear the
2920 + instrument voice: these are facts about the character, stamped
2921 + rather than said. */
2922 +.dm-agents__chips os-chip,
2923 +.dm-agents__portrait-chips os-chip {
2924 + --os-ui-chip-font-size: 10.5px;
2925 + --os-ui-chip-font-weight: 500;
2926 + --os-ui-chip-bg: transparent;
2927 + --os-ui-chip-fg: var( --os-ui-fg-muted, #646970 );
2928 + --os-ui-chip-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) );
2929 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2930 + text-transform: uppercase;
2931 + letter-spacing: 0.06em;
2932 +}
2933 +
2934 +.dm-agents__spacer {
2935 + flex: 1;
2936 +}
2937 +
2938 +@media ( max-width: 640px ) {
2939 + .dm-agents__meet,
2940 + .dm-agents__summary {
2941 + grid-template-columns: 1fr;
2942 + }
2943 + .dm-agents__portrait-face {
2944 + width: 128px;
2945 + height: 128px;
2946 + }
2947 +}
2948 +
2949 +/* The way back to the grid. The window's own breadcrumb goes UP to the
2950 + folder, which is a different journey: this one returns to the cast. */
2951 +.dm-agents__back {
2952 + align-self: flex-start;
2953 + margin-block-end: 4px;
1881 2954 }