.authorbox{border:1px solid #e5e7eb;background:#fff;border-radius:14px;padding:22px 24px;margin-top:40px;display:flex;gap:18px;align-items:flex-start}.authorbox .ab-mark{flex-shrink:0;width:46px;height:46px;border-radius:10px;background:#f5f6ff;display:grid;place-items:center}.authorbox .ab-mark svg{width:26px;height:26px}.authorbox h4{font-size:15.5px;margin-bottom:4px;color:#0d0e23}.authorbox p{font-size:14px;color:#4b5563;line-height:1.6;margin-bottom:8px}.authorbox a{font-size:13.5px;font-weight:600;color:#4042E4}@media(max-width:620px){.authorbox{flex-direction:column;gap:12px}}

/* ==================================================================
   DESIGN SYSTEM v2 - tokens, lockup, header, container, spacing.
   Appended last so it wins on source order. See shellkit.py.
   ================================================================== */
:root{
  /* 8px spacing scale - the only permitted vertical/horizontal steps */
  --sp-1:8px;  --sp-2:16px; --sp-3:24px; --sp-4:32px; --sp-5:40px;
  --sp-6:48px; --sp-7:56px; --sp-8:64px; --sp-10:80px; --sp-12:96px;
  --sp-16:128px; --sp-20:160px;
  /* one container, one gutter */
  --container:var(--container-default);
  /* --measure / --measure-wide / --gutter now live ONLY in the layout
     contract below. They were defined here too (32em, 46em, 24px) and were
     shadowed purely by source order - one reordering away from silently
     reverting the site to a 544px measure. */
  --header-h:72px;
  /* #9ca3af failed AA at 2.5:1 and is off-palette. --ink-4 now resolves to a
     palette colour that measures 4.77:1 on white. */
  --ink-4:#6b7280;
  --ink-muted:#6b7280;
}
@media (max-width:860px){ :root{ --gutter:20px; --header-h:64px; } }

html{scroll-padding-top:calc(var(--header-h) + var(--sp-2))}

/* ---------- container: header and content share one edge ----------
   .nav-in / .container are the legacy shell (blog, /proof/, /deadline-checker).
   They get the same container so the two shells cannot drift apart again. */
.wrap,.wrap-narrow,.nv-in,.nav-in,.nav-inner,.container{
  max-width:var(--container);
  margin-inline:auto;
  padding-inline:var(--gutter);
}
/* the reading column constrains the TEXT, not the box, so its left edge stays
   on the container edge and lines up with the header above it */
.wrap-narrow > *{max-width:var(--measure-wide)}
.wrap-narrow > .split,
.wrap-narrow > .scroller,
.wrap-narrow > figure,
.wrap-narrow > .qset,
.wrap-narrow > .res,
.wrap-narrow > .toolbar,
.wrap-narrow > .plans{max-width:none}

/* ---------- vertical rhythm ---------- */
.sec{padding-block:var(--sp-12)}
.sec-air{padding-block:var(--sp-12)}
/* deliberately short bands: a one-line statement or a closing CTA. At 96px
   these measured 56-72% empty. */
.sec-tight{padding-block:var(--sp-8)}
/* Wide tables scroll inside their own box instead of pushing the page sideways. */
/* ---- legacy shell: burger + drawer, matching the modern shell ---- */
.burger{display:none;width:40px;height:40px;align-items:center;justify-content:center;
background:none;border:0;cursor:pointer;padding:0;flex:none}
.burger span,.burger span::before,.burger span::after{content:"";display:block;width:20px;
height:2px;background:var(--ink-0);border-radius:2px;position:relative}
.burger span::before{position:absolute;top:-6px}
.burger span::after{position:absolute;top:6px}
@media (max-width:999.98px){
  .nav-links,.nav-cta{display:none}
  .nav-in .burger,.nav-inner .burger{display:flex;margin-inline-start:auto}
  .nav-in.open,.nav-inner.open{height:auto;flex-wrap:wrap;padding-bottom:18px}
  .nav-in.open .nav-links,.nav-inner.open .nav-links{display:flex;flex-direction:column;
    align-items:flex-start;width:100%;gap:2px;margin:12px 0 0}
  .nav-in.open .nav-cta,.nav-inner.open .nav-cta{display:inline-flex;margin-top:12px}
  .nav-in.open .nav-links > a,.nav-in.open .dd > button{width:100%;padding:11px 0;
    border-bottom:1px solid var(--rule);justify-content:space-between}
  .nav-in.open .dd{width:100%}
  .nav-in.open .dd-menu{position:static;opacity:1;visibility:visible;transform:none;
    box-shadow:none;border:0;padding:2px 0;min-width:0;width:100%}
}
/* ---- Part F: mega-menu navigation ----
   The BASE .dd rules live here, not in a page stylesheet. They previously only
   existed on the modern shell, so when the dropdowns were rolled out to the
   legacy pages the menus had no positioning and rendered inline and expanded,
   pushing the nav 695px off centre and overflowing the page. */
.dd{position:relative}
.dd>button{font:inherit;font-size:14.5px;color:var(--ink-2);background:none;border:0;
cursor:pointer;padding:6px 0;display:inline-flex;align-items:center;gap:6px}
.dd>button:hover{color:var(--ink-0)}
.dd>button svg{transition:transform .18s ease}
.dd[data-open="1"]>button svg{transform:rotate(180deg)}
.dd-menu{position:absolute;top:calc(100% + 10px);left:-16px;min-width:290px;
background:var(--paper);border:1px solid var(--rule);border-radius:12px;padding:8px;
box-shadow:0 12px 34px rgba(13,14,35,.10);opacity:0;visibility:hidden;
transform:translateY(-6px);transition:opacity .16s ease,transform .16s ease,
visibility .16s ease;z-index:80}
.dd[data-open="1"] .dd-menu{opacity:1;visibility:visible;transform:none}
.dd-menu a{display:flex;gap:10px;align-items:flex-start;padding:10px 12px;
border-radius:8px;text-decoration:none}
.dd-menu a:hover{background:var(--bg-tint);text-decoration:none}
.dd-menu .tl{font-size:14.5px;color:var(--ink-0);font-weight:600;display:block}
.dd-menu .ds{font-family:var(--f-mono);font-size:11px;color:var(--ink-3);display:block;
margin-top:3px;line-height:1.45}
.dd-menu .mi{width:18px;height:18px;color:var(--brand);flex:none;margin-top:2px}
.dd-menu .mt{display:block;min-width:0}
/* A mega panel is centred on the LINKS GROUP, not on its own button. Centring
   each panel under its button pushed the leftmost one off-screen at 1000px. */
.nv-links,.nav-links{position:relative}
.dd-mega{position:static}
.dd-mega .dd-menu.mega{display:grid;grid-template-columns:repeat(2,minmax(240px,1fr));
gap:2px;width:min(600px,calc(100vw - 32px));left:50%;margin-left:0;
transform:translateX(-50%) translateY(-6px)}
.dd-mega[data-open="1"] .dd-menu.mega{transform:translateX(-50%)}
@media (max-width:999.98px){
  .dd-mega{position:relative}
  .dd-mega .dd-menu.mega{display:block;width:100%;left:0;transform:none}
  .dd-mega[data-open="1"] .dd-menu.mega{transform:none}
  .dd-menu .mi{display:none}
}
.tbl-wrap{display:block;max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}
/* A 70-char URL inside a <code> block set a 332px min-content floor in a
   279px box and pushed the page 6px sideways at 375. Long tokens wrap. */
code,kbd,samp{overflow-wrap:anywhere}

/* ==================================================================
   LAYOUT CONTRACT v2
   One container. No page, shell or component may invent its own width.

   measure  570px = 73 characters COUNTED at 17px/28px Inter (not computed).
            680px counts 89 and fails the 60-75 target.
   Nesting rule: a section's container matches its content. Prose-only
   sections use the measure as their container - a 570px column centred
   inside a 1200px container is 48% dead space, which is the defect this
   block exists to remove.
   ================================================================== */
:root{
  --container:1200px;
  --container-wide:1200px;      /* deliberately identical - one width */
  --container-default:1200px;   /* legacy alias, same value */
  --container-prose:calc(var(--measure) + var(--gutter) * 2);
  --measure:570px;
  --measure-wide:880px;
  --measure-center:470px;
  --rule-tint:#e0e2ff;
  --gutter:24px;
  --gutter-desktop:24px;
  --gutter-mobile:20px;
  --sp-1:8px;  --sp-2:16px; --sp-3:24px; --sp-4:32px; --sp-5:40px;
  --sp-6:48px; --sp-7:56px; --sp-8:64px; --sp-9:72px; --sp-10:80px;
  --sp-12:96px; --sp-16:128px; --sp-20:160px;
}
@media (max-width:860px){
  :root{--gutter:var(--gutter-mobile)}
}

/* ---- the one container. Every wrapper, every shell, every page ---- */
.wrap,.wrap-wide,.wrap-narrow,.wrap-narrow.rise,.container,.container.narrow,
.nav-in,.nv-in,.nav-inner,.foot-in,.list-head,.cards,
.post.art-grid,.art-grid{
  max-width:var(--container);
  margin-inline:auto;
  padding-inline:var(--gutter);
}

/* ---- prose-only sections: the container IS the measure ----
   .legal-nav-inner is here, not in the group above: /terms and /privacy are
   prose-only pages, so their nav has to align with the prose shell beneath
   it. At 1200 it sat 291px wider on each side than the text it framed. */
.wrap-prose,.legal-shell,.legal-nav-inner,.sec-prose > .wrap,.sec-prose > .wrap-narrow,
.sec-prose > .wrap-mw,.sec-prose > .container,.sec-prose > .wrap-wide{
  max-width:calc(var(--measure) + var(--gutter) * 2);
  margin-inline:auto;
  padding-inline:var(--gutter);
}

/* ---- artifact bands: prose plus one table/diagram ---- */
.wrap-mw,.sec-mw > .wrap,.sec-mw > .wrap-narrow,.sec-mw > .wrap-prose,
.sec-mw > .container,.sec-mw > .wrap-wide{
  max-width:calc(var(--measure-wide) + var(--gutter) * 2);
  margin-inline:auto;
  padding-inline:var(--gutter);
}

/* prose never widens past the measure; tables, diagrams and grids break out */
.measure{max-width:min(var(--measure),52ch)}
.breakout{max-width:var(--measure-wide)}
.measure-center{max-width:var(--measure-center);margin-inline:auto}

/* the article grid has to fit inside the one container:
   220 + 570 + 260 + (2 x 40 gap) = 1130, inside the 1152 content box */
@media (min-width:1201px){
  .art-grid{grid-template-columns:220px minmax(0,var(--measure)) 260px;
    gap:var(--sp-5);justify-content:center}
}

/* ---- Part 2: dead-band treatments ----
   .band-split WAS a two-column grid, 1fr + 520px. Measured on the two bands
   that use it, the prose column came out 312px wide and ran far taller than
   the diagram beside it:
       /trust/    prose 312x867  art 520x334  -> 533px of empty column
       /product/  prose 312x1601 art 520x297  -> 1303px of empty column
   and 312px renders 33-39 characters a line against a 60-75 target. A grid is
   the wrong shape when one cell is three paragraphs and the other is a fixed
   360px diagram. The band now STACKS: prose at the measure, diagram beneath
   it, and the section's container is the measure so both fill it. */
.band-split{display:block}
.band-art{margin-top:var(--sp-6)}
.band-art svg{width:100%;height:auto;display:block}
.mx td{text-align:left}
.dd > button,.nv-links .dd > button{min-height:40px}

/* ---- content must FILL its container ----
   A centred container holding a narrower fixed-width child is still a
   lopsided band: the pricing row was 720px of grid sitting left inside an
   1152px box, which is the gap the screenshots circled. */
.pricerow{max-width:none;grid-template-columns:repeat(2,minmax(0,1fr))}
.intro-sub{max-width:min(var(--measure),52ch)}

/* ---- line length is FONT-RELATIVE, not a fixed pixel width ----
   A px cap only holds the character count at one font size. The same 570px
   column counts 67 characters at 17px and 97 at 12px, so every caption, note
   and small-print line broke the 75-character limit while the body copy sat
   inside it. 1ch scales with the font, so a ch cap holds the count steady across sizes.
   55ch measured 71-79 depending on word length; 52ch brings the worst
   line on the whole site to 75. min() keeps the
   body column at the counted 570px and lets smaller text cap itself. */
main p,main li,main dd,main blockquote,
main .wrap p,main .wrap-narrow p,main .wrap p.t-body,main .wrap p.t-lead,
main .wrap-narrow p.t-body,main .wrap-narrow p.t-lead,
main p.t-cap,main .wrap > p.t-cap,main .wrap-narrow > p.t-cap,
.note,.lede,.excerpt,.who,.t-cap,.t-body,.t-lead,.intro-sub,.legal-meta,
/* .list-head is a header element outside main, so `main p` never reached it */
.list-head p,.secbar p,
/* blog posts render outside the main element, so scope to article too */
article p,article li,article dd,.post-body p,.div-item p{
  max-width:min(var(--measure),52ch);
}

/* ---- monospace needs its own ch cap ----
   In a MONOSPACE font 1ch is exactly one character, so the 52ch above renders
   71-75 characters in Inter but exactly 52 in JetBrains Mono - under the 60
   floor. Measured: .t-cap at 13px came out 406px wide, median 47-52 chars.
   68ch puts mono captions at 68 characters, inside the same 60-75 band. */
main p.t-cap,main .wrap > p.t-cap,main .wrap-narrow > p.t-cap,.t-cap{
  max-width:min(var(--measure),68ch);
}
/* CONTRACT_END */
/* Product mega menu: a labelled group.
   .dd-menu.mega is a two-column grid, so a group heading has to span both
   tracks or it sits in a cell and reads as a fifth menu item. */
.dd-menu.mega .mega-h{
  grid-column:1 / -1;
  font-family:var(--f-mono);
  font-size:10.5px;
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--ink-3);
  padding:13px 12px 5px;
  margin-top:6px;
  border-top:1px solid var(--rule);
}
@media (max-width:860px){
  /* the mega collapses to a single block on mobile; the heading still spans */
  .dd-menu.mega .mega-h{grid-column:auto}
}

