:root{
  --bg:#080c0a; --bg2:#0b1210; --panel:#0f1714; --panel2:#13201a;
  --line:#16281f; --line2:#20402e;
  --fg:#e8f2ec; --fg2:#a3b8ad; --fg3:#6d8579;
  --acc:#01CE87; --acc2:#00A607; --warn:#f7b955; --bad:#f77070; --good:#01CE87;
  --chip:#080f0b; --chip2:#070d09; --rowhov:#12201a;
  --consbd:#0a5c3e; --consbg:#04170f; --onacc:#06231f;
  --notebg:#0b120e; --warnbg:#1a1408; --warnbd:#4a3a12; --warnfg:#e8d9b0; --warncode:#ffd88a;
  --shadow:none;
  /* Text column caps. Measured in rem, NOT ch: ch scales with font-size, so one
     "68ch" rule gives a 15px paragraph and an 11px caption two different pixel
     widths, and near-alignment reads as a mistake. 44rem = 704px ~= 75 characters
     at 15px, inside the 65-80 band. Was `none`, which let the lede run to 117
     characters a line at 1440px. */
  --measure:44rem;
  --measure-head:33rem;   /* display sizes want a far shorter line than prose */
  --mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,Helvetica,Arial,sans-serif;
}
/* ---------- light theme ---------- */
html[data-theme="light"]{
  --bg:#f6faf7; --bg2:#ffffff; --panel:#ffffff; --panel2:#eef5f0;
  --line:#dde8e1; --line2:#bed3c6;
  --fg:#0d1c15; --fg2:#4a5f54; --fg3:#5f7768;
  --acc:#00713C; --acc2:#00A607; --warn:#9a6206; --bad:#c0392b; --good:#00A607;
  --chip:#f0f5f2; --chip2:#f0f5f2; --rowhov:#f2f8f4;
  --consbd:#a7d8c0; --consbg:#eaf7f0; --onacc:#ffffff;
  --notebg:#fdf7e9; --warnbg:#fdf6e6; --warnbd:#e6d3a4; --warnfg:#5c4508; --warncode:#7a5c05;
  --shadow:0 1px 2px rgba(13,28,21,.05);
}
html[data-theme="light"] .dot.fresh{box-shadow:0 0 6px #00A60755}
html[data-theme="light"] .wordmark::after{background:linear-gradient(90deg,#00713C,#00A607)}
html[data-theme="light"] h1 em{background:linear-gradient(90deg,#00A607,#00713C);-webkit-background-clip:text;background-clip:text}
html[data-theme="light"] .logo svg circle:first-child{stroke:#00713C}

/* ---------- theme toggle ---------- */
.themebtn{display:inline-flex;align-items:center;gap:6px}
.themebtn svg{width:14px;height:14px;flex:none}
html:not([data-theme="light"]) .themebtn .i-sun{display:none}
html[data-theme="light"] .themebtn .i-moon{display:none}

*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{background:var(--bg);color:var(--fg);font-family:var(--sans);font-size:15px;line-height:1.6;-webkit-font-smoothing:antialiased}
a{color:var(--acc);text-decoration:none}
a:hover{text-decoration:underline}

/* ---------- layout ---------- */
.wrap{display:grid;grid-template-columns:264px minmax(0,1fr);min-height:100vh}
aside{border-right:1px solid var(--line);background:var(--bg2);position:sticky;top:0;height:100vh;overflow-y:auto;scrollbar-gutter:stable;padding:22px 18px 40px}
main{min-width:0;padding:0 clamp(34px,3vw,72px) 90px}

/* ---------- sidebar ---------- */
.logo{display:flex;align-items:center;gap:11px;margin-bottom:12px}
.logo svg{width:38px;height:38px;flex:none}
.wordmark{font-size:22px;font-weight:800;letter-spacing:-.5px;line-height:1;color:var(--fg)}
.wordmark::after{content:"";display:block;width:26px;height:2px;background:linear-gradient(90deg,#00A607,#01CE87);margin-top:5px;border-radius:2px}
.tagline{font-size:9.5px;text-transform:uppercase;letter-spacing:.6px;color:var(--fg3);line-height:1.45;margin-top:6px;font-weight:600}
.brandsub{font-size:11.5px;color:var(--fg3);margin-bottom:22px;line-height:1.5}
.brandsub b{color:var(--acc);font-weight:700}
.eyebrow{font-size:10.5px;text-transform:uppercase;letter-spacing:2px;font-weight:700;color:var(--acc);margin-bottom:12px}
h1 em{background:linear-gradient(90deg,#01CE87,#00A607);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
.navlbl{font-size:10px;text-transform:uppercase;letter-spacing:1.3px;color:var(--fg3);margin:20px 0 8px;font-weight:700}
.navlink{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:6px 9px;border-radius:6px;font-size:12.8px;color:var(--fg2);cursor:pointer;border:1px solid transparent}
.navlink:hover{background:var(--panel);color:var(--fg)}
.navlink.on{background:var(--panel2);color:var(--fg);border-color:var(--line2)}
.navlink .n{font-family:var(--mono);font-size:10.5px;color:var(--fg3);background:var(--chip2);border:1px solid var(--line);border-radius:20px;padding:0 6px;flex:none}

/* Mobile category disclosure. Hidden on desktop, where the rail is always open. */
.navtoggle{display:none;width:100%;align-items:center;justify-content:space-between;gap:10px;
  background:var(--panel);border:1px solid var(--line2);color:var(--fg);border-radius:8px;
  padding:11px 13px;font:inherit;font-size:13.5px;font-weight:600;cursor:pointer;margin-bottom:4px}
.navtoggle svg{width:16px;height:16px;flex:none;transition:transform .18s}
.navtoggle[aria-expanded="true"] svg{transform:rotate(180deg)}
.navtoggle:focus-visible{outline:2px solid var(--acc);outline-offset:2px}

/* ---------- header ---------- */
header{padding:44px 0 22px;border-bottom:1px solid var(--line);margin-bottom:26px}
h1{font-size:clamp(32px,3.2vw,52px);line-height:1.05;letter-spacing:-1.2px;margin:0 0 16px;font-weight:800;max-width:var(--measure-head)}
h1 em{font-style:normal}
.lede{color:var(--fg2);font-size:clamp(15px,1.05vw,17px);max-width:var(--measure);margin:0 0 18px}
.stats{display:flex;gap:26px;flex-wrap:wrap;font-family:var(--mono);font-size:11.5px;color:var(--fg3)}
.stats b{display:block;font-family:var(--sans);font-size:21px;color:var(--fg);font-weight:700;line-height:1.2;font-variant-numeric:tabular-nums}

/* ---------- controls ---------- */
.controls{position:sticky;top:0;z-index:30;background:var(--bg);padding:14px 0 12px;border-bottom:1px solid var(--line);margin-bottom:24px;display:flex;gap:10px;flex-wrap:wrap;align-items:center}
input[type=search],select{background:var(--panel);border:1px solid var(--line2);color:var(--fg);border-radius:7px;padding:8px 11px;font-size:13px;font-family:var(--sans);outline:none}
input[type=search]{flex:1;min-width:220px}
input[type=search]:focus,select:focus{border-color:var(--acc)}
.btn{background:var(--panel);border:1px solid var(--line2);color:var(--fg2);border-radius:7px;padding:8px 13px;font-size:12.5px;cursor:pointer;font-family:var(--sans);white-space:nowrap}
.btn:hover{border-color:var(--acc);color:var(--fg)}
.btn.on{background:var(--acc);border-color:var(--acc);color:var(--onacc);font-weight:600}
.seg{display:flex;border:1px solid var(--line2);border-radius:7px;overflow:hidden}
.seg .btn{border:0;border-radius:0}

/* ---------- category block ---------- */
.cat{margin:0 0 46px;scroll-margin-top:76px}
.cathead{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:6px}
.cathead h2{font-size:19px;margin:0;letter-spacing:-.4px;font-weight:700}
.cathead .n{font-family:var(--mono);font-size:11px;color:var(--acc);border:1px solid var(--line2);border-radius:20px;padding:1px 9px}
.catblurb{color:var(--fg2);font-size:13.5px;max-width:var(--measure);margin:0 0 18px;padding-left:12px;border-left:2px solid var(--line2)}

/* ---------- cards ---------- */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(clamp(340px,23vw,440px),1fr));gap:16px;
  /* Grid rows stretch every card to the tallest in the row by default, and .cfoot is
     pushed down by margin-top:auto -- so one long caveat opened up to 383px of dead
     space inside its neighbours (41 of 135 cards had >80px). Cards now take their
     natural height; a ragged bottom edge reads as content-driven, a void reads as
     broken. */
  align-items:start}
.card{background:var(--panel);border:1px solid var(--line);border-radius:11px;padding:16px 17px 14px;display:flex;flex-direction:column;gap:9px;transition:border-color .15s,box-shadow .15s;box-shadow:var(--shadow)}
.card:hover{border-color:var(--line2)}
.ctop{display:flex;justify-content:space-between;align-items:flex-start;gap:10px}
.cname{font-size:15px;font-weight:700;letter-spacing:-.25px;line-height:1.3;margin:0}
.cauth{font-size:11.5px;color:var(--fg3);margin-top:3px}
.cdesc{font-size:13px;color:var(--fg2);margin:0}
/* Descriptions run 133-910 characters. Clamp the long ones so a card's height is
   driven by its content rather than by its longest neighbour, with an explicit
   expander. The CAVEAT is never clamped: honesty-critical text must not be
   truncated, which is why .cnote has no clamp rule. */

.cnote{font-size:12.2px;color:var(--fg2);background:var(--notebg);border:1px solid var(--line);border-left:2px solid var(--warn);border-radius:0 6px 6px 0;padding:8px 11px;margin:0}
.cnote b{color:var(--warn);font-weight:600;font-size:9.5px;letter-spacing:1px;text-transform:uppercase;display:block;margin-bottom:2px}
.tags{display:flex;gap:5px;flex-wrap:wrap}
.tag{font-family:var(--mono);font-size:10px;color:var(--fg3);background:var(--chip);border:1px solid var(--line);border-radius:4px;padding:2px 6px}
.tag.cons{color:var(--acc);border-color:var(--consbd);background:var(--consbg);cursor:help}
.newpill{font-family:var(--mono);font-size:9px;font-weight:700;letter-spacing:.9px;color:var(--onacc);background:var(--acc);border-radius:4px;padding:2px 6px;flex:none;text-transform:uppercase}
.addedon{font-family:var(--mono);font-size:10px;color:var(--fg3)}
.cfoot{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:auto;padding-top:10px;border-top:1px solid var(--line)}
.cfoot a,.cfoot button{font-size:11.5px;font-family:var(--mono);color:var(--fg2);background:var(--chip);border:1px solid var(--line);border-radius:5px;padding:4px 9px;cursor:pointer}
.cfoot a:hover,.cfoot button:hover{border-color:var(--acc);color:var(--acc);text-decoration:none}
.upd{margin-left:auto;font-family:var(--mono);font-size:10.5px;color:var(--fg3);display:flex;align-items:center;gap:6px}
/* Freshness marks.

   These five states were previously encoded by 6px of HUE ALONE. Measured in OKLab,
   all ten pairs failed this project's small-mark standard (dE >= 24 AND a lightness
   gap >= 16); the worst, "updated < 3 months" against "repo not found", came to
   dE 4.4 under deuteranopia -- the two most opposite meanings on the site rendered
   as one colour. The adjacent text always said which was which, so nothing was
   unreadable, but the dot exists to let you SCAN a grid of 135 cards without
   reading 135 dates, and that is the part that failed.

   SHAPE now carries the state; colour is redundant reinforcement:
     fresh  <3mo     filled disc
     ok     <1y      hollow ring
     stale  >1y      triangle
     dead   404      x
     none   no repo  dash
     wait   checking dotted ring
   Raised 6px -> 9px: a shape needs more pixels than a blob to read. */
.dot{width:9px;height:2.5px;margin-top:1px;flex:none;position:relative;
  background:var(--fg3);border-radius:2px}                       /* dash = no repository */
.dot.fresh{background:var(--good);border-radius:50%;height:9px;margin-top:0}
.dot.ok{background:none;border:2px solid var(--acc2);border-radius:50%;height:9px;margin-top:0;
  box-sizing:border-box}
.dot.stale{background:var(--warn);height:9px;margin-top:0;border-radius:0;
  clip-path:polygon(50% 0,100% 100%,0 100%)}
.dot.dead{background:none;height:9px;margin-top:0;border-radius:0}
.dot.dead::before,.dot.dead::after{content:"";position:absolute;left:50%;top:0;
  width:2px;height:100%;background:var(--bad);border-radius:1px;margin-left:-1px}
.dot.dead::before{transform:rotate(45deg)}
.dot.dead::after{transform:rotate(-45deg)}
.dot.wait{background:none;border:1.5px dotted var(--fg3);border-radius:50%;height:9px;
  margin-top:0;box-sizing:border-box}

/* ---------- table ---------- */
/* Capped-height scroll region: the table scrolls inside this box (both axes), so the
   horizontal scrollbar sits at the box's bottom edge — on-screen wherever you are —
   instead of at the very bottom of a ~130-row page. The sticky <th> headers (below)
   then stay pinned while scrolling. */
.tablewrap{overflow:auto;max-height:calc(100vh - 96px);border:1px solid var(--line);border-radius:10px;background:var(--panel)}
table{border-collapse:collapse;width:100%;font-size:12.6px;min-width:1080px}
th{text-align:left;padding:10px 13px;background:var(--panel2);color:var(--fg3);font-size:10px;text-transform:uppercase;letter-spacing:1.1px;border-bottom:1px solid var(--line2);position:sticky;top:0;white-space:nowrap;cursor:pointer;user-select:none}
th:hover{color:var(--acc)}
td{padding:10px 13px;border-bottom:1px solid var(--line);vertical-align:top;color:var(--fg2)}
tr:last-child td{border-bottom:0}
tr:hover td{background:var(--rowhov)}
td.nm{color:var(--fg);font-weight:600;white-space:nowrap}
td.rp{font-family:var(--mono);font-size:11px}
td.dt{font-family:var(--mono);font-size:11px;white-space:nowrap}
.mini{font-size:11px;color:var(--fg3)}

/* ---------- close button (used in the discussion sheet head) ---------- */
.dclose{background:none;border:1px solid var(--line2);color:var(--fg2);width:30px;height:30px;border-radius:7px;cursor:pointer;font-size:16px;line-height:1;flex:none}
.dclose:hover{border-color:var(--bad);color:var(--bad)}

/* ---------- join modal ---------- */
.modal{position:fixed;inset:0;z-index:90;display:flex;align-items:center;justify-content:center;padding:20px;
  background:#000a;backdrop-filter:blur(3px);opacity:0;pointer-events:none;transition:opacity .18s}
html[data-theme="light"] .modal{background:#0d1c1566}
.modal.on{opacity:1;pointer-events:auto}
.mbox{background:var(--bg2);border:1px solid var(--line2);border-radius:13px;width:min(520px,100%);
  max-height:90vh;overflow-y:auto;padding:26px 28px 24px;transform:translateY(8px) scale(.99);transition:transform .18s}
.modal.on .mbox{transform:none}
.mbox h3{margin:0 0 6px;font-size:19px;letter-spacing:-.35px}
.mbox .sub{margin:0 0 20px;font-size:13px;color:var(--fg2)}
.field{margin-bottom:14px}
.field label{display:block;font-size:12px;font-weight:600;color:var(--fg2);margin-bottom:5px}
.field label .req{color:var(--acc)}
.field input{width:100%;background:var(--panel);border:1px solid var(--line2);color:var(--fg);
  border-radius:7px;padding:9px 11px;font-size:13.5px;font-family:var(--sans);outline:none}
.field input:focus{border-color:var(--acc);box-shadow:0 0 0 3px color-mix(in srgb,var(--acc) 18%,transparent)}
.field input[aria-invalid="true"]{border-color:var(--bad)}
.field .err{font-size:11.5px;color:var(--bad);margin-top:4px;display:none}
.field .err.on{display:block}
.field .hint{font-size:11.5px;color:var(--fg3);margin-top:4px}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.consent{display:flex;gap:9px;align-items:flex-start;margin:16px 0 6px;font-size:12px;color:var(--fg2);line-height:1.5}
.consent input{margin-top:2px;flex:none;width:15px;height:15px;accent-color:var(--acc)}
.mactions{display:flex;gap:10px;align-items:center;margin-top:18px;padding-top:16px;border-top:1px solid var(--line)}
.bprim{background:var(--acc);border:1px solid var(--acc);color:var(--onacc);font-weight:600;border-radius:7px;
  padding:10px 18px;font-size:13.5px;cursor:pointer;font-family:var(--sans)}
.bprim:hover{filter:brightness(1.08)}
.bprim:disabled{opacity:.55;cursor:default}
.privacy{font-size:11px;color:var(--fg3);margin-top:14px;line-height:1.55}
.done{text-align:center;padding:26px 6px 10px}
.done .tick{width:44px;height:44px;border-radius:50%;background:var(--consbg);border:1px solid var(--consbd);
  color:var(--acc);display:flex;align-items:center;justify-content:center;margin:0 auto 14px;font-size:22px}
.done h4{margin:0 0 8px;font-size:16px}
.done p{margin:0 0 8px;font-size:13px;color:var(--fg2)}
.joinbtn{background:var(--acc);border:1px solid var(--acc);color:var(--onacc);font-weight:600;
  border-radius:8px;padding:10px 18px;font-size:13.5px;cursor:pointer;font-family:var(--sans);white-space:nowrap}
.joinbtn:hover{filter:brightness(1.08)}
.navjoin{background:var(--consbg);border:1px solid var(--consbd);color:var(--acc);font-weight:600}
.navjoin:hover{background:var(--consbg);border-color:var(--acc)}
.headcta{display:flex;gap:14px;align-items:center;flex-wrap:wrap;margin:22px 0 4px}
.headcta .note{font-size:12px;color:var(--fg3);max-width:var(--measure)}

/* ---------- misc ---------- */
.banner{background:var(--panel);border:1px solid var(--line2);border-left:3px solid var(--acc);border-radius:0 9px 9px 0;padding:14px 18px;margin:0 0 26px;font-size:13px;color:var(--fg2)}
.banner b{color:var(--fg)}
/* The provenance line is written by renderVerifyNote() and its text changes length
   as tiers resolve ("Checking…" -> "verified for 82 of 82 repositories. Read
   today."). Reserve the row so a text swap cannot shift the controls below it. */
.banner .mini{display:block;margin-top:6px;min-height:1.5em;font-family:var(--mono);font-size:11px}
.empty{text-align:center;padding:60px 20px;color:var(--fg3)}
footer{border-top:1px solid var(--line);margin-top:50px;padding:26px 0 0;font-size:12px;color:var(--fg3);max-width:var(--measure)}
footer p{margin:0 0 10px}
.tip{position:relative}
.legend{display:flex;gap:16px;flex-wrap:wrap;font-size:11px;color:var(--fg3);font-family:var(--mono);margin:-10px 0 22px;align-items:center}
.legend span{display:flex;align-items:center;gap:5px}
@media(max-width:900px){
  .wrap{grid-template-columns:1fr}
  aside{position:static;height:auto;border-right:0;border-bottom:1px solid var(--line);padding:16px 18px}
  main{padding:0 18px 60px}
  h1{font-size:25px}

  /* The rail stacked ABOVE the content on mobile: brand + 10 categories + 6
     community links came to ~770px, so a phone scrolled a screen and a half of
     navigation before reaching the headline. Collapse it behind one disclosure.
     .brandsub goes too — it restates the lede sitting directly underneath it.
     The Submit / Join calls to action are not lost: header carries both. */
  .brandsub{display:none}
  .navtoggle{display:flex}
  aside .navlbl,aside #nav,aside .navlink,aside .memberbar{display:none}
  aside.nav-open .navlbl,aside.nav-open #nav,aside.nav-open .navlink,
  aside.nav-open .memberbar{display:block}
  aside.nav-open .navlink{display:flex}
  .legend{gap:10px 14px}
}


/* ---------- membership: sidebar bar + sign-in ---------- */
.memberbar{margin-bottom:6px}
.memberbar .me-name{font-size:12px;color:var(--fg2);padding:4px 9px;line-height:1.4}
.memberbar .me-name b{color:var(--fg)}
.memberbar .me-link{background:none;border:0;color:var(--acc);font-size:11.5px;cursor:pointer;padding:2px 9px;font-family:var(--sans)}
.memberbar .me-link:hover{text-decoration:underline}

/* ---------- Turnstile widget spacing ---------- */
.ts-box{margin:6px 0 2px;min-height:0}
.ts-box:empty{margin:0}
.ts-note{font-size:11.5px;color:var(--fg3)}

/* ---------- form controls shared by the submit modal ---------- */
.field textarea,.field select{width:100%;background:var(--panel);border:1px solid var(--line2);color:var(--fg);
  border-radius:7px;padding:9px 11px;font-size:13.5px;font-family:var(--sans);outline:none}
.field textarea{resize:vertical;line-height:1.5}
.field textarea:focus,.field select:focus{border-color:var(--acc);box-shadow:0 0 0 3px color-mix(in srgb,var(--acc) 18%,transparent)}
.field textarea[aria-invalid="true"],.field select[aria-invalid="true"]{border-color:var(--bad)}

/* ---------- submitter credit on a card ---------- */
.credit{font-size:11px;color:var(--fg3);margin:2px 0 0}
.credit a{color:var(--acc);text-decoration:none}
.credit a:hover{text-decoration:underline}

/* ---------- admin: tabs + benchmark review queue ---------- */
.admin-tabs{display:flex;gap:8px;margin:0 0 18px}
.admin-tab{background:var(--panel);border:1px solid var(--line2);color:var(--fg2);border-radius:7px;
  padding:7px 14px;font-size:12.5px;cursor:pointer;font-family:var(--sans)}
.admin-tab.on{background:var(--acc);border-color:var(--acc);color:var(--onacc);font-weight:600}
.bench-card{background:var(--panel);border:1px solid var(--line);border-radius:11px;padding:18px 18px 16px;margin-bottom:14px}
.bench-top{display:flex;align-items:baseline;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:10px}
.bench-name{font-size:15px;font-weight:700;color:var(--fg)}
.src-badge{font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:.7px;text-transform:uppercase;
  border-radius:4px;padding:2px 7px;border:1px solid var(--line2);color:var(--fg3)}
.src-badge.web{color:var(--acc);border-color:var(--consbd);background:var(--consbg)}
.src-badge.auto{color:var(--warn);border-color:var(--line2)}
.checks{width:100%;border-collapse:collapse;font-size:12px;margin:8px 0 12px}
.checks td{padding:4px 8px;border-top:1px solid var(--line);vertical-align:top;color:var(--fg2)}
.checks td:first-child{white-space:nowrap;color:var(--fg2);font-weight:600;width:160px}
.checks td:nth-child(2){width:26px;text-align:center}
.chk-pass{color:var(--acc)}
.chk-warn{color:var(--warn)}
.chk-fail{color:var(--bad)}
.bench-edit{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:8px 0}
.bench-edit .fld{display:flex;flex-direction:column;gap:3px}
.bench-edit .fld.full{grid-column:1 / -1}
.bench-edit label{font-size:11px;font-weight:600;color:var(--fg3)}
.bench-edit input,.bench-edit textarea,.bench-edit select{background:var(--bg2);border:1px solid var(--line2);color:var(--fg);
  border-radius:6px;padding:7px 9px;font-size:12.5px;font-family:var(--sans);outline:none}
.bench-edit textarea{resize:vertical;line-height:1.45}
.bench-credit{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--fg2);margin:6px 0 4px}
.bench-actions{display:flex;gap:9px;align-items:center;margin-top:10px}
.bench-sub{font-size:11px;color:var(--fg3);font-family:var(--mono);margin-top:6px}

/* ---------- discussion: gateway (non-members) ---------- */
.gateway{text-align:center;padding:26px 18px 8px}
.gateway-icon{font-size:26px;margin-bottom:10px;opacity:.9}
.gateway h4{margin:0 0 10px;font-size:16px;letter-spacing:-.2px}
.gateway p{margin:0 auto 20px;font-size:13.5px;color:var(--fg2);max-width:46ch;line-height:1.6}
.gateway-actions{display:flex;gap:10px;justify-content:center;flex-wrap:wrap}

/* ---------- discussion: comments ---------- */
.cmt-loading,.cmt-empty,.cmt-error{font-size:13px;color:var(--fg3);padding:18px 2px}
.cmt-error{color:var(--bad)}
.cmt-count{font-size:11px;text-transform:uppercase;letter-spacing:1px;color:var(--fg3);margin-bottom:12px;font-weight:700}
.cmt-list{display:flex;flex-direction:column;gap:14px;margin-bottom:22px}
.cmt{display:flex;gap:11px}
.cmt-avatar{width:32px;height:32px;flex:none;border-radius:50%;background:var(--consbg);border:1px solid var(--consbd);
  color:var(--acc);display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;font-family:var(--mono)}
.cmt-main{flex:1;min-width:0}
.cmt-head{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;margin-bottom:3px}
.cmt-author{font-weight:700;font-size:13px;color:var(--fg)}
.cmt-org{font-size:11.5px;color:var(--fg3)}
.cmt-time{font-size:11px;color:var(--fg3);font-family:var(--mono);margin-left:auto}
.cmt-del{background:none;border:0;color:var(--fg3);cursor:pointer;font-size:15px;line-height:1;padding:0 4px;flex:none}
.cmt-del:hover{color:var(--bad)}
.cmt-body{font-size:13.5px;color:var(--fg2);line-height:1.6;white-space:normal;word-wrap:break-word;overflow-wrap:anywhere}

/* ---------- discussion: composer ---------- */
.composer{border-top:1px solid var(--line);padding-top:16px}
.composer textarea{width:100%;background:var(--panel);border:1px solid var(--line2);color:var(--fg);
  border-radius:9px;padding:11px 13px;font-size:13.5px;font-family:var(--sans);line-height:1.55;outline:none;resize:vertical;min-height:74px}
.composer textarea:focus{border-color:var(--acc);box-shadow:0 0 0 3px color-mix(in srgb,var(--acc) 18%,transparent)}
.composer-foot{display:flex;align-items:center;gap:12px;margin-top:10px}
.composer-hint{font-size:11.5px;color:var(--fg3);flex:1;min-width:0}
.composer-hint b{color:var(--fg2)}
.composer-kbd{font-family:var(--mono);font-size:10px;color:var(--fg3);border:1px solid var(--line);border-radius:4px;padding:1px 5px;margin-left:6px;white-space:nowrap}
.composer .bprim{flex:none}

/* ---------- flash banner (welcome / invalid link) ---------- */
.flash{position:fixed;left:50%;top:18px;transform:translateX(-50%);z-index:95;max-width:min(560px,92vw);
  background:var(--panel);border:1px solid var(--consbd);border-left:3px solid var(--acc);border-radius:9px;
  padding:12px 40px 12px 16px;font-size:13px;color:var(--fg);box-shadow:0 6px 24px rgba(0,0,0,.28)}
.flash-bad{border-color:var(--bad);border-left-color:var(--bad)}
.flash-x{position:absolute;right:10px;top:8px;background:none;border:0;color:var(--fg3);font-size:17px;cursor:pointer;line-height:1}
.flash-x:hover{color:var(--fg)}

/* ---------- dev-only confirmation link (only shown when DEV_MODE returns devLink) ---------- */
.devlink{display:block;margin-top:14px;padding:10px 12px;border:1px dashed var(--line2);border-radius:8px;
  background:var(--notebg);font-size:12.5px;color:var(--fg2)}
.devlink b{color:var(--warn);text-transform:uppercase;font-size:9.5px;letter-spacing:1px;margin-right:6px}

/* ============================================================
   Discussion popup "sheet" — large centered overlay
   ============================================================ */
body.sheet-open{overflow:hidden}
.sheetwrap{position:fixed;inset:0;z-index:80;display:flex;align-items:center;justify-content:center;padding:28px;
  background:#000a;backdrop-filter:blur(3px);opacity:0;pointer-events:none;transition:opacity .18s}
html[data-theme="light"] .sheetwrap{background:#0d1c1566}
.sheetwrap.on{opacity:1;pointer-events:auto}
.sheet{display:flex;flex-direction:column;background:var(--bg2);border:1px solid var(--line2);border-radius:14px;
  width:min(1040px,94vw);height:min(860px,90vh);overflow:hidden;box-shadow:0 20px 60px rgba(0,0,0,.4);
  transform:translateY(8px) scale(.995);transition:transform .18s}
.sheetwrap.on .sheet{transform:none}
.sheet-head{display:flex;align-items:center;gap:14px;padding:16px 20px;border-bottom:1px solid var(--line);flex:none}
.sheet-titles{flex:1;min-width:0}
.sheet-titles h3{margin:0;font-size:17px;letter-spacing:-.3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sheet-titles p{margin:2px 0 0;font-size:12px;color:var(--fg3)}
.sheet-back{background:none;border:1px solid var(--line2);color:var(--fg2);border-radius:7px;padding:5px 11px;font-size:12.5px;
  cursor:pointer;font-family:var(--sans);white-space:nowrap;flex:none}
.sheet-back:hover{border-color:var(--acc);color:var(--fg)}
.sheet-body{flex:1;overflow-y:auto;padding:22px 24px 28px}
@media(max-width:760px){
  .sheetwrap{padding:0}
  .sheet{width:100vw;height:100vh;height:100dvh;border-radius:0;border:0}
}

/* ---- topic list (board view) ---- */
.board-head{display:flex;justify-content:flex-start;margin-bottom:16px}
.topic-list{display:flex;flex-direction:column;gap:10px}
.topic-row{display:flex;align-items:center;gap:14px;padding:13px 15px;background:var(--panel);border:1px solid var(--line);
  border-radius:10px;cursor:pointer;transition:border-color .12s,background .12s;position:relative}
.topic-row:hover{border-color:var(--line2);background:var(--rowhov)}
.topic-row:focus-visible{outline:2px solid var(--acc);outline-offset:1px}
.topic-main{flex:1;min-width:0}
.topic-title{font-size:14.5px;font-weight:700;color:var(--fg);letter-spacing:-.2px;margin-bottom:3px}
.topic-meta{font-size:11.5px;color:var(--fg3)}
.topic-side{display:flex;flex-direction:column;align-items:flex-end;gap:2px;font-family:var(--mono);font-size:10.5px;color:var(--fg3);flex:none}
.topic-count{color:var(--acc)}
.topic-del{flex:none;background:none;border:0;color:var(--fg3);font-size:17px;line-height:1;cursor:pointer;padding:0 2px}
.topic-del:hover{color:var(--bad)}
.topic-adminbar{display:flex;justify-content:flex-end;margin-bottom:14px}
.topic-del-btn:hover{border-color:var(--bad);color:var(--bad)}

/* ---- new-topic form (reuses .field / .mactions) ---- */
.newtopic .field textarea{width:100%;background:var(--panel);border:1px solid var(--line2);color:var(--fg);
  border-radius:7px;padding:10px 12px;font-size:13.5px;font-family:var(--sans);line-height:1.55;outline:none;resize:vertical}
.newtopic .field textarea:focus{border-color:var(--acc);box-shadow:0 0 0 3px color-mix(in srgb,var(--acc) 18%,transparent)}

/* ============================================================
   Admin portal (admin.html)
   ============================================================ */
.admin-wrap{max-width:820px;margin:0 auto;padding:32px 22px 80px}
.admin-head{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;
  padding-bottom:18px;margin-bottom:26px;border-bottom:1px solid var(--line);flex-wrap:wrap}
.admin-who{font-size:12.5px;color:var(--fg3)}
.admin-who b{color:var(--fg2)}
.admin-title{font-size:18px;margin:0 0 16px;display:flex;align-items:center;gap:10px}
.admin-count{font-family:var(--mono);font-size:12px;color:var(--acc);border:1px solid var(--line2);border-radius:20px;padding:1px 9px}
.admin-loading,.admin-empty,.admin-error{color:var(--fg3);font-size:14px;padding:28px 4px}
.admin-error{color:var(--bad)}
.admin-card{background:var(--panel);border:1px solid var(--line);border-radius:11px;padding:24px 22px;max-width:520px}
.admin-card h2{margin:0 0 8px;font-size:18px}
.admin-card p{margin:0 0 14px;color:var(--fg2);font-size:14px;line-height:1.6}
.admin-list{display:flex;flex-direction:column;gap:12px}
.admin-row{display:flex;align-items:center;justify-content:space-between;gap:16px;
  background:var(--panel);border:1px solid var(--line);border-radius:11px;padding:15px 17px;flex-wrap:wrap}
.admin-person{min-width:0;flex:1}
.admin-name{font-size:15px;font-weight:700;color:var(--fg);letter-spacing:-.2px}
.admin-meta{font-size:12.5px;color:var(--fg2);margin-top:3px}
.admin-sub{font-size:11px;color:var(--fg3);font-family:var(--mono);margin-top:4px}
.admin-actions{display:flex;gap:9px;flex:none}

/* ============================================================
   Load-time polish (Tier 1): pending-auth gate, skeletons, reserved space
   ============================================================ */
/* While /api/me is in flight the auth state is unknown, so the member-dependent
   controls must not show the wrong content (a member must never flash "Join the
   consortium"). Header CTA row: hide both buttons and the note, and show a spinner
   in their place. Sidebar: keep the subtler shimmer-pill treatment. On resolve,
   updateMemberUI() removes .auth-pending and the real controls take over (Submit +
   Join/note for a guest, Submit for a member). */
.headcta{min-height:40px}                        /* stable row height across the swap */
.auth-pending #headSubmit,
.auth-pending #headJoin,
.auth-pending #headNote{display:none}

/* Neutral ring spinner in the brand accent — shown only while auth is pending. */
.spinner{display:none;width:22px;height:22px;border-radius:50%;
  border:2.5px solid var(--line2);border-top-color:var(--acc);animation:spin .7s linear infinite}
.auth-pending .spinner{display:inline-block}
@keyframes spin{to{transform:rotate(360deg)}}
@media(prefers-reduced-motion:reduce){ .spinner{animation:skpulse 1.2s ease-in-out infinite} }

.auth-pending #navJoin{
  color:transparent;background:var(--panel);border-color:var(--line);
  animation:skpulse 1.2s ease-in-out infinite;pointer-events:none
}
.auth-pending .memberbar{
  background:var(--panel);border-radius:7px;
  animation:skpulse 1.2s ease-in-out infinite
}
/* Reserve the sidebar member bar's height so it doesn't shove the nav links down
   when it fills in after /api/me (and gives the shimmer a stable box). */
.memberbar{min-height:26px}

/* Skeleton placeholders shown while the catalog loads. */
@keyframes skpulse{0%,100%{opacity:.5}50%{opacity:.9}}
.sk-card{height:190px;border:1px solid var(--line);border-radius:11px;background:var(--panel);animation:skpulse 1.2s ease-in-out infinite}
.sk-nav{height:15px;margin:9px 4px;border-radius:6px;background:var(--panel);animation:skpulse 1.2s ease-in-out infinite}
#out{min-height:70vh}   /* reserve the content column so the footer doesn't jump when cards land */

/* While the counts are still their 0 / — placeholders, shimmer them so they read
   as "loading" rather than as real (broken-looking) zeros. Removed in buildNav(). */
.stats.loading b{color:transparent;border-radius:5px;background:var(--panel);animation:skpulse 1.2s ease-in-out infinite}

/* Reserve widths so late-arriving data doesn't reflow the row it sits in:
   the consortium filter grows as options are appended; the per-card "updated"
   badge changes text as GitHub responds. */
#fcons{min-width:172px}
.upd{min-width:104px;justify-content:flex-end}

/* ============================================================================
   FIELD FIGURE — "where the field's attention has gone"
   ----------------------------------------------------------------------------
   One stacked bar per category: how many of its benchmarks have a repository
   that was pushed in the last year, how many have gone a year without a commit,
   and how many cannot be verified at all. Every number is rendered from
   data/benchmarks.json + data/freshness.json at runtime — nothing here is typed
   in, so a heading that counts things cannot go stale.

   COLOUR (measured, not chosen by eye). Three fills, validated with the dataviz
   skill's checker against this site's own card surfaces:

     dark  #0f1714   active #14764a  dormant #be8700  neutral #5c7266
                     CVD dE 12.4 (protan, worst adjacent) · normal dE 18.8 · all >= 3:1
     light #ffffff   active #007240  dormant #b97f2b  neutral #a7b8ac
                     CVD dE 10.5 · normal dE 17.3 · neutral 2.08:1

   Two exemptions, each with a reason:
     - the neutral fails the chroma floor BY DESIGN. "Activity not verified" is a
       recessive non-state, not a third magnitude; a grey that reads as grey is
       the correct answer, exactly as the genbio ramps treat their quiet band.
     - the light neutral is 2.08:1, a contrast WARN. Relief is mandatory and is
       provided: every segment carries a direct numeric label and the neutral
       carries a 1px border so its extent is legible without its fill.

   The site's shipped emerald/amber pair was measured FIRST and rejected:
   #01CE87 against #f7b955 is dE 5.6 under protanopia, below the hard floor of 6.
   Green-versus-amber cannot be separated by hue — the recurring trap in this
   project — so lightness carries it here and TEXTURE carries it again:
   active is solid, dormant is hatched 45deg, neutral is hatched 135deg. A reader
   who sees no colour at all can still read all three.
   ========================================================================== */
:root{
  --fs-active:#14764a; --fs-dormant:#be8700; --fs-neutral:#5c7266;
  /* One ink per fill, not one ink for the figure. The five DockQ-style bands in the
     sibling builds taught this the hard way: a mark drawn ON a multi-fill scale
     cannot have a single colour, because the fills tile the lightness axis. A shared
     light ink measured 2.76:1 on the amber and 2.47:1 on the grey — both below the
     4.5:1 that 10px bold text needs. Each fill now guarantees its own ink:
       active  #e8f2ec on #14764a  4.93:1
       dormant #1a1200 on #be8700  5.89:1
       neutral #ffffff on #5c7266  5.18:1 */
  --fs-ink-active:#e8f2ec; --fs-ink-dormant:#1a1200; --fs-ink-neutral:#ffffff;
}
html[data-theme="light"]{
  --fs-active:#007240; --fs-dormant:#b97f2b; --fs-neutral:#a7b8ac;
  /* active  #ffffff on #007240  6.03:1
     dormant #2a1c00 on #b97f2b  4.85:1
     neutral #0d1c15 on #a7b8ac  8.45:1 */
  --fs-ink-active:#ffffff; --fs-ink-dormant:#2a1c00; --fs-ink-neutral:#0d1c15;
}

.field{margin:0 0 30px}
.field__h{font-size:clamp(19px,1.5vw,23px);line-height:1.25;letter-spacing:-.4px;font-weight:700;
  margin:0 0 8px;max-width:var(--measure);
  /* Reserved: this line is rendered from data and its length changes as repository
     checks resolve. Without a floor, the swap reflows every row beneath it. */
  min-height:2.5em}
.field__lede{font-size:13px;color:var(--fg2);max-width:var(--measure);margin:0 0 16px}
.field__lede b{color:var(--fg)}

/* key — the swatches are real segments, so they cannot drift from the bars */
.field__key{display:flex;gap:16px;flex-wrap:wrap;font-size:11px;color:var(--fg3);
  font-family:var(--mono);margin:0 0 14px;align-items:center}
.field__key span{display:flex;align-items:center;gap:6px}
.field__key .fseg{width:22px;height:11px;border-radius:2px;flex:none}

.field__rows{display:flex;flex-direction:column;gap:3px}
/* One template, shared by every row AND the axis, via a custom property — a grid
   declared on the container would make the ROWS its items and collapse the tracks. */
.frow{display:grid;grid-template-columns:var(--fcols);align-items:center;gap:12px;
  padding:4px 8px;border-radius:7px;background:none;border:1px solid transparent;
  cursor:pointer;text-align:left;font:inherit;color:inherit;width:100%}
.frow:hover{background:var(--rowhov);border-color:var(--line)}
.frow:focus-visible{outline:2px solid var(--acc);outline-offset:1px}
.frow__name{font-size:12.5px;color:var(--fg2);line-height:1.3}
.frow:hover .frow__name{color:var(--fg)}
.frow__n{font-family:var(--mono);font-size:11px;color:var(--fg3);text-align:right;
  font-variant-numeric:tabular-nums}

.fbar{display:flex;height:19px;gap:2px}          /* 2px surface gap between fills */
.fseg{position:relative;min-width:2px;display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-size:10px;font-weight:600;overflow:hidden}
.fseg:first-child{border-radius:4px 0 0 4px}
.fseg:last-child{border-radius:0 4px 4px 0}
.fseg:only-child{border-radius:4px}
.fseg--active{background:var(--fs-active);color:var(--fs-ink-active)}
/* Texture is the third channel, after lightness and hue: 45deg for dormant, its
   135deg mirror for the neutral, solid for active. Tone-on-tone so it reads as a
   fill rather than as stripes of a second colour. */
.fseg--dormant{background:var(--fs-dormant);color:var(--fs-ink-dormant);
  background-image:repeating-linear-gradient(45deg,rgba(0,0,0,.26) 0 2px,transparent 2px 5px)}
.fseg--neutral{background:var(--fs-neutral);border:1px solid var(--line2);box-sizing:border-box;
  background-image:repeating-linear-gradient(135deg,rgba(0,0,0,.16) 0 2px,transparent 2px 5px);
  color:var(--fs-ink-neutral)}
.field__key .fseg--neutral{color:transparent}

.field__note{font-size:11.5px;color:var(--fg3);max-width:var(--measure);margin:12px 0 0}

@media(max-width:760px){
  .frow{grid-template-columns:1fr;gap:4px;padding:7px 8px}
  .frow__n{text-align:left}
}
