/* Inter Tight (SIL Open Font License), self-hosted variable weight 400–700.
   Latin subset only — 45 KB. Arabic has no coverage here and correctly falls
   through to the system Arabic face. */
@font-face{
  font-family:"Inter Tight";
  font-style:normal;
  font-weight:400 700;
  font-display:swap;
  src:url("/site/fonts/inter-tight-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;
}


/* ============================================================
   TOKENS — see docs/DIRECTION.md
   Monochrome by decision. The primary action is the highest-contrast
   element on the page rather than the most colourful one, so there is no
   accent hue to get wrong. Colour appears once, on availability, where it
   carries meaning.
   ============================================================ */
:root{
  color-scheme:dark;
  --bg:#0A0A0A;
  --surface:#121212;   /* --faint is solved for 4.6:1 against this; do not lighten it */
  --surface-2:#191919;
  --raise:#262626;
  --line:rgba(255,255,255,.12);   /* .07 drew a 1.15:1 edge; boundaries need to be seen */
  --line-2:rgba(255,255,255,.22);
  --ink:#F2F2F0;
  --muted:#A3A3A0;
  --faint:#7E7E7B;      /* solved for 4.6:1 on surface and page */
  --action:#F2F2F0;          /* CTA fill, inverted against the page */
  --action-ink:#0A0A0A;
  --action-hover:#FFFFFF;
  --green:#3FB27F;
  --green-bg:rgba(63,178,127,.12);
  --green-bg-2:rgba(63,178,127,.2);
  --bg-fade:rgba(10,10,10,.72);

  /* type scale — nine steps, down from eighteen ad hoc sizes */
  --t-display:clamp(46px,7.2vw,92px);
  --t-h1:clamp(30px,3.9vw,52px);
  --t-h2:26px;
  --t-h3:21px;
  --t-lead:clamp(19px,2vw,23px);
  --t-h4:18px;
  --t-body:16.5px;
  --t-sm:15px;
  --t-xs:13px;
  --t-micro:11.5px;

  /* spacing — base 4 */
  --s1:4px;  --s2:8px;   --s3:12px; --s4:16px; --s5:20px; --s6:24px;
  --s7:32px; --s8:40px;  --s9:56px; --s10:72px; --s11:96px; --s12:128px;

  --sidebar-w:280px;
  --pad-x:52px;
  --measure:720px;
  --max:1180px;
  --r:10px;
  --r-sm:6px;
  --r-pill:100px;

  --ease:cubic-bezier(.16,1,.3,1);
  --f-sys:Arial,"Helvetica Neue",Helvetica,sans-serif;
  --f-alt:"Inter Tight",Arial,"Helvetica Neue",sans-serif;
  --font:var(--f-alt);
}
/* ------------------------------------------------------------
   THE LIGHT THEME
   It used to exist only under html[data-theme="light"], which a deferred script
   writes. So the entire light theme depended on JavaScript: the page painted
   dark first and flipped up to 900ms later on a slow connection, and with
   scripts off it never arrived. On a site whose argument is that the response
   should be complete before any script runs, that was the one thing that wasn't.

   Now the values live in one place (--l-*) and two short rules apply them: the
   OS preference, which costs nothing and paints correctly on the first frame,
   and the explicit attribute the toggle writes, which must still win over it.
   Two assignment lists, one set of values, so they cannot drift.
   ------------------------------------------------------------ */
:root{
  --l-bg:#F2F2F0;            /* a real step below the card, not #FBFBFA */
  --l-surface:#FFFFFF;
  --l-surface-2:#F7F7F5;
  --l-raise:#E4E4E0;
  --l-line:rgba(0,0,0,.14);  /* was .08, which drew a 1.2:1 edge on a white card */
  --l-line-2:rgba(0,0,0,.22);
  --l-ink:#0C0C0C;
  --l-muted:#4B4B48;
  --l-faint:#6B6B69;         /* re-solved for the darker page: 4.76:1 on bg, 5.34:1 on surface */
  --l-action:#0C0C0C;
  --l-action-ink:#FBFBFA;
  --l-action-hover:#000000;
  --l-green:#09623B;         /* re-solved for the darker page: 5.54:1 on its own wash,
                                and 4.86:1 on the hover wash, which #0B7647 never cleared */
  --l-green-bg:rgba(11,118,71,.12);
  --l-green-bg-2:rgba(11,118,71,.2);
  --l-bg-fade:rgba(242,242,240,.72);
}
html[data-theme="light"]{
  color-scheme:light;
  --bg:var(--l-bg); --surface:var(--l-surface); --surface-2:var(--l-surface-2);
  --raise:var(--l-raise); --line:var(--l-line); --line-2:var(--l-line-2);
  --ink:var(--l-ink); --muted:var(--l-muted); --faint:var(--l-faint);
  --action:var(--l-action); --action-ink:var(--l-action-ink);
  --action-hover:var(--l-action-hover); --green:var(--l-green);
  --green-bg:var(--l-green-bg); --green-bg-2:var(--l-green-bg-2);
  --bg-fade:var(--l-bg-fade);
}
@media (prefers-color-scheme:light){
  /* only until the visitor has chosen; data-theme always wins */
  html:not([data-theme="dark"]):not([data-theme="light"]){
    color-scheme:light;
    --bg:var(--l-bg); --surface:var(--l-surface); --surface-2:var(--l-surface-2);
    --raise:var(--l-raise); --line:var(--l-line); --line-2:var(--l-line-2);
    --ink:var(--l-ink); --muted:var(--l-muted); --faint:var(--l-faint);
    --action:var(--l-action); --action-ink:var(--l-action-ink);
    --action-hover:var(--l-action-hover); --green:var(--l-green);
    --green-bg:var(--l-green-bg); --green-bg-2:var(--l-green-bg-2);
    --bg-fade:var(--l-bg-fade);
  }
}

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{
  background:var(--bg);color:var(--muted);
  font-family:var(--font);font-size:var(--t-body);line-height:1.55;
  -webkit-font-smoothing:antialiased;overflow:hidden;
}
button{font:inherit;color:inherit;background:none;border:none;cursor:pointer}
a{color:inherit;text-decoration:none}
::selection{background:var(--action);color:var(--action-ink)}
.mono{font-family:var(--font);font-size:var(--t-micro);letter-spacing:.08em;text-transform:uppercase;font-weight:700;color:var(--faint)}

.shell{display:grid;grid-template-columns:var(--sidebar-w) 1fr;height:100dvh}

/* ---------------- SIDEBAR ---------------- */
.side{border-right:1px solid var(--line);display:flex;flex-direction:column;padding:34px 26px 24px}
/* The mark is the Y lockup with the registered symbol, 200x169. It is a solid
   white shape, so masking it means the alpha becomes the stencil and --ink
   supplies the colour: one asset, correct in both themes, no disc needed. */
.logo{
  display:block;width:46px;aspect-ratio:200/169;
  background:var(--ink);margin-bottom:18px;
  -webkit-mask:url("/site/brand/mark.svg") left center/contain no-repeat;
          mask:url("/site/brand/mark.svg") left center/contain no-repeat;
  transition:opacity .18s var(--ease);
}
.logo:hover{opacity:.75}

.wordmark{font-size:var(--t-h4);font-weight:700;color:var(--ink);letter-spacing:-.01em;line-height:1.1}
.wordmark span{display:block;font-size:var(--t-sm);font-weight:400;color:var(--faint)}

.nav{margin-top:38px}
.nav a{
  display:flex;align-items:center;gap:11px;padding:11px 0;
  border-bottom:1px solid var(--line);font-size:var(--t-sm);color:var(--muted);
  transition:color .18s var(--ease),padding-left .26s var(--ease);
}
.nav a:hover{color:var(--ink);padding-left:6px}
.nav a[aria-current="page"]{color:var(--ink);font-weight:600}

.ctx{margin-top:auto;padding-top:20px;display:grid;gap:10px}
/* The availability pill is the booking control. It always read as one, so it
   is a link to cal.com that the embed upgrades to a popup in place. */
.pill{display:inline-flex;align-items:center;gap:7px;background:var(--green-bg);color:var(--green);border-radius:var(--r-pill);padding:5px 11px;font-size:var(--t-micro);font-weight:500;width:max-content;
  cursor:pointer;transition:background .18s var(--ease)}
.pill:hover{background:var(--green-bg-2)}
.pill i{width:6px;height:6px;border-radius:50%;background:var(--green);animation:p 2.6s var(--ease) infinite}
@keyframes p{0%,100%{opacity:1}50%{opacity:.3}}
.urlbar .lock{color:var(--faint);flex:0 0 auto}
.urlbar code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:var(--t-micro);color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.urlbar code b{color:var(--faint);font-weight:400}
.badge{display:flex;align-items:center;gap:8px;background:var(--surface);color:var(--muted);border:1px solid var(--line);border-radius:var(--r-sm);padding:8px 11px;font-size:var(--t-xs);font-weight:500;width:max-content}

.proto{margin-top:16px;padding-top:14px;border-top:1px dashed var(--line-2);display:grid;gap:8px}
.proto .k{font-size:var(--t-micro);letter-spacing:.1em;text-transform:uppercase;color:var(--faint);font-weight:700}
.proto-btns{display:grid;grid-template-columns:repeat(3,1fr);gap:2px;padding:2px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface)}
.proto-btns button{border:0;border-radius:var(--r-sm);padding:6px 0;font-size:var(--t-micro);line-height:1;color:var(--faint);background:transparent;cursor:pointer;transition:background .16s var(--ease),color .16s var(--ease)}
.proto-btns button:hover{color:var(--muted);background:var(--surface-2)}
.proto-btns button[aria-pressed="true"]{background:var(--raise);color:var(--ink);font-weight:600}


