# Project Overview Modal — Click-to-Reveal Sliding Detail Overlay

## Goal
Build a full-screen **project index**: a numbered list of projects sits at the bottom-left of a dark, non-scrolling viewport. Clicking any list row plays a **paused GSAP timeline** that slides a large white detail panel **up from far off-screen** while **un-rotating it from a 20° tilt to flat**, landing it pinned to the bottom-right. The panel's content (title, category, copy, link, image) is swapped in from a data array for whichever row you clicked. Clicking **Close**, or anywhere outside the panel, **reverses the same timeline** and the panel tilts and drops back off-screen. The star effect is that single tilt-corrected slide-up driven by one reversible timeline.

## Tech
Vanilla HTML/CSS/JS with ES module imports. Use **`gsap` (npm)** only — **no GSAP plugins**, no ScrollTrigger, no Lenis, no SplitText.
```js
import gsap from "gsap";
import { data } from "./data.js";   // project content lives in a sibling module
```
No framework. `script.js` is loaded as `<script type="module" src="./script.js">` at the end of `<body>`, so it runs after the DOM is parsed — no `DOMContentLoaded` wrapper needed. The whole effect is **one `gsap.timeline({ paused: true })` with a single `.to()` tween**, played/reversed on click. Project a Vite-style dev server that resolves the npm import.

## Layout / HTML
Four top-level blocks in `<body>`: a fixed `.nav`, a fixed `.footer`, the hidden `.overlay` panel, and the `.container` holding the list. Class/ID names are load-bearing — the JS/CSS query them.

```html
<body>
  <div class="nav">
    <p>Nanotech</p>          <!-- fictional demo brand, top-left -->
    <p>Showreel</p>          <!-- top-right -->
  </div>

  <div class="footer">
    <p>Nanotech 2023 &copy;</p>   <!-- bottom-right -->
  </div>

  <!-- The white detail panel, parked far off-screen and tilted -->
  <div class="overlay">
    <div class="overlay-header">
      <div class="col">
        <h1 id="item-name">Item 1</h1>
        <p id="item-category">Item Category</p>
      </div>
      <div class="col">
        <p id="close-btn">Close</p>
      </div>
    </div>
    <div class="item-details">
      <p><a id="item-link" href="#"><i class="icon-arrow-up-right"></i> View Site</a></p>
      <p id="item-copy">Lorem ipsum dolor sit amet…</p>
    </div>
    <div class="img-container">
      <img id="item-img" src="(img-1)" alt="" />
    </div>
  </div>

  <!-- The project index -->
  <div class="container">
    <div class="items">
      <div class="item">
        <div class="item-index">01</div>
        <div class="item-name">Advanced Nanotech Fabrics</div>
        <div class="item-year">2022</div>
      </div>
      <!-- …9 more .item rows… -->
    </div>
  </div>

  <script type="module" src="./script.js"></script>
</body>
```

**The 10 list rows** (index / name / year — three flex columns per row):
```
01  Advanced Nanotech Fabrics            2022
02  Natural Bio-luminescent Accents      2021
03  Integrated Electronic Wearables      2023
04  Dynamic Morphing Clothing            2021
05  3D Holographic Prints                2017
06  Atmospheric Aero-adaptable Garments  2018
07  Responsive Skinsync Attire           2019
08  Sustainable Zero-waste Weavings      2023
09  Lab-crafted Synthetic Leather        2022
10  Neuro-responsive Fashion Gear        2021
```

The link's leading `<i>` is an **arrow-up-right glyph** (original uses a Phosphor icon font `<i class="ph-bold ph-arrow-up-right">` loaded via CDN `<script>`; any icon font or inline SVG arrow works, or omit it).

