pandorga 1.14.0 → 2.0.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/DESIGN.md +15 -30
  4. data/_includes/content-runtime/43-blocks.html +0 -3
  5. data/_includes/content-runtime/60-fragments.html +83 -149
  6. data/_includes/content-runtime/90-bootstrap.html +3 -0
  7. data/_includes/cv/cv-box.html +10 -4
  8. data/_includes/home/band-index.html +22 -7
  9. data/_includes/home/band-listing.html +7 -3
  10. data/_includes/page/timeline-section.html +8 -3
  11. data/_includes/theme/theme-vars-block.liquid +3 -0
  12. data/_layouts/articles.html +0 -2
  13. data/_layouts/bibliography.html +0 -2
  14. data/_layouts/blog.html +0 -5
  15. data/_layouts/cv.html +27 -8
  16. data/_layouts/home.html +2 -13
  17. data/_layouts/projects.html +17 -9
  18. data/_layouts/resources.html +0 -9
  19. data/_layouts/shell.html +1 -7
  20. data/assets/css/article.css +10 -2
  21. data/assets/css/base.css +68 -44
  22. data/assets/css/ledger/bibliography.css +0 -31
  23. data/assets/css/ledger/chrome.css +51 -2
  24. data/assets/css/ledger/cv.css +0 -40
  25. data/assets/css/ledger/detail.css +20 -6
  26. data/assets/css/ledger/home.css +261 -402
  27. data/assets/css/ledger/projects.css +17 -19
  28. data/assets/css/ledger/rascunhos.css +0 -27
  29. data/assets/css/ledger/sources.css +0 -57
  30. data/assets/css/ledger/writing.css +0 -62
  31. data/lib/pandorga/commands/new.rb +2 -2
  32. data/lib/pandorga/commands/thumbnails.rb +3 -2
  33. data/lib/pandorga/registry/schema.yml +0 -3
  34. data/lib/pandorga/thumbnail_variants.rb +29 -3
  35. data/lib/pandorga/version.rb +1 -1
  36. data/scripts/check_home_lists.mjs +33 -5
  37. data/scripts/content/hero_portraits.rb +59 -11
  38. data/scripts/test/test-css-structure.rb +0 -1
  39. data/scripts/test/test-hero-16x9.rb +379 -0
  40. data/scripts/test/test-hero-portraits.mjs +10 -20
  41. data/scripts/test/test-hero-portraits.rb +31 -25
  42. data/scripts/test/test-highlighter-pills.rb +12 -2
  43. data/scripts/test/test-home-lists.rb +5 -0
  44. data/scripts/test/test-new.rb +6 -6
  45. data/scripts/test/test-shell-a11y.rb +2 -1
  46. data/scripts/test/test-studio-section-texts.rb +625 -0
  47. data/scripts/test/test-theme-contrast.rb +13 -2
  48. data/scripts/test/test-v3-layout.rb +559 -0
  49. data/scripts/validate.sh +3 -0
  50. data/studio/assets/{index-BJyN-kB3.js → index-B9h1WUYY.js} +1 -1
  51. data/studio/index.html +1 -1
  52. data/studio/schema.yml +126 -2
  53. data/studio-app/src/lib/yaml-doc.js +6 -0
  54. data/templates/network/studio.yml +10 -0
  55. metadata +5 -2
@@ -21,11 +21,11 @@
21
21
  }
22
22
 
23
23
  .home-band--hero {
24
- background-color: var(--color-background);
24
+ background-color: transparent;
25
25
  }
26
26
 
27
27
  .home-band--contrast {
28
- background-color: var(--color-panel-contrast);
28
+ background-color: transparent;
29
29
  background-image: none;
30
30
  }
31
31
 
@@ -104,40 +104,6 @@
104
104
  }
105
105
  }
106
106
 
107
- /* Ambient pool behind the hero.
108
- *
109
- * Same weight as the earlier primary-container bloom, but keyed to neutral
110
- * ink/outline so it stays grey paper depth instead of a blue spotlight. */
111
- .home-band--hero::before {
112
- content: "";
113
- position: absolute;
114
- z-index: -1;
115
- top: -18%;
116
- right: 8%;
117
- width: min(38rem, 60%);
118
- aspect-ratio: 1;
119
- border-radius: 50%;
120
- background: radial-gradient(circle,
121
- color-mix(in srgb, var(--color-outline) 22%, transparent) 0%,
122
- color-mix(in srgb, var(--color-on-surface) 8%, transparent) 42%,
123
- transparent 72%);
124
- filter: blur(52px);
125
- pointer-events: none;
126
- }
127
-
128
- [data-theme="dark"] .home-band--hero::before {
129
- background: radial-gradient(circle,
130
- color-mix(in srgb, var(--color-on-surface) 10%, transparent) 0%,
131
- color-mix(in srgb, var(--color-outline) 8%, transparent) 50%,
132
- transparent 70%);
133
- filter: blur(60px);
134
- }
135
-
136
- @media (prefers-reduced-motion: no-preference) {
137
- .home-band--hero::before {
138
- transition: opacity var(--motion-duration-slow, 400ms) var(--motion-ease-default);
139
- }
140
- }
141
107
 
