# Parallax Pinned Chapters + Snap Carousel Design

**Date:** 2026-03-05
**Status:** Approved

## Overview

Redesign the landing page scroll experience using a "chapter" model. Sections are grouped into 4 chapters that pin (sticky) in place as the user scrolls. Each subsequent chapter slides up and covers the previous one, creating a layered card-stack effect. The services carousel is rebuilt with CSS scroll-snap for native horizontal snapping.

## Chapter Structure

| Chapter | Sections                   | Purpose         |
| ------- | -------------------------- | --------------- |
| 1       | HeroSection + TrustStrip   | "Who we are"    |
| 2       | ServicesCarousel           | "What we offer" |
| 3       | WhyUsSection + CTABanner   | "Why choose us" |
| 4       | SupportDownloads + Contact | "Get in touch"  |

## Scroll Behavior

### Pinning Mechanism

- Each chapter wrapper uses `position: sticky; top: 0`
- Chapters stack with increasing `z-index` (1, 2, 3, 4)
- Each chapter has a solid background so it fully covers the chapter behind it
- A subtle `box-shadow` on the top edge of each chapter creates the "sliding over" depth illusion
- The outer container uses `scroll-snap-type: y proximity` for soft vertical snap (helps chapters land cleanly but does not fight free scrolling)

### Chapter Details

**Chapter 1: Hero + TrustStrip**

- `min-height: 100dvh` to fill the viewport
- Hero fills most of the space, TrustStrip anchored at the bottom
- Pins in place while Chapter 2 slides up over it
- Existing gradient background + dot pattern preserved

**Chapter 2: ServicesCarousel**

- Natural content height (not forced to viewport height)
- Pins in place while Chapter 3 slides up
- Carousel rebuilt with CSS scroll-snap (see Carousel section below)

**Chapter 3: WhyUs + CTABanner**

- Natural content height
- Pins in place while Chapter 4 slides up
- WhyUs image gets a subtle parallax speed difference (scrolls slightly slower than text) for added depth

**Chapter 4: SupportDownloads + Contact**

- Normal scroll, no pinning (last chapter, nothing covers it)
- Existing fade-in animations preserved

## Carousel Rebuild (CSS Scroll-Snap)

The current carousel uses absolute positioning with JS-driven transforms and offset calculations. This will be replaced with a native CSS scroll-snap approach.

### New approach

- Horizontal scroll container with `scroll-snap-type: x mandatory`
- Each card is a snap point with `scroll-snap-align: center`
- Cards are laid out in a flex row, each taking full width of the visible area
- Native touch/swipe works out of the box
- Dot indicators sync with scroll position via `IntersectionObserver` or `scrollLeft` calculation
- Arrow buttons use `scrollBy()` with `behavior: 'smooth'`
- Auto-play uses `scrollBy()` and pauses on user interaction (touch, hover, focus)
- Personal/Business tab toggle resets scroll position to 0

### Benefits over current approach

- No absolute positioning or transform math
- Touch/swipe is native and performant
- Reduced JS complexity
- Better accessibility (native scroll semantics)
- Respects `prefers-reduced-motion` automatically

## Technical Implementation

### New component: ChapterWrapper

A reusable wrapper that applies sticky positioning:

```tsx
interface ChapterProps {
  children: React.ReactNode;
  zIndex: number;
  className?: string;
}

function Chapter({ children, zIndex, className }: ChapterProps) {
  return (
    <section className={cn("sticky top-0", className)} style={{ zIndex }}>
      {children}
    </section>
  );
}
```

### Updated PublicLandingView structure

```tsx
<main>
  <Chapter zIndex={1} className="min-h-dvh">
    <HeroSection />
    <TrustStrip />
  </Chapter>

  <Chapter zIndex={2}>
    <ServicesCarousel />
  </Chapter>

  <Chapter zIndex={3}>
    <WhyUsSection />
    <CTABanner />
  </Chapter>

  <div>
    {" "}
    {/* No sticky - last chapter */}
    <SupportDownloadsSection />
    <ContactSection />
  </div>
</main>
```

### Shadow/depth effect

Each Chapter (except Chapter 1) gets a top shadow to enhance the "sliding over" illusion:

```css
.chapter-overlay {
  box-shadow: 0 -8px 30px -10px rgba(0, 0, 0, 0.1);
}
```

## Mobile Considerations

- Use `100dvh` (dynamic viewport height) instead of `100vh` to avoid iOS address bar issues
- Carousel CSS scroll-snap is touch-native and works well on mobile
- Sticky positioning works on mobile browsers (iOS Safari, Chrome Android)
- If performance is poor on low-end devices, sticky can be disabled via a CSS class

## Accessibility

- `prefers-reduced-motion: reduce` disables sticky pinning behavior (falls back to normal scroll)
- Carousel maintains keyboard navigation (arrow keys, tab through cards)
- ARIA attributes preserved: `role="region"`, `aria-roledescription="carousel"`, slide labels
- Scroll-snap does not interfere with screen readers

## Files to Modify

| File                                                    | Change                                  |
| ------------------------------------------------------- | --------------------------------------- |
| `features/landing-page/views/PublicLandingView.tsx`     | Wrap sections in Chapter components     |
| `features/landing-page/components/ServicesCarousel.tsx` | Rebuild with CSS scroll-snap            |
| `features/landing-page/components/HeroSection.tsx`      | Adjust to fill chapter space (flex-1)   |
| `features/landing-page/components/TrustStrip.tsx`       | Adjust to anchor at bottom of Chapter 1 |
| `features/landing-page/hooks/useInfiniteCarousel.ts`    | Replace with scroll-snap hook or remove |
| `features/landing-page/components/index.ts`             | Export new Chapter component            |
| New: `features/landing-page/components/Chapter.tsx`     | Chapter wrapper component               |
| New: `features/landing-page/hooks/useSnapCarousel.ts`   | Hook for scroll-snap carousel state     |
