explico 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (298) hide show
  1. package/CHANGELOG.md +1292 -1
  2. package/README.md +91 -13
  3. package/dist/compiler/astro.d.ts +11 -4
  4. package/dist/compiler/astro.js +48 -15
  5. package/dist/compiler/cli.d.ts +27 -12
  6. package/dist/compiler/cli.js +96 -25
  7. package/dist/compiler/collections.d.ts +33 -2
  8. package/dist/compiler/collections.js +91 -19
  9. package/dist/compiler/content-schema.d.ts +31 -0
  10. package/dist/compiler/content-schema.js +82 -0
  11. package/dist/compiler/content-schemas.d.ts +422 -0
  12. package/dist/compiler/content-schemas.js +229 -0
  13. package/dist/compiler/course-config.d.ts +1 -1
  14. package/dist/compiler/course-config.js +30 -2
  15. package/dist/compiler/exports/combined-html.d.ts +97 -0
  16. package/dist/compiler/exports/combined-html.js +492 -0
  17. package/dist/compiler/exports/download-index.d.ts +9 -0
  18. package/dist/compiler/exports/download-index.js +33 -0
  19. package/dist/compiler/exports/export-chapters.d.ts +98 -0
  20. package/dist/compiler/exports/export-chapters.js +164 -0
  21. package/dist/compiler/exports/export-pages.d.ts +48 -0
  22. package/dist/compiler/exports/export-pages.js +22 -0
  23. package/dist/compiler/exports/html-tree.d.ts +22 -0
  24. package/dist/compiler/exports/html-tree.js +47 -0
  25. package/dist/compiler/exports/latex/capture.d.ts +106 -0
  26. package/dist/compiler/exports/latex/capture.js +435 -0
  27. package/dist/compiler/exports/latex/compile.d.ts +73 -0
  28. package/dist/compiler/exports/latex/compile.js +115 -0
  29. package/dist/compiler/exports/latex/document.d.ts +102 -0
  30. package/dist/compiler/exports/latex/document.js +349 -0
  31. package/dist/compiler/exports/latex/explico.cls +415 -0
  32. package/dist/compiler/exports/latex/numbering.d.ts +18 -0
  33. package/dist/compiler/exports/latex/numbering.js +57 -0
  34. package/dist/compiler/exports/latex/serialize.d.ts +112 -0
  35. package/dist/compiler/exports/latex/serialize.js +606 -0
  36. package/dist/compiler/exports/latex-export.d.ts +44 -0
  37. package/dist/compiler/exports/latex-export.js +169 -0
  38. package/dist/compiler/exports/markdown/serialize.d.ts +51 -0
  39. package/dist/compiler/exports/markdown/serialize.js +527 -0
  40. package/dist/compiler/exports/markdown-export.d.ts +39 -0
  41. package/dist/compiler/exports/markdown-export.js +240 -0
  42. package/dist/compiler/exports/standalone-html.d.ts +15 -5
  43. package/dist/compiler/exports/standalone-html.js +23 -51
  44. package/dist/compiler/exports/static-tree.d.ts +253 -0
  45. package/dist/compiler/exports/static-tree.js +625 -0
  46. package/dist/compiler/exports/zip.d.ts +16 -0
  47. package/dist/compiler/exports/zip.js +72 -0
  48. package/dist/compiler/lesson-derivation.d.ts +8 -2
  49. package/dist/compiler/lesson-derivation.js +6 -7
  50. package/dist/compiler/manifest.d.ts +115 -8
  51. package/dist/compiler/manifest.js +221 -39
  52. package/dist/compiler/render-diagnostics.d.ts +65 -0
  53. package/dist/compiler/render-diagnostics.js +56 -0
  54. package/dist/compiler/resolution-report.d.ts +3 -1
  55. package/dist/{course-config-Dsnri9Tk.d.ts → course-config-rRtME5UV.d.ts} +56 -13
  56. package/dist/curriculum/validation.d.ts +9 -3
  57. package/dist/curriculum/validation.js +35 -17
  58. package/dist/reference/base-abbreviations.d.mts +2 -2
  59. package/dist/reference/base-abbreviations.mjs +2 -2
  60. package/dist/reference/base-operators.d.mts +8 -2
  61. package/dist/reference/base-operators.mjs +8 -10
  62. package/dist/reference/base-roman-labels.d.mts +4 -3
  63. package/dist/reference/base-roman-labels.mjs +4 -3
  64. package/dist/reference/citation-format.d.mts +35 -0
  65. package/dist/reference/citation-format.mjs +74 -0
  66. package/dist/reference/consistency.d.ts +5 -9
  67. package/dist/reference/consistency.js +34 -38
  68. package/dist/reference/curriculum-alignment.js +3 -24
  69. package/dist/reference/derived-entries.d.ts +45 -0
  70. package/dist/reference/derived-entries.js +50 -0
  71. package/dist/reference/derived-notation.d.mts +125 -0
  72. package/dist/reference/derived-notation.mjs +121 -0
  73. package/dist/reference/diagram-fit.d.ts +45 -0
  74. package/dist/reference/diagram-fit.js +54 -0
  75. package/dist/reference/diagram-layout.d.ts +59 -10
  76. package/dist/reference/diagram-layout.js +85 -24
  77. package/dist/reference/diagrams-validate.d.ts +1 -1
  78. package/dist/reference/equations-validate.d.ts +1 -1
  79. package/dist/reference/equations-validate.js +26 -1
  80. package/dist/reference/equations.d.mts +27 -0
  81. package/dist/reference/equations.mjs +120 -22
  82. package/dist/reference/examples-validate.d.ts +1 -1
  83. package/dist/reference/figure-fit.d.ts +70 -0
  84. package/dist/reference/figure-fit.js +60 -0
  85. package/dist/reference/figure-pictures.d.ts +27 -0
  86. package/dist/reference/figure-pictures.js +49 -0
  87. package/dist/reference/figure-symbols.d.ts +32 -0
  88. package/dist/reference/figure-symbols.js +55 -0
  89. package/dist/reference/figures-validate.d.ts +1 -1
  90. package/dist/reference/gate-abbreviations.d.ts +1 -1
  91. package/dist/reference/gate-abbreviations.js +1 -1
  92. package/dist/reference/gate-course-rules.d.ts +4 -3
  93. package/dist/reference/gate-course-rules.js +9 -4
  94. package/dist/reference/gate-heading-length.d.ts +2 -0
  95. package/dist/reference/gate-heading-length.js +7 -5
  96. package/dist/reference/gate-heading-structure.js +26 -0
  97. package/dist/reference/gate-indices.d.ts +35 -0
  98. package/dist/reference/gate-indices.js +299 -0
  99. package/dist/reference/gate-math.d.ts +32 -2
  100. package/dist/reference/gate-math.js +68 -12
  101. package/dist/reference/gate-mdx-expression.d.ts +17 -0
  102. package/dist/reference/gate-mdx-expression.js +29 -0
  103. package/dist/reference/gate-non-chapter-pages.d.ts +39 -0
  104. package/dist/reference/gate-non-chapter-pages.js +156 -0
  105. package/dist/reference/gate-part-pages.d.ts +4 -2
  106. package/dist/reference/gate-part-pages.js +2 -1
  107. package/dist/reference/gate-prop-text.d.ts +28 -0
  108. package/dist/reference/gate-prop-text.js +106 -0
  109. package/dist/reference/gate-prose-notation.d.ts +3 -1
  110. package/dist/reference/gate-prose-notation.js +4 -2
  111. package/dist/reference/gate-retired-keys.d.ts +35 -0
  112. package/dist/reference/gate-retired-keys.js +58 -0
  113. package/dist/reference/gloss.d.ts +2 -0
  114. package/dist/reference/katex-measure.d.ts +17 -0
  115. package/dist/reference/katex-measure.js +223 -0
  116. package/dist/reference/keyed-float-validate.js +12 -0
  117. package/dist/reference/keyed-jsx-floats.d.mts +2 -2
  118. package/dist/reference/keyed-jsx-floats.mjs +11 -14
  119. package/dist/reference/lab-katex-validate.d.ts +43 -0
  120. package/dist/reference/lab-katex-validate.js +96 -0
  121. package/dist/reference/lab-math-scope.d.ts +1 -0
  122. package/dist/reference/lab-math-scope.js +13 -2
  123. package/dist/reference/lint-ignore.js +1 -1
  124. package/dist/reference/math-glyphs.d.mts +94 -2
  125. package/dist/reference/math-glyphs.mjs +851 -46
  126. package/dist/reference/mixed-content.d.ts +8 -3
  127. package/dist/reference/mixed-content.js +4 -2
  128. package/dist/reference/page-notation.d.ts +39 -7
  129. package/dist/reference/page-notation.js +62 -10
  130. package/dist/reference/raw-floats.mjs +37 -13
  131. package/dist/reference/references.d.ts +11 -1
  132. package/dist/reference/references.js +6 -0
  133. package/dist/reference/registry.js +115 -11
  134. package/dist/reference/rehype-fail-katex-errors.d.mts +9 -0
  135. package/dist/reference/rehype-fail-katex-errors.mjs +19 -0
  136. package/dist/reference/rehype-heading-anchors.mjs +11 -1
  137. package/dist/reference/remark-citation.mjs +0 -0
  138. package/dist/reference/remark-notation.mjs +275 -23
  139. package/dist/reference/render-lab-math.d.ts +13 -2
  140. package/dist/reference/render-lab-math.js +18 -6
  141. package/dist/reference/sections-validate.d.ts +28 -3
  142. package/dist/reference/sections-validate.js +31 -4
  143. package/dist/reference/sections.d.mts +24 -6
  144. package/dist/reference/sections.mjs +92 -17
  145. package/dist/reference/tables-validate.d.ts +1 -1
  146. package/dist/reference/tables-validate.js +24 -1
  147. package/dist/reference/text-measure.d.ts +10 -0
  148. package/dist/reference/text-measure.js +41 -0
  149. package/dist/reference/text-widths.d.ts +10 -0
  150. package/dist/reference/text-widths.js +1238 -0
  151. package/dist/reference/types.d.ts +64 -8
  152. package/dist/rules/catalog.js +223 -32
  153. package/dist/rules/in-effect.d.ts +1 -1
  154. package/dist/rules/rules.schema.json +100 -27
  155. package/dist/rules/settings.d.ts +1 -1
  156. package/dist/rules/settings.js +7 -0
  157. package/dist/session/PreferenceStore.d.ts +18 -1
  158. package/dist/session/PreferenceStore.js +29 -1
  159. package/docs/authoring/README.md +33 -21
  160. package/docs/authoring/agents.md +38 -7
  161. package/docs/authoring/components.md +115 -10
  162. package/docs/authoring/migrating.md +878 -0
  163. package/docs/authoring/notation.md +285 -30
  164. package/docs/authoring/retired.md +1 -0
  165. package/docs/authoring/structure-rules.md +84 -45
  166. package/docs/authoring/structure.md +67 -2
  167. package/docs/authoring/visualizations.md +167 -25
  168. package/package.json +12 -7
  169. package/src/compiler/astro.ts +72 -21
  170. package/src/compiler/cli.ts +143 -35
  171. package/src/compiler/collections.ts +158 -27
  172. package/src/compiler/content-schema.ts +108 -0
  173. package/src/compiler/content-schemas.ts +391 -0
  174. package/src/compiler/course-config.ts +86 -15
  175. package/src/compiler/exports/combined-html.ts +669 -0
  176. package/src/compiler/exports/download-index.ts +36 -0
  177. package/src/compiler/exports/export-chapters.ts +233 -0
  178. package/src/compiler/exports/export-pages.ts +44 -0
  179. package/src/compiler/exports/html-tree.ts +62 -0
  180. package/src/compiler/exports/latex/capture.ts +651 -0
  181. package/src/compiler/exports/latex/compile.ts +168 -0
  182. package/src/compiler/exports/latex/document.ts +479 -0
  183. package/src/compiler/exports/latex/explico.cls +415 -0
  184. package/src/compiler/exports/latex/numbering.ts +74 -0
  185. package/src/compiler/exports/latex/serialize.ts +787 -0
  186. package/src/compiler/exports/latex/texlive-packages.txt +76 -0
  187. package/src/compiler/exports/latex-export.ts +255 -0
  188. package/src/compiler/exports/markdown/serialize.ts +632 -0
  189. package/src/compiler/exports/markdown-export.ts +329 -0
  190. package/src/compiler/exports/standalone-html.ts +39 -70
  191. package/src/compiler/exports/static-tree.ts +944 -0
  192. package/src/compiler/exports/zip.ts +88 -0
  193. package/src/compiler/lesson-derivation.ts +13 -8
  194. package/src/compiler/manifest.ts +387 -40
  195. package/src/compiler/render-diagnostics.ts +111 -0
  196. package/src/components/CourseContents.astro +6 -0
  197. package/src/components/HoverPanel.astro +204 -6
  198. package/src/components/PartOverview.astro +18 -9
  199. package/src/components/abbreviation/AbbreviationGlossary.astro +6 -2
  200. package/src/components/abbreviation/AbbreviationLayer.astro +21 -12
  201. package/src/components/assessments/AssessmentRunner.tsx +7 -1
  202. package/src/components/assessments/AssessmentSet.astro +14 -1
  203. package/src/components/block-heading.ts +38 -0
  204. package/src/components/card-arrow.ts +18 -4
  205. package/src/components/card-carry.ts +406 -0
  206. package/src/components/card-drag.ts +1 -30
  207. package/src/components/card-fetch.ts +87 -0
  208. package/src/components/card-manager.ts +1222 -38
  209. package/src/components/card-memory.ts +5 -1
  210. package/src/components/card-placement.ts +211 -13
  211. package/src/components/card-side.ts +281 -31
  212. package/src/components/citation/CitationLayer.astro +45 -40
  213. package/src/components/diagram/Diagram.astro +123 -44
  214. package/src/components/disclosure/print-as-shown.ts +42 -6
  215. package/src/components/figure/Figure.astro +17 -1
  216. package/src/components/figure/FloatCaption.astro +4 -17
  217. package/src/components/figure/figure-state.ts +26 -17
  218. package/src/components/float-toolbar.ts +274 -0
  219. package/src/components/memo/Memo.astro +21 -10
  220. package/src/components/memo/ShortNote.astro +4 -0
  221. package/src/components/notation/NotationGlossary.astro +22 -17
  222. package/src/components/notation/NotationLayer.astro +300 -79
  223. package/src/components/notation/NotationMath.astro +8 -1
  224. package/src/components/notation/render-notation-math.ts +51 -5
  225. package/src/components/readable-text.ts +25 -0
  226. package/src/components/reference/FloatRefLayer.astro +162 -89
  227. package/src/components/starlight/HeadingAnchorEnhancer.astro +13 -8
  228. package/src/components/starlight/KeyedFloatEnhancer.astro +28 -9
  229. package/src/components/starlight/LayoutPageSidebar.astro +17 -3
  230. package/src/components/starlight/LayoutSocialIcons.astro +79 -29
  231. package/src/components/starlight/LessonExportActions.astro +384 -310
  232. package/src/components/starlight/LessonFooter.astro +66 -8
  233. package/src/components/starlight/PrintController.astro +28 -0
  234. package/src/components/starlight/TextSizeControl.astro +137 -0
  235. package/src/components/starlight/print-controller.ts +380 -0
  236. package/src/components/starlight/section-summary.ts +13 -5
  237. package/src/components/table/Table.astro +14 -22
  238. package/src/components/table/table-truncation.ts +15 -0
  239. package/src/content-config.ts +20 -312
  240. package/src/curriculum/validation.ts +72 -24
  241. package/src/reference/base-abbreviations.mjs +2 -2
  242. package/src/reference/base-operators.mjs +8 -10
  243. package/src/reference/base-roman-labels.mjs +4 -3
  244. package/src/reference/citation-format.mjs +74 -0
  245. package/src/reference/consistency.ts +47 -61
  246. package/src/reference/curriculum-alignment.ts +4 -41
  247. package/src/reference/derived-entries.ts +68 -0
  248. package/src/reference/derived-notation.mjs +121 -0
  249. package/src/reference/diagram-fit.ts +104 -0
  250. package/src/reference/diagram-layout.ts +182 -51
  251. package/src/reference/diagrams-validate.ts +2 -1
  252. package/src/reference/equations-validate.ts +41 -2
  253. package/src/reference/equations.mjs +120 -22
  254. package/src/reference/examples-validate.ts +1 -0
  255. package/src/reference/figure-fit.ts +139 -0
  256. package/src/reference/figure-pictures.ts +96 -0
  257. package/src/reference/figure-symbols.ts +117 -0
  258. package/src/reference/figures-validate.ts +2 -1
  259. package/src/reference/gate-abbreviations.ts +1 -1
  260. package/src/reference/gate-course-rules.ts +22 -7
  261. package/src/reference/gate-heading-length.ts +25 -7
  262. package/src/reference/gate-heading-structure.ts +40 -1
  263. package/src/reference/gate-indices.ts +470 -0
  264. package/src/reference/gate-math.ts +102 -13
  265. package/src/reference/gate-mdx-expression.ts +45 -0
  266. package/src/reference/gate-non-chapter-pages.ts +241 -0
  267. package/src/reference/gate-part-pages.ts +1 -1
  268. package/src/reference/gate-prop-text.ts +145 -0
  269. package/src/reference/gate-prose-notation.ts +19 -5
  270. package/src/reference/gate-retired-keys.ts +101 -0
  271. package/src/reference/gloss.ts +2 -0
  272. package/src/reference/katex-measure.ts +384 -0
  273. package/src/reference/keyed-float-validate.ts +22 -0
  274. package/src/reference/keyed-jsx-floats.mjs +11 -14
  275. package/src/reference/lab-katex-validate.ts +156 -0
  276. package/src/reference/lab-math-scope.ts +15 -1
  277. package/src/reference/lint-ignore.ts +4 -1
  278. package/src/reference/math-glyphs.mjs +851 -46
  279. package/src/reference/mixed-content.ts +11 -2
  280. package/src/reference/page-notation.ts +114 -14
  281. package/src/reference/raw-floats.mjs +37 -13
  282. package/src/reference/references.ts +14 -0
  283. package/src/reference/registry.ts +170 -9
  284. package/src/reference/rehype-fail-katex-errors.mjs +19 -0
  285. package/src/reference/rehype-heading-anchors.mjs +11 -1
  286. package/src/reference/remark-citation.mjs +0 -0
  287. package/src/reference/remark-notation.mjs +275 -23
  288. package/src/reference/render-lab-math.ts +40 -5
  289. package/src/reference/sections-validate.ts +67 -3
  290. package/src/reference/sections.mjs +92 -17
  291. package/src/reference/tables-validate.ts +40 -2
  292. package/src/reference/text-measure.ts +63 -0
  293. package/src/reference/text-widths.ts +133 -0
  294. package/src/reference/types.ts +75 -8
  295. package/src/rules/catalog.ts +241 -31
  296. package/src/rules/settings.ts +10 -0
  297. package/src/session/PreferenceStore.ts +53 -0
  298. package/src/styles/global.css +322 -83
