@blaaiz/docs-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -0
  3. package/dist/chunk-3ZX4WIE3.js +2984 -0
  4. package/dist/chunk-3ZX4WIE3.js.map +1 -0
  5. package/dist/chunk-JCYR6RPE.js +31 -0
  6. package/dist/chunk-JCYR6RPE.js.map +1 -0
  7. package/dist/chunk-ZKOOKLZ3.js +124 -0
  8. package/dist/chunk-ZKOOKLZ3.js.map +1 -0
  9. package/dist/cli.js +534 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/generator.cjs +508 -0
  12. package/dist/generator.cjs.map +1 -0
  13. package/dist/generator.d.cts +122 -0
  14. package/dist/generator.d.ts +122 -0
  15. package/dist/generator.js +177 -0
  16. package/dist/generator.js.map +1 -0
  17. package/dist/index.cjs +3024 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +1182 -0
  20. package/dist/index.d.ts +1182 -0
  21. package/dist/index.js +3 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/navigation-CGqFIPlP.d.cts +498 -0
  24. package/dist/navigation-CGqFIPlP.d.ts +498 -0
  25. package/dist/openapi-types-CJ6p5Cux.d.cts +78 -0
  26. package/dist/openapi-types-CJ6p5Cux.d.ts +78 -0
  27. package/dist/ui/api-try-it.cjs +654 -0
  28. package/dist/ui/api-try-it.cjs.map +1 -0
  29. package/dist/ui/api-try-it.d.cts +78 -0
  30. package/dist/ui/api-try-it.d.ts +78 -0
  31. package/dist/ui/api-try-it.js +509 -0
  32. package/dist/ui/api-try-it.js.map +1 -0
  33. package/dist/ui/ask-ai.cjs +810 -0
  34. package/dist/ui/ask-ai.cjs.map +1 -0
  35. package/dist/ui/ask-ai.d.cts +57 -0
  36. package/dist/ui/ask-ai.d.ts +57 -0
  37. package/dist/ui/ask-ai.js +808 -0
  38. package/dist/ui/ask-ai.js.map +1 -0
  39. package/dist/ui/copy-page.cjs +312 -0
  40. package/dist/ui/copy-page.cjs.map +1 -0
  41. package/dist/ui/copy-page.d.cts +33 -0
  42. package/dist/ui/copy-page.d.ts +33 -0
  43. package/dist/ui/copy-page.js +183 -0
  44. package/dist/ui/copy-page.js.map +1 -0
  45. package/dist/ui/mermaid.cjs +363 -0
  46. package/dist/ui/mermaid.cjs.map +1 -0
  47. package/dist/ui/mermaid.d.cts +13 -0
  48. package/dist/ui/mermaid.d.ts +13 -0
  49. package/dist/ui/mermaid.js +361 -0
  50. package/dist/ui/mermaid.js.map +1 -0
  51. package/dist/ui.cjs +661 -0
  52. package/dist/ui.cjs.map +1 -0
  53. package/dist/ui.d.cts +428 -0
  54. package/dist/ui.d.ts +428 -0
  55. package/dist/ui.js +537 -0
  56. package/dist/ui.js.map +1 -0
  57. package/package.json +145 -0
  58. package/patches/fumadocs-openapi.patch +173 -0
  59. package/skills/AGENTS-section.md +36 -0
  60. package/skills/SKILL.md +363 -0
  61. package/styles/api-reference.css +1417 -0
  62. package/styles/ask-ai.css +563 -0
  63. package/styles/auth.css +462 -0
  64. package/styles/docs.css +247 -0
  65. package/styles/home.css +376 -0
  66. package/templates/init/content/docs/index.mdx.tmpl +52 -0
  67. package/templates/init/content/docs/meta.json.tmpl +3 -0
  68. package/templates/init/content/docs.json.tmpl +12 -0
  69. package/templates/init/content/nav.json.tmpl +7 -0
  70. package/templates/init/docs.config.ts.tmpl +36 -0
  71. package/templates/init/env.example.tmpl +15 -0
