/* ============================================================================
   f.css —— 北极光首页「首屏」新皮肤（照 fatfish.ca 第一屏：白底黑字 / 三行大字 /
   描边水印 / 贴纸槽）。只在 index.html 生效，靠 <html class="fh"> 开关。

   为什么白底只做首屏一块图层、不动 html,body 的全局深色底：
     第二屏 page2.js 的内容流卡片是 color:#fff + rgba(255,255,255,.03) 的深色系，
     把全局底改白会让第二屏白底白字彻底不可读。用户要求「只换第一屏」。
     → 深色 body 渐变原样不动，#fHero 是盖在首屏位置的一层白底，底部 140px 渐隐，
       接上第二屏现成的 .SectionGridCarousel::before 30vh 深色顶渐变。

   为什么挂 <html> 层而不是 body：
     index.html 是 Next.js 静态导出 + 客户端水合，React 水合会清掉 <body> 内的非
     React 节点（apeTop.js / intro.js 都是被这件事逼到 <html> 层 / 自愈轮询的）。
     f-home.js 在 defer 阶段直接 documentElement.appendChild，不用等水合、无空白窗口。

   数字全部换算成 px，禁止用 rem：fatfish 根字号 14px、本站 16px，
   照抄它的 6.2222rem / 1.6rem 会整体放大 14%。原始值见注释里的 "≡"。
   ========================================================================== */

/* ---------- 1. 字体（自托管；Archivo=SIL OFL 1.1，Roboto Slab=Apache-2.0） ---------- */
/* 大字重体：Archivo 变量字体（wght 100–900 + wdth 62–125），替代 fatfish 那个商业字体
   IntegralCF。有宽度轴，'wght' 900 + 'wdth' 100~112 最接近它的 Heavy 宽几何黑体。 */
@font-face{
  font-family:"ArchivoVar";
  src:url('/fonts/archivo/ArchivoVar-latin.woff2') format('woff2');
  font-weight:100 900; font-stretch:62% 125%; font-style:normal; font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
/* 导航/正文：fatfish 用的就是 Roboto Slab（实测 "Roboto Slab", arial, helvetica） */
@font-face{
  font-family:"Roboto Slab";
  src:url('/fonts/robotoslab/RobotoSlab-latin.woff2') format('woff2');
  font-weight:100 900; font-style:normal; font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

/* 首屏三行大字的**汉字字体** = 方正牛黑简 EB（FZNiuHJW-EB.ttf，用户指定，
   2026-09-12，文件在仓库根目录、线上 HTTP 200，拉丁字母 A-Z/0-9 齐备）。

   为什么要单独起个家族名: 各页 inline <style> 里那份 `"FZNiuHJW-EB"` 没写
   font-weight → 默认只有 normal(400)，而这行要大字重 → 浏览器会**合成假粗体**
   （边缘发糊）。这里同一个文件再声明一次、把字重范围写成 100–900，
   命中真实字形、不做合成。URL 相同，浏览器只下一次（走缓存）。
   只作用于 .expertises-marquees__expertise-link，不影响其它页用 FZ 的地方。 */
@font-face{
  font-family:"FZHero";
  src:url('/FZNiuHJW-EB.ttf') format('truetype');
  font-weight:100 900; font-style:normal; font-display:swap;
}

/* 首屏第 2 行「游戏攻略」的**红色毛笔体** = 马善政毛笔楷书（2026-09-13 用户给了一张实拍海报
   "REVERSE BUNNY GIRL"，三行是「黑体 / 红毛笔 / 黑体」，用户选定「中间改红色毛笔体」）。
   SIL OFL 1.1，免费可商用 —— 来源、选型理由、复现步骤见 fonts/mashanzheng/SOURCE.txt。
       ⚠ 用户字体夹 F:\字体\ 里那 16 个**全是方正商用字体**，别从那儿挑。
   ⚠ **只有 400 一档**（毛笔楷书没有第二字重）→ 用它的规则必须显式写 font-weight:400，
     否则继承到大字那套 900 会触发浏览器**合成假粗体**，毛笔的飞白会被抹成一坨。
   ⚠ 子集只到 GB2312 一级（3891 字，与站酷快乐体同口径）→ 改文案若用了生僻字会**静默**
     回落成黑体，同一行里混出两种字体。换字必须重跑 subset，见 SOURCE.txt 第 3 步。 */
@font-face{
  font-family:"MaShanZheng";
  src:url('/fonts/mashanzheng/MaShanZheng-GB2312L1.woff2') format('woff2');
  font-weight:400; font-style:normal; font-display:swap;
}

/* ---------- 2. 首屏变量（≡ 后面的数字是 fatfish **16px** 根字号下的原值） ----------
   ⚠ 2026-09-12 修：本文件原来整段把 rem 按 **14px** 折算（--fh-fs:87.11px 等），
   整块比原站小 12.5%、横向缩进也跟着错位。实测 fatfish `<html>` 的 computed
   font-size = 16px（浏览器默认，它 CSS 里根本没设），它的 `h1{font-size:6.2222rem}`
   → 99.5556px。换算一律 N×16。 */
html.fh{
  --fh-pad:40px;              /* ≡ --wrapper_margin (2.5rem@16) */
  --fh-pad-top:128px;         /* ≡ .home-intro{padding:--section_v_padding_sm 0} (8rem@16；原站实测 128px) */
  /* 顶栏高度，须与 apeTop.js 里 html.fh #apeTop 的 height 一致。
     2026-09-11 用户要「顶栏的字和 logo 都大 30px」：logo 87→117、导航 12.44→42.44、
     胶囊 14→44，于是顶栏按 logo(117) + 上下各 22.5px 留白 = 162px。
     **写成 vw 等比**：42px 的导航 + 117px 的 logo 在 1024 宽的笔记本上会顶出屏
     （四个中文菜单项本身就 387px 宽），所以每一条都用 clamp 按视口缩放，
     1440 及以上才是用户要的满值。 */
  --fh-top:clamp(92px,11.25vw,162px);   /* 1440→162 / 1024→115 */
  --fh-handoff:140px;         /* 白 → 深色 的交接渐隐长度 */
  /* ≡ --grid-gutter:1.5rem。12 栏栅格：左右各 40px 外边距，栏间 24px 沟槽。
     --fh-col 是**真·栏宽** = (100vw - 2*40 - 11*24)/12（1440 宽 → 91.33px）。
     别写成 (100vw-80)/12：那是"含沟槽的平均宽"，会让描述块缩进偏大。 */
  --fh-gutter:24px;
  --fh-col:calc((100vw - var(--fh-pad) * 2 - 11 * var(--fh-gutter)) / 12);
  /* 首屏内容区左右内边距 = **wrapper 的左边缘**（原站是 10 栏盒子居中：宽度 = -g + (g+col)*10，
     1440 → 1129.33，左右各 155.33px；原站实测左缘 155）。
     ⚠ 别再写成 "外边距 + 半栅格" —— 那是按 14px 根字号凑出来的经验式，16px 下差 5px。 */
  --fh-inset:calc((100vw - (var(--fh-col) * 10 + var(--fh-gutter) * 9)) / 2);
  --fh-shift:80px;            /* ≡ .home-intro__wrapper{margin-top:5rem}：整块从正中再往下压一点 */
  --fh-desc-gap:48px;         /* ≡ .home-intro__descriptions{margin-top:3rem}（原站实测 48px）
                                 ⚠ 2026-09-13 晚「描述往下两行」**没有**改这个值 —— 见 §6 里
                                   `.home-intro__descriptions` 的 `top:`，原因写在那边。 */
  --fh-fs:99.56px;            /* ≡ 6.2222rem@16（原站实测 99.5556px；**不是 87.11** —— 那是按 14px 根字号算的，小 12.5%） */
  /* ⚠ 下面两个是**有意偏离原站**的（用户 2026-09-12：「好友系统游戏攻略休闲桌宠往中间聚一下，
     现在分的太远了，文字再大一点」）。要回原站口径：--fh-fs-k 设 1、--fh-gather 设 0。
       --fh-fs-k  字号放大系数：原站值 × 它（1.18 → 1440 下 99.56 → 117.5px）
       --fh-gather 三行整体内收：行1 左缘右移、行3 右缘左移同样的距离。
                   挂在 --fh-fs 上 → 逐档自动跟着缩，不用每档重给。
                   窄屏（≤960px）归零，见 m.css —— 手机上三行本来就快占满了。
                   0.9 是 09-12 上一轮的值；用户同日又说「三个文字**再**往中间聚一下」→ 抬到 1.35
                   （1440 下内收量 105.7 → 158.6px，行1/行3 各再进 53px）。
                   ⚠ 再往上抬要同步看 .…-link 的 font-size min()：那个兜底原来除以 5，
                     新内收会把 1024 档挤到"字号被迫缩小"，所以这里同时把除数改成 4
                     （我们最长的行就是 4 个汉字，5 是按原站法文词长留的余量）。 */
  --fh-fs-k:1.18;
  /* 第 2 行「游戏攻略」单独的再放大系数。
     2026-09-13 上午用户：「游戏攻略这几个字放大一点」→ 1.3；
     2026-09-13 下午用户给了实拍海报，要求「大小…也一样」—— 海报三行是**等大**的
     （近等高的堆叠字标，不是大字压小字），于是回到 **1**。
     2026-09-13 第五轮用户又拉开：「**游戏攻略大一点，好友系统休闲桌宠小一点**」
     → 1.15 → **1.32**，同时给第 1/3 行加了 `--fh-fs-k13`。
     只作用于 nth-child(2)，见 §5 那条覆盖规则。 */
  --fh-fs-k2:1.32;
  /* 第 1/3 行（好友系统 / 休闲桌宠）的**收小**系数（2026-09-13 第五轮，见上面那条）。
     ⚠ 与 k2 **不是**互斥关系：第 2 行不吃这一条，第 1/3 行不吃 k2，两档各管各的。
     ⚠ 两处 min() 都要乘（基准那截 + "装不下就让路"那截）—— 只乘一处的话，
       1024 档那种"兜底已经比基准小"的场景会把收小整个吃掉、还不报错（k2 踩过同一个坑）。 */
  --fh-fs-k13:0.92;
  --fh-gather:calc(var(--fh-fs) * var(--fh-fs-k) * 1.35);
  --fh-stroke:#f2f2f0;        /* 水印描边色（白底上对比度≈1.09:1，fatfish 就是这种"几乎看不见"） */
  --fh-word-gap:25.6px;       /* ≡ 1.6rem@16（原站 ::after 的 margin-left 实测 25.6px） */
  --fh-icon:104px;            /* ≡ 6.5rem@16（原站图标槽实测 104px） */
  --fh-txt:#222;              /* ≡ --color_text */
  /* fatfish 的两个品牌色（2026-09-11 从它的 :root 变量里挖出来的，全站配色就这两个）：
       --fh-red  ≡ --text_color_hover / --header_underline_color / --plyr-color-main / 按钮本底色
       --fh-gold ≡ --tag_color（行侧小字）+ 按钮悬停滑入的那层 */
  --fh-red:#cb413f;
  --fh-gold:#cab99a;
  /* ≡ .expertises-marquees{--link_trans}。悬停时水印"不直接变"就是靠它：0.6s Expo-out 填色 */
  --fh-link-trans:0.6s cubic-bezier(.19,1,.22,1);
  /* 补字重：IntegralCF Heavy 是商业字体（见文件头法务边界），Archivo 900 笔画偏细，
     加一丝描边把观感拉近。设 0 即关掉。 */
  --fh-fat:1.6px;             /* ≡ u-text-stroke 的 .1rem@16；描边色透明，仅对齐原站 */
  --fh-display:"ArchivoVar","Archivo Black","Anton","Noto Sans SC","PingFang SC","Microsoft YaHei",sans-serif;
}

/* ---------- 3. 旧 React 首屏：保留标记（水合要校验）+ 全部子节点隐藏 ---------- */
/* 只隐藏子节点、不隐藏容器：容器仍占满 min-h-svh，第二屏起始 y 坐标与改版前逐像素相同 */
html.fh .SectionHomepageHero > *{display:none!important}

/* 必须中和：同段前面那条 .SectionHomepageHero::before{content:none} 是普通声明，
   而这条 ::after 带 !important（height:40vh、z-index:5 的深色底），它是活的，
   会盖在 #fHero 白底上。 */
html.fh .SectionHomepageHero::after{content:none!important;display:none!important}
html.fh .SectionHomepageHero::before{content:none!important;display:none!important}

/* ---------- 4. #fHero 容器（≡ fatfish .home-intro） ---------- */
#fHero{
  position:absolute; top:0; left:0; right:0; z-index:2;
  height:100svh; min-height:100vh;
  overflow:hidden;            /* ≡ .home-intro{overflow:hidden}：水印 nowrap 无限延伸，就地裁掉 */
  pointer-events:none;        /* 让空白处可穿透；大字 / 贴纸各自开回 auto */
  background:linear-gradient(180deg,
    #fff 0,
    #fff calc(100% - var(--fh-handoff)),
    rgba(255,255,255,0) 100%);
}
/* ≡ .home-intro{display:flex;align-items:center;padding:var(--section_v_padding_sm) 0}
     + .home-intro__wrapper.wrapper{margin:0 auto; width:wrapper_w - col*2; margin-top:5rem}
   水平那份内边距 = --wrapper_side_space = col + margin，与 .wrapper 居中后的左边缘同一数值。
   （2026-09-11 用户指出"文案位置和原站不一样"：原先是顶部对齐 + 描述贴底，两处都不对。） */
/* ---------- 4a. 背景水印层（2026-09-13） ----------
   用户：「把背后的白色英文改成铺满首页背景，三行，放大就行」。
   三行白色描边英文（FRIENDS / GUIDES / DESKTOP PET）。每行是同一个词重复 60 遍 + nowrap
   → 必然横向溢出，由 #fHero 的 overflow:hidden 裁掉 = 通栏铺满，不需要量宽度。
   字号挂 svh 不挂 vw：这是"铺满背景"不是"跟着宽度等比缩"，按屏高定、每行占 1/2.45 屏高，
   三行叠起来略高于一屏（上下各切掉一点），看起来才是铺满而不是三行飘在中间。
   旧的两条水印带（.…-link::before/::after）见 §5 —— 已 content:none 关掉，别两套并存。 */
#fHero .fh-wm{
  position:absolute; inset:0; z-index:0; overflow:hidden; pointer-events:none;
  /* space-between 而不是 center：字号调小后（见下，2.45→3.6）三行叠起来不足一屏，
     center 会在上下各留一条空白，"铺满"就没了；space-between 把三行钉在
     屏顶 / 正中 / 屏底，字号小了也照样铺满。 */
  display:flex; flex-direction:column; justify-content:space-between;
}
#fHero .fh-wm__line{
  display:block; white-space:nowrap;
  font-family:"FZHero",var(--fh-display); font-weight:900;
  /* 2026-09-13 用户「背景白色文字再小一点」：2.45 → 3.6（1440×805 下 328px → 224px）。 */
  font-size:calc(100svh / 3.6); line-height:1;
  letter-spacing:.02em; text-transform:uppercase;
  color:transparent;
  -webkit-text-stroke:2px var(--fh-stroke);   /* ≡ 旧水印带的 1.6px，放大后跟着加粗一档 */
}
/* ---------- 4b. 掠过水印的反光带（2026-09-14 ~ 2026-09-15，**已下线，这里没有活规则**） ----------
   ⚠⚠ 2026-09-15 用户第九次定档之后改口：**「还是去掉吧」** ⇒ 这道扫带**整个删掉了**。
      `.fh-glare` / `.fh-glare__shade` / `@keyframes fh-glare` 三条规则 + `f-home.js` buildHero() 里
      `var glare = el('div','fh-glare')` 那 4 行一起撤。**这一段现在只剩这条墓志铭，没有任何生效的 CSS。**
   ⚠ 它前后定过九次档（红直带 → 显影字 → 同心弧 → 无色灰带 → 105deg 直带 → 删除），
      几何推导（`mask-size:300%` 下 `mask-position` 百分比不是屏宽百分比、带子核心 P∈[25,75]、
      105deg 的斜带上端偏右是 "/" 不是 "\"）与"白底上做不成无色的提亮"那条结论，
      全在 memory `aurora-home-fhero-fatfish.md` 与 `css-animation-freeze-must-rebuild.md` 里。
   ⚠ 要复活：同目录 `_retired/glare/` 里有 `f.css.4b.txt`（本节原文）、`f-home.js.glare.txt`
      （那 4 行原文）、`_bandprobe.js` / `_bandshot.js` / `_glareprobe.js`（三份探针，逐字可用），
      外加这个目录的 `README.md`。**照抄回去即可，不需要重新推导。**
   🔴 两条**跟这道带子无关、仍然照旧成立**的教训，别因为这里删空了就一起忘掉：
     · **本文件里不许给任何动效加 `prefers-reduced-motion` 闩** —— 站长的 Windows 关着
       「动画效果」，他的 Chrome **恒报 reduce**，加闩等于在他那儿把这个功能删了（已栽两次，
       见下面 §4c 与 `skin.css:845` / `home-fx.js:34` 的并列记录）。
     · `.fh-tint` 那团红**仍然在**（它管鼠标跟随，本来就不归这里管）：
       当年它跟这层洗层是**故意不一致**的（洗层无色、光晕有红），别看到配色不齐就"顺手对齐"。 */

