
*,*::before,*::after{box-sizing:border-box}
:root{
  --paper:#e9ebe6; --stock:#f5f6f2; --line:#c3c8bf; --line-soft:#d8dcd4;
  --ink:#15181a; --ink-2:#4c5359; --ink-3:#646b70;
  --mark:#f2d024; --mark-soft:#f7e7a0;
  --pos:#1c5c3f; --neg:#8c3230;
  --bg:#e9ebe6; --surface:#f5f6f2;
  /* 这四个此前从未定义，而年龄门（第一屏）与证据面板（全站最强的那块）
     都在用它们：未定义的 var() 会让整条声明作废，于是遮罩里的按钮没有
     底色、说明文字继承正文色，证据面板与它嵌进去的表格同色、看不出分层。
     --surface-2 刻意不等于 --stock：面板正是插在 --stock 的表格里的，
     两者同色等于没有面板。 */
  --muted:#646b70; --card:#f5f6f2; --surface-2:#dfe3da;
  --up:#1c5c3f; --down:#8c3230;
  --sans:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --serif:ui-serif,"Iowan Old Style",Charter,"Palatino Linotype",Palatino,
          "Book Antiqua",Georgia,serif;
  --mono:ui-monospace,"Cascadia Mono","Cascadia Code",SFMono-Regular,"SF Mono",
         Consolas,"Liberation Mono",Menlo,monospace;
}
@media (prefers-color-scheme:dark){:root:not([data-theme=light]){
  --paper:#16191b; --stock:#1d2124; --line:#333a3d; --line-soft:#262b2e;
  --ink:#e6e9e6; --ink-2:#a5acae; --ink-3:#8b9397;
  --mark:#e8c520; --mark-soft:#4a3f08;
  --pos:#6fbf95; --neg:#e0837c;
  --bg:#16191b; --surface:#1d2124;
  --muted:#8b9397; --card:#1d2124; --surface-2:#262c30;
  --up:#6fbf95; --down:#e0837c;
}}
:root[data-theme=dark]{
  --paper:#16191b; --stock:#1d2124; --line:#333a3d; --line-soft:#262b2e;
  --ink:#e6e9e6; --ink-2:#a5acae; --ink-3:#8b9397;
  --mark:#e8c520; --mark-soft:#4a3f08;
  --pos:#6fbf95; --neg:#e0837c;
  --bg:#16191b; --surface:#1d2124;
  --muted:#8b9397; --card:#1d2124; --surface-2:#262c30;
  --up:#6fbf95; --down:#e0837c;
}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--paper);color:var(--ink);
     font:16px/1.62 var(--serif)}

/* 版心刻意不居中：左侧留出 160px 的「基数栏」，小标题的样本量挂在那里。
   这是整站唯一的非对称，也是识别点。窄屏下栏位塌回标题上方。 */
.wrap{max-width:1080px;margin:0 auto;padding:0 22px}
@media(min-width:1024px){.wrap{padding-left:186px}}

/* 链接不用颜色区分，用荧光笔下划线 —— 「这是标出来的」而不是「这是蓝色的」。
   下划线本身就是非颜色信号，色觉障碍下同样成立。 */
a{color:inherit;text-decoration:underline;text-decoration-color:var(--mark);
  text-decoration-thickness:2px;text-underline-offset:3px}
a:hover{text-decoration-color:var(--ink-2)}
a:visited{color:inherit}

header{background:var(--stock);border-bottom:1px solid var(--line)}
.head-in{display:flex;align-items:baseline;gap:20px;flex-wrap:wrap;
         padding:15px 0 13px}
.brand{font:600 15px/1.3 var(--mono);letter-spacing:-.035em}
.brand span{color:var(--ink-3);font-weight:400}
nav{display:flex;gap:17px;margin-left:auto;flex-wrap:wrap;align-items:baseline}
nav a{font:500 13px/1.2 var(--sans);color:var(--ink-2);text-decoration:none;
      padding-bottom:4px}
nav a:hover{color:var(--ink)}
nav a[aria-current]{color:var(--ink);box-shadow:inset 0 -3px 0 var(--mark)}

/* 覆盖率计 —— 每一页顶部都写着这个站测到了多少、没测到多少。
   它是产品的中心主张，所以它是报头的一部分，不是首页的一个板块。 */
.gauge-wrap{background:var(--stock);border-bottom:1px solid var(--ink)}
.gauge{padding:11px 0 12px}
/* 计量条画成 SVG 而不是一个 style="width:NN%" 的 <i>。
   CSP 收紧到 style-src 'self' 之后 HTML 里的 style 属性一律被拦，
   而这个宽度是构建时才知道的一个数 —— SVG 的几何属性不是 CSS，
   不受 style-src 管辖，于是不必为这一根条子留 'unsafe-inline'。 */
.meter{display:block;width:100%;height:6px;background:var(--line-soft);
       margin-bottom:8px}
.meter rect{fill:var(--mark);transform-origin:left center;
            animation:meterin .5s cubic-bezier(.22,.72,.24,1) both}
