/* Luxel — 首页
   ────────────────────────────────────────────────────────────────────────
   三列横排，一屏不滚动：

     ┌────┬───────────────────────────┬──┐
     │ 标 │                    L   u  │██│  ← 字标竖排右上，
     │    │       ┌────────┐   x   e  │▓▓│     灰阶条紧贴其右、
     │ 竖 │       │ 工具卡 │      l   │▒▒│     贴住屏幕右缘
     │ 排 │       └────────┘          │░░│
     │ 文 │                          │  │
     │ ☾  │  一句话说明                │  │
     └────┴───────────────────────────┴──┘

   全页没有任何分隔线：左栏是一根浮起的胶囊柱，靠底色而非描边与画面区分。
   灰阶条与字标同高：两者是 .masthead 这个 flex 行的兄弟，stretch 对齐即可，
   不需要任何计算。字标本身为单色，灰阶只由条子承担。
   ──────────────────────────────────────────────────────────────────────── */

body{height:100dvh;display:flex;overflow:hidden}

/* ── 左侧竖栏：全页唯一保留的分隔 ── */
.rail{flex:none;width:64px;margin:14px 0 14px 14px;padding:16px 0;
  display:flex;flex-direction:column;align-items:center;justify-content:space-between;
  background:var(--surface);border-radius:var(--r-panel)}
.brand{display:block;color:inherit;text-decoration:none}
.brand-mark{width:22px;height:22px;color:var(--text);display:block}

/* 工具条：选中项把对应卡片推到堆叠最前，未选中的留在后方。 */
/* 按钮组的中心要对齐封面（缩略图）的中心，而不是左栏中心：
   卡组居中时包含下方 56px 的文字块，封面中心比卡组中心高 28px；
   左栏上 logo 22px、下面是语言 + 主题两颗键共 74px，按钮组因此比栏中心高 26px。合计 28 − 26 = 2px。 */
.tool-list{display:flex;flex-direction:column;align-items:center;gap:6px;width:100%;translate:0 -2px}
/* 工具按钮是「胶囊一律给可点元素」这条规则的例外：它里面是图标＋英文名，
   名字一长（RUBRIC）做成圆形就几乎贴到两侧。改成大圆角方形，宽度跟随左栏，
   给名字留出左右余量。 */
.tool-btn{padding:9px 0 8px;width:calc(100% - 12px);display:flex;flex-direction:column;align-items:center;gap:6px;
  background:transparent;border-color:transparent;border-radius:var(--r-card);color:var(--text-3);
  transition:background .18s, color .18s, border-color .18s}
.tool-btn svg{width:19px;height:19px}
.tool-name{font:8.5px var(--mono);letter-spacing:.06em;text-transform:uppercase;line-height:1}
.tool-btn:hover{background:var(--surface-3);border-color:transparent;color:var(--text-2)}
.tool-btn[aria-pressed=true]{background:var(--accent);border-color:var(--accent);color:var(--accent-fg)}
/* 左栏底部：语言 + 深浅模式，两颗叠在一起 */
.rail-foot{flex:none;display:flex;flex-direction:column;align-items:center;gap:6px}
.rail-foot .lang-btn{background:var(--surface-2);border-color:transparent}
#theme{flex:none;padding:0;width:34px;height:34px;display:grid;place-items:center;
  background:var(--surface-2);border-color:transparent;color:var(--text-2)}
#theme:hover{background:var(--surface-3);color:var(--text)}
#theme svg{width:15px;height:15px}

/* ── 画面 ── */
.stage{--pad:30px;flex:1 1 0;min-width:0;position:relative;
  display:flex;flex-direction:column;justify-content:center;align-items:center;
  padding:30px var(--pad)}

/* 字标 ＋ 灰阶条。绝对定位贴住 stage 的内边距框，也就是屏幕右缘。 */
.masthead{position:absolute;top:0;right:0;display:flex;align-items:stretch;gap:14px;padding-top:24px}
/* 竖排字标：不是把字母逐个堆叠，而是把正常排版整体转 90°。
   用 writing-mode 而非 transform —— 前者参与布局，元素真实占位，
   灰阶条才能靠 flex stretch 与它等高；后者不占位，得另外算尺寸。
   text-orientation 默认 mixed：拉丁字母顺时针躺倒，字顶朝右，自上而下读。

   实测 Plex Sans SemiBold 全大写 "LUXEL" 连同 -.02em 字距长 2.875 em。
   两项约束：长度取视口高的 38%（除以 2.875 得字号），厚度不超过视口宽的 13%
   —— 后者在窄屏自动收住，不必逐断点写死 px 上限（写死会让字标在高视口
   显小、矮视口显大，实测波动达 19%–62%）。 */
.wordmark{margin:0;writing-mode:vertical-rl;line-height:1;
  font-weight:600;letter-spacing:-.02em;
  font-size:min(calc(38vh / 2.875), 13vw)}
