PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.5
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.5
4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 All 203 releases
betterdocs / react-src / admin / mcp / mcp.scss

mcp.scss in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.5, at react-src/admin/mcp/mcp.scss

1,248 lines 36.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * MCP page — the delta on top of the shared `bda-*` shell.
3 *
4 * Everything that already exists is reused by class name: `.bda-card`,
5 * `.bda-card-head/body/title`, `.bda-btn` (`primary` / `ghost` / `sm`),
6 * `.bda-badge` (`green` / `amber` / `red` / `slate`), `.bda-table` and its
7 * `.bda-t-right` / `.bda-strong` helpers. Only what this screen genuinely adds
8 * is below, and every colour is a token so dark mode follows for free
9 * (`docs/design-system.md` → Foundations rule 2).
10 */
11
12 /**
13 * The `bda-*` alias tokens, re-declared for this page.
14 *
15 * BetterDocs Pro's `admin/css/analytics.css` is enqueued on **every** BetterDocs
16 * admin screen, and it re-declares the shared shell rules — `.bda-card`,
17 * `.bda-card-head`, `.bda-btn.primary`, `.bda-badge`, `.bda-table` and a dozen
18 * more — in terms of `--bda-*` variables it only defines inside its own
19 * `.bda-app` wrapper. On any other page those variables resolve to nothing, the
20 * whole declaration becomes invalid, and the element falls back to its initial
21 * value: cards lose their background *and* their border, the primary button
22 * loses its green and reads as disabled. That is what the MCP screen was showing
23 * (measured: `.bda-card` computed `background: rgba(0,0,0,0)`, `border-width: 0`;
24 * 19 unscoped Pro rules affected).
25 *
26 * Pro's own `.bda-app` block maps each alias onto a BetterDocs design-system
27 * token, so declaring the same mapping on this page's wrapper restores every one
28 * of those rules to the value it was written for — and, because the right-hand
29 * side is the shared token, dark mode still follows for free. Free-side and
30 * local on purpose: it must not depend on a Pro release, and with Pro inactive
31 * the aliases are simply unused.
32 */
33 /* `.bd-mcp-header-host` is the node the MCP header row is portalled into — it
34 lives in the admin shell's header, outside `.bd-mcp-wrapper`, and the status
35 badge inside it is a `bda-badge`, so it needs the same aliases or Pro's
36 admin-wide stylesheet strips the badge's background. */
37 .bd-mcp-wrapper,
38 .bd-mcp-header-host {
39 --bda-green: var(--base-color-700);
40 --bda-green-d: var(--base-color-700);
41 --bda-green-soft: var(--base-color-75);
42 --bda-green-border: var(--base-color-100);
43 --bda-bg: var(--text-color-50);
44 --bda-card-bg: var(--base-background);
45 --bda-border: var(--text-color-200);
46 --bda-border-2: var(--text-color-100);
47 --bda-border-strong: var(--text-color-300);
48 --bda-hover: var(--base-color-65);
49 --bda-thead: var(--text-color-50);
50 --bda-ink: var(--text-color-900);
51 --bda-s900: var(--text-color-900);
52 --bda-s800: var(--text-color-800);
53 --bda-s700: var(--text-color-700);
54 --bda-s600: var(--text-color-850);
55 --bda-s500: var(--text-color-600);
56 --bda-s400: var(--text-color-500);
57 --bda-amber: #f59e0b;
58 --bda-red: #ef4444;
59 --bda-blue: #3b82f6;
60 }
61
62 // Toast notifications (the master switch, rotate, revoke) — use the BetterDocs
63 // brand colors instead of react-toastify's default green/red. The library's
64 // `--toastify-color-success` (#07bc0c) is a brighter, yellower green than the
65 // CTA green this page uses everywhere else, so a success toast landed beside a
66 // green button that did not match it.
67 //
68 // Scoped to this screen, matching `.betterdocs-faq-builder-wrapper`
69 // (`admin/scss/faq-builder/bd-faq.scss`) and `.betterdocs-settings`
70 // (`admin/scss/admin.scss`), which theme their toasts exactly this way — and
71 // which is why this page, a third wrapper, never inherited it. The library
72 // default is deliberately left alone: every screen that has not opted in still
73 // gets it.
74 //
75 // A block of its own rather than a member of the alias selector list above:
76 // that list also carries `.bd-mcp-header-host`, the portalled header node, and
77 // no toast is ever rendered inside it.
78 .bd-mcp-wrapper {
79 .Toastify__toast--success {
80 background: var(--base-color-700) !important;
81 }
82
83 .Toastify__toast--error {
84 background: var(--danger-600) !important;
85 }
86 }
87
88 .bd-mcp {
89 display: flex;
90 flex-direction: column;
91 gap: 20px;
92 // No cap of its own: the header row is portalled into the shell's
93 // `.betterdocs-dashboard-container` (1170px), and a page whose cards stop
94 // 90px short of their own title reads as broken. The 1080px cap that used
95 // to sit here existed to keep a third column out of the card grid; the grid
96 // states its two columns outright now, so the cap has nothing left to do
97 // (ADR-062).
98
99 // -- Header row ---------------------------------------------------------
100 //
101 // Portalled into the admin shell's own header container, so it inherits the
102 // shell's `.betterdocs-row` grid and its 24px title size — the same heading
103 // every other BetterDocs screen wears — and only needs what the shell has no
104 // rule for: the mark, the badge's alignment and the switch beside the
105 // dark-mode button (ADR-062).
106 &-header {
107 &__left {
108 display: flex;
109 align-items: center;
110 gap: 10px;
111 }
112
113 &__tile {
114 flex: 0 0 auto;
115 width: 36px;
116 height: 36px;
117 border-radius: 8px;
118 display: inline-flex;
119 align-items: center;
120 justify-content: center;
121 color: var(--base-color-700);
122 background: var(--base-color-75);
123 }
124
125 &__title {
126 margin: 0;
127 }
128
129 &__right {
130 display: flex;
131 align-items: center;
132 gap: 16px;
133 }
134 }
135
136 // -- Intro strip --------------------------------------------------------
137 //
138 // What the hero card used to be. The description is one muted line with the
139 // capability disclosure beside it; opening the disclosure lets the line wrap
140 // in full and reveals the list. A dashboard's first job is to show numbers,
141 // and five lines of prose above the KPI row pushed them off the fold
142 // (ADR-062).
143 &-intro {
144 padding: 10px 16px;
145
146 &__row {
147 display: flex;
148 align-items: baseline;
149 gap: 16px;
150 }
151
152 &__desc {
153 margin: 0;
154 flex: 1 1 auto;
155 min-width: 0;
156 font-size: 13px;
157 line-height: 1.6;
158 color: var(--text-color-600);
159 white-space: nowrap;
160 overflow: hidden;
161 text-overflow: ellipsis;
162 }
163
164 &.is-open &__desc {
165 white-space: normal;
166 }
167
168 &__more {
169 flex: 0 0 auto;
170 display: inline-flex;
171 align-items: center;
172 gap: 4px;
173 border: 0;
174 background: none;
175 padding: 0;
176 cursor: pointer;
177 font-size: 12.5px;
178 font-weight: 500;
179 color: var(--base-color-700);
180
181 &:hover {
182 color: var(--base-color-800);
183 }
184
185 &:focus-visible {
186 outline: 2px solid var(--base-color-700);
187 outline-offset: 3px;
188 border-radius: 4px;
189 }
190 }
191 }
192
193 // -- KPI tiles ----------------------------------------------------------
194 //
195 // The geometry is the shared `bda-kpi*` shell; only the two things it has
196 // no rule for are here. There is no sparkline and no delta chip on this row
197 // on purpose: the server keeps one `last_used` stamp per credential and no
198 // history, so a trend could only be invented (ADR-062).
199 &-kpi {
200 &__ico {
201 color: var(--bd-accent, var(--base-color-700));
202 background: var(--bd-accent-tint, var(--base-color-75));
203 }
204
205 // Two of the four tiles hold words, not a number. At 28px "Setup
206 // needed" wraps to two lines and pushes the row taller than its
207 // neighbours.
208 &__value--sm {
209 font-size: 20px;
210 }
211
212 &__note {
213 font-size: 11.5px;
214 color: var(--text-color-500);
215 white-space: nowrap;
216 }
217 }
218
219 // -- Toggle -------------------------------------------------------------
220 &-switch {
221 display: inline-flex;
222 align-items: center;
223 gap: 10px;
224 cursor: pointer;
225 user-select: none;
226
227 input {
228 // Kept in the accessibility tree and focusable — the visible track
229 // is styled from its :focus-visible state below.
230 position: absolute;
231 width: 1px;
232 height: 1px;
233 margin: -1px;
234 padding: 0;
235 overflow: hidden;
236 clip: rect(0 0 0 0);
237 white-space: nowrap;
238 border: 0;
239 }
240
241 &__track {
242 position: relative;
243 width: 42px;
244 height: 24px;
245 border-radius: 999px;
246 background: var(--text-color-300);
247 transition: background 0.15s;
248 flex: 0 0 auto;
249 }
250
251 &__thumb {
252 position: absolute;
253 top: 3px;
254 left: 3px;
255 width: 18px;
256 height: 18px;
257 border-radius: 50%;
258 background: #fff;
259 box-shadow: 0 1px 2px rgba(16, 24, 40, 0.2);
260 transition: transform 0.15s;
261 }
262
263 &__label {
264 font-size: 13px;
265 font-weight: 600;
266 color: var(--text-color-800);
267 }
268
269 input:checked + &__track {
270 background: var(--base-color-700);
271 }
272
273 input:checked + &__track &__thumb,
274 input:checked + .bd-mcp-switch__track .bd-mcp-switch__thumb {
275 transform: translateX(18px);
276 }
277
278 input:disabled + &__track {
279 opacity: 0.6;
280 }
281
282 // Standard focus state (design-system Foundations rule 5).
283 input:focus-visible + &__track {
284 box-shadow:
285 0 0 0 4px var(--focus-shadow-1),
286 0 1px 2px 0 var(--focus-shadow-2);
287 }
288 }
289
290 // -- Capability list ----------------------------------------------------
291 //
292 // What the disclosure in the intro strip reveals. Two columns, because the
293 // strip is the full width of the page and five short items down one side of
294 // it would leave the other side empty.
295 &-caps {
296 list-style: none;
297 margin: 12px 0 4px;
298 padding: 10px 0 0;
299 border-top: 1px solid var(--text-color-150);
300 display: grid;
301 grid-template-columns: 1fr 1fr;
302 gap: 8px 24px;
303
304 &__note {
305 grid-column: 1 / -1;
306 margin: 0 0 2px;
307 font-size: 12.5px;
308 line-height: 1.6;
309 color: var(--text-color-600);
310 }
311
312 &__item {
313 display: flex;
314 align-items: flex-start;
315 gap: 9px;
316 font-size: 13px;
317 color: var(--text-color-700);
318 }
319
320 &__check {
321 color: var(--base-color-700);
322 display: inline-flex;
323 margin-top: 3px;
324 flex: 0 0 auto;
325 }
326 }
327
328 // -- Shared bits --------------------------------------------------------
329 &-muted {
330 margin: 0;
331 font-size: 13px;
332 line-height: 1.6;
333 color: var(--text-color-600);
334 max-width: 78ch;
335 }
336
337 &-cardhead {
338 display: flex;
339 align-items: flex-start;
340 justify-content: space-between;
341 gap: 16px;
342 flex-wrap: wrap;
343 }
344
345 &-actions {
346 display: flex;
347 align-items: center;
348 gap: 10px;
349 flex-wrap: wrap;
350 justify-content: flex-end;
351 }
352
353 &-danger {
354 color: var(--danger-600);
355
356 &:hover {
357 border-color: var(--danger-600);
358 }
359 }
360
361 &-alert {
362 border-radius: 10px;
363 padding: 12px 14px;
364 font-size: 13px;
365 line-height: 1.6;
366 border: 1px solid var(--text-color-200);
367 background: var(--text-color-50);
368 color: var(--text-color-800);
369 margin-bottom: 14px;
370
371 &.is-success {
372 border-color: var(--base-color-700);
373 background: var(--base-color-75);
374 color: var(--text-color-900);
375 }
376
377 &.is-warning {
378 border-color: var(--warning-400);
379 background: var(--warning-50);
380 color: var(--warning-700);
381 }
382
383 &.is-error {
384 border-color: var(--danger-600);
385 background: var(--danger-50);
386 color: var(--danger-700);
387 }
388 }
389
390 // -- Copy fields --------------------------------------------------------
391 &-field {
392 display: block;
393 margin-bottom: 14px;
394
395 &__label {
396 display: block;
397 font-size: 12px;
398 font-weight: 600;
399 text-transform: uppercase;
400 letter-spacing: 0.03em;
401 color: var(--text-color-600);
402 margin-bottom: 6px;
403 }
404
405 &__control {
406 display: flex;
407 align-items: center;
408 gap: 10px;
409 border: 1px solid var(--text-color-200);
410 border-radius: 8px;
411 background: var(--base-background);
412 padding: 7px 8px 7px 12px;
413 box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
414
415 &.is-block {
416 display: block;
417 padding: 12px;
418 }
419 }
420
421 &__value {
422 flex: 1 1 auto;
423 min-width: 0;
424 font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
425 font-size: 12.5px;
426 color: var(--text-color-900);
427 background: none;
428
429 // wp-admin's own `code` rule (`wp-admin/css/common.css`, WP 7.1
430 // L419) hands this element `padding: 3px 5px 2px` and
431 // `margin: 0 1px`, and the element is `box-sizing: border-box`.
432 // Those 12px come out of the width the flex algorithm just handed
433 // it, so the value is laid out in a box narrower than the space it
434 // was given. Measured at 1400px on a 52-character connector URL
435 // (`https://numberlessview.s6-tastewp.com/betterdocs/mcp`): the box
436 // is 401.31px, the text 391.34px, and the content box 391.31px —
437 // three hundredths of a pixel short. The URL therefore broke after
438 // `numberlessview.s6-` into two 195.67px lines, leaving 207.6px of
439 // the field empty between the first line and the Copy button
440 // `background` was already being
441 // neutralised here for the same reason; these two belong with it.
442 padding: 0;
443 margin: 0;
444
445 // `anywhere` cuts mid-token as soon as any constraint is hit, and
446 // it also drops the box's min-content contribution to a single
447 // character. `break-word` keeps the URL's own break opportunities
448 // (after `/`, after `-`) and only cuts a word up when that one word
449 // cannot fit on a line of its own — which is still what the
450 // 64-character connection token needs, so it wraps exactly as
451 // before.
452 overflow-wrap: break-word;
453 }
454
455 &__pre {
456 margin: 0 0 10px;
457 font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
458 font-size: 12.5px;
459 line-height: 1.6;
460 color: var(--text-color-900);
461 white-space: pre-wrap;
462 overflow-wrap: anywhere;
463 max-height: 260px;
464 overflow-y: auto;
465 }
466
467 &__actions {
468 display: flex;
469 align-items: center;
470 gap: 6px;
471 flex: 0 0 auto;
472 }
473
474 &__help {
475 display: block;
476 font-size: 12px;
477 color: var(--text-color-600);
478 margin-top: 6px;
479 max-width: 78ch;
480 }
481 }
482
483 // -- Tabs ---------------------------------------------------------------
484 //
485 // The two tabs are now the shared analytics segmented control (`bda-segtabs`
486 // / `bda-segtab`), sitting in the connect card's head — the underline strip
487 // this class used to draw is gone with the standalone section head (ADR-062).
488 // Only the focus ring is ours: `bda-segtab` has none.
489 &-tabs {
490 flex-shrink: 0;
491
492 .bda-segtab:focus-visible {
493 outline: 2px solid var(--base-color-700);
494 outline-offset: -2px;
495 }
496 }
497
498 &-tabpanel {
499 min-height: 120px;
500 }
501
502 // -- Steps --------------------------------------------------------------
503 &-steps {
504 list-style: none;
505 counter-reset: none;
506 margin: 14px 0 0;
507 padding: 0;
508 display: grid;
509 gap: 10px;
510
511 &__item {
512 display: flex;
513 align-items: flex-start;
514 gap: 10px;
515 font-size: 13.5px;
516 color: var(--text-color-700);
517 }
518
519 &__num {
520 flex: 0 0 auto;
521 width: 20px;
522 height: 20px;
523 border-radius: 6px;
524 display: inline-flex;
525 align-items: center;
526 justify-content: center;
527 font-size: 11.5px;
528 font-weight: 700;
529 background: var(--base-color-75);
530 color: var(--base-color-700);
531 }
532 }
533
534 // -- Connect section ----------------------------------------------------
535 //
536 // An analytics card (ADR-062): the title, its "how it works" line and the
537 // two tabs live in `bda-card-head`, the panel in `bda-card-body`, and the
538 // rotate row is the card's footer. Nothing of its own is left to declare.
539
540 &-link {
541 display: inline-flex;
542 align-items: center;
543 gap: 4px;
544 color: var(--base-color-700);
545 text-decoration: none;
546 font-weight: 500;
547
548 &:hover,
549 &:focus {
550 color: var(--base-color-800);
551 text-decoration: underline;
552 }
553 }
554
555 // -- Client cards -------------------------------------------------------
556 //
557 // Two columns on a wide screen, one below 782px (WordPress' own admin
558 // breakpoint). Stated rather than left to `auto-fit`/`minmax`: this grid
559 // holds exactly two cards since the five editor clients moved into the
560 // picker (ADR-057), so the arithmetic that once had to keep a third column
561 // out (ADR-054) is arithmetic about cards that are no longer here.
562 &-grid {
563 display: grid;
564 grid-template-columns: 1fr 1fr;
565 gap: 16px;
566 }
567
568 &-card {
569 display: flex;
570 flex-direction: column;
571 gap: 14px;
572 padding: 16px;
573
574 // The card owns its spacing; `CopyField`'s own bottom margin would
575 // double up against the flex gap.
576 .bd-mcp-field {
577 margin-bottom: 0;
578 }
579
580 &__head {
581 display: flex;
582 align-items: center;
583 gap: 12px;
584 }
585
586 // `--bd-accent` / `--bd-accent-tint` are set inline per card, so each
587 // client wears its own mark's colour without a rule per client.
588 &__icon {
589 flex: 0 0 auto;
590 width: 36px;
591 height: 36px;
592 border-radius: 8px;
593 display: inline-flex;
594 align-items: center;
595 justify-content: center;
596 color: var(--bd-accent, var(--base-color-700));
597 background: var(--bd-accent-tint, var(--base-color-75));
598
599 // Every client tile is white with a hairline border and the glyph
600 // in its own colour: the vendor marks carry their own fill from the
601 // file they were supplied as, ours take the accent. One treatment
602 // for the whole row (ADR-066, replacing ADR-065's split).
603 &.is-solid {
604 background: var(--base-background);
605 border: 1px solid var(--text-color-200);
606 }
607 }
608
609 &__headtext {
610 min-width: 0;
611 flex: 1 1 auto;
612 }
613
614 &__title {
615 display: flex;
616 align-items: center;
617 gap: 8px;
618 flex-wrap: wrap;
619 font-size: 14px;
620 font-weight: 600;
621 color: var(--text-color-900);
622 }
623
624 &__sub {
625 display: block;
626 margin-top: 3px;
627 font-size: 12.5px;
628 line-height: 1.5;
629 color: var(--text-color-600);
630 }
631
632 &__cta {
633 align-self: flex-start;
634 }
635 }
636
637 // -- The accent tile ----------------------------------------------------
638 //
639 // One rule for every client mark. `--bd-accent` / `--bd-accent-tint` are set
640 // inline per client, so a brand colour never needs a rule of its own, and
641 // dark mode can override the pair in one place (ADR-054).
642 &-tile {
643 flex: 0 0 auto;
644 display: inline-flex;
645 align-items: center;
646 justify-content: center;
647 color: var(--bd-accent, var(--base-color-700));
648 background: var(--bd-accent-tint, var(--base-color-75));
649
650 // Same pair as `.bd-mcp-card__icon` above, and for the same reason.
651 // Every client tile is white with a hairline border and the glyph in
652 // its own colour — the vendor marks carry their own fill from the
653 // supplied file, ours take the accent. One treatment for the row, so a
654 // mark we drew never looks more endorsed than a vendor's (ADR-066).
655 &.is-solid {
656 background: var(--base-background);
657 border: 1px solid var(--text-color-200);
658 }
659
660 &--36 {
661 width: 36px;
662 height: 36px;
663 border-radius: 9px;
664 }
665
666 &--28 {
667 width: 28px;
668 height: 28px;
669 border-radius: 7px;
670 }
671 }
672
673 // -- Editors & CLI: rail + detail panel ---------------------------------
674 //
675 // Five clients whose natural heights are nothing alike — a deep-link button
676 // beside a twelve-line prompt — so they are picked one at a time instead of
677 // tiled (ADR-057). The OAuth tab keeps the grid; its two cards match.
678 &-split {
679 display: flex;
680 flex-wrap: wrap;
681 margin-top: 4px;
682 }
683
684 &-rail {
685 flex: 0 0 250px;
686 border-right: 1px solid var(--text-color-150);
687 padding: 6px 10px 12px 0;
688
689 &__label {
690 display: block;
691 padding: 8px 14px 6px;
692 font-size: 11px;
693 font-weight: 600;
694 text-transform: uppercase;
695 letter-spacing: 0.04em;
696 color: var(--text-color-600);
697 }
698
699 &__item {
700 position: relative;
701 display: flex;
702 align-items: center;
703 gap: 10px;
704 width: 100%;
705 padding: 7px 12px 7px 14px;
706 border: 0;
707 border-radius: 0 8px 8px 0;
708 background: none;
709 color: inherit;
710 font: inherit;
711 text-align: left;
712 cursor: pointer;
713
714 &:hover:not(.is-selected) {
715 background: var(--text-color-50);
716 }
717
718 &.is-selected {
719 background: var(--base-color-75);
720 }
721
722 // The 3px edge, drawn rather than bordered so the item's own
723 // padding does not shift by 3px when it becomes selected.
724 &.is-selected::before {
725 content: "";
726 position: absolute;
727 left: 0;
728 top: 6px;
729 bottom: 6px;
730 width: 3px;
731 border-radius: 0 2px 2px 0;
732 background: var(--base-color-700);
733 }
734
735 &:focus-visible {
736 outline: 2px solid var(--base-color-700);
737 outline-offset: -2px;
738 }
739 }
740
741 &__name {
742 flex: 1 1 auto;
743 min-width: 0;
744 font-size: 13px;
745 font-weight: 500;
746 line-height: 1.3;
747 color: var(--text-color-900);
748 }
749 }
750
751 &-detail {
752 flex: 1 1 560px;
753 min-width: 0;
754 padding: 18px 4px 4px 22px;
755
756 &:focus {
757 outline: none;
758 }
759
760 &__head {
761 display: flex;
762 align-items: flex-start;
763 gap: 12px;
764 }
765
766 &__headtext {
767 min-width: 0;
768 flex: 1 1 auto;
769 }
770
771 &__name {
772 display: flex;
773 align-items: center;
774 gap: 8px;
775 flex-wrap: wrap;
776 font-size: 16px;
777 font-weight: 600;
778 line-height: 1.3;
779 color: var(--text-color-900);
780 }
781
782 &__desc {
783 display: block;
784 margin-top: 2px;
785 font-size: 13px;
786 line-height: 1.5;
787 color: var(--text-color-600);
788 }
789
790 &__label {
791 display: block;
792 margin: 18px 0 8px;
793 font-size: 11px;
794 font-weight: 600;
795 text-transform: uppercase;
796 letter-spacing: 0.04em;
797 color: var(--text-color-600);
798 }
799
800 // `CopyField` carries its own label and bottom margin; the panel owns
801 // the rhythm, so the field only supplies the box.
802 &__pay {
803 margin-top: 18px;
804
805 .bd-mcp-field {
806 margin-bottom: 0;
807 }
808 }
809
810 &__cta {
811 align-self: flex-start;
812 }
813
814 &__hint {
815 margin: 8px 0 0;
816 font-size: 13px;
817 line-height: 1.5;
818 color: var(--text-color-600);
819 }
820 }
821
822 // -- Browser / token segmented control ----------------------------------
823 &-seg {
824 display: inline-flex;
825 margin-top: 16px;
826 padding: 3px;
827 border-radius: 8px;
828 background: var(--text-color-50);
829
830 &__btn {
831 border: 0;
832 border-radius: 6px;
833 padding: 6px 12px;
834 background: none;
835 color: var(--text-color-700);
836 font: inherit;
837 font-size: 13px;
838 line-height: 1.4;
839 cursor: pointer;
840 white-space: nowrap;
841
842 small {
843 margin-left: 6px;
844 font-size: 12px;
845 font-weight: 400;
846 color: var(--text-color-600);
847 }
848
849 &:hover:not(.is-active) {
850 color: var(--text-color-900);
851 }
852
853 &.is-active {
854 background: var(--base-background);
855 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
856 color: var(--text-color-900);
857 font-weight: 500;
858 }
859
860 &:focus-visible {
861 outline: 2px solid var(--base-color-700);
862 outline-offset: 1px;
863 }
864 }
865 }
866
867 // The one line that says what the token mode costs. Informational, beside
868 // the payload rather than over it: choosing the token is supported.
869 &-warn {
870 display: flex;
871 align-items: center;
872 gap: 6px;
873 margin: 10px 0 0;
874 font-size: 13px;
875 line-height: 1.5;
876 color: var(--text-color-600);
877
878 svg {
879 flex: 0 0 auto;
880 }
881 }
882
883 // -- Rotate row (the connect card's footer) -----------------------------
884 //
885 // A footer row on the card rather than a block inside one tab's panel: it
886 // is a property of the connection, not of the tab that happens to be open,
887 // and its own last clause is addressed to the OAuth reader (ADR-062).
888 &-reset {
889 display: flex;
890 align-items: center;
891 justify-content: space-between;
892 gap: 16px;
893 flex-wrap: wrap;
894 padding: 14px 20px;
895 border-top: 1px solid var(--text-color-100);
896
897 &__text {
898 margin: 0;
899 flex: 1 1 420px;
900 max-width: 78ch;
901 font-size: 12.5px;
902 line-height: 1.6;
903 color: var(--text-color-600);
904 }
905 }
906
907 &-checkline {
908 display: flex;
909 align-items: flex-start;
910 gap: 10px;
911 text-align: left;
912 margin: 16px 0 18px;
913 font-size: 13.5px;
914 color: var(--text-color-800);
915 cursor: pointer;
916
917 input {
918 margin-top: 3px;
919 flex: 0 0 auto;
920 }
921
922 label {
923 cursor: pointer;
924 }
925
926 em {
927 display: block;
928 font-style: normal;
929 font-size: 12.5px;
930 color: var(--text-color-600);
931 margin-top: 2px;
932 max-width: 60ch;
933 }
934 }
935
936 // -- Empty state --------------------------------------------------------
937 &-empty {
938 text-align: center;
939 padding: 26px 20px;
940
941 &__icon {
942 display: inline-flex;
943 align-items: center;
944 justify-content: center;
945 width: 46px;
946 height: 46px;
947 border-radius: 12px;
948 background: var(--text-color-100);
949 color: var(--text-color-500);
950 margin-bottom: 12px;
951 }
952
953 &__title {
954 margin: 0 0 6px;
955 font-size: 15px;
956 font-weight: 600;
957 color: var(--text-color-900);
958 }
959
960 &__text {
961 margin: 0 auto;
962 font-size: 13px;
963 color: var(--text-color-600);
964 max-width: 54ch;
965 }
966 }
967
968 // -- Connected apps table ----------------------------------------------
969 &-tablewrap {
970 overflow-x: auto;
971 }
972
973 &-table {
974 width: 100%;
975 min-width: 780px;
976 }
977
978 // The `Actions` column's header, dragged left by a three-rule cascade:
979 //
980 // .bda-t-right right (0,1,0) the helper
981 // .bda-table thead th left (0,1,2) outranks it
982 // .bda-app .bda-t-right center (0,2,0) Pro, inside Analytics only
983 //
984 // So a `<th class="bda-t-right">` came out **left** while its `<td>` came out
985 // right, and the label could never line up with the Revoke buttons under it.
986 //
987 // Scoped to this page rather than fixed on the helper, because the helper is
988 // not broken — this page is the thing using it somewhere its author did not
989 // plan for. Pro's `analytics/scss/analytics.scss` says so in as many words:
990 // *"`.bda-t-right` is used only inside `.bda-app` … so this scoped override
991 // is safe"*, and inside `.bda-app` it centres header **and** values together
992 // on purpose. An unscoped `.bda-table thead th.bda-t-right` (0,2,2) outranks
993 // that and leaves Analytics' headers right over centred cells — built,
994 // measured at 29.1px and 25.1px of drift on the Overview teaser, and
995 // reverted. Do not "simplify" this back onto the shared helper.
996 .bda-table thead th.bda-t-right {
997 text-align: right;
998 }
999
1000 &-host {
1001 font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1002 font-size: 12px;
1003 color: var(--text-color-700);
1004 }
1005
1006 // -- Health -------------------------------------------------------------
1007 &-health {
1008 &__list {
1009 display: grid;
1010 border: 1px solid var(--text-color-150);
1011 border-radius: 10px;
1012 overflow: hidden;
1013 }
1014
1015 &__row {
1016 display: flex;
1017 align-items: flex-start;
1018 justify-content: space-between;
1019 gap: 16px;
1020 padding: 13px 16px;
1021
1022 + .bd-mcp-health__row {
1023 border-top: 1px solid var(--text-color-150);
1024 }
1025 }
1026
1027 &__text {
1028 min-width: 0;
1029 }
1030
1031 &__label {
1032 display: block;
1033 font-size: 13.5px;
1034 font-weight: 600;
1035 color: var(--text-color-900);
1036 }
1037
1038 &__detail {
1039 display: block;
1040 font-size: 12.5px;
1041 color: var(--text-color-600);
1042 margin-top: 2px;
1043 max-width: 78ch;
1044 }
1045 }
1046
1047 &-checks {
1048 list-style: none;
1049 margin: 0 0 16px;
1050 padding: 0;
1051 display: grid;
1052 gap: 8px;
1053
1054 &__item {
1055 display: flex;
1056 align-items: flex-start;
1057 gap: 9px;
1058 font-size: 12.5px;
1059 line-height: 1.6;
1060 color: var(--text-color-700);
1061 }
1062
1063 &__ok {
1064 color: var(--base-color-700);
1065 font-weight: 700;
1066 }
1067
1068 &__warn {
1069 color: var(--warning-500);
1070 font-weight: 700;
1071 }
1072 }
1073 }
1074
1075 @media (max-width: 900px) {
1076 // Title + badge + label + switch + the dark-mode button is more than one
1077 // phone-width line: the header row's two halves wrap onto their own lines
1078 // and the right one aligns left under the title, rather than pushing the
1079 // page wider than the viewport (the failure U12b measured at 390px).
1080 .bd-mcp-header {
1081 &__left,
1082 &__right {
1083 flex-wrap: wrap;
1084 }
1085
1086 &__right {
1087 justify-content: flex-start;
1088 gap: 12px;
1089 }
1090 }
1091
1092 // The description is a single ellipsised line on a wide screen; on a narrow
1093 // one there is no room for both it and the disclosure beside it, so the
1094 // strip stacks and the line wraps.
1095 .bd-mcp-intro__row {
1096 flex-wrap: wrap;
1097 gap: 8px;
1098 }
1099
1100 .bd-mcp-intro__desc {
1101 white-space: normal;
1102 flex-basis: 100%;
1103 }
1104
1105 .bd-mcp-caps {
1106 grid-template-columns: 1fr;
1107 }
1108
1109 .bd-mcp-actions {
1110 justify-content: flex-start;
1111 }
1112 }
1113
1114 // WordPress' own admin breakpoint. `auto-fit` already folds the grid when the
1115 // cards no longer fit, but a phone-width viewport must never leave two 320px
1116 // columns fighting for 390px — so the single column is stated, not left to the
1117 // minmax arithmetic.
1118 @media (max-width: 782px) {
1119 .bd-mcp-grid {
1120 grid-template-columns: 1fr;
1121 }
1122
1123 // The rail turns into a horizontal, scrollable row of chips above the
1124 // panel: five items down the side of a phone would push the payload —
1125 // the thing anyone came for — below the fold.
1126 .bd-mcp-rail {
1127 flex: 1 1 100%;
1128 display: flex;
1129 gap: 8px;
1130 overflow-x: auto;
1131 border-right: 0;
1132 border-bottom: 1px solid var(--text-color-150);
1133 padding: 0 0 12px;
1134 // The chips scroll inside this container; the page must not.
1135 -webkit-overflow-scrolling: touch;
1136
1137 &__label {
1138 display: none;
1139 }
1140
1141 &__item {
1142 flex: 0 0 auto;
1143 width: auto;
1144 gap: 8px;
1145 padding: 6px 12px 6px 10px;
1146 border: 1px solid var(--text-color-200);
1147 border-radius: 999px;
1148
1149 &.is-selected {
1150 border-color: var(--base-color-100);
1151 }
1152
1153 // The selected edge is a vertical bar against a vertical list; on a
1154 // chip the tint alone reads as selected.
1155 &.is-selected::before {
1156 content: none;
1157 }
1158 }
1159
1160 &__name {
1161 flex: 0 0 auto;
1162 white-space: nowrap;
1163 }
1164
1165 // The badge repeats what the payload below already says, and it is what
1166 // makes a chip too wide to scan.
1167 .bda-badge {
1168 display: none;
1169 }
1170 }
1171
1172 .bd-mcp-detail {
1173 flex: 1 1 100%;
1174 padding: 16px 0 4px;
1175 }
1176
1177 .bd-mcp-reset {
1178 align-items: flex-start;
1179
1180 .bd-mcp-actions {
1181 justify-content: flex-start;
1182 }
1183 }
1184 }
1185
1186 @media (max-width: 600px) {
1187 // Two labels of this length cannot sit side by side on a phone without
1188 // either wrapping mid-word or scrolling the page.
1189 .bd-mcp-seg {
1190 display: flex;
1191 width: 100%;
1192
1193 &__btn {
1194 flex: 1 1 50%;
1195 padding: 8px 10px;
1196 text-align: center;
1197 white-space: normal;
1198
1199 small {
1200 display: block;
1201 margin-left: 0;
1202 }
1203 }
1204 }
1205 }
1206
1207 /**
1208 * Dark mode — the one thing the tokens cannot do for us.
1209 *
1210 * A card's accent is a brand colour set inline (ChatGPT's black, Cursor's
1211 * near-black), and a tint computed as `#rrggbb14` is an 8% wash of that colour
1212 * over whatever is behind it. On a dark surface the near-black marks disappear
1213 * and the tints turn to mud, so both are replaced here: the mark takes the
1214 * card's own `--bd-accent-dark` and the square takes a neutral wash.
1215 */
1216 .betterdocs-admin.betterdocs-dark-mode {
1217 .bd-mcp-card__icon,
1218 .bd-mcp-tile {
1219 color: var(--bd-accent-dark, var(--base-color-700));
1220 background: rgba(255, 255, 255, 0.07);
1221 }
1222
1223 // The vendor marks carry their own colours, so their tile stays a light
1224 // chip in dark mode too — a terracotta logo on a near-black square is
1225 // unreadable, and we no longer recolour anyone's logo (ADR-066).
1226 .bd-mcp-card__icon.is-solid,
1227 .bd-mcp-tile.is-solid {
1228 background: #fff;
1229 border-color: rgba(255, 255, 255, 0.16);
1230 }
1231
1232 // `--text-color-50` is a near-black surface in dark mode, so an unselected
1233 // rail item hovering to it, and the segmented track, both need the lighter
1234 // wash the cards use rather than the token's own value.
1235 .bd-mcp-rail__item:hover:not(.is-selected) {
1236 background: rgba(255, 255, 255, 0.05);
1237 }
1238
1239 .bd-mcp-seg {
1240 background: rgba(255, 255, 255, 0.06);
1241 }
1242
1243 .bd-mcp-seg__btn.is-active {
1244 background: var(--text-color-100);
1245 box-shadow: none;
1246 }
1247 }
1248