/* =================================================================
   site.css — Arthur Ottevaere · portfolio
   -----------------------------------------------------------------
   THE CHARTER, IN ONE PLACE.

   Colour      paper / ink / one signature orange. Nothing else.
   Type        Archivo (display + text, variable width 62–125),
               Instrument Serif italic (places, accents, quotes),
               Geist Mono (dates, labels, numbers).
   Motifs      · the orange dot: it ends the big titles, it opens the
                 intro, it is the cursor of the timeline;
               · text that fills or rises as you scroll;
               · the width of the name, which breathes.

   Everything is driven by the custom properties in :root below, and
   the dark theme only re-declares those — no component is styled
   twice. Content lives in data.js; this file never mentions a
   project, a date or a name.
   ================================================================= */

/* ─── 1 · Tokens ────────────────────────────────────────────────── */
:root{
  /* surfaces + ink */
  --paper:#F6F4EF;
  --paper-2:#FCFBF8;
  --ink:#101014;
  --ink-2:#33323A;
  --muted:#67656C;
  --faint:#A6A39C;
  --rule:#DDD9D0;
  --rule-2:#CBC6BC;

  /* the one colour */
  --accent:#FF4A12;
  --accent-ink:#C83500;        /* same orange, darkened for small text on paper */
  --on-accent:#101014;         /* what sits ON the orange */
  --ok:#1F8A4C;

  /* the dark form card (contact) keeps its own pair, so it stays a
     dark object on a light page and a lifted object on a dark one */
  --form-bg:#101014;
  --form-fg:#F6F4EF;
  --form-line:rgba(246,244,239,.20);

  /* borrowed logos (schools, companies, tools) are pulled down to one ink
     silhouette so they never fight the charter; --logo-mono-on is what a
     logo becomes on the step you are actually on, and --logo-flip is for
     marks that are already a single black shape, like Notion's. */
  --logo-mono:grayscale(1) brightness(0);
  --logo-mono-on:none;
  --logo-flip:none;

  --grain: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='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .15 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  --lift:0 1px 0 rgba(16,16,20,.04), 0 26px 50px -30px rgba(16,16,20,.34);

  --f-display:'Archivo','Helvetica Neue',Arial,sans-serif;
  --f-voice:'Instrument Serif',Georgia,'Times New Roman',serif;
  --f-mono:'Geist Mono',ui-monospace,SFMono-Regular,Menlo,monospace;

  --ease:cubic-bezier(.2,.7,.2,1);
  --ease-expo:cubic-bezier(.16,1,.3,1);
  --ease-io:cubic-bezier(.65,0,.35,1);
  --ease-pill:cubic-bezier(.32,.72,0,1);

  /* a press: how far a thing gives under the finger, by its size, and how
     fast it gets there. Text has nothing to scale, so it dims instead.
     --press-o stays 1 until reduced motion trades the movement for a dim. */
  --press:.97;
  --press-sm:.95;
  --press-lg:.985;
  --press-fade:.6;
  --press-o:1;
  --press-t:90ms;

  --shell:1280px;
  /* The margin every page keeps from the edge of the screen. The low end is
     what a phone gets: 20px read as text pinned to the bezel. On a turned
     iPhone the Dynamic Island is wider than that, so the safe area wins. */
  --gutter:max(clamp(24px,4.6vw,48px),env(safe-area-inset-left,0px),env(safe-area-inset-right,0px));
  --nav-h:calc(60px + env(safe-area-inset-top,0px));

  color-scheme:light;
}
html[data-theme="dark"]{
  --paper:#0E0E11;
  --paper-2:#17171B;
  --ink:#ECEAE4;
  --ink-2:#C6C4BE;
  --muted:#96949C;
  --faint:#5E5C63;
  --rule:#2A2A31;
  --rule-2:#3B3B44;

  --accent:#FF6E40;
  --accent-ink:#FF8A62;
  --on-accent:#0E0E11;
  --ok:#4ADE80;

  --form-bg:#1C1C21;
  --form-fg:#ECEAE4;
  --form-line:rgba(236,234,228,.18);

  /* on a dark page a navy or black logo would simply vanish, so every mark
     turns white here — including the one on the step you are on. */
  --logo-mono:grayscale(1) brightness(0) invert(1);
  --logo-mono-on:grayscale(1) brightness(0) invert(1);
  --logo-flip:invert(1);

  --grain: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='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .07 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  --lift:0 1px 0 rgba(255,255,255,.03), 0 26px 50px -30px rgba(0,0,0,.85);

  color-scheme:dark;
}
/* Increase Contrast: the two quietest inks and the two hairlines step up, and
   --faint becomes something you can read. Ratios are in DESIGN.md §13. */
@media (prefers-contrast:more){
  :root{--muted:#4A4850;--faint:#5C5A61;--rule:#B8B3A8;--rule-2:#8F8A80}
  html[data-theme="dark"]{--muted:#BDBBC3;--faint:#A2A0A8;--rule:#4A4A54;--rule-2:#6E6E7A}
}

/* ─── 2 · Base ──────────────────────────────────────────────────── */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}
html,body{margin:0;padding:0}
body{
  min-height:100vh;
  background-color:var(--paper);
  background-image:var(--grain);
  color:var(--ink);
  font-family:var(--f-display);
  font-size:17px;
  font-weight:400;
  font-stretch:100%;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}
html,body{overflow-x:hidden;overflow-x:clip}   /* hidden is the fallback for Safari < 16 */
::selection{background:var(--accent);color:var(--on-accent)}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}
a,button{-webkit-tap-highlight-color:transparent}
input,textarea{font:inherit;color:inherit}
img,svg{display:block;max-width:100%}
picture{display:contents}
h1,h2,h3,h4,p,figure,blockquote,dl,dd{margin:0}
ul,ol{margin:0;padding:0;list-style:none}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:3px}

.shell{width:100%;max-width:var(--shell);margin:0 auto;padding:0 var(--gutter)}
.mono{font-family:var(--f-mono)}
.voice{font-family:var(--f-voice);font-style:italic;font-weight:400}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.tabular{font-variant-numeric:tabular-nums}

/* Lenis (smooth scroll) */
html.lenis,html.lenis body{height:auto}
.lenis.lenis-smooth{scroll-behavior:auto!important}
.lenis.lenis-stopped{overflow:clip}
.lenis [data-lenis-prevent]{overscroll-behavior:contain}

/* ─── 3 · Type utilities ────────────────────────────────────────── */
.eyebrow{
  display:inline-block;font-family:var(--f-mono);font-size:11.5px;font-weight:400;
  letter-spacing:.09em;text-transform:uppercase;color:var(--muted);line-height:1.3;
}
.tag{
  font-family:var(--f-mono);font-size:12px;letter-spacing:.075em;text-transform:uppercase;
  color:var(--muted);line-height:1.3;
}
.tag b{font-weight:400;color:var(--ink)}
.dot{
  display:inline-block;width:.17em;height:.17em;border-radius:50%;
  background:var(--accent);margin-left:.06em;flex:none;
}
/* the last word and its dot, kept on one line */
.words .wn{white-space:nowrap}

/* The disc that catches the orange page at the end of the home scene, and
   shrinks into the dot of the projects heading.

   It is a separate, always-composited layer rather than a transform on the
   dot itself. Scaling the dot means repainting it inside the page's own
   layer on every frame, and WebKit does not invalidate the previous, larger
   circle when the page is scrolling at the same time: what is left behind is
   a comb of orange ring fragments under the dot. Its own layer has nothing
   to invalidate — the compositor simply rescales a texture that was
   rasterised once, at the large size, so it also stays crisp all the way
   down. --catch is the scale, written by home.jsx; 0 is off. */
#featured-dot{position:relative}
#featured-dot::after{
  content:"";position:absolute;left:50%;top:50%;
  width:120px;height:120px;margin:-60px 0 0 -60px;
  border-radius:50%;background:var(--accent);pointer-events:none;
  transform:scale(var(--catch,0));
  transform-origin:50% 50%;
  will-change:transform;
}
.sec-title{
  font-weight:750;font-stretch:108%;font-size:clamp(38px,5.6vw,86px);
  line-height:.95;letter-spacing:-.04em;text-wrap:balance;
}
.page-title{
  font-weight:800;font-stretch:112%;font-size:clamp(52px,9.4vw,140px);
  line-height:.86;letter-spacing:-.045em;
}
.lede{
  font-size:clamp(17px,1.4vw,20px);line-height:1.55;color:var(--muted);
  max-width:54ch;text-wrap:pretty;
}
.link-arrow{
  display:inline-flex;align-items:center;gap:8px;font-size:15px;font-weight:550;
  padding-bottom:4px;border-bottom:1px solid var(--rule-2);
  transition:border-color .25s ease,color .25s ease,opacity .18s ease;
}
.link-arrow svg{width:14px;height:14px;transition:transform .2s var(--ease)}
@media (hover:hover) and (pointer:fine){
  .link-arrow:hover{border-color:var(--ink)}
  .link-arrow:hover svg{transform:translate(3px,-3px)}
}

