*,*::before,*::after{box-sizing:border-box}
:root{
  --ground:#eef1f1; --surface:#fff; --surface-2:#f7f9f9;
  /* Light-mode tokens darkened to clear WCAG AA (4.5:1) for normal text. Measured,
     not guessed: --muted was 4.23 on the page ground and carried the lede, the
     changelog dates and the nav links; --good was 4.01 on white; --accent-ink was
     4.48 on its own 8% pill. The dark-mode set already passed (6.4-7.5) and is
     unchanged. Each new value is the smallest darkening that clears the bar, so
     the palette still reads as it did. */
  --ink:#0e1a1d; --ink-2:#3d4d51; --muted:#5f6f74; --line:#dbe2e3; --line-2:#eaeff0;
  --accent:#0090a8; --accent-ink:#00697f; --second:#cc7a10;
  --good:#0d7047; --warn:#7d5607; --crit:#b3241c;
  --mark:#f2e2a0; --mark-ink:#2b2410;
  --chart-a:#0090a8; --chart-b:#cc7a10;
  --shadow:0 1px 2px rgba(14,26,29,.05),0 8px 24px -12px rgba(14,26,29,.18);
  --serif:ui-serif,"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
  --sans:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
  --mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Consolas,monospace;
}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){
  --ground:#0a1113; --surface:#111a1d; --surface-2:#162125;
  --ink:#e7edee; --ink-2:#c0ced1; --muted:#8ba0a4; --line:#213034; --line-2:#1a262a;
  --accent:#2199af; --accent-ink:#4bb8cc; --second:#c1872b;
  --good:#26ac72; --warn:#c1872b; --crit:#e04f6a;
  --mark:#5c4a12; --mark-ink:#f6ebc0;
  --chart-a:#2199af; --chart-b:#c1872b;
  --shadow:0 1px 2px rgba(0,0,0,.4),0 12px 32px -14px rgba(0,0,0,.7);
}}
:root[data-theme="dark"]{
  --ground:#0a1113; --surface:#111a1d; --surface-2:#162125;
  --ink:#e7edee; --ink-2:#c0ced1; --muted:#8ba0a4; --line:#213034; --line-2:#1a262a;
  --accent:#2199af; --accent-ink:#4bb8cc; --second:#c1872b;
  --good:#26ac72; --warn:#c1872b; --crit:#e04f6a;
  --mark:#5c4a12; --mark-ink:#f6ebc0;
  --chart-a:#2199af; --chart-b:#c1872b;
  --shadow:0 1px 2px rgba(0,0,0,.4),0 12px 32px -14px rgba(0,0,0,.7);
}
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}*{animation:none!important;transition:none!important}}
body{margin:0;background:var(--ground);color:var(--ink);font-family:var(--sans);
  font-size:16px;line-height:1.6;-webkit-font-smoothing:antialiased}
a{color:inherit}
.ext{width:.62em;height:.62em;fill:none;stroke:currentColor;stroke-width:1.4;stroke-linecap:round;
  stroke-linejoin:round;opacity:.55;flex:none;vertical-align:baseline}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:2px}

.wrap{display:grid;grid-template-columns:210px minmax(0,1fr);gap:0;max-width:1360px;margin:0 auto}
/* Three things here are deliberate, all to stop the rail painting a ghost of
   itself when the page scrolls beneath it:
     - an opaque background: a transparent sticky element has nothing to paint
       over the outgoing frame with;
     - max-height rather than height:100vh with overflow-y:auto, because a sticky
       element that is also its own scroll container double-paints on some engines;
     - no transform/will-change: forcing a compositing layer on a sticky element
       makes that worse, not better. */
/* top and max-height both clear the site nav; sticking at 0 put the rail behind it
   and the last nav items were cut off at the bottom of the viewport. */
.rail{position:sticky;top:var(--navh,0px);align-self:start;
  max-height:calc(100vh - var(--navh,0px));padding:34px 20px 34px 26px;
  border-right:1px solid var(--line);display:flex;flex-direction:column;gap:26px;
  background:var(--ground)}
/* MOBILE AND TABLET. Stacked, the rail put a full screen of navigation above the
   headline - a phone visitor arriving from a link saw a wall of section names and no
   content. The sections move into a floating button that travels with the scroll, so
   the jump list is always one tap away and costs no vertical space.
   Selectors are prefixed with `body` because these sit ABOVE the base rules in this
   stylesheet, and a media query adds no specificity - the later base rule would win.

   RAILBP IS ONE NUMBER IN THREE PLACES and it is substituted rather than typed, because
   when it was typed the three drifted. The grid collapsed at 900px while this block and
   the floating button fired at 860, leaving a 40px band - a tablet, held upright - where
   the rail had stacked into a full-width wall of section names at the top and the button
   that replaces it had not appeared. Nothing tested that band, and nothing could: it is
   only wrong in the 40px between two rules that were each correct on their own side. */
