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
data/assets/css/base.css
ADDED
|
@@ -0,0 +1,1432 @@
|
|
|
1
|
+
/* ── Material Symbols ── */
|
|
2
|
+
.material-symbols-outlined {
|
|
3
|
+
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
vertical-align: middle;
|
|
8
|
+
line-height: 1;
|
|
9
|
+
text-transform: none;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* Cubist triad — ~+25% over the 1.25rem theme-toggle box (rounded to 1.5rem). */
|
|
13
|
+
.site-logo {
|
|
14
|
+
display: block;
|
|
15
|
+
width: 1.5rem;
|
|
16
|
+
height: 1.5rem;
|
|
17
|
+
overflow: visible;
|
|
18
|
+
flex-shrink: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#theme-toggle-icon.material-symbols-outlined {
|
|
22
|
+
font-size: var(--type-card);
|
|
23
|
+
width: 1.25rem;
|
|
24
|
+
height: 1.25rem;
|
|
25
|
+
line-height: 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* Light page → pastel triad; dark page → production triad. Each theme paints
|
|
29
|
+
the mark in the *other* theme's primary/secondary/tertiary, which is why
|
|
30
|
+
these are literals: a palette only ever exposes its own three as custom
|
|
31
|
+
properties, so there is no token here to reach for. Tokenising them needs
|
|
32
|
+
inverse_secondary / inverse_tertiary in `_data/themes/*.yml` (and
|
|
33
|
+
inverse_primary corrected to the triad value) wired through
|
|
34
|
+
`_includes/theme/theme-vars-block.liquid`. */
|
|
35
|
+
:root .site-logo-primary,
|
|
36
|
+
[data-theme="light"] .site-logo-primary {
|
|
37
|
+
fill: #a4c8da;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
:root .site-logo-secondary,
|
|
41
|
+
[data-theme="light"] .site-logo-secondary {
|
|
42
|
+
fill: #95c8a6;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
:root .site-logo-tertiary,
|
|
46
|
+
[data-theme="light"] .site-logo-tertiary {
|
|
47
|
+
fill: #ddd8a8;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[data-theme="dark"] .site-logo-primary {
|
|
51
|
+
fill: #1c5a7a;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
[data-theme="dark"] .site-logo-secondary {
|
|
55
|
+
fill: #287445;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
[data-theme="dark"] .site-logo-tertiary {
|
|
59
|
+
fill: #746f30;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Same contract as Studio `.studio-mark-frame`: hairline in on-surface-variant
|
|
63
|
+
(light page → dark edge, dark page → light edge). Public site token is
|
|
64
|
+
`--color-on-surface-variant` (Studio uses `--on-surface-variant`). */
|
|
65
|
+
.site-logo-frame {
|
|
66
|
+
fill: none;
|
|
67
|
+
stroke: var(--color-on-surface-variant);
|
|
68
|
+
stroke-width: 1;
|
|
69
|
+
stroke-linejoin: miter;
|
|
70
|
+
vector-effect: non-scaling-stroke;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* ── Base typography ──
|
|
74
|
+
Scale maps Stitch Borders tokens (§19.4) onto rem at root 110%:
|
|
75
|
+
display-hero → --type-display · headline-lg → --type-page ·
|
|
76
|
+
headline-md → --type-section · headline-sm → --type-card ·
|
|
77
|
+
body-lg → --type-prose · body-md → --type-body · body-sm → --type-meta ·
|
|
78
|
+
label-md → --type-label · label-lg → --type-label-lg.
|
|
79
|
+
Type tokens are literal rem at html { font-size: 110% } (1rem = 17.6px).
|
|
80
|
+
They already include the old +2pt / +1pt / +1.3pt steps. Prose is
|
|
81
|
+
1.2rem below 1024px and 1.25rem from there, plus `--prose-size-offset`
|
|
82
|
+
(−4pt … +4pt). Selectors consume tokens directly. */
|
|
83
|
+
html {
|
|
84
|
+
font-size: 110%;
|
|
85
|
+
|
|
86
|
+
/* Display — Cinzel Decorative 400 */
|
|
87
|
+
--type-display: 2.9015rem;
|
|
88
|
+
--type-display-lg: 3.6515rem;
|
|
89
|
+
--type-page: 2.4015rem;
|
|
90
|
+
--type-page-lg: 2.9015rem;
|
|
91
|
+
--type-section: 1.9015rem;
|
|
92
|
+
--type-section-lg: 2.1515rem;
|
|
93
|
+
|
|
94
|
+
/* Content titles — Newsreader Regular 400 */
|
|
95
|
+
--type-title: 2.0015rem;
|
|
96
|
+
--type-title-lg: 2.4015rem;
|
|
97
|
+
--type-card: 1.4015rem;
|
|
98
|
+
--type-card-lg: 1.5515rem;
|
|
99
|
+
|
|
100
|
+
/* Reading + UI — Newsreader Light 300.
|
|
101
|
+
Default reading size is 1.2rem below 1024px and 1.25rem from there.
|
|
102
|
+
`--prose-size-offset` is the detail-page step (−4pt through +4pt); the
|
|
103
|
+
shell sets it from the reader's saved choice. The desktop step is in the
|
|
104
|
+
media query below. `--drop-cap-lines` is the fallback; the shell
|
|
105
|
+
overrides it from `_config.yml` `drop_cap_lines`. */
|
|
106
|
+
--prose-size-offset: 0pt;
|
|
107
|
+
--type-prose: calc(1.2rem + var(--prose-size-offset));
|
|
108
|
+
--drop-cap-lines: 2;
|
|
109
|
+
--type-body: 1.0758rem;
|
|
110
|
+
/* Card/row blurbs, standfirsts, project/source copy — same as body. */
|
|
111
|
+
--type-subtitle: var(--type-body);
|
|
112
|
+
--type-dek: 0.9375rem;
|
|
113
|
+
--type-meta: 0.8125rem;
|
|
114
|
+
|
|
115
|
+
/* Chrome — quiet relative to body (tuned for Marcellus SC cap-height to match icons) */
|
|
116
|
+
--type-label: 0.8485rem;
|
|
117
|
+
--type-label-lg: 0.9735rem;
|
|
118
|
+
--type-nav: 1.036rem;
|
|
119
|
+
--type-cta: 0.911rem;
|
|
120
|
+
--type-mono: 0.8504rem;
|
|
121
|
+
--type-micro: 0.75rem;
|
|
122
|
+
|
|
123
|
+
/* Soft apparatus ink — legends, card excerpts, muted descriptions. */
|
|
124
|
+
--color-legend: color-mix(in srgb, var(--color-on-surface) 58%, var(--color-on-surface-variant));
|
|
125
|
+
|
|
126
|
+
--font-size-body: var(--type-body);
|
|
127
|
+
--leading-body: 1.5;
|
|
128
|
+
--leading-prose: 1.6;
|
|
129
|
+
|
|
130
|
+
--weight-display: 400;
|
|
131
|
+
--weight-title: 400;
|
|
132
|
+
--weight-body: 300;
|
|
133
|
+
--weight-label: 400;
|
|
134
|
+
--weight-emphasis: 500;
|
|
135
|
+
|
|
136
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-default), color var(--motion-duration-fast) var(--motion-ease-default);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@media (min-width: 1024px) {
|
|
140
|
+
html {
|
|
141
|
+
--type-prose: calc(1.25rem + var(--prose-size-offset));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* `html body`, not `body`: Tailwind's Preflight ships `body { line-height:
|
|
146
|
+
inherit }` from the CDN <style> that loads after this file, so a bare `body`
|
|
147
|
+
selector here loses the tie on source order and the leading silently becomes
|
|
148
|
+
Preflight's. That is how the old 1.65 in this rule came to be inert. */
|
|
149
|
+
html body {
|
|
150
|
+
font-family: var(--font-body);
|
|
151
|
+
font-size: var(--font-size-body);
|
|
152
|
+
font-weight: var(--weight-body);
|
|
153
|
+
background-color: var(--color-background);
|
|
154
|
+
/* Quiet square grid on both palettes; only --color-background changes. */
|
|
155
|
+
background-image:
|
|
156
|
+
linear-gradient(color-mix(in srgb, var(--color-outline) 4%, transparent) 1px, transparent 1px),
|
|
157
|
+
linear-gradient(90deg, color-mix(in srgb, var(--color-outline) 4%, transparent) 1px, transparent 1px);
|
|
158
|
+
background-size: 1.25rem 1.25rem;
|
|
159
|
+
background-attachment: fixed;
|
|
160
|
+
color: var(--color-on-surface);
|
|
161
|
+
line-height: var(--leading-body);
|
|
162
|
+
letter-spacing: 0.01em;
|
|
163
|
+
-webkit-font-smoothing: antialiased;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* Display face only when opted in — do not paint every heading Cinzel
|
|
167
|
+
(article / card titles use --font-content-title). */
|
|
168
|
+
.font-title {
|
|
169
|
+
font-family: var(--font-title);
|
|
170
|
+
font-synthesis: none;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* ── Utility class used in various places ── */
|
|
174
|
+
.marginalia {
|
|
175
|
+
font-family: var(--font-label);
|
|
176
|
+
font-size: var(--type-nav);
|
|
177
|
+
font-weight: var(--weight-label);
|
|
178
|
+
letter-spacing: 0.05em;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* ── Display title scale (home hero, 404) ── */
|
|
182
|
+
.page-title {
|
|
183
|
+
font-family: var(--font-title);
|
|
184
|
+
font-size: var(--type-display);
|
|
185
|
+
font-weight: var(--weight-display);
|
|
186
|
+
line-height: 1.05;
|
|
187
|
+
color: var(--color-on-surface);
|
|
188
|
+
letter-spacing: normal;
|
|
189
|
+
font-synthesis: none;
|
|
190
|
+
font-optical-sizing: auto;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@media (min-width: 768px) {
|
|
194
|
+
.page-title {
|
|
195
|
+
font-size: var(--type-display-lg);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Listing page headers (CV, Articles, Portfolio, Rascunhos, …) */
|
|
200
|
+
.page-header-title {
|
|
201
|
+
font-family: var(--font-title);
|
|
202
|
+
font-size: var(--type-page);
|
|
203
|
+
font-weight: var(--weight-display);
|
|
204
|
+
line-height: 1.15;
|
|
205
|
+
color: var(--color-on-surface);
|
|
206
|
+
letter-spacing: normal;
|
|
207
|
+
margin-bottom: 0.5rem;
|
|
208
|
+
font-synthesis: none;
|
|
209
|
+
font-optical-sizing: auto;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@media (min-width: 768px) {
|
|
213
|
+
.page-header-title {
|
|
214
|
+
font-size: var(--type-page-lg);
|
|
215
|
+
line-height: 1.1;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Article / post / project detail titles — content title voice.
|
|
220
|
+
Regular 400 only — never Medium/bold, and never synthesize a heavier cut. */
|
|
221
|
+
.text-title {
|
|
222
|
+
font-family: var(--font-content-title);
|
|
223
|
+
font-size: var(--type-title);
|
|
224
|
+
font-weight: var(--weight-title);
|
|
225
|
+
font-synthesis: none;
|
|
226
|
+
line-height: 1.2;
|
|
227
|
+
color: var(--color-on-surface);
|
|
228
|
+
letter-spacing: normal;
|
|
229
|
+
font-optical-sizing: auto;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
@media (min-width: 768px) {
|
|
233
|
+
.text-title {
|
|
234
|
+
font-size: var(--type-title-lg);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.section-title {
|
|
239
|
+
font-family: var(--font-title);
|
|
240
|
+
font-size: var(--type-section);
|
|
241
|
+
font-weight: var(--weight-display);
|
|
242
|
+
line-height: 1.15;
|
|
243
|
+
color: var(--color-on-surface);
|
|
244
|
+
font-synthesis: none;
|
|
245
|
+
font-optical-sizing: auto;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@media (min-width: 640px) {
|
|
249
|
+
.section-title {
|
|
250
|
+
font-size: var(--type-section-lg);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* Featured article lead on the writing index is a content title, not a page name. */
|
|
255
|
+
.writing-lead-title.section-title {
|
|
256
|
+
font-family: var(--font-content-title);
|
|
257
|
+
font-weight: var(--weight-title);
|
|
258
|
+
font-synthesis: none;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/* The italicized second half of a section or page title ("Articles & Notes",
|
|
262
|
+
"Personal Projects & Experiments") carries the page's accent colour plus a
|
|
263
|
+
marker highlight — felt-pen underlay, not invented copy. Same wash on home
|
|
264
|
+
band titles (`.section-title em`) and listing mastheads (`.page-header-title
|
|
265
|
+
em`) so the two never diverge. */
|
|
266
|
+
.section-title em,
|
|
267
|
+
.page-header-title em {
|
|
268
|
+
font-style: italic;
|
|
269
|
+
color: var(--color-primary);
|
|
270
|
+
background: linear-gradient(104deg,
|
|
271
|
+
transparent 0.9%,
|
|
272
|
+
color-mix(in srgb, var(--color-primary) 16%, transparent) 2.4%,
|
|
273
|
+
color-mix(in srgb, var(--color-primary) 12%, transparent) 58%,
|
|
274
|
+
color-mix(in srgb, var(--color-primary) 4%, transparent) 98%);
|
|
275
|
+
box-decoration-break: clone;
|
|
276
|
+
-webkit-box-decoration-break: clone;
|
|
277
|
+
padding: 0 0.1em;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* Dark ink paper: dim the felt wash so primary/ochre highlighter does not glow. */
|
|
281
|
+
[data-theme="dark"] .section-title em,
|
|
282
|
+
[data-theme="dark"] .page-header-title em {
|
|
283
|
+
background: linear-gradient(104deg,
|
|
284
|
+
transparent 0.9%,
|
|
285
|
+
color-mix(in srgb, var(--color-primary) 9%, transparent) 2.4%,
|
|
286
|
+
color-mix(in srgb, var(--color-primary) 6%, transparent) 58%,
|
|
287
|
+
color-mix(in srgb, var(--color-primary) 2%, transparent) 98%);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
/* When headers.yml marks only half the title, the outer loading span's
|
|
293
|
+
blanket italic must not italicize the unmarked half. */
|
|
294
|
+
.page-header-title:has(em) [data-content-header-title] {
|
|
295
|
+
font-style: normal;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* Marker wash for any field-backed highlight (hero family name, etc.). */
|
|
299
|
+
.ledger-marker {
|
|
300
|
+
background: linear-gradient(104deg,
|
|
301
|
+
transparent 0.9%,
|
|
302
|
+
color-mix(in srgb, var(--color-tertiary) 20%, transparent) 2.4%,
|
|
303
|
+
color-mix(in srgb, var(--color-tertiary) 16%, transparent) 58%,
|
|
304
|
+
color-mix(in srgb, var(--color-tertiary) 5%, transparent) 98%);
|
|
305
|
+
box-decoration-break: clone;
|
|
306
|
+
-webkit-box-decoration-break: clone;
|
|
307
|
+
padding: 0 0.1em;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
[data-theme="dark"] .ledger-marker {
|
|
311
|
+
background: linear-gradient(104deg,
|
|
312
|
+
transparent 0.9%,
|
|
313
|
+
color-mix(in srgb, var(--color-tertiary) 10%, transparent) 2.4%,
|
|
314
|
+
color-mix(in srgb, var(--color-tertiary) 7%, transparent) 58%,
|
|
315
|
+
color-mix(in srgb, var(--color-tertiary) 2%, transparent) 98%);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.ledger-marker--primary {
|
|
319
|
+
background: linear-gradient(104deg,
|
|
320
|
+
transparent 0.9%,
|
|
321
|
+
color-mix(in srgb, var(--color-primary) 16%, transparent) 2.4%,
|
|
322
|
+
color-mix(in srgb, var(--color-primary) 12%, transparent) 58%,
|
|
323
|
+
color-mix(in srgb, var(--color-primary) 4%, transparent) 98%);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
[data-theme="dark"] .ledger-marker--primary {
|
|
327
|
+
background: linear-gradient(104deg,
|
|
328
|
+
transparent 0.9%,
|
|
329
|
+
color-mix(in srgb, var(--color-primary) 9%, transparent) 2.4%,
|
|
330
|
+
color-mix(in srgb, var(--color-primary) 6%, transparent) 58%,
|
|
331
|
+
color-mix(in srgb, var(--color-primary) 2%, transparent) 98%);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* ── Page-level accent ────────────────────────────────────────────────
|
|
335
|
+
The shared triad (theme/theme-vars-block.liquid) declares one primary per
|
|
336
|
+
theme, but each section of the site reads in its own hue: Projects and
|
|
337
|
+
Sources blue, Articles and the CV green, Rascunhos and Network ochre.
|
|
338
|
+
Rather than forking the palette, a page puts one of these classes on its
|
|
339
|
+
top-level container and the class rebinds the same custom property names
|
|
340
|
+
theme-vars-block.liquid already defines, pointing them at an *existing*
|
|
341
|
+
triad token — never a new literal. Custom properties inherit, so every
|
|
342
|
+
rule anywhere (here or in a ledger/*.css) that reads `var(--color-primary)`
|
|
343
|
+
picks the page's hue up for free: card titles, CTA fills and borders,
|
|
344
|
+
section-title italics.
|
|
345
|
+
|
|
346
|
+
Parameterisation: set `accent: green|ochre` in the listing page front
|
|
347
|
+
matter and in `content/pages/headers.yml` (home bands apply it at
|
|
348
|
+
hydrate time). Collection defaults in `_config.yml` cover studio/detail
|
|
349
|
+
paths. Omit the field (or use `blue`) for the default primary.
|
|
350
|
+
|
|
351
|
+
The hover tone is mixed toward `--color-on-surface` rather than borrowing
|
|
352
|
+
a `*-fixed-dim` token. Those tokens hold the same value in both palettes,
|
|
353
|
+
so they lighten the link on hover in *both* — correct against a near-black
|
|
354
|
+
canvas, wrong against white paper, where hover has to go darker to read as
|
|
355
|
+
emphasis. Mixing toward the foreground ink moves the right direction in
|
|
356
|
+
each theme automatically, because that ink is dark in light and light in
|
|
357
|
+
dark. Blue needs no class: it is the default primary. Categorical drawings
|
|
358
|
+
that must keep the absolute triad (network graph) read `--triad-*` instead.
|
|
359
|
+
|
|
360
|
+
These lived in cv.css and writing.css as two byte-similar copies until
|
|
361
|
+
the third page needed one. One definition, here, beside the rule that
|
|
362
|
+
consumes it. */
|
|
363
|
+
.page-accent--green {
|
|
364
|
+
--color-primary: var(--color-secondary);
|
|
365
|
+
--color-link: var(--color-secondary);
|
|
366
|
+
--color-link-hover: color-mix(in srgb, var(--color-secondary) 72%, var(--color-on-surface));
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.page-accent--ochre {
|
|
370
|
+
--color-primary: var(--color-tertiary);
|
|
371
|
+
--color-link: var(--color-tertiary);
|
|
372
|
+
--color-link-hover: color-mix(in srgb, var(--color-tertiary) 72%, var(--color-on-surface));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/* Card / row / collection entry titles — content-title voice (Regular 400). */
|
|
376
|
+
.card-title,
|
|
377
|
+
.ledger-card-title,
|
|
378
|
+
.ledger-row-title,
|
|
379
|
+
.source-card-title {
|
|
380
|
+
font-family: var(--font-content-title);
|
|
381
|
+
font-size: var(--type-card);
|
|
382
|
+
font-weight: var(--weight-title);
|
|
383
|
+
font-synthesis: none;
|
|
384
|
+
line-height: 1.25;
|
|
385
|
+
color: var(--color-on-surface);
|
|
386
|
+
font-optical-sizing: auto;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/* Label-face meta on CV identity lines, etc. */
|
|
390
|
+
.card-meta {
|
|
391
|
+
font-family: var(--font-label);
|
|
392
|
+
font-size: var(--type-meta);
|
|
393
|
+
font-weight: var(--weight-label);
|
|
394
|
+
letter-spacing: 0.04em;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.card-meta-block {
|
|
398
|
+
display: flex;
|
|
399
|
+
flex-direction: column;
|
|
400
|
+
align-items: flex-start;
|
|
401
|
+
gap: 0.5rem;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* Notation — tabular mono only. */
|
|
405
|
+
.code-technical {
|
|
406
|
+
font-family: var(--font-mono);
|
|
407
|
+
font-size: var(--type-mono);
|
|
408
|
+
line-height: 1.45;
|
|
409
|
+
font-variant-numeric: tabular-nums;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/* Masthead tag line under the page H1. */
|
|
413
|
+
[data-content-header-tags],
|
|
414
|
+
.page-header-tags {
|
|
415
|
+
font-size: var(--type-dek);
|
|
416
|
+
line-height: 1.45;
|
|
417
|
+
letter-spacing: 0.04em;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* Dates, kickers, section notes — body face at meta size. */
|
|
421
|
+
.ledger-meta,
|
|
422
|
+
.ledger-kicker,
|
|
423
|
+
.ledger-section-note {
|
|
424
|
+
font-family: var(--font-body);
|
|
425
|
+
font-size: var(--type-meta);
|
|
426
|
+
font-weight: var(--weight-body);
|
|
427
|
+
line-height: 1.45;
|
|
428
|
+
font-variant-numeric: tabular-nums;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/* ── Detail-page return link ── */
|
|
432
|
+
.back-link {
|
|
433
|
+
color: var(--color-primary);
|
|
434
|
+
text-decoration: none;
|
|
435
|
+
font-size: var(--type-label-lg);
|
|
436
|
+
display: inline-block;
|
|
437
|
+
margin-bottom: 2rem;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.back-link:hover {
|
|
441
|
+
text-decoration: underline;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/* Text selection: quiet ink wash at high transparency, matching the
|
|
445
|
+
neutral shard/hero highlights rather than a primary-container spotlight. */
|
|
446
|
+
::selection {
|
|
447
|
+
background: color-mix(in srgb, var(--color-on-surface) 12%, transparent);
|
|
448
|
+
color: inherit;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
::-moz-selection {
|
|
452
|
+
background: color-mix(in srgb, var(--color-on-surface) 12%, transparent);
|
|
453
|
+
color: inherit;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/* ── Shared layout tokens ── */
|
|
457
|
+
/* Light densifies paper rules. Prefer :root:not([data-theme=dark]) over
|
|
458
|
+
html:root so a missing attribute (pre-boot) still densifies light without
|
|
459
|
+
also matching dark once data-theme=dark is set. Dark keeps Liquid defaults
|
|
460
|
+
from theme-vars-block — do not re-dense them here. */
|
|
461
|
+
html:root,
|
|
462
|
+
html[data-theme="light"] {
|
|
463
|
+
--cubist-shard-opacity: 18%;
|
|
464
|
+
--cubist-shard-opacity-strong: 26%;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
:root:not([data-theme="dark"]),
|
|
468
|
+
html[data-theme="light"] {
|
|
469
|
+
/* Light paper rules: denser outline mixes so white-on-white cards keep a
|
|
470
|
+
Stitch-like margin. */
|
|
471
|
+
--border-gentle-faint: color-mix(in srgb, var(--color-outline-variant) 40%, transparent);
|
|
472
|
+
--border-gentle-subtle: color-mix(in srgb, var(--color-outline-variant) 58%, transparent);
|
|
473
|
+
--border-gentle-divider: color-mix(in srgb, var(--color-outline-variant) 64%, transparent);
|
|
474
|
+
--border-gentle: color-mix(in srgb, var(--color-outline-variant) 82%, transparent);
|
|
475
|
+
--border-gentle-hover: color-mix(in srgb, var(--color-outline-variant) 94%, transparent);
|
|
476
|
+
/* Card / chip frames: same hairline as the CV disclaimer box
|
|
477
|
+
(`.cv-disclaimer-box` → `--border-gentle-subtle`). Opaque mid-ink was
|
|
478
|
+
heavier than the rest of the light ledger; this outline-variant mix
|
|
479
|
+
keeps a clear edge without a second border family. */
|
|
480
|
+
--border-ink: var(--border-gentle-subtle);
|
|
481
|
+
--border-ink-strong: var(--border-gentle-subtle);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
html:root,
|
|
485
|
+
html[data-theme="light"] {
|
|
486
|
+
/* Spacing scale. Rhythm comes from deliberate jumps on this ladder rather
|
|
487
|
+
than from repeated rules, so components step xl -> 2xl -> 3xl between
|
|
488
|
+
bands and stay on sm/md inside a card. */
|
|
489
|
+
--space-xs: 0.25rem;
|
|
490
|
+
--space-sm: 0.5rem;
|
|
491
|
+
--space-md: 1rem;
|
|
492
|
+
--space-lg: 1.5rem;
|
|
493
|
+
--space-xl: 2rem;
|
|
494
|
+
--space-2xl: 3rem;
|
|
495
|
+
--space-3xl: 4.5rem;
|
|
496
|
+
--gutter: 2rem;
|
|
497
|
+
--gutter-mobile: 1rem;
|
|
498
|
+
|
|
499
|
+
/* The two canonical shard polygons. Every accent uses one of these; the
|
|
500
|
+
five ad hoc variants the old home and CV panels carried went out with
|
|
501
|
+
those panels, so nothing outside this pair is left. */
|
|
502
|
+
--shard-alpha: polygon(26% 0, 100% 0, 78% 100%, 0 68%);
|
|
503
|
+
--shard-beta: polygon(0 0, 74% 18%, 100% 100%, 18% 84%);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
html[data-theme="dark"] {
|
|
507
|
+
--cubist-shard-opacity: 10%;
|
|
508
|
+
--cubist-shard-opacity-strong: 14%;
|
|
509
|
+
/* Dark Borders: sit on the canvas, barely a step above background. */
|
|
510
|
+
--border-ink: color-mix(in srgb, var(--color-on-surface) 5%, var(--color-background));
|
|
511
|
+
--border-ink-strong: color-mix(in srgb, var(--color-on-surface) 8%, var(--color-background));
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/* Soft paper radius token (0 = square Borders Edition). Pills and status
|
|
515
|
+
dots stay fully round; shards stay clipped polygons. */
|
|
516
|
+
.shard-accent {
|
|
517
|
+
content: "";
|
|
518
|
+
position: absolute;
|
|
519
|
+
pointer-events: none;
|
|
520
|
+
z-index: 0;
|
|
521
|
+
clip-path: var(--shard-alpha);
|
|
522
|
+
opacity: var(--cubist-shard-opacity);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.shard-accent-beta {
|
|
526
|
+
clip-path: var(--shard-beta);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/* Shards are decoration, not information: below the tablet breakpoint they
|
|
530
|
+
cost layout without earning it. */
|
|
531
|
+
@media (max-width: 639px) {
|
|
532
|
+
.shard-accent {
|
|
533
|
+
display: none;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.professional-connections-grid {
|
|
538
|
+
display: grid;
|
|
539
|
+
grid-template-columns: 1fr;
|
|
540
|
+
gap: 1rem;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
@media (min-width: 640px) {
|
|
544
|
+
.professional-connections-grid {
|
|
545
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.professional-connections-grid .group {
|
|
550
|
+
position: relative;
|
|
551
|
+
min-height: 100%;
|
|
552
|
+
align-items: flex-start;
|
|
553
|
+
padding: 0.95rem 1rem;
|
|
554
|
+
background: transparent;
|
|
555
|
+
border: 1px solid var(--border-gentle);
|
|
556
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-default), border-color var(--motion-duration-fast) var(--motion-ease-default);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.professional-connections-grid .group:hover {
|
|
560
|
+
background: var(--color-surface-high);
|
|
561
|
+
border-color: var(--border-gentle-hover);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/* Also remove underline for elements using the utility class 'no-underline' */
|
|
565
|
+
a.no-underline,
|
|
566
|
+
a.no-underline:hover,
|
|
567
|
+
a.no-underline:focus,
|
|
568
|
+
a.no-underline:active {
|
|
569
|
+
text-decoration: none;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/* Interactive applets follow surface / on-surface tokens (light stays white) */
|
|
573
|
+
.applet-canvas {
|
|
574
|
+
background-color: var(--color-surface);
|
|
575
|
+
color: var(--color-on-surface);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.applet-canvas .timeline-applet-control,
|
|
579
|
+
.applet-canvas .timeline-zoom-btn {
|
|
580
|
+
background-color: var(--color-surface);
|
|
581
|
+
border-color: color-mix(in srgb, var(--color-outline-variant) 55%, transparent);
|
|
582
|
+
color: var(--color-on-surface-variant);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.applet-canvas .timeline-applet-control:hover,
|
|
586
|
+
.applet-canvas .timeline-zoom-btn:hover {
|
|
587
|
+
border-color: var(--color-primary);
|
|
588
|
+
color: var(--color-primary);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/* ── Listing pages: full-width card grid + header filter ── */
|
|
592
|
+
.sr-only {
|
|
593
|
+
position: absolute;
|
|
594
|
+
width: 1px;
|
|
595
|
+
height: 1px;
|
|
596
|
+
padding: 0;
|
|
597
|
+
margin: -1px;
|
|
598
|
+
overflow: hidden;
|
|
599
|
+
clip: rect(0, 0, 0, 0);
|
|
600
|
+
white-space: nowrap;
|
|
601
|
+
border: 0;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.listing-card-grid {
|
|
605
|
+
--listing-grid-cols: 2;
|
|
606
|
+
display: grid;
|
|
607
|
+
grid-template-columns: 1fr;
|
|
608
|
+
gap: 2rem;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
@media (min-width: 768px) {
|
|
612
|
+
.listing-card-grid {
|
|
613
|
+
grid-template-columns: repeat(var(--listing-grid-cols), minmax(0, 1fr));
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.page-header-row {
|
|
618
|
+
display: flex;
|
|
619
|
+
flex-direction: column;
|
|
620
|
+
gap: 1.25rem;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
@media (min-width: 768px) {
|
|
624
|
+
.page-header-row--with-filter {
|
|
625
|
+
flex-direction: row;
|
|
626
|
+
align-items: flex-end;
|
|
627
|
+
justify-content: space-between;
|
|
628
|
+
gap: 2rem;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.page-header-row--with-filter .listing-filter {
|
|
632
|
+
flex-shrink: 0;
|
|
633
|
+
align-self: flex-end;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.listing-filter {
|
|
638
|
+
position: relative;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.listing-filter-trigger {
|
|
642
|
+
display: inline-flex;
|
|
643
|
+
align-items: center;
|
|
644
|
+
gap: 0.5rem;
|
|
645
|
+
padding: 0.35rem 0.75rem;
|
|
646
|
+
border: 1.5px solid var(--border-ink);
|
|
647
|
+
border-radius: var(--radius-card);
|
|
648
|
+
background: var(--color-surface-card);
|
|
649
|
+
color: var(--color-on-surface-variant);
|
|
650
|
+
cursor: pointer;
|
|
651
|
+
transition: color var(--motion-duration-fast) var(--motion-ease-default),
|
|
652
|
+
border-color var(--motion-duration-fast) var(--motion-ease-default),
|
|
653
|
+
background-color var(--motion-duration-fast) var(--motion-ease-default);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.listing-filter-trigger:hover,
|
|
657
|
+
.listing-filter-trigger[aria-expanded="true"] {
|
|
658
|
+
color: var(--color-secondary);
|
|
659
|
+
border-color: var(--color-secondary);
|
|
660
|
+
background: var(--color-surface-high);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.listing-filter-trigger .material-symbols-outlined {
|
|
664
|
+
font-size: var(--type-card);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.listing-filter-panel {
|
|
668
|
+
position: absolute;
|
|
669
|
+
z-index: 30;
|
|
670
|
+
top: calc(100% + 0.5rem);
|
|
671
|
+
right: 0;
|
|
672
|
+
width: min(22rem, calc(100vw - 4rem));
|
|
673
|
+
padding: 1rem 1.125rem;
|
|
674
|
+
background: var(--color-surface);
|
|
675
|
+
border: 1px solid var(--border-gentle);
|
|
676
|
+
box-shadow: 0 12px 32px color-mix(in srgb, var(--color-on-surface) 12%, transparent);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
@media (max-width: 767px) {
|
|
680
|
+
.listing-filter-panel {
|
|
681
|
+
position: static;
|
|
682
|
+
width: 100%;
|
|
683
|
+
margin-top: 0.25rem;
|
|
684
|
+
box-shadow: none;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
.listing-filter-search-label {
|
|
689
|
+
display: block;
|
|
690
|
+
margin-bottom: 0.75rem;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.listing-filter-search {
|
|
694
|
+
width: 100%;
|
|
695
|
+
padding: 0.5rem 0.65rem;
|
|
696
|
+
border: 1px solid var(--border-gentle-subtle);
|
|
697
|
+
background: var(--color-background);
|
|
698
|
+
--listing-search-value: color-mix(in srgb, black 75%, white);
|
|
699
|
+
color: var(--listing-search-value);
|
|
700
|
+
-webkit-text-fill-color: var(--listing-search-value);
|
|
701
|
+
caret-color: var(--listing-search-value);
|
|
702
|
+
font-family: var(--font-body);
|
|
703
|
+
font-size: var(--type-subtitle);
|
|
704
|
+
font-weight: var(--weight-body);
|
|
705
|
+
line-height: 1.4;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
[data-theme="dark"] .listing-filter-search {
|
|
709
|
+
--listing-search-value: var(--color-on-surface);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/* The Tailwind forms plugin paints text inputs white from a runtime stylesheet
|
|
713
|
+
injected after this file, and its [type='text'] selector outranks a single
|
|
714
|
+
class. In dark mode that leaves the keyword box as a white hole in the
|
|
715
|
+
panel, on every listing page. Two classes take the specificity back. */
|
|
716
|
+
.listing-filter-panel .listing-filter-search,
|
|
717
|
+
.listing-filter .listing-filter-search {
|
|
718
|
+
background: var(--color-background);
|
|
719
|
+
border: 1px solid var(--border-gentle-subtle);
|
|
720
|
+
color: var(--listing-search-value);
|
|
721
|
+
-webkit-text-fill-color: var(--listing-search-value);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
.listing-filter-search::placeholder {
|
|
725
|
+
font-family: var(--font-body);
|
|
726
|
+
font-size: var(--type-subtitle);
|
|
727
|
+
font-weight: var(--weight-body);
|
|
728
|
+
color: color-mix(in srgb, var(--color-on-surface) 50%, var(--color-on-surface-variant));
|
|
729
|
+
-webkit-text-fill-color: color-mix(in srgb, var(--color-on-surface) 50%, var(--color-on-surface-variant));
|
|
730
|
+
opacity: 1;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.listing-filter-search:-webkit-autofill,
|
|
734
|
+
.listing-filter-search:-webkit-autofill:focus {
|
|
735
|
+
-webkit-text-fill-color: var(--listing-search-value);
|
|
736
|
+
transition: background-color 99999s ease-out 0s;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/* The forms plugin's [type='search']:focus is equal specificity and injected
|
|
740
|
+
later, so a single-class rule here never applied and the ring stayed its
|
|
741
|
+
1px blue. Two classes take it back, and its box-shadow has to go with it. */
|
|
742
|
+
.listing-filter-panel .listing-filter-search:focus,
|
|
743
|
+
.listing-filter .listing-filter-search:focus {
|
|
744
|
+
outline: 2px solid var(--color-primary);
|
|
745
|
+
outline-offset: 1px;
|
|
746
|
+
box-shadow: none;
|
|
747
|
+
border-color: var(--color-primary);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
.listing-filter-group+.listing-filter-group {
|
|
751
|
+
margin-top: 0.85rem;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.listing-filter-group-label {
|
|
755
|
+
margin: 0 0 0.5rem;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
.listing-filter-chips {
|
|
759
|
+
display: flex;
|
|
760
|
+
flex-wrap: wrap;
|
|
761
|
+
gap: 0.5rem;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
.listing-filter-chip {
|
|
765
|
+
display: inline-flex;
|
|
766
|
+
align-items: center;
|
|
767
|
+
justify-content: center;
|
|
768
|
+
gap: 0.35rem;
|
|
769
|
+
padding: 0.35rem 0.55rem;
|
|
770
|
+
border: 1.5px solid var(--border-ink);
|
|
771
|
+
border-radius: var(--radius-card);
|
|
772
|
+
background: transparent;
|
|
773
|
+
color: var(--color-on-surface-variant);
|
|
774
|
+
font-family: var(--font-label);
|
|
775
|
+
font-size: var(--type-label);
|
|
776
|
+
font-weight: var(--weight-label);
|
|
777
|
+
letter-spacing: 0.02em;
|
|
778
|
+
line-height: 1.2;
|
|
779
|
+
cursor: pointer;
|
|
780
|
+
transition: border-color var(--motion-duration-fast) var(--motion-ease-default), color var(--motion-duration-fast) var(--motion-ease-default), background-color var(--motion-duration-fast) var(--motion-ease-default);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.listing-filter-chip:hover {
|
|
784
|
+
border-color: color-mix(in srgb, var(--color-primary) 55%, var(--border-ink));
|
|
785
|
+
color: var(--color-primary);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.listing-filter-chip.is-active {
|
|
789
|
+
border-color: var(--color-primary);
|
|
790
|
+
color: var(--color-primary);
|
|
791
|
+
/* High-transparency accent wash — selected cue without a solid green plate. */
|
|
792
|
+
background: color-mix(in srgb, var(--color-primary) 8%, transparent);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
.listing-filter-swatch {
|
|
796
|
+
display: inline-block;
|
|
797
|
+
width: 0.75rem;
|
|
798
|
+
height: 0.75rem;
|
|
799
|
+
flex-shrink: 0;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/* ══ Architectural Ledger blocks ══
|
|
803
|
+
Styles for the shared renderers in content-runtime/43-blocks.html. Depth
|
|
804
|
+
comes from the surface ladder, never from shadow, and every corner is
|
|
805
|
+
square. Each block is invisible when its backing field is absent, so these
|
|
806
|
+
rules only ever describe the populated case. */
|
|
807
|
+
|
|
808
|
+
.lang-pill {
|
|
809
|
+
font-family: var(--font-label);
|
|
810
|
+
font-size: var(--type-label);
|
|
811
|
+
font-weight: var(--weight-label);
|
|
812
|
+
letter-spacing: 0.03em;
|
|
813
|
+
padding: 0.1rem 0.4rem;
|
|
814
|
+
border: 1px solid color-mix(in srgb, currentColor 35%, transparent);
|
|
815
|
+
white-space: nowrap;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
.lang-pill--enus {
|
|
819
|
+
color: var(--color-primary);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.lang-pill--ptbr {
|
|
823
|
+
color: var(--color-secondary);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
.ledger-chip-row {
|
|
827
|
+
display: flex;
|
|
828
|
+
flex-wrap: wrap;
|
|
829
|
+
gap: var(--space-xs);
|
|
830
|
+
align-items: center;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
.ledger-chip {
|
|
834
|
+
font-family: var(--font-label);
|
|
835
|
+
font-size: var(--type-cta);
|
|
836
|
+
font-weight: var(--weight-label);
|
|
837
|
+
letter-spacing: 0.03em;
|
|
838
|
+
padding: 0.15rem 0.45rem;
|
|
839
|
+
/* Tags carry the page's accent rather than a neutral grey. Both values mix
|
|
840
|
+
`--color-primary`, which `.page-accent--*` rebinds per page, so a chip is
|
|
841
|
+
blue on Projects, green on Articles and the CV, ochre on Rascunhos with
|
|
842
|
+
no per-page rule anywhere. The fill stays mostly surface so a row of
|
|
843
|
+
chips reads as metadata and not as a row of buttons; the ink carries
|
|
844
|
+
most of the hue, which is what makes the tag legible as a tag.
|
|
845
|
+
Note this is `.ledger-chip` only: the page-header tag line (the CV's
|
|
846
|
+
"Engineering Leadership. Team Building…") is a separate
|
|
847
|
+
`data-content-header-tags` span and is deliberately left alone. */
|
|
848
|
+
background: color-mix(in srgb, var(--color-primary) 16%, var(--color-surface-high));
|
|
849
|
+
color: color-mix(in srgb, var(--color-primary) 82%, var(--color-on-surface));
|
|
850
|
+
text-decoration: none;
|
|
851
|
+
border-radius: var(--radius-card);
|
|
852
|
+
/* The hover hairline arrives as an inset ring rather than a border, and is
|
|
853
|
+
declared transparent here so it has something to transition from. This is
|
|
854
|
+
not the shadow the system forbids — nothing is offset, blurred or spread,
|
|
855
|
+
so it draws a 1px line on the chip's own edge, exactly where a border
|
|
856
|
+
would sit. A real border would be the idiom, but it would have to be
|
|
857
|
+
present at rest to avoid a 2px jump under the pointer, and the padding
|
|
858
|
+
compensation that costs only lands exactly at integer device pixel
|
|
859
|
+
ratios: at 1.75 the browser snaps the border to 0.571px and the chip
|
|
860
|
+
quietly loses most of a pixel on both axes. The ring takes no space at
|
|
861
|
+
any ratio, so the resting chip keeps its size, its fill and its square
|
|
862
|
+
corners. */
|
|
863
|
+
box-shadow: inset 0 0 0 1px transparent;
|
|
864
|
+
transition: color var(--motion-duration-fast) var(--motion-ease-default), background-color var(--motion-duration-fast) var(--motion-ease-default), box-shadow var(--motion-duration-fast) var(--motion-ease-default), border-color var(--motion-duration-fast) var(--motion-ease-default);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
/* Hover reads `.ledger-card:hover` — one tonal step plus an accent-warmed
|
|
868
|
+
hairline — at chip scale.
|
|
869
|
+
|
|
870
|
+
The step cannot be a ladder rung: the chip's resting fill is already the
|
|
871
|
+
ladder's top one. What it used to do instead was borrow a rung from the
|
|
872
|
+
other surface family (`surfaceHigh` → `surfaceContainerHigh`), which is
|
|
873
|
+
the defect DESIGN.md records under *Two surface families name the same
|
|
874
|
+
colours* — in dark the two are the same value to the byte, so the hover
|
|
875
|
+
measured 1.000 : 1 and only the text changed, and in light they are close
|
|
876
|
+
enough to have read the same way. Tinting the resting fill with the page
|
|
877
|
+
accent instead is a real step in both palettes — down on paper, up on the
|
|
878
|
+
dark canvas — and stays right whichever way the light ladder is pointed,
|
|
879
|
+
which the borrowed rung did not. 8% is the value
|
|
880
|
+
`.listing-filter-chip.is-active` already uses for an accent-tinted fill.
|
|
881
|
+
|
|
882
|
+
The hairline carries the rest, at 35% accent over plain `--border-gentle`
|
|
883
|
+
rather than the card's 40% over `--border-gentle-hover`. A line drawn
|
|
884
|
+
around something this small is a far larger share of its ink than the same
|
|
885
|
+
line around a card, so the quieter mix is the proportionate one. */
|
|
886
|
+
a.ledger-chip:hover {
|
|
887
|
+
color: var(--color-on-surface);
|
|
888
|
+
/* Resting fill is 16% accent in light; hover must clear that. */
|
|
889
|
+
background: color-mix(in srgb, var(--color-primary) 28%, var(--color-surface-high));
|
|
890
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-primary) 45%, var(--border-gentle));
|
|
891
|
+
/* No-op for the filled chip, which has no border. It matters on the sources
|
|
892
|
+
facets, the one place that outlines the chip instead of filling it: the
|
|
893
|
+
ring would otherwise be drawn just inside that outline and the two
|
|
894
|
+
hairlines would read as one 2px line. Handing the edge to the ring keeps
|
|
895
|
+
the hover at a single line there too, and the fill that page expects on
|
|
896
|
+
hover still arrives. */
|
|
897
|
+
border-color: transparent;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
[data-theme="dark"] a.ledger-chip {
|
|
901
|
+
background: color-mix(in srgb, var(--color-primary) 10%, var(--color-surface-high));
|
|
902
|
+
color: color-mix(in srgb, var(--color-primary) 78%, var(--color-on-surface));
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
[data-theme="dark"] a.ledger-chip:hover {
|
|
906
|
+
background: color-mix(in srgb, var(--color-primary) 22%, var(--color-surface-high));
|
|
907
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-primary) 35%, var(--border-gentle));
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/* Dates live here. The chips and language pills inside the strip set their
|
|
911
|
+
own uppercase, so the strip itself leaves case alone — running small caps
|
|
912
|
+
over a date is heavier than the date deserves. */
|
|
913
|
+
.ledger-meta {
|
|
914
|
+
display: flex;
|
|
915
|
+
flex-wrap: wrap;
|
|
916
|
+
align-items: center;
|
|
917
|
+
gap: var(--space-sm);
|
|
918
|
+
color: var(--color-on-surface-variant);
|
|
919
|
+
letter-spacing: 0.04em;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/* A separator is ink, not a border. --color-outline-variant is tuned for
|
|
923
|
+
hairlines and reads at 1.31:1 here, which is invisible rather than quiet. */
|
|
924
|
+
.ledger-meta-sep {
|
|
925
|
+
color: var(--color-outline);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.ledger-cta {
|
|
929
|
+
display: inline-flex;
|
|
930
|
+
align-items: center;
|
|
931
|
+
gap: var(--space-xs);
|
|
932
|
+
font-family: var(--font-label);
|
|
933
|
+
font-size: var(--type-cta);
|
|
934
|
+
font-weight: var(--weight-label);
|
|
935
|
+
letter-spacing: 0.04em;
|
|
936
|
+
color: var(--color-primary);
|
|
937
|
+
text-decoration: none;
|
|
938
|
+
white-space: nowrap;
|
|
939
|
+
line-height: 1;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
.ledger-cta>span {
|
|
943
|
+
display: inline-flex;
|
|
944
|
+
align-items: center;
|
|
945
|
+
line-height: 1;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/* Text CTAs carry a gentle accent underline so the clickable cue is not the
|
|
949
|
+
same gesture as the marker wash on titles. Emphasis buttons keep a box
|
|
950
|
+
fill instead — underline would fight the border. */
|
|
951
|
+
.ledger-cta:not(.ledger-cta--emphasis):not(.ledger-cta--emphasis-alt)>span:not(.ledger-cta-arrow):not(.ledger-cta-icon) {
|
|
952
|
+
text-decoration: underline;
|
|
953
|
+
text-decoration-color: color-mix(in srgb, var(--color-primary) 50%, transparent);
|
|
954
|
+
text-decoration-thickness: 1.5px;
|
|
955
|
+
text-underline-offset: 0.28em;
|
|
956
|
+
text-underline-position: under;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
.ledger-cta-icon,
|
|
960
|
+
.ledger-cta-arrow {
|
|
961
|
+
display: inline-flex;
|
|
962
|
+
align-items: center;
|
|
963
|
+
justify-content: center;
|
|
964
|
+
font-size: 1.35em;
|
|
965
|
+
line-height: 1;
|
|
966
|
+
text-decoration: none;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
.ledger-cta:hover {
|
|
970
|
+
color: var(--color-link-hover);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
.ledger-cta:not(.ledger-cta--emphasis):not(.ledger-cta--emphasis-alt):hover>span:not(.ledger-cta-arrow):not(.ledger-cta-icon) {
|
|
974
|
+
text-decoration-color: color-mix(in srgb, var(--color-link-hover) 75%, transparent);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
.ledger-cta-arrow {
|
|
978
|
+
transition: transform var(--motion-duration-fast) var(--motion-ease-default);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
.ledger-cta:hover .ledger-cta-arrow {
|
|
982
|
+
transform: translateX(4px);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/* Shared discrete-button replacement for the old per-page `.home-cta` /
|
|
986
|
+
`.home-cta--solid` / `.home-cta--outline` pattern (assets/css/ledger/home.css).
|
|
987
|
+
Those set real button padding (0.85rem vertical, --space-lg horizontal) and a
|
|
988
|
+
solid filled background for the primary case. `.ledger-cta--emphasis` is the
|
|
989
|
+
one or two truly primary actions per page (e.g. hero "Full Curriculum
|
|
990
|
+
Vitæ"): still a `.ledger-cta`, just with tight padding and a tinted fill
|
|
991
|
+
instead of a heavy solid block, so it reads as a real button — with color
|
|
992
|
+
and contrast — without becoming a bulky box. Reuse this instead of adding
|
|
993
|
+
another one-off CTA class. `--emphasis-alt` swaps the hue to secondary, for
|
|
994
|
+
a second hero-level action (e.g. "Contacts") that needs to read as a
|
|
995
|
+
distinct button next to the primary one rather than a duplicate of it. */
|
|
996
|
+
.ledger-cta--emphasis {
|
|
997
|
+
padding: 0.35rem 0.85rem;
|
|
998
|
+
border: 1px solid var(--color-primary);
|
|
999
|
+
border-radius: var(--radius-card);
|
|
1000
|
+
background: color-mix(in srgb, var(--color-primary) 22%, var(--color-background));
|
|
1001
|
+
transition: color var(--motion-duration-fast) var(--motion-ease-default),
|
|
1002
|
+
border-color var(--motion-duration-fast) var(--motion-ease-default),
|
|
1003
|
+
background-color var(--motion-duration-fast) var(--motion-ease-default);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
.ledger-cta--emphasis:hover {
|
|
1007
|
+
color: var(--color-on-primary, var(--color-surface));
|
|
1008
|
+
border-color: var(--color-link-hover);
|
|
1009
|
+
background: var(--color-primary);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
.ledger-cta--emphasis-alt {
|
|
1013
|
+
padding: 0.35rem 0.85rem;
|
|
1014
|
+
border: 1px solid var(--color-secondary);
|
|
1015
|
+
border-radius: var(--radius-card);
|
|
1016
|
+
background: color-mix(in srgb, var(--color-secondary) 22%, var(--color-background));
|
|
1017
|
+
color: var(--color-secondary);
|
|
1018
|
+
transition: color var(--motion-duration-fast) var(--motion-ease-default),
|
|
1019
|
+
border-color var(--motion-duration-fast) var(--motion-ease-default),
|
|
1020
|
+
background-color var(--motion-duration-fast) var(--motion-ease-default);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
[data-theme="dark"] .ledger-cta--emphasis {
|
|
1024
|
+
background: color-mix(in srgb, var(--color-primary) 18%, var(--color-background));
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
[data-theme="dark"] .ledger-cta--emphasis-alt {
|
|
1028
|
+
background: color-mix(in srgb, var(--color-secondary) 18%, var(--color-background));
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/* Dark resting mixes sit after the shared :hover fill and match its
|
|
1032
|
+
specificity (attr + class vs class + :hover), so without these the
|
|
1033
|
+
primary button kept the 14% wash under the pointer while the ink
|
|
1034
|
+
stepped to on-primary — dark text on a near-black hero. Alt escaped
|
|
1035
|
+
because its :hover rule already follows the dark rest. */
|
|
1036
|
+
[data-theme="dark"] .ledger-cta--emphasis:hover {
|
|
1037
|
+
background: var(--color-primary);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
[data-theme="dark"] .ledger-cta--emphasis-alt:hover {
|
|
1041
|
+
background: var(--color-secondary);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
.ledger-cta--emphasis-alt:hover {
|
|
1045
|
+
color: var(--color-on-secondary, var(--color-surface));
|
|
1046
|
+
border-color: var(--color-secondary);
|
|
1047
|
+
background: var(--color-secondary);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
.ledger-section-banner {
|
|
1051
|
+
display: flex;
|
|
1052
|
+
flex-wrap: wrap;
|
|
1053
|
+
align-items: baseline;
|
|
1054
|
+
justify-content: space-between;
|
|
1055
|
+
gap: var(--space-md);
|
|
1056
|
+
margin-bottom: var(--space-xl);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
.ledger-kicker {
|
|
1060
|
+
display: block;
|
|
1061
|
+
color: var(--color-on-surface-variant);
|
|
1062
|
+
letter-spacing: 0.12em;
|
|
1063
|
+
margin-bottom: var(--space-xs);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
.ledger-section-note {
|
|
1067
|
+
color: var(--color-on-surface-variant);
|
|
1068
|
+
letter-spacing: 0.08em;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/* ── Ledger row: dense horizontal entry ── */
|
|
1072
|
+
.ledger-row {
|
|
1073
|
+
display: flex;
|
|
1074
|
+
flex-direction: column;
|
|
1075
|
+
gap: var(--space-sm);
|
|
1076
|
+
padding: var(--space-lg);
|
|
1077
|
+
background: var(--color-surface-card);
|
|
1078
|
+
border: 1.5px solid var(--border-ink);
|
|
1079
|
+
border-radius: var(--radius-card);
|
|
1080
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-default),
|
|
1081
|
+
border-color var(--motion-duration-fast) var(--motion-ease-default);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
.ledger-row:hover {
|
|
1085
|
+
background: var(--color-surface-high);
|
|
1086
|
+
border-color: var(--border-gentle-hover);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
.ledger-row-title a,
|
|
1090
|
+
.ledger-card-title a {
|
|
1091
|
+
color: var(--color-on-surface);
|
|
1092
|
+
text-decoration: none;
|
|
1093
|
+
transition: color var(--motion-duration-fast) var(--motion-ease-default);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
.ledger-row:hover .ledger-row-title a,
|
|
1097
|
+
.ledger-card:hover .ledger-card-title a,
|
|
1098
|
+
.ledger-system-card:hover .ledger-card-title a {
|
|
1099
|
+
color: var(--color-primary);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
.ledger-row-copy,
|
|
1103
|
+
.ledger-card-copy {
|
|
1104
|
+
margin: 0;
|
|
1105
|
+
font-family: var(--font-body);
|
|
1106
|
+
font-size: var(--type-subtitle);
|
|
1107
|
+
font-weight: var(--weight-body);
|
|
1108
|
+
color: var(--color-legend);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/* The footer is a rule with links under it, so it needs enough air not to sit
|
|
1112
|
+
on the line. --space-sm is twice what this used to carry, and it is now the
|
|
1113
|
+
one value for every card of this shape — the home system grid and the
|
|
1114
|
+
projects page each used to override it with a larger one of their own. */
|
|
1115
|
+
.ledger-row-foot,
|
|
1116
|
+
.ledger-card-foot {
|
|
1117
|
+
display: flex;
|
|
1118
|
+
flex-wrap: wrap;
|
|
1119
|
+
align-items: center;
|
|
1120
|
+
justify-content: space-between;
|
|
1121
|
+
gap: var(--space-sm);
|
|
1122
|
+
padding-top: var(--space-sm);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
/* ── Ledger card: vertical grid cell ── */
|
|
1126
|
+
.ledger-card,
|
|
1127
|
+
.ledger-system-card {
|
|
1128
|
+
display: flex;
|
|
1129
|
+
flex-direction: column;
|
|
1130
|
+
background: var(--color-surface-card);
|
|
1131
|
+
border: 1.5px solid var(--border-ink);
|
|
1132
|
+
border-radius: var(--radius-card);
|
|
1133
|
+
transition: background-color var(--motion-duration-fast) var(--motion-ease-default), border-color var(--motion-duration-fast) var(--motion-ease-default);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
/* Light paper: match the CV disclaimer edge — 1px outline hairline, not the
|
|
1137
|
+
1.5px ink frame dark mode needs for faint canvas borders. */
|
|
1138
|
+
:root:not([data-theme="dark"]) .ledger-card,
|
|
1139
|
+
:root:not([data-theme="dark"]) .ledger-system-card,
|
|
1140
|
+
html[data-theme="light"] .ledger-card,
|
|
1141
|
+
html[data-theme="light"] .ledger-system-card {
|
|
1142
|
+
border-width: 1px;
|
|
1143
|
+
border-color: var(--border-ink);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/* Triad top hairline — reusable edge accent (home grids + any listing). */
|
|
1147
|
+
.ledger-card--edge-primary {
|
|
1148
|
+
border-top-width: 2px;
|
|
1149
|
+
border-top-style: solid;
|
|
1150
|
+
border-top-color: var(--color-primary);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
.ledger-card--edge-secondary {
|
|
1154
|
+
border-top-width: 2px;
|
|
1155
|
+
border-top-style: solid;
|
|
1156
|
+
border-top-color: var(--color-secondary);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
.ledger-card--edge-tertiary {
|
|
1160
|
+
border-top-width: 2px;
|
|
1161
|
+
border-top-style: solid;
|
|
1162
|
+
border-top-color: var(--color-tertiary);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
/* Drafting corner brackets — four L-corners, no labels. Pure decoration.
|
|
1166
|
+
Outline ink (not primary): the corners are drafting marks on the plate,
|
|
1167
|
+
same register as `.ledger-frame-crosshairs`, not a brand accent. */
|
|
1168
|
+
.ledger-frame-corners {
|
|
1169
|
+
--frame-corner-size: 0.85rem;
|
|
1170
|
+
--frame-corner-thickness: 1px;
|
|
1171
|
+
position: absolute;
|
|
1172
|
+
inset: 0.35rem;
|
|
1173
|
+
pointer-events: none;
|
|
1174
|
+
z-index: 2;
|
|
1175
|
+
background:
|
|
1176
|
+
linear-gradient(var(--color-outline), var(--color-outline)) top left / var(--frame-corner-size) var(--frame-corner-thickness) no-repeat,
|
|
1177
|
+
linear-gradient(var(--color-outline), var(--color-outline)) top left / var(--frame-corner-thickness) var(--frame-corner-size) no-repeat,
|
|
1178
|
+
linear-gradient(var(--color-outline), var(--color-outline)) top right / var(--frame-corner-size) var(--frame-corner-thickness) no-repeat,
|
|
1179
|
+
linear-gradient(var(--color-outline), var(--color-outline)) top right / var(--frame-corner-thickness) var(--frame-corner-size) no-repeat,
|
|
1180
|
+
linear-gradient(var(--color-outline), var(--color-outline)) bottom left / var(--frame-corner-size) var(--frame-corner-thickness) no-repeat,
|
|
1181
|
+
linear-gradient(var(--color-outline), var(--color-outline)) bottom left / var(--frame-corner-thickness) var(--frame-corner-size) no-repeat,
|
|
1182
|
+
linear-gradient(var(--color-outline), var(--color-outline)) bottom right / var(--frame-corner-size) var(--frame-corner-thickness) no-repeat,
|
|
1183
|
+
linear-gradient(var(--color-outline), var(--color-outline)) bottom right / var(--frame-corner-thickness) var(--frame-corner-size) no-repeat;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/* Empty drafting crosshairs outside a plate — no coordinates, no labels. */
|
|
1187
|
+
.ledger-frame-crosshairs {
|
|
1188
|
+
--cross-arm: 0.55rem;
|
|
1189
|
+
--cross-thick: 1px;
|
|
1190
|
+
position: absolute;
|
|
1191
|
+
inset: -0.35rem;
|
|
1192
|
+
pointer-events: none;
|
|
1193
|
+
z-index: 3;
|
|
1194
|
+
background:
|
|
1195
|
+
linear-gradient(var(--color-outline), var(--color-outline)) top left / var(--cross-arm) var(--cross-thick) no-repeat,
|
|
1196
|
+
linear-gradient(var(--color-outline), var(--color-outline)) top left / var(--cross-thick) var(--cross-arm) no-repeat,
|
|
1197
|
+
linear-gradient(var(--color-outline), var(--color-outline)) top right / var(--cross-arm) var(--cross-thick) no-repeat,
|
|
1198
|
+
linear-gradient(var(--color-outline), var(--color-outline)) top right / var(--cross-thick) var(--cross-arm) no-repeat,
|
|
1199
|
+
linear-gradient(var(--color-outline), var(--color-outline)) bottom left / var(--cross-arm) var(--cross-thick) no-repeat,
|
|
1200
|
+
linear-gradient(var(--color-outline), var(--color-outline)) bottom left / var(--cross-thick) var(--cross-arm) no-repeat,
|
|
1201
|
+
linear-gradient(var(--color-outline), var(--color-outline)) bottom right / var(--cross-arm) var(--cross-thick) no-repeat,
|
|
1202
|
+
linear-gradient(var(--color-outline), var(--color-outline)) bottom right / var(--cross-thick) var(--cross-arm) no-repeat;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
/* Lithography section divider — double hairline with a short primary tick. */
|
|
1206
|
+
.ledger-section-rule {
|
|
1207
|
+
display: block;
|
|
1208
|
+
height: 0;
|
|
1209
|
+
margin: 0 0 var(--space-xl);
|
|
1210
|
+
border: 0;
|
|
1211
|
+
border-top: 1px solid var(--border-gentle-divider);
|
|
1212
|
+
box-shadow: 0 3px 0 -2px var(--border-gentle-faint);
|
|
1213
|
+
position: relative;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
.ledger-section-rule::after {
|
|
1217
|
+
content: "";
|
|
1218
|
+
position: absolute;
|
|
1219
|
+
left: 0;
|
|
1220
|
+
top: -1.5px;
|
|
1221
|
+
width: 2.5rem;
|
|
1222
|
+
height: 2px;
|
|
1223
|
+
background: var(--color-primary);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
.ledger-section-rule--dashed {
|
|
1227
|
+
border-top-style: dashed;
|
|
1228
|
+
box-shadow: none;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/* Hover lifts the card one rung and warms its hairline toward the page's
|
|
1232
|
+
accent. The reference mockup states this as "the line transitions to
|
|
1233
|
+
subtle charcoal slate or warm antique brass at 40% opacity" — the accent
|
|
1234
|
+
mix is that idea, made page-accent-aware rather than hardcoded to one hue.
|
|
1235
|
+
The card itself stays flat: no shadow, no glow. Depth is the rung plus
|
|
1236
|
+
the line, which is the whole premise of the ladder. */
|
|
1237
|
+
.ledger-card:hover,
|
|
1238
|
+
.ledger-system-card:hover {
|
|
1239
|
+
background: var(--color-surface-high);
|
|
1240
|
+
border-color: color-mix(in srgb, var(--color-primary) 55%, var(--border-gentle-hover));
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
[data-theme="dark"] .ledger-card:hover,
|
|
1244
|
+
[data-theme="dark"] .ledger-system-card:hover {
|
|
1245
|
+
border-color: color-mix(in srgb, var(--color-primary) 10%, var(--border-ink));
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
/* Cards and rows sitting on contrast bands keep the same transparent rest
|
|
1249
|
+
as the canvas cards; hover steps to surface-high-contrast. */
|
|
1250
|
+
.home-band--contrast .ledger-card,
|
|
1251
|
+
.home-band--contrast .ledger-row,
|
|
1252
|
+
.home-band--contrast .home-index-panel {
|
|
1253
|
+
background: var(--color-surface-card-contrast);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
.home-band--contrast .ledger-card:hover,
|
|
1257
|
+
.home-band--contrast .ledger-row:hover,
|
|
1258
|
+
.home-band--contrast .home-index-panel:hover {
|
|
1259
|
+
background: var(--color-surface-high-contrast);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/* A project card used to end in a "Details" link pointing at the same place
|
|
1263
|
+
its own title points at, which is a second copy of the destination the
|
|
1264
|
+
reader is already looking at. The title's anchor now covers the whole card
|
|
1265
|
+
instead, so the poster, the tagline and the chips all navigate, and the
|
|
1266
|
+
footer is left to the links that actually leave the site.
|
|
1267
|
+
|
|
1268
|
+
One anchor, stretched: screen readers still announce a single link named
|
|
1269
|
+
after the project, and the keyboard still stops once. The outbound links
|
|
1270
|
+
and the tag chips are lifted above the overlay so they stay clickable. */
|
|
1271
|
+
.ledger-system-card {
|
|
1272
|
+
position: relative;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
.ledger-card-stretch::after {
|
|
1276
|
+
content: "";
|
|
1277
|
+
position: absolute;
|
|
1278
|
+
inset: 0;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
.ledger-system-card .ledger-card-links,
|
|
1282
|
+
.ledger-system-card .ledger-chip-row {
|
|
1283
|
+
position: relative;
|
|
1284
|
+
z-index: 1;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
.ledger-card-body,
|
|
1288
|
+
.ledger-system-card {
|
|
1289
|
+
display: flex;
|
|
1290
|
+
flex-direction: column;
|
|
1291
|
+
gap: var(--space-sm);
|
|
1292
|
+
padding: var(--space-lg);
|
|
1293
|
+
flex: 1;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
.ledger-card-copy {
|
|
1297
|
+
flex: 1;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
.ledger-card-foot {
|
|
1301
|
+
margin-top: auto;
|
|
1302
|
+
border-top: 1px solid var(--border-gentle-divider);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
.ledger-card-links {
|
|
1306
|
+
justify-content: flex-start;
|
|
1307
|
+
gap: var(--space-md);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
.ledger-status {
|
|
1311
|
+
color: var(--color-secondary);
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
/* Optional page standfirst. Sits under the title at the prose measure so a
|
|
1315
|
+
long intro does not run the full page width. */
|
|
1316
|
+
.page-header-standfirst {
|
|
1317
|
+
max-width: var(--reading-measure, 45rem);
|
|
1318
|
+
margin-top: var(--space-md);
|
|
1319
|
+
font-size: var(--type-body);
|
|
1320
|
+
line-height: var(--leading-body);
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
/* Poster media on system cards. Fixed ratio so a grid of projects keeps its
|
|
1324
|
+
baseline whether a card illustrates itself with a still or a capture. */
|
|
1325
|
+
.ledger-poster {
|
|
1326
|
+
position: relative;
|
|
1327
|
+
width: 100%;
|
|
1328
|
+
aspect-ratio: 16 / 9;
|
|
1329
|
+
overflow: hidden;
|
|
1330
|
+
background: var(--color-surface-container);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/* Drafting corners on every poster plate — CSS-only, no invented labels. */
|
|
1334
|
+
.ledger-poster::after {
|
|
1335
|
+
content: "";
|
|
1336
|
+
--frame-corner-size: 0.7rem;
|
|
1337
|
+
--frame-corner-thickness: 1px;
|
|
1338
|
+
position: absolute;
|
|
1339
|
+
inset: 0.3rem;
|
|
1340
|
+
pointer-events: none;
|
|
1341
|
+
z-index: 2;
|
|
1342
|
+
background:
|
|
1343
|
+
linear-gradient(var(--color-primary), var(--color-primary)) top left / var(--frame-corner-size) var(--frame-corner-thickness) no-repeat,
|
|
1344
|
+
linear-gradient(var(--color-primary), var(--color-primary)) top left / var(--frame-corner-thickness) var(--frame-corner-size) no-repeat,
|
|
1345
|
+
linear-gradient(var(--color-primary), var(--color-primary)) top right / var(--frame-corner-size) var(--frame-corner-thickness) no-repeat,
|
|
1346
|
+
linear-gradient(var(--color-primary), var(--color-primary)) top right / var(--frame-corner-thickness) var(--frame-corner-size) no-repeat,
|
|
1347
|
+
linear-gradient(var(--color-primary), var(--color-primary)) bottom left / var(--frame-corner-size) var(--frame-corner-thickness) no-repeat,
|
|
1348
|
+
linear-gradient(var(--color-primary), var(--color-primary)) bottom left / var(--frame-corner-thickness) var(--frame-corner-size) no-repeat,
|
|
1349
|
+
linear-gradient(var(--color-primary), var(--color-primary)) bottom right / var(--frame-corner-size) var(--frame-corner-thickness) no-repeat,
|
|
1350
|
+
linear-gradient(var(--color-primary), var(--color-primary)) bottom right / var(--frame-corner-thickness) var(--frame-corner-size) no-repeat;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
.ledger-poster img,
|
|
1354
|
+
.ledger-poster video {
|
|
1355
|
+
width: 100%;
|
|
1356
|
+
height: 100%;
|
|
1357
|
+
object-fit: cover;
|
|
1358
|
+
display: block;
|
|
1359
|
+
filter: saturate(0.92) contrast(1.02);
|
|
1360
|
+
transition: filter var(--motion-duration-base) var(--motion-ease-default), transform var(--motion-duration-slow) var(--motion-ease-default);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
.ledger-system-card:hover .ledger-poster img,
|
|
1364
|
+
.ledger-system-card:hover .ledger-poster video {
|
|
1365
|
+
filter: saturate(1.05) contrast(1);
|
|
1366
|
+
transform: scale(1.02);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1370
|
+
|
|
1371
|
+
.ledger-system-card:hover .ledger-poster img,
|
|
1372
|
+
.ledger-system-card:hover .ledger-poster video {
|
|
1373
|
+
transform: none;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
/* Project-specific meta furniture grafted onto the shared system card. */
|
|
1378
|
+
.material-symbols-outlined.ledger-system-icon {
|
|
1379
|
+
font-size: var(--type-card);
|
|
1380
|
+
line-height: 1;
|
|
1381
|
+
color: var(--color-secondary);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
.ledger-span {
|
|
1385
|
+
color: var(--color-on-surface-variant);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
/* ── Listing pagination ──
|
|
1389
|
+
The shared renderer used to hand these buttons a string of Tailwind
|
|
1390
|
+
utilities and no component, so three page stylesheets each restated the
|
|
1391
|
+
same metrics at whatever specificity it took to beat the utilities. One
|
|
1392
|
+
definition here; the pages keep only what is genuinely theirs. */
|
|
1393
|
+
.pagination-nav-btn {
|
|
1394
|
+
padding: var(--space-xs) var(--space-md);
|
|
1395
|
+
min-height: 2.5rem;
|
|
1396
|
+
background: var(--color-surface-card);
|
|
1397
|
+
border: 1.5px solid var(--border-ink);
|
|
1398
|
+
border-radius: var(--radius-card);
|
|
1399
|
+
color: var(--color-on-surface-variant);
|
|
1400
|
+
font-family: var(--font-label);
|
|
1401
|
+
font-size: var(--type-label-lg);
|
|
1402
|
+
letter-spacing: 0.04em;
|
|
1403
|
+
cursor: pointer;
|
|
1404
|
+
transition: color var(--motion-duration-fast) var(--motion-ease-default), border-color var(--motion-duration-fast) var(--motion-ease-default), background-color var(--motion-duration-fast) var(--motion-ease-default);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
.pagination-nav-btn:hover:not(:disabled) {
|
|
1408
|
+
color: var(--color-on-surface);
|
|
1409
|
+
border-color: var(--border-gentle-hover);
|
|
1410
|
+
background: var(--color-surface-high);
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
.pagination-nav-btn:focus-visible {
|
|
1414
|
+
outline: 2px solid var(--color-primary);
|
|
1415
|
+
outline-offset: 2px;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
.pagination-nav-btn:disabled {
|
|
1419
|
+
opacity: 0.4;
|
|
1420
|
+
cursor: default;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
/* Page count shares the Previous/Next label face; tabular nums keep "1 / 3"
|
|
1424
|
+
from jumping width as the page changes. */
|
|
1425
|
+
.pagination-count {
|
|
1426
|
+
font-family: var(--font-label);
|
|
1427
|
+
font-size: var(--type-label-lg);
|
|
1428
|
+
font-variant-numeric: tabular-nums;
|
|
1429
|
+
letter-spacing: 0.04em;
|
|
1430
|
+
color: var(--color-on-surface-variant);
|
|
1431
|
+
white-space: nowrap;
|
|
1432
|
+
}
|