@keyframes meterin{from{transform:scaleX(0)}to{transform:scaleX(1)}}
.gauge p{margin:0;max-width:none;font:400 12px/1.5 var(--mono);
         letter-spacing:-.015em;color:var(--ink-3)}
.gauge b{color:var(--ink);font-weight:600}

h1{font:600 27px/1.26 var(--mono);letter-spacing:-.04em;margin:34px 0 10px;
   overflow-wrap:break-word}
/* 小标题顶一条通栏墨线，左栏挂它依据的样本量。基数栏是这套设计的签名：
   每个断言旁边都写着它站在多少数据上，读者不必翻到方法页才知道。 */
h2.sec{position:relative;font:600 16px/1.4 var(--mono);letter-spacing:-.025em;
       margin:46px 0 8px;padding-top:13px;border-top:1px solid var(--ink)}
h2.sec .basis{display:block;font:400 11.5px/1.4 var(--mono);
              letter-spacing:-.01em;color:var(--ink-3);margin-bottom:4px}
@media(min-width:1024px){
  h2.sec .basis{position:absolute;left:-164px;top:13px;width:132px;
                text-align:right;margin:0}
}
main p{max-width:68ch}
.lede{font:400 17px/1.55 var(--serif);color:var(--ink-2);margin:0 0 26px}
.sub{font:400 15px/1.58 var(--serif);color:var(--ink-2);margin:0 0 14px}

.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(132px,1fr));
       border-top:1px solid var(--ink);border-bottom:1px solid var(--line);
       margin:24px 0 10px}
.stat{padding:12px 16px 13px;border-left:1px solid var(--line-soft)}
.stat:first-child{border-left:0;padding-left:0}
.stat b{display:block;font:600 25px/1.15 var(--mono);letter-spacing:-.04em;
        font-variant-numeric:tabular-nums}
/* 子选择器，不是后代选择器。这条规则是给卡片下面那行小标签的，而写成
   `.stat span` 会连 <b> 里那个证据触发 <span> 一起吃掉 —— 品牌页顶部
   「4,946 mentions」的 4,946 因此被渲染成 10px 全大写，比它的标签还小。 */
.stat>span{display:block;margin-top:3px;font:600 10px/1.4 var(--sans);
           letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3)}

.card{background:var(--stock);border:1px solid var(--line);margin:14px 0}
table{width:100%;border-collapse:collapse;background:var(--stock);
      font:400 13.5px/1.5 var(--sans)}
th{text-align:left;font:600 10px/1.3 var(--sans);letter-spacing:.09em;
   text-transform:uppercase;color:var(--ink-3);padding:10px 13px;
   border-bottom:1px solid var(--ink);white-space:nowrap;
   background:var(--stock);position:sticky;top:0}
td{padding:8px 13px;border-bottom:1px solid var(--line-soft);
   vertical-align:baseline}
tbody tr:last-child td{border-bottom:0}
tbody tr:hover{background:var(--paper)}
tbody tr:hover td:first-child{box-shadow:inset 3px 0 0 var(--mark)}
.num{text-align:right;font-family:var(--mono);font-size:12.5px;
     font-variant-numeric:tabular-nums}
.rk{font:400 11px/1.4 var(--mono);color:var(--ink-3);width:38px;
    font-variant-numeric:tabular-nums}
.nm{font-weight:600}
/* 123 行每行一条黄下划线会变成一堵墙，所以表内链接的荧光笔留给悬停/聚焦。
   但下划线不能整条抹成透明 —— 123 行里只有 20 行有详情页，抹掉之后
   可点的和不可点的字重、字色完全一样，用户只能拿鼠标一行行扫过去才知道
   哪个能点。改成一条 1px 的灰线：安静到不成墙，但始终在，
   悬停/聚焦时才升级成 2px 荧光笔。 */
.nm a{text-decoration-color:var(--ink-3);text-decoration-thickness:1px}
.nm a:hover,.nm a:focus-visible{text-decoration-color:var(--mark);
  text-decoration-thickness:2px}
.muted{color:var(--ink-3);font-size:12px}
p.muted{font:400 13px/1.6 var(--serif)}
.bar{display:inline-block;height:5px;background:var(--ink-2);
     vertical-align:.1em;min-width:2px}

/* 定量下限标记 —— 全站签名。数字本该在的位置画一小段横线，
   意思是「这里是有意留白，不是数据丢了」。检验报告的 <LOQ 就是这个动作。 */
.limit{color:var(--ink-3);font:400 11.5px/1.4 var(--mono);
       letter-spacing:-.01em;white-space:nowrap}
.limit::before{content:"";display:inline-block;width:15px;height:1px;
               background:currentColor;vertical-align:.32em;margin-right:8px}
.rate{font:600 14px/1 var(--mono);letter-spacing:-.03em;
      font-variant-numeric:tabular-nums}
.ci{color:var(--ink-3);font:400 11.5px/1.4 var(--mono);margin-left:7px;
    letter-spacing:-.01em;white-space:nowrap}