/* ---------- 4c. 鼠标跟随光晕（2026-09-13） ----------
   来历：用户看到 https://wonderboy.studio/ 「首页背后的特效，鼠标滑过的时候有颜色」。
   第一版照着做全了（枕头格底纹 + 光标处泛色），2026-09-13 第三轮用户改口：
   「**背景的格子可以去掉，只保留跟随鼠标的光晕**」。→ 现在只剩这一层。

   ⚠⚠ 这段历史别丢，否则后人会再走一遍（两轮都实测否掉，**结论都反直觉**）：
     · `color` 混合（理论最优解）：取上层色相/饱和度 + 下层**明度**，明度 1 的白混完仍是白 →
       理论上"凹槽深红、平面淡红、凸边纯白"完美。但**明度越高能承载的饱和度越低**
       （HSL 在 L=.96 时哪怕 S=100% 也只到 #FFECEB）：底纹一白，色就没了。
       实测两版（平面 #fbfbfa / 凹槽 #d9d9d6）红团都**肉眼看不见**。要让 `color` 出色
       得把底纹压到中灰（L≈.5），那首屏就不是白底了。**弃用。**
     · `multiply` + 只 mask 凹槽：凹槽红得准，但平面还是灰的，整块像"一摊红网格"贴上去的
       瑕疵，不像"这一片染上了颜色"。**弃用。**
     · **`multiply` 铺满整团**（第二轮的最终形态，配枕头格才有立体感）。
   ✅ 现在没有底纹了 → `multiply` 底下就是 `#fHero` 自己的白色渐变，**乘白 = 原色**，
      等于一层普通的 alpha 光晕。留着 multiply 是为了"底纹哪天回来也不用改这里"。

   ⚠ `.fh-tint` 是 `#fHero` 的**直接子节点**（第二轮曾包一层 `.fh-bg` 来同时装底纹和光晕，
   并在那一层统一做底部收口；底纹删了之后这层包装没用了，已撤）。底部收口现在写在它自己身上：
   `#fHero` 有一层 `linear-gradient(180deg,#fff … transparent)` 做"交接给第二屏"，
   光晕到那儿要跟着淡出，否则首屏底缘会留一圈红。
   ⚠ `#fHero` 是 `position:absolute; z-index:2`，本身就是层叠上下文；
     `--qb-x/--qb-y` 由 f-home.js 的一次性 window pointermove 写在 <html> 上
     （`#fHero` 是 pointer-events:none，挂在它身上收不到空白处的移动）。 */
#fHero .fh-tint{
  position:absolute; inset:0; z-index:0; pointer-events:none;
  -webkit-mask-image:linear-gradient(180deg,#000 calc(100% - var(--fh-handoff)),transparent 100%);
          mask-image:linear-gradient(180deg,#000 calc(100% - var(--fh-handoff)),transparent 100%);
  /* 默认圆心放在屏幕中心偏上（还没动过鼠标时看到的是一团柔和的中心光）。
     ⚠ 半径用 `circle 460px`，**不能用百分比** —— 百分比在 radial-gradient 里是相对
       渐变盒的对角线算的，宽屏下会大得离谱。
     ⚠ 三段 stop 是**柔度**旋钮：第一段越短、第二段越靠中，中间那圈"硬边"越明显
       （试过 0/38/74 那版，会被切出一个能看出来的方形色块边界）。
       没有底纹打散之后整团是连续的红，所以中心从 .72 收到 **.60** —— 不然中间是一块实心红饼，
       不像"光晕"（用户要的就是"跟随鼠标的光晕"）。
     ⚠ 半径走 `--qb-r` 而不是写死：搬到 390 的手机上**整个首屏都被染红**
       （半径比屏宽还大，实拍过）。窄屏在下面 @media 里收到更小一档。
       2026-09-13 第五轮用户：「背景光晕**小一点**」→ 520 → **360**（窄屏 260 → 180）。
       只动半径不动那三段 stop：stop 是"柔度"，缩半径之后如果也把 stop 收紧，
       就会变回中间一块硬边色的饼，反而不像光晕。 */
  background:radial-gradient(circle var(--qb-r,360px) at var(--qb-x,50%) var(--qb-y,42%),
    rgba(203,65,63,.60) 0%, rgba(203,65,63,.40) 34%, rgba(203,65,63,.18) 58%, rgba(203,65,63,0) 82%);
  /* 第三轮起**不再用 mix-blend-mode**：底纹在的时候 multiply 是有意义的（凹槽比平面红），
     底纹一撤，底下只剩 #fHero 那层白渐变 —— 乘白等于原色，multiply 就是白写一句。
     而且 #fHero 的底缘那 `--fh-handoff` 一条是**全透明**的（留给第二屏），
     multiply 在非不透明底上会**穿过去和第二屏的画面相乘**，这片红光就会在第二屏顶边留一道脏。
     纯 alpha 光晕没有这个问题：底透明就是透明。 */
}

#fHero .fh-band{
  position:absolute; inset:0; z-index:1;
  display:flex; flex-direction:column; justify-content:center;
  padding:var(--fh-pad-top) var(--fh-inset);
  box-sizing:border-box;
}
.fh-stack{margin-top:var(--fh-shift)}   /* ≡ .home-intro__wrapper{margin-top:5rem} */

