@erclx/canon 4.110.0 → 4.112.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/brand/mark.svg +10 -0
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/auto-ship/REQUIREMENT.md +1 -0
- package/claude/skills/auto-ship/SKILL.md +12 -2
- package/claude/skills/canon-operator/REQUIREMENT.md +2 -2
- package/claude/skills/canon-operator/SKILL.md +3 -4
- package/claude/skills/git-pr/REQUIREMENT.md +2 -0
- package/claude/skills/git-pr/SKILL.md +21 -3
- package/claude/skills/target-check/references/domains.md +2 -2
- package/claude/skills/teach-workspace/SKILL.md +1 -1
- package/docs/agents/install-and-sync.md +4 -13
- package/docs/agents/teach.md +3 -1
- package/docs/target-projects.md +0 -2
- package/docs/workflow/ai-workflow.md +7 -11
- package/package.json +2 -1
- package/scripts/core/capture-home.sh +59 -0
- package/src/claude/cases/setup.ts +2 -23
- package/src/commands/sync.ts +0 -16
- package/src/design/components.ts +153 -152
- package/src/design/favicon.ts +8 -0
- package/src/design/tokens.ts +1 -1
- package/src/shipped/references.ts +1 -2
- package/src/sync/reverse.ts +3 -74
- package/src/teach/nav.ts +55 -13
- package/claude/skills/migration-claude-md/REQUIREMENT.md +0 -40
- package/claude/skills/migration-claude-md/SKILL.md +0 -79
- package/claude/skills/migration-context/REQUIREMENT.md +0 -36
- package/claude/skills/migration-context/SKILL.md +0 -97
- package/claude/skills/migration-standards-drop/REQUIREMENT.md +0 -55
- package/claude/skills/migration-standards-drop/SKILL.md +0 -113
- package/claude/skills/migration-superseded/REQUIREMENT.md +0 -44
- package/claude/skills/migration-superseded/SKILL.md +0 -115
package/src/design/components.ts
CHANGED
|
@@ -223,6 +223,7 @@ const TEACH_CHROME: Component = {
|
|
|
223
223
|
reads: [
|
|
224
224
|
'--color-background',
|
|
225
225
|
'--color-surface',
|
|
226
|
+
'--color-chrome',
|
|
226
227
|
'--color-border',
|
|
227
228
|
'--color-text',
|
|
228
229
|
'--color-text-body',
|
|
@@ -232,7 +233,6 @@ const TEACH_CHROME: Component = {
|
|
|
232
233
|
'--teach-sans',
|
|
233
234
|
'--teach-mono',
|
|
234
235
|
'--teach-measure',
|
|
235
|
-
'--teach-chrome',
|
|
236
236
|
'--teach-shadow',
|
|
237
237
|
'--color-teach-accent-bg',
|
|
238
238
|
'--type-body-family',
|
|
@@ -247,8 +247,7 @@ const TEACH_CHROME: Component = {
|
|
|
247
247
|
--teach-hand: 'Virgil', 'Excalifont', cursive;
|
|
248
248
|
--teach-mono: var(--type-code-family);
|
|
249
249
|
--teach-measure: 52rem;
|
|
250
|
-
--teach-
|
|
251
|
-
--teach-mast-h: 4.4rem;
|
|
250
|
+
--teach-mast-h: 3.5rem;
|
|
252
251
|
--teach-shadow: 0 1px 2px rgba(20, 20, 20, 0.04);
|
|
253
252
|
--color-teach-accent-bg: color-mix(in srgb, var(--color-accent) 14%, var(--color-background));
|
|
254
253
|
|
|
@@ -292,7 +291,7 @@ body {
|
|
|
292
291
|
color: var(--color-text-body);
|
|
293
292
|
font-family: var(--teach-sans);
|
|
294
293
|
font-size: var(--t3);
|
|
295
|
-
line-height: 1.
|
|
294
|
+
line-height: 1.55;
|
|
296
295
|
-webkit-font-smoothing: antialiased;
|
|
297
296
|
}
|
|
298
297
|
|
|
@@ -328,6 +327,7 @@ html.sb-shut .pane { border-left: 0; }
|
|
|
328
327
|
background: var(--color-background);
|
|
329
328
|
border-bottom: 1px solid var(--color-border);
|
|
330
329
|
padding: 0 1.5rem;
|
|
330
|
+
cursor: default;
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
/* The reading bar rides the masthead's own bottom edge rather than arriving as
|
|
@@ -345,37 +345,26 @@ html.sb-shut .pane { border-left: 0; }
|
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
/* The height is the token rather than the sum of two paddings, so the sidebar's
|
|
348
|
-
own top row lands on the same seam the bar's bottom border draws.
|
|
348
|
+
own top row lands on the same seam the bar's bottom border draws. The row
|
|
349
|
+
spans the window rather than the reading measure: capped, the toggle and the
|
|
350
|
+
theme control floated in from the edges by an amount that changed per page. */
|
|
349
351
|
.mast {
|
|
350
|
-
max-width:
|
|
351
|
-
margin: 0
|
|
352
|
+
max-width: none;
|
|
353
|
+
margin: 0;
|
|
352
354
|
min-height: var(--teach-mast-h);
|
|
353
|
-
padding: 0;
|
|
355
|
+
padding: 0 0.9rem;
|
|
354
356
|
display: flex;
|
|
355
357
|
justify-content: space-between;
|
|
356
358
|
align-items: center;
|
|
357
|
-
gap:
|
|
359
|
+
gap: 0.55rem;
|
|
358
360
|
font-size: var(--t5);
|
|
359
361
|
color: var(--color-muted);
|
|
362
|
+
cursor: default;
|
|
360
363
|
}
|
|
361
364
|
|
|
362
365
|
.mast a { color: var(--color-accent); text-decoration: none; font-weight: 600; }
|
|
363
366
|
.mast a:hover { text-decoration: underline; }
|
|
364
367
|
|
|
365
|
-
.theme {
|
|
366
|
-
font-family: var(--teach-sans);
|
|
367
|
-
font-size: 0.75rem;
|
|
368
|
-
font-weight: 600;
|
|
369
|
-
color: var(--color-muted);
|
|
370
|
-
background: transparent;
|
|
371
|
-
border: 1px solid var(--color-border);
|
|
372
|
-
border-radius: 99px;
|
|
373
|
-
padding: 0.25rem 0.7rem;
|
|
374
|
-
cursor: pointer;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
.theme:hover { border-color: var(--color-accent); color: var(--color-accent); }
|
|
378
|
-
|
|
379
368
|
.nav {
|
|
380
369
|
width: 100%;
|
|
381
370
|
margin: 0;
|
|
@@ -404,10 +393,9 @@ html.sb-shut .pane { border-left: 0; }
|
|
|
404
393
|
|
|
405
394
|
.nav .lbl {
|
|
406
395
|
display: block;
|
|
407
|
-
font-size:
|
|
396
|
+
font-size: var(--t6);
|
|
408
397
|
font-weight: 700;
|
|
409
|
-
letter-spacing: 0
|
|
410
|
-
text-transform: uppercase;
|
|
398
|
+
letter-spacing: 0;
|
|
411
399
|
color: var(--color-muted);
|
|
412
400
|
margin-bottom: 0.15rem;
|
|
413
401
|
}
|
|
@@ -422,7 +410,7 @@ html.sb-shut .pane { border-left: 0; }
|
|
|
422
410
|
restates either. */
|
|
423
411
|
/* A breadcrumb rather than a row of links. The gap tightens because the
|
|
424
412
|
separators now carry the spacing the gap used to. */
|
|
425
|
-
.mast-left { display: flex; align-items: center; gap: 0.
|
|
413
|
+
.mast-left { display: flex; align-items: center; gap: 0.3rem; min-width: 0; flex-wrap: wrap; cursor: default; }
|
|
426
414
|
|
|
427
415
|
/* \`--rule\` measured 1.27:1 here. It is a border token, and a border is a
|
|
428
416
|
shape a reader infers rather than a glyph they resolve, so it is the wrong
|
|
@@ -430,6 +418,7 @@ html.sb-shut .pane { border-left: 0; }
|
|
|
430
418
|
.crumb-sep {
|
|
431
419
|
color: var(--color-muted);
|
|
432
420
|
font-size: var(--t4);
|
|
421
|
+
margin: 0 0.1rem;
|
|
433
422
|
user-select: none;
|
|
434
423
|
}
|
|
435
424
|
|
|
@@ -442,27 +431,55 @@ html.sb-shut .pane { border-left: 0; }
|
|
|
442
431
|
|
|
443
432
|
.crumb { display: inline-flex; align-items: center; }
|
|
444
433
|
|
|
445
|
-
/* The label and its caret are one
|
|
446
|
-
the label navigates, the caret opens the menu
|
|
447
|
-
|
|
434
|
+
/* The label and its caret are one component with two zones, like a split
|
|
435
|
+
button: the label navigates, the caret opens the menu, and one surface fills
|
|
436
|
+
for both so the pair reads as a single chip rather than two loose pieces.
|
|
437
|
+
The pointer covers the gap between them, which is interactive on both sides. */
|
|
438
|
+
.crumb-item {
|
|
439
|
+
display: inline-flex;
|
|
440
|
+
align-items: center;
|
|
441
|
+
gap: 0;
|
|
442
|
+
border-radius: 7px;
|
|
443
|
+
padding: 0.1rem 0.15rem 0.1rem 0.35rem;
|
|
444
|
+
cursor: pointer;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.crumb-item:has(details.jump):hover,
|
|
448
|
+
.crumb-item:has(details.jump[open]) { background: var(--color-chrome); }
|
|
448
449
|
|
|
449
|
-
.crumb
|
|
450
|
+
/* Accent reports where you are. A crumb is navigation you can take, so it is
|
|
451
|
+
neutral, and \`.mast a\` keeps the accent for any other bar link. */
|
|
452
|
+
.mast a.crumb { color: var(--color-text-secondary); padding: 0.1rem 0.15rem 0.1rem 0; }
|
|
453
|
+
.mast a.crumb:hover { text-decoration: none; }
|
|
454
|
+
.crumb-item:hover a.crumb { color: var(--color-text); }
|
|
450
455
|
|
|
451
|
-
|
|
452
|
-
|
|
456
|
+
.crumb-here { color: var(--color-text); font-weight: 650; }
|
|
457
|
+
|
|
458
|
+
/* \`align-items: center\` centres boxes, and a label's line box carries
|
|
459
|
+
half-leading above the cap and descender room below, so its capitals sit
|
|
460
|
+
above its own centre by an amount that scales with the font size. Trimming
|
|
461
|
+
the label to its cap band fixes the box rather than nudging the mark. It
|
|
462
|
+
lands on a span holding the text alone, since the link's own \`inline-flex\`
|
|
463
|
+
ignores it. Firefox ships no \`text-box\` and keeps the caret a pixel low. */
|
|
464
|
+
.crumb-t, .crumb-here, .sb-ws .ws-name {
|
|
465
|
+
display: block;
|
|
466
|
+
text-box: trim-both cap alphabetic;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/* The caret is the whole control, so it carries the 24 pixel minimum on its
|
|
470
|
+
own rather than inheriting a label's width. It fills nothing itself, since
|
|
471
|
+
the chip around it does. */
|
|
453
472
|
.crumb-item > .jump > summary {
|
|
454
473
|
min-width: 1.5rem;
|
|
455
474
|
min-height: 1.5rem;
|
|
456
475
|
display: inline-flex;
|
|
457
476
|
align-items: center;
|
|
458
477
|
justify-content: center;
|
|
459
|
-
padding: 0;
|
|
478
|
+
padding: 0.15rem 0.3rem 0.15rem 0;
|
|
460
479
|
border: 0;
|
|
461
480
|
background: transparent;
|
|
462
481
|
}
|
|
463
482
|
|
|
464
|
-
.crumb-item > .jump > summary:hover { background: var(--color-surface); border-radius: 5px; }
|
|
465
|
-
|
|
466
483
|
/* A bar link is its own tap target rather than a line of text, so it carries
|
|
467
484
|
the 24 pixel minimum the a11y rule sets. */
|
|
468
485
|
.mast a,
|
|
@@ -478,23 +495,25 @@ html.sb-shut .pane { border-left: 0; }
|
|
|
478
495
|
color: var(--color-muted);
|
|
479
496
|
}
|
|
480
497
|
|
|
481
|
-
.mast-right { display: flex; align-items: center; gap: 0.
|
|
498
|
+
.mast-right { display: flex; align-items: center; gap: 0.2rem; margin-left: auto; }
|
|
482
499
|
|
|
500
|
+
/* No border, at the fold control's size and radius, so every control on the
|
|
501
|
+
bar is drawn one way. */
|
|
483
502
|
.theme {
|
|
484
|
-
width: 1.
|
|
485
|
-
height: 1.
|
|
503
|
+
width: 1.75rem;
|
|
504
|
+
height: 1.75rem;
|
|
486
505
|
display: inline-flex;
|
|
487
506
|
align-items: center;
|
|
488
507
|
justify-content: center;
|
|
489
508
|
padding: 0;
|
|
490
|
-
border:
|
|
491
|
-
border-radius:
|
|
509
|
+
border: 0;
|
|
510
|
+
border-radius: 6px;
|
|
492
511
|
background: transparent;
|
|
493
512
|
color: var(--color-muted);
|
|
494
513
|
cursor: pointer;
|
|
495
514
|
}
|
|
496
515
|
|
|
497
|
-
.theme:hover {
|
|
516
|
+
.theme:hover { color: var(--color-text); background: var(--color-chrome); }
|
|
498
517
|
.theme svg { width: 0.95rem; height: 0.95rem; display: block; }
|
|
499
518
|
.theme .moon { display: none; }
|
|
500
519
|
:root[data-theme="dark"] .theme .sun { display: none; }
|
|
@@ -528,7 +547,7 @@ html.sb-shut .pane { border-left: 0; }
|
|
|
528
547
|
cursor: pointer;
|
|
529
548
|
}
|
|
530
549
|
|
|
531
|
-
.sb-fold:hover { color: var(--color-text); background: var(--color-
|
|
550
|
+
.sb-fold:hover { color: var(--color-text); background: var(--color-chrome); }
|
|
532
551
|
|
|
533
552
|
.sb {
|
|
534
553
|
flex: 0 0 var(--sb-w, 16rem);
|
|
@@ -564,7 +583,7 @@ html.sb-shut .sb { flex-basis: 0; width: 0; overflow: hidden; }
|
|
|
564
583
|
display: inline-flex;
|
|
565
584
|
align-items: center;
|
|
566
585
|
gap: 0.35rem;
|
|
567
|
-
padding: 0.
|
|
586
|
+
padding: 0.25rem 0.3rem;
|
|
568
587
|
border-radius: 6px;
|
|
569
588
|
font-size: var(--t4);
|
|
570
589
|
font-weight: 650;
|
|
@@ -572,7 +591,7 @@ html.sb-shut .sb { flex-basis: 0; width: 0; overflow: hidden; }
|
|
|
572
591
|
}
|
|
573
592
|
|
|
574
593
|
.sb-ws > summary::-webkit-details-marker { display: none; }
|
|
575
|
-
.sb-ws > summary:hover, .sb-ws[open] > summary { background: var(--color-
|
|
594
|
+
.sb-ws > summary:hover, .sb-ws[open] > summary { background: var(--color-chrome); color: var(--color-text); }
|
|
576
595
|
.sb-ws .car { color: var(--color-muted); display: inline-flex; align-items: center; }
|
|
577
596
|
|
|
578
597
|
.sb-wl {
|
|
@@ -585,7 +604,7 @@ html.sb-shut .sb { flex-basis: 0; width: 0; overflow: hidden; }
|
|
|
585
604
|
border: 1px solid var(--color-border);
|
|
586
605
|
border-radius: 9px;
|
|
587
606
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14);
|
|
588
|
-
padding: 0.
|
|
607
|
+
padding: 0.3rem;
|
|
589
608
|
}
|
|
590
609
|
|
|
591
610
|
:root[data-theme="dark"] .sb-wl { box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5); }
|
|
@@ -644,7 +663,7 @@ html.sb-shut .sb { flex-basis: 0; width: 0; overflow: hidden; }
|
|
|
644
663
|
line-height: 1.45;
|
|
645
664
|
}
|
|
646
665
|
|
|
647
|
-
.sb-l:hover { background: var(--color-
|
|
666
|
+
.sb-l:hover { background: var(--color-chrome); color: var(--color-text); }
|
|
648
667
|
|
|
649
668
|
.sb-n {
|
|
650
669
|
flex: none;
|
|
@@ -764,46 +783,47 @@ html.sb-shut .sb-grip { display: none; }
|
|
|
764
783
|
}
|
|
765
784
|
|
|
766
785
|
.jump summary::-webkit-details-marker { display: none; }
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
.jump summary .caret {
|
|
772
|
-
.jump summary:hover .caret { opacity: 1; }
|
|
773
|
-
.jump[open] summary { border-color: var(--color-accent); color: var(--color-accent); }
|
|
774
|
-
.jump[open] summary .caret { opacity: 1; }
|
|
786
|
+
/* The caret says "this is a menu" and nothing else, so it is drawn at full
|
|
787
|
+
strength in every state and stays muted. A reveal on hover hid the only
|
|
788
|
+
route into the menu from a reader who cannot hover, and an affordance is not
|
|
789
|
+
a state, so it takes no accent on hover or while open. */
|
|
790
|
+
.jump summary .caret { color: var(--color-muted); }
|
|
775
791
|
|
|
776
792
|
.jump-list {
|
|
777
793
|
position: absolute;
|
|
778
794
|
top: calc(100% + 0.4rem);
|
|
779
795
|
left: 0;
|
|
780
796
|
z-index: 20;
|
|
781
|
-
min-width: 21rem;
|
|
782
797
|
background: var(--color-background);
|
|
783
798
|
border: 1px solid var(--color-border);
|
|
784
799
|
border-radius: 9px;
|
|
785
800
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14);
|
|
786
|
-
padding: 0.
|
|
801
|
+
padding: 0.25rem;
|
|
787
802
|
list-style: none;
|
|
788
803
|
margin: 0;
|
|
789
804
|
}
|
|
790
805
|
|
|
791
806
|
:root[data-theme="dark"] .jump-list { box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5); }
|
|
792
807
|
|
|
808
|
+
/* One row recipe wherever the menu is mounted, on the steps the lesson list
|
|
809
|
+
beside it already reads: the label at \`--t5\` and the numeral at \`--t6\` mono.
|
|
810
|
+
The weight is restated because \`.mast a\` sets 600 for the bar's own links. */
|
|
793
811
|
.jump-list a {
|
|
794
812
|
display: grid;
|
|
795
813
|
grid-template-columns: 1.65rem 1fr auto;
|
|
796
|
-
gap: 0.
|
|
814
|
+
gap: 0.55rem;
|
|
797
815
|
align-items: center;
|
|
798
|
-
padding: 0.
|
|
816
|
+
padding: 0.38rem 0.55rem;
|
|
799
817
|
border-radius: 6px;
|
|
800
818
|
text-decoration: none;
|
|
801
819
|
color: var(--color-text-body);
|
|
802
|
-
font-size:
|
|
820
|
+
font-size: var(--t5);
|
|
821
|
+
font-weight: 400;
|
|
822
|
+
line-height: 1.45;
|
|
803
823
|
}
|
|
804
824
|
|
|
805
|
-
.jump-list a:hover { background: var(--color-
|
|
806
|
-
.jump-list .n { font-family: var(--teach-mono); font-size:
|
|
825
|
+
.jump-list a:hover { background: var(--color-chrome); }
|
|
826
|
+
.jump-list .n { font-family: var(--teach-mono); font-size: var(--t6); color: var(--color-muted); }
|
|
807
827
|
/* The third column used to hold a status dot that read the same on every row,
|
|
808
828
|
so it reported nothing. It is a per-entry trailing slot now: the workspace
|
|
809
829
|
menu puts a lesson count there and the lesson menu leaves it empty. */
|
|
@@ -825,11 +845,11 @@ html.sb-shut .sb-grip { display: none; }
|
|
|
825
845
|
.jump-list li.all a { color: var(--color-accent); }
|
|
826
846
|
.jump-list li.all .n { color: var(--color-accent); }
|
|
827
847
|
|
|
828
|
-
/* The menu
|
|
829
|
-
|
|
830
|
-
|
|
848
|
+
/* The menu fits its content above an 11rem floor rather than padding three
|
|
849
|
+
short titles out to a wide panel. It is anchored to its summary, so the
|
|
850
|
+
clamp keeps a long row wrapping on screen without a breakpoint. */
|
|
831
851
|
.jump-list {
|
|
832
|
-
min-width: min(
|
|
852
|
+
min-width: min(11rem, calc(100vw - 2rem));
|
|
833
853
|
max-width: calc(100vw - 2rem);
|
|
834
854
|
}
|
|
835
855
|
|
|
@@ -850,8 +870,7 @@ html.sb-shut .sb-grip { display: none; }
|
|
|
850
870
|
@media (max-width: 640px) {
|
|
851
871
|
.bar { padding: 0 1rem; }
|
|
852
872
|
main { padding-left: 1rem; padding-right: 1rem; }
|
|
853
|
-
.mast { font-size: 0.75rem;
|
|
854
|
-
.mast-left { gap: 0.5rem; row-gap: 0.35rem; }
|
|
873
|
+
.mast { font-size: 0.75rem; }
|
|
855
874
|
}
|
|
856
875
|
|
|
857
876
|
@media (max-width: 640px) {
|
|
@@ -933,7 +952,7 @@ html.sb-shut .sb-grip { display: none; }
|
|
|
933
952
|
cursor: pointer;
|
|
934
953
|
}
|
|
935
954
|
|
|
936
|
-
.sb-close:hover { color: var(--color-text); background: var(--color-
|
|
955
|
+
.sb-close:hover { color: var(--color-text); background: var(--color-chrome); }
|
|
937
956
|
|
|
938
957
|
.sb-scrim {
|
|
939
958
|
display: block;
|
|
@@ -971,6 +990,9 @@ const TEACH_ARTICLE: Component = {
|
|
|
971
990
|
'--color-accent',
|
|
972
991
|
'--teach-hand',
|
|
973
992
|
'--teach-mono',
|
|
993
|
+
'--t1',
|
|
994
|
+
'--t2',
|
|
995
|
+
'--t3',
|
|
974
996
|
'--t4',
|
|
975
997
|
'--t5',
|
|
976
998
|
'--t6',
|
|
@@ -978,8 +1000,8 @@ const TEACH_ARTICLE: Component = {
|
|
|
978
1000
|
rules: `/* ---- Type ---- */
|
|
979
1001
|
|
|
980
1002
|
h1 {
|
|
981
|
-
font-size:
|
|
982
|
-
line-height: 1.
|
|
1003
|
+
font-size: var(--t1);
|
|
1004
|
+
line-height: 1.1;
|
|
983
1005
|
font-weight: 800;
|
|
984
1006
|
letter-spacing: -0.022em;
|
|
985
1007
|
margin: 0 0 0.55rem;
|
|
@@ -987,19 +1009,20 @@ h1 {
|
|
|
987
1009
|
}
|
|
988
1010
|
|
|
989
1011
|
h2 {
|
|
990
|
-
font-size:
|
|
1012
|
+
font-size: var(--t2);
|
|
1013
|
+
line-height: 1.3;
|
|
991
1014
|
font-weight: 700;
|
|
992
1015
|
letter-spacing: -0.012em;
|
|
993
1016
|
margin: 3rem 0 0.85rem;
|
|
994
1017
|
}
|
|
995
1018
|
|
|
996
|
-
h3 { font-size: 1.
|
|
1019
|
+
h3 { font-size: var(--t3); line-height: 1.55; font-weight: 700; margin: 1.9rem 0 0.4rem; }
|
|
997
1020
|
|
|
998
1021
|
p { margin: 0 0 1.15rem; }
|
|
999
1022
|
|
|
1000
1023
|
.lede {
|
|
1001
|
-
font-size:
|
|
1002
|
-
line-height: 1.
|
|
1024
|
+
font-size: var(--t3);
|
|
1025
|
+
line-height: 1.55;
|
|
1003
1026
|
color: var(--color-text-secondary);
|
|
1004
1027
|
margin-bottom: 2rem;
|
|
1005
1028
|
}
|
|
@@ -1011,8 +1034,8 @@ em { font-style: italic; }
|
|
|
1011
1034
|
/* ---- Panels ---- */
|
|
1012
1035
|
|
|
1013
1036
|
.assumes, .progress {
|
|
1014
|
-
font-size:
|
|
1015
|
-
line-height: 1.
|
|
1037
|
+
font-size: var(--t5);
|
|
1038
|
+
line-height: 1.6;
|
|
1016
1039
|
color: var(--color-text-secondary);
|
|
1017
1040
|
background: var(--color-surface);
|
|
1018
1041
|
border-radius: 7px;
|
|
@@ -1024,8 +1047,7 @@ em { font-style: italic; }
|
|
|
1024
1047
|
display: block;
|
|
1025
1048
|
color: var(--color-muted);
|
|
1026
1049
|
font-weight: 700;
|
|
1027
|
-
letter-spacing: 0
|
|
1028
|
-
text-transform: uppercase;
|
|
1050
|
+
letter-spacing: 0;
|
|
1029
1051
|
font-size: var(--t6);
|
|
1030
1052
|
margin-bottom: 0.2rem;
|
|
1031
1053
|
}
|
|
@@ -1043,7 +1065,7 @@ code {
|
|
|
1043
1065
|
pre {
|
|
1044
1066
|
font-family: var(--teach-mono);
|
|
1045
1067
|
font-size: var(--t5);
|
|
1046
|
-
line-height: 1.
|
|
1068
|
+
line-height: 1.6;
|
|
1047
1069
|
background: var(--color-surface);
|
|
1048
1070
|
padding: 1.15rem 1.3rem;
|
|
1049
1071
|
border-radius: 7px;
|
|
@@ -1064,7 +1086,7 @@ pre code { background: none; padding: 0; font-size: 1em; }
|
|
|
1064
1086
|
|
|
1065
1087
|
.hard-label {
|
|
1066
1088
|
font-family: var(--teach-hand);
|
|
1067
|
-
font-size:
|
|
1089
|
+
font-size: var(--t3);
|
|
1068
1090
|
color: var(--color-accent);
|
|
1069
1091
|
display: block;
|
|
1070
1092
|
margin-bottom: 0.35rem;
|
|
@@ -1074,15 +1096,14 @@ pre code { background: none; padding: 0; font-size: 1em; }
|
|
|
1074
1096
|
|
|
1075
1097
|
/* ---- Tables ---- */
|
|
1076
1098
|
|
|
1077
|
-
table { width: 100%; border-collapse: collapse; font-size:
|
|
1099
|
+
table { width: 100%; border-collapse: collapse; font-size: var(--t4); margin: 0 0 1.5rem; }
|
|
1078
1100
|
th, td { text-align: right; padding: 0.55rem 0.7rem; border-bottom: 1px solid var(--color-border); }
|
|
1079
1101
|
th:first-child, td:first-child { text-align: left; }
|
|
1080
1102
|
|
|
1081
1103
|
th {
|
|
1082
1104
|
font-weight: 700;
|
|
1083
|
-
font-size:
|
|
1084
|
-
letter-spacing: 0
|
|
1085
|
-
text-transform: uppercase;
|
|
1105
|
+
font-size: var(--t5);
|
|
1106
|
+
letter-spacing: 0;
|
|
1086
1107
|
color: var(--color-muted);
|
|
1087
1108
|
border-bottom-color: var(--color-border);
|
|
1088
1109
|
}
|
|
@@ -1107,26 +1128,22 @@ tbody tr.mark td { background: var(--color-teach-accent-bg); }
|
|
|
1107
1128
|
}
|
|
1108
1129
|
|
|
1109
1130
|
.toc a:hover { background: var(--color-surface); }
|
|
1110
|
-
.toc .num { font-family: var(--teach-mono); font-size: var(--t5); color: var(--color-
|
|
1111
|
-
.toc b { font-size:
|
|
1131
|
+
.toc .num { font-family: var(--teach-mono); font-size: var(--t5); color: var(--color-muted); }
|
|
1132
|
+
.toc b { font-size: var(--t3); font-weight: 700; letter-spacing: -0.012em; }
|
|
1112
1133
|
|
|
1113
1134
|
.toc .blurb {
|
|
1114
1135
|
grid-column: 2;
|
|
1115
1136
|
font-size: var(--t4);
|
|
1116
|
-
line-height: 1.
|
|
1137
|
+
line-height: 1.55;
|
|
1117
1138
|
color: var(--color-text-secondary);
|
|
1118
1139
|
margin-top: 0.1rem;
|
|
1119
1140
|
}
|
|
1120
1141
|
|
|
1121
|
-
/* A
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
case and no tracking, per the UI copy rule. */
|
|
1142
|
+
/* A word, not a pill and not a dot. A mark reading the same on every live row
|
|
1143
|
+
reports nothing, so the dot retired here as it did from the jump menus.
|
|
1144
|
+
Sentence case and no tracking, per the UI copy rule. */
|
|
1125
1145
|
.toc .state {
|
|
1126
|
-
|
|
1127
|
-
align-items: center;
|
|
1128
|
-
gap: 0.42rem;
|
|
1129
|
-
font-size: 0.75rem;
|
|
1146
|
+
font-size: var(--t5);
|
|
1130
1147
|
font-weight: 500;
|
|
1131
1148
|
letter-spacing: 0;
|
|
1132
1149
|
text-transform: none;
|
|
@@ -1134,24 +1151,10 @@ tbody tr.mark td { background: var(--color-teach-accent-bg); }
|
|
|
1134
1151
|
white-space: nowrap;
|
|
1135
1152
|
}
|
|
1136
1153
|
|
|
1137
|
-
.toc .state::before {
|
|
1138
|
-
content: "";
|
|
1139
|
-
width: 6px;
|
|
1140
|
-
height: 6px;
|
|
1141
|
-
border-radius: 99px;
|
|
1142
|
-
background: var(--color-border);
|
|
1143
|
-
flex: none;
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
.toc .state.done { color: var(--color-text-secondary); }
|
|
1147
|
-
.toc .state.done::before { background: var(--color-accent); }
|
|
1148
|
-
.toc .state.next { color: var(--color-text-secondary); }
|
|
1149
|
-
.toc .state.next::before { background: var(--color-accent); }
|
|
1150
|
-
|
|
1151
1154
|
/* A link leaving the page says so with a mark beside it rather than with a
|
|
1152
1155
|
badge spelling out the sentence. */
|
|
1153
1156
|
.toc .ext {
|
|
1154
|
-
font-size:
|
|
1157
|
+
font-size: var(--t4);
|
|
1155
1158
|
color: var(--color-muted);
|
|
1156
1159
|
margin-left: 0.35rem;
|
|
1157
1160
|
}
|
|
@@ -1162,7 +1165,7 @@ ol.succ {
|
|
|
1162
1165
|
margin: 0 0 2.5rem;
|
|
1163
1166
|
padding-left: 1.35rem;
|
|
1164
1167
|
font-size: var(--t4);
|
|
1165
|
-
line-height: 1.
|
|
1168
|
+
line-height: 1.55;
|
|
1166
1169
|
color: var(--color-text-secondary);
|
|
1167
1170
|
}
|
|
1168
1171
|
|
|
@@ -1185,10 +1188,6 @@ ol.succ li { margin-bottom: 0.4rem; }
|
|
|
1185
1188
|
accent. Suppressing it only where the token above replaces it. */
|
|
1186
1189
|
:where(a, button, summary, input, .opt):focus:not(:focus-visible) { outline: none; }
|
|
1187
1190
|
|
|
1188
|
-
@media (max-width: 640px) {
|
|
1189
|
-
body { font-size: 1.0625rem; }
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
1191
|
/* The week plan. Its markup shipped with no rule matching any of its five
|
|
1193
1192
|
classes, so it rendered as a run of inline spans with the ordinal, the
|
|
1194
1193
|
title, the description, and the duration all touching. */
|
|
@@ -1218,7 +1217,7 @@ ol.succ li { margin-bottom: 0.4rem; }
|
|
|
1218
1217
|
|
|
1219
1218
|
.road-t {
|
|
1220
1219
|
font-family: var(--teach-mono);
|
|
1221
|
-
font-size:
|
|
1220
|
+
font-size: var(--t5);
|
|
1222
1221
|
color: var(--color-muted);
|
|
1223
1222
|
white-space: nowrap;
|
|
1224
1223
|
}
|
|
@@ -1253,6 +1252,7 @@ const TEACH_QUIZ: Component = {
|
|
|
1253
1252
|
'--color-teach-accent-bg',
|
|
1254
1253
|
'--t4',
|
|
1255
1254
|
'--t5',
|
|
1255
|
+
'--t6',
|
|
1256
1256
|
],
|
|
1257
1257
|
rules: `/* ---- Quiz ---- */
|
|
1258
1258
|
|
|
@@ -1284,7 +1284,7 @@ const TEACH_QUIZ: Component = {
|
|
|
1284
1284
|
.opt::before {
|
|
1285
1285
|
content: attr(data-k);
|
|
1286
1286
|
font-family: var(--teach-mono);
|
|
1287
|
-
font-size:
|
|
1287
|
+
font-size: var(--t6);
|
|
1288
1288
|
color: var(--color-muted);
|
|
1289
1289
|
border: 1px solid var(--color-border);
|
|
1290
1290
|
border-radius: 5px;
|
|
@@ -1309,23 +1309,21 @@ const TEACH_QUIZ: Component = {
|
|
|
1309
1309
|
}
|
|
1310
1310
|
|
|
1311
1311
|
.opt[data-state="chosen"]::after {
|
|
1312
|
-
content: "
|
|
1312
|
+
content: "Your answer";
|
|
1313
1313
|
margin-left: auto;
|
|
1314
|
-
font-size:
|
|
1314
|
+
font-size: var(--t6);
|
|
1315
1315
|
font-weight: 700;
|
|
1316
|
-
letter-spacing: 0
|
|
1317
|
-
text-transform: uppercase;
|
|
1316
|
+
letter-spacing: 0;
|
|
1318
1317
|
color: var(--color-muted);
|
|
1319
1318
|
align-self: center;
|
|
1320
1319
|
}
|
|
1321
1320
|
|
|
1322
1321
|
.opt[data-state="right"]::after {
|
|
1323
|
-
content: "
|
|
1322
|
+
content: "Correct";
|
|
1324
1323
|
margin-left: auto;
|
|
1325
|
-
font-size:
|
|
1324
|
+
font-size: var(--t6);
|
|
1326
1325
|
font-weight: 700;
|
|
1327
|
-
letter-spacing: 0
|
|
1328
|
-
text-transform: uppercase;
|
|
1326
|
+
letter-spacing: 0;
|
|
1329
1327
|
color: var(--color-accent);
|
|
1330
1328
|
align-self: center;
|
|
1331
1329
|
}
|
|
@@ -1374,34 +1372,37 @@ const TEACH_GLOSSARY: Component = {
|
|
|
1374
1372
|
],
|
|
1375
1373
|
rules: `/* ---- Glossary view, generated from the markdown source ---- */
|
|
1376
1374
|
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
margin-left: 0.5rem;
|
|
1386
|
-
vertical-align: middle;
|
|
1375
|
+
/* No border, sitting on the page ground, with the count beside it. The border and the
|
|
1376
|
+
reading surface are what made the control look like something to fill in,
|
|
1377
|
+
and the count is what says it filters the list under it. */
|
|
1378
|
+
.filter-row {
|
|
1379
|
+
display: flex;
|
|
1380
|
+
align-items: baseline;
|
|
1381
|
+
gap: 1rem;
|
|
1382
|
+
margin: 0 0 1rem;
|
|
1387
1383
|
}
|
|
1388
1384
|
|
|
1389
1385
|
.filter {
|
|
1390
|
-
|
|
1386
|
+
flex: 1;
|
|
1387
|
+
min-width: 0;
|
|
1391
1388
|
font-family: var(--teach-sans);
|
|
1392
1389
|
font-size: var(--t4);
|
|
1393
1390
|
color: var(--color-text-body);
|
|
1394
|
-
background:
|
|
1395
|
-
border:
|
|
1396
|
-
|
|
1397
|
-
padding: 0.55rem 0.8rem;
|
|
1398
|
-
margin: 0 0 1rem;
|
|
1391
|
+
background: transparent;
|
|
1392
|
+
border: 0;
|
|
1393
|
+
padding: 0.3rem 0;
|
|
1399
1394
|
}
|
|
1400
1395
|
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1396
|
+
.filter-count {
|
|
1397
|
+
font-family: var(--teach-mono);
|
|
1398
|
+
font-size: var(--t5);
|
|
1399
|
+
color: var(--color-muted);
|
|
1400
|
+
white-space: nowrap;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
/* The \`:where(...)\` focus token has zero specificity, so a \`.filter:focus\`
|
|
1404
|
+
rule outranks it and leaves a keyboard reaching the control with no ring.
|
|
1405
|
+
The ring stays the token's and only the pointer ring is dropped. */
|
|
1405
1406
|
.filter:focus:not(:focus-visible) { outline: none; }
|
|
1406
1407
|
.filter::placeholder { color: var(--color-muted); }
|
|
1407
1408
|
|
|
@@ -1449,8 +1450,7 @@ h2 .count {
|
|
|
1449
1450
|
font-size: var(--t5);
|
|
1450
1451
|
font-weight: 600;
|
|
1451
1452
|
color: var(--color-muted);
|
|
1452
|
-
|
|
1453
|
-
letter-spacing: 0.02em;
|
|
1453
|
+
letter-spacing: 0;
|
|
1454
1454
|
}
|
|
1455
1455
|
|
|
1456
1456
|
.gloss-group:first-of-type { margin-top: 0; }
|
|
@@ -1469,7 +1469,7 @@ ol.succ {
|
|
|
1469
1469
|
margin: 0 0 2.5rem;
|
|
1470
1470
|
padding-left: 1.35rem;
|
|
1471
1471
|
font-size: var(--t4);
|
|
1472
|
-
line-height: 1.
|
|
1472
|
+
line-height: 1.55;
|
|
1473
1473
|
color: var(--color-text-secondary);
|
|
1474
1474
|
}
|
|
1475
1475
|
|
|
@@ -1479,7 +1479,7 @@ ol.succ li { margin-bottom: 0.4rem; }
|
|
|
1479
1479
|
/* A filter that matches nothing needs a way back, not just a sentence. */
|
|
1480
1480
|
.gloss .empty .clear {
|
|
1481
1481
|
font-family: var(--teach-sans);
|
|
1482
|
-
font-size:
|
|
1482
|
+
font-size: var(--t4);
|
|
1483
1483
|
color: var(--color-accent);
|
|
1484
1484
|
background: none;
|
|
1485
1485
|
border: 0;
|
|
@@ -1534,6 +1534,7 @@ const TEACH_REFERENCES: Component = {
|
|
|
1534
1534
|
'--teach-mono',
|
|
1535
1535
|
'--color-teach-accent-bg',
|
|
1536
1536
|
'--t5',
|
|
1537
|
+
'--t6',
|
|
1537
1538
|
],
|
|
1538
1539
|
rules: `footer {
|
|
1539
1540
|
margin-top: 4rem;
|
|
@@ -1548,7 +1549,7 @@ const TEACH_REFERENCES: Component = {
|
|
|
1548
1549
|
|
|
1549
1550
|
sup.cite {
|
|
1550
1551
|
font-family: var(--teach-mono);
|
|
1551
|
-
font-size:
|
|
1552
|
+
font-size: var(--t6);
|
|
1552
1553
|
line-height: 0;
|
|
1553
1554
|
margin-left: 0.1em;
|
|
1554
1555
|
}
|
|
@@ -1569,7 +1570,7 @@ ol.refs {
|
|
|
1569
1570
|
padding: 0;
|
|
1570
1571
|
margin: 0.9rem 0 1.1rem;
|
|
1571
1572
|
font-size: var(--t5);
|
|
1572
|
-
line-height: 1.
|
|
1573
|
+
line-height: 1.6;
|
|
1573
1574
|
}
|
|
1574
1575
|
|
|
1575
1576
|
ol.refs li {
|