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

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