@@ -149,14 +149,15 @@ body {
149
149
  color: var(--sl-color-text);
150
150
  }
151
151
 
152
- /* Clip only the inline axis: `overflow-x: auto` alone makes the block axis
153
- compute to `auto` too, which adds a spurious vertical scrollbar around
154
- display math. */
155
- .lab-math {
152
+ /* A lab's math (`renderLabMath`), in the element the lab puts it in. The
153
+ wrapper never scrolls: a display template's `.katex-display` scrolls
154
+ sideways when it is wider than its box, like a lesson equation (below),
155
+ so a lab equation is one scroll container at most, and one that fits
156
+ draws no scrollbar. An inline template (`{ display: false }` — a legend
157
+ entry, an axis title, a control label) has no `.katex-display`: it sits
158
+ in its line, with no margin and nothing to scroll. */
159
+ .lab-math:has(.katex-display) {
156
160
  margin-block: 1.25rem;
157
- padding-block: 0.15rem;
158
- overflow-x: auto;
159
- overflow-y: hidden;
160
161
  }
161
162
 
162
163
  .lab-math .katex-display {
@@ -171,12 +172,42 @@ body {
171
172
  edge is KaTeX's metric strut — but glyph ink overshoots its metrics (a
172
173
  denominator's `g` descender, a stretched `\right]`), so the bottom of the
173
174
  equation was cut off. The padding is the room that ink needs; em-based so
174
- it scales with the math. */
175
- .sl-markdown-content .katex-display {
175
+ it scales with the math.
176
+ The inline padding is KaTeX's own: every stacked script box
177
+ (`.vlist-t2`) has `margin-right: -2px` and holds a 2px spacer cell, which
178
+ lies outside the box's layout width but inside its scrollable overflow.
179
+ An equation within 2px of its column's width scrolled by those 2px for
180
+ nothing. Padding both sides reserves them and keeps a centred equation
181
+ centred. The same holds for a lab's display math (`.lab-math`, above). */
182
+ .sl-markdown-content .katex-display,
183
+ .lab-math .katex-display {
176
184
  max-width: 100%;
177
185
  overflow-x: auto;
178
186
  overflow-y: hidden;
179
187
  padding-block: 0.25em;
188
+ padding-inline: 2px;
189
+ }
190
+
191
+ /* An equation wider than the column scrolls inside it, with no scrollbar on
192
+ a phone: the edge it is cut at fades, so the reader sees the formula goes
193
+ on (`data-overflow`, set by `KeyedFloatEnhancer.astro`). The mask fades the
194
+ ink, whatever the background; focus and print show the equation whole. */
195
+ .sl-markdown-content .katex-display[data-overflow='end'] {
196
+ mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
197
+ }
198
+ .sl-markdown-content .katex-display[data-overflow='start'] {
199
+ mask-image: linear-gradient(to left, #000 calc(100% - 2.5rem), transparent);
200
+ }
201
+ .sl-markdown-content .katex-display[data-overflow='start end'] {
202
+ mask-image: linear-gradient(to right, transparent, #000 2.5rem, #000 calc(100% - 2.5rem), transparent);
203
+ }
204
+ .sl-markdown-content .katex-display[data-overflow]:focus-visible {
205
+ mask-image: none;
206
+ }
207
+ @media print {
208
+ .sl-markdown-content .katex-display[data-overflow] {
209
+ mask-image: none;
210
+ }
180
211
  }
181
212
 
182
213
  /* Focusable only while it overflows (`KeyedFloatEnhancer.astro`), so it can
@@ -255,6 +286,24 @@ body {
255
286
  margin-block-start: 0;
256
287
  }
257
288
 
289
+ /* The reader's text size on a touch screen (`TextSizeControl.astro`): the
290
+ root font size, which every `rem` follows, so the text, the math, the
291
+ spacing and the cards scale together. Screen only: print keeps its own
292
+ sizes, and a screen with hover has the browser's zoom instead. */
293
+ @media screen and (hover: none) {
294
+ :root[data-text-size='90'] {
295
+ font-size: 90%;
296
+ }
297
+
298
+ :root[data-text-size='80'] {
299
+ font-size: 80%;
300
+ }
301
+
302
+ :root[data-text-size='70'] {
303
+ font-size: 70%;
304
+ }
305
+ }
306
+
258
307
  .lab-chart {
259
308
  overflow-x: auto;
260
309
  min-height: 18rem;
@@ -267,6 +316,14 @@ body {
267
316
  background: var(--sl-color-bg);
268
317
  }
269
318
 
319
+ /* A lab shows the build's copy of its default plot (its children, in an
320
+ `<astro-slot>`) until it has drawn its own into `.lab-chart-live`. The
321
+ copy goes the moment the live plot exists, so the two never stack, not
322
+ even for the frame before the island drops it (rule V3). */
323
+ .lab-chart:has(> .lab-chart-live > svg) > astro-slot {
324
+ display: none;
325
+ }
326
+
270
327
  .cds-leg-chart:empty {
271
328
  display: none;
272
329
  }
@@ -1358,6 +1415,15 @@ details.layout-part-has-open > summary > .group-label {
1358
1415
  z-index: calc(var(--sl-z-index-navbar) + 1);
1359
1416
  }
1360
1417
 
1418
+ /* Below 50rem Starlight's menu button is not in the header: it is a fixed
1419
+ button in `nav.sidebar`, at the navbar's level, laid over the header's
1420
+ end. It rises above the raised header, or the header's box takes every
1421
+ tap and the navigation never opens on a phone. Starlight hides the
1422
+ button from 50rem up, so the rule needs no media query. */
1423
+ .sl-menu-button {
1424
+ z-index: calc(var(--sl-z-index-navbar) + 2);
1425
+ }
1426
+
1361
1427
  /* A slimmer site header: 3rem at every width, where Starlight's is 3.5rem
1362
1428
  and 4rem from 50rem up. The title, the logo (sized from the height and
1363
1429
  padding) and the search box scale with it, so more of the window goes to
@@ -1399,13 +1465,111 @@ details.layout-part-has-open > summary > .group-label {
1399
1465
  z-index: var(--sl-z-index-toc);
1400
1466
  }
1401
1467
 
1402
- /* S9: "Keep the article left", fixed at the top of the side column just
1403
- under the header, over the cards scrolling past it. `card-side.ts` sets
1404
- its `left`. */
1405
- .explico-keep-left {
1468
+ /* The row of card controls (`card-side.ts`, S9, S11, K1): at the top of the
1469
+ side column, "Article left", "Carry cards" and, once a card has been
1470
+ resized, the width menu; with no side column in use, "Carry cards" alone
1471
+ at the article's top right while a card is kept (D7). Fixed just under
1472
+ the site header, over the cards scrolling past; `card-side.ts` sets its
1473
+ `left` or `right`. It wraps rather than reaching over the rail. */
1474
+ .explico-side-controls {
1406
1475
  position: fixed;
1407
1476
  z-index: var(--sl-z-index-navbar);
1408
1477
  top: calc(var(--sl-nav-height) + 0.5rem);
1478
+ display: flex;
1479
+ flex-wrap: wrap;
1480
+ align-items: center;
1481
+ gap: 0.4rem;
1482
+ }
1483
+
1484
+ .explico-side-controls[data-at='article'] {
1485
+ justify-content: flex-end;
1486
+ }
1487
+
1488
+ .explico-side-controls[hidden] {
1489
+ display: none;
1490
+ }
1491
+
1492
+ /* S11: the width menu, a pill like the switch beside it. */
1493
+ .explico-side-width {
1494
+ display: inline-flex;
1495
+ align-items: center;
1496
+ gap: 0.35rem;
1497
+ padding: 0.2rem 0.35rem 0.2rem 0.65rem;
1498
+ border: var(--ex-edge-border);
1499
+ border-radius: 999px;
1500
+ font-size: var(--sl-text-xs);
1501
+ line-height: 1.4;
1502
+ color: var(--sl-color-gray-2);
1503
+ background: var(--sl-color-bg);
1504
+ box-shadow: var(--sl-shadow-sm);
1505
+ }
1506
+
1507
+ .explico-side-width[hidden] {
1508
+ display: none;
1509
+ }
1510
+
1511
+ .explico-side-width select {
1512
+ border: 0;
1513
+ padding: 0 0.2rem;
1514
+ font: inherit;
1515
+ color: var(--sl-color-text);
1516
+ background: transparent;
1517
+ cursor: pointer;
1518
+ }
1519
+
1520
+ .explico-side-width:focus-within {
1521
+ outline: 3px solid var(--sl-color-accent);
1522
+ outline-offset: 2px;
1523
+ }
1524
+
1525
+ /* K7: a card's "Opened at" link lands on the trigger the card was opened
1526
+ at, and lights it a moment (`card-manager.ts`, `is-card-origin`). */
1527
+ .is-card-origin {
1528
+ border-radius: 0.2em;
1529
+ animation: explico-card-origin 1.2s ease-out;
1530
+ }
1531
+
1532
+ @keyframes explico-card-origin {
1533
+ from {
1534
+ background-color: var(--sl-color-accent-low);
1535
+ box-shadow: 0 0 0 0.2em var(--sl-color-accent-low);
1536
+ }
1537
+ to {
1538
+ background-color: transparent;
1539
+ box-shadow: 0 0 0 0.2em transparent;
1540
+ }
1541
+ }
1542
+
1543
+ @media (prefers-reduced-motion: reduce) {
1544
+ .is-card-origin {
1545
+ animation: none;
1546
+ background-color: var(--sl-color-accent-low);
1547
+ }
1548
+ }
1549
+
1550
+ /* K5: cards carried in from another page and held at the window's bottom
1551
+ edge never leave it, so they would cover the page's last stretch — its
1552
+ Next and Previous links. The page grows by their stack, so its end
1553
+ scrolls clear of them (`card-manager.ts` sets the room). */
1554
+ @media screen {
1555
+ body {
1556
+ padding-block-end: var(--explico-carried-room, 0px);
1557
+ }
1558
+ }
1559
+
1560
+ /* S11: while a docked card reaches over the rail, the rail goes under the
1561
+ card layer, so the card and its edge stay whole; the header covers both. */
1562
+ :root[data-card-over-rail] .right-sidebar {
1563
+ z-index: calc(var(--sl-z-index-navbar) - 1);
1564
+ }
1565
+
1566
+ :root[data-card-resizing],
1567
+ :root[data-card-resizing] * {
1568
+ cursor: col-resize !important;
1569
+ user-select: none;
1570
+ }
1571
+
1572
+ .explico-side-switch {
1409
1573
  display: inline-flex;
1410
1574
  align-items: center;
1411
1575
  gap: 0.45rem;
@@ -1422,20 +1586,20 @@ details.layout-part-has-open > summary > .group-label {
1422
1586
  cursor: pointer;
1423
1587
  }
1424
1588
 
1425
- .explico-keep-left[hidden] {
1589
+ .explico-side-switch[hidden] {
1426
1590
  display: none;
1427
1591
  }
1428
1592
 
1429
- .explico-keep-left:hover {
1593
+ .explico-side-switch:hover {
1430
1594
  color: var(--sl-color-text);
1431
1595
  }
1432
1596
 
1433
- .explico-keep-left:focus-visible {
1597
+ .explico-side-switch:focus-visible {
1434
1598
  outline: 3px solid var(--sl-color-accent);
1435
1599
  outline-offset: 2px;
1436
1600
  }
1437
1601
 
1438
- .explico-keep-left-switch {
1602
+ .explico-side-switch-knob {
1439
1603
  position: relative;
1440
1604
  flex: none;
1441
1605
  width: 1.75em;
@@ -1445,7 +1609,7 @@ details.layout-part-has-open > summary > .group-label {
1445
1609
  transition: background-color 100ms ease;
1446
1610
  }
1447
1611
 
1448
- .explico-keep-left-switch::after {
1612
+ .explico-side-switch-knob::after {
1449
1613
  content: '';
1450
1614
  position: absolute;
1451
1615
  top: 0.125em;
@@ -1457,33 +1621,33 @@ details.layout-part-has-open > summary > .group-label {
1457
1621
  transition: transform 100ms ease;
1458
1622
  }
1459
1623
 
1460
- .explico-keep-left[aria-pressed='true'] .explico-keep-left-switch {
1624
+ .explico-side-switch[aria-pressed='true'] .explico-side-switch-knob {
1461
1625
  background: var(--sl-color-accent);
1462
1626
  }
1463
1627
 
1464
- .explico-keep-left[aria-pressed='true'] .explico-keep-left-switch::after {
1628
+ .explico-side-switch[aria-pressed='true'] .explico-side-switch-knob::after {
1465
1629
  transform: translateX(0.75em);
1466
1630
  }
1467
1631
 
1468
1632
  @media (prefers-reduced-motion: reduce) {
1469
- .explico-keep-left-switch,
1470
- .explico-keep-left-switch::after {
1633
+ .explico-side-switch-knob,
1634
+ .explico-side-switch-knob::after {
1471
1635
  transition: none;
1472
1636
  }
1473
1637
  }
1474
1638
 
1475
1639
  @media (forced-colors: active) {
1476
- .explico-keep-left-switch {
1640
+ .explico-side-switch-knob {
1477
1641
  border: 1px solid ButtonText;
1478
1642
  }
1479
1643
 
1480
- .explico-keep-left[aria-pressed='true'] .explico-keep-left-switch {
1644
+ .explico-side-switch[aria-pressed='true'] .explico-side-switch-knob {
1481
1645
  background: Highlight;
1482
1646
  }
1483
1647
  }
1484
1648
 
1485
1649
  @media print {
1486
- .explico-keep-left {
1650
+ .explico-side-controls {
1487
1651
  display: none !important;
1488
1652
  }
1489
1653
  }
@@ -1538,14 +1702,6 @@ details.layout-part-has-open > summary > .group-label {
1538
1702
  overflow-wrap: anywhere;
1539
1703
  }
1540
1704
 
1541
- .sl-markdown-content .citation-status {
1542
- padding-inline: 0.4em;
1543
- border: 1px solid var(--sl-color-gray-5);
1544
- border-radius: 999px;
1545
- font-size: var(--sl-text-xs);
1546
- text-transform: lowercase;
1547
- }
1548
-
1549
1705
  .sl-markdown-content .citation-backref {
1550
1706
  margin-inline-start: 0.15rem;
1551
1707
  }
@@ -1588,12 +1744,12 @@ details.layout-part-has-open > summary > .group-label {
1588
1744
  }
1589
1745
 
1590
1746
  /* Equation identity (D7): a keyed `$$…$$` is wrapped by the remark adapter in
1591
- `.keyed-equation` with an `id="eq-<key>"`, a `data-eq-number`, and a
1592
- `.keyed-equation__controls` pair — a focusable number link, and a "Copy
1593
- LaTeX" button. Prose refers to it with `[[eq:key]]` → `a.equation-ref`. The
1594
- deep-link highlight is a pure-CSS `:target` animation with a
1595
- reduced-motion fallback; `KeyedFloatEnhancer.astro` only re-triggers it on
1596
- a repeated activation and moves focus. */
1747
+ `.keyed-equation` with an `id="eq-<key>"`, a `data-eq-number`, its number
1748
+ (`.keyed-equation__controls`, a focusable number link) and a "Copy LaTeX"
1749
+ button on its bottom border. Prose refers to it with `[[eq:key]]` →
1750
+ `a.equation-ref`. The deep-link highlight is a pure-CSS `:target`
1751
+ animation with a reduced-motion fallback; `KeyedFloatEnhancer.astro` only
1752
+ re-triggers it on a repeated activation and moves focus. */
1597
1753
  .sl-markdown-content .keyed-equation {
1598
1754
  position: relative;
1599
1755
  scroll-margin-block-start: 5rem;
@@ -1628,23 +1784,32 @@ details.layout-part-has-open > summary > .group-label {
1628
1784
  transition: opacity 120ms ease-out;
1629
1785
  }
1630
1786
 
1787
+ /* Copy LaTeX, centred on the block's bottom border: the edge of its hover
1788
+ highlight. `float-toolbar.ts` measures each equation and sets
1789
+ `--eq-copy-inline-end`: under the number, or, in an equation too short
1790
+ for the number's copy-link button between the two, slid left along the
1791
+ border, clear of the number. It shows while the equation is hovered or
1792
+ focused, and always on a touch screen. */
1631
1793
  .sl-markdown-content .keyed-equation__copy {
1794
+ --eq-copy-size: 1.375rem;
1795
+ position: absolute;
1796
+ inset-block-end: calc(var(--eq-copy-size) / -2);
1797
+ inset-inline-end: var(--eq-copy-inline-end, 1rem);
1798
+ z-index: 2;
1632
1799
  display: inline-flex;
1633
1800
  align-items: center;
1634
1801
  justify-content: center;
1635
- width: 1.5rem;
1636
- height: 1.5rem;
1802
+ width: var(--eq-copy-size);
1803
+ height: var(--eq-copy-size);
1637
1804
  padding: 0;
1638
1805
  border: 1px solid var(--sl-color-gray-5);
1639
1806
  border-radius: 999px;
1640
- color: var(--sl-color-gray-1);
1641
- background: transparent;
1807
+ color: var(--sl-color-gray-2);
1808
+ background: var(--sl-color-bg);
1809
+ box-shadow: 0 1px 3px rgb(0 0 0 / 0.12);
1642
1810
  font: inherit;
1643
1811
  cursor: pointer;
1644
- /* Quiet by default for the same reason as the number above — it fades in
1645
- alongside it, not the equation, so print (which drops it entirely, see
1646
- below) never has to reconcile the two. */
1647
- opacity: 0.45;
1812
+ opacity: 0;
1648
1813
  transition:
1649
1814
  opacity 120ms ease-out,
1650
1815
  border-color 120ms ease-out,
@@ -1652,8 +1817,8 @@ details.layout-part-has-open > summary > .group-label {
1652
1817
  }
1653
1818
 
1654
1819
  .sl-markdown-content .keyed-equation__copy svg {
1655
- width: 0.85rem;
1656
- height: 0.85rem;
1820
+ width: 0.75rem;
1821
+ height: 0.75rem;
1657
1822
  fill: none;
1658
1823
  stroke: currentColor;
1659
1824
  stroke-width: 1.75;
@@ -1695,6 +1860,78 @@ details.layout-part-has-open > summary > .group-label {
1695
1860
  outline-offset: 2px;
1696
1861
  }
1697
1862
 
1863
+ /* The toolbar on a float's number (`float-toolbar.ts`): Pin and Dock just
1864
+ above the number, Copy link just below it, none of them over it. The
1865
+ script places each part against the number, inside the number's own
1866
+ box; it shows only with a mouse, and never prints. */
1867
+ .float-toolbar {
1868
+ display: contents;
1869
+ }
1870
+
1871
+ .float-toolbar[hidden] {
1872
+ display: none;
1873
+ }
1874
+
1875
+ .float-toolbar__above,
1876
+ .float-toolbar__link {
1877
+ position: absolute;
1878
+ z-index: 4;
1879
+ display: inline-flex;
1880
+ gap: 0.25rem;
1881
+ }
1882
+
1883
+ .float-toolbar__button,
1884
+ .float-toolbar__link {
1885
+ align-items: center;
1886
+ justify-content: center;
1887
+ width: 1.375rem;
1888
+ height: 1.375rem;
1889
+ padding: 0;
1890
+ border: 1px solid var(--sl-color-gray-5);
1891
+ border-radius: 999px;
1892
+ color: var(--sl-color-gray-2);
1893
+ background: var(--sl-color-bg);
1894
+ box-shadow: 0 1px 3px rgb(0 0 0 / 0.12);
1895
+ font: inherit;
1896
+ text-decoration: none;
1897
+ cursor: pointer;
1898
+ }
1899
+
1900
+ .float-toolbar__button {
1901
+ display: inline-flex;
1902
+ }
1903
+
1904
+ .float-toolbar__button:hover,
1905
+ .float-toolbar__button:focus-visible,
1906
+ .float-toolbar__link:hover,
1907
+ .float-toolbar__link:focus-visible {
1908
+ border-color: var(--sl-color-accent);
1909
+ color: var(--sl-color-text-accent);
1910
+ }
1911
+
1912
+ .float-toolbar svg {
1913
+ width: 0.75rem;
1914
+ height: 0.75rem;
1915
+ }
1916
+
1917
+ .float-toolbar__button svg {
1918
+ fill: none;
1919
+ stroke: currentColor;
1920
+ stroke-width: 1.5;
1921
+ stroke-linecap: round;
1922
+ stroke-linejoin: round;
1923
+ }
1924
+
1925
+ .float-toolbar__link svg {
1926
+ fill: currentColor;
1927
+ }
1928
+
1929
+ @media print {
1930
+ .float-toolbar {
1931
+ display: none !important;
1932
+ }
1933
+ }
1934
+
1698
1935
  /* The click-to-copy gesture (`KeyedFloatEnhancer.astro`) works on any part of
1699
1936
  the box — including the reserved gutter between the equation and its
1700
1937
  number/button, not just the rendered `.katex-html` — that isn't a symbol
@@ -2020,7 +2257,11 @@ details.layout-part-has-open > summary > .group-label {
2020
2257
  gap: 0.25rem;
2021
2258
  }
2022
2259
 
2260
+ /* The line is at least as tall as its buttons (`.float-tool`'s height), so
2261
+ the row never hangs below it — past a figure's body, or into a table's
2262
+ header — even when a course hides the caption's text. */
2023
2263
  .sl-markdown-content .float-caption:has(> .float-tools > .float-tool:not([hidden])) {
2264
+ min-block-size: 1.5rem;
2024
2265
  padding-inline-end: 2.25rem;
2025
2266
  }
2026
2267
 
@@ -2092,6 +2333,7 @@ details.layout-part-has-open > summary > .group-label {
2092
2333
  }
2093
2334
 
2094
2335
  .sl-markdown-content .float-caption:has(> .float-tools) {
2336
+ min-block-size: auto;
2095
2337
  padding-inline-end: 0;
2096
2338
  }
2097
2339
  }
@@ -2105,6 +2347,8 @@ details.layout-part-has-open > summary > .group-label {
2105
2347
  }
2106
2348
 
2107
2349
  .sl-markdown-content .float-caption__number {
2350
+ /* The room the chain icon used to keep before the short caption. */
2351
+ margin-inline-end: 0.45em;
2108
2352
  font-weight: 600;
2109
2353
  font-variant-numeric: tabular-nums;
2110
2354
  color: var(--sl-color-text-accent);
@@ -2116,31 +2360,7 @@ details.layout-part-has-open > summary > .group-label {
2116
2360
  text-decoration: underline;
2117
2361
  }
2118
2362
 
2119
- .sl-markdown-content .float-caption__anchor {
2120
- display: inline-flex;
2121
- width: 1.15em;
2122
- height: 1.15em;
2123
- margin-inline: 0.15em 0.3em;
2124
- vertical-align: -0.2em;
2125
- align-items: center;
2126
- justify-content: center;
2127
- color: var(--sl-color-gray-4);
2128
- text-decoration: none;
2129
- -webkit-user-select: none;
2130
- user-select: none;
2131
- }
2132
-
2133
- .sl-markdown-content .float-caption__anchor svg {
2134
- width: 0.85em;
2135
- height: 0.85em;
2136
- }
2137
-
2138
- .sl-markdown-content .float-caption__anchor:hover,
2139
- .sl-markdown-content .float-caption__anchor:focus-visible {
2140
- color: var(--sl-color-text-accent);
2141
- }
2142
-
2143
- .sl-markdown-content :is(.float-caption__number, .float-caption__anchor):focus-visible {
2363
+ .sl-markdown-content .float-caption__number:focus-visible {
2144
2364
  outline: 2px solid var(--sl-color-accent);
2145
2365
  outline-offset: 0.15rem;
2146
2366
  }
@@ -2628,12 +2848,14 @@ details.layout-part-has-open > summary > .group-label {
2628
2848
  opacity: 1;
2629
2849
  }
2630
2850
 
2631
- /* A narrow screen shows no section number and, often, has no hover to reveal
2632
- the chain icon — yet the icon is the way to the section's link and, in its
2633
- "Link copied · § 1.1.2", its number (docs/plans/titles-and-containers.md
2634
- Q3). So it is always shown there, smaller and in the muted text colour.
2635
- The size variable also sets the room `anchor-links.css` reserves for it at
2636
- the end of the title. After the hover rules above, which it overrides. */
2851
+ /* A narrow screen shows no section number, so the chain icon is the way to
2852
+ the section's link and, in its "Link copied · § 1.1.2", its number
2853
+ (docs/plans/titles-and-containers.md Q3). There it is smaller and in the
2854
+ muted text colour. The size variable also sets the room `anchor-links.css`
2855
+ reserves for it at the end of the title. A touch screen has no hover to
2856
+ reveal it, so there it is always shown; a narrow window with a mouse
2857
+ reveals it on hover, as a wide one does, instead of showing it on every
2858
+ heading. After the hover rules above, which it overrides. */
2637
2859
  @media screen and (width < 50em) {
2638
2860
  .sl-markdown-content .sl-heading-wrapper {
2639
2861
  --sl-anchor-icon-size: 0.7em;
@@ -2641,11 +2863,17 @@ details.layout-part-has-open > summary > .group-label {
2641
2863
 
2642
2864
  .sl-markdown-content .sl-heading-wrapper > .sl-anchor-link,
2643
2865
  .sl-markdown-content .sl-heading-wrapper.sl-section-titlebar > .sl-anchor-link {
2644
- opacity: 1;
2645
2866
  color: var(--sl-color-gray-3);
2646
2867
  }
2647
2868
  }
2648
2869
 
2870
+ @media screen and (width < 50em) and (hover: none) {
2871
+ .sl-markdown-content .sl-heading-wrapper > .sl-anchor-link,
2872
+ .sl-markdown-content .sl-heading-wrapper.sl-section-titlebar > .sl-anchor-link {
2873
+ opacity: 1;
2874
+ }
2875
+ }
2876
+
2649
2877
  /* The chevron sits to the *left* of the title without shifting the title's
2650
2878
  own position: `markdown.css`'s default marker is inline, right before the
2651
2879
  text, pushed left slightly by a negative margin — meaning the title
@@ -3102,7 +3330,7 @@ details.layout-part-has-open > summary > .group-label {
3102
3330
  }
3103
3331
  }
3104
3332
 
3105
- /* Browser-native lesson printing. The print controller opens real native
3333
+ /* Browser-native page printing. The print controller opens real native
3106
3334
  disclosures before the browser lays out pages; these rules own only the
3107
3335
  paper presentation and therefore also remain a useful no-JS fallback. */
3108
3336
  @media print {
@@ -3152,7 +3380,6 @@ details.layout-part-has-open > summary > .group-label {
3152
3380
  .keyed-table__pagination-links,
3153
3381
  .keyed-table__more-row,
3154
3382
  .sl-anchor-link,
3155
- .float-caption__anchor,
3156
3383
  .short-note__collapse,
3157
3384
  .assessment-actions,
3158
3385
  .assessment-chevron,
@@ -3163,6 +3390,18 @@ details.layout-part-has-open > summary > .group-label {
3163
3390
  display: none !important;
3164
3391
  }
3165
3392
 
3393
+ /* Print is always light (above), so the splash hero prints its light image
3394
+ whatever theme the reader is in; the dark one's pale strokes would vanish
3395
+ on white paper. Starlight's own site logo does this with `print:hidden`
3396
+ and `print:block`, but its hero does not. */
3397
+ .hero > img.light\:sl-hidden {
3398
+ display: none;
3399
+ }
3400
+
3401
+ .hero > img.dark\:sl-hidden {
3402
+ display: block;
3403
+ }
3404
+
3166
3405
  .main-frame,
3167
3406
  .main-pane,
3168
3407
  .content-panel,