# Site update v5 — hero centered, real pin-scroll, card illustrations, bifrost headline breathing room Four changes in this round. v4's wheelMultiplier sticky approach is replaced with a proper ScrollTrigger pin implementation because the previous attempts didn't feel sticky enough. ## Files to replace | File | What changed | |---|---| | `protected/index.html` | Hero centered vertically (align-items + padding reset); AI + Agents cards get custom mask-image illustrations; Project Bifrost headline padding + looser line-height | | `protected/bifrost.js` | wheelMultiplier approach removed; ScrollTrigger pins added on hero, each map stop, and #bifrost-join; map path rebuilds on ScrollTrigger refresh | | `protected/timeline.js` | **No change** from v4 — copy only if you want fresh copy alongside | ## New files to upload | File | Destination on server | |---|---| | `ai.png` | `/opt/fenja/protected/fenja/illustrations/ai.png` | | `agents.png` | `/opt/fenja/protected/fenja/illustrations/agents.png` | If you're using rsync with `--delete`, ensure the local `protected/fenja/illustrations/` folder contains both PNGs so they end up on the VPS. Otherwise the AI and Agents cards will show blank (the mask-image URL points to 404). ## Change-by-change ### 1. Hero vertically centered - `#hero` is now `align-items: center` (was `start`) with `padding-top: 0`. - `#page-overview #hero .hero-wrap` also has `padding-top: 0` — parent centering now handles vertical position. - The whole block (eyebrow + title + lede + hero-foot row) reads as centered on viewport load. No block-level math needed — CSS grid does it. ### 2. Real scroll pinning via GSAP ScrollTrigger v4's `wheelMultiplier` trick (reducing Lenis wheel input to 0.35× inside a "sticky zone") didn't produce the tactile "scroll a few times to pass" feel you wanted. **Replaced entirely** with proper GSAP ScrollTrigger pins. **How it works:** ```js ScrollTrigger.create({ trigger: el, start: 'center center', end: '+=300', // 300px of extra scroll distance held pin: true, pinSpacing: true, // document grows by 300px — user MUST scroll through anticipatePin: 1, }); ``` **Pinned targets + their hold distances:** | Target | Hold | Why | |---|---|---| | `#hero` | +300px | Gentle hold on entry | | `#bifrost-meaning .map-stop--intro` | +260px | Short — it's just an intro card | | `#bifrost-meaning .map-stop[data-stop="1"]` (Community) | +360px | Full artifact card — noticeable hold | | `#bifrost-meaning .map-stop[data-stop="2"]` (Advisory Council) | +360px | Same | | `#bifrost-meaning .map-stop[data-stop="3"]` (Pilot Projects) | +360px | Same | | `#bifrost-join` | +260px | CTA gets a moment before footer | **Deliberately NOT pinned** (they already have their own pin/scrub mechanics — double-pinning would fight): - `#stack-scene` (S2 architecture — GSAP scrub pin) - `#words-scene` (S3 — position:sticky pin inside tall parent) - `#bifrost` (S4 — position:sticky pin on `.bifrost-pin`) **Why pinSpacing:true matters:** `pinSpacing: true` injects a spacer div that grows the document by the pin hold distance. Without it, the pin is visually held but the scroll counter advances normally — no "scroll multiple times" feeling. With it, every pin zone adds real scroll distance, so the user genuinely has to keep scrolling. **The map path rebuild:** `pinSpacing: true` pushes each subsequent map stop further down the document. The winding SVG path that connects the dots is computed from live DOM positions via `buildMapPath()`. To keep the path threading correctly through the shifted dots, `buildMapPath` now re-runs on every `ScrollTrigger.refresh` event. No manual intervention needed. ### 3. Card illustrations — AI + Agents Two of the four architecture cards now carry bespoke illustrations: - **The AI** (data-layer="0") → `/fenja/illustrations/ai.png` — the single topographic orb - **The Agents** (data-layer="3") → `/fenja/illustrations/agents.png` — central orb with six connected smaller orbs **How the rendering works:** The PNGs are white line-art on black backgrounds. They're applied via CSS `mask-image` on the existing `.card-brain` element. The black background becomes transparent (mask excludes black) and the white lines paint through in the card's `paper` fill color. Because it's a mask, the lines render as a single flat color on every card background — matches the existing design system. **Per-card overrides** (layers 1 and 2 keep the default brain mask): ```css .layer-card[data-layer="0"] .card-brain { mask-image: url('/fenja/illustrations/ai.png'); aspect-ratio: 1 / 1; mask-size: contain; mask-position: center; } .layer-card[data-layer="3"] .card-brain { mask-image: url('/fenja/illustrations/agents.png'); aspect-ratio: 1 / 1; mask-size: contain; mask-position: center; } ``` The PNGs **must be deployed** to `/opt/fenja/protected/fenja/illustrations/` or the cards will show blank. See "New files to upload" above. ### 4. Project Bifrost headline — room to breathe `.bifrost-pin` has `overflow: hidden` (it clips the aurora arc's off-screen portions). Combined with `.bifrost-name`'s tight `line-height: 0.95` and the italic "Bifrost" token's gradient `background-clip: text`, the top and bottom edges of the headline were getting clipped. **Fixes, both on `.bifrost-name`:** - `line-height: 0.95` → `1.12` — room for ascenders and descenders - Added `padding: 0.12em 0.08em` — generous vertical buffer absorbed into the headline's own box, so the parent `.bifrost-pin` overflow:hidden doesn't reach into the glyphs Parent overflow:hidden stays (it's protecting the arc animation). The headline simply carries its own breathing room now. ## Spot-check after deploy - [ ] Overview → hero text sits vertically centered on viewport load. Visually balanced — not clinging to top or bottom. - [ ] Scroll past hero → real resistance. After a bit of wheel input, hero unpins and S2 comes in. NOT a smooth flyby. - [ ] Scroll into treasure map. **Each stop visibly holds** as it reaches viewport center. You need several wheel flicks to pass through Community, then Advisory Council, then Pilot Projects. - [ ] The winding SVG path still threads through each dot correctly — the path has rebuilt itself to account for the new document height from pin spacing. - [ ] AI card shows a single textured orb illustration (white line-art, paper color on the card's color). - [ ] Agents card shows the network-of-orbs illustration. - [ ] Knowledge and Tools cards still show the default brain illustration. - [ ] S4 Bifrost reveal: "Project Bifrost" headline visible top to bottom — no clipping on italic "Bifrost" tall letters. ## Tuning knobs If the pin holds feel too aggressive: | Target | In `bifrost.js` (`stickyPinSpecs` array) | Change | |---|---|---| | Less hero hold | `{ sel: '#hero', hold: 300 }` | → `150` | | Less treasure map hold | `hold: 360` on `data-stop` entries | → `200` | | Stronger hold | any `hold: N` | → higher N | If a single pin feels wrong and you want to kill it without breaking others, just delete that entry from `stickyPinSpecs`. If the pins break the map path threading (it's rebuilt on refresh but if something's off): open DevTools, scroll through, and verify `buildMapPath` is firing via the `ScrollTrigger.addEventListener('refresh', buildMapPath)` hook. The path uses live DOM positions so it should always match. ## Things NOT touched - `public/entrance.html` / `entrance.js` — unchanged. - Any of `src/` (auth, db, sessions) — unchanged. - S2 architecture scene (scrub pin intact), S3 words fly-in (sticky pin intact), S4 aurora arc (sticky pin intact). - The topography parallax behind Europe map (from v3) — still present, still working. - The Welcome dot, 7-section nav, hero-foot restructure — all still in place. ## Deploy steps ```powershell # Local — copy the new files in Copy-Item -Force site-update-v5\protected\index.html protected\index.html Copy-Item -Force site-update-v5\protected\timeline.js protected\timeline.js Copy-Item -Force site-update-v5\protected\bifrost.js protected\bifrost.js # Create the illustrations folder if it doesn't exist and copy PNGs New-Item -ItemType Directory -Force -Path protected\fenja\illustrations Copy-Item -Force site-update-v5\protected\fenja\illustrations\ai.png protected\fenja\illustrations\ Copy-Item -Force site-update-v5\protected\fenja\illustrations\agents.png protected\fenja\illustrations\ # Test locally npm run dev # Verify all four changes work before pushing # Commit + deploy git add protected/ git commit -m "Hero centered, real ScrollTrigger pins, AI+Agents card illustrations, Bifrost headline breathing room" # rsync + VPS steps same as before — rsync picks up the new PNGs automatically ``` On the VPS after rsync, verify the PNGs landed: ```bash ls -la /opt/fenja/protected/fenja/illustrations/ # Should show: ai.png, agents.png, and the three existing stop illustrations ``` Then `sudo systemctl restart fenja` and spot-check.