# Performant Brand Motion and Layered Hero Comparisons

Use this pattern when a marketing or local-business website needs a memorable logo intro or an interactive “drawing to real result” hero without turning the site into a WebGL demo.

## Decision rule

Do not reintroduce Three.js, GSAP, or a global animation runtime merely because the effect looks three-dimensional. First test whether a transparent 3D-rendered logo asset plus CSS transforms can deliver the illusion. Prefer the smallest implementation that preserves the brand moment and keeps the underlying page rendered and usable.

Use WebGL only when the effect truly requires changing geometry, lighting, camera parallax, or live 3D interaction.

## Short branded intro

Recommended structure:

1. Render the real page immediately beneath a fixed, `aria-hidden` overlay.
2. Dim the page with a lightweight color layer; avoid expensive full-screen blur/backdrop filters.
3. Animate a transparent 3D logo asset in three phases:
   - quick, calm entrance,
   - short near-static hold,
   - accelerating scale through the logo opening while the overlay fades.
4. Keep the overlay `pointer-events: none` so it never traps navigation.
5. Run once per tab/session using a stable `sessionStorage` key.
6. Add a small inline head guard that applies the pending class before first paint, preventing an overlay flash after the page is already visible.
7. Always add an `animationend` cleanup plus a hard timeout fallback.
8. Respect `prefers-reduced-motion` and Save-Data. The default may skip the intro, but when the owner requires a consistent branded opening on every device, show a short static logo hold with no zoom/rotation instead of rendering nothing. Never force the full animated path in reduced-motion mode. On iOS, a missing intro plus an abrupt threshold-based hero swap often means the system-wide “Reduce Motion” preference is active in both Safari and Edge.
9. Shorten the duration on mobile. A useful budget is roughly 1.1–1.6 seconds total.
10. Preload only the small logo asset used by the intro, not an animation library.

A query flag such as `?intro=1` is useful for QA to replay an otherwise session-limited intro. It should not appear as public explanatory copy.

## Layered drawing-to-reality hero

When a planning drawing and completed-project photo share the same crop and camera geometry:

1. Produce matching responsive derivatives for both assets, including a dedicated mobile crop. Generate drawing and reality derivatives with the **same deterministic resize/crop operation** from registered masters; two independently chosen “similar” mobile crops will double during the blend even when their output dimensions match.
2. Place them as absolute layers with identical `object-fit` and `object-position`.
3. Show the drawing as the initial LCP-friendly layer.
4. Reveal the real image with the lightest brand-consistent technique:
   - `clip-path: circle(...)` is a neutral default;
   - when a circular spotlight conflicts with the design, use the alpha channel of an existing lightweight logo/emblem asset as a CSS `mask-image` and ease its opacity/position;
   - do not add WebGL merely to obtain a branded mask.
5. Throttle pointer-coordinate writes with `requestAnimationFrame`; for a softer rendered feel, interpolate current coordinates toward the pointer target and stop the loop once the difference is negligible. For an emblem mask that must reach every edge, track the pointer in pixels relative to `getBoundingClientRect()`, set the mask's top-left to `pointer - maskSize/2`, and allow negative positions at the top/left. CSS percentage mask-position semantics and clamps such as `48–92%` commonly make half the hero unreachable.
6. Only enable pointer-follow interaction for `(hover: hover) and (pointer: fine)`. Attach tracking to the full hero interaction surface or a common ancestor, not only the background layer: copy/buttons above the media otherwise make the left side appear dead.
7. Touch behavior must match the requested UX:
   - default: a real `aria-pressed` button that toggles the whole real image;
   - alternative: hide the button on small touch screens and map natural page scroll continuously to drawing→reality state, reversing automatically when the user scrolls back up. Start changing state on the first scroll pixels and use enough travel (often roughly 240–320px or 30–40% of viewport height) to avoid an abrupt switch. Let scroll drive opacity directly—do not add a second CSS opacity transition that lags behind it. A subtle reciprocal blur/scale can improve the morph only when both layers remain geometrically registered. Do not intercept native vertical scrolling. On iOS, scroll events can be coalesced or delayed during the finger gesture; add passive `touchstart`/`touchmove` tracking that predicts scroll progress from finger displacement and writes the same morph variables, then reconcile with real `scrollY` on scroll/touchend. Keep `touch-action: pan-y` and never call `preventDefault()`. Gesture cues are optional and should be removed when the interaction is self-evident or the owner requests a cleaner hero.
