datalog-theme 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +162 -0
  3. data/CITATION.cff +29 -0
  4. data/LICENSE +21 -0
  5. data/README.md +257 -0
  6. data/_data/academic.yml +217 -0
  7. data/_data/cdn-integrity.yml +51 -0
  8. data/_data/config/author.yml +121 -0
  9. data/_data/config/features.yml +262 -0
  10. data/_data/config/site.yml +42 -0
  11. data/_data/config/theme.yml +181 -0
  12. data/_data/datasets.yml +28 -0
  13. data/_data/i18n/en.yml +438 -0
  14. data/_data/i18n/es.yml +438 -0
  15. data/_data/i18n/pt.yml +438 -0
  16. data/_data/js_manifest.json +22 -0
  17. data/_data/js_meta.json +371 -0
  18. data/_data/navigation.yml +145 -0
  19. data/_data/projects.yml +41 -0
  20. data/_data/publications.yml +28 -0
  21. data/_data/social.yml +73 -0
  22. data/_data/visualizations.yml +51 -0
  23. data/_includes/analytics/dashboard.html +149 -0
  24. data/_includes/analytics.html +9 -0
  25. data/_includes/components/academic-dashboard.html +347 -0
  26. data/_includes/components/advanced-search.html +682 -0
  27. data/_includes/components/api-function.html +94 -0
  28. data/_includes/components/author-bio.html +166 -0
  29. data/_includes/components/bookmark-system.html +96 -0
  30. data/_includes/components/breadcrumbs.html +89 -0
  31. data/_includes/components/citation-tools.html +94 -0
  32. data/_includes/components/comments.html +244 -0
  33. data/_includes/components/content-provenance.html +43 -0
  34. data/_includes/components/content-recommendations.html +228 -0
  35. data/_includes/components/difficulty-badge.html +84 -0
  36. data/_includes/components/email-preferences.html +200 -0
  37. data/_includes/components/enhanced-code-block.html +212 -0
  38. data/_includes/components/enhanced-metadata.html +228 -0
  39. data/_includes/components/enhanced-toc.html +209 -0
  40. data/_includes/components/hero.html +15 -0
  41. data/_includes/components/language-switcher.html +396 -0
  42. data/_includes/components/math-fallback.html +19 -0
  43. data/_includes/components/navigation-enhancements.html +454 -0
  44. data/_includes/components/newsletter-signup.html +178 -0
  45. data/_includes/components/open-science-badges.html +16 -0
  46. data/_includes/components/package-install.html +194 -0
  47. data/_includes/components/performance-monitor.html +170 -0
  48. data/_includes/components/popular-posts.html +233 -0
  49. data/_includes/components/post-hero.html +62 -0
  50. data/_includes/components/reading-progress.html +133 -0
  51. data/_includes/components/reading-time.html +121 -0
  52. data/_includes/components/responsive-image.html +63 -0
  53. data/_includes/components/search-facets.html +307 -0
  54. data/_includes/components/series-navigation.html +124 -0
  55. data/_includes/components/social-proof.html +34 -0
  56. data/_includes/components/social-share.html +119 -0
  57. data/_includes/components/user-preferences.html +566 -0
  58. data/_includes/components/visualization-card.html +24 -0
  59. data/_includes/components/viz-table-fallback.html +19 -0
  60. data/_includes/critical-css/default.html +0 -0
  61. data/_includes/critical-css/home.html +0 -0
  62. data/_includes/critical-css/post.html +0 -0
  63. data/_includes/csp-meta.html +17 -0
  64. data/_includes/footer/nav-column.html +31 -0
  65. data/_includes/footer.html +76 -0
  66. data/_includes/head.html +337 -0
  67. data/_includes/header/navigation.html +48 -0
  68. data/_includes/header.html +70 -0
  69. data/_includes/helpers/array-to-sentence.html +21 -0
  70. data/_includes/helpers/date-format.html +16 -0
  71. data/_includes/helpers/link-with-icon.html +26 -0
  72. data/_includes/layouts/default/article.html +67 -0
  73. data/_includes/meta/language-attributes.html +25 -0
  74. data/_includes/meta/math-config.html +21 -0
  75. data/_includes/meta/schema.html +153 -0
  76. data/_includes/meta/scripts-loader.html +52 -0
  77. data/_includes/post/related-posts.html +63 -0
  78. data/_includes/scripts.html +72 -0
  79. data/_includes/skip-link.html +1 -0
  80. data/_includes/toc.html +2 -0
  81. data/_layouts/archive.html +282 -0
  82. data/_layouts/dataset.html +52 -0
  83. data/_layouts/default.html +33 -0
  84. data/_layouts/home.html +48 -0
  85. data/_layouts/notebook.html +173 -0
  86. data/_layouts/package.html +238 -0
  87. data/_layouts/page.html +16 -0
  88. data/_layouts/portfolio.html +41 -0
  89. data/_layouts/post-sidebar.html +183 -0
  90. data/_layouts/post.html +338 -0
  91. data/_layouts/project.html +252 -0
  92. data/_layouts/research.html +504 -0
  93. data/_plugins/analytics_dashboard.rb +315 -0
  94. data/_plugins/config_validator.rb +397 -0
  95. data/_plugins/csp_generator.rb +115 -0
  96. data/_plugins/datalog_bibliography.rb +19 -0
  97. data/_plugins/datalog_comments.rb +18 -0
  98. data/_plugins/datalog_slides.rb +20 -0
  99. data/_plugins/front_matter_compat.rb +109 -0
  100. data/_plugins/i18n.rb +160 -0
  101. data/_plugins/image_optimizer.rb +445 -0
  102. data/_plugins/math_preprocessor.rb +185 -0
  103. data/_plugins/notebook_converter.rb +1107 -0
  104. data/_plugins/plugin_loader.rb +185 -0
  105. data/_plugins/publications_generator.rb +234 -0
  106. data/_plugins/reading_time.rb +15 -0
  107. data/_plugins/search_normalizer.rb +74 -0
  108. data/_plugins/sri_filter.rb +18 -0
  109. data/_plugins/toc_filter.rb +68 -0
  110. data/_plugins/warning_filter.rb +3 -0
  111. data/_sass/_base.scss +142 -0
  112. data/_sass/_components.scss +2380 -0
  113. data/_sass/_header.scss +470 -0
  114. data/_sass/_interactive.scss +344 -0
  115. data/_sass/_layout.scss +1760 -0
  116. data/_sass/_mathematical.scss +368 -0
  117. data/_sass/_mixins.scss +99 -0
  118. data/_sass/_package-docs.scss +715 -0
  119. data/_sass/_phase1-enhancements.scss +721 -0
  120. data/_sass/_phase3-enhancements.scss +874 -0
  121. data/_sass/_phase4-enhancements.scss +1214 -0
  122. data/_sass/_phase5-enhancements.scss +414 -0
  123. data/_sass/_search.scss +654 -0
  124. data/_sass/_syntax-highlighting.scss +128 -0
  125. data/_sass/_theme.scss +79 -0
  126. data/_sass/_typography.scss +148 -0
  127. data/_sass/_utilities.scss +334 -0
  128. data/_sass/_variables.scss +153 -0
  129. data/_sass/_visualizations.scss +242 -0
  130. data/assets/css/main.scss +4 -0
  131. data/assets/img/20220607123041_detail.001.png +0 -0
  132. data/assets/img/favicons/android-chrome-192x192.png +0 -0
  133. data/assets/img/favicons/android-chrome-512x512.png +0 -0
  134. data/assets/img/favicons/apple-touch-icon.png +0 -0
  135. data/assets/img/favicons/favicon-16x16.png +0 -0
  136. data/assets/img/favicons/favicon-32x32.png +0 -0
  137. data/assets/img/favicons/favicon.ico +0 -0
  138. data/assets/img/favicons/site.webmanifest +21 -0
  139. data/assets/img/portfolio-placeholder.svg +20 -0
  140. data/assets/js/academic.js +262 -0
  141. data/assets/js/analytics-dashboard.js +382 -0
  142. data/assets/js/core/dark-mode.js +79 -0
  143. data/assets/js/core/github-cards.js +123 -0
  144. data/assets/js/core/language-filter.js +69 -0
  145. data/assets/js/core/navigation.js +184 -0
  146. data/assets/js/core/scroll-progress.js +45 -0
  147. data/assets/js/core/search-hotkeys.js +62 -0
  148. data/assets/js/core/skip-links.js +62 -0
  149. data/assets/js/loader.js +163 -0
  150. data/assets/js/main.js +23 -0
  151. data/assets/js/math.js +818 -0
  152. data/assets/js/notebook.js +158 -0
  153. data/assets/js/search/analytics.js +91 -0
  154. data/assets/js/search/app.js +271 -0
  155. data/assets/js/search/autocomplete.js +120 -0
  156. data/assets/js/search/engine.js +260 -0
  157. data/assets/js/search/filters.js +38 -0
  158. data/assets/js/search/render.js +217 -0
  159. data/assets/js/search/utils.js +99 -0
  160. data/assets/js/search.js +354 -0
  161. data/assets/js/visualizations.js +816 -0
  162. data/assets/publications/datalog-publications.bib +8 -0
  163. data/assets/publications/datalog-publications.ris +9 -0
  164. data/assets/publications/publications.bib +30 -0
  165. data/assets/templates/diogo-ribeiro-cv.md +31 -0
  166. data/assets/templates/diogo-ribeiro-cv.tex +32 -0
  167. data/bin/datalog +26 -0
  168. data/datalog-theme.gemspec +72 -0
  169. data/lib/datalog/cli.rb +563 -0
  170. data/lib/datalog/plugin_system/dependency_resolver.rb +223 -0
  171. data/lib/datalog/plugin_system.rb +237 -0
  172. data/lib/datalog/plugins/citations.rb +182 -0
  173. data/lib/datalog/plugins/comments.rb +183 -0
  174. data/lib/datalog/plugins/search.rb +45 -0
  175. data/lib/datalog/plugins/slides.rb +121 -0
  176. data/lib/datalog/theme/theme.rb +18 -0
  177. data/lib/datalog/theme/version.rb +7 -0
  178. data/lib/datalog/warning_filter.rb +28 -0
  179. data/lib/datalog-theme.rb +17 -0
  180. metadata +555 -0
