
/* Design tokens mirror chongk.com (虫库 Skill) so the two read as the same
   family of catalogue sites: light surfaces, a single brand green, and a
   1320px content column. Dark mode reuses the same roles with inverted
   surfaces rather than a second palette. */
:root{
  --brand:#10a37f; --brand-dark:#0d8467; --brand-soft:#e8f6f2;
  --ink:#1a1d21; --ink-2:#4a5158; --ink-3:#8b939b; --ink-4:#b3bac1;
  --line:#e6e9ec; --line-2:#f0f2f4;
  --bg:#fff; --bg-2:#fafbfc; --bg-3:#f4f6f8;
  --ok:#0b7a5e; --ok-bg:#f0f7f4; --warn:#a8641a; --warn-bg:#fdf6ec;
  /* Card system: the detail page is three nesting levels deep. `--card` is the card
     surface, `--card-line` the section border, `--card-line-2` the inner divider, and one
     shadow token keeps every card's depth the same instead of each rule inventing its own.
     These must be declared or `background:var(--card)` computes to nothing. */
  --card:#fff; --card-line:#e2e7ec; --card-line-2:#eef1f5;
  --shadow-card:0 1px 2px rgba(16,24,40,.04), 0 10px 28px -18px rgba(16,24,40,.22);
  /* The content column scales with the window: 94% of the viewport, never wider
     than 1420px. A fixed cap either crowds a 1366px laptop (1320px left 23px a
     side) or strands a 2560px monitor (1180px left 690px a side); a proportional
     one leaves a consistent margin at every size.

     `min(94vw, …)` rather than a pixel floor: a floor larger than the viewport
     computes a zero margin, which is exactly the crowding this replaces. The
     side padding scales too, or at 1280px the padding rather than the margin
     would be what shrinks away. */
  --maxw:min(1420px,94vw);
  --pad:max(24px,3vw);
  --radius:10px;
  --shadow:0 1px 2px rgba(26,29,33,.04),0 4px 12px rgba(26,29,33,.05);
  /* a lifted shadow for interactive elements, and a hairline for grouping */
  --shadow-lift:0 3px 14px rgba(16,24,40,.07);
  --shadow-soft:0 1px 1px rgba(16,24,40,.03);
  --hero-grad:linear-gradient(180deg,#fff 0%,var(--bg-2) 100%);
  --tint:linear-gradient(180deg,var(--brand-soft) 0%,rgba(232,246,242,0) 100%);
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
}
@media (prefers-color-scheme:dark){
  :root{
    --brand:#2bbd97; --brand-dark:#10a37f; --brand-soft:#12241f;
    --ink:#e8eaed; --ink-2:#b6bcc4; --ink-3:#868e97; --ink-4:#5c646d;
    --line:#2a2f36; --line-2:#23272d;
    --bg:#15181c; --bg-2:#1a1e23; --bg-3:#20242a;
    --ok:#3fbf95; --ok-bg:#12241f; --warn:#d9a441; --warn-bg:#2a2114;
    --card:#1b1f24; --card-line:#2c3238; --card-line-2:#252a30;
    --shadow-card:0 1px 2px rgba(0,0,0,.35), 0 12px 30px -20px rgba(0,0,0,.65);
    --shadow:none;
  }
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
font:15px/1.65 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
-webkit-font-smoothing:antialiased}
a{color:inherit;text-decoration:none}
a:hover{color:var(--brand)}
/* The site header scrolls away with the page. It was pinned with a blurred
   backdrop, which costs a strip of every screen and, on a page as long as a
   plugin detail, leaves the navigation floating over the content indefinitely. */
header{background:var(--bg);border-bottom:1px solid var(--line)}
.hwrap{max-width:var(--maxw);margin:0 auto;padding:12px var(--pad);display:flex;gap:22px;align-items:center;
flex-wrap:wrap}
.brand{font-weight:700;font-size:19px;letter-spacing:-.02em;white-space:nowrap;display:flex;
align-items:center;gap:9px}
/* The brand mark is the site's own logo, kept whole: beetle, 虫库 and the domain, on
   transparency. `width:auto` keeps its 290x101 proportions; the height is set so the two
   characters stay legible. It replaced a drawn dot that stood in for a logo. */
.brand a{display:flex;align-items:center;gap:9px}
.brand img{height:32px;width:auto;display:block;flex:none}
@media (prefers-color-scheme:dark){
  /* navy on a near-black header has almost no contrast; a light tile keeps the mark
     readable without inverting the brand colours */
  .brand img{background:#fff;border-radius:7px;padding:2px 3px;height:32px}
}
.brand span{color:var(--brand)}
nav{display:flex;gap:6px;font-size:14px;flex-wrap:wrap}
nav a{padding:5px 11px;border-radius:8px;color:var(--ink-2)}
nav a:hover{background:var(--bg-3);color:var(--ink)}
/* The content column stops growing past 1320px, as on the reference site: a
   directory that stretches across a 2560px monitor turns wide cards into long
   lines of unreadable ragging. */
main{max-width:var(--maxw);margin:0 auto;padding:30px var(--pad) 72px}
h1{font-size:30px;margin:0 0 8px;letter-spacing:-.025em;line-height:1.25}
h2{font-size:20px;margin:38px 0 14px;letter-spacing:-.015em}
/* The opening band: a soft gradient and a hairline, as on the reference site.
   It used to bleed to the window edges with a negative horizontal margin, which
   fought the new side margins — the band was full width while everything under it
   was inset. It also carried a negative top margin to cancel the padding of
   `<main>`, which double-counted the inset and pulled the band against the site
   header. It now sits inside the content column like every other block. */
/* The opening band carries the particle field. Three layers: a brand glow from
   the top, a hairline grid, and the canvas the script draws into. The glow and
   the grid are pseudo-elements so they cost nothing if the canvas never runs, and
   the whole layer is `pointer-events:none` so it cannot swallow a click.

   Layout note: the layer is absolutely positioned and the hero's content sits
   above it with `position:relative`, so adding it does not move anything. */
/* No `overflow:hidden` and no background here: the wash is painted by `.hero::before` across
   the viewport, and clipping the hero cut it back to the content column. The particle layer
   clips itself, which is what the `overflow` was for. */
.hero{position:relative;text-align:center;padding:30px 0 28px;
margin:0 0 30px;border-bottom:1px solid var(--line-2)}
.hero>.wrap{position:relative;z-index:1}
.hero-fx{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.hero-fx canvas{position:absolute;inset:0;width:100%;height:100%;display:block;
background:transparent}
.hero-fx::before{content:"";position:absolute;inset:-30% -10% auto;height:150%;
background:radial-gradient(46% 58% at 50% 24%,var(--brand-soft),transparent 70%);
opacity:.85}
.hero-fx::after{content:"";position:absolute;inset:0;opacity:.6;
background-image:
linear-gradient(to right,rgba(125,135,145,.06) 1px,transparent 1px),
linear-gradient(to bottom,rgba(125,135,145,.06) 1px,transparent 1px);
background-size:34px 34px;
mask-image:radial-gradient(70% 80% at 50% 30%,#000,transparent 78%);
-webkit-mask-image:radial-gradient(70% 80% at 50% 30%,#000,transparent 78%)}

/* Motion never gates content. A scroll-in reveal was tried and removed: it held
   the notice and the whole card grid at `opacity:0` until the script marked them
   visible, so a slow load showed a blank page and a failed script would have shown
   one permanently. The particle field is the only animation left, and it decorates
   a background layer rather than the content itself.
   `prefers-reduced-motion` turns that off too, and removes the hover lift. */
@media (prefers-reduced-motion:reduce){
  .card,.panel,.tile{transition:none}
  .card:hover{transform:none}
  .hero-fx{display:none}
}
.hero h1{font-size:34px}
.hero .sub{max-width:660px;margin:0 auto 22px}
.sub{color:var(--ink-3);margin:0 0 22px;font-size:14.5px}
.searchbar{display:flex;gap:10px;margin:0 0 16px}
input[type=search]{flex:1;background:var(--bg);border:1px solid var(--line);color:var(--ink);
padding:12px 16px;border-radius:var(--radius);font-size:15px;transition:border-color .15s,box-shadow .15s}
input[type=search]:focus{outline:none;border-color:var(--brand);
box-shadow:0 0 0 3px var(--brand-soft)}
.stats{display:flex;gap:34px;flex-wrap:wrap;justify-content:center;margin:0 0 26px}
.stats div{text-align:center;color:var(--ink-3);font-size:13px}
.stats b{display:block;color:var(--ink);font-size:23px;font-weight:700;letter-spacing:-.02em}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:16px}
/* Category and search grids run at the wide setting chosen from the comparison
   page (public/densities.html): ~4 cards per row at 1400px, ~332x173 — a
   landscape block rather than a near-square one, which suits a name, a source
   and a two-line description far better. The badge row is hidden and one inline
   flag stays in the footer. */
.grid.compact{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:8px}

/* The card is a positioning context: `.card-link` is a full-bleed anchor and
   the content floats above it ignoring pointer events, with links and buttons
   opting back in. A click anywhere opens the plugin; the copy button still
   receives its own clicks. */
/* ---------- 数据与方法页 ----------
   A methodology page reads as a document, not as a wall of prose: one column with a
   fixed rhythm, a summary strip of the headline numbers, a sticky section nav, and one
   card per check so coverage and outcome sit together instead of in a wide table that
   wraps badly on a phone. */
.doc{max-width:920px;margin:0 auto}
.doc h1{font-size:29px;margin:0 0 10px;letter-spacing:-.01em}
.doc .lead{color:var(--ink-2);font-size:15px;line-height:1.75;margin:0 0 22px;max-width:74ch}
.doc h2{font-size:19px;margin:38px 0 12px;padding-top:22px;border-top:1px solid var(--line);letter-spacing:-.005em}
.doc h2:first-of-type{border-top:0;padding-top:0}
.doc h3{font-size:15px;margin:22px 0 8px}
.doc p{line-height:1.75;font-size:14px;color:var(--ink-2)}
.doc .sub{color:var(--ink-3)}
.stat-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:1px;
  background:var(--line);border:1px solid var(--line);border-radius:10px;overflow:hidden;margin:0 0 20px}
.stat-strip>div{background:var(--bg);padding:12px 14px}
.stat-strip span{display:block;color:var(--ink-3);font-size:12px;margin:0 0 4px}
.stat-strip b{font-size:19px;letter-spacing:-.01em;font-variant-numeric:tabular-nums}
.docnav{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 6px}
.docnav a{padding:5px 11px;border:1px solid var(--line);border-radius:999px;font-size:12.5px;
  color:var(--ink-3);background:var(--bg)}
.docnav a:hover{border-color:var(--brand);color:var(--brand-dark)}
.steps{counter-reset:step;list-style:none;margin:0;padding:0}
.steps li{counter-increment:step;position:relative;padding:0 0 14px 40px;font-size:14px;
  line-height:1.75;color:var(--ink-2)}
.steps li::before{content:counter(step,decimal-leading-zero);position:absolute;left:0;top:1px;
  font-size:12px;color:var(--brand-dark);background:var(--brand-soft);border-radius:6px;padding:2px 7px}
.layers{display:grid;gap:10px}
.layer{border:1px solid var(--line);border-radius:10px;padding:14px 16px;background:var(--bg)}
.layer header{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin:0 0 6px}
.layer header code{font-size:12px;color:var(--brand-dark);background:var(--brand-soft);
  border-radius:5px;padding:2px 7px;font-weight:600}
.layer header h3{margin:0;font-size:15px}
.layer header .state{margin-left:auto;font-size:11.5px;padding:2px 9px;border-radius:999px;
  border:1px solid var(--line);color:var(--ink-3)}
.layer header .state.done{color:var(--ok);background:var(--ok-bg);border-color:transparent}
.layer header .state.sample{color:var(--warn);background:var(--warn-bg);border-color:transparent}
.layer .q{margin:0 0 10px;font-size:13.5px;color:var(--ink-2);line-height:1.7}
.cov{display:flex;align-items:center;gap:10px;margin:0 0 10px}
.cov .track{flex:1;height:6px;border-radius:3px;background:var(--bg-2);overflow:hidden}
.cov .fill{height:100%;border-radius:3px;background:var(--brand)}
.cov span{font-size:12px;color:var(--ink-3);font-variant-numeric:tabular-nums;white-space:nowrap}
.layer .res{display:flex;flex-wrap:wrap;gap:6px;list-style:none;margin:0;padding:0}
.layer .res li{font-size:12.5px;color:var(--ink-2);background:var(--bg-2);border-radius:6px;
  padding:3px 9px;font-variant-numeric:tabular-nums}
.layer .res li b{font-weight:600}
.layer .res li.flag{background:var(--warn-bg);color:var(--warn)}
.layer .res li.bad{background:#fdecea;color:#b3261e}
@media (prefers-color-scheme:dark){ .layer .res li.bad{background:#3a1d1a;color:#f2b8b5} }
.limits{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
.limits article{border-left:3px solid var(--line);padding:2px 0 2px 12px}
.limits h3{margin:0 0 4px;font-size:14px}
.limits p{margin:0;font-size:13px;color:var(--ink-3);line-height:1.7}
.doc table{width:100%;border-collapse:collapse;margin:6px 0 10px;font-size:13.5px}
.doc table th{text-align:left;font-weight:600;color:var(--ink-3);font-size:12.5px;
  padding:0 12px 8px 0;border-bottom:1px solid var(--line)}
.doc table td{padding:8px 12px 8px 0;border-bottom:1px solid var(--line-2);vertical-align:top}
.doc table tr:last-child td{border-bottom:0}

/* Trust signals (CHA2A level, scan state, health grade, weekly downloads).
   Four signals, four colours. They used to share one green, which is what made a row of
   chips read as a single word: the level, the scan state and the score are three different
   kinds of statement and a reader should be able to tell them apart before reading them.
   Downloads stay grey on purpose — popularity is not a judgement and must not look like one. */
.trust{display:flex;flex-wrap:wrap;gap:6px;margin:2px 0 10px}
.tchip{padding:3px 10px;border-radius:999px;font-size:11.5px;white-space:nowrap;
  background:var(--bg-2);color:var(--ink-3);border:1px solid var(--line)}
.tchip b{font-weight:600}
.tchip.lv{color:#0b6b52;background:#e3f4ee;border-color:transparent}
.tchip.lv.lv-l0{color:#25606b;background:#e2f1f3}
.tchip.lv.lv-l1{color:#1f6b3f;background:#e5f5e9}
.tchip.lv.lv-l2{color:#0b6b52;background:#ddf2ea}
.tchip.lv.lv-l3{color:#0a5b46;background:#d2efe4}
.tchip.lv.none{color:var(--ink-3);background:var(--bg-2);border-color:var(--line)}
.tchip.scan{color:#1d5c96;background:#e6f0fa;border-color:transparent}
.tchip.scan.pending{color:var(--warn);background:var(--warn-bg)}
.tchip.grade{color:#8a5a00;background:#fdf1dc;border-color:transparent}
.tchip.grade.g-s{color:#0b6b52;background:#ddf2ea}
.tchip.grade.g-b{color:#8a5a00;background:#fdf1dc}
.tchip.grade.low{color:#b3261e;background:#fdecea}
.tchip.dl{color:var(--ink-3);background:var(--bg-2)}
@media (prefers-color-scheme:dark){
  .tchip.lv,.tchip.lv.lv-l2,.tchip.lv.lv-l3,.tchip.grade.g-s{color:#8fe0c4;background:#12352c}
  .tchip.lv.lv-l1{color:#9fdcb4;background:#12301c}
  .tchip.lv.lv-l0{color:#9fd3dc;background:#123037}
  .tchip.scan{color:#9ec8ee;background:#12293c}
  .tchip.grade,.tchip.grade.g-b{color:#f0c98a;background:#3a2c12}
  .tchip.grade.low{color:#f2b8b5;background:#3a1d1a}
}
/* The ladder and the deduction list, now inside the 可信度 tab. */
.ladder{list-style:none;margin:10px 0 0;padding:0}
.ladder li{display:flex;gap:10px;padding:8px 10px;border-radius:7px;font-size:13px;
  align-items:baseline}
.ladder li + li{margin-top:4px}
.ladder .rung{flex:0 0 132px;color:var(--ink-3);display:flex;gap:6px;align-items:baseline}
.ladder .rung b{font-weight:600}
.ladder .mark{flex:0 0 16px;font-weight:600}
.ladder .why{flex:1;color:var(--ink-3);word-break:break-word}
.ladder .why .ev{display:block;font-size:11.5px;color:var(--warn);margin-top:2px}
.ladder .yes{background:var(--bg-2)}
.ladder .yes .mark{color:var(--ok)}
.ladder .yes.lv-l0{background:#eef6f7}
.ladder .yes.lv-l0 .rung b{color:#25606b}
.ladder .yes.lv-l1{background:#eef8f1}
.ladder .yes.lv-l1 .rung b{color:#1f6b3f}
.ladder .yes.lv-l2{background:#e9f6f1}
.ladder .yes.lv-l2 .rung b{color:#0b6b52}
.ladder .yes.lv-l3{background:#e2f3ec}
.ladder .yes.lv-l3 .rung b{color:#0a5b46}
.ladder .partial{background:var(--bg-2)}
.ladder .partial .mark{color:var(--warn)}
.ladder .no{background:transparent;border:1px dashed var(--line)}
.ladder .no .mark{color:var(--ink-3)}
@media (prefers-color-scheme:dark){
  .ladder .yes.lv-l0{background:#12262b}
  .ladder .yes.lv-l1{background:#122a1c}
  .ladder .yes.lv-l2{background:#122b24}
  .ladder .yes.lv-l3{background:#102a23}
  .ladder .yes.lv-l0 .rung b{color:#9fd3dc}
  .ladder .yes.lv-l1 .rung b{color:#9fdcb4}
  .ladder .yes.lv-l2 .rung b,.ladder .yes.lv-l3 .rung b{color:#8fe0c4}
}
.lv-chip{font-size:12px;padding:2px 9px;border-radius:999px;background:var(--bg-2);
  color:var(--ink-2)}
.lv-chip.lv-l0{background:#e2f1f3;color:#25606b}
.lv-chip.lv-l1{background:#e5f5e9;color:#1f6b3f}
.lv-chip.lv-l2{background:#ddf2ea;color:#0b6b52}
.lv-chip.lv-l3{background:#d2efe4;color:#0a5b46}
.lv-chip.lv-未评级{background:var(--bg-2);color:var(--ink-3)}
.deduct{list-style:none;margin:10px 0 0;padding:0;font-size:13px}
.deduct li{display:flex;gap:10px;padding:7px 0;border-top:1px solid var(--line-2)}
.deduct li:first-child{border-top:0}
.deduct .pts{flex:0 0 34px;color:#b3261e;font-variant-numeric:tabular-nums}
@media (prefers-color-scheme:dark){ .deduct .pts{color:#f2b8b5} }
.deduct .reason{flex:1}
.deduct .ev{display:block;margin-top:3px;color:var(--ink-3);font-size:12px;word-break:break-word}
.scoreline{display:flex;align-items:baseline;flex-wrap:wrap;gap:10px 12px;margin:2px 0 0}
.scoreline .big{font-size:34px;font-weight:600;line-height:1;color:var(--brand-dark)}
.scoreline .big.low{color:#b3261e}
@media (prefers-color-scheme:dark){ .scoreline .big.low{color:#f2b8b5} }
.scoreline .grade{font-size:15px;color:var(--brand-dark);white-space:nowrap}
.scoreline .grade.low{color:#b3261e}
.scoreline .grade-note{color:var(--ink-3);font-size:12.5px}
.panel h3{margin:20px 0 0;font-size:14px;display:flex;align-items:center;gap:8px}
/* Everything a reader needs once, folded away by default. */
.fold{margin:16px 0 0;border-top:1px solid var(--line);padding-top:12px}
.fold summary{cursor:pointer;font-size:12.5px;color:var(--ink-3)}
.fold summary:hover{color:var(--brand-dark)}
.fold .kv{margin:10px 0 0}
.disc{margin:10px 0 0;padding:10px 12px;border-radius:8px;background:var(--bg-2);
  color:var(--ink-3);font-size:12.5px;line-height:1.65}

/* ---------- detail tabs ----------
   The overview is inline; the complete README is fetched when its tab is first
   opened. The bar is the same underlined strip the category page uses, so the two
   tab sets on the site behave and look alike.

   While a README tab is open the bar sticks to the top of the window, so the
   switcher stays reachable through a document that can be hundreds of lines long.
   It is deliberately not sticky on the overview: the overview is short, a pinned
   bar there would take space for nothing, and a bar that followed the reader
   everywhere was rejected once already. The script sets `data-stuck` on the tab
   container when the visible panel is a README, and the rule below only applies
   then. */
.ptabs{display:flex;gap:2px;overflow-x:auto;border-bottom:1px solid var(--line);
margin:0 0 22px;scrollbar-width:thin;min-width:0;max-width:100%}
.ptabs[data-stuck]{position:sticky;top:0;z-index:20;background:var(--bg);
box-shadow:0 6px 12px -8px rgba(16,24,40,.14)}
.ptab{flex:0 0 auto;padding:9px 15px;font:inherit;font-size:14px;font-weight:500;
color:var(--ink-3);background:none;border:none;border-bottom:2px solid transparent;
cursor:pointer;white-space:nowrap;transition:color .15s,border-color .15s}
.ptab:hover{color:var(--ink)}
.ptab.on{color:var(--brand-dark);border-bottom-color:var(--brand)}
.ptab:focus-visible{outline:2px solid var(--brand);outline-offset:-2px;border-radius:6px}
.ppanel[hidden]{display:none}
/* the fetched body is the same `.md` typography as the rest of the page */
.readme-body{max-width:88ch}
/* a README centres its banner with align="center"; honour that one attribute */
.readme-body img[align=center],.readme-body p[align=center]{display:block;margin:8px auto}
.readme-body img[align=right]{float:right;margin:0 0 12px 16px}
.readme-body img[align=left]{float:left;margin:0 16px 12px 0}
@media (max-width:720px){
  /* on a phone the bar can be as tall as a third of the viewport once the labels
     wrap, and pinning it there costs more than it gives */
  .ptabs[data-stuck]{position:static;box-shadow:none}
}
@media (prefers-reduced-motion:no-preference){
  /* the sticky bar must not jump as it engages */
  .ptabs[data-stuck]{transition:box-shadow .2s ease}
}

/* ---------- detection report ----------
   The opening tab. The summary answers "should I worry" in one line, the list is
   ordered by how much attention each finding deserves, and the caveats sit at the
   bottom where they qualify everything above rather than competing with it. */
/* The report's summary is a symbol and its counts, not a sentence: one large level
   icon, then one chip per severity. Words survive as tooltips and as sr-only text. */
.rsum{background:var(--bg-2);border:1px solid var(--line-2);border-radius:12px;
padding:14px 18px;margin:0;display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.rsum-lead{display:flex;align-items:center;gap:12px;min-width:0}
.rsum-ic{color:var(--tone);display:inline-flex;flex:none}
.rsum-chips{display:flex;gap:8px;flex-wrap:wrap}
.lvl-chip{display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:20px;
background:color-mix(in srgb,var(--tone) 10%,var(--bg));color:var(--tone);
border:1px solid color-mix(in srgb,var(--tone) 22%,transparent)}
.lvl-chip b{font-size:14px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}
.lvl-chip.quiet{--tone:var(--ink-4)}
.rsum-legend{margin-left:auto;display:flex;gap:14px;flex-wrap:wrap;color:var(--ink-3);
font-size:12px}
.lg-i{display:inline-flex;align-items:center;gap:4px;color:var(--tone)}
/* The capability header: a shape for the detection type, a shape for the severity.
   `--tone` is the level colour, so the icon and the count agree. */
.cap-ic{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;
border-radius:7px;flex:none;color:var(--ink-2);
background:color-mix(in srgb,var(--ink-4) 14%,var(--bg))}
.cap-elevated .cap-ic{color:#d9534f;background:color-mix(in srgb,#d9534f 13%,var(--bg))}
.cap-notable .cap-ic{color:#a8641a;background:color-mix(in srgb,#d9a441 16%,var(--bg))}
@media (prefers-color-scheme:dark){
  .cap-elevated .cap-ic{color:#e07b76}
  .cap-notable .cap-ic{color:#d9b666}
}
.cap-n{display:inline-flex;align-items:center;gap:6px}
.lvl{display:inline-flex;align-items:center}
.cap-n b{font-size:14px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}
/* Screen-reader-only: keeps the words that the icons replaced. */
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
clip:rect(0 0 0 0);white-space:nowrap;border:0}
.pmain h3{font-size:14px;font-weight:650;color:var(--ink-2);margin:24px 0 10px;
padding-bottom:6px;border-bottom:1px solid var(--line-2)}
.caps{list-style:none;margin:0;padding:0;display:grid;gap:8px}
/* Each finding is a card with a tinted header band. The old style put the severity in
   a 3px strip down the left edge; a band reads as a heading rather than an alert, and
   the level still travels as text in the pill so colour is never the only signal. */
.cap{border:1px solid var(--line);border-radius:9px;background:var(--bg);overflow:hidden}
.cap-h{display:flex;align-items:center;gap:9px;flex-wrap:wrap;padding:8px 12px;
background:var(--bg-2);border-bottom:1px solid var(--line-2)}
.cap-h b{font-size:13.5px;font-weight:650}
.cap-h code{font-size:11.5px}
.cap-n{margin-left:auto;font-size:12px;color:var(--ink-4);white-space:nowrap}
.cap-b{padding:9px 12px 10px}
.cap-elevated .cap-h{background:color-mix(in srgb,#d9534f 7%,var(--bg))}
.cap-notable .cap-h{background:color-mix(in srgb,#d9a441 9%,var(--bg))}
@media (prefers-color-scheme:dark){
  .cap-elevated .cap-h{background:color-mix(in srgb,#e07b76 12%,var(--bg))}
  .cap-notable .cap-h{background:color-mix(in srgb,#d9b666 12%,var(--bg))}
}
/* the matched lines: a file reference the reader can act on, then the code itself */
.ev{list-style:none;margin:7px 0 0;padding:0;display:grid;gap:3px}
.ev li{display:flex;gap:9px;align-items:baseline;font-size:12px;min-width:0}
.ev-loc{flex:0 0 auto;font-size:11.5px;color:var(--ink-4);background:none;
border:none;padding:0;max-width:32%;overflow:hidden;text-overflow:ellipsis;
white-space:nowrap}
.ev-tx{flex:1 1 auto;min-width:0;font-size:11.5px;color:var(--ink-3);
background:var(--bg-3);border:1px solid var(--line-2);border-radius:5px;
padding:2px 7px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
display:block}
.ev-note{font-size:12px;color:var(--ink-3)}
/* the L1 report's "what to check" line, quieter than the code above it */
.ev-advice{font-size:12px;color:var(--ink-3);line-height:1.65}
.ev-advice::before{content:"→ ";color:var(--ink-4)}

/* the install state at the top of the install card, above the command it qualifies */
.ins{margin:0 0 6px}
.ins .bdg{font-size:12px}

/* ---------- depth ----------
   Shadows carry the hierarchy: a resting shadow that is barely there, a lift on
   hover, and a larger one for the hero's own panel. Values follow the reference
   catalogue's scale. */
.card{position:relative;background:var(--bg);border:1px solid var(--line);
border-radius:var(--radius);box-shadow:var(--shadow-soft);
transition:border-color .18s ease,box-shadow .18s ease,transform .18s ease}
.card:hover{border-color:color-mix(in srgb,var(--brand) 38%,var(--line));
box-shadow:0 10px 26px rgba(16,24,40,.11);transform:translateY(-4px);z-index:2}
.card:focus-within{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
.card-link{position:absolute;inset:0;z-index:1;border-radius:inherit}
.card-link:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.card-inner{position:relative;z-index:2;pointer-events:none;
padding:16px;display:flex;flex-direction:column;gap:10px;height:100%}
.card-inner a,.card-inner button,.card-inner input{pointer-events:auto}
.compact .card-inner{padding:12px 14px;gap:6px;border-radius:9px}

.card-top{display:flex;gap:11px;align-items:flex-start}
.compact .card-top{gap:10px}
/* the initial block: a stable per-package colour, as in the reference */
.ico{width:36px;height:36px;flex:0 0 36px;border-radius:10px;color:#fff;
display:flex;align-items:center;justify-content:center;font-weight:700;font-size:16px;
line-height:1;box-shadow:inset 0 -1px 0 rgba(0,0,0,.12)}
.compact .ico{width:32px;height:32px;flex:0 0 32px;border-radius:9px;font-size:15px}

.card h3{margin:0;font-size:15px;font-weight:650;line-height:1.35}
.card h3{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.compact .card h3{font-size:14px}
.ns{margin-top:2px;font-size:12px;color:var(--ink-3);overflow:hidden;
text-overflow:ellipsis;white-space:nowrap}
.compact .ns{font-size:11.5px}

/* A square icon button, as on the reference cards: quiet until hovered, never
   a filled pill. The earlier pill turned solid white on hover, which read as a
   different control appearing under the cursor. */
.ccopy{display:inline-grid;place-items:center;flex:0 0 auto;
width:28px;height:28px;padding:0;cursor:pointer;
color:var(--ink-3);background:var(--bg);border:1px solid var(--line);
border-radius:7px;transition:color .15s,border-color .15s,background .15s,opacity .15s}
/* Hidden until the card is hovered or the button is focused, as on the reference
   site: the action is available without competing with the content for
   attention. Never hidden from keyboard or touch users. */
.card .ccopy{opacity:0}
.card:hover .ccopy,.card:focus-within .ccopy,.ccopy:focus-visible{opacity:1}
@media (hover:none){.card .ccopy,.codebox .ccopy{opacity:1}}
.ccopy svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.9;
stroke-linecap:round;stroke-linejoin:round}
.ccopy:hover{color:var(--brand-dark);border-color:var(--brand);background:var(--bg)}
.ccopy:active{background:var(--brand-soft)}
/* the confirmation replaces the icon with a tick, so the button keeps its size */
.ccopy.done{color:var(--ok);border-color:var(--ok)}
.ccopy.off{display:inline-flex;align-items:center;width:auto;height:auto;padding:2px 8px;
color:var(--ink-4);background:var(--bg-3);border-color:transparent;
font-size:11px;cursor:default;border-radius:99px}
.compact .ccopy{width:26px;height:26px}
.compact .ccopy svg{width:13px;height:13px}
.compact .ccopy.off{width:auto;height:auto;padding:2px 8px}

.badges{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.bdg{padding:2px 8px;font-size:11.5px;white-space:nowrap;color:var(--ink-3);
background:var(--bg-3);border-radius:99px}
.bdg.key{color:var(--brand-dark);background:var(--brand-soft)}
.bdg.ok{color:var(--ok);background:var(--ok-bg)}
.bdg.warn{color:var(--warn);background:var(--warn-bg)}
.bdg.bad{color:#b3261e;background:#fdecea}
@media (prefers-color-scheme:dark){ .bdg.bad{color:#f2b8b5;background:#3a1d1a} }

.tx{margin:0;font-size:13.5px;line-height:1.6;color:var(--ink-2);
display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.compact .tx{font-size:12.5px;line-height:1.5;-webkit-line-clamp:2}
/* the author's original wording, subordinate to the translation */
.en{margin:0;font-size:12px;line-height:1.5;color:var(--ink-4);
display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.compact .en{font-size:11.5px;-webkit-line-clamp:1}
/* the description takes the slack so the footer sits on the card's floor */
.card-inner > .tx{flex:1 0 auto}
.card-inner > .en{flex:0 1 auto}

/* The footer is an information row, not a divider: install flag, license,
   category, and the install action all live here rather than leaving the strip
   nearly empty and the action floating in the title row. */
.foot{display:flex;align-items:center;gap:7px;flex-wrap:wrap;
font-size:11.5px;color:var(--ink-3);border-top:1px solid var(--line-2);
padding-top:8px;margin-top:auto}
.foot .lic{color:var(--ok)}
.foot .lic.none{color:var(--ink-4)}
.foot .cat-l{color:var(--ink-4)}
/* the action sits at the end of the row without pushing the metadata apart */
.foot .ccopy{margin-left:auto}
.compact .foot{font-size:11px;padding-top:7px;gap:6px}

/* ---- colour ----
   The homepage was white on white: hero, counters and chips all in the same grey, so the six
   numbers summarising the catalogue were the quietest thing on the page. Each counter now
   carries a hue from the same family the trust chips use, and the hero is washed with three
   of them at low opacity rather than one flat gradient. */
/* The wash belongs to the page, not to the content column: painted on `.hero` it stopped at
   the column's edge and left white margins on both sides. The pseudo-element spans the
   viewport while the hero's own content stays inside the column. */
/* `100vw` includes the scrollbar, so the full-bleed hero wash is ~15px wider than the
   content area. Clip at the root, where viewport scrolling is decided. */
html{overflow-x:hidden}
body{overflow-x:clip}
.hero{position:relative;isolation:isolate}
.hero::before{
  content:"";position:absolute;z-index:-1;top:0;bottom:0;
  left:calc(50% - 50vw);width:100vw;
  background:
    radial-gradient(900px 380px at 6% -12%, rgba(16,163,127,.20), transparent 62%),
    radial-gradient(780px 320px at 94% 2%, rgba(47,111,208,.17), transparent 64%),
    radial-gradient(720px 300px at 50% 110%, rgba(192,122,30,.14), transparent 62%),
    radial-gradient(520px 240px at 30% 100%, rgba(122,92,208,.10), transparent 66%),
    linear-gradient(180deg, #f2faf8 0%, #ffffff 82%)}
@media (prefers-color-scheme:dark){
  .hero::before{
    background:
      radial-gradient(900px 340px at 8% -10%, rgba(43,189,151,.10), transparent 62%),
      radial-gradient(760px 300px at 92% 4%, rgba(96,149,222,.10), transparent 64%),
      linear-gradient(180deg, #14181d 0%, var(--bg) 78%)}
}
.stat{position:relative;overflow:hidden}
.stat::before{content:"";position:absolute;left:0;right:0;top:0;height:3px;
  background:var(--tone,var(--line));opacity:.85}
.stat .k{color:var(--tone,var(--ink-3))}
.stat .k svg{stroke:var(--tone,currentColor)}
.stat .v{color:var(--ink)}
.stat .v small{color:var(--ink-3)}
/* the filter chips keep their own hue when active, so a filtered list says which filter */
.quickchips .chip[data-only="installable"].on{background:#e8f0fb;color:#1d5c96}
.quickchips .chip[data-only="client"].on{background:#f0ebfb;color:#5b3fb0}
.quickchips .chip[data-only="scanned"].on{background:#e6f4f6;color:#0e7383}
.quickchips .chip[data-only=""].on{background:var(--brand-soft);color:var(--brand-dark)}

/* ---- hero search, as on the reference homepage ---- */
.bigsearch{display:flex;align-items:center;gap:10px;max-width:680px;margin:0 auto 14px;
background:var(--bg);border:1px solid var(--card-line);border-radius:99px;
/* no right padding and clipping on: the button is the pill's right end, not a box inside it */
padding:0 0 0 18px;overflow:hidden;
box-shadow:0 1px 2px rgba(16,24,40,.04), 0 12px 28px -18px rgba(16,24,40,.22)}
.bigsearch:focus-within{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
.bigsearch .mag{width:18px;height:18px;flex:0 0 18px;fill:none;stroke:var(--ink-4);
stroke-width:2;stroke-linecap:round}
.bigsearch input{flex:1;min-width:0;border:none;background:none;padding:15px 0;
font:inherit;font-size:15px;color:var(--ink)}
.bigsearch input:focus{outline:none}
.bigsearch .go{flex:0 0 auto;align-self:stretch;padding:0 30px;font:inherit;font-size:14.5px;
font-weight:600;color:#fff;background:var(--brand);border:none;border-radius:0;cursor:pointer;
transition:background .15s ease}
.bigsearch .go:hover{background:var(--brand-dark)}
.chiphint{color:var(--ink-3);font-size:12.5px;margin:0 0 9px;text-align:center}
.quickchips{display:flex;gap:7px;flex-wrap:wrap;justify-content:center;margin:0 0 22px}
/* Each filter keeps its own tint even when idle, so the row of chips is not six grey pills
   in a row — the colours are the same ones the counters and the trust chips use. */
.quickchips .chip{padding:5px 13px;font-size:13px;color:var(--ink-2);background:var(--bg-3);
border-radius:99px;border:1px solid transparent}
.quickchips .chip[data-only=""]{background:#e8f6f2;color:#0b6b52}
.quickchips .chip[data-only="installable"]{background:#e8f0fb;color:#1d5c96}
.quickchips .chip[data-only="client"]{background:#f0ebfb;color:#5b3fb0}
.quickchips .chip[data-only="scanned"]{background:#e6f4f6;color:#0e7383}
.quickchips .chip[href="/categories.html"]{background:#fdf1dc;color:#8a5a00}
.quickchips .chip:hover{filter:brightness(.97)}
.quickchips .chip:hover{color:var(--brand-dark);background:var(--brand-soft)}
.quickchips .chip.on{color:#fff;background:var(--brand)}
/* centred key/value counters under the search */
.metagrid{display:flex;gap:34px;justify-content:center;flex-wrap:wrap;margin:0}
.metagrid div{text-align:center}
.metagrid dt{color:var(--ink-3);font-size:12.5px}
.metagrid dd{margin:2px 0 0;color:var(--ink);font-size:21px;font-weight:700;letter-spacing:-.02em}
/* a section heading row: title, hint, and a "more" link */
.sec-h{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin:0 0 14px}
.sec-h h2,.sec-h h1{margin:0;font-size:19px}
.sec-h .hint{color:var(--ink-3);font-size:12.5px}
.sec-h .more{margin-left:auto;font-size:13px;color:var(--brand-dark)}
.sec{margin:0 0 38px}
/* the "load more" row under the homepage grid */
.more-row{display:flex;justify-content:center;margin:22px 0 0}
.more-row .go{padding:9px 22px;font:inherit;font-size:14px;font-weight:600;color:#fff;
background:var(--brand);border:none;border-radius:99px;cursor:pointer}
.more-row .go:hover{background:var(--brand-dark)}
#restPages[hidden]{display:none}

/* Pills on the plugin detail page. The card grid uses `.bdg` instead; these
   stay because the detail page has room for the fuller labels. */
.tags{display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin:0 0 6px}
.tag{font-size:11.5px;padding:2.5px 9px;border-radius:99px;background:var(--bg-3);
border:1px solid var(--line);color:var(--ink-3);white-space:nowrap}
.tag.ok{color:var(--ok);background:var(--ok-bg);border-color:transparent}
.tag.warn{color:var(--warn);background:var(--warn-bg);border-color:transparent}
.tag.bad{color:#b3261e;background:#fdecea;border-color:transparent}
.tag.acc{color:var(--brand-dark);background:var(--brand-soft);border-color:transparent}
@media (prefers-color-scheme:dark){ .tag.bad{color:#f2b8b5;background:#3a1d1a} }
/* The author's English line under a translated description, collapsed by
   default. It reuses `details.lang`, so the marker and hover behaviour match the
   English README further down the page rather than being a second pattern. */
.lead-en{margin:0 0 12px}
.lead-en>summary{font-size:12px;color:var(--ink-4)}
.lead-en[open]>summary{margin-bottom:8px}
.lead-en p{margin:0;color:var(--ink-3);font-size:13px;line-height:1.7;
max-width:88ch}
.lead{font-size:15.5px;color:var(--ink-2);margin:0 0 8px;line-height:1.7}

/* ---- detail page ---- */
/* a bordered block per section, as on the reference plugin page */
.panel{background:var(--bg);border:1px solid var(--line);border-radius:var(--radius);
padding:18px 20px;margin:0 0 18px;box-shadow:var(--shadow-soft);
transition:box-shadow .18s ease}
.panel:hover{box-shadow:0 6px 18px rgba(16,24,40,.07)}
.panel h2{margin:0 0 12px;font-size:16px;display:flex;align-items:center;gap:8px}
/* a short brand tick before each section title, so the panels read as a series */
.panel h2::before{content:"";width:3px;height:15px;border-radius:2px;
background:var(--brand);flex:0 0 3px}
.panel .note{margin:0 0 7px;color:var(--ink-3);font-size:13px}
.panel .note.warn{color:var(--warn);background:var(--warn-bg);border-radius:8px;
padding:10px 13px;margin:14px 0 0;font-size:13px;line-height:1.65}
.cmd{display:flex;align-items:center;gap:9px;background:var(--bg-2);
border:1px solid var(--line);border-radius:8px;padding:8px 8px 8px 12px;margin:0 0 12px}
.cmd code{flex:1;min-width:0;overflow-x:auto;white-space:nowrap;background:none;
padding:0;font-size:13px;color:var(--ink)}
.cmd .ccopy{flex:0 0 auto}
/* ---------- plugin detail: identity band over two columns ----------
   The arrangement follows the reference catalogue's item page: the plugin's own
   name, source, description and key facts sit in a full-width band above the
   grid, and the grid below is two columns — prose on the left, facts and actions
   on the right, both starting on the same line. */
.phead{display:flex;gap:18px;align-items:flex-start;padding:26px 0 22px;
border-bottom:1px solid var(--line-2);margin:0 0 26px}
.phead .ico-lg{width:56px;height:56px;flex:0 0 56px;border-radius:15px;font-size:25px;
display:flex}
.phead-tx{flex:1;min-width:0}
.phead h1{margin:0 0 5px;font-size:26px;letter-spacing:-.025em;line-height:1.2;
overflow-wrap:anywhere}
.phead .sub{margin:0 0 12px;font-size:13.5px}
.phead .sub a{color:var(--ink-3);overflow-wrap:anywhere}
.phead .sub a:hover{color:var(--brand-dark)}
.phead .lead{font-size:15px;line-height:1.8;margin:0 0 10px;max-width:820px}
/* the collapsed English original, matching the English README's treatment */
.phead .lead-en{margin:0 0 12px;max-width:820px}
/* the provenance note sits under the description it qualifies, and stays quiet */
.phead .phead-tx>p.sub:not(:first-of-type){font-size:12px;line-height:1.65;
color:var(--ink-4);margin:0 0 12px;max-width:820px}
.phead-pills{display:flex;flex-wrap:wrap;gap:7px;margin:0 0 16px}
/* the author's own keywords, as quiet chips beside the name */
.phead-kw{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 14px}
.kw{font-size:11.5px;color:var(--ink-3);background:var(--bg-3);
border:1px solid var(--line-2);border-radius:99px;padding:2px 9px}
/* a monospace value that must wrap rather than widen the column */
.pside .mono-sm{font-family:var(--mono);font-size:11.5px;line-height:1.6;
overflow-wrap:anywhere}
/* The label reads as a label and the value carries the colour. The colon is part
   of the label rather than a separate node, so a wrapped row never leaves one
   stranded at the end of a line.

   `dd` carries a user-agent `margin-inline-start: 40px` — the indent that makes a
   description list readable when it is a stacked block, and exactly 40px of
   unwanted space when the pair is a flex row. Measured with tools/dom_pair.py:
   the value sat 40px past the label until this reset it. `dt` needs no reset; the
   element has no default margin. */
.phead-meta{display:flex;flex-wrap:wrap;gap:10px 30px;margin:0;padding:0;
justify-content:flex-start}
.phead-meta>div{display:flex;align-items:baseline;gap:6px;min-width:0}
.phead-meta dt{font-size:12px;color:var(--ink-4);white-space:nowrap;flex:0 0 auto;
margin:0}
.phead-meta dt::after{content:"："}
.phead-meta dd{font-size:13.5px;font-weight:650;color:var(--brand-dark);
flex:0 0 auto;margin:0}

/* ---------- article beside a sticky fact column ---------- */
/* Two columns, following the reference catalogue page (chongk.com item pages):
   a reading column and a column of fact panels, with the latter sticky under the
   header.
   The sticky part is what makes this work on a catalogue where most articles are
   short. The column is taller than the viewport, so a static sidebar would sit
   below the fold while the article is read; stuck, it stays in view and reveals
   itself as the reader scrolls, and its own end releases it.
   The widths are a reading measure first: 780px minus the panel's own padding
   leaves ~730px of text, which is where the prose sat when it was capped by
   `max-width` alone. The fact column is 380px rather than the reference's 268px
   because the install commands carry a package spec or a repo URL — at 268px they
   broke mid-token and the column grew taller than the article beside it. */
.pdetail{display:grid;grid-template-columns:minmax(0,780px) 380px;gap:34px;
align-items:start;min-width:0}
.pmain{min-width:0}
/* Article panels are cards, like the fact column's.
   They used to be stripped bare — `border:none; background:none; box-shadow:none`
   — separated only by a hairline. On a page whose content is mostly short
   sections that read as one undifferentiated sheet of text, which is exactly the
   complaint: the detail page felt flat. Giving each section a surface costs a
   little vertical space and buys the page its structure back. */
.pmain .panel{background:var(--bg);border:1px solid var(--line);border-radius:14px;
padding:20px 24px 22px;box-shadow:0 1px 2px rgba(16,24,40,.04);margin:0}
.pmain .panel+.panel{margin-top:14px}
.pmain .panel h2{font-size:17px;font-weight:700;letter-spacing:-.01em;
margin:0 0 16px}
/* the prose column is where a reader spends their time, so it gets the reference
   catalogue's line height rather than the listing pages' tighter one */
.pmain .md{font-size:14.5px;line-height:1.9}
.pmain .md p{margin:0 0 13px}
/* The reading measure is the column itself now, so the `max-width` cap that used
   to enforce it — 88ch on .md, .lead, .phead-meta and others — would only leave
   the card half empty. */
.pmain .md,.pmain .sub{max-width:none}

/* The fact column is a stack of cards that sticks under the header while the
   article scrolls past it. `top` clears the site header so the first card is not
   hidden behind it. The column is often taller than the viewport; a sticky box
   taller than its viewport scrolls with the page until its own bottom is reached,
   which is the behaviour wanted here — the cards are revealed as the reader
   scrolls rather than being unreachable below the article. */
.pside{display:flex;flex-direction:column;gap:12px;min-width:0;
position:sticky;top:76px}
.pside .panel{padding:15px 16px;margin:0;border-radius:12px;
box-shadow:0 1px 2px rgba(16,24,40,.03);min-width:0}
/* The peer list is a wrapped row of names, one chip each.
   As a single comma-joined run it broke names mid-word and, at thirteen entries,
   made this panel taller than everything beside it. `nowrap` on the chips keeps
   each name whole; the names are shown without their scope, which every peer
   shares, because the scope alone was wider than the column. */
.pside .mono-sm{display:flex;flex-wrap:wrap;gap:2px 6px;overflow-wrap:normal}
.pside .mono-sm>span{white-space:nowrap}
.pside .panel h2{font-size:13px;margin:0 0 10px}
.pside .panel h2::before{height:13px}
/* The install commands live in this column, which is 268px wide, so the generic
   `.cmd` box does not fit them: it keeps the code on one line and scrolls it
   sideways, and a package spec plus a repo URL are both longer than that.
   Here the box stacks instead — code wraps (`anywhere` plus `break-all`) and the
   copy button floats in the corner, which costs 26px of line width but no height.
   Tried and reverted: putting the button *below* the code, which gives the text
   the full width but added a row (566px -> 597px for the panel) without stopping
   the break, since the token is still longer than the box. */
.pside #install .cmd{display:block;padding:9px 10px;margin:0 0 9px;position:relative}
.pside #install .cmd code{display:block;white-space:normal;overflow-wrap:anywhere;
word-break:break-all;font-size:11.5px;line-height:1.6;padding-right:26px}
.pside #install .cmd .ccopy{position:absolute;top:6px;right:6px}
/* One label/value pair per row.
   A two-column split was tried here and reverted: at a 284px sidebar the columns
   come out 118px wide, long values (a patch path, a peer list, a homepage URL)
   wrap hard, and the block grew from 604px to 911px — worse than what it
   replaced. Two narrow columns lose to one wider one whenever the values are
   URLs and identifiers rather than short words.
   The unbalanced sidebar this leaves behind is a real finding, recorded in the
   handoff notes; fixing it needs the sidebar itself to be wider, not the rows
   to be split. */
.pside .kv{display:grid;grid-template-columns:1fr;gap:0;margin:0}
.pside .kv dt{font-size:11.5px;color:var(--ink-4);margin-top:9px}
.pside .kv dt:first-child{margin-top:0}
.pside .kv dd{margin:1px 0 0;font-size:13px;overflow-wrap:anywhere}
.pside table{font-size:11.5px}
.pside th,.pside td{padding:4px 7px;white-space:normal}
.pside .md{font-size:12.5px;line-height:1.6}
.pside .codebox code{font-size:11.5px}
.pside .note{font-size:11.5px;line-height:1.6}
.pback{margin:30px 0 0}

/* A reading measure for the prose. The column is 814px at this width, which is
   56 Chinese characters a line — past the point where the eye loses its place on
   the return sweep. 88ch lands at about 700px, or 48 characters.
   
   `ch` is the width of a digit, not of a CJK glyph, so the number looks large for
   the measure it produces: 1ch is about 8px here while one Chinese character is
   14.5px. 88ch is therefore ~700px, not ~1280px. */
.pmain .md,.phead .lead,.phead .lead-en,
.phead .phead-tx>p.sub:not(:first-of-type),.phead-kw,.phead-meta{max-width:88ch}

/* Below 1080px there is no room for two columns, so the page becomes one and the
   fact card moves above the article — the same move the reference page makes.
   That order is deliberate: on a phone the install commands are the thing a
   visitor came for, and pushing them under a long README buries them. */
@media (max-width:1080px){
  .pdetail{grid-template-columns:minmax(0,1fr);gap:20px}
  .pside{position:static;order:-1}
  .phead .ico-lg{width:46px;height:46px;flex:0 0 46px;font-size:20px;border-radius:12px}
  .phead h1{font-size:22px}
}


/* ---------- plugin page header ----------
   The page used to open on a bare <h1>; this gives it a subject: the same
   coloured initial the cards use, the name, the source, and the two facts a
   visitor checks first — version and category. */
.phead{display:flex;align-items:flex-start;gap:15px;padding:22px 0 18px;
margin:0 0 20px;border-bottom:1px solid var(--line-2)}
.phead .ico-lg{width:52px;height:52px;flex:0 0 52px;border-radius:13px;font-size:23px}
.phead-tx{flex:1;min-width:0}
.phead h1{margin:0 0 5px;font-size:27px;letter-spacing:-.025em;line-height:1.2;
overflow-wrap:anywhere}
.phead .sub{margin:0;font-size:13.5px}
.phead .sub a{color:var(--ink-3)}
.phead .sub a:hover{color:var(--brand-dark)}
.phead-act{display:flex;gap:7px;flex:0 0 auto;align-items:center;padding-top:5px}
.pill{display:inline-flex;align-items:center;font-size:12px;font-weight:600;
padding:4px 10px;border-radius:99px;background:var(--bg-3);color:var(--ink-2);
border:1px solid var(--line-2);white-space:nowrap}
.pill.soft{background:var(--brand-soft);color:var(--brand-dark);border-color:transparent}
.pill.warn{background:var(--warn-bg);color:var(--warn);border-color:transparent}
.pill.bad{background:#fdeceb;color:#b3261e;border-color:transparent}

/* ---------- collapsible block ----------
   For evidence that supports a summary rather than being the summary: the
   scanner's per-capability table, the English README. */
details.fold{margin:0}
details.fold>summary{cursor:pointer;font-size:12.5px;color:var(--ink-3);
list-style:none;display:flex;align-items:center;gap:6px;user-select:none;
padding:7px 0}
details.fold>summary::-webkit-details-marker{display:none}
details.fold>summary::before{content:"▸";font-size:10px;color:var(--ink-4);
transition:transform .15s}
details.fold[open]>summary::before{transform:rotate(90deg)}
details.fold>summary:hover{color:var(--brand-dark)}
details.fold>summary:focus-visible{outline:2px solid var(--brand);outline-offset:2px;
border-radius:4px}
details.fold[open]>summary{margin-bottom:8px}

/* ---------- rendered README ----------
   Third-party text, so nothing here may escape the panel: images are capped,
   code scrolls inside its own box, and long words break rather than widening
   the page. */
.md{font-size:14px;line-height:1.78;color:var(--ink-2);overflow-wrap:anywhere}
.md>*:first-child{margin-top:0}
.md>*:last-child{margin-bottom:0}
.md h1,.md h2,.md h3,.md h4,.md h5,.md h6{
color:var(--ink);line-height:1.35;margin:24px 0 10px;letter-spacing:-.01em}
.md h1{font-size:21px}
.md h2{font-size:18px;padding-bottom:7px;border-bottom:1px solid var(--line-2)}
.md h3{font-size:16px}
.md h4,.md h5,.md h6{font-size:14.5px}
.md p{margin:0 0 13px}
.md ul,.md ol{margin:0 0 13px;padding-left:23px}
.md li{margin:0 0 6px}
.md li::marker{color:var(--ink-4)}
.md a{color:var(--brand-dark);text-decoration:underline;text-underline-offset:2px;
text-decoration-color:color-mix(in srgb,var(--brand) 34%,transparent)}
.md a:hover{text-decoration-color:var(--brand)}
.md blockquote{margin:0 0 15px;padding:3px 0 3px 15px;border-left:3px solid var(--line);
color:var(--ink-3)}
.md hr{border:none;border-top:1px solid var(--line-2);margin:22px 0}
.md img.img-dead{display:none}
.md img{max-width:100%;height:auto;border-radius:7px;margin:5px 0}
.md table{display:block;width:100%;overflow-x:auto;border-collapse:collapse;
font-size:13px;margin:0 0 15px}
.md th,.md td{border:1px solid var(--line);padding:7px 11px;text-align:left;
vertical-align:top;white-space:nowrap}
.md th{background:var(--bg-3);font-weight:640;color:var(--ink)}
.md tbody tr:nth-child(even){background:var(--bg-2)}
.md del{color:var(--ink-4)}

/* ---------- code box ----------
   Structure follows the reference catalogue's `.cmdbox`: a header strip with a
   language label and a copy action, then the scrolling body. Tabs, not spaces,
   for the highlight palette below. */
.codebox{border:1px solid var(--line);border-radius:10px;overflow:hidden;
margin:0 0 15px;background:var(--bg)}
.codebox-h{display:flex;align-items:center;justify-content:space-between;gap:8px;
padding:5px 7px 5px 12px;background:var(--bg-3);border-bottom:1px solid var(--line-2)}
.codebox-lang{font-size:11.5px;font-weight:640;color:var(--ink-3);letter-spacing:.3px;
font-family:var(--mono)}
.codebox .ccopy{width:24px;height:24px;border-color:transparent;background:transparent}
.codebox .ccopy svg{width:13px;height:13px}
.codebox .ccopy:hover{background:var(--bg);border-color:var(--line)}
.codebox:hover .ccopy,.codebox .ccopy:focus-visible{opacity:1}
.codebox .ccopy{opacity:0;transition:opacity .15s,color .15s,border-color .15s,background .15s}

/* Light-theme token palette: distinguishable, and quiet enough to read as code
   rather than as a fruit salad. */
.tok-k{color:#a626a4}
.tok-s{color:#0a7d55}
.tok-n{color:#b76b01}
.tok-c{color:var(--ink-4);font-style:italic}
.tok-f{color:#3b6fb5}
.tok-p{color:var(--ink-3)}
.md blockquote{margin:0 0 14px;padding:2px 0 2px 14px;border-left:3px solid var(--line);
color:var(--ink-3)}
.md hr{border:none;border-top:1px solid var(--line-2);margin:20px 0}
.md img{max-width:100%;height:auto;border-radius:6px;margin:4px 0}
.md table{display:block;width:100%;overflow-x:auto;border-collapse:collapse;
font-size:13px;margin:0 0 14px}
.md th,.md td{border:1px solid var(--line);padding:6px 10px;text-align:left;
vertical-align:top;white-space:nowrap}
.md th{background:var(--bg-3);font-weight:600;color:var(--ink)}
.md del{color:var(--ink-4)}

/* Inline code, and the body of a fenced block. A fence is wrapped in `.codebox`
   by the renderer, so `pre` here only styles what sits inside that wrapper. */
.md code{font-family:var(--mono);font-size:.9em;background:var(--bg-3);
padding:1.5px 5px;border-radius:5px;border:1px solid var(--line-2)}
.md pre{margin:0;background:var(--bg);overflow-x:auto}
.md pre code{display:block;background:none;border:none;padding:13px 15px;
font-size:12.5px;line-height:1.65;color:var(--ink)}

/* The English README sits under the Chinese one, collapsed: it is the original
   and sometimes the fuller document, but it should not push the Chinese text
   off the first screen. */
details.lang{margin:18px 0 0;border-top:1px solid var(--line-2);padding-top:12px}
details.lang>summary{cursor:pointer;font-size:13px;color:var(--ink-3);
list-style:none;display:flex;align-items:center;gap:7px;user-select:none}
details.lang>summary::-webkit-details-marker{display:none}
details.lang>summary::before{content:"▸";font-size:11px;color:var(--ink-4);
transition:transform .15s}
details.lang[open]>summary::before{transform:rotate(90deg)}
details.lang>summary:hover{color:var(--brand-dark)}
details.lang>summary:focus-visible{outline:2px solid var(--brand);outline-offset:2px;
border-radius:4px}
details.lang[open]>summary{margin-bottom:12px}
/* the nested copy is dimmer so it reads as secondary to the Chinese text */
details.lang .md{font-size:13.5px;color:var(--ink-3)}

code,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
code{font-size:.92em;background:var(--bg-3);padding:1px 5px;border-radius:5px}
pre{background:var(--bg-2);border:1px solid var(--line);border-radius:var(--radius);
padding:13px 15px;overflow:auto;font-size:13px;margin:0;color:var(--ink)}
pre code{background:none;padding:0}
.filters{display:flex;gap:7px;flex-wrap:wrap;margin:0 0 18px}
.filters a{font-size:13px;padding:5.5px 13px;border:1px solid var(--line);border-radius:99px;
color:var(--ink-2);background:var(--bg);cursor:pointer}
.filters a:hover{border-color:var(--brand);color:var(--brand-dark)}
.filters a.on{background:var(--brand);border-color:var(--brand);color:#fff}
.filters a b{font-weight:600;opacity:.65;margin-left:3px}
table{width:100%;border-collapse:collapse;font-size:14px}
th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line-2);vertical-align:top}
th{color:var(--ink-3);font-weight:600;font-size:12.5px;text-transform:uppercase;
letter-spacing:.04em;border-bottom-color:var(--line)}
.kv{display:grid;grid-template-columns:170px 1fr;gap:9px 18px;font-size:14px}
.kv dt{color:var(--ink-3)}
.kv dd{margin:0;word-break:break-word}
/* Icons sit on the text baseline and inherit the surrounding colour. */
.ic{vertical-align:-2px;flex:none}
/* Stat cards: the six headline numbers. A grid rather than a flex row so the cards
   keep their width when they wrap on a narrow screen. */
.stats{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:12px;
margin:22px auto 0;max-width:1020px}
@media (max-width:980px){.stats{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (max-width:620px){.stats{grid-template-columns:repeat(2,minmax(0,1fr))}}
.stat{background:var(--bg);border:1px solid var(--line);border-radius:var(--radius);
padding:12px 14px;text-align:left}
.stat .k{display:flex;align-items:center;gap:6px;color:var(--ink-3);font-size:12.5px}
.stat .v{margin-top:4px;font-size:24px;font-weight:700;letter-spacing:-.02em;color:var(--ink);
font-variant-numeric:tabular-nums}
.stat .v small{font-size:13px;font-weight:600;color:var(--ink-3);margin-left:2px}
.stat.good .k{color:var(--ok)}
.stat.warn .k{color:var(--warn)}
/* Horizontal bars, used by /about for the capability distribution. */
.hbar{display:grid;grid-template-columns:190px 1fr 54px 52px;align-items:center;
gap:10px;font-size:13px;padding:3px 0}
.hbar .lbl{color:var(--ink-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hbar .track{height:9px;border-radius:5px;background:var(--bg-3);overflow:hidden}
.hbar .fill{display:block;height:100%;border-radius:5px}
.hbar .num{text-align:right;font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums}
.hbar .pct{text-align:right;color:var(--ink-3);font-variant-numeric:tabular-nums}
@media (max-width:620px){.hbar{grid-template-columns:120px 1fr 44px 46px;font-size:12px}}
/* Coverage meter on the /about table: how much of the catalogue a layer has seen. */
.cov{display:flex;align-items:center;gap:8px;white-space:nowrap}
.cov .track{width:56px;height:5px;border-radius:3px;background:var(--bg-3);overflow:hidden;flex:none}
.cov .fill{display:block;height:100%;background:var(--brand)}
/* A full-coverage layer and a sampled one must not look alike. */
.tag{font-size:11px;padding:1px 6px;border-radius:20px;border:1px solid var(--line);
color:var(--ink-3);white-space:nowrap}
.tag.full{color:var(--ok);border-color:color-mix(in srgb,var(--ok) 35%,transparent);background:var(--ok-bg)}
.tag.sample{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 30%,transparent);background:var(--warn-bg)}
/* The verification band: nine rows of numbers. The layer key and the question are
   reference material, the two right columns are what a reader came for, so the
   right columns get the width and the key column stays narrow. */
table.verify{font-size:13.5px}
table.verify td:first-child,table.verify th:first-child{width:64px;white-space:nowrap}
table.verify td:nth-child(2){color:var(--ink-3);width:32%}
table.verify td:nth-child(3){white-space:nowrap;color:var(--ink-3)}
table.verify td:last-child b{color:var(--ink)}
table.verify tr:last-child td{border-bottom:none}
.notice{background:var(--warn-bg);border:1px solid color-mix(in srgb,var(--warn) 24%,transparent);
border-radius:var(--radius);padding:13px 16px;color:var(--ink-2);font-size:13.5px;margin:0 0 24px}
/* The source-audit banner comes in two weights: a repository that is gone or
   empty is a warning, one that was merely renamed or archived is information.
   Using the warning colour for both would train the reader to ignore it. */
.notice.info{background:var(--bg-2);
border-color:var(--line);
border-left:3px solid var(--ink-4)}
.notice a{color:var(--brand-dark)}
.lead{font-size:15.5px;color:var(--ink-2);margin:0 0 18px}
footer{border-top:1px solid var(--line);color:var(--ink-3);font-size:13px;
text-align:center;
padding:26px var(--pad);max-width:var(--maxw);margin:0 auto}
footer p{margin:0 0 7px}
.empty{color:var(--ink-3);padding:52px 0;text-align:center}
.chips{display:flex;gap:7px;flex-wrap:wrap;margin:0 0 22px;font-size:13px}
.chips a{padding:4px 11px;border-radius:99px;background:var(--bg-3);color:var(--ink-2)}
.chips a:hover{background:var(--brand-soft);color:var(--brand-dark)}

/* category tabs: an underlined strip on wide viewports, a scrollable row on
   narrow ones. The tab buttons carry the panel titles; the link row above them
   is the crawlable, no-JS path to the same content. */
.tablinks{display:flex;gap:6px;flex-wrap:wrap;margin:0 0 18px;font-size:12.5px}
.tablinks a{padding:3px 10px;border-radius:99px;color:var(--ink-3);background:var(--bg-3)}
.tablinks a:hover{color:var(--brand-dark);background:var(--brand-soft)}
.tabs{display:flex;gap:2px;overflow-x:auto;border-bottom:1px solid var(--line);
margin:0 0 24px;scrollbar-width:thin}
.tab{flex:0 0 auto;padding:10px 15px;font:inherit;font-size:14px;font-weight:500;
color:var(--ink-3);background:none;border:none;border-bottom:2px solid transparent;
cursor:pointer;white-space:nowrap;transition:color .15s,border-color .15s}
.tab:hover{color:var(--ink)}
.tab b{margin-left:6px;font-size:12px;font-weight:600;opacity:.7}
.tab.on{color:var(--brand-dark);border-bottom-color:var(--brand)}
.tab:focus-visible{outline:2px solid var(--brand);outline-offset:-2px;border-radius:6px}
@media (prefers-color-scheme:dark){ .tab.on{color:var(--brand)} }
.panel[hidden]{display:none}

/* ---------- card density ----------
   Restored after a stylesheet edit dropped them: the dense grid hides the
   capability badge row and shows the inline install flag instead, and without
   these four rules every card renders at full size in the compact layout. */
.compact .bdg{padding:2px 8px;font-size:11px}
.compact .badges{display:none}
.only-dense{display:none}
.compact .only-dense{display:inline-block}

/* ---------- detail page: cards ----------
   Three nesting levels (see the header comment on `.ppanel`): a section card on the page,
   a group card inside it, and an evidence well inside that. Each level differs in border
   weight, radius, background and shadow so the containment is visible before a word is
   read — the previous layout gave every group the same hairline and the same white, which
   is why a finding and the code it was found in looked like siblings. */
.pdetail{gap:22px}
.ppanel > .panel,
.pside > .panel{
  background:var(--card);border:1px solid var(--card-line);border-radius:15px;
  box-shadow:var(--shadow-card);padding:20px 22px 22px}
.panel:hover{box-shadow:var(--shadow-card)}
.panel h2{margin:0 0 16px;font-size:16px;font-weight:650;letter-spacing:-.01em}
.panel h2::before{width:4px;height:17px;border-radius:3px}
.panel h3{margin:22px 0 0;padding-top:16px;border-top:1px solid var(--card-line-2)}

/* the identity band becomes the first card */
.phead{
  background:var(--card);border:1px solid var(--card-line);border-radius:15px;
  box-shadow:var(--shadow-card);padding:22px 24px;margin:0 0 20px}
.phead .phead-meta{margin-top:14px;padding-top:14px;border-top:1px solid var(--card-line-2)}
.phead-kw{margin-top:12px}

/* ---- level B: group cards ---- */
.rsum{
  background:var(--card);border:1px solid var(--card-line);border-radius:12px;
  box-shadow:0 1px 2px rgba(16,24,40,.03);padding:14px 16px}
.caps{gap:11px}
.cap{
  background:var(--card);border:1px solid var(--card-line);border-radius:12px;
  /* the left bar carries the same colour as the severity icon, so a group's signal is
     readable at the edge of the card rather than only inside it */
  border-left:3px solid var(--cap-tone,var(--card-line));
  box-shadow:0 1px 2px rgba(16,24,40,.035), 0 6px 18px -14px rgba(16,24,40,.18);
  overflow:hidden}
.cap + .cap{margin-top:0}
.cap-h{padding:10px 14px;border-bottom:1px solid var(--card-line-2)}
.cap-b{
  /* level C: the evidence sits in a well, tinted and inset, joined to its finding by the
     tinted band above it rather than by a label */
  background:var(--bg-2);box-shadow:inset 0 1px 0 rgba(16,24,40,.03);padding:11px 14px 13px}

.ev{gap:2px;margin:0}
.ev li{
  position:relative;padding:5px 0 5px 15px;border-radius:6px;font-size:12px}
.ev li:hover{background:var(--bg-3)}
.ev li::before{
  /* a small elbow: the visual "belongs to the item above" */
  content:"";position:absolute;left:2px;top:9px;width:8px;height:9px;
  border-left:1.5px solid var(--line);border-bottom:1.5px solid var(--line);
  border-bottom-left-radius:4px}
.ev-loc{
  font-family:var(--mono);font-size:11px;color:var(--ink-2);background:var(--bg);
  border:1px solid var(--card-line-2);border-radius:5px;padding:1px 6px}
.ev code, .ev .ev-code{
  font-family:var(--mono);font-size:11.5px;color:var(--ink-2);background:var(--bg);
  border:1px solid var(--card-line-2);border-radius:5px;padding:1px 6px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
.ev-advice{
  margin-top:9px;padding:8px 11px;background:var(--bg);border:1px solid var(--card-line-2);
  border-radius:8px;font-size:12px}

/* the score reads as its own card inside 可信度 */
.scoreline{
  background:var(--bg-2);border:1px solid var(--card-line-2);border-radius:12px;
  padding:14px 16px;box-shadow:inset 0 1px 0 rgba(16,24,40,.03)}
.deduct{gap:0}
.deduct li{
  border-top:1px solid var(--card-line-2);padding:10px 0 10px 0}
.deduct .pts{
  flex:0 0 40px;display:flex;align-items:center;justify-content:center;
  background:var(--card);border:1px solid var(--card-line);border-radius:8px;
  height:26px;font-size:12.5px;font-weight:600}
.deduct .ev{
  margin-top:6px;padding:7px 10px;background:var(--bg);border:1px solid var(--card-line-2);
  border-radius:7px;border-left:2px solid var(--warn)}

/* one card per rung, coloured at the edge by the level it stands for */
.ladder{gap:8px;display:grid}
.ladder li{
  border:1px solid var(--card-line);border-radius:10px;padding:10px 13px;
  border-left:3px solid var(--card-line);box-shadow:0 1px 2px rgba(16,24,40,.03)}
.ladder li.yes.lv-l0{border-left-color:#25606b}
.ladder li.yes.lv-l1{border-left-color:#1f6b3f}
.ladder li.yes.lv-l2{border-left-color:#0b6b52}
.ladder li.yes.lv-l3{border-left-color:#0a5b46}
.ladder li.partial{border-left-color:var(--warn)}
.ladder li.no{border-left-color:var(--line);border-style:dashed;background:transparent}
.ladder .rung{flex:0 0 128px}

/* the folded block keeps the evidence but steps out of the reading path */
.fold{
  background:var(--bg-2);border:1px solid var(--card-line-2);border-radius:11px;
  padding:2px 14px 14px;margin-top:18px}
.fold summary{padding:11px 0 9px;font-weight:500}
.fold .kv{margin-top:12px}

/* ---- sidebar ---- */
.pside{gap:20px}
.pside .panel{padding:18px 20px 20px}
.kv{margin:0}
.kv dt{
  font-size:11.5px;color:var(--ink-4);letter-spacing:.02em;margin:0}
.kv dd{
  margin:2px 0 0;font-size:13px;color:var(--ink-2);word-break:break-word}
.kv > div{
  display:grid;grid-template-columns:1fr;gap:0;padding:9px 0;
  border-top:1px solid var(--card-line-2)}
.kv > div:first-child{border-top:0;padding-top:2px}

/* the install command is a code card, not a paragraph */
.panel .cmd, .panel .co{
  background:var(--bg-2);border:1px solid var(--card-line);border-radius:10px;
  box-shadow:inset 0 1px 0 rgba(16,24,40,.03)}
.panel .note.warn{
  border:1px solid #f0dfc4;background:var(--warn-bg);border-radius:10px;padding:10px 12px;
  margin-top:14px}

/* notices above the page open as a card with a coloured edge */
.notice{
  border:1px solid var(--card-line);border-left:3px solid var(--warn);
  border-radius:12px;box-shadow:0 1px 2px rgba(16,24,40,.03)}
.pback{margin-top:22px}

@media (prefers-color-scheme:dark){
  .cap, .rsum, .ladder li, .deduct .pts{
    box-shadow:0 1px 2px rgba(0,0,0,.3), 0 8px 20px -16px rgba(0,0,0,.5)}
  .panel .note.warn{border-color:#5a4520}
}

/* The card page needs a backdrop. A white card on a white page has a border nobody can
   see; the tint is what turns the border and the shadow into a visible edge. */
body.pg-cards{background:var(--bg-3)}
body.pg-cards main{padding-bottom:34px}
.pg-cards .panel, .pg-cards .phead{background:var(--card)}
.pg-cards .notice{background:var(--card)}
@media (prefers-color-scheme:dark){
  body.pg-cards{background:#101317}
}

footer .icp{margin:6px 0 0;font-size:12.5px}
footer .icp a{color:var(--ink-3)}
footer .icp a:hover{color:var(--brand-dark);text-decoration:underline}

.ins-why{background:var(--warn-bg);border-radius:8px;padding:8px 11px;
  color:var(--ink-2);font-size:12.5px;line-height:1.6}

/* ---------- policy pages ----------
   `.limits` is a card grid for the about page's short boundary items; full sentences need a
   list. Same reading measure as the rest of the prose, with a brand-coloured marker. */
.doc-list, .points{list-style:none;margin:14px 0 0;padding:0;max-width:78ch}
.points li{position:relative;padding:0 0 12px 20px;font-size:14px;line-height:1.75;
  color:var(--ink-2)}
.points li::before{content:"";position:absolute;left:2px;top:.62em;width:7px;height:7px;
  border-radius:2px;background:var(--brand);opacity:.65}
.points li b{color:var(--ink)}
.points li code{font-family:var(--mono);font-size:12.5px;background:var(--bg-3);
  border-radius:4px;padding:1px 5px}
.sec > h2{margin:26px 0 0;font-size:16px;font-weight:650}
.sec > p{max-width:78ch;line-height:1.8;color:var(--ink-2)}
footer .flinks{margin:6px 0 0}
footer .flinks a{color:var(--ink-3)}
footer .flinks a:hover{color:var(--brand-dark);text-decoration:underline}

/* ---------- back to top ----------
   A tab on the right screen edge, level with the reading area. It used to sit in the bottom
   corner, which is the far end of every pointer journey; a screen border is an effectively
   infinite-width target, so a tab on it costs the least to hit. The text column never comes
   closer than about 47px to the right edge (--maxw 94vw + --pad 3vw), so a 40px tab covers
   gutter only. Touch screens get the bottom corner instead, where the thumb already rests. */
.totop{position:fixed;right:0;top:calc(50% - 22px);
  z-index:40;width:40px;height:44px;padding:0;display:flex;align-items:center;justify-content:center;
  background:var(--bg);border:1px solid var(--card-line);border-right:0;
  border-radius:12px 0 0 12px;cursor:pointer;
  color:var(--ink-2);box-shadow:0 1px 2px rgba(16,24,40,.05), 0 10px 24px -14px rgba(16,24,40,.30);
  opacity:0;visibility:hidden;transform:translateX(12px);
  transition:opacity .18s ease, transform .18s ease, visibility .18s, border-color .15s ease}
.totop.on{opacity:1;visibility:visible;transform:none}
.totop:hover{border-color:var(--brand);color:var(--brand-dark)}
.totop:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
.totop svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
@media (max-width:760px){
  /* Touch layout: back to the bottom corner, where the thumb already rests, but well clear of
     the browser's own bottom bar — iOS Safari overlays roughly the last 50px of the page — and
     of the gesture strip under it. 48px because on a phone the pointer is a thumb, which is the
     one case where the plain 44px target is not enough. `.totop.on` above still wins on
     specificity, so the shown state keeps no transform. */
  .totop{top:auto;right:16px;bottom:calc(80px + env(safe-area-inset-bottom,0px));
    width:48px;height:48px;border-right:1px solid var(--card-line);border-radius:14px;
    transform:translateY(8px)}
  .totop svg{width:20px;height:20px}
}
@media (prefers-reduced-motion:reduce){
  .totop{transition:none;transform:none}
}

/* ---------- breadcrumb + siblings ----------
   The detail page's only inbound context used to be the JSON-LD breadcrumb, which no reader
   and few crawlers follow. Both blocks are plain links. */
.crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:7px;margin:0 0 14px;
  font-size:13px;color:var(--ink-3)}
.crumbs a{color:var(--ink-2)}
.crumbs a:hover{color:var(--brand-dark);text-decoration:underline}
.crumbs .sep{color:var(--ink-4)}
.crumbs [aria-current]{color:var(--ink-3)}
.related{margin:26px 0 0;padding:18px 20px;background:var(--card);
  border:1px solid var(--card-line);border-radius:14px;box-shadow:var(--shadow-card)}
.related h2{margin:0 0 12px;font-size:15px;font-weight:650}
.related .rel-grid{display:grid;gap:10px;grid-template-columns:repeat(3,minmax(0,1fr))}
@media (max-width:980px){ .related .rel-grid{grid-template-columns:repeat(2,minmax(0,1fr))} }
.related .rel-card{display:flex;align-items:center;gap:10px;min-width:0;
  padding:10px 12px;border:1px solid var(--card-line);border-radius:11px;background:var(--bg);
  box-shadow:0 1px 2px rgba(16,24,40,.03);transition:border-color .15s ease, box-shadow .15s ease}
.related .rel-card:hover{border-color:var(--brand);
  box-shadow:0 1px 2px rgba(16,24,40,.04), 0 8px 20px -14px rgba(16,24,40,.28)}
.related .rel-ico{flex:0 0 30px;width:30px;height:30px;border-radius:9px;color:#fff;
  display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700}
.related .rel-body{min-width:0;display:flex;flex-direction:column;gap:2px;flex:1 1 auto}
.related .rel-body b{font-size:13.5px;font-weight:600;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.related .tchip{flex:none}
.related ul{list-style:none;margin:0;padding:0;display:grid;gap:8px}
.related li{display:flex;gap:10px;align-items:baseline;min-width:0}
.related li a{font-size:13.5px;color:var(--brand-dark);font-weight:500;white-space:nowrap}
.related li a:hover{text-decoration:underline}
.related .rel-tx{color:var(--ink-3);font-size:12px;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.related .rel-more{margin:14px 0 0;font-size:13px}

/* ---------- narrow screens ----------
   Measured at 375px against 1440px, every parameter was identical — the desktop layout was
   simply squeezed, and 131 of 252 tappable elements were under 40px high. These rules give
   the small screen its own numbers instead of the desktop ones scaled down. */
@media (max-width:640px){
  .phead{padding:16px 16px 18px}
  .phead .ico-lg{width:44px;height:44px;flex-basis:44px;font-size:20px;border-radius:12px}
  .phead h1{font-size:20px;line-height:1.3}
  .grid{gap:12px}
  .card .tx{font-size:13.5px;line-height:1.6;-webkit-line-clamp:3}
  .card .foot{row-gap:6px}
  /* 40px is the smallest comfortable touch target; the measured median here was 30px. */
  .ccopy,.chip,.ptab,.tchip,.pill{min-height:0}
  .ccopy{width:34px;height:34px}
  .chip{padding:8px 13px;font-size:13px}
  .ptab{padding:11px 13px;font-size:13.5px}
  .pager .pg{padding:9px 13px;min-width:40px}
  .related{padding:15px 15px}
  .related .rel-grid{grid-template-columns:1fr}
  .crumbs{font-size:12.5px}
  .lv-block{padding:13px 13px 15px}
  .ladder li{padding-left:15px}
  .ladder .rung{flex-basis:auto;display:block}
  .ladder li{display:block}
  .ladder .mark{float:right}
}

/* ---------- category pager ---------- */
.pager{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:24px 0 0}
.pager .pg{min-width:34px;text-align:center;padding:6px 11px;border:1px solid var(--card-line);
  border-radius:9px;font-size:13.5px;color:var(--ink-2);background:var(--card)}
.pager .pg:hover{border-color:var(--brand);color:var(--brand-dark)}
.pager .pg.on{background:var(--brand);border-color:var(--brand);color:#fff;font-weight:600}
.pager .pg-count{margin-left:auto;color:var(--ink-3);font-size:12.5px}

/* ---------- straight bars, and 认证等级 as a card ---------- */
/* A thick vertical rule, square at both ends. `border-left` could not do this: it follows
   the border radius, so the bar bowed into the rounded corners and stopped reading as a
   rule. The bar is a pseudo-element pinned to the left edge for the card's full height. */
.ladder li{position:relative;border-left:0;padding-left:18px;overflow:hidden}
.ladder li::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:4px;
  background:var(--bar,var(--card-line))}
.ladder li.yes.lv-l0{--bar:#25606b}
.ladder li.yes.lv-l1{--bar:#1f6b3f}
.ladder li.yes.lv-l2{--bar:#0b6b52}
.ladder li.yes.lv-l3{--bar:#0a5b46}
.ladder li.partial{--bar:var(--warn)}
.ladder li.no{--bar:var(--line)}

/* the finding cards keep the same rule shape, coloured by severity */
.cap{position:relative;border-left:0;padding-left:0;overflow:hidden}
.cap::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:4px;
  background:var(--cap-tone,var(--card-line));z-index:1}
.deduct .ev{position:relative;border-left:0;padding-left:12px}
.deduct .ev::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--warn)}

/* 认证等级 is its own card: a bordered, shadowed box holding the heading and the rungs,
   so the ladder reads as one object rather than five loose rows under a heading. */
.lv-block{
  margin:20px 0 0;padding:16px 18px 18px;background:var(--card);
  border:1px solid var(--card-line);border-radius:13px;
  box-shadow:0 1px 2px rgba(16,24,40,.04), 0 8px 22px -16px rgba(16,24,40,.24)}
.lv-block h3{margin:0 0 14px;padding:0;border-top:0}
.lv-block .ladder{margin:0}
@media (prefers-color-scheme:dark){
  .lv-block{box-shadow:0 1px 2px rgba(0,0,0,.35), 0 10px 24px -18px rgba(0,0,0,.6)}
}
