/* =============================================================================
   MD3 组件库 · beta/1.0 · 全量样式（合并版，一次请求）
   由 tools/md3-split.mjs 从源码生成，改样式请改源文件后重跑，别直接改这里
   ============================================================================= */

/* ===================== core/fonts.css ===================== */
/* =============================================================================
   字体 + 图标 · @font-face（绝对地址，不依赖 JS 也能生效）

   文字：思源黑体 Noto Sans SC  300 / 400 / 500 / 700
   图标：Material Symbols Rounded（可变字体，FILL / wght / GRAD / opsz 四轴）

   下面这些地址就是部署地址，任何站点 <link> 本文件即可跨域取到（CF 自动带 CORS 头）。
   换自己的字体：把每处的 https://optimize-material3-web.pages.dev 换成你的域名；
   或者改用 using_font/fonts.js 的 useFonts({ base:"..." }) —— 它注入的 <style>
   在文档最后，同族同字重的声明以后者为准，会盖住这里。
   ============================================================================= */
@font-face{font-family:'Noto Sans SC';font-style:normal;font-weight:300;font-display:swap;
  src:url(https://optimize-material3-web.pages.dev/using_font/NotoSansSC-Light.ttf) format('truetype');}
@font-face{font-family:'Noto Sans SC';font-style:normal;font-weight:400;font-display:swap;
  src:url(https://optimize-material3-web.pages.dev/using_font/NotoSansSC-Regular.ttf) format('truetype');}
@font-face{font-family:'Noto Sans SC';font-style:normal;font-weight:500;font-display:swap;
  src:url(https://optimize-material3-web.pages.dev/using_font/NotoSansSC-Medium.ttf) format('truetype');}
@font-face{font-family:'Noto Sans SC';font-style:normal;font-weight:700;font-display:swap;
  src:url(https://optimize-material3-web.pages.dev/using_font/NotoSansSC-Bold.ttf) format('truetype');}

@font-face{font-family:'Material Symbols Rounded';font-style:normal;font-weight:400;font-display:block;
  src:url(https://optimize-material3-web.pages.dev/using_font/MaterialSymbolsRounded.woff2) format('woff2');}

/* 两个变量名：组件里所有 font-family 都读它们，覆盖即可换字体 */
:root{
  --md-font-text:'Noto Sans SC',system-ui,sans-serif;
  --md-font-icon:'Material Symbols Rounded';
}

/* ===================== core/tokens.css ===================== */
/* 设计令牌：颜色 / 形状 / 动效 / 禁用灰。全部是 CSS 变量，覆盖即可换肤 */
:root{
  --md-primary:#6750A4;--md-on-primary:#FFFFFF;--md-primary-container:#EADDFF;--md-on-primary-container:#21005D;
  --md-secondary:#625B71;--md-on-secondary:#FFFFFF;--md-secondary-container:#E8DEF8;--md-on-secondary-container:#1D192B;
  --md-tertiary:#7D5260;--md-on-tertiary:#FFFFFF;--md-tertiary-container:#FFD8E4;--md-on-tertiary-container:#31111D;
  --md-error:#B3261E;--md-on-error:#FFFFFF;--md-error-container:#F9DEDC;--md-on-error-container:#410E0B;
  --md-background:#FFFBFE;--md-on-background:#1C1B1F;
  --md-surface:#FFFBFE;--md-on-surface:#1C1B1F;
  --md-surface-variant:#E7E0EC;--md-on-surface-variant:#49454F;
  --md-surface-container-lowest:#FFFFFF;--md-surface-container-low:#F7F2FA;--md-surface-container:#F3EDF7;--md-surface-container-high:#E6E1E5;--md-surface-container-highest:#ECE6F0;
  --md-outline:#79747E;--md-outline-variant:#CAC4D0;
  /* 无饱和度的浅灰：开关未开启时的轨道用它。surface-container-highest 是从种子色算出来的，
     带一点点色相（换主题色就跟着偏紫 / 偏粉），这里要的是纯灰，所以单列一个不跟主题走的常量 */
  --md-neutral-hi:#E6E6E6;
  --md-inverse-surface:#313033;--md-inverse-on-surface:#F4EFF4;--md-inverse-primary:#D0BCFF;
  --md-shadow:#000000;--md-scrim:#000000;
  --md-corner:12px;--md-corner-small:8px;--md-corner-large:16px;--md-corner-xl:28px;
  --md-motion-short:150ms;--md-motion-medium:250ms;--md-easing:cubic-bezier(0.2,0,0,1);
}
body.dark{
  --md-primary:#D0BCFF;--md-on-primary:#381E72;--md-primary-container:#4F378B;--md-on-primary-container:#EADDFF;
  --md-secondary:#CCC2DC;--md-on-secondary:#332D41;--md-secondary-container:#4A4458;--md-on-secondary-container:#E8DEF8;
  --md-tertiary:#EFB8C8;--md-on-tertiary:#492532;--md-tertiary-container:#633B48;--md-on-tertiary-container:#FFD8E4;
  --md-error:#F2B8B5;--md-on-error:#601410;--md-error-container:#8C1D18;--md-on-error-container:#F9DEDC;
  --md-background:#1C1B1F;--md-on-background:#E6E1E5;
  --md-surface:#1C1B1F;--md-on-surface:#E6E1E5;
  --md-surface-variant:#49454F;--md-on-surface-variant:#CAC4D0;
  --md-surface-container-lowest:#141218;--md-surface-container-low:#1D1B20;--md-surface-container:#211F26;--md-surface-container-high:#2B2930;--md-surface-container-highest:#36343B;
  --md-outline:#938F99;--md-outline-variant:#49454F;
  --md-neutral-hi:#373737;
  --md-inverse-surface:#E6E1E5;--md-inverse-on-surface:#313033;--md-inverse-primary:#6750A4;
}
/* ===== 禁用态（Disabled）=====
   MD3 的禁用只有两种颜色：内容 = on-surface @38%、容器 = on-surface @12%。
   两个值都用 color-mix 从当前主题的 on-surface 现推 —— 换主题色 / 切深浅时会自己跟着走，
   不需要为每套主题各写一份。
   标记口径：
     · 表单控件（button / input / select）直接用原生 :disabled，顺带真的不能点、不能聚焦；
     · 非表单元素（纸片是 <span>、列表项 / 菜单项是 <div>）补一个 .is-disabled 类；
     · 外壳容器（输入框 / 下拉 / 滑块 / 步进器）也挂 .is-disabled —— 真正被禁的是它里面那个 input，
       外面这层负责把描边、标签一起调灰。 */
:root{
  /* 两个灰都是「没有饱和度」的纯灰（#000 派生），不是主题那套带色相的灰；
     而且是**不透明**的色值 —— 之前用 on-surface 的 38% / 12% 半透明色，会透出底下的卡片底色
     （底色本身有主题色），看着还是偏紫 / 偏粉；半透明还让滑块的圆形拇指把轨道透出来，
     像「轨道压在圆上面」。写成实色就没有这两个问题，在任何背景上都一样灰。 */
  --md-dis-content:#A9A9A9;
  --md-dis-container:#E4E4E4;
}
body.dark{
  --md-dis-content:#757575;
  --md-dis-container:#373737;
}

/* ===================== core/base.css ===================== */
/* 基础：重置 + 页面底色 + 文字 + 换肤过渡兜底 + 滚动条 */
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent;}
html{scroll-behavior:smooth;}
body{
  font-family:var(--md-font-text);
  background:var(--md-background);color:var(--md-on-background);
  min-height:100vh;
  transition:background var(--md-motion-medium) var(--md-easing),color var(--md-motion-medium) var(--md-easing);
  /* 阴影：无饱和度（纯黑派生）。想要主题色阴影就把上面换成 color-mix(in srgb, var(--md-primary) 25%, transparent) */
  --md-shadow-color:rgba(0,0,0,.3);
  --md-shadow-scale:1;
  --md-elev1:0 calc(1px*var(--md-shadow-scale)) calc(2px*var(--md-shadow-scale)) var(--md-shadow-color),
             0 calc(1px*var(--md-shadow-scale)) calc(3px*var(--md-shadow-scale)) calc(1px*var(--md-shadow-scale)) color-mix(in srgb, var(--md-shadow-color) 50%, transparent);
  --md-elev2:0 calc(1px*var(--md-shadow-scale)) calc(2px*var(--md-shadow-scale)) var(--md-shadow-color),
             0 calc(2px*var(--md-shadow-scale)) calc(6px*var(--md-shadow-scale)) calc(2px*var(--md-shadow-scale)) color-mix(in srgb, var(--md-shadow-color) 50%, transparent);
  --md-elev3:0 calc(4px*var(--md-shadow-scale)) calc(8px*var(--md-shadow-scale)) calc(3px*var(--md-shadow-scale)) color-mix(in srgb, var(--md-shadow-color) 50%, transparent),
             0 calc(1px*var(--md-shadow-scale)) calc(3px*var(--md-shadow-scale)) var(--md-shadow-color);
  /* 卡片专用：更扩散、更明显 */
  --md-elev-card:0 calc(6px*var(--md-shadow-scale)) calc(18px*var(--md-shadow-scale)) calc(4px*var(--md-shadow-scale)) color-mix(in srgb, var(--md-shadow-color) 55%, transparent),
                 0 calc(2px*var(--md-shadow-scale)) calc(8px*var(--md-shadow-scale)) calc(1px*var(--md-shadow-scale)) var(--md-shadow-color);
}
/* 阴影总开关：body.no-shadow 时全部关闭 */
body.no-shadow,body.no-shadow *{box-shadow:none !important;}

/* 换肤过渡：切主题色 / 切深浅的瞬间，由 JS 在 body 上挂 .theme-anim（约 320ms）。
   整条用 :where() 包着 —— 选择器权重为 0，比任何组件选择器（.btn / .card …）都低，
   所以组件自己写了 transition 的照旧用自己的，这条只兜住「平时没写 transition 的元素」
   （卡片、hero、标题、logo 这些）：否则换肤时它们是「啪」一下跳色，只有一部分组件在渐变 */
:where(body.theme-anim,body.theme-anim *){
  transition:background-color var(--md-motion-medium) var(--md-easing),
             color var(--md-motion-medium) var(--md-easing),
             border-color var(--md-motion-medium) var(--md-easing),
             fill var(--md-motion-medium) var(--md-easing),
             box-shadow var(--md-motion-medium) var(--md-easing);
}
/* ===== Scrollbar ===== */
::-webkit-scrollbar{width:10px;height:10px;}
::-webkit-scrollbar-track{background:var(--md-surface);}
::-webkit-scrollbar-thumb{background:var(--md-outline-variant);border-radius:10px;border:2px solid var(--md-surface);}
::-webkit-scrollbar-thumb:hover{background:var(--md-outline);}


/* ===================== core/icons.css ===================== */
/* 图标元素：字体文件 MaterialSymbolsRounded.woff2 的 @font-face 在 core/fonts.css */
.material-symbols-rounded{font-variation-settings:'FILL' 0,'wght' 400,'GRAD' 0,'opsz' 24;display:inline-block;vertical-align:middle;user-select:none;}
.material-symbols-rounded.fill{font-variation-settings:'FILL' 1;}
.material-symbols-rounded{font-family:var(--md-font-icon);}

/* ===================== core/choice.css ===================== */
/* Checkbox / Radio 共用底座与态层（两家都要，所以单独一层） */
/* ===== Checkbox / Radio / Switch ===== */
.checkbox,.radio{display:inline-flex;align-items:center;gap:8px;cursor:pointer;font-size:0.9rem;color:var(--md-on-surface);user-select:none;}
.checkbox input,.radio input{position:absolute;opacity:0;width:0;height:0;}
/* ---- 态层 state layer ----
   MD3 选择控件的四大件是：容器 / 图标 / 态层 / 触摸目标，之前整块漏了态层。
   规格：40dp 圆，压在盒子下面；悬停 8%、聚焦与按压 10%；
        未选中用 on-surface、选中用 primary（MD3 state-layer 色 token）。
   只在交互时淡入淡出 —— 选中那一刻不会从中心炸出一圈，那是安卓水波，不做。 */
.checkbox .box::before,.radio .circle::before{
  content:'';position:absolute;left:50%;top:50%;width:40px;height:40px;margin:-20px 0 0 -20px;
  border-radius:50%;background:var(--md-on-surface);opacity:0;pointer-events:none;
  transition:opacity 100ms linear,background-color 100ms linear;
}
.checkbox:hover .box::before,.radio:hover .circle::before{opacity:.08;}
.checkbox:active .box::before,.radio:active .circle::before{opacity:.10;}
.checkbox input:focus-visible~.box::before,.radio input:focus-visible~.circle::before{opacity:.10;}
.checkbox input:checked~.box::before,.radio input:checked~.circle::before{background:var(--md-primary);}

/* ===================== core/demo-shell.css ===================== */
/* 演示壳：页面骨架 / 示例卡片 / 代码块 / 禁用行标签。组件本身不依赖这一层 */
/* ===== Main ===== */
main{max-width:1200px;margin:0 auto;padding:32px 24px 80px;}
.hero{text-align:center;margin-bottom:40px;}
.hero h1{font-size:clamp(1.75rem,4vw,2.5rem);font-weight:700;color:var(--md-on-background);margin-bottom:8px;}
.hero p{color:var(--md-on-surface-variant);font-size:1rem;}

/* ===== Demo Cards ===== */
.demo-card{
  background:var(--md-surface);
  border:2px solid var(--md-outline-variant);
  border-radius:var(--md-corner-xl);
  padding:24px;margin-bottom:24px;
  box-shadow:0 2px 6px rgba(0,0,0,.05);
  transition:background var(--md-motion-medium),border-color var(--md-motion-medium);
}
.demo-head{margin-bottom:20px;}
.demo-title{font-size:1.2rem;font-weight:700;color:var(--md-on-surface);margin-bottom:4px;}
.demo-desc{font-size:0.85rem;color:var(--md-on-surface-variant);}
.demo-preview{
  background:var(--md-surface-container-low);
  border-radius:var(--md-corner);padding:24px;
  margin-bottom:16px;
  display:flex;flex-wrap:wrap;gap:16px;align-items:center;
}
.code-wrap{position:relative;}
.code-wrap pre{
  background:var(--md-surface-container-highest);
  border-radius:var(--md-corner);padding:16px;
  overflow-x:auto;font-family:'Consolas','Monaco',monospace;font-size:0.8rem;
  line-height:1.6;color:var(--md-on-surface);
  margin:0;
}
.copy-btn{
  position:absolute;top:8px;right:8px;
  width:32px;height:32px;border-radius:50%;border:none;cursor:pointer;
  background:var(--md-surface-container);color:var(--md-on-surface-variant);
  display:flex;align-items:center;justify-content:center;
  transition:background var(--md-motion-short);
}
.copy-btn:hover{background:var(--md-surface-container-high);color:var(--md-on-surface);}
.copy-btn .material-symbols-rounded{font-size:18px;}
.copy-btn.copied{color:var(--md-primary);}

/* ===== 缩放性演示的控制条 ===== */
.rz-demo{display:flex;flex-direction:column;gap:14px;align-items:flex-start;}
.rz-ctl{display:flex;align-items:center;gap:14px;flex-wrap:wrap;}
.rz-ctl-label{font-size:0.8rem;color:var(--md-on-surface-variant);}
.rz-seg{display:inline-flex;border:2px solid var(--md-outline);border-radius:999px;overflow:hidden;}
.rz-seg-btn{
  border:none;background:transparent;color:var(--md-on-surface-variant);
  font-family:inherit;font-size:0.75rem;padding:5px 14px;cursor:pointer;
  transition:background var(--md-motion-short),color var(--md-motion-short);
}
.rz-seg-btn.is-on{background:var(--md-primary-container);color:var(--md-on-primary-container);}

.demo-card.exp{border-style:dashed;border-color:var(--md-tertiary);}
.group-head{
  display:flex;align-items:center;gap:12px;
  margin:44px 0 16px;font-size:1.05rem;font-weight:700;color:var(--md-on-background);
}
.group-head::after{content:'';flex:1;height:1px;background:var(--md-outline-variant);}
.group-head .exp-badge{margin-left:0;}

/* 预览区里「禁用」那一栏：flex-basis:100% 让它整行另起，上面压一条细分隔线 */
.dp-dis-row{
  flex-basis:100%;display:flex;flex-wrap:wrap;gap:16px;align-items:center;
  padding-top:18px;margin-top:2px;border-top:1px solid var(--md-outline-variant);
}
.dp-dis{
  flex-shrink:0;display:inline-flex;align-items:center;gap:6px;
  font-size:0.72rem;font-weight:700;letter-spacing:.08em;
  color:var(--md-on-surface-variant);white-space:nowrap;
}
.dp-dis::before{content:'';width:6px;height:6px;border-radius:50%;background:var(--md-dis-content);}

/* ===================== components/app-bar.css ===================== */
/* ===== Top App Bar ===== */
.top-app-bar{
  position:sticky;top:0;z-index:100;
  display:flex;align-items:center;gap:12px;
  padding:12px 20px;padding-top:calc(12px + env(safe-area-inset-top,0px));
  background:var(--md-surface-container);backdrop-filter:blur(8px);
  border-bottom:1px solid var(--md-outline-variant);
  transition:background var(--md-motion-medium);
}
.app-logo{width:40px;height:40px;border-radius:var(--md-corner);background:var(--md-primary-container);display:flex;align-items:center;justify-content:center;flex-shrink:0;}
.app-logo .material-symbols-rounded{font-size:24px;color:var(--md-on-primary-container);font-variation-settings:'FILL' 1;}
.app-title{font-size:1.25rem;font-weight:700;color:var(--md-on-surface);}
.top-spacer{flex:1;}
.theme-controls{display:flex;align-items:center;gap:8px;}
/* .seed-color-btn（右上角那颗取色圆）的样式在下面「主题色切换组件」那一节 ——
   它和组件里的 .ts-custom 是同一个东西，合在一起写，两边才不会各长一样 */
.icon-btn{
  width:40px;height:40px;border-radius:50%;border:none;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
  background:transparent;color:var(--md-on-surface-variant);
  transition:background var(--md-motion-short),color var(--md-motion-short);
}
.icon-btn:hover{background:var(--md-surface-container-high);color:var(--md-on-surface);}
.icon-btn .material-symbols-rounded{font-size:22px;}


/* ===================== components/button.css ===================== */
/* ===== Buttons ===== */
.btn{border:none;border-radius:100px;padding:10px 24px;font-size:0.9rem;font-weight:500;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:inherit;transition:all var(--md-motion-short) var(--md-easing);position:relative;overflow:hidden;text-decoration:none;}
.btn .material-symbols-rounded{font-size:18px;}
.btn-filled{background:var(--md-primary);color:var(--md-on-primary);box-shadow:var(--md-elev1);}
.btn-filled:hover{box-shadow:var(--md-elev2);}
.btn-filled:active{transform:scale(.97);}
.btn-tonal{background:var(--md-secondary-container);color:var(--md-on-secondary-container);}
.btn-outlined{background:transparent;color:var(--md-primary);border:2px solid var(--md-on-surface-variant);}
.btn-outlined:hover{background:var(--md-primary-container);border-color:var(--md-primary);}
.btn-text{background:transparent;color:var(--md-primary);padding:10px 12px;}
.btn-text:hover{background:var(--md-primary-container);}
.btn-elevated{background:var(--md-surface-container-low);color:var(--md-primary);box-shadow:var(--md-elev1);}

/* --- Buttons --- */
.btn:disabled{box-shadow:none;cursor:default;pointer-events:none;}
.btn-filled:disabled,.btn-tonal:disabled,.btn-elevated:disabled{background:var(--md-dis-container);color:var(--md-dis-content);}
.btn-outlined:disabled{background:transparent;border-color:var(--md-dis-container);color:var(--md-dis-content);}
.btn-text:disabled{background:transparent;color:var(--md-dis-content);}

/* ===================== components/fab.css ===================== */
/* ===== FAB ===== */
.fab{width:56px;height:56px;border-radius:16px;border:none;cursor:pointer;background:var(--md-primary-container);color:var(--md-on-primary-container);display:inline-flex;align-items:center;justify-content:center;box-shadow:var(--md-elev3);transition:all var(--md-motion-short) var(--md-easing);font-family:inherit;}
.fab:hover{transform:translateY(-2px);}
.fab .material-symbols-rounded{font-size:24px;font-variation-settings:'FILL' 1;}
.fab-sm{width:40px;height:40px;border-radius:12px;}
.fab-sm .material-symbols-rounded{font-size:20px;}
.fab-lg{width:96px;height:96px;border-radius:28px;}
.fab-lg .material-symbols-rounded{font-size:36px;}
.fab-ext{width:auto;height:56px;padding:0 20px;border-radius:16px;gap:12px;font-weight:500;font-size:0.9rem;}

/* --- FAB --- */
.fab:disabled{background:var(--md-dis-container);color:var(--md-dis-content);box-shadow:none;cursor:default;pointer-events:none;}

/* ===================== components/icon-button.css ===================== */
/* ===== Icon Button ===== */
.iconbtn{width:40px;height:40px;border-radius:50%;border:none;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;background:transparent;color:var(--md-on-surface-variant);transition:all var(--md-motion-short);}
.iconbtn:hover{background:var(--md-surface-container-high);}
.iconbtn .material-symbols-rounded{font-size:22px;}
.iconbtn-filled{background:var(--md-primary);color:var(--md-on-primary);}
.iconbtn-filled:hover{background:var(--md-primary);filter:brightness(1.1);}
.iconbtn-tonal{background:var(--md-secondary-container);color:var(--md-on-secondary-container);}
.iconbtn-outlined{border:2px solid var(--md-on-surface-variant);color:var(--md-primary);}

/* --- Icon Button --- */
.iconbtn:disabled{background:transparent;color:var(--md-dis-content);cursor:default;pointer-events:none;}
.iconbtn-filled:disabled,.iconbtn-tonal:disabled{background:var(--md-dis-container);color:var(--md-dis-content);}
.iconbtn-outlined:disabled{border-color:var(--md-dis-container);}

/* ===================== components/chip.css ===================== */
/* ===== Chips ===== */
.chip{display:inline-flex;align-items:center;gap:8px;padding:7px 15px;border-radius:8px;font-size:0.85rem;font-weight:500;cursor:pointer;border:2px solid var(--md-outline);background:transparent;color:var(--md-on-surface-variant);transition:all var(--md-motion-short);font-family:inherit;}
.chip:hover{background:var(--md-surface-container-high);}
.chip .material-symbols-rounded{font-size:18px;}
.chip-assist{color:var(--md-primary);}
.chip-filter{border-color:var(--md-outline);}
.chip-filter.selected{background:var(--md-primary-container);color:var(--md-on-primary-container);border-color:var(--md-primary-container);}
.chip-input{background:var(--md-surface-container-high);}
.chip-suggestion{background:var(--md-surface-container-high);color:var(--md-on-surface);}

/* --- Chips --- */
.chip:disabled,.chip.is-disabled{background:transparent;border-color:var(--md-dis-container);color:var(--md-dis-content);cursor:default;pointer-events:none;}

/* ===================== components/text-field.css ===================== */
/* ===== Text Field ===== */
.text-field{position:relative;display:flex;flex-direction:column;width:280px;}
.text-field input,.text-field textarea{
  width:100%;padding:13px 15px;border:2px solid var(--md-outline);border-radius:var(--md-corner);
  background:var(--md-surface-container-low);color:var(--md-on-surface);font-size:1rem;font-family:inherit;
  transition:border-color var(--md-motion-short);outline:none;
}
.text-field input:focus,.text-field textarea:focus{border-color:var(--md-primary);border-width:3px;padding:12px 14px;}
.text-field label{position:absolute;left:16px;top:14px;color:var(--md-on-surface-variant);font-size:1rem;pointer-events:none;transition:all var(--md-motion-short);background:var(--md-surface-container-low);padding:0 4px;}
.text-field input:focus~label,.text-field input:not(:placeholder-shown)~label{top:-8px;font-size:0.75rem;color:var(--md-primary);}
.text-field textarea{min-height:100px;resize:vertical;}
.text-field textarea:focus~label,.text-field textarea:not(:placeholder-shown)~label{top:-8px;font-size:0.75rem;color:var(--md-primary);}
/* 占位提示统一用 on-surface-variant，深浅色下都清晰（旧字段用空格占位，不受影响） */
.text-field input::placeholder,.text-field textarea::placeholder{color:var(--md-on-surface-variant);opacity:1;}
/* 变体：标签固定在顶部（不随聚焦移动），中间保留占位提示 —— 上下同时有字 */
.text-field.up-label label{top:-8px;font-size:0.75rem;}
/* 带尾部图标的输入框（密码可见性切换）：右侧留出按钮位，聚焦边框加粗时同步回退 1px */
.text-field.has-trailing input{padding-right:47px;}
.text-field.has-trailing input:focus{padding-right:46px;}
.tf-trailing{position:absolute;right:8px;top:50%;transform:translateY(-50%);width:40px;height:40px;padding:0;border:none;
  background:transparent;color:var(--md-on-surface-variant);border-radius:50%;display:grid;place-items:center;cursor:pointer;
  transition:background var(--md-motion-short),color var(--md-motion-short);}
.tf-trailing:hover{background:var(--md-surface-container-high);}
.tf-trailing:active{background:var(--md-surface-container-highest);}
.tf-trailing .material-symbols-rounded{font-size:20px;}
/* 可自由缩放的多行输入框：本体就是缩放盒 .rz-box（宽高都能拖），内部 textarea 铺满；
   复原文本自身的交互（可选中 / 可滚动），别被 .rz-box 的 user-select:none、touch-action:none 吃掉 */
.text-field.textarea-resizer{width:280px;height:120px;}
.text-field.textarea-resizer textarea{
  flex:1;min-height:0;resize:none;
  user-select:text;-webkit-user-select:text;touch-action:auto;cursor:text;
}

/* --- Text Field / Textarea --- */
.text-field.is-disabled{pointer-events:none;}
.text-field.is-disabled input,.text-field.is-disabled textarea{border-color:var(--md-dis-container);color:var(--md-dis-content);cursor:default;}
.text-field.is-disabled label,
.text-field.is-disabled .tf-trailing{color:var(--md-dis-content);}
.text-field.is-disabled input::placeholder,
.text-field.is-disabled textarea::placeholder{color:var(--md-dis-content);}
.text-field.is-disabled input:focus~label,
.text-field.is-disabled input:not(:placeholder-shown)~label,
.text-field.is-disabled textarea:focus~label,
.text-field.is-disabled textarea:not(:placeholder-shown)~label{color:var(--md-dis-content);}

/* ===================== components/select.css ===================== */
/* ===== Select 下拉选择 =====
   demo 里写的仍然是原生 <select>（复制出去就是一份干净、能直接用的老写法），
   显示层由本页底部脚本增强出来：
     .select-field > select      —— 视觉上收起来，继续承载 value / change / 表单语义
     .select-field > .sel-btn    —— 可见的触发区（填充式，和输入框同一套描边圆角）
     .select-field > .sel-menu   —— 展开的选项面板（容器色 / 阴影跟 Dialog 同一套，圆角收到 12px）
   没有脚本时退化成上面那份原生样式，照常能用。见底部 initSelect() */
.select-field{position:relative;width:280px;}
.select-field select{
  width:100%;padding:13px 39px 13px 15px;border:2px solid var(--md-outline);border-radius:var(--md-corner);
  background:var(--md-surface-container-low);color:var(--md-on-surface);font-size:1rem;font-family:inherit;cursor:pointer;outline:none;appearance:none;
}
.select-field select:focus{border-color:var(--md-primary);border-width:3px;padding:12px 38px 12px 14px;}
.select-field::after{content:'arrow_drop_down';position:absolute;right:12px;top:50%;transform:translateY(-50%);color:var(--md-on-surface-variant);font-family:'Material Symbols Rounded';font-size:22px;pointer-events:none;}
/* 被脚本接管后：原生的箭头伪元素和原生框一起退场，改由 .sel-btn 呈现 */
.select-field.is-enhanced::after{display:none;}
/* visibility:hidden 顺带把它移出 tab 顺序 —— 键盘该停在 .sel-btn 上，不然一个控件两个焦点 */
.select-field.is-enhanced > select{position:absolute;width:1px;height:1px;margin:0;padding:0;border:0;opacity:0;visibility:hidden;pointer-events:none;}

.sel-btn{
  display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;box-sizing:border-box;
  padding:13px 15px;border:2px solid var(--md-outline);border-radius:var(--md-corner);
  background:var(--md-surface-container-low);color:var(--md-on-surface);
  font-size:1rem;font-family:inherit;text-align:left;cursor:pointer;outline:none;
  transition:border-color var(--md-motion-short) var(--md-easing),background var(--md-motion-short) var(--md-easing);
}
.sel-btn:hover{background:var(--md-surface-container);}
.sel-btn:focus-visible{border-color:var(--md-primary);}
.select-field.is-open .sel-btn{border-color:var(--md-primary);}
.sel-value{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.sel-arrow{font-size:22px;color:var(--md-on-surface-variant);flex-shrink:0;
  transition:transform var(--md-motion-short) var(--md-easing),color var(--md-motion-short) var(--md-easing);}
.select-field.is-open .sel-arrow{transform:rotate(180deg);color:var(--md-primary);}

/* 展开的面板：容器色 / 阴影跟 Dialog 同一套（surface-container-high + elev3，官方这两个浮层本来就一个样），
   圆角比 Dialog 收两档（--md-corner 12px）—— 这种「窄、且高只有选项列表那么点」的面板，
   大圆角会把四个角连起来。
   上下不加 padding：选项直接顶到面板边缘，否则会多出一条空缝；
   面板自己 overflow 裁剪，所以首尾两项的 hover 高亮会被圆角切齐，不会方角戳出去 */
.sel-menu{
  position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:40;
  box-sizing:border-box;padding:0;max-height:264px;overflow-y:auto;
  background:var(--md-surface-container-high);border-radius:var(--md-corner);
  box-shadow:var(--md-elev3);
  opacity:0;visibility:hidden;transform:translateY(-6px) scale(.96);transform-origin:top center;
  transition:opacity var(--md-motion-short) var(--md-easing),transform var(--md-motion-short) var(--md-easing),
             visibility 0s var(--md-motion-short);
}
.select-field.is-open .sel-menu{opacity:1;visibility:visible;transform:none;
  transition:opacity var(--md-motion-short) var(--md-easing),transform var(--md-motion-short) var(--md-easing),visibility 0s 0s;}

/* 选项：48px 高、左右 16px（MD3 Menu 的 item 规格），hover / 键盘高亮走 8% 的 state layer */
.sel-opt{display:flex;align-items:center;gap:12px;height:48px;padding:0 16px;box-sizing:border-box;
  font-size:0.95rem;color:var(--md-on-surface);cursor:pointer;transition:background var(--md-motion-short);}
.sel-opt:hover,.sel-opt.is-active{background:color-mix(in srgb, var(--md-on-surface) 8%, transparent);}
.sel-opt[aria-selected="true"]{color:var(--md-primary);font-weight:500;}
.sel-opt-text{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.sel-opt-check{font-size:18px;color:var(--md-primary);opacity:0;flex-shrink:0;transition:opacity var(--md-motion-short);}
.sel-opt[aria-selected="true"] .sel-opt-check{opacity:1;}

/* --- Select 下拉 ---
   触发区那个「框」要留着：底色照旧（surface-container-low），只把 2px 描边换成禁用灰、文字调灰。
   之前把描边设成 transparent + 底色改成灰，整个触发区就糊成一块没有边框的灰块了 */
.select-field.is-disabled{pointer-events:none;}
.select-field.is-disabled .sel-btn{border-color:var(--md-dis-container);color:var(--md-dis-content);}
.select-field.is-disabled .sel-arrow{color:var(--md-dis-content);}
.select-field.is-disabled > select{border-color:var(--md-dis-container);color:var(--md-dis-content);cursor:default;}

/* ===================== components/checkbox.css ===================== */
/* ===========================================================================
   Checkbox / Radio —— 几何照安卓原版，动效照实机观感
   几何来源（原文件本身，不是二手描述）：
     material-components-android / checkbox
       drawable/mtrl_checkbox_button.xml                       （状态选择器）
       drawable/mtrl_checkbox_button_unchecked_checked.xml     （勾选 · 容器）
       drawable/mtrl_checkbox_button_checked_unchecked.xml     （取消 · 容器）
       drawable/mtrl_checkbox_button_icon_unchecked_checked.xml（勾选 · 对勾）
       drawable/mtrl_checkbox_button_icon_checked_unchecked.xml（取消 · 对勾）
       drawable/mtrl_ic_checkbox_checked.xml
       drawable/mtrl_ic_checkbox_unchecked.xml
       drawable/mtrl_ic_check_mark.xml
       res/values/strings.xml                                  （全部 pathData）

   几何（32×32 画布）
     方框占 7..25 ⇒ 18px 方框（MD3 ContainerSize）/ 圆角 2 / 描边 2
     未选中只画描边环；选中时在环之上再叠一层 18px 实心圆角方块；
     对勾是独立图层，折线 (10,17) → (14,21) → (22,13)，2 宽、平头端帽
     ⇒ 换算到 18px 方框就是 M3.7 9.3 L7 12.6 L14.3 5.3

   动画 —— 以实机观感为准（上面 AVD 里的数值只作几何参考，不照搬其数值）
     按下   整个控件先缩一下（scale .9）再弹回 1
     勾选   ① 方框中间的空白收缩到零
              = 主题色填充块由中心 scale 0 → 1（200ms，emphasized-decelerate）
            ② 空白收完后再把对勾「描」出来
              = stroke-dashoffset 1 → 0（220ms，延迟 140ms）
     取消   ① 对勾反向描回去、消失（stroke-dashoffset 0 → 1，160ms）
            ② 白底随后回来（填充块 scale 1 → 0，180ms，延迟 140ms）

   与上一版的差别（这次要改的三处）
     · 填充块起点是 0 而不是 0.6 —— 是「中间空白缩到零」，不是「整块弹出来」；
     · 对勾是纯描边绘制（不缩放），而且排在空白收完之后，不是同时；
     · 取消是「反向描没 + 白底回来」，不是「停 100ms 后瞬间消失」。

   不做涟漪 / 水滴（那是按压时的另一套，不在此列）。

   曲线 token → cubic-bezier：emphasized-decelerate = (0.05,0.7,0.1,1)
                              emphasized-accelerate = (0.3,0,0.8,0.15)
=========================================================================== */
/* ---- Checkbox ---- */
.checkbox{position:relative;}
.checkbox .box{
  position:relative;width:18px;height:18px;flex-shrink:0;
  transition:transform 220ms cubic-bezier(0.2,0,0,1);                   /* 松开：弹回原尺寸 */
}
/* 按下：整个控件先缩一下（按住期间保持，松手弹回） */
.checkbox:active .box{transform:scale(.9);transition:transform 90ms cubic-bezier(0.2,0,0,1);}
.checkbox .box .outline{
  position:absolute;inset:0;border-radius:2px;border:2px solid var(--md-on-surface-variant);
  transition:border-color 150ms cubic-bezier(0.3,0,0.8,0.15) 140ms;     /* 取消：等勾描没、白底回来后再转灰 */
}
/* 填充块：起点 0 —— 按下后「中间的空白收缩到零」就是这个块由中心长满 */
.checkbox .box .fill{
  position:absolute;inset:0;border-radius:2px;background:var(--md-primary);
  transform:scale(0);transform-origin:center;
  transition:transform 180ms cubic-bezier(0.3,0,0.8,0.15) 140ms;        /* 取消：勾先没，白底随后回来 */
}
/* 对勾：纯描边绘制，不缩放 */
.checkbox .box .check-icon{
  position:absolute;inset:0;width:18px;height:18px;fill:none;
  stroke:var(--md-on-primary);stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;
  stroke-dasharray:1;stroke-dashoffset:1;
  transition:stroke-dashoffset 160ms cubic-bezier(0.3,0,0.8,0.15);      /* 取消：勾反向描回去、消失 */
}
/* 勾选方向 */
.checkbox input:checked~.box .outline{
  border-color:var(--md-primary);
  transition:border-color 150ms cubic-bezier(0.05,0.7,0.1,1);           /* 勾选：立刻转主题色 */
}
.checkbox input:checked~.box .fill{
  transform:scale(1);
  transition:transform 200ms cubic-bezier(0.05,0.7,0.1,1);              /* 勾选：空白收缩到零 */
}
.checkbox input:checked~.box .check-icon{
  stroke-dashoffset:0;
  transition:stroke-dashoffset 220ms cubic-bezier(0.05,0.7,0.1,1) 140ms;/* 勾选：空白收完再把勾描出来 */
}
/* 悬停用 MD3 的 hover token（CheckboxTokens.UnselectedHoverOutlineColor = on-surface） */
.checkbox:hover .box .outline{border-color:var(--md-on-surface);}
.checkbox input:checked:hover~.box .outline{border-color:var(--md-primary);}
/* 键盘聚焦由态层（10%）表达，MD3 规格如此，不再另加蓝色光圈 */

/* --- Checkbox / Radio --- */
.checkbox:has(input:disabled),.radio:has(input:disabled){color:var(--md-dis-content);cursor:default;pointer-events:none;}
.checkbox:has(input:disabled) .box .outline{border-color:var(--md-dis-content);}
.checkbox:has(input:disabled) .box .fill{background:var(--md-dis-content);}
.checkbox:has(input:disabled) .box .check-icon{stroke:var(--md-surface);}

/* ===================== components/radio.css ===================== */
/* ---- Radio ----
   20px 图标 / 外环 2px（半径 9）/ 内点直径 10px
   外环与内点共用同一个颜色（currentColor），一条 color 过渡让两者天然同步。
   动画与上面的复选框同族（同一套曲线与时长）：
     按下 整个控件先缩一下再弹回；勾选 内点由中心长满（空白收缩到零）；
     取消 内点缩回中心、白底随即回来。 */
.radio .circle{
  position:relative;width:20px;height:20px;flex-shrink:0;
  border:2px solid currentColor;border-radius:50%;
  color:var(--md-on-surface-variant);
  display:flex;align-items:center;justify-content:center;background:transparent;
  transition:color 150ms cubic-bezier(0.3,0,0.8,0.15) 120ms,
             transform 220ms cubic-bezier(0.2,0,0,1);                   /* 松开：弹回原尺寸 */
}
/* 按下：整个控件先缩一下（已选中时也要生效，故补一条更高优先级的） */
.radio:active .circle,
.radio input:checked:active~.circle{transform:scale(.9);transition:transform 90ms cubic-bezier(0.2,0,0,1);}
.radio .circle .dot{
  width:10px;height:10px;border-radius:50%;background:currentColor;
  transform:scale(0);transform-origin:center;
  transition:transform 180ms cubic-bezier(0.3,0,0.8,0.15);            /* 取消：内点缩回、白底回来 */
}
.radio input:checked~.circle{
  color:var(--md-primary);
  transition:color 150ms cubic-bezier(0.05,0.7,0.1,1),
             transform 220ms cubic-bezier(0.2,0,0,1);
}
.radio input:checked~.circle .dot{
  transform:scale(1);
  transition:transform 200ms cubic-bezier(0.05,0.7,0.1,1);              /* 勾选：中部空白收缩到零 */
}
.radio:hover .circle{color:var(--md-on-surface);}
.radio input:checked:hover~.circle{color:var(--md-primary);}
/* 键盘聚焦同样交给态层 */

/* Switch - track 52x32, 开启时圆小 关闭时圆大, 无 hover 变色 */
.radio:has(input:disabled) .circle{color:var(--md-dis-content);}

/* ===================== components/switch.css ===================== */
/* Switch - track 52x32, 开启时圆小 关闭时圆大, 无 hover 变色 */
.switch{position:relative;display:inline-flex;align-items:center;gap:8px;cursor:pointer;font-size:0.9rem;color:var(--md-on-surface);user-select:none;}
.switch input{position:absolute;opacity:0;width:0;height:0;}
/* 描边 3px：外框 54x32，减去双边后内部 48x26 */
.switch .switch-track{display:inline-block;width:54px;height:32px;background:var(--md-neutral-hi);border:3px solid var(--md-outline);border-radius:9999px;position:relative;flex-shrink:0;box-sizing:border-box;transition:background-color 250ms cubic-bezier(0.2,0,0,1),border-color 250ms cubic-bezier(0.2,0,0,1);}
/* thumb 以中心定位（left = 圆心），缩放时不会偏移 */
.switch .switch-thumb{position:absolute;top:50%;left:13px;width:16px;height:16px;border-radius:50%;background:var(--md-outline);transform:translate(-50%,-50%);
  /* 曲线走变量：默认回弹，切到「平滑」时页面级 --sw-ease 被改写，全站开关一起变 */
  transition:left var(--sw-move-dur,300ms) var(--sw-ease,cubic-bezier(0.2,0,0,1)),
             width 250ms cubic-bezier(0.2,0,0,1),
             height 250ms cubic-bezier(0.2,0,0,1),
             background 250ms cubic-bezier(0.2,0,0,1);}
.switch.is-checked .switch-track{background:var(--md-primary);border-color:var(--md-primary);}
/* 开启态的圆心按「外框右端」定位：外框宽 54 - 留白 4 - 半径 11 = 39（外框坐标）。
   绝对定位的原点在描边内侧（padding box），再减掉一侧描边 3px → left:36px。
   关闭态的描边是看得见的一道深色环，位置按内框算；开启态描边和轨道同色、根本看不见，
   就不该把它当成可见留白。之前按内框算（35）偏左、只减一侧描边（38）又调到太右，
   取中间 36：右缘离外框 4px，比内框那套多出来的那点就是描边自己占掉的宽度 */
.switch.is-checked .switch-thumb{background:var(--md-on-primary);left:36px;width:22px;height:22px;}
/* 按下 / 长按（还没松手）：thumb 胀到「开启态」的大小 22px —— 和开启开关那个圆一样大，不再更大。
   16 → 22 是圆本身「小圆长大圆」的那一档，松手滑到另一端后大小正好就是终态，不会再动一次。
   （之前胀到 26px = 内框短边，比开启态还大一圈，圆会顶住描边，已按此改回。）
   节奏 short4 = 200ms + 标准减速：普通点一下也看得清胀大过程，不是 0ms 硬跳、也不拖沓。
   选中态按下去已经是 22px，尺寸不变、位置不动，所以这里不再需要挪圆心 */
.switch.is-pressed .switch-thumb{
  width:22px;height:22px;
  transition:width 200ms cubic-bezier(0.2,0,0,1),
             height 200ms cubic-bezier(0.2,0,0,1),
             left 200ms cubic-bezier(0.2,0,0,1),
             background 200ms cubic-bezier(0.2,0,0,1);
}
.switch input:focus-visible~.switch-track{box-shadow:0 0 0 4px var(--md-primary-container);}
/* 回弹模式：切换时 thumb 做一个温和的「放大又缩回」（scale 1 → 1.1 → 1），强化回弹手感。
   平滑模式由 body.anim-smooth 屏蔽，完全不播放；圆本身的尺寸（width/height）依旧走非过冲曲线，
   不会因过冲而「突然变大」，这里只是在视觉上叠一个轻微的 pop */
@keyframes rzPop{
  0%{transform:translate(-50%,-50%) scale(1);}
  40%{transform:translate(-50%,-50%) scale(1.18);}
  100%{transform:translate(-50%,-50%) scale(1);}
}
body:not(.anim-smooth) .switch .switch-thumb.rz-pop{animation:rzPop var(--sw-move-dur,300ms) cubic-bezier(0.22,0.61,0.36,1);}
/* --- Switch ---
   轨道一律 on-surface @12%，圆一律 @38%；开 / 关只差圆的位置，不再有主题色 */
.switch:has(input:disabled){color:var(--md-dis-content);cursor:default;pointer-events:none;}
.switch:has(input:disabled) .switch-track{background:var(--md-dis-container);border-color:var(--md-dis-container);}
.switch:has(input:disabled) .switch-thumb{background:var(--md-dis-content);}

/* ===================== components/resizable.css ===================== */
/* ===== 可缩放 Switch：长按 / 拖右下角手柄自由缩放，带尺寸极限 =====
   全部尺寸由 --sw-w / --sw-h 派生，thumb 按内高比例算，所以任意比例都不会溢出轨道
   基准 54x32 时：描边 3、内高 26、关闭圆 16、开启圆 22、按下圆 22（= 开启圆），与固定版完全一致 */
.switch-resizer{
  --sw-w:54px;--sw-h:32px;
  --sw-stretch:1;                        /* 长条系数：1 = 正圆；>1 只把 thumb 沿长轴拉长（圆角不动，天生就是胶囊）。由 JS 算出 */
  --sw-d:26px;                           /* thumb 基准直径 = 内框短边，由 JS 算出 */
  --sw-gap:5px;--sw-gap2:2px;            /* 两端留白 = 关闭态 / 开启态滑块自身的四周留白，由 JS 算出 */
  --sw-bd:calc(min(var(--sw-w), var(--sw-h)) * 0.09375);  /* 描边 = 短边 * 3/32（按短边算，竖过来才不会把宽度吃光） */
  --sw-ease:cubic-bezier(0.2,0,0,1);                     /* 默认平滑曲线（与全站一致），切到回弹时由 JS 改写 */
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  padding:10px 14px 16px 10px;border-radius:var(--md-corner);
  touch-action:none;user-select:none;-webkit-user-select:none;
}
.switch-resizer:hover{outline:1px dashed var(--md-outline-variant);outline-offset:-1px;}
.switch-resizer.resizing{outline:1px solid var(--md-primary);background:color-mix(in srgb, var(--md-primary) 8%, transparent);}
.switch-resizer .switch .switch-track{width:var(--sw-w);height:var(--sw-h);border-width:var(--sw-bd);}
/* thumb 圆角必须写 999px（大数），不能写 50%：
   - 999px 会被浏览器夹到「短边的一半」，等宽高时是正圆，拉长后两端还是半圆 —— 就是胶囊（长条）
   - 50% 是按各自边长算的，宽高一旦不等就变成椭圆 —— 拉长后那个难看的椭圆就是它
   --sw-d = 内框短边（thumb 基准直径）；--sw-gap / --sw-gap2 = 两端留白；--sw-stretch = 长条系数，三者都由 JS 按当前尺寸算出
   横向沿 X 轴切换；轨道竖过来（h > w，加 .rs-vert）时切换方向变成上下 */
.switch-resizer .switch .switch-thumb{
  border-radius:999px;
  /* --sw-ease 由 JS 按行程长度实时算：行程越长过冲越小，保证圆冲不出轨道外沿
     data-anim="smooth" 时 --sw-ease 换成平滑曲线、--sw-move-dur 缩到 250ms，全程无过冲 */
  transition:left var(--sw-move-dur,300ms) var(--sw-ease),
             top var(--sw-move-dur,300ms) var(--sw-ease),
             width 250ms cubic-bezier(0.2,0,0,1),
             height 250ms cubic-bezier(0.2,0,0,1),
             background 250ms cubic-bezier(0.2,0,0,1);
}
/* 拖拽缩放期间，thumb 绝不能走过渡动画：轨道是瞬时改尺寸的，而 thumb 的 left/top/width/height
   若还带 250~300ms 过渡（且 left/top 走的是回弹过冲曲线），缩小时会滞留在旧的「大尺寸/旧位置」
   而冲出轨道 —— 实测不修时缩小拖拽瞬时溢出可达 220px 以上。缩放时关掉过渡即可彻底消除 */
.switch-resizer.resizing .switch .switch-thumb{transition:none !important;}
/* 留白的定义（和官方一致）：滑块四周留白 = (内框短边 - 滑块直径) / 2。
   官方 Compose Switch.kt:295 的 offset = (SwitchHeight - size)/2 说的就是这件事 ——
   一个静止态里，滑块到内框端头的距离 == 它到内框上下边的距离。
   所以关闭态（圆 0.615d）留白 = 0.1925d，开启态（圆 0.846d）留白 = 0.077d，
   两个数都由 JS 按当前内框短边算出，任何比例 / 拉伸下都成立。
   基准 54×32（内框 48×26）→ 关闭 5px、开启 2px；滑块中心 = 留白 + 半个身位 */
/* 未选中（关闭）态：小圆 / 短胶囊，前缘落在留白 --sw-gap 上（= 它上下的留白） */
.switch-resizer:not(.rs-vert) .switch .switch-thumb{
  width:calc(var(--sw-d) * 0.615 * var(--sw-stretch));height:calc(var(--sw-d) * 0.615);
  left:calc(var(--sw-gap) + var(--sw-d) * 0.3075 * var(--sw-stretch));top:50%;
}
/* 选中（开启）态：大一档的圆 / 长胶囊，后缘的留白从「外框」右端量起 ——
   开启态描边跟轨道同色、看不见，不能算成可见留白，否则圆会停得偏左。
   --sw-bd 是换算坐标用的（绝对定位原点在描边内侧），后面那个 gap2 才是真正的留白余量 */
.switch-resizer:not(.rs-vert) .switch.is-checked .switch-thumb{
  width:calc(var(--sw-d) * 0.846 * var(--sw-stretch));height:calc(var(--sw-d) * 0.846);
  left:calc(var(--sw-w) - var(--sw-bd) - var(--sw-gap2) * 2 - var(--sw-d) * 0.423 * var(--sw-stretch));
}
/* 上面两条是基准：留白都从内框端头算起（绝对定位的包含块是 padding box）
   竖过来的轨道：长轴换成 Y，其余同理 */
.switch-resizer.rs-vert .switch .switch-thumb{
  width:calc(var(--sw-d) * 0.615);height:calc(var(--sw-d) * 0.615 * var(--sw-stretch));
  left:50%;top:calc(var(--sw-gap) + var(--sw-d) * 0.3075 * var(--sw-stretch));
}
.switch-resizer.rs-vert .switch.is-checked .switch-thumb{
  width:calc(var(--sw-d) * 0.846);height:calc(var(--sw-d) * 0.846 * var(--sw-stretch));
  top:calc(var(--sw-h) - var(--sw-bd) - var(--sw-gap2) * 2 - var(--sw-d) * 0.423 * var(--sw-stretch));
}
/* 按下 / 长按：只把「关闭」态的滑块胀到「开启」态那么大 —— 和固定版一致（16 → 22 那一档）。
   1) 尺寸带上 --sw-stretch、沿长轴一起胀：轨道被拉成横条时滑块本身也是条状，
      若按下写死成 var(--sw-d) 的正方形，就会「长条 → 按下变圆 → 松手又变回长条」地跳
   2) 圆心用「开启态那套留白」定位（gap2 + 半个身位）：s = 1 时圆心和关闭态完全重合
      （0.077d + 0.423d = 0.1925d + 0.3075d = 0.5d）—— 也就是原地长大，和固定版的按下反馈一致；
      轨道拉长后前缘仍保持与开启态相同的留白，不会因为「原地放大」而顶出轨道前端
   选中态按下去本来就是「开启」态大小，尺寸/位置都不变，所以无需另外的规则 */
.switch-resizer:not(.rs-vert) .switch.is-pressed:not(.is-checked) .switch-thumb{
  width:calc(var(--sw-d) * 0.846 * var(--sw-stretch));
  height:calc(var(--sw-d) * 0.846);
  left:calc(var(--sw-gap2) + var(--sw-d) * 0.423 * var(--sw-stretch));
  top:50%;
}
.switch-resizer.rs-vert .switch.is-pressed:not(.is-checked) .switch-thumb{
  width:calc(var(--sw-d) * 0.846);
  height:calc(var(--sw-d) * 0.846 * var(--sw-stretch));
  left:50%;
  top:calc(var(--sw-gap2) + var(--sw-d) * 0.423 * var(--sw-stretch));
}
/* 胀大的节奏跟固定版一致（200ms 标准减速）；不写 left/top 的话会去吃基础规则里的回弹曲线 */
.switch-resizer .switch.is-pressed .switch-thumb{
  transition:width 200ms cubic-bezier(0.2,0,0,1),
             height 200ms cubic-bezier(0.2,0,0,1),
             left 200ms cubic-bezier(0.2,0,0,1),
             top 200ms cubic-bezier(0.2,0,0,1);
}
/* 实验性功能标记 */
.exp-badge{
  display:inline-flex;align-items:center;gap:4px;vertical-align:middle;margin-left:8px;
  padding:3px 10px;border-radius:999px;line-height:1;
  background:var(--md-tertiary-container);color:var(--md-on-tertiary-container);
  font-size:0.65rem;font-weight:600;letter-spacing:.3px;
}
.exp-badge .material-symbols-rounded{font-size:14px;}

/* ===== 可缩放通用外壳：Switch / Card / Dialog 共用 =====
   用法 <div class="rz-box switch-resizer" data-rz="switch" data-w="54" data-h="32">
     data-w / data-h   代码里预设的初始宽高（复位会回到这个值）
     data-resizable    "false" = 锁死尺寸：手柄隐藏、长按不触发缩放
     data-anim         switch 专用："bounce" 回弹（默认） / "smooth" 平滑 */
.rz-box{
  position:relative;touch-action:none;user-select:none;-webkit-user-select:none;
  transition:background var(--md-motion-short);
}
.rz-box:hover{outline:1px dashed var(--md-outline-variant);outline-offset:-1px;}
.rz-box.resizing{outline:1px solid var(--md-primary);}
/* 只有本体透明的 switch 才染背景；卡片 / 对话框染了会跟 --rz-bg 的渐变对不上色 */
.switch-resizer.resizing{background:color-mix(in srgb, var(--md-primary) 8%, transparent);}
/* 禁用缩放：手柄和复位都藏掉，只允许交互不允许改尺寸 */
.rz-box[data-resizable="false"]:hover{outline-style:dotted;}
.rz-box[data-resizable="false"] .rs-handle,
.rz-box[data-resizable="false"] .rs-reset{display:none;}

/* 缩放手柄：两个小三角，鼠标靠近才浮出来 */
.rz-box .rs-handle{
  position:absolute;right:0;bottom:0;width:18px;height:18px;
  cursor:nwse-resize;opacity:0;z-index:5;
  transition:opacity var(--md-motion-short);
}
.rz-box .rs-handle::before,.rz-box .rs-handle::after{
  content:'';position:absolute;background:var(--md-primary);
  clip-path:polygon(0 100%,100% 100%,100% 0);
}
.rz-box .rs-handle::before{right:1px;bottom:1px;width:9px;height:9px;}
.rz-box .rs-handle::after{left:1px;top:1px;width:7px;height:7px;}
.rz-box:hover .rs-handle,.rz-box.resizing .rs-handle{opacity:1;}
@media (hover:none){.rz-box .rs-handle{opacity:1;}}

/* 复位：纯图标按钮（对齐 .icon-btn）—— 静止只有图标，没有任何边框 / 底色；
   要获得反馈才在 hover / 按下时浮出圆形态层。手机上没有 hover，常态就给到 1 */
.rz-box .rs-reset{
  position:absolute;right:0;top:0;width:24px;height:24px;padding:0;
  display:flex;align-items:center;justify-content:center;
  border:none;border-radius:50%;
  background:transparent;color:var(--md-on-surface-variant);
  cursor:pointer;opacity:.72;z-index:6;
  transition:opacity var(--md-motion-short),background var(--md-motion-short),color var(--md-motion-short);
}
.rz-box .rs-reset:hover{opacity:1;background:var(--md-surface-container-high);color:var(--md-on-surface);}
.rz-box .rs-reset:active{background:var(--md-surface-container-highest);}
.rz-box .rs-reset .material-symbols-rounded{font-size:16px;}
@media (hover:none){.rz-box .rs-reset{opacity:1;}}
.rz-box .rs-badge{
  position:absolute;left:50%;bottom:-4px;transform:translateX(-50%);
  background:var(--md-primary);color:var(--md-on-primary);
  font-size:0.65rem;line-height:1;padding:3px 8px;border-radius:999px;
  white-space:nowrap;opacity:0;pointer-events:none;z-index:6;
  transition:opacity var(--md-motion-short);
}
.rz-box.resizing .rs-badge,.rz-box:hover .rs-badge{opacity:1;}
/* 顺序有意义：长条在前、极限在后，所以顶到极限时红色优先 */
.rz-box.rs-stretch .rs-badge{background:var(--md-tertiary);color:var(--md-on-tertiary);}
.rz-box.rs-limit .rs-badge{background:var(--md-error);color:var(--md-on-error);}
.rz-box.rz-locked .rs-badge{background:var(--md-outline);color:var(--md-surface);}

/* 裁剪区 + 四边渐变：内容超出时才有对应方向的渐变
   圆角裁剪放在这一层做，本体不放 overflow:hidden —— 否则徽章 / 手柄 / 复位会被裁掉、盖不住内容 */
.rz-clip{position:relative;flex:1;min-height:0;overflow:hidden;border-radius:inherit;}
/* 这里刻意不阻断 overscroll：内容没超出时它会把滚轮吞掉，整页都滚不动 */
.rz-scroll{
  position:absolute;inset:0;overflow:auto;touch-action:auto;
  scrollbar-width:none;-ms-overflow-style:none;
}
.rz-scroll::-webkit-scrollbar{width:0;height:0;display:none;}
.rz-fade{
  position:absolute;pointer-events:none;opacity:0;z-index:3;
  transition:opacity 180ms var(--md-easing);
}
.rz-fade-t{top:0;left:0;right:0;height:32px;background:linear-gradient(to bottom,var(--rz-bg),transparent);}
.rz-fade-b{bottom:0;left:0;right:0;height:32px;background:linear-gradient(to top,var(--rz-bg),transparent);}
.rz-fade-l{top:0;bottom:0;left:0;width:32px;background:linear-gradient(to right,var(--rz-bg),transparent);}
.rz-fade-r{top:0;bottom:0;right:0;width:32px;background:linear-gradient(to left,var(--rz-bg),transparent);}
.rz-box.f-t .rz-fade-t,.rz-box.f-b .rz-fade-b,
.rz-box.f-l .rz-fade-l,.rz-box.f-r .rz-fade-r{opacity:1;}

/* ===== 可缩放 Slider：盒子里就是一个滑块，轨道宽度跟着盒子走 =====
   滑块是「宽度敏感」的组件：盒子变宽 / 变窄都得按新宽度重算手柄中心（--tc）和刻度位置，
   所以滑块的初始化里挂了 ResizeObserver（见底部脚本），缩放过程中几何实时跟着走。
   长按在滑块本体上是「拖动手柄」，不能同时进缩放，所以整块排除长按（RZ_SLIDER_CFG.holdExclude），
   缩放只从右下角那两个小三角发起 */
.slider-resizer{
  display:flex;align-items:center;justify-content:center;
  padding:0 14px;box-sizing:border-box;
  width:260px;height:72px;
}
/* 滑块本体在 demo 里是固定 240px，进了缩放盒就铺满（宽度交给盒子） */
.slider-resizer > .slider,
.slider-resizer > .slider-ticks{width:100%;}

/* ===== 可缩放 Card：内容有自然尺寸，卡片缩到比它小就开始裁剪 ===== */
.card-resizer{
  --rz-bg:var(--md-surface);
  display:inline-flex;flex-direction:column;
  width:360px;height:200px;
  background:var(--md-surface);border-radius:var(--md-corner-large);
  box-shadow:var(--md-elev1);
}
.card-resizer .rz-body{width:320px;padding:16px;}
.rc-title{font-size:1.05rem;font-weight:600;color:var(--md-on-surface);margin-bottom:8px;}
.rc-text{font-size:0.85rem;color:var(--md-on-surface-variant);line-height:1.6;margin-bottom:12px;}
.rc-chips{display:flex;gap:6px;margin-bottom:12px;white-space:nowrap;}
.rc-chip{
  font-size:0.7rem;padding:4px 10px;border-radius:999px;
  background:var(--md-secondary-container);color:var(--md-on-secondary-container);
}
.rc-rows{display:flex;flex-direction:column;gap:8px;margin-bottom:12px;}
.rc-row{display:flex;align-items:center;gap:10px;font-size:0.85rem;color:var(--md-on-surface);}
.rc-row .material-symbols-rounded{font-size:18px;color:var(--md-primary);}
.rc-actions{display:flex;gap:8px;}

/* ===== 可缩放 Dialog：标题固定、中间内容可滚动、操作区固定 ===== */
.dialog-resizer{
  --rz-bg:var(--md-surface-container-high);
  display:inline-flex;flex-direction:column;
  width:340px;height:200px;padding:20px;box-sizing:border-box;
  background:var(--md-surface-container-high);
  border-radius:var(--md-corner-xl);box-shadow:var(--md-elev3);
}
/* 对话框的裁剪区在 20px padding 里，继承 28px 圆角会太夸张，收到 8px */
.dialog-resizer .rz-clip{border-radius:var(--md-corner-small);}
.dialog-resizer .dlg-title{
  font-size:1.15rem;font-weight:500;color:var(--md-on-surface);
  margin-bottom:10px;flex-shrink:0;
}
.dialog-resizer .rz-body{width:300px;}
.dialog-resizer .dlg-text{font-size:0.85rem;color:var(--md-on-surface-variant);line-height:1.6;}
.dialog-resizer .dlg-actions{
  display:flex;justify-content:flex-end;gap:8px;flex-shrink:0;padding-top:12px;
}


/* ===================== components/slider.css ===================== */
/* ===== Slider ===== */
.comp-row{display:flex;flex-wrap:wrap;gap:24px;align-items:center;}
/* 拖手柄时别把周围文字一起选中 —— 选中后浏览器会弹「搜索所选文本」那种菜单；
   touch-action:none 是顺便的：手指落在滑块上别带着页面一起滚 */
.slider,.slider-ticks{user-select:none;-webkit-user-select:none;touch-action:none;}
.slider input[type=range],.slider-ticks input[type=range]{-webkit-user-drag:none;}
/* 连续滑块（圆形拇指）：轨道 4px，已走过段 primary / 未走过段 secondary-container —— 和竖条版同一套配色。
   切点跟着拇指中心走：拇指直径 20，中心 = 10px + p × (宽 - 20px)，p 由 JS 写进 --p（0~1），
   所以切点用 calc 直接从 --p 算，不用再量元素。
   进度本身存成 --sld-fill，虚线模式直接在上面叠一层「挖空层」复用它（见下） */
.slider{position:relative;width:240px;height:40px;display:flex;align-items:center;--p:0;--dash-a:0%;--dash-b:0%;
  --sld-fill:linear-gradient(to right,
    var(--md-primary) 0,              var(--md-primary) calc(10px + var(--p) * (100% - 20px)),
    var(--md-secondary-container) calc(10px + var(--p) * (100% - 20px)), var(--md-secondary-container) 100%);}
.slider input[type=range]{width:100%;appearance:none;height:4px;border-radius:2px;outline:none;
  background:var(--sld-fill);}
/* 虚线模式（旁边输入框填了 ≥2 的数字）：最上层用 repeating-linear-gradient 把空隙「挖」成卡片底色
   （--md-surface-container-low，就是滑块背后的颜色），露出来的就是一段段 dash。
   --dash-a = 一段 dash 的宽度，--dash-b = 「dash + 空隙」的周期（两个值都由 JS 按分段数算好）。
   底下的进度 --sld-fill 原样保留，已走过 / 未走过段各自的颜色都不变。
   这里不用 mask：Chrome 里 ::-webkit-slider-thumb 是 track 容器里的子节点，mask 加在滑块上会连拇指一起裁掉 */
.slider.is-dashed input[type=range]{
  background:repeating-linear-gradient(to right,
      transparent 0,transparent var(--dash-a),
      var(--md-surface-container-low) var(--dash-a),var(--md-surface-container-low) var(--dash-b)),
    var(--sld-fill);
}
/* 拇指：20 正圆、primary，没有任何边框 / 光环 ——
   常显的 4px primary-container 圈和悬停 / 按下时的态层都去掉了，静止与交互时都是干干净净一个圆 */
.slider input[type=range]::-webkit-slider-thumb{appearance:none;width:20px;height:20px;border-radius:50%;background:var(--md-primary);cursor:pointer;}
.slider input[type=range]::-moz-range-thumb{width:20px;height:20px;border-radius:50%;background:var(--md-primary);cursor:pointer;border:none;}

/* 圆形滑块的「波浪轨道」（点卡片上那颗「波浪」chip 切换）：
   细波浪（secondary-container，sw 2）当轨道、粗波浪（primary，sw 4）当进度，两层同相位叠着
   —— 和线性进度条那条波浪同一套画法，共用同一个正弦采样函数；波长也统一成 48px（240px 上 5 个弯）。
   路径由底部脚本按滑块的实际宽度重算（缩放盒里宽度是拖出来的），viewBox 与像素 1:1，不做拉伸变形。
   两头是圆头（stroke-linecap:round），所以路径两端各内缩一个「圆头半径」（细的 1px、粗的 2px）——
   圆头会从端点往外探半个线宽，贴着边界画会被 SVG 视野切平，看着就是「头尾一刀切」。
   svg 铺在轨道位置上，input 压在上面（拇指得能点到）；波浪模式下 input 自己的轨道背景让出来
   （进度 / 颜色这时由 svg 负责，input 只留拇指） */
.slider-wave{position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);height:24px;display:none;pointer-events:none;}
.slider-wave path{fill:none;stroke-linecap:round;stroke-linejoin:round;}
/* 两条波浪都要 4px：跟不开波浪时那条 4px 实心轨道对齐 —— 否则一开波浪，
   「未走过」的那段灰会突然变细，跟进度段（4px）不是一个粗细 */
.slider-wave .slw-track{stroke:var(--md-secondary-container);stroke-width:4;}
.slider-wave .slw-active{stroke:var(--md-primary);stroke-width:4;}
.slider.is-wave .slider-wave{display:block;}
.slider.is-wave input[type=range]{background:transparent;position:relative;z-index:1;}
/* 波浪 + 分段同时开：不能再用「input 背景上的空隙层」来切了 —— 波浪模式下 input 是透明的（见上一条），
   空隙层跟着一起没了，看着就是「开了波浪，分段不见了」。
   改成拿遮罩切底层那个波浪 svg：空隙处 alpha=0 → 透出卡片底色，波浪就成了和原来同一套位置的虚线。
   这里敢用 mask 是因为被切的是 svg（纯路径，不含拇指）；拇指在 input 上，切 input 才会连拇指一起裁掉。
   遮罩用百分比、和轨道实际宽度无关，所以滑块放在缩放盒里被拖宽拖窄也不用重算；
   --dash-a / --dash-b 就是 input 虚线那套变量（由 __setSegs 写在 .slider 上，svg 继承） */
.slider.is-wave.is-dashed .slider-wave{
  -webkit-mask-image:repeating-linear-gradient(to right,
      #000 0,#000 var(--dash-a),transparent var(--dash-a),transparent var(--dash-b));
  mask-image:repeating-linear-gradient(to right,
      #000 0,#000 var(--dash-a),transparent var(--dash-a),transparent var(--dash-b));
}

/* Slider discrete（刻度竖条）：按本地 Compose material3 Slider.kt / SliderTokens.kt 复刻
   常量（dp→px 1:1）：
     · 轨道 16 高，两段条各是独立胶囊：外端 8（半高全圆），朝手柄的内端（拼接处）稍小一档 = --st-inner
       —— 官方 TrackInsideCornerSize 是 2dp，这里取 4px，比官方的拼接处更圆一点（用户指定）
     · 手柄 4×44 全圆条（HandleWidth / HandleHeight，CornerFull），粗细恒定 —— 按下不再减半
     · 手柄与轨道之间恒留 6（ActiveHandleLeadingSpace）→ 轨道在「手柄中心 ± (手柄半宽 + 6)」处断开
     · 刻度 4（StopIndicatorSize）圆点，中心从 8 均布到 W-8
     · 配色：已走过段 = primary，未走过段 = secondary-container（InactiveTrackColor）；
             刻度落在已走过段用 secondary-container、未走过段用 primary —— 官方 tickColor 是反着配的
   层级：条在最上面，点画在条里面（详见下面 .st-seg 那段注释）。
   手柄中心 --tc 由 JS 按官方 isOnFirstOrLastStep 分支算：首尾两档贴到轨道两端，中间档位
   落在 8 + (W-16) * 进度 上（刻度也一样均布在这条线上，所以档位与刻度严格对齐） */
.slider-ticks{position:relative;width:240px;height:44px;--tc:97.6px;--st-gap:8px;--track-w:240px;
  --st-outer:8px;                        /* 条的外端：半高，全圆 */
  --st-inner:4px;                        /* 条朝手柄的内端（两段拼接处）：比外端小一档，改这一个数就能调圆度 */
}
/* 条是顶层，刻度点是条里面的小圆点（.st-dot，由 JS 按当前 --tc / --track-w 塞进对应的那一段里）。
   .st-seg 用 overflow:hidden + 自身圆角做剪贴蒙版，点只能长在条的形状里；
   会被条的端头切掉半个的点，JS 直接不生成（所以剪贴只是兜底，正常看不到半个点）。
   点用真元素 + border-radius:50% 保证是正圆，不依赖径向渐变在各浏览器里的渲染差异 */
.slider-ticks .st-seg{position:absolute;top:50%;transform:translateY(-50%);height:16px;border-radius:var(--st-outer);overflow:hidden;pointer-events:none;}
.slider-ticks .st-active{left:0;width:max(0px,calc(var(--tc) - var(--st-gap)));border-radius:var(--st-outer) var(--st-inner) var(--st-inner) var(--st-outer);background-color:var(--md-primary);}
.slider-ticks .st-inactive{left:calc(var(--tc) + var(--st-gap));right:0;border-radius:var(--st-inner) var(--st-outer) var(--st-outer) var(--st-inner);background-color:var(--md-secondary-container);}
/* 刻度点：2px 半径的小圆，left 是它在所属那一段里的中心横坐标 */
.slider-ticks .st-dot{position:absolute;top:50%;width:4px;height:4px;border-radius:50%;transform:translate(-50%,-50%);}
.slider-ticks .slider-thumb{position:absolute;top:50%;left:var(--tc);transform:translate(-50%,-50%);width:4px;height:44px;border-radius:2px;background:var(--md-primary);z-index:3;pointer-events:none;}
.slider-ticks input[type=range]{position:absolute;left:0;top:50%;transform:translateY(-50%);width:100%;height:48px;margin:0;appearance:none;-webkit-appearance:none;background:transparent;outline:none;z-index:4;cursor:pointer;}
.slider-ticks input[type=range]::-webkit-slider-runnable-track{background:transparent;height:48px;border:none;}
.slider-ticks input[type=range]::-moz-range-track{background:transparent;height:48px;border:none;}
.slider-ticks input[type=range]::-webkit-slider-thumb{appearance:none;-webkit-appearance:none;width:16px;height:48px;border-radius:8px;background:transparent;border:none;cursor:pointer;}
.slider-ticks input[type=range]::-moz-range-thumb{width:16px;height:48px;border-radius:8px;background:transparent;border:none;cursor:pointer;}

/* --- Slider（圆形 / 竖条）---
   已走过段 @38%、未走过段 @12%，和上面几件是同一套；波浪那两条也一起换成灰的 */
.slider.is-disabled{--sld-fill:linear-gradient(to right,
  var(--md-dis-content) 0,var(--md-dis-content) calc(10px + var(--p) * (100% - 20px)),
  var(--md-dis-container) calc(10px + var(--p) * (100% - 20px)),var(--md-dis-container) 100%);}
.slider.is-disabled input[type=range],
.slider-ticks.is-disabled input[type=range]{cursor:default;}
/* 拇指那个圆用实色（--md-dis-content）：以前两个值都是半透明的 on-surface，
   圆就会把底下的轨道透出来，看着像「轨道压在圆上面」。实色之后圆是干干净净盖住轨道的，
   和未禁用时（primary 圆盖在 primary 轨道上）一模一样，只是换成了灰 */
.slider.is-disabled input[type=range]::-webkit-slider-thumb{background:var(--md-dis-content);cursor:default;}
.slider.is-disabled input[type=range]::-moz-range-thumb{background:var(--md-dis-content);cursor:default;}
.slider.is-disabled .slider-wave .slw-track{stroke:var(--md-dis-container);}
.slider.is-disabled .slider-wave .slw-active{stroke:var(--md-dis-content);}
.slider-ticks.is-disabled .st-active{background-color:var(--md-dis-content);}
.slider-ticks.is-disabled .st-inactive{background-color:var(--md-dis-container);}
.slider-ticks.is-disabled .slider-thumb{background:var(--md-dis-content);}

/* ===================== components/stepper.css ===================== */
/* 滑块旁边的「分段数」控制框（默认 0）：
   0 → 不分段：不画点（竖条）、实心轨道（圆形），拖动连续自由；
   1~50 → 轨道等分成 n 段，停靠点 n+1 个（含两端）：竖条画 n+1 颗点、圆形切成 n 段虚线，拖动吸附到这些位置。
   控件本身是个胶囊 stepper：中间是紧凑文本框、两侧是圆形 − / + 按钮（原生上下箭头已经藏掉，
   那个是浏览器默认画风，跟这页不是一个体系）；聚焦时整个胶囊的描边变 primary */
.sld-ctl{display:flex;align-items:center;gap:6px;}
.sld-ctl-label{font-size:0.75rem;color:var(--md-on-surface-variant);white-space:nowrap;}
/* 外壳归到输入框那一套：2px outline 描边 + --md-corner 圆角 + surface-container-low 底色
   （原来是 999px 全圆胶囊，太圆了，往输入框那边收一收）；内部 − / 数字 / + 之间不留缝 */
.sld-step{
  display:inline-flex;align-items:center;gap:0;padding:1px;
  border:2px solid var(--md-outline);border-radius:var(--md-corner);
  background:var(--md-surface-container-low);
  transition:border-color var(--md-motion-short);
}
.sld-step:focus-within{border-color:var(--md-primary);}
.sld-step-btn{
  width:30px;height:30px;padding:0;border:none;border-radius:50%;background:transparent;
  color:var(--md-on-surface-variant);cursor:pointer;font-family:inherit;
  display:grid;place-items:center;user-select:none;
  transition:background var(--md-motion-short),color var(--md-motion-short);
}
.sld-step-btn .material-symbols-rounded{font-size:18px;}
.sld-step-btn:hover{background:var(--md-surface-container-highest);color:var(--md-on-surface);}
.sld-step-btn:active{background:var(--md-primary-container);color:var(--md-on-primary-container);}
.sld-step-btn:disabled{color:var(--md-outline-variant);background:transparent;cursor:default;}
.sld-n{
  width:36px;padding:4px 0;border:none;background:transparent;outline:none;
  color:var(--md-on-surface);font-family:inherit;font-size:0.9rem;font-weight:500;text-align:center;
}
/* 藏掉原生上下箭头（Chrome/Safari 走伪元素，Firefox 走 appearance） */
.sld-n::-webkit-outer-spin-button,.sld-n::-webkit-inner-spin-button{-webkit-appearance:none;margin:0;}
.sld-n{-moz-appearance:textfield;appearance:textfield;}
/* --- 步进器（− / 数字 / +）--- */
.sld-ctl.is-disabled .sld-ctl-label{color:var(--md-dis-content);}
.sld-ctl.is-disabled .sld-step{border-color:var(--md-dis-container);}
.sld-ctl.is-disabled .sld-step-btn,
.sld-ctl.is-disabled .sld-n{color:var(--md-dis-content);background:transparent;pointer-events:none;}

/* ===================== components/progress.css ===================== */
/* ===== Progress ===== */
/* 圆形：容器旋转 + 弧长变化，两层动画叠加 */
.progress-circular{width:48px;height:48px;animation:spin 2s linear infinite;}
.progress-circular svg{width:100%;height:100%;display:block;}
.progress-circular .progress-circular-arc{fill:none;stroke:var(--md-primary);stroke-width:6;stroke-linecap:round;animation:progress-arc 1.5s ease-in-out infinite;}
@keyframes spin{to{transform:rotate(360deg);}}
@keyframes progress-arc{
  0%{stroke-dasharray:4 121.6;stroke-dashoffset:0;}
  50%{stroke-dasharray:100 25.6;stroke-dashoffset:-25.6;}
  100%{stroke-dasharray:4 121.6;stroke-dashoffset:-125.6;}
}
/* 线性：回到位移 + 长度变化方案 */
.progress-linear{position:relative;width:100%;max-width:240px;height:8px;background:var(--md-surface-container-high);border-radius:4px;overflow:hidden;}
.progress-linear-bar{height:100%;background:var(--md-primary);border-radius:4px;width:0%;transition:width .3s ease;animation:indeterminate 1.5s ease-in-out infinite;}
@keyframes indeterminate{
  0%{width:0;margin-left:0;}
  50%{width:60%;margin-left:20%;}
  100%{width:0;margin-left:100%;}
}
/* --- Progress ---
   禁用之后停止转动，停在半截的静态位置（不是画一整圈 —— 整圈看着像「已完成」）。
   开了波浪也一样：那颗「波浪」按钮现在会连禁用版一起切（以前把它们排除在外，于是旁边禁用那条
   还是一根直棍），但禁用的那条不参与动画 —— 线性钉在 40% 不动、环形钉在「12 点起 1/4 圈」，
   波峰也不往前淌（相位写死 0），都由底部脚本按 is-disabled 分开画 */
.progress-circular.is-disabled{animation:none;transform:rotate(-90deg);}
.progress-circular.is-disabled .progress-circular-arc{
  animation:none;stroke:var(--md-dis-content);
  stroke-dasharray:31.4 94.2;stroke-dashoffset:0;       /* 圆周长 2πr = 125.6 → 25% */
}
.progress-circular.is-disabled .progress-circular-wave{stroke:var(--md-dis-content);}
.progress-linear.is-disabled{background:var(--md-dis-container);}
.progress-linear.is-disabled.is-wave{background:transparent;}
.progress-linear.is-disabled .progress-linear-bar{animation:none;transition:none;width:40%;margin-left:0;background:var(--md-dis-content);}
.progress-linear.is-disabled .plw-track{stroke:var(--md-dis-container);}
.progress-linear.is-disabled .plw-active{stroke:var(--md-dis-content);}

/* ===================== components/wavy.css ===================== */
/* ===== Wavy 变体（M3 Expressive 的波浪进度）：点右边那颗「波浪」按钮切换 =====
   线性：细波浪当轨道（整宽）、粗波浪当进度（一段会「伸长 → 缩短 + 往前挪」的波浪），
        两条波浪的波峰还在一浪一浪地往前淌（相位每帧推进，见底部脚本的 LINE_FLOW）；
   圆形：圆环 → 一段起皱的波浪弧，弧长在 10%~87% 之间呼吸（1.5s 一拍，同原版圆弧），外层再叠一圈自转。
   两边的两头都是「圆头」—— 官方的 stroke 一律 StrokeCap.Round（WavyProgressIndicator.kt）。
   注意圆头会从端点往外探出半个线宽，所以路径两端必须画在 SVG 视野内侧（留出 cap 半径），
   贴着边界画的话，探出去的那半个圆头会被视野切平，看着就是「头尾一刀切」。
   路径 d 全由底部脚本按正弦算出来填进去（波长 / 起伏数也写在脚本里），
   所以这几层平时 display:none，切换时才亮出来 */
.progress-circular-wave{fill:none;stroke:var(--md-primary);stroke-width:3;stroke-linecap:round;stroke-linejoin:round;display:none;}
.progress-linear-wave{position:absolute;inset:0;width:100%;height:100%;display:none;}
.progress-linear-wave path{fill:none;stroke-linecap:round;stroke-linejoin:round;}
.progress-linear-wave .plw-track{stroke:var(--md-secondary-container);stroke-width:2;}
.progress-linear-wave .plw-active{stroke:var(--md-primary);stroke-width:4;}

.progress-circular.is-wave .progress-circular-arc,
.progress-linear.is-wave .progress-linear-bar{display:none;}
.progress-circular.is-wave .progress-circular-wave,
.progress-linear.is-wave .progress-linear-wave{display:block;}

/* 线性切波浪时要把盒子撑高（波浪有起伏，8px 装不下），底色也让出来 */
.progress-linear.is-wave{height:20px;background:transparent;border-radius:0;}

/* 圆形怎么动：完全照原版那条纯色圆弧来，一点没改 ——
   ① 「转」还是 .progress-circular 上那条 spin（2s 一圈，这里不要盖掉它）；
   ② 「伸缩 + 扫圈」由底部脚本逐帧算，数值就是原版 progress-arc 那三个关键帧的等价换算
      （弧长 3.2% → 79.6% → 3.2%、起点 0° → 73.4° → 360°，1.5s ease-in-out），
      所以波浪弧和纯色圆弧是同一个动法、同一个节奏，只是那条线变成了波浪。
   注意它是「一段不闭合的弧」（路径没有 Z），不是闭合环：闭合环没有头尾，转起来只剩旋转感，
   而且弧的两头靠圆头收口，跟原版一样。 */

/* 那颗「波浪」按钮：选中态借用 chip 的选中配色 */
.chip-wave.selected{background:var(--md-primary-container);color:var(--md-on-primary-container);border-color:var(--md-primary-container);}


/* ===================== components/segmented.css ===================== */
/* ===== Segmented Buttons ===== */
.segmented{display:inline-flex;border:2px solid var(--md-outline);border-radius:var(--md-corner);overflow:hidden;}
.segmented button{padding:10px 16px;border:none;background:transparent;color:var(--md-on-surface-variant);font-size:0.85rem;font-weight:500;cursor:pointer;display:flex;align-items:center;gap:6px;border-right:2px solid var(--md-outline);font-family:inherit;transition:background var(--md-motion-short);}
.segmented button:last-child{border-right:none;}
.segmented button.active{background:var(--md-primary-container);color:var(--md-on-primary-container);}
.segmented button:hover:not(.active){background:var(--md-surface-container-high);}

/* --- Segmented Buttons --- */
.segmented.is-disabled{border-color:var(--md-dis-container);}
.segmented.is-disabled button{color:var(--md-dis-content);border-right-color:var(--md-dis-container);cursor:default;pointer-events:none;}
.segmented.is-disabled button.active{background:var(--md-dis-container);color:var(--md-dis-content);}

/* ===================== components/tabs.css ===================== */
/* ===== Tabs ===== */
.tabs{display:flex;border-bottom:1px solid var(--md-outline-variant);}
.tab{padding:12px 24px;border:none;background:transparent;color:var(--md-on-surface-variant);font-size:0.9rem;font-weight:500;cursor:pointer;position:relative;font-family:inherit;transition:color var(--md-motion-short);}
.tab.active{color:var(--md-primary);}
.tab.active::after{content:'';position:absolute;bottom:0;left:0;right:0;height:3px;background:var(--md-primary);border-radius:3px 3px 0 0;}

/* --- Tabs --- */
.tabs.is-disabled{border-bottom-color:var(--md-dis-container);}
.tabs.is-disabled .tab{color:var(--md-dis-content);cursor:default;pointer-events:none;}
.tabs.is-disabled .tab.active::after{display:none;}   /* 禁用的选项卡不画指示条 */

/* ===================== components/list.css ===================== */
/* ===== List ===== */
.list{background:var(--md-background);border-radius:var(--md-corner);overflow:hidden;}
.list-item{display:flex;align-items:center;gap:16px;padding:12px 16px;cursor:pointer;transition:background var(--md-motion-short);}
.list-item:hover{background:var(--md-surface-container-high);}
.list-item .leading{width:40px;height:40px;border-radius:50%;background:var(--md-primary-container);color:var(--md-on-primary-container);display:flex;align-items:center;justify-content:center;flex-shrink:0;}
.list-item .leading .material-symbols-rounded{font-size:22px;}
.list-item .body{flex:1;min-width:0;}
.list-item .title{font-size:0.95rem;font-weight:500;color:var(--md-on-surface);}
.list-item .subtitle{font-size:0.8rem;color:var(--md-on-surface-variant);}
.list-item .trailing{color:var(--md-on-surface-variant);font-size:0.8rem;}

/* --- List --- */
.list-item.is-disabled{cursor:default;pointer-events:none;}
.list-item.is-disabled .title,
.list-item.is-disabled .subtitle,
.list-item.is-disabled .trailing{color:var(--md-dis-content);}
.list-item.is-disabled .leading{background:var(--md-dis-container);color:var(--md-dis-content);}

/* ===================== components/divider.css ===================== */
/* ===== Divider ===== */
.divider{height:1px;background:var(--md-outline-variant);border:none;margin:0;}


/* ===================== components/badge.css ===================== */
/* ===== Badge ===== */
.badge-wrap{position:relative;display:inline-flex;}
/* 角标不带描边：原来用 box-shadow 画了一圈 3px 的背景色 ring，已去掉 */
.badge{position:absolute;top:-4px;right:-4px;min-width:16px;height:16px;padding:0 4px;border-radius:8px;background:var(--md-error);color:var(--md-on-error);font-size:0.65rem;font-weight:700;line-height:1;display:flex;align-items:center;justify-content:center;z-index:1;}


/* ===================== components/tooltip.css ===================== */
/* ===== Tooltip ===== */
.tooltip-wrap{position:relative;display:inline-flex;}
.tooltip{position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%) scale(.75);transform-origin:bottom center;background:var(--md-primary-container);color:var(--md-on-primary-container);padding:8px 14px;border-radius:var(--md-corner-small);font-size:0.75rem;font-weight:500;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity 120ms linear;box-shadow:var(--md-elev2);z-index:10;}
.tooltip-wrap:hover .tooltip{opacity:1;animation:tooltip-pop 420ms cubic-bezier(0.34,1.56,0.64,1) forwards;}
@keyframes tooltip-pop{
  0%{opacity:0;transform:translateX(-50%) scale(.7);}
  55%{opacity:1;transform:translateX(-50%) scale(1.12);}
  75%{opacity:1;transform:translateX(-50%) scale(.96);}
  100%{opacity:1;transform:translateX(-50%) scale(1);}
}


/* ===================== components/card.css ===================== */
/* ===== Cards ===== */
.card{background:var(--md-surface);border-radius:var(--md-corner-large);padding:16px;box-shadow:var(--md-elev1);}
.card-elevated{background:var(--md-surface-container-low);box-shadow:var(--md-elev1);}
.card-filled{background:var(--md-surface-container-highest);box-shadow:none;}
.card-outlined{background:var(--md-surface);border:2px solid var(--md-outline-variant);box-shadow:none;}
.card-title{font-size:1.1rem;font-weight:600;color:var(--md-on-surface);margin-bottom:8px;}
.card-text{font-size:0.9rem;color:var(--md-on-surface-variant);line-height:1.5;}

/* Cards / Dialog / Snackbar 不做禁用版：
   这三件都是「内容已经在页面里、由外部条件决定要不要出现」的容器 —— 卡片渲染出来就是可读的，
   对话框由触发它的那颗按钮决定能不能开，消息条本身就是操作结果的通知；
   给它们一个「灰掉但仍然杵在那儿」的样子没有任何使用场景，看着还像没渲染好，所以整块略过。
   真要挡，把它们里面的操作件（按钮）禁用掉就行 —— 那部分本来就已经有了。 */

/* ===================== components/dialog.css ===================== */
/* ===== Dialog ===== */
.dialog-overlay{position:fixed;inset:0;background:rgba(0,0,0,.5);display:flex;align-items:center;justify-content:center;z-index:1000;}
.dialog{background:var(--md-surface-container-high);border-radius:var(--md-corner-xl);padding:24px;max-width:400px;width:90%;box-shadow:var(--md-elev3);}
.dialog-title{font-size:1.25rem;font-weight:500;color:var(--md-on-surface);margin-bottom:12px;}
.dialog-content{font-size:0.9rem;color:var(--md-on-surface-variant);margin-bottom:24px;line-height:1.5;}
.dialog-actions{display:flex;justify-content:flex-end;gap:8px;}


/* ===================== components/snackbar.css ===================== */
/* ===== Snackbar ===== */
/* 反色对调：浅色模式下浅底深字，深色模式下深底浅字 */
.snackbar{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:var(--md-inverse-on-surface);color:var(--md-inverse-surface);padding:14px 20px;border-radius:var(--md-corner-xl);display:flex;align-items:center;gap:16px;box-shadow:var(--md-elev3);z-index:1000;min-height:48px;}
.snackbar .action{color:var(--md-primary);font-weight:500;cursor:pointer;background:none;border:none;font-family:inherit;}


/* ===================== components/menu.css ===================== */
/* ===== Menu ===== */
.menu{background:var(--md-surface-container);border-radius:var(--md-corner);padding:8px;box-shadow:var(--md-elev3);min-width:200px;}
.menu-item{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:var(--md-corner-small);cursor:pointer;color:var(--md-on-surface);font-size:0.9rem;transition:background var(--md-motion-short);}
.menu-item:hover{background:var(--md-surface-container-high);}
.menu-item .material-symbols-rounded{font-size:20px;color:var(--md-on-surface-variant);}

/* --- Menu --- */
.menu-item.is-disabled{color:var(--md-dis-content);cursor:default;pointer-events:none;}
.menu-item.is-disabled .material-symbols-rounded{color:var(--md-dis-content);}

/* ===================== components/nav-bar.css ===================== */
/* ===== Navigation Bar ===== */
.nav-bar{display:flex;justify-content:space-around;background:var(--md-surface-container);padding:12px 0;border-radius:var(--md-corner-large);width:100%;max-width:400px;}
.nav-bar-item{display:flex;flex-direction:column;align-items:center;gap:4px;padding:8px 12px;border-radius:var(--md-corner);cursor:pointer;color:var(--md-on-surface-variant);transition:all var(--md-motion-short);}
.nav-bar-item.active{color:var(--md-on-surface);}
.nav-bar-item .nav-icon{width:56px;height:32px;border-radius:16px;display:flex;align-items:center;justify-content:center;transition:background var(--md-motion-short);}
.nav-bar-item.active .nav-icon{background:var(--md-primary-container);}
.nav-bar-item.active .nav-icon .material-symbols-rounded{color:var(--md-on-primary-container);}
.nav-bar-item .material-symbols-rounded{font-size:22px;}
.nav-bar-item .label{font-size:0.7rem;font-weight:500;}

/* --- Navigation Bar / Rail / Drawer --- */
.nav-bar-item.is-disabled,.nav-rail-item.is-disabled{color:var(--md-dis-content);cursor:default;pointer-events:none;}
.nav-bar-item.is-disabled .nav-icon,.nav-rail-item.is-disabled .nav-icon{background:transparent;}
.nav-bar-item.is-disabled .nav-icon .material-symbols-rounded,
.nav-rail-item.is-disabled .nav-icon .material-symbols-rounded{color:var(--md-dis-content);}

/* ===================== components/nav-rail.css ===================== */
/* ===== Navigation Rail ===== */
.nav-rail{display:flex;flex-direction:column;align-items:center;gap:12px;background:var(--md-surface-container);padding:16px 8px;border-radius:var(--md-corner-large);width:80px;}
.nav-rail-item{display:flex;flex-direction:column;align-items:center;gap:4px;padding:8px;border-radius:var(--md-corner);cursor:pointer;color:var(--md-on-surface-variant);transition:all var(--md-motion-short);width:100%;}
.nav-rail-item.active{color:var(--md-on-surface);}
.nav-rail-item .nav-icon{width:56px;height:32px;border-radius:16px;display:flex;align-items:center;justify-content:center;transition:background var(--md-motion-short);}
.nav-rail-item.active .nav-icon{background:var(--md-primary-container);}
/* 选中图标要跟 nav-bar / drawer 对齐，都是 on-primary-container（深色模式下就是那抹淡蓝）。
   之前漏了这一条，图标只能继承 .active 的 on-surface，所以只有它是白的 */
.nav-rail-item.active .nav-icon .material-symbols-rounded{color:var(--md-on-primary-container);}
.nav-rail-item .material-symbols-rounded{font-size:22px;font-variation-settings:'FILL' 0,'wght' 400,'GRAD' 0,'opsz' 24;transition:font-variation-settings 200ms cubic-bezier(0.2,0,0,1);}
.nav-rail-item.active .material-symbols-rounded{font-variation-settings:'FILL' 1,'wght' 400,'GRAD' 0,'opsz' 24;}
.nav-rail-item .label{font-size:0.7rem;font-weight:500;}

/* --- Navigation Bar / Rail / Drawer --- */
.nav-bar-item.is-disabled,.nav-rail-item.is-disabled{color:var(--md-dis-content);cursor:default;pointer-events:none;}
.nav-bar-item.is-disabled .nav-icon,.nav-rail-item.is-disabled .nav-icon{background:transparent;}
.nav-bar-item.is-disabled .nav-icon .material-symbols-rounded,
.nav-rail-item.is-disabled .nav-icon .material-symbols-rounded{color:var(--md-dis-content);}

/* ===================== components/nav-drawer.css ===================== */
/* ===== Navigation Drawer ===== */
.nav-drawer{position:relative;background:var(--md-surface-container-low);border-radius:var(--md-corner-large);padding:12px;width:256px;overflow:hidden;}
.nav-drawer-title{font-size:0.75rem;font-weight:500;color:var(--md-on-surface-variant);padding:8px 16px;text-transform:uppercase;letter-spacing:.5px;}
.nav-drawer-indicator{position:absolute;top:0;left:12px;width:calc(100% - 24px);height:48px;border-radius:999px;background:var(--md-primary-container);transition:transform 300ms cubic-bezier(0.2,0,0,1);z-index:0;pointer-events:none;}
.nav-drawer-item{position:relative;display:flex;align-items:center;gap:12px;height:48px;padding:0 16px;border-radius:0;background:transparent;cursor:pointer;color:var(--md-on-surface-variant);font-size:0.9rem;font-weight:500;transition:color var(--md-motion-short),background var(--md-motion-short),border-radius var(--md-motion-short);z-index:1;}
/* 未选中项 hover：MD3 是 8% 的 on-surface 状态层，实际观感就是一层灰。
   圆角跟着指示器做成胶囊，跟那个滑动指示器对上 */
.nav-drawer-item:hover{color:var(--md-on-surface);background:color-mix(in srgb, var(--md-on-surface) 8%, transparent);border-radius:999px;}
.nav-drawer-item.active{color:var(--md-on-primary-container);}
/* 选中项已经有指示器了，hover 时不再叠灰 */
.nav-drawer-item.active:hover{background:transparent;}
.nav-drawer-item .material-symbols-rounded{font-size:22px;font-variation-settings:'FILL' 0,'wght' 400,'GRAD' 0,'opsz' 24;transition:font-variation-settings 200ms cubic-bezier(0.2,0,0,1);}
.nav-drawer-item.active .material-symbols-rounded{font-variation-settings:'FILL' 1,'wght' 400,'GRAD' 0,'opsz' 24;}
/* 抽屉导航 —— 渐显模式：隐藏滑动指示器，选中项自身背景与文字颜色淡入淡出
   （旧选中先淡出，新选中再淡入，见底部 JS 的 setTimeout 时序） */
.nav-drawer.is-fade .nav-drawer-indicator{opacity:0;}
.nav-drawer.is-fade .nav-drawer-item{
  border-radius:999px;
  transition:background 240ms cubic-bezier(0.2,0,0,1),
             color 240ms cubic-bezier(0.2,0,0,1),
             border-radius var(--md-motion-short);
}
.nav-drawer.is-fade .nav-drawer-item.active{
  background:var(--md-primary-container);
  color:var(--md-on-primary-container);
}
/* 渐显模式下，已选项 hover 仍保持选中背景 */
.nav-drawer.is-fade .nav-drawer-item.active:hover{background:var(--md-primary-container);}
/* 切换动画模式（滑动↔渐显）时不播放任何过渡：由 JS 在按钮点击瞬间加 .no-anim，下一帧移除 */
.nav-drawer.no-anim .nav-drawer-indicator,
.nav-drawer.no-anim .nav-drawer-item,
.nav-drawer.no-anim .nav-drawer-item .material-symbols-rounded{transition:none !important;}

.nav-drawer-item.is-disabled{color:var(--md-dis-content);background:transparent;cursor:default;pointer-events:none;}
.nav-drawer-item.is-disabled .material-symbols-rounded{color:var(--md-dis-content);}
.nav-drawer-item.is-disabled.active{color:var(--md-dis-content);}

/* ===================== components/toast.css ===================== */
/* ===== Toast 轻提示（自研，非 MD3 官方；复刻自 skyargforum） ===== */
.toast-container{position:fixed;bottom:24px;right:16px;z-index:1100;display:flex;flex-direction:column-reverse;gap:8px;align-items:flex-end;pointer-events:none;}
.custom-toast{
  display:flex;align-items:center;
  background:color-mix(in srgb, var(--md-surface-container-high) 75%, transparent);
  color:var(--md-on-surface);padding:12px 20px;border-radius:28px;
  box-shadow:var(--md-elev3);
  animation:toastSlideIn .3s cubic-bezier(0.2,0,0,1);
  transition:opacity .3s ease,transform .3s ease;
  border:2px solid color-mix(in srgb, var(--md-outline-variant) 50%, transparent);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  pointer-events:auto;
}
.custom-toast.removing{opacity:0;transform:translateX(100%);}
@keyframes toastSlideIn{from{opacity:0;transform:translateX(100%);}to{opacity:1;transform:translateX(0);}}
.toast-icon{font-size:24px;margin-right:12px;}
.toast-content{display:flex;flex-direction:column;}
.toast-title{font-weight:600;font-size:.875rem;}
.toast-desc{font-size:.75rem;color:var(--md-on-surface-variant);}


/* ===================== components/theme-picker.css ===================== */
/* ===== 主题色切换组件（结构参考旧项目 settings 面板的 color-picker-grid） ===== */
.theme-switcher{
  display:flex;flex-wrap:wrap;align-items:center;gap:8px;
  padding:16px;border-radius:var(--md-corner-large);
  background:var(--md-surface-container);
  border:2px solid var(--md-outline-variant);
  width:100%;max-width:360px;
}
.ts-head{display:flex;align-items:center;gap:8px;font-size:0.85rem;font-weight:600;color:var(--md-on-surface);}
.ts-head .material-symbols-rounded{font-size:18px;color:var(--md-primary);}
/* 预设色：右上角（#themePicker）和组件内（#demoThemePicker）共用这一套「胶囊 + 圆点」 */
/* 预设色：右上角 / 组件内共用的「胶囊 + 圆点」（原项目 .theme-picker / .theme-picker-btn 原样） */
.theme-picker{
  display:flex;align-items:center;gap:4px;
  padding:4px;border-radius:100px;
  background:var(--md-surface-container-high);
  position:relative;box-shadow:inset 0 1px 3px rgba(0,0,0,0.1);
}
.theme-picker-btn{
  width:28px;height:28px;border-radius:50%;border:2px solid transparent;cursor:pointer;
  transition:transform .2s,border-color .2s,box-shadow .2s;
  position:relative;z-index:1;padding:0;flex-shrink:0;
}
.theme-picker-btn:hover{transform:scale(1.08);z-index:2;}
.theme-picker-btn.active{
  transform:scale(1.12);z-index:3;
  box-shadow:0 0 0 2px var(--md-surface-container-high),0 0 0 4px var(--md-primary);
}
.ts-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
/* 自定义取色圆（右上角 .seed-color-btn + 组件内 .ts-custom，同一套）：
   彩虹底表示「随便挑」，点开的是本页自建的取色器 —— 原生 input[type=color] 弹的是系统面板，
   CSS 碰不到，只能自己补一个（见下面 .tsp-pop） */
.seed-color-btn,.ts-custom{
  position:relative;width:28px;height:28px;flex-shrink:0;
  border-radius:50%;overflow:hidden;cursor:pointer;
  border:2px solid var(--md-outline-variant);
  background:conic-gradient(red,orange,yellow,lime,cyan,blue,magenta,red);
  transition:transform var(--md-motion-short) var(--md-easing);
}
.seed-color-btn:hover,.ts-custom:hover{transform:scale(1.06);}
.seed-color-btn input[type=color],.ts-custom input[type=color]{
  position:absolute;inset:0;width:100%;height:100%;
  opacity:0;cursor:pointer;border:none;padding:0;margin:0;
}
/* 脚本跑起来（body 挂上 has-tsp）之后，原生 input 不再收点击 —— 不然点下去还是会弹系统取色面板 */
body.has-tsp .seed-color-btn input[type=color],body.has-tsp .ts-custom input[type=color]{pointer-events:none;}
.ts-hex{
  font-family:ui-monospace,Consolas,'Courier New',monospace;font-size:0.85rem;letter-spacing:.5px;
  color:var(--md-on-surface);background:var(--md-surface-container-high);
  padding:7px 11px;border-radius:var(--md-corner-small);
  border:2px solid var(--md-outline-variant);
}
.ts-note{font-size:0.72rem;color:var(--md-on-surface-variant);line-height:1.5;}


/* ===================== components/color-picker.css ===================== */
/* ===== 自建取色器（点彩虹圆弹出来的那个） =====
   原生 input[type=color] 点开是系统取色面板，样式完全在浏览器手里，改不到，
   所以这里自己补一个：饱和度/明度方块 + 色相条 + hex 输入，拖动实时改主题色。
   外壳跟 Dialog 同一套（surface-container-high + elev3 + 大圆角），挂在 body 上、fixed 定位。
   原生 input 仍留在 DOM 里（复制出去的代码还是原生取色），只是被挡住不再弹系统面板 */
.tsp-pop{
  position:fixed;left:0;top:0;z-index:90;width:264px;box-sizing:border-box;padding:14px;
  background:var(--md-surface-container-high);border-radius:var(--md-corner-large);
  box-shadow:var(--md-elev3);
  opacity:0;visibility:hidden;transform:translateY(-6px) scale(.96);transform-origin:top right;
  transition:opacity var(--md-motion-short) var(--md-easing),transform var(--md-motion-short) var(--md-easing),
             visibility 0s var(--md-motion-short);
}
.tsp-pop.is-open{opacity:1;visibility:visible;transform:none;
  transition:opacity var(--md-motion-short) var(--md-easing),transform var(--md-motion-short) var(--md-easing),visibility 0s 0s;}
.tsp-title{font-size:0.8rem;font-weight:600;color:var(--md-on-surface);margin-bottom:10px;}
/* 饱和度（左右）/ 明度（上下）方块：底层是纯色相，上面叠一层白、一层黑 */
.tsp-sv{
  position:relative;height:132px;border-radius:var(--md-corner-small);cursor:crosshair;touch-action:none;
  background:
    linear-gradient(to top,#000,rgba(0,0,0,0)),
    linear-gradient(to right,#fff,rgba(255,255,255,0)),
    hsl(var(--tsp-h,270) 100% 50%);
}
.tsp-hue{
  position:relative;height:14px;margin-top:12px;border-radius:999px;cursor:pointer;touch-action:none;
  background:linear-gradient(to right,#f00,#ff0,#0f0,#0ff,#00f,#f0f,#f00);
}
/* 两个滑块都是「白圈 + 一圈淡黑描边」，压在任何底色上都看得见 */
.tsp-knob{
  position:absolute;left:0;top:0;width:16px;height:16px;border-radius:50%;
  border:3px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,.3),0 1px 3px rgba(0,0,0,.45);
  transform:translate(-50%,-50%);pointer-events:none;
}
.tsp-hue .tsp-knob{top:50%;}
.tsp-foot{display:flex;align-items:center;gap:8px;margin-top:12px;}
.tsp-chip{width:28px;height:28px;border-radius:50%;flex-shrink:0;background:#6750A4;
  box-shadow:inset 0 0 0 1px rgba(128,128,128,.35);}
.tsp-hex{
  flex:1;min-width:0;box-sizing:border-box;padding:7px 8px;text-align:center;
  font-family:ui-monospace,Consolas,'Courier New',monospace;font-size:0.82rem;letter-spacing:.5px;
  color:var(--md-on-surface);background:var(--md-surface-container-low);
  border:2px solid var(--md-outline);border-radius:var(--md-corner-small);outline:none;
  transition:border-color var(--md-motion-short) var(--md-easing);
}
.tsp-hex:focus{border-color:var(--md-primary);}
.tsp-ok{
  flex-shrink:0;padding:8px 12px;border:none;border-radius:999px;cursor:pointer;
  background:transparent;color:var(--md-primary);
  font-family:inherit;font-size:0.78rem;font-weight:600;
  transition:background var(--md-motion-short) var(--md-easing);
}
.tsp-ok:hover{background:color-mix(in srgb, var(--md-primary) 12%, transparent);}