/* ---------------- MAIN ---------------- */
.main{position:relative;overflow:hidden}
.stage{height:100dvh;overflow-y:auto;padding:0 var(--pad-x);scrollbar-width:thin;scrollbar-color:var(--line-2) transparent}
.stage::-webkit-scrollbar{width:9px}
.stage::-webkit-scrollbar-thumb{background:var(--line-2);border-radius:var(--r-sm)}
.wrap{max-width:var(--max);margin:0 auto}

/* hero + prompt */
/* Height follows the content. min-height:74vh with centred content left a
   large dead band between the chips and the sections below. */
.hero{display:flex;flex-direction:column;justify-content:center;
  padding:clamp(56px,9dvh,124px) 0 clamp(44px,6dvh,84px)}
h1.display{
  font-size:var(--t-display);font-weight:700;color:var(--ink);
  letter-spacing:-.032em;line-height:1.02;margin-bottom:20px;text-wrap:balance;
}
.sub{font-size:var(--t-body);max-width:56ch;line-height:1.6;margin-bottom:38px}

.promptblock{width:min(var(--measure),100%)}
body.answered .promptblock{position:fixed;left:var(--sidebar-w);right:0;bottom:34px;width:auto;padding:0 var(--pad-x);z-index:30;display:flex;justify-content:center;pointer-events:none}
body.answered .promptblock::before{content:"";position:absolute;left:0;right:0;bottom:-34px;height:150px;background:linear-gradient(to top,var(--bg) 45%,var(--bg-fade) 74%,transparent);pointer-events:none!important}
.pinner{width:min(var(--measure),100%);position:relative;z-index:1;pointer-events:auto}

