Commit Graph
31 Commits
Author SHA1 Message Date
tiennm99 ff6dc3b39e feat: the room gets later as she reads
One continuous transformation across all nine scenes, so the page reads as a
single evening instead of nine separate slides. A fixed backdrop layer travels
from early evening toward past midnight as she scrolls: the corners go to plum,
the lamp keeps its warmth, and dust turns over in the beam.

It starts from the hour on her own device, so opening it at 1am begins deep in
the night and a friend shown it the next afternoon sees a different room.
Nothing leaves the device and there is no backend — it reads her clock.

Deliberate boundaries:
- Atmosphere only. The palette tokens are never interpolated, so an explicit
  light or dark choice always wins; both themes simply travel within themselves.
- The scrubbed value is written to one element, not to :root. On :root it would
  invalidate everything inheriting it on every frame.
- Dust is a masked, tiled dot pattern on one composited layer rather than N
  particle elements — many things moving, one transform.
- Defaults to a lit room in CSS, so the no-JS tier gets a deliberate composition
  rather than an unlit one, and reduced motion freezes the evening at about 22:00
  with the air still.

Verified at 1440x900 and 390x844, both themes, and reduced motion: the value
travels with scroll, the theme choice survives, no horizontal overflow, no hidden
beats, no console output.

This is the light system the remaining objects will be lit by; it is groundwork
as much as it is a scene.
2026-07-25 14:32:47 +07:00
tiennm99 046151403a docs: add wow-factor framework research and object-concept direction 2026-07-25 14:32:47 +07:00
tiennm99 53e7c14d34 feat: give each scene its own motion, and let her pick the photograph up
Every one of the 35 beats used the same fade-and-rise, so the page moved
constantly and expressed nothing. Motion is now per-scene and physical: this is
a letter on a desk, so beats behave like paper on a surface.

- Per-scene entrance recipes replace one global timing. A narrow vocabulary —
  drop, slide, land fractionally off-square, rack focus — with no scene using
  more than two. #honest and #shared are now the stillest scenes on the page and
  #home the most unhurried, so tempo carries meaning instead of decorating it.
- Scene 2 set-piece: the portrait starts lying on the desk and hinges up on its
  own bottom edge as she scrolls, through 68 degrees rather than 180. It never
  passes the plane, so there is no back face and no two-sided markup — the
  mechanical limit is what makes it an object obeying gravity instead of a card
  transition. Scrubbed, so she raises it with her thumb, and it locks upright on
  leave rather than lying back down.

The film now runs on phones. It required 768px, which meant the device this link
is most often opened on got none of the choreography. Phone variants ship with
it: shorter pins, a numeric scrub so momentum tracks the thumb instead of
stepping, the focus pull swapped from filter to a compositor-only scale, and no
horizontal travel where the gutter is 16px.

Fixes a real defect found while specifying this: GSAP leaves a residual inline
`transform: translate(0px, 0px)` on every beat, and an inline transform outranks
any stylesheet rule, so `.panel:hover` had been silently dead for the whole
session. Beats now hand themselves back to CSS on completion.

Setup failures also stop being silent. The catch still reveals all content so a
visitor loses nothing, but it now reports — that silence had been hiding a
broken timeline during this very change.

Verified at 1440x900 and 390x844, both themes, plus reduced motion: portrait
lifts and stays up across a full down-and-back-up pass, zero hidden beats, zero
invisible-but-focusable controls, zero horizontal overflow, no console output.
Page is ~24% longer on a phone, the cost of keeping the pins there.
2026-07-25 13:11:09 +07:00
tiennm99 1ba2ddd6a9 docs: add cinematic motion spec, technique library and motion-lab findings 2026-07-25 13:11:09 +07:00
tiennm99 6d0a21eaf7 refactor: drop the job-description conceit for a plainly romantic register
The page was a mock job posting: applicants, positions, benefits,
responsibilities, a spec table and selection notes. That framing carried real
wit, but it kept the warmest scenes at arm's length behind HR vocabulary.

Every sentence carrying a personal fact is untouched — the dev job, Genshin and
T1 Keria, the light novels, the three-year relationship, 162cm, the playlist,
the whole appendix. Only the framing around them changed:

