pandorga 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -0
  3. data/README.md +1 -1
  4. data/_includes/chord-tab.html +7 -7
  5. data/_includes/content-runtime/41-listing-helpers.html +7 -0
  6. data/_includes/content-runtime/43-blocks.html +4 -3
  7. data/_includes/content-runtime/60-fragments.html +77 -11
  8. data/_includes/page/site-footer.html +3 -2
  9. data/_includes/theme/critical-preload.html +35 -0
  10. data/_includes/theme/json-ld.html +35 -0
  11. data/_includes/transcription.html +5 -5
  12. data/_layouts/articles.html +8 -2
  13. data/_layouts/cv.html +20 -14
  14. data/_layouts/home.html +23 -3
  15. data/_layouts/shell.html +53 -12
  16. data/assets/css/ledger/chrome.css +23 -0
  17. data/assets/css/ledger/cv.css +13 -0
  18. data/assets/css/ledger/home.css +27 -20
  19. data/assets/css/ledger/writing.css +27 -3
  20. data/assets/css/tailwind.css +3 -0
  21. data/assets/tailwind-play.js +2 -1
  22. data/lib/pandorga/commands/doctor.rb +3 -1
  23. data/lib/pandorga/commands/new.rb +1 -1
  24. data/lib/pandorga/discovery.rb +78 -0
  25. data/lib/pandorga/jekyll/plugins/discovery.rb +32 -0
  26. data/lib/pandorga/version.rb +1 -1
  27. data/scripts/cdn-allowlist.json +2 -5
  28. data/scripts/tailwind/build.sh +19 -0
  29. data/scripts/tailwind/input.css +12 -0
  30. data/scripts/tailwind/tailwind.config.js +102 -0
  31. data/scripts/test/render-parity/blocks/project-inventory-row.json +3 -2
  32. data/scripts/test/test-css-delivery.rb +32 -0
  33. data/scripts/test/test-css-structure.rb +8 -0
  34. data/scripts/test/test-discovery.rb +46 -0
  35. data/scripts/test/test-doctor.rb +38 -0
  36. data/scripts/test/test-examples-build.rb +15 -0
  37. data/scripts/test/test-hero-portraits.mjs +1 -1
  38. data/scripts/test/test-hydrate-reserve.rb +64 -0
  39. data/scripts/test/test-new.rb +11 -0
  40. data/scripts/test/test-shell-a11y.rb +30 -0
  41. data/scripts/test/test-template-packages.rb +8 -0
  42. data/scripts/test/test-validate-allowlist.rb +29 -0
  43. data/scripts/validate.sh +7 -1
  44. metadata +14 -2
  45. data/assets/css/cv.css +0 -13
data/_layouts/shell.html CHANGED
@@ -13,6 +13,7 @@
13
13
  {% assign canonical_path = page.canonical_url | default: page.url %}
14
14
  <title>{{ meta_title | strip | escape_once }}</title>
15
15
  <meta name="description" content="{{ meta_description | escape_once }}" />
16
+ {% include theme/json-ld.html %}
16
17
  <!-- Open Graph / Social Sharing -->
17
18
  {% assign og_type = page.og_type | default: "website" %}
18
19
  <meta property="og:type" content="{{ og_type }}" />
@@ -57,17 +58,54 @@
57
58
  <meta name="twitter:image" content="{{ og_image_url }}" />
58
59
  <!-- Canonical URL -->
59
60
  <link rel="canonical" href="{{ canonical_path | absolute_url }}" />
61
+ {% include theme/critical-preload.html %}
60
62
  <link rel="stylesheet" href="/assets/css/base.css" />
61
- <link rel="stylesheet" href="/assets/css/article.css" />
62
- {% include theme/theme-vars.html %}
63
- <link rel="stylesheet" href="/assets/css/cv.css" />
64
63
  <link rel="stylesheet" href="/assets/css/global-link.css" />