.field{
  display:flex;align-items:center;gap:12px;background:var(--surface);
  border:1px solid var(--line-2);border-radius:var(--r);padding:0 12px 0 18px;height:60px;
  transition:border-color .22s var(--ease),background .22s var(--ease);
}
.field:focus-within{border-color:var(--line-2);background:var(--surface-2)}
.field input{
  /* the input took its 20px content height inside a 60px box, so two thirds of
     the site's primary control did nothing when clicked */
  flex:1;align-self:stretch;background:none;border:none;outline:none;color:var(--ink);
  font-family:var(--font);font-size:var(--t-body);letter-spacing:-.006em;caret-color:var(--ink);
}
.field input::placeholder{color:var(--faint)}
.go{
  width:38px;height:38px;border-radius:var(--r-sm);background:var(--raise);color:var(--faint);
  display:grid;place-items:center;flex:0 0 auto;opacity:.55;pointer-events:none;
  transition:background .18s var(--ease),color .18s var(--ease),opacity .18s var(--ease);
}
.field.on .go{background:var(--action);color:var(--action-ink);opacity:1;pointer-events:auto}
.field.on .go:hover{background:var(--action-hover)}

.chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}
.chip{
  border:1px solid var(--line);background:var(--surface);border-radius:var(--r-pill);
  padding:9px 15px;font-size:var(--t-sm);color:var(--muted);
  transition:color .18s var(--ease),border-color .18s var(--ease),background .18s var(--ease);
}
.chip:hover{color:var(--ink);border-color:var(--line-2);background:var(--surface-2)}
body.leaving .hero .sub,body.leaving .hero h1,body.leaving .chips{opacity:0;transform:translateY(-8px);transition:opacity .16s var(--ease),transform .16s var(--ease)}
/* Returning home is the mirror of leaving, choreographed as one movement.
   Leaving lifts the hero out and docks the field. Coming back, the field FLIPs
   460ms up into the hero while the hero settles down into place behind it —
   same easing curve, staggered so the page resolves as a single system rather
   than the field moving alone through static content. */
body.returning .hero h1,
body.returning .hero .sub,
body.returning .chips,
body.returning #homescroll{opacity:0;transform:translateY(-10px)}

.hero h1,.hero .sub,.chips,#homescroll{
  transition:opacity .40s var(--ease),transform .40s var(--ease)}
.hero h1{transition-delay:60ms}
.hero .sub{transition-delay:110ms}
.chips{transition-delay:260ms}        /* the field has cleared this band by now */
#homescroll{transition-delay:260ms}
body.answered .hero h1,body.answered .hero .sub,body.answered .chips,body.answered #homescroll{display:none}
body.answered .hero{min-height:0;padding:0}
body.answered .stage{padding-top:70px;padding-bottom:250px}

/* ---------------- HOME SCROLL CONTENT ---------------- */
.sec{padding:58px 0;border-top:1px solid var(--line)}
.sec h2{font-size:var(--t-h2);font-weight:700;color:var(--ink);letter-spacing:-.015em;margin-bottom:var(--s3)}
.sec .lede{font-size:var(--t-sm);margin-bottom:28px;max-width:60ch}

.faq{border-top:1px solid var(--line)}
.faq details{border-bottom:1px solid var(--line)}
.faq summary{
  list-style:none;cursor:pointer;padding:19px 4px;display:flex;justify-content:space-between;
  align-items:center;gap:20px;font-size:var(--t-body);color:var(--ink);font-weight:500;
  transition:padding-left .24s var(--ease);
}
.faq summary::-webkit-details-marker{display:none}
.faq summary:hover{padding-left:8px}
.faq summary .x{color:var(--faint);flex:0 0 auto;transition:transform .26s var(--ease)}
.faq details[open] summary .x{transform:rotate(45deg);color:var(--ink)}
.alink{display:inline-block;margin-top:10px;font-size:var(--t-xs);color:var(--ink);text-decoration:none;border-bottom:1px solid transparent;transition:border-color .2s}
.alink:hover{border-bottom-color:currentColor}
.faq .a{padding:0 4px 22px;max-width:70ch;font-size:var(--t-sm);line-height:1.68}

.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
.card{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  padding:20px;display:flex;flex-direction:column;gap:9px;text-align:left;
  transition:border-color .22s var(--ease),background .22s var(--ease),transform .22s var(--ease);
}
.card:hover{border-color:var(--line-2);background:var(--surface-2);transform:translateY(-2px)}
.card .thumb{aspect-ratio:16/9;border-radius:var(--r-sm);background:var(--surface-2);border:1px solid var(--line);display:grid;place-items:center;color:var(--faint);font-size:var(--t-micro);letter-spacing:.08em}
.card h3{font-size:var(--t-body);font-weight:600;color:var(--ink);letter-spacing:-.01em;line-height:1.3}
.card p{font-size:var(--t-xs);line-height:1.55}
.card .meta{margin-top:auto;padding-top:6px;font-size:var(--t-micro);color:var(--faint);letter-spacing:.04em;text-transform:uppercase;font-weight:700}

