jekyll-theme-zer0 1.29.0 → 1.30.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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +715 -2
  3. data/_data/README.md +2 -0
  4. data/_data/ai.yml +5 -3
  5. data/_data/ai_pricing.yml +36 -0
  6. data/_data/backlog.yml +169 -18
  7. data/_data/consumers.yml +48 -5
  8. data/_data/features.yml +146 -20
  9. data/_data/feedback_types.yml +17 -12
  10. data/_data/landing.yml +5 -2
  11. data/_data/navigation/quickstart.yml +4 -0
  12. data/_data/site_builder.yml +874 -0
  13. data/_data/theme-manifest.yml +122 -114
  14. data/_data/ui-text.yml +18 -0
  15. data/_includes/README.md +11 -1
  16. data/_includes/analytics/posthog.html +2 -2
  17. data/_includes/components/admin-links.html +2 -2
  18. data/_includes/components/admin-tabs.html +2 -2
  19. data/_includes/components/ai-chat.html +14 -11
  20. data/_includes/components/analytics-dashboard.html +8 -8
  21. data/_includes/components/author-bio.html +1 -1
  22. data/_includes/components/author-card.html +10 -2
  23. data/_includes/components/author-eeat.html +4 -4
  24. data/_includes/components/background-customizer.html +8 -8
  25. data/_includes/components/background-image.html +114 -0
  26. data/_includes/components/background-settings.html +28 -15
  27. data/_includes/components/collection-manager.html +5 -5
  28. data/_includes/components/component-showcase.html +13 -13
  29. data/_includes/components/config-editor.html +12 -12
  30. data/_includes/components/config-viewer.html +8 -8
  31. data/_includes/components/cookie-consent.html +11 -11
  32. data/_includes/components/cta-button.html +7 -2
  33. data/_includes/components/dev-shortcuts.html +7 -7
  34. data/_includes/components/env-dashboard.html +8 -8
  35. data/_includes/components/env-switcher.html +9 -9
  36. data/_includes/components/feature-card.html +2 -2
  37. data/_includes/components/halfmoon.html +2 -2
  38. data/_includes/components/info-section.html +36 -36
  39. data/_includes/components/js-cdn.html +15 -15
  40. data/_includes/components/language-toggle.html +4 -4
  41. data/_includes/components/mermaid.html +72 -435
  42. data/_includes/components/nanobar.html +5 -5
  43. data/_includes/components/nav-editor.html +2 -2
  44. data/_includes/components/nav-export.html +2 -2
  45. data/_includes/components/nav-overview.html +2 -2
  46. data/_includes/components/page-feedback.html +45 -30
  47. data/_includes/components/page-views-init.html +1 -1
  48. data/_includes/components/post-card.html +22 -22
  49. data/_includes/components/post-type-badge.html +2 -2
  50. data/_includes/components/powered-by.html +2 -2
  51. data/_includes/components/preview-image.html +6 -0
  52. data/_includes/components/quick-index.html +2 -2
  53. data/_includes/components/search-modal.html +27 -2
  54. data/_includes/components/searchbar.html +2 -2
  55. data/_includes/components/svg-background.html +2 -2
  56. data/_includes/components/theme-customizer.html +2 -2
  57. data/_includes/components/theme-info.html +6 -6
  58. data/_includes/components/theme-preview-gallery.html +22 -22
  59. data/_includes/content/giscus.html +2 -2
  60. data/_includes/content/intro.html +8 -8
  61. data/_includes/content/jsonld-faq.html +2 -2
  62. data/_includes/content/jsonld-software.html +2 -2
  63. data/_includes/content/seo.html +4 -4
  64. data/_includes/content/sitemap.html +27 -27
  65. data/_includes/content/toc.html +183 -183
  66. data/_includes/core/branding.html +6 -6
  67. data/_includes/core/console-capture.html +32 -74
  68. data/_includes/core/favicon.html +49 -7
  69. data/_includes/core/footer-fabs.html +17 -3
  70. data/_includes/core/footer.html +31 -18
  71. data/_includes/core/head.html +102 -90
  72. data/_includes/core/header.html +71 -52
  73. data/_includes/docs/bootstrap-docs.html +8 -8
  74. data/_includes/landing/landing-install-cards.html +2 -2
  75. data/_includes/landing/landing-quick-links.html +1 -1
  76. data/_includes/navigation/admin-nav.html +2 -2
  77. data/_includes/navigation/nav-tree.html +8 -8
  78. data/_includes/navigation/navbar.html +12 -12
  79. data/_includes/navigation/section-sidebar.html +16 -16
  80. data/_includes/navigation/sidebar-config.html +36 -2
  81. data/_includes/navigation/sidebar-left.html +15 -15
  82. data/_includes/navigation/sidebar-right.html +6 -6
  83. data/_includes/obsidian/full-graph.html +2 -2
  84. data/_includes/setup/claude-session.html +72 -0
  85. data/_includes/setup/prereq-checklist.html +90 -0
  86. data/_includes/setup/wizard.html +906 -291
  87. data/_includes/stats/stats-categories.html +8 -8
  88. data/_includes/stats/stats-header.html +14 -14
  89. data/_includes/stats/stats-metrics.html +12 -12
  90. data/_includes/stats/stats-no-data.html +12 -12
  91. data/_includes/stats/stats-overview.html +6 -6
  92. data/_includes/stats/stats-tags.html +8 -8
  93. data/_layouts/404.html +38 -24
  94. data/_layouts/admin.html +22 -22
  95. data/_layouts/article.html +39 -34
  96. data/_layouts/author.html +20 -20
  97. data/_layouts/authors.html +2 -2
  98. data/_layouts/book-abc.html +12 -12
  99. data/_layouts/book-story.html +15 -15
  100. data/_layouts/book.html +12 -12
  101. data/_layouts/collection.html +33 -33
  102. data/_layouts/cookbook.html +12 -12
  103. data/_layouts/default.html +27 -24
  104. data/_layouts/home.html +23 -23
  105. data/_layouts/index.html +10 -10
  106. data/_layouts/landing.html +17 -17
  107. data/_layouts/news.html +44 -44
  108. data/_layouts/note.html +38 -38
  109. data/_layouts/notebook.html +34 -34
  110. data/_layouts/recipe.html +24 -24
  111. data/_layouts/root.html +73 -55
  112. data/_layouts/section.html +23 -23
  113. data/_layouts/setup.html +3 -3
  114. data/_layouts/sitemap-collection.html +49 -49
  115. data/_layouts/stats.html +40 -40
  116. data/_layouts/tag.html +12 -12
  117. data/_layouts/welcome.html +21 -21
  118. data/_sass/components/_mermaid.scss +375 -0
  119. data/_sass/components/_setup-wizard.scss +569 -40
  120. data/_sass/core/_navbar.scss +11 -31
  121. data/_sass/layouts/_navbar-extras.scss +14 -4
  122. data/assets/css/main.scss +1 -0
  123. data/assets/js/ai-chat.js +47 -5
  124. data/assets/js/fleet-feedback-capture.js +124 -0
  125. data/assets/js/fleet-feedback.js +853 -0
  126. data/assets/js/mermaid-diagrams.js +1267 -0
  127. data/assets/js/modules/navigation/config.js +9 -6
  128. data/assets/js/modules/navigation/scroll-spy.js +315 -80
  129. data/assets/js/modules/theme/appearance.js +8 -2
  130. data/assets/js/obsidian-wiki-links.js +8 -3
  131. data/assets/js/page-feedback.js +125 -192
  132. data/assets/js/search-modal.js +26 -0
  133. data/assets/js/setup-wizard.js +2112 -361
  134. data/assets/js/site-builder.js +1834 -0
  135. data/assets/js/ui-enhancements.js +11 -3
  136. data/scripts/README.md +15 -0
  137. data/scripts/ai/README.md +38 -0
  138. data/scripts/ai/api_call.rb +124 -0
  139. data/scripts/ai/usage.rb +314 -0
  140. data/scripts/ai/usage_report.rb +225 -0
  141. data/scripts/ci/test_visual_evidence_autogen.py +341 -0
  142. data/scripts/ci/visual_evidence_autogen.py +1060 -0
  143. data/scripts/content-review.rb +20 -1
  144. data/scripts/test/integration/mermaid +22 -8
  145. data/scripts/test/lib/run_tests.sh +1 -0
  146. data/scripts/test/lib/test_visual_evidence_autogen.sh +24 -0
  147. data/scripts/translate.rb +23 -1
  148. metadata +19 -2