/* 「n 个分里 k 个 ≤6」。颜色不单独承载信息 —— 这句话自己就把数字写出来了，
   色觉障碍读的是同一句英文。 */
.ci.lo{color:var(--neg)}
.num .ci{display:block;margin-left:0}
/* 全部原始分数点。整站最重要的一行字：它让读者不必相信任何估计量。
   刻意允许换行 —— 53 个分数点就是要占三行，压缩它等于把它藏起来。 */
.pts{font:400 12.5px/1.7 var(--mono);letter-spacing:-.01em;color:var(--ink);
     overflow-wrap:anywhere}
.pts b{font-weight:600;box-shadow:inset 0 -8px 0 var(--mark-soft)}
.pts .lo{color:var(--neg);font-weight:600}
/* ≤6 分显著多于全站的标记。全库只有一个品牌命中，所以它可以张扬。 */
.flag{display:inline-block;border:1px solid var(--neg);color:var(--neg);
      font:600 11px/1.3 var(--mono);padding:2px 6px;white-space:nowrap}
h1 .flag{vertical-align:.28em;margin-left:10px}
.flag-note{border-left:3px solid var(--neg)}
/* 引文旁边作者自己打的分。这句话的全部分量来自它，所以它和引文同屏、
   同一个块，不做成 tooltip。 */
.q .qr{font:600 11.5px/1.3 var(--mono);color:var(--ink-2);margin-right:8px}
.mt-l{margin-top:32px}

/* pos/warn 的区别由实线框与虚线框承担，不由颜色承担 ——
   两者都出现在写着「Rated well」「Rated poorly」的列里，词已经说清了。 */
.pill{display:inline-block;font:600 11.5px/1 var(--mono);padding:4px 6px;
      border:1px solid var(--line);white-space:nowrap;
      font-variant-numeric:tabular-nums}
.pill.pos{border-color:var(--ink);color:var(--ink)}
.pill.warn{border-style:dashed;color:var(--ink-2)}
.pill.flat{border-color:var(--line-soft);color:var(--ink-2);font-weight:400}
/* 颜色在这里与词义重复承载（「17 positive, 2 negative」），
   色盲读者读词即可。这不是用颜色单独表达方向。 */
.up{color:var(--pos)} .down{color:var(--neg)}
.scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}
.note{background:var(--stock);border-left:3px solid var(--mark);
      padding:13px 17px;margin:18px 0;font:400 14.5px/1.6 var(--serif);
      color:var(--ink-2)}
.note b{color:var(--ink);font-weight:600}
/* 引文块。刻意不做成圆角气泡 —— 整套是发丝线的检测报告，
   引文是附在数字后面的证据材料，不是社交媒体卡片。
   左侧那道竖线区分正负：负面用墨色实线、正面用细灰线，
   **但颜色不单独承载信息** —— 顺序已经是「批评在前」，
   而日期链接和原文本身才是读者真正要看的东西。 */
.quotes{margin:14px 0 4px}
.q{margin:0 0 14px;padding:2px 0 2px 15px;border-left:2px solid var(--line)}
.q.neg{border-left-color:var(--ink-3)}
.q p{margin:0;font:400 14.5px/1.62 var(--serif);color:var(--ink)}
.q p::before{content:"\201C"} .q p::after{content:"\201D"}
.q cite{display:block;margin-top:5px;font:400 11.5px/1 var(--mono);
        font-style:normal;color:var(--ink-3)}
.q cite a{text-decoration-thickness:1px;text-underline-offset:2px}

details{margin:14px 0}
details summary{cursor:pointer;font:500 12.5px/1.4 var(--sans);padding:6px 0;
                list-style:none;user-select:none;color:var(--ink-2)}
details summary::-webkit-details-marker{display:none}
details summary::before{content:"+";display:inline-block;width:14px;
                        font-family:var(--mono);color:var(--ink-3)}
details[open] summary::before{content:"−"}
details summary:hover{color:var(--ink)}

.tools{display:flex;gap:10px;margin:14px 0;flex-wrap:wrap;align-items:center}
.tools input{flex:1;min-width:180px;padding:8px 11px;
             font:400 14px/1.4 var(--sans);border:1px solid var(--line);
             background:var(--stock);color:var(--ink)}