/* buttons */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  height:46px;padding:0 20px;border-radius:999px;
  font-size:15px;font-weight:550;letter-spacing:-.005em;
  border:1px solid transparent;white-space:nowrap;
  transition:background-color .25s ease,color .25s ease,border-color .25s ease,transform .18s var(--ease),opacity .18s ease;
}
.btn svg{width:15px;height:15px;transition:transform .2s var(--ease)}
.btn-ink{background:var(--ink);color:var(--paper);border-color:var(--ink)}
@media (hover:hover) and (pointer:fine){
  .btn-ink:hover{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
  .btn-ink:hover svg{transform:translate(3px,-3px)}
}
.btn-line{border-color:var(--rule-2);color:var(--ink)}
@media (hover:hover) and (pointer:fine){.btn-line:hover{border-color:var(--ink);background:var(--paper-2)}}
.btn-accent{background:var(--accent);color:var(--on-accent);border-color:var(--accent)}
@media (hover:hover) and (pointer:fine){.btn-accent:hover{background:var(--ink);border-color:var(--ink);color:var(--paper)}}
.btn-sm{height:38px;padding:0 15px;font-size:13.5px;gap:7px}
.btn-sm svg{width:13px;height:13px}

/* ─── 4 · Reveals ───────────────────────────────────────────────────
   Every animated block carries .rv (+ a variant) and gets .in when it
   enters the viewport. The hidden state only exists when JS is on, so
   the page is readable no matter what. --d delays a block, --i staggers
   the words inside a title. */
.js .rv-up{opacity:0;transform:translateY(26px)}
.js .rv-up.in{opacity:1;transform:none;transition:opacity .8s var(--ease-expo) var(--d,0s),transform 1.05s var(--ease-expo) var(--d,0s)}
.js .rv-fade{opacity:0}
.js .rv-fade.in{opacity:1;transition:opacity .9s ease var(--d,0s)}
.js .rv-media{clip-path:inset(100% 0 0 0 round var(--r,18px))}
.js .rv-media img,.js .rv-media svg{transform:scale(1.12)}
.js .rv-media.in{clip-path:inset(0 0 0 0 round var(--r,18px));transition:clip-path 1.15s var(--ease-expo) var(--d,0s)}
.js .rv-media.in img,.js .rv-media.in svg{transform:none;transition:transform 1.5s var(--ease-expo) var(--d,0s)}

.words{display:block}
/* Give descenders and glyph overhangs room inside the reveal mask.
   Negative margins preserve the heading spacing and line wrapping. */
.words .w{display:inline-block;overflow:hidden;vertical-align:bottom;padding:.18em .1em .28em;margin:-.18em -.1em -.28em}
.js .words .wi{display:inline-block;transform:translateY(160%)}
.js .words.in .wi{transform:none;transition:transform .95s var(--ease-expo) calc(var(--d,0s) + var(--i,0) * 42ms)}

@media (prefers-reduced-motion:reduce){
  .js .rv-up,.js .rv-fade,.js .rv-media,.js .rv-media > img,.js .rv-media > svg,.js .words .wi{
    opacity:1!important;transform:none!important;clip-path:none!important;transition:none!important;
  }
}

/* A page stages itself once. On the way back through history it is simply
   there (settleReveals in lib.jsx does the rest, before it paints), and on a
   second visit by link it shortens to one quick arrival. */
.page-wrap.is-back{animation:none}
.js .is-back .words.in .wi{transition:none}
.js .is-again :is(.rv-up.in,.rv-fade.in,.rv-media.in,.rv-media.in img,.rv-media.in svg,.words.in .wi){transition-duration:.4s;transition-delay:0s}

/* ─── 5 · Boot splash ───────────────────────────────────────────────
   Orange screen with the greetings; when the site is ready it closes
   into the dot of the name (or the logo's dot on the other pages). */
/* Not the home page: the markup is shared by every page, so it is hidden
   before it can paint and removed by the script right after. */
.no-splash #boot{display:none}
#boot{
  position:fixed;inset:0;z-index:100;
  background:var(--accent);color:var(--on-accent);
  display:grid;place-items:center;
  will-change:clip-path;
}
#boot.closing{transition:clip-path .95s var(--ease-io)}
#boot.fade{opacity:0;transition:opacity .45s ease}
.boot-hello{
  font-family:var(--f-display);font-weight:800;font-stretch:112%;
  font-size:clamp(46px,9vw,132px);letter-spacing:-.045em;line-height:1;
  display:inline-flex;align-items:baseline;
  transition:opacity .18s ease,transform .18s ease;
}
.boot-hello .bd{display:inline-block;width:.17em;height:.17em;border-radius:50%;background:var(--on-accent);margin-left:.06em}
.boot-meta{
  position:absolute;left:var(--gutter);right:var(--gutter);bottom:max(22px,env(safe-area-inset-bottom,0px));
  display:flex;justify-content:space-between;gap:16px;
  font-family:var(--f-mono);font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;
  opacity:.66;transition:opacity .18s ease;
}
#boot.leaving .boot-hello{opacity:0;transform:translateY(-6px)}
#boot.leaving .boot-meta{opacity:0}
@media (prefers-reduced-motion:reduce){ .boot-hello{transition:none} }

/* ─── 6 · Nav ───────────────────────────────────────────────────── */
.nav{
  position:fixed;top:max(14px,env(safe-area-inset-top,0px));left:50%;transform:translateX(-50%);z-index:60;
  display:flex;align-items:center;gap:4px;padding:6px;
  border-radius:999px;background:var(--ink);color:var(--paper);
  box-shadow:0 14px 34px -20px rgba(0,0,0,.55);
  max-width:calc(100vw - 24px);
  will-change:transform;
  view-transition-name:nav;
  transition:transform .24s var(--ease-expo),opacity .2s ease,background-color .3s ease,color .3s ease;
}
.nav.hidden{transform:translate(-50%,-140%);opacity:0;pointer-events:none}
.nav.hidden:focus-within{transform:translateX(-50%);opacity:1;pointer-events:auto}
.nav-logo{display:inline-flex;align-items:center;height:36px;padding:0 12px 0 11px;color:inherit;transition:transform .18s var(--ease),opacity .18s ease}
.nav-logo svg{height:16px;width:auto}
.nav-links{position:relative;isolation:isolate;display:grid;grid-auto-flow:column;grid-auto-columns:1fr;align-items:center;gap:2px}
/* The travelling pill is exactly one tab wide. That width has to come from the
   number of tabs — it was written as a literal /3, which silently broke the
   moment a fourth one was added. --nav-n is set by Nav from NAV.length. */
.nav-active{
  position:absolute;inset:0 auto 0 0;border-radius:999px;
  width:calc((100% - (var(--nav-n,3) - 1) * 2px) / var(--nav-n,3));
  pointer-events:none;z-index:-1;
  will-change:transform;
  transition:transform .28s var(--ease-pill),opacity .18s ease;
}
.nav-active::before{
  content:"";position:absolute;inset:2px;border-radius:inherit;
  background:linear-gradient(180deg,color-mix(in oklab,var(--paper) 13%,transparent),color-mix(in oklab,var(--paper) 7%,transparent));
  box-shadow:inset 0 1px 0 color-mix(in oklab,var(--paper) 16%,transparent),0 2px 5px rgba(0,0,0,.08);
}
@media (prefers-reduced-motion:reduce){.nav-active{transition:none}}
.nav-link{
  position:relative;display:inline-flex;align-items:center;justify-content:center;gap:7px;height:36px;padding:0 13px;
  border-radius:999px;font-size:14.5px;font-weight:500;letter-spacing:-.005em;
  color:color-mix(in oklab,var(--paper) 66%,transparent);
  transition:color .18s ease,transform .14s var(--ease-expo),opacity .18s ease;
}
@media (hover:hover) and (pointer:fine){.nav-link:hover{color:var(--paper)}}
.nav-link:focus-visible{outline:2px solid var(--accent);outline-offset:-3px}
.nav-link.on{color:var(--paper)}
.nav-sep{width:1px;height:20px;background:color-mix(in oklab,var(--paper) 16%,transparent);margin:0 5px;flex:none}
.nav-icon{
  width:36px;height:36px;border-radius:50%;display:grid;place-items:center;color:inherit;
  transition:background-color .22s ease,transform .18s var(--ease),opacity .18s ease;
}
@media (hover:hover) and (pointer:fine){.nav-icon:hover{background:color-mix(in oklab,var(--paper) 12%,transparent)}}
.nav-cv-wrap{position:relative;display:inline-flex}
.nav-cv{
  height:36px;padding:0 15px;border-radius:999px;background:var(--accent);color:var(--on-accent);
  font-size:14px;font-weight:600;display:inline-flex;align-items:center;gap:7px;
  transition:background-color .22s ease,color .22s ease,transform .18s var(--ease),opacity .18s ease;
}
.nav-cv svg{width:14px;height:14px;transition:transform .2s var(--ease)}
@media (hover:hover) and (pointer:fine){
  .nav-cv:hover{background:var(--paper);color:var(--ink)}
  .nav-cv:hover svg{transform:translateY(2px)}
}
.cv-pop{
  position:absolute;top:calc(100% + 10px);right:0;z-index:70;min-width:170px;padding:6px;
  display:grid;gap:2px;border-radius:16px;background:var(--paper-2);color:var(--ink);
  border:1px solid var(--rule);box-shadow:var(--lift);
  /* it grows out of the button that opened it, and leaves faster than it came.
     Transitions, not keyframes, so a second click can turn it around midway. */
  transform-origin:top right;
  visibility:hidden;opacity:0;transform:translateY(-6px) scale(.97);
  transition:opacity .12s ease,transform .12s ease,visibility 0s linear .12s;
}
.cv-pop.open{
  visibility:visible;opacity:1;transform:none;
  transition:opacity .18s var(--ease),transform .18s var(--ease);
}
@media (prefers-reduced-motion:reduce){.cv-pop{transform:none}}
.cv-pop a{display:flex;align-items:center;gap:10px;padding:9px 11px;border-radius:11px;font-size:14px;transition:background-color .16s ease,opacity .18s ease}
@media (hover:hover) and (pointer:fine){.cv-pop a:hover{background:color-mix(in oklab,var(--ink) 7%,transparent)}}
.cv-pop .lang{font-family:var(--f-mono);font-size:11px;color:var(--muted);min-width:22px}
.nav-burger{display:none}

/* mobile: the pill keeps the logo + CV and opens a full-screen menu */
@media (max-width:860px){
  .nav{left:max(12px,env(safe-area-inset-left,0px));right:max(12px,env(safe-area-inset-right,0px));transform:none;max-width:none;justify-content:space-between;gap:8px}
  .nav.hidden{transform:translateY(-140%)}
  .nav-links,.nav-sep,.nav-theme{display:none}
  .nav-burger{display:grid;place-items:center;width:36px;height:36px;border-radius:50%;color:inherit;transition:background-color .22s ease,transform .18s var(--ease),opacity .18s ease}
  .nav-burger svg{width:19px;height:19px}
}
@media (max-width:860px) and (hover:hover) and (pointer:fine){
  .nav-burger:hover{background:color-mix(in oklab,var(--paper) 12%,transparent)}
}
.nav-right{display:flex;align-items:center;gap:4px}

/* one icon taking over from another (Swap in app.jsx) */
.swap{display:grid;place-items:center}
.swap>svg{grid-area:1/1;transition:opacity .2s ease,transform .2s var(--ease),filter .2s ease}
.swap>svg:not(.on){opacity:0;transform:scale(.8);filter:blur(2px)}
@media (prefers-reduced-motion:reduce){.swap>svg:not(.on){transform:none;filter:none}}

/* a check that draws itself, stroke first. pathLength="1" on Icon.Check is what
   lets one number fit any size. */
.draw path{stroke-dasharray:1 2;stroke-dashoffset:1;animation:draw .32s var(--ease) .08s forwards}
@keyframes draw{to{stroke-dashoffset:0}}
@media (prefers-reduced-motion:reduce){.draw path{animation:none;stroke-dashoffset:0}}

.menu{
  position:fixed;inset:0;z-index:59;background:var(--paper);
  display:flex;flex-direction:column;justify-content:center;gap:6px;
  padding:calc(var(--nav-h) + 40px) var(--gutter) max(40px,env(safe-area-inset-bottom,0px));
  clip-path:inset(0 0 100% 0);transition:clip-path .6s var(--ease-expo);
}
.menu.open{clip-path:inset(0 0 0 0)}
.menu-link{
  display:flex;align-items:baseline;gap:14px;padding:10px 0;
  font-weight:800;font-stretch:110%;font-size:clamp(40px,12vw,72px);
  letter-spacing:-.04em;line-height:1;
  border-bottom:1px solid var(--rule);
  opacity:0;transform:translateY(24px);
}
.menu.open .menu-link{opacity:1;transform:none;transition:opacity .5s ease calc(120ms + var(--i)*60ms),transform .7s var(--ease-expo) calc(120ms + var(--i)*60ms)}
.menu-link .n{font-family:var(--f-mono);font-size:12px;font-weight:400;letter-spacing:.08em;color:var(--faint)}
.menu-link.on{color:var(--accent)}
.menu-foot{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-top:28px}
.menu-socials{display:flex;gap:8px}

