explico 0.13.1 → 0.15.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 (256) hide show
  1. package/CHANGELOG.md +990 -1
  2. package/README.md +200 -30
  3. package/dist/compiler/astro.d.ts +15 -2
  4. package/dist/compiler/astro.js +40 -11
  5. package/dist/compiler/cli-main.d.ts +100 -0
  6. package/dist/compiler/cli-main.js +433 -0
  7. package/dist/compiler/cli-reference.d.ts +50 -0
  8. package/dist/compiler/cli-reference.js +213 -0
  9. package/dist/compiler/cli-verbs.d.ts +45 -0
  10. package/dist/compiler/cli-verbs.js +224 -0
  11. package/dist/compiler/cli-verify.d.ts +51 -0
  12. package/dist/compiler/cli-verify.js +151 -0
  13. package/dist/compiler/cli.d.ts +408 -24
  14. package/dist/compiler/cli.js +1328 -8
  15. package/dist/compiler/collections.d.ts +88 -1
  16. package/dist/compiler/collections.js +170 -25
  17. package/dist/compiler/content-release.d.ts +49 -0
  18. package/dist/compiler/content-release.js +63 -0
  19. package/dist/compiler/content-schemas.d.ts +2 -2
  20. package/dist/compiler/course-config.d.ts +1 -1
  21. package/dist/compiler/course-config.js +105 -1
  22. package/dist/compiler/decks.d.ts +161 -0
  23. package/dist/compiler/decks.js +235 -0
  24. package/dist/compiler/exports/combined-html.d.ts +5 -1
  25. package/dist/compiler/exports/deck-fit.d.ts +55 -0
  26. package/dist/compiler/exports/deck-fit.js +90 -0
  27. package/dist/compiler/exports/export-chapters.d.ts +37 -6
  28. package/dist/compiler/exports/export-chapters.js +64 -5
  29. package/dist/compiler/exports/export-pages.d.ts +30 -3
  30. package/dist/compiler/exports/export-pages.js +24 -1
  31. package/dist/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  32. package/dist/compiler/exports/latex/cache.d.ts +78 -0
  33. package/dist/compiler/exports/latex/cache.js +129 -0
  34. package/dist/compiler/exports/latex/capture.d.ts +10 -1
  35. package/dist/compiler/exports/latex/capture.js +3 -1
  36. package/dist/compiler/exports/latex/compile.d.ts +13 -2
  37. package/dist/compiler/exports/latex/compile.js +12 -1
  38. package/dist/compiler/exports/latex/deck.d.ts +92 -0
  39. package/dist/compiler/exports/latex/deck.js +349 -0
  40. package/dist/compiler/exports/latex/document.d.ts +51 -6
  41. package/dist/compiler/exports/latex/document.js +71 -12
  42. package/dist/compiler/exports/latex/explico-common.sty +198 -0
  43. package/dist/compiler/exports/latex/explico.cls +13 -183
  44. package/dist/compiler/exports/latex/serialize.d.ts +60 -4
  45. package/dist/compiler/exports/latex/serialize.js +4 -2
  46. package/dist/compiler/exports/latex-export.d.ts +24 -2
  47. package/dist/compiler/exports/latex-export.js +107 -26
  48. package/dist/compiler/exports/markdown/serialize.d.ts +24 -4
  49. package/dist/compiler/exports/markdown/serialize.js +85 -8
  50. package/dist/compiler/exports/markdown-export.d.ts +6 -1
  51. package/dist/compiler/exports/markdown-export.js +50 -11
  52. package/dist/compiler/exports/standalone-html.d.ts +10 -5
  53. package/dist/compiler/exports/standalone-html.js +3 -3
  54. package/dist/compiler/exports/static-tree.d.ts +38 -1
  55. package/dist/compiler/exports/static-tree.js +120 -4
  56. package/dist/compiler/knowledge-version.d.ts +26 -2
  57. package/dist/compiler/knowledge-version.js +27 -17
  58. package/dist/compiler/lesson-derivation.d.ts +143 -6
  59. package/dist/compiler/lesson-derivation.js +145 -13
  60. package/dist/compiler/manifest.d.ts +133 -4
  61. package/dist/compiler/manifest.js +364 -58
  62. package/dist/compiler/page-versions.d.ts +184 -0
  63. package/dist/compiler/page-versions.js +217 -0
  64. package/dist/compiler/render-diagnostics.d.ts +5 -1
  65. package/dist/compiler/resolution-report.d.ts +24 -6
  66. package/dist/compiler/resolution-report.js +22 -4
  67. package/dist/compiler/validate-content.d.ts +7 -0
  68. package/dist/compiler/validate-content.js +160 -0
  69. package/dist/{course-config-rRtME5UV.d.ts → course-config-BLtIdkei.d.ts} +164 -1
  70. package/dist/curriculum/competency-graph.d.ts +54 -0
  71. package/dist/curriculum/competency-graph.js +41 -0
  72. package/dist/reference/base-library.d.mts +8 -0
  73. package/dist/reference/base-library.mjs +19 -0
  74. package/dist/reference/consistency.d.ts +8 -4
  75. package/dist/reference/consistency.js +82 -26
  76. package/dist/reference/deck-remark.d.mts +123 -0
  77. package/dist/reference/deck-remark.mjs +600 -0
  78. package/dist/reference/deck-source.d.ts +107 -0
  79. package/dist/reference/deck-source.js +140 -0
  80. package/dist/reference/decks-validate.d.ts +133 -0
  81. package/dist/reference/decks-validate.js +359 -0
  82. package/dist/reference/derived-entries.d.ts +5 -1
  83. package/dist/reference/diagram-fit.d.ts +6 -2
  84. package/dist/reference/diagram-fit.js +5 -1
  85. package/dist/reference/figure-symbols.d.ts +23 -6
  86. package/dist/reference/figure-symbols.js +51 -15
  87. package/dist/reference/float-name.d.ts +43 -0
  88. package/dist/reference/float-name.js +58 -0
  89. package/dist/reference/folds-validate.d.ts +1 -1
  90. package/dist/reference/gate-abbreviations.d.ts +1 -1
  91. package/dist/reference/gate-indices.js +5 -4
  92. package/dist/reference/gate-math.d.ts +29 -2
  93. package/dist/reference/gate-math.js +99 -18
  94. package/dist/reference/gate-part-pages.d.ts +1 -1
  95. package/dist/reference/lab-katex-validate.d.ts +10 -6
  96. package/dist/reference/lab-katex-validate.js +17 -9
  97. package/dist/reference/lab-math-scope.js +29 -4
  98. package/dist/reference/math-glyphs.d.mts +25 -0
  99. package/dist/reference/math-glyphs.mjs +189 -25
  100. package/dist/reference/page-notation.d.ts +22 -3
  101. package/dist/reference/page-notation.js +32 -10
  102. package/dist/reference/references.d.ts +14 -1
  103. package/dist/reference/references.js +13 -0
  104. package/dist/reference/registry-scope.d.mts +33 -0
  105. package/dist/reference/registry-scope.mjs +34 -0
  106. package/dist/reference/registry.js +29 -10
  107. package/dist/reference/rehype-deck.d.mts +6 -0
  108. package/dist/reference/rehype-deck.mjs +286 -0
  109. package/dist/reference/rehype-heading-anchors.d.mts +20 -0
  110. package/dist/reference/rehype-heading-anchors.mjs +48 -6
  111. package/dist/reference/remark-notation.mjs +249 -40
  112. package/dist/reference/source-lines.d.mts +12 -0
  113. package/dist/reference/source-lines.mjs +65 -0
  114. package/dist/reference/types.d.ts +28 -0
  115. package/dist/rules/catalog.d.ts +21 -4
  116. package/dist/rules/catalog.js +237 -2
  117. package/dist/rules/course-rules.d.ts +5 -1
  118. package/dist/rules/course-rules.js +17 -3
  119. package/dist/rules/course-rules.schema.json +12 -0
  120. package/dist/rules/in-effect.d.ts +13 -3
  121. package/dist/rules/in-effect.js +34 -10
  122. package/dist/rules/rules.schema.json +280 -1
  123. package/dist/rules/settings.d.ts +1 -1
  124. package/dist/session/PreferenceStore.d.ts +6 -0
  125. package/dist/session/PreferenceStore.js +18 -1
  126. package/dist/session/deck-memory.d.ts +116 -0
  127. package/dist/session/deck-memory.js +143 -0
  128. package/dist/session/reading-memory.d.ts +96 -0
  129. package/dist/session/reading-memory.js +133 -0
  130. package/docs/VERSIONING.md +21 -18
  131. package/docs/authoring/README.md +31 -9
  132. package/docs/authoring/agents.md +72 -18
  133. package/docs/authoring/cli-reference.json +2172 -0
  134. package/docs/authoring/cli.md +887 -0
  135. package/docs/authoring/components.md +15 -0
  136. package/docs/authoring/deck.md +612 -0
  137. package/docs/authoring/migrating.md +1195 -16
  138. package/docs/authoring/notation.md +49 -6
  139. package/docs/authoring/structure.md +80 -1
  140. package/docs/authoring/versioning.md +56 -3
  141. package/docs/authoring/visualizations.md +7 -2
  142. package/docs/course-agent/AGENTS.md +40 -0
  143. package/docs/course-agent/agents/rules-reviewer.md +33 -0
  144. package/docs/course-agent/skills/draft-lesson/SKILL.md +38 -0
  145. package/docs/course-agent/skills/migrate-course/SKILL.md +32 -0
  146. package/docs/course-agent/skills/release-content/SKILL.md +26 -0
  147. package/docs/diagram-scaling.md +148 -0
  148. package/docs/privacy.md +59 -0
  149. package/package.json +8 -2
  150. package/src/compiler/astro.ts +60 -21
  151. package/src/compiler/cli-main.ts +557 -0
  152. package/src/compiler/cli-reference.ts +285 -0
  153. package/src/compiler/cli-verbs.ts +270 -0
  154. package/src/compiler/cli-verify.ts +241 -0
  155. package/src/compiler/cli.ts +2119 -170
  156. package/src/compiler/collections.ts +284 -25
  157. package/src/compiler/content-release.ts +91 -0
  158. package/src/compiler/course-config.ts +267 -0
  159. package/src/compiler/decks.ts +455 -0
  160. package/src/compiler/exports/deck-fit.ts +127 -0
  161. package/src/compiler/exports/export-chapters.ts +105 -6
  162. package/src/compiler/exports/export-pages.ts +46 -2
  163. package/src/compiler/exports/latex/beamerthemeexplico.sty +124 -0
  164. package/src/compiler/exports/latex/cache.ts +183 -0
  165. package/src/compiler/exports/latex/capture.ts +5 -2
  166. package/src/compiler/exports/latex/compile.ts +18 -0
  167. package/src/compiler/exports/latex/deck.ts +419 -0
  168. package/src/compiler/exports/latex/document.ts +117 -21
  169. package/src/compiler/exports/latex/explico-common.sty +198 -0
  170. package/src/compiler/exports/latex/explico.cls +13 -183
  171. package/src/compiler/exports/latex/serialize.ts +25 -21
  172. package/src/compiler/exports/latex/texlive-packages.txt +6 -2
  173. package/src/compiler/exports/latex-export.ts +187 -45
  174. package/src/compiler/exports/markdown/serialize.ts +118 -9
  175. package/src/compiler/exports/markdown-export.ts +56 -11
  176. package/src/compiler/exports/standalone-html.ts +8 -7
  177. package/src/compiler/exports/static-tree.ts +175 -3
  178. package/src/compiler/knowledge-version.ts +56 -21
  179. package/src/compiler/lesson-derivation.ts +282 -16
  180. package/src/compiler/manifest.ts +568 -63
  181. package/src/compiler/page-versions.ts +401 -0
  182. package/src/compiler/resolution-report.ts +38 -7
  183. package/src/compiler/validate-content.ts +215 -0
  184. package/src/components/CourseContents.astro +124 -6
  185. package/src/components/CurriculumMap.astro +49 -1
  186. package/src/components/FloatIndex.astro +1 -1
  187. package/src/components/PartOverview.astro +51 -5
  188. package/src/components/assessments/AssessmentSet.astro +41 -15
  189. package/src/components/block-heading.ts +15 -6
  190. package/src/components/card-carry.ts +7 -4
  191. package/src/components/card-manager.ts +14 -2
  192. package/src/components/card-memory.ts +13 -9
  193. package/src/components/deck/DeckEnhancer.astro +13 -0
  194. package/src/components/deck/deck-context.ts +104 -0
  195. package/src/components/deck/deck-controller.ts +1287 -0
  196. package/src/components/deck/deck-map.ts +91 -0
  197. package/src/components/deck-lane.ts +163 -0
  198. package/src/components/diagram/Diagram.astro +131 -86
  199. package/src/components/disclosure/persisted-disclosure.ts +9 -1
  200. package/src/components/figure/Figure.astro +7 -2
  201. package/src/components/figure/FloatCaption.astro +5 -3
  202. package/src/components/fold/Fold.astro +2 -2
  203. package/src/components/notation/NotationFormula.astro +81 -11
  204. package/src/components/notation/NotationGlossary.astro +132 -1
  205. package/src/components/notation/NotationLayer.astro +15 -7
  206. package/src/components/starlight/AboutPanel.astro +186 -3
  207. package/src/components/starlight/CollapsibleSections.astro +24 -3
  208. package/src/components/starlight/DiagramEnhancer.astro +5 -1
  209. package/src/components/starlight/HeadingAnchorEnhancer.astro +1 -1
  210. package/src/components/starlight/LayoutHeader.astro +17 -0
  211. package/src/components/starlight/LayoutPageTitle.astro +110 -0
  212. package/src/components/starlight/LayoutSidebar.astro +807 -21
  213. package/src/components/starlight/LayoutSocialIcons.astro +62 -6
  214. package/src/components/starlight/LessonExportActions.astro +27 -9
  215. package/src/components/starlight/LessonFooter.astro +59 -0
  216. package/src/components/starlight/ReadingMemoryNotice.astro +114 -0
  217. package/src/components/starlight/TextSizeControl.astro +24 -13
  218. package/src/components/starlight/page-marks.ts +139 -0
  219. package/src/components/starlight/section-summary.ts +1 -1
  220. package/src/components/starlight/sidebar-editions.ts +245 -0
  221. package/src/components/table/Table.astro +15 -7
  222. package/src/content-config.ts +87 -1
  223. package/src/curriculum/competency-graph.ts +95 -0
  224. package/src/reference/base-library.mjs +19 -0
  225. package/src/reference/consistency.ts +137 -39
  226. package/src/reference/deck-remark.mjs +600 -0
  227. package/src/reference/deck-source.ts +273 -0
  228. package/src/reference/decks-validate.ts +604 -0
  229. package/src/reference/diagram-fit.ts +18 -4
  230. package/src/reference/diagram-layout.ts +3 -1
  231. package/src/reference/figure-symbols.ts +108 -31
  232. package/src/reference/folds-validate.ts +1 -1
  233. package/src/reference/gate-indices.ts +5 -3
  234. package/src/reference/gate-math.ts +169 -25
  235. package/src/reference/lab-katex-validate.ts +30 -15
  236. package/src/reference/lab-math-scope.ts +34 -3
  237. package/src/reference/math-glyphs.mjs +189 -25
  238. package/src/reference/page-notation.ts +69 -15
  239. package/src/reference/references.ts +28 -0
  240. package/src/reference/registry-scope.mjs +34 -0
  241. package/src/reference/registry.ts +41 -11
  242. package/src/reference/rehype-deck.mjs +286 -0
  243. package/src/reference/rehype-heading-anchors.mjs +48 -6
  244. package/src/reference/remark-notation.mjs +249 -40
  245. package/src/reference/source-lines.mjs +65 -0
  246. package/src/reference/types.ts +28 -0
  247. package/src/rules/catalog.ts +274 -4
  248. package/src/rules/course-rules.ts +21 -1
  249. package/src/rules/in-effect.ts +58 -11
  250. package/src/session/PreferenceStore.ts +23 -0
  251. package/src/session/deck-memory.ts +263 -0
  252. package/src/session/reading-memory.ts +243 -0
  253. package/src/styles/deck.css +1150 -0
  254. package/src/styles/global.css +958 -27
  255. package/src/styles/zones.css +277 -0
  256. /package/src/{components → reference}/float-name.ts +0 -0