@media (max-width:825px){
  body .rail{position:static;max-height:none;padding:12px 18px 10px;gap:8px;
    border-right:0;border-bottom:1px solid var(--line)}
  body .rail-id.home{font-size:9.5px;line-height:1.5}
  body .rail-id.foot{display:none}
  body .rail nav{display:none}          /* replaced by the floating jump list */
  body .asktop{margin:0 0 8px}
  body #askopen{width:100%;justify-content:center}
}
/* the floating jump list */
#jump{display:none}
@media (max-width:825px){
  /* bottom-left, and now the only floating control on the page. Anchored to the top it
     sat permanently over the heading you were reading; a fixed element has to live
     where content is not. */
  #jump{display:block;position:fixed;left:18px;bottom:22px;z-index:65}
  #jump>button{display:inline-flex;align-items:center;gap:7px;font-family:var(--mono);
    font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink);
    background:var(--surface);border:1px solid var(--line);border-radius:8px;
    padding:9px 13px;cursor:pointer;box-shadow:var(--shadow);min-height:40px}
  #jump>button svg{width:11px;height:11px;fill:none;stroke:currentColor;
    stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;transition:transform .15s}
  #jump>button svg{transform:rotate(180deg)}
  #jump[data-open="1"]>button svg{transform:rotate(0deg)}
  #jump[data-open="1"]>button{border-color:var(--accent);color:var(--accent-ink)}
  /* opens upward, so the list never runs off the bottom of the screen */
  #jumplist{position:absolute;bottom:calc(100% + 8px);left:0;min-width:236px;
    max-height:min(62vh,440px);overflow-y:auto;background:var(--surface);
    border:1px solid var(--line);border-radius:10px;box-shadow:var(--shadow);
    padding:6px;display:none}
  #jump[data-open="1"] #jumplist{display:block}
  #jumplist a{display:block;padding:11px 12px;font-size:14px;color:var(--ink-2);
    text-decoration:none;border-radius:6px}
  #jumplist a:active,#jumplist a:hover{background:var(--surface-2);color:var(--accent-ink)}
}
.rail-id{font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);line-height:1.7}
.rail-id b{display:block;color:var(--ink);font-size:12px;letter-spacing:.08em}
.rail nav{display:flex;flex-direction:column;gap:1px}
.rail nav a{display:flex;align-items:center;gap:9px;font-size:12.5px;color:var(--muted);
  text-decoration:none;padding:5px 0;transition:color .15s}
.rail nav a span{width:14px;height:1px;background:var(--line);transition:width .18s,background .18s}
.rail nav a:hover,.rail nav a.on{color:var(--ink)}
.rail nav a:hover span,.rail nav a.on span{width:22px;background:var(--accent)}
.rail nav a.off{color:var(--accent-ink)}
.main{padding:34px 40px 90px;min-width:0}

/* Every section header carries its own top rule, so the masthead's bottom border
   drew a second line 34px above the first one - two rules doing one job. The
   section rule is the system; the masthead defers to it. */
.masthead{padding-bottom:0;margin-bottom:34px}
.stamp{display:inline-flex;align-items:center;gap:7px;margin:22px 0 0;font-family:var(--mono);
  font-size:11px;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-2);
  border:1px solid color-mix(in srgb,var(--good) 38%,transparent);border-radius:20px;
  padding:4px 11px;background:color-mix(in srgb,var(--good) 7%,transparent)}
.stamp .dot{width:6px;height:6px;border-radius:50%;background:var(--good);flex:none}
/* Explicit three columns rather than auto-fit: auto-fit reflowed "newest by
   publication date" out of the right slot at intermediate widths. Below the
   breakpoint the columns stack in DOM order, which keeps the same reading. */
.chg{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:30px}
/* Two columns, never three: a third column made every row narrow enough to wrap a
   topic name over three lines. Below the breakpoint the date stacks above its row
   rather than holding a gutter, so two columns survive a narrow page; narrower
   still, one - where "newest by publication date" reads last instead of stranded
   at the bottom left, which is what a mid-width reflow did to it. */
@media (max-width:860px){
  .chg{gap:20px}
  body .chg-item{flex-direction:column;gap:3px}
  .chg-when{margin-top:0}
}
@media (max-width:620px){
  body .chg-tabs{display:flex;gap:18px;margin:0 0 4px;
    border-bottom:1px solid var(--line-2)}
  body .chg[data-tab="0"] > .chg-col:nth-child(2){display:none}
  body .chg[data-tab="1"] > .chg-col:nth-child(1){display:none}
  /* the tab label already says which list this is */
  body .chg > .chg-col > h3{display:none}
}
@media (max-width:620px){.chg{grid-template-columns:1fr}
  body .chg-item{flex-direction:row;gap:11px}}
.chg-col{display:flex;flex-direction:column;
  /* pan-y hands vertical scrolling to the browser and keeps horizontal for us, so the
     swipe handler needs no preventDefault and its listeners stay passive. Doing it the
     other way - intercepting touchmove - is what breaks scrolling on a phone. */
  touch-action:pan-y}