/* ---------------- ANSWER ---------------- */
/* margin:0 auto on a grid item makes it shrink-wrap, so the column was as wide
   as whatever happened to be on the page: the heading, rule and CTA sat at a
   different x on every page, moving up to 88px on navigation. */
.answer{display:none;width:100%;max-width:var(--max);margin:0 auto}
body.answered .answer{display:block}
.answer .b{opacity:0;transform:translateY(15px)}
.answer.reveal .b{opacity:1;transform:none;transition:opacity .4s var(--ease),transform .4s var(--ease)}
.hr{height:1px;background:var(--line);transform:scaleX(0);transform-origin:left}
.answer.reveal .hr{transform:scaleX(1);transition:transform .62s var(--ease)}

.ahead{display:flex;align-items:flex-start;justify-content:space-between;gap:26px;padding-bottom:18px}
.ahead h1{flex:1;font-size:var(--t-h1);font-weight:700;color:var(--ink);letter-spacing:-.026em;line-height:1.04;text-wrap:balance}
.ahead .tag{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-pill);padding:6px 12px;font-size:var(--t-micro);color:var(--faint);white-space:nowrap;font-weight:600;letter-spacing:.05em;text-transform:uppercase}
.abody{padding-top:34px;display:grid;gap:32px}
.lead{font-size:var(--t-lead);line-height:1.4;letter-spacing:-.017em;text-wrap:pretty;color:var(--ink);max-width:820px;font-weight:500}
.prose p{max-width:var(--measure);font-size:var(--t-body);line-height:1.7}
.prose p+p{margin-top:14px}

.facts{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}
.fact{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:17px 18px}
.fact .k{font-size:var(--t-micro);letter-spacing:.09em;text-transform:uppercase;color:var(--faint);font-weight:700;display:block;margin-bottom:7px}
.fact .v{font-size:var(--t-body);color:var(--ink);letter-spacing:-.01em;line-height:1.35}

.pgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:14px}
.p{color:inherit;text-decoration:none;background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:22px;display:flex;flex-direction:column;gap:11px;text-align:left;transition:border-color .22s var(--ease),background .22s var(--ease)}
.p:hover{border-color:var(--line-2);background:var(--surface-2)}
.p .top{display:flex;justify-content:space-between;font-size:var(--t-micro);color:var(--faint);letter-spacing:.06em;text-transform:uppercase;font-weight:700}
.p h2{font-size:var(--t-h3);font-weight:700;color:var(--ink);letter-spacing:-.018em}
.p p{font-size:var(--t-sm);line-height:1.6}
.p .ms{margin-top:auto;padding-top:14px;border-top:1px solid var(--line);display:flex;gap:26px}
.p .ms b{display:block;font-size:var(--t-body);color:var(--ink);font-weight:700;letter-spacing:-.01em}
.p .ms span{font-size:var(--t-micro);color:var(--faint);letter-spacing:.07em;text-transform:uppercase;font-weight:700}

.rows{border-top:1px solid var(--line)}
.row{display:grid;grid-template-columns:1fr 150px 80px;gap:18px;align-items:baseline;padding:16px 4px;border-bottom:1px solid var(--line);width:100%;text-align:left;transition:background .2s var(--ease),padding-left .2s var(--ease)}
.row:hover{padding-left:10px;background:var(--surface)}
.row .t{font-size:var(--t-body);color:var(--ink);letter-spacing:-.008em}
.row .t small{display:block;font-size:var(--t-sm);color:var(--muted);margin-top:3px;letter-spacing:0}
.row .m,.row .r{font-size:var(--t-micro);color:var(--faint);letter-spacing:.06em;text-transform:uppercase;font-weight:700}
.row .r{text-align:right}
/* The two metadata tracks were absolute while the content track was 1fr, so the
   content was the only thing that could yield. The fixed tracks plus their gaps
   take 266px, which on a phone left 61px for the words and set an eight-word
   sentence over seven lines. Below the width where all three fit, the row wraps:
   the name and its line take the full measure, and the metadata falls beneath as
   one row, the way every other metadata line on the site already reads. */
@media (max-width:900px){
  .row{display:flex;flex-wrap:wrap;align-items:baseline;gap:9px 18px;padding:18px 4px}
  .row .t{flex:1 1 100%;min-width:0}
  .row .r{text-align:left}
}

.steps{display:grid;gap:14px}
.step{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:20px 22px;display:grid;grid-template-columns:34px 1fr;gap:18px}
.step .n{width:26px;height:26px;border-radius:var(--r-sm);background:var(--action);color:var(--action-ink);display:grid;place-items:center;font-size:var(--t-micro);font-weight:700}
.step h2{font-size:var(--t-body);font-weight:600;color:var(--ink);margin-bottom:6px;letter-spacing:-.01em}
.step p{font-size:var(--t-sm);line-height:1.65;max-width:64ch}

.afoot{display:flex;flex-direction:column;gap:20px;padding-top:6px}
.src{font-size:var(--t-micro);color:var(--faint);letter-spacing:.05em;text-transform:uppercase;font-weight:700}
/* As a heading, .src labels the block beneath it rather than sitting in it.
   Uppercase micro type has no descender to lend it room, so at margin 0 the
   label's own box lands on the content's top edge. */
