pandorga 1.6.9 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -0
  3. data/DESIGN.md +9 -9
  4. data/_includes/content-runtime/30-includes.html +1 -1
  5. data/_includes/content-runtime/43-blocks.html +1 -1
  6. data/_includes/content-runtime/50-detail.html +5 -5
  7. data/_includes/content-runtime/60-fragments.html +1 -1
  8. data/_includes/page/network-graph.html +2 -2
  9. data/_includes/page/site-footer.html +2 -2
  10. data/_includes/theme/font-loader.html +2 -54
  11. data/_includes/theme/theme-vars-block.liquid +10 -18
  12. data/_layouts/articles.html +1 -1
  13. data/_layouts/projects.html +2 -2
  14. data/_layouts/resources.html +1 -1
  15. data/_layouts/shell.html +1 -1
  16. data/_layouts/text.html +4 -4
  17. data/_plugins/lib/writing_entry.rb +1 -1
  18. data/assets/css/article.css +3 -3
  19. data/assets/css/base.css +0 -79
  20. data/assets/css/global-link.css +0 -1
  21. data/assets/css/ledger/chrome.css +1 -1
  22. data/assets/css/ledger/cv.css +1 -1
  23. data/assets/css/ledger/detail.css +1 -1
  24. data/assets/css/ledger/footer.css +1 -1
  25. data/assets/css/ledger/home.css +13 -42
  26. data/assets/css/ledger/projects.css +2 -2
  27. data/assets/css/ledger/sources.css +3 -3
  28. data/assets/css/ledger/writing.css +1 -1
  29. data/examples/full/_config.yml +9 -0
  30. data/lib/pandorga/fonts.rb +235 -0
  31. data/lib/pandorga/jekyll/plugins/fonts.rb +17 -0
  32. data/lib/pandorga/jekyll/plugins/lib/writing_entry.rb +1 -1
  33. data/lib/pandorga/registry/schema.yml +21 -0
  34. data/lib/pandorga/version.rb +1 -1
  35. data/scripts/content/export-content-json.rb +1 -1
  36. data/scripts/generate-configuration-doc.rb +1 -1
  37. data/scripts/test/render-parity/load-runtime.mjs +1 -1
  38. data/scripts/test/test-css-structure.rb +20 -0
  39. data/scripts/test/test-discovery.rb +5 -0
  40. data/scripts/test/test-examples-build.rb +9 -0
  41. data/scripts/test/test-fonts.rb +91 -0
  42. data/scripts/test/test-listing-filter-contract.rb +1 -1
  43. data/scripts/test/test-shell-a11y.rb +21 -0
  44. data/scripts/validate.sh +1 -0
  45. metadata +4 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 110f9fdf3636db9ef496093f969e15ef4cd3322ee143c5efadb210fb5e4b98b9
4
- data.tar.gz: 6c5625b1cbdac652c0120d2b25e49a722c59941862cf91da7f44eff6f1ada004
3
+ metadata.gz: aa9e02cc789f512401bb4e9bdbc28c732dff0f3849bbf055d0cd546678cbdd1d
4
+ data.tar.gz: 8a17699ce008fa34e5114f9b4e8e643d0d754f7620ed3b0357aeb2cc9a4c201e
5
5
  SHA512:
6
- metadata.gz: 22f67174268b9edd80046d916e6d8394d4ce1df62e8802be7dc4eab8771479825c1ba8b3aa2f668ebef53bc95f116f840ddd300428362b938a19b9955d0914d8
7
- data.tar.gz: 85ce7605aae925b73d384a9ddbc0269b5b31bb58652b05b60617c5b784f8cfc2c68dc92cc3824ed07bf016f305aee36c5fba17a51c891ecce06cd58a0c3f94d0
6
+ metadata.gz: 7c6e5413b160b64e546f4aa7c80f1e45bb8c5716336593984e4f1f14e642ed444adba3dc5261d08d31257367060dab631777f744f6126c38df1c264e7259d139
7
+ data.tar.gz: 21588e084fe1f417a4f9fa55e532c8ebca01930ea95ad740ecafece15578df4c237461e0213a9328526b982847dcb4bff07d47f1a182a50a36a9fb5348f9cb74
data/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.7.1] — 2026-10-11
4
+
5
+ ### Fixed
6
+
7
+ - Decorative Material Symbols beside a label no longer expose the ligature
8
+ (`arrow_left_alt`, `shuffle`, a contact icon, a missing-media mark).
9
+ The public detail path and the private layout agree. Nine spans.
10
+ - The shell published two `og:image` properties. The second meta element
11
+ carried both `name="image"` and `property="og:image"`. `name="image"`
12
+ stays on its own element, so a page has one Open Graph image.
13
+
14
+ ### Changed
15
+
16
+ - Unused ledger selectors are gone. `assets/css/base.css` drops 2,041
17
+ bytes: `.page-title` (404), `.ledger-marker--primary` (616),
18
+ `.ledger-card--edge-*` (462), and `.ledger-section-rule` (559).
19
+ `assets/css/ledger/home.css` drops 577 bytes of rules that no band
20
+ uses: `.home-band--ruled` (310), `.home-index-shard--gamma` (163),
21
+ and `.home-index-note` (104). `assets/css/global-link.css` drops the
22
+ `.post-content` hover selector (23 bytes). None of them matched a
23
+ template, the runtime, or current content. 2,641 bytes in all.
24
+ - Comments that a font rename had rewritten, eating the letters inside
25
+ the word "behind", read "behind" again. 45 occurrences in 21 files.
26
+
27
+ No `pandorga:` config keys were added or renamed. Font roles and the
28
+ ledger fallbacks are unchanged.
29
+
30
+ ## [1.7.0] — 2026-10-10
31
+
32
+ ### Added
33
+
34
+ - A site can name its typefaces under `pandorga.fonts`. `title`, `body`,
35
+ and `mono` take a Google Fonts family, or a map with `family` and
36
+ optional `weights` and `italic`. `label` and `subtitle` are optional.
37
+ Omitted roles keep Cinzel Decorative, Newsreader, and Courier Prime.
38
+ The text faces still load after the first paint.
39
+
3
40
  ## [1.6.9] — 2026-10-10