/* reading progress + back to top */
.progress{
  position:fixed;top:0;left:0;right:0;height:2px;z-index:70;pointer-events:none;
  background:var(--accent);transform-origin:0 50%;transform:scaleX(var(--p,0));
  transition:opacity .3s ease;
}
.progress.at-top{opacity:0}
.to-top{
  position:fixed;right:max(20px,env(safe-area-inset-right,0px));bottom:max(20px,env(safe-area-inset-bottom,0px));z-index:55;
  width:46px;height:46px;border-radius:50%;display:grid;place-items:center;
  background:var(--paper-2);color:var(--ink);border:1px solid var(--rule);box-shadow:var(--lift);
  opacity:0;transform:translateY(12px) scale(.9);pointer-events:none;
  transition:opacity .3s ease,transform .4s var(--ease-expo),background-color .25s ease,color .25s ease;
}
.to-top.show{opacity:1;transform:none;pointer-events:auto}
@media (hover:hover) and (pointer:fine){.to-top:hover{background:var(--ink);color:var(--paper);border-color:var(--ink)}}
.to-top svg{width:17px;height:17px;transform:rotate(180deg)}
@media (max-width:640px){ .to-top{right:max(14px,env(safe-area-inset-right,0px));bottom:max(14px,env(safe-area-inset-bottom,0px));width:42px;height:42px} }

/* ─── 7 · Page shell + transitions ─────────────────────────────── */
.page-wrap{animation:page-enter .24s var(--ease-expo) backwards}
/* The home scene owns its entrance. A transformed ancestor would re-anchor
   its fixed circle overlay to the page instead of the viewport. */
.page-wrap.page-home{animation:none}
@keyframes page-enter{from{opacity:.65;transform:translateY(8px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.page-wrap{animation:none}}
/* A cover leaving for the next page (fly() in lib.jsx). The copy sits under the
   nav and over everything else, and only ever moves by transform and opacity. */
.page-wrap.is-grown{animation:none}
.fly{position:fixed;z-index:58;margin:0;overflow:hidden;pointer-events:none;background:var(--rule);will-change:transform,opacity}
.fly img,.fly svg{display:block;width:100%;height:100%;object-fit:cover;transform:none}
/* A screenshot opening into the lightbox (morph() in lib.jsx). The picture
   travels on the same curve as the nav pill, and the page crossfades under it.
   The nav sits out: it is the one thing on screen that does not change. Without
   View Transitions none of this applies. */
::view-transition-group(cover){animation-duration:.42s;animation-timing-function:var(--ease-pill)}
::view-transition-old(cover),::view-transition-new(cover){width:100%;height:100%;object-fit:cover;animation-duration:.24s}
::view-transition-old(root),::view-transition-new(root){animation-duration:.28s}
/* The theme is the one change the nav is part of. Everything flips as a single
   picture over .2s, so every component's own colour transition has to stand down. */
.theme-shift .nav{view-transition-name:none}
.theme-shift *{transition:none!important}
.theme-shift::view-transition-old(root),.theme-shift::view-transition-new(root){animation-duration:.2s}
::view-transition-group(nav){animation:none}
::view-transition-old(nav){display:none}
::view-transition-new(nav){animation:none}
.page{padding-bottom:clamp(60px,8vw,120px)}
.page-head{padding-top:calc(var(--nav-h) + clamp(48px,8vw,116px));display:grid;gap:20px}
.page-head .lede{margin-top:4px}

/* ─── 8 · Hero ─────────────────────────────────────────────────── */
.hero-scene{position:relative}
.hero-scene.is-animated{height:330vh}
.hero-sticky{position:sticky;top:0;height:100vh;height:100svh;overflow:hidden}
.hero{
  height:100%;display:flex;flex-direction:column;justify-content:space-between;
  padding:calc(var(--nav-h) + clamp(26px,4vw,54px)) var(--gutter) clamp(22px,3vw,34px);
  max-width:var(--shell);margin:0 auto;
}
.hero-row{display:flex;align-items:center;justify-content:space-between;gap:20px}
.hero-row > *{min-width:0}
.hero-name{
  margin:auto 0;font-weight:850;line-height:.8;letter-spacing:-.005em;
  display:flex;flex-direction:column;align-items:flex-start;
  font-size:11vw;   /* replaced by the fit, this is only the fallback */
}
.hero-l1{display:inline-flex;align-items:baseline;position:relative}
.hero-l2{display:block;margin-top:.02em}
.mask{display:inline-block;overflow:hidden;vertical-align:bottom;padding:.06em 0 .03em;margin:-.06em 0 -.03em}
.mask-in{display:inline-block}
.hero-word{white-space:nowrap}
.hero-dot{
  display:inline-block;width:.16em;height:.16em;border-radius:50%;
  background:var(--accent);margin-left:.05em;flex:none;
}
.hero-photo{
  height:.58em;width:auto;aspect-ratio:4/5;object-fit:cover;border-radius:.05em;
  margin:0 0 .03em .14em;align-self:flex-end;flex:none;
}
.hero-note{
  position:absolute;left:100%;bottom:.1em;margin-left:clamp(14px,2.2vw,38px);
  width:min(22vw,290px);
  font-size:clamp(13px,1.15vw,17px);line-height:1.4;font-weight:500;
  letter-spacing:0;font-stretch:100%;color:var(--muted);text-wrap:pretty;
}
/* Phones only — the name cannot grow any bigger (its width is what sets the
   size), so the calm comes from where it sits: a wide void above it, a
   hairline closing it, and a shorter void below. The two spacers are the
   only honest way to weight that split; they collapse on desktop. */
.hero-lead,.hero-tail,.hero-rule{display:none}
.hero-ctas{display:flex;gap:10px;flex:none}
.hero-badge{width:clamp(74px,7vw,104px);height:auto;aspect-ratio:1;flex:none;color:var(--ink);overflow:visible}
.hero-badge text{font-family:var(--f-mono);font-size:8.6px;letter-spacing:.08em;fill:currentColor}
.badge-ring{transform-box:view-box;transform-origin:50% 50%;animation:spin 20s linear infinite}
.badge-core{fill:var(--accent)}
.badge-arrow{fill:none;stroke:var(--on-accent);stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){ .badge-ring{animation:none} }

/* the hero arrives once the splash has closed into the dot */
.js .hero .mask-in{transform:translateY(102%)}
.js .hero.in .mask-in{transform:none;transition:transform 1.1s var(--ease-expo) calc(var(--i,0)*90ms)}
.js .hero .hero-row,.js .hero .hero-note{opacity:0}
.js .hero.in .hero-row,.js .hero.in .hero-note{opacity:1;transition:opacity .8s ease .35s}
@media (prefers-reduced-motion:reduce){
  .js .hero .mask-in,.js .hero .hero-row,.js .hero .hero-note{transform:none;opacity:1}
}

@media (max-width:860px){
  .hero{justify-content:flex-start;padding-bottom:26px}
  .hero-l1{flex-wrap:nowrap}
  .hero-name{margin:0}
  .hero-lead{display:block;flex:2.1 1 0;min-height:16px}
  .hero-tail{display:block;flex:1 1 0;min-height:16px}
  .hero-rule{display:block;height:1px;background:var(--rule-2);margin-top:clamp(16px,3.4vh,26px)}
  .hero-bottom{flex-direction:column;align-items:flex-start;gap:16px}
  .hero-bottom .hero-badge{position:absolute;right:var(--gutter);bottom:calc(var(--gutter) + 6px)}
  .hero-ctas{flex-wrap:wrap}
}
@media (max-width:520px){
  .hero-bottom .hero-badge{width:64px}
  .hero-ctas .btn{height:42px;padding:0 16px;font-size:14px}
}

/* ─── 9 · Intro veil (the dot that becomes a page) ─────────────── */
.veil{
  position:fixed;inset:0;z-index:40;
  background:var(--accent);color:var(--on-accent);
  visibility:hidden;pointer-events:none;
  display:flex;align-items:center;
  will-change:clip-path;
}
.veil-inner{width:100%;max-width:var(--shell);margin:0 auto;padding:0 var(--gutter);display:grid;gap:clamp(22px,3vw,38px)}
.veil-eyebrow{font-family:var(--f-mono);font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;opacity:.72}
.veil-text{
  font-weight:600;font-stretch:92%;font-size:clamp(28px,4.4vw,70px);
  line-height:1.07;letter-spacing:-.028em;max-width:19em;text-wrap:pretty;
}
.veil-text em{font-family:var(--f-voice);font-style:italic;font-weight:400;font-stretch:100%;font-size:1.06em;letter-spacing:-.01em}
.veil-text .wd{opacity:.2;transition:opacity .18s linear}
/* the way out of the intro — a real button, not a footnote */
.veil-foot{
  display:flex;align-items:center;justify-content:space-between;gap:14px 30px;flex-wrap:wrap;
  padding-top:clamp(18px,2.2vw,30px);
  border-top:1px solid color-mix(in oklab,var(--on-accent) 22%,transparent);
  opacity:0;transform:translateY(14px);
  transition:opacity .6s ease,transform .8s var(--ease-expo);
}
.veil-foot.on{opacity:1;transform:none}
.veil-cta-note{
  flex:0 1 auto;min-width:0;max-width:38ch;
  font-family:var(--f-voice);font-style:italic;font-size:clamp(18px,1.7vw,24px);
  line-height:1.25;letter-spacing:-.005em;opacity:.82;text-wrap:pretty;
}
.veil-cta{
  pointer-events:auto;display:inline-flex;align-items:center;gap:16px;
  height:clamp(56px,5vw,68px);padding:0 10px 0 clamp(22px,2.2vw,30px);
  border-radius:999px;background:var(--on-accent);color:var(--accent);
  font-size:clamp(17px,1.5vw,21px);font-weight:600;font-stretch:104%;letter-spacing:-.02em;
  transition:transform .18s var(--ease),background-color .25s ease,opacity .18s ease;
}
.veil-cta-i{
  width:clamp(38px,3.4vw,46px);height:clamp(38px,3.4vw,46px);border-radius:50%;
  display:grid;place-items:center;background:var(--accent);color:var(--on-accent);flex:none;
  transition:transform .22s var(--ease);
}
.veil-cta-i svg{width:16px;height:16px}
@media (hover:hover) and (pointer:fine){
  .veil-cta:hover .veil-cta-i{transform:translate(3px,-3px)}
}
@media (max-width:640px){
  .veil-cta-note{flex-basis:100%}
  .veil-cta{width:100%;justify-content:space-between}
}

/* the same intro, unanimated (reduced motion) */
.intro-static{background:var(--accent);color:var(--on-accent);padding:clamp(80px,12vw,160px) 0}

/* ─── 10 · Project cards (the Majd grid) ───────────────────────── */
.pcard{display:grid;gap:16px;align-content:start;transition:transform .18s var(--ease),opacity .18s ease}
.pcard-media{
  position:relative;border-radius:18px;overflow:hidden;--r:18px;
  aspect-ratio:16/11;background:var(--rule);
}
.pcard-media img,.pcard-media svg{width:100%;height:100%;object-fit:cover;transition:transform .35s var(--ease)}
@media (hover:hover) and (pointer:fine){.pcard:hover .pcard-media img,.pcard:hover .pcard-media svg{transform:scale(1.035);transition:transform .7s var(--ease-expo)}}
.pcard-row{display:flex;align-items:flex-start;justify-content:space-between;gap:14px}
.pcard-title{
  display:flex;align-items:center;gap:10px;
  font-size:clamp(23px,2.3vw,33px);font-weight:550;letter-spacing:-.025em;line-height:1.06;
}
.pcard-title svg{width:.62em;height:.62em;flex:none;opacity:0;transform:translate(-7px,7px);transition:opacity .18s ease,transform .22s var(--ease)}
@media (hover:hover) and (pointer:fine){.pcard:hover .pcard-title svg{opacity:1;transform:none}}
.pcard-sub{margin-top:7px;font-size:16px;line-height:1.35;color:var(--muted)}
.pcard-date{
  font-family:var(--f-mono);font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);white-space:nowrap;padding-top:6px;
}
.pcard.sm .pcard-media{border-radius:14px;--r:14px;aspect-ratio:4/3}
.pcard.sm .pcard-title{font-size:20px;gap:8px}
.pcard.sm .pcard-sub{font-size:14.5px;margin-top:5px}
.flag{
  position:absolute;top:12px;left:12px;z-index:2;
  display:inline-flex;align-items:center;gap:7px;
  font-family:var(--f-mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;
  background:var(--paper);color:var(--ink);border-radius:999px;padding:6px 11px 5px;
}
.flag::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--accent)}

