gp-dimm-city 1.0.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 (58) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/LICENSE +32 -0
  3. package/README.md +165 -0
  4. package/components.yaml +832 -0
  5. package/fonts/lixdu/LICENSE.txt +34 -0
  6. package/fonts/lixdu/lixdu.woff +0 -0
  7. package/fonts/titillium-web/OFL.txt +93 -0
  8. package/fonts/titillium-web/titillium-web-400-italic.ttf +0 -0
  9. package/fonts/titillium-web/titillium-web-400.ttf +0 -0
  10. package/fonts/titillium-web/titillium-web-600-italic.ttf +0 -0
  11. package/fonts/titillium-web/titillium-web-600.ttf +0 -0
  12. package/fonts/titillium-web/titillium-web-700-italic.ttf +0 -0
  13. package/fonts/titillium-web/titillium-web-700.ttf +0 -0
  14. package/fonts/tomorrow/OFL.txt +93 -0
  15. package/fonts/tomorrow/tomorrow-400-italic.ttf +0 -0
  16. package/fonts/tomorrow/tomorrow-400.ttf +0 -0
  17. package/fonts/tomorrow/tomorrow-500-italic.ttf +0 -0
  18. package/fonts/tomorrow/tomorrow-500.ttf +0 -0
  19. package/fonts/tomorrow/tomorrow-700-italic.ttf +0 -0
  20. package/fonts/tomorrow/tomorrow-700.ttf +0 -0
  21. package/images/brick-bg-01.png +0 -0
  22. package/package.json +78 -0
  23. package/plugin.js +2131 -0
  24. package/snippets/alert.md +2 -0
  25. package/snippets/block.md +5 -0
  26. package/snippets/callout.md +5 -0
  27. package/snippets/card.md +11 -0
  28. package/snippets/chapter-start-page.md +11 -0
  29. package/snippets/credits-page.md +13 -0
  30. package/snippets/definition.md +7 -0
  31. package/snippets/dm-note.md +5 -0
  32. package/snippets/gear.md +9 -0
  33. package/snippets/learning-path.md +10 -0
  34. package/snippets/lede.md +5 -0
  35. package/snippets/outcome.md +8 -0
  36. package/snippets/procedure.md +7 -0
  37. package/snippets/sidebar.md +7 -0
  38. package/snippets/skill.md +9 -0
  39. package/snippets/specialty-art.md +5 -0
  40. package/snippets/specialty-card.md +5 -0
  41. package/snippets/specialty.md +11 -0
  42. package/snippets/toc-page.md +15 -0
  43. package/styles/components/callouts.css +227 -0
  44. package/styles/components/cards.css +487 -0
  45. package/styles/components/chrome.css +483 -0
  46. package/styles/components/data.css +1026 -0
  47. package/styles/components/images.css +215 -0
  48. package/styles/components/section.css +1074 -0
  49. package/styles/components/specialty-identity.css +315 -0
  50. package/styles/components/specialty.css +273 -0
  51. package/styles/dc-component-defaults.css +214 -0
  52. package/styles/dc-core.css +268 -0
  53. package/styles/dc-fonts.css +107 -0
  54. package/styles/dc-identity.css +66 -0
  55. package/styles/dc-native.css +330 -0
  56. package/styles/dc-palette.css +198 -0
  57. package/styles/page-rules.css +207 -0
  58. package/styles/page-templates.css +636 -0
