@guard-rail/core 0.9.1 → 0.11.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 (74) hide show
  1. package/README.md +253 -36
  2. package/dist/adapters/supabase/auth.js +1 -2
  3. package/dist/adapters/supabase/auth.js.map +1 -1
  4. package/dist/adapters/supabase/client.js +6 -1
  5. package/dist/adapters/supabase/client.js.map +1 -1
  6. package/dist/adapters/supabase/confirm.d.ts +11 -11
  7. package/dist/adapters/supabase/confirm.js +14 -18
  8. package/dist/adapters/supabase/confirm.js.map +1 -1
  9. package/dist/adapters/supabase/index.d.ts +3 -1
  10. package/dist/adapters/supabase/index.js +5 -0
  11. package/dist/adapters/supabase/index.js.map +1 -1
  12. package/dist/adapters/supabase/rateLimit.d.ts +24 -0
  13. package/dist/adapters/supabase/rateLimit.js +37 -0
  14. package/dist/adapters/supabase/rateLimit.js.map +1 -0
  15. package/dist/cli/doctor.d.ts +1 -0
  16. package/dist/cli/doctor.js +1046 -0
  17. package/dist/cli/doctor.js.map +1 -0
  18. package/dist/cli/init-db.js +23 -5
  19. package/dist/cli/init-db.js.map +1 -1
  20. package/dist/core/adapter-types.d.ts +71 -1
  21. package/dist/core/config.d.ts +12 -1
  22. package/dist/core/config.js +29 -1
  23. package/dist/core/config.js.map +1 -1
  24. package/dist/core/csp.d.ts +32 -0
  25. package/dist/core/csp.js +26 -0
  26. package/dist/core/csp.js.map +1 -0
  27. package/dist/core/validate.d.ts +47 -0
  28. package/dist/core/validate.js +118 -0
  29. package/dist/core/validate.js.map +1 -0
  30. package/dist/index.client.d.ts +2 -1
  31. package/dist/index.client.js +3 -0
  32. package/dist/index.client.js.map +1 -1
  33. package/dist/index.d.ts +2 -1
  34. package/dist/index.js +3 -0
  35. package/dist/index.js.map +1 -1
  36. package/dist/react/Admin.d.ts +5 -3
  37. package/dist/react/Admin.js +29 -41
  38. package/dist/react/Admin.js.map +1 -1
  39. package/dist/react/AuthDrawer.js +22 -34
  40. package/dist/react/AuthDrawer.js.map +1 -1
  41. package/dist/react/AuthRedirectSync.js +28 -22
  42. package/dist/react/AuthRedirectSync.js.map +1 -1
  43. package/dist/react/GRail/client.js +27 -47
  44. package/dist/react/GRail/client.js.map +1 -1
  45. package/dist/react/GRail/render.js +3 -2
  46. package/dist/react/GRail/render.js.map +1 -1
  47. package/dist/react/MediaPicker.js +12 -16
  48. package/dist/react/MediaPicker.js.map +1 -1
  49. package/dist/react/Repeater/client.js +12 -13
  50. package/dist/react/Repeater/client.js.map +1 -1
  51. package/dist/react/Team.d.ts +1 -7
  52. package/dist/react/Team.js +21 -46
  53. package/dist/react/Team.js.map +1 -1
  54. package/dist/react/guardrail.css +563 -0
  55. package/dist/server/actions.js +52 -31
  56. package/dist/server/actions.js.map +1 -1
  57. package/dist/server/loader.js +4 -4
  58. package/dist/server/loader.js.map +1 -1
  59. package/dist/server/rateLimit.d.ts +21 -0
  60. package/dist/server/rateLimit.js +94 -0
  61. package/dist/server/rateLimit.js.map +1 -0
  62. package/dist/server/repeaterActions.js +9 -1
  63. package/dist/server/repeaterActions.js.map +1 -1
  64. package/dist/server/wrapper.js +18 -1
  65. package/dist/server/wrapper.js.map +1 -1
  66. package/package.json +11 -9
  67. package/supabase/admin/check-grants.sql +41 -0
  68. package/supabase/migrations/20261028000000_require_confirmed_email.sql +53 -0
  69. package/supabase/migrations/20261028010000_rate_limits.sql +67 -0
  70. package/supabase/migrations/20261028020000_member_and_bucket_guards.sql +96 -0
  71. package/dist/react/styles.d.ts +0 -504
  72. package/dist/react/styles.js +0 -500
  73. package/dist/react/styles.js.map +0 -1
  74. package/scripts/postinstall.mjs +0 -48