/* ─── 11 · Home · selected work ────────────────────────────────── */
.featured{padding:clamp(110px,13vw,190px) 0 clamp(70px,9vw,130px)}
.sec-head{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;margin-bottom:clamp(34px,4vw,58px);flex-wrap:wrap}
.sec-head .eyebrow{margin-bottom:14px}
.feat-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(40px,5vw,66px) clamp(16px,1.8vw,26px)}
@media (max-width:720px){ .feat-grid{grid-template-columns:1fr;gap:44px} }

/* The way out of the deck: navigation at the scale of the four cards above
   it, in the same language as the "next project" row on a project page. */
.all-work{
  margin-top:clamp(40px,5vw,70px);
  display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:18px;
  padding:clamp(20px,2.4vw,30px) clamp(20px,2.4vw,30px) clamp(20px,2.4vw,30px) clamp(22px,2.8vw,34px);
  border:1px solid var(--rule-2);border-radius:clamp(20px,2.2vw,28px);
  transition:background-color .22s var(--ease),color .22s var(--ease),border-color .22s var(--ease),transform .18s var(--ease),opacity .18s ease;
}
.all-work-text{display:grid;gap:8px;min-width:0}
.all-work-k{
  font-family:var(--f-mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);transition:color .22s var(--ease);
}
.all-work-t{
  font-size:clamp(26px,3.4vw,46px);font-weight:700;font-stretch:104%;
  letter-spacing:-.035em;line-height:1;text-wrap:balance;
}
.all-work-go{
  width:clamp(48px,4.6vw,62px);height:clamp(48px,4.6vw,62px);border-radius:50%;flex:none;
  border:1px solid var(--rule-2);display:grid;place-items:center;
  transition:background-color .22s var(--ease),color .22s var(--ease),border-color .22s var(--ease),transform .22s var(--ease);
}
.all-work-go svg{width:18px;height:18px}
@media (hover:hover) and (pointer:fine){
  .all-work:hover{background:var(--ink);color:var(--paper);border-color:var(--ink)}
  .all-work:hover .all-work-k{color:color-mix(in oklab,var(--paper) 60%,transparent)}
  .all-work:hover .all-work-go{
    background:var(--accent);border-color:var(--accent);color:var(--on-accent);
    transform:translate(4px,-4px);
  }
}
@media (max-width:560px){ .all-work{gap:14px} .all-work-go{width:46px;height:46px} }

