/* Trần Tôn Nữ Thục Anh — portfolio
   Design source: Claude Design artifact 54559e05-eda1-4835-9b23-bd439f19e24d
   Recovered source + oracles: docs/design/artifact-source/
   Approved deviations from the artifact: reference/DEVIATIONS.md
*/

/* Self-hosted subsets extracted from the artifact bundle (OFL).
   Dropped, each verified against the actual content rather than assumed:
     - cyrillic / cyrillic-ext : unreachable for Vietnamese/English copy
     - latin-ext               : ZERO characters in config.json need it once `vietnamese` is
                                 declared last. Google's ranges overlap (latin-ext covers
                                 U+0100-02AF and U+1EF2-1EFF; vietnamese covers U+0102-0103,
                                 U+0110-0111, U+01A0-01B0, U+1EA0-1EF9), and the LATER rule wins —
                                 so shipping latin-ext after vietnamese made it win for core
                                 Vietnamese letters (ă Đ đ ũ ơ ư ỳ ỹ) and forced a whole extra
                                 subset per family.
     - Be Vietnam Pro 200/500  : declared but never fetched; nothing on the page uses them.
   `vietnamese` is declared LAST so it wins any remaining overlap.
   Verify coverage after a content change: node tools/check-font-coverage.mjs */
/* latin */
@font-face{font-family:'Be Vietnam Pro';font-style:normal;font-weight:300;font-display:swap;src:url('fonts/bevietnampro-300-normal-latin.36755b3a.woff2') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* vietnamese */
@font-face{font-family:'Be Vietnam Pro';font-style:normal;font-weight:300;font-display:swap;src:url('fonts/bevietnampro-300-normal-vietnamese.36e0cd62.woff2') format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB}
/* latin */
@font-face{font-family:'Be Vietnam Pro';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/bevietnampro-400-normal-latin.d25f70ff.woff2') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* vietnamese */
@font-face{font-family:'Be Vietnam Pro';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/bevietnampro-400-normal-vietnamese.b0d9b8ae.woff2') format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB}
/* latin */
@font-face{font-family:'Cormorant Garamond';font-style:italic;font-weight:300;font-display:swap;src:url('fonts/cormorantgaramond-300-italic-latin.e6d6d1d7.woff2') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* vietnamese */
@font-face{font-family:'Cormorant Garamond';font-style:italic;font-weight:300;font-display:swap;src:url('fonts/cormorantgaramond-300-italic-vietnamese.3512ad3b.woff2') format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB}
/* latin */
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:300;font-display:swap;src:url('fonts/cormorantgaramond-300-normal-latin.5d618c46.woff2') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* vietnamese */
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:300;font-display:swap;src:url('fonts/cormorantgaramond-300-normal-vietnamese.826f73ca.woff2') format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB}
/* latin */
@font-face{font-family:'Cormorant Garamond';font-style:italic;font-weight:400;font-display:swap;src:url('fonts/cormorantgaramond-300-italic-latin.e6d6d1d7.woff2') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* vietnamese */
@font-face{font-family:'Cormorant Garamond';font-style:italic;font-weight:400;font-display:swap;src:url('fonts/cormorantgaramond-300-italic-vietnamese.3512ad3b.woff2') format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB}
/* latin */
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/cormorantgaramond-300-normal-latin.5d618c46.woff2') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* vietnamese */
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/cormorantgaramond-300-normal-vietnamese.826f73ca.woff2') format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB}
/* latin */
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:500;font-display:swap;src:url('fonts/cormorantgaramond-300-normal-latin.5d618c46.woff2') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* vietnamese */
@font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:500;font-display:swap;src:url('fonts/cormorantgaramond-300-normal-vietnamese.826f73ca.woff2') format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB}
/* latin */
@font-face{font-family:'Italianno';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/italianno-400-normal-latin.c91c4b8b.woff2') format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* vietnamese */
@font-face{font-family:'Italianno';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/italianno-400-normal-vietnamese.d09b833f.woff2') format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB}

/* ── design system: verbatim from the artifact, except the D1 contrast tokens ── */
*{box-sizing:border-box;margin:0;padding:0}
:root{--bg:#F4F2ED;--paper:#FBFAF6;--ink:#2B2A25;--soft:#6A6759;--line:#DAD5C7;--pink:#BD5565;--rose:#893941;--olive:#5E6623;--citron:#D4D994;--beige:#D0A583;--accent:var(--pink);--accent2:var(--olive);--disp:"Cormorant Garamond",Georgia,serif;--sans:"Be Vietnam Pro","Helvetica Neue",Helvetica,sans-serif;--script:"Italianno",cursive;--scale:1}
html{scroll-behavior:smooth}
body{background:var(--bg);color:var(--ink);font-family:var(--sans);font-weight:300;-webkit-font-smoothing:antialiased}
a{color:var(--ink);text-decoration:none;transition:color .25s}
a:hover{color:var(--accent)}
::selection{background:var(--citron);color:var(--ink)}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
.wrap{max-width:1280px;margin:0 auto;padding:0 46px}
.eyebrow{font-family:var(--sans);font-weight:400;font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--soft)}
.disp{font-family:var(--disp);font-weight:300;line-height:1.02;letter-spacing:-.005em}
.it{font-style:italic}
.script{font-family:var(--script);font-weight:400;line-height:.85}
.body{font-size:15px;line-height:1.8;letter-spacing:.002em;color:var(--soft)}
.rule{height:1px;background:var(--line);border:0}
.reveal{opacity:0;transform:translateY(20px);transition:opacity .9s cubic-bezier(.2,.7,.2,1),transform .9s cubic-bezier(.2,.7,.2,1)}
.reveal.in{opacity:1;transform:none}
.btn{display:inline-flex;align-items:center;gap:10px;min-height:44px;padding:12px 24px;border:1px solid var(--line);border-radius:999px;font-size:11px;font-weight:400;letter-spacing:.18em;text-transform:uppercase;transition:all .3s}
.btn:hover{border-color:var(--accent);color:var(--accent)}
.btn-solid{background:var(--accent);border-color:var(--accent);color:#fff}
.btn-solid:hover{background:var(--rose);border-color:var(--rose);color:#fff}
.pill{display:inline-block;padding:6px 14px;border:1px solid var(--line);border-radius:999px;font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--soft)}
image-slot{display:block;width:100%;height:100%}
.frame{position:relative;background:var(--paper);border:1px solid var(--line);overflow:hidden}
/* folder stack */
.stack{display:flex;flex-direction:column;position:relative;min-width:0;width:100%}
.folder{--off:var(--off-instance,0px);position:relative;cursor:pointer;transition:transform .45s cubic-bezier(.2,.8,.2,1),filter .3s;margin-top:-16px}
.clip + .folder{margin-top:0}
.folder:hover{transform:translateY(-12px)}
.folder:hover .tab{filter:brightness(1.06)}
.tab{height:44px;width:max-content;max-width:calc(100% - var(--off));margin-left:var(--off);border-radius:14px 20px 0 0;display:flex;align-items:center;gap:12px;padding:0 20px;box-shadow:0 -1px 0 rgba(0,0,0,.05)}
.tabname{font-family:var(--disp);font-size:16.5px;letter-spacing:.03em;line-height:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.folder-body{height:86px;border-radius:0 10px 2px 2px;display:flex;align-items:center;justify-content:space-between;gap:20px;padding:0 30px;box-shadow:inset 0 1px 0 rgba(255,255,255,.14)}
.fnote{font-size:12.5px;letter-spacing:.05em;opacity:1}
.fgo{font-size:10.5px;letter-spacing:.2em;text-transform:uppercase;opacity:1;white-space:nowrap;transition:opacity .3s,transform .35s}
.folder:hover .fgo{opacity:1;transform:translateX(6px)}
.clip{width:16px;height:52px;border-radius:9px;border:3px solid #B9B3A4;position:absolute;right:44px;top:-20px;background:transparent;z-index:3}
.sechead{display:flex;align-items:baseline;justify-content:space-between;gap:20px;padding-bottom:16px;border-bottom:2px solid var(--sc);margin-bottom:40px;flex-wrap:wrap}
.crow{display:grid;grid-template-columns:56px 1fr 210px 130px;gap:26px;align-items:start;padding:32px 0;border-top:1px solid var(--line);cursor:pointer;transition:padding-left .4s cubic-bezier(.2,.7,.2,1)}
.crow:hover{padding-left:16px}
.crow:hover .ct{color:var(--accent)}
.ct{font-family:var(--disp);font-weight:300;font-size:calc(42px * var(--scale));line-height:1.05;transition:color .3s}
.ccard{cursor:pointer}
.ccard:hover .ct{color:var(--accent)}
.sheet{position:fixed;inset:0;z-index:60;background:var(--bg);overflow-y:auto;animation:rise .55s cubic-bezier(.2,.7,.2,1)}
@keyframes rise{from{transform:translateY(2.5%);opacity:0}to{transform:none;opacity:1}}
.toast{position:fixed;left:50%;bottom:34px;transform:translateX(-50%);z-index:80;background:var(--ink);color:var(--bg);padding:13px 22px;border-radius:999px;font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;animation:rise .3s}
.brandwall{display:flex;flex-wrap:wrap;align-items:center;gap:26px 40px}
.brandwall span{font-family:var(--disp);font-size:clamp(19px,2.1vw,30px);padding:0}
.blogo{width:auto;object-fit:contain;opacity:.9}
.brandwall span:nth-child(even){font-style:italic;color:var(--olive)}
.backtop{font-size:10.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--soft)}

/* ==========================================================================
   Lifted component styles
   Every value below is copied verbatim from the artifact's inline React styles
   (docs/design/artifact-source/modules/app.jsx). They are classes rather than
   inline styles for one reason: inline styles are unreachable by a media query,
   which is precisely why the artifact's own mobile layout is broken.
   Four properties stay inline because they are genuinely per-instance:
   folder background, slot aspect-ratio, --off-instance, --sc.
   ========================================================================== */

/* ── nav ─────────────────────────────────────────────────────────────────── */
.nav{position:fixed;top:0;left:0;right:0;z-index:50;background:transparent;
     border-bottom:1px solid transparent;transition:background .4s,backdrop-filter .4s,border-color .4s}
.nav.is-scrolled{background:color-mix(in oklab,var(--bg) 90%,transparent);
     backdrop-filter:blur(12px);border-bottom-color:var(--line)}
.nav-inner{height:74px;display:flex;align-items:center;justify-content:space-between;gap:20px}
.nav-brand{font-size:21px}
.nav-brand strong{font-weight:500}
.lang{display:flex;gap:7px}
.lang button{padding:7px 13px;border-radius:999px;font-size:10.5px;letter-spacing:.16em;
     border:1px solid var(--line);background:transparent;color:var(--soft);transition:all .3s}
.lang button[aria-pressed="true"]{border-color:transparent;background:var(--ink);color:var(--bg)}

/* ── opening ─────────────────────────────────────────────────────────────── */
.opening{min-height:100vh;display:flex;align-items:center;padding:80px 0 60px}
.opening .wrap{width:100%}
.opening-grid{display:grid;grid-template-columns:1fr 1.05fr;gap:64px;align-items:center}
.opening-title{font-size:clamp(56px,8.6vw,124px);margin-top:18px}
.opening-title .script{color:var(--accent);font-size:1.28em;margin-left:.02em}
.opening-name{font-size:clamp(30px,4vw,60px);font-weight:400;letter-spacing:.05em;
     text-transform:uppercase;margin-top:14px;color:#893941}
.opening-name strong{font-weight:500}
.opening-intro{margin-top:22px;max-width:30em}
.opening-pills{display:flex;gap:9px;flex-wrap:wrap;margin-top:26px}
.opening-pick{margin-top:34px}
.folder-title{font-size:26px;line-height:1}
.folder .fnote{margin-top:5px}
.folder[data-note-first] .folder-title{margin-top:5px}
.folder[data-note-first] .fnote{margin-top:0}

/* folders become real buttons; .folder was a <div>, so display/alignment must be restated
   or an inline <button> box would break the stack before any media query runs */
button.folder{display:block;width:100%;text-align:left;font:inherit;color:inherit;
     background:none;border:0;padding:0}
.folder:focus-visible{outline:2px solid var(--ink);outline-offset:3px}

/* ── section heads ───────────────────────────────────────────────────────── */
.sec-title{font-size:clamp(32px,4.4vw,58px);margin-top:12px}

/* ── about ───────────────────────────────────────────────────────────────── */
.sec{padding:100px 0;border-top:1px solid var(--line)}
.sec--paper{background:var(--paper)}
.about-grid{display:grid;grid-template-columns:1.2fr minmax(250px,.8fr);gap:64px;align-items:start}
.about-quote{font-size:clamp(26px,3.4vw,44px);max-width:19em}
.about-bio{display:grid;gap:16px;margin-top:28px;max-width:44em}
.about-skills{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:32px;
     margin-top:46px;padding-top:30px;border-top:1px solid var(--line)}
.about-skills h4{color:var(--ink);margin-bottom:13px}
.about-skills ul{list-style:none;display:grid;gap:8px}
.about-skills li{font-size:14.5px;line-height:1.45}
.about-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:26px;
     margin-top:46px;padding-top:28px;border-top:1px solid var(--line)}
.about-stat-v{font-size:clamp(34px,3.8vw,52px)}
.about-stat-k{margin-top:6px}
.about-card{margin-top:26px;padding:24px 24px 26px;color:#fff;border-radius:2px}
.about-card .eyebrow{color:#fff}
.about-card-rows{display:grid;gap:12px;margin-top:14px}
.about-card-rows a,.about-card-rows span{color:#fff;font-size:15px}
.about-card-btns{display:flex;gap:9px;flex-wrap:wrap;margin-top:20px}
.about-card-btns .btn{border-color:rgba(255,255,255,.5);color:#fff}
.about-card-btns .btn:hover{border-color:#fff;color:#fff}
.brands{margin-top:80px}
.brands .eyebrow{margin-bottom:20px}

/* ── cv ──────────────────────────────────────────────────────────────────── */
.cv-row{display:grid;grid-template-columns:190px 1fr;gap:34px;padding:28px 0;
     border-top:1px solid var(--line)}
.cv-year{padding-top:8px}
.cv-role{font-family:var(--disp);font-weight:400;font-size:26px;line-height:1.2}
.cv-co{margin-top:8px}
.cv-row ul{list-style:none;display:grid;gap:9px;margin-top:14px;max-width:52em}
.cv-row li{font-size:14.5px;line-height:1.7}

/* ── education ───────────────────────────────────────────────────────────── */
.edu-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:50px;align-items:start}
.edu-school{font-size:clamp(28px,3.4vw,44px);line-height:1.1}
.edu-deg{margin-top:14px}
.edu-years{margin-top:8px}
.edu-notes{list-style:none;display:grid;gap:0}
.edu-notes li{font-family:var(--disp);font-size:20px;line-height:1.35;padding:16px 0;
     border-top:1px solid var(--line)}

/* ── work ────────────────────────────────────────────────────────────────── */
button.crow{width:100%;text-align:left;font:inherit;color:inherit;background:none;border:0;
     border-top:1px solid var(--line)}
.crow:focus-visible{outline:2px solid var(--ink);outline-offset:-2px}
.crow-n{padding-top:14px}
.crow-sum{margin-top:10px;max-width:40em;font-size:14.5px}
.cmeta{padding-top:14px}
.cmeta .eyebrow:first-child{color:var(--ink)}
.cmeta .eyebrow+.eyebrow{margin-top:7px}
.ckpi{padding-top:8px;text-align:right}
.ckpi p{font-size:26px}
.peek{position:fixed;width:258px;pointer-events:none;z-index:40;
     box-shadow:0 26px 70px rgba(43,42,37,.2)}

/* ── videos ──────────────────────────────────────────────────────────────── */
.videos{padding:0 0 100px;background:var(--paper)}
.videos-label{margin-bottom:20px;margin-top:70px;color:#5F6624;font-size:20px;font-family:Georgia,serif;
     font-weight:500;text-align:left;text-transform:none;line-height:1;letter-spacing:normal}
.videos-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:22px;align-items:start}
.videos-grid .eyebrow{margin-top:9px}

/* ── extracurricular ─────────────────────────────────────────────────────── */
.extra-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:0 44px}
.extra-item{padding:24px 0;border-top:2px solid var(--sc)}
.extra-role{font-size:26px;margin-top:8px;line-height:1.15}
.extra-org{margin-top:8px;color:var(--ink)}
.extra-desc{font-size:14.5px;margin-top:10px}

/* ── awards ──────────────────────────────────────────────────────────────── */
.awards-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(270px,1fr));gap:0 44px}
.award{display:grid;grid-template-columns:44px 1fr;gap:12px;padding:20px 0;
     border-top:1px solid var(--line)}
.award-n{font-size:26px}
.award-t{font-family:var(--disp);font-size:20px;line-height:1.35}

/* ── contact ─────────────────────────────────────────────────────────────── */
.contact{padding:110px 0 80px;border-top:1px solid var(--line)}
.contact-thanks{font-size:clamp(52px,9vw,118px);color:var(--accent)}
.contact-open{margin-top:12px;max-width:30em}
.contact-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:26px;
     margin-top:52px;padding-top:28px;border-top:1px solid var(--line)}
.contact-grid .eyebrow{margin-bottom:8px}
.contact-grid a,.contact-grid span{font-size:15.5px}
.contact-btns{display:flex;gap:10px;margin-top:40px;flex-wrap:wrap}
.contact-copy{margin-top:70px}

/* ── campaign sheet ──────────────────────────────────────────────────────── */
.sheet-bar{position:sticky;top:0;z-index:5;background:color-mix(in oklab,var(--bg) 92%,transparent);
     backdrop-filter:blur(12px);border-bottom:1px solid var(--line)}
.sheet-bar .wrap{height:74px;display:flex;align-items:center;justify-content:space-between}
.sheet-body{padding:66px 46px 110px}
.sheet-title{font-size:clamp(38px,6.6vw,90px);max-width:15em;margin-top:16px}
.sheet-sum{font-size:clamp(20px,2.2vw,30px);line-height:1.45;margin-top:22px;max-width:24em}
.sheet-shot-a{margin-top:54px}
.sheet-vids{margin-top:26px}
.sheet-vids h3{margin-bottom:16px}
.sheet-vids-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:24px}
.sheet-vids figcaption{margin-top:9px}
.sheet-cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:56px;margin:74px 0}
.sheet-cols h3{margin-bottom:14px}
.sheet-goal{font-size:16.5px;line-height:1.8}
.sheet-did{list-style:none;display:grid;gap:15px}
.sheet-did li{display:grid;grid-template-columns:30px 1fr;gap:10px;font-size:15.5px;line-height:1.7;
     min-width:0}
.sheet-did .eyebrow{padding-top:5px}
.sheet-shots{display:grid;grid-template-columns:1fr 1fr;gap:24px}
.sheet-res{margin-top:82px;padding-top:32px;border-top:1px solid var(--line)}
.sheet-res h3{margin-bottom:26px}
.sheet-res-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:34px}
.sheet-res-v{font-size:clamp(38px,5vw,64px)}
.sheet-res-k{margin-top:10px}
.sheet-foot{margin-top:90px;padding-top:28px;border-top:1px solid var(--line);display:flex;
     justify-content:space-between;gap:16px;flex-wrap:wrap}

/* ── image slots ─────────────────────────────────────────────────────────── */
.slot{width:100%}
.slot img{width:100%;height:100%;object-fit:cover;display:block}
.slot-empty{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
     justify-content:center;gap:6px;text-align:center;padding:12px;
     background:rgba(127,127,127,.08);color:var(--soft);
     font:13px/1.3 system-ui,-apple-system,sans-serif}

/* long content must never force a track wider than its column */
.about-grid>*,.cv-row>*,.edu-grid>*,.extra-grid>*,.awards-grid>*,.contact-grid>*,
.videos-grid>*,.sheet-cols>*,.sheet-vids-grid>*,.sheet-res-grid>*,.award>*,.crow>*{min-width:0}

/* ==========================================================================
   Layout-stability and text-fitting fixes (post-launch audit)
   ========================================================================== */

/* The artifact animates .crow:hover with padding-left:16px. That shrinks the flexible title
   column (714px -> 698px), which tips a borderline title onto a second line and makes the row
   44px taller — the page jumps while you hover. A transform is compositor-only and costs no
   layout, so the same slide happens with zero reflow. */
.crow:hover{padding-left:0}
.crow{transition:none}
.crow > *{transition:transform .4s cubic-bezier(.2,.7,.2,1)}
.crow:hover > *{transform:translateX(16px)}

/* Measured natural widths: the tag/year line needs up to 246px in a 210px column, and the KPI
   needs up to 241px in a 130px column — so 3 of 5 rows wrapped at rest, orphaning single words
   ("2026", "visuals"). Widened to fit, and where the longest KPI still needs two lines
   text-wrap:balance splits it evenly instead of stranding one word. */
.crow{grid-template-columns:56px 1fr 250px 175px}
.ckpi p{text-wrap:balance}
.cmeta p{text-wrap:balance}

/* The brand is two names; let it break between them rather than wherever it runs out of room. */
.nav-brand{white-space:nowrap}
.nav-brand strong{white-space:nowrap}

/* A two-word action label should never wrap. */
.nav-cv{white-space:nowrap}

/* ==========================================================================
   Audit fixes — see the workflow audit; 44 findings confirmed by adversarial verification
   ========================================================================== */

/* Anchor jumps left ~15px of every section tucked under the 74px fixed nav. scroll-padding-top
   fixes browser-driven scrolls (Tab, :target); index.js NAV_CLEARANCE matches it for JS scrolls. */
html{scroll-padding-top:88px}

/* Shift+Tab could park focus behind the nav, and a focused .folder had its outline painted over
   by the next folder (they overlap by -16px). Give focusable things their own clearance, and move
   the folder's focus ring onto the parts that are actually on top. */
/* On anchor TARGETS only. Applying it to every focusable control made focus() scroll further,
   which turned the sheet-open jump from 131px into 660px. */
section[id]{scroll-margin-top:88px}
.folder:focus-visible{outline:none}
.folder:focus-visible .tab,
.folder:focus-visible .folder-body{outline:2px solid var(--ink);outline-offset:2px}

/* The hover slide moved the right-hand metadata 16px past the alignment edge shared by the
   section rule and the row borders. The artifact's padding-left only ever shifted the first two
   columns (the 1fr track absorbed it), so restrict the transform the same way. */
.crow:hover > *{transform:none}
.crow:hover > *:nth-child(-n+2){transform:translateX(16px)}

/* The nav CV label ships as "Tải CV" in the HTML and is swapped to "Download CV" once config
   loads, which moved the button 56px left on every English load — a real layout shift, and it
   fired even unthrottled. Reserving the wider label's width removes the jump in both languages. */
/* 8.6em resolved to only 90px against the button's own 10.5px font — far short of the 141px the
   English label needs. Reserved in px per breakpoint, measured from the wider of the two labels. */
.nav-cv{min-width:141px;justify-content:center}

/* Atomic names must not be split across lines: "(UEF)" was orphaned onto a third display line,
   "Baresoul Cosme" and "tốc độ" were broken mid-name. */
.edu-school,.hero-brands,.ct,.sheet-title,.about-quote,.award-t{text-wrap:balance}
.hero-brands [data-hero-brands]{white-space:normal}

/* Wrapping display type inherited .disp{line-height:1.02}, so descenders collided. .edu-school
   already sets 1.1 and never had the problem, so it is deliberately left alone. */
.about-quote,.sheet-title{line-height:1.12}

/* .tabname clipped descenders (its 44px tab and line-height:1 left no room) and its
   text-overflow:ellipsis never fired because the box was never actually constrained. */
.tabname{line-height:1.25;padding-block:2px}

/* CV bullets are dash-led; hang the dash so wrapped lines align with the text, not the dash. */
.cv-row li{padding-left:1.1em;text-indent:-1.1em}

/* A two-word action label should never wrap, on any surface. */
.sheet-bar .btn,.sheet-foot .btn{white-space:nowrap}

/* A link that renders identically to the plain text beside it is not a link a reader can find.
   In the contact grid, "Download PDF" (a real <a download>) was pixel-identical to
   "District 2, Ho Chi Minh City" (a plain <span>) — same colour, no underline — so the only
   discoverable affordance was hover. Underline the real links, and leave the plain values alone.
   Kept deliberately quiet: a hairline underline at a generous offset, in the muted rule colour,
   so it reads as editorial rather than as a web-form link. */
.contact-grid a,.about-card-rows a{
  text-decoration:underline;
  text-decoration-thickness:1px;
  text-underline-offset:.28em;
  text-decoration-color:var(--line);
  transition:text-decoration-color .25s,color .25s}
.contact-grid a:hover,.contact-grid a:focus-visible,
.about-card-rows a:hover,.about-card-rows a:focus-visible{text-decoration-color:currentColor}
/* the contact card sits on the pink surface, so its rule colour must be a light tint */
.about-card-rows a{text-decoration-color:rgba(255,255,255,.45)}

/* ==========================================================================
   Responsive tier — everything below 900px.
   The artifact has no working layout here: its grids are inline styles that a
   media query cannot reach, producing 208px of overflow at 375px.
   Explicit 1fr, never repeat(auto-fit,minmax(0,1fr)) — a zero minimum still
   permits several narrow tracks, which would pass an overflow check while
   remaining visually multi-column.
   ========================================================================== */
@media (max-width:900px){
  .wrap{padding:0 22px}

  .opening{min-height:0;padding:104px 0 56px}
  .opening-grid{grid-template-columns:1fr;gap:44px}
  .stack{margin-top:0}
  .folder{--off:0px}
  .tab{max-width:100%;margin-left:0}
  .tabname{white-space:normal}
  .folder-body{height:auto;min-height:76px;padding:16px 20px;gap:12px}
  .clip{display:none}
  .fnote{display:block;font-size:12px}

  .sec,.contact{padding:64px 0}
  .videos{padding:0 0 64px}
  .videos-label{margin-top:48px}

  .about-grid{grid-template-columns:1fr;gap:40px}
  .about-skills{grid-template-columns:1fr;gap:26px;margin-top:34px}
  .about-stats{grid-template-columns:1fr 1fr;gap:22px;margin-top:34px}
  .brands{margin-top:52px}

  .cv-row{grid-template-columns:1fr;gap:10px}
  .cv-year{padding-top:0}

  .edu-grid{grid-template-columns:1fr;gap:30px}
  .extra-grid,.awards-grid,.contact-grid,.videos-grid{grid-template-columns:1fr}
  .extra-grid,.awards-grid{gap:0}
  .contact-grid{gap:22px}
  /* contact rows were 14-20px tall — below the 24px WCAG 2.2 target minimum, far below comfortable */
  .contact-grid a{display:inline-block;min-height:44px;line-height:44px}
  .about-card-rows a{display:inline-block;min-height:40px;line-height:40px}
  .videos-grid{gap:26px}

  .crow{grid-template-columns:34px 1fr;gap:14px;padding:24px 0}
  .crow .cmeta,.crow .ckpi{display:none}

  /* The nav has three items in ~346px of usable width. The brand gets an intentional break
     between the two names instead of orphaning "Anh", and the CV label stays on one line. */
  .nav-brand{font-size:17px;line-height:1.15;white-space:normal}
  .nav-brand strong{display:block}
  .nav-cv{padding:8px 11px;letter-spacing:.08em}
  .nav-inner{gap:12px}

  .sheet-body{padding:40px 22px 72px}
  .sheet-cols{grid-template-columns:1fr;gap:34px;margin:48px 0}
  .sheet-vids-grid,.sheet-res-grid{grid-template-columns:1fr}
  .sheet-shots{grid-template-columns:1fr;gap:16px}
  .sheet-did li{grid-template-columns:24px 1fr}
  .sheet-res{margin-top:52px}
  .sheet-foot{margin-top:56px}
  .peek{display:none}
}

@media (max-width:420px){
  .about-stats{grid-template-columns:1fr}
}


/* Reduced motion must FORCE .reveal visible. Disabling only the transition
   leaves it stranded at opacity:0 — invisible content, not a calmer page. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;scroll-behavior:auto!important}
  .reveal{opacity:1!important;transform:none!important}
  .sheet{animation:none!important}
}

/* Skip link — off-screen until focused, so it costs no layout but stays reachable by keyboard. */
.skip{position:absolute;left:-9999px;top:0;z-index:100;padding:12px 18px;background:var(--ink);
      color:var(--bg);font-size:12px;letter-spacing:.14em;text-transform:uppercase}
.skip:focus{left:8px;top:8px}

/* ==========================================================================
   Reader-flow additions (beyond artifact parity — see reference/DEVIATIONS.md D5–D7)
   Measured problem: the first screen carried identity but no evidence, and the CV
   sat 2.1 screens down on desktop / 4.3 on mobile.
   ========================================================================== */

/* D5 — proof in the first screen */
.hero-proof{margin-top:26px;padding-top:22px;border-top:1px solid var(--line)}
/* 2x2 rather than flex-wrap: four stats never fit one line in the hero column, and wrapping
   left the fourth orphaned on a line of its own. */
.hero-stats{display:grid;grid-template-columns:repeat(2,minmax(0,auto));justify-content:start;
     gap:14px 44px}
.hero-stat{display:flex;align-items:baseline;gap:7px}
.hero-stat-v{font-size:27px;line-height:1;color:var(--accent)}
.hero-stat-k{font-size:11.5px;letter-spacing:.06em;color:var(--soft)}
/* Brand names are the recognisable asset here (L'Oréal, Estée Lauder). Dense 10px uppercase
   tracking makes them slow to read, so this row drops the caps treatment. */
.hero-brands{margin-top:18px;line-height:1.7;font-size:12px;letter-spacing:.01em;
     text-transform:none;color:var(--soft)}
.hero-brands [data-hero-brands]{color:var(--ink)}

/* D6 — persistent CV action in the nav */
.nav-actions{display:flex;align-items:center;gap:12px}
.nav-cv{min-height:36px;padding:9px 18px;font-size:10.5px}
.nav-cv:hover{background:var(--rose);border-color:var(--rose);color:#fff}

@media (max-width:900px){
  .hero-proof{margin-top:22px;padding-top:18px}
  .hero-stats{gap:12px 26px}
  .hero-stat-v{font-size:23px}
  .hero-brands{font-size:11.5px}
  .nav-actions{gap:8px}
  .nav-cv{padding:8px 12px;letter-spacing:.1em;min-width:120px}
}
@media (max-width:420px){
  .nav-cv{padding:8px 10px;min-width:116px}
}

/* At 320px the English "Download CV" label left the brand only ~43px, forcing it to a third line.
   Tightening the language pills and the brand buys back enough room for the intended two-line
   break to hold. This must come AFTER the 420px block above, which also sets .nav-brand — both
   match at 320px, and the later rule wins. */
@media (max-width:365px){
  .nav-brand{font-size:15.5px}
  .lang button{padding:6px 9px;letter-spacing:.1em}
  .lang{gap:5px}
  .nav-cv{padding:8px 9px;letter-spacing:.04em;min-width:107px}
  .nav-inner{gap:9px}
}

/* D9 — false floor at the fold. NN/G: 57% of viewing time is above the fold and the illusion of
   completeness suppresses scrolling, so a signifier (cut-off content) is needed. At 1440x900 the
   hero was exactly 100vh with the folder stack ending 90px short, so nothing was cut and the page
   read as finished. Shortening it lets the next section peek above the fold.
   NOTE: the svh variant only ever applies ABOVE 900px, because the media query below sets
   min-height:0 on mobile. It is kept for large short viewports (landscape tablets), not for the
   phone-chrome problem an earlier version of this comment wrongly claimed it solved — on phones
   the hero is content-sized and already taller than the viewport, which is its own scroll cue. */
.opening{min-height:calc(100vh - 72px)}
@supports (min-height:100svh){ .opening{min-height:calc(100svh - 72px)} }
@media (max-width:900px){ .opening{min-height:0} }