/* ---------- 5. 三行大字 + 描边水印（.expertises-marquees 原样复刻） ---------- */
.expertises-marquees{
  /* 三行整体内收（用户 2026-09-12 要「往中间聚」）。加在容器上 = 行1 左缘右移、行3 右缘
     左移同样的距离，行2 的居中基准也跟着收，三行一起变窄。
     ⚠ **只加在这里，别加在 .fh-stack 上** —— 描述块是 .fh-stack 的另一个子节点，它的
     margin-left 是按 fatfish 栅格写死的 calc(100vw…)，跟着位移会顶出右边界。 */
  padding:0 var(--fh-gather);
  box-sizing:border-box;
  --word_spacing:var(--fh-word-gap);   /* ≡ 1.6rem@16 = 25.6px（窄屏档逐档重给，见 §6.5） */
  --icon_size:var(--fh-icon);          /* ≡ 6.5rem@16 = 104px（同上） */
  --stroke_color:var(--fh-stroke);
  --link_trans:var(--fh-link-trans);
}
.expertises-marquees__expertise{display:flex}
.expertises-marquees__expertise:nth-child(3n+2){justify-content:center}
.expertises-marquees__expertise:nth-child(3n+3){justify-content:flex-end}
.expertises-marquees__expertise-title-ctn{display:flex;align-items:center}
/* 原站实测（1440）：第2行 title-ctn margin-left=-224px、第3行 margin-right=-48px（≡ -14rem / -3rem@16）。
   这两条是有意的出血版式，窄屏档在 m.css 里归零。
   ── 2026-09-13「三行再左右错开一点，以好友系统为锚点，好友系统不动」──
   ⚠ **只动这两条、绝对不动 `--fh-gather`**：gather 是 .expertises-marquees 的左右内边距，
     一改连**行1 的左缘**一起挪（那正是要锚住的那行）。
   ⚠ **换算是 2:1，不是 1:1**：第2行在 `justify-content:center` 下，margin-left=-M 时
     字盒左缘 = (内容宽 - 字盒宽)/2 - M/2 —— **想要往左挪 X 就得把 M 多给 2X**。
     第3行是 `flex-end`，margin-right 才是 1:1（往右挪 X 就把 M 多给 X）。
   ⚠ **别贪多**：三行整体转 -8°，横向铺开 ΔW 会把包围盒**抬高 0.139·ΔW**
     （bboxH ≈ h₀·cos8 + W·sin8），而 fitTilt() 的缩放是 min(1, availH/bboxH) ——
     实测 s 现在是 **1**、余量只有 ~19px（availH 549 / bboxH 530），
     ΔW 超过 ≈130px 就会让 s<1，**那时缩的是整个 .expertises-marquees，行1 跟着变小**，
     正好违反"好友系统不动"。加完必须复核 `_tiltprobe.js` 的 `scale` 仍是 1。 */
/* ── 09-13 最终定档：**三条只往右让，行1 当锚点** ──
   （用户看完"两边外扩"那版说「不是这样的，是以好友系统为锚点错开」，
     再选定的口径是「**都往右移**」：行1 不动，行2 右移，行3 再右移 —— 向右下的斜梯。）
   实测 1440（`_ovf.js`）：行1 **279..860 不动** / 行2 157→**417**..1071 / 行3 607→**787**..。
   `_tiltprobe.js` 的 `inline` 里 fitTilt 写回的 scale 仍是 **1**（行1 没跟着缩）。
   ⚠ 第 2 行 `margin-left` 从 -224 翻成 **+180**（正数=把居中项往右推，见上面那条 2:1 换算）。
   ⚠ `@media (max-width:1299px)` 里那两条**原样保留**（行2 -160 / 行3 -48）→
     1025~1299 与改版前逐字相同，这轮改动**只在 ≥1300 生效**。 */
.expertises-marquees__expertise:nth-child(2) .expertises-marquees__expertise-title-ctn{margin-left:180px} /* ≡ -14rem@16；09-13 斜梯：-224 → +180 */
.expertises-marquees__expertise:nth-child(3) .expertises-marquees__expertise-title-ctn{margin-right:-248px}  /* ≡ -3rem@16；09-13 斜梯：-48 → -248 */
/* 2026-09-13 用户：「休闲桌宠这几个字再往右移动半个字的距离」。
   为什么**不动上面那条 margin-right**、改成动 `.…-link` 自己的 `left`：
   ① `margin-right` 的量纲是 px，而字号的**实际像素值只有 fitLines() 运行时才知道**
      （它把 `l.style.fontSize` 写在 `.…-link` 上、可能比 CSS 的 min() 更小）——
      写死一个 px 在别的宽度就不是"半个字"了，而 `left` 的 `em` **认的正是 `.…-link`
      自己的字号**（含 letter-spacing .02em → 一个字 = 1.02em → 半个 = .51em），任何宽度都成立。
   ② `position:relative; left` 是**纯渲染位移**，不参与布局：`-title-ctn` 的宽度、
      `fitTilt()` 量到的包围盒、`fitLines()` 量到的字宽**全都不变**，
      不会再触发一轮"量→缩→再量"的连锁（那个链条 09-13 已经踩过两次）。
      行内图标是 `.…-link::after`（odd 行 `left:100%`），跟着 link 一起走 ——
      用户要挪的正是"字 + 它右边那张图"这一整组。
   ⚠ **限定 ≥1300**，与上面那条斜梯同档：≤1299 有自己的一套 margin（-48px / 0），
     那几档的版式是照原站抄的、不该被这次微调碰到。 */
@media (min-width:1300px){
  .expertises-marquees__expertise:nth-child(3) .expertises-marquees__expertise-link{left:.51em}
}

/* ---------- 5c. 倾斜版式（2026-09-13） ----------
   用户：「好友系统，游戏系统，休息桌宠和他们的图案，逆时针倾斜40°」
       → 当日下午改成：「你看这张图（实拍海报 REVERSE BUNNY GIRL），按这个图片的倾斜做」。
   海报实测倾角 ≈ **-8°**（REVERSE 基线 760px 内掉 85px → 6.4°；GIRL 那行 ≈9°），
   三行是**近等大的居中堆叠字标**，不是 40° 那版的大斜梯。
   于是这一整段从"斜梯专用"退回**只留倾斜本身** —— 40° 时为它加的三件补丁全部撤掉：
     ① 顶栏下内边距从 128px 收到"顶栏+16px"  → 撤，-8° 的包围盒只比竖排高 ~1.13 倍，128px 够
     ② 描述块脱流钉到右上角 + 覆盖 min-width:480px → 撤，-8° 下右上角**不再空**
        （行 1 右缘 ≈1020 > 描述块左缘 970，实测算过会压字）→ 描述回到流内，跟着大字走
     ③ 飞白箭头改挂文字右侧 → 撤，回到原本的"文字左侧 64px 处"
   ⚠ 只在 ≥1200px 开（站上自己那档 1199 断点，不是另造一个数）：961~1199 与 ≤960 保持
     本来的竖排版式，m.css 那几档就是按竖排调的。
   ⚠ 这是**可以整体回退的一段**：把本条 @media 删掉即回到完全竖排，其它规则一条都不依赖它。 */