/* A 18px dot is a fine mouse target and a poor thumb one. On a coarse pointer the
   button grows to 34px while the dot inside stays 7px, so nothing looks different
   and the tap area roughly triples. Written after the base rule, not inside an
   earlier media block, because a media query adds no specificity and would lose. */
@media (pointer:coarse){
  body .pager{gap:6px}
  body .pager button{width:34px;height:34px}
}
/* --- Search the research ------------------------------------------------------
   Grounded on this index's own writing only. The 507KB index is fetched on first
   use, never at page load - most visitors never open it. */
.asktop{display:flex;justify-content:flex-end;margin:0 0 -10px}
#askopen{display:inline-flex;align-items:center;gap:7px;font-family:var(--mono);
  font-size:11px;letter-spacing:.07em;text-transform:uppercase;color:var(--muted);
  background:var(--surface);border:1px solid var(--line);border-radius:6px;
  padding:7px 12px;cursor:pointer}
#askopen:hover{color:var(--accent-ink);border-color:color-mix(in srgb,var(--accent) 45%,transparent)}
#askopen svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round}
.askwrap{position:fixed;inset:0;z-index:60;background:color-mix(in srgb,var(--ink) 58%,transparent);
  display:flex;align-items:flex-start;justify-content:center;padding:8vh 20px 20px}
.askwrap[hidden]{display:none}
/* --surface, not --bg: --bg is not a token in this stylesheet, so it resolved to
   transparent and the whole page showed through the dialog. Fourth time a missing or
   overridden selector has silently produced a see-through element here. */
.askbox{width:min(760px,100%);max-height:80vh;overflow:auto;background:var(--surface);
  border:1px solid var(--line);border-radius:10px;padding:18px 20px 20px;
  box-shadow:0 24px 60px color-mix(in srgb,var(--ink) 26%,transparent)}
.askbar{display:flex;gap:10px;align-items:center}
#askq{flex:1;font-family:inherit;font-size:17px;color:var(--ink);background:none;
  border:0;border-bottom:1px solid var(--line);padding:7px 2px;outline:none}
#askq:focus{border-bottom-color:var(--accent)}
#askclose{font-family:var(--mono);font-size:10px;letter-spacing:.06em;color:var(--muted);
  background:var(--surface);border:1px solid var(--line);border-radius:4px;
  padding:4px 8px;cursor:pointer}
.askhint{font-size:12px;line-height:1.6;color:var(--muted);margin:11px 0 0;max-width:62ch}
.askout{margin:16px 0 0}
.askhit{padding:13px 0;border-top:1px solid var(--line-2)}
.askhit h4{font-size:15px;line-height:1.4;margin:0 0 5px;font-weight:600}
.askhit h4 a{color:var(--ink);text-decoration:none;border-bottom:1px solid var(--line)}
.askhit h4 a:hover{border-bottom-color:var(--accent)}
.askwhere{font-family:var(--mono);font-size:10px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted);margin:0 0 6px}
.askline{font-size:13.5px;line-height:1.62;margin:6px 0 0}
.asksrc{display:block;font-size:11.5px;color:var(--muted);margin-top:3px}
.asksrc a{color:var(--muted)}
.askgrade{display:inline-block;font-family:var(--mono);font-size:9px;letter-spacing:.06em;
  border:1px solid var(--line);border-radius:3px;padding:0 4px;margin-right:5px}
.askgrade.A,.askgrade.B{border-color:color-mix(in srgb,var(--good) 50%,transparent);color:var(--good)}
.askgrade.C{border-color:color-mix(in srgb,var(--warn) 50%,transparent);color:var(--warn)}
.askcount{font-family:var(--mono);font-size:10px;letter-spacing:.06em;color:var(--muted)}
/* A dull yellow rather than a tint of the accent: at 22% the accent was almost
   invisible against the panel, and it competed with the accent used for links. */
.askout mark{background:var(--mark);color:var(--mark-ink);
  border-radius:2px;padding:0 2px;font-weight:600}
.asknone{font-size:14px;line-height:1.65;color:var(--ink);margin:14px 0 0}
.asknone b{font-weight:600}
.askstep{font-size:13px;line-height:1.6;margin:5px 0 0;padding-left:13px;
  border-left:2px solid var(--line-2)}
.askown{font-family:var(--mono);font-size:10px;color:var(--muted);letter-spacing:.05em}
.chg-none{font-size:13px;color:var(--muted);margin:10px 0 0}
.chg-item[hidden]{display:none}
/* margin-top:auto, not 11px: the column reserves the tallest page's height and the
   pager has to sit at the bottom of it, or a short last page pulls the dots upward
   and the two columns stop lining up. Set inside the shorthand - a separate
   margin-top rule at the same specificity loses to it. */
.pager{display:flex;align-items:center;gap:2px;margin:11px 0 0;margin-top:auto;
  padding-top:10px;border-top:1px solid var(--line-2)}
