pandorga 1.2.1

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 (376) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +77 -0
  3. data/DESIGN.md +1434 -0
  4. data/LICENSE +21 -0
  5. data/README.md +81 -0
  6. data/_data/themes/dark.yml +97 -0
  7. data/_data/themes/light.yml +120 -0
  8. data/_data/themes/shared.yml +37 -0
  9. data/_data/translations.yml +85 -0
  10. data/_includes/chord-tab.html +38 -0
  11. data/_includes/content-runtime/00-config.html +22 -0
  12. data/_includes/content-runtime/10-utils.html +176 -0
  13. data/_includes/content-runtime/20-markdown.html +264 -0
  14. data/_includes/content-runtime/25-code-blocks.html +315 -0
  15. data/_includes/content-runtime/30-includes-bib.html +190 -0
  16. data/_includes/content-runtime/30-includes-xcite.html +122 -0
  17. data/_includes/content-runtime/30-includes.html +365 -0
  18. data/_includes/content-runtime/40-listing.html +225 -0
  19. data/_includes/content-runtime/41-listing-helpers.html +71 -0
  20. data/_includes/content-runtime/42-listing-filter.html +208 -0
  21. data/_includes/content-runtime/43-blocks.html +379 -0
  22. data/_includes/content-runtime/50-detail.html +861 -0
  23. data/_includes/content-runtime/55-toc.html +176 -0
  24. data/_includes/content-runtime/60-fragments.html +567 -0
  25. data/_includes/content-runtime/90-bootstrap.html +76 -0
  26. data/_includes/cut-in.html +10 -0
  27. data/_includes/cv/cv-box.html +18 -0
  28. data/_includes/dict-card.html +24 -0
  29. data/_includes/figure.html +36 -0
  30. data/_includes/home/band-index.html +43 -0
  31. data/_includes/home/band-listing.html +49 -0
  32. data/_includes/mathjax.html +10 -0
  33. data/_includes/ongoing-product.html +1 -0
  34. data/_includes/page/content-runtime.html +20 -0
  35. data/_includes/page/date_evolution.html +19 -0
  36. data/_includes/page/figure-lightbox.html +102 -0
  37. data/_includes/page/formatted_date.html +15 -0
  38. data/_includes/page/hero-default.html +18 -0
  39. data/_includes/page/listing-runtime-attrs.html +8 -0
  40. data/_includes/page/mermaid.html +101 -0
  41. data/_includes/page/network-graph.html +1253 -0
  42. data/_includes/page/page-header.html +41 -0
  43. data/_includes/page/professional-connections.html +2 -0
  44. data/_includes/page/site-footer.html +101 -0
  45. data/_includes/page/timeline-section.html +868 -0
  46. data/_includes/plotly.html +22 -0
  47. data/_includes/theme/dev-reexport.html +100 -0
  48. data/_includes/theme/favicons.html +4 -0
  49. data/_includes/theme/font-loader.html +55 -0
  50. data/_includes/theme/shell-theme-boot.html +115 -0
  51. data/_includes/theme/site-logo.html +6 -0
  52. data/_includes/theme/tailwind-theme-colors.json.liquid +65 -0
  53. data/_includes/theme/tailwind-theme-fonts.json.liquid +6 -0
  54. data/_includes/theme/theme-context.liquid +20 -0
  55. data/_includes/theme/theme-toggle.html +94 -0
  56. data/_includes/theme/theme-vars-block.liquid +161 -0
  57. data/_includes/theme/theme-vars.html +16 -0
  58. data/_includes/transcription.html +80 -0
  59. data/_includes/tweet.html +22 -0
  60. data/_includes/video.html +11 -0
  61. data/_includes/xcite.html +3 -0
  62. data/_includes/youtube-short.html +26 -0
  63. data/_includes/youtube.html +12 -0
  64. data/_layouts/article.html +27 -0
  65. data/_layouts/articles.html +534 -0
  66. data/_layouts/bibliography.html +417 -0
  67. data/_layouts/blog.html +544 -0
  68. data/_layouts/cv.html +393 -0
  69. data/_layouts/home.html +319 -0
  70. data/_layouts/network.html +9 -0
  71. data/_layouts/pandorga/articles.html +4 -0
  72. data/_layouts/pandorga/bibliography.html +4 -0
  73. data/_layouts/pandorga/blog.html +4 -0
  74. data/_layouts/pandorga/cv.html +4 -0
  75. data/_layouts/pandorga/media.html +4 -0
  76. data/_layouts/pandorga/network.html +4 -0
  77. data/_layouts/pandorga/portfolio.html +4 -0
  78. data/_layouts/post.html +7 -0
  79. data/_layouts/product.html +5 -0
  80. data/_layouts/project.html +27 -0
  81. data/_layouts/projects.html +534 -0
  82. data/_layouts/resources.html +387 -0
  83. data/_layouts/shell.html +203 -0
  84. data/_layouts/text.html +246 -0
  85. data/_plugins/bibliography_artifact.rb +57 -0
  86. data/_plugins/content_backend.rb +37 -0
  87. data/_plugins/content_collections.rb +51 -0
  88. data/_plugins/content_detail_paths.rb +58 -0
  89. data/_plugins/content_headers.rb +73 -0
  90. data/_plugins/content_keys.rb +310 -0
  91. data/_plugins/content_local_serve.rb +168 -0
  92. data/_plugins/content_site_adapter.rb +159 -0
  93. data/_plugins/content_validators_hook.rb +20 -0
  94. data/_plugins/content_writing_index.rb +52 -0
  95. data/_plugins/content_writing_paths.rb +55 -0
  96. data/_plugins/force_utf8_encoding.rb +13 -0
  97. data/_plugins/git_chronology.rb +211 -0
  98. data/_plugins/git_metadata.rb +43 -0
  99. data/_plugins/job_sections.rb +25 -0
  100. data/_plugins/lib/content_validators.rb +27 -0
  101. data/_plugins/lib/resource_validator.rb +56 -0
  102. data/_plugins/lib/tag_validator.rb +153 -0
  103. data/_plugins/lib/text_excerpt.rb +56 -0
  104. data/_plugins/lib/writing_entry.rb +107 -0
  105. data/_plugins/local_detail_rewrites.rb +194 -0
  106. data/_plugins/timeline.rb +507 -0
  107. data/_plugins/writing_slug_limits.rb +70 -0
  108. data/assets/css/article.css +1330 -0
  109. data/assets/css/base.css +1432 -0
  110. data/assets/css/cv.css +13 -0
  111. data/assets/css/global-link.css +94 -0
  112. data/assets/css/ledger/bibliography.css +307 -0
  113. data/assets/css/ledger/chrome.css +118 -0
  114. data/assets/css/ledger/cv.css +836 -0
  115. data/assets/css/ledger/detail.css +1270 -0
  116. data/assets/css/ledger/footer.css +298 -0
  117. data/assets/css/ledger/home.css +1490 -0
  118. data/assets/css/ledger/network.css +106 -0
  119. data/assets/css/ledger/projects.css +716 -0
  120. data/assets/css/ledger/rascunhos.css +379 -0
  121. data/assets/css/ledger/sources.css +327 -0
  122. data/assets/css/ledger/writing.css +754 -0
  123. data/assets/tailwind-play.js +15 -0
  124. data/examples/full/Gemfile +7 -0
  125. data/examples/full/_config.yml +92 -0
  126. data/examples/full/_redirects +3 -0
  127. data/examples/full/content/collections/articles/2026-01-01-welcome.md +9 -0
  128. data/examples/full/content/collections/data/contacts.yml +3 -0
  129. data/examples/full/content/collections/data/education.yml +4 -0
  130. data/examples/full/content/collections/jobs/research-fellow.md +12 -0
  131. data/examples/full/content/collections/posts/2026-01-02-note.md +9 -0
  132. data/examples/full/content/collections/products/ledger-tool.md +6 -0
  133. data/examples/full/content/collections/projects/demo-kit.md +10 -0
  134. data/examples/full/content/collections/resources/style-guide.md +7 -0
  135. data/examples/full/content/pages/cv/summary.md +1 -0
  136. data/examples/full/content/pages/headers.yml +20 -0
  137. data/examples/full/index.html +4 -0
  138. data/examples/minimal/Gemfile +7 -0
  139. data/examples/minimal/_config.yml +47 -0
  140. data/examples/minimal/_redirects +3 -0
  141. data/examples/minimal/content/collections/articles/2026-01-01-hello.md +10 -0
  142. data/examples/minimal/content/collections/articles/2026-01-02-second.md +9 -0
  143. data/examples/minimal/content/pages/headers.yml +6 -0
  144. data/examples/minimal/index.html +4 -0
  145. data/exe/pandorga +7 -0
  146. data/functions/api/studio/[[path]].js +562 -0
  147. data/functions/api/studio/_lib/auth.js +131 -0
  148. data/functions/api/studio/_lib/blocks.js +156 -0
  149. data/functions/api/studio/_lib/gemini.js +62 -0
  150. data/functions/api/studio/_lib/github.js +278 -0
  151. data/functions/api/studio/_lib/guidelines.js +143 -0
  152. data/functions/api/studio/_lib/paths.js +76 -0
  153. data/functions/api/studio/_lib/prompts.js +165 -0
  154. data/functions/api/studio/_lib/rate-limit.js +40 -0
  155. data/functions/api/studio/_lib/rewrite.js +136 -0
  156. data/functions/api/studio/_lib/sanitize.js +143 -0
  157. data/lib/pandorga/cli.rb +66 -0
  158. data/lib/pandorga/commands/doctor.rb +66 -0
  159. data/lib/pandorga/commands/export.rb +30 -0
  160. data/lib/pandorga/commands/install_functions.rb +41 -0
  161. data/lib/pandorga/commands/new.rb +113 -0
  162. data/lib/pandorga/commands/publish.rb +53 -0
  163. data/lib/pandorga/commands/serve.rb +34 -0
  164. data/lib/pandorga/commands/studio_schema.rb +309 -0
  165. data/lib/pandorga/jekyll/generator.rb +74 -0
  166. data/lib/pandorga/jekyll/plugins/bibliography_artifact.rb +57 -0
  167. data/lib/pandorga/jekyll/plugins/content_backend.rb +37 -0
  168. data/lib/pandorga/jekyll/plugins/content_collections.rb +51 -0
  169. data/lib/pandorga/jekyll/plugins/content_detail_paths.rb +58 -0
  170. data/lib/pandorga/jekyll/plugins/content_headers.rb +73 -0
  171. data/lib/pandorga/jekyll/plugins/content_keys.rb +310 -0
  172. data/lib/pandorga/jekyll/plugins/content_local_serve.rb +168 -0
  173. data/lib/pandorga/jekyll/plugins/content_site_adapter.rb +159 -0
  174. data/lib/pandorga/jekyll/plugins/content_validators_hook.rb +20 -0
  175. data/lib/pandorga/jekyll/plugins/content_writing_index.rb +52 -0
  176. data/lib/pandorga/jekyll/plugins/content_writing_paths.rb +55 -0
  177. data/lib/pandorga/jekyll/plugins/force_utf8_encoding.rb +13 -0
  178. data/lib/pandorga/jekyll/plugins/git_chronology.rb +211 -0
  179. data/lib/pandorga/jekyll/plugins/git_metadata.rb +43 -0
  180. data/lib/pandorga/jekyll/plugins/job_sections.rb +25 -0
  181. data/lib/pandorga/jekyll/plugins/lib/content_validators.rb +27 -0
  182. data/lib/pandorga/jekyll/plugins/lib/resource_validator.rb +56 -0
  183. data/lib/pandorga/jekyll/plugins/lib/tag_validator.rb +153 -0
  184. data/lib/pandorga/jekyll/plugins/lib/text_excerpt.rb +56 -0
  185. data/lib/pandorga/jekyll/plugins/lib/writing_entry.rb +107 -0
  186. data/lib/pandorga/jekyll/plugins/local_detail_rewrites.rb +194 -0
  187. data/lib/pandorga/jekyll/plugins/timeline.rb +507 -0
  188. data/lib/pandorga/jekyll/plugins/writing_slug_limits.rb +70 -0
  189. data/lib/pandorga/jekyll.rb +15 -0
  190. data/lib/pandorga/object_store_env.rb +51 -0
  191. data/lib/pandorga/registry/schema.yml +115 -0
  192. data/lib/pandorga/registry.rb +279 -0
  193. data/lib/pandorga/version.rb +5 -0
  194. data/lib/pandorga.rb +19 -0
  195. data/scripts/cdn-allowlist.json +19 -0
  196. data/scripts/content/backfill-content-keys.rb +99 -0
  197. data/scripts/content/band_art.rb +362 -0
  198. data/scripts/content/export-content-json.rb +741 -0
  199. data/scripts/content/flatten-ansi-capture.mjs +153 -0
  200. data/scripts/content/hero_portraits.rb +28 -0
  201. data/scripts/content/publish-content-to-object-store.sh +78 -0
  202. data/scripts/content/publish-content-to-r2.sh +5 -0
  203. data/scripts/content/sync-content-json-to-r2.rb +167 -0
  204. data/scripts/content/sync-content-redirects.rb +12 -0
  205. data/scripts/generate-configuration-doc.rb +127 -0
  206. data/scripts/lib/content_license.rb +50 -0
  207. data/scripts/lib/content_storage_config.rb +70 -0
  208. data/scripts/lib/normalize-display-math.mjs +52 -0
  209. data/scripts/lib/r2_json_sync.rb +76 -0
  210. data/scripts/lib/withdrawn_public_objects.rb +23 -0
  211. data/scripts/lib/writing_index_contract.rb +61 -0
  212. data/scripts/pdf/md-to-pdf.mjs +1400 -0
  213. data/scripts/pdf/md-to-pdf.sh +27 -0
  214. data/scripts/test/fixtures/dual-template/_config.yml +32 -0
  215. data/scripts/test/fixtures/dual-template/index.html +4 -0
  216. data/scripts/test/render-parity/article-note.json +5 -0
  217. data/scripts/test/render-parity/blockquote.json +5 -0
  218. data/scripts/test/render-parity/blocks/ansi-basic.json +7 -0
  219. data/scripts/test/render-parity/blocks/ansi-bold-is-bright.json +7 -0
  220. data/scripts/test/render-parity/blocks/ansi-drops-cursor-moves.json +7 -0
  221. data/scripts/test/render-parity/blocks/ansi-escapes-markup.json +7 -0
  222. data/scripts/test/render-parity/blocks/ansi-extended-colour.json +7 -0
  223. data/scripts/test/render-parity/blocks/ansi-real-control-byte.json +7 -0
  224. data/scripts/test/render-parity/blocks/archive-card-date-once.json +19 -0
  225. data/scripts/test/render-parity/blocks/archive-card-empty-foot.json +7 -0
  226. data/scripts/test/render-parity/blocks/archive-card-footer-dates.json +22 -0
  227. data/scripts/test/render-parity/blocks/archive-card-meta-parts.json +26 -0
  228. data/scripts/test/render-parity/blocks/archive-card.json +32 -0
  229. data/scripts/test/render-parity/blocks/cta-link.json +13 -0
  230. data/scripts/test/render-parity/blocks/lang-pill-pt.json +8 -0
  231. data/scripts/test/render-parity/blocks/lang-pill.json +8 -0
  232. data/scripts/test/render-parity/blocks/ledger-row-meta-parts.json +29 -0
  233. data/scripts/test/render-parity/blocks/ledger-row.json +35 -0
  234. data/scripts/test/render-parity/blocks/meta-strip.json +27 -0
  235. data/scripts/test/render-parity/blocks/poster-media-image.json +8 -0
  236. data/scripts/test/render-parity/blocks/poster-media-video.json +9 -0
  237. data/scripts/test/render-parity/blocks/section-banner.json +13 -0
  238. data/scripts/test/render-parity/blocks/system-card-custom-meta.json +22 -0
  239. data/scripts/test/render-parity/blocks/system-card.json +48 -0
  240. data/scripts/test/render-parity/blocks/tag-chips-links.json +12 -0
  241. data/scripts/test/render-parity/blocks/tag-chips.json +9 -0
  242. data/scripts/test/render-parity/cut-in-blank-line.json +9 -0
  243. data/scripts/test/render-parity/cut-in-right.json +9 -0
  244. data/scripts/test/render-parity/cut-in.json +8 -0
  245. data/scripts/test/render-parity/figure.json +5 -0
  246. data/scripts/test/render-parity/load-runtime.mjs +337 -0
  247. data/scripts/test/render-parity/ongoing-product.json +5 -0
  248. data/scripts/test/render-parity/plotly.json +5 -0
  249. data/scripts/test/render-parity/poem.json +5 -0
  250. data/scripts/test/render-parity/run-absent-field-checks.mjs +232 -0
  251. data/scripts/test/render-parity/run-block-fixture.mjs +86 -0
  252. data/scripts/test/render-parity/run-detail-parity-checks.mjs +123 -0
  253. data/scripts/test/render-parity/run-field-flow-checks.mjs +227 -0
  254. data/scripts/test/render-parity/run-fixture.mjs +159 -0
  255. data/scripts/test/render-parity/selectors.mjs +43 -0
  256. data/scripts/test/render-parity/video.json +5 -0
  257. data/scripts/test/render-parity/youtube-short.json +10 -0
  258. data/scripts/test/render-parity/youtube.json +8 -0
  259. data/scripts/test/test-absent-field-absent-element.rb +43 -0
  260. data/scripts/test/test-cdn-pins.rb +44 -0
  261. data/scripts/test/test-cloudflare-watch-paths-doc.rb +31 -0
  262. data/scripts/test/test-configuration-doc.rb +36 -0
  263. data/scripts/test/test-content-detail-urls.rb +346 -0
  264. data/scripts/test/test-content-keys.rb +110 -0
  265. data/scripts/test/test-content-license.rb +45 -0
  266. data/scripts/test/test-content-render-parity.rb +38 -0
  267. data/scripts/test/test-content-validators.rb +12 -0
  268. data/scripts/test/test-css-structure.rb +72 -0
  269. data/scripts/test/test-cv-export-contract.rb +158 -0
  270. data/scripts/test/test-detail-render-parity.rb +40 -0
  271. data/scripts/test/test-dev-reexport.rb +122 -0
  272. data/scripts/test/test-doctor.rb +39 -0
  273. data/scripts/test/test-dual-template-instances.rb +87 -0
  274. data/scripts/test/test-dual-template-runtime.rb +167 -0
  275. data/scripts/test/test-examples-build.rb +60 -0
  276. data/scripts/test/test-export-content-json-incremental.rb +138 -0
  277. data/scripts/test/test-export.rb +53 -0
  278. data/scripts/test/test-exported-fields-reach-the-view.rb +60 -0
  279. data/scripts/test/test-form-control-theming.rb +143 -0
  280. data/scripts/test/test-git-chronology.rb +414 -0
  281. data/scripts/test/test-hero-portraits.mjs +97 -0
  282. data/scripts/test/test-hero-portraits.rb +162 -0
  283. data/scripts/test/test-home-band-art.rb +135 -0
  284. data/scripts/test/test-install-functions.rb +39 -0
  285. data/scripts/test/test-language-export.rb +79 -0
  286. data/scripts/test/test-listing-filter-contract.rb +119 -0
  287. data/scripts/test/test-math-row-breaks.mjs +79 -0
  288. data/scripts/test/test-math-row-breaks.rb +18 -0
  289. data/scripts/test/test-new.rb +113 -0
  290. data/scripts/test/test-no-personal-data.rb +85 -0
  291. data/scripts/test/test-public-cv-export.rb +164 -0
  292. data/scripts/test/test-r2-config-consistency.rb +57 -0
  293. data/scripts/test/test-r2-json-sync.rb +193 -0
  294. data/scripts/test/test-registry-home.rb +34 -0
  295. data/scripts/test/test-runtime-js-syntax.rb +64 -0
  296. data/scripts/test/test-slim-writing-index.rb +126 -0
  297. data/scripts/test/test-studio-api-contract.rb +99 -0
  298. data/scripts/test/test-studio-content-files.rb +79 -0
  299. data/scripts/test/test-studio-liquid-roundtrip.mjs +179 -0
  300. data/scripts/test/test-studio-rate-limit.mjs +70 -0
  301. data/scripts/test/test-studio-rewrite-api-contract.rb +59 -0
  302. data/scripts/test/test-studio-rewrite-sanitize.mjs +210 -0
  303. data/scripts/test/test-studio-schema-composition.rb +126 -0
  304. data/scripts/test/test-studio-slug-config.rb +167 -0
  305. data/scripts/test/test-template-packages.rb +51 -0
  306. data/scripts/test/test-text-excerpt.rb +51 -0
  307. data/scripts/test/test-unwrap-media-src.mjs +39 -0
  308. data/scripts/test/test-unwrap-media-src.rb +19 -0
  309. data/scripts/test/test-withdrawn-content-export.rb +69 -0
  310. data/scripts/test/test-writing-index-export-slugs.rb +84 -0
  311. data/scripts/test/test-writing-slug-limits.rb +61 -0
  312. data/scripts/test/test-xcite-chronology.rb +106 -0
  313. data/scripts/validate.sh +74 -0
  314. data/scripts/visual/build-cv-pages-json.js +33 -0
  315. data/scripts/visual/build-site-pages-json.js +94 -0
  316. data/scripts/visual/publish-visual-inspection-to-r2.sh +42 -0
  317. data/scripts/visual/take-screenshots.js +88 -0
  318. data/scripts/visual/visual-inspection-pages.json +113 -0
  319. data/studio/assets/index-B_j0pMD5.css +1 -0
  320. data/studio/assets/index-Ch4YEv9g.js +285 -0
  321. data/studio/index.html +27 -0
  322. data/studio/schema.yml +869 -0
  323. data/studio-app/README.md +27 -0
  324. data/studio-app/index.html +26 -0
  325. data/studio-app/package-lock.json +2499 -0
  326. data/studio-app/package.json +26 -0
  327. data/studio-app/skills-lock.json +47 -0
  328. data/studio-app/src/api/client.js +71 -0
  329. data/studio-app/src/editor/body-editor.js +70 -0
  330. data/studio-app/src/editor/liquid-core.js +382 -0
  331. data/studio-app/src/editor/liquid.js +77 -0
  332. data/studio-app/src/lib/document.js +207 -0
  333. data/studio-app/src/lib/list-controls.js +317 -0
  334. data/studio-app/src/lib/schema.js +66 -0
  335. data/studio-app/src/lib/yaml-doc.js +100 -0
  336. data/studio-app/src/main.js +3693 -0
  337. data/studio-app/src/media/browser.js +578 -0
  338. data/studio-app/src/preview/render.js +430 -0
  339. data/studio-app/src/styles/studio.css +2826 -0
  340. data/studio-app/vite.config.js +25 -0
  341. data/templates/articles/README.md +40 -0
  342. data/templates/articles/fixtures/article.md +9 -0
  343. data/templates/articles/screenshots/README.md +6 -0
  344. data/templates/articles/studio.yml +73 -0
  345. data/templates/articles/template.yml +21 -0
  346. data/templates/bibliography/README.md +41 -0
  347. data/templates/bibliography/fixtures/references.json +9 -0
  348. data/templates/bibliography/screenshots/README.md +6 -0
  349. data/templates/bibliography/studio.yml +66 -0
  350. data/templates/bibliography/template.yml +18 -0
  351. data/templates/blog/README.md +38 -0
  352. data/templates/blog/fixtures/post.md +9 -0
  353. data/templates/blog/screenshots/README.md +6 -0
  354. data/templates/blog/studio.yml +69 -0
  355. data/templates/blog/template.yml +20 -0
  356. data/templates/cv/README.md +34 -0
  357. data/templates/cv/fixtures/job.md +12 -0
  358. data/templates/cv/screenshots/README.md +6 -0
  359. data/templates/cv/studio.yml +200 -0
  360. data/templates/cv/template.yml +25 -0
  361. data/templates/media/README.md +38 -0
  362. data/templates/media/fixtures/resource.md +7 -0
  363. data/templates/media/screenshots/README.md +6 -0
  364. data/templates/media/studio.yml +58 -0
  365. data/templates/media/template.yml +17 -0
  366. data/templates/network/README.md +34 -0
  367. data/templates/network/fixtures/sources.json +3 -0
  368. data/templates/network/screenshots/README.md +6 -0
  369. data/templates/network/studio.yml +28 -0
  370. data/templates/network/template.yml +10 -0
  371. data/templates/portfolio/README.md +39 -0
  372. data/templates/portfolio/fixtures/project.md +10 -0
  373. data/templates/portfolio/screenshots/README.md +6 -0
  374. data/templates/portfolio/studio.yml +89 -0
  375. data/templates/portfolio/template.yml +18 -0
  376. metadata +505 -0