.chips{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.chips .cl{font:600 10px/1.4 var(--sans);letter-spacing:.09em;
           text-transform:uppercase;color:var(--ink-3);margin-right:3px}
.chip{border:1px solid var(--line);background:transparent;color:var(--ink-2);
      padding:5px 10px;font:500 12px/1.3 var(--sans);cursor:pointer}
.chip:hover{border-color:var(--ink-2);color:var(--ink)}
.chip[aria-pressed=true]{color:var(--ink);border-color:var(--ink);
                         box-shadow:inset 0 -3px 0 var(--mark)}
/* 表头下常驻的口径小字。Mentions 那行随口径切换，Reviews / Positive 永远
   写 all-time —— 同一个排版槽位里的三个标签互相对照，比任何散文都直接。 */
.th-basis{display:block;font-weight:400;text-transform:none;letter-spacing:0;
          color:var(--ink-3);font-size:10px;margin-top:2px}
/* 序号格里的全时段名次。只在窗口名次与全时段名次差得够远时才出现 ——
   123 行每行挂一个就成了噪声，而它要说的恰恰是「这一行不寻常」。 */
.rk-a{display:block;font-size:9px;color:var(--ink-3);white-space:nowrap}
/* [hidden] 的 UA 样式是 display:none，但上面那条显式 display:block 特异度
   更高，会把它盖掉 —— 属性设对了，元素照样显示。必须显式写回来。 */
.rk-a[hidden]{display:none}
.tools-w{margin-top:-4px}
th.sortable{cursor:pointer;user-select:none}
th.sortable:hover{color:var(--ink)}
th.sortable::after{content:"↕";opacity:.35;margin-left:5px;font-size:10px}
th[aria-sort=ascending]::after{content:"↑";opacity:1;color:var(--ink)}
th[aria-sort=descending]::after{content:"↓";opacity:1;color:var(--ink)}

.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(212px,1fr));
       border-top:1px solid var(--ink);margin:26px 0 8px}
.pcard{display:block;text-decoration:none;padding:15px 18px 17px;
       border-bottom:1px solid var(--line)}
.pcard:hover{background:var(--stock);box-shadow:inset 3px 0 0 var(--mark)}
@media(min-width:700px){
  .pcard{border-left:1px solid var(--line-soft)}
  .pcard:first-child{border-left:0;padding-left:0}
}
.pcard b{display:block;font:600 10px/1.4 var(--sans);letter-spacing:.09em;
         text-transform:uppercase;color:var(--ink-3)}
.pcard em{display:block;font-style:normal;font:600 24px/1.15 var(--mono);
          letter-spacing:-.04em;color:var(--ink);margin:5px 0 4px}
.pcard span{display:block;color:var(--ink-2);font:400 13px/1.55 var(--serif)}

footer{border-top:1px solid var(--ink);margin-top:60px;padding:24px 0 48px;
       color:var(--ink-3);font:400 13px/1.65 var(--sans)}

.theme{background:none;border:1px solid var(--line);color:var(--ink-2);
       cursor:pointer;font:500 11px/1 var(--sans);padding:6px 8px}
.theme:hover{border-color:var(--ink);color:var(--ink)}

/* 之前只有 .tools input 有聚焦样式，其余全靠浏览器默认 —— 在这套
   低对比纸张上默认焦点环几乎看不见。全局补齐。 */
:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
a:focus-visible{outline-offset:3px;text-decoration-color:var(--mark)}
th.sortable:focus-visible,td:focus-visible{outline-offset:-2px}

/* 只给读屏软件的标签。搜索框此前只有 placeholder —— placeholder 不是
   可访问名称，聚焦之后它还会消失。 */
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
    overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* 只在窄屏出现 / 只在宽屏出现的散文片段。图在 ≤640px 是 display:none，
   而讲图的句子（「hover 任一个点」「虚线左边的点」「灰色云团」）原样留着 ——
   手机上没有点、没有虚线、没有云，读者的结论是「图没加载出来」。
   同一件事在两种载体上要用两种说法，两种说法都写死在构建里，
   不靠谁记得同步改。 */
.w-only{display:inline}
.n-only{display:none}

/* 首页的品牌查询入口。整站唯一的用途是「站在货架前照着盒子打字」，
   所以它在首页第一屏，而不是等人自己找到 brands.html。
   没有 <form>：CSP 是 form-action 'none'，提交会被直接拦掉。JS 拼一个
   ?q= 跳过去；JS 挂了的时候旁边那条「browse all」链接仍然是活的。 */
.find{border-top:1px solid var(--ink);border-bottom:1px solid var(--line);
      padding:16px 0 17px;margin:22px 0 6px}
.find label{display:block;font:600 10px/1.4 var(--sans);letter-spacing:.09em;
            text-transform:uppercase;color:var(--ink-3);margin-bottom:7px}
.find-row{display:flex;gap:9px;flex-wrap:wrap;align-items:stretch}
.find input{flex:1 1 200px;min-width:0;padding:11px 12px;
            font:400 16px/1.4 var(--sans);border:1px solid var(--ink);
            background:var(--stock);color:var(--ink)}
.find button{padding:11px 18px;font:600 14px/1.4 var(--sans);cursor:pointer;
             border:1px solid var(--ink);background:var(--ink);color:var(--bg)}
.find button:hover{opacity:.86}
.find p{margin:9px 0 0;font:400 13px/1.55 var(--serif);color:var(--ink-2);
        max-width:68ch}

/* 宽屏没有「折叠」这回事：summary 藏掉，details 永远是展开的那一态。
   HTML 里写死 open，窄屏由脚本收起 —— 与 build_map.py 的 .mapfold 同一
   做法，没有 JS 时是展开的（次优，但不是坏的）。
   这一段在 390px 下有约 500 px 高，而它挡在搜索框和结果中间。 */
