@casoon/pages-theme 0.0.0-stage → 0.12.6

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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +274 -2
  3. package/assets/casoon-logo-dark.svg +70 -0
  4. package/assets/casoon-logo.svg +71 -0
  5. package/assets/favicon.svg +1 -0
  6. package/components/AnchorGrid.astro +135 -0
  7. package/components/ApiEntry.astro +136 -0
  8. package/components/Badge.astro +31 -0
  9. package/components/Badges.astro +10 -0
  10. package/components/BuildOutput.astro +84 -0
  11. package/components/Button.astro +55 -0
  12. package/components/CallToAction.astro +64 -0
  13. package/components/Callout.astro +66 -0
  14. package/components/CodeBlock.astro +84 -0
  15. package/components/Demo.astro +80 -0
  16. package/components/ExamplePanel.astro +153 -0
  17. package/components/Facts.astro +112 -0
  18. package/components/Faq.astro +123 -0
  19. package/components/Features.astro +139 -0
  20. package/components/FileSet.astro +258 -0
  21. package/components/Footer.astro +160 -0
  22. package/components/Header.astro +278 -0
  23. package/components/Hero.astro +318 -0
  24. package/components/Install.astro +154 -0
  25. package/components/Integrations.astro +125 -0
  26. package/components/Keymap.astro +102 -0
  27. package/components/Quickstart.astro +95 -0
  28. package/components/ReleaseHighlights.astro +115 -0
  29. package/components/Spotlight.astro +177 -0
  30. package/components/Swatch.astro +45 -0
  31. package/components/Tabs.astro +90 -0
  32. package/components/Terminal.astro +79 -0
  33. package/components/index.ts +25 -0
  34. package/content.ts +23 -0
  35. package/fonts/OFL.txt +95 -0
  36. package/fonts/inter-latin-400-normal.woff2 +0 -0
  37. package/fonts/inter-latin-500-normal.woff2 +0 -0
  38. package/fonts/inter-latin-600-normal.woff2 +0 -0
  39. package/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  40. package/fonts/jetbrains-mono-latin-500-normal.woff2 +0 -0
  41. package/fonts/onest-latin-wght-normal.woff2 +0 -0
  42. package/index.ts +204 -0
  43. package/layouts/Base.astro +129 -0
  44. package/layouts/Docs.astro +286 -0
  45. package/lib/ansi.ts +168 -0
  46. package/lib/changelog.ts +85 -0
  47. package/lib/docs.ts +54 -0
  48. package/lib/i18n.ts +136 -0
  49. package/lib/og.ts +91 -0
  50. package/lib/showcase.ts +48 -0
  51. package/lib/url.ts +16 -0
  52. package/lib/version.ts +50 -0
  53. package/package.json +56 -4
  54. package/routes/404.astro +49 -0
  55. package/routes/changelog.astro +129 -0
  56. package/routes/docs/[...slug].astro +39 -0
  57. package/routes/showcase/[slug].astro +108 -0
  58. package/routes/showcase/index.astro +195 -0
  59. package/styles/base.css +307 -0
  60. package/styles/fonts.css +52 -0
  61. package/styles/tokens.css +136 -0
  62. package/virtual.d.ts +12 -0