4
41
 
5
42
  ### Fixed
data/DESIGN.md CHANGED
@@ -436,7 +436,7 @@ motion:
436
436
  mediaFilter: "0.4s ease"
437
437
  mediaTransform: ["0.6s ease", "0.7s ease"]
438
438
  lightbox: ["160ms ease-out", "180ms ease-out"]
439
- readingProgress: "scroll(root block) timeline, beNewsreader @supports"
439
+ readingProgress: "scroll(root block) timeline, behind @supports"
440
440
  reducedMotion: "5 rules — see Motion below"
441
441
  ---
442
442
 
@@ -568,7 +568,7 @@ mix — and on the article detail view.
568
568
 
569
569
  `renderPosterMedia` extends the rule to runtime failure: a thumbnail that
570
570
  404s removes its own `.ledger-poster` wrapper via an inline `onerror`, so a
571
- dead third-party image leaves no empty plate beNewsreader.
571
+ dead third-party image leaves no empty plate behind.
572
572
 
573
573
  ### Where the library is not yet the only source
574
574
 
@@ -972,7 +972,7 @@ Letter-spacing in use, by frequency: `0.1em` (12), `0.08em` (10), `0.12em` (7),
972
972
  table of contents appears in that sidebar only from `1024px`; below it, the
973
973
  same list is mounted as a `<details>` disclosure instead. A 2px `primary`
974
974
  reading-progress bar is drawn on a pseudo-element of the detail section with
975
- `animation-timeline: scroll(root block)`, beNewsreader an `@supports` guard, so
975
+ `animation-timeline: scroll(root block)`, behind an `@supports` guard, so
976
976
  engines without scroll-driven animation simply get no bar.
977
977
  * **CV.** A `panelContrast` dossier band with two shards, then five
978
978
  `.cv-register` sections and a timeline band. Each register is a 3/9 grid at
@@ -1035,7 +1035,7 @@ linear` on the reading-progress bar, where the duration is a placeholder that
1035
1035
  `animation-timeline: scroll(root block)` overrides.
1036
1036
 
1037
1037
  **Scroll-driven animation** is new: the reading-progress bar in
1038
- `ledger/detail.css` uses `animation-timeline: scroll(root block)` beNewsreader an
1038
+ `ledger/detail.css` uses `animation-timeline: scroll(root block)` behind an
1039
1039
  `@supports` guard, with a 1ms `linear` animation scaling a fixed 2px `primary`
1040
1040
  bar from 0 to 1.
1041
1041
 
@@ -1117,7 +1117,7 @@ everything else, so the query catches them too.
1117
1117
  carries both class names, so any media query that repositions the pair has to
1118
1118
  restate the base rule's `auto` offsets on the beta: leave all four set and
1119
1119
  the browser resolves to top and left, and the pair collapses into one corner.
1120
- And a shard beNewsreader an opaque plate is a shard nobody sees, which is why the
1120
+ And a shard behind an opaque plate is a shard nobody sees, which is why the
1121
1121
  home featured pair goes around its poster rather than across the card.
1122
1122
 
1123
1123
  On the writing index the shard was for a long time written to stand down
@@ -1152,7 +1152,7 @@ everything else, so the query catches them too.
1152
1152
  `overflow: hidden` and lifts its content clear with `position: relative;
