pandorga 1.10.1 → 1.11.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12f24b429006ae62f3e74335afdf759a851068879cc639273a77d5cc95e8d9fd
4
- data.tar.gz: efa405146c9212bcdff063abfbcadc8b03c5a222679894acd9db8dbeea522c92
3
+ metadata.gz: 9d3adc84d250c4ff140df5b9cc9db266896fdfce92fd899789fd943ce8ac4659
4
+ data.tar.gz: 474d788dea7851796fb189732c9bb75d118666177f4cf81df70d8361f4310872
5
5
  SHA512:
6
- metadata.gz: 2dbd18257d2507115b35b5370d0ef9e1da9e289cab3eb6861aa52532e1927a23fe834120c689a74d0331631ac929d0f88c71fe38b93563415a98de3d2e6176e9
7
- data.tar.gz: 431866457e1c4f3d7945f6756b9331f9191923b6e353411b98540c9adcfdad75b80ad4bfaefd6200de6057cb4ca2021c94097b45c9fdd601d25dc7c99879aae2
6
+ metadata.gz: d9e76b0be8485c3870366cce0bc816fc55f3dee7ea2b17ec40602f2c70dccbf985e2d7be6da7911e54a7f830c6fd3c4030dbb985689d50c84cb9226c04cd0f2e
7
+ data.tar.gz: 6853fe1a1d0edd4066d45187a4cddd67258a753afd849284615383fb9cae4844a24aabff188681a73066e2808170bfc1e28730832e68d976c34400899d2cea88
data/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
2
 
3
+ ## [Unreleased]
4
+
5
+ ## [1.11.0] — 2026-10-19
6
+
7
+ ### Fixed
8
+
9
+ - Home listing bands keep their kicker and note on sites built with
10
+ `shell_content_agnostic: true`; the slots are always in the HTML and the
11
+ runtime fills them from `pages/headers.json` (regression from 1.10.0)
12
+
13
+ ### Changed
14
+
15
+ - On the home projects bench at or above 900px, the specimen column takes 35%
16
+ of the band (at least 20rem) and the filing rack 45% (at least 21rem) across
17
+ one `--space-xl` column gap from the band's left edge, keeping the 16:9
18
+ plate at 60% of its previous height. The specimen sets the bench height (the
19
+ tallest project), and the rack rows share that height, each capped at 1.6
20
+ times its minimum
21
+ - Below 900px the project icon-strip glyphs and the plate badge glyph grow to
22
+ 1.375rem
23
+ - The home project specimen title uses `--type-section` from 900px and
24
+ `--type-card-lg` below it; inventory rack titles use `--type-card`
25
+ - The home specimen meta strip wraps instead of clipping on narrow viewports
26
+ - The home hero summary uses `--color-legend`, the same ink as the other
27
+ description texts
28
+
29
+ ## [1.10.2] — 2026-10-16
30
+
31
+ ### Changed
32
+
33
+ - On the home projects bench, each inventory row shows the project icon mark
34
+ (or initial fallback), optional index and label strip, and title, without a
35
+ thumbnail poster, tagline preview, or attached-resource icons. On desktop
36
+ (`>= 900px`) each row fits that two-line body and square icon mark, and the
37
+ specimen height matches the inventory stack
38
+ - Project hero action pills keep their label text free of underlines at rest
39
+ and on hover
40
+
3
41
  ## [1.10.1] — 2026-10-14
4
42
 
5
43
  ### Changed
data/DESIGN.md CHANGED
@@ -941,13 +941,15 @@ Letter-spacing in use, by frequency: `0.1em` (12), `0.08em` (10), `0.12em` (7),
941
941
  icon plate, the name's initial standing in for a missing icon; the title
942
942
  always below the plate; a tagline clamped to the
943
943
  height the plate leaves; tags, outbound links and an emphasis "Open
944
- project" cue). From 900px it takes 50% of the band, beside a 34%
945
- `panelContrast` filing rack of equal-flex project cards (16:9 thumb, code
946
- line, title, tagline; the selected card steps to `surfaceHigh` with a 3px
947
- `primary` inset rule), the rest left blank. Rack click swaps the specimen
944
+ project" cue). From 900px the specimen and a `panelContrast` filing rack
945
+ take 35% and 45% of the band across a `--space-xl` gap, from its left edge;
946
+ rack rows are equal-flex
947
+ cards (square icon mark, code line, title; the selected one steps to
948
+ `surfaceHigh` with a 3px `primary` inset rule) that share the specimen's
949
+ height. Rack click swaps the specimen
948
950
  in place; specimen click opens the project. Below 900px the rack is an