data/_layouts/stats.html CHANGED
@@ -2,7 +2,7 @@
2
2
  # Features: ZER0-019, ZER0-043
3
3
  layout: root
4
4
  ---
5
- <!--
5
+ {% comment %}
6
6
  ===================================================================
7
7
  STATS LAYOUT - Statistics dashboard layout
8
8
  ===================================================================
@@ -39,68 +39,68 @@ layout: root
39
39
  - card: Card components for metric display
40
40
  - badge: Badges for counts and labels
41
41
  ===================================================================
42
- -->
42
+ {% endcomment %}
43
43
 
44
- <!-- ================================================ -->
45
- <!-- STATS LAYOUT CONTAINER -->
46
- <!-- Full-width responsive container for dashboard -->
47
- <!-- ================================================ -->
44
+ {% comment %} ================================================ {% endcomment %}
45
+ {% comment %} STATS LAYOUT CONTAINER {% endcomment %}
46
+ {% comment %} Full-width responsive container for dashboard {% endcomment %}
47
+ {% comment %} ================================================ {% endcomment %}
48
48
  <div class="container-fluid py-4">
49
49
 
50
- <!-- ================================================ -->
51
- <!-- STATS HEADER SECTION -->
52
- <!-- Page title, description, and metadata -->
53
- <!-- ================================================ -->
50
+ {% comment %} ================================================ {% endcomment %}
51
+ {% comment %} STATS HEADER SECTION {% endcomment %}
52
+ {% comment %} Page title, description, and metadata {% endcomment %}
53
+ {% comment %} ================================================ {% endcomment %}
54
54
  {% include stats/stats-header.html %}
