The GSAP tier was a net negative: entrance triggers were created before the
pins and without refreshPriority, so ScrollTrigger measured them before each
pin inserted its spacer and every scene's animation completed below the fold.
Film mode also collapsed the two-column editorial layout, because
`html.film-ready .film-scene{display:flex}` outranks `.section-grid`, and both
pins held scroll without playing anything.
Engine:
- Create pins first, with refreshPriority, so reveals measure against the real
document height.
- Replace the per-scene scrub with per-beat, non-reversing batches. Fixes tall
scenes a single trigger can never time, and stops beats — including the
closing CTAs — from sitting at opacity 0 while still in the tab order.
- Give both pins real work: the cold open scrubs a camera push-in and
dissolves its copy; the closing pin scrubs a decorative vignette only, never
content opacity.
- Slow the honesty scene deliberately, and restore the longer landing the
closing beat is meant to get.
- Route in-page links through Lenis so nav travel shares the film's easing.
Layout and craft:
- Stop clobbering `display`; grid scenes keep their columns in every tier.
- Request Fraunces as a weight range — 550 and 600 were both snapping to 650,
so h1, h2 and h3 rendered identically.
- Collapse the mobile header from three rows to one (254px to 73px) and drive
scroll-margin-top from the measured height, so anchor jumps stop landing
headings behind the bar.
- Un-hide the page if no motion engine claims it within 1200ms, so a slow CDN
no longer means a wordless first paint.
- Full-bleed header, per-tween will-change, no backdrop blur on phones, cached
scroll extent, preloaded opening frame.
Story:
- Three shapes for three registers in the sagging middle: a numbered package,
an unboxed vow, and a climax set quieter than anything around it.
- An end card after the appendix, carrying the freshness stamp out of the
pinned closing scene and repeating the one apply door at the true bottom.
- Dissolve the hero into the page instead of cutting to ivory.
Vietnamese copy is moved, not reworded; the end card's single new word is
marked for the owner. Verified at 1440x900, 390x844 and 320x640, both themes,
reduced motion, CDN blocked and engine blocked.
Audit of the scroll film as visual storytelling, measured in Chromium across
viewports, themes and motion tiers. Stack research covering GSAP, Lenis, native
CSS scroll-driven animations and the lighter alternatives, with the Vietnamese
diacritic constraints on any text-splitting technique.
- closing scene: Facebook + Instagram connect buttons (placeholder handles)
- Canh 2: portrait slot with placeholder and real-photo swap-in comment
- Canh 2: playlist link on the music claim (placeholder URL)
- closing: runtime freshness stamp from the GitHub commit date; hidden if the fetch fails
- theme: follow the OS light/dark on first visit, stored choice still wins, persist only on toggle
- commented scaffolds for owner-written vulnerability and concrete-texture beats
- retune light/dark themes to warm ivory and candlelit palettes
- add GPU-cheap grain, vignette, and drifting candlelight atmosphere
- soften typography, buttons, and panels toward an editorial feel
- drop fixed-background, blend-mode, and blur repaints that caused scroll jank
- add styling for the portrait slot, inline claim links, and freshness stamp
feat: turn dating packet into scroll-driven short film
Rearrange the JD/CV content into a 7-scene narrative arc that opens on the
person, builds through the offer, and closes on sincerity, with the full
facts kept as a dossier appendix. Layer a GSAP + ScrollTrigger + Lenis film
engine that scrubs each scene into view and pins the opening and closing
title cards.
Keep the progressive-enhancement contract: the film only activates on wide
viewports with motion allowed and the CDN present; otherwise the existing
static reveal runs. Scope the reveal styles so the two engines never animate
the same content, and fail open so content is never trapped hidden.
@
Pace the single-page packet as a five-chapter narrative: sections fade
and rise into view on scroll with staggered children, the hero cascades
on load, and the closing beat reveals slower to let it settle. Add light
'Chuong N' chapter tags above each major section eyebrow, a top
scroll-progress bar, and scroll-linked active nav highlighting.
All motion is progressive enhancement gated behind html.js-ready plus
IntersectionObserver support and neutralized under prefers-reduced-motion,
so no-JS and reduced-motion visitors always get the full, un-jumped
content. New behavior lives in assets/story.{css,js} (reveal engine) and
assets/story-chrome.{css,js} (chapter tags, progress bar, active nav);
styles.css and theme-switch.js are untouched.
- hardcode vietnamese: replace i18n stores with plain site-copy module, keep theme store only
- remove language switcher, locale bootstrap, and unused locale styles
- fix jd section heading mismatched with its eyebrow and content
- add og:title/og:description per page, og:locale, twitter:image:alt
- fix light-theme eyebrow AA contrast; raise heading line-height for stacked diacritics
- expose summary group labels to assistive tech; use definition list for cv facts
- load fonts via preconnected link tags instead of css import
- consolidate base path to BASE_PATH only; add lint step to deploy workflow
- delete unused audio, placeholder, and hero image assets, .agents dir, skills-lock.json
- rewrite lib, routes, and vite config as js with jsdoc type annotations
- replace tsconfig with jsconfig and point svelte-check at it
- drop typescript-eslint from eslint config and dev dependencies
- resolve nav hrefs in template to satisfy svelte navigation lint rule
- add gitattributes forcing lf so prettier and autocrlf stop conflicting