Merge nucleic/rustic-ancient-civet-tzav into main

Mirrored from corpo 66136b9.
This commit is contained in:
2026-08-11 17:06:48 -07:00
parent 511e184326
commit 913ac2bb70
+50 -11
View File
@@ -125,12 +125,26 @@ const canonical = new URL('/', Astro.site);
</span> </span>
<span class="card-rule" aria-hidden="true"></span> <span class="card-rule" aria-hidden="true"></span>
</a> </a>
<a
id="technology-hydrangea"
class="product-card product-card-next"
data-technology-card
href="https://hydrangea.blakeslee.xyz"
>
<span class="card-index">03 / Machine fabric</span>
<span class="product-name">Hydrangea <span aria-hidden="true">↗</span></span>
<span class="product-description">
Not kept anywhere — alive everywhere, whole wherever you meet it.
</span>
<span class="card-rule" aria-hidden="true"></span>
</a>
</div> </div>
<div class="technology-index" role="status" aria-live="polite" aria-atomic="true"> <div class="technology-index" role="status" aria-live="polite" aria-atomic="true">
<span class="technology-index-current" data-technology-progress-current>01</span> <span class="technology-index-current" data-technology-progress-current>01</span>
<span aria-hidden="true">/</span> <span aria-hidden="true">/</span>
<span>02</span> <span>03</span>
</div> </div>
</div> </div>
</div> </div>
@@ -138,6 +152,7 @@ const canonical = new URL('/', Astro.site);
<div class="technology-steps" aria-hidden="true"> <div class="technology-steps" aria-hidden="true">
<span data-technology-step="0"></span> <span data-technology-step="0"></span>
<span data-technology-step="1"></span> <span data-technology-step="1"></span>
<span data-technology-step="2"></span>
</div> </div>
</div> </div>
</div> </div>
@@ -510,9 +525,12 @@ const canonical = new URL('/', Astro.site);
row-gap: clamp(2.75rem, 7vh, 6rem); row-gap: clamp(2.75rem, 7vh, 6rem);
} }
/* Scroll length is per story beat, not a fixed page height: a card added to the
stack has to lengthen this or every beat gets shorter. Sized so three beats each
hold the stage for about as long as the two-card stack gave one. */
.hero-lead.has-scroll-stack { .hero-lead.has-scroll-stack {
position: relative; position: relative;
min-height: clamp(54rem, 145svh, 84rem); min-height: clamp(66rem, 178svh, 103rem);
align-items: stretch; align-items: stretch;
} }
@@ -548,7 +566,8 @@ const canonical = new URL('/', Astro.site);
.is-stacked-stack .technology-stack { .is-stacked-stack .technology-stack {
width: min(100%, 28rem); width: min(100%, 28rem);
min-width: 0; min-width: 0;
min-height: 100svh; /* Same per-beat rule as the two-column stack above, at this fallback's own pace. */
min-height: 130svh;
margin-top: clamp(3.25rem, 9vh, 6rem); margin-top: clamp(3.25rem, 9vh, 6rem);
margin-left: auto; margin-left: auto;
} }
@@ -812,12 +831,26 @@ const canonical = new URL('/', Astro.site);
position: absolute; position: absolute;
inset: 0; inset: 0;
display: none; display: none;
grid-template-rows: repeat(2, minmax(0, 1fr));
pointer-events: none; pointer-events: none;
} }
/* Deliberately not equal shares. A step box is read against a band across the
middle of the viewport, and the stack's own top edge starts well below it, so
the opening box has to be the longest simply to reach that band — split evenly,
the first product would flick past in a third of the scroll the middle one gets.
Measured against the rendered page: these hold each product for within ~10% of
the same distance from 700px to 1100px tall. */
.is-scroll-stack .technology-steps { .is-scroll-stack .technology-steps {
display: grid; display: grid;
grid-template-rows: 44% 19% 37%;
}
/* The wrapped fallback pins its card near the top of the viewport rather than at
the middle of a column, so the opening box reaches the band far sooner and needs
the smallest share here, not the largest. Declared after the rule above because
the two selectors weigh the same. */
.is-stacked-stack .technology-steps {
grid-template-rows: 20% 32% 48%;
} }
.footer { .footer {
@@ -982,9 +1015,11 @@ const canonical = new URL('/', Astro.site);
} }
/* Scroll length for the story, and nothing else — the card no longer spends any /* Scroll length for the story, and nothing else — the card no longer spends any
of it travelling, so a full viewport of it would read as a dead stretch. */ of it travelling, so a full viewport of it would read as a dead stretch. Holds
the phone's pace from the two-card stack across three beats rather than making
the story a third longer again. */
.has-scroll-stack .technology-stack { .has-scroll-stack .technology-stack {
min-height: 70svh; min-height: 90svh;
} }
/* The script pins this at the card's own resting offset. The clamp is the fallback /* The script pins this at the card's own resting offset. The clamp is the fallback
@@ -998,12 +1033,12 @@ const canonical = new URL('/', Astro.site);
min-height: 11.9rem; min-height: 11.9rem;
} }
/* Keep Nucleic alone for roughly the first half of the scroll, then move directly /* The same unequal split as the desktop stack, retuned: a phone's step boxes are
into the next product without the old empty stretch between story beats. The read against the same centre band, but the stack starts much further down its
split is below half because the step boxes are read against a band at the middle own scroll, so the opening share is larger again. Measured across 640–932px
of the viewport, which the stack's own top edge starts well below. */ tall, these keep the three products within ~25% of one another. */
.is-scroll-stack .technology-steps { .is-scroll-stack .technology-steps {
grid-template-rows: 23% 77%; grid-template-rows: 29% 19% 52%;
} }
/* One line each: side by side these two need ~360px of tracking-heavy mono, which /* One line each: side by side these two need ~360px of tracking-heavy mono, which
@@ -1235,10 +1270,14 @@ const canonical = new URL('/', Astro.site);
} }
}; };
/* A card waits below the active one and leaves above it, so which side of the
composition a given card sits on depends on where the story currently is — the
markup class is only the resting state of the stack before any of this runs. */
const activateTechnology = (index: number) => { const activateTechnology = (index: number) => {
cards.forEach((card, cardIndex) => { cards.forEach((card, cardIndex) => {
const isActive = cardIndex === index; const isActive = cardIndex === index;
card.classList.toggle('is-active', isActive); card.classList.toggle('is-active', isActive);
card.classList.toggle('product-card-next', cardIndex > index);
card.toggleAttribute('inert', !isActive); card.toggleAttribute('inert', !isActive);
card.setAttribute('aria-hidden', String(!isActive)); card.setAttribute('aria-hidden', String(!isActive));
}); });