/* UI 素材库 · ui-kit.css —— 由 build.py 生成于 2026-08-26，请勿直接编辑此文件 */

/* ============================================================
 * UI 素材库 · 设计令牌（可选）
 * 每个组件的 CSS 都自带 fallback 默认值，所以这份令牌不是必需的；
 * 引入它只是为了让多个组件共用同一套配色与缓动，改一处全局生效。
 * 接入项目时：把下面的值改成项目自己的品牌色即可。
 * ============================================================ */
:root{
  /* 配色 */
  --uik-fg:#1d1d1f;             /* 主文字 / 像素格颜色 */
  --uik-muted:#86868b;          /* 次要文字 */
  --uik-line:rgba(67,67,74,.3); /* 描边 */
  --uik-accent:#0096d6;         /* 强调色（悬停填充起点） */
  --uik-accent-2:#46c0ee;       /* 强调色渐变终点 */
  --uik-on-accent:#ffffff;      /* 强调色之上的文字 */
  --uik-accent-shadow:rgba(0,150,214,.26);
  --uik-danger:#ff6b6b;         /* 危险操作（菜单里的「删除」） */

  /* 毛玻璃浮层（DropMenu）：默认深色，浅色底上是深色浮层，这是原件的样子 */
  --uik-glass:rgba(17,17,17,.6);
  --uik-glass-hi:rgba(17,17,17,.82);
  --uik-glass-fg:#ffffff;

  /* 实色浮层（DateFilter 的日历面板） */
  --uik-surface:#ffffff;        /* 面板底 */
  --uik-surface-2:#f5f5f7;      /* 面板内的次级底（悬停 / 区间内） */

  /* 缓动：三条曲线来自原组件，改动会明显改变手感，非必要不动 */
  --uik-ease-flow:cubic-bezier(.23,1,.32,1);    /* 收边 / 变色 */
  --uik-ease-circle:cubic-bezier(.19,1,.22,1);  /* 圆形扩散 */
  --uik-ease-back:cubic-bezier(.34,1.56,.64,1); /* 箭头回弹 */
}

/* 暗色：跟随系统，或手动给根元素加 data-uik-theme="dark" */
@media (prefers-color-scheme:dark){
  :root:not([data-uik-theme="light"]){
    --uik-fg:#f5f5f7; --uik-muted:#8e8e93; --uik-line:rgba(235,235,245,.28);
    --uik-surface:#1f1f22; --uik-surface-2:#2a2a2e;
  }
}
:root[data-uik-theme="dark"]{
  --uik-fg:#f5f5f7; --uik-muted:#8e8e93; --uik-line:rgba(235,235,245,.28);
  --uik-surface:#1f1f22; --uik-surface-2:#2a2a2e;
}


/* ==== flow-button ==== */

/* ============================================================
 * FlowButton —— 悬停时圆形色块自中心扩散铺满，左箭头滑入、右箭头滑出，
 * 文字右移让位，胶囊圆角收成圆角矩形。
 * 原件：React + Tailwind + lucide-react；此处为等效的原生 CSS 实现。
 * 依赖：无。令牌缺失时用 fallback 默认值。
 * 结构：
 *   <button class="flowbtn">
 *     <span class="fb-c"></span>
 *     <svg class="fb-arr fb-arr-l">…</svg>
 *     <span class="fb-t">文字</span>
 *     <svg class="fb-arr fb-arr-r">…</svg>
 *   </button>
 * ============================================================ */