/* ─── 12 · Contact ─────────────────────────────────────────────── */
.contact{padding:clamp(80px,10vw,140px) 0 clamp(60px,8vw,110px)}
.contact.as-page{padding-top:calc(var(--nav-h) + clamp(56px,9vw,130px))}
.contact-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.92fr);gap:clamp(32px,5vw,76px);align-items:stretch}
.contact-l{display:flex;flex-direction:column;gap:18px}
.contact-title{font-weight:800;font-stretch:96%;font-size:clamp(60px,9.6vw,150px);line-height:.86;letter-spacing:-.05em}
.contact-l p{font-size:clamp(16px,1.4vw,19px);color:var(--muted);max-width:40ch;text-wrap:pretty}
.socials{display:flex;gap:10px;margin-top:auto;padding-top:30px}
.soc{
  width:48px;height:48px;border-radius:13px;display:grid;place-items:center;
  border:1px solid var(--rule);background:var(--paper-2);color:var(--ink);
  transition:background-color .25s ease,color .25s ease,border-color .25s ease,transform .2s var(--ease),opacity .18s ease;
}
@media (hover:hover) and (pointer:fine){.soc:hover{background:var(--ink);color:var(--paper);border-color:var(--ink);transform:translateY(-3px)}}
.soc svg{width:19px;height:19px}
.contact-mail{font-family:var(--f-mono);font-size:13px;color:var(--muted);transition:color .2s ease,opacity .18s ease}
@media (hover:hover) and (pointer:fine){.contact-mail:hover{color:var(--ink)}}
.form{
  background:var(--form-bg);color:var(--form-fg);border-radius:22px;
  padding:clamp(18px,2.2vw,28px);display:grid;gap:16px;align-content:start;
}
.topics{display:flex;flex-wrap:wrap;gap:6px}
.topic{
  border:1px solid var(--form-line);border-radius:999px;padding:7px 13px;
  font-size:13.5px;opacity:.72;transition:background-color .2s ease,color .2s ease,opacity .2s ease,border-color .2s ease,transform .18s var(--ease);
}
@media (hover:hover) and (pointer:fine){.topic:hover{opacity:1}}
.topic.on{background:var(--form-fg);color:var(--form-bg);border-color:var(--form-fg);opacity:1}
.field{display:grid;gap:8px}
.field-top{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.field-label{font-size:13px;font-weight:500;letter-spacing:.01em}
.count{font-family:var(--f-mono);font-size:11px;opacity:.5}
.input{
  width:100%;border:1px solid var(--form-line);background:transparent;color:var(--form-fg);
  border-radius:12px;padding:13px 15px;font-size:15.5px;outline:none;
  transition:border-color .2s ease,background-color .2s ease;
}
.input::placeholder{color:var(--form-fg);opacity:.4}
.input:focus{border-color:var(--accent);background:color-mix(in oklab,var(--form-fg) 5%,transparent)}
textarea.input{resize:vertical;min-height:132px;line-height:1.5}
/* Safari on iPhone zooms the whole page into any field under 16px */
@media (pointer:coarse){ .input{font-size:16px} }
.send{
  height:52px;border-radius:12px;background:var(--form-fg);color:var(--form-bg);
  font-size:15.5px;font-weight:600;display:flex;align-items:center;justify-content:center;gap:10px;
  transition:background-color .25s ease,color .25s ease,opacity .25s ease,transform .18s var(--ease);
}
/* each state replaces the last with a soft cut: the blur is what stops two
   words from reading as two objects */
.send-label{display:inline-flex;align-items:center;justify-content:center;gap:10px;animation:send-in .24s var(--ease)}
@keyframes send-in{from{opacity:0;filter:blur(2px);transform:translateY(3px)}}
@media (prefers-reduced-motion:reduce){.send-label{animation:none}}
.send svg{width:15px;height:15px;transition:transform .2s var(--ease)}
@media (hover:hover) and (pointer:fine){
  .send:not(:disabled,[aria-disabled="true"]):hover{background:var(--accent);color:var(--on-accent)}
  .send:not(:disabled,[aria-disabled="true"]):hover svg{transform:translate(3px,-3px)}
}
/* Not ready yet is aria-disabled, not disabled: the button still answers a press,
   and what it answers with is the reason (.form-note.is-hint) */
.send:disabled,.send[aria-disabled="true"]{opacity:.4;cursor:not-allowed}
.send.sent{background:var(--accent);color:var(--on-accent);opacity:1}
.form-note{font-family:var(--f-mono);font-size:11.5px;line-height:1.5;opacity:.6}
.form-note.is-hint{opacity:.95}
.form-err{font-family:var(--f-mono);font-size:12px;color:var(--accent)}
@media (max-width:860px){ .contact-grid{grid-template-columns:1fr} .socials{margin-top:6px;padding-top:10px} }

/* ─── 13 · Work page ───────────────────────────────────────────── */
.work-bar{
  display:flex;align-items:baseline;justify-content:space-between;gap:14px 28px;flex-wrap:wrap;
  padding:16px 0;border-top:1px solid var(--rule);margin:clamp(30px,4vw,52px) 0 clamp(26px,3vw,44px);
}
.filters{position:relative;display:flex;flex-wrap:wrap;gap:6px 9px}
.filter{
  display:inline-flex;align-items:baseline;gap:8px;padding:4px 0 4px 15px;
  font-size:16px;font-weight:500;color:var(--muted);transition:color .22s ease,opacity .18s ease;
}
@media (hover:hover) and (pointer:fine){.filter:hover{color:var(--ink)}}
.filter.on{color:var(--ink)}
/* Placed by Projects in work.jsx. .live arrives one frame after the first
   placement, so the dot opens on its filter instead of flying in from the corner. */
.filter-dot{
  position:absolute;left:0;top:0;width:7px;height:7px;margin-top:-2.5px;
  border-radius:50%;background:var(--accent);pointer-events:none;
}
.filter-dot.live{transition:transform .28s var(--ease-pill)}
@media (prefers-reduced-motion:reduce){.filter-dot.live{transition:none}}
.filter .n{font-family:var(--f-mono);font-size:11px;color:var(--faint)}
.sort{
  display:inline-flex;align-items:center;gap:8px;font-family:var(--f-mono);
  font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
  transition:color .22s ease,opacity .18s ease;
}
@media (hover:hover) and (pointer:fine){.sort:hover{color:var(--ink)}}
.sort svg{width:13px;height:13px;transition:transform .22s var(--ease)}
.sort.asc svg{transform:rotate(180deg)}
.work-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(34px,4vw,54px) clamp(14px,1.8vw,24px)}
@media (max-width:980px){ .work-grid{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (max-width:560px){ .work-grid{grid-template-columns:1fr} }
.empty{padding:60px 0;text-align:center;color:var(--muted)}

/* ─── 14 · Project page ────────────────────────────────────────── */
.pp-head{padding-top:calc(var(--nav-h) + clamp(34px,5vw,74px))}
.back{
  display:inline-flex;align-items:center;gap:9px;font-family:var(--f-mono);
  font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
  transition:color .22s ease,opacity .18s ease;
}
.back svg{width:14px;height:14px;transform:rotate(180deg);transition:transform .2s var(--ease)}
@media (hover:hover) and (pointer:fine){
  .back:hover{color:var(--ink)}
  .back:hover svg{transform:translateX(-4px) rotate(180deg)}
}
.pp-eyebrow{margin-top:26px}
.pp-title{
  margin-top:16px;font-weight:800;font-stretch:100%;
  font-size:clamp(38px,6vw,92px);line-height:.94;letter-spacing:-.042em;
  max-width:17ch;text-wrap:balance;
}
.pp-sub{
  margin-top:20px;font-family:var(--f-voice);font-style:italic;
  font-size:clamp(21px,2.5vw,34px);line-height:1.16;letter-spacing:-.01em;
  max-width:34ch;color:var(--ink);
}
.pp-intro{
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:20px 40px;align-items:end;
  margin-top:clamp(26px,3vw,42px);padding-bottom:clamp(22px,2.6vw,34px);
  border-bottom:1px solid var(--rule);
}
.pp-lead{font-size:clamp(17px,1.5vw,21px);line-height:1.55;color:var(--ink-2);max-width:56ch;text-wrap:pretty}
.pp-links{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end}
.pp-cover{margin-top:clamp(30px,3.4vw,50px);border-radius:22px;overflow:hidden;--r:22px;aspect-ratio:16/8;background:var(--rule)}
.pp-cover img,.pp-cover svg{width:100%;height:100%;object-fit:cover}
/* padding-block, NOT the shorthand: this sits on a .shell, and `padding: x 0 0`
   would reset the left and right gutter to zero and press the whole page
   against the edge of the screen. */
.pp-body{display:grid;grid-template-columns:250px minmax(0,1fr);gap:clamp(30px,5vw,90px);padding-block:clamp(50px,6vw,96px) 0}
.rail{position:sticky;top:calc(var(--nav-h) + 36px);align-self:start;display:grid;gap:26px}
.facts{display:grid;border-top:1px solid var(--rule)}
.fact{display:grid;grid-template-columns:86px minmax(0,1fr);gap:12px;padding:11px 0;border-bottom:1px solid var(--rule)}
.fact dt{font-family:var(--f-mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);padding-top:3px}
.fact dd{font-size:14.5px;line-height:1.4}
.rail-block{display:grid;gap:12px}
.rail-h{font-family:var(--f-mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
.tools{display:flex;flex-wrap:wrap;gap:6px}
.tools li{font-size:13px;border:1px solid var(--rule-2);border-radius:999px;padding:5px 11px;color:var(--ink-2)}
.rail-links{display:grid;gap:8px;justify-items:start}
.sec{padding-bottom:clamp(38px,4.5vw,64px)}
.sec-n{font-family:var(--f-mono);font-size:11.5px;letter-spacing:.08em;color:var(--accent-ink)}
.sec-h{margin-top:10px;font-size:clamp(25px,2.6vw,36px);font-weight:700;font-stretch:104%;letter-spacing:-.03em;line-height:1.08;text-wrap:balance}
.prose{margin-top:18px}
.prose p{margin-bottom:1.1em;font-size:17.5px;line-height:1.72;color:var(--ink-2);max-width:66ch;text-wrap:pretty}
.prose p:last-child{margin-bottom:0}
.prose h3{margin:1.9em 0 .6em;font-size:19px;font-weight:650;letter-spacing:-.015em;color:var(--ink)}
.prose ul{display:grid;gap:10px;margin:0 0 1.3em;max-width:66ch}
.prose li{display:grid;grid-template-columns:24px minmax(0,1fr);font-size:17px;line-height:1.62;color:var(--ink-2)}
.prose li::before{content:"—";color:var(--faint)}
.quote{
  margin:1.5em 0;font-family:var(--f-voice);font-style:italic;
  font-size:clamp(24px,2.7vw,36px);line-height:1.22;letter-spacing:-.012em;
  max-width:30ch;color:var(--ink);
}
.quote::before{content:"“";color:var(--accent);margin-right:.04em}
.quote::after{content:"”";color:var(--accent)}
@media (max-width:900px){
  .pp-body{grid-template-columns:1fr;gap:36px}
  .rail{position:static}
  .pp-intro{grid-template-columns:1fr;gap:22px;align-items:start}
  .pp-links{justify-content:flex-start}
}

/* gallery — one column of evidence, nothing hidden behind an arrow */
.shots{display:grid;gap:clamp(26px,3.4vw,52px);margin-top:22px}
.shot{display:grid;gap:12px}
.shot.is-tall{max-width:560px}
.shot-frame{
  display:block;width:100%;border-radius:16px;overflow:hidden;
  background:var(--paper-2);border:1px solid var(--rule);cursor:zoom-in;
  transition:border-color .3s ease,transform .18s var(--ease),opacity .18s ease;
}
@media (hover:hover) and (pointer:fine){.shot-frame:hover{border-color:var(--rule-2)}}
.shot-frame img{width:100%;height:auto;display:block;transition:transform .35s var(--ease)}
@media (hover:hover) and (pointer:fine){.shot-frame:hover img{transform:scale(1.012);transition:transform .7s var(--ease-expo)}}
.shot-cap{
  display:grid;grid-template-columns:30px minmax(0,1fr);gap:10px;align-items:baseline;
  font-size:14.5px;line-height:1.5;color:var(--muted);max-width:64ch;text-wrap:pretty;
}
.shot-n{font-family:var(--f-mono);font-size:11px;letter-spacing:.08em;color:var(--faint)}
.lb{position:fixed;inset:0;z-index:90;display:grid;place-items:center;padding:clamp(20px,4vw,56px);touch-action:pinch-zoom;animation:lbIn .3s ease both}
/* the scrim is its own layer so a downward swipe can fade it and leave the
   picture alone */
.lb-scrim{position:absolute;inset:0;background:color-mix(in oklab,var(--paper) 92%,transparent);backdrop-filter:blur(10px);transition:opacity .25s ease}
@keyframes lbIn{from{opacity:0}to{opacity:1}}
@media (prefers-reduced-transparency:reduce){ .lb-scrim{background:var(--paper);backdrop-filter:none} }
.lb figure{position:relative;max-width:min(1180px,100%);display:grid;gap:14px;justify-items:center}
/* height:auto as well: Img prints the file's real height as an attribute, and
   capping that with max-height alone would stretch a wide picture on a phone */
.lb img{max-height:78vh;width:auto;height:auto;border-radius:12px;box-shadow:var(--lift)}
.lb figcaption{font-size:14.5px;color:var(--muted);text-align:center;max-width:70ch}
.lb-close{position:absolute;top:max(18px,env(safe-area-inset-top,0px));right:max(18px,env(safe-area-inset-right,0px));width:44px;height:44px;border-radius:50%;border:1px solid var(--rule);background:var(--paper-2);display:grid;place-items:center;transition:transform .18s var(--ease),opacity .18s ease}
.lb-close svg{width:17px;height:17px}
.lb-nav{position:absolute;bottom:max(20px,env(safe-area-inset-bottom,0px));left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:14px;font-family:var(--f-mono);font-size:12px;color:var(--muted)}
.lb-nav button{width:38px;height:38px;border-radius:50%;border:1px solid var(--rule);display:grid;place-items:center;background:var(--paper-2);transition:transform .18s var(--ease),opacity .18s ease}
.lb-nav button:disabled{opacity:.3}
.lb-nav svg{width:14px;height:14px}

/* attachments + takeaways: quiet rows, not cards */
.rows{border-top:1px solid var(--rule)}
.row{
  display:flex;align-items:center;gap:18px;padding:16px 2px;border-bottom:1px solid var(--rule);
  transition:background-color .25s ease,opacity .18s ease;
}
@media (hover:hover) and (pointer:fine){a.row:hover :is(.row-ico,.sup-av,.row-text){transform:translateX(12px)}}
.row-ico{width:38px;height:38px;border-radius:10px;border:1px solid var(--rule);display:grid;place-items:center;color:var(--muted);flex:none;transition:color .25s ease,border-color .25s ease,transform .22s var(--ease)}
.row-ico svg{width:17px;height:17px}
@media (hover:hover) and (pointer:fine){a.row:hover .row-ico{color:var(--accent-ink);border-color:var(--accent)}}
.row-text{display:grid;gap:3px;min-width:0;flex:1;transition:transform .22s var(--ease)}
.row-label{font-size:17px;font-weight:550;letter-spacing:-.012em}
.row-note{font-size:14px;color:var(--muted);text-wrap:pretty}
.row-kind{font-family:var(--f-mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);white-space:nowrap}
.row-go{width:16px;height:16px;color:var(--muted);flex:none;transition:transform .2s var(--ease),color .2s ease}
@media (hover:hover) and (pointer:fine){
  a.row:hover .row-go{color:var(--ink);transform:translate(2px,-2px)}
  a.row:hover .row-go.dl{transform:translateY(2px)}
}
/* what I took away — an enclosed panel: the last content, clearly content */
.takeaways{padding:clamp(46px,6vw,86px) 0 0}
.take-panel{
  background:var(--paper-2);border:1px solid var(--rule);
  border-radius:clamp(20px,2.4vw,30px);padding:clamp(26px,3.6vw,54px);
}
.take-h{font-size:clamp(23px,2.5vw,34px);font-weight:700;font-stretch:104%;letter-spacing:-.03em;line-height:1.05}
.take-grid{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(22px,2.6vw,36px) clamp(28px,4vw,64px);margin-top:clamp(24px,3vw,40px);
}
.take{display:grid;gap:7px;align-content:start}
.take-name{
  display:flex;align-items:baseline;gap:10px;
  font-size:clamp(17px,1.7vw,21px);font-weight:600;font-stretch:102%;letter-spacing:-.02em;line-height:1.25;
}
.take-name::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--accent);flex:none;transform:translateY(-1px)}
.take-note{font-size:15px;line-height:1.5;color:var(--muted);text-wrap:pretty;padding-left:16px}
@media (max-width:720px){ .take-grid{grid-template-columns:1fr} }

/* the way out — navigation, at navigation scale */
.pnav{
  margin-top:clamp(44px,6vw,96px);padding-top:clamp(20px,2.2vw,30px);
  border-top:1px solid var(--rule);display:grid;gap:clamp(14px,1.8vw,26px);
}
.pnav-back{
  display:inline-flex;align-items:center;gap:9px;justify-self:start;
  font-family:var(--f-mono);font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
  transition:color .22s ease,opacity .18s ease;
}
.pnav-back svg{width:14px;height:14px;transform:rotate(180deg);transition:transform .2s var(--ease)}
@media (hover:hover) and (pointer:fine){
  .pnav-back:hover{color:var(--ink)}
  .pnav-back:hover svg{transform:translateX(-4px) rotate(180deg)}
}
.pnav-next{
  display:grid;grid-template-columns:clamp(92px,11vw,148px) minmax(0,1fr) auto;
  gap:clamp(16px,2.4vw,34px);align-items:center;padding:clamp(8px,1.2vw,16px) 0 clamp(4px,1vw,12px);
  transition:transform .18s var(--ease),opacity .18s ease;
}
.pnav-media{border-radius:14px;overflow:hidden;aspect-ratio:4/3;background:var(--rule)}
.pnav-media img,.pnav-media svg{width:100%;height:100%;object-fit:cover;transition:transform .35s var(--ease)}
@media (hover:hover) and (pointer:fine){.pnav-next:hover .pnav-media img,.pnav-next:hover .pnav-media svg{transform:scale(1.05);transition:transform .7s var(--ease-expo)}}
.pnav-text{display:grid;gap:6px;min-width:0;transition:transform .22s var(--ease)}
@media (hover:hover) and (pointer:fine){.pnav-next:hover .pnav-text{transform:translateX(6px)}}
.pnav-t{
  font-size:clamp(26px,3.4vw,50px);font-weight:700;font-stretch:104%;
  letter-spacing:-.035em;line-height:1.02;text-wrap:balance;
}
.pnav-m{font-family:var(--f-mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.pnav-go{
  width:clamp(44px,4.4vw,60px);height:clamp(44px,4.4vw,60px);border-radius:50%;
  border:1px solid var(--rule-2);display:grid;place-items:center;color:var(--ink);
  transition:background-color .25s ease,color .25s ease,border-color .25s ease,transform .22s var(--ease);
}
.pnav-go svg{width:17px;height:17px}
@media (hover:hover) and (pointer:fine){.pnav-next:hover .pnav-go{background:var(--ink);border-color:var(--ink);color:var(--paper);transform:translateX(5px)}}
@media (max-width:720px){
  .pnav-next{grid-template-columns:76px minmax(0,1fr) 44px;gap:14px}
  .pnav-t{font-size:clamp(22px,6vw,30px)}
}

/* ─── 15 · About ───────────────────────────────────────────────── */
.ab-scene{position:relative}
.ab-scene.is-animated{height:250vh}
.ab-sticky{position:sticky;top:0;min-height:100vh;display:flex;align-items:center;padding:calc(var(--nav-h) + 40px) 0 40px}
.ab-grid{display:grid;grid-template-columns:minmax(0,1fr) 270px;gap:clamp(28px,5vw,80px);align-items:center;width:100%}
.ab-bio{font-size:clamp(23px,2.8vw,42px);line-height:1.24;font-weight:550;letter-spacing:-.024em;text-wrap:pretty}
.ab-bio p + p{margin-top:.65em}
.ab-bio .wd{opacity:.16;transition:opacity .18s linear}
.idcard{
  background:var(--paper-2);border:1px solid var(--rule);border-radius:20px;padding:18px;
  display:grid;gap:4px;box-shadow:var(--lift);
}
.idcard-av{width:56px;height:56px;border-radius:50%;overflow:hidden;margin-bottom:10px;display:grid;place-items:end center;background:color-mix(in oklab,var(--accent) 24%,var(--paper-2))}
/* bottom-anchored on purpose — the head rises out of the bubble — but lifted
   a few pixels so the chin never touches the rim. */
.idcard-av img{width:70%;display:block;margin-bottom:3px}
.idcard-n{font-size:17px;font-weight:700;letter-spacing:-.015em}
.idcard-m{font-family:var(--f-mono);font-size:11.5px;letter-spacing:.04em;color:var(--muted)}
.idcard-s{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--muted);margin:8px 0 12px;text-wrap:pretty}
.idcard-s i{width:7px;height:7px;border-radius:50%;background:var(--ok);flex:none}
.idcard-b{display:flex;flex-wrap:wrap;gap:6px}
@media (max-width:860px){
  .ab-scene.is-animated{height:auto}
  .ab-sticky{position:static;min-height:0;display:block;padding:calc(var(--nav-h) + 40px) 0 0}
  .ab-grid{grid-template-columns:1fr;gap:26px}
  .idcard{order:-1;max-width:340px}
}

/* timeline — the travelling dot. The title scrolls away before the stage
   pins, so the travel gets the whole screen; a quiet top line keeps the
   context while it does. */
.tl-scene{position:relative}
/* padding-block, NOT the shorthand: this sits on a .shell, and `padding: x 0 y`
   would wipe out the side gutter and leave the title flush against the screen. */
.tl-head{padding-block:calc(var(--nav-h) + clamp(24px,4vw,56px)) clamp(28px,4.5vw,60px)}
.tl-sticky{position:sticky;top:0;height:100vh;overflow:hidden;display:flex;flex-direction:column}
.tl-top{
  flex:none;display:flex;align-items:baseline;justify-content:space-between;gap:20px;
  padding-top:calc(var(--nav-h) + 16px);
  font-family:var(--f-mono);font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint);
}
.tl-stage{position:relative;flex:1;min-height:0;overflow:hidden;-webkit-mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent);mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent)}
.tl-track{position:absolute;top:0;bottom:0;left:0;will-change:transform}
.tl-axis{position:absolute;left:0;right:0;top:var(--axis);height:1px;background:var(--rule-2)}
.tl-past{right:auto;width:0;transform:scaleX(0);transform-origin:left;background:var(--accent);opacity:.8}
.tl-tick{position:absolute;top:calc(var(--axis) - 3px);width:1px;height:7px;background:var(--rule-2);opacity:.6}
.tl-tick.is-year{top:calc(var(--axis) - 7px);height:15px;opacity:1}
.tl-year{position:absolute;font-family:var(--f-mono);font-size:12px;letter-spacing:.06em;color:var(--faint);transform:translateX(-50%)}
.tl-bar{position:absolute;height:7px;border-radius:4px;background:var(--rule);overflow:hidden}
.tl-bar.up{background:transparent;border:1px dashed var(--rule-2)}
.tl-bar-fill{position:absolute;inset:0;transform:scaleX(0);transform-origin:left;background:var(--accent);border-radius:inherit}
.tl-today{position:absolute;top:var(--axis);transform:translate(-50%,-50%);display:grid;place-items:center;z-index:2}
.tl-today i{width:11px;height:11px;border-radius:50%;border:2px solid var(--accent);background:var(--paper)}
.tl-today span{
  position:absolute;bottom:calc(100% + 9px);
  font-family:var(--f-mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--accent-ink);white-space:nowrap;
}
.tl-card{
  position:absolute;z-index:1;width:min(340px,31vw);padding:22px 24px 24px;border-radius:18px;
  background:var(--paper-2);border:1px solid var(--rule);display:grid;gap:8px;
  transition:border-color .35s ease,box-shadow .45s ease;
}
.tl-card.up{border-style:dashed;background:color-mix(in oklab,var(--paper-2) 55%,transparent)}
/* short screens: the same card, tightened, so it always fits the stage */
.is-tight .tl-card{width:min(310px,29vw);padding:17px 19px 19px;gap:6px}
.is-tight .tl-title{font-size:18px}
.is-tight .tl-place{margin-top:5px;padding-top:10px;font-size:17px}
.tl-card.now{border-color:var(--accent);box-shadow:var(--lift)}
.tl-stem{
  position:absolute;z-index:0;width:1px;
  background:linear-gradient(color-mix(in oklab,var(--rule-2) 75%,transparent),var(--accent));
}
.tl-stem.is-down{background:linear-gradient(var(--accent),color-mix(in oklab,var(--rule-2) 75%,transparent))}
.tl-type{display:flex;align-items:center;gap:8px;font-family:var(--f-mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
/* the school / company mark, in the corner of the label row. Two caps, not
   one: a long wordmark is held by its width, a compact mark by its height,
   so a six-to-one logotype and a signature end up carrying the same weight.
   Negative margins keep the mark out of the row's height, so a card with a
   logo is exactly as tall as one without — the travelling layout measures
   those heights. */
.tl-logo{
  height:auto;width:auto;max-height:24px;max-width:118px;flex:none;
  margin:-9px 0 -9px auto;object-fit:contain;object-position:right center;
  filter:var(--logo-mono);opacity:.38;
  transition:opacity .5s var(--ease),filter .5s var(--ease);
}
.tl-card.now .tl-logo,.tl-v-item.lit .tl-logo{opacity:1;filter:var(--logo-mono-on)}
.is-tight .tl-logo{max-height:20px;max-width:100px}
@media (max-width:640px){ .tl-logo{max-height:20px;max-width:92px} }
.pill-up{border:1px solid var(--accent);color:var(--accent-ink);border-radius:999px;padding:2px 8px 1px;letter-spacing:.06em}
.tl-title{font-size:20px;font-weight:650;letter-spacing:-.018em;line-height:1.18;text-wrap:balance}
.tl-date{font-family:var(--f-mono);font-size:12px;color:var(--muted);letter-spacing:.02em}
.tl-place{margin-top:8px;padding-top:13px;border-top:1px solid var(--rule);font-family:var(--f-voice);font-style:italic;font-size:19px;line-height:1.2;letter-spacing:-.005em;text-wrap:pretty}
.tl-cursor{
  position:absolute;width:15px;height:15px;border-radius:50%;background:var(--accent);
  transform:translate(-50%,-50%);box-shadow:0 0 0 6px color-mix(in oklab,var(--accent) 14%,transparent);
}
.tl-readout{
  position:absolute;transform:translateX(-50%);
  font-family:var(--f-mono);font-size:12.5px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--accent-ink);white-space:nowrap;
}
/* timeline, vertical (mobile + reduced motion) */
.tl-v{position:relative;padding-left:34px}
.tl-v-axis{position:absolute;left:5px;top:6px;bottom:6px;width:2px;background:var(--rule)}
.tl-v-fill{position:absolute;left:0;top:0;width:100%;height:0;background:var(--accent)}
.tl-v-item{position:relative;padding:0 0 18px}
.tl-v-node{position:absolute;left:-34px;top:22px;width:12px;height:12px;border-radius:50%;background:var(--paper);border:2px solid var(--rule-2);transition:background-color .3s ease,border-color .3s ease}
.tl-v-item.lit .tl-v-node{background:var(--accent);border-color:var(--accent)}
.tl-v-item .tl-card{position:static;width:auto}
/* On a phone the spine used to start exactly on the text margin, so the line
   and its dots read as glued to the screen edge. Move the whole lane in, and
   tighten the card's own padding by the same amount so nothing gets narrower.
   (The node is offset by -34px from the card, so the axis follows the pad.) */
@media (max-width:860px){
  .tl-v{padding-left:44px}
  .tl-v-axis{left:15px}
  .tl-v .tl-card{padding:19px 20px 21px}
}

/* toolkit rows */
.tools-rows{border-top:1px solid var(--rule)}
.trow{
  display:flex;align-items:baseline;justify-content:space-between;gap:24px;padding:18px 0;
  border-bottom:1px solid var(--rule);
}
.trow-k{display:inline-flex;align-items:baseline;gap:10px;font-size:clamp(21px,2.3vw,32px);font-weight:600;font-stretch:104%;letter-spacing:-.025em;flex:none;transition:transform .22s var(--ease)}
/* the dot takes no room: its negative margin cancels its width, so opening it
   moves nothing, and the row steps forward as one piece */
.trow-k::before{content:"";width:.2em;height:.2em;margin-right:-.2em;border-radius:50%;background:var(--accent);transform:scale(0);transition:transform .22s var(--ease)}
@media (hover:hover) and (pointer:fine){
  .trow:hover .trow-k{transform:translateX(12px)}
  .trow:hover .trow-k::before{transform:none}
}
.trow-v{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px 16px;font-size:15.5px;color:var(--muted);text-align:right}
.titem{display:inline-flex;align-items:center;gap:7px}
/* the mark unrolls with the row. It is the WIDTH that opens, not a square
   box: a wordmark like Canva keeps its proportions instead of being squeezed
   into 16×16 with nothing left to read. */
.titem img{width:auto;height:16px;max-width:0;object-fit:contain;opacity:0;transition:max-width .4s var(--ease),opacity .35s ease}
.titem img.is-mono{filter:var(--logo-flip)}
/* a language flag behaves exactly like a tool logo — it unrolls with the row
   — only smaller, rounded, and on a hairline so the white band of a tricolour
   still reads against paper. (Drawn, not emoji: Windows shows no flag emoji.) */
.titem img.lang-flag{height:12px;border-radius:2.5px;box-shadow:0 0 0 1px color-mix(in oklab,var(--ink) 14%,transparent)}
@media (hover:hover) and (pointer:fine){.trow:hover .titem img{max-width:46px;opacity:1}}
.titem em{font-family:var(--f-voice);font-style:italic;font-size:1.1em;color:var(--ink)}
@media (max-width:720px){ .trow{flex-direction:column;gap:8px} .trow-v{justify-content:flex-start;text-align:left} .titem img{max-width:46px;opacity:1} }

/* interests marquee */
.marquee{overflow:hidden;padding:6px 0;-webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent)}
.marquee-track{display:flex;width:max-content;will-change:transform}
.mq-item{
  display:inline-flex;align-items:center;gap:.3em;padding-right:.34em;
  font-size:clamp(42px,7vw,104px);line-height:1.06;font-weight:800;letter-spacing:-.04em;white-space:nowrap;
}
.mq-item svg{width:.52em;height:.52em;color:var(--accent);flex:none}
.mq-sep{display:inline-block;width:.14em;height:.14em;border-radius:50%;background:var(--accent);margin:0 .3em;flex:none;align-self:center}
.marquee.wide .mq-item{font-stretch:125%}
.marquee.narrow .mq-item{font-stretch:62%;color:var(--muted)}

