book-kit 0.0.0-stage → 0.6.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 (139) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +72 -0
  3. package/README.md +251 -2
  4. package/bin/book-kit.js +115 -0
  5. package/commands/_emoji_io.py +32 -0
  6. package/commands/audit-chapter-lengths.js +244 -0
  7. package/commands/check-epub-size.js +68 -0
  8. package/commands/check-pdf-page-count.js +162 -0
  9. package/commands/composite-kindle-emoji.py +85 -0
  10. package/commands/composite-print-emoji.py +98 -0
  11. package/commands/conventions.js +76 -0
  12. package/commands/convert-pdfx.sh +194 -0
  13. package/commands/download-icc-profile.sh +105 -0
  14. package/commands/fonts.py +212 -0
  15. package/commands/generate-reference-docx.py +344 -0
  16. package/commands/kindle-fonts.js +173 -0
  17. package/commands/links-to-footnotes.js +366 -0
  18. package/commands/markdownlint.js +25 -0
  19. package/commands/minify-css.js +43 -0
  20. package/commands/optimize-images.js +73 -0
  21. package/commands/parity.sh +150 -0
  22. package/commands/postprocess-epub.js +541 -0
  23. package/commands/prep-print-src.js +77 -0
  24. package/commands/prose.js +38 -0
  25. package/commands/release-files.js +89 -0
  26. package/commands/reorder-print-frontmatter.js +79 -0
  27. package/commands/strip-page-rules.js +1168 -0
  28. package/commands/strip-property-rules.js +93 -0
  29. package/commands/update-key-takeaways.js +314 -0
  30. package/commands/validate.js +283 -0
  31. package/configs/markdownlint-cli2.jsonc +98 -0
  32. package/configs/textlintrc.json +22 -0
  33. package/css/theme/01-tokens.css +83 -0
  34. package/css/theme/02-fonts.css +79 -0
  35. package/css/theme/03-prose.css +53 -0
  36. package/css/theme/04-base.css +226 -0
  37. package/css/theme/05-components.css +546 -0
  38. package/css/theme/06-utilities.css +40 -0
  39. package/css/theme/07-ereader.css +245 -0
  40. package/css/theme/08-epub.css +865 -0
  41. package/css/theme/09-back-matter.css +47 -0
  42. package/css/theme/10-dark.css +302 -0
  43. package/css/theme/11-print.css +1229 -0
  44. package/css/theme.css +38 -0
  45. package/defaults/book.config.yml +318 -0
  46. package/defaults/pandoc-base.yml +19 -0
  47. package/defaults/pandoc-docx.yml +6 -0
  48. package/defaults/pandoc-epub-fonts.yml +6 -0
  49. package/defaults/pandoc-epub.yml +15 -0
  50. package/defaults/pandoc-kindle.yml +6 -0
  51. package/emoji/1f382.png +0 -0
  52. package/emoji/1f389.png +0 -0
  53. package/emoji/1f440.png +0 -0
  54. package/emoji/1f44d.png +0 -0
  55. package/emoji/1f44f.png +0 -0
  56. package/emoji/1f454.png +0 -0
  57. package/emoji/1f4a1.png +0 -0
  58. package/emoji/1f4bb.png +0 -0
  59. package/emoji/1f602.png +0 -0
  60. package/emoji/1f603.png +0 -0
  61. package/emoji/1f604.png +0 -0
  62. package/emoji/1f60a.png +0 -0
  63. package/emoji/1f60f.png +0 -0
  64. package/emoji/1f64c.png +0 -0
  65. package/emoji/1f64f.png +0 -0
  66. package/emoji/1f914.png +0 -0
  67. package/emoji/1f923.png +0 -0
  68. package/emoji/2714.png +0 -0
  69. package/emoji/274c.png +0 -0
  70. package/filters/add-div-titles.lua +196 -0
  71. package/filters/arc-notice.lua +36 -0
  72. package/filters/body-emoji-images.lua +48 -0
  73. package/filters/callout-classes.lua +40 -0
  74. package/filters/lib/book_kit.lua +110 -0
  75. package/filters/revision.lua +17 -0
  76. package/filters/strip-comments.lua +40 -0
  77. package/filters/strip-emoji-kindle.lua +134 -0
  78. package/fonts/LICENSE-source-code-pro.md +93 -0
  79. package/fonts/LICENSE-source-sans.md +93 -0
  80. package/fonts/LICENSE-source-serif.md +93 -0
  81. package/fonts/recipe.json +41 -0
  82. package/just/book-kit.just +229 -0
  83. package/lib/acronyms.js +47 -0
  84. package/lib/callouts.js +17 -0
  85. package/lib/config.js +211 -0
  86. package/lib/is-main.js +21 -0
  87. package/lib/lint.js +148 -0
  88. package/lib/pandoc.js +148 -0
  89. package/lib/paragraphs.js +173 -0
  90. package/lib/revision.js +35 -0
  91. package/lib/structure.js +39 -0
  92. package/lib/textlint-github-formatter.js +58 -0
  93. package/lib/validate.js +202 -0
  94. package/package.json +98 -4
  95. package/presets/business-nonfiction/CONVENTIONS.md +35 -0
  96. package/presets/business-nonfiction/preset.yml +128 -0
  97. package/profiles/pdfx-def.ps +42 -0
  98. package/requirements.txt +14 -0
  99. package/rules/no-ai-like-patterns.js +209 -0
  100. package/rules/no-assumptive.js +76 -0
  101. package/rules/no-bare-acronyms.js +129 -0
  102. package/rules/no-cliches.js +544 -0
  103. package/rules/no-cultural-idioms.js +422 -0
  104. package/rules/no-vague-intensifiers.js +94 -0
  105. package/templates/template-epub.html +79 -0
  106. package/templates/template.html +73 -0
  107. package/toolchain.json +42 -0
  108. package/validators/acronym-expansion.js +94 -0
  109. package/validators/bold-keywords.js +56 -0
  110. package/validators/callout-balance.js +74 -0
  111. package/validators/callout-divs.js +66 -0
  112. package/validators/callout-labels.js +102 -0
  113. package/validators/chapter-lengths.js +14 -0
  114. package/validators/chapter.js +57 -0
  115. package/validators/cross-references.js +45 -0
  116. package/validators/doubled-words.js +76 -0
  117. package/validators/duplicate-headings.js +52 -0
  118. package/validators/em-dashes.js +73 -0
  119. package/validators/escaped-smallcaps.js +46 -0
  120. package/validators/footnotes.js +78 -0
  121. package/validators/heading-hierarchy.js +58 -0
  122. package/validators/hypothetical-hooks.js +103 -0
  123. package/validators/index-integrity.js +28 -0
  124. package/validators/index.js +73 -0
  125. package/validators/list-punctuation.js +123 -0
  126. package/validators/min-opener-length.js +87 -0
  127. package/validators/no-bare-url-link-text.js +38 -0
  128. package/validators/no-todo-markers.js +49 -0
  129. package/validators/no-transition-todos.js +34 -0
  130. package/validators/number-range-dashes.js +72 -0
  131. package/validators/paragraph-length.js +62 -0
  132. package/validators/quote-punctuation.js +85 -0
  133. package/validators/section-intro-final-lead.js +90 -0
  134. package/validators/section-intro-has-lead.js +41 -0
  135. package/validators/section-intro-no-tldr.js +37 -0
  136. package/validators/section-intros.js +33 -0
  137. package/validators/sentence-starters.js +132 -0
  138. package/validators/smallcaps.js +67 -0
  139. package/validators/tldr-length.js +58 -0
