exceljs last shipped a stable release in October 2023 (4.4.0) and has no
repository commits since January 2024, so it no longer meets the
dependency maintenance bar. write-excel-file is actively maintained, MIT,
Node 18+, and writes to a file path, buffer, or stream, which fits the
worker export job and protected volume.
CSV stays on PapaParse. read-excel-file is noted as the counterpart if
XLSX import is ever required; imports remain CSV-only.
Phones report a viewport taller than the area they actually paint, so
centring in the full 760px hero left a visibly bigger gap above the copy
than below it — on the reported preview roughly 110px above against 56px
below.
Cap the mobile centring box at 700px. Viewports shorter than that are
unaffected and stay exactly centred; taller ones now sit 30px higher.
The hero is a fixed 760px, which is taller than most phone viewports, so
copy centred within the section still sat low on screen — on a 690px
viewport there was 114px above the block and only 36px below it.
Below `lg`, centre the copy in min(100svh, 760px) instead, floored at
560px so a landscape phone cannot clip it. From `lg` up nothing changes:
the same absolute offsets as the homepage, which keeps rendering
byte-identical markup because the new layout is opt-in per page.
Same problem as /build-the-parallel: the whole card is the link, so the
Umami click tracker was naming the event after the card's entire text —
heading, CTA label and body run together — which made the three cards
indistinguishable in the dashboard.
Attributes only; the rendered markup is otherwise unchanged and the page
is pixel-identical to develop.
Page views, bounce rate and time on site already come from the site-wide
Umami script, and UmamiButtonTracker already fires on every link click —
but it names the event after the element's text. That breaks on a copy
edit, and for the path cards the whole card is the link, so the event name
was the heading, CTA label and body run together.
Give the hero and path-card sections an optional event-name prop and set a
stable name for all seven CTAs. Both props are optional, so the homepage
keeps rendering byte-identical markup.
The centred flex layout spaced the three hero blocks evenly, but the
homepage's offsets are not even (52px above the headline, 78px below), so
the headline landed 22px lower than on the homepage.
Go back to the shared offsets layout on /build-the-parallel. Both pages now
render the hero from the same component with the same absolute positions,
and both overlay their header, so the section starts at page y=0 on each.
The hero placed its three copy blocks at hand-tuned absolute offsets, so
the group drifted a little below the section's centre and the drift moved
with the viewport and the length the copy wrapped to.
Add a `centred` content layout that stacks the same blocks in a flex
column with an even 70px rhythm, centred in the hero, and use it on
/build-the-parallel. The homepage keeps the existing offsets layout and
renders byte-identical markup.
The header reserved its 40/42px bar on this page, so a beige strip sat
above the hero. Register /build-the-parallel with the existing hero-tone
header list so the nav floats over the hero image the way Figma draws it.
Figma leaves 224px between the path cards and the footer; the shared
choose-your-path section only contributes 56px, so the page adds the
rest and scales it down below `lg`.
Compose the Figma landing page entirely from existing sections: the
shared hero (still image + its own copy), the statement heading with two
CTAs, the Movement page's centred lead-in above the Circles map, and the
choose-your-path cards.
Copy lives in content/pages/en/build-the-parallel.json using existing
section schemas, so no schema changes were needed. British spelling
follows the rest of the site rather than the mixed spelling in Figma.
Move the hero and choose-your-path card sections out of sections/home so
pages other than the homepage can compose them, extract the two-line
statement heading, and lift CenterCtaSection out of the Movement page's
private atoms module.
The hero now takes an optional `background` node, defaulting to the same
Logos background video the homepage has always rendered.
Rendered output is unchanged: the homepage's hero, social proof, and
choose-your-path markup and the Movement page body are byte-identical to
develop, and the generated stylesheet loses no rules.