@bkazemi/verily 0.9.4 → 0.9.5
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.
- package/README.md +4 -2
- package/dist/browser/badge.css.d.ts +2 -0
- package/dist/browser/badge.css.js +2 -0
- package/dist/browser/badge.js +2 -66
- package/dist/browser/connect-dialog.css.d.ts +2 -0
- package/dist/browser/connect-dialog.css.js +2 -0
- package/dist/browser/connect-dialog.js +4 -24
- package/dist/browser/evidence-dialog.css.d.ts +2 -0
- package/dist/browser/evidence-dialog.css.js +2 -0
- package/dist/browser/evidence-dialog.d.ts +6 -1
- package/dist/browser/evidence-dialog.js +33 -114
- package/dist/browser/mark.css.d.ts +2 -0
- package/dist/browser/mark.css.js +2 -0
- package/dist/browser/mark.d.ts +1 -1
- package/dist/browser/mark.js +2 -6
- package/dist/server/style.css.d.ts +2 -0
- package/dist/server/style.css.js +2 -0
- package/dist/server/style.d.ts +1 -1
- package/dist/server/style.js +2 -455
- package/dist/verily.js +569 -210
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -1
package/README.md
CHANGED
|
@@ -168,7 +168,7 @@ import { init } from '@bkazemi/verily';
|
|
|
168
168
|
const client = init({ backendUrl: '/api/verily' });
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
Without one, serve `node_modules/@bkazemi/verily/dist/verily.js` yourself, or load it from a CDN such as `https://cdn.jsdelivr.net/npm/@bkazemi/verily@0.9.
|
|
171
|
+
Without one, serve `node_modules/@bkazemi/verily/dist/verily.js` yourself, or load it from a CDN such as `https://cdn.jsdelivr.net/npm/@bkazemi/verily@0.9.5/dist/verily.js`. The script defines a global `Verily`.
|
|
172
172
|
|
|
173
173
|
Then place the badge wherever the account appears:
|
|
174
174
|
|
|
@@ -370,7 +370,7 @@ A static site only needs the script and the badge markup; the backend runs somew
|
|
|
370
370
|
|
|
371
371
|
```html
|
|
372
372
|
<script
|
|
373
|
-
src="https://cdn.jsdelivr.net/npm/@bkazemi/verily@0.9.
|
|
373
|
+
src="https://cdn.jsdelivr.net/npm/@bkazemi/verily@0.9.5/dist/verily.js"
|
|
374
374
|
defer
|
|
375
375
|
></script>
|
|
376
376
|
<verily-badge
|
|
@@ -641,6 +641,8 @@ setInterval(
|
|
|
641
641
|
|
|
642
642
|
Clone the repository and run `npm ci`. `npm run preview` then shows the badge in every state at http://localhost:3001, with no other setup.
|
|
643
643
|
|
|
644
|
+
Stylesheets are the `.css` files under `src`. `npm run css` turns each into a `.css.ts` module the code imports, and the build, check and run scripts do this first. Run it yourself after editing a `.css` file when you run a file with `tsx` directly.
|
|
645
|
+
|
|
644
646
|
The full example also needs Postgres and a GitHub OAuth app, a Discord application or a Google OAuth client (any of them) with the callback URL `http://localhost:3000/api/verily/callback`:
|
|
645
647
|
|
|
646
648
|
```sh
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: ":host {\n display: inline-block;\n max-width: 100%;\n vertical-align: middle;\n}\n* {\n box-sizing: border-box;\n}\n.badge,\n.peek {\n border: 1px solid var(--verily-border, #dce2e0);\n border-radius: 6px;\n background: var(--verily-surface, #fff);\n color: var(--verily-text, #202c29);\n font: var(--verily-font-size, 13px)/1.35\n var(--verily-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n}\n.badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n max-width: 100%;\n padding: 3px 5px;\n text-decoration: none;\n}\nbutton.badge {\n margin: 0;\n cursor: pointer;\n}\na[href]:hover,\nbutton.badge:hover {\n background: var(--verily-hover, #f3f6f4);\n border-color: var(--verily-border, #dce2e0);\n}\n.badge:focus-visible {\n outline: 2px solid #357ce5;\n outline-offset: 2px;\n}\n.name {\n font-weight: 600;\n overflow-wrap: anywhere;\n min-width: 0;\n}\n.more {\n flex-shrink: 0;\n font-size: 11px;\n font-weight: 600;\n color: var(--verily-muted, #65726c);\n}\n.label {\n display: none;\n font-size: 11px;\n color: var(--verily-muted, #65726c);\n}\n.badge:hover .label,\n.badge:focus-within .label {\n display: inline;\n}\n.icon {\n display: grid;\n place-items: center;\n flex-shrink: 0;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n font-size: 12px;\n font-weight: 750;\n}\n.icon .glyph {\n display: block;\n width: 12px;\n height: 12px;\n}\n.mark {\n display: block;\n flex-shrink: 0;\n width: 20px;\n height: 20px;\n}\n.mark.pending {\n opacity: 0.45;\n}\n/* A dim ring where the account will go; it only spins where motion is welcome. */\n.spinner {\n flex-shrink: 0;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n border: 2px solid currentColor;\n opacity: 0.3;\n}\n.divider {\n flex-shrink: 0;\n width: 1px;\n height: 12px;\n background: var(--verily-border, #dce2e0);\n}\n/* Several accounts, one to a row. The mark stays beside them and the rule runs their height. */\n.badge.stacked {\n align-items: stretch;\n padding: 5px 8px 5px 6px;\n text-align: left;\n}\n.stacked .mark {\n align-self: center;\n}\n.stacked .divider {\n height: auto;\n}\n.rows {\n display: grid;\n gap: 4px;\n min-width: 0;\n}\n.row {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n/*\n * The accounts behind a short pill's count, opened beside it. Shut unless the browser\n * has it open as a popover, so one without popovers never shows it at all.\n */\n.peek {\n display: none;\n position: fixed;\n inset: auto;\n margin: 0;\n padding: 8px 10px;\n width: max-content;\n max-width: min(320px, calc(100vw - 16px));\n border-radius: 8px;\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.14);\n pointer-events: none;\n overflow: hidden;\n}\n/* A row taken out to fit the window: its own display would otherwise keep it showing. */\n.row[hidden] {\n display: none;\n}\n.peek:popover-open {\n display: block;\n}\n.peek .via {\n display: block;\n margin-top: 6px;\n font-size: 11px;\n color: var(--verily-muted, #65726c);\n}\n.provider {\n display: block;\n flex-shrink: 0;\n width: 14px;\n height: 14px;\n}\n.expired .icon {\n color: #815a12;\n background: #fbefce;\n}\n.revoked .icon,\n.retired .icon,\n.message .icon {\n color: #626d69;\n background: #edf0ee;\n}\n/* Nothing ran out and nothing wants attention: the holder chose this, so it is only quiet. */\n.retired .name {\n color: var(--verily-muted, #65726c);\n}\n.message {\n color: var(--verily-muted, #65726c);\n}\n.add {\n font-weight: 600;\n}\n@media (prefers-reduced-motion: no-preference) {\n a,\n button {\n transition:\n background 0.12s,\n border-color 0.12s;\n }\n /* The mark is drawn before the answer arrives, so it resolves rather than swaps. */\n .mark,\n .mark path {\n transition:\n opacity 0.18s ease,\n stroke 0.18s ease;\n }\n .spinner {\n border-top-color: transparent;\n opacity: 0.4;\n animation: verily-spin 0.7s linear infinite;\n }\n}\n@keyframes verily-spin {\n to {\n transform: rotate(360deg);\n }\n}\n";
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// Generated from badge.css by scripts/css.mjs. Do not edit.
|
|
2
|
+
export default ":host {\n display: inline-block;\n max-width: 100%;\n vertical-align: middle;\n}\n* {\n box-sizing: border-box;\n}\n.badge,\n.peek {\n border: 1px solid var(--verily-border, #dce2e0);\n border-radius: 6px;\n background: var(--verily-surface, #fff);\n color: var(--verily-text, #202c29);\n font: var(--verily-font-size, 13px)/1.35\n var(--verily-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);\n}\n.badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n max-width: 100%;\n padding: 3px 5px;\n text-decoration: none;\n}\nbutton.badge {\n margin: 0;\n cursor: pointer;\n}\na[href]:hover,\nbutton.badge:hover {\n background: var(--verily-hover, #f3f6f4);\n border-color: var(--verily-border, #dce2e0);\n}\n.badge:focus-visible {\n outline: 2px solid #357ce5;\n outline-offset: 2px;\n}\n.name {\n font-weight: 600;\n overflow-wrap: anywhere;\n min-width: 0;\n}\n.more {\n flex-shrink: 0;\n font-size: 11px;\n font-weight: 600;\n color: var(--verily-muted, #65726c);\n}\n.label {\n display: none;\n font-size: 11px;\n color: var(--verily-muted, #65726c);\n}\n.badge:hover .label,\n.badge:focus-within .label {\n display: inline;\n}\n.icon {\n display: grid;\n place-items: center;\n flex-shrink: 0;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n font-size: 12px;\n font-weight: 750;\n}\n.icon .glyph {\n display: block;\n width: 12px;\n height: 12px;\n}\n.mark {\n display: block;\n flex-shrink: 0;\n width: 20px;\n height: 20px;\n}\n.mark.pending {\n opacity: 0.45;\n}\n/* A dim ring where the account will go; it only spins where motion is welcome. */\n.spinner {\n flex-shrink: 0;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n border: 2px solid currentColor;\n opacity: 0.3;\n}\n.divider {\n flex-shrink: 0;\n width: 1px;\n height: 12px;\n background: var(--verily-border, #dce2e0);\n}\n/* Several accounts, one to a row. The mark stays beside them and the rule runs their height. */\n.badge.stacked {\n align-items: stretch;\n padding: 5px 8px 5px 6px;\n text-align: left;\n}\n.stacked .mark {\n align-self: center;\n}\n.stacked .divider {\n height: auto;\n}\n.rows {\n display: grid;\n gap: 4px;\n min-width: 0;\n}\n.row {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n/*\n * The accounts behind a short pill's count, opened beside it. Shut unless the browser\n * has it open as a popover, so one without popovers never shows it at all.\n */\n.peek {\n display: none;\n position: fixed;\n inset: auto;\n margin: 0;\n padding: 8px 10px;\n width: max-content;\n max-width: min(320px, calc(100vw - 16px));\n border-radius: 8px;\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.14);\n pointer-events: none;\n overflow: hidden;\n}\n/* A row taken out to fit the window: its own display would otherwise keep it showing. */\n.row[hidden] {\n display: none;\n}\n.peek:popover-open {\n display: block;\n}\n.peek .via {\n display: block;\n margin-top: 6px;\n font-size: 11px;\n color: var(--verily-muted, #65726c);\n}\n.provider {\n display: block;\n flex-shrink: 0;\n width: 14px;\n height: 14px;\n}\n.expired .icon {\n color: #815a12;\n background: #fbefce;\n}\n.revoked .icon,\n.retired .icon,\n.message .icon {\n color: #626d69;\n background: #edf0ee;\n}\n/* Nothing ran out and nothing wants attention: the holder chose this, so it is only quiet. */\n.retired .name {\n color: var(--verily-muted, #65726c);\n}\n.message {\n color: var(--verily-muted, #65726c);\n}\n.add {\n font-weight: 600;\n}\n@media (prefers-reduced-motion: no-preference) {\n a,\n button {\n transition:\n background 0.12s,\n border-color 0.12s;\n }\n /* The mark is drawn before the answer arrives, so it resolves rather than swaps. */\n .mark,\n .mark path {\n transition:\n opacity 0.18s ease,\n stroke 0.18s ease;\n }\n .spinner {\n border-top-color: transparent;\n opacity: 0.4;\n animation: verily-spin 0.7s linear infinite;\n }\n}\n@keyframes verily-spin {\n to {\n transform: rotate(360deg);\n }\n}\n";
|
package/dist/browser/badge.js
CHANGED
|
@@ -1,72 +1,8 @@
|
|
|
1
1
|
import { externalName, statusLabel } from '../core/index.js';
|
|
2
|
+
import badgeCss from './badge.css.js';
|
|
2
3
|
import { boxMark, clockMark, dashMark, markStyles, paintMark, verificationMark } from './mark.js';
|
|
3
4
|
import { providerMark } from './provider-mark.js';
|
|
4
|
-
const styles =
|
|
5
|
-
:host { display: inline-block; max-width: 100%; vertical-align: middle; }
|
|
6
|
-
* { box-sizing: border-box; }
|
|
7
|
-
.badge, .peek {
|
|
8
|
-
border: 1px solid var(--verily-border, #dce2e0); border-radius: 6px;
|
|
9
|
-
background: var(--verily-surface, #fff); color: var(--verily-text, #202c29);
|
|
10
|
-
font: var(--verily-font-size, 13px)/1.35
|
|
11
|
-
var(--verily-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
|
|
12
|
-
}
|
|
13
|
-
.badge {
|
|
14
|
-
display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 3px 5px;
|
|
15
|
-
text-decoration: none;
|
|
16
|
-
}
|
|
17
|
-
button.badge { margin: 0; cursor: pointer; }
|
|
18
|
-
a[href]:hover, button.badge:hover { background: var(--verily-hover, #f3f6f4); border-color: var(--verily-border, #dce2e0); }
|
|
19
|
-
.badge:focus-visible { outline: 2px solid #357ce5; outline-offset: 2px; }
|
|
20
|
-
.name { font-weight: 600; overflow-wrap: anywhere; min-width: 0; }
|
|
21
|
-
.more { flex-shrink: 0; font-size: 11px; font-weight: 600; color: var(--verily-muted, #65726c); }
|
|
22
|
-
.label { display: none; font-size: 11px; color: var(--verily-muted, #65726c); }
|
|
23
|
-
.badge:hover .label, .badge:focus-within .label { display: inline; }
|
|
24
|
-
.icon { display: grid; place-items: center; flex-shrink: 0; width: 20px; height: 20px; border-radius: 50%; font-size: 12px; font-weight: 750; }
|
|
25
|
-
.icon .glyph { display: block; width: 12px; height: 12px; }
|
|
26
|
-
.mark { display: block; flex-shrink: 0; width: 20px; height: 20px; }
|
|
27
|
-
.mark.pending { opacity: .45; }
|
|
28
|
-
/* A dim ring where the account will go; it only spins where motion is welcome. */
|
|
29
|
-
.spinner {
|
|
30
|
-
flex-shrink: 0; width: 14px; height: 14px; border-radius: 50%;
|
|
31
|
-
border: 2px solid currentColor; opacity: .3;
|
|
32
|
-
}
|
|
33
|
-
.divider { flex-shrink: 0; width: 1px; height: 12px; background: var(--verily-border, #dce2e0); }
|
|
34
|
-
/* Several accounts, one to a row. The mark stays beside them and the rule runs their height. */
|
|
35
|
-
.badge.stacked { align-items: stretch; padding: 5px 8px 5px 6px; text-align: left; }
|
|
36
|
-
.stacked .mark { align-self: center; }
|
|
37
|
-
.stacked .divider { height: auto; }
|
|
38
|
-
.rows { display: grid; gap: 4px; min-width: 0; }
|
|
39
|
-
.row { display: flex; align-items: center; gap: 6px; min-width: 0; }
|
|
40
|
-
/*
|
|
41
|
-
* The accounts behind a short pill's count, opened beside it. Shut unless the browser
|
|
42
|
-
* has it open as a popover, so one without popovers never shows it at all.
|
|
43
|
-
*/
|
|
44
|
-
.peek {
|
|
45
|
-
display: none; position: fixed; inset: auto; margin: 0; padding: 8px 10px;
|
|
46
|
-
width: max-content; max-width: min(320px, calc(100vw - 16px));
|
|
47
|
-
border-radius: 8px; box-shadow: 0 6px 20px rgba(0, 0, 0, .14); pointer-events: none;
|
|
48
|
-
overflow: hidden;
|
|
49
|
-
}
|
|
50
|
-
/* A row taken out to fit the window: its own display would otherwise keep it showing. */
|
|
51
|
-
.row[hidden] { display: none; }
|
|
52
|
-
.peek:popover-open { display: block; }
|
|
53
|
-
.peek .via { display: block; margin-top: 6px; font-size: 11px; color: var(--verily-muted, #65726c); }
|
|
54
|
-
.provider { display: block; flex-shrink: 0; width: 14px; height: 14px; }
|
|
55
|
-
.expired .icon { color: #815a12; background: #fbefce; }
|
|
56
|
-
.revoked .icon, .retired .icon, .message .icon { color: #626d69; background: #edf0ee; }
|
|
57
|
-
/* Nothing ran out and nothing wants attention: the holder chose this, so it is only quiet. */
|
|
58
|
-
.retired .name { color: var(--verily-muted, #65726c); }
|
|
59
|
-
.message { color: var(--verily-muted, #65726c); }
|
|
60
|
-
.add { font-weight: 600; }
|
|
61
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
62
|
-
a, button { transition: background .12s, border-color .12s; }
|
|
63
|
-
/* The mark is drawn before the answer arrives, so it resolves rather than swaps. */
|
|
64
|
-
.mark, .mark path { transition: opacity .18s ease, stroke .18s ease; }
|
|
65
|
-
.spinner { border-top-color: transparent; opacity: .4; animation: verily-spin .7s linear infinite; }
|
|
66
|
-
}
|
|
67
|
-
@keyframes verily-spin { to { transform: rotate(360deg); } }
|
|
68
|
-
${markStyles}
|
|
69
|
-
`;
|
|
5
|
+
const styles = badgeCss + markStyles;
|
|
70
6
|
/** Built once: a re-render adopts the same sheet rather than reparsing the CSS. */
|
|
71
7
|
let sheet;
|
|
72
8
|
/** The shadow host already built inside an element, kept so refreshes reuse it. */
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: ".steps > p:first-child {\n margin-top: 0;\n}\n.choices {\n display: grid;\n gap: 8px;\n margin-top: 16px;\n}\n/* With a way back beside it, the heading sits by that and not in the middle. */\nheader.backed h2 {\n margin-right: auto;\n}\n.choices .action {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.choices .more {\n margin-left: auto;\n color: #6b786f;\n font-weight: 500;\n font-size: 12px;\n}\n.row.start {\n justify-content: start;\n}\n/* The block scrolls and its frame does not, so the copy button stays in its corner. */\n.code {\n position: relative;\n margin: 10px 0;\n}\npre {\n margin: 0;\n padding: 10px 64px 10px 12px;\n border: 1px solid #e0e6df;\n border-radius: 8px;\n background: #f6f8f5;\n font:\n 12px/1.5 ui-monospace,\n monospace;\n white-space: pre;\n overflow-x: auto;\n}\n.code .action {\n position: absolute;\n top: 9px;\n right: 9px;\n padding: 3px 8px;\n font-size: 11px;\n}\nlabel {\n display: grid;\n gap: 4px;\n margin-top: 12px;\n font-weight: 600;\n}\ninput[type='url'],\ninput[type='email'],\ninput[type='tel'],\ninput[type='text'],\ntextarea {\n width: 100%;\n padding: 8px 10px;\n border: 1px solid #cfd8d2;\n border-radius: 8px;\n font:\n 13px/1.4 system-ui,\n sans-serif;\n color: inherit;\n background: #fff;\n}\ntextarea {\n font-family: ui-monospace, monospace;\n font-size: 12px;\n resize: vertical;\n}\nfieldset {\n margin: 16px 0 0;\n padding: 10px 14px;\n border: 1px solid #e0e6df;\n border-radius: 10px;\n}\nlegend {\n padding: 0 4px;\n color: #6b786f;\n font-size: 11px;\n font-weight: 550;\n}\nfieldset label {\n display: flex;\n align-items: baseline;\n gap: 8px;\n margin-top: 4px;\n font-weight: 500;\n}\n/* A radio has no baseline to speak of, so it is set in the middle of the label's first line. */\nfieldset input {\n flex-shrink: 0;\n align-self: flex-start;\n width: 13px;\n height: 13px;\n font: inherit;\n margin-top: 4px;\n margin-top: calc((1lh - 13px) / 2);\n margin-bottom: 0;\n}\n.error {\n color: #9b2c33;\n}\n";
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// Generated from connect-dialog.css by scripts/css.mjs. Do not edit.
|
|
2
|
+
export default ".steps > p:first-child {\n margin-top: 0;\n}\n.choices {\n display: grid;\n gap: 8px;\n margin-top: 16px;\n}\n/* With a way back beside it, the heading sits by that and not in the middle. */\nheader.backed h2 {\n margin-right: auto;\n}\n.choices .action {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.choices .more {\n margin-left: auto;\n color: #6b786f;\n font-weight: 500;\n font-size: 12px;\n}\n.row.start {\n justify-content: start;\n}\n/* The block scrolls and its frame does not, so the copy button stays in its corner. */\n.code {\n position: relative;\n margin: 10px 0;\n}\npre {\n margin: 0;\n padding: 10px 64px 10px 12px;\n border: 1px solid #e0e6df;\n border-radius: 8px;\n background: #f6f8f5;\n font:\n 12px/1.5 ui-monospace,\n monospace;\n white-space: pre;\n overflow-x: auto;\n}\n.code .action {\n position: absolute;\n top: 9px;\n right: 9px;\n padding: 3px 8px;\n font-size: 11px;\n}\nlabel {\n display: grid;\n gap: 4px;\n margin-top: 12px;\n font-weight: 600;\n}\ninput[type='url'],\ninput[type='email'],\ninput[type='tel'],\ninput[type='text'],\ntextarea {\n width: 100%;\n padding: 8px 10px;\n border: 1px solid #cfd8d2;\n border-radius: 8px;\n font:\n 13px/1.4 system-ui,\n sans-serif;\n color: inherit;\n background: #fff;\n}\ntextarea {\n font-family: ui-monospace, monospace;\n font-size: 12px;\n resize: vertical;\n}\nfieldset {\n margin: 16px 0 0;\n padding: 10px 14px;\n border: 1px solid #e0e6df;\n border-radius: 10px;\n}\nlegend {\n padding: 0 4px;\n color: #6b786f;\n font-size: 11px;\n font-weight: 550;\n}\nfieldset label {\n display: flex;\n align-items: baseline;\n gap: 8px;\n margin-top: 4px;\n font-weight: 500;\n}\n/* A radio has no baseline to speak of, so it is set in the middle of the label's first line. */\nfieldset input {\n flex-shrink: 0;\n align-self: flex-start;\n width: 13px;\n height: 13px;\n font: inherit;\n margin-top: 4px;\n margin-top: calc((1lh - 13px) / 2);\n margin-bottom: 0;\n}\n.error {\n color: #9b2c33;\n}\n";
|
|
@@ -1,29 +1,8 @@
|
|
|
1
|
-
import { accountCard, backMark, closeMark, linkMark, node, namedCard, outward, stampLink, styles, } from './evidence-dialog.js';
|
|
1
|
+
import { accountCard, backMark, closeMark, holdPage, linkMark, node, namedCard, outward, stampLink, styles, } from './evidence-dialog.js';
|
|
2
2
|
import { providerMark } from './provider-mark.js';
|
|
3
|
+
import connectCss from './connect-dialog.css.js';
|
|
3
4
|
import { version } from '../version.js';
|
|
4
|
-
const connectStyles =
|
|
5
|
-
${styles}
|
|
6
|
-
.steps > p:first-child { margin-top: 0; }
|
|
7
|
-
.choices { display: grid; gap: 8px; margin-top: 16px; }
|
|
8
|
-
/* With a way back beside it, the heading sits by that and not in the middle. */
|
|
9
|
-
header.backed h2 { margin-right: auto; }
|
|
10
|
-
.choices .action { display: flex; align-items: center; gap: 8px; }
|
|
11
|
-
.choices .more { margin-left: auto; color: #6b786f; font-weight: 500; font-size: 12px; }
|
|
12
|
-
.row.start { justify-content: start; }
|
|
13
|
-
/* The block scrolls and its frame does not, so the copy button stays in its corner. */
|
|
14
|
-
.code { position: relative; margin: 10px 0; }
|
|
15
|
-
pre { margin: 0; padding: 10px 64px 10px 12px; border: 1px solid #e0e6df; border-radius: 8px; background: #f6f8f5; font: 12px/1.5 ui-monospace, monospace; white-space: pre; overflow-x: auto; }
|
|
16
|
-
.code .action { position: absolute; top: 9px; right: 9px; padding: 3px 8px; font-size: 11px; }
|
|
17
|
-
label { display: grid; gap: 4px; margin-top: 12px; font-weight: 600; }
|
|
18
|
-
input[type=url], input[type=email], input[type=tel], input[type=text], textarea { width: 100%; padding: 8px 10px; border: 1px solid #cfd8d2; border-radius: 8px; font: 13px/1.4 system-ui, sans-serif; color: inherit; background: #fff; }
|
|
19
|
-
textarea { font-family: ui-monospace, monospace; font-size: 12px; resize: vertical; }
|
|
20
|
-
fieldset { margin: 16px 0 0; padding: 10px 14px; border: 1px solid #e0e6df; border-radius: 10px; }
|
|
21
|
-
legend { padding: 0 4px; color: #6b786f; font-size: 11px; font-weight: 550; }
|
|
22
|
-
fieldset label { display: flex; align-items: baseline; gap: 8px; margin-top: 4px; font-weight: 500; }
|
|
23
|
-
/* A radio has no baseline to speak of, so it is set in the middle of the label's first line. */
|
|
24
|
-
fieldset input { flex-shrink: 0; align-self: flex-start; width: 13px; height: 13px; font: inherit; margin-top: 4px; margin-top: calc((1lh - 13px) / 2); margin-bottom: 0; }
|
|
25
|
-
.error { color: #9b2c33; }
|
|
26
|
-
`;
|
|
5
|
+
const connectStyles = styles + connectCss;
|
|
27
6
|
/** Puts a copy control on a block the holder must reproduce exactly, as the flow page does. */
|
|
28
7
|
function codeBlock(code) {
|
|
29
8
|
const block = node('div', '', 'code');
|
|
@@ -528,6 +507,7 @@ renew) {
|
|
|
528
507
|
.catch(() => { })
|
|
529
508
|
.then(() => resolve(outcome));
|
|
530
509
|
}, { once: true }));
|
|
510
|
+
holdPage(dialog);
|
|
531
511
|
dialog.showModal();
|
|
532
512
|
void choose();
|
|
533
513
|
return closed;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: "* {\n box-sizing: border-box;\n}\ndialog {\n width: min(460px, calc(100vw - 32px));\n max-height: calc(100dvh - 40px);\n margin: auto;\n padding: 0;\n overflow: hidden;\n border: 1px solid #dce2de;\n border-radius: 16px;\n background: #fff;\n color: #23312b;\n box-shadow: 0 24px 90px #10201935;\n font:\n 13px/1.6 system-ui,\n sans-serif;\n}\n/* The pane scrolls inside the dialog, which clips its scrollbar to the rounded corners.\n Contain keeps a scroll past its end from scrolling the page behind. */\ndialog[open] {\n display: flex;\n flex-direction: column;\n}\n.pane {\n min-height: 0;\n padding: 24px;\n overflow-y: auto;\n overscroll-behavior: contain;\n}\ndialog::backdrop {\n background: #15271f66;\n}\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n margin-bottom: 20px;\n}\nh2 {\n margin: 0;\n font-size: 18px;\n font-weight: 650;\n letter-spacing: -0.3px;\n}\nbutton {\n width: 32px;\n height: 32px;\n border: 1px solid #dce2de;\n border-radius: 8px;\n background: #fff;\n color: #52645a;\n font:\n 20px system-ui,\n sans-serif;\n cursor: pointer;\n}\nbutton:hover {\n background: #f2f5f1;\n}\n.icon {\n display: grid;\n place-items: center;\n padding: 0;\n}\n.icon svg {\n width: 16px;\n height: 16px;\n}\n.title {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n.all {\n display: grid;\n place-items: center;\n width: 28px;\n height: 28px;\n border-radius: 8px;\n color: #52645a;\n}\n.all:hover {\n background: #f2f5f1;\n}\n.all svg {\n width: 16px;\n height: 16px;\n}\na {\n color: #245f43;\n text-underline-offset: 3px;\n overflow-wrap: anywhere;\n}\na:focus-visible,\nbutton:focus-visible,\nsummary:focus-visible {\n outline: 2px solid #357ce5;\n outline-offset: 3px;\n}\n/* An element given its own display would otherwise go on showing while hidden. */\n[hidden] {\n display: none !important;\n}\n.summary {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 16px;\n}\n.mark {\n width: 36px;\n height: 36px;\n flex-shrink: 0;\n}\n.provider {\n width: 14px;\n height: 14px;\n}\n.state {\n font-size: 14px;\n font-weight: 650;\n line-height: 1.4;\n}\n.muted {\n color: #6b786f;\n font-size: 12px;\n}\n.account {\n position: relative;\n}\n.veiled > :not(.checking) {\n filter: blur(4px);\n opacity: 0.55;\n user-select: none;\n}\n.checking {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n color: #23312b;\n font-size: 13px;\n font-weight: 600;\n}\n.spinner {\n width: 14px;\n height: 14px;\n border: 2px solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: spin 0.7s linear infinite;\n}\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .spinner {\n animation: none;\n }\n}\n/* The \"signed\" pill after the verifier's name, one size smaller than the line it sits on. */\n.summary .signed {\n display: inline-block;\n margin-left: 5px;\n padding: 0 6px;\n border: 1px solid #cfdcd3;\n border-radius: 999px;\n font-size: 10px;\n font-weight: 550;\n line-height: 1.3;\n text-decoration: none;\n vertical-align: 1px;\n}\n.summary a.signed:hover {\n background: #f2f5f1;\n}\n.summary .signed.bad {\n border-color: #b3261e;\n color: #b3261e;\n font-weight: 650;\n cursor: help;\n}\n.caution {\n display: inline-block;\n margin-left: 3px;\n color: #a15c00;\n vertical-align: -1.5px;\n}\n.caution svg {\n display: block;\n width: 10px;\n height: 10px;\n}\n.account {\n padding: 14px 16px;\n border: 1px solid #e0e6df;\n border-radius: 10px;\n margin-top: 12px;\n}\n.account h3 {\n display: flex;\n align-items: center;\n gap: 6px;\n margin: 0 0 3px;\n color: #6b786f;\n font-size: 11px;\n font-weight: 550;\n}\n.account a,\n.account strong {\n font-weight: 650;\n font-size: 14px;\n}\n/* The account's own name carries that weight; a link inside a line of prose does not. */\n.summary a,\n.method a,\n.additional a {\n font: inherit;\n}\n.reference {\n margin-top: 2px;\n}\n/* A fingerprint in fours: it breaks between groups, and copies as one unbroken word. */\n.fingerprint {\n font-family: ui-monospace, monospace;\n text-wrap: balance;\n}\n.fingerprint span {\n display: inline-block;\n margin-right: 0.5ch;\n}\n.method {\n margin-top: 8px;\n}\n/* In the corner the heading leaves empty, apart from everything that was checked. */\n.listing {\n position: absolute;\n top: 12px;\n right: 14px;\n padding: 1px 8px;\n border: 1px solid #e0e6df;\n border-radius: 999px;\n color: #6b786f;\n font-size: 11px;\n font-weight: 550;\n}\n.listing.preferred {\n border-color: #245f43;\n color: #245f43;\n}\n/* Quiet, as its pill is: the surface and the name step back, and the controls do not. */\n.account.retired {\n background: #f6f8f5;\n}\n.account.retired .identity > a,\n.account.retired .identity > strong,\n.account.retired .named > a,\n.account.retired .named > strong {\n color: #6b786f;\n}\n/* The name, then a dot and the account's creation date. The date wraps under a long name. */\n/* Clipped 4px left of the text, which hides the dot when the date wraps to a new line. */\n.named {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: 0 14px;\n margin-left: -4px;\n padding-left: 4px;\n overflow-x: clip;\n}\n.since {\n position: relative;\n white-space: nowrap;\n}\n/* The dot sits in the gap before the date. */\n.since::before {\n content: '\u00B7';\n position: absolute;\n left: -7px;\n transform: translateX(-50%);\n}\n.account.retired .mark {\n opacity: 0.45;\n}\n.group {\n margin: 20px 0 0;\n color: #6b786f;\n font-size: 11px;\n font-weight: 550;\n}\nsummary.group {\n display: flex;\n align-items: center;\n gap: 4px;\n width: fit-content;\n border-radius: 4px;\n list-style: none;\n cursor: pointer;\n}\nsummary::-webkit-details-marker {\n display: none;\n}\n.fold {\n display: flex;\n align-items: center;\n gap: 4px;\n width: auto;\n height: auto;\n padding: 2px 8px 2px 4px;\n margin-left: -4px;\n border: 0;\n background: none;\n font:\n 550 12px/1.6 system-ui,\n sans-serif;\n}\n.fold svg,\nsummary svg {\n width: 12px;\n height: 12px;\n}\n.fold[aria-expanded='true'] svg,\ndetails[open] > summary svg {\n transform: rotate(90deg);\n}\n@media (prefers-reduced-motion: no-preference) {\n .fold svg,\n summary svg {\n transition: transform 0.15s;\n }\n}\n/* The fold and what the holder may do share a line, until a question needs the whole of it. */\n.bar {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n margin-top: 10px;\n}\n.account .bar .row {\n margin: 0 0 0 auto;\n}\n.additional {\n margin-top: 2px;\n padding-left: 12px;\n}\n.joiner {\n display: block;\n width: 20px;\n height: 20px;\n margin: 8px auto -4px;\n color: #90a096;\n}\ndl {\n margin: 16px 0 0;\n padding-top: 12px;\n border-top: 1px solid #e5e9e3;\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 5px 16px;\n font-size: 11px;\n}\ndt {\n color: #6b786f;\n}\ndd {\n margin: 0;\n text-align: right;\n overflow-wrap: anywhere;\n}\n.explanation {\n margin-top: 16px;\n}\n.action {\n width: auto;\n height: auto;\n padding: 9px 14px;\n font:\n 600 13px/1.3 system-ui,\n sans-serif;\n color: #23312b;\n text-align: left;\n}\n.action.primary {\n border-color: #245f43;\n background: #245f43;\n color: #fff;\n}\n.action.primary:hover {\n background: #1d4f37;\n}\n.action:disabled {\n opacity: 0.6;\n cursor: progress;\n}\n.action.danger:hover {\n border-color: #b3261e;\n background: #fdecea;\n color: #b3261e;\n}\n.row {\n display: flex;\n justify-content: end;\n gap: 8px;\n margin-top: 16px;\n}\n.account .row {\n align-items: center;\n justify-content: start;\n flex-wrap: wrap;\n margin-top: 12px;\n}\n.account .action {\n padding: 5px 10px;\n font-size: 12px;\n}\n.account .action.dots {\n display: grid;\n place-items: center;\n align-self: stretch;\n width: 30px;\n padding: 0;\n}\n.dots svg {\n width: 16px;\n height: 16px;\n}\n/* Placed under its button each time it opens, above the dialog and clipped by none of it. */\n.menu {\n position: fixed;\n inset: auto;\n margin: 0;\n min-width: 170px;\n padding: 6px;\n border: 1px solid #dce2de;\n border-radius: 10px;\n background: #fff;\n color: #23312b;\n box-shadow: 0 12px 40px #10201930;\n}\n.menu:popover-open {\n display: grid;\n}\n.account .menu .action {\n border: 0;\n border-radius: 6px;\n padding: 6px 10px;\n font-weight: 550;\n}\n.menu .danger {\n color: #b3261e;\n}\n.menu hr {\n width: 100%;\n margin: 4px 0;\n border: 0;\n border-top: 1px solid #e5e9e3;\n}\nfooter {\n display: flex;\n align-items: center;\n justify-content: start;\n gap: 6px;\n margin: 20px 0 -8px;\n color: #9aa9a0;\n font-size: 11px;\n}\nfooter a {\n display: flex;\n color: inherit;\n}\n.logo {\n display: block;\n height: 13px;\n}\n/* Four or more accounts on a wide screen: two equal cards to a row. */\n@media (min-width: 960px) {\n dialog.many {\n width: min(900px, calc(100vw - 32px));\n }\n dialog.many .tiles {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n margin-top: 12px;\n }\n /* Each card is three rows of the grid: who, its verdict, and the rest. A row is as tall as\n the taller card's part, so the verdicts and details of neighbouring cards line up. */\n dialog.many .tiles > .account {\n display: grid;\n grid-row: span 3;\n grid-template-rows: subgrid;\n row-gap: 0;\n margin-top: 0;\n }\n /* The dates sit at the foot of the third part, so they line up too. */\n dialog.many .tiles .rest {\n display: flex;\n flex-direction: column;\n }\n dialog.many .tiles .rest > dl {\n margin-top: auto;\n }\n dialog.many .tiles .rest > :has(+ dl) {\n margin-bottom: 16px;\n }\n /* The site's card is one tile wide, centred over the two columns. */\n dialog.many .local {\n width: calc(50% - 6px);\n margin-inline: auto;\n text-align: center;\n }\n dialog.many .local h3,\n dialog.many .local .named {\n justify-content: center;\n }\n dialog.many .local .additional {\n padding-left: 0;\n }\n}\n";
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// Generated from evidence-dialog.css by scripts/css.mjs. Do not edit.
|
|
2
|
+
export default "* {\n box-sizing: border-box;\n}\ndialog {\n width: min(460px, calc(100vw - 32px));\n max-height: calc(100dvh - 40px);\n margin: auto;\n padding: 0;\n overflow: hidden;\n border: 1px solid #dce2de;\n border-radius: 16px;\n background: #fff;\n color: #23312b;\n box-shadow: 0 24px 90px #10201935;\n font:\n 13px/1.6 system-ui,\n sans-serif;\n}\n/* The pane scrolls inside the dialog, which clips its scrollbar to the rounded corners.\n Contain keeps a scroll past its end from scrolling the page behind. */\ndialog[open] {\n display: flex;\n flex-direction: column;\n}\n.pane {\n min-height: 0;\n padding: 24px;\n overflow-y: auto;\n overscroll-behavior: contain;\n}\ndialog::backdrop {\n background: #15271f66;\n}\nheader {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n margin-bottom: 20px;\n}\nh2 {\n margin: 0;\n font-size: 18px;\n font-weight: 650;\n letter-spacing: -0.3px;\n}\nbutton {\n width: 32px;\n height: 32px;\n border: 1px solid #dce2de;\n border-radius: 8px;\n background: #fff;\n color: #52645a;\n font:\n 20px system-ui,\n sans-serif;\n cursor: pointer;\n}\nbutton:hover {\n background: #f2f5f1;\n}\n.icon {\n display: grid;\n place-items: center;\n padding: 0;\n}\n.icon svg {\n width: 16px;\n height: 16px;\n}\n.title {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n.all {\n display: grid;\n place-items: center;\n width: 28px;\n height: 28px;\n border-radius: 8px;\n color: #52645a;\n}\n.all:hover {\n background: #f2f5f1;\n}\n.all svg {\n width: 16px;\n height: 16px;\n}\na {\n color: #245f43;\n text-underline-offset: 3px;\n overflow-wrap: anywhere;\n}\na:focus-visible,\nbutton:focus-visible,\nsummary:focus-visible {\n outline: 2px solid #357ce5;\n outline-offset: 3px;\n}\n/* An element given its own display would otherwise go on showing while hidden. */\n[hidden] {\n display: none !important;\n}\n.summary {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 16px;\n}\n.mark {\n width: 36px;\n height: 36px;\n flex-shrink: 0;\n}\n.provider {\n width: 14px;\n height: 14px;\n}\n.state {\n font-size: 14px;\n font-weight: 650;\n line-height: 1.4;\n}\n.muted {\n color: #6b786f;\n font-size: 12px;\n}\n.account {\n position: relative;\n}\n.veiled > :not(.checking) {\n filter: blur(4px);\n opacity: 0.55;\n user-select: none;\n}\n.checking {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n color: #23312b;\n font-size: 13px;\n font-weight: 600;\n}\n.spinner {\n width: 14px;\n height: 14px;\n border: 2px solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: spin 0.7s linear infinite;\n}\n@keyframes spin {\n to {\n transform: rotate(360deg);\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .spinner {\n animation: none;\n }\n}\n/* The \"signed\" pill after the verifier's name, one size smaller than the line it sits on. */\n.summary .signed {\n display: inline-block;\n margin-left: 5px;\n padding: 0 6px;\n border: 1px solid #cfdcd3;\n border-radius: 999px;\n font-size: 10px;\n font-weight: 550;\n line-height: 1.3;\n text-decoration: none;\n vertical-align: 1px;\n}\n.summary a.signed:hover {\n background: #f2f5f1;\n}\n.summary .signed.bad {\n border-color: #b3261e;\n color: #b3261e;\n font-weight: 650;\n cursor: help;\n}\n.caution {\n display: inline-block;\n margin-left: 3px;\n color: #a15c00;\n vertical-align: -1.5px;\n}\n.caution svg {\n display: block;\n width: 10px;\n height: 10px;\n}\n.account {\n padding: 14px 16px;\n border: 1px solid #e0e6df;\n border-radius: 10px;\n margin-top: 12px;\n}\n.account h3 {\n display: flex;\n align-items: center;\n gap: 6px;\n margin: 0 0 3px;\n color: #6b786f;\n font-size: 11px;\n font-weight: 550;\n}\n.account a,\n.account strong {\n font-weight: 650;\n font-size: 14px;\n}\n/* The account's own name carries that weight; a link inside a line of prose does not. */\n.summary a,\n.method a,\n.additional a {\n font: inherit;\n}\n.reference {\n margin-top: 2px;\n}\n/* A fingerprint in fours: it breaks between groups, and copies as one unbroken word. */\n.fingerprint {\n font-family: ui-monospace, monospace;\n text-wrap: balance;\n}\n.fingerprint span {\n display: inline-block;\n margin-right: 0.5ch;\n}\n.method {\n margin-top: 8px;\n}\n/* In the corner the heading leaves empty, apart from everything that was checked. */\n.listing {\n position: absolute;\n top: 12px;\n right: 14px;\n padding: 1px 8px;\n border: 1px solid #e0e6df;\n border-radius: 999px;\n color: #6b786f;\n font-size: 11px;\n font-weight: 550;\n}\n.listing.preferred {\n border-color: #245f43;\n color: #245f43;\n}\n/* Quiet, as its pill is: the surface and the name step back, and the controls do not. */\n.account.retired {\n background: #f6f8f5;\n}\n.account.retired .identity > a,\n.account.retired .identity > strong,\n.account.retired .named > a,\n.account.retired .named > strong {\n color: #6b786f;\n}\n/* The name, then a dot and the account's creation date. The date wraps under a long name. */\n/* Clipped 4px left of the text, which hides the dot when the date wraps to a new line. */\n.named {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: 0 14px;\n margin-left: -4px;\n padding-left: 4px;\n overflow-x: clip;\n}\n.since {\n position: relative;\n white-space: nowrap;\n}\n/* The dot sits in the gap before the date. */\n.since::before {\n content: '·';\n position: absolute;\n left: -7px;\n transform: translateX(-50%);\n}\n.account.retired .mark {\n opacity: 0.45;\n}\n.group {\n margin: 20px 0 0;\n color: #6b786f;\n font-size: 11px;\n font-weight: 550;\n}\nsummary.group {\n display: flex;\n align-items: center;\n gap: 4px;\n width: fit-content;\n border-radius: 4px;\n list-style: none;\n cursor: pointer;\n}\nsummary::-webkit-details-marker {\n display: none;\n}\n.fold {\n display: flex;\n align-items: center;\n gap: 4px;\n width: auto;\n height: auto;\n padding: 2px 8px 2px 4px;\n margin-left: -4px;\n border: 0;\n background: none;\n font:\n 550 12px/1.6 system-ui,\n sans-serif;\n}\n.fold svg,\nsummary svg {\n width: 12px;\n height: 12px;\n}\n.fold[aria-expanded='true'] svg,\ndetails[open] > summary svg {\n transform: rotate(90deg);\n}\n@media (prefers-reduced-motion: no-preference) {\n .fold svg,\n summary svg {\n transition: transform 0.15s;\n }\n}\n/* The fold and what the holder may do share a line, until a question needs the whole of it. */\n.bar {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n margin-top: 10px;\n}\n.account .bar .row {\n margin: 0 0 0 auto;\n}\n.additional {\n margin-top: 2px;\n padding-left: 12px;\n}\n.joiner {\n display: block;\n width: 20px;\n height: 20px;\n margin: 8px auto -4px;\n color: #90a096;\n}\ndl {\n margin: 16px 0 0;\n padding-top: 12px;\n border-top: 1px solid #e5e9e3;\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 5px 16px;\n font-size: 11px;\n}\ndt {\n color: #6b786f;\n}\ndd {\n margin: 0;\n text-align: right;\n overflow-wrap: anywhere;\n}\n.explanation {\n margin-top: 16px;\n}\n.action {\n width: auto;\n height: auto;\n padding: 9px 14px;\n font:\n 600 13px/1.3 system-ui,\n sans-serif;\n color: #23312b;\n text-align: left;\n}\n.action.primary {\n border-color: #245f43;\n background: #245f43;\n color: #fff;\n}\n.action.primary:hover {\n background: #1d4f37;\n}\n.action:disabled {\n opacity: 0.6;\n cursor: progress;\n}\n.action.danger:hover {\n border-color: #b3261e;\n background: #fdecea;\n color: #b3261e;\n}\n.row {\n display: flex;\n justify-content: end;\n gap: 8px;\n margin-top: 16px;\n}\n.account .row {\n align-items: center;\n justify-content: start;\n flex-wrap: wrap;\n margin-top: 12px;\n}\n.account .action {\n padding: 5px 10px;\n font-size: 12px;\n}\n.account .action.dots {\n display: grid;\n place-items: center;\n align-self: stretch;\n width: 30px;\n padding: 0;\n}\n.dots svg {\n width: 16px;\n height: 16px;\n}\n/* Placed under its button each time it opens, above the dialog and clipped by none of it. */\n.menu {\n position: fixed;\n inset: auto;\n margin: 0;\n min-width: 170px;\n padding: 6px;\n border: 1px solid #dce2de;\n border-radius: 10px;\n background: #fff;\n color: #23312b;\n box-shadow: 0 12px 40px #10201930;\n}\n.menu:popover-open {\n display: grid;\n}\n.account .menu .action {\n border: 0;\n border-radius: 6px;\n padding: 6px 10px;\n font-weight: 550;\n}\n.menu .danger {\n color: #b3261e;\n}\n.menu hr {\n width: 100%;\n margin: 4px 0;\n border: 0;\n border-top: 1px solid #e5e9e3;\n}\nfooter {\n display: flex;\n align-items: center;\n justify-content: start;\n gap: 6px;\n margin: 20px 0 -8px;\n color: #9aa9a0;\n font-size: 11px;\n}\nfooter a {\n display: flex;\n color: inherit;\n}\n.logo {\n display: block;\n height: 13px;\n}\n/* Four or more accounts on a wide screen: two equal cards to a row. */\n@media (min-width: 960px) {\n dialog.many {\n width: min(900px, calc(100vw - 32px));\n }\n dialog.many .tiles {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n margin-top: 12px;\n }\n /* Each card is three rows of the grid: who, its verdict, and the rest. A row is as tall as\n the taller card's part, so the verdicts and details of neighbouring cards line up. */\n dialog.many .tiles > .account {\n display: grid;\n grid-row: span 3;\n grid-template-rows: subgrid;\n row-gap: 0;\n margin-top: 0;\n }\n /* The dates sit at the foot of the third part, so they line up too. */\n dialog.many .tiles .rest {\n display: flex;\n flex-direction: column;\n }\n dialog.many .tiles .rest > dl {\n margin-top: auto;\n }\n dialog.many .tiles .rest > :has(+ dl) {\n margin-bottom: 16px;\n }\n /* The site's card is one tile wide, centred over the two columns. */\n dialog.many .local {\n width: calc(50% - 6px);\n margin-inline: auto;\n text-align: center;\n }\n dialog.many .local h3,\n dialog.many .local .named {\n justify-content: center;\n }\n dialog.many .local .additional {\n padding-left: 0;\n }\n}\n";
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { type Evidence, type ExternalAccount } from '../core/index.js';
|
|
2
2
|
/** The logotype as a link to the project, as both dialogs carry it in their footer. */
|
|
3
3
|
export declare function stampLink(): HTMLAnchorElement;
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Stops the page behind an open dialog from scrolling, and puts back the
|
|
6
|
+
* page's own inline styles once the last open dialog closes.
|
|
7
|
+
*/
|
|
8
|
+
export declare function holdPage(dialog: HTMLDialogElement): void;
|
|
9
|
+
export declare const styles: string;
|
|
5
10
|
export declare function node<K extends keyof HTMLElementTagNameMap>(tag: K, text?: string, className?: string): HTMLElementTagNameMap[K];
|
|
6
11
|
/**
|
|
7
12
|
* Points an anchor out of the dialog. Every link here opens in a new tab: the dialog is
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { attestationLabel, externalId, externalIdGroups, externalLink, externalName, externalSince, lastProved, localSide, proofTitle, statusLabel, } from '../core/index.js';
|
|
2
2
|
import { markStyles, verificationMark } from './mark.js';
|
|
3
|
+
import dialogCss from './evidence-dialog.css.js';
|
|
3
4
|
import { providerMark } from './provider-mark.js';
|
|
4
5
|
import { onSignatureResult, signature, standing } from './signed.js';
|
|
5
6
|
import { projectUrl, verilyLogo } from './logo.js';
|
|
@@ -11,120 +12,37 @@ export function stampLink() {
|
|
|
11
12
|
return link;
|
|
12
13
|
}
|
|
13
14
|
const openDialogs = new WeakMap();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }
|
|
46
|
-
/* The "signed" pill after the verifier's name, one size smaller than the line it sits on. */
|
|
47
|
-
.summary .signed { display: inline-block; margin-left: 5px; padding: 0 6px; border: 1px solid #cfdcd3; border-radius: 999px; font-size: 10px; font-weight: 550; line-height: 1.3; text-decoration: none; vertical-align: 1px; }
|
|
48
|
-
.summary a.signed:hover { background: #f2f5f1; }
|
|
49
|
-
.summary .signed.bad { border-color: #b3261e; color: #b3261e; font-weight: 650; cursor: help; }
|
|
50
|
-
.caution { display: inline-block; margin-left: 3px; color: #a15c00; vertical-align: -1.5px; }
|
|
51
|
-
.caution svg { display: block; width: 10px; height: 10px; }
|
|
52
|
-
.account { padding: 14px 16px; border: 1px solid #e0e6df; border-radius: 10px; margin-top: 12px; }
|
|
53
|
-
.account h3 { display: flex; align-items: center; gap: 6px; margin: 0 0 3px; color: #6b786f; font-size: 11px; font-weight: 550; }
|
|
54
|
-
.account a, .account strong { font-weight: 650; font-size: 14px; }
|
|
55
|
-
/* The account's own name carries that weight; a link inside a line of prose does not. */
|
|
56
|
-
.summary a, .method a, .additional a { font: inherit; }
|
|
57
|
-
.reference { margin-top: 2px; }
|
|
58
|
-
/* A fingerprint in fours: it breaks between groups, and copies as one unbroken word. */
|
|
59
|
-
.fingerprint { font-family: ui-monospace, monospace; text-wrap: balance; }
|
|
60
|
-
.fingerprint span { display: inline-block; margin-right: .5ch; }
|
|
61
|
-
.method { margin-top: 8px; }
|
|
62
|
-
/* In the corner the heading leaves empty, apart from everything that was checked. */
|
|
63
|
-
.listing { position: absolute; top: 12px; right: 14px; padding: 1px 8px; border: 1px solid #e0e6df; border-radius: 999px; color: #6b786f; font-size: 11px; font-weight: 550; }
|
|
64
|
-
.listing.preferred { border-color: #245f43; color: #245f43; }
|
|
65
|
-
/* Quiet, as its pill is: the surface and the name step back, and the controls do not. */
|
|
66
|
-
.account.retired { background: #f6f8f5; }
|
|
67
|
-
.account.retired .identity > a, .account.retired .identity > strong, .account.retired .named > a, .account.retired .named > strong { color: #6b786f; }
|
|
68
|
-
/* The name, then a dot and the account's creation date. The date wraps under a long name. */
|
|
69
|
-
/* Clipped 4px left of the text, which hides the dot when the date wraps to a new line. */
|
|
70
|
-
.named { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 14px; margin-left: -4px; padding-left: 4px; overflow-x: clip; }
|
|
71
|
-
.since { position: relative; white-space: nowrap; }
|
|
72
|
-
/* The dot sits in the gap before the date. */
|
|
73
|
-
.since::before { content: "·"; position: absolute; left: -7px; transform: translateX(-50%); }
|
|
74
|
-
.account.retired .mark { opacity: .45; }
|
|
75
|
-
.group { margin: 20px 0 0; color: #6b786f; font-size: 11px; font-weight: 550; }
|
|
76
|
-
summary.group { display: flex; align-items: center; gap: 4px; width: fit-content; border-radius: 4px; list-style: none; cursor: pointer; }
|
|
77
|
-
summary::-webkit-details-marker { display: none; }
|
|
78
|
-
.fold { display: flex; align-items: center; gap: 4px; width: auto; height: auto; padding: 2px 8px 2px 4px; margin-left: -4px; border: 0; background: none; font: 550 12px/1.6 system-ui, sans-serif; }
|
|
79
|
-
.fold svg, summary svg { width: 12px; height: 12px; }
|
|
80
|
-
.fold[aria-expanded="true"] svg, details[open] > summary svg { transform: rotate(90deg); }
|
|
81
|
-
@media (prefers-reduced-motion: no-preference) { .fold svg, summary svg { transition: transform .15s; } }
|
|
82
|
-
/* The fold and what the holder may do share a line, until a question needs the whole of it. */
|
|
83
|
-
.bar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
|
|
84
|
-
.account .bar .row { margin: 0 0 0 auto; }
|
|
85
|
-
.additional { margin-top: 2px; padding-left: 12px; }
|
|
86
|
-
.joiner { display: block; width: 20px; height: 20px; margin: 8px auto -4px; color: #90a096; }
|
|
87
|
-
dl { margin: 16px 0 0; padding-top: 12px; border-top: 1px solid #e5e9e3; display: grid; grid-template-columns: auto 1fr; gap: 5px 16px; font-size: 11px; }
|
|
88
|
-
dt { color: #6b786f; }
|
|
89
|
-
dd { margin: 0; text-align: right; overflow-wrap: anywhere; }
|
|
90
|
-
.explanation { margin-top: 16px; }
|
|
91
|
-
.action { width: auto; height: auto; padding: 9px 14px; font: 600 13px/1.3 system-ui, sans-serif; color: #23312b; text-align: left; }
|
|
92
|
-
.action.primary { border-color: #245f43; background: #245f43; color: #fff; }
|
|
93
|
-
.action.primary:hover { background: #1d4f37; }
|
|
94
|
-
.action:disabled { opacity: .6; cursor: progress; }
|
|
95
|
-
.action.danger:hover { border-color: #b3261e; background: #fdecea; color: #b3261e; }
|
|
96
|
-
.row { display: flex; justify-content: end; gap: 8px; margin-top: 16px; }
|
|
97
|
-
.account .row { align-items: center; justify-content: start; flex-wrap: wrap; margin-top: 12px; }
|
|
98
|
-
.account .action { padding: 5px 10px; font-size: 12px; }
|
|
99
|
-
.account .action.dots { display: grid; place-items: center; align-self: stretch; width: 30px; padding: 0; }
|
|
100
|
-
.dots svg { width: 16px; height: 16px; }
|
|
101
|
-
/* Placed under its button each time it opens, above the dialog and clipped by none of it. */
|
|
102
|
-
.menu { position: fixed; inset: auto; margin: 0; min-width: 170px; padding: 6px; border: 1px solid #dce2de; border-radius: 10px; background: #fff; color: #23312b; box-shadow: 0 12px 40px #10201930; }
|
|
103
|
-
.menu:popover-open { display: grid; }
|
|
104
|
-
.account .menu .action { border: 0; border-radius: 6px; padding: 6px 10px; font-weight: 550; }
|
|
105
|
-
.menu .danger { color: #b3261e; }
|
|
106
|
-
.menu hr { width: 100%; margin: 4px 0; border: 0; border-top: 1px solid #e5e9e3; }
|
|
107
|
-
footer { display: flex; align-items: center; justify-content: start; gap: 6px; margin: 20px 0 -8px; color: #9aa9a0; font-size: 11px; }
|
|
108
|
-
footer a { display: flex; color: inherit; }
|
|
109
|
-
.logo { display: block; height: 13px; }
|
|
110
|
-
/* Four or more accounts on a wide screen: two equal cards to a row. */
|
|
111
|
-
@media (min-width: 960px) {
|
|
112
|
-
dialog.many { width: min(900px, calc(100vw - 32px)); }
|
|
113
|
-
dialog.many .tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 12px; }
|
|
114
|
-
/* Each card is three rows of the grid: who, its verdict, and the rest. A row is as tall as
|
|
115
|
-
the taller card's part, so the verdicts and details of neighbouring cards line up. */
|
|
116
|
-
dialog.many .tiles > .account { display: grid; grid-row: span 3; grid-template-rows: subgrid; row-gap: 0; margin-top: 0; }
|
|
117
|
-
/* The dates sit at the foot of the third part, so they line up too. */
|
|
118
|
-
dialog.many .tiles .rest { display: flex; flex-direction: column; }
|
|
119
|
-
dialog.many .tiles .rest > dl { margin-top: auto; }
|
|
120
|
-
dialog.many .tiles .rest > :has(+ dl) { margin-bottom: 16px; }
|
|
121
|
-
/* The site's card is one tile wide, centred over the two columns. */
|
|
122
|
-
dialog.many .local { width: calc(50% - 6px); margin-inline: auto; text-align: center; }
|
|
123
|
-
dialog.many .local h3, dialog.many .local .named { justify-content: center; }
|
|
124
|
-
dialog.many .local .additional { padding-left: 0; }
|
|
125
|
-
}
|
|
126
|
-
${markStyles}
|
|
127
|
-
`;
|
|
15
|
+
let holds = 0;
|
|
16
|
+
let release = () => { };
|
|
17
|
+
/**
|
|
18
|
+
* Stops the page behind an open dialog from scrolling, and puts back the
|
|
19
|
+
* page's own inline styles once the last open dialog closes.
|
|
20
|
+
*/
|
|
21
|
+
export function holdPage(dialog) {
|
|
22
|
+
if (holds++ === 0) {
|
|
23
|
+
const { style, clientWidth } = document.documentElement;
|
|
24
|
+
// Longhands, since the overflow shorthand reads empty when only one axis is set.
|
|
25
|
+
const held = ['overflow-x', 'overflow-y', 'scrollbar-gutter'];
|
|
26
|
+
const saved = held.map((name) => [
|
|
27
|
+
style.getPropertyValue(name),
|
|
28
|
+
style.getPropertyPriority(name),
|
|
29
|
+
]);
|
|
30
|
+
// Keep the space of a hidden scrollbar so the page does not shift sideways.
|
|
31
|
+
if (innerWidth > clientWidth)
|
|
32
|
+
style.setProperty('scrollbar-gutter', 'stable', 'important');
|
|
33
|
+
style.setProperty('overflow-x', 'hidden', 'important');
|
|
34
|
+
style.setProperty('overflow-y', 'hidden', 'important');
|
|
35
|
+
release = () => held.forEach((name, i) => {
|
|
36
|
+
const [value, priority] = saved[i];
|
|
37
|
+
if (value)
|
|
38
|
+
style.setProperty(name, value, priority);
|
|
39
|
+
else
|
|
40
|
+
style.removeProperty(name);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
dialog.addEventListener('close', () => --holds === 0 && release(), { once: true });
|
|
44
|
+
}
|
|
45
|
+
export const styles = dialogCss + markStyles;
|
|
128
46
|
export function node(tag, text = '', className = '') {
|
|
129
47
|
const element = document.createElement(tag);
|
|
130
48
|
element.textContent = text;
|
|
@@ -1023,6 +941,7 @@ detailsOpen = false) {
|
|
|
1023
941
|
}, { once: true });
|
|
1024
942
|
if (opened && [opened].flat().length)
|
|
1025
943
|
draw(opened);
|
|
944
|
+
holdPage(dialog);
|
|
1026
945
|
dialog.showModal();
|
|
1027
946
|
void refresh();
|
|
1028
947
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: "@media (prefers-reduced-motion: no-preference) {\n .mark.current path:last-child {\n animation: verily-draw 0.8s cubic-bezier(0.65, 0, 0.35, 1) 0.1s backwards;\n }\n}\n@keyframes verily-draw {\n from {\n stroke-dasharray: 0 176;\n }\n to {\n stroke-dasharray: 108 176;\n }\n}\n";
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// Generated from mark.css by scripts/css.mjs. Do not edit.
|
|
2
|
+
export default "@media (prefers-reduced-motion: no-preference) {\n .mark.current path:last-child {\n animation: verily-draw 0.8s cubic-bezier(0.65, 0, 0.35, 1) 0.1s backwards;\n }\n}\n@keyframes verily-draw {\n from {\n stroke-dasharray: 0 176;\n }\n to {\n stroke-dasharray: 108 176;\n }\n}\n";
|
package/dist/browser/mark.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type { MarkTone };
|
|
|
4
4
|
* The check draws itself in from its left end once the verification holds: when a pill's
|
|
5
5
|
* mark first becomes `current`, and each time the dialog opens on a current one.
|
|
6
6
|
*/
|
|
7
|
-
export declare const markStyles = "
|
|
7
|
+
export declare const markStyles = "@media (prefers-reduced-motion: no-preference) {\n .mark.current path:last-child {\n animation: verily-draw 0.8s cubic-bezier(0.65, 0, 0.35, 1) 0.1s backwards;\n }\n}\n@keyframes verily-draw {\n from {\n stroke-dasharray: 0 176;\n }\n to {\n stroke-dasharray: 108 176;\n }\n}\n";
|
|
8
8
|
/** Repaints a mark in place, so answering a check never replaces the drawing. */
|
|
9
9
|
export declare function paintMark(svg: SVGSVGElement, tone: MarkTone): void;
|
|
10
10
|
/** V2: the green check fills its section of the symmetric red V. */
|
package/dist/browser/mark.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { tones, verification } from '../marks.js';
|
|
2
|
+
import markCss from './mark.css.js';
|
|
2
3
|
/**
|
|
3
4
|
* The check draws itself in from its left end once the verification holds: when a pill's
|
|
4
5
|
* mark first becomes `current`, and each time the dialog opens on a current one.
|
|
5
6
|
*/
|
|
6
|
-
export const markStyles =
|
|
7
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
8
|
-
.mark.current path:last-child { animation: verily-draw .8s cubic-bezier(.65, 0, .35, 1) .1s backwards; }
|
|
9
|
-
}
|
|
10
|
-
@keyframes verily-draw { from { stroke-dasharray: 0 176; } to { stroke-dasharray: 108 176; } }
|
|
11
|
-
`;
|
|
7
|
+
export const markStyles = markCss;
|
|
12
8
|
/** Repaints a mark in place, so answering a check never replaces the drawing. */
|
|
13
9
|
export function paintMark(svg, tone) {
|
|
14
10
|
const colors = tones[tone];
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: ":root {\n --ink: #23312b;\n --muted: #6b786f;\n --line: #dce2de;\n --surface: #fff;\n --raised: #f7f9f7;\n --accent: #245f43;\n --warn: #a4442b;\n --caution: #b07a12;\n --syntax-tag: #245f43;\n --syntax-attr: #8a5a0b;\n --syntax-string: #2b5f8a;\n color-scheme: light dark;\n accent-color: var(--accent);\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --ink: #dde5e0;\n --muted: #96a39b;\n --line: #2f3b35;\n --surface: #161a18;\n --raised: #1d2220;\n --accent: #7fc4a2;\n --warn: #e59a82;\n --caution: #d9a843;\n --syntax-tag: #7fc4a2;\n --syntax-attr: #d9a843;\n --syntax-string: #8fbbe0;\n }\n}\n\n* {\n box-sizing: border-box;\n}\n\nbody {\n margin: 0;\n padding: 0 1rem 4rem;\n background: var(--surface);\n color: var(--ink);\n font:\n 16px/1.65 system-ui,\n -apple-system,\n 'Segoe UI',\n sans-serif;\n text-rendering: optimizeLegibility;\n}\n\nmain {\n max-width: 34rem;\n margin: 0 auto;\n padding-top: 3rem;\n}\n\n/*\n * The logotype, above the page's own heading, on its own dark plate. Sized so the word is\n * the height it was without the plate, whose margin makes the box that much taller. The\n * word is white in either scheme: the plate is what it is read against, not the page.\n */\n.logo {\n display: block;\n height: 2.5rem;\n margin-bottom: 1.5rem;\n}\n\nh1 {\n margin: 0 0 1.75rem;\n padding-bottom: 1rem;\n border-bottom: 1px solid var(--line);\n font-size: 1.5rem;\n font-weight: 620;\n letter-spacing: -0.02em;\n line-height: 1.25;\n}\n\n/*\n * A page holding one thing needs no rule under its heading. The rule divides a heading\n * from the record beneath it, and a page that only asks for a key has no record to divide.\n */\nmain.single h1 {\n margin-bottom: 1.25rem;\n padding-bottom: 0;\n border-bottom: none;\n}\n\n/*\n * A page holding one thing sits in the middle of the window rather than at its top,\n * where it would leave most of the window empty below it.\n */\nbody:has(main.single) {\n display: flex;\n min-height: 100vh;\n min-height: 100dvh;\n}\nmain.single {\n width: 100%;\n max-width: 24rem;\n margin: auto;\n padding-bottom: 3rem;\n}\n\nh2 {\n margin: 2rem 0 0.75rem;\n font-size: 1.125rem;\n font-weight: 620;\n letter-spacing: -0.01em;\n}\n\nh3 {\n margin: 0 0 0.5rem;\n font-size: 1rem;\n font-weight: 620;\n}\n\np {\n margin: 0 0 1rem;\n overflow-wrap: break-word;\n}\n\na {\n color: var(--accent);\n text-underline-offset: 3px;\n overflow-wrap: anywhere;\n}\n\na:hover {\n text-decoration-thickness: 2px;\n}\n\n/*\n * Something to be run or published exactly as written. Its own line breaks are kept and a\n * long line is soft-wrapped rather than cut off: a soft wrap is drawn, not inserted, so\n * what is copied is still character for character what was written. Selecting inside the\n * block takes all of it, since half a command is worse than none.\n */\npre {\n position: relative;\n margin: 0 0 1rem;\n /* Room along the top line for the copy button, which sits over the block rather than in it. */\n padding: 0.75rem 5.25rem 0.75rem 0.875rem;\n border: 1px solid var(--line);\n border-radius: 6px;\n background: var(--raised);\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n user-select: all;\n}\n\ncode {\n font:\n 0.8125rem/1.6 ui-monospace,\n SFMono-Regular,\n Menlo,\n monospace;\n}\n\n/* Highlighted HTML, marked up by highlight.js. Brackets stay muted so the names read first. */\n.hljs-tag {\n color: var(--muted);\n}\n.hljs-name {\n color: var(--syntax-tag);\n}\n.hljs-attr {\n color: var(--syntax-attr);\n}\n.hljs-string {\n color: var(--syntax-string);\n}\n\n/* Made by a script, so it exists only where it works. Quiet: the block is the point. */\n.copy {\n position: absolute;\n top: 0.5rem;\n right: 0.5rem;\n padding: 0.1875rem 0.5rem;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--surface);\n color: var(--muted);\n font:\n 500 0.75rem/1.5 system-ui,\n sans-serif;\n user-select: none;\n}\n\n.copy:hover {\n color: var(--ink);\n border-color: var(--muted);\n}\n\n/*\n * The two sides of the link, which is what a reader came to read. Each fact about a side\n * gets its own line: what it is, what it is called, the identifier behind that, and how it\n * was shown. Everything else on the page qualifies the pair, so it is set quieter.\n */\n.side {\n margin-bottom: 0.75rem;\n padding: 0.875rem 1rem;\n border: 1px solid var(--line);\n border-radius: 8px;\n background: var(--raised);\n}\n\n/*\n * A subject's accounts are peers, so where there is room they sit two across. The page\n * that lists them is wider for it, and a narrow one keeps them in a single column.\n */\nmain:has(.accounts) {\n max-width: 54rem;\n}\n\n.accounts {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: 0.75rem;\n margin-bottom: 0.75rem;\n}\n\n.accounts .side {\n position: relative;\n display: flex;\n flex-direction: column;\n margin-bottom: 0;\n}\n\n/* How the holder lists the account, in the card's corner and out of the lines below. */\n.listing {\n position: absolute;\n top: 0.75rem;\n right: 0.875rem;\n padding: 0.0625rem 0.5rem;\n border: 1px solid var(--line);\n border-radius: 999px;\n color: var(--muted);\n font-size: 0.6875rem;\n font-weight: 550;\n}\n\n.listing.preferred {\n border-color: var(--accent);\n color: var(--accent);\n}\n\n/* The times sit at the foot of the card, so they line up along a row of uneven cards. */\n.accounts .side dl {\n margin-top: auto;\n}\n.accounts .rest {\n display: flex;\n flex-direction: column;\n}\n.accounts .rest > p:last-of-type {\n margin-bottom: 0.625rem;\n}\n\n/*\n * Two across, with the subject one card wide and centred above them. Every card on the\n * page is then the same width.\n */\n@media (min-width: 48rem) {\n .accounts {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n /* Each card is three rows of the grid: who, its verdict, and the rest. A row is as tall\n as the taller card's part, so the verdicts and times of neighbouring cards line up. */\n .accounts > .side {\n display: grid;\n grid-row: span 3;\n grid-template-rows: subgrid;\n row-gap: 0;\n }\n .subject {\n width: calc(50% - 0.375rem);\n margin-inline: auto;\n text-align: center;\n }\n .subject .name {\n justify-content: center;\n }\n}\n\n/* Between a subject and its accounts on the page that lists them. */\n.joiner {\n display: block;\n width: 1.25rem;\n height: 1.25rem;\n margin: 0 auto 0.75rem;\n color: var(--muted);\n}\n\n.who {\n margin: 0 0 0.125rem;\n color: var(--muted);\n font-size: 0.75rem;\n font-weight: 550;\n letter-spacing: 0.02em;\n}\n\n/* A provider's mark before its name, at the height of the letters. */\n.who .provider {\n display: inline-block;\n width: 0.875rem;\n height: 0.875rem;\n margin-right: 0.375rem;\n vertical-align: -0.125rem;\n}\n\n/* Clipped 4px left of the text, which hides the date's dot when the date wraps to a new line. */\n.name {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: 0 1rem;\n margin: 0 0 0 -4px;\n padding-left: 4px;\n overflow-x: clip;\n font-size: 1.0625rem;\n font-weight: 620;\n letter-spacing: -0.01em;\n}\n\n/* The account's creation date, after the name and a dot. It wraps under a long name. */\n.name .since {\n position: relative;\n color: var(--muted);\n font-size: 0.8125rem;\n font-weight: 400;\n letter-spacing: 0;\n white-space: nowrap;\n}\n\n/* The dot sits in the gap before the date. */\n.name .since::before {\n content: '\u00B7';\n position: absolute;\n left: -0.5rem;\n transform: translateX(-50%);\n}\n\n/* The name is the card's subject before it is a link, so the rule is drawn on hover. */\n.name a {\n text-decoration: none;\n}\n\n.name a:hover {\n text-decoration: underline;\n}\n\n.reference,\n.how {\n margin: 0;\n color: var(--muted);\n font-size: 0.8125rem;\n line-height: 1.5;\n}\n\n.identity + .how {\n margin-top: 0.5rem;\n}\n.fingerprint {\n font-family: ui-monospace, monospace;\n text-wrap: balance;\n}\n.fingerprint span {\n display: inline-block;\n margin-right: 0.5ch;\n}\n.how.additional {\n padding-left: 0.75rem;\n}\n/* The \"signed\" pill after the verifier's name, one size smaller than the line it sits on. */\n.how .signed-mark {\n display: inline-block;\n margin-left: 0.3125rem;\n padding: 0 0.375rem;\n border: 1px solid var(--line);\n border-radius: 999px;\n font-size: 0.625rem;\n font-weight: 550;\n line-height: 1.3;\n text-decoration: none;\n vertical-align: 0.0625rem;\n}\n\n.fine {\n color: var(--muted);\n font-size: 0.8125rem;\n line-height: 1.55;\n margin-bottom: 0.625rem;\n}\n\n/* That the record is signed is a fact about it, so it is set as one and not as fine print. */\n.signed {\n font-size: 0.875rem;\n line-height: 1.55;\n margin-bottom: 0.875rem;\n}\n/* Arrived at from a badge's \"signed\" link, so the reader is shown where they landed. */\n.signed:target {\n margin-inline: -0.75rem;\n padding: 0.625rem 0.75rem;\n border-radius: 8px;\n background: var(--raised);\n}\n\n/* Every time on the record in one column, so they can be compared rather than hunted for. */\ndl {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 0.3rem 1.5rem;\n margin: 1.25rem 0;\n padding-top: 1rem;\n border-top: 1px solid var(--line);\n font-size: 0.8125rem;\n}\n\ndt {\n color: var(--muted);\n}\n\ndd {\n margin: 0;\n font-variant-numeric: tabular-nums;\n}\n\n/* Inside a card the rule above it is the card's own edge, so the list needs none. */\nsection dl {\n margin: 0.5rem 0 1rem;\n padding-top: 0;\n border-top: 0;\n}\n\n/* Right under the heading, the heading's own rule is the one above it. */\nh1 + dl {\n padding-top: 0;\n border-top: 0;\n}\n.side dl {\n margin: 0.625rem 0 0;\n padding-top: 0.625rem;\n}\n\n/* The links under a record, ruled off from the record and its disclaimer. */\nfooter {\n margin-top: 1.25rem;\n padding-top: 1rem;\n border-top: 1px solid var(--line);\n}\nfooter > :last-child {\n margin-bottom: 0;\n}\n\nsection {\n margin: 0 0 1.25rem;\n padding: 1.25rem 1.375rem;\n border: 1px solid var(--line);\n border-radius: 10px;\n}\n\nsection > :last-child {\n margin-bottom: 0;\n}\n\n/* What holds a control that cannot be undone. */\nsection.danger,\nsection.caution {\n margin-top: 2rem;\n}\nsection.danger {\n border-color: var(--warn);\n}\n\n/* What holds a control that breaks something for a while, but can be recovered from. */\nsection.caution {\n border-color: var(--caution);\n}\nsection > h2:first-child {\n margin-top: 0;\n}\n\nform {\n margin: 0 0 1.5rem;\n}\n\nlabel {\n display: block;\n margin: 0 0 0.75rem;\n color: var(--muted);\n font-size: 0.8125rem;\n}\n\ninput,\ntextarea {\n display: block;\n width: 100%;\n margin-top: 0.375rem;\n padding: 0.5rem 0.625rem;\n border: 1px solid var(--line);\n border-radius: 6px;\n background: var(--surface);\n color: var(--ink);\n font:\n 0.875rem/1.5 ui-monospace,\n SFMono-Regular,\n Menlo,\n monospace;\n}\n\n/* Height comes from the rows attribute, which differs per box. */\ntextarea {\n resize: vertical;\n}\n\ninput:focus-visible,\ntextarea:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 1px;\n}\n\nbutton {\n padding: 0.5rem 1rem;\n border: 1px solid var(--accent);\n border-radius: 6px;\n background: var(--accent);\n color: var(--surface);\n font:\n 500 0.9375rem/1.4 system-ui,\n -apple-system,\n 'Segoe UI',\n sans-serif;\n cursor: pointer;\n}\n\n/* A link that starts something, drawn as a button. */\na.button {\n display: inline-block;\n padding: 0.5rem 1rem;\n border: 1px solid var(--accent);\n border-radius: 6px;\n background: var(--accent);\n color: var(--surface);\n font-weight: 500;\n font-size: 0.9375rem;\n line-height: 1.4;\n text-decoration: none;\n}\n\nbutton + button {\n margin-left: 0.5rem;\n border-color: var(--line);\n background: none;\n color: var(--ink);\n}\n\n/* The other choice beside a page's one action, such as Cancel. */\na.button.secondary {\n border-color: var(--line);\n background: none;\n color: var(--ink);\n}\n\n/* A page's action and its alternative, side by side. */\n.choice {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n margin-bottom: 1.5rem;\n}\n.choice form {\n margin: 0;\n}\n\n/* Ways to verify: one full-width button per method, none filled over the others. */\n.choices {\n display: grid;\n gap: 0.5rem;\n margin-bottom: 1.5rem;\n}\n.choices form {\n margin: 0;\n}\n\n.choices button {\n width: 100%;\n text-align: left;\n border-color: var(--line);\n background: var(--surface);\n color: var(--ink);\n}\n\n.choices button:hover {\n border-color: var(--muted);\n}\n\n/* A provider's mark before a button's label, at the height of the letters. */\nbutton .provider {\n width: 1rem;\n height: 1rem;\n margin-right: 0.5rem;\n vertical-align: -0.1875rem;\n}\n\n/* A control that ends something. Filled where it is the page's one action. */\nbutton.danger {\n border-color: var(--warn);\n background: var(--warn);\n}\n\n/*\n * A connection on the settings page: the account's card, then what can be done with it.\n * Its state sits in the card's corner, on the line that names the method.\n */\n.connection {\n position: relative;\n margin-bottom: 1rem;\n}\n\n.state {\n position: absolute;\n top: 0.75rem;\n right: 0.875rem;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n height: 1.625rem;\n padding: 0 0.75rem 0 0.5rem;\n border: 1px solid var(--line);\n border-radius: 999px;\n background: var(--surface);\n color: var(--ink);\n font-size: 0.8125rem;\n font-weight: 550;\n}\n\n/*\n * The V the badge draws, in the tone that says how the record stands. A record that claims\n * nothing is drawn faint in the text colour, as the badge draws it.\n */\n.verdict {\n flex-shrink: 0;\n width: 1rem;\n height: 1rem;\n}\n.verdict.pending {\n opacity: 0.45;\n}\n.how .verdict {\n width: 0.875rem;\n height: 0.875rem;\n margin-right: 0.3125rem;\n vertical-align: -0.125rem;\n}\n/* Cropped to its strokes and taller than the heading's letters, centred beside them. */\nh1:has(.verdict) {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n}\nh1 .verdict {\n width: auto;\n height: 2.25rem;\n}\n\n/*\n * In a state pill the V is cropped to its strokes and fills the pill's height, so its arms\n * and its foot meet the border. The pill's left padding keeps the arms clear of its curve.\n */\n.state .verdict {\n width: auto;\n height: 100%;\n}\n\n/*\n * A pill's line includes room for descenders below the letters, which leaves its text\n * sitting high. Trimmed to the letters, the padding alone sets the space above and below.\n * In a state pill the word alone is trimmed, so it centres on the V by its letters.\n */\n@supports (text-box: trim-both cap alphabetic) {\n .listing {\n padding: 0.3125rem 0.5rem;\n text-box: trim-both cap alphabetic;\n }\n .state > span {\n text-box: trim-both cap alphabetic;\n }\n}\n\n/* Links that sit together on one line, wrapping where there is no room. */\n.links {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 0.5rem 1.25rem;\n font-size: 0.875rem;\n}\n\n/* Under the account's id, with the facts about it rather than the controls. */\n.evidence {\n margin: 0.375rem 0 0;\n font-size: 0.8125rem;\n}\n\n.connection dl {\n margin-bottom: 0.75rem;\n}\n.connection .side > :last-child {\n margin-bottom: 0;\n}\n.connection pre {\n margin: 0.75rem 0;\n background: var(--surface);\n}\n.connection .actions {\n margin-top: 0.875rem;\n}\n\n/* The buttons for one connection in a row, wrapping where there is no room. */\n.actions {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n.actions form {\n display: contents;\n}\n\n.actions button,\n.actions a.button {\n margin: 0;\n padding: 0.3125rem 0.75rem;\n border-color: var(--line);\n background: var(--surface);\n color: var(--ink);\n font-size: 0.8125rem;\n}\n\n.actions button:hover,\n.actions a.button:hover {\n border-color: var(--muted);\n}\n\n/* The controls that cannot be undone, together at the foot of the card. */\n.ending {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: 0.5rem;\n margin-top: 0.75rem;\n padding-top: 0.75rem;\n border-top: 1px solid var(--line);\n}\n.ending form {\n margin: 0;\n}\n\n.ending button.danger {\n padding: 0.3125rem 0.75rem;\n background: var(--surface);\n color: var(--warn);\n font-size: 0.8125rem;\n}\n\nfieldset {\n margin: 0 0 1.25rem;\n padding: 1rem 1.125rem 0.25rem;\n border: 1px solid var(--line);\n border-radius: 8px;\n}\n\nlegend {\n padding: 0 0.375rem;\n color: var(--muted);\n font-size: 0.8125rem;\n}\n\nfieldset label {\n display: flex;\n align-items: baseline;\n gap: 0.5rem;\n color: var(--ink);\n font-size: 0.9375rem;\n}\n\nfieldset input {\n width: auto;\n margin: 0;\n}\n\nfieldset p {\n margin: 0.25rem 0 0.875rem 1.5rem;\n color: var(--muted);\n font-size: 0.8125rem;\n}\n";
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// Generated from style.css by scripts/css.mjs. Do not edit.
|
|
2
|
+
export default ":root {\n --ink: #23312b;\n --muted: #6b786f;\n --line: #dce2de;\n --surface: #fff;\n --raised: #f7f9f7;\n --accent: #245f43;\n --warn: #a4442b;\n --caution: #b07a12;\n --syntax-tag: #245f43;\n --syntax-attr: #8a5a0b;\n --syntax-string: #2b5f8a;\n color-scheme: light dark;\n accent-color: var(--accent);\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --ink: #dde5e0;\n --muted: #96a39b;\n --line: #2f3b35;\n --surface: #161a18;\n --raised: #1d2220;\n --accent: #7fc4a2;\n --warn: #e59a82;\n --caution: #d9a843;\n --syntax-tag: #7fc4a2;\n --syntax-attr: #d9a843;\n --syntax-string: #8fbbe0;\n }\n}\n\n* {\n box-sizing: border-box;\n}\n\nbody {\n margin: 0;\n padding: 0 1rem 4rem;\n background: var(--surface);\n color: var(--ink);\n font:\n 16px/1.65 system-ui,\n -apple-system,\n 'Segoe UI',\n sans-serif;\n text-rendering: optimizeLegibility;\n}\n\nmain {\n max-width: 34rem;\n margin: 0 auto;\n padding-top: 3rem;\n}\n\n/*\n * The logotype, above the page's own heading, on its own dark plate. Sized so the word is\n * the height it was without the plate, whose margin makes the box that much taller. The\n * word is white in either scheme: the plate is what it is read against, not the page.\n */\n.logo {\n display: block;\n height: 2.5rem;\n margin-bottom: 1.5rem;\n}\n\nh1 {\n margin: 0 0 1.75rem;\n padding-bottom: 1rem;\n border-bottom: 1px solid var(--line);\n font-size: 1.5rem;\n font-weight: 620;\n letter-spacing: -0.02em;\n line-height: 1.25;\n}\n\n/*\n * A page holding one thing needs no rule under its heading. The rule divides a heading\n * from the record beneath it, and a page that only asks for a key has no record to divide.\n */\nmain.single h1 {\n margin-bottom: 1.25rem;\n padding-bottom: 0;\n border-bottom: none;\n}\n\n/*\n * A page holding one thing sits in the middle of the window rather than at its top,\n * where it would leave most of the window empty below it.\n */\nbody:has(main.single) {\n display: flex;\n min-height: 100vh;\n min-height: 100dvh;\n}\nmain.single {\n width: 100%;\n max-width: 24rem;\n margin: auto;\n padding-bottom: 3rem;\n}\n\nh2 {\n margin: 2rem 0 0.75rem;\n font-size: 1.125rem;\n font-weight: 620;\n letter-spacing: -0.01em;\n}\n\nh3 {\n margin: 0 0 0.5rem;\n font-size: 1rem;\n font-weight: 620;\n}\n\np {\n margin: 0 0 1rem;\n overflow-wrap: break-word;\n}\n\na {\n color: var(--accent);\n text-underline-offset: 3px;\n overflow-wrap: anywhere;\n}\n\na:hover {\n text-decoration-thickness: 2px;\n}\n\n/*\n * Something to be run or published exactly as written. Its own line breaks are kept and a\n * long line is soft-wrapped rather than cut off: a soft wrap is drawn, not inserted, so\n * what is copied is still character for character what was written. Selecting inside the\n * block takes all of it, since half a command is worse than none.\n */\npre {\n position: relative;\n margin: 0 0 1rem;\n /* Room along the top line for the copy button, which sits over the block rather than in it. */\n padding: 0.75rem 5.25rem 0.75rem 0.875rem;\n border: 1px solid var(--line);\n border-radius: 6px;\n background: var(--raised);\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n user-select: all;\n}\n\ncode {\n font:\n 0.8125rem/1.6 ui-monospace,\n SFMono-Regular,\n Menlo,\n monospace;\n}\n\n/* Highlighted HTML, marked up by highlight.js. Brackets stay muted so the names read first. */\n.hljs-tag {\n color: var(--muted);\n}\n.hljs-name {\n color: var(--syntax-tag);\n}\n.hljs-attr {\n color: var(--syntax-attr);\n}\n.hljs-string {\n color: var(--syntax-string);\n}\n\n/* Made by a script, so it exists only where it works. Quiet: the block is the point. */\n.copy {\n position: absolute;\n top: 0.5rem;\n right: 0.5rem;\n padding: 0.1875rem 0.5rem;\n border: 1px solid var(--line);\n border-radius: 5px;\n background: var(--surface);\n color: var(--muted);\n font:\n 500 0.75rem/1.5 system-ui,\n sans-serif;\n user-select: none;\n}\n\n.copy:hover {\n color: var(--ink);\n border-color: var(--muted);\n}\n\n/*\n * The two sides of the link, which is what a reader came to read. Each fact about a side\n * gets its own line: what it is, what it is called, the identifier behind that, and how it\n * was shown. Everything else on the page qualifies the pair, so it is set quieter.\n */\n.side {\n margin-bottom: 0.75rem;\n padding: 0.875rem 1rem;\n border: 1px solid var(--line);\n border-radius: 8px;\n background: var(--raised);\n}\n\n/*\n * A subject's accounts are peers, so where there is room they sit two across. The page\n * that lists them is wider for it, and a narrow one keeps them in a single column.\n */\nmain:has(.accounts) {\n max-width: 54rem;\n}\n\n.accounts {\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n gap: 0.75rem;\n margin-bottom: 0.75rem;\n}\n\n.accounts .side {\n position: relative;\n display: flex;\n flex-direction: column;\n margin-bottom: 0;\n}\n\n/* How the holder lists the account, in the card's corner and out of the lines below. */\n.listing {\n position: absolute;\n top: 0.75rem;\n right: 0.875rem;\n padding: 0.0625rem 0.5rem;\n border: 1px solid var(--line);\n border-radius: 999px;\n color: var(--muted);\n font-size: 0.6875rem;\n font-weight: 550;\n}\n\n.listing.preferred {\n border-color: var(--accent);\n color: var(--accent);\n}\n\n/* The times sit at the foot of the card, so they line up along a row of uneven cards. */\n.accounts .side dl {\n margin-top: auto;\n}\n.accounts .rest {\n display: flex;\n flex-direction: column;\n}\n.accounts .rest > p:last-of-type {\n margin-bottom: 0.625rem;\n}\n\n/*\n * Two across, with the subject one card wide and centred above them. Every card on the\n * page is then the same width.\n */\n@media (min-width: 48rem) {\n .accounts {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n /* Each card is three rows of the grid: who, its verdict, and the rest. A row is as tall\n as the taller card's part, so the verdicts and times of neighbouring cards line up. */\n .accounts > .side {\n display: grid;\n grid-row: span 3;\n grid-template-rows: subgrid;\n row-gap: 0;\n }\n .subject {\n width: calc(50% - 0.375rem);\n margin-inline: auto;\n text-align: center;\n }\n .subject .name {\n justify-content: center;\n }\n}\n\n/* Between a subject and its accounts on the page that lists them. */\n.joiner {\n display: block;\n width: 1.25rem;\n height: 1.25rem;\n margin: 0 auto 0.75rem;\n color: var(--muted);\n}\n\n.who {\n margin: 0 0 0.125rem;\n color: var(--muted);\n font-size: 0.75rem;\n font-weight: 550;\n letter-spacing: 0.02em;\n}\n\n/* A provider's mark before its name, at the height of the letters. */\n.who .provider {\n display: inline-block;\n width: 0.875rem;\n height: 0.875rem;\n margin-right: 0.375rem;\n vertical-align: -0.125rem;\n}\n\n/* Clipped 4px left of the text, which hides the date's dot when the date wraps to a new line. */\n.name {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: 0 1rem;\n margin: 0 0 0 -4px;\n padding-left: 4px;\n overflow-x: clip;\n font-size: 1.0625rem;\n font-weight: 620;\n letter-spacing: -0.01em;\n}\n\n/* The account's creation date, after the name and a dot. It wraps under a long name. */\n.name .since {\n position: relative;\n color: var(--muted);\n font-size: 0.8125rem;\n font-weight: 400;\n letter-spacing: 0;\n white-space: nowrap;\n}\n\n/* The dot sits in the gap before the date. */\n.name .since::before {\n content: '·';\n position: absolute;\n left: -0.5rem;\n transform: translateX(-50%);\n}\n\n/* The name is the card's subject before it is a link, so the rule is drawn on hover. */\n.name a {\n text-decoration: none;\n}\n\n.name a:hover {\n text-decoration: underline;\n}\n\n.reference,\n.how {\n margin: 0;\n color: var(--muted);\n font-size: 0.8125rem;\n line-height: 1.5;\n}\n\n.identity + .how {\n margin-top: 0.5rem;\n}\n.fingerprint {\n font-family: ui-monospace, monospace;\n text-wrap: balance;\n}\n.fingerprint span {\n display: inline-block;\n margin-right: 0.5ch;\n}\n.how.additional {\n padding-left: 0.75rem;\n}\n/* The \"signed\" pill after the verifier's name, one size smaller than the line it sits on. */\n.how .signed-mark {\n display: inline-block;\n margin-left: 0.3125rem;\n padding: 0 0.375rem;\n border: 1px solid var(--line);\n border-radius: 999px;\n font-size: 0.625rem;\n font-weight: 550;\n line-height: 1.3;\n text-decoration: none;\n vertical-align: 0.0625rem;\n}\n\n.fine {\n color: var(--muted);\n font-size: 0.8125rem;\n line-height: 1.55;\n margin-bottom: 0.625rem;\n}\n\n/* That the record is signed is a fact about it, so it is set as one and not as fine print. */\n.signed {\n font-size: 0.875rem;\n line-height: 1.55;\n margin-bottom: 0.875rem;\n}\n/* Arrived at from a badge's \"signed\" link, so the reader is shown where they landed. */\n.signed:target {\n margin-inline: -0.75rem;\n padding: 0.625rem 0.75rem;\n border-radius: 8px;\n background: var(--raised);\n}\n\n/* Every time on the record in one column, so they can be compared rather than hunted for. */\ndl {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 0.3rem 1.5rem;\n margin: 1.25rem 0;\n padding-top: 1rem;\n border-top: 1px solid var(--line);\n font-size: 0.8125rem;\n}\n\ndt {\n color: var(--muted);\n}\n\ndd {\n margin: 0;\n font-variant-numeric: tabular-nums;\n}\n\n/* Inside a card the rule above it is the card's own edge, so the list needs none. */\nsection dl {\n margin: 0.5rem 0 1rem;\n padding-top: 0;\n border-top: 0;\n}\n\n/* Right under the heading, the heading's own rule is the one above it. */\nh1 + dl {\n padding-top: 0;\n border-top: 0;\n}\n.side dl {\n margin: 0.625rem 0 0;\n padding-top: 0.625rem;\n}\n\n/* The links under a record, ruled off from the record and its disclaimer. */\nfooter {\n margin-top: 1.25rem;\n padding-top: 1rem;\n border-top: 1px solid var(--line);\n}\nfooter > :last-child {\n margin-bottom: 0;\n}\n\nsection {\n margin: 0 0 1.25rem;\n padding: 1.25rem 1.375rem;\n border: 1px solid var(--line);\n border-radius: 10px;\n}\n\nsection > :last-child {\n margin-bottom: 0;\n}\n\n/* What holds a control that cannot be undone. */\nsection.danger,\nsection.caution {\n margin-top: 2rem;\n}\nsection.danger {\n border-color: var(--warn);\n}\n\n/* What holds a control that breaks something for a while, but can be recovered from. */\nsection.caution {\n border-color: var(--caution);\n}\nsection > h2:first-child {\n margin-top: 0;\n}\n\nform {\n margin: 0 0 1.5rem;\n}\n\nlabel {\n display: block;\n margin: 0 0 0.75rem;\n color: var(--muted);\n font-size: 0.8125rem;\n}\n\ninput,\ntextarea {\n display: block;\n width: 100%;\n margin-top: 0.375rem;\n padding: 0.5rem 0.625rem;\n border: 1px solid var(--line);\n border-radius: 6px;\n background: var(--surface);\n color: var(--ink);\n font:\n 0.875rem/1.5 ui-monospace,\n SFMono-Regular,\n Menlo,\n monospace;\n}\n\n/* Height comes from the rows attribute, which differs per box. */\ntextarea {\n resize: vertical;\n}\n\ninput:focus-visible,\ntextarea:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 1px;\n}\n\nbutton {\n padding: 0.5rem 1rem;\n border: 1px solid var(--accent);\n border-radius: 6px;\n background: var(--accent);\n color: var(--surface);\n font:\n 500 0.9375rem/1.4 system-ui,\n -apple-system,\n 'Segoe UI',\n sans-serif;\n cursor: pointer;\n}\n\n/* A link that starts something, drawn as a button. */\na.button {\n display: inline-block;\n padding: 0.5rem 1rem;\n border: 1px solid var(--accent);\n border-radius: 6px;\n background: var(--accent);\n color: var(--surface);\n font-weight: 500;\n font-size: 0.9375rem;\n line-height: 1.4;\n text-decoration: none;\n}\n\nbutton + button {\n margin-left: 0.5rem;\n border-color: var(--line);\n background: none;\n color: var(--ink);\n}\n\n/* The other choice beside a page's one action, such as Cancel. */\na.button.secondary {\n border-color: var(--line);\n background: none;\n color: var(--ink);\n}\n\n/* A page's action and its alternative, side by side. */\n.choice {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n margin-bottom: 1.5rem;\n}\n.choice form {\n margin: 0;\n}\n\n/* Ways to verify: one full-width button per method, none filled over the others. */\n.choices {\n display: grid;\n gap: 0.5rem;\n margin-bottom: 1.5rem;\n}\n.choices form {\n margin: 0;\n}\n\n.choices button {\n width: 100%;\n text-align: left;\n border-color: var(--line);\n background: var(--surface);\n color: var(--ink);\n}\n\n.choices button:hover {\n border-color: var(--muted);\n}\n\n/* A provider's mark before a button's label, at the height of the letters. */\nbutton .provider {\n width: 1rem;\n height: 1rem;\n margin-right: 0.5rem;\n vertical-align: -0.1875rem;\n}\n\n/* A control that ends something. Filled where it is the page's one action. */\nbutton.danger {\n border-color: var(--warn);\n background: var(--warn);\n}\n\n/*\n * A connection on the settings page: the account's card, then what can be done with it.\n * Its state sits in the card's corner, on the line that names the method.\n */\n.connection {\n position: relative;\n margin-bottom: 1rem;\n}\n\n.state {\n position: absolute;\n top: 0.75rem;\n right: 0.875rem;\n display: flex;\n align-items: center;\n gap: 0.375rem;\n height: 1.625rem;\n padding: 0 0.75rem 0 0.5rem;\n border: 1px solid var(--line);\n border-radius: 999px;\n background: var(--surface);\n color: var(--ink);\n font-size: 0.8125rem;\n font-weight: 550;\n}\n\n/*\n * The V the badge draws, in the tone that says how the record stands. A record that claims\n * nothing is drawn faint in the text colour, as the badge draws it.\n */\n.verdict {\n flex-shrink: 0;\n width: 1rem;\n height: 1rem;\n}\n.verdict.pending {\n opacity: 0.45;\n}\n.how .verdict {\n width: 0.875rem;\n height: 0.875rem;\n margin-right: 0.3125rem;\n vertical-align: -0.125rem;\n}\n/* Cropped to its strokes and taller than the heading's letters, centred beside them. */\nh1:has(.verdict) {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n}\nh1 .verdict {\n width: auto;\n height: 2.25rem;\n}\n\n/*\n * In a state pill the V is cropped to its strokes and fills the pill's height, so its arms\n * and its foot meet the border. The pill's left padding keeps the arms clear of its curve.\n */\n.state .verdict {\n width: auto;\n height: 100%;\n}\n\n/*\n * A pill's line includes room for descenders below the letters, which leaves its text\n * sitting high. Trimmed to the letters, the padding alone sets the space above and below.\n * In a state pill the word alone is trimmed, so it centres on the V by its letters.\n */\n@supports (text-box: trim-both cap alphabetic) {\n .listing {\n padding: 0.3125rem 0.5rem;\n text-box: trim-both cap alphabetic;\n }\n .state > span {\n text-box: trim-both cap alphabetic;\n }\n}\n\n/* Links that sit together on one line, wrapping where there is no room. */\n.links {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 0.5rem 1.25rem;\n font-size: 0.875rem;\n}\n\n/* Under the account's id, with the facts about it rather than the controls. */\n.evidence {\n margin: 0.375rem 0 0;\n font-size: 0.8125rem;\n}\n\n.connection dl {\n margin-bottom: 0.75rem;\n}\n.connection .side > :last-child {\n margin-bottom: 0;\n}\n.connection pre {\n margin: 0.75rem 0;\n background: var(--surface);\n}\n.connection .actions {\n margin-top: 0.875rem;\n}\n\n/* The buttons for one connection in a row, wrapping where there is no room. */\n.actions {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n.actions form {\n display: contents;\n}\n\n.actions button,\n.actions a.button {\n margin: 0;\n padding: 0.3125rem 0.75rem;\n border-color: var(--line);\n background: var(--surface);\n color: var(--ink);\n font-size: 0.8125rem;\n}\n\n.actions button:hover,\n.actions a.button:hover {\n border-color: var(--muted);\n}\n\n/* The controls that cannot be undone, together at the foot of the card. */\n.ending {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: 0.5rem;\n margin-top: 0.75rem;\n padding-top: 0.75rem;\n border-top: 1px solid var(--line);\n}\n.ending form {\n margin: 0;\n}\n\n.ending button.danger {\n padding: 0.3125rem 0.75rem;\n background: var(--surface);\n color: var(--warn);\n font-size: 0.8125rem;\n}\n\nfieldset {\n margin: 0 0 1.25rem;\n padding: 1rem 1.125rem 0.25rem;\n border: 1px solid var(--line);\n border-radius: 8px;\n}\n\nlegend {\n padding: 0 0.375rem;\n color: var(--muted);\n font-size: 0.8125rem;\n}\n\nfieldset label {\n display: flex;\n align-items: baseline;\n gap: 0.5rem;\n color: var(--ink);\n font-size: 0.9375rem;\n}\n\nfieldset input {\n width: auto;\n margin: 0;\n}\n\nfieldset p {\n margin: 0.25rem 0 0.875rem 1.5rem;\n color: var(--muted);\n font-size: 0.8125rem;\n}\n";
|