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 +699 -148 1.1.01.1.10 View file →
@@ -275,9 +275,13 @@
275 275 background: var(
276 276 --os-media-tile-bg,
277 277 rgba( 0, 0, 0, 0.05 )
278 278 );
279 - 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 ) );
280 284 }
281 285
282 286 /* Banded list views. A section can split its tiles into labelled
283 287 * bands (WooCommerce Orders groups by status, needs-attention first)
@@ -406,9 +410,12 @@
406 410 0 1px 3px rgba( 0, 0, 0, 0.35 );
407 411 pointer-events: none;
408 412 }
409 413
410 -/* 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. */
411 418 .os-my-wordpress__tooltip-lock {
412 419 display: flex;
413 420 align-items: center;
414 421 gap: 6px;
@@ -414,9 +421,12 @@
414 421 gap: 6px;
415 422 margin: 0 0 8px;
416 423 padding: 6px 8px;
417 424 border-radius: 4px;
418 - 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 + );
419 429 color: var( --os-danger, var( --os-ui-danger, #b32d2e ) );
420 430 font-size: 12px;
421 431 font-weight: 600;
422 432 }
@@ -701,9 +711,23 @@
701 711 .os-my-wordpress__error {
702 712 color: var( --os-danger, var( --os-ui-danger, #b32d2e ) );
703 713 }
704 714
705 -/* ----- 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. */
706 730
707 731 .os-my-wordpress__tooltip {
708 732 position: fixed;
709 733 z-index: 100000;
@@ -708,19 +732,28 @@
708 732 position: fixed;
709 733 z-index: 100000;
710 734 max-width: 320px;
711 735 background: var(
712 - --os-tooltip-bg,
713 - 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 + )
714 741 );
715 742 color: var(
716 - --os-tooltip-fg,
717 - var( --os-fg, #1d2327 )
743 + --os-my-wordpress-card-fg,
744 + var( --os-tooltip-fg, var( --os-fg, #1d2327 ) )
718 745 );
719 - 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 );
720 750 border-radius: 6px;
721 751 padding: 12px;
722 - 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 + );
723 756 font-size: 12px;
724 757 pointer-events: none;
725 758 }
726 759
@@ -729,8 +762,12 @@
729 762 margin-bottom: 6px;
730 763 font-size: 13px;
731 764 }
732 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. */
733 770 .os-my-wordpress__tooltip-thumb {
734 771 display: block;
735 772 max-width: 100%;
736 773 height: auto;
@@ -735,13 +772,18 @@
735 772 max-width: 100%;
736 773 height: auto;
737 774 border-radius: 4px;
738 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 + );
739 781 }
740 782
741 783 .os-my-wordpress__tooltip-excerpt {
742 784 margin: 0;
743 - color: var( --os-ui-fg-muted, #50575e );
785 + color: var( --os-my-wordpress-card-fg-muted, #50575e );
744 786 line-height: 1.4;
745 787 }
746 788
747 789 /* ----- Context menu host ----- */
@@ -804,9 +846,9 @@
804 846 font-size: 11px;
805 847 font-weight: 600;
806 848 text-transform: uppercase;
807 849 letter-spacing: 0.04em;
808 - background: rgba( 34, 113, 177, 0.12 );
850 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, transparent );
809 851 color: var( --wp-admin-theme-color, #2271b1 );
810 852 }
811 853
812 854 .os-my-wordpress__user-links {
@@ -909,9 +951,9 @@
909 951 .os-my-wordpress__user-spark-bar {
910 952 background: linear-gradient(
911 953 180deg,
912 954 var( --wp-admin-theme-color, #2271b1 ) 0%,
913 - rgba( 34, 113, 177, 0.55 ) 100%
955 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 55%, transparent ) 100%
914 956 );
915 957 border-radius: 3px 3px 0 0;
916 958 min-height: 2px;
917 959 transition: height 0.3s ease;
@@ -1045,9 +1087,9 @@
1045 1087 }
1046 1088
1047 1089 .os-my-wordpress__term-icon--category {
1048 1090 background: linear-gradient( 135deg, var( --os-ui-accent, #2271b1 ) 0%, var( --os-ui-accent-strong, #135e96 ) 100% );
1049 - 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 );
1050 1092 }
1051 1093
1052 1094 .os-my-wordpress__term-icon--tag {
1053 1095 background: linear-gradient( 135deg, #7c3aed 0%, #5b21b6 100% );
@@ -1054,9 +1096,9 @@
1054 1096 box-shadow: 0 4px 12px rgba( 124, 58, 237, 0.32 );
1055 1097 }
1056 1098
1057 1099 .os-my-wordpress__user-role--category {
1058 - background: rgba( 34, 113, 177, 0.12 );
1100 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, transparent );
1059 1101 color: var( --os-ui-accent-strong, #135e96 );
1060 1102 }
1061 1103
1062 1104 .os-my-wordpress__user-role--tag {
@@ -1116,9 +1158,9 @@
1116 1158 * for users with moderate_comments.
1117 1159 * --------------------------------------------------------------- */
1118 1160
1119 1161 .os-my-wordpress__comment-status--approved {
1120 - background: rgba( 34, 113, 177, 0.12 );
1162 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, transparent );
1121 1163 color: var( --os-ui-accent-strong, #135e96 );
1122 1164 }
1123 1165
1124 1166 .os-my-wordpress__comment-status--pending {
@@ -1149,9 +1191,9 @@
1149 1191 .os-my-wordpress__comment-quote {
1150 1192 margin: 0 0 16px;
1151 1193 padding: 10px 14px;
1152 1194 border-left: 3px solid var( --wp-admin-theme-color, #2271b1 );
1153 - background: rgba( 34, 113, 177, 0.06 );
1195 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 6%, transparent );
1154 1196 border-radius: 0 6px 6px 0;
1155 1197 font-size: 13px;
1156 1198 }
1157 1199
@@ -1294,10 +1336,10 @@
1294 1336 border-radius: 50%;
1295 1337 overflow: hidden;
1296 1338 background: linear-gradient(
1297 1339 135deg,
1298 - rgba( 34, 113, 177, 0.18 ),
1299 - 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 )
1300 1342 );
1301 1343 box-shadow:
1302 1344 inset 0 0 0 1px rgba( 0, 0, 0, 0.08 ),
1303 1345 0 2px 6px rgba( 0, 0, 0, 0.08 );
@@ -1418,10 +1460,10 @@
1418 1460 padding: 24px;
1419 1461 border-radius: 16px;
1420 1462 background: linear-gradient(
1421 1463 135deg,
1422 - rgba( 34, 113, 177, 0.10 ) 0%,
1423 - 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%,
1424 1466 transparent 100%
1425 1467 );
1426 1468 border: 1px solid var( --os-ui-border, #dcdcde );
1427 1469 }
@@ -1436,10 +1478,10 @@
1436 1478 inset 0 0 0 2px rgba( 255, 255, 255, 0.8 ),
1437 1479 0 6px 24px rgba( 0, 0, 0, 0.14 );
1438 1480 background: linear-gradient(
1439 1481 135deg,
1440 - rgba( 34, 113, 177, 0.18 ),
1441 - 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 )
1442 1484 );
1443 1485 display: inline-flex;
1444 1486 align-items: center;
1445 1487 justify-content: center;
@@ -1571,17 +1613,17 @@
1571 1613 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) );
1572 1614 }
1573 1615
1574 1616 .os-my-wordpress__footprint-cell--l1 {
1575 - background: rgba( 34, 113, 177, 0.25 );
1617 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 25%, transparent );
1576 1618 }
1577 1619
1578 1620 .os-my-wordpress__footprint-cell--l2 {
1579 - background: rgba( 34, 113, 177, 0.5 );
1621 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 50%, transparent );
1580 1622 }
1581 1623
1582 1624 .os-my-wordpress__footprint-cell--l3 {
1583 - background: rgba( 34, 113, 177, 0.75 );
1625 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 75%, transparent );
1584 1626 }
1585 1627
1586 1628 .os-my-wordpress__footprint-cell--l4 {
1587 1629 background: var( --wp-admin-theme-color, #2271b1 );
@@ -1651,9 +1693,9 @@
1651 1693 .os-my-wordpress__footprint-bar {
1652 1694 background: linear-gradient(
1653 1695 180deg,
1654 1696 var( --wp-admin-theme-color, #2271b1 ) 0%,
1655 - rgba( 34, 113, 177, 0.55 ) 100%
1697 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 55%, transparent ) 100%
1656 1698 );
1657 1699 border-radius: 3px 3px 0 0;
1658 1700 min-height: 2px;
1659 1701 transition: opacity 0.2s ease;
@@ -1685,10 +1727,10 @@
1685 1727 text-align: center;
1686 1728 padding: 20px 24px;
1687 1729 background: linear-gradient(
1688 1730 135deg,
1689 - rgba( 34, 113, 177, 0.10 ),
1690 - 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 )
1691 1733 );
1692 1734 }
1693 1735
1694 1736 .os-my-wordpress__footprint-callout-label {
@@ -2113,17 +2155,8 @@
2113 2155 overflow: hidden;
2114 2156 box-sizing: border-box;
2115 2157 }
2116 2158
2117 -/* The framework is opt-in but the section is always listed, so with
2118 - the option off the surface paints inert rather than vanishing.
2119 - Only the sidebar dims: the detail pane holds the empty state
2120 - explaining why, and the one button that undoes it — dimming the way
2121 - out along with everything else is how a disabled screen becomes a
2122 - dead end. */
2123 -.dm-agents.is-disabled .dm-agents__sidebar {
2124 - opacity: 0.6;
2125 -}
2126 2159
2127 2160 /* Section-level loading — same scale curve as the My WordPress
2128 2161 preview loader and the window-loading overlay, instead of the bare
2129 2162 48px component default. */
@@ -2138,103 +2171,8 @@
2138 2171 .dm-agents__loading os-spinner {
2139 2172 --os-ui-spinner-size: clamp( 96px, 14vw, 192px );
2140 2173 }
2141 2174
2142 -.dm-agents__layout {
2143 - display: flex;
2144 - flex: 1;
2145 - min-height: 0;
2146 - gap: 12px;
2147 -}
2148 -
2149 -/* Sidebar column: a fixed "Create agent" header over the scrolling
2150 - list. The button used to be the list's last child, which put it out
2151 - of reach on any site with enough agents to need it. */
2152 -.dm-agents__sidebar {
2153 - display: flex;
2154 - flex-direction: column;
2155 - flex: 0 0 260px;
2156 - /* `min-width: auto` on a flex item resolves to its min-content
2157 - width, and the rows carry long `white-space: nowrap` descriptions
2158 - — without this the column ignores its 260px basis and eats the
2159 - detail pane. The scroll container used to be this item, whose
2160 - non-visible overflow zeroed the minimum for free. */
2161 - min-width: 0;
2162 - min-height: 0;
2163 - border-inline-end: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2164 -}
2165 -
2166 -.dm-agents__list-toolbar {
2167 - flex: none;
2168 - display: flex;
2169 - padding-block-end: 8px;
2170 - padding-inline-end: 8px;
2171 -}
2172 -
2173 -.dm-agents__list-toolbar .dm-agents__create {
2174 - flex: 1;
2175 -}
2176 -
2177 -.dm-agents__list {
2178 - display: flex;
2179 - flex-direction: column;
2180 - gap: 4px;
2181 - flex: 1;
2182 - min-height: 0;
2183 - overflow-y: auto;
2184 - padding-inline-end: 4px;
2185 -}
2186 -
2187 -.dm-agents__row {
2188 - display: flex;
2189 - align-items: center;
2190 - gap: 8px;
2191 - padding: 8px;
2192 - border-radius: 8px;
2193 - cursor: pointer;
2194 -}
2195 -
2196 -.dm-agents__row:hover {
2197 - background: var( --os-hover, rgba( 0, 0, 0, 0.06 ) );
2198 -}
2199 -
2200 -.dm-agents__row.is-selected {
2201 - background: var( --os-selected, rgba( 0, 0, 0, 0.1 ) );
2202 -}
2203 -
2204 -.dm-agents__row-avatar {
2205 - width: 32px;
2206 - height: 32px;
2207 - border-radius: 50%;
2208 - /* Placeholder behind the avatar image while it loads — follows the
2209 - palette so it isn't a white disc on a dark station. */
2210 - background: var( --os-ui-surface-elevated, #fff );
2211 - flex: none;
2212 -}
2213 -
2214 -.dm-agents__row-text {
2215 - display: flex;
2216 - flex-direction: column;
2217 - min-width: 0;
2218 - flex: 1;
2219 -}
2220 -
2221 -.dm-agents__row-name {
2222 - font-weight: 600;
2223 - white-space: nowrap;
2224 - overflow: hidden;
2225 - text-overflow: ellipsis;
2226 -}
2227 -
2228 -.dm-agents__row-desc {
2229 - font-size: 12px;
2230 - opacity: 0.65;
2231 - white-space: nowrap;
2232 - overflow: hidden;
2233 - text-overflow: ellipsis;
2234 -}
2235 -
2236 -
2237 2175 .dm-agents__detail {
2238 2176 flex: 1;
2239 2177 min-width: 0;
2240 2178 overflow-y: auto;
@@ -2343,31 +2281,156 @@
2343 2281 font-size: 12px;
2344 2282 opacity: 0.7;
2345 2283 }
2346 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 +
2347 2312 .dm-agents__category {
2348 - margin: 8px 0 0;
2349 - 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;
2350 2317 text-transform: uppercase;
2351 - letter-spacing: 0.04em;
2352 - opacity: 0.6;
2318 + letter-spacing: 0.08em;
2319 + color: var( --os-ui-fg-muted, #646970 );
2353 2320 }
2354 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). */
2355 2391 .dm-agents__ability {
2356 2392 display: grid;
2357 2393 grid-template-columns: 1fr auto;
2358 - gap: 2px 8px;
2359 - align-items: center;
2360 - 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 ) );
2361 2398 }
2362 2399
2400 +.dm-agents__ability:last-of-type {
2401 + border-bottom: 0;
2402 +}
2403 +
2363 2404 .dm-agents__ability-desc {
2364 2405 grid-column: 1 / -1;
2365 2406 margin: 0;
2366 - font-size: 12px;
2367 - opacity: 0.6;
2407 + padding-inline-start: 26px;
2408 + font-size: 12.5px;
2409 + color: var( --os-ui-fg-muted, #646970 );
2368 2410 }
2369 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 +
2370 2433 .dm-agents__trigger {
2371 2434 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2372 2435 border-radius: 8px;
2373 2436 padding: 8px 10px;
@@ -2381,15 +2444,12 @@
2381 2444 align-items: center;
2382 2445 gap: 8px;
2383 2446 }
2384 2447
2385 -.dm-agents__trigger-summary {
2386 - flex: 1;
2448 +.dm-agents__trigger-desc {
2449 + margin: 0;
2387 2450 font-size: 12px;
2388 - opacity: 0.65;
2389 - white-space: nowrap;
2390 - overflow: hidden;
2391 - text-overflow: ellipsis;
2451 + opacity: 0.75;
2392 2452 }
2393 2453
2394 2454 .dm-agents__trigger-config {
2395 2455 display: flex;
@@ -2399,5 +2459,496 @@
2399 2459
2400 2460 .dm-agents__actions {
2401 2461 display: flex;
2402 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;
2403 2954 }