@@ -0,0 +1,98 @@
1
+ // book-kit's base markdownlint config. The build merges lint.markdownlint
2
+ // from book.config.yml (and its presets) into `config` (lib/lint.js); a book
3
+ // with its own .markdownlint-cli2.jsonc uses that instead.
4
+ {
5
+ "config": {
6
+ // Enable all default rules as baseline
7
+ "default": true,
8
+
9
+ // === Line and Formatting Rules ===
10
+ // Disable line length for prose-heavy content (books need flowing paragraphs)
11
+ "line-length": false,
12
+ // Allow chapters to start with H2 (H1 is reserved for book title)
13
+ "first-line-h1": false,
14
+
15
+ // === Heading Rules ===
16
+ // MD001: Heading levels should increment by one level at a time
17
+ "heading-increment": true,
18
+ // MD003: Heading style - use ATX style (hash marks)
19
+ "heading-style": { "style": "atx" },
20
+ // MD022: Headings should be surrounded by blank lines
21
+ "blanks-around-headings": true,
22
+ // MD024: Allow duplicate headings in different sections (common in books)
23
+ "no-duplicate-heading": { "siblings_only": true },
24
+ // MD025: Only one H1 per document (but we use H2 for chapters)
25
+ "single-h1": false,
26
+ // MD041: First line doesn't need to be H1 (chapters start with H2)
27
+ "first-line-heading": false,
28
+
29
+ // === List Rules ===
30
+ // MD004: Use dashes for unordered lists consistently
31
+ "ul-style": { "style": "dash" },
32
+ // MD005: Consistent list indentation
33
+ "list-indent": true,
34
+ // MD007: Unordered list indentation (2 spaces)
35
+ "ul-indent": { "indent": 2 },
36
+ // MD029: Ordered list item prefix (use ordered numbers)
37
+ "ol-prefix": { "style": "ordered" },
38
+ // MD030: Spaces after list markers
39
+ "list-marker-space": true,
40
+ // MD032: Lists should be surrounded by blank lines
41
+ "blanks-around-lists": true,
42
+
43
+ // === Link and Reference Rules ===
44
+ // Disable link fragment checking (custom anchor handling)
45
+ "link-fragments": false,
46
+ // MD034: Allow bare URLs when appropriate
47
+ "no-bare-urls": false,
48
+
49
+ // === HTML Rules ===
50
+ // Allow inline HTML for pandoc custom divs and spans
51
+ "no-inline-html": false,
52
+
53
+ // === Code Block Rules ===
54
+ // MD031: Code blocks should be surrounded by blank lines
55
+ "blanks-around-fences": true,
56
+ // MD040: Fenced code blocks should have a language specified
57
+ "fenced-code-language": false,
58
+ // MD046: Use fenced code blocks consistently
59
+ "code-block-style": { "style": "fenced" },
60
+ // MD048: Use backticks for code fences
61
+ "code-fence-style": { "style": "backtick" },
62
+
63
+ // === Whitespace Rules ===
64
+ // MD009: No trailing spaces (except for line breaks)
65
+ "no-trailing-spaces": { "br_spaces": 2 },
66
+ // MD010: No hard tabs
67
+ "no-hard-tabs": true,
68
+ // MD012: No multiple consecutive blank lines
69
+ "no-multiple-blanks": { "maximum": 1 },
70
+ // MD027: No multiple spaces after blockquote symbol
71
+ "no-multiple-space-blockquote": true,
72
+ // MD047: Files should end with a single newline
73
+ "single-trailing-newline": true,
74
+
75
+ // === Content Rules ===
76
+ // MD026: No trailing punctuation in headings (except ? for questions)
77
+ "no-trailing-punctuation": { "punctuation": ".,;:!" },
78
+ // MD036: No emphasis used as heading
79
+ "no-emphasis-as-heading": true,
80
+ // MD037: No spaces inside emphasis markers
81
+ "no-space-in-emphasis": true,
82
+ // MD038: No spaces inside code span elements
83
+ "no-space-in-code": true,
84
+ // MD039: No spaces inside link text
85
+ "no-space-in-links": true,
86
+ // MD045: Images should have alt text for accessibility
87
+ "no-alt-text": true,
88
+ // MD049: Use asterisks for emphasis consistently
89
+ "emphasis-style": { "style": "asterisk" },
90
+ // MD050: Use asterisks for strong consistently
91
+ "strong-style": { "style": "asterisk" }
92
+ },
93
+ // Ignore generated content (lib/lint.js adds drafts, structure.draft_suffix)
94
+ "ignores": [
95
+ "**/node_modules/**",
96
+ "**/dist/**"
97
+ ]
98
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "filters": {
3
+ "comments": true
4
+ },
5
+ "rules": {
6
+ "no-start-duplicated-conjunction": {
7
+ "interval": 2
8
+ },
9
+ "common-misspellings": {
10
+ "ignore": [
11
+ "doesnt",
12
+ "isnt"
13
+ ]
14
+ },
15
+ "diacritics": true,
16
+ "doubled-spaces": true,
17
+ "en-capitalization": false,
18
+ "@textlint-rule/no-unmatched-pair": true,
19
+ "@textlint-rule/no-duplicate-abbr": true,
20
+ "abbr-within-parentheses": true
21
+ }
22
+ }
@@ -0,0 +1,83 @@
1
+ /* ============================================
2
+ THEME LAYER - Design Tokens & Variables
3
+ Using @theme for utility class generation
4
+ ============================================ */
5
+ @theme {
6
+ /* ── Design Tokens ──────────────────────────────────────────────
7
+ These tokens generate Tailwind utility classes.
8
+
9
+ Safe to modify:
10
+ • Brand colors (--color-brand, etc.) — used in screen CSS only
11
+ • Spacing scale (--spacing-*) — layout breathing room
12
+ • Line-height, paragraph spacing — reading comfort
13
+
14
+ Format-dependent (change with care):
15
+ • Font families — must match @font-face declarations
16
+ • Callout colors — have corresponding dark-mode overrides
17
+ • Border radius — EPUB strips border-radius on some readers
18
+ ────────────────────────────────────────────────────────────── */
19
+
20
+ /* Book Typography - Source font family (Adobe, SIL Open Font License)
21
+ Source Serif 4: transitional serif with optical sizes, ideal for book body text
22
+ Source Sans 3: matching sans-serif for headings and callouts
23
+ Source Code Pro: matching monospace for code blocks */
24
+ --font-body: "Source Serif 4", "Source Serif 4 NotEqual", serif;
25
+ --font-heading: "Source Sans 3", sans-serif;
26
+ --font-mono: "Source Code Pro", "Courier New", monospace;
27
+ --font-emoji:
28
+ "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
29
+
30
+ /* Book Brand Colors */
31
+ --color-brand: #0056b3;
32
+ --color-muted: #6c757d;
33
+ --color-surface: #f8f9fa; /* Available for backgrounds */
34
+ --color-border: #dee2e6;
35
+ --color-emphasis: #495057;
36
+
37
+ /* Callout: Summary (TL;DR) */
38
+ --color-summary-bg: #f8f9fa;
39
+ --color-summary-border: #0056b3;
40
+
41
+ /* Callout: Tip */
42
+ --color-tip-bg: #f8f6f0;
43
+ --color-tip-accent: #b8956a;
44
+
45
+ /* Callout: Manager Guidance */
46
+ --color-manager-bg: #f0f4fa;
47
+ --color-manager-accent: #6a95c2;
48
+
49
+ /* Callout: IC Guidance */
50
+ --color-ic-bg: #f0f8f1;
51
+ --color-ic-accent: #6aaa6e;
52
+
53
+ /* Callout: Objection */
54
+ --color-objection-bg: #faf0f3;
55
+ --color-objection-accent: #b07090;
56
+
57
+ /* Core callouts (the kit defaults' note, tip, warning). Tip reuses the
58
+ Pro-Tip palette. */
59
+ --color-note-bg: #f5f7fa;
60
+ --color-note-accent: #8a9bb0;
61
+ --color-warning-bg: #fdf6e9;
62
+ --color-warning-accent: #c98a2b;
63
+
64
+ /* Callout: Takeaways */
65
+ --color-takeaway-bg: #f5f6f7;
66
+ --color-takeaway-border: #c8d4d9;
67
+
68
+ /* Print Grayscale Palette — Values used by @media print hardcoded rules */
69
+
70
+ /* Spacing Scale for Book Layout */
71
+ --spacing-callout: 1rem;
72
+ --spacing-section: 2rem;
73
+
74
+ /* Border Radius Scale */
75
+ --radius-callout: 0.375rem;
76
+
77
+ /* Book Typography Scale — Optimized for long-form reading */
78
+ --book-line-height: 1.6;
79
+ --book-paragraph-spacing: 0.75em;
80
+ --book-heading-line-height: 1.2;
81
+ --book-letter-spacing: normal;
82
+ --book-word-spacing: 0.02em;
83
+ }
@@ -0,0 +1,79 @@
1
+ /* ============================================
2
+ FONT FACE DECLARATIONS
3
+ Source font family — variable WOFF2 with full OpenType features.
4
+ Source Serif 4: smcp, c2sc, onum, lnum, kern, liga, opsz (optical sizes)
5
+ Source Sans 3: kern, liga, pnum
6
+ Source Code Pro: frac, dnom, numr
7
+ Single file per style covers all weights 200-900.
8
+ ============================================ */
9
+
10
+ /* Source Serif 4 — Body text (variable, Latin subset, optical sizes 8-60) */
11
+ @font-face {
12
+ font-family: "Source Serif 4";
13
+ font-style: normal;
14
+ font-weight: 200 900;
15
+ font-display: swap;
16
+ src: url("../fonts/SourceSerif4-Variable-Latin.woff2") format("woff2");
17
+ }
18
+ @font-face {
19
+ font-family: "Source Serif 4";
20
+ font-style: italic;
21
+ font-weight: 200 900;
22
+ font-display: swap;
23
+ src: url("../fonts/SourceSerif4-Italic-Variable-Latin.woff2") format("woff2");
24
+ }
25
+
26
+ /* Source Serif 4 — ≠ only. The Latin subsets above lack U+2260, so a "≠"
27
+ in body text fell back to Georgia. Upstream Source Serif 4.005
28
+ (adobe-fonts/source-serif), subset to that one glyph. It's a separate
29
+ family, listed second in each serif stack: as an extra face of "Source
30
+ Serif 4", WeasyPrint embedded it under the main face's font name and drew
31
+ the wrong glyph in its place (a blank, or a "C"). */
32
+ @font-face {
33
+ font-family: "Source Serif 4 NotEqual";
34
+ font-style: normal;
35
+ font-weight: 200 900;
36
+ font-display: swap;
37
+ src: url("../fonts/SourceSerif4-NotEqual.woff2") format("woff2");
38
+ unicode-range: U+2260;
39
+ }
40
+ @font-face {
41
+ font-family: "Source Serif 4 NotEqual";
42
+ font-style: italic;
43
+ font-weight: 200 900;
44
+ font-display: swap;
45
+ src: url("../fonts/SourceSerif4-Italic-NotEqual.woff2") format("woff2");
46
+ unicode-range: U+2260;
47
+ }
48
+
49
+ /* Source Sans 3 — Headings, callouts, UI elements (variable, Latin subset) */
50
+ @font-face {
51
+ font-family: "Source Sans 3";
52
+ font-style: normal;
53
+ font-weight: 200 900;
54
+ font-display: swap;
55
+ src: url("../fonts/SourceSans3-Variable-Latin.woff2") format("woff2");
56
+ }
57
+ @font-face {
58
+ font-family: "Source Sans 3";
59
+ font-style: italic;
60
+ font-weight: 200 900;
61
+ font-display: swap;
62
+ src: url("../fonts/SourceSans3-Italic-Variable-Latin.woff2") format("woff2");
63
+ }
64
+
65
+ /* Source Code Pro — Code blocks, inline code (variable, Latin subset) */
66
+ @font-face {
67
+ font-family: "Source Code Pro";
68
+ font-style: normal;
69
+ font-weight: 200 900;
70
+ font-display: swap;
71
+ src: url("../fonts/SourceCodePro-Variable-Latin.woff2") format("woff2");
72
+ }
73
+ @font-face {
74
+ font-family: "Source Code Pro";
75
+ font-style: italic;
76
+ font-weight: 200 900;
77
+ font-display: swap;
78
+ src: url("../fonts/SourceCodePro-Italic-Variable-Latin.woff2") format("woff2");
79
+ }
@@ -0,0 +1,53 @@
1
+ /* ============================================
2
+ TAILWIND TYPOGRAPHY - PROSE CONTAINER STYLES
3
+ Pandoc content should use class="prose" wrapper
4
+ for Typography descendant rules to activate.
5
+ ============================================ */
6
+
7
+ /* Base prose configuration with book theme colors */
8
+ .prose {
9
+ @apply prose-neutral max-w-none;
10
+ --tw-prose-body: var(--color-emphasis);
11
+ --tw-prose-headings: var(--color-emphasis);
12
+ --tw-prose-lead: var(--color-muted);
13
+ --tw-prose-links: var(--color-brand);
14
+ --tw-prose-bold: var(--color-emphasis);
15
+ --tw-prose-counters: var(--color-muted);
16
+ --tw-prose-bullets: var(--color-border);
17
+ --tw-prose-hr: var(--color-border);
18
+ --tw-prose-quotes: var(--color-emphasis);
19
+ --tw-prose-quote-borders: var(--color-muted);
20
+ --tw-prose-captions: var(--color-muted);
21
+ --tw-prose-code: var(--color-emphasis);
22
+ --tw-prose-pre-code: #1f2937;
23
+ --tw-prose-pre-bg: #f8f8f8;
24
+ --tw-prose-th-borders: var(--color-border);
25
+ --tw-prose-td-borders: var(--color-border);
26
+ }
27
+
28
+ /* Comfortable reading width on screen */
29
+ @media screen {
30
+ .prose {
31
+ max-width: 40rem;
32
+ margin-left: auto;
33
+ margin-right: auto;
34
+ padding-left: 1.5rem;
35
+ padding-right: 1.5rem;
36
+ }
37
+ }
38
+
39
+ /* Larger text on medium screens */
40
+ @media screen and (min-width: 768px) {
41
+ .prose {
42
+ @apply prose-lg;
43
+ max-width: 42rem;
44
+ }
45
+ }
46
+
47
+ /* Extra large text on wide screens */
48
+ @media screen and (min-width: 1024px) {
49
+ .prose {
50
+ @apply prose-xl;
51
+ max-width: 44rem;
52
+ }
53
+ }
@@ -0,0 +1,226 @@
1
+ /* ============================================
2
+ BASE LAYER - Element Resets & Typography
3
+ ============================================ */
4
+ @layer base {
5
+ body {
6
+ font-family: var(--font-body);
7
+ font-optical-sizing: auto;
8
+ text-align: left;
9
+ line-height: var(--book-line-height);
10
+ letter-spacing: var(--book-letter-spacing);
11
+ word-spacing: var(--book-word-spacing);
12
+ font-kerning: normal;
13
+ font-variant-ligatures: common-ligatures contextual;
14
+ font-variant-numeric: oldstyle-nums proportional-nums;
15
+ text-rendering: optimizeLegibility;
16
+ -webkit-font-smoothing: antialiased;
17
+ -moz-osx-font-smoothing: grayscale;
18
+ }
19
+
20
+ /* Variable font weight for bold — 600 (semibold) gives better
21
+ color and texture than browser-default 700 with Source Serif 4. */
22
+ strong,
23
+ b {
24
+ font-weight: 600;
25
+ }
26
+
27
+ /* Paragraph typography for book readability */
28
+ p {
29
+ margin-bottom: var(--book-paragraph-spacing);
30
+ /* hanging-punctuation: Safari-only, stripped for EPUB compatibility */
31
+ hanging-punctuation: first allow-end last;
32
+ }
33
+
34
+ /* Heading Hierarchy — clear visual distinction at every level */
35
+ h1 {
36
+ font-family: var(--font-heading);
37
+ font-size: 2.25rem;
38
+ font-weight: 700;
39
+ line-height: var(--book-heading-line-height);
40
+ margin-top: 0;
41
+ margin-bottom: 1.25rem;
42
+ letter-spacing: normal;
43
+ page-break-after: avoid;
44
+ page-break-inside: avoid;
45
+ orphans: 3;
46
+ widows: 3;
47
+ }
48
+
49
+ h2 {
50
+ font-family: var(--font-heading);
51
+ font-size: 1.75rem;
52
+ font-weight: 700;
53
+ line-height: var(--book-heading-line-height);
54
+ margin-top: 2.5rem;
55
+ margin-bottom: 0.75rem;
56
+ letter-spacing: normal;
57
+ page-break-after: avoid;
58
+ page-break-inside: avoid;
59
+ orphans: 3;
60
+ widows: 3;
61
+ }
62
+
63
+ h3 {
64
+ font-family: var(--font-heading);
65
+ font-size: 1.35rem;
66
+ font-weight: 700;
67
+ line-height: var(--book-heading-line-height);
68
+ margin-top: 2rem;
69
+ margin-bottom: 0.5rem;
70
+ letter-spacing: normal;
71
+ page-break-after: avoid;
72
+ page-break-inside: avoid;
73
+ orphans: 3;
74
+ widows: 3;
75
+ }
76
+
77
+ h4 {
78
+ font-family: var(--font-heading);
79
+ font-size: 1.15rem;
80
+ font-weight: 700;
81
+ line-height: var(--book-heading-line-height);
82
+ margin-top: 1.5rem;
83
+ margin-bottom: 0.35rem;
84
+ letter-spacing: normal;
85
+ orphans: 2;
86
+ widows: 2;
87
+ page-break-after: avoid;
88
+ page-break-inside: avoid;
89
+ }
90
+
91
+ h5 {
92
+ font-family: var(--font-heading);
93
+ font-size: 1rem;
94
+ font-weight: 700;
95
+ line-height: var(--book-heading-line-height);
96
+ margin-top: 1.25rem;
97
+ margin-bottom: 0.25rem;
98
+ letter-spacing: 0.07em;
99
+ text-transform: uppercase;
100
+ orphans: 2;
101
+ widows: 2;
102
+ page-break-after: avoid;
103
+ page-break-inside: avoid;
104
+ }
105
+
106
+ h6 {
107
+ font-family: var(--font-heading);
108
+ font-size: 0.95rem;
109
+ font-weight: 700;
110
+ line-height: var(--book-heading-line-height);
111
+ margin-top: 1rem;
112
+ margin-bottom: 0.25rem;
113
+ letter-spacing: normal;
114
+ font-style: italic;
115
+ orphans: 2;
116
+ widows: 2;
117
+ page-break-after: avoid;
118
+ page-break-inside: avoid;
119
+ }
120
+
121
+ /* Navigation and TOC */
122
+ nav {
123
+ page-break-after: avoid;
124
+ }
125
+
126
+ #TOC {
127
+ page-break-inside: auto;
128
+ }
129
+
130
+ #TOC li {
131
+ list-style: none;
132
+ margin-bottom: 0.4em;
133
+ line-height: 1.4;
134
+ }
135
+
136
+ #TOC a {
137
+ color: inherit;
138
+ text-decoration: none;
139
+ margin-bottom: 0.2em;
140
+ }
141
+
142
+ /* Page numbers in TOC — CSS Paged Media, print/PDF only.
143
+ Scoped here so strip-page-rules.js naturally excludes it from EPUB
144
+ (target-counter is unsupported by Kindle and all e-readers). */
145
+ @media print {
146
+ #TOC a {
147
+ display: flex;
148
+ justify-content: space-between;
149
+ align-items: baseline;
150
+ padding-right: 0;
151
+ }
152
+
153
+ #TOC a::after {
154
+ content: target-counter(attr(href), page);
155
+ flex-shrink: 0;
156
+ margin-left: auto;
157
+ font-weight: normal;
158
+ white-space: nowrap;
159
+ font-family: var(--font-heading);
160
+ font-variant-numeric: lining-nums tabular-nums;
161
+ text-align: right;
162
+ width: 2.5em;
163
+ }
164
+ }
165
+
166
+ #TOC ul {
167
+ padding-left: 1.5rem;
168
+ }
169
+
170
+ #TOC > ul {
171
+ padding-left: 0.75rem;
172
+ padding-right: 0.75rem;
173
+ }
174
+
175
+ /* Top-level TOC entries (section headings) get extra weight */
176
+ #TOC > ul > li {
177
+ margin-bottom: 0.6em;
178
+ font-weight: 500;
179
+ }
180
+
181
+ /* Nested TOC entries (chapter titles) */
182
+ #TOC > ul > li > ul > li {
183
+ font-weight: 400;
184
+ font-size: 0.95em;
185
+ }
186
+
187
+ /* Small caps strategy:
188
+ • Screen: OpenType smcp/c2sc glyphs at natural size (no compensation)
189
+ • Print: font-size: inherit — Source Serif 4 provides real smcp glyphs
190
+ • EPUB: font-size: 1.05em compensation — e-readers often synthesize
191
+ small caps by scaling down uppercase, which renders shorter than
192
+ true OpenType glyphs. 1.05em brings visual height closer to x-height. */
193
+
194
+ /* Small Caps for Acronyms
195
+ * IMPORTANT: Do NOT add font-size reduction here.
196
+ * all-small-caps renders at x-height (matching lowercase).
197
+ * Adding font-size < 1em would make small caps shorter than lowercase letters.
198
+ */
199
+ span.smallcaps {
200
+ font-variant-caps: all-small-caps;
201
+ letter-spacing: 0.05em;
202
+ }
203
+
204
+ /* Title Block Elements */
205
+ p.author,
206
+ p.subtitle,
207
+ p.date {
208
+ font-family: var(--font-heading);
209
+ }
210
+
211
+ h1.title,
212
+ p.author,
213
+ p.subtitle,
214
+ p.date,
215
+ div.rights {
216
+ text-align: center;
217
+ }
218
+
219
+ p.subtitle {
220
+ font-size: 1.15rem;
221
+ font-weight: 300;
222
+ line-height: 1.5;
223
+ font-style: italic;
224
+ color: var(--color-muted);
225
+ }
226
+ }