55
55
 
56
- <!-- ================================================ -->
57
- <!-- STATS VALIDATION CHECK -->
58
- <!-- Verify data availability before rendering -->
59
- <!-- ================================================ -->
56
+ {% comment %} ================================================ {% endcomment %}
57
+ {% comment %} STATS VALIDATION CHECK {% endcomment %}
58
+ {% comment %} Verify data availability before rendering {% endcomment %}
59
+ {% comment %} ================================================ {% endcomment %}
60
60
  {% if site.data.content_statistics %}
61
61
 
62
- <!-- ================================================ -->
63
- <!-- OVERVIEW METRICS SECTION -->
64
- <!-- High-level statistics cards -->
65
- <!-- ================================================ -->
62
+ {% comment %} ================================================ {% endcomment %}
63
+ {% comment %} OVERVIEW METRICS SECTION {% endcomment %}
64
+ {% comment %} High-level statistics cards {% endcomment %}
65
+ {% comment %} ================================================ {% endcomment %}
66
66
  {% include stats/stats-overview.html %}
67
67
 
68
- <!-- ================================================ -->
69
- <!-- DETAILED ANALYTICS SECTIONS -->
70
- <!-- Categories, tags, and metrics breakdown -->
71
- <!-- ================================================ -->
68
+ {% comment %} ================================================ {% endcomment %}
69
+ {% comment %} DETAILED ANALYTICS SECTIONS {% endcomment %}
70
+ {% comment %} Categories, tags, and metrics breakdown {% endcomment %}
71
+ {% comment %} ================================================ {% endcomment %}
72
72
  <div class="row g-4 mb-5">
73
- <!-- Categories Analysis -->
73
+ {% comment %} Categories Analysis {% endcomment %}
74
74
  <div class="col-lg-6">
75
75
  {% include stats/stats-categories.html %}
76
76
  </div>
77
77
 
78
- <!-- Tags Analysis -->
78
+ {% comment %} Tags Analysis {% endcomment %}
79
79
  <div class="col-lg-6">
80
80
  {% include stats/stats-tags.html %}
81
81
  </div>
82
82
  </div>
83
83
 
84
- <!-- ================================================ -->
85
- <!-- ADDITIONAL METRICS SECTION -->
86
- <!-- Quick facts and summary information -->
87
- <!-- ================================================ -->
84
+ {% comment %} ================================================ {% endcomment %}
85
+ {% comment %} ADDITIONAL METRICS SECTION {% endcomment %}
86
+ {% comment %} Quick facts and summary information {% endcomment %}
87
+ {% comment %} ================================================ {% endcomment %}
88
88
  {% include stats/stats-metrics.html %}
89
89
 
90
90
  {% else %}
91
91
 
92
- <!-- ================================================ -->
93
- <!-- NO DATA AVAILABLE SECTION -->
94
- <!-- Error state when statistics are not generated -->
95
- <!-- ================================================ -->
92
+ {% comment %} ================================================ {% endcomment %}
93
+ {% comment %} NO DATA AVAILABLE SECTION {% endcomment %}
94
+ {% comment %} Error state when statistics are not generated {% endcomment %}
95
+ {% comment %} ================================================ {% endcomment %}
96
96
  {% include stats/stats-no-data.html %}
97
97
 
98
98
  {% endif %}
99
99
 
100
- <!-- ================================================ -->
101
- <!-- MAIN CONTENT AREA -->
102
- <!-- Additional page content below statistics -->
103
- <!-- ================================================ -->
100
+ {% comment %} ================================================ {% endcomment %}
101
+ {% comment %} MAIN CONTENT AREA {% endcomment %}
102
+ {% comment %} Additional page content below statistics {% endcomment %}
103
+ {% comment %} ================================================ {% endcomment %}
104
104
  {% if content and content != empty %}
105
105
  <div class="row mt-5">