@@ -0,0 +1,161 @@
1
+ {% comment %}
2
+ Emits CSS custom properties for one palette. Expects `theme_name` (e.g. light, dark).
3
+ Colour roles come from _data/themes/<name>.yml; fonts / motion / geometry from shared.yml.
4
+ {% endcomment %}
5
+ {% assign theme_profile = site.data.themes[theme_name] %}
6
+ {% if theme_profile == nil %}
7
+ {% assign theme_profile = site.data.themes.light %}
8
+ {% endif %}
9
+ {% assign theme_shared = site.data.themes.shared %}
10
+ {% assign tc = theme_profile.colors %}
11
+ {% assign tn = theme_profile.neutrals %}
12
+ {% assign tl = theme_profile.links %}
13
+ {% assign sf = theme_shared.fonts %}
14
+ {% assign sm = theme_shared.motion %}
15
+ {% assign sg = theme_shared.geometry %}
16
+ {% assign ts = theme_profile.static %}
17
+ {% assign color_primary = tc.primary | default: "#1c5a7a" %}
18
+ {% assign color_secondary = tc.secondary | default: "#287445" %}
19
+ {% assign color_tertiary = tc.tertiary | default: "#746f30" %}
20
+ {% assign font_title = sf.title | default: "Lora" %}
21
+ {% assign font_subtitle = sf.subtitle | default: "Lato" %}
22
+ {% assign font_label = sf.label | default: "Ysabeau SC" %}
23
+ {% assign font_body = sf.body | default: "Newsreader" %}
24
+ {% assign font_cv_body = sf.cv_body | default: "Newsreader" %}
25
+ {% assign font_cv_body_weight = sf.cv_body_weight | default: 400 %}
26
+ {% assign font_title_fallback = sf.title_fallback | default: "serif" %}
27
+ {% assign font_subtitle_fallback = sf.subtitle_fallback | default: "serif" %}
28
+ {% assign font_label_fallback = sf.label_fallback | default: "serif" %}
29
+ {% assign font_body_fallback = sf.body_fallback | default: "sans-serif" %}
30
+ {% assign bg_secondary = tn.background_secondary | default: tn.surface_container_low | default: "#f5f5f5" %}
31
+ {% assign surface_value = tn.surface | default: "#ffffff" %}
32
+ {% assign surface_variant_value = tn.surface_variant | default: "#e2e2e2" %}
33
+ {% if theme_name == "light" %}:root,{% endif %}
34
+ [data-theme="{{ theme_name }}"] {
35
+ --font-title: '{{ font_title }}', {{ font_title_fallback }};
36
+ --font-subtitle: '{{ font_subtitle }}', {{ font_subtitle_fallback }};
37
+ --font-label: '{{ font_label }}', {{ font_label_fallback }};
38
+ --font-body: '{{ font_body }}', {{ font_body_fallback }};
39
+ --font-cv-body: '{{ font_cv_body }}', sans-serif;
40
+ --font-cv-body-weight: {{ font_cv_body_weight }};
41
+ --font-serif: '{{ font_title }}', {{ font_title_fallback }};
42
+ /* Content titles share the body face at 600 — Cinzel Decorative is
43
+ reserved for page/display titles. */
44
+ --font-content-title: var(--font-body);
45
+ --font-sans: var(--font-body);
46
+ --font-mono: {% if sf.mono and sf.mono != '' %}'{{ sf.mono }}', {% endif %}ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace;
47
+ --font-code: {% if sf.code and sf.code != '' %}'{{ sf.code }}', {% elsif sf.mono and sf.mono != '' %}'{{ sf.mono }}', {% endif %}ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace;
48
+
49
+ /* Motion. Named for what they are for: state change, poster filter
50
+ warm-up, poster/plate scale. Shared across palettes. */
51
+ --motion-duration-fast: {{ sm.duration_fast | default: "0.15s" }};
52
+ --motion-duration-base: {{ sm.duration_base | default: "0.4s" }};
53
+ --motion-duration-slow: {{ sm.duration_slow | default: "0.7s" }};
54
+ --motion-ease-default: {{ sm.ease_default | default: "ease" }};
55
+ --motion-ease-out: {{ sm.ease_out | default: "ease-out" }};
56
+
57
+ --color-primary: {{ color_primary }};
58
+ --color-secondary: {{ color_secondary }};
59
+ --color-tertiary: {{ color_tertiary }};
60
+ --color-primary-container: {{ tc.primary_container | default: "#b5c9d4" }};
61
+ --color-secondary-container: {{ tc.secondary_container | default: "#d2e4d8" }};
62
+ --color-tertiary-container: {{ tc.tertiary_container | default: "#ebe9d6" }};
63
+ --color-on-primary-container: {{ tc.on_primary_container | default: "#1a2d38" }};
64
+ --color-on-secondary-container: {{ tc.on_secondary_container | default: "#1f3528" }};
65
+ --color-on-tertiary-container: {{ tc.on_tertiary_container | default: "#3d3c2a" }};
66
+ --color-on-primary: {{ ts.on_primary | default: "#ffffff" }};
67
+ --color-on-secondary: {{ ts.on_secondary | default: "#ffffff" }};
68
+ --color-on-tertiary: {{ ts.on_tertiary | default: "#ffffff" }};
69
+ --color-on-surface: {{ tn.on_surface | default: "#1b1c15" }};
70
+ --color-on-surface-variant: {{ tn.on_surface_variant | default: "#4a4a4a" }};
71
+ --color-on-background: {{ tn.on_background | default: "#1b1c15" }};
72
+ --color-outline: {{ tn.outline | default: "#6b6b6b" }};
73
+ --color-outline-variant: {{ tn.outline_variant | default: "#d4d4d4" }};
74
+ --border-gentle-faint: color-mix(in srgb, {{ tn.outline_variant | default: "#d4d4d4" }} 22%, transparent);
75
+ --border-gentle-subtle: color-mix(in srgb, {{ tn.outline_variant | default: "#d4d4d4" }} 32%, transparent);
76
+ --border-gentle-divider: color-mix(in srgb, {{ tn.outline_variant | default: "#d4d4d4" }} 35%, transparent);
77
+ --border-gentle: color-mix(in srgb, {{ tn.outline_variant | default: "#d4d4d4" }} 62%, transparent);
78
+ --border-gentle-hover: color-mix(in srgb, {{ tn.outline_variant | default: "#d4d4d4" }} 78%, transparent);
79
+ /* Sketchbook ink frame. Light densifies toward near-black in base.css;
80
+ dark is overridden there to sit almost on the background. */
81
+ --border-ink: color-mix(in srgb, {{ tn.on_surface | default: "#1b1c15" }} 5%, {{ tn.background | default: "#ffffff" }});
82
+ --border-ink-strong: color-mix(in srgb, {{ tn.on_surface | default: "#1b1c15" }} 8%, {{ tn.background | default: "#ffffff" }});
83
+ --radius-card: {{ sg.radius_card | default: "0" }};
84
+ --cubist-card-border: var(--border-gentle);
85
+ --cubist-card-border-hover: var(--border-gentle-hover);
86
+ --color-background: {{ tn.background | default: "#ffffff" }};
87
+ --color-background-secondary: {{ bg_secondary }};
88
+ --color-surface: {{ surface_value }};
89
+ --color-surface-bright: {{ surface_value }};
90
+ --color-surface-container-lowest: {{ ts.surface_container_lowest | default: "#ffffff" }};
91
+ --color-surface-container-low: {{ bg_secondary }};
92
+ --color-surface-container: {{ tn.surface_container | default: "#eeeeee" }};
93
+ --color-surface-container-high: {{ tn.surface_container_high | default: "#e8e8e8" }};
94
+ --color-surface-container-highest: {{ surface_variant_value }};
95
+ --color-surface-variant: {{ surface_variant_value }};
96
+ --color-surface-dim: {{ ts.surface_dim | default: "#d9d9d9" }};
97
+ --color-panel-contrast: {{ tn.panel_contrast | default: bg_secondary }};
98
+ --color-surface-card: {{ tn.surface_card | default: surface_value }};
99
+ --color-surface-card-contrast: {{ tn.surface_card_contrast | default: tn.surface_card | default: surface_value }};
100
+ --color-surface-high: {{ tn.surface_high | default: tn.surface_container_high | default: "#e8e8e8" }};
101
+ --color-surface-high-contrast: {{ tn.surface_high_contrast | default: tn.surface_high | default: "#dedede" }};
102
+ --color-panel-grey: color-mix(in srgb, {{ tn.surface_container | default: "#eeeeee" }} 82%, {{ tn.background | default: "#ffffff" }});
103
+ --color-link: {{ tl.default | default: "#1c5a7a" }};
104
+ --color-link-hover: {{ tl.hover | default: "#436a7d" }};
105
+ --color-surface-tint: {{ color_primary }};
106
+ --color-inverse-primary: {{ ts.inverse_primary | default: "#8fa8b5" }};
107
+ --color-inverse-surface: {{ ts.inverse_surface | default: "#303129" }};
108
+ --color-inverse-on-surface: {{ ts.inverse_on_surface | default: "#f2f2f2" }};
109
+ --color-primary-fixed: {{ ts.primary_fixed | default: "#c5d4dc" }};
110
+ --color-primary-fixed-dim: {{ ts.primary_fixed_dim | default: "#8fa8b5" }};
111
+ --color-secondary-fixed: {{ ts.secondary_fixed | default: "#d2e4d8" }};
112
+ --color-secondary-fixed-dim: {{ ts.secondary_fixed_dim | default: "#7fa88f" }};
113
+ --color-tertiary-fixed: {{ ts.tertiary_fixed | default: "#e5e4d0" }};
114
+ --color-tertiary-fixed-dim: {{ ts.tertiary_fixed_dim | default: "#c8c6b0" }};
115
+ --color-on-primary-fixed: {{ ts.on_primary_fixed | default: "#0f1a22" }};
116
+ --color-on-primary-fixed-variant: {{ ts.on_primary_fixed_variant | default: tc.on_primary_container | default: "#1a2833" }};
117
+ --color-on-secondary-fixed: {{ ts.on_secondary_fixed | default: "#141a15" }};
118
+ --color-on-secondary-fixed-variant: {{ ts.on_secondary_fixed_variant | default: tc.on_secondary_container | default: "#2d362f" }};
119
+ --color-on-tertiary-fixed: {{ ts.on_tertiary_fixed | default: "#1b1c12" }};
120
+ --color-on-tertiary-fixed-variant: {{ ts.on_tertiary_fixed_variant | default: tc.on_tertiary_container | default: "#3d3c2a" }};
121
+ --color-error: {{ ts.error | default: "#ba1a1a" }};
122
+ --color-on-error: {{ ts.on_error | default: "#ffffff" }};
123
+ --color-error-container: {{ ts.error_container | default: "#ffdad6" }};
124
+ --color-on-error-container: {{ ts.on_error_container | default: "#93000a" }};
125
+ --color-res-youtube: {{ ts.res_youtube | default: "#c62828" }};
126
+ --color-res-podcast: {{ ts.res_podcast | default: "#2e7d32" }};
127
+ --color-res-doc: {{ ts.res_doc | default: "#b45309" }};
128
+ {% comment %}
129
+ Frozen triad aliases. `.page-accent--*` rebinds `--color-primary` (and
130
+ link) to secondary or tertiary for page chrome; categorical drawings
131
+ that must keep the absolute slate / forest / ochre map — the network
132
+ graph, ambient pools keyed to content type — read these instead.
133
+ {% endcomment %}
134
+ --triad-primary: {{ color_primary }};
135
+ --triad-secondary: {{ color_secondary }};
136
+ --triad-tertiary: {{ color_tertiary }};
137
+ {% comment %}
138
+ Timeline lane palette. A categorical scale, not the UI triad: the chart
139
+ draws six kinds of event at once and they have to be told apart, so this
140
+ is the one place the site holds more than three inks. Deliberately kept
141
+ out of `--color-primary` and friends, which a `.page-accent--*` class
142
+ rebinds — the CV reads green, but a green chart is an unreadable chart.
143
+ {% endcomment %}
144
+ {% assign tt = theme_profile.timeline %}
145
+ --timeline-employment: {{ tt.employment | default: color_primary }};
146
+ --timeline-education: {{ tt.education | default: color_secondary }};
147
+ --timeline-product: {{ tt.product | default: color_tertiary }};
148
+ --timeline-project: {{ tt.project | default: "#5c5896" }};
149
+ --timeline-article: {{ tt.article | default: "#a0543a" }};
150
+ --timeline-post: {{ tt.post | default: "#4f7d72" }};
151
+ {% assign st = theme_profile.sidebar_thumbnail %}
152
+ --sidebar-thumbnail-background: {{ st.background | default: site.sidebar_thumbnail.background | default: '#f5f0ee' }};
153
+ --sidebar-thumbnail-overlay-start: {{ st.overlay_gradient_start | default: site.sidebar_thumbnail.overlay_gradient_start | default: 'rgba(245, 240, 238, 0.18)' }};
154
+ --sidebar-thumbnail-overlay-end: {{ st.overlay_gradient_end | default: site.sidebar_thumbnail.overlay_gradient_end | default: 'rgba(237, 229, 224, 0.28)' }};
155
+ --sidebar-thumbnail-width: {{ st.width | default: site.sidebar_thumbnail.width | default: '22rem' }};
156
+ --sidebar-thumbnail-image-opacity: {{ st.image_opacity | default: site.sidebar_thumbnail.image_opacity | default: 0.94 }};
157
+ --sidebar-thumbnail-filter-saturate: {{ st.filter_saturate | default: site.sidebar_thumbnail.filter_saturate | default: 0.68 }};
158
+ --sidebar-thumbnail-filter-contrast: {{ st.filter_contrast | default: site.sidebar_thumbnail.filter_contrast | default: 0.98 }};
159
+ --sidebar-thumbnail-filter-brightness: {{ st.filter_brightness | default: site.sidebar_thumbnail.filter_brightness | default: 1.02 }};
160
+ --sidebar-thumbnail-filter-sepia: {{ st.filter_sepia | default: site.sidebar_thumbnail.filter_sepia | default: 0.10 }};
161
+ }
@@ -0,0 +1,16 @@
1
+ <style>
2
+ {% assign theme_name = "light" %}{% include theme/theme-vars-block.liquid %}
3
+ {% assign theme_name = "dark" %}{% include theme/theme-vars-block.liquid %}
4
+
5
+ /* One switch for the whole site: zeroing the duration scale stops every
6
+ transition and animation that reads it, so a new rule cannot forget to
7
+ opt in. It sits here rather than in the per-theme block because the
8
+ block is rendered once per palette and this is not a palette concern. */
9
+ @media (prefers-reduced-motion: reduce) {
10
+ :root, [data-theme] {
11
+ --motion-duration-fast: 0s;
12
+ --motion-duration-base: 0s;
13
+ --motion-duration-slow: 0s;
14
+ }
15
+ }
16
+ </style>
@@ -0,0 +1,80 @@
1
+ {% comment %}
2
+ transcription.html
3
+ Exibe uma transcrição estruturada, com até 4 personagens (cores definidas por ordem de aparição).
4
+ Uso: {% include transcription.html transcription=page.transcription youtube_id='fpmZiY24U0k' %}
5
+ Ou para usar um arquivo em _data:
6
+ {% assign transcription_data = site.data.transcriptions[page.transcription].transcription %}
7
+ {% include transcription.html transcription=transcription_data youtube_id='fpmZiY24U0k' %}
8
+ Também suporta url e image para thumbnail:
9
+ {% include transcription.html transcription=transcription_data url='https://...' image='https://...' %}
10
+
11
+ Speaker labels are title-cased at render time (Patrícia, not PATRÍCIA) so
12
+ ALL-CAPS source data still reads as dialogue names rather than shouting.
13
+ {% endcomment %}
14
+
15
+ {% assign color_palette = "primary,secondary,tertiary,outline" | split: "," %}
16
+ {% assign speakers = "" | split: "," %}
17
+
18
+ {%- for line in include.transcription -%}
19
+ {%- assign char_val = line.character | default: "" | strip -%}
20
+ {%- unless speakers contains line.character or char_val == "" -%}
21
+ {%- assign speakers = speakers | push: line.character -%}
22
+ {%- endunless -%}
23
+ {%- endfor -%}
24
+
25
+ <div class="mt-16 mb-8">
26
+ <hr class="border-t border-gentle-divider mb-6">
27
+ {% if include.youtube_id %}
28
+ <div class="mb-2">
29
+ {% include youtube.html id=include.youtube_id %} <!-- Exibe o vídeo do YouTube -->
30
+ <span class="font-label text-xs text-on-surface-variant">Transcrição do vídeo</span>
31
+ </div>
32
+ {% elsif include.url and include.image %}
33
+ <div class="mb-2 block">
34
+ <a href="{{ include.url }}" target="_blank" rel="noopener">
35
+ <img src="{{ include.image }}" alt="Thumbnail" class="w-full h-[12vw] object-cover rounded shadow" />
36
+ </a>
37
+ <span class="block mt-2 font-label text-xs text-on-surface-variant">Transcrição da obra</span>
38
+ </div>
39
+ {% else %}
40
+ <span class="font-label text-xs text-on-surface-variant">Transcrição</span>
41
+ {% endif %}
42
+ </div>
43
+
44
+ <div class="transcription-block space-y-4">
45
+ {%- assign last_scene = nil -%}
46
+ {%- for line in include.transcription -%}
47
+ {%- assign char_val = line.character | default: "" | strip -%}
48
+ <div class="flex items-start gap-0 w-full" style="justify-content: flex-start;">
49
+ {% if line.timestamp and line.timestamp != '' %}
50
+ <span class="font-mono text-xs text-on-surface-variant whitespace-nowrap"
51
+ style="flex-basis:15%; flex-shrink:0; flex-grow:0; max-width:15%; text-align: left; align-self: flex-start;">[{{ line.timestamp }}]</span>
52
+ {% elsif line.scene and line.scene != '' and line.scene != last_scene %}
53
+ <span class="font-mono text-xs text-on-surface-variant whitespace-nowrap align-top"
54
+ style="flex-basis:15%; flex-shrink:0; flex-grow:0; max-width:15%; writing-mode: vertical-lr; text-align: left; align-self: flex-start;">{{ line.scene }}</span>
55
+ {%- assign last_scene = line.scene -%}
56
+ {% else %}
57
+ <span class="font-mono text-xs text-on-surface-variant opacity-0 whitespace-nowrap align-top"
58
+ style="flex-basis:15%; flex-shrink:0; flex-grow:0; max-width:15%;">&nbsp;</span>
59
+ {% endif %}
60
+ {% if char_val == "" %}
61
+ <span class="font-body text-on-surface italic opacity-60" style="flex-basis:85%; flex-shrink:1; flex-grow:1; max-width:85%; min-width:0;">{{ line.content }}</span>
62
+ {% else %}
63
+ {%- assign idx = 0 -%}
64
+ {%- for sp in speakers -%}
65
+ {%- if sp == line.character -%}
66
+ {%- break -%}
67
+ {%- endif -%}
68
+ {%- assign idx = idx | plus: 1 -%}
69
+ {%- endfor -%}
70
+ {%- assign color = color_palette[idx] | default: "outline" -%}
71
+ {%- assign name_words = char_val | downcase | split: " " -%}
72
+ {%- capture speaker_label -%}
73
+ {%- for w in name_words -%}{{ w | capitalize }}{%- unless forloop.last %} {% endunless -%}{%- endfor -%}
74
+ {%- endcapture -%}
75
+ <span class="transcription-speaker text-{{ color }}">{{ speaker_label | strip }}</span>
76
+ <span class="font-body text-on-surface" style="flex-basis:65%; flex-shrink:1; flex-grow:1; max-width:65%; min-width:0;">{{ line.content }}</span>
77
+ {% endif %}
78
+ </div>
79
+ {%- endfor -%}
80
+ </div>
@@ -0,0 +1,22 @@
1
+ {% if include.body %}
2
+ {% assign _handle = include.handle | default: '' | strip | remove_first: '@' %}
3
+ {% assign _icon = include.icon | default: 'chat' | strip %}
4
+ {% assign _url = include.url | default: '' | strip %}
5
+ {% assign _link = _url | remove: 'https://' | remove: 'http://' %}
6
+ <article class="sov-tweet article-tweet">
7
+ <div class="sov-tweet__head article-tweet__head">
8
+ <span class="sov-tweet__avatar article-tweet__avatar" aria-hidden="true"><span class="material-symbols-outlined">{{ _icon | escape }}</span></span>
9
+ <span class="sov-tweet__id article-tweet__id">
10
+ {% if include.name and include.name != '' %}<span class="sov-tweet__name article-tweet__name">{{ include.name | escape }}</span>{% endif %}
11
+ {% if _handle != '' %}<span class="sov-tweet__handle article-tweet__handle">@{{ _handle | escape }}</span>{% endif %}
12
+ </span>
13
+ </div>
14
+ <p class="sov-tweet__body article-tweet__body">{{ include.body }}</p>
15
+ {% if _url != '' or include.date %}
16
+ <p class="sov-tweet__cite article-tweet__cite">{% if _url != '' %}<a href="{{ _url }}">{{ _link }}</a>{% endif %}{% if _url != '' and include.date %} · {% endif %}{% if include.date %}{{ include.date | escape }}{% endif %}</p>
17
+ {% endif %}
18
+ {% if include.note and include.note != '' %}
19
+ <p class="sov-tweet__note article-tweet__note">{{ include.note }}</p>
20
+ {% endif %}
21
+ </article>
22
+ {% endif %}
@@ -0,0 +1,11 @@
1
+ {% if include.src %}
2
+ <figure class="article-video-landscape" style="margin: 1.5rem auto; text-align: center; max-width: 100%;">
3
+ <video controls style="max-width: 100%; width: 100%; border-radius: 0;">
4
+ <source src="{{ include.src }}" type="video/mp4">
5
+ Your browser does not support the video tag.
6
+ </video>
7
+ {% if include.caption %}
8
+ <figcaption>{{ include.caption }}</figcaption>
9
+ {% endif %}
10
+ </figure>
11
+ {% endif %}
@@ -0,0 +1,3 @@
1
+ {%- assign _xcite_all = site.articles | concat: site.posts | concat: site.projects | concat: site.products -%}
2
+ {%- assign _xcite_entry = _xcite_all | where: "key", include.key | first -%}
3
+ {%- if _xcite_entry -%}<a href="{{ _xcite_entry.url }}" class="xcite-ref" title="{{ _xcite_entry.title }}">{{ _xcite_entry.title }}</a>{%- else -%}<span class="xcite-missing" title="Internal reference '{{ include.key }}' not found">[{{ include.key }}?]</span>{%- endif -%}
@@ -0,0 +1,26 @@
1
+ {% assign short_src = include.src | default: include.id %}
2
+ {% assign short_src2 = include.src2 | default: include.id2 %}
3
+ {% if short_src %}
4
+ <figure class="article-youtube-short-wrap{% if short_src2 %} article-youtube-short-wrap--pair{% endif %} my-8 mx-auto text-center w-full" style="margin: 1.5rem auto; max-width: {% if short_src2 %}100{% else %}45{% endif %}%;">
5
+ <div class="article-youtube-short{% if short_src2 %} article-youtube-short--pair{% endif %}" style="display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem;">
6
+ <iframe width="315" height="560"
7
+ src="https://www.youtube.com/embed/{{ short_src }}"
8
+ title="YouTube short"
9
+ frameborder="0"
10
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
11
+ allowfullscreen
12
+ style="width: {% if short_src2 %}45{% else %}100{% endif %}%; max-width: 100%; aspect-ratio: 9 / 16; height: auto; border-radius: 0.25rem;">
13
+ </iframe>
14
+ {% if short_src2 %}
15
+ <iframe width="315" height="560"
16
+ src="https://www.youtube.com/embed/{{ short_src2 }}"
17
+ title="YouTube short"
18
+ frameborder="0"
19
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
20
+ allowfullscreen
21
+ style="width: 45%; max-width: 45%; aspect-ratio: 9 / 16; height: auto; border-radius: 0.25rem;">
22
+ </iframe>
23
+ {% endif %}
24
+ </div>
25
+ </figure>
26
+ {% endif %}
@@ -0,0 +1,12 @@
1
+ {% if include.id %}
2
+ <figure class="article-youtube-landscape my-8 mx-auto text-center w-full" style="margin: 1.5rem auto; max-width: 100%;">
3
+ <div style="display: flex; justify-content: center; aspect-ratio: 16 / 9;">
4
+ <iframe width="560" height="315" src="https://www.youtube.com/embed/{{ include.id }}" title="YouTube video"
5
+ frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
6
+ allowfullscreen style="width: 100%; height: 100%; max-width: 100%; border-radius: 0;"></iframe>
7
+ </div>
8
+ {% if include.caption %}
9
+ <figcaption>{{ include.caption }}</figcaption>
10
+ {% endif %}
11
+ </figure>
12
+ {% endif %}
@@ -0,0 +1,27 @@
1
+ ---
2
+ layout: text
3
+ ---
4
+
5
+ {% comment %}Studio front-matter metadata only. Public article detail pages render via ContentRuntime. Bibliography below applies to private Jekyll builds.{% endcomment %}
6
+
7
+ {{ content }}
8
+
9
+ {% capture _scholar_refs %}{% bibliography --cited %}{% endcapture %}
10
+ {% if _scholar_refs contains '<li>' %}
11
+ <div class="article-references">
12
+ <h2 class="section-title">References</h2>
13
+ {{ _scholar_refs }}
14
+ </div>
15
+ {% endif %}
16
+
17
+ <script>
18
+ (function () {
19
+ document.querySelectorAll('.article-references .bibliography li').forEach(function (li) {
20
+ li.innerHTML = li.innerHTML.replace(/^(<(?:sup|span)\b[^>]*>)\[[0-9]+\]\s*/, '$1');
21
+ li.innerHTML = li.innerHTML.replace(
22
+ /(Available(?:\s+at)?):\s*(https?:\/\/[^\s<"]+)([.,;)\]]*)/g,
23
+ '$1: <a href="$2" target="_blank" rel="noopener noreferrer">$2</a>$3'
24
+ );
25
+ });
26
+ })();
27
+ </script>