heroes /
Deep Zoom (Into the Screen)
The same scroll dive as heroes/deep-zoom, ending inside the product. A quiet office, a laptop on the desk, and the screen fills the frame as you arrive — drawn as live DOM glued to the photograph, so the interface is sharp at 5x instead of being an upscaled screenshot. This is mercury.com
Preview
Source
tsx
"use client";
import DeepZoomHero from "../deep-zoom/component";
import type { DeepZoomHeroProps } from "../deep-zoom/component";
/**
* Deep Zoom (Into the Screen) — the same dive as 'heroes/deep-zoom', pointed at
* a display instead of a surface.
*
* mercury.com's hero ends inside the monitor on the desk: the camera falls
* through a landscape and the product is what is waiting at the bottom. This is
* that ending. The kitchen version lands on a material and the copy is the
* payoff; this one lands on an interface and the interface IS the payoff, so
* the copy gets out of the way before you arrive.
*
* ── WHAT IS DIFFERENT, AND WHY EACH ONE IS DIFFERENT
*
* ONE PLATE, NOT THREE. There is no photograph of the screen to hand over to,
* because the screen is live DOM. The wide shot is asked for a 5.2x zoom on its
* own, which it cannot serve — and does not have to, because by the time it is
* that big it is a blurred room around a sharp rectangle. That is what endBlur
* is for: the focal plane moves to the display, the room falls away, and the
* upscaling goes with it. Photographically correct and convenient at once.
*
* THE SCREEN RECT IS MEASURED, AND THEN INSET FOR THE BEZEL. Two separate
* numbers, and conflating them was the first version's bug.
*
* Scanning the source for its dark rectangle finds the GLASS: x 558 to 817 at
* the top, y 333 to 505, in the 1376 x 768 frame. But with the display off, the
* bezel is exactly as black as the screen, so that rectangle is glass PLUS
* bezel — and drawing the interface across the whole of it paints over the
* bezel and out onto the lid. It reads as a screenshot stuck on top of a
* laptop rather than as a laptop that is switched on.
*
* So the interface is inset ~8px horizontally and ~7px vertically inside the
* measured glass, which leaves the photograph's own black showing as a frame:
* 41.13 / 44.27 / 17.73 / 20.70, focus at the centre of that, 50.0 / 54.62.
* The bezel is off-frame by the time the dive lands, which is correct — you are
* inside the screen by then, not looking at a computer.
*
* Note the glass is a slight trapezoid, a few px wider at the bottom than the
* top, because the lid is not perfectly square to the lens. The inset uses the
* NARROWER top width, so the black frame is a touch fatter along the bottom
* edge. That is the right way round: too much bezel reads as bezel, too little
* reads as a mistake.
*
* Regenerate the plate and all of this has to be measured again. Nothing fails
* loudly if it is not — the interface just sits slightly off its own laptop.
*
* ZOOM IS 1 / 0.1962 = 5.1 TO FILL THE FRAME WIDTH, and it is set a little past
* that so the bezel clears the edge. Any less and the dive ends on a laptop in
* a room rather than inside the product.
*
* THE COPY LEAVES. endOut is set, which the kitchen version does not use. A
* headline still sitting there when the UI arrives reads as a caption on a
* screenshot; letting it go turns the last beat into the product itself.
*
* ── THE SCREEN IS A PROP
* Pass anything as 'screen'. The default is a Hermes thread because that is the
* product this was built to show, and it is DOM rather than a screenshot, so it
* is sharp at every depth and can be edited without regenerating a picture.
*/
export type DeepZoomScreenHeroProps = DeepZoomHeroProps;
export default function DeepZoomScreenHero(props: DeepZoomScreenHeroProps) {
return (
<DeepZoomHero
brand="Hermes"
navLinks={[
{ label: "How it works", href: "#" },
{ label: "Memory", href: "#" },
{ label: "Pricing", href: "#" },
]}
navCta={{ label: "Get set up", href: "#" }}
navOut={0.7}
eyebrow="A personal AI that remembers"
headline="It already knows what you told it in June"
sub="Hermes keeps your notes and your decisions in one place you own, and answers out of them. You text it. It does not start over every morning."
cta={{ label: "See how it works", href: "#" }}
endHeadline="Everything you have ever told it, still there"
endSub="Your vault, on your machine. Nobody else's model gets to keep it."
endCta={null}
endIn={0.3}
endOut={0.54}
plates={[
{
src: "/heroes/deep-zoom/office-wide.webp",
alt: "A quiet office desk with an open laptop, a window behind it and a monstera in the corner",
aspect: 1376 / 768,
focus: { x: 50.0, y: 54.62 },
zoom: 5.15,
inset: {
x: 41.13,
y: 44.27,
w: 17.73,
h: 20.7,
radius: 3,
baseFont: 3.4,
},
},
]}
endBlur={14}
scrim={0.4}
accent="#c98a3c"
{...props}
/>
);
} Claude Code Instructions
CLI Install
npx innovations add deep-zoom-screenWhere to use it
Use this when the thing being sold is on a screen — software, a dashboard, an app, an agent, a booking system. The dive earns the reveal: you spend a viewport and a half travelling toward a lit rectangle across a room, and what is waiting is the product. It is mercury.com's hero read literally, and it is the strongest version of this mechanic because the payload has a reason to be looked at closely.
In Astro:
---
import DeepZoomScreenHero from '../components/innovations/heroes/deep-zoom-screen';
---
<DeepZoomScreenHero client:load />
This wraps 'heroes/deep-zoom' and passes it one plate plus an inset. Every prop on the base component passes straight through, so override copy and imagery here rather than forking it.
THE SCREEN IS LIVE DOM, NOT A SCREENSHOT, and that is the whole reason this variant exists. Pass anything as 'screen'. It is positioned in real pixels each frame — not scaled by the parent transform — because a transformed element that the compositor has promoted gets rasterised once at layout size and then magnified as a bitmap, so DOM inside a scale(5) parent is a 5x blow-up of small text. Laying it out at its true size each frame means the browser rasterises it at its true size, which is why the interface arrives sharp. Everything inside it should be sized in em off the inset's own font-size, which the effect drives.
FOUR NUMBERS ARE MEASURED OFF THE PHOTOGRAPH, and they are the only fragile thing here. Scanning the bundled plate for its dark rectangle finds the glass at x 558 to 817, y 333 to 505, in a 1376 x 768 source.
DO NOT USE THAT RECTANGLE DIRECTLY — this was the first version's visible bug. With the display switched off the bezel is exactly as black as the screen, so a dark-pixel scan returns glass PLUS bezel, and an interface drawn across all of it paints over the bezel and out onto the lid. It stops looking like a laptop that is on and starts looking like a screenshot stuck on top of a laptop. Inset it: about 8px horizontally and 7px vertically inside the measured glass, so the photograph's own black shows as a frame. That gives 41.13 / 44.27 / 17.73 / 20.70, with focus at the centre of the inset rect, 50.0 / 54.62.
Two details worth carrying to the next photograph. The glass is usually a slight trapezoid — a few px wider at the bottom, because the lid is rarely perfectly square to the lens — so take the NARROWER top width and let the bottom frame run a little fatter; too much bezel reads as bezel, too little reads as a mistake. And the bezel is off-frame by the time the dive lands, which is right: you are inside the screen by then, not looking at a computer.
SWAP THE PHOTOGRAPH AND ALL OF THIS HAS TO BE MEASURED AGAIN. Nothing fails loudly if you skip it — the interface just sits slightly off its own laptop, which reads as a rendering fault rather than as a mistake.
GENERATE OR SHOOT THE PLATE WITH THE OVERLAY IN MIND: the display straight-on with no rotation or keystone, and switched OFF. A dark rectangle keys cleanly and gives the lit overlay something to sit in. A screen with content already on it fights whatever you put over it, and a screen at an angle needs a perspective transform this component does not do.
ZOOM IS MEASURED, NOT SET. The 'zoom' on the plate is only a fallback: for a plate with an inset the base component computes the real number from live geometry on mount and on every resize, so the dive always ends with the interface filling the frame. A hard-coded number was the first version and it broke the moment the window was not a laptop — the plate box is sized to COVER the viewport, so on a portrait phone it is three times wider than the screen and a value tuned on a desktop overshoots into a single message bubble. The one dial left is 'insetCrop' (default 1.25): how much of the interface the dive may crop past the scale that would have fitted it whole. Short of filling the frame the dive ends on a laptop in a room instead of inside the product, which is the difference between a nice photograph and a hero.
ONE PLATE, AND endBlur DOES THE WORK. There is no second photograph to hand over to, so the wide shot is asked for 5x on its own. It does not have to carry that, because the focal plane moves to the display: the room falls out of focus as you arrive, which is what a real lens would do and also what hides the upscaling. Set endBlur to 0 and you will see exactly how much it was doing.
THE COPY LEAVES BEFORE YOU LAND, and it leaves EARLY — in at 0.30, out at 0.54. endOut is set here and deliberately unset on the kitchen version. Two reasons, and only the second one showed up on screen. A headline still parked over the UI at the bottom turns the payoff into a captioned screenshot. And a headline running ACROSS a laptop that is by then half the frame is two things fighting for the same pixels, which is exactly what an earlier 0.42-to-0.68 window produced. Give the line its own moment while the room is still wide, then get it out before the screen is big.
THE SITE NAV LEAVES TOO, at navOut 0.7. The app has its own title bar and its own wordmark, so letting the page nav sit on top of them puts two 'Hermes' marks in the same corner. On the kitchen version the nav stays, because over a photograph it is the only thing still saying this is a website.
THE SCREEN IS LIT AT REST, not woken during the dive. It was a wake at first, on the theory that a rectangle lighting up gives the eye a reason to travel toward it. Two things killed that. A dark laptop just reads as a switched-off laptop, and the opening frame is the one most visitors ever see. And the wake ran across the same scroll as the opening copy leaving, so there was no frame anywhere in the hero with the screen lit AND the headline crisp — and that frame is what a gallery card needs.
STATIC STATE: no JS or reduced motion gives you the photograph, both pieces of copy, and the screen rendered underneath at a legible size as an ordinary block. The interface is the point of the page, so the fallback shows it rather than hiding it behind a scroll handler that is not going to run.