/* phantruongthanh.dev — bright paper, wet ink, a light that follows the hand.
   Every animation is additive: without `.anim` on <html> (reduced motion, or
   no JS) every stroke is drawn, every line is visible, every screen is shown,
   the whole timeline is on the page. */

/* ---------- tokens ---------- */
:root {
  /* paper: luminous, warm-white; the old cream read as dim */
  --paper: #fdfcf9; --paper-2: #f4f1ea; --paper-3: #ffffff;
  --rule: #e3dfd5; --rule-2: #ede9e0;
  --ink: #131c26; --ink-2: #46535f; --ink-3: #7b8692; --sumi: #1a2430;
  /* emerald stays the anchor; persimmon (柿) is the warm second voice */
  --em-600: #0a8a5e; --em-500: #11ac79; --em-100: #d3f2e4;
  --ka-600: #d95a2f; --ka-500: #f27a4e; --ka-100: #ffe4d5;
  --wash-mint: rgba(80, 222, 170, .34); --wash-kaki: rgba(255, 168, 118, .34); --wash-sun: rgba(255, 226, 140, .30);
  --light: radial-gradient(closest-side, rgba(150, 242, 205, .60), rgba(255, 214, 186, .34) 46%, transparent 72%);
  --shade: rgba(14, 42, 32, .30); --shade-2: rgba(14, 42, 32, .12);
  --contact: 0 1px 1px rgba(14, 30, 24, .05), 0 2px 6px -2px rgba(14, 30, 24, .10);
  --glare: rgba(255,255,255,.28);
  --card: linear-gradient(158deg, #ffffff 0%, #f7f4ec 100%);
  --chrome: #f1eee7;
  --trail: #1a2430; --blend: multiply; --grain: multiply;
  --dot-fill: currentColor; --dot-stroke: none;
  --ease-ink: cubic-bezier(.35, 0, .1, 1);
  --ease-out: cubic-bezier(.2, .8, .2, 1);
  --ease-pop: cubic-bezier(.3, 1.6, .4, 1);
  --display: "Newsreader", Georgia, "Times New Roman", serif;
  --body: "Noto Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, "Noto Sans JP", monospace;
  color-scheme: light;
}
/* Dark is a choice, never a default: only [data-theme="dark"] (restored from
   localStorage by the inline script) switches it on. Lifted charcoal, not black. */
:root[data-theme="dark"] {
  --paper: #15191e; --paper-2: #1c2127; --paper-3: #232a31;
  --rule: #2e353d; --rule-2: #272d34;
  --ink: #eef0ea; --ink-2: #aeb6be; --ink-3: #7f8993; --sumi: #f3f1e9;
  --em-600: #41d69e; --em-500: #37c68f; --em-100: #173a2b;
  --ka-600: #ff9c75; --ka-500: #ff8a5c; --ka-100: #3b2419;
  --wash-mint: rgba(40, 180, 125, .17); --wash-kaki: rgba(255, 140, 90, .13); --wash-sun: rgba(255, 200, 100, .09);
  --light: radial-gradient(closest-side, rgba(90, 225, 170, .17), rgba(255, 170, 120, .08) 46%, transparent 72%);
  --shade: rgba(0, 0, 0, .62); --shade-2: rgba(0, 0, 0, .35);
  --contact: 0 1px 1px rgba(0,0,0,.4), 0 2px 6px -2px rgba(0,0,0,.5);
  --glare: rgba(255,255,255,.09);
  --card: linear-gradient(158deg, #232a31 0%, #191e24 100%);
  --chrome: #22282f;
  --trail: #ece9df; --blend: screen; --grain: soft-light;
  --dot-fill: none; --dot-stroke: currentColor;
  color-scheme: dark;
}

/* Japanese: Noto Serif JP carries the display voice, Noto Sans JP the body. No
   Latin tracking on Japanese text; no synthetic italic (Noto Serif JP has none). */
:root[lang="ja"] { --display: "Noto Serif JP", "Newsreader", Georgia, serif; --body: "Noto Sans JP", "Noto Sans", system-ui, sans-serif; }
:root[lang="ja"] h1 { font-size: clamp(2.7rem, 9.2vw, 5.7rem); line-height: 1.14; letter-spacing: 0; font-feature-settings: "palt"; }
:root[lang="ja"] .hello { font-style: normal; letter-spacing: 0; }
:root[lang="ja"] .creed { font-style: normal; letter-spacing: 0; line-height: 1.55; max-width: 21em; font-feature-settings: "palt"; }
:root[lang="ja"] .meta h3, :root[lang="ja"] .what h3, :root[lang="ja"] .when, :root[lang="ja"] .li { letter-spacing: 0; }
:root[lang="ja"] .eyebrow, :root[lang="ja"] .tab, :root[lang="ja"] .where, :root[lang="ja"] .when .to, :root[lang="ja"] .slot .mono, :root[lang="ja"] .cue, :root[lang="ja"] .role { letter-spacing: .12em; }
:root[lang="ja"] .note, :root[lang="ja"] .blurb, :root[lang="ja"] .lang-note, :root[lang="ja"] .small { line-height: 1.8; }

/* ---------- base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { font-size: 17px; -webkit-text-size-adjust: 100%; scroll-behavior: smooth; overflow-x: clip; }
@media (min-width: 768px) { html { font-size: 18px; } }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body {
  margin: 0; min-height: 100svh; display: flex; flex-direction: column; isolation: isolate;
  background: var(--paper); color: var(--ink);
  font-family: var(--body); font-size: 1rem; line-height: 1.6;
  -webkit-font-smoothing: antialiased; overflow-x: clip;
  transition: background-color 400ms ease, color 400ms ease;
}
/* paper grain: one small SVG noise tile, multiplied at 5% — the page stops being a flat colour */
body::before {
  content: ""; position: fixed; inset: 0; z-index: 30; pointer-events: none; opacity: .055; mix-blend-mode: var(--grain);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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: 220px 220px;
}
main { flex: 1; }
h1, h2, h3 { font-family: var(--display); font-weight: 400; margin: 0; color: var(--ink); }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img { display: block; max-width: 100%; }
i { font-style: normal; }
.small { font-size: .8125rem; color: var(--ink-3); }
.mono { font-family: var(--mono); font-size: .8125rem; letter-spacing: .06em; }
.eyebrow { text-transform: uppercase; color: var(--ink-3); font-weight: 400; margin: 0; font-size: .6875rem; letter-spacing: .18em; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

.wrap { width: 100%; max-width: 74rem; margin-inline: auto; padding-inline: 16px; }
@media (min-width: 640px) { .wrap { padding-inline: 28px; } }

:focus-visible { outline: 2px solid var(--em-600); outline-offset: 3px; border-radius: 3px; }
::selection { background: var(--em-100); }
.skip { position: absolute; left: 16px; top: -3rem; padding: .5rem .75rem; background: var(--ink); color: var(--paper); border-radius: 4px; z-index: 50; text-decoration: none; }
.skip:focus { top: .5rem; }

/* links: the underline writes itself left → right */
a { color: var(--em-600); text-decoration: none;
  background: linear-gradient(currentColor, currentColor) no-repeat 0 100% / 0 1px;
  transition: background-size 320ms var(--ease-ink), color 200ms; }
a:hover, a:focus-visible { background-size: 100% 1px; }

/* ---------- the two light layers (JS adds them) ----------
   .light: a 960px radial that trails the cursor (transform only, so it never
   repaints). .ink: the brush canvas, multiplied onto the paper. */
.light { position: fixed; left: -480px; top: -480px; width: 960px; height: 960px; z-index: -1; pointer-events: none;
  background: var(--light); opacity: 0; transition: opacity 900ms ease; will-change: transform; }
.light.on { opacity: 1; }
.ink { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 40; pointer-events: none; mix-blend-mode: var(--blend); }

/* soft colour washes used by the hero and the two lower sections */
/* wash layers are 4rem wider than their section so the pointer parallax (≤22px) never drags a
   clip edge into view; the sections clip on x only, so this adds no horizontal scroll */
.hero, .work, .history { overflow-x: clip; }
.bg { position: absolute; inset: 0 -4rem; overflow: hidden; pointer-events: none; z-index: -1; }
/* each wash layer reaches past its section (vertically only, so no horizontal overflow) and every
   blob is centred far enough inside that the clip falls where the gradient is already transparent */
.hero .bg { top: -8rem; bottom: -8rem; }
.work .bg, .history .bg { top: -6rem; bottom: -6rem; }
.wash { position: absolute; border-radius: 50%; aspect-ratio: 1; }
.w1 { width: 58vmax; right: -14vmax; top: -24vmax; background: radial-gradient(closest-side, var(--wash-mint), transparent 72%); }
.w2 { width: 52vmax; left: -20vmax; bottom: -14vmax; background: radial-gradient(closest-side, var(--wash-kaki), transparent 72%); }
.w3 { width: 34vmax; left: 34%; top: 36%; background: radial-gradient(closest-side, var(--wash-sun), transparent 70%); }
.anim .wash { animation: drift 26s ease-in-out infinite alternate; }
.anim .w2 { animation-duration: 31s; animation-delay: -9s; }
.anim .w3 { animation-duration: 22s; animation-delay: -5s; }
@keyframes drift { from { transform: translate3d(0,0,0) scale(1); } to { transform: translate3d(3%, 4%, 0) scale(1.06); } }
.bg { transform: translate3d(calc(var(--px, 0) * -22px), calc(var(--py, 0) * -16px), 0); } /* pointer parallax, set by JS */

/* ---------- header ---------- */
.site-head { display: flex; align-items: center; justify-content: space-between; padding-block: 1.1rem; position: relative; z-index: 2; }
/* the wordmark is the site's own address: lowercase mono, the TLD in the quieter ink, tracking eased so it sets as one word */
.brand { color: var(--ink); font-size: .8125rem; letter-spacing: .03em; white-space: nowrap; }
.brand span { color: var(--ink-3); }
@media (max-width: 400px) { .brand { font-size: .75rem; letter-spacing: .02em; } }
.theme { width: 40px; height: 40px; display: grid; place-items: center; border: 1px solid var(--rule); border-radius: 50%;
  background: var(--paper-3); color: var(--ink); cursor: pointer; box-shadow: var(--contact);
  transition: border-color 200ms, background-color 300ms, box-shadow 300ms; }
.theme .dot { fill: var(--dot-fill); stroke: var(--dot-stroke); stroke-width: 1.6; transition: fill 300ms, stroke 300ms; }
.theme:hover { border-color: var(--ink-3); }
.anim .site-head > * { opacity: 0; transform: translateY(-6px); }
.anim.in .site-head > * { opacity: 1; transform: none; transition: opacity 700ms ease 150ms, transform 700ms var(--ease-ink) 150ms; }

/* EN / 日本語: two quiet mono words beside the theme toggle; JS swaps the strings in place (shown only once JS is here — without it the page is English) */
.head-r { display: flex; align-items: center; gap: 1.1rem; }
.langsw { display: none; align-items: center; gap: .35rem; font-family: var(--mono); font-size: .6875rem; letter-spacing: .12em; color: var(--ink-3); }
[data-js] .langsw { display: flex; }
.langsw button { background: none; border: 0; padding: .35rem .15rem; margin: 0; color: inherit; font: inherit; letter-spacing: inherit; cursor: pointer; border-radius: 3px; transition: color 250ms; }
.langsw button[lang="ja"] { letter-spacing: .08em; }
.langsw button:hover { color: var(--ink-2); }
.langsw button[aria-pressed="true"] { color: var(--ink); cursor: default; }
.langsw .sep { opacity: .45; }

/* magnets: the element is pulled a little toward a nearby cursor (JS sets --mx/--my) */
.mag { transform: translate3d(var(--mx, 0px), var(--my, 0px), 0); }

/* ---------- opening ---------- */
.hero { position: relative; }
.hero-in { position: relative; display: grid; gap: 2.25rem; padding-top: clamp(1.5rem, 6vw, 4.5rem); padding-bottom: 6rem; }
.hero-copy { align-self: center; }
h1 { font-size: clamp(3.1rem, 11vw, 6.9rem); line-height: .98; letter-spacing: -.02em; font-variation-settings: "opsz" 36; }
.hello { font-style: italic; color: var(--ink-2); font-weight: 400; letter-spacing: -.01em; }
.end { color: var(--ka-500); display: inline-block; transform-origin: 50% 80%; }
.ch { display: inline-block; white-space: pre; transform: translateY(calc(var(--k, 0) * -.07em)); color: color-mix(in oklab, currentColor, var(--em-600) calc(var(--k, 0) * 100%)); will-change: transform; }
.role { margin-top: 1.6rem; color: var(--em-600); text-transform: uppercase; letter-spacing: .18em; font-size: .75rem; display: flex; align-items: center; gap: .9rem; }
.role::before { content: ""; width: 2.25rem; height: 1px; background: var(--em-500); transform-origin: 0 50%; }
.creed { margin-top: 1.1rem; font-family: var(--display); font-style: italic; font-size: clamp(1.35rem, 2.7vw, 1.95rem); line-height: 1.28; color: var(--ink-2); max-width: 24ch; font-variation-settings: "opsz" 36; }
.creed em { font-style: inherit; background: linear-gradient(92deg, var(--em-600), var(--ka-500)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.mask { display: block; overflow: hidden; padding: .14em 0 .08em; margin: -.14em 0 -.08em; }
.line { display: block; }
.anim .line { transform: translateY(118%); }
.anim .hero.in .line { transform: none; transition: transform 1050ms var(--ease-ink) calc(var(--t) + 220ms); }
.anim .end { transform: scale(0); }
.anim .hero.in .end { transform: none; transition: transform 700ms var(--ease-pop) 1250ms; }
.anim .role::before { transform: scaleX(0); }
.anim .hero.in .role::before { transform: none; transition: transform 800ms var(--ease-ink) 1150ms; }

/* signature: タ, a washi card that leans toward the cursor and casts a shadow away from the light */
.sig { position: relative; width: clamp(12rem, 46vw, 24rem); aspect-ratio: 1; justify-self: start; perspective: 1400px; }
.obj { position: absolute; inset: 0; transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); transform-style: preserve-3d; }
.shade { position: absolute; inset: 8% 6% 2%; border-radius: 22px; pointer-events: none;
  box-shadow: 0 30px 60px -18px var(--shade), 0 10px 24px -10px var(--shade-2);
  transform: translate3d(var(--lx, 0px), var(--ly, 10px), -1px); will-change: transform; }
.card { position: absolute; inset: 0; border-radius: 20px; background: var(--card); border: 1px solid var(--rule-2);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.75), var(--contact); overflow: hidden; }
:root[data-theme="dark"] .card { box-shadow: inset 0 1px 0 rgba(255,255,255,.06), var(--contact); }
.sheen { position: absolute; inset: 0; pointer-events: none; opacity: 0; transition: opacity 500ms;
  background: radial-gradient(60% 60% at var(--gx, 50%) var(--gy, 50%), var(--glare), transparent 70%); }
.hero.lit .sheen { opacity: 1; }
.cell { position: absolute; inset: 0; width: 100%; height: 100%; }
.cell rect { fill: none; stroke: var(--rule); stroke-width: .6; }
.cell path { fill: none; stroke: var(--rule); stroke-width: .5; stroke-dasharray: 2 2.5; }
.guide, .glyph { position: absolute; inset: 7%; width: 86%; height: 86%; overflow: visible; }
.guide path { fill: none; stroke: var(--em-500); stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 2.4 3.2; opacity: .85; }
.glyph { cursor: pointer; border-radius: 8px; }
.glyph .ink-s { fill: none; stroke: var(--sumi); stroke-width: 6.8; stroke-linecap: round; stroke-linejoin: round; }
/* reveal masks: dash 1, gap 2 on a pathLength of 1 — at offset 1 the whole path sits in the gap, so no round cap shows before the stroke starts */
.glyph .rv  { fill: none; stroke: #fff; stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 1 2; stroke-dashoffset: 0; }
/* タ: 1200ms of drawing split by path length (55 : 161 : 30), two 110ms lifts → ends at 1420ms */
.glyph .s1 { --d: 270ms; --t: 0ms; } .glyph .s2 { --d: 780ms; --t: 380ms; } .glyph .s3 { --d: 150ms; --t: 1270ms; }
.anim .glyph .rv { stroke-dashoffset: 1; }
.anim .glyph.drawn .rv { stroke-dashoffset: 0; transition: stroke-dashoffset var(--d) var(--ease-ink) var(--t); }
.anim .cell, .anim .guide { opacity: 0; }
.anim .hero.in .cell { opacity: 1; transition: opacity 900ms ease 650ms; }
.anim .hero.in .guide { opacity: 1; transition: opacity 700ms ease 700ms; }
.anim .sig { opacity: 0; transform: translateY(28px); }
.anim .hero.in .sig { opacity: 1; transform: none; transition: opacity 900ms ease 320ms, transform 1200ms var(--ease-ink) 320ms; }
.anim .shade { opacity: 0; } .anim .hero.in .shade { opacity: 1; transition: opacity 1200ms ease 600ms; }

@media (min-width: 768px) {
  .hero-in { grid-template-columns: minmax(0, 1fr) clamp(16rem, 32vw, 24rem); gap: 3rem; align-items: center; min-height: calc(100svh - 5.5rem); }
  .sig { order: 2; width: 100%; justify-self: end; }
  .role { margin-top: 2.1rem; }
}

.cue { position: absolute; left: 16px; bottom: 1.4rem; display: flex; align-items: center; gap: .75rem; color: var(--ink-3); background: none; text-transform: uppercase; letter-spacing: .16em; font-size: .6875rem; }
@media (min-width: 640px) { .cue { left: 28px; } }
.cue svg { overflow: visible; }
.cue path { fill: none; stroke: var(--em-500); stroke-width: 2; stroke-linecap: round; stroke-dasharray: 1 2; stroke-dashoffset: 0; }
.anim .cue { opacity: 0; } .anim .cue path { stroke-dashoffset: 1; }
.anim .hero.in .cue { opacity: 1; transition: opacity 600ms ease 1900ms; }
.anim .hero.in .cue path { stroke-dashoffset: 0; transition: stroke-dashoffset 900ms var(--ease-ink) 2000ms; }
.cue:hover, .cue:focus-visible { color: var(--ink); }
.anim .cue path { animation: cue-breathe 2.8s ease-in-out 3s infinite; }
@keyframes cue-breathe { 50% { transform: translateY(6px); } }

/* ---------- work ---------- */
.work { position: relative; padding-top: clamp(1rem, 4vw, 3rem); padding-bottom: 1rem; }
.work .w1 { width: 48vmax; right: auto; left: -10vmax; top: -10vmax; opacity: .8; }
.section-bar { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: 1.25rem; }
.section-bar .num { color: var(--ka-500); margin-right: .6em; }
.slider-count { margin-left: auto; }
.slider-nav { display: none; gap: .5rem; }
.slider-nav:not([hidden]) { display: flex; }
.arrow { width: 38px; height: 38px; border: 1px solid var(--rule); border-radius: 50%; background: var(--paper-3); color: var(--ink); font: inherit; cursor: pointer; box-shadow: var(--contact); transition: border-color 200ms, opacity 200ms; }
.arrow:hover:not(:disabled) { border-color: var(--ink-3); }
.arrow:disabled { opacity: .3; cursor: default; }

.track {
  position: relative; /* offsetParent for the slides, so offsetLeft is layout position inside the track */
  display: flex; gap: 1rem; align-items: stretch;
  overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; overscroll-behavior-x: contain;
  scroll-padding-inline: 4px;
  scrollbar-width: none; -ms-overflow-style: none;
  padding: 1rem 4px 4rem; margin: -1rem -4px -4rem; /* room for shadow, lift and focus ring */
}
.track::-webkit-scrollbar { display: none; }
.track > noscript { display: contents; }
.track.drag { scroll-snap-type: none; cursor: grabbing; user-select: none; }
.slide { flex: 0 0 calc(100% - 2.25rem); scroll-snap-align: start; min-width: 0; transform-origin: 50% 60%; }
@media (min-width: 640px) { .slide { flex-basis: calc(100% - 4.5rem); } }
@media (min-width: 1024px) { .slide { flex-basis: min(calc(100% - 6rem), 66rem); } }
.anim .slide { transform: scale(calc(1 - .06 * var(--k, 0))); opacity: calc(1 - .45 * var(--k, 0)); will-change: transform; }
.slot {
  flex: 0 0 min(14rem, 42%); scroll-snap-align: end; display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 1.25rem;
  border: 1.5px dashed var(--rule); border-radius: 18px; color: var(--ink-3); padding: 1.5rem .9rem;
}
@media (min-width: 640px) { .slot { padding: 2rem 1.4rem; } }
.slot .n { font-family: var(--display); font-size: clamp(2rem, 5vw, 3rem); line-height: 1; color: var(--rule); }
.slot .mono { writing-mode: vertical-rl; height: 11rem; white-space: nowrap; text-transform: uppercase; font-size: .6875rem; letter-spacing: .16em; }

.dots { display: flex; justify-content: center; gap: .5rem; margin-top: 1.25rem; }
.dots[hidden] { display: none; }
.dots button { width: 10px; height: 10px; padding: 0; border: 0; border-radius: 50%; background: var(--rule); cursor: pointer; transition: background 200ms, transform 300ms var(--ease-ink); }
.dots button[aria-selected="true"] { background: var(--em-500); transform: scale(1.25); }

/* ---------- languages ----------
   Two meters in the showcase's own tab-bar grammar: a solid bar is a level
   written, a dashed one the stroke not yet made (N3, December 2026). */
.langs { margin-top: clamp(2.25rem, 5vw, 3.5rem); padding-top: 1.4rem; border-top: 1px solid var(--rule); }
.lang-grid { display: grid; gap: 1.75rem; margin: 1.25rem 0 0; }
@media (min-width: 768px) { .lang-grid { grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: 3rem; } }
.lang { display: grid; gap: .7rem; }
.lang dt { display: flex; align-items: baseline; gap: .7rem; color: var(--ink); }
.lang dt .jp { font-family: var(--display); font-size: 1.5rem; line-height: 1; }
.lang dt .mono { color: var(--ink-3); text-transform: uppercase; font-size: .6875rem; letter-spacing: .16em; }
.lang dd { margin: 0; display: grid; gap: .55rem; }
.meter { display: flex; gap: .5rem; }
.meter i { flex: 1 1 0; min-width: 0; display: grid; gap: .45rem; font-family: var(--mono); font-size: .625rem; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3); }
.meter b { font-weight: 400; } .meter b em { font-style: normal; color: var(--ink-3); margin-left: .35em; }
.meter .done b { color: var(--ink); } .meter .here b { color: var(--em-600); } .meter .ahead b { color: var(--em-600); }
.meter .bar { position: relative; display: block; height: 2px; border-radius: 2px; background: var(--rule); overflow: hidden; }
.meter .done .bar::after { content: ""; position: absolute; inset: 0; background: var(--em-500); transform-origin: 0 50%; }
.meter .ahead .bar { background: none; overflow: visible; }
.meter .ahead .bar::after { content: ""; position: absolute; left: 0; right: 0; top: 0; border-top: 2px dashed var(--em-500); opacity: .7; }
.meter.one { max-width: 22rem; }
.meter.one .done .bar::after { right: auto; width: calc(var(--v, 1) * 100%); }
.meter.one .bar::before { content: ""; position: absolute; left: calc(var(--v, 1) * 100%); top: -3px; width: 1px; height: 8px; background: var(--ka-500); z-index: 1; } /* where 960 lands on 990 */
.lang-note { font-size: .875rem; color: var(--ink-2); }
.anim .meter .done .bar::after { transform: scaleX(0); }
.anim .langs.in .meter .done .bar::after { transform: none; transition: transform 900ms var(--ease-ink) calc(var(--i, 0) * 160ms + 200ms); }
.anim .meter .ahead .bar::after, .anim .meter.one .bar::before { opacity: 0; }
.anim .langs.in .meter .ahead .bar::after { opacity: .7; transition: opacity 600ms ease 800ms; }
.anim .langs.in .meter.one .bar::before { opacity: 1; transition: opacity 400ms ease 1000ms; }

/* ---------- the browser frame ---------- */
.project { display: grid; gap: 1.4rem; }
.frame { position: relative; display: block; color: inherit; background: none; border-radius: 16px; cursor: pointer; -webkit-tap-highlight-color: transparent; perspective: 1400px; }
.frame:focus-visible { outline: 3px solid var(--em-600); outline-offset: 6px; border-radius: 16px; }
.frame .shade { inset: 6% 3% 0; border-radius: 24px; box-shadow: 0 48px 90px -24px var(--shade), 0 18px 36px -14px var(--shade-2); transform: translate3d(var(--lx, 0px), var(--ly, 14px), 0); }
.tilt { position: relative; display: block; border-radius: 16px; overflow: hidden; background: var(--chrome); border: 1px solid var(--rule-2);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.7), var(--contact);
  transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); transform-style: preserve-3d; }
:root[data-theme="dark"] .tilt { box-shadow: inset 0 1px 0 rgba(255,255,255,.06), var(--contact); }
.chrome { display: flex; align-items: center; gap: .75rem; height: 2.3rem; padding: 0 .75rem; border-bottom: 1px solid var(--rule-2); }
@media (min-width: 640px) { .chrome { height: 2.7rem; padding: 0 1rem; } }
.lights { display: flex; gap: 6px; }
.lights i { width: 9px; height: 9px; border-radius: 50%; background: var(--rule); transition: background 300ms; }
.frame:hover .lights i:nth-child(1) { background: var(--ka-500); }
.frame:hover .lights i:nth-child(2) { background: #f2c14e; }
.frame:hover .lights i:nth-child(3) { background: var(--em-500); }
.url { flex: 1; display: flex; align-items: center; justify-content: center; gap: .4rem; height: 1.45rem; max-width: 20rem; margin-inline: auto;
  border-radius: 999px; background: var(--paper); border: 1px solid var(--rule); color: var(--ink-2); font-size: .6875rem; letter-spacing: .03em; }
@media (min-width: 640px) { .url { font-size: .75rem; height: 1.6rem; } }
.url::before { content: ""; width: 7px; height: 9px; border: 1.5px solid currentColor; border-radius: 2px 2px 1px 1px; opacity: .6; }
.host.typing::after { content: ""; display: inline-block; width: 1px; height: .95em; background: currentColor; margin-left: 1px; vertical-align: -.12em; animation: blink 1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }
.ext { color: var(--ink-3); font-size: .9rem; width: 1.2rem; text-align: right; transition: color 300ms, transform 400ms var(--ease-ink); }
.frame:hover .ext { color: var(--em-600); transform: translate(2px, -2px); }

.screen { position: relative; display: block; aspect-ratio: 16 / 10; background: #fff; overflow: hidden; }
.shot { position: absolute; inset: 0; display: block; opacity: 0; transform: scale(1); overflow: hidden;
  transition: opacity 900ms ease, transform 900ms ease-out; }
.shot.on { opacity: 1; }
.anim .shot.on { transform: scale(1.035); transition: opacity 900ms ease, transform var(--hold, 4000ms) linear; }
.shot img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 0; transform: scale(var(--zoom, 1)); transform-origin: 50% 0; }
.glare { position: absolute; inset: 0; pointer-events: none; opacity: 0; transition: opacity 400ms;
  background: radial-gradient(55% 55% at var(--gx, 50%) var(--gy, 50%), var(--glare), transparent 70%); }
.anim .frame:hover .glare { opacity: 1; }

/* The frame assembles itself when it scrolls into view */
.anim .project .frame { opacity: 0; transform: translateY(22px); }
.anim .project.in .frame { opacity: 1; transform: none; transition: opacity 800ms ease, transform 1000ms var(--ease-ink); }
.anim .project .chrome > * { opacity: 0; transform: translateY(-6px); }
.anim .project.in .chrome > * { opacity: 1; transform: none; transition: opacity 500ms ease 250ms, transform 500ms var(--ease-ink) 250ms; }
.anim .project .screen { opacity: 0; }
.anim .project.in .screen { opacity: 1; transition: opacity 900ms ease 400ms; }
.anim .project .meta > * { opacity: 0; transform: translateY(8px); }
.anim .project.in .meta > * { opacity: 1; transform: none; transition: opacity 700ms ease 500ms, transform 700ms var(--ease-ink) 500ms; }

/* Re-enactment overlay on the draw screen */
.re { position: absolute; inset: 0; width: 100%; height: 100%; display: none; pointer-events: none; }
.anim .re { display: block; }
.re .cover { fill: #fff; }
.re .gd { fill: none; stroke: #16a34a; stroke-width: 5; stroke-linecap: round; stroke-linejoin: round; }
.re .rv { fill: none; stroke: #fff; stroke-width: 10; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 1 2; stroke-dashoffset: 1; }
.re .c1 { clip-path: inset(0 0 0 0); }
[data-re="guide"] .re .s1 { stroke-dashoffset: 0; transition: stroke-dashoffset 300ms var(--ease-ink) 0ms; }
[data-re="guide"] .re .s2 { stroke-dashoffset: 0; transition: stroke-dashoffset 440ms var(--ease-ink) 440ms; }
[data-re="guide"] .re .s3 { stroke-dashoffset: 0; transition: stroke-dashoffset 900ms var(--ease-ink) 1020ms; }
[data-re="ink"] .re .rv { stroke-dashoffset: 0; }
[data-re="ink"] .re .c1 { clip-path: inset(100% 0 0 0); transition: clip-path 700ms var(--ease-ink); }
[data-re="ink"] .re .c2 { opacity: 0; transition: opacity 300ms ease 550ms; }
[data-re="ink"] .re .gd { opacity: 0; transition: opacity 400ms ease 800ms; }
/* brush kind (万 on the Learn screen): the cover hides the printed glyph, the
   crimson strokes draw with their own per-stroke timing, then the whole
   overlay cross-fades to the capture */
.re.brush .st { fill: none; stroke-linecap: round; stroke-linejoin: round; }
[data-re="brush"] .re .rv { stroke-dashoffset: 0; transition: stroke-dashoffset var(--d, 400ms) var(--ease-ink) var(--t, 0ms); }
[data-re="fade"] .re .rv { stroke-dashoffset: 0; }
[data-re="fade"] .re { opacity: 0; transition: opacity 450ms ease; }
[data-re="done"] .re { display: none; }
/* tabs kind (Choose): stacked board layers cross-fade; a fingertip ring and a
   pressed-tab tint play on the tab being chosen */
.layer { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 0; opacity: 0; transition: opacity 600ms ease 260ms; }
[data-re="tap2"] .l2, [data-re="tap3"] .l2, [data-re="tap3"] .l3, [data-re="rest"] .l2, [data-re="rest"] .l3 { opacity: 1; }
[data-re="idle"] .layer { transition: none; }
.re .ptr, .re .tp { opacity: 0; transform-box: fill-box; transform-origin: center; }
.re.nav .ptr { fill: rgba(255,255,255,.22); stroke: #fff; stroke-width: 2.5; }
.re.nav .tp { fill: #fff; }
[data-re="tap1"] .re .p1, [data-re="tap2"] .re .p2, [data-re="tap3"] .re .p3 { animation: tap 1000ms var(--ease-out) forwards; }
[data-re="tap1"] .re .t1, [data-re="tap2"] .re .t2, [data-re="tap3"] .re .t3 { animation: tabpress 700ms var(--ease-out) forwards; }
@keyframes tap { 0% { opacity: 0; transform: scale(.5); } 30% { opacity: 1; transform: scale(1.08); } 45% { transform: scale(.82); } 65% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(1); } }
@keyframes tabpress { 0% { opacity: 0; transform: scale(.94); } 45% { opacity: .26; transform: scale(.96); } 100% { opacity: 0; transform: scale(1); } }
/* press kind (Quiz): the fingertip lands, the pill compresses, then the verdict:
   emerald ring + emerald text on the chosen pill, the others veiled */
.re.press .ptr { fill: rgba(70,83,95,.14); stroke: #46535f; stroke-width: 2.5; transform: scale(.5); }
[data-re="tap"] .re .ptr, [data-re="press"] .re .ptr { opacity: 1; transform: scale(1); transition: opacity 220ms ease, transform 320ms var(--ease-out); }
[data-re="press"] .re .ptr { transform: scale(.78); transition: transform 140ms var(--ease-out); }
[data-re="right"] .re .ptr { opacity: 0; transform: scale(1); transition: opacity 400ms ease 250ms, transform 200ms var(--ease-out); }
.re.press .pill { transform-box: fill-box; transform-origin: center; transition: transform 180ms var(--ease-out); }
[data-re="press"] .re .pill { transform: scale(.965); }
.re.press .ok { fill: none; stroke: #16a34a; stroke-width: 3; opacity: 0; }
.re.press .okt, .re.press .dim { opacity: 0; }
.re.press .dim { fill: #fff; }
[data-re="right"] .re .ok, [data-re="right"] .re .okt { opacity: 1; transition: opacity 260ms ease; }
[data-re="right"] .re .dim { opacity: .55; transition: opacity 450ms ease 80ms; }

/* ---------- caption + screen tabs ---------- */
.meta { display: grid; gap: 1rem; padding-inline: .25rem; }
@media (min-width: 768px) { .meta { grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 2rem; } }
.meta h3 { font-size: clamp(1.85rem, 3.4vw, 2.6rem); line-height: 1.1; letter-spacing: -.01em; }
.blurb { margin-top: .4rem; color: var(--ink-2); font-size: 1rem; max-width: 52ch; }
.tabs { display: flex; gap: .5rem; }
.tab { flex: 1 1 0; min-width: 0; display: grid; gap: .45rem; padding: .35rem 0 0; border: 0; background: none; color: var(--ink-3); cursor: pointer;
  font-family: var(--mono); font-size: .625rem; letter-spacing: .12em; text-transform: uppercase; text-align: left; transition: color 300ms; }
@media (min-width: 768px) { .tab { flex: 0 0 5.25rem; } }
.tab:hover, .tab.on { color: var(--ink); }
.tab .bar { position: relative; display: block; height: 2px; border-radius: 2px; background: var(--rule); overflow: hidden; }
.tab .bar::after { content: ""; position: absolute; inset: 0; background: var(--em-500); transform: scaleX(0); transform-origin: 0 50%; }
.tab.on .bar::after { transform: scaleX(1); }
.anim .tab.on .bar::after { transform: scaleX(0); animation: fill var(--hold, 4000ms) linear forwards; }
.anim .paused .tab.on .bar::after { animation-play-state: paused; }
@keyframes fill { to { transform: scaleX(1); } }
.tab:focus-visible { outline-offset: 2px; }

/* ---------- work history ----------
   A plain ordered list; the spine, dots and bead are decoration (pseudo-elements
   and an aria-hidden node), so a screen reader hears dates, role, employer, line. */
.history { position: relative; padding-top: clamp(3rem, 8vw, 6rem); }
.history .w2 { left: auto; right: -22vmax; bottom: -8vmax; width: 50vmax; opacity: .9; }
.history .section-bar { margin-bottom: clamp(1rem, 3vw, 2rem); }
.tl { --sx: 7px; --dot: calc(1.4rem + .55em); }
@media (min-width: 768px) { .tl { --sx: calc(9.5rem + 1.5rem); --dot: calc(1.9rem + .62em); } }
.tl { position: relative; }
.tl::before { content: ""; position: absolute; left: var(--sx); top: calc(var(--dot) + 5px); bottom: 0; width: 1px; background: linear-gradient(var(--em-500), var(--em-500) 70%, var(--ka-500));
  transform: scaleY(var(--draw, 1)); transform-origin: 50% 0; }
.tl::after { content: ""; position: absolute; left: var(--sx); top: calc(var(--dot) + 5px); bottom: 0; width: 1px; background: var(--rule); z-index: -1; } /* the faint rail the ink draws over */
.bead { position: absolute; left: var(--sx); top: 0; width: 9px; height: 9px; margin-left: -4px; border-radius: 50%; background: var(--ka-500);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--ka-500) 16%, transparent), 0 0 22px var(--ka-500); opacity: 0; transition: opacity 400ms;
  transform: translate3d(0, var(--by, 0px), 0); will-change: transform; pointer-events: none; }
.bead::before { content: ""; position: absolute; left: 4px; bottom: 100%; width: 1px; height: 48px; background: linear-gradient(transparent, var(--ka-500)); }
.bead.on { opacity: 1; }
.ev { position: relative; display: grid; gap: .35rem 0; padding: 1.4rem 0 1.4rem 2rem; }
@media (min-width: 768px) { .ev { grid-template-columns: 9.5rem minmax(0, 1fr); gap: 0 3rem; padding: 1.9rem 0; align-items: start; } }
.ev::before { content: ""; position: absolute; left: var(--sx); top: var(--dot); width: 11px; height: 11px; margin-left: -5px; border-radius: 50%;
  background: var(--em-500); border: 1.5px solid var(--em-500); box-shadow: 0 0 0 4px var(--paper);
  transition: transform 450ms var(--ease-pop), background-color 300ms, border-color 300ms, box-shadow 300ms; }
.ev.edu::before { background: var(--paper); }                       /* a different kind of event: hollow */

.when { font-family: var(--display); font-size: clamp(1.55rem, 3vw, 2.4rem); line-height: 1; color: var(--ink-2); letter-spacing: -.01em; font-variation-settings: "opsz" 36; transition: color 350ms; display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap; }
.when .to { font-family: var(--mono); font-size: .6875rem; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3); }
.what { transition: transform 500ms var(--ease-ink); }
.what h3 { font-size: clamp(1.25rem, 2.1vw, 1.6rem); line-height: 1.2; }
.what h3 .then { color: var(--ink-3); }
.what h3 .arrow-s { color: var(--ka-500); }
.where { margin-top: .25rem; font-family: var(--mono); font-size: .6875rem; letter-spacing: .16em; text-transform: uppercase; color: var(--em-600); }
.ev.edu .where { color: var(--ink-2); }
.note { margin-top: .5rem; color: var(--ink-2); font-size: .9375rem; max-width: 56ch; }
.ev.near .when { color: var(--em-600); }
.ev.near::before { background: var(--ka-500); border-color: var(--ka-500); transform: scale(1.45); box-shadow: 0 0 0 4px var(--paper), 0 0 0 9px color-mix(in srgb, var(--ka-500) 18%, transparent); }
.ev.edu.near::before { background: var(--paper); }
.ev.near .what { transform: translateX(8px); }
.ev.near .what h3 .arrow-s { color: var(--em-500); }
.anim .ev { opacity: 0; transform: translateY(14px); }
.anim .ev.in { opacity: 1; transform: none; transition: opacity 700ms ease, transform 900ms var(--ease-ink); }
.anim .ev::before { transform: scale(0); } .anim .ev.in::before { transform: none; transition-delay: 150ms; }
.anim .ev.in.near::before { transform: scale(1.45); }

/* ---------- footer ---------- */
.site-foot { margin-top: clamp(4rem, 10vw, 7rem); padding-block: 2rem 2.5rem; border-top: 1px solid var(--rule); display: grid; gap: 1.25rem; }
@media (min-width: 768px) { .site-foot { grid-template-columns: auto minmax(0, 1fr); align-items: baseline; gap: 3rem; } }
.li { display: inline-block; font-family: var(--display); font-size: 1.6rem; color: var(--ink); background-size: 0 1.5px; }
.li:hover, .li:focus-visible { color: var(--em-600); background-size: 100% 1.5px; }
.site-foot .small { max-width: 60ch; }
@media (min-width: 768px) { .site-foot .small { justify-self: end; text-align: right; } }
.site-foot .small a { color: var(--ink-2); }

/* Reduced motion: belt and braces beyond the missing .anim class */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
  .light { display: none; }
}