949
951
  icon strip above the specimen, one equal column per project (up to six)
950
- so it spans the band. Contract: `PLT-AC-13`–`17` in
952
+ so it spans the band, with 1.375rem glyphs. Contract: `PLT-AC-13`–`17` in
951
953
  `templates/portfolio/README.md`. Below 768px the hero portrait runs to the screen edge, pulled
952
954
  out of the band's gutter by exactly `--gutter-mobile` on each side and losing
953
955
  its side hairlines, which at the edge would read as a frame cut off.
@@ -330,9 +330,9 @@
330
330
  }
331
331
 
332
332
  /* Rack card for the home portfolio bench.
333
- * Desktop: thumb (or icon, or initial) beside a code line, title and
334
- * tagline. The mobile strip shows the project glyph only. Selectable rows
335
- * update the specimen. */
333
+ * Desktop: square icon mark (or initial) beside a code line and title.
334
+ * The mobile strip shows the project glyph only. Selectable rows update
335
+ * the specimen. */
336
336
  function renderProjectInventoryRow(item, options) {
337
337
  options = options || {};
338
338
  var f = blockFields(item);
@@ -357,12 +357,12 @@
357
357
  row.setAttribute('aria-label', name);
358
358
  }
359
359
 
360
- var media = renderPosterMedia(fm.thumbnail, name);
361
360
  var initial = String(name || '?').charAt(0).toUpperCase();
362
- if (!media && fm.icon) {
361
+ var media;
362
+ if (fm.icon) {
363
363
  media = '<div class="home-project-inventory-mark" aria-hidden="true">' +
364
364
  '<span class="material-symbols-outlined">' + escapeHtml(fm.icon) + '</span></div>';
365
- } else if (!media) {
365
+ } else {
366
366
  media = '<div class="home-project-inventory-mark home-project-inventory-mark--initial" aria-hidden="true">' +
367
367
  escapeHtml(initial) + '</div>';
368
368
  }
@@ -388,9 +388,6 @@
388
388
  '<a class="ledger-card-stretch" href="' + escapeAttr(href) + '">' +
389
389
  escapeHtml(name) + '</a></h3>';
390
390
  }
391
- if (fm.tagline) {
392
- html += '<p class="home-project-inventory-copy font-body">' + escapeHtml(fm.tagline) + '</p>';
393
- }
394
391
  html += '</div>';
395
392
  if (fm.icon) {
396
393
  html += '<span class="home-project-inventory-glyph material-symbols-outlined" aria-hidden="true">' +
@@ -399,7 +396,6 @@
399
396
  html += '<span class="home-project-inventory-glyph home-project-inventory-glyph--initial" aria-hidden="true">' +
400
397
  escapeHtml(initial) + '</span>';
401
398
  }
402
- html += renderProjectResourceBadges(fm);
403
399
 
404
400
  row.innerHTML = html;
405
401
  return row;
@@ -473,32 +469,6 @@
473
469
  return links.join('');
474
470
  }
475
471
 