65
- {%- comment -%} Per-page ledger styles. One file per page so page work stays
66
- independent; shared component rules live in base.css. {%- endcomment -%}
67
- {%- for sheet in site.static_files -%}
68
- {%- if sheet.path contains "/assets/css/ledger/" -%}
69
- <link rel="stylesheet" href="{{ sheet.path }}" />
70
- {%- endif -%}
64
+ {% include theme/theme-vars.html %}
65
+ <link rel="stylesheet" href="/assets/css/ledger/chrome.css" />
66
+ <link rel="stylesheet" href="/assets/css/ledger/footer.css" />
67
+ {%- comment -%}
68
+ Ledger sheets stay with the layout that uses them. article.css is the
69
+ reading column, so only detail-capable layouts load it. An unknown
70
+ layout still receives every sheet.
71
+ {%- endcomment -%}
72
+ {%- case page.layout -%}
73
+ {%- when "home" -%}
74
+ {%- assign _ledger = "home" | split: "|" -%}
75
+ {%- assign _article_css = false -%}
76
+ {%- when "articles" -%}
77
+ {%- assign _ledger = "writing|detail" | split: "|" -%}
78
+ {%- assign _article_css = true -%}
79
+ {%- when "blog" -%}
80
+ {%- assign _ledger = "rascunhos|detail" | split: "|" -%}
81
+ {%- assign _article_css = true -%}
82
+ {%- when "cv" -%}
83
+ {%- assign _ledger = "cv|detail" | split: "|" -%}
84
+ {%- assign _article_css = true -%}
85
+ {%- when "projects" -%}
86
+ {%- assign _ledger = "projects|detail" | split: "|" -%}
87
+ {%- assign _article_css = true -%}
88
+ {%- when "resources" -%}
89
+ {%- assign _ledger = "sources" | split: "|" -%}
90
+ {%- assign _article_css = false -%}
91
+ {%- when "network" -%}
92
+ {%- assign _ledger = "network" | split: "|" -%}
93
+ {%- assign _article_css = false -%}
94
+ {%- when "bibliography" -%}
95
+ {%- assign _ledger = "bibliography" | split: "|" -%}
96
+ {%- assign _article_css = false -%}
97
+ {%- when "text" -%}
98
+ {%- assign _ledger = "detail" | split: "|" -%}
99
+ {%- assign _article_css = true -%}
100
+ {%- else -%}
101
+ {%- assign _ledger = "home|writing|detail|projects|rascunhos|bibliography|network|sources|cv" | split: "|" -%}
102
+ {%- assign _article_css = true -%}
103
+ {%- endcase -%}
104
+ {%- if _article_css -%}
105
+ <link rel="stylesheet" href="/assets/css/article.css" />
106
+ {%- endif -%}
107
+ {%- for _sheet in _ledger -%}
108
+ <link rel="stylesheet" href="/assets/css/ledger/{{ _sheet }}.css" />
71
109
  {%- endfor -%}
72
110
  <link href="https://fonts.googleapis.com" rel="preconnect" />
73
111
  <link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect" />
@@ -78,13 +116,14 @@
78
116
  <style>
79
117
  :root { --drop-cap-lines: {{ site.drop_cap_lines | default: 2 }}; }
80
118
  </style>
81
- <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
82
- <script src="/assets/tailwind-play.js"></script>
119
+ <link rel="stylesheet" href="/assets/css/tailwind.css" />
83
120
  </head>
84
121
 
85
122
  <body class="bg-background text-on-surface">
123
+ <a class="skip-link" href="#page-content">Skip to content</a>
86
124
 
87
125
  {% assign _nav_name = site.pandorga.identity | pandorga_hero_name: site.author, site.title %}
126
+ {% assign _home_label = site.author | default: site.pandorga.identity.name | default: _nav_name.wordmark %}
88
127
  {% assign nav_active = nil %}
89
128
  {% if page.collection == "articles" %}{% assign nav_active = "articles" %}{% endif %}
90
129
  {% if page.collection == "posts" %}{% assign nav_active = "blog" %}{% endif %}
@@ -98,7 +137,7 @@
98
137
  <nav class="sticky top-0 z-50 bg-surface">
99
138
  <div class="flex justify-between items-center w-full px-6 sm:px-8 md:px-10 pt-5 pb-3.5 leading-none max-w-7xl mx-auto">
100
139
  <div class="flex items-center gap-3 sm:gap-4 min-w-0">
101
- <a href="/" aria-label="{{ site.author }} – Home" class="shrink-0 inline-flex items-center gap-3 site-wordmark-link">
140
+ <a href="/" aria-label="{% if _home_label != blank %}{{ _home_label }} – Home{% else %}Home{% endif %}" class="shrink-0 inline-flex items-center gap-3 site-wordmark-link">
102
141
  {% include theme/site-logo.html %}
103
142
  <span class="site-wordmark">{{ _nav_name.wordmark }}</span>
104
143
  </a>
@@ -190,7 +229,9 @@
190
229
 