@@ -0,0 +1,563 @@
1
+ /*
2
+ * Guardrail's editing chrome — the sign-in bar, toolbar, editors, drawers and
3
+ * the outlines drawn around editable fields.
4
+ *
5
+ * Imported by the package itself (see GRailAdmin), so a site doesn't link it by
6
+ * hand, and shipped as a real stylesheet rather than inline styles so it works
7
+ * under a strict Content-Security-Policy (style-src 'self', no 'unsafe-inline').
8
+ *
9
+ * Theming: every color is a --gr-* variable. Override any of them in your own
10
+ * CSS to restyle the chrome, e.g.
11
+ *
12
+ * :root { --gr-accent: #7c3aed; --gr-field-outline: #7c3aed; }
13
+ *
14
+ * Three layers: change a base color (--gr-bg, --gr-fg, --gr-border …) to retheme
15
+ * everything, or set the --gr-overlay-* variables to restyle only the floating
16
+ * surfaces (sign-in bar, toolbar, popups, drawers, picker) — they default to the
17
+ * base colors, so you only set what you want to differ.
18
+ *
19
+ * The defaults below are declared in :where(:root), which has no specificity, so
20
+ * a plain :root rule of yours wins whatever order the stylesheets load in — no
21
+ * !important, no selector tricks.
22
+ *
23
+ * Every class is prefixed gr-, so nothing here can collide with a site's own.
24
+ */
25
+
26
+ /* ---- theme ---------------------------------------------------------------- */
27
+
28
+ :where(:root) { --gr-bg: #ffffff; --gr-bg-glass: rgba(255,255,255,0.97); --gr-fg: #111827; --gr-fg2: #4b5563; --gr-muted: #6b7280; --gr-subtle: #9ca3af; --gr-border: #e5e7eb; --gr-border-strong: #d1d5db; --gr-border-soft: #f3f4f6; --gr-input-bg: #ffffff; --gr-soft: #f3f4f6; --gr-accent: #2563eb; --gr-accent-soft: #eff6ff; --gr-accent-text: #1d4ed8; --gr-primary-bg: #111827; --gr-primary-fg: #ffffff; --gr-danger: #dc2626; --gr-label: #1e3a8a; --gr-shadow: rgba(0,0,0,0.25); --gr-field-outline: #60a5fa; --gr-field-outline-active: #2563eb; --gr-on-accent: #ffffff; --gr-scrim: rgba(0,0,0,0.4); --gr-scrim-modal: rgba(0,0,0,0.5); --gr-badge-bg: #fef3c7; --gr-badge-fg: #92400e; --gr-overlay-bg: var(--gr-bg); --gr-overlay-bg-glass: var(--gr-bg-glass); --gr-overlay-fg: var(--gr-fg); --gr-overlay-border: var(--gr-border); }
29
+ [data-guardrail] { color-scheme: light; }
30
+ @media (prefers-color-scheme: dark) {
31
+ :where(:root) { --gr-bg: #0f1b2d; --gr-bg-glass: rgba(15,27,45,0.97); --gr-fg: #e6edf7; --gr-fg2: #b6c4d8; --gr-muted: #9fb0c7; --gr-subtle: #7d8ea6; --gr-border: rgba(148,163,184,0.22); --gr-border-strong: rgba(148,163,184,0.35); --gr-border-soft: rgba(148,163,184,0.14); --gr-input-bg: #14243b; --gr-soft: #1a2d49; --gr-accent: #3b82f6; --gr-accent-soft: rgba(59,130,246,0.16); --gr-accent-text: #93c5fd; --gr-primary-bg: #3b82f6; --gr-primary-fg: #ffffff; --gr-danger: #f87171; --gr-label: #dbeafe; --gr-shadow: rgba(0,0,0,0.5); }
32
+ [data-guardrail] { color-scheme: dark; }
33
+ }
34
+
35
+ /* ---- components ------------------------------------------------------------ */
36
+
37
+ .gr-banner {
38
+ position: fixed;
39
+ bottom: 0;
40
+ left: 0;
41
+ width: 100%;
42
+ background: var(--gr-overlay-bg);
43
+ color: var(--gr-overlay-fg);
44
+ padding: 12px 16px;
45
+ z-index: 10000;
46
+ border-top: 1px solid var(--gr-overlay-border);
47
+ font-family: system-ui, -apple-system, sans-serif;
48
+ }
49
+ .gr-banner-form {
50
+ display: flex;
51
+ gap: 12px;
52
+ justify-content: center;
53
+ align-items: center;
54
+ flex-wrap: wrap;
55
+ }
56
+ .gr-brand {
57
+ font-weight: 900;
58
+ letter-spacing: 0.15em;
59
+ font-size: 12px;
60
+ text-transform: uppercase;
61
+ }
62
+ .gr-brand-stack {
63
+ display: flex;
64
+ flex-direction: column;
65
+ align-items: flex-start;
66
+ gap: 2px;
67
+ }
68
+ .gr-error {
69
+ color: var(--gr-danger);
70
+ font-size: 12px;
71
+ font-weight: 700;
72
+ }
73
+ .gr-input {
74
+ border: 1px solid var(--gr-border-strong);
75
+ border-radius: 4px;
76
+ padding: 6px 8px;
77
+ font-size: 13px;
78
+ color: var(--gr-fg);
79
+ background: var(--gr-input-bg);
80
+ outline: none;
81
+ }
82
+ .gr-primary-btn {
83
+ background: var(--gr-primary-bg);
84
+ color: var(--gr-primary-fg);
85
+ padding: 7px 16px;
86
+ border-radius: 4px;
87
+ font-size: 12px;
88
+ font-weight: 700;
89
+ border: none;
90
+ cursor: pointer;
91
+ }
92
+ .gr-toolbar {
93
+ position: fixed;
94
+ bottom: 24px;
95
+ left: 50%;
96
+ transform: translateX(-50%);
97
+ z-index: 9999;
98
+ background: var(--gr-overlay-bg-glass);
99
+ backdrop-filter: blur(8px);
100
+ color: var(--gr-overlay-fg);
101
+ padding: 12px 20px;
102
+ border-radius: 9999px;
103
+ box-shadow: 0 20px 40px var(--gr-shadow);
104
+ display: flex;
105
+ align-items: center;
106
+ gap: 20px;
107
+ border: 1px solid var(--gr-overlay-border);
108
+ font-family: system-ui, -apple-system, sans-serif;
109
+ }
110
+ .gr-dot {
111
+ display: inline-block;
112
+ width: 8px;
113
+ height: 8px;
114
+ border-radius: 50%;
115
+ background: var(--gr-accent);
116
+ }
117
+ .gr-toolbar-label {
118
+ font-size: 10px;
119
+ font-weight: 900;
120
+ text-transform: uppercase;
121
+ letter-spacing: 0.2em;
122
+ color: var(--gr-label);
123
+ }
124
+ .gr-divider {
125
+ width: 1px;
126
+ height: 16px;
127
+ background: var(--gr-border-strong);
128
+ }
129
+ .gr-link-btn {
130
+ font-size: 10px;
131
+ font-weight: 700;
132
+ text-transform: uppercase;
133
+ letter-spacing: 0.15em;
134
+ color: var(--gr-muted);
135
+ background: none;
136
+ border: none;
137
+ cursor: pointer;
138
+ }
139
+ .gr-popover {
140
+ position: fixed;
141
+ bottom: 90px;
142
+ left: 50%;
143
+ transform: translateX(-50%);
144
+ z-index: 10001;
145
+ background: var(--gr-overlay-bg);
146
+ color: var(--gr-overlay-fg);
147
+ padding: 16px;
148
+ border-radius: 8px;
149
+ box-shadow: 0 20px 40px var(--gr-shadow);
150
+ border: 1px solid var(--gr-overlay-border);
151
+ min-width: 300px;
152
+ cursor: default;
153
+ font-family: system-ui, -apple-system, sans-serif;
154
+ }
155
+ .gr-label {
156
+ display: block;
157
+ font-size: 10px;
158
+ font-weight: 700;
159
+ color: var(--gr-subtle);
160
+ margin-bottom: 4px;
161
+ text-transform: uppercase;
162
+ }
163
+ .gr-field {
164
+ border: 1px solid var(--gr-border-strong);
165
+ border-radius: 4px;
166
+ padding: 6px 8px;
167
+ font-size: 13px;
168
+ color: var(--gr-fg);
169
+ background: var(--gr-input-bg);
170
+ width: 100%;
171
+ margin-bottom: 8px;
172
+ box-sizing: border-box;
173
+ }
174
+ .gr-row {
175
+ display: flex;
176
+ gap: 8px;
177
+ margin-top: 8px;
178
+ padding-top: 8px;
179
+ border-top: 1px solid var(--gr-border-soft);
180
+ }
181
+ .gr-cancel-btn {
182
+ background: var(--gr-soft);
183
+ color: var(--gr-fg2);
184
+ padding: 7px 14px;
185
+ border-radius: 4px;
186
+ font-size: 12px;
187
+ border: none;
188
+ cursor: pointer;
189
+ }
190
+ .gr-anchored-bar {
191
+ position: fixed;
192
+ z-index: 10001;
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 4px;
196
+ background: var(--gr-overlay-bg-glass);
197
+ color: var(--gr-overlay-fg);
198
+ backdrop-filter: blur(8px);
199
+ padding: 4px;
200
+ border-radius: 8px;
201
+ box-shadow: 0 8px 24px var(--gr-shadow);
202
+ border: 1px solid var(--gr-overlay-border);
203
+ font-family: system-ui, -apple-system, sans-serif;
204
+ }
205
+ .gr-anchored-panel {
206
+ position: fixed;
207
+ z-index: 10001;
208
+ max-height: calc(100vh - 24px);
209
+ overflow-y: auto;
210
+ background: var(--gr-overlay-bg);
211
+ color: var(--gr-overlay-fg);
212
+ padding: 12px;
213
+ border-radius: 8px;
214
+ box-shadow: 0 12px 32px var(--gr-shadow);
215
+ border: 1px solid var(--gr-overlay-border);
216
+ min-width: 260px;
217
+ max-width: min(340px, calc(100vw - 16px));
218
+ cursor: default;
219
+ font-family: system-ui, -apple-system, sans-serif;
220
+ }
221
+ .gr-icon-btn {
222
+ display: inline-flex;
223
+ align-items: center;
224
+ justify-content: center;
225
+ width: 26px;
226
+ height: 26px;
227
+ border-radius: 6px;
228
+ border: none;
229
+ background: transparent;
230
+ color: var(--gr-fg);
231
+ cursor: pointer;
232
+ padding: 0;
233
+ }
234
+ .gr-icon-btn-primary {
235
+ display: inline-flex;
236
+ align-items: center;
237
+ justify-content: center;
238
+ width: 26px;
239
+ height: 26px;
240
+ border-radius: 6px;
241
+ border: none;
242
+ background: var(--gr-accent);
243
+ color: var(--gr-on-accent);
244
+ cursor: pointer;
245
+ padding: 0;
246
+ }
247
+ .gr-scrim {
248
+ position: fixed;
249
+ inset: 0;
250
+ background: var(--gr-scrim);
251
+ z-index: 10002;
252
+ }
253
+ .gr-drawer {
254
+ position: fixed;
255
+ top: 0;
256
+ right: 0;
257
+ height: 100%;
258
+ width: min(420px, 100vw);
259
+ background: var(--gr-overlay-bg);
260
+ color: var(--gr-overlay-fg);
261
+ z-index: 10003;
262
+ box-shadow: -12px 0 32px var(--gr-shadow);
263
+ display: flex;
264
+ flex-direction: column;
265
+ font-family: system-ui, -apple-system, sans-serif;
266
+ }
267
+ .gr-drawer-head {
268
+ display: flex;
269
+ align-items: center;
270
+ justify-content: space-between;
271
+ padding: 16px 20px;
272
+ border-bottom: 1px solid var(--gr-overlay-border);
273
+ }
274
+ .gr-drawer-title {
275
+ font-size: 14px;
276
+ font-weight: 800;
277
+ margin: 0;
278
+ }
279
+ .gr-drawer-body {
280
+ padding: 20px;
281
+ overflow-y: auto;
282
+ flex: 1;
283
+ }
284
+ .gr-member-row {
285
+ display: flex;
286
+ align-items: center;
287
+ gap: 8px;
288
+ padding: 10px 0;
289
+ border-bottom: 1px solid var(--gr-border-soft);
290
+ }
291
+ .gr-member-email {
292
+ flex: 1;
293
+ font-size: 13px;
294
+ overflow: hidden;
295
+ text-overflow: ellipsis;
296
+ white-space: nowrap;
297
+ }
298
+ .gr-select {
299
+ border: 1px solid var(--gr-border-strong);
300
+ border-radius: 4px;
301
+ padding: 4px 6px;
302
+ font-size: 12px;
303
+ background: var(--gr-input-bg);
304
+ color: var(--gr-fg);
305
+ }
306
+ .gr-badge {
307
+ font-size: 9px;
308
+ font-weight: 800;
309
+ text-transform: uppercase;
310
+ letter-spacing: 0.1em;
311
+ padding: 2px 6px;
312
+ border-radius: 999px;
313
+ background: var(--gr-badge-bg);
314
+ color: var(--gr-badge-fg);
315
+ }
316
+ .gr-section-label {
317
+ font-size: 10px;
318
+ font-weight: 800;
319
+ text-transform: uppercase;
320
+ letter-spacing: 0.15em;
321
+ color: var(--gr-subtle);
322
+ margin: 20px 0 8px;
323
+ }
324
+ .gr-picker-scrim {
325
+ position: fixed;
326
+ inset: 0;
327
+ background: var(--gr-scrim-modal);
328
+ z-index: 10010;
329
+ }
330
+ .gr-picker {
331
+ position: fixed;
332
+ top: 50%;
333
+ left: 50%;
334
+ transform: translate(-50%, -50%);
335
+ width: min(720px, calc(100vw - 32px));
336
+ max-height: min(640px, calc(100vh - 32px));
337
+ background: var(--gr-overlay-bg);
338
+ color: var(--gr-overlay-fg);
339
+ border-radius: 12px;
340
+ z-index: 10011;
341
+ box-shadow: 0 24px 64px var(--gr-shadow);
342
+ display: flex;
343
+ flex-direction: column;
344
+ overflow: hidden;
345
+ font-family: system-ui, -apple-system, sans-serif;
346
+ }
347
+ .gr-picker-grid {
348
+ display: grid;
349
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
350
+ gap: 10px;
351
+ }
352
+ .gr-picker-tile {
353
+ position: relative;
354
+ aspect-ratio: 1;
355
+ border-radius: 8px;
356
+ overflow: hidden;
357
+ border: 2px solid transparent;
358
+ background: var(--gr-soft);
359
+ cursor: pointer;
360
+ padding: 0;
361
+ }
362
+ .gr-picker-tile-active {
363
+ border-color: var(--gr-accent);
364
+ }
365
+ .gr-picker-thumb {
366
+ width: 100%;
367
+ height: 100%;
368
+ object-fit: cover;
369
+ display: block;
370
+ }
371
+ .gr-dropzone {
372
+ border: 1.5px dashed var(--gr-border-strong);
373
+ border-radius: 8px;
374
+ padding: 16px;
375
+ text-align: center;
376
+ font-size: 13px;
377
+ color: var(--gr-muted);
378
+ margin-bottom: 14px;
379
+ cursor: pointer;
380
+ }
381
+ .gr-dropzone-active {
382
+ border-color: var(--gr-accent);
383
+ background: var(--gr-accent-soft);
384
+ color: var(--gr-accent-text);
385
+ }
386
+ .gr-preview-thumb {
387
+ width: 100%;
388
+ max-width: 300px;
389
+ height: auto;
390
+ max-height: 200px;
391
+ object-fit: contain;
392
+ background: var(--gr-soft);
393
+ border-radius: 6px;
394
+ display: block;
395
+ margin-bottom: 8px;
396
+ }
397
+ .gr-secondary-btn {
398
+ background: var(--gr-soft);
399
+ color: var(--gr-fg);
400
+ padding: 7px 12px;
401
+ border-radius: 4px;
402
+ font-size: 12px;
403
+ font-weight: 600;
404
+ border: none;
405
+ cursor: pointer;
406
+ width: 100%;
407
+ margin-bottom: 10px;
408
+ }
409
+ .gr-marker-outline {
410
+ position: fixed;
411
+ z-index: 9990;
412
+ pointer-events: none;
413
+ box-sizing: border-box;
414
+ border-radius: 2px;
415
+ }
416
+ .gr-marker-handle {
417
+ position: fixed;
418
+ z-index: 9991;
419
+ display: inline-flex;
420
+ align-items: center;
421
+ gap: 5px;
422
+ padding: 4px 9px 4px 7px;
423
+ border-radius: 6px;
424
+ border: none;
425
+ background: var(--gr-accent);
426
+ color: var(--gr-on-accent);
427
+ font-size: 11px;
428
+ font-weight: 700;
429
+ font-family: system-ui, -apple-system, sans-serif;
430
+ cursor: pointer;
431
+ box-shadow: 0 4px 12px var(--gr-shadow);
432
+ }
433
+ .gr-repeater-item {
434
+ position: relative;
435
+ outline: 1px dashed var(--gr-field-outline);
436
+ outline-offset: 2px;
437
+ }
438
+ .gr-repeater-toolbar {
439
+ position: absolute;
440
+ top: 4px;
441
+ right: 4px;
442
+ z-index: 20;
443
+ display: flex;
444
+ gap: 2px;
445
+ background: var(--gr-overlay-bg-glass);
446
+ color: var(--gr-overlay-fg);
447
+ backdrop-filter: blur(6px);
448
+ padding: 3px;
449
+ border-radius: 6px;
450
+ box-shadow: 0 4px 12px var(--gr-shadow);
451
+ border: 1px solid var(--gr-overlay-border);
452
+ }
453
+ .gr-repeater-add-row {
454
+ display: flex;
455
+ gap: 8px;
456
+ align-items: center;
457
+ margin-top: 8px;
458
+ font-family: system-ui, -apple-system, sans-serif;
459
+ }
460
+ .gr-repeater-add-btn {
461
+ display: inline-flex;
462
+ align-items: center;
463
+ gap: 6px;
464
+ padding: 8px 14px;
465
+ border-radius: 6px;
466
+ border: 1.5px dashed var(--gr-accent);
467
+ background: transparent;
468
+ color: var(--gr-accent);
469
+ font-size: 12px;
470
+ font-weight: 700;
471
+ cursor: pointer;
472
+ }
473
+ .gr-repeater-slug-input {
474
+ border: 1px solid var(--gr-border-strong);
475
+ border-radius: 4px;
476
+ padding: 7px 9px;
477
+ font-size: 13px;
478
+ color: var(--gr-fg);
479
+ background: var(--gr-input-bg);
480
+ min-width: 180px;
481
+ }
482
+
483
+ /* ---- modifiers and utilities ----------------------------------------------
484
+ * Single-class rules that follow the components above, so they win over a
485
+ * component's own value when both are on one element. */
486
+
487
+ .gr-text-fg { color: var(--gr-fg); }
488
+ .gr-text-muted { color: var(--gr-muted); }
489
+ .gr-text-subtle { color: var(--gr-subtle); }
490
+ .gr-text-danger { color: var(--gr-danger); }
491
+ .gr-fs-11 { font-size: 11px; }
492
+ .gr-fs-12 { font-size: 12px; }
493
+ .gr-fs-13 { font-size: 13px; }
494
+ .gr-lh-15 { line-height: 1.5; }
495
+ .gr-m-0 { margin: 0; }
496
+ .gr-mx-6 { margin-left: 6px; margin-right: 6px; }
497
+ .gr-mt-8 { margin-top: 8px; }
498
+ .gr-mt-10 { margin-top: 10px; }
499
+ .gr-mt-16 { margin-top: 16px; }
500
+ .gr-mb-0 { margin-bottom: 0; }
501
+ .gr-mb-8 { margin-bottom: 8px; }
502
+ .gr-mb-12 { margin-bottom: 12px; }
503
+ .gr-mb-16 { margin-bottom: 16px; }
504
+ .gr-p-0 { padding: 0; }
505
+ .gr-w-full { width: 100%; }
506
+ .gr-w-auto { width: auto; }
507
+ .gr-size-22 { width: 22px; height: 22px; }
508
+ .gr-size-24 { width: 24px; height: 24px; }
509
+ .gr-flex { display: flex; }
510
+ .gr-inline-flex { display: inline-flex; }
511
+ .gr-flex-1 { flex: 1; }
512
+ .gr-items-center { align-items: center; }
513
+ .gr-justify-between { justify-content: space-between; }
514
+ .gr-justify-end { justify-content: flex-end; }
515
+ .gr-gap-4 { gap: 4px; }
516
+ .gr-gap-6 { gap: 6px; }
517
+ .gr-gap-8 { gap: 8px; }
518
+ .gr-no-underline { text-decoration: none; }
519
+
520
+ /* A text-only button, e.g. "Forgot password?". */
521
+ .gr-text-btn {
522
+ background: none;
523
+ border: none;
524
+ cursor: pointer;
525
+ color: var(--gr-muted);
526
+ font-size: 12px;
527
+ text-decoration: underline;
528
+ padding: 0 4px;
529
+ white-space: nowrap;
530
+ }
531
+
532
+ /* ---- editable fields ------------------------------------------------------
533
+ * Drawn on the developer's own elements by <GRail>, which marks them with a
534
+ * data attribute instead of a style attribute. Images get a separate marker. */
535
+
536
+ [data-gr-field] { cursor: pointer; }
537
+ [data-gr-field="text"] { outline: 1px dashed var(--gr-field-outline); outline-offset: 2px; }
538
+ [data-gr-field="text"][data-gr-editing] { outline: 2px solid var(--gr-field-outline-active); cursor: text; }
539
+
540
+ .gr-marker-outline { border: 1px dashed var(--gr-field-outline); }
541
+ .gr-marker-outline[data-gr-editing] { border: 2px solid var(--gr-field-outline-active); }
542
+
543
+ /* ---- drawer animation ------------------------------------------------------ */
544
+ @keyframes gr-fade-in { from { opacity: 0 } to { opacity: 1 } }
545
+ @keyframes gr-slide-in {
546
+ from { transform: translateX(100%) }
547
+ to { transform: translateX(0) }
548
+ }
549
+ [data-guardrail-scrim] { animation: gr-fade-in 160ms ease-out both }
550
+ [data-guardrail-team] { animation: gr-slide-in 220ms cubic-bezier(0.32, 0.72, 0, 1) both }
551
+ [data-guardrail-team] [data-gr-row] { animation: gr-fade-in 240ms ease-out both }
552
+ @keyframes gr-fade-out { from { opacity: 1 } to { opacity: 0 } }
553
+ @keyframes gr-slide-out {
554
+ from { transform: translateX(0) }
555
+ to { transform: translateX(100%) }
556
+ }
557
+ [data-guardrail-scrim][data-closing] { animation: gr-fade-out 180ms ease-in both }
558
+ [data-guardrail-team][data-closing] { animation: gr-slide-out 180ms cubic-bezier(0.4, 0, 1, 1) both }
559
+ @media (prefers-reduced-motion: reduce) {
560
+ [data-guardrail-scrim], [data-guardrail-team], [data-guardrail-team] [data-gr-row] {
561
+ animation: none
562
+ }
563
+ }