@media (min-width:1200px){
  /* 行距：-8° 下斜向垂距 = 行距 × cos8 = 0.990 × 行距，几乎不缩，**理论上不需要任何间隙**
     （40° 那版需要 0.336×字号）。这里给 0.01vw ≈ 14.4px 纯粹是要海报那种**紧贴堆叠**的观感，
     不是防重叠 —— 别照 40° 那版的 0.065vw 抄，那会白撑高 200px。 */
  .expertises-marquees{display:flex;flex-direction:column;gap:calc(100vw * 0.01)}
  /* ⚠ 转在 **title-ctn** 上，不能转在 .expertises-marquees__expertise（行）上 ——
     行上有 `home-fx.js` 的 heroTick() 每帧写死的**内联** `transform:translateX(…)`
     （首屏→第二屏的左右错开 scrub），内联样式会把这行 rotate 整个盖掉、而且不报错。
     title-ctn 没有别的 transform 写入方（`.…-icon` 的悬停 scale/rotate 在更下一层）。
     转它 = 字和图标一起转，正是用户要的"和他们的图案"。 */
  .expertises-marquees__expertise-title-ctn{transform:rotate(-8deg)}
  /* 竖轴透视（2026-09-13 用户：「变成透视角度，近大远小」→ 澄清为「绕竖轴，左近右远」）。
     ⚠ **必须分两个元素写**：`perspective` 属性只作用于**子元素**，元素自己的 transform
       吃不到自己的 perspective。所以 perspective 挂在 .fh-stack、rotateY 挂在 .fh-3d。
     ⚠ `.fh-3d` 由 f-home.js 建（`.fh-stack > .fh-3d > .expertises-marquees`），
       `.home-intro__descriptions` 留在它外面 —— 否则描述文字也跟着转斜。
     ⚠ **rotateY 不能写 .expertises-marquees**：fitTilt() 每轮先 `style.transform=''` 复位、
       再把 `translateY(…) scale(…)` 写回去，写在它上面的 3D 会被整个盖掉且不报错。
     符号：`rotateY(+θ)` 时 +x 侧的 z 变负（离观察者远）→ **右侧变小**，正是"左近右远"。
     2026-09-13 第三轮后：用户「字也要近大远小，**透视角度大一点**」→ 16° 太温吞，
     改 **28° + perspective 1600→1100**（透视越近，同样的角度近端放得越大）。
     算过：半宽 440 时 z=±440·sin28°=±207，近端 ×1100/(1100-207)=1.23、远端 ×1100/(1100+207)=0.84，
     **近端比远端大 46%**（16°/1600 那版只差 26%），肉眼一眼能看出来。
     ⚠ 角度再大就要看 fitTilt() 的脸色：近端放大 = 包围盒变高，超出会逼着整块字缩小。 */
  .fh-stack{perspective:1100px; perspective-origin:50% 50%}
  .fh-3d{transform:rotateY(28deg)}
  /* 第 3 行小字（现在在「休闲桌宠」左边）的纵向位置。
     ⚠ 别再用「小字中心 vs 大字中心」去对！三行整体 -8°，外面还套着 .fh-3d 的 rotateY(28deg)+透视，
       **屏幕空间里同一行字在左端比在右端低得多** —— 行 3 的小字中心 x≈670、大字中心 x≈1065，
       光这 395px 的横移在这个透视下就值 ~90px 的高差。拿两个中心直接相减必然得出"没对齐"。
       唯一正确的基准是**小字那个 x 处、横杠这条带子自己的中心线**（用 `_bandgeo.js` 量：
       往 .fh-bar 里插 4 个 0×0 标记，测出杠的上下边两条直线，再按 x 插值）。
     ⭐ 2026-09-13 晚用户：「休闲桌宠左边的顺便聊两句没有和休闲桌宠和背后的长条在一条线上」。
       实测行 1/2 的小字离各自杠带中心只有 11~12px（这就是"看着对"的基准），
       行 3 却偏了 **80.9px**（浮在杠的上方一大截）—— 病根是上一轮为"改到左边"加的 `-92px`：
       当时描述块还在 y=641 起，小字不抬就压它头顶；后来描述块整块下移了 59.7px（见 §2 那条 `top:`），
       这 92px 就成了纯粹的错位。
       现改成 `- 11px`：正好把行 3 拉回与行 1/2 同款的"离杠带中心 ~11px"，
       同时墨迹底缘 y≈722 仍**恰好**贴着描述块顶 y=721（与当初 `-92px` 时期的余量一致，不新增碰撞）。
       ⚠ 只在 ≥1200 这一档做：961~1199 是竖排版式（没有 -8°、`.fh-bar` 也是 display:none），
         那时候描述块与小字的相对位置是另一套，硬套会把它挪到行 2 上去。 */
  .expertises-marquees__expertise:nth-child(3) .expertises-marquees__expertise-subtitle{
    margin-top:calc(.3em - 11px)}
  /* 横杠（f-home.js 里建在 .…-link 内、`<a>` 之前那个 span）。
     2026-09-13 第三轮后：用户「三条横杠的**长度超出屏幕**」→ 不再是 `inset:0`（那样只有
     字那么宽），改成以字心为基准向两侧各外扩 110vw（`calc(50% - 110vw)`）。
     **左右不对称 + 两种单位**（2026-09-13 第五轮用户：「好友系统背后横杠**远的那端没有超出屏幕**」
     → 110vw 对称那版在他屏上就是盖不住右边）。先把投影公式立好，一切从它推：
       横杠自己也吃 .fh-3d 的 rotateY。设离**透视原点**的 raw 距离为 u，投影后落在
         `screen_x = origin + u / (1 + u·sinθ/d)`，θ=28°/d=1100 → sinθ/d = 4.268e-4。
       · **远端（右）永远到不了 `d/sinθ = 2343px` 以外** —— 这个函数在 u=2343 处发散封顶。
         所以"到屏幕右边缘"需要 u/(1+u·4.268e-4) ≥ 0.61·vw（原点实测在 39% 屏宽处）：
           vw=1424 → u≥1378（≈0.97vw）   1920 → u≥2335（1.22vw）   2560 → u≥4684（1.83vw）
         需要的比例**随屏宽超线性涨**，所以只能给 vw 乘一个大系数：`250vw`
         （解 2.5vw/(1+2.5vw·4.268e-4) ≥ 0.61vw 得 vw ≤ 2903 —— **约 2900px 宽以内都盖得住**，
          4K/超宽屏会顶到 2343 的封顶而盖不住，那是这套透视的物理上限，不是参数没调够）。
       · **近端（左）反过来：raw 越往左 scale 越大，且 u>2343 就跑到相机背后**（w′<0）——
         几何会翻折出洋相。所以左边**不能用 vw 放大**，要用绝对像素钉住：`1700px`
         （离字心 1700px，离透视原点约 1726，安全落在 2343 以内）。
       · ✅ **屏幕上看得见的那一段两种写法完全一样**：可见的 raw 范围只由"投影到 0..vw"决定
         （1424 下约 [-450, +1378]），scale 只有 1.24→0.63 —— 一根**越往右越细**的楔形。
         加宽只是把**屏幕外**的几何拉长，观感不变、也不多画一个可见像素。
     ⚠ 它同时吃 .…-title-ctn 的 -8°：绕字心转 8°，远端（raw 250vw）在屏幕外高高抬起、
     落到视口外；屏幕上看到的是三条**斜穿全屏的带子** —— 这是几何必然，嫌太斜只能回头动 -8°。
     ⚠ 定位父级仍是 link（`position:relative`），所以**它照样跟着 .…-title-ctn 的 -8° 和
       .fh-3d 的 rotateY 走** —— "背后的横杠也是"（用户原话）就是白拿的，不用各写一遍。
     `z-index:-1` 让字压在它上面。⚠ **靠的是 link 上不再有 `z-index:0`**（见 §5 那条的注释）：
     负 z 会窜出非层叠上下文的父级、落到 .fh-band 的层叠上下文里 —— 于是三道杠全部沉到
     **三行字的下面**，而不是各自压在自己那行里（杠拉满屏之后，后者会让行3 的杠骑在行1/行2 的字上）。
     半透明材质：底色 12% 的红 + 32% 的红描边，底下的水印和鼠标光晕能透过来。 */
  .fh-bar{
    /* ⚠ 左右**故意不对称、连单位都不一样**：左边是绝对像素、右边是 vw。
       为什么不能对称、也不能都用 vw —— 见上面那段长注释，别"顺手统一一下"。 */
    position:absolute;
    /* ⚠ 2026-09-13 用户「三条横杠宽度再宽一点」→ **加的是厚度（上下），不是左右**。
       判据：用户上一轮说的"三条横杠的**长度**超出屏幕"指的是左右那一维（已经 coversL/coversR 双真，
       实测 AABB 横向 −7470..1930 ≫ 1424，再拉长只是把**屏幕外**的几何变长、一个可见像素都不多）。
       所以这里的"宽度"只能是杠的粗细 = 原来的 link 高度（`inset:0`）往上往下各放 20px。
       改完必须复核 `_tiltprobe.js` 的 scale 仍是 1 —— 不过这里只动绝对定位子元素的溢出、
       **不进 title-ctn 的 border box**，fitTilt 量不到它，理论上不受影响。 */
    top:-20px; bottom:-20px;
    left:calc(50% - 1700px);
    right:calc(50% - 250vw);
    z-index:-1; pointer-events:none;
    background:rgba(255,255,255,.60);
    border:1px solid rgba(255,255,255,.86);
    border-radius:6px;
    /* 柔和投影（2026-09-13 用户：「背后的横杠改成**白色**」，选的是"白半透明 + 柔和投影"）。
       ⚠ 投影是**必须的**，不是装饰：杠变成白色之后在白底上本来就看不见 ——
       投影是它在没有光晕的地方唯一能显形的东西。别当性能优化删掉。
       ⚠ 两层不是随手叠的：**近的一层薄而紧**负责"边"，**远的一层厚而散**负责"柔"。
       只有远层时实测边缘最深才压到 237/255（Δ18，肉眼几乎看不出是一条杠）；两层各给
       一点之后 Δ≈35，才是"看得见但仍是柔和投影"。改小/删掉任意一层都会退回看不见。 */
    box-shadow:0 2px 6px rgba(31,26,24,.09), 0 14px 34px rgba(31,26,24,.15);
  }
}

/* 横杠/透视只在 ≥1200px 开（与上面那条 -8° 同档）。窄屏那几档大字是**竖排**的，
   垫一块斜板 + 转个 3D 只会穿帮 —— 元素还是建的，这里显式关掉就行。 */
@media (max-width:1199px){
  .fh-bar{display:none}
  /* 光晕半径在窄屏要一起收：不收 → 390px 的手机上红团比屏还宽，整个首屏糊成一片红（实拍过）。
     （第三轮删底纹后这里只剩半径一条，原来那条 .fh-quilt 的格子尺寸跟着底纹一起撤了。）
     第五轮桌面档 520→360，这档同比 260→180。 */
  #fHero{--qb-r:180px}
}

/* ≡ .u-h1{font-size:6.2222rem;font-family:IntegralCF;font-weight:900;line-height:1em;text-transform:uppercase}
   + .u-text-stroke{color:transparent;-webkit-text-stroke-width:.1rem;letter-spacing:.02em}
   本页的大字取实测值 99.5556px（原站根字号 **16px** × 6.2222 —— 2026-09-12 到线上量出来的；
   早先按 14px 记的 87.11px 是错的，整块小了一号，用户一眼就看出来了）。 */
.expertises-marquees__expertise-link{
  /* ⚠ **故意不给 z-index**（第三轮加过 0、又拿掉了）。这是 §5d 那条满屏横杠的命门：
     横杠是 link 的第一个子节点 + `z-index:-1`，而负 z 的子元素会**窜出非层叠上下文的父级**、
     落到 .fh-band（z-index:1，本身是层叠上下文）里 —— 于是三道杠全部沉到**三行字的下面**。
     一给 link 加 `z-index:0`，link 就自成了层叠上下文，几道杠会被各自**关在自己那一行里**：
     杠拉满屏之后，行3 的杠（DOM 最后）就会**骑在行1/行2 的字上面**。实测过一次，别再加回来。
     要的只是 `position:relative` —— 给 .fh-bar 当定位父级（"和字一样宽"靠的就是这个框）。 */
  position:relative; display:inline-block; margin:0; white-space:nowrap; pointer-events:auto;
  font-family:"FZHero",var(--fh-display); font-weight:900;
  /* 字号 = 原站该档的原值 × 用户要的放大系数（--fh-fs-k），只在"装不下"时按屏幕宽让路。
     min() 的后半截是**塞得下**的兜底：本行 nowrap，按现有 4 个字估（CJK 一字 = 1em，
     再加一档余量 → 除以 4），先扣掉左右内边距、内收量、行内图标槽和字/图标间隙。
     2026-09-12：除数原为 5（照原站法文词长），内收量抬到 1.35 后 1024 档会被挤到
     字号缩水（那是"字变小"不是"装不下"），所以按我们自己的 4 字长改成 4。
     2026-09-12 换掉了原来那三个手调 clamp(…,64px)/(…,44px)/(…,36px) —— 那是按 14px
     根字号凑的，把 961~1199 和手机档的大字压得比原站小一大截（960px 下原站 82px、
     我们只有 64px）。四档基准值实测：1440→99.56 / 960→82 / 390→44.57，等于原站。 */
  font-size:min(calc(var(--fh-fs) * var(--fh-fs-k)),
    (100vw - var(--fh-inset) * 2 - var(--fh-gather) * 2 - var(--fh-icon) - var(--fh-word-gap)) / 4);
  line-height:1em; letter-spacing:.02em; text-transform:uppercase; color:var(--fh-txt);
  font-variation-settings:'wght' 900,'wdth' 100;
  -webkit-text-stroke-width:1.6px; -webkit-text-stroke-color:var(--fh-txt);   /* ≡ .1rem@16 u-text-stroke */
  transition:color 475ms cubic-bezier(.455,.03,.515,.955);
}
/* 2026-09-13 用户：「游戏攻略这几个字放大一点」→ **只第 2 行**再单独乘一档 --fh-fs-k2。
   ⚠ **两处都要乘**：min() 的后半截（"装不下就让路"的兜底）不一起乘的话，1024 档那种
   "兜底已经比基准小"的场景（那里 min() 取的正是后半截）会把放大整个吃掉、还不报错。
   放大是安全的：真溢出时 fitLines() 会把它缩回来。 */