/* ================= PART 3: article template ================= */
.art-grid{
  display:grid;
  grid-template-columns:220px minmax(0,var(--measure)) 260px;
  align-items:start;                 /* required, or the rails stretch */
  gap:var(--sp-5);
  justify-content:center;
  max-width:var(--container);
  margin-inline:auto;
  padding-inline:var(--gutter);
}
.art-head{grid-column:2;grid-row:1;min-width:0}
.art-toc {grid-column:1;grid-row:2;position:sticky;top:96px}
.art-main{grid-column:2;grid-row:2;min-width:0}
.art-rail{grid-column:3;grid-row:2;position:sticky;top:96px}

/* --- left rail --- */
.art-toc > summary{display:none}
.toc-t{font-family:var(--f-mono);font-size:10.5px;letter-spacing:.12em;
text-transform:uppercase;color:var(--ink-4);margin:0 0 var(--sp-2)}
.toc-prog{height:2px;background:var(--rule);border-radius:2px;overflow:hidden;
margin-bottom:var(--sp-2)}
.toc-prog i{display:block;height:100%;width:0;background:var(--brand);
transition:width .12s linear}
.toc-list{list-style:none;margin:0;padding:0;border-left:1px solid var(--rule)}
.toc-list a{display:block;padding:5px 0 5px 12px;margin-left:-1px;
border-left:2px solid transparent;font-size:13.5px;line-height:1.4;
color:var(--ink-3);text-decoration:none}
.toc-list a:hover{color:var(--ink-0);text-decoration:none}
.toc-list a.on{color:var(--brand-dark);border-left-color:var(--brand);font-weight:600}
.toc-list .lv3 a{padding-left:24px;font-size:12.5px}

