PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.13
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.13
1.1.13 1.1.12 1.1.11 1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 All 37 releases
← All changes | assets/css/my-wordpress.css +714 -153 1.0.1 → 1.1.13 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 {
@@ -826,9 +868,9 @@
826 868
827 869 .os-my-wordpress__user-bio {
828 870 font-size: 14px;
829 871 line-height: 1.5;
830 - color: var( --os-fg, #1d2327 );
872 + color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
831 873 margin: 0 0 16px;
832 874 padding-left: 12px;
833 875 border-left: 3px solid var( --os-ui-border, #dcdcde );
834 876 }
@@ -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
@@ -1165,9 +1207,9 @@
1165 1207 }
1166 1208
1167 1209 .os-my-wordpress__comment-quote p {
1168 1210 margin: 0;
1169 - color: var( --os-fg, #1d2327 );
1211 + color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
1170 1212 }
1171 1213
1172 1214 /* Comment body — same style as article-content but with a soft
1173 1215 * card background so it reads as the focal element. */
@@ -1257,9 +1299,9 @@
1257 1299
1258 1300 .os-my-wordpress__comment-reply-excerpt {
1259 1301 margin: 0;
1260 1302 font-size: 13px;
1261 - color: var( --os-fg, #1d2327 );
1303 + color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
1262 1304 line-height: 1.45;
1263 1305 }
1264 1306
1265 1307 /* =================================================================== *
@@ -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;
@@ -1464,14 +1506,17 @@
1464 1506 flex-direction: column;
1465 1507 gap: 8px;
1466 1508 }
1467 1509
1510 +/* Window text reads the app's own foreground, never `--os-fg`: that token
1511 + * is the shell's text ON THE DESK, so a light theme (Legacy) leaves it
1512 + * white and the name vanished into the white hero. */
1468 1513 .os-my-wordpress__footprint-title {
1469 1514 margin: 0;
1470 1515 font-size: 28px;
1471 1516 line-height: 1.15;
1472 1517 font-weight: 700;
1473 - color: var( --os-fg, #1d2327 );
1518 + color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
1474 1519 }
1475 1520
1476 1521 .os-my-wordpress__footprint-meta {
1477 1522 display: flex;
@@ -1571,17 +1616,17 @@
1571 1616 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) );
1572 1617 }
1573 1618
1574 1619 .os-my-wordpress__footprint-cell--l1 {
1575 - background: rgba( 34, 113, 177, 0.25 );
1620 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 25%, transparent );
1576 1621 }
1577 1622
1578 1623 .os-my-wordpress__footprint-cell--l2 {
1579 - background: rgba( 34, 113, 177, 0.5 );
1624 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 50%, transparent );
1580 1625 }
1581 1626
1582 1627 .os-my-wordpress__footprint-cell--l3 {
1583 - background: rgba( 34, 113, 177, 0.75 );
1628 + background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 75%, transparent );
1584 1629 }
1585 1630
1586 1631 .os-my-wordpress__footprint-cell--l4 {
1587 1632 background: var( --wp-admin-theme-color, #2271b1 );
@@ -1651,9 +1696,9 @@
1651 1696 .os-my-wordpress__footprint-bar {
1652 1697 background: linear-gradient(
1653 1698 180deg,
1654 1699 var( --wp-admin-theme-color, #2271b1 ) 0%,
1655 - rgba( 34, 113, 177, 0.55 ) 100%
1700 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 55%, transparent ) 100%
1656 1701 );
1657 1702 border-radius: 3px 3px 0 0;
1658 1703 min-height: 2px;
1659 1704 transition: opacity 0.2s ease;
@@ -1685,10 +1730,10 @@
1685 1730 text-align: center;
1686 1731 padding: 20px 24px;
1687 1732 background: linear-gradient(
1688 1733 135deg,
1689 - rgba( 34, 113, 177, 0.10 ),
1690 - rgba( 34, 113, 177, 0.02 )
1734 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 10%, transparent ),
1735 + color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 2%, transparent )
1691 1736 );
1692 1737 }
1693 1738
1694 1739 .os-my-wordpress__footprint-callout-label {
@@ -1702,9 +1747,9 @@
1702 1747 .os-my-wordpress__footprint-callout-value {
1703 1748 margin: 2px 0;
1704 1749 font-size: 24px;
1705 1750 font-weight: 700;
1706 - color: var( --os-fg, #1d2327 );
1751 + color: var( --os-my-wordpress-fg, var( --os-ui-fg, #1d2327 ) );
1707 1752 }
1708 1753
1709 1754 .os-my-wordpress__footprint-callout-detail {
1710 1755 margin: 0;
@@ -2113,17 +2158,8 @@
2113 2158 overflow: hidden;
2114 2159 box-sizing: border-box;
2115 2160 }
2116 2161
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 2162
2127 2163 /* Section-level loading — same scale curve as the My WordPress
2128 2164 preview loader and the window-loading overlay, instead of the bare
2129 2165 48px component default. */
@@ -2138,103 +2174,8 @@
2138 2174 .dm-agents__loading os-spinner {
2139 2175 --os-ui-spinner-size: clamp( 96px, 14vw, 192px );
2140 2176 }
2141 2177
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 2178 .dm-agents__detail {
2238 2179 flex: 1;
2239 2180 min-width: 0;
2240 2181 overflow-y: auto;
@@ -2343,31 +2284,156 @@
2343 2284 font-size: 12px;
2344 2285 opacity: 0.7;
2345 2286 }
2346 2287
2288 +/* Role first: least privilege is the decision, so the control holds
2289 + the mockup's width instead of stretching into a landing strip. */
2290 +.dm-agents__role-select {
2291 + max-width: 280px;
2292 +}
2293 +
2294 +/* The ability filter. Sized like the role select rather than the
2295 + full pane: it sits above a list of rules, and a search box as wide
2296 + as the rules reads as another one of them. */
2297 +.dm-agents__ability-search {
2298 + display: block;
2299 + max-width: 320px;
2300 + margin: 12px 0 4px;
2301 +}
2302 +
2303 +/* Each category is a native disclosure. `<details>` because the
2304 + behaviour is exactly what it is for: keyboard, screen-reader
2305 + semantics and the open/closed state all arrive for free, and the
2306 + only thing left to write is the costume. */
2307 +.dm-agents__ability-group {
2308 + border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2309 +}
2310 +
2311 +.dm-agents__ability-group:last-of-type {
2312 + border-bottom: 0;
2313 +}
2314 +
2347 2315 .dm-agents__category {
2348 - margin: 8px 0 0;
2349 - font-size: 12px;
2316 + margin: 18px 0 2px;
2317 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2318 + font-size: 11px;
2319 + font-weight: 500;
2350 2320 text-transform: uppercase;
2351 - letter-spacing: 0.04em;
2352 - opacity: 0.6;
2321 + letter-spacing: 0.08em;
2322 + color: var( --os-ui-fg-muted, #646970 );
2353 2323 }
2354 2324
2325 +/* A `<summary>` is a button, so it gets a button's affordances: a
2326 + pointer, a hover, and a focus ring that is visible on the dark
2327 + surface the section is drawn on. */
2328 +summary.dm-agents__category {
2329 + display: flex;
2330 + align-items: center;
2331 + gap: 8px;
2332 + margin: 0;
2333 + padding: 10px 0;
2334 + cursor: pointer;
2335 + list-style: none;
2336 + user-select: none;
2337 +}
2338 +
2339 +summary.dm-agents__category::-webkit-details-marker {
2340 + display: none;
2341 +}
2342 +
2343 +/* The marker is drawn here rather than left to the UA so it can be
2344 + the same triangle in every browser and turn with the disclosure.
2345 + Rotation only: a transform, so opening a group of forty rows
2346 + costs no layout. */
2347 +summary.dm-agents__category::before {
2348 + content: '';
2349 + flex: none;
2350 + width: 0;
2351 + height: 0;
2352 + border-inline-start: 5px solid currentcolor;
2353 + border-block: 4px solid transparent;
2354 + transition: transform 120ms ease;
2355 +}
2356 +
2357 +.dm-agents__ability-group[ open ] > summary.dm-agents__category::before {
2358 + transform: rotate( 90deg );
2359 +}
2360 +
2361 +@media ( prefers-reduced-motion: reduce ) {
2362 + summary.dm-agents__category::before {
2363 + transition: none;
2364 + }
2365 +}
2366 +
2367 +summary.dm-agents__category:hover {
2368 + color: var( --os-ui-fg, #1e1e1e );
2369 +}
2370 +
2371 +summary.dm-agents__category:focus-visible {
2372 + outline: 2px solid var( --os-ui-accent, #3858e9 );
2373 + outline-offset: 2px;
2374 + border-radius: 4px;
2375 +}
2376 +
2377 +.dm-agents__category-name {
2378 + flex: 1 1 auto;
2379 +}
2380 +
2381 +/* "3 of 12" when some are ticked, otherwise just the size of the
2382 + group. The count is why a closed group is still informative:
2383 + collapsing must not hide the fact that something in there is
2384 + switched on. */
2385 +.dm-agents__category-count {
2386 + flex: none;
2387 + font-variant-numeric: tabular-nums;
2388 + opacity: 0.75;
2389 +}
2390 +
2391 +/* One ability per row, ruled like the mockup: tick and name on the
2392 + left, badge on the right, the description indented under the name
2393 + (the checkbox column is the control's own 26px). */
2355 2394 .dm-agents__ability {
2356 2395 display: grid;
2357 2396 grid-template-columns: 1fr auto;
2358 - gap: 2px 8px;
2359 - align-items: center;
2360 - padding: 4px 0;
2397 + gap: 2px 10px;
2398 + align-items: start;
2399 + padding: 11px 0;
2400 + border-bottom: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2361 2401 }
2362 2402
2403 +.dm-agents__ability:last-of-type {
2404 + border-bottom: 0;
2405 +}
2406 +
2363 2407 .dm-agents__ability-desc {
2364 2408 grid-column: 1 / -1;
2365 2409 margin: 0;
2366 - font-size: 12px;
2367 - opacity: 0.6;
2410 + padding-inline-start: 26px;
2411 + font-size: 12.5px;
2412 + color: var( --os-ui-fg-muted, #646970 );
2368 2413 }
2369 2414
2415 +/* Access badges in the instrument voice. The palette's own warning
2416 + tokens carry the pale-yellow "can modify"; all this changes is the
2417 + costume: mono, uppercase, an outline instead of a wash. */
2418 +.dm-agents__ability os-badge {
2419 + --os-ui-badge-font: 500 10px/1.6 var( --os-ui-font-mono, ui-monospace, monospace );
2420 + --os-ui-badge-dot-size: 0;
2421 + --os-ui-badge-gap: 0;
2422 + --os-ui-badge-padding: 3px 8px;
2423 + --os-ui-badge-bg: transparent;
2424 + --os-ui-badge-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) );
2425 + text-transform: uppercase;
2426 + letter-spacing: 0.06em;
2427 + white-space: nowrap;
2428 + margin-top: 2px;
2429 +}
2430 +
2431 +.dm-agents__ability os-badge[ tone='warning' ] {
2432 + --os-ui-badge-border: 1px solid
2433 + var( --os-ui-warning-border, rgba( 154, 103, 0, 0.4 ) );
2434 +}
2435 +
2370 2436 .dm-agents__trigger {
2371 2437 border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2372 2438 border-radius: 8px;
2373 2439 padding: 8px 10px;
@@ -2381,15 +2447,12 @@
2381 2447 align-items: center;
2382 2448 gap: 8px;
2383 2449 }
2384 2450
2385 -.dm-agents__trigger-summary {
2386 - flex: 1;
2451 +.dm-agents__trigger-desc {
2452 + margin: 0;
2387 2453 font-size: 12px;
2388 - opacity: 0.65;
2389 - white-space: nowrap;
2390 - overflow: hidden;
2391 - text-overflow: ellipsis;
2454 + opacity: 0.75;
2392 2455 }
2393 2456
2394 2457 .dm-agents__trigger-config {
2395 2458 display: flex;
@@ -2399,5 +2462,503 @@
2399 2462
2400 2463 .dm-agents__actions {
2401 2464 display: flex;
2402 2465 gap: 8px;
2466 + align-items: center;
2467 + flex-wrap: wrap;
2468 + margin-block-start: 12px;
2469 +}
2470 +
2471 +/* ---------------------------------------------------------------------
2472 + The cast
2473 + ------------------------------------------------------------------ */
2474 +
2475 +/* One view at a time: the grid, an agent, or the wizard. A sidebar of
2476 + faces next to a detail form would leave neither enough room, and the
2477 + whole point of the grid is that the faces are big enough to tell
2478 + apart. */
2479 +/* The pane scrolls, and a scroll container clips ink overflow: a field
2480 + flush with its edge loses the focus halo the kit paints outside the
2481 + border (1px ring + 4px bloom), so the brief's ring read as a top and
2482 + bottom line with nothing at the sides. Trade 5px of the section's
2483 + 12px padding for a gutter inside the pane; the content does not move. */
2484 +.dm-agents__view {
2485 + display: flex;
2486 + flex-direction: column;
2487 + flex: 1;
2488 + min-height: 0;
2489 + overflow-y: auto;
2490 + gap: 12px;
2491 + margin-inline: -5px;
2492 + padding-inline: 5px;
2493 +}
2494 +
2495 +.dm-agents__cast-head {
2496 + display: flex;
2497 + align-items: baseline;
2498 + gap: 8px;
2499 +}
2500 +
2501 +.dm-agents__cast-head h3 {
2502 + margin: 0;
2503 +}
2504 +
2505 +.dm-agents__cast-count {
2506 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2507 + font-size: 12px;
2508 + color: var( --os-ui-fg-muted, #646970 );
2509 +}
2510 +
2511 +.dm-agents__cast {
2512 + display: grid;
2513 + gap: 16px;
2514 + grid-template-columns: repeat( auto-fill, minmax( 288px, 1fr ) );
2515 + align-content: start;
2516 +}
2517 +
2518 +/* The card reacts like the mockup's: a lift and a brighter edge.
2519 + Transform and border-color only — both cheap, and the same pair the
2520 + mockup transitions. Document styles win over the component's own
2521 + :host rules, which is the sanctioned way to restyle a host. */
2522 +.dm-agents__cast-card,
2523 +.dm-agents__cast-new {
2524 + --os-ui-card-padding: 20px 18px 16px;
2525 + transition: border-color 0.18s ease, transform 0.18s ease;
2526 +}
2527 +
2528 +.dm-agents__cast-card:hover,
2529 +.dm-agents__cast-card:focus-within {
2530 + border-color: var( --os-ui-border-strong, #8c8f94 );
2531 + transform: translateY( -2px );
2532 +}
2533 +
2534 +/* The dashed door, same as the mockup's crew-new card. */
2535 +.dm-agents__cast-new {
2536 + border-style: dashed;
2537 + --os-ui-card-bg: var( --os-ui-surface-subtle, transparent );
2538 +}
2539 +
2540 +/* The crew breathes. Transform only, staggered so five faces do not
2541 + bob in lockstep, and gone entirely under reduced motion. */
2542 +@media ( prefers-reduced-motion: no-preference ) {
2543 + .dm-agents__cast-card .dm-agents__cast-face {
2544 + animation: dm-agents-bob 6s ease-in-out infinite;
2545 + }
2546 + .dm-agents__cast-card:nth-child( 2n ) .dm-agents__cast-face {
2547 + animation-duration: 7.2s;
2548 + animation-delay: -1.4s;
2549 + }
2550 + .dm-agents__cast-card:nth-child( 3n ) .dm-agents__cast-face {
2551 + animation-duration: 6.6s;
2552 + animation-delay: -2.8s;
2553 + }
2554 +}
2555 +
2556 +@keyframes dm-agents-bob {
2557 + 0%,
2558 + 100% {
2559 + transform: translateY( 0 );
2560 + }
2561 + 50% {
2562 + transform: translateY( -4px );
2563 + }
2564 +}
2565 +
2566 +/* The column lives on a wrapper we own, not on the host: the slotted
2567 + children sit inside the card's own container, so a flex rule on the
2568 + host never reaches them and the role badges land wherever the
2569 + description's line count leaves them. */
2570 +.dm-agents__cast-inner {
2571 + display: flex;
2572 + flex-direction: column;
2573 + align-items: center;
2574 + text-align: center;
2575 + gap: 2px;
2576 + height: 100%;
2577 +}
2578 +
2579 +/* The faces are the loud part of this surface, and deliberately so:
2580 + agent identity is the one place the station spends the mesh budget
2581 + on something other than a hero CTA. A cast has to be legible at a
2582 + glance, which is exactly what a flat accent cannot do. */
2583 +.dm-agents__cast-face {
2584 + width: 118px;
2585 + height: 118px;
2586 + margin: -10px 0 6px;
2587 + flex: none;
2588 +}
2589 +
2590 +.dm-agents__cast-name {
2591 + font-size: 17px;
2592 + font-weight: 600;
2593 + color: var( --os-ui-fg, #1d2327 );
2594 +}
2595 +
2596 +/* The voice line, in the instrument register: this is metadata about
2597 + the agent, not prose from it. */
2598 +.dm-agents__cast-vibes {
2599 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2600 + font-size: 11.5px;
2601 + color: var( --os-ui-accent-strong, #2271b1 );
2602 +}
2603 +
2604 +.dm-agents__cast-good {
2605 + font-size: 13px;
2606 + color: var( --os-ui-fg-muted, #646970 );
2607 + margin-block: 4px 14px;
2608 + flex: 1;
2609 +}
2610 +
2611 +/* The role pill in the instrument voice: mono, uppercase, no status
2612 + dot — this is a fact about the agent, not a state to monitor. The
2613 + custom properties are the component's own theming hooks; the
2614 + inherited text properties flow into its shadow label. */
2615 +.dm-agents__cast-card os-badge {
2616 + --os-ui-badge-font: 500 11px/1.6 var( --os-ui-font-mono, ui-monospace, monospace );
2617 + --os-ui-badge-dot-size: 0;
2618 + --os-ui-badge-gap: 0;
2619 + --os-ui-badge-padding: 3px 10px;
2620 + text-transform: uppercase;
2621 + letter-spacing: 0.08em;
2622 +}
2623 +
2624 +/* The framework-off bar: the section's status and its one action, on
2625 + one line above the crew it would hire. It is deliberately not an
2626 + `os-empty-state` — that block is centred, icon-led and paragraph-
2627 + shaped, which is exactly the wall of text the cast replaced, and at
2628 + five cards tall it would push the button that undoes the situation
2629 + off the bottom of the window. `flex: none` because `.dm-agents__view`
2630 + is a scrolling flex column and the bar must not absorb the slack. */
2631 +.dm-agents__off-head {
2632 + display: flex;
2633 + align-items: center;
2634 + justify-content: space-between;
2635 + flex-wrap: wrap;
2636 + flex: none;
2637 + gap: 8px 16px;
2638 + padding: 12px 14px;
2639 + border-radius: 10px;
2640 + background: var( --os-ui-surface-elevated, #fff );
2641 + border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2642 +}
2643 +
2644 +.dm-agents__off-copy {
2645 + min-width: 0;
2646 +}
2647 +
2648 +.dm-agents__off-copy h3 {
2649 + margin: 0;
2650 +}
2651 +
2652 +.dm-agents__off-copy p {
2653 + margin: 2px 0 0;
2654 + font-size: 13px;
2655 + color: var( --os-ui-fg-muted, #646970 );
2656 +}
2657 +
2658 +/* The cast the site would get, drawn while the framework is off.
2659 + Dimmed as a whole rather than desaturated: the five hues ARE the
2660 + argument this state is making, and a greyscale filter would flatten
2661 + the crew into five identical blobs on the way to saying "not yet".
2662 + Opacity says inactive and leaves the faces telling each other apart.
2663 +
2664 + `pointer-events` is belt and braces — the cards are rendered without
2665 + `interactive` and without a click handler, so there is nothing to
2666 + press. It exists so a hover state cannot promise otherwise. */
2667 +.dm-agents__cast--preview {
2668 + opacity: 0.6;
2669 + pointer-events: none;
2670 +}
2671 +
2672 +.dm-agents__cast-new .dm-agents__cast-plus {
2673 + display: grid;
2674 + place-items: center;
2675 + width: 118px;
2676 + height: 118px;
2677 + margin: -10px 0 6px;
2678 + border-radius: 50%;
2679 + border: 1.5px dashed var( --os-ui-border-strong, #8c8f94 );
2680 + font-size: 30px;
2681 + font-weight: 300;
2682 + line-height: 1;
2683 + color: var( --os-ui-fg-muted, #646970 );
2684 +}
2685 +
2686 +/* ---------------------------------------------------------------------
2687 + The wizard
2688 + ------------------------------------------------------------------ */
2689 +
2690 +.dm-agents__wizard {
2691 + display: flex;
2692 + flex-direction: column;
2693 + gap: 10px;
2694 +}
2695 +
2696 +.dm-agents__wiz-head {
2697 + display: flex;
2698 + align-items: center;
2699 + justify-content: space-between;
2700 + gap: 8px 12px;
2701 + flex-wrap: wrap;
2702 +}
2703 +
2704 +.dm-agents__wiz-head h3 {
2705 + margin: 0;
2706 +}
2707 +
2708 +/* The trail wears the instrument voice: mono uppercase labels, 22px
2709 + dots, a thin rule between stations. All of it goes through the
2710 + component's own theming hooks; the host elements live in this light
2711 + DOM, so the per-state overrides below can select them directly. */
2712 +.dm-agents__trail {
2713 + margin-block: 6px 14px;
2714 + --os-ui-steps-gap: 10px;
2715 + --os-ui-step-gap: 7px;
2716 + --os-ui-step-chip-size: 22px;
2717 + --os-ui-step-chip-font-size: 11px;
2718 + --os-ui-step-chip-family: var( --os-ui-font-mono, ui-monospace, monospace );
2719 + --os-ui-step-title-size: 11px;
2720 + --os-ui-step-title-family: var( --os-ui-font-mono, ui-monospace, monospace );
2721 + --os-ui-step-title-transform: uppercase;
2722 + --os-ui-step-title-spacing: 0.08em;
2723 +}
2724 +
2725 +/* A station you have not reached is an outline, not a filled dot: the
2726 + mesh chip is where you ARE, and it only reads that way if it is the
2727 + only one lit. */
2728 +.dm-agents__trail os-step:not( [ done ] ):not( [ current ] ) {
2729 + --os-ui-step-chip-bg: transparent;
2730 + --os-ui-step-chip-border: 1px solid var( --os-ui-border-strong, #8c8f94 );
2731 + --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 );
2732 +}
2733 +
2734 +/* Done is quiet: a filled tick on the elevated surface, muted like its
2735 + label. It reads as history, not as a second highlight. */
2736 +.dm-agents__trail os-step[ done ] {
2737 + --os-ui-step-chip-done-bg: var( --os-ui-surface-elevated, #f0f0f1 );
2738 + --os-ui-step-chip-fg: var( --os-ui-fg-muted, #646970 );
2739 +}
2740 +
2741 +.dm-agents__wiz-heading {
2742 + margin: 12px 0 2px;
2743 + font-size: 16px;
2744 + font-weight: 600;
2745 + color: var( --os-ui-fg, #1d2327 );
2746 +}
2747 +
2748 +/* The door. Five complete agents already ship with the plugin, and
2749 + until now the create flow showed them to nobody. */
2750 +.dm-agents__starters {
2751 + display: grid;
2752 + gap: 12px;
2753 + grid-template-columns: repeat( auto-fill, minmax( 148px, 1fr ) );
2754 + margin-block: 8px 16px;
2755 +}
2756 +
2757 +.dm-agents__starter {
2758 + display: flex;
2759 + flex-direction: column;
2760 + align-items: center;
2761 + text-align: center;
2762 + gap: 3px;
2763 + --os-ui-card-padding: 16px 14px;
2764 + --os-ui-card-radius: 10px;
2765 + --os-ui-card-bg: var( --os-ui-surface-subtle, transparent );
2766 + transition: border-color 0.18s ease;
2767 +}
2768 +
2769 +.dm-agents__starter:hover,
2770 +.dm-agents__starter:focus-within {
2771 + border-color: var( --os-ui-border-strong, #8c8f94 );
2772 +}
2773 +
2774 +.dm-agents__starter-face {
2775 + width: 76px;
2776 + height: 76px;
2777 + margin: -8px 0 4px;
2778 + flex: none;
2779 +}
2780 +
2781 +.dm-agents__starter-name {
2782 + font-size: 14px;
2783 + font-weight: 500;
2784 + color: var( --os-ui-fg, #1d2327 );
2785 +}
2786 +
2787 +.dm-agents__starter-vibes {
2788 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2789 + font-size: 11.5px;
2790 + color: var( --os-ui-fg-muted, #646970 );
2791 +}
2792 +
2793 +/* Meet: the portrait beside the fields, so the thing being named is
2794 + visible while it is being named. */
2795 +.dm-agents__meet {
2796 + display: grid;
2797 + gap: 20px;
2798 + grid-template-columns: 220px 1fr;
2799 + align-items: start;
2800 +}
2801 +
2802 +/* The character card. Sunken rather than subtle: the portrait is the
2803 + hero of this step, and the mockup floats it on the darkest surface
2804 + so the face's own glow does the framing. */
2805 +.dm-agents__portrait {
2806 + display: flex;
2807 + flex-direction: column;
2808 + align-items: center;
2809 + gap: 10px;
2810 + padding: 20px 14px 14px;
2811 + border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.08 ) );
2812 + border-radius: var( --os-ui-card-radius, 10px );
2813 + background: var( --os-ui-surface-sunken, #f6f7f7 );
2814 +}
2815 +
2816 +.dm-agents__portrait-face {
2817 + width: 176px;
2818 + height: 176px;
2819 + max-width: 100%;
2820 + margin-top: -12px;
2821 +}
2822 +
2823 +/* What this face is, in two words. */
2824 +.dm-agents__portrait-chips {
2825 + display: flex;
2826 + gap: 6px;
2827 + flex-wrap: wrap;
2828 + justify-content: center;
2829 +}
2830 +
2831 +.dm-agents__faces {
2832 + display: grid;
2833 + grid-template-columns: repeat( 6, 1fr );
2834 + gap: 2px;
2835 + width: 100%;
2836 +}
2837 +
2838 +.dm-agents__face-pick {
2839 + display: grid;
2840 + place-items: center;
2841 + padding: 2px;
2842 + border: 1px solid transparent;
2843 + border-radius: 8px;
2844 + background: none;
2845 + cursor: pointer;
2846 +}
2847 +
2848 +.dm-agents__face-pick img {
2849 + width: 100%;
2850 + height: auto;
2851 + display: block;
2852 +}
2853 +
2854 +.dm-agents__face-pick:hover {
2855 + border-color: var( --os-ui-border-strong, #8c8f94 );
2856 +}
2857 +
2858 +.dm-agents__face-pick.is-picked {
2859 + border-color: var( --os-ui-accent, #2271b1 );
2860 +}
2861 +
2862 +.dm-agents__face-pick:focus-visible {
2863 + outline: var( --os-ui-focus-ring, 2px solid #2271b1 );
2864 + outline-offset: 1px;
2865 +}
2866 +
2867 +.dm-agents__meet-fields {
2868 + display: flex;
2869 + flex-direction: column;
2870 + gap: 10px;
2871 + min-width: 0;
2872 +}
2873 +
2874 +/* Launch: one card, the brand's card radius, floating on the darkest
2875 + surface like the portrait it echoes. */
2876 +.dm-agents__summary {
2877 + display: grid;
2878 + grid-template-columns: 106px 1fr;
2879 + gap: 18px;
2880 + align-items: start;
2881 + --os-ui-card-padding: 18px;
2882 + --os-ui-card-radius: 10px;
2883 + --os-ui-card-bg: var( --os-ui-surface-sunken, #f6f7f7 );
2884 +}
2885 +
2886 +.dm-agents__summary-face {
2887 + width: 106px;
2888 + height: 106px;
2889 + flex: none;
2890 +}
2891 +
2892 +.dm-agents__summary-text h4 {
2893 + margin: 0 0 2px;
2894 + font-size: 17px;
2895 +}
2896 +
2897 +.dm-agents__summary-vibes {
2898 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2899 + font-size: 11.5px;
2900 + color: var( --os-ui-accent-strong, #2271b1 );
2901 + margin: 0 0 6px;
2902 +}
2903 +
2904 +.dm-agents__summary-desc {
2905 + margin: 0;
2906 + font-size: 13px;
2907 + color: var( --os-ui-fg-muted, #646970 );
2908 +}
2909 +
2910 +.dm-agents__chips {
2911 + display: flex;
2912 + flex-wrap: wrap;
2913 + gap: 6px;
2914 + margin-block-start: 10px;
2915 +}
2916 +
2917 +/* What the agent will actually be told, under the chips. Clamped: a
2918 + summary card quotes the instructions, it does not host them. */
2919 +.dm-agents__summary-instr {
2920 + margin: 10px 0 0;
2921 + font-size: 13px;
2922 + color: var( --os-ui-fg-muted, #646970 );
2923 + display: -webkit-box;
2924 + -webkit-line-clamp: 3;
2925 + -webkit-box-orient: vertical;
2926 + overflow: hidden;
2927 +}
2928 +
2929 +/* Identity chips — role, abilities, silhouette, hue — all wear the
2930 + instrument voice: these are facts about the character, stamped
2931 + rather than said. */
2932 +.dm-agents__chips os-chip,
2933 +.dm-agents__portrait-chips os-chip {
2934 + --os-ui-chip-font-size: 10.5px;
2935 + --os-ui-chip-font-weight: 500;
2936 + --os-ui-chip-bg: transparent;
2937 + --os-ui-chip-fg: var( --os-ui-fg-muted, #646970 );
2938 + --os-ui-chip-border: 1px solid var( --os-border, rgba( 0, 0, 0, 0.12 ) );
2939 + font-family: var( --os-ui-font-mono, ui-monospace, monospace );
2940 + text-transform: uppercase;
2941 + letter-spacing: 0.06em;
2942 +}
2943 +
2944 +.dm-agents__spacer {
2945 + flex: 1;
2946 +}
2947 +
2948 +@media ( max-width: 640px ) {
2949 + .dm-agents__meet,
2950 + .dm-agents__summary {
2951 + grid-template-columns: 1fr;
2952 + }
2953 + .dm-agents__portrait-face {
2954 + width: 128px;
2955 + height: 128px;
2956 + }
2957 +}
2958 +
2959 +/* The way back to the grid. The window's own breadcrumb goes UP to the
2960 + folder, which is a different journey: this one returns to the cast. */
2961 +.dm-agents__back {
2962 + align-self: flex-start;
2963 + margin-block-end: 4px;
2403 2964 }