PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.13
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.13
1.1.13 1.1.12 1.1.11 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 All 37 releases
desktop-mode / assets / css / my-wordpress.css

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

2,965 lines 77.5 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: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, transparent );
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-my-wordpress-fg, var( --os-ui-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 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 55%, transparent ) 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 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 32%, transparent );
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: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, transparent );
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: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, transparent );
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: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 6%, transparent );
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-my-wordpress-fg, var( --os-ui-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-my-wordpress-fg, var( --os-ui-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 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 18%, transparent ),
1341 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 4%, transparent )
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 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 10%, transparent ) 0%,
1465 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 2%, transparent ) 60%,
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 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 18%, transparent ),
1483 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 4%, transparent )
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 /* Window text reads the app's own foreground, never `--os-fg`: that token
1511 * is the shell's text ON THE DESK, so a light theme (Legacy) leaves it
1512 * white and the name vanished into the white hero. */
1513 .os-my-wordpress__footprint-title {
1514 margin: 0;
1515 font-size: 28px;
1516 line-height: 1.15;
1517 font-weight: 700;
1518 color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
1519 }
1520
1521 .os-my-wordpress__footprint-meta {
1522 display: flex;
1523 flex-wrap: wrap;
1524 gap: 6px;
1525 align-items: center;
1526 }
1527
1528 .os-my-wordpress__footprint-since {
1529 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) );
1530 color: var( --os-ui-fg-muted, #50575e );
1531 }
1532
1533 /* ----- Headline stats row (4 cards) ----- */
1534
1535 .os-my-wordpress__footprint-stats-row {
1536 display: grid;
1537 grid-template-columns: repeat( auto-fit, minmax( 160px, 1fr ) );
1538 gap: 12px;
1539 padding: 16px 18px;
1540 }
1541
1542 .os-my-wordpress__footprint-stats-row
1543 .os-my-wordpress__user-stat {
1544 background: var( --os-surface, var( --os-ui-surface, rgba( 255, 255, 255, 0.9 ) ) );
1545 box-shadow: 0 1px 4px rgba( 0, 0, 0, 0.04 );
1546 }
1547
1548 /* ----- Calendar heatmap (GitHub-style) ----- */
1549
1550 .os-my-wordpress__footprint-calendar-section {
1551 overflow-x: auto;
1552 }
1553
1554 .os-my-wordpress__footprint-calendar {
1555 display: flex;
1556 flex-direction: column;
1557 gap: 8px;
1558 /* Size to the grid itself so the legend (justify-content: flex-end
1559 * inside this same column) anchors to the grid's right edge instead
1560 * of the section's. Centered within the wider section by
1561 * `margin-inline: auto`. */
1562 width: max-content;
1563 max-width: 100%;
1564 margin-inline: auto;
1565 }
1566
1567 .os-my-wordpress__footprint-grid {
1568 display: grid;
1569 /* Row 1 holds month labels (auto height); rows 2–8 are the 7
1570 * weekdays at fixed 11px. Col 1 holds weekday labels (auto width);
1571 * subsequent cols are 11px-wide week tracks, set per-cell via
1572 * `grid-column` so the JS can place items explicitly. */
1573 grid-template-rows: auto repeat( 7, 11px );
1574 grid-template-columns: auto;
1575 grid-auto-columns: 11px;
1576 column-gap: 3px;
1577 row-gap: 3px;
1578 width: max-content;
1579 }
1580
1581 .os-my-wordpress__footprint-month,
1582 .os-my-wordpress__footprint-weekday {
1583 font-size: 10px;
1584 line-height: 11px;
1585 color: var( --os-ui-fg-muted, #50575e );
1586 white-space: nowrap;
1587 }
1588
1589 .os-my-wordpress__footprint-weekday {
1590 /* Sit flush to the right of the label gutter so each label reads
1591 * adjacent to its row. The 6px breathing room is in addition to
1592 * the grid's column-gap. */
1593 padding-inline-end: 6px;
1594 align-self: center;
1595 justify-self: end;
1596 }
1597
1598 .os-my-wordpress__footprint-month {
1599 padding-bottom: 2px;
1600 }
1601
1602 .os-my-wordpress__footprint-cell {
1603 width: 11px;
1604 height: 11px;
1605 border-radius: 2px;
1606 background: var( --os-ui-border, #ebedf0 );
1607 display: inline-block;
1608 transition: transform 0.12s ease;
1609 }
1610
1611 .os-my-wordpress__footprint-cell--pad {
1612 background: transparent;
1613 }
1614
1615 .os-my-wordpress__footprint-cell--l0 {
1616 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) );
1617 }
1618
1619 .os-my-wordpress__footprint-cell--l1 {
1620 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 25%, transparent );
1621 }
1622
1623 .os-my-wordpress__footprint-cell--l2 {
1624 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 50%, transparent );
1625 }
1626
1627 .os-my-wordpress__footprint-cell--l3 {
1628 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 75%, transparent );
1629 }
1630
1631 .os-my-wordpress__footprint-cell--l4 {
1632 background: var( --wp-admin-theme-color, #2271b1 );
1633 }
1634
1635 .os-my-wordpress__footprint-cell:hover {
1636 transform: scale( 1.4 );
1637 z-index: 1;
1638 }
1639
1640 .os-my-wordpress__footprint-legend {
1641 display: flex;
1642 gap: 4px;
1643 align-items: center;
1644 justify-content: flex-end;
1645 font-size: 11px;
1646 color: var( --os-ui-fg-muted, #50575e );
1647 }
1648
1649 .os-my-wordpress__footprint-legend-label {
1650 font-size: 11px;
1651 color: var( --os-ui-fg-muted, #50575e );
1652 padding: 0 4px;
1653 }
1654
1655 /* ----- Rhythm (weekday + hour bar charts) ----- */
1656
1657 .os-my-wordpress__footprint-rhythm-grid {
1658 display: grid;
1659 grid-template-columns: minmax( 0, 1fr ) minmax( 0, 1.7fr );
1660 gap: 24px;
1661 }
1662
1663 @container ( max-width: 720px ) {
1664 .os-my-wordpress__footprint-rhythm-grid {
1665 grid-template-columns: 1fr;
1666 }
1667 }
1668
1669 .os-my-wordpress__footprint-chart-caption {
1670 font-size: 12px;
1671 color: var( --os-ui-fg-muted, #50575e );
1672 margin-bottom: 6px;
1673 }
1674
1675 .os-my-wordpress__footprint-bars {
1676 display: flex;
1677 align-items: end;
1678 gap: 4px;
1679 height: 100px;
1680 padding-bottom: 18px;
1681 position: relative;
1682 }
1683
1684 .os-my-wordpress__footprint-bar-col {
1685 flex: 1 1 0;
1686 min-width: 0;
1687 display: flex;
1688 flex-direction: column;
1689 align-items: stretch;
1690 justify-content: flex-end;
1691 gap: 4px;
1692 height: 100%;
1693 position: relative;
1694 }
1695
1696 .os-my-wordpress__footprint-bar {
1697 background: linear-gradient(
1698 180deg,
1699 var( --wp-admin-theme-color, #2271b1 ) 0%,
1700 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 55%, transparent ) 100%
1701 );
1702 border-radius: 3px 3px 0 0;
1703 min-height: 2px;
1704 transition: opacity 0.2s ease;
1705 }
1706
1707 .os-my-wordpress__footprint-bar--empty {
1708 background: var( --os-ui-border, #dcdcde );
1709 height: 2px !important;
1710 min-height: 2px;
1711 }
1712
1713 .os-my-wordpress__footprint-bar-label {
1714 position: absolute;
1715 bottom: -16px;
1716 left: 0;
1717 right: 0;
1718 text-align: center;
1719 font-size: 10px;
1720 color: var( --os-ui-fg-muted, #787c82 );
1721 }
1722
1723 /* ----- Most-prolific-month callout ----- */
1724
1725 .os-my-wordpress__footprint-callout {
1726 display: flex;
1727 flex-direction: column;
1728 gap: 4px;
1729 align-items: center;
1730 text-align: center;
1731 padding: 20px 24px;
1732 background: linear-gradient(
1733 135deg,
1734 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 10%, transparent ),
1735 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 2%, transparent )
1736 );
1737 }
1738
1739 .os-my-wordpress__footprint-callout-label {
1740 font-size: 11px;
1741 text-transform: uppercase;
1742 letter-spacing: 0.08em;
1743 color: var( --os-ui-fg-muted, #50575e );
1744 font-weight: 600;
1745 }
1746
1747 .os-my-wordpress__footprint-callout-value {
1748 margin: 2px 0;
1749 font-size: 24px;
1750 font-weight: 700;
1751 color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
1752 }
1753
1754 .os-my-wordpress__footprint-callout-detail {
1755 margin: 0;
1756 font-size: 13px;
1757 color: var( --os-ui-fg-muted, #50575e );
1758 max-width: 480px;
1759 }
1760
1761 /* ----- Recent-activity timeline ----- */
1762
1763 .os-my-wordpress__footprint-timeline {
1764 list-style: none;
1765 margin: 0;
1766 padding: 0;
1767 position: relative;
1768 display: flex;
1769 flex-direction: column;
1770 gap: 12px;
1771 }
1772
1773 .os-my-wordpress__footprint-timeline::before {
1774 content: '';
1775 position: absolute;
1776 inset-inline-start: 13px;
1777 top: 4px;
1778 bottom: 4px;
1779 width: 2px;
1780 background: var( --os-ui-border, #dcdcde );
1781 border-radius: 1px;
1782 }
1783
1784 .os-my-wordpress__footprint-event {
1785 display: flex;
1786 gap: 12px;
1787 align-items: flex-start;
1788 position: relative;
1789 padding-inline-start: 36px;
1790 min-height: 28px;
1791 }
1792
1793 .os-my-wordpress__footprint-dot {
1794 position: absolute;
1795 inset-inline-start: 0;
1796 top: 0;
1797 width: 28px;
1798 height: 28px;
1799 border-radius: 50%;
1800 display: inline-flex;
1801 align-items: center;
1802 justify-content: center;
1803 background: var( --os-surface, var( --os-ui-surface, #fff ) );
1804 border: 2px solid var( --wp-admin-theme-color, #2271b1 );
1805 color: var( --wp-admin-theme-color, #2271b1 );
1806 }
1807
1808 .os-my-wordpress__footprint-event--comment
1809 .os-my-wordpress__footprint-dot {
1810 border-color: var( --os-ui-fg-muted, #787c82 );
1811 color: var( --os-ui-fg-muted, #787c82 );
1812 }
1813
1814 .os-my-wordpress__footprint-dot .dashicons {
1815 font-size: 14px;
1816 width: 14px;
1817 height: 14px;
1818 line-height: 1;
1819 }
1820
1821 .os-my-wordpress__footprint-event-body {
1822 display: flex;
1823 flex-direction: column;
1824 gap: 2px;
1825 min-width: 0;
1826 flex: 1 1 auto;
1827 }
1828
1829 .os-my-wordpress__footprint-event-title {
1830 font-weight: 600;
1831 font-size: 13px;
1832 color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
1833 text-decoration: none;
1834 overflow: hidden;
1835 text-overflow: ellipsis;
1836 white-space: nowrap;
1837 }
1838
1839 a.os-my-wordpress__footprint-event-title:hover {
1840 text-decoration: underline;
1841 }
1842
1843 .os-my-wordpress__footprint-event-meta {
1844 font-size: 11px;
1845 color: var( --os-ui-fg-muted, #787c82 );
1846 }
1847
1848 /* ----- Footer actions ----- */
1849
1850 .os-my-wordpress__footprint-footer {
1851 display: flex;
1852 justify-content: flex-end;
1853 align-items: center;
1854 gap: 8px;
1855 flex-wrap: wrap;
1856 background: transparent;
1857 border-color: transparent;
1858 box-shadow: none;
1859 padding: 0;
1860 }
1861
1862 /* ============================================================ *
1863 * Status ribbon — diagonal corner banner for non-published tiles
1864 * (since 0.21.0)
1865 *
1866 * Classic 45° corner ribbon — same affordance as a stamp on a
1867 * document. Applied to every tile whose post status isn't
1868 * `publish` across every My WordPress section (Posts, Pages,
1869 * Media drill-in, plugin kinds).
1870 *
1871 * Implementation:
1872 * 1. Tiles with a status modifier get `overflow: hidden` so the
1873 * ribbon's overshoot clips to the tile bounds.
1874 * 2. Ribbon is a wide strip (`width: 110px`) anchored to the
1875 * top-right corner, rotated +45° so it forms a diagonal
1876 * banner across that corner. The strip extends past the
1877 * tile edges; `overflow: hidden` on the parent crops it
1878 * into a clean triangle.
1879 *
1880 * The status tiles need a positioning context. `.os-
1881 * file-tile` is `position: absolute` (canvas layout) on the
1882 * Posts/Pages browse views — that satisfies the positioning-
1883 * context requirement. For the Media drill-in usage tiles we
1884 * swap from `static` to `relative` via the per-status rule
1885 * below so the ribbon stays anchored to its own tile.
1886 * ============================================================ */
1887
1888 /* Status ribbon — the diagonal corner banner — is now drawn by
1889 * the `<os-ribbon>` web component slotted inside `<os-tile>`.
1890 * The component's own shadow-DOM styles own placement, rotation,
1891 * background, lettering, and the WP admin-theme tint. The only
1892 * surface-specific rule left here is dimming the icon on non-
1893 * published tiles so the unpublished signal carries even when
1894 * the ribbon is hidden via the OS-settings toggle. */
1895
1896 .os-my-wordpress__tile--draft,
1897 .os-my-wordpress__tile--pending,
1898 .os-my-wordpress__tile--private,
1899 .os-my-wordpress__tile--future {
1900 overflow: hidden;
1901 }
1902
1903 os-tile[ status='draft' ] .os-file-tile__icon,
1904 os-tile[ status='pending' ] .os-file-tile__icon,
1905 os-tile[ status='private' ] .os-file-tile__icon,
1906 os-tile[ status='future' ] .os-file-tile__icon {
1907 opacity: 0.7;
1908 }
1909
1910 /* ============================================================ *
1911 * Media section (since 0.21.0)
1912 * ============================================================ */
1913
1914 .os-my-wordpress__media-grid {
1915 display: grid;
1916 grid-template-columns: repeat( auto-fill, minmax( 120px, 1fr ) );
1917 /* A flow grid rather than the absolute canvas, but the air
1918 * between two icons should not depend on which window you are
1919 * looking at — so the gaps and the gutter come from the same
1920 * tokens the canvas pitch is built from. */
1921 gap: var( --os-grid-gap-y, 16px ) var( --os-grid-gap-x, 20px );
1922 padding: var( --os-grid-padding, 16px );
1923 overflow-y: auto;
1924 align-content: start;
1925 }
1926
1927 .os-my-wordpress__media-tile {
1928 /* Override the base `.os-file-tile` (position: absolute,
1929 * width: 88px) so media tiles flow inside the CSS Grid instead of
1930 * stacking at (0,0) like the absolute-positioned post/user tiles.
1931 *
1932 * `relative` (not `static`) so the tile remains a containing
1933 * block for absolutely-positioned descendants — the `::after`
1934 * shortcut-arrow badge on attachment tiles, and the
1935 * `<os-ribbon>` post-status corner ribbon — both of which
1936 * would otherwise escape to the nearest positioned ancestor
1937 * (the window, the desktop, the viewport) and render way off
1938 * the tile. */
1939 position: relative;
1940 width: auto;
1941 /* …and out of the fixed tile height for the same reason: a media
1942 * tile is a square thumbnail plus a caption, sized by the grid
1943 * column it lands in, not by the icon-canvas cell. */
1944 height: auto;
1945 display: flex;
1946 flex-direction: column;
1947 align-items: stretch;
1948 gap: 6px;
1949 padding: 8px;
1950 background: transparent;
1951 border: 1px solid transparent;
1952 border-radius: 8px;
1953 cursor: pointer;
1954 text-align: center;
1955 min-width: 0;
1956 }
1957
1958 .os-my-wordpress__media-tile:hover {
1959 background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ) );
1960 }
1961
1962 /* Media tiles inherit the canonical `.os-file-tile--selected`
1963 * rule from `desktop-files.css` — accent inset border + tinted
1964 * background — same as every other tile in the shell. */
1965
1966 /* Override the canonical 48×48 visual/preview sizing — media tiles
1967 * want the visual to fill the full grid-cell width as a square
1968 * thumbnail (or icon-fallback square). Photos browser look, not
1969 * folder-icon look. Applies to BOTH the `<img>` (real thumbnail)
1970 * and the dashicon fallback (non-image MIME), so the two tile
1971 * shapes are visually consistent in the grid. */
1972 .os-my-wordpress__media-tile .os-file-tile__visual {
1973 display: flex;
1974 align-items: center;
1975 justify-content: center;
1976 width: 100%;
1977 height: auto;
1978 aspect-ratio: 1 / 1;
1979 overflow: hidden;
1980 border-radius: 6px;
1981 background: var(
1982 --os-media-tile-bg,
1983 rgba( 0, 0, 0, 0.05 )
1984 );
1985 /* Reset the base 32px font + 48px size since we're sizing the
1986 * inner img/icon explicitly below. */
1987 font-size: 0;
1988 line-height: 1;
1989 }
1990
1991 .os-my-wordpress__media-tile .os-file-tile__preview {
1992 width: 100%;
1993 height: 100%;
1994 object-fit: cover;
1995 border-radius: 0;
1996 }
1997
1998 .os-my-wordpress__media-tile .os-file-tile__icon {
1999 /* Centered dashicon fallback — proportional to the larger
2000 * thumbnail well so it doesn't look like a postage stamp. */
2001 width: auto;
2002 height: auto;
2003 font-size: 36px;
2004 color: var( --os-ui-fg-muted, #787c82 );
2005 }
2006
2007 .os-my-wordpress__media-tile .os-file-tile__label {
2008 font-size: 12px;
2009 line-height: 1.3;
2010 overflow: hidden;
2011 text-overflow: ellipsis;
2012 display: -webkit-box;
2013 -webkit-line-clamp: 2;
2014 -webkit-box-orient: vertical;
2015 }
2016
2017 /* ----- Media preview pane ----- */
2018
2019 .os-my-wordpress__media-pane {
2020 display: flex;
2021 flex-direction: column;
2022 gap: 16px;
2023 padding: 16px;
2024 }
2025
2026 .os-my-wordpress__media-title {
2027 font-size: 16px;
2028 font-weight: 600;
2029 margin: 0;
2030 }
2031
2032 .os-my-wordpress__media-visual {
2033 display: flex;
2034 align-items: center;
2035 justify-content: center;
2036 background: var( --os-media-visual-bg, var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) ) );
2037 border-radius: 8px;
2038 min-height: 200px;
2039 max-height: 480px;
2040 overflow: hidden;
2041 }
2042
2043 .os-my-wordpress__media-image,
2044 .os-my-wordpress__media-video {
2045 max-width: 100%;
2046 max-height: 480px;
2047 object-fit: contain;
2048 }
2049
2050 .os-my-wordpress__media-fallback-icon {
2051 font-size: 96px;
2052 width: 96px;
2053 height: 96px;
2054 color: var( --os-ui-fg-muted, #787c82 );
2055 }
2056
2057 .os-my-wordpress__media-doc-link {
2058 margin-inline-start: 16px;
2059 align-self: center;
2060 }
2061
2062 .os-my-wordpress__media-audio-stack {
2063 display: flex;
2064 flex-direction: column;
2065 align-items: center;
2066 gap: 12px;
2067 padding: 24px;
2068 }
2069
2070 .os-my-wordpress__media-meta {
2071 display: grid;
2072 grid-template-columns: max-content 1fr;
2073 gap: 4px 16px;
2074 font-size: 13px;
2075 margin: 0;
2076 }
2077
2078 .os-my-wordpress__media-meta-term {
2079 color: var( --os-ui-fg-muted, #787c82 );
2080 margin: 0;
2081 }
2082
2083 .os-my-wordpress__media-meta-value {
2084 color: var( --os-ui-fg, #1d2327 );
2085 word-break: break-word;
2086 margin: 0;
2087 }
2088
2089 .os-my-wordpress__media-actions {
2090 display: flex;
2091 flex-wrap: wrap;
2092 gap: 8px;
2093 padding-top: 8px;
2094 border-top: 1px solid var( --os-ui-border, #dcdcde );
2095 }
2096
2097 /* ----- Media drill-in ----- */
2098
2099 /* Drill-in: referencing-posts list. Visually identical to the
2100 * Posts / Pages tiles — same `os-file-tile` chrome (48px
2101 * dashicon + 88px-wide two-line label). We can't reuse the
2102 * absolute-positioning canvas layout from `renderEntityList` here
2103 * (those internals don't export), so this surface uses a flex-wrap
2104 * of fixed-width tiles to reproduce the look.
2105 *
2106 * NOTE: this rule INTENTIONALLY overrides the `__media-grid`
2107 * defaults (which target square thumbnails on 130px columns) so
2108 * usage tiles match the canvas-flowed post tiles instead.
2109 */
2110 .os-my-wordpress__usage-grid {
2111 display: flex;
2112 flex-wrap: wrap;
2113 gap: var( --os-grid-gap-y, 16px ) var( --os-grid-gap-x, 20px );
2114 padding: var( --os-grid-padding, 16px );
2115 align-content: flex-start;
2116 }
2117
2118 /* Reset the `__media-tile` overrides for usage tiles so they
2119 * inherit the unmodified `.os-file-tile` chrome the
2120 * canvas-positioned post tiles use — except for `position: static`
2121 * so they participate in the flex layout. */
2122 .os-my-wordpress__tile--usage {
2123 width: var( --os-tile-w, 88px );
2124 align-items: center;
2125 padding: 8px 4px;
2126 gap: 6px;
2127 }
2128
2129 /* Right-pane summary bar sits ABOVE the inline media preview. */
2130 .os-my-wordpress__media-detail-summary-bar {
2131 padding: 12px 16px;
2132 border-bottom: 1px solid var( --os-ui-border, #dcdcde );
2133 }
2134
2135 .os-my-wordpress__media-detail-summary {
2136 margin: 0;
2137 color: var( --os-ui-fg-muted, #787c82 );
2138 font-size: 13px;
2139 }
2140
2141 .os-my-wordpress__media-detail-preview {
2142 /* Lets the embedded `__media-pane` keep its own padding without
2143 * fighting the summary bar's spacing. */
2144 display: block;
2145 }
2146
2147 /* ------------------------------------------------------------------ */
2148 /* Agents section (`agent` entity kind — src/my-wordpress/agents-renderer.ts) */
2149 /* ------------------------------------------------------------------ */
2150
2151 .dm-agents {
2152 display: flex;
2153 flex-direction: column;
2154 height: 100%;
2155 min-height: 0;
2156 padding: 12px;
2157 gap: 10px;
2158 overflow: hidden;
2159 box-sizing: border-box;
2160 }
2161
2162
2163 /* Section-level loading — same scale curve as the My WordPress
2164 preview loader and the window-loading overlay, instead of the bare
2165 48px component default. */
2166 .dm-agents__loading {
2167 display: flex;
2168 align-items: center;
2169 justify-content: center;
2170 height: 100%;
2171 min-height: 320px;
2172 }
2173
2174 .dm-agents__loading os-spinner {
2175 --os-ui-spinner-size: clamp( 96px, 14vw, 192px );
2176 }
2177
2178 .dm-agents__detail {
2179 flex: 1;
2180 min-width: 0;
2181 overflow-y: auto;
2182 display: flex;
2183 flex-direction: column;
2184 gap: 10px;
2185 }
2186
2187 /* `<os-empty-state>` owns the icon + copy and centres them within
2188 itself; the host is still a flex item that shrinks to its content,
2189 so without this it sits at the top of a pane that is mostly empty
2190 space. Auto margins on both axes absorb the slack — placement only,
2191 same as the comments window. Covers the framework-off state and
2192 "No agents yet" alike. */
2193 .dm-agents__detail > os-empty-state {
2194 margin: auto;
2195 }
2196
2197 .dm-agents__detail-head {
2198 display: flex;
2199 align-items: center;
2200 gap: 10px;
2201 }
2202
2203 .dm-agents__detail-avatar {
2204 width: 48px;
2205 height: 48px;
2206 border-radius: 50%;
2207 /* Placeholder behind the avatar image while it loads — follows the
2208 palette so it isn't a white disc on a dark station. */
2209 background: var( --os-ui-surface-elevated, #fff );
2210 flex: none;
2211 }
2212
2213 .dm-agents__detail-title {
2214 flex: 1;
2215 min-width: 0;
2216 }
2217
2218 .dm-agents__detail-title h3 {
2219 margin: 0;
2220 font-size: 16px;
2221 }
2222
2223 .dm-agents__detail-slug {
2224 font-size: 12px;
2225 opacity: 0.6;
2226 }
2227
2228 /* Verbs get their own row under the identity block: the set grows
2229 (profile, contributions, desktop, chat, delete) and squeezing them
2230 beside the name truncated it first. */
2231 .dm-agents__detail-actions {
2232 display: flex;
2233 flex-wrap: wrap;
2234 gap: 8px;
2235 }
2236
2237 .dm-agents__tabs {
2238 display: flex;
2239 gap: 4px;
2240 border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2241 }
2242
2243 .dm-agents__tab {
2244 appearance: none;
2245 background: none;
2246 border: none;
2247 border-bottom: 2px solid transparent;
2248 padding: 6px 10px;
2249 font: inherit;
2250 color: inherit;
2251 cursor: pointer;
2252 opacity: 0.7;
2253 }
2254
2255 .dm-agents__tab.is-active {
2256 border-bottom-color: var( --os-accent, #2271b1 );
2257 opacity: 1;
2258 font-weight: 600;
2259 }
2260
2261 .dm-agents__pane {
2262 display: flex;
2263 flex-direction: column;
2264 gap: 10px;
2265 padding-bottom: 12px;
2266 }
2267
2268 /* Pane-level loading: same big, centered WordPress mark the section
2269 loader uses, instead of a bare 48px spinner pinned to the top-left
2270 of the pane. */
2271 .dm-agents__pane--loading {
2272 align-items: center;
2273 justify-content: center;
2274 flex: 1;
2275 min-height: 260px;
2276 }
2277
2278 .dm-agents__pane--loading os-spinner {
2279 --os-ui-spinner-size: clamp( 96px, 12vw, 160px );
2280 }
2281
2282 .dm-agents__hint {
2283 margin: 0;
2284 font-size: 12px;
2285 opacity: 0.7;
2286 }
2287
2288 /* Role first: least privilege is the decision, so the control holds
2289 the mockup's width instead of stretching into a landing strip. */
2290 .dm-agents__role-select {
2291 max-width: 280px;
2292 }
2293
2294 /* The ability filter. Sized like the role select rather than the
2295 full pane: it sits above a list of rules, and a search box as wide
2296 as the rules reads as another one of them. */
2297 .dm-agents__ability-search {
2298 display: block;
2299 max-width: 320px;
2300 margin: 12px 0 4px;
2301 }
2302
2303 /* Each category is a native disclosure. `<details>` because the
2304 behaviour is exactly what it is for: keyboard, screen-reader
2305 semantics and the open/closed state all arrive for free, and the
2306 only thing left to write is the costume. */
2307 .dm-agents__ability-group {
2308 border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2309 }
2310
2311 .dm-agents__ability-group:last-of-type {
2312 border-bottom: 0;
2313 }
2314
2315 .dm-agents__category {
2316 margin: 18px 0 2px;
2317 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2318 font-size: 11px;
2319 font-weight: 500;
2320 text-transform: uppercase;
2321 letter-spacing: 0.08em;
2322 color: var( --os-ui-fg-muted, #646970 );
2323 }
2324
2325 /* A `<summary>` is a button, so it gets a button's affordances: a
2326 pointer, a hover, and a focus ring that is visible on the dark
2327 surface the section is drawn on. */
2328 summary.dm-agents__category {
2329 display: flex;
2330 align-items: center;
2331 gap: 8px;
2332 margin: 0;
2333 padding: 10px 0;
2334 cursor: pointer;
2335 list-style: none;
2336 user-select: none;
2337 }
2338
2339 summary.dm-agents__category::-webkit-details-marker {
2340 display: none;
2341 }
2342
2343 /* The marker is drawn here rather than left to the UA so it can be
2344 the same triangle in every browser and turn with the disclosure.
2345 Rotation only: a transform, so opening a group of forty rows
2346 costs no layout. */
2347 summary.dm-agents__category::before {
2348 content: '';
2349 flex: none;
2350 width: 0;
2351 height: 0;
2352 border-inline-start: 5px solid currentcolor;
2353 border-block: 4px solid transparent;
2354 transition: transform 120ms ease;
2355 }
2356
2357 .dm-agents__ability-group[ open ] > summary.dm-agents__category::before {
2358 transform: rotate( 90deg );
2359 }
2360
2361 @media ( prefers-reduced-motion: reduce ) {
2362 summary.dm-agents__category::before {
2363 transition: none;
2364 }
2365 }
2366
2367 summary.dm-agents__category:hover {
2368 color: var( --os-ui-fg, #1e1e1e );
2369 }
2370
2371 summary.dm-agents__category:focus-visible {
2372 outline: 2px solid var( --os-ui-accent, #3858e9 );
2373 outline-offset: 2px;
2374 border-radius: 4px;
2375 }
2376
2377 .dm-agents__category-name {
2378 flex: 1 1 auto;
2379 }
2380
2381 /* "3 of 12" when some are ticked, otherwise just the size of the
2382 group. The count is why a closed group is still informative:
2383 collapsing must not hide the fact that something in there is
2384 switched on. */
2385 .dm-agents__category-count {
2386 flex: none;
2387 font-variant-numeric: tabular-nums;
2388 opacity: 0.75;
2389 }
2390
2391 /* One ability per row, ruled like the mockup: tick and name on the
2392 left, badge on the right, the description indented under the name
2393 (the checkbox column is the control's own 26px). */
2394 .dm-agents__ability {
2395 display: grid;
2396 grid-template-columns: 1fr auto;
2397 gap: 2px 10px;
2398 align-items: start;
2399 padding: 11px 0;
2400 border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2401 }
2402
2403 .dm-agents__ability:last-of-type {
2404 border-bottom: 0;
2405 }
2406
2407 .dm-agents__ability-desc {
2408 grid-column: 1 / -1;
2409 margin: 0;
2410 padding-inline-start: 26px;
2411 font-size: 12.5px;
2412 color: var( --os-ui-fg-muted, #646970 );
2413 }
2414
2415 /* Access badges in the instrument voice. The palette's own warning
2416 tokens carry the pale-yellow "can modify"; all this changes is the
2417 costume: mono, uppercase, an outline instead of a wash. */
2418 .dm-agents__ability os-badge {
2419 --os-ui-badge-font: 500 10px/1.6 var( --os-ui-font-mono, ui-monospace, monospace );
2420 --os-ui-badge-dot-size: 0;
2421 --os-ui-badge-gap: 0;
2422 --os-ui-badge-padding: 3px 8px;
2423 --os-ui-badge-bg: transparent;
2424 --os-ui-badge-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) );
2425 text-transform: uppercase;
2426 letter-spacing: 0.06em;
2427 white-space: nowrap;
2428 margin-top: 2px;
2429 }
2430
2431 .dm-agents__ability os-badge[ tone='warning' ] {
2432 --os-ui-badge-border: 1px solid
2433 var( --os-ui-warning-border, rgba( 154, 103, 0, 0.4 ) );
2434 }
2435
2436 .dm-agents__trigger {
2437 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2438 border-radius: 8px;
2439 padding: 8px 10px;
2440 display: flex;
2441 flex-direction: column;
2442 gap: 6px;
2443 }
2444
2445 .dm-agents__trigger-head {
2446 display: flex;
2447 align-items: center;
2448 gap: 8px;
2449 }
2450
2451 .dm-agents__trigger-desc {
2452 margin: 0;
2453 font-size: 12px;
2454 opacity: 0.75;
2455 }
2456
2457 .dm-agents__trigger-config {
2458 display: flex;
2459 flex-wrap: wrap;
2460 gap: 4px 14px;
2461 }
2462
2463 .dm-agents__actions {
2464 display: flex;
2465 gap: 8px;
2466 align-items: center;
2467 flex-wrap: wrap;
2468 margin-block-start: 12px;
2469 }
2470
2471 /* ---------------------------------------------------------------------
2472 The cast
2473 ------------------------------------------------------------------ */
2474
2475 /* One view at a time: the grid, an agent, or the wizard. A sidebar of
2476 faces next to a detail form would leave neither enough room, and the
2477 whole point of the grid is that the faces are big enough to tell
2478 apart. */
2479 /* The pane scrolls, and a scroll container clips ink overflow: a field
2480 flush with its edge loses the focus halo the kit paints outside the
2481 border (1px ring + 4px bloom), so the brief's ring read as a top and
2482 bottom line with nothing at the sides. Trade 5px of the section's
2483 12px padding for a gutter inside the pane; the content does not move. */
2484 .dm-agents__view {
2485 display: flex;
2486 flex-direction: column;
2487 flex: 1;
2488 min-height: 0;
2489 overflow-y: auto;
2490 gap: 12px;
2491 margin-inline: -5px;
2492 padding-inline: 5px;
2493 }
2494
2495 .dm-agents__cast-head {
2496 display: flex;
2497 align-items: baseline;
2498 gap: 8px;
2499 }
2500
2501 .dm-agents__cast-head h3 {
2502 margin: 0;
2503 }
2504
2505 .dm-agents__cast-count {
2506 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2507 font-size: 12px;
2508 color: var( --os-ui-fg-muted, #646970 );
2509 }
2510
2511 .dm-agents__cast {
2512 display: grid;
2513 gap: 16px;
2514 grid-template-columns: repeat( auto-fill, minmax( 288px, 1fr ) );
2515 align-content: start;
2516 }
2517
2518 /* The card reacts like the mockup's: a lift and a brighter edge.
2519 Transform and border-color only — both cheap, and the same pair the
2520 mockup transitions. Document styles win over the component's own
2521 :host rules, which is the sanctioned way to restyle a host. */
2522 .dm-agents__cast-card,
2523 .dm-agents__cast-new {
2524 --os-ui-card-padding: 20px 18px 16px;
2525 transition: border-color 0.18s ease, transform 0.18s ease;
2526 }
2527
2528 .dm-agents__cast-card:hover,
2529 .dm-agents__cast-card:focus-within {
2530 border-color: var( --os-ui-border-strong, #8c8f94 );
2531 transform: translateY( -2px );
2532 }
2533
2534 /* The dashed door, same as the mockup's crew-new card. */
2535 .dm-agents__cast-new {
2536 border-style: dashed;
2537 --os-ui-card-bg: var( --os-ui-surface-subtle, transparent );
2538 }
2539
2540 /* The crew breathes. Transform only, staggered so five faces do not
2541 bob in lockstep, and gone entirely under reduced motion. */
2542 @media ( prefers-reduced-motion: no-preference ) {
2543 .dm-agents__cast-card .dm-agents__cast-face {
2544 animation: dm-agents-bob 6s ease-in-out infinite;
2545 }
2546 .dm-agents__cast-card:nth-child( 2n ) .dm-agents__cast-face {
2547 animation-duration: 7.2s;
2548 animation-delay: -1.4s;
2549 }
2550 .dm-agents__cast-card:nth-child( 3n ) .dm-agents__cast-face {
2551 animation-duration: 6.6s;
2552 animation-delay: -2.8s;
2553 }
2554 }
2555
2556 @keyframes dm-agents-bob {
2557 0%,
2558 100% {
2559 transform: translateY( 0 );
2560 }
2561 50% {
2562 transform: translateY( -4px );
2563 }
2564 }
2565
2566 /* The column lives on a wrapper we own, not on the host: the slotted
2567 children sit inside the card's own container, so a flex rule on the
2568 host never reaches them and the role badges land wherever the
2569 description's line count leaves them. */
2570 .dm-agents__cast-inner {
2571 display: flex;
2572 flex-direction: column;
2573 align-items: center;
2574 text-align: center;
2575 gap: 2px;
2576 height: 100%;
2577 }
2578
2579 /* The faces are the loud part of this surface, and deliberately so:
2580 agent identity is the one place the station spends the mesh budget
2581 on something other than a hero CTA. A cast has to be legible at a
2582 glance, which is exactly what a flat accent cannot do. */
2583 .dm-agents__cast-face {
2584 width: 118px;
2585 height: 118px;
2586 margin: -10px 0 6px;
2587 flex: none;
2588 }
2589
2590 .dm-agents__cast-name {
2591 font-size: 17px;
2592 font-weight: 600;
2593 color: var( --os-ui-fg, #1d2327 );
2594 }
2595
2596 /* The voice line, in the instrument register: this is metadata about
2597 the agent, not prose from it. */
2598 .dm-agents__cast-vibes {
2599 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2600 font-size: 11.5px;
2601 color: var( --os-ui-accent-strong, #2271b1 );
2602 }
2603
2604 .dm-agents__cast-good {
2605 font-size: 13px;
2606 color: var( --os-ui-fg-muted, #646970 );
2607 margin-block: 4px 14px;
2608 flex: 1;
2609 }
2610
2611 /* The role pill in the instrument voice: mono, uppercase, no status
2612 dot — this is a fact about the agent, not a state to monitor. The
2613 custom properties are the component's own theming hooks; the
2614 inherited text properties flow into its shadow label. */
2615 .dm-agents__cast-card os-badge {
2616 --os-ui-badge-font: 500 11px/1.6 var( --os-ui-font-mono, ui-monospace, monospace );
2617 --os-ui-badge-dot-size: 0;
2618 --os-ui-badge-gap: 0;
2619 --os-ui-badge-padding: 3px 10px;
2620 text-transform: uppercase;
2621 letter-spacing: 0.08em;
2622 }
2623
2624 /* The framework-off bar: the section's status and its one action, on
2625 one line above the crew it would hire. It is deliberately not an
2626 `os-empty-state` — that block is centred, icon-led and paragraph-
2627 shaped, which is exactly the wall of text the cast replaced, and at
2628 five cards tall it would push the button that undoes the situation
2629 off the bottom of the window. `flex: none` because `.dm-agents__view`
2630 is a scrolling flex column and the bar must not absorb the slack. */
2631 .dm-agents__off-head {
2632 display: flex;
2633 align-items: center;
2634 justify-content: space-between;
2635 flex-wrap: wrap;
2636 flex: none;
2637 gap: 8px 16px;
2638 padding: 12px 14px;
2639 border-radius: 10px;
2640 background: var( --os-ui-surface-elevated, #fff );
2641 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2642 }
2643
2644 .dm-agents__off-copy {
2645 min-width: 0;
2646 }
2647
2648 .dm-agents__off-copy h3 {
2649 margin: 0;
2650 }
2651
2652 .dm-agents__off-copy p {
2653 margin: 2px 0 0;
2654 font-size: 13px;
2655 color: var( --os-ui-fg-muted, #646970 );
2656 }
2657
2658 /* The cast the site would get, drawn while the framework is off.
2659 Dimmed as a whole rather than desaturated: the five hues ARE the
2660 argument this state is making, and a greyscale filter would flatten
2661 the crew into five identical blobs on the way to saying "not yet".
2662 Opacity says inactive and leaves the faces telling each other apart.
2663
2664 `pointer-events` is belt and braces — the cards are rendered without
2665 `interactive` and without a click handler, so there is nothing to
2666 press. It exists so a hover state cannot promise otherwise. */
2667 .dm-agents__cast--preview {
2668 opacity: 0.6;
2669 pointer-events: none;
2670 }
2671
2672 .dm-agents__cast-new .dm-agents__cast-plus {
2673 display: grid;
2674 place-items: center;
2675 width: 118px;
2676 height: 118px;
2677 margin: -10px 0 6px;
2678 border-radius: 50%;
2679 border: 1.5px dashed var( --os-ui-border-strong, #8c8f94 );
2680 font-size: 30px;
2681 font-weight: 300;
2682 line-height: 1;
2683 color: var( --os-ui-fg-muted, #646970 );
2684 }
2685
2686 /* ---------------------------------------------------------------------
2687 The wizard
2688 ------------------------------------------------------------------ */
2689
2690 .dm-agents__wizard {
2691 display: flex;
2692 flex-direction: column;
2693 gap: 10px;
2694 }
2695
2696 .dm-agents__wiz-head {
2697 display: flex;
2698 align-items: center;
2699 justify-content: space-between;
2700 gap: 8px 12px;
2701 flex-wrap: wrap;
2702 }
2703
2704 .dm-agents__wiz-head h3 {
2705 margin: 0;
2706 }
2707
2708 /* The trail wears the instrument voice: mono uppercase labels, 22px
2709 dots, a thin rule between stations. All of it goes through the
2710 component's own theming hooks; the host elements live in this light
2711 DOM, so the per-state overrides below can select them directly. */
2712 .dm-agents__trail {
2713 margin-block: 6px 14px;
2714 --os-ui-steps-gap: 10px;
2715 --os-ui-step-gap: 7px;
2716 --os-ui-step-chip-size: 22px;
2717 --os-ui-step-chip-font-size: 11px;
2718 --os-ui-step-chip-family: var( --os-ui-font-mono, ui-monospace, monospace );
2719 --os-ui-step-title-size: 11px;
2720 --os-ui-step-title-family: var( --os-ui-font-mono, ui-monospace, monospace );
2721 --os-ui-step-title-transform: uppercase;
2722 --os-ui-step-title-spacing: 0.08em;
2723 }
2724
2725 /* A station you have not reached is an outline, not a filled dot: the
2726 mesh chip is where you ARE, and it only reads that way if it is the
2727 only one lit. */
2728 .dm-agents__trail os-step:not( [ done ] ):not( [ current ] ) {
2729 --os-ui-step-chip-bg: transparent;
2730 --os-ui-step-chip-border: 1px solid var( --os-ui-border-strong, #8c8f94 );
2731 --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 );
2732 }
2733
2734 /* Done is quiet: a filled tick on the elevated surface, muted like its
2735 label. It reads as history, not as a second highlight. */
2736 .dm-agents__trail os-step[ done ] {
2737 --os-ui-step-chip-done-bg: var( --os-ui-surface-elevated, #f0f0f1 );
2738 --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 );
2739 }
2740
2741 .dm-agents__wiz-heading {
2742 margin: 12px 0 2px;
2743 font-size: 16px;
2744 font-weight: 600;
2745 color: var( --os-ui-fg, #1d2327 );
2746 }
2747
2748 /* The door. Five complete agents already ship with the plugin, and
2749 until now the create flow showed them to nobody. */
2750 .dm-agents__starters {
2751 display: grid;
2752 gap: 12px;
2753 grid-template-columns: repeat( auto-fill, minmax( 148px, 1fr ) );
2754 margin-block: 8px 16px;
2755 }
2756
2757 .dm-agents__starter {
2758 display: flex;
2759 flex-direction: column;
2760 align-items: center;
2761 text-align: center;
2762 gap: 3px;
2763 --os-ui-card-padding: 16px 14px;
2764 --os-ui-card-radius: 10px;
2765 --os-ui-card-bg: var( --os-ui-surface-subtle, transparent );
2766 transition: border-color 0.18s ease;
2767 }
2768
2769 .dm-agents__starter:hover,
2770 .dm-agents__starter:focus-within {
2771 border-color: var( --os-ui-border-strong, #8c8f94 );
2772 }
2773
2774 .dm-agents__starter-face {
2775 width: 76px;
2776 height: 76px;
2777 margin: -8px 0 4px;
2778 flex: none;
2779 }
2780
2781 .dm-agents__starter-name {
2782 font-size: 14px;
2783 font-weight: 500;
2784 color: var( --os-ui-fg, #1d2327 );
2785 }
2786
2787 .dm-agents__starter-vibes {
2788 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2789 font-size: 11.5px;
2790 color: var( --os-ui-fg-muted, #646970 );
2791 }
2792
2793 /* Meet: the portrait beside the fields, so the thing being named is
2794 visible while it is being named. */
2795 .dm-agents__meet {
2796 display: grid;
2797 gap: 20px;
2798 grid-template-columns: 220px 1fr;
2799 align-items: start;
2800 }
2801
2802 /* The character card. Sunken rather than subtle: the portrait is the
2803 hero of this step, and the mockup floats it on the darkest surface
2804 so the face's own glow does the framing. */
2805 .dm-agents__portrait {
2806 display: flex;
2807 flex-direction: column;
2808 align-items: center;
2809 gap: 10px;
2810 padding: 20px 14px 14px;
2811 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2812 border-radius: var( --os-ui-card-radius, 10px );
2813 background: var( --os-ui-surface-sunken, #f6f7f7 );
2814 }
2815
2816 .dm-agents__portrait-face {
2817 width: 176px;
2818 height: 176px;
2819 max-width: 100%;
2820 margin-top: -12px;
2821 }
2822
2823 /* What this face is, in two words. */
2824 .dm-agents__portrait-chips {
2825 display: flex;
2826 gap: 6px;
2827 flex-wrap: wrap;
2828 justify-content: center;
2829 }
2830
2831 .dm-agents__faces {
2832 display: grid;
2833 grid-template-columns: repeat( 6, 1fr );
2834 gap: 2px;
2835 width: 100%;
2836 }
2837
2838 .dm-agents__face-pick {
2839 display: grid;
2840 place-items: center;
2841 padding: 2px;
2842 border: 1px solid transparent;
2843 border-radius: 8px;
2844 background: none;
2845 cursor: pointer;
2846 }
2847
2848 .dm-agents__face-pick img {
2849 width: 100%;
2850 height: auto;
2851 display: block;
2852 }
2853
2854 .dm-agents__face-pick:hover {
2855 border-color: var( --os-ui-border-strong, #8c8f94 );
2856 }
2857
2858 .dm-agents__face-pick.is-picked {
2859 border-color: var( --os-ui-accent, #2271b1 );
2860 }
2861
2862 .dm-agents__face-pick:focus-visible {
2863 outline: var( --os-ui-focus-ring, 2px solid #2271b1 );
2864 outline-offset: 1px;
2865 }
2866
2867 .dm-agents__meet-fields {
2868 display: flex;
2869 flex-direction: column;
2870 gap: 10px;
2871 min-width: 0;
2872 }
2873
2874 /* Launch: one card, the brand's card radius, floating on the darkest
2875 surface like the portrait it echoes. */
2876 .dm-agents__summary {
2877 display: grid;
2878 grid-template-columns: 106px 1fr;
2879 gap: 18px;
2880 align-items: start;
2881 --os-ui-card-padding: 18px;
2882 --os-ui-card-radius: 10px;
2883 --os-ui-card-bg: var( --os-ui-surface-sunken, #f6f7f7 );
2884 }
2885
2886 .dm-agents__summary-face {
2887 width: 106px;
2888 height: 106px;
2889 flex: none;
2890 }
2891
2892 .dm-agents__summary-text h4 {
2893 margin: 0 0 2px;
2894 font-size: 17px;
2895 }
2896
2897 .dm-agents__summary-vibes {
2898 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2899 font-size: 11.5px;
2900 color: var( --os-ui-accent-strong, #2271b1 );
2901 margin: 0 0 6px;
2902 }
2903
2904 .dm-agents__summary-desc {
2905 margin: 0;
2906 font-size: 13px;
2907 color: var( --os-ui-fg-muted, #646970 );
2908 }
2909
2910 .dm-agents__chips {
2911 display: flex;
2912 flex-wrap: wrap;
2913 gap: 6px;
2914 margin-block-start: 10px;
2915 }
2916
2917 /* What the agent will actually be told, under the chips. Clamped: a
2918 summary card quotes the instructions, it does not host them. */
2919 .dm-agents__summary-instr {
2920 margin: 10px 0 0;
2921 font-size: 13px;
2922 color: var( --os-ui-fg-muted, #646970 );
2923 display: -webkit-box;
2924 -webkit-line-clamp: 3;
2925 -webkit-box-orient: vertical;
2926 overflow: hidden;
2927 }
2928
2929 /* Identity chips — role, abilities, silhouette, hue — all wear the
2930 instrument voice: these are facts about the character, stamped
2931 rather than said. */
2932 .dm-agents__chips os-chip,
2933 .dm-agents__portrait-chips os-chip {
2934 --os-ui-chip-font-size: 10.5px;
2935 --os-ui-chip-font-weight: 500;
2936 --os-ui-chip-bg: transparent;
2937 --os-ui-chip-fg: var( --os-ui-fg-muted, #646970 );
2938 --os-ui-chip-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) );
2939 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2940 text-transform: uppercase;
2941 letter-spacing: 0.06em;
2942 }
2943
2944 .dm-agents__spacer {
2945 flex: 1;
2946 }
2947
2948 @media ( max-width: 640px ) {
2949 .dm-agents__meet,
2950 .dm-agents__summary {
2951 grid-template-columns: 1fr;
2952 }
2953 .dm-agents__portrait-face {
2954 width: 128px;
2955 height: 128px;
2956 }
2957 }
2958
2959 /* The way back to the grid. The window's own breadcrumb goes UP to the
2960 folder, which is a different journey: this one returns to the cast. */
2961 .dm-agents__back {
2962 align-self: flex-start;
2963 margin-block-end: 4px;
2964 }
2965