/* 公式落在右下角，与左下的说明构成对角 —— 四角各有一处落点。联系我们叠在公式上方。 */
.corner{position:absolute;right:var(--pad);bottom:30px;display:flex;flex-direction:column;align-items:flex-end;gap:10px}
.formula{font:10px var(--mono);color:var(--text-3);letter-spacing:.08em;white-space:nowrap}

/* 灰阶条：16 级自上而下由实到空，与字标同向（深色都在顶部）。
   与字标精确等长 —— 两者是同一 flex 行的兄弟，stretch 对齐即可。 */
.wedge{flex:none;width:clamp(10px,1.5vw,18px);display:flex;flex-direction:column}
.wedge i{flex:1 1 0;background:currentColor;display:block}

/* 卡片堆叠：选中的在最前，其余按列表顺序循环叠在后方，像安卓的近期应用。
   深度循环取模，所以滚动到末尾会绕回开头 —— 轮播是深度算法的副产物，
   不需要额外的边界判断。
   层间位移随视口收缩：deck 宽度只等于一张卡，扇出的部分是溢出它的，
   固定 32px 在 360px 宽的屏上会把最后一张顶出右缘。
   deck 的高度必须写死 —— 所有卡片都是绝对定位，没有一张在常规流里撑开容器。
   数值 = 缩略图（宽 / 1.5）＋ 间距 10 ＋ 文字块 46。 */
.deck{--cw:clamp(180px,20vw,250px);--step:clamp(14px,3.2vw,32px);position:relative;touch-action:pan-y;cursor:grab;
  width:var(--cw);height:calc(var(--cw) / 1.5 + 56px)}
.tool-card{position:absolute;top:0;left:0;width:100%;
  display:flex;flex-direction:column;gap:10px;text-decoration:none;color:inherit;
  transform-origin:center left;
  transform:translateX(calc(var(--d) * var(--step)))
            translateY(calc(var(--d) * -5px))
            scale(calc(1 - var(--d) * .05));
  opacity:calc(1 - var(--d) * .28);
  z-index:calc(10 - var(--d));
  /* 错峰：延迟按新深度递增，所以进到最前的那张先动，后面的依次落位，
     堆叠像被推了一下而不是整体平移。z-index 不可过渡，切换瞬间就换层，
     于是离开的卡是「滑到背后」而不是「从前面横穿」。 */
  transition:transform .46s cubic-bezier(.2,.8,.2,1),
             opacity .46s cubic-bezier(.2,.8,.2,1);
  transition-delay:calc(var(--d) * 30ms)}
/* 卡片本身没有底色（保持无框的观感），所以前卡遮不住后卡的文字。
   非最前者只留缩略图 —— 缩略图是不透明的，能正常相互遮挡，
   读起来就是一摞只露出边缘的卡。 */
.tool-card[data-front=false]{pointer-events:none}
.tool-card[data-front=false] .body{opacity:0;transition:opacity .16s}
.tool-card[data-front=true] .body{opacity:1;transition:opacity .3s ease .2s}
/* 缩略图：静态 PNG 打底，WebGL 就绪后淡入盖在上面。
   WebGL 不可用或用户偏好减少动态时，PNG 就是最终形态，不会留白。 */
.thumb{position:relative;display:block;border-radius:var(--r-card);overflow:hidden;
  background:var(--stage);transition:filter .18s,transform .18s}
/* 悬停不能降不透明度：前卡一透，堆在后面那张就从它底下显出来了。
   改用滤镜和微抬 —— 两者都不牺牲遮挡。 */
.tool-card[data-front=true]:hover .thumb{filter:brightness(.94) saturate(1.06);transform:translateY(-2px)}
.thumb img{width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;display:block}
.is-empty .thumb{display:grid;place-items:center;aspect-ratio:3/2;background:var(--surface)}
.is-empty .slot{font:13px var(--mono);letter-spacing:.14em;color:var(--text-3)}
/* Rubric 卡没有实时画面，用它自己产出的东西当缩略图：一条色阶。 */
/* Rubric 封面：上排竖立的胶囊色标横向滚动，下方 Aa 随滑块加粗。
   Plex 只打包了 400/600 两档，做不出连续字重 —— 用同色 text-stroke 加粗，
   滑块位置、描边粗细、数字读数都由同一个 --w（0→1）驱动，保证同步。 */