.expertises-marquees__expertise:nth-child(2) .expertises-marquees__expertise-link{
  font-size:min(calc(var(--fh-fs) * var(--fh-fs-k) * var(--fh-fs-k2)),
    (100vw - var(--fh-inset) * 2 - var(--fh-gather) * 2 - var(--fh-icon) - var(--fh-word-gap)) / 4 * var(--fh-fs-k2));
}
/* 2026-09-13 第五轮：「好友系统、休闲桌宠**小一点**」→ 第 1/3 行乘 `--fh-fs-k13`。
   写成两条 `:nth-child(1)` / `:nth-child(3)` 而不是 `:not(:nth-child(2))` 或 `:nth-child(odd)`：
   前者在只有 3 行时等价，但**改了行数就会静默变味**（用户配置里 lines 是数组，随时可能加一行），
   后者只对奇数行生效、加第 4 行就漏 —— 明写 1 和 3，加行时能一眼看出要不要跟着改。
   ⚠ 收小之后 `--fh-gather` 不动：内收量是按 `--fh-fs-k`（全局档）算的，不跟 k13 走。
     所以第 1/3 行会显得比第 2 行更靠里 —— 这正是"三行不等大"该有的样子。 */
.expertises-marquees__expertise:nth-child(1) .expertises-marquees__expertise-link,
.expertises-marquees__expertise:nth-child(3) .expertises-marquees__expertise-link{
  font-size:min(calc(var(--fh-fs) * var(--fh-fs-k) * var(--fh-fs-k13)),
    (100vw - var(--fh-inset) * 2 - var(--fh-gather) * 2 - var(--fh-icon) - var(--fh-word-gap)) / 4 * var(--fh-fs-k13));
}
/* 第 2 行「游戏攻略」= **红色毛笔体**（2026-09-13 用户给了实拍海报，三行是
   「黑体 / 红毛笔 / 黑体」，用户选定「中间改红色毛笔体」）。
   ⚠ font-weight:400 是**必须的**，不是可调项：毛笔楷书只有一档字重，继承到大字的 900
     会合成假粗体，飞白全糊（见 §1 的 @font-face 注释）。
   ⚠ 字号**不**在这里加系数 —— 用户这次要的是三行等大（海报就是等大堆叠），
     靠 --fh-fs-k2 统一控制，上面那条 min() 就是它的消费方。
   毛笔字的字面比牛黑饱满，同样 font-size 下视觉更重，所以这里不动字号、
     让它自己长（真溢出由 fitLines() 兜）。 */
.expertises-marquees__expertise:nth-child(2) .expertises-marquees__expertise-link a{
  font-family:"MaShanZheng",var(--fh-display);
  font-weight:400;
  color:var(--fh-red);
}
/* ⚠ 这一条是**补悬停的洞**，不是装饰：第 2 行本来就红了，而 §5a 的悬停是"红色副本从光标处
   洇开盖住黑字" —— 拿红盖红＝**什么都看不见**，这一行的悬停会静默失效。
   反过来做：第 2 行的副本改成黑字（--fh-txt），悬停时红底上洇出黑字，交互照样在，
   而且恰好是海报的反色关系（黑 / 红 / 黑）。 */
.expertises-marquees__expertise:nth-child(2) .u-text-fill__fill{color:var(--fh-txt)}
/* 黑字本体：IntegralCF 不在手上（商业字体）→ 换 Archivo 900。描边宽跟原站取齐
   （原站 1.6px 但描边色透明，等于不加粗；这里保持一致，不做"假加粗"）。 */
.expertises-marquees__expertise-link > a{position:relative;-webkit-text-stroke-width:var(--fh-fat)}
.expertises-marquees__expertise-link a,
.expertises-marquees__expertise-link::before,
.expertises-marquees__expertise-link::after{line-height:1.1em}
/* ≡ .home-intro .expertises-marquees__expertise-link a{-webkit-text-stroke-color:transparent}
   大字本体是实心黑字，描边透明；水印带才是描边字。 */
.expertises-marquees__expertise-link a{
  display:inline-block; color:inherit; text-decoration:none;
  -webkit-text-stroke-color:transparent;
  transition:-webkit-text-stroke 475ms cubic-bezier(.455,.03,.515,.955);
}
.expertises-marquees__expertise-link a:focus:not(:hover){color:var(--fh-red)}

/* ---------- 5a. 「碰到字会变颜色」= 红色副本从光标处洇开（原站 2026-09-11 实测复原） ----------
   原站结构：<a class="u-text-fill u-text-fill--hover-alt">Identité<span class="u-text-fill__fill">Identité</span></a>
   —— <a> 里额外塞一份**同文字的红色副本**，平时 clip-path:circle(0%) 整个裁掉看不见；
   鼠标进 <a> 时 gsap 把圆心设成**光标在 <a> 盒内的百分比位置**，半径 0% → 150%，
   1.4s Power4.easeOut —— 红就从光标那点"洇"开盖住黑字（这就是"变色不是直接变"的真身）；
   移出再缩回 0%（0.6s）。实测原站中间态：60ms→32%、300ms→108%、700ms→146%、1400ms→150%。
   我们没引 gsap，用 CSS 过渡 + --fx/--fy 两个自定义属性做等价实现；圆心由 f-home.js 写。 */
.u-text-fill__fill{
  display:inline-block; position:absolute; left:0; bottom:0;
  color:var(--fh-red);                  /* ≡ --text_color_hover（fatfish 品牌红） */
  /* 同上一坑：这个 span 也挂在 <a> 里，会继承描边。原站继承到的是透明，这里写死透明。 */
  -webkit-text-stroke-color:transparent;
  clip-path:circle(0% at 50% 50%);
  pointer-events:none;
  transition:clip-path .6s cubic-bezier(.22,1,.36,1);          /* 移出：0.6s */
}
.u-text-fill__fill.on{
  clip-path:circle(150% at var(--fx,50%) var(--fy,50%));
  transition:clip-path 1.4s cubic-bezier(.22,1,.36,1);         /* 进入：1.4s Power4.out */
}
/* 描边水印带：**读 data-title-en（英文）**，同一个词重复 60 遍。**::before 与 ::after
   各走一侧**（原站就是这么写的）——奇数行 ::before 尾部向左、::after 从 100% 处向右；
   偶数行反之。向外那一侧要空出 --word_spacing + --icon_size，那是图标的位置。

   2026-09-12 用户：「黑色的字是中文，旁边白色透明的字改成他们的英文」——
   于是本体（<a> 里的汉字）不动，两条水印带换成英文。`data-title` 仍保留着中文原文
   （intro.js 的就位判据、_fhero_verify.js 的 >200 长度断言都读它），英文另开一个
   `data-title-en`，由 f-home.js 按 FH.lines[].en 写入。 */
/* ⚠ 2026-09-13 关掉：水印改成通栏背景层了（见 §4a 的 #fHero .fh-wm），原来这两条
   贴着大字走的 ::before/::after 一并撤掉，免得同屏两套白描边英文。
   撤的只是 `content`，下面那堆定位/过渡声明原样留着 —— 它们没有副作用，
   而万一要回退，把这一条去掉就整个回来（data-title-en 仍由 f-home.js 写在 link 上）。 */
.expertises-marquees__expertise-link::before,
.expertises-marquees__expertise-link::after{
  content:none; position:absolute; top:0; z-index:-1;
  color:transparent; -webkit-text-stroke:1.6px var(--stroke_color);   /* ≡ .1rem@16 */
  white-space:nowrap; pointer-events:none;
  /* 「变颜色不直接变」就是这条：0.6s Expo-out 从描边色渐变到实色 */
  transition:color var(--link_trans),-webkit-text-stroke 475ms cubic-bezier(.455,.03,.515,.955);
}
.expertises-marquees__expertise:nth-child(odd) .expertises-marquees__expertise-link::before{right:0}
.expertises-marquees__expertise:nth-child(odd) .expertises-marquees__expertise-link::after{
  left:100%; margin-left:var(--word_spacing);
  padding-left:calc(var(--word_spacing) + var(--icon_size))}
.expertises-marquees__expertise:nth-child(even) .expertises-marquees__expertise-link::before{
  right:100%; padding-right:calc(var(--word_spacing) + var(--icon_size));
  margin-right:var(--word_spacing)}
.expertises-marquees__expertise:nth-child(even) .expertises-marquees__expertise-link::after{left:0}

/* ---------- 5b. 悬停四连（原站加在 .…title-ctn 上的 .is-hovered，不是加在整块上） ----------
   ① 整条水印填成 --stroke_color 本身（不是另一个色值）+ 0.6s 渐变
   ② 图标放大微转 ③ 图标开始 wiggle_2 抖动 ④ 行侧黄字平移淡入
   类由 f-home.js 的 setOn() 加到 .…__expertise-title-ctn 上。 */
.expertises-marquees__expertise-title-ctn.is-hovered .expertises-marquees__expertise-link::before,
.expertises-marquees__expertise-title-ctn.is-hovered .expertises-marquees__expertise-link::after{color:var(--stroke_color)}

/* 行侧手写小字：**黄色的字**。原站是 GuthenBoots 商业花体 → 用系统手写体兜底
   （裸 cursive 在 Windows 会落到 Comic Sans，所以先点名副手写体；中文照样走后面的黑体栈）。 */
.expertises-marquees__expertise-subtitle{
  width:100%; position:absolute; top:50%; font-size:42.67px; /* ≡ 2.6667rem@16（原站实测 42.6667px） */
  font-family:"Segoe Script","Bradley Hand","Comic Sans MS",cursive,var(--fh-display); line-height:1.4em; color:var(--fh-gold);   /* ≡ --tag_color */
  /* 这个小字在 DOM 上是 **title-ctn** 的子节点（2026-09-12 从 link 里搬过去的，看下面那条规则）
     → 会继承大字那 1.6px 的描边色(#222)，描在细笔画的花体上等于把整行涂黑(实测过)。原站继承
     到的描边色是透明(它的大字靠 .u-text-stroke{color:transparent})，这里对齐成透明。 */
  -webkit-text-stroke-color:transparent;
  opacity:0; margin-top:.3em; white-space:nowrap; pointer-events:none;
  transition:var(--link_trans);                 /* ≡ transition:var(--link_trans) —— 平移淡入不是瞬现 */
}
/* ⚠ **小字的定位父级是 title-ctn，不是 link**（2026-09-12 改；原站 DOM 就是这样，1440 实测小字盒
   宽 674 = ctn 宽，而 link 只有 544）。这一条就是「小字被图标挡住」的根治 —— ctn 的盒子**含图标那一列**，
   所以 `left:100%` 天然从**图标右边**起；以前挂在 link 上，100% 只到字的右缘，`+6rem` 正好落在图标身上。
   `width:100%`（=ctn 宽）也才跟原站对上。由 f-home.js 把 subtitle 建成 ctn 的子节点。 */
.expertises-marquees__expertise-title-ctn{position:relative}
/* ⚠ 2026-09-13 用户「休闲桌宠左边的顺便聊两句改到右边」→ **第 3 行不再单独走左边那一套**，
   三行统一 `left:calc(100%+96px)`。原来第 3 行那条是 `right:calc(100%+64px)` + `text-align:right`
   + 从 +80px 滑入（镜像版），连同 ≤1299 / ≤1199 里那两条 `right:` 覆盖**一起删了** ——
   留着的话窄屏档会把第 3 行又拽回左边。原站值（6rem / -5rem@16）见每行尾注。 */
.expertises-marquees__expertise .expertises-marquees__expertise-subtitle{
  left:calc(100% + 96px); transform:translateX(-80px) translateY(-50%)}   /* ≡ 6rem / -5rem@16 */
