@gemboss/website-ui 0.0.0-stage → 0.2.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 (44) hide show
  1. package/AGENTS.md +191 -0
  2. package/CHANGELOG.md +47 -0
  3. package/README.md +118 -2
  4. package/bin/gemboss-website.mjs +78 -0
  5. package/bin/lib/check.mjs +695 -0
  6. package/bin/lib/gap.mjs +189 -0
  7. package/bin/lib/init.mjs +99 -0
  8. package/bin/lib/upgrade.mjs +155 -0
  9. package/bin/lib/util.mjs +147 -0
  10. package/css/base.css +107 -0
  11. package/css/components.css +324 -0
  12. package/css/footer.css +53 -0
  13. package/css/header.css +231 -0
  14. package/css/layout.css +59 -0
  15. package/css/prose.css +119 -0
  16. package/css/sections.css +173 -0
  17. package/css/tokens.css +200 -0
  18. package/demo/index.html +703 -0
  19. package/demo/theme-example.html +133 -0
  20. package/docs/components.md +407 -0
  21. package/docs/markup-contract.md +148 -0
  22. package/docs/philosophy.md +69 -0
  23. package/docs/theming.md +96 -0
  24. package/js/website.js +186 -0
  25. package/package.json +50 -4
  26. package/skill/SKILL.md +49 -0
  27. package/system.json +122 -0
  28. package/templates/footer.html +40 -0
  29. package/templates/header.html +60 -0
  30. package/templates/page.html +134 -0
  31. package/templates/sections/cta.html +15 -0
  32. package/templates/sections/faq.html +24 -0
  33. package/templates/sections/features.html +32 -0
  34. package/templates/sections/hero.html +27 -0
  35. package/templates/sections/logos.html +15 -0
  36. package/templates/sections/pricing.html +51 -0
  37. package/templates/sections/prose.html +36 -0
  38. package/templates/sections/quote.html +13 -0
  39. package/templates/sections/showcase.html +25 -0
  40. package/templates/sections/split.html +27 -0
  41. package/templates/sections/stats.html +26 -0
  42. package/templates/sections/steps.html +17 -0
  43. package/website.css +1274 -0
  44. package/website.min.css +1 -0