/* Dots, not numbers: with five rows a column the page number carries no meaning a
   reader needs - only how many pages there are and which one is showing. The button
   stays 18px for the hit target; the dot inside it is 7px. */
.pager button{width:18px;height:18px;padding:0;border:0;background:none;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;border-radius:50%}
.pager button::before{content:"";width:7px;height:7px;border-radius:50%;
  border:1px solid var(--muted);background:none;transition:background .12s,border-color .12s}
.pager button:hover::before{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 30%,transparent)}
.pager button[aria-current="true"]::before{border-color:var(--accent);background:var(--accent)}
/* On a phone the two lists stack, so the second one starts about a screen and a half
   down and most readers never reach it. Below 620px - the width at which .chg drops to
   a single column - they become two tabs over one area instead. Desktop keeps both
   side by side, which is the whole point of the two-column layout.
   The tab bar is built by JS from the headings it replaces, so it cannot describe a
   section that has been renamed. With no JS nothing is inserted and both lists render
   exactly as before, which is the degradation that costs the reader least. */
.chg-tabs{display:none}
.chg-tab{font-family:var(--mono);font-size:11px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--muted);background:none;border:0;
  border-bottom:2px solid transparent;padding:9px 2px;margin:0;cursor:pointer;
  flex:1 1 0;text-align:center;line-height:1.3}
.chg-tab[aria-selected="true"]{color:var(--ink);border-bottom-color:var(--accent)}
.pager button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.pager .of{font-family:var(--mono);font-size:10px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--muted);margin-left:auto}
.chg-col h3{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);font-weight:600;margin:0 0 12px;padding-bottom:9px;
  border-bottom:1px solid var(--line)}
.chg-item{display:flex;gap:11px;padding:10px 0;border-bottom:1px solid var(--line-2);font-size:13.5px;
  line-height:1.45;color:var(--ink-2)}
.chg-item:last-child{border-bottom:0}
.chg-when{font-family:var(--mono);font-size:10.5px;color:var(--muted);white-space:nowrap;
  padding-top:3px;min-width:62px;flex:none;letter-spacing:.03em}
.chg-body a{color:var(--ink);text-decoration:none;border-bottom:1px solid var(--line)}
.chg-body a:hover{border-bottom-color:var(--accent)}
.chg-kind{display:inline-block;font-family:var(--mono);font-size:9.5px;letter-spacing:.08em;
  text-transform:uppercase;padding:1px 6px;border-radius:3px;margin-right:7px;
  border:1px solid var(--line);color:var(--muted);vertical-align:1px}
.chg-kind.dissent{border-color:color-mix(in srgb,var(--second) 45%,transparent);color:var(--second)}
.chg-kind.issue{border-color:color-mix(in srgb,var(--accent) 40%,transparent);color:var(--accent-ink)}
.chg-kind.added{border-color:var(--line);color:var(--muted)}
.chg-src{display:block;font-size:11.5px;color:var(--muted);margin-top:2px}
.eyebrow{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--accent-ink)}
h1{font-family:var(--serif);font-weight:600;font-size:clamp(30px,4.1vw,46px);line-height:1.08;
  letter-spacing:-.018em;margin:12px 0 0;text-wrap:balance;max-width:19ch}
.dek{color:var(--ink-2);font-size:17px;max-width:62ch;margin:14px 0 0}
.provenance{display:flex;flex-wrap:wrap;gap:0;margin-top:22px;border:1px solid var(--line);
  border-radius:3px;overflow:hidden;background:var(--surface)}
/* Each figure links to where a reader can check it. The counts themselves all
   derive from one computed set in builddata.py - see pipeline/verify_counts.py,
   which fails the build if any of them drift apart. */
/* The cells are <a> now, so every rule below targets the cell generically rather
   than `div` - selecting by tag name silently dropped the padding, borders and
   flex sizing the moment the markup changed. */
.provenance > *{padding:10px 16px;border-right:1px solid var(--line);flex:1 1 auto;
  min-width:112px;text-decoration:none;color:inherit;display:block}
.provenance > *:last-child{border-right:0}
.provenance a:hover b{color:var(--accent-ink)}
.provenance a:hover span{color:var(--ink-2)}
.provenance b{display:block;font-family:var(--mono);font-size:19px;font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.provenance span{font-family:var(--mono);font-size:10px;letter-spacing:.11em;text-transform:uppercase;color:var(--muted)}

section.blk{margin:0 0 56px;scroll-margin-top:calc(20px + var(--navh,0px))}
.blk > header{margin-bottom:20px;border-top:1px solid var(--line);padding-top:16px}
.blk > header .eyebrow{display:block}
.blk h2{font-family:var(--serif);font-weight:600;font-size:26px;letter-spacing:-.012em;margin:7px 0 0}
.blk .lede{color:var(--muted);font-size:14.5px;max-width:70ch;margin:8px 0 0}

.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(228px,1fr));gap:12px}
.kpi{display:flex;flex-direction:column;gap:4px;padding:18px;background:var(--surface);
  border:1px solid var(--line);border-top:3px solid var(--accent);border-radius:3px;
  text-decoration:none;box-shadow:var(--shadow);transition:transform .16s,border-color .16s}