/* 2026-09-13 晚 用户：「休闲桌宠右边的顺便聊两句改到左边」→ 第 3 行**改回镜像版**
   （09-13 白天刚按「休闲桌宠左边的顺便聊两句改到右边」并成三行统一，现在只把这一行掰回来，
   第 1/2 行仍是右边那一套）。镜像 = `left:auto` + `right:calc(100% + 96px)` + 右对齐 +
   从 **+80px**（左边）滑进来，跟右边那套的 -80px 正好镜像。
   ⚠ **必须写在下面那条 `.is-hovered` 的上面**：两条特异性都是 (0,3,0)，
     同分时后写的赢 —— 放到下面去的话，悬停时 `.is-hovered` 那条 `transform:translateX(0)`
     会被这条的 `translateX(80px)` 盖掉，小字**永远滑不进来**（且不报错）。
   ⚠ 这里只改 `transform` 的**起始**值，`.is-hovered` 那条的终点 `translateX(0)` 两套共用，别动。 */
.expertises-marquees__expertise:nth-child(3) .expertises-marquees__expertise-subtitle{
  left:auto; right:calc(100% + 96px); text-align:right;
  transform:translateX(80px) translateY(-50%)}
.expertises-marquees__expertise-title-ctn.is-hovered .expertises-marquees__expertise-subtitle{
  opacity:1; transform:translateX(0) translateY(-50%)}

/* 行内浮动图标（原站的骷髅/手柄/咖啡就在这儿；白描边靠 4 向 drop-shadow 做出来）。
   想换成自己的图：把路径填进 f-home.js 顶部 FH.lines[].icon 即可。 */