@@ -0,0 +1,1417 @@
1
+ /*
2
+ * @blaaiz/docs-core — the API-reference view (Mintlify-style).
3
+ *
4
+ * Import after docs.css:
5
+ * @import '@blaaiz/docs-core/styles/api-reference.css';
6
+ *
7
+ * Pairs with `apiOperationLayout` from @blaaiz/docs-core/ui and the
8
+ * fumadocs-openapi patch shipped in this package (patches/).
9
+ *
10
+ * ---------------------------------------------------------------------------
11
+ * UPGRADE CHECKLIST — the fumadocs class signatures this file selects on
12
+ * ---------------------------------------------------------------------------
13
+ *
14
+ * fumadocs and fumadocs-openapi render most of the reference page themselves,
15
+ * with Tailwind utility classes and no stable hooks. Where a rule must reach one
16
+ * of their elements, it matches a *signature*: the utility classes that element
17
+ * is the only one to carry. That works, and it is only as stable as their markup.
18
+ *
19
+ * Re-check every key below after a `fumadocs-ui` or `fumadocs-openapi` upgrade.
20
+ * A key that no longer matches loses its styling silently — nothing errors.
21
+ *
22
+ * | Key (`[class*='…']`) | Matches |
23
+ * | ----------------------------------- | ---------------------------------------------- |
24
+ * | `w-[400px` | The right-hand code/response rail column |
25
+ * | `1285` | A `full: true` page's width cap (also docs.css) |
26
+ * | `min-w-0` + `flex-1` | The operation's left (prose) column |
27
+ * | `prose-no-margin` | A prose block whose margins fumadocs zeroes |
28
+ * | `overflow-x-auto` | A code block's scroll container |
29
+ * | `rounded-xl` + `overflow-hidden` | The code panel shell in the rail |
30
+ * | `bg-fd-background` | The code panel's body inside that shell |
31
+ * | `rounded-lg` | The prev/next footer cards |
32
+ * | `border-y` + `bg-fd-secondary` | The playground's header/footer bars |
33
+ * | `group` + `w-fit` | The playground's auth ("Authorization") button |
34
+ * | `rounded-xl` (on `form`) | The playground form shell |
35
+ * | `rounded-xl` + `shadow-md` (form) | The playground dialog |
36
+ * | `font-mono` (on `textarea`) | The JSON body editor |
37
+ * | `rounded-md` (on `input`) | A parameter input |
38
+ * | `grid` + `gap-4` | The parameter grid |
39
+ * | `font-medium` (on `span`) | A field name row; with `text-red` its required |
40
+ * | | marker |
41
+ * | `bg-fd-secondary` + `text-xs` | A schema chip; with `border`, a schema toggle |
42
+ * | | button; with `shadow-md`, a constraint chip |
43
+ * | `mt-2` + `flex-row` | The "Value in" enum row; with `flex-wrap`, its |
44
+ * | | wrapping variant |
45
+ * | `flex-col` + `bg-fd-secondary` | One enum card (the `data-open` menu target) |
46
+ * | `bg-fd-popover` + `backdrop-blur` | The schema popover |
47
+ * | `py-4` + `border-t` | One field row inside a schema block; |
48
+ * | | `first:border-t-0` marks the first |
49
+ * | `border-b` + `last:border-b-0` | A response/schema section divider |
50
+ *
51
+ * The playground's own DOM hooks (`.api-tryit-*`, `.api-*`) are ours — they come
52
+ * from `@blaaiz/docs-core/ui`, so they change only when this package changes.
53
+ */
54
+
55
+ /* --- The code/response rail ------------------------------------------- */
56
+
57
+ /* API rail panels: no window dots; the code body sits on the page background
58
+ (white in light, near-black in dark) so request and response match. */
59
+ :is(#nd-page div[class*='w-[400px'], .api-tryit-example) figure.shiki {
60
+ padding-top: 0;
61
+ border-radius: 0;
62
+ box-shadow: none;
63
+ background: var(--color-fd-background);
64
+ }
65
+
66
+ :is(#nd-page div[class*='w-[400px'], .api-tryit-example) figure.shiki::before,
67
+ :is(#nd-page div[class*='w-[400px'], .api-tryit-example) figure.shiki::after {
68
+ content: none;
69
+ }
70
+
71
+ :is(#nd-page div[class*='w-[400px'], .api-tryit-example) figure.shiki > button {
72
+ top: 0.6rem;
73
+ right: 0.6rem;
74
+ transform: none;
75
+ }
76
+
77
+ /* Denser code in the rail: smaller type, tighter lines than the guide blocks. */
78
+ :is(#nd-page div[class*='w-[400px'], .api-tryit-example) figure.shiki pre {
79
+ font-size: 12.5px;
80
+ line-height: 1.7;
81
+ }
82
+
83
+ /* Request code: the same inset, rounded inner shape as the response's panel
84
+ (white, radius 12, 1px of the grey card framing it). */
85
+ :is(#nd-page div[class*='w-[400px'], .api-tryit-example) .openapi-usage figure.shiki {
86
+ margin: 0 1px 1px;
87
+ border-radius: 12px;
88
+ }
89
+
90
+ /* Response body panel: inset it the same 1px so its 12px corners read as a
91
+ rounded card framed by the box, matching the request code. It was flush to
92
+ the box edge, so the rounding had no frame to show against. */
93
+ :is(#nd-page div[class*='w-[400px'], .api-tryit-example)
94
+ div[class*='rounded-xl'][class*='overflow-hidden']
95
+ > div[class*='bg-fd-background'] {
96
+ margin: 0 1px 1px;
97
+ }
98
+
99
+ /* The request panel gets the same card as the response: rounded, grey, clipped. */
100
+ :is(#nd-page div[class*='w-[400px'], .api-tryit-example) .openapi-usage {
101
+ display: flex;
102
+ flex-direction: column;
103
+ margin: 1rem 0;
104
+ border: 1px solid var(--color-fd-border);
105
+ border-radius: 12px;
106
+ overflow: hidden;
107
+ background: var(--color-fd-secondary);
108
+ }
109
+
110
+ /* Panel headers (the response status bar): breathable, not bulky. The status
111
+ buttons carry their own py-2, so the bar itself gets only a sliver — this
112
+ lands both headers at the same 43px height. */
113
+ :is(#nd-page div[class*='w-[400px'], .api-tryit-example) div[class*='overflow-x-auto'] {
114
+ padding: 0.1875rem 0.8rem;
115
+ }
116
+
117
+ /* The active tab's underline hugs the text instead of floating at the button's
118
+ far edge — trade padding for margin so height and centering are unchanged. */
119
+ :is(#nd-page div[class*='w-[400px'], .api-tryit-example) div[class*='overflow-x-auto'] > button {
120
+ padding-bottom: 0.25rem;
121
+ margin-bottom: 0.25rem;
122
+ }
123
+
124
+ /* Patched usage panel header: endpoint name left, language dropdown right. */
125
+ .openapi-usage-header {
126
+ display: flex;
127
+ align-items: center;
128
+ justify-content: space-between;
129
+ gap: 0.75rem;
130
+ padding: 0.45rem 0.8rem;
131
+ border-bottom: 1px solid var(--color-fd-border);
132
+ }
133
+
134
+ .openapi-usage-title {
135
+ min-width: 0;
136
+ overflow: hidden;
137
+ font-size: 13px;
138
+ font-weight: 600;
139
+ color: var(--color-fd-foreground);
140
+ text-overflow: ellipsis;
141
+ white-space: nowrap;
142
+ }
143
+
144
+ .openapi-usage-lang {
145
+ flex-shrink: 0;
146
+ /* The fumadocs Select trigger defaults to full width; keep it compact. */
147
+ width: auto !important;
148
+ min-width: 0;
149
+ gap: 6px;
150
+ height: auto;
151
+ padding: 4px 9px;
152
+ font-size: 12px;
153
+ border-radius: 7px;
154
+ background: var(--color-fd-card);
155
+ }
156
+
157
+ /* Multi-example request selector (endpoints with several request bodies, e.g.
158
+ NGN/GBP/USD payout examples): a clean compact dropdown above the request box,
159
+ not a bare full-width button. Its hidden Select input is removed. */
160
+ :is(#nd-page div[class*='w-[400px'], .api-tryit-example)
161
+ > div[class*='prose-no-margin']
162
+ > button:first-child {
163
+ flex-shrink: 0;
164
+ width: auto;
165
+ align-self: flex-start;
166
+ gap: 8px;
167
+ margin-bottom: 0.75rem;
168
+ padding: 5px 11px;
169
+ border-radius: 9px;
170
+ border-color: var(--color-fd-border);
171
+ background: var(--color-fd-card);
172
+ font-size: 12.5px;
173
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
174
+ }
175
+
176
+ :is(#nd-page div[class*='w-[400px'], .api-tryit-example) > div[class*='prose-no-margin'] > input {
177
+ display: none;
178
+ }
179
+
180
+ /* The endpoint title renders inside the left column; style it as a page title. */
181
+ #nd-page div[class*='min-w-0'][class*='flex-1'] > div:first-child :is(h1, h2) {
182
+ font-size: 1.875rem;
183
+ font-weight: 700;
184
+ line-height: 1.15;
185
+ letter-spacing: -0.01em;
186
+ }
187
+
188
+ /* Copy-page action on the title row, held to the left column's right edge. The
189
+ row overlaps other content (negative margin + z-index), so it must not
190
+ swallow their clicks: the row is click-through, only its button is live. */
191
+ #nd-page .api-page-actions {
192
+ display: flex;
193
+ justify-content: flex-end;
194
+ position: relative;
195
+ z-index: 2;
196
+ margin-bottom: -2.75rem;
197
+ pointer-events: none;
198
+ }
199
+
200
+ #nd-page .api-page-actions > * {
201
+ pointer-events: auto;
202
+ }
203
+
204
+ @media (min-width: 1280px) {
205
+ #nd-page .api-page-actions {
206
+ padding-right: calc(460px + 1.5rem);
207
+ }
208
+ }
209
+
210
+ /* Roomier endpoint description. */
211
+ #nd-page div[class*='min-w-0'][class*='flex-1'] > div:first-child + * :is(p, span),
212
+ #nd-page div[class*='min-w-0'][class*='flex-1'] > p:first-of-type {
213
+ line-height: 1.95;
214
+ }
215
+
216
+ /* Prev/next footer belongs to the left column — margin (not padding) so the
217
+ divider line also ends where the rail's column begins. Scoped to the API
218
+ reference view (its page marker is the 1285px cap); guide pages have no rail,
219
+ so their footer must span the full content width. */
220
+ @media (min-width: 1280px) {
221
+ #nd-page[class*='1285'] .grid:has(> a[class*='rounded-lg']) {
222
+ margin-right: calc(460px + 1.5rem);
223
+ }
224
+ }
225
+
226
+ /* The rail never moves. The outer div stays in the flow as a 460px spacer; the
227
+ inner wrapper is position:fixed (auto left = the spacer's x), viewport height.
228
+ Both boxes stay visible; only their content scrolls. */
229
+ @media (min-width: 1280px) {
230
+ #nd-page div[class*='w-[400px'] {
231
+ position: static;
232
+ width: 460px;
233
+ }
234
+
235
+ #nd-page div[class*='w-[400px'] > div[class*='prose-no-margin'] {
236
+ position: fixed;
237
+ top: 6.5rem;
238
+ width: 460px;
239
+ /* Max height, not fixed: the boxes are content-sized and only shrink and
240
+ scroll when the viewport runs out — no grey filler on tall screens. */
241
+ max-height: calc(100dvh - 8rem);
242
+ display: flex;
243
+ flex-direction: column;
244
+ gap: 1rem;
245
+ min-height: 0;
246
+ }
247
+
248
+ /* Request box: what it needs, up to half the rail; its code scrolls. */
249
+ #nd-page div[class*='w-[400px'] .openapi-usage {
250
+ flex: 0 1 auto;
251
+ max-height: 50%;
252
+ min-height: 0;
253
+ margin: 0;
254
+ }
255
+
256
+ /* Response box: content-sized; shrinks and scrolls when space runs out. */
257
+ #nd-page div[class*='w-[400px'] > div[class*='prose-no-margin'] > div:last-child {
258
+ display: flex;
259
+ flex-direction: column;
260
+ flex: 0 1 auto;
261
+ min-height: 0;
262
+ margin: 0;
263
+ }
264
+
265
+ /* Panel headers never shrink — a squeezed box clips its code, not its header. */
266
+ #nd-page div[class*='w-[400px'] .openapi-usage-header,
267
+ #nd-page div[class*='w-[400px'] div[class*='overflow-x-auto'] {
268
+ flex-shrink: 0;
269
+ }
270
+
271
+ /* The response card's tab panel is the element that shrinks when the rail is
272
+ squeezed — let it shrink (flex defaults to min-height:auto) and make it the
273
+ scroll container, otherwise its content paints past it and the card's
274
+ overflow:hidden swallows it. */
275
+ #nd-page div[class*='w-[400px'] > div[class*='prose-no-margin'] > div:last-child > * {
276
+ min-height: 0;
277
+ overflow-y: auto;
278
+ scrollbar-width: thin;
279
+ }
280
+
281
+ /* Request figure only: a flex column so its code region gets a real (shrunk)
282
+ height to scroll in when the box is squeezed, and its content height when
283
+ not. The response figure sits inside fumadocs' own scroll wrapper and must
284
+ stay untouched — clipping it there hides content instead of scrolling. */
285
+ #nd-page div[class*='w-[400px'] .openapi-usage figure.shiki {
286
+ display: flex;
287
+ flex-direction: column;
288
+ flex: 0 1 auto;
289
+ min-height: 0;
290
+ overflow: hidden;
291
+ }
292
+ }
293
+
294
+ #nd-page div[class*='w-[400px'] .openapi-usage figure.shiki [role='region'] {
295
+ flex: 0 1 auto;
296
+ min-height: 0;
297
+ overflow: auto;
298
+ scrollbar-width: thin;
299
+ }
300
+
301
+ /* --- Try-it bar + modal ------------------------------------------------- */
302
+
303
+ /* Compact endpoint bar (Mintlify-style): method chip, url, "Try it" button. */
304
+ #nd-page .api-tryit-bar {
305
+ display: flex;
306
+ align-items: center;
307
+ gap: 0.65rem;
308
+ margin: 1.25rem 0 0.5rem;
309
+ padding: 0.4rem 0.4rem 0.4rem 0.65rem;
310
+ border: 1px solid var(--color-fd-border);
311
+ border-radius: 12px;
312
+ background: var(--color-fd-card);
313
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
314
+ }
315
+
316
+ #nd-page .api-tryit-method {
317
+ flex-shrink: 0;
318
+ padding: 3px 9px;
319
+ border-radius: 7px;
320
+ font-size: 11.5px;
321
+ font-weight: 700;
322
+ letter-spacing: 0.02em;
323
+ font-family: var(--font-mono, ui-monospace, monospace);
324
+ color: #fff;
325
+ background: var(--color-fd-primary);
326
+ }
327
+
328
+ #nd-page .api-tryit-method[data-method='get'] {
329
+ background: #16a34a;
330
+ }
331
+
332
+ #nd-page .api-tryit-method[data-method='delete'] {
333
+ background: #dc2626;
334
+ }
335
+
336
+ #nd-page .api-tryit-method[data-method='patch'],
337
+ #nd-page .api-tryit-method[data-method='put'] {
338
+ background: #d97706;
339
+ }
340
+
341
+ #nd-page .api-tryit-url {
342
+ flex: 1;
343
+ min-width: 0;
344
+ overflow: hidden;
345
+ white-space: nowrap;
346
+ text-overflow: ellipsis;
347
+ font-family: var(--font-mono, ui-monospace, monospace);
348
+ font-size: 13px;
349
+ }
350
+
351
+ #nd-page .api-tryit-host {
352
+ color: var(--color-fd-muted-foreground);
353
+ }
354
+
355
+ #nd-page .api-tryit-path {
356
+ color: var(--color-fd-foreground);
357
+ font-weight: 500;
358
+ }
359
+
360
+ #nd-page .api-tryit-btn {
361
+ display: inline-flex;
362
+ align-items: center;
363
+ gap: 6px;
364
+ flex-shrink: 0;
365
+ padding: 7px 14px;
366
+ border: none;
367
+ border-radius: 9px;
368
+ font-size: 13px;
369
+ font-weight: 600;
370
+ color: var(--color-fd-primary-foreground, #fff);
371
+ background: var(--color-fd-primary);
372
+ cursor: pointer;
373
+ transition: filter 0.15s ease;
374
+ }
375
+
376
+ #nd-page .api-tryit-btn:hover {
377
+ filter: brightness(1.08);
378
+ }
379
+
380
+ /* Modal: dimmed backdrop, centered panel; the playground form lives inside. */
381
+ .api-tryit-overlay {
382
+ position: fixed;
383
+ inset: 0;
384
+ z-index: 9999;
385
+ display: grid;
386
+ place-items: center;
387
+ padding: clamp(16px, 4vw, 56px);
388
+ background: color-mix(in oklab, black 52%, transparent);
389
+ backdrop-filter: blur(2px);
390
+ }
391
+
392
+ .api-tryit-modal {
393
+ position: relative;
394
+ width: min(720px, 100%);
395
+ height: min(85vh, 64rem);
396
+ display: grid;
397
+ grid-template-columns: minmax(0, 1fr);
398
+ overflow: hidden;
399
+ border: 1px solid var(--color-fd-border);
400
+ border-radius: 16px;
401
+ background: var(--color-fd-background);
402
+ box-shadow: 0 24px 70px -12px rgba(0, 0, 0, 0.45);
403
+ }
404
+
405
+ /* Two columns (Mintlify): the form left, the code sample + response right. */
406
+ .api-tryit-modal[data-columns='2'] {
407
+ width: min(1240px, 100%);
408
+ grid-template-columns: minmax(0, 1fr) minmax(0, 440px);
409
+ }
410
+
411
+ @media (max-width: 1100px) {
412
+ .api-tryit-modal[data-columns='2'] {
413
+ width: min(720px, 100%);
414
+ grid-template-columns: minmax(0, 1fr);
415
+ }
416
+
417
+ .api-tryit-modal[data-columns='2'] .api-tryit-example {
418
+ display: none;
419
+ }
420
+ }
421
+
422
+ .api-tryit-form {
423
+ min-height: 0;
424
+ display: flex;
425
+ flex-direction: column;
426
+ overflow: hidden;
427
+ }
428
+
429
+ /* Only the fields scroll; the request bar above stays pinned. */
430
+ .api-tryit-scroll {
431
+ flex: 1;
432
+ min-height: 0;
433
+ overflow-y: auto;
434
+ scrollbar-width: thin;
435
+ padding: 1.25rem 1.5rem 1.5rem;
436
+ }
437
+
438
+ /* The playground form inside the modal: drop its card chrome — the modal is
439
+ the card. */
440
+ .api-tryit-form form[class*='rounded-xl'] {
441
+ border: none;
442
+ box-shadow: none;
443
+ }
444
+
445
+ /* The request bar. Fumadocs renders a server-URL row and a method/Send row; the
446
+ framework hides both and renders its own single-line bar (RequestBar), which
447
+ mirrors the live URL and forwards clicks to those hidden controls — the URL
448
+ stays live and Send still runs the real request. */
449
+ .api-tryit-form form > button:first-of-type,
450
+ .api-tryit-form form > div:first-of-type {
451
+ display: none;
452
+ }
453
+
454
+ /* The bar: [method badge] base-url [env chevron] /path — with Send outside. A
455
+ pinned header (it does not scroll with the fields), set a little down from the
456
+ modal's top edge and divided from the scrolling content below. */
457
+ .api-tryit-reqline {
458
+ flex: none;
459
+ display: flex;
460
+ align-items: center;
461
+ gap: 10px;
462
+ padding: 1.7rem 1.5rem 1.1rem;
463
+ border-bottom: 1px solid var(--color-fd-border);
464
+ }
465
+
466
+ .api-tryit-reqbar {
467
+ flex: 1;
468
+ min-width: 0;
469
+ display: flex;
470
+ align-items: center;
471
+ gap: 8px;
472
+ padding: 6px 8px 6px 7px;
473
+ border: 1px solid var(--color-fd-border);
474
+ border-radius: 12px;
475
+ background: color-mix(in oklab, var(--color-fd-muted) 45%, var(--color-fd-secondary));
476
+ font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, monospace);
477
+ font-size: 13.5px;
478
+ overflow: hidden;
479
+ }
480
+
481
+ /* The URL + path scroll here when long; the method (left) and copy (right) stay
482
+ pinned, so a long endpoint never breaks the bar or pushes Send off. */
483
+ .api-tryit-reqscroll {
484
+ flex: 1;
485
+ min-width: 0;
486
+ display: flex;
487
+ align-items: center;
488
+ gap: 8px;
489
+ overflow-x: auto;
490
+ scrollbar-width: none;
491
+ }
492
+
493
+ .api-tryit-reqscroll::-webkit-scrollbar {
494
+ display: none;
495
+ }
496
+
497
+ .api-tryit-reqmethod {
498
+ flex: none;
499
+ padding: 3px 9px;
500
+ border-radius: 7px;
501
+ font-size: 12px;
502
+ font-weight: 700;
503
+ letter-spacing: 0.02em;
504
+ color: var(--color-fd-primary);
505
+ background: color-mix(in oklab, var(--color-fd-primary) 15%, transparent);
506
+ }
507
+
508
+ .api-tryit-reqmethod[data-method='get'] {
509
+ color: #16a34a;
510
+ background: color-mix(in oklab, #16a34a 15%, transparent);
511
+ }
512
+
513
+ .api-tryit-reqmethod[data-method='delete'] {
514
+ color: #dc2626;
515
+ background: color-mix(in oklab, #dc2626 15%, transparent);
516
+ }
517
+
518
+ .api-tryit-reqmethod[data-method='put'],
519
+ .api-tryit-reqmethod[data-method='patch'] {
520
+ color: #d97706;
521
+ background: color-mix(in oklab, #d97706 15%, transparent);
522
+ }
523
+
524
+ .api-tryit-envwrap {
525
+ position: relative;
526
+ flex: none;
527
+ display: inline-flex;
528
+ }
529
+
530
+ .api-tryit-env {
531
+ flex: none;
532
+ display: inline-flex;
533
+ align-items: center;
534
+ gap: 5px;
535
+ padding: 0;
536
+ border: none;
537
+ background: none;
538
+ font: inherit;
539
+ color: var(--color-fd-muted-foreground);
540
+ cursor: pointer;
541
+ }
542
+
543
+ /* Server menu: a small list under the URL, rather than the playground's
544
+ full-screen server dialog. Portaled and fixed-positioned from the trigger's
545
+ rect, because the request bar clips its overflow for long URLs. */
546
+ .api-tryit-envmenu {
547
+ position: fixed;
548
+ z-index: 10001;
549
+ margin: 0;
550
+ padding: 4px;
551
+ list-style: none;
552
+ min-width: max-content;
553
+ border: 1px solid var(--color-fd-border);
554
+ border-radius: 10px;
555
+ background: var(--color-fd-popover, var(--color-fd-card));
556
+ box-shadow:
557
+ 0 1px 2px rgba(0, 0, 0, 0.06),
558
+ 0 10px 26px -8px rgba(0, 0, 0, 0.35);
559
+ }
560
+
561
+ .api-tryit-envitem {
562
+ display: flex;
563
+ align-items: center;
564
+ gap: 7px;
565
+ width: 100%;
566
+ padding: 6px 10px 6px 6px;
567
+ border: none;
568
+ border-radius: 7px;
569
+ background: none;
570
+ color: var(--color-fd-foreground);
571
+ font: inherit;
572
+ text-align: left;
573
+ white-space: nowrap;
574
+ cursor: pointer;
575
+ }
576
+
577
+ .api-tryit-envitem:hover {
578
+ background: color-mix(in oklab, var(--color-fd-foreground) 8%, transparent);
579
+ }
580
+
581
+ .api-tryit-envitem[data-active] {
582
+ color: var(--color-fd-primary);
583
+ }
584
+
585
+ /* A fixed slot for the tick, so the URLs line up whether or not one is set. */
586
+ .api-tryit-envcheck {
587
+ flex: none;
588
+ display: inline-flex;
589
+ align-items: center;
590
+ justify-content: center;
591
+ width: 13px;
592
+ height: 13px;
593
+ }
594
+
595
+ .api-tryit-envcheck svg {
596
+ width: 12px;
597
+ height: 12px;
598
+ }
599
+
600
+ .api-tryit-env svg {
601
+ width: 13px;
602
+ height: 13px;
603
+ opacity: 0.85;
604
+ }
605
+
606
+ .api-tryit-env:hover {
607
+ color: var(--color-fd-foreground);
608
+ }
609
+
610
+ .api-tryit-reqpath {
611
+ display: inline-flex;
612
+ align-items: center;
613
+ white-space: nowrap;
614
+ color: var(--color-fd-foreground);
615
+ font-weight: 600;
616
+ }
617
+
618
+ .api-tryit-slash {
619
+ margin: 0 3px;
620
+ font-weight: 400;
621
+ color: var(--color-fd-muted-foreground);
622
+ }
623
+
624
+ /* Copy the full URL — pinned to the right of the bar, past the scrolling URL. */
625
+ .api-tryit-copy {
626
+ flex: none;
627
+ display: inline-flex;
628
+ align-items: center;
629
+ justify-content: center;
630
+ width: 28px;
631
+ height: 28px;
632
+ padding: 0;
633
+ border: none;
634
+ border-radius: 7px;
635
+ background: none;
636
+ color: var(--color-fd-muted-foreground);
637
+ cursor: pointer;
638
+ transition:
639
+ color 0.12s ease,
640
+ background 0.12s ease;
641
+ }
642
+
643
+ .api-tryit-copy svg {
644
+ width: 15px;
645
+ height: 15px;
646
+ }
647
+
648
+ .api-tryit-copy:hover {
649
+ color: var(--color-fd-foreground);
650
+ background: color-mix(in oklab, var(--color-fd-foreground) 8%, transparent);
651
+ }
652
+
653
+ .api-tryit-copy[data-copied] {
654
+ color: #16a34a;
655
+ }
656
+
657
+ .api-tryit-send {
658
+ flex: none;
659
+ display: inline-flex;
660
+ align-items: center;
661
+ gap: 7px;
662
+ padding: 9px 18px;
663
+ border: none;
664
+ border-radius: 10px;
665
+ background: var(--color-fd-primary);
666
+ color: var(--color-fd-primary-foreground, #fff);
667
+ font-size: 14px;
668
+ font-weight: 600;
669
+ cursor: pointer;
670
+ transition: filter 0.15s ease;
671
+ }
672
+
673
+ .api-tryit-send svg {
674
+ width: 10px;
675
+ height: 10px;
676
+ }
677
+
678
+ .api-tryit-send:hover {
679
+ filter: brightness(1.07);
680
+ }
681
+
682
+ .api-tryit-send[data-loading] {
683
+ cursor: progress;
684
+ opacity: 0.85;
685
+ }
686
+
687
+ .api-tryit-send .api-tryit-spin {
688
+ width: 13px;
689
+ height: 13px;
690
+ animation: api-tryit-spin 0.7s linear infinite;
691
+ }
692
+
693
+ @keyframes api-tryit-spin {
694
+ to {
695
+ transform: rotate(360deg);
696
+ }
697
+ }
698
+
699
+ /* The response panel (fumadocs' result display). Round it off instead of the
700
+ default full-bleed top/bottom borders, and give the JSON body room to breathe.
701
+ The response headers are hidden — the status, URL, and body are what a reader
702
+ needs from a try-it call. */
703
+ .api-tryit-form div[class*='border-y'][class*='bg-fd-secondary'] {
704
+ margin-top: 1.1rem;
705
+ padding: 14px;
706
+ border: 1px solid var(--color-fd-border);
707
+ border-radius: 14px;
708
+ }
709
+
710
+ /* Hide the "Headers (N)" disclosure (its button carries the `group`/`w-fit`
711
+ utilities the status-row Close button does not). */
712
+ .api-tryit-form
713
+ div[class*='border-y'][class*='bg-fd-secondary']
714
+ > div:has(> button[class*='group'][class*='w-fit']) {
715
+ display: none;
716
+ }
717
+
718
+ /* Roomier line height for the JSON body. The result code block is portaled with
719
+ the modal, outside #nd-page, so the base code styles don't reach it. */
720
+ .api-tryit-form figure.shiki code {
721
+ line-height: 1.75;
722
+ }
723
+
724
+ /* Right column: the code sample + response, on the secondary surface. */
725
+ .api-tryit-example {
726
+ min-height: 0;
727
+ overflow-y: auto;
728
+ scrollbar-width: thin;
729
+ padding: 1.25rem;
730
+ border-left: 1px solid var(--color-fd-border);
731
+ background: color-mix(in oklab, var(--color-fd-secondary) 45%, var(--color-fd-background));
732
+ }
733
+
734
+ /* Playground sections (Authorization, Body) as always-open cards. */
735
+ .api-tryit-section {
736
+ margin: 1rem 0;
737
+ padding: 1rem 1.1rem;
738
+ border: 1px solid var(--color-fd-border);
739
+ border-radius: 12px;
740
+ background: var(--color-fd-card);
741
+ }
742
+
743
+ .api-tryit-section-title {
744
+ margin: 0 0 0.75rem;
745
+ font-size: 14px;
746
+ font-weight: 600;
747
+ color: var(--color-fd-foreground);
748
+ }
749
+
750
+ /* Fumadocs' playground uses Base UI popups (the language select, the server-URL
751
+ dialog, schema popovers) that portal to <body> at z-50. The modal overlay is
752
+ at 9999, so while it is open those popups would open *behind* it. Lifting the
753
+ portal container above the overlay puts them back on top. Scoped to when the
754
+ modal is open so page-level popups are untouched. */
755
+ body.api-tryit-open [data-base-ui-portal] {
756
+ position: relative;
757
+ z-index: 10000;
758
+ }
759
+
760
+ /* The server-URL editor is a plain Base UI dialog. Give it a premium,
761
+ intentional feel: narrower, roomier, on the card surface, with a refined
762
+ shadow — and style the URL select and the (otherwise unstyled) custom-URL
763
+ input so nothing looks like a raw browser control. */
764
+ body.api-tryit-open [data-base-ui-portal] [role='dialog'] {
765
+ width: min(440px, calc(100vw - 32px));
766
+ max-width: min(440px, calc(100vw - 32px));
767
+ gap: 14px;
768
+ padding: 22px;
769
+ border-radius: 16px;
770
+ background: var(--color-fd-card);
771
+ box-shadow:
772
+ 0 1px 2px rgba(0, 0, 0, 0.06),
773
+ 0 20px 50px -16px rgba(0, 0, 0, 0.4);
774
+ }
775
+
776
+ body.api-tryit-open [data-base-ui-portal] [role='dialog'] h2 {
777
+ font-size: 16px;
778
+ letter-spacing: -0.01em;
779
+ }
780
+
781
+ body.api-tryit-open [data-base-ui-portal] [role='dialog'] button[role='combobox'] {
782
+ padding: 10px 12px;
783
+ border-radius: 10px;
784
+ background: var(--color-fd-background);
785
+ font-size: 13px;
786
+ }
787
+
788
+ body.api-tryit-open [data-base-ui-portal] [role='dialog'] > button:last-of-type {
789
+ position: absolute;
790
+ top: 14px;
791
+ right: 14px;
792
+ color: var(--color-fd-muted-foreground);
793
+ }
794
+
795
+ /* Body-mode switch. Fumadocs renders one button that toggles between the field
796
+ inputs and a raw-JSON textarea ("Open/Close JSON Editor"). Restyle it into a
797
+ Mintlify-style segmented control — Fields | JSON — drawn as the button's two
798
+ pseudo-elements. The button is made inert and pointer-events are re-enabled
799
+ only on the *inactive* segment, so clicking a segment selects it and clicking
800
+ the active one is a no-op. Current mode is read from whether the JSON textarea
801
+ is present. The toggle is the only form button that is secondary + text-xs +
802
+ bordered (Send is primary; the auth combobox is text-sm). */
803
+ .api-tryit-section[data-type='body']
804
+ button[class*='bg-fd-secondary'][class*='text-xs'][class*='border'] {
805
+ position: relative;
806
+ display: inline-flex;
807
+ align-items: stretch;
808
+ gap: 0;
809
+ height: auto;
810
+ padding: 3px;
811
+ font-size: 0;
812
+ line-height: 1;
813
+ border: 1px solid var(--color-fd-border);
814
+ border-radius: 11px;
815
+ background: color-mix(in oklab, var(--color-fd-muted) 55%, var(--color-fd-secondary));
816
+ box-shadow: none;
817
+ pointer-events: none;
818
+ }
819
+
820
+ /* Any native icon/label children are hidden; the segments are the pseudos. */
821
+ .api-tryit-section[data-type='body']
822
+ button[class*='bg-fd-secondary'][class*='text-xs'][class*='border']
823
+ > * {
824
+ display: none;
825
+ }
826
+
827
+ .api-tryit-section[data-type='body']
828
+ button[class*='bg-fd-secondary'][class*='text-xs'][class*='border']::before,
829
+ .api-tryit-section[data-type='body']
830
+ button[class*='bg-fd-secondary'][class*='text-xs'][class*='border']::after {
831
+ display: inline-flex;
832
+ align-items: center;
833
+ padding: 6px 14px 6px 32px;
834
+ font-size: 13px;
835
+ font-weight: 500;
836
+ line-height: 1;
837
+ border-radius: 8px;
838
+ color: var(--color-fd-muted-foreground);
839
+ background-repeat: no-repeat;
840
+ background-position: 12px center;
841
+ background-size: 14px;
842
+ pointer-events: none;
843
+ transition:
844
+ color 0.12s ease,
845
+ background-color 0.12s ease,
846
+ box-shadow 0.12s ease;
847
+ }
848
+
849
+ .api-tryit-section[data-type='body']
850
+ button[class*='bg-fd-secondary'][class*='text-xs'][class*='border']::before {
851
+ content: 'Fields';
852
+ margin-right: 2px;
853
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%238a8a94' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1'/%3E%3Cpath d='M16 21h1a2 2 0 0 0 2-2v-5a2 2 0 0 1 2-2 2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1'/%3E%3C/svg%3E");
854
+ }
855
+
856
+ .api-tryit-section[data-type='body']
857
+ button[class*='bg-fd-secondary'][class*='text-xs'][class*='border']::after {
858
+ content: 'JSON';
859
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%238a8a94' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'/%3E%3Cpath d='M14 2v6h6'/%3E%3C/svg%3E");
860
+ }
861
+
862
+ /* The active segment is raised. Fields mode = no JSON textarea present. */
863
+ .api-tryit-section[data-type='body']:not(:has(textarea[class*='font-mono']))
864
+ button[class*='bg-fd-secondary'][class*='text-xs'][class*='border']::before,
865
+ .api-tryit-section[data-type='body']:has(textarea[class*='font-mono'])
866
+ button[class*='bg-fd-secondary'][class*='text-xs'][class*='border']::after {
867
+ color: var(--color-fd-foreground);
868
+ background-color: var(--color-fd-card);
869
+ box-shadow:
870
+ 0 1px 2px rgba(0, 0, 0, 0.08),
871
+ 0 0 0 1px rgba(0, 0, 0, 0.03);
872
+ }
873
+
874
+ /* Only the inactive segment accepts clicks (re-enabled on the inert button),
875
+ so selecting the current mode is a no-op instead of an accidental toggle. */
876
+ .api-tryit-section[data-type='body']:not(:has(textarea[class*='font-mono']))
877
+ button[class*='bg-fd-secondary'][class*='text-xs'][class*='border']::after,
878
+ .api-tryit-section[data-type='body']:has(textarea[class*='font-mono'])
879
+ button[class*='bg-fd-secondary'][class*='text-xs'][class*='border']::before {
880
+ pointer-events: auto;
881
+ cursor: pointer;
882
+ }
883
+
884
+ /* --- The JSON body editor ------------------------------------------------ */
885
+
886
+ /* Fumadocs' own JSON textarea stays mounted and authoritative (it owns parsing
887
+ and the form engine) but is not shown — JsonBodyEditor renders the editor the
888
+ reader actually sees and writes every edit back to it. */
889
+ .api-tryit-section[data-type='body'] div:has(> textarea:not(.api-json-input)) {
890
+ display: none;
891
+ }
892
+
893
+ .api-json {
894
+ margin-top: 0.75rem;
895
+ border: 1px solid var(--color-fd-border);
896
+ border-radius: 12px;
897
+ overflow: hidden;
898
+ background: var(--color-fd-background);
899
+ }
900
+
901
+ .api-json[data-invalid] {
902
+ border-color: color-mix(in oklab, #ef4444 55%, var(--color-fd-border));
903
+ }
904
+
905
+ .api-json-toolbar {
906
+ display: flex;
907
+ justify-content: flex-end;
908
+ gap: 6px;
909
+ padding: 6px 8px;
910
+ border-bottom: 1px solid var(--color-fd-border);
911
+ background: color-mix(in oklab, var(--color-fd-muted) 45%, var(--color-fd-secondary));
912
+ }
913
+
914
+ .api-json-action {
915
+ padding: 4px 10px;
916
+ border: none;
917
+ border-radius: 7px;
918
+ background: none;
919
+ color: var(--color-fd-muted-foreground);
920
+ font-size: 12px;
921
+ font-weight: 500;
922
+ cursor: pointer;
923
+ transition:
924
+ color 0.12s ease,
925
+ background 0.12s ease;
926
+ }
927
+
928
+ .api-json-action:hover:not(:disabled) {
929
+ color: var(--color-fd-foreground);
930
+ background: color-mix(in oklab, var(--color-fd-foreground) 8%, transparent);
931
+ }
932
+
933
+ .api-json-action:disabled {
934
+ opacity: 0.4;
935
+ cursor: default;
936
+ }
937
+
938
+ /* The mirror and the textarea must share every text metric, or the caret drifts
939
+ from the painted glyphs. Both inherit the editor's font, padding, and
940
+ wrapping; the mirror sits in flow and sets the height, and the textarea lies
941
+ exactly over it with transparent text and a visible caret. */
942
+ .api-json-editor {
943
+ position: relative;
944
+ font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, monospace);
945
+ font-size: 12.5px;
946
+ line-height: 1.7;
947
+ tab-size: 2;
948
+ }
949
+
950
+ .api-json-mirror,
951
+ .api-json-editor .api-json-input {
952
+ margin: 0;
953
+ padding: 0.85rem 1rem;
954
+ border: 0;
955
+ font: inherit;
956
+ line-height: inherit;
957
+ tab-size: inherit;
958
+ white-space: pre-wrap;
959
+ overflow-wrap: break-word;
960
+ word-break: break-word;
961
+ }
962
+
963
+ .api-json-mirror {
964
+ min-height: 8.5rem;
965
+ pointer-events: none;
966
+ color: var(--color-fd-foreground);
967
+ }
968
+
969
+ .api-json-editor .api-json-input {
970
+ position: absolute;
971
+ inset: 0;
972
+ width: 100%;
973
+ height: 100%;
974
+ resize: none;
975
+ outline: none;
976
+ overflow: hidden;
977
+ background: transparent;
978
+ color: transparent;
979
+ caret-color: var(--color-fd-primary);
980
+ }
981
+
982
+ .api-json-editor .api-json-input::selection {
983
+ background: color-mix(in oklab, var(--color-fd-primary) 30%, transparent);
984
+ }
985
+
986
+ /* Highlight palette, keyed to the token kinds the tokenizer emits. */
987
+ .api-json-mirror [data-token='key'] {
988
+ color: #2563eb;
989
+ }
990
+
991
+ .api-json-mirror [data-token='string'] {
992
+ color: #b91c1c;
993
+ }
994
+
995
+ .api-json-mirror [data-token='number'] {
996
+ color: #047857;
997
+ }
998
+
999
+ .api-json-mirror [data-token='literal'] {
1000
+ color: #7c3aed;
1001
+ }
1002
+
1003
+ .api-json-mirror [data-token='punctuation'] {
1004
+ color: var(--color-fd-muted-foreground);
1005
+ }
1006
+
1007
+ .dark .api-json-mirror [data-token='key'] {
1008
+ color: #7aa2f7;
1009
+ }
1010
+
1011
+ .dark .api-json-mirror [data-token='string'] {
1012
+ color: #ff9e9e;
1013
+ }
1014
+
1015
+ .dark .api-json-mirror [data-token='number'] {
1016
+ color: #6ee7b7;
1017
+ }
1018
+
1019
+ .dark .api-json-mirror [data-token='literal'] {
1020
+ color: #c4b5fd;
1021
+ }
1022
+
1023
+ .api-json-error {
1024
+ margin: 0;
1025
+ padding: 8px 12px;
1026
+ border-top: 1px solid color-mix(in oklab, #ef4444 40%, var(--color-fd-border));
1027
+ background: color-mix(in oklab, #ef4444 10%, transparent);
1028
+ color: #b42318;
1029
+ font-size: 12px;
1030
+ font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, monospace);
1031
+ }
1032
+
1033
+ .dark .api-json-error {
1034
+ color: #ff9a94;
1035
+ }
1036
+
1037
+ /* --- Try-it parameter fields -------------------------------------------- */
1038
+
1039
+ /* The section header row carries the mode toggle, a required asterisk, and the
1040
+ body type. The asterisk floats between them as a stray glyph — the body being
1041
+ required is already implied — so hide just that one (field asterisks stay).
1042
+ The row is keyed by the name slot holding the toggle button. */
1043
+ .api-tryit-section[data-type='body']
1044
+ span[class*='font-medium']:has(> button)
1045
+ > span[class*='text-red'] {
1046
+ display: none;
1047
+ }
1048
+
1049
+ /* Body type ("object") — a quiet chip, not loose muted text. */
1050
+ .api-tryit-section[data-type='body'] span[class*='font-medium']:has(> button) + code {
1051
+ padding: 2px 8px;
1052
+ border-radius: 6px;
1053
+ background: color-mix(in oklab, var(--color-fd-foreground) 6%, transparent);
1054
+ color: var(--color-fd-muted-foreground);
1055
+ }
1056
+
1057
+ /* Field label row: the name reads first, its type trails quietly behind. */
1058
+ .api-tryit-scroll fieldset > label {
1059
+ margin-bottom: 1px;
1060
+ gap: 0.5rem;
1061
+ }
1062
+
1063
+ .api-tryit-scroll fieldset > label > span {
1064
+ font-size: 12.5px;
1065
+ letter-spacing: -0.01em;
1066
+ color: var(--color-fd-foreground);
1067
+ }
1068
+
1069
+ .api-tryit-scroll fieldset > label > span > span[class*='text-red'] {
1070
+ margin: 0 0 0 2px;
1071
+ color: #ef4444;
1072
+ }
1073
+
1074
+ .api-tryit-scroll fieldset > label > code {
1075
+ flex-shrink: 0;
1076
+ font-size: 11.5px;
1077
+ opacity: 0.75;
1078
+ }
1079
+
1080
+ /* Inputs and selects: a real, editable surface. Fumadocs' default is a flat
1081
+ secondary block whose border barely reads against it, so a reader cannot tell
1082
+ what is editable — give it contrast and a hover. */
1083
+ .api-tryit-scroll input[class*='rounded-md'],
1084
+ .api-tryit-scroll button[role='combobox'] {
1085
+ border-radius: 9px;
1086
+ border-color: var(--color-fd-border);
1087
+ background: var(--color-fd-background);
1088
+ font-family: var(--font-mono, ui-monospace, 'SF Mono', Menlo, monospace);
1089
+ font-size: 12.5px;
1090
+ transition:
1091
+ border-color 0.12s ease,
1092
+ background 0.12s ease;
1093
+ }
1094
+
1095
+ /* A single-line value: a fixed control height reads evenly down the form. */
1096
+ .api-tryit-scroll input[class*='rounded-md'] {
1097
+ height: 38px;
1098
+ padding: 0 11px;
1099
+ }
1100
+
1101
+ /* Selects must grow instead: the authorization-scheme picker carries its scheme
1102
+ name and a description, so a fixed height clipped the description over the
1103
+ label beneath it. */
1104
+ .api-tryit-scroll button[role='combobox'] {
1105
+ min-height: 38px;
1106
+ padding: 8px 11px;
1107
+ }
1108
+
1109
+ /* That picker is a direct child of the panel and carries no spacing of its own,
1110
+ so it sat flush against the field below it. */
1111
+ .api-tryit-section[data-type='authorization'] > button[role='combobox'] {
1112
+ margin-bottom: 0.9rem;
1113
+ }
1114
+
1115
+ /* Fumadocs' clear-value action sits bare beside the type label, reading as a
1116
+ stray glyph; give it a hit area and a hover so it reads as a control. */
1117
+ .api-tryit-scroll fieldset > label > button:last-child {
1118
+ flex: none;
1119
+ display: inline-flex;
1120
+ align-items: center;
1121
+ justify-content: center;
1122
+ width: 20px;
1123
+ height: 20px;
1124
+ margin-left: 2px;
1125
+ border-radius: 5px;
1126
+ transition: background 0.12s ease;
1127
+ }
1128
+
1129
+ .api-tryit-scroll fieldset > label > button:last-child:hover {
1130
+ background: color-mix(in oklab, var(--color-fd-foreground) 9%, transparent);
1131
+ }
1132
+
1133
+ .api-tryit-scroll fieldset > label > button:last-child svg {
1134
+ width: 13px;
1135
+ height: 13px;
1136
+ }
1137
+
1138
+ .api-tryit-scroll input[class*='rounded-md']:hover,
1139
+ .api-tryit-scroll button[role='combobox']:hover {
1140
+ border-color: color-mix(in oklab, var(--color-fd-foreground) 22%, var(--color-fd-border));
1141
+ }
1142
+
1143
+ /* Placeholders stay clearly lighter than real values, so a pre-filled example
1144
+ never reads as an empty field. */
1145
+ .api-tryit-scroll input[class*='rounded-md']::placeholder {
1146
+ color: var(--color-fd-muted-foreground);
1147
+ opacity: 0.6;
1148
+ }
1149
+
1150
+ /* The fields box: even rhythm between fields, a little air under the mode
1151
+ toggle, and a lighter shadow than the default `shadow-sm`. */
1152
+ .api-tryit-scroll div[class*='grid'][class*='gap-4'] {
1153
+ margin-top: 0.55rem;
1154
+ gap: 0.9rem;
1155
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
1156
+ }
1157
+
1158
+ /* --- Sidebar method badges ---------------------------------------------- */
1159
+
1160
+ /* Each API sidebar item: a fixed-width method badge, then the endpoint name —
1161
+ badges align in a column, as Mintlify does. */
1162
+ .api-nav-item {
1163
+ display: inline-flex;
1164
+ align-items: center;
1165
+ gap: 0.6rem;
1166
+ min-width: 0;
1167
+ }
1168
+
1169
+ .api-nav-text {
1170
+ min-width: 0;
1171
+ overflow: hidden;
1172
+ text-overflow: ellipsis;
1173
+ }
1174
+
1175
+ .api-method-badge {
1176
+ flex-shrink: 0;
1177
+ display: inline-flex;
1178
+ align-items: center;
1179
+ justify-content: center;
1180
+ min-width: 42px;
1181
+ padding: 2px 6px;
1182
+ border-radius: 5px;
1183
+ font-size: 9.5px;
1184
+ font-weight: 700;
1185
+ letter-spacing: 0.03em;
1186
+ font-family: var(--font-mono, ui-monospace, monospace);
1187
+ line-height: 1.4;
1188
+ color: var(--color-fd-primary);
1189
+ background: color-mix(in oklab, var(--color-fd-primary) 12%, transparent);
1190
+ }
1191
+
1192
+ .api-method-badge[data-method='get'] {
1193
+ color: #16a34a;
1194
+ background: color-mix(in oklab, #16a34a 13%, transparent);
1195
+ }
1196
+
1197
+ .api-method-badge[data-method='delete'] {
1198
+ color: #dc2626;
1199
+ background: color-mix(in oklab, #dc2626 13%, transparent);
1200
+ }
1201
+
1202
+ .api-method-badge[data-method='put'],
1203
+ .api-method-badge[data-method='patch'] {
1204
+ color: #d97706;
1205
+ background: color-mix(in oklab, #d97706 14%, transparent);
1206
+ }
1207
+
1208
+ /* --- Schema fields ------------------------------------------------------ */
1209
+
1210
+ /* Constraint chips (Length, Format…): quiet, flat tags. Not scoped to #nd-page
1211
+ so the portaled schema popover gets the same treatment. */
1212
+ .not-prose[class*='bg-fd-secondary'][class*='shadow-md'][class*='text-xs'] {
1213
+ box-shadow: none;
1214
+ border: none;
1215
+ border-radius: 6px;
1216
+ padding: 2px 8px;
1217
+ background: color-mix(in oklab, var(--color-fd-muted-foreground) 9%, transparent);
1218
+ }
1219
+
1220
+ /* Nested-object popover: opaque, roomy, scrolls within a sane height. */
1221
+ div[class*='bg-fd-popover'][class*='backdrop-blur'] {
1222
+ background: var(--color-fd-popover, var(--color-fd-card));
1223
+ backdrop-filter: none;
1224
+ min-width: 340px;
1225
+ padding: 10px;
1226
+ max-height: min(72vh, 34rem);
1227
+ box-shadow:
1228
+ 0 4px 6px -2px rgba(0, 0, 0, 0.08),
1229
+ 0 16px 40px -8px rgba(0, 0, 0, 0.35);
1230
+ }
1231
+
1232
+ /* Field spacing comes from the wrapper, not the name row — a taller name row
1233
+ would throw off the inline tag's centering. */
1234
+ div[class*='bg-fd-popover'] div[class*='py-4'] {
1235
+ padding-block: 1.15rem;
1236
+ }
1237
+
1238
+ div[class*='bg-fd-popover'] [class*='prose-no-margin'] {
1239
+ padding-block: 0.15rem 0.35rem;
1240
+ }
1241
+
1242
+ /* Clear separation between properties. */
1243
+ #nd-page div[class*='border-t'][class*='py-4'][class*='first:border-t-0'] {
1244
+ border-top-color: var(--color-fd-border);
1245
+ padding-block: 1.15rem;
1246
+ }
1247
+
1248
+ /* Show the Format/constraint tag inline on the field row (top-right), centered
1249
+ on the row's midline — Mintlify's name · type · badge layout. */
1250
+ :is(#nd-page, div[class*='bg-fd-popover']) div[class*='py-4'][class*='border-t'] {
1251
+ position: relative;
1252
+ }
1253
+
1254
+ /* A property row lifts while hovered, so an open enum menu paints above the
1255
+ rows beneath it — their top borders would otherwise show through it. */
1256
+ :is(#nd-page, div[class*='bg-fd-popover']) div[class*='py-4'][class*='border-t']:hover {
1257
+ z-index: 5;
1258
+ }
1259
+
1260
+ :is(#nd-page, div[class*='bg-fd-popover'])
1261
+ div[class*='prose-no-margin']
1262
+ > div[class*='mt-2'][class*='flex-row'][class*='flex-wrap'] {
1263
+ position: absolute;
1264
+ top: calc(1.15rem + 11px);
1265
+ transform: translateY(-50%);
1266
+ right: 2.2rem;
1267
+ margin: 0;
1268
+ align-items: center;
1269
+ }
1270
+
1271
+ /* Enum values ("Value in") share that pinned row with the small constraint
1272
+ chips, but they are a whole card with a bulleted list — pinned, it floated
1273
+ over the neighbouring property's text. Collapse it to a chip the same size as
1274
+ its neighbours that reveals its values on hover. */
1275
+ :is(#nd-page, div[class*='bg-fd-popover'])
1276
+ div[class*='prose-no-margin']
1277
+ > div[class*='mt-2'][class*='flex-row']
1278
+ > div[class*='flex-col'][class*='bg-fd-secondary'] {
1279
+ position: relative;
1280
+ width: auto;
1281
+ gap: 0;
1282
+ padding: 2px 8px;
1283
+ border: none;
1284
+ border-radius: 6px;
1285
+ background: color-mix(in oklab, var(--color-fd-muted-foreground) 9%, transparent);
1286
+ box-shadow: none;
1287
+ }
1288
+
1289
+ /* The card's heading is the chip label; mark it as openable. */
1290
+ :is(#nd-page, div[class*='bg-fd-popover'])
1291
+ div[class*='prose-no-margin']
1292
+ > div[class*='mt-2'][class*='flex-row']
1293
+ > div[class*='flex-col'][class*='bg-fd-secondary']
1294
+ > p {
1295
+ display: flex;
1296
+ align-items: center;
1297
+ gap: 4px;
1298
+ white-space: nowrap;
1299
+ cursor: pointer;
1300
+ user-select: none;
1301
+ }
1302
+
1303
+ :is(#nd-page, div[class*='bg-fd-popover'])
1304
+ div[class*='prose-no-margin']
1305
+ > div[class*='mt-2'][class*='flex-row']
1306
+ > div[class*='flex-col'][class*='bg-fd-secondary']
1307
+ > p::after {
1308
+ content: '';
1309
+ width: 11px;
1310
+ height: 11px;
1311
+ background: currentColor;
1312
+ opacity: 0.65;
1313
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
1314
+ no-repeat center / contain;
1315
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
1316
+ no-repeat center / contain;
1317
+ }
1318
+
1319
+ /* The values become the menu: hidden until the chip is hovered. */
1320
+ :is(#nd-page, div[class*='bg-fd-popover'])
1321
+ div[class*='prose-no-margin']
1322
+ > div[class*='mt-2'][class*='flex-row']
1323
+ > div[class*='flex-col'][class*='bg-fd-secondary']
1324
+ > ul {
1325
+ display: none;
1326
+ position: absolute;
1327
+ top: calc(100% + 6px);
1328
+ right: 0;
1329
+ z-index: 20;
1330
+ margin: 0;
1331
+ padding: 5px;
1332
+ min-width: max-content;
1333
+ border: 1px solid var(--color-fd-border);
1334
+ border-radius: 10px;
1335
+ background: var(--color-fd-popover, var(--color-fd-card));
1336
+ box-shadow:
1337
+ 0 1px 2px rgba(0, 0, 0, 0.06),
1338
+ 0 10px 26px -8px rgba(0, 0, 0, 0.35);
1339
+ }
1340
+
1341
+ :is(#nd-page, div[class*='bg-fd-popover'])
1342
+ div[class*='prose-no-margin']
1343
+ > div[class*='mt-2'][class*='flex-row']
1344
+ > div[class*='flex-col'][class*='bg-fd-secondary'][data-open]
1345
+ > ul {
1346
+ display: block;
1347
+ }
1348
+
1349
+ :is(#nd-page, div[class*='bg-fd-popover'])
1350
+ div[class*='prose-no-margin']
1351
+ > div[class*='mt-2'][class*='flex-row']
1352
+ > div[class*='flex-col'][class*='bg-fd-secondary']
1353
+ > ul
1354
+ > li {
1355
+ padding: 4px 9px;
1356
+ border-radius: 6px;
1357
+ list-style: none;
1358
+ white-space: nowrap;
1359
+ }
1360
+
1361
+ /* The request-example selector above the property list ("NGN — Bank Transfer"):
1362
+ a compact pill rather than a full-width field. */
1363
+ #nd-page button[role='combobox'] {
1364
+ gap: 8px;
1365
+ padding: 6px 11px;
1366
+ border-radius: 9px;
1367
+ border-color: var(--color-fd-border);
1368
+ background: var(--color-fd-card);
1369
+ font-size: 13px;
1370
+ }
1371
+
1372
+ #nd-page button[role='combobox']:hover {
1373
+ border-color: color-mix(in oklab, var(--color-fd-foreground) 22%, var(--color-fd-border));
1374
+ }
1375
+
1376
+ /* Every select popup on the page — the example picker, the code-sample
1377
+ languages — gets the same menu as the request bar's server switcher. */
1378
+ [data-base-ui-portal] [role='listbox'] {
1379
+ padding: 4px;
1380
+ }
1381
+
1382
+ [data-base-ui-portal] [role='listbox'] [role='option'] {
1383
+ padding: 6px 10px;
1384
+ border-radius: 7px;
1385
+ font-size: 13px;
1386
+ }
1387
+
1388
+ /* Roomier response/status accordion rows. */
1389
+ #nd-page div[class*='border-b'][class*='last:border-b-0'] > h3[class*='font-medium'] {
1390
+ padding-block: 0.7rem;
1391
+ }
1392
+
1393
+ /* Replace the bare "*" required marker with a Mintlify-style pill. */
1394
+ #nd-page span[class*='font-medium'][class*='font-mono'] > span[class*='text-red-400'] {
1395
+ font-size: 0;
1396
+ margin-left: 6px;
1397
+ }
1398
+
1399
+ #nd-page span[class*='font-medium'][class*='font-mono'] > span[class*='text-red-400']::after {
1400
+ content: 'required';
1401
+ font-size: 11px;
1402
+ font-weight: 500;
1403
+ letter-spacing: 0.01em;
1404
+ color: #e5484d;
1405
+ background: color-mix(in oklab, #e5484d 13%, transparent);
1406
+ padding: 2px 7px;
1407
+ border-radius: 6px;
1408
+ }
1409
+
1410
+ /* Softer playground card. */
1411
+ #nd-page form[class*='rounded-xl'][class*='shadow-md'] {
1412
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
1413
+ }
1414
+
1415
+ .dark #nd-page form[class*='rounded-xl'][class*='shadow-md'] {
1416
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
1417
+ }