/* about cta */
.ab-cta{display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap;padding:clamp(50px,6vw,90px) 0 0;border-top:1px solid var(--rule);margin-top:clamp(50px,6vw,90px)}
.ab-cta p{font-size:clamp(20px,2.2vw,30px);font-weight:600;letter-spacing:-.025em;max-width:24ch;text-wrap:balance}

/* section spacing on About */
.ab-sec{padding:clamp(70px,9vw,130px) 0 0}

/* ─── 16 · Outside page ────────────────────────────────────────────
   The personal half of the site. It borrows every instrument from the
   charter — the mono label, the italic serif for a place, the dot — and
   changes only the rhythm: the numbers get bigger, and the grid steps
   down the page instead of sitting square. Nothing new is invented. */
.ot-sec{padding-top:clamp(70px,9vw,130px)}

/* The records, set like a printed table rather than a sentence of numbers:
   ruled top and bottom, one cell per distance, cells divided by hairlines.
   Each cell carries the pace under the time — the pace is what a runner
   actually reads, and it is computed, never typed. Still no huge figures:
   this block announces itself by structure, not by size. */
.pbs{
  margin-top:clamp(40px,5vw,66px);
  border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);
  padding-block:clamp(18px,2.2vw,26px);
}
.pbs-k{
  display:block;margin-bottom:clamp(14px,1.8vw,22px);
  font-family:var(--f-mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);
}
.pbs-list{display:grid;grid-template-columns:repeat(var(--pbn,3),minmax(0,1fr));margin:0}
.pb{padding-left:clamp(16px,2.4vw,34px);border-left:1px solid var(--rule)}
.pb:first-child{padding-left:0;border-left:0}
.pb dt{
  font-size:clamp(16px,1.6vw,20px);font-weight:600;font-stretch:104%;
  letter-spacing:-.015em;line-height:1.1;color:var(--ink);text-wrap:balance;
}
.pb dd{margin:10px 0 0;display:flex;flex-direction:column;gap:6px}
.pb-t{font-size:clamp(25px,2.7vw,37px);font-weight:700;font-stretch:104%;letter-spacing:-.028em;line-height:1}
.pb-p{font-family:var(--f-mono);font-size:11px;letter-spacing:.06em;color:var(--muted)}
@media (max-width:560px){
  .pbs-list{grid-template-columns:minmax(0,1fr)}
  .pb{padding:14px 0 0;margin-top:14px;border-left:0;border-top:1px solid var(--rule)}
  .pb:first-child{padding-top:0;margin-top:0;border-top:0}
  .pb dd{flex-direction:row;align-items:baseline;gap:12px;margin-top:5px}
}

