PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.11
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.11
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 0.8.9 0.8.8 All 35 releases
desktop-mode / apps / my-wordpress / my-wordpress.css

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

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