.fold{margin:0}
.fold>summary{display:none}
.fold>.lede{margin-top:14px}

/* 零结果时的说明。此前零结果的页面一个字都不说 —— 只剩一行表头，
   而最合理的读法是「这站没收录我的牌子」。这个站自认的头号缺口恰恰是
   词典漏收（about 页写着 330 条评测的品牌没被识别），那就在这里说出来，
   并给一个能寄的地址。 */
.noq{border-left:3px solid var(--mark);background:var(--stock);
     padding:14px 17px;margin:6px 0 16px;font:400 14.5px/1.6 var(--serif);
     color:var(--ink-2);max-width:68ch}
.noq[hidden]{display:none}
.noq b{color:var(--ink);font-weight:600}
.noq .noq-q{font-family:var(--mono);color:var(--ink);
            overflow-wrap:anywhere}

/* ⚑ 的解释此前挂在 title= 上 —— 触摸设备永远不触发它。全站只有一个品牌
   命中这个标记，所以把解释直接印出来的成本是一行字，而它是站点上唯一
   一个「别买」方向的信号。 */
.flag{white-space:normal}
.flag-why{display:block;margin-top:4px;font:400 11.5px/1.45 var(--sans);
          color:var(--neg);max-width:44ch}
/* 「没有分数，但有 158 条讨论，在这儿」。253 个品牌的口碑格此前到
   「样本不足」为止，而同一行写着它有几百条提及 —— 那一格没有任何东西
   指向那些讨论在哪。 */
.gotalk{display:inline-block;margin-top:5px;font:500 12px/1.4 var(--sans);
        text-decoration-color:var(--ink-3);text-decoration-thickness:1px}
.gotalk:hover,.gotalk:focus-visible{text-decoration-color:var(--mark);
        text-decoration-thickness:2px}

