Match progressrail.com homepage more closely - #1
Merged
Conversation
Compared the local build against the live source page and fixed the
gaps found:
- header: fix hero overlap (header { height: var(--nav-height) } didn't
account for the two-row desktop layout); add search box and EN/FR
language switcher, replacing the header's misplaced "Contact Us" link
(the source only has that in the jump-nav CTA)
- scripts.js: decorateButtons now always emits the brand's single
a.button.cta style instead of the unused primary/secondary/accent
variants
- content: bold-wrap the CTA links that should render as buttons
(hero, jump-nav, teaser, columns) so decorateButtons picks them up
- pr-jump-nav: real sticky-on-scroll (position: sticky doesn't hold once
its own short section scrolls past, so this uses an IntersectionObserver
sentinel + spacer instead) and a scroll-position-based active-link
underline
- pr-teaser: source renders this as a two-column split (dark text panel
+ image column), not a full-bleed background image like the hero;
restructured accordingly
- pr-cards: split the trailing "Learn More" link into its own footer
with a divider + chevron, matching the source
- styles.css: add the yellow underline rule under section headings
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Hello, I'm the AEM Code Sync Bot and I will run some actions to deploy your branch and validate page speed.
Commits
|
…h width The mobile "hamburger open" full-screen grid-template (header nav[aria-expanded='true']) wasn't scoped to mobile, and its attribute-selector specificity beat the desktop two-row grid-template regardless of viewport — aria-expanded is legitimately 'true' at desktop widths too (toggleMenu() overloads it to also mean "no hamburger needed"). That silently broke the desktop layout into brand/sections/ language stacked as separate rows instead of the intended two-row logo+search+language / nav-links layout. Also matches the source more closely: nav links use their authored title-case text (not forced uppercase+bold), and the search input stretches to fill the row instead of being capped at 420px. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Measured the source header precisely: row 1 is logo + search only: the language switcher actually sits on row 2 with the nav links (right- aligned), not next to search. Also tightened nav-link gap (24px -> 8px) and font-size (15px -> 14px) to match the source's much denser spacing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ding - globe.svg: hardcode fill to white instead of currentcolor. It's loaded via <img>, and currentColor doesn't resolve through an externally- referenced SVG image the way it does for an inlined one — it was defaulting to black, rendering invisibly against the black header. - language switcher links: the source shows both languages in plain white, same weight, body font (not the heading/condensed font, and no yellow highlight for the current one) — matches that now, and drops the fake "|" separator (there isn't one; it's just a 16px gap). - top-level nav items (Company, Business Segments, ...): added ~8px padding to each, matching the source's button-like hit targets — the gap value alone read much tighter than the source. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- nav-sections links were set to the condensed heading font at 500 weight; the source uses the plain body font at regular (400) weight. Same fix for the stale comment claiming that was intentional. - the dropdown chevron was a CSS-drawn border triangle absolutely positioned against the text (6x6px, cramped) — reads as a checkmark at that size. Replaced with an actual chevron icon inserted as a flex sibling of the label with a real gap, matching how the source lays it out (a 14-16px icon, not a tiny corner clipped out of a box). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ubmenu - logo: the two-row breakpoint bumped it to 48px "a touch larger" — the source is 40px at every width. Dropped that override. - chevron: 10px -> 14px to match the source, and rebuilt as an inlined SVG (stroke="currentColor") instead of an <img> — an externally- referenced image can't track the label's color on hover the way an inline one can, so the arrow wasn't changing color together with the text like it does on the source. - language switcher: removed the yellow hover color I'd added — the source has no hover effect on these links at all. - nav.html: "Careers" had no submenu, so it never got a dropdown or chevron. Added the source's actual regional careers links (Brazil, India, Italy, UK, US) so it matches. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The global a:hover { text-decoration: underline } default was bleeding
through on nav-sections and language links since neither hover rule
overrode it. Source has no underline on hover anywhere in the header.
Also matched the source's #555 gray background on nav-item hover — it
wasn't just a text color change, it highlights the whole padded item.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…all) The icon box was already 14x14 to match, but my stroke-based caret (M4 6l4 4 4-4, stroke-width 2) only filled about half of that box — the source's chevron is a filled path that spans nearly the full viewBox. Copied its exact path data so the visual weight matches, not just the container size. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…source Measured the source's h1 and .button.cta precisely and found several mismatches: - h1 (hero heading): 45px/600 -> 54px/700, and it had a 36px top margin from the shared h1-h6 rule stacking on top of the hero's own padding — zeroed that out (only one h1 exists sitewide, the hero's). - .button.cta: was inheriting font-size from the surrounding copy (14-22px depending on context) instead of a fixed 14px, was bold instead of regular weight, 2px square corners instead of 1px/4px rounded, and forced text-transform: uppercase instead of capitalize — the latter was visibly wrong for authored text like "Read More", which should keep its casing rather than becoming "READ MORE". Verified against all 7 cta buttons on the source page, not just the hero one — the values are consistent site-wide. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The source vertically centers the hero's content within the media's full height (a ~24px-padded box centered via flex), not top-anchored with a fixed padding. Ours used a flat 40px top padding, which put the heading noticeably higher than the source at any height other than exactly (media height - content height - 40px*2). Marked the two rows (media/body) with distinct classes so the media can be a full-bleed absolute layer while .pr-hero itself is a flex container centering the body within it — mirrors the pr-teaser restructure from earlier in this branch. (CTA button padding was already fixed to match the source in the previous commit — verified again here, still 7px/16.8px.) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The CSS padding value (16.8px) was already correct, but the visible gap
either side of the text on the source is 62px, not 16.8px — because
every in-content CTA (hero, teaser, columns) has min-width: 200px, and
the shared .button rule's justify-content: center centers the (shorter)
label within that fixed width. Confirmed across all 6 in-content CTAs
on the source page — all exactly 200px regardless of label length.
The jump-nav's own CTA ("Contact Us") is the one exception: it sizes to
its label with no min-width, confirmed separately. Reset min-width: 0
there so it doesn't inherit the 200px floor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
header (was 114px, source is 128px):
- header nav p { line-height: 1 } was squashing the nav-sections links
(they're wrapped in a <p> where standalone) — the source keeps the
body's normal line-height (1.6) on these, which alone accounts for
most of the missing height (each row ends up ~9px shorter than the
source's without it).
- row-gap 12px -> 16px, matching the source's logo row having 16px
padding on both top and bottom rather than a smaller gap plus the
outer nav's own padding.
hero (was min-height: 300px, source is 350px, via --hero-height): fixed
value updated directly.
Header now measures 126.4px vs the source's 128px (a ~1.6px rounding
difference, not worth chasing further); hero is an exact 350px match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…isely Measured every property on the source's jump-nav links and found it was wrong on nearly every axis: - font-family: roboto (body) -> roboto-condensed (heading) — the source uses the condensed font here, unlike the main header nav which uses body - font-weight: 700 -> 400 - padding: only a 6px bottom offset for the underline -> full button-like 0.5em/1.2em (7px/16.8px), matching the header nav-link/CTA pattern - link gap: 24px -> 0 — the source's links touch directly, padding alone provides the spacing - alignment: left-aligned -> centered (`justify-content: safe center` on the link list, which grows via flex:1 to fill the row and push the CTA to the right) - active-state indicator: was a 3px border-bottom sized to the text only; the source uses a 5px-tall ::after spanning the full padded link width, toggled by opacity rather than added/removed Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tion
- height/gap: the strip was 40px below the hero (the boilerplate's
generic `main > .section { margin: 40px 0 }` exempts only the first
section — zeroed the hero's margin-bottom too, since sibling margins
collapse to their max and zeroing just one side wasn't enough) and
61px tall instead of the source's 68px (added min-height: 68px,
removed the redundant vertical padding that was stacking on top of it).
This also fixes the active-state underline's position, since it's
anchored to the link's own (now correctly centered) box.
- click navigation was actually broken: the authored hrefs carry the
source's own path convention ("/en/#featured", trailing slash), but
this page is served at "/en" (no trailing slash) — clicking navigated
to a different, 404ing path instead of jumping to the anchor on the
current page. Normalized same-page anchor hrefs to the page's real
path in decorate(), so this doesn't depend on content being
re-authored/republished to take effect.
Also fixed the same trailing-slash issue in content/en.plain.html
directly (the authored source of truth) — not deployed yet, needs a DA
upload same as previous content fixes this session.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…" anchor - the bar's background/divider was constrained to the standard 1200px content column instead of spanning the full viewport width like the source — added the max-width:unset wrapper override (same pattern as pr-hero/pr-teaser), with the nav's own content now centered at 1140px inside it, matching the source's structure exactly. - divider color: var(--light-color) (#f2f2f2, near-white) -> #ccc, matching the source's actual rgb(204,204,204). - "FEATURED" link was dead: it points to #featured, but nothing on the page had that id — the source sets it directly on the cards section itself (no visible heading there to hang an id off of). Added the same in pr-cards.js. - bumped scroll-margin-top (headings: 40px -> 100px; sections generally, for id targets with no heading like #featured) so an anchor-jump target isn't left hidden underneath the sticky jump-nav strip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… previous one The "has this section been passed?" threshold was derived from the sticky bar's own bottom edge (~69-93px), but clicking a link scrolls its target to sit at scroll-margin-top (100px) via native anchor scrolling — a taller offset. So the just-clicked target never satisfied its own "passed" check immediately after landing, and the previous link's section (the last one that legitimately had passed) stayed active instead. Reproduced exactly as reported: clicking "News & Events" activated "Featured", clicking "Supply Chain" activated "News & Events". Fixed by tying the JS threshold to the same 100px used for scroll-margin-top, plus a couple of px of tolerance — the source page renders at sub-pixel positions throughout (e.g. 494.796875), so a just-landed target can sit at 100.2px, a hair over an exact-100 threshold, and needed a small buffer to register as "passed" (this is what was still failing for "Gifts & Apparel" specifically after the first part of this fix). Also drops the now-unused `nav` param from setupScrollSpy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The "passed" line was pinned to ~100px (matching where a click-triggered anchor jump lands), which only validates "did my click land correctly". For ordinary scrolling it left a large dead zone: a section could fill most of the viewport — its heading clearly the dominant, current content — without having crossed that narrow a line yet, leaving the *previous* section (long since scrolled entirely out of view) marked active. Reproduced exactly as reported via screenshot: scrolled to Gifts & Apparel (heading ~200px down, clearly the current section), Supply Chain still underlined. Now uses whichever is larger: that same ~100px floor (so the click-jump case is unaffected — it already satisfies a bigger line trivially) or 35% of the viewport height, which tracks ordinary scrolling the way scrollspy usually works. Verified it doesn't overcorrect either: a section still mostly below the fold (target top ~700px in a 900px viewport) correctly leaves the previous link active. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…d edge display:flex + justify-content:space-between stretched the link to the footer's full width, pushing the chevron out to the card's right border. The source's link is plain inline content sized to itself (text + small gap + chevron) sitting flush-left, with the rest of the footer's width simply unused. inline-flex (no space-between) matches that. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Measured the "Advanced Rail Technology" teaser directly against the source and found the block itself was structurally wrong, not just its type sizes: - width: unlike the hero (genuinely full-bleed), the source keeps this teaser within the page's normal 1140px content column — it does NOT stretch edge-to-edge. Ours had `max-width: unset` copied over from the hero, making it full-bleed when it shouldn't be. Confirmed by walking the source's actual DOM ancestor chain (the outer section is full width, but its wrapper is capped at 1140px, and the teaser block itself measures exactly that). - height: 325px specifically for this "teaser" variant (the source defines a separate --hero-height for it, distinct from the plain hero's 350px) — ours was 360px. - panel: max-width 488px, padding 24px 64px (notably less vertical and more horizontal than what we had, 40px 32px). - heading: fixed 20px/700/1.25 line-height, no top margin — ours inherited the shared, much larger heading scale. - body copy: 14px/400/1.6 line-height — margin already matched via the global paragraph rule once the font-size was corrected (0.8em/0.25em at 14px lines up with the measured 11.2px/3.5px exactly). - panel background: solid black (#000), not var(--brand-dark) (#131313). (The source's actual mechanism is a single full-bleed image with a bottom gradient scrim, relying on the photo's own left-side darkness for contrast — kept our two-column structural approach instead, since it's more robust for a CMS where the authored image could vary, but matched every measurable style value.) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Measured the "PROGRESS RAIL" default-content heading against the source and found the underlying issue is bigger than just this section: - content column width: main > .section > div was 1200px; the source measures exactly 1140px across every section checked (default content, cards, teaser, jump-nav) — this was our own assumed default, not verified against the source until now. Fixed at the source (styles.css), which also let two now-redundant per-block 1140px overrides (pr-teaser, and pr-jump-nav's inner nav already had its own reason to keep) get cleaned up. - h2/h3 in body content inherit the shared h1-h6 scale sized for the hero (36-44px) — the source uses a fixed, much smaller scale for content headings: 26px for h2 (default content, cards — already fixed for cards), 20px for h3 (columns, teaser — teaser already fixed). Added the same for default-content h2 and pr-columns h3. - body copy (default-content and pr-columns paragraphs) inherits the page's body font-size (18-22px) — source is a fixed 14px throughout, matching the cards/teaser copy already fixed earlier. - underline bar: 4px -> 5px tall, and the bottom margin was doubling up with the next element's own top margin (16px 0 -> 16px 0 0). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Verified against source via getComputedStyle: all four paragraphs under the "PROGRESS RAIL" heading carry a symmetric 35px left/right margin (the heading itself does not), producing both the visible left indent and the narrower 1070px effective width already implied by 1140-35*2.
…out model Verified against source via getBoundingClientRect + CSSOM rule inspection: - pr-teaser was built as a two-panel flex split (solid black text panel beside the image) under an earlier, unverified assumption. The source actually uses the same full-bleed-media-with-overlaid-text model as pr-hero: .media-wrapper is absolutely positioned across the block's entire width, with the text panel on top of it (z-index layered) and a dark gradient — not a solid panel — providing contrast underneath the copy. Restructured to match at >=900px; mobile's simpler stacked layout is unchanged. - pr-hero had the same absolute-media/overlaid-body structure already, but was missing the gradient entirely, so light hero images visibly lacked the contrast backdrop under the heading/CTA that the source gives them. Gradient values (linear-gradient(to right, ...)) read directly off the source's stylesheet via document.styleSheets: 85% opaque black fading out by 60% width for pr-hero, fully opaque fading out by 93% for the stronger pr-teaser variant.
…ify-content) The desktop overlay rule set align-items: center on .pr-teaser, but since flex-direction is column (inherited from the base rule), align-items controls the cross (horizontal) axis, not the vertical one — this centered the text panel horizontally in the middle of the full-bleed image instead of vertically centering it while keeping it pinned left, per the source (verified via getBoundingClientRect: body-wrapper's left edge matches the teaser's own left edge, vertically centered in its height). Swapped to justify-content: center (vertical) + align-items: flex-start (horizontal, left).
Verified against the source via getBoundingClientRect on every section boundary (cards-container, the teaser/columns/video container, and the gap before the footer): sections are separated by 64px, not the boilerplate's 40px default we'd never corrected. Also explains the recently-reported "section needs bottom margin" — the gap was present, just visibly smaller than the source's.
Verified against the source's own stylesheet (via document.styleSheets): it separates block wrappers stacked in the same section with their own margin-top, not section padding — a generic rule for any wrapper following another wrapper (32px), and a smaller special case (16.8px, i.e. 1.2em at the body copy's 14px font-size) specifically right after default content. Applied the same generic pattern here so it covers pr-teaser-wrapper, pr-columns-wrapper, and pr-video-wrapper (all three were flush against each other with 0 margin).
Verified against the source via getBoundingClientRect on both a picture-first row (News & Events) and a text-first one (Supply Chain): each row is a 2-column grid with zero gap, and the text panel supplies its own uniform 64px padding on every side — that's what actually separates it from the image, not a gap between columns. Our text column had no padding at all (just a 24px flex-gap), so headings/copy sat almost flush against the adjacent image. Replaced the flex-gap with the matching padding on the non-image column.
Verified against the source via getBoundingClientRect: image and text columns are always exactly equal halves (570px each of the 1140px row). Our flex:1/flex:1 layout looked equivalent in principle, but the text column's own min-content width (driven by its longest unbreakable word/heading, especially after the last commit added 64px padding) forced the flex algorithm to over-allocate it space at the image column's expense — e.g. 506px/634px instead of an even 570/570 split. Switched the row to a true CSS grid (1fr 1fr), matching how the source actually implements it, with an explicit min-width: 0 on both columns so long text still can't push the split off balance.
…ect ratio) Verified against the source: rows measured 380px (varying slightly per row) instead of the source's uniform, fixed 314px. Root cause was the picture element being left in normal flow — since its child img's height:100% can't resolve during the grid's own row-sizing pass (the row's height is literally what's being computed), the browser fell back to the image's natural aspect ratio to size it, so each row ended up as tall as its own image happened to render (object-fit was also missing entirely, defaulting to `fill` and distorting images in the process). Matches the source's actual technique (found directly in its stylesheet): position the picture absolutely (inset: 0) inside the column so it can't contribute to the column's intrinsic height at all — the column's height then comes purely from its min-height (184px mobile, 314px desktop) or the row's own natural height from the text column, whichever is taller, with the image cropped via object-fit: cover to fill whatever height it ends up with.
Verified via getComputedStyle: the source's .video block uses the same 1070px --content-width constraint as the default-content paragraphs (35px margin each side within the 1140px section column), not the boilerplate's own 900px default we'd inherited unchanged. Also dropped the block's own 24px vertical margin — spacing above it is already handled by the generic wrapper-to-wrapper margin rule added earlier.
…n row) Verified against the source via getComputedStyle/CSSOM — the footer had never actually been checked precisely, and turned out to be a completely different design than assumed: - Background is a flat #ccc, not dark/black; nav links and headings use the page's own dark text color, not white — a from-scratch color correction. - Each nav category is a native <details>/<summary> accordion (collapsible on mobile via free native behavior, forced open and non-interactive at desktop via CSS), not a plain heading + always-visible list. Since DA's content pipeline strips <details>/<summary>/<button> from block-cell content on upload, these are authored as plain <h3>/<ul>/<a> and built into their real elements client-side in footer.js instead. - A second, icon-only row of the same 5 social links sits below the nav (source renders both a text column AND an icon row) — added as its own section (footer-social, via section-metadata) with brand SVGs inlined for currentColor, matching the project's existing icon convention. - The legal/copyright line is a <ul> with a genuine "Cookie Settings" control (a CSS-generated "|" separator between items, not typed pipes), not a <p> of loose links — restructured to match, with its own distinct blue link color (verified: rgb(38, 121, 184), separate from the rest of the footer's dark text). blocks/footer/footer.js, blocks/footer/footer.css, and tools/nav-footer-source/footer.html (uploaded to DA + previewed) all rewritten together.
Verified via getBoundingClientRect: the source's footer-nav content column is exactly 1140px wide, starting at the same left edge (x=230 at 1600px viewport) as every other section on the page. The footer's own container was still the boilerplate's unmodified 1200px default, so its left/right edges didn't line up with the header/hero/cards/etc. above it — the same class of bug as the earlier main-content 1200→1140 fix, just never applied to the footer's own (separate) container rule.
Two alignment bugs, both verified against the source via getComputedStyle: - The generic .columns block centers grid items vertically at >=900px (blocks/columns/columns.css's own desktop rule), so a shorter category (e.g. "Supply Chain", 3 links) had its heading pulled down to align with the vertical center of a taller neighbor (e.g. "Company", 5 links) instead of starting at the same top row. Source uses default top alignment. Added align-items: start to override. - The social icon row's <ul> had no justify-content, so icons clustered left under "COMPANY" instead of right-aligning under "SOCIAL MEDIA" — source sets justify-content: flex-end here.
Two more things verified against the source that our footer had wrong: - The copyright row sits on a genuinely different (white) background from the rest of the footer's gray — found via getComputedStyle(el, '::after'): the source gives .footer-copyright a full-viewport-width white rectangle behind it (position: absolute, left: calc(50% - 50vw), width: 100vw — a standard full-bleed technique to escape the footer's own centered/padded column), extending from its own top edge down through its 32px padding-top plus another 32px beyond, landing exactly on the footer block's own bottom edge. isolation: isolate keeps its z-index: -1 contained to this element rather than escaping behind the whole page. - The footer.block itself is a CSS grid (grid-template-areas: "nav" "social" "copyright"), with a verified 24px row gap between each section — our three sections had no gap between them at all (nav sat flush against the icon row, which sat flush against the copyright row), which is what read as "social needs bigger top/bottom margins." Restructured footer.js to append the fragment's three sections directly as the block's children (matching the source's own DOM shape) instead of an extra unstyled wrapper div, so footer.css's grid can target them by name.
Verified against the source via getBoundingClientRect/getComputedStyle: - The gray background belongs on the outer <footer> element (spans the full viewport width), not on .footer.block itself — the block is only the centered/padded 1140px content column sitting on top of it (background: transparent). Moved background-color: #ccc off the block onto the bare `footer` selector. - .footer-copyright ul has justify-content: center on the source (missed in the earlier rebuild) — the legal/cookie links center within the row rather than starting flush left.
Reproduced from a user screenshot: clicking "NEWS & EVENTS" landed there correctly, but "SUPPLY CHAIN" stayed underlined instead. Root cause: the "passed" line was Math.max(102, innerHeight * 0.35), uncapped. On a tall enough viewport (reproduced at 1250px), 35% of it (437.5px) exceeds the ~314px vertical gap between two adjacent jump-nav targets — the "News & Events"/"Supply Chain" pr-columns rows (a fixed height established by an earlier fix in this same area). Once the line exceeds that gap, BOTH targets simultaneously satisfy "top <= line", and .filter(passed).pop() picks the later (wrong) one. Capped the line at 260px — comfortably under every real inter-target gap on this page, while still clearing the original dead-zone floor the 0.35 multiplier was there to solve. Verified: reproduced the exact bug at 2000x1250 before the fix, confirmed fixed after; re-verified the original dead-zone scenario and ordinary scroll-through of all four sections still track correctly at 1600x900.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pixel-parity pass against the live source (https://www.progressrail.com/en/), driven by iterative side-by-side comparison (
getComputedStyle/getBoundingClientRectmeasurements against the source, not visual guessing) across many rounds of feedback.Header / Hero
pr-heroandpr-teaserwere missing entirely (verified via the source's own CSSOM).Jump nav
position: stickycan't hold across sibling sections), scroll-position-based active-link underline, working anchor navigation (fixed a trailing-slash href bug), full-bleed bar with correct divider color.Content blocks
--content-widthas body copy.Footer
Full rebuild — the previous footer was built on an unverified assumption (dark/white theme, generic heading+list nav) that turned out to be wrong in almost every respect once actually measured:
#ccc, full viewport width) with dark nav text, not dark/white.<details>/<summary>accordion (collapsible on mobile, static on desktop) — authored as plain<h3>/<ul>since DA's content pipeline strips<details>/<button>from block content, then built into real elements client-side.<ul>with a genuine "Cookie Settings" control and a CSS-generated|separator, sitting on its own distinct white background (a full-bleed pseudo-element), not a paragraph of loose links on the same gray.Preview
https://match-source-page-parity--progressrail-eds--samshao.aem.page/en
Checks
npm run lint: clean.noindex/robots.txtdisallow, which is a platform-wide default for every*.aem.page/*.aem.livepreview URL (confirmed present onmain's own preview too) until a production custom domain is configured, and (2) generic "Learn More"/"Read More" link text, copied verbatim from the source site's own copy as part of this PR's content-fidelity goal.aem-psi-check's mobile run timed out against Google's PSI API; a local Lighthouse mobile run completed normally (Accessibility 95, mainly a footer-link touch-target-size nitpick inherited from matching the source's own tight link spacing).🤖 Generated with Claude Code