/* apple.css — the fluid-interface layer, shared by every static page.
   Loaded AFTER each page's inline <style>, so it wins on conflicts.
   § numbers refer to Apple's Designing Fluid Interfaces principles. */

:root{
  /* §4 behaviour over animation. Critically damped (damping 1.0): reaches the
     target and stops. No overshoot, because nothing here is thrown by a gesture. */
  --ease-settle:cubic-bezier(.32,.72,0,1);
  /* §7 the return path mirrors the outbound one */
  --ease-settle-back:cubic-bezier(.68,0,1,.28);
  --resp-press:100ms;  /* §1 press feedback must read as instant */
  --resp-ui:320ms;     /* §4 response 0.3-0.4s */
}

/* ── §1 Response: feedback on pointer-DOWN, not on release ───────────── */
.btn,button,[role="button"],.card,.chip,.node,.nn-node,.row-link,.tool-btn,
.land-btn,.add-btn,.link-btn,.close-btn,.gate-btn,.stepper,.cal-cell,.pills>*{
  -webkit-tap-highlight-color:transparent;
  touch-action:manipulation;              /* kills the ~300ms tap delay */
}
.btn:active,button:active,[role="button"]:active,.card:active,.chip:active,
.node:active,.nn-node:active,.row-link:active,.tool-btn:active,.land-btn:active,
.add-btn:active,.link-btn:active,.gate-btn:active,.stepper:active{
  transform:scale(.978);
  transition:transform var(--resp-press) ease-out;
}

/* ── §11 Frame-level smoothness: animate transform/opacity only ───────── */
/* was: padding-left 8px -> 20px on hover, which relays out the whole grid row */
#work .row-link{transition:background var(--resp-press) ease-out,transform var(--resp-ui) var(--ease-settle)}
#work .row-link:hover{padding-left:8px;transform:translateX(12px)}
#work .row-link:active{transform:translateX(12px) scale(.99)}
.go{transition:transform var(--resp-ui) var(--ease-settle)}

/* ── §4/§7 Disclosure settles, and returns along the same curve ───────── */
.xp{transition:grid-template-rows var(--resp-ui) var(--ease-settle)}
li.open .xp{transition-timing-function:var(--ease-settle-back)}
.chev{transition:transform var(--resp-ui) var(--ease-settle)}

/* ── §12 Materials: chrome is a translucent layer, content passes under ── */
/* :has(>.nav) scopes this to the three pages that carry the site nav. */
header:has(>.nav){
  position:fixed;                          /* was absolute — chrome stays */
  /* 78%: enough body that the material still reads as light when the dark
     work band scrolls under it, rather than washing out to grey-on-grey */
  background:color-mix(in srgb,var(--paper) 78%,transparent);
  backdrop-filter:blur(24px) saturate(180%);
  -webkit-backdrop-filter:blur(24px) saturate(180%);
  /* §12 scroll edge effect: the material fades out instead of ending on a 1px rule */
  -webkit-mask-image:linear-gradient(#000 calc(100% - 14px),transparent);
  mask-image:linear-gradient(#000 calc(100% - 14px),transparent);
}
/* padding-block only: a `padding` shorthand here would zero out the
   horizontal inset .wrap sets on the same element, and the nav would
   run off the left edge on mobile. */
header:has(>.nav) .nav{padding-block:20px 26px}
/* §16 wayfinding: chrome that stays must not land on the thing you jumped to */
html:has(header>.nav) [id]{scroll-margin-top:92px}
/* §12 vibrancy: type over a translucent surface needs contrast AND weight.
   The muted grey these links inherit is legible on paper but collapses once
   the dark work band passes underneath. Near-ink holds in both. */
header:has(>.nav) .nav-links a{color:var(--ink);font-weight:500;letter-spacing:.07em}
header:has(>.nav) .nav-links a:hover{color:var(--accent)}

/* ── §10 Touch targets: a 12px mono link is a ~16px tall tap target ───── */
/* Only on coarse pointers, so the desktop nav keeps its tight rhythm. */
@media (pointer:coarse){
  .nav-links{gap:6px}
  .nav-links a{padding:12px 8px;margin:-12px 0}   /* negative margin keeps the visual height */
  .foot a,.band-foot a{padding:6px 0;display:inline-block}
}

/* ── §15 Typography: let the line breaks be deliberate ───────────────── */
h1,h2,h3,h4,.stack .row,.quote,.hero-copy{font-optical-sizing:auto;text-wrap:balance}
p,li,.desc,.lead{text-wrap:pretty}

/* ── scroll reveals (driven by /apple.js or a page's own script) ──────── */
/* hidden only under .js, so no-JS visitors always see everything */
.js .rv{opacity:0;transform:translateY(14px);transition:opacity .6s ease,transform .6s var(--ease-settle)}
.js .rv.in{opacity:1;transform:none}

/* ── §14 Accessibility: three independent signals, three answers ─────── */
@media (prefers-reduced-motion:reduce){
  /* not "no feedback" — a non-vestibular equivalent: cross-fade, never travel */
  html{scroll-behavior:auto}
  *,*::before,*::after{
    transition-property:opacity,background-color,border-color,color,fill!important;
    transition-duration:180ms!important;
    /* complete animations instantly instead of cancelling them — `forwards`
       fills still land on their end state, so nothing stays hidden */
    animation-duration:.01ms!important;
    animation-iteration-count:1!important;
    animation-delay:0s!important;
  }
  *:active{transform:none!important}
  .btn:active,button:active,[role="button"]:active,.row-link:active{opacity:.68}
  #work .row-link:hover{transform:none}
}
@media (prefers-reduced-transparency:reduce){
  header:has(>.nav){
    background:var(--paper);
    backdrop-filter:none;-webkit-backdrop-filter:none;
    mask-image:none;-webkit-mask-image:none;
    border-bottom:1px solid var(--line);
  }
}
@media (prefers-contrast:more){
  header:has(>.nav){background:var(--paper);backdrop-filter:none;-webkit-backdrop-filter:none;mask-image:none;-webkit-mask-image:none;border-bottom:1px solid var(--ink)}
  .btn-line,.btn-ghost,.chip,.card{border-color:currentColor}
  .nav-links a,.ch p,.hero-copy,.desc{color:var(--ink)}
}