@media(max-width:640px){
  body{font-size:15px}
  h1{font-size:21px} h2.sec{font-size:15px;margin-top:38px}
  .lede{font-size:15.5px}
  .wrap{padding:0 14px}
  td,th{padding:7px 9px}
  .stat{padding:11px 12px}
  .hide-s{display:none}
  .w-only{display:none}
  .n-only{display:inline}

  /* 报头吸顶。390px 下 index.html 有 8,319px 高（≈10 屏）、brands.html
     15,488px（≈18 屏）—— 往下滑两屏之后，去别处的唯一入口已经不在屏上。
     覆盖率计因此被移出 <header>（它是页面内容，不是导航），
     否则吸顶的那条会有 110px 高。 */
  header{position:sticky;top:0;z-index:60}
  .head-in{padding:7px 0 6px;gap:6px 12px;align-items:center}
  .brand{font-size:13px;flex:0 0 auto}
  .brand span{display:none}
  /* 六个导航项在 390px 下一行放不下（实测 422px）。换行成两行会让吸顶条
     涨到 110px，所以改成单行横滚，右端一道渐隐说明「还有」。
     每一项 44px 高 —— 此前是 20px。 */
  nav{margin-left:auto;flex-wrap:nowrap;overflow-x:auto;gap:10px;
      align-items:stretch;-webkit-overflow-scrolling:touch;
      scrollbar-width:none;
      -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 14px),
                         transparent);
      mask-image:linear-gradient(90deg,#000 calc(100% - 14px),transparent)}
  nav::-webkit-scrollbar{display:none}
  nav a{display:flex;align-items:center;min-height:44px;padding:0;
        font-size:13px;white-space:nowrap}
  nav a[aria-current]{box-shadow:inset 0 -3px 0 var(--mark)}
  .theme{min-height:44px;padding:0 10px;white-space:nowrap;flex:0 0 auto}

  /* 触摸目标。实测改前：导航 20px、chip 27px、Theme 25px、
     表里的品牌链接 42×17px、搜索框 14px 字（iOS 聚焦会自动放大整页）。
     做法与 build_map.py 里那一段一致（那边把 "Open in maps" 从 17.5px
     修到 45.5px），但只放大这一侧，不去动那个文件。 */
  .chip{padding:13px 14px;font-size:13.5px}
  .tools input{font-size:16px;padding:12px}
  details summary{padding:12px 0;min-height:44px;display:flex;
                  align-items:center}
  .fold>summary{display:flex;font:600 13px/1.4 var(--sans);color:var(--ink);
                border-bottom:1px solid var(--line-soft)}

  /* brands 页的搜索框吸在报头下面。这一页有 299 行，卡片布局之后更长，
     而这个框是整个 30 秒任务的入口 —— 它不能只在第一屏存在。 */
  .tools.tools-stick{position:sticky;top:var(--hdr,60px);z-index:40;
    background:var(--paper);margin:0 -14px;padding:9px 14px 10px;
    border-bottom:1px solid var(--line)}

  /* 表格换成「每行一张卡」。
     实测改前：brands 表容器 clientWidth 347、内容 scrollWidth 576，
     那个大号的中位分（span.rate）落在 x=551–582，**完全在可视区外**，
     而 iOS/Android 是 overlay scrollbar，不滑根本不知道右边还有东西。
     加提示不如换布局：横滚本身在这一列上没有可辩护的理由。
     列头由 JS 从 <thead> 抄成每格的 data-lab，所以没有 JS 时这里
     一条都不生效，退回原来那张（会横滚的）表 —— 那是一个更差但完整的
     形态，不是一堆没有标签的数字。 */
  /* 右边留 4px：证据触发器（.js-ev [data-ev]）带 -3px 的负外边距，
     不留这一点整张卡就会溢出 3px，而 .card 是 overflow-x:auto ——
     换掉横滚之后又长出一条 3px 的横滚条。 */
  table.ct>tbody>tr{display:flex;flex-wrap:wrap;column-gap:14px;
    padding:10px 4px 12px 0;border-bottom:1px solid var(--line)}
  table.ct>thead{display:none}
  /* `table.ct>tbody>tr>td` 的特异度比 `.hide-s` 高，所以上面那条
     display:block 会把窄屏本该藏起来的两列（Mostly / Sustained from）
     重新显示出来 —— 卡片因此凭空多两行。显式写回去。 */
  table.ct>tbody>tr>td.hide-s{display:none}
  table.ct>tbody>tr:last-child{border-bottom:0}
  table.ct>tbody>tr:hover{background:transparent}
  table.ct>tbody>tr>td{display:block;padding:0;border:0;flex:0 0 auto;
    max-width:100%}
  table.ct>tbody>tr>td.num{text-align:left}
  /* 最后一格通常是这一行最长的那个（口碑格、分数串、「Also makes」）。
     让它自己占一行，其余的短格并排 —— 卡片因此是两三行，不是七行。 */
  table.ct>tbody>tr>td:last-child{flex:1 1 100%}
  /* 标签走 data-lab，不走 :nth-child 数格子 —— 列数各表不同，
     而 hide-s 还会在窄屏抽掉其中两列。 */
  /* 标签行内而不是独占一行：每格多一行标题，一张卡就要多出 5 行、
     299 行乘起来是几千像素的纯标签。口碑格是例外 —— 那是这张卡的
     主角，它的数字要大，标签压在上面一行。 */
  table.ct>tbody>tr>td[data-lab]::before{content:attr(data-lab) " ";
    font:600 9.5px/1.4 var(--sans);letter-spacing:.085em;
    text-transform:uppercase;color:var(--ink-3)}
  table.ct>tbody>tr>td.med[data-lab]::before{display:block}
  /* 序号 + 品牌名占第一行，其余各自成块。序号自带 # 前缀，
     不用一行「#」标签去说明一个已经写着 # 的东西。 */
  table.ct>tbody>tr>td.rk{flex:0 0 auto;width:auto;padding-top:13px}
  table.ct>tbody>tr>td.rk::before{content:"#";margin:0}
  table.ct>tbody>tr>td.nm{flex:1 1 60%;font-size:16px}
  table.ct>tbody>tr>td.nm::before{content:none}
  /* 唯一的下钻入口。改前 42×17px。 */
  table.ct>tbody>tr>td.nm a{display:inline-block;padding:12px 0;
    font-size:17px;line-height:1.25}
  /* 口碑格独占一行、并且是这张卡上最先被看见的东西。 */
  table.ct>tbody>tr>td.med{flex:1 0 100%;margin-top:4px}
  table.ct .rate{font-size:19px}
  table.ct .ci,table.ct .limit{display:block;margin-left:0}
  /* 证据面板那一行是 JS 造的 <tr><td colspan> —— 它没有 data-lab，
     也不该被排进卡片的 flex 行里。 */
  table.ct>tbody>tr.ev-row{display:block;padding:0;border-bottom:0}
  /* 卡片布局下没有列头可点。sticky 的 th 在吸顶报头下面会叠在一起，
     一并关掉。 */
  th{position:static}
  /* 「Most discussed right now」正下方那张表是这根条子的窄屏替代表的
     超集（多了名次、分数条数、中位分）。两张都画等于把同样八个品牌
     在手机上说两遍 —— 那是这一页最长的一段冗余。 */
  .w-hot .viz-narrow{display:none}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;
    animation-iteration-count:1!important;transition-duration:.001ms!important}
}

/* 变量作用域必须**同时**包含 .viz-legend：图例是 .viz 的兄弟节点而不是
   它的后代，此前 --v-1..--v-5 在那里根本没有定义，五个色块全是透明的 ——
   一张有图例的图，图例上没有颜色。这与 --muted / --card / --surface-2
   那三个未定义变量是同一类错，一起修掉。 */
