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.
- package/CHANGELOG.md +86 -0
- package/LICENSE +32 -0
- package/README.md +165 -0
- package/components.yaml +832 -0
- package/fonts/lixdu/LICENSE.txt +34 -0
- package/fonts/lixdu/lixdu.woff +0 -0
- package/fonts/titillium-web/OFL.txt +93 -0
- package/fonts/titillium-web/titillium-web-400-italic.ttf +0 -0
- package/fonts/titillium-web/titillium-web-400.ttf +0 -0
- package/fonts/titillium-web/titillium-web-600-italic.ttf +0 -0
- package/fonts/titillium-web/titillium-web-600.ttf +0 -0
- package/fonts/titillium-web/titillium-web-700-italic.ttf +0 -0
- package/fonts/titillium-web/titillium-web-700.ttf +0 -0
- package/fonts/tomorrow/OFL.txt +93 -0
- package/fonts/tomorrow/tomorrow-400-italic.ttf +0 -0
- package/fonts/tomorrow/tomorrow-400.ttf +0 -0
- package/fonts/tomorrow/tomorrow-500-italic.ttf +0 -0
- package/fonts/tomorrow/tomorrow-500.ttf +0 -0
- package/fonts/tomorrow/tomorrow-700-italic.ttf +0 -0
- package/fonts/tomorrow/tomorrow-700.ttf +0 -0
- package/images/brick-bg-01.png +0 -0
- package/package.json +78 -0
- package/plugin.js +2131 -0
- package/snippets/alert.md +2 -0
- package/snippets/block.md +5 -0
- package/snippets/callout.md +5 -0
- package/snippets/card.md +11 -0
- package/snippets/chapter-start-page.md +11 -0
- package/snippets/credits-page.md +13 -0
- package/snippets/definition.md +7 -0
- package/snippets/dm-note.md +5 -0
- package/snippets/gear.md +9 -0
- package/snippets/learning-path.md +10 -0
- package/snippets/lede.md +5 -0
- package/snippets/outcome.md +8 -0
- package/snippets/procedure.md +7 -0
- package/snippets/sidebar.md +7 -0
- package/snippets/skill.md +9 -0
- package/snippets/specialty-art.md +5 -0
- package/snippets/specialty-card.md +5 -0
- package/snippets/specialty.md +11 -0
- package/snippets/toc-page.md +15 -0
- package/styles/components/callouts.css +227 -0
- package/styles/components/cards.css +487 -0
- package/styles/components/chrome.css +483 -0
- package/styles/components/data.css +1026 -0
- package/styles/components/images.css +215 -0
- package/styles/components/section.css +1074 -0
- package/styles/components/specialty-identity.css +315 -0
- package/styles/components/specialty.css +273 -0
- package/styles/dc-component-defaults.css +214 -0
- package/styles/dc-core.css +268 -0
- package/styles/dc-fonts.css +107 -0
- package/styles/dc-identity.css +66 -0
- package/styles/dc-native.css +330 -0
- package/styles/dc-palette.css +198 -0
- package/styles/page-rules.css +207 -0
- 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
|
+

|
|
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
|
+
}
|