106
106
  <div class="col-12">
@@ -115,10 +115,10 @@ layout: root
115
115
 
116
116
  </div>
117
117
 
118
- <!-- ================================================ -->
119
- <!-- STATS-SPECIFIC JAVASCRIPT -->
120
- <!-- Initialize charts and interactive components -->
121
- <!-- ================================================ -->
118
+ {% comment %} ================================================ {% endcomment %}
119
+ {% comment %} STATS-SPECIFIC JAVASCRIPT {% endcomment %}
120
+ {% comment %} Initialize charts and interactive components {% endcomment %}
121
+ {% comment %} ================================================ {% endcomment %}
122
122
  <script>
123
123
  document.addEventListener('DOMContentLoaded', function() {
124
124
  // Initialize tooltips for stats elements
data/_layouts/tag.html CHANGED
@@ -2,7 +2,7 @@
2
2
  # Feature: ZER0-019
3
3
  layout: default
4
4
  ---
5
- <!--
5
+ {% comment %}
6
6
  ===================================================================
7
7
  TAG LAYOUT - Tag archive page displaying posts by tag
8
8
  ===================================================================
@@ -27,11 +27,11 @@ layout: default
27
27
  - _includes/components/post-card.html: Reusable post card component
28
28
  - Bootstrap 5 grid and badge components
29
29
  ===================================================================
30
- -->
30
+ {% endcomment %}
31
31
 
32
- <!-- ========================== -->
33
- <!-- TAG HEADER SECTION -->
34
- <!-- ========================== -->
32
+ {% comment %} ========================== {% endcomment %}
33
+ {% comment %} TAG HEADER SECTION {% endcomment %}
34
+ {% comment %} ========================== {% endcomment %}
35
35
  <header class="tag-header mb-5">
36
36
  <nav aria-label="breadcrumb" class="mb-3">
37
37
  <ol class="breadcrumb">
@@ -60,9 +60,9 @@ layout: default
60
60
  {{ content }}
61
61
  </header>
62
62
 
63
- <!-- ========================== -->
64
- <!-- TAGGED POSTS GRID -->
65
- <!-- ========================== -->
63
+ {% comment %} ========================== {% endcomment %}
64
+ {% comment %} TAGGED POSTS GRID {% endcomment %}
65
+ {% comment %} ========================== {% endcomment %}
66
66
  <section class="tagged-posts">
67
67
  {% if tag_posts.size > 0 %}
68
68
  <div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
@@ -71,7 +71,7 @@ layout: default
71
71
  {% endfor %}
72
72
  </div>
73
73
  {% else %}
74
- <!-- Empty state -->
74
+ {% comment %} Empty state {% endcomment %}
75
75
  <div class="text-center py-5">
76
76
  <i class="bi bi-tag fs-1 text-muted"></i>
77
77
  <p class="text-muted mt-3">No posts with this tag yet.</p>
@@ -82,9 +82,9 @@ layout: default
82
82
  {% endif %}
83
83
  </section>
84
84
 
85
- <!-- ========================== -->
86
- <!-- RELATED TAGS -->
87
- <!-- ========================== -->
85
+ {% comment %} ========================== {% endcomment %}
86
+ {% comment %} RELATED TAGS {% endcomment %}
87
+ {% comment %} ========================== {% endcomment %}
88
88
  {% comment %} Find tags that appear with this tag {% endcomment %}
89
89
  {% assign related_tags = "" | split: "" %}
90
90
  {% for post in tag_posts %}
@@ -2,7 +2,7 @@
2
2
  # Features: ZER0-019, ZER0-046
3
3
  layout: root
4
4
  ---
5
- <!--
5
+ {% comment %}
6
6
  ===================================================================
7
7
  WELCOME LAYOUT — Onboarding landing page for new sites
8
8
  ===================================================================
@@ -36,15 +36,15 @@ layout: root
36
36
  Gemfile (github-pages + jekyll-remote-theme)
37
37
  index.md (this layout)
38
38
  ===================================================================
39
- -->
39
+ {% endcomment %}
40
40
 
41
41
  {% include components/setup-check.html %}
42
42
 
43
43
  {% if site_needs_setup %}
44
44
 
45
- <!-- ============================================================ -->
46
- <!-- HERO -->
47
- <!-- ============================================================ -->
45
+ {% comment %} ============================================================ {% endcomment %}
46
+ {% comment %} HERO {% endcomment %}
47
+ {% comment %} ============================================================ {% endcomment %}
48
48
  <section class="bg-body-tertiary border-bottom">
49
49
  <div class="container-lg py-5">
50
50
  <div class="row align-items-center g-4">
@@ -114,9 +114,9 @@ layout: root
114
114
  </div>
115
115
  </section>
116
116
 
117
- <!-- ============================================================ -->
118
- <!-- MINIMAL STARTER INSTRUCTIONS -->
119
- <!-- ============================================================ -->
117
+ {% comment %} ============================================================ {% endcomment %}
118
+ {% comment %} MINIMAL STARTER INSTRUCTIONS {% endcomment %}
119
+ {% comment %} ============================================================ {% endcomment %}
120
120
  <section id="minimal-starter" class="container-lg py-5">
121
121
  <div class="row justify-content-center">
122
122
  <div class="col-lg-10">
@@ -129,7 +129,7 @@ layout: root
129
129
 
130
130
  <div class="accordion mt-4" id="starterAccordion">
131
131
 
132
- <!-- _config.yml -->
132
+ {% comment %} _config.yml {% endcomment %}
133
133
  <div class="accordion-item">
134
134
  <h3 class="accordion-header">
135
135
  <button class="accordion-button" type="button"
@@ -171,7 +171,7 @@ site_configured: true
171
171
  </div>
172
172
  </div>
173
173
 
174
- <!-- Gemfile -->
174
+ {% comment %} Gemfile {% endcomment %}
175
175
  <div class="accordion-item">
176
176
  <h3 class="accordion-header">
177
177
  <button class="accordion-button collapsed" type="button"
@@ -197,7 +197,7 @@ gem "webrick", "~> 1.7"
197
197
  </div>
198
198
  </div>
199
199
 
200
- <!-- index.md -->
200
+ {% comment %} index.md {% endcomment %}
201
201
  <div class="accordion-item">
202
202
  <h3 class="accordion-header">
203
203
  <button class="accordion-button collapsed" type="button"
@@ -232,7 +232,7 @@ is set in `_config.yml`.
232
232
  </div>
233
233
  </div>
234
234
 
235
- </div><!-- /accordion -->
235
+ </div>{% comment %} /accordion {% endcomment %}
236
236
 
237
237
  <div class="alert alert-info d-flex align-items-start mt-4" role="alert">
238
238
  <i class="bi bi-info-circle-fill me-2 mt-1"></i>
@@ -248,16 +248,16 @@ is set in `_config.yml`.
248
248
  </div>
249
249
  </section>
250
250
 
251
- <!-- ============================================================ -->
252
- <!-- SETUP WIZARD -->
253
- <!-- ============================================================ -->
251
+ {% comment %} ============================================================ {% endcomment %}
252
+ {% comment %} SETUP WIZARD {% endcomment %}
253
+ {% comment %} ============================================================ {% endcomment %}
254
254
  <section id="setup-wizard" class="bg-body-tertiary border-top py-5">
255
255
  {% include setup/wizard.html %}
256
256
  </section>
257
257
 
258
- <!-- ============================================================ -->
259
- <!-- NEXT STEPS / RESOURCES -->
260
- <!-- ============================================================ -->
258
+ {% comment %} ============================================================ {% endcomment %}
259
+ {% comment %} NEXT STEPS / RESOURCES {% endcomment %}
260
+ {% comment %} ============================================================ {% endcomment %}
261
261
  <section class="container-lg py-5">
262
262
  <h2 class="mb-4"><i class="bi bi-signpost-2"></i> Next steps</h2>
263
263
  <div class="row g-3">
@@ -321,9 +321,9 @@ is set in `_config.yml`.
321
321
 
322
322
  {% else %}
323
323
 
324
- <!-- ============================================================ -->
325
- <!-- CONFIGURED SITE — render the user's content -->
326
- <!-- ============================================================ -->
324
+ {% comment %} ============================================================ {% endcomment %}
325
+ {% comment %} CONFIGURED SITE — render the user's content {% endcomment %}
326
+ {% comment %} ============================================================ {% endcomment %}
327
327
  <div class="container-lg py-4">
328
328
  <div class="row justify-content-center">
329
329
  <div class="col-lg-10">
@@ -0,0 +1,375 @@
1
+ // Feature: ZER0-013
2
+ // ============================================================================
3
+ // Mermaid diagram figures
4
+ // ----------------------------------------------------------------------------
5
+ // Markup: built at runtime by assets/js/mermaid-diagrams.js from every
6
+ // ```mermaid fence: figure.zer0-diagram > [toolbar] + viewport
7
+ // > canvas > svg, + figcaption
8
+ // Loader: _includes/components/mermaid.html (pages with `mermaid: true`)
9
+ // Theming: every colour here is a token, and the SVG's own palette is
10
+ // derived from the same tokens in JS — so light / dark / wizard
11
+ // modes and all skins work without per-mode overrides, and
12
+ // nothing needs `!important` (which used to flatten per-node
13
+ // `style`/`classDef` colours and pie slices).
14
+ // ============================================================================
15
+
16
+ .zer0-diagram {
17
+ --zer0-diagram-surface: color-mix(in srgb, var(--zer0-color-bg-elevated) 55%, var(--bs-body-bg));
18
+ --zer0-diagram-padding: var(--zer0-space-3);
19
+
20
+ position: relative;
21
+ margin: var(--zer0-space-4) 0;
22
+ padding: 0;
23
+ background: var(--zer0-diagram-surface);
24
+ border: 1px solid var(--zer0-color-border);
25
+ border-radius: var(--zer0-radius-lg);
26
+ box-shadow: var(--zer0-shadow-xs);
27
+ overflow: hidden;
28
+ transition: border-color var(--zer0-motion-duration-base) var(--zer0-motion-ease-standard),
29
+ box-shadow var(--zer0-motion-duration-base) var(--zer0-motion-ease-standard);
30
+
31
+ &:hover,
32
+ &:focus-within {
33
+ border-color: color-mix(in srgb, var(--zer0-color-primary) 35%, var(--zer0-color-border));
34
+ box-shadow: var(--zer0-shadow-sm);
35
+ }
36
+
37
+ // --- Toolbar ---------------------------------------------------------------
38
+ // On pointer devices it floats over the top-right corner and is hidden until
39
+ // the figure is hovered or holds focus. On touch devices there is no hover
40
+ // to reveal it, so it becomes a static, right-aligned row ABOVE the diagram
41
+ // — a floating bar that is always visible would cover the top of a phone-
42
+ // width diagram.
43
+ &__toolbar {
44
+ position: absolute;
45
+ top: var(--zer0-space-2);
46
+ right: var(--zer0-space-2);
47
+ z-index: 2;
48
+ display: inline-flex;
49
+ align-items: center;
50
+ gap: 0.125rem;
51
+ padding: 0.125rem;
52
+ background: color-mix(in srgb, var(--bs-body-bg) 88%, transparent);
53
+ border: 1px solid var(--zer0-color-border-translucent);
54
+ border-radius: var(--zer0-radius-pill);
55
+ box-shadow: var(--zer0-shadow-sm);
56
+ backdrop-filter: blur(6px);
57
+ -webkit-backdrop-filter: blur(6px);
58
+ opacity: 0;
59
+ transition: opacity var(--zer0-motion-duration-fast) var(--zer0-motion-ease-standard);
60
+
61
+ @media (hover: none) {
62
+ position: static;
63
+ display: flex;
64
+ justify-content: flex-end;
65
+ margin: var(--zer0-space-2) var(--zer0-space-2) 0 auto;
66
+ width: fit-content;
67
+ max-width: calc(100% - 2 * var(--zer0-space-2));
68
+ opacity: 1;
69
+ box-shadow: none;
70
+ backdrop-filter: none;
71
+ -webkit-backdrop-filter: none;
72
+ }
73
+ }
74
+
75
+ &:hover &__toolbar,
76
+ &:focus-within &__toolbar,
77
+ &__toolbar:focus-within,
78
+ &__toolbar--static {
79
+ opacity: 1;
80
+ }
81
+
82
+ &__btn {
83
+ display: inline-flex;
84
+ align-items: center;
85
+ justify-content: center;
86
+ width: 2rem;
87
+ height: 2rem;
88
+ padding: 0;
89
+ color: var(--zer0-color-ink-muted);
90
+ background: transparent;
91
+ border: 0;
92
+ border-radius: var(--zer0-radius-circle);
93
+ line-height: 1;
94
+ font-size: 0.95rem;
95
+ cursor: pointer;
96
+ transition: background-color var(--zer0-motion-duration-fast) var(--zer0-motion-ease-standard),
97
+ color var(--zer0-motion-duration-fast) var(--zer0-motion-ease-standard);
98
+
99
+ &:hover:not(:disabled) {
100
+ color: var(--zer0-color-ink);
101
+ background: var(--zer0-color-bg-muted);
102
+ }
103
+
104
+ &:focus-visible {
105
+ outline: 2px solid var(--zer0-color-primary);
106
+ outline-offset: 1px;
107
+ color: var(--zer0-color-ink);
108
+ }
109
+
110
+ &:disabled {
111
+ opacity: 0.35;
112
+ cursor: not-allowed;
113
+ }
114
+
115
+ &.is-success { color: var(--zer0-color-success); }
116
+ &.is-failure { color: var(--zer0-color-danger); }
117
+
118
+ // Comfortable touch targets without changing the desktop density.
119
+ @media (hover: none) and (pointer: coarse) {
120
+ width: 2.25rem;
121
+ height: 2.25rem;
122
+ }
123
+ }
124
+
125
+ &__sep {
126
+ width: 1px;
127
+ height: 1.125rem;
128
+ margin: 0 0.125rem;
129
+ background: var(--zer0-color-border);
130
+ }
131
+
132
+ &__zoom {
133
+ min-width: 2.5rem;
134
+ padding: 0 0.25rem;
135
+ font-size: 0.75rem;
136
+ font-variant-numeric: tabular-nums;
137
+ text-align: center;
138
+ color: var(--zer0-color-ink-muted);
139
+
140
+ &[hidden] { display: none; }
141
+ }
142
+
143
+ // --- Viewport / canvas -----------------------------------------------------
144
+ &__viewport {
145
+ position: relative;
146
+ max-height: min(80vh, 60rem);
147
+ padding: var(--zer0-diagram-padding);
148
+ overflow: auto;
149
+ overscroll-behavior: contain;
150
+ scrollbar-width: thin;
151
+ outline: none;
152
+
153
+ &:focus-visible {
154
+ box-shadow: inset 0 0 0 2px var(--zer0-color-primary);
155
+ border-radius: var(--zer0-radius-lg);
156
+ }
157
+
158
+ &.is-zoomed { cursor: grab; }
159
+ &.is-panning { cursor: grabbing; user-select: none; }
160
+ }
161
+
162
+ &__canvas {
163
+ display: block;
164
+ width: 100%;
165
+ }
166
+
167
+ // Mermaid emits width="100%" + style="max-width: <natural>px". At 1:1 the
168
+ // SVG fills the viewport up to its natural size and is centred; when zoomed
169
+ // the JS sets an explicit width, the block overflows to the right and the
170
+ // viewport scrolls — a real layout, no transforms, so print/centring hold.
171
+ &__svg,
172
+ &__canvas > svg {
173
+ display: block;
174
+ height: auto;
175
+ margin: 0 auto;
176
+ max-width: 100%;
177
+ }
178
+
179
+ &__caption {
180
+ padding: 0 var(--zer0-space-3) var(--zer0-space-3);
181
+ font-size: 0.875rem;
182
+ color: var(--zer0-color-ink-muted);
183
+ text-align: center;
184
+
185
+ &[hidden] { display: none; }
186
+ }
187
+
188
+ // --- Loading ---------------------------------------------------------------
189
+ &__skeleton {
190
+ min-height: 8rem;
191
+ border-radius: var(--zer0-radius);
192
+ }
193
+
194
+ // --- Error state -----------------------------------------------------------
195
+ // The diagram could not be parsed: say so, show why, and keep the source on
196
+ // the page — content is never lost to a typo.
197
+ &.has-error {
198
+ border-color: color-mix(in srgb, var(--zer0-color-warning) 55%, var(--zer0-color-border));
199
+ }
200
+
201
+ &__error {
202
+ display: grid;
203
+ grid-template-columns: auto 1fr;
204
+ gap: var(--zer0-space-3);
205
+ align-items: flex-start;
206
+ padding: var(--zer0-space-3);
207
+ color: var(--bs-warning-text-emphasis, var(--zer0-color-ink));
208
+ background: var(--bs-warning-bg-subtle, var(--zer0-color-bg-elevated));
209
+ border-left: 0.25rem solid var(--zer0-color-warning);
210
+ border-radius: var(--zer0-radius);
211
+
212
+ > .bi {
213
+ font-size: 1.25rem;
214
+ line-height: 1;
215
+ padding-top: 0.125rem;
216
+ color: var(--zer0-color-warning);
217
+ }
218
+ }
219
+
220
+ &__error-body { min-width: 0; }
221
+
222
+ &__error-title {
223
+ margin: 0 0 0.25rem;
224
+ font-weight: var(--zer0-font-weight-semibold, 600);
225
+ }
226
+
227
+ &__error-message {
228
+ margin: 0 0 0.5rem;
229
+ padding: 0.5rem 0.75rem;
230
+ font-size: 0.8125rem;
231
+ white-space: pre-wrap;
232
+ word-break: break-word;
233
+ background: color-mix(in srgb, var(--bs-body-bg) 60%, transparent);
234
+ border: 0;
235
+ border-radius: var(--zer0-radius-sm);
236
+ }
237
+
238
+ &__error-hint {
239
+ margin: 0;
240
+ font-size: 0.875rem;
241
+ }
242
+
243
+ &__source {
244
+ margin-top: var(--zer0-space-3);
245
+ font-size: 0.875rem;
246
+
247
+ > summary {
248
+ cursor: pointer;
249
+ color: var(--zer0-color-ink-muted);
250
+ }
251
+
252
+ pre {
253
+ margin: 0.5rem 0 0;
254
+ padding: var(--zer0-space-3);
255
+ max-height: 20rem;
256
+ overflow: auto;
257
+ background: var(--zer0-color-code-bg);
258
+ border: 1px solid var(--zer0-color-border);
259
+ border-radius: var(--zer0-radius);
260
+ font-size: 0.8125rem;
261
+ }
262
+ }
263
+
264
+ // --- Print -----------------------------------------------------------------
265
+ @media print {
266
+ border: 0;
267
+ box-shadow: none;
268
+ background: transparent;
269
+ break-inside: avoid;
270
+
271
+ .zer0-diagram__toolbar { display: none; }
272
+ .zer0-diagram__viewport { max-height: none; overflow: visible; }
273
+ }
274
+ }
275
+
276
+ // --- Fullscreen lightbox -------------------------------------------------------
277
+ // A native <dialog> (top layer, focus trap, Escape) — the figure's canvas is
278
+ // moved in and back out, so it is the same SVG the reader was looking at.
279
+ .zer0-diagram-lightbox {
280
+ width: 100vw;
281
+ height: 100vh;
282
+ height: 100dvh;
283
+ max-width: none;
284
+ max-height: none;
285
+ margin: 0;
286
+ padding: 0;
287
+ border: 0;
288
+ color: var(--bs-body-color);
289
+ background: var(--bs-body-bg);
290
+
291
+ &::backdrop {
292
+ background: rgba(0, 0, 0, 0.6);
293
+ }
294
+
295
+ &__inner {
296
+ display: flex;
297
+ flex-direction: column;
298
+ height: 100%;
299
+ }
300
+
301
+ &__bar {
302
+ display: flex;
303
+ align-items: center;
304
+ justify-content: space-between;
305
+ gap: var(--zer0-space-3);
306
+ padding: var(--zer0-space-2) var(--zer0-space-3);
307
+ border-bottom: 1px solid var(--zer0-color-border);
308
+ background: var(--zer0-color-bg-elevated);
309
+ }
310
+
311
+ &__title {
312
+ margin: 0;
313
+ min-width: 0;
314
+ overflow: hidden;
315
+ text-overflow: ellipsis;
316
+ white-space: nowrap;
317
+ font-weight: var(--zer0-font-weight-semibold, 600);
318
+ }
319
+
320
+ // The toolbar is static chrome here rather than a floating overlay.
321
+ .zer0-diagram__toolbar--static {
322
+ position: static;
323
+ flex: none;
324
+ box-shadow: none;
325
+ backdrop-filter: none;
326
+ -webkit-backdrop-filter: none;
327
+ }
328
+
329
+ &__viewport {
330
+ flex: 1 1 auto;
331
+ max-height: none;
332
+ padding: var(--zer0-space-4);
333
+ }
334
+
335
+ .zer0-diagram__btn--close {
336
+ margin-left: 0.25rem;
337
+ color: var(--zer0-color-ink);
338
+ }
339
+ }
340
+
341
+ // Lock page scroll while the lightbox is open.
342
+ .zer0-diagram-lightbox-open {
343
+ overflow: hidden;
344
+ }
345
+
346
+ // --- Progressive enhancement --------------------------------------------------
347
+ // Until the deferred scripts run, a raw ```mermaid fence would paint as a code
348
+ // block and then be swapped for the diagram — a visible flash of source. With
349
+ // JS available, paint it as a placeholder instead. The delayed animation is a
350
+ // safety net: if the scripts never arrive (blocked asset, old browser) the
351
+ // source fades back in, so a broken script can never hide content. Without JS
352
+ // (`html.no-js`) or when mermaid.min.js fails to load
353
+ // (`.zer0-diagram-unavailable`, set by the script) the fence stays a normal,
354
+ // readable code block.
355
+ .js:not(.zer0-diagram-unavailable) pre:has(> code.language-mermaid) {
356
+ min-height: 8rem;
357
+ color: transparent;
358
+ background: linear-gradient(
359
+ 90deg,
360
+ var(--zer0-color-bg-elevated) 25%,
361
+ var(--zer0-color-bg-muted) 50%,
362
+ var(--zer0-color-bg-elevated) 75%
363
+ );
364
+ background-size: 200% 100%;
365
+ border-radius: var(--zer0-radius-lg);
366
+ animation:
367
+ zer0-skeleton-shimmer 1.5s ease-in-out infinite,
368
+ zer0-diagram-reveal 0s linear 8s forwards;
369
+
370
+ > code { color: inherit; }
371
+ }
372
+
373
+ @keyframes zer0-diagram-reveal {
374
+ to { color: inherit; animation: none; }
375
+ }