- Retitled throughout: page title, OG and Twitter tags, wordmark. The site is
  now an open letter rather than a description and a CV.
- "Ứng viên là ai?" becomes "Mình là ai?"; the cold open is a seat that has been
  empty a while rather than a vacancy; the CTA meets a person, not a candidate.
- Scene 4 offers what he can give instead of listing entitlements, and loses the
  job-spec table whose three jokes only worked as a job spec.
- Scene 5 drops "Trách nhiệm" for a smaller, warmer promise.
- Scene 6 keeps its no-tests sentiment without the stress-test phrasing, and the
  climax speaks about the person he hopes to meet rather than a strong applicant.
- The appendix is a few concrete things, not a dossier; the freshness stamp says
  he is still looking rather than that a position remains open.

Also drops the office-parody joke, which pointed at a conceit that no longer
exists, and restyles the numbered package as four unhurried lines so scene 4
keeps a shape of its own without reading as a compensation schedule.
2026-07-25 12:02:10 +07:00
tiennm99 b0cd86e07c feat: make the scroll film land, and give the story a last frame
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.
2026-07-25 11:45:59 +07:00
tiennm99 6f5c0d7ae6 docs: add storytelling audit, stack research and repair plan
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.
2026-07-25 11:45:40 +07:00
tiennm99 d4dc7f4415 docs: add plan for interesting-and-honest profile enhancements 2026-07-25 02:29:21 +07:00
tiennm99 bc499368a5 feat: add connect, photo slot, playlist link, freshness stamp, OS theme
- 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
2026-07-25 02:29:14 +07:00
tiennm99 3427b5950c feat: warm romantic redesign with smoother scroll
- 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
2026-07-25 02:29:07 +07:00
tiennm99 e2d09fd3de @
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.
@
2026-07-24 23:34:28 +07:00
tiennm99 6e996f4d66 chore: ignore local claude settings 2026-07-24 23:34:09 +07:00
tiennm99 2769c0cf17 feat: add scroll-reveal story motion and chapter framing
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.
2026-07-24 22:16:56 +07:00
tiennm99 569c4ad153 refactor: merge jd and cv into single-page index and drop pages workflow 2026-07-23 22:14:19 +07:00
tiennm99 5b4b8a1ac9 refactor: replace sveltekit with plain html/css/js static site 2026-07-23 21:21:18 +07:00
tiennm99 fb2a214cc2 feat(cv): cover full cv.txt content with original witty tone 2026-07-23 21:11:30 +07:00
tiennm99 651783a1e4 chore: ignore temp directory 2026-07-23 21:11:30 +07:00
tiennm99 c54a38c3f8 refactor: apply review fixes and drop i18n for vietnamese-only copy
- 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
2026-07-23 20:52:47 +07:00
tiennm99 631c334c59 docs: add code review and ux review reports 2026-07-23 20:52:46 +07:00
tiennm99 546e8d56f2 refactor: convert typescript sources to javascript with jsdoc
- 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
2026-07-23 20:25:41 +07:00
tiennm99 286a689a86 feat(cv): personalize dating profile 2026-07-21 23:53:17 +07:00
tiennm99 4b2fc4493e feat(assets): add generated media set 2026-06-21 23:47:19 +07:00
tiennm99 3ad97e3e1f style(skills): format minimax cli skill 2026-06-21 23:46:35 +07:00
tiennm99 1700cc532b feat(skills): add minimax cli skill 2026-06-21 23:12:32 +07:00
tiennm99 8ce63afcf3 fix: prefer vietnamese locale labels 2026-06-21 22:57:48 +07:00
tiennm99 8acf5839b4 fix: render language flags without emoji 2026-06-21 22:55:03 +07:00
tiennm99 9043f3a711 feat: polish locale and theme controls 2026-06-21 22:50:34 +07:00
tiennm99 90a4c1eccf feat: add theme and locale switches 2026-06-21 22:41:29 +07:00
tiennm99 f008c4731e build: override vulnerable cookie dependency 2026-06-21 22:29:04 +07:00
tiennm99 0731513a01 build: pin pnpm package manager 2026-06-21 22:24:50 +07:00
tiennm99 84bdb135d9 feat: initialize dating site 2026-06-21 22:23:38 +07:00