/* --- right rail --- */
.rail-card{border:1px solid var(--rule);border-radius:12px;padding:var(--sp-3);
background:var(--paper);margin-bottom:var(--sp-3)}
.rail-card.go-card{background:var(--bg-tint);border-color:transparent}
.rail-t{font-family:var(--f-mono);font-size:10.5px;letter-spacing:.12em;
text-transform:uppercase;color:var(--ink-4);margin:0 0 10px}
.rail-card h4{font-size:15px;font-weight:650;color:var(--ink-0);margin:0 0 6px;
letter-spacing:-.01em}
.rail-card p{font-size:13.5px;line-height:1.55;color:var(--ink-2);margin:0 0 12px}
.rail-btn{display:inline-flex;align-items:center;justify-content:center;
min-height:40px;padding:0 14px;border-radius:12px;background:var(--brand);
color:#fff;font-size:13.5px;font-weight:600;text-decoration:none}
.rail-btn:hover{text-decoration:none}
.rail-list{list-style:none;margin:0;padding:0}
.rail-list li + li{margin-top:10px;padding-top:10px;border-top:1px solid var(--rule)}
.rail-list a{font-size:13.5px;line-height:1.45;color:var(--ink-1);text-decoration:none;
display:block}
.rail-list a:hover{color:var(--brand-dark)}
.cite-box{font-family:var(--f-mono);font-size:11px;line-height:1.6;color:var(--ink-2);
background:var(--canvas);border:1px solid var(--rule);border-radius:8px;
padding:10px;overflow-wrap:anywhere}
.share a{font-size:13px;color:var(--ink-2);text-decoration:none;margin-right:12px}
.share a:hover{color:var(--brand-dark)}

/* --- prose may break out, prose itself never widens --- */
.art-main > *{max-width:min(var(--measure),52ch)}
.art-main > figure,.art-main > .tbl-wrap,.art-main > .pull,
.art-main > .art,.art-main > .scroller{max-width:var(--measure-wide);width:100%}
.art-main > .tbl-wrap,.art-main > .scroller{overflow-x:auto}
.art-main > .tbl-wrap::after,.art-main > .scroller::after{content:"scroll \2192";
display:none;font-family:var(--f-mono);font-size:10.5px;color:var(--ink-4)}
.pull{border-left:3px solid var(--brand);padding:var(--sp-1) 0 var(--sp-1) var(--sp-3);
font-size:20px;line-height:1.45;color:var(--ink-0);font-weight:500;
margin:var(--sp-4) 0}

/* --- right rail collapses below 1024, left rail below 1200 --- */
@media (max-width:1200px){
  .art-grid{grid-template-columns:minmax(0,var(--measure)) 280px}
  .art-head{grid-column:1}
  .art-toc{grid-column:1;grid-row:auto;position:static;top:auto;
    margin-bottom:var(--sp-4)}
  .art-toc > summary{display:list-item;cursor:pointer;font-family:var(--f-mono);
    font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-2);
    padding:10px 0;border-block:1px solid var(--rule)}
  .art-toc .toc-t,.art-toc .toc-prog{display:none}
  .art-main{grid-column:1;grid-row:auto}
  .art-rail{grid-column:2;grid-row:1/span 3}
}
@media (max-width:1024px){
  .art-grid{grid-template-columns:minmax(0,1fr)}
  .art-head,.art-toc,.art-main,.art-rail{grid-column:1;grid-row:auto}
  .art-rail{position:static;top:auto;margin-top:var(--sp-6);
    display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-3);
    align-items:start}
  .art-main > *{max-width:100%}
}
@media (max-width:760px){
  .art-rail{grid-template-columns:1fr}
  .art-main > .tbl-wrap::after,.art-main > .scroller::after{display:block}
}

