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

2,001 lines 48.0 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__facts {
927 display: grid;
928 grid-template-columns: max-content 1fr;
929 gap: 6px 14px;
930 margin: 0;
931 font-size: 13px;
932 }
933
934 .os-mywp__fact {
935 display: contents;
936 }
937
938 .os-mywp__fact dt {
939 color: var( --os-ui-fg-muted, #646970 );
940 }
941
942 .os-mywp__fact dd {
943 margin: 0;
944 }
945
946 .os-mywp__pane-h {
947 margin: 4px 0 0;
948 font-size: 12px;
949 text-transform: uppercase;
950 letter-spacing: 0.04em;
951 color: var( --os-ui-fg-muted, #646970 );
952 }
953
954 .os-mywp__used-in {
955 margin: 0;
956 padding-inline-start: 18px;
957 font-size: 13px;
958 }
959
960 .os-mywp__actions {
961 display: flex;
962 gap: 8px;
963 flex-wrap: wrap;
964 }
965
966 .os-mywp__content {
967 max-inline-size: 100%;
968 overflow-wrap: anywhere;
969 font-size: 13px;
970 }
971
972 .os-mywp__content img {
973 max-inline-size: 100%;
974 block-size: auto;
975 }
976
977 .os-mywp__hero.is-zoomable {
978 cursor: zoom-in;
979 }
980
981 .os-mywp__sentinel {
982 block-size: 1px;
983 }
984
985 /* The hover card (.os-my-wordpress__tooltip and friends) is painted
986 * by the SHARED explorer stylesheet, which rides this window as a
987 * companion style (includes/my-wordpress/assets.php) — one set of
988 * rules for both explorers' cards, not a copy here. */
989
990 /* ---------------------------------------------------- marquee + menu */
991
992 /* The SAME token pair the shell's rubber band wears
993 * (desktop-files.css): a ring on the admin scheme colour over the
994 * shared marquee wash — a WASH, so you can see what it is about to
995 * catch. */
996 .os-mywp__marquee {
997 position: fixed;
998 z-index: 100000;
999 border: 1px solid
1000 var( --os-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) );
1001 border-radius: 2px;
1002 background: var(
1003 --os-selection-marquee-bg,
1004 var( --os-ui-accent-soft, rgba( 34, 113, 177, 0.14 ) )
1005 );
1006 pointer-events: none;
1007 }
1008
1009 .os-mywp__menu-backdrop {
1010 position: fixed;
1011 inset: 0;
1012 z-index: 99998;
1013 }
1014
1015 .os-mywp__menu {
1016 z-index: 99999;
1017 }
1018
1019 /* -------------------------------------------------------------- zoom */
1020
1021 .os-mywp__zoom {
1022 position: fixed;
1023 inset: 0;
1024 z-index: 100000;
1025 display: grid;
1026 place-items: center;
1027 background: rgba( 0, 0, 0, 0.8 );
1028 cursor: zoom-out;
1029 }
1030
1031 .os-mywp__zoom img {
1032 max-inline-size: 92vw;
1033 max-block-size: 92vh;
1034 object-fit: contain;
1035 }
1036
1037 /* -------------------------------------------- sub-list stats panes */
1038
1039 .os-mywp__icon-mask {
1040 inline-size: 48px;
1041 block-size: 48px;
1042 display: inline-block;
1043 background: currentColor;
1044 -webkit-mask: var( --mywp-icon ) center / contain no-repeat;
1045 mask: var( --mywp-icon ) center / contain no-repeat;
1046 }
1047
1048 .os-mywp__term-head {
1049 display: flex;
1050 align-items: flex-start;
1051 gap: 12px;
1052 }
1053
1054 .os-mywp__term-head div {
1055 display: flex;
1056 flex-direction: column;
1057 align-items: flex-start;
1058 gap: 4px;
1059 }
1060
1061 /* WP Explorer's taxonomy medallion, tokens and shadow included
1062 * (`.os-my-wordpress__term-icon--category`). */
1063 .os-mywp__term-swatch {
1064 inline-size: 48px;
1065 block-size: 48px;
1066 border-radius: 12px;
1067 background: linear-gradient(
1068 135deg,
1069 var( --os-ui-accent, #2271b1 ) 0%,
1070 var( --os-ui-accent-strong, #135e96 ) 100%
1071 );
1072 box-shadow: 0 4px 12px rgba( 34, 113, 177, 0.32 );
1073 flex: 0 0 auto;
1074 }
1075
1076 .os-mywp__stats {
1077 display: grid;
1078 grid-template-columns: repeat( auto-fill, minmax( 110px, 1fr ) );
1079 gap: 8px;
1080 inline-size: 100%;
1081 }
1082
1083 /* The stat tiles are the kit's <os-stat>, on its defaults — the
1084 accent-coloured value is the same --wp-admin-theme-color chain WP
1085 Explorer's stat values read, declared in the component. */
1086
1087 .os-mywp__activity {
1088 display: flex;
1089 align-items: flex-end;
1090 gap: 4px;
1091 block-size: 72px;
1092 inline-size: 100%;
1093 }
1094
1095 .os-mywp__activity-col {
1096 flex: 1 1 0;
1097 display: flex;
1098 flex-direction: column;
1099 align-items: center;
1100 justify-content: flex-end;
1101 block-size: 100%;
1102 gap: 2px;
1103 min-inline-size: 0;
1104 }
1105
1106 .os-mywp__activity-bar {
1107 inline-size: 100%;
1108 min-block-size: 1px;
1109 border-radius: 3px 3px 0 0;
1110 /* WP Explorer's sparkline gradient, verbatim. */
1111 background: linear-gradient(
1112 180deg,
1113 var( --wp-admin-theme-color, #2271b1 ) 0%,
1114 rgba( 34, 113, 177, 0.55 ) 100%
1115 );
1116 }
1117
1118 .os-mywp__activity-label {
1119 font-size: 9px;
1120 color: var( --os-ui-fg-muted, #646970 );
1121 }
1122
1123 .os-mywp__recent {
1124 display: flex;
1125 flex-direction: column;
1126 gap: 4px;
1127 inline-size: 100%;
1128 }
1129
1130 .os-mywp__recent-row {
1131 display: flex;
1132 flex-direction: column;
1133 gap: 2px;
1134 padding: 8px 10px;
1135 border: 0;
1136 border-radius: 6px;
1137 background: var( --os-ui-surface-raised, rgba( 0, 0, 0, 0.04 ) );
1138 color: inherit;
1139 font: inherit;
1140 text-align: start;
1141 cursor: pointer;
1142 }
1143
1144 .os-mywp__recent-row:hover,
1145 .os-mywp__recent-row:focus-visible {
1146 background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) ) );
1147 }
1148
1149 .os-mywp__recent-title {
1150 font-weight: 600;
1151 color: var( --os-link, var( --os-ui-accent, #2271b1 ) );
1152 }
1153
1154 .os-mywp__people {
1155 display: flex;
1156 flex-direction: column;
1157 gap: 6px;
1158 inline-size: 100%;
1159 }
1160
1161 .os-mywp__person {
1162 display: flex;
1163 align-items: center;
1164 gap: 8px;
1165 }
1166
1167 .os-mywp__person-name {
1168 font-weight: 600;
1169 flex: 1 1 auto;
1170 min-inline-size: 0;
1171 overflow: hidden;
1172 text-overflow: ellipsis;
1173 white-space: nowrap;
1174 }
1175
1176 .os-mywp__chips {
1177 display: flex;
1178 flex-wrap: wrap;
1179 gap: 6px;
1180 }
1181
1182 .os-mywp__chip {
1183 border: 1px solid var( --os-ui-border, #dcdcde );
1184 border-radius: 999px;
1185 padding: 3px 10px;
1186 background: transparent;
1187 color: inherit;
1188 font: inherit;
1189 font-size: 12px;
1190 cursor: pointer;
1191 }
1192
1193 .os-mywp__chip:hover,
1194 .os-mywp__chip:focus-visible {
1195 background: var( --os-hover, var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) ) );
1196 }
1197
1198 /* The user dossier's top-terms chips are facts, not doors — no hover. */
1199 .os-mywp__chip--static:hover {
1200 background: transparent;
1201 cursor: default;
1202 }
1203
1204 /* ------------------------------------------------------------ status */
1205
1206 .os-mywp__status {
1207 display: flex;
1208 align-items: center;
1209 justify-content: space-between;
1210 gap: 12px;
1211 padding: 4px 12px;
1212 font-size: 11px;
1213 color: var( --os-ui-fg-muted, #646970 );
1214 border-block-start: 1px solid var( --os-ui-border, #dcdcde );
1215 flex: 0 0 auto;
1216 }
1217
1218 /* ------------------------------------------------------------------ */
1219 /* Agents section — WP Explorer's `dm-agents` design, ported 1:1 */
1220 /* (assets/css/my-wordpress.css is the original; same class names, */
1221 /* same tokens, so a desktop theme that repaints one repaints both). */
1222 /* ------------------------------------------------------------------ */
1223
1224 .dm-agents {
1225 display: flex;
1226 flex-direction: column;
1227 height: 100%;
1228 min-height: 0;
1229 padding: 12px;
1230 gap: 10px;
1231 overflow: hidden;
1232 box-sizing: border-box;
1233 }
1234
1235 .dm-agents__detail {
1236 flex: 1;
1237 min-width: 0;
1238 overflow-y: auto;
1239 display: flex;
1240 flex-direction: column;
1241 gap: 10px;
1242 }
1243
1244 /* `<os-empty-state>` owns the icon + copy and centres them within
1245 itself; the host is still a flex item that shrinks to its content,
1246 so without this it sits at the top of a pane that is mostly empty
1247 space. Auto margins on both axes absorb the slack — placement only,
1248 same as the comments window. Covers the framework-off state and
1249 "No agents yet" alike. */
1250 .dm-agents__detail > os-empty-state {
1251 margin: auto;
1252 }
1253
1254 .dm-agents__detail-head {
1255 display: flex;
1256 align-items: center;
1257 gap: 10px;
1258 }
1259
1260 .dm-agents__detail-avatar {
1261 width: 48px;
1262 height: 48px;
1263 border-radius: 50%;
1264 /* Placeholder behind the avatar image while it loads — follows the
1265 palette so it isn't a white disc on a dark station. */
1266 background: var( --os-ui-surface-elevated, #fff );
1267 flex: none;
1268 }
1269
1270 .dm-agents__detail-title {
1271 flex: 1;
1272 min-width: 0;
1273 }
1274
1275 .dm-agents__detail-title h3 {
1276 margin: 0;
1277 font-size: 16px;
1278 }
1279
1280 .dm-agents__detail-slug {
1281 font-size: 12px;
1282 opacity: 0.6;
1283 }
1284
1285 /* Verbs get their own row under the identity block: the set grows
1286 (profile, contributions, desktop, chat, delete) and squeezing them
1287 beside the name truncated it first. */
1288 .dm-agents__detail-actions {
1289 display: flex;
1290 flex-wrap: wrap;
1291 gap: 8px;
1292 }
1293
1294 /* The pane strip is the kit's own <os-tabs>; nothing to restyle. */
1295
1296 .dm-agents__pane {
1297 display: flex;
1298 flex-direction: column;
1299 gap: 10px;
1300 padding-bottom: 12px;
1301 }
1302
1303 .dm-agents__hint {
1304 margin: 0;
1305 font-size: 12px;
1306 opacity: 0.7;
1307 }
1308
1309 /* Role first: least privilege is the decision, so the control holds
1310 the mockup's width instead of stretching into a landing strip. */
1311 .dm-agents__role-select {
1312 max-width: 280px;
1313 }
1314
1315 /* The ability filter. Sized like the role select rather than the
1316 full pane: it sits above a list of rules, and a search box as wide
1317 as the rules reads as another one of them. */
1318 .dm-agents__ability-search {
1319 display: block;
1320 max-width: 320px;
1321 margin: 12px 0 4px;
1322 }
1323
1324 /* Each category is a native disclosure. `<details>` because the
1325 behaviour is exactly what it is for: keyboard, screen-reader
1326 semantics and the open/closed state all arrive for free, and the
1327 only thing left to write is the costume. */
1328 .dm-agents__ability-group {
1329 border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
1330 }
1331
1332 .dm-agents__ability-group:last-of-type {
1333 border-bottom: 0;
1334 }
1335
1336 .dm-agents__category {
1337 margin: 18px 0 2px;
1338 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
1339 font-size: 11px;
1340 font-weight: 500;
1341 text-transform: uppercase;
1342 letter-spacing: 0.08em;
1343 color: var( --os-ui-fg-muted, #646970 );
1344 }
1345
1346 /* A `<summary>` is a button, so it gets a button's affordances: a
1347 pointer, a hover, and a focus ring that is visible on the dark
1348 surface the section is drawn on. */
1349 summary.dm-agents__category {
1350 display: flex;
1351 align-items: center;
1352 gap: 8px;
1353 margin: 0;
1354 padding: 10px 0;
1355 cursor: pointer;
1356 list-style: none;
1357 user-select: none;
1358 }
1359
1360 summary.dm-agents__category::-webkit-details-marker {
1361 display: none;
1362 }
1363
1364 /* The marker is drawn here rather than left to the UA so it can be
1365 the same triangle in every browser and turn with the disclosure.
1366 Rotation only: a transform, so opening a group of forty rows
1367 costs no layout. */
1368 summary.dm-agents__category::before {
1369 content: '';
1370 flex: none;
1371 width: 0;
1372 height: 0;
1373 border-inline-start: 5px solid currentcolor;
1374 border-block: 4px solid transparent;
1375 transition: transform 120ms ease;
1376 }
1377
1378 .dm-agents__ability-group[ open ] > summary.dm-agents__category::before {
1379 transform: rotate( 90deg );
1380 }
1381
1382 @media ( prefers-reduced-motion: reduce ) {
1383 summary.dm-agents__category::before {
1384 transition: none;
1385 }
1386 }
1387
1388 summary.dm-agents__category:hover {
1389 color: var( --os-ui-fg, #1e1e1e );
1390 }
1391
1392 summary.dm-agents__category:focus-visible {
1393 outline: 2px solid var( --os-ui-accent, #3858e9 );
1394 outline-offset: 2px;
1395 border-radius: 4px;
1396 }
1397
1398 .dm-agents__category-name {
1399 flex: 1 1 auto;
1400 }
1401
1402 /* "3 of 12" when some are ticked, otherwise just the size of the
1403 group. The count is why a closed group is still informative:
1404 collapsing must not hide the fact that something in there is
1405 switched on. */
1406 .dm-agents__category-count {
1407 flex: none;
1408 font-variant-numeric: tabular-nums;
1409 opacity: 0.75;
1410 }
1411
1412 /* One ability per row, ruled like the mockup: tick and name on the
1413 left, badge on the right, the description indented under the name
1414 (the checkbox column is the control's own 26px). */
1415 .dm-agents__ability {
1416 display: grid;
1417 grid-template-columns: 1fr auto;
1418 gap: 2px 10px;
1419 align-items: start;
1420 padding: 11px 0;
1421 border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
1422 }
1423
1424 .dm-agents__ability:last-of-type {
1425 border-bottom: 0;
1426 }
1427
1428 .dm-agents__ability-desc {
1429 grid-column: 1 / -1;
1430 margin: 0;
1431 padding-inline-start: 26px;
1432 font-size: 12.5px;
1433 color: var( --os-ui-fg-muted, #646970 );
1434 }
1435
1436 /* Access badges in the instrument voice. The palette's own warning
1437 tokens carry the pale-yellow "can modify"; all this changes is the
1438 costume: mono, uppercase, an outline instead of a wash. */
1439 .dm-agents__ability os-badge {
1440 --os-ui-badge-font: 500 10px/1.6 var( --os-ui-font-mono, ui-monospace, monospace );
1441 --os-ui-badge-dot-size: 0;
1442 --os-ui-badge-gap: 0;
1443 --os-ui-badge-padding: 3px 8px;
1444 --os-ui-badge-bg: transparent;
1445 --os-ui-badge-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) );
1446 text-transform: uppercase;
1447 letter-spacing: 0.06em;
1448 white-space: nowrap;
1449 margin-top: 2px;
1450 }
1451
1452 .dm-agents__ability os-badge[ tone='warning' ] {
1453 --os-ui-badge-border: 1px solid
1454 var( --os-ui-warning-border, rgba( 154, 103, 0, 0.4 ) );
1455 }
1456
1457 .dm-agents__trigger {
1458 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
1459 border-radius: 8px;
1460 padding: 8px 10px;
1461 display: flex;
1462 flex-direction: column;
1463 gap: 6px;
1464 }
1465
1466 .dm-agents__trigger-head {
1467 display: flex;
1468 align-items: center;
1469 gap: 8px;
1470 }
1471
1472 .dm-agents__trigger-desc {
1473 margin: 0;
1474 font-size: 12px;
1475 opacity: 0.75;
1476 }
1477
1478 .dm-agents__trigger-config {
1479 display: flex;
1480 flex-wrap: wrap;
1481 gap: 4px 14px;
1482 }
1483
1484 .dm-agents__actions {
1485 display: flex;
1486 gap: 8px;
1487 align-items: center;
1488 flex-wrap: wrap;
1489 margin-block-start: 12px;
1490 }
1491
1492 /* ---------------------------------------------------------------------
1493 The cast
1494 ------------------------------------------------------------------ */
1495
1496 /* One view at a time: the grid, an agent, or the wizard. A sidebar of
1497 faces next to a detail form would leave neither enough room, and the
1498 whole point of the grid is that the faces are big enough to tell
1499 apart. */
1500 .dm-agents__view {
1501 display: flex;
1502 flex-direction: column;
1503 flex: 1;
1504 min-height: 0;
1505 overflow-y: auto;
1506 gap: 12px;
1507 }
1508
1509 .dm-agents__cast-head {
1510 display: flex;
1511 align-items: baseline;
1512 gap: 8px;
1513 }
1514
1515 .dm-agents__cast-head h3 {
1516 margin: 0;
1517 }
1518
1519 .dm-agents__cast-count {
1520 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
1521 font-size: 12px;
1522 color: var( --os-ui-fg-muted, #646970 );
1523 }
1524
1525 .dm-agents__cast {
1526 display: grid;
1527 gap: 16px;
1528 grid-template-columns: repeat( auto-fill, minmax( 288px, 1fr ) );
1529 align-content: start;
1530 }
1531
1532 /* The card reacts like the mockup's: a lift and a brighter edge.
1533 Transform and border-color only — both cheap, and the same pair the
1534 mockup transitions. Document styles win over the component's own
1535 :host rules, which is the sanctioned way to restyle a host. */
1536 .dm-agents__cast-card,
1537 .dm-agents__cast-new {
1538 --os-ui-card-padding: 20px 18px 16px;
1539 transition: border-color 0.18s ease, transform 0.18s ease;
1540 }
1541
1542 .dm-agents__cast-card:hover,
1543 .dm-agents__cast-card:focus-within {
1544 border-color: var( --os-ui-border-strong, #8c8f94 );
1545 transform: translateY( -2px );
1546 }
1547
1548 /* The dashed door, same as the mockup's crew-new card. */
1549 .dm-agents__cast-new {
1550 border-style: dashed;
1551 --os-ui-card-bg: var( --os-ui-surface-subtle, transparent );
1552 }
1553
1554 /* The crew breathes. Transform only, staggered so five faces do not
1555 bob in lockstep, and gone entirely under reduced motion. */
1556 @media ( prefers-reduced-motion: no-preference ) {
1557 .dm-agents__cast-card .dm-agents__cast-face {
1558 animation: dm-agents-bob 6s ease-in-out infinite;
1559 }
1560 .dm-agents__cast-card:nth-child( 2n ) .dm-agents__cast-face {
1561 animation-duration: 7.2s;
1562 animation-delay: -1.4s;
1563 }
1564 .dm-agents__cast-card:nth-child( 3n ) .dm-agents__cast-face {
1565 animation-duration: 6.6s;
1566 animation-delay: -2.8s;
1567 }
1568 }
1569
1570 @keyframes dm-agents-bob {
1571 0%,
1572 100% {
1573 transform: translateY( 0 );
1574 }
1575 50% {
1576 transform: translateY( -4px );
1577 }
1578 }
1579
1580 /* The column lives on a wrapper we own, not on the host: the slotted
1581 children sit inside the card's own container, so a flex rule on the
1582 host never reaches them and the role badges land wherever the
1583 description's line count leaves them. */
1584 .dm-agents__cast-inner {
1585 display: flex;
1586 flex-direction: column;
1587 align-items: center;
1588 text-align: center;
1589 gap: 2px;
1590 height: 100%;
1591 }
1592
1593 /* The faces are the loud part of this surface, and deliberately so:
1594 agent identity is the one place the station spends the mesh budget
1595 on something other than a hero CTA. A cast has to be legible at a
1596 glance, which is exactly what a flat accent cannot do. */
1597 .dm-agents__cast-face {
1598 width: 118px;
1599 height: 118px;
1600 margin: -10px 0 6px;
1601 flex: none;
1602 }
1603
1604 .dm-agents__cast-name {
1605 font-size: 17px;
1606 font-weight: 600;
1607 color: var( --os-ui-fg, #1d2327 );
1608 }
1609
1610 /* The voice line, in the instrument register: this is metadata about
1611 the agent, not prose from it. */
1612 .dm-agents__cast-vibes {
1613 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
1614 font-size: 11.5px;
1615 color: var( --os-ui-accent-strong, #2271b1 );
1616 }
1617
1618 .dm-agents__cast-good {
1619 font-size: 13px;
1620 color: var( --os-ui-fg-muted, #646970 );
1621 margin-block: 4px 14px;
1622 flex: 1;
1623 }
1624
1625 /* The role pill in the instrument voice: mono, uppercase, no status
1626 dot — this is a fact about the agent, not a state to monitor. The
1627 custom properties are the component's own theming hooks; the
1628 inherited text properties flow into its shadow label. */
1629 .dm-agents__cast-card os-badge {
1630 --os-ui-badge-font: 500 11px/1.6 var( --os-ui-font-mono, ui-monospace, monospace );
1631 --os-ui-badge-dot-size: 0;
1632 --os-ui-badge-gap: 0;
1633 --os-ui-badge-padding: 3px 10px;
1634 text-transform: uppercase;
1635 letter-spacing: 0.08em;
1636 }
1637
1638 /* The framework-off bar: the section's status and its one action, on
1639 one line above the crew it would hire. It is deliberately not an
1640 `os-empty-state` — that block is centred, icon-led and paragraph-
1641 shaped, which is exactly the wall of text the cast replaced, and at
1642 five cards tall it would push the button that undoes the situation
1643 off the bottom of the window. `flex: none` because `.dm-agents__view`
1644 is a scrolling flex column and the bar must not absorb the slack. */
1645 .dm-agents__off-head {
1646 display: flex;
1647 align-items: center;
1648 justify-content: space-between;
1649 flex-wrap: wrap;
1650 flex: none;
1651 gap: 8px 16px;
1652 padding: 12px 14px;
1653 border-radius: 10px;
1654 background: var( --os-ui-surface-elevated, #fff );
1655 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
1656 }
1657
1658 .dm-agents__off-copy {
1659 min-width: 0;
1660 }
1661
1662 .dm-agents__off-copy h3 {
1663 margin: 0;
1664 }
1665
1666 .dm-agents__off-copy p {
1667 margin: 2px 0 0;
1668 font-size: 13px;
1669 color: var( --os-ui-fg-muted, #646970 );
1670 }
1671
1672 /* The cast the site would get, drawn while the framework is off.
1673 Dimmed as a whole rather than desaturated: the five hues ARE the
1674 argument this state is making, and a greyscale filter would flatten
1675 the crew into five identical blobs on the way to saying "not yet".
1676 Opacity says inactive and leaves the faces telling each other apart.
1677
1678 `pointer-events` is belt and braces — the cards are rendered without
1679 `interactive` and without a click handler, so there is nothing to
1680 press. It exists so a hover state cannot promise otherwise. */
1681 .dm-agents__cast--preview {
1682 opacity: 0.6;
1683 pointer-events: none;
1684 }
1685
1686 .dm-agents__cast-new .dm-agents__cast-plus {
1687 display: grid;
1688 place-items: center;
1689 width: 118px;
1690 height: 118px;
1691 margin: -10px 0 6px;
1692 border-radius: 50%;
1693 border: 1.5px dashed var( --os-ui-border-strong, #8c8f94 );
1694 font-size: 30px;
1695 font-weight: 300;
1696 line-height: 1;
1697 color: var( --os-ui-fg-muted, #646970 );
1698 }
1699
1700 /* ---------------------------------------------------------------------
1701 The wizard
1702 ------------------------------------------------------------------ */
1703
1704 .dm-agents__wizard {
1705 display: flex;
1706 flex-direction: column;
1707 gap: 10px;
1708 }
1709
1710 .dm-agents__wiz-head {
1711 display: flex;
1712 align-items: center;
1713 justify-content: space-between;
1714 gap: 8px 12px;
1715 flex-wrap: wrap;
1716 }
1717
1718 .dm-agents__wiz-head h3 {
1719 margin: 0;
1720 }
1721
1722 /* The trail wears the instrument voice: mono uppercase labels, 22px
1723 dots, a thin rule between stations. All of it goes through the
1724 component's own theming hooks; the host elements live in this light
1725 DOM, so the per-state overrides below can select them directly. */
1726 .dm-agents__trail {
1727 margin-block: 6px 14px;
1728 --os-ui-steps-gap: 10px;
1729 --os-ui-step-gap: 7px;
1730 --os-ui-step-chip-size: 22px;
1731 --os-ui-step-chip-font-size: 11px;
1732 --os-ui-step-chip-family: var( --os-ui-font-mono, ui-monospace, monospace );
1733 --os-ui-step-title-size: 11px;
1734 --os-ui-step-title-family: var( --os-ui-font-mono, ui-monospace, monospace );
1735 --os-ui-step-title-transform: uppercase;
1736 --os-ui-step-title-spacing: 0.08em;
1737 }
1738
1739 /* A station you have not reached is an outline, not a filled dot: the
1740 mesh chip is where you ARE, and it only reads that way if it is the
1741 only one lit. */
1742 .dm-agents__trail os-step:not( [ done ] ):not( [ current ] ) {
1743 --os-ui-step-chip-bg: transparent;
1744 --os-ui-step-chip-border: 1px solid var( --os-ui-border-strong, #8c8f94 );
1745 --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 );
1746 }
1747
1748 /* Done is quiet: a filled tick on the elevated surface, muted like its
1749 label. It reads as history, not as a second highlight. */
1750 .dm-agents__trail os-step[ done ] {
1751 --os-ui-step-chip-done-bg: var( --os-ui-surface-elevated, #f0f0f1 );
1752 --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 );
1753 }
1754
1755 .dm-agents__wiz-heading {
1756 margin: 12px 0 2px;
1757 font-size: 16px;
1758 font-weight: 600;
1759 color: var( --os-ui-fg, #1d2327 );
1760 }
1761
1762 /* The door. Five complete agents already ship with the plugin, and
1763 until now the create flow showed them to nobody. */
1764 .dm-agents__starters {
1765 display: grid;
1766 gap: 12px;
1767 grid-template-columns: repeat( auto-fill, minmax( 148px, 1fr ) );
1768 margin-block: 8px 16px;
1769 }
1770
1771 .dm-agents__starter {
1772 display: flex;
1773 flex-direction: column;
1774 align-items: center;
1775 text-align: center;
1776 gap: 3px;
1777 --os-ui-card-padding: 16px 14px;
1778 --os-ui-card-radius: 10px;
1779 --os-ui-card-bg: var( --os-ui-surface-subtle, transparent );
1780 transition: border-color 0.18s ease;
1781 }
1782
1783 .dm-agents__starter:hover,
1784 .dm-agents__starter:focus-within {
1785 border-color: var( --os-ui-border-strong, #8c8f94 );
1786 }
1787
1788 .dm-agents__starter-face {
1789 width: 76px;
1790 height: 76px;
1791 margin: -8px 0 4px;
1792 flex: none;
1793 }
1794
1795 .dm-agents__starter-name {
1796 font-size: 14px;
1797 font-weight: 500;
1798 color: var( --os-ui-fg, #1d2327 );
1799 }
1800
1801 .dm-agents__starter-vibes {
1802 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
1803 font-size: 11.5px;
1804 color: var( --os-ui-fg-muted, #646970 );
1805 }
1806
1807 /* Meet: the portrait beside the fields, so the thing being named is
1808 visible while it is being named. */
1809 .dm-agents__meet {
1810 display: grid;
1811 gap: 20px;
1812 grid-template-columns: 220px 1fr;
1813 align-items: start;
1814 }
1815
1816 /* The character card. Sunken rather than subtle: the portrait is the
1817 hero of this step, and the mockup floats it on the darkest surface
1818 so the face's own glow does the framing. */
1819 .dm-agents__portrait {
1820 display: flex;
1821 flex-direction: column;
1822 align-items: center;
1823 gap: 10px;
1824 padding: 20px 14px 14px;
1825 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
1826 border-radius: var( --os-ui-card-radius, 10px );
1827 background: var( --os-ui-surface-sunken, #f6f7f7 );
1828 }
1829
1830 .dm-agents__portrait-face {
1831 width: 176px;
1832 height: 176px;
1833 max-width: 100%;
1834 margin-top: -12px;
1835 }
1836
1837 /* What this face is, in two words. */
1838 .dm-agents__portrait-chips {
1839 display: flex;
1840 gap: 6px;
1841 flex-wrap: wrap;
1842 justify-content: center;
1843 }
1844
1845 .dm-agents__faces {
1846 display: grid;
1847 grid-template-columns: repeat( 6, 1fr );
1848 gap: 2px;
1849 width: 100%;
1850 }
1851
1852 .dm-agents__face-pick {
1853 display: grid;
1854 place-items: center;
1855 padding: 2px;
1856 border: 1px solid transparent;
1857 border-radius: 8px;
1858 background: none;
1859 cursor: pointer;
1860 }
1861
1862 .dm-agents__face-pick img {
1863 width: 100%;
1864 height: auto;
1865 display: block;
1866 }
1867
1868 .dm-agents__face-pick:hover {
1869 border-color: var( --os-ui-border-strong, #8c8f94 );
1870 }
1871
1872 .dm-agents__face-pick.is-picked {
1873 border-color: var( --os-ui-accent, #2271b1 );
1874 }
1875
1876 .dm-agents__face-pick:focus-visible {
1877 outline: var( --os-ui-focus-ring, 2px solid #2271b1 );
1878 outline-offset: 1px;
1879 }
1880
1881 .dm-agents__meet-fields {
1882 display: flex;
1883 flex-direction: column;
1884 gap: 10px;
1885 min-width: 0;
1886 }
1887
1888 /* Launch: one card, the brand's card radius, floating on the darkest
1889 surface like the portrait it echoes. */
1890 .dm-agents__summary {
1891 display: grid;
1892 grid-template-columns: 106px 1fr;
1893 gap: 18px;
1894 align-items: start;
1895 --os-ui-card-padding: 18px;
1896 --os-ui-card-radius: 10px;
1897 --os-ui-card-bg: var( --os-ui-surface-sunken, #f6f7f7 );
1898 }
1899
1900 .dm-agents__summary-face {
1901 width: 106px;
1902 height: 106px;
1903 flex: none;
1904 }
1905
1906 .dm-agents__summary-text h4 {
1907 margin: 0 0 2px;
1908 font-size: 17px;
1909 }
1910
1911 .dm-agents__summary-vibes {
1912 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
1913 font-size: 11.5px;
1914 color: var( --os-ui-accent-strong, #2271b1 );
1915 margin: 0 0 6px;
1916 }
1917
1918 .dm-agents__summary-desc {
1919 margin: 0;
1920 font-size: 13px;
1921 color: var( --os-ui-fg-muted, #646970 );
1922 }
1923
1924 .dm-agents__chips {
1925 display: flex;
1926 flex-wrap: wrap;
1927 gap: 6px;
1928 margin-block-start: 10px;
1929 }
1930
1931 /* What the agent will actually be told, under the chips. Clamped: a
1932 summary card quotes the instructions, it does not host them. */
1933 .dm-agents__summary-instr {
1934 margin: 10px 0 0;
1935 font-size: 13px;
1936 color: var( --os-ui-fg-muted, #646970 );
1937 display: -webkit-box;
1938 -webkit-line-clamp: 3;
1939 -webkit-box-orient: vertical;
1940 overflow: hidden;
1941 }
1942
1943 /* Identity chips — role, abilities, silhouette, hue — all wear the
1944 instrument voice: these are facts about the character, stamped
1945 rather than said. */
1946 .dm-agents__chips os-chip,
1947 .dm-agents__portrait-chips os-chip {
1948 --os-ui-chip-font-size: 10.5px;
1949 --os-ui-chip-font-weight: 500;
1950 --os-ui-chip-bg: transparent;
1951 --os-ui-chip-fg: var( --os-ui-fg-muted, #646970 );
1952 --os-ui-chip-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) );
1953 font-family: var( --os-ui-font-mono, ui-monospace, monospace );
1954 text-transform: uppercase;
1955 letter-spacing: 0.06em;
1956 }
1957
1958 @media ( max-width: 640px ) {
1959 .dm-agents__meet,
1960 .dm-agents__summary {
1961 grid-template-columns: 1fr;
1962 }
1963 .dm-agents__portrait-face {
1964 width: 128px;
1965 height: 128px;
1966 }
1967 }
1968
1969 /* The way back to the grid. The window's own breadcrumb goes UP to the
1970 folder, which is a different journey: this one returns to the cast. */
1971 .dm-agents__back {
1972 align-self: flex-start;
1973 margin-block-end: 4px;
1974 }
1975
1976 /*
1977 * Long press is the touch right-click (`parts/long-press.ts`). What
1978 * the browser would do with a held finger — iOS's callout, a text
1979 * selection creeping across a label — is turned off on the pressable
1980 * surfaces, and only where a finger is the pointer: on a desk the
1981 * labels stay selectable.
1982 */
1983 @media ( pointer: coarse ) {
1984 .os-mywp__tile,
1985 .os-mywp__cell,
1986 .os-mywp__row,
1987 .os-mywp__canvas {
1988 -webkit-touch-callout: none;
1989 -webkit-user-select: none;
1990 user-select: none;
1991 }
1992 }
1993 html[data-os-mode="mobile"] .os-mywp__tile,
1994 html[data-os-mode="mobile"] .os-mywp__cell,
1995 html[data-os-mode="mobile"] .os-mywp__row,
1996 html[data-os-mode="mobile"] .os-mywp__canvas {
1997 -webkit-touch-callout: none;
1998 -webkit-user-select: none;
1999 user-select: none;
2000 }
2001