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
← All changes | assets/css/my-wordpress.css +725 -142 1.0.01.1.10 View file →
@@ -27,12 +27,24 @@
27 27 background: var(
28 28 --os-my-wordpress-bg,
29 29 var( --os-window-bg, #fff )
30 30 );
31 - /* Light-context tile color tokens — same recipe as the folder
32 - * window. Tiles inside My WordPress paint dark-on-light. */
31 + /* Tile color tokens — same recipe as the folder window. Both
32 + * chain through the palette so the surface follows the active
33 + * desktop theme, with the pre-brand WordPress-admin literal as
34 + * the floor if no stylesheet declares one.
35 + *
36 + * `--os-tile-fg-muted` used to be the bare literal below, which
37 + * outranked the palette's own value and painted every tile's
38 + * secondary line near-black — invisible on a dark station, while
39 + * the label right beside it followed the theme correctly. A
40 + * colour declared next to the thing it paints is out of reach of
41 + * the palette AND of every desktop theme. */
33 42 --os-tile-fg: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
34 - --os-tile-fg-muted: rgba( 0, 0, 0, 0.55 );
43 + --os-tile-fg-muted: var(
44 + --os-my-wordpress-fg-muted,
45 + var( --os-ui-fg-muted, rgba( 0, 0, 0, 0.55 ) )
46 + );
35 47 --os-tile-hover-bg: var(
36 48 --os-hover,
37 49 rgba( 0, 0, 0, 0.06 )
38 50 );
@@ -263,9 +275,13 @@
263 275 background: var(
264 276 --os-media-tile-bg,
265 277 rgba( 0, 0, 0, 0.05 )
266 278 );
267 - box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.12 );
279 + /* The hairline was a flat 12%-alpha black — right on the pre-brand
280 + * white canvas, and nothing at all on a dark one, where a black
281 + * line over a near-black surface leaves the photo edgeless. It
282 + * follows the border token now, so it inverts with the theme. */
283 + box-shadow: inset 0 0 0 1px var( --os-ui-border, rgba( 0, 0, 0, 0.12 ) );
268 284 }
269 285
270 286 /* Banded list views. A section can split its tiles into labelled
271 287 * bands (WooCommerce Orders groups by status, needs-attention first)
@@ -394,9 +410,12 @@
394 410 0 1px 3px rgba( 0, 0, 0, 0.35 );
395 411 pointer-events: none;
396 412 }
397 413
398 -/* Tooltip lock banner — shown above the excerpt when present. */
414 +/* Hover-card lock banner — shown above the excerpt when present. The
415 + * wash was a fixed 8%-alpha red, which on a dark card was a smear too
416 + * faint to read as a banner at all; it follows the badge-danger token
417 + * now, so it lands at the alpha the active theme picked. */
399 418 .os-my-wordpress__tooltip-lock {
400 419 display: flex;
401 420 align-items: center;
402 421 gap: 6px;
@@ -402,9 +421,12 @@
402 421 gap: 6px;
403 422 margin: 0 0 8px;
404 423 padding: 6px 8px;
405 424 border-radius: 4px;
406 - background: rgba( 179, 45, 46, 0.08 );
425 + background: var(
426 + --os-my-wordpress-card-lock-bg,
427 + rgba( 179, 45, 46, 0.08 )
428 + );
407 429 color: var( --os-danger, var( --os-ui-danger, #b32d2e ) );
408 430 font-size: 12px;
409 431 font-weight: 600;
410 432 }
@@ -689,9 +711,23 @@
689 711 .os-my-wordpress__error {
690 712 color: var( --os-danger, var( --os-ui-danger, #b32d2e ) );
691 713 }
692 714
693 -/* ----- Hover tooltip (floats over body) ----- */
715 +/* ----- Hover card (floats over body) -----
716 + *
717 + * The card is summoned from a tile and belongs to the window that
718 + * tile is in, so it follows the active desktop theme rather than the
719 + * shell's tooltip chip. Every colour chains
720 + * `--os-my-wordpress-card-*` first — derived in `variables.css` from
721 + * the window family, so a theme that repaints the window repaints the
722 + * card with it — then the chip tokens, then the pre-brand literal.
723 + *
724 + * It is appended to `document.body`, not to the window, because it is
725 + * `position: fixed` and a window with `overflow: hidden` would clip
726 + * it. Theme tokens still reach it: a desktop theme declares on
727 + * `body.os-desktop-theme-<slug>`, an ancestor of both. What does NOT
728 + * reach it is anything scoped to `.desktop-mode-my-wordpress`, which
729 + * is why the card family is declared at palette level. */
694 730
695 731 .os-my-wordpress__tooltip {
696 732 position: fixed;
697 733 z-index: 100000;
@@ -696,19 +732,28 @@
696 732 position: fixed;
697 733 z-index: 100000;
698 734 max-width: 320px;
699 735 background: var(
700 - --os-tooltip-bg,
701 - var( --os-surface, var( --os-ui-surface, #fff ) )
736 + --os-my-wordpress-card-bg,
737 + var(
738 + --os-tooltip-bg,
739 + var( --os-surface, var( --os-ui-surface, #fff ) )
740 + )
702 741 );
703 742 color: var(
704 - --os-tooltip-fg,
705 - var( --os-fg, #1d2327 )
743 + --os-my-wordpress-card-fg,
744 + var( --os-tooltip-fg, var( --os-fg, #1d2327 ) )
706 745 );
707 - border: 1px solid var( --os-ui-border, #dcdcde );
746 + /* The card lands on the same colour as the window under it — the
747 + * border and the shadow are the only reason it reads as a separate
748 + * object, so neither is decoration. */
749 + border: 1px solid var( --os-my-wordpress-card-border, #dcdcde );
708 750 border-radius: 6px;
709 751 padding: 12px;
710 - box-shadow: 0 6px 24px rgba( 0, 0, 0, 0.18 );
752 + box-shadow: var(
753 + --os-my-wordpress-card-shadow,
754 + 0 6px 24px rgba( 0, 0, 0, 0.18 )
755 + );
711 756 font-size: 12px;
712 757 pointer-events: none;
713 758 }
714 759
@@ -717,8 +762,12 @@
717 762 margin-bottom: 6px;
718 763 font-size: 13px;
719 764 }
720 765
766 +/* Same neutral well the entry and media tiles give their thumbnails:
767 + * a transparent PNG or a still-loading image would otherwise show the
768 + * card straight through, and a photo whose edge matches the card
769 + * would have no boundary. */
721 770 .os-my-wordpress__tooltip-thumb {
722 771 display: block;
723 772 max-width: 100%;
724 773 height: auto;
@@ -723,13 +772,18 @@
723 772 max-width: 100%;
724 773 height: auto;
725 774 border-radius: 4px;
726 775 margin: 6px 0;
776 + background: var( --os-my-wordpress-card-thumb-bg, rgba( 0, 0, 0, 0.05 ) );
777 + box-shadow: inset 0 0 0 1px var(
778 + --os-my-wordpress-card-border,
779 + #dcdcde
780 + );
727 781 }
728 782
729 783 .os-my-wordpress__tooltip-excerpt {
730 784 margin: 0;
731 - color: var( --os-ui-fg-muted, #50575e );
785 + color: var( --os-my-wordpress-card-fg-muted, #50575e );
732 786 line-height: 1.4;
733 787 }
734 788
735 789 /* ----- Context menu host ----- */
@@ -792,9 +846,9 @@
792 846 font-size: 11px;
793 847 font-weight: 600;
794 848 text-transform: uppercase;
795 849 letter-spacing: 0.04em;
796 - background: rgba( 34, 113, 177, 0.12 );
850 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, transparent );
797 851 color: var( --wp-admin-theme-color, #2271b1 );
798 852 }
799 853
800 854 .os-my-wordpress__user-links {
@@ -897,9 +951,9 @@
897 951 .os-my-wordpress__user-spark-bar {
898 952 background: linear-gradient(
899 953 180deg,
900 954 var( --wp-admin-theme-color, #2271b1 ) 0%,
901 - rgba( 34, 113, 177, 0.55 ) 100%
955 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 55%, transparent ) 100%
902 956 );
903 957 border-radius: 3px 3px 0 0;
904 958 min-height: 2px;
905 959 transition: height 0.3s ease;
@@ -1033,9 +1087,9 @@
1033 1087 }
1034 1088
1035 1089 .os-my-wordpress__term-icon--category {
1036 1090 background: linear-gradient( 135deg, var( --os-ui-accent, #2271b1 ) 0%, var( --os-ui-accent-strong, #135e96 ) 100% );
1037 - box-shadow: 0 4px 12px rgba( 34, 113, 177, 0.32 );
1091 + box-shadow: 0 4px 12px color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 32%, transparent );
1038 1092 }
1039 1093
1040 1094 .os-my-wordpress__term-icon--tag {
1041 1095 background: linear-gradient( 135deg, #7c3aed 0%, #5b21b6 100% );
@@ -1042,9 +1096,9 @@
1042 1096 box-shadow: 0 4px 12px rgba( 124, 58, 237, 0.32 );
1043 1097 }
1044 1098
1045 1099 .os-my-wordpress__user-role--category {
1046 - background: rgba( 34, 113, 177, 0.12 );
1100 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, transparent );
1047 1101 color: var( --os-ui-accent-strong, #135e96 );
1048 1102 }
1049 1103
1050 1104 .os-my-wordpress__user-role--tag {
@@ -1104,9 +1158,9 @@
1104 1158 * for users with moderate_comments.
1105 1159 * --------------------------------------------------------------- */
1106 1160
1107 1161 .os-my-wordpress__comment-status--approved {
1108 - background: rgba( 34, 113, 177, 0.12 );
1162 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, transparent );
1109 1163 color: var( --os-ui-accent-strong, #135e96 );
1110 1164 }
1111 1165
1112 1166 .os-my-wordpress__comment-status--pending {
@@ -1137,9 +1191,9 @@
1137 1191 .os-my-wordpress__comment-quote {
1138 1192 margin: 0 0 16px;
1139 1193 padding: 10px 14px;
1140 1194 border-left: 3px solid var( --wp-admin-theme-color, #2271b1 );
1141 - background: rgba( 34, 113, 177, 0.06 );
1195 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 6%, transparent );
1142 1196 border-radius: 0 6px 6px 0;
1143 1197 font-size: 13px;
1144 1198 }
1145 1199
@@ -1282,10 +1336,10 @@
1282 1336 border-radius: 50%;
1283 1337 overflow: hidden;
1284 1338 background: linear-gradient(
1285 1339 135deg,
1286 - rgba( 34, 113, 177, 0.18 ),
1287 - rgba( 34, 113, 177, 0.04 )
1340 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 18%, transparent ),
1341 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 4%, transparent )
1288 1342 );
1289 1343 box-shadow:
1290 1344 inset 0 0 0 1px rgba( 0, 0, 0, 0.08 ),
1291 1345 0 2px 6px rgba( 0, 0, 0, 0.08 );
@@ -1406,10 +1460,10 @@
1406 1460 padding: 24px;
1407 1461 border-radius: 16px;
1408 1462 background: linear-gradient(
1409 1463 135deg,
1410 - rgba( 34, 113, 177, 0.10 ) 0%,
1411 - rgba( 34, 113, 177, 0.02 ) 60%,
1464 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 10%, transparent ) 0%,
1465 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 2%, transparent ) 60%,
1412 1466 transparent 100%
1413 1467 );
1414 1468 border: 1px solid var( --os-ui-border, #dcdcde );
1415 1469 }
@@ -1424,10 +1478,10 @@
1424 1478 inset 0 0 0 2px rgba( 255, 255, 255, 0.8 ),
1425 1479 0 6px 24px rgba( 0, 0, 0, 0.14 );
1426 1480 background: linear-gradient(
1427 1481 135deg,
1428 - rgba( 34, 113, 177, 0.18 ),
1429 - rgba( 34, 113, 177, 0.04 )
1482 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 18%, transparent ),
1483 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 4%, transparent )
1430 1484 );
1431 1485 display: inline-flex;
1432 1486 align-items: center;
1433 1487 justify-content: center;
@@ -1559,17 +1613,17 @@
1559 1613 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) );
1560 1614 }
1561 1615
1562 1616 .os-my-wordpress__footprint-cell--l1 {
1563 - background: rgba( 34, 113, 177, 0.25 );
1617 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 25%, transparent );
1564 1618 }
1565 1619
1566 1620 .os-my-wordpress__footprint-cell--l2 {
1567 - background: rgba( 34, 113, 177, 0.5 );
1621 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 50%, transparent );
1568 1622 }
1569 1623
1570 1624 .os-my-wordpress__footprint-cell--l3 {
1571 - background: rgba( 34, 113, 177, 0.75 );
1625 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 75%, transparent );
1572 1626 }
1573 1627
1574 1628 .os-my-wordpress__footprint-cell--l4 {
1575 1629 background: var( --wp-admin-theme-color, #2271b1 );
@@ -1639,9 +1693,9 @@
1639 1693 .os-my-wordpress__footprint-bar {
1640 1694 background: linear-gradient(
1641 1695 180deg,
1642 1696 var( --wp-admin-theme-color, #2271b1 ) 0%,
1643 - rgba( 34, 113, 177, 0.55 ) 100%
1697 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 55%, transparent ) 100%
1644 1698 );
1645 1699 border-radius: 3px 3px 0 0;
1646 1700 min-height: 2px;
1647 1701 transition: opacity 0.2s ease;
@@ -1673,10 +1727,10 @@
1673 1727 text-align: center;
1674 1728 padding: 20px 24px;
1675 1729 background: linear-gradient(
1676 1730 135deg,
1677 - rgba( 34, 113, 177, 0.10 ),
1678 - rgba( 34, 113, 177, 0.02 )
1731 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 10%, transparent ),
1732 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 2%, transparent )
1679 1733 );
1680 1734 }
1681 1735
1682 1736 .os-my-wordpress__footprint-callout-label {
@@ -2101,8 +2155,9 @@
2101 2155 overflow: hidden;
2102 2156 box-sizing: border-box;
2103 2157 }
2104 2158
2159 +
2105 2160 /* Section-level loading — same scale curve as the My WordPress
2106 2161 preview loader and the window-loading overlay, instead of the bare
2107 2162 48px component default. */
2108 2163 .dm-agents__loading {
@@ -2116,112 +2171,27 @@
2116 2171 .dm-agents__loading os-spinner {
2117 2172 --os-ui-spinner-size: clamp( 96px, 14vw, 192px );
2118 2173 }
2119 2174
2120 -.dm-agents__layout {
2121 - display: flex;
2175 +.dm-agents__detail {
2122 2176 flex: 1;
2123 - min-height: 0;
2124 - gap: 12px;
2125 -}
2126 -
2127 -/* Sidebar column: a fixed "Create agent" header over the scrolling
2128 - list. The button used to be the list's last child, which put it out
2129 - of reach on any site with enough agents to need it. */
2130 -.dm-agents__sidebar {
2131 - display: flex;
2132 - flex-direction: column;
2133 - flex: 0 0 260px;
2134 - /* `min-width: auto` on a flex item resolves to its min-content
2135 - width, and the rows carry long `white-space: nowrap` descriptions
2136 - — without this the column ignores its 260px basis and eats the
2137 - detail pane. The scroll container used to be this item, whose
2138 - non-visible overflow zeroed the minimum for free. */
2139 2177 min-width: 0;
2140 - min-height: 0;
2141 - border-inline-end: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2142 -}
2143 -
2144 -.dm-agents__list-toolbar {
2145 - flex: none;
2146 - display: flex;
2147 - padding-block-end: 8px;
2148 - padding-inline-end: 8px;
2149 -}
2150 -
2151 -.dm-agents__list-toolbar .dm-agents__create {
2152 - flex: 1;
2153 -}
2154 -
2155 -.dm-agents__list {
2156 - display: flex;
2157 - flex-direction: column;
2158 - gap: 4px;
2159 - flex: 1;
2160 - min-height: 0;
2161 2178 overflow-y: auto;
2162 - padding-inline-end: 4px;
2163 -}
2164 -
2165 -.dm-agents__row {
2166 2179 display: flex;
2167 - align-items: center;
2168 - gap: 8px;
2169 - padding: 8px;
2170 - border-radius: 8px;
2171 - cursor: pointer;
2172 -}
2173 -
2174 -.dm-agents__row:hover {
2175 - background: var( --os-hover, rgba( 0, 0, 0, 0.06 ) );
2176 -}
2177 -
2178 -.dm-agents__row.is-selected {
2179 - background: var( --os-selected, rgba( 0, 0, 0, 0.1 ) );
2180 -}
2181 -
2182 -.dm-agents__row-avatar {
2183 - width: 32px;
2184 - height: 32px;
2185 - border-radius: 50%;
2186 - /* Placeholder behind the avatar image while it loads — follows the
2187 - palette so it isn't a white disc on a dark station. */
2188 - background: var( --os-ui-surface-elevated, #fff );
2189 - flex: none;
2190 -}
2191 -
2192 -.dm-agents__row-text {
2193 - display: flex;
2194 2180 flex-direction: column;
2195 - min-width: 0;
2196 - flex: 1;
2181 + gap: 10px;
2197 2182 }
2198 2183
2199 -.dm-agents__row-name {
2200 - font-weight: 600;
2201 - white-space: nowrap;
2202 - overflow: hidden;
2203 - text-overflow: ellipsis;
2184 +/* `<os-empty-state>` owns the icon + copy and centres them within
2185 + itself; the host is still a flex item that shrinks to its content,
2186 + so without this it sits at the top of a pane that is mostly empty
2187 + space. Auto margins on both axes absorb the slack — placement only,
2188 + same as the comments window. Covers the framework-off state and
2189 + "No agents yet" alike. */
2190 +.dm-agents__detail > os-empty-state {
2191 + margin: auto;
2204 2192 }
2205 2193
2206 -.dm-agents__row-desc {
2207 - font-size: 12px;
2208 - opacity: 0.65;
2209 - white-space: nowrap;
2210 - overflow: hidden;
2211 - text-overflow: ellipsis;
2212 -}
2213 -
2214 -
2215 -.dm-agents__detail {
2216 - flex: 1;
2217 - min-width: 0;
2218 - overflow-y: auto;
2219 - display: flex;
2220 - flex-direction: column;
2221 - gap: 10px;
2222 -}
2223 -
2224 2194 .dm-agents__detail-head {
2225 2195 display: flex;
2226 2196 align-items: center;
2227 2197 gap: 10px;
@@ -2311,31 +2281,156 @@
2311 2281 font-size: 12px;
2312 2282 opacity: 0.7;
2313 2283 }
2314 2284
2285 +/* Role first: least privilege is the decision, so the control holds
2286 + the mockup's width instead of stretching into a landing strip. */
2287 +.dm-agents__role-select {
2288 + max-width: 280px;
2289 +}
2290 +
2291 +/* The ability filter. Sized like the role select rather than the
2292 + full pane: it sits above a list of rules, and a search box as wide
2293 + as the rules reads as another one of them. */
2294 +.dm-agents__ability-search {
2295 + display: block;
2296 + max-width: 320px;
2297 + margin: 12px 0 4px;
2298 +}
2299 +
2300 +/* Each category is a native disclosure. `<details>` because the
2301 + behaviour is exactly what it is for: keyboard, screen-reader
2302 + semantics and the open/closed state all arrive for free, and the
2303 + only thing left to write is the costume. */
2304 +.dm-agents__ability-group {
2305 + border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2306 +}
2307 +
2308 +.dm-agents__ability-group:last-of-type {
2309 + border-bottom: 0;
2310 +}
2311 +
2315 2312 .dm-agents__category {
2316 - margin: 8px 0 0;
2317 - font-size: 12px;
2313 + margin: 18px 0 2px;
2314 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2315 + font-size: 11px;
2316 + font-weight: 500;
2318 2317 text-transform: uppercase;
2319 - letter-spacing: 0.04em;
2320 - opacity: 0.6;
2318 + letter-spacing: 0.08em;
2319 + color: var( --os-ui-fg-muted, #646970 );
2321 2320 }
2322 2321
2322 +/* A `<summary>` is a button, so it gets a button's affordances: a
2323 + pointer, a hover, and a focus ring that is visible on the dark
2324 + surface the section is drawn on. */
2325 +summary.dm-agents__category {
2326 + display: flex;
2327 + align-items: center;
2328 + gap: 8px;
2329 + margin: 0;
2330 + padding: 10px 0;
2331 + cursor: pointer;
2332 + list-style: none;
2333 + user-select: none;
2334 +}
2335 +
2336 +summary.dm-agents__category::-webkit-details-marker {
2337 + display: none;
2338 +}
2339 +
2340 +/* The marker is drawn here rather than left to the UA so it can be
2341 + the same triangle in every browser and turn with the disclosure.
2342 + Rotation only: a transform, so opening a group of forty rows
2343 + costs no layout. */
2344 +summary.dm-agents__category::before {
2345 + content: '';
2346 + flex: none;
2347 + width: 0;
2348 + height: 0;
2349 + border-inline-start: 5px solid currentcolor;
2350 + border-block: 4px solid transparent;
2351 + transition: transform 120ms ease;
2352 +}
2353 +
2354 +.dm-agents__ability-group[ open ] > summary.dm-agents__category::before {
2355 + transform: rotate( 90deg );
2356 +}
2357 +
2358 +@media ( prefers-reduced-motion: reduce ) {
2359 + summary.dm-agents__category::before {
2360 + transition: none;
2361 + }
2362 +}
2363 +
2364 +summary.dm-agents__category:hover {
2365 + color: var( --os-ui-fg, #1e1e1e );
2366 +}
2367 +
2368 +summary.dm-agents__category:focus-visible {
2369 + outline: 2px solid var( --os-ui-accent, #3858e9 );
2370 + outline-offset: 2px;
2371 + border-radius: 4px;
2372 +}
2373 +
2374 +.dm-agents__category-name {
2375 + flex: 1 1 auto;
2376 +}
2377 +
2378 +/* "3 of 12" when some are ticked, otherwise just the size of the
2379 + group. The count is why a closed group is still informative:
2380 + collapsing must not hide the fact that something in there is
2381 + switched on. */
2382 +.dm-agents__category-count {
2383 + flex: none;
2384 + font-variant-numeric: tabular-nums;
2385 + opacity: 0.75;
2386 +}
2387 +
2388 +/* One ability per row, ruled like the mockup: tick and name on the
2389 + left, badge on the right, the description indented under the name
2390 + (the checkbox column is the control's own 26px). */
2323 2391 .dm-agents__ability {
2324 2392 display: grid;
2325 2393 grid-template-columns: 1fr auto;
2326 - gap: 2px 8px;
2327 - align-items: center;
2328 - padding: 4px 0;
2394 + gap: 2px 10px;
2395 + align-items: start;
2396 + padding: 11px 0;
2397 + border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2329 2398 }
2330 2399
2400 +.dm-agents__ability:last-of-type {
2401 + border-bottom: 0;
2402 +}
2403 +
2331 2404 .dm-agents__ability-desc {
2332 2405 grid-column: 1 / -1;
2333 2406 margin: 0;
2334 - font-size: 12px;
2335 - opacity: 0.6;
2407 + padding-inline-start: 26px;
2408 + font-size: 12.5px;
2409 + color: var( --os-ui-fg-muted, #646970 );
2336 2410 }
2337 2411
2412 +/* Access badges in the instrument voice. The palette's own warning
2413 + tokens carry the pale-yellow "can modify"; all this changes is the
2414 + costume: mono, uppercase, an outline instead of a wash. */
2415 +.dm-agents__ability os-badge {
2416 + --os-ui-badge-font: 500 10px/1.6 var( --os-ui-font-mono, ui-monospace, monospace );
2417 + --os-ui-badge-dot-size: 0;
2418 + --os-ui-badge-gap: 0;
2419 + --os-ui-badge-padding: 3px 8px;
2420 + --os-ui-badge-bg: transparent;
2421 + --os-ui-badge-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) );
2422 + text-transform: uppercase;
2423 + letter-spacing: 0.06em;
2424 + white-space: nowrap;
2425 + margin-top: 2px;
2426 +}
2427 +
2428 +.dm-agents__ability os-badge[ tone='warning' ] {
2429 + --os-ui-badge-border: 1px solid
2430 + var( --os-ui-warning-border, rgba( 154, 103, 0, 0.4 ) );
2431 +}
2432 +
2338 2433 .dm-agents__trigger {
2339 2434 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2340 2435 border-radius: 8px;
2341 2436 padding: 8px 10px;
@@ -2349,15 +2444,12 @@
2349 2444 align-items: center;
2350 2445 gap: 8px;
2351 2446 }
2352 2447
2353 -.dm-agents__trigger-summary {
2354 - flex: 1;
2448 +.dm-agents__trigger-desc {
2449 + margin: 0;
2355 2450 font-size: 12px;
2356 - opacity: 0.65;
2357 - white-space: nowrap;
2358 - overflow: hidden;
2359 - text-overflow: ellipsis;
2451 + opacity: 0.75;
2360 2452 }
2361 2453
2362 2454 .dm-agents__trigger-config {
2363 2455 display: flex;
@@ -2367,5 +2459,496 @@
2367 2459
2368 2460 .dm-agents__actions {
2369 2461 display: flex;
2370 2462 gap: 8px;
2463 + align-items: center;
2464 + flex-wrap: wrap;
2465 + margin-block-start: 12px;
2466 +}
2467 +
2468 +/* ---------------------------------------------------------------------
2469 + The cast
2470 + ------------------------------------------------------------------ */
2471 +
2472 +/* One view at a time: the grid, an agent, or the wizard. A sidebar of
2473 + faces next to a detail form would leave neither enough room, and the
2474 + whole point of the grid is that the faces are big enough to tell
2475 + apart. */
2476 +.dm-agents__view {
2477 + display: flex;
2478 + flex-direction: column;
2479 + flex: 1;
2480 + min-height: 0;
2481 + overflow-y: auto;
2482 + gap: 12px;
2483 +}
2484 +
2485 +.dm-agents__cast-head {
2486 + display: flex;
2487 + align-items: baseline;
2488 + gap: 8px;
2489 +}
2490 +
2491 +.dm-agents__cast-head h3 {
2492 + margin: 0;
2493 +}
2494 +
2495 +.dm-agents__cast-count {
2496 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2497 + font-size: 12px;
2498 + color: var( --os-ui-fg-muted, #646970 );
2499 +}
2500 +
2501 +.dm-agents__cast {
2502 + display: grid;
2503 + gap: 16px;
2504 + grid-template-columns: repeat( auto-fill, minmax( 288px, 1fr ) );
2505 + align-content: start;
2506 +}
2507 +
2508 +/* The card reacts like the mockup's: a lift and a brighter edge.
2509 + Transform and border-color only — both cheap, and the same pair the
2510 + mockup transitions. Document styles win over the component's own
2511 + :host rules, which is the sanctioned way to restyle a host. */
2512 +.dm-agents__cast-card,
2513 +.dm-agents__cast-new {
2514 + --os-ui-card-padding: 20px 18px 16px;
2515 + transition: border-color 0.18s ease, transform 0.18s ease;
2516 +}
2517 +
2518 +.dm-agents__cast-card:hover,
2519 +.dm-agents__cast-card:focus-within {
2520 + border-color: var( --os-ui-border-strong, #8c8f94 );
2521 + transform: translateY( -2px );
2522 +}
2523 +
2524 +/* The dashed door, same as the mockup's crew-new card. */
2525 +.dm-agents__cast-new {
2526 + border-style: dashed;
2527 + --os-ui-card-bg: var( --os-ui-surface-subtle, transparent );
2528 +}
2529 +
2530 +/* The crew breathes. Transform only, staggered so five faces do not
2531 + bob in lockstep, and gone entirely under reduced motion. */
2532 +@media ( prefers-reduced-motion: no-preference ) {
2533 + .dm-agents__cast-card .dm-agents__cast-face {
2534 + animation: dm-agents-bob 6s ease-in-out infinite;
2535 + }
2536 + .dm-agents__cast-card:nth-child( 2n ) .dm-agents__cast-face {
2537 + animation-duration: 7.2s;
2538 + animation-delay: -1.4s;
2539 + }
2540 + .dm-agents__cast-card:nth-child( 3n ) .dm-agents__cast-face {
2541 + animation-duration: 6.6s;
2542 + animation-delay: -2.8s;
2543 + }
2544 +}
2545 +
2546 +@keyframes dm-agents-bob {
2547 + 0%,
2548 + 100% {
2549 + transform: translateY( 0 );
2550 + }
2551 + 50% {
2552 + transform: translateY( -4px );
2553 + }
2554 +}
2555 +
2556 +/* The column lives on a wrapper we own, not on the host: the slotted
2557 + children sit inside the card's own container, so a flex rule on the
2558 + host never reaches them and the role badges land wherever the
2559 + description's line count leaves them. */
2560 +.dm-agents__cast-inner {
2561 + display: flex;
2562 + flex-direction: column;
2563 + align-items: center;
2564 + text-align: center;
2565 + gap: 2px;
2566 + height: 100%;
2567 +}
2568 +
2569 +/* The faces are the loud part of this surface, and deliberately so:
2570 + agent identity is the one place the station spends the mesh budget
2571 + on something other than a hero CTA. A cast has to be legible at a
2572 + glance, which is exactly what a flat accent cannot do. */
2573 +.dm-agents__cast-face {
2574 + width: 118px;
2575 + height: 118px;
2576 + margin: -10px 0 6px;
2577 + flex: none;
2578 +}
2579 +
2580 +.dm-agents__cast-name {
2581 + font-size: 17px;
2582 + font-weight: 600;
2583 + color: var( --os-ui-fg, #1d2327 );
2584 +}
2585 +
2586 +/* The voice line, in the instrument register: this is metadata about
2587 + the agent, not prose from it. */
2588 +.dm-agents__cast-vibes {
2589 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2590 + font-size: 11.5px;
2591 + color: var( --os-ui-accent-strong, #2271b1 );
2592 +}
2593 +
2594 +.dm-agents__cast-good {
2595 + font-size: 13px;
2596 + color: var( --os-ui-fg-muted, #646970 );
2597 + margin-block: 4px 14px;
2598 + flex: 1;
2599 +}
2600 +
2601 +/* The role pill in the instrument voice: mono, uppercase, no status
2602 + dot — this is a fact about the agent, not a state to monitor. The
2603 + custom properties are the component's own theming hooks; the
2604 + inherited text properties flow into its shadow label. */
2605 +.dm-agents__cast-card os-badge {
2606 + --os-ui-badge-font: 500 11px/1.6 var( --os-ui-font-mono, ui-monospace, monospace );
2607 + --os-ui-badge-dot-size: 0;
2608 + --os-ui-badge-gap: 0;
2609 + --os-ui-badge-padding: 3px 10px;
2610 + text-transform: uppercase;
2611 + letter-spacing: 0.08em;
2612 +}
2613 +
2614 +/* The framework-off bar: the section's status and its one action, on
2615 + one line above the crew it would hire. It is deliberately not an
2616 + `os-empty-state` — that block is centred, icon-led and paragraph-
2617 + shaped, which is exactly the wall of text the cast replaced, and at
2618 + five cards tall it would push the button that undoes the situation
2619 + off the bottom of the window. `flex: none` because `.dm-agents__view`
2620 + is a scrolling flex column and the bar must not absorb the slack. */
2621 +.dm-agents__off-head {
2622 + display: flex;
2623 + align-items: center;
2624 + justify-content: space-between;
2625 + flex-wrap: wrap;
2626 + flex: none;
2627 + gap: 8px 16px;
2628 + padding: 12px 14px;
2629 + border-radius: 10px;
2630 + background: var( --os-ui-surface-elevated, #fff );
2631 + border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2632 +}
2633 +
2634 +.dm-agents__off-copy {
2635 + min-width: 0;
2636 +}
2637 +
2638 +.dm-agents__off-copy h3 {
2639 + margin: 0;
2640 +}
2641 +
2642 +.dm-agents__off-copy p {
2643 + margin: 2px 0 0;
2644 + font-size: 13px;
2645 + color: var( --os-ui-fg-muted, #646970 );
2646 +}
2647 +
2648 +/* The cast the site would get, drawn while the framework is off.
2649 + Dimmed as a whole rather than desaturated: the five hues ARE the
2650 + argument this state is making, and a greyscale filter would flatten
2651 + the crew into five identical blobs on the way to saying "not yet".
2652 + Opacity says inactive and leaves the faces telling each other apart.
2653 +
2654 + `pointer-events` is belt and braces — the cards are rendered without
2655 + `interactive` and without a click handler, so there is nothing to
2656 + press. It exists so a hover state cannot promise otherwise. */
2657 +.dm-agents__cast--preview {
2658 + opacity: 0.6;
2659 + pointer-events: none;
2660 +}
2661 +
2662 +.dm-agents__cast-new .dm-agents__cast-plus {
2663 + display: grid;
2664 + place-items: center;
2665 + width: 118px;
2666 + height: 118px;
2667 + margin: -10px 0 6px;
2668 + border-radius: 50%;
2669 + border: 1.5px dashed var( --os-ui-border-strong, #8c8f94 );
2670 + font-size: 30px;
2671 + font-weight: 300;
2672 + line-height: 1;
2673 + color: var( --os-ui-fg-muted, #646970 );
2674 +}
2675 +
2676 +/* ---------------------------------------------------------------------
2677 + The wizard
2678 + ------------------------------------------------------------------ */
2679 +
2680 +.dm-agents__wizard {
2681 + display: flex;
2682 + flex-direction: column;
2683 + gap: 10px;
2684 +}
2685 +
2686 +.dm-agents__wiz-head {
2687 + display: flex;
2688 + align-items: center;
2689 + justify-content: space-between;
2690 + gap: 8px 12px;
2691 + flex-wrap: wrap;
2692 +}
2693 +
2694 +.dm-agents__wiz-head h3 {
2695 + margin: 0;
2696 +}
2697 +
2698 +/* The trail wears the instrument voice: mono uppercase labels, 22px
2699 + dots, a thin rule between stations. All of it goes through the
2700 + component's own theming hooks; the host elements live in this light
2701 + DOM, so the per-state overrides below can select them directly. */
2702 +.dm-agents__trail {
2703 + margin-block: 6px 14px;
2704 + --os-ui-steps-gap: 10px;
2705 + --os-ui-step-gap: 7px;
2706 + --os-ui-step-chip-size: 22px;
2707 + --os-ui-step-chip-font-size: 11px;
2708 + --os-ui-step-chip-family: var( --os-ui-font-mono, ui-monospace, monospace );
2709 + --os-ui-step-title-size: 11px;
2710 + --os-ui-step-title-family: var( --os-ui-font-mono, ui-monospace, monospace );
2711 + --os-ui-step-title-transform: uppercase;
2712 + --os-ui-step-title-spacing: 0.08em;
2713 +}
2714 +
2715 +/* A station you have not reached is an outline, not a filled dot: the
2716 + mesh chip is where you ARE, and it only reads that way if it is the
2717 + only one lit. */
2718 +.dm-agents__trail os-step:not( [ done ] ):not( [ current ] ) {
2719 + --os-ui-step-chip-bg: transparent;
2720 + --os-ui-step-chip-border: 1px solid var( --os-ui-border-strong, #8c8f94 );
2721 + --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 );
2722 +}
2723 +
2724 +/* Done is quiet: a filled tick on the elevated surface, muted like its
2725 + label. It reads as history, not as a second highlight. */
2726 +.dm-agents__trail os-step[ done ] {
2727 + --os-ui-step-chip-done-bg: var( --os-ui-surface-elevated, #f0f0f1 );
2728 + --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 );
2729 +}
2730 +
2731 +.dm-agents__wiz-heading {
2732 + margin: 12px 0 2px;
2733 + font-size: 16px;
2734 + font-weight: 600;
2735 + color: var( --os-ui-fg, #1d2327 );
2736 +}
2737 +
2738 +/* The door. Five complete agents already ship with the plugin, and
2739 + until now the create flow showed them to nobody. */
2740 +.dm-agents__starters {
2741 + display: grid;
2742 + gap: 12px;
2743 + grid-template-columns: repeat( auto-fill, minmax( 148px, 1fr ) );
2744 + margin-block: 8px 16px;
2745 +}
2746 +
2747 +.dm-agents__starter {
2748 + display: flex;
2749 + flex-direction: column;
2750 + align-items: center;
2751 + text-align: center;
2752 + gap: 3px;
2753 + --os-ui-card-padding: 16px 14px;
2754 + --os-ui-card-radius: 10px;
2755 + --os-ui-card-bg: var( --os-ui-surface-subtle, transparent );
2756 + transition: border-color 0.18s ease;
2757 +}
2758 +
2759 +.dm-agents__starter:hover,
2760 +.dm-agents__starter:focus-within {
2761 + border-color: var( --os-ui-border-strong, #8c8f94 );
2762 +}
2763 +
2764 +.dm-agents__starter-face {
2765 + width: 76px;
2766 + height: 76px;
2767 + margin: -8px 0 4px;
2768 + flex: none;
2769 +}
2770 +
2771 +.dm-agents__starter-name {
2772 + font-size: 14px;
2773 + font-weight: 500;
2774 + color: var( --os-ui-fg, #1d2327 );
2775 +}
2776 +
2777 +.dm-agents__starter-vibes {
2778 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2779 + font-size: 11.5px;
2780 + color: var( --os-ui-fg-muted, #646970 );
2781 +}
2782 +
2783 +/* Meet: the portrait beside the fields, so the thing being named is
2784 + visible while it is being named. */
2785 +.dm-agents__meet {
2786 + display: grid;
2787 + gap: 20px;
2788 + grid-template-columns: 220px 1fr;
2789 + align-items: start;
2790 +}
2791 +
2792 +/* The character card. Sunken rather than subtle: the portrait is the
2793 + hero of this step, and the mockup floats it on the darkest surface
2794 + so the face's own glow does the framing. */
2795 +.dm-agents__portrait {
2796 + display: flex;
2797 + flex-direction: column;
2798 + align-items: center;
2799 + gap: 10px;
2800 + padding: 20px 14px 14px;
2801 + border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2802 + border-radius: var( --os-ui-card-radius, 10px );
2803 + background: var( --os-ui-surface-sunken, #f6f7f7 );
2804 +}
2805 +
2806 +.dm-agents__portrait-face {
2807 + width: 176px;
2808 + height: 176px;
2809 + max-width: 100%;
2810 + margin-top: -12px;
2811 +}
2812 +
2813 +/* What this face is, in two words. */
2814 +.dm-agents__portrait-chips {
2815 + display: flex;
2816 + gap: 6px;
2817 + flex-wrap: wrap;
2818 + justify-content: center;
2819 +}
2820 +
2821 +.dm-agents__faces {
2822 + display: grid;
2823 + grid-template-columns: repeat( 6, 1fr );
2824 + gap: 2px;
2825 + width: 100%;
2826 +}
2827 +
2828 +.dm-agents__face-pick {
2829 + display: grid;
2830 + place-items: center;
2831 + padding: 2px;
2832 + border: 1px solid transparent;
2833 + border-radius: 8px;
2834 + background: none;
2835 + cursor: pointer;
2836 +}
2837 +
2838 +.dm-agents__face-pick img {
2839 + width: 100%;
2840 + height: auto;
2841 + display: block;
2842 +}
2843 +
2844 +.dm-agents__face-pick:hover {
2845 + border-color: var( --os-ui-border-strong, #8c8f94 );
2846 +}
2847 +
2848 +.dm-agents__face-pick.is-picked {
2849 + border-color: var( --os-ui-accent, #2271b1 );
2850 +}
2851 +
2852 +.dm-agents__face-pick:focus-visible {
2853 + outline: var( --os-ui-focus-ring, 2px solid #2271b1 );
2854 + outline-offset: 1px;
2855 +}
2856 +
2857 +.dm-agents__meet-fields {
2858 + display: flex;
2859 + flex-direction: column;
2860 + gap: 10px;
2861 + min-width: 0;
2862 +}
2863 +
2864 +/* Launch: one card, the brand's card radius, floating on the darkest
2865 + surface like the portrait it echoes. */
2866 +.dm-agents__summary {
2867 + display: grid;
2868 + grid-template-columns: 106px 1fr;
2869 + gap: 18px;
2870 + align-items: start;
2871 + --os-ui-card-padding: 18px;
2872 + --os-ui-card-radius: 10px;
2873 + --os-ui-card-bg: var( --os-ui-surface-sunken, #f6f7f7 );
2874 +}
2875 +
2876 +.dm-agents__summary-face {
2877 + width: 106px;
2878 + height: 106px;
2879 + flex: none;
2880 +}
2881 +
2882 +.dm-agents__summary-text h4 {
2883 + margin: 0 0 2px;
2884 + font-size: 17px;
2885 +}
2886 +
2887 +.dm-agents__summary-vibes {
2888 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2889 + font-size: 11.5px;
2890 + color: var( --os-ui-accent-strong, #2271b1 );
2891 + margin: 0 0 6px;
2892 +}
2893 +
2894 +.dm-agents__summary-desc {
2895 + margin: 0;
2896 + font-size: 13px;
2897 + color: var( --os-ui-fg-muted, #646970 );
2898 +}
2899 +
2900 +.dm-agents__chips {
2901 + display: flex;
2902 + flex-wrap: wrap;
2903 + gap: 6px;
2904 + margin-block-start: 10px;
2905 +}
2906 +
2907 +/* What the agent will actually be told, under the chips. Clamped: a
2908 + summary card quotes the instructions, it does not host them. */
2909 +.dm-agents__summary-instr {
2910 + margin: 10px 0 0;
2911 + font-size: 13px;
2912 + color: var( --os-ui-fg-muted, #646970 );
2913 + display: -webkit-box;
2914 + -webkit-line-clamp: 3;
2915 + -webkit-box-orient: vertical;
2916 + overflow: hidden;
2917 +}
2918 +
2919 +/* Identity chips — role, abilities, silhouette, hue — all wear the
2920 + instrument voice: these are facts about the character, stamped
2921 + rather than said. */
2922 +.dm-agents__chips os-chip,
2923 +.dm-agents__portrait-chips os-chip {
2924 + --os-ui-chip-font-size: 10.5px;
2925 + --os-ui-chip-font-weight: 500;
2926 + --os-ui-chip-bg: transparent;
2927 + --os-ui-chip-fg: var( --os-ui-fg-muted, #646970 );
2928 + --os-ui-chip-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) );
2929 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2930 + text-transform: uppercase;
2931 + letter-spacing: 0.06em;
2932 +}
2933 +
2934 +.dm-agents__spacer {
2935 + flex: 1;
2936 +}
2937 +
2938 +@media ( max-width: 640px ) {
2939 + .dm-agents__meet,
2940 + .dm-agents__summary {
2941 + grid-template-columns: 1fr;
2942 + }
2943 + .dm-agents__portrait-face {
2944 + width: 128px;
2945 + height: 128px;
2946 + }
2947 +}
2948 +
2949 +/* The way back to the grid. The window's own breadcrumb goes UP to the
2950 + folder, which is a different journey: this one returns to the cast. */
2951 +.dm-agents__back {
2952 + align-self: flex-start;
2953 + margin-block-end: 4px;
2371 2954 }