/* ---- Part G: prose measure, applied where page CSS never did ---- */
/* A caption is not body prose. The prose measure (32em) resolved to 416px at
   13px and left 23 of 24 captions hugging the left of a 1200px band. */
/* 40em, not the prose or container token: JetBrains Mono at 13px fits ~66
   characters in 520px. 46em gave 598px and pushed captions past 75 chars,
   32em gave 416px and left them as a narrow column. */
.t-cap{max-width:min(var(--measure),68ch)}
/* main .wrap-narrow > p (0,1,2) outranks .t-cap (0,1,0), so these need to
   match on element AND class to win. */
/* Part 4: a caption follows its block instead of imposing its own centred
   box. It KEEPS the measure cap: .t-cap is monospace, and in a monospace
   font 1ch IS one character, so 54ch here is 54 characters - correct.
   (Dropping it first time round let captions run to 133 characters.) */
main p.t-cap,main .wrap > p.t-cap,main .wrap-narrow > p.t-cap{max-width:min(var(--measure),68ch);margin-inline:0;text-align:inherit}
p.t-cap.t-cap-center{text-align:center;margin-inline:auto}
main p.t-cap.t-cap-left,main .wrap > p.t-cap.t-cap-left,main .wrap-narrow > p.t-cap.t-cap-left,.t-cap-left{text-align:left;margin-inline:0}