8. Load the real image at lower priority (`loading="lazy"`, `fetchpriority="low"`) while keeping the drawing eager.
9. Keep text contrast in an independent gradient layer so the reveal never makes headings unreadable. If asked to lighten mobile by a percentage, adjust the mobile-only shade alpha and re-check text contrast at 0%, 50%, and 100% morph states.
10. For reduced motion, disable spatial movement, blur, and animated transitions. A continuous reciprocal opacity crossfade tied directly to scroll/touch position is often preferable to a binary threshold: it avoids an abrupt visual jump while still removing zoom and parallax. If the product policy requires zero visual interpolation, use a threshold—but test that this does not recreate the exact defect the owner reported.

## Repeating intro variant

Running once per session is the default recommendation, not a hard rule. If the site owner explicitly wants the brand intro on every fresh homepage load (including mobile):

- remove the session-storage gate and apply the pending class on each `/` document load;
- keep `prefers-reduced-motion` as the mandatory exception;
- keep the mobile duration shorter than desktop;
- verify a real reload shows the intro again in both desktop and mobile Playwright projects;
- ensure Astro client navigation back to `/` creates/reinitializes the overlay without accumulating listeners. A robust Astro pattern is to render the intro component in the shared layout, keep it inert off-home, and use a small `data-astro-rerun` inline initializer on each body swap. Guard timeout cleanup with `intro.isConnected` so a timer from a previous page cannot alter the new document state;
- test both a direct mobile load and a client-routed transition from a subpage to Home; a reload-only test misses the common “Home link does not replay” regression.

## Photo-enabled inquiry fallback

For static sites without a verified upload backend:

1. Offer separate camera (`accept="image/*" capture="environment"`) and gallery (`accept="image/*" multiple`) controls.
2. Validate image type, count, and size locally; preview with object URLs and revoke them on removal.
3. Do not put `File` objects in `localStorage` and explain that photo selection is lost on reload.
4. If a verified endpoint exists, submit `multipart/form-data` when files are selected while preserving the existing JSON path without files.
5. Without an endpoint, use Web Share Level 2 (`navigator.canShare({files})` + `navigator.share`) so mobile users can hand text and photos to Mail or WhatsApp. If unsupported, open the honest mailto fallback and tell the user attachments must be added manually.
6. Update the privacy page to explain when selected files remain local and when they leave the device.

## Stacking and pointer-events pitfall

A full-height hero copy container above the background can silently intercept all pointer movement even if visually transparent. Setting the wrapper to `pointer-events: none` and restoring `pointer-events: auto` only on copy/buttons is useful, but it still means a listener attached only to the background layer will not receive events originating on the copy sibling. Attach pointer tracking to the common `.hero` ancestor and compute coordinates against the media stage's `getBoundingClientRect()`; events from both copy and media then bubble through the same surface. Keep the comparison button clickable and verify pointer positions over the left copy area, center, and both physical image edges.

## Trust-content provenance

A visually polished trades site still needs human proof. When reviewing Drive or other company archives:

- logos, question sheets, and old reports do not prove current team composition or job titles;
- never infer that a person in an unlabeled photo is the managing director;
- do not publish names, roles, team size, response time, memberships, or testimonials without a verified source or explicit approval;
- if no approved portrait exists, keep the public company section factual and record the content gap internally.

The next highest-value content request is usually an authentic portrait of the verified owner/managing director in a real work context, followed by a current team photo and clearly attributed project case studies. Publish no placeholder team cards.

## Review-only content placeholders

When the client wants visible placeholders for a management review but unfinished copy would damage the live site's credibility:

1. Create a dedicated review route (for example `/inhalte-ergaenzen/`) instead of exposing placeholder cards on the public homepage.
2. Give the route `noindex, nofollow`, exclude it from the sitemap, and do not link it in public navigation.
3. Organize gaps by placement, required evidence, and approval owner: managing-director portrait/role, current team image, verified company facts, testimonials, memberships, and attributable project images.
4. Test both that the review route contains the checklist and that public rendered pages contain none of the editorial placeholder phrases.

## Mobile conversion and fixed-navigation pitfalls

A fixed mobile contact bar can cover the hero trust strip, lead cards, or the first form step even when the page has bottom padding. Treat this as a viewport-occlusion problem, not only a document-height problem.