h2.src,h3.src{margin-bottom:var(--s4)}
.follows{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.follows .l{font-size:var(--t-micro);color:var(--faint);letter-spacing:.08em;text-transform:uppercase;font-weight:700;margin-right:4px}
.cta{display:inline-flex;align-items:center;gap:10px;background:var(--action);color:var(--action-ink);text-decoration:none;border-radius:var(--r-sm);padding:14px 20px;font-size:var(--t-sm);font-weight:500;align-self:flex-start;transition:background .2s var(--ease),transform .2s var(--ease)}
.cta:hover{background:var(--action-hover);transform:translateX(3px)}
.miss{border-left:2px solid var(--ink);padding-left:20px}
.miss strong{display:block;color:var(--ink);font-size:var(--t-h4);font-weight:600;margin-bottom:9px}
.miss p{max-width:60ch;font-size:var(--t-sm)}

/* opacity:0 hides it from the mouse, not the keyboard: both buttons stayed in
   the tab order on the homepage, and one of them left the site. */
.topbar{position:absolute;top:0;right:0;height:60px;display:flex;align-items:center;padding:0 var(--pad-x);gap:8px;opacity:0;visibility:hidden;transition:opacity .24s var(--ease),visibility 0s linear .24s;z-index:20;pointer-events:none}
body.answered .topbar{opacity:1;visibility:visible;pointer-events:auto;transition-delay:0s}
.topbar button{border:1px solid var(--line);background:var(--surface);border-radius:var(--r-sm);padding:7px 12px;font-size:var(--t-xs);color:var(--muted);transition:color .18s var(--ease),border-color .18s var(--ease)}
.topbar button:hover{color:var(--ink);border-color:var(--line-2)}

@media (max-width:1080px){:root{--pad-x:28px;--sidebar-w:248px}.facts{grid-template-columns:repeat(2,1fr)}.pgrid,.cards{grid-template-columns:1fr}}
/* two 162px columns made the longer values wrap and the top row 26px taller */
@media (max-width:520px){.facts{grid-template-columns:1fr}}
@media (max-width:720px){.shell{grid-template-columns:1fr}body.answered .promptblock{left:0}}
@media (prefers-reduced-motion:reduce){*{animation-duration:.001ms!important;transition-duration:.001ms!important}.answer .b{opacity:1;transform:none}.hr{transform:scaleX(1)}}

/* ============================================================
   ACCESSIBILITY — visible focus, skip link, screen-reader text
   ============================================================ */
.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}
.skiplink{
  position:fixed;top:8px;left:8px;z-index:200;transform:translateY(-200%);
  background:var(--action);color:var(--action-ink);border-radius:var(--r-sm);padding:12px 18px;
  font-size:var(--t-sm);font-weight:600;transition:transform .18s var(--ease)
}
.skiplink:focus-visible{transform:translateY(0)}
/* No border-radius here: the outline already follows the element's own
   corners, and setting it made every chip animate from a 100px pill to a 6px
   rectangle the moment a keyboard reached it. */
a:focus-visible,button:focus-visible,summary:focus-visible,input:focus-visible,
[tabindex]:focus-visible{outline:2px solid var(--ink);outline-offset:3px}
.field:focus-within{outline:2px solid var(--ink);outline-offset:2px}
#input:focus-visible,.stage:focus-visible,.answer:focus-visible{outline:none}
.nav a[aria-current="page"]{color:var(--ink)}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms !important;transition-duration:.01ms !important}
}
button.cta{border:none;cursor:pointer;font:inherit}


/* ============================================================
   MOBILE NAVIGATION
   The sidebar is the only navigation, so below 720px it used to vanish and
   leave the site unnavigable. It is now the same <aside> moved off-canvas and
   translated back in — one element, transformed, never a duplicated menu.
   ============================================================ */
.navtoggle{display:none}

@media (max-width:720px){
  .navtoggle{display:inline-flex;align-items:center;justify-content:center;position:fixed;top:12px;left:12px;z-index:60;
    width:44px;height:44px;padding:0;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);cursor:pointer;
    transition:transform .34s var(--ease)}
  /* when open it rides to the panel's inner edge so it stops covering the logo */
  body.nav-open .navtoggle{transform:translateX(calc(min(320px,86vw) - 68px))}
  .bars{display:block;position:relative;width:18px;height:11px}
  .bars i{position:absolute;left:0;right:0;height:1.5px;background:var(--ink);border-radius:var(--r-sm);
    transition:transform .3s var(--ease),opacity .18s var(--ease)}
  .bars i:nth-child(1){top:0}
  .bars i:nth-child(2){top:5px}
  .bars i:nth-child(3){top:10px}
  body.nav-open .bars i:nth-child(1){transform:translateY(5px) rotate(45deg)}
  body.nav-open .bars i:nth-child(2){opacity:0}
  body.nav-open .bars i:nth-child(3){transform:translateY(-5px) rotate(-45deg)}

  .side{position:fixed;top:0;bottom:0;left:0;z-index:55;width:min(320px,86vw);
    background:var(--bg);border-right:1px solid var(--line);overflow-y:auto;
    transform:translateX(-100%);transition:transform .34s var(--ease)}
  body.nav-open .side{transform:translateX(0)}

  /* scrim, with no extra element in the markup */
  .shell::before{content:"";position:fixed;inset:0;z-index:50;background:rgba(0,0,0,.5);
    opacity:0;pointer-events:none;transition:opacity .3s var(--ease)}
  body.nav-open .shell::before{opacity:1;pointer-events:auto}
  /* body already has overflow:hidden and is not the scroller, so locking it did
     nothing: the page kept scrolling 300px behind the open panel. .stage is the
     scroll container, so that is what has to be held. */
  body.nav-open .stage{overflow:hidden}
  .side{overscroll-behavior:contain}

  /* The menu button is 44px tall at top:12, so its centre is at 34. The topbar
     was 60px tall with its buttons centred at 30, which put them 4px higher
     than the hamburger and read as misaligned. 68px puts both centres on 34. */
  .topbar{padding-left:64px;height:68px}

  /* body.answered .stage sets padding-top:70px and outranks a bare .stage
     rule, so the title was landing 14px under the controls. This needs the
     same specificity to win, and enough air to read as a margin. */
  .stage{padding-top:68px}
  body.answered .stage{padding-top:104px}
}

