This commit is contained in:
NotSoToothless
2026-06-07 20:44:44 -07:00
committed by GitHub
parent b88f41c14c
commit 28ce4f0541
+41 -21
View File
@@ -45,8 +45,8 @@
} }
@media (min-width: 1024px) { @media (min-width: 1024px) {
.timeline-hero { .timeline-hero {
padding-top: 7.5rem; padding-top: 8.5rem; /* clearance below the fixed nav */
padding-bottom: 3.25rem; /* room between hero text and row 1 */ padding-bottom: 1.75rem; /* JS centering adds the rest of the hero→row1 gap */
} }
} }
@@ -125,7 +125,7 @@
@media (min-width: 1024px) { @media (min-width: 1024px) {
.timeline { .timeline {
margin-top: 2.5rem; margin-top: 1.5rem; /* base; JS centering adds to this */
} }
.timeline-grid { .timeline-grid {
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
@@ -169,7 +169,7 @@
.timeline-node:nth-child(4) { grid-area: 2 / 3; } .timeline-node:nth-child(4) { grid-area: 2 / 3; }
.timeline-node:nth-child(5) { grid-area: 2 / 2; } .timeline-node:nth-child(5) { grid-area: 2 / 2; }
.timeline-node:nth-child(6) { grid-area: 2 / 1; } .timeline-node:nth-child(6) { grid-area: 2 / 1; }
/* Static fallback gap before row 2; JS (applyFirstGap) overrides /* Static fallback gap before row 2; JS (layoutFirstScreen) overrides
this on desktop with a measured value so row 2 lands just below this on desktop with a measured value so row 2 lands just below
the fold on any viewport height. */ the fold on any viewport height. */
.timeline-node:nth-child(4), .timeline-node:nth-child(4),
@@ -191,10 +191,10 @@
already have that room, so they keep the roomier sizing above. */ already have that room, so they keep the roomier sizing above. */
@media (min-width: 1024px) and (max-height: 1080px) { @media (min-width: 1024px) and (max-height: 1080px) {
.timeline-hero { .timeline-hero {
padding-top: 4.5rem; padding-top: 6.5rem; /* nav clearance on short screens */
padding-bottom: 1.5rem; padding-bottom: 1rem;
} }
.timeline { margin-top: 1rem; } .timeline { margin-top: 0.75rem; }
.timeline-card { padding: 1.85rem; } .timeline-card { padding: 1.85rem; }
.timeline-title { font-size: 1.5rem; } .timeline-title { font-size: 1.5rem; }
.timeline-desc { font-size: 1.02rem; line-height: 1.6; } .timeline-desc { font-size: 1.02rem; line-height: 1.6; }
@@ -479,27 +479,47 @@
return { x: x, y: y }; return { x: x, y: y };
} }
// Size the gap before row 2 so it sits just below the fold on load, // Desktop layout pass: vertically centre row 1 + the comet's breathing
// measured from the real row-1 bottom and the real viewport height — // band in the viewport (spare space split evenly above row 1 and below
// no per-resolution magic numbers. Desktop (3-col) only. // the comet), then push row 2 just under the fold so it stays hidden.
function applyFirstGap() { // This keeps the same balanced look on any viewport height — no
// per-resolution magic numbers.
var COMET_BAND = 90; // breathing room reserved below row 1 for the comet
function layoutFirstScreen() {
var rowTwo = [nodes[3], nodes[4], nodes[5]].filter(Boolean); var rowTwo = [nodes[3], nodes[4], nodes[5]].filter(Boolean);
if (!rowTwo.length) return; // Reset both adjustables so we measure the natural layout each time.
if (!window.matchMedia('(min-width: 1024px)').matches) { timeline.style.marginTop = '';
rowTwo.forEach(function (n) { n.style.marginTop = ''; }); rowTwo.forEach(function (n) { n.style.marginTop = ''; });
return; if (!window.matchMedia('(min-width: 1024px)').matches) return;
} if (!rowTwo.length) return;
// Measure row 2's natural top with no extra gap (grid row gap only).
var vh = window.innerHeight;
rowTwo.forEach(function (n) { n.style.marginTop = '0px'; }); rowTwo.forEach(function (n) { n.style.marginTop = '0px'; });
var top = rowTwo[0].getBoundingClientRect().top + window.pageYOffset;
// +32 so the marker (which pokes ~1.8rem above the node) clears too. // Natural row-1 extent (tallest of the first three cards).
var gap = Math.max(48, (window.innerHeight + 32) - top); var row1Top = Infinity, row1Bottom = 0;
for (var k = 0; k < 3 && k < nodes.length; k++) {
var r = nodes[k].getBoundingClientRect();
row1Top = Math.min(row1Top, r.top);
row1Bottom = Math.max(row1Bottom, r.bottom);
}
var row1Height = row1Bottom - row1Top;
// Centre: half the leftover height goes above row 1, half below comet.
var spare = vh - row1Top - row1Height - COMET_BAND;
var topMargin = Math.max(0, spare * 0.5);
var base = parseFloat(getComputedStyle(timeline).marginTop) || 0;
timeline.style.marginTop = (base + topMargin) + 'px';
// After the shift, drop row 2 just under the fold (+40 clears its marker).
var row2Top = rowTwo[0].getBoundingClientRect().top;
var gap = Math.max(48, (vh + 40) - row2Top);
rowTwo.forEach(function (n) { n.style.marginTop = gap + 'px'; }); rowTwo.forEach(function (n) { n.style.marginTop = gap + 'px'; });
} }
function build() { function build() {
_samples = null; _samples = null;
applyFirstGap(); layoutFirstScreen();
// Use layout dimensions (not getBoundingClientRect) so the SVG // Use layout dimensions (not getBoundingClientRect) so the SVG
// coordinate system matches the transform-independent getMarkerCenter. // coordinate system matches the transform-independent getMarkerCenter.
svg.setAttribute('viewBox', '0 0 ' + timeline.offsetWidth + ' ' + timeline.offsetHeight); svg.setAttribute('viewBox', '0 0 ' + timeline.offsetWidth + ' ' + timeline.offsetHeight);
@@ -568,7 +588,7 @@
// reveal line is the frontier. This keeps the comet just below the last // reveal line is the frontier. This keeps the comet just below the last
// in-view card on load, then leading the draw as you scroll, instead of // in-view card on load, then leading the draw as you scroll, instead of
// jumping ahead off-screen. // jumping ahead off-screen.
var REVEAL_FRACTION = 0.82; var REVEAL_FRACTION = 0.85;
function frontierProgress() { function frontierProgress() {
if (!pathLength || !_samples) return state.progress; if (!pathLength || !_samples) return state.progress;
var revealY = window.innerHeight * REVEAL_FRACTION - timeline.getBoundingClientRect().top; var revealY = window.innerHeight * REVEAL_FRACTION - timeline.getBoundingClientRect().top;