191
230
  {% include page/content-runtime.html %}
192
231
 
232
+ <div id="page-content" tabindex="-1">
193
233
  {{ content }}
234
+ </div>
194
235
 
195
236
  {% include page/figure-lightbox.html %}
196
237
 
@@ -6,6 +6,29 @@
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
 
9
+ .skip-link {
10
+ position: absolute;
11
+ left: 0.75rem;
12
+ top: 0.75rem;
13
+ z-index: 80;
14
+ padding: 0.45rem 0.75rem;
15
+ background: var(--color-surface);
16
+ color: var(--color-on-surface);
17
+ border: 1px solid var(--color-outline);
18
+ text-decoration: none;
19
+ font-family: var(--font-label);
20
+ font-size: var(--type-label);
21
+ transform: translateY(-150%);
22
+ }
23
+
24
+ .skip-link:focus {
25
+ transform: none;
26
+ }
27
+
28
+ #page-content:focus {
29
+ outline: none;
30
+ }
31
+
9
32
  .site-wordmark-link {
10
33
  display: inline-flex;
11
34
  align-items: center;
@@ -419,6 +419,19 @@ html[data-theme="light"] .cv-product {
419
419
  padding: var(--space-xl);
420
420
  }
421
421
 
422
+ .cv-summary:empty,
423
+ .cv-dossier-note:empty {
424
+ min-height: calc(var(--type-body) * var(--leading-body) * 4);
425
+ }
426
+
427
+ .cv-reserve-card {
428
+ display: block;
429
+ min-height: 6.5rem;
430
+ border: 1.5px solid var(--border-ink);
431
+ border-radius: var(--radius-card);
432
+ background: var(--color-surface-card);
433
+ }
434
+
422
435
  .cv-summary p {
423
436
  margin: 0 0 1em;
424
437
  }
@@ -239,6 +239,10 @@
239
239
  margin: 0;
240
240
  }
241
241
 
242
+ .home-hero-tagline:empty {
243
+ min-height: calc(var(--type-body) * 1.35);
244
+ }
245
+
242
246
  /* Name spans the copy column (container query on .home-hero-copy). The floor
243
247
  is the band-title step — Articles, Rascunhos, Projects — so a phone does
244
248
  not drop the name to the card size to keep it on one line. From 640px the
@@ -308,6 +312,12 @@
308
312
  line-height: var(--leading-body);
309
313
  }
310
314
 
315
+ /* Hold the summary's box until the fragment paints, so the CTA under it
316
+ does not jump when the paragraph arrives. */
317
+ .home-hero-summary:empty {
318
+ min-height: calc(var(--type-body) * var(--leading-body) * 6);
319
+ }
320
+
311
321
  .home-hero-summary p {
312
322
  margin: 0 0 var(--space-md);
313
323
  }
@@ -781,6 +791,16 @@ html[data-theme="light"] .home-hero-plate {
781
791
  }
782
792
 
783
793
  /* ── Writing cards ── */