- Hide the fixed bar while any primary lead region is visible (`.hero`, `.page-hero`, wizard intro, or equivalent), then show it after those regions leave the viewport.
- Use one `IntersectionObserver` with a set of currently visible lead targets; show the bar only when the set is empty.
- Make `visibility` switch immediately and animate only `transform`, otherwise a 150–250ms visibility transition can create a flash or brief overlap.
- Keep direct phone/request actions inside the hero so hiding the fixed bar there does not remove contact access.
- Verify hidden-at-top and visible-after-scroll states on both the homepage and form page.

For mobile inquiry flows:

- require a conscious first-step choice; do not silently preselect the most specific project category;
- keep an explicit “not sure yet” option;
- expose the live project summary in a compact `<details>` near the progress indicator and hide the large desktop summary card on mobile;
- avoid consecutive CTAs with the same destination, especially at the end of a long homepage.

## Legacy reference migration and provenance

When the owner points to an official old website as the source for project references:

1. Inspect the original page and map each project heading, year, architect, description, and its image group before downloading anything. Legacy site builders often place headings and image galleries in sibling columns; infer grouping from the shared layout wrapper, not image filename alone.
2. Prefer the source site's larger derivative over a 500×500 thumbnail, preserving the official page as the attribution source. Some image processors require a normal browser User-Agent and same-site `Referer`; use those request headers rather than treating a 403 as proof the asset is unavailable.
3. Migrate one clearly associated image per project first, optimize locally, and keep a concise repository document mapping local file → official project → source file/URL/date accessed.
4. Never merge two same-named projects with different year/architect details. Give them distinct slugs and clarifying public titles.
5. Replace generic reference placeholders everywhere, count rendered cards/pages/images, and put selected real project images on the homepage—not only text links—when the owner asks to bring old references into the new site.
6. Visually inspect a contact sheet of all migrated images before publication. Verify motif plausibility against the official grouping; do not invent project identity from visual resemblance.

Official social profiles can corroborate service categories and provide links, but do not publish volatile follower counts or infer detailed claims from inaccessible posts.

## Container alignment and title-scale pitfall

A shared `.container` commonly has `margin-inline: auto`. Applying `max-width: var(--text-width)` to that same outer container silently centers the whole intro block, which can look “pushed right” even though its text is left-aligned. Keep the outer container at the site's full content width and apply the reading-width limit to its `h2`/`p` child instead. Measure the left edge of the intro and heading in-browser to confirm they match.

When the owner flags oversized text, audit the whole heading system rather than shrinking only the named example: page H1, homepage hero H1, section-heading H2, service-detail H1/H2, CTA headings, and mobile overrides. Reduce display maxima and tighten line-height consistently, then inspect FAQ/reference/about pages at desktop and mobile widths.

## Public-copy hygiene

After implementation, search for editorial/developer explanations such as “cards are only shown when…”, “image areas are omitted…”, “placeholder”, “server connection”, and “hosting decision”. Replace them with customer-facing context or remove them entirely. Internal asset-provenance rules belong in documentation, not public page copy.

## Verification recipe

Automate at least:

- intro is visible on the intended trigger, underlying H1 is already present, then intro becomes hidden within the timeout;
- intro persistence matches the chosen policy: for session-limited mode verify the session key and no replay; for every-load mode verify a real reload on desktop/mobile **and a client-routed subpage→Home transition**;
- desktop branded mask responds over copy and media, reaches left edge/left third/center/right third/right edge, and the full-image fallback remains keyboard operable. Assert the mask's computed center/position variables rather than relying only on a screenshot; wait for the interpolation to settle before measuring;
- touch mode either has an accessible toggle or a verified reversible scroll-linked morph, according to the chosen design. Sample multiple scroll points (for example 0/25/50/75/100%), verify reciprocal drawing/reality state, confirm gesture cues are absent when not requested, and visually compare fixed 0/50/100% layer screenshots for double contours;
- reduced-motion context either skips the intro or shows the intentionally static no-zoom variant, keeps the mobile drawing→reality blend free of spatial motion, and produces no console errors;
- mobile viewport has no horizontal overflow; fixed contact navigation is hidden over lead regions and visible after scrolling past them;
- camera/gallery selection, preview/removal, native file sharing, and honest mail/manual-attachment fallback are tested separately;
- review-only routes are `noindex`, absent from sitemap/navigation, while public pages contain no placeholder/editorial copy;
- Axe has no serious/critical violations;
- Lighthouse is run on the actual configured intro policy so animation cost is included rather than accidentally measuring a skipped state;
- local and deployed URLs both contain a deterministic new marker before claiming deployment success.

Report real Lighthouse scores and Core Web Vitals; do not estimate them.
