lowkey-doc 0.0.0-stage → 1.0.1

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.
@@ -0,0 +1,853 @@
1
+ // css.mjs - the whole stylesheet, inlined into the page.
2
+
3
+ export const CSS = `
4
+ *,*::before,*::after{box-sizing:border-box}
5
+ html{-webkit-text-size-adjust:100%}
6
+ :root{
7
+ color-scheme:dark;
8
+ --bg:#08090b; --ink:#f3f5f7; --muted:#9aa2ad; --faint:#5f6873; --line:rgba(255,255,255,.08);
9
+ --line-2:rgba(255,255,255,.16); --surface:#13161b; --surface-2:#1a1e24; --hover:rgba(255,255,255,.05);
10
+ /* the one accent: an electric blue, used for what is lit, current or alive */
11
+ --accent:#4da3ff; --accent-soft:rgba(77,163,255,.12); --accent-ring:rgba(77,163,255,.36);
12
+ /* a filled button is white with dark text */
13
+ --on-ink:#0b0d10; --ink-hover:#dde2e8;
14
+ --serif:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
15
+ --mono:ui-monospace,"JetBrains Mono","Cascadia Mono",SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
16
+ /* inline code */
17
+ --code-bg:rgba(255,255,255,.06); --code-bg-on-white:rgba(255,255,255,.07); --code-bg-table:rgba(255,255,255,.06);
18
+ --code-ring:rgba(255,255,255,.1); --code-ink:#d4e5ff; --code-var:#f0c674; --code-text:#d6dbe2;
19
+ /* Inter is vendored into the page as a variable font; everything after it is
20
+ the offline fallback. Swap this one line to re-face the whole dashboard. */
21
+ --font:Inter,"Avenir Next","Segoe UI",system-ui,-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif;
22
+ /* Emphasis is one notch of weight, not two: bold should read as emphasis
23
+ inside the sentence, not as a heavier typeface dropped into it. */
24
+ --semi:560; --bold:620;
25
+ --scroll:rgba(255,255,255,.12); --scroll-on:rgba(255,255,255,.22);
26
+ --rail:270px;
27
+ /* content column: --measure is the text width, --gutter the breathing room
28
+ either side of it. The column is centred in the main area. */
29
+ --measure:92ch; --gutter:46px;
30
+ --column:calc(var(--measure) + var(--gutter) * 2);
31
+ --margin:var(--gutter);
32
+ /* diagrams */
33
+ --dg-node:#1a1e24; --dg-node-line:#3a414c; --dg-edge:#6b7480; --dg-edge-text:#9aa2ad;
34
+ --dg-group:#0f1216; --dg-group-line:#262b33; --dg-hot:#4da3ff; --dg-hot-soft:#10284d;
35
+ --dg-tie:#3a414c; --dg-life:#2e343d; --dg-text-2:#b7c0cc;
36
+ /* entity tables: the header band and the row hover are washes laid over the
37
+ box, so a classDef colour on an entity still reads through them */
38
+ --dg-er-head:rgba(255,255,255,.035); --dg-er-head-hot:rgba(77,163,255,.08);
39
+ --dg-er-line:#262b33; --dg-er-hover:rgba(77,163,255,.1);
40
+ --dg-er-badge:#22272e; --dg-er-badge-line:#323842; --dg-er-badge-ink:#9aa2ad;
41
+ --dg-er-badge-pk:#2b323b; --dg-er-badge-pk-line:#424a56;
42
+ /* sequences: notes are the one warm surface, so an aside never reads as a message */
43
+ --dg-seq-num:#4b535e; --dg-seq-note:#221f14; --dg-seq-note-line:#4a4128; --dg-seq-note-ink:#e6d7a6;
44
+ }
45
+ /* Paper is white: printing goes back to ink on white rather than spend a
46
+ cartridge on the night sky, or lose the text when backgrounds are dropped. */
47
+ @media print{:root{color-scheme:light;
48
+ --bg:#fff; --ink:#141a26; --muted:#5f6674; --faint:#98a0ad; --line:#d7dbe2; --line-2:#c3c9d4;
49
+ --surface:#fff; --surface-2:#f2f3f6; --accent:#1d4ed8;
50
+ --code-bg:#f5f6f9; --code-bg-on-white:#f5f6f9; --code-bg-table:#f1f3f8; --code-ring:rgba(20,26,38,.1);
51
+ --code-ink:#3a3d78; --code-var:#7a5a1e; --code-text:#2a3040;
52
+ --dg-node:#fff; --dg-node-line:#c3c9d4; --dg-edge:#8a91a0; --dg-edge-text:#5f6674;
53
+ --dg-group:#f7f8fa; --dg-group-line:#dfe3ea; --dg-tie:#c3c9d4; --dg-life:#d3d8e0; --dg-text-2:#3a4150;
54
+ --dg-er-head:rgba(20,26,38,.045); --dg-er-line:#e7eaf0;
55
+ --dg-er-badge:#eef0f4; --dg-er-badge-line:#dce0e8; --dg-er-badge-ink:#5f6674;
56
+ --dg-er-badge-pk:#dfe4ee; --dg-er-badge-pk-line:#c8d0dd;
57
+ --dg-seq-num:#5f6674; --dg-seq-note:#fbf8ee; --dg-seq-note-line:#e7dfc4; --dg-seq-note-ink:#4f4a3a}
58
+ body::before{display:none}}
59
+ /* A project that fixes its margins (layout.margin) gives the column whatever
60
+ the reading area leaves once they are taken: --margin runs from the edge of
61
+ the area to the text, so the column's own gutter is inside it. Never wider
62
+ than the area, never so narrow the text stops reading as a column. */
63
+ :root.fixed-margin{--column:clamp(360px,calc(100% - var(--margin) * 2 + var(--gutter) * 2),100%)}
64
+ body{margin:0;background:var(--bg);color:var(--ink);
65
+ font:15px/1.7 var(--font);
66
+ font-optical-sizing:auto;
67
+ -webkit-font-smoothing:antialiased}
68
+ /* the night sky the page sits on: a blue glow in the top corner and two
69
+ sparse grids of stars, sized so they never line up into a visible pattern */
70
+ body::before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
71
+ background:
72
+ radial-gradient(55% 45% at 100% 0%,rgba(30,111,255,.11),transparent 70%),
73
+ radial-gradient(40% 40% at 0% 100%,rgba(30,111,255,.05),transparent 70%),
74
+ radial-gradient(circle,rgba(255,255,255,.14) 0 .7px,transparent 1.2px) 13px 29px/173px 157px,
75
+ radial-gradient(circle,rgba(255,255,255,.09) 0 .6px,transparent 1.1px) 61px 7px/241px 211px,
76
+ radial-gradient(circle,rgba(77,163,255,.16) 0 .6px,transparent 1.1px) 137px 71px/311px 283px}
77
+ a{color:inherit;text-decoration:none}
78
+ .layout{display:grid;grid-template-columns:var(--rail) 1fr;min-height:100vh}
79
+ .layout.collapsed{--rail:62px}
80
+
81
+ /* ---- scrollbars ----
82
+ Chrome and Safari take the ::-webkit rules. Firefox only understands the two
83
+ standard properties - and Chrome lets those override everything below - so
84
+ they are kept behind a Firefox-only @supports. */
85
+ ::-webkit-scrollbar{width:11px;height:11px}
86
+ ::-webkit-scrollbar-track{background:transparent}
87
+ ::-webkit-scrollbar-thumb{min-height:34px;border:3px solid transparent;border-radius:99px;
88
+ background:var(--scroll);background-clip:padding-box}
89
+ ::-webkit-scrollbar-thumb:hover{background:var(--scroll-on);background-clip:padding-box}
90
+ ::-webkit-scrollbar-thumb:active{background:var(--muted);background-clip:padding-box}
91
+ ::-webkit-scrollbar-corner{background:transparent}
92
+ @supports (-moz-appearance:none){*{scrollbar-width:thin;scrollbar-color:var(--scroll) transparent}}
93
+
94
+ /* ---- sidebar ---- */
95
+ .side{position:sticky;top:0;height:100vh;display:flex;flex-direction:column;
96
+ border-right:1px solid var(--line);overflow:hidden}
97
+ .side-top{padding:16px 14px 12px}
98
+ .brand{min-width:0}
99
+ /* the toggle shares a row with the title, so its 28px box - and the background
100
+ that appears under it on hover - is centred on the title's own line rather
101
+ than on the title-plus-subtitle block */
102
+ .brand-row{display:flex;align-items:center;gap:8px;min-width:0}
103
+ .brand-name{flex:1;min-width:0;height:28px;line-height:28px;font-size:14px;font-weight:var(--bold);
104
+ letter-spacing:-.012em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
105
+ .brand-name::before{content:"";display:inline-block;width:8px;height:8px;margin:0 9px 1px 2px;
106
+ border-radius:50%;background:var(--accent);box-shadow:0 0 10px 1px var(--accent-ring);vertical-align:middle}
107
+ .icon-btn{width:28px;height:28px;flex:none;display:grid;place-items:center;border:1px solid transparent;
108
+ border-radius:999px;background:none;color:var(--muted);cursor:pointer;
109
+ transition:background .15s,color .15s,border-color .15s}
110
+ .icon-btn:hover{background:var(--hover);color:var(--ink);border-color:var(--line)}
111
+ .icon-btn svg{width:15px;height:15px}
112
+ .search{margin:8px 14px 6px;position:relative}
113
+ .search input{width:100%;padding:7px 12px 7px 31px;border:1px solid var(--line);border-radius:999px;
114
+ background:var(--surface);color:var(--ink);font:inherit;font-size:13px;outline:none;
115
+ transition:border-color .15s,box-shadow .15s}
116
+ .search input::placeholder{color:var(--faint)}
117
+ .search input:focus{border-color:var(--accent-ring);box-shadow:0 0 0 3px var(--accent-soft)}
118
+ .search svg{position:absolute;left:12px;top:50%;transform:translateY(-50%);width:13px;height:13px;color:var(--faint)}
119
+ .nav{flex:1;overflow-y:auto;overflow-x:hidden;padding:2px 10px 24px}
120
+ .nav::-webkit-scrollbar{width:9px}
121
+ .nav-group{margin:16px 6px 6px;font-size:10px;font-weight:var(--bold);letter-spacing:.11em;
122
+ text-transform:uppercase;color:var(--faint)}
123
+ .nav-group:first-child{margin-top:2px}
124
+
125
+ /* documents: a flat row; the open one is an outlined pill, its number lit */
126
+ .item{position:relative;display:flex;align-items:center;gap:10px;padding:7px 10px 7px 14px;
127
+ border-radius:999px;cursor:pointer;color:var(--muted);transition:color .12s,background .12s,box-shadow .12s}
128
+ .item:hover{color:var(--ink);background:var(--hover)}
129
+ .item.active{color:var(--ink);background:rgba(255,255,255,.035);box-shadow:inset 0 0 0 1px var(--line-2)}
130
+ .num{flex:none;min-width:15px;font-size:11px;font-weight:var(--semi);color:var(--faint);
131
+ font-variant-numeric:tabular-nums;transition:color .12s}
132
+ .item:hover .num{color:var(--muted)}
133
+ .item.active .num{color:var(--accent)}
134
+ .item-title{flex:1;min-width:0;font-size:13.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
135
+ .item.active .item-title{font-weight:var(--semi)}
136
+ .caret{width:13px;height:13px;flex:none;color:var(--faint);opacity:0;
137
+ transition:transform .18s ease,opacity .15s}
138
+ .item:hover .caret,.item.active .caret{opacity:1}
139
+ .item.open .caret{transform:rotate(90deg)}
140
+
141
+ /* sections: a hairline rail with one node per heading */
142
+ .subs{display:none;position:relative;margin:2px 0 10px;padding:2px 0}
143
+ .subs::before{content:"";position:absolute;left:20px;top:9px;bottom:9px;width:1px;background:var(--line)}
144
+ .item.open+.subs{display:block}
145
+ .sub{position:relative;display:flex;gap:8px;padding:4px 10px 4px 33px;border-radius:999px;
146
+ font-size:12.5px;color:var(--muted);cursor:pointer;transition:color .12s,background .12s}
147
+ .sub::after{content:"";position:absolute;left:17px;top:50%;width:7px;height:7px;margin-top:-3.5px;
148
+ border-radius:50%;background:var(--bg);border:1px solid var(--line);transition:.15s}
149
+ .sub:hover{color:var(--ink);background:var(--hover)}
150
+ .sub:hover::after{border-color:var(--muted)}
151
+ .sub.current{color:var(--ink);font-weight:var(--semi)}
152
+ .sub.current::after{background:var(--accent);border-color:var(--accent);box-shadow:0 0 8px var(--accent-ring)}
153
+ .sub-n{flex:none;font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}
154
+ .sub.current .sub-n{color:var(--muted)}
155
+ .sub-t{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
156
+
157
+ .collapsed .brand-name,.collapsed .search,.collapsed .nav-group,
158
+ .collapsed .item-title,.collapsed .caret,.collapsed .foot-lbl{display:none}
159
+ .collapsed .subs,.collapsed .item.open+.subs{display:none}
160
+ .collapsed .side-top{padding:16px 0 12px}
161
+ .collapsed .brand-row{justify-content:center}
162
+ .collapsed .nav{padding:2px 8px;scrollbar-width:none}
163
+ .collapsed .nav::-webkit-scrollbar{display:none}
164
+ .collapsed .item{justify-content:center;padding:8px 0;margin-bottom:2px}
165
+ .collapsed .num{min-width:0;font-size:12px}
166
+ .collapsed .side-foot{padding:10px 8px}
167
+ .collapsed .foot-btn{justify-content:center;padding:7px 0}
168
+
169
+ /* the foot of the sidebar: the settings, an icon with its name beside it */
170
+ .side-foot{display:flex;padding:10px 12px;border-top:1px solid var(--line)}
171
+ .foot-btn{flex:1;display:flex;align-items:center;gap:9px;padding:7px 12px;border:1px solid transparent;
172
+ border-radius:999px;background:none;color:var(--muted);font:inherit;font-size:13px;cursor:pointer;
173
+ white-space:nowrap;overflow:hidden;transition:background .15s,color .15s,border-color .15s}
174
+ .foot-btn:hover,.foot-btn[aria-expanded=true]{background:var(--hover);color:var(--ink);border-color:var(--line)}
175
+ .foot-btn svg{flex:none;width:15px;height:15px}
176
+ @media print{.side-foot{display:none}}
177
+ `;
178
+
179
+ export const CSS_MAIN = `
180
+ /* ---- main ---- */
181
+ .main{min-width:0;display:flex;flex-direction:column}
182
+ /* The title is a card: a slab of dark glass, lit by one blue light that sits
183
+ just off its bottom-left corner. Everything on it is that light or the sky
184
+ above, so it falls where a real one would:
185
+ - the face glows from the corner and fades out smoothly;
186
+ - the light is also fed into the glass, and runs along inside its edge both
187
+ ways - up the left side and over the top, along the bottom and up the
188
+ right - leaking out as it goes, so the edge glows brightest at the
189
+ corner and dims the further it travels, until the two runs die out
190
+ near the top-right corner;
191
+ - a bend leaks more light than a straight, so the two corners it rounds,
192
+ top-left and bottom-right, glint where the run turns;
193
+ - under all of it the edge keeps the grey of the sky, brighter on top.
194
+ Two faint copies of the light, one a little along the bottom and one a
195
+ little up the side, fade in and out of step, so it drifts and breathes too
196
+ slowly to watch; each one feeds a little more light to the corner on its
197
+ side. Its text keeps the column's gutter, so it starts on the same edge as
198
+ the prose.
199
+ The edge is painted by the border-box layers rather than a border colour,
200
+ so it can carry the light; the card can't clip its overflow without
201
+ clipping that edge, so long words wrap instead. Nothing repeats: a
202
+ repeating gradient wraps its far side round into the opposite border. */
203
+ .head{--lamp:-24px calc(100% + 24px);
204
+ /* the middle of each rounded corner's curve, where a glint sits */
205
+ --bend-tl:7px 7px; --bend-br:calc(100% - 7px) calc(100% - 7px);
206
+ position:relative;isolation:isolate;width:calc(100% - 32px);max-width:var(--column);
207
+ margin:28px auto 34px;padding:46px var(--gutter) 48px;border:1px solid transparent;border-radius:24px;
208
+ overflow-wrap:break-word;
209
+ background:
210
+ radial-gradient(90% 140% at var(--lamp),rgba(115,185,255,.52),rgba(86,159,255,.442) 17%,
211
+ rgba(58,133,255,.284) 33%,rgba(45,119,255,.146) 50%,rgba(34,106,255,.058) 67%,rgba(28,100,255,.013) 83%,
212
+ transparent) padding-box,
213
+ linear-gradient(rgba(255,255,255,.02),transparent 45%) padding-box,
214
+ linear-gradient(var(--surface),var(--surface)) padding-box,
215
+ radial-gradient(circle 160px at var(--bend-tl),rgba(150,205,255,.6),rgba(140,197,255,.364) 6%,
216
+ rgba(126,186,255,.205) 14%,rgba(106,171,255,.106) 25%,
217
+ rgba(80,150,255,.046) 40%,rgba(60,133,255,.013) 60%,transparent) border-box,
218
+ radial-gradient(circle 160px at var(--bend-br),rgba(150,205,255,.48),rgba(140,197,255,.291) 6%,
219
+ rgba(126,186,255,.164) 14%,rgba(106,171,255,.085) 25%,
220
+ rgba(80,150,255,.037) 40%,rgba(60,133,255,.01) 60%,transparent) border-box,
221
+ radial-gradient(150% 150% at var(--lamp),rgba(150,205,255,.85),rgba(133,191,255,.681) 10%,
222
+ rgba(115,178,255,.543) 20%,rgba(98,164,255,.427) 30%,rgba(80,150,255,.33) 40%,rgba(70,141,255,.248) 50%,
223
+ rgba(60,133,255,.178) 60%,rgba(50,124,255,.12) 70%,rgba(40,115,255,.071) 80%,rgba(40,115,255,.032) 90%,
224
+ transparent) border-box,
225
+ linear-gradient(rgba(255,255,255,.13),rgba(255,255,255,.06) 55%,rgba(255,255,255,.05)) border-box;
226
+ background-repeat:no-repeat}
227
+ /* the two drifting copies: each lights the edge fully, and the face by half
228
+ near the light and not at all by the far corners, so the more light it
229
+ sends round a bend shows on the edge, not as a smudge on the face */
230
+ .head::before,.head::after{content:"";position:absolute;inset:-1px;z-index:-1;pointer-events:none;
231
+ border:1px solid transparent;border-radius:inherit;opacity:0;
232
+ -webkit-mask:radial-gradient(90% 140% at 0 100%,rgba(0,0,0,.5),#000 60%) padding-box,linear-gradient(#000 0 0);
233
+ -webkit-mask-composite:xor;mask-composite:exclude;
234
+ animation:head-lamp 13s ease-in-out infinite alternate}
235
+ .head::before{background:
236
+ radial-gradient(circle 160px at var(--bend-br),rgba(150,205,255,.3),rgba(140,197,255,.182) 6%,
237
+ rgba(126,186,255,.102) 14%,rgba(106,171,255,.053) 25%,
238
+ rgba(80,150,255,.023) 40%,rgba(60,133,255,.006) 60%,transparent) border-box,
239
+ radial-gradient(70% 120% at 96px calc(100% + 24px),
240
+ rgba(115,185,255,.16),rgba(72,146,255,.107) 25%,rgba(45,119,255,.04) 50%,rgba(28,100,255,.008) 75%,
241
+ transparent) border-box;background-repeat:no-repeat}
242
+ .head::after{background:
243
+ radial-gradient(circle 160px at var(--bend-tl),rgba(150,205,255,.3),rgba(140,197,255,.182) 6%,
244
+ rgba(126,186,255,.102) 14%,rgba(106,171,255,.053) 25%,
245
+ rgba(80,150,255,.023) 40%,rgba(60,133,255,.006) 60%,transparent) border-box,
246
+ radial-gradient(70% 120% at -24px calc(100% - 26px),
247
+ rgba(115,185,255,.16),rgba(72,146,255,.107) 25%,rgba(45,119,255,.04) 50%,rgba(28,100,255,.008) 75%,
248
+ transparent) border-box;background-repeat:no-repeat;animation-duration:19s;animation-delay:-8s}
249
+ @keyframes head-lamp{to{opacity:1}}
250
+ @media (prefers-reduced-motion:reduce){.head::before,.head::after{animation:none}}
251
+ .head h1{margin:0;max-width:22ch;font-size:36px;line-height:1.12;font-weight:650;letter-spacing:-.034em;color:var(--ink)}
252
+ .head p{margin:14px 0 0;max-width:68ch;font-size:15.5px;line-height:1.62;color:var(--muted)}
253
+ .doc{padding:4px var(--gutter) 30px;width:100%;max-width:var(--column);margin-inline:auto}
254
+
255
+ /* ---- prev / next ----
256
+ Always at the very bottom of the page: under a long document it follows the
257
+ text, and under a short one it is pushed down to the foot of the screen
258
+ (the page is at least a screen tall, and the buttons take what is left). */
259
+ .docnav{width:100%;max-width:var(--column);margin:auto auto 0;padding:24px var(--gutter) 32px;
260
+ display:grid;grid-template-columns:1fr 1fr;gap:14px}
261
+ .docnav-btn{display:flex;flex-direction:column;gap:5px;min-width:0;padding:18px 20px;
262
+ background:var(--surface);border:1px solid var(--line);border-radius:18px;
263
+ transition:background .2s ease,border-color .2s ease}
264
+ .docnav-btn:hover{border-color:var(--line-2);
265
+ background:radial-gradient(110% 140% at 100% 0%,rgba(77,163,255,.13),transparent 60%),var(--surface-2)}
266
+ .docnav-btn.next{grid-column:2;text-align:right}
267
+ .docnav-label{font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
268
+ .docnav-title{font-size:14px;font-weight:var(--semi);letter-spacing:-.01em;color:var(--ink);
269
+ white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
270
+
271
+ /* ---- sections ----
272
+ Every heading owns the blocks under it, so a section can be folded away and
273
+ the space between two of them can be set as one rule instead of guessed at
274
+ from heading margins. */
275
+ .doc [id]{scroll-margin-top:88px}
276
+ .sec{margin-top:30px}
277
+ .doc>.sec{margin-top:54px;padding-top:32px;border-top:1px solid var(--line)}
278
+ .doc>.sec:first-child{margin-top:2px;padding-top:0;border-top:0}
279
+ .sec .sec{margin-top:34px}
280
+ .sec .sec .sec{margin-top:24px}
281
+ .sec .sec .sec .sec{margin-top:18px}
282
+ .sec.shut>.sec-body{display:none}
283
+
284
+ .sec-head{position:relative;display:flex;align-items:baseline;gap:11px;cursor:pointer;
285
+ line-height:1.32;letter-spacing:-.018em;scroll-margin-top:98px}
286
+ h1.sec-head{margin:0 0 15px;font-size:22px;font-weight:var(--bold)}
287
+ h2.sec-head{margin:0 0 13px;font-size:18.5px;font-weight:var(--bold)}
288
+ h3.sec-head{margin:0 0 10px;font-size:15.5px;font-weight:var(--semi)}
289
+ h4.sec-head,h5.sec-head,h6.sec-head{margin:0 0 8px;font-size:14px;font-weight:var(--semi);color:var(--muted)}
290
+ /* The fold arrow and the number sit out in the margin, so a heading starts on
291
+ the same left edge as the prose under it. The cluster is anchored by its
292
+ right edge and grows leftwards, so the number never moves when the arrow
293
+ fades in; its box is exactly one line tall, which is what centres both of
294
+ them on the heading's first line whatever the heading's size. */
295
+ .sec-gut{position:absolute;right:100%;top:0;margin-right:14px;height:1.32em;
296
+ display:flex;align-items:center;gap:5px;white-space:nowrap;user-select:none}
297
+ .sec-num{font-size:.8em;font-weight:var(--semi);letter-spacing:0;color:var(--faint);
298
+ font-variant-numeric:tabular-nums}
299
+ .sec-txt{min-width:0}
300
+ .sec-head em{font-family:var(--serif);font-style:italic;font-weight:500;letter-spacing:-.005em;color:var(--accent)}
301
+ .sec-tog{flex:none;width:18px;height:18px;padding:0;display:grid;place-items:center;
302
+ border:0;border-radius:6px;background:none;color:var(--faint);
303
+ cursor:pointer;opacity:0;transition:opacity .15s,background .15s,color .15s}
304
+ .sec-tog svg{width:12px;height:12px;transition:transform .18s ease}
305
+ .sec-tog:hover{background:var(--hover);color:var(--ink)}
306
+ .sec-head:hover .sec-tog,.sec.shut>.sec-head .sec-tog{opacity:1}
307
+ .sec.shut>.sec-head{margin-bottom:0;color:var(--muted)}
308
+ .sec.shut>.sec-head .sec-num{color:var(--muted)}
309
+ .sec.shut>.sec-head .sec-tog svg{transform:rotate(-90deg)}
310
+ .h-anchor{flex:none;font-size:.78em;font-weight:400;color:var(--faint);
311
+ opacity:0;transition:opacity .15s,color .15s}
312
+ .sec-head:hover .h-anchor{opacity:1}
313
+ .h-anchor:hover{color:var(--ink)}
314
+
315
+ /* ---- prose ---- */
316
+ .doc p{margin:0 0 18px}
317
+ .doc>p:first-child{margin-top:6px}
318
+ .sec-body>:last-child,.doc blockquote>:last-child,.doc li>:last-child{margin-bottom:0}
319
+ .doc li{margin:6px 0}
320
+ .doc ul,.doc ol{margin:0 0 18px;padding-left:24px}
321
+ .doc li>ul,.doc li>ol{margin:6px 0}
322
+ .doc li::marker{color:var(--faint)}
323
+ .doc ul>li::marker{color:var(--accent)}
324
+ .doc strong{font-weight:var(--semi);color:inherit}
325
+ .doc em{font-style:italic}
326
+ .doc a:not(.h-anchor){color:var(--ink);border-bottom:1px solid var(--line-2);padding-bottom:1px;
327
+ transition:border-color .12s,color .12s}
328
+ .doc a:not(.h-anchor):hover{border-bottom-color:var(--accent);color:var(--accent)}
329
+ .doc hr{margin:32px 0;border:0;border-top:1px solid var(--line)}
330
+ .doc blockquote{margin:0 0 18px;padding:2px 0 2px 17px;border-left:2px solid var(--accent-ring);color:var(--muted)}
331
+ /* Inline code is a plate set into the sentence: a faint white fill that
332
+ lifts it off the dark page, a hairline ring drawn inside the box (so it
333
+ costs no layout and still reads on a white table cell, where the fill
334
+ alone would vanish), and an ink a shade cooler than the prose, so a name
335
+ reads as a name without shouting. Padding is in em, so the plate tracks
336
+ the text it sits in, headings included. */
337
+ .doc code{font-family:var(--mono);font-size:.84em;letter-spacing:-.01em;
338
+ background:var(--code-bg);box-shadow:inset 0 0 0 1px var(--code-ring);
339
+ border-radius:5px;padding:.1em .38em .12em;color:var(--code-ink);white-space:nowrap;
340
+ -webkit-box-decoration-break:clone;box-decoration-break:clone}
341
+ .api code{background:var(--code-bg-on-white)}
342
+ .doc a:not(.h-anchor) code{color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent-ring)}
343
+ .doc h1 code,.doc h2 code,.doc h3 code,.doc h4 code{font-size:.86em;font-weight:var(--semi)}
344
+ /* a {placeholder} inside code: the braces fade, the name stays */
345
+ .c-var{color:var(--code-var);font-style:italic}
346
+ /* an HTTP verb named in the prose, alone or leading a url */
347
+ .c-verb{font-weight:750;letter-spacing:.02em;color:var(--m-ink)}
348
+ .doc code.c-verb-only{color:var(--m-ink);background:var(--m-soft);box-shadow:inset 0 0 0 1px var(--m-ring);
349
+ font-weight:750;font-size:.78em;letter-spacing:.04em;padding:.14em .44em .12em;vertical-align:.04em}
350
+ /* one colour per verb, as a family: ink for text, a soft fill and a ring for
351
+ plates, and a solid for the badge on a call's header */
352
+ .m-get{--m-ink:#8ab8ff;--m-soft:rgba(80,140,255,.14);--m-ring:rgba(80,140,255,.38);--m-solid:#3b82f6}
353
+ .m-post{--m-ink:#7ee3a6;--m-soft:rgba(110,231,160,.13);--m-ring:rgba(110,231,160,.38);--m-solid:#22c55e}
354
+ .m-put{--m-ink:#fbc078;--m-soft:rgba(251,180,90,.13);--m-ring:rgba(251,180,90,.38);--m-solid:#f59e0b}
355
+ .m-patch{--m-ink:#bfa8ff;--m-soft:rgba(160,130,255,.15);--m-ring:rgba(160,130,255,.4);--m-solid:#8b5cf6}
356
+ .m-delete{--m-ink:#ff9393;--m-soft:rgba(255,110,110,.14);--m-ring:rgba(255,110,110,.4);--m-solid:#ef4444}
357
+ .m-head,.m-options{--m-ink:#b4bcc8;--m-soft:rgba(255,255,255,.07);--m-ring:rgba(255,255,255,.2);--m-solid:#6b7280}
358
+ .doc del{color:var(--faint)}
359
+ .doc img{max-width:100%;border-radius:14px}
360
+
361
+ /* ---- code ---- */
362
+ .code-wrap{position:relative;margin:0 0 20px}
363
+ .code-lang{position:absolute;top:9px;right:12px;font-family:var(--mono);font-size:10px;
364
+ letter-spacing:.06em;text-transform:uppercase;color:var(--faint);pointer-events:none}
365
+ pre.code{margin:0;padding:16px 18px;overflow-x:auto;max-width:100%;background:var(--surface);
366
+ border:1px solid var(--line);border-radius:16px}
367
+ pre.code code{font-family:var(--mono);font-size:12.5px;line-height:1.62;white-space:pre;
368
+ letter-spacing:0;background:none;border:0;padding:0;color:var(--code-text)}
369
+
370
+ /* ---- tables ---- */
371
+ .table-wrap{margin:0 0 20px;overflow-x:auto;max-width:100%;background:var(--surface);
372
+ border:1px solid var(--line);border-radius:16px}
373
+ table{border-collapse:collapse;width:100%;font-size:13.5px}
374
+ th,td{padding:10px 14px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
375
+ th{font-size:10.5px;letter-spacing:.07em;text-transform:uppercase;color:var(--muted);
376
+ font-weight:var(--bold);white-space:nowrap;background:var(--surface-2)}
377
+ tbody tr:last-child td{border-bottom:0}
378
+ /* Code in a table is often a whole column of it, and a ringed plate on every
379
+ row turns the column into a grid of boxes. Here it is a soft fill with no
380
+ ring, a size up from inline code, so a value reads as the
381
+ value of the row, not as a tag stuck on it. A header keeps its code as
382
+ written: the header's capitals would change the name. */
383
+ .doc td code{font-size:12.5px;letter-spacing:-.005em;font-weight:500;color:var(--code-ink);
384
+ background:var(--code-bg-table);box-shadow:none;padding:.2em .5em .22em;border-radius:6px;white-space:normal}
385
+ .doc th code{font-size:11.5px;letter-spacing:0;text-transform:none;font-weight:var(--semi);color:var(--muted);
386
+ background:rgba(255,255,255,.06);box-shadow:none;padding:.12em .42em}
387
+
388
+ /* ---- api calls ----
389
+ A call is a neutral box: a solid badge in the colour of its verb, the url
390
+ in mono beside it, and a chevron. The header is all there is until it is clicked;
391
+ the body then lists what each part of the url is. */
392
+ .api{margin:0 0 20px;background:var(--surface);border:1px solid var(--line);border-radius:16px;
393
+ overflow:hidden}
394
+ .api-head{display:flex;align-items:center;gap:12px;min-height:44px;padding:8px 12px 8px 15px;
395
+ background:var(--surface);transition:background .12s}
396
+ .api-head[role=button]{cursor:pointer;user-select:text}
397
+ .api-head[role=button]:hover{background:var(--surface-2)}
398
+ .api-head:focus-visible{outline:2px solid var(--m-ring);outline-offset:-2px;border-radius:15px}
399
+ .api-verb{flex:none;min-width:62px;padding:3px 10px;border-radius:999px;text-align:center;
400
+ font:750 11px/1.5 var(--font);letter-spacing:.07em;color:var(--m-ink);background:var(--m-soft);
401
+ box-shadow:inset 0 0 0 1px var(--m-ring)}
402
+ .doc .api-url{flex:1;min-width:0;padding:0;background:none;box-shadow:none;border-radius:0;
403
+ font-size:13px;letter-spacing:-.005em;color:var(--ink);white-space:normal;overflow-wrap:anywhere}
404
+ .api-scheme{color:var(--faint)}
405
+ .api-url .c-var{color:var(--m-ink);font-style:normal;font-weight:var(--semi)}
406
+ .api-title{flex:none;max-width:34%;font-size:12.5px;color:var(--muted);white-space:nowrap;
407
+ overflow:hidden;text-overflow:ellipsis}
408
+ .api-tog{flex:none;width:22px;height:22px;display:grid;place-items:center;border-radius:999px;
409
+ color:var(--faint);transition:transform .18s ease,color .15s,background .15s}
410
+ .api-tog svg{width:13px;height:13px}
411
+ .api-head:hover .api-tog{color:var(--ink);background:var(--hover)}
412
+ .api.is-open .api-tog{transform:rotate(90deg)}
413
+ /* a described part of the url: marked only while the key is held, so the
414
+ url reads as a url the rest of the time */
415
+ .api-part{border-radius:4px;transition:background .12s,box-shadow .12s}
416
+ .api-asking .api:not(.is-open) .api-part{box-shadow:inset 0 -1.5px 0 var(--m-ring);cursor:help}
417
+ .api-part.is-asked{background:var(--m-soft);box-shadow:0 0 0 2px var(--m-soft)}
418
+ .api-body{display:none;margin:0;padding:6px 0;border-top:1px solid var(--line)}
419
+ .api.is-open .api-body{display:block}
420
+ .api-row{display:grid;grid-template-columns:minmax(120px,max-content) 1fr;gap:4px 18px;
421
+ align-items:baseline;padding:8px 16px 8px 18px;font-size:13.5px;line-height:1.6}
422
+ .api-row+.api-row{border-top:1px solid var(--dg-er-line)}
423
+ .api-row dt{margin:0}
424
+ .api-row dt code{font-size:12px}
425
+ .api-row dt .c-var{color:var(--m-ink);font-style:normal;font-weight:var(--semi)}
426
+ .api-row dd{margin:0;font-size:13.5px;line-height:1.6;color:var(--ink)}
427
+ .api-row dd p{margin:0}
428
+ .api-row dd ul{margin:4px 0 0;padding-left:18px}
429
+ .api-row dd li{margin:2px 0}
430
+ .api-tip-key{margin-bottom:5px}
431
+ .api-tip-key code{font-weight:var(--semi);color:var(--ink)}
432
+ .dg-tip.api-tip .c-var{color:#f3c77b;font-style:normal}
433
+ .dg-tip.api-tip ul{margin:5px 0 0;padding-left:16px}
434
+ /* ---- http examples ----
435
+ The same box as a call, with the example's own parts under the header: the
436
+ HTTP headers as a quiet list of names and values, then the body, coloured
437
+ when it is JSON. A response wears its status in the badge, in the colour of
438
+ what it means; written after the request in the same block, it sits
439
+ under it in the same box. */
440
+ .http{margin:0 0 20px;background:var(--surface);border:1px solid var(--line);border-radius:16px;overflow:hidden}
441
+ .http-msg+.http-msg{border-top:1px solid var(--line)}
442
+ /* the response, when it follows the request in the same box: set a step
443
+ back, so the two read as what was sent and what came back */
444
+ .http-msg.is-res:not(:first-child){background:rgba(255,255,255,.015)}
445
+ .http-msg.is-res:not(:first-child) .http-headers{background:rgba(255,255,255,.03)}
446
+ .http-head{display:flex;align-items:center;gap:12px;min-height:44px;padding:8px 14px 8px 15px}
447
+ .http-reason{flex:1;min-width:0;font-size:13.5px;font-weight:var(--semi);color:var(--ink)}
448
+ .http-kind{flex:none;font-size:10px;font-weight:var(--bold);letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
449
+ .http-headers{margin:0;padding:9px 16px 9px 15px;border-top:1px solid var(--line);background:var(--surface-2);
450
+ font-family:var(--mono);font-size:12px;line-height:1.7}
451
+ .http-headers div{display:flex;gap:8px;min-width:0}
452
+ .http-headers dt{flex:none;color:var(--muted)}
453
+ .http-headers dt::after{content:":";color:var(--faint)}
454
+ .http-headers dd{margin:0;min-width:0;color:var(--ink);overflow-wrap:anywhere}
455
+ .http-body{margin:0;padding:14px 17px 15px;overflow-x:auto;border-top:1px solid var(--line)}
456
+ .doc .http-body code,.doc pre.code code{font-size:12.5px;line-height:1.65;white-space:pre;letter-spacing:0;
457
+ background:none;box-shadow:none;border-radius:0;padding:0;color:var(--code-text)}
458
+ /* json */
459
+ .j-key{color:#9ecbff}
460
+ .j-str{color:#7ee3a6}
461
+ .j-num{color:#fbc078}
462
+ .j-lit{color:#c4a8ff;font-weight:var(--semi)}
463
+ .j-pun{color:var(--faint)}
464
+ /* sql: keywords carry the statement's shape, so they get the weight; names
465
+ stay in the ink, and what is data - strings, numbers - takes the colours
466
+ JSON uses for the same things */
467
+ .s-kw{color:#8ab8ff;font-weight:600}
468
+ .s-type{color:#67d4e6}
469
+ .s-fn{color:#c4a8ff}
470
+ .s-str{color:#7ee3a6}
471
+ .s-num{color:#fbc078}
472
+ .s-var{color:#f49ac1}
473
+ .s-id{color:var(--code-text)}
474
+ .s-com{color:#6b7480;font-style:italic}
475
+ .s-pun{color:var(--muted)}
476
+ @media (max-width:640px){
477
+ .api-title,.http-kind{display:none}
478
+ .api-row{grid-template-columns:1fr}
479
+ }
480
+ @media print{.api-body{display:block}.api-tog{display:none}}
481
+
482
+ /* ---- misc ---- */
483
+ .empty{padding:60px var(--gutter);width:100%;max-width:var(--column);margin-inline:auto;color:var(--muted)}
484
+ mark{background:rgba(77,163,255,.22);color:var(--ink);border-radius:3px;padding:0 2px}
485
+ @media (max-width:820px){
486
+ .layout{grid-template-columns:var(--rail)}
487
+ .layout:not(.collapsed) .main{display:none}
488
+ :root{--gutter:20px}
489
+ :root.fixed-margin{--column:100%}
490
+ .docnav{grid-template-columns:1fr}
491
+ .docnav-btn.next{grid-column:1;text-align:left}
492
+ .head{width:calc(100% - 20px);margin-top:14px;padding-block:34px 36px}
493
+ .head h1{font-size:29px}
494
+ }
495
+ /* Not enough room beside the column to hang the arrow and the number in:
496
+ bring them back into the heading row rather than let them fall off-screen. */
497
+ @media (max-width:1120px){
498
+ .sec-gut{position:static;height:auto;margin-right:0;align-self:center}
499
+ .sec-tog{opacity:1}
500
+ }
501
+ @media print{
502
+ .side{display:none}.layout{grid-template-columns:1fr}
503
+ .head{border:0;border-radius:0;padding-block:20px;background:none}.head::before,.head::after{display:none}
504
+ .sec.shut>.sec-body{display:block}.sec-tog,.h-anchor{display:none}
505
+ }
506
+ `;
507
+
508
+ export const CSS_BAR = `
509
+ .topbar{position:fixed;top:0;left:var(--rail);right:0;z-index:6;pointer-events:none;
510
+ display:flex;height:46px;
511
+ background:rgba(8,9,11,.78);backdrop-filter:saturate(160%) blur(14px);
512
+ border-bottom:1px solid var(--line);transform:translateY(-101%);transition:transform .22s ease}
513
+ .topbar.show{transform:none}
514
+ .topbar-in{display:flex;align-items:center;gap:10px;min-width:0;width:100%;
515
+ max-width:var(--column);margin-inline:auto;padding:0 var(--gutter)}
516
+ .topbar b{font-size:13.5px;font-weight:var(--bold);letter-spacing:-.012em}
517
+ .topbar span{font-size:12px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
518
+ `;
519
+
520
+ /* ------------------------------------------------------------- diagrams */
521
+
522
+ export const CSS_DIAGRAM = `
523
+ .diagram{position:relative;margin:0 0 22px;padding:0 18px 22px;background:var(--surface);
524
+ border:1px solid var(--line);border-radius:18px;text-align:center;overflow-x:auto;max-width:100%}
525
+ .diagram-src{display:none}
526
+ .diagram svg{max-width:100%;height:auto}
527
+ /* The header: what the diagram is, and what can be done with it. It spans the
528
+ frame (the negative margins undo the frame's side padding) and stays put
529
+ while a diagram wider than the frame is scrolled sideways. */
530
+ .diagram-head{position:sticky;left:0;z-index:4;display:flex;align-items:center;gap:12px;min-height:40px;
531
+ margin:0 -18px 20px;padding:6px 8px 6px 16px;border-bottom:1px solid var(--line);
532
+ background:var(--surface);border-radius:18px 18px 0 0;text-align:left}
533
+ .diagram-name{flex:1;min-width:0;display:flex;align-items:baseline;gap:9px;overflow:hidden;white-space:nowrap}
534
+ .diagram-kind{flex:none;font-size:10px;font-weight:var(--bold);letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
535
+ .diagram-title{min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:12.5px;font-weight:var(--semi);color:var(--ink)}
536
+ .diagram-bar{flex:none;display:flex;gap:5px}
537
+ .diagram-btn{padding:3px 11px;font:inherit;font-size:11px;color:var(--muted);
538
+ background:transparent;border:1px solid var(--line-2);border-radius:999px;cursor:pointer;
539
+ transition:color .12s,border-color .12s}
540
+ .diagram-btn:hover{color:var(--ink);border-color:rgba(255,255,255,.32)}
541
+ .diagram-btn:empty{display:none}
542
+ .diagram:has(svg):hover{border-color:var(--line-2)}
543
+ .diagram .fail{font-family:var(--mono);font-size:12px;color:var(--muted);text-align:left;white-space:pre-wrap}
544
+ .diagram-note{margin-top:10px;font-size:11px;color:var(--faint);text-align:center}
545
+ /* A collapsed diagram is its header and nothing else: a plain white box the
546
+ height of a line, which a click opens again. */
547
+ .diagram.is-folded{padding-bottom:0;overflow:hidden;cursor:pointer}
548
+ .diagram.is-folded .diagram-head{margin-bottom:0;border-bottom-color:transparent}
549
+ .diagram.is-folded:hover{border-color:var(--line-2)}
550
+ .diagram.is-folded .diagram-out,.diagram.is-folded .diagram-note,
551
+ .diagram.is-folded .diagram-btn:not([data-act=fold]){display:none}
552
+ @media print{.diagram.is-folded{display:none}.diagram-bar{display:none}}
553
+
554
+ /* A wide diagram is allowed to spill a little past the text column - the point
555
+ of the column is to keep prose readable, and a diagram is not prose. */
556
+ @media (min-width:1280px){.diagram,.table-wrap{width:calc(100% + 170px)}}
557
+
558
+ /* ---- the clean renderer ----
559
+ Nothing here is decoration. Every rule exists so a reader can answer one of
560
+ two questions in a dense chart: which arrow is this label on, and what
561
+ touches this box. Hovering answers the first, clicking a node the second. */
562
+ svg.dg{display:block;margin-inline:auto;cursor:default;-webkit-user-select:none;user-select:none}
563
+ svg.dg text{font-family:var(--font);dominant-baseline:middle}
564
+ .dg-node-shape{fill:var(--dg-node);stroke:var(--dg-node-line);stroke-width:1.1}
565
+ .dg-node.is-root .dg-node-shape{fill:var(--bg)}
566
+ .dg-node-text{fill:var(--ink);font-size:12.5px}
567
+ .dg-group-shape{fill:var(--dg-group);stroke:var(--dg-group-line);stroke-width:1}
568
+ .dg-group-text{fill:var(--muted);font-size:12.5px;font-weight:560}
569
+ .dg-edge-line{fill:none;stroke:var(--dg-edge);stroke-width:1.3;stroke-linejoin:round}
570
+ .dg-edge-hit{fill:none;stroke:transparent;stroke-width:14;stroke-linejoin:round;cursor:pointer}
571
+ .dg-edge-text{fill:var(--dg-edge-text);font-size:11.5px}
572
+ /* arrowheads are drawn into the edge's own group, not shared <marker>s, so one
573
+ rule recolours a head with its line and a cloned diagram carries its own */
574
+ .dg-head{fill:var(--dg-edge);stroke:none}
575
+ .dg-head-open{fill:var(--surface);stroke:var(--dg-edge);stroke-width:1.4}
576
+ /* the plate under a label: the label owns its own space, so this only has to
577
+ survive the odd arc that ELK routes through the reserved band */
578
+ .dg-label-plate{fill:var(--surface);fill-opacity:.95}
579
+ /* the tie between a label and its arc: a hairline to a dot sitting on the arc
580
+ itself, which is what makes "which arrow is this text on" a fact, not a guess */
581
+ .dg-tie{stroke:var(--dg-tie);stroke-width:1}
582
+ .dg-tie-dot{fill:var(--dg-edge)}
583
+ .dg-label{cursor:pointer}
584
+
585
+ /* hover states: two tiers, both fully lit - see .is-near below */
586
+ .dg-el{transition:opacity .12s ease}
587
+ svg.dg.has-hot .dg-el{opacity:.14}
588
+ svg.dg .dg-el.is-hot,svg.dg .dg-el.is-near{opacity:1}
589
+ .dg-el.is-hot .dg-edge-line{stroke:var(--dg-hot);stroke-width:2}
590
+ .dg-el.is-hot .dg-tie{stroke:var(--dg-hot)}
591
+ .dg-el.is-hot .dg-tie-dot{fill:var(--dg-hot)}
592
+ .dg-el.is-hot .dg-edge-text{fill:var(--dg-hot);font-weight:560}
593
+ .dg-el.is-hot .dg-label-plate{fill:var(--dg-hot-soft);fill-opacity:1}
594
+ .dg-el.is-hot .dg-node-shape{stroke:var(--dg-hot);stroke-width:2;fill:var(--dg-hot-soft)}
595
+ .dg-el.is-hot .dg-head{fill:var(--dg-hot)}
596
+ .dg-el.is-hot .dg-head-open{fill:var(--surface);stroke:var(--dg-hot)}
597
+ /* the box at the far end of a lit arrow: lit too - "where does this go" is the
598
+ question being asked - but left unfilled, so which box the pointer is on stays
599
+ obvious. Both tiers are fully opaque; only the untouched rest fades. */
600
+ .dg-el.is-near .dg-node-shape{stroke:var(--dg-hot);stroke-width:1.5}
601
+
602
+ /* ---- entities ----
603
+ The reason ER gets the same treatment as flowcharts: mermaid draws an entity
604
+ as a stack of text, so nothing lines up and every row has to be read in full.
605
+ Here it is a table - the type column, the name column, and the keys pinned
606
+ right - which is what lets the eye scan one column instead of all of them. */
607
+ .dg-er-head{fill:var(--dg-er-head);stroke:none;pointer-events:none}
608
+ .dg-er-rule{stroke:var(--dg-node-line);stroke-width:1}
609
+ .dg-er-sep{stroke:var(--dg-er-line);stroke-width:.8}
610
+ .dg-er-title{fill:var(--ink);font-size:13px;font-weight:600;letter-spacing:-.004em}
611
+ .dg-er-type{fill:var(--muted);font-size:11.6px}
612
+ .dg-er-name{fill:var(--ink);font-size:11.6px}
613
+ .dg-er-comment{fill:var(--faint);font-size:11.6px;font-style:italic}
614
+ .dg-er-badge{fill:var(--dg-er-badge);stroke:var(--dg-er-badge-line);stroke-width:.8}
615
+ .dg-er-badge.is-pk{fill:var(--dg-er-badge-pk);stroke:var(--dg-er-badge-pk-line)}
616
+ .dg-er-key{fill:var(--dg-er-badge-ink);font-size:9.4px;font-weight:600;letter-spacing:.02em}
617
+ /* on a wide entity the band is what keeps the eye on one row while it travels
618
+ from the type on the left across to the keys on the right */
619
+ .dg-er-band{fill:transparent;transition:fill .12s ease}
620
+ .dg-er-row:hover .dg-er-band{fill:var(--dg-er-hover)}
621
+
622
+ /* crow's feet: line-drawn like the arrowheads, and lit by the same rules, so a
623
+ relationship and both of its cardinalities light as one thing */
624
+ .dg-card-line{fill:none;stroke:var(--dg-edge);stroke-width:1.3;stroke-linecap:round}
625
+ .dg-card-ring{fill:var(--surface);stroke:var(--dg-edge);stroke-width:1.3}
626
+ .dg-card-dot{fill:var(--dg-edge);stroke:none}
627
+ .dg-el.is-hot .dg-card-line{stroke:var(--dg-hot);stroke-width:1.8}
628
+ .dg-el.is-hot .dg-card-ring{stroke:var(--dg-hot);stroke-width:1.8}
629
+ .dg-el.is-hot .dg-card-dot{fill:var(--dg-hot)}
630
+ .dg-el.is-hot .dg-er-head{fill:var(--dg-er-head-hot)}
631
+
632
+ /* the button in an entity's header that opens it to every column, and closes it */
633
+ .dg-er-more{cursor:pointer}
634
+ .dg-er-more-box{fill:var(--surface);stroke:var(--dg-node-line);stroke-width:1;transition:fill .12s,stroke .12s}
635
+ .dg-er-more-icon{fill:none;stroke:var(--muted);stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}
636
+ .dg-er-more:hover .dg-er-more-box{fill:var(--dg-hot-soft);stroke:var(--dg-hot)}
637
+ .dg-er-more:hover .dg-er-more-icon{stroke:var(--dg-hot)}
638
+ .dg-er-more.is-open .dg-er-more-box{fill:var(--dg-er-badge-pk);stroke:var(--dg-er-badge-pk-line)}
639
+ .dg-er-more.is-open .dg-er-more-icon{stroke:var(--ink)}
640
+ .dg-er-more.is-open:hover .dg-er-more-box{fill:var(--dg-hot-soft);stroke:var(--dg-hot)}
641
+
642
+ /* ---- an opened entity ----
643
+ The same table with every column a schema document writes down, and the
644
+ notes above it - in the faces and weights of the compact table, so opening
645
+ an entity adds columns without changing how the ones already there read.
646
+ The sizes here are the ones the layout measures with: change them together. */
647
+ .dg-er-x-label{fill:var(--faint);font-size:9.2px;font-weight:620;letter-spacing:.07em}
648
+ .dg-er-x-name{font-weight:560}
649
+ .dg-er-x-code{font-family:var(--mono);font-size:10.8px;fill:var(--dg-text-2)}
650
+ .dg-er-x-bold{font-weight:620}
651
+ .dg-er-note,.dg-er-desc{fill:var(--dg-text-2);font-size:11.6px}
652
+ .dg-er-bullet{fill:var(--faint)}
653
+ .dg-er-ref{fill:var(--muted);font-size:10.6px}
654
+ /* text.dg-er-def, to outweigh svg.dg text, which sets the family of every label */
655
+ svg.dg text.dg-er-def{fill:var(--dg-text-2);font-family:var(--mono);font-size:10.8px}
656
+ .dg-er-none{fill:var(--faint);font-size:11px}
657
+ .dg-er-notnull{fill:var(--faint);font-size:9.4px;font-weight:600;letter-spacing:.02em}
658
+ .dg-er-null{fill:var(--dg-seq-note);stroke:var(--dg-seq-note-line);stroke-width:.8}
659
+ .dg-er-null-text{fill:var(--dg-seq-note-ink);font-size:9.4px;font-weight:600;letter-spacing:.02em}
660
+ /* laid out again around it: the entities glide (inline transitions), and the
661
+ relationships fade back in once their new routes are drawn */
662
+ .dg-reflow .dg-edges,.dg-reflow .dg-labels{animation:dg-fade-in .3s ease-out .18s both}
663
+ .dg-er-opening{animation:dg-fade-in .24s ease-out both}
664
+ @keyframes dg-fade-in{from{opacity:0}}
665
+ /* what an entity is for, shown while the pointer is on it */
666
+ .dg-tip{position:fixed;z-index:70;max-width:min(440px,calc(100vw - 16px));padding:9px 12px;text-align:left;
667
+ font-size:12.5px;line-height:1.5;color:var(--ink);background:#20252c;border:1px solid var(--line-2);border-radius:12px;
668
+ pointer-events:none;box-shadow:0 14px 36px -12px rgba(0,0,0,.7);animation:dg-fade-in .12s ease-out}
669
+ .dg-tip p{margin:0}
670
+ .dg-tip p+p{margin-top:6px}
671
+ .dg-tip code{font-family:var(--mono);font-size:.88em;background:rgba(255,255,255,.13);border-radius:4px;padding:.05em .3em}
672
+ @media print{.dg-er-more,.dg-tip{display:none}}
673
+
674
+ /* ---- sequences ----
675
+ Participants are nodes and messages are edges, so hovering and pinning run
676
+ on the rules above; what follows is only what a sequence adds. Frames, notes
677
+ and participant boxes are the ground the messages sit on: they fade part of
678
+ the way with the rest, never all the way, so a lit message keeps its context. */
679
+ .dg-seq-life{stroke:var(--dg-life);stroke-width:1.1}
680
+ .dg-seq-life-hit{stroke:transparent;stroke-width:12;cursor:pointer}
681
+ .dg-seq-actor{font-weight:560;letter-spacing:-.004em}
682
+ .dg-seq-glyph{fill:none;stroke:var(--muted);stroke-width:1.2}
683
+ .dg-seq-bar{fill:var(--dg-group);stroke:var(--dg-node-line);stroke-width:1}
684
+ .dg-seq-x{fill:none;stroke:var(--dg-edge);stroke-width:1.7;stroke-linecap:round}
685
+ .dg-seq-text{fill:var(--dg-text-2)}
686
+ .dg-head-line{fill:none;stroke:var(--dg-edge);stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}
687
+ .dg-seq-num{fill:var(--dg-seq-num);stroke:var(--surface);stroke-width:1.5}
688
+ .dg-seq-num-text{fill:#fff;font-size:9.6px;font-weight:620}
689
+ .dg-seq-frame{fill:rgba(255,255,255,.015);stroke:var(--dg-life);stroke-width:1}
690
+ .dg-seq-split{stroke:var(--dg-tie);stroke-width:1;stroke-dasharray:5 4}
691
+ .dg-seq-tab{fill:var(--dg-er-badge);stroke:var(--dg-life);stroke-width:1}
692
+ .dg-seq-kw{fill:var(--dg-er-badge-ink);font-size:9.4px;font-weight:620;letter-spacing:.05em}
693
+ .dg-seq-cond{fill:var(--muted);font-size:11.5px}
694
+ .dg-seq-rect{fill:var(--dg-group);fill-opacity:.8}
695
+ .dg-seq-note{fill:var(--dg-seq-note);stroke:var(--dg-seq-note-line);stroke-width:1}
696
+ .dg-seq-note-text{fill:var(--dg-seq-note-ink);font-size:11.5px}
697
+ .dg-seq-box{fill:var(--dg-group);stroke:var(--dg-group-line);stroke-width:1}
698
+ .dg-seq-box-text{fill:var(--muted);font-size:12px;font-weight:560}
699
+ .dg-seq-title{fill:var(--ink);font-size:14px;font-weight:620}
700
+ .dg-seq-boxes,.dg-seq-frames,.dg-seq-notes,.dg-seq-marks{transition:opacity .12s ease}
701
+ svg.dg.has-hot .dg-seq-boxes,svg.dg.has-hot .dg-seq-frames,
702
+ svg.dg.has-hot .dg-seq-notes,svg.dg.has-hot .dg-seq-marks{opacity:.4}
703
+ .dg-el.is-hot .dg-seq-life{stroke:var(--dg-hot);stroke-width:1.6}
704
+ .dg-el.is-near .dg-seq-life{stroke:var(--dg-hot);stroke-opacity:.5}
705
+ .dg-el.is-hot .dg-seq-bar{stroke:var(--dg-hot)}
706
+ .dg-el.is-hot .dg-seq-x{stroke:var(--dg-hot)}
707
+ .dg-el.is-hot .dg-head-line{stroke:var(--dg-hot)}
708
+ .dg-el.is-hot .dg-seq-num{fill:var(--dg-hot)}
709
+ .dg-el.is-hot .dg-seq-num-text{fill:var(--on-ink)}
710
+ .dg-el.is-hot .dg-seq-text{fill:var(--dg-hot)}
711
+ /* the participant row that rides along the top of a long sequence */
712
+ .dg-float{position:absolute;left:0;right:0;top:0;z-index:3;pointer-events:none;opacity:0;visibility:hidden;
713
+ background:rgba(19,22,27,.93);backdrop-filter:blur(6px);border-bottom:1px solid var(--line);
714
+ box-shadow:0 8px 16px -12px rgba(0,0,0,.6);transition:opacity .15s ease,visibility .15s}
715
+ .dg-float.on{opacity:1;visibility:visible}
716
+ .dg-float svg.dg{position:absolute;top:0;margin:0;max-width:none}
717
+ .dg-float.on .dg-node{pointer-events:auto;cursor:pointer}
718
+
719
+ /* a box given a light fill by a classDef, written for a light page: its text
720
+ goes dark, so the fill still reads as the author meant it */
721
+ svg.dg .dg-node.on-light text{fill:#141a26}
722
+ svg.dg .dg-node.on-light .dg-er-type,svg.dg .dg-node.on-light .dg-er-comment,
723
+ svg.dg .dg-node.on-light .dg-er-ref,svg.dg .dg-node.on-light .dg-er-x-label{fill:#4f5664}
724
+ svg.dg .dg-node.on-light .dg-er-key{fill:var(--dg-er-badge-ink)}
725
+
726
+ @media print{svg.dg.has-hot .dg-el{opacity:1}
727
+ svg.dg.has-hot .dg-seq-boxes,svg.dg.has-hot .dg-seq-frames,
728
+ svg.dg.has-hot .dg-seq-notes,svg.dg.has-hot .dg-seq-marks{opacity:1}
729
+ .dg-float{display:none}}
730
+ `;
731
+
732
+ export const CSS_LIGHTBOX = `
733
+ .lightbox{position:fixed;inset:0;z-index:50;display:none;background:rgba(8,9,11,.94);
734
+ backdrop-filter:blur(8px);cursor:grab}
735
+ .lightbox.on{display:block}
736
+ .lightbox.drag{cursor:grabbing}
737
+ .lightbox-stage{position:absolute;top:50%;left:50%;transform-origin:center;will-change:transform}
738
+ .lightbox-stage svg{max-width:none}
739
+ .lightbox-bar{position:absolute;top:16px;right:18px;display:flex;gap:6px;align-items:center;
740
+ font-size:11.5px;color:var(--muted)}
741
+ .lightbox-bar button{padding:4px 12px;font:inherit;font-size:12px;background:var(--surface);
742
+ border:1px solid var(--line-2);border-radius:999px;color:var(--ink);cursor:pointer}
743
+ .lightbox-bar button:hover{background:var(--surface-2)}
744
+ `;
745
+
746
+ export const CSS_CMT = `
747
+ /* ---- comments: sidebar footer ---- */
748
+ /* ---- comments: the count beside the switch in the settings ---- */
749
+ .cmt-n{margin-left:4px;padding:1px 7px;border-radius:20px;background:var(--surface);border:1px solid var(--line);
750
+ font-size:11px;font-weight:400;font-variant-numeric:tabular-nums;color:var(--muted)}
751
+
752
+ /* ---- comments: highlight + margin cards ----
753
+ A highlight and its card are two views of one comment, so hovering either
754
+ lights both (.hot); clicking either pins the pair (.on). */
755
+ .body-wrap{position:relative;display:flex;flex-direction:column;min-height:100vh}
756
+ .cmt-hl{background:rgba(250,204,21,.16);box-shadow:inset 0 -1px 0 rgba(250,204,21,.6);border-radius:3px;padding:0 1px;
757
+ color:inherit;cursor:pointer;transition:background .12s,box-shadow .12s}
758
+ .cmt-hl.hot{background:rgba(250,204,21,.26);box-shadow:inset 0 -1px 0 rgba(250,204,21,.8)}
759
+ .cmt-hl.on{background:rgba(250,204,21,.36);box-shadow:inset 0 -1px 0 #facc15}
760
+ .cmt-layer{position:absolute;inset:0;pointer-events:none;z-index:2}
761
+ .cmt-card{position:absolute;left:0;top:0;width:var(--cmt-w,264px);pointer-events:auto;padding:10px 12px;
762
+ background:var(--surface);border:1px solid var(--line);border-radius:14px;cursor:pointer;
763
+ font-size:12.5px;line-height:1.55;transition:border-color .15s ease,box-shadow .15s ease}
764
+ .cmt-card.hot{border-color:rgba(250,204,21,.45);box-shadow:0 4px 16px rgba(0,0,0,.4)}
765
+ .cmt-card.on{border-color:rgba(250,204,21,.7);box-shadow:0 6px 20px rgba(0,0,0,.5)}
766
+ .cmt-card[hidden]{display:none!important}
767
+ .cmt-face{display:none}
768
+ .cmt-quote{margin:0 0 7px;padding-left:9px;border-left:2px solid rgba(250,204,21,.6);color:var(--muted);
769
+ font-size:11.5px;line-height:1.45;overflow:hidden;
770
+ display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
771
+ .cmt-text{white-space:pre-wrap;overflow-wrap:anywhere}
772
+ .cmt-foot{margin-top:9px;display:flex;align-items:center;gap:9px;font-size:11px;color:var(--faint)}
773
+ .cmt-when{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
774
+ .cmt-act{padding:0;border:0;background:none;color:var(--muted);font:inherit;font-size:11px;
775
+ cursor:pointer;border-bottom:1px solid transparent}
776
+ .cmt-act:hover{color:var(--ink);border-bottom-color:var(--ink)}
777
+ .cmt-lost{margin:0 0 5px;font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:#e0b252}
778
+
779
+ /* tight gutters: the card shrinks to a numbered dot, opened as a popover */
780
+ .cmt-layer.dots .cmt-card{width:22px;height:22px;padding:0;border-radius:50%;
781
+ background:#3a3215;border-color:#a8862a;display:grid;place-items:center;overflow:hidden}
782
+ .cmt-layer.dots .cmt-card.hot,.cmt-layer.dots .cmt-card.on{background:#53461a;border-color:#facc15}
783
+ .cmt-layer.dots .cmt-full{display:none}
784
+ .cmt-layer.dots .cmt-face{display:block;font-size:11px;font-weight:var(--bold);color:#f5d76e;
785
+ font-variant-numeric:tabular-nums}
786
+
787
+ /* ---- comments: floating bits ---- */
788
+ .cmt-add{position:fixed;z-index:9;padding:5px 13px;background:var(--ink);color:var(--on-ink);border:0;
789
+ border-radius:999px;font:inherit;font-size:12px;font-weight:var(--semi);cursor:pointer;
790
+ box-shadow:0 4px 16px rgba(0,0,0,.5)}
791
+ .cmt-add:hover{background:var(--ink-hover)}
792
+ .cmt-pop{position:fixed;z-index:9;width:290px;max-width:calc(100vw - 24px);padding:12px;
793
+ background:var(--surface-2);border:1px solid var(--line-2);border-radius:16px;font-size:12.5px;
794
+ line-height:1.55;box-shadow:0 14px 40px rgba(0,0,0,.6)}
795
+ .cmt-pop textarea{display:block;width:100%;min-height:80px;margin:0 0 9px;padding:8px 10px;
796
+ border:1px solid var(--line);border-radius:12px;background:var(--bg);color:var(--ink);
797
+ font:inherit;font-size:12.5px;line-height:1.55;resize:vertical;outline:none}
798
+ .cmt-pop textarea:focus{border-color:var(--accent-ring)}
799
+ .cmt-row{display:flex;align-items:center;gap:8px}
800
+ .cmt-row .cmt-spacer{flex:1}
801
+ .cmt-btn{padding:5px 13px;border:1px solid var(--line-2);border-radius:999px;background:transparent;
802
+ color:var(--ink);font:inherit;font-size:12px;cursor:pointer;transition:.15s}
803
+ .cmt-btn:hover{background:var(--hover);border-color:rgba(255,255,255,.32)}
804
+ .cmt-btn.go{background:var(--ink);border-color:var(--ink);color:var(--on-ink);font-weight:var(--semi)}
805
+ .cmt-btn.go:hover{background:var(--ink-hover);border-color:var(--ink-hover)}
806
+ .cmt-toast{position:fixed;left:50%;bottom:26px;transform:translateX(-50%);z-index:10;padding:8px 16px;
807
+ background:var(--ink);color:var(--on-ink);border-radius:999px;font-size:12.5px;font-weight:var(--semi);
808
+ box-shadow:0 8px 26px rgba(0,0,0,.55)}
809
+ @media print{.cmt-layer,.cmt-add,.cmt-pop,.cmt-toast{display:none}}
810
+ `;
811
+
812
+ /* ---- settings ----
813
+ A dialog in the middle of the page. The veil behind it dims the page only a
814
+ little, so a change is still seen on the documents as it is made, and it
815
+ takes the click that closes the dialog, so that click lands on nothing else. */
816
+ export const CSS_SETTINGS = `
817
+ .settings-veil{position:fixed;inset:0;z-index:39;background:rgba(4,5,7,.42);animation:dg-fade-in .16s ease-out}
818
+ .settings-veil[hidden]{display:none}
819
+ .settings{position:fixed;z-index:40;top:50%;left:50%;transform:translate(-50%,-50%);width:360px;
820
+ max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);overflow-y:auto;padding:16px 18px;
821
+ background:var(--surface-2);border:1px solid var(--line-2);border-radius:20px;
822
+ box-shadow:0 24px 60px -16px rgba(0,0,0,.8);
823
+ font-size:13px;line-height:1.45;animation:settings-in .16s ease-out}
824
+ @keyframes settings-in{from{opacity:0;transform:translate(-50%,calc(-50% + 6px))}}
825
+ .settings[hidden]{display:none}
826
+ .settings-head{display:flex;align-items:center;justify-content:space-between;margin:-4px -6px 8px 0}
827
+ .settings-head b{font-size:13.5px;font-weight:var(--bold);letter-spacing:-.012em}
828
+ .settings-row{padding:10px 0 12px;border-top:1px solid var(--line)}
829
+ .settings-label{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:6px}
830
+ .settings-label label{font-weight:var(--semi)}
831
+ .settings-val{font-variant-numeric:tabular-nums;color:var(--muted)}
832
+ .settings input[type=range]{display:block;width:100%;margin:0;accent-color:var(--accent);cursor:pointer}
833
+ .settings-hint{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-top:6px;
834
+ font-size:11.5px;color:var(--faint)}
835
+ .settings-link{padding:0;border:0;border-bottom:1px solid var(--line);background:none;font:inherit;
836
+ color:var(--muted);cursor:pointer}
837
+ .settings-link:hover{color:var(--ink);border-bottom-color:var(--ink)}
838
+ .settings-link:disabled{color:var(--faint);border-bottom-color:transparent;cursor:default}
839
+ /* an on/off setting: a pill with a knob, lit when on */
840
+ .switch{flex:none;position:relative;width:34px;height:20px;padding:0;border:1px solid var(--line-2);
841
+ border-radius:999px;background:var(--surface);cursor:pointer;transition:background .15s,border-color .15s}
842
+ .switch::after{content:"";position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;
843
+ background:var(--muted);transition:transform .18s ease,background .15s}
844
+ .switch[aria-checked=true]{background:var(--accent);border-color:var(--accent)}
845
+ .switch[aria-checked=true]::after{transform:translateX(14px);background:var(--on-ink)}
846
+ .switch:focus-visible{outline:2px solid var(--accent-ring);outline-offset:2px}
847
+ .settings-foot{padding-top:9px;border-top:1px solid var(--line);font-size:11.5px;color:var(--muted)}
848
+ .settings-foot.bad{color:#ff9393}
849
+ .settings-foot code{font-family:var(--mono);font-size:.92em}
850
+ @media print{.settings,.settings-veil{display:none}}
851
+ `;
852
+
853
+ export const allCss = () => CSS + CSS_MAIN + CSS_BAR + CSS_DIAGRAM + CSS_LIGHTBOX + CSS_CMT + CSS_SETTINGS;