794
+ .home-card-grid:empty,
795
+ .home-draft-list:empty,
796
+ [data-home-featured-slot]:empty {
797
+ min-height: 16rem;
798
+ }
799
+
800
+ .home-project-inventory:empty {
801
+ min-height: 8rem;
802
+ }
803
+
784
804
  .home-card-grid {
785
805
  display: grid;
786
806
  grid-template-columns: minmax(0, 1fr);
@@ -1232,6 +1252,13 @@ html[data-theme="light"] .home-hero-plate {
1232
1252
  padding: 0;
1233
1253
  }
1234
1254
 
1255
+ button.home-project-inventory-row {
1256
+ font: inherit;
1257
+ color: inherit;
1258
+ text-align: start;
1259
+ width: 100%;
1260
+ }
1261
+
1235
1262
  .home-project-inventory-row {
1236
1263
  position: relative;
1237
1264
  display: flex;
@@ -1498,26 +1525,6 @@ html[data-theme="light"] .home-hero-plate {
1498
1525
  }
1499
1526
  }
1500
1527
 
1501
- @media (min-width: 768px) {}
1502
-
1503
- /* ── Profile band, hero plate scale, section action order ──
1504
- Appended after the parallel page work; see the redesign PR. */
1505
-
1506
- /* The portrait sits at the end of its grid column so its right edge meets
1507
- the edge every band below aligns to. Cap lives on the earlier
1508
- `.home-hero-portrait` rule (Borders Edition). */
1509
- .home-profile-summary {
1510
- max-width: var(--reading-measure);
1511
- }
1512
-
1513
- .home-profile-summary p {
1514
- margin-bottom: var(--space-md);
1515
- }
1516
-
1517
- .home-profile-summary p:last-child {
1518
- margin-bottom: 0;
1519
- }
1520
-
1521
1528
  /* ── Reference band: Sources + Network of Ideas ──────────────────────────
1522
1529
  Two destinations in one band, each a card whose poster is a plate drawn
1523
1530
  from that section's own content by the export and fetched at runtime.
@@ -272,10 +272,34 @@ html[data-theme="light"] .writing-index .writing-lead-media .ledger-poster {
272
272
  }
273
273
  }
274
274
 
275
- /* The revision date is the quieter half of the ledger line: it explains the
276
- default order without competing with the publication date. */
275
+ /* The revision date is the quieter half of the ledger line. It uses the
276
+ variant ink solid: a 78% mix with transparent measured 3.69 and 3.90
277
+ against the light paper, under the 4.5:1 text floor. */
277
278
  .writing-index .writing-revised {
278
- color: color-mix(in srgb, var(--color-on-surface-variant) 78%, transparent);
279
+ color: var(--color-on-surface-variant);
280
+ }
281
+
282
+ .writing-index .writing-archive-reserve {
283
+ display: flex;
284
+ flex-direction: column;
285
+ gap: var(--space-md);
286
+ max-width: none;
287
+ padding: 0;
288
+ background: transparent;
289
+ border: 0;
290
+ }
291
+
292
+ .writing-index .writing-reserve-row {
293
+ display: block;
294
+ min-height: 7.25rem;
295
+ border: 1.5px solid var(--border-ink);
296
+ border-radius: var(--radius-card);
297
+ background: var(--color-surface-card);
298
+ }
299
+
300
+ #articles-lead:empty {
301
+ min-height: 16rem;
302
+ margin-bottom: var(--space-xl);
279
303
  }
280
304
 