.kpi:hover{transform:translateY(-2px)}
.kpi.t-crit{border-top-color:var(--crit)} .kpi.t-warn{border-top-color:var(--warn)}
.kpi.t-good{border-top-color:var(--good)} .kpi.t-accent{border-top-color:var(--accent)}
.kpi-fig{font-family:var(--mono);font-size:33px;font-weight:600;letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;line-height:1}
.kpi-lab{font-family:var(--mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--muted)}
.kpi p{font-size:13.5px;color:var(--ink-2);margin:8px 0 0;line-height:1.52}
.kpi-src{display:flex;align-items:center;gap:5px;margin-top:auto;padding-top:12px;
  font-family:var(--mono);font-size:10.5px;color:var(--accent-ink)}

.libpage{max-width:1180px;margin:0 auto}
.libpage .blk h1{font-family:var(--serif);font-weight:600;font-size:26px;
  letter-spacing:-.012em;margin:7px 0 0}
.libback{margin:0 0 18px}
.libback a{font-family:var(--mono);font-size:11px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--accent-ink);text-decoration:none}
.libback a:hover{color:var(--accent)}
.libgo{margin:18px 0 0}
.libgo a{font-family:var(--mono);font-size:12.5px;letter-spacing:.03em;
  color:var(--accent-ink);text-decoration:none;border-bottom:1px solid var(--line)}
.libgo a:hover{color:var(--accent);border-bottom-color:var(--accent)}
.chartcard{background:var(--surface);border:1px solid var(--line);border-radius:3px;padding:20px 22px;box-shadow:var(--shadow)}
.chartcard h3{font-size:14px;margin:0;letter-spacing:-.005em}
.chartcard .sub{font-size:12.5px;color:var(--muted);margin:4px 0 16px}
.bars{display:flex;flex-direction:column;gap:9px}
.bar{display:grid;grid-template-columns:minmax(140px,215px) 1fr auto;align-items:center;gap:12px}
.bar-l{font-size:13px;color:var(--ink-2)}
.bar-track{display:block;height:15px;background:var(--line-2);border-radius:2px;position:relative;overflow:hidden}
.bar-fill{display:block;height:100%;background:var(--chart-a);border-radius:0 4px 4px 0;
  transition:width .7s cubic-bezier(.22,.8,.3,1)}
.bar-v{font-family:var(--mono);font-size:12.5px;font-variant-numeric:tabular-nums;color:var(--ink-2);min-width:76px;text-align:right}
.bar:hover .bar-l{color:var(--ink)}
/* The affordance is anchored to the right edge of the label column, not trailing
   the text: following the text put it at a different x on every row, and on the
   two labels that wrap it broke the alignment of the chart entirely. A chevron
   rather than a word, because the accent-coloured label already says it is a link
   and the column is only 215px wide. */
a.bar-l{text-decoration:none;color:var(--accent-ink);display:flex;align-items:center;
  justify-content:space-between;gap:10px}
a.bar-l:hover{color:var(--accent)}
a.bar-l .deeper{margin-left:auto;flex:none;width:17px;height:17px;border-radius:50%;
  border:1px solid color-mix(in srgb,var(--accent) 35%,transparent);display:flex;
  align-items:center;justify-content:center;opacity:.75;transition:opacity .15s,background .15s}
a.bar-l .deeper svg{width:7px;height:7px;fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
a.bar-l:hover .deeper{opacity:1;background:color-mix(in srgb,var(--accent) 14%,transparent)}
.bar-deep .bar-fill{background:linear-gradient(90deg,var(--chart-a),color-mix(in srgb,var(--chart-a) 72%,var(--chart-b)))}
.legend{display:flex;gap:16px;flex-wrap:wrap;margin:0 0 14px;font-family:var(--mono);
  font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--muted)}
.legend i{display:inline-block;width:10px;height:10px;border-radius:2px;margin-right:6px;vertical-align:-1px}
.stack{display:flex;flex-direction:column;gap:16px}
.stackrow{display:grid;grid-template-columns:96px 1fr;gap:14px;align-items:center}
.stackrow .yr{font-family:var(--mono);font-size:12px;color:var(--muted);letter-spacing:.06em}
.stackbar{display:flex;height:34px;border-radius:2px;overflow:hidden;gap:2px}
/* A DARK label on the fills rather than white. White was 3.10-3.78 on all four
   segment colours across both themes; near-black is 5.04-6.14 on the same fills, so
   the chart keeps its palette and only the text changes. Fixed rather than tokenised
   because the fills are bright in dark mode and dark in light mode - the label has to
   stay dark in both. */