1153
1153
  z-index: 1`. The filter decks and `.rascunhos-pagination` are plain
1154
1154
  `panelContrast` strips. `.home-hero-plate` and `.home-graph-plate` sit a rung
1155
- lower, on `surfaceCard`, beNewsreader a `gentleSubtle` hairline.
1155
+ lower, on `surfaceCard`, behind a `gentleSubtle` hairline.
1156
1156
 
1157
1157
  ### Filter deck
1158
1158
 
@@ -1311,11 +1311,11 @@ as enforced by [`_plugins/lib/tag_validator.rb`](_plugins/lib/tag_validator.rb):
1311
1311
  mixes `--color-primary` at 40% into the border — the reference mockup's
1312
1312
  "hairline transitions to warm antique brass at 40%", made accent-aware).
1313
1313
  One sanctioned exception, added 2026-09: a wide, very low opacity accent
1314
- pool sits *beNewsreader* a band. It is a backdrop, not a content effect — no card,
1314
+ pool sits *behind* a band. It is a backdrop, not a content effect — no card,
1315
1315
  panel or row gains a glow, and the pool is blurred past any visible edge so
1316
1316
  it reads as ambient light in the room rather than as a shape. Three of them
1317
1317
  ship: the home hero at 7% (`.home-band--hero::before`), the home Sources and
1318
- Network band at 5% (`.home-band--index::before`), and one beNewsreader the Network
1318
+ Network band at 5% (`.home-band--index::before`), and one behind the Network
1319
1319
  of Ideas graph at 7%, whose centre and width are read off the settled D3
1320
1320
  layout rather than fixed (`assets/css/ledger/network.css`).
1321
1321
 
@@ -1418,7 +1418,7 @@ them.
1418
1418
  buttons, `shadow-sm`, and a `bg-gradient-to-b from-primary