main p,main li,main dd,main blockquote{max-width:min(var(--measure),52ch)}
/* .wrap-narrow > * carries --measure-wide so HEADINGS are not squeezed, but a
   class selector outranks "main p", so prose inside it kept the 46em box and
   ran to 102 characters. This is more specific and wins. */
.wrap-narrow > p,.wrap-narrow > li,.wrap-narrow > blockquote,main p.t-body,main p.t-lead,main .wrap-narrow > p{max-width:min(var(--measure),52ch)}
/* things that are not prose must not be clamped */
main table p,main table li,main .art p,main .logrow,main .cardgrid p,
main .rescards p,main .triad p,main .why p,main .stepcaps p,main .ptier p,
main figure p,main .dd-menu p{max-width:none}
/* ---- Part G: one button size and one radius ---- */
.primary.primary,.hov.hov,.go.go,.tgo.tgo,.lv-btn.lv-btn,.nav-cta.nav-cta,.btn.btn{
min-height:48px;border-radius:12px;display:inline-flex;align-items:center;
justify-content:center;padding-top:0;padding-bottom:0;line-height:1.1}

.sec-tight{padding-block:var(--sp-8)}
.sec-hero{padding-block:var(--sp-12) var(--sp-6)}
@media (max-width:860px){
  .sec{padding-block:var(--sp-8)}
  .sec-air{padding-block:var(--sp-10)}
  .sec-tight{padding-block:var(--sp-6)}
  .sec-hero{padding-block:var(--sp-8) var(--sp-5)}
}