/* Comfortable targets where there is no cursor. .go is the submit button: it is
   the single most-tapped control on the site and was 38px, under the minimum.
   .logo and .badge are links too, and were 39px and 38px. */
@media (pointer:coarse){
  #nav a,.chip,.topbar button,.proto-btns button,.card,.go,.logo,.badge,.pill{min-height:44px}
  .pill{padding:5px 16px}
  .proto-btns button,.go{display:inline-flex;align-items:center;justify-content:center}
  .go{min-width:44px}
  .badge{align-items:center}
}

/* One designed focus ring, everywhere it matters */
a:focus-visible,button:focus-visible,summary:focus-visible,input:focus-visible{
  outline:2px solid var(--ink);outline-offset:3px}
/* these are focused programmatically to move the reading position, not by the user */
#input:focus-visible,.stage:focus-visible,.answer:focus-visible,#answer-title:focus-visible{outline:none}

/* Secondary action on the miss page — present, but never the loud one */
.ctarow{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.cta.ghost{background:transparent;color:var(--muted);border:1px solid var(--line);text-decoration:none}
.cta.ghost:hover{color:var(--ink);border-color:var(--raise);background:var(--surface)}

/* ============================================================
   TYPOGRAPHIC FINISH
   ============================================================ */
html{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}
h2,h3{text-wrap:balance}
p,.a,.sub{text-wrap:pretty}
::selection{background:var(--action);color:var(--action-ink)}

/* ============================================================
   FIXED FURNITURE — reserve real space for it
   The menu button and the docked ask field are both fixed over .stage, which
   is the scroll container. Space for them is measured in app.js and published
   as --dock-h / --header-h, so content can never scroll underneath either.
   ============================================================ */
:root{--dock-h:0px;--header-h:0px}

/* The stage physically ends above the docked field rather than being padded
   past it, so short pages cannot land a CTA behind the input. */
/* The stage resizes on the same curve and duration as the field's FLIP into
   the docked position, so the layout moves with the field rather than
   snapping around it. */
.stage{transition:height 460ms cubic-bezier(.16,1,.3,1)}
body.answered .stage{height:calc(100dvh - var(--dock-h));padding-bottom:40px}

/* A short answer used to sit at the top of a full-height stage with a few
   hundred pixels of nothing between it and the docked field. Contact and
   pricing are the shortest pages and read as truncated because of it.
   align-content centres the block only while it fits; the moment the content
   is taller than the stage this behaves exactly like the old top alignment,
   and "safe" keeps the first line reachable rather than clipped above. */
body.answered .wrap{min-height:100%;display:grid;align-content:safe center}

@media (max-width:720px){
  /* a band behind the menu button, so content passes under a surface instead
     of colliding with a floating control */
  /* Solid until past the lowest control, then a short fade. The old stops put
     the fade at 58% of a band sized off the menu button alone, so a heading
     scrolling past the Back and Home buttons showed through it. */
  .shell::after{content:"";position:fixed;top:0;left:0;right:0;height:var(--header-h);
    z-index:45;pointer-events:none;
    background:linear-gradient(to bottom,var(--bg) 0 72%,var(--bg-fade) 88%,transparent)}
  body.nav-open .shell::after{opacity:0}

  /* The band exists to put a surface between scrolled content and the floating
     controls. It was covering the Back and Home buttons too, because they sit
     at z-index 20 and the band at 45, so they washed out under it. Controls go
     above the band, content stays under it. */
  .topbar{z-index:50}
}

/* ============================================================
   CARD IMAGERY
   Covers and thumbnails bleed to the card edge — inset artwork inside a
   bordered box reads as a placeholder, full bleed reads as the work.
   ============================================================ */
.card,.p{color:inherit;text-decoration:none}

.p .cover,.card img.thumb{
  display:block;width:calc(100% + 44px);height:auto;margin:-22px -22px 4px;
  border-radius:var(--r) var(--r) 0 0;border:0;border-bottom:1px solid var(--line);
  object-fit:cover;background:var(--surface-2)}

.card img.thumb{width:calc(100% + 40px);margin:-20px -20px 3px;height:auto;
  display:block;place-items:unset;color:transparent;font-size:0}

/* A project with no live site has no cover to capture. It keeps the same band
   so the grid stays even, rather than one short card among the tall ones. */
.p .cover--none{aspect-ratio:900/472;background:var(--surface-2)}

/* the image itself carries the hover, so the card feels like one object */
.p .cover,.card img.thumb{transition:opacity .28s var(--ease)}
.p:hover .cover,.card:hover img.thumb{opacity:.9}

/* ============================================================
   FAQ — animate the disclosure instead of snapping it open
   ============================================================ */
.faq details::details-content{
  block-size:0;overflow:hidden;
  transition:block-size .34s var(--ease),content-visibility .34s allow-discrete;
  interpolate-size:allow-keywords}
.faq details[open]::details-content{block-size:auto}
.faq summary{transition:padding-left .22s var(--ease),color .22s var(--ease)}
.faq details[open] summary{color:var(--ink)}
.faq details:hover{background:linear-gradient(90deg,var(--surface) 0%,transparent 60%)}

/* Navigation: the active item is marked by weight and a hairline, not a block
   of colour. Indentation on hover carries the interaction instead. */
.nav a{position:relative;padding-left:2px}
.nav a[aria-current="page"]::before{content:"";position:absolute;left:-12px;top:50%;
  transform:translateY(-50%);width:2px;height:15px;border-radius:var(--r-sm);background:var(--ink)}

/* The partner badge is a link to the real profile, so it needs link styling */
.badge{text-decoration:none;transition:border-color .2s var(--ease),background .2s var(--ease)}
.badge:hover{border-color:var(--line-2);background:var(--surface-2)}
.badge img{border-radius:var(--r-sm);flex:0 0 auto}

/* ============================================================
   TYPE — one rule per step, so line height and tracking travel with
   the size instead of being set ad hoc at each call site.
   ============================================================ */
h1.display{line-height:.98;letter-spacing:-.035em}
.ahead h1{line-height:1.04;letter-spacing:-.028em}
h2{line-height:1.15;letter-spacing:-.02em}
h3{line-height:1.25;letter-spacing:-.015em}
.card h3{line-height:1.3;letter-spacing:-.012em}
.lead{line-height:1.38;letter-spacing:-.016em}
.sub{line-height:1.6}
.mono,.meta,.src,.proto .k,.ahead .tag{letter-spacing:.08em;text-transform:uppercase;font-weight:700}

/* ============================================================
   THE MARK AT SCALE
   The Y is the only piece of brand this site owns. It appears once more,
   very large and cropped by the viewport, as the hero's only ornament.
   Decorative, so it is hidden from assistive tech and skipped in print.
   ============================================================ */
.heromark{
  position:absolute;right:calc(var(--pad-x) * -1);top:50%;
  translate:0 -50%;
  width:min(42vw,520px);aspect-ratio:160/169;
  background:var(--ink);opacity:.035;
  -webkit-mask:url("/site/brand/mark-y.svg") center/contain no-repeat;
          mask:url("/site/brand/mark-y.svg") center/contain no-repeat;
  pointer-events:none;z-index:0;
}
html[data-theme="light"] .heromark{opacity:.05}
.hero{position:relative}
.hero > *:not(.heromark){position:relative;z-index:1}

/* The bug was never that the mark appears on answer pages. It is that in the
   answered state the hero collapses to zero height, and a mark anchored to the
   centre of a zero-height element hangs down across the content.

   So it re-anchors instead of disappearing: on an answer page it is fixed to
   the viewport, cropped by the same right edge, sitting behind the content
   rather than in front of it. One element, moved between two states, never a
   second copy. It fades through the transition so the change of anchor is a
   crossfade rather than a jump. */
.heromark{transition:opacity .34s var(--ease)}
body.leaving .heromark,body.returning .heromark{opacity:0}

body.answered .hero{position:static}
body.answered .heromark{
  position:fixed;right:calc(var(--pad-x) * -1);top:50%;translate:0 -50%;
  opacity:.03;
}
html[data-theme="light"] body.answered .heromark{opacity:.045}
/* content sits above the mark, which is the half that was actually missing */
body.answered .answer{position:relative;z-index:1}

@media (max-width:900px){.heromark{display:none}}
@media print{.heromark{display:none}}

/* ============================================================
   LONG FORM
   Articles are answer pages that keep going, so they reuse .abody and add
   only what prose needs: a measure, a heading rhythm, and a table, because
   the most useful thing this site publishes is a matrix of what was measured.
   ============================================================ */
/* Grid and flex items default to min-width:auto, so a wide <pre> or table
   refuses to shrink and pushes the whole column past the viewport instead of
   scrolling inside its own box. This is what min-width:0 is for. */
.abody > *{min-width:0}
.art{max-width:var(--measure);min-width:0}
.art > *{min-width:0;max-width:100%}
.art pre,.art .tablewrap{max-width:100%}
.ahead h1{min-width:0}
.art code{overflow-wrap:anywhere}
.art > * + *{margin-top:18px}
.art h2{font-size:var(--t-h2);font-weight:700;color:var(--ink);letter-spacing:-.02em;
  margin-top:44px;text-wrap:balance}
.art h3{font-size:var(--t-h3);font-weight:600;color:var(--ink);letter-spacing:-.015em;margin-top:32px}
.art p{font-size:var(--t-body);line-height:1.72}
.art strong{color:var(--ink);font-weight:600}
.art a:not(.cta){color:var(--ink);border-bottom:1px solid var(--line-2);transition:border-color .18s var(--ease)}
.art a:not(.cta):hover{border-bottom-color:currentColor}

.art ul,.art ol{padding-left:22px;display:grid;gap:9px}
.art li{font-size:var(--t-body);line-height:1.65}
.art li::marker{color:var(--faint)}

.art code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.88em;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-sm);padding:1px 5px;color:var(--ink)}
.art pre{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  padding:16px 18px;overflow-x:auto;font-size:var(--t-xs);line-height:1.6}
.art pre code{background:none;border:0;padding:0;font-size:inherit;color:var(--muted)}