.expertises-marquees__expertise-icon-ctn{
  position:relative; pointer-events:none;
  animation:sway_vertical 11s cubic-bezier(.445,.05,.55,.95) infinite;
  filter:drop-shadow(2px 2px 0 #fff) drop-shadow(2px -2px 0 #fff)
         drop-shadow(-2px 2px 0 #fff) drop-shadow(-2px -2px 0 #fff);
}
.expertises-marquees__expertise:nth-child(odd) .expertises-marquees__expertise-icon-ctn{margin-left:var(--word_spacing)}
.expertises-marquees__expertise:nth-child(even) .expertises-marquees__expertise-icon-ctn{order:-1;margin-right:var(--word_spacing)}
/* 2026-09-13 晚 用户：「休闲桌宠右边的图片放到左边去，现在挡住文字了」。
   实测（`_rowimg.js`）第 3 行图片 x=1097..1163 压住「休闲桌宠」的字（字右缘 1122）=
   **25px 宽 × 96px 高 叠在字上**，第 1/2 行都不叠 —— 是 09-13「行3 再右移半个字」那句
   （`left:.51em`，见上）把字顶进图片里去的。改法 = 把偶数行那套（`order:-1`）套给第 3 行，
   图片挪到字的左边。
   ⚠ 特异性与上面 `:nth-child(odd)` 那条**同分 (0,3,0)** → **必须排在它后面**，
     写到前面会被 odd 那条的 `margin-left` 盖回来（且不报错）。
   ⚠ `margin-left:0` 不能省：odd 那条给所有奇数行都留了 `margin-left:var(--word_spacing)`，
     不清掉的话图片右边会多出 25.6px 的槽。 */
.expertises-marquees__expertise:nth-child(3) .expertises-marquees__expertise-icon-ctn{
  order:-1; margin-left:0; margin-right:var(--word_spacing)}
.expertises-marquees__expertise-icon-inner{animation:sway_horizontal 14s cubic-bezier(.455,.03,.515,.955) infinite}
.expertises-marquees__expertise-icon{width:var(--icon_size);height:0;display:flex;align-items:center;margin-top:32px;   /* ≡ 2rem@16 */
  transition:transform var(--link_trans)}
.expertises-marquees__expertise-icon img{width:var(--icon_size);height:var(--icon_size);object-fit:contain;transform-origin:center 70%}
.expertises-marquees__expertise-title-ctn.is-hovered .expertises-marquees__expertise-icon{transform:scale(1.16) rotate(8deg)}
.expertises-marquees__expertise-title-ctn.is-hovered .expertises-marquees__expertise-img{animation:wiggle_2 2.6s cubic-bezier(.455,.03,.515,.955) infinite}

@keyframes sway_vertical{0%{transform:translateY(0)}25%{transform:translateY(-11.2px)}50%{transform:translateY(6.4px)}75%{transform:translateY(-8px)}100%{transform:translateY(0)}}   /* ≡ -.7/.4/-.5rem@16 */
@keyframes sway_horizontal{0%{transform:rotate(0)}17%{transform:rotate(4deg)}33%{transform:rotate(-6deg)}50%{transform:rotate(7deg)}67%{transform:rotate(-4deg)}83%{transform:rotate(5deg)}100%{transform:rotate(0)}}
@keyframes sway_vertical_less{0%{transform:translateY(0)}25%{transform:translateY(-4.8px)}50%{transform:translateY(3.2px)}75%{transform:translateY(-3.2px)}100%{transform:translateY(0)}}   /* ≡ -.3/.2/-.2rem@16 */
/* ≡ fatfish 原站的悬停抖动手感（"他的图片还是晃动的"就是这条） */
@keyframes wiggle_2{
  0%{transform:translate(0,0) scale(1) rotate(0)}
  5%{transform:translate(0,0) scale(1) rotate(-1deg)}
  11%{transform:translate(2%,-2%) scale(1.05) rotate(2deg)}
  17%{transform:translate(-4%,-2%) scale(1.05) rotate(-4deg)}
  21%{transform:translate(2%,-2%) scale(1.05) rotate(3deg)}
  26%{transform:translate(-1%,0) scale(1.025) rotate(-1deg)}
  33%{transform:translate(0,0) scale(1.025) rotate(1deg)}
  69%{transform:translate(0,0) scale(1) rotate(0)}
  100%{transform:translate(0,0) scale(1) rotate(0)}
}

/* ---------- 6. 描述区（≡ .home-intro__descriptions：紧跟大字正下方、左缩进 4 列，
     三条描述叠在同一格里用 opacity 切换，不是替换文字） ---------- */
.home-intro__descriptions{
  display:grid; margin-top:var(--fh-desc-gap); position:relative;
  /* ≡ margin-left:calc(var(--grid-gutter)*-1 + (var(--grid-gutter) + var(--grid-col-width))*4)
     2026-09-13 用户「把描述和箭头往左移动」→ 缩进从 **4 列改 2 列**（1440 下整块左移约 228px）。
     箭头是 ::before + `right:calc(100% + 64px)`，**锚在这个块上** —— 所以只动这一处，
     描述和箭头会一起走，不用分别给箭头写偏移。 */
  /* ⚠ 09-13 第三轮起，**≥1200 这个值已经不管左缘了** —— 用户「描述和箭头还得往左移动，和好友系统对齐」，
     而"对齐行1"在 ≥1200 是 f-home.js 的 `alignDesc()` **量出行1 包围盒再补 margin-left（内联）** 做的：
     行1 的左缘吃 -8° 倾斜外扩 + rotateY(28deg) 透视，**不是** 100vw 的线性函数，
     实测需要补的列数 1904→0.40 / 1584→1.00 / 1424→1.31 / 1184→1.65，写死哪个都会在别的宽度上歪。
     这里留的值只对 **<1200**（无倾斜无透视，行1 就在栅格上）生效，= 第三轮之前的行为，别拿它调 ≥1200。 */
  --fh-desc-cols:2;   /* 09-13 用户「还得左移」→ 3 改 2（1440 下再左移 115px，累计比原 4 列左移 230px） */
  margin-left:calc(var(--fh-gutter) * -1 + (var(--fh-gutter) + var(--fh-col)) * var(--fh-desc-cols));
  pointer-events:auto;
}
/* 2026-09-13 晚 用户：「描述往下两行」。
   "一行" = 描述正文自己的行高 = `.home-intro__description` 的
   `font-size:21.33px × line-height:1.4` = **29.862px**（写死 px 不是 clamp）。
   ⚠ **不能用 `margin-top` 推**（我先那么写，量出来只下去 29.8px 而不是 59.7px）：
     `.fh-stack` 是**居中**的 flex 列 —— 给最后一个子元素加 margin，整组内容先上移 Δ/2，
     净效果 = 描述只下移 Δ/2，而**三行大字被顶上去 Δ/2**。用户只要描述动，不要大浮动。
   `top` 是相对定位的纯渲染位移：不动布局、不碰兄弟节点，箭头(::before)锚在这个块上跟着走。
   ⚠ **只在 ≥961 开**：≤960 由 m.css 接管，那里 `--fh-desc-gap` 各给 32/28，
     而且描述字号降到 **16px**（行高 1.4 → 22.4px）—— 常数 59.724px 在那边根本不是"两行"，
     还会把本来就紧的竖屏首屏顶出 `#fHero` 的 overflow:hidden。 */
@media (min-width:961px){
  .home-intro__descriptions{top:calc(2 * 29.862px)}
}
/* 描述块左侧的回环箭头（原站是 arrow_loop_right.svg，那是它的自有素材 → 我们自己画了一个）。
   入场时 scale(0) rotate(-30deg) → scale(1,-1) rotate(-15deg)，0.7s。
   2026-09-12：素材换成用户给的毛笔飞白箭头 stk-arrow2.png（384×192，比例≈2:1）。
   2026-09-13：**又换了一次**，成用户给的图 stk-hero-desc.png（同样 384×192，正好 2:1）。
   两版都是**白底不透明**的 PNG，正好贴在首屏白底上。
   ⚠ 不再 `scale(1,-1)`：那是为老 SVG「先向右下再回勾」的形状做的垂直翻转，
   新箭头是一笔向右的飞白，翻过来会变成朝上翘。保留 rotate(-15deg) 的入场倾斜。 */
.home-intro__descriptions::before{
  content:""; position:absolute; top:-44px; right:calc(100% + 64px);  /* 原站实测 top -8 / left -144（=块的左缘再往左 4rem）；09-13 又上移 16px（-28 → -44） */
  /* 2026-09-13 用户「描述左边的图片改成桌面 b569…、要上移一点、放大2倍」：
     图换成 stk-hero-desc.png（384×192，正好 2:1，和原来的槽位同比例、不会 letterbox），
     尺寸 80×38.4 → **160×80**（严格 2 倍），`top` -8 → **-28**（上移 20px，别再往下压着正文第一行）。
     ⚠ **width/height 必须成对改**：下面两个窄屏档只改 width 不改 height 的话，盒子会被拉成 144×38.4
       这种非 2:1 的形状，`contain` 就把图缩回 76.8×38.4 居中 —— 看着像"根本没放大"。 */
  width:160px; height:80px;
  background:url('/img/stk/stk-hero-desc.png') no-repeat center/contain;
  transform:scale(0) rotate(-30deg);
  transition:transform .7s cubic-bezier(.19,1,.22,1);
}
/* 09-13 用户「顺时针旋转 10°」：终点角 -15° → **-5°**（CSS 里正的 rotate 就是顺时针，
   所以"顺时针 10°" = 在原来的 -15° 上加 10°）。**只动终点、不动入场起点的 -30°** ——
   那条是"从更斜的位置转正"的动效，起点跟着改会让入场幅度变小。 */
.home-intro__descriptions--animated::before{transform:scale(1) rotate(-5deg)}

.home-intro__description{
  grid-row:1; grid-column:1/1; opacity:0; pointer-events:none;
  width:calc(var(--fh-gutter) * -1 + (var(--fh-gutter) + var(--fh-col)) * 4);
  min-width:480px;                                  /* ≡ 30rem@16（原站实测每条描述宽 480px） */
  font-family:"Roboto Slab",var(--ff-cjk);
  font-weight:300; font-size:21.33px; line-height:1.4; color:var(--fh-txt);  /* ≡ 1.3333rem@16 / 原站实测 21.3333px、行高 29.8667px */
  /* 原站这三条描述是 GSAP fromTo（x:±6rem / y:6rem / rotate:∓5 → 0）0.8s Expo-out。
     这里用等价 CSS 过渡：新进来的一条从"左下 + 反向倾斜"滑到位，出去的那条往右下走。 */
  transform:translateX(-84px) translateY(84px) rotate(-5deg);
  transition:opacity .8s cubic-bezier(.19,1,.22,1), transform .8s cubic-bezier(.19,1,.22,1);
}
.home-intro__description.active{opacity:1; transform:none; pointer-events:auto}
/* ≡ .home-intro__description.default{opacity:1}：首屏默认显示的那条。
   它被顶掉时走的是另一条路径（x:+6rem 向右下淡出），与其它三条方向不同。 */
.home-intro__description.default{opacity:1; transform:none}
.home-intro__description.default:not(.active){opacity:0; transform:translateX(84px) translateY(84px) rotate(0)}
/* 「查看全部应用」胶囊按钮：2026-09-12 用户要求去掉 → 标记与样式一并移除。
   （原站首屏描述区里本来也只有四条 <p>、没有按钮，所以去掉后正好回到原站结构。） */

/* ---------- 6.5 窄屏档位（2026-09-12 照 fatfish frontend.min.css 逐条抄，不是估算） ----------
   原站的断点是 **1299 / 1199 / 1024 / 849 / 749**（比常见的 1200/992/768 错开），
   大字字号来自 `.u-h1,h1{font-size:6.2222rem}` 加两档覆盖。之前本文件一刀切：
   ≥961px 全用桌面值、≤960px 才切档 → 961~1299 这段整段错（大字 99.56 应为 82）。
   ⚠ 下面每一条的注释都是原站原文对应的 rem 值（16px 根字号）。
   ⚠ m.css 里也有同名档（960/560/400），它晚于本文件加载 → 同特异性时以它为准；
     两处的数值必须一致，改一处顺手改另一处。 */
@media (max-width:1299px){
  .expertises-marquees__expertise:nth-child(2) .expertises-marquees__expertise-title-ctn{margin-left:-160px}  /* ≡ -10rem */
  /* ⚠ 09-13 那轮"三行再错开"**只在 ≥1300 生效**：第 2 行本来就有这条 ≤1299 覆盖，
     第 3 行**原来没有**（它的 -48px 一直管到 1025）—— 不给它补一条的话，1025~1299
     这段会跟着吃新值、而第 2 行反而收回原值，两边不对称。补这一条 = ≤1299 与改版前逐字相同。 */
  .expertises-marquees__expertise:nth-child(3) .expertises-marquees__expertise-title-ctn{margin-right:-48px}  /* ≡ -3rem */
  .expertises-marquees__expertise-subtitle{font-size:40px}                                                   /* ≡ 2.5rem */
  /* ⚠ 第 3 行那条 `right:calc(100% - 112px)`（原站值）早先已删、09-13 白天三行共用上面那条，
     09-13 晚用户要第 3 行回左边 → 下面补了一条镜像，见紧跟着那行 */
  .expertises-marquees__expertise .expertises-marquees__expertise-subtitle{left:calc(100% + 56px)}  /* ≡ 3.5rem */
  /* 第 3 行跟着基准档一起镜像（见 §5 那条的说明）。**必须排在上面那条后面**：
     两条对第 3 行而言特异性相同 (0,3,0)，后写的赢。 */
  .expertises-marquees__expertise:nth-child(3) .expertises-marquees__expertise-subtitle{right:calc(100% + 56px)}  /* ≡ 3.5rem */
}
@media (max-width:1199px){
  html.fh{ --fh-fs:82px;                       /* ≡ .u-h1,h1{font-size:5.125rem} */
           --fh-word-gap:17.6px;               /* ≡ --word_spacing:1.1rem */
           --fh-icon:72px; }                   /* ≡ --icon_size:4.5rem */
  /* ≡ nth-child(2) ctn{margin-left:calc((--icon_size + --word_spacing) * -1)} = -(72+17.6) */
  .expertises-marquees__expertise:nth-child(2) .expertises-marquees__expertise-title-ctn{
    margin-left:calc((var(--fh-icon) + var(--fh-word-gap)) * -1)}
  .expertises-marquees__expertise-subtitle{font-size:28.8px}                                                 /* ≡ 1.8rem */
  /* ⚠ 第 3 行那条 `right:calc(100% - 32px)`（原站值）已删（见 §5 的 2026-09-13 说明）；
     这一档的镜像用不着 —— ≤849 起小字整条 display:none */
  .expertises-marquees__expertise-icon{margin-top:24px}                                                      /* ≡ 1.5rem */
  .home-intro__descriptions::before{width:144px; height:72px; right:calc(100% + 32px)}                       /* ≡ 4.5rem / 2rem；09-13 随基准档一起 ×2（原 72） */
}
/* 描述块左边那张图的**第三档**（09-13 新增）。为什么非要有这一档：
   960 时描述块左缘只剩 x≈75（`.home-intro__descriptions` 在那个宽度被推到很左），
   左边能用的横向空间 = 75 − 32 = 43px —— 144 宽的图会从 −101 起，**几乎整张被 #fHero 裁掉**
   （改之前 72 宽也只露出 43/72 = 六成，是**老毛病**，不是我这次弄出来的）。
   这一档把它压到 56×28：960 下从 −5 起，基本能看全；≤749 那档同值，两档接得上。
   ⚠ **必须排在上面那条 ≤1199 的后面** —— 同特异性下后写的赢。 */
@media (max-width:960px){
  .home-intro__descriptions::before{width:56px; height:28px}
}
@media (max-width:1024px){
  .expertises-marquees__expertise:nth-child(3) .expertises-marquees__expertise-title-ctn{margin-right:0}
}
@media (max-width:849px){
  .expertises-marquees__expertise-subtitle{display:none}
}
@media (max-width:749px){
  html.fh{ --fh-fs:44.57px;                    /* ≡ .u-h1,h1{font-size:2.7857rem}（原站这里本身就写歪了，按 14 折算的 39px） */
           --fh-word-gap:12.8px;               /* ≡ --word_spacing:0.8rem */
           --fh-icon:40px; }                   /* ≡ --icon_size:2.5rem */
  .expertises-marquees__expertise:not(:last-child){margin-bottom:3.2px}                                      /* ≡ .2rem */
  .expertises-marquees__expertise-icon-ctn{animation-name:sway_vertical_less}
  .expertises-marquees__expertise-icon{margin-top:9.6px}                                                     /* ≡ .6rem */
  .home-intro__descriptions::before{width:56px; height:28px; right:calc(100% + 24px)}                        /* ≡ 3.5rem / 1.5rem；09-13 高度补成 2:1（原 height 38.4 是 letterbox 的） */
}

/* ---------- 7. 贴纸槽（用户自己出图：把图放进 img/stk/，在 f-home.js 的 stickers[].src 填路径） ---------- */
.fh-stickers{position:absolute; inset:0; pointer-events:none}
.fstk{
  position:absolute; left:var(--x,0); top:var(--y,0); width:var(--w,140px);
  transform:rotate(var(--r,0deg)); transform-origin:50% 50%;
  transition:transform .6s cubic-bezier(.19,1,.22,1);
  pointer-events:auto; z-index:3;
}
.fstk:hover{transform:rotate(0deg) scale(1.06)}
.fstk img{display:block; width:100%; height:auto; user-select:none; -webkit-user-drag:none}
/* 空槽只在调试模式（?fstickers=1）显形，平时完全不可见 */
.fstk--empty::after{content:""; position:absolute; inset:0;
  border:1px dashed rgba(34,34,34,.22); border-radius:14px}
html:not(.fdbg) .fstk--empty::after{display:none}

/* ---------- 8. 顶栏白底皮肤（全部锁在 html.fh 里；只 index.html 命中） ---------- */
/* #apeTop 是 apeTop.js 注入的，它把自己的样式表追加进 head，顺序在 f.css 之后。
   但下面这些选择器特异性 (1,2,1)/(1,3,1) 稳压它的 (0,1,0) 与 m.css 的 (1,1,1)，
   与加载顺序无关。apeTop.js 本身只多注入了一个汉堡按钮，其余一字未改。
   实测值来源：fatfish.ca 1440×900 的计算样式（它的根字号 14px，这里已换算成 px）。

   ⚠ **2026-09-12 记一笔（下次别重复踩）**：skin.css §5/§5c 有一份**逐条对等**的泛化副本
   （选择器 `#apeTop` → `.lv-top`，为的是让内页的静态 `<header class="lv-top">` 也吃到同一套皮）。
   两份并存时**首页永远是本文件赢**（带 id 的特异性高一档），所以 skin.css 里改尺寸对首页无效
   —— 调顶栏缩放时真踩到了：skin.css 里 8 处尺寸全改成 `calc(... * var(--top-k))`，
   量出来首页只有 padding 那条生效（因为它是唯一一条这里没重复的）。
   试过把本块整个删掉让 skin.css 接管，**结果窄屏首页跟着变**：logo 44→28px、nav 间距 20→8.8px
   —— 因为 m.css 那套 44/38/32、16/12/10 的数字和这里一直在压的值不一样，删了等于顺手改了窄屏。
   结论：**这份留着**，改顶栏尺寸时本文件与 skin.css §5 必须一起改（同一个 --top-k，三处引用）。 */
html.fh #apeTop.lv-top{
  height:calc(var(--fh-top,clamp(92px,11.25vw,162px)) * var(--top-k));
                                     /* = logo + 上下留白（见 §2 的 --fh-top）；
                                        ⚠ 这里原来写死 `var(--fh-top)` 无兜底 —— 窄屏时
                                        m.css 已把 --fh-top 定义成 62/56/52，桌面才走 fallback。 */
  background:#fff;
  box-shadow:0 1px 0 rgba(0,0,0,.06);
}
html.fh #apeTop.lv-top::after{background:none}   /* 干掉原深色底上的那条白色细线 */
/* nav 间距：原站是 li{margin-left:3.4rem}@14 = 47.6px（配 12.44px 的字）。字号放大后按视口等比。
   2026-09-13 晚 用户：「首页顶栏的 首页/游戏实况/桌宠/主页 这几个按钮分开一点」，
   紧接着「还要变大，要比现在大一倍的间距」。
   ⭐ 「现在」= 站长眼睛看到的那个值。而第一轮改的那条**根本没生效**（病根见本段末），
     他看到的始终是**最初**的 20/2.6vw/40 → 1440 实测 16.47px。所以「×2」就是拿这一档乘 2：
     20/2.6vw/40 → 40/5.2vw/80。--top-k 是线性乘数，三个参数一起乘 2 = 任何宽度都正好翻倍。
     ⚠ 我一度按"已经生效的 49.0px"去乘 2 算成 56/6.8vw/112，方向对但**基数错了**，
       那样会一次给到 2.6 倍；现已按用户的字面意思回到 2 倍。
   ⚠ 只改首页。内页那份等价规则在 skin.css §5（选择器 `.lv-top`），本窗口没动 ——
     用户点名的是「首页顶栏」；要让全站跟着变，得再改 skin.css 那一条并重推。
   ⚠ 本文件这条特异性 (1,2,1)，高过 skin.css 的 (0,3,1) 和 m.css 的 (1,1,1) → 首页永远赢，
     所以它**必须**自带 min-width:961px 的媒体查询兜住：否则 ≤960 那三档的窄屏间距
     （16/12/10 × --top-k，那几档 --top-k 还是 0.88）会被这条盖掉，400px 屏上缝隙翻倍、直接撑破。
     改大后必须回拍 1024/1200 两档看有没有和 logo 撞上（_ovf 不查 fixed 层，量不到）。
   ⚠⚠ 这条规则 09-13 一度整条"改了不生效"。病根**不在**选择器打架，而在**紧挨着它的这段注释自己**：
     注释里只要写出「两个星号立刻跟一个斜杠」（就是 markdown 加粗后直接接斜杠的写法），
     那三个字符就是 CSS 的注释结束符 —— 注释**提前闭合**，其后直到下一个大括号的内容全变垃圾，
     随即**这条规则被静默吞掉**：不报错、curl 取回的文件和磁盘逐字节一致、控制台干净，
     只有 document.styleSheets 里少一条（本次 f.css 顶层 105 条只解析出 104 条）。
     我因此白查了半天"是不是选择器打不赢"，特此留档。
     自检：全文搜「星号-星号-斜杠」三连必须为空。 */
@media (min-width:961px){
  html.fh #apeTop .lv-nav{gap:calc(clamp(40px,5.2vw,80px) * var(--top-k))}
}
/* 导航：fatfish 实测原是 "Roboto Slab" 300 / 12.44px / #222。
   **2026-09-11 用户要求「字大 30px」→ 12.44 + 30 = 42.44px**（1440 及以上满值；
   窄的桌面按 2.95vw 等比缩，否则 4 个中文菜单项会把 1024 宽的顶栏顶爆）。 */
html.fh #apeTop .lv-li{
  font-family:"Roboto Slab",var(--ff-cjk);
  font-weight:300; font-size:calc(clamp(22px,2.95vw,42.44px) * var(--top-k)); line-height:1; letter-spacing:.04em;
  color:var(--fh-txt);
}
/* 原站导航**文字颜色悬停不变**（实测：hover 后仍是 #222）—— 悬停只出一条红色下划线。
   所以这里不给 :hover 变色，改成下面那条 1px 红杠（≡ .header__main-nav .menu>li>a::after
   的 background-color:#cb413f + scaleX(0)，从右往左拉开）。 */