281
305
  /* ══ Archive and rail ══
@@ -0,0 +1,3 @@
1
+ /* Compiled Tailwind utilities (3.4.17, forms plugin). Replaces the Play CDN.
2
+ Regenerate with scripts/tailwind/build.sh after adding a utility class. */
3
+ *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-body),sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:var(--font-mono);font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],input:where(:not([type])),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,input:where(:not([type])):focus,select:focus,textarea:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:#6b7280;border-width:1px;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0'/%3E%3C/svg%3E")}@media (forced-colors:active) {[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}@media (forced-colors:active) {[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=radio]:checked:focus,[type=radio]:checked:hover{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}@media (forced-colors:active) {[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.\!visible{visibility:visible!important}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.left-0{left:0}.right-0{right:0}.top-0{top:0}.top-full{top:100%}.isolate{isolation:isolate}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.col-span-full{grid-column:1/-1}.m-0{margin:0}.mx-0\.5{margin-left:.125rem;margin-right:.125rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1\.5{margin-top:.375rem;margin-bottom:.375rem}.my-8{margin-top:2rem;margin-bottom:2rem}.my-\[1\.55em\]{margin-top:1.55em;margin-bottom:1.55em}.mb-10{margin-bottom:2.5rem}.mb-16{margin-bottom:4rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.mb-\[0\.35rem\]{margin-bottom:.35rem}.ml-2{margin-left:.5rem}.mt-1{margin-top:.25rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.\!grid{display:grid!important}.grid{display:grid}.contents{display:contents}.\!hidden{display:none!important}.hidden{display:none}.h-12{height:3rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-\[12vw\]{height:12vw}.h-\[1px\]{height:1px}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.min-h-screen{min-height:100vh}.w-12{width:3rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.min-w-0{min-width:0}.min-w-\[14rem\]{min-width:14rem}.min-w-\[3\.5rem\]{min-width:3.5rem}.min-w-\[9\.5rem\]{min-width:9.5rem}.max-w-7xl{max-width:80rem}.max-w-full{max-width:100%}.max-w-xs{max-width:20rem}.flex-shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.resize{resize:both}.scroll-mt-28{scroll-margin-top:7rem}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0{gap:0}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.overflow-x-auto{overflow-x:auto}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.rounded{border-radius:.125rem}.rounded-\[9999px\]{border-radius:9999px}.border{border-width:1px}.border-0{border-width:0}.border-t{border-top-width:1px}.border-gentle{border-color:var(--border-gentle)}.border-gentle-divider{border-color:var(--border-gentle-divider)}.border-gentle-subtle{border-color:var(--border-gentle-subtle)}.bg-background{background-color:var(--color-background)}.bg-surface{background-color:var(--color-surface)}.bg-surface-container-highest{background-color:var(--color-surface-container-highest)}.bg-surface-container-low{background-color:var(--color-surface-container-low)}.bg-transparent{background-color:transparent}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-1{padding:.25rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pb-16{padding-bottom:4rem}.pb-3\.5{padding-bottom:.875rem}.pt-10{padding-top:2.5rem}.pt-5{padding-top:1.25rem}.text-left{text-align:left}.text-center{text-align:center}.align-top{vertical-align:top}.font-body{font-family:var(--font-body),sans-serif}.font-label{font-family:var(--font-label),serif}.font-mono{font-family:var(--font-mono)}.font-title{font-family:var(--font-title)}.text-5xl{font-size:3rem;line-height:1}.text-\[0\.8em\]{font-size:.8em}.text-\[0\.9em\]{font-size:.9em}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-light{font-weight:300}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.ordinal{--tw-ordinal:ordinal}.ordinal,.tabular-nums{font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.tabular-nums{--tw-numeric-spacing:tabular-nums}.leading-\[1\.55\]{line-height:1.55}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.tracking-widest{letter-spacing:.1em}.text-on-surface{color:var(--color-on-surface)}.text-on-surface-variant{color:var(--color-on-surface-variant)}.text-outline{color:var(--color-outline)}.text-primary{color:var(--color-primary)}.text-secondary{color:var(--color-secondary)}.text-tertiary{color:var(--color-tertiary)}.underline{text-decoration-line:underline}.no-underline{text-decoration-line:none}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-60{opacity:.6}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.outline{outline-style:solid}.blur{--tw-blur:blur(8px)}.blur,.drop-shadow{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px rgba(0,0,0,.1)) drop-shadow(0 1px 1px rgba(0,0,0,.06))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.bg-outline-variant\/30{background-color:color-mix(in srgb,var(--color-outline-variant) 30%,transparent)}.border-tertiary\/40{border-color:color-mix(in srgb,var(--color-tertiary) 40%,transparent)}.hover\:border-primary:hover{border-color:var(--color-primary)}.hover\:border-tertiary:hover{border-color:var(--color-tertiary)}.hover\:bg-surface-container-low:hover{background-color:var(--color-surface-container-low)}.hover\:text-primary:hover{color:var(--color-primary)}.hover\:underline:hover{text-decoration-line:underline}.group:hover .group-hover\:bg-primary-container{background-color:var(--color-primary-container)}.group:hover .group-hover\:text-on-primary-container{color:var(--color-on-primary-container)}@media (min-width:640px){.sm\:gap-4{gap:1rem}.sm\:gap-5{gap:1.25rem}.sm\:px-8{padding-left:2rem;padding-right:2rem}}@media (min-width:768px){.md\:sticky{position:sticky}.md\:top-32{top:8rem}.md\:mb-12{margin-bottom:3rem}.md\:px-10{padding-left:2.5rem;padding-right:2.5rem}.md\:px-16{padding-left:4rem;padding-right:4rem}.md\:text-6xl{font-size:3.75rem;line-height:1}}@media (min-width:1024px){.lg\:sticky{position:sticky}.lg\:top-32{top:8rem}.lg\:gap-12{gap:3rem}}
@@ -1,5 +1,6 @@
1
1
  ---
2
- # Play CDN browser config (not a Node Tailwind project file).
2
+ # Kept as the readable theme binding. The shell does not load this file.
3
+ # Utilities are compiled to assets/css/tailwind.css (scripts/tailwind/build.sh).
3
4
  # Named *-play.js so the Tailwind IntelliSense extension does not treat it as
4
5
  # tailwind.config.js and fail with "tailwind is not defined" / _site duplicates.
5
6
  ---
@@ -30,7 +30,9 @@ module Pandorga
30
30
  end
31
31
  pages = pandorga["pages"]
32
32
  if pages.nil?
33
- warnings << "pandorga.pages is empty — no pages will be generated"
33
+ warnings << "pandorga.pages is missing; no pages will be generated"
34
+ elsif pages.is_a?(Array) && pages.empty?
35
+ warnings << "pandorga.pages is empty; no pages will be generated"
34
36
  elsif !pages.is_a?(Array)