476
- /* Square resource marks for the home inventory rack — same fields as
477
- * renderProjectLinks (github / article_url / external_url), icon-only
478
- * indicators (not links). */
479
- function renderProjectResourceBadges(fm) {
480
- var badges = [];
481
- if (fm.github) {
482
- badges.push({ icon: 'code', label: 'Code' });
483
- }
484
- if (fm.article_url) {
485
- badges.push({ icon: 'article', label: 'Article' });
486
- }
487
- if (fm.external_url) {
488
- badges.push({ icon: 'public', label: 'Site' });
489
- }
490
- if (!badges.length) return '';
491
- return '<div class="home-project-inventory-resources" aria-label="Attached resources">' +
492
- badges.map(function (badge) {
493
- return '<span class="home-project-inventory-resource" title="' +
494
- escapeAttr(badge.label) + '" aria-label="' +
495
- escapeAttr(badge.label) + '">' +
496
- '<span class="material-symbols-outlined" aria-hidden="true">' +
497
- escapeHtml(badge.icon) + '</span></span>';
498
- }).join('') +
499
- '</div>';
500
- }
501
-
502
472
  /* Home specimen — the featured project on the portfolio bench. A system
503
473
  * card whose strip carries folio, label and start year, with the title
504
474
  * below a 16:9 plate: the thumbnail (icon badge on it) or else the icon
@@ -1,6 +1,8 @@
1
1
  {%- comment -%}
2
2
  One homepage listing band. Expects `band` from site.data.pandorga_home_bands.
3
- Markup slots are filled by the home layout script from writing_index.json.
3
+ Kicker and note slots are always emitted, prefilled from headers.yml when it
4
+ was read at build time and filled by the runtime otherwise (PLT-AC-42).
5
+ Listing slots are filled by the home layout script from writing_index.json.
4
6
  {%- endcomment -%}
5
7
  {%- assign _accent = band.accent -%}
6
8
  {%- assign _limit = band.home.limit | default: 3 -%}
@@ -28,13 +30,9 @@
28
30
  {%- if _section.title != blank -%}{%- assign _title = _section.title -%}{%- endif -%}
29
31
  <div class="ledger-section-banner" data-content-home-section="{{ band.section_key }}">
30
32
  <div class="ledger-section-lead">
31
- {%- if _section.kicker != blank -%}
32
33
  <span class="ledger-kicker" data-content-section-kicker>{{ _section.kicker }}</span>
33
- {%- endif -%}
34
34
  <h2 class="section-title" data-content-section-title>{{ _title }}</h2>
35
- {%- if _section.note != blank -%}
36
35
  <p class="home-section-note font-body" data-content-section-note>{{ _section.note }}</p>
37
- {%- endif -%}
38
36
  </div>
39
37
  <div class="home-section-aside">
40
38
  <a class="ledger-cta" href="{{ _path }}">
data/_layouts/home.html CHANGED
@@ -399,22 +399,12 @@ layout: shell
399
399
  async function lockSpecimenHeight() {
400
400
  var bench = specimenSlot.closest('.home-projects-bench');
401
401
  if (!bench) return;
402
+ bench.style.setProperty('--home-project-icon-count', String(iconSlots));
402
403
  var tallest = await measureTallestSpecimen();
403
- /* Floor at the CSS inventory minimum so six rows always clear
404
- three stacked resource icons (see --home-inventory-min-height). */
405
- var inventoryMin = 0;
406
- var raw = window.getComputedStyle(bench)
407
- .getPropertyValue('--home-inventory-min-height').trim();
408
- if (raw) {
409
- var probe = document.createElement('div');
410
- probe.style.cssText = 'position:absolute;visibility:hidden;height:' + raw;
411
- bench.appendChild(probe);
412
- inventoryMin = Math.ceil(probe.getBoundingClientRect().height);
413
- probe.remove();
414
- }
415
- var locked = Math.max(tallest, inventoryMin);
416
- if (locked > 0) {
417
- bench.style.setProperty('--home-specimen-height', locked + 'px');
404
+ /* Every breakpoint locks the specimen to the tallest project; from
405
+ 900px the rack rows share that height. */
406
+ if (tallest > 0) {
407
+ bench.style.setProperty('--home-specimen-height', tallest + 'px');
418
408
  }
419
409
  }
420
410
 
@@ -441,12 +431,10 @@ layout: shell
441
431
  selected: entry.slug === selectedSlug,
442
432
  index: i + 1
443
433
  });
