HALOGEN Where Chrome Meets Chaos AI website prompt
A bold fashion storefront for an independent Rotterdam womenswear label, featuring new arrivals, campaign imagery, collections, brand manifesto, fashion film, and newsletter signup.
Now put it in WordPress
You have a site. Now make it yours.
An AI build hands you a front end and stops there. No CMS, no client login, no real ownership: you cannot hand it over, and you cannot change it without going back and re-prompting. UiChemy turns that build into native, editable WordPress you own, in the builder you already use.
Bolt to WordPressClaude to WordPressCodex to WordPressCursor to WordPressLovable to WordPressv0 to WordPressAI to WordPress
Get Started
More prompts
Showing 6 prompts
Frequently asked questions
What does the HALOGEN Where Chrome Meets Chaos prompt build?
A bold fashion storefront for an independent Rotterdam womenswear label, featuring new arrivals, campaign imagery, collections, brand manifesto, fashion film, and newsletter signup.
Which AI tools does the HALOGEN Where Chrome Meets Chaos prompt work with?
It's written for Claude. Paste the whole prompt into Claude and it builds the complete site for you, then bring the result into WordPress with UiChemy. This prompt also has a WordPress version: with the UiChemy MCP connected, Claude builds the page straight into WordPress.
How do I turn the result into a WordPress site?
After the AI builds your site, UiChemy Composer rebuilds it as native, editable WordPress in Elementor or Gutenberg (Bricks coming soon) — you own it and edit it in your builder. See AI to WordPress.
Is the HALOGEN Where Chrome Meets Chaos prompt free?
Yes — it's free to copy, no account needed.
Can I edit the website after it's built?
Yes. Once it's in WordPress with UiChemy, every section is native and editable — no re-prompting, no lock-in.
Copy prompt
Copy code
Coming soon
Copy SKILL.md
Coming soon
HALOGEN Where Chrome Meets Chaos · Section prompt
# MASTER PROMPT — HALOGEN — Where Chrome Meets Chaos
### Plain HTML, CSS and JavaScript. No WordPress, no page builder, no framework, no build step.
## ▶ HOW TO USE THIS PROMPT
**Paste this whole document into Claude. Nothing to attach.** It carries the complete source of the site plus the address of every image, so Claude can assemble a folder that owns its own assets and hand it back as a zip.
**INSTRUCTION TO CLAUDE — BUILD IT, DO NOT ASK.** Every decision is already made in this document. Do not stop to ask the user questions, do not offer options, and do not ask for confirmation before starting. Write the files, zip them, hand over the archive, then report what you built.
1. Create a folder named `halogen` and write out every file in the appendix below **exactly as written** — do not reformat, rename, shorten, reorder or "improve" anything.
2. **Download the 20 images, 1 video and 4 fonts into `assets/`.** The code already points at `assets/…` — nothing in it references a remote host. Run this from inside the folder; it reads the ASSET MANIFEST at the end of this document:
```bash
mkdir -p assets
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__font-anton-400.woff2' -o 'assets/anton-400.woff2'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__font-ibm-plex-mono-400.woff2' -o 'assets/ibm-plex-mono-400.woff2'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__font-ibm-plex-mono-500.woff2' -o 'assets/ibm-plex-mono-500.woff2'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__font-ibm-plex-mono-600.woff2' -o 'assets/ibm-plex-mono-600.woff2'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-c1.jpg' -o 'assets/c1.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-c2.jpg' -o 'assets/c2.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-c3.jpg' -o 'assets/c3.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-c4.jpg' -o 'assets/c4.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-c5.jpg' -o 'assets/c5.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-c6.jpg' -o 'assets/c6.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-camp.jpg' -o 'assets/camp.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-col1.jpg' -o 'assets/col1.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-col2.jpg' -o 'assets/col2.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-film-poster.jpg' -o 'assets/film-poster.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-film.mp4' -o 'assets/film.mp4'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-foot.jpg' -o 'assets/foot.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-hero.jpg' -o 'assets/hero.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p1.jpg' -o 'assets/p1.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p2.jpg' -o 'assets/p2.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p3.jpg' -o 'assets/p3.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p4.jpg' -o 'assets/p4.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p5.jpg' -o 'assets/p5.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p6.jpg' -o 'assets/p6.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p7.jpg' -o 'assets/p7.jpg'
curl -fsSL 'https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p8.jpg' -o 'assets/p8.jpg'
```
Then check every file arrived and none is a zero-byte or HTML error page:
```bash
ls assets | wc -l # expect 25 files
find assets -size -1k # expect no output
```
If any download fails, retry it — hosts occasionally drop a request. Do **not** fall back to hotlinking the remote URL: the whole point is a folder that works offline.
3. **Zip that folder and give the user the archive.** Its contents:
```
halogen/
index.html
sections/01-hero.html
sections/02-arrivals.html
sections/03-campaign.html
sections/04-categories.html
sections/05-collections.html
sections/06-manifesto.html
sections/07-film.html
sections/08-footer.html
sections/09-motion.html
template.html
assets/
anton-400.woff2
ibm-plex-mono-400.woff2
ibm-plex-mono-500.woff2
ibm-plex-mono-600.woff2
… 21 more
```
```bash
zip -r halogen.zip halogen -x '*.DS_Store'
```
Then hand over `halogen.zip` as a file the user can download, not as text in the chat.
4. Before handing it over, serve the folder and confirm it runs:
```bash
cd halogen && python3 -m http.server 8000 # then open http://localhost:8000
```
Check the page renders, the images load from `assets/` with the network throttled or offline, and scrolling drives the animations.
The finished folder needs no network for its images — they are in `assets/`; only the motion libraries load from a CDN.
Do not rebuild the design from the description below. The description is there so a person can understand and edit the code. The appendix is the deliverable.
---
## ▶ WHAT YOU GET
HALOGEN is an independent womenswear label: sharp tailoring, sheer mesh and loud metallics, cut in small runs.
**11 code files plus an `assets/` folder holding 20 images, 1 video and 4 fonts, delivered as `halogen.zip`.**
| File | What it is |
|---|---|
| `index.html` | a page, 92 KB |
| `sections/01-hero.html` | a page, 11 KB |
| `sections/02-arrivals.html` | a page, 16 KB |
| `sections/03-campaign.html` | a page, 5 KB |
| `sections/04-categories.html` | a page, 6 KB |
| `sections/05-collections.html` | a page, 5 KB |
| `sections/06-manifesto.html` | a page, 8 KB |
| `sections/07-film.html` | a page, 8 KB |
| `sections/08-footer.html` | a page, 10 KB |
| `sections/09-motion.html` | a page, 13 KB |
| `template.html` | a page, 5 KB |
| `assets/` | 20 images, 1 video and 4 fonts, downloaded at build time |
**Libraries, from a CDN, already referenced in the HTML:** `gsap.min.js`, `ScrollTrigger.min.js`, `lenis.min.js`. No npm install, no bundler, no framework.
**Sections (6):** `#arrivals` · `#campaign` · `#categories` · `#collections` · `#about` · `#film`.
---
## ▶ WHERE THE MEDIA COMES FROM
All 25 files (20 images, 1 video and 4 fonts) live in `assets/` inside the folder, and every reference in the markup, the CSS and the script is a relative `assets/…` path. Once the folder is assembled it owns its own media: no CDN, no media library, no external image host. Open it on a laptop with the wifi off and it still renders.
They are fetched **once, at build time**, from the WordPress media library on `demo.uichemy.com` — the same `halogen__` files the WordPress build of this page uses. That is only how they travel into the folder; nothing in the delivered code points back at that host. Replacing an image means dropping a new file into `assets/` under the same name.
The manifest at the end of this document lists every source URL against the filename it is saved as, so the download step is reproducible and checkable.
---
## ▶ WHAT THIS PROMPT DELIBERATELY DOES NOT CONTAIN
- No WordPress, no page builder, and none of the wrapper classes or compatibility CSS a builder needs. The WordPress build is a separate prompt.
- No framework, no TypeScript, no bundler, no `package.json`.
- No backend. Any form here validates and replies in place; it posts nowhere.
- No CMS. The content lives in the files.
- No external image host. The images ship inside the zip; nothing in the code calls out to a media library at runtime.
---
## ASSET MANIFEST — download these into `assets/`
Source is the media library on `demo.uichemy.com`. Save each one under the name in the second column; the code already refers to it by that name.
| Download from | Save as |
|---|---|
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__font-anton-400.woff2` | `assets/anton-400.woff2` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__font-ibm-plex-mono-400.woff2` | `assets/ibm-plex-mono-400.woff2` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__font-ibm-plex-mono-500.woff2` | `assets/ibm-plex-mono-500.woff2` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__font-ibm-plex-mono-600.woff2` | `assets/ibm-plex-mono-600.woff2` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-c1.jpg` | `assets/c1.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-c2.jpg` | `assets/c2.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-c3.jpg` | `assets/c3.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-c4.jpg` | `assets/c4.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-c5.jpg` | `assets/c5.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-c6.jpg` | `assets/c6.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-camp.jpg` | `assets/camp.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-col1.jpg` | `assets/col1.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-col2.jpg` | `assets/col2.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-film-poster.jpg` | `assets/film-poster.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-film.mp4` | `assets/film.mp4` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-foot.jpg` | `assets/foot.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-hero.jpg` | `assets/hero.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p1.jpg` | `assets/p1.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p2.jpg` | `assets/p2.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p3.jpg` | `assets/p3.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p4.jpg` | `assets/p4.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p5.jpg` | `assets/p5.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p6.jpg` | `assets/p6.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p7.jpg` | `assets/p7.jpg` |
| `https://demo.uichemy.com/wp-content/uploads/2026/09/halogen__img-p8.jpg` | `assets/p8.jpg` |
---
# APPENDIX · VERBATIM SOURCE
The complete source of every file. **Write them out exactly** — do not change a value, a comment, a class name, or the order of a declaration. Recreate this file layout:
````
index.html
sections/01-hero.html
sections/02-arrivals.html
sections/03-campaign.html
sections/04-categories.html
sections/05-collections.html
sections/06-manifesto.html
sections/07-film.html
sections/08-footer.html
sections/09-motion.html
template.html
````
## `index.html`
````html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HALOGEN — Where Chrome Meets Chaos</title>
<meta name="description" content="HALOGEN is an independent womenswear label: sharp tailoring, sheer mesh and loud metallics, cut in small runs.">
<style>
@font-face{font-family:'Anton';font-style:normal;font-weight:400;font-display:swap;src:url('assets/anton-400.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url('assets/ibm-plex-mono-400.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url('assets/ibm-plex-mono-500.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:600;font-display:swap;src:url('assets/ibm-plex-mono-600.woff2') format('woff2')}
/* ---------- HALOGEN locked palette ---------- */
:root{
--bg:#F4F2FA; /* lilac-white page */
--paper:#FFFFFF; /* cards */
--ink:#1E1530; /* violet ink: text only, never a background */
--violet:#5B2EFF; /* primary */
--violet-soft:#E6DFFF;/* primary tint */
--lime:#D4FF3A; /* accent */
--mist:#8C86A0; /* secondary text */
--line:#DCD7EA; /* borders */
--shadow:0 18px 40px -22px rgba(91,46,255,.35);
--halogen-display:'Anton', 'Impact', sans-serif;
--halogen-mono:'IBM Plex Mono', ui-monospace, monospace;
--gutter:clamp(16px, 2.4vw, 36px);
--ease:cubic-bezier(.2,.7,.1,1);
}
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--halogen-mono);font-size:12px;line-height:1.5;
text-transform:uppercase;letter-spacing:.02em;overflow-x:hidden;-webkit-font-smoothing:antialiased}
img{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;text-transform:inherit;letter-spacing:inherit}
/* ---------- shared atoms (use these in every section) ---------- */
.wrap{padding-inline:var(--gutter)}
.sec{padding-block:clamp(72px, 10vw, 150px)}
.sec-num{font-family:var(--halogen-mono);font-weight:600;color:var(--violet);font-size:12px} /* "02. NEW ARRIVALS" */
.display{font-family:var(--halogen-display);font-weight:400;line-height:.86;letter-spacing:-.005em;text-transform:uppercase}
.paren{color:var(--mist)} /* "(BERLIN)" style labels */
.hl{color:var(--violet)} /* violet highlight inside running text */
.brk{display:inline-flex;gap:.5em;align-items:center;font-weight:600;white-space:nowrap;transition:color .25s var(--ease)}
.brk::before{content:'['}.brk::after{content:']'}
.brk:hover{color:var(--violet)}
.tape{display:inline-block;background:var(--lime);color:var(--ink);font-weight:600;font-size:10px;padding:3px 10px;transform:rotate(-3deg);
clip-path:polygon(2% 8%,100% 0,97% 50%,100% 100%,0 94%,3% 50%)}
.rail{display:flex;gap:14px;overflow-x:auto;scrollbar-width:none;cursor:grab;scroll-snap-type:x proximity;padding-inline:var(--gutter);overscroll-behavior-x:contain}
.rail::-webkit-scrollbar{display:none}
.rail.dragging{cursor:grabbing;scroll-snap-type:none}
.rail.dragging *{pointer-events:none}
.drag-hint{font-weight:600}.drag-hint b{color:var(--violet);font-weight:600}
/* reveal on scroll */
[data-reveal]{opacity:0;transform:translateY(28px);transition:opacity .9s var(--ease),transform .9s var(--ease)}
[data-reveal].in{opacity:1;transform:none}
@media (prefers-reduced-motion: reduce) and (update: none){
[data-reveal]{opacity:1;transform:none;transition:none}
*{animation-duration:.001ms!important;animation-iteration-count:1!important}
}
</style>
</head>
<body>
<header class="hero" id="top">
<script>document.documentElement.classList.add('js');</script>
<style>
/* ================= 01 HERO ================= */
.hero{position:relative;--hero-fs:calc((100vw - 2 * var(--gutter)) / 3.7)}
.hero :focus-visible{outline:2px solid var(--violet);outline-offset:3px}
/* ---- intro loader (hidden unless html.js) ---- */
.hero-loader{display:none}
html.js .hero-loader{display:flex;position:fixed;inset:0;z-index:200;background:var(--lime);color:var(--ink);
flex-direction:column;justify-content:space-between;padding:clamp(16px,3vw,40px);
transition:transform .85s var(--ease);
/* CSS failsafe: leaves on its own even if the script never runs */
animation:hero-loader-out .85s var(--ease) 2.6s forwards}
.hero-loader.is-done{transform:translateY(-101%);pointer-events:none}
.hero-loader.is-gone{display:none!important}
.hero-loader-top{display:flex;justify-content:space-between;gap:16px;font-weight:600;font-size:11px}
.hero-loader-count{font-family:var(--halogen-mono);font-weight:600;font-size:clamp(56px,14vw,180px);line-height:.9;letter-spacing:-.04em}
.hero-loader-bar{height:2px;background:rgba(30,21,48,.14);margin-top:14px;overflow:hidden}
.hero-loader-bar i{display:block;height:100%;width:100%;background:var(--violet);transform:scaleX(0);transform-origin:left}
@keyframes hero-loader-out{to{transform:translateY(-101%);visibility:hidden;pointer-events:none}}
/* ---- fixed nav ---- */
.hero-nav{position:fixed;top:0;left:0;right:0;z-index:100;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
gap:16px;height:56px;padding-inline:var(--gutter);font-size:11px;font-weight:500;
border-bottom:1px solid transparent;transition:background-color .35s var(--ease),border-color .35s var(--ease)}
.hero-nav.is-scrolled{background:var(--bg);border-bottom-color:var(--line)}
.hero-nav-side{display:flex;gap:clamp(14px,2vw,28px);align-items:center;min-width:0}
.hero-nav-side--r{justify-content:flex-end}
.hero-nav a,.hero-nav button{padding:6px 0;white-space:nowrap;transition:color .25s var(--ease)}
.hero-nav a:hover,.hero-nav button:hover{color:var(--violet)}
.hero-nav-logo{font-family:var(--halogen-display);font-size:24px;line-height:1;letter-spacing:.02em}
.hero-nav-count{color:var(--violet);font-weight:600}
@media (max-width:760px){
.hero-nav-hide{display:none}
}
/* ---- stage ---- */
.hero-stage{min-height:100svh;padding:72px var(--gutter) clamp(28px,4vw,48px);display:flex;flex-direction:column;overflow:hidden}
.hero-meta{display:grid;grid-template-columns:1fr auto 1fr;gap:12px;font-size:11px;font-weight:600;padding-bottom:clamp(8px,1.2vw,16px)}
.hero-meta span:nth-child(2){text-align:center}
.hero-meta span:last-child{text-align:right;white-space:nowrap}
@media (max-width:420px){.hero-meta{font-size:10px;gap:8px}}
.hero-word{position:relative;margin:0;font-size:var(--hero-fs);line-height:.8;letter-spacing:-.01em;white-space:nowrap;
color:var(--violet);z-index:1;padding-top:.04em}
.hero-word-in{display:inline-block}
.hero-word--stroke{position:absolute;left:0;right:0;top:0;z-index:3;color:transparent;pointer-events:none;
-webkit-text-stroke:clamp(1px,.18vw,2.5px) var(--violet)}
.hero-wordbox{position:relative}
.hero-grid{position:relative;display:grid;grid-template-columns:1fr auto 1fr;gap:clamp(16px,3vw,48px);align-items:end;
margin-top:calc(var(--hero-fs) * -.46);flex:1}
.hero-photo{position:relative;z-index:2;width:clamp(210px,27vw,400px);aspect-ratio:867/1300;overflow:hidden;
background:var(--violet-soft);box-shadow:var(--shadow)}
.hero-photo img{width:100%;height:112%;object-fit:cover;object-position:50% 40%;margin-top:-6%;will-change:transform}
.hero-photo .tape{position:absolute;left:-6px;top:18%;z-index:2}
.hero-cap{position:absolute;left:0;right:0;bottom:0;display:flex;justify-content:space-between;gap:8px;padding:10px 12px;
font-size:10px;font-weight:600;background:var(--paper);color:var(--ink)}
.hero-cap b{color:var(--violet);font-weight:600}
.hero-left,.hero-right{padding-bottom:4px;display:flex;flex-direction:column;gap:18px}
.hero-right{align-items:flex-end;text-align:right}
.hero-tag{font-size:clamp(13px,1.2vw,16px);font-weight:600;line-height:1.35;max-width:22ch;margin:0}
.hero-tag em{font-style:normal;background:var(--lime);padding:0 .25em}
.hero-sub{margin:0;color:var(--mist);font-size:11px;max-width:30ch}
.hero-links{display:flex;flex-direction:column;gap:10px;align-items:flex-end;font-size:13px}
.hero-links .brk:first-child{color:var(--violet)}
.hero-links .brk:first-child:hover{color:var(--ink)}
.hero-scroll{font-size:10px;color:var(--mist);display:flex;gap:8px;align-items:center}
.hero-scroll i{display:block;width:1px;height:28px;background:var(--line);position:relative;overflow:hidden}
.hero-scroll i::after{content:'';position:absolute;left:0;top:-40%;width:1px;height:40%;background:var(--violet);animation:hero-drip 1.8s var(--ease) infinite}
@keyframes hero-drip{to{top:100%}}
@media (max-width:760px){
.hero-stage{padding-top:68px}
.hero-grid{grid-template-columns:1fr 1fr;row-gap:22px}
.hero-photo{grid-column:1 / -1;grid-row:1;justify-self:center;width:min(66vw,320px)}
.hero-left{grid-row:2;grid-column:1 / -1}
.hero-right{grid-row:3;grid-column:1 / -1;align-items:flex-start;text-align:left}
.hero-links{flex-direction:row;flex-wrap:wrap;gap:10px 20px;align-items:center}
.hero-scroll{display:none}
.hero-tag{max-width:none}
}
/* ---- marquee bands ---- */
.hero-bands{position:relative;height:clamp(150px,17vw,230px);overflow:hidden;margin-top:clamp(8px,2vw,24px)}
.hero-band{position:absolute;left:-10%;width:120%;top:50%;padding-block:clamp(10px,1.2vw,16px);overflow:hidden;
font-family:var(--halogen-display);font-size:clamp(28px,4.2vw,60px);line-height:1;white-space:nowrap}
.hero-band--v{background:var(--violet);color:var(--paper);transform:translateY(-50%) rotate(-2deg);z-index:2;
box-shadow:var(--shadow)}
.hero-band--l{background:var(--lime);color:var(--ink);transform:translateY(-50%) rotate(2.4deg);z-index:1}
.hero-track{display:flex;width:max-content;animation:hero-marq 34s linear infinite}
.hero-band--l .hero-track{animation-direction:reverse;animation-duration:40s}
.hero-track span{display:inline-flex;align-items:center;padding-right:.4em}
.hero-track b{font-weight:400;padding-inline:.4em;font-size:.7em}
.hero-band--v b{color:var(--lime)}
.hero-band--l b{color:var(--violet)}
@keyframes hero-marq{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media (prefers-reduced-motion: reduce) and (update: none){
html.js .hero-loader{display:none}
.hero-track,.hero-scroll i::after{animation:none}
.hero-photo img{transform:none!important}
}
</style>
<div class="hero-loader" aria-hidden="true">
<div class="hero-loader-top"><span>HALOGEN / ROTTERDAM</span><span>DROP 04 LOADING</span></div>
<div>
<div class="hero-loader-count"><span data-hero-count>00</span>%</div>
<div class="hero-loader-bar"><i data-hero-bar></i></div>
</div>
</div>
<nav class="hero-nav" aria-label="Main">
<div class="hero-nav-side">
<button type="button" aria-label="Open menu">MENU +</button>
<a class="hero-nav-hide" href="#arrivals">SHOP ALL</a>
<a class="hero-nav-hide" href="#categories">CATEGORIES ▾</a>
</div>
<a class="hero-nav-logo" href="#top" aria-label="HALOGEN home">HALOGEN</a>
<div class="hero-nav-side hero-nav-side--r">
<button type="button" class="hero-nav-hide">SEARCH</button>
<a class="hero-nav-hide" href="#arrivals">SAVED <span class="hero-nav-count">(0)</span></a>
<a href="#arrivals">BAG <span class="hero-nav-count">(0)</span></a>
</div>
</nav>
<div class="hero-stage">
<div class="hero-meta">
<span class="paren">(NL)</span>
<span class="paren">(SS_'26)</span>
<span>DROP 04 / <span class="hl">12 PIECES</span></span>
</div>
<div class="hero-wordbox">
<h1 class="hero-word display"><span class="hero-word-in" data-hero-word>HALOGEN</span></h1>
<div class="hero-word hero-word--stroke display" aria-hidden="true"><span class="hero-word-in">HALOGEN</span></div>
</div>
<div class="hero-grid">
<div class="hero-left">
<p class="hero-tag"><em>WHERE CHROME</em><br>MEETS CHAOS</p>
<p class="hero-sub">Rotterdam-made womenswear. Leather, mesh and loud metal, cut in runs of forty. When it's gone, it's gone.</p>
</div>
<figure class="hero-photo" style="margin:0">
<img src="assets/hero.jpg" alt="Model in a black leather bomber and pleated skirt lunging toward the camera on a pale studio floor" width="867" height="1300" fetchpriority="high" data-hero-par>
<span class="tape">CHROME CAPSULE</span>
<figcaption class="hero-cap"><span>LOOK 07 / VINYL BOMBER</span><b>€ 248</b></figcaption>
</figure>
<div class="hero-right">
<div class="hero-links">
<a class="brk" href="#arrivals">BE LOUD</a>
<a class="brk" href="#arrivals">SEE COLLECTION</a>
</div>
<span class="hero-scroll"><i></i>SCROLL</span>
</div>
</div>
</div>
<div class="hero-bands" aria-hidden="true">
<div class="hero-band hero-band--l">
<div class="hero-track">
<span>NEW DROP<b>✦</b>CHROME CAPSULE<b>✦</b>AFTER HOURS<b>✦</b>ROTTERDAM NIGHTS<b>✦</b>LOUD IS A LOVE LANGUAGE<b>✦</b></span>
<span>NEW DROP<b>✦</b>CHROME CAPSULE<b>✦</b>AFTER HOURS<b>✦</b>ROTTERDAM NIGHTS<b>✦</b>LOUD IS A LOVE LANGUAGE<b>✦</b></span>
</div>
</div>
<div class="hero-band hero-band--v">
<div class="hero-track">
<span>NEW DROP<b>✦</b>CHROME CAPSULE<b>✦</b>AFTER HOURS<b>✦</b>ROTTERDAM NIGHTS<b>✦</b>LOUD IS A LOVE LANGUAGE<b>✦</b></span>
<span>NEW DROP<b>✦</b>CHROME CAPSULE<b>✦</b>AFTER HOURS<b>✦</b>ROTTERDAM NIGHTS<b>✦</b>LOUD IS A LOVE LANGUAGE<b>✦</b></span>
</div>
</div>
</div>
<script>
(()=>{
const reduce = matchMedia('(prefers-reduced-motion: reduce) and (update: none)').matches;
const hero = document.querySelector('.hero');
if(!hero) return;
/* loader */
const loader = hero.querySelector('.hero-loader');
if(loader){
const done = ()=>{loader.classList.add('is-done');setTimeout(()=>loader.classList.add('is-gone'),900)};
if(reduce){loader.classList.add('is-gone')}
else{
loader.style.animation='none'; /* JS is alive: take over from the CSS failsafe */
const num = loader.querySelector('[data-hero-count]'), bar = loader.querySelector('[data-hero-bar]');
const D = 1200, t0 = performance.now();
const tick = now=>{
const p = Math.min(1,(now-t0)/D), e = 1-Math.pow(1-p,3), v = Math.round(e*100);
num.textContent = String(v).padStart(2,'0'); bar.style.transform = 'scaleX('+e+')';
if(p<1) requestAnimationFrame(tick); else setTimeout(done,180);
};
requestAnimationFrame(tick);
}
}
/* nav state */
const nav = hero.querySelector('.hero-nav');
const onNav = ()=>nav.classList.toggle('is-scrolled', scrollY > 40);
/* fit wordmark exactly to the content width */
const box = hero.querySelector('.hero-wordbox'), word = hero.querySelector('[data-hero-word]');
const fit = ()=>{
const avail = box.clientWidth; if(!avail) return;
hero.style.setProperty('--hero-fs','100px');
const w = word.getBoundingClientRect().width;
if(w) hero.style.setProperty('--hero-fs', (Math.floor(avail/w*100*10)/10 - .2)+'px');
};
fit();
if(document.fonts && document.fonts.ready) document.fonts.ready.then(fit);
let rz; addEventListener('resize',()=>{cancelAnimationFrame(rz);rz=requestAnimationFrame(fit)});
/* photo parallax */
const img = hero.querySelector('[data-hero-par]');
let ticking = false;
const par = ()=>{
ticking = false; onNav();
if(reduce || !img) return;
const y = Math.min(scrollY, innerHeight*1.2);
img.style.transform = 'translate3d(0,'+(y*-.03).toFixed(1)+'px,0) scale(1.02)';
};
addEventListener('scroll',()=>{if(!ticking){ticking=true;requestAnimationFrame(par)}},{passive:true});
par();
})();
</script>
</header>
<main>
<section id="arrivals" class="sec arr" aria-labelledby="arr-title">
<style>
.arr-grid{display:grid;grid-template-columns:minmax(200px,22%) minmax(0,1fr);gap:clamp(20px,3vw,48px);padding-left:var(--gutter);align-items:start}
.arr-side{position:sticky;top:clamp(24px,6vh,72px);display:flex;flex-direction:column;gap:22px;min-width:0}
.arr-head{display:flex;justify-content:space-between;align-items:baseline;gap:12px}
.arr-title{font-size:clamp(44px,5.4vw,84px);margin:0;color:var(--ink)}
.arr-title .hl{display:block}
.arr-meta{display:flex;justify-content:space-between;gap:10px;border-top:1px solid var(--line);padding-top:12px}
.arr-count{font-weight:600}
.arr-filters{display:flex;flex-direction:column;align-items:flex-start;gap:8px;margin:0;padding:0;list-style:none}
.arr-flabel{color:var(--mist);margin-bottom:2px}
.arr-filter{position:relative;padding:4px 8px;margin-left:-8px;font-weight:500;color:var(--ink);transition:color .25s var(--ease),background-color .25s var(--ease)}
.arr-filter::before{content:'('}.arr-filter::after{content:')'}
.arr-filter:hover{color:var(--violet)}
.arr-filter[aria-pressed="true"]{background:var(--lime);color:var(--ink)}
.arr-filter:focus-visible,.arr-nav button:focus-visible,.arr-save:focus-visible,.arr-media:focus-visible{outline:2px solid var(--violet);outline-offset:2px}
.arr-reset{color:var(--mist);font-weight:500;padding:4px 0;transition:color .25s var(--ease)}
.arr-reset:hover{color:var(--violet)}
.arr-reset[hidden]{display:none}
.arr-main{min-width:0}
.arr-bar{display:flex;justify-content:space-between;align-items:center;gap:12px;padding-right:var(--gutter);margin-bottom:6px}
.arr-nav{display:flex;gap:6px}
.arr-nav button{width:40px;height:40px;display:grid;place-items:center;border:1px solid var(--line);background:var(--paper);color:var(--ink);
transition:background-color .25s var(--ease),color .25s var(--ease),border-color .25s var(--ease),opacity .25s}
.arr-nav button:hover:not(:disabled){background:var(--violet);border-color:var(--violet);color:var(--paper)}
.arr-nav button:disabled{opacity:.35;cursor:default}
.arr-nav svg{width:16px;height:16px}
.arr .rail.arr-rail{padding-left:0;padding-right:var(--gutter);padding-block:16px 10px;gap:clamp(12px,1.4vw,18px)}
.arr-card{flex:0 0 clamp(220px,24vw,340px);position:relative;background:var(--paper);border:1px solid var(--line);scroll-snap-align:start;
transition:opacity .3s var(--ease),transform .3s var(--ease),box-shadow .35s var(--ease),border-color .35s var(--ease)}
.arr-card:hover{box-shadow:var(--shadow);border-color:var(--violet-soft)}
.arr-card.is-out{opacity:0;transform:scale(.94)}
.arr-card.is-in{animation:arr-in .5s var(--ease) both;animation-delay:calc(var(--i,0) * 60ms)}
@keyframes arr-in{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}
.arr-card .tape{position:absolute;top:-9px;left:12px;z-index:3;pointer-events:none}
.arr-media{position:relative;display:block;aspect-ratio:3/4;overflow:hidden;background:var(--violet-soft)}
.arr-media img{width:100%;height:100%;object-fit:cover;transition:transform .8s var(--ease)}
.arr-card:hover .arr-media img{transform:scale(1.05)}
.arr-sizes{position:absolute;left:8px;right:8px;bottom:8px;display:flex;flex-wrap:wrap;align-items:center;gap:6px;padding:8px;
background:rgba(255,255,255,.92);border:1px solid var(--line);transform:translateY(calc(100% + 12px));transition:transform .45s var(--ease)}
.arr-card:hover .arr-sizes,.arr-card:focus-within .arr-sizes{transform:none}
.arr-sizes-l{color:var(--mist);font-size:10px;margin-right:auto}
.arr-chip{font-size:10px;font-weight:600;padding:2px 6px;border:1px solid var(--line);background:var(--paper);white-space:nowrap}
.arr-chip::before{content:'('}.arr-chip::after{content:')'}
.arr-info{display:grid;grid-template-columns:1fr auto;gap:4px 10px;padding:12px 12px 14px;align-items:start}
.arr-name{margin:0;font-size:12px;font-weight:600;line-height:1.35}
.arr-tag{grid-column:1;color:var(--mist);font-size:10px}
.arr-price{font-weight:600;color:var(--violet);white-space:nowrap}
.arr-save{position:absolute;top:10px;right:10px;z-index:2;width:36px;height:36px;display:grid;place-items:center;background:var(--paper);
border:1px solid var(--line);color:var(--ink);transition:color .25s var(--ease),background-color .25s var(--ease),transform .25s var(--ease)}
.arr-save:hover{color:var(--violet)}
.arr-save svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;transition:fill .25s var(--ease)}
.arr-save[aria-pressed="true"]{background:var(--lime);color:var(--violet)}
.arr-save[aria-pressed="true"] svg{fill:currentColor}
.arr-save.pop{animation:arr-pop .35s var(--ease)}
@keyframes arr-pop{50%{transform:scale(1.2)}}
.arr-empty{flex:0 0 auto;align-self:center;color:var(--mist);padding:40px 0}
.arr-progress{position:relative;height:2px;background:var(--line);margin-right:var(--gutter);margin-top:14px;overflow:hidden}
.arr-thumb{position:absolute;inset:0 auto 0 0;width:30%;background:var(--violet);transform-origin:left;transition:width .3s var(--ease)}
@media (hover:none){
.arr-sizes{transform:none}
}
@media (max-width:860px){
.arr-grid{grid-template-columns:minmax(0,1fr);padding-left:0}
.arr-side{position:static;padding-inline:var(--gutter)}
.arr-title br{display:none}
.arr-filters{flex-direction:row;flex-wrap:wrap;gap:6px 10px}
.arr-flabel{flex-basis:100%}
.arr-bar{padding-inline:var(--gutter)}
.arr .rail.arr-rail{padding-inline:var(--gutter)}
.arr-progress{margin-inline:var(--gutter)}
.arr-card{flex-basis:clamp(220px,62vw,300px)}
}
@media (prefers-reduced-motion: reduce) and (update: none){
.arr-card,.arr-media img,.arr-sizes,.arr-thumb,.arr-save{transition:none}
.arr-card.is-in,.arr-save.pop{animation:none}
.arr-card:hover .arr-media img{transform:none}
}
</style>
<div class="arr-grid">
<aside class="arr-side" data-reveal>
<div class="arr-head">
<span class="sec-num">02. NEW ARRIVALS</span>
<span class="paren">(NL)</span>
</div>
<h2 class="display arr-title" id="arr-title">Fresh off<br> <span class="hl">the table</span></h2>
<div class="arr-meta">
<span class="arr-count" aria-live="polite"><span data-arr-count>08</span> PIECES</span>
<a href="#" class="brk">SEE ALL</a>
</div>
<ul class="arr-filters" role="group" aria-label="Filter by drop">
<li class="arr-flabel">FILTER BY DROP</li>
<li><button type="button" class="arr-filter" data-tag="chrome" aria-pressed="false">CHROME CAPSULE</button></li>
<li><button type="button" class="arr-filter" data-tag="after" aria-pressed="false">AFTER HOURS</button></li>
<li><button type="button" class="arr-filter" data-tag="new" aria-pressed="false">NEW DROP</button></li>
<li><button type="button" class="arr-reset" hidden>× CLEAR</button></li>
</ul>
</aside>
<div class="arr-main" data-reveal>
<div class="arr-bar">
<span class="drag-hint">[ <b>DRAG</b> · CLICK ]</span>
<div class="arr-nav">
<button type="button" data-arr-prev aria-label="Previous piece"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="M10 3 5 8l5 5"/></svg></button>
<button type="button" data-arr-next aria-label="Next piece"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="m6 3 5 5-5 5"/></svg></button>
</div>
</div>
<div class="rail arr-rail" data-arr-rail>
<article class="arr-card" data-tag="after">
<span class="tape">BEST SELLER</span>
<a href="#" class="arr-media" draggable="false"><img src="assets/p1.jpg" alt="Nocturne slip dress" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">S</span><span class="arr-chip">M</span><span class="arr-chip">L</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Nocturne Slip Dress"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">NOCTURNE SLIP DRESS</h3><span class="arr-price">€ 248</span><span class="arr-tag">(AFTER HOURS)</span></div>
</article>
<article class="arr-card" data-tag="chrome">
<span class="tape">BEST SELLER</span>
<a href="#" class="arr-media" draggable="false"><img src="assets/p2.jpg" alt="Rivet corset set" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">ONE SIZE</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Rivet Corset Set"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">RIVET CORSET SET</h3><span class="arr-price">€ 312</span><span class="arr-tag">(CHROME CAPSULE)</span></div>
</article>
<article class="arr-card" data-tag="new">
<a href="#" class="arr-media" draggable="false"><img src="assets/p3.jpg" alt="Haze mesh body" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">S</span><span class="arr-chip">M</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Haze Mesh Body"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">HAZE MESH BODY</h3><span class="arr-price">€ 96</span><span class="arr-tag">(NEW DROP)</span></div>
</article>
<article class="arr-card" data-tag="chrome">
<span class="tape">BEST SELLER</span>
<a href="#" class="arr-media" draggable="false"><img src="assets/p4.jpg" alt="Static sequin mini dress" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">S</span><span class="arr-chip">M</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Static Sequin Mini"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">STATIC SEQUIN MINI</h3><span class="arr-price">€ 184</span><span class="arr-tag">(CHROME CAPSULE)</span></div>
</article>
<article class="arr-card" data-tag="after">
<a href="#" class="arr-media" draggable="false"><img src="assets/p5.jpg" alt="Polka faux fur coat" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">ONE SIZE</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Polka Faux Fur Coat"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">POLKA FAUX FUR COAT</h3><span class="arr-price">€ 540</span><span class="arr-tag">(AFTER HOURS)</span></div>
</article>
<article class="arr-card" data-tag="new">
<span class="tape">BEST SELLER</span>
<a href="#" class="arr-media" draggable="false"><img src="assets/p6.jpg" alt="White noise suit" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">XS</span><span class="arr-chip">S</span><span class="arr-chip">M</span><span class="arr-chip">L</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save White Noise Suit"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">WHITE NOISE SUIT</h3><span class="arr-price">€ 465</span><span class="arr-tag">(NEW DROP)</span></div>
</article>
<article class="arr-card" data-tag="new">
<a href="#" class="arr-media" draggable="false"><img src="assets/p7.jpg" alt="Lilac rib knit dress" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">S</span><span class="arr-chip">M</span><span class="arr-chip">L</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Lilac Rib Knit Dress"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">LILAC RIB KNIT DRESS</h3><span class="arr-price">€ 158</span><span class="arr-tag">(NEW DROP)</span></div>
</article>
<article class="arr-card" data-tag="after">
<a href="#" class="arr-media" draggable="false"><img src="assets/p8.jpg" alt="Seam line trousers" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">XS</span><span class="arr-chip">S</span><span class="arr-chip">M</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Seam Line Trousers"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">SEAM LINE TROUSERS</h3><span class="arr-price">€ 210</span><span class="arr-tag">(AFTER HOURS)</span></div>
</article>
</div>
<div class="arr-progress" aria-hidden="true"><span class="arr-thumb" data-arr-thumb></span></div>
</div>
</div>
<script>
(()=>{
const sec=document.getElementById('arrivals');if(!sec)return;
const rail=sec.querySelector('[data-arr-rail]'),thumb=sec.querySelector('[data-arr-thumb]');
const prev=sec.querySelector('[data-arr-prev]'),next=sec.querySelector('[data-arr-next]');
const cards=[...sec.querySelectorAll('.arr-card')],btns=[...sec.querySelectorAll('.arr-filter')];
const reset=sec.querySelector('.arr-reset'),count=sec.querySelector('[data-arr-count]');
const rm=matchMedia('(prefers-reduced-motion: reduce) and (update: none)');
const beh=()=>rm.matches?'auto':'smooth';
function progress(){
const max=rail.scrollWidth-rail.clientWidth;
const vis=rail.scrollWidth?rail.clientWidth/rail.scrollWidth:1;
const w=Math.max(vis,.08)*100,p=max>0?rail.scrollLeft/max:0;
thumb.style.width=w+'%';
thumb.style.transform='translateX('+(p*(100-w)/w*100)+'%)';
prev.disabled=rail.scrollLeft<=2;
next.disabled=max<=0||rail.scrollLeft>=max-2;
}
function step(dir){
const c=cards.find(c=>!c.hidden);if(!c)return;
const gap=parseFloat(getComputedStyle(rail).columnGap)||14;
rail.scrollBy({left:dir*(c.getBoundingClientRect().width+gap),behavior:beh()});
}
prev.addEventListener('click',()=>step(-1));
next.addEventListener('click',()=>step(1));
rail.addEventListener('scroll',progress,{passive:true});
addEventListener('resize',progress);
/* filters */
let timer;
function apply(){
const on=btns.filter(b=>b.getAttribute('aria-pressed')==='true').map(b=>b.dataset.tag);
reset.hidden=!on.length;
const show=c=>!on.length||on.includes(c.dataset.tag);
const n=cards.filter(show).length;count.textContent=String(n).padStart(2,'0');
clearTimeout(timer);
cards.forEach(c=>{c.classList.remove('is-in');if(!c.hidden)c.classList.add('is-out')});
timer=setTimeout(()=>{
let i=0;
cards.forEach(c=>{
c.classList.remove('is-out');
const s=show(c);c.hidden=!s;
if(s){c.style.setProperty('--i',i++);void c.offsetWidth;c.classList.add('is-in')}
});
rail.scrollTo({left:0,behavior:'auto'});progress();
},rm.matches?0:260);
}
btns.forEach(b=>b.addEventListener('click',()=>{
b.setAttribute('aria-pressed',b.getAttribute('aria-pressed')==='true'?'false':'true');apply();
}));
reset.addEventListener('click',()=>{btns.forEach(b=>b.setAttribute('aria-pressed','false'));apply();btns[0].focus()});
cards.forEach(c=>c.addEventListener('animationend',e=>{if(e.animationName==='arr-in')c.classList.remove('is-in')}));
/* save toggles */
sec.querySelectorAll('.arr-save').forEach(b=>b.addEventListener('click',()=>{
const on=b.getAttribute('aria-pressed')!=='true';
b.setAttribute('aria-pressed',on);
b.classList.remove('pop');void b.offsetWidth;if(on)b.classList.add('pop');
}));
sec.querySelectorAll('.arr-media').forEach(a=>a.addEventListener('click',e=>e.preventDefault()));
progress();
addEventListener('load',progress);
})();
</script>
</section>
<section class="camp" id="campaign" aria-label="SS '26 campaign">
<style>
.camp{--p:1;position:relative;height:90vh;height:90svh;min-height:560px;background:var(--bg);overflow:hidden}
.camp-frame{position:absolute;inset:0;overflow:hidden;
--ix:calc((1 - var(--p)) * 12%);--iy:calc((1 - var(--p)) * 9%);
clip-path:inset(var(--iy) var(--ix) round calc((1 - var(--p)) * 28px + 0px));
-webkit-clip-path:inset(var(--iy) var(--ix) round calc((1 - var(--p)) * 28px + 0px));
will-change:clip-path}
.camp-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 45%;
transform:scale(calc(1 + (1 - var(--p)) * .12));transform-origin:50% 50%;will-change:transform}
.camp-shade{position:absolute;inset:0;pointer-events:none;
background:linear-gradient(180deg,rgba(91,46,255,0) 38%,rgba(91,46,255,.22) 62%,rgba(91,46,255,.58) 100%)}
.camp-grain{position:absolute;inset:-50%;pointer-events:none;opacity:.14;mix-blend-mode:overlay;
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .9 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
background-size:180px 180px;animation:camp-grain 1.2s steps(4) infinite}
@keyframes camp-grain{0%{transform:translate(0,0)}25%{transform:translate(-4%,3%)}50%{transform:translate(3%,-2%)}75%{transform:translate(-2%,-4%)}100%{transform:translate(0,0)}}
.camp-ui{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:space-between;
padding:calc(var(--iy) + clamp(18px,2.6vw,36px)) calc(var(--ix) + var(--gutter))}
.camp-top{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
.camp-chip{display:inline-block;background:var(--paper);padding:6px 10px;box-shadow:var(--shadow)}
.camp-chip .sec-num{display:block;line-height:1.2}
.camp-meta{color:var(--paper);font-weight:500;font-size:11px;text-align:right;text-shadow:0 1px 10px rgba(91,46,255,.35)}
.camp-bottom{display:flex;justify-content:space-between;align-items:flex-end;gap:clamp(20px,4vw,64px);color:var(--paper)}
.camp-title{display:flex;align-items:flex-end;gap:clamp(10px,1.4vw,22px);min-width:0}
.camp-new{color:var(--lime);font-size:clamp(96px,17vw,300px);margin:0;line-height:.8;
text-shadow:0 10px 40px rgba(91,46,255,.35)}
.camp-tag{font-weight:600;font-size:clamp(11px,1vw,14px);padding-bottom:.6em;white-space:nowrap}
.camp-copy{max-width:36ch;display:flex;flex-direction:column;gap:14px;padding-bottom:.4em}
.camp-state{margin:0;font-weight:500;font-size:clamp(12px,1.05vw,15px);line-height:1.55}
.camp-state .camp-hl{color:var(--lime);font-weight:600}
.camp-foot{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px 18px;
border-top:1px solid rgba(255,255,255,.4);padding-top:12px}
.camp-foot .camp-pr{color:var(--violet-soft);font-weight:500}
.camp .brk{color:var(--paper)}
.camp .brk:hover,.camp .brk:focus-visible{color:var(--lime)}
.camp .brk:focus-visible{outline:2px solid var(--lime);outline-offset:4px}
@media (max-width:760px){
.camp{min-height:600px}
.camp-frame{--ix:calc((1 - var(--p)) * 6%);--iy:calc((1 - var(--p)) * 7%)}
.camp-img{object-position:42% 50%}
.camp-shade{background:linear-gradient(180deg,rgba(91,46,255,0) 26%,rgba(91,46,255,.3) 52%,rgba(91,46,255,.66) 100%)}
.camp-bottom{flex-direction:column;align-items:stretch;gap:18px}
.camp-new{font-size:clamp(84px,27vw,150px)}
.camp-copy{max-width:none}
}
@media (max-width:420px){
.camp-meta{display:none}
.camp-title{flex-wrap:wrap;gap:6px 10px}
}
@media (prefers-reduced-motion: reduce) and (update: none){
.camp{--p:1!important}
.camp-grain{animation:none}
}
</style>
<div class="camp-frame">
<img class="camp-img" src="assets/camp.jpg" alt="Model in oversized black tailoring and red heels seated on a pale grey studio floor" width="1880" height="1253" loading="lazy" decoding="async">
<div class="camp-shade" aria-hidden="true"></div>
<div class="camp-grain" aria-hidden="true"></div>
<div class="camp-ui">
<div class="camp-top">
<span class="camp-chip"><span class="sec-num">03. CAMPAIGN</span></span>
<span class="camp-meta">SHOT IN ROTTERDAM<br>RUNS OF 40, NEVER MORE</span>
</div>
<div class="camp-bottom">
<div class="camp-title">
<h2 class="display camp-new">NEW</h2>
<span class="camp-tag">( TAILORING )</span>
</div>
<div class="camp-copy">
<p class="camp-state">A HARD SHOULDER IS THE <span class="camp-hl">SOFTEST</span> WAY TO SAY <span class="camp-hl">NO</span>.</p>
<div class="camp-foot">
<span class="camp-pr">(NL) (SS_'26)</span>
<a class="brk" href="#collections">SEE CAMPAIGN</a>
</div>
</div>
</div>
</div>
</div>
<script>
(()=>{
const sec=document.currentScript&&document.currentScript.closest('.camp')||document.querySelector('.camp');
if(!sec)return;
const mq=matchMedia('(prefers-reduced-motion: reduce) and (update: none)');
let ticking=false,active=false;
const clamp=v=>v<0?0:v>1?1:v;
const ease=t=>1-Math.pow(1-t,3);
function update(){
ticking=false;
if(mq.matches){sec.style.setProperty('--p','1');return}
const r=sec.getBoundingClientRect(),vh=innerHeight||1;
// 0 when section top enters the viewport bottom, 1 once its top reaches ~10% from the top
const p=clamp((vh-r.top)/(vh*.9));
sec.style.setProperty('--p',ease(p).toFixed(4));
}
function onScroll(){if(!active||ticking)return;ticking=true;requestAnimationFrame(update)}
if('IntersectionObserver' in window){
new IntersectionObserver(es=>{active=es[0].isIntersecting;if(active)onScroll()},{rootMargin:'10% 0px'}).observe(sec);
}else active=true;
addEventListener('scroll',onScroll,{passive:true});
addEventListener('resize',onScroll);
(mq.addEventListener?mq.addEventListener('change',update):mq.addListener(update));
update();
})();
</script>
</section>
<section class="sec cat" id="categories" aria-labelledby="cat-title">
<style>
.cat-head{display:flex;align-items:center;justify-content:space-between;gap:12px 20px;flex-wrap:wrap;margin-bottom:clamp(22px,3vw,40px)}
.cat-head .drag-hint{color:var(--mist)}
.cat-rail{padding-bottom:8px}
.cat-card{--w:clamp(200px,20vw,300px);--pad:clamp(12px,1.1vw,16px);position:relative;flex:0 0 var(--w);width:var(--w);aspect-ratio:3/4.4;
background:var(--paper);border:1px solid var(--line);overflow:hidden;scroll-snap-align:start;container-type:inline-size;
transition:opacity .9s var(--ease),transform .9s var(--ease),background-color .35s var(--ease),border-color .35s var(--ease),box-shadow .35s var(--ease);
transition-delay:calc(var(--i,0) * 80ms),calc(var(--i,0) * 80ms),0s,0s,0s}
.cat-card.in{transition-delay:0s}
.cat-card:focus-visible{outline:2px solid var(--violet);outline-offset:3px}
.cat-photo{position:absolute;top:var(--pad);right:var(--pad);width:62%;height:70%;overflow:hidden;background:var(--violet-soft);
transition:transform .5s var(--ease)}
.cat-photo img{width:100%;height:100%;object-fit:cover;transition:transform .7s var(--ease)}
.cat-name{position:absolute;left:calc(var(--pad) - .06em);bottom:var(--pad);z-index:2;margin:0;color:var(--ink);
writing-mode:vertical-rl;transform:rotate(180deg);font-size:26cqi;line-height:.84;white-space:nowrap;
transition:color .3s var(--ease)}
.cat-count{position:absolute;right:var(--pad);bottom:var(--pad);font-weight:600;font-size:11px;color:var(--mist);transition:color .3s var(--ease)}
.cat-tape{position:absolute;top:calc(var(--pad) + 10px);left:var(--pad);z-index:3;opacity:0;transform:rotate(-6deg) translateY(6px) scale(.9);
transition:opacity .3s var(--ease),transform .35s var(--ease);pointer-events:none}
.cat-card:hover,.cat-card:focus-visible{background:var(--violet-soft);border-color:color-mix(in srgb,var(--violet) 30%,var(--line));box-shadow:var(--shadow)}
.cat-card:hover .cat-name,.cat-card:focus-visible .cat-name{color:var(--violet)}
.cat-card:hover .cat-count,.cat-card:focus-visible .cat-count{color:var(--violet)}
.cat-card:hover .cat-photo,.cat-card:focus-visible .cat-photo{transform:translateY(-8px)}
.cat-card:hover .cat-photo img,.cat-card:focus-visible .cat-photo img{transform:scale(1.04)}
.cat-card:hover .cat-tape,.cat-card:focus-visible .cat-tape{opacity:1;transform:rotate(-6deg)}
@media (max-width:640px){
.cat-head .drag-hint{order:3;flex-basis:100%}
}
@media (prefers-reduced-motion: reduce) and (update: none){
.cat-card,.cat-photo,.cat-photo img,.cat-name,.cat-tape{transition:none!important}
.cat-card:hover .cat-photo,.cat-card:focus-visible .cat-photo,
.cat-card:hover .cat-photo img,.cat-card:focus-visible .cat-photo img{transform:none}
.cat-card:hover .cat-tape,.cat-card:focus-visible .cat-tape{transform:rotate(-6deg)}
}
</style>
<div class="wrap cat-head">
<h2 class="sec-num" id="cat-title" style="margin:0">04. CATEGORIES</h2>
<span class="drag-hint" aria-hidden="true">[ <b>DRAG</b> · CLICK ]</span>
<a class="brk" href="#categories">SEE ALL</a>
</div>
<div class="rail cat-rail" role="list" aria-label="Shop by category">
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:0" aria-label="Slip dresses, 14 pieces">
<span class="cat-photo"><img src="assets/c1.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">SLIP DRESSES</h3>
<span class="cat-count">[ 14 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:1" aria-label="Leather, 9 pieces">
<span class="cat-photo"><img src="assets/c2.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">LEATHER</h3>
<span class="cat-count">[ 9 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:2" aria-label="Jewellery, 22 pieces">
<span class="cat-photo"><img src="assets/c3.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">JEWELLERY</h3>
<span class="cat-count">[ 22 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:3" aria-label="Boots and heels, 11 pieces">
<span class="cat-photo"><img src="assets/c4.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">BOOTS & HEELS</h3>
<span class="cat-count">[ 11 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:4" aria-label="Sequin and metal, 7 pieces">
<span class="cat-photo"><img src="assets/c5.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">SEQUIN & METAL</h3>
<span class="cat-count">[ 7 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:5" aria-label="Bodysuits, 6 pieces">
<span class="cat-photo"><img src="assets/c6.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">BODYSUITS</h3>
<span class="cat-count">[ 6 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:6" aria-label="Knitwear, 8 pieces">
<span class="cat-photo"><img src="assets/p7.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">KNITWEAR</h3>
<span class="cat-count">[ 8 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:7" aria-label="Outerwear, 5 pieces">
<span class="cat-photo"><img src="assets/p5.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">OUTERWEAR</h3>
<span class="cat-count">[ 5 ]</span>
</a>
</div>
<script>
(()=>{
/* fit each vertical name to its card height: start from the CSS size (26cqi) and shrink only if it would overflow */
const names=[...document.querySelectorAll('#categories .cat-name')];
if(!names.length)return;
const fit=()=>names.forEach(n=>{
const card=n.parentElement,pad=parseFloat(getComputedStyle(card).getPropertyValue('--pad'))||14;
n.style.fontSize='';
const max=card.clientHeight-pad*2;
let fs=parseFloat(getComputedStyle(n).fontSize),guard=0;
while(n.offsetHeight>max&&fs>12&&guard++<60){fs*=.95;n.style.fontSize=fs+'px';}
});
let t;const run=()=>{cancelAnimationFrame(t);t=requestAnimationFrame(fit)};
run();
if(document.fonts&&document.fonts.ready)document.fonts.ready.then(run);
if('ResizeObserver' in window){const ro=new ResizeObserver(run);ro.observe(names[0].parentElement);}
else addEventListener('resize',run);
})();
</script>
</section>
<section class="sec col" id="collections" aria-labelledby="col-title">
<style>
.col-head{display:flex;justify-content:space-between;align-items:baseline;gap:16px;flex-wrap:wrap;margin-bottom:clamp(28px,4vw,56px)}
.col-head h2{margin:0;font:inherit}
.col-grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(12px,1.6vw,24px);align-items:start}
.col-panel{position:relative;display:block;height:88svh;min-height:560px;max-height:980px;overflow:hidden;background:var(--violet-soft);
/* torn-paper top edge */
clip-path:polygon(0 3.2%,3% 1.6%,6% 2.9%,9.5% .8%,13% 2.4%,16% 1.1%,19.5% 3%,23% 1.4%,26% 2.6%,30% .6%,33% 2.2%,36.5% 1.2%,40% 3.1%,43% 1.5%,46.5% 2.7%,50% .9%,53% 2.5%,57% 1.3%,60% 3.2%,63.5% 1.7%,67% 2.8%,70% .7%,73.5% 2.3%,77% 1.2%,80% 3%,83.5% 1.4%,87% 2.6%,90% .8%,93.5% 2.4%,97% 1.3%,100% 2.8%,100% 100%,0 100%);
will-change:transform}
.col-panel:focus-visible{outline:none}
.col-panel:focus-visible .col-frame{box-shadow:inset 0 0 0 3px var(--lime)}
.col-frame{position:absolute;inset:0}
.col-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 30%;transform:scale(1);transition:transform 1.1s var(--ease)}
.col-panel::after{content:'';position:absolute;inset:0;pointer-events:none;
background:linear-gradient(to top,rgba(91,46,255,.82) 0%,rgba(91,46,255,.5) 22%,rgba(91,46,255,0) 50%)}
.col-intro{position:absolute;z-index:2;top:clamp(40px,5vw,64px);max-width:min(300px,calc(100% - 32px));margin:0;
background:rgba(255,255,255,.86);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
color:var(--ink);font-size:11px;line-height:1.55;padding:10px 12px;border:1px solid rgba(220,215,234,.9)}
.col-intro--l{left:clamp(16px,2vw,28px)}
.col-intro--r{right:clamp(16px,2vw,28px)}
.col-foot{position:absolute;z-index:2;left:clamp(16px,2vw,28px);right:clamp(16px,2vw,28px);bottom:clamp(16px,2vw,28px);
display:flex;justify-content:space-between;align-items:flex-end;gap:16px}
.col-title{margin:0;color:var(--lime);font-size:clamp(64px,9.4vw,168px);transition:letter-spacing .6s var(--ease);overflow-wrap:anywhere}
.col-panel--b .col-title{color:var(--paper)}
.col-tag{display:block;margin-top:8px;color:var(--paper);font-weight:600;font-size:12px}
.col-count{color:var(--paper);font-weight:600;font-size:13px;flex:none}
.col-panel--b .col-count{color:var(--lime)}
@media (hover:hover){
.col-panel:hover .col-img{transform:scale(1.05)}
.col-panel:hover .col-title{letter-spacing:.03em}
}
.col-panel:focus-visible .col-img{transform:scale(1.05)}
@media (max-width:860px){
.col-grid{grid-template-columns:1fr}
.col-panel{height:78svh;min-height:480px;max-height:720px}
.col-title{font-size:clamp(56px,17vw,120px)}
}
@media (max-width:420px){
.col-intro{font-size:10.5px;padding:9px 10px}
}
@media (prefers-reduced-motion: reduce) and (update: none){
.col-img,.col-title{transition:none}
.col-panel:hover .col-img,.col-panel:focus-visible .col-img{transform:none}
.col-panel{transform:none!important}
}
</style>
<div class="wrap">
<div class="col-head" data-reveal>
<h2 class="sec-num" id="col-title">05. COLLECTIONS</h2>
<a class="brk" href="#collections">SEE COLLECTION</a>
</div>
<div class="col-grid">
<a class="col-panel col-panel--a" href="#collections" aria-label="Chrome capsule SS26, 18 pieces" data-reveal>
<div class="col-frame">
<img class="col-img" src="assets/col1.jpg" alt="Model in a metallic silver strapless dress against a pale backdrop" loading="lazy" decoding="async">
</div>
<p class="col-intro col-intro--l">BORN IN THE LAST TRAM HOME, <span class="hl">CHROME CAPSULE</span> SS26 IS FOIL, LAMÉ AND NERVE.</p>
<div class="col-foot">
<div>
<h3 class="col-title display">CHROME</h3>
<span class="col-tag">(GLAM)</span>
</div>
<span class="col-count">[ 18 ]</span>
</div>
</a>
<a class="col-panel col-panel--b" href="#collections" aria-label="After Hours collection, 11 pieces" data-reveal data-col-par>
<div class="col-frame">
<img class="col-img" src="assets/col2.jpg" alt="Model in a black latex outfit and thigh-high boots lounging on a hot-pink inflatable" loading="lazy" decoding="async">
</div>
<p class="col-intro col-intro--r">PATENT, FISHNET AND FAUX FUR — <span class="hl">AFTER HOURS</span> DRESSES FOR THE ROOM YOU HAVEN'T WALKED INTO YET.</p>
<div class="col-foot">
<div>
<h3 class="col-title display">AFTER<br>HOURS</h3>
<span class="col-tag">(GRUNGE)</span>
</div>
<span class="col-count">[ 11 ]</span>
</div>
</a>
</div>
</div>
<script>
(()=>{
const sec=document.currentScript&&document.currentScript.closest('.col')||document.querySelector('.col');
if(!sec)return;
const panel=sec.querySelector('[data-col-par]');
const mqWide=matchMedia('(min-width:861px)'),mqRM=matchMedia('(prefers-reduced-motion: reduce) and (update: none)');
let ticking=false,active=false;
const apply=()=>{
ticking=false;
if(!mqWide.matches||mqRM.matches){panel.style.translate='';return}
const r=sec.getBoundingClientRect(),vh=innerHeight;
// progress -1..1 as the section travels through the viewport
const p=Math.max(-1,Math.min(1,((r.top+r.height/2)-vh/2)/(vh/2+r.height/2)));
panel.style.translate='0 '+(p*60).toFixed(1)+'px';
};
const onScroll=()=>{if(active&&!ticking){ticking=true;requestAnimationFrame(apply)}};
new IntersectionObserver(es=>{active=es[0].isIntersecting;if(active)onScroll()},{rootMargin:'120px 0px'}).observe(sec);
addEventListener('scroll',onScroll,{passive:true});
addEventListener('resize',()=>{ticking=false;apply()});
mqRM.addEventListener&&mqRM.addEventListener('change',apply);
})();
</script>
</section>
<style>
/* ---------- 06. manifesto (slug: man) ---------- */
.man{position:relative;overflow:hidden}
.man-head{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:clamp(28px,4vw,56px)}
.man-head .paren{font-weight:500}
.man-statement{font-size:clamp(40px,8.4vw,120px);line-height:.92;margin:0;max-width:15.5ch;overflow-wrap:break-word}
.man-w{color:var(--line);transition:color .45s var(--ease)}
.man-w.on{color:var(--ink)}
.man-w.man-key.on{color:var(--violet)}
.man-body{display:grid;grid-template-columns:1fr;gap:clamp(28px,4vw,48px);margin-top:clamp(48px,7vw,104px);align-items:start}
@media (min-width:1100px){.man-body{grid-template-columns:minmax(220px,280px) 1fr}}
/* portrait */
.man-aside{display:flex;flex-direction:column;align-items:flex-start;gap:22px}
.man-frame{position:relative;width:min(260px,72vw);background:var(--paper);border:1px solid var(--line);padding:10px 10px 34px;
transform:rotate(-3.5deg);box-shadow:var(--shadow);margin:14px 0 0 8px;transition:transform .6s var(--ease)}
.man-frame:hover{transform:rotate(-1deg)}
.man-frame img{width:100%;aspect-ratio:4/5;object-fit:cover;filter:saturate(.92)}
.man-frame figcaption{position:absolute;left:10px;right:10px;bottom:9px;font-size:10px;color:var(--mist);display:flex;justify-content:space-between}
.man-frame .tape{position:absolute;top:-12px;right:-10px;transform:rotate(5deg)}
.man-note{max-width:32ch;color:var(--mist);margin:0}
/* value cards */
.man-cards{display:grid;grid-template-columns:1fr;gap:14px;list-style:none;margin:0;padding:0}
@media (min-width:760px){.man-cards{grid-template-columns:repeat(3,1fr)}}
.man-card{position:relative;background:var(--paper);border:1px solid var(--line);padding:clamp(20px,2.4vw,30px);display:flex;flex-direction:column;gap:18px;min-height:clamp(240px,26vw,340px);
transition:transform .45s var(--ease),box-shadow .45s var(--ease),border-color .45s var(--ease)}
.man-card:hover{transform:translateY(-8px);box-shadow:var(--shadow);border-color:var(--violet-soft)}
.man-idx{font-family:var(--halogen-mono);font-weight:600;color:var(--violet);font-size:clamp(34px,3.6vw,48px);line-height:1;letter-spacing:-.04em}
.man-label{margin:auto 0 0;font-size:clamp(34px,3.4vw,52px);display:flex;flex-wrap:wrap;align-items:baseline;gap:.35em}
.man-label .display{position:relative;display:inline-block;padding-bottom:6px}
.man-label .display::after{content:'';position:absolute;left:0;right:0;bottom:0;height:6px;background:var(--lime);transform:scaleX(0);transform-origin:left;transition:transform .5s var(--ease)}
.man-card:hover .man-label .display::after,.man-card:focus-within .man-label .display::after{transform:scaleX(1)}
.man-label .paren{font-family:var(--halogen-mono);font-size:12px;font-weight:500}
.man-card p{margin:0;max-width:34ch}
/* counters */
.man-counters{display:grid;grid-template-columns:1fr;border-top:1px solid var(--line);margin-top:clamp(48px,6vw,88px);list-style:none;padding:0;margin-bottom:0}
@media (min-width:760px){.man-counters{grid-template-columns:repeat(3,1fr)}}
.man-counters li{display:flex;align-items:flex-end;gap:14px;padding:22px 0;border-bottom:1px solid var(--line)}
@media (min-width:760px){
.man-counters li{border-bottom:0;padding:28px 20px 0}
.man-counters li:first-child{padding-left:0}
.man-counters li+li{border-left:1px solid var(--line)}
}
.man-num{font-size:clamp(64px,8vw,112px);color:var(--violet);font-variant-numeric:tabular-nums;min-width:1.1ch}
.man-counters span.man-cap{font-weight:600;max-width:14ch;padding-bottom:8px}
@media (prefers-reduced-motion: reduce) and (update: none){
.man-w{transition:none;color:var(--ink)}
.man-w.man-key{color:var(--violet)}
.man-card,.man-frame,.man-label .display::after{transition:none}
.man-card:hover{transform:none}
}
</style>
<section class="sec man" id="about" aria-labelledby="man-title">
<div class="wrap">
<div class="man-head">
<span class="sec-num">06. WHO WE ARE</span>
<span class="paren">(ROTTERDAM, NL — EST. ATTIC)</span>
</div>
<h2 class="display man-statement" id="man-title" data-man-words data-man-keys="SMALL,RUNS,ROTTERDAM,UNIFORM.">WE CUT SMALL RUNS IN A ROTTERDAM ATTIC SO NOTHING WE MAKE EVER FEELS LIKE A UNIFORM.</h2>
<div class="man-body">
<aside class="man-aside" data-reveal>
<figure class="man-frame">
<span class="tape">ATELIER NOTES</span>
<img src="assets/p6.jpg" alt="Model in a sharp white suit photographed in the HALOGEN atelier" width="520" height="650" loading="lazy">
<figcaption><span>FIT TEST #112</span><span>ATTIC, 3RD FLOOR</span></figcaption>
</figure>
<p class="man-note">ONE CUTTING TABLE, TWO SEWING MACHINES, A RADIATOR THAT ONLY WORKS ON TUESDAYS. EVERYTHING STARTS HERE.</p>
<a href="#about" class="brk">OUR STORY</a>
</aside>
<div>
<ul class="man-cards">
<li class="man-card" data-reveal>
<span class="man-idx">01</span>
<h3 class="man-label"><span class="display">RUNS</span><span class="paren">(SMALL)</span></h3>
<p>EVERY PIECE IS MADE IN BATCHES OF 40 OR FEWER. WHEN IT'S GONE, IT'S GONE.</p>
</li>
<li class="man-card" data-reveal style="transition-delay:.08s">
<span class="man-idx">02</span>
<h3 class="man-label"><span class="display">CLOTH</span><span class="paren">(DEADSTOCK)</span></h3>
<p>WE BUY LEFTOVER BOLTS FROM LARGER HOUSES AND TURN THEM INTO SOMETHING LOUDER.</p>
</li>
<li class="man-card" data-reveal style="transition-delay:.16s">
<span class="man-idx">03</span>
<h3 class="man-label"><span class="display">REPAIR</span><span class="paren">(FOR LIFE)</span></h3>
<p>SNAPPED STRAP, LOST RIVET, BLOWN SEAM — SEND IT BACK AND WE FIX IT FREE.</p>
</li>
</ul>
<ul class="man-counters">
<li><span class="display man-num" data-man-count="40" data-man-from="0">40</span><span class="man-cap">PIECES PER RUN MAX</span></li>
<li><span class="display man-num" data-man-count="3" data-man-from="0">3</span><span class="man-cap">YEARS FREE REPAIRS</span></li>
<li><span class="display man-num" data-man-count="0" data-man-from="99">0</span><span class="man-cap">WHOLESALE MARKUP</span></li>
</ul>
</div>
</div>
</div>
</section>
<script>
(()=>{
const root=document.getElementById('about');if(!root)return;
const reduce=matchMedia('(prefers-reduced-motion: reduce) and (update: none)').matches;
/* word-by-word scroll reveal */
const h=root.querySelector('[data-man-words]');
const keys=(h.dataset.manKeys||'').split(',');
const text=h.textContent.trim();
h.setAttribute('aria-label',text);
h.textContent='';
const words=text.split(/\s+/).map((w,i,a)=>{
const s=document.createElement('span');
s.className='man-w'+(keys.includes(w)?' man-key':'');
s.setAttribute('aria-hidden','true');
s.textContent=w;
h.appendChild(s);
if(i<a.length-1)h.appendChild(document.createTextNode(' '));
return s;
});
if(reduce){words.forEach(w=>w.classList.add('on'))}
else{
let ticking=false;
const update=()=>{
ticking=false;
const r=h.getBoundingClientRect(),vh=innerHeight;
const p=Math.min(1,Math.max(0,(vh*.88-r.top)/(r.height+vh*.4)));
const lit=Math.round(p*words.length);
words.forEach((w,i)=>w.classList.toggle('on',i<lit));
};
const req=()=>{if(!ticking){ticking=true;requestAnimationFrame(update)}};
addEventListener('scroll',req,{passive:true});
addEventListener('resize',req);
update();
}
/* counters */
const nums=root.querySelectorAll('[data-man-count]');
const run=el=>{
const to=+el.dataset.manCount,from=+(el.dataset.manFrom||0);
if(reduce){el.textContent=to;return}
const dur=1400,t0=performance.now();
const step=t=>{
const k=Math.min(1,(t-t0)/dur),e=1-Math.pow(1-k,3);
el.textContent=Math.round(from+(to-from)*e);
if(k<1)requestAnimationFrame(step);
};
el.textContent=from;
requestAnimationFrame(step);
};
if(!('IntersectionObserver' in window)){return}
const io=new IntersectionObserver(es=>es.forEach(e=>{if(e.isIntersecting){run(e.target);io.unobserve(e.target)}}),{threshold:.6});
nums.forEach(n=>io.observe(n));
})();
</script>
<section id="film" class="film" aria-labelledby="film-title">
<style>
.film{position:relative;background:var(--bg)}
.film-track{position:relative;height:250svh}
.film-stage{position:sticky;top:0;height:100svh;overflow:hidden;background:var(--bg)}
.film-frame{position:absolute;left:50%;top:50%;width:38vw;height:calc(38vw * .64);transform:translate(-50%,-50%);
border-radius:18px;overflow:hidden;background:var(--violet-soft);box-shadow:var(--shadow);will-change:width,height,border-radius}
.film-frame video{display:block;width:100%;height:100%;object-fit:cover;transform:scale(1.08);will-change:transform;background:var(--violet-soft)}
.film-frame.is-playing .film-play{opacity:0}
.film-frame.is-playing:hover .film-play,.film-play:focus-visible{opacity:1}
.film-frame.is-playing .film-play::before{animation:none;opacity:0}
.film-play .i-pause{display:none}
.film-frame.is-playing .film-play .i-play{display:none}
.film-frame.is-playing .film-play .i-pause{display:block;margin-left:0}
.film-sound{position:absolute;right:14px;bottom:14px;z-index:3;font-size:10px;font-weight:600;padding:7px 11px;
background:rgba(255,255,255,.86);border:1px solid var(--line);color:var(--ink);transition:background-color .25s var(--ease),color .25s var(--ease)}
.film-sound:hover{color:var(--violet)}
.film-sound[aria-pressed="true"]{background:var(--lime)}
.film-sound:focus-visible{outline:2px solid var(--violet);outline-offset:3px}
.film-words{position:absolute;inset:0;pointer-events:none;z-index:2}
.film-word{position:absolute;top:50%;margin:0;color:var(--violet);font-size:clamp(64px,9vw,170px);white-space:nowrap;
transform:translateY(-50%);will-change:transform}
.film-word--l{right:50%}
.film-word--r{left:50%}
.film-play{position:absolute;left:50%;top:50%;z-index:3;width:72px;height:72px;margin:-36px 0 0 -36px;border-radius:50%;
background:var(--lime);display:grid;place-items:center;box-shadow:0 12px 30px -12px rgba(91,46,255,.55);
transition:transform .35s var(--ease),opacity .35s var(--ease)}
.film-play::before{content:'';position:absolute;inset:0;border-radius:50%;border:2px solid var(--lime);animation:film-pulse 2.2s var(--ease) infinite}
.film-play svg{width:22px;height:22px;margin-left:4px;fill:var(--ink)}
.film-play:hover{transform:scale(1.08)}
.film-play:focus-visible{outline:2px solid var(--violet);outline-offset:6px}
@keyframes film-pulse{0%{transform:scale(1);opacity:.9}100%{transform:scale(1.7);opacity:0}}
.film-label{position:absolute;top:clamp(20px,3vw,36px);left:var(--gutter);z-index:4;margin:0;display:flex;gap:12px;align-items:center}
.film-foot{position:absolute;left:var(--gutter);right:var(--gutter);bottom:clamp(20px,3vw,36px);z-index:4;display:flex;
justify-content:space-between;align-items:flex-end;gap:24px;flex-wrap:wrap}
.film-copy{max-width:46ch;margin:0;padding:14px 16px;background:rgba(255,255,255,.78);border:1px solid var(--line);
-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);font-size:12px}
.film-copy .brk{margin-top:10px;display:inline-flex;color:var(--violet)}
.film-copy .brk:hover{color:var(--ink)}
.film-hint{font-weight:600;color:var(--ink);background:rgba(255,255,255,.78);padding:6px 10px;border:1px solid var(--line);
transition:opacity .4s var(--ease)}
.film-hint.is-gone{opacity:0}
.film-hint b{color:var(--violet);font-weight:600}
/* static layout: mobile + reduced motion */
@media (max-width:759px),(prefers-reduced-motion: reduce) and (update: none){
.film{padding-block:clamp(56px,10vw,100px)}
.film-track{height:auto}
.film-stage{position:relative;height:auto;overflow:visible;display:flex;flex-direction:column;gap:18px;padding-inline:var(--gutter)}
.film-label{position:static}
.film-words{position:static;order:1;display:flex;flex-direction:column}
.film-word{position:static;transform:none!important;font-size:clamp(56px,17vw,120px)}
.film-word--r{align-self:flex-end}
.film-frame{position:relative;left:auto;top:auto;order:2;width:100%!important;height:auto!important;aspect-ratio:4/3;
transform:none;border-radius:14px!important}
.film-frame video{transform:none!important}
.film-play{order:2}
.film-foot{position:static;order:3;flex-direction:column;align-items:flex-start}
.film-hint{display:none}
}
@media (prefers-reduced-motion: reduce) and (update: none){
.film-play::before{animation:none;opacity:0}
}
</style>
<div class="film-track">
<div class="film-stage">
<p class="film-label"><span class="sec-num">07. HALOGEN FILM</span><span class="tape">NEW</span></p>
<div class="film-words" aria-hidden="true">
<span class="film-word film-word--l display">A VISION</span>
<span class="film-word film-word--r display">IN MOTION</span>
</div>
<h2 id="film-title" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)">A vision in motion — the HALOGEN film</h2>
<div class="film-frame">
<video class="film-video" muted loop playsinline preload="metadata" poster="assets/film-poster.jpg"
aria-label="HALOGEN film: a model in a silver foil outfit and chrome helmet moving against a pale blue wall">
<source src="assets/film.mp4" type="video/mp4">
</video>
<button class="film-play" type="button" aria-label="Play the HALOGEN film">
<svg class="i-play" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 3.5v17l15-8.5z"/></svg>
<svg class="i-pause" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>
</button>
<button class="film-sound" type="button" aria-pressed="false">SOUND OFF</button>
</div>
<div class="film-foot">
<p class="film-copy">TWELVE MINUTES, ONE ROOM, NO RETAKES. OUR FILM FOLLOWS THE <span class="hl">CHROME CAPSULE</span> FROM THE CUTTING TABLE TO THE DANCE FLOOR — SWEAT, SEQUINS AND ALL.<br>
<a class="brk" href="#film">DISCOVER</a></p>
<span class="film-hint" aria-hidden="true">TIP: SCROLL TO DIVE <b>↓</b></span>
</div>
</div>
</div>
<script>
(()=>{
const root=document.getElementById('film');if(!root)return;
const track=root.querySelector('.film-track'),stage=root.querySelector('.film-stage'),frame=root.querySelector('.film-frame'),
img=frame.querySelector('video'),wl=root.querySelector('.film-word--l'),wr=root.querySelector('.film-word--r'),
hint=root.querySelector('.film-hint'),play=root.querySelector('.film-play'),
sound=root.querySelector('.film-sound');
const mq=matchMedia('(max-width:759px),(prefers-reduced-motion: reduce) and (update: none)');
const clamp=(v,a,b)=>Math.min(b,Math.max(a,v)),lerp=(a,b,t)=>a+(b-a)*t,ease=t=>1-Math.pow(1-t,3);
let ticking=false;
function reset(){[frame,wl,wr,img].forEach(el=>el.removeAttribute('style'));hint.classList.remove('is-gone')}
function update(){
ticking=false;
if(mq.matches){reset();return}
const r=track.getBoundingClientRect(),vw=stage.clientWidth,vh=stage.clientHeight;
const p=clamp(-r.top/Math.max(1,r.height-vh),0,1),t=ease(clamp(p/.8,0,1));
const w0=vw*.38,h0=w0*.64,w=lerp(w0,vw,t),h=lerp(h0,vh,t);
frame.style.width=w+'px';frame.style.height=h+'px';frame.style.borderRadius=lerp(18,0,t)+'px';
img.style.transform='scale('+lerp(1.08,1,t)+')';
/* words start tucked just over the frame edges, then slide outward */
const gap=w0*.5-Math.min(60,vw*.04),push=t*vw*.34;
wl.style.transform='translate('+(-gap-push)+'px,-50%)';
wr.style.transform='translate('+(gap+push)+'px,-50%)';
hint.classList.toggle('is-gone',p>.2);
}
const req=()=>{if(!ticking){ticking=true;requestAnimationFrame(update)}};
addEventListener('scroll',req,{passive:true});addEventListener('resize',req);
(mq.addEventListener?mq.addEventListener('change',req):mq.addListener(req));
update();
/* video: plays muted while in view, pauses when scrolled away; manual pause sticks */
const vid=img;let userPaused=false,inView=false;
const sync=()=>{const on=!vid.paused;frame.classList.toggle('is-playing',on);
play.setAttribute('aria-label',(on?'Pause':'Play')+' the HALOGEN film')};
const tryPlay=()=>{const pr=vid.play();pr&&pr.catch(()=>{})};
vid.addEventListener('play',sync);vid.addEventListener('pause',sync);
new IntersectionObserver(es=>es.forEach(e=>{inView=e.isIntersecting;
if(inView&&!userPaused)tryPlay();else if(!inView)vid.pause()}),{threshold:.35}).observe(frame);
play.addEventListener('click',()=>{if(vid.paused){userPaused=false;tryPlay()}else{userPaused=true;vid.pause()}});
sound.addEventListener('click',()=>{vid.muted=!vid.muted;const on=!vid.muted;
sound.setAttribute('aria-pressed',String(on));sound.textContent=on?'SOUND ON':'SOUND OFF';
if(on&&vid.paused){userPaused=false;tryPlay()}});
})();
</script>
</section>
</main>
<footer class="ft" id="footer" aria-labelledby="ft-title">
<style>
.ft{position:relative;background:var(--violet);color:var(--paper);padding-top:clamp(64px,8vw,110px);overflow:hidden;isolation:isolate}
/* torn-paper edge: a strip of page colour with a jagged bottom */
.ft::before{content:'';position:absolute;left:0;right:0;top:-1px;height:30px;background:var(--bg);z-index:1;
clip-path:polygon(0 0,100% 0,100% 38%,97.5% 70%,95% 44%,92% 82%,89.5% 52%,86% 90%,83% 48%,80.5% 74%,77% 36%,74% 86%,71% 56%,68.5% 96%,65% 50%,62% 78%,59.5% 40%,56% 88%,53% 58%,50.5% 100%,47% 46%,44% 72%,41.5% 34%,38% 84%,35% 52%,32.5% 92%,29% 44%,26% 76%,23.5% 38%,20% 88%,17% 54%,14.5% 80%,11% 42%,8% 94%,5.5% 56%,2.5% 78%,0 46%)}
.ft-top{display:grid;grid-template-columns:minmax(0,1fr) clamp(200px,22vw,300px);gap:clamp(36px,5vw,80px);align-items:start}
.ft-main{min-width:0}
.ft-kicker{font-weight:600;color:var(--lime);margin:0 0 18px}
.ft-title{font-size:clamp(46px,7.4vw,118px);margin:0;color:var(--paper)}
.ft-title .ft-hl{color:var(--lime)}
.ft-sub{margin:18px 0 0;color:var(--violet-soft);font-weight:500}
.ft-form{margin-top:30px;display:flex;gap:14px;align-items:flex-end;flex-wrap:wrap;max-width:620px}
.ft-field{flex:1 1 260px;min-width:0;position:relative}
.ft-field label{display:block;font-size:10px;font-weight:600;color:var(--violet-soft);margin-bottom:6px}
.ft-input{width:100%;background:transparent;border:0;border-bottom:2px solid var(--paper);color:var(--paper);
font:500 14px/1.2 var(--halogen-mono);text-transform:uppercase;letter-spacing:.04em;padding:10px 0;border-radius:0;outline:none;
transition:border-color .25s var(--ease)}
.ft-input::placeholder{color:rgba(230,223,255,.62)}
.ft-input:focus{border-bottom-color:var(--lime)}
.ft-input[aria-invalid="true"]{border-bottom-color:var(--lime);border-bottom-style:dashed}
.ft-btn{background:var(--lime);color:var(--ink);padding:14px 20px;font-weight:600;font-size:12px;white-space:nowrap;
transition:transform .25s var(--ease),box-shadow .25s var(--ease)}
.ft-btn::before{content:'[ '}.ft-btn::after{content:' ]'}
.ft-btn:hover{transform:translate(-3px,-3px);box-shadow:3px 3px 0 var(--paper)}
.ft-btn:focus-visible,.ft-top-btn:focus-visible,.ft a:focus-visible{outline:2px solid var(--lime);outline-offset:4px}
.ft-msg{flex-basis:100%;margin:2px 0 0;min-height:1.5em;font-weight:600;color:var(--lime)}
.ft-form.done .ft-field,.ft-form.done .ft-btn{display:none}
.ft-form.done .ft-msg{font-family:var(--halogen-display);font-weight:400;font-size:clamp(26px,3.4vw,44px);line-height:1;letter-spacing:0;min-height:0}
.ft-links{margin-top:clamp(48px,6vw,84px);display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:34px 20px}
.ft-col h3{margin:0 0 14px;font:600 11px/1 var(--halogen-mono);color:var(--lime);letter-spacing:.06em}
.ft-col ul{list-style:none;margin:0;padding:0;display:grid;gap:9px}
.ft-link{position:relative;display:inline-block;color:var(--paper);font-weight:500;transition:color .25s var(--ease),transform .3s var(--ease)}
.ft-link::before,.ft-link::after{position:absolute;top:0;opacity:0;transition:opacity .25s var(--ease),transform .3s var(--ease)}
.ft-link::before{content:'[';left:-.9em;transform:translateX(-6px)}
.ft-link::after{content:']';right:-.9em;transform:translateX(6px)}
.ft-link:hover,.ft-link:focus-visible{color:var(--lime);transform:translateX(.9em)}
.ft-link:hover::before,.ft-link:hover::after,.ft-link:focus-visible::before,.ft-link:focus-visible::after{opacity:1;transform:none}
.ft-photo{margin:0;justify-self:end;width:100%}
.ft-frame{background:var(--paper);padding:10px 10px 12px;transform:rotate(2deg);box-shadow:0 22px 44px -24px rgba(30,21,48,.55);
transition:transform .5s var(--ease)}
.ft-frame:hover{transform:rotate(-3deg) scale(1.02)}
.ft-frame img{width:100%;aspect-ratio:4/5;object-fit:cover}
.ft-frame figcaption{margin-top:9px;display:flex;justify-content:space-between;gap:8px;color:var(--ink);font-size:10px;font-weight:600}
.ft-frame figcaption span:last-child{color:var(--violet)}
.ft-photo .tape{position:relative;z-index:2;margin:0 0 -10px 18px}
/* giant wordmark */
.ft-mark{margin-top:clamp(56px,8vw,110px);overflow:hidden;line-height:0}
.ft-word{display:flex;justify-content:space-between;font-family:var(--halogen-display);font-weight:400;color:var(--lime);
font-size:calc((100vw - 2 * var(--gutter)) / 3.62);line-height:.82;letter-spacing:0;white-space:nowrap;padding-top:.04em}
.ft-word span{display:inline-block;transform:translateY(105%);transition:transform 1s var(--ease);transition-delay:calc(var(--i) * 70ms)}
.ft-mark.in .ft-word span{transform:none}
.ft-legal{margin-top:clamp(18px,2vw,28px);border-top:1px solid rgba(255,255,255,.28);padding-block:18px 26px;
display:flex;flex-wrap:wrap;gap:10px 26px;align-items:center;font-size:10px;font-weight:500;color:var(--violet-soft)}
.ft-legal a{transition:color .25s var(--ease)}
.ft-legal a:hover{color:var(--lime)}
.ft-legal .ft-copy{margin-right:auto;color:var(--paper)}
.ft-top-btn{font-size:10px;font-weight:600;color:var(--ink);background:var(--lime);padding:7px 12px;transition:transform .25s var(--ease)}
.ft-top-btn:hover{transform:translateY(-3px)}
@media (max-width:900px){
.ft-top{grid-template-columns:minmax(0,1fr)}
.ft-photo{justify-self:start;max-width:250px}
}
@media (max-width:640px){
.ft-links{grid-template-columns:repeat(2,minmax(0,1fr));gap:30px 16px}
.ft-form{gap:18px}
.ft-btn{width:100%}
.ft-legal{gap:10px 18px}
.ft-legal .ft-copy{flex-basis:100%}
.ft-photo{max-width:210px}
}
@media (prefers-reduced-motion: reduce) and (update: none){
.ft-word span{transform:none;transition:none}
.ft-frame,.ft-link,.ft-btn,.ft-top-btn{transition:none}
.ft-frame:hover{transform:rotate(2deg)}
}
</style>
<div class="wrap">
<div class="ft-top">
<div class="ft-main">
<p class="ft-kicker">08. THE DROP LIST</p>
<h2 class="display ft-title" id="ft-title">GET THE DROP <span class="ft-hl">FIRST</span></h2>
<p class="ft-sub">ONE EMAIL PER DROP. NO SALE SPAM, EVER.</p>
<form class="ft-form" novalidate>
<div class="ft-field">
<label for="ft-email">YOUR EMAIL</label>
<input class="ft-input" id="ft-email" name="email" type="email" autocomplete="email" placeholder="[email protected]" required aria-describedby="ft-msg">
</div>
<button class="ft-btn" type="submit">SUBSCRIBE</button>
<p class="ft-msg" id="ft-msg" role="status" aria-live="polite"></p>
</form>
<nav class="ft-links" aria-label="Footer">
<div class="ft-col">
<h3>SHOP</h3>
<ul>
<li><a class="ft-link" href="#">SHOP ALL</a></li>
<li><a class="ft-link" href="#">NEW DROP</a></li>
<li><a class="ft-link" href="#">CATEGORIES</a></li>
<li><a class="ft-link" href="#">GIFT CARDS</a></li>
</ul>
</div>
<div class="ft-col">
<h3>HELP</h3>
<ul>
<li><a class="ft-link" href="#">SHIPPING</a></li>
<li><a class="ft-link" href="#">RETURNS</a></li>
<li><a class="ft-link" href="#">SIZE GUIDE</a></li>
<li><a class="ft-link" href="#">REPAIRS</a></li>
</ul>
</div>
<div class="ft-col">
<h3>HALOGEN</h3>
<ul>
<li><a class="ft-link" href="#">WHO WE ARE</a></li>
<li><a class="ft-link" href="#">FILM</a></li>
<li><a class="ft-link" href="#">STOCKISTS</a></li>
<li><a class="ft-link" href="#">CAREERS</a></li>
</ul>
</div>
<div class="ft-col">
<h3>FOLLOW</h3>
<ul>
<li><a class="ft-link" href="#">INSTAGRAM</a></li>
<li><a class="ft-link" href="#">TIKTOK</a></li>
<li><a class="ft-link" href="#">PINTEREST</a></li>
<li><a class="ft-link" href="#">SPOTIFY</a></li>
</ul>
</div>
</nav>
</div>
<div class="ft-photo">
<span class="tape">STUDIO NIGHT ✦ ROTTERDAM</span>
<figure class="ft-frame">
<img src="assets/foot.jpg" alt="Dancer with both arms raised against a pale studio backdrop" loading="lazy" width="600" height="750">
<figcaption><span>(FIG. 08)</span><span>CHROME / CHAOS</span></figcaption>
</figure>
</div>
</div>
</div>
<div class="wrap">
<div class="ft-mark" aria-hidden="true">
<div class="ft-word"><span style="--i:0">H</span><span style="--i:1">A</span><span style="--i:2">L</span><span style="--i:3">O</span><span style="--i:4">G</span><span style="--i:5">E</span><span style="--i:6">N</span></div>
</div>
<div class="ft-legal">
<span class="ft-copy">© 2026 HALOGEN STUDIO, ROTTERDAM</span>
<a href="#">PRIVACY</a>
<a href="#">TERMS</a>
<span>(EN) / (NL)</span>
<span>PHOTOS: PEXELS</span>
<button class="ft-top-btn" type="button">BACK TO TOP ↑</button>
</div>
</div>
<script>
(()=>{
const ft=document.querySelector('.ft');if(!ft)return;
const reduce=matchMedia('(prefers-reduced-motion: reduce) and (update: none)').matches;
/* newsletter: validate locally, never sent anywhere */
const form=ft.querySelector('.ft-form'),input=ft.querySelector('.ft-input'),msg=ft.querySelector('.ft-msg');
const ok=v=>/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v);
form.addEventListener('submit',e=>{
e.preventDefault();
const v=input.value.trim();
if(!ok(v)){input.setAttribute('aria-invalid','true');msg.textContent='THAT EMAIL LOOKS OFF. TRY AGAIN.';input.focus();return}
input.removeAttribute('aria-invalid');
form.classList.add('done');
msg.textContent="YOU'RE ON THE LIST ✦";
});
input.addEventListener('input',()=>{if(input.hasAttribute('aria-invalid')&&ok(input.value.trim())){input.removeAttribute('aria-invalid');msg.textContent=''}});
/* wordmark: fit exactly to the footer's inner width */
const mark=ft.querySelector('.ft-mark'),word=ft.querySelector('.ft-word');
const fit=()=>{
word.style.fontSize='100px';word.style.justifyContent='flex-start';
const w=word.scrollWidth,avail=mark.clientWidth;
word.style.justifyContent='';
if(w>0&&avail>0)word.style.fontSize=(100*avail/w*0.995).toFixed(2)+'px';
};
fit();
if(document.fonts&&document.fonts.ready)document.fonts.ready.then(fit);
if('ResizeObserver' in window)new ResizeObserver(fit).observe(mark);else addEventListener('resize',fit);
/* letters rise when the footer enters view */
if(reduce||!('IntersectionObserver' in window)){mark.classList.add('in')}
else{const io=new IntersectionObserver(es=>es.forEach(e=>{if(e.isIntersecting){mark.classList.add('in');io.disconnect()}}),{threshold:.25});io.observe(mark)}
/* back to top */
ft.querySelector('.ft-top-btn').addEventListener('click',()=>scrollTo({top:0,behavior:reduce?'auto':'smooth'}));
})();
</script>
</footer>
<script>
/* shared: reveal-on-scroll */
(()=>{const io=new IntersectionObserver(es=>es.forEach(e=>{if(e.isIntersecting){e.target.classList.add('in');io.unobserve(e.target)}}),{threshold:.12,rootMargin:'0px 0px -6% 0px'});
document.querySelectorAll('[data-reveal]').forEach(el=>io.observe(el));})();
/* shared: drag-to-scroll for every .rail */
document.querySelectorAll('.rail').forEach(r=>{let down=false,moved=0,x0=0,s0=0;
r.addEventListener('pointerdown',e=>{if(e.pointerType!=='mouse')return;down=true;moved=0;x0=e.clientX;s0=r.scrollLeft});
addEventListener('pointermove',e=>{if(!down)return;const dx=e.clientX-x0;moved=Math.max(moved,Math.abs(dx));if(moved>4)r.classList.add('dragging');r.scrollLeft=s0-dx});
addEventListener('pointerup',()=>{down=false;requestAnimationFrame(()=>r.classList.remove('dragging'))});
r.addEventListener('click',e=>{if(moved>4){e.preventDefault();e.stopPropagation()}},true);
});
</script>
<!-- ================= 09 MOTION (GSAP + ScrollTrigger + Lenis) =================
Animates CSS variables that drive the individual translate / rotate / scale / opacity
properties, so it never fights the sections' own `transform` transitions and hovers. -->
<style>
html.lenis,html.lenis body{height:auto}
html.lenis{scroll-behavior:auto!important}
.lenis.lenis-stopped{overflow:hidden}
.m-c,[data-m]{translate:var(--mx,0) var(--my,0);rotate:var(--mr,0deg);opacity:var(--mo,1)}
.m-c{display:inline-block;will-change:translate,opacity}
.m-w{display:inline-block;white-space:nowrap}
[data-m-img]{scale:var(--ms,1)}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/lenis.min.js"></script>
<script>
(()=>{
if(!window.gsap||!window.ScrollTrigger)return;
if(matchMedia('(prefers-reduced-motion: reduce) and (update: none)').matches)return;
gsap.registerPlugin(ScrollTrigger);
const $=(s,r=document)=>r.querySelector(s), $$=(s,r=document)=>[...r.querySelectorAll(s)];
const EASE='expo.out';
/* ---------- helpers ---------- */
// take an element away from the CSS data-reveal system (GSAP owns it now)
const own=els=>els.forEach(el=>{el.removeAttribute('data-reveal');el.style.transitionDelay=''});
// split text nodes into per-character spans, keeping child elements (.hl, <br>, ...)
const split=el=>{
if(!el)return[];
if(el._chars)return el._chars;
el.setAttribute('aria-label',el.textContent.replace(/\s+/g,' ').trim());
const chars=[];
const walk=node=>[...node.childNodes].forEach(n=>{
if(n.nodeType===3){
const frag=document.createDocumentFragment();
n.textContent.split(/(\s+)/).forEach(part=>{
if(!part)return;
if(/^\s+$/.test(part)){frag.appendChild(document.createTextNode(part));return}
const w=document.createElement('span');w.className='m-w';w.setAttribute('aria-hidden','true');
[...part].forEach(ch=>{const c=document.createElement('span');c.className='m-c';c.textContent=ch;w.appendChild(c);chars.push(c)});
frag.appendChild(w);
});
n.replaceWith(frag);
}else if(n.nodeType===1&&n.tagName!=='BR')walk(n);
});
walk(el);
return el._chars=chars;
};
// tween FROM the given var state to rest; clean up afterwards so hovers etc. own the element again
const clean=els=>els.forEach(el=>{el.removeAttribute('data-m');['--mx','--my','--mr','--mo'].forEach(p=>el.style.removeProperty(p))});
const from=(els,vars,opt={})=>{
els=(Array.isArray(els)?els:[els]).filter(Boolean);
if(!els.length)return null;
els.forEach(el=>{if(!el.classList.contains('m-c'))el.setAttribute('data-m','')});
const to={'--mx':'0px','--my':'0px','--mr':'0deg','--mo':1};
const fromV={'--mx':'0px','--my':'0px','--mr':'0deg','--mo':1,...vars};
return gsap.fromTo(els,fromV,{...to,duration:1.1,ease:EASE,...opt,
onComplete(){clean(els.filter(e=>!e.classList.contains('m-c')));opt.onComplete&&opt.onComplete()}});
};
const onEnter=(trigger,fn,start='top 80%')=>trigger&&ScrollTrigger.create({trigger,start,once:true,onEnter:fn});
/* ---------- Lenis smooth scroll ---------- */
let lenis=null;
if(window.Lenis){
lenis=new Lenis({duration:1.15,smoothWheel:true});
lenis.on('scroll',ScrollTrigger.update);
gsap.ticker.add(t=>lenis.raf(t*1000));
gsap.ticker.lagSmoothing(0);
// in-page anchors glide instead of jumping
document.addEventListener('click',e=>{
const a=e.target.closest('a[href^="#"]');if(!a||e.defaultPrevented)return;
const id=a.getAttribute('href');const t=id.length>1&&document.querySelector(id);
if(id!=='#top'&&!t)return;
e.preventDefault();lenis.scrollTo(t||0,{offset:t?-56:0,duration:1.4});
});
// film modal locks the page: pause Lenis with it
new MutationObserver(()=>document.documentElement.classList.contains('film-lock')?lenis.stop():lenis.start())
.observe(document.documentElement,{attributes:true,attributeFilter:['class']});
const top=$('.ft-top-btn');top&&top.addEventListener('click',e=>{e.stopImmediatePropagation();lenis.scrollTo(0,{duration:1.8})},true);
}
/* ---------- section labels: scramble in ---------- */
const GLYPHS='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/#_+';
$$('.sec-num').forEach(el=>{
if(el.children.length)return;
const txt=el.textContent;
onEnter(el,()=>{
const o={p:0};
gsap.to(o,{p:1,duration:.9,ease:'none',onUpdate(){
const n=Math.floor(o.p*txt.length);
el.textContent=txt.slice(0,n)+[...txt.slice(n)].map(c=>c===' '?' ':GLYPHS[Math.random()*GLYPHS.length|0]).join('');
},onComplete(){el.textContent=txt}});
},'top 88%');
});
/* ---------- 01 HERO intro (waits for the loader) ---------- */
const hero=$('.hero');
if(hero){
const tl=gsap.timeline({paused:true});
const solid=split($('.hero-word:not(.hero-word--stroke) .hero-word-in',hero));
const stroke=split($('.hero-word--stroke .hero-word-in',hero));
const photo=$('.hero-photo',hero);
const charFrom={'--my':'110%','--mr':'9deg','--mo':0};
tl.add(from($('.hero-nav',hero),{'--my':'-100%','--mo':0},{duration:1}),0)
.add(from(solid,charFrom,{stagger:.06,duration:1.3}),.05)
.add(from(stroke,{...charFrom,'--my':'140%'},{stagger:.06,duration:1.5}),.15)
.fromTo(photo,{clipPath:'inset(100% 0% 0% 0%)'},{clipPath:'inset(0% 0% 0% 0%)',duration:1.3,ease:'expo.inOut',clearProps:'clipPath'},.25)
.add(from($('.tape',photo),{'--mx':'-40px','--mo':0,'--mr':'-12deg'},{duration:.8}),1.1)
.add(from($$('.hero-meta > span, .hero-left > *, .hero-links > *, .hero-scroll',hero),{'--my':'26px','--mo':0},{stagger:.07,duration:1}),.7);
const loader=$('.hero-loader',hero);
let played=false;const play=()=>{if(!played){played=true;tl.play()}};
if(!loader||loader.classList.contains('is-gone')||getComputedStyle(loader).display==='none')play();
else{
new MutationObserver((m,obs)=>{if(loader.classList.contains('is-done')){obs.disconnect();setTimeout(play,150)}})
.observe(loader,{attributes:true,attributeFilter:['class']});
setTimeout(play,3200);
}
// marquee: scroll velocity speeds the tickers up and leans the type
const anims=$$('.hero-track',hero).flatMap(t=>t.getAnimations?t.getAnimations():[]);
const spans=$$('.hero-track span',hero);
const skew=gsap.quickTo(spans,'skewX',{duration:.5,ease:'power3.out'});
const boost={v:1};const apply=()=>anims.forEach(a=>a.playbackRate=boost.v);
ScrollTrigger.create({trigger:$('.hero-bands',hero),start:'top bottom',end:'bottom top',onUpdate(self){
const vel=self.getVelocity();
skew(gsap.utils.clamp(-14,14,vel/-180));
const v=Math.min(7,1+Math.abs(vel)/260);
if(v>boost.v+.2)gsap.to(boost,{v,duration:.25,overwrite:true,onUpdate:apply,
onComplete:()=>gsap.to(boost,{v:1,duration:1.4,ease:'power2.out',onUpdate:apply})});
}});
ScrollTrigger.addEventListener('scrollEnd',()=>skew(0));
}
/* ---------- 02 NEW ARRIVALS ---------- */
const arr=$('#arrivals');
if(arr){
const main=$('.arr-main',arr);own([main]);
const cards=$$('.arr-card',arr),imgs=$$('.arr-media img',arr);
imgs.forEach(i=>i.setAttribute('data-m-img',''));
gsap.set(imgs,{'--ms':1.35});
from(split($('.arr-title',arr)),{'--my':'100%','--mr':'6deg','--mo':0},{stagger:.035,scrollTrigger:{trigger:arr,start:'top 75%'}});
const arail=$('.arr-rail',arr);
onEnter(main,()=>{
// translated cards would shift the snap points mid-entrance: hold snapping until they land
arail.style.scrollSnapType='none';
from($$('.arr-head > *',arr),{'--my':'20px','--mo':0},{stagger:.08,duration:.9});
from(cards,{'--mx':'180px','--mr':'5deg','--mo':0},{stagger:.09,duration:1.4,onComplete:()=>{arail.style.scrollSnapType=''}});
gsap.to(imgs,{'--ms':1,duration:1.8,ease:EASE,stagger:.09,delay:.1});
},'top 78%');
}
/* ---------- 04 CATEGORIES: pinned horizontal run + photo parallax ---------- */
const cat=$('#categories');
if(cat){
const rail=$('.cat-rail',cat),cards=$$('.cat-card',cat);own(cards);
const names=$$('.cat-name',cat),photos=$$('.cat-photo',cat),pimgs=$$('.cat-photo img',cat);
from($$('.cat-head > *',cat).slice(1),{'--my':'18px','--mo':0},{stagger:.08,scrollTrigger:{trigger:cat,start:'top 80%'}});
onEnter(cat,()=>{
from(cards,{'--my':'160px','--mr':'-5deg','--mo':0},{stagger:.08,duration:1.3});
gsap.fromTo(photos,{clipPath:'inset(100% 0% 0% 0%)'},{clipPath:'inset(0% 0% 0% 0%)',duration:1.3,ease:'expo.inOut',stagger:.08,delay:.25,clearProps:'clipPath'});
gsap.fromTo(names,{clipPath:'inset(0% 0% 100% 0%)'},{clipPath:'inset(0% 0% 0% 0%)',duration:1.2,ease:'expo.inOut',stagger:.08,delay:.45,clearProps:'clipPath'});
},'top 72%');
// photos drift inside their frames as the row moves (drag, wheel or pin)
pimgs.forEach(i=>{i.style.scale='1.22'});
const drift=()=>{const rr=rail.getBoundingClientRect(),cx=rr.left+rr.width/2;
pimgs.forEach(i=>{const r=i.parentElement.getBoundingClientRect();i.style.translate=(((r.left+r.width/2)-cx)/rr.width*-34).toFixed(1)+'px 0'})};
rail.addEventListener('scroll',()=>requestAnimationFrame(drift),{passive:true});drift();
// desktop: pin the section and turn vertical scroll into a horizontal run through the rail
const mm=gsap.matchMedia();
mm.add('(min-width: 900px)',()=>{
rail.style.scrollSnapType='none';
const max=()=>Math.max(0,rail.scrollWidth-rail.clientWidth);
if(max()<40)return;
const lean=gsap.quickTo(rail,'skewX',{duration:.6,ease:'power3.out'});
const tw=gsap.fromTo(rail,{scrollLeft:0},{scrollLeft:max,ease:'none',scrollTrigger:{
trigger:cat,start:'top top',end:()=>'+='+max(),pin:true,scrub:.7,invalidateOnRefresh:true,anticipatePin:1,
onUpdate:s=>lean(gsap.utils.clamp(-6,6,s.getVelocity()/-350)),onLeave:()=>lean(0),onLeaveBack:()=>lean(0)}});
ScrollTrigger.addEventListener('scrollEnd',()=>lean(0));
return()=>{rail.style.scrollSnapType='';tw.scrollTrigger&&tw.scrollTrigger.kill();tw.kill();gsap.set(rail,{clearProps:'transform'})};
});
}
/* ---------- 03 CAMPAIGN ---------- */
const camp=$('.camp');
if(camp){
onEnter(camp,()=>{
from(split($('.camp-new',camp)),{'--my':'110%','--mr':'12deg','--mo':0},{stagger:.1,duration:1.4});
from($$('.camp-tag, .camp-copy, .camp-foot > *, .camp-chip',camp),{'--my':'28px','--mo':0},{stagger:.08,delay:.3});
},'top 55%');
}
/* ---------- 05 COLLECTIONS ---------- */
const col=$('#collections');
if(col){
const panels=$$('.col-panel',col);own(panels);
const cimgs=$$('.col-img',col);cimgs.forEach(i=>i.setAttribute('data-m-img',''));
onEnter($('.col-grid',col)||col,()=>{
gsap.fromTo(panels,{clipPath:'inset(100% 0% 0% 0%)'},{clipPath:'inset(0% 0% 0% 0%)',duration:1.5,ease:'expo.inOut',stagger:.18,clearProps:'clipPath'});
panels.forEach((p,i)=>{
from(split($('.col-title',p)),{'--my':'110%','--mr':'8deg','--mo':0},{stagger:.05,duration:1.3,delay:.7+i*.18});
from($$('.col-intro, .col-foot > *',p),{'--my':'24px','--mo':0},{stagger:.08,delay:.9+i*.18});
});
},'top 78%');
cimgs.forEach(i=>gsap.fromTo(i,{'--ms':1.3},{'--ms':1,ease:'none',scrollTrigger:{trigger:i.closest('.col-panel'),start:'top bottom',end:'bottom 40%',scrub:true}}));
}
/* ---------- 06 MANIFESTO ---------- */
const man=$('#about');
if(man){
const cardsM=$$('.man-card',man),aside=$('.man-aside',man);own([...cardsM,aside]);
onEnter($('.man-cards',man),()=>from(cardsM,{'--my':'110px','--mr':'4deg','--mo':0},{stagger:.12,duration:1.3}),'top 82%');
onEnter(aside,()=>{
from($('.man-frame',man),{'--my':'90px','--mr':'-14deg','--mo':0},{duration:1.5});
from($$('.man-aside > :not(.man-frame)',man),{'--my':'20px','--mo':0},{stagger:.08,delay:.4});
},'top 82%');
onEnter($('.man-counters',man),()=>from($$('.man-counters > *',man),{'--my':'50px','--mo':0},{stagger:.1}),'top 88%');
}
/* ---------- 07 FILM: play button breathes with the scroll ---------- */
const play=$('.film-play');
if(play){
play.setAttribute('data-m-img','');
gsap.fromTo(play,{'--ms':.4},{'--ms':1,ease:'back.out(2)',scrollTrigger:{trigger:'#film',start:'top 70%',end:'top 10%',scrub:.5}});
}
/* ---------- 08 FOOTER ---------- */
const ft=$('.ft');
if(ft){
onEnter(ft,()=>{
from($('.ft-kicker',ft),{'--mx':'-30px','--mo':0});
from(split($('.ft-title',ft)),{'--my':'110%','--mr':'8deg','--mo':0},{stagger:.035,duration:1.2});
from($$('.ft-sub, .ft-form',ft),{'--my':'24px','--mo':0},{stagger:.1,delay:.35});
from($('.ft-frame',ft),{'--my':'120px','--mr':'14deg','--mo':0},{duration:1.6,delay:.2});
},'top 75%');
onEnter($('.ft-links',ft),()=>from($$('.ft-col',ft),{'--my':'40px','--mo':0},{stagger:.08}),'top 90%');
}
/* layout shifts (fonts, wordmark fit, images) -> recompute trigger positions */
const refresh=()=>ScrollTrigger.refresh();
document.fonts&&document.fonts.ready.then(refresh);
addEventListener('load',refresh);
})();
</script>
</body>
</html>
````
## `sections/01-hero.html`
````html
<header class="hero" id="top">
<script>document.documentElement.classList.add('js');</script>
<style>
/* ================= 01 HERO ================= */
.hero{position:relative;--hero-fs:calc((100vw - 2 * var(--gutter)) / 3.7)}
.hero :focus-visible{outline:2px solid var(--violet);outline-offset:3px}
/* ---- intro loader (hidden unless html.js) ---- */
.hero-loader{display:none}
html.js .hero-loader{display:flex;position:fixed;inset:0;z-index:200;background:var(--lime);color:var(--ink);
flex-direction:column;justify-content:space-between;padding:clamp(16px,3vw,40px);
transition:transform .85s var(--ease);
/* CSS failsafe: leaves on its own even if the script never runs */
animation:hero-loader-out .85s var(--ease) 2.6s forwards}
.hero-loader.is-done{transform:translateY(-101%);pointer-events:none}
.hero-loader.is-gone{display:none!important}
.hero-loader-top{display:flex;justify-content:space-between;gap:16px;font-weight:600;font-size:11px}
.hero-loader-count{font-family:var(--halogen-mono);font-weight:600;font-size:clamp(56px,14vw,180px);line-height:.9;letter-spacing:-.04em}
.hero-loader-bar{height:2px;background:rgba(30,21,48,.14);margin-top:14px;overflow:hidden}
.hero-loader-bar i{display:block;height:100%;width:100%;background:var(--violet);transform:scaleX(0);transform-origin:left}
@keyframes hero-loader-out{to{transform:translateY(-101%);visibility:hidden;pointer-events:none}}
/* ---- fixed nav ---- */
.hero-nav{position:fixed;top:0;left:0;right:0;z-index:100;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
gap:16px;height:56px;padding-inline:var(--gutter);font-size:11px;font-weight:500;
border-bottom:1px solid transparent;transition:background-color .35s var(--ease),border-color .35s var(--ease)}
.hero-nav.is-scrolled{background:var(--bg);border-bottom-color:var(--line)}
.hero-nav-side{display:flex;gap:clamp(14px,2vw,28px);align-items:center;min-width:0}
.hero-nav-side--r{justify-content:flex-end}
.hero-nav a,.hero-nav button{padding:6px 0;white-space:nowrap;transition:color .25s var(--ease)}
.hero-nav a:hover,.hero-nav button:hover{color:var(--violet)}
.hero-nav-logo{font-family:var(--halogen-display);font-size:24px;line-height:1;letter-spacing:.02em}
.hero-nav-count{color:var(--violet);font-weight:600}
@media (max-width:760px){
.hero-nav-hide{display:none}
}
/* ---- stage ---- */
.hero-stage{min-height:100svh;padding:72px var(--gutter) clamp(28px,4vw,48px);display:flex;flex-direction:column;overflow:hidden}
.hero-meta{display:grid;grid-template-columns:1fr auto 1fr;gap:12px;font-size:11px;font-weight:600;padding-bottom:clamp(8px,1.2vw,16px)}
.hero-meta span:nth-child(2){text-align:center}
.hero-meta span:last-child{text-align:right;white-space:nowrap}
@media (max-width:420px){.hero-meta{font-size:10px;gap:8px}}
.hero-word{position:relative;margin:0;font-size:var(--hero-fs);line-height:.8;letter-spacing:-.01em;white-space:nowrap;
color:var(--violet);z-index:1;padding-top:.04em}
.hero-word-in{display:inline-block}
.hero-word--stroke{position:absolute;left:0;right:0;top:0;z-index:3;color:transparent;pointer-events:none;
-webkit-text-stroke:clamp(1px,.18vw,2.5px) var(--violet)}
.hero-wordbox{position:relative}
.hero-grid{position:relative;display:grid;grid-template-columns:1fr auto 1fr;gap:clamp(16px,3vw,48px);align-items:end;
margin-top:calc(var(--hero-fs) * -.46);flex:1}
.hero-photo{position:relative;z-index:2;width:clamp(210px,27vw,400px);aspect-ratio:867/1300;overflow:hidden;
background:var(--violet-soft);box-shadow:var(--shadow)}
.hero-photo img{width:100%;height:112%;object-fit:cover;object-position:50% 40%;margin-top:-6%;will-change:transform}
.hero-photo .tape{position:absolute;left:-6px;top:18%;z-index:2}
.hero-cap{position:absolute;left:0;right:0;bottom:0;display:flex;justify-content:space-between;gap:8px;padding:10px 12px;
font-size:10px;font-weight:600;background:var(--paper);color:var(--ink)}
.hero-cap b{color:var(--violet);font-weight:600}
.hero-left,.hero-right{padding-bottom:4px;display:flex;flex-direction:column;gap:18px}
.hero-right{align-items:flex-end;text-align:right}
.hero-tag{font-size:clamp(13px,1.2vw,16px);font-weight:600;line-height:1.35;max-width:22ch;margin:0}
.hero-tag em{font-style:normal;background:var(--lime);padding:0 .25em}
.hero-sub{margin:0;color:var(--mist);font-size:11px;max-width:30ch}
.hero-links{display:flex;flex-direction:column;gap:10px;align-items:flex-end;font-size:13px}
.hero-links .brk:first-child{color:var(--violet)}
.hero-links .brk:first-child:hover{color:var(--ink)}
.hero-scroll{font-size:10px;color:var(--mist);display:flex;gap:8px;align-items:center}
.hero-scroll i{display:block;width:1px;height:28px;background:var(--line);position:relative;overflow:hidden}
.hero-scroll i::after{content:'';position:absolute;left:0;top:-40%;width:1px;height:40%;background:var(--violet);animation:hero-drip 1.8s var(--ease) infinite}
@keyframes hero-drip{to{top:100%}}
@media (max-width:760px){
.hero-stage{padding-top:68px}
.hero-grid{grid-template-columns:1fr 1fr;row-gap:22px}
.hero-photo{grid-column:1 / -1;grid-row:1;justify-self:center;width:min(66vw,320px)}
.hero-left{grid-row:2;grid-column:1 / -1}
.hero-right{grid-row:3;grid-column:1 / -1;align-items:flex-start;text-align:left}
.hero-links{flex-direction:row;flex-wrap:wrap;gap:10px 20px;align-items:center}
.hero-scroll{display:none}
.hero-tag{max-width:none}
}
/* ---- marquee bands ---- */
.hero-bands{position:relative;height:clamp(150px,17vw,230px);overflow:hidden;margin-top:clamp(8px,2vw,24px)}
.hero-band{position:absolute;left:-10%;width:120%;top:50%;padding-block:clamp(10px,1.2vw,16px);overflow:hidden;
font-family:var(--halogen-display);font-size:clamp(28px,4.2vw,60px);line-height:1;white-space:nowrap}
.hero-band--v{background:var(--violet);color:var(--paper);transform:translateY(-50%) rotate(-2deg);z-index:2;
box-shadow:var(--shadow)}
.hero-band--l{background:var(--lime);color:var(--ink);transform:translateY(-50%) rotate(2.4deg);z-index:1}
.hero-track{display:flex;width:max-content;animation:hero-marq 34s linear infinite}
.hero-band--l .hero-track{animation-direction:reverse;animation-duration:40s}
.hero-track span{display:inline-flex;align-items:center;padding-right:.4em}
.hero-track b{font-weight:400;padding-inline:.4em;font-size:.7em}
.hero-band--v b{color:var(--lime)}
.hero-band--l b{color:var(--violet)}
@keyframes hero-marq{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){
html.js .hero-loader{display:none}
.hero-track,.hero-scroll i::after{animation:none}
.hero-photo img{transform:none!important}
}
</style>
<div class="hero-loader" aria-hidden="true">
<div class="hero-loader-top"><span>HALOGEN / ROTTERDAM</span><span>DROP 04 LOADING</span></div>
<div>
<div class="hero-loader-count"><span data-hero-count>00</span>%</div>
<div class="hero-loader-bar"><i data-hero-bar></i></div>
</div>
</div>
<nav class="hero-nav" aria-label="Main">
<div class="hero-nav-side">
<button type="button" aria-label="Open menu">MENU +</button>
<a class="hero-nav-hide" href="#arrivals">SHOP ALL</a>
<a class="hero-nav-hide" href="#categories">CATEGORIES ▾</a>
</div>
<a class="hero-nav-logo" href="#top" aria-label="HALOGEN home">HALOGEN</a>
<div class="hero-nav-side hero-nav-side--r">
<button type="button" class="hero-nav-hide">SEARCH</button>
<a class="hero-nav-hide" href="#arrivals">SAVED <span class="hero-nav-count">(0)</span></a>
<a href="#arrivals">BAG <span class="hero-nav-count">(0)</span></a>
</div>
</nav>
<div class="hero-stage">
<div class="hero-meta">
<span class="paren">(NL)</span>
<span class="paren">(SS_'26)</span>
<span>DROP 04 / <span class="hl">12 PIECES</span></span>
</div>
<div class="hero-wordbox">
<h1 class="hero-word display"><span class="hero-word-in" data-hero-word>HALOGEN</span></h1>
<div class="hero-word hero-word--stroke display" aria-hidden="true"><span class="hero-word-in">HALOGEN</span></div>
</div>
<div class="hero-grid">
<div class="hero-left">
<p class="hero-tag"><em>WHERE CHROME</em><br>MEETS CHAOS</p>
<p class="hero-sub">Rotterdam-made womenswear. Leather, mesh and loud metal, cut in runs of forty. When it's gone, it's gone.</p>
</div>
<figure class="hero-photo" style="margin:0">
<img src="assets/hero.jpg" alt="Model in a black leather bomber and pleated skirt lunging toward the camera on a pale studio floor" width="867" height="1300" fetchpriority="high" data-hero-par>
<span class="tape">CHROME CAPSULE</span>
<figcaption class="hero-cap"><span>LOOK 07 / VINYL BOMBER</span><b>€ 248</b></figcaption>
</figure>
<div class="hero-right">
<div class="hero-links">
<a class="brk" href="#arrivals">BE LOUD</a>
<a class="brk" href="#arrivals">SEE COLLECTION</a>
</div>
<span class="hero-scroll"><i></i>SCROLL</span>
</div>
</div>
</div>
<div class="hero-bands" aria-hidden="true">
<div class="hero-band hero-band--l">
<div class="hero-track">
<span>NEW DROP<b>✦</b>CHROME CAPSULE<b>✦</b>AFTER HOURS<b>✦</b>ROTTERDAM NIGHTS<b>✦</b>LOUD IS A LOVE LANGUAGE<b>✦</b></span>
<span>NEW DROP<b>✦</b>CHROME CAPSULE<b>✦</b>AFTER HOURS<b>✦</b>ROTTERDAM NIGHTS<b>✦</b>LOUD IS A LOVE LANGUAGE<b>✦</b></span>
</div>
</div>
<div class="hero-band hero-band--v">
<div class="hero-track">
<span>NEW DROP<b>✦</b>CHROME CAPSULE<b>✦</b>AFTER HOURS<b>✦</b>ROTTERDAM NIGHTS<b>✦</b>LOUD IS A LOVE LANGUAGE<b>✦</b></span>
<span>NEW DROP<b>✦</b>CHROME CAPSULE<b>✦</b>AFTER HOURS<b>✦</b>ROTTERDAM NIGHTS<b>✦</b>LOUD IS A LOVE LANGUAGE<b>✦</b></span>
</div>
</div>
</div>
<script>
(()=>{
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
const hero = document.querySelector('.hero');
if(!hero) return;
/* loader */
const loader = hero.querySelector('.hero-loader');
if(loader){
const done = ()=>{loader.classList.add('is-done');setTimeout(()=>loader.classList.add('is-gone'),900)};
if(reduce){loader.classList.add('is-gone')}
else{
loader.style.animation='none'; /* JS is alive: take over from the CSS failsafe */
const num = loader.querySelector('[data-hero-count]'), bar = loader.querySelector('[data-hero-bar]');
const D = 1200, t0 = performance.now();
const tick = now=>{
const p = Math.min(1,(now-t0)/D), e = 1-Math.pow(1-p,3), v = Math.round(e*100);
num.textContent = String(v).padStart(2,'0'); bar.style.transform = 'scaleX('+e+')';
if(p<1) requestAnimationFrame(tick); else setTimeout(done,180);
};
requestAnimationFrame(tick);
}
}
/* nav state */
const nav = hero.querySelector('.hero-nav');
const onNav = ()=>nav.classList.toggle('is-scrolled', scrollY > 40);
/* fit wordmark exactly to the content width */
const box = hero.querySelector('.hero-wordbox'), word = hero.querySelector('[data-hero-word]');
const fit = ()=>{
const avail = box.clientWidth; if(!avail) return;
hero.style.setProperty('--hero-fs','100px');
const w = word.getBoundingClientRect().width;
if(w) hero.style.setProperty('--hero-fs', (Math.floor(avail/w*100*10)/10 - .2)+'px');
};
fit();
if(document.fonts && document.fonts.ready) document.fonts.ready.then(fit);
let rz; addEventListener('resize',()=>{cancelAnimationFrame(rz);rz=requestAnimationFrame(fit)});
/* photo parallax */
const img = hero.querySelector('[data-hero-par]');
let ticking = false;
const par = ()=>{
ticking = false; onNav();
if(reduce || !img) return;
const y = Math.min(scrollY, innerHeight*1.2);
img.style.transform = 'translate3d(0,'+(y*-.03).toFixed(1)+'px,0) scale(1.02)';
};
addEventListener('scroll',()=>{if(!ticking){ticking=true;requestAnimationFrame(par)}},{passive:true});
par();
})();
</script>
</header>
````
## `sections/02-arrivals.html`
````html
<section id="arrivals" class="sec arr" aria-labelledby="arr-title">
<style>
.arr-grid{display:grid;grid-template-columns:minmax(200px,22%) minmax(0,1fr);gap:clamp(20px,3vw,48px);padding-left:var(--gutter);align-items:start}
.arr-side{position:sticky;top:clamp(24px,6vh,72px);display:flex;flex-direction:column;gap:22px;min-width:0}
.arr-head{display:flex;justify-content:space-between;align-items:baseline;gap:12px}
.arr-title{font-size:clamp(44px,5.4vw,84px);margin:0;color:var(--ink)}
.arr-title .hl{display:block}
.arr-meta{display:flex;justify-content:space-between;gap:10px;border-top:1px solid var(--line);padding-top:12px}
.arr-count{font-weight:600}
.arr-filters{display:flex;flex-direction:column;align-items:flex-start;gap:8px;margin:0;padding:0;list-style:none}
.arr-flabel{color:var(--mist);margin-bottom:2px}
.arr-filter{position:relative;padding:4px 8px;margin-left:-8px;font-weight:500;color:var(--ink);transition:color .25s var(--ease),background-color .25s var(--ease)}
.arr-filter::before{content:'('}.arr-filter::after{content:')'}
.arr-filter:hover{color:var(--violet)}
.arr-filter[aria-pressed="true"]{background:var(--lime);color:var(--ink)}
.arr-filter:focus-visible,.arr-nav button:focus-visible,.arr-save:focus-visible,.arr-media:focus-visible{outline:2px solid var(--violet);outline-offset:2px}
.arr-reset{color:var(--mist);font-weight:500;padding:4px 0;transition:color .25s var(--ease)}
.arr-reset:hover{color:var(--violet)}
.arr-reset[hidden]{display:none}
.arr-main{min-width:0}
.arr-bar{display:flex;justify-content:space-between;align-items:center;gap:12px;padding-right:var(--gutter);margin-bottom:6px}
.arr-nav{display:flex;gap:6px}
.arr-nav button{width:40px;height:40px;display:grid;place-items:center;border:1px solid var(--line);background:var(--paper);color:var(--ink);
transition:background-color .25s var(--ease),color .25s var(--ease),border-color .25s var(--ease),opacity .25s}
.arr-nav button:hover:not(:disabled){background:var(--violet);border-color:var(--violet);color:var(--paper)}
.arr-nav button:disabled{opacity:.35;cursor:default}
.arr-nav svg{width:16px;height:16px}
.arr .rail.arr-rail{padding-left:0;padding-right:var(--gutter);padding-block:16px 10px;gap:clamp(12px,1.4vw,18px)}
.arr-card{flex:0 0 clamp(220px,24vw,340px);position:relative;background:var(--paper);border:1px solid var(--line);scroll-snap-align:start;
transition:opacity .3s var(--ease),transform .3s var(--ease),box-shadow .35s var(--ease),border-color .35s var(--ease)}
.arr-card:hover{box-shadow:var(--shadow);border-color:var(--violet-soft)}
.arr-card.is-out{opacity:0;transform:scale(.94)}
.arr-card.is-in{animation:arr-in .5s var(--ease) both;animation-delay:calc(var(--i,0) * 60ms)}
@keyframes arr-in{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}
.arr-card .tape{position:absolute;top:-9px;left:12px;z-index:3;pointer-events:none}
.arr-media{position:relative;display:block;aspect-ratio:3/4;overflow:hidden;background:var(--violet-soft)}
.arr-media img{width:100%;height:100%;object-fit:cover;transition:transform .8s var(--ease)}
.arr-card:hover .arr-media img{transform:scale(1.05)}
.arr-sizes{position:absolute;left:8px;right:8px;bottom:8px;display:flex;flex-wrap:wrap;align-items:center;gap:6px;padding:8px;
background:rgba(255,255,255,.92);border:1px solid var(--line);transform:translateY(calc(100% + 12px));transition:transform .45s var(--ease)}
.arr-card:hover .arr-sizes,.arr-card:focus-within .arr-sizes{transform:none}
.arr-sizes-l{color:var(--mist);font-size:10px;margin-right:auto}
.arr-chip{font-size:10px;font-weight:600;padding:2px 6px;border:1px solid var(--line);background:var(--paper);white-space:nowrap}
.arr-chip::before{content:'('}.arr-chip::after{content:')'}
.arr-info{display:grid;grid-template-columns:1fr auto;gap:4px 10px;padding:12px 12px 14px;align-items:start}
.arr-name{margin:0;font-size:12px;font-weight:600;line-height:1.35}
.arr-tag{grid-column:1;color:var(--mist);font-size:10px}
.arr-price{font-weight:600;color:var(--violet);white-space:nowrap}
.arr-save{position:absolute;top:10px;right:10px;z-index:2;width:36px;height:36px;display:grid;place-items:center;background:var(--paper);
border:1px solid var(--line);color:var(--ink);transition:color .25s var(--ease),background-color .25s var(--ease),transform .25s var(--ease)}
.arr-save:hover{color:var(--violet)}
.arr-save svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;transition:fill .25s var(--ease)}
.arr-save[aria-pressed="true"]{background:var(--lime);color:var(--violet)}
.arr-save[aria-pressed="true"] svg{fill:currentColor}
.arr-save.pop{animation:arr-pop .35s var(--ease)}
@keyframes arr-pop{50%{transform:scale(1.2)}}
.arr-empty{flex:0 0 auto;align-self:center;color:var(--mist);padding:40px 0}
.arr-progress{position:relative;height:2px;background:var(--line);margin-right:var(--gutter);margin-top:14px;overflow:hidden}
.arr-thumb{position:absolute;inset:0 auto 0 0;width:30%;background:var(--violet);transform-origin:left;transition:width .3s var(--ease)}
@media (hover:none){
.arr-sizes{transform:none}
}
@media (max-width:860px){
.arr-grid{grid-template-columns:minmax(0,1fr);padding-left:0}
.arr-side{position:static;padding-inline:var(--gutter)}
.arr-title br{display:none}
.arr-filters{flex-direction:row;flex-wrap:wrap;gap:6px 10px}
.arr-flabel{flex-basis:100%}
.arr-bar{padding-inline:var(--gutter)}
.arr .rail.arr-rail{padding-inline:var(--gutter)}
.arr-progress{margin-inline:var(--gutter)}
.arr-card{flex-basis:clamp(220px,62vw,300px)}
}
@media (prefers-reduced-motion:reduce){
.arr-card,.arr-media img,.arr-sizes,.arr-thumb,.arr-save{transition:none}
.arr-card.is-in,.arr-save.pop{animation:none}
.arr-card:hover .arr-media img{transform:none}
}
</style>
<div class="arr-grid">
<aside class="arr-side" data-reveal>
<div class="arr-head">
<span class="sec-num">02. NEW ARRIVALS</span>
<span class="paren">(NL)</span>
</div>
<h2 class="display arr-title" id="arr-title">Fresh off<br> <span class="hl">the table</span></h2>
<div class="arr-meta">
<span class="arr-count" aria-live="polite"><span data-arr-count>08</span> PIECES</span>
<a href="#" class="brk">SEE ALL</a>
</div>
<ul class="arr-filters" role="group" aria-label="Filter by drop">
<li class="arr-flabel">FILTER BY DROP</li>
<li><button type="button" class="arr-filter" data-tag="chrome" aria-pressed="false">CHROME CAPSULE</button></li>
<li><button type="button" class="arr-filter" data-tag="after" aria-pressed="false">AFTER HOURS</button></li>
<li><button type="button" class="arr-filter" data-tag="new" aria-pressed="false">NEW DROP</button></li>
<li><button type="button" class="arr-reset" hidden>× CLEAR</button></li>
</ul>
</aside>
<div class="arr-main" data-reveal>
<div class="arr-bar">
<span class="drag-hint">[ <b>DRAG</b> · CLICK ]</span>
<div class="arr-nav">
<button type="button" data-arr-prev aria-label="Previous piece"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="M10 3 5 8l5 5"/></svg></button>
<button type="button" data-arr-next aria-label="Next piece"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="m6 3 5 5-5 5"/></svg></button>
</div>
</div>
<div class="rail arr-rail" data-arr-rail>
<article class="arr-card" data-tag="after">
<span class="tape">BEST SELLER</span>
<a href="#" class="arr-media" draggable="false"><img src="assets/p1.jpg" alt="Nocturne slip dress" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">S</span><span class="arr-chip">M</span><span class="arr-chip">L</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Nocturne Slip Dress"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">NOCTURNE SLIP DRESS</h3><span class="arr-price">€ 248</span><span class="arr-tag">(AFTER HOURS)</span></div>
</article>
<article class="arr-card" data-tag="chrome">
<span class="tape">BEST SELLER</span>
<a href="#" class="arr-media" draggable="false"><img src="assets/p2.jpg" alt="Rivet corset set" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">ONE SIZE</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Rivet Corset Set"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">RIVET CORSET SET</h3><span class="arr-price">€ 312</span><span class="arr-tag">(CHROME CAPSULE)</span></div>
</article>
<article class="arr-card" data-tag="new">
<a href="#" class="arr-media" draggable="false"><img src="assets/p3.jpg" alt="Haze mesh body" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">S</span><span class="arr-chip">M</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Haze Mesh Body"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">HAZE MESH BODY</h3><span class="arr-price">€ 96</span><span class="arr-tag">(NEW DROP)</span></div>
</article>
<article class="arr-card" data-tag="chrome">
<span class="tape">BEST SELLER</span>
<a href="#" class="arr-media" draggable="false"><img src="assets/p4.jpg" alt="Static sequin mini dress" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">S</span><span class="arr-chip">M</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Static Sequin Mini"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">STATIC SEQUIN MINI</h3><span class="arr-price">€ 184</span><span class="arr-tag">(CHROME CAPSULE)</span></div>
</article>
<article class="arr-card" data-tag="after">
<a href="#" class="arr-media" draggable="false"><img src="assets/p5.jpg" alt="Polka faux fur coat" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">ONE SIZE</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Polka Faux Fur Coat"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">POLKA FAUX FUR COAT</h3><span class="arr-price">€ 540</span><span class="arr-tag">(AFTER HOURS)</span></div>
</article>
<article class="arr-card" data-tag="new">
<span class="tape">BEST SELLER</span>
<a href="#" class="arr-media" draggable="false"><img src="assets/p6.jpg" alt="White noise suit" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">XS</span><span class="arr-chip">S</span><span class="arr-chip">M</span><span class="arr-chip">L</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save White Noise Suit"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">WHITE NOISE SUIT</h3><span class="arr-price">€ 465</span><span class="arr-tag">(NEW DROP)</span></div>
</article>
<article class="arr-card" data-tag="new">
<a href="#" class="arr-media" draggable="false"><img src="assets/p7.jpg" alt="Lilac rib knit dress" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">S</span><span class="arr-chip">M</span><span class="arr-chip">L</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Lilac Rib Knit Dress"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">LILAC RIB KNIT DRESS</h3><span class="arr-price">€ 158</span><span class="arr-tag">(NEW DROP)</span></div>
</article>
<article class="arr-card" data-tag="after">
<a href="#" class="arr-media" draggable="false"><img src="assets/p8.jpg" alt="Seam line trousers" loading="lazy" draggable="false">
<span class="arr-sizes"><span class="arr-sizes-l">SIZES</span><span class="arr-chip">XS</span><span class="arr-chip">S</span><span class="arr-chip">M</span></span></a>
<button type="button" class="arr-save" aria-pressed="false" aria-label="Save Seam Line Trousers"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5 4.2 12.8a4.9 4.9 0 0 1 6.9-6.9l.9.9.9-.9a4.9 4.9 0 0 1 6.9 6.9Z"/></svg></button>
<div class="arr-info"><h3 class="arr-name">SEAM LINE TROUSERS</h3><span class="arr-price">€ 210</span><span class="arr-tag">(AFTER HOURS)</span></div>
</article>
</div>
<div class="arr-progress" aria-hidden="true"><span class="arr-thumb" data-arr-thumb></span></div>
</div>
</div>
<script>
(()=>{
const sec=document.getElementById('arrivals');if(!sec)return;
const rail=sec.querySelector('[data-arr-rail]'),thumb=sec.querySelector('[data-arr-thumb]');
const prev=sec.querySelector('[data-arr-prev]'),next=sec.querySelector('[data-arr-next]');
const cards=[...sec.querySelectorAll('.arr-card')],btns=[...sec.querySelectorAll('.arr-filter')];
const reset=sec.querySelector('.arr-reset'),count=sec.querySelector('[data-arr-count]');
const rm=matchMedia('(prefers-reduced-motion: reduce)');
const beh=()=>rm.matches?'auto':'smooth';
function progress(){
const max=rail.scrollWidth-rail.clientWidth;
const vis=rail.scrollWidth?rail.clientWidth/rail.scrollWidth:1;
const w=Math.max(vis,.08)*100,p=max>0?rail.scrollLeft/max:0;
thumb.style.width=w+'%';
thumb.style.transform='translateX('+(p*(100-w)/w*100)+'%)';
prev.disabled=rail.scrollLeft<=2;
next.disabled=max<=0||rail.scrollLeft>=max-2;
}
function step(dir){
const c=cards.find(c=>!c.hidden);if(!c)return;
const gap=parseFloat(getComputedStyle(rail).columnGap)||14;
rail.scrollBy({left:dir*(c.getBoundingClientRect().width+gap),behavior:beh()});
}
prev.addEventListener('click',()=>step(-1));
next.addEventListener('click',()=>step(1));
rail.addEventListener('scroll',progress,{passive:true});
addEventListener('resize',progress);
/* filters */
let timer;
function apply(){
const on=btns.filter(b=>b.getAttribute('aria-pressed')==='true').map(b=>b.dataset.tag);
reset.hidden=!on.length;
const show=c=>!on.length||on.includes(c.dataset.tag);
const n=cards.filter(show).length;count.textContent=String(n).padStart(2,'0');
clearTimeout(timer);
cards.forEach(c=>{c.classList.remove('is-in');if(!c.hidden)c.classList.add('is-out')});
timer=setTimeout(()=>{
let i=0;
cards.forEach(c=>{
c.classList.remove('is-out');
const s=show(c);c.hidden=!s;
if(s){c.style.setProperty('--i',i++);void c.offsetWidth;c.classList.add('is-in')}
});
rail.scrollTo({left:0,behavior:'auto'});progress();
},rm.matches?0:260);
}
btns.forEach(b=>b.addEventListener('click',()=>{
b.setAttribute('aria-pressed',b.getAttribute('aria-pressed')==='true'?'false':'true');apply();
}));
reset.addEventListener('click',()=>{btns.forEach(b=>b.setAttribute('aria-pressed','false'));apply();btns[0].focus()});
cards.forEach(c=>c.addEventListener('animationend',e=>{if(e.animationName==='arr-in')c.classList.remove('is-in')}));
/* save toggles */
sec.querySelectorAll('.arr-save').forEach(b=>b.addEventListener('click',()=>{
const on=b.getAttribute('aria-pressed')!=='true';
b.setAttribute('aria-pressed',on);
b.classList.remove('pop');void b.offsetWidth;if(on)b.classList.add('pop');
}));
sec.querySelectorAll('.arr-media').forEach(a=>a.addEventListener('click',e=>e.preventDefault()));
progress();
addEventListener('load',progress);
})();
</script>
</section>
````
## `sections/03-campaign.html`
````html
<section class="camp" id="campaign" aria-label="SS '26 campaign">
<style>
.camp{--p:1;position:relative;height:90vh;height:90svh;min-height:560px;background:var(--bg);overflow:hidden}
.camp-frame{position:absolute;inset:0;overflow:hidden;
--ix:calc((1 - var(--p)) * 12%);--iy:calc((1 - var(--p)) * 9%);
clip-path:inset(var(--iy) var(--ix) round calc((1 - var(--p)) * 28px + 0px));
-webkit-clip-path:inset(var(--iy) var(--ix) round calc((1 - var(--p)) * 28px + 0px));
will-change:clip-path}
.camp-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 45%;
transform:scale(calc(1 + (1 - var(--p)) * .12));transform-origin:50% 50%;will-change:transform}
.camp-shade{position:absolute;inset:0;pointer-events:none;
background:linear-gradient(180deg,rgba(91,46,255,0) 38%,rgba(91,46,255,.22) 62%,rgba(91,46,255,.58) 100%)}
.camp-grain{position:absolute;inset:-50%;pointer-events:none;opacity:.14;mix-blend-mode:overlay;
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .9 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
background-size:180px 180px;animation:camp-grain 1.2s steps(4) infinite}
@keyframes camp-grain{0%{transform:translate(0,0)}25%{transform:translate(-4%,3%)}50%{transform:translate(3%,-2%)}75%{transform:translate(-2%,-4%)}100%{transform:translate(0,0)}}
.camp-ui{position:absolute;inset:0;display:flex;flex-direction:column;justify-content:space-between;
padding:calc(var(--iy) + clamp(18px,2.6vw,36px)) calc(var(--ix) + var(--gutter))}
.camp-top{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
.camp-chip{display:inline-block;background:var(--paper);padding:6px 10px;box-shadow:var(--shadow)}
.camp-chip .sec-num{display:block;line-height:1.2}
.camp-meta{color:var(--paper);font-weight:500;font-size:11px;text-align:right;text-shadow:0 1px 10px rgba(91,46,255,.35)}
.camp-bottom{display:flex;justify-content:space-between;align-items:flex-end;gap:clamp(20px,4vw,64px);color:var(--paper)}
.camp-title{display:flex;align-items:flex-end;gap:clamp(10px,1.4vw,22px);min-width:0}
.camp-new{color:var(--lime);font-size:clamp(96px,17vw,300px);margin:0;line-height:.8;
text-shadow:0 10px 40px rgba(91,46,255,.35)}
.camp-tag{font-weight:600;font-size:clamp(11px,1vw,14px);padding-bottom:.6em;white-space:nowrap}
.camp-copy{max-width:36ch;display:flex;flex-direction:column;gap:14px;padding-bottom:.4em}
.camp-state{margin:0;font-weight:500;font-size:clamp(12px,1.05vw,15px);line-height:1.55}
.camp-state .camp-hl{color:var(--lime);font-weight:600}
.camp-foot{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px 18px;
border-top:1px solid rgba(255,255,255,.4);padding-top:12px}
.camp-foot .camp-pr{color:var(--violet-soft);font-weight:500}
.camp .brk{color:var(--paper)}
.camp .brk:hover,.camp .brk:focus-visible{color:var(--lime)}
.camp .brk:focus-visible{outline:2px solid var(--lime);outline-offset:4px}
@media (max-width:760px){
.camp{min-height:600px}
.camp-frame{--ix:calc((1 - var(--p)) * 6%);--iy:calc((1 - var(--p)) * 7%)}
.camp-img{object-position:42% 50%}
.camp-shade{background:linear-gradient(180deg,rgba(91,46,255,0) 26%,rgba(91,46,255,.3) 52%,rgba(91,46,255,.66) 100%)}
.camp-bottom{flex-direction:column;align-items:stretch;gap:18px}
.camp-new{font-size:clamp(84px,27vw,150px)}
.camp-copy{max-width:none}
}
@media (max-width:420px){
.camp-meta{display:none}
.camp-title{flex-wrap:wrap;gap:6px 10px}
}
@media (prefers-reduced-motion:reduce){
.camp{--p:1!important}
.camp-grain{animation:none}
}
</style>
<div class="camp-frame">
<img class="camp-img" src="assets/camp.jpg" alt="Model in oversized black tailoring and red heels seated on a pale grey studio floor" width="1880" height="1253" loading="lazy" decoding="async">
<div class="camp-shade" aria-hidden="true"></div>
<div class="camp-grain" aria-hidden="true"></div>
<div class="camp-ui">
<div class="camp-top">
<span class="camp-chip"><span class="sec-num">03. CAMPAIGN</span></span>
<span class="camp-meta">SHOT IN ROTTERDAM<br>RUNS OF 40, NEVER MORE</span>
</div>
<div class="camp-bottom">
<div class="camp-title">
<h2 class="display camp-new">NEW</h2>
<span class="camp-tag">( TAILORING )</span>
</div>
<div class="camp-copy">
<p class="camp-state">A HARD SHOULDER IS THE <span class="camp-hl">SOFTEST</span> WAY TO SAY <span class="camp-hl">NO</span>.</p>
<div class="camp-foot">
<span class="camp-pr">(NL) (SS_'26)</span>
<a class="brk" href="#collections">SEE CAMPAIGN</a>
</div>
</div>
</div>
</div>
</div>
<script>
(()=>{
const sec=document.currentScript&&document.currentScript.closest('.camp')||document.querySelector('.camp');
if(!sec)return;
const mq=matchMedia('(prefers-reduced-motion: reduce)');
let ticking=false,active=false;
const clamp=v=>v<0?0:v>1?1:v;
const ease=t=>1-Math.pow(1-t,3);
function update(){
ticking=false;
if(mq.matches){sec.style.setProperty('--p','1');return}
const r=sec.getBoundingClientRect(),vh=innerHeight||1;
// 0 when section top enters the viewport bottom, 1 once its top reaches ~10% from the top
const p=clamp((vh-r.top)/(vh*.9));
sec.style.setProperty('--p',ease(p).toFixed(4));
}
function onScroll(){if(!active||ticking)return;ticking=true;requestAnimationFrame(update)}
if('IntersectionObserver' in window){
new IntersectionObserver(es=>{active=es[0].isIntersecting;if(active)onScroll()},{rootMargin:'10% 0px'}).observe(sec);
}else active=true;
addEventListener('scroll',onScroll,{passive:true});
addEventListener('resize',onScroll);
(mq.addEventListener?mq.addEventListener('change',update):mq.addListener(update));
update();
})();
</script>
</section>
````
## `sections/04-categories.html`
````html
<section class="sec cat" id="categories" aria-labelledby="cat-title">
<style>
.cat-head{display:flex;align-items:center;justify-content:space-between;gap:12px 20px;flex-wrap:wrap;margin-bottom:clamp(22px,3vw,40px)}
.cat-head .drag-hint{color:var(--mist)}
.cat-rail{padding-bottom:8px}
.cat-card{--w:clamp(200px,20vw,300px);--pad:clamp(12px,1.1vw,16px);position:relative;flex:0 0 var(--w);width:var(--w);aspect-ratio:3/4.4;
background:var(--paper);border:1px solid var(--line);overflow:hidden;scroll-snap-align:start;container-type:inline-size;
transition:opacity .9s var(--ease),transform .9s var(--ease),background-color .35s var(--ease),border-color .35s var(--ease),box-shadow .35s var(--ease);
transition-delay:calc(var(--i,0) * 80ms),calc(var(--i,0) * 80ms),0s,0s,0s}
.cat-card.in{transition-delay:0s}
.cat-card:focus-visible{outline:2px solid var(--violet);outline-offset:3px}
.cat-photo{position:absolute;top:var(--pad);right:var(--pad);width:62%;height:70%;overflow:hidden;background:var(--violet-soft);
transition:transform .5s var(--ease)}
.cat-photo img{width:100%;height:100%;object-fit:cover;transition:transform .7s var(--ease)}
.cat-name{position:absolute;left:calc(var(--pad) - .06em);bottom:var(--pad);z-index:2;margin:0;color:var(--ink);
writing-mode:vertical-rl;transform:rotate(180deg);font-size:26cqi;line-height:.84;white-space:nowrap;
transition:color .3s var(--ease)}
.cat-count{position:absolute;right:var(--pad);bottom:var(--pad);font-weight:600;font-size:11px;color:var(--mist);transition:color .3s var(--ease)}
.cat-tape{position:absolute;top:calc(var(--pad) + 10px);left:var(--pad);z-index:3;opacity:0;transform:rotate(-6deg) translateY(6px) scale(.9);
transition:opacity .3s var(--ease),transform .35s var(--ease);pointer-events:none}
.cat-card:hover,.cat-card:focus-visible{background:var(--violet-soft);border-color:color-mix(in srgb,var(--violet) 30%,var(--line));box-shadow:var(--shadow)}
.cat-card:hover .cat-name,.cat-card:focus-visible .cat-name{color:var(--violet)}
.cat-card:hover .cat-count,.cat-card:focus-visible .cat-count{color:var(--violet)}
.cat-card:hover .cat-photo,.cat-card:focus-visible .cat-photo{transform:translateY(-8px)}
.cat-card:hover .cat-photo img,.cat-card:focus-visible .cat-photo img{transform:scale(1.04)}
.cat-card:hover .cat-tape,.cat-card:focus-visible .cat-tape{opacity:1;transform:rotate(-6deg)}
@media (max-width:640px){
.cat-head .drag-hint{order:3;flex-basis:100%}
}
@media (prefers-reduced-motion:reduce){
.cat-card,.cat-photo,.cat-photo img,.cat-name,.cat-tape{transition:none!important}
.cat-card:hover .cat-photo,.cat-card:focus-visible .cat-photo,
.cat-card:hover .cat-photo img,.cat-card:focus-visible .cat-photo img{transform:none}
.cat-card:hover .cat-tape,.cat-card:focus-visible .cat-tape{transform:rotate(-6deg)}
}
</style>
<div class="wrap cat-head">
<h2 class="sec-num" id="cat-title" style="margin:0">04. CATEGORIES</h2>
<span class="drag-hint" aria-hidden="true">[ <b>DRAG</b> · CLICK ]</span>
<a class="brk" href="#categories">SEE ALL</a>
</div>
<div class="rail cat-rail" role="list" aria-label="Shop by category">
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:0" aria-label="Slip dresses, 14 pieces">
<span class="cat-photo"><img src="assets/c1.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">SLIP DRESSES</h3>
<span class="cat-count">[ 14 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:1" aria-label="Leather, 9 pieces">
<span class="cat-photo"><img src="assets/c2.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">LEATHER</h3>
<span class="cat-count">[ 9 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:2" aria-label="Jewellery, 22 pieces">
<span class="cat-photo"><img src="assets/c3.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">JEWELLERY</h3>
<span class="cat-count">[ 22 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:3" aria-label="Boots and heels, 11 pieces">
<span class="cat-photo"><img src="assets/c4.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">BOOTS & HEELS</h3>
<span class="cat-count">[ 11 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:4" aria-label="Sequin and metal, 7 pieces">
<span class="cat-photo"><img src="assets/c5.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">SEQUIN & METAL</h3>
<span class="cat-count">[ 7 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:5" aria-label="Bodysuits, 6 pieces">
<span class="cat-photo"><img src="assets/c6.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">BODYSUITS</h3>
<span class="cat-count">[ 6 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:6" aria-label="Knitwear, 8 pieces">
<span class="cat-photo"><img src="assets/p7.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">KNITWEAR</h3>
<span class="cat-count">[ 8 ]</span>
</a>
<a class="cat-card" role="listitem" href="#categories" data-reveal style="--i:7" aria-label="Outerwear, 5 pieces">
<span class="cat-photo"><img src="assets/p5.jpg" alt="" loading="lazy" draggable="false"></span>
<span class="tape cat-tape">SHOP →</span>
<h3 class="cat-name display">OUTERWEAR</h3>
<span class="cat-count">[ 5 ]</span>
</a>
</div>
<script>
(()=>{
/* fit each vertical name to its card height: start from the CSS size (26cqi) and shrink only if it would overflow */
const names=[...document.querySelectorAll('#categories .cat-name')];
if(!names.length)return;
const fit=()=>names.forEach(n=>{
const card=n.parentElement,pad=parseFloat(getComputedStyle(card).getPropertyValue('--pad'))||14;
n.style.fontSize='';
const max=card.clientHeight-pad*2;
let fs=parseFloat(getComputedStyle(n).fontSize),guard=0;
while(n.offsetHeight>max&&fs>12&&guard++<60){fs*=.95;n.style.fontSize=fs+'px';}
});
let t;const run=()=>{cancelAnimationFrame(t);t=requestAnimationFrame(fit)};
run();
if(document.fonts&&document.fonts.ready)document.fonts.ready.then(run);
if('ResizeObserver' in window){const ro=new ResizeObserver(run);ro.observe(names[0].parentElement);}
else addEventListener('resize',run);
})();
</script>
</section>
````
## `sections/05-collections.html`
````html
<section class="sec col" id="collections" aria-labelledby="col-title">
<style>
.col-head{display:flex;justify-content:space-between;align-items:baseline;gap:16px;flex-wrap:wrap;margin-bottom:clamp(28px,4vw,56px)}
.col-head h2{margin:0;font:inherit}
.col-grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(12px,1.6vw,24px);align-items:start}
.col-panel{position:relative;display:block;height:88svh;min-height:560px;max-height:980px;overflow:hidden;background:var(--violet-soft);
/* torn-paper top edge */
clip-path:polygon(0 3.2%,3% 1.6%,6% 2.9%,9.5% .8%,13% 2.4%,16% 1.1%,19.5% 3%,23% 1.4%,26% 2.6%,30% .6%,33% 2.2%,36.5% 1.2%,40% 3.1%,43% 1.5%,46.5% 2.7%,50% .9%,53% 2.5%,57% 1.3%,60% 3.2%,63.5% 1.7%,67% 2.8%,70% .7%,73.5% 2.3%,77% 1.2%,80% 3%,83.5% 1.4%,87% 2.6%,90% .8%,93.5% 2.4%,97% 1.3%,100% 2.8%,100% 100%,0 100%);
will-change:transform}
.col-panel:focus-visible{outline:none}
.col-panel:focus-visible .col-frame{box-shadow:inset 0 0 0 3px var(--lime)}
.col-frame{position:absolute;inset:0}
.col-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 30%;transform:scale(1);transition:transform 1.1s var(--ease)}
.col-panel::after{content:'';position:absolute;inset:0;pointer-events:none;
background:linear-gradient(to top,rgba(91,46,255,.82) 0%,rgba(91,46,255,.5) 22%,rgba(91,46,255,0) 50%)}
.col-intro{position:absolute;z-index:2;top:clamp(40px,5vw,64px);max-width:min(300px,calc(100% - 32px));margin:0;
background:rgba(255,255,255,.86);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
color:var(--ink);font-size:11px;line-height:1.55;padding:10px 12px;border:1px solid rgba(220,215,234,.9)}
.col-intro--l{left:clamp(16px,2vw,28px)}
.col-intro--r{right:clamp(16px,2vw,28px)}
.col-foot{position:absolute;z-index:2;left:clamp(16px,2vw,28px);right:clamp(16px,2vw,28px);bottom:clamp(16px,2vw,28px);
display:flex;justify-content:space-between;align-items:flex-end;gap:16px}
.col-title{margin:0;color:var(--lime);font-size:clamp(64px,9.4vw,168px);transition:letter-spacing .6s var(--ease);overflow-wrap:anywhere}
.col-panel--b .col-title{color:var(--paper)}
.col-tag{display:block;margin-top:8px;color:var(--paper);font-weight:600;font-size:12px}
.col-count{color:var(--paper);font-weight:600;font-size:13px;flex:none}
.col-panel--b .col-count{color:var(--lime)}
@media (hover:hover){
.col-panel:hover .col-img{transform:scale(1.05)}
.col-panel:hover .col-title{letter-spacing:.03em}
}
.col-panel:focus-visible .col-img{transform:scale(1.05)}
@media (max-width:860px){
.col-grid{grid-template-columns:1fr}
.col-panel{height:78svh;min-height:480px;max-height:720px}
.col-title{font-size:clamp(56px,17vw,120px)}
}
@media (max-width:420px){
.col-intro{font-size:10.5px;padding:9px 10px}
}
@media (prefers-reduced-motion:reduce){
.col-img,.col-title{transition:none}
.col-panel:hover .col-img,.col-panel:focus-visible .col-img{transform:none}
.col-panel{transform:none!important}
}
</style>
<div class="wrap">
<div class="col-head" data-reveal>
<h2 class="sec-num" id="col-title">05. COLLECTIONS</h2>
<a class="brk" href="#collections">SEE COLLECTION</a>
</div>
<div class="col-grid">
<a class="col-panel col-panel--a" href="#collections" aria-label="Chrome capsule SS26, 18 pieces" data-reveal>
<div class="col-frame">
<img class="col-img" src="assets/col1.jpg" alt="Model in a metallic silver strapless dress against a pale backdrop" loading="lazy" decoding="async">
</div>
<p class="col-intro col-intro--l">BORN IN THE LAST TRAM HOME, <span class="hl">CHROME CAPSULE</span> SS26 IS FOIL, LAMÉ AND NERVE.</p>
<div class="col-foot">
<div>
<h3 class="col-title display">CHROME</h3>
<span class="col-tag">(GLAM)</span>
</div>
<span class="col-count">[ 18 ]</span>
</div>
</a>
<a class="col-panel col-panel--b" href="#collections" aria-label="After Hours collection, 11 pieces" data-reveal data-col-par>
<div class="col-frame">
<img class="col-img" src="assets/col2.jpg" alt="Model in a black latex outfit and thigh-high boots lounging on a hot-pink inflatable" loading="lazy" decoding="async">
</div>
<p class="col-intro col-intro--r">PATENT, FISHNET AND FAUX FUR — <span class="hl">AFTER HOURS</span> DRESSES FOR THE ROOM YOU HAVEN'T WALKED INTO YET.</p>
<div class="col-foot">
<div>
<h3 class="col-title display">AFTER<br>HOURS</h3>
<span class="col-tag">(GRUNGE)</span>
</div>
<span class="col-count">[ 11 ]</span>
</div>
</a>
</div>
</div>
<script>
(()=>{
const sec=document.currentScript&&document.currentScript.closest('.col')||document.querySelector('.col');
if(!sec)return;
const panel=sec.querySelector('[data-col-par]');
const mqWide=matchMedia('(min-width:861px)'),mqRM=matchMedia('(prefers-reduced-motion:reduce)');
let ticking=false,active=false;
const apply=()=>{
ticking=false;
if(!mqWide.matches||mqRM.matches){panel.style.translate='';return}
const r=sec.getBoundingClientRect(),vh=innerHeight;
// progress -1..1 as the section travels through the viewport
const p=Math.max(-1,Math.min(1,((r.top+r.height/2)-vh/2)/(vh/2+r.height/2)));
panel.style.translate='0 '+(p*60).toFixed(1)+'px';
};
const onScroll=()=>{if(active&&!ticking){ticking=true;requestAnimationFrame(apply)}};
new IntersectionObserver(es=>{active=es[0].isIntersecting;if(active)onScroll()},{rootMargin:'120px 0px'}).observe(sec);
addEventListener('scroll',onScroll,{passive:true});
addEventListener('resize',()=>{ticking=false;apply()});
mqRM.addEventListener&&mqRM.addEventListener('change',apply);
})();
</script>
</section>
````
## `sections/06-manifesto.html`
````html
<style>
/* ---------- 06. manifesto (slug: man) ---------- */
.man{position:relative;overflow:hidden}
.man-head{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:clamp(28px,4vw,56px)}
.man-head .paren{font-weight:500}
.man-statement{font-size:clamp(40px,8.4vw,120px);line-height:.92;margin:0;max-width:15.5ch;overflow-wrap:break-word}
.man-w{color:var(--line);transition:color .45s var(--ease)}
.man-w.on{color:var(--ink)}
.man-w.man-key.on{color:var(--violet)}
.man-body{display:grid;grid-template-columns:1fr;gap:clamp(28px,4vw,48px);margin-top:clamp(48px,7vw,104px);align-items:start}
@media (min-width:1100px){.man-body{grid-template-columns:minmax(220px,280px) 1fr}}
/* portrait */
.man-aside{display:flex;flex-direction:column;align-items:flex-start;gap:22px}
.man-frame{position:relative;width:min(260px,72vw);background:var(--paper);border:1px solid var(--line);padding:10px 10px 34px;
transform:rotate(-3.5deg);box-shadow:var(--shadow);margin:14px 0 0 8px;transition:transform .6s var(--ease)}
.man-frame:hover{transform:rotate(-1deg)}
.man-frame img{width:100%;aspect-ratio:4/5;object-fit:cover;filter:saturate(.92)}
.man-frame figcaption{position:absolute;left:10px;right:10px;bottom:9px;font-size:10px;color:var(--mist);display:flex;justify-content:space-between}
.man-frame .tape{position:absolute;top:-12px;right:-10px;transform:rotate(5deg)}
.man-note{max-width:32ch;color:var(--mist);margin:0}
/* value cards */
.man-cards{display:grid;grid-template-columns:1fr;gap:14px;list-style:none;margin:0;padding:0}
@media (min-width:760px){.man-cards{grid-template-columns:repeat(3,1fr)}}
.man-card{position:relative;background:var(--paper);border:1px solid var(--line);padding:clamp(20px,2.4vw,30px);display:flex;flex-direction:column;gap:18px;min-height:clamp(240px,26vw,340px);
transition:transform .45s var(--ease),box-shadow .45s var(--ease),border-color .45s var(--ease)}
.man-card:hover{transform:translateY(-8px);box-shadow:var(--shadow);border-color:var(--violet-soft)}
.man-idx{font-family:var(--halogen-mono);font-weight:600;color:var(--violet);font-size:clamp(34px,3.6vw,48px);line-height:1;letter-spacing:-.04em}
.man-label{margin:auto 0 0;font-size:clamp(34px,3.4vw,52px);display:flex;flex-wrap:wrap;align-items:baseline;gap:.35em}
.man-label .display{position:relative;display:inline-block;padding-bottom:6px}
.man-label .display::after{content:'';position:absolute;left:0;right:0;bottom:0;height:6px;background:var(--lime);transform:scaleX(0);transform-origin:left;transition:transform .5s var(--ease)}
.man-card:hover .man-label .display::after,.man-card:focus-within .man-label .display::after{transform:scaleX(1)}
.man-label .paren{font-family:var(--halogen-mono);font-size:12px;font-weight:500}
.man-card p{margin:0;max-width:34ch}
/* counters */
.man-counters{display:grid;grid-template-columns:1fr;border-top:1px solid var(--line);margin-top:clamp(48px,6vw,88px);list-style:none;padding:0;margin-bottom:0}
@media (min-width:760px){.man-counters{grid-template-columns:repeat(3,1fr)}}
.man-counters li{display:flex;align-items:flex-end;gap:14px;padding:22px 0;border-bottom:1px solid var(--line)}
@media (min-width:760px){
.man-counters li{border-bottom:0;padding:28px 20px 0}
.man-counters li:first-child{padding-left:0}
.man-counters li+li{border-left:1px solid var(--line)}
}
.man-num{font-size:clamp(64px,8vw,112px);color:var(--violet);font-variant-numeric:tabular-nums;min-width:1.1ch}
.man-counters span.man-cap{font-weight:600;max-width:14ch;padding-bottom:8px}
@media (prefers-reduced-motion:reduce){
.man-w{transition:none;color:var(--ink)}
.man-w.man-key{color:var(--violet)}
.man-card,.man-frame,.man-label .display::after{transition:none}
.man-card:hover{transform:none}
}
</style>
<section class="sec man" id="about" aria-labelledby="man-title">
<div class="wrap">
<div class="man-head">
<span class="sec-num">06. WHO WE ARE</span>
<span class="paren">(ROTTERDAM, NL — EST. ATTIC)</span>
</div>
<h2 class="display man-statement" id="man-title" data-man-words data-man-keys="SMALL,RUNS,ROTTERDAM,UNIFORM.">WE CUT SMALL RUNS IN A ROTTERDAM ATTIC SO NOTHING WE MAKE EVER FEELS LIKE A UNIFORM.</h2>
<div class="man-body">
<aside class="man-aside" data-reveal>
<figure class="man-frame">
<span class="tape">ATELIER NOTES</span>
<img src="assets/p6.jpg" alt="Model in a sharp white suit photographed in the HALOGEN atelier" width="520" height="650" loading="lazy">
<figcaption><span>FIT TEST #112</span><span>ATTIC, 3RD FLOOR</span></figcaption>
</figure>
<p class="man-note">ONE CUTTING TABLE, TWO SEWING MACHINES, A RADIATOR THAT ONLY WORKS ON TUESDAYS. EVERYTHING STARTS HERE.</p>
<a href="#about" class="brk">OUR STORY</a>
</aside>
<div>
<ul class="man-cards">
<li class="man-card" data-reveal>
<span class="man-idx">01</span>
<h3 class="man-label"><span class="display">RUNS</span><span class="paren">(SMALL)</span></h3>
<p>EVERY PIECE IS MADE IN BATCHES OF 40 OR FEWER. WHEN IT'S GONE, IT'S GONE.</p>
</li>
<li class="man-card" data-reveal style="transition-delay:.08s">
<span class="man-idx">02</span>
<h3 class="man-label"><span class="display">CLOTH</span><span class="paren">(DEADSTOCK)</span></h3>
<p>WE BUY LEFTOVER BOLTS FROM LARGER HOUSES AND TURN THEM INTO SOMETHING LOUDER.</p>
</li>
<li class="man-card" data-reveal style="transition-delay:.16s">
<span class="man-idx">03</span>
<h3 class="man-label"><span class="display">REPAIR</span><span class="paren">(FOR LIFE)</span></h3>
<p>SNAPPED STRAP, LOST RIVET, BLOWN SEAM — SEND IT BACK AND WE FIX IT FREE.</p>
</li>
</ul>
<ul class="man-counters">
<li><span class="display man-num" data-man-count="40" data-man-from="0">40</span><span class="man-cap">PIECES PER RUN MAX</span></li>
<li><span class="display man-num" data-man-count="3" data-man-from="0">3</span><span class="man-cap">YEARS FREE REPAIRS</span></li>
<li><span class="display man-num" data-man-count="0" data-man-from="99">0</span><span class="man-cap">WHOLESALE MARKUP</span></li>
</ul>
</div>
</div>
</div>
</section>
<script>
(()=>{
const root=document.getElementById('about');if(!root)return;
const reduce=matchMedia('(prefers-reduced-motion: reduce)').matches;
/* word-by-word scroll reveal */
const h=root.querySelector('[data-man-words]');
const keys=(h.dataset.manKeys||'').split(',');
const text=h.textContent.trim();
h.setAttribute('aria-label',text);
h.textContent='';
const words=text.split(/\s+/).map((w,i,a)=>{
const s=document.createElement('span');
s.className='man-w'+(keys.includes(w)?' man-key':'');
s.setAttribute('aria-hidden','true');
s.textContent=w;
h.appendChild(s);
if(i<a.length-1)h.appendChild(document.createTextNode(' '));
return s;
});
if(reduce){words.forEach(w=>w.classList.add('on'))}
else{
let ticking=false;
const update=()=>{
ticking=false;
const r=h.getBoundingClientRect(),vh=innerHeight;
const p=Math.min(1,Math.max(0,(vh*.88-r.top)/(r.height+vh*.4)));
const lit=Math.round(p*words.length);
words.forEach((w,i)=>w.classList.toggle('on',i<lit));
};
const req=()=>{if(!ticking){ticking=true;requestAnimationFrame(update)}};
addEventListener('scroll',req,{passive:true});
addEventListener('resize',req);
update();
}
/* counters */
const nums=root.querySelectorAll('[data-man-count]');
const run=el=>{
const to=+el.dataset.manCount,from=+(el.dataset.manFrom||0);
if(reduce){el.textContent=to;return}
const dur=1400,t0=performance.now();
const step=t=>{
const k=Math.min(1,(t-t0)/dur),e=1-Math.pow(1-k,3);
el.textContent=Math.round(from+(to-from)*e);
if(k<1)requestAnimationFrame(step);
};
el.textContent=from;
requestAnimationFrame(step);
};
if(!('IntersectionObserver' in window)){return}
const io=new IntersectionObserver(es=>es.forEach(e=>{if(e.isIntersecting){run(e.target);io.unobserve(e.target)}}),{threshold:.6});
nums.forEach(n=>io.observe(n));
})();
</script>
````
## `sections/07-film.html`
````html
<section id="film" class="film" aria-labelledby="film-title">
<style>
.film{position:relative;background:var(--bg)}
.film-track{position:relative;height:250svh}
.film-stage{position:sticky;top:0;height:100svh;overflow:hidden;background:var(--bg)}
.film-frame{position:absolute;left:50%;top:50%;width:38vw;height:calc(38vw * .64);transform:translate(-50%,-50%);
border-radius:18px;overflow:hidden;background:var(--violet-soft);box-shadow:var(--shadow);will-change:width,height,border-radius}
.film-frame video{display:block;width:100%;height:100%;object-fit:cover;transform:scale(1.08);will-change:transform;background:var(--violet-soft)}
.film-frame.is-playing .film-play{opacity:0}
.film-frame.is-playing:hover .film-play,.film-play:focus-visible{opacity:1}
.film-frame.is-playing .film-play::before{animation:none;opacity:0}
.film-play .i-pause{display:none}
.film-frame.is-playing .film-play .i-play{display:none}
.film-frame.is-playing .film-play .i-pause{display:block;margin-left:0}
.film-sound{position:absolute;right:14px;bottom:14px;z-index:3;font-size:10px;font-weight:600;padding:7px 11px;
background:rgba(255,255,255,.86);border:1px solid var(--line);color:var(--ink);transition:background-color .25s var(--ease),color .25s var(--ease)}
.film-sound:hover{color:var(--violet)}
.film-sound[aria-pressed="true"]{background:var(--lime)}
.film-sound:focus-visible{outline:2px solid var(--violet);outline-offset:3px}
.film-words{position:absolute;inset:0;pointer-events:none;z-index:2}
.film-word{position:absolute;top:50%;margin:0;color:var(--violet);font-size:clamp(64px,9vw,170px);white-space:nowrap;
transform:translateY(-50%);will-change:transform}
.film-word--l{right:50%}
.film-word--r{left:50%}
.film-play{position:absolute;left:50%;top:50%;z-index:3;width:72px;height:72px;margin:-36px 0 0 -36px;border-radius:50%;
background:var(--lime);display:grid;place-items:center;box-shadow:0 12px 30px -12px rgba(91,46,255,.55);
transition:transform .35s var(--ease),opacity .35s var(--ease)}
.film-play::before{content:'';position:absolute;inset:0;border-radius:50%;border:2px solid var(--lime);animation:film-pulse 2.2s var(--ease) infinite}
.film-play svg{width:22px;height:22px;margin-left:4px;fill:var(--ink)}
.film-play:hover{transform:scale(1.08)}
.film-play:focus-visible{outline:2px solid var(--violet);outline-offset:6px}
@keyframes film-pulse{0%{transform:scale(1);opacity:.9}100%{transform:scale(1.7);opacity:0}}
.film-label{position:absolute;top:clamp(20px,3vw,36px);left:var(--gutter);z-index:4;margin:0;display:flex;gap:12px;align-items:center}
.film-foot{position:absolute;left:var(--gutter);right:var(--gutter);bottom:clamp(20px,3vw,36px);z-index:4;display:flex;
justify-content:space-between;align-items:flex-end;gap:24px;flex-wrap:wrap}
.film-copy{max-width:46ch;margin:0;padding:14px 16px;background:rgba(255,255,255,.78);border:1px solid var(--line);
-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);font-size:12px}
.film-copy .brk{margin-top:10px;display:inline-flex;color:var(--violet)}
.film-copy .brk:hover{color:var(--ink)}
.film-hint{font-weight:600;color:var(--ink);background:rgba(255,255,255,.78);padding:6px 10px;border:1px solid var(--line);
transition:opacity .4s var(--ease)}
.film-hint.is-gone{opacity:0}
.film-hint b{color:var(--violet);font-weight:600}
/* static layout: mobile + reduced motion */
@media (max-width:759px),(prefers-reduced-motion:reduce){
.film{padding-block:clamp(56px,10vw,100px)}
.film-track{height:auto}
.film-stage{position:relative;height:auto;overflow:visible;display:flex;flex-direction:column;gap:18px;padding-inline:var(--gutter)}
.film-label{position:static}
.film-words{position:static;order:1;display:flex;flex-direction:column}
.film-word{position:static;transform:none!important;font-size:clamp(56px,17vw,120px)}
.film-word--r{align-self:flex-end}
.film-frame{position:relative;left:auto;top:auto;order:2;width:100%!important;height:auto!important;aspect-ratio:4/3;
transform:none;border-radius:14px!important}
.film-frame video{transform:none!important}
.film-play{order:2}
.film-foot{position:static;order:3;flex-direction:column;align-items:flex-start}
.film-hint{display:none}
}
@media (prefers-reduced-motion:reduce){
.film-play::before{animation:none;opacity:0}
}
</style>
<div class="film-track">
<div class="film-stage">
<p class="film-label"><span class="sec-num">07. HALOGEN FILM</span><span class="tape">NEW</span></p>
<div class="film-words" aria-hidden="true">
<span class="film-word film-word--l display">A VISION</span>
<span class="film-word film-word--r display">IN MOTION</span>
</div>
<h2 id="film-title" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)">A vision in motion — the HALOGEN film</h2>
<div class="film-frame">
<video class="film-video" muted loop playsinline preload="metadata" poster="assets/film-poster.jpg"
aria-label="HALOGEN film: a model in a silver foil outfit and chrome helmet moving against a pale blue wall">
<source src="assets/film.mp4" type="video/mp4">
</video>
<button class="film-play" type="button" aria-label="Play the HALOGEN film">
<svg class="i-play" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 3.5v17l15-8.5z"/></svg>
<svg class="i-pause" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>
</button>
<button class="film-sound" type="button" aria-pressed="false">SOUND OFF</button>
</div>
<div class="film-foot">
<p class="film-copy">TWELVE MINUTES, ONE ROOM, NO RETAKES. OUR FILM FOLLOWS THE <span class="hl">CHROME CAPSULE</span> FROM THE CUTTING TABLE TO THE DANCE FLOOR — SWEAT, SEQUINS AND ALL.<br>
<a class="brk" href="#film">DISCOVER</a></p>
<span class="film-hint" aria-hidden="true">TIP: SCROLL TO DIVE <b>↓</b></span>
</div>
</div>
</div>
<script>
(()=>{
const root=document.getElementById('film');if(!root)return;
const track=root.querySelector('.film-track'),stage=root.querySelector('.film-stage'),frame=root.querySelector('.film-frame'),
img=frame.querySelector('video'),wl=root.querySelector('.film-word--l'),wr=root.querySelector('.film-word--r'),
hint=root.querySelector('.film-hint'),play=root.querySelector('.film-play'),
sound=root.querySelector('.film-sound');
const mq=matchMedia('(max-width:759px),(prefers-reduced-motion:reduce)');
const clamp=(v,a,b)=>Math.min(b,Math.max(a,v)),lerp=(a,b,t)=>a+(b-a)*t,ease=t=>1-Math.pow(1-t,3);
let ticking=false;
function reset(){[frame,wl,wr,img].forEach(el=>el.removeAttribute('style'));hint.classList.remove('is-gone')}
function update(){
ticking=false;
if(mq.matches){reset();return}
const r=track.getBoundingClientRect(),vw=stage.clientWidth,vh=stage.clientHeight;
const p=clamp(-r.top/Math.max(1,r.height-vh),0,1),t=ease(clamp(p/.8,0,1));
const w0=vw*.38,h0=w0*.64,w=lerp(w0,vw,t),h=lerp(h0,vh,t);
frame.style.width=w+'px';frame.style.height=h+'px';frame.style.borderRadius=lerp(18,0,t)+'px';
img.style.transform='scale('+lerp(1.08,1,t)+')';
/* words start tucked just over the frame edges, then slide outward */
const gap=w0*.5-Math.min(60,vw*.04),push=t*vw*.34;
wl.style.transform='translate('+(-gap-push)+'px,-50%)';
wr.style.transform='translate('+(gap+push)+'px,-50%)';
hint.classList.toggle('is-gone',p>.2);
}
const req=()=>{if(!ticking){ticking=true;requestAnimationFrame(update)}};
addEventListener('scroll',req,{passive:true});addEventListener('resize',req);
(mq.addEventListener?mq.addEventListener('change',req):mq.addListener(req));
update();
/* video: plays muted while in view, pauses when scrolled away; manual pause sticks */
const vid=img;let userPaused=false,inView=false;
const sync=()=>{const on=!vid.paused;frame.classList.toggle('is-playing',on);
play.setAttribute('aria-label',(on?'Pause':'Play')+' the HALOGEN film')};
const tryPlay=()=>{const pr=vid.play();pr&&pr.catch(()=>{})};
vid.addEventListener('play',sync);vid.addEventListener('pause',sync);
new IntersectionObserver(es=>es.forEach(e=>{inView=e.isIntersecting;
if(inView&&!userPaused)tryPlay();else if(!inView)vid.pause()}),{threshold:.35}).observe(frame);
play.addEventListener('click',()=>{if(vid.paused){userPaused=false;tryPlay()}else{userPaused=true;vid.pause()}});
sound.addEventListener('click',()=>{vid.muted=!vid.muted;const on=!vid.muted;
sound.setAttribute('aria-pressed',String(on));sound.textContent=on?'SOUND ON':'SOUND OFF';
if(on&&vid.paused){userPaused=false;tryPlay()}});
})();
</script>
</section>
````
## `sections/08-footer.html`
````html
<footer class="ft" id="footer" aria-labelledby="ft-title">
<style>
.ft{position:relative;background:var(--violet);color:var(--paper);padding-top:clamp(64px,8vw,110px);overflow:hidden;isolation:isolate}
/* torn-paper edge: a strip of page colour with a jagged bottom */
.ft::before{content:'';position:absolute;left:0;right:0;top:-1px;height:30px;background:var(--bg);z-index:1;
clip-path:polygon(0 0,100% 0,100% 38%,97.5% 70%,95% 44%,92% 82%,89.5% 52%,86% 90%,83% 48%,80.5% 74%,77% 36%,74% 86%,71% 56%,68.5% 96%,65% 50%,62% 78%,59.5% 40%,56% 88%,53% 58%,50.5% 100%,47% 46%,44% 72%,41.5% 34%,38% 84%,35% 52%,32.5% 92%,29% 44%,26% 76%,23.5% 38%,20% 88%,17% 54%,14.5% 80%,11% 42%,8% 94%,5.5% 56%,2.5% 78%,0 46%)}
.ft-top{display:grid;grid-template-columns:minmax(0,1fr) clamp(200px,22vw,300px);gap:clamp(36px,5vw,80px);align-items:start}
.ft-main{min-width:0}
.ft-kicker{font-weight:600;color:var(--lime);margin:0 0 18px}
.ft-title{font-size:clamp(46px,7.4vw,118px);margin:0;color:var(--paper)}
.ft-title .ft-hl{color:var(--lime)}
.ft-sub{margin:18px 0 0;color:var(--violet-soft);font-weight:500}
.ft-form{margin-top:30px;display:flex;gap:14px;align-items:flex-end;flex-wrap:wrap;max-width:620px}
.ft-field{flex:1 1 260px;min-width:0;position:relative}
.ft-field label{display:block;font-size:10px;font-weight:600;color:var(--violet-soft);margin-bottom:6px}
.ft-input{width:100%;background:transparent;border:0;border-bottom:2px solid var(--paper);color:var(--paper);
font:500 14px/1.2 var(--halogen-mono);text-transform:uppercase;letter-spacing:.04em;padding:10px 0;border-radius:0;outline:none;
transition:border-color .25s var(--ease)}
.ft-input::placeholder{color:rgba(230,223,255,.62)}
.ft-input:focus{border-bottom-color:var(--lime)}
.ft-input[aria-invalid="true"]{border-bottom-color:var(--lime);border-bottom-style:dashed}
.ft-btn{background:var(--lime);color:var(--ink);padding:14px 20px;font-weight:600;font-size:12px;white-space:nowrap;
transition:transform .25s var(--ease),box-shadow .25s var(--ease)}
.ft-btn::before{content:'[ '}.ft-btn::after{content:' ]'}
.ft-btn:hover{transform:translate(-3px,-3px);box-shadow:3px 3px 0 var(--paper)}
.ft-btn:focus-visible,.ft-top-btn:focus-visible,.ft a:focus-visible{outline:2px solid var(--lime);outline-offset:4px}
.ft-msg{flex-basis:100%;margin:2px 0 0;min-height:1.5em;font-weight:600;color:var(--lime)}
.ft-form.done .ft-field,.ft-form.done .ft-btn{display:none}
.ft-form.done .ft-msg{font-family:var(--halogen-display);font-weight:400;font-size:clamp(26px,3.4vw,44px);line-height:1;letter-spacing:0;min-height:0}
.ft-links{margin-top:clamp(48px,6vw,84px);display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:34px 20px}
.ft-col h3{margin:0 0 14px;font:600 11px/1 var(--halogen-mono);color:var(--lime);letter-spacing:.06em}
.ft-col ul{list-style:none;margin:0;padding:0;display:grid;gap:9px}
.ft-link{position:relative;display:inline-block;color:var(--paper);font-weight:500;transition:color .25s var(--ease),transform .3s var(--ease)}
.ft-link::before,.ft-link::after{position:absolute;top:0;opacity:0;transition:opacity .25s var(--ease),transform .3s var(--ease)}
.ft-link::before{content:'[';left:-.9em;transform:translateX(-6px)}
.ft-link::after{content:']';right:-.9em;transform:translateX(6px)}
.ft-link:hover,.ft-link:focus-visible{color:var(--lime);transform:translateX(.9em)}
.ft-link:hover::before,.ft-link:hover::after,.ft-link:focus-visible::before,.ft-link:focus-visible::after{opacity:1;transform:none}
.ft-photo{margin:0;justify-self:end;width:100%}
.ft-frame{background:var(--paper);padding:10px 10px 12px;transform:rotate(2deg);box-shadow:0 22px 44px -24px rgba(30,21,48,.55);
transition:transform .5s var(--ease)}
.ft-frame:hover{transform:rotate(-3deg) scale(1.02)}
.ft-frame img{width:100%;aspect-ratio:4/5;object-fit:cover}
.ft-frame figcaption{margin-top:9px;display:flex;justify-content:space-between;gap:8px;color:var(--ink);font-size:10px;font-weight:600}
.ft-frame figcaption span:last-child{color:var(--violet)}
.ft-photo .tape{position:relative;z-index:2;margin:0 0 -10px 18px}
/* giant wordmark */
.ft-mark{margin-top:clamp(56px,8vw,110px);overflow:hidden;line-height:0}
.ft-word{display:flex;justify-content:space-between;font-family:var(--halogen-display);font-weight:400;color:var(--lime);
font-size:calc((100vw - 2 * var(--gutter)) / 3.62);line-height:.82;letter-spacing:0;white-space:nowrap;padding-top:.04em}
.ft-word span{display:inline-block;transform:translateY(105%);transition:transform 1s var(--ease);transition-delay:calc(var(--i) * 70ms)}
.ft-mark.in .ft-word span{transform:none}
.ft-legal{margin-top:clamp(18px,2vw,28px);border-top:1px solid rgba(255,255,255,.28);padding-block:18px 26px;
display:flex;flex-wrap:wrap;gap:10px 26px;align-items:center;font-size:10px;font-weight:500;color:var(--violet-soft)}
.ft-legal a{transition:color .25s var(--ease)}
.ft-legal a:hover{color:var(--lime)}
.ft-legal .ft-copy{margin-right:auto;color:var(--paper)}
.ft-top-btn{font-size:10px;font-weight:600;color:var(--ink);background:var(--lime);padding:7px 12px;transition:transform .25s var(--ease)}
.ft-top-btn:hover{transform:translateY(-3px)}
@media (max-width:900px){
.ft-top{grid-template-columns:minmax(0,1fr)}
.ft-photo{justify-self:start;max-width:250px}
}
@media (max-width:640px){
.ft-links{grid-template-columns:repeat(2,minmax(0,1fr));gap:30px 16px}
.ft-form{gap:18px}
.ft-btn{width:100%}
.ft-legal{gap:10px 18px}
.ft-legal .ft-copy{flex-basis:100%}
.ft-photo{max-width:210px}
}
@media (prefers-reduced-motion:reduce){
.ft-word span{transform:none;transition:none}
.ft-frame,.ft-link,.ft-btn,.ft-top-btn{transition:none}
.ft-frame:hover{transform:rotate(2deg)}
}
</style>
<div class="wrap">
<div class="ft-top">
<div class="ft-main">
<p class="ft-kicker">08. THE DROP LIST</p>
<h2 class="display ft-title" id="ft-title">GET THE DROP <span class="ft-hl">FIRST</span></h2>
<p class="ft-sub">ONE EMAIL PER DROP. NO SALE SPAM, EVER.</p>
<form class="ft-form" novalidate>
<div class="ft-field">
<label for="ft-email">YOUR EMAIL</label>
<input class="ft-input" id="ft-email" name="email" type="email" autocomplete="email" placeholder="[email protected]" required aria-describedby="ft-msg">
</div>
<button class="ft-btn" type="submit">SUBSCRIBE</button>
<p class="ft-msg" id="ft-msg" role="status" aria-live="polite"></p>
</form>
<nav class="ft-links" aria-label="Footer">
<div class="ft-col">
<h3>SHOP</h3>
<ul>
<li><a class="ft-link" href="#">SHOP ALL</a></li>
<li><a class="ft-link" href="#">NEW DROP</a></li>
<li><a class="ft-link" href="#">CATEGORIES</a></li>
<li><a class="ft-link" href="#">GIFT CARDS</a></li>
</ul>
</div>
<div class="ft-col">
<h3>HELP</h3>
<ul>
<li><a class="ft-link" href="#">SHIPPING</a></li>
<li><a class="ft-link" href="#">RETURNS</a></li>
<li><a class="ft-link" href="#">SIZE GUIDE</a></li>
<li><a class="ft-link" href="#">REPAIRS</a></li>
</ul>
</div>
<div class="ft-col">
<h3>HALOGEN</h3>
<ul>
<li><a class="ft-link" href="#">WHO WE ARE</a></li>
<li><a class="ft-link" href="#">FILM</a></li>
<li><a class="ft-link" href="#">STOCKISTS</a></li>
<li><a class="ft-link" href="#">CAREERS</a></li>
</ul>
</div>
<div class="ft-col">
<h3>FOLLOW</h3>
<ul>
<li><a class="ft-link" href="#">INSTAGRAM</a></li>
<li><a class="ft-link" href="#">TIKTOK</a></li>
<li><a class="ft-link" href="#">PINTEREST</a></li>
<li><a class="ft-link" href="#">SPOTIFY</a></li>
</ul>
</div>
</nav>
</div>
<div class="ft-photo">
<span class="tape">STUDIO NIGHT ✦ ROTTERDAM</span>
<figure class="ft-frame">
<img src="assets/foot.jpg" alt="Dancer with both arms raised against a pale studio backdrop" loading="lazy" width="600" height="750">
<figcaption><span>(FIG. 08)</span><span>CHROME / CHAOS</span></figcaption>
</figure>
</div>
</div>
</div>
<div class="wrap">
<div class="ft-mark" aria-hidden="true">
<div class="ft-word"><span style="--i:0">H</span><span style="--i:1">A</span><span style="--i:2">L</span><span style="--i:3">O</span><span style="--i:4">G</span><span style="--i:5">E</span><span style="--i:6">N</span></div>
</div>
<div class="ft-legal">
<span class="ft-copy">© 2026 HALOGEN STUDIO, ROTTERDAM</span>
<a href="#">PRIVACY</a>
<a href="#">TERMS</a>
<span>(EN) / (NL)</span>
<span>PHOTOS: PEXELS</span>
<button class="ft-top-btn" type="button">BACK TO TOP ↑</button>
</div>
</div>
<script>
(()=>{
const ft=document.querySelector('.ft');if(!ft)return;
const reduce=matchMedia('(prefers-reduced-motion: reduce)').matches;
/* newsletter: validate locally, never sent anywhere */
const form=ft.querySelector('.ft-form'),input=ft.querySelector('.ft-input'),msg=ft.querySelector('.ft-msg');
const ok=v=>/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v);
form.addEventListener('submit',e=>{
e.preventDefault();
const v=input.value.trim();
if(!ok(v)){input.setAttribute('aria-invalid','true');msg.textContent='THAT EMAIL LOOKS OFF. TRY AGAIN.';input.focus();return}
input.removeAttribute('aria-invalid');
form.classList.add('done');
msg.textContent="YOU'RE ON THE LIST ✦";
});
input.addEventListener('input',()=>{if(input.hasAttribute('aria-invalid')&&ok(input.value.trim())){input.removeAttribute('aria-invalid');msg.textContent=''}});
/* wordmark: fit exactly to the footer's inner width */
const mark=ft.querySelector('.ft-mark'),word=ft.querySelector('.ft-word');
const fit=()=>{
word.style.fontSize='100px';word.style.justifyContent='flex-start';
const w=word.scrollWidth,avail=mark.clientWidth;
word.style.justifyContent='';
if(w>0&&avail>0)word.style.fontSize=(100*avail/w*0.995).toFixed(2)+'px';
};
fit();
if(document.fonts&&document.fonts.ready)document.fonts.ready.then(fit);
if('ResizeObserver' in window)new ResizeObserver(fit).observe(mark);else addEventListener('resize',fit);
/* letters rise when the footer enters view */
if(reduce||!('IntersectionObserver' in window)){mark.classList.add('in')}
else{const io=new IntersectionObserver(es=>es.forEach(e=>{if(e.isIntersecting){mark.classList.add('in');io.disconnect()}}),{threshold:.25});io.observe(mark)}
/* back to top */
ft.querySelector('.ft-top-btn').addEventListener('click',()=>scrollTo({top:0,behavior:reduce?'auto':'smooth'}));
})();
</script>
</footer>
````
## `sections/09-motion.html`
````html
<!-- ================= 09 MOTION (GSAP + ScrollTrigger + Lenis) =================
Animates CSS variables that drive the individual translate / rotate / scale / opacity
properties, so it never fights the sections' own `transform` transitions and hovers. -->
<style>
html.lenis,html.lenis body{height:auto}
html.lenis{scroll-behavior:auto!important}
.lenis.lenis-stopped{overflow:hidden}
.m-c,[data-m]{translate:var(--mx,0) var(--my,0);rotate:var(--mr,0deg);opacity:var(--mo,1)}
.m-c{display:inline-block;will-change:translate,opacity}
.m-w{display:inline-block;white-space:nowrap}
[data-m-img]{scale:var(--ms,1)}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/lenis.min.js"></script>
<script>
(()=>{
if(!window.gsap||!window.ScrollTrigger)return;
if(matchMedia('(prefers-reduced-motion: reduce)').matches)return;
gsap.registerPlugin(ScrollTrigger);
const $=(s,r=document)=>r.querySelector(s), $$=(s,r=document)=>[...r.querySelectorAll(s)];
const EASE='expo.out';
/* ---------- helpers ---------- */
// take an element away from the CSS data-reveal system (GSAP owns it now)
const own=els=>els.forEach(el=>{el.removeAttribute('data-reveal');el.style.transitionDelay=''});
// split text nodes into per-character spans, keeping child elements (.hl, <br>, ...)
const split=el=>{
if(!el)return[];
if(el._chars)return el._chars;
el.setAttribute('aria-label',el.textContent.replace(/\s+/g,' ').trim());
const chars=[];
const walk=node=>[...node.childNodes].forEach(n=>{
if(n.nodeType===3){
const frag=document.createDocumentFragment();
n.textContent.split(/(\s+)/).forEach(part=>{
if(!part)return;
if(/^\s+$/.test(part)){frag.appendChild(document.createTextNode(part));return}
const w=document.createElement('span');w.className='m-w';w.setAttribute('aria-hidden','true');
[...part].forEach(ch=>{const c=document.createElement('span');c.className='m-c';c.textContent=ch;w.appendChild(c);chars.push(c)});
frag.appendChild(w);
});
n.replaceWith(frag);
}else if(n.nodeType===1&&n.tagName!=='BR')walk(n);
});
walk(el);
return el._chars=chars;
};
// tween FROM the given var state to rest; clean up afterwards so hovers etc. own the element again
const clean=els=>els.forEach(el=>{el.removeAttribute('data-m');['--mx','--my','--mr','--mo'].forEach(p=>el.style.removeProperty(p))});
const from=(els,vars,opt={})=>{
els=(Array.isArray(els)?els:[els]).filter(Boolean);
if(!els.length)return null;
els.forEach(el=>{if(!el.classList.contains('m-c'))el.setAttribute('data-m','')});
const to={'--mx':'0px','--my':'0px','--mr':'0deg','--mo':1};
const fromV={'--mx':'0px','--my':'0px','--mr':'0deg','--mo':1,...vars};
return gsap.fromTo(els,fromV,{...to,duration:1.1,ease:EASE,...opt,
onComplete(){clean(els.filter(e=>!e.classList.contains('m-c')));opt.onComplete&&opt.onComplete()}});
};
const onEnter=(trigger,fn,start='top 80%')=>trigger&&ScrollTrigger.create({trigger,start,once:true,onEnter:fn});
/* ---------- Lenis smooth scroll ---------- */
let lenis=null;
if(window.Lenis){
lenis=new Lenis({duration:1.15,smoothWheel:true});
lenis.on('scroll',ScrollTrigger.update);
gsap.ticker.add(t=>lenis.raf(t*1000));
gsap.ticker.lagSmoothing(0);
// in-page anchors glide instead of jumping
document.addEventListener('click',e=>{
const a=e.target.closest('a[href^="#"]');if(!a||e.defaultPrevented)return;
const id=a.getAttribute('href');const t=id.length>1&&document.querySelector(id);
if(id!=='#top'&&!t)return;
e.preventDefault();lenis.scrollTo(t||0,{offset:t?-56:0,duration:1.4});
});
// film modal locks the page: pause Lenis with it
new MutationObserver(()=>document.documentElement.classList.contains('film-lock')?lenis.stop():lenis.start())
.observe(document.documentElement,{attributes:true,attributeFilter:['class']});
const top=$('.ft-top-btn');top&&top.addEventListener('click',e=>{e.stopImmediatePropagation();lenis.scrollTo(0,{duration:1.8})},true);
}
/* ---------- section labels: scramble in ---------- */
const GLYPHS='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/#_+';
$$('.sec-num').forEach(el=>{
if(el.children.length)return;
const txt=el.textContent;
onEnter(el,()=>{
const o={p:0};
gsap.to(o,{p:1,duration:.9,ease:'none',onUpdate(){
const n=Math.floor(o.p*txt.length);
el.textContent=txt.slice(0,n)+[...txt.slice(n)].map(c=>c===' '?' ':GLYPHS[Math.random()*GLYPHS.length|0]).join('');
},onComplete(){el.textContent=txt}});
},'top 88%');
});
/* ---------- 01 HERO intro (waits for the loader) ---------- */
const hero=$('.hero');
if(hero){
const tl=gsap.timeline({paused:true});
const solid=split($('.hero-word:not(.hero-word--stroke) .hero-word-in',hero));
const stroke=split($('.hero-word--stroke .hero-word-in',hero));
const photo=$('.hero-photo',hero);
const charFrom={'--my':'110%','--mr':'9deg','--mo':0};
tl.add(from($('.hero-nav',hero),{'--my':'-100%','--mo':0},{duration:1}),0)
.add(from(solid,charFrom,{stagger:.06,duration:1.3}),.05)
.add(from(stroke,{...charFrom,'--my':'140%'},{stagger:.06,duration:1.5}),.15)
.fromTo(photo,{clipPath:'inset(100% 0% 0% 0%)'},{clipPath:'inset(0% 0% 0% 0%)',duration:1.3,ease:'expo.inOut',clearProps:'clipPath'},.25)
.add(from($('.tape',photo),{'--mx':'-40px','--mo':0,'--mr':'-12deg'},{duration:.8}),1.1)
.add(from($$('.hero-meta > span, .hero-left > *, .hero-links > *, .hero-scroll',hero),{'--my':'26px','--mo':0},{stagger:.07,duration:1}),.7);
const loader=$('.hero-loader',hero);
let played=false;const play=()=>{if(!played){played=true;tl.play()}};
if(!loader||loader.classList.contains('is-gone')||getComputedStyle(loader).display==='none')play();
else{
new MutationObserver((m,obs)=>{if(loader.classList.contains('is-done')){obs.disconnect();setTimeout(play,150)}})
.observe(loader,{attributes:true,attributeFilter:['class']});
setTimeout(play,3200);
}
// marquee: scroll velocity speeds the tickers up and leans the type
const anims=$$('.hero-track',hero).flatMap(t=>t.getAnimations?t.getAnimations():[]);
const spans=$$('.hero-track span',hero);
const skew=gsap.quickTo(spans,'skewX',{duration:.5,ease:'power3.out'});
const boost={v:1};const apply=()=>anims.forEach(a=>a.playbackRate=boost.v);
ScrollTrigger.create({trigger:$('.hero-bands',hero),start:'top bottom',end:'bottom top',onUpdate(self){
const vel=self.getVelocity();
skew(gsap.utils.clamp(-14,14,vel/-180));
const v=Math.min(7,1+Math.abs(vel)/260);
if(v>boost.v+.2)gsap.to(boost,{v,duration:.25,overwrite:true,onUpdate:apply,
onComplete:()=>gsap.to(boost,{v:1,duration:1.4,ease:'power2.out',onUpdate:apply})});
}});
ScrollTrigger.addEventListener('scrollEnd',()=>skew(0));
}
/* ---------- 02 NEW ARRIVALS ---------- */
const arr=$('#arrivals');
if(arr){
const main=$('.arr-main',arr);own([main]);
const cards=$$('.arr-card',arr),imgs=$$('.arr-media img',arr);
imgs.forEach(i=>i.setAttribute('data-m-img',''));
gsap.set(imgs,{'--ms':1.35});
from(split($('.arr-title',arr)),{'--my':'100%','--mr':'6deg','--mo':0},{stagger:.035,scrollTrigger:{trigger:arr,start:'top 75%'}});
const arail=$('.arr-rail',arr);
onEnter(main,()=>{
// translated cards would shift the snap points mid-entrance: hold snapping until they land
arail.style.scrollSnapType='none';
from($$('.arr-head > *',arr),{'--my':'20px','--mo':0},{stagger:.08,duration:.9});
from(cards,{'--mx':'180px','--mr':'5deg','--mo':0},{stagger:.09,duration:1.4,onComplete:()=>{arail.style.scrollSnapType=''}});
gsap.to(imgs,{'--ms':1,duration:1.8,ease:EASE,stagger:.09,delay:.1});
},'top 78%');
}
/* ---------- 04 CATEGORIES: pinned horizontal run + photo parallax ---------- */
const cat=$('#categories');
if(cat){
const rail=$('.cat-rail',cat),cards=$$('.cat-card',cat);own(cards);
const names=$$('.cat-name',cat),photos=$$('.cat-photo',cat),pimgs=$$('.cat-photo img',cat);
from($$('.cat-head > *',cat).slice(1),{'--my':'18px','--mo':0},{stagger:.08,scrollTrigger:{trigger:cat,start:'top 80%'}});
onEnter(cat,()=>{
from(cards,{'--my':'160px','--mr':'-5deg','--mo':0},{stagger:.08,duration:1.3});
gsap.fromTo(photos,{clipPath:'inset(100% 0% 0% 0%)'},{clipPath:'inset(0% 0% 0% 0%)',duration:1.3,ease:'expo.inOut',stagger:.08,delay:.25,clearProps:'clipPath'});
gsap.fromTo(names,{clipPath:'inset(0% 0% 100% 0%)'},{clipPath:'inset(0% 0% 0% 0%)',duration:1.2,ease:'expo.inOut',stagger:.08,delay:.45,clearProps:'clipPath'});
},'top 72%');
// photos drift inside their frames as the row moves (drag, wheel or pin)
pimgs.forEach(i=>{i.style.scale='1.22'});
const drift=()=>{const rr=rail.getBoundingClientRect(),cx=rr.left+rr.width/2;
pimgs.forEach(i=>{const r=i.parentElement.getBoundingClientRect();i.style.translate=(((r.left+r.width/2)-cx)/rr.width*-34).toFixed(1)+'px 0'})};
rail.addEventListener('scroll',()=>requestAnimationFrame(drift),{passive:true});drift();
// desktop: pin the section and turn vertical scroll into a horizontal run through the rail
const mm=gsap.matchMedia();
mm.add('(min-width: 900px)',()=>{
rail.style.scrollSnapType='none';
const max=()=>Math.max(0,rail.scrollWidth-rail.clientWidth);
if(max()<40)return;
const lean=gsap.quickTo(rail,'skewX',{duration:.6,ease:'power3.out'});
const tw=gsap.fromTo(rail,{scrollLeft:0},{scrollLeft:max,ease:'none',scrollTrigger:{
trigger:cat,start:'top top',end:()=>'+='+max(),pin:true,scrub:.7,invalidateOnRefresh:true,anticipatePin:1,
onUpdate:s=>lean(gsap.utils.clamp(-6,6,s.getVelocity()/-350)),onLeave:()=>lean(0),onLeaveBack:()=>lean(0)}});
ScrollTrigger.addEventListener('scrollEnd',()=>lean(0));
return()=>{rail.style.scrollSnapType='';tw.scrollTrigger&&tw.scrollTrigger.kill();tw.kill();gsap.set(rail,{clearProps:'transform'})};
});
}
/* ---------- 03 CAMPAIGN ---------- */
const camp=$('.camp');
if(camp){
onEnter(camp,()=>{
from(split($('.camp-new',camp)),{'--my':'110%','--mr':'12deg','--mo':0},{stagger:.1,duration:1.4});
from($$('.camp-tag, .camp-copy, .camp-foot > *, .camp-chip',camp),{'--my':'28px','--mo':0},{stagger:.08,delay:.3});
},'top 55%');
}
/* ---------- 05 COLLECTIONS ---------- */
const col=$('#collections');
if(col){
const panels=$$('.col-panel',col);own(panels);
const cimgs=$$('.col-img',col);cimgs.forEach(i=>i.setAttribute('data-m-img',''));
onEnter($('.col-grid',col)||col,()=>{
gsap.fromTo(panels,{clipPath:'inset(100% 0% 0% 0%)'},{clipPath:'inset(0% 0% 0% 0%)',duration:1.5,ease:'expo.inOut',stagger:.18,clearProps:'clipPath'});
panels.forEach((p,i)=>{
from(split($('.col-title',p)),{'--my':'110%','--mr':'8deg','--mo':0},{stagger:.05,duration:1.3,delay:.7+i*.18});
from($$('.col-intro, .col-foot > *',p),{'--my':'24px','--mo':0},{stagger:.08,delay:.9+i*.18});
});
},'top 78%');
cimgs.forEach(i=>gsap.fromTo(i,{'--ms':1.3},{'--ms':1,ease:'none',scrollTrigger:{trigger:i.closest('.col-panel'),start:'top bottom',end:'bottom 40%',scrub:true}}));
}
/* ---------- 06 MANIFESTO ---------- */
const man=$('#about');
if(man){
const cardsM=$$('.man-card',man),aside=$('.man-aside',man);own([...cardsM,aside]);
onEnter($('.man-cards',man),()=>from(cardsM,{'--my':'110px','--mr':'4deg','--mo':0},{stagger:.12,duration:1.3}),'top 82%');
onEnter(aside,()=>{
from($('.man-frame',man),{'--my':'90px','--mr':'-14deg','--mo':0},{duration:1.5});
from($$('.man-aside > :not(.man-frame)',man),{'--my':'20px','--mo':0},{stagger:.08,delay:.4});
},'top 82%');
onEnter($('.man-counters',man),()=>from($$('.man-counters > *',man),{'--my':'50px','--mo':0},{stagger:.1}),'top 88%');
}
/* ---------- 07 FILM: play button breathes with the scroll ---------- */
const play=$('.film-play');
if(play){
play.setAttribute('data-m-img','');
gsap.fromTo(play,{'--ms':.4},{'--ms':1,ease:'back.out(2)',scrollTrigger:{trigger:'#film',start:'top 70%',end:'top 10%',scrub:.5}});
}
/* ---------- 08 FOOTER ---------- */
const ft=$('.ft');
if(ft){
onEnter(ft,()=>{
from($('.ft-kicker',ft),{'--mx':'-30px','--mo':0});
from(split($('.ft-title',ft)),{'--my':'110%','--mr':'8deg','--mo':0},{stagger:.035,duration:1.2});
from($$('.ft-sub, .ft-form',ft),{'--my':'24px','--mo':0},{stagger:.1,delay:.35});
from($('.ft-frame',ft),{'--my':'120px','--mr':'14deg','--mo':0},{duration:1.6,delay:.2});
},'top 75%');
onEnter($('.ft-links',ft),()=>from($$('.ft-col',ft),{'--my':'40px','--mo':0},{stagger:.08}),'top 90%');
}
/* layout shifts (fonts, wordmark fit, images) -> recompute trigger positions */
const refresh=()=>ScrollTrigger.refresh();
document.fonts&&document.fonts.ready.then(refresh);
addEventListener('load',refresh);
})();
</script>
````
## `template.html`
````html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HALOGEN — Where Chrome Meets Chaos</title>
<meta name="description" content="HALOGEN is an independent womenswear label: sharp tailoring, sheer mesh and loud metallics, cut in small runs.">
<style>
@font-face{font-family:'Anton';font-style:normal;font-weight:400;font-display:swap;src:url('assets/anton-400.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url('assets/ibm-plex-mono-400.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url('assets/ibm-plex-mono-500.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:600;font-display:swap;src:url('assets/ibm-plex-mono-600.woff2') format('woff2')}
/* ---------- HALOGEN locked palette ---------- */
:root{
--bg:#F4F2FA; /* lilac-white page */
--paper:#FFFFFF; /* cards */
--ink:#1E1530; /* violet ink: text only, never a background */
--violet:#5B2EFF; /* primary */
--violet-soft:#E6DFFF;/* primary tint */
--lime:#D4FF3A; /* accent */
--mist:#8C86A0; /* secondary text */
--line:#DCD7EA; /* borders */
--shadow:0 18px 40px -22px rgba(91,46,255,.35);
--halogen-display:'Anton', 'Impact', sans-serif;
--halogen-mono:'IBM Plex Mono', ui-monospace, monospace;
--gutter:clamp(16px, 2.4vw, 36px);
--ease:cubic-bezier(.2,.7,.1,1);
}
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--halogen-mono);font-size:12px;line-height:1.5;
text-transform:uppercase;letter-spacing:.02em;overflow-x:hidden;-webkit-font-smoothing:antialiased}
img{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;text-transform:inherit;letter-spacing:inherit}
/* ---------- shared atoms (use these in every section) ---------- */
.wrap{padding-inline:var(--gutter)}
.sec{padding-block:clamp(72px, 10vw, 150px)}
.sec-num{font-family:var(--halogen-mono);font-weight:600;color:var(--violet);font-size:12px} /* "02. NEW ARRIVALS" */
.display{font-family:var(--halogen-display);font-weight:400;line-height:.86;letter-spacing:-.005em;text-transform:uppercase}
.paren{color:var(--mist)} /* "(BERLIN)" style labels */
.hl{color:var(--violet)} /* violet highlight inside running text */
.brk{display:inline-flex;gap:.5em;align-items:center;font-weight:600;white-space:nowrap;transition:color .25s var(--ease)}
.brk::before{content:'['}.brk::after{content:']'}
.brk:hover{color:var(--violet)}
.tape{display:inline-block;background:var(--lime);color:var(--ink);font-weight:600;font-size:10px;padding:3px 10px;transform:rotate(-3deg);
clip-path:polygon(2% 8%,100% 0,97% 50%,100% 100%,0 94%,3% 50%)}
.rail{display:flex;gap:14px;overflow-x:auto;scrollbar-width:none;cursor:grab;scroll-snap-type:x proximity;padding-inline:var(--gutter);overscroll-behavior-x:contain}
.rail::-webkit-scrollbar{display:none}
.rail.dragging{cursor:grabbing;scroll-snap-type:none}
.rail.dragging *{pointer-events:none}
.drag-hint{font-weight:600}.drag-hint b{color:var(--violet);font-weight:600}
/* reveal on scroll */
[data-reveal]{opacity:0;transform:translateY(28px);transition:opacity .9s var(--ease),transform .9s var(--ease)}
[data-reveal].in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
[data-reveal]{opacity:1;transform:none;transition:none}
*{animation-duration:.001ms!important;animation-iteration-count:1!important}
}
</style>
</head>
<body>
<!-- @include sections/01-hero.html -->
<main>
<!-- @include sections/02-arrivals.html -->
<!-- @include sections/03-campaign.html -->
<!-- @include sections/04-categories.html -->
<!-- @include sections/05-collections.html -->
<!-- @include sections/06-manifesto.html -->
<!-- @include sections/07-film.html -->
</main>
<!-- @include sections/08-footer.html -->
<script>
/* shared: reveal-on-scroll */
(()=>{const io=new IntersectionObserver(es=>es.forEach(e=>{if(e.isIntersecting){e.target.classList.add('in');io.unobserve(e.target)}}),{threshold:.12,rootMargin:'0px 0px -6% 0px'});
document.querySelectorAll('[data-reveal]').forEach(el=>io.observe(el));})();
/* shared: drag-to-scroll for every .rail */
document.querySelectorAll('.rail').forEach(r=>{let down=false,moved=0,x0=0,s0=0;
r.addEventListener('pointerdown',e=>{if(e.pointerType!=='mouse')return;down=true;moved=0;x0=e.clientX;s0=r.scrollLeft});
addEventListener('pointermove',e=>{if(!down)return;const dx=e.clientX-x0;moved=Math.max(moved,Math.abs(dx));if(moved>4)r.classList.add('dragging');r.scrollLeft=s0-dx});
addEventListener('pointerup',()=>{down=false;requestAnimationFrame(()=>r.classList.remove('dragging'))});
r.addEventListener('click',e=>{if(moved>4){e.preventDefault();e.stopPropagation()}},true);
});
</script>
<!-- @include sections/09-motion.html -->
</body>
</html>
````