/* ---------- the lockup ---------- */
.lockup{
  display:inline-flex;
  align-items:center;
  gap:var(--sp-1);
  text-decoration:none;
  color:var(--ink-0);
  font-family:var(--f-sans);
  font-weight:700;
  font-size:17px;
  line-height:1;
  letter-spacing:-.01em;
  white-space:nowrap;
}
.lockup:hover{text-decoration:none}
.lockup-mark{width:1.03em;height:1em;flex:none;display:block;color:var(--brand);/* viewBox is cropped to the ink (15 12 70 68), so this box IS the mark. Nudge onto the cap-band centre of the wordmark. */transform:translateY(-.2px)}
/* one span, so the two words share a baseline and normal tracking */
.lockup-word{display:inline-block;letter-spacing:-.01em}
.lockup-co{color:var(--emerald);letter-spacing:-.01em;margin:0;padding:0}
.lockup-dark{color:#fff}
.lockup-dark .lockup-mark{color:#fff}
.lockup:focus-visible{outline:2px solid var(--brand);outline-offset:4px;border-radius:4px}

/* ---------- header: identical on both shells ----------
   DESKTOP ONLY. Below 861px the shells have their own drawer behaviour
   (.nv-links stacks, .nv-cta hides behind the burger). Applying the flex row
   unconditionally overrode that and pushed a 458px nav across a 375px
   viewport, which is a horizontal scrollbar on every page. */
.nv,.nav{position:sticky;top:0;z-index:60}
@media (min-width:861px){
  /* Legacy .nav shell fallback (its own drawer breakpoint is 760px). */
  .nav-in,.nav-inner{
    height:var(--header-h);
    display:flex;
    align-items:center;
    gap:var(--sp-6);
    justify-content:space-between;
  }
  .nav-links{
    display:flex;
    align-items:center;
    gap:var(--sp-4);
    margin:0;
    margin-inline-start:auto;
  }
  .nav-cta{margin:0;flex:none}
  .nav-links > a{
    display:inline-flex;align-items:center;gap:6px;
    height:var(--sp-5);padding:0;
    font-size:15px;line-height:1;
  }
}
/* Modern .nv shell. Base state is the drawer bar; .nv-in.open grows it. */
.nv-in{
  height:var(--header-h);
  display:flex;
  align-items:center;
  gap:var(--sp-6);
  justify-content:space-between;
}
@media (min-width:1000px){
  /* Centred nav, BOTH shells. 1fr auto 1fr centres the middle column on the
     CONTAINER; margin-inline-start:auto centred it on nothing, which is why the
     nav sat up to 327px right of centre on the legacy pages. */
  .nv-in,.nav-in{
    display:grid;
    grid-template-columns:1fr auto 1fr;
    align-items:center;
  }
  .nv-in > .lockup,.nav-in > .lockup{justify-self:start}
  .nv-links,.nav-links{
    display:flex;align-items:center;gap:var(--sp-4);
    margin:0;justify-self:center;
  }
  .nv-cta{
    display:flex;align-items:center;gap:var(--sp-2);
    margin:0;flex:none;justify-self:end;
  }
  .nav-cta{margin:0;flex:none;justify-self:end}
  .nv-links > a,.nv-links .dd > button,.nav-links > a{
    display:inline-flex;align-items:center;gap:6px;
    height:var(--sp-5);padding:0;
    font-size:15px;line-height:1;
  }
}

/* ---------- control sizing: one height per role ---------- */
.tgo,.hov,.btn,.plan .go,.nv-cta .cta,.nv-cta a[class*="cta"]{
  min-height:var(--sp-6);
  display:inline-flex;align-items:center;justify-content:center;
  padding-block:0;
}
.btn.sec,.btn[disabled]{min-height:var(--sp-6)}

/* ---------- one card radius ---------- */
.art,.q,.plan,.pgrid,.scroller,.tcard,.linkbox,.res,.sub,figure.dg .fr{border-radius:12px}
.note{border-radius:0 12px 12px 0}

/* ---------- contrast: lift the mono meta off the failing grey ---------- */
.t-cap,.logrow .ts,.scroll-cue,.pmap td.ct,.hintline,.mx tr.yr td,
.lrow .u,.tmeta .k,.plan .yr,.scoreof,.rrow .rk,.pgrid .r,.so .k,.ev .meta{
  color:var(--ink-muted);
}
/* On the dark ground, #6b7280 measures 3.4-3.7:1 and #2B2DBC measures 1.7:1.
   Neither is legible; both are replaced with light tints of the same hues. */
.dark .t-cap,.dark .t-label,.dark .logrow .ts,.dark .ln-k,.dark .art-bar .nm,
.dark .so .k,.dark .tree .s,.dark .hintline,.dark .rrow .rk{color:#a9adc4}
.dark .badge.req,.dark .lv-l,.dark .tag.ok,.dark .art-bar .nm b{
  background:rgba(255,255,255,.12);color:#cfd2ff}
.dark code,.dark .rrow code{color:#a9b0ff}
/* #059669 on white is 3.8:1 at small sizes. Drop small mono figures to ink on
   LIGHT grounds only - scoping this globally put #1a1a2e on a #141530 card and
   measured 1.04:1, i.e. invisible. Caught by the composited contrast pass. */
.logrow .new,.pmap td .new,.rrow .new{color:var(--ink-1);font-weight:600}
.dark .logrow .new,.dark .pmap td .new,.dark .rrow .new{color:#7ee2b8}
/* brand-dark on an 8% brand tint is 1.4:1. Solid brand ground instead. */
.ev-ic{background:var(--brand);color:#fff}
/* small mono meta sits on white AND on #f5f6ff. #6b7280 measures 4.77:1 on
   white but only 4.49:1 on the tint, which misses AA. #4b5563 is on-palette
   and clears both. */
.pmap th,.mx th,.grants th,.ledger th,.scroll-cue,.pmap tr.grp td,.mx tr.yr td,
.t-cap,
.t-label,.art-bar .nm,.rrow .rk,.so .k,.tmeta .k,.pgrid .r,.ln-k,
.scoreof,.res-hd .rt,.chk .cr,.hintline,.lrow .u{color:#4b5563}
/* badges: tint grounds need the darker ink of their own hue */
.tag.ok,.badge.ok,.pill,.lv-r{color:#04613f}
.badge.warn,.tag.no{color:#8a3f07}
.lv-n,.badge.soft{color:#4b5563}
/* legacy blog badges sat at 3.37:1 on their tints */
.cat,.cat.ref,.fmt,.fmt.research,.pill.final,.pill.draft,
.card .cat,.card .cat.ref,.card .fmt,.card .fmt.research{color:#3a3f52}

/* /resources/: heading block then a card grid. The old 1.35fr/1fr rgrid left a
   340px void beside every list. */
.rhead{max-width:var(--measure-wide);margin-bottom:var(--sp-4)}
.rlist{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:var(--sp-2)}
.rlist a{display:block;padding:var(--sp-2) var(--sp-3);border:1px solid var(--rule);
border-radius:12px;background:var(--paper);text-decoration:none;
transition:transform 150ms ease,box-shadow 150ms ease,border-color 150ms ease}
.rlist a:hover{transform:translateY(-2px);border-color:var(--rule-strong);
box-shadow:0 1px 2px rgba(13,14,35,.04),0 8px 24px rgba(13,14,35,.06)}
.rlist .tl{font-size:16px;font-weight:600;color:var(--ink-0);display:block}
.rlist .ds{font-size:14px;color:var(--ink-2);display:block;margin-top:5px;line-height:1.5}
@media (max-width:640px){.rlist{grid-template-columns:1fr}}

/* ---------- measure: captions must not run the full container ---------- */
.t-cap{max-width:min(var(--measure),68ch)}















/* ================= ONE COLUMN v3 ================= */
:root{
  --col-prose:600px;
  --col-mixed:880px;
  --col-full:1200px;
  --body-size:19px;
  --body-lh:1.65;
}
/* --- the column. A section has exactly one, and it is centred. --- */
.wrap,.wrap-wide,.wrap-narrow,.wrap-narrow.rise,.wrap-prose,.wrap-mw,
.container,.container.narrow,.nav-in,.nv-in,.nav-inner,.legal-nav-inner,
.foot-in,.list-head,.cards,.art-grid,.post.art-grid{
  max-width:calc(var(--col-full) + var(--gutter) * 2);
  margin-inline:auto;
  padding-inline:var(--gutter);
}
.sec-prose > .wrap,.sec-prose > .wrap-narrow,.sec-prose > .wrap-wide,
.sec-prose > .wrap-prose,.sec-prose > .wrap-mw,.sec-prose > .container,
.wrap-prose,.legal-shell,.legal-nav-inner{
  max-width:calc(var(--col-prose) + var(--gutter) * 2);
  margin-inline:auto;
  padding-inline:var(--gutter);
}
.sec-mixed > .wrap,.sec-mixed > .wrap-narrow,.sec-mixed > .wrap-wide,
.sec-mixed > .wrap-prose,.sec-mixed > .wrap-mw,.sec-mixed > .container,
.sec-mw > .wrap,.sec-mw > .wrap-narrow,.sec-mw > .wrap-prose,
.sec-mw > .container,.sec-mw > .wrap-wide,.wrap-mw{
  max-width:calc(var(--col-mixed) + var(--gutter) * 2);
  margin-inline:auto;
  padding-inline:var(--gutter);
}

/* --- EVERY direct child of a column fills it. This is the whole fix. ---
   !important is deliberate: the competing caps are two- and three-class
   selectors (main .wrap > p.t-cap) that outrank any one-class rule here. */
.wrap > *,.wrap-wide > *,.wrap-narrow > *,.wrap-prose > *,.wrap-mw > *,
.container > *,.legal-shell > *{
  max-width:none !important;
  margin-inline:0 !important;
}

/* --- ONE prose cap, and it is font-relative ---
   A pixel cap only holds the character count at one font size: 640px counts
   73 characters at 19px but 104 at 12.5px, which is how notes and list items
   kept breaking the limit. 1ch scales with the font, so min() holds the count
   steady at every size while --col-prose sets the ceiling. Measured:
     19px -> 600px -> 70 chars (the column binds, so prose fills it exactly)
     15px -> 475px -> 71        12.5px -> 395px -> 73
   --col-prose is 600 so that at body size min() picks the column exactly and
   prose FILLS its column in a prose-only section rather than sitting short. */
main p,main li,main dd,main blockquote,main figcaption,
.wrap p,.wrap li,.wrap dd,.wrap blockquote,
.wrap-narrow p,.wrap-narrow li,.wrap-narrow dd,
.wrap-wide p,.wrap-wide li,.wrap-mw p,.wrap-mw li,
.container p,.container li,.legal-shell p,.legal-shell li,
.qa p,.note,article p,article li,.post-body p{
  max-width:min(var(--col-prose),55ch) !important;
}
/* 55ch is tuned so that at 19px it coincides with --col-prose and body prose
   FILLS its column. The smaller faces need a tighter value or they overshoot:
   .qa p (15px) and list items (14.5px) measured 76-77 characters at 55ch. */
.qa p,.note p,.callout p,main li,.wrap li,.wrap-narrow li,.container li,
article li,.legal-shell li{
  max-width:min(var(--col-prose),50ch) !important;
}
/* A paragraph keeps the readable cap whether it is a direct child or nested.
   The first version let direct children fill the column, which pushed prose in
   1200px sections to 166 characters a line. In a --col-prose section the cap
   equals the column, so the same rule covers both cases. */
/* direct-child prose obeys the same cap - see above */
/* Single-line labels and callout BOXES fill the column: they never wrap, so a
   readability cap only shrinks the box and breaks the shared right edge.
   Wrapping text inside a callout is still capped by the prose rule above. */
p.t-label,.t-label,.legal-meta,div.note,.callout,.legal-callout{
  max-width:none !important;
}
p.t-cap,.t-cap{
  max-width:min(var(--col-prose),55ch) !important;
}

/* --- content wrappers are transparent ---
   Most sections are column > .rise > [label, heading, prose, caption], so the
   real elements are grandchildren. .rise fills the column; its children must
   fill it too, or the right edges stay ragged one level down. */
.rise > *,.intro > *,.rhead > *,.hero-copy > *,.band-split > div > *{
  max-width:none !important;
  margin-inline:0 !important;
}
/* MONO CAPTIONS carried arbitrary caps of their own - 530px (68ch), 359px
   (52ch), 580px, 619px. They now share ONE width with every other text
   element in the section, --col-prose. Letting them fill a 1200px column
   instead put a 13px caption on a 152-character line. */
p.t-cap,p.t-label,.t-cap,.t-label,figcaption{
  max-width:min(var(--col-prose),55ch) !important;
}
/* body prose keeps the readable cap even when its siblings are wider; this is
   the one authorised exception to right-edge equality */
.rise > p.t-body,.rise > p.lede,.intro > p.t-body,.rhead > p.t-body,
.band-split > div > p.t-body{
  max-width:var(--col-prose) !important;
}

/* --- body type: 19px/1.65 in a 640px column counts 73 characters ---
   .t-cap / .t-label are MONO captions and eyebrows, not body copy: the first
   version of this rule caught .wrap > p and pushed them from 13px to 19px. */
p.t-body,p.lede,.post-body p,article p,.legal-shell p,
.wrap > p:not(.t-cap):not(.t-label),
.wrap-narrow > p:not(.t-cap):not(.t-label){
  font-size:var(--body-size);
  line-height:var(--body-lh);
}

/* --- ALIGNMENT: left by default ---
   Measured on a large enterprise sustainability platform's marketing site:
   365 left-aligned text elements to 4 centred, and all four of those belong
   to a third-party cookie dialog - their own content never centres. Every
   left edge sits at one x (80px), and the text carries max-width:none.

   Ours ran 824 left to 46 centred, and the centred ones staircased, because
   text-align:center centres inside each element's OWN box. On /pricing/ the
   heading, prose and caption were 880 / 600 / 429 wide on a shared left edge
   at 513, so their optical centres landed at 953 / 813 / 727 - drifting left
   down the block. That is the staircase in the screenshot, and no amount of
   container work fixes it because the containers were already correct.

   So: left is the default, and centring is only valid when every child is the
   SAME width as the column, which makes the optical centres coincide. */
.cta-wrap,.intro-hero,.splash{text-align:left !important}
.cta-wrap > *,.intro-hero > *,.splash > *{
  width:auto;
  margin-inline:0 !important;
  text-align:left !important;
}
/* .measure-center existed only to centre a narrow block inside a wider one,
   which is the staircase generator. It is now a plain left-aligned cap. */
.measure-center{margin-inline:0 !important}

/* --- /trust/ hero: the second line's descenders met the block beneath --- */
.sec-hero h1,.hero-copy h1{
  line-height:1.14;
  padding-bottom:.06em;
}
.sec-hero h1{margin-bottom:var(--sp-3)}
/* A horizontally scrolling container's border box includes its scrollbar, so
   its bottom edge sat 2px BELOW the top of the caption after it. The caption
   carries an inline margin-top, so the override has to be !important. */
.scroller{padding-bottom:6px}
.scroller + p,.scroller + .t-cap,.scroller + *{
  margin-top:var(--sp-3) !important;
}
/* ONECOL_END */