package/website.css ADDED
@@ -0,0 +1,1274 @@
1
+ /*! @gemboss/website-ui 0.2.0 | generated by scripts/build.mjs from the files in css/. Edit those and not this file */
2
+ /* @gemboss/website-ui tokens. GENERATED from system.json by scripts/build.mjs: do not edit by hand. */
3
+ :root {
4
+ color-scheme: light;
5
+ --gw-font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
6
+ --gw-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
7
+ --gw-text-11: 11px;
8
+ --gw-text-12: 12px;
9
+ --gw-text-13: 13px;
10
+ --gw-text-14: 14px;
11
+ --gw-text-16: 16px;
12
+ --gw-text-18: 18px;
13
+ --gw-text-20: 20px;
14
+ --gw-text-22: 22px;
15
+ --gw-text-24: 24px;
16
+ --gw-text-30: 30px;
17
+ --gw-display-h1: clamp(40px, 4.6vw, 68px);
18
+ --gw-display-h2: clamp(32px, 3.4vw, 52px);
19
+ --gw-display-weight: 600;
20
+ --gw-display-tracking: -0.03em;
21
+ --gw-heading-weight: 650;
22
+ --gw-heading-tracking: -0.022em;
23
+ --gw-heading-leading: 1.08;
24
+ --gw-body-size: 18px;
25
+ --gw-body-leading: 1.6;
26
+ --gw-space-0: 0;
27
+ --gw-space-2: 2px;
28
+ --gw-space-4: 4px;
29
+ --gw-space-6: 6px;
30
+ --gw-space-8: 8px;
31
+ --gw-space-12: 12px;
32
+ --gw-space-16: 16px;
33
+ --gw-space-20: 20px;
34
+ --gw-space-24: 24px;
35
+ --gw-space-32: 32px;
36
+ --gw-space-40: 40px;
37
+ --gw-space-48: 48px;
38
+ --gw-space-64: 64px;
39
+ --gw-space-80: 80px;
40
+ --gw-space-96: 96px;
41
+ --gw-space-120: 120px;
42
+ --gw-space-section: clamp(56px, 38px + 5vw, 110px);
43
+ --gw-radius-0: 0;
44
+ --gw-radius-4: 4px;
45
+ --gw-radius-6: 6px;
46
+ --gw-radius-8: 8px;
47
+ --gw-radius-12: 12px;
48
+ --gw-radius-16: 16px;
49
+ --gw-radius-20: 20px;
50
+ --gw-radius-999: 999px;
51
+ --gw-radius-action: 8px;
52
+ --gw-radius-chip: 999px;
53
+ --gw-radius-card: 12px;
54
+ --gw-radius-panel: 16px;
55
+ --gw-shadow: 0 1px 2px rgba(31,30,29,.04), 0 2px 6px rgba(31,30,29,.06);
56
+ --gw-shadow-sm: 0 1px 2px rgba(31,30,29,.04);
57
+ --gw-shadow-raised: 0 4px 16px rgba(31,30,29,.10);
58
+ --gw-wrap: 1120px;
59
+ --gw-wrap-narrow: 780px;
60
+ --gw-gutter: 22px;
61
+ --gw-header-height: 64px;
62
+ --gw-button-height: 40px;
63
+ --gw-tap-min: 44px;
64
+ --gw-ease: cubic-bezier(.22,1,.36,1);
65
+ --gw-reveal-ms: 700ms;
66
+ --gw-spin-ms: 700ms;
67
+ --gw-wash-pink: #ff6f5e;
68
+ --gw-wash-violet: #7c8af7;
69
+ --gw-wash-sky: #43b0ff;
70
+ --gw-wash-mint: #1fcf9f;
71
+ /* accent primitives: the only colours a brand overrides (docs/theming.md) */
72
+ --gw-accent-light: #5661f5;
73
+ --gw-accent-strong-light: #3a44de;
74
+ --gw-accent-soft-light: #eef0fe;
75
+ --gw-on-accent-light: #ffffff;
76
+ --gw-accent-dark: #7c84ff;
77
+ --gw-accent-strong-dark: #a3a9ff;
78
+ --gw-accent-soft-dark: #1f2140;
79
+ --gw-on-accent-dark: #0d0d12;
80
+ --gw-bg: #ffffff;
81
+ --gw-bg-soft: #f2f3f5;
82
+ --gw-surface: #ffffff;
83
+ --gw-ink: #1b1c1e;
84
+ --gw-ink-2: #3b3d42;
85
+ --gw-muted: #686b71;
86
+ --gw-line: #e5e7ea;
87
+ --gw-line-strong: #d5d8dd;
88
+ --gw-action: #1b1c1e;
89
+ --gw-action-hover: #000000;
90
+ --gw-on-action: #ffffff;
91
+ --gw-accent: var(--gw-accent-light);
92
+ --gw-accent-strong: var(--gw-accent-strong-light);
93
+ --gw-accent-soft: var(--gw-accent-soft-light);
94
+ --gw-on-accent: var(--gw-on-accent-light);
95
+ --gw-success: #007f5f;
96
+ --gw-success-soft: #e3f1df;
97
+ --gw-warning: #9a6700;
98
+ --gw-night: #0c0e14;
99
+ --gw-night-2: #141722;
100
+ --gw-on-night: #e3e4e8;
101
+ --gw-on-night-muted: #9aa3ff;
102
+ }
103
+ @media (prefers-color-scheme: dark) {
104
+ :root:not([data-theme="light"]) {
105
+ color-scheme: dark;
106
+ --gw-bg: #0d0d12;
107
+ --gw-bg-soft: #121318;
108
+ --gw-surface: #16171d;
109
+ --gw-ink: #e3e4e8;
110
+ --gw-ink-2: #c4c6cc;
111
+ --gw-muted: #9a9ca6;
112
+ --gw-line: #2a2b33;
113
+ --gw-line-strong: #383944;
114
+ --gw-action: #f2f3f5;
115
+ --gw-action-hover: #ffffff;
116
+ --gw-on-action: #0d0d12;
117
+ --gw-accent: var(--gw-accent-dark);
118
+ --gw-accent-strong: var(--gw-accent-strong-dark);
119
+ --gw-accent-soft: var(--gw-accent-soft-dark);
120
+ --gw-on-accent: var(--gw-on-accent-dark);
121
+ --gw-success: #34c38f;
122
+ --gw-success-soft: #132a22;
123
+ --gw-warning: #e0b341;
124
+ --gw-night: #0a0b10;
125
+ --gw-night-2: #12141c;
126
+ --gw-on-night: #e3e4e8;
127
+ --gw-on-night-muted: #9aa3ff;
128
+ }
129
+ }
130
+ :root[data-theme="dark"] {
131
+ color-scheme: dark;
132
+ --gw-bg: #0d0d12;
133
+ --gw-bg-soft: #121318;
134
+ --gw-surface: #16171d;
135
+ --gw-ink: #e3e4e8;
136
+ --gw-ink-2: #c4c6cc;
137
+ --gw-muted: #9a9ca6;
138
+ --gw-line: #2a2b33;
139
+ --gw-line-strong: #383944;
140
+ --gw-action: #f2f3f5;
141
+ --gw-action-hover: #ffffff;
142
+ --gw-on-action: #0d0d12;
143
+ --gw-accent: var(--gw-accent-dark);
144
+ --gw-accent-strong: var(--gw-accent-strong-dark);
145
+ --gw-accent-soft: var(--gw-accent-soft-dark);
146
+ --gw-on-accent: var(--gw-on-accent-dark);
147
+ --gw-success: #34c38f;
148
+ --gw-success-soft: #132a22;
149
+ --gw-warning: #e0b341;
150
+ --gw-night: #0a0b10;
151
+ --gw-night-2: #12141c;
152
+ --gw-on-night: #e3e4e8;
153
+ --gw-on-night-muted: #9aa3ff;
154
+ }
155
+ :root[data-theme="light"] {
156
+ color-scheme: light;
157
+ --gw-bg: #ffffff;
158
+ --gw-bg-soft: #f2f3f5;
159
+ --gw-surface: #ffffff;
160
+ --gw-ink: #1b1c1e;
161
+ --gw-ink-2: #3b3d42;
162
+ --gw-muted: #686b71;
163
+ --gw-line: #e5e7ea;
164
+ --gw-line-strong: #d5d8dd;
165
+ --gw-action: #1b1c1e;
166
+ --gw-action-hover: #000000;
167
+ --gw-on-action: #ffffff;
168
+ --gw-accent: var(--gw-accent-light);
169
+ --gw-accent-strong: var(--gw-accent-strong-light);
170
+ --gw-accent-soft: var(--gw-accent-soft-light);
171
+ --gw-on-accent: var(--gw-on-accent-light);
172
+ --gw-success: #007f5f;
173
+ --gw-success-soft: #e3f1df;
174
+ --gw-warning: #9a6700;
175
+ --gw-night: #0c0e14;
176
+ --gw-night-2: #141722;
177
+ --gw-on-night: #e3e4e8;
178
+ --gw-on-night-muted: #9aa3ff;
179
+ }
180
+ /* night band: the dark palette inside, on the night ground, in both themes */
181
+ .gw-band--night, .gw-showcase {
182
+ color-scheme: dark;
183
+ --gw-bg: var(--gw-night);
184
+ --gw-bg-soft: var(--gw-night-2);
185
+ --gw-surface: var(--gw-night-2);
186
+ --gw-ink: #e3e4e8;
187
+ --gw-ink-2: #c4c6cc;
188
+ --gw-muted: #9a9ca6;
189
+ --gw-line: #2a2b33;
190
+ --gw-line-strong: #383944;
191
+ --gw-action: #f2f3f5;
192
+ --gw-action-hover: #ffffff;
193
+ --gw-on-action: #0d0d12;
194
+ --gw-accent: var(--gw-accent-dark);
195
+ --gw-accent-strong: var(--gw-accent-strong-dark);
196
+ --gw-accent-soft: var(--gw-accent-soft-dark);
197
+ --gw-on-accent: var(--gw-on-accent-dark);
198
+ --gw-success: #34c38f;
199
+ --gw-success-soft: #132a22;
200
+ --gw-warning: #e0b341;
201
+ }
202
+
203
+ /* base: reset, type, links, focus, skip link, reveal. Colours, radii and shadows only from tokens. */
204
+ *, *::before, *::after { box-sizing: border-box; }
205
+ [hidden] { display: none !important; }
206
+ html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
207
+ body {
208
+ margin: 0;
209
+ background: var(--gw-bg);
210
+ color: var(--gw-ink);
211
+ font-family: var(--gw-font);
212
+ font-size: var(--gw-body-size);
213
+ line-height: var(--gw-body-leading);
214
+ -webkit-font-smoothing: antialiased;
215
+ text-rendering: optimizeLegibility;
216
+ }
217
+ :where(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, dd, figure, blockquote) { margin: 0; }
218
+ img, svg, video { display: block; max-width: 100%; }
219
+ img, video { height: auto; }
220
+ button, input, select, textarea { font: inherit; color: inherit; }
221
+ button { cursor: pointer; }
222
+ a { color: inherit; text-decoration: none; }
223
+ b, strong { font-weight: 600; }
224
+
225
+ /* the page: the footer stays at the bottom of a short page */
226
+ .gw-page { display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh; }
227
+ .gw-page > main { flex: 1 0 auto; }
228
+
229
+ /* headings: h1/h2 are display (fluid), h3 and below are the heading scale */
230
+ h1, h2, h3, h4, h5, h6, .gw-h1, .gw-h2, .gw-h3, .gw-h4 {
231
+ font-weight: var(--gw-heading-weight);
232
+ letter-spacing: var(--gw-heading-tracking);
233
+ line-height: var(--gw-heading-leading);
234
+ text-wrap: balance;
235
+ }
236
+ h1, .gw-h1, h2, .gw-h2 { font-weight: var(--gw-display-weight); letter-spacing: var(--gw-display-tracking); }
237
+ h1, .gw-h1 { font-size: var(--gw-display-h1); }
238
+ h2, .gw-h2 { font-size: var(--gw-display-h2); }
239
+ h3, .gw-h3 { font-size: 24px; }
240
+ h4, .gw-h4 { font-size: 20px; font-weight: 600; letter-spacing: -0.01em; }
241
+ h5, h6 { font-size: 16px; font-weight: 600; letter-spacing: 0; }
242
+
243
+ .gw-lead { font-size: 18px; line-height: 1.55; color: var(--gw-muted); max-width: 60ch; }
244
+ .gw-note { font-size: 14px; color: var(--gw-muted); }
245
+ .gw-dim { color: var(--gw-muted); }
246
+
247
+ /* the label above a heading: sentence case, muted, led by the 8px accent square */
248
+ .gw-label {
249
+ display: inline-flex;
250
+ align-items: center;
251
+ gap: 8px;
252
+ font-size: 13px;
253
+ font-weight: 500;
254
+ line-height: 1.4;
255
+ color: var(--gw-muted);
256
+ }
257
+ .gw-label::before {
258
+ content: "";
259
+ flex: none;
260
+ width: 8px;
261
+ height: 8px;
262
+ border-radius: var(--gw-radius-0);
263
+ background: var(--gw-accent);
264
+ }
265
+
266
+ /* links inside running text (and anywhere in .gw-prose) get a visible link style; everything else opts in with .gw-link */
267
+ :where(p, dd, td, figcaption, .gw-list--check li, .gw-prose) a:not([class]),
268
+ .gw-link {
269
+ color: var(--gw-accent-strong);
270
+ font-weight: 500;
271
+ text-decoration: underline;
272
+ text-decoration-thickness: 1px;
273
+ text-underline-offset: 4px;
274
+ text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
275
+ }
276
+ :where(p, dd, td, figcaption, .gw-list--check li, .gw-prose) a:not([class]):hover,
277
+ .gw-link:hover { text-decoration-color: currentColor; }
278
+
279
+ :focus-visible { outline: 2px solid var(--gw-accent); outline-offset: 2px; }
280
+ [id] { scroll-margin-top: calc(var(--gw-header-height) + 16px); }
281
+
282
+ .gw-skip {
283
+ position: absolute;
284
+ left: -9999px;
285
+ top: 8px;
286
+ z-index: 100;
287
+ padding: 12px 16px;
288
+ border-radius: var(--gw-radius-action);
289
+ background: var(--gw-action);
290
+ color: var(--gw-on-action);
291
+ font-size: 14px;
292
+ font-weight: 600;
293
+ }
294
+ .gw-skip:focus { left: 16px; }
295
+
296
+ /* scroll reveal: the script adds .is-in; the animation is a failsafe that shows it anyway after 2.5s */
297
+ .gw-reveal {
298
+ opacity: 0;
299
+ transform: translateY(24px);
300
+ transition: opacity var(--gw-reveal-ms) var(--gw-ease), transform var(--gw-reveal-ms) var(--gw-ease);
301
+ animation: gw-reveal-failsafe 1ms linear 2.5s forwards;
302
+ }
303
+ .gw-reveal.is-in { opacity: 1; transform: none; }
304
+ @keyframes gw-reveal-failsafe { to { opacity: 1; transform: none; } }
305
+
306
+ @media (prefers-reduced-motion: reduce) {
307
+ *, *::before, *::after { scroll-behavior: auto !important; }
308
+ .gw-reveal { opacity: 1; transform: none; transition: none; animation: none; }
309
+ }
310
+
311
+ /* layout: widths, section rhythm, grids, night band. Breakpoints 480 / 768 / 1080 (system.json). */
312
+ .gw-wrap,
313
+ .gw-wrap--narrow {
314
+ width: min(100% - 2 * var(--gw-gutter), var(--gw-wrap));
315
+ margin-inline: auto;
316
+ }
317
+ .gw-wrap--narrow { max-width: var(--gw-wrap-narrow); }
318
+
319
+ .gw-section { padding-block: var(--gw-space-section); }
320
+ .gw-section--soft { background: var(--gw-bg-soft); }
321
+ .gw-section--tight { padding-block: 40px; }
322
+ @media (min-width: 768px) { .gw-section--tight { padding-block: 64px; } }
323
+
324
+ /* label + h2 + one or two lines; the vertical flow of every text block in the kit */
325
+ .gw-section__head { max-width: 660px; }
326
+ .gw-section__head--center { margin-inline: auto; text-align: center; }
327
+ .gw-section__head--center .gw-lead { margin-inline: auto; }
328
+ .gw-section__head:not(:last-child) { margin-bottom: 40px; }
329
+ @media (min-width: 768px) { .gw-section__head:not(:last-child) { margin-bottom: 48px; } }
330
+
331
+ :where(.gw-section__head, .gw-hero > .gw-wrap, .gw-split > :not(.gw-window), .gw-cta, .gw-card, .gw-feature, .gw-plan) > * + * { margin-top: 16px; }
332
+ :where(.gw-section__head, .gw-hero > .gw-wrap, .gw-split > :not(.gw-window), .gw-cta, .gw-card, .gw-plan) > :is(.gw-actions, .gw-segmented) { margin-top: 32px; }
333
+ :where(.gw-section__head, .gw-hero > .gw-wrap, .gw-split > :not(.gw-window), .gw-cta, .gw-card, .gw-plan) > .gw-list--check { margin-top: 24px; }
334
+
335
+ /* a row of actions: one .gw-btn, then a secondary or a link */
336
+ .gw-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
337
+
338
+ /* grids: one column on phones */
339
+ .gw-grid,
340
+ .gw-grid--2,
341
+ .gw-grid--3,
342
+ .gw-grid--4 {
343
+ display: grid;
344
+ grid-template-columns: minmax(0, 1fr);
345
+ gap: 16px;
346
+ }
347
+ @media (min-width: 480px) {
348
+ .gw-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
349
+ }
350
+ @media (min-width: 768px) {
351
+ .gw-grid, .gw-grid--2, .gw-grid--3, .gw-grid--4 { gap: 24px; }
352
+ .gw-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
353
+ .gw-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
354
+ }
355
+ @media (min-width: 1080px) {
356
+ .gw-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
357
+ }
358
+
359
+ /* night band: the dark palette (tokens.css) on the night ground, lit from the top edge.
360
+ At most three on a page, never two in a row. */
361
+ .gw-band--night,
362
+ .gw-showcase {
363
+ position: relative;
364
+ isolation: isolate;
365
+ color: var(--gw-ink);
366
+ background:
367
+ radial-gradient(800px 360px at 50% 0%, color-mix(in srgb, var(--gw-accent) 22%, transparent), transparent 72%),
368
+ var(--gw-night);
369
+ }
370
+
371
+ /* components: buttons, links, chips, badges, cards, windows, icons, lists, tables, FAQ. */
372
+
373
+ /* actions are rectangles: 40px, radius 8, 14px/600, sentence case, ink fill (never the accent) */
374
+ .gw-btn {
375
+ position: relative;
376
+ display: inline-flex;
377
+ align-items: center;
378
+ justify-content: center;
379
+ gap: 8px;
380
+ max-width: 100%;
381
+ height: var(--gw-button-height);
382
+ padding: 0 20px;
383
+ border: 1px solid transparent;
384
+ border-radius: var(--gw-radius-action);
385
+ background: var(--gw-action);
386
+ color: var(--gw-on-action);
387
+ font-size: 14px;
388
+ font-weight: 600;
389
+ line-height: 1.4;
390
+ letter-spacing: 0;
391
+ text-align: center;
392
+ white-space: nowrap;
393
+ cursor: pointer;
394
+ }
395
+ .gw-btn:hover { background: var(--gw-action-hover); }
396
+ .gw-btn svg { flex: none; width: 16px; height: 16px; }
397
+ .gw-btn--secondary { background: transparent; border-color: var(--gw-line-strong); color: var(--gw-ink); }
398
+ .gw-btn--secondary:hover { background: transparent; border-color: var(--gw-ink); }
399
+ .gw-btn--ghost { background: transparent; color: var(--gw-ink); }
400
+ .gw-btn--ghost:hover { background: var(--gw-bg-soft); }
401
+ .gw-btn--on-night { background: var(--gw-on-night); color: var(--gw-night); }
402
+ .gw-btn--on-night:hover { background: var(--gw-on-night); opacity: 0.88; }
403
+ .gw-btn--sm { height: 36px; padding: 0 16px; }
404
+ .gw-btn--sm::after { content: ""; position: absolute; top: -6px; right: 0; bottom: -6px; left: 0; }
405
+ .gw-btn--lg { height: 48px; padding: 0 24px; font-size: 16px; }
406
+ .gw-btn--block { display: flex; width: 100%; }
407
+ .gw-btn:disabled, .gw-btn[aria-disabled="true"] { opacity: 0.48; cursor: not-allowed; pointer-events: none; }
408
+ /* pressed: the ink one step darker (the hover ink, as the dashboard's primary), nothing moves; the frameless variants
409
+ take one more step of tint */
410
+ .gw-btn:active { background: var(--gw-action-hover); }
411
+ .gw-btn--secondary:active { background: var(--gw-bg-soft); border-color: var(--gw-ink); }
412
+ .gw-btn--ghost:active { background: var(--gw-line); }
413
+ .gw-btn--on-night:active { background: var(--gw-on-night); opacity: 0.76; }
414
+ /* loading: aria-busy="true" on the <button>. The label stays (it keeps the width and the accessible name) but is not
415
+ painted; a 16px ring in the button's own ink turns in its place, the dashboard's spinner. ::before, because
416
+ .gw-btn--sm draws its hit area with ::after. website.js ignores presses on a busy button (no double submit). */
417
+ .gw-btn[aria-busy="true"] { cursor: progress; -webkit-text-fill-color: transparent; }
418
+ .gw-btn[aria-busy="true"] svg { opacity: 0; }
419
+ .gw-btn[aria-busy="true"]::before {
420
+ content: "";
421
+ position: absolute;
422
+ top: 50%;
423
+ left: 50%;
424
+ width: 16px;
425
+ height: 16px;
426
+ margin: -8px 0 0 -8px;
427
+ border: 2px solid color-mix(in srgb, currentColor 18%, transparent);
428
+ border-top-color: currentColor;
429
+ border-radius: var(--gw-radius-chip);
430
+ animation: gw-spin var(--gw-spin-ms) linear infinite;
431
+ }
432
+ @keyframes gw-spin { to { transform: rotate(360deg); } }
433
+ /* reduced motion: the ring stands still as an ellipsis, three dots in the button's ink */
434
+ @media (prefers-reduced-motion: reduce) {
435
+ .gw-btn[aria-busy="true"]::before {
436
+ border: 0;
437
+ border-radius: 0;
438
+ background:
439
+ radial-gradient(circle at 3px 50%, currentColor 1.5px, transparent 2px),
440
+ radial-gradient(circle at 8px 50%, currentColor 1.5px, transparent 2px),
441
+ radial-gradient(circle at 13px 50%, currentColor 1.5px, transparent 2px);
442
+ animation: none;
443
+ }
444
+ }
445
+
446
+ /* inline text link with an arrow that moves 2px on hover */
447
+ .gw-link--arrow {
448
+ display: inline-flex;
449
+ align-items: center;
450
+ gap: 6px;
451
+ min-height: 24px;
452
+ font-weight: 600;
453
+ text-decoration: none;
454
+ }
455
+ .gw-link--arrow::after { content: "\2192"; transition: transform 0.18s var(--gw-ease); }
456
+ .gw-link--arrow:hover::after { transform: translateX(2px); }
457
+
458
+ /* chips are pills: a small selectable token, not an action */
459
+ .gw-chip {
460
+ display: inline-flex;
461
+ align-items: center;
462
+ gap: 6px;
463
+ min-height: 32px;
464
+ padding: 4px 12px;
465
+ border: 1px solid var(--gw-line-strong);
466
+ border-radius: var(--gw-radius-chip);
467
+ background: var(--gw-surface);
468
+ color: var(--gw-ink-2);
469
+ font-size: 13px;
470
+ font-weight: 500;
471
+ line-height: 1.4;
472
+ white-space: nowrap;
473
+ }
474
+ :is(a, button, label).gw-chip { cursor: pointer; }
475
+ :is(a, button, label).gw-chip:hover { border-color: var(--gw-ink); color: var(--gw-ink); }
476
+ .gw-chip.is-active, .gw-chip[aria-pressed="true"], .gw-chip[aria-selected="true"] {
477
+ border-color: var(--gw-accent);
478
+ background: var(--gw-accent-soft);
479
+ color: var(--gw-accent-strong);
480
+ }
481
+
482
+ /* segmented control: one choice out of two to four (Monthly / Yearly), a radio group of buttons. The dashboard's look
483
+ (@gemboss/ui .ui-seg): one hairline group, radius 8, a hairline between options, the chosen one on the accent tint,
484
+ so it follows a brand's accent. The weight never changes with the choice: the labels do not move. */
485
+ .gw-segmented {
486
+ display: inline-flex;
487
+ max-width: 100%;
488
+ border: 1px solid var(--gw-line-strong);
489
+ border-radius: var(--gw-radius-action);
490
+ background: var(--gw-surface);
491
+ overflow: hidden;
492
+ overflow: clip;
493
+ vertical-align: middle;
494
+ }
495
+ .gw-segmented--block { display: flex; width: 100%; }
496
+ .gw-segmented > button {
497
+ display: inline-flex;
498
+ flex: 0 1 auto;
499
+ align-items: center;
500
+ justify-content: center;
501
+ min-width: 0;
502
+ height: calc(var(--gw-button-height) - 2px);
503
+ padding: 0 16px;
504
+ border: 0;
505
+ background: transparent;
506
+ color: var(--gw-muted);
507
+ font-size: 14px;
508
+ font-weight: 500;
509
+ line-height: 1.4;
510
+ white-space: nowrap;
511
+ cursor: pointer;
512
+ }
513
+ .gw-segmented--block > button { flex: 1 1 0; }
514
+ .gw-segmented > button + button { border-left: 1px solid var(--gw-line-strong); }
515
+ .gw-segmented > button:hover { background: var(--gw-bg-soft); color: var(--gw-ink); }
516
+ .gw-segmented > button:active { background: var(--gw-line); }
517
+ /* inside the group: the group clips its corners, so the ring sits within the option */
518
+ .gw-segmented > button:focus-visible { outline-offset: -2px; }
519
+ .gw-segmented > button[aria-checked="true"] { background: var(--gw-accent-soft); color: var(--gw-accent-strong); }
520
+
521
+ /* badges: small status, pill */
522
+ .gw-badge {
523
+ display: inline-flex;
524
+ align-items: center;
525
+ gap: 6px;
526
+ min-height: 24px;
527
+ padding: 2px 8px;
528
+ border-radius: var(--gw-radius-chip);
529
+ background: color-mix(in srgb, var(--gw-ink) 8%, transparent);
530
+ color: var(--gw-ink-2);
531
+ font-size: 12px;
532
+ font-weight: 500;
533
+ line-height: 1.4;
534
+ white-space: nowrap;
535
+ vertical-align: middle;
536
+ }
537
+ .gw-badge--accent { background: var(--gw-accent-soft); color: var(--gw-accent-strong); }
538
+ /* state hues fail 4.5:1 as small text on a tint, so the state is a dot and the words stay ink */
539
+ .gw-badge--success { background: var(--gw-success-soft); color: var(--gw-ink); }
540
+ .gw-badge--success::before, .gw-badge--warning::before { content: ""; flex: none; width: 6px; height: 6px; border-radius: var(--gw-radius-chip); background: var(--gw-success); }
541
+ .gw-badge--warning::before { background: var(--gw-warning); }
542
+
543
+ /* cards sit: white, 1px hairline, radius 12, the dashboard's resting shadow (Chris 24/10/2026: one language with the
544
+ app); no hover lift. A soft card is a tint and carries no shadow. */
545
+ .gw-card {
546
+ padding: 24px;
547
+ border: 1px solid var(--gw-line);
548
+ border-radius: var(--gw-radius-card);
549
+ background: var(--gw-surface);
550
+ box-shadow: var(--gw-shadow);
551
+ }
552
+ @media (min-width: 768px) { .gw-card { padding: 32px; } }
553
+ .gw-card--soft { background: var(--gw-bg-soft); box-shadow: none; }
554
+ .gw-card--link { display: block; color: inherit; }
555
+ .gw-card--link:hover { border-color: var(--gw-line-strong); }
556
+ :where(.gw-card, .gw-feature, .gw-plan) > :is(h3, h4) + p { margin-top: 8px; }
557
+ :where(.gw-card, .gw-feature) h3 { font-size: 20px; }
558
+ :where(.gw-card, .gw-feature) p { font-size: 16px; color: var(--gw-muted); }
559
+
560
+ /* a framed screen: a browser or app window around a screenshot or a drawn mock-up */
561
+ .gw-window {
562
+ overflow: hidden;
563
+ border: 1px solid var(--gw-line);
564
+ border-radius: var(--gw-radius-card);
565
+ background: var(--gw-surface);
566
+ color: var(--gw-ink);
567
+ box-shadow: var(--gw-shadow);
568
+ font-size: 13px;
569
+ line-height: 1.5;
570
+ }
571
+ .gw-window__bar {
572
+ display: flex;
573
+ align-items: center;
574
+ gap: 12px;
575
+ min-height: 40px;
576
+ padding: 8px 16px;
577
+ border-bottom: 1px solid var(--gw-line);
578
+ color: var(--gw-muted);
579
+ font-size: 12px;
580
+ }
581
+ .gw-window__bar::before {
582
+ content: "";
583
+ flex: none;
584
+ width: 36px;
585
+ height: 8px;
586
+ background:
587
+ radial-gradient(circle at 4px 4px, var(--gw-line-strong) 4px, transparent 4px),
588
+ radial-gradient(circle at 18px 4px, var(--gw-line-strong) 4px, transparent 4px),
589
+ radial-gradient(circle at 32px 4px, var(--gw-line-strong) 4px, transparent 4px);
590
+ }
591
+ .gw-window__body { padding: 16px; }
592
+ .gw-window__body:has(> img:only-child) { padding: 0; }
593
+ .gw-window__body > img { width: 100%; }
594
+ /* what the numbers in the window are: "Illustrative data." under a drawn mock-up, or the source of real ones. The
595
+ last child of .gw-window, after __body (a <figcaption> when the window is a <figure>). */
596
+ .gw-window__caption {
597
+ margin: 0;
598
+ padding: 0 16px 12px;
599
+ color: var(--gw-muted);
600
+ font-size: 12px;
601
+ line-height: 1.5;
602
+ text-align: end;
603
+ }
604
+ .gw-window__body:has(> img:only-child) + .gw-window__caption { padding-top: 12px; }
605
+
606
+ /* icon: 20px inline stroke SVG (1.7 to 2px stroke, currentColor) */
607
+ .gw-icon { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 20px; height: 20px; }
608
+ .gw-icon svg { width: 20px; height: 20px; }
609
+
610
+ /* list with check marks */
611
+ .gw-list--check { display: grid; gap: 12px; padding: 0; list-style: none; }
612
+ .gw-list--check > li { position: relative; padding-left: 24px; }
613
+ .gw-list--check > li::before {
614
+ content: "";
615
+ position: absolute;
616
+ left: 0;
617
+ top: 6px;
618
+ top: calc((1lh - 16px) / 2);
619
+ width: 16px;
620
+ height: 16px;
621
+ background: var(--gw-accent);
622
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.4l2.9 2.8L12.5 5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
623
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.4l2.9 2.8L12.5 5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
624
+ }
625
+
626
+ /* table: the wrapper scrolls inside its own box on phones, the page never does */
627
+ .gw-table {
628
+ overflow-x: auto;
629
+ border: 1px solid var(--gw-line);
630
+ border-radius: var(--gw-radius-card);
631
+ background: var(--gw-surface);
632
+ -webkit-overflow-scrolling: touch;
633
+ }
634
+ .gw-table table { width: 100%; border-collapse: collapse; font-size: 14px; line-height: 1.5; }
635
+ .gw-table caption { padding: 16px; text-align: left; font-size: 13px; color: var(--gw-muted); }
636
+ .gw-table th, .gw-table td { padding: 12px 16px; border-bottom: 1px solid var(--gw-line); text-align: left; vertical-align: top; }
637
+ .gw-table thead th { background: var(--gw-bg-soft); color: var(--gw-ink); font-size: 13px; font-weight: 600; white-space: nowrap; }
638
+ .gw-table tbody th { font-weight: 500; }
639
+ .gw-table tbody tr:last-child > * { border-bottom: 0; }
640
+ .gw-table td { color: var(--gw-ink-2); font-variant-numeric: tabular-nums; }
641
+ .gw-table--compare table { min-width: 640px; }
642
+ .gw-table--compare td { text-align: center; }
643
+ .gw-table--compare thead th:not(:first-child) { text-align: center; }
644
+ .gw-table--compare tbody th { position: sticky; left: 0; z-index: 1; background: var(--gw-surface); min-width: 160px; }
645
+
646
+ /* FAQ: native details/summary, no script */
647
+ .gw-faq { display: grid; gap: 12px; }
648
+ .gw-faq__item {
649
+ padding: 0 24px;
650
+ border: 1px solid var(--gw-line);
651
+ border-radius: var(--gw-radius-card);
652
+ background: var(--gw-surface);
653
+ }
654
+ .gw-faq__item[open] { border-color: var(--gw-line-strong); }
655
+ .gw-faq__item > summary {
656
+ display: flex;
657
+ align-items: center;
658
+ justify-content: space-between;
659
+ gap: 16px;
660
+ min-height: 44px;
661
+ padding: 20px 0;
662
+ font-size: 18px;
663
+ font-weight: 600;
664
+ line-height: 1.4;
665
+ list-style: none;
666
+ cursor: pointer;
667
+ }
668
+ .gw-faq__item > summary::-webkit-details-marker { display: none; }
669
+ .gw-faq__item > summary::after {
670
+ content: "";
671
+ flex: none;
672
+ width: 16px;
673
+ height: 16px;
674
+ background:
675
+ linear-gradient(var(--gw-accent), var(--gw-accent)) center / 16px 2px no-repeat,
676
+ linear-gradient(var(--gw-accent), var(--gw-accent)) center / 2px 16px no-repeat;
677
+ transition: transform 0.2s var(--gw-ease);
678
+ }
679
+ .gw-faq__item[open] > summary::after { transform: rotate(45deg); }
680
+ .gw-faq__item > :not(summary) { margin: 0 0 20px; max-width: 68ch; color: var(--gw-muted); font-size: 16px; }
681
+
682
+ /* touch: every control is at least 44px tall, labels may wrap */
683
+ @media (max-width: 768px), (pointer: coarse) {
684
+ .gw-btn, .gw-btn--sm, .gw-btn--lg {
685
+ height: auto;
686
+ min-height: var(--gw-tap-min);
687
+ padding-block: 8px;
688
+ white-space: normal;
689
+ text-wrap: balance;
690
+ }
691
+ .gw-btn--lg { min-height: 48px; }
692
+ :is(a, button, label).gw-chip { min-height: var(--gw-tap-min); }
693
+ .gw-segmented > button { height: auto; min-height: var(--gw-tap-min); padding-block: 8px; white-space: normal; text-wrap: balance; }
694
+ }
695
+
696
+ /* header: announcement, a flat 64px bar with one hairline, mega menu, mobile menu below 1080px.
697
+ Not a floating pill: no radius, no blur, no shadow on the bar. Nothing tucks under it. */
698
+ .gw-announce {
699
+ padding: 8px var(--gw-gutter);
700
+ border-bottom: 1px solid var(--gw-line);
701
+ background: var(--gw-bg-soft);
702
+ color: var(--gw-ink-2);
703
+ font-size: 13px;
704
+ line-height: 1.5;
705
+ text-align: center;
706
+ }
707
+ .gw-announce a { color: var(--gw-ink); font-weight: 600; text-decoration: underline; text-underline-offset: 4px; }
708
+
709
+ .gw-header {
710
+ position: sticky;
711
+ top: 0;
712
+ z-index: 50;
713
+ border-bottom: 1px solid var(--gw-line);
714
+ background: var(--gw-surface);
715
+ }
716
+ .gw-header.is-scrolled { border-bottom-color: var(--gw-line-strong); }
717
+ .gw-header__bar { display: flex; align-items: center; gap: 16px; height: var(--gw-header-height); }
718
+
719
+ .gw-brand {
720
+ display: inline-flex;
721
+ align-items: center;
722
+ gap: 8px;
723
+ flex: none;
724
+ min-height: 44px;
725
+ color: var(--gw-ink);
726
+ font-size: 18px;
727
+ font-weight: 700;
728
+ letter-spacing: -0.01em;
729
+ white-space: nowrap;
730
+ }
731
+ .gw-brand__mark { flex: none; width: 26px; height: 26px; }
732
+ .gw-brand__name { line-height: 1.2; }
733
+
734
+ /* desktop nav */
735
+ .gw-nav { display: none; }
736
+ .gw-nav__list { display: flex; align-items: center; gap: 4px; margin: 0; padding: 0; list-style: none; }
737
+ .gw-nav__trigger,
738
+ .gw-nav__link {
739
+ display: inline-flex;
740
+ align-items: center;
741
+ gap: 6px;
742
+ height: 40px;
743
+ padding: 0 12px;
744
+ border: 0;
745
+ border-radius: var(--gw-radius-action);
746
+ background: transparent;
747
+ color: var(--gw-ink-2);
748
+ font-size: 14px;
749
+ font-weight: 500;
750
+ line-height: 1.4;
751
+ white-space: nowrap;
752
+ cursor: pointer;
753
+ }
754
+ .gw-nav__trigger:hover,
755
+ .gw-nav__link:hover,
756
+ .gw-nav__item.is-open > .gw-nav__trigger { background: var(--gw-bg-soft); color: var(--gw-ink); }
757
+ /* the chevron: the site's own 24px path, drawn as a mask so it takes currentColor */
758
+ .gw-nav__trigger::after,
759
+ .gw-mobile-menu__group > summary::after {
760
+ content: "";
761
+ flex: none;
762
+ width: 12px;
763
+ height: 12px;
764
+ background: currentColor;
765
+ opacity: 0.6;
766
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 9l6 6 6-6' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
767
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 9l6 6 6-6' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
768
+ transition: transform 0.18s var(--gw-ease);
769
+ }
770
+ .gw-nav__trigger[aria-expanded="true"]::after,
771
+ .gw-mobile-menu__group[open] > summary::after { transform: rotate(180deg); }
772
+ .gw-nav__link[aria-current="page"] {
773
+ color: var(--gw-ink);
774
+ font-weight: 600;
775
+ text-decoration: underline;
776
+ text-decoration-color: var(--gw-accent);
777
+ text-decoration-thickness: 2px;
778
+ text-underline-offset: 8px;
779
+ }
780
+
781
+ /* mega menu: a floating card centred under the bar, as on gemboss.ai (Chris 24/10/2026): one hairline, radius 20,
782
+ the raised shadow (it is a popover), 32px inside. The bridge (::before) keeps the pointer inside while it crosses
783
+ the 8px gap from the trigger. */
784
+ .gw-mega {
785
+ position: absolute;
786
+ top: calc(100% + 8px);
787
+ left: 50%;
788
+ width: min(960px, calc(100vw - 40px));
789
+ max-height: calc(100vh - var(--gw-header-height) - 24px);
790
+ overflow-y: auto;
791
+ transform: translateX(-50%);
792
+ border: 1px solid var(--gw-line);
793
+ border-radius: 20px;
794
+ background: var(--gw-surface);
795
+ box-shadow: var(--gw-shadow-raised);
796
+ }
797
+ .gw-mega::before { content: ""; position: absolute; top: -12px; left: 0; right: 0; height: 12px; }
798
+ .gw-mega:not([hidden]) { animation: gw-mega-in 0.16s var(--gw-ease); }
799
+ @keyframes gw-mega-in { from { opacity: 0; transform: translateX(-50%) translateY(8px); } }
800
+ @media (prefers-reduced-motion: reduce) { .gw-mega:not([hidden]) { animation: none; } }
801
+ .gw-mega > .gw-wrap { width: auto; margin: 0; }
802
+ .gw-mega__grid { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 32px; padding: 32px; }
803
+ .gw-mega__col { display: flex; flex-direction: column; gap: 2px; }
804
+ .gw-mega__heading { padding: 0 8px 8px; color: var(--gw-muted); font-size: 12px; font-weight: 500; }
805
+ .gw-mega__link {
806
+ display: grid;
807
+ grid-template-columns: 32px minmax(0, 1fr);
808
+ align-items: start;
809
+ gap: 12px;
810
+ padding: 8px;
811
+ border-radius: var(--gw-radius-action);
812
+ color: var(--gw-ink);
813
+ }
814
+ .gw-mega__link:hover { background: var(--gw-bg-soft); }
815
+ .gw-mega__link b { display: block; font-size: 14px; font-weight: 500; line-height: 20px; }
816
+ .gw-mega__link i { display: block; margin-top: 2px; color: var(--gw-muted); font-size: 13px; font-style: normal; line-height: 18px; }
817
+ .gw-mega__link[aria-current="page"] b { color: var(--gw-accent-strong); font-weight: 600; }
818
+ .gw-mega__icon {
819
+ display: grid;
820
+ place-items: center;
821
+ width: 32px;
822
+ height: 32px;
823
+ border: 1px solid var(--gw-line);
824
+ border-radius: var(--gw-radius-action);
825
+ background: var(--gw-surface);
826
+ color: var(--gw-muted);
827
+ }
828
+ .gw-mega__icon svg { width: 16px; height: 16px; }
829
+ .gw-mega__link:hover .gw-mega__icon { border-color: var(--gw-accent-soft); background: var(--gw-accent-soft); color: var(--gw-accent-strong); }
830
+ /* the feature card is the Stage (the house illustration recipe, Chris 22/10/2026): a night ground lit with the accent
831
+ from two corners, white words. An illustration may light itself, so its glows are gradients, not chrome shadows. */
832
+ .gw-mega__feature {
833
+ display: flex;
834
+ flex-direction: column;
835
+ justify-content: flex-end;
836
+ gap: 4px;
837
+ min-height: 216px;
838
+ padding: 24px;
839
+ border-radius: var(--gw-radius-panel);
840
+ background:
841
+ radial-gradient(360px 260px at 12% 112%, color-mix(in srgb, var(--gw-accent) 78%, transparent), transparent 62%),
842
+ radial-gradient(300px 220px at 92% -14%, color-mix(in srgb, var(--gw-wash-violet) 44%, transparent), transparent 70%),
843
+ var(--gw-night);
844
+ color: var(--gw-on-night);
845
+ }
846
+ .gw-mega__feature b { font-size: 18px; font-weight: 650; line-height: 1.25; }
847
+ .gw-mega__feature i { color: var(--gw-on-night); font-size: 13px; font-style: normal; line-height: 1.5; opacity: 0.9; }
848
+ .gw-mega__feature .gw-link--arrow { margin-top: 16px; color: var(--gw-on-night); font-size: 13px; font-weight: 600; }
849
+
850
+ /* actions on the right */
851
+ .gw-header__actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
852
+ .gw-header__signin { display: none; }
853
+ .gw-header__actions > .gw-btn { display: none; }
854
+ .gw-theme-toggle,
855
+ .gw-menu-toggle {
856
+ display: inline-flex;
857
+ align-items: center;
858
+ justify-content: center;
859
+ flex: none;
860
+ width: 40px;
861
+ height: 40px;
862
+ padding: 0;
863
+ border: 0;
864
+ border-radius: var(--gw-radius-action);
865
+ background: transparent;
866
+ color: var(--gw-ink);
867
+ }
868
+ .gw-theme-toggle:hover,
869
+ .gw-menu-toggle:hover { background: var(--gw-bg-soft); }
870
+ .gw-theme-toggle svg,
871
+ .gw-menu-toggle svg { width: 20px; height: 20px; }
872
+ .gw-menu-toggle[aria-expanded="true"] .gw-menu-toggle__open,
873
+ .gw-menu-toggle:not([aria-expanded="true"]) .gw-menu-toggle__close { display: none; }
874
+
875
+ /* mobile menu: a full-height sheet under the bar; html.is-menu-open locks the page */
876
+ .gw-mobile-menu {
877
+ position: fixed;
878
+ top: calc(var(--gw-header-height) + 1px);
879
+ left: 0;
880
+ right: 0;
881
+ bottom: 0;
882
+ overflow-y: auto;
883
+ overscroll-behavior: contain;
884
+ padding-block: 8px 32px;
885
+ background: var(--gw-bg);
886
+ }
887
+ html.is-menu-open { overflow: hidden; }
888
+ html.is-menu-open .gw-header { position: fixed; top: 0; left: 0; right: 0; }
889
+ .gw-mobile-menu__group > summary,
890
+ .gw-mobile-menu__top {
891
+ display: flex;
892
+ align-items: center;
893
+ justify-content: space-between;
894
+ min-height: 48px;
895
+ padding-block: 8px;
896
+ border-bottom: 1px solid var(--gw-line);
897
+ color: var(--gw-ink);
898
+ font-size: 18px;
899
+ font-weight: 500;
900
+ list-style: none;
901
+ cursor: pointer;
902
+ }
903
+ .gw-mobile-menu__group > summary::-webkit-details-marker { display: none; }
904
+ .gw-mobile-menu__list { margin: 0; padding: 8px 0 16px; list-style: none; }
905
+ .gw-mobile-menu__link { display: flex; align-items: center; min-height: 44px; color: var(--gw-ink-2); font-size: 16px; }
906
+ .gw-mobile-menu__link[aria-current="page"],
907
+ .gw-mobile-menu__top[aria-current="page"] { color: var(--gw-accent-strong); font-weight: 600; }
908
+ .gw-mobile-menu__actions { display: grid; gap: 8px; margin-top: 24px; }
909
+
910
+ @media (min-width: 480px) {
911
+ .gw-header__actions > .gw-btn:not(.gw-btn--secondary) { display: inline-flex; }
912
+ }
913
+ @media (min-width: 768px) {
914
+ .gw-header__actions > .gw-btn--secondary { display: inline-flex; }
915
+ }
916
+ @media (min-width: 1080px) {
917
+ .gw-header__bar { gap: 32px; }
918
+ .gw-nav { display: block; }
919
+ .gw-header__signin { display: inline-flex; }
920
+ .gw-menu-toggle, .gw-mobile-menu { display: none !important; }
921
+ html.is-menu-open { overflow: visible; }
922
+ html.is-menu-open .gw-header { position: sticky; }
923
+ }
924
+ @media (max-width: 768px), (pointer: coarse) {
925
+ .gw-theme-toggle, .gw-menu-toggle { width: var(--gw-tap-min); height: var(--gw-tap-min); }
926
+ }
927
+
928
+ /* footer: brand block, link columns, bottom row. One hairline on top, white like the page. */
929
+ .gw-footer {
930
+ padding-block: 64px 40px;
931
+ border-top: 1px solid var(--gw-line);
932
+ background: var(--gw-bg);
933
+ color: var(--gw-ink);
934
+ font-size: 14px;
935
+ }
936
+ .gw-footer__grid {
937
+ display: grid;
938
+ grid-template-columns: repeat(2, minmax(0, 1fr));
939
+ gap: 32px 24px;
940
+ }
941
+ .gw-footer__brand { grid-column: 1 / -1; max-width: 320px; }
942
+ .gw-footer__brand p { margin-top: 16px; color: var(--gw-muted); }
943
+ .gw-footer__heading { margin: 0 0 8px; color: var(--gw-ink); font-size: 13px; font-weight: 600; letter-spacing: 0; }
944
+ .gw-footer__col ul { margin: 0; padding: 0; list-style: none; }
945
+ .gw-footer__col a { display: block; padding-block: 6px; color: var(--gw-muted); }
946
+ .gw-footer__col a:hover { color: var(--gw-ink); }
947
+ .gw-footer__col a[aria-current="page"] { color: var(--gw-accent-strong); }
948
+ .gw-footer__bottom {
949
+ display: flex;
950
+ flex-wrap: wrap;
951
+ align-items: center;
952
+ justify-content: space-between;
953
+ gap: 8px 24px;
954
+ margin-top: 48px;
955
+ padding-top: 24px;
956
+ border-top: 1px solid var(--gw-line);
957
+ color: var(--gw-muted);
958
+ font-size: 13px;
959
+ }
960
+ .gw-footer__bottom:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
961
+ .gw-footer__bottom ul { display: flex; flex-wrap: wrap; gap: 0 16px; margin: 0; padding: 0; list-style: none; }
962
+ .gw-footer__bottom a {
963
+ display: inline-flex;
964
+ align-items: center;
965
+ justify-content: center;
966
+ min-width: 24px;
967
+ min-height: 24px;
968
+ color: var(--gw-muted);
969
+ }
970
+ .gw-footer__bottom a:hover { color: var(--gw-ink); }
971
+ @media (min-width: 768px) {
972
+ .gw-footer__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
973
+ }
974
+ @media (min-width: 1080px) {
975
+ .gw-footer__grid { grid-auto-flow: column; grid-template-columns: minmax(0, 1.6fr); grid-auto-columns: minmax(0, 1fr); }
976
+ .gw-footer__brand { grid-column: auto; }
977
+ }
978
+ @media (max-width: 768px), (pointer: coarse) {
979
+ .gw-footer__bottom a { min-height: var(--gw-tap-min); }
980
+ }
981
+
982
+ /* sections: hero, logos, stats, features, split, showcase, steps, quote, pricing, CTA. */
983
+
984
+ /* hero: the first screen. __wash is the one place the wash hues may appear. */
985
+ .gw-hero { position: relative; isolation: isolate; padding-block: 48px 64px; }
986
+ .gw-hero__wash {
987
+ position: absolute;
988
+ inset: 0;
989
+ z-index: -1;
990
+ pointer-events: none;
991
+ background:
992
+ radial-gradient(640px 460px at 88% -10%, color-mix(in srgb, var(--gw-accent) 16%, transparent), transparent 62%),
993
+ radial-gradient(560px 480px at 6% 108%, color-mix(in srgb, var(--gw-wash-violet) 12%, transparent), transparent 62%),
994
+ radial-gradient(420px 320px at 52% 118%, color-mix(in srgb, var(--gw-wash-mint) 10%, transparent), transparent 62%);
995
+ }
996
+ .gw-hero h1 .gw-dim { display: block; }
997
+ .gw-hero > .gw-wrap > .gw-window { margin-top: 48px; }
998
+ .gw-hero--center { text-align: center; }
999
+ .gw-hero--center > .gw-wrap > * { margin-inline: auto; }
1000
+ .gw-hero--center .gw-actions { justify-content: center; }
1001
+ .gw-hero--compact { padding-block: 40px 48px; }
1002
+ .gw-hero--compact h1 { font-size: var(--gw-display-h2); }
1003
+ @media (min-width: 768px) {
1004
+ .gw-hero { padding-block: 80px 96px; }
1005
+ .gw-hero--compact { padding-block: 64px; }
1006
+ .gw-hero .gw-lead { font-size: 20px; }
1007
+ .gw-hero > .gw-wrap > .gw-window { margin-top: 64px; }
1008
+ }
1009
+ @media (max-width: 479px) {
1010
+ :is(.gw-hero, .gw-cta) .gw-actions > .gw-btn { flex: 1 1 100%; }
1011
+ }
1012
+
1013
+ /* logos: a proof strip of real logos or wordmarks */
1014
+ .gw-logos { padding-block: 40px; border-block: 1px solid var(--gw-line); }
1015
+ .gw-logos p { color: var(--gw-muted); font-size: 13px; text-align: center; }
1016
+ .gw-logos ul {
1017
+ display: flex;
1018
+ flex-wrap: wrap;
1019
+ align-items: center;
1020
+ justify-content: center;
1021
+ gap: 16px 40px;
1022
+ margin-top: 20px;
1023
+ padding: 0;
1024
+ list-style: none;
1025
+ }
1026
+ .gw-logos li { color: var(--gw-muted); font-size: 22px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.2; }
1027
+ .gw-logos img { width: auto; height: 28px; }
1028
+
1029
+ /* stats: three or four facts, each with its source */
1030
+ .gw-stats { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; margin: 0; padding: 0; list-style: none; }
1031
+ .gw-stats__value { font-size: var(--gw-display-h2); font-weight: 600; letter-spacing: -0.03em; line-height: 1.1; font-variant-numeric: tabular-nums; }
1032
+ .gw-stats__label { margin-top: 8px; max-width: 28ch; color: var(--gw-muted); font-size: 14px; }
1033
+ .gw-stats__source { margin-top: 8px; color: var(--gw-muted); font-size: 12px; }
1034
+ @media (min-width: 480px) { .gw-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
1035
+ @media (min-width: 1080px) {
1036
+ .gw-stats { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 0; }
1037
+ .gw-stats__item { padding: 4px 24px; border-left: 1px solid var(--gw-line); }
1038
+ .gw-stats__item:first-child { padding-left: 0; border-left: 0; }
1039
+ }
1040
+
1041
+ /* features: icon, title, one or two lines (inside .gw-grid--2|3|4) */
1042
+ .gw-features { row-gap: 40px; }
1043
+ .gw-feature .gw-icon { width: 40px; height: 40px; border: 1px solid var(--gw-line); border-radius: var(--gw-radius-action); background: var(--gw-surface); color: var(--gw-accent); }
1044
+
1045
+ /* split: text beside a figure; stacks on phones, text first */
1046
+ .gw-split { display: grid; grid-template-columns: minmax(0, 1fr); align-items: center; gap: 40px; }
1047
+ @media (min-width: 768px) {
1048
+ .gw-split { grid-template-columns: repeat(2, minmax(0, 1fr)); }
1049
+ .gw-split--reverse > :first-child { order: 2; }
1050
+ }
1051
+ .gw-split h2 { font-size: 30px; }
1052
+ @media (min-width: 1080px) { .gw-split { gap: 64px; } }
1053
+
1054
+ /* showcase: a night band with a large screen and two or three points */
1055
+ .gw-showcase .gw-window { max-width: 960px; margin-inline: auto; }
1056
+ .gw-showcase .gw-window + * { margin-top: 48px; }
1057
+
1058
+ /* steps: numbered, how it works */
1059
+ .gw-steps {
1060
+ display: grid;
1061
+ grid-template-columns: minmax(0, 1fr);
1062
+ gap: 32px 24px;
1063
+ margin: 0;
1064
+ padding: 0;
1065
+ list-style: none;
1066
+ counter-reset: gw-step;
1067
+ }
1068
+ .gw-steps > li { counter-increment: gw-step; }
1069
+ .gw-steps > li::before {
1070
+ content: counter(gw-step, decimal-leading-zero);
1071
+ display: block;
1072
+ margin-bottom: 16px;
1073
+ padding-bottom: 12px;
1074
+ border-bottom: 1px solid var(--gw-line);
1075
+ color: var(--gw-accent-strong);
1076
+ font-size: 13px;
1077
+ font-weight: 600;
1078
+ font-variant-numeric: tabular-nums;
1079
+ }
1080
+ .gw-steps h3 { font-size: 20px; }
1081
+ .gw-steps p { margin-top: 8px; color: var(--gw-muted); font-size: 16px; }
1082
+ @media (min-width: 768px) { .gw-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
1083
+ @media (min-width: 1080px) { .gw-steps { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); } }
1084
+
1085
+ /* quote: one real testimonial, with name, role and company */
1086
+ .gw-quote {
1087
+ max-width: 880px;
1088
+ margin-inline: auto;
1089
+ padding: 40px 24px;
1090
+ border: 1px solid var(--gw-line);
1091
+ border-radius: var(--gw-radius-panel);
1092
+ background: var(--gw-surface);
1093
+ text-align: center;
1094
+ }
1095
+ .gw-quote::before {
1096
+ content: "";
1097
+ display: block;
1098
+ width: 32px;
1099
+ height: 24px;
1100
+ margin: 0 auto 24px;
1101
+ background: var(--gw-accent);
1102
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 24'%3E%3Cpath d='M0 24V14C0 6 4 1 12 0l1 4c-4 1-6 4-6 8h5v12zm19 0V14c0-8 4-13 12-14l1 4c-4 1-6 4-6 8h5v12z'/%3E%3C/svg%3E") center / contain no-repeat;
1103
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 24'%3E%3Cpath d='M0 24V14C0 6 4 1 12 0l1 4c-4 1-6 4-6 8h5v12zm19 0V14c0-8 4-13 12-14l1 4c-4 1-6 4-6 8h5v12z'/%3E%3C/svg%3E") center / contain no-repeat;
1104
+ }
1105
+ .gw-quote blockquote { font-size: 24px; font-weight: 500; line-height: 1.4; letter-spacing: -0.01em; text-wrap: balance; }
1106
+ .gw-quote figcaption { margin-top: 24px; color: var(--gw-muted); font-size: 14px; }
1107
+ .gw-quote figcaption b { color: var(--gw-ink); }
1108
+ .gw-quote figcaption img { width: 40px; height: 40px; margin: 0 auto 12px; border-radius: var(--gw-radius-chip); }
1109
+ @media (min-width: 768px) {
1110
+ .gw-quote { padding: 64px; }
1111
+ .gw-quote blockquote { font-size: 30px; }
1112
+ }
1113
+
1114
+ /* pricing: plan cards; the featured one has a hairline in the accent, never a fill */
1115
+ .gw-pricing { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); align-items: start; gap: 16px; }
1116
+ .gw-plan {
1117
+ display: flex;
1118
+ flex-direction: column;
1119
+ padding: 24px;
1120
+ border: 1px solid var(--gw-line);
1121
+ border-radius: var(--gw-radius-panel);
1122
+ background: var(--gw-surface);
1123
+ box-shadow: var(--gw-shadow);
1124
+ }
1125
+ .gw-plan--featured { border-color: var(--gw-accent); }
1126
+ .gw-plan { position: relative; }
1127
+ .gw-plan > .gw-badge { align-self: flex-start; }
1128
+ .gw-plan > .gw-badge:first-child { position: absolute; top: -12px; left: 24px; }
1129
+ .gw-plan > .gw-badge:first-child + * { margin-top: 0; }
1130
+ .gw-plan h3 { font-size: 20px; }
1131
+ :where(.gw-plan) p { color: var(--gw-muted); font-size: 14px; }
1132
+ .gw-plan__price {
1133
+ display: flex;
1134
+ flex-wrap: wrap;
1135
+ align-items: baseline;
1136
+ gap: 6px;
1137
+ color: var(--gw-ink);
1138
+ font-size: var(--gw-display-h2);
1139
+ font-weight: 600;
1140
+ letter-spacing: -0.03em;
1141
+ line-height: 1.1;
1142
+ font-variant-numeric: tabular-nums;
1143
+ }
1144
+ .gw-plan__price small { color: var(--gw-muted); font-size: 14px; font-weight: 500; letter-spacing: 0; }
1145
+ .gw-plan .gw-list--check { font-size: 14px; }
1146
+ @media (min-width: 768px) {
1147
+ .gw-plan { padding: 32px; }
1148
+ .gw-plan > .gw-badge:first-child { left: 32px; }
1149
+ }
1150
+
1151
+ /* CTA: the closing call to action, centred; put it in a night band or a soft section */
1152
+ .gw-cta { max-width: 780px; margin-inline: auto; text-align: center; }
1153
+ .gw-cta .gw-lead { margin-inline: auto; }
1154
+ .gw-cta .gw-actions { justify-content: center; }
1155
+
1156
+ /* prose: long-form reading (a blog post, a docs page, terms and privacy) inside one wrapper, .gw-prose. The plain HTML a
1157
+ CMS or a markdown renderer writes is styled as it comes: headings, paragraphs, lists, quotes, code, tables, images
1158
+ and captions, on the kit's scales. The rules sit at :where() weight and reach bare elements only (lists without a
1159
+ class, the wrapper's own headings, quotes and tables), so a kit component placed inside the prose (.gw-table,
1160
+ .gw-window, .gw-list--check, .gw-quote, .gw-btn) keeps its own look. --sm: a 16px body for docs and legal. */
1161
+ .gw-prose {
1162
+ max-width: 68ch;
1163
+ min-width: 0;
1164
+ color: var(--gw-ink);
1165
+ font-size: var(--gw-body-size);
1166
+ line-height: var(--gw-body-leading);
1167
+ overflow-wrap: break-word;
1168
+ }
1169
+ .gw-prose--sm { font-size: 16px; }
1170
+
1171
+ /* the flow: each block 20px below the one before it (16px in --sm); more air around headings and large blocks */
1172
+ :where(.gw-prose) > * + * { margin-top: 20px; }
1173
+ :where(.gw-prose--sm) > * + * { margin-top: 16px; }
1174
+ :where(.gw-prose) > * + :where(figure, img, video, pre, table, hr, .gw-table, .gw-window),
1175
+ :where(.gw-prose) > :where(figure, img, video, pre, table, hr, .gw-table, .gw-window) + * { margin-top: 32px; }
1176
+ :where(.gw-prose) > * + h2 { margin-top: 48px; }
1177
+ :where(.gw-prose) > * + h3 { margin-top: 40px; }
1178
+ :where(.gw-prose) > * + h4 { margin-top: 32px; }
1179
+ :where(.gw-prose--sm) > * + h2 { margin-top: 40px; }
1180
+ :where(.gw-prose--sm) > * + h3 { margin-top: 32px; }
1181
+ :where(.gw-prose--sm) > * + h4 { margin-top: 24px; }
1182
+ :where(.gw-prose) > h1 + * { margin-top: 24px; }
1183
+ :where(.gw-prose) > :is(h2, h3, h4) + * { margin-top: 12px; }
1184
+
1185
+ /* headings: the article's own steps of the type scale, not the page's display sizes (an h2 in a post is not a
1186
+ section headline); h1 is the article's title at the section-headline size */
1187
+ :where(.gw-prose) > :is(h1, h2, h3, h4) {
1188
+ color: var(--gw-ink);
1189
+ font-weight: var(--gw-heading-weight);
1190
+ letter-spacing: var(--gw-heading-tracking);
1191
+ line-height: 1.25;
1192
+ }
1193
+ :where(.gw-prose) > h1 { font-size: var(--gw-display-h2); font-weight: var(--gw-display-weight); letter-spacing: var(--gw-display-tracking); line-height: var(--gw-heading-leading); }
1194
+ :where(.gw-prose) > h2 { font-size: 30px; line-height: 1.2; }
1195
+ :where(.gw-prose) > h3 { font-size: 24px; }
1196
+ :where(.gw-prose) > h4 { font-size: 20px; }
1197
+ :where(.gw-prose--sm) > h2 { font-size: 24px; }
1198
+ :where(.gw-prose--sm) > h3 { font-size: 20px; }
1199
+ :where(.gw-prose--sm) > h4 { font-size: 18px; }
1200
+
1201
+ /* lists without a class: plain bullets and numbers, the markers muted */
1202
+ :where(.gw-prose :is(ul, ol):not([class])) { padding-left: 24px; }
1203
+ :where(.gw-prose ul:not([class])) { list-style: disc; }
1204
+ :where(.gw-prose ol:not([class])) { list-style: decimal; }
1205
+ :where(.gw-prose :is(ul, ol):not([class]) > li + li),
1206
+ :where(.gw-prose :is(ul, ol):not([class]) > li > * + *),
1207
+ :where(.gw-prose :is(ul, ol):not([class]) > li > :is(ul, ol)) { margin-top: 8px; }
1208
+ :where(.gw-prose :is(ul, ol):not([class]) > li)::marker { color: var(--gw-muted); font-variant-numeric: tabular-nums; }
1209
+
1210
+ /* a quotation: a rule on its left and muted words, as the dashboard's reader draws it */
1211
+ :where(.gw-prose) > blockquote { padding-left: 20px; border-left: 3px solid var(--gw-line-strong); color: var(--gw-muted); }
1212
+ :where(.gw-prose) > blockquote > * + * { margin-top: 12px; }
1213
+
1214
+ :where(.gw-prose) > hr { height: 0; border: 0; border-top: 1px solid var(--gw-line); }
1215
+ :where(.gw-prose) :is(sup, sub) { line-height: 0; }
1216
+
1217
+ /* code: inline, a tint chip that wraps on a phone; a block scrolls inside its own box */
1218
+ :where(.gw-prose) :is(pre, code, kbd, samp) { font-family: var(--gw-font-mono); }
1219
+ :where(.gw-prose) code {
1220
+ padding: 2px 6px;
1221
+ border-radius: var(--gw-radius-6);
1222
+ background: color-mix(in srgb, var(--gw-ink) 8%, transparent);
1223
+ color: var(--gw-ink);
1224
+ font-size: 16px;
1225
+ overflow-wrap: anywhere;
1226
+ -webkit-box-decoration-break: clone;
1227
+ box-decoration-break: clone;
1228
+ }
1229
+ :where(.gw-prose--sm) code { font-size: 14px; }
1230
+ :where(.gw-prose) :is(h1, h2, h3, h4) code { font-size: inherit; }
1231
+ :where(.gw-prose) figcaption code { font-size: 13px; }
1232
+ /* in a table a name stays whole: the table scrolls instead */
1233
+ :where(.gw-prose) :is(th, td) code { font-size: 13px; overflow-wrap: normal; white-space: nowrap; }
1234
+ :where(.gw-prose) pre {
1235
+ overflow-x: auto;
1236
+ padding: 16px 20px;
1237
+ border: 1px solid var(--gw-line);
1238
+ border-radius: var(--gw-radius-card);
1239
+ background: var(--gw-bg-soft);
1240
+ color: var(--gw-ink);
1241
+ font-size: 14px;
1242
+ line-height: 1.6;
1243
+ tab-size: 2;
1244
+ }
1245
+ :where(.gw-prose) pre code { padding: 0; border-radius: 0; background: none; font-size: inherit; overflow-wrap: normal; }
1246
+
1247
+ /* images and captions (an image inside a .gw-window keeps the window's square edges) */
1248
+ :where(.gw-prose) > :is(img, video),
1249
+ :where(.gw-prose) > :is(p, figure) > :is(img, video) { border-radius: var(--gw-radius-card); }
1250
+ :where(.gw-prose) figcaption { margin-top: 12px; color: var(--gw-muted); font-size: 14px; line-height: 1.5; }
1251
+
1252
+ /* a table straight from markdown scrolls inside its own box, never the page. When you write the markup, the .gw-table
1253
+ wrapper (a region with a name and tabindex="0") is the better choice. */
1254
+ :where(.gw-prose) > table {
1255
+ display: block;
1256
+ width: max-content;
1257
+ max-width: 100%;
1258
+ overflow-x: auto;
1259
+ border: 1px solid var(--gw-line);
1260
+ border-radius: var(--gw-radius-card);
1261
+ background: var(--gw-surface);
1262
+ border-collapse: collapse;
1263
+ font-size: 14px;
1264
+ line-height: 1.5;
1265
+ -webkit-overflow-scrolling: touch;
1266
+ }
1267
+ :where(.gw-prose) > table :is(th, td) { padding: 12px 16px; border-bottom: 1px solid var(--gw-line); text-align: left; vertical-align: top; }
1268
+ :where(.gw-prose) > table thead th { background: var(--gw-bg-soft); color: var(--gw-ink); font-size: 13px; font-weight: 600; white-space: nowrap; }
1269
+ :where(.gw-prose) > table tbody th { font-weight: 500; }
1270
+ :where(.gw-prose) > table td { color: var(--gw-ink-2); font-variant-numeric: tabular-nums; }
1271
+ :where(.gw-prose) > table tbody tr:last-child > * { border-bottom: 0; }
1272
+ :where(.gw-prose) > table caption { padding: 12px 16px; color: var(--gw-muted); font-size: 13px; text-align: left; }
1273
+ /* on a phone a cell keeps room for a few words a line: the table scrolls further instead of squeezing its text */
1274
+ @media (max-width: 767px) { :where(.gw-prose) > table td { min-width: 160px; } }