# Interactive Team Section — hover thumbnails reveal giant staggered names

## Goal

Build a full-viewport "meet the team" section: a centered row of 9 small square portrait thumbnails sits above a giant clip-masked headline area. Hovering a thumbnail smoothly enlarges it (70px → 140px) while that member's name slides up into view as huge red condensed type, animated character-by-character with a center-out SplitText stagger. When the cursor enters the row at all, a default title ("The Squad") animates in the same way in an off-white color; leaving the row hides it again.

## Tech

Vanilla HTML/CSS/JS with ES module imports. Use `gsap` (npm) plus the GSAP plugin `SplitText` (`import { SplitText } from "gsap/SplitText"`, then `gsap.registerPlugin(SplitText)`). No other libraries, no smooth-scroll.

## Layout / HTML

```
<section class="team">
  <div class="profile-images">
    <div class="img"><img src="..." alt="" /></div>   <!-- x9 -->
  </div>
  <div class="profile-names">
    <div class="name default"><h1>The Squad</h1></div>
    <div class="name"><h1>Colin</h1></div>
    <div class="name"><h1>Liam</h1></div>
    <div class="name"><h1>Tabitha</h1></div>
    <div class="name"><h1>Tyson</h1></div>
    <div class="name"><h1>Max</h1></div>
    <div class="name"><h1>Everest</h1></div>
    <div class="name"><h1>Simon</h1></div>
    <div class="name"><h1>Gideon</h1></div>
    <div class="name"><h1>Benton</h1></div>
  </div>
</section>
```

- Exactly 9 `.img` thumbnails and 10 `.name` blocks: the first one has the extra class `default` and holds the group title; the remaining 9 map 1:1 (in order) to the 9 thumbnails.
- Load the JS with `<script type="module" src="./script.js">`.

## Styling

- Global reset: `* { margin: 0; padding: 0; box-sizing: border-box; }`. All `img` elements: `width: 100%; height: 100%; object-fit: cover;`.
- Google Font: **Barlow Condensed** (import the full weight range; weight 900 is the one used).
- `.team`: `position: relative; width: 100vw; height: 100svh; background-color: #0f0f0f; color: #e3e3db;` flex column, `justify-content: center; align-items: center; gap: 2.5em; overflow: hidden;`.
- `.profile-images`: `width: max-content;` flex row, centered items.
- `.img`: `position: relative; width: 70px; height: 70px; padding: 5px; cursor: pointer; will-change: width, height;` — the padding creates the gutter between thumbnails, so animating width/height grows the tile in place. Inner `img { border-radius: 0.5rem; }`.
- `.profile-names`: `width: 100%; height: 15rem; overflow: hidden;` plus `clip-path: polygon(0 0, 100% 0, 100% 100%, 0% 100%);` (a full-rect clip that hard-masks the sliding text).
- `.name h1`: `position: absolute; width: 100%; text-align: center; text-transform: uppercase; font-family: "Barlow Condensed"; font-size: 15rem; font-weight: 900; letter-spacing: -0.2rem; line-height: 1; color: #f93535; user-select: none; transform: translateY(100%);` — every heading is absolutely stacked in the same spot and parked one full line **below** the mask.
- `.name.default h1`: overrides `color: #e3e3db;` and `transform: translateY(-100%);` — the default title is parked one full line **above** instead.
- `.name h1 .letter` (class added by JS to each SplitText char): `position: relative; transform: translateY(0%); will-change: transform;`.

### The reveal mechanism (critical)

Each `h1` keeps a **static** CSS translateY offset (+100% for member names, −100% for the default) and is never animated itself. GSAP only animates the **letters inside**. Because the letters are `position: relative` children, their y transform adds to the parent's: letters at `y: -100%` inside a `+100%` parent net out to 0 → the name is visible; letters at `y: 0%` leave it hidden below. For the default title it's mirrored: letters at `y: 100%` inside a `−100%` parent net to 0 → visible (this is the initial state, so "The Squad" shows on load); letters at `y: 0%` hide it above.

## GSAP effect (exhaustive)

Everything runs inside `DOMContentLoaded`. Triggers are pure **hover** (mouseenter/mouseleave); no ScrollTrigger, no timelines — independent `gsap.to` tweens.

1. **SplitText setup (all 10 headings):** `new SplitText(heading, { type: "chars" })` on every `.name h1`; add the class `letter` to every resulting char element (`split.chars.forEach(char => char.classList.add("letter"))`).
2. **Initial state:** `gsap.set` the default title's letters (`.name.default` → `.letter`) to `y: "100%"`. Combined with the parent's CSS `-100%` this makes "The Squad" visible on load.
3. **Desktop gate:** wrap all event wiring in `if (window.innerWidth >= 900)` — below 900px there are no listeners and no animation at all.
4. **Per-thumbnail hover (index i pairs with name i+1, skipping the default):**
   - `mouseenter` on `.img`:
     - Tween the `.img` wrapper: `width: 140, height: 140, duration: 0.5, ease: "power4.out"` (grows from 70×70 to 140×140, pushing siblings apart).
     - Tween that member's letters: `y: "-100%", duration: 0.75, ease: "power4.out", stagger: { each: 0.025, from: "center" }` → the name rises into the mask, characters unfolding from the middle outward.
   - `mouseleave` on `.img`:
     - Tween the wrapper back: `width: 70, height: 70, duration: 0.5, ease: "power4.out"`.
     - Tween the letters back: `y: "0%", duration: 0.75, ease: "power4.out", stagger: { each: 0.025, from: "center" }` → the name sinks back below the mask.
5. **Row-level hover (default title):**
   - `mouseenter` on `.profile-images` container: tween the default letters to `y: "0%", duration: 0.75, ease: "power4.out", stagger: { each: 0.025, from: "center" }` → "The Squad" slides **up and out** of view (so a member name can take the stage).
   - `mouseleave` on the container: tween them back to `y: "100%"` with the same duration/ease/stagger → "The Squad" drops back into view.
   - Note these container events fire alongside the per-thumbnail ones (entering a thumbnail also means entering the container), which is exactly what produces the crossfade-like swap: default title exits upward while the hovered name enters from below.

## Assets / images

9 square (1:1) editorial studio portraits — tight head-and-shoulders shots of different people against solid, saturated color-block backgrounds (warm reds, oranges, ochres, sage/olive greens, teal), consistent fashion-editorial lighting. One per thumbnail, in row order. Any cohesive square portrait set works; no logos or brand marks.

## Behavior notes

- **Desktop-only interaction:** below 900px viewport width no listeners are attached; the section renders statically with "The Squad" visible.
- Responsive layout at `max-width: 900px`: `.team` becomes `flex-direction: column-reverse` (names above the images), `.profile-images` wraps (`flex-wrap: wrap; max-width: 90%;`), `.img` shrinks to `60px × 60px` with `2.5px` padding, `.profile-names` height drops to `4rem`, and `h1` becomes `font-size: 4rem; letter-spacing: 0;`.
- No loops, no scroll hijacking; overlapping tweens on quick hover swaps are fine (GSAP's default overwrite behavior handles it).