/* The races. Two columns, and the right-hand one rides lower — the step is
   what stops six cards reading as a checkerboard of more work. */
.race-grid{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(44px,5.6vw,84px) clamp(16px,1.8vw,26px);align-items:start;
}
.race-grid>.race:nth-child(even){margin-top:clamp(26px,4.4vw,72px)}
.race{display:grid;gap:16px;align-content:start}
/* neither photograph nor time yet: the card is its text, and it says so by
   standing on the same hairline the rest of the site gives a list. */
.race.bare{gap:0;border-top:1px solid var(--rule);padding-top:16px}
.race-media{
  position:relative;border-radius:18px;overflow:hidden;--r:18px;
  aspect-ratio:16/11;background:var(--rule);
}
.race-media img{width:100%;height:100%;object-fit:cover;display:block}

/* No photograph yet: the box goes to the number the photograph was only
   ever framing. A real state, not a placeholder. */
.race-blank{
  position:absolute;inset:0;display:grid;place-items:center;align-content:center;gap:10px;
  padding:20px;text-align:center;
  background:var(--paper-2);border:1px solid var(--rule);border-radius:inherit;
}
.race-blank-p{font-family:var(--f-mono);font-size:11.5px;letter-spacing:.07em;color:var(--muted)}
.race-blank-t{font-size:clamp(38px,5.4vw,76px);font-weight:800;font-stretch:104%;letter-spacing:-.04em;line-height:1}
.race-blank-d{
  font-size:clamp(21px,2.4vw,34px);font-weight:700;font-stretch:104%;
  letter-spacing:-.03em;line-height:1.05;color:var(--muted);text-wrap:balance;
}

.race-text{display:grid;gap:7px}
.race-meta{
  display:flex;justify-content:space-between;align-items:baseline;gap:12px;
  font-family:var(--f-mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);
}
.race-date{white-space:nowrap;color:var(--faint)}
.race-n{font-size:clamp(23px,2.3vw,33px);font-weight:550;letter-spacing:-.025em;line-height:1.06;text-wrap:balance}
.race-p{font-family:var(--f-voice);font-style:italic;font-size:19px;line-height:1.2;color:var(--muted)}
.race-note{font-size:15px;line-height:1.5;color:var(--muted);max-width:46ch;text-wrap:pretty}
.race-t{
  margin-top:5px;display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;
  font-size:clamp(34px,3.6vw,52px);font-weight:800;font-stretch:104%;
  letter-spacing:-.035em;line-height:1;
}
/* the pace rides against the time, as it does in the records row */
.race-pace{
  font-family:var(--f-mono);font-size:12px;font-weight:400;font-stretch:100%;
  letter-spacing:.06em;color:var(--muted);
}