.seg{display:flex;align-items:center;padding:0 10px;font-family:var(--mono);font-size:11.5px;
  color:#0a1113;white-space:nowrap;transition:flex-basis .7s cubic-bezier(.22,.8,.3,1)}
.seg-a{background:var(--chart-a)} .seg-b{background:var(--chart-b);justify-content:flex-end}

.two{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:12px}
.ev{background:var(--surface);border:1px solid var(--line);border-radius:3px;padding:17px 18px;
  display:flex;flex-direction:column;gap:8px}
.ev header{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin:0;border:0;padding:0}
.ev h3{font-size:15px;margin:0;letter-spacing:-.006em;line-height:1.32}
.ev-n{font-family:var(--mono);font-size:17px;font-weight:600;font-variant-numeric:tabular-nums;
  color:var(--good);white-space:nowrap}
.ev-notworking{border-left:3px solid var(--second)}
.ev-working{border-left:3px solid var(--good)}
.ev p{margin:0;font-size:13.5px;color:var(--ink-2);line-height:1.55}
.ev-src{display:inline-flex;align-items:center;gap:5px;font-family:var(--mono);font-size:10.5px;
  color:var(--accent-ink);text-decoration:none;margin-top:auto;padding-top:4px}
.ev-src:hover{text-decoration:underline}

.debts{display:grid;grid-template-columns:repeat(auto-fit,minmax(264px,1fr));gap:12px;margin-bottom:12px}
.debt{background:var(--surface);border:1px solid var(--line);border-radius:3px;padding:16px 17px}
.debt-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:11px}
.debt h3{font-size:14.5px;margin:0}
.debt-lab{font-family:var(--mono);font-size:10.5px;color:var(--muted);text-align:right}
.debt-track{height:6px;background:var(--line-2);border-radius:3px;overflow:hidden}
.debt-fill{height:100%;width:var(--v,0);border-radius:3px;transition:width .8s cubic-bezier(.22,.8,.3,1)}
.debt.s-crit .debt-fill{background:var(--crit)} .debt.s-warn .debt-fill{background:var(--warn)}
.debt p{font-size:13px;color:var(--ink-2);margin:11px 0 0;line-height:1.5}
.fails{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px}
.fail{background:var(--surface-2);border:1px solid var(--line);border-left:3px solid var(--crit);
  border-radius:3px;padding:16px 17px;display:flex;flex-direction:column;gap:8px}
.fail h3{font-size:14.5px;margin:0;line-height:1.34}
.fail p{margin:0;font-size:13.5px;color:var(--ink-2);line-height:1.55}

.focuslist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;
  border:1px solid var(--line);border-radius:3px;overflow:hidden;background:var(--surface)}
.focus{display:grid;grid-template-columns:52px 1fr auto;gap:16px;padding:16px 18px;
  border-bottom:1px solid var(--line-2);align-items:start}
.focus:last-child{border-bottom:0}
.focus-rank{font-family:var(--mono);font-size:12px;color:var(--accent);padding-top:3px;font-variant-numeric:tabular-nums}
.owner{font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
.focus h3{font-size:15.5px;margin:3px 0 5px;letter-spacing:-.008em}
.focus p{margin:0;font-size:13.5px;color:var(--ink-2);line-height:1.55;max-width:74ch}
.focus-src{font-family:var(--mono);font-size:10.5px;color:var(--muted);white-space:nowrap;padding-top:3px}

.pbs{display:grid;grid-template-columns:repeat(auto-fit,minmax(330px,1fr));gap:14px}
.pb{background:var(--surface);border:1px solid var(--line);border-radius:3px;overflow:hidden;
  display:flex;flex-direction:column;box-shadow:var(--shadow)}
.pb-img{display:block;background:var(--surface-2);border-bottom:1px solid var(--line);line-height:0}
.pb-img{height:96px}
.pb-img .mark{display:block;height:96px;width:100%}
.noimg{height:6px;background:var(--accent)}
.pb-body{padding:15px 17px 17px;display:flex;flex-direction:column;gap:7px;flex:1}
.pb-pub{font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--accent-ink)}
.pb-pub em{font-style:normal;color:var(--muted);display:block;margin-top:3px;letter-spacing:.08em}
.pb h3{font-size:16px;margin:0;line-height:1.3;letter-spacing:-.01em}
.pb h3 a{text-decoration:none;display:inline-flex;align-items:baseline;gap:5px}
.pb h3 a:hover{color:var(--accent-ink)}
.pb-brief{font-size:13.5px;color:var(--ink-2);margin:0;line-height:1.5}
.pb-pts{margin:2px 0 0;padding:0 0 0 15px;font-size:12.5px;color:var(--muted);line-height:1.5}
.pb-pts li{margin-bottom:3px}
/* --- Ask an AI about this document ------------------------------------------
   Hands the reader's question and the PUBLISHER'S OWN link to an assistant of
   their choosing, in their own tab, under their own account. Nothing from the
   private corpus is sent anywhere - the assistant fetches the publisher's page
   itself, exactly as the reader could by clicking the link. */
/* margin-top:auto pins this to the bottom of the card so the control sits on one
   line across a row whatever the summary length. Inside the shorthand - a separate
   margin-top rule at equal specificity loses to it, as .pager already proved. */
