more changes

- Restore the SHARP-ER in the homepage hero section
- Match the design for the 3-image section in the homepage
This commit is contained in:
2026-09-23 23:36:36 +02:00
parent cc2b426119
commit 2ff5cf47cf
6 changed files with 130 additions and 28 deletions
+7 -15
View File
@@ -203,22 +203,14 @@
}
}
/* A picture pushing in a little while the frame around it is hovered. The
frame clips, so the picture grows into its own box and not over the page.
Paired with .sh-lift the tile rises and its picture closes in, which is
how the draft hovers the gallery. */
.sh-zoom {
overflow: hidden;
}
.sh-zoom img {
transition: transform var(--motion-duration-slow)
var(--motion-ease-standard);
}
/* A gallery tile that grows as a WHOLE frame instead of pushing the picture
inside it - the draft hovers the frame, not the image. It keeps `.sh-lift`'s
rise and adds a uniform scale to it. Written after `.sh-lift:hover` so the
compound transform wins at equal specificity; `.sh-lift`'s transition
already covers `transform`, so the two move together. */
@media (hover: hover) {
.sh-zoom:hover img {
transform: scale(1.09);
.sh-lift--grow:hover {
transform: translateY(-5px) scale(1.04);
}
}
+97 -7
View File
@@ -89,10 +89,47 @@ import {
PageHero,
PageSection,
SectionHeader,
type PageHeroWordmark,
} from "./_page-kit/sections";
import "./_page-kit/motion.css";
/**
* The hero lockup above the headline: the full SHARP-ER pair at the SVGs' own
* box (218 + 112 wide), picked per surface the way the navigation's brand
* picks it (Brand.tsx) - white "inverse" exports on the dark hero, the ink
* pair on a light one. `alt` is empty: the sticky bar's brand link already
* carries the accessible project name.
*/
const HERO_WORDMARK: Record<AppTheme, PageHeroWordmark> = {
dark: {
src: "/assets/sharp-wordmark-inverse.svg",
alt: "",
width: 218,
height: 46,
drawnWidth: 218,
suffix: {
src: "/assets/sharp-er-suffix-inverse.svg",
width: 112,
height: 46,
drawnWidth: 112,
},
},
light: {
src: "/assets/sharp-wordmark.svg",
alt: "",
width: 218,
height: 46,
drawnWidth: 218,
suffix: {
src: "/assets/sharp-er-suffix.svg",
width: 112,
height: 46,
drawnWidth: 112,
},
},
};
/** The network logo wall - a wide but finite grid. */
const PARTNER_PAGE_SIZE = 36;
/**
@@ -312,24 +349,70 @@ function StatusPill({ label }: { label: string }) {
}
/**
* The approach band's three pictures. The CMS alt text doubles as the visible
* The approach band's pictures. The CMS alt text doubles as the visible
* caption - so the picture is described once, in the caption, and the <img>
* itself is marked decorative rather than having a screen reader read the same
* sentence twice.
*
* The frame and the picture hover apart, as the third draft has them: the
* frame lifts and takes the accent hairline (`sh-lift`) while the picture
* closes in inside it (`sh-zoom`, which also does the clipping).
* The frame and the picture move as one: the frame lifts and takes the accent
* hairline (`sh-lift`) and the whole tile grows under the pointer
* (`sh-lift--grow`) - the draft grows the frame, it does not push the picture
* inside it.
*
* THREE PICTURES RENDER AS THE DRAFT'S MOSAIC (figma 559:4089): the middle
* tile dominates - 605px wide, full height, full opacity - and the two flanks
* sit shorter beside it (427/426px wide, 0.72 of its height) at 50% opacity,
* vertically centred on the middle. One or two pictures fall back to the
* uniform grid, where the mosaic's "faded flank" reading has no partner.
*/
function GalleryShots({ images }: { images: readonly ResolvedImage[] }) {
if (images.length === 0) return null;
const mosaic = images.length === 3;
if (mosaic) {
return (
/* The track widths are the draft's own drawing (605 centre, ~427/426
flanks); the aspect ratios keep each tile at its drawn proportions, so
the row only reads as a mosaic from lg up and stacks full-width, full
opacity below it. `lg:items-center` sits the shorter flanks level with
the middle one, as the draft has them. gap-3xl is the closest step to
the draft's 60px. */
<ul className="grid list-none grid-cols-1 gap-3xl lg:grid-cols-[427fr_605fr_426fr] lg:items-center">
{images.map((image, index) => (
<li key={image.src} className="sh-reveal">
<figure className="flex flex-col gap-xs">
<div
className={cx(
"sh-lift sh-lift--grow rounded-control border border-[var(--border-subtle)] hover:border-[var(--border-accent)]",
index === 1 ? "aspect-[605/310]" : "aspect-[427/228] lg:opacity-50",
)}
>
<img
src={image.src}
alt=""
loading="lazy"
className="h-full w-full object-cover"
/>
</div>
{image.alt ? (
<figcaption className="body-sm-bold text-[var(--text-secondary)]">
{image.alt}
</figcaption>
) : null}
</figure>
</li>
))}
</ul>
);
}
return (
<ul className="grid list-none grid-cols-1 gap-lg sm:grid-cols-2 lg:grid-cols-3">
{images.map((image) => (
<li key={image.src} className="sh-reveal">
<figure className="flex flex-col gap-xs">
<div className="sh-lift sh-zoom rounded-control border border-[var(--border-subtle)] hover:border-[var(--border-accent)]">
<div className="sh-lift sh-lift--grow rounded-control border border-[var(--border-subtle)] hover:border-[var(--border-accent)]">
<img
src={image.src}
alt=""
@@ -497,7 +580,8 @@ export default function Home({ loaderData }: Route.ComponentProps) {
reverse: true,
},
]}
reveal
reveal
wordmark={HERO_WORDMARK[theme]}
/* EU visibility sits at the top of the hero (the draft's badge). The
emblem matches the page surface the hero stands on, which the theme
cookie decides - dark emblem in dark mode, light emblem in light
@@ -521,11 +605,17 @@ export default function Home({ loaderData }: Route.ComponentProps) {
/>
{/* WHAT THE PROJECT IS, AND WHO DELIVERS IT */}
{/* `bleed` drops the 80rem Container so the band's content - the three
pictures especially - runs the full width of the viewport, the way
the draft draws the mosaic. The gutter the Container used to add is
re-applied here, and the header column still constrains itself to its
own measure, so only the gallery actually strays to the edges. */}
<PageSection
aria-labelledby="home-approach-heading"
band
bleed
>
<div className="flex flex-col gap-3xl">
<div className="flex w-full flex-col gap-3xl px-[var(--layout-gutter)] lg:px-[var(--layout-gutter-lg)]">
<div className="sh-reveal mx-auto flex max-w-[55rem] flex-col items-center gap-lg text-center">
<SectionHeader
id="home-approach-heading"