@@ -0,0 +1,1760 @@
1
+ @use 'sass:map';
2
+ @use 'sass:color';
3
+
4
+ @use 'variables' as *;
5
+ @use 'mixins' as *;
6
+
7
+ // ---------------------------------------------------------------------------
8
+ // Container
9
+ // ---------------------------------------------------------------------------
10
+ .container {
11
+ width: min(100% - 2rem, $max-width-wide);
12
+ margin-inline: auto;
13
+ padding-inline: map.get($spacing, 3);
14
+
15
+ @include respond-to(md) {
16
+ padding-inline: map.get($spacing, 5);
17
+ }
18
+ }
19
+
20
+ .container--narrow {
21
+ width: min(100% - 2rem, $max-width-reading + 4rem);
22
+ }
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Main content
26
+ // ---------------------------------------------------------------------------
27
+ main {
28
+ padding: map.get($spacing, 6) 0 map.get($spacing, 8);
29
+ }
30
+
31
+ .site-main {
32
+ min-height: 60vh;
33
+ }
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Page structure
37
+ // ---------------------------------------------------------------------------
38
+ .page-shell {
39
+ display: grid;
40
+ gap: map.get($spacing, 6);
41
+ }
42
+
43
+ .page-meta {
44
+ display: flex;
45
+ flex-wrap: wrap;
46
+ gap: map.get($spacing, 3);
47
+ font-size: map.get($font-size-scale, sm);
48
+ color: $color-ink-500;
49
+ }
50
+
51
+ body.dark-mode .page-meta {
52
+ color: rgba($dark-ink-500, 0.8);
53
+ }
54
+
55
+ .sidebar-layout {
56
+ display: grid;
57
+ gap: map.get($spacing, 6);
58
+
59
+ @include respond-to(lg) {
60
+ grid-template-columns: minmax(0, 3fr) minmax(16rem, 1fr);
61
+ align-items: start;
62
+ }
63
+ }
64
+
65
+ aside {
66
+ position: sticky;
67
+ top: 6.5rem;
68
+ }
69
+
70
+ // ---------------------------------------------------------------------------
71
+ // Hero
72
+ // ---------------------------------------------------------------------------
73
+ .hero {
74
+ position: relative;
75
+ // Solid base under the image: keeps the white text legible if the image
76
+ // fails to load, and gives contrast checkers a real colour to measure.
77
+ background: $color-surface-900 var(--hero-bg) center / cover no-repeat;
78
+ color: #fff;
79
+ padding: clamp(4rem, 10vw, 8rem) 0;
80
+ text-align: center;
81
+ overflow: hidden;
82
+ }
83
+
84
+ .hero-overlay {
85
+ position: absolute;
86
+ inset: 0;
87
+ // The accent end used to be rgba($color-accent-primary, 0.55). Over a light
88
+ // hero image that composites to #87a9f4, where the white hero text measures
89
+ // 2.33:1 -- below AA. Blending toward the navy and raising the alpha puts the
90
+ // worst case at 5.17:1 regardless of how bright the image is.
91
+ background: linear-gradient(
92
+ 160deg,
93
+ rgba($color-surface-900, 0.82) 0%,
94
+ rgba(color.mix($color-accent-primary, $color-surface-900, 60%), 0.8) 100%
95
+ );
96
+ pointer-events: none;
97
+ }
98
+
99
+ .hero-content {
100
+ position: relative;
101
+ z-index: 1;
102
+
103
+ h1 {
104
+ font-size: map.get($font-size-scale, display);
105
+ font-weight: 800;
106
+ margin: 0 0 map.get($spacing, 3);
107
+ line-height: 1.15;
108
+ letter-spacing: -0.02em;
109
+ text-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
110
+ }
111
+ }
112
+
113
+ .hero-tagline {
114
+ font-size: map.get($font-size-scale, md);
115
+ opacity: 0.9;
116
+ max-width: 48ch;
117
+ margin: 0 auto map.get($spacing, 5);
118
+ line-height: $line-height-loose;
119
+ font-weight: 300;
120
+ text-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
121
+ }
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // Sections
125
+ // ---------------------------------------------------------------------------
126
+ .section {
127
+ padding: clamp(2.5rem, 5vw, 4rem) 0;
128
+ }
129
+
130
+ .section-highlight {
131
+ background: $color-surface-100;
132
+ border-top: 1px solid rgba($color-neutral-200, 0.5);
133
+ border-bottom: 1px solid rgba($color-neutral-200, 0.5);
134
+ }
135
+
136
+ body.dark-mode .section-highlight {
137
+ background: rgba($dark-surface-200, 0.5);
138
+ border-color: rgba($dark-ink-500, 0.1);
139
+ }
140
+
141
+ .section-portfolio {
142
+ background: $color-surface-50;
143
+ }
144
+
145
+ body.dark-mode .section-portfolio {
146
+ background: $dark-surface-100;
147
+ }
148
+
149
+ .section-title {
150
+ font-size: map.get($font-size-scale, xxl);
151
+ font-weight: 700;
152
+ margin: 0 0 map.get($spacing, 2);
153
+ color: $color-ink-900;
154
+ letter-spacing: -0.01em;
155
+
156
+ &::after {
157
+ content: '';
158
+ display: block;
159
+ width: 3rem;
160
+ height: 3px;
161
+ background: $color-accent-primary;
162
+ border-radius: 2px;
163
+ margin-top: map.get($spacing, 2);
164
+ margin-bottom: map.get($spacing, 4);
165
+ }
166
+ }
167
+
168
+ body.dark-mode .section-title {
169
+ color: $dark-ink-900;
170
+
171
+ &::after {
172
+ background: $dark-accent-primary;
173
+ }
174
+ }
175
+
176
+ // ---------------------------------------------------------------------------
177
+ // Card grid
178
+ // ---------------------------------------------------------------------------
179
+ .card-grid {
180
+ display: grid;
181
+ gap: map.get($spacing, 5);
182
+ grid-template-columns: 1fr;
183
+
184
+ @include respond-to(sm) {
185
+ grid-template-columns: repeat(2, 1fr);
186
+ }
187
+
188
+ @include respond-to(lg) {
189
+ grid-template-columns: repeat(3, 1fr);
190
+ }
191
+ }
192
+
193
+ .card-meta {
194
+ font-size: map.get($font-size-scale, xs);
195
+ color: $color-ink-500;
196
+ margin: 0;
197
+ display: flex;
198
+ align-items: center;
199
+ gap: 0.375rem;
200
+ }
201
+
202
+ body.dark-mode .card-meta {
203
+ color: $dark-ink-500;
204
+ }
205
+
206
+ .card-link {
207
+ display: inline-flex;
208
+ align-items: center;
209
+ gap: 0.25rem;
210
+ font-size: map.get($font-size-scale, sm);
211
+ font-weight: 600;
212
+ color: $color-accent-primary;
213
+ text-decoration: none;
214
+ margin-top: auto;
215
+ padding-top: map.get($spacing, 2);
216
+ transition: gap $transition-base;
217
+
218
+ &::after {
219
+ content: '\2192';
220
+ transition: transform $transition-base;
221
+ }
222
+
223
+ &:hover {
224
+ &::after {
225
+ transform: translateX(3px);
226
+ }
227
+ }
228
+ }
229
+
230
+ body.dark-mode .card-link {
231
+ color: $dark-accent-primary;
232
+ }
233
+
234
+ // ---------------------------------------------------------------------------
235
+ // Old footer class (kept for compatibility)
236
+ // ---------------------------------------------------------------------------
237
+ .footer {
238
+ background: $color-surface-100;
239
+ color: $color-ink-700;
240
+ padding: map.get($spacing, 7) 0;
241
+ margin-top: map.get($spacing, 8);
242
+ border-top: 1px solid rgba($color-neutral-200, 0.9);
243
+ }
244
+
245
+ body.dark-mode .footer {
246
+ background: $dark-surface-200;
247
+ color: $dark-ink-700;
248
+ border-top-color: rgba($dark-ink-500, 0.25);
249
+ }
250
+
251
+ // ---------------------------------------------------------------------------
252
+ // Site footer
253
+ // ---------------------------------------------------------------------------
254
+ .site-footer {
255
+ background: $color-surface-900;
256
+ color: rgba(#fff, 0.75);
257
+ padding: clamp(2.5rem, 5vw, 4rem) 0 map.get($spacing, 5);
258
+ margin-top: 0;
259
+ font-size: map.get($font-size-scale, sm);
260
+ line-height: $line-height-base;
261
+
262
+ h3 {
263
+ color: #fff;
264
+ font-size: map.get($font-size-scale, sm);
265
+ font-weight: 700;
266
+ text-transform: uppercase;
267
+ letter-spacing: 0.06em;
268
+ margin: 0 0 map.get($spacing, 3);
269
+ }
270
+
271
+ a {
272
+ color: rgba(#fff, 0.85);
273
+ text-decoration: none;
274
+ transition: color $transition-base;
275
+
276
+ &:hover {
277
+ color: $dark-accent-secondary;
278
+ }
279
+ }
280
+
281
+ p {
282
+ margin: 0 0 map.get($spacing, 2);
283
+ }
284
+ }
285
+
286
+ .footer-container {
287
+ display: grid;
288
+ gap: map.get($spacing, 6);
289
+ grid-template-columns: 1fr;
290
+
291
+ @include respond-to(sm) {
292
+ grid-template-columns: repeat(2, 1fr);
293
+ }
294
+
295
+ @include respond-to(lg) {
296
+ grid-template-columns: repeat(4, 1fr);
297
+ }
298
+ }
299
+
300
+ .footer-description {
301
+ margin: 0 0 map.get($spacing, 3);
302
+ }
303
+
304
+ .footer-link-list {
305
+ list-style: none;
306
+ padding: 0;
307
+ margin: 0;
308
+ display: flex;
309
+ flex-direction: column;
310
+ gap: map.get($spacing, 1);
311
+ }
312
+
313
+ .footer-nav-columns {
314
+ display: grid;
315
+ gap: map.get($spacing, 4);
316
+
317
+ @include respond-to(md) {
318
+ grid-template-columns: repeat(3, 1fr);
319
+ }
320
+ }
321
+
322
+ .footer-form {
323
+ display: flex;
324
+ gap: map.get($spacing, 2);
325
+ max-width: 24rem;
326
+
327
+ input[type="email"] {
328
+ flex: 1;
329
+ min-width: 0;
330
+ padding: map.get($spacing, 2) map.get($spacing, 3);
331
+ border: 1px solid rgba(#fff, 0.15);
332
+ border-radius: map.get($radius, md);
333
+ background: rgba(#fff, 0.08);
334
+ color: #fff;
335
+ font-size: map.get($font-size-scale, sm);
336
+
337
+ &::placeholder { color: rgba(#fff, 0.4); }
338
+ &:focus {
339
+ outline: none;
340
+ border-color: $dark-accent-secondary;
341
+ }
342
+ }
343
+
344
+ button {
345
+ padding: map.get($spacing, 2) map.get($spacing, 4);
346
+ background: $color-accent-primary;
347
+ color: #fff;
348
+ border: none;
349
+ border-radius: map.get($radius, md);
350
+ cursor: pointer;
351
+ font-weight: 600;
352
+ font-size: map.get($font-size-scale, sm);
353
+ white-space: nowrap;
354
+ transition: background-color $transition-base;
355
+
356
+ &:hover { background: color.adjust($color-accent-primary, $lightness: 8%); }
357
+ }
358
+ }
359
+
360
+ .footer-privacy-note {
361
+ font-size: map.get($font-size-scale, xs);
362
+ opacity: 0.5;
363
+ margin-top: map.get($spacing, 1);
364
+ }
365
+
366
+ .footer-author {
367
+ strong { color: #fff; }
368
+ }
369
+
370
+ .footer-affiliation {
371
+ display: block;
372
+ opacity: 0.65;
373
+ font-size: map.get($font-size-scale, xs);
374
+ }
375
+
376
+ .footer-citation,
377
+ .footer-orcid,
378
+ .footer-contact {
379
+ font-size: map.get($font-size-scale, xs);
380
+ opacity: 0.65;
381
+ }
382
+
383
+ .footer-tagline {
384
+ font-style: italic;
385
+ opacity: 0.7;
386
+ }
387
+
388
+ .footer-feeds {
389
+ margin-top: map.get($spacing, 3);
390
+ }
391
+
392
+ .footer-bottom {
393
+ margin-top: map.get($spacing, 6);
394
+ padding-top: map.get($spacing, 4);
395
+ border-top: 1px solid rgba(#fff, 0.1);
396
+ text-align: center;
397
+ font-size: map.get($font-size-scale, xs);
398
+ opacity: 0.5;
399
+
400
+ p { margin: 0; }
401
+ }
402
+
403
+ // ---------------------------------------------------------------------------
404
+ // Research page
405
+ // ---------------------------------------------------------------------------
406
+ .research-intro {
407
+ margin-bottom: map.get($spacing, 5);
408
+ padding-bottom: map.get($spacing, 5);
409
+ border-bottom: 1px solid rgba($color-neutral-200, 0.5);
410
+
411
+ .research-bio {
412
+ font-size: map.get($font-size-scale, lg);
413
+ color: $color-ink-700;
414
+ line-height: $line-height-loose;
415
+ margin: 0 0 map.get($spacing, 2);
416
+ }
417
+
418
+ p:last-child {
419
+ margin: 0;
420
+ font-size: map.get($font-size-scale, sm);
421
+ color: $color-ink-500;
422
+ }
423
+ }
424
+
425
+ body.dark-mode .research-intro {
426
+ border-bottom-color: rgba($dark-ink-500, 0.12);
427
+ .research-bio { color: $dark-ink-700; }
428
+ p:last-child { color: $dark-ink-500; }
429
+ }
430
+
431
+ .research-metrics {
432
+ display: grid;
433
+ grid-template-columns: repeat(3, 1fr);
434
+ gap: map.get($spacing, 4);
435
+ margin-bottom: map.get($spacing, 6);
436
+
437
+ .card {
438
+ text-align: center;
439
+ padding: map.get($spacing, 5) map.get($spacing, 3);
440
+ }
441
+
442
+ .stat__value {
443
+ display: block;
444
+ font-size: 2.5rem;
445
+ font-weight: 800;
446
+ font-family: $font-family-mono;
447
+ color: $color-accent-tertiary;
448
+ line-height: 1;
449
+ margin-bottom: map.get($spacing, 1);
450
+ }
451
+
452
+ .stat__label {
453
+ display: block;
454
+ font-size: map.get($font-size-scale, xs);
455
+ text-transform: uppercase;
456
+ letter-spacing: 0.08em;
457
+ color: $color-ink-500;
458
+ font-weight: 600;
459
+ }
460
+ }
461
+
462
+ body.dark-mode .research-metrics {
463
+ .stat__value { color: $dark-accent-tertiary; }
464
+ .stat__label { color: $dark-ink-500; }
465
+ }
466
+
467
+ .research-area-card {
468
+ text-decoration: none;
469
+ color: inherit;
470
+ transition: border-color $transition-base;
471
+
472
+ h3 {
473
+ color: $color-accent-secondary;
474
+ margin: 0;
475
+ }
476
+
477
+ p {
478
+ margin: 0;
479
+ }
480
+
481
+ &:hover {
482
+ border-color: rgba($color-accent-secondary, 0.4);
483
+ h3 { color: $color-accent-secondary; }
484
+ }
485
+ }
486
+
487
+ body.dark-mode .research-area-card {
488
+ h3 { color: $dark-accent-secondary; }
489
+ &:hover { border-color: rgba($dark-accent-secondary, 0.4); }
490
+ }
491
+
492
+ .research-profiles {
493
+ display: flex;
494
+ flex-wrap: wrap;
495
+ gap: map.get($spacing, 2);
496
+ }
497
+
498
+ // ---------------------------------------------------------------------------
499
+ // Author profile page
500
+ // ---------------------------------------------------------------------------
501
+ .author-profile {
502
+ margin-bottom: map.get($spacing, 6);
503
+ }
504
+
505
+ .author-profile__hero {
506
+ display: grid;
507
+ gap: map.get($spacing, 5);
508
+ padding-bottom: map.get($spacing, 6);
509
+ border-bottom: 1px solid rgba($color-neutral-200, 0.5);
510
+ margin-bottom: map.get($spacing, 5);
511
+
512
+ @include respond-to(md) {
513
+ grid-template-columns: 10rem 1fr;
514
+ align-items: start;
515
+ }
516
+ }
517
+
518
+ body.dark-mode .author-profile__hero {
519
+ border-bottom-color: rgba($dark-ink-500, 0.12);
520
+ }
521
+
522
+ .author-profile__photo {
523
+ width: 10rem;
524
+ height: 10rem;
525
+ border-radius: 50%;
526
+ overflow: hidden;
527
+ flex-shrink: 0;
528
+ justify-self: center;
529
+
530
+ @include respond-to(md) {
531
+ justify-self: start;
532
+ }
533
+ }
534
+
535
+ .author-profile__img {
536
+ width: 100%;
537
+ height: 100%;
538
+ object-fit: cover;
539
+ }
540
+
541
+ .author-profile__placeholder {
542
+ width: 100%;
543
+ height: 100%;
544
+ background: linear-gradient(135deg, rgba($color-accent-primary, 0.15), rgba($color-accent-tertiary, 0.2));
545
+ display: flex;
546
+ align-items: center;
547
+ justify-content: center;
548
+ font-size: 3.5rem;
549
+ font-weight: 800;
550
+ color: $color-accent-primary;
551
+ font-family: $font-family-serif;
552
+ }
553
+
554
+ body.dark-mode .author-profile__placeholder {
555
+ background: linear-gradient(135deg, rgba($dark-accent-primary, 0.2), rgba($dark-accent-tertiary, 0.25));
556
+ color: $dark-accent-primary;
557
+ }
558
+
559
+ .author-profile__intro {
560
+ display: grid;
561
+ gap: map.get($spacing, 2);
562
+ text-align: center;
563
+
564
+ @include respond-to(md) {
565
+ text-align: left;
566
+ }
567
+ }
568
+
569
+ .author-profile__name {
570
+ font-size: map.get($font-size-scale, xxl);
571
+ font-weight: 800;
572
+ margin: 0;
573
+ letter-spacing: -0.01em;
574
+ }
575
+
576
+ .author-profile__title {
577
+ font-size: map.get($font-size-scale, lg);
578
+ font-weight: 600;
579
+ color: $color-accent-primary;
580
+ margin: 0;
581
+ }
582
+
583
+ body.dark-mode .author-profile__title {
584
+ color: $dark-accent-primary;
585
+ }
586
+
587
+ .author-profile__affiliation {
588
+ color: $color-ink-500;
589
+ font-size: map.get($font-size-scale, sm);
590
+ margin: 0;
591
+ }
592
+
593
+ body.dark-mode .author-profile__affiliation {
594
+ color: $dark-ink-500;
595
+ }
596
+
597
+ .author-profile__bio {
598
+ font-size: map.get($font-size-scale, base);
599
+ color: $color-ink-700;
600
+ line-height: $line-height-loose;
601
+ margin: map.get($spacing, 2) 0;
602
+ max-width: 52ch;
603
+ }
604
+
605
+ body.dark-mode .author-profile__bio {
606
+ color: $dark-ink-700;
607
+ }
608
+
609
+ // ---------------------------------------------------------------------------
610
+ // Skills grid
611
+ // ---------------------------------------------------------------------------
612
+ .skills-grid {
613
+ display: grid;
614
+ gap: map.get($spacing, 4);
615
+
616
+ @include respond-to(sm) {
617
+ grid-template-columns: repeat(2, 1fr);
618
+ }
619
+ }
620
+
621
+ .skills-tags {
622
+ display: flex;
623
+ flex-wrap: wrap;
624
+ gap: map.get($spacing, 1);
625
+ margin-top: map.get($spacing, 2);
626
+ }
627
+
628
+ .skill-tag {
629
+ display: inline-block;
630
+ padding: 0.2rem 0.625rem;
631
+ border-radius: 999px;
632
+ font-size: map.get($font-size-scale, xs);
633
+ font-weight: 500;
634
+ }
635
+
636
+ .skill-tag--lang {
637
+ background: rgba($color-accent-primary, 0.08);
638
+ color: $color-accent-primary;
639
+ }
640
+
641
+ .skill-tag--framework {
642
+ background: rgba($color-accent-secondary, 0.08);
643
+ color: $color-accent-secondary;
644
+ }
645
+
646
+ .skill-tag--tool {
647
+ background: rgba($color-accent-tertiary, 0.08);
648
+ color: $color-accent-tertiary;
649
+ }
650
+
651
+ .skill-tag--domain {
652
+ background: rgba($color-ink-500, 0.08);
653
+ color: $color-ink-700;
654
+ }
655
+
656
+ body.dark-mode {
657
+ .skill-tag--lang { background: rgba($dark-accent-primary, 0.12); color: $dark-accent-primary; }
658
+ .skill-tag--framework { background: rgba($dark-accent-secondary, 0.12); color: $dark-accent-secondary; }
659
+ .skill-tag--tool { background: rgba($dark-accent-tertiary, 0.12); color: $dark-accent-tertiary; }
660
+ .skill-tag--domain { background: rgba($dark-ink-500, 0.12); color: $dark-ink-700; }
661
+ }
662
+
663
+ // ---------------------------------------------------------------------------
664
+ // Timeline
665
+ // ---------------------------------------------------------------------------
666
+ .timeline {
667
+ display: grid;
668
+ gap: map.get($spacing, 4);
669
+ padding-left: map.get($spacing, 5);
670
+ position: relative;
671
+
672
+ &::before {
673
+ content: '';
674
+ position: absolute;
675
+ left: 0.5rem;
676
+ top: 0.5rem;
677
+ bottom: 0.5rem;
678
+ width: 2px;
679
+ background: rgba($color-neutral-200, 0.8);
680
+ border-radius: 1px;
681
+ }
682
+ }
683
+
684
+ body.dark-mode .timeline::before {
685
+ background: rgba($dark-ink-500, 0.2);
686
+ }
687
+
688
+ .timeline__item {
689
+ position: relative;
690
+ }
691
+
692
+ .timeline__marker {
693
+ position: absolute;
694
+ left: calc(-1 * map.get($spacing, 5) + 0.25rem);
695
+ top: map.get($spacing, 4);
696
+ width: 0.625rem;
697
+ height: 0.625rem;
698
+ border-radius: 50%;
699
+ background: $color-accent-primary;
700
+ border: 2px solid white;
701
+ box-shadow: 0 0 0 2px rgba($color-accent-primary, 0.2);
702
+ }
703
+
704
+ body.dark-mode .timeline__marker {
705
+ background: $dark-accent-primary;
706
+ border-color: $dark-surface-200;
707
+ box-shadow: 0 0 0 2px rgba($dark-accent-primary, 0.2);
708
+ }
709
+
710
+ .timeline__content {
711
+ h3 { margin: 0; }
712
+ p { margin: map.get($spacing, 1) 0 0; }
713
+ }
714
+
715
+ .timeline__org {
716
+ color: $color-ink-500;
717
+ font-size: map.get($font-size-scale, sm);
718
+ }
719
+
720
+ body.dark-mode .timeline__org {
721
+ color: $dark-ink-500;
722
+ }
723
+
724
+ // ---------------------------------------------------------------------------
725
+ // Year chart (citations by year)
726
+ // ---------------------------------------------------------------------------
727
+ .year-chart {
728
+ display: flex;
729
+ align-items: flex-end;
730
+ justify-content: space-between;
731
+ gap: map.get($spacing, 2);
732
+ height: 10rem;
733
+ padding: map.get($spacing, 3) 0;
734
+ }
735
+
736
+ .year-chart__bar {
737
+ flex: 1;
738
+ display: flex;
739
+ flex-direction: column;
740
+ align-items: center;
741
+ justify-content: flex-end;
742
+ height: 100%;
743
+ gap: map.get($spacing, 1);
744
+ }
745
+
746
+ .year-chart__value {
747
+ font-size: map.get($font-size-scale, xs);
748
+ font-weight: 700;
749
+ font-family: $font-family-mono;
750
+ color: $color-ink-700;
751
+ }
752
+
753
+ body.dark-mode .year-chart__value {
754
+ color: $dark-ink-700;
755
+ }
756
+
757
+ .year-chart__fill {
758
+ width: 100%;
759
+ max-width: 3rem;
760
+ height: var(--bar-height, 10%);
761
+ min-height: 4px;
762
+ background: linear-gradient(to top, $color-accent-primary, $color-accent-tertiary);
763
+ border-radius: map.get($radius, sm) map.get($radius, sm) 0 0;
764
+ transition: height 0.3s ease;
765
+ }
766
+
767
+ .year-chart__label {
768
+ font-size: map.get($font-size-scale, xs);
769
+ color: $color-ink-500;
770
+ }
771
+
772
+ body.dark-mode .year-chart__label {
773
+ color: $dark-ink-500;
774
+ }
775
+
776
+ // ---------------------------------------------------------------------------
777
+ // Project cards
778
+ // ---------------------------------------------------------------------------
779
+ .project-card {
780
+ .project-metrics {
781
+ display: flex;
782
+ gap: map.get($spacing, 4);
783
+ padding: map.get($spacing, 3) 0;
784
+ border-top: 1px solid rgba($color-neutral-200, 0.5);
785
+ margin-top: auto;
786
+ }
787
+
788
+ .project-links {
789
+ display: flex;
790
+ gap: map.get($spacing, 2);
791
+ padding-top: map.get($spacing, 2);
792
+ }
793
+ }
794
+
795
+ .project-metric {
796
+ display: flex;
797
+ flex-direction: column;
798
+ }
799
+
800
+ .project-metric__value {
801
+ font-size: map.get($font-size-scale, lg);
802
+ font-weight: 700;
803
+ font-family: $font-family-mono;
804
+ color: $color-accent-primary;
805
+ line-height: 1;
806
+ }
807
+
808
+ body.dark-mode .project-metric__value {
809
+ color: $dark-accent-primary;
810
+ }
811
+
812
+ .project-metric__label {
813
+ font-size: map.get($font-size-scale, xs);
814
+ color: $color-ink-500;
815
+ text-transform: uppercase;
816
+ letter-spacing: 0.04em;
817
+ }
818
+
819
+ body.dark-mode .project-metric__label {
820
+ color: $dark-ink-500;
821
+ }
822
+
823
+ body.dark-mode .project-card .project-metrics {
824
+ border-top-color: rgba($dark-ink-500, 0.12);
825
+ }
826
+
827
+ // ---------------------------------------------------------------------------
828
+ // Blog post list
829
+ // ---------------------------------------------------------------------------
830
+ .post-list {
831
+ display: grid;
832
+ gap: 0;
833
+ }
834
+
835
+ .post-list-item {
836
+ display: grid;
837
+ gap: map.get($spacing, 2);
838
+ padding: map.get($spacing, 5) 0;
839
+ border-bottom: 1px solid rgba($color-neutral-200, 0.6);
840
+
841
+ &:first-child {
842
+ padding-top: 0;
843
+ }
844
+
845
+ &:last-child {
846
+ border-bottom: none;
847
+ }
848
+ }
849
+
850
+ body.dark-mode .post-list-item {
851
+ border-bottom-color: rgba($dark-ink-500, 0.12);
852
+ }
853
+
854
+ .post-list-item__title {
855
+ font-size: map.get($font-size-scale, xl);
856
+ font-weight: 700;
857
+ margin: 0;
858
+ line-height: $line-height-tight;
859
+
860
+ a {
861
+ color: $color-ink-900;
862
+ text-decoration: none;
863
+ transition: color $transition-base;
864
+
865
+ &:hover { color: $color-accent-primary; }
866
+ }
867
+ }
868
+
869
+ body.dark-mode .post-list-item__title a {
870
+ color: $dark-ink-900;
871
+ &:hover { color: $dark-accent-primary; }
872
+ }
873
+
874
+ .post-list-item__excerpt {
875
+ margin: 0;
876
+ color: $color-ink-500;
877
+ font-size: map.get($font-size-scale, base);
878
+ line-height: $line-height-base;
879
+ }
880
+
881
+ body.dark-mode .post-list-item__excerpt {
882
+ color: $dark-ink-500;
883
+ }
884
+
885
+ .post-list-item__tags {
886
+ display: flex;
887
+ flex-wrap: wrap;
888
+ gap: map.get($spacing, 1);
889
+ list-style: none;
890
+ margin: 0;
891
+ padding: 0;
892
+
893
+ li {
894
+ font-size: map.get($font-size-scale, xs);
895
+ padding: 0.125rem 0.5rem;
896
+ border-radius: 999px;
897
+ background: rgba($color-accent-tertiary, 0.08);
898
+ color: $color-accent-tertiary;
899
+ font-weight: 500;
900
+ }
901
+ }
902
+
903
+ body.dark-mode .post-list-item__tags li {
904
+ background: rgba($dark-accent-tertiary, 0.12);
905
+ color: $dark-accent-tertiary;
906
+ }
907
+
908
+ .post-meta {
909
+ display: flex;
910
+ align-items: center;
911
+ gap: map.get($spacing, 2);
912
+ font-size: map.get($font-size-scale, sm);
913
+ color: $color-ink-500;
914
+ margin: 0;
915
+ }
916
+
917
+ body.dark-mode .post-meta {
918
+ color: $dark-ink-500;
919
+ }
920
+
921
+ .post-meta__badge {
922
+ display: inline-block;
923
+ font-size: map.get($font-size-scale, xs);
924
+ font-weight: 600;
925
+ text-transform: capitalize;
926
+ padding: 0.1rem 0.5rem;
927
+ border-radius: map.get($radius, sm);
928
+ background: rgba($color-accent-secondary, 0.1);
929
+ color: $color-accent-secondary;
930
+ }
931
+
932
+ body.dark-mode .post-meta__badge {
933
+ background: rgba($dark-accent-secondary, 0.15);
934
+ color: $dark-accent-secondary;
935
+ }
936
+
937
+ // ---------------------------------------------------------------------------
938
+ // Pagination
939
+ // ---------------------------------------------------------------------------
940
+ .pagination {
941
+ padding-top: map.get($spacing, 5);
942
+ margin-top: map.get($spacing, 3);
943
+ border-top: 1px solid rgba($color-neutral-200, 0.6);
944
+ }
945
+
946
+ body.dark-mode .pagination {
947
+ border-top-color: rgba($dark-ink-500, 0.12);
948
+ }
949
+
950
+ .pagination__links {
951
+ display: flex;
952
+ align-items: center;
953
+ justify-content: center;
954
+ gap: map.get($spacing, 4);
955
+ }
956
+
957
+ .pagination__info {
958
+ font-size: map.get($font-size-scale, sm);
959
+ color: $color-ink-500;
960
+ }
961
+
962
+ body.dark-mode .pagination__info {
963
+ color: $dark-ink-500;
964
+ }
965
+
966
+ // ---------------------------------------------------------------------------
967
+ // Blog post layout
968
+ // ---------------------------------------------------------------------------
969
+ .post-wrapper {
970
+ display: grid;
971
+ gap: map.get($spacing, 5);
972
+ }
973
+
974
+ // Post metadata (published, author, reading time, difficulty)
975
+ .post-meta-list {
976
+ display: flex;
977
+ flex-wrap: wrap;
978
+ gap: map.get($spacing, 3) map.get($spacing, 5);
979
+ margin: 0;
980
+ padding: map.get($spacing, 4) 0;
981
+ border-bottom: 1px solid rgba($color-neutral-200, 0.5);
982
+
983
+ > div {
984
+ display: flex;
985
+ align-items: baseline;
986
+ gap: map.get($spacing, 2);
987
+ }
988
+ }
989
+
990
+ body.dark-mode .post-meta-list {
991
+ border-bottom-color: rgba($dark-ink-500, 0.12);
992
+ }
993
+
994
+ .post-meta-term {
995
+ font-size: map.get($font-size-scale, xs);
996
+ font-weight: 600;
997
+ text-transform: uppercase;
998
+ letter-spacing: 0.04em;
999
+ color: $color-ink-500;
1000
+ }
1001
+
1002
+ body.dark-mode .post-meta-term {
1003
+ color: $dark-ink-500;
1004
+ }
1005
+
1006
+ .post-meta-definition {
1007
+ font-size: map.get($font-size-scale, sm);
1008
+ color: $color-ink-700;
1009
+ margin: 0;
1010
+ }
1011
+
1012
+ body.dark-mode .post-meta-definition {
1013
+ color: $dark-ink-700;
1014
+ }
1015
+
1016
+ .post-author-affiliation {
1017
+ display: block;
1018
+ font-size: map.get($font-size-scale, xs);
1019
+ color: $color-ink-500;
1020
+ }
1021
+
1022
+ body.dark-mode .post-author-affiliation {
1023
+ color: $dark-ink-500;
1024
+ }
1025
+
1026
+ .post-notebook-link {
1027
+ font-weight: 500;
1028
+ }
1029
+
1030
+ // Tags & categories
1031
+ .post-taxonomy {
1032
+ padding: map.get($spacing, 3) 0;
1033
+ }
1034
+
1035
+ .post-tag-list {
1036
+ display: flex;
1037
+ flex-wrap: wrap;
1038
+ gap: map.get($spacing, 2);
1039
+ list-style: none;
1040
+ margin: 0;
1041
+ padding: 0;
1042
+ }
1043
+
1044
+ .post-tag {
1045
+ display: inline-block;
1046
+ padding: 0.2rem 0.625rem;
1047
+ border-radius: 999px;
1048
+ font-size: map.get($font-size-scale, xs);
1049
+ font-weight: 500;
1050
+ text-decoration: none;
1051
+ background: rgba($color-accent-tertiary, 0.08);
1052
+ color: $color-accent-tertiary;
1053
+ transition: background $transition-base;
1054
+
1055
+ &:hover {
1056
+ background: rgba($color-accent-tertiary, 0.15);
1057
+ }
1058
+ }
1059
+
1060
+ body.dark-mode .post-tag {
1061
+ background: rgba($dark-accent-tertiary, 0.12);
1062
+ color: $dark-accent-tertiary;
1063
+
1064
+ &:hover { background: rgba($dark-accent-tertiary, 0.2); }
1065
+ }
1066
+
1067
+ .post-category {
1068
+ background: rgba($color-accent-secondary, 0.08);
1069
+ color: $color-accent-secondary;
1070
+
1071
+ &:hover { background: rgba($color-accent-secondary, 0.15); }
1072
+ }
1073
+
1074
+ body.dark-mode .post-category {
1075
+ background: rgba($dark-accent-secondary, 0.12);
1076
+ color: $dark-accent-secondary;
1077
+ }
1078
+
1079
+ // Post body + TOC sidebar
1080
+ .post-body-wrapper {
1081
+ display: grid;
1082
+ gap: map.get($spacing, 5);
1083
+
1084
+ @include respond-to(lg) {
1085
+ grid-template-columns: 14rem minmax(0, 1fr);
1086
+ }
1087
+ }
1088
+
1089
+ .post-content {
1090
+ min-width: 0;
1091
+
1092
+ h2 { margin-top: map.get($spacing, 6); }
1093
+ h3 { margin-top: map.get($spacing, 5); }
1094
+
1095
+ img {
1096
+ border-radius: map.get($radius, md);
1097
+ margin: map.get($spacing, 4) 0;
1098
+ }
1099
+
1100
+ blockquote {
1101
+ border-left: 3px solid $color-accent-primary;
1102
+ padding-left: map.get($spacing, 4);
1103
+ margin-left: 0;
1104
+ color: $color-ink-700;
1105
+ font-style: italic;
1106
+ }
1107
+
1108
+ table {
1109
+ width: 100%;
1110
+ border-collapse: collapse;
1111
+ font-size: map.get($font-size-scale, sm);
1112
+ margin: map.get($spacing, 4) 0;
1113
+
1114
+ th, td {
1115
+ padding: map.get($spacing, 2) map.get($spacing, 3);
1116
+ border: 1px solid rgba($color-neutral-200, 0.6);
1117
+ text-align: left;
1118
+ }
1119
+
1120
+ th {
1121
+ background: rgba($color-neutral-100, 0.5);
1122
+ font-weight: 600;
1123
+ }
1124
+ }
1125
+ }
1126
+
1127
+ body.dark-mode .post-content {
1128
+ blockquote {
1129
+ border-left-color: $dark-accent-primary;
1130
+ color: $dark-ink-700;
1131
+ }
1132
+
1133
+ table {
1134
+ th, td { border-color: rgba($dark-ink-500, 0.15); }
1135
+ th { background: rgba($dark-surface-300, 0.5); }
1136
+ }
1137
+ }
1138
+
1139
+ // Code blocks
1140
+ .code-block-wrapper {
1141
+ border: 1px solid rgba($color-neutral-200, 0.5);
1142
+ border-radius: map.get($radius, lg);
1143
+ overflow: hidden;
1144
+ margin: map.get($spacing, 4) 0;
1145
+ }
1146
+
1147
+ body.dark-mode .code-block-wrapper {
1148
+ border-color: rgba($dark-ink-500, 0.15);
1149
+ }
1150
+
1151
+ .code-block-meta {
1152
+ display: flex;
1153
+ justify-content: space-between;
1154
+ align-items: center;
1155
+ padding: map.get($spacing, 2) map.get($spacing, 3);
1156
+ background: rgba($color-neutral-100, 0.5);
1157
+ border-bottom: 1px solid rgba($color-neutral-200, 0.3);
1158
+ font-size: map.get($font-size-scale, xs);
1159
+ }
1160
+
1161
+ body.dark-mode .code-block-meta {
1162
+ background: rgba($dark-surface-300, 0.5);
1163
+ border-bottom-color: rgba($dark-ink-500, 0.1);
1164
+ }
1165
+
1166
+ .code-language {
1167
+ font-family: $font-family-mono;
1168
+ font-weight: 600;
1169
+ color: $color-accent-secondary;
1170
+ letter-spacing: 0.03em;
1171
+ }
1172
+
1173
+ body.dark-mode .code-language {
1174
+ color: $dark-accent-secondary;
1175
+ }
1176
+
1177
+ .code-copy {
1178
+ padding: 0.2rem 0.5rem;
1179
+ border: 1px solid rgba($color-neutral-200, 0.5);
1180
+ border-radius: map.get($radius, sm);
1181
+ background: transparent;
1182
+ font-size: map.get($font-size-scale, xs);
1183
+ font-weight: 500;
1184
+ cursor: pointer;
1185
+ color: $color-ink-500;
1186
+ transition: background $transition-base, color $transition-base;
1187
+
1188
+ &:hover {
1189
+ background: rgba($color-accent-primary, 0.06);
1190
+ color: $color-accent-primary;
1191
+ }
1192
+
1193
+ &.is-copied {
1194
+ background: rgba($color-success, 0.1);
1195
+ color: $color-success;
1196
+ border-color: rgba($color-success, 0.3);
1197
+ }
1198
+ }
1199
+
1200
+ body.dark-mode .code-copy {
1201
+ border-color: rgba($dark-ink-500, 0.2);
1202
+ color: $dark-ink-500;
1203
+
1204
+ &:hover {
1205
+ background: rgba($dark-accent-primary, 0.1);
1206
+ color: $dark-accent-primary;
1207
+ }
1208
+ }
1209
+
1210
+ // Interactive section
1211
+ .post-interactive {
1212
+ padding: map.get($spacing, 4) 0;
1213
+ font-size: map.get($font-size-scale, sm);
1214
+ color: $color-ink-500;
1215
+ }
1216
+
1217
+ body.dark-mode .post-interactive {
1218
+ color: $dark-ink-500;
1219
+ }
1220
+
1221
+ // Related posts
1222
+ .post-related {
1223
+ padding-top: map.get($spacing, 5);
1224
+ border-top: 1px solid rgba($color-neutral-200, 0.5);
1225
+ }
1226
+
1227
+ body.dark-mode .post-related {
1228
+ border-top-color: rgba($dark-ink-500, 0.12);
1229
+ }
1230
+
1231
+ .post-related-list {
1232
+ display: grid;
1233
+ gap: map.get($spacing, 3);
1234
+ list-style: none;
1235
+ margin: map.get($spacing, 3) 0 0;
1236
+ padding: 0;
1237
+
1238
+ @include respond-to(md) {
1239
+ grid-template-columns: repeat(2, 1fr);
1240
+ }
1241
+
1242
+ li a {
1243
+ display: block;
1244
+ padding: map.get($spacing, 3);
1245
+ border: 1px solid rgba($color-neutral-200, 0.5);
1246
+ border-radius: map.get($radius, md);
1247
+ text-decoration: none;
1248
+ color: $color-ink-900;
1249
+ transition: border-color $transition-base, box-shadow $transition-base;
1250
+
1251
+ &:hover {
1252
+ border-color: rgba($color-accent-primary, 0.3);
1253
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
1254
+ }
1255
+ }
1256
+ }
1257
+
1258
+ body.dark-mode .post-related-list li a {
1259
+ border-color: rgba($dark-ink-500, 0.12);
1260
+ color: $dark-ink-900;
1261
+
1262
+ &:hover {
1263
+ border-color: rgba($dark-accent-primary, 0.3);
1264
+ }
1265
+ }
1266
+
1267
+ // Post pagination (prev/next)
1268
+ .post-pagination {
1269
+ display: grid;
1270
+ gap: map.get($spacing, 4);
1271
+ padding-top: map.get($spacing, 5);
1272
+ border-top: 1px solid rgba($color-neutral-200, 0.5);
1273
+
1274
+ @include respond-to(md) {
1275
+ grid-template-columns: 1fr 1fr;
1276
+ }
1277
+ }
1278
+
1279
+ body.dark-mode .post-pagination {
1280
+ border-top-color: rgba($dark-ink-500, 0.12);
1281
+ }
1282
+
1283
+ .post-prev,
1284
+ .post-next {
1285
+ display: grid;
1286
+ gap: map.get($spacing, 1);
1287
+
1288
+ a {
1289
+ font-weight: 600;
1290
+ font-size: map.get($font-size-scale, base);
1291
+ color: $color-ink-900;
1292
+ text-decoration: none;
1293
+
1294
+ &:hover { color: $color-accent-primary; }
1295
+ }
1296
+ }
1297
+
1298
+ body.dark-mode .post-prev a,
1299
+ body.dark-mode .post-next a {
1300
+ color: $dark-ink-900;
1301
+ &:hover { color: $dark-accent-primary; }
1302
+ }
1303
+
1304
+ .post-next {
1305
+ @include respond-to(md) {
1306
+ text-align: right;
1307
+ }
1308
+ }
1309
+
1310
+ .post-pagination-label {
1311
+ font-size: map.get($font-size-scale, xs);
1312
+ font-weight: 600;
1313
+ text-transform: uppercase;
1314
+ letter-spacing: 0.04em;
1315
+ color: $color-ink-500;
1316
+ }
1317
+
1318
+ body.dark-mode .post-pagination-label {
1319
+ color: $dark-ink-500;
1320
+ }
1321
+
1322
+ .post-pagination-context {
1323
+ font-size: map.get($font-size-scale, xs);
1324
+ color: $color-ink-500;
1325
+ margin: 0;
1326
+ }
1327
+
1328
+ body.dark-mode .post-pagination-context {
1329
+ color: $dark-ink-500;
1330
+ }
1331
+
1332
+ // Author bio card
1333
+ .author-bio {
1334
+ padding: map.get($spacing, 5);
1335
+ border: 1px solid rgba($color-neutral-200, 0.5);
1336
+ border-radius: map.get($radius, lg);
1337
+ margin-top: map.get($spacing, 5);
1338
+ }
1339
+
1340
+ body.dark-mode .author-bio {
1341
+ border-color: rgba($dark-ink-500, 0.12);
1342
+ }
1343
+
1344
+ .author-bio__inner {
1345
+ display: flex;
1346
+ gap: map.get($spacing, 4);
1347
+ align-items: flex-start;
1348
+
1349
+ @media (max-width: 30rem) {
1350
+ flex-direction: column;
1351
+ }
1352
+ }
1353
+
1354
+ .author-bio__avatar {
1355
+ flex-shrink: 0;
1356
+ width: 4rem;
1357
+ height: 4rem;
1358
+ border-radius: 50%;
1359
+ overflow: hidden;
1360
+ }
1361
+
1362
+ .author-bio__avatar-placeholder {
1363
+ width: 100%;
1364
+ height: 100%;
1365
+ background: linear-gradient(135deg, rgba($color-accent-primary, 0.15), rgba($color-accent-secondary, 0.2));
1366
+ display: flex;
1367
+ align-items: center;
1368
+ justify-content: center;
1369
+ font-size: 1.5rem;
1370
+ font-weight: 700;
1371
+ color: $color-accent-primary;
1372
+ }
1373
+
1374
+ .author-bio__content {
1375
+ display: grid;
1376
+ gap: map.get($spacing, 2);
1377
+ }
1378
+
1379
+ .author-bio__name {
1380
+ font-size: map.get($font-size-scale, md);
1381
+ font-weight: 700;
1382
+ margin: 0;
1383
+ }
1384
+
1385
+ .author-bio__affiliation {
1386
+ font-size: map.get($font-size-scale, xs);
1387
+ color: $color-ink-500;
1388
+ }
1389
+
1390
+ body.dark-mode .author-bio__affiliation {
1391
+ color: $dark-ink-500;
1392
+ }
1393
+
1394
+ .author-bio__social {
1395
+ display: flex;
1396
+ flex-wrap: wrap;
1397
+ gap: map.get($spacing, 2);
1398
+ margin-top: map.get($spacing, 1);
1399
+ }
1400
+
1401
+ .author-bio__social-link {
1402
+ font-size: map.get($font-size-scale, xs);
1403
+ font-weight: 500;
1404
+ color: $color-accent-primary;
1405
+ text-decoration: none;
1406
+
1407
+ &:hover { text-decoration: underline; }
1408
+ }
1409
+
1410
+ body.dark-mode .author-bio__social-link {
1411
+ color: $dark-accent-primary;
1412
+ }
1413
+
1414
+ // Citation tools
1415
+ .citation-tools {
1416
+ border: 1px solid rgba($color-neutral-200, 0.5);
1417
+ border-radius: map.get($radius, lg);
1418
+ padding: map.get($spacing, 4);
1419
+ margin-top: map.get($spacing, 4);
1420
+ }
1421
+
1422
+ body.dark-mode .citation-tools {
1423
+ border-color: rgba($dark-ink-500, 0.12);
1424
+ }
1425
+
1426
+ .citation-tools__header {
1427
+ font-size: map.get($font-size-scale, sm);
1428
+ font-weight: 600;
1429
+ margin: 0 0 map.get($spacing, 3);
1430
+ }
1431
+
1432
+ .citation-tools__summary {
1433
+ font-size: map.get($font-size-scale, sm);
1434
+ color: $color-ink-500;
1435
+ margin: 0 0 map.get($spacing, 3);
1436
+ }
1437
+
1438
+ body.dark-mode .citation-tools__summary {
1439
+ color: $dark-ink-500;
1440
+ }
1441
+
1442
+ .citation-tools__exports {
1443
+ display: flex;
1444
+ flex-wrap: wrap;
1445
+ gap: map.get($spacing, 2);
1446
+ }
1447
+
1448
+ .citation-tools__format {
1449
+ font-size: map.get($font-size-scale, xs);
1450
+ padding: 0.25rem 0.5rem;
1451
+ border: 1px solid rgba($color-neutral-200, 0.5);
1452
+ border-radius: map.get($radius, sm);
1453
+ background: transparent;
1454
+ cursor: pointer;
1455
+ color: $color-ink-700;
1456
+ transition: background $transition-base;
1457
+
1458
+ &:hover { background: rgba($color-accent-primary, 0.06); }
1459
+ }
1460
+
1461
+ body.dark-mode .citation-tools__format {
1462
+ border-color: rgba($dark-ink-500, 0.15);
1463
+ color: $dark-ink-700;
1464
+ }
1465
+
1466
+ .citation-tools__citation {
1467
+ margin-top: map.get($spacing, 3);
1468
+ padding: map.get($spacing, 3);
1469
+ background: rgba($color-neutral-100, 0.5);
1470
+ border-radius: map.get($radius, md);
1471
+ font-size: map.get($font-size-scale, sm);
1472
+ font-family: $font-family-mono;
1473
+ white-space: pre-wrap;
1474
+ overflow-x: auto;
1475
+ }
1476
+
1477
+ body.dark-mode .citation-tools__citation {
1478
+ background: rgba($dark-surface-300, 0.5);
1479
+ }
1480
+
1481
+ // Bibliography & slides
1482
+ .post-bibliography,
1483
+ .post-slides {
1484
+ padding-top: map.get($spacing, 5);
1485
+ border-top: 1px solid rgba($color-neutral-200, 0.5);
1486
+ }
1487
+
1488
+ body.dark-mode .post-bibliography,
1489
+ body.dark-mode .post-slides {
1490
+ border-top-color: rgba($dark-ink-500, 0.12);
1491
+ }
1492
+
1493
+ // ---------------------------------------------------------------------------
1494
+ // 404 page
1495
+ // ---------------------------------------------------------------------------
1496
+ .error-404 {
1497
+ text-align: center;
1498
+ padding: clamp(3rem, 8vw, 6rem) 0;
1499
+
1500
+ h1 {
1501
+ font-size: map.get($font-size-scale, display);
1502
+ font-weight: 800;
1503
+ letter-spacing: -0.02em;
1504
+ margin: 0 0 map.get($spacing, 3);
1505
+ }
1506
+
1507
+ > .container > p {
1508
+ font-size: map.get($font-size-scale, md);
1509
+ color: $color-ink-500;
1510
+ max-width: 48ch;
1511
+ margin: 0 auto map.get($spacing, 6);
1512
+ line-height: $line-height-loose;
1513
+ }
1514
+ }
1515
+
1516
+ body.dark-mode .error-404 > .container > p {
1517
+ color: $dark-ink-500;
1518
+ }
1519
+
1520
+ .error-search {
1521
+ max-width: 28rem;
1522
+ margin: 0 auto map.get($spacing, 6);
1523
+ display: flex;
1524
+ gap: map.get($spacing, 2);
1525
+
1526
+ label {
1527
+ position: absolute;
1528
+ width: 1px;
1529
+ height: 1px;
1530
+ padding: 0;
1531
+ margin: -1px;
1532
+ overflow: hidden;
1533
+ clip: rect(0, 0, 0, 0);
1534
+ border: 0;
1535
+ }
1536
+
1537
+ input[type="search"] {
1538
+ flex: 1;
1539
+ padding: 0.625rem 1rem;
1540
+ border: 2px solid rgba($color-neutral-200, 0.8);
1541
+ border-radius: map.get($radius, lg);
1542
+ font-size: map.get($font-size-scale, base);
1543
+ font-family: $font-family-sans;
1544
+
1545
+ &:focus {
1546
+ outline: none;
1547
+ border-color: $color-accent-primary;
1548
+ box-shadow: 0 0 0 3px rgba($color-accent-primary, 0.12);
1549
+ }
1550
+ }
1551
+
1552
+ button {
1553
+ padding: 0.625rem 1.25rem;
1554
+ border: none;
1555
+ border-radius: map.get($radius, lg);
1556
+ background: $color-accent-primary;
1557
+ color: white;
1558
+ font-weight: 600;
1559
+ cursor: pointer;
1560
+
1561
+ &:hover { background: color.adjust($color-accent-primary, $lightness: -6%); }
1562
+ }
1563
+ }
1564
+
1565
+ body.dark-mode .error-search input[type="search"] {
1566
+ background: $dark-surface-300;
1567
+ border-color: rgba($dark-ink-500, 0.2);
1568
+ color: $dark-ink-900;
1569
+ }
1570
+
1571
+ .error-links {
1572
+ display: grid;
1573
+ gap: map.get($spacing, 5);
1574
+ text-align: left;
1575
+ max-width: 36rem;
1576
+ margin: 0 auto;
1577
+
1578
+ @include respond-to(sm) {
1579
+ grid-template-columns: 1fr 1fr;
1580
+ }
1581
+
1582
+ h2 {
1583
+ font-size: map.get($font-size-scale, sm);
1584
+ font-weight: 700;
1585
+ text-transform: uppercase;
1586
+ letter-spacing: 0.04em;
1587
+ margin: 0 0 map.get($spacing, 2);
1588
+ color: $color-ink-500;
1589
+ }
1590
+
1591
+ ul {
1592
+ list-style: none;
1593
+ margin: 0;
1594
+ padding: 0;
1595
+ display: grid;
1596
+ gap: map.get($spacing, 1);
1597
+ }
1598
+
1599
+ a {
1600
+ font-size: map.get($font-size-scale, sm);
1601
+ color: $color-accent-primary;
1602
+ text-decoration: none;
1603
+
1604
+ &:hover { text-decoration: underline; }
1605
+ }
1606
+ }
1607
+
1608
+ body.dark-mode .error-links {
1609
+ h2 { color: $dark-ink-500; }
1610
+ a { color: $dark-accent-primary; }
1611
+ }
1612
+
1613
+ // ---------------------------------------------------------------------------
1614
+ // Minimal Mistakes compatibility: post heroes, teaser thumbnails, wide posts
1615
+ // and the editorial provenance note. See docs/migrating-from-minimal-mistakes.md.
1616
+ // ---------------------------------------------------------------------------
1617
+ .post-hero {
1618
+ margin: 0 0 map.get($spacing, 5);
1619
+ border-radius: map.get($radius, lg);
1620
+ overflow: hidden;
1621
+ }
1622
+
1623
+ .post-hero__image {
1624
+ display: block;
1625
+ width: 100%;
1626
+ height: auto;
1627
+ }
1628
+
1629
+ .post-hero__caption {
1630
+ padding: map.get($spacing, 2) 0 0;
1631
+ font-size: map.get($font-size-scale, sm);
1632
+ color: $color-ink-500;
1633
+ }
1634
+
1635
+ body.dark-mode .post-hero__caption {
1636
+ color: $dark-ink-500;
1637
+ }
1638
+
1639
+ .post-hero--overlay {
1640
+ position: relative;
1641
+ display: flex;
1642
+ align-items: flex-end;
1643
+ min-height: 16rem;
1644
+ padding: map.get($spacing, 6) map.get($spacing, 5);
1645
+ background-color: $color-ink-900;
1646
+ background-size: cover;
1647
+ background-position: center;
1648
+ color: #fff;
1649
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55);
1650
+
1651
+ @include respond-to(md) {
1652
+ min-height: 22rem;
1653
+ padding: map.get($spacing, 7) map.get($spacing, 6);
1654
+ }
1655
+ }
1656
+
1657
+ .post-hero__inner {
1658
+ max-width: $max-width-reading;
1659
+ }
1660
+
1661
+ .post-hero__title {
1662
+ margin: 0;
1663
+ color: inherit;
1664
+ }
1665
+
1666
+ .post-hero__subtitle {
1667
+ margin: map.get($spacing, 2) 0 0;
1668
+ font-size: map.get($font-size-scale, lg);
1669
+ color: inherit;
1670
+ }
1671
+
1672
+ .article-subtitle {
1673
+ margin: map.get($spacing, 1) 0 0;
1674
+ font-size: map.get($font-size-scale, lg);
1675
+ color: $color-ink-700;
1676
+ }
1677
+
1678
+ body.dark-mode .article-subtitle {
1679
+ color: $dark-ink-700;
1680
+ }
1681
+
1682
+ // Teaser thumbnails on home cards and related-post cards. Cards are grids
1683
+ // whose rows stretch to the tallest card in the row; without these two rules
1684
+ // the extra height inflates the thumbnail and pushes the text to the bottom.
1685
+ .card-grid > .card {
1686
+ align-content: start;
1687
+ }
1688
+
1689
+ .card-media {
1690
+ display: block;
1691
+ margin: -#{map.get($spacing, 5)} -#{map.get($spacing, 5)} 0;
1692
+ overflow: hidden;
1693
+ border-radius: 0.75rem 0.75rem 0 0;
1694
+
1695
+ img {
1696
+ display: block;
1697
+ width: 100%;
1698
+ height: auto;
1699
+ aspect-ratio: 16 / 9;
1700
+ object-fit: cover;
1701
+ }
1702
+ }
1703
+
1704
+ .related-post__media {
1705
+ display: block;
1706
+ margin: 0 0 map.get($spacing, 2);
1707
+ overflow: hidden;
1708
+ border-radius: map.get($radius, md);
1709
+
1710
+ img {
1711
+ display: block;
1712
+ width: 100%;
1713
+ max-width: 20rem;
1714
+ aspect-ratio: 16 / 9;
1715
+ object-fit: cover;
1716
+ }
1717
+ }
1718
+
1719
+ // `classes: wide` lifts the reading measure inside the article, as the
1720
+ // Minimal Mistakes wide layout does.
1721
+ body.wide .article-body,
1722
+ body.wide .post-content {
1723
+ p,
1724
+ li,
1725
+ blockquote,
1726
+ .lead {
1727
+ max-width: none;
1728
+ }
1729
+ }
1730
+
1731
+ // Editorial provenance note (why_this_exists, evidence, methodology, reviewed_at).
1732
+ .content-provenance {
1733
+ @include callout($color-accent-secondary);
1734
+ font-size: map.get($font-size-scale, sm);
1735
+ }
1736
+
1737
+ .content-provenance__heading {
1738
+ margin: 0 0 map.get($spacing, 2);
1739
+ font-size: map.get($font-size-scale, base);
1740
+ }
1741
+
1742
+ .content-provenance__list {
1743
+ display: grid;
1744
+ gap: map.get($spacing, 2);
1745
+ margin: 0;
1746
+
1747
+ > div {
1748
+ display: grid;
1749
+ gap: map.get($spacing, 1);
1750
+ }
1751
+
1752
+ dt {
1753
+ font-weight: 600;
1754
+ }
1755
+
1756
+ dd {
1757
+ margin: 0;
1758
+ max-width: $max-width-reading;
1759
+ }
1760
+ }