.art blockquote{border-left:2px solid var(--line-2);padding-left:18px;color:var(--ink);
  font-size:var(--t-lead);line-height:1.45;letter-spacing:-.014em}

/* Wide tables scroll inside their own box; the page never scrolls sideways. */
.tablewrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--r);max-width:100%}
.art table{border-collapse:collapse;width:100%;font-size:var(--t-sm);min-width:520px}
.art th,.art td{text-align:left;padding:11px 14px;border-bottom:1px solid var(--line);vertical-align:top}
.art thead th{font-size:var(--t-micro);letter-spacing:.07em;text-transform:uppercase;color:var(--faint);
  font-weight:700;background:var(--surface)}
.art tbody tr:last-child td{border-bottom:0}
.art td b{color:var(--ink);font-weight:600}
.art .yes{color:var(--green);font-weight:600}
.art .no{color:var(--ink);font-weight:600}

.artmeta{display:flex;flex-wrap:wrap;gap:18px;font-size:var(--t-micro);letter-spacing:.06em;
  text-transform:uppercase;color:var(--faint);font-weight:700}
.artnote{font-size:var(--t-sm);line-height:1.6;color:var(--muted);
  border-left:2px solid var(--line);padding-left:16px}

/* ============================================================
   PRINT
   body{overflow:hidden} and .stage{height:100dvh;overflow-y:auto} were never
   reset, so every page printed as a single sheet holding one viewport: a
   3362px article came out as one page. Pricing and process are exactly the
   pages someone saves to send to a colleague.
   ============================================================ */
