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

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