.viz,.viz-legend{
  --v-surface:#e9ebe6; --v-ink:#15181a; --v-ink-2:#4c5359; --v-muted:#646b70;
  --v-grid:#c3c8bf; --v-axis:#a8aea3;
  --v-main:#2a78d6; --v-main-soft:#bcd8f7; --v-dim:#a3a99d;
  --v-1:#2a78d6; --v-2:#eb6834; --v-3:#1baf7a; --v-4:#eda100; --v-5:#e87ba4;
  position:relative;
}
/* 这里曾有 --v-good:#0ca30c / --v-bad:#d03b3b 一对涨跌色。删掉了：
   validator 实测两者 deutan ΔE 只有 4.1（下限 6.0）—— 红绿色盲看它们
   几乎是同一个颜色，而「涨还是跌」恰恰是最不能靠猜的一件事。
   深色块当时也根本没给它们覆盖值。方向一律由文字和位置承担。 */
@media (prefers-color-scheme:dark){
  :root:not([data-theme=light]) .viz,:root:not([data-theme=light]) .viz-legend{
    --v-surface:#16191b; --v-ink:#e6e9e6; --v-ink-2:#a5acae; --v-muted:#8b9397;
    --v-grid:#333a3d; --v-axis:#454c50;
    --v-main:#3987e5; --v-main-soft:#184f95; --v-dim:#565a52;
    --v-1:#3987e5; --v-2:#d95926; --v-3:#199e70; --v-4:#c98500; --v-5:#d55181;
  }
}
:root[data-theme=dark] .viz,:root[data-theme=dark] .viz-legend{
  --v-surface:#16191b; --v-ink:#e6e9e6; --v-ink-2:#a5acae; --v-muted:#8b9397;
  --v-grid:#333a3d; --v-axis:#454c50;
  --v-main:#3987e5; --v-main-soft:#184f95; --v-dim:#565a52;
  --v-1:#3987e5; --v-2:#d95926; --v-3:#199e70; --v-4:#c98500; --v-5:#d55181;
}
.viz svg{display:block;width:100%;height:auto;overflow:visible}
/* 窄屏换承载形式。viewBox 720 配 width:100%，390px 视口下缩放比 0.503 —— */
/* 图里 11 单位的字实际是 5.5 CSS px。放大字号撑破 viewBox，横滚会漏看。 */
.viz-narrow{display:none}
/* 散点图的替代表在宽屏也留着：直接标签碰撞就不画，没画到的名字要有地方读。 */
.viz-narrow.always{display:block;margin:14px 0 4px}
@media(max-width:640px){
  .viz.has-narrow{display:none}
  .viz-narrow{display:block;margin:14px 0 4px}
  /* 堆叠条与 sparkline 不配替代表（正下方本来就跟着表），
     但它们的刻度字同样缩到 5.5px —— 藏掉刻度保留形状，
     具体数字去下面那张表里读。 */
  .viz .tk,.viz .lb{display:none}
  .viz .dl{font-size:15px}
}
/* SVG 里的 text 会继承 body 的字族。正文是衬线，图表标签用衬线会很怪，
   所以在图表根上显式声明：名字用无衬线，数值用等宽。 */
