-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprimer.css
More file actions
690 lines (643 loc) · 26.2 KB
/
Copy pathprimer.css
File metadata and controls
690 lines (643 loc) · 26.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
/*
* primer.css — the single source of the Interactive Primer's look-and-feel.
*
* Page-level (light DOM) styling lives here. Design tokens are declared on :root
* as CSS custom properties; because custom properties pierce shadow boundaries,
* the Web Components inherit the same palette and typography automatically
* (see js/components/shared.js for the component-internal stylesheet).
*/
/*
* Themes. Colour tokens are grouped per theme and selected by `data-theme` on <html>
* (set early, before paint, by js/boot.js and the inline script in index.html). The
* LIGHT palette is the :root default so the app looks right even before JS runs. The
* `viz` tokens drive diagrams and manim animations (see js/theme.js → themeColors()).
*/
:root {
/* Typography. The reading face is STIX Two Text — a modern serif co-designed with the STIX
math fonts, so prose sits comfortably beside KaTeX's Computer-Modern math. It's loaded by
js/boot.js; Georgia/Times are the fallbacks if the webfont can't load (so the page still
reads well offline). The UI face stays native system-sans for speed + a clean modern chrome. */
--primer-font-body: "STIX Two Text", Georgia, "Iowan Old Style", "Times New Roman", serif;
--primer-font-ui: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
--primer-font-display: var(--primer-font-body); /* headings; the fun theme overrides */
--primer-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; /* instrument readouts */
/* Rhythm */
--primer-measure: 42rem; /* comfortable reading width */
--primer-radius: 0.85rem;
--primer-gap: 1.4rem;
/* Palette — LIGHT (default): a calm, "primer"-like parchment + ink scheme. */
--primer-bg: #f8f4ec;
--primer-surface: #ffffff;
--primer-ink: #20252f;
--primer-ink-soft: #5a6675;
--primer-accent: #4d5bd1; /* links, interactive accents */
--primer-accent-ink: #ffffff;
--primer-border: #ebe5d9;
--primer-control-bg: #f1ede4; /* recessed HUD control surface */
--primer-control-border: #d8d1c2;
--primer-badge-bg: #eceefb;
--primer-badge-ink: #3a45a6;
--primer-star: #f5b301; /* filled confidence star */
/* Elevation & focus — modern depth. Shadows are warm-tinted so cards sit naturally on
the parchment (a neutral black shadow reads cold against the cream). The focus ring is
the accent at low alpha. Each theme overrides these to match its own backdrop. */
--primer-shadow-sm: 0 1px 2px rgba(74, 60, 34, 0.06);
--primer-shadow-md: 0 1px 2px rgba(74, 60, 34, 0.05), 0 6px 18px rgba(74, 60, 34, 0.07);
--primer-shadow-lg: 0 12px 36px rgba(74, 60, 34, 0.14);
--primer-ring: rgba(77, 91, 209, 0.40);
/* Quiz panel: a warm golden card (the standardized "Quick quiz"). */
--primer-quiz-bg: #fdf3d7;
--primer-quiz-border: #ecd29a;
--primer-quiz-ink: #7a5b16; /* the "Quick quiz" header */
/* Vignette panel: a calm sage/teal aside — a path off the main trail. */
--primer-vignette-bg: #eef5f2;
--primer-vignette-border: #5b9c8f;
--primer-vignette-ink: #2f6b5e;
/* Quiz feedback: correct (ok) / incorrect (bad), ink + subtle tinted background. */
--primer-ok: #1a8f3c;
--primer-ok-bg: #e6f6ec;
--primer-bad: #c0392b;
--primer-bad-bg: #fdecea;
/* Visualisation palette (diagrams + animations) */
--primer-viz-bg: #ffffff; /* animation/diagram backdrop */
--primer-viz-ink: #1f2430; /* labels and text on diagrams */
--primer-viz-line: #1f2430; /* axes, strokes, number lines */
/* Categorical fills are GENERATED by a golden-angle hue sequence (js/theme.js →
themeColors().cat) from these three knobs, so early colours are maximally distinct
and only crowd as more are used. Tune per theme here. */
--primer-cat-hue: 210;
--primer-cat-sat: 62%;
--primer-cat-light: 52%;
/* Confidence colour ramp (explorer node fills): hue is the rating 0→120
(red→green); saturation/lightness are theme-tuned for text legibility. */
--primer-conf-sat: 70%;
--primer-conf-light: 62%;
/* Active-course accent: the course members + the course predecessor/successor in the explorers. */
--primer-course: #e3b15c;
}
:root[data-theme="dark"] {
--primer-bg: #14171f;
--primer-surface: #1e222c;
--primer-ink: #e8eaed;
--primer-ink-soft: #9aa3b2;
--primer-accent: #8c9eff;
--primer-accent-ink: #14171f;
--primer-border: #2c323e;
--primer-control-bg: #11151d; /* recessed HUD control surface (darker than surface) */
--primer-control-border: #323b4a;
--primer-badge-bg: #232a3a;
--primer-badge-ink: #aeb9ff;
--primer-star: #ffd24d;
--primer-quiz-bg: #2e2716;
--primer-quiz-border: #4f4226;
--primer-quiz-ink: #f0cf78;
--primer-vignette-bg: #16302b;
--primer-vignette-border: #4f9d8c;
--primer-vignette-ink: #8fd6c6;
--primer-ok: #4ccb76;
--primer-ok-bg: #16321f;
--primer-bad: #ff6b6b;
--primer-bad-bg: #3a1d1d;
/* Deeper, neutral shadows read correctly on the dark backdrop. */
--primer-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.30);
--primer-shadow-md: 0 2px 4px rgba(0, 0, 0, 0.30), 0 8px 22px rgba(0, 0, 0, 0.42);
--primer-shadow-lg: 0 16px 40px rgba(0, 0, 0, 0.55);
--primer-ring: rgba(140, 158, 255, 0.45);
--primer-viz-bg: #1e222c;
--primer-viz-ink: #e8eaed;
--primer-viz-line: #e8eaed;
--primer-cat-hue: 210;
--primer-cat-sat: 60%;
--primer-cat-light: 66%; /* lighter so fills pop on the dark backdrop */
/* Darker, muted fills so the light node text stays readable on them. */
--primer-conf-sat: 45%;
--primer-conf-light: 34%;
--primer-course: #8a6d2a;
}
:root[data-theme="fun"] {
--primer-bg: #fff7fb;
--primer-surface: #ffffff;
--primer-ink: #2b2350;
--primer-ink-soft: #6b5fa0;
/* A deep rose (not the old #ff5da2): as a link on the near-white bg AND as the background
under white --primer-accent-ink (active buttons, .focus-course) it clears WCAG AA 4.5:1,
which the lighter pink failed. Still unmistakably the "fun" pink. */
--primer-accent: #c81e66;
--primer-accent-ink: #ffffff;
--primer-border: #ffd6ec;
--primer-control-bg: #fdeef7; /* recessed HUD control surface */
--primer-control-border: #ffd0e6;
--primer-badge-bg: #e7f6ff;
--primer-badge-ink: #1a8fd6;
--primer-star: #ffb627;
--primer-quiz-bg: #fff3c4;
--primer-quiz-border: #ffdf8a;
--primer-quiz-ink: #8a6d12;
--primer-vignette-bg: #eafff7;
--primer-vignette-border: #4fcab0;
--primer-vignette-ink: #1f8f78;
--primer-ok: #1fa84f;
--primer-ok-bg: #e3f8e8;
--primer-bad: #e23b54;
--primer-bad-bg: #fde6ea;
/* Soft pink-tinted shadows to match the cheerful backdrop. */
--primer-shadow-sm: 0 1px 3px rgba(214, 79, 142, 0.12);
--primer-shadow-md: 0 2px 6px rgba(214, 79, 142, 0.14), 0 10px 26px rgba(214, 79, 142, 0.16);
--primer-shadow-lg: 0 16px 38px rgba(214, 79, 142, 0.22);
--primer-ring: rgba(255, 93, 162, 0.45);
--primer-viz-bg: #fffdf7;
--primer-viz-ink: #2b2350;
--primer-viz-line: #2b2350;
--primer-cat-hue: 200;
--primer-cat-sat: 85%;
--primer-cat-light: 60%; /* punchy */
/* Bright and cheerful — fun's dark ink stays readable on them. */
--primer-conf-sat: 90%;
--primer-conf-light: 72%;
--primer-course: #f2ad3c;
/* Playful shapes + a rounded display font (loaded by js/theme.js while active). */
--primer-radius: 1rem;
--primer-font-display: "Fredoka", "Baloo 2", var(--primer-font-ui);
}
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
background: var(--primer-bg);
color: var(--primer-ink);
font-family: var(--primer-font-body);
font-size: 18px;
line-height: 1.65;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
/* Ease the colour swap when the theme changes. */
body { transition: background-color 0.2s ease, color 0.2s ease; }
h1, h2 { font-family: var(--primer-font-display); }
main, .primer-shell {
max-width: var(--primer-measure);
margin: 0 auto;
padding: 2.25rem 1rem 4.5rem;
}
a { color: var(--primer-accent); }
/*
* Accessibility utilities (light DOM). Shadow-DOM controls carry their own focus rings (see
* js/components/shared.js); these cover the light-DOM surfaces — links, the skip link, and any
* focusable element render.js/pages emit — plus a global reduced-motion honour.
*/
/* Visually hidden but readable by assistive tech. Used for the skip link, off-screen headings,
and live-region status text. (index.html carries a page-local copy for its pre-CSS paint.) */
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* Skip-to-content: the first focusable element on every page. Off-screen until focused, then it
slides into the top-left so a keyboard user can jump straight past the fixed chrome to <main>. */
.skip-link {
position: fixed; top: 0.5rem; left: -999px; z-index: 2000;
padding: 0.55rem 0.9rem; border-radius: var(--primer-radius);
background: var(--primer-surface); color: var(--primer-ink);
border: 1px solid var(--primer-accent); box-shadow: var(--primer-shadow-md);
font-family: var(--primer-font-ui); text-decoration: none;
}
.skip-link:focus, .skip-link:focus-visible { left: 0.5rem; }
/* A consistent, theme-tinted focus ring for light-DOM interactives (links included). Only shows
for keyboard/assistive focus (:focus-visible), so mouse clicks stay ring-free. The main landmark
is programmatically focusable (tabindex=-1, set by the skip link target) but shouldn't show a
ring when jumped to. */
a:focus-visible,
[tabindex]:focus-visible,
summary:focus-visible {
outline: 3px solid var(--primer-accent);
outline-offset: 2px;
border-radius: 3px;
}
main:focus, main:focus-visible, [tabindex="-1"]:focus { outline: none; }
/* Honour "reduce motion": drop non-essential transitions/animations site-wide. Component shadow
sheets do the same locally (shared.js and the menu/pathway/quiz components). */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
scroll-behavior: auto !important;
}
}
/* <primer-title> is a metadata carrier, not visible content: render.js reads its text for the
<h1> + document.title. Hide it so the raw title never flashes before the shell is built. */
primer-title { display: none; }
/*
* Cards (light DOM). <primer-card> adopts this class, and because cards are slotted
* into <primer-concept> their styling must live in the document stylesheet — slotted
* content is styled here, not by the components' shadow stylesheet (js/components/shared.js).
* Surface/border/radius match the shadow `.card`; the vertical padding here is tighter.
*/
.card {
display: flow-root; /* contains a floated .card-image even when prose is short */
position: relative; /* anchor for the top-right read-aloud button */
background: var(--primer-surface);
border: 1px solid var(--primer-border);
border-radius: var(--primer-radius);
padding: 1.15rem 1.6rem;
margin: var(--primer-gap) 0;
box-shadow: var(--primer-shadow-md);
}
/* Collapse the first/last child's outer margin so the 5px top/bottom padding is the
actual gap (otherwise a leading <p>'s margin stacks on top of it). The read-aloud button is
prepended as the first child but is absolutely positioned (out of flow), so also collapse the
top margin of the real first content that follows it — otherwise it would push the card down. */
.card > :first-child { margin-top: 0; }
.card > .card-readaloud + * { margin-top: 0; }
.card > :last-child { margin-bottom: 0; }
/* Read-aloud button: a small chip pinned to the card's top-right corner. Mirrors the manim
.play control — token-driven so it recolours with the theme, with a visible focus ring. */
.card-readaloud {
position: absolute;
top: 0.55rem;
right: 0.6rem;
z-index: 1;
display: inline-flex;
padding: 0.2rem;
border: 1px solid var(--primer-control-border);
border-radius: 0.4rem;
background: var(--primer-control-bg);
color: var(--primer-ink);
cursor: pointer;
line-height: 0;
opacity: 0.7;
transition: opacity 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}
.card-readaloud:hover { opacity: 1; border-color: var(--primer-accent); }
.card-readaloud[aria-pressed="true"] { opacity: 1; border-color: var(--primer-accent); }
.card-readaloud:focus-visible {
outline: none;
opacity: 1;
border-color: var(--primer-accent);
box-shadow: 0 0 0 2px var(--primer-ring);
}
.card-readaloud svg { width: 1.15rem; height: 1.15rem; display: block; }
@media (prefers-reduced-motion: reduce) {
.card-readaloud { transition: none; }
}
/* The sentence currently being read aloud (CSS Custom Highlight API). A solid accent chip is
the no-color-mix fallback; browsers that support color-mix get a soft tint over the prose. */
::highlight(primer-reading) {
background-color: var(--primer-accent);
color: var(--primer-accent-ink);
}
@supports (background-color: color-mix(in srgb, red, blue)) {
::highlight(primer-reading) {
background-color: color-mix(in srgb, var(--primer-accent) 26%, transparent);
color: inherit;
}
}
/* The standard "intro illustration" inside a card (subject + course intros, and the many image-led
pages to come). It floats top-right at a third of the card's width with the prose flowing around
it; on a narrow screen it stops floating and centres so it never shrinks to a thumbnail. Keep the
shared look here — author with just <img class="card-image" src="…" alt="…"> (no inline styles). */
.card-image {
float: right;
width: 33%;
height: auto;
margin: 0.1rem 0 0.7rem 1.25rem;
border-radius: var(--primer-radius);
}
@media (max-width: 32rem) {
.card-image { float: none; display: block; width: min(62%, 240px); margin: 0.2rem auto 0.9rem; }
}
/* Full-width hero banner at the top of a card. Bleeds to the card's rounded corners. */
.card-hero {
display: block;
width: calc(100% + 3.2rem);
max-width: none;
height: auto;
margin: -1.15rem -1.6rem 1.1rem;
border-radius: var(--primer-radius) var(--primer-radius) 0 0;
}
.card > .card-readaloud + .card-hero { margin-top: -1.15rem; }
/* A wide code block scrolls horizontally inside its column instead of overflowing the page
(same approach as a wide display equation, below). Inline <code> in prose still wraps. */
pre {
overflow-x: auto;
max-width: 100%;
}
/* <primer-quiz> renders its own golden card inside its shadow root, so it sits at the
top level (not wrapped in a .card). Give the host the same vertical rhythm as a card
so the gap before/after a quiz matches the gap between cards. */
primer-quiz { display: block; margin: var(--primer-gap) 0; }
/* Tables. Wrap a plain <table> in <primer-table> for consistent, themed presentation:
centered cells, hairline borders, a shaded header row, and horizontal scroll on overflow.
Light DOM, so styling lives here; author with <primer-table><table>…</table></primer-table>. */
primer-table { display: block; overflow-x: auto; margin: var(--primer-gap) 0; }
primer-table > table {
border-collapse: collapse;
margin: 0 auto; /* centre the table within the block */
background: var(--primer-surface);
color: var(--primer-ink);
font-family: var(--primer-font-ui);
font-size: 0.95rem;
}
primer-table th,
primer-table td {
border: 1px solid var(--primer-border);
padding: 0.45rem 0.85rem;
text-align: center;
vertical-align: middle;
}
primer-table thead th { background: var(--primer-control-bg); font-weight: 700; }
primer-table caption {
caption-side: top;
color: var(--primer-ink-soft);
font-family: var(--primer-font-ui);
font-size: 0.85rem;
padding-bottom: 0.4rem;
text-align: center;
}
/*
* Tinted callouts that sit INSIDE a card: <primer-theorem> (a formal statement) and
* <primer-vignette> (a collapsible aside). Both are light DOM (slotted), so their
* styling lives here. They share an "eyebrow" header — a small uppercase label with an icon
* before it — and the same accent-rule + tinted-panel shape, differing only in colour token and
* (for the vignette) being collapsible. The eyebrow icons are masked SVGs tinted by currentColor,
* so they follow the theme.
*/
.theorem {
display: block;
background: var(--primer-badge-bg);
border-left: 4px solid var(--primer-accent);
border-radius: var(--primer-radius);
padding: 0.9rem 1.2rem;
margin: var(--primer-gap) 0;
box-shadow: var(--primer-shadow-sm);
}
.theorem > :first-child { margin-top: 0; }
.theorem > :last-child { margin-bottom: 0; }
/* Shared eyebrow (icon + label) for theorem and vignette headers. */
.eyebrow {
display: flex;
align-items: center;
gap: 0.5rem;
font-family: var(--primer-font-ui);
font-size: 0.85rem;
letter-spacing: 0.08em;
text-transform: uppercase;
margin-bottom: 0.55rem;
}
.eyebrow-icon {
flex: none;
width: 1.05em;
height: 1.05em;
background-color: currentColor; /* the masked SVG is tinted to the eyebrow colour */
-webkit-mask: var(--eyebrow-icon) center / contain no-repeat;
mask: var(--eyebrow-icon) center / contain no-repeat;
}
.theorem .eyebrow { color: var(--primer-badge-ink); }
.theorem .eyebrow-icon { --eyebrow-icon: url("/images/gears.svg"); }
/* Vignette — a collapsible aside (a path off the main trail) inside a card. Starts collapsed; the
<summary> is the eyebrow (footsteps icon + title), and expands to card-like body content. */
.vignette { display: block; }
.vignette-details {
background: var(--primer-vignette-bg);
border-left: 4px solid var(--primer-vignette-border);
border-radius: var(--primer-radius);
padding: 0.9rem 1.2rem;
margin: var(--primer-gap) 0;
box-shadow: var(--primer-shadow-sm);
}
.vignette-summary {
cursor: pointer;
list-style: none; /* hide the native disclosure triangle… */
color: var(--primer-vignette-ink);
margin-bottom: 0;
}
.vignette-summary::-webkit-details-marker { display: none; } /* …including in WebKit */
.vignette-summary .eyebrow { margin-bottom: 0; }
.vignette-summary .eyebrow-icon { --eyebrow-icon: url("/images/foot-steps.svg"); }
/* A chevron affordance at the end of the row that flips when open. */
.vignette-summary .eyebrow::after {
content: "▸";
margin-left: auto;
font-size: 0.9em;
transition: transform 0.15s ease;
}
.vignette-details[open] .vignette-summary .eyebrow::after { transform: rotate(90deg); }
.vignette-details[open] .vignette-summary { margin-bottom: 0.6rem; }
.vignette-summary:focus-visible { outline: 2px solid var(--primer-ring); outline-offset: 2px; border-radius: 4px; }
.vignette-body > :first-child { margin-top: 0; }
.vignette-body > :last-child { margin-bottom: 0; }
/*
* Concept cross-references (light DOM). <primer-ref> wraps its text in an
* <a class="concept-ref"> pointing at another lesson; it sits inside slotted card prose,
* so it is styled here. It inherits the accent link colour (the `a` rule above); the dotted
* underline marks it as a *concept* link (a jump elsewhere in the tree) rather than a plain
* external link, without shouting.
*/
a.concept-ref {
text-decoration: underline;
text-decoration-style: dotted;
text-underline-offset: 0.15em;
text-decoration-thickness: 1px;
}
a.concept-ref:hover { text-decoration-style: solid; }
/*
* A small gold course crest shown to the LEFT of a <primer-ref> whose target is a course
* (course: true) — the inline-link echo of the shield on course nodes (the graph) and course
* page titles. Sized in em so it tracks the surrounding text; nudged onto the baseline.
*/
.concept-ref-crest {
height: 0.95em;
width: auto;
vertical-align: -0.12em;
margin-right: 0.25em;
}
/*
* A `todo/…` placeholder reference — a concept the author plans to write but hasn't yet. Rendered
* by <primer-ref> as a muted, NON-link span (no accent colour, no confidence dot) plus a small
* "todo" chip, so it reads as planned rather than a working link.
*/
.concept-todo {
color: var(--primer-ink-soft);
text-decoration: underline dotted var(--primer-ink-soft);
text-decoration-thickness: 1px;
text-underline-offset: 0.15em;
cursor: default;
}
.concept-todo-tag {
margin-left: 0.3em;
font-family: var(--primer-font-ui);
font-size: 0.62rem;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
vertical-align: 0.08em;
color: var(--primer-ink-soft);
background: var(--primer-badge-bg);
border-radius: 999px;
padding: 0.05rem 0.4rem;
}
/*
* The ⧉ confidence icon that follows the reference words. Its colour is set inline by
* <primer-ref> from the target concept's star rating (the same RED→YELLOW→GREEN ramp as the
* pathway nodes); with no rating the inline colour is empty and this default ink shows
* through. A monochrome glyph (not an emoji), so it takes the colour cleanly.
*/
.concept-ref-icon {
margin-left: 0.3em;
color: var(--primer-ink); /* unrated → default ink */
font-size: 0.92em;
vertical-align: baseline;
text-decoration: none; /* it's a link, but show the glyph only — never an underline */
cursor: pointer;
}
/*
* Wikipedia-summary lookup popup (src/wiki-card.ts): appears next to a short text selection.
* It lives on <body> in the light DOM, so these --primer-* tokens apply directly; the surface
* mirrors the context menu so it matches the rest of the chrome and re-themes for free.
*/
.wiki-card {
position: fixed;
z-index: 1200;
max-width: 22rem;
width: max-content;
padding: 0.7rem 0.85rem;
background: var(--primer-surface, #fff);
color: var(--primer-ink, #111);
border: 1px solid var(--primer-border, #ddd);
border-radius: var(--primer-radius, 0.6rem);
box-shadow: var(--primer-shadow-lg, 0 12px 36px rgba(0, 0, 0, 0.18));
font-family: var(--primer-font-ui, sans-serif);
font-size: 0.9rem;
line-height: 1.4;
}
.wiki-card[hidden] {
display: none;
}
.wiki-card-close {
position: absolute;
top: 0.3rem;
right: 0.4rem;
padding: 0.15rem;
border: 0;
background: none;
color: var(--primer-ink-soft, #667);
font-size: 1rem;
line-height: 1;
cursor: pointer;
}
.wiki-card-close:hover,
.wiki-card-close:focus-visible {
color: var(--primer-ink, #111);
outline: none;
}
.wiki-card-thumb {
float: right;
margin: 0 0 0.4rem 0.6rem;
max-width: 5rem;
max-height: 5rem;
border-radius: 0.4rem;
}
.wiki-card-title {
margin: 0 1.2rem 0.25rem 0; /* leave room for the ✕ */
font-weight: 600;
}
.wiki-card-extract {
margin: 0;
color: var(--primer-ink, #111);
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 5; /* clamp the summary — no internal scroll (page scroll dismisses) */
overflow: hidden;
}
.wiki-card-foot {
clear: both;
margin-top: 0.5rem;
}
.wiki-card-link {
color: var(--primer-accent, #46e);
font-size: 0.85rem;
text-decoration: none;
}
.wiki-card-link:hover {
text-decoration: underline;
}
/*
* Math (light DOM). <primer-math> renders KaTeX into itself; it's inline by default
* (a custom element starts as display:inline), so inline math flows with the prose.
* The `display` attribute makes it a centered block, like KaTeX's own display mode.
*
* A display equation can be wider than its column; rather than overflow the page, it
* scrolls horizontally inside its own panel. `justify-content: safe center` keeps the
* equation centered when it fits but falls back to start-aligned when it overflows, so
* the left edge stays reachable (plain centering + overflow clips the left, unscrollable).
*/
primer-math[display] {
display: flex;
justify-content: safe center;
overflow-x: auto;
overflow-y: hidden;
margin: 0.75rem 0;
scrollbar-width: thin;
}
/* The wrapper owns the vertical spacing; pad the bottom so the scrollbar (when present)
doesn't sit on the equation. */
primer-math[display] .katex-display {
margin: 0;
padding-bottom: 0.25rem;
}
/* Landing / index helpers (light DOM) */
.primer-tree-list { list-style: none; padding: 0; }
.primer-tree-list li { margin: 0.4rem 0; }
/*
* MathLive virtual keyboard (the math input editor). MathLive renders it into the document
* body — outside our component shadow roots — so it's styled here, globally. The keycap
* variables go on `body` (per MathLive's docs); raising --keycap-max-width lets the keys
* widen to fill the row instead of staying narrow + centred. The keyboard panel is then
* capped at 640px and centred, so it's edge-to-edge on a phone but doesn't stretch on a
* wide screen.
*/
body {
/* Keys grow to fill the row (no narrow cap), comfortable touch height, and the keyboard's
own horizontal + per-row padding zeroed so the keys reach the edges. */
--keycap-max-width: 9999px;
--keycap-gap: 3px;
--keycap-height: 2.4rem;
--keyboard-padding-horizontal: 0px;
--keyboard-padding-top: 0px; /* no grey gap above the top row */
--keyboard-row-padding-left: 0px;
--keyboard-row-padding-right: 0px;
}
/* The grey host (.ML__keyboard) + its full-width grey backdrop (.MLK__backdrop) stay full
width. The plate is absolutely positioned, so padding on the backdrop is ignored — the 5px
grey band above/below the keys is created by insetting the plate itself (top/bottom below). */
.ML__keyboard .MLK__backdrop {
padding-top: 0;
padding-bottom: 0;
}
/* editToolbar="none" empties the toolbar but leaves the element (min-height:32px) — that 32px
is the grey gap above the keys. Hide it. */
.ML__keyboard .MLK__toolbar {
display: none;
}
/* The plate is absolutely positioned (MathLive sets left + an explicit width, so plain auto
margins can't centre it). Re-anchor it left:0/right:0 with auto width so max-width + auto
margins centre it at 640px on a wide screen (full width on a phone), and drop its top offset. */
.ML__keyboard .MLK__plate {
top: 0;
left: 0;
right: 0;
width: auto;
max-width: 640px;
margin-inline: auto;
/* The plate is transparent over the grey backdrop and is sized by its content; padding here
grows it 5px at top + bottom, revealing the grey band above and below the keys. */
padding-top: 5px;
padding-bottom: 5px;
}
/* MathLive sizes each keycap to ~10% of the panel (min(--keycap-max-width, 10cqw)), assuming
~10 keys per row; our rows have only 5–6, so they'd leave a big side gap. The rows are
flexboxes, so make every keycap flex-grow equally to fill the full row width. */
.ML__keyboard .MLK__row > * {
flex: 1 1 0;
max-width: none;
}