@@ -0,0 +1,1074 @@
1
+ @layer dc.components {
2
+
3
+ /* ── Base card: every @section gets a cream surface + left accent strip ──
4
+ Public tokens (defaults declared in dc-component-defaults.css :root):
5
+ --dc-section-bg — panel background surface
6
+ --dc-section-accent — left strip / top bar accent color */
7
+ .section {
8
+ /* `filter` on this element stops negative-margin heading bars escaping the
9
+ parent's padding area (it creates a stacking context that suppresses
10
+ negative-margin overflow rendering). Shadow moved to ::after below.
11
+ `isolation: isolate` is load-bearing: ::before/::after paint at negative
12
+ z-index, and without a stacking context here they would escape to the
13
+ nearest one above and paint behind the page. It creates that context
14
+ without blocking child margin overflow the way `filter` does. */
15
+ background: transparent;
16
+ isolation: isolate;
17
+ /* display: flow-root moved to the :where(.section) rule above — the
18
+ float-containment default must sit at zero specificity so core layout
19
+ classes can own `display`. */
20
+ padding: var(--space-md) var(--space-lg);
21
+ /* Single-direction margin: bottom only. Consecutive sections collapse to
22
+ the bottom margin gap (12pt). First section after a chevron has no
23
+ top gap → flush attachment without an adjacency rule.
24
+ Iter-8 (2026-05-25).
25
+
26
+ margin-right: 3px is ROOM FOR THE ::after SHADOW, which sits at
27
+ `right: -3px` and so overhangs this box. While `.page` clipped
28
+ everything that overhang was invisible; on a page that must not clip
29
+ (one carrying pinned or bleed art — see page-templates.css) it escapes
30
+ to the document and trips core's engine.width.overflow hard error at
31
+ 711px against a 708px content box. Giving the decoration room is the
32
+ fix; clipping it back was only ever hiding it. Same shape as
33
+ .dc-path-shell's padding-right. */
34
+ margin: 0 3px 12pt 0;
35
+ position: relative;
36
+ /* Sections are atomic units — they MUST NOT span pages. If a section
37
+ does not fit on the remaining page space, it moves to the next page
38
+ entirely. The resulting dead zone at the end of the previous page is
39
+ intentional design behavior, not a layout failure. Authors keep
40
+ sections short enough to fit; if a section is too tall, split it
41
+ into multiple @section blocks at logical boundaries. */
42
+ break-inside: avoid;
43
+ /* Wall-shadow is the ::after fake-shadow below (vector, same clip-path
44
+ as ::before). Do NOT add filter:drop-shadow here — that would double
45
+ the shadow (raster filter + vector pseudo) per print R9 finding. */
46
+ }
47
+
48
+ /* Optional escape hatch for genuinely long rules sections where atomic-card
49
+ pagination creates unusable dead zones. Use sparingly and only on sections
50
+ that have already been split at logical authoring boundaries. */
51
+ .section.dc-allow-split {
52
+ break-inside: auto;
53
+ }
54
+
55
+ /* clip-path + left accent band on ::before.
56
+
57
+ The substrate sits BEHIND the content (z-index: -1), it does not sit at 0
58
+ with every child lifted over it. Both orderings paint the same picture —
59
+ CSS 2.1 Appendix E paints negative-z child stacking contexts (step 2)
60
+ before in-flow content (steps 3-5) and z-index:0 positioned descendants
61
+ (step 6) after it — but only one of them is free.
62
+
63
+ The lift cost a rule, `.section > * { position: relative; z-index: 1 }`,
64
+ and that rule made EVERY direct child of every section a containing block
65
+ for absolute positioning. Core's `.gp-pin` is `position: absolute;
66
+ inset: 0`, and a lone markdown image is wrapped in `p.dc-img-wrapper`
67
+ whose only child is that pin — so the wrapper had no in-flow content, no
68
+ height, and `.gp-bottom` resolved to the bottom of a zero-height box.
69
+ MEASURED (dc-native.css §6, CSS.getMatchedStylesForNode): the
70
+ credits founders plate authored inside its `@section` painted 179pt below
71
+ the sheet and fragmented onto the next one. The workaround was to author
72
+ every pinned image after `@end-section`, as page furniture.
73
+
74
+ Deleting the lift removes the trap at the source: a `.gp-pin` inside an
75
+ `@section` now resolves against `.section` itself, which is a real box on
76
+ the sheet. `.section` keeps `position: relative` because these two pseudos
77
+ need it, so a pin inside a section still pins to the SECTION, not the
78
+ page — that is core's stated `.gp-pin` contract (nearest positioned
79
+ ancestor) working correctly, not a bug. Page-level furniture still belongs
80
+ outside the section.
81
+
82
+ `isolation: isolate` on `.section` is what makes -1 safe: without a
83
+ stacking context here the negative layers would escape to the nearest one
84
+ above and paint behind the page. */
85
+ .section::before {
86
+ content: '';
87
+ position: absolute;
88
+ inset: 0;
89
+ background: linear-gradient(to right, var(--dc-section-accent) 5px, var(--dc-section-bg) 5px);
90
+ clip-path: polygon(0 0, calc(100% - 18px) 0, 100% 18px, 100% 100%, 0 100%);
91
+ z-index: -1;
92
+ }
93
+
94
+ /* Fake shadow via ::after — same clip-path as ::before, shifted 3px right + down.
95
+ Replaces the removed filter: drop-shadow() while leaving the box model unaffected
96
+ so negative-margin heading bars can reach the card edges. Base card only — variants
97
+ with different or no chrome suppress this via content: none below.
98
+ -2, one step under the card surface: at equal z-index the two pseudos would
99
+ order by tree position and the shadow (::after) would paint over the card. */
100
+ .section::after {
101
+ content: '';
102
+ position: absolute;
103
+ top: 3px;
104
+ left: 3px;
105
+ right: -3px;
106
+ bottom: -3px;
107
+ background: var(--ink-dark);
108
+ clip-path: polygon(0 0, calc(100% - 18px) 0, 100% 18px, 100% 100%, 0 100%);
109
+ z-index: -2;
110
+ pointer-events: none;
111
+ }
112
+
113
+ /* Suppress fake shadow on variants that have different chrome or no chrome */
114
+ /* ── .dc-plain — the one documented escape from .section's panel chrome ──
115
+ `.section` is CORE's structural class: every @section marker in every book
116
+ produces one, and this theme paints a full panel card on it by default via
117
+ the unqualified `::before` (substrate + accent rail + clipped corner) and
118
+ `::after` (fake shadow) above. Because that chrome is opt-OUT, every
119
+ non-panel use of @section needed its own bespoke undo — there were four,
120
+ spread across two files, each re-declaring the same `content: none`.
121
+
122
+ `.dc-plain` is now the single documented way to say "this section is a
123
+ layout wrapper, not a card". The four production sites are listed in the
124
+ same rule rather than each carrying its own copy, so a future non-panel
125
+ section is one documented class instead of a fifth bespoke reset.
126
+
127
+ Each site keeps EXACTLY the pseudos it suppressed before — `.dc-tabbed` is
128
+ ::after only, because it deliberately keeps its ::before accent strip (see
129
+ the tabbed variant below). A blanket `.dc-plain` on all four would strip
130
+ that strip and change what `.dc-tabbed` renders.
131
+
132
+ Lives here, not in page-templates.css, even for the two `.page.*` sites:
133
+ the chrome these cancel is defined in this file, a dozen lines above, so
134
+ its opt-out belongs beside it. The chapter-opener composite's own
135
+ suppression is deliberately NOT folded in — it is part of the frozen
136
+ composite (CLAUDE.md) and keeps its own higher-specificity rule below. */
137
+ .section.dc-plain::before,
138
+ .section.dc-plain::after,
139
+ .section.dc-tabbed::after,
140
+ .section.dc-column-panel::after,
141
+ .section.dc-card-grid::before,
142
+ .section.dc-card-grid::after,
143
+ .page.page-intro > .section::before,
144
+ .page.page-intro > .section::after,
145
+ .page.page-credits .section.credits-colophon::before,
146
+ .page.page-credits .section.credits-colophon::after {
147
+ content: none;
148
+ }
149
+
150
+ /* `.section` already sets `background: transparent` and nothing in the stack
151
+ sets `filter` on it, so this is belt-and-braces for an author who tints a
152
+ section and then marks it plain. `.dc-card-grid` is NOT listed: it already
153
+ declares both in its own rule, and one home per declaration is the
154
+ convention dc#34 wrote down. */
155
+ .section.dc-plain {
156
+ filter: none;
157
+ background: transparent;
158
+ }
159
+
160
+ /* ── COLUMN PANEL — the section variant for a column run ──────────────────
161
+ `.dc-column-panel` is the CHROME half of what `.two-column` /
162
+ `.three-column` used to mean. Those names were the book's own generic
163
+ spelling of core's `.gp-columns-2` / `.gp-columns-3` (gutterpress
164
+ CLAUDE.md §6: a theme must not ship competing generic column names), but
165
+ they were doing two unrelated jobs at once — the column COUNT and this
166
+ card treatment. Core owns the count now; the count class says how many
167
+ columns, this class says what the box around them looks like.
168
+
169
+ Authors write both: `@section .gp-columns-2 .dc-column-panel`. A column
170
+ run that wants no card is `@section .gp-columns-2` alone — which is the
171
+ point of splitting them.
172
+
173
+ Top/bottom borders only: the base `.section` card's clip-path corner
174
+ breaks multicol, so this variant trades it for flat rules. */
175
+ .section.dc-column-panel {
176
+ filter: none;
177
+ background: var(--dc-section-bg);
178
+ border-top: 3px solid var(--dc-section-accent);
179
+ border-bottom: 2px solid var(--border-hairline);
180
+ /* Horizontal padding was 0 — Dream Mastery body text + other two/three
181
+ column sections pressed against the section's left and right edges.
182
+ Add comfortable horizontal padding so prose has breathing room. */
183
+ padding: var(--space-sm) var(--space-md);
184
+ margin: 12pt 0;
185
+ }
186
+
187
+ .section.dc-column-panel::before {
188
+ display: none;
189
+ }
190
+
191
+ /* ── Tabbed variant — first-child heading sits ABOVE the section ─────────
192
+ `@section .dc-tabbed` opts the section into a `.dc-card-tab → .dc-card-body`
193
+ silhouette. Flex container is required so the first-child heading's
194
+ negative margin-top actually lifts it above the section (block layout
195
+ clamps negative margins on the first in-flow child). `.section::before`
196
+ is absolutely positioned, so flex doesn't disturb the background pseudo. */
197
+ .section.dc-tabbed {
198
+ --dc-section-tab-overlap: 0px;
199
+ --dc-section-tab-indent: 0px;
200
+ display: flex;
201
+ flex-direction: column;
202
+ margin-top: calc(var(--space-md) + 1.5em + 5px);
203
+ }
204
+
205
+ /* ── Shared base — both H2 and H3 tabs use the cut-corner stencil silhouette */
206
+ .section.dc-tabbed > h2:first-child,
207
+ .section.dc-tabbed > h3:first-child {
208
+ display: inline-block;
209
+ width: fit-content;
210
+ /* Surface treatment: subtle vertical gradient (top highlight + bottom
211
+ shadow) so the tab reads as a physical material (chipped stone /
212
+ anodized panel / circuit substrate) catching light from above,
213
+ rather than a flat colored shape. Per user feedback: "should look
214
+ more realistic as in, like, crystals or broken rock or circuit
215
+ boards, digital displays that are kinda chipped up." Stays subtle —
216
+ no noise overlays, no decorative additions. */
217
+ background:
218
+ linear-gradient(180deg,
219
+ color-mix(in srgb, var(--dc-section-accent) 86%, #fff 14%) 0%, /* PDF/X-1a: pre-composite to ~86% accent + 14% white; e.g. on --brand-magenta (#9b2b7a) → #ad4891; on Ingram submission replace with static opaque value in dc-tokens-print.css */
220
+ var(--dc-section-accent) 45%,
221
+ color-mix(in srgb, var(--dc-section-accent) 93%, #000 7%) 100%); /* PDF/X-1a: pre-composite to ~93% accent + 7% black; e.g. on --brand-magenta (#9b2b7a) → #912872; on Ingram submission replace with static opaque value in dc-tokens-print.css */
222
+ /* Inset highlights/shadows complete the material illusion — 1px bright
223
+ stripe at top, 1px dark stripe at bottom-right edge. */
224
+ box-shadow:
225
+ inset 0 1px 0 rgba(255, 255, 255, 0.18),
226
+ inset 0 -1px 0 rgba(0, 0, 0, 0.22),
227
+ inset -1px 0 0 rgba(0, 0, 0, 0.12);
228
+ color: var(--paper-cream);
229
+ margin-right: 0;
230
+ margin-bottom: var(--space-sm);
231
+ margin-left: var(--dc-section-tab-indent);
232
+ /* No borders — silhouette is defined entirely by the clip-path against the
233
+ paper background. This makes the tab read as a stenciled/torn artifact
234
+ rather than a bordered UI control. */
235
+ border: 0;
236
+ border-radius: 0;
237
+ /* Chipped-stone clip — multiple notches on all exposed edges so the tab
238
+ reads as a fragment of physical material placed on the card:
239
+ top-left 10px chamfer (beveled corner)
240
+ top edge one 4px chip at ~25% from left
241
+ top-right 16px setback before the right diagonal
242
+ right side double-V staircase — two inward bites create a jagged
243
+ diagonal rather than a smooth slope
244
+ bottom-right flush (the card covers this edge)
245
+ bottom edge clean
246
+ bottom-left asymmetric triangular scar
247
+ left edge small 3px chip at midpoint
248
+ left-top 10px from corner before entering the top edge */
249
+ clip-path: polygon(
250
+ 10px 0,
251
+ calc(25% - 2px) 0, 25% 4px, calc(25% + 2px) 0,
252
+ calc(100% - 16px) 0,
253
+ calc(100% - 7px) 27%,
254
+ calc(100% - 12px) 38%,
255
+ calc(100% - 5px) 48%,
256
+ calc(100% - 11px) 58%,
257
+ calc(100% - 4px) 68%,
258
+ 100% 100%,
259
+ 14px 100%,
260
+ 6px calc(100% - 5px),
261
+ 0 calc(100% - 11px),
262
+ 3px 48%, 0 52%, 3px 56%,
263
+ 0 10px
264
+ );
265
+ font-family: var(--font-tab);
266
+ line-height: 1.2;
267
+ letter-spacing: var(--ls-display);
268
+ /* 1px ink-dark text-shadow adds grit/weight to the tab typography without
269
+ triggering the clip-path+filter conflict that would kill the clip mask. */
270
+ text-shadow: 1px 1px 0 var(--ink-dark);
271
+ text-transform: uppercase;
272
+ position: relative;
273
+ z-index: 2;
274
+ break-after: avoid;
275
+ }
276
+
277
+ /* ── H2 tab — PRIMARY: --fs-h4 (13pt) + heavier padding for visual weight.
278
+ lift = section padding-top + tab height (1.2em × --fs-h4 + paddingY ~0.55em)
279
+ + 2px tolerance + --dc-section-tab-overlap (negative = gap above card). */
280
+ .section.dc-tabbed > h2:first-child {
281
+ font-size: var(--fs-h4);
282
+ padding: 0.30em 0.95em calc(0.25em + 2px);
283
+ margin-top: calc(-1 * var(--space-md) - 1.75em - 2px + var(--dc-section-tab-overlap));
284
+ }
285
+
286
+ /* ── H3 tab — SUBORDINATE: --fs-h5 (11pt) + tighter padding.
287
+ Flush-bottom margin uses smaller em coefficient since H3's paddings are
288
+ tighter (paddingY ~0.45em + 2px). */
289
+ .section.dc-tabbed > h3:first-child {
290
+ font-size: var(--fs-h5);
291
+ padding: 0.25em 0.75em calc(0.20em + 2px);
292
+ margin-top: calc(-1 * var(--space-md) - 1.65em - 2px + var(--dc-section-tab-overlap));
293
+ }
294
+
295
+ /* ── First-child heading inside a section becomes a filled top bar ───────
296
+ Base rule covers all sections. More-specific .section.dc-tabbed rules
297
+ (above) already override via higher specificity — no negation needed. */
298
+ .section > h2:first-child,
299
+ .section > h3:first-child {
300
+ background: var(--dc-section-accent);
301
+ color: var(--paper-cream);
302
+ /* Negative margins pull bar flush with the section border-box edges. */
303
+ margin: calc(-1 * var(--space-md)) calc(-1 * var(--space-lg)) var(--space-md);
304
+ padding: 0.42rem var(--space-lg) 0.28rem;
305
+ border: 0;
306
+ letter-spacing: var(--ls-display);
307
+ text-transform: uppercase;
308
+ break-after: avoid;
309
+ }
310
+
311
+ /* H2 — PRIMARY: taller padding + 18px chamfer + orange rule. */
312
+ .section > h2:first-child {
313
+ font-size: var(--fs-h3);
314
+ padding-top: 0.55rem;
315
+ padding-bottom: 0.40rem;
316
+ clip-path: polygon(0 0, calc(100% - 18px) 0, 100% 18px, 100% 100%, 0 100%);
317
+ border-bottom: 3pt solid var(--orange);
318
+ }
319
+
320
+ /* H3 — SUBORDINATE: hierarchy reads through font size alone. */
321
+ .section > h3:first-child {
322
+ font-size: var(--fs-h4);
323
+ clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%);
324
+ }
325
+
326
+ /* ── Column-panel sections — only the deltas from the base rule ───────────
327
+ column-span:all keeps the bar from being trapped inside column 1.
328
+ The negative margins must cancel THIS variant's padding, which is
329
+ `var(--space-sm) var(--space-md)` — smaller than the base section's
330
+ `var(--space-md) var(--space-lg)`. Both axes need restating: the base
331
+ rule's -space-lg horizontal pull overshoots -space-md padding by ~3px per
332
+ side, which pushed the bar outside the card and past the print safe area. */
333
+ .section.dc-column-panel > h2:first-child,
334
+ .section.dc-column-panel > h3:first-child {
335
+ column-span: all;
336
+ margin-top: calc(-1 * var(--space-sm));
337
+ margin-left: calc(-1 * var(--space-md));
338
+ margin-right: calc(-1 * var(--space-md));
339
+ }
340
+
341
+ /* Column-panel sections whose first child is a heading: drop the redundant
342
+ `border-top: 3px solid var(--dc-section-accent)` since the bar IS the top.
343
+ Unconditional rather than :has(): column panels always lead with a
344
+ heading in this system, so the two are functionally equivalent and the
345
+ unconditional form is one selector instead of a tree query. */
346
+ .section.dc-column-panel {
347
+ border-top: 0;
348
+ }
349
+
350
+ /* Heading bar followed immediately by a table — give the table head room
351
+ so the bar and `<th>` background don't bleed into a single coloured block.
352
+ The two selectors are spelled out; :is(h2, h3) would be equivalent and
353
+ carries identical specificity, so collapsing them is safe whenever this
354
+ rule is next touched. */
355
+ .section > h2:first-child + table,
356
+ .section > h3:first-child + table {
357
+ margin-top: var(--space-sm);
358
+ }
359
+
360
+ /* Column-break orphan protection: keep first paragraph with the spanning bar.
361
+ `break-after: avoid` on the bar guards page breaks; these guards also cover
362
+ column-break orphaning that can strand a bar above an empty column. */
363
+ .section.dc-column-panel > h2:first-child + *,
364
+ .section.dc-column-panel > h3:first-child + * {
365
+ break-before: avoid;
366
+ }
367
+
368
+ /* Span the intro paragraph across both columns so it reads as a shared
369
+ preamble rather than being trapped in column 1. */
370
+ .section.dc-column-panel > h2:first-child + p,
371
+ .section.dc-column-panel > h3:first-child + p {
372
+ column-span: all;
373
+ break-inside: avoid;
374
+ }
375
+
376
+ /* Multi-column body copy: kill rivers from narrow-column justification. */
377
+ .section.dc-column-panel p {
378
+ text-align: left;
379
+ }
380
+
381
+ /* Section sub-heading H3 — H3s inside a section that are NOT the first child
382
+ (i.e. NOT the section's header bar) get a left-accent border matching the
383
+ section accent. This visually distinguishes subsection headings from the
384
+ section title bar. Universal across all chapters. */
385
+ .section > h3:not(:first-child) {
386
+ border-left: 3px solid var(--dc-section-accent);
387
+ padding-left: var(--space-sm);
388
+ }
389
+
390
+ /* ── A7: free-standing heading directly before a section card — add extra
391
+ top clearance so the page-level heading and the card's header bar don't
392
+ compete at the same visual weight on the vertical scan. */
393
+ h2 + .section,
394
+ h3 + .section {
395
+ margin-top: 20pt;
396
+ }
397
+
398
+ /* ── A1 + P1: H3 tabbed sections use ink-dark background (secondary/subordinate
399
+ vs chapter-accent H2 tabs). Same chipped-stone structure as H2 but with
400
+ notch depths scaled ~70% for the smaller tab height. */
401
+ .section.dc-tabbed > h3:first-child {
402
+ background: var(--ink-dark);
403
+ clip-path: polygon(
404
+ 10px 0,
405
+ calc(25% - 2px) 0, 25% 3px, calc(25% + 2px) 0,
406
+ calc(100% - 16px) 0,
407
+ calc(100% - 6px) 27%,
408
+ calc(100% - 9px) 38%,
409
+ calc(100% - 4px) 48%,
410
+ calc(100% - 8px) 58%,
411
+ calc(100% - 3px) 68%,
412
+ 100% 100%,
413
+ 14px 100%,
414
+ 4px calc(100% - 4px),
415
+ 0 calc(100% - 8px),
416
+ 2px 48%, 0 52%, 2px 56%,
417
+ 0 10px
418
+ );
419
+ }
420
+
421
+ /* ═══════════════════════════════════════════════════════════════════════════
422
+ SECTION ENCLOSURES — dc-block family
423
+ ═══════════════════════════════════════════════════════════════════════════
424
+ Reusable card-like enclosures for text content sections. Each variant has
425
+ a distinct clip-path geometry, surface color, and accent treatment that
426
+ signal different content registers.
427
+
428
+ Plugin macro API (plugin.js):
429
+ @block .dc-panel|.dc-slate|.dc-shard|.dc-codex label="Title" … @end-block
430
+
431
+ CSS class API (direct use without plugin):
432
+ <div class="dc-block dc-panel"><div class="dc-block-title">…</div>…</div>
433
+
434
+ Public token API (override at usage site to create variants):
435
+ --dc-block-bg → body surface color
436
+ --dc-block-fg → body text color
437
+ --dc-block-accent → left-strip accent / border highlight
438
+ --dc-block-title-bg → title band background
439
+ --dc-block-title-fg → title band text
440
+ ─────────────────────────────────────────────────────────────────────── */
441
+
442
+ /* ── Base shell ──────────────────────────────────────────────────────────── */
443
+ /* Token defaults for .dc-block are declared in :root in dc-component-defaults.css.
444
+ Override via variant rules (.dc-block.dc-panel etc.) below. */
445
+ .dc-block {
446
+ background: var(--dc-block-bg);
447
+ color: var(--dc-block-fg);
448
+ padding: 0 var(--space-lg) var(--space-lg);
449
+ margin: 0.15in 0;
450
+ position: relative;
451
+ break-inside: avoid;
452
+ }
453
+
454
+ /* Title band — bleeds to horizontal edges of the container */
455
+ .dc-block-title {
456
+ font-family: var(--font-display);
457
+ font-size: var(--fs-h4);
458
+ font-weight: var(--fw-bold);
459
+ letter-spacing: var(--ls-heading);
460
+ text-transform: uppercase;
461
+ line-height: 1.3;
462
+ color: var(--dc-block-title-fg);
463
+ background: var(--dc-block-title-bg);
464
+ padding: 7px 18px 6px;
465
+ margin: 0 calc(-1 * var(--space-lg)) var(--space-md) calc(-1 * var(--space-lg));
466
+ display: block;
467
+ border: none;
468
+ }
469
+
470
+ /* Content children inherit fg color (overrides dc-core.css heading defaults) */
471
+ .dc-block h3,
472
+ .dc-block h4,
473
+ .dc-block p,
474
+ .dc-block li {
475
+ color: var(--dc-block-fg);
476
+ }
477
+
478
+ .dc-block > p:last-child,
479
+ .dc-block > ul:last-child,
480
+ .dc-block > ol:last-child {
481
+ margin-bottom: 0;
482
+ }
483
+
484
+ /* ── Variant: Panel — military/HUD tactical container ────────────────────── */
485
+ /* Geometry: hex corner cuts top-left + bottom-right. Left accent strip.
486
+ Signal: structured information, mission briefings, system data. */
487
+ .dc-block.dc-panel {
488
+ --dc-block-accent: var(--hud-blue-dark);
489
+ --dc-block-title-bg: var(--hud-blue-dark);
490
+ background:
491
+ linear-gradient(to right, var(--dc-block-accent) 3px, var(--paper-light) 3px);
492
+ clip-path: polygon(
493
+ 14px 0, 100% 0,
494
+ 100% calc(100% - 14px), calc(100% - 14px) 100%,
495
+ 0 100%, 0 14px
496
+ );
497
+ }
498
+ .dc-block.dc-panel .dc-block-title {
499
+ clip-path: polygon(0 0, calc(100% - 22px) 0, 100% 100%, 0 100%);
500
+ }
501
+
502
+ /* ── Variant: Slate — dark authority block ───────────────────────────────── */
503
+ /* Geometry: top-left step cut. Dark surface with magenta accent.
504
+ Signal: key rules, Dream Master directives, important definitions. */
505
+ .dc-block.dc-slate {
506
+ --dc-block-bg: var(--ink-dark);
507
+ --dc-block-fg: var(--paper-cream);
508
+ --dc-block-accent: var(--hud-magenta);
509
+ --dc-block-title-bg: var(--hud-magenta);
510
+ --dc-block-title-fg: var(--paper-cream);
511
+ clip-path: polygon(
512
+ 20px 0, 100% 0,
513
+ 100% 100%, 0 100%, 0 20px
514
+ );
515
+ }
516
+ .dc-block.dc-slate .dc-block-title {
517
+ clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
518
+ }
519
+
520
+ /* ── Variant: Shard — zine/punk asymmetric slash ─────────────────────────── */
521
+ /* Geometry: diagonal bottom-right cut, left accent strip.
522
+ Signal: flavor text, atmospheric descriptions, setting detail. */
523
+ .dc-block.dc-shard {
524
+ --dc-block-bg: var(--paper-aged);
525
+ --dc-block-fg: var(--ink);
526
+ --dc-block-accent: var(--rust);
527
+ --dc-block-title-bg: var(--rust);
528
+ --dc-block-title-fg: var(--paper-cream);
529
+ background:
530
+ linear-gradient(to right, var(--dc-block-accent) 4px, var(--paper-aged) 4px);
531
+ clip-path: polygon(
532
+ 0 0, 100% 0,
533
+ 100% calc(100% - 28px), calc(100% - 52px) 100%,
534
+ 0 100%
535
+ );
536
+ }
537
+ .dc-block.dc-shard .dc-block-title {
538
+ clip-path: polygon(0 0, 100% 0, 100% 100%, 16px 100%, 0 calc(100% - 14px));
539
+ }
540
+
541
+ /* ── Variant: Codex — reference/data entry ───────────────────────────────── */
542
+ /* Geometry: symmetric octagon corner cuts. HUD-blue surface.
543
+ Signal: tables, reference data, rules lookups, compendium entries. */
544
+ .dc-block.dc-codex {
545
+ --dc-block-bg: var(--hud-blue-dim);
546
+ --dc-block-fg: var(--ink);
547
+ --dc-block-accent: var(--hud-blue-dark);
548
+ --dc-block-title-bg: var(--hud-blue-dark);
549
+ --dc-block-title-fg: var(--paper-cream);
550
+ clip-path: polygon(
551
+ 8px 0, calc(100% - 8px) 0, 100% 8px,
552
+ 100% calc(100% - 8px), calc(100% - 8px) 100%,
553
+ 8px 100%, 0 calc(100% - 8px), 0 8px
554
+ );
555
+ }
556
+ .dc-block.dc-codex .dc-block-title {
557
+ clip-path: polygon(
558
+ 0 0, calc(100% - 8px) 0, 100% 8px,
559
+ 100% 100%, 0 100%
560
+ );
561
+ }
562
+
563
+ /* ═════════════════════════════════════════════════════════════════════════
564
+ CARD PRIMITIVE — dc-card family
565
+ ═════════════════════════════════════════════════════════════════════════
566
+ Generic structured card. Emits .dc-card with optional sub-elements:
567
+ .dc-card-heading — card title (from first h4)
568
+ .dc-card-pull — pull-quote or flavor intro (first blockquote)
569
+ .dc-card-body — remaining content container
570
+ Footer: the last blockquote in .dc-card-body; no wrapper div emitted.
571
+ The plugin tags it explicitly as .dc-card-footer (dc#44) as it streams
572
+ past each body blockquote in turn, with no lookahead — it can't know in
573
+ advance which one is last, so it moves the tag forward onto each new one
574
+ and lets it land on whichever is open when the card closes. The
575
+ `blockquote:last-of-type` selector below stays as a fallback for one
576
+ release; once nothing depends on it, delete it and keep the class alone.
577
+
578
+ Plugin macro:
579
+ @card .dc-flaws … @end-card
580
+
581
+ IMPORTANT: .dc-skill-card and .dc-card are different outer classes.
582
+ Sub-elements (.dc-card-body etc.) are always scoped to their outer parent:
583
+ .dc-skill-card .dc-card-body → skill card body (existing rules)
584
+ .dc-card > .dc-card-body → card primitive body (this section)
585
+ No selector conflict. */
586
+
587
+ .dc-card {
588
+ background: var(--paper-light);
589
+ border-left: 3px solid var(--ink-dark);
590
+ padding: var(--space-sm) var(--space-md) var(--space-md);
591
+ margin: var(--space-sm) 0;
592
+ break-inside: avoid;
593
+ }
594
+
595
+ .dc-card-heading {
596
+ font-family: var(--font-display);
597
+ font-size: var(--fs-h4);
598
+ font-weight: var(--fw-bold);
599
+ text-transform: uppercase;
600
+ letter-spacing: var(--ls-heading);
601
+ margin-bottom: var(--space-xs);
602
+ color: var(--ink-dark);
603
+ }
604
+
605
+ .dc-card-pull {
606
+ font-style: italic;
607
+ color: var(--ink-mid);
608
+ margin-bottom: var(--space-xs);
609
+ font-size: var(--fs-sm);
610
+ }
611
+
612
+ .dc-card > .dc-card-body {
613
+ margin-top: var(--space-xs);
614
+ }
615
+
616
+ .dc-card > .dc-card-body > p {
617
+ margin-bottom: var(--space-xs);
618
+ }
619
+
620
+ .dc-card > .dc-card-body > p:last-child {
621
+ margin-bottom: 0;
622
+ }
623
+
624
+ /* Footer: last blockquote in card body. `.dc-card-footer` (plugin-tagged,
625
+ dc#44) is the real contract; `blockquote:last-of-type` is kept alongside
626
+ it for one release as a fallback for markup the plugin didn't generate,
627
+ per the same rule. Both always resolve to the same element today — the
628
+ plugin only ever tags the blockquote that is, in fact, last. */
629
+ .dc-card > .dc-card-body blockquote:last-of-type,
630
+ .dc-card > .dc-card-body .dc-card-footer {
631
+ border-left: none;
632
+ padding-left: 0;
633
+ margin-top: var(--space-xs);
634
+ font-size: var(--fs-sm);
635
+ color: var(--ink-mid);
636
+ font-style: italic;
637
+ }
638
+
639
+ /* ── Card list contexts (components/*.css owns these — NOT the book's own sheet) ── */
640
+
641
+ /* Flaw cards */
642
+ .section.dc-flaws > .dc-card {
643
+ border-left-color: var(--blood);
644
+ }
645
+
646
+ /* Ideal cards */
647
+ .section.dc-ideals > .dc-card {
648
+ border-left-color: var(--hud-blue-dark);
649
+ }
650
+
651
+ /* Dream cards */
652
+ .section.dc-dreams > .dc-card {
653
+ border-left-color: var(--augmerc-accent);
654
+ }
655
+
656
+ /* ═════════════════════════════════════════════════════════════════════════
657
+ CITIZEN-WALKTHROUGH — guide for filling out the Citizen File
658
+ ═════════════════════════════════════════════════════════════════════════
659
+ The chapter that walks a reader through each Citizen File field. NOT
660
+ the filled-form artifact (that is a future .dc-citizen-file component
661
+ on a separate page).
662
+
663
+ Applied via:
664
+ @page .dc-citizen-file-page ← topic page (running header)
665
+ @section .dc-citizen-walkthrough ← standard section chassis
666
+
667
+ STRIP-FIRST POLICY: this component carries minimal bespoke CSS.
668
+ Styling comes primarily from the standard .section chassis (clipped-
669
+ corner ::before with --dc-section-accent stripe + --dc-section-bg
670
+ substrate + ::after fake shadow) plus the dc-core.css element baselines.
671
+ Rules below address observed problems that the chassis does not cover.
672
+
673
+ To tune the walkthrough's accent or substrate, override the existing
674
+ --dc-section-accent / --dc-section-bg custom properties at the chapter
675
+ id scope in the book's own sheet (`@layer book`) — do NOT add bespoke rules here unless an
676
+ observed problem requires it.
677
+
678
+ OBSERVED PROBLEM (2026-05-25): When the walkthrough's first card uses
679
+ `.gp-columns-2` for an internal Handle/Designation split, the longer h4
680
+ text ("What's Yr Handle?") wraps to 2 lines while shorter text
681
+ ("Designation") fits on 1 line at the narrow column width. The body
682
+ text below each h4 then starts at different baselines across columns
683
+ — visible asymmetry the user-eye reads as a production error. Fix:
684
+ reserve uniform heading height inside two-column walkthrough cards so
685
+ both columns' body content starts at the same y. Keyed on the COUNT
686
+ class, not `.dc-column-panel`: the asymmetry is caused by the narrow
687
+ column measure, not by the card chrome. */
688
+ .dc-citizen-walkthrough.gp-columns-2 h4 {
689
+ min-height: calc(2 * 1.35em);
690
+ }
691
+
692
+ /* ═════════════════════════════════════════════════════════════════════════
693
+ NPC STAT BLOCK — DM/NPC entries (Patchhead, Grease Monkey, Undertow, ...)
694
+ ═════════════════════════════════════════════════════════════════════════
695
+ Apply via @section .dc-npc-stat in markdown. Inside the wrapper:
696
+ #### Name → labeled NPC entry (rust top-rule)
697
+ > "flavor quote"
698
+ 2 HP — 1 Damage → primary stat line (mono, hud-blue)
699
+ Fodder — Medium → secondary stat line (mono, ink-smoke)
700
+ ##### Traits → subsection label (mono caps, hud-blue)
701
+ ##### Equipment → ditto
702
+ Reusable: any DC project can use .dc-npc-stat for stat-block authoring.
703
+
704
+ AUTHOR-FACING CONTRACT (dc#44): this is a markdown-CONVENTION component,
705
+ not a macro — @section is core Gutterpress's generic wrapper, and nothing
706
+ in plugin.js knows about NPC stat blocks specifically. The
707
+ "primary" and "secondary" stat lines below are selected POSITIONALLY —
708
+ `blockquote + p` and `blockquote + p + p` — by counting the plain
709
+ paragraphs that immediately follow the flavor quote. There is no macro
710
+ here to teach the difference between "the first paragraph after the
711
+ quote" and "any other paragraph," so the convention above IS the API:
712
+ write exactly one stat-line paragraph, then exactly one more, directly
713
+ after the `>` quote, with nothing else between them. An extra paragraph,
714
+ a blank aside, or reordering those two lines silently reassigns which
715
+ line prints as primary vs. secondary — the build gives no warning. */
716
+
717
+ /* Token contract for .dc-npc-stat:
718
+ --dc-npc-stat-rule → name top-rule color (default --rust)
719
+ --dc-npc-stat-rule-width → name top-rule weight (default 2pt, was 1.5pt — hairline risk on press)
720
+ --dc-npc-stat-label-color → h5 subsection-label color (default --hud-blue-dark)
721
+ --dc-npc-stat-primary → primary stat-line color, e.g. "2 HP — 1 Damage" (default --hud-blue)
722
+ --dc-npc-stat-secondary → secondary stat-line color (default --ink-smoke)
723
+ Override per chapter / per faction in the book's own sheet selector chains. */
724
+ .dc-npc-stat h4 {
725
+ border-top: var(--dc-npc-stat-rule-width) solid var(--dc-npc-stat-rule);
726
+ padding-top: 0.05in;
727
+ margin-top: 0.1in;
728
+ }
729
+
730
+ .dc-npc-stat h5 {
731
+ /* TRAITS / EQUIPMENT / CYBERNETICS section-anchor labels — must read
732
+ as CATEGORICALLY DIFFERENT from body text, not just heavier (contest
733
+ judge R10: "they feel labelled rather than anchored... mono-caps +
734
+ accent underline + left-border accent pip would close the PHB gap").
735
+ Triple-cue treatment: bold mono-caps tracked + 1pt accent underline
736
+ + 3pt accent left-border pip. The pip is the anchor — scannable at
737
+ reading distance, unmistakable as a section start. */
738
+ font-family: var(--font-mono);
739
+ font-size: var(--fs-h6);
740
+ font-weight: 700;
741
+ letter-spacing: 0.14em;
742
+ text-transform: uppercase;
743
+ color: var(--dc-npc-stat-label-color);
744
+ border-left: 3pt solid var(--dc-npc-stat-label-color);
745
+ border-bottom: 1pt solid var(--dc-npc-stat-label-color);
746
+ padding: 1pt 0 1.5pt 6pt;
747
+ margin-top: 0.08in;
748
+ margin-bottom: 0.04in;
749
+ }
750
+
751
+ /* Primary stat line — "2 HP — 1 Damage" sits directly after the flavor quote. */
752
+ .dc-npc-stat blockquote + p {
753
+ font-family: var(--font-mono);
754
+ font-size: 8pt;
755
+ /* --hud-blue (#1f6f94) on wall = 3.24:1 (fails AA). --hud-blue-dark
756
+ (#14516e) gives ~5.5:1 on wall and 7.45:1 on paper-cream. */
757
+ color: var(--dc-npc-stat-primary);
758
+ letter-spacing: 0.04em;
759
+ margin-bottom: 0.02in;
760
+ }
761
+
762
+ /* Secondary stat line — "Fodder — Medium" type+size. Carries the
763
+ stat-summary divider: a dotted ink hairline AFTER this line and
764
+ BEFORE the TRAITS h5 creates the visible skeleton
765
+ (header → flavor quote → meta-stats → SEPARATOR → ability sections)
766
+ per contest judge R9 finding. */
767
+ .dc-npc-stat blockquote + p + p {
768
+ font-family: var(--font-mono);
769
+ font-size: 7.5pt;
770
+ color: var(--dc-npc-stat-secondary);
771
+ letter-spacing: 0.04em;
772
+ border-bottom: 0.75pt dotted var(--ink-dust);
773
+ padding-bottom: 0.05in;
774
+ margin-bottom: 0.06in;
775
+ }
776
+
777
+
778
+ /* ═════════════════════════════════════════════════════════════════════════
779
+ CARD-GRID — compact specialty-card display in a 2-column layout
780
+ ═════════════════════════════════════════════════════════════════════════
781
+ Apply via section class — same architectural split as .dc-citizen-walkthrough:
782
+ the @page directive identifies the page template, the @section provides
783
+ the layout + component-tweak context.
784
+
785
+ @page .card-grid
786
+ @section .dc-card-grid
787
+ ## Choose Your Specialty
788
+ @lede ... @end-lede
789
+ @specialty .augmerc
790
+ @specialty-card ... @end-specialty-card
791
+ @end-specialty
792
+ [repeat for 6+ specialties]
793
+ @end-section
794
+
795
+ The column-flow rules (columns:2, column-span:all) live in page-templates.css
796
+ per the architectural contract. Here we own only the chrome-reset (the
797
+ .dc-card-grid section has no card background/shadow — it's a pure layout
798
+ wrapper) plus the per-card density tweaks so all 8 specialty cards fit
799
+ comfortably on one page. */
800
+
801
+ /* Chrome reset — strip .section's card visuals so the wrapper is invisible */
802
+ .section.dc-card-grid {
803
+ background: transparent;
804
+ padding: 0;
805
+ margin: var(--space-md) 0 0;
806
+ /* Explicit override of .section default (break-inside: avoid) — the grid is a
807
+ layout wrapper, not a cohesive content block, and may span across pages
808
+ when the card count exceeds what fits on a single page. */
809
+ break-inside: auto;
810
+ filter: none;
811
+ }
812
+
813
+ /* `.page.dc-chapter-start.two-column`'s ornamental `column-rule` was DELETED
814
+ before this migration: it needed a page carrying BOTH classes and no book's
815
+ markdown has ever written one. The `column-rule` that IS live sits on
816
+ `.gp-columns-2` in components/*.css — an ornament, not a layout rule. */
817
+
818
+ /* ── LAYOUT: CARD GRID (.dc-card-grid section) ─────────────────────────────── */
819
+ /* Specialty card grid — two columns, auto-fill. Layout owns columns + column-
820
+ span here; component styling (chrome reset, card density, break control)
821
+ lives in components/*.css under the same .dc-card-grid selector. */
822
+ .dc-card-grid {
823
+ columns: 2;
824
+ column-gap: var(--gutter);
825
+ column-fill: balance;
826
+ }
827
+ /* `.section.dc-card-grid::before/::after { content: none }` was here and is now
828
+ part of the single `.dc-plain` escape rule above, beside the chrome it
829
+ cancels (dc#39). */
830
+
831
+ /* The generic `.section > h2:first-child` bar uses negative margins sized to
832
+ cancel .section's padding so the bar sits flush with the card edge. This
833
+ wrapper zeroes that padding (above), so those negative margins have nothing
834
+ to cancel and the bar overhangs the card by the full --space-lg/--space-md
835
+ on each side — pushing it outside the 0.5in print safe area. Zero them so
836
+ the bar aligns with the grid it heads. */
837
+ .section.dc-card-grid > h2:first-child,
838
+ .section.dc-card-grid > h3:first-child {
839
+ margin-top: 0;
840
+ margin-left: 0;
841
+ margin-right: 0;
842
+ }
843
+
844
+ /* Header + intro stay close to the cards below (no orphan break) */
845
+ .dc-card-grid > h2 { break-after: avoid; }
846
+ .dc-card-grid > .dc-intro {
847
+ break-after: avoid;
848
+ padding: 5px 12px 5px 16px;
849
+ margin: 0 0 var(--space-md);
850
+ }
851
+ .dc-card-grid .dc-specialty:first-child { break-before: avoid; }
852
+
853
+ /* Compact specialty card density: 10 cards on one page in 2 columns */
854
+ .dc-card-grid .dc-specialty-card {
855
+ --dc-specialty-card-media-height: 0.42in;
856
+ --dc-specialty-card-media-width: 0.38in;
857
+ padding: var(--space-sm);
858
+ margin: 0;
859
+ }
860
+ .dc-card-grid .dc-specialty-card img { margin-bottom: 0; }
861
+ .dc-card-grid .dc-specialty-card p { line-height: 1.2; font-size: 8pt; }
862
+ .dc-card-grid .dc-specialty-card h3 { font-size: 0.95em; margin: 0; padding-top: 0; line-height: 1.3; }
863
+ .dc-card-grid .dc-specialty-card blockquote { font-size: 0.85em; margin: 0; line-height: 1.3; }
864
+
865
+
866
+
867
+
868
+ /* Per-book variant assignment lives in the book's own sheet (`@layer book`) using natural
869
+ selector chains (chapter id → page template class → section component
870
+ class) that set component CSS custom properties. No utility-variant
871
+ classes here — that would force authors to re-type variant intent. */
872
+
873
+
874
+ /* ═════════════════════════════════════════════════════════════════════════
875
+ CHAPTER OPENER — composite of badge + intro section as one component
876
+ ═════════════════════════════════════════════════════════════════════════
877
+
878
+ Pure CSS reaction to the structural DOM produced by Gutterpress's marker
879
+ parser (markers.js). No plugin transformation, no hardcoded class
880
+ injection. The author's markdown is:
881
+
882
+ @chapter C.01
883
+ @page intro
884
+ @section
885
+ # Who Do You Dream to Be?
886
+
887
+ ...which produces the natural DOM:
888
+
889
+ .chapter[data-chapter-label="C.01"]
890
+ .page[data-page="intro"]
891
+ .section
892
+ h1, p, p, ...
893
+
894
+ Every visual aspect of the chapter-opener composite is selected via
895
+ attribute selectors that match THIS structure: the C.NN badge, the
896
+ chevron-styled h1, the section's substrate, padding, and clip path.
897
+ Any project built on Gutterpress's markers can write its own attribute-
898
+ selector CSS to react to the same data-chapter-label / data-page="intro"
899
+ structure without coupling to DC-specific class names.
900
+
901
+ Local alias for the matched composite (a CSS-only nickname; not a
902
+ class anyone has to author): the structural selector
903
+ `.chapter[data-chapter-label] > .page[data-page="intro"] > .section`
904
+ is the "chapter-opener intro section." The CSS below targets that
905
+ selector chain through a small set of rules.
906
+
907
+ Token contract — override at the chapter id scope for per-chapter
908
+ variants. Authors don't write these in markdown; they're project CSS
909
+ overrides. Defaults live in dc-component-defaults.css; example per-chapter
910
+ override (NOT nested CSS comments — these are illustrative only):
911
+
912
+ #ch-example-specialty-overview .chapter {
913
+ --dc-chapter-opener-accent: var(--augmerc-deep);
914
+ --dc-chapter-opener-clip-tail: 32px;
915
+ }
916
+
917
+ ───────────────────────────────────────────────────────────────────── */
918
+
919
+ /* Token defaults for this component live in dc-component-defaults.css alongside the
920
+ space / color / clip primitives they reference — one declaration site for
921
+ the whole override surface. */
922
+
923
+ /* Chapter-opener badge — styled on the .chapter-opener element that
924
+ Gutterpress's marker parser emits as the first child of a labeled
925
+ chapter's first page (see openPage in markers.js). The element carries
926
+ the chapter label as both its text content and a data attribute. */
927
+ .chapter-opener {
928
+ display: block;
929
+ font-family: var(--font-display);
930
+ font-size: calc(var(--fs-chapter) * 1.35);
931
+ letter-spacing: 0.18em;
932
+ text-transform: uppercase;
933
+ color: var(--dc-chapter-opener-accent);
934
+ background: var(--ink-dark);
935
+ line-height: 1;
936
+ padding: 0.22in 0.18in 0.14in;
937
+ margin: 0;
938
+ width: 100%;
939
+ box-sizing: border-box;
940
+ border-top: 4pt solid var(--dc-chapter-opener-accent);
941
+ }
942
+
943
+ /* Cohesive drop shadow for the entire chapter-opener composite.
944
+ `filter: drop-shadow()` follows the union of the page's visible child
945
+ silhouettes — badge, tapered chevron, clipped section — and renders
946
+ ONE continuous shadow that looks like the composite is pinned to the
947
+ wall and casting a single offset. Applied on the page wrapper rather
948
+ than per-element so the diagonal join between chevron's tapered right
949
+ edge and the section's vertical right edge is preserved in the shadow
950
+ shape. */
951
+ .chapter[data-chapter-label] > .page[data-page="intro"] {
952
+ filter: drop-shadow(var(--dc-chapter-opener-shadow));
953
+ }
954
+
955
+ /* Section substrate for the chapter-opener intro section. Selector
956
+ matches a .section that's an immediate child of a chapter-opener intro
957
+ page (page named "intro" inside a chapter with a label). */
958
+ .chapter[data-chapter-label] > .page[data-page="intro"] > .section {
959
+ /* Right padding (--pad-right = clip-tail + space-md) is larger than
960
+ the visual clip-tail so body text has breathing room from the
961
+ clipped substrate edge. */
962
+ padding: var(--dc-chapter-opener-pad-top)
963
+ var(--dc-chapter-opener-pad-right)
964
+ var(--dc-chapter-opener-pad-top)
965
+ var(--dc-chapter-opener-pad-side);
966
+
967
+ /* Feed the existing section accent / bg surfaces from the
968
+ chapter-opener tokens so per-chapter overrides flow through. */
969
+ --dc-section-accent: var(--dc-chapter-opener-accent);
970
+ --dc-section-bg: var(--dc-chapter-opener-bg);
971
+
972
+ /* Propagate the chapter-opener's clip-tail into --clip-tail so any
973
+ .dc-chevron descendant's --clip-banner polygon tapers to the same
974
+ x position as this section's right edge. */
975
+ --clip-tail: var(--dc-chapter-opener-clip-tail);
976
+ }
977
+
978
+ /* ::before's right edge is straight-vertical at the clip-tail x. The
979
+ chevron above tapers from full-width to this same x, so the silhouette
980
+ transitions diagonal → vertical with no horizontal step at the chevron/
981
+ section boundary. */
982
+ .chapter[data-chapter-label] > .page[data-page="intro"] > .section::before {
983
+ clip-path: polygon(
984
+ 0 0,
985
+ calc(100% - var(--dc-chapter-opener-clip-tail)) 0,
986
+ calc(100% - var(--dc-chapter-opener-clip-tail)) 100%,
987
+ 0 100%
988
+ );
989
+ }
990
+
991
+ /* Suppress the .section::after fake-shadow on the chapter-opener composite:
992
+ the chapter badge above already provides the upper visual weight, and a
993
+ vector shadow offset would visually clip at the indented right edge. */
994
+ .chapter[data-chapter-label] > .page[data-page="intro"] > .section::after {
995
+ content: none;
996
+ }
997
+
998
+ /* Auto-style the section's first h1 as a chevron banner — no markdown
999
+ `{.dc-chevron}` attribute required. The chevron banner extends to the
1000
+ section's outer border-box edges (card-header pattern) by claiming back
1001
+ the section's padding via negative margins. */
1002
+ .chapter[data-chapter-label] > .page[data-page="intro"] > .section > h1:first-of-type {
1003
+ /* Negative margins claim back the section's padding so the chevron
1004
+ banner extends to the section's outer border-box edges (card-header
1005
+ pattern). Margin-right uses --pad-right (the same value that pads
1006
+ body content) so the chevron's outer right edge aligns with the
1007
+ section's outer right edge — its --clip-banner tail then tapers
1008
+ in by --clip-tail, matching where the section substrate ends. */
1009
+ margin-top: calc(var(--dc-chapter-opener-pad-top) * -1);
1010
+ margin-left: calc(var(--dc-chapter-opener-pad-side) * -1);
1011
+ margin-right: calc(var(--dc-chapter-opener-pad-right) * -1);
1012
+ --dc-chevron-bg: var(--dc-chapter-opener-accent);
1013
+ }
1014
+
1015
+
1016
+ /* ═════════════════════════════════════════════════════════════════════════
1017
+ FICTION EXCERPT — narrative prose with optional inline art
1018
+ ═════════════════════════════════════════════════════════════════════════
1019
+ Wraps a fiction section (chapter opener prose, vignette, in-character
1020
+ narrative). Provides baseline narrative typography, break control, and
1021
+ automatic image positioning (first image floats left, subsequent floats
1022
+ right) — authors don't need per-image positioning classes.
1023
+
1024
+ @section .dc-fiction-excerpt
1025
+ "It's hard being me, but I guess..."
1026
+ ![Lil Thump](url)
1027
+ The cauldron wuz right on mai heels now...
1028
+ @end-section
1029
+
1030
+ Variants come from chapter/page context — e.g. a specific chapter could
1031
+ set --dc-fiction-bg to a tinted paper, or a page-template could constrain
1032
+ art width differently. */
1033
+
1034
+ /* Token defaults for .section.dc-fiction-excerpt (--dc-fiction-bg,
1035
+ --dc-fiction-art-width/-shadow/-outline, --dc-fiction-line-height) are
1036
+ declared in :root in dc-component-defaults.css — same values as before this moved
1037
+ (the 3in art width: user feedback said the previous 1.85in "Lil Thump
1038
+ placeholder image is too small... should take up more of the left side
1039
+ of the page"). Per-page/per-chapter variants override via the book's own sheet (`@layer book`)
1040
+ context selectors, e.g. `.page....chapter-01 .section.dc-fiction-excerpt`. */
1041
+ .section.dc-fiction-excerpt {
1042
+ background: var(--dc-fiction-bg);
1043
+ line-height: var(--dc-fiction-line-height);
1044
+ }
1045
+
1046
+ .section.dc-fiction-excerpt p {
1047
+ orphans: 3;
1048
+ widows: 3;
1049
+ }
1050
+
1051
+ /* Images inside the excerpt float by default (--dc-fiction-art-float; left
1052
+ by default, set to right on a specific page/chapter via the book's own sheet if
1053
+ needed). Text wraps to the opposite side. Author places the image at the
1054
+ start of the paragraph flow — no per-image class required. */
1055
+ .section.dc-fiction-excerpt img {
1056
+ float: var(--dc-fiction-art-float);
1057
+ width: var(--dc-fiction-art-width);
1058
+ height: auto;
1059
+ max-height: 2.25in;
1060
+ object-fit: cover;
1061
+ margin: 0 var(--space-md) var(--space-sm) 0;
1062
+ outline: var(--dc-fiction-art-outline);
1063
+ box-shadow: var(--dc-fiction-art-shadow);
1064
+ }
1065
+
1066
+
1067
+ /* `.section.dc-rules-definition` (this chrome/tokens/doc block + the
1068
+ columns block in page-templates.css) is DELETED (2026-09-02): 0 uses,
1069
+ 0 emissions — verified against both books' rendered HTML before
1070
+ deleting. A future component demonstrating this "two opposed
1071
+ definitions" shape should get one real usage in the guide's gallery so
1072
+ it stays alive and checkable, rather than being reintroduced dark. */
1073
+
1074
+ }