concept-atlas-dense-explain 0.8.0 → 0.10.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/bin/cli.mjs CHANGED
@@ -156,12 +156,13 @@ if (command === 'create' || command === 'new') {
156
156
  <Children><ConceptRef id="first-branch" /><ConceptRef id="second-branch" /></Children>
157
157
  </ConceptNode>
158
158
 
159
- <ConceptNode id="first-branch" title="第一条关键分支" level="L1" parent="root-node">
159
+ <ConceptNode id="first-branch" title="第一条关键分支" level="L1" parent="root-node" summary="第一条关键分支解决什么问题,以及它为什么值得先理解。">
160
160
  <Overview>解释第一个重要组成部分。</Overview>
161
161
  <Example title="典型例子">填写一个具体例子,帮助读者验证理解。</Example>
162
+ <CodeBlock language="bash" title="验证方式" code="npm run validate" lineNumbers />
162
163
  </ConceptNode>
163
164
 
164
- <ConceptNode id="second-branch" title="第二条关键分支" level="L1" parent="root-node">
165
+ <ConceptNode id="second-branch" title="第二条关键分支" level="L1" parent="root-node" summary="第二条关键分支解决什么问题,以及它和第一条分支如何配合。">
165
166
  <Overview>解释第二个重要组成部分。</Overview>
166
167
  <Boundary>填写它的边界、代价或常见误区。</Boundary>
167
168
  </ConceptNode>
@@ -182,6 +183,7 @@ if (command === 'create' || command === 'new') {
182
183
  <ScrollSection title="解释关键机制">
183
184
  <ScrollProse>按输入、步骤、输出的顺序解释过程,不要只罗列名词。</ScrollProse>
184
185
  <Flow title="处理流程" steps={[{title:'输入',description:'原始条件或数据'},{title:'处理',description:'关键变化或判断'},{title:'输出',description:'结果与可观察证据'}]} />
186
+ <CodeBlock language="bash" title="实践命令" code="npm run validate" caption="通用代码块可以放在任意章节;需要逐行讲解时加 lineNumbers。" />
185
187
  </ScrollSection>
186
188
 
187
189
  <ScrollSection title="边界与实践">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "concept-atlas-dense-explain",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Portable dense-explanation skill and MDX concept atlas template",
5
5
  "type": "module",
6
6
  "bin": {
package/skill/SKILL.md CHANGED
@@ -38,7 +38,7 @@ Drive everything through the `concept-atlas-dense-explain` npm CLI. This skill i
38
38
 
39
39
  ## Component families
40
40
 
41
- - Node semantics: `Overview`, `Definition`, `Mechanism`, `Implementation`, `Boundary`, `Example`, `Counterexample`, `Prerequisite`, `Input`, `Output`, `Glossary`
41
+ - Node semantics: `Overview`, `Definition`, `Mechanism`, `Implementation`, `CodeBlock`, `Boundary`, `Example`, `Counterexample`, `Prerequisite`, `Input`, `Output`, `Glossary`
42
42
  - Argument and evidence: `Evidence`, `Invariant`, `FailureMode`, `Tradeoff`, `LearningObjectives`, `KeyQuestion`
43
43
  - Information models: `Flow`, `Timeline`, `Compare`, `DecisionMatrix`, `FrameworkModel`, `MatrixModel`, `FormulaModel`, `PyramidModel`, `FunnelModel`
44
44
  - Reading and layout: `Insight`, `Callout`, `Details`, `NoteGrid`, `Tabs`, `Columns`, `Stack`, `Grid`, `Split`, `ScrollGrid`, `ScrollPair`, `ScrollToc`
@@ -52,6 +52,7 @@ Drive everything through the `concept-atlas-dense-explain` npm CLI. This skill i
52
52
  - Array props are arrays of objects: `Flow steps={[{title, description}]}`, `Timeline events={[{label, content}]}`, `MatrixModel cells={[{title, description, tone}]}`, `DecisionMatrix headers={[...]} rows={[[...]]}`, `Chart data={[{label, value}]}`, `References items={[{id, authors, year, title, url, source}]}`. The validator warns (`PROP_EXPECTS_ARRAY`) when an array prop gets a string or non-array.
53
53
  - `Relation type` must be one of `prerequisite`, `causes`, `produces`, `uses`, `implements`, `contrasts`, `depends-on`, `exception-of`, `precedes`, and each `Relation` needs a `label`. Parent/child hierarchy is implicit (via `parent` and `Children`/`ConceptRef`) — do not express it with a `Relation`.
54
54
  - **Math**: MDX parses `{ ... }` in children as expressions, so pass LaTeX with braces or backslashes through `formula`: `<Math formula="r_{\text{ann}} = (1 + r)^{12} - 1" />`, `<MathBlock formula="I(x) = -\log_2 p(x)" variables={[{symbol, description}]} />`. Brace-free children such as `<Math>\log_2 N</Math>` are fine. The validator warns (`MATH_CHILDREN_BRACES`).
55
+ - **CodeBlock**: a general code block usable anywhere; `Implementation` is the node-bound variant. Pass the code as a string via `code` (or as children) wrapped in a template literal so MDX does not read it as expressions: `<CodeBlock language="bash" title="..." lineNumbers>{`npm run validate`}</CodeBlock>`. `language` adds the badge, `title`/`caption` add labels, `lineNumbers` and `wrap` are booleans.
55
56
  - **Chart**: `type` is `bar` | `line` | `pie`; use `data` for bar/pie and `labels` + `series={[{name, values}]}` for line. Charts follow theme colors.
56
57
  - **Figure**: a relative `src` (`./assets/diagram.png`) is inlined as base64 at build time so the HTML stays standalone; `http(s)` URLs stay links. Always set `alt`; add `label` and `caption` for a numbered caption. A missing relative file warns (`ASSET_MISSING`) and shows a placeholder. Readers can click a figure to open it full-screen (zoom, drag, `Esc`) — mention it for diagram-heavy pages.
57
58
  - **Figure size & cost**: inlining images is what makes a screenshot-heavy page large; a page with no heavy renderers otherwise lands near 250KB. When the user cares, compile with `--link-assets` to keep images as relative links (measured 1.51MB → 270KB); the output must then sit beside the MDX's `assets/`, and the CLI warns if `-o` points elsewhere — tell the user that trade-off instead of choosing silently.
@@ -114,9 +114,9 @@
114
114
  <RelationMap title="可视化摘要" items={[{from:'MDX',type:'produces',to:'Graph',note:'生成关系图'},{from:'Node',type:'uses',to:'Mermaid',note:'展示局部流程'}]} />
115
115
  </ConceptNode>
116
116
 
117
- <ConceptNode id="extension-family" title="扩展能力:公式、图表、图片与引用" level="L1" parent="showroom" summary="数学公式、数据图表、配图题注和参考文献四项能力,让讲解可以带上推导、数据与出处。">
118
- <Definition>扩展组件把文本之外的证据接进页面:Math/MathBlock 渲染数学、Chart 绘制数据、Figure 承载配图、Cite/References 保留出处。</Definition>
119
- <Children><ConceptRef id="math-demo" /><ConceptRef id="chart-demo" /><ConceptRef id="figure-demo" /><ConceptRef id="citation-demo" /></Children>
117
+ <ConceptNode id="extension-family" title="扩展能力:公式、图表、图片、代码与引用" level="L1" parent="showroom" summary="数学公式、数据图表、配图题注、代码块和参考文献五项能力,让讲解可以带上推导、数据、实现与出处。">
118
+ <Definition>扩展组件把文本之外的证据接进页面:Math/MathBlock 渲染数学、Chart 绘制数据、Figure 承载配图、CodeBlock 展示代码或命令、Cite/References 保留出处。</Definition>
119
+ <Children><ConceptRef id="math-demo" /><ConceptRef id="chart-demo" /><ConceptRef id="figure-demo" /><ConceptRef id="code-demo" /><ConceptRef id="citation-demo" /></Children>
120
120
  </ConceptNode>
121
121
 
122
122
  <ConceptNode id="math-demo" title="Math 与 MathBlock:把推导写进正文" level="L2" parent="extension-family" summary="Math 用于行内符号,MathBlock 用于独立公式并支持变量说明。">
@@ -138,6 +138,15 @@
138
138
  <Boundary>大图会显著增大单文件 HTML;截图类内容建议控制尺寸。</Boundary>
139
139
  </ConceptNode>
140
140
 
141
+ <ConceptNode id="code-demo" title="CodeBlock:把命令、配置或伪代码放进正文" level="L2" parent="extension-family" summary="与绑定节点的 Implementation 不同,CodeBlock 可以出现在任何位置,并支持标题、题注、换行和行号。">
142
+ <Definition>CodeBlock 通过 code(或子内容)接收字符串,language 决定语言标记,title 与 caption 提供说明,lineNumbers 和 wrap 控制逐行讲解与长行折行。</Definition>
143
+ <CodeBlock language="bash" title="校验与构建" caption="命令行、伪代码或配置都可以用它承载,不依附于某个概念节点。" lineNumbers>{`npm run validate
144
+ npm run sync
145
+ npm run build`}</CodeBlock>
146
+ <CodeBlock language="javascript" title="语义优先" wrap>{`const explain = concept => concept.mechanism ?? concept.definition ?? concept.overview;`}</CodeBlock>
147
+ <Boundary>代码里的反引号、花括号请放在模板字符串中传入,避免被 MDX 当作表达式解析。</Boundary>
148
+ </ConceptNode>
149
+
141
150
  <ConceptNode id="citation-demo" title="Cite 与 References:让结论可以追溯" level="L2" parent="extension-family" summary="行内用 Cite 标记引用,文末用 References 列出完整出处,编号自动对应。">
142
151
  <Definition>Cite 的 id 与 References 条目的 id 对应,渲染时显示条目在列表中的序号,并链接到该条目。</Definition>
143
152
  <Overview>信息密度的价值在于可验证性<Cite id="shannon1948" />,而可验证性依赖清晰的出处<Cite id="tufte1983" />。</Overview>
@@ -42,6 +42,14 @@
42
42
  <Chart title="四周留存趋势" type="line" labels={['第1周','第2周','第3周','第4周']} series={[{name:'留存率',values:[100,72,58,49]},{name:'活跃度',values:[100,64,52,40]}]} />
43
43
  </ScrollSection>
44
44
 
45
+ <ScrollSection title="把命令与代码放进正文">
46
+ <ScrollProse>CodeBlock 承载命令行、配置或伪代码。它不依附于某个概念节点,可以出现在任何章节;需要逐行讲解时打开行号,长行过多时可以折行。</ScrollProse>
47
+ <CodeBlock language="bash" title="本地校验与预览" caption="CodeBlock 复用模板的代码表面,因此自动跟随当前皮肤与组件风格。" lineNumbers>{`npm run validate
48
+ npm run sync
49
+ npm run dev`}</CodeBlock>
50
+ <CodeBlock language="javascript" title="语义优先" wrap>{`const explain = concept => concept.mechanism ?? concept.definition ?? concept.overview;`}</CodeBlock>
51
+ </ScrollSection>
52
+
45
53
  <ScrollSection title="组织证据,再收敛到行动">
46
54
  <ScrollGrid columns="3">
47
55
  <PyramidModel title="论证层级" levels={[{title:'结论',description:'读者需要带走的判断'},{title:'理由',description:'支撑判断的关键分组'},{title:'证据',description:'事实、数据与案例'}]} />
@@ -33,7 +33,7 @@
33
33
  <!-- Google Fonts for Academic / Editorial Knowledge style -->
34
34
  <link rel="preconnect" href="https://fonts.googleapis.com">
35
35
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
36
- <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600&family=Lora:ital,wght@0,500;0,600;0,700;1,400&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=Noto+Serif+SC:wght@500;600;700;900&display=swap" rel="stylesheet">
36
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Lora:ital,wght@0,500;0,600;0,700;1,400&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=Noto+Serif+SC:wght@500;600;700;900&display=swap" rel="stylesheet">
37
37
  </head>
38
38
  <body>
39
39
  <div id="root"></div>
@@ -173,13 +173,50 @@ Mechanism.displayName = 'Mechanism';
173
173
  export function Implementation({ language = 'text', title = '实现代码', children }) {
174
174
  return (
175
175
  <div className="semantic-implementation" data-language={language}>
176
- {title && <div className="impl-header">{title} <span className="lang-badge">{language}</span></div>}
177
- <pre><code>{typeof children === 'string' ? children.trim() : children}</code></pre>
176
+ {title && (
177
+ <div className="semantic-code-head impl-header">
178
+ <span className="semantic-code-title">{title}</span>
179
+ {language && <span className="lang-badge">{language}</span>}
180
+ </div>
181
+ )}
182
+ <pre className="code-block"><code>{typeof children === 'string' ? children.trim() : children}</code></pre>
178
183
  </div>
179
184
  );
180
185
  }
181
186
  Implementation.displayName = 'Implementation';
182
187
 
188
+ /**
189
+ * Generic code block for any carrier. `Implementation` is bound to a node's
190
+ * implementation section; `CodeBlock` is meant for shell commands, config,
191
+ * output, prompts and pseudocode that live anywhere in a document.
192
+ * Pass the code as a string through `code` (or as children) so MDX parsing and
193
+ * the validator never confuse code with markup.
194
+ */
195
+ export function CodeBlock({ code, language = 'text', title, caption, lineNumbers = false, wrap = false, children }) {
196
+ const raw = typeof code === 'string' ? code : childrenToText(children);
197
+ const text = typeof raw === 'string' ? raw.replace(/^\n+|\s+$/g, '') : '';
198
+ if (!text) return null;
199
+ const showLanguage = Boolean(language) && language !== 'text';
200
+ const showHead = Boolean(title) || showLanguage;
201
+ return (
202
+ <figure className="semantic-code" data-language={language}>
203
+ {showHead && (
204
+ <div className="semantic-code-head">
205
+ {title && <span className="semantic-code-title">{title}</span>}
206
+ {showLanguage && <span className="lang-badge">{language}</span>}
207
+ </div>
208
+ )}
209
+ <pre className={`code-block${lineNumbers ? ' code-block-numbered' : ''}${wrap ? ' code-block-wrap' : ''}`}>
210
+ {lineNumbers
211
+ ? <code>{text.split('\n').map((line, index) => <span className="code-line" key={index}>{line}</span>)}</code>
212
+ : <code>{text}</code>}
213
+ </pre>
214
+ {caption && <figcaption className="semantic-code-caption">{caption}</figcaption>}
215
+ </figure>
216
+ );
217
+ }
218
+ CodeBlock.displayName = 'CodeBlock';
219
+
183
220
  export function Boundary({ title = '边界与约束', children }) {
184
221
  return (
185
222
  <div className="semantic-boundary">
@@ -27,6 +27,7 @@ export const KNOWN_COMPONENTS = [
27
27
  'Definition',
28
28
  'Mechanism',
29
29
  'Implementation',
30
+ 'CodeBlock',
30
31
  'Boundary',
31
32
  'Example',
32
33
  'Counterexample',
@@ -110,6 +111,7 @@ const CORE_CONTENT_COMPONENTS = new Set([
110
111
  'Definition',
111
112
  'Mechanism',
112
113
  'Implementation',
114
+ 'CodeBlock',
113
115
  'Boundary',
114
116
  'Example',
115
117
  'Counterexample',
@@ -1026,13 +1026,99 @@ button.flow-box {
1026
1026
  border-radius: 10px;
1027
1027
  overflow-x: auto;
1028
1028
  font-family: var(--font-data);
1029
- font-size: 13px;
1030
- line-height: 1.6;
1029
+ /* A comfortable, taller setting for a mono face; ligatures stay off so
1030
+ operators render exactly as they were typed. */
1031
+ font-size: 14px;
1032
+ line-height: 1.7;
1033
+ font-variant-ligatures: none;
1034
+ font-feature-settings: 'liga' 0, 'calt' 0;
1031
1035
  color: var(--code-text);
1032
1036
  border: 1px solid var(--code-border);
1033
1037
  box-shadow: var(--shadow-inset);
1034
1038
  }
1035
1039
 
1040
+ /* -- Code family: `.code-block` is the one shared dark inset used by the
1041
+ explorer, `Implementation` and `CodeBlock`. Both components wrap it with
1042
+ an optional head, caption and numbered lines, and they share the head +
1043
+ surface classes so every style pack only has to re-voice one grammar. -- */
1044
+ .semantic-code,
1045
+ .semantic-implementation {
1046
+ display: flex;
1047
+ flex-direction: column;
1048
+ gap: 8px;
1049
+ margin: 0;
1050
+ }
1051
+
1052
+ .semantic-code > pre,
1053
+ .semantic-implementation > pre {
1054
+ margin: 0;
1055
+ }
1056
+
1057
+ .semantic-code-head {
1058
+ display: flex;
1059
+ align-items: baseline;
1060
+ justify-content: space-between;
1061
+ gap: 10px;
1062
+ }
1063
+
1064
+ .semantic-code-title {
1065
+ font-family: var(--font-heading);
1066
+ font-weight: 700;
1067
+ font-size: 13px;
1068
+ color: var(--text-secondary);
1069
+ }
1070
+
1071
+ /* The language tag rides every code head; a pack may dress it as a badge. */
1072
+ .lang-badge {
1073
+ font-family: var(--font-data);
1074
+ font-size: 10px;
1075
+ font-weight: 700;
1076
+ letter-spacing: 0.08em;
1077
+ text-transform: uppercase;
1078
+ color: var(--text-muted);
1079
+ white-space: nowrap;
1080
+ }
1081
+
1082
+ .semantic-code-caption {
1083
+ font-size: 12px;
1084
+ color: var(--text-muted);
1085
+ }
1086
+
1087
+ .code-block-wrap {
1088
+ white-space: pre-wrap;
1089
+ overflow-wrap: anywhere;
1090
+ }
1091
+
1092
+ .code-block-numbered {
1093
+ counter-reset: code-line;
1094
+ }
1095
+
1096
+ .code-block-numbered code {
1097
+ display: block;
1098
+ }
1099
+
1100
+ .code-block-numbered .code-line {
1101
+ position: relative;
1102
+ display: block;
1103
+ min-height: 1.7em;
1104
+ padding-left: 3.6em;
1105
+ counter-increment: code-line;
1106
+ }
1107
+
1108
+ .code-block-numbered .code-line::before {
1109
+ content: counter(code-line);
1110
+ position: absolute;
1111
+ top: 0;
1112
+ bottom: 0;
1113
+ left: 0;
1114
+ width: 2.5em;
1115
+ padding-right: 0.85em;
1116
+ text-align: right;
1117
+ color: color-mix(in srgb, var(--code-text) 50%, transparent);
1118
+ border-right: 1px solid color-mix(in srgb, var(--code-text) 18%, transparent);
1119
+ user-select: none;
1120
+ }
1121
+
1036
1122
  .example-item, .counterexample-item {
1037
1123
  margin-top: 12px;
1038
1124
  padding: 14px 18px;
@@ -2033,10 +2119,10 @@ button.flow-box {
2033
2119
  .relation-path-node.tone-warning { border-top-color: var(--accent-amber); }
2034
2120
  .relation-path-node.tone-danger { border-top-color: var(--accent-rose); }
2035
2121
  .relation-path-kicker, .relation-path-node strong, .relation-path-node small { display: block; }
2036
- .relation-path-kicker { color: var(--text-dim); font: 700 10px 'Fira Code', monospace; margin-bottom: 4px; }
2122
+ .relation-path-kicker { color: var(--text-dim); font: 700 10px var(--font-data); margin-bottom: 4px; }
2037
2123
  .relation-path-node strong { color: var(--text-primary); font-size: 13px; }
2038
2124
  .relation-path-node small { margin-top: 4px; color: var(--text-muted); font-size: 11px; }
2039
- .relation-path-edge { flex: 0 0 72px; display: grid; place-items: center; position: relative; color: var(--text-accent); font: 700 10px 'Fira Code', monospace; }
2125
+ .relation-path-edge { flex: 0 0 72px; display: grid; place-items: center; position: relative; color: var(--text-accent); font: 700 10px var(--font-data); }
2040
2126
  .relation-path-edge::before { content: ''; position: absolute; left: 4px; right: 4px; top: 50%; border-top: 1px dashed var(--border-strong); }
2041
2127
  .relation-path-edge span { z-index: 1; padding: 2px 4px; background: var(--bg-surface); }
2042
2128
 
@@ -3379,7 +3465,8 @@ html[data-carrier='scroll'] #root {
3379
3465
  .continuous-document .model-matrix-cell span,
3380
3466
  .continuous-document .model-pyramid-level span,
3381
3467
  .continuous-document .model-funnel-step span,
3382
- .continuous-document .tradeoff-option span {
3468
+ .continuous-document .tradeoff-option span,
3469
+ .continuous-document .code-block {
3383
3470
  font-size: calc(14px * var(--reading-ui-scale));
3384
3471
  }
3385
3472
 
@@ -515,3 +515,60 @@
515
515
  [data-style='elastic'] .semantic-compare-table tbody tr {
516
516
  transition: background-color var(--elastic-motion);
517
517
  }
518
+
519
+ /* -- Code as a panel: a 6px bordered surface under a subdued header strip,
520
+ the well set flush inside and inset like a terminal. The whole panel
521
+ answers on the pack's 250ms curve with the same 1px lift as every other
522
+ elastic surface, and the caption closes as a dim mono footer. -- */
523
+ [data-style='elastic'] .semantic-code,
524
+ [data-style='elastic'] .semantic-implementation {
525
+ gap: 0;
526
+ border: 1px solid var(--border-medium);
527
+ border-radius: var(--radius-2);
528
+ background: var(--bg-surface);
529
+ box-shadow: var(--shadow-card);
530
+ overflow: hidden;
531
+ transition: border-color var(--elastic-motion), transform var(--elastic-motion);
532
+ }
533
+
534
+ [data-style='elastic'] .semantic-code:hover,
535
+ [data-style='elastic'] .semantic-implementation:hover {
536
+ border-color: var(--border-strong);
537
+ transform: translateY(-1px);
538
+ }
539
+
540
+ [data-style='elastic'] .semantic-code-head {
541
+ padding: 9px 12px;
542
+ background: var(--bg-subtle);
543
+ border-bottom: 1px solid var(--border-subtle);
544
+ }
545
+
546
+ [data-style='elastic'] .semantic-code-title {
547
+ font-family: var(--font-sans);
548
+ font-size: 13px;
549
+ font-weight: 600;
550
+ letter-spacing: -0.01em;
551
+ color: var(--text-primary);
552
+ }
553
+
554
+ [data-style='elastic'] .lang-badge {
555
+ padding: 2px 6px;
556
+ border: 1px solid var(--border-medium);
557
+ border-radius: var(--radius-1);
558
+ background: var(--bg-surface);
559
+ color: var(--text-secondary);
560
+ letter-spacing: 0.03em;
561
+ }
562
+
563
+ [data-style='elastic'] .semantic-code > .code-block,
564
+ [data-style='elastic'] .semantic-implementation > .code-block {
565
+ border: 0;
566
+ border-radius: 0;
567
+ box-shadow: var(--shadow-inset);
568
+ }
569
+
570
+ [data-style='elastic'] .semantic-code-caption {
571
+ padding: 8px 12px 10px;
572
+ border-top: 1px solid var(--border-subtle);
573
+ font-family: var(--font-sans);
574
+ }
@@ -424,3 +424,41 @@
424
424
  border-color: var(--primary);
425
425
  color: var(--primary);
426
426
  }
427
+
428
+ /* -- Listings: the code well keeps its ink, but the frame drops to a
429
+ near-square ruled inset under a heavier top rule. The head reads as a
430
+ printed listing caption in the heading face, the language tag as a quiet
431
+ mono folio, and the caption closes as an italic printer's note. Paper
432
+ answers the pointer through rule weight only — no nudging. -- */
433
+ [data-style='manuscript'] .code-block {
434
+ padding: 16px 18px;
435
+ border-radius: 2px;
436
+ border-top: 2px solid color-mix(in srgb, var(--code-text) 42%, transparent);
437
+ box-shadow: none;
438
+ transition: border-top-color var(--motion-base) var(--ease-standard);
439
+ }
440
+
441
+ [data-style='manuscript'] .code-block:hover {
442
+ border-top-color: color-mix(in srgb, var(--code-text) 62%, transparent);
443
+ }
444
+
445
+ [data-style='manuscript'] .semantic-code-title {
446
+ font-family: var(--font-heading);
447
+ font-size: 14px;
448
+ font-weight: 600;
449
+ color: var(--text-primary);
450
+ }
451
+
452
+ [data-style='manuscript'] .lang-badge {
453
+ color: var(--text-dim);
454
+ letter-spacing: 0.12em;
455
+ }
456
+
457
+ [data-style='manuscript'] .semantic-code-caption {
458
+ font-style: italic;
459
+ }
460
+
461
+ [data-style='manuscript'] .code-block-numbered .code-line::before {
462
+ color: color-mix(in srgb, var(--code-text) 40%, transparent);
463
+ border-right-color: color-mix(in srgb, var(--code-text) 16%, transparent);
464
+ }
@@ -515,3 +515,57 @@
515
515
  [data-style='shadcn'] .semantic-compare-table tbody tr {
516
516
  transition: background-color var(--shadcn-motion);
517
517
  }
518
+
519
+ /* -- Code as a card: hairline chassis, a muted divided header and the well set
520
+ flush inside it, closed by a caption footer. The language tag is an
521
+ outline badge, and the surface only answers on the shared 150ms curve. -- */
522
+ [data-style='shadcn'] .semantic-code,
523
+ [data-style='shadcn'] .semantic-implementation {
524
+ gap: 0;
525
+ border: 1px solid var(--border-subtle);
526
+ border-radius: var(--radius-3);
527
+ background: var(--bg-surface);
528
+ box-shadow: var(--shadow-sm);
529
+ overflow: hidden;
530
+ transition: border-color var(--shadcn-motion);
531
+ }
532
+
533
+ [data-style='shadcn'] .semantic-code:hover,
534
+ [data-style='shadcn'] .semantic-implementation:hover {
535
+ border-color: var(--border-medium);
536
+ }
537
+
538
+ [data-style='shadcn'] .semantic-code-head {
539
+ padding: 10px 14px;
540
+ background: var(--bg-subtle);
541
+ border-bottom: 1px solid var(--border-subtle);
542
+ }
543
+
544
+ [data-style='shadcn'] .semantic-code-title {
545
+ font-family: var(--font-sans);
546
+ font-size: 13px;
547
+ font-weight: 600;
548
+ letter-spacing: -0.01em;
549
+ color: var(--text-primary);
550
+ }
551
+
552
+ [data-style='shadcn'] .lang-badge {
553
+ padding: 2px 7px;
554
+ border: 1px solid var(--border-subtle);
555
+ border-radius: var(--radius-1);
556
+ background: var(--bg-surface);
557
+ color: var(--text-muted);
558
+ letter-spacing: 0.04em;
559
+ }
560
+
561
+ [data-style='shadcn'] .semantic-code > .code-block,
562
+ [data-style='shadcn'] .semantic-implementation > .code-block {
563
+ border: 0;
564
+ border-radius: 0;
565
+ box-shadow: none;
566
+ }
567
+
568
+ [data-style='shadcn'] .semantic-code-caption {
569
+ padding: 8px 14px 10px;
570
+ border-top: 1px solid var(--border-subtle);
571
+ }
@@ -262,7 +262,7 @@
262
262
  /* Family stacks */
263
263
  --font-sans: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Noto Sans SC", sans-serif;
264
264
  --font-serif: 'Lora', 'Noto Serif SC', Georgia, serif;
265
- --font-mono: 'Fira Code', ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
265
+ --font-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
266
266
 
267
267
  /* Roles — the only families component styles should consume */
268
268
  --font-body: var(--font-sans);