PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.7
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.7
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 / apps / my-wordpress / my-wordpress.css

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

2,000 lines 47.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * My WordPress — the content explorer app.
3 *
4 * Injected on the window's first open, after the runtime's own sheet.
5 * Every colour resolves through the palette tokens with the pre-brand
6 * literal as the floor, per the variables.css contract.
7 */
8
9 .os-mywp {
10 display: flex;
11 flex-direction: column;
12 block-size: 100%;
13 font-family: var(
14 --os-font,
15 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif
16 );
17 /* The SAME window-token family WP Explorer wears
18 * (`--os-my-wordpress-*`), so a desktop theme that repaints one
19 * explorer repaints both — with the palette chain underneath and
20 * the pre-brand literal as the floor. */
21 color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
22 background: var(
23 --os-my-wordpress-bg,
24 var( --os-window-bg, #fff )
25 );
26 /*
27 * Tile token re-scope — the same recipe as WP Explorer. The
28 * shared `.os-file-tile` is born on the WALLPAPER, where its
29 * label is white over a dark gradient; inside a window those
30 * defaults are invisible on a light surface. Re-pointing the
31 * `--os-tile-*` tokens here retones the one shared element for
32 * in-window reading, and keeps it on the theme chain.
33 */
34 --os-tile-fg: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
35 --os-tile-fg-muted: var(
36 --os-my-wordpress-fg-muted,
37 var( --os-ui-fg-muted, rgba( 0, 0, 0, 0.55 ) )
38 );
39 --os-tile-hover-bg: var( --os-hover, rgba( 0, 0, 0, 0.06 ) );
40 --os-tile-label-shadow: none;
41 --os-tile-label-weight: 500;
42 --os-tile-label-smoothing: antialiased;
43 --os-tile-label-color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
44 }
45
46 /* ------------------------------------------------------------ header */
47
48 .os-mywp__header {
49 display: flex;
50 align-items: center;
51 gap: 8px;
52 padding: 8px 12px;
53 border-block-end: 1px solid var( --os-ui-border, #dcdcde );
54 flex: 0 0 auto;
55 }
56
57 .os-mywp__back {
58 flex: 0 0 auto;
59 inline-size: 28px;
60 block-size: 28px;
61 display: grid;
62 place-items: center;
63 border: 0;
64 border-radius: 6px;
65 background: transparent;
66 color: var( --os-ui-fg-muted, #646970 );
67 font: inherit;
68 font-size: 18px;
69 line-height: 1;
70 cursor: pointer;
71 }
72
73 .os-mywp__back:hover,
74 .os-mywp__back:focus-visible {
75 /* The shared breadcrumb back-button hover chain
76 * (`.os-breadcrumbs__back` in desktop-files.css). */
77 background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) ) );
78 color: inherit;
79 }
80
81 .os-mywp__crumbs {
82 display: flex;
83 align-items: center;
84 gap: 8px;
85 min-inline-size: 0;
86 flex: 1 1 auto;
87 font-size: 13px;
88 }
89
90 .os-mywp__crumb-link {
91 border: 0;
92 padding: 0;
93 background: transparent;
94 font: inherit;
95 /* WP Explorer's link chain: the palette's link colour first, the
96 * accent next, the pre-brand admin blue as the floor. */
97 color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
98 cursor: pointer;
99 }
100
101 .os-mywp__crumb-link:hover,
102 .os-mywp__crumb-link:focus-visible {
103 text-decoration: underline;
104 }
105
106 .os-mywp__crumb-current {
107 font-weight: 600;
108 }
109
110 .os-mywp__sep {
111 color: var( --os-ui-fg-muted, #646970 );
112 }
113
114 /* Search row — its own band under the header, WP Explorer style.
115 * The view switch sits at its far end. */
116 .os-mywp__add-user {
117 flex: 0 0 auto;
118 }
119
120 .os-mywp__search {
121 display: flex;
122 align-items: center;
123 justify-content: space-between;
124 gap: 12px;
125 padding: 10px 14px;
126 flex: 0 0 auto;
127 border-block-end: 1px solid var( --os-ui-border, #dcdcde );
128 }
129
130 .os-mywp__search os-text-field {
131 inline-size: min( 360px, 100% );
132 display: block;
133 flex: 1 1 auto;
134 }
135
136 .os-mywp__view-switch {
137 flex: 0 0 auto;
138 }
139
140 .os-mywp__view-switch .dashicons {
141 font-size: 16px;
142 inline-size: 16px;
143 block-size: 16px;
144 vertical-align: -3px;
145 }
146
147 /* Under ~560px the switch keeps its glyphs and drops the words. */
148 @container ( max-width: 560px ) {
149 .os-mywp__view-label {
150 display: none;
151 }
152 }
153
154 /* -------------------------------------------------------------- body */
155
156 .os-mywp__body {
157 flex: 1 1 auto;
158 overflow: hidden;
159 display: flex;
160 flex-direction: column;
161 }
162
163 /* -------------------------------------------------------------- root */
164
165 /*
166 * The group-extras seam's container — a plugin panel above a folder's
167 * tiles (Woo's Store totals). The panel inside brings its own chrome;
168 * this only gives it the folder grid's gutter, and only when a
169 * subscriber actually painted something.
170 */
171 .os-mywp__body > .os-my-wordpress__group-extras:not( :empty ) {
172 padding: 16px 16px 0;
173 }
174
175 .os-mywp__root {
176 display: grid;
177 grid-template-columns: repeat( auto-fill, 116px );
178 justify-content: start;
179 gap: 8px;
180 padding: 16px;
181 overflow-y: auto;
182 align-content: start;
183 }
184
185 .os-mywp__tile {
186 display: flex;
187 flex-direction: column;
188 align-items: center;
189 gap: 8px;
190 padding: 14px 8px;
191 border: 0;
192 border-radius: 8px;
193 background: transparent;
194 color: inherit;
195 font: inherit;
196 cursor: pointer;
197 }
198
199 .os-mywp__tile:hover,
200 .os-mywp__tile:focus-visible {
201 background: var( --os-tile-hover-bg, rgba( 0, 0, 0, 0.06 ) );
202 }
203
204 /* The CANONICAL selection pair every tile in the shell wears
205 * (`.os-file-tile--selected` in desktop-files.css): the
206 * `--os-tile-selected-bg` wash under an inset `--os-tile-focus-ring`
207 * ring that resolves to the admin scheme colour. The root folder
208 * tiles are plain buttons rather than `<os-tile>`s, so they restate
209 * the same chains — reading `--os-ui-accent` here painted the raw
210 * brand Pulse instead of the theme's selection, which is exactly the
211 * divergence the shared tokens exist to prevent. */
212 .os-mywp__tile.is-selected {
213 background: var( --os-tile-selected-bg, rgba( 34, 113, 177, 0.14 ) );
214 box-shadow: inset 0 0 0 1px
215 var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) );
216 }
217
218 /* The icon well matches WP Explorer's `.os-file-tile__icon` /
219 * `__preview` pair (48px box, 32px glyph), so the two windows' root
220 * grids — the Agents portrait included — read at the same size. */
221 .os-mywp__tile-icon.dashicons {
222 inline-size: 48px;
223 block-size: 48px;
224 font-size: 32px;
225 display: grid;
226 place-items: center;
227 }
228
229 .os-mywp__icon-img {
230 inline-size: 48px;
231 block-size: 48px;
232 object-fit: contain;
233 }
234
235 .os-mywp__tile-label {
236 font-size: 12px;
237 text-align: center;
238 overflow-wrap: anywhere;
239 }
240
241 /* ------------------------------------------------------------- split */
242
243 .os-mywp__split {
244 display: grid;
245 /*
246 * WP Explorer's two-pane ratio: the list leads, the preview takes
247 * roughly the remaining 40% and grows with the window — but never
248 * below 430px while the window can afford it, because under that
249 * the pane's fact tables and the Woo merchant panels wrap every
250 * row. When the window truly can't (both floors exceed it), the
251 * list keeps its 240px and the pane takes what is left.
252 */
253 grid-template-columns: minmax( 240px, 1fr ) minmax( min( 430px, calc( 100% - 240px ) ), 40% );
254 flex: 1 1 auto;
255 min-block-size: 0;
256 }
257
258 /* Nothing open, no pane: the list has the whole window until a
259 * click needs the preview beside it (`splitView()` in
260 * `parts/dossier-views.ts` leaves the aside out entirely). */
261 .os-mywp__split--solo {
262 grid-template-columns: minmax( 0, 1fr );
263 }
264
265 .os-mywp__list-pane {
266 min-inline-size: 0;
267 /* Grid items default to min-size auto; without the 0 the row
268 grows with a tall list instead of letting the canvas scroll. */
269 min-block-size: 0;
270 display: flex;
271 flex-direction: column;
272 }
273
274 .os-mywp__detail-pane {
275 min-inline-size: 0;
276 min-block-size: 0;
277 border-inline-start: 1px solid var( --os-ui-border, #dcdcde );
278 overflow-y: auto;
279 }
280
281 /*
282 * A narrow window — a phone, or a desktop window pulled in under
283 * the two floors above (240px list + 430px pane). The panes stack:
284 * the list takes the window and the preview becomes a sheet along
285 * the bottom, at most 45% tall and scrolling on its own. It is only
286 * rendered while something is selected, so the list alone fills the
287 * single row the rest of the time. The container is the app root
288 * (`.os-app`, `app-runtime.css`).
289 */
290 @container ( max-width: 640px ) {
291 .os-mywp__split {
292 grid-template-columns: minmax( 0, 1fr );
293 grid-template-rows: minmax( 0, 1fr ) auto;
294 }
295
296 .os-mywp__detail-pane {
297 border-inline-start: 0;
298 border-block-start: 1px solid var( --os-ui-border, #dcdcde );
299 max-block-size: 45%;
300 }
301 }
302
303 /*
304 * The phone's item page (`renderBody` in `my-wordpress.os.ts`): the
305 * opened item takes the whole body, pushed over the list, with Back
306 * in the header and the trail one step longer. The pane's own ✕ is
307 * redundant beside Back and goes; the action row stacks so every
308 * button is a full-width finger's target.
309 */
310 .os-mywp__detail-page {
311 flex: 1 1 auto;
312 min-block-size: 0;
313 overflow-y: auto;
314 -webkit-overflow-scrolling: touch;
315 }
316
317 .os-mywp__detail-page .os-mywp__pane-close {
318 display: none;
319 }
320
321 .os-mywp__detail-page .os-mywp__detail {
322 padding: 16px 16px 24px;
323 }
324
325 .os-mywp__detail-page .os-mywp__actions {
326 flex-direction: column;
327 align-items: stretch;
328 align-self: stretch;
329 }
330
331 .os-mywp__detail-page .os-mywp__actions os-button {
332 inline-size: 100%;
333 }
334
335 /* -------------------------------------------------- quick-edit modal */
336
337 .os-mywp__qe {
338 display: flex;
339 flex-direction: column;
340 gap: 12px;
341 min-inline-size: 240px;
342 }
343
344 .os-mywp__qe-row {
345 display: flex;
346 flex-direction: column;
347 gap: 4px;
348 font-size: 12px;
349 }
350
351 /* The hint above the fields — the same info notice the original's
352 * bulk modal leads with. Ink contrast on the dark dialog is owned by
353 * os-modal itself (it re-points --os-ui-notice-color through
354 * --os-ui-modal-text); this rule is spacing only. */
355 .os-mywp__qe-notice {
356 margin-block-end: 12px;
357 }
358
359 .os-mywp__cell.is-disabled {
360 opacity: 0.5;
361 }
362
363 /* --------------------------------------------------------- tile grid */
364
365 /*
366 * The same tile element the wallpaper and WP Explorer paint
367 * (`os-tile` → `.os-file-tile`), flowed as a grid: in-window lists
368 * are not a free canvas here, so the wallpaper's absolute
369 * positioning is switched off and the grid owns the layout.
370 */
371 /* Fixed pitch, like the original canvas: resizing the window changes
372 * how many tiles fit per row, never how wide a tile is. */
373 .os-mywp__tiles {
374 display: grid;
375 grid-template-columns: repeat( auto-fill, 104px );
376 justify-content: start;
377 gap: 10px 4px;
378 align-content: start;
379 padding: 12px;
380 overflow-y: auto;
381 flex: 1 1 auto;
382 }
383
384 .os-mywp__cell {
385 display: flex;
386 justify-content: center;
387 }
388
389 /*
390 * `relative`, not `static`: the tile must join normal flow (the grid
391 * owns layout here, not the wallpaper's absolute canvas) but it must
392 * REMAIN the containing block of its own `<os-ribbon>` — flattened to
393 * static, the DRAFT ribbon anchors to the grid cell and floats off
394 * the tile's corner.
395 */
396 .os-mywp__cell os-tile,
397 .os-mywp__cell .os-file-tile {
398 position: relative;
399 inset: auto;
400 }
401
402 .os-mywp__tilebox {
403 position: relative;
404 display: inline-flex;
405 }
406
407 .os-mywp__cell.is-open .os-file-tile {
408 outline: 1px solid
409 var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) );
410 outline-offset: -1px;
411 border-radius: 8px;
412 }
413
414 .os-mywp__lock {
415 position: absolute;
416 inset-block-start: 2px;
417 inset-inline-start: 2px;
418 font-size: 12px;
419 pointer-events: none;
420 }
421
422 /* Skeletons for the page being fetched — WP Explorer's loading
423 * placeholders: same cell footprint, shimmering blocks. */
424 .os-mywp__cell--ghost {
425 pointer-events: none;
426 }
427
428 .os-mywp__ghost {
429 inline-size: var( --os-tile-w, 88px );
430 padding: 8px 4px;
431 display: flex;
432 flex-direction: column;
433 align-items: center;
434 gap: 6px;
435 }
436
437 /* The shell's skeleton fill chain (`--os-skeleton-*`, see WP
438 * Explorer's loading placeholders), so the ghosts follow the theme
439 * instead of guessing at a raised surface. */
440 .os-mywp__ghost-visual {
441 inline-size: 48px;
442 block-size: 48px;
443 border-radius: 8px;
444 background: var( --os-skeleton-high, var( --os-ui-hover, rgba( 0, 0, 0, 0.08 ) ) );
445 animation: os-mywp-shimmer 1.2s ease-in-out infinite;
446 }
447
448 .os-mywp__ghost-label {
449 inline-size: 64px;
450 block-size: 10px;
451 border-radius: 5px;
452 background: var( --os-skeleton-high, var( --os-ui-hover, rgba( 0, 0, 0, 0.08 ) ) );
453 animation: os-mywp-shimmer 1.2s ease-in-out infinite;
454 animation-delay: 0.15s;
455 }
456
457 @keyframes os-mywp-shimmer {
458 0%, 100% { opacity: 0.45; }
459 50% { opacity: 1; }
460 }
461
462 @media ( prefers-reduced-motion: reduce ) {
463 .os-mywp__ghost-visual,
464 .os-mywp__ghost-label {
465 animation: none;
466 }
467 }
468
469 /* --------------------------------------------------------- list view */
470
471 /*
472 * The table: the same rows as the tile canvas, in columns. The
473 * wrapper is the scrolling canvas (both axes — a narrow window scrolls
474 * the columns sideways rather than crushing them), the header sticks,
475 * and every colour resolves through the same chains the tiles read so
476 * a desktop theme repaints both views at once.
477 */
478 .os-mywp__table-wrap {
479 flex: 1 1 auto;
480 min-block-size: 0;
481 overflow: auto;
482 position: relative;
483 }
484
485 /*
486 * Auto layout with ONE stretching column: every other column is
487 * declared 1px wide, which in an auto table means "as wide as your
488 * (nowrap) content"; the title column takes what is left. Its
489 * `max-inline-size: 0` is what lets the ellipsis work — an auto
490 * layout would otherwise size the column to its longest title. No
491 * percentage widths and no `max-content` minimum on the table: a
492 * 100% column against a max-content floor resolves to an unbounded
493 * width and the wrapper scrolls forever.
494 */
495 .os-mywp__table {
496 inline-size: 100%;
497 border-collapse: separate;
498 border-spacing: 0;
499 font-size: 13px;
500 table-layout: auto;
501 }
502
503 .os-mywp__th:not( .os-mywp__th--title ),
504 .os-mywp__td:not( .os-mywp__td--title ) {
505 inline-size: 1px;
506 }
507
508 .os-mywp__th--title,
509 .os-mywp__td--title {
510 inline-size: 100%;
511 max-inline-size: 0;
512 min-inline-size: 160px;
513 }
514
515 .os-mywp__th {
516 position: sticky;
517 inset-block-start: 0;
518 z-index: 2;
519 padding: 0 10px;
520 block-size: 32px;
521 text-align: start;
522 font-size: 11px;
523 font-weight: 600;
524 letter-spacing: 0.04em;
525 text-transform: uppercase;
526 white-space: nowrap;
527 color: var( --os-ui-fg-muted, #646970 );
528 /* The band-head recipe: near-opaque frost, so rows scrolling under
529 * the header stay behind it. */
530 background: color-mix( in srgb, var( --os-ui-bg, #fff ) 94%, transparent );
531 backdrop-filter: blur( 14px ) saturate( 180% );
532 border-block-end: 1px solid var( --os-ui-border, #dcdcde );
533 }
534
535 @supports not ( background: color-mix( in srgb, red 50%, transparent ) ) {
536 .os-mywp__th {
537 background: var( --os-ui-bg, #fff );
538 }
539 }
540
541 .os-mywp__th--end,
542 .os-mywp__td--end {
543 text-align: end;
544 }
545
546 .os-mywp__th,
547 .os-mywp__td {
548 vertical-align: middle;
549 }
550
551 /* The column-chooser button sits on the header's centre line, not on
552 * its baseline. */
553 .os-mywp__th--actions {
554 inline-size: 1px;
555 line-height: 0;
556 }
557
558 .os-mywp__columns-btn {
559 vertical-align: middle;
560 }
561
562 /* A sortable heading is a button wearing the heading's costume. */
563 .os-mywp__sort {
564 display: inline-flex;
565 align-items: center;
566 gap: 4px;
567 margin: 0;
568 padding: 0;
569 border: 0;
570 background: transparent;
571 color: inherit;
572 font: inherit;
573 letter-spacing: inherit;
574 text-transform: inherit;
575 cursor: pointer;
576 }
577
578 .os-mywp__sort:hover,
579 .os-mywp__sort:focus-visible,
580 .os-mywp__sort.is-active {
581 color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
582 }
583
584 .os-mywp__sort-arrow {
585 font-size: 8px;
586 min-inline-size: 8px;
587 }
588
589 .os-mywp__row {
590 cursor: default;
591 }
592
593 .os-mywp__td {
594 padding: 0 10px;
595 block-size: 36px;
596 vertical-align: middle;
597 white-space: nowrap;
598 max-inline-size: 320px;
599 overflow: hidden;
600 text-overflow: ellipsis;
601 border-block-end: 1px solid
602 var( --os-ui-border-subtle, var( --os-ui-border, rgba( 0, 0, 0, 0.06 ) ) );
603 }
604
605 .os-mywp__td--mono,
606 .os-mywp__cell-id {
607 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
608 font-variant-numeric: tabular-nums;
609 font-size: 12px;
610 }
611
612 /* Hover, selection and the open row read the tile canvas's tokens —
613 * one selection colour in the whole window, whichever view. */
614 .os-mywp__row:hover .os-mywp__td {
615 background: var( --os-tile-hover-bg, rgba( 0, 0, 0, 0.06 ) );
616 }
617
618 .os-mywp__row.is-selected .os-mywp__td {
619 background: var( --os-tile-selected-bg, rgba( 34, 113, 177, 0.14 ) );
620 }
621
622 .os-mywp__row.is-open .os-mywp__td {
623 box-shadow: inset 0 -1px 0 var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) ),
624 inset 0 1px 0 var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) );
625 }
626
627 .os-mywp__row:focus-visible {
628 outline: none;
629 }
630
631 .os-mywp__row:focus-visible .os-mywp__td {
632 box-shadow: inset 0 0 0 1px var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) );
633 }
634
635 /* The id copies itself; it looks like a chip so the affordance reads. */
636 .os-mywp__cell-id--copy {
637 margin: 0;
638 padding: 2px 6px;
639 border: 0;
640 border-radius: 4px;
641 background: var( --os-ui-surface-raised, rgba( 0, 0, 0, 0.04 ) );
642 color: var( --os-my-wordpress-fg-muted, var( --os-ui-fg-muted, #646970 ) );
643 cursor: copy;
644 }
645
646 .os-mywp__cell-id--copy:hover,
647 .os-mywp__cell-id--copy:focus-visible {
648 background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.08 ) ) );
649 color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
650 }
651
652 .os-mywp__cell-title {
653 display: inline-flex;
654 align-items: center;
655 gap: 8px;
656 min-inline-size: 0;
657 max-inline-size: 100%;
658 }
659
660 .os-mywp__cell-text {
661 overflow: hidden;
662 text-overflow: ellipsis;
663 font-weight: 500;
664 }
665
666 .os-mywp__cell-thumb {
667 inline-size: 24px;
668 block-size: 24px;
669 border-radius: 4px;
670 object-fit: cover;
671 flex: 0 0 auto;
672 background: var( --os-ui-surface-raised, rgba( 0, 0, 0, 0.04 ) );
673 }
674
675 .os-mywp__cell-glyph.dashicons {
676 inline-size: 24px;
677 block-size: 24px;
678 font-size: 18px;
679 display: grid;
680 place-items: center;
681 color: var( --os-ui-fg-muted, #646970 );
682 flex: 0 0 auto;
683 }
684
685 .os-mywp__cell-glyph.os-mywp__icon-mask {
686 inline-size: 20px;
687 block-size: 20px;
688 }
689
690 .os-mywp__cell-badge {
691 flex: 0 0 auto;
692 }
693
694 .os-mywp__cell-lock {
695 font-size: 14px;
696 inline-size: 14px;
697 block-size: 14px;
698 color: var( --os-ui-fg-muted, #646970 );
699 }
700
701 .os-mywp__cell-muted {
702 color: var( --os-ui-fg-muted, #646970 );
703 }
704
705 .os-mywp__cell-parent .os-mywp__cell-id {
706 color: var( --os-ui-fg-muted, #646970 );
707 }
708
709 /* The action cluster: icon-only ghost buttons, visible on hover, on
710 * the selected row, and whenever one of them holds focus. */
711 .os-mywp__row-actions {
712 display: inline-flex;
713 gap: 2px;
714 opacity: 0;
715 transition: opacity 120ms ease;
716 }
717
718 .os-mywp__row:hover .os-mywp__row-actions,
719 .os-mywp__row.is-selected .os-mywp__row-actions,
720 .os-mywp__row.is-open .os-mywp__row-actions,
721 .os-mywp__row-actions:focus-within {
722 opacity: 1;
723 }
724
725 @media ( prefers-reduced-motion: reduce ) {
726 .os-mywp__row-actions {
727 transition: none;
728 }
729 }
730
731 .os-mywp__row-action {
732 --os-ui-button-padding: 3px;
733 --os-ui-button-border-radius: 4px;
734 line-height: 0;
735 }
736
737 .os-mywp__row-action .dashicons {
738 font-size: 16px;
739 inline-size: 16px;
740 block-size: 16px;
741 }
742
743 .os-mywp__columns-btn {
744 opacity: 1;
745 }
746
747 /* Skeleton rows for the page being fetched — the tile ghosts' fill. */
748 .os-mywp__row--ghost td {
749 padding: 10px;
750 border-block-end: 1px solid
751 var( --os-ui-border-subtle, var( --os-ui-border, rgba( 0, 0, 0, 0.06 ) ) );
752 }
753
754 .os-mywp__ghost-line {
755 display: block;
756 block-size: 12px;
757 inline-size: min( 60%, 420px );
758 border-radius: 6px;
759 background: var( --os-skeleton-high, var( --os-ui-hover, rgba( 0, 0, 0, 0.08 ) ) );
760 animation: os-mywp-shimmer 1.2s ease-in-out infinite;
761 }
762
763 @media ( prefers-reduced-motion: reduce ) {
764 .os-mywp__ghost-line {
765 animation: none;
766 }
767 }
768
769 .os-mywp__table-empty {
770 margin: 0;
771 padding: 32px 16px;
772 text-align: center;
773 color: var( --os-ui-fg-muted, #646970 );
774 }
775
776 /* ------------------------------------------------------- list bands */
777
778 /* Banded layout — the `os.my-wordpress.list-bands` filter's canvas.
779 * The scroller becomes a column of band sections, each with its own
780 * grid at the same pitch as the flat canvas; headings mirror WP
781 * Explorer's band bars (sticky, frosted, tone-tintable). */
782
783 .os-mywp__tiles--banded {
784 display: flex;
785 flex-direction: column;
786 gap: 4px;
787 }
788
789 .os-mywp__band + .os-mywp__band {
790 margin-block-start: 8px;
791 }
792
793 .os-mywp__band-head {
794 display: flex;
795 align-items: center;
796 gap: 8px;
797 position: sticky;
798 /* Keeps the band label visible while scrolling a long band —
799 * the whole point of banding is knowing what you're looking at. */
800 inset-block-start: 0;
801 z-index: 3;
802 margin: 0;
803 padding: 6px 12px;
804 font-size: 11px;
805 font-weight: 600;
806 letter-spacing: 0.06em;
807 text-transform: uppercase;
808 color: var( --os-ui-fg-muted, #787c82 );
809 /* Near-opaque frosted bar, WP Explorer's recipe: at 94% the tiles
810 * scrolling beneath stay behind the bar instead of resolving into
811 * ghostly shapes through it. */
812 background: color-mix(
813 in srgb,
814 var( --os-ui-bg, #fff ) 94%,
815 transparent
816 );
817 backdrop-filter: blur( 14px ) saturate( 180% );
818 border-block-end: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) );
819 }
820
821 @supports not ( background: color-mix( in srgb, red 50%, transparent ) ) {
822 .os-mywp__band-head {
823 background: var( --os-ui-bg, #fff );
824 }
825 }
826
827 /* Attention-carrying bands tint their bar so the eye lands there
828 * first instead of having to read every heading. */
829 .os-mywp__band-head--warn {
830 color: var( --os-ui-warning, #8a6116 );
831 background: color-mix(
832 in srgb,
833 var( --os-ui-warning, #8a6116 ) 12%,
834 var( --os-ui-bg, #fff )
835 );
836 border-block-end-color: color-mix(
837 in srgb,
838 var( --os-ui-warning, #8a6116 ) 35%,
839 transparent
840 );
841 }
842
843 .os-mywp__band-head--danger {
844 color: var( --os-ui-danger, #b32d2e );
845 background: color-mix(
846 in srgb,
847 var( --os-ui-danger, #b32d2e ) 12%,
848 var( --os-ui-bg, #fff )
849 );
850 border-block-end-color: color-mix(
851 in srgb,
852 var( --os-ui-danger, #b32d2e ) 35%,
853 transparent
854 );
855 }
856
857 .os-mywp__band-count {
858 padding: 0 6px;
859 border-radius: 999px;
860 font-size: 11px;
861 line-height: 1.7;
862 background: color-mix( in srgb, currentColor 16%, transparent );
863 color: inherit;
864 }
865
866 .os-mywp__band-grid {
867 display: grid;
868 grid-template-columns: repeat( auto-fill, 104px );
869 justify-content: start;
870 gap: 10px 4px;
871 align-content: start;
872 padding: 12px;
873 }
874
875 /* ------------------------------------------------------------ detail */
876
877 .os-mywp__detail {
878 position: relative;
879 padding: 16px;
880 display: flex;
881 flex-direction: column;
882 gap: 12px;
883 align-items: flex-start;
884 }
885
886 .os-mywp__pane-close {
887 position: absolute;
888 inset-block-start: 8px;
889 inset-inline-end: 8px;
890 }
891
892 /* Plugin slots on the article (`os.my-wordpress.preview-extras`).
893 * The article is a gap-spaced column, so a slot nobody painted into
894 * must not spend a gap step. Scoped to OUR pane — WP Explorer's own
895 * slots keep their stylesheet's rules. */
896 .os-mywp__detail .os-my-wordpress__article-slot {
897 align-self: stretch;
898 }
899
900 .os-mywp__detail .os-my-wordpress__article-slot:empty {
901 display: none;
902 }
903
904 .os-mywp__hero {
905 max-inline-size: 100%;
906 max-block-size: 200px;
907 border-radius: 8px;
908 object-fit: contain;
909 }
910
911 .os-mywp__detail-title {
912 margin: 0;
913 font-size: 17px;
914 padding-inline-end: 32px;
915 }
916
917 /* The identity line under a person's name: role badge + archive link. */
918 .os-mywp__user-meta {
919 display: flex;
920 align-items: center;
921 gap: 10px;
922 margin: 0;
923 }
924
925 .os-mywp__facts {
926 display: grid;
927 grid-template-columns: max-content 1fr;
928 gap: 6px 14px;
929 margin: 0;
930 font-size: 13px;
931 }
932
933 .os-mywp__fact {
934 display: contents;
935 }
936
937 .os-mywp__fact dt {
938 color: var( --os-ui-fg-muted, #646970 );
939 }
940
941 .os-mywp__fact dd {
942 margin: 0;
943 }
944
945 .os-mywp__pane-h {
946 margin: 4px 0 0;
947 font-size: 12px;
948 text-transform: uppercase;
949 letter-spacing: 0.04em;
950 color: var( --os-ui-fg-muted, #646970 );
951 }
952
953 .os-mywp__used-in {
954 margin: 0;
955 padding-inline-start: 18px;
956 font-size: 13px;
957 }
958
959 .os-mywp__actions {
960 display: flex;
961 gap: 8px;
962 flex-wrap: wrap;
963 }
964
965 .os-mywp__content {
966 max-inline-size: 100%;
967 overflow-wrap: anywhere;
968 font-size: 13px;
969 }
970
971 .os-mywp__content img {
972 max-inline-size: 100%;
973 block-size: auto;
974 }
975
976 .os-mywp__hero.is-zoomable {
977 cursor: zoom-in;
978 }
979
980 .os-mywp__sentinel {
981 block-size: 1px;
982 }
983
984 /* The hover card (.os-my-wordpress__tooltip and friends) is painted
985 * by the SHARED explorer stylesheet, which rides this window as a
986 * companion style (includes/my-wordpress/assets.php) — one set of
987 * rules for both explorers' cards, not a copy here. */
988
989 /* ---------------------------------------------------- marquee + menu */
990
991 /* The SAME token pair the shell's rubber band wears
992 * (desktop-files.css): a ring on the admin scheme colour over the
993 * shared marquee wash — a WASH, so you can see what it is about to
994 * catch. */
995 .os-mywp__marquee {
996 position: fixed;
997 z-index: 100000;
998 border: 1px solid
999 var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) );
1000 border-radius: 2px;
1001 background: var(
1002 --os-selection-marquee-bg,
1003 var( --os-ui-accent-soft, rgba( 34, 113, 177, 0.14 ) )
1004 );
1005 pointer-events: none;
1006 }
1007
1008 .os-mywp__menu-backdrop {
1009 position: fixed;
1010 inset: 0;
1011 z-index: 99998;
1012 }
1013
1014 .os-mywp__menu {
1015 z-index: 99999;
1016 }
1017
1018 /* -------------------------------------------------------------- zoom */
1019
1020 .os-mywp__zoom {
1021 position: fixed;
1022 inset: 0;
1023 z-index: 100000;
1024 display: grid;
1025 place-items: center;
1026 background: rgba( 0, 0, 0, 0.8 );
1027 cursor: zoom-out;
1028 }
1029
1030 .os-mywp__zoom img {
1031 max-inline-size: 92vw;
1032 max-block-size: 92vh;
1033 object-fit: contain;
1034 }
1035
1036 /* -------------------------------------------- sub-list stats panes */
1037
1038 .os-mywp__icon-mask {
1039 inline-size: 48px;
1040 block-size: 48px;
1041 display: inline-block;
1042 background: currentColor;
1043 -webkit-mask: var( --mywp-icon ) center / contain no-repeat;
1044 mask: var( --mywp-icon ) center / contain no-repeat;
1045 }
1046
1047 .os-mywp__term-head {
1048 display: flex;
1049 align-items: flex-start;
1050 gap: 12px;
1051 }
1052
1053 .os-mywp__term-head div {
1054 display: flex;
1055 flex-direction: column;
1056 align-items: flex-start;
1057 gap: 4px;
1058 }
1059
1060 /* WP Explorer's taxonomy medallion, tokens and shadow included
1061 * (`.os-my-wordpress__term-icon--category`). */
1062 .os-mywp__term-swatch {
1063 inline-size: 48px;
1064 block-size: 48px;
1065 border-radius: 12px;
1066 background: linear-gradient(
1067 135deg,
1068 var( --os-ui-accent, #2271b1 ) 0%,
1069 var( --os-ui-accent-strong, #135e96 ) 100%
1070 );
1071 box-shadow: 0 4px 12px rgba( 34, 113, 177, 0.32 );
1072 flex: 0 0 auto;
1073 }
1074
1075 .os-mywp__stats {
1076 display: grid;
1077 grid-template-columns: repeat( auto-fill, minmax( 110px, 1fr ) );
1078 gap: 8px;
1079 inline-size: 100%;
1080 }
1081
1082 /* The stat tiles are the kit's <os-stat>, on its defaults — the
1083 accent-coloured value is the same --wp-admin-theme-color chain WP
1084 Explorer's stat values read, declared in the component. */
1085
1086 .os-mywp__activity {
1087 display: flex;
1088 align-items: flex-end;
1089 gap: 4px;
1090 block-size: 72px;
1091 inline-size: 100%;
1092 }
1093
1094 .os-mywp__activity-col {
1095 flex: 1 1 0;
1096 display: flex;
1097 flex-direction: column;
1098 align-items: center;
1099 justify-content: flex-end;
1100 block-size: 100%;
1101 gap: 2px;
1102 min-inline-size: 0;
1103 }
1104
1105 .os-mywp__activity-bar {
1106 inline-size: 100%;
1107 min-block-size: 1px;
1108 border-radius: 3px 3px 0 0;
1109 /* WP Explorer's sparkline gradient, verbatim. */
1110 background: linear-gradient(
1111 180deg,
1112 var( --wp-admin-theme-color, #2271b1 ) 0%,
1113 rgba( 34, 113, 177, 0.55 ) 100%
1114 );
1115 }
1116
1117 .os-mywp__activity-label {
1118 font-size: 9px;
1119 color: var( --os-ui-fg-muted, #646970 );
1120 }
1121
1122 .os-mywp__recent {
1123 display: flex;
1124 flex-direction: column;
1125 gap: 4px;
1126 inline-size: 100%;
1127 }
1128
1129 .os-mywp__recent-row {
1130 display: flex;
1131 flex-direction: column;
1132 gap: 2px;
1133 padding: 8px 10px;
1134 border: 0;
1135 border-radius: 6px;
1136 background: var( --os-ui-surface-raised, rgba( 0, 0, 0, 0.04 ) );
1137 color: inherit;
1138 font: inherit;
1139 text-align: start;
1140 cursor: pointer;
1141 }
1142
1143 .os-mywp__recent-row:hover,
1144 .os-mywp__recent-row:focus-visible {
1145 background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) ) );
1146 }
1147
1148 .os-mywp__recent-title {
1149 font-weight: 600;
1150 color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
1151 }
1152
1153 .os-mywp__people {
1154 display: flex;
1155 flex-direction: column;
1156 gap: 6px;
1157 inline-size: 100%;
1158 }
1159
1160 .os-mywp__person {
1161 display: flex;
1162 align-items: center;
1163 gap: 8px;
1164 }
1165
1166 .os-mywp__person-name {
1167 font-weight: 600;
1168 flex: 1 1 auto;
1169 min-inline-size: 0;
1170 overflow: hidden;
1171 text-overflow: ellipsis;
1172 white-space: nowrap;
1173 }
1174
1175 .os-mywp__chips {
1176 display: flex;
1177 flex-wrap: wrap;
1178 gap: 6px;
1179 }
1180
1181 .os-mywp__chip {
1182 border: 1px solid var( --os-ui-border, #dcdcde );
1183 border-radius: 999px;
1184 padding: 3px 10px;
1185 background: transparent;
1186 color: inherit;
1187 font: inherit;
1188 font-size: 12px;
1189 cursor: pointer;
1190 }
1191
1192 .os-mywp__chip:hover,
1193 .os-mywp__chip:focus-visible {
1194 background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) ) );
1195 }
1196
1197 /* The user dossier's top-terms chips are facts, not doors — no hover. */
1198 .os-mywp__chip--static:hover {
1199 background: transparent;
1200 cursor: default;
1201 }
1202
1203 /* ------------------------------------------------------------ status */
1204
1205 .os-mywp__status {
1206 display: flex;
1207 align-items: center;
1208 justify-content: space-between;
1209 gap: 12px;
1210 padding: 4px 12px;
1211 font-size: 11px;
1212 color: var( --os-ui-fg-muted, #646970 );
1213 border-block-start: 1px solid var( --os-ui-border, #dcdcde );
1214 flex: 0 0 auto;
1215 }
1216
1217 /* ------------------------------------------------------------------ */
1218 /* Agents section — WP Explorer's `dm-agents` design, ported 1:1 */
1219 /* (assets/css/my-wordpress.css is the original; same class names, */
1220 /* same tokens, so a desktop theme that repaints one repaints both). */
1221 /* ------------------------------------------------------------------ */
1222
1223 .dm-agents {
1224 display: flex;
1225 flex-direction: column;
1226 height: 100%;
1227 min-height: 0;
1228 padding: 12px;
1229 gap: 10px;
1230 overflow: hidden;
1231 box-sizing: border-box;
1232 }
1233
1234 .dm-agents__detail {
1235 flex: 1;
1236 min-width: 0;
1237 overflow-y: auto;
1238 display: flex;
1239 flex-direction: column;
1240 gap: 10px;
1241 }
1242
1243 /* `<os-empty-state>` owns the icon + copy and centres them within
1244 itself; the host is still a flex item that shrinks to its content,
1245 so without this it sits at the top of a pane that is mostly empty
1246 space. Auto margins on both axes absorb the slack — placement only,
1247 same as the comments window. Covers the framework-off state and
1248 "No agents yet" alike. */
1249 .dm-agents__detail > os-empty-state {
1250 margin: auto;
1251 }
1252
1253 .dm-agents__detail-head {
1254 display: flex;
1255 align-items: center;
1256 gap: 10px;
1257 }
1258
1259 .dm-agents__detail-avatar {
1260 width: 48px;
1261 height: 48px;
1262 border-radius: 50%;
1263 /* Placeholder behind the avatar image while it loads — follows the
1264 palette so it isn't a white disc on a dark station. */
1265 background: var( --os-ui-surface-elevated, #fff );
1266 flex: none;
1267 }
1268
1269 .dm-agents__detail-title {
1270 flex: 1;
1271 min-width: 0;
1272 }
1273
1274 .dm-agents__detail-title h3 {
1275 margin: 0;
1276 font-size: 16px;
1277 }
1278
1279 .dm-agents__detail-slug {
1280 font-size: 12px;
1281 opacity: 0.6;
1282 }
1283
1284 /* Verbs get their own row under the identity block: the set grows
1285 (profile, contributions, desktop, chat, delete) and squeezing them
1286 beside the name truncated it first. */
1287 .dm-agents__detail-actions {
1288 display: flex;
1289 flex-wrap: wrap;
1290 gap: 8px;
1291 }
1292
1293 /* The pane strip is the kit's own <os-tabs>; nothing to restyle. */
1294
1295 .dm-agents__pane {
1296 display: flex;
1297 flex-direction: column;
1298 gap: 10px;
1299 padding-bottom: 12px;
1300 }
1301
1302 .dm-agents__hint {
1303 margin: 0;
1304 font-size: 12px;
1305 opacity: 0.7;
1306 }
1307
1308 /* Role first: least privilege is the decision, so the control holds
1309 the mockup's width instead of stretching into a landing strip. */
1310 .dm-agents__role-select {
1311 max-width: 280px;
1312 }
1313
1314 /* The ability filter. Sized like the role select rather than the
1315 full pane: it sits above a list of rules, and a search box as wide
1316 as the rules reads as another one of them. */
1317 .dm-agents__ability-search {
1318 display: block;
1319 max-width: 320px;
1320 margin: 12px 0 4px;
1321 }
1322
1323 /* Each category is a native disclosure. `<details>` because the
1324 behaviour is exactly what it is for: keyboard, screen-reader
1325 semantics and the open/closed state all arrive for free, and the
1326 only thing left to write is the costume. */
1327 .dm-agents__ability-group {
1328 border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
1329 }
1330
1331 .dm-agents__ability-group:last-of-type {
1332 border-bottom: 0;
1333 }
1334
1335 .dm-agents__category {
1336 margin: 18px 0 2px;
1337 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
1338 font-size: 11px;
1339 font-weight: 500;
1340 text-transform: uppercase;
1341 letter-spacing: 0.08em;
1342 color: var( --os-ui-fg-muted, #646970 );
1343 }
1344
1345 /* A `<summary>` is a button, so it gets a button's affordances: a
1346 pointer, a hover, and a focus ring that is visible on the dark
1347 surface the section is drawn on. */
1348 summary.dm-agents__category {
1349 display: flex;
1350 align-items: center;
1351 gap: 8px;
1352 margin: 0;
1353 padding: 10px 0;
1354 cursor: pointer;
1355 list-style: none;
1356 user-select: none;
1357 }
1358
1359 summary.dm-agents__category::-webkit-details-marker {
1360 display: none;
1361 }
1362
1363 /* The marker is drawn here rather than left to the UA so it can be
1364 the same triangle in every browser and turn with the disclosure.
1365 Rotation only: a transform, so opening a group of forty rows
1366 costs no layout. */
1367 summary.dm-agents__category::before {
1368 content: '';
1369 flex: none;
1370 width: 0;
1371 height: 0;
1372 border-inline-start: 5px solid currentcolor;
1373 border-block: 4px solid transparent;
1374 transition: transform 120ms ease;
1375 }
1376
1377 .dm-agents__ability-group[ open ] > summary.dm-agents__category::before {
1378 transform: rotate( 90deg );
1379 }
1380
1381 @media ( prefers-reduced-motion: reduce ) {
1382 summary.dm-agents__category::before {
1383 transition: none;
1384 }
1385 }
1386
1387 summary.dm-agents__category:hover {
1388 color: var( --os-ui-fg, #1e1e1e );
1389 }
1390
1391 summary.dm-agents__category:focus-visible {
1392 outline: 2px solid var( --os-ui-accent, #3858e9 );
1393 outline-offset: 2px;
1394 border-radius: 4px;
1395 }
1396
1397 .dm-agents__category-name {
1398 flex: 1 1 auto;
1399 }
1400
1401 /* "3 of 12" when some are ticked, otherwise just the size of the
1402 group. The count is why a closed group is still informative:
1403 collapsing must not hide the fact that something in there is
1404 switched on. */
1405 .dm-agents__category-count {
1406 flex: none;
1407 font-variant-numeric: tabular-nums;
1408 opacity: 0.75;
1409 }
1410
1411 /* One ability per row, ruled like the mockup: tick and name on the
1412 left, badge on the right, the description indented under the name
1413 (the checkbox column is the control's own 26px). */
1414 .dm-agents__ability {
1415 display: grid;
1416 grid-template-columns: 1fr auto;
1417 gap: 2px 10px;
1418 align-items: start;
1419 padding: 11px 0;
1420 border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
1421 }
1422
1423 .dm-agents__ability:last-of-type {
1424 border-bottom: 0;
1425 }
1426
1427 .dm-agents__ability-desc {
1428 grid-column: 1 / -1;
1429 margin: 0;
1430 padding-inline-start: 26px;
1431 font-size: 12.5px;
1432 color: var( --os-ui-fg-muted, #646970 );
1433 }
1434
1435 /* Access badges in the instrument voice. The palette's own warning
1436 tokens carry the pale-yellow "can modify"; all this changes is the
1437 costume: mono, uppercase, an outline instead of a wash. */
1438 .dm-agents__ability os-badge {
1439 --os-ui-badge-font: 500 10px/1.6 var( --os-ui-font-mono, ui-monospace, monospace );
1440 --os-ui-badge-dot-size: 0;
1441 --os-ui-badge-gap: 0;
1442 --os-ui-badge-padding: 3px 8px;
1443 --os-ui-badge-bg: transparent;
1444 --os-ui-badge-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) );
1445 text-transform: uppercase;
1446 letter-spacing: 0.06em;
1447 white-space: nowrap;
1448 margin-top: 2px;
1449 }
1450
1451 .dm-agents__ability os-badge[ tone='warning' ] {
1452 --os-ui-badge-border: 1px solid
1453 var( --os-ui-warning-border, rgba( 154, 103, 0, 0.4 ) );
1454 }
1455
1456 .dm-agents__trigger {
1457 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
1458 border-radius: 8px;
1459 padding: 8px 10px;
1460 display: flex;
1461 flex-direction: column;
1462 gap: 6px;
1463 }
1464
1465 .dm-agents__trigger-head {
1466 display: flex;
1467 align-items: center;
1468 gap: 8px;
1469 }
1470
1471 .dm-agents__trigger-desc {
1472 margin: 0;
1473 font-size: 12px;
1474 opacity: 0.75;
1475 }
1476
1477 .dm-agents__trigger-config {
1478 display: flex;
1479 flex-wrap: wrap;
1480 gap: 4px 14px;
1481 }
1482
1483 .dm-agents__actions {
1484 display: flex;
1485 gap: 8px;
1486 align-items: center;
1487 flex-wrap: wrap;
1488 margin-block-start: 12px;
1489 }
1490
1491 /* ---------------------------------------------------------------------
1492 The cast
1493 ------------------------------------------------------------------ */
1494
1495 /* One view at a time: the grid, an agent, or the wizard. A sidebar of
1496 faces next to a detail form would leave neither enough room, and the
1497 whole point of the grid is that the faces are big enough to tell
1498 apart. */
1499 .dm-agents__view {
1500 display: flex;
1501 flex-direction: column;
1502 flex: 1;
1503 min-height: 0;
1504 overflow-y: auto;
1505 gap: 12px;
1506 }
1507
1508 .dm-agents__cast-head {
1509 display: flex;
1510 align-items: baseline;
1511 gap: 8px;
1512 }
1513
1514 .dm-agents__cast-head h3 {
1515 margin: 0;
1516 }
1517
1518 .dm-agents__cast-count {
1519 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
1520 font-size: 12px;
1521 color: var( --os-ui-fg-muted, #646970 );
1522 }
1523
1524 .dm-agents__cast {
1525 display: grid;
1526 gap: 16px;
1527 grid-template-columns: repeat( auto-fill, minmax( 288px, 1fr ) );
1528 align-content: start;
1529 }
1530
1531 /* The card reacts like the mockup's: a lift and a brighter edge.
1532 Transform and border-color only — both cheap, and the same pair the
1533 mockup transitions. Document styles win over the component's own
1534 :host rules, which is the sanctioned way to restyle a host. */
1535 .dm-agents__cast-card,
1536 .dm-agents__cast-new {
1537 --os-ui-card-padding: 20px 18px 16px;
1538 transition: border-color 0.18s ease, transform 0.18s ease;
1539 }
1540
1541 .dm-agents__cast-card:hover,
1542 .dm-agents__cast-card:focus-within {
1543 border-color: var( --os-ui-border-strong, #8c8f94 );
1544 transform: translateY( -2px );
1545 }
1546
1547 /* The dashed door, same as the mockup's crew-new card. */
1548 .dm-agents__cast-new {
1549 border-style: dashed;
1550 --os-ui-card-bg: var( --os-ui-surface-subtle, transparent );
1551 }
1552
1553 /* The crew breathes. Transform only, staggered so five faces do not
1554 bob in lockstep, and gone entirely under reduced motion. */
1555 @media ( prefers-reduced-motion: no-preference ) {
1556 .dm-agents__cast-card .dm-agents__cast-face {
1557 animation: dm-agents-bob 6s ease-in-out infinite;
1558 }
1559 .dm-agents__cast-card:nth-child( 2n ) .dm-agents__cast-face {
1560 animation-duration: 7.2s;
1561 animation-delay: -1.4s;
1562 }
1563 .dm-agents__cast-card:nth-child( 3n ) .dm-agents__cast-face {
1564 animation-duration: 6.6s;
1565 animation-delay: -2.8s;
1566 }
1567 }
1568
1569 @keyframes dm-agents-bob {
1570 0%,
1571 100% {
1572 transform: translateY( 0 );
1573 }
1574 50% {
1575 transform: translateY( -4px );
1576 }
1577 }
1578
1579 /* The column lives on a wrapper we own, not on the host: the slotted
1580 children sit inside the card's own container, so a flex rule on the
1581 host never reaches them and the role badges land wherever the
1582 description's line count leaves them. */
1583 .dm-agents__cast-inner {
1584 display: flex;
1585 flex-direction: column;
1586 align-items: center;
1587 text-align: center;
1588 gap: 2px;
1589 height: 100%;
1590 }
1591
1592 /* The faces are the loud part of this surface, and deliberately so:
1593 agent identity is the one place the station spends the mesh budget
1594 on something other than a hero CTA. A cast has to be legible at a
1595 glance, which is exactly what a flat accent cannot do. */
1596 .dm-agents__cast-face {
1597 width: 118px;
1598 height: 118px;
1599 margin: -10px 0 6px;
1600 flex: none;
1601 }
1602
1603 .dm-agents__cast-name {
1604 font-size: 17px;
1605 font-weight: 600;
1606 color: var( --os-ui-fg, #1d2327 );
1607 }
1608
1609 /* The voice line, in the instrument register: this is metadata about
1610 the agent, not prose from it. */
1611 .dm-agents__cast-vibes {
1612 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
1613 font-size: 11.5px;
1614 color: var( --os-ui-accent-strong, #2271b1 );
1615 }
1616
1617 .dm-agents__cast-good {
1618 font-size: 13px;
1619 color: var( --os-ui-fg-muted, #646970 );
1620 margin-block: 4px 14px;
1621 flex: 1;
1622 }
1623
1624 /* The role pill in the instrument voice: mono, uppercase, no status
1625 dot — this is a fact about the agent, not a state to monitor. The
1626 custom properties are the component's own theming hooks; the
1627 inherited text properties flow into its shadow label. */
1628 .dm-agents__cast-card os-badge {
1629 --os-ui-badge-font: 500 11px/1.6 var( --os-ui-font-mono, ui-monospace, monospace );
1630 --os-ui-badge-dot-size: 0;
1631 --os-ui-badge-gap: 0;
1632 --os-ui-badge-padding: 3px 10px;
1633 text-transform: uppercase;
1634 letter-spacing: 0.08em;
1635 }
1636
1637 /* The framework-off bar: the section's status and its one action, on
1638 one line above the crew it would hire. It is deliberately not an
1639 `os-empty-state` — that block is centred, icon-led and paragraph-
1640 shaped, which is exactly the wall of text the cast replaced, and at
1641 five cards tall it would push the button that undoes the situation
1642 off the bottom of the window. `flex: none` because `.dm-agents__view`
1643 is a scrolling flex column and the bar must not absorb the slack. */
1644 .dm-agents__off-head {
1645 display: flex;
1646 align-items: center;
1647 justify-content: space-between;
1648 flex-wrap: wrap;
1649 flex: none;
1650 gap: 8px 16px;
1651 padding: 12px 14px;
1652 border-radius: 10px;
1653 background: var( --os-ui-surface-elevated, #fff );
1654 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
1655 }
1656
1657 .dm-agents__off-copy {
1658 min-width: 0;
1659 }
1660
1661 .dm-agents__off-copy h3 {
1662 margin: 0;
1663 }
1664
1665 .dm-agents__off-copy p {
1666 margin: 2px 0 0;
1667 font-size: 13px;
1668 color: var( --os-ui-fg-muted, #646970 );
1669 }
1670
1671 /* The cast the site would get, drawn while the framework is off.
1672 Dimmed as a whole rather than desaturated: the five hues ARE the
1673 argument this state is making, and a greyscale filter would flatten
1674 the crew into five identical blobs on the way to saying "not yet".
1675 Opacity says inactive and leaves the faces telling each other apart.
1676
1677 `pointer-events` is belt and braces — the cards are rendered without
1678 `interactive` and without a click handler, so there is nothing to
1679 press. It exists so a hover state cannot promise otherwise. */
1680 .dm-agents__cast--preview {
1681 opacity: 0.6;
1682 pointer-events: none;
1683 }
1684
1685 .dm-agents__cast-new .dm-agents__cast-plus {
1686 display: grid;
1687 place-items: center;
1688 width: 118px;
1689 height: 118px;
1690 margin: -10px 0 6px;
1691 border-radius: 50%;
1692 border: 1.5px dashed var( --os-ui-border-strong, #8c8f94 );
1693 font-size: 30px;
1694 font-weight: 300;
1695 line-height: 1;
1696 color: var( --os-ui-fg-muted, #646970 );
1697 }
1698
1699 /* ---------------------------------------------------------------------
1700 The wizard
1701 ------------------------------------------------------------------ */
1702
1703 .dm-agents__wizard {
1704 display: flex;
1705 flex-direction: column;
1706 gap: 10px;
1707 }
1708
1709 .dm-agents__wiz-head {
1710 display: flex;
1711 align-items: center;
1712 justify-content: space-between;
1713 gap: 8px 12px;
1714 flex-wrap: wrap;
1715 }
1716
1717 .dm-agents__wiz-head h3 {
1718 margin: 0;
1719 }
1720
1721 /* The trail wears the instrument voice: mono uppercase labels, 22px
1722 dots, a thin rule between stations. All of it goes through the
1723 component's own theming hooks; the host elements live in this light
1724 DOM, so the per-state overrides below can select them directly. */
1725 .dm-agents__trail {
1726 margin-block: 6px 14px;
1727 --os-ui-steps-gap: 10px;
1728 --os-ui-step-gap: 7px;
1729 --os-ui-step-chip-size: 22px;
1730 --os-ui-step-chip-font-size: 11px;
1731 --os-ui-step-chip-family: var( --os-ui-font-mono, ui-monospace, monospace );
1732 --os-ui-step-title-size: 11px;
1733 --os-ui-step-title-family: var( --os-ui-font-mono, ui-monospace, monospace );
1734 --os-ui-step-title-transform: uppercase;
1735 --os-ui-step-title-spacing: 0.08em;
1736 }
1737
1738 /* A station you have not reached is an outline, not a filled dot: the
1739 mesh chip is where you ARE, and it only reads that way if it is the
1740 only one lit. */
1741 .dm-agents__trail os-step:not( [ done ] ):not( [ current ] ) {
1742 --os-ui-step-chip-bg: transparent;
1743 --os-ui-step-chip-border: 1px solid var( --os-ui-border-strong, #8c8f94 );
1744 --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 );
1745 }
1746
1747 /* Done is quiet: a filled tick on the elevated surface, muted like its
1748 label. It reads as history, not as a second highlight. */
1749 .dm-agents__trail os-step[ done ] {
1750 --os-ui-step-chip-done-bg: var( --os-ui-surface-elevated, #f0f0f1 );
1751 --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 );
1752 }
1753
1754 .dm-agents__wiz-heading {
1755 margin: 12px 0 2px;
1756 font-size: 16px;
1757 font-weight: 600;
1758 color: var( --os-ui-fg, #1d2327 );
1759 }
1760
1761 /* The door. Five complete agents already ship with the plugin, and
1762 until now the create flow showed them to nobody. */
1763 .dm-agents__starters {
1764 display: grid;
1765 gap: 12px;
1766 grid-template-columns: repeat( auto-fill, minmax( 148px, 1fr ) );
1767 margin-block: 8px 16px;
1768 }
1769
1770 .dm-agents__starter {
1771 display: flex;
1772 flex-direction: column;
1773 align-items: center;
1774 text-align: center;
1775 gap: 3px;
1776 --os-ui-card-padding: 16px 14px;
1777 --os-ui-card-radius: 10px;
1778 --os-ui-card-bg: var( --os-ui-surface-subtle, transparent );
1779 transition: border-color 0.18s ease;
1780 }
1781
1782 .dm-agents__starter:hover,
1783 .dm-agents__starter:focus-within {
1784 border-color: var( --os-ui-border-strong, #8c8f94 );
1785 }
1786
1787 .dm-agents__starter-face {
1788 width: 76px;
1789 height: 76px;
1790 margin: -8px 0 4px;
1791 flex: none;
1792 }
1793
1794 .dm-agents__starter-name {
1795 font-size: 14px;
1796 font-weight: 500;
1797 color: var( --os-ui-fg, #1d2327 );
1798 }
1799
1800 .dm-agents__starter-vibes {
1801 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
1802 font-size: 11.5px;
1803 color: var( --os-ui-fg-muted, #646970 );
1804 }
1805
1806 /* Meet: the portrait beside the fields, so the thing being named is
1807 visible while it is being named. */
1808 .dm-agents__meet {
1809 display: grid;
1810 gap: 20px;
1811 grid-template-columns: 220px 1fr;
1812 align-items: start;
1813 }
1814
1815 /* The character card. Sunken rather than subtle: the portrait is the
1816 hero of this step, and the mockup floats it on the darkest surface
1817 so the face's own glow does the framing. */
1818 .dm-agents__portrait {
1819 display: flex;
1820 flex-direction: column;
1821 align-items: center;
1822 gap: 10px;
1823 padding: 20px 14px 14px;
1824 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
1825 border-radius: var( --os-ui-card-radius, 10px );
1826 background: var( --os-ui-surface-sunken, #f6f7f7 );
1827 }
1828
1829 .dm-agents__portrait-face {
1830 width: 176px;
1831 height: 176px;
1832 max-width: 100%;
1833 margin-top: -12px;
1834 }
1835
1836 /* What this face is, in two words. */
1837 .dm-agents__portrait-chips {
1838 display: flex;
1839 gap: 6px;
1840 flex-wrap: wrap;
1841 justify-content: center;
1842 }
1843
1844 .dm-agents__faces {
1845 display: grid;
1846 grid-template-columns: repeat( 6, 1fr );
1847 gap: 2px;
1848 width: 100%;
1849 }
1850
1851 .dm-agents__face-pick {
1852 display: grid;
1853 place-items: center;
1854 padding: 2px;
1855 border: 1px solid transparent;
1856 border-radius: 8px;
1857 background: none;
1858 cursor: pointer;
1859 }
1860
1861 .dm-agents__face-pick img {
1862 width: 100%;
1863 height: auto;
1864 display: block;
1865 }
1866
1867 .dm-agents__face-pick:hover {
1868 border-color: var( --os-ui-border-strong, #8c8f94 );
1869 }
1870
1871 .dm-agents__face-pick.is-picked {
1872 border-color: var( --os-ui-accent, #2271b1 );
1873 }
1874
1875 .dm-agents__face-pick:focus-visible {
1876 outline: var( --os-ui-focus-ring, 2px solid #2271b1 );
1877 outline-offset: 1px;
1878 }
1879
1880 .dm-agents__meet-fields {
1881 display: flex;
1882 flex-direction: column;
1883 gap: 10px;
1884 min-width: 0;
1885 }
1886
1887 /* Launch: one card, the brand's card radius, floating on the darkest
1888 surface like the portrait it echoes. */
1889 .dm-agents__summary {
1890 display: grid;
1891 grid-template-columns: 106px 1fr;
1892 gap: 18px;
1893 align-items: start;
1894 --os-ui-card-padding: 18px;
1895 --os-ui-card-radius: 10px;
1896 --os-ui-card-bg: var( --os-ui-surface-sunken, #f6f7f7 );
1897 }
1898
1899 .dm-agents__summary-face {
1900 width: 106px;
1901 height: 106px;
1902 flex: none;
1903 }
1904
1905 .dm-agents__summary-text h4 {
1906 margin: 0 0 2px;
1907 font-size: 17px;
1908 }
1909
1910 .dm-agents__summary-vibes {
1911 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
1912 font-size: 11.5px;
1913 color: var( --os-ui-accent-strong, #2271b1 );
1914 margin: 0 0 6px;
1915 }
1916
1917 .dm-agents__summary-desc {
1918 margin: 0;
1919 font-size: 13px;
1920 color: var( --os-ui-fg-muted, #646970 );
1921 }
1922
1923 .dm-agents__chips {
1924 display: flex;
1925 flex-wrap: wrap;
1926 gap: 6px;
1927 margin-block-start: 10px;
1928 }
1929
1930 /* What the agent will actually be told, under the chips. Clamped: a
1931 summary card quotes the instructions, it does not host them. */
1932 .dm-agents__summary-instr {
1933 margin: 10px 0 0;
1934 font-size: 13px;
1935 color: var( --os-ui-fg-muted, #646970 );
1936 display: -webkit-box;
1937 -webkit-line-clamp: 3;
1938 -webkit-box-orient: vertical;
1939 overflow: hidden;
1940 }
1941
1942 /* Identity chips — role, abilities, silhouette, hue — all wear the
1943 instrument voice: these are facts about the character, stamped
1944 rather than said. */
1945 .dm-agents__chips os-chip,
1946 .dm-agents__portrait-chips os-chip {
1947 --os-ui-chip-font-size: 10.5px;
1948 --os-ui-chip-font-weight: 500;
1949 --os-ui-chip-bg: transparent;
1950 --os-ui-chip-fg: var( --os-ui-fg-muted, #646970 );
1951 --os-ui-chip-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) );
1952 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
1953 text-transform: uppercase;
1954 letter-spacing: 0.06em;
1955 }
1956
1957 @media ( max-width: 640px ) {
1958 .dm-agents__meet,
1959 .dm-agents__summary {
1960 grid-template-columns: 1fr;
1961 }
1962 .dm-agents__portrait-face {
1963 width: 128px;
1964 height: 128px;
1965 }
1966 }
1967
1968 /* The way back to the grid. The window's own breadcrumb goes UP to the
1969 folder, which is a different journey: this one returns to the cast. */
1970 .dm-agents__back {
1971 align-self: flex-start;
1972 margin-block-end: 4px;
1973 }
1974
1975 /*
1976 * Long press is the touch right-click (`parts/long-press.ts`). What
1977 * the browser would do with a held finger — iOS's callout, a text
1978 * selection creeping across a label — is turned off on the pressable
1979 * surfaces, and only where a finger is the pointer: on a desk the
1980 * labels stay selectable.
1981 */
1982 @media ( pointer: coarse ) {
1983 .os-mywp__tile,
1984 .os-mywp__cell,
1985 .os-mywp__row,
1986 .os-mywp__canvas {
1987 -webkit-touch-callout: none;
1988 -webkit-user-select: none;
1989 user-select: none;
1990 }
1991 }
1992 html[data-os-mode="mobile"] .os-mywp__tile,
1993 html[data-os-mode="mobile"] .os-mywp__cell,
1994 html[data-os-mode="mobile"] .os-mywp__row,
1995 html[data-os-mode="mobile"] .os-mywp__canvas {
1996 -webkit-touch-callout: none;
1997 -webkit-user-select: none;
1998 user-select: none;
1999 }
2000