@media print{
  html,body{height:auto;overflow:visible;background:#fff;color:#000}
  .shell{display:block;height:auto}
  .main{overflow:visible}
  .stage{height:auto;max-height:none;overflow:visible;padding:0;transition:none}
  body.answered .stage{height:auto;padding:0}
  body.answered .wrap{display:block;min-height:0}
  .side,.topbar,.navtoggle,.promptblock,.follows,.skiplink,.heromark{display:none!important}
  .answer,.wrap{max-width:none;width:auto}
  .art,.prose p{max-width:none}
  a[href^="http"]::after{content:" (" attr(href) ")";font-size:.8em;color:#555}
  .card,.p,.fact,.step,pre,.tablewrap{break-inside:avoid;border:1px solid #ccc}
  h1,h2,h3{break-after:avoid}
  .cta{border:1px solid #000;color:#000;background:none}
}

/* ============================================================
   WIDE CONTENT SCROLLS, AND NOW SAYS SO
   A code block hid 187px of a curl command on a 1280 desktop and 573px of it
   on a phone, cut mid-URL. On macOS the scrollbar is hidden until used, so
   there was nothing at all to indicate the rest existed. The fade is painted
   by the container and only appears while there is more to the right.
   ============================================================ */
.tablewrap,.art pre{
  background-image:linear-gradient(to right,transparent calc(100% - 48px),var(--surface));
  background-repeat:no-repeat;
  background-attachment:local, scroll;
  scrollbar-width:thin;
  scrollbar-color:var(--line-2) transparent;
}
.tablewrap{background-color:var(--bg)}
.art pre{background-color:var(--surface)}
.tablewrap::-webkit-scrollbar,.art pre::-webkit-scrollbar{height:8px}
.tablewrap::-webkit-scrollbar-thumb,.art pre::-webkit-scrollbar-thumb{
  background:var(--line-2);border-radius:var(--r-sm)}

/* A phone has no room for a 520px table, so let the code wrap rather than
   asking someone to drag a 332px window across it. */
@media (max-width:720px){
  .art pre{white-space:pre-wrap;overflow-wrap:anywhere}
  .art pre,.tablewrap{background-image:none}
}

/* ============================================================
   SHORT VIEWPORTS
   At 200% zoom on a 1280x800 laptop the viewport is 640x400 CSS px, and the
   masking band plus the docked field took 112px of it, leaving a 288px window
   to read a 1007px page through. Below that height the field returns to the
   flow and the page gets its screen back.
   ============================================================ */
@media (max-height:560px){
  body.answered .promptblock{position:static;padding:0;width:min(var(--measure),100%);
    display:block;pointer-events:auto}
  body.answered .promptblock::before{display:none}
  body.answered .stage{height:auto;max-height:100dvh;padding-top:24px;padding-bottom:24px}
  .stage{transition:none}
}

/* ============================================================
   CHANGING THEME
   The swap used to arrive in three speeds at once: the page and the headings
   changed in one frame while the cards, chips, field and nav eased over 181 to
   211ms, so the ground moved and the furniture caught up.

   One clock instead, and only while the switch is actually being used. The
   class is added by the toggle and removed when the transition ends, so the
   theme never animates on load, which would otherwise turn a correct first
   paint into a visible fade.
   ============================================================ */
html.theming, html.theming *, html.theming *::before, html.theming *::after{
  transition:background-color .2s var(--ease),
             border-color .2s var(--ease),
             color .2s var(--ease),
             fill .2s var(--ease) !important;
}
@media (prefers-reduced-motion:reduce){
  html.theming, html.theming *{transition:none!important}
}