/* The count beside the map's title, in the slot .sec-head already keeps free. */
.tally{display:flex;gap:clamp(22px,3vw,42px);margin:0;flex:none}
.tally>div{display:flex;flex-direction:column-reverse;gap:5px}
.tally dt{
  font-family:var(--f-mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
}
.tally dd{margin:0;font-size:clamp(34px,4vw,56px);font-weight:800;font-stretch:104%;letter-spacing:-.04em;line-height:1}

/* The map. Land is the hairline token used as a fill; the countries I've been
   to are one step of ink above it; and the marker is the site's dot, doing the
   fourth of its four jobs. */
.atlas{
  margin:0 auto;position:relative;
  width:min(1560px,100% - 2 * clamp(24px,3vw,40px));
}
.atlas-svg{width:100%;height:100%;display:block;overflow:visible;shape-rendering:geometricPrecision}

/* The world is drawn, not blocked in. Every border is a hairline at a tenth
   of the ink — the plate — and the countries visited are the only thing on it
   that is actually filled. Every stroke here is `non-scaling-stroke`, set on
   the paths themselves, so a hairline is one screen pixel at any size instead
   of fattening with the viewBox. */
.atlas-land{
  fill:color-mix(in oklab,var(--ink) 5%,transparent);
  stroke:color-mix(in oklab,var(--ink) 15%,transparent);
  stroke-width:1;
}
/* Solid ink, cut apart by a paper-coloured hairline. Without that separator
   the eight countries in western Europe merge into one shapeless blot and the
   map stops saying anything. */
.atlas-on{
  fill:color-mix(in oklab,var(--ink) 92%,transparent);
  stroke:var(--paper);stroke-width:1;stroke-linejoin:round;
  transition:fill .4s var(--ease),stroke .4s var(--ease);
}
/* A country one pixel across would be rubbed out by a paper hairline. It keeps
   its own ink instead and accepts merging into its neighbours — being visible
   matters more than being separate. */
.atlas-on.tiny{
  stroke:color-mix(in oklab,var(--ink) 92%,transparent);stroke-width:2.4;
}
/* One country at a time: the pointer picks out the one it is over. The map is
   already complete without this — every country visited is filled at rest — so
   the hover adds a reading, never the information itself. That is why it is
   gated on a real pointer and a phone loses nothing by not having it. */
@media (hover:hover) and (pointer:fine){
  .atlas-c:hover .atlas-on{fill:var(--accent)}
  .atlas-c:hover .atlas-on.tiny{stroke:var(--accent)}
}
/* The pointer target, invisible and painting nothing. It MUST keep an explicit
   `fill` — a path with no fill declared falls back to black and would cover the
   country it is supposed to listen for. `.wide` is the rescue for the handful
   too small to aim at, chiefly Luxembourg at one pixel across; it is not given
   to everyone, because a halo on every country eats its neighbours' edges. */
.atlas-hit{fill:transparent;stroke:transparent;stroke-width:0;pointer-events:all}
.atlas-hit.wide{stroke-width:9;stroke-linejoin:round}
@media (prefers-reduced-motion:reduce){ .atlas-on{transition:none} }

.sup-rows .row{padding:22px 2px;gap:20px}
.sup-rows .row-ico{width:56px;height:56px;border-radius:50%}
.sup-rows .row-ico svg{width:21px;height:21px}
.sup-av{
  position:relative;width:56px;height:56px;flex:none;border-radius:50%;overflow:hidden;
  background:var(--paper-2);border:1px solid var(--rule);
  transition:transform .22s var(--ease);
}
/* Img renders <picture><source><img>. The picture's box is sized by its own
   child, which makes every percentage on the image circular, and a percentage
   height inside a grid slot does not resolve at all — between them the avatars
   came out ratio-locked and overflowing their circle, hidden only by the clip.
   So: the picture stops being a box, and the image is positioned against the
   circle, where 100% has something definite to mean. */
.sup-av picture{display:contents}
.sup-av img{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover}
/* A crest is a mark, not a portrait: it sits whole inside the circle with room
   to breathe. The inset is padding on the image, so object-fit centres the
   artwork inside what is left whatever shape it is. */
.sup-av.mark img{padding:10px;object-fit:contain}
.sup-rows .row-text{gap:6px}
.sup-rows .row-label{font-size:clamp(19px,1.8vw,23px);font-weight:600;letter-spacing:-.018em}
.sup-rows .row-note{font-size:15px;line-height:1.5;max-width:64ch}

@media (max-width:720px){
  .race-grid{grid-template-columns:minmax(0,1fr);gap:clamp(38px,7vw,54px)}
  .race-grid>.race:nth-child(even){margin-top:0}
  /* At phone width a country is a few pixels across, and a paper hairline
     would eat most of one. Separation gives way to simply being legible:
     the countries take their own ink and are allowed to touch. */
  .atlas-on{stroke:color-mix(in oklab,var(--ink) 92%,transparent);stroke-width:1.4}
  .atlas-on.tiny{stroke-width:2.2}
  .sup-rows .row{align-items:flex-start}
  .sup-rows .row-ico{margin-top:2px}
}

/* ─── 17 · Footer (kept as it was, in the charter's colours) ───── */
/* The last thing on the page, and it has to end exactly where the page ends:
   the safe-area padding puts the home indicator ON the orange instead of
   below it, and ::after bleeds the same orange into whatever the browser
   shows past the end of the document (the rubber-band, the strip under the
   bottom bar). The bleed is painted, never laid out, so it adds no scroll. */
.foot{
  width:min(1360px,calc(100% - 40px));margin:clamp(90px,12vw,150px) auto 0;
  padding:0 clamp(18px,3vw,40px) env(safe-area-inset-bottom,0px);
  border-radius:30px 30px 0 0;position:relative;overflow:visible;
  background:linear-gradient(180deg,var(--accent) 0%,color-mix(in oklab,var(--accent) 88%,#fff) 100%);
  color:var(--on-accent);
  --logo-dot:currentColor;
}
.foot::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:1px;z-index:1;
  background:color-mix(in oklab,var(--accent) 88%,#fff);
  box-shadow:0 0 0 240px color-mix(in oklab,var(--accent) 88%,#fff);
  clip-path:inset(100% -260px -260px -260px);
}
.foot-sheen{position:absolute;inset:0;z-index:3;pointer-events:none;border-radius:inherit;background:radial-gradient(120% 80% at 50% -25%,color-mix(in oklab,#fff 22%,transparent),transparent 60%)}
.foot-forest{position:absolute;left:0;right:0;top:-112px;height:134px;z-index:2;overflow:hidden;pointer-events:none;color:var(--accent)}
/* max-content, so the grove is always its own size and simply gets cropped by
   the footer's edges — without it the row is squeezed by whatever room is left
   to the right of the 50% anchor, and the trees squash on narrow screens. */
.foot-forest-row{position:absolute;left:50%;bottom:20px;width:max-content;transform:translateX(-50%);transform-origin:bottom center;display:flex;align-items:flex-end;justify-content:center}
.fir{position:relative;margin-left:-10px;pointer-events:auto;transform-origin:50% 100%;animation:fir-sway 6s ease-in-out infinite}
.fir:first-child{margin-left:0}
.fir:nth-child(2n){animation-duration:7.2s;animation-delay:-1.3s}
.fir:nth-child(3n){animation-duration:5.4s;animation-delay:-.7s}
.fir:nth-child(4n){animation-duration:6.6s;animation-delay:-2.1s}
.fir-front{z-index:2}
.fir-back{z-index:0}
.fir-tree,.fir-twig{display:block;transform-origin:50% 100%}
.foot-forest svg{display:block}
.fir-back .fir-tree{transform:scale(.8);opacity:.5}
.fir-twig{transition:transform .8s ease-out}
.fir:hover{z-index:6}
.fir:hover .fir-twig{animation:fir-gust 2.4s ease-in-out infinite}
@keyframes fir-sway{0%,100%{transform:rotate(-2.2deg)}50%{transform:rotate(2.2deg)}}
@keyframes fir-gust{0%,100%{transform:rotate(-4.5deg)}50%{transform:rotate(4.5deg)}}
@media (prefers-reduced-motion:reduce){ .fir,.fir:hover .fir-twig{animation:none} .fir-twig{transition:none} }
.foot-inner{position:relative;z-index:4;padding:34px 0 26px;display:flex;flex-direction:column;align-items:center;gap:20px}
.foot-meta{width:100%;display:flex;align-items:flex-start;justify-content:space-between;gap:16px}
.foot-col{display:flex;flex-direction:column;gap:4px}
.foot-col-right{align-items:flex-end;text-align:right}
.foot-eyebrow{font-family:var(--f-mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:color-mix(in oklab,var(--on-accent) 62%,transparent)}
.foot-meta-val{font-size:13px;letter-spacing:-.01em}
.foot-note{
  position:relative;max-width:680px;padding:16px 28px 58px;border-radius:18px;
  background:color-mix(in oklab,#fff 18%,transparent);
  border:1px solid color-mix(in oklab,var(--on-accent) 16%,transparent);
  text-align:left;
}
.foot-note-label{display:block;margin-bottom:8px;font-family:var(--f-mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:color-mix(in oklab,var(--on-accent) 60%,transparent)}
.foot-note-text{font-size:16px;line-height:1.55;letter-spacing:-.01em}
.foot-note-text a{text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px;transition:opacity .18s ease}
@media (hover:hover) and (pointer:fine){.foot-note-text a:hover{opacity:.7}}
.foot-socials{display:flex;position:relative;z-index:2;margin-top:-58px}
.foot-tile{
  width:106px;height:106px;border-radius:27px;display:grid;place-items:center;
  color:var(--on-accent);border:2px solid color-mix(in oklab,var(--on-accent) 45%,transparent);
  background:var(--accent);position:relative;z-index:1;
  box-shadow:0 10px 24px -12px rgba(0,0,0,.45);cursor:pointer;
  transform:rotate(calc((var(--i) - 1) * 4deg));
  transition:transform .28s cubic-bezier(.34,1.56,.64,1),background-color .22s ease,color .22s ease,border-color .22s ease,box-shadow .28s ease;
}
.foot-tile + .foot-tile{margin-left:-24px}
.foot-tile svg{width:48px;height:48px;transition:transform .28s cubic-bezier(.34,1.56,.64,1)}
@media (hover:hover) and (pointer:fine){
  .foot-tile:hover{
    z-index:5;transform:translateY(-12px) rotate(calc((var(--i) - 1) * 4deg)) scale(1.1);
    background:var(--on-accent);color:var(--accent);border-color:var(--on-accent);
    box-shadow:0 22px 40px -14px rgba(0,0,0,.5);
  }
  .foot-tile:hover svg{transform:scale(1.08)}
}
.foot-tile:active{transform:rotate(calc((var(--i) - 1) * 4deg)) scale(var(--press))}
@media (hover:hover) and (pointer:fine){.foot-tile:hover:active{transform:translateY(-5px) rotate(calc((var(--i) - 1) * 4deg)) scale(1.04)}}
.foot-tile.copied{background:var(--on-accent);color:var(--accent);border-color:var(--on-accent)}
.foot-tile-toast{
  position:absolute;left:50%;top:-6px;transform:translate(-50%,0);
  padding:5px 11px;border-radius:999px;font-size:12px;font-weight:600;
  background:var(--on-accent);color:var(--accent);white-space:nowrap;pointer-events:none;
  opacity:0;z-index:6;animation:foot-toast 1500ms ease both;
}
@keyframes foot-toast{
  0%{opacity:0;transform:translate(-50%,6px) scale(.9)}
  15%{opacity:1;transform:translate(-50%,-14px) scale(1)}
  80%{opacity:1;transform:translate(-50%,-16px) scale(1)}
  100%{opacity:0;transform:translate(-50%,-22px) scale(.96)}
}
@media (prefers-reduced-motion:reduce){
  .foot-tile-toast{animation:none;opacity:1;transform:translate(-50%,-16px)}
  .foot-tile{transition:background-color .22s ease,color .22s ease}
  .foot-tile:hover,.foot-tile:hover:active{transform:rotate(calc((var(--i) - 1) * 4deg))}
}
.foot-base{width:100%;margin-top:2px;padding-top:18px;display:flex;align-items:center;justify-content:space-between;gap:16px;border-top:1px solid color-mix(in oklab,var(--on-accent) 18%,transparent)}
.foot-brand{display:inline-flex;align-items:center;gap:9px;font-size:12.5px;font-weight:500;letter-spacing:-.01em}
.foot-brand svg{height:14px;width:auto}
.foot-mid{font-family:var(--f-mono);font-size:11px;color:color-mix(in oklab,var(--on-accent) 62%,transparent)}
@media (max-width:640px){
  .foot-inner{padding:28px 22px 26px;gap:18px}
  /* the grove keeps the same footing as on desktop: a tree line along the top
     edge, wide enough to read as one, stopping short of the rounded corners */
  .foot-forest{top:-72px;height:94px}
  .foot-forest-row{transform:translateX(-50%) scale(.56)}
  .fir:nth-child(n+17){display:none}
  .foot-mid{display:none}
  .foot-note{padding:16px 22px 52px}
  .foot-note-text{font-size:15px}
  .foot-socials{margin-top:-50px}
  .foot-tile{width:86px;height:86px;border-radius:22px}
  .foot-tile + .foot-tile{margin-left:-18px}
  .foot-tile svg{width:38px;height:38px}
}

/* ─── 18 · Night sky (dark theme, home only) ───────────────────── */
.sky{position:fixed;inset:0;width:100%;height:100%;z-index:0;pointer-events:none}
html[data-theme="light"] .sky{display:none}

/* 404 */
.nf{min-height:70vh;display:grid;place-items:center;text-align:center;padding-top:var(--nav-h)}
.nf-in{display:grid;gap:18px;justify-items:center}

/* ─── 19 · Press + touch ───────────────────────────────────────────
   Everything you can press gives way the moment you touch it, and comes
   back at its own pace: --press-t is the way in, each component's own
   transition is the way out. It lives here, last, so a press always wins
   over the hover it interrupts, and so the list of what is pressable can
   be read in one place. A new control joins one of these lists and adds
   transform + opacity to its own transition.

   :is() on purpose: its specificity is that of its strongest entry, which
   is what lets a plain .soc:active outrank .soc:hover and .to-top.show. */
:is(.btn,.nav-link,.nav-cv,.veil-cta,.topic,.send:not(:disabled,[aria-disabled="true"])):active{
  transform:scale(var(--press));opacity:var(--press-o);
  transition-duration:var(--press-t);transition-delay:0s;
}
:is(.nav-icon,.nav-burger,.nav-logo,.soc,.to-top.show,.lb-close,.lb-nav button:not(:disabled)):active{
  transform:scale(var(--press-sm));opacity:var(--press-o);
  transition-duration:var(--press-t);transition-delay:0s;
}
:is(.pcard,.all-work,.pnav-next,.shot-frame):active{
  transform:scale(var(--press-lg));opacity:var(--press-o);
  transition-duration:var(--press-t);transition-delay:0s;
}
:is(.filter,.sort,.back,.pnav-back,.link-arrow,.contact-mail,.cv-pop a,.foot-note-text a,a.row):active{
  opacity:var(--press-fade);
  transition-duration:var(--press-t);transition-delay:0s;
}
.menu.open .menu-link:active{opacity:var(--press-fade);transition:opacity var(--press-t) ease}
.foot-tile:active{transition-duration:var(--press-t)}
@media (prefers-reduced-motion:reduce){
  :root{--press:1;--press-sm:1;--press-lg:1;--press-o:.7}
}

/* The target is bigger than the thing you see. Each control keeps its drawn
   size and grows an invisible ::after up to 44px, stopping short of its
   neighbours; `.to-top` is left out of the position rule because it is fixed. */
:is(.nav-link,.nav-icon,.nav-cv,.nav-logo,.nav-burger,.sort,.filter,.back,.pnav-back,.topic,.link-arrow,.contact-mail,.btn-sm,.lb-nav button,.foot-note-text a){position:relative}
:is(.nav-link,.nav-icon,.nav-cv,.nav-logo,.nav-burger,.sort,.filter,.back,.pnav-back,.topic,.link-arrow,.contact-mail,.btn-sm,.lb-nav button,.foot-note-text a,.to-top)::after{content:"";position:absolute}
.nav-link::after,.nav-icon::after,.nav-cv::after,.nav-logo::after{inset:-6px 0}
.nav-burger::after{inset:-6px -4px}
.sort::after{inset:-10px -6px}
.back::after,.pnav-back::after{inset:-13px -6px}
.filter::after{inset:-5px -4px}
.topic::after,.btn-sm::after,.lb-nav button::after{inset:-3px}
.link-arrow::after{inset:-9px 0}
.contact-mail::after{inset:-12px 0}
.foot-note-text a::after{inset:-14px -4px}
.to-top::after{inset:-1px}
/* rows of a menu touch each other, so here the row itself has to grow */
@media (pointer:coarse){ .cv-pop a{min-height:44px} }
/* the labels are drawn for a desktop; on a phone nothing goes under Apple's 11pt */
@media (max-width:860px){
  .flag,.fact dt,.rail-h,.row-kind,.tl-type,.tl-today span,.pbs-k,.tally dt,.foot-eyebrow,.foot-note-label{font-size:11px}
}