.viz svg text{font-family:var(--sans)}
.viz .gl{stroke:var(--v-grid);stroke-width:1}
.viz .ax{stroke:var(--v-axis);stroke-width:1}
.viz .tk{fill:var(--v-muted);font:11px var(--mono);
         font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.viz .lb{fill:var(--v-ink-2);font-size:11px}
.viz .dl{fill:var(--v-ink);font-size:11px;font-weight:600}
.viz .dot{stroke:var(--v-surface);stroke-width:2;cursor:default}
.viz .dot.dim{fill:var(--v-dim)}
.viz .dot.hi{fill:var(--v-main)}
.viz .dot:hover{stroke-width:3}
.viz .bar{rx:4}
/* 提示框与图例跟随站点的「送检报告」样式：直角、发丝线、等宽数字。
   圆角卡片是这一版刻意去掉的东西，图表层不能自己留一份。 */
.viz-tip{
  position:absolute;pointer-events:none;opacity:0;transition:opacity .1s;
  background:var(--surface);border:1px solid var(--ink);
  padding:8px 10px;font:400 12px/1.5 var(--mono);color:var(--ink);
  letter-spacing:-.01em;z-index:20;max-width:230px;
}
.viz-tip b{display:block;margin-bottom:2px;font-weight:600}
.viz-tip span{color:var(--ink-3)}
.viz-legend{display:flex;gap:16px;flex-wrap:wrap;margin:11px 0 0;
            font:500 11.5px/1.4 var(--sans);color:var(--ink-2)}
/* 色块用类名而不是 style 属性。CSP 收紧到 style-src 'self' 之后，
   HTML 里的 style="" 会被整体拦掉 —— 图例的色块是站点上仅有的几处
   内联样式之一，搬进这里就不必为它留 'unsafe-inline'。 */
.viz-legend i{display:inline-block;width:10px;height:10px;
              margin-right:6px;vertical-align:-1px}
.viz-legend i.sw-1{background:var(--v-1)}
.viz-legend i.sw-2{background:var(--v-2)}
.viz-legend i.sw-3{background:var(--v-3)}
.viz-legend i.sw-4{background:var(--v-4)}
.viz-legend i.sw-5{background:var(--v-5)}
/* 原始分数点的带状图。每个点画出来，不做任何摘要 —— 这条路线的核心
   主张就是读者不必相信任何估计量。半透明是为了让重叠处自己变深，
   那是这张图唯一的"密度"表达，而它是真实的重叠，不是估计。 */
.viz .pt{fill:var(--v-main);fill-opacity:.5;stroke:var(--v-surface);
         stroke-width:1.5}
.viz .pt.low{fill:var(--v-2);fill-opacity:.75}
.viz .med{stroke:var(--v-ink);stroke-width:2;stroke-linecap:round}
.viz .sep{stroke:var(--v-grid);stroke-width:1}
.viz-note{font:400 12px/1.55 var(--mono);letter-spacing:-.01em;
          color:var(--ink-3);margin:10px 0 0;max-width:74ch}
/* 窄屏替代表里的原始分数串。等宽 + 允许在任意位置断行 —— 它是这张表里
   唯一一个长到会决定列宽的单元格，用无衬线又不许断行时列宽会被它拖到
   85px 以外去挤掉中位分那一列（实测就是这么发生的）。 */
.viz-narrow td{font-family:var(--mono);letter-spacing:-.01em;
               overflow-wrap:anywhere}
.viz-narrow td b.lo{color:var(--neg);font-weight:600}
.viz-narrow .pts-cut{color:var(--ink-3);white-space:nowrap}

.js-ev [data-ev]{border-bottom:1px dotted var(--ink-3);cursor:pointer;
  display:inline-block;padding:6px 3px;margin:-6px -3px}
.js-ev [data-ev]:hover,.js-ev [data-ev]:focus-visible{border-bottom:2px solid var(--mark)}
.js-ev [data-ev][aria-expanded=true]{border-bottom:2px solid var(--mark)}
.ev-row>td{background:var(--surface-2);padding:0!important}
.ev-panel{padding:14px 16px;border-left:2px solid var(--mark);
  background:var(--surface-2);margin:6px 0 14px;max-width:100%}
/* 最后一道保险：万一面板还是落进了某个 grid/flex 容器（页面加了新的卡片
   排版而 panelFor 的爬升没料到），也让它横跨整行，而不是缩成一列。 */
.ev-panel,.ev-li{grid-column:1/-1;width:100%;flex-basis:100%}
.ev-li{list-style:none;margin:0;padding:0}
.ev-row .ev-panel{margin:0;border-left-width:3px}
.ev-head{font:600 13px/1.5 var(--sans);margin:0 0 2px}
.ev-sub{font:400 12px/1.6 var(--sans);color:var(--ink-2);margin:0 0 10px}
.ev-status{font:400 12px/1.6 var(--sans);color:var(--ink-3);margin:8px 0 0}
.ev-list{list-style:none;margin:0;padding:0;font:400 12px/1.7 var(--mono);
  max-height:22em;overflow-y:auto}
/* 换行而不是横向滚动。overflow-y:auto 会把 overflow-x 从 visible 提升成
   auto，所以任何一点水平溢出都会长出一条横向滚动条，把 open 链接推到屏外。
   flex 项的默认 min-width:auto 正是那个溢出源 —— 三个项都不肯缩到
   min-content 以下。 */
.ev-list li{display:flex;flex-wrap:wrap;gap:2px 10px;padding:3px 0;
  border-top:1px solid var(--line)}
.ev-list li:first-child{border-top:0}
.ev-list li>a{flex:0 0 auto}
.ev-when{color:var(--ink-3);flex:0 0 auto;min-width:6.5em}
.ev-what{flex:1 1 8em;min-width:0;color:var(--ink-2);overflow-wrap:anywhere}
.ev-more{font:400 12px/1.6 var(--sans);margin:8px 0 0}
.ev-p{color:var(--up)}.ev-n{color:var(--down)}
.ev-src{color:var(--ink-3)}.ev-none{color:var(--ink-3);font-style:italic}
@media(max-width:640px){
  .ev-list{font-size:11px}.ev-list li{gap:2px 8px}
  .ev-when{min-width:0}
}

.agegate{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;
  justify-content:center;padding:24px;background:var(--bg);}
.age-ok .agegate{display:none}
html:not(.age-ok) body{overflow:hidden}
.agegate-box{max-width:30rem;text-align:center}
.agegate-box h1{font-size:1.5rem;margin:0 0 .75rem}
.agegate-box p{margin:0 0 1rem;line-height:1.5}
.agegate-btns{display:flex;gap:.75rem;justify-content:center;flex-wrap:wrap}
.agegate-btns button{font:inherit;padding:.6rem 1.4rem;cursor:pointer;
  border:1px solid var(--line);border-radius:6px;background:var(--card);
  color:inherit}
.agegate-btns button.primary{background:var(--ink);color:var(--bg);
  border-color:var(--ink)}
.agegate-fine{font-size:.82rem;color:var(--muted);margin-top:1rem}