### `data.js` — the swap-in content (one object per list row, same order)
Export `const data = [ … ]; export { data };`. Each object has `itemName`, `itemCategory`, `itemLink` (all `"#"`), `itemCopy` (a ~50-word marketing paragraph), and `itemImg` (path to one of the 5 images). The overlay `itemName` is a shortened form of the list-row name (e.g. row "Advanced Nanotech Fabrics" → overlay "Nanotech Fabrics"). Categories cycle among a few labels like "Eco-Futurism Line", "Neural Nuance", "Galactic Glam", "Digital Elegance". **Images cycle 1→5 then repeat:** items 1 & 6 use `img-1`, items 2 & 7 use `img-2`, 3 & 8 → `img-3`, 4 & 9 → `img-4`, 5 & 10 → `img-5`.

## Styling
Global reset `* { margin:0; padding:0; box-sizing:border-box }`. Font: a **neutral sans-serif** (original references a proprietary face "Aeonik"; any clean grotesque like Inter / `system-ui` matches).

**Palette (exact hex):**
- `#0f0f0f` — page background (near-black).
- `#fff` — page text, and the **overlay panel background**.
- `#000` — text color inside the white overlay (and link color).

**Page shell — the page never scrolls:**
```css
html, body { width:100vw; height:100vh; overflow:hidden; background:#0f0f0f; color:#fff; }
h1 { font-weight:500; margin-bottom:0.5em; }
a  { text-decoration:none; color:#000; }
img { width:100%; height:100%; object-fit:cover; }
```

**Nav / footer (fixed chrome):**
```css
.nav { position:fixed; width:100%; padding:2em; display:flex; justify-content:space-between; }
.footer { position:fixed; bottom:0; right:0; padding:2em; }
```

**Container + list (bottom-left):**
```css
.container { width:100%; height:100%; display:flex; justify-content:flex-start; align-items:flex-end; padding:2em; }
.items { position:absolute; width:50%; display:flex; flex-direction:column; }   /* left half */
.item  { display:flex; padding:0.25em 0.5em; cursor:pointer; }
.item-index { flex:1; }   /* narrow */
.item-name  { flex:4; }   /* wide */
.item-year  { flex:1; }   /* narrow */
```

**The overlay panel — this CSS defines the animation's START state.** It is parked `1200px` below the viewport and rotated `20deg`, tilting up from its bottom-center pivot:
```css
.overlay {
  position:absolute; bottom:-1200px; right:0;      /* far below the screen */
  width:70%; height:700px; padding:2em;
  background:#fff; color:#000;
  overflow-x:hidden; overflow-y:scroll;            /* the panel scrolls internally… */
  z-index:2;
  will-change:bottom;
  transform:translateZ(0) rotate(20deg);           /* …tilted 20° at rest */
  transform-origin:bottom center;
}
.overlay::-webkit-scrollbar { display:none; }      /* hide its scrollbar */
```

**Overlay internals:**
```css
.overlay-header { width:100%; display:flex; justify-content:space-between; align-items:flex-end; padding:2em 0; }
.col:nth-child(1) { flex:3; }   /* title + category */
.col:nth-child(2) { flex:2; }   /* Close */
#close-btn { cursor:pointer; opacity:0.4; }

.item-details { width:100%; display:flex; justify-content:space-between; padding:1em 0 4em 0; }
.item-details p:nth-child(1) { flex:3; }   /* the View-Site link */
.item-details p:nth-child(2) { flex:2; }   /* the copy paragraph */

.img-container { width:100%; padding-bottom:2em; }   /* the detail image fills the panel width */
```

## GSAP effect (be exhaustive)

There is exactly **one paused timeline holding one tween**, plus three click handlers. All positional/rotational start values live in CSS (above); the timeline animates them to the resting/on-screen state.

