/* Fonts are self-hosted and preloaded (see each page's <head>) so they are ready at first paint: no network wait,
   no fallback-font swap that re-flows text and the menu in the middle of a page change. */
@font-face { font-family: 'Google Sans Flex'; font-style: oblique 0deg 10deg; font-weight: 300 500; font-display: block;
             src: url(assets/fonts/gsf-latin.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; }
@font-face { font-family: 'Google Sans Flex'; font-style: oblique 0deg 10deg; font-weight: 300 500; font-display: block;
             src: url(assets/fonts/gsf-latin-ext.woff2) format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: 'Google Sans Flex'; font-style: oblique 0deg 10deg; font-weight: 300 500; font-display: block;
             src: url(assets/fonts/gsf-symbols.woff2) format('woff2'); unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AE, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F8D0-1F8D8, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FAC6, U+1FAC8, U+1FACC-1FADD, U+1FADF-1FAEB, U+1FAEF-1FAFA, U+1FB00-1FBFF; }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: block;
             src: url(assets/fonts/inter-latin.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; }

/* v3 shared: tokens, base, header actions (theme toggle + nav), theme wipe.
   Page-specific layout stays inline in each page. */
:root {
  --bg: #fff;
  --brand: #ff3c15;
  --ink: #141414;
  --text: #272727;
  --muted: #676767;
  --chip: #f2f2f2;
  --chip-ink: #2f2f2f;
  --nav-track: #efefef;
  --active-bg: #141414;   /* selected nav pill, primary button */
  --active-ink: #fff;
  --label-bg: #fff;       /* project card labels */
  --label-ink: #000;
  --sheet: #d9d9d9;       /* resume placeholder */
  /* case-study text + surfaces */
  --cs-title: #121212;
  --cs-body: #444;
  --cs-sub: #797979;
  --cs-note: #848484;
  --cs-faint: #5f5f5f;
  --card: #efefef;
  --line: #e1e1e1;
  --pill-line: #cfcfcf;
  --num-a: #ffded8;       /* gradient numerals */
  --num-b: #fff;
  --info: #257def;
  --thumb: rgba(0,0,0,.22);
  --thumb-hover: rgba(0,0,0,.4);
  --sans: "Google Sans Flex", "Inter", system-ui, sans-serif;
  --pad: 20px;            /* page gutter + top/bottom inset (user: half of Figma's 40) */
}
:root[data-theme="dark"] {
  --bg: #0f0f0f;
  --brand: #ff4d29;
  --ink: #f4f4f4;
  --text: #e6e6e6;
  --muted: #9a9a9a;
  --chip: #1d1d1d;
  --chip-ink: #d2d2d2;
  --nav-track: #1d1d1d;
  --active-bg: #f4f4f4;
  --active-ink: #141414;
  --label-bg: #141414;
  --label-ink: #f4f4f4;
  --sheet: #262626;
  --cs-title: #f2f2f2;
  --cs-body: #c4c4c4;
  --cs-sub: #8d8d8d;
  --cs-note: #8a8a8a;
  --cs-faint: #9a9a9a;
  --card: #1a1a1a;
  --line: #2a2a2a;
  --pill-line: #3a3a3a;
  --num-a: #5a2418;
  --num-b: #0f0f0f;
  --info: #4c95f5;
  --thumb: rgba(255,255,255,.22);
  --thumb-hover: rgba(255,255,255,.4);
}
@media (max-width: 900px) { :root { --pad: 16px; } }
@media (max-width: 600px) { :root { --pad: 10px; } }

/* Theme switch: only the BACKGROUND wipes top → bottom (.bg-wipe, behind everything).
   Anything marked [data-wipe] re-colours as the wipe edge passes it (--d delay set by site.js). */
.bg-wipe { position: fixed; inset: 0; z-index: -1; pointer-events: none; will-change: transform; }
.theming .wv {
  transition: background-color .3s, color .3s, border-color .3s !important; transition-delay: var(--d, 0ms) !important; }

* { box-sizing: border-box; margin: 0; padding: 0; }
/* Lenis smooth scrolling (site.js) */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }
html { background: var(--bg); }
/* scrollbar: no track or arrows, just a slim grey thumb (same as v2) */
@supports not selector(::-webkit-scrollbar) {   /* Firefox — in Chrome these standard props would override the rules below */
  html { scrollbar-width: thin; scrollbar-color: var(--thumb) transparent; }
}
::-webkit-scrollbar { width: 6px; height: 6px; background: transparent; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-button { display: none; }
::-webkit-scrollbar-thumb { background: var(--thumb); border-radius: 6px; }
::-webkit-scrollbar-thumb:hover { background: var(--thumb-hover); }
body { background: transparent; font-family: var(--sans); color: var(--text); -webkit-font-smoothing: antialiased; overflow-x: clip; }
a { color: inherit; text-decoration: none; }

/* Header actions: theme toggle + nav pill */
.actions { flex: none; display: flex; align-items: center; gap: 8px; }
.nav { position: relative; contain: layout; flex: none; display: flex; align-items: center; padding: 2px; background: var(--nav-track); border-radius: 44px; }
.nav a { position: relative; z-index: 1; font-size: 16px; line-height: 11px; letter-spacing: -.32px; color: var(--muted); padding: 13px 16px; border-radius: 40px; transition: color .2s, background .2s; }
.nav a:hover { color: var(--ink); }
.nav a[aria-current="page"] { color: var(--active-ink); }
.nav a[aria-current="page"] { background: none; }
/* the pill: a ::before behind the active label — always exactly the label's size (even while fonts load) */
.nav a[aria-current="page"]::before { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: 40px; background: var(--active-bg); transform: translateX(var(--dx, 0px)); }
.theming .wv::before { transition: background-color .3s; transition-delay: var(--d, 0ms) !important; }
/* rigid slide, quick (.24s) with a short ease-in and a long ease-out so it never feels like it is waiting: position only — no stretch, no resize */
.nav a.go::before { transition: transform .24s cubic-bezier(.4, 0, .2, 1); }
.nav.go a { transition: color .24s cubic-bezier(.4, 0, .2, 1); }
.nav.pre a[aria-current="page"] { color: var(--muted); }      /* arrival: the previous label still reads as the active one */
.nav.pre a.prev { color: var(--active-ink); }
/* phones: 4 menu items + toggle must fit inside the gutter */
@media (max-width: 600px) { .nav a { padding: 13px 11px; } }
@media (max-width: 360px) { .nav a { padding: 13px 8px; font-size: 14px; } .actions { gap: 6px; } }

/* Theme toggle (v2's sun/moon): round button left of the menu, same height + track colour.
   Shows the current mode's icon; on hover the whole button flips to the other mode's look and icon. */
.theme-toggle { position: relative; flex: none; width: 41px; height: 41px; border: 0; border-radius: 50%;
                background: var(--nav-track); color: var(--muted); cursor: pointer; transition: background .25s, color .25s; }
.theme-toggle:hover, .theme-toggle:focus-visible { background: var(--active-bg); color: var(--active-ink); outline: none; }
.theme-toggle svg { position: absolute; left: 10.5px; top: 10.5px; width: 20px; height: 20px; transition: opacity .25s, transform .35s cubic-bezier(.2,.7,.2,1); }
/* light: sun shown, moon on hover — dark: the reverse */
html:not([data-theme="dark"]) .theme-toggle .ic-moon,
html[data-theme="dark"] .theme-toggle .ic-sun { opacity: 0; transform: rotate(-45deg) scale(.6); }
html:not([data-theme="dark"]) .theme-toggle:is(:hover, :focus-visible) .ic-sun,
html[data-theme="dark"] .theme-toggle:is(:hover, :focus-visible) .ic-moon { opacity: 0; transform: rotate(45deg) scale(.6); }
html:not([data-theme="dark"]) .theme-toggle:is(:hover, :focus-visible) .ic-moon,
html[data-theme="dark"] .theme-toggle:is(:hover, :focus-visible) .ic-sun { opacity: 1; transform: none; }

/* Page change: "frost" — the old page goes soft and fades, the new one sharpens out of the blur.
   Cross-document View Transitions (Chrome/Edge 126+, Safari 18.2+); site.js plays the same keyframes on <body>
   where those can't run (Firefox, file://). The theme toggle + menu keep their own name so they stay put. */
@view-transition { navigation: auto; }
.actions { view-transition-name: site-actions; }
/* The menu never takes part in the page animation: it is a fixed element (.site-menu), excluded from the root snapshot,
   and shown as-is (no cross-fade, no morph) while the page content frosts out and in. */
::view-transition-group(site-actions) { animation: none; }
::view-transition-old(site-actions) { display: none; }
::view-transition-new(site-actions) { animation: none; }
.site-menu { position: fixed; top: var(--pad); right: var(--pad); z-index: 40; }
::view-transition-old(root), ::view-transition-new(root) { mix-blend-mode: normal; }
/* Frost: the old page goes soft and fades, the new one sharpens out of a 10px blur and settles up 10px. (Blur is the costly part, so
   it is kept small; fonts, scripts and images are now local + preloaded, which is what removed the glitches.) */
::view-transition-old(root) { animation: frost-out .28s cubic-bezier(.4, 0, .6, 1) both; }
::view-transition-new(root) { animation: frost-in .5s cubic-bezier(.2, .8, .2, 1) .03s both; }
@keyframes frost-out { to { opacity: 0; filter: blur(10px); } }
@keyframes frost-in { from { opacity: 0; filter: blur(10px); transform: translate3d(0, 10px, 0); } }

/* fallback (site.js): same keyframes on <body> */
/* the animation goes on the page content (.pt), never on <body>: a filter on body would also blur the fixed menu and re-anchor it */
html.pt-out .pt { animation: frost-out .28s cubic-bezier(.4, 0, .6, 1) both; }
html.pt-in .pt  { animation: frost-in .5s cubic-bezier(.2, .8, .2, 1) both; }
html.pt-out .pt, html.pt-in .pt { will-change: opacity, filter, transform; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*), html.pt-out .pt, html.pt-in .pt { animation: none !important; }
}
