PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.3
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.3
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
desktop-mode / assets / css / my-wordpress.css

my-wordpress.css in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.3, at assets/css/my-wordpress.css

2,446 lines 62.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * My WordPress — window-local layout.
3 *
4 * Two-pane file-explorer for browsing WordPress entities. Mirrors the
5 * Recycle Bin and Posts window CSS conventions (`os-*`
6 * BEM-ish prefix, container-relative geometry, theming via the
7 * os-variables CSS custom properties).
8 *
9 */
10
11 .desktop-mode-my-wordpress {
12 display: flex;
13 flex-direction: column;
14 width: 100%;
15 height: 100%;
16 min-height: 0;
17 font-family: var(
18 --os-font,
19 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif
20 );
21 color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
22 /* Window background — separate from the wallpaper bg so the
23 * folder window reads as a real OS window even when the desktop
24 * gradient is dark. Themes / plugins can override
25 * `--os-my-wordpress-bg` to repaint just this surface
26 * without touching every other window. */
27 background: var(
28 --os-my-wordpress-bg,
29 var( --os-window-bg, #fff )
30 );
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,
49 rgba( 0, 0, 0, 0.06 )
50 );
51 /* Light-context label rendering — same recipe as the folder
52 * window. Defined as tokens in `variables.css`; the single
53 * `.os-file-tile__label` rule reads them for every
54 * surface. */
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 ) );
59 }
60
61 /* Pinned-icon visual cue — used by `os-icon--pinned` (the
62 * generic flag added in 0.8.0). The icon-rail itself owns the
63 * non-draggable invariant; this rule just removes the grab affordance
64 * if the framework ever attaches one in a later phase.
65 */
66 .os-icon--pinned {
67 cursor: pointer;
68 }
69
70 /* Breadcrumb header chrome — owned by the shared
71 * `.os-breadcrumbs` rules in `desktop-files.css`. No
72 * per-surface duplication. */
73
74 /* ----- Body host ----- */
75
76 .os-my-wordpress__body {
77 flex: 1 1 auto;
78 min-height: 0;
79 overflow: hidden;
80 position: relative;
81 display: flex;
82 flex-direction: column;
83 }
84
85 /* List-view toolbar — sits between the breadcrumb header and the
86 * split pane in every entity list view (Posts, Pages, Users, Media).
87 * Holds the server-side search input today; reserved for future
88 * filter / sort controls. Visual treatment mirrors the Content
89 * Graph toolbar's search row so the two surfaces read as the same
90 * control. */
91 .os-my-wordpress__list-toolbar {
92 flex: 0 0 auto;
93 display: flex;
94 align-items: center;
95 gap: 8px;
96 padding: 8px 12px;
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 );
103 }
104
105 .os-my-wordpress__list-toolbar-search {
106 position: relative;
107 flex: 1 1 auto;
108 min-width: 0;
109 max-width: 360px;
110 }
111
112 .os-my-wordpress__list-toolbar-search-input {
113 width: 100%;
114 padding: 6px 10px;
115 border: 1px solid var( --os-ui-border, #d8dde4 );
116 border-radius: 6px;
117 background: var( --os-ui-surface, #fff );
118 color: inherit;
119 font-size: 13px;
120 box-sizing: border-box;
121 }
122
123 .os-my-wordpress__list-toolbar-search-input:focus {
124 outline: none;
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 ) );
127 }
128
129 /* When the list-toolbar mounts, the split pane needs to claim the
130 * remaining vertical space inside the flex column body — without
131 * this rule it would size to its content (which, for an empty list,
132 * is zero) and the tile canvas would collapse. */
133 .os-my-wordpress__body > .os-my-wordpress__split {
134 flex: 1 1 auto;
135 min-height: 0;
136 }
137
138 /* Search-in-progress dimming. The previous result set stays in place
139 * (no skeleton flash, no empty intermediate page) but reads as
140 * "stale" until the new page lands and the renderer atomically swaps
141 * the tiles. `pointer-events: none` blocks the user from clicking
142 * a tile that's about to disappear. */
143 .os-my-wordpress__tiles--searching,
144 .os-my-wordpress__media-grid--searching {
145 opacity: 0.45;
146 pointer-events: none;
147 transition: opacity 0.12s ease-out;
148 }
149
150 /* Reuse the file-system folder window's status-bar element
151 * (`.os-folder-status-bar`) so segments render with the
152 * exact same DOM the file-system uses — but retone it for the
153 * white window background. The dark default (white-on-dark) reads
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 );
157 background: transparent;
158 color: var( --os-ui-fg-muted, #50575e );
159 }
160
161 .desktop-mode-my-wordpress
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 ) ) );
165 }
166
167 /* ----- Tile canvas (root + entity list) -----
168 *
169 * Both views use the SAME tile element (`.os-file-tile`)
170 * the wallpaper uses, so visuals stay 1:1 with the desktop. The
171 * canvas is `position: relative`; tiles are absolute-positioned and
172 * draggable — same interaction pattern as the wallpaper, just
173 * scoped to the window. Positions persist per-window in
174 * `localStorage` (see `createTileLayout` in src/my-wordpress).
175 */
176
177 .os-my-wordpress__canvas {
178 position: relative;
179 min-height: 100%;
180 box-sizing: border-box;
181 /* Note — DO NOT add `overflow: auto` here. Scroll is owned by
182 * the surrounding pane (`__list` for entity views, `__body` for
183 * the root view via `__grid` below). A second scroller here
184 * would steal the wheel from the outer pane and break the
185 * IntersectionObserver-based infinite scroll. */
186 }
187
188 .os-my-wordpress__grid {
189 height: 100%;
190 overflow-y: scroll;
191 }
192
193 /* Override the wallpaper file-tile palette for in-window context.
194 * On the wallpaper the file tile sits on a dark gradient and uses
195 * white text + a 12% white hover — both of which would be invisible
196 * on a white window background. Inside My WordPress we re-tone the
197 * same element to read on light surfaces. The dimensions / tile
198 * structure are inherited from `.os-file-tile`.
199 */
200 .desktop-mode-my-wordpress .os-file-tile {
201 /* Color + hover come from `--os-tile-*` — see the
202 * scope-level variable overrides on `.desktop-mode-my-wordpress`
203 * above. We only override structural / animation bits here.
204 *
205 * Drop the wallpaper's `transition: transform` — it hints at
206 * GPU compositing in Chrome, and the moment the layer is
207 * composited the label rasterizes against fractional device
208 * pixels (sub-pixel blur on HiDPI). We don't animate transform
209 * here, so the transition is dead code anyway. */
210 transition: background-color 0.15s ease;
211 transform: none;
212 }
213
214 /* Selected state for every tile in My WordPress is rendered by
215 * the canonical `.os-file-tile--selected` rule in
216 * `desktop-files.css` — same accent border + tinted background
217 * the folder windows + wallpaper tiles use. No section-specific
218 * override here — visual parity across the whole shell.
219 */
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
387 /* Lock state — another user is currently editing this post. We dim
388 * the tile a touch and overlay a small dashicon-lock badge on the
389 * top-right of the icon. The whole tile stays clickable; the lock
390 * is just a visual heads-up so the user knows the takeover dialog
391 * is coming if they double-click. */
392 .os-my-wordpress__tile--locked {
393 opacity: 0.85;
394 }
395
396 .os-my-wordpress__tile-lock {
397 position: absolute;
398 top: 4px;
399 inset-inline-end: 14px;
400 width: 18px;
401 height: 18px;
402 font-size: 12px;
403 line-height: 18px;
404 text-align: center;
405 border-radius: 50%;
406 background: var( --os-danger, var( --os-ui-danger, #b32d2e ) );
407 color: var( --os-ui-fg-on-accent, #fff );
408 box-shadow:
409 0 0 0 1.5px rgba( 0, 0, 0, 0.4 ),
410 0 1px 3px rgba( 0, 0, 0, 0.35 );
411 pointer-events: none;
412 }
413
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 {
419 display: flex;
420 align-items: center;
421 gap: 6px;
422 margin: 0 0 8px;
423 padding: 6px 8px;
424 border-radius: 4px;
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 ) );
430 font-size: 12px;
431 font-weight: 600;
432 }
433
434 .os-my-wordpress__tooltip-lock .dashicons {
435 font-size: 14px;
436 width: 14px;
437 height: 14px;
438 }
439
440 /* Plain two-line clamp without `-webkit-line-clamp`. The webkit
441 * variant promotes the label to its own composited layer, which on
442 * HiDPI screens snaps text to fractional device pixels and renders
443 * blurry. `max-height: 2 × line-height` + `overflow: hidden` looks
444 * the same and stays sharp.
445 */
446 .os-my-wordpress__tile .os-file-tile__label {
447 font-size: 12px;
448 line-height: 1.3;
449 max-height: calc( 1.3em * 2 );
450 text-align: center;
451 overflow: hidden;
452 max-width: 100%;
453 word-break: break-word;
454 }
455
456 /* ----- Entity list: two-pane split ----- */
457
458 .os-my-wordpress__split {
459 display: grid;
460 grid-template-columns: minmax( 240px, 60% ) minmax( 0, 1fr );
461 height: 100%;
462 min-height: 0;
463 }
464
465 .os-my-wordpress__list {
466 overflow-y: scroll;
467 border-inline-end: 1px solid var( --os-ui-border, #dcdcde );
468 min-width: 0;
469 }
470
471 /* Always-visible scrollbar styling for the icon canvases. macOS
472 * defaults to overlay scrollbars that hide until a wheel/scroll
473 * event — fine for documents but actively confusing on a tile
474 * canvas where the user can't tell whether more icons live below
475 * or to the right. We force the scrollbar to render at all times
476 * via `::-webkit-scrollbar` (works in WebKit / Blink / Edge), and
477 * the explicit `scrollbar-width: thin` covers Firefox. */
478 .os-my-wordpress__list,
479 .os-my-wordpress__grid {
480 scrollbar-width: thin;
481 scrollbar-color: rgba( 0, 0, 0, 0.25 ) transparent;
482 }
483
484 .os-my-wordpress__list::-webkit-scrollbar,
485 .os-my-wordpress__grid::-webkit-scrollbar {
486 width: 12px;
487 height: 12px;
488 }
489
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 ) );
493 border-radius: 6px;
494 border: 3px solid transparent;
495 background-clip: padding-box;
496 }
497
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 ) );
501 background-clip: padding-box;
502 }
503
504 .os-my-wordpress__list::-webkit-scrollbar-track,
505 .os-my-wordpress__grid::-webkit-scrollbar-track {
506 background: transparent;
507 }
508
509 .os-my-wordpress__sentinel {
510 height: 1px;
511 }
512
513 /* Tile-grid loading skeleton — placeholder tiles that mimic the
514 * icon + label shape of what's about to load. We dropped the
515 * spinning WordPress mark here because it read as an alien
516 * affordance inside the file-explorer surface; a skeleton signals
517 * "tiles are landing in these slots" instead of "the system is
518 * busy doing something opaque."
519 *
520 * Each placeholder is parented directly to the positioned canvas
521 * (`__canvas--positioned`) and `position: absolute` at the
522 * (left, top) the next real tile will occupy — see
523 * `layout.peekNextCells()` and `showLoadingSkeleton()` in
524 * `src/my-wordpress/index.ts`. Per-tile shimmer delay and label
525 * width are inlined so a row of placeholders reads as a cascade,
526 * not a uniform pulse.
527 *
528 * Tile dimensions mirror the real `.os-file-tile` width
529 * so a placeholder sits cleanly in the cell its successor will
530 * inherit.
531 */
532 .os-my-wordpress__skeleton-tile {
533 position: absolute;
534 display: flex;
535 flex-direction: column;
536 align-items: center;
537 gap: 6px;
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 );
542 padding: 8px 4px;
543 box-sizing: border-box;
544 pointer-events: none;
545 }
546
547 .os-my-wordpress__skeleton-icon,
548 .os-my-wordpress__skeleton-label {
549 background: linear-gradient(
550 90deg,
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%
554 );
555 background-size: 200% 100%;
556 animation: os-my-wordpress-skeleton-shimmer 1.4s ease-in-out
557 var( --os-skeleton-delay, 0s ) infinite;
558 }
559
560 .os-my-wordpress__skeleton-icon {
561 width: 44px;
562 height: 44px;
563 border-radius: 10px;
564 }
565
566 .os-my-wordpress__skeleton-label {
567 width: 72%;
568 height: 10px;
569 border-radius: 4px;
570 }
571
572 @keyframes os-my-wordpress-skeleton-shimmer {
573 from {
574 background-position: 200% 0;
575 }
576 to {
577 background-position: -200% 0;
578 }
579 }
580
581 @media ( prefers-reduced-motion: reduce ) {
582 .os-my-wordpress__skeleton-icon,
583 .os-my-wordpress__skeleton-label {
584 animation: none;
585 }
586 }
587
588 /* ----- Right pane: preview ----- */
589
590 .os-my-wordpress__preview {
591 overflow: auto;
592 min-width: 0;
593 background: transparent;
594 }
595
596 .os-my-wordpress__preview-empty {
597 display: flex;
598 flex-direction: column;
599 gap: 6px;
600 align-items: center;
601 justify-content: center;
602 height: 100%;
603 color: var( --os-ui-fg-muted, #787c82 );
604 font-size: 13px;
605 padding: 24px;
606 text-align: center;
607 }
608
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 {
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;
634 align-items: center;
635 justify-content: center;
636 height: 100%;
637 min-height: 320px;
638 padding: 32px;
639 box-sizing: border-box;
640 }
641
642 /* Preview-pane loader — same scale curve as the tile-grid first
643 * spinner and the window-loading overlay. The JS used to hard-code
644 * `size="128"`, which set an inline `--os-ui-spinner-size` that
645 * overrode any CSS knob; the size attribute is now omitted so this
646 * rule is the single source of truth for both the My WordPress
647 * preview and the wallpaper file-preview pane (which reuses this
648 * class deliberately).
649 */
650 .os-my-wordpress__preview-loading os-spinner {
651 --os-ui-spinner-size: clamp( 96px, 14vw, 192px );
652 }
653
654 .os-my-wordpress__article {
655 padding: 24px 32px;
656 max-width: 720px;
657 margin: 0 auto;
658 }
659
660 .os-my-wordpress__article-title {
661 font-size: 22px;
662 margin: 0 0 8px;
663 line-height: 1.3;
664 }
665
666 .os-my-wordpress__article-meta {
667 margin: 0 0 16px;
668 color: var( --os-ui-fg-muted, #50575e );
669 font-size: 12px;
670 }
671
672 .os-my-wordpress__article-hero {
673 width: 100%;
674 height: auto;
675 border-radius: 6px;
676 margin-bottom: 16px;
677 display: block;
678 }
679
680 .os-my-wordpress__article-content {
681 font-size: 14px;
682 line-height: 1.6;
683 }
684
685 .os-my-wordpress__article-content img {
686 max-width: 100%;
687 height: auto;
688 }
689
690 .os-my-wordpress__article-footer {
691 margin-top: 24px;
692 padding-top: 16px;
693 border-top: 1px solid var( --os-ui-border, #dcdcde );
694 display: flex;
695 justify-content: flex-end;
696 align-items: center;
697 gap: 8px;
698 flex-wrap: wrap;
699 }
700
701 /* ----- Empty / error states ----- */
702
703 .os-my-wordpress__empty,
704 .os-my-wordpress__error {
705 padding: 32px 24px;
706 text-align: center;
707 color: var( --os-ui-fg-muted, #787c82 );
708 font-size: 13px;
709 }
710
711 .os-my-wordpress__error {
712 color: var( --os-danger, var( --os-ui-danger, #b32d2e ) );
713 }
714
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. */
730
731 .os-my-wordpress__tooltip {
732 position: fixed;
733 z-index: 100000;
734 max-width: 320px;
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 );
750 border-radius: 6px;
751 padding: 12px;
752 box-shadow: var(
753 --os-my-wordpress-card-shadow,
754 0 6px 24px rgba( 0, 0, 0, 0.18 )
755 );
756 font-size: 12px;
757 pointer-events: none;
758 }
759
760 .os-my-wordpress__tooltip-title {
761 font-weight: 600;
762 margin-bottom: 6px;
763 font-size: 13px;
764 }
765
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 {
771 display: block;
772 max-width: 100%;
773 height: auto;
774 border-radius: 4px;
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 );
781 }
782
783 .os-my-wordpress__tooltip-excerpt {
784 margin: 0;
785 color: var( --os-my-wordpress-card-fg-muted, #50575e );
786 line-height: 1.4;
787 }
788
789 /* ----- Context menu host ----- */
790
791 .os-my-wordpress__menu {
792 position: fixed;
793 z-index: 100001;
794 }
795
796 /* ---------------------------------------------------------------
797 * User dossier — right-pane preview when a person is selected.
798 * Header (avatar + name + roles + links), bio, stat cards row,
799 * 12-month activity sparkline, milestones, recent posts, top
800 * categories. Designed to fit comfortably on a 360px-wide pane
801 * but to stretch nicely up to ~720px before the line-length stops
802 * helping readability.
803 * --------------------------------------------------------------- */
804
805 .os-my-wordpress__user {
806 max-width: 720px;
807 }
808
809 .os-my-wordpress__user-header {
810 display: flex;
811 gap: 16px;
812 align-items: center;
813 margin-bottom: 16px;
814 }
815
816 .os-my-wordpress__user-avatar {
817 width: 96px;
818 height: 96px;
819 border-radius: 50%;
820 flex-shrink: 0;
821 box-shadow: 0 2px 8px rgba( 0, 0, 0, 0.12 );
822 }
823
824 .os-my-wordpress__user-headline {
825 flex: 1 1 auto;
826 min-width: 0;
827 }
828
829 .os-my-wordpress__user
830 .os-my-wordpress__article-title {
831 margin: 0 0 4px;
832 }
833
834 .os-my-wordpress__user-roles {
835 display: flex;
836 flex-wrap: wrap;
837 gap: 6px;
838 margin-bottom: 6px;
839 }
840
841 .os-my-wordpress__user-role {
842 display: inline-flex;
843 align-items: center;
844 padding: 2px 8px;
845 border-radius: 10px;
846 font-size: 11px;
847 font-weight: 600;
848 text-transform: uppercase;
849 letter-spacing: 0.04em;
850 background: rgba( 34, 113, 177, 0.12 );
851 color: var( --wp-admin-theme-color, #2271b1 );
852 }
853
854 .os-my-wordpress__user-links {
855 display: flex;
856 gap: 12px;
857 font-size: 12px;
858 }
859
860 .os-my-wordpress__user-links a {
861 color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
862 text-decoration: none;
863 }
864
865 .os-my-wordpress__user-links a:hover {
866 text-decoration: underline;
867 }
868
869 .os-my-wordpress__user-bio {
870 font-size: 14px;
871 line-height: 1.5;
872 color: var( --os-fg, #1d2327 );
873 margin: 0 0 16px;
874 padding-left: 12px;
875 border-left: 3px solid var( --os-ui-border, #dcdcde );
876 }
877
878 /* ----- Stat cards row ----- */
879
880 .os-my-wordpress__user-stats {
881 display: grid;
882 grid-template-columns: repeat( auto-fit, minmax( 120px, 1fr ) );
883 gap: 8px;
884 margin-bottom: 20px;
885 }
886
887 .os-my-wordpress__user-stat {
888 display: flex;
889 flex-direction: column;
890 gap: 2px;
891 padding: 12px 14px;
892 border-radius: 8px;
893 background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ) );
894 border: 1px solid var( --os-ui-border, #dcdcde );
895 }
896
897 .os-my-wordpress__user-stat-value {
898 font-size: 26px;
899 font-weight: 700;
900 line-height: 1.1;
901 color: var( --wp-admin-theme-color, #2271b1 );
902 font-variant-numeric: tabular-nums;
903 }
904
905 .os-my-wordpress__user-stat-label {
906 font-size: 12px;
907 color: var( --os-ui-fg-muted, #50575e );
908 text-transform: uppercase;
909 letter-spacing: 0.04em;
910 }
911
912 .os-my-wordpress__user-stat-caption {
913 font-size: 11px;
914 color: var( --os-ui-fg-muted, #787c82 );
915 }
916
917 /* ----- 12-month activity sparkline ----- */
918
919 .os-my-wordpress__user-section {
920 margin-bottom: 20px;
921 }
922
923 .os-my-wordpress__user-section h3 {
924 font-size: 13px;
925 font-weight: 600;
926 color: var( --os-ui-fg-muted, #50575e );
927 text-transform: uppercase;
928 letter-spacing: 0.04em;
929 margin: 0 0 8px;
930 }
931
932 .os-my-wordpress__user-spark-chart {
933 display: grid;
934 grid-template-columns: repeat( 12, 1fr );
935 gap: 4px;
936 height: 80px;
937 align-items: end;
938 padding: 6px 0;
939 }
940
941 .os-my-wordpress__user-spark-col {
942 display: flex;
943 flex-direction: column;
944 align-items: stretch;
945 justify-content: flex-end;
946 gap: 4px;
947 height: 100%;
948 min-height: 0;
949 }
950
951 .os-my-wordpress__user-spark-bar {
952 background: linear-gradient(
953 180deg,
954 var( --wp-admin-theme-color, #2271b1 ) 0%,
955 rgba( 34, 113, 177, 0.55 ) 100%
956 );
957 border-radius: 3px 3px 0 0;
958 min-height: 2px;
959 transition: height 0.3s ease;
960 }
961
962 .os-my-wordpress__user-spark-bar--empty {
963 background: var( --os-ui-border, #dcdcde );
964 min-height: 2px;
965 height: 2px !important;
966 }
967
968 .os-my-wordpress__user-spark-label {
969 font-size: 10px;
970 text-align: center;
971 color: var( --os-ui-fg-muted, #787c82 );
972 }
973
974 /* ----- Milestones ----- */
975
976 .os-my-wordpress__user-milestones {
977 display: grid;
978 grid-template-columns: repeat( auto-fit, minmax( 140px, 1fr ) );
979 gap: 12px;
980 margin: 0 0 20px;
981 }
982
983 .os-my-wordpress__user-milestones dt {
984 font-size: 11px;
985 color: var( --os-ui-fg-muted, #50575e );
986 text-transform: uppercase;
987 letter-spacing: 0.04em;
988 margin-bottom: 2px;
989 }
990
991 .os-my-wordpress__user-milestones dd {
992 font-size: 14px;
993 font-weight: 600;
994 margin: 0;
995 }
996
997 /* ----- Recent posts list ----- */
998
999 .os-my-wordpress__user-recent {
1000 list-style: none;
1001 margin: 0;
1002 padding: 0;
1003 display: flex;
1004 flex-direction: column;
1005 gap: 6px;
1006 }
1007
1008 .os-my-wordpress__user-recent li {
1009 display: flex;
1010 flex-direction: column;
1011 gap: 2px;
1012 padding: 8px 10px;
1013 border-radius: 6px;
1014 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.02 ) );
1015 }
1016
1017 .os-my-wordpress__user-recent a {
1018 font-weight: 600;
1019 color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
1020 text-decoration: none;
1021 }
1022
1023 .os-my-wordpress__user-recent a:hover {
1024 text-decoration: underline;
1025 }
1026
1027 .os-my-wordpress__user-recent-meta {
1028 font-size: 11px;
1029 color: var( --os-ui-fg-muted, #787c82 );
1030 }
1031
1032 /* ----- Top categories chips ----- */
1033
1034 .os-my-wordpress__user-chips {
1035 display: flex;
1036 flex-wrap: wrap;
1037 gap: 6px;
1038 }
1039
1040 .os-my-wordpress__user-chip {
1041 display: inline-flex;
1042 align-items: center;
1043 gap: 6px;
1044 padding: 4px 10px;
1045 border-radius: 999px;
1046 font-size: 12px;
1047 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.05 ) );
1048 border: 1px solid var( --os-ui-border, #dcdcde );
1049 }
1050
1051 .os-my-wordpress__user-chip--tag {
1052 background: rgba( 124, 58, 237, 0.08 );
1053 border-color: rgba( 124, 58, 237, 0.25 );
1054 }
1055
1056 .os-my-wordpress__user-chip-count {
1057 font-weight: 700;
1058 color: var( --wp-admin-theme-color, #2271b1 );
1059 font-variant-numeric: tabular-nums;
1060 }
1061
1062 /* ---------------------------------------------------------------
1063 * Term dossier — same skeleton as the user dossier (header, stat
1064 * cards, sparkline, milestones, recent list, chips) plus a few
1065 * term-specific bits below: a colored taxonomy icon medallion in
1066 * the header, role badge variants for category vs tag, and a
1067 * "Top contributors" grid that pairs an author avatar with a
1068 * post count.
1069 * --------------------------------------------------------------- */
1070
1071 .os-my-wordpress__term-header {
1072 display: flex;
1073 align-items: center;
1074 gap: 16px;
1075 margin-bottom: 16px;
1076 }
1077
1078 .os-my-wordpress__term-icon {
1079 width: 64px;
1080 height: 64px;
1081 border-radius: 12px;
1082 display: inline-flex;
1083 align-items: center;
1084 justify-content: center;
1085 flex-shrink: 0;
1086 color: var( --os-ui-fg-on-accent, #fff );
1087 }
1088
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 rgba( 34, 113, 177, 0.32 );
1092 }
1093
1094 .os-my-wordpress__term-icon--tag {
1095 background: linear-gradient( 135deg, #7c3aed 0%, #5b21b6 100% );
1096 box-shadow: 0 4px 12px rgba( 124, 58, 237, 0.32 );
1097 }
1098
1099 .os-my-wordpress__user-role--category {
1100 background: rgba( 34, 113, 177, 0.12 );
1101 color: var( --os-ui-accent-strong, #135e96 );
1102 }
1103
1104 .os-my-wordpress__user-role--tag {
1105 background: rgba( 124, 58, 237, 0.12 );
1106 color: #6d28d9;
1107 }
1108
1109 /* Top contributors row — paired avatar + name + post count. */
1110 .os-my-wordpress__term-authors {
1111 display: grid;
1112 grid-template-columns: repeat( auto-fit, minmax( 180px, 1fr ) );
1113 gap: 8px;
1114 }
1115
1116 .os-my-wordpress__term-author {
1117 display: flex;
1118 align-items: center;
1119 gap: 10px;
1120 padding: 8px 12px;
1121 border-radius: 8px;
1122 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.03 ) );
1123 border: 1px solid var( --os-ui-border, #dcdcde );
1124 }
1125
1126 .os-my-wordpress__term-author-avatar {
1127 width: 36px;
1128 height: 36px;
1129 border-radius: 50%;
1130 flex-shrink: 0;
1131 }
1132
1133 .os-my-wordpress__term-author-text {
1134 display: flex;
1135 flex-direction: column;
1136 gap: 2px;
1137 min-width: 0;
1138 }
1139
1140 .os-my-wordpress__term-author-name {
1141 font-weight: 600;
1142 font-size: 13px;
1143 overflow: hidden;
1144 text-overflow: ellipsis;
1145 white-space: nowrap;
1146 }
1147
1148 .os-my-wordpress__term-author-count {
1149 font-size: 11px;
1150 color: var( --os-ui-fg-muted, #50575e );
1151 }
1152
1153 /* ---------------------------------------------------------------
1154 * Comment dossier — header (avatar + name + status / date / count
1155 * badges + author archive / website / moderate links), optional
1156 * "in reply to" quote, the comment body, parent-post card,
1157 * direct-reply thread, and a moderation strip (IP + user agent)
1158 * for users with moderate_comments.
1159 * --------------------------------------------------------------- */
1160
1161 .os-my-wordpress__comment-status--approved {
1162 background: rgba( 34, 113, 177, 0.12 );
1163 color: var( --os-ui-accent-strong, #135e96 );
1164 }
1165
1166 .os-my-wordpress__comment-status--pending {
1167 background: rgba( 217, 119, 6, 0.16 );
1168 color: #b45309;
1169 }
1170
1171 .os-my-wordpress__comment-status--spam {
1172 background: rgba( 179, 45, 46, 0.14 );
1173 color: var( --os-ui-danger-hover, #b32d2e );
1174 }
1175
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 );
1179 }
1180
1181 .os-my-wordpress__comment-date-badge {
1182 background: transparent;
1183 color: var( --os-ui-fg-muted, #50575e );
1184 border: 1px solid var( --os-ui-border, #dcdcde );
1185 text-transform: none;
1186 letter-spacing: normal;
1187 }
1188
1189 /* "In reply to ..." quote shown above the comment body when this
1190 * is a thread reply. */
1191 .os-my-wordpress__comment-quote {
1192 margin: 0 0 16px;
1193 padding: 10px 14px;
1194 border-left: 3px solid var( --wp-admin-theme-color, #2271b1 );
1195 background: rgba( 34, 113, 177, 0.06 );
1196 border-radius: 0 6px 6px 0;
1197 font-size: 13px;
1198 }
1199
1200 .os-my-wordpress__comment-quote-lead {
1201 font-size: 11px;
1202 font-weight: 600;
1203 color: var( --os-ui-fg-muted, #50575e );
1204 text-transform: uppercase;
1205 letter-spacing: 0.04em;
1206 margin-bottom: 4px;
1207 }
1208
1209 .os-my-wordpress__comment-quote p {
1210 margin: 0;
1211 color: var( --os-fg, #1d2327 );
1212 }
1213
1214 /* Comment body — same style as article-content but with a soft
1215 * card background so it reads as the focal element. */
1216 .os-my-wordpress__comment-body {
1217 padding: 14px 16px;
1218 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.03 ) );
1219 border-radius: 8px;
1220 border: 1px solid var( --os-ui-border, #dcdcde );
1221 }
1222
1223 /* Parent-post card under the comment body. */
1224 .os-my-wordpress__comment-post {
1225 display: flex;
1226 flex-direction: column;
1227 gap: 4px;
1228 padding: 10px 14px;
1229 border-radius: 8px;
1230 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) );
1231 border: 1px solid var( --os-ui-border, #dcdcde );
1232 }
1233
1234 .os-my-wordpress__comment-post-title {
1235 font-weight: 600;
1236 color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
1237 text-decoration: none;
1238 }
1239
1240 .os-my-wordpress__comment-post-title:hover {
1241 text-decoration: underline;
1242 }
1243
1244 .os-my-wordpress__comment-post-meta {
1245 font-size: 11px;
1246 color: var( --os-ui-fg-muted, #787c82 );
1247 }
1248
1249 /* Replies list. */
1250 .os-my-wordpress__comment-replies {
1251 list-style: none;
1252 margin: 0;
1253 padding: 0;
1254 display: flex;
1255 flex-direction: column;
1256 gap: 10px;
1257 }
1258
1259 .os-my-wordpress__comment-reply {
1260 display: flex;
1261 gap: 10px;
1262 align-items: flex-start;
1263 padding: 10px 12px;
1264 border-radius: 8px;
1265 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.02 ) );
1266 border: 1px solid var( --os-ui-border, #dcdcde );
1267 }
1268
1269 .os-my-wordpress__comment-reply-avatar {
1270 width: 32px;
1271 height: 32px;
1272 border-radius: 50%;
1273 flex-shrink: 0;
1274 }
1275
1276 .os-my-wordpress__comment-reply-text {
1277 display: flex;
1278 flex-direction: column;
1279 gap: 2px;
1280 min-width: 0;
1281 }
1282
1283 .os-my-wordpress__comment-reply-head {
1284 display: flex;
1285 align-items: baseline;
1286 gap: 8px;
1287 flex-wrap: wrap;
1288 }
1289
1290 .os-my-wordpress__comment-reply-name {
1291 font-weight: 600;
1292 font-size: 13px;
1293 }
1294
1295 .os-my-wordpress__comment-reply-when {
1296 font-size: 11px;
1297 color: var( --os-ui-fg-muted, #787c82 );
1298 }
1299
1300 .os-my-wordpress__comment-reply-excerpt {
1301 margin: 0;
1302 font-size: 13px;
1303 color: var( --os-fg, #1d2327 );
1304 line-height: 1.45;
1305 }
1306
1307 /* =================================================================== *
1308 * USERS — list tiles + activity footprint surface.
1309 *
1310 * The Users folder reuses the same canvas + 96×92 tile grid the
1311 * post tiles use; the user-tile content (avatar circle + label +
1312 * small sub-line) fits inside the cell. The "View activity
1313 * footprint" surface is a full-width body view (no split pane)
1314 * with hero / stats / heatmap / rhythm / timeline.
1315 *
1316 * Since 0.20.0.
1317 * =================================================================== */
1318
1319 /* User tile — replaces the dashicon glyph with a real avatar */
1320 .os-my-wordpress__tile--user {
1321 display: flex;
1322 flex-direction: column;
1323 align-items: center;
1324 justify-content: flex-start;
1325 gap: 4px;
1326 padding: 8px 4px 6px;
1327 box-sizing: border-box;
1328 }
1329
1330 .os-my-wordpress__user-tile-avatar {
1331 display: inline-flex;
1332 align-items: center;
1333 justify-content: center;
1334 width: 44px;
1335 height: 44px;
1336 border-radius: 50%;
1337 overflow: hidden;
1338 background: linear-gradient(
1339 135deg,
1340 rgba( 34, 113, 177, 0.18 ),
1341 rgba( 34, 113, 177, 0.04 )
1342 );
1343 box-shadow:
1344 inset 0 0 0 1px rgba( 0, 0, 0, 0.08 ),
1345 0 2px 6px rgba( 0, 0, 0, 0.08 );
1346 flex: 0 0 auto;
1347 }
1348
1349 .os-my-wordpress__user-tile-avatar img {
1350 width: 100%;
1351 height: 100%;
1352 object-fit: cover;
1353 display: block;
1354 }
1355
1356 .os-my-wordpress__user-tile-initials {
1357 font-weight: 600;
1358 font-size: 14px;
1359 letter-spacing: 0.02em;
1360 color: var( --wp-admin-theme-color, #2271b1 );
1361 text-transform: uppercase;
1362 }
1363
1364 /* Single-line label for user tiles — we already have a generic
1365 * label clamp, but the user variant gets only ONE line so the
1366 * sub-line ("Editor · 12 posts") can fit beneath it. */
1367 .os-my-wordpress__tile--user .os-file-tile__label {
1368 font-size: 11px;
1369 font-weight: 500;
1370 line-height: 1.25;
1371 max-height: 1.25em;
1372 white-space: nowrap;
1373 overflow: hidden;
1374 text-overflow: ellipsis;
1375 width: 100%;
1376 padding: 0 4px;
1377 box-sizing: border-box;
1378 }
1379
1380 .os-my-wordpress__user-tile-sub {
1381 font-size: 10px;
1382 line-height: 1.2;
1383 color: var( --os-tile-fg-muted, rgba( 0, 0, 0, 0.55 ) );
1384 max-height: 1.2em;
1385 white-space: nowrap;
1386 overflow: hidden;
1387 text-overflow: ellipsis;
1388 width: 100%;
1389 text-align: center;
1390 padding: 0 4px;
1391 box-sizing: border-box;
1392 }
1393
1394 /* Selected/hover user tiles inherit the same chrome the post tiles
1395 * use — see `.os-my-wordpress__tile--selected` above. */
1396
1397 /* ----- Activity footprint — full-width body surface -----
1398 *
1399 * Opens via the right-click context menu on a user tile (or the
1400 * dossier's "View activity footprint" button). Replaces the split
1401 * list/preview with a wide canvas so each section gets enough room
1402 * to read.
1403 */
1404
1405 .os-my-wordpress__footprint {
1406 display: flex;
1407 flex-direction: column;
1408 gap: 20px;
1409 padding: 24px 28px 32px;
1410 box-sizing: border-box;
1411 overflow-y: auto;
1412 height: 100%;
1413 /* Scroll container spans the full window width so wheel + touch
1414 * scroll work no matter where the pointer is — including in the
1415 * empty margins to the left / right of the content. The 1080px
1416 * cap is applied to each immediate child instead (below). */
1417 align-items: center;
1418 }
1419
1420 .os-my-wordpress__footprint > * {
1421 width: 100%;
1422 max-width: 1080px;
1423 }
1424
1425 .os-my-wordpress__footprint-section {
1426 border: 1px solid var( --os-ui-border, #dcdcde );
1427 border-radius: 12px;
1428 padding: 18px 20px;
1429 background: var(
1430 --os-my-wordpress-surface,
1431 var( --os-surface, var( --os-ui-surface, rgba( 255, 255, 255, 0.6 ) ) )
1432 );
1433 box-shadow: 0 1px 2px rgba( 0, 0, 0, 0.04 );
1434 /* The parent `.__footprint` is a flex column with `overflow-y:
1435 * auto`. Flex items default to `flex-shrink: 1`, so each section
1436 * would compress along the column axis when the combined natural
1437 * height exceeds the container — clipping the calendar heatmap
1438 * (the calendar section also sets `overflow-x: auto`, which
1439 * doesn't surface a vertical scrollbar). Lock the intrinsic
1440 * height so the parent scroller, not the children, handles the
1441 * overflow. */
1442 flex: 0 0 auto;
1443 }
1444
1445 .os-my-wordpress__footprint-section h3 {
1446 margin: 0 0 12px;
1447 font-size: 13px;
1448 font-weight: 600;
1449 letter-spacing: 0.04em;
1450 text-transform: uppercase;
1451 color: var( --os-ui-fg-muted, #50575e );
1452 }
1453
1454 /* ----- Hero ----- */
1455
1456 .os-my-wordpress__footprint-hero {
1457 display: flex;
1458 gap: 20px;
1459 align-items: center;
1460 padding: 24px;
1461 border-radius: 16px;
1462 background: linear-gradient(
1463 135deg,
1464 rgba( 34, 113, 177, 0.10 ) 0%,
1465 rgba( 34, 113, 177, 0.02 ) 60%,
1466 transparent 100%
1467 );
1468 border: 1px solid var( --os-ui-border, #dcdcde );
1469 }
1470
1471 .os-my-wordpress__footprint-avatar {
1472 width: 112px;
1473 height: 112px;
1474 border-radius: 50%;
1475 overflow: hidden;
1476 flex: 0 0 auto;
1477 box-shadow:
1478 inset 0 0 0 2px rgba( 255, 255, 255, 0.8 ),
1479 0 6px 24px rgba( 0, 0, 0, 0.14 );
1480 background: linear-gradient(
1481 135deg,
1482 rgba( 34, 113, 177, 0.18 ),
1483 rgba( 34, 113, 177, 0.04 )
1484 );
1485 display: inline-flex;
1486 align-items: center;
1487 justify-content: center;
1488 }
1489
1490 .os-my-wordpress__footprint-avatar img {
1491 width: 100%;
1492 height: 100%;
1493 object-fit: cover;
1494 display: block;
1495 }
1496
1497 .os-my-wordpress__footprint-avatar
1498 .os-my-wordpress__user-tile-initials {
1499 font-size: 36px;
1500 }
1501
1502 .os-my-wordpress__footprint-headline {
1503 min-width: 0;
1504 flex: 1 1 auto;
1505 display: flex;
1506 flex-direction: column;
1507 gap: 8px;
1508 }
1509
1510 .os-my-wordpress__footprint-title {
1511 margin: 0;
1512 font-size: 28px;
1513 line-height: 1.15;
1514 font-weight: 700;
1515 color: var( --os-fg, #1d2327 );
1516 }
1517
1518 .os-my-wordpress__footprint-meta {
1519 display: flex;
1520 flex-wrap: wrap;
1521 gap: 6px;
1522 align-items: center;
1523 }
1524
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 );
1528 }
1529
1530 /* ----- Headline stats row (4 cards) ----- */
1531
1532 .os-my-wordpress__footprint-stats-row {
1533 display: grid;
1534 grid-template-columns: repeat( auto-fit, minmax( 160px, 1fr ) );
1535 gap: 12px;
1536 padding: 16px 18px;
1537 }
1538
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 ) ) );
1542 box-shadow: 0 1px 4px rgba( 0, 0, 0, 0.04 );
1543 }
1544
1545 /* ----- Calendar heatmap (GitHub-style) ----- */
1546
1547 .os-my-wordpress__footprint-calendar-section {
1548 overflow-x: auto;
1549 }
1550
1551 .os-my-wordpress__footprint-calendar {
1552 display: flex;
1553 flex-direction: column;
1554 gap: 8px;
1555 /* Size to the grid itself so the legend (justify-content: flex-end
1556 * inside this same column) anchors to the grid's right edge instead
1557 * of the section's. Centered within the wider section by
1558 * `margin-inline: auto`. */
1559 width: max-content;
1560 max-width: 100%;
1561 margin-inline: auto;
1562 }
1563
1564 .os-my-wordpress__footprint-grid {
1565 display: grid;
1566 /* Row 1 holds month labels (auto height); rows 2–8 are the 7
1567 * weekdays at fixed 11px. Col 1 holds weekday labels (auto width);
1568 * subsequent cols are 11px-wide week tracks, set per-cell via
1569 * `grid-column` so the JS can place items explicitly. */
1570 grid-template-rows: auto repeat( 7, 11px );
1571 grid-template-columns: auto;
1572 grid-auto-columns: 11px;
1573 column-gap: 3px;
1574 row-gap: 3px;
1575 width: max-content;
1576 }
1577
1578 .os-my-wordpress__footprint-month,
1579 .os-my-wordpress__footprint-weekday {
1580 font-size: 10px;
1581 line-height: 11px;
1582 color: var( --os-ui-fg-muted, #50575e );
1583 white-space: nowrap;
1584 }
1585
1586 .os-my-wordpress__footprint-weekday {
1587 /* Sit flush to the right of the label gutter so each label reads
1588 * adjacent to its row. The 6px breathing room is in addition to
1589 * the grid's column-gap. */
1590 padding-inline-end: 6px;
1591 align-self: center;
1592 justify-self: end;
1593 }
1594
1595 .os-my-wordpress__footprint-month {
1596 padding-bottom: 2px;
1597 }
1598
1599 .os-my-wordpress__footprint-cell {
1600 width: 11px;
1601 height: 11px;
1602 border-radius: 2px;
1603 background: var( --os-ui-border, #ebedf0 );
1604 display: inline-block;
1605 transition: transform 0.12s ease;
1606 }
1607
1608 .os-my-wordpress__footprint-cell--pad {
1609 background: transparent;
1610 }
1611
1612 .os-my-wordpress__footprint-cell--l0 {
1613 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) );
1614 }
1615
1616 .os-my-wordpress__footprint-cell--l1 {
1617 background: rgba( 34, 113, 177, 0.25 );
1618 }
1619
1620 .os-my-wordpress__footprint-cell--l2 {
1621 background: rgba( 34, 113, 177, 0.5 );
1622 }
1623
1624 .os-my-wordpress__footprint-cell--l3 {
1625 background: rgba( 34, 113, 177, 0.75 );
1626 }
1627
1628 .os-my-wordpress__footprint-cell--l4 {
1629 background: var( --wp-admin-theme-color, #2271b1 );
1630 }
1631
1632 .os-my-wordpress__footprint-cell:hover {
1633 transform: scale( 1.4 );
1634 z-index: 1;
1635 }
1636
1637 .os-my-wordpress__footprint-legend {
1638 display: flex;
1639 gap: 4px;
1640 align-items: center;
1641 justify-content: flex-end;
1642 font-size: 11px;
1643 color: var( --os-ui-fg-muted, #50575e );
1644 }
1645
1646 .os-my-wordpress__footprint-legend-label {
1647 font-size: 11px;
1648 color: var( --os-ui-fg-muted, #50575e );
1649 padding: 0 4px;
1650 }
1651
1652 /* ----- Rhythm (weekday + hour bar charts) ----- */
1653
1654 .os-my-wordpress__footprint-rhythm-grid {
1655 display: grid;
1656 grid-template-columns: minmax( 0, 1fr ) minmax( 0, 1.7fr );
1657 gap: 24px;
1658 }
1659
1660 @container ( max-width: 720px ) {
1661 .os-my-wordpress__footprint-rhythm-grid {
1662 grid-template-columns: 1fr;
1663 }
1664 }
1665
1666 .os-my-wordpress__footprint-chart-caption {
1667 font-size: 12px;
1668 color: var( --os-ui-fg-muted, #50575e );
1669 margin-bottom: 6px;
1670 }
1671
1672 .os-my-wordpress__footprint-bars {
1673 display: flex;
1674 align-items: end;
1675 gap: 4px;
1676 height: 100px;
1677 padding-bottom: 18px;
1678 position: relative;
1679 }
1680
1681 .os-my-wordpress__footprint-bar-col {
1682 flex: 1 1 0;
1683 min-width: 0;
1684 display: flex;
1685 flex-direction: column;
1686 align-items: stretch;
1687 justify-content: flex-end;
1688 gap: 4px;
1689 height: 100%;
1690 position: relative;
1691 }
1692
1693 .os-my-wordpress__footprint-bar {
1694 background: linear-gradient(
1695 180deg,
1696 var( --wp-admin-theme-color, #2271b1 ) 0%,
1697 rgba( 34, 113, 177, 0.55 ) 100%
1698 );
1699 border-radius: 3px 3px 0 0;
1700 min-height: 2px;
1701 transition: opacity 0.2s ease;
1702 }
1703
1704 .os-my-wordpress__footprint-bar--empty {
1705 background: var( --os-ui-border, #dcdcde );
1706 height: 2px !important;
1707 min-height: 2px;
1708 }
1709
1710 .os-my-wordpress__footprint-bar-label {
1711 position: absolute;
1712 bottom: -16px;
1713 left: 0;
1714 right: 0;
1715 text-align: center;
1716 font-size: 10px;
1717 color: var( --os-ui-fg-muted, #787c82 );
1718 }
1719
1720 /* ----- Most-prolific-month callout ----- */
1721
1722 .os-my-wordpress__footprint-callout {
1723 display: flex;
1724 flex-direction: column;
1725 gap: 4px;
1726 align-items: center;
1727 text-align: center;
1728 padding: 20px 24px;
1729 background: linear-gradient(
1730 135deg,
1731 rgba( 34, 113, 177, 0.10 ),
1732 rgba( 34, 113, 177, 0.02 )
1733 );
1734 }
1735
1736 .os-my-wordpress__footprint-callout-label {
1737 font-size: 11px;
1738 text-transform: uppercase;
1739 letter-spacing: 0.08em;
1740 color: var( --os-ui-fg-muted, #50575e );
1741 font-weight: 600;
1742 }
1743
1744 .os-my-wordpress__footprint-callout-value {
1745 margin: 2px 0;
1746 font-size: 24px;
1747 font-weight: 700;
1748 color: var( --os-fg, #1d2327 );
1749 }
1750
1751 .os-my-wordpress__footprint-callout-detail {
1752 margin: 0;
1753 font-size: 13px;
1754 color: var( --os-ui-fg-muted, #50575e );
1755 max-width: 480px;
1756 }
1757
1758 /* ----- Recent-activity timeline ----- */
1759
1760 .os-my-wordpress__footprint-timeline {
1761 list-style: none;
1762 margin: 0;
1763 padding: 0;
1764 position: relative;
1765 display: flex;
1766 flex-direction: column;
1767 gap: 12px;
1768 }
1769
1770 .os-my-wordpress__footprint-timeline::before {
1771 content: '';
1772 position: absolute;
1773 inset-inline-start: 13px;
1774 top: 4px;
1775 bottom: 4px;
1776 width: 2px;
1777 background: var( --os-ui-border, #dcdcde );
1778 border-radius: 1px;
1779 }
1780
1781 .os-my-wordpress__footprint-event {
1782 display: flex;
1783 gap: 12px;
1784 align-items: flex-start;
1785 position: relative;
1786 padding-inline-start: 36px;
1787 min-height: 28px;
1788 }
1789
1790 .os-my-wordpress__footprint-dot {
1791 position: absolute;
1792 inset-inline-start: 0;
1793 top: 0;
1794 width: 28px;
1795 height: 28px;
1796 border-radius: 50%;
1797 display: inline-flex;
1798 align-items: center;
1799 justify-content: center;
1800 background: var( --os-surface, var( --os-ui-surface, #fff ) );
1801 border: 2px solid var( --wp-admin-theme-color, #2271b1 );
1802 color: var( --wp-admin-theme-color, #2271b1 );
1803 }
1804
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 );
1809 }
1810
1811 .os-my-wordpress__footprint-dot .dashicons {
1812 font-size: 14px;
1813 width: 14px;
1814 height: 14px;
1815 line-height: 1;
1816 }
1817
1818 .os-my-wordpress__footprint-event-body {
1819 display: flex;
1820 flex-direction: column;
1821 gap: 2px;
1822 min-width: 0;
1823 flex: 1 1 auto;
1824 }
1825
1826 .os-my-wordpress__footprint-event-title {
1827 font-weight: 600;
1828 font-size: 13px;
1829 color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
1830 text-decoration: none;
1831 overflow: hidden;
1832 text-overflow: ellipsis;
1833 white-space: nowrap;
1834 }
1835
1836 a.os-my-wordpress__footprint-event-title:hover {
1837 text-decoration: underline;
1838 }
1839
1840 .os-my-wordpress__footprint-event-meta {
1841 font-size: 11px;
1842 color: var( --os-ui-fg-muted, #787c82 );
1843 }
1844
1845 /* ----- Footer actions ----- */
1846
1847 .os-my-wordpress__footprint-footer {
1848 display: flex;
1849 justify-content: flex-end;
1850 align-items: center;
1851 gap: 8px;
1852 flex-wrap: wrap;
1853 background: transparent;
1854 border-color: transparent;
1855 box-shadow: none;
1856 padding: 0;
1857 }
1858
1859 /* ============================================================ *
1860 * Status ribbon — diagonal corner banner for non-published tiles
1861 * (since 0.21.0)
1862 *
1863 * Classic 45° corner ribbon — same affordance as a stamp on a
1864 * document. Applied to every tile whose post status isn't
1865 * `publish` across every My WordPress section (Posts, Pages,
1866 * Media drill-in, plugin kinds).
1867 *
1868 * Implementation:
1869 * 1. Tiles with a status modifier get `overflow: hidden` so the
1870 * ribbon's overshoot clips to the tile bounds.
1871 * 2. Ribbon is a wide strip (`width: 110px`) anchored to the
1872 * top-right corner, rotated +45° so it forms a diagonal
1873 * banner across that corner. The strip extends past the
1874 * tile edges; `overflow: hidden` on the parent crops it
1875 * into a clean triangle.
1876 *
1877 * The status tiles need a positioning context. `.os-
1878 * file-tile` is `position: absolute` (canvas layout) on the
1879 * Posts/Pages browse views — that satisfies the positioning-
1880 * context requirement. For the Media drill-in usage tiles we
1881 * swap from `static` to `relative` via the per-status rule
1882 * below so the ribbon stays anchored to its own tile.
1883 * ============================================================ */
1884
1885 /* Status ribbon — the diagonal corner banner — is now drawn by
1886 * the `<os-ribbon>` web component slotted inside `<os-tile>`.
1887 * The component's own shadow-DOM styles own placement, rotation,
1888 * background, lettering, and the WP admin-theme tint. The only
1889 * surface-specific rule left here is dimming the icon on non-
1890 * published tiles so the unpublished signal carries even when
1891 * the ribbon is hidden via the OS-settings toggle. */
1892
1893 .os-my-wordpress__tile--draft,
1894 .os-my-wordpress__tile--pending,
1895 .os-my-wordpress__tile--private,
1896 .os-my-wordpress__tile--future {
1897 overflow: hidden;
1898 }
1899
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 {
1904 opacity: 0.7;
1905 }
1906
1907 /* ============================================================ *
1908 * Media section (since 0.21.0)
1909 * ============================================================ */
1910
1911 .os-my-wordpress__media-grid {
1912 display: grid;
1913 grid-template-columns: repeat( auto-fill, minmax( 120px, 1fr ) );
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 );
1920 overflow-y: auto;
1921 align-content: start;
1922 }
1923
1924 .os-my-wordpress__media-tile {
1925 /* Override the base `.os-file-tile` (position: absolute,
1926 * width: 88px) so media tiles flow inside the CSS Grid instead of
1927 * stacking at (0,0) like the absolute-positioned post/user tiles.
1928 *
1929 * `relative` (not `static`) so the tile remains a containing
1930 * block for absolutely-positioned descendants — the `::after`
1931 * shortcut-arrow badge on attachment tiles, and the
1932 * `<os-ribbon>` post-status corner ribbon — both of which
1933 * would otherwise escape to the nearest positioned ancestor
1934 * (the window, the desktop, the viewport) and render way off
1935 * the tile. */
1936 position: relative;
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;
1942 display: flex;
1943 flex-direction: column;
1944 align-items: stretch;
1945 gap: 6px;
1946 padding: 8px;
1947 background: transparent;
1948 border: 1px solid transparent;
1949 border-radius: 8px;
1950 cursor: pointer;
1951 text-align: center;
1952 min-width: 0;
1953 }
1954
1955 .os-my-wordpress__media-tile:hover {
1956 background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ) );
1957 }
1958
1959 /* Media tiles inherit the canonical `.os-file-tile--selected`
1960 * rule from `desktop-files.css` — accent inset border + tinted
1961 * background — same as every other tile in the shell. */
1962
1963 /* Override the canonical 48×48 visual/preview sizing — media tiles
1964 * want the visual to fill the full grid-cell width as a square
1965 * thumbnail (or icon-fallback square). Photos browser look, not
1966 * folder-icon look. Applies to BOTH the `<img>` (real thumbnail)
1967 * and the dashicon fallback (non-image MIME), so the two tile
1968 * shapes are visually consistent in the grid. */
1969 .os-my-wordpress__media-tile .os-file-tile__visual {
1970 display: flex;
1971 align-items: center;
1972 justify-content: center;
1973 width: 100%;
1974 height: auto;
1975 aspect-ratio: 1 / 1;
1976 overflow: hidden;
1977 border-radius: 6px;
1978 background: var(
1979 --os-media-tile-bg,
1980 rgba( 0, 0, 0, 0.05 )
1981 );
1982 /* Reset the base 32px font + 48px size since we're sizing the
1983 * inner img/icon explicitly below. */
1984 font-size: 0;
1985 line-height: 1;
1986 }
1987
1988 .os-my-wordpress__media-tile .os-file-tile__preview {
1989 width: 100%;
1990 height: 100%;
1991 object-fit: cover;
1992 border-radius: 0;
1993 }
1994
1995 .os-my-wordpress__media-tile .os-file-tile__icon {
1996 /* Centered dashicon fallback — proportional to the larger
1997 * thumbnail well so it doesn't look like a postage stamp. */
1998 width: auto;
1999 height: auto;
2000 font-size: 36px;
2001 color: var( --os-ui-fg-muted, #787c82 );
2002 }
2003
2004 .os-my-wordpress__media-tile .os-file-tile__label {
2005 font-size: 12px;
2006 line-height: 1.3;
2007 overflow: hidden;
2008 text-overflow: ellipsis;
2009 display: -webkit-box;
2010 -webkit-line-clamp: 2;
2011 -webkit-box-orient: vertical;
2012 }
2013
2014 /* ----- Media preview pane ----- */
2015
2016 .os-my-wordpress__media-pane {
2017 display: flex;
2018 flex-direction: column;
2019 gap: 16px;
2020 padding: 16px;
2021 }
2022
2023 .os-my-wordpress__media-title {
2024 font-size: 16px;
2025 font-weight: 600;
2026 margin: 0;
2027 }
2028
2029 .os-my-wordpress__media-visual {
2030 display: flex;
2031 align-items: center;
2032 justify-content: center;
2033 background: var( --os-media-visual-bg, var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ) );
2034 border-radius: 8px;
2035 min-height: 200px;
2036 max-height: 480px;
2037 overflow: hidden;
2038 }
2039
2040 .os-my-wordpress__media-image,
2041 .os-my-wordpress__media-video {
2042 max-width: 100%;
2043 max-height: 480px;
2044 object-fit: contain;
2045 }
2046
2047 .os-my-wordpress__media-fallback-icon {
2048 font-size: 96px;
2049 width: 96px;
2050 height: 96px;
2051 color: var( --os-ui-fg-muted, #787c82 );
2052 }
2053
2054 .os-my-wordpress__media-doc-link {
2055 margin-inline-start: 16px;
2056 align-self: center;
2057 }
2058
2059 .os-my-wordpress__media-audio-stack {
2060 display: flex;
2061 flex-direction: column;
2062 align-items: center;
2063 gap: 12px;
2064 padding: 24px;
2065 }
2066
2067 .os-my-wordpress__media-meta {
2068 display: grid;
2069 grid-template-columns: max-content 1fr;
2070 gap: 4px 16px;
2071 font-size: 13px;
2072 margin: 0;
2073 }
2074
2075 .os-my-wordpress__media-meta-term {
2076 color: var( --os-ui-fg-muted, #787c82 );
2077 margin: 0;
2078 }
2079
2080 .os-my-wordpress__media-meta-value {
2081 color: var( --os-ui-fg, #1d2327 );
2082 word-break: break-word;
2083 margin: 0;
2084 }
2085
2086 .os-my-wordpress__media-actions {
2087 display: flex;
2088 flex-wrap: wrap;
2089 gap: 8px;
2090 padding-top: 8px;
2091 border-top: 1px solid var( --os-ui-border, #dcdcde );
2092 }
2093
2094 /* ----- Media drill-in ----- */
2095
2096 /* Drill-in: referencing-posts list. Visually identical to the
2097 * Posts / Pages tiles — same `os-file-tile` chrome (48px
2098 * dashicon + 88px-wide two-line label). We can't reuse the
2099 * absolute-positioning canvas layout from `renderEntityList` here
2100 * (those internals don't export), so this surface uses a flex-wrap
2101 * of fixed-width tiles to reproduce the look.
2102 *
2103 * NOTE: this rule INTENTIONALLY overrides the `__media-grid`
2104 * defaults (which target square thumbnails on 130px columns) so
2105 * usage tiles match the canvas-flowed post tiles instead.
2106 */
2107 .os-my-wordpress__usage-grid {
2108 display: flex;
2109 flex-wrap: wrap;
2110 gap: var( --os-grid-gap-y, 16px ) var( --os-grid-gap-x, 20px );
2111 padding: var( --os-grid-padding, 16px );
2112 align-content: flex-start;
2113 }
2114
2115 /* Reset the `__media-tile` overrides for usage tiles so they
2116 * inherit the unmodified `.os-file-tile` chrome the
2117 * canvas-positioned post tiles use — except for `position: static`
2118 * so they participate in the flex layout. */
2119 .os-my-wordpress__tile--usage {
2120 width: var( --os-tile-w, 88px );
2121 align-items: center;
2122 padding: 8px 4px;
2123 gap: 6px;
2124 }
2125
2126 /* Right-pane summary bar sits ABOVE the inline media preview. */
2127 .os-my-wordpress__media-detail-summary-bar {
2128 padding: 12px 16px;
2129 border-bottom: 1px solid var( --os-ui-border, #dcdcde );
2130 }
2131
2132 .os-my-wordpress__media-detail-summary {
2133 margin: 0;
2134 color: var( --os-ui-fg-muted, #787c82 );
2135 font-size: 13px;
2136 }
2137
2138 .os-my-wordpress__media-detail-preview {
2139 /* Lets the embedded `__media-pane` keep its own padding without
2140 * fighting the summary bar's spacing. */
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 /* The framework is opt-in but the section is always listed, so with
2160 the option off the surface paints inert rather than vanishing.
2161 Only the sidebar dims: the detail pane holds the empty state
2162 explaining why, and the one button that undoes it — dimming the way
2163 out along with everything else is how a disabled screen becomes a
2164 dead end. */
2165 .dm-agents.is-disabled .dm-agents__sidebar {
2166 opacity: 0.6;
2167 }
2168
2169 /* Section-level loading — same scale curve as the My WordPress
2170 preview loader and the window-loading overlay, instead of the bare
2171 48px component default. */
2172 .dm-agents__loading {
2173 display: flex;
2174 align-items: center;
2175 justify-content: center;
2176 height: 100%;
2177 min-height: 320px;
2178 }
2179
2180 .dm-agents__loading os-spinner {
2181 --os-ui-spinner-size: clamp( 96px, 14vw, 192px );
2182 }
2183
2184 .dm-agents__layout {
2185 display: flex;
2186 flex: 1;
2187 min-height: 0;
2188 gap: 12px;
2189 }
2190
2191 /* Sidebar column: a fixed "Create agent" header over the scrolling
2192 list. The button used to be the list's last child, which put it out
2193 of reach on any site with enough agents to need it. */
2194 .dm-agents__sidebar {
2195 display: flex;
2196 flex-direction: column;
2197 flex: 0 0 260px;
2198 /* `min-width: auto` on a flex item resolves to its min-content
2199 width, and the rows carry long `white-space: nowrap` descriptions
2200 — without this the column ignores its 260px basis and eats the
2201 detail pane. The scroll container used to be this item, whose
2202 non-visible overflow zeroed the minimum for free. */
2203 min-width: 0;
2204 min-height: 0;
2205 border-inline-end: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2206 }
2207
2208 .dm-agents__list-toolbar {
2209 flex: none;
2210 display: flex;
2211 padding-block-end: 8px;
2212 padding-inline-end: 8px;
2213 }
2214
2215 .dm-agents__list-toolbar .dm-agents__create {
2216 flex: 1;
2217 }
2218
2219 .dm-agents__list {
2220 display: flex;
2221 flex-direction: column;
2222 gap: 4px;
2223 flex: 1;
2224 min-height: 0;
2225 overflow-y: auto;
2226 padding-inline-end: 4px;
2227 }
2228
2229 .dm-agents__row {
2230 display: flex;
2231 align-items: center;
2232 gap: 8px;
2233 padding: 8px;
2234 border-radius: 8px;
2235 cursor: pointer;
2236 }
2237
2238 .dm-agents__row:hover {
2239 background: var( --os-hover, rgba( 0, 0, 0, 0.06 ) );
2240 }
2241
2242 .dm-agents__row.is-selected {
2243 background: var( --os-selected, rgba( 0, 0, 0, 0.1 ) );
2244 }
2245
2246 .dm-agents__row-avatar {
2247 width: 32px;
2248 height: 32px;
2249 border-radius: 50%;
2250 /* Placeholder behind the avatar image while it loads — follows the
2251 palette so it isn't a white disc on a dark station. */
2252 background: var( --os-ui-surface-elevated, #fff );
2253 flex: none;
2254 }
2255
2256 .dm-agents__row-text {
2257 display: flex;
2258 flex-direction: column;
2259 min-width: 0;
2260 flex: 1;
2261 }
2262
2263 .dm-agents__row-name {
2264 font-weight: 600;
2265 white-space: nowrap;
2266 overflow: hidden;
2267 text-overflow: ellipsis;
2268 }
2269
2270 .dm-agents__row-desc {
2271 font-size: 12px;
2272 opacity: 0.65;
2273 white-space: nowrap;
2274 overflow: hidden;
2275 text-overflow: ellipsis;
2276 }
2277
2278
2279 .dm-agents__detail {
2280 flex: 1;
2281 min-width: 0;
2282 overflow-y: auto;
2283 display: flex;
2284 flex-direction: column;
2285 gap: 10px;
2286 }
2287
2288 /* `<os-empty-state>` owns the icon + copy and centres them within
2289 itself; the host is still a flex item that shrinks to its content,
2290 so without this it sits at the top of a pane that is mostly empty
2291 space. Auto margins on both axes absorb the slack — placement only,
2292 same as the comments window. Covers the framework-off state and
2293 "No agents yet" alike. */
2294 .dm-agents__detail > os-empty-state {
2295 margin: auto;
2296 }
2297
2298 .dm-agents__detail-head {
2299 display: flex;
2300 align-items: center;
2301 gap: 10px;
2302 }
2303
2304 .dm-agents__detail-avatar {
2305 width: 48px;
2306 height: 48px;
2307 border-radius: 50%;
2308 /* Placeholder behind the avatar image while it loads — follows the
2309 palette so it isn't a white disc on a dark station. */
2310 background: var( --os-ui-surface-elevated, #fff );
2311 flex: none;
2312 }
2313
2314 .dm-agents__detail-title {
2315 flex: 1;
2316 min-width: 0;
2317 }
2318
2319 .dm-agents__detail-title h3 {
2320 margin: 0;
2321 font-size: 16px;
2322 }
2323
2324 .dm-agents__detail-slug {
2325 font-size: 12px;
2326 opacity: 0.6;
2327 }
2328
2329 /* Verbs get their own row under the identity block: the set grows
2330 (profile, contributions, desktop, chat, delete) and squeezing them
2331 beside the name truncated it first. */
2332 .dm-agents__detail-actions {
2333 display: flex;
2334 flex-wrap: wrap;
2335 gap: 8px;
2336 }
2337
2338 .dm-agents__tabs {
2339 display: flex;
2340 gap: 4px;
2341 border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2342 }
2343
2344 .dm-agents__tab {
2345 appearance: none;
2346 background: none;
2347 border: none;
2348 border-bottom: 2px solid transparent;
2349 padding: 6px 10px;
2350 font: inherit;
2351 color: inherit;
2352 cursor: pointer;
2353 opacity: 0.7;
2354 }
2355
2356 .dm-agents__tab.is-active {
2357 border-bottom-color: var( --os-accent, #2271b1 );
2358 opacity: 1;
2359 font-weight: 600;
2360 }
2361
2362 .dm-agents__pane {
2363 display: flex;
2364 flex-direction: column;
2365 gap: 10px;
2366 padding-bottom: 12px;
2367 }
2368
2369 /* Pane-level loading: same big, centered WordPress mark the section
2370 loader uses, instead of a bare 48px spinner pinned to the top-left
2371 of the pane. */
2372 .dm-agents__pane--loading {
2373 align-items: center;
2374 justify-content: center;
2375 flex: 1;
2376 min-height: 260px;
2377 }
2378
2379 .dm-agents__pane--loading os-spinner {
2380 --os-ui-spinner-size: clamp( 96px, 12vw, 160px );
2381 }
2382
2383 .dm-agents__hint {
2384 margin: 0;
2385 font-size: 12px;
2386 opacity: 0.7;
2387 }
2388
2389 .dm-agents__category {
2390 margin: 8px 0 0;
2391 font-size: 12px;
2392 text-transform: uppercase;
2393 letter-spacing: 0.04em;
2394 opacity: 0.6;
2395 }
2396
2397 .dm-agents__ability {
2398 display: grid;
2399 grid-template-columns: 1fr auto;
2400 gap: 2px 8px;
2401 align-items: center;
2402 padding: 4px 0;
2403 }
2404
2405 .dm-agents__ability-desc {
2406 grid-column: 1 / -1;
2407 margin: 0;
2408 font-size: 12px;
2409 opacity: 0.6;
2410 }
2411
2412 .dm-agents__trigger {
2413 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2414 border-radius: 8px;
2415 padding: 8px 10px;
2416 display: flex;
2417 flex-direction: column;
2418 gap: 6px;
2419 }
2420
2421 .dm-agents__trigger-head {
2422 display: flex;
2423 align-items: center;
2424 gap: 8px;
2425 }
2426
2427 .dm-agents__trigger-summary {
2428 flex: 1;
2429 font-size: 12px;
2430 opacity: 0.65;
2431 white-space: nowrap;
2432 overflow: hidden;
2433 text-overflow: ellipsis;
2434 }
2435
2436 .dm-agents__trigger-config {
2437 display: flex;
2438 flex-wrap: wrap;
2439 gap: 4px 14px;
2440 }
2441
2442 .dm-agents__actions {
2443 display: flex;
2444 gap: 8px;
2445 }
2446