444
- row.addEventListener('click', function (event) {
445
- if (event.target.closest('.home-project-inventory-resources')) return;
434
+ row.addEventListener('click', function () {
446
435
  select(entry.slug);
447
436
  });
448
437
  row.addEventListener('keydown', function (event) {
449
- if (event.target.closest('.home-project-inventory-resources')) return;
450
438
  if (event.key === 'Enter' || event.key === ' ') {
451
439
  event.preventDefault();
452
440
  select(entry.slug);
@@ -308,7 +308,7 @@
308
308
 
309
309
  .home-hero-summary {
310
310
  max-width: var(--reading-measure, 45rem);
311
- color: var(--color-on-surface);
311
+ color: var(--color-legend);
312
312
  line-height: var(--leading-body);
313
313
  }
314
314
 
@@ -1035,10 +1035,11 @@ html[data-theme="light"] .home-hero-plate {
1035
1035
  the inventory. Taglines clamp to two lines with an ellipsis; chips wrap
1036
1036
  whole pills like the projects index; spare height parks chips and the foot
1037
1037
  on the floor. Every plate is 16:9, the ratio the thumbnails are drawn at,
1038
- with the title below it. From 900px the specimen and the rack share equal
1039
- columns (~45.6% each); rows reserve height for three resource icons.
1040
- Below 900px the rack is an icon strip above the specimen, one column
1041
- per project. */
1038
+ with the title below it. From 900px the specimen column takes 35% of the
1039
+ band (at least 20rem) and the rack 45% (at least 21rem) across one
1040
+ `--space-xl` column gap, starting at the band's left edge, and the rack
1041
+ rows share the specimen height. Below 900px the rack is an icon strip
1042
+ above the specimen, one column per project. */
1042
1043
  .home-projects-bench {
1043
1044
  --home-specimen-height: 32rem;
1044
1045
  --home-icon-btn-size: 2.75rem;
@@ -1101,10 +1102,18 @@ html[data-theme="light"] .home-hero-plate {
1101
1102
  letter-spacing: 0.03em;
1102
1103
  }
1103
1104
 
1105
+ /* The specimen strip flows as text, so a long label wraps between words
1106
+ instead of clipping the start year on a phone. */
1104
1107
  .home-specimen-strip .ledger-meta {
1108
+ display: block;
1109
+ white-space: normal;
1105
1110
  font-size: var(--type-label-lg);
1106
1111
  }
1107
1112
 
1113
+ .home-specimen-strip .ledger-meta-sep {
1114
+ margin-inline: var(--space-sm);
1115
+ }
1116
+
1108
1117
  .home-specimen-folio,
1109
1118
  .home-project-inventory-index {
1110
1119
  color: var(--color-primary);
@@ -1205,6 +1214,22 @@ html[data-theme="light"] .home-hero-plate {
1205
1214
  color: var(--color-primary);
1206
1215
  }
1207
1216
 
1217
+ @media (max-width: 899px) {
1218
+ .home-specimen-figure:has(> .ledger-poster)>.home-specimen-badge {
1219
+ font-size: 1.375rem;
1220
+ }
1221
+
1222
+ /* Two classes: the Material Symbols sheet loads after this one and resets
1223
+ the glyph size on a single class. */
1224
+ .home-project-inventory-row .home-project-inventory-glyph {
1225
+ font-size: 1.375rem;
1226
+ }
1227
+
1228
+ .home-project-inventory-row .home-project-inventory-glyph--initial {
1229
+ font-size: var(--type-card);
1230
+ }
1231
+ }
1232
+
1208
1233
  .home-project-specimen .home-specimen-title {
1209
1234
  margin: 0;
1210
1235
  font-size: var(--type-card-lg);
@@ -1321,10 +1346,6 @@ button.home-project-inventory-row {
1321
1346
  display: none;
1322
1347
  }
1323
1348
 
1324
- .home-project-inventory-resources {
1325
- display: none;
1326
- }
1327
-
1328
1349
  .home-project-inventory-glyph {
1329
1350
  display: inline-flex;
1330
1351
  align-items: center;
@@ -1367,26 +1388,22 @@ button.home-project-inventory-row {
1367
1388
 
1368
1389
  @media (min-width: 900px) {
1369
1390
 
1370
- /* Desktop fallback before JS measures the tallest specimen.
1371
- Specimen and inventory share equal columns (specimen was 38% then
1372
- +20% → 45.6%; the list matches that width). Rows still reserve
1373
- height for three stacked resource icons. */
1391
+ /* The specimen column is 35% of the band (at least 20rem), which keeps the
1392
+ 16:9 plate at 60% of the 7:5 split's height; the rack is 45% (at least
1393
+ 21rem); the pair starts at the band's left edge and leaves the rest of
1394
+ the row blank. The specimen sets the bench height; rack rows share it. */
1374
1395
  .home-projects-bench {
1375
- --home-inventory-icon-size: 1.55rem;
1376
- --home-inventory-icon-gap: 0.2rem;
1377
- --home-inventory-resources-height: calc(
1378
- 3 * var(--home-inventory-icon-size) + 2 * var(--home-inventory-icon-gap)
1379
- );
1396
+ --home-inventory-mark-size: 3rem;
1380
1397
  --home-inventory-row-min: calc(
1381
- var(--home-inventory-resources-height) + 2 * var(--space-sm)
1382
- );
1383
- --home-inventory-min-height: calc(
1384
- 6 * var(--home-inventory-row-min) + 5 * var(--space-sm) + 2 * var(--space-md)
1398
+ var(--home-inventory-mark-size) + 2 * var(--space-sm) + 2px
1385
1399
  );
1386
- --home-specimen-height: max(36rem, var(--home-inventory-min-height));
1400
+ /* Rows share the specimen height, but a short rack must not stretch a
1401
+ single row to the full plate. */
1402
+ --home-inventory-row-max: calc(1.6 * var(--home-inventory-row-min));
1387
1403
  display: grid;
1388
- grid-template-columns: minmax(0, 45.6%) minmax(0, 45.6%);
1404
+ grid-template-columns: max(35%, 20rem) max(45%, 21rem);
1389
1405
  column-gap: var(--space-xl);
1406
+ justify-content: start;
1390
1407
  align-items: stretch;
1391
1408
  }
1392
1409
 
@@ -1397,6 +1414,7 @@ button.home-project-inventory-row {
1397
1414
  gap: var(--space-sm);
1398
1415
  box-sizing: border-box;
1399
1416
  width: 100%;
1417
+ min-height: var(--home-specimen-height);
1400
1418
  min-width: 0;
1401
1419
  min-height: 0;
1402
1420
  overflow: hidden;
@@ -1405,16 +1423,18 @@ button.home-project-inventory-row {
1405
1423
  }
1406
1424
 
1407
1425
  .home-project-specimen .home-specimen-title {
1408
- font-size: var(--type-title);
1426
+ font-size: var(--type-section);
1409
1427
  }
1410
1428
 
1411
1429
  .home-project-inventory-row {
1412
1430
  flex: 1 1 0;
1413
- align-items: flex-start;
1431
+ align-items: center;
1414
1432
  justify-content: flex-start;
1415
1433
  gap: var(--space-md);
1416
1434
  height: auto;
1417
1435
  min-height: var(--home-inventory-row-min);
1436
+ max-height: var(--home-inventory-row-max);
1437
+ box-sizing: border-box;
1418
1438
  padding: var(--space-sm) var(--space-md);
1419
1439
  border: 1px solid var(--border-gentle-divider);
1420
1440
  background: var(--color-surface-card);
@@ -1433,15 +1453,13 @@ button.home-project-inventory-row {
1433
1453
  box-shadow: inset 3px 0 0 var(--color-primary);
1434
1454
  }
1435
1455
 
1436
- /* Plate fills the row’s content height; width follows 16:9. Stretch keeps
1437
- it tall while title/copy/icons stay top-aligned with its top edge. */
1456
+ /* Square icon mark beside the two-line code + title body. */
1438
1457
  .home-project-inventory-media {
1439
1458
  display: block;
1440
1459
  flex: 0 0 auto;
1441
- align-self: stretch;
1442
- width: auto;
1443
- height: auto;
1444
- aspect-ratio: 16 / 9;
1460
+ width: var(--home-inventory-mark-size);
1461
+ height: var(--home-inventory-mark-size);
1462
+ aspect-ratio: 1 / 1;
1445
1463
  overflow: hidden;
1446
1464
  background: var(--color-surface-high);
1447
1465
  }
@@ -1451,33 +1469,25 @@ button.home-project-inventory-row {
1451
1469
  outline-offset: 2px;
1452
1470
  }
1453
1471
 
1454
- .home-project-inventory-media .ledger-poster,
1455
1472
  .home-project-inventory-mark {
1473
+ display: flex;
1474
+ align-items: center;
1475
+ justify-content: center;
1456
1476
  width: 100%;
1457
1477
  height: 100%;
1458
1478
  aspect-ratio: auto;
1459
1479
  overflow: hidden;
1460
1480
  background: var(--color-surface-high);
1461
- }
1462
-
1463
- .home-project-inventory-media .ledger-poster::after {
1464
- display: none;
1465
- }
1466
-
1467
- .home-project-inventory-mark {
1468
- display: flex;
1469
- align-items: center;
1470
- justify-content: center;
1471
1481
  color: var(--color-primary);
1472
1482
  }
1473
1483
 
1474
1484
  .home-project-inventory-mark .material-symbols-outlined {
1475
- font-size: 2rem;
1485
+ font-size: 1.5rem;
1476
1486
  }
1477
1487
 
1478
1488
  .home-project-inventory-mark--initial {
1479
1489
  font-family: var(--font-content-title);
1480
- font-size: var(--type-card-lg);
1490
+ font-size: var(--type-card);
1481
1491
  font-weight: var(--weight-title);
1482
1492
  line-height: 1;
1483
1493
  color: var(--color-on-surface);
@@ -1486,55 +1496,22 @@ button.home-project-inventory-row {
1486
1496
  .home-project-inventory-body {
1487
1497
  display: flex;
1488
1498
  flex-direction: column;
1489
- justify-content: flex-start;
1490
- align-self: flex-start;
1491
- gap: 0.2rem;
1499
+ justify-content: center;
1500
+ align-self: center;
1501
+ gap: 0.15rem;
1492
1502
  flex: 1 1 0;
1493
1503
  min-width: 0;
1494
1504
  padding-top: 0;
1495
1505
  }
1496
1506
 
1497
- .home-project-inventory-resources {
1498
- display: flex;
1499
- flex-direction: column;
1500
- justify-content: flex-start;
1501
- align-items: center;
1502
- align-self: flex-start;
1503
- flex: 0 0 auto;
1504
- gap: var(--home-inventory-icon-gap);
1505
- width: var(--home-inventory-icon-size);
1506
- min-height: var(--home-inventory-resources-height);
1507
- position: relative;
1508
- z-index: 1;
1509
- }
1510
-
1511
- /* Neutral marks — same voice as list descriptions, not primary CTAs. */
1512
- .home-project-inventory-resource {
1513
- display: inline-flex;
1514
- align-items: center;
1515
- justify-content: center;
1516
- width: var(--home-inventory-icon-size);
1517
- height: var(--home-inventory-icon-size);
1518
- flex: 0 0 auto;
1519
- background: transparent;
1520
- color: var(--color-on-surface-variant);
1521
- border: 0;
1522
- }
1523
-
1524
- .home-project-inventory-resource .material-symbols-outlined {
1525
- font-size: 1rem;
1526
- line-height: 1;
1527
- font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 20;
1528
- }
1529
-
1530
1507
  .home-project-inventory-code .ledger-meta {
1531
1508
  font-size: var(--type-label-lg);
1532
1509
  }
1533
1510
 
1534
1511
  .home-project-inventory-title {
1535
- margin: 0.2rem 0 0;
1512
+ margin: 0;
1536
1513
  font-family: var(--font-content-title);
1537
- font-size: calc(var(--type-card) - 2pt);
1514
+ font-size: var(--type-card);
1538
1515
  font-weight: var(--weight-title);
1539
1516
  line-height: 1.2;
1540
1517
  color: var(--color-on-surface);
@@ -1549,17 +1526,6 @@ button.home-project-inventory-row {
1549
1526
  color: var(--color-primary);
1550
1527
  }
1551
1528
 
1552
- .home-project-inventory-copy {
1553
- margin: 0;
1554
- font-size: var(--type-label-lg);
1555
- font-weight: var(--weight-body);
1556
- line-height: 1.35;
1557
- color: var(--color-on-surface-variant);
1558
- overflow: hidden;
1559
- text-overflow: ellipsis;
1560
- white-space: nowrap;
1561
- }
1562
-
1563
1529
  /* Two classes: the glyph also carries .material-symbols-outlined, whose
1564
1530
  font stylesheet loads after this one and sets display. */
1565
1531
  .home-project-inventory-row .home-project-inventory-glyph {
@@ -1901,7 +1867,7 @@ button.home-project-inventory-row {
1901
1867
  margin-top: 0;
1902
1868
  font-size: var(--type-body);
1903
1869
  line-height: var(--leading-body);
1904
- color: var(--color-on-surface);
1870
+ color: var(--color-legend);
1905
1871
  }
1906
1872
 
1907
1873
  .home-hero-summary p {
@@ -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.10.1"
27
+ # GitHub tag: gem "pandorga", github: "alexandrelheinen/pandorga", tag: "v1.11.0"
28
28
  gem "csv"
29
29
  gem "base64"
30
30
  RUBY
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pandorga
4
- VERSION = "1.10.1"
4
+ VERSION = "1.11.0"
5
5
  end
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "project-inventory-row/rack card (PLT-AC-15)",
2
+ "name": "project-inventory-row/rack card (PLT-AC-41)",
3
3
  "block": "renderProjectInventoryRow",
4
4
  "args": [
5
5
  {
@@ -25,28 +25,25 @@
25
25
  "selectors": [
26
26
  "button.home-project-inventory-row",
27
27
  "div.home-project-inventory-media",
28
- "div.ledger-poster",
28
+ "div.home-project-inventory-mark",
29
29
  "div.home-project-inventory-code",
30
30
  "span.home-project-inventory-index",
31
31
  "h3.home-project-inventory-title",
32
- "p.home-project-inventory-copy",
33
- "span.home-project-inventory-glyph",
34
- "div.home-project-inventory-resources",
35
- "span.home-project-inventory-resource"
32
+ "span.home-project-inventory-glyph"
36
33
  ],
37
34
  "forbidden": [
38
35
  "ledger-card-stretch",
39
- "role=\"button\""
36
+ "role=\"button\"",
37
+ "ledger-poster",
38
+ "home-project-inventory-copy",
39
+ "home-project-inventory-resources",
40
+ "home-project-inventory-resource"
40
41
  ],
41
42
  "contains": [
42
43
  ">02<",
43
44
  ">Simulation<",
44
45
  ">ARCO<",
45
- ">A city-scale race replayed from telemetry.<",
46
46
  ">route<",
47
- ">code<",
48
- ">article<",
49
- ">public<",
50
47
  "aria-pressed=\"false\"",
51
48
  "data-slug=\"arco\""
52
49
  ]
@@ -226,6 +226,63 @@ sheets.each do |path|
226
226
  end
227
227
  end
228
228
 
229
+ # PLT-AC-41. Home project inventory rows use the square icon mark, two-line
230
+ # code + title body, and no tagline preview or attached-resource icons; on
231
+ # desktop the specimen (35%, min 20rem) and rack (45%, min 21rem) start at the
232
+ # band's left edge across one --space-xl gap, the specimen sets the bench
233
+ # height, rack titles use --type-card, the specimen strip wraps instead of
234
+ # clipping, and below 900px the rack tile glyphs are 1.375rem.
235
+ home_sheet = strip_comments(File.read(CSS_ROOT.join("ledger/home.css")))
236
+ %w[
237
+ home-project-inventory-copy
238
+ home-project-inventory-resources
239
+ home-project-inventory-resource
240
+ ].each do |retired_home|
241
+ if home_sheet.match?(/[.#]#{Regexp.escape(retired_home)}\b/)
242
+ errors << "assets/css/ledger/home.css: .#{retired_home} must be removed (PLT-AC-41)"
243
+ end
244
+ end
245
+ require_declarations(
246
+ errors, "assets/css/ledger/home.css", home_sheet,
247
+ ".home-project-inventory-media", ["aspect-ratio: 1 / 1"]
248
+ )
249
+ require_declarations(
250
+ errors, "assets/css/ledger/home.css", home_sheet,
251
+ ".home-projects-bench", ["grid-template-columns: max(35%, 20rem) max(45%, 21rem)", "column-gap: var(--space-xl)", "justify-content: start", "--home-inventory-row-max: calc(1.6 * var(--home-inventory-row-min))"]
252
+ )
253
+ require_declarations(
254
+ errors, "assets/css/ledger/home.css", home_sheet,
255
+ ".home-specimen-strip .ledger-meta", ["display: block", "white-space: normal"]
256
+ )
257
+ require_declarations(
258
+ errors, "assets/css/ledger/home.css", home_sheet,
259
+ ".home-project-inventory-title", ["font-size: var(--type-card)"]
260
+ )
261
+ require_declarations(
262
+ errors, "assets/css/ledger/home.css", home_sheet,
263
+ ".home-project-inventory-row .home-project-inventory-glyph", ["font-size: 1.375rem"]
264
+ )
265
+ [
266
+ "45.6%",
267
+ "minmax(0, 7fr)",
268
+ "--home-inventory-min-height",
269
+ "calc(var(--type-card) - 2pt)"
270
+ ].each do |forbidden_home|
271
+ if home_sheet.include?(forbidden_home)
272
+ errors << "assets/css/ledger/home.css: #{forbidden_home} must be removed (PLT-AC-41)"
273
+ end
274
+ end
275
+
276
+ # PLT-AC-23. The home hero summary uses --color-legend, the ink of the other
277
+ # description texts.
278
+ require_declarations(
279
+ errors, "assets/css/ledger/home.css", home_sheet,
280
+ ".home-hero-summary", ["color: var(--color-legend)"]
281
+ )
282
+ if declaration_bodies(home_sheet, ".home-hero-summary").any? { |body| declarations(body).include?("color:var(--color-on-surface)") }
283
+ errors << "assets/css/ledger/home.css: .home-hero-summary must use --color-legend (PLT-AC-23)"
284
+ end
285
+
229
286
  fail!("CSS structure problems:\n - #{errors.join("\n - ")}") unless errors.empty?
230
287
 
231
288
  puts " #{sheets.size} stylesheet(s) balanced, no conflict markers."
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Refs: PLT-AC-42 — home listing bands emit kicker, title, and note slots in both normal and agnostic builds.
4
+ require "pathname"
5
+ require "open3"
6
+ require "fileutils"
7
+ require "tmpdir"
8
+
9
+ ROOT = Pathname.new(__dir__).join("../..").expand_path
10
+ EXE = ROOT.join("exe/pandorga")
11
+ EXAMPLE = ROOT.join("examples/full")
12
+
13
+ def fail!(message)
14
+ warn "FAIL test-home-band-intro: #{message}"
15
+ exit 1
16
+ end
17
+
18
+ def run!(*cmd, chdir:)
19
+ env = {
20
+ "RUBYOPT" => "-I#{ROOT.join('lib')}",
21
+ "PANDORGA_SITE_ROOT" => chdir.to_s,
22
+ "CONTENT_API_BASE_URL" => "",
23
+ "CONTENT_EXPORT_SKIP_VALIDATE" => "1",
24
+ "JEKYLL_ENV" => "production"
25
+ }
26
+ stdout, stderr, status = Open3.capture3(env, *cmd, chdir: chdir.to_s)
27
+ return [stdout, stderr] if status.success?
28
+
29
+ fail!("#{cmd.join(' ')} (cwd=#{chdir})\n#{stdout}\n#{stderr}")
30
+ end
31
+
32
+ def listing_sections(html)
33
+ sections = []
34
+ html.scan(/<section\b[^>]*\bdata-home-kind="([^"]+)"[^>]*>.*?<\/section>/m) do
35
+ section = Regexp.last_match(0)
36
+ kind = Regexp.last_match(1)
37
+ next if kind == "index"
38
+
39
+ key = section[/\bdata-home-key="([^"]+)"/, 1] || kind
40
+ banner_idx = section.index('data-content-home-section="')
41
+ fail!("section #{key} missing data-content-home-section") unless banner_idx
42
+
43
+ sections << [key, section[banner_idx..]]
44
+ end
45
+ sections
46
+ end
47
+
48
+ run!("bundle", "install", "--quiet", chdir: EXAMPLE) if EXAMPLE.join("Gemfile").file?
49
+ run!("ruby", EXE.to_s, "export", "content", "_content_json", chdir: EXAMPLE)
50
+
51
+ Dir.mktmpdir("pandorga-home-band-normal-") do |normal_dir|
52
+ run!("bundle", "exec", "jekyll", "build", "--quiet", "-d", normal_dir, chdir: EXAMPLE)
53
+ html = File.read(File.join(normal_dir, "index.html"))
54
+ sections = listing_sections(html)
55
+ fail!("normal build produced no listing sections") if sections.empty?
56
+
57
+ sections.each do |key, slice|
58
+ %w[data-content-section-kicker data-content-section-title data-content-section-note].each do |attr|
59
+ fail!("normal build band #{key} missing #{attr}") unless slice.include?(attr)
60
+ end
61
+ end
62
+
63
+ projects = sections.assoc("projects")&.last
64
+ fail!("normal build missing projects band") unless projects
65
+ unless projects.include?("Portfolio systems in the full example.")
66
+ fail!("normal build projects band missing note text from headers.yml")
67
+ end
68
+ end
69
+
70
+ Dir.mktmpdir("pandorga-home-band-agnostic-") do |agnostic_dir|
71
+ agnostic_cfg = File.join(agnostic_dir, "agnostic.yml")
72
+ File.write(agnostic_cfg, "shell_content_agnostic: true\n")
73
+ out_dir = File.join(agnostic_dir, "site")
74
+ run!(
75
+ "bundle", "exec", "jekyll", "build", "--quiet",
76
+ "--config", "_config.yml,#{agnostic_cfg}",
77
+ "-d", out_dir,
78
+ chdir: EXAMPLE
79
+ )
80
+ html = File.read(File.join(out_dir, "index.html"))
81
+ sections = listing_sections(html)
82
+ fail!("agnostic build produced no listing sections") if sections.empty?
83
+
84
+ sections.each do |key, slice|
85
+ %w[data-content-section-kicker data-content-section-title data-content-section-note].each do |attr|
86
+ fail!("agnostic build band #{key} missing #{attr}") unless slice.include?(attr)
87
+ end
88
+ end
89
+
90
+ projects = sections.assoc("projects")&.last
91
+ fail!("agnostic build missing projects band") unless projects
92
+ unless projects.include?("data-content-section-note></p>")
93
+ fail!("agnostic build projects band note slot is not empty")
94
+ end
95
+ end
96
+
97
+ puts "PASS test-home-band-intro"
data/scripts/validate.sh CHANGED
@@ -55,6 +55,7 @@ TESTS=(
55
55
  scripts/test/test-r2-json-sync.rb
56
56
  scripts/test/test-writing-slug-limits.rb
57
57
  scripts/test/test-examples-build.rb
58
+ scripts/test/test-home-band-intro.rb
58
59
  scripts/test/test-validate-allowlist.rb
59
60
  scripts/test/test-shell-a11y.rb
60
61
  scripts/test/test-theme-contrast.rb
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pandorga
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.1
4
+ version: 1.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - The Pandorga contributors
@@ -435,6 +435,7 @@ files:
435
435
  - scripts/test/test-hero-summary.rb
436
436
  - scripts/test/test-home-articles.rb
437
437
  - scripts/test/test-home-band-art.rb
438
+ - scripts/test/test-home-band-intro.rb
438
439
  - scripts/test/test-home-lists.rb
439
440
  - scripts/test/test-hydrate-reserve.rb
440
441
  - scripts/test/test-install-functions.rb