html.fh #apeTop .lv-li{position:relative}
html.fh #apeTop .lv-li::after{
  content:""; position:absolute; left:0; right:0; bottom:-12px; height:1px;
  background:var(--fh-red); background-image:none;
  transform:scaleX(0); transform-origin:right;
  transition:transform .5s cubic-bezier(.83,0,.17,1);
}
html.fh #apeTop .lv-li:hover::after{transform:scaleX(1)}
html.fh #apeTop .lv-on{color:var(--fh-txt)}
html.fh #apeTop .lv-on::after{
  transform:scaleX(1); background:var(--fh-txt);   /* 当前页：黑色常驻（原站当前项走的是黑色手绘波浪线） */
  transition:none;
}
/* 认证位(#apcAcc 里 authnav.js 挂的 .lv-li: 未登录=登录 / 已登录=主页) → fatfish 的胶囊 CTA
   ≡ .cta：本底**品牌红 #cb413f**，悬停时金色 #cab99a 从左侧滑入盖住它（0.4s cubic-bezier(.76,0,.24,1)）。
   金色层用 ::before 做（**不能**用两层 background-image + background-position:-101%：
   background-size 与容器同宽时百分比定位退化成 0，金层根本藏不住，实测过）。
   ::before 归它用 → 上面那根导航下划线（::after）在胶囊上关掉。 */
html.fh #apeTop #apcAcc .lv-li{
  position:relative; z-index:0; overflow:hidden;   /* 站位 + 让金层裁在胶囊圆角内 */
  font-family:var(--fh-display);
  font-weight:900; letter-spacing:.04em;
  /* 与导航同一条「+30px」：14 → 44（内边距/字号一起按视口等比，胶囊形状不变） */
  font-size:calc(clamp(22px,3.06vw,44px) * var(--top-k));
  color:#fff; border:0; border-radius:999px;
  padding:calc(clamp(9px,1.25vw,18px) * var(--top-k)) calc(clamp(20px,3.75vw,54px) * var(--top-k)) calc(clamp(12px,1.67vw,24px) * var(--top-k));
  background:var(--fh-red);                        /* ≡ .cta::before */
}
html.fh #apeTop #apcAcc .lv-li::before{
  content:""; position:absolute; inset:0; z-index:-1;   /* -1: 压在本底色之上、文字之下 */
  background:var(--fh-gold);                            /* ≡ .cta::after */
  transform:translateX(-101%);
  transition:transform .4s cubic-bezier(.76,0,.24,1);
}
html.fh #apeTop #apcAcc .lv-li:hover::before{transform:translateX(0)}
html.fh #apeTop #apcAcc .lv-li::after{display:none}   /* 胶囊不要下划线 */
html.fh #apeTop #apcAcc .lv-li:hover{color:#fff}
/* logo：2026-09-12 换成用户给的「桌宠界面UI设计.png」。
   新图已经在 /img/logo.png 里**裁到墨迹边**（猫 + 北极光字标，1242×669）并把白底抠成透明，
   所以：① 不再需要 `filter:brightness(0)` —— 那是给旧 aurora.png 那种**纯白**字标用的，
   现在字标本来就是黑的，压黑会把整张图（含猫）糊成一团实心黑；② 不再需要负左边距 ——
   旧图 2848×1600 里字标左边距 685px 才对齐 x40，新图紧裁后左缘就是墨迹左缘。
   高度：旧值 117px 是**整张画布**的高度，可见字标只有 49px（墨迹仅占画布 42%）。
   新图整张都是墨迹，所以按"可见字标高 ≈ 63px（≈ 旧 49px 的 1.3 倍，延续用户 09-11
   「logo 也大一点」的口味）"反推 → 63 ÷ 0.747（字标占新图高的比例）= 84px。
   用 clamp 等比，1440 以上封顶 84，窄桌面自动缩。 */
html.fh #apeTop .lv-logo{margin-left:0}
html.fh #apeTop .lv-logo img{height:calc(clamp(44px,5.85vw,84px) * var(--top-k))}

/* ---------- 9. 汉堡按钮 —— **2026-09-12 已整体删除** ----------
   原来是 `html.fh #apeTop .fh-burger{…}` + 4 条 span 横线。
   用户 2026-09-12:「汉堡全屏菜单整个删掉, 桌面和手机都不要」→ 按钮、全屏浮层
   (#fhMenu)、以及它们在 apeTop.js / skin.css §5c 里的对应块一起删了。
   **别加回来**。窄屏的导航现在在侧边抽屉 #fhDrawer 里(见 skin.css §10)。
   ⚠ 代价(用户知情并接受): 桌面上「代码站 / 关注 / 投稿」暂时没有别的入口。 */

/* ---------- 10. 汉堡全屏浮层 #fhMenu —— **2026-09-12 已整体删除** ----------
   原来这里是一张 position:fixed;inset:0 的白纸(项目符号列表 + 右上角 ×)。
   与 §9 同一次改动一起移除。窄屏改成从左侧滑出的抽屉 #fhDrawer。 */

/* ---------- 11. 左下角隐私 / 跟踪告知条（≡ fatfish #ff-wp-tracking-popup） ----------
   原站是 WordPress 插件（ff-wp-tracking）弹出的白色小卡，钉在**左下角**：
     position:fixed → 白卡 width:50% / padding:21px / z-index:1020 / 字号 14px Roboto Slab
     正文一句 + 一条红色(它的品牌色 #cb413f)隐私政策链接 + 三个**透明底黑字大写**按钮（不是胶囊）
     出现/消失 transition: opacity .6s cubic-bezier(.25,1,.5,1)
   我们保留形状与位置，文案本土化、链到本站 /privacy.html。**只首次显示**（localStorage 记一笔），
   点「知道了 / 全部接受」即收起；点正文里的链接不会收起。
   关掉这一整条：把 f-home.js 里 FH.privacy.show 改成 false。 */
#fhPrivacy{
  position:fixed; left:0; bottom:0; z-index:1020;
  width:min(50%,560px);
  background:#fff; color:#000;
  padding:21px 21px 18px; box-sizing:border-box;
  font-family:"Roboto Slab",var(--ff-cjk);
  font-weight:300; font-size:14px; line-height:1.55;
  box-shadow:0 0 0 1px rgba(0,0,0,.06), 0 18px 48px rgba(0,0,0,.10);
  opacity:0; visibility:hidden;
  transition:opacity .6s cubic-bezier(.25,1,.5,1), visibility .6s cubic-bezier(.25,1,.5,1);
}
#fhPrivacy.on{opacity:1; visibility:visible}
#fhPrivacy p{margin:0}
#fhPrivacy a{color:var(--fh-red);text-decoration:underline;text-underline-offset:2px}
#fhPrivacy .fh-pv-btns{display:flex;align-items:center;gap:10px;margin-top:14px;flex-wrap:wrap}
/* 按钮：原站(ff-wp-tracking 插件)**不是胶囊** —— 它是一条**透明底的黑字**，
   大写、12px、无边框无圆角，悬停只把文字/下划线加重。
   ≡ .ff-wp-tracking button{background:0 0;border:none;padding:0;color:inherit;text-transform:uppercase;font-size:12px} */
#fhPrivacy button{
  font-family:"Roboto Slab",var(--ff-cjk); font-weight:400; font-size:12px;
  letter-spacing:.06em; line-height:1.5;
  color:#000; background:none; border:0; border-radius:0; padding:0;
  cursor:pointer; text-transform:uppercase;
  transition:opacity .2s ease;
}
#fhPrivacy button:hover{opacity:.6}
#fhPrivacy button.fh-pv-not{
  font-weight:300; font-size:12.6px; letter-spacing:0; text-transform:none;
  color:#111; border-bottom:1px solid rgba(17,17,17,.35); padding:0 0 2px;
}
#fhPrivacy button.fh-pv-not:hover{border-bottom-color:#111;opacity:1}
/* 窄屏: 50% 宽会把正文压成 5 行 → 整条铺满(原站手机档也是全宽) */
@media (max-width:560px){ #fhPrivacy{width:100%;max-width:none;padding:16px 16px 14px} }