.askdoc{margin:10px 0 0;margin-top:auto;padding-top:9px;
  border-top:1px solid var(--line-2)}
.askdoc-open{display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);
  font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);
  background:none;border:0;padding:2px 0;cursor:pointer}
.askdoc-open:hover{color:var(--accent-ink)}
.askdoc-open svg{width:11px;height:11px;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round}
.askdoc-panel{margin:9px 0 0}
.askdoc-panel[hidden]{display:none}
.askdoc-panel input{width:100%;font-family:inherit;font-size:12px;color:var(--ink);
  background:var(--surface);border:1px solid var(--line);border-radius:5px;
  padding:6px 8px;outline:none}
.askdoc-panel input:focus{border-color:var(--accent)}
/* iOS Safari zooms the whole page when a focused input is under 16px, and it never
   zooms back out - not on blur, not on escape. The fix is to remove the trigger, not
   to disable pinch-zoom with maximum-scale, which would take zooming away from
   everyone who needs it. Touch only, so the desktop panel keeps its 12px.
   Placed AFTER the base rule and prefixed with `body`: a media query adds no
   specificity, which is the fault verify_css.py now fails the build on. */
@media (pointer:coarse){body .askdoc-panel input{font-size:16px}}
.askdoc-go{display:flex;flex-wrap:wrap;gap:5px;margin:6px 0 0;align-items:center}
.askdoc-go button{font-family:var(--mono);font-size:9px;letter-spacing:.05em;
  border:1px solid var(--line);background:var(--surface);color:var(--muted);
  border-radius:4px;padding:2px 7px;cursor:pointer}
.askdoc-go button:hover{color:var(--accent-ink);
  border-color:color-mix(in srgb,var(--accent) 45%,transparent)}
/* .askdoc-panel p.askdoc-note: `.art p` sets 12.5px on every paragraph in a library
   card and sits later in the stylesheet, so it beat both `.askdoc-note` and
   `p.askdoc-note` on equal specificity. Two classes settles it. */
.askdoc-panel p.askdoc-note{font-size:10.5px;color:var(--muted);margin:6px 0 0;
  line-height:1.45}
/* The compact variant used through the library: an icon at the bottom right that
   opens the same panel underneath. Icon-only because a row of 180 entries cannot
   carry a sentence of label each. */
.askdoc.mini{margin:0;padding:0;border-top:0;display:flex;justify-content:flex-end}
/* Pin the whole meta row, not the icon. Pinning the icon alone left it stranded at
   the bottom of a body stretched to match a tall cover image, with a long gap above. */
/* align-items:flex-end, not center: the meta text wraps to two lines on some cards,
   and a centred icon then sits at a different height from its neighbours even though
   the rows themselves line up. Bottom-aligning puts every icon on the last meta line. */
.art-foot{display:flex;align-items:flex-end;justify-content:space-between;gap:10px;
  margin-top:auto;padding-top:8px}
.art-foot .art-meta{margin:0}
.art-foot .askdoc.mini{flex:0 0 auto}
.askdoc.mini .askdoc-open{padding:3px;border-radius:4px;color:var(--muted);opacity:.55}
@media (pointer:coarse){
  .askdoc.mini .askdoc-open{min-width:44px;min-height:44px;padding:0;opacity:.8}
  .pager button{width:34px;height:34px}
  .rail nav a{padding-top:6px;padding-bottom:6px}
}
.askdoc.mini .askdoc-open svg{width:12px;height:12px}
.askdoc.mini .askdoc-open:hover{opacity:1;color:var(--accent-ink)}
.askdoc.mini .askdoc-open[aria-expanded="true"]{opacity:1;color:var(--accent-ink)}
/* the panel spans the full width of the entry, below the icon */
/* The panel is one size everywhere. It used to render larger on a featured card than
   in the library purely because of which rule won; the sizes now live on the base. */
.askdoc-panel.compact{width:100%;margin-top:7px}
.askdoc.mini{flex-wrap:wrap}
/* restlist rows are a grid of link + meta; the control takes its own cell at the end */
.restlist li{position:relative}
.askdoc.mini.row{grid-column:2;grid-row:1 / span 3;align-self:start;padding-top:1px}
.askdoc.mini.row .askdoc-panel{margin-bottom:6px}
.pb-why{margin:auto 0 0;padding-top:11px;border-top:1px solid var(--line-2);font-size:12.5px;color:var(--ink-2);line-height:1.5}
.pb-why span{display:block;font-family:var(--mono);font-size:9.5px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--muted);margin-bottom:4px}

.libsec{margin-bottom:30px}
.libhead{display:flex;align-items:baseline;gap:12px;border-bottom:1px solid var(--line);
  padding-bottom:8px;margin-bottom:14px}
/* h2, not h3: these sit directly under the page h1, so h3 skipped a level.
   letter-spacing is stated because .libsec lives INSIDE a .blk and `.blk h2`
   sets -.012em at EQUAL specificity - this rule only wins by being later, and
   only on the properties it names. */