35
37
  errors << "pandorga.pages must be an array"
36
38
  else
@@ -24,7 +24,7 @@ module Pandorga
24
24
 
25
25
  gem "pandorga", "~> 1.3"
26
26
  # Local checkout: gem "pandorga", path: "../pandorga"
27
- # GitHub tag: gem "pandorga", github: "alexandrelheinen/pandorga", tag: "v1.3.11"
27
+ # GitHub tag: gem "pandorga", github: "alexandrelheinen/pandorga", tag: "v1.5.0"
28
28
  gem "csv"
29
29
  gem "base64"
30
30
  RUBY
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pandorga
4
+ # Sitemap and robots text. No Jekyll dependency, so the gate can call it
5
+ # without building a site.
6
+ module Discovery
7
+ module_function
8
+
9
+ def origin(config)
10
+ config = {} unless config.is_a?(Hash)
11
+ pandorga = config["pandorga"]
12
+ pandorga = {} unless pandorga.is_a?(Hash)
13
+ identity = pandorga["identity"]
14
+ identity = {} unless identity.is_a?(Hash)
15
+ url = identity["url"].to_s.strip
16
+ url = config["url"].to_s.strip if url.empty?
17
+ url.sub(%r{/\z}, "")
18
+ end
19
+
20
+ def absolute(origin, path)
21
+ path = path.to_s
22
+ return path if path.match?(%r{\Ahttps?://}i)
23
+
24
+ path = "/#{path}" unless path.start_with?("/")
25
+ path = "/" if path.empty?
26
+ base = origin.to_s.sub(%r{/\z}, "")
27
+ return path if base.empty?
28
+
29
+ "#{base}#{path}"
30
+ end
31
+
32
+ def public_paths(pages)
33
+ Array(pages).filter_map do |page|
34
+ data = page_data(page)
35
+ next if data["sitemap"] == false
36
+
37
+ url = page.respond_to?(:url) ? page.url.to_s : page["url"].to_s
38
+ next if url.empty?
39
+ next if File.extname(url).match?(/\A\.(xml|txt|json)\z/i)
40
+
41
+ url
42
+ end.uniq
43
+ end
44
+
45
+ def sitemap_xml(origin, paths)
46
+ locs = Array(paths).map { |path| absolute(origin, path) }.uniq
47
+ body = locs.map { |loc| " <url><loc>#{xml_escape(loc)}</loc></url>" }.join("\n")
48
+ <<~XML
49
+ <?xml version="1.0" encoding="UTF-8"?>
50
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
51
+ #{body}
52
+ </urlset>
53
+ XML
54
+ end
55
+
56
+ def robots_txt(sitemap_url)
57
+ <<~TXT
58
+ User-agent: *
59
+ Allow: /
60
+ Disallow: /studio/
61
+
62
+ Sitemap: #{sitemap_url}
63
+ TXT
64
+ end
65
+
66
+ def xml_escape(value)
67
+ value.to_s.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;").gsub('"', "&quot;")
68
+ end
69
+
70
+ def page_data(page)
71
+ return page.data if page.respond_to?(:data) && page.data.is_a?(Hash)
72
+
73
+ data = page["data"]
74
+ data.is_a?(Hash) ? data : {}
75
+ end
76
+ private_class_method :page_data
77
+ end
78
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll"
4
+ require_relative "../../discovery"
5
+
6
+ module Pandorga
7
+ module Jekyll
8
+ # Writes /sitemap.xml and /robots.txt from the pages already generated.
9
+ class DiscoveryGenerator < ::Jekyll::Generator
10
+ safe true
11
+ priority :lowest
12
+
13
+ def generate(site)
14
+ origin = Pandorga::Discovery.origin(site.config)
15
+ paths = Pandorga::Discovery.public_paths(site.pages)
16
+ sitemap = Pandorga::Discovery.absolute(origin, "/sitemap.xml")
17
+ emit(site, "sitemap.xml", Pandorga::Discovery.sitemap_xml(origin, paths))
18
+ emit(site, "robots.txt", Pandorga::Discovery.robots_txt(sitemap))
19
+ end
20
+
21
+ private
22
+
23
+ def emit(site, name, content)
24
+ page = ::Jekyll::PageWithoutAFile.new(site, site.source, "", name)
25
+ page.data["layout"] = nil
26
+ page.data["sitemap"] = false
27
+ page.content = content
28
+ site.pages << page
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pandorga
4
- VERSION = "1.5.0"
4
+ VERSION = "1.6.0"
5
5
  end
@@ -5,15 +5,12 @@
5
5
  "https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/tex-mml-chtml.js",
6
6
  "https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js",
7
7
  "https://cdn.jsdelivr.net/npm/plotly.js-dist-min@3.1.0/plotly.min.js",
8
- "https://cdn.jsdelivr.net/gh/lipis/flag-icons@7.2.3/css/flag-icons.min.css",
9
- "https://cdn.tailwindcss.com?plugins=forms,container-queries"
8
+ "https://cdn.jsdelivr.net/gh/lipis/flag-icons@7.2.3/css/flag-icons.min.css"
10
9
  ],
11
10
  "scan_globs": [
12
11
  "_layouts",
13
12
  "_includes",
14
13
  "content/media/plots"
15
14
  ],
16
- "exceptions": {
17
- "tailwind_play_cdn": "Tailwind play CDN cannot be SRI-pinned; URL must match exactly."
18
- }
15
+ "exceptions": {}
19
16
  }
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ # Rebuild assets/css/tailwind.css. Not part of the site build or validate.sh.
3
+ # Requires tailwindcss 3.4 and @tailwindcss/forms on NODE_PATH or via npx.
4
+ set -euo pipefail
5
+
6
+ ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
7
+ cd "$ROOT"
8
+
9
+ npx --yes tailwindcss@3.4.17 -c scripts/tailwind/tailwind.config.js \
10
+ -i scripts/tailwind/input.css \
11
+ -o assets/css/tailwind.css \
12
+ --minify
13
+
14
+ HEADER='/* Compiled Tailwind utilities (3.4.17, forms plugin). Replaces the Play CDN.
15
+ Regenerate with scripts/tailwind/build.sh after adding a utility class. */
16
+ '
17
+ TMP="$(mktemp)"
18
+ printf '%s' "$HEADER" | cat - assets/css/tailwind.css > "$TMP"
19
+ mv "$TMP" assets/css/tailwind.css
@@ -0,0 +1,12 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ /* Opacity modifiers do not apply to colors stored as var(--token). */
6
+ .bg-outline-variant\/30 {
7
+ background-color: color-mix(in srgb, var(--color-outline-variant) 30%, transparent);
8
+ }
9
+
10
+ .border-tertiary\/40 {
11
+ border-color: color-mix(in srgb, var(--color-tertiary) 40%, transparent);
12
+ }
@@ -0,0 +1,102 @@
1
+ const path = require("path");
2
+ const forms = require("@tailwindcss/forms");
3
+
4
+ const root = path.join(__dirname, "..", "..");
5
+
6
+ module.exports = {
7
+ content: [
8
+ path.join(root, "_layouts/**/*.{html,liquid}"),
9
+ path.join(root, "_includes/**/*.{html,liquid,js}"),
10
+ path.join(root, "assets/**/*.{js,html,liquid}"),
11
+ ],
12
+ darkMode: ["selector", '[data-theme="dark"]'],
13
+ theme: {
14
+ extend: {
15
+ colors: {
16
+ primary: "var(--color-primary)",
17
+ secondary: "var(--color-secondary)",
18
+ tertiary: "var(--color-tertiary)",
19
+ "primary-container": "var(--color-primary-container)",
20
+ "secondary-container": "var(--color-secondary-container)",
21
+ "tertiary-container": "var(--color-tertiary-container)",
22
+ "on-primary": "var(--color-on-primary)",
23
+ "on-secondary": "var(--color-on-secondary)",
24
+ "on-tertiary": "var(--color-on-tertiary)",
25
+ "on-primary-container": "var(--color-on-primary-container)",
26
+ "on-secondary-container": "var(--color-on-secondary-container)",
27
+ "on-tertiary-container": "var(--color-on-tertiary-container)",
28
+ "on-surface": "var(--color-on-surface)",
29
+ "on-surface-variant": "var(--color-on-surface-variant)",
30
+ "on-background": "var(--color-on-background)",
31
+ outline: "var(--color-outline)",
32
+ "outline-variant": "var(--color-outline-variant)",
33
+ "gentle-faint": "var(--border-gentle-faint)",
34
+ "gentle-subtle": "var(--border-gentle-subtle)",
35
+ "gentle-divider": "var(--border-gentle-divider)",
36
+ gentle: "var(--border-gentle)",
37
+ "gentle-hover": "var(--border-gentle-hover)",
38
+ background: "var(--color-background)",
39
+ "background-secondary": "var(--color-background-secondary)",
40
+ surface: "var(--color-surface)",
41
+ "surface-bright": "var(--color-surface-bright)",
42
+ "surface-container-lowest": "var(--color-surface-container-lowest)",
43
+ "surface-container-low": "var(--color-surface-container-low)",
44
+ "surface-container": "var(--color-surface-container)",
45
+ "surface-container-high": "var(--color-surface-container-high)",
46
+ "surface-container-highest": "var(--color-surface-container-highest)",
47
+ "surface-variant": "var(--color-surface-variant)",
48
+ "surface-dim": "var(--color-surface-dim)",
49
+ "panel-contrast": "var(--color-panel-contrast)",
50
+ "surface-card": "var(--color-surface-card)",
51
+ "surface-card-contrast": "var(--color-surface-card-contrast)",
52
+ "surface-high": "var(--color-surface-high)",
53
+ "surface-high-contrast": "var(--color-surface-high-contrast)",
54
+ "surface-tint": "var(--color-surface-tint)",
55
+ "inverse-primary": "var(--color-inverse-primary)",
56
+ "inverse-surface": "var(--color-inverse-surface)",
57
+ "inverse-on-surface": "var(--color-inverse-on-surface)",
58
+ "primary-fixed": "var(--color-primary-fixed)",
59
+ "primary-fixed-dim": "var(--color-primary-fixed-dim)",
60
+ "secondary-fixed": "var(--color-secondary-fixed)",
61
+ "secondary-fixed-dim": "var(--color-secondary-fixed-dim)",
62
+ "tertiary-fixed": "var(--color-tertiary-fixed)",
63
+ "tertiary-fixed-dim": "var(--color-tertiary-fixed-dim)",
64
+ "on-primary-fixed": "var(--color-on-primary-fixed)",
65
+ "on-primary-fixed-variant": "var(--color-on-primary-fixed-variant)",
66
+ "on-secondary-fixed": "var(--color-on-secondary-fixed)",
67
+ "on-secondary-fixed-variant": "var(--color-on-secondary-fixed-variant)",
68
+ "on-tertiary-fixed": "var(--color-on-tertiary-fixed)",
69
+ "on-tertiary-fixed-variant": "var(--color-on-tertiary-fixed-variant)",
70
+ error: "var(--color-error)",
71
+ "on-error": "var(--color-on-error)",
72
+ "error-container": "var(--color-error-container)",
73
+ "on-error-container": "var(--color-on-error-container)",
74
+ "res-youtube": "var(--color-res-youtube)",
75
+ "res-podcast": "var(--color-res-podcast)",
76
+ "res-doc": "var(--color-res-doc)",
77
+ link: "var(--color-link)",
78
+ "link-hover": "var(--color-link-hover)",
79
+ },
80
+ fontFamily: {
81
+ title: ["var(--font-title)"],
82
+ subtitle: ["var(--font-subtitle)", "serif"],
83
+ label: ["var(--font-label)", "serif"],
84
+ body: ["var(--font-body)", "sans-serif"],
85
+ headline: ["var(--font-title)"],
86
+ editorial: ["var(--font-title)"],
87
+ serif: ["var(--font-title)"],
88
+ sans: ["var(--font-body)", "sans-serif"],
89
+ mono: ["var(--font-mono)"],
90
+ code: ["var(--font-code)"],
91
+ },
92
+ borderRadius: {
93
+ DEFAULT: "0.125rem",
94
+ lg: "0.25rem",
95
+ xl: "0.5rem",
96
+ full: "0.75rem",
97
+ },
98
+ },
99
+ },
100
+ safelist: ["text-primary", "text-secondary", "text-tertiary", "text-outline"],
101
+ plugins: [forms],
102
+ };
@@ -23,7 +23,7 @@
23
23
  }
24
24
  ],
25
25
  "selectors": [
26
- "article.home-project-inventory-row",
26
+ "button.home-project-inventory-row",
27
27
  "div.home-project-inventory-media",
28
28
  "div.ledger-poster",
29
29
  "div.home-project-inventory-code",
@@ -35,7 +35,8 @@
35
35
  "span.home-project-inventory-resource"
36
36
  ],
37
37
  "forbidden": [
38
- "ledger-card-stretch"
38
+ "ledger-card-stretch",
39
+ "role=\"button\""
39
40
  ],
40
41
  "contains": [
41
42
  ">02<",