1419
1419
  to-primary-container` fill — three of the system's rules broken on one
1420
1420
  page.
1421
- 11. **Navigation is still fully hidden.** A rich, deep site sits beNewsreader one
1421
+ 11. **Navigation is still fully hidden.** A rich, deep site sits behind one
1422
1422
  hamburger at every breakpoint, so its structure is invisible on arrival.
1423
1423
  This survived the redesign untouched.
1424
1424
  12. **Tailwind runs from the Play CDN in production**
@@ -194,7 +194,7 @@
194
194
  }
195
195
 
196
196
  function placeholderHtml() {
197
- return '<div class="w-full h-full flex items-center justify-center opacity-30"><span class="material-symbols-outlined text-5xl">article</span></div>';
197
+ return '<div class="w-full h-full flex items-center justify-center opacity-30"><span class="material-symbols-outlined text-5xl" aria-hidden="true">article</span></div>';
198
198
  }
199
199
 
200
200
  function normalizeDataPath(path, fallbackPrefix) {
@@ -317,7 +317,7 @@
317
317
  '<img src="', escapeAttr(url), '" alt="', escapeAttr(alt || ''),
318
318
  '" loading="lazy" decoding="async"',
319
319
  // Thumbnails are frequently hotlinked to third-party hosts. When one
320
- // dies, remove the frame rather than leaving an empty plate beNewsreader.
320
+ // dies, remove the frame rather than leaving an empty plate behind.
321
321
  ' onerror="var p=this.closest(\'.ledger-poster\'); if (p) p.remove();"',
322
322
  '>',
323
323
  '</div>'
@@ -12,7 +12,7 @@
12
12
  /* The page accent, not a fixed triad role: this glyph marks a
13
13
  project, and the projects page reads blue. text-secondary pinned
14
14
  it green there regardless. */
15
- parts.push('<span class="material-symbols-outlined text-5xl md:text-6xl text-primary mb-4 block leading-none">' + escapeHtml(fm.icon) + '</span>');
15
+ parts.push('<span class="material-symbols-outlined text-5xl md:text-6xl text-primary mb-4 block leading-none" aria-hidden="true">' + escapeHtml(fm.icon) + '</span>');
16
16
  }
17
17
  parts.push('<h1 class="text-title">' + escapeHtml(title) + '</h1>');
18
18
  if (fm.layout === 'post' && fm.thumbnail) {
@@ -416,7 +416,7 @@
416
416
  '<a href="', escapeAttr(options.backUrl || '#'), '" ',
417
417
  'class="back-link text-sm font-label inline-flex items-center gap-2 no-underline hover:underline text-primary mb-8 block" ',
418
418
  'id="', escapeAttr(options.id || 'runtime-back-link'), '">',
419
- '<span class="material-symbols-outlined text-sm">arrow_left_alt</span>',
419
+ '<span class="material-symbols-outlined text-sm" aria-hidden="true">arrow_left_alt</span>',
420
420
  escapeHtml(label),
421
421
  '</a>'
422
422
  ].join('');
@@ -429,7 +429,7 @@
429
429
  if (options.previousUrl) {
430
430
  parts.push(
431
431
  '<a href="', escapeAttr(options.previousUrl), '">',
432
- '<span class="material-symbols-outlined text-sm">arrow_left_alt</span>',
432
+ '<span class="material-symbols-outlined text-sm" aria-hidden="true">arrow_left_alt</span>',
433
433
  escapeHtml(translate('previous_text', lang)),
434
434
  '</a>'
435
435
  );
@@ -441,14 +441,14 @@
441
441
  parts.push(
442
442
  '<a href="', escapeAttr(options.nextUrl), '">',
443
443
  escapeHtml(translate('next_text', lang)),
444
- '<span class="material-symbols-outlined text-sm">arrow_right_alt</span>',
444
+ '<span class="material-symbols-outlined text-sm" aria-hidden="true">arrow_right_alt</span>',
445
445
  '</a>'
446
446
  );
447
447
  } else if (options.showRandom) {
448
448
  parts.push(
449
449
  '<a href="#" id="random-post-link">',
450
450
  escapeHtml(translate('random_text', lang)),
451
- '<span class="material-symbols-outlined text-sm">shuffle</span>',
451
+ '<span class="material-symbols-outlined text-sm" aria-hidden="true">shuffle</span>',
452
452
  '</a>'
453
453
  );
454
454
  } else {
@@ -461,7 +461,7 @@
461
461
  '<a href="', escapeAttr(href), '"', targetRel,
462
462
  ' class="group flex h-full items-center gap-3 no-underline">',
463
463
  '<div class="w-12 h-12 rounded-[9999px] bg-surface-container-highest flex items-center justify-center group-hover:bg-primary-container transition-colors">',
464
- '<span class="material-symbols-outlined text-on-surface group-hover:text-on-primary-container">',
464
+ '<span class="material-symbols-outlined text-on-surface group-hover:text-on-primary-container" aria-hidden="true">',
465
465
  escapeHtml(contact.icon || ''),
466
466
  '</span></div><div>',
467
467
  '<span class="block marginalia text-tertiary">',
@@ -723,7 +723,7 @@
723
723
  settled layout in placePool() below.
724
724
 
725
725
  Drawn inside the SVG because the canvas rectangle above is opaque
726
- and would cover anything beNewsreader it, and between that rectangle and
726
+ and would cover anything behind it, and between that rectangle and
727
727
  the zoom group so it stays a backdrop: every edge, mark and label is
728
728
  painted over it. It sits in screen space rather than in graph space,
729
729
  like the hover card, so the blur is the same 60px at every
@@ -1182,7 +1182,7 @@
1182
1182
  }
1183
1183
 
1184
1184
  /* Screen space, like the hover card: the pools track their clusters
1185
- under panning and scale with the zoom, but the blur beNewsreader them stays
1185
+ under panning and scale with the zoom, but the blur behind them stays
1186
1186
  the same 60px at every magnification instead of dissolving as the
1187
1187
  reader pulls back. */
1188
1188
  function placePool() {
@@ -5,7 +5,7 @@
5
5
  Five of the six mockups draw the same footer: a wide masthead, a colophon
6
6
  with a typeset line, an index of external repositories, and a mono imprint
7
7
  carrying coordinates and an edition number. Almost none of that has a field
8
- beNewsreader it. What is left after the hard rule is three blocks over one
8
+ behind it. What is left after the hard rule is three blocks over one
9
9
  imprint, and every one of them reads a value that already exists:
10
10
 
11
11
  Masthead site.author + the home hero catch phrase
@@ -14,7 +14,7 @@
14
14
  Imprint site.author + site.content_license
15
15
 
16
16
  A colophon block naming the four type roles was added and then removed at
17
- the owner's request: the faces did have a field beNewsreader them, but the line
17
+ the owner's request: the faces did have a field behind them, but the line
18
18
  was not wanted in the footer. The mockup's edition number, paper stock and
19
19
  press note have no field at all and were never drawn.
20
20
 
@@ -1,62 +1,10 @@
1
- {% include theme/theme-context.liquid %}
2
- {% assign font_title = sf.title | default: "Lora" %}
3
- {% assign font_subtitle = sf.subtitle | default: "Lato" %}
4
- {% assign font_label = sf.label | default: "Ysabeau SC" %}
5
- {% assign font_body = sf.body | default: "Newsreader" %}
6
- {% assign font_mono = sf.mono %}
7
- {% assign font_code = sf.code %}
8
- {% assign font_cv_body = sf.cv_body %}
9
- {% assign title_slug = font_title | replace: ' ', '+' %}
10
- {% assign subtitle_slug = font_subtitle | replace: ' ', '+' %}
11
- {% assign label_slug = font_label | replace: ' ', '+' %}
12
- {% assign body_slug = font_body | replace: ' ', '+' %}
13
- {% assign mono_slug = font_mono | replace: ' ', '+' %}
14
- {% assign code_slug = font_code | replace: ' ', '+' %}
15
- {% assign cv_body_slug = font_cv_body | replace: ' ', '+' %}
16
- {% assign title_spec = sf.google_title_spec | default: "ital,wght@0,400;0,500;0,700;1,400;1,500" %}
17
- {% assign subtitle_spec = sf.google_subtitle_spec | default: "ital,wght@0,300;0,400;0,500;0,700;1,400;1,500" %}
18
- {% assign label_spec = sf.google_label_spec | default: "ital,wght@0,400;0,500;0,700;1,400;1,500" %}
19
- {% assign body_spec = sf.google_body_spec | default: "ital,wght@0,300;0,400;0,500;0,700;1,400;1,500;1,700" %}
20
- {% assign mono_spec = sf.google_mono_spec | default: "wght@400;500;700" %}
21
- {% assign code_spec = sf.google_code_spec | default: "wght@300;400;500;600;700" %}
22
- {% assign cv_body_spec = sf.google_cv_body_spec | default: "wght@300;400;500;600;700" %}
23
- {% comment %}
24
- Build the Google Fonts URL without repeating a family. Title is always
25
- requested. Body / label / subtitle / mono / code append only when their
26
- family name differs from ones already on the URL.
27
- {% endcomment %}
28
- {% assign fonts_href = "https://fonts.googleapis.com/css2?family=" | append: title_slug | append: ":" | append: title_spec %}
29
- {% unless font_label == font_title %}
30
- {% assign fonts_href = fonts_href | append: "&family=" | append: label_slug | append: ":" | append: label_spec %}
31
- {% endunless %}
32
- {% unless font_body == font_title or font_body == font_label %}
33
- {% assign fonts_href = fonts_href | append: "&family=" | append: body_slug | append: ":" | append: body_spec %}
34
- {% endunless %}
35
- {% if font_mono and font_mono != '' %}
36
- {% unless font_mono == font_title or font_mono == font_label or font_mono == font_body %}
37
- {% assign fonts_href = fonts_href | append: "&family=" | append: mono_slug | append: ":" | append: mono_spec %}
38
- {% endunless %}
39
- {% endif %}
40
- {% if font_code and font_code != '' %}
41
- {% unless font_code == font_title or font_code == font_label or font_code == font_body or font_code == font_mono %}
42
- {% assign fonts_href = fonts_href | append: "&family=" | append: code_slug | append: ":" | append: code_spec %}
43
- {% endunless %}
44
- {% endif %}
45
- {% if font_subtitle != font_body and font_subtitle != font_label and font_subtitle != font_title %}
46
- {% assign fonts_href = fonts_href | append: "&family=" | append: subtitle_slug | append: ":" | append: subtitle_spec %}
47
- {% endif %}
48
- {% if font_cv_body and font_cv_body != '' %}
49
- {% unless font_cv_body == font_title or font_cv_body == font_label or font_cv_body == font_body or font_cv_body == font_subtitle %}
50
- {% assign fonts_href = fonts_href | append: "&family=" | append: cv_body_slug | append: ":" | append: cv_body_spec %}
51
- {% endunless %}
52
- {% endif %}
53
- {% assign fonts_href = fonts_href | append: "&display=swap" %}
54
1
  {% comment %}
55
2
  Not a render-blocking head link, and not the load event. A blocking
56
3
  sheet holds first paint on the font host. The load event holds the hero
57
4
  summary until images finish. The shell inserts this sheet after the
58
- first paint. display=swap stays.
5
+ first paint. The URL always ends in &display=swap.
59
6
  {% endcomment %}
7
+ {% assign fonts_href = site | pandorga_font_stylesheet %}
60
8
  <script>
61
9
  window.__pandorgaFontsHref = {{ fonts_href | jsonify }};
62
10
  </script>
@@ -1,6 +1,7 @@
1
1
  {% comment %}
2
2
  Emits CSS custom properties for one palette. Expects `theme_name` (e.g. light, dark).
3
- Colour roles come from _data/themes/<name>.yml; fonts / motion / geometry from shared.yml.
3
+ Colour roles come from _data/themes/<name>.yml. Type stacks come from
4
+ pandorga.fonts, then shared.yml. Motion and geometry come from shared.yml.
4
5
  {% endcomment %}
5
6
  {% assign theme_profile = site.data.themes[theme_name] %}
6
7
  {% if theme_profile == nil %}
@@ -17,34 +18,25 @@
17
18
  {% assign color_primary = tc.primary | default: "#1c5a7a" %}
18
19
  {% assign color_secondary = tc.secondary | default: "#287445" %}
19
20
  {% assign color_tertiary = tc.tertiary | default: "#746f30" %}
20
- {% assign font_title = sf.title | default: "Lora" %}
21
- {% assign font_subtitle = sf.subtitle | default: "Lato" %}
22
- {% assign font_label = sf.label | default: "Ysabeau SC" %}
23
- {% assign font_body = sf.body | default: "Newsreader" %}
24
- {% assign font_cv_body = sf.cv_body | default: "Newsreader" %}
25
21
  {% assign font_cv_body_weight = sf.cv_body_weight | default: 400 %}
26
- {% assign font_title_fallback = sf.title_fallback | default: "serif" %}
27
- {% assign font_subtitle_fallback = sf.subtitle_fallback | default: "serif" %}
28
- {% assign font_label_fallback = sf.label_fallback | default: "serif" %}
29
- {% assign font_body_fallback = sf.body_fallback | default: "Georgia, serif" %}
30
22
  {% assign bg_secondary = tn.background_secondary | default: tn.surface_container_low | default: "#f5f5f5" %}
31
23
  {% assign surface_value = tn.surface | default: "#ffffff" %}
32
24
  {% assign surface_variant_value = tn.surface_variant | default: "#e2e2e2" %}
33
25
  {% if theme_name == "light" %}:root,{% endif %}
34
26
  [data-theme="{{ theme_name }}"] {
35
- --font-title: '{{ font_title }}', {{ font_title_fallback }};
36
- --font-subtitle: '{{ font_subtitle }}', {{ font_subtitle_fallback }};
37
- --font-label: '{{ font_label }}', {{ font_label_fallback }};
38
- --font-body: '{{ font_body }}', {{ font_body_fallback }};
39
- --font-cv-body: '{{ font_cv_body }}', {{ font_body_fallback }};
27
+ --font-title: {{ site | pandorga_font_stack: "title" }};
28
+ --font-subtitle: {{ site | pandorga_font_stack: "subtitle" }};
29
+ --font-label: {{ site | pandorga_font_stack: "label" }};
30
+ --font-body: {{ site | pandorga_font_stack: "body" }};
31
+ --font-cv-body: {{ site | pandorga_font_stack: "cv_body" }};
40
32
  --font-cv-body-weight: {{ font_cv_body_weight }};
41
- --font-serif: '{{ font_title }}', {{ font_title_fallback }};
33
+ --font-serif: {{ site | pandorga_font_stack: "title" }};
42
34
  /* Content titles share the body face at 600 — Cinzel Decorative is
43
35
  reserved for page/display titles. */
44
36
  --font-content-title: var(--font-body);
45
37
  --font-sans: var(--font-body);
46
- --font-mono: {% if sf.mono and sf.mono != '' %}'{{ sf.mono }}', {% endif %}ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace;
47
- --font-code: {% if sf.code and sf.code != '' %}'{{ sf.code }}', {% elsif sf.mono and sf.mono != '' %}'{{ sf.mono }}', {% endif %}ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace;
38
+ --font-mono: {{ site | pandorga_font_stack: "mono" }};
39
+ --font-code: {{ site | pandorga_font_stack: "code" }};
48
40
 
49
41
  /* Motion. Named for what they are for: state change, poster filter
50
42
  warm-up, poster/plate scale. Shared across palettes. */
@@ -327,7 +327,7 @@ layout: shell
327
327
  /* Lead panel. Binds title, date, revision, tags, description and the
328
328
  * thumbnail — the same strip as the rows below it, language pill included
329
329
  * in the omission. The mockup's topology figure, its DOI/arXiv line and its
330
- * "Critical Notes" list have no field beNewsreader them, so none is rendered.
330
+ * "Critical Notes" list have no field behind them, so none is rendered.
331
331
  *
332
332
  * The panel reads down the left and stops on the action: kicker, ledger
333
333
  * line, title, summary, tags, then "Read article" — with the plate holding
@@ -102,7 +102,7 @@ layout: shell
102
102
 
103
103
  /* ── Field readers ──────────────────────────────────────────────────
104
104
  Each returns '' when its field is unset, so an absent field drops the
105
- element rather than leaving a placeholder beNewsreader. */
105
+ element rather than leaving a placeholder behind. */
106
106
 
107
107
  function projectName(item) {
108
108
  var fm = item.front_matter || {};
@@ -297,7 +297,7 @@ layout: shell
297
297
 
298
298
  /* Closing ledger. One row per repository that front matter actually
299
299
  names — five of seven today. The mockup's licence stamp, spec download
300
- and commit hashes have nothing beNewsreader them and are not drawn. */
300
+ and commit hashes have nothing behind them and are not drawn. */
301
301
  function renderRepositories(items) {
302
302
  var rows = items.filter(function (item) {
303
303
  return (item.front_matter || {}).github;
@@ -173,7 +173,7 @@ layout: shell
173
173
 
174
174
  /* The four facets a source is filed under: what it is, how it is
175
175
  classified, what language it speaks, and where it speaks from. Each
176
- renders only when the field beNewsreader it is populated, so a source missing
176
+ renders only when the field behind it is populated, so a source missing
177
177
  a category shows three chips and no gap where the fourth would be. */
178
178
  function renderFacets(fm, medium) {
179
179
  var chips = [
data/_layouts/shell.html CHANGED
@@ -47,7 +47,7 @@
47
47
  {% assign og_image_type = "image/png" %}
48
48
  {% endif %}
49
49
  <meta property="og:image" content="{{ og_image_url }}" />
50
- <meta name="image" property="og:image" content="{{ og_image_url }}" />
50
+ <meta name="image" content="{{ og_image_url }}" />
51
51
  <meta property="og:image:width" content="1200" />
52
52
  <meta property="og:image:height" content="630" />
53
53
  <meta property="og:image:type" content="{{ og_image_type }}" />
data/_layouts/text.html CHANGED
@@ -69,7 +69,7 @@ layout: shell
69
69
  {% if page.collection == "posts" or page.collection == "articles" or page.layout == "product" or page.layout == "project" %}
70
70
  <div class="article-sidebar-heading mb-8">
71
71
  {% if page.icon %}
72
- <span class="material-symbols-outlined text-5xl md:text-6xl text-secondary mb-4 block leading-none">{{ page.icon }}</span>
72
+ <span class="material-symbols-outlined text-5xl md:text-6xl text-secondary mb-4 block leading-none" aria-hidden="true">{{ page.icon }}</span>
73
73
  {% endif %}
74
74
  <h1 class="text-title">{{ page.title }}</h1>
75
75
  {% if page.collection == "posts" and page.thumbnail %}
@@ -202,7 +202,7 @@ layout: shell
202
202
  <nav class="text-page-nav text-reading-column w-full" aria-label="Article navigation">
203
203
  {% if page.previous %}
204
204
  <a href="{{ page.previous.url }}">
205
- <span class="material-symbols-outlined text-sm">arrow_left_alt</span>
205
+ <span class="material-symbols-outlined text-sm" aria-hidden="true">arrow_left_alt</span>
206
206
  {{ t.previous_text[lang] }}
207
207
  </a>
208
208
  {% else %}
@@ -211,12 +211,12 @@ layout: shell
211
211
  {% if page.next %}
212
212
  <a href="{{ page.next.url }}">
213
213
  {{ t.next_text[lang] }}
214
- <span class="material-symbols-outlined text-sm">arrow_right_alt</span>
214
+ <span class="material-symbols-outlined text-sm" aria-hidden="true">arrow_right_alt</span>
215
215
  </a>
216
216
  {% elsif page.collection == "posts" %}
217
217
  <a href="#" id="random-post-link">
218
218
  {{ t.random_text[lang] }}
219
- <span class="material-symbols-outlined text-sm">shuffle</span>
219
+ <span class="material-symbols-outlined text-sm" aria-hidden="true">shuffle</span>
220
220
  </a>
221
221
  {% else %}
222
222
  <span></span>
@@ -15,7 +15,7 @@ require_relative "text_excerpt"
15
15
  # (standalone, for scripts/content/export-content-json.rb). They differ only in how
16
16
  # they derive the repo-relative path used for Git chronology, so each passes
17
17
  # its own. Everything downstream lives here, because a field added or a bug
18
- # fixed in one copy used to leave the other silently beNewsreader.
18
+ # fixed in one copy used to leave the other silently behind.
19
19
  module WritingEntry
20
20
  XCITE_RE = /\{%-?\s*include\s+xcite\.html\s+key=["']([^"']+)["'][^%]*-?%\}/
21
21
 
@@ -186,7 +186,7 @@
186
186
  justify-content: center;
187
187
  box-sizing: border-box;
188
188
  padding: 0.35rem 0.55rem;
189
- /* A fixed dark plate beNewsreader light-on-transparent marks. It must stay dark in
189
+ /* A fixed dark plate behind light-on-transparent marks. It must stay dark in
190
190
  both themes, so no surface token fits: every one of them flips. */
191
191
  background: #111;
192
192
  }
@@ -349,7 +349,7 @@
349
349
 
350
350
  The projects page had this to itself until now; these rules are the ones it
351
351
  proved, moved up so the treatment is the site's rather than one page's. What
352
- stayed beNewsreader in ledger/projects.css is the *other* thing that page does —
352
+ stayed behind in ledger/projects.css is the *other* thing that page does —
353
353
  letting media out of the reading measure — which is a separate decision.
354
354
 
355
355
  `.plotly-figure` and the videos both carry the `figure` element, so one
@@ -436,7 +436,7 @@ body.figure-lightbox-open {
436
436
  .figure-lightbox-backdrop {
437
437
  position: absolute;
438
438
  inset: 0;
439
- /* Near-opaque black so the article beNewsreader never reads as “another page”. */
439
+ /* Near-opaque black so the article behind never reads as “another page”. */
440
440
  background: rgba(0, 0, 0, 0.85);
441
441
  cursor: zoom-out;
442
442
  animation: figure-lightbox-fade-in var(--motion-duration-fast) var(--motion-ease-out);
data/assets/css/base.css CHANGED
@@ -208,24 +208,6 @@ html body {
208
208
  letter-spacing: 0.05em;
209
209
  }
210
210
 
211
- /* ── Display title scale (home hero, 404) ── */
212
- .page-title {
213
- font-family: var(--font-title);
214
- font-size: var(--type-display);
215
- font-weight: var(--weight-display);
216
- line-height: 1.05;
217
- color: var(--color-on-surface);
218
- letter-spacing: normal;
219
- font-synthesis: none;
220
- font-optical-sizing: auto;
221
- }
222
-
223
- @media (min-width: 768px) {
224
- .page-title {
225
- font-size: var(--type-display-lg);
226
- }
227
- }
228
-
229
211
  /* Listing page headers (CV, Articles, Portfolio, Rascunhos, …) */
230
212
  .page-header-title {
231
213
  font-family: var(--font-title);
@@ -345,22 +327,6 @@ html body {
345
327
  color-mix(in srgb, var(--color-tertiary) 2%, transparent) 98%);
346
328
  }
347
329
 
348
- .ledger-marker--primary {
349
- background: linear-gradient(104deg,
350
- transparent 0.9%,
351
- color-mix(in srgb, var(--color-primary) 16%, transparent) 2.4%,
352
- color-mix(in srgb, var(--color-primary) 12%, transparent) 58%,
353
- color-mix(in srgb, var(--color-primary) 4%, transparent) 98%);
354
- }
355
-
356
- [data-theme="dark"] .ledger-marker--primary {
357
- background: linear-gradient(104deg,
358
- transparent 0.9%,
359
- color-mix(in srgb, var(--color-primary) 9%, transparent) 2.4%,
360
- color-mix(in srgb, var(--color-primary) 6%, transparent) 58%,
361
- color-mix(in srgb, var(--color-primary) 2%, transparent) 98%);
362
- }
363
-
364
330
  /* ── Page-level accent ────────────────────────────────────────────────
365
331
  The shared triad (theme/theme-vars-block.liquid) declares one primary per
366
332
  theme, but each section of the site reads in its own hue: Projects and
@@ -1175,25 +1141,6 @@ html[data-theme="light"] .ledger-system-card {
1175
1141
  border-color: var(--border-ink);
1176
1142
  }
1177
1143
 
1178
- /* Triad top hairline — reusable edge accent (home grids + any listing). */
1179
- .ledger-card--edge-primary {
1180
- border-top-width: 2px;
1181
- border-top-style: solid;
1182
- border-top-color: var(--color-primary);
1183
- }
1184
-
1185
- .ledger-card--edge-secondary {
1186
- border-top-width: 2px;
1187
- border-top-style: solid;
1188
- border-top-color: var(--color-secondary);
1189
- }
1190
-
1191
- .ledger-card--edge-tertiary {
1192
- border-top-width: 2px;
1193
- border-top-style: solid;
1194
- border-top-color: var(--color-tertiary);
1195
- }
1196
-
1197
1144
  /* Drafting corner brackets — four L-corners, no labels. Pure decoration.
1198
1145
  Outline ink (not primary): the corners are drafting marks on the plate,
1199
1146
  same register as `.ledger-frame-crosshairs`, not a brand accent. */
@@ -1234,32 +1181,6 @@ html[data-theme="light"] .ledger-system-card {
1234
1181
  linear-gradient(var(--color-outline), var(--color-outline)) bottom right / var(--cross-thick) var(--cross-arm) no-repeat;
1235
1182
  }
1236
1183
 
1237
- /* Lithography section divider — double hairline with a short primary tick. */
1238
- .ledger-section-rule {
1239
- display: block;
1240
- height: 0;
1241
- margin: 0 0 var(--space-xl);
1242
- border: 0;
1243
- border-top: 1px solid var(--border-gentle-divider);
1244
- box-shadow: 0 3px 0 -2px var(--border-gentle-faint);
1245
- position: relative;
1246
- }
1247
-
1248
- .ledger-section-rule::after {
1249
- content: "";
1250
- position: absolute;
1251
- left: 0;
1252
- top: -1.5px;
1253
- width: 2.5rem;
1254
- height: 2px;
1255
- background: var(--color-primary);
1256
- }
1257
-
1258
- .ledger-section-rule--dashed {
1259
- border-top-style: dashed;
1260
- box-shadow: none;
1261
- }
1262
-
1263
1184
  /* Hover lifts the card one rung and warms its hairline toward the page's
1264
1185
  accent. The reference mockup states this as "the line transitions to
1265
1186
  subtle charcoal slate or warm antique brass at 40% opacity" — the accent
@@ -22,7 +22,6 @@ a {
22
22
  * titles, plus the chip exceptions that were being added one class at a time
23
23
  * as each new piece of card chrome turned up underlined. */
24
24
  p a:hover,
25
- .post-content a:hover,
26
25
  .article-content a:hover,
27
26
  .description a:hover {
28
27
  color: var(--color-link-hover);
@@ -2,7 +2,7 @@
2
2
  belong in assets/css/base.css under "Architectural Ledger blocks".
3
3
 
4
4
  The mockups put the sections in a flat, always-visible rail rather than
5
- beNewsreader a button. The links still come from _data/nav.yml, so the site keeps
5
+ behind a button. The links still come from _data/nav.yml, so the site keeps
6
6
  one source of navigation; only the presentation changes. The Explore menu
7
7
  stays for narrow viewports, where six links do not fit. */
8
8
 
@@ -347,7 +347,7 @@ html[data-theme="light"] .cv-product {
347
347
 
348
348
  /* A place is prose, not notation, so it reads in the body face rather than
349
349
  mono — and on the same left edge as every other line of the card. The pin
350
- icon and the indent it sat beNewsreader said nothing the word "France" does not
350
+ icon and the indent it sat behind said nothing the word "France" does not
351
351
  already say, and cost the entry its one straight margin. */
352
352
  .cv-entry-place {
353
353
  color: var(--color-on-surface-variant);
@@ -14,7 +14,7 @@
14
14
  to beat one are written with two compound parts on purpose. */
15
15
 
16
16
  /* ══ Reading progress ══
17
- Pure UI state, no field beNewsreader it, and no DOM hook to hang it on — the
17
+ Pure UI state, no field behind it, and no DOM hook to hang it on — the
18
18
  runtime owns the detail markup. A scroll-driven animation on a pseudo of
19
19
  the detail section gives the mockup's indicator with zero markup, and the
20
20
  @supports guard means older engines simply get no bar.
@@ -230,7 +230,7 @@
230
230
  }
231
231
 
232
232
  /* A middot between items rather than a separator element, so an absent
233
- license leaves no orphaned punctuation beNewsreader. */
233
+ license leaves no orphaned punctuation behind. */
234
234
  .site-footer-imprint-item+.site-footer-imprint-item::before {
235
235
  content: "·";
236
236
  margin-right: var(--space-md);