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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +77 -0
- data/DESIGN.md +1434 -0
- data/LICENSE +21 -0
- data/README.md +81 -0
- data/_data/themes/dark.yml +97 -0
- data/_data/themes/light.yml +120 -0
- data/_data/themes/shared.yml +37 -0
- data/_data/translations.yml +85 -0
- data/_includes/chord-tab.html +38 -0
- data/_includes/content-runtime/00-config.html +22 -0
- data/_includes/content-runtime/10-utils.html +176 -0
- data/_includes/content-runtime/20-markdown.html +264 -0
- data/_includes/content-runtime/25-code-blocks.html +315 -0
- data/_includes/content-runtime/30-includes-bib.html +190 -0
- data/_includes/content-runtime/30-includes-xcite.html +122 -0
- data/_includes/content-runtime/30-includes.html +365 -0
- data/_includes/content-runtime/40-listing.html +225 -0
- data/_includes/content-runtime/41-listing-helpers.html +71 -0
- data/_includes/content-runtime/42-listing-filter.html +208 -0
- data/_includes/content-runtime/43-blocks.html +379 -0
- data/_includes/content-runtime/50-detail.html +861 -0
- data/_includes/content-runtime/55-toc.html +176 -0
- data/_includes/content-runtime/60-fragments.html +567 -0
- data/_includes/content-runtime/90-bootstrap.html +76 -0
- data/_includes/cut-in.html +10 -0
- data/_includes/cv/cv-box.html +18 -0
- data/_includes/dict-card.html +24 -0
- data/_includes/figure.html +36 -0
- data/_includes/home/band-index.html +43 -0
- data/_includes/home/band-listing.html +49 -0
- data/_includes/mathjax.html +10 -0
- data/_includes/ongoing-product.html +1 -0
- data/_includes/page/content-runtime.html +20 -0
- data/_includes/page/date_evolution.html +19 -0
- data/_includes/page/figure-lightbox.html +102 -0
- data/_includes/page/formatted_date.html +15 -0
- data/_includes/page/hero-default.html +18 -0
- data/_includes/page/listing-runtime-attrs.html +8 -0
- data/_includes/page/mermaid.html +101 -0
- data/_includes/page/network-graph.html +1253 -0
- data/_includes/page/page-header.html +41 -0
- data/_includes/page/professional-connections.html +2 -0
- data/_includes/page/site-footer.html +101 -0
- data/_includes/page/timeline-section.html +868 -0
- data/_includes/plotly.html +22 -0
- data/_includes/theme/dev-reexport.html +100 -0
- data/_includes/theme/favicons.html +4 -0
- data/_includes/theme/font-loader.html +55 -0
- data/_includes/theme/shell-theme-boot.html +115 -0
- data/_includes/theme/site-logo.html +6 -0
- data/_includes/theme/tailwind-theme-colors.json.liquid +65 -0
- data/_includes/theme/tailwind-theme-fonts.json.liquid +6 -0
- data/_includes/theme/theme-context.liquid +20 -0
- data/_includes/theme/theme-toggle.html +94 -0
- data/_includes/theme/theme-vars-block.liquid +161 -0
- data/_includes/theme/theme-vars.html +16 -0
- data/_includes/transcription.html +80 -0
- data/_includes/tweet.html +22 -0
- data/_includes/video.html +11 -0
- data/_includes/xcite.html +3 -0
- data/_includes/youtube-short.html +26 -0
- data/_includes/youtube.html +12 -0
- data/_layouts/article.html +27 -0
- data/_layouts/articles.html +534 -0
- data/_layouts/bibliography.html +417 -0
- data/_layouts/blog.html +544 -0
- data/_layouts/cv.html +393 -0
- data/_layouts/home.html +319 -0
- data/_layouts/network.html +9 -0
- data/_layouts/pandorga/articles.html +4 -0
- data/_layouts/pandorga/bibliography.html +4 -0
- data/_layouts/pandorga/blog.html +4 -0
- data/_layouts/pandorga/cv.html +4 -0
- data/_layouts/pandorga/media.html +4 -0
- data/_layouts/pandorga/network.html +4 -0
- data/_layouts/pandorga/portfolio.html +4 -0
- data/_layouts/post.html +7 -0
- data/_layouts/product.html +5 -0
- data/_layouts/project.html +27 -0
- data/_layouts/projects.html +534 -0
- data/_layouts/resources.html +387 -0
- data/_layouts/shell.html +203 -0
- data/_layouts/text.html +246 -0
- data/_plugins/bibliography_artifact.rb +57 -0
- data/_plugins/content_backend.rb +37 -0
- data/_plugins/content_collections.rb +51 -0
- data/_plugins/content_detail_paths.rb +58 -0
- data/_plugins/content_headers.rb +73 -0
- data/_plugins/content_keys.rb +310 -0
- data/_plugins/content_local_serve.rb +168 -0
- data/_plugins/content_site_adapter.rb +159 -0
- data/_plugins/content_validators_hook.rb +20 -0
- data/_plugins/content_writing_index.rb +52 -0
- data/_plugins/content_writing_paths.rb +55 -0
- data/_plugins/force_utf8_encoding.rb +13 -0
- data/_plugins/git_chronology.rb +211 -0
- data/_plugins/git_metadata.rb +43 -0
- data/_plugins/job_sections.rb +25 -0
- data/_plugins/lib/content_validators.rb +27 -0
- data/_plugins/lib/resource_validator.rb +56 -0
- data/_plugins/lib/tag_validator.rb +153 -0
- data/_plugins/lib/text_excerpt.rb +56 -0
- data/_plugins/lib/writing_entry.rb +107 -0
- data/_plugins/local_detail_rewrites.rb +194 -0
- data/_plugins/timeline.rb +507 -0
- data/_plugins/writing_slug_limits.rb +70 -0
- data/assets/css/article.css +1330 -0
- data/assets/css/base.css +1432 -0
- data/assets/css/cv.css +13 -0
- data/assets/css/global-link.css +94 -0
- data/assets/css/ledger/bibliography.css +307 -0
- data/assets/css/ledger/chrome.css +118 -0
- data/assets/css/ledger/cv.css +836 -0
- data/assets/css/ledger/detail.css +1270 -0
- data/assets/css/ledger/footer.css +298 -0
- data/assets/css/ledger/home.css +1490 -0
- data/assets/css/ledger/network.css +106 -0
- data/assets/css/ledger/projects.css +716 -0
- data/assets/css/ledger/rascunhos.css +379 -0
- data/assets/css/ledger/sources.css +327 -0
- data/assets/css/ledger/writing.css +754 -0
- data/assets/tailwind-play.js +15 -0
- data/examples/full/Gemfile +7 -0
- data/examples/full/_config.yml +92 -0
- data/examples/full/_redirects +3 -0
- data/examples/full/content/collections/articles/2026-01-01-welcome.md +9 -0
- data/examples/full/content/collections/data/contacts.yml +3 -0
- data/examples/full/content/collections/data/education.yml +4 -0
- data/examples/full/content/collections/jobs/research-fellow.md +12 -0
- data/examples/full/content/collections/posts/2026-01-02-note.md +9 -0
- data/examples/full/content/collections/products/ledger-tool.md +6 -0
- data/examples/full/content/collections/projects/demo-kit.md +10 -0
- data/examples/full/content/collections/resources/style-guide.md +7 -0
- data/examples/full/content/pages/cv/summary.md +1 -0
- data/examples/full/content/pages/headers.yml +20 -0
- data/examples/full/index.html +4 -0
- data/examples/minimal/Gemfile +7 -0
- data/examples/minimal/_config.yml +47 -0
- data/examples/minimal/_redirects +3 -0
- data/examples/minimal/content/collections/articles/2026-01-01-hello.md +10 -0
- data/examples/minimal/content/collections/articles/2026-01-02-second.md +9 -0
- data/examples/minimal/content/pages/headers.yml +6 -0
- data/examples/minimal/index.html +4 -0
- data/exe/pandorga +7 -0
- data/functions/api/studio/[[path]].js +562 -0
- data/functions/api/studio/_lib/auth.js +131 -0
- data/functions/api/studio/_lib/blocks.js +156 -0
- data/functions/api/studio/_lib/gemini.js +62 -0
- data/functions/api/studio/_lib/github.js +278 -0
- data/functions/api/studio/_lib/guidelines.js +143 -0
- data/functions/api/studio/_lib/paths.js +76 -0
- data/functions/api/studio/_lib/prompts.js +165 -0
- data/functions/api/studio/_lib/rate-limit.js +40 -0
- data/functions/api/studio/_lib/rewrite.js +136 -0
- data/functions/api/studio/_lib/sanitize.js +143 -0
- data/lib/pandorga/cli.rb +66 -0
- data/lib/pandorga/commands/doctor.rb +66 -0
- data/lib/pandorga/commands/export.rb +30 -0
- data/lib/pandorga/commands/install_functions.rb +41 -0
- data/lib/pandorga/commands/new.rb +113 -0
- data/lib/pandorga/commands/publish.rb +53 -0
- data/lib/pandorga/commands/serve.rb +34 -0
- data/lib/pandorga/commands/studio_schema.rb +309 -0
- data/lib/pandorga/jekyll/generator.rb +74 -0
- data/lib/pandorga/jekyll/plugins/bibliography_artifact.rb +57 -0
- data/lib/pandorga/jekyll/plugins/content_backend.rb +37 -0
- data/lib/pandorga/jekyll/plugins/content_collections.rb +51 -0
- data/lib/pandorga/jekyll/plugins/content_detail_paths.rb +58 -0
- data/lib/pandorga/jekyll/plugins/content_headers.rb +73 -0
- data/lib/pandorga/jekyll/plugins/content_keys.rb +310 -0
- data/lib/pandorga/jekyll/plugins/content_local_serve.rb +168 -0
- data/lib/pandorga/jekyll/plugins/content_site_adapter.rb +159 -0
- data/lib/pandorga/jekyll/plugins/content_validators_hook.rb +20 -0
- data/lib/pandorga/jekyll/plugins/content_writing_index.rb +52 -0
- data/lib/pandorga/jekyll/plugins/content_writing_paths.rb +55 -0
- data/lib/pandorga/jekyll/plugins/force_utf8_encoding.rb +13 -0
- data/lib/pandorga/jekyll/plugins/git_chronology.rb +211 -0
- data/lib/pandorga/jekyll/plugins/git_metadata.rb +43 -0
- data/lib/pandorga/jekyll/plugins/job_sections.rb +25 -0
- data/lib/pandorga/jekyll/plugins/lib/content_validators.rb +27 -0
- data/lib/pandorga/jekyll/plugins/lib/resource_validator.rb +56 -0
- data/lib/pandorga/jekyll/plugins/lib/tag_validator.rb +153 -0
- data/lib/pandorga/jekyll/plugins/lib/text_excerpt.rb +56 -0
- data/lib/pandorga/jekyll/plugins/lib/writing_entry.rb +107 -0
- data/lib/pandorga/jekyll/plugins/local_detail_rewrites.rb +194 -0
- data/lib/pandorga/jekyll/plugins/timeline.rb +507 -0
- data/lib/pandorga/jekyll/plugins/writing_slug_limits.rb +70 -0
- data/lib/pandorga/jekyll.rb +15 -0
- data/lib/pandorga/object_store_env.rb +51 -0
- data/lib/pandorga/registry/schema.yml +115 -0
- data/lib/pandorga/registry.rb +279 -0
- data/lib/pandorga/version.rb +5 -0
- data/lib/pandorga.rb +19 -0
- data/scripts/cdn-allowlist.json +19 -0
- data/scripts/content/backfill-content-keys.rb +99 -0
- data/scripts/content/band_art.rb +362 -0
- data/scripts/content/export-content-json.rb +741 -0
- data/scripts/content/flatten-ansi-capture.mjs +153 -0
- data/scripts/content/hero_portraits.rb +28 -0
- data/scripts/content/publish-content-to-object-store.sh +78 -0
- data/scripts/content/publish-content-to-r2.sh +5 -0
- data/scripts/content/sync-content-json-to-r2.rb +167 -0
- data/scripts/content/sync-content-redirects.rb +12 -0
- data/scripts/generate-configuration-doc.rb +127 -0
- data/scripts/lib/content_license.rb +50 -0
- data/scripts/lib/content_storage_config.rb +70 -0
- data/scripts/lib/normalize-display-math.mjs +52 -0
- data/scripts/lib/r2_json_sync.rb +76 -0
- data/scripts/lib/withdrawn_public_objects.rb +23 -0
- data/scripts/lib/writing_index_contract.rb +61 -0
- data/scripts/pdf/md-to-pdf.mjs +1400 -0
- data/scripts/pdf/md-to-pdf.sh +27 -0
- data/scripts/test/fixtures/dual-template/_config.yml +32 -0
- data/scripts/test/fixtures/dual-template/index.html +4 -0
- data/scripts/test/render-parity/article-note.json +5 -0
- data/scripts/test/render-parity/blockquote.json +5 -0
- data/scripts/test/render-parity/blocks/ansi-basic.json +7 -0
- data/scripts/test/render-parity/blocks/ansi-bold-is-bright.json +7 -0
- data/scripts/test/render-parity/blocks/ansi-drops-cursor-moves.json +7 -0
- data/scripts/test/render-parity/blocks/ansi-escapes-markup.json +7 -0
- data/scripts/test/render-parity/blocks/ansi-extended-colour.json +7 -0
- data/scripts/test/render-parity/blocks/ansi-real-control-byte.json +7 -0
- data/scripts/test/render-parity/blocks/archive-card-date-once.json +19 -0
- data/scripts/test/render-parity/blocks/archive-card-empty-foot.json +7 -0
- data/scripts/test/render-parity/blocks/archive-card-footer-dates.json +22 -0
- data/scripts/test/render-parity/blocks/archive-card-meta-parts.json +26 -0
- data/scripts/test/render-parity/blocks/archive-card.json +32 -0
- data/scripts/test/render-parity/blocks/cta-link.json +13 -0
- data/scripts/test/render-parity/blocks/lang-pill-pt.json +8 -0
- data/scripts/test/render-parity/blocks/lang-pill.json +8 -0
- data/scripts/test/render-parity/blocks/ledger-row-meta-parts.json +29 -0
- data/scripts/test/render-parity/blocks/ledger-row.json +35 -0
- data/scripts/test/render-parity/blocks/meta-strip.json +27 -0
- data/scripts/test/render-parity/blocks/poster-media-image.json +8 -0
- data/scripts/test/render-parity/blocks/poster-media-video.json +9 -0
- data/scripts/test/render-parity/blocks/section-banner.json +13 -0
- data/scripts/test/render-parity/blocks/system-card-custom-meta.json +22 -0
- data/scripts/test/render-parity/blocks/system-card.json +48 -0
- data/scripts/test/render-parity/blocks/tag-chips-links.json +12 -0
- data/scripts/test/render-parity/blocks/tag-chips.json +9 -0
- data/scripts/test/render-parity/cut-in-blank-line.json +9 -0
- data/scripts/test/render-parity/cut-in-right.json +9 -0
- data/scripts/test/render-parity/cut-in.json +8 -0
- data/scripts/test/render-parity/figure.json +5 -0
- data/scripts/test/render-parity/load-runtime.mjs +337 -0
- data/scripts/test/render-parity/ongoing-product.json +5 -0
- data/scripts/test/render-parity/plotly.json +5 -0
- data/scripts/test/render-parity/poem.json +5 -0
- data/scripts/test/render-parity/run-absent-field-checks.mjs +232 -0
- data/scripts/test/render-parity/run-block-fixture.mjs +86 -0
- data/scripts/test/render-parity/run-detail-parity-checks.mjs +123 -0
- data/scripts/test/render-parity/run-field-flow-checks.mjs +227 -0
- data/scripts/test/render-parity/run-fixture.mjs +159 -0
- data/scripts/test/render-parity/selectors.mjs +43 -0
- data/scripts/test/render-parity/video.json +5 -0
- data/scripts/test/render-parity/youtube-short.json +10 -0
- data/scripts/test/render-parity/youtube.json +8 -0
- data/scripts/test/test-absent-field-absent-element.rb +43 -0
- data/scripts/test/test-cdn-pins.rb +44 -0
- data/scripts/test/test-cloudflare-watch-paths-doc.rb +31 -0
- data/scripts/test/test-configuration-doc.rb +36 -0
- data/scripts/test/test-content-detail-urls.rb +346 -0
- data/scripts/test/test-content-keys.rb +110 -0
- data/scripts/test/test-content-license.rb +45 -0
- data/scripts/test/test-content-render-parity.rb +38 -0
- data/scripts/test/test-content-validators.rb +12 -0
- data/scripts/test/test-css-structure.rb +72 -0
- data/scripts/test/test-cv-export-contract.rb +158 -0
- data/scripts/test/test-detail-render-parity.rb +40 -0
- data/scripts/test/test-dev-reexport.rb +122 -0
- data/scripts/test/test-doctor.rb +39 -0
- data/scripts/test/test-dual-template-instances.rb +87 -0
- data/scripts/test/test-dual-template-runtime.rb +167 -0
- data/scripts/test/test-examples-build.rb +60 -0
- data/scripts/test/test-export-content-json-incremental.rb +138 -0
- data/scripts/test/test-export.rb +53 -0
- data/scripts/test/test-exported-fields-reach-the-view.rb +60 -0
- data/scripts/test/test-form-control-theming.rb +143 -0
- data/scripts/test/test-git-chronology.rb +414 -0
- data/scripts/test/test-hero-portraits.mjs +97 -0
- data/scripts/test/test-hero-portraits.rb +162 -0
- data/scripts/test/test-home-band-art.rb +135 -0
- data/scripts/test/test-install-functions.rb +39 -0
- data/scripts/test/test-language-export.rb +79 -0
- data/scripts/test/test-listing-filter-contract.rb +119 -0
- data/scripts/test/test-math-row-breaks.mjs +79 -0
- data/scripts/test/test-math-row-breaks.rb +18 -0
- data/scripts/test/test-new.rb +113 -0
- data/scripts/test/test-no-personal-data.rb +85 -0
- data/scripts/test/test-public-cv-export.rb +164 -0
- data/scripts/test/test-r2-config-consistency.rb +57 -0
- data/scripts/test/test-r2-json-sync.rb +193 -0
- data/scripts/test/test-registry-home.rb +34 -0
- data/scripts/test/test-runtime-js-syntax.rb +64 -0
- data/scripts/test/test-slim-writing-index.rb +126 -0
- data/scripts/test/test-studio-api-contract.rb +99 -0
- data/scripts/test/test-studio-content-files.rb +79 -0
- data/scripts/test/test-studio-liquid-roundtrip.mjs +179 -0
- data/scripts/test/test-studio-rate-limit.mjs +70 -0
- data/scripts/test/test-studio-rewrite-api-contract.rb +59 -0
- data/scripts/test/test-studio-rewrite-sanitize.mjs +210 -0
- data/scripts/test/test-studio-schema-composition.rb +126 -0
- data/scripts/test/test-studio-slug-config.rb +167 -0
- data/scripts/test/test-template-packages.rb +51 -0
- data/scripts/test/test-text-excerpt.rb +51 -0
- data/scripts/test/test-unwrap-media-src.mjs +39 -0
- data/scripts/test/test-unwrap-media-src.rb +19 -0
- data/scripts/test/test-withdrawn-content-export.rb +69 -0
- data/scripts/test/test-writing-index-export-slugs.rb +84 -0
- data/scripts/test/test-writing-slug-limits.rb +61 -0
- data/scripts/test/test-xcite-chronology.rb +106 -0
- data/scripts/validate.sh +74 -0
- data/scripts/visual/build-cv-pages-json.js +33 -0
- data/scripts/visual/build-site-pages-json.js +94 -0
- data/scripts/visual/publish-visual-inspection-to-r2.sh +42 -0
- data/scripts/visual/take-screenshots.js +88 -0
- data/scripts/visual/visual-inspection-pages.json +113 -0
- data/studio/assets/index-B_j0pMD5.css +1 -0
- data/studio/assets/index-Ch4YEv9g.js +285 -0
- data/studio/index.html +27 -0
- data/studio/schema.yml +869 -0
- data/studio-app/README.md +27 -0
- data/studio-app/index.html +26 -0
- data/studio-app/package-lock.json +2499 -0
- data/studio-app/package.json +26 -0
- data/studio-app/skills-lock.json +47 -0
- data/studio-app/src/api/client.js +71 -0
- data/studio-app/src/editor/body-editor.js +70 -0
- data/studio-app/src/editor/liquid-core.js +382 -0
- data/studio-app/src/editor/liquid.js +77 -0
- data/studio-app/src/lib/document.js +207 -0
- data/studio-app/src/lib/list-controls.js +317 -0
- data/studio-app/src/lib/schema.js +66 -0
- data/studio-app/src/lib/yaml-doc.js +100 -0
- data/studio-app/src/main.js +3693 -0
- data/studio-app/src/media/browser.js +578 -0
- data/studio-app/src/preview/render.js +430 -0
- data/studio-app/src/styles/studio.css +2826 -0
- data/studio-app/vite.config.js +25 -0
- data/templates/articles/README.md +40 -0
- data/templates/articles/fixtures/article.md +9 -0
- data/templates/articles/screenshots/README.md +6 -0
- data/templates/articles/studio.yml +73 -0
- data/templates/articles/template.yml +21 -0
- data/templates/bibliography/README.md +41 -0
- data/templates/bibliography/fixtures/references.json +9 -0
- data/templates/bibliography/screenshots/README.md +6 -0
- data/templates/bibliography/studio.yml +66 -0
- data/templates/bibliography/template.yml +18 -0
- data/templates/blog/README.md +38 -0
- data/templates/blog/fixtures/post.md +9 -0
- data/templates/blog/screenshots/README.md +6 -0
- data/templates/blog/studio.yml +69 -0
- data/templates/blog/template.yml +20 -0
- data/templates/cv/README.md +34 -0
- data/templates/cv/fixtures/job.md +12 -0
- data/templates/cv/screenshots/README.md +6 -0
- data/templates/cv/studio.yml +200 -0
- data/templates/cv/template.yml +25 -0
- data/templates/media/README.md +38 -0
- data/templates/media/fixtures/resource.md +7 -0
- data/templates/media/screenshots/README.md +6 -0
- data/templates/media/studio.yml +58 -0
- data/templates/media/template.yml +17 -0
- data/templates/network/README.md +34 -0
- data/templates/network/fixtures/sources.json +3 -0
- data/templates/network/screenshots/README.md +6 -0
- data/templates/network/studio.yml +28 -0
- data/templates/network/template.yml +10 -0
- data/templates/portfolio/README.md +39 -0
- data/templates/portfolio/fixtures/project.md +10 -0
- data/templates/portfolio/screenshots/README.md +6 -0
- data/templates/portfolio/studio.yml +89 -0
- data/templates/portfolio/template.yml +18 -0
- metadata +505 -0
|
@@ -0,0 +1,1330 @@
|
|
|
1
|
+
/* ── Article content ── */
|
|
2
|
+
:root {
|
|
3
|
+
--reading-measure: 45rem;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.text-reading-column {
|
|
7
|
+
width: 100%;
|
|
8
|
+
max-width: var(--reading-measure);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Sidebar title/description/meta share the same measure as the body column */
|
|
12
|
+
.text-detail-sidebar {
|
|
13
|
+
width: 100%;
|
|
14
|
+
max-width: var(--reading-measure);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Detail pages: title in left third; body starts aligned with the title */
|
|
18
|
+
.text-page-main {
|
|
19
|
+
align-self: start;
|
|
20
|
+
background-color: var(--color-background);
|
|
21
|
+
border: 1.5px solid var(--border-ink);
|
|
22
|
+
border-radius: var(--radius-card);
|
|
23
|
+
padding: var(--space-md);
|
|
24
|
+
width: 100%;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@media (min-width: 768px) {
|
|
28
|
+
.text-page-main {
|
|
29
|
+
padding: var(--space-2xl);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Size and leading are not set here: ledger/detail.css loads after this sheet
|
|
34
|
+
and owns both (1.1rem at var(--leading-body)). They used to be declared in
|
|
35
|
+
both files, 1em/1.8 here against 1.1rem/1.7 there, and the pair that lost
|
|
36
|
+
was still being used to derive line-box measurements further down. */
|
|
37
|
+
/* Body / prose ink is full on-surface (black in light, white in dark).
|
|
38
|
+
Meta, captions, and chrome stay on on-surface-variant. */
|
|
39
|
+
.article-content {
|
|
40
|
+
width: 100%;
|
|
41
|
+
max-width: var(--reading-measure);
|
|
42
|
+
color: var(--color-on-surface);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Section headings — content-title voice (Regular 400, never bold);
|
|
46
|
+
detail.css owns final size/leading. */
|
|
47
|
+
.article-content h1,
|
|
48
|
+
.article-content h2,
|
|
49
|
+
.article-content h3 {
|
|
50
|
+
font-family: var(--font-content-title);
|
|
51
|
+
font-weight: var(--weight-title);
|
|
52
|
+
font-synthesis: none;
|
|
53
|
+
margin-top: 2rem;
|
|
54
|
+
margin-bottom: 0.75rem;
|
|
55
|
+
color: var(--color-on-surface);
|
|
56
|
+
border-left: none;
|
|
57
|
+
padding-left: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.article-content h4 {
|
|
61
|
+
font-family: var(--font-body);
|
|
62
|
+
font-weight: var(--weight-title);
|
|
63
|
+
font-synthesis: none;
|
|
64
|
+
margin-top: 2rem;
|
|
65
|
+
margin-bottom: 0.75rem;
|
|
66
|
+
color: var(--color-on-surface);
|
|
67
|
+
border-left: none;
|
|
68
|
+
padding-left: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.article-content h1 {
|
|
72
|
+
font-size: var(--type-title);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.article-content h2 {
|
|
76
|
+
font-size: var(--type-title);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.article-content h3 {
|
|
80
|
+
font-size: var(--type-card-lg);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.article-content h4 {
|
|
84
|
+
font-size: var(--type-body);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* The paragraph gap belongs to ledger/detail.css, which sets it at 1.15em;
|
|
88
|
+
only the reset survives here. */
|
|
89
|
+
.article-content p {
|
|
90
|
+
margin-top: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* MathJax: keep inline math in the text flow; display math on its own line */
|
|
94
|
+
.article-content mjx-container:not([display="true"]) {
|
|
95
|
+
display: inline;
|
|
96
|
+
margin: 0;
|
|
97
|
+
vertical-align: baseline;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* A display equation is a block in the prose flow, so it takes the paragraph
|
|
101
|
+
gap and nothing more. Two things used to inflate it, to a measured 52px
|
|
102
|
+
above the equation and 39px below against a 22px paragraph gap.
|
|
103
|
+
|
|
104
|
+
First, the runtime wraps a standalone `$$…$$` in a `.math-display` div
|
|
105
|
+
(20-markdown.html) and MathJax then renders its own container inside that
|
|
106
|
+
wrapper, so a selector list naming both spaced one equation twice.
|
|
107
|
+
|
|
108
|
+
Second, the container half of that pair never applied: MathJax injects
|
|
109
|
+
`mjx-container[jax="CHTML"][display="true"] { margin: 1em 0 }` at runtime,
|
|
110
|
+
which outranks `.article-content mjx-container[display="true"]` on
|
|
111
|
+
specificity. The 0.5rem this file asked for was never what the page drew.
|
|
112
|
+
|
|
113
|
+
So the wrapper owns the gap, the scroll and the padding; the container
|
|
114
|
+
inside it owns none of them. */
|
|
115
|
+
.article-content .math-display {
|
|
116
|
+
display: block;
|
|
117
|
+
/* The bottom gap is the paragraph gap `.article-content p` sets in
|
|
118
|
+
ledger/detail.css; the top gap is left to the previous block, exactly as
|
|
119
|
+
it is between two paragraphs. The negative top margin hands back the
|
|
120
|
+
padding below, which a margin collapse cannot absorb — the two are one
|
|
121
|
+
number, so change them together or the rhythm moves. */
|
|
122
|
+
margin: -1.25rem 0 calc(1.15em - 1.25rem);
|
|
123
|
+
/* overflow-x lets an over-wide equation scroll sideways. CSS forces
|
|
124
|
+
overflow-y to auto alongside it, so a bracket, radical or matrix rule that
|
|
125
|
+
MathJax draws past its own line box gets clipped, or grows a vertical
|
|
126
|
+
scrollbar it has no use for. The padding is the allowance for that
|
|
127
|
+
overshoot: 1.25rem clears the worst case on the object-centric article
|
|
128
|
+
(a 3-row bmatrix, measured at 12px past a 0.5rem allowance). It costs
|
|
129
|
+
nothing visually, because the margin above gives it straight back. */
|
|
130
|
+
padding: 1.25rem 0;
|
|
131
|
+
overflow-x: auto;
|
|
132
|
+
text-align: center;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* `[jax]` is redundant as a selector — every MathJax container carries it —
|
|
136
|
+
but it lifts this rule past MathJax's own injected margin, which is the
|
|
137
|
+
whole point of the rule. */
|
|
138
|
+
.article-content .math-display mjx-container[jax][display="true"] {
|
|
139
|
+
margin: 0;
|
|
140
|
+
padding: 0;
|
|
141
|
+
overflow: visible;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* Display math the wrapper regex cannot reach, because the `$$` sits inside a
|
|
145
|
+
list item or a table cell rather than alone in a paragraph. It still has to
|
|
146
|
+
break the line; its vertical margin stays MathJax's own 1em, which lands
|
|
147
|
+
within a pixel of the paragraph gap. */
|
|
148
|
+
.article-content mjx-container[display="true"] {
|
|
149
|
+
display: block;
|
|
150
|
+
overflow-x: auto;
|
|
151
|
+
text-align: center;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* End-of-section brand strip: centered raw HTML row of logos (not figure.html).
|
|
155
|
+
Default height is shared; override per <img style="height:…"> when a mark
|
|
156
|
+
needs a different optical weight. Pairs sit ~2ch apart (inline-block, not flex).
|
|
157
|
+
Vertical margin is one body line box before and after, read off the leading
|
|
158
|
+
token rather than copied as a number; 1lh is unusable while line-height is
|
|
159
|
+
0, and the literal this used to carry (1.8em) outlived the leading it was
|
|
160
|
+
measured against. */
|
|
161
|
+
.article-content .article-brand-strip {
|
|
162
|
+
display: block;
|
|
163
|
+
text-align: center;
|
|
164
|
+
margin: calc(var(--leading-body) * 1em) 0;
|
|
165
|
+
line-height: 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.article-content .article-brand-strip img {
|
|
169
|
+
display: inline-block;
|
|
170
|
+
vertical-align: middle;
|
|
171
|
+
width: auto;
|
|
172
|
+
height: 2.75rem;
|
|
173
|
+
max-width: min(100%, 14rem);
|
|
174
|
+
object-fit: contain;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.article-content .article-brand-strip img+img {
|
|
178
|
+
margin-left: 2ch;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.article-content .article-brand-strip-dark {
|
|
182
|
+
display: inline-flex;
|
|
183
|
+
align-items: center;
|
|
184
|
+
justify-content: center;
|
|
185
|
+
box-sizing: border-box;
|
|
186
|
+
padding: 0.35rem 0.55rem;
|
|
187
|
+
/* A fixed dark plate beNewsreader light-on-transparent marks. It must stay dark in
|
|
188
|
+
both themes, so no surface token fits: every one of them flips. */
|
|
189
|
+
background: #111;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.article-content .article-brand-strip-dark img {
|
|
193
|
+
height: 2.25rem;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* Poem blocks preserve line breaks typed in Markdown without trailing spaces.
|
|
197
|
+
Same face and size as the reading column — not a smaller apparatus step. */
|
|
198
|
+
.article-content .poem {
|
|
199
|
+
white-space: normal;
|
|
200
|
+
font-family: var(--font-body);
|
|
201
|
+
font-size: var(--type-prose);
|
|
202
|
+
font-weight: var(--weight-body);
|
|
203
|
+
font-style: normal;
|
|
204
|
+
line-height: var(--leading-prose);
|
|
205
|
+
color: var(--color-on-surface);
|
|
206
|
+
margin: 0;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.article-content .poem p {
|
|
210
|
+
white-space: pre-line;
|
|
211
|
+
font-size: inherit;
|
|
212
|
+
font-weight: inherit;
|
|
213
|
+
margin: 0;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.article-content .poem p+p {
|
|
217
|
+
/* Blank line in source = new stanza or refrão. */
|
|
218
|
+
margin-top: 1em;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.article-content ul {
|
|
222
|
+
margin-left: 1.5rem;
|
|
223
|
+
margin-bottom: 1rem;
|
|
224
|
+
list-style-type: disc;
|
|
225
|
+
list-style-position: outside;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.article-content ol {
|
|
229
|
+
margin-left: 1.5rem;
|
|
230
|
+
margin-bottom: 1rem;
|
|
231
|
+
list-style-type: decimal;
|
|
232
|
+
list-style-position: outside;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.article-content li {
|
|
236
|
+
margin-bottom: 0.3rem;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.article-content code {
|
|
240
|
+
background: var(--color-surface-container);
|
|
241
|
+
padding: 0.1rem 0.3rem;
|
|
242
|
+
font-size: var(--type-meta);
|
|
243
|
+
font-family: var(--font-code);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
:is(.article-content, .article-cite-block) pre {
|
|
247
|
+
background: var(--color-surface-container);
|
|
248
|
+
padding: 1rem;
|
|
249
|
+
overflow-x: auto;
|
|
250
|
+
margin-bottom: 1rem;
|
|
251
|
+
font-family: var(--font-code);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
:is(.article-content, .article-cite-block) pre code {
|
|
255
|
+
background: none;
|
|
256
|
+
padding: 0;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.article-content a {
|
|
260
|
+
color: var(--color-link);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.article-content img {
|
|
264
|
+
display: block;
|
|
265
|
+
margin-left: auto;
|
|
266
|
+
margin-right: auto;
|
|
267
|
+
max-width: 100%;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
@media (max-width: 767px) {
|
|
271
|
+
|
|
272
|
+
/* Override inline figure widths to allow full width on mobile */
|
|
273
|
+
.article-content figure[style] {
|
|
274
|
+
max-width: 100% !important;
|
|
275
|
+
width: 100% !important;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.article-content img:not(.article-cut-in) {
|
|
279
|
+
width: 100%;
|
|
280
|
+
max-height: 100vw;
|
|
281
|
+
object-fit: contain;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.article-content img.article-cut-in {
|
|
285
|
+
max-height: calc(var(--leading-body) * 6em);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* Typographic cut-in: 3×4 portrait floated into the column so type wraps
|
|
290
|
+
around it. Height is six body line boxes and the width is the 3:4 of that,
|
|
291
|
+
both derived from the leading token so the crop keeps landing on the text
|
|
292
|
+
it wraps. The pair was frozen at 8.1em by 10.8em, which was six line boxes
|
|
293
|
+
of a leading the sheet no longer sets.
|
|
294
|
+
|
|
295
|
+
The image carries the geometry; the plate around it carries the float. */
|
|
296
|
+
.article-content img.article-cut-in {
|
|
297
|
+
display: block;
|
|
298
|
+
width: calc(var(--leading-body) * 4.5em);
|
|
299
|
+
height: calc(var(--leading-body) * 6em);
|
|
300
|
+
max-width: 100%;
|
|
301
|
+
object-fit: cover;
|
|
302
|
+
object-position: center 18%;
|
|
303
|
+
margin: 0;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* A cut-in sits on the same plate as every other picture in an article, which
|
|
307
|
+
it did not before: the include emitted a bare `<img>`, so a portrait dropped
|
|
308
|
+
into a paragraph was the one piece of media on the site arriving without a
|
|
309
|
+
ground under it. `.article-content figure` is written for a full-width
|
|
310
|
+
block, so the three properties that make this one a float have to be stated
|
|
311
|
+
back: the width shrinks to its portrait, the float moves here off the image,
|
|
312
|
+
and the margin becomes the gutter the wrapping text reads against. The
|
|
313
|
+
padding is the small step, not the figure's own: a plate as thick as a
|
|
314
|
+
full-width figure's would swallow a 3x4 portrait set into running text. */
|
|
315
|
+
.article-content figure.article-cut-in-plate {
|
|
316
|
+
float: left;
|
|
317
|
+
display: block;
|
|
318
|
+
width: auto;
|
|
319
|
+
max-width: 40%;
|
|
320
|
+
margin: 0.15em 0.9em 0.45em 0;
|
|
321
|
+
padding: var(--space-sm);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.article-content figure.article-cut-in-plate.article-cut-in--right {
|
|
325
|
+
float: right;
|
|
326
|
+
margin: 0.15em 0 0.45em 0.9em;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/* Short wrapping grafs beside a cut-in. The default 1.1em paragraph gap is
|
|
330
|
+
meant for long reading-column blocks; next to a 3×4 float and a *** rule
|
|
331
|
+
it reads as an extra blank line. Only pages that float a cut-in as a
|
|
332
|
+
direct child (the bulletin layout) pick this up. */
|
|
333
|
+
.article-content:has(> figure.article-cut-in-plate)>p {
|
|
334
|
+
margin-bottom: 0.8em;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.article-content:has(> figure.article-cut-in-plate)>hr {
|
|
338
|
+
margin: 1.5rem 0;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/* ── Media plates ──
|
|
342
|
+
Every figure sits on a plate. This is not decoration: a light-background
|
|
343
|
+
Matplotlib plot and a dark simulation video otherwise arrive on the page as
|
|
344
|
+
two different surfaces, and a transparent PNG on the dark palette reads as a
|
|
345
|
+
rendering fault rather than as a picture. The plate gives all of them one
|
|
346
|
+
ground in both themes.
|
|
347
|
+
|
|
348
|
+
The projects page had this to itself until now; these rules are the ones it
|
|
349
|
+
proved, moved up so the treatment is the site's rather than one page's. What
|
|
350
|
+
stayed beNewsreader in ledger/projects.css is the *other* thing that page does —
|
|
351
|
+
letting media out of the reading measure — which is a separate decision.
|
|
352
|
+
|
|
353
|
+
`.plotly-figure` and the videos both carry the `figure` element, so one
|
|
354
|
+
selector covers the include library, the browser runtime and the two legacy
|
|
355
|
+
WordPress `wp-block-image` figures in the Portuguese posts. */
|
|
356
|
+
.article-content figure {
|
|
357
|
+
margin: 1.5rem auto;
|
|
358
|
+
text-align: center;
|
|
359
|
+
width: 100%;
|
|
360
|
+
display: block;
|
|
361
|
+
background: var(--color-surface-container-low);
|
|
362
|
+
padding: var(--space-md);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.article-content figure img {
|
|
366
|
+
margin-inline: auto;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/* Video is the one medium that brings its own black, so it takes the full
|
|
370
|
+
plate width over the dim step rather than the container step under it. */
|
|
371
|
+
.article-content figure video {
|
|
372
|
+
display: block;
|
|
373
|
+
width: 100%;
|
|
374
|
+
background: var(--color-surface-dim);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* Landscape YouTube / local video: always the full reading measure.
|
|
378
|
+
Shorts stay narrow (45%); a paired short block uses the full measure with
|
|
379
|
+
each frame at 45%. */
|
|
380
|
+
.article-content figure.article-youtube-landscape,
|
|
381
|
+
.article-content figure.article-video-landscape {
|
|
382
|
+
max-width: 100%;
|
|
383
|
+
width: 100%;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.article-content figure.article-youtube-short-wrap {
|
|
387
|
+
max-width: 45%;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.article-content figure.article-youtube-short-wrap--pair {
|
|
391
|
+
max-width: 100%;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.article-content .article-youtube-short iframe {
|
|
395
|
+
display: block;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/* The Plotly iframe paints its own themed background and is already inside the
|
|
399
|
+
plate; a second fill here would box a box. */
|
|
400
|
+
.article-content .plotly-embed-frame {
|
|
401
|
+
background: transparent;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* ── Figure lightbox (docs/features/figure-lightbox.md) ── */
|
|
405
|
+
img.figure-lightbox-trigger {
|
|
406
|
+
cursor: zoom-in;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
img.figure-lightbox-trigger:focus-visible {
|
|
410
|
+
outline: 2px solid var(--color-primary);
|
|
411
|
+
outline-offset: 3px;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
html.figure-lightbox-open,
|
|
415
|
+
body.figure-lightbox-open {
|
|
416
|
+
overflow: hidden;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.figure-lightbox {
|
|
420
|
+
position: fixed;
|
|
421
|
+
inset: 0;
|
|
422
|
+
z-index: 80;
|
|
423
|
+
display: flex;
|
|
424
|
+
align-items: center;
|
|
425
|
+
justify-content: center;
|
|
426
|
+
padding: 1.25rem;
|
|
427
|
+
box-sizing: border-box;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.figure-lightbox[hidden] {
|
|
431
|
+
display: none;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.figure-lightbox-backdrop {
|
|
435
|
+
position: absolute;
|
|
436
|
+
inset: 0;
|
|
437
|
+
/* Near-opaque black so the article beNewsreader never reads as “another page”. */
|
|
438
|
+
background: rgba(0, 0, 0, 0.85);
|
|
439
|
+
cursor: zoom-out;
|
|
440
|
+
animation: figure-lightbox-fade-in var(--motion-duration-fast) var(--motion-ease-out);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.figure-lightbox-panel {
|
|
444
|
+
position: relative;
|
|
445
|
+
z-index: 1;
|
|
446
|
+
display: flex;
|
|
447
|
+
flex-direction: column;
|
|
448
|
+
align-items: center;
|
|
449
|
+
max-width: min(92vw, 72rem);
|
|
450
|
+
max-height: 92vh;
|
|
451
|
+
animation: figure-lightbox-scale-in var(--motion-duration-fast) var(--motion-ease-out);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.figure-lightbox-img {
|
|
455
|
+
display: block;
|
|
456
|
+
max-width: 92vw;
|
|
457
|
+
max-height: 85vh;
|
|
458
|
+
width: auto;
|
|
459
|
+
height: auto;
|
|
460
|
+
object-fit: contain;
|
|
461
|
+
background: var(--color-surface);
|
|
462
|
+
border: 1px solid color-mix(in srgb, var(--color-outline-variant) 40%, transparent);
|
|
463
|
+
border-radius: 0;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/* Moves with the caption under the plate — the lightbox shows the same
|
|
467
|
+
sentence about the same figure. */
|
|
468
|
+
.figure-lightbox-caption {
|
|
469
|
+
margin: 0.75rem 0 0;
|
|
470
|
+
max-width: min(40rem, 90vw);
|
|
471
|
+
font-family: var(--font-body);
|
|
472
|
+
font-size: var(--type-label-lg);
|
|
473
|
+
line-height: 1.45;
|
|
474
|
+
text-align: center;
|
|
475
|
+
/* Light caption on the black scrim (readable in light and dark themes). */
|
|
476
|
+
color: rgba(255, 255, 255, 0.92);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.figure-lightbox-close {
|
|
480
|
+
position: fixed;
|
|
481
|
+
top: 1rem;
|
|
482
|
+
right: 1rem;
|
|
483
|
+
z-index: 2;
|
|
484
|
+
display: inline-flex;
|
|
485
|
+
align-items: center;
|
|
486
|
+
justify-content: center;
|
|
487
|
+
width: 2.75rem;
|
|
488
|
+
height: 2.75rem;
|
|
489
|
+
padding: 0;
|
|
490
|
+
border: 1px solid color-mix(in srgb, var(--color-outline-variant) 50%, transparent);
|
|
491
|
+
border-radius: 0;
|
|
492
|
+
background: var(--color-surface);
|
|
493
|
+
color: var(--color-on-surface);
|
|
494
|
+
cursor: pointer;
|
|
495
|
+
transition: color var(--motion-duration-fast) var(--motion-ease-default), background-color var(--motion-duration-fast) var(--motion-ease-default);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.figure-lightbox-close:hover,
|
|
499
|
+
.figure-lightbox-close:focus-visible {
|
|
500
|
+
color: var(--color-primary);
|
|
501
|
+
background: var(--color-surface-container-low);
|
|
502
|
+
outline: none;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.figure-lightbox-close:focus-visible {
|
|
506
|
+
outline: 2px solid var(--color-primary);
|
|
507
|
+
outline-offset: 2px;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.figure-lightbox-close .material-symbols-outlined {
|
|
511
|
+
font-size: var(--type-section);
|
|
512
|
+
line-height: 1;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
@keyframes figure-lightbox-fade-in {
|
|
516
|
+
from {
|
|
517
|
+
opacity: 0;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
to {
|
|
521
|
+
opacity: 1;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
@keyframes figure-lightbox-scale-in {
|
|
526
|
+
from {
|
|
527
|
+
opacity: 0;
|
|
528
|
+
transform: scale(0.97);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
to {
|
|
532
|
+
opacity: 1;
|
|
533
|
+
transform: scale(1);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
@media (prefers-reduced-motion: reduce) {
|
|
538
|
+
|
|
539
|
+
.figure-lightbox-backdrop,
|
|
540
|
+
.figure-lightbox-panel {
|
|
541
|
+
animation: none;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/* Mermaid takes the container step, not the panel step. `mermaidConfig()`
|
|
546
|
+
paints cluster backgrounds with --color-panel-contrast and node fills with
|
|
547
|
+
--color-surface-card, so a panel plate would swallow every subgraph box the
|
|
548
|
+
diagram draws. The container step sits clear of both in each theme — do not
|
|
549
|
+
"simplify" this to panel-contrast. */
|
|
550
|
+
.article-content .mermaid {
|
|
551
|
+
margin: var(--space-2xl) 0;
|
|
552
|
+
padding: var(--space-xl) var(--space-lg);
|
|
553
|
+
background: var(--color-surface-container);
|
|
554
|
+
text-align: center;
|
|
555
|
+
overflow-x: auto;
|
|
556
|
+
-webkit-overflow-scrolling: touch;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.article-content .mermaid svg {
|
|
560
|
+
display: block;
|
|
561
|
+
margin-left: auto;
|
|
562
|
+
margin-right: auto;
|
|
563
|
+
max-width: none;
|
|
564
|
+
width: auto;
|
|
565
|
+
height: auto;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/* Captions are sentences. The include centres the whole figure, and a centred
|
|
569
|
+
paragraph of provenance is harder to read than a left-set one; the rule on
|
|
570
|
+
the element itself beats the `text-center` utility the runtime emits. The
|
|
571
|
+
hairline separates the caption from the picture now that both share a
|
|
572
|
+
plate.
|
|
573
|
+
|
|
574
|
+
Scoped to `figure figcaption`, not to any figcaption, so the treatment and
|
|
575
|
+
the plate always travel together. That used to exclude the YouTube caption,
|
|
576
|
+
because youtube.html emitted a bare `<figcaption>` inside a `<div>` with no
|
|
577
|
+
`<figure>` around it and the rule would have drawn a hairline under an
|
|
578
|
+
unplated iframe. The include emits a `<figure>` now, so the embed takes the
|
|
579
|
+
plate and its caption takes this treatment with it. */
|
|
580
|
+
/* Captions match the detail standfirst: body − 1pt, upright, not fine print.
|
|
581
|
+
Ink is a soft step toward variant so legends sit quieter than body copy. */
|
|
582
|
+
.article-content figure figcaption,
|
|
583
|
+
.article-content figcaption {
|
|
584
|
+
text-align: left;
|
|
585
|
+
font-family: var(--font-body);
|
|
586
|
+
font-size: var(--type-subtitle);
|
|
587
|
+
font-style: normal;
|
|
588
|
+
font-weight: var(--weight-body);
|
|
589
|
+
line-height: 1.55;
|
|
590
|
+
letter-spacing: normal;
|
|
591
|
+
color: var(--color-legend);
|
|
592
|
+
margin-top: var(--space-sm);
|
|
593
|
+
padding-top: var(--space-sm);
|
|
594
|
+
border-top: 1px solid var(--border-gentle-faint);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.article-content figcaption p {
|
|
598
|
+
display: inline;
|
|
599
|
+
margin: 0;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
.article-content blockquote {
|
|
603
|
+
background: var(--color-surface-container-low);
|
|
604
|
+
padding: 1rem 1.5rem;
|
|
605
|
+
margin: 1.5em 0;
|
|
606
|
+
color: var(--color-on-surface);
|
|
607
|
+
font-size: var(--type-prose);
|
|
608
|
+
font-style: normal;
|
|
609
|
+
font-weight: var(--weight-body);
|
|
610
|
+
border-left: none;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.article-content blockquote a {
|
|
614
|
+
color: var(--color-link);
|
|
615
|
+
text-decoration: underline;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.article-content blockquote p {
|
|
619
|
+
margin-bottom: 0.5rem;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
.article-content blockquote p:last-child {
|
|
623
|
+
margin-bottom: 0;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/* ── Tables ── */
|
|
627
|
+
.article-content table {
|
|
628
|
+
width: 100%;
|
|
629
|
+
border-collapse: collapse;
|
|
630
|
+
margin-bottom: 1.5rem;
|
|
631
|
+
font-size: var(--type-dek);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.article-content th,
|
|
635
|
+
.article-content td {
|
|
636
|
+
border: 1px solid var(--border-gentle-subtle);
|
|
637
|
+
padding: 0.5rem 0.75rem;
|
|
638
|
+
text-align: left;
|
|
639
|
+
vertical-align: top;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
.article-content thead th {
|
|
643
|
+
background: var(--color-surface-container-low);
|
|
644
|
+
font-family: var(--font-body);
|
|
645
|
+
font-weight: var(--weight-emphasis);
|
|
646
|
+
color: var(--color-legend);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
.article-content tbody tr:nth-child(even) {
|
|
650
|
+
background: var(--color-surface-container-low);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.article-content tbody tr:nth-child(even) td {
|
|
654
|
+
color: var(--color-on-surface);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.article-content hr {
|
|
658
|
+
border: 0;
|
|
659
|
+
border-top: 1px solid var(--border-gentle-divider);
|
|
660
|
+
margin: 2rem 0;
|
|
661
|
+
clear: both;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/* ── Article sidebar heading (title + description live in the left column) ── */
|
|
665
|
+
.article-sidebar-heading {
|
|
666
|
+
border-bottom: 1px solid var(--border-gentle-divider);
|
|
667
|
+
padding-bottom: 1.5rem;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.article-sidebar-heading .text-title {
|
|
671
|
+
margin: 0 0 0.875rem;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
@media (min-width: 768px) {
|
|
675
|
+
.article-sidebar-heading .text-title {
|
|
676
|
+
margin-bottom: 1rem;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.article-sidebar-description {
|
|
681
|
+
font-family: var(--font-body);
|
|
682
|
+
font-size: var(--type-body);
|
|
683
|
+
font-weight: var(--weight-body);
|
|
684
|
+
line-height: var(--leading-body);
|
|
685
|
+
letter-spacing: normal;
|
|
686
|
+
color: var(--color-legend);
|
|
687
|
+
margin: 0;
|
|
688
|
+
text-transform: none;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/* Speaker cue in a transcript — title-cased name, accent colour, no shouty
|
|
692
|
+
caps and no chip chrome. Colour alone marks who is speaking; the dialogue
|
|
693
|
+
stays the main measure. */
|
|
694
|
+
.transcription-speaker {
|
|
695
|
+
flex: 0 0 20%;
|
|
696
|
+
max-width: 20%;
|
|
697
|
+
font-family: var(--font-body);
|
|
698
|
+
font-size: var(--type-body);
|
|
699
|
+
font-weight: var(--weight-emphasis);
|
|
700
|
+
line-height: var(--leading-body);
|
|
701
|
+
letter-spacing: normal;
|
|
702
|
+
text-transform: none;
|
|
703
|
+
white-space: nowrap;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/* Sequential page navigation — same typeface as article descriptions.
|
|
707
|
+
This used to cover .pagination-nav-btn too, which loads after base.css at
|
|
708
|
+
the same specificity and so silently overrode the listing pagination
|
|
709
|
+
component. Article prev/next and listing pagination are different controls
|
|
710
|
+
on different pages; the listing one is defined once in base.css. */
|
|
711
|
+
.text-page-nav a {
|
|
712
|
+
font-family: var(--font-subtitle);
|
|
713
|
+
font-weight: 400;
|
|
714
|
+
letter-spacing: normal;
|
|
715
|
+
text-transform: none;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/* Post thumbnail in sidebar — softer than card thumbnails, same 16:9 family */
|
|
719
|
+
.article-sidebar-thumbnail {
|
|
720
|
+
margin: 1rem 0 0;
|
|
721
|
+
aspect-ratio: 16 / 9;
|
|
722
|
+
overflow: hidden;
|
|
723
|
+
position: relative;
|
|
724
|
+
background: var(--sidebar-thumbnail-background);
|
|
725
|
+
width: min(100%, var(--sidebar-thumbnail-width));
|
|
726
|
+
max-width: var(--sidebar-thumbnail-width);
|
|
727
|
+
flex-shrink: 0;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.article-sidebar-thumbnail img {
|
|
731
|
+
width: 100%;
|
|
732
|
+
height: 100%;
|
|
733
|
+
object-fit: cover;
|
|
734
|
+
display: block;
|
|
735
|
+
opacity: var(--sidebar-thumbnail-image-opacity);
|
|
736
|
+
filter: saturate(var(--sidebar-thumbnail-filter-saturate)) contrast(var(--sidebar-thumbnail-filter-contrast)) brightness(var(--sidebar-thumbnail-filter-brightness)) sepia(var(--sidebar-thumbnail-filter-sepia));
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
.article-sidebar-thumbnail::after {
|
|
740
|
+
content: '';
|
|
741
|
+
position: absolute;
|
|
742
|
+
inset: 0;
|
|
743
|
+
background: linear-gradient(180deg,
|
|
744
|
+
var(--sidebar-thumbnail-overlay-start) 0%,
|
|
745
|
+
var(--sidebar-thumbnail-overlay-end) 100%);
|
|
746
|
+
pointer-events: none;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/* ── Article meta ── */
|
|
750
|
+
.article-meta {
|
|
751
|
+
color: var(--color-on-surface-variant);
|
|
752
|
+
font-size: var(--type-label-lg);
|
|
753
|
+
margin-bottom: 2rem;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
@media (max-width: 1023px) {
|
|
757
|
+
.text-detail-sidebar .article-meta {
|
|
758
|
+
margin-bottom: 0;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/* ── Article table of contents ── */
|
|
763
|
+
.article-toc--sidebar {
|
|
764
|
+
display: none;
|
|
765
|
+
margin-top: 1.5rem;
|
|
766
|
+
padding-top: 1.5rem;
|
|
767
|
+
border-top: 1px solid var(--border-gentle-divider);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
.article-toc-title,
|
|
771
|
+
.article-toc-summary {
|
|
772
|
+
font-family: var(--font-label);
|
|
773
|
+
font-size: var(--type-label-lg);
|
|
774
|
+
letter-spacing: 0.08em;
|
|
775
|
+
text-transform: uppercase;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
.article-toc-title {
|
|
779
|
+
font-weight: 600;
|
|
780
|
+
color: var(--color-on-surface);
|
|
781
|
+
margin: 0 0 0.75rem;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
.article-toc--sidebar .article-toc-list {
|
|
785
|
+
padding-left: 0.75rem;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.article-toc-list {
|
|
789
|
+
list-style: none;
|
|
790
|
+
margin: 0;
|
|
791
|
+
padding: 0;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
.article-toc-item+.article-toc-item {
|
|
795
|
+
margin-top: 0.375rem;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
.article-toc-link {
|
|
799
|
+
display: flex;
|
|
800
|
+
align-items: baseline;
|
|
801
|
+
gap: var(--space-sm);
|
|
802
|
+
font-family: var(--font-body);
|
|
803
|
+
font-size: var(--type-body);
|
|
804
|
+
line-height: 1.45;
|
|
805
|
+
color: var(--color-on-surface-variant);
|
|
806
|
+
text-decoration: none;
|
|
807
|
+
text-transform: none;
|
|
808
|
+
letter-spacing: normal;
|
|
809
|
+
font-weight: 400;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
.article-toc-num {
|
|
813
|
+
flex: 0 0 auto;
|
|
814
|
+
min-width: 1.75rem;
|
|
815
|
+
font-family: var(--font-mono);
|
|
816
|
+
font-size: 0.9em;
|
|
817
|
+
letter-spacing: 0.04em;
|
|
818
|
+
color: var(--color-outline);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
.article-toc-item.is-active>.article-toc-link,
|
|
822
|
+
.article-toc-item.is-active .article-toc-num {
|
|
823
|
+
color: var(--color-primary);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
.article-toc-item.is-active>.article-toc-link {
|
|
827
|
+
text-decoration: underline;
|
|
828
|
+
text-decoration-thickness: 2px;
|
|
829
|
+
text-underline-offset: 0.22em;
|
|
830
|
+
text-decoration-color: color-mix(in srgb, var(--color-primary) 55%, transparent);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
.article-toc-link:hover {
|
|
834
|
+
color: var(--color-link-hover);
|
|
835
|
+
text-decoration: underline;
|
|
836
|
+
text-underline-offset: 2px;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
.article-toc-item--h3 .article-toc-link {
|
|
840
|
+
padding-left: 1.75rem;
|
|
841
|
+
font-size: var(--type-subtitle);
|
|
842
|
+
color: var(--color-on-surface-variant);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
.article-toc--disclosure {
|
|
846
|
+
margin-bottom: 1.5rem;
|
|
847
|
+
border: 1px solid var(--border-gentle-divider);
|
|
848
|
+
background: var(--color-surface-container-low);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
@media (max-width: 1023px) {
|
|
852
|
+
.text-detail-sidebar .article-toc--disclosure {
|
|
853
|
+
margin-top: 1.5em;
|
|
854
|
+
margin-bottom: 0;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
.text-page-main[data-dynamic-article-detail],
|
|
858
|
+
.text-page-main[data-dynamic-post-detail],
|
|
859
|
+
.text-page-main[data-dynamic-portfolio-detail] {
|
|
860
|
+
margin-top: 2.5rem;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
.article-toc-summary {
|
|
865
|
+
display: flex;
|
|
866
|
+
align-items: center;
|
|
867
|
+
justify-content: space-between;
|
|
868
|
+
gap: 0.75rem;
|
|
869
|
+
padding: 0.75rem 1rem;
|
|
870
|
+
cursor: pointer;
|
|
871
|
+
list-style: none;
|
|
872
|
+
font-weight: 600;
|
|
873
|
+
color: var(--color-on-surface-variant);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
.article-toc-summary::-webkit-details-marker {
|
|
877
|
+
display: none;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
.article-toc-summary-icon {
|
|
881
|
+
font-size: var(--type-prose);
|
|
882
|
+
color: var(--color-secondary);
|
|
883
|
+
transition: transform var(--motion-duration-fast) var(--motion-ease-default);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
.article-toc--disclosure[open] .article-toc-summary-icon {
|
|
887
|
+
transform: rotate(180deg);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
.article-toc-panel {
|
|
891
|
+
padding: 0 1rem 1rem;
|
|
892
|
+
border-top: 1px solid var(--border-gentle-divider);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
.article-content h2[id],
|
|
896
|
+
.article-content h3[id] {
|
|
897
|
+
scroll-margin-top: 6rem;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
@media (min-width: 1024px) {
|
|
901
|
+
.article-toc--sidebar {
|
|
902
|
+
display: block;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
.article-toc--disclosure {
|
|
906
|
+
display: none;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/* ── Internal cross-citation links (xcite) ── */
|
|
911
|
+
a.xcite-ref {
|
|
912
|
+
color: var(--color-secondary);
|
|
913
|
+
text-decoration-line: underline;
|
|
914
|
+
text-underline-offset: 2px;
|
|
915
|
+
text-decoration-thickness: 1px;
|
|
916
|
+
text-decoration-style: dotted;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
a.xcite-ref:hover {
|
|
920
|
+
color: var(--color-link-hover);
|
|
921
|
+
text-decoration-style: solid;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
.xcite-missing {
|
|
925
|
+
color: var(--color-error);
|
|
926
|
+
font-family: monospace;
|
|
927
|
+
font-size: var(--type-meta);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/* ── Inline note block (gray, similar to CV/portfolio boxes) ── */
|
|
931
|
+
.article-content .article-note {
|
|
932
|
+
background: var(--color-surface-container-low);
|
|
933
|
+
padding: 1rem 1.5rem;
|
|
934
|
+
margin: 1.5em 0;
|
|
935
|
+
font-size: var(--type-subtitle);
|
|
936
|
+
font-weight: var(--weight-body);
|
|
937
|
+
color: var(--color-on-surface);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
.article-content .article-note> :last-child {
|
|
941
|
+
margin-bottom: 0;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/* ══ Code blocks ══
|
|
945
|
+
A fence used to render as a bare grey box: no indication of what language
|
|
946
|
+
was in it, and nothing to do with it but select the text by hand. It now
|
|
947
|
+
carries the chrome an editor gives a file — a bar across the top with the
|
|
948
|
+
language on a tab and a copy control at the right edge, and the pane below.
|
|
949
|
+
|
|
950
|
+
The tab is drawn in the pane's own fill with a rule of primary across its
|
|
951
|
+
top edge, which is the one idiom every editor shares for "this is the file
|
|
952
|
+
you are looking at". It is the same square, hairline-bordered construction
|
|
953
|
+
the ledger cards use; nothing here is rounded and nothing floats.
|
|
954
|
+
|
|
955
|
+
The markup is assembled from the rendered DOM by
|
|
956
|
+
_includes/content-runtime/25-code-blocks.html, so these rules never see a
|
|
957
|
+
`pre` that has not been wrapped. */
|
|
958
|
+
:is(.article-content, .article-cite-block) .code-block {
|
|
959
|
+
margin: 1.5rem 0;
|
|
960
|
+
background: var(--color-surface-container);
|
|
961
|
+
border: 1px solid var(--border-gentle-subtle);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
:is(.article-content, .article-cite-block) .code-block-bar {
|
|
965
|
+
display: flex;
|
|
966
|
+
align-items: stretch;
|
|
967
|
+
justify-content: space-between;
|
|
968
|
+
gap: var(--space-md);
|
|
969
|
+
background: var(--color-surface-container-high);
|
|
970
|
+
border-bottom: 1px solid var(--border-gentle-subtle);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
:is(.article-content, .article-cite-block) .code-block-tab {
|
|
974
|
+
display: inline-flex;
|
|
975
|
+
align-items: center;
|
|
976
|
+
padding: 0.4rem 0.9rem;
|
|
977
|
+
background: var(--color-surface-container);
|
|
978
|
+
border-right: 1px solid var(--border-gentle-subtle);
|
|
979
|
+
/* An inset shadow rather than a border-top, so the rule does not add a
|
|
980
|
+
pixel to the tab's height and push the bar taller than the button. */
|
|
981
|
+
box-shadow: inset 0 2px 0 var(--color-primary);
|
|
982
|
+
color: var(--color-on-surface);
|
|
983
|
+
font-size: var(--type-label);
|
|
984
|
+
font-weight: 500;
|
|
985
|
+
letter-spacing: 0.12em;
|
|
986
|
+
line-height: 1;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
:is(.article-content, .article-cite-block) .code-block-copy {
|
|
990
|
+
padding: 0.4rem 0.9rem;
|
|
991
|
+
border: 0;
|
|
992
|
+
background: transparent;
|
|
993
|
+
color: var(--color-on-surface-variant);
|
|
994
|
+
font-size: var(--type-label);
|
|
995
|
+
letter-spacing: 0.12em;
|
|
996
|
+
line-height: 1;
|
|
997
|
+
cursor: pointer;
|
|
998
|
+
transition: color var(--motion-duration-fast) var(--motion-ease-default);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
:is(.article-content, .article-cite-block) .code-block-copy:hover,
|
|
1002
|
+
:is(.article-content, .article-cite-block) .code-block-copy:focus-visible {
|
|
1003
|
+
color: var(--color-primary);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/* The acknowledgement is the secondary green, which is the colour this site
|
|
1007
|
+
already uses for a state rather than for a link. */
|
|
1008
|
+
:is(.article-content, .article-cite-block) .code-block-copy.is-copied {
|
|
1009
|
+
color: var(--color-secondary);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/* The container carries the fill and the frame now, so the pane inside is
|
|
1013
|
+
only the scroll box and the padding. */
|
|
1014
|
+
:is(.article-content, .article-cite-block) .code-block pre {
|
|
1015
|
+
margin: 0;
|
|
1016
|
+
background: none;
|
|
1017
|
+
border: 0;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
/* ── Terminal captures ──
|
|
1021
|
+
An `ansi` fence is a real capture, escape codes and all, rendered by
|
|
1022
|
+
renderAnsiHtml into spans that paint from the sixteen custom properties
|
|
1023
|
+
below. The ground is stated outright instead of being taken from the
|
|
1024
|
+
surface ladder, and it does not follow the theme: ANSI colours are defined
|
|
1025
|
+
against a dark terminal and a bright yellow on the light palette's white
|
|
1026
|
+
canvas is illegible. A capture of a terminal brings its own surface with
|
|
1027
|
+
it, the way a photograph brings its own light.
|
|
1028
|
+
|
|
1029
|
+
The palette is Tango, which is what Ubuntu's terminal ships and therefore
|
|
1030
|
+
what the capture in the WSL article was actually drawn with. Bold maps to
|
|
1031
|
+
the bright half in the renderer, not here, because that is a terminal
|
|
1032
|
+
behaviour rather than a typographic one. */
|
|
1033
|
+
.article-content .code-block--ansi {
|
|
1034
|
+
--ansi-bg: #12130f;
|
|
1035
|
+
--ansi-fg: #d3d7cf;
|
|
1036
|
+
--ansi-fg-bold: #eeeeec;
|
|
1037
|
+
/* The resting colour of the copy control. `--ansi-8`, the bright black the
|
|
1038
|
+
palette would otherwise suggest, is 2.5 : 1 on this ground; this is 4.9 : 1
|
|
1039
|
+
against the bar it sits on. */
|
|
1040
|
+
--ansi-fg-dim: #9aa09a;
|
|
1041
|
+
--ansi-0: #2e3436;
|
|
1042
|
+
--ansi-1: #cc0000;
|
|
1043
|
+
--ansi-2: #4e9a06;
|
|
1044
|
+
--ansi-3: #c4a000;
|
|
1045
|
+
--ansi-4: #3465a4;
|
|
1046
|
+
--ansi-5: #75507b;
|
|
1047
|
+
--ansi-6: #06989a;
|
|
1048
|
+
--ansi-7: #d3d7cf;
|
|
1049
|
+
--ansi-8: #555753;
|
|
1050
|
+
--ansi-9: #ef2929;
|
|
1051
|
+
--ansi-10: #8ae234;
|
|
1052
|
+
--ansi-11: #fce94f;
|
|
1053
|
+
--ansi-12: #729fcf;
|
|
1054
|
+
--ansi-13: #ad7fa8;
|
|
1055
|
+
--ansi-14: #34e2e2;
|
|
1056
|
+
--ansi-15: #eeeeec;
|
|
1057
|
+
background: var(--ansi-bg);
|
|
1058
|
+
border-color: var(--ansi-0);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
.article-content .code-block--ansi .code-block-bar {
|
|
1062
|
+
background: var(--ansi-0);
|
|
1063
|
+
border-bottom-color: var(--ansi-8);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
.article-content .code-block--ansi .code-block-tab {
|
|
1067
|
+
background: var(--ansi-bg);
|
|
1068
|
+
border-right-color: var(--ansi-8);
|
|
1069
|
+
color: var(--ansi-fg);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
.article-content .code-block--ansi .code-block-copy {
|
|
1073
|
+
color: var(--ansi-fg-dim);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
.article-content .code-block--ansi .code-block-copy:hover,
|
|
1077
|
+
.article-content .code-block--ansi .code-block-copy:focus-visible {
|
|
1078
|
+
color: var(--ansi-fg);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
.article-content .code-block--ansi .code-block-copy.is-copied {
|
|
1082
|
+
color: var(--ansi-10);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/* Box drawing made of `s`, `o` and `/` only holds together on a tight grid,
|
|
1086
|
+
so the line height is closed up and the size dropped below the prose
|
|
1087
|
+
`code`. A capture is as wide as the terminal that took it — the neofetch
|
|
1088
|
+
block below runs to about 100 columns — and the reading measure is not
|
|
1089
|
+
negotiable, so past that width the pane scrolls. */
|
|
1090
|
+
.article-content .code-block--ansi pre {
|
|
1091
|
+
padding: 0.9rem 1rem;
|
|
1092
|
+
color: var(--ansi-fg);
|
|
1093
|
+
overflow-x: auto;
|
|
1094
|
+
/* Both terms sit on the `pre` rather than on the `code` inside it. `code` is
|
|
1095
|
+
inline, so the block it sits in is what establishes the line boxes: a
|
|
1096
|
+
line-height set on the `code` only sizes its own inline box, and the
|
|
1097
|
+
taller of that and the block's strut wins. With detail.css setting the
|
|
1098
|
+
`pre` to 0.85rem/1.6, the strut was 23.9px against a 15.6px inline box,
|
|
1099
|
+
and the capture rendered at two-thirds again the leading it asked for.
|
|
1100
|
+
Stating the size here as well keeps the strut and the content in step.
|
|
1101
|
+
|
|
1102
|
+
1.15 is a terminal cell. VTE — what Ubuntu's terminal is built on — takes
|
|
1103
|
+
its row height from the font's own ascent plus descent and adds nothing,
|
|
1104
|
+
which lands between 1.16 and 1.20 for the monospace faces it ships. Box
|
|
1105
|
+
drawing made of `s`, `o` and `/` needs that: at prose leading the Ubuntu
|
|
1106
|
+
logo's swirl breaks into twenty unrelated rows of punctuation. */
|
|
1107
|
+
font-size: var(--type-micro);
|
|
1108
|
+
line-height: 1.15;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
.article-content .code-block--ansi code {
|
|
1112
|
+
font-size: inherit;
|
|
1113
|
+
line-height: inherit;
|
|
1114
|
+
-webkit-font-smoothing: antialiased;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/* ── Definition cards & social quote callouts (e.g. sovereignty) ── */
|
|
1118
|
+
.article-content .sov-grid {
|
|
1119
|
+
display: grid;
|
|
1120
|
+
grid-template-columns: 1fr;
|
|
1121
|
+
gap: 1.0rem;
|
|
1122
|
+
margin: 1.25rem 0 1.75rem;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
@media (min-width: 720px) {
|
|
1126
|
+
.article-content .sov-grid {
|
|
1127
|
+
grid-template-columns: 1fr 1fr;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
.article-content .sov-card {
|
|
1132
|
+
border: 1px solid color-mix(in srgb, var(--color-outline, #888) 45%, transparent);
|
|
1133
|
+
border-left: 3px solid var(--sov-accent, var(--color-primary, #5b6cff));
|
|
1134
|
+
border-radius: 0;
|
|
1135
|
+
background: color-mix(in srgb, var(--color-surface-card, transparent) 92%, transparent);
|
|
1136
|
+
padding: 0.7rem 0.85rem 0.8rem;
|
|
1137
|
+
display: flex;
|
|
1138
|
+
flex-direction: column;
|
|
1139
|
+
gap: 0.5rem;
|
|
1140
|
+
min-width: 0;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
.article-content .sov-card p {
|
|
1144
|
+
margin: 0;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
.article-content .sov-card__head {
|
|
1148
|
+
display: flex;
|
|
1149
|
+
align-items: center;
|
|
1150
|
+
gap: 0.55rem;
|
|
1151
|
+
flex-wrap: wrap;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
.article-content .sov-card__flag {
|
|
1155
|
+
display: inline-flex;
|
|
1156
|
+
align-items: center;
|
|
1157
|
+
justify-content: center;
|
|
1158
|
+
height: 1.65rem;
|
|
1159
|
+
padding: 0 0.4rem;
|
|
1160
|
+
border: 1.5px solid color-mix(in srgb, currentColor 40%, var(--border-ink, #444));
|
|
1161
|
+
border-radius: 0;
|
|
1162
|
+
line-height: 1;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
.article-content .sov-card__flag .fi {
|
|
1166
|
+
font-size: 1.05rem;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
.article-content .sov-card__meta {
|
|
1170
|
+
font-size: 0.72rem;
|
|
1171
|
+
letter-spacing: 0.04em;
|
|
1172
|
+
text-transform: uppercase;
|
|
1173
|
+
opacity: 0.8;
|
|
1174
|
+
font-weight: 600;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
.article-content .sov-card__lemma {
|
|
1178
|
+
font-style: italic;
|
|
1179
|
+
font-weight: 400;
|
|
1180
|
+
margin: 0;
|
|
1181
|
+
font-size: 1.05rem;
|
|
1182
|
+
color: var(--color-on-surface);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
.article-content .sov-card__def {
|
|
1186
|
+
margin: 0;
|
|
1187
|
+
padding: 0.5rem 0.7rem;
|
|
1188
|
+
border-left: 2px solid color-mix(in srgb, var(--sov-accent, #5b6cff) 55%, transparent);
|
|
1189
|
+
background: color-mix(in srgb, var(--sov-accent, #5b6cff) 8%, transparent);
|
|
1190
|
+
font-size: 0.88rem;
|
|
1191
|
+
line-height: 1.68;
|
|
1192
|
+
color: var(--color-on-surface);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
.article-content .sov-card__def p {
|
|
1196
|
+
margin: 0;
|
|
1197
|
+
font-size: inherit;
|
|
1198
|
+
line-height: inherit;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
.article-content .sov-card blockquote {
|
|
1202
|
+
margin: 0;
|
|
1203
|
+
border: none;
|
|
1204
|
+
border-left: 2px solid color-mix(in srgb, var(--sov-accent, #5b6cff) 55%, transparent);
|
|
1205
|
+
border-radius: 0;
|
|
1206
|
+
padding: 0.5rem 0.7rem;
|
|
1207
|
+
background: color-mix(in srgb, var(--sov-accent, #5b6cff) 8%, transparent);
|
|
1208
|
+
font-size: 0.88rem;
|
|
1209
|
+
line-height: 1.68;
|
|
1210
|
+
color: var(--color-on-surface);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
.article-content .sov-card blockquote p {
|
|
1214
|
+
margin: 0;
|
|
1215
|
+
font-size: inherit;
|
|
1216
|
+
line-height: inherit;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
.article-content .sov-card__note {
|
|
1220
|
+
margin: 0;
|
|
1221
|
+
font-size: 0.82rem;
|
|
1222
|
+
line-height: 1.55;
|
|
1223
|
+
opacity: 0.9;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
.article-content .sov-card__cite {
|
|
1227
|
+
margin: 0;
|
|
1228
|
+
font-size: 0.76rem;
|
|
1229
|
+
line-height: 1.45;
|
|
1230
|
+
opacity: 0.75;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
.article-content .sov-card__cite a {
|
|
1234
|
+
color: inherit;
|
|
1235
|
+
text-decoration: underline;
|
|
1236
|
+
text-underline-offset: 2px;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
.sov-card--us {
|
|
1240
|
+
--sov-accent: #003494;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
.sov-card--gb {
|
|
1244
|
+
--sov-accent: #797300;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
.sov-card--br {
|
|
1248
|
+
--sov-accent: #118826;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
.sov-card--fr {
|
|
1252
|
+
--sov-accent: #9f020a;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
.article-content :is(.sov-tweet, .article-tweet) {
|
|
1256
|
+
max-width: 34rem;
|
|
1257
|
+
margin: 1.25rem 0 1.75rem;
|
|
1258
|
+
padding: 0.95rem 1.15rem 1rem;
|
|
1259
|
+
border: 1px solid color-mix(in srgb, var(--color-outline, #888) 45%, transparent);
|
|
1260
|
+
border-radius: 0;
|
|
1261
|
+
background: color-mix(in srgb, var(--color-surface-card, transparent) 92%, transparent);
|
|
1262
|
+
font-size: 0.95rem;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
.article-content :is(.sov-tweet, .article-tweet) p {
|
|
1266
|
+
margin: 0;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
.article-content :is(.sov-tweet__head, .article-tweet__head) {
|
|
1270
|
+
display: flex;
|
|
1271
|
+
align-items: center;
|
|
1272
|
+
gap: 0.55rem;
|
|
1273
|
+
margin-bottom: 0.85rem;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
.article-content :is(.sov-tweet__avatar, .article-tweet__avatar) {
|
|
1277
|
+
width: 2.2rem;
|
|
1278
|
+
height: 2.2rem;
|
|
1279
|
+
border-radius: 0%;
|
|
1280
|
+
background: color-mix(in srgb, var(--color-primary, #7b8896) 25%, transparent);
|
|
1281
|
+
display: inline-flex;
|
|
1282
|
+
align-items: center;
|
|
1283
|
+
justify-content: center;
|
|
1284
|
+
font-weight: 300;
|
|
1285
|
+
font-size: 1.25rem;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
.article-content :is(.sov-tweet__id, .article-tweet__id) {
|
|
1289
|
+
display: flex;
|
|
1290
|
+
flex-direction: column;
|
|
1291
|
+
line-height: 1.15;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
.article-content :is(.sov-tweet__name, .article-tweet__name) {
|
|
1295
|
+
font-weight: 700;
|
|
1296
|
+
font-size: 0.95rem;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
.article-content :is(.sov-tweet__handle, .article-tweet__handle) {
|
|
1300
|
+
font-size: 0.82rem;
|
|
1301
|
+
opacity: 0.75;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
.article-content :is(.sov-tweet__body, .article-tweet__body) {
|
|
1305
|
+
margin: 0 0 0.5rem !important;
|
|
1306
|
+
font-size: 0.98rem;
|
|
1307
|
+
line-height: 1.65;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
.article-content :is(.sov-tweet__body, .article-tweet__body) em {
|
|
1311
|
+
font-style: italic;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
.article-content :is(.sov-tweet__cite, .article-tweet__cite) {
|
|
1315
|
+
margin: 0 0 0.55rem !important;
|
|
1316
|
+
font-size: 0.82rem;
|
|
1317
|
+
line-height: 1.5;
|
|
1318
|
+
opacity: 0.8;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
.article-content :is(.sov-tweet__cite, .article-tweet__cite) a {
|
|
1322
|
+
text-decoration: none;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
.article-content :is(.sov-tweet__note, .article-tweet__note) {
|
|
1326
|
+
margin: 0 !important;
|
|
1327
|
+
font-size: 0.88rem;
|
|
1328
|
+
line-height: 1.6;
|
|
1329
|
+
opacity: 0.9;
|
|
1330
|
+
}
|