.libhead h2{font-family:var(--serif);font-size:19px;margin:0;font-weight:600;
  letter-spacing:normal}
.cnt{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
.arts{display:grid;grid-template-columns:repeat(auto-fit,minmax(315px,1fr));gap:12px}
.art{display:grid;grid-template-columns:78px 1fr;gap:13px;background:var(--surface);
  border:1px solid var(--line);border-radius:3px;padding:12px}
/* align-self:start stops the cover stretching. As a grid item it filled the whole card
   height, so opening the ask panel made the chart grow with it. */
.art-img{display:block;line-height:0;border:1px solid var(--line-2);border-radius:2px;overflow:hidden;
  background:var(--surface-2);min-height:96px;display:flex;align-items:center;justify-content:center;
  text-align:center;align-self:start}
.art-img .mark{display:block;height:94px;width:100%}
.chip-pub{font-family:var(--mono);font-size:9px;letter-spacing:.06em;color:var(--muted);padding:6px;line-height:1.35}
.art-pub{font-family:var(--mono);font-size:9.5px;letter-spacing:.11em;text-transform:uppercase;color:var(--accent-ink)}
.star{color:var(--second);margin-left:6px;font-size:9px}
.art h3{font-size:13.5px;margin:4px 0 5px;line-height:1.35;font-weight:600}
.art h3 a{text-decoration:none}
.art h3 a:hover{color:var(--accent-ink)}
.art p{font-size:12.5px;color:var(--muted);margin:0 0 6px;line-height:1.48}
/* The meta line is a footer: pushed to the bottom of the text column with
   margin-top:auto so it sits on one line across a row, instead of floating at a
   different height on every card depending on how long the summary ran. */
.art-body{display:flex;flex-direction:column;min-width:0}
.art-body p{flex:0 1 auto}
.art-meta{font-family:var(--mono);font-size:9.5px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);margin-top:auto;padding-top:9px;border-top:1px solid var(--line-2);
  display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.pdf{display:inline-block;margin-left:6px;padding:1px 4px;border:1px solid var(--line);border-radius:2px;
  font-family:var(--mono);font-size:8.5px;font-weight:500;letter-spacing:.08em;color:var(--muted)}
.more{margin-top:12px}
.more summary{font-family:var(--mono);font-size:11px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted);cursor:pointer;padding:7px 0}
.more summary:hover{color:var(--ink)}
.restlist{list-style:none;margin:6px 0 0;padding:0;columns:2;column-gap:26px}
@media (max-width:620px){.restlist{columns:1}}
/* A GRID, not a wrapping flex row. As a flex row with flex-wrap, a long title pushed
   the meta span and the ask button onto lines of their own - so some rows read
   "title / publisher-date" over two lines and others sat on one, and a row whose
   button had wrapped looked like an entry with no title at all. Every row now has the
   same shape: title, meta beneath it, button to the right spanning both. */
.restlist li{break-inside:avoid;font-size:12.5px;padding:5px 0;border-bottom:1px solid var(--line-2);
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:1px 10px;align-items:start}
.restlist li > a{grid-column:1;grid-row:1;min-width:0}
/* The title carries the row - weight AND ink colour, because at 12.5px weight alone
   does not separate it from the mono meta line beneath. */
.restlist li > a{font-weight:600;color:var(--ink)}
.restlist a{text-decoration:none;color:var(--ink-2)}
.restlist a:hover{color:var(--accent-ink);text-decoration:underline}
.restlist .restwhy{grid-column:1;grid-row:3;margin:2px 0 0;font-size:12px;
  line-height:1.5;color:var(--ink-2);max-width:62ch}
.restlist span{grid-column:1;grid-row:2;font-family:var(--mono);font-size:9.5px;
  color:var(--muted);white-space:nowrap;padding-top:1px}

a.rail-id.home{text-decoration:none;color:inherit;display:block}
a.rail-id.home:hover b{color:var(--accent-ink)}
footer.foot{border-top:1px solid var(--line);padding-top:20px;margin-top:50px;
  font-size:12.5px;color:var(--muted);max-width:80ch}
footer.foot a{color:var(--accent-ink)}
.note{font-family:var(--mono);font-size:10.5px;letter-spacing:.05em;line-height:1.7}

@media (max-width:825px){
  .wrap{grid-template-columns:1fr}
  /* The .rail and .rail nav rules that used to sit here are gone. They restated what the
     RAILBP block above already says, with different padding, and were outranked by it -
     `body .rail` beats `.rail`. Two rules for one element at one breakpoint, one of them
     dead and both looking live. */
  .main{padding:24px 22px 70px}
  .restlist{columns:1}
  .focus{grid-template-columns:34px 1fr;gap:12px}
  .focus-src{grid-column:2;padding-top:6px}
  .bar{grid-template-columns:1fr;gap:4px}
  .bar-v{text-align:left;min-width:0}
}