.flowbtn{
  position:relative;display:inline-flex;align-items:center;justify-content:center;gap:4px;
  overflow:hidden;isolation:isolate;background:transparent;
  border:1.5px solid var(--uik-line,rgba(67,67,74,.3));border-radius:980px;
  padding:8px 22px;font-family:inherit;font-size:13px;font-weight:600;
  color:var(--uik-fg,#1d1d1f);cursor:pointer;
  transition:color .6s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)),
             border-color .6s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)),
             border-radius .6s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)),
             box-shadow .6s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.flowbtn:hover:not(:disabled){
  background:transparent;border-color:transparent;border-radius:12px;
  color:var(--uik-on-accent,#fff);
  box-shadow:0 6px 18px var(--uik-accent-shadow,rgba(0,150,214,.26));
}
.flowbtn:active:not(:disabled){transform:scale(.95)}
.flowbtn:focus-visible{outline:2px solid var(--uik-accent,#0096d6);outline-offset:2px}
.flowbtn:disabled{opacity:.45;cursor:default}

/* 扩散的圆：z-index 0，压在文字（z1）与箭头（z9）之下 */
.flowbtn .fb-c{
  position:absolute;top:50%;left:50%;z-index:0;width:16px;height:16px;
  transform:translate(-50%,-50%);border-radius:50%;opacity:0;pointer-events:none;
  background:linear-gradient(135deg,var(--uik-accent,#0096d6),var(--uik-accent-2,#46c0ee));
  transition:width .8s var(--uik-ease-circle,cubic-bezier(.19,1,.22,1)),
             height .8s var(--uik-ease-circle,cubic-bezier(.19,1,.22,1)),
             opacity .8s var(--uik-ease-circle,cubic-bezier(.19,1,.22,1));
}
/* 280px 需大于按钮对角线；按钮特别宽时把这里调大，否则四角会露白 */
.flowbtn:hover:not(:disabled) .fb-c{width:280px;height:280px;opacity:1}

.flowbtn .fb-arr{
  position:absolute;z-index:9;width:15px;height:15px;fill:none;
  stroke:var(--uik-fg,#1d1d1f);stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
  transition:left .8s var(--uik-ease-back,cubic-bezier(.34,1.56,.64,1)),
             right .8s var(--uik-ease-back,cubic-bezier(.34,1.56,.64,1)),
             stroke .6s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.flowbtn .fb-arr-l{left:-25%}
.flowbtn .fb-arr-r{right:13px}
.flowbtn:hover:not(:disabled) .fb-arr-l{left:13px;stroke:var(--uik-on-accent,#fff)}
.flowbtn:hover:not(:disabled) .fb-arr-r{right:-25%;stroke:var(--uik-on-accent,#fff)}

.flowbtn .fb-t{
  position:relative;z-index:1;transform:translateX(-11px);
  transition:transform .8s cubic-bezier(.16,1,.3,1);
}
.flowbtn:hover:not(:disabled) .fb-t{transform:translateX(11px)}

@media (prefers-reduced-motion:reduce){
  .flowbtn,.flowbtn *{transition-duration:.12s!important}
  .flowbtn .fb-t,.flowbtn:hover:not(:disabled) .fb-t{transform:none}
}


/* ==== loading-state ==== */

/* ============================================================
 * LoadingState —— 3x3 像素网格 + 流光标签 + 等宽实时计时。
 * 适合 10 秒以上的长任务：转圈只说明"在跑"，这个还回答"跑了多久"。
 * 原件：React + Tailwind；此处为等效的原生 CSS 实现。依赖：无。
 * 结构：
 *   <span class="loadst">
 *     <span class="pg" aria-hidden="true"><i class="pg-c" style="animation:…"></i> x9</span>
 *     <span class="ld-l">标签</span>
 *     <span class="ld-t">1.2s</span>
 *   </span>
 * ============================================================ */
.loadst{display:inline-flex;align-items:center;gap:10px;vertical-align:middle}

.loadst .pg{display:grid;grid-template-columns:repeat(3,4px);gap:1.5px}
.loadst .pg-c{display:block;width:4px;height:4px;border-radius:1px;
  background:var(--uik-fg,#1d1d1f)}
.loadst .pg-c.rd{border-radius:50%}

.loadst .ld-l{font-size:13px;font-weight:500;
  background-image:linear-gradient(90deg,
    var(--uik-muted,#86868b) 35%, var(--uik-fg,#1d1d1f) 50%, var(--uik-muted,#86868b) 65%);
  background-size:200% 100%;
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent;
  animation:shimmer-text 1.4s linear infinite}

/* tabular-nums：读秒时数字宽度恒定，不会左右跳 */
.loadst .ld-t{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:12px;color:var(--uik-muted,#86868b);font-variant-numeric:tabular-nums}

@keyframes pixel-on{0%,100%{opacity:.12}50%{opacity:1}}
@keyframes shimmer-text{0%{background-position:200% 0}100%{background-position:-200% 0}}

@media (prefers-reduced-motion:reduce){
  /* 网格冻结在暗态、标签停止流光；计时器照常走 */
  .loadst .pg-c{animation:none!important;opacity:.2!important}
  .loadst .ld-l{animation:none;-webkit-text-fill-color:var(--uik-muted,#86868b);
    color:var(--uik-muted,#86868b)}
}


/* ==== slide-tabs ==== */

/* ============================================================
 * SlideTabs —— 一条滑动的高亮块在标签之间移动：悬停时先滑过去「预演」，
 * 点击才真正切换；鼠标移出容器，滑块自动回到当前选中项。
 * 适合做切片器 / 分段控件 / 视图切换。
 * 原件：React + framer-motion；此处为等效的原生 CSS 实现（transform+width 过渡）。
 * 依赖：无。令牌缺失时用 fallback 默认值。
 * 结构：
 *   <div class="slidetabs" role="tablist">
 *     <span class="st-cur"></span>          滑块（z0）
 *     <button class="st-t on">全部</button>  标签（z1），.on = 滑块当前所在项
 *     …
 *   </div>
 * ============================================================ */
.slidetabs{
  position:relative;display:inline-flex;align-items:center;gap:2px;
  padding:3px;max-width:100%;
  border:1.5px solid var(--uik-line,rgba(67,67,74,.3));border-radius:980px;
  background:transparent;
  /* 标签多到放不下时横向滚动，滚动条藏起来（滑块随内容一起滚，位置不会错） */
  overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-webkit-overflow-scrolling:touch;
}
.slidetabs::-webkit-scrollbar{display:none}

/* 铺满一行、等分每一格：表头上方的切片器常用 */
.slidetabs.fill{display:flex;width:100%}
.slidetabs.fill .st-t{flex:1 1 0;min-width:0}

/* 滑块：left/width 由 JS 写成 transform+width，两者都走同一条缓动 */
.slidetabs .st-cur{
  position:absolute;z-index:0;left:0;top:3px;bottom:3px;width:0;
  border-radius:980px;opacity:0;pointer-events:none;
  transform:translateX(0);
  background:linear-gradient(135deg,var(--uik-accent,#0096d6),var(--uik-accent-2,#46c0ee));
  box-shadow:0 4px 12px var(--uik-accent-shadow,rgba(0,150,214,.26));
  transition:transform .45s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)),
             width .45s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)),
             opacity .3s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}

.slidetabs .st-t{
  position:relative;z-index:1;flex:0 0 auto;
  appearance:none;-webkit-appearance:none;background:transparent;border:0;
  padding:9px 18px;border-radius:980px;
  font-family:inherit;font-size:13px;font-weight:600;line-height:1;
  color:var(--uik-muted,#86868b);white-space:nowrap;cursor:pointer;
  transition:color .35s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.slidetabs .st-t:hover:not(:disabled){color:var(--uik-fg,#1d1d1f)}
/* .on = 滑块此刻停在这一项（可能是悬停预演，未必是选中项）——
   所以文字变色只认 .on，不认 aria-selected，颜色永远跟着滑块走 */
.slidetabs .st-t.on{color:var(--uik-on-accent,#fff)}
.slidetabs .st-t:focus-visible{outline:2px solid var(--uik-accent,#0096d6);outline-offset:2px}
.slidetabs .st-t:disabled{opacity:.4;cursor:default}

@media (prefers-reduced-motion:reduce){
  .slidetabs .st-cur,.slidetabs .st-t{transition-duration:.12s!important}
}


/* ==== drop-menu ==== */

/* ============================================================
 * DropMenu —— 毛玻璃下拉菜单：面板带模糊淡入 + 缩放，条目逐条错峰滑入，
 * 触发按钮的箭头翻转 180°。
 * 原件：React + framer-motion + shadcn Button；此处为等效的原生实现。
 * 依赖：无。令牌缺失时用 fallback 默认值。
 * 结构：
 *   <button class="dm-btn" aria-haspopup="menu">文字<svg class="dm-chev"></svg></button>
 *   <div class="dropmenu" role="menu">          面板挂在 <body> 下，position:fixed
 *     <button class="dm-i" role="menuitem"><span class="dm-ic">…</span>编辑</button>
 *     <div class="dm-sep"></div>
 *   </div>
 * ============================================================ */

/* ---------- 触发按钮 ---------- */
.dm-btn{
  display:inline-flex;align-items:center;gap:8px;
  padding:9px 16px;border:0;border-radius:12px;
  background:var(--uik-glass,rgba(17,17,17,.6));
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  box-shadow:0 0 20px rgba(0,0,0,.2);
  color:var(--uik-glass-fg,#fff);
  font-family:inherit;font-size:13px;font-weight:600;line-height:1;
  cursor:pointer;
  transition:background .35s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.dm-btn:hover:not(:disabled){background:var(--uik-glass-hi,rgba(17,17,17,.82))}
.dm-btn:active:not(:disabled){transform:scale(.97)}
.dm-btn:focus-visible{outline:2px solid var(--uik-accent,#0096d6);outline-offset:2px}
.dm-btn:disabled{opacity:.45;cursor:default}

.dm-btn .dm-chev{
  width:15px;height:15px;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
  transition:transform .4s var(--uik-ease-back,cubic-bezier(.34,1.56,.64,1));
}
.dm-btn[aria-expanded="true"] .dm-chev{transform:rotate(180deg)}

/* ---------- 面板 ---------- */
/* position:fixed + 挂在 body 下：工具栏常有 overflow:hidden / transform，
   用 absolute 会被裁掉；坐标由 JS 按触发器的 rect 算，越界自动翻边。 */
.dropmenu{
  position:fixed;z-index:9999;left:0;top:0;
  display:flex;flex-direction:column;gap:2px;
  min-width:176px;max-height:min(60vh,420px);overflow-y:auto;
  padding:5px;border-radius:14px;
  background:var(--uik-glass,rgba(17,17,17,.6));
  -webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);
  box-shadow:0 0 20px rgba(0,0,0,.2);
  opacity:0;filter:blur(10px);pointer-events:none;
  transform:translateY(-5px) scale(.95);
  transition:opacity .28s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)),
             transform .28s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)),
             filter .28s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.dropmenu.up:not(.open){transform:translateY(5px) scale(.95)}   /* 向上弹时从下方进入 */
.dropmenu.open{opacity:1;filter:none;transform:none;pointer-events:auto}

/* ---------- 条目 ---------- */
.dropmenu .dm-i{
  display:flex;align-items:center;gap:9px;width:100%;
  padding:10px 11px;border:0;border-radius:9px;background:transparent;
  color:var(--uik-glass-fg,#fff);
  font-family:inherit;font-size:13px;line-height:1.2;text-align:left;
  cursor:pointer;
  transition:background .25s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.dropmenu .dm-i:hover:not(:disabled),
.dropmenu .dm-i:focus-visible{background:rgba(255,255,255,.12);outline:none}
.dropmenu .dm-i:active:not(:disabled){transform:scale(.97)}
.dropmenu .dm-i:disabled{opacity:.4;cursor:default}
.dropmenu .dm-i.danger{color:var(--uik-danger,#ff6b6b)}

.dropmenu .dm-ic{display:inline-flex;flex:0 0 auto;width:16px;height:16px}
.dropmenu .dm-ic svg{width:16px;height:16px;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.dropmenu .dm-sep{height:1px;margin:4px 6px;background:rgba(255,255,255,.14)}
.dropmenu .dm-empty{padding:10px 11px;font-size:12px;opacity:.6;
  color:var(--uik-glass-fg,#fff)}

/* 条目错峰滑入：--i 由 JS 写在每个条目上；关闭时不逐条演，整块淡出更利落 */
.dropmenu.open .dm-i,.dropmenu.open .dm-sep{
  animation:dm-in .4s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)) both;
  animation-delay:calc(var(--i,0) * 60ms);
}
@keyframes dm-in{
  from{opacity:0;transform:translateX(10px) scale(.95);filter:blur(10px)}
  to{opacity:1;transform:none;filter:none}
}

@media (prefers-reduced-motion:reduce){
  .dropmenu,.dropmenu .dm-i,.dm-btn,.dm-btn .dm-chev{transition-duration:.12s!important}
  .dropmenu.open .dm-i,.dropmenu.open .dm-sep{animation:none}
}


/* ==== menu-vertical ==== */

/* ============================================================
 * MenuVertical —— 竖排大字导航：悬停时箭头自左侧滑入、文字右移让位并变色，
 * 可选斜切（skew）。做落地页导航 / 目录 / 分区入口。
 * 原件：React + motion/react + lucide-react + next/link；此处为等效的原生实现。
 * 依赖：无。令牌缺失时用 fallback 默认值。
 * 结构：
 *   <nav class="menuvert">
 *     <a class="mv-i" href="#a">
 *       <span class="mv-a"><svg class="mv-arr"></svg></span>   箭头（外层负责裁剪）
 *       <span class="mv-t">关于</span>
 *     </a>
 *   </nav>
 * 尺寸只由两个变量决定，改一处全跟着走：
 *   --mv-size   文字大小（默认 clamp(28px,5vw,36px)）
 *   --mv-arrow  箭头边长（默认 1.1em，随文字缩放）
 * 另有 --mv-color（高亮色，默认取 --uik-accent）与 --mv-skew（斜切角）。
 * ============================================================ */
.menuvert{
  display:flex;flex-direction:column;align-items:flex-start;gap:16px;
  width:-moz-fit-content;width:fit-content;max-width:100%;
  --mv-size:clamp(28px,5vw,36px);
  --mv-arrow:1.1em;
  --mv-skew:0deg;
}

.menuvert .mv-i{
  display:flex;align-items:center;gap:8px;max-width:100%;
  padding:2px .35em 2px 0;          /* 右侧留一点，斜切时字不会被裁 */
  font-size:var(--mv-size);font-weight:600;line-height:1.2;
  color:var(--uik-fg,#1d1d1f);text-decoration:none;
  background:none;border:0;font-family:inherit;cursor:pointer;
}
.menuvert .mv-i:focus{outline:none}
.menuvert .mv-i:focus-visible{outline:2px solid var(--uik-accent,#0096d6);outline-offset:4px;border-radius:4px}

/* 箭头静止时缩到自身宽度之外，由这层裁掉——否则它会探到列表左边界之外 */
.menuvert .mv-a{
  display:block;flex:0 0 auto;overflow:hidden;
  width:var(--mv-arrow);height:var(--mv-arrow);
}
.menuvert .mv-arr{
  display:block;width:100%;height:100%;
  fill:none;stroke:currentColor;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;
  opacity:0;transform:translateX(-100%);
  transition:transform .3s ease-out,opacity .3s ease-out,color .3s ease-out;
}

.menuvert .mv-t{
  display:block;
  /* 静止时左移盖住箭头的位置；悬停归位，视觉上是「箭头把文字推开」 */
  transform:translateX(calc(-1 * var(--mv-arrow)));
  transition:transform .3s ease-out,color .3s ease-out;
}

/* 键盘 :focus-visible 与鼠标 :hover 同一套动效——不然只有鼠标用户看得见反馈 */
.menuvert .mv-i:hover .mv-arr,
.menuvert .mv-i:focus-visible .mv-arr,
.menuvert .mv-i[aria-current] .mv-arr{
  opacity:1;transform:none;color:var(--mv-color,var(--uik-accent,#0096d6));
}
.menuvert .mv-i:hover .mv-t,
.menuvert .mv-i:focus-visible .mv-t{
  transform:skewX(var(--mv-skew));color:var(--mv-color,var(--uik-accent,#0096d6));
}
/* 当前项：常驻高亮且不斜切（它不是在响应指针，斜着会显得歪） */
.menuvert .mv-i[aria-current] .mv-t{transform:none;color:var(--mv-color,var(--uik-accent,#0096d6))}

.menuvert .mv-i[aria-disabled="true"]{opacity:.4;pointer-events:none}

@media (prefers-reduced-motion:reduce){
  .menuvert .mv-arr,.menuvert .mv-t{transition-duration:.12s!important}
  .menuvert .mv-i:hover .mv-t,
  .menuvert .mv-i:focus-visible .mv-t{transform:none}   /* 斜切对前庭敏感者不友好 */
}


/* ==== date-filter ==== */

/* ============================================================
 * DateFilter —— 日期筛选：单日 / 日期区间，带快捷区间（本月、近 30 天…）。
 * 原件：react-aria-components 的 JollyDatePicker / JollyDateRangePicker
 *（依赖 react-aria + shadcn 的 calendar/datefield/field/popover 一整套）；
 * 此处为等效的原生实现，无依赖。
 * 结构：
 *   <button class="df-btn"><svg class="df-ic"></svg><span class="df-v">2026-01-01 ~ …</span></button>
 *   <div class="datefilter">                面板挂在 <body> 下，position:fixed
 *     <div class="df-pre">快捷区间</div>
 *     <div class="df-cals"><div class="df-cal">…</div></div>
 *     <div class="df-foot">…</div>
 *   </div>
 * ============================================================ */

/* ---------- 触发器 ---------- */
.df-btn{
  display:inline-flex;align-items:center;gap:8px;
  padding:8px 12px;border:1.5px solid var(--uik-line,rgba(67,67,74,.3));border-radius:10px;
  background:transparent;color:var(--uik-fg,#1d1d1f);
  font-family:inherit;font-size:13px;font-weight:600;line-height:1.2;
  cursor:pointer;
  transition:border-color .3s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.df-btn:hover:not(:disabled){border-color:var(--uik-accent,#0096d6)}
.df-btn:focus-visible{outline:2px solid var(--uik-accent,#0096d6);outline-offset:2px}
.df-btn:disabled{opacity:.45;cursor:default}
.df-btn[aria-expanded="true"]{border-color:var(--uik-accent,#0096d6)}
.df-btn .df-ic{width:15px;height:15px;flex:0 0 auto;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.df-btn .df-v{white-space:nowrap;font-variant-numeric:tabular-nums}
.df-btn .df-v.empty{color:var(--uik-muted,#86868b);font-weight:500}

/* ---------- 面板 ---------- */
.datefilter{
  position:fixed;z-index:9999;left:0;top:0;display:flex;
  padding:10px;border-radius:14px;
  border:1px solid var(--uik-line,rgba(67,67,74,.3));
  background:var(--uik-surface,#fff);color:var(--uik-fg,#1d1d1f);
  box-shadow:0 12px 34px rgba(0,0,0,.16);
  font-family:inherit;font-size:13px;
  opacity:0;transform:translateY(-4px) scale(.98);pointer-events:none;
  transition:opacity .22s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)),
             transform .22s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.datefilter.up:not(.open){transform:translateY(4px) scale(.98)}
.datefilter.open{opacity:1;transform:none;pointer-events:auto}

/* 快捷区间 */
.datefilter .df-pre{
  display:flex;flex-direction:column;gap:2px;
  padding-right:10px;margin-right:10px;min-width:92px;
  border-right:1px solid var(--uik-line,rgba(67,67,74,.3));
}
.datefilter .df-pre button{
  padding:7px 10px;border:0;border-radius:8px;background:transparent;
  color:var(--uik-fg,#1d1d1f);font-family:inherit;font-size:12.5px;text-align:left;
  white-space:nowrap;cursor:pointer;
  transition:background .2s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.datefilter .df-pre button:hover{background:var(--uik-surface-2,#f5f5f7)}
.datefilter .df-pre button:focus-visible{outline:2px solid var(--uik-accent,#0096d6);outline-offset:-2px}
.datefilter .df-pre button.on{color:var(--uik-accent,#0096d6);font-weight:600}

.datefilter .df-main{display:flex;flex-direction:column;min-width:0}
.datefilter .df-cals{display:flex;gap:14px}
.datefilter .df-cal{width:238px}

/* 月份头 */
.datefilter .df-hd{display:flex;align-items:center;justify-content:space-between;
  height:28px;margin-bottom:6px}
.datefilter .df-hd b{font-size:13px;font-weight:600;font-variant-numeric:tabular-nums}
.datefilter .df-nav{display:flex;gap:2px}
.datefilter .df-nav button{
  width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;
  border:0;border-radius:7px;background:transparent;color:var(--uik-fg,#1d1d1f);cursor:pointer;
  transition:background .2s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.datefilter .df-nav button:hover:not(:disabled){background:var(--uik-surface-2,#f5f5f7)}
.datefilter .df-nav button:disabled{opacity:.3;cursor:default}
.datefilter .df-nav button:focus-visible{outline:2px solid var(--uik-accent,#0096d6);outline-offset:-2px}
.datefilter .df-nav svg{width:14px;height:14px;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}

/* 网格：7 列 × 恒定 6 行，切月时面板高度不跳 */
.datefilter .df-wk,.datefilter .df-grid{display:grid;grid-template-columns:repeat(7,34px)}
.datefilter .df-wk span{
  height:24px;display:flex;align-items:center;justify-content:center;
  font-size:11.5px;color:var(--uik-muted,#86868b);
}
.datefilter .df-grid{grid-template-rows:repeat(6,32px)}

.datefilter .df-d{
  position:relative;display:flex;align-items:center;justify-content:center;
  border:0;background:transparent;color:var(--uik-fg,#1d1d1f);
  font-family:inherit;font-size:12.5px;font-variant-numeric:tabular-nums;
  cursor:pointer;padding:0;
  transition:background .18s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)),
             color .18s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
/* 圆角只在段落两端：区间中段是直角，连起来才是一条完整的带子 */
.datefilter .df-d>i{
  position:relative;z-index:1;display:flex;align-items:center;justify-content:center;
  width:28px;height:28px;border-radius:9px;font-style:normal;
}
.datefilter .df-d:hover:not(:disabled)>i{background:var(--uik-surface-2,#f5f5f7)}
.datefilter .df-d:focus{outline:none}
.datefilter .df-d:focus-visible>i{outline:2px solid var(--uik-accent,#0096d6);outline-offset:-1px}
.datefilter .df-d.out>i{color:var(--uik-muted,#86868b);opacity:.55}
.datefilter .df-d:disabled{cursor:default}
.datefilter .df-d:disabled>i{opacity:.28}
.datefilter .df-d.today>i{box-shadow:inset 0 0 0 1.5px var(--uik-accent,#0096d6)}

/* 区间底色：铺满格子（含格子间距），首尾用半边圆角收口 */
.datefilter .df-d.in::before{
  content:"";position:absolute;inset:2px 0;z-index:0;
  background:var(--uik-surface-2,#f5f5f7);
}
.datefilter .df-d.start::before{border-radius:9px 0 0 9px;left:3px}
.datefilter .df-d.end::before{border-radius:0 9px 9px 0;right:3px}
.datefilter .df-d.start.end::before{border-radius:9px}
.datefilter .df-d.sel>i{
  background:linear-gradient(135deg,var(--uik-accent,#0096d6),var(--uik-accent-2,#46c0ee));
  color:var(--uik-on-accent,#fff);font-weight:600;box-shadow:none;
}

/* 底栏 */
.datefilter .df-foot{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  margin-top:8px;padding-top:8px;border-top:1px solid var(--uik-line,rgba(67,67,74,.3));
}
.datefilter .df-tip{color:var(--uik-muted,#86868b);font-size:12px;font-variant-numeric:tabular-nums}
.datefilter .df-foot button{
  border:1px solid var(--uik-line,rgba(67,67,74,.3));background:transparent;
  color:var(--uik-fg,#1d1d1f);border-radius:7px;padding:5px 11px;
  font-family:inherit;font-size:12px;cursor:pointer;
}
.datefilter .df-foot button:hover{border-color:var(--uik-muted,#86868b)}

@media (prefers-reduced-motion:reduce){
  .datefilter,.datefilter *{transition-duration:.1s!important}
}
/* 窄屏：区间的双月并排改成单列，避免面板比视口还宽 */
@media (max-width:560px){
  .datefilter{flex-direction:column}
  .datefilter .df-pre{flex-direction:row;flex-wrap:wrap;min-width:0;border-right:0;
    padding-right:0;margin-right:0;margin-bottom:8px;
    border-bottom:1px solid var(--uik-line,rgba(67,67,74,.3));padding-bottom:8px}
  .datefilter .df-cals{flex-direction:column}
}


/* ==== dock ==== */

/* ============================================================
 * Dock —— macOS 式放大坞：鼠标靠近时图标按距离连续放大，弹簧收敛，
 * 悬停/聚焦时冒出标签气泡。
 * 原件：React + framer-motion（useMotionValue/useSpring/useTransform）；
 * 此处为等效的原生实现：一个 rAF 弹簧积分器写 width/height，静止即停。
 * 依赖：无。令牌缺失时用 fallback 默认值。
 * 结构：
 *   <div class="dock">                     外层：预留放大空间，高度恒定
 *     <div class="dk-p" role="toolbar">    面板：底对齐
 *       <button class="dk-i">
 *         <span class="dk-ic">svg</span>
 *         <span class="dk-l">标签</span>
 *       </button>
 *     </div>
 *   </div>
 * 尺寸由 JS 逐帧写进 style.width/height，CSS 里不要再给 .dk-i 设过渡，
 * 否则会和弹簧打架（两套动画抢同一个属性）。
 * ============================================================ */
.dock{
  --dk-gap:12px;
  display:flex;align-items:flex-end;justify-content:center;
  width:100%;overflow-x:auto;overflow-y:visible;
  scrollbar-width:none;
}
.dock::-webkit-scrollbar{display:none}

.dock .dk-p{
  display:flex;align-items:flex-end;gap:var(--dk-gap);
  padding:8px 12px;border-radius:18px;
  border:1px solid var(--uik-line,rgba(67,67,74,.3));
  background:var(--uik-surface-2,#f5f5f7);
  box-shadow:0 8px 24px rgba(0,0,0,.10);
}

.dock .dk-i{
  position:relative;flex:0 0 auto;
  display:flex;align-items:center;justify-content:center;
  padding:0;border:0;border-radius:26%;
  background:var(--uik-surface,#fff);color:var(--uik-fg,#1d1d1f);
  box-shadow:0 1px 3px rgba(0,0,0,.12);
  cursor:pointer;text-decoration:none;
  /* width/height 由 JS 每帧写入，这里不设 transition */
}
.dock .dk-i:focus{outline:none}
.dock .dk-i:focus-visible{outline:2px solid var(--uik-accent,#0096d6);outline-offset:3px}
.dock .dk-i[aria-disabled="true"]{opacity:.4;pointer-events:none}

/* 图标用百分比，跟着格子一起放大，不用 JS 再算一遍 */
.dock .dk-ic{
  display:flex;align-items:center;justify-content:center;
  width:55%;height:55%;pointer-events:none;line-height:1;
}
.dock .dk-ic svg{width:100%;height:100%;fill:none;stroke:currentColor;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.dock .dk-ic img{width:100%;height:100%;object-fit:contain}
/* 纯文字/emoji 图标：字号跟着格子走 */
.dock .dk-ic.txt{font-size:calc(var(--dk-w,44px) * .42)}

/* 当前项：底部一个小点，dock 的老规矩 */
.dock .dk-i.on::after{
  content:"";position:absolute;left:50%;bottom:-6px;
  width:4px;height:4px;margin-left:-2px;border-radius:50%;
  background:var(--uik-accent,#0096d6);
}

/* 标签气泡 */
.dock .dk-l{
  position:absolute;left:50%;bottom:100%;z-index:2;
  margin-bottom:8px;padding:3px 8px;border-radius:7px;
  border:1px solid var(--uik-line,rgba(67,67,74,.3));
  background:var(--uik-surface,#fff);color:var(--uik-fg,#1d1d1f);
  font-size:12px;font-weight:500;line-height:1.4;white-space:pre;
  pointer-events:none;opacity:0;
  transform:translate(-50%,4px);
  transition:opacity .2s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)),
             transform .2s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.dock .dk-i:hover .dk-l,
.dock .dk-i:focus-visible .dk-l{opacity:1;transform:translate(-50%,0)}

/* 放大是纯动效，前庭敏感者关掉：JS 见 reduced 分支，这里收掉气泡位移 */
@media (prefers-reduced-motion:reduce){
  .dock .dk-l{transition-duration:.1s!important;transform:translate(-50%,0)}
}


/* ==== select-box ==== */

/* ============================================================
 * SelectBox —— 下拉框（持有值的那种，不是动作菜单）：胶囊触发器 + 选项列表，
 * 选中项打勾并高亮，选项多时自带搜索框。
 * 原件：React + lucide-react 的语言切换器（useState + 点外面关闭）；
 * 此处为等效的原生实现，无依赖，并补上键盘、搜索、原生 <select> 升级。
 * 结构：
 *   <button class="sb-btn" role="combobox">
 *     <span class="sb-ic">🇨🇳</span><span class="sb-v">简体中文</span><svg class="sb-chev"></svg>
 *   </button>
 *   <div class="selectbox" role="listbox">        面板挂在 <body> 下，position:fixed
 *     <div class="sb-search"><input></div>
 *     <div class="sb-list">
 *       <div class="sb-o sel" role="option"><span class="sb-ic">🇨🇳</span><span>简体中文</span><svg class="sb-ck"></svg></div>
 *     </div>
 *   </div>
 * 动作菜单（点了就执行、不留值）请用 DropMenu，别用这个。
 * ============================================================ */

/* ---------- 触发器 ---------- */
.sb-btn{
  display:inline-flex;align-items:center;gap:8px;max-width:100%;
  padding:7px 12px;border-radius:980px;
  border:1px solid var(--uik-line,rgba(67,67,74,.3));
  background:var(--uik-surface,#fff);color:var(--uik-fg,#1d1d1f);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  box-shadow:0 1px 2px rgba(0,0,0,.06);
  font-family:inherit;font-size:13px;line-height:1.3;cursor:pointer;
  transition:background .25s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)),
             border-color .25s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.sb-btn:hover:not(:disabled){background:var(--uik-surface-2,#f5f5f7)}
.sb-btn:focus-visible{outline:2px solid var(--uik-accent,#0096d6);outline-offset:2px}
.sb-btn:disabled{opacity:.45;cursor:default}
.sb-btn[aria-expanded="true"]{border-color:var(--uik-accent,#0096d6)}
.sb-btn .sb-v{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sb-btn .sb-v.empty{color:var(--uik-muted,#86868b)}
.sb-btn .sb-chev{
  width:15px;height:15px;flex:0 0 auto;margin-left:auto;
  fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
  transition:transform .3s var(--uik-ease-back,cubic-bezier(.34,1.56,.64,1));
}
.sb-btn[aria-expanded="true"] .sb-chev{transform:rotate(180deg)}

/* ---------- 面板 ---------- */
.selectbox{
  position:fixed;z-index:9999;left:0;top:0;
  display:flex;flex-direction:column;
  border-radius:12px;overflow:hidden;
  border:1px solid var(--uik-line,rgba(67,67,74,.3));
  background:var(--uik-surface,#fff);color:var(--uik-fg,#1d1d1f);
  -webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);
  box-shadow:0 12px 30px rgba(0,0,0,.15);
  font-family:inherit;font-size:13px;
  opacity:0;transform:translateY(-4px) scale(.98);pointer-events:none;
  transition:opacity .2s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1)),
             transform .2s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
.selectbox.up:not(.open){transform:translateY(4px) scale(.98)}
.selectbox.open{opacity:1;transform:none;pointer-events:auto}

.selectbox .sb-search{padding:7px;border-bottom:1px solid var(--uik-line,rgba(67,67,74,.3))}
.selectbox .sb-search input{
  width:100%;box-sizing:border-box;padding:6px 9px;
  border:1px solid var(--uik-line,rgba(67,67,74,.3));border-radius:8px;
  background:var(--uik-surface-2,#f5f5f7);color:var(--uik-fg,#1d1d1f);
  font-family:inherit;font-size:13px;
}
.selectbox .sb-search input:focus{outline:none;border-color:var(--uik-accent,#0096d6)}

/* 列表自己滚，面板不滚——搜索框才能钉在顶上 */
.selectbox .sb-list{overflow-y:auto;max-height:min(46vh,280px);padding:5px;scrollbar-width:thin}

.selectbox .sb-o{
  display:flex;align-items:center;gap:8px;width:100%;
  padding:8px 9px;border:0;border-radius:8px;background:transparent;
  color:var(--uik-fg,#1d1d1f);font-family:inherit;font-size:13px;line-height:1.35;
  text-align:left;cursor:pointer;
  transition:background .18s var(--uik-ease-flow,cubic-bezier(.23,1,.32,1));
}
/* .on = 键盘光标所在项；焦点留在触发器上，靠 aria-activedescendant 播报 */
.selectbox .sb-o.on{background:var(--uik-surface-2,#f5f5f7)}
.selectbox .sb-o:hover:not([aria-disabled="true"]){background:var(--uik-surface-2,#f5f5f7)}
.selectbox .sb-o.sel{color:var(--uik-accent,#0096d6);font-weight:600}
.selectbox .sb-o[aria-disabled="true"]{opacity:.4;cursor:default}
.selectbox .sb-o .sb-t{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.selectbox .sb-o .sb-ck{width:15px;height:15px;flex:0 0 auto;opacity:0;
  fill:none;stroke:var(--uik-accent,#0096d6);stroke-width:2.4;
  stroke-linecap:round;stroke-linejoin:round}
.selectbox .sb-o.sel .sb-ck{opacity:1}

.selectbox .sb-gp{
  padding:8px 9px 4px;font-size:11.5px;font-weight:600;
  color:var(--uik-muted,#86868b);
}
.selectbox .sb-empty{padding:14px 10px;text-align:center;color:var(--uik-muted,#86868b)}

.sb-ic{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:1.25em;height:1.25em;line-height:1}
.sb-ic svg{width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:2}

/* 被接管的原生 <select>：留在 DOM 里供表单提交，但不可见、不可聚焦 */
.sb-native{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

@media (prefers-reduced-motion:reduce){
  .selectbox,.selectbox *,.sb-btn,.sb-btn .sb-chev{transition-duration:.1s!important}
}


/* ==== flow-diagram ==== */

/* ============================================================
 * FlowDiagram —— 系统图 / 流程图：泳道 + 语义配色节点 + 正交连线。
 * 风格提炼自 archify（github.com/tt-a1i/archify，MIT）的图表模板：
 * 深板岩底 + 点状网格、等宽字、按语义分色的半透明节点、细线正交箭头。
 * 此处是等效的原生实现（archify 本体是 Node CLI，按 IR 生成整页 HTML）。
 * 依赖：无。
 *
 * 配色自成一套 --fd-*，不吃 --uik-accent：这套板岩色是它的识别度所在，
 * 换成品牌色就不是这个风格了。但主题跟随全库的 data-uik-theme 一起切。
 *
 * 结构（全部由 JS 生成）：
 *   <div class="flowdia">
 *     <svg viewBox="0 0 W H">
 *       <rect class="fd-bg"/>            底 + 网格
 *       <g class="fd-lanes">  <rect class="fd-lane"/> <text class="fd-lane-t"/>
 *       <g class="fd-edges">  <path class="fd-e a-default"/>
 *       <g class="fd-nodes">  <g class="fd-n" data-type="backend">
 *                               <rect class="fd-mask"/>   ← 不透明垫底
 *                               <rect class="fd-box"/>    ← 半透明填充
 *     <div class="fd-legend">
 * ============================================================ */
.flowdia{
  /* ---- 深色（默认，archify 的主态） ---- */
  --fd-bg:#020617;
  --fd-grid:#1e293b;
  --fd-text:#ffffff;
  --fd-muted:#94a3b8;
  --fd-dim:#475569;
  --fd-lane-fill:rgba(15,23,42,.22);
  --fd-lane-stroke:#334155;
  --fd-arrow:#64748b;
  --fd-arrow-emphasis:#34d399;
  /* 半透明节点底下的不透明垫层：不垫的话，从节点下面穿过的连线会透出来 */
  --fd-mask:#0f172a;

  --fd-frontend-fill:rgba(8,51,68,.4);      --fd-frontend-stroke:#22d3ee;
  --fd-backend-fill:rgba(6,78,59,.4);       --fd-backend-stroke:#34d399;
  --fd-database-fill:rgba(76,29,149,.4);    --fd-database-stroke:#a78bfa;
  --fd-cloud-fill:rgba(120,53,15,.3);       --fd-cloud-stroke:#fbbf24;
  --fd-security-fill:rgba(136,19,55,.4);    --fd-security-stroke:#fb7185;
  --fd-queue-fill:rgba(251,146,60,.3);      --fd-queue-stroke:#fb923c;
  --fd-external-fill:rgba(30,41,59,.5);     --fd-external-stroke:#94a3b8;

  --fd-font:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,
            'Noto Sans Mono CJK SC','Microsoft YaHei',monospace;

  position:relative;border-radius:14px;overflow:hidden;
  background:var(--fd-bg);color:var(--fd-text);font-family:var(--fd-font);
}

/* ---- 浅色：跟随全库主题开关 ---- */
.flowdia[data-fd-theme="light"],
:root[data-uik-theme="light"] .flowdia{
  --fd-bg:#f8fafc; --fd-grid:#e2e8f0; --fd-text:#0f172a;
  --fd-muted:#64748b; --fd-dim:#94a3b8;
  --fd-lane-fill:rgba(248,250,252,.65); --fd-lane-stroke:#cbd5e1;
  --fd-arrow:#94a3b8; --fd-arrow-emphasis:#059669; --fd-mask:#ffffff;
  --fd-frontend-fill:rgba(34,211,238,.15);  --fd-frontend-stroke:#0891b2;
  --fd-backend-fill:rgba(52,211,153,.18);   --fd-backend-stroke:#059669;
  --fd-database-fill:rgba(167,139,250,.2);  --fd-database-stroke:#7c3aed;
  --fd-cloud-fill:rgba(251,191,36,.18);     --fd-cloud-stroke:#d97706;
  --fd-security-fill:rgba(251,113,133,.15); --fd-security-stroke:#e11d48;
  --fd-queue-fill:rgba(251,146,60,.15);     --fd-queue-stroke:#ea580c;
  --fd-external-fill:rgba(148,163,184,.18); --fd-external-stroke:#64748b;
}
@media (prefers-color-scheme:light){
  :root:not([data-uik-theme="dark"]) .flowdia:not([data-fd-theme="dark"]){
    --fd-bg:#f8fafc; --fd-grid:#e2e8f0; --fd-text:#0f172a;
    --fd-muted:#64748b; --fd-dim:#94a3b8;
    --fd-lane-fill:rgba(248,250,252,.65); --fd-lane-stroke:#cbd5e1;
    --fd-arrow:#94a3b8; --fd-arrow-emphasis:#059669; --fd-mask:#ffffff;
    --fd-frontend-fill:rgba(34,211,238,.15);  --fd-frontend-stroke:#0891b2;
    --fd-backend-fill:rgba(52,211,153,.18);   --fd-backend-stroke:#059669;
    --fd-database-fill:rgba(167,139,250,.2);  --fd-database-stroke:#7c3aed;
    --fd-cloud-fill:rgba(251,191,36,.18);     --fd-cloud-stroke:#d97706;
    --fd-security-fill:rgba(251,113,133,.15); --fd-security-stroke:#e11d48;
    --fd-queue-fill:rgba(251,146,60,.15);     --fd-queue-stroke:#ea580c;
    --fd-external-fill:rgba(148,163,184,.18); --fd-external-stroke:#64748b;
  }
}

.flowdia svg{display:block;width:100%;height:auto}

/* ---------- 底与网格 ---------- */
.flowdia .fd-bg{fill:var(--fd-bg)}
.flowdia .fd-grid-l{stroke:var(--fd-grid);fill:none;stroke-width:.5;
  stroke-dasharray:1 4;opacity:.48}

/* ---------- 泳道 ---------- */
.flowdia .fd-lane{fill:var(--fd-lane-fill);stroke:var(--fd-lane-stroke);
  stroke-width:1;stroke-dasharray:7 3}
.flowdia .fd-lane-t{fill:var(--fd-dim);font-size:11px;letter-spacing:.08em}

/* ---------- 节点 ---------- */
.flowdia .fd-mask{fill:var(--fd-mask);stroke:none}
.flowdia .fd-box{stroke-width:1.5;
  transition:filter .2s ease,opacity .2s ease}
.flowdia .fd-n{cursor:default}
.flowdia .fd-n[data-href],.flowdia .fd-n[data-click]{cursor:pointer}
.flowdia .fd-l{fill:var(--fd-text);font-size:12.5px;font-weight:600}
.flowdia .fd-s{fill:var(--fd-muted);font-size:10.5px}
.flowdia .fd-tag{fill:var(--fd-dim);font-size:9.5px;letter-spacing:.06em}

/* 每类把自己的色写进 color：这样 currentColor 就是「本节点的颜色」，
   辉光/聚焦发光才会跟着节点走。不设的话 currentColor 是文字色，全都发白光。 */
.flowdia .fd-n[data-type="frontend"]{color:var(--fd-frontend-stroke)}
.flowdia .fd-n[data-type="backend"] {color:var(--fd-backend-stroke)}
.flowdia .fd-n[data-type="database"]{color:var(--fd-database-stroke)}
.flowdia .fd-n[data-type="cloud"]   {color:var(--fd-cloud-stroke)}
.flowdia .fd-n[data-type="security"]{color:var(--fd-security-stroke)}
.flowdia .fd-n[data-type="queue"]   {color:var(--fd-queue-stroke)}
.flowdia .fd-n[data-type="external"]{color:var(--fd-external-stroke)}

.flowdia .fd-n[data-type="frontend"] .fd-box{fill:var(--fd-frontend-fill);stroke:var(--fd-frontend-stroke)}
.flowdia .fd-n[data-type="backend"]  .fd-box{fill:var(--fd-backend-fill); stroke:var(--fd-backend-stroke)}
.flowdia .fd-n[data-type="database"] .fd-box{fill:var(--fd-database-fill);stroke:var(--fd-database-stroke)}
.flowdia .fd-n[data-type="cloud"]    .fd-box{fill:var(--fd-cloud-fill);   stroke:var(--fd-cloud-stroke)}
.flowdia .fd-n[data-type="security"] .fd-box{fill:var(--fd-security-fill);stroke:var(--fd-security-stroke)}
.flowdia .fd-n[data-type="queue"]    .fd-box{fill:var(--fd-queue-fill);   stroke:var(--fd-queue-stroke)}
.flowdia .fd-n[data-type="external"] .fd-box{fill:var(--fd-external-fill);stroke:var(--fd-external-stroke)}

/* ---------- 连线 ---------- */
.flowdia .fd-e{fill:none;stroke:var(--fd-arrow);stroke-width:1.4;
  transition:opacity .2s ease}
.flowdia .fd-e.emphasis{stroke:var(--fd-arrow-emphasis);stroke-width:1.8}
.flowdia .fd-e.dashed{stroke:var(--fd-database-stroke);stroke-dasharray:4 4}
.flowdia .fd-e.security{stroke:var(--fd-security-stroke);stroke-dasharray:5 5}
.flowdia .fd-e-l{fill:var(--fd-muted);font-size:10px;
  paint-order:stroke;stroke:var(--fd-bg);stroke-width:3px;stroke-linejoin:round}

/* 悬停聚焦：与当前节点无关的一律压暗，图一复杂就靠这个读 */
.flowdia[data-focus] .fd-n:not(.hit),
.flowdia[data-focus] .fd-e:not(.hit),
.flowdia[data-focus] .fd-e-l:not(.hit){opacity:.16}
.flowdia .fd-n.hit .fd-box{filter:drop-shadow(0 0 6px currentColor)}

/* ---------- 图例 ---------- */
.flowdia .fd-legend{
  display:flex;flex-wrap:wrap;gap:6px 14px;
  padding:10px 14px;border-top:1px solid var(--fd-lane-stroke);
  font-size:11px;color:var(--fd-muted);
}
.flowdia .fd-legend span{display:inline-flex;align-items:center;gap:6px}
.flowdia .fd-legend i{width:10px;height:10px;border-radius:3px;
  border:1.5px solid currentColor;display:inline-block}
.flowdia .fd-legend .frontend{color:var(--fd-frontend-stroke)}
.flowdia .fd-legend .backend{color:var(--fd-backend-stroke)}
.flowdia .fd-legend .database{color:var(--fd-database-stroke)}
.flowdia .fd-legend .cloud{color:var(--fd-cloud-stroke)}
.flowdia .fd-legend .security{color:var(--fd-security-stroke)}
.flowdia .fd-legend .queue{color:var(--fd-queue-stroke)}
.flowdia .fd-legend .external{color:var(--fd-external-stroke)}

@media (prefers-reduced-motion:reduce){
  .flowdia .fd-box,.flowdia .fd-e{transition-duration:.1s!important}
}

/* ============================================================
 * 换皮：preset
 * 默认 slate 来自 archify；下面四套提炼自 fireworks-tech-graph
 *（github.com/yizhiyanhua-ai/fireworks-tech-graph，MIT）的风格指南 1/2/3/4。
 * 全部只改令牌与少量几何，走线与交互一个字不用动。
 * rx 用的是 CSS 几何属性（SVG2），老浏览器不认就退回标签上的 rx=6，不会坏。
 * ============================================================ */

/* ---- flat：白底、浅灰描边、语义四色、无衬线（style-1 Flat Icon） ---- */
.flowdia[data-fd-preset="flat"]{
  --fd-bg:#ffffff; --fd-grid:transparent; --fd-mask:#ffffff;
  --fd-text:#111827; --fd-muted:#374151; --fd-dim:#6b7280;
  --fd-lane-fill:#f9fafb; --fd-lane-stroke:#e5e7eb;
  --fd-arrow:#6b7280; --fd-arrow-emphasis:#2563eb;
  --fd-frontend-fill:rgba(37,99,235,.08);   --fd-frontend-stroke:#2563eb;
  --fd-backend-fill:rgba(22,163,74,.08);    --fd-backend-stroke:#16a34a;
  --fd-database-fill:rgba(147,51,234,.08);  --fd-database-stroke:#9333ea;
  --fd-cloud-fill:rgba(8,145,178,.08);      --fd-cloud-stroke:#0891b2;
  --fd-security-fill:rgba(220,38,38,.08);   --fd-security-stroke:#dc2626;
  --fd-queue-fill:rgba(234,88,12,.08);      --fd-queue-stroke:#ea580c;
  --fd-external-fill:#ffffff;               --fd-external-stroke:#d1d5db;
  --fd-font:'Helvetica Neue',Helvetica,Arial,'PingFang SC','Microsoft YaHei',sans-serif;
}
.flowdia[data-fd-preset="flat"] .fd-box,
.flowdia[data-fd-preset="flat"] .fd-mask{rx:8px}
.flowdia[data-fd-preset="flat"] .fd-lane{stroke-dasharray:none}

/* ---- terminal：近黑渐变底、等宽、霓虹描边 + 辉光（style-2 Dark Terminal） ---- */
.flowdia[data-fd-preset="terminal"]{
  --fd-bg:transparent;                       /* 底交给容器的渐变 */
  --fd-grid:transparent; --fd-mask:#0f172a;
  --fd-text:#e2e8f0; --fd-muted:#94a3b8; --fd-dim:#475569;
  --fd-lane-fill:rgba(15,23,42,.55); --fd-lane-stroke:#334155;
  --fd-arrow:#475569; --fd-arrow-emphasis:#10b981;
  --fd-frontend-fill:#0f172a;  --fd-frontend-stroke:#3b82f6;
  --fd-backend-fill:#0f172a;   --fd-backend-stroke:#f97316;
  --fd-database-fill:#0f172a;  --fd-database-stroke:#10b981;
  --fd-cloud-fill:#0f172a;     --fd-cloud-stroke:#3b82f6;
  --fd-security-fill:#0f172a;  --fd-security-stroke:#a855f7;
  --fd-queue-fill:#0f172a;     --fd-queue-stroke:#f97316;
  --fd-external-fill:#0f172a;  --fd-external-stroke:#475569;
  --fd-font:'SF Mono','Fira Code','Cascadia Code','Courier New','Microsoft YaHei',monospace;
  background:linear-gradient(160deg,#0f0f1a 0%,#1a1a2e 100%);
}
.flowdia[data-fd-preset="terminal"] .fd-l,
.flowdia[data-fd-preset="terminal"] .fd-s{letter-spacing:.02em}
/* 原作用的是 feGaussianBlur 辉光，这里用等价的 drop-shadow，省一层滤镜节点 */
.flowdia[data-fd-preset="terminal"] .fd-box{filter:drop-shadow(0 0 3px currentColor)}

/* ---- blueprint：深蓝工程底 + 30px 网格 + 青色角标（style-3 Blueprint） ---- */
.flowdia[data-fd-preset="blueprint"]{
  --fd-bg:#0a1628; --fd-grid:#112240; --fd-mask:#0a1628;
  --fd-text:#caf0f8; --fd-muted:#90e0ef; --fd-dim:#48cae4;
  --fd-lane-fill:rgba(13,31,60,.5); --fd-lane-stroke:#00b4d8;
  --fd-arrow:#00b4d8; --fd-arrow-emphasis:#06d6a0;
  --fd-frontend-fill:#0d1f3c;  --fd-frontend-stroke:#00b4d8;
  --fd-backend-fill:#0d1f3c;   --fd-backend-stroke:#06d6a0;
  --fd-database-fill:#0d1f3c;  --fd-database-stroke:#48cae4;
  --fd-cloud-fill:#0d1f3c;     --fd-cloud-stroke:#90e0ef;
  --fd-security-fill:#0d1f3c;  --fd-security-stroke:#f77f00;
  --fd-queue-fill:#0d1f3c;     --fd-queue-stroke:#f77f00;
  --fd-external-fill:#0d1f3c;  --fd-external-stroke:#48cae4;
  --fd-font:'Courier New','Lucida Console','Microsoft YaHei',monospace;
}
.flowdia[data-fd-preset="blueprint"] .fd-grid-l{stroke-dasharray:none;opacity:.6}
.flowdia[data-fd-preset="blueprint"] .fd-box,
.flowdia[data-fd-preset="blueprint"] .fd-mask{rx:2px}
.flowdia[data-fd-preset="blueprint"] .fd-lane{stroke-dasharray:6 3}
.flowdia[data-fd-preset="blueprint"] .fd-lane-t{text-transform:uppercase;letter-spacing:.05em}
/* 工程图的角标：只留四角，主描边收掉 */
.flowdia[data-fd-preset="blueprint"] .fd-box{stroke-opacity:.35}
.flowdia .fd-bracket{fill:none;stroke-width:1.5;display:none}
.flowdia[data-fd-preset="blueprint"] .fd-bracket{display:inline}
.flowdia[data-fd-preset="blueprint"] .fd-n[data-type="frontend"] .fd-bracket{stroke:var(--fd-frontend-stroke)}
.flowdia[data-fd-preset="blueprint"] .fd-n[data-type="backend"]  .fd-bracket{stroke:var(--fd-backend-stroke)}
.flowdia[data-fd-preset="blueprint"] .fd-n[data-type="database"] .fd-bracket{stroke:var(--fd-database-stroke)}
.flowdia[data-fd-preset="blueprint"] .fd-n[data-type="cloud"]    .fd-bracket{stroke:var(--fd-cloud-stroke)}
.flowdia[data-fd-preset="blueprint"] .fd-n[data-type="security"] .fd-bracket{stroke:var(--fd-security-stroke)}
.flowdia[data-fd-preset="blueprint"] .fd-n[data-type="queue"]    .fd-bracket{stroke:var(--fd-queue-stroke)}
.flowdia[data-fd-preset="blueprint"] .fd-n[data-type="external"] .fd-bracket{stroke:var(--fd-external-stroke)}

/* ---- notion：白底、中性盒、单一蓝箭头、无阴影（style-4 Notion Clean） ---- */
.flowdia[data-fd-preset="notion"]{
  --fd-bg:#ffffff; --fd-grid:transparent; --fd-mask:#ffffff;
  --fd-text:#111827; --fd-muted:#374151; --fd-dim:#6b7280;
  --fd-lane-fill:#ffffff; --fd-lane-stroke:#e5e7eb;
  --fd-arrow:#3b82f6; --fd-arrow-emphasis:#3b82f6;
  --fd-database-stroke:#3b82f6; --fd-security-stroke:#3b82f6;   /* 虚线也统一成蓝 */
  --fd-font:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,
            'PingFang SC','Microsoft YaHei',sans-serif;
}
/* 「不用装饰色、只用几何形」是这套风格的明文规矩：所有盒子一律中性。
   选择器要压过按 type 分色那条（那条带属性选择器，权重 4），所以这里也带上 [data-type]。 */
.flowdia[data-fd-preset="notion"] .fd-n[data-type] .fd-box{fill:#f9fafb;stroke:#e5e7eb;stroke-width:1}
.flowdia[data-fd-preset="notion"] .fd-n[data-type]{color:#3b82f6}
.flowdia[data-fd-preset="notion"] .fd-box,
.flowdia[data-fd-preset="notion"] .fd-mask{rx:4px}
.flowdia[data-fd-preset="notion"] .fd-lane{stroke-dasharray:none}
.flowdia[data-fd-preset="notion"] .fd-e{stroke:#3b82f6}
.flowdia[data-fd-preset="notion"] .fd-n.hit .fd-box{filter:none;stroke:#3b82f6;stroke-width:1.5}
/* 语义色被这套风格刻意去掉了，留着图例只会是一排一模一样的色块 */
.flowdia[data-fd-preset="notion"] .fd-legend{display:none}