142
108
  .home-hero {
143
109
  display: grid;
@@ -146,64 +112,6 @@
146
112
  align-items: start;
147
113
  }
148
114
 
149
- @media (min-width: 1024px) {
150
- .home-hero {
151
- /* Copy content height sets the flex line. Portrait stretches to that
152
- height; aspect-ratio then sets W = 3/4 × H. Children are taken out of
153
- flow so the photo's intrinsic box cannot inflate the line or collapse
154
- the copy. */
155
- display: flex;
156
- flex-direction: row;
157
- align-items: stretch;
158
- gap: var(--space-2xl);
159
- }
160
-
161
- .home-hero-copy {
162
- flex: 1 1 auto;
163
- min-width: min(100%, 20rem);
164
- }
165
-
166
- .home-hero-portrait {
167
- position: relative;
168
- flex: 0 0 auto;
169
- align-self: stretch;
170
- width: auto;
171
- height: auto;
172
- aspect-ratio: 3 / 4;
173
- overflow: hidden;
174
- min-width: 0;
175
- min-height: 0;
176
- max-width: none;
177
- justify-self: unset;
178
- }
179
-
180
- .home-hero-plate-wrap {
181
- position: absolute;
182
- inset: 0;
183
- width: 100%;
184
- height: 100%;
185
- flex: none;
186
- }
187
-
188
- .home-hero-plate {
189
- position: absolute;
190
- inset: 0;
191
- width: 100%;
192
- height: 100%;
193
- aspect-ratio: auto;
194
- flex: none;
195
- }
196
-
197
- .home-hero-plate img {
198
- position: absolute;
199
- inset: 0;
200
- width: 100%;
201
- height: 100%;
202
- max-width: none;
203
- object-fit: cover;
204
- }
205
- }
206
-
207
115
  .home-hero-copy {
208
116
  display: flex;
209
117
  flex-direction: column;
@@ -327,8 +235,6 @@
327
235
  .home-hero-portrait {
328
236
  position: relative;
329
237
  width: 100%;
330
- max-width: 16rem;
331
- justify-self: end;
332
238
  display: flex;
333
239
  flex-direction: column;
334
240
  min-height: 0;
@@ -368,18 +274,16 @@
368
274
  transform: translateY(0);
369
275
  }
370
276
 
371
- /* ── Portrait plate ──
372
- Beside the copy on a wide page the plate is portrait (3:4): its height
373
- matches the left column and the width is W = 3/4 × H. On a phone it stays
374
- 3:4. A wider box would cover-crop that portrait a second time. When
375
- hero_landscape is set, or a portrait entry has its own landscape, the
376
- phone plate is 16:9 and the chosen 16:9 file is shown whole. */
277
+ /* ── Photograph plate ──
278
+ One 16:9 frame at every width, the shape of the hero photographs, so
279
+ below 1024px cover does not crop them. From 1024px the plate fills a box
280
+ as tall as the copy and cover trims the sides or the top (PLT-AC-51). */
377
281
  .home-hero-plate {
378
282
  position: relative;
379
283
  z-index: 1;
380
284
  margin: 0;
381
285
  overflow: hidden;
382
- aspect-ratio: 3 / 4;
286
+ aspect-ratio: 16 / 9;
383
287
  /* Black in both themes: the photograph fades in over this, so a theme
384
288
  canvas here would read as the wrong picture arriving late. */
385
289
  background: black;
@@ -401,64 +305,13 @@ html[data-theme="light"] .home-hero-plate {
401
305
  box-shadow: none;
402
306
  }
403
307
 
404
- /* Silk facet behind the portrait — restored; the Borders pass had hidden it. */
405
- .home-hero-shard {
406
- top: -2.25rem;
407
- right: -2rem;
408
- width: 15rem;
409
- height: 15rem;
410
- background: var(--color-primary);
411
- opacity: var(--cubist-shard-opacity-strong, 16%);
412
- z-index: 0;
413
- }
414
-
415
- @media (max-width: 1023px) {
416
- .home-hero-shard {
417
- width: 11rem;
418
- height: 11rem;
419
- top: -1.5rem;
420
- right: -1.25rem;
421
- }
422
- }
423
308
 
424
- @media (max-width: 1023px) {
425
- .home-hero-portrait::before {
426
- content: "";
427
- position: absolute;
428
- z-index: 0;
429
- top: 55%;
430
- left: 50%;
431
- transform: translate(-50%, -50%);
432
- width: 140%;
433
- height: 120%;
434
- border-radius: 50%;
435
- background: radial-gradient(circle,
436
- color-mix(in srgb, var(--color-outline) 18%, transparent) 0%,
437
- color-mix(in srgb, var(--color-on-surface) 7%, transparent) 45%,
438
- transparent 72%);
439
- filter: blur(40px);
440
- pointer-events: none;
441
- }
442
-
443
- [data-theme="dark"] .home-hero-portrait::before {
444
- background: radial-gradient(circle,
445
- color-mix(in srgb, var(--color-on-surface) 12%, transparent) 0%,
446
- transparent 70%);
447
- }
448
- }
449
-
450
- .home-hero-plate picture {
451
- display: block;
452
- width: 100%;
453
- height: 100%;
454
- margin: 0;
455
- }
456
309
 
457
310
  .home-hero-plate img {
458
311
  width: 100%;
459
312
  height: 100%;
460
313
  object-fit: cover;
461
- object-position: 52% 18%;
314
+ object-position: center;
462
315
  display: block;
463
316
  filter: saturate(1.05) contrast(0.98);
464
317
  opacity: 0;
@@ -472,27 +325,6 @@ html[data-theme="light"] .home-hero-plate {
472
325
  opacity: 1;
473
326
  }
474
327
 
475
- /* 16:9 file in a 16:9 plate. contain shows the whole frame. The image stays
476
- hidden until the chosen file has loaded, so a fallback source does not
477
- flash before a pool landscape replaces it. The plate is already 16:9, so
478
- the wait does not move the layout. transition none so the paint is not a fade. */
479
- @media (max-width: 767px) {
480
- .home-hero-plate--landscape {
481
- aspect-ratio: 16 / 9;
482
- }
483
-
484
- .home-hero-plate--landscape img {
485
- object-fit: contain;
486
- object-position: center center;
487
- opacity: 0;
488
- transition: none;
489
- }
490
-
491
- .home-hero-plate--landscape img.is-shown {
492
- opacity: 1;
493
- }
494
- }
495
-
496
328
  .home-hero-plate:hover img {
497
329
  transform: scale(1.02);
498
330
  }
@@ -506,10 +338,86 @@ html[data-theme="light"] .home-hero-plate {
506
338
  }
507
339
  }
508
340
 
341
+ /* ── Hero from 1024px ──
342
+ The copy takes 46% and the name sets on two lines, given name over family
343
+ name, so the photograph takes the rest of the row. The copy sets the band
344
+ height; the plate wrapper and the plate leave the flow and the photograph
345
+ covers that box, at least 15rem tall, like the featured article poster.
346
+ This block follows the base hero rules on purpose: before them, their
347
+ max-width, position and aspect-ratio won on source order (PLT-AC-51). */
348
+ @media (min-width: 1024px) {
349
+ .home-hero {
350
+ display: flex;
351
+ flex-direction: row;
352
+ align-items: stretch;
353
+ gap: var(--space-2xl);
354
+ }
355
+
356
+ .home-hero-copy {
357
+ flex: 0 0 46%;
358
+ min-width: 0;
359
+ }
360
+
361
+ .home-hero-portrait {
362
+ flex: 1 1 0;
363
+ align-self: stretch;
364
+ width: auto;
365
+ min-width: 0;
366
+ min-height: 15rem;
367
+ max-width: none;
368
+ aspect-ratio: auto;
369
+ overflow: hidden;
370
+ }
371
+
372
+ .home-hero-plate-wrap {
373
+ position: absolute;
374
+ inset: 0;
375
+ aspect-ratio: auto;
376
+ }
377
+
378
+ .home-hero-plate {
379
+ position: absolute;
380
+ inset: 0;
381
+ width: 100%;
382
+ height: 100%;
383
+ aspect-ratio: auto;
384
+ }
385
+
386
+ .home-hero-plate img {
387
+ position: absolute;
388
+ inset: 0;
389
+ width: 100%;
390
+ height: 100%;
391
+ max-width: none;
392
+ object-fit: cover;
393
+ object-position: center;
394
+ }
395
+
396
+ /* --hero-name-fit is the longer name part in em at the title's tracking;
397
+ a site with a longer word raises it. The selector outranks a site's
398
+ one-line size written as .home-ledger .home-hero-title. */
399
+ .home-ledger .home-hero-title {
400
+ white-space: normal;
401
+ font-size: min(calc(100cqi / var(--hero-name-fit, 8.4)), var(--hero-name-max, 4.5rem));
402
+ }
403
+
404
+ .home-hero-given,
405
+ .home-hero-family {
406
+ display: inline-block;
407
+ white-space: nowrap;
408
+ }
409
+ }
410
+
509
411
  /* ── Section banners ──
510
412
  Atelier plate edge: a denser hairline, a longer accent rule, and a short
511
- vertical tick so each band reads as a ruled plate rather than a soft fade. */
413
+ vertical tick so each band reads as a ruled plate rather than a soft fade.
414
+ The banner is a 2-column grid so the lead's children join the template. */
512
415
  .home-band:not(.home-band--hero) .ledger-section-banner {
416
+ display: grid;
417
+ grid-template-columns: minmax(0, 1fr) auto;
418
+ column-gap: var(--space-md);
419
+ row-gap: var(--space-xs);
420
+ align-items: baseline;
513
421
  padding-top: var(--space-lg);
514
422
  padding-bottom: var(--space-sm);
515
423
  border-top: 1px solid var(--border-gentle);
@@ -537,50 +445,58 @@ html[data-theme="light"] .home-hero-plate {
537
445
  background: color-mix(in srgb, var(--color-primary) 55%, var(--color-outline));
538
446
  }
539
447
 
540
- .home-section-aside {
541
- display: flex;
542
- flex-direction: column;
543
- align-items: flex-start;
544
- gap: var(--space-sm);
545
- max-width: 30rem;
448
+ .home-band .ledger-section-lead {
449
+ display: contents;
546
450
  }
547
451
 
548
- /* The aside sits at the end of the banner, so its contents align to that same
549
- edge — button and copy on one axis. Aligning the button left while the copy
550
- ran right made every band look like a zigzag. */
551
- @media (min-width: 768px) {
552
- .home-section-aside {
553
- align-items: flex-end;
554
- text-align: right;
555
- }
452
+ .home-band .ledger-section-banner .ledger-kicker {
453
+ grid-column: 1 / -1;
454
+ grid-row: 1;
455
+ }
556
456
 
557
- .home-section-aside .ledger-cta {
558
- align-self: flex-end;
559
- }
457
+ .home-band .ledger-kicker:empty {
458
+ display: none;
560
459
  }
561
460
 
562
- /* The lead column of a banner holds the kicker, the title and the note, all
563
- three filled from content/pages/headers.yml. It has to be able to
564
- shrink: the banner is a flex row and the aside beside it is sized by its
565
- button, so without this the note pushes the button off the line. */
461
+ .home-band .ledger-section-banner .section-title {
462
+ grid-column: 1;
463
+ grid-row: 2;
464
+ margin: 0;
465
+ font-size: var(--type-section-sm);
466
+ }
467
+
468
+ .home-band .home-section-aside {
469
+ grid-column: 2;
470
+ grid-row: 2;
471
+ align-self: baseline;
472
+ margin-top: 0;
473
+ }
474
+
475
+ /* Outside the home bands the banner is still a flex row, so the lead has to
476
+ be able to shrink or the note pushes the aside off the line. */
566
477
  .ledger-section-banner .ledger-section-lead {
567
478
  min-width: 0;
568
479
  }
569
480
 
570
- /* One line under the title saying what the band holds. It is prose rather than
571
- a chrome label, so it takes the body face and the reading measure instead of
572
- the uppercase treatment `.ledger-section-note` carries in base.css — that
573
- one is the right-hand tally at the end of a banner, which is a different
574
- thing standing in a different place.
575
-
576
- The element is removed outright when the field is empty (see
577
- hydrateHomeSections), so this margin never opens a gap of its own. */
578
- .home-section-note {
579
- margin: var(--space-sm) 0 0;
481
+ .home-band .home-section-note {
482
+ grid-column: 1 / -1;
483
+ grid-row: 3;
484
+ margin: 0;
485
+ font-size: var(--type-dek);
580
486
  max-width: var(--reading-measure, 45rem);
581
487
  color: var(--color-on-surface-variant);
582
488
  }
583
489
 
490
+ @media (min-width: 768px) {
491
+ .home-band .ledger-section-banner .section-title {
492
+ font-size: var(--type-section);
493
+ }
494
+
495
+ .home-band .home-section-note {
496
+ font-size: var(--type-body);
497
+ }
498
+ }
499
+
584
500
  /* ── Featured article band ── */
585
501
  /* The lead is a panel, not a row.
586
502
  It used to be three bare columns sitting on the band background, which is
@@ -611,7 +527,7 @@ html[data-theme="light"] .home-hero-plate {
611
527
  gap: var(--space-md);
612
528
  align-items: stretch;
613
529
  background: var(--color-surface-card);
614
- border: 1.5px solid var(--border-ink);
530
+ border: 1px solid var(--border-ink);
615
531
  border-radius: var(--radius-card);
616
532
  border-top: 2px solid var(--color-primary);
617
533
  padding: var(--space-md) var(--space-lg);
@@ -621,6 +537,28 @@ html[data-theme="light"] .home-hero-plate {
621
537
  margin-bottom: var(--space-lg);
622
538
  }
623
539
 
540
+ @media (max-width: 767px) {
541
+ /* Stacks image first edge to edge on narrow screens. */
542
+ .home-featured {
543
+ display: flex;
544
+ flex-direction: column;
545
+ padding: 0;
546
+ }
547
+
548
+ .home-featured > .ledger-poster {
549
+ order: -1;
550
+ width: 100%;
551
+ height: auto;
552
+ margin: 0;
553
+ aspect-ratio: 16 / 9;
554
+ border: 0;
555
+ }
556
+
557
+ .home-featured-lead {
558
+ padding: var(--space-md);
559
+ }
560
+ }
561
+
624
562
  /* The hover rung, same as `.ledger-card` takes: the panel is one link target
625
563
  end to end, so the whole surface answers. */
626
564
  .home-featured:hover {
@@ -652,65 +590,6 @@ html[data-theme="light"] .home-hero-plate {
652
590
  }
653
591
  }
654
592
 
655
- /* Shards on the featured panel, which carried none: the one card the home
656
- page leads with was also the only one on the band with no footing in the
657
- cubist half of the identity.
658
-
659
- The pair is placed around the poster rather than across the card, because
660
- the poster is opaque and a shard behind it is a shard nobody sees. The
661
- alpha takes the top-left, over the index column, where there is nothing but
662
- chrome and air. The beta takes the bottom-right and comes out from *under*
663
- the poster into the strip below it, which is the same move the index band
664
- makes with its own plates.
665
-
666
- Hues are the two the home band does not already spend: the index panels
667
- below run secondary and primary, so this pair runs tertiary and secondary,
668
- and neither repeats the corner its neighbor uses. Shards are absolutely
669
- positioned, so they are not grid items and the three-column template is
670
- untouched. */
671
- .home-featured-shard {
672
- left: -3rem;
673
- top: -3.5rem;
674
- width: 11rem;
675
- height: 11rem;
676
- background: var(--color-tertiary);
677
- }
678
-
679
- .home-featured-shard--beta {
680
- left: auto;
681
- right: -3rem;
682
- top: auto;
683
- bottom: -4rem;
684
- width: 13rem;
685
- height: 13rem;
686
- background: var(--color-secondary);
687
- }
688
-
689
- /* Below the two-column layout the panel is short and the columns stack, so a
690
- shard sized for the wide card reaches the title.
691
-
692
- The beta restates `top`/`left` as `auto`. It carries both class names, so
693
- the rule above also matches it, and an override that gives it a `bottom`
694
- without taking back the `top` leaves it with all four offsets set — at
695
- which point top and left win and the pair collapses into one corner. */
696
- @media (max-width: 1023px) {
697
- .home-featured-shard {
698
- width: 8rem;
699
- height: 8rem;
700
- top: -2.5rem;
701
- left: -2.5rem;
702
- }
703
-
704
- .home-featured-shard--beta {
705
- width: 9rem;
706
- height: 9rem;
707
- top: auto;
708
- left: auto;
709
- bottom: -3rem;
710
- right: -2.5rem;
711
- }
712
- }
713
-
714
593
  /* The columns sit clear of both. */
715
594
  .home-featured> :not(.shard-accent) {
716
595
  position: relative;
@@ -800,6 +679,18 @@ html[data-theme="light"] .home-hero-plate {
800
679
  font-weight: var(--weight-body);
801
680
  line-height: var(--home-featured-copy-line);
802
681
  color: var(--color-legend);
682
+ display: -webkit-box;
683
+ -webkit-box-orient: vertical;
684
+ -webkit-line-clamp: 3;
685
+ line-clamp: 3;
686
+ overflow: hidden;
687
+ }
688
+
689
+ @media (min-width: 768px) {
690
+ .home-featured-copy {
691
+ -webkit-line-clamp: 4;
692
+ line-clamp: 4;
693
+ }
803
694
  }
804
695
 
805
696
  .home-featured-foot {
@@ -918,7 +809,6 @@ html[data-theme="light"] .home-hero-plate {
918
809
  border-top-width: 2px;
919
810
  border-top-style: solid;
920
811
  border-top-color: var(--color-tertiary);
921
- transform: rotate(0deg);
922
812
  transition: transform var(--motion-duration-fast) var(--motion-ease-default),
923
813
  background-color var(--motion-duration-fast) var(--motion-ease-default),
924
814
  border-color var(--motion-duration-fast) var(--motion-ease-default);
@@ -931,25 +821,73 @@ html[data-theme="light"] .home-hero-plate {
931
821
 
932
822
  /* A handful of fixed degrees per grid position — not a JS random() — so the
933
823
  tilt is identical on every render and every screenshot. */
934
- .home-draft-list>*:nth-child(4n + 1) {
935
- transform: rotate(-1.2deg);
936
- }
824
+ @media (min-width: 768px) {
825
+ .home-draft-list>*:nth-child(4n + 1) {
826
+ transform: rotate(-1.2deg);
827
+ }
937
828
 
938
- .home-draft-list>*:nth-child(4n + 2) {
939
- transform: rotate(0.9deg);
940
- }
829
+ .home-draft-list>*:nth-child(4n + 2) {
830
+ transform: rotate(0.9deg);
831
+ }
941
832
 
942
- .home-draft-list>*:nth-child(4n + 3) {
943
- transform: rotate(-0.7deg);
944
- }
833
+ .home-draft-list>*:nth-child(4n + 3) {
834
+ transform: rotate(-0.7deg);
835
+ }
836
+
837
+ .home-draft-list>*:nth-child(4n) {
838
+ transform: rotate(1.1deg);
839
+ }
945
840
 
946
- .home-draft-list>*:nth-child(4n) {
947
- transform: rotate(1.1deg);
841
+ .home-draft-card:hover,
842
+ .home-draft-card:focus-within {
843
+ transform: rotate(0deg);
844
+ }
948
845
  }
949
846
 
950
- .home-draft-card:hover,
951
- .home-draft-card:focus-within {
952
- transform: rotate(0deg);
847
+ @media (max-width: 767px) {
848
+ .home-draft-list>.home-draft-card:nth-child(3) {
849
+ grid-column: 1 / -1;
850
+ flex-direction: row;
851
+ }
852
+
853
+ .home-draft-list>.home-draft-card:nth-child(3) .ledger-poster {
854
+ flex: 0 0 38%;
855
+ aspect-ratio: auto;
856
+ align-self: stretch;
857
+ }
858
+
859
+ .home-draft-list>.home-draft-card:nth-child(3) .ledger-poster img,
860
+ .home-draft-list>.home-draft-card:nth-child(3) .ledger-poster video {
861
+ position: absolute;
862
+ inset: 0;
863
+ }
864
+
865
+ .home-draft-list>.home-draft-card:nth-child(3) .ledger-card-body {
866
+ flex: 1 1 auto;
867
+ min-width: 0;
868
+ }
869
+
870
+ .home-draft-list>.home-draft-card:nth-child(n + 4) {
871
+ display: none;
872
+ }
873
+
874
+ .home-draft-card .ledger-poster {
875
+ aspect-ratio: 4 / 3;
876
+ }
877
+
878
+ .home-draft-card .ledger-meta {
879
+ flex-wrap: nowrap;
880
+ white-space: nowrap;
881
+ overflow: hidden;
882
+ }
883
+
884
+ .home-draft-card .ledger-card-title {
885
+ font-size: var(--type-dek);
886
+ }
887
+
888
+ .home-draft-card .ledger-card-copy {
889
+ font-size: var(--type-label);
890
+ }
953
891
  }
954
892
 
955
893
  .home-draft-card .ledger-card-body {
@@ -964,6 +902,11 @@ html[data-theme="light"] .home-hero-plate {
964
902
  .home-draft-card .ledger-card-title {
965
903
  font-size: var(--type-card);
966
904
  line-height: 1.3;
905
+ display: -webkit-box;
906
+ -webkit-line-clamp: 3;
907
+ line-clamp: 3;
908
+ -webkit-box-orient: vertical;
909
+ overflow: hidden;
967
910
  }
968
911
 
969
912
  .home-draft-card .ledger-meta {
@@ -978,30 +921,18 @@ html[data-theme="light"] .home-hero-plate {
978
921
  font-weight: var(--weight-body);
979
922
  line-height: var(--leading-body);
980
923
  color: var(--color-legend);
981
- /* Fill the surplus the equal-height grid hands the body. `height: 0` with
982
- `flex: 1` gives the paragraph a definite block size from the flex
983
- allocation, so line-clamp/overflow clip against that height instead of
984
- growing past the ellipsis (the old flex:1 + clamp:2 bug that left text
985
- after "…" and sliced mid-glyph at the card edge). */
986
924
  flex: 1 1 auto;
987
- /* With the 16:9 plate, this band keeps the whole card about 20% shorter
988
- than the old 16/12.6 poster and 8.4-line excerpt, on narrow and wide grids. */
989
- min-height: calc(var(--type-subtitle) * var(--leading-body) * 5.33);
990
- height: 0;
991
925
  display: -webkit-box;
992
- -webkit-line-clamp: 5;
993
- line-clamp: 5;
926
+ -webkit-line-clamp: 3;
927
+ line-clamp: 3;
994
928
  -webkit-box-orient: vertical;
995
929
  overflow: hidden;
996
930
  }
997
931
 
998
- /* Four-up posters were the tall part of the old card. Give the excerpt a
999
- little of that height back so the wide card is 20% shorter too, not more. */
1000
- @media (min-width: 1024px) {
932
+ @media (min-width: 768px) {
1001
933
  .home-draft-card .ledger-card-copy {
1002
- min-height: calc(var(--type-subtitle) * var(--leading-body) * 6.05);
1003
- -webkit-line-clamp: 6;
1004
- line-clamp: 6;
934
+ -webkit-line-clamp: 4;
935
+ line-clamp: 4;
1005
936
  }
1006
937
  }
1007
938
 
@@ -1073,14 +1004,6 @@ html[data-theme="light"] .home-hero-plate {
1073
1004
  border-top: 2px solid var(--color-primary);
1074
1005
  }
1075
1006
 
1076
- .home-band--contrast .home-project-specimen {
1077
- background: var(--color-surface-card-contrast);
1078
- }
1079
-
1080
- .home-band--contrast .home-project-specimen:hover {
1081
- background: var(--color-surface-high-contrast);
1082
- }
1083
-
1084
1007
  .home-specimen-strip,
1085
1008
  .home-project-inventory-code {
1086
1009
  display: flex;
@@ -1338,8 +1261,8 @@ button.home-project-inventory-row {
1338
1261
  min-width: 0;
1339
1262
  height: var(--home-icon-btn-size);
1340
1263
  padding: 0;
1341
- border: 1px solid var(--border-gentle-divider);
1342
- background: var(--color-panel-contrast);
1264
+ border: 1px solid var(--border-ink);
1265
+ background: var(--color-surface-card);
1343
1266
  cursor: pointer;
1344
1267
  transition: background-color var(--motion-duration-fast) var(--motion-ease-default),
1345
1268
  border-color var(--motion-duration-fast) var(--motion-ease-default);
@@ -1384,12 +1307,6 @@ button.home-project-inventory-row {
1384
1307
  color: var(--color-primary);
1385
1308
  }
1386
1309
 
1387
- .home-band--contrast .home-project-inventory-row:hover,
1388
- .home-band--contrast .home-project-inventory-row:focus-visible,
1389
- .home-band--contrast .home-project-inventory-row.is-selected {
1390
- background: var(--color-surface-high-contrast);
1391
- }
1392
-
1393
1310
  @media (min-width: 900px) {
1394
1311
 
1395
1312
  /* The specimen column is 35% of the band (at least 20rem), which keeps the
@@ -1440,17 +1357,12 @@ button.home-project-inventory-row {
1440
1357
  max-height: var(--home-inventory-row-max);
1441
1358
  box-sizing: border-box;
1442
1359
  padding: var(--space-sm) var(--space-md);
1443
- border: 1px solid var(--border-gentle-divider);
1360
+ border: 1px solid var(--border-ink);
1444
1361
  background: var(--color-surface-card);
1445
1362
  outline: none;
1446
1363
  }
1447
1364
 
1448
- .home-band--contrast .home-project-inventory-row {
1449
- background: var(--color-surface-card-contrast);
1450
- }
1451
-
1452
- .home-project-inventory-row.is-selected,
1453
- .home-band--contrast .home-project-inventory-row.is-selected {
1365
+ .home-project-inventory-row.is-selected {
1454
1366
  background: var(--color-surface-high);
1455
1367
  border-color: var(--border-gentle-divider);
1456
1368
  outline: none;
@@ -1567,23 +1479,6 @@ button.home-project-inventory-row {
1567
1479
  isolation: isolate;
1568
1480
  }
1569
1481
 
1570
- [data-theme="dark"] .home-band--index::before {
1571
- content: "";
1572
- position: absolute;
1573
- z-index: -1;
1574
- top: -8%;
1575
- left: 50%;
1576
- width: min(72rem, 100%);
1577
- height: 116%;
1578
- transform: translateX(-50%);
1579
- border-radius: 50%;
1580
- background: radial-gradient(ellipse,
1581
- color-mix(in srgb, var(--color-primary) 5%, transparent) 0%,
1582
- transparent 70%);
1583
- filter: blur(70px);
1584
- pointer-events: none;
1585
- }
1586
-
1587
1482
  .home-index-split {
1588
1483
  display: grid;
1589
1484
  grid-template-columns: minmax(0, 1fr);
@@ -1603,10 +1498,10 @@ button.home-project-inventory-row {
1603
1498
  display: flex;
1604
1499
  flex-direction: column;
1605
1500
  align-items: flex-start;
1606
- gap: var(--space-md);
1607
- padding: 0 0 var(--space-lg);
1608
- background: var(--color-surface-card-contrast);
1609
- border: 1.5px solid var(--border-ink);
1501
+ gap: 0;
1502
+ padding: 0;
1503
+ background: var(--color-surface-card);
1504
+ border: 1px solid var(--border-ink);
1610
1505
  border-radius: var(--radius-card);
1611
1506
  transition:
1612
1507
  background-color var(--motion-duration-fast) var(--motion-ease-default),
@@ -1614,59 +1509,31 @@ button.home-project-inventory-row {
1614
1509
  }
1615
1510
 
1616
1511
  .home-index-panel:hover {
1617
- background: var(--color-surface-high-contrast);
1512
+ background: var(--color-surface-high);
1618
1513
  border-color: var(--border-gentle-hover);
1619
1514
  }
1620
1515
 
1621
- /* The plates are line-art, so the cubist half of the identity had no footing
1622
- on this band at all. One shard per panel, the canonical polygons across
1623
- the set, rotating hue the way every other shard on the site does.
1624
- *
1625
- * They sit in the foot of the panel, under the title and the link: the plate
1626
- * above carries an opaque background, so a shard behind it would be a shard
1627
- * nobody sees. `overflow: hidden` on the panel above keeps the negative
1628
- * offsets out of the document scroll width, and the shared utility already
1629
- * unmounts both below 640px. */
1630
- .home-index-shard {
1631
- right: -3rem;
1632
- bottom: -3.5rem;
1633
- width: 11rem;
1634
- height: 11rem;
1635
- background: var(--color-secondary);
1636
- }
1637
-
1638
- .home-index-shard--beta {
1639
- right: auto;
1640
- left: -3rem;
1641
- bottom: -3.5rem;
1642
- width: 11rem;
1643
- height: 11rem;
1644
- background: var(--color-primary);
1645
- }
1646
-
1647
- /* Between the shard cut-off and the layout each panel is the full
1648
- band wide on small screens, so a shard sized for half of it stops being a corner accent. */
1649
- @media (max-width: 899px) {
1650
-
1651
- .home-index-shard,
1652
- .home-index-shard--beta {
1653
- width: 8rem;
1654
- height: 8rem;
1655
- bottom: -2.5rem;
1656
- }
1516
+ /* Header row for index panels: holds title and Open link. */
1517
+ .home-index-panel-head {
1518
+ display: flex;
1519
+ justify-content: space-between;
1520
+ align-items: center;
1521
+ gap: var(--space-md);
1522
+ padding: var(--space-sm) var(--space-md);
1523
+ width: 100%;
1657
1524
  }
1658
1525
 
1659
- .home-index-panel:not(:has(.home-index-plate)) {
1660
- padding-top: var(--space-xl);
1526
+ /* The Open link keeps the right end of the head when the title is empty. */
1527
+ .home-index-panel-head > .ledger-cta {
1528
+ margin-left: auto;
1661
1529
  }
1662
1530
 
1663
- /* The title and the link have to sit clear of the shard behind them. The
1664
- plate is excluded because it is the one child that must stay full-bleed,
1665
- and the shard because it is the thing being sat clear of. */
1666
- .home-index-panel> :not(.home-index-plate):not(.shard-accent) {
1667
- position: relative;
1668
- z-index: 1;
1669
- margin-inline: var(--space-lg);
1531
+ /* Panel description from the page's headers note (PLT-AC-50). */
1532
+ .home-index-note {
1533
+ margin: 0;
1534
+ padding: 0 var(--space-md) var(--space-sm);
1535
+ font-size: var(--type-dek);
1536
+ color: var(--color-on-surface-variant);
1670
1537
  }
1671
1538
 
1672
1539
  /* Card-scale title, not `.section-title`: that face is for band banners and
@@ -1693,6 +1560,7 @@ button.home-project-inventory-row {
1693
1560
  min-height: 0;
1694
1561
  margin: 0;
1695
1562
  background: var(--color-background);
1563
+ border-top: 1px solid var(--border-gentle-divider);
1696
1564
  border-bottom: 1px solid var(--border-gentle-subtle);
1697
1565
  }
1698
1566
 
@@ -1908,7 +1776,7 @@ html[data-theme="light"] .home-featured .ledger-poster {
1908
1776
  --frame-corner-size: 0.85rem;
1909
1777
  }
1910
1778
 
1911
- @media (max-width: 899px) {
1779
+ @media (min-width: 768px) and (max-width: 899px) {
1912
1780
  .home-featured-lead {
1913
1781
  order: 1;
1914
1782
  }
@@ -1927,9 +1795,8 @@ html[data-theme="light"] .home-featured .ledger-poster {
1927
1795
  ends flush with the viewport and adds no scrollable width; this must stay
1928
1796
  below 768px, which is where .home-band-inner swaps that token for --gutter.
1929
1797
  The side hairlines go with it: a border pinned to the screen edge reads as a
1930
- frame cut off rather than as the plate's edge. The plate stays 3:4, the
1931
- same frame as the portrait file, so cover does not crop it again. A
1932
- configured hero_landscape switches this query to 16:9 and contain. */
1798
+ frame cut off rather than as the plate's edge. The plate stays 16:9, the
1799
+ same frame as the photograph, so cover does not crop it. */
1933
1800
  @media (max-width: 767px) {
1934
1801
  .home-hero-portrait {
1935
1802
  max-width: none;
@@ -1944,14 +1811,6 @@ html[data-theme="light"] .home-featured .ledger-poster {
1944
1811
  }
1945
1812
  }
1946
1813
 
1947
- /* ── Hero portrait between phone and desktop ──
1948
- Keep the plate modest; the 3:4 crop applies from tablet up. */
1949
- @media (min-width: 768px) and (max-width: 1023px) {
1950
- .home-hero-portrait {
1951
- max-width: 14rem;
1952
- }
1953
- }
1954
-
1955
1814
  /* ── Bibliography stats plate ── */
1956
1815
  #home-bib-panel .home-index-plate {
1957
1816
  display: flex;