@@ -0,0 +1,318 @@
1
+ ---
2
+ /**
3
+ * Start-page hero in three layouts:
4
+ * split — text beside the `output` slot (default)
5
+ * stack — centred text, then a band: `source` slot → `command` → `output` slot
6
+ * editorial — full-width title, lead beside the default slot, `output` slot below
7
+ * `tone="invert"` puts any of them on a dark brand ground.
8
+ */
9
+ import config from 'virtual:casoon-pages/config';
10
+ import { t } from '../lib/i18n';
11
+
12
+ interface Props {
13
+ title: string;
14
+ lead: string;
15
+ eyebrow?: string;
16
+ layout?: 'split' | 'stack' | 'editorial';
17
+ /** Shown between the `source` and `output` slots of the stack layout, e.g. `astro build`. */
18
+ command?: string;
19
+ tone?: 'plain' | 'invert';
20
+ }
21
+
22
+ const {
23
+ title,
24
+ lead,
25
+ eyebrow = t(config.lang).heroEyebrow,
26
+ layout = 'split',
27
+ command,
28
+ tone = 'plain',
29
+ } = Astro.props;
30
+ const hasSource = Astro.slots.has('source');
31
+ ---
32
+
33
+ <section class:list={['hero', `is-${layout}`, tone === 'invert' && 'invert']}>
34
+ {
35
+ layout === 'split' && (
36
+ <div class="inner">
37
+ <div>
38
+ <p class="eyebrow">{eyebrow}</p>
39
+ <h1>{title}</h1>
40
+ <p class="lead">{lead}</p>
41
+ <slot />
42
+ </div>
43
+ <div class="output glow">
44
+ <div class="glow-inner">
45
+ <slot name="output" />
46
+ </div>
47
+ </div>
48
+ </div>
49
+ )
50
+ }
51
+
52
+ {
53
+ layout === 'stack' && (
54
+ <div class="inner">
55
+ <div class="head">
56
+ <p class="eyebrow">{eyebrow}</p>
57
+ <h1>{title}</h1>
58
+ <p class="lead">{lead}</p>
59
+ </div>
60
+ <div class="band">
61
+ {hasSource && (
62
+ <div class="source">
63
+ <slot name="source" />
64
+ </div>
65
+ )}
66
+ {hasSource && command && (
67
+ <p class="step">
68
+ <span class="pill">{command}</span>
69
+ <span class="arrow" aria-hidden="true">
70
+ →
71
+ </span>
72
+ </p>
73
+ )}
74
+ <div class="output">
75
+ <slot name="output" />
76
+ </div>
77
+ </div>
78
+ <div class="actions">
79
+ <slot />
80
+ </div>
81
+ </div>
82
+ )
83
+ }
84
+
85
+ {
86
+ layout === 'editorial' && (
87
+ <div class="inner">
88
+ <p class="eyebrow">{eyebrow}</p>
89
+ <h1>{title}</h1>
90
+ <div class="row">
91
+ <p class="lead">{lead}</p>
92
+ <div class="actions">
93
+ <slot />
94
+ </div>
95
+ </div>
96
+ <div class="output">
97
+ <slot name="output" />
98
+ </div>
99
+ </div>
100
+ )
101
+ }
102
+ </section>
103
+
104
+ <style>
105
+ /* Sand ground. */
106
+ .hero {
107
+ position: relative;
108
+ margin-top: calc(-1 * (var(--header-h) + 14px));
109
+ padding-top: calc(var(--header-h) + 14px);
110
+ background: var(--bg-deep);
111
+ overflow: hidden;
112
+ }
113
+ .inner {
114
+ position: relative;
115
+ max-width: 1240px;
116
+ margin: 0 auto;
117
+ padding: 88px 40px 140px;
118
+ }
119
+ .eyebrow {
120
+ display: flex;
121
+ align-items: center;
122
+ gap: 10px;
123
+ margin: 0 0 18px;
124
+ color: var(--heading);
125
+ font-size: 15px;
126
+ line-height: 1.6;
127
+ text-transform: uppercase;
128
+ }
129
+ .eyebrow::before {
130
+ content: '';
131
+ width: 6px;
132
+ height: 6px;
133
+ border-radius: 50%;
134
+ background: var(--accent-fill);
135
+ }
136
+ h1 {
137
+ margin: 0 0 28px;
138
+ color: var(--heading);
139
+ font-size: clamp(44px, 6vw, 84px);
140
+ font-weight: 600;
141
+ letter-spacing: -0.035em;
142
+ line-height: 0.98;
143
+ }
144
+ .lead {
145
+ max-width: 31rem;
146
+ margin: 0 0 34px;
147
+ color: var(--heading);
148
+ font-size: 20px;
149
+ line-height: 1.6;
150
+ }
151
+ /* Output in a card whose edge carries a slowly turning orange arc. */
152
+ .glow {
153
+ position: relative;
154
+ isolation: isolate;
155
+ overflow: hidden;
156
+ border-radius: 30px;
157
+ background: #f0ebe2;
158
+ box-shadow: var(--shadow-lift);
159
+ }
160
+ .glow::before {
161
+ content: '';
162
+ position: absolute;
163
+ z-index: -1;
164
+ top: 50%;
165
+ left: 50%;
166
+ width: 220%;
167
+ aspect-ratio: 1;
168
+ translate: -50% -50%;
169
+ background: conic-gradient(rgb(236 74 32 / 0) 0deg 185deg, #ec4a20 360deg);
170
+ animation: glow-spin 8.15s linear infinite;
171
+ }
172
+ .glow-inner {
173
+ margin: 4px;
174
+ overflow: hidden;
175
+ border-radius: 27px;
176
+ background: var(--surface);
177
+ }
178
+ .glow-inner :global(.terminal),
179
+ .glow-inner :global(figure) {
180
+ border: 0;
181
+ border-radius: 0;
182
+ }
183
+ @keyframes glow-spin {
184
+ to {
185
+ rotate: 360deg;
186
+ }
187
+ }
188
+ .output {
189
+ min-width: 0;
190
+ }
191
+
192
+ /* Text gets a little more than half and a slightly smaller title, so a title of about 40
193
+ characters fits on two lines at desktop width; in two equal halves it took three. */
194
+ .is-split .inner {
195
+ display: grid;
196
+ gap: 44px;
197
+ align-items: center;
198
+ }
199
+ @media (min-width: 900px) {
200
+ .is-split .inner {
201
+ grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
202
+ gap: 48px;
203
+ }
204
+ .is-split h1 {
205
+ font-size: clamp(44px, 5.4vw, 80px);
206
+ }
207
+ }
208
+
209
+ .is-stack .head {
210
+ text-align: center;
211
+ }
212
+ .is-stack .eyebrow {
213
+ display: inline-flex;
214
+ }
215
+ .is-stack .lead {
216
+ max-width: 62ch;
217
+ margin-inline: auto;
218
+ }
219
+ /* Input over output, not beside it: side by side, neither the source nor the file names
220
+ get enough width inside --max — the code is cut off and the names wrap to a second row. */
221
+ .is-stack .band {
222
+ display: flex;
223
+ flex-direction: column;
224
+ gap: 18px;
225
+ }
226
+ .is-stack .source {
227
+ min-width: 0;
228
+ }
229
+ .step {
230
+ display: flex;
231
+ align-items: center;
232
+ justify-content: center;
233
+ gap: 12px;
234
+ margin: 0;
235
+ }
236
+ .pill {
237
+ padding: 8px 14px;
238
+ border-radius: 999px;
239
+ background: var(--brand);
240
+ /* The page ground, not white: on the light brand of the dark theme white reaches only 3:1. */
241
+ color: var(--bg);
242
+ font-family: var(--font-mono);
243
+ font-size: 12px;
244
+ white-space: nowrap;
245
+ }
246
+ .arrow {
247
+ rotate: 90deg;
248
+ color: var(--brand);
249
+ font-size: 20px;
250
+ line-height: 1;
251
+ }
252
+ .actions {
253
+ display: flex;
254
+ flex-wrap: wrap;
255
+ align-items: center;
256
+ gap: 14px;
257
+ }
258
+ .is-stack .actions {
259
+ justify-content: center;
260
+ margin-top: 34px;
261
+ }
262
+
263
+ .is-editorial h1 {
264
+ margin-bottom: 36px;
265
+ font-size: clamp(38px, 6.2vw, 74px);
266
+ line-height: 1.02;
267
+ }
268
+ .is-editorial .row {
269
+ display: flex;
270
+ flex-wrap: wrap;
271
+ align-items: flex-start;
272
+ justify-content: space-between;
273
+ gap: 32px;
274
+ margin-bottom: 44px;
275
+ }
276
+ /* Lead and actions share the row about evenly; the actions keep the install box's width, so
277
+ the badges go below it instead of squeezing the lead into a narrow column. Both start at the
278
+ top, so the lead follows the title directly however tall the actions are. */
279
+ .is-editorial .lead {
280
+ flex: 1 1 380px;
281
+ margin: 0;
282
+ }
283
+ .is-editorial .actions {
284
+ flex: 0 1 470px;
285
+ }
286
+
287
+ /* Dark brand ground. Child components follow through the tokens. */
288
+ .invert {
289
+ --heading: #fff;
290
+ --text: rgb(255 255 255 / 0.78);
291
+ --muted: rgb(255 255 255 / 0.68);
292
+ --border: rgb(255 255 255 / 0.2);
293
+ --border-strong: rgb(255 255 255 / 0.32);
294
+ --surface: rgb(255 255 255 / 0.07);
295
+ --bg-deep: rgb(255 255 255 / 0.1);
296
+ --code-bg: rgb(255 255 255 / 0.07);
297
+ --accent: var(--invert-accent);
298
+ --shadow: none;
299
+
300
+ background: var(--invert-bg);
301
+ color: var(--text);
302
+ }
303
+
304
+ @media (max-width: 640px) {
305
+ .inner {
306
+ padding: 56px 20px 120px;
307
+ }
308
+ .is-stack .actions {
309
+ margin-top: 26px;
310
+ }
311
+ .is-editorial h1 {
312
+ margin-bottom: 26px;
313
+ }
314
+ .is-editorial .row {
315
+ margin-bottom: 32px;
316
+ }
317
+ }
318
+ </style>
@@ -0,0 +1,154 @@
1
+ ---
2
+ /** Install commands per ecosystem: CSS-only tabs, copy button when JS is available. */
3
+ import config from 'virtual:casoon-pages/config';
4
+ import { t } from '../lib/i18n';
5
+
6
+ interface Props {
7
+ commands: { label: string; command: string }[];
8
+ label?: string;
9
+ }
10
+
11
+ const s = t(config.lang);
12
+ const { commands, label = s.installLabel } = Astro.props;
13
+ const uid = `install-${commands.map((c) => c.label.replace(/\W/g, '')).join('-')}`;
14
+
15
+ /** Splits a command into the units a line may break between: words, and inside a word the path
16
+ segments after each "/" ("https://" is one). "./", "../", "~/" and a leading "/" stay with
17
+ what follows. Spaces are kept as separate parts. */
18
+ const units = (command: string) =>
19
+ command
20
+ .split(/( +)/)
21
+ .filter(Boolean)
22
+ .map((word) =>
23
+ word.startsWith(' ') ? word : word.split(/(?<=\/)(?!\/)(?<!^(?:\.{1,2}|~)?\/)/)
24
+ );
25
+ ---
26
+
27
+ <div class="install">
28
+ <div class="list" role="radiogroup" aria-label={label}>
29
+ {
30
+ commands.map((c, i) => (
31
+ <>
32
+ <input
33
+ class="visually-hidden"
34
+ type="radio"
35
+ name={uid}
36
+ id={`${uid}-${i}`}
37
+ value={i}
38
+ checked={i === 0}
39
+ />
40
+ <label for={`${uid}-${i}`}>{c.label}</label>
41
+ </>
42
+ ))
43
+ }
44
+ </div>
45
+ {
46
+ commands.map((c, i) => (
47
+ <div class="panel" data-i={i}>
48
+ <span class="prompt" aria-hidden="true">
49
+ $
50
+ </span>
51
+ {/* Long commands wrap instead of being cut off, so all of it stays visible and selectable.
52
+ Each unit is an inline-block, so a line breaks only at spaces and after a "/", never at a
53
+ hyphen; a unit wider than the box still breaks inside as a last resort. */}
54
+ <code id={`${uid}-cmd-${i}`}>
55
+ {units(c.command).map((part) =>
56
+ typeof part === 'string' ? part : part.map((unit) => <span class="unit">{unit}</span>),
57
+ )}
58
+ </code>
59
+ <button class="copy" type="button" data-copy={`${uid}-cmd-${i}`} data-copied={s.copied} hidden>
60
+ {s.copy}
61
+ </button>
62
+ </div>
63
+ ))
64
+ }
65
+ </div>
66
+
67
+ <style>
68
+ .install {
69
+ max-width: 470px;
70
+ overflow: hidden;
71
+ border: 1px solid var(--border);
72
+ border-radius: 14px;
73
+ background: var(--surface);
74
+ box-shadow: var(--shadow);
75
+ }
76
+ .list {
77
+ display: flex;
78
+ gap: 2px;
79
+ padding: 6px;
80
+ border-bottom: 1px solid var(--border);
81
+ }
82
+ label {
83
+ flex: 1;
84
+ padding: 7px 10px;
85
+ border-radius: 8px;
86
+ color: var(--muted);
87
+ font-family: var(--font-mono);
88
+ font-size: 12.5px;
89
+ text-align: center;
90
+ cursor: pointer;
91
+ }
92
+ input:checked + label {
93
+ background: var(--bg-deep);
94
+ color: var(--heading);
95
+ }
96
+ input:focus-visible + label {
97
+ outline: 2px solid var(--accent);
98
+ outline-offset: 2px;
99
+ }
100
+ .panel {
101
+ display: none;
102
+ align-items: baseline;
103
+ gap: 12px;
104
+ padding: 14px 16px;
105
+ }
106
+ /* First panel is the no-:has() fallback. */
107
+ .panel[data-i='0'],
108
+ .install:has(input[value='1']:checked) .panel[data-i='1'],
109
+ .install:has(input[value='2']:checked) .panel[data-i='2'],
110
+ .install:has(input[value='3']:checked) .panel[data-i='3'],
111
+ .install:has(input[value='4']:checked) .panel[data-i='4'],
112
+ .install:has(input[value='5']:checked) .panel[data-i='5'] {
113
+ display: flex;
114
+ }
115
+ .install:has(input:not([value='0']):checked) .panel[data-i='0'] {
116
+ display: none;
117
+ }
118
+ .prompt {
119
+ color: var(--muted);
120
+ font-family: var(--font-mono);
121
+ font-size: 13px;
122
+ }
123
+ /* Breaks between units (see above); a unit longer than the line breaks anywhere. */
124
+ code {
125
+ flex: 1;
126
+ min-width: 0;
127
+ color: var(--heading);
128
+ font-size: 13.5px;
129
+ line-height: 1.6;
130
+ white-space: pre-wrap;
131
+ overflow-wrap: anywhere;
132
+ }
133
+ .unit {
134
+ display: inline-block;
135
+ max-width: 100%;
136
+ }
137
+ .copy {
138
+ flex-shrink: 0;
139
+ padding: 4px 10px;
140
+ border: 1px solid var(--border);
141
+ border-radius: 7px;
142
+ background: none;
143
+ color: var(--muted);
144
+ font-size: 12px;
145
+ cursor: pointer;
146
+ }
147
+ .copy:hover {
148
+ border-color: var(--border-strong);
149
+ color: var(--heading);
150
+ }
151
+ .copy[hidden] {
152
+ display: none;
153
+ }
154
+ </style>
@@ -0,0 +1,125 @@
1
+ ---
2
+ /**
3
+ * A grid of things the project plugs into — registries, CI providers, editors. `icon` is a
4
+ * short mono label (an abbreviation, not a logo image: no external badge or icon service, same
5
+ * reasoning as `Badge`). `more`, given, appends a dashed placeholder card.
6
+ */
7
+ import config from 'virtual:casoon-pages/config';
8
+ import { t } from '../lib/i18n';
9
+
10
+ interface Props {
11
+ items: { name: string; category: string; icon: string; href?: string }[];
12
+ heading?: string;
13
+ /** Label for a trailing "more coming" placeholder card. Omitted: no placeholder card. */
14
+ more?: string;
15
+ }
16
+
17
+ const { items, heading, more } = Astro.props;
18
+ const s = t(config.lang);
19
+ ---
20
+
21
+ <section class="integrations" aria-label={heading ?? s.overview}>
22
+ {heading && <p class="title">{heading}</p>}
23
+ <div class="marquee">
24
+ {
25
+ [false, true].map((copy) => (
26
+ <ul aria-hidden={copy ? 'true' : undefined}>
27
+ {items.map((item) => (
28
+ <li>
29
+ {item.href && !copy ? (
30
+ <a href={item.href}>
31
+ <span class="icon" aria-hidden="true">
32
+ {item.icon}
33
+ </span>
34
+ <span class="name">{item.name}</span>
35
+ <span class="visually-hidden">{item.category}</span>
36
+ </a>
37
+ ) : (
38
+ <span class="item">
39
+ <span class="icon" aria-hidden="true">
40
+ {item.icon}
41
+ </span>
42
+ <span class="name">{item.name}</span>
43
+ </span>
44
+ )}
45
+ </li>
46
+ ))}
47
+ {more && (
48
+ <li>
49
+ <span class="item more">
50
+ <span class="name">{more}</span>
51
+ </span>
52
+ </li>
53
+ )}
54
+ </ul>
55
+ ))
56
+ }
57
+ </div>
58
+ </section>
59
+
60
+ <style>
61
+ /* Logo row that drifts sideways. The list is
62
+ rendered twice; the copy is hidden from assistive tech and not focusable. */
63
+ .integrations {
64
+ padding: 64px 0 80px;
65
+ background: var(--bg-deep);
66
+ text-align: center;
67
+ }
68
+ .title {
69
+ margin: 0 0 32px;
70
+ color: var(--heading);
71
+ font-size: 18px;
72
+ }
73
+ .marquee {
74
+ display: flex;
75
+ overflow: hidden;
76
+ mask-image: linear-gradient(90deg, transparent, #000 10% 90%, transparent);
77
+ }
78
+ ul {
79
+ display: flex;
80
+ flex: none;
81
+ gap: 72px;
82
+ margin: 0;
83
+ padding: 0 72px 0 0;
84
+ list-style: none;
85
+ animation: marquee 25s linear infinite;
86
+ }
87
+ .marquee:hover ul {
88
+ animation-play-state: paused;
89
+ }
90
+ a,
91
+ .item {
92
+ display: flex;
93
+ align-items: center;
94
+ gap: 12px;
95
+ color: var(--muted);
96
+ white-space: nowrap;
97
+ }
98
+ a:hover {
99
+ color: var(--heading);
100
+ }
101
+ .icon {
102
+ display: grid;
103
+ place-items: center;
104
+ width: 40px;
105
+ height: 40px;
106
+ border: 1.5px solid currentColor;
107
+ border-radius: 50%;
108
+ font-family: var(--font-mono);
109
+ font-size: 12px;
110
+ font-weight: 500;
111
+ }
112
+ .name {
113
+ font-size: 28px;
114
+ font-weight: 500;
115
+ letter-spacing: -0.01em;
116
+ }
117
+ .more .name {
118
+ font-weight: 400;
119
+ }
120
+ @keyframes marquee {
121
+ to {
122
+ transform: translateX(-100%);
123
+ }
124
+ }
125
+ </style>
@@ -0,0 +1,102 @@
1
+ ---
2
+ /**
3
+ * Keyboard shortcuts or CLI flags, grouped into cards: a label and one or more `<kbd>` keys per
4
+ * row. `keys` is a key sequence (chorded) — `['ctrl', 'k']` renders as two adjacent keycaps.
5
+ */
6
+ interface Props {
7
+ groups: {
8
+ heading: string;
9
+ items: { label: string; keys: string[] }[];
10
+ }[];
11
+ /** Section heading. Omitted: the section carries no heading of its own. */
12
+ heading?: string;
13
+ }
14
+
15
+ const { groups, heading } = Astro.props;
16
+ ---
17
+
18
+ <section class="keymap">
19
+ {heading && <h2 class="cp-label">{heading}</h2>}
20
+ <div class="groups">
21
+ {
22
+ groups.map((group) => (
23
+ <div class="group">
24
+ <p class="heading">{group.heading}</p>
25
+ <ul>
26
+ {group.items.map((item) => (
27
+ <li>
28
+ <span class="label">{item.label}</span>
29
+ <span class="keys">
30
+ {item.keys.map((key) => (
31
+ <kbd>{key}</kbd>
32
+ ))}
33
+ </span>
34
+ </li>
35
+ ))}
36
+ </ul>
37
+ </div>
38
+ ))
39
+ }
40
+ </div>
41
+ </section>
42
+
43
+ <style>
44
+ .keymap {
45
+ max-width: var(--max);
46
+ margin: 0 auto;
47
+ padding: 0 24px 80px;
48
+ }
49
+ .keymap > h2 {
50
+ margin: 0 0 20px;
51
+ }
52
+ .groups {
53
+ display: grid;
54
+ grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
55
+ gap: 18px;
56
+ }
57
+ .group {
58
+ padding: 20px 22px;
59
+ border-radius: 16px;
60
+ background: var(--bg-deep);
61
+ }
62
+ .heading {
63
+ margin: 0 0 14px;
64
+ color: var(--muted);
65
+ font-size: 13.5px;
66
+ font-weight: 600;
67
+ }
68
+ ul {
69
+ margin: 0;
70
+ padding: 0;
71
+ list-style: none;
72
+ }
73
+ li {
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: space-between;
77
+ gap: 16px;
78
+ padding: 11px 0;
79
+ border-top: 1px solid var(--border);
80
+ font-size: 14px;
81
+ }
82
+ li:first-child {
83
+ border-top: 0;
84
+ }
85
+ .keys {
86
+ display: flex;
87
+ flex-shrink: 0;
88
+ gap: 4px;
89
+ }
90
+ kbd {
91
+ min-width: 22px;
92
+ padding: 3px 6px;
93
+ border: 1px solid var(--border-strong);
94
+ border-radius: 6px;
95
+ background: var(--surface);
96
+ box-shadow: 0 1px 0 var(--border-strong);
97
+ color: var(--heading);
98
+ font-family: var(--font-mono);
99
+ font-size: 12px;
100
+ text-align: center;
101
+ }
102
+ </style>