### The timeline (built once, on load)
```js
const overlay  = document.querySelector(".overlay");
const closeBtn = document.querySelector("#close-btn");

const tl = gsap.timeline({ paused: true, overwrite: "auto" });
tl.to(overlay, {
  duration: 0.5,
  bottom: "0px",                  // slides UP: bottom -1200px → 0px
  rotation: 0,                    // un-tilts: 20deg → 0deg
  transformOrigin: "bottom center",
  ease: "power2.out",
});
```
- **`paused: true`** — nothing plays until a click.
- **`overwrite: "auto"`** — a rapid re-click won't stack conflicting tweens.
- The tween animates the panel **from its CSS start state** (`bottom:-1200px`, `rotate(20deg)`) **to** `bottom:0px`, `rotation:0` in **0.5 s** with **`power2.out`** (fast start, gentle settle). Because it pivots about `bottom center`, the panel appears to swing upright as it rises into the bottom-right corner.
- No stagger, no delay, no labels — a single tween is the whole timeline.

### Open — click a list row → `tl.play()` + swap content
```js
const items = document.querySelectorAll(".item");
items.forEach((item, index) => {
  item.addEventListener("click", () => {
    tl.play();                    // play forward from current position
    updateOverlay(data[index]);   // fill panel with THIS row's data
  });
});

function updateOverlay(dataItem) {
  const itemName     = document.querySelector("#item-category").previousElementSibling; // the <h1>
  const itemCategory = document.querySelector("#item-category");
  const itemLink     = document.querySelector("#item-link");
  const itemCopy     = document.querySelector("#item-copy");
  const itemImg      = document.querySelector("#item-img");

  itemName.textContent     = dataItem.itemName;
  itemCategory.textContent = dataItem.itemCategory;
  itemLink.href            = dataItem.itemLink;
  itemCopy.textContent     = dataItem.itemCopy;
  itemImg.src              = dataItem.itemImg;
}
```
`tl.play()` always drives the same timeline **forward**, so clicking a different row while the panel is already open just re-fills the content in place (the panel is already at `bottom:0`, so the tween has nothing left to travel).

### Close — button, and click-outside → `tl.reverse()`
```js
closeBtn.addEventListener("click", () => { tl.reverse(); });

document.addEventListener("click", (e) => {
  if (!overlay.contains(e.target) && !isItem(e.target)) {
    tl.reverse();               // clicking empty space closes the panel
  }
});
function isItem(target) { return target.closest(".item"); }
```
`tl.reverse()` plays the exact same tween backward: the panel **re-tilts to 20°** and **drops back to `bottom:-1200px`** over 0.5 s (the `power2.out` ease reads as `power2.in` in reverse). The document-level guard reverses on any click that is **neither inside the overlay nor on a list item**, so clicking a row never immediately re-closes it.

### Net choreography
1. Load: panel invisible, parked 1200px below and tilted 20°.
2. Click row *n*: content swaps to `data[n]`; panel **slides up + straightens** into the bottom-right corner in 0.5 s (`power2.out`).
3. Click Close or any empty area: panel **tilts back + slides down** off-screen in 0.5 s (reversed tween).

No SplitText, CustomEase, lerp/rAF loop, ScrollTrigger, Three.js, or canvas — just this one reversible `power2.out` slide-and-rotate.

## Assets / images
**5 editorial fashion photographs**, full-bleed (shown `object-fit:cover`, filling the panel's full width — roughly portrait/3:4). Mood: **futuristic, high-fashion, avant-garde** — models in sculptural techwear / iridescent fabrics / metallic and bio-luminescent styling against clean studio or moody sets. They are the detail image shown inside the white overlay, one per project, cycling `img-1`…`img-5` and repeating for rows 6–10. No brands or logos.

## Behavior notes
- **No scroll on the page** — `html, body { overflow:hidden }`; the only scrollable region is inside the open overlay (its scrollbar hidden). The effect is entirely **click-driven**, not scroll-driven.
- **Fully reversible** off one timeline: the same 0.5 s tween opens and closes; state is preserved so mid-flight clicks just reverse direction (`overwrite:"auto"` guards against tween pile-up).
- **Responsive** (`max-width: 900px`): `.items { width:100%; bottom:8em; }` (list spans full width, lifted off the bottom); `.overlay { width:100%; height:100vh; }` (panel becomes a full-screen sheet). The GSAP tween is unchanged.
- No reduced-motion branch in the original; if adding one, swap the slide for an instant show/hide.