@property --w{syntax:"<number>";inherits:true;initial-value:0}
@property --wn{syntax:"<integer>";inherits:true;initial-value:300}
/* 底色：三团色各自沿一条路线慢慢漂，周期互质（19/23/29s），叠在一起很久才重复一次 */
@property --g1x{syntax:"<percentage>";inherits:false;initial-value:15%}
@property --g1y{syntax:"<percentage>";inherits:false;initial-value:0%}
@property --g2x{syntax:"<percentage>";inherits:false;initial-value:95%}
@property --g2y{syntax:"<percentage>";inherits:false;initial-value:100%}
@property --g3x{syntax:"<percentage>";inherits:false;initial-value:80%}
@property --g3y{syntax:"<percentage>";inherits:false;initial-value:10%}
.thumb-rubric{aspect-ratio:3/2;display:flex;flex-direction:column;justify-content:center;container-type:inline-size;
  background:radial-gradient(110% 90% at var(--g1x) var(--g1y),#ffd8a8,transparent 60%),
    radial-gradient(100% 90% at var(--g2x) var(--g2y),#a5d8ff,transparent 60%),
    radial-gradient(80% 70% at var(--g3x) var(--g3y),#fcc2d7,transparent 62%),#e5dbff;
  color:#0d0d0d;animation:rb-weight 5s ease-in-out infinite,rb-g1 19s ease-in-out infinite,rb-g2 23s ease-in-out infinite,rb-g3 29s ease-in-out infinite}
@keyframes rb-g1{0%,100%{--g1x:15%;--g1y:0%}33%{--g1x:55%;--g1y:25%}66%{--g1x:5%;--g1y:70%}}
@keyframes rb-g2{0%,100%{--g2x:95%;--g2y:100%}40%{--g2x:40%;--g2y:85%}70%{--g2x:85%;--g2y:40%}}
@keyframes rb-g3{0%,100%{--g3x:80%;--g3y:10%}30%{--g3x:30%;--g3y:5%}60%{--g3x:20%;--g3y:95%}85%{--g3x:90%;--g3y:65%}}
/* 景深：同一排色标叠三份同步滚动 —— 清晰、轻糊、重糊 —— 各用渐变遮罩只露出
   自己负责的一段：中间清晰，往两侧逐级变糊，最外缘淡出。只模糊色标本身，
   不碰底色，所以不会出现 backdrop-filter 那种带硬边的糊块。 */
.rb-strip{display:grid;flex:none;margin:-5cqw 0}
/* 遮罩挂在不动的 .rb-layer 上（挂在滚动的 track 上会跟着一起跑）；
   上下留 5cqw 让模糊有地方晕开，再用负 margin 抵掉，不改布局。 */
.rb-layer{grid-area:1/1;overflow:hidden;padding:5cqw 0}
/* 每枚色标自己跑一圈：从右缘外 100cqw 走到左缘外 -7cqw（10 枚 × 10.7cqw 间距
   刚好首尾相接），负 delay 把它们错开。位移匀速，缩放另走一条关键帧：
   走到路程一半（正好在中线）时最大，两端最小。 */
.rb-track{display:block;position:relative;height:19cqw}
.rb-track i{position:absolute;left:0;top:0;translate:100cqw 0;scale:.7;
  animation:rb-move 14s linear infinite,rb-size 14s linear infinite;animation-delay:calc(var(--k) * -1.4s)}
.rb-layer.sharp{-webkit-mask:linear-gradient(90deg,transparent 26%,#000 40%,#000 60%,transparent 74%);mask:linear-gradient(90deg,transparent 26%,#000 40%,#000 60%,transparent 74%)}
.rb-layer.soft .rb-track{filter:blur(2.5px)}
.rb-layer.soft{-webkit-mask:linear-gradient(90deg,transparent 8%,#000 18%,#000 28%,transparent 42%,transparent 58%,#000 72%,#000 82%,transparent 92%);
  mask:linear-gradient(90deg,transparent 8%,#000 18%,#000 28%,transparent 42%,transparent 58%,#000 72%,#000 82%,transparent 92%)}
.rb-layer.far .rb-track{filter:blur(6px)}
.rb-layer.far{-webkit-mask:linear-gradient(90deg,transparent,#000 5%,transparent 20%,transparent 80%,#000 95%,transparent);
  mask:linear-gradient(90deg,transparent,#000 5%,transparent 20%,transparent 80%,#000 95%,transparent)}
.rb-track i{width:6.7cqw;height:19cqw;border-radius:var(--r-pill);box-shadow:0 4px 10px -4px #0004,inset 0 0 0 1px #ffffff40}
.rb-type{display:flex;align-items:center;gap:6cqw;padding:0 9cqw;margin-top:7cqw}
.rb-aa{font:400 16cqw/1 var(--sans);-webkit-text-stroke:calc(var(--w) * 1.6px) currentColor;paint-order:stroke fill;letter-spacing:.01em}
.rb-ctl{flex:1;display:flex;flex-direction:column;gap:3cqw}
.rb-num{font:max(8px,4.2cqw) var(--mono);color:#0d0d0d99}
.rb-rail{position:relative;height:3px;border-radius:var(--r-pill);background:#0d0d0d26}
.rb-rail::before{content:"";position:absolute;inset:0 auto 0 0;width:calc(var(--w) * 100%);border-radius:inherit;background:#0d0d0d}
.rb-rail::after{content:"";position:absolute;top:50%;left:calc(var(--w) * 100%);width:13px;height:13px;margin:-6.5px 0 0 -6.5px;
  border-radius:50%;background:#fff;box-shadow:0 1px 4px #0004,0 0 0 1px #0d0d0d1a}
@keyframes rb-move{from{translate:100cqw 0}to{translate:-7cqw 0}}
@keyframes rb-size{0%,100%{scale:.7}22%,78%{scale:.82}36%,64%{scale:1.02}50%{scale:1.3}}
@keyframes rb-weight{0%,8%{--w:0;--wn:300}45%,55%{--w:1;--wn:700}92%,100%{--w:0;--wn:300}}
/* 不在最前面的卡片停掉动画，和 Rheo 的 shader 一样 */
.tool-card[data-front=false] .thumb-rubric,.tool-card[data-front=false] .rb-track i{animation-play-state:paused}
/* 减弱动态：色标用 paused 而不是 none —— 负 delay 仍然生效，停在各自错开的位置上 */
@media(prefers-reduced-motion:reduce){.thumb-rubric{animation:none;--w:.6;--wn:540}.rb-track i{animation-play-state:paused}}
/* Recast 封面：格式胶囊沿一条路线滚过，中间放大、清晰，两侧缩小、虚化（和 Rubric 同一套
   景深语法）。胶囊是磨砂 / 液态玻璃：半透明白 + backdrop 模糊提饱和 + 顶部高光与内描边。
   玻璃只有「后面有东西」才看得出来，所以胶囊后面垫一层慢速漂移的彩色光斑，
   光斑比胶囊走得慢，两者错开，玻璃里透出的颜色一直在变。 */
@property --c1x{syntax:"<percentage>";inherits:false;initial-value:10%}
@property --c1y{syntax:"<percentage>";inherits:false;initial-value:5%}
@property --c2x{syntax:"<percentage>";inherits:false;initial-value:95%}
@property --c2y{syntax:"<percentage>";inherits:false;initial-value:95%}
@property --c3x{syntax:"<percentage>";inherits:false;initial-value:85%}
@property --c3y{syntax:"<percentage>";inherits:false;initial-value:5%}
@property --q{syntax:"<integer>";inherits:true;initial-value:98}
@property --kb{syntax:"<integer>";inherits:true;initial-value:34200}
.thumb-recast{aspect-ratio:3/2;display:flex;flex-direction:column;justify-content:center;container-type:inline-size;
  background:radial-gradient(110% 90% at var(--c1x) var(--c1y),#b197fc,transparent 60%),
    radial-gradient(100% 90% at var(--c2x) var(--c2y),#96f2d7,transparent 60%),
    radial-gradient(80% 70% at var(--c3x) var(--c3y),#a5d8ff,transparent 62%),#dde1ff;
  animation:rc-m1 21s ease-in-out infinite,rc-m2 25s ease-in-out infinite,rc-m3 31s ease-in-out infinite}
@keyframes rc-m1{0%,100%{--c1x:10%;--c1y:5%}35%{--c1x:50%;--c1y:30%}70%{--c1x:5%;--c1y:80%}}
@keyframes rc-m2{0%,100%{--c2x:95%;--c2y:95%}40%{--c2x:45%;--c2y:90%}75%{--c2x:90%;--c2y:35%}}
@keyframes rc-m3{0%,100%{--c3x:85%;--c3y:5%}30%{--c3x:35%;--c3y:10%}65%{--c3x:15%;--c3y:90%}}
.rc-lane{position:relative;display:block;height:14cqw}
/* 每枚胶囊和它的彩色光晕装在同一个 slot 里：位移、缩放、景深虚化都作用在 slot 上，
   光晕永远正好垫在自己那块玻璃底下，不会错位。 */
.rc-slot{position:absolute;left:0;top:50%;margin-top:-5cqw;display:block;
  translate:100cqw 0;animation:rc-run 16s linear infinite,rc-dof 16s linear infinite;animation-delay:calc(var(--k) * -2s)}
.rc-halo{position:absolute;inset:-1.2cqw 1.5cqw;border-radius:var(--r-pill);background:var(--c);filter:blur(7px);opacity:.85}
.rc-pill{position:relative;height:10cqw;display:flex;align-items:center;gap:1.8cqw;padding:0 3.6cqw;
  border-radius:var(--r-pill);color:#16162a99;font:600 4.3cqw/1 var(--mono);letter-spacing:.02em;
  background:linear-gradient(180deg,#ffffff99,#ffffff29 55%,#ffffff47);
  -webkit-backdrop-filter:blur(6px) saturate(1.9);backdrop-filter:blur(6px) saturate(1.9);
  box-shadow:inset 0 1px 0 #ffffffeb,inset 0 -1px 0 #ffffff66,inset 0 0 0 1px #ffffff73,0 6px 14px -6px #1b1b4a40;}
/* 液态玻璃的镜面高光：贴着上沿的一道亮带 */
.rc-pill::before{content:"";position:absolute;left:14%;right:14%;top:1px;height:42%;border-radius:var(--r-pill);
  background:linear-gradient(#ffffffc0,#fff0);pointer-events:none}
.rc-pill s{width:2.6cqw;height:2.6cqw;border-radius:50%;background:var(--c);box-shadow:inset 0 .4cqw .5cqw #ffffff8c}
.rc-pill b{position:relative;font-weight:600}
@keyframes rc-run{from{translate:100cqw 0}to{translate:-100cqw 0}}
@keyframes rc-dof{0%{scale:.7;filter:blur(3px)}15%{scale:.86;filter:blur(1.2px)}30%{scale:1.22;filter:blur(0)}45%{scale:.86;filter:blur(1.2px)}58%,100%{scale:.7;filter:blur(3px)}}
.rc-row{display:flex;align-items:center;gap:5cqw;padding:0 8cqw;margin-top:8cqw;animation:rc-q 6s ease-in-out infinite}
.rc-q{flex:1;display:flex;flex-direction:column;gap:2.6cqw}
.rc-qn{font:max(8px,4cqw) var(--mono);color:#16162a99}
.rc-rail{position:relative;height:3px;border-radius:var(--r-pill);background:#16162a26}
.rc-rail::before{content:"";position:absolute;inset:0 auto 0 0;width:calc(var(--q) * 1%);border-radius:inherit;background:#16162a}
.rc-rail::after{content:"";position:absolute;top:50%;left:calc(var(--q) * 1%);width:13px;height:13px;margin:-6.5px 0 0 -6.5px;
  border-radius:50%;background:#fff;box-shadow:0 1px 4px #0004,0 0 0 1px #0d0d0d1a}
.rc-kb{min-width:8.4em;text-align:center;padding:1.8cqw 3.4cqw;border-radius:var(--r-pill);font:600 max(8px,4.2cqw)/1 var(--mono);color:#16162a;
  background:linear-gradient(180deg,#ffffffb3,#ffffff4d);-webkit-backdrop-filter:blur(6px) saturate(1.6);backdrop-filter:blur(6px) saturate(1.6);
  box-shadow:inset 0 1px 0 #ffffffeb,inset 0 0 0 1px #ffffff73,0 4px 10px -4px #1b1b4a33}
@keyframes rc-q{0%,10%{--q:98;--kb:34200}24%{--q:80;--kb:6800}45%,55%{--q:24;--kb:286}76%{--q:80;--kb:6800}90%,100%{--q:98;--kb:34200}}
/* 不在最前时整张封面停下；减弱动态时也停，负 delay 让胶囊停在错开的位置上 */
.tool-card[data-front=false] .thumb-recast,.tool-card[data-front=false] .thumb-recast *{animation-play-state:paused}
@media(prefers-reduced-motion:reduce){.thumb-recast,.thumb-recast *{animation-play-state:paused}}
/* Roll 封面：三条轨道上的玻璃片段（与 Recast 胶囊同一套材质，各自垫同色光晕），
   白色播放头从左扫到右，右下角时间码跟着走。上下两轨缩小虚化、只有中间轨清晰 —— 景深。
   底色同一套漂移网格渐变，色板换成暖调珊瑚 / 杏 / 青。
   注意：封面本身是尺寸容器，它自己的 padding / gap 不能写 cqw（会退回视口单位）。 */
@property --e1x{syntax:"<percentage>";inherits:false;initial-value:8%}
@property --e1y{syntax:"<percentage>";inherits:false;initial-value:6%}
@property --e2x{syntax:"<percentage>";inherits:false;initial-value:96%}
@property --e2y{syntax:"<percentage>";inherits:false;initial-value:94%}
@property --e3x{syntax:"<percentage>";inherits:false;initial-value:88%}
@property --e3y{syntax:"<percentage>";inherits:false;initial-value:8%}
@property --tf{syntax:"<integer>";inherits:true;initial-value:0}
.thumb-roll{aspect-ratio:3/2;display:flex;flex-direction:column;justify-content:center;container-type:inline-size;
  background:radial-gradient(110% 90% at var(--e1x) var(--e1y),#ffa8a8,transparent 60%),
    radial-gradient(100% 90% at var(--e2x) var(--e2y),#63e6be,transparent 60%),
    radial-gradient(80% 70% at var(--e3x) var(--e3y),#ffd8a8,transparent 62%),#fff0e6;
  animation:rl-m1 20s ease-in-out infinite,rl-m2 26s ease-in-out infinite,rl-m3 32s ease-in-out infinite,rl-tc 6s linear infinite}
@keyframes rl-m1{0%,100%{--e1x:8%;--e1y:6%}35%{--e1x:48%;--e1y:30%}70%{--e1x:6%;--e1y:82%}}
@keyframes rl-m2{0%,100%{--e2x:96%;--e2y:94%}40%{--e2x:42%;--e2y:88%}75%{--e2x:92%;--e2y:36%}}
@keyframes rl-m3{0%,100%{--e3x:88%;--e3y:8%}30%{--e3x:36%;--e3y:12%}65%{--e3x:14%;--e3y:88%}}
/* 时间码：和播放头同一个 6s 周期，帧数 0 → 749（30fps 下约 25 秒），由 home.js 写成 00:SS:FF */
@keyframes rl-tc{from{--tf:0}to{--tf:749}}
.rl-glass{background:linear-gradient(180deg,#ffffff99,#ffffff29 55%,#ffffff47);
  -webkit-backdrop-filter:blur(6px) saturate(1.8);backdrop-filter:blur(6px) saturate(1.8);
  box-shadow:inset 0 1px 0 #ffffffeb,inset 0 -1px 0 #ffffff66,inset 0 0 0 1px #ffffff73,0 6px 14px -6px #4a1b1b33}
.rl-tracks{position:relative;display:flex;flex-direction:column;gap:2.6cqw;padding:0 6cqw;margin-top:5cqw}   /* 给播放头顶上的小帽子留位置，上下留白才对称 */
.rl-lane{position:relative;display:block;height:8cqw}
.rl-lane.far{filter:blur(1.6px);opacity:.75;scale:.94}
.rl-clip{position:absolute;top:0;bottom:0;display:flex;align-items:center;padding:0 2cqw;border-radius:2.4cqw}
.rl-clip i{position:absolute;inset:-1cqw 1cqw;border-radius:2.4cqw;background:var(--c);filter:blur(6px);opacity:.8;z-index:-1}
.rl-clip .rl-glass{position:absolute;inset:0;border-radius:2.4cqw}
.rl-clip b{position:relative;font:600 3.2cqw/1 var(--mono);color:#2a161699;white-space:nowrap}
.rl-head{position:absolute;top:-3cqw;bottom:-3cqw;left:0;width:2px;margin-left:-1px;background:#fff;box-shadow:0 0 8px #0004;
  translate:8cqw 0;animation:rl-head 6s linear infinite}
.rl-head::before{content:"";position:absolute;top:-2.4cqw;left:50%;width:5cqw;height:3.4cqw;margin-left:-2.5cqw;
  border-radius:1.4cqw 1.4cqw 2.5cqw 2.5cqw;background:#fff;box-shadow:0 2px 6px #0004}
@keyframes rl-head{from{translate:8cqw 0}to{translate:92cqw 0}}
.rl-bar{display:flex;align-items:center;gap:2.5cqw;padding:0 6cqw;margin-top:5cqw}
.rl-play{width:9cqw;height:9cqw;border-radius:50%;display:grid;place-items:center}
.rl-play::before{content:"";margin-left:.6cqw;border-left:2.6cqw solid #2a1616;border-top:1.6cqw solid transparent;border-bottom:1.6cqw solid transparent}
.rl-tc{margin-left:auto;font:600 max(8px,4.2cqw)/1 var(--mono);color:#2a1616;padding:1.8cqw 3.4cqw;border-radius:var(--r-pill);white-space:nowrap}
.tool-card[data-front=false] .thumb-roll,.tool-card[data-front=false] .thumb-roll *{animation-play-state:paused}
@media(prefers-reduced-motion:reduce){.thumb-roll,.thumb-roll *{animation-play-state:paused}}
/* Relief 封面：一张原始截图先铺满整张卡，然后缩进画面正中，长出圆角、窗口栏和大阴影，
   背后的渐变随之露出来 —— 「把裸截图衬托成能发出去的作品图」。
   底色同一套漂移网格渐变，色板换成清爽的柠檬 / 天蓝 / 薄绿。
   封面本身是尺寸容器：它自己的 padding / gap 不写 cqw。 */
@property --v1x{syntax:"<percentage>";inherits:false;initial-value:10%}
@property --v1y{syntax:"<percentage>";inherits:false;initial-value:6%}
@property --v2x{syntax:"<percentage>";inherits:false;initial-value:94%}
@property --v2y{syntax:"<percentage>";inherits:false;initial-value:94%}
@property --v3x{syntax:"<percentage>";inherits:false;initial-value:86%}
@property --v3y{syntax:"<percentage>";inherits:false;initial-value:8%}
.thumb-relief{aspect-ratio:3/2;container-type:inline-size;
  background:radial-gradient(110% 90% at var(--v1x) var(--v1y),#ffe066,transparent 60%),
    radial-gradient(100% 90% at var(--v2x) var(--v2y),#74c0fc,transparent 60%),
    radial-gradient(80% 70% at var(--v3x) var(--v3y),#b2f2bb,transparent 62%),#eef6ff;
  animation:rf-m1 21s ease-in-out infinite,rf-m2 25s ease-in-out infinite,rf-m3 31s ease-in-out infinite}
@keyframes rf-m1{0%,100%{--v1x:10%;--v1y:6%}35%{--v1x:50%;--v1y:28%}70%{--v1x:6%;--v1y:80%}}
@keyframes rf-m2{0%,100%{--v2x:94%;--v2y:94%}40%{--v2x:44%;--v2y:88%}75%{--v2x:90%;--v2y:36%}}
@keyframes rf-m3{0%,100%{--v3x:86%;--v3y:8%}30%{--v3x:34%;--v3y:12%}65%{--v3x:14%;--v3y:88%}}
.rf-win{position:absolute;inset:0;display:flex;flex-direction:column;overflow:hidden;background:#fff;
  animation:rf-mount 7s cubic-bezier(.6,0,.25,1) infinite}
.rf-bar{flex:none;height:0;display:flex;align-items:center;gap:1cqw;padding:0 2.2cqw;overflow:hidden;background:#f6f7f9;
  border-bottom:1px solid #0000000f;animation:rf-chrome 7s cubic-bezier(.6,0,.25,1) infinite}
.rf-bar i{width:1.6cqw;height:1.6cqw;border-radius:50%;background:#ff6b6b}
.rf-bar i:nth-child(2){background:#fcc419}.rf-bar i:nth-child(3){background:#51cf66}
.rf-bar s{flex:1;max-width:34cqw;height:2.2cqw;margin-left:3cqw;border-radius:9px;background:#0000000d}
.rf-pic{flex:1;background:url(png/rheo-card.png) center/cover}
@keyframes rf-mount{
  0%,14%{inset:0;border-radius:0;box-shadow:0 0 0 0 #0000}
  42%,86%{inset:16% 15% 16% 15%;border-radius:2.6cqw;box-shadow:0 1.6cqw 5cqw -1cqw #1b2a4a59,0 0 0 1px #ffffff99}
  100%{inset:0;border-radius:0;box-shadow:0 0 0 0 #0000}}
@keyframes rf-chrome{0%,20%{height:0}46%,84%{height:5.2cqw}96%,100%{height:0}}
.tool-card[data-front=false] .thumb-relief,.tool-card[data-front=false] .thumb-relief *{animation-play-state:paused}
@media(prefers-reduced-motion:reduce){.thumb-relief,.thumb-relief *{animation-play-state:paused}}
/* Rise 封面：七根玻璃柱从底线依次长高，每根下面垫一团同色光晕（和 Recast 胶囊同一种玻璃），
   右上角读数跟着从 +0 % 数到 +382 %。只留柱子和读数，不再叠折线，保持干净。
   底色同一套漂移网格渐变，色板换成青柠 / 靛蓝 / 青。封面本身是尺寸容器：它自己的 padding / gap 不写 cqw。 */
@property --s1x{syntax:"<percentage>";inherits:false;initial-value:10%}
@property --s1y{syntax:"<percentage>";inherits:false;initial-value:6%}
@property --s2x{syntax:"<percentage>";inherits:false;initial-value:94%}
@property --s2y{syntax:"<percentage>";inherits:false;initial-value:94%}
@property --s3x{syntax:"<percentage>";inherits:false;initial-value:86%}
@property --s3y{syntax:"<percentage>";inherits:false;initial-value:8%}
@property --gr{syntax:"<integer>";inherits:true;initial-value:0}
.thumb-rise{aspect-ratio:3/2;container-type:inline-size;
  background:radial-gradient(110% 90% at var(--s1x) var(--s1y),#c0eb75,transparent 60%),
    radial-gradient(100% 90% at var(--s2x) var(--s2y),#91a7ff,transparent 60%),
    radial-gradient(80% 70% at var(--s3x) var(--s3y),#66d9e8,transparent 62%),#f1f6ff;
  animation:rs-m1 21s ease-in-out infinite,rs-m2 25s ease-in-out infinite,rs-m3 31s ease-in-out infinite,rs-count 6s ease-out infinite}
@keyframes rs-m1{0%,100%{--s1x:10%;--s1y:6%}35%{--s1x:50%;--s1y:28%}70%{--s1x:6%;--s1y:80%}}
@keyframes rs-m2{0%,100%{--s2x:94%;--s2y:94%}40%{--s2x:44%;--s2y:88%}75%{--s2x:90%;--s2y:36%}}
@keyframes rs-m3{0%,100%{--s3x:86%;--s3y:8%}30%{--s3x:34%;--s3y:12%}65%{--s3x:14%;--s3y:88%}}
@keyframes rs-count{0%,8%{--gr:0}55%,88%{--gr:382}100%{--gr:0}}
.rs-axis{position:absolute;left:9%;right:9%;bottom:18%;height:1px;background:#161b3326}
.rs-plot{position:absolute;left:9%;right:9%;top:16%;bottom:20%;display:flex;align-items:flex-end;gap:2.6cqw}
.rs-bar{position:relative;flex:1;height:var(--h);transform-origin:bottom;
  animation:rs-grow 6s cubic-bezier(.2,.9,.25,1) infinite;animation-delay:calc(var(--i) * .08s)}
.rs-bar i{position:absolute;inset:8% -8% 0;border-radius:2cqw 2cqw 1cqw 1cqw;background:var(--c);filter:blur(5px);opacity:.75}
.rs-bar b,.rs-read{background:linear-gradient(180deg,#ffffff99,#ffffff29 55%,#ffffff47);
  -webkit-backdrop-filter:blur(6px) saturate(1.8);backdrop-filter:blur(6px) saturate(1.8);
  box-shadow:inset 0 1px 0 #ffffffeb,inset 0 -1px 0 #ffffff66,inset 0 0 0 1px #ffffff73,0 6px 14px -6px #1b224a33}
.rs-bar b{position:absolute;inset:0;border-radius:2cqw 2cqw 1cqw 1cqw}
@keyframes rs-grow{0%,6%{transform:scaleY(.04)}40%,88%{transform:scaleY(1)}100%{transform:scaleY(.04)}}
.rs-read{position:absolute;right:6%;top:6%;font:600 max(8px,4cqw)/1 var(--mono);color:#161b33;
  padding:1.8cqw 3.2cqw;border-radius:var(--r-pill);white-space:nowrap}
.tool-card[data-front=false] .thumb-rise,.tool-card[data-front=false] .thumb-rise *{animation-play-state:paused}
@media(prefers-reduced-motion:reduce){.thumb-rise{animation:none;--gr:382}.rs-bar{animation:none}}
.thumb canvas{position:absolute;inset:0;width:100%;height:100%;display:block;
  opacity:0;transition:opacity .4s}
.thumb.live canvas{opacity:1}
.tool-card .body{display:flex;flex-direction:column;gap:3px}
.tool-card h2{margin:0;font-size:14px;font-weight:600;letter-spacing:-.01em}
.tool-card p{margin:0;font-size:11.5px;color:var(--text-3);line-height:1.45}

/* 说明：画面左下，与工具卡拉开层级 */
.intro{position:absolute;left:var(--pad);bottom:30px}
.blurb{margin:0;max-width:34ch;
  font-size:13px;line-height:1.75;color:var(--text-2)}
/* 点一下把邮箱复制到剪贴板；复制后短暂换成「邮箱已复制」 */
.contact{display:inline-flex;align-items:center;gap:5px;height:26px;padding:0 10px 0 9px;border:0;border-radius:var(--r-pill);
  background:var(--surface);color:var(--text-2);font:inherit;font-size:11px;cursor:pointer;white-space:nowrap}
.contact:hover{background:var(--surface-3);color:var(--text)}
.contact:focus-visible{outline:2px solid var(--text);outline-offset:2px}
.contact svg{width:12px;height:12px;flex:none}
.contact.copied{color:var(--text)}
.contact .email{font-family:var(--mono);user-select:all}

@media(max-width:860px){
  .stage{--pad:22px;padding:26px var(--pad)}
  .intro{bottom:26px}
  .blurb{font-size:12.5px;max-width:32ch}
  .corner{bottom:26px}
  .masthead{gap:10px}
  .masthead{padding-top:18px}
}
@media(max-width:560px){
  .rail{width:56px;padding:14px 0}
  .tool-name{font-size:7.5px}
  .rail-text{display:none}
  .formula{display:none}
  /* 手机上一行放不下说明和联系我们：说明抬到按钮上方，按钮独占右下角 */
  .intro{bottom:calc(26px + 26px + 12px)}
  .tools{padding-top:12px}
}
/* 矮窗口：一屏放不下就别硬撑 */
@media(max-height:480px){
  body{height:auto;min-height:100dvh;overflow:auto}
  /* 静态排布后仍要贴住屏幕右缘，用负边距抵掉 stage 的内边距 */
  .masthead{position:static;align-self:stretch;justify-content:flex-end;margin:0 calc(var(--pad) * -1) 18px 0}
  .intro{position:static;margin-top:20px}
  .corner{position:static;align-self:flex-end;margin-top:14px}
  .stage{justify-content:flex-start}
}
@media(prefers-reduced-motion:reduce){*{transition:none!important}}

/* 拉丁字母的语言：一句话比中文长得多，放宽说明文字，免得两句折成四行 */
:is(:lang(en),:lang(fr)) .blurb{max-width:52ch}
@media(max-width:860px){:is(:lang(en),:lang(fr)) .blurb{max-width:min(52ch,calc(100% - var(--pad) * 2))}}