@@ -376,6 +376,21 @@ sits outside the text flow, so the title keeps the whole line.
376
376
  default) or `icon`, which leaves the corner label to the number and keeps
377
377
  the word for a screen reader.
378
378
 
379
+ ## In a deck
380
+
381
+ A lesson's deck, `<lesson>.deck.mdx` ([deck.md](deck.md)), has a few elements
382
+ of its own. None needs an import, and none works in a chapter's `.mdx`.
383
+
384
+ | Element | Notes | Doc |
385
+ | --- | --- | --- |
386
+ | `<Pause />` | A step: what follows appears on the next key press. `control`, `from`, `to` and `seconds` make the step move a lab's control instead; `highlight="key"` lights up a symbol | [deck.md § D6](deck.md#d6) |
387
+ | `<Notes>…</Notes>` | The frame's speaker notes: Markdown with math, terms and citations | [deck.md § D4](deck.md#d4) |
388
+ | `<Recall ref="eq-…" />` | The chapter's equation, figure (`fig-`), table (`tab-`) or diagram (`dia-`), with the chapter's number | [deck.md § D8](deck.md#d8) |
389
+ | `### Title [morph]` | The frame morphs from the one before, its shared symbols moving into place | [deck.md](deck.md) |
390
+
391
+ A question frame is a `###` frame holding `<AssessmentSet id="…" />` (imported
392
+ as in a chapter): the `id` picks one of the lesson's checks.
393
+
379
394
  ## Choosing between the "explain a term" options
380
395
 
381
396
  From lightest to heaviest — pick the first one that's actually enough:
@@ -0,0 +1,612 @@
1
+ # Decks
2
+
3
+ A **deck** is the slide edition of a lesson: `content/docs/<part>/<lesson>.deck.mdx`.
4
+ It is written in frames, shown one at a time in the browser's Present mode, read
5
+ as a study page with its notes at home, and downloaded as a beamer PDF. Its
6
+ symbols, sources, abbreviations, checks and competencies are the lesson's, so it
7
+ is validated like a chapter and every symbol on it explains itself. A part can
8
+ have a deck too, the lecture of the whole part
9
+ ([A deck for a part](#a-deck-for-a-part)).
10
+
11
+ `D` rules govern what only a frame has: its grammar, its fit, its steps and its
12
+ notes. The rest of the rule set holds for a deck as for a chapter, except the
13
+ few rules about a chapter's page (V7, V8, S1, S6, S7), which a `D` rule says of
14
+ a frame instead. `pnpm content rules --edition deck` (or
15
+ `build/rules-in-effect-deck.md`) lists exactly the rules in force for a deck.
16
+
17
+ See [README.md](README.md) for what "strong recommendation", "Build-enforced",
18
+ and "Review-only" mean here, and how to amend a rule.
19
+
20
+ ## The deck file
21
+
22
+ A lesson has up to two **editions**: its chapter (`<lesson>.mdx`) and its deck
23
+ (`<lesson>.deck.mdx`), side by side in the part's directory.
24
+
25
+ | The course has | The deck is served at | Its frontmatter |
26
+ | --- | --- | --- |
27
+ | a chapter and a deck | `/<part>/<lesson>/slides/`; the lesson's sidebar row opens one edition and carries the other as an icon, and `sidebarEditions` and the reader's switch say which | `title` and `description` only: the chapter holds the lesson's `teaches`, `assumptions`, `studyMinutes` and `notation` |
28
+ | a deck only (slides first) | `/<part>/<lesson>/`, the lesson's own URL | the lesson's: `title`, `description`, `teaches`, `assumptions`, `studyMinutes`, `notation` |
29
+ | a part's deck, `<part>/index.deck.mdx` | `/<part>/slides/`, sharing the part's row with its Overview | `title` and `description` only: its chapters hold the rest |
30
+
31
+ What the two editions share is the lesson's: its notation (a deck binds its
32
+ symbols in the lesson's scope, the chapter's `notation.local` included), its
33
+ checks (`<lesson>.checks.yml`), its sources, its competencies, and its part and
34
+ chapter numbers. A slides-first lesson is a lesson like any other: it has a
35
+ chapter number, a place in the sidebar and the curriculum map, and checks.
36
+
37
+ `pnpm content new deck <part>/<lesson>` drafts a deck: from the chapter when
38
+ there is one — a section per `##`, a frame per `###`, a `<Recall>` for every
39
+ numbered equation, figure, table and diagram, and the section's first sentence in
40
+ the notes — or as a slides-first lesson when there is none. `pnpm content new
41
+ deck <part>`, for a part's directory with its page, drafts the part's deck: a
42
+ section per chapter, in the order the tracks read them, and a frame per section
43
+ of the chapter. Rewrite the draft: the frame titles become claims (D3), the
44
+ frames keep to what fits (D2), and the argument goes into the notes (D4).
45
+
46
+ The `decks` invariant holds always: a deck sits in a part's directory, sets only
47
+ `title` and `description` beside a chapter and as a part's, recalls only what its
48
+ chapter numbers (a part's: what the chapters it covers number), and gives
49
+ `<Pause>` only the props below. Its codes: `deck-frontmatter`,
50
+ `deck-outside-part`, `recall-unresolved` and `pause-invalid`; on a part's deck,
51
+ `deck-chapter-unknown` (a section names no chapter of the part),
52
+ `deck-chapter-repeated` (a chapter named again after another section), `deck-check-no-chapter`
53
+ (an `<AssessmentSet>` in a section that names no chapter) and
54
+ `deck-route-taken` (a lesson of the part is named `slides`, the deck's address);
55
+ `deck-chapter-outside-part` for a `[chapter: …]` on a lesson's deck, and
56
+ `deck-marker-misplaced` for a `[chapter: …]` on a `###` or a `[morph]` on a
57
+ `##`. It cannot be waived.
58
+
59
+ ## Writing a deck
60
+
61
+ ```mdx
62
+ ---
63
+ title: Projectile Range
64
+ description: Derive and compute the range of a projectile over level ground.
65
+ ---
66
+
67
+ ## The range
68
+
69
+ ### Range is largest at a 45° launch angle
70
+
71
+ <Recall ref="eq-range" />
72
+
73
+ - $\sin(2\theta)$ peaks at $\theta = 45^\circ$
74
+
75
+ <Pause />
76
+
77
+ - the same range at $\theta$ and at $90^\circ - \theta$
78
+
79
+ <Notes>
80
+ The range formula is [[eq-range]] from the notes. Ask where $\sin(2\theta)$ is
81
+ largest before showing the second bullet; most of the class says 90°.
82
+ </Notes>
83
+ ```
84
+
85
+ | You write | It means |
86
+ | --- | --- |
87
+ | `## Title` | a section: a divider frame that shows its title, and an entry in the deck's outline |
88
+ | `### Title` | a frame; the heading is its title (D3) |
89
+ | `<Pause />` | a step: what follows appears on the next key press (D6) |
90
+ | `<Notes>…</Notes>` | the frame's speaker notes: Markdown, math and cards (D4) |
91
+ | `<Recall ref="eq-range" />` | the chapter's equation (`fig-`, `tab-`, `dia-`: figure, table, diagram), as it is in the chapter, with its number, linked to it (D8) |
92
+ | `### Title [morph]` | the frame morphs from the one before it |
93
+ | `## Title [chapter: <name>]` | on a part's deck, the section covers that chapter of the part ([A deck for a part](#a-deck-for-a-part)) |
94
+
95
+ None of them needs an `import` line. The engine writes three kinds of frame
96
+ itself: the **title frame** first (the deck's title, the course, its authors and
97
+ version), the **outcomes frame** last but one (what the lesson's competencies say
98
+ the student can now do; `deck.outcomesFrame`), and the **references frame** last
99
+ when the deck cites a source.
100
+
101
+ Everything a chapter may hold is allowed on a frame. A `<Memo>` and an aside are
102
+ blocks, always open; a `<Figure>` is the frame's picture, its lab live in Present
103
+ mode; `<AssessmentSet />` makes a question frame; a citation `[@id]` puts its
104
+ marker on the frame and its entry on the references frame.
105
+
106
+ ### A deck for a part
107
+
108
+ A part's deck is the lecture of the whole part, beside the part's page:
109
+
110
+ ```text
111
+ content/docs/motion/index.mdx the part's page (as before)
112
+ content/docs/motion/index.deck.mdx the part's deck, served at /motion/slides/
113
+ ```
114
+
115
+ ```mdx
116
+ ---
117
+ title: Motion in the plane
118
+ description: The lecture's slides for both chapters.
119
+ ---
120
+
121
+ ## Where the point is [chapter: position-and-coordinates]
122
+
123
+ ### A position is two coordinates on one clock
124
+
125
+ <Recall ref="eq-position-components" />
126
+
127
+ ## Two coordinate systems [chapter: position-and-coordinates]
128
+
129
+ ### Polar coordinates give distance and direction
130
+
131
+ <Recall ref="eq-polar-from-cartesian" />
132
+
133
+ ## Velocity over time [chapter: constant-acceleration]
134
+
135
+ ### A constant acceleration adds the same each second
136
+
137
+ <Recall ref="eq-velocity" />
138
+ ```
139
+
140
+ A `##` section may name the chapter it covers, `[chapter: <name>]`, `<name>`
141
+ being the chapter's file in the part's directory; a section without one is the
142
+ deck's own. A chapter may run over several sections in a row, each naming it,
143
+ as a chapter's own deck runs over its sections; it never comes back after a
144
+ section of another chapter, or of the deck's own (`deck-chapter-repeated`). A
145
+ lesson taught as slides only has no chapter to cover. The first divider of a
146
+ chapter's run is the chapter's address in the deck: above,
147
+ `/motion/slides/#slide-6` for the second chapter, after the title frame and the
148
+ first chapter's four. To turn a chapter's own deck into its part's, give each of
149
+ its `##` headings the marker and move its frames in; the chapter's deck goes.
150
+
151
+ The deck is the part's, so it draws on every chapter of it:
152
+
153
+ - **Symbols** bind in the scope of the part's chapters, a key two chapters
154
+ define locally binding to the first in reading order.
155
+ - **`<Recall ref>`** shows a float of a chapter a section covers, with that
156
+ chapter's number, linked to that chapter: the section's own chapter first,
157
+ else the one covered chapter that numbers it. Two that number it, with
158
+ neither the section's own, leave it open (`recall-unresolved`), as does one
159
+ ref recalled from two chapters on two frames. `[[eq-…]]` and the rest name
160
+ the deck's own or recalled float first, else the one covered chapter that
161
+ has it; write `[[<chapter>#eq-…]]` where two do.
162
+ - **`<AssessmentSet />`** on a frame asks the checks of the chapter its
163
+ section covers; it cannot stand in a section that names none
164
+ (`deck-check-no-chapter`).
165
+ - **The generated frames**: the title frame shows the deck's title (the
166
+ part's label without one); the outcomes frame lists the competencies of
167
+ every chapter the deck covers, in reading order.
168
+
169
+ In the sidebar the part's deck shares the part's row with its Overview, and
170
+ follows the sidebar's two controls as a chapter's deck does, Overview standing
171
+ for the chapter ([In the sidebar](#in-the-sidebar)): "Overview" with the deck
172
+ as the icon at its end, or "Slides for this part" with Overview there, or one
173
+ alone. Its glyph is two frames, and both links keep theirs in every state; a
174
+ compact part joins their tiles. Its tooltip names the part, its chapters and
175
+ its slides ("Chapters 1–2 · 26 slides"). In a part with no Overview it is a
176
+ row of its own. A chapter it covers that has no deck of its own gets its
177
+ sections as its other edition, "Slides: *chapter title*" (its tooltip adds
178
+ "Slides 17–25 of the part's slides"), which opens the part's deck at the
179
+ chapter's first divider; "Slides for this chapter" under the chapter's title
180
+ goes there too, and the chapter's Download menu offers the part's deck as "Its
181
+ slides". While the part's deck is open, the section of the chapter on screen
182
+ is lit as the open page is, and the rail runs to it; the title and outcomes
183
+ frames, no chapter's, light none. A lesson's own deck wins over its part's. The part's page offers
184
+ "Slides for this part", and the deck's page "Open the part" beside Present.
185
+ Everything else is what a deck has: Present mode, the marks of the slides a
186
+ reader has seen, rule C5's version of its page (`motion/slides`), its
187
+ downloads (`motion-slides.pdf` and the rest, listed on its page, on the
188
+ part's and on each chapter it covers that has no deck of its own), and the `D`
189
+ rules. A deck for an arbitrary set of chapters is not part of the model: a
190
+ part's deck covers its own part.
191
+
192
+ ### Numbers on a deck
193
+
194
+ Numbers belong to the chapter. A recalled equation, figure, table or diagram keeps
195
+ the chapter's number, and the number links to the chapter. An equation the deck
196
+ labels itself (`\label{eq:…}`) numbers (1), (2), … within the deck. The deck's own
197
+ figures, tables and diagrams are unnumbered. `[[eq-…]]`, `[[fig-…]]` and the rest
198
+ name the deck's own or recalled float first, and the chapter's otherwise;
199
+ `[[sec-…]]` names a section of the chapter.
200
+
201
+ ### Steps and animation
202
+
203
+ A frame shows in steps. `<Pause />` alone reveals what follows it. With props, the
204
+ step also does something to the frame — what the browser can show that a PDF
205
+ cannot:
206
+
207
+ | Step | What it shows | On paper |
208
+ | --- | --- | --- |
209
+ | `<Pause />` | the next claim, when the speaker reaches it | `\pause` |
210
+ | `<Pause control="Launch angle" to="75" seconds="3" />` | the frame's lab moves the control labelled "Launch angle" to 75 over 3 s (`from` sets its start), and the figure follows: cause and effect while it happens | the figure as captured |
211
+ | `<Pause highlight="motion.launch-angle" />` | every occurrence of the symbol on the frame lights up: in the equation, the text, the legend and the figure | nothing |
212
+ | `### Title [morph]` | moving to this frame keeps every symbol the two frames share in its identity: each moves from its old place to its new one, so a derivation shows which term went where | two frames |
213
+
214
+ A driven step moves the lab's own control as a reader's hand would, so any lab can
215
+ be driven. Stepping back and forward replays every step; reduced motion turns the
216
+ movement off and shows each step's end.
217
+
218
+ ### The canvas and the legend
219
+
220
+ Every deck of the course is laid out on one canvas, `deck.aspectRatio` in the
221
+ course settings: `16:9` (800 × 450 px, the default), `16:10` (720 × 450) or `4:3`
222
+ (600 × 450). The canvas is as wide as the page's reading column, so a figure, a
223
+ table or a lab lays out on a frame at the size it was made for. A frame is laid
224
+ out at that size and scaled to the screen, so what fits when the build measures
225
+ it fits everywhere (D2); printed, it fills a page 7.5 in tall.
226
+
227
+ Each frame ends on a **legend line**: the symbols it shows for the first time in
228
+ the deck, each with its name, because a projected frame has no mouse to hover a
229
+ card with. `deck.legend` is `first-use` (the default), `every-frame` or `off`.
230
+
231
+ ### Present mode and the study page
232
+
233
+ The deck's page is a study page: every frame in its final step, its notes under it
234
+ (`deck.notes`: `reader`, the default; `presenter` keeps them to the presenter
235
+ window and the handout; `off`), every card, lab and question live. Each
236
+ frame there has an edge and a drop shadow, set by `appearance.slides` in the
237
+ course settings: `lifted` (the default; in the dark theme the slide is a
238
+ step lighter than the page), `edge` (on the page's own colour) or `sorter`
239
+ (the deck on a tinted well). **Present** shows one frame at a time, full
240
+ screen, with none of them:
241
+
242
+ | Key | Does |
243
+ | --- | --- |
244
+ | → ↓ Space PageDown | the next step, then the next frame (a clicker sends PageDown) |
245
+ | ← ↑ PageUp Backspace | the step or frame before |
246
+ | Home, End | the first frame, the last |
247
+ | a number, then Enter | that frame |
248
+ | `F` | full screen on or off |
249
+ | `N` | the notes beside the frame |
250
+ | `P` | the presenter window: the frame, the next one, the notes and a clock |
251
+ | `B` | a black screen, and back |
252
+ | Esc | leaves Present mode |
253
+
254
+ The address of frame 7 is `#slide-7`, the same number the PDF prints; a link to
255
+ it opens the study page at that frame. A reader who left Present mode on frame
256
+ 7 with a step of it still to come finds a "Continue at slide 7" button beside
257
+ Present, which presents from there; one who was shown all of frame 7 finds
258
+ "Continue at slide 8". It is there right after Esc, after a reload, and on a
259
+ later visit where the course's reading memory may keep their place. One who
260
+ was shown all of the last frame finds "Present again", which presents from the
261
+ first.
262
+
263
+ Above each frame is a line in the page's own type, outside the frame: "Slide
264
+ 7", whether Present mode has shown it ("Shown", "Not shown yet" or "Changed
265
+ since shown"), and a button. On the frame to continue at it is "Continue
266
+ here", always shown; on every other it is "Present from here", shown, with a
267
+ ring round the frame, while the pointer is on the frame or the keyboard's
268
+ focus is in it (always, as its icon, on a touch screen). Under the title, the
269
+ **slide map** shows every frame as a numbered square: filled once shown,
270
+ outlined before, dotted when changed since, the frame to continue at ringed and
271
+ the one in view underlined. It stays under the header while the deck scrolls,
272
+ and a square scrolls to its frame. A deck too long for the map's line keeps
273
+ its first and last frames, the frames before and after the one to continue at,
274
+ and those round the one in view, as many as the line has room for, with a gap
275
+ for each run left out; a gap scrolls to the frame in its middle.
276
+
277
+ The deck keeps each frame Present mode showed the reader, at any step, in the
278
+ room's window or the presenter's, as it was. A frame counts as changed when
279
+ what it shows changed: its text, its notes, or the chapter's number for what it
280
+ recalls (`decks[].frames[].id`); a frame moved by one added before it is still
281
+ the frame the reader saw. "Not shown yet" and "Changed since shown" are the
282
+ sidebar's two marks: `sidebarMarks` turns either off here too, and with
283
+ `unopened` off no frame says "Shown" either. A line under Present counts them
284
+ ("14 slides not shown yet", "2 slides changed since shown") and offers "Mark
285
+ all as seen" and "Mark all as unseen" for the deck; the sidebar's two buttons
286
+ cover every deck's frames as well as the pages. Before the reader answers the
287
+ course's reading-memory question, the frames shown are kept and marked for
288
+ the tab alone, as kept cards are; "Remember" keeps them for later visits, and
289
+ "Don't remember" clears them and records none after. With `readingMemory:
290
+ "off"`, nothing is marked and no frame is recorded. The resume button, the
291
+ lines, the map and the marks are the reader's own: Present mode, print and a
292
+ downloaded deck show none of them.
293
+
294
+ ### In the sidebar
295
+
296
+ A lesson is one row in the sidebar, whatever editions it has. The row opens
297
+ the **lead** edition under the lesson's title, and the other edition is an
298
+ icon at the row's end, a second tile joined to the first in a compact part,
299
+ named "Slides: *title*" or "Chapter: *title*". A lesson with one edition is a
300
+ row like any other; only when that edition is not the lead does the row carry
301
+ its glyph (a slide frame with a play mark cut out of it, or a sheet with
302
+ lines of text). A part's deck shares the part's row with its Overview, and
303
+ follows the sidebar's two controls as a chapter's deck does; a chapter it
304
+ covers with no deck of its own takes its section as the other edition
305
+ ([A deck for a part](#a-deck-for-a-part)). `sidebarEditions`
306
+ in the course settings sets the default:
307
+
308
+ ```json
309
+ "sidebarEditions": { "lead": "chapter", "other": "listed" }
310
+ ```
311
+
312
+ `lead` is `chapter` (the default) or `slides`. `other` is `listed` (the
313
+ default) or `hidden`, which leaves the other edition out of the sidebar: the
314
+ chapter's "Slides for this chapter" and the deck's "Read the chapter" still
315
+ reach it, and the page open now is listed whatever the setting. Each reader
316
+ switches both in the sidebar's foot ("Lessons open as" Chapter or Slides, "List
317
+ the other edition too"). Their choice is a setting kept in their browser
318
+ (`explico-sidebar-editions`), field by field, so a field they never set follows
319
+ the course's default. Without scripts the sidebar lists each chapter, then its
320
+ "Slides" row.
321
+
322
+ At the foot of the page, Next and Previous read on in the edition the reader
323
+ is in: from chapter to chapter on a chapter's page, from deck to deck on a
324
+ deck's ("Slides: *title*"), a part's deck and a slides-first lesson among
325
+ them.
326
+
327
+ ### Downloads
328
+
329
+ With the course's exports on (`lessonExports`), the Download menu has a row for
330
+ the deck, on its page and on its chapter's (a part's deck: on its part's page):
331
+
332
+ - **HTML**: the deck page as one file. It presents offline, with its notes,
333
+ cards, labs and checks.
334
+ - **PDF**: the slides in beamer, one page per step. Every symbol on a frame
335
+ links to its entry on the notation frames after the last frame.
336
+ - **Handout**: each frame in its final step, followed by a page of its notes
337
+ and its check's answers. It is built when `deck.handoutPdf` is on and
338
+ `deck.notes` is not `off`.
339
+ - **LaTeX**: the folder both PDFs are built from: `main.tex`, `handout.tex`,
340
+ `deck.tex`, the theme `beamerthemeexplico.sty` and `explico-common.sty`,
341
+ which the chapters' class shares.
342
+ - **Markdown**: Marp's dialect, one slide per frame, the notes as comments. It
343
+ opens in Marp, its VS Code extension or Deckset.
344
+
345
+ The build fails when the PDF numbers a frame differently from the page, as it
346
+ does for a chapter's numbers, and warns when a frame runs over its slide on
347
+ paper. Beamer cannot write a tagged PDF, so the deck's PDFs do not serve a
348
+ screen reader; its page and its chapter's PDF do. A part's and the course's
349
+ book keep a slides-first lesson's place with a short chapter: its title, its
350
+ outcomes and a link to its slides. The chapters after it keep their numbers.
351
+
352
+ ---
353
+
354
+ ## D1
355
+
356
+ **A deck is sections of frames: `##` opens a section, `###` is a frame, and
357
+ nothing is deeper or outside a frame.**
358
+
359
+ A `##` heading is a divider frame that shows its title and opens a section of the
360
+ deck's outline. A `###` heading is a frame, and everything up to the next `##` or
361
+ `###` is that frame. Nothing else is a heading in a deck.
362
+
363
+ ```mdx
364
+ <!-- Not a deck: content outside a frame, a heading too deep. -->
365
+ An opening sentence.
366
+
367
+ ## The range
368
+
369
+ The range is how far the projectile flies.
370
+
371
+ ### Range is largest at 45°
372
+
373
+ #### Why
374
+
375
+ <!-- A deck: every line in a frame. -->
376
+ ## The range
377
+
378
+ ### The range is how far the projectile flies
379
+
380
+ ### Range is largest at 45°
381
+ ```
382
+
383
+ _Rationale._ The frame is the unit everything else is built on: the deck page's
384
+ cards, Present mode's paging, the frame numbers, the PDF's frames. A line outside
385
+ a frame has nowhere to be shown, and a `####` inside one would be a second title
386
+ on a frame that has one.
387
+
388
+ _How to apply._ Start each frame with `###`; put a section's words in a frame of
389
+ their own. Emphasis inside a frame is bold text or a list, not a heading.
390
+
391
+ _Enforcement._ Build-enforced (`decks-validate.ts`, `pnpm validate:content`):
392
+ `frame-depth` for a `#` or `####`, `deck-loose-content` for content before the
393
+ first heading or between a `##` and its first `###`, `deck-empty` for a deck with
394
+ no frame. Locked: it cannot be turned off.
395
+
396
+ _Exceptions._ None.
397
+
398
+ ## D2
399
+
400
+ **A frame fits its canvas in every step, and never continues on the next frame.**
401
+
402
+ Everything a frame shows — its title, its body at its last step, its legend line —
403
+ fits the course's canvas. A frame that does not fit is two frames.
404
+
405
+ _Rationale._ The frame number is the address of what the room saw: "slide 7" in
406
+ the lecture, on the web, and in the PDF. A frame that overflows is clipped by
407
+ beamer without a word and scrolls on the web, and a frame that continues on the
408
+ next makes two numbers for one idea. Shrinking the type to make it fit defeats the
409
+ canvas: the back row reads what the front row reads.
410
+
411
+ _How to apply._ Keep a frame to one claim (D3) and what supports it. Move the
412
+ argument into its notes (D4), split a list over two frames, and recall a long
413
+ table's head rows rather than the table. `content check` warns early, from the
414
+ source, when a frame passes `maxWords` words or `maxBullets` list items.
415
+
416
+ _Enforcement._ Partial. `frame-dense` (`decks-validate.ts`, `pnpm
417
+ validate:content`) warns from the source. `frame-overflow` (`deck-fit.ts`) fails
418
+ the build: after Astro builds the site, every deck page is opened in the course's
419
+ Chromium and each frame measured at its canvas size, every step shown. Without a
420
+ browser it warns and measures nothing; in CI a missing browser fails the build.
421
+ Review-only: the paper, where the PDF's export reports a frame that runs over as
422
+ a warning naming it.
423
+
424
+ _Exceptions._ None for the fit. A frame may pass the word count when the words are
425
+ a quotation the room reads together; waive `frame-dense` on that frame with its
426
+ reason.
427
+
428
+ ## D3
429
+
430
+ **A frame makes one claim, and its title states the claim.**
431
+
432
+ The title of a frame is the sentence the frame shows to be true: "Range is largest
433
+ at a 45° launch angle", not "Range". Everything on the frame serves that claim.
434
+
435
+ _Rationale._ A room reads the title first and sometimes only the title. A topic
436
+ title ("Range") makes the room work out what the frame is for; a claim tells it,
437
+ and makes the deck's outline a summary of the lecture.
438
+
439
+ _How to apply._ Write the title as a statement with a verb. If a frame needs two
440
+ claims, it is two frames.
441
+
442
+ _Enforcement._ Review-only.
443
+
444
+ _Exceptions._ A section's divider (`##`) names the section; it is a topic, not a
445
+ claim.
446
+
447
+ ## D4
448
+
449
+ **A frame with text carries speaker notes: the argument is in `<Notes>`, the frame
450
+ shows its claims.**
451
+
452
+ A `###` frame whose body has words carries a `<Notes>…</Notes>` block with what the
453
+ speaker says: the reasoning, the transitions, the question to ask the room.
454
+
455
+ _Rationale._ A frame shows claims; the argument between them is spoken. Written
456
+ down, it is what the presenter window shows, what the handout prints under each
457
+ frame, and what a student reads at home, so the deck is as complete as the chapter
458
+ without the frame carrying paragraphs. The prose rules (P1–P9) hold for the notes.
459
+
460
+ _How to apply._ Write the notes as you would say them, in full sentences. A frame
461
+ that only shows a picture, an equation or a question needs none.
462
+
463
+ _Enforcement._ Build-enforced (`decks-validate.ts`, `pnpm validate:content`):
464
+ `frame-notes-missing`, a warning.
465
+
466
+ _Exceptions._ A frame whose words are a label the room needs no help with.
467
+
468
+ ## D5
469
+
470
+ **A frame title fits one line: at most `maxLength` characters.**
471
+
472
+ A `##` or `###` heading in a deck runs to at most `maxLength` characters (50 by
473
+ default) as the reader sees it: math as it reads, a `[[key]]` term as its label.
474
+
475
+ _Rationale._ A title that wraps pushes the frame's body down by a line of the
476
+ largest type on it, and a two-line title is usually a claim and its reason; the
477
+ reason belongs on the frame.
478
+
479
+ _How to apply._ Shorten the claim; move its condition or reason into the frame.
480
+
481
+ _Enforcement._ Build-enforced (`decks-validate.ts`, `pnpm validate:content`):
482
+ `frame-title-length`. The limit is `rules.D5.maxLength`.
483
+
484
+ _Exceptions._ None.
485
+
486
+ ## D6
487
+
488
+ **A frame shows in at most `maxSteps` steps, and each step adds to what is shown.**
489
+
490
+ `<Pause />` is the one step mark. What follows it appears on the next key press, and
491
+ nothing shown earlier changes or goes, so the last step is the whole frame — what the
492
+ scroll view, the print and the PDF show.
493
+
494
+ _Rationale._ A build-up shows the order of an argument; more steps than the room can
495
+ hold in mind is a second frame. A step that takes something away would leave the
496
+ handout, which shows the last step, without what the room saw.
497
+
498
+ _How to apply._ Put `<Pause />` where the speaker stops to ask or to explain. Keep to
499
+ `maxSteps` steps (4 by default); a driven figure or a spotlight is a step too.
500
+
501
+ _Enforcement._ Partial (`decks-validate.ts`, `pnpm validate:content`): `frame-steps`
502
+ warns past `maxSteps`. Review-only: that a later step contradicts nothing already
503
+ shown.
504
+
505
+ _Exceptions._ None.
506
+
507
+ ## D7
508
+
509
+ **A frame holds at most one figure, table or diagram, and it is the frame’s
510
+ picture.**
511
+
512
+ One `<Figure>`, `<Table>`, `<Diagram>` or recalled float per frame, laid out at the
513
+ canvas width. A lab's controls are the frame's tools, live in Present mode.
514
+
515
+ _Rationale._ Two pictures on one frame each get half the canvas, and the room looks
516
+ at neither long enough. One picture at full width is what a projector shows well.
517
+
518
+ _How to apply._ Give a second picture its own frame, or put the two side by side in
519
+ one figure when the comparison is the point.
520
+
521
+ _Enforcement._ Build-enforced (`decks-validate.ts`, `pnpm validate:content`):
522
+ `frame-floats`, a warning.
523
+
524
+ _Exceptions._ A small table and the figure it reads off, when the frame is about
525
+ reading one from the other.
526
+
527
+ ## D8
528
+
529
+ **A result the chapter numbers is recalled with `<Recall>`, never retyped; the
530
+ deck’s own floats carry no number.**
531
+
532
+ `<Recall ref="eq-range" />` (and `fig-`, `tab-`, `dia-`) shows the chapter's
533
+ equation, figure, table or diagram as it is in the chapter, with the chapter's
534
+ number, and links to it.
535
+
536
+ _Rationale._ A retyped equation drifts from the chapter's the first time either is
537
+ corrected, and its number cannot be the chapter's. A recall is the chapter's own, so
538
+ "(2.1.3)" on the frame is the same equation as (2.1.3) in the notes.
539
+
540
+ _How to apply._ Recall what the chapter numbers; label (`\label{eq:…}`) only an
541
+ equation that exists on the deck alone. A deck beside no chapter has nothing to
542
+ recall: its floats are its own.
543
+
544
+ _Enforcement._ Build-enforced (`decks-validate.ts`, `pnpm validate:content`):
545
+ `frame-retyped` warns at a display equation whose LaTeX is a labelled chapter
546
+ equation's; a recall that names nothing is the `decks` invariant's
547
+ `recall-unresolved`.
548
+
549
+ _Exceptions._ A step of a derivation that rewrites the chapter's equation on the way
550
+ to it is not a retype.
551
+
552
+ ## D9
553
+
554
+ **A frame introduces at most `maxNewSymbols` symbols the deck has not shown before.**
555
+
556
+ Count the symbols a frame shows for the first time in the deck. The legend line
557
+ under the frame names them.
558
+
559
+ _Rationale._ The room reads a frame once, at the speaker's pace. Each new symbol is a
560
+ name to learn on the spot; past a handful, the frame is read as notation, not as an
561
+ argument.
562
+
563
+ _How to apply._ Introduce a symbol on the frame that first needs it; split a frame
564
+ that introduces many.
565
+
566
+ _Enforcement._ Build-enforced (`decks-validate.ts`, `pnpm validate:content`):
567
+ `frame-new-symbols`, info.
568
+
569
+ _Exceptions._ A frame that lists the lesson's notation on purpose.
570
+
571
+ ## D10
572
+
573
+ **A deck opens on its title frame and closes on what the student can now do.**
574
+
575
+ The title frame (the lesson, the course, its authors and version) and the closing
576
+ frame (the outcomes of the competencies the lesson teaches) are generated: do not
577
+ write them.
578
+
579
+ _Rationale._ The title frame is what the room sees while it sits down, and the
580
+ closing frame is what it leaves with. Generated, they are the same in every deck and
581
+ always current.
582
+
583
+ _How to apply._ Write neither. With `deck.outcomesFrame` off, end on a `###` frame
584
+ that says what the student can now do.
585
+
586
+ _Enforcement._ Review-only: the engine writes both frames.
587
+
588
+ _Exceptions._ None.
589
+
590
+ ## D11
591
+
592
+ **A deck is flagged when its chapter changed, by rule C5’s version, after the deck itself last did.**
593
+
594
+ Two files mean the chapter and the deck can drift apart with nothing to notice
595
+ it: `deck-stale` is an info diagnostic naming a lesson's own deck whose
596
+ chapter's version (rule C5) is newer than the deck's.
597
+
598
+ _Rationale._ The deck recalls the chapter's results and follows its argument; a
599
+ chapter that moved on is easy to miss from inside the deck file, which never
600
+ changed itself.
601
+
602
+ _How to apply._ When `deck-stale` names a deck, read what changed in the
603
+ chapter since, and judge whether the deck's frames, notes or recalls need the
604
+ same update. A deck the lecturer knows is still correct needs nothing further;
605
+ there is no build failure to clear.
606
+
607
+ _Enforcement._ Build-enforced (`page-versions.ts`, `pnpm validate:content`):
608
+ `deck-stale`, info. Needs rule C5 on — it is what gives a chapter and a deck
609
+ each their own version to compare.
610
+
611
+ _Exceptions._ A part's deck, which covers more than one chapter: it has no
612
+ single chapter to compare against, so it is never flagged.