/* ============================================================
   c0lor-MAG — iOS 26 "Liquid Glass" design system
   Ported from the v3 prototype (app3.css), adapted to:
   - drive the --accent family from the app's resolved primaryColor
     (App.tsx sets --accent + --accent-rgb inline; category teal/orange/blue
     when the colourful theme is on, mono primaryColor when off)
   - coexist with the custom Electron title bar (native window-control overlay)
   - layer the translucent glass over an animated colour-mesh backdrop
   - keep backward-compatible token aliases (--blue / --text* / --surface / --bg)
     so the already-ported screens resolve without churn
   ============================================================ */
:root {
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
    "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", system-ui, sans-serif;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;

  --r-xl: 28px; --r-lg: 22px; --r-md: 15px; --r-sm: 11px; --pill: 999px;
  /* Keep in sync with INTEGRATED_TITLE_BAR_HEIGHT in shared/electron-api.ts. */
  --side-w: 250px; --tb-h: 46px;

  /* motion */
  --spring: cubic-bezier(.34, 1.46, .54, 1);
  --smooth: cubic-bezier(.32, .72, 0, 1);
  --ease: cubic-bezier(.32, .72, 0, 1); /* legacy alias */

  --green: #34C759; --red: #FF453A; --amber: #FF9F0A;
}

/* The accent family is derived from a single --accent (+ --accent-rgb) which
   App.tsx sets inline on <html>; --accent-2 follows automatically. Defaults
   below keep the design intact before JS runs / in tests (category M = teal). */
[data-theme="light"] {
  --accent: #0FB5AE; --accent-rgb: 15, 181, 174;
  --accent-2: color-mix(in srgb, var(--accent) 78%, #fff);
  --on-accent: #fff; /* App.tsx flips to dark ink for near-white accents */

  --ink: #15151A; --ink-2: rgba(40,40,55,.66); --ink-3: rgba(40,40,55,.4); --ink-4: rgba(40,40,55,.2);
  --base: #E9E9EF;
  --glass: rgba(255,255,255,.58);
  --glass-2: rgba(255,255,255,.42);
  --glass-strong: rgba(255,255,255,.78);
  --glass-edge: rgba(255,255,255,.9);
  --glass-border: rgba(255,255,255,.55);
  --hair: rgba(40,40,55,.10);
  --sep: rgba(40,40,55,.12);
  --fill: rgba(120,120,135,.12);
  --fill-2: rgba(120,120,135,.07);
  --fill-press: rgba(120,120,135,.2);
  --track: rgba(120,120,135,.14);
  --field: rgba(255,255,255,.55);
  --thumb: #fff;
  --tile: #fff;
  --mesh-op: .4;

  --sh-card: 0 1px 1px rgba(20,20,30,.04), 0 10px 30px rgba(20,20,30,.10);
  --sh-pop: 0 8px 24px rgba(20,20,30,.10), 0 24px 60px rgba(20,20,30,.18);
  --sh-accent: 0 6px 20px rgba(var(--accent-rgb), .40);
  --sh-sm: 0 1px 2px rgba(17,17,22,.06);

  /* status soft fills */
  --green-soft: rgba(52,199,89,.16); --amber-soft: rgba(255,159,10,.16); --red-soft: rgba(255,69,58,.14);
  --purple: #8b5cf6; --purple-soft: rgba(120,90,255,.14);

  /* legacy aliases */
  --blue: var(--accent); --blue-2: var(--accent-2);
  --blue-soft: rgba(var(--accent-rgb), .12); --blue-line: rgba(var(--accent-rgb), .22);
  --sh-blue: var(--sh-accent); --sh-md: var(--sh-card); --sh-lg: var(--sh-pop);
  --text: var(--ink); --text-2: var(--ink-2); --text-3: var(--ink-3); --text-4: var(--ink-4);
  --bg: var(--base); --surface: var(--glass); --surface-2: var(--glass-2);
  --side-bg: var(--glass); --tb-bg: var(--glass-2); --inset: var(--field);
  --nav-hover: var(--fill);
}
[data-theme="dark"] {
  --accent: #0FB5AE; --accent-rgb: 15, 181, 174;
  --accent-2: color-mix(in srgb, var(--accent) 72%, #fff);
  --on-accent: #fff; /* App.tsx flips to dark ink for near-white accents */

  --ink: #FFFFFF; --ink-2: rgba(235,235,245,.62); --ink-3: rgba(235,235,245,.34); --ink-4: rgba(235,235,245,.18);
  --base: #07070B;
  --glass: rgba(44,44,52,.52);
  --glass-2: rgba(34,34,42,.46);
  --glass-strong: rgba(54,54,64,.66);
  --glass-edge: rgba(255,255,255,.18);
  --glass-border: rgba(255,255,255,.10);
  --hair: rgba(255,255,255,.07);
  --sep: rgba(255,255,255,.09);
  --fill: rgba(140,140,160,.16);
  --fill-2: rgba(140,140,160,.10);
  --fill-press: rgba(140,140,160,.28);
  --track: rgba(140,140,160,.16);
  --field: rgba(255,255,255,.06);
  --thumb: #f2f2f5;
  --tile: #fff;
  --mesh-op: .8;

  --sh-card: 0 1px 1px rgba(0,0,0,.3), 0 14px 40px rgba(0,0,0,.5);
  --sh-pop: 0 20px 60px rgba(0,0,0,.6);
  --sh-accent: 0 6px 22px rgba(var(--accent-rgb), .5);
  --sh-sm: 0 1px 2px rgba(0,0,0,.45);

  --green-soft: rgba(52,199,89,.18); --amber-soft: rgba(255,159,10,.2); --red-soft: rgba(255,69,58,.18);
  --purple: #a78bfa; --purple-soft: rgba(120,90,255,.2);

  /* legacy aliases */
  --blue: var(--accent); --blue-2: var(--accent-2);
  --blue-soft: rgba(var(--accent-rgb), .2); --blue-line: rgba(var(--accent-rgb), .34);
  --sh-blue: var(--sh-accent); --sh-md: var(--sh-card); --sh-lg: var(--sh-pop);
  --text: var(--ink); --text-2: var(--ink-2); --text-3: var(--ink-3); --text-4: var(--ink-4);
  --bg: var(--base); --surface: var(--glass); --surface-2: var(--glass-2);
  --side-bg: var(--glass); --tb-bg: var(--glass-2); --inset: var(--field);
  --nav-hover: var(--fill);
}

/* ---- base (scoped so Ant pages are undisturbed) ---- */
.mag-root, .mag-root * { box-sizing: border-box; }
html[data-theme="light"], html[data-theme="light"] body { background: #E9E9EF; }
html[data-theme="dark"], html[data-theme="dark"] body { background: #07070B; }
html { transition: background .5s var(--smooth); }
body.mag-body {
  font-family: var(--font); background: var(--base); color: var(--ink);
  -webkit-font-smoothing: antialiased; overflow: hidden; letter-spacing: -.012em;
  transition: color .5s var(--smooth);
}
.mag-root button { font-family: inherit; cursor: pointer; color: inherit; border: none; background: none; }
.mag-root input { font-family: inherit; }
.tnum { font-variant-numeric: tabular-nums; }
.mag-root ::selection { background: rgba(var(--accent-rgb), .25); }
.c0lor-window-no-drag { -webkit-app-region: no-drag; app-region: no-drag; }
.scroll::-webkit-scrollbar { width: 10px; height: 10px; }
.scroll::-webkit-scrollbar-thumb { background: var(--ink-4); border-radius: 99px; border: 3px solid transparent; background-clip: content-box; }
.scroll::-webkit-scrollbar-thumb:hover { background: var(--ink-3); background-clip: content-box; }

/* ===================== BACKDROP MESH ===================== */
.mesh { position: absolute; inset: -10%; z-index: 0; pointer-events: none; opacity: var(--mesh-op);
  filter: blur(70px) saturate(125%); transition: opacity .6s ease; }
.mesh .b { position: absolute; border-radius: 50%; animation: drift 26s var(--smooth) infinite alternate; }
.b1 { width: 46vw; height: 46vw; left: -6vw; top: -8vw; background: radial-gradient(circle, rgba(var(--accent-rgb),.9), transparent 66%); }
.b2 { width: 40vw; height: 40vw; right: -4vw; top: 6vh; background: radial-gradient(circle, rgba(120,90,255,.55), transparent 64%); animation-delay: -8s; }
.b3 { width: 42vw; height: 42vw; left: 24vw; bottom: -16vw; background: radial-gradient(circle, rgba(255,120,170,.5), transparent 64%); animation-delay: -15s; }
.b4 { width: 32vw; height: 32vw; right: 16vw; bottom: -10vw; background: radial-gradient(circle, rgba(60,200,255,.5), transparent 64%); animation-delay: -20s; }
@keyframes drift { 0%{transform:translate(0,0) scale(1)} 50%{transform:translate(4vw,3vh) scale(1.12)} 100%{transform:translate(-3vw,5vh) scale(.94)} }
[data-theme="light"] .b1 { background: radial-gradient(circle, rgba(var(--accent-rgb),.5), transparent 66%); }
[data-theme="light"] .b2 { background: radial-gradient(circle, rgba(120,90,255,.3), transparent 64%); }
[data-theme="light"] .b3 { background: radial-gradient(circle, rgba(255,120,170,.28), transparent 64%); }
[data-theme="light"] .b4 { background: radial-gradient(circle, rgba(60,200,255,.3), transparent 64%); }

/* ===================== GLASS ===================== */
.glass {
  background: var(--glass);
  backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border);
  box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge);
}

/* ===================== WINDOW / TITLEBAR ===================== */
/* Anchored to the viewport (position:fixed, NOT height:100%): the host app root
   is typically a plain React fragment with no full-height containing block, so
   height:100% here would collapse to content height — breaking vertical scroll
   and leaving blank space below short pages. */
.win { position: fixed; inset: 0; display: flex; flex-direction: column; background: var(--base);
  overflow: hidden; isolation: isolate; z-index: 0; }
.tb { height: var(--tb-h); flex: 0 0 auto; display: flex; align-items: center; gap: 12px; padding: 0 10px 0 16px;
  background: var(--glass-2); backdrop-filter: blur(30px) saturate(180%); -webkit-backdrop-filter: blur(30px) saturate(180%);
  border-bottom: 1px solid var(--hair); position: relative; z-index: 40;
  -webkit-app-region: drag; app-region: drag; user-select: none; }
/* reserve room for the native window-control overlay (min/max/close) */
.tb.tb--win32, .tb.tb--linux { padding-right: 148px; }
.tb.tb--darwin { padding-left: 84px; }
.tb button, .tb .tb-right, .tb .tb-right * { -webkit-app-region: no-drag; app-region: no-drag; }
.tb-icn { width: 34px; height: 34px; border-radius: 11px; display: grid; place-items: center; color: var(--ink-2);
  transition: background .2s, color .2s, transform .15s var(--spring); }
.tb-icn:hover { background: var(--fill); color: var(--ink); }
.tb-icn:active { transform: scale(.9); }
.tb-icn.on { background: rgba(var(--accent-rgb), .14); color: var(--accent); }
.tb-mid { position: absolute; left: 50%; top: 0; height: 100%; transform: translateX(-50%); display: flex; align-items: center; gap: 9px; font-size: 13px; color: var(--ink-3); pointer-events: none; }
.tb-mid .crumb { color: var(--ink); font-weight: 600; }
.tb-mid .cat-badge { width: 21px; height: 21px; display: inline-flex; align-items: center; justify-content: center; border-radius: 7px; font-size: 11px; font-weight: 800; line-height: 1; }
.tb-right { margin-left: auto; display: flex; align-items: center; gap: 5px; }
.tb-online { display: inline-flex; align-items: center; gap: 7px; height: 30px; padding: 0 13px; border-radius: 99px; font-size: 12.5px; font-weight: 650; white-space: nowrap;
  background: rgba(52,199,89,.16); color: var(--green); border: 1px solid rgba(52,199,89,.25); }
.tb-online.off { background: var(--red-soft); color: var(--red); border-color: rgba(255,69,58,.25); }
.dot { width: 7px; height: 7px; border-radius: 99px; background: currentColor; animation: pulse 2.6s var(--smooth) infinite; }
@keyframes pulse { 0%{box-shadow:0 0 0 0 rgba(52,199,89,.5)} 70%{box-shadow:0 0 0 6px rgba(52,199,89,0)} 100%{box-shadow:0 0 0 0 rgba(52,199,89,0)} }

.body { flex: 1; display: flex; min-height: 0; position: relative; z-index: 1; }

/* ===================== SIDEBAR (floating glass) ===================== */
.side { width: var(--side-w); flex: 0 0 auto; margin: 12px 0 12px 12px; border-radius: var(--r-xl);
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
  background: var(--glass); backdrop-filter: blur(40px) saturate(180%); -webkit-backdrop-filter: blur(40px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge);
  transition: width .32s var(--smooth); }
.side.collapsed { width: 64px; }  /* keep in sync with SIDER_COLLAPSED_WIDTH (inline width wins) */
/* Collapse is a single continuous "one-shot": every element stays mounted and
   animates its width/height/opacity in lockstep with the `.side` width transition
   above — never conditionally rendered (DOM add/remove snaps the layout). */
.brand { display: flex; align-items: center; gap: 0; padding: 18px 20px 14px; overflow: hidden; transition: padding .32s var(--smooth); }
.side.collapsed .brand { padding-left: 15px; padding-right: 15px; }
.bmark { width: 34px; height: 34px; border-radius: 10px; flex: 0 0 auto; display: block; object-fit: contain;
  filter: drop-shadow(0 2px 6px rgba(0,0,0,.18)); transition: opacity .3s; }
/* .bname collapse (max-width/opacity/margin) is driven inline in AppSidebar so it
   shares the `opacity` channel with the welcome-intro handoff without fighting it. */
.bname { display: inline-block; font-size: 17px; font-weight: 750; letter-spacing: -.03em; white-space: nowrap; overflow: hidden; vertical-align: middle; }
.bname b { color: var(--accent); font-weight: 750; }

/* M/A/G segmented control. The buttons and thumb stay in a single absolute
   coordinate system, then morph between horizontal and vertical positions. */
.mag-shell { --mag-active: 0; position: relative; margin: 2px 16px 16px; height: 68px;
  transition: margin .32s var(--smooth), height .32s var(--smooth); }
.mag-shell.vmag { margin: 2px 10px 14px; height: 107px; }
.mag { position: absolute; inset: 0; background: var(--track); border-radius: 13px; overflow: hidden; }
.mag > .thumb { position: absolute; z-index: 0; border-radius: 10px;
  background: var(--glass-strong); box-shadow: 0 2px 8px rgba(0,0,0,.14), inset 0 1px 0 var(--glass-edge);
  left: 4px; top: 4px; width: calc((100% - 8px) / 3); height: calc(100% - 8px);
  transition: left .32s var(--smooth), width .32s var(--smooth), height .32s var(--smooth), transform .38s var(--spring);
  transform: translate3d(calc(var(--mag-active) * 100%), 0, 0); }
.mag-shell.vmag .mag > .thumb { left: calc(50% - 17px); width: 34px; height: 31px;
  transform: translate3d(0, calc(var(--mag-active) * 34px), 0); }
[data-theme="dark"] .mag > .thumb { background: rgba(var(--accent-rgb),.9); box-shadow: var(--sh-accent); }
.mag button { --mag-index: 0; position: absolute; left: 4px; top: 4px; z-index: 1; min-width: 0;
  width: calc((100% - 8px) / 3); height: calc(100% - 8px); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px; padding: 7px 0; color: var(--ink-2); font-weight: 650;
  transform: translate3d(calc(var(--mag-index) * 100%), 0, 0);
  transition: left .32s var(--smooth), width .32s var(--smooth), height .32s var(--smooth), padding .32s var(--smooth), transform .32s var(--smooth), color .3s; }
.mag-shell.vmag .mag button { left: calc(50% - 17px); width: 34px; height: 31px; padding: 6px 0;
  transform: translate3d(0, calc(var(--mag-index) * 34px), 0); }
.mag button .l { position: relative; z-index: 1; font-size: 14px; font-weight: 800; }
.mag button .s { position: relative; z-index: 1; font-size: 9.5px; opacity: .7; max-height: 14px; overflow: hidden;
  transition: max-height .28s var(--smooth), opacity .26s var(--smooth); }
.side.collapsed .mag button .s { max-height: 0; opacity: 0; }
.mag button.on { color: var(--ink); }
[data-theme="dark"] .mag button.on { color: var(--on-accent); }
[data-theme="light"] .mag button.on { color: var(--accent); }

.scap { font-size: 11px; font-weight: 700; letter-spacing: .09em; color: var(--ink-3); padding: 2px 22px 8px; white-space: nowrap; overflow: hidden;
  max-height: 22px; opacity: 1; transition: max-height .3s var(--smooth), opacity .26s var(--smooth), padding .3s var(--smooth); }
.side.collapsed .scap { max-height: 0; opacity: 0; padding-top: 0; padding-bottom: 0; }
.nav { position: relative; padding: 0 12px; display: flex; flex-direction: column; gap: 3px; overflow-y: auto; overflow-x: hidden; flex: 1; }
.nav-ind { position: absolute; left: 12px; right: 12px; border-radius: 13px; z-index: 0; pointer-events: none;
  background: rgba(var(--accent-rgb),.16); border: 1px solid rgba(var(--accent-rgb),.22);
  box-shadow: inset 0 1px 0 var(--glass-edge); transition: transform .44s var(--spring), height .3s var(--spring), opacity .3s; }
.nitem { position: relative; z-index: 1; display: flex; align-items: center; gap: 13px; padding: 10px 13px; border-radius: 13px; text-align: left; width: 100%; color: var(--ink);
  transition: background .2s, transform .14s var(--spring), gap .3s var(--smooth), padding .3s var(--smooth); }
.nitem:hover:not(.on) { background: var(--fill); }
.nitem:active { transform: scale(.97); }
/* Centre the 30px icon in the collapsed nav track via the already-transitioning
   padding — never `justify-content`, which can't animate and would snap the icon to
   the (still-wide) centre for one frame on toggle. Track = 64 (rail) − 2 (.side
   border) − 24 (.nav padding) = 38px, so (38−30)/2 = 4px exactly fills the box. */
.side.collapsed .nitem { padding: 10px 4px; gap: 0; }
.nico { width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; flex: 0 0 auto; color: var(--ink-2); transition: color .25s, background .25s, transform .3s var(--spring); }
.nitem.on .nico { color: var(--on-accent); background: var(--accent); box-shadow: var(--sh-accent); transform: scale(1.04); }
.nlbl { font-size: 14.5px; font-weight: 550; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 170px; opacity: 1; transition: color .25s, max-width .3s var(--smooth), opacity .26s var(--smooth); }
.side.collapsed .nlbl { max-width: 0; opacity: 0; }
.nitem.on .nlbl { font-weight: 680; color: var(--accent); }
.nbadge { margin-left: auto; font-size: 11px; font-weight: 700; color: var(--ink-3); background: var(--fill); height: 19px; min-width: 19px; padding: 0 5px; border-radius: 99px; display: grid; place-items: center; flex: 0 0 auto; }
.nitem.on .nbadge { color: var(--on-accent); background: var(--accent); }

.side-foot { padding: 12px; display: flex; flex-direction: column; gap: 9px; transition: gap .3s var(--smooth); }
.side.collapsed .side-foot { gap: 0; }
.conncard, .conn { border-radius: var(--r-md); padding: 11px 13px; display: flex; align-items: center; gap: 11px;
  background: var(--glass-2); border: 1px solid var(--glass-border); box-shadow: inset 0 1px 0 var(--glass-edge); }
.conncard { max-height: 80px; opacity: 1; overflow: hidden;
  transition: max-height .32s var(--smooth), opacity .26s var(--smooth), padding .32s var(--smooth), margin .32s var(--smooth), border-width .32s var(--smooth); }
.side.collapsed .conncard { max-height: 0; opacity: 0; padding-top: 0; padding-bottom: 0; border-top-width: 0; border-bottom-width: 0; }
.conncard .cdot { width: 9px; height: 9px; border-radius: 99px; background: var(--green); flex: 0 0 auto; box-shadow: 0 0 0 3px rgba(52,199,89,.18); }
.conncard .cdot.off { background: var(--ink-3); box-shadow: none; }
.conncard .ctxt { min-width: 0; }
.conncard .cname { font-size: 12.5px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.conncard .cstat { font-size: 11px; color: var(--ink-3); }
.collapse { width: 100%; height: 34px; border-radius: 11px; color: var(--ink-3); font-size: 12px; font-weight: 600; display: flex; align-items: center; justify-content: center; gap: 7px; white-space: nowrap; transition: background .18s, gap .3s var(--smooth); }
.collapse:hover { background: var(--fill); color: var(--ink-2); }
.collapse-lbl { display: inline-block; max-width: 80px; opacity: 1; overflow: hidden; transition: max-width .3s var(--smooth), opacity .26s var(--smooth); }
.side.collapsed .collapse { gap: 0; }
.side.collapsed .collapse-lbl { max-width: 0; opacity: 0; }

/* ===================== CONTENT ===================== */
.content { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.cscroll { flex: 1; min-height: 0; overflow-y: auto; }
.cinner { padding: 26px 36px 64px; max-width: 1520px; }

.phead { display: flex; align-items: flex-end; gap: 20px; margin-bottom: 26px; }
.ptitles { flex: 1; min-width: 0; }
.ptitle { font-size: 33px; font-weight: 800; letter-spacing: -.035em; line-height: 1; }
.psub { font-size: 14.5px; color: var(--ink-2); margin-top: 10px; }
.phead .actions { display: flex; gap: 10px; align-items: center; }

/* entrance — transform-only (content always visible even if paused/hidden).
   NOTE: `animation: ...both` locks `transform` after it finishes, which would
   override any `:hover { transform }`. Do NOT apply `.stagger` to elements that
   need a hover-lift (.dcard/.rcard/.patch) — page entrance is driven by the
   framer-motion wrapper in AppLayout instead, keeping card hover-lift intact. */
.page { animation: slideUp .5s var(--spring) both; }
.stagger > * { animation: slideUp .55s var(--spring) both; animation-delay: calc(var(--i, 0) * 55ms); }
@keyframes slideUp { from { transform: translateY(13px); } to { transform: none; } }

/* ===================== BUTTONS ===================== */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; border-radius: var(--pill);
  font-size: 14.5px; font-weight: 650; padding: 0 18px; height: 42px; white-space: nowrap; position: relative; overflow: hidden;
  transition: transform .14s var(--spring), background .2s, box-shadow .2s, color .2s, opacity .2s; }
.btn:active { transform: scale(.94); }
.btn.lg { height: 50px; font-size: 16px; padding: 0 26px; }
.btn.xl { height: 56px; font-size: 17px; padding: 0 32px; }
.btn.sm { height: 34px; font-size: 13px; padding: 0 14px; }
.btn.block { width: 100%; }
.btn.pri { background: var(--accent); color: var(--on-accent); box-shadow: var(--sh-accent); }
.btn.pri::before { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(255,255,255,.28), transparent 55%); pointer-events: none; }
.btn.pri:hover { filter: brightness(1.06); transform: translateY(-1px); }
.btn.pri:active { transform: scale(.94); }
.btn.pri:disabled { background: var(--fill); color: var(--ink-3); box-shadow: none; cursor: not-allowed; }
.btn.pri:disabled::before { display: none; }
.btn.glass, .btn.sec { background: var(--glass-strong); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); color: var(--ink); border: 1px solid var(--glass-border); box-shadow: inset 0 1px 0 var(--glass-edge); }
.btn.glass:hover, .btn.sec:hover { background: var(--fill-press); }
/* tint/danger carry the same glass depth as .btn.glass (border + inset top edge
   + backdrop blur) so they don't read as flat color pills on glass cards —
   only the tint hue says "accent action" / "destructive". */
.btn.tint { background: linear-gradient(180deg, rgba(var(--accent-rgb),.19), rgba(var(--accent-rgb),.12));
  color: var(--accent); border: 1px solid rgba(var(--accent-rgb),.30);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  box-shadow: inset 0 1px 0 var(--glass-edge); }
.btn.tint:hover { background: linear-gradient(180deg, rgba(var(--accent-rgb),.27), rgba(var(--accent-rgb),.18)); }
.btn.ghost { color: var(--accent); }
.btn.ghost:hover { background: rgba(var(--accent-rgb),.12); }
.btn.danger { background: linear-gradient(180deg, rgba(255,69,58,.19), rgba(255,69,58,.12));
  color: var(--red); border: 1px solid rgba(255,69,58,.30);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  box-shadow: inset 0 1px 0 var(--glass-edge); }
.btn.danger:hover { background: linear-gradient(180deg, rgba(255,69,58,.27), rgba(255,69,58,.18)); }
.btn:disabled { color: var(--ink-3); cursor: not-allowed; opacity: .85; }
.btn.glass:disabled, .btn.sec:disabled, .btn.tint:disabled, .btn.ghost:disabled, .btn.danger:disabled { background: var(--fill); box-shadow: none; border-color: var(--glass-border); }
.btn.icon { width: 42px; padding: 0; }
.btn.icon.lg { width: 50px; }
.btn.icon.sm { width: 34px; }

/* ===================== CARD / SECTION ===================== */
.card { border-radius: var(--r-lg); background: var(--glass); backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge); }
.card.flat { box-shadow: inset 0 1px 0 var(--glass-edge); }
.pad { padding: 22px; }
.shead { display: flex; align-items: center; gap: 11px; margin: 0 0 14px 2px; }
.sicn { width: 30px; height: 30px; border-radius: 10px; display: grid; place-items: center; flex: 0 0 auto; background: rgba(var(--accent-rgb),.15); color: var(--accent); }
.sicn.blue { background: rgba(var(--accent-rgb),.15); color: var(--accent); }
.sicn.alt, .sicn.purple { background: rgba(120,90,255,.16); color: var(--purple); }
.sicn.green { background: var(--green-soft); color: var(--green); }
.sicn.amber { background: var(--amber-soft); color: var(--amber); }
.stitle { font-size: 17px; font-weight: 720; letter-spacing: -.02em; }
.shead .right { margin-left: auto; display: flex; gap: 8px; align-items: center; }

/* ===================== INSET LIST (iOS settings) ===================== */
.list { border-radius: var(--r-lg); overflow: hidden; background: var(--glass); backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge); }
.lrow { display: flex; align-items: center; gap: 14px; padding: 0 18px; min-height: 56px; position: relative; transition: background .16s; }
.lrow.tall { min-height: 64px; }
.lrow + .lrow::before { content: ""; position: absolute; left: 18px; right: 0; top: 0; height: 1px; background: var(--sep); }
.lrow.tap, .lrow.btnrow { cursor: pointer; }
.lrow.tap:hover, .lrow.btnrow:hover { background: var(--fill); }
.lrow.tap:active, .lrow.btnrow:active { background: var(--fill-press); }
.lrow .ll { font-size: 15px; font-weight: 500; flex: 0 0 auto; display: flex; flex-direction: column; }
.lrow .lsub { font-size: 12px; color: var(--ink-3); margin-top: 2px; }
/* Inline hint on the SAME line as the label (replaces the old two-line sub on
   settings rows — keep it short). */
.lhint { font-size: 12px; font-weight: 500; color: var(--ink-3); margin-left: 10px; }
/* Single-line rows (no .lsub second line): tighter vertical rhythm. */
.list.dense .lrow { min-height: 48px; }
.list.dense .lrow.tall { min-height: 56px; }
.lrow .lr { margin-left: auto; display: flex; align-items: center; gap: 9px; color: var(--ink-2); font-size: 15px; min-width: 0; }
.lrow .lr .val { color: var(--ink); font-weight: 550; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lrow .lr .ph { color: var(--ink-3); }
.chev { color: var(--ink-4); flex: 0 0 auto; display: inline-flex; transition: transform .2s var(--spring); }
.lrow.tap:hover .chev, .lrow.btnrow:hover .chev { transform: translateX(2px); }
.flabel { font-size: 12.5px; font-weight: 650; color: var(--ink-2); margin-bottom: 8px; display: block; }

/* ===================== CONTROLS ===================== */
.seg { position: relative; display: inline-flex; padding: 4px; background: var(--track); border-radius: 12px; }
.seg .thumb { position: absolute; top: 4px; bottom: 4px; left: 0; border-radius: 9px; background: var(--glass-strong); box-shadow: 0 2px 7px rgba(0,0,0,.12), inset 0 1px 0 var(--glass-edge); transition: transform .4s var(--spring), width .4s var(--spring); }
[data-theme="dark"] .seg .thumb { background: rgba(var(--accent-rgb),.85); }
.seg button { position: relative; z-index: 1; color: var(--ink-2); font-size: 13px; font-weight: 650; padding: 7px 15px; border-radius: 9px; transition: color .3s; white-space: nowrap; }
.seg button.on { color: var(--ink); }
[data-theme="dark"] .seg button.on { color: var(--on-accent); }
.seg.lg button { padding: 10px 20px; font-size: 14px; }
.seg.sm { padding: 3px; border-radius: 10px; }
.seg.sm .thumb { border-radius: 8px; }
.seg.sm button { padding: 5px 12px; font-size: 12.5px; border-radius: 8px; }
/* full-width "top bar" mode toggle: equal-width options, no empty gap */
.seg.block { display: flex; width: 100%; }
.seg.block button { flex: 1; }
.seg.disabled { opacity: .55; pointer-events: none; }

/* Off-state track must read as a toggle: a semi-opaque fill + inner ring + inset
   depth (and a light glass blur), since the bare --track is near-invisible on
   the glass surfaces. On-state fills with the category accent. */
.sw { width: 52px; height: 31px; border-radius: 99px; background: var(--track); padding: 0; position: relative; flex: 0 0 auto;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  box-shadow: inset 0 0 0 1px var(--glass-border), inset 0 1px 2px rgba(0,0,0,.14);
  transition: background .35s var(--spring), box-shadow .35s; }
[data-theme="dark"] .sw { background: rgba(235,235,245,.13); box-shadow: inset 0 0 0 1px rgba(255,255,255,.22), inset 0 1px 2px rgba(0,0,0,.32); }
[data-theme="light"] .sw { background: rgba(40,40,55,.14); box-shadow: inset 0 0 0 1px rgba(40,40,55,.20), inset 0 1px 2px rgba(0,0,0,.07); }
.sw::after { content: ""; position: absolute; top: 2.5px; left: 2.5px; width: 26px; height: 26px; border-radius: 50%; background: #fff; box-shadow: 0 2px 6px rgba(0,0,0,.3); transition: transform .35s var(--spring), width .2s var(--spring); }
.sw.on, .sw.on.blue { background: var(--accent); box-shadow: inset 0 0 0 1px rgba(0,0,0,.10), inset 0 1px 0 rgba(255,255,255,.3); }
.sw.on::after { transform: translateX(21px); }
.sw:disabled { opacity: .5; cursor: default; }
.sw:active::after { width: 30px; }
.sw.on:active::after { transform: translateX(17px); }
.swwrap { display: inline-flex; align-items: center; gap: 11px; }
.swside { font-size: 13px; font-weight: 600; color: var(--ink-3); transition: color .3s; }
.swside.on { color: var(--ink); }

.input { width: 100%; height: 44px; border-radius: var(--r-md); background: var(--field); border: 1px solid var(--glass-border); color: var(--ink); font-size: 15px; padding: 0 14px; transition: border-color .2s, box-shadow .2s; backdrop-filter: blur(8px); }
.input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 4px rgba(var(--accent-rgb),.16); }
.input::placeholder { color: var(--ink-3); }
/* In light mode the near-white glass border vanishes on pale surfaces; give
   form fields a faint dark hairline so they read as inputs. */
[data-theme="light"] .input { border-color: var(--sep); }
.f2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

.chips { display: flex; flex-wrap: wrap; gap: 7px; }
.chip { display: inline-flex; align-items: center; gap: 5px; height: 32px; padding: 0 9px 0 12px; border-radius: 10px; background: var(--fill); color: var(--ink); font-size: 13px; font-weight: 600; transition: transform .14s var(--spring), background .16s; }
.chip:active { transform: scale(.92); }
.chip .x { width: 16px; height: 16px; border-radius: 99px; display: grid; place-items: center; color: var(--ink-3); }
.chip:hover .x { background: var(--fill-press); color: var(--ink); }
.chip.add { border: 1px dashed var(--ink-4); background: transparent; color: var(--ink-2); }
.chip.add:hover { border-color: var(--accent); color: var(--accent); }
.chip.on { background: var(--accent); color: #fff; }

.cbx { display: inline-flex; align-items: center; gap: 10px; font-size: 15px; font-weight: 500; color: var(--ink); transition: transform .14s var(--spring); }
.cbx:active { transform: scale(.96); }
.cbx .box { width: 24px; height: 24px; border-radius: 8px; display: grid; place-items: center; background: var(--fill); border: 1px solid var(--glass-border); color: #fff; transition: background .2s, border-color .2s, transform .25s var(--spring); }
.cbx.on .box { background: var(--accent); border-color: var(--accent); transform: scale(1.05); }

/* ===================== DEVICE CARDS ===================== */
.dgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(310px, 1fr)); gap: 16px; }
.dcard { border-radius: var(--r-lg); padding: 18px; display: flex; flex-direction: column; gap: 15px;
  background: var(--glass); backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge);
  transition: transform .3s var(--spring), box-shadow .3s; }
.dcard:hover { transform: translateY(-3px); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge); }
.dtop { display: flex; align-items: center; gap: 14px; }
.dphoto, .dtile { width: 64px; height: 64px; border-radius: 16px; background: var(--tile); display: grid; place-items: center; overflow: hidden; flex: 0 0 auto; box-shadow: 0 4px 14px rgba(0,0,0,.12); }
.dphoto img, .dtile img { width: 100%; height: 100%; object-fit: contain; }
.dtile.purple { background: rgba(120,90,255,.14); color: var(--purple); box-shadow: none; }
.dtile.muted { background: var(--fill); color: var(--ink-3); box-shadow: none; }
.dname { font-size: 16.5px; font-weight: 720; letter-spacing: -.02em; }
.dmeta { font-size: 12.5px; color: var(--ink-3); margin-top: 3px; }
.spill { display: inline-flex; align-items: center; gap: 6px; height: 25px; padding: 0 11px; border-radius: 99px; font-size: 12px; font-weight: 700; margin-left: auto; flex: 0 0 auto; }
.spill.ok { background: var(--green-soft); color: var(--green); }
.spill.idle { background: var(--amber-soft); color: var(--amber); }
.spill.busy { background: rgba(var(--accent-rgb),.16); color: var(--accent); }
.dstats { display: flex; gap: 20px; flex-wrap: wrap; padding: 13px 2px; border-top: 1px solid var(--sep); border-bottom: 1px solid var(--sep); }
.dstat .k { font-size: 11px; color: var(--ink-3); font-weight: 650; }
.dstat .v { font-size: 14px; font-weight: 650; margin-top: 3px; }
.dactions { display: flex; gap: 8px; }
/* `.mag-root button` (124) resets border+background; outspecify it so the glass
   actually renders on the <button>. Liquid-glass card: a rounded frosted pane
   stacked on the base mesh — same treatment as .dcard, so the empty add-tile is
   unified with the device cards and the morph grows from a real glass square. */
.mag-root .addcard { border-radius: var(--r-lg); border: 1px solid var(--glass-border);
  background: var(--glass); backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; min-height: 200px; color: var(--ink-2); padding: 24px; cursor: pointer;
  transition: border-color .25s, box-shadow .3s, transform .3s var(--spring); }
/* :not(.hover-muted): the cursor necessarily rests on the tile when the morph
   opens (the opening click) and usually when it closes (the panel covered it) —
   an instant hover lift there would anchor the morph 3px high / make the card
   jump at the swap. MorphAddCard mutes hover from open until the pointer moves
   again; transition:none makes the strip take effect within the same frame so
   the origin rect is measured at resting geometry. */
.addcard.hover-muted { transition: none; }
.addcard:hover:not(.hover-muted) { border-color: var(--accent); color: var(--accent); transform: translateY(-3px); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge); }
.addcard .pl { width: 50px; height: 50px; border-radius: 50%; background: var(--fill); color: var(--ink-2); display: grid; place-items: center; transition: .25s var(--spring); }
.addcard:hover:not(.hover-muted) .pl { background: var(--accent); color: #fff; box-shadow: var(--sh-accent); transform: scale(1.08) rotate(90deg); }
.addcard .at { font-size: 15px; font-weight: 650; color: var(--ink); }
.addcard:hover:not(.hover-muted) .at { color: var(--accent); }
.addcard .as { font-size: 12.5px; color: var(--ink-3); }

/* ===================== READOUTS / RESULTS ===================== */
.split { display: grid; grid-template-columns: 372px 1fr; gap: 24px; align-items: start; }
.split > * { min-width: 0; }
.stack { display: flex; flex-direction: column; gap: 22px; }
/* Gamut: diagram + metrics side-by-side so indicators are visible without
   scrolling past the (square) horseshoe. Collapses to a single column when the
   data area gets too narrow for a useful split. */
.gamut-data { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(320px, .95fr); gap: 18px; align-items: stretch; }
.gamut-data > * { min-width: 0; }
@media (max-width: 1280px) { .gamut-data { grid-template-columns: 1fr; } }
/* EOTF "全部" overview: all charts at once in a 2-up grid (compact height). */
.eotf-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.eotf-grid > * { min-width: 0; }
@media (max-width: 1280px) { .eotf-grid { grid-template-columns: 1fr; } }
.rgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
.rcard { border-radius: var(--r-lg); padding: 18px 20px; background: var(--glass); backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge); transition: transform .3s var(--spring); }
.rcard:hover { transform: translateY(-2px); }
.rcard .rk { font-size: 12.5px; color: var(--ink-2); font-weight: 650; display: flex; align-items: center; gap: 7px; white-space: nowrap; }
.rcard .rk .ki { color: var(--ink-3); }
.rcard .rv { font-size: 30px; font-weight: 780; letter-spacing: -.035em; margin-top: 10px; line-height: 1; }
.rcard .rv .u { font-size: 15px; font-weight: 650; color: var(--ink-3); margin-left: 5px; letter-spacing: 0; }
.rcard .rd { font-size: 12px; color: var(--accent); font-weight: 650; margin-top: 8px; }
.rcard.hero { grid-column: span 2; color: var(--on-accent); border: none; position: relative; overflow: hidden;
  background: linear-gradient(135deg, var(--accent), var(--accent-2)); box-shadow: var(--sh-accent); }
.rcard.hero::before { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(255,255,255,.25), transparent 50%); }
.rcard.hero .rk, .rcard.hero .rk .ki, .rcard.hero .rv .u, .rcard.hero .rd { color: color-mix(in srgb, var(--on-accent) 88%, transparent); }

.chartcard { border-radius: var(--r-lg); padding: 20px 22px; background: var(--glass); backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge); }
.chart-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.chart-title { font-size: 15px; font-weight: 700; }
.legend { margin-left: auto; display: flex; gap: 14px; font-size: 12px; color: var(--ink-2); }
.legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 5px; vertical-align: -1px; }

.derow { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
.destat { border-radius: var(--r-md); padding: 14px 16px; background: var(--glass); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); border: 1px solid var(--glass-border); box-shadow: inset 0 1px 0 var(--glass-edge); }
.destat .k { font-size: 12px; color: var(--ink-2); font-weight: 650; }
.destat .v { font-size: 25px; font-weight: 780; letter-spacing: -.03em; margin-top: 6px; }
.destat .v small { font-size: 13px; color: var(--ink-3); font-weight: 650; }
.patchgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 10px; }
/* Calman-style patch strip: fixed 实测/目标 axis + a horizontal scrolling row of
   columns, each split Actual (top) / Target (bottom) with label + ΔE below. */
.cpatch-wrap { display: flex; gap: 12px; align-items: flex-start; }
.cpatch-axis { display: flex; flex-direction: column; flex: 0 0 auto; padding-top: 0; }
.cpatch-axis span { height: 52px; display: flex; align-items: center; font-size: 11px; font-weight: 650; color: var(--ink-3); white-space: nowrap; }
.cpatch-scroll { flex: 1; min-width: 0; overflow-x: auto; padding-bottom: 8px; }
.cpatch-row { display: flex; gap: 4px; width: max-content; }
.cpatch { display: flex; flex-direction: column; align-items: center; flex: 0 0 auto; width: 42px; }
.cp-sw { width: 100%; height: 52px; }
.cp-sw.cp-actual { border-radius: 9px 9px 0 0; box-shadow: inset 0 0 0 1px var(--glass-border); }
.cp-sw.cp-target { border-radius: 0 0 9px 9px; box-shadow: inset 0 0 0 1px var(--glass-border); }
.cp-name { font-size: 10.5px; font-weight: 600; color: var(--ink-2); margin-top: 6px; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-de { font-size: 11px; font-weight: 750; margin-top: 2px; }
.cp-de.good { color: var(--green); } .cp-de.warn { color: var(--amber); } .cp-de.bad { color: var(--red); }

/* Chromaticity diagram floated legend pill: ΔE stats (left) + 目标/测量 key
   (right), sitting in the empty bottom-right corner inside the plot. */
.cie-legend { position: absolute; right: 12px; bottom: 54px; display: flex; align-items: center; gap: 11px; pointer-events: none;
  padding: 6px 12px; border-radius: 12px; font-size: 11.5px; color: var(--ink-2);
  background: var(--glass-strong); backdrop-filter: blur(14px) saturate(180%); -webkit-backdrop-filter: blur(14px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge); }
.cie-legend .cie-stat { font-weight: 760; color: var(--ink); font-variant-numeric: tabular-nums; }
.cie-legend .cie-stat b { color: var(--ink-3); font-weight: 600; margin-right: 5px; }
.cie-legend .cie-sep { width: 1px; height: 15px; background: var(--sep); }
.cie-legend .cie-key { display: inline-flex; align-items: center; gap: 5px; font-weight: 600; color: var(--ink-2); }
.cie-legend .cie-key .sq { width: 9px; height: 9px; border: 1.6px solid var(--ink-2); border-radius: 2px; }
.cie-legend .cie-key .dot { width: 9px; height: 9px; border-radius: 99px; background: var(--ink-2); }
.patch { border-radius: var(--r-sm); overflow: hidden; background: var(--glass-2); border: 1px solid var(--glass-border); box-shadow: inset 0 1px 0 var(--glass-edge); transition: transform .25s var(--spring); }
.patch:hover { transform: translateY(-2px) scale(1.02); }
.patch .sw2 { height: 48px; }
.patch .pb { padding: 8px 11px; display: flex; align-items: center; justify-content: space-between; }
.patch .pn { font-size: 11.5px; color: var(--ink-2); font-weight: 650; }
.patch .pe { font-size: 12.5px; font-weight: 750; }
.patch .pe.good { color: var(--green); } .patch .pe.warn { color: var(--amber); } .patch .pe.bad { color: var(--red); }

.vtabs { position: relative; display: inline-flex; padding: 4px; background: var(--track); border-radius: 14px; }
.vtabs .thumb { position: absolute; top: 4px; bottom: 4px; left: 0; border-radius: 10px; background: var(--glass-strong); box-shadow: 0 2px 8px rgba(0,0,0,.12), inset 0 1px 0 var(--glass-edge); transition: transform .4s var(--spring), width .4s var(--spring); }
[data-theme="dark"] .vtabs .thumb { background: rgba(var(--accent-rgb),.85); }
.vtabs button { position: relative; z-index: 1; color: var(--ink-2); font-size: 14px; font-weight: 650; padding: 9px 20px; border-radius: 10px; transition: color .3s; }
.vtabs button.on { color: var(--ink); }
[data-theme="dark"] .vtabs button.on { color: var(--on-accent); }

.dpanel { border-radius: var(--r-lg); overflow: hidden; background: var(--glass); backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge); }
.dptool { display: flex; align-items: center; gap: 12px; padding: 14px 18px; border-bottom: 1px solid var(--sep); }

.empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; color: var(--ink-3); text-align: center; padding: 50px; }
.empty .ei { width: 84px; height: 84px; border-radius: 26px; display: grid; place-items: center; background: var(--fill); color: var(--ink-3); }
.empty .et { font-size: 15px; color: var(--ink-2); font-weight: 500; }

.soon { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; height: 60vh; color: var(--ink-3); text-align: center; }
.soon .ei { width: 96px; height: 96px; border-radius: 30px; display: grid; place-items: center; background: var(--glass); border: 1px solid var(--glass-border); box-shadow: var(--sh-card); color: var(--accent); }
.soon .big { font-size: 23px; font-weight: 760; color: var(--ink-2); }

/* ===================== GLASS TABLE ===================== */
.gtable { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.gtable th { text-align: left; font-size: 12px; font-weight: 700; color: var(--ink-3); padding: 0 14px 11px; white-space: nowrap; letter-spacing: -.01em; }
.gtable th.num, .gtable td.num { text-align: right; font-variant-numeric: tabular-nums; }
.gtable th.sortable { cursor: pointer; user-select: none; }
.gtable th.sortable:hover { color: var(--ink-2); }
.gtable th.sortable:focus-visible { outline: 2px solid rgba(var(--accent-rgb), .55); outline-offset: -3px; border-radius: 8px; color: var(--ink); }
.gtable th .sortcaret { opacity: .6; margin-left: 4px; font-size: 10px; }
.gtable td { padding: 11px 14px; border-top: 1px solid var(--sep); color: var(--ink); font-weight: 500; white-space: nowrap; }
.gtable tr, .gtable tbody tr { transition: background .15s; }
.gtable tbody tr:hover { background: var(--fill); }
.gtable tr.hl td { background: rgba(var(--accent-rgb),.10); }
.gtable tr.hl td:first-child { color: var(--accent); font-weight: 700; box-shadow: inset 3px 0 0 var(--accent); }
.gtable .lead { font-weight: 700; }
.tag { display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 10px; border-radius: 8px; font-size: 12px; font-weight: 650; }
.tag.ok { background: var(--green-soft); color: var(--green); }
.tag.warn { background: var(--amber-soft); color: var(--amber); }
.tag.bad { background: var(--red-soft); color: var(--red); }
.tbl-wrap { overflow-x: auto; }
.swatch-cell { width: 22px; height: 22px; border-radius: 6px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); display: inline-block; vertical-align: middle; }

/* metric strip */
.metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; padding: 4px 2px; }
.metric .k { font-size: 12.5px; color: var(--ink-2); font-weight: 600; }
.metric .v { font-size: 27px; font-weight: 780; letter-spacing: -.03em; margin-top: 6px; line-height: 1; }
.metric .v .u { font-size: 14px; font-weight: 650; color: var(--ink-3); margin-left: 4px; letter-spacing: 0; }
.delta-chips { display: flex; gap: 9px; flex-wrap: wrap; }
.dchip { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 12px; border-radius: 9px; font-size: 12.5px; font-weight: 650; background: var(--fill); }
.dchip i { width: 8px; height: 8px; border-radius: 99px; }

/* chart legend row */
.clegend { display: flex; flex-wrap: wrap; gap: 16px; justify-content: center; margin-top: 6px; font-size: 12.5px; color: var(--ink-2); font-weight: 550; }
.clegend span { display: inline-flex; align-items: center; gap: 7px; }
.clegend i { width: 16px; height: 3px; border-radius: 2px; }
.clegend i.dash { background: none !important; border-top: 2px dashed currentColor; height: 0; }

/* upload dropzone */
.drop { border: 1.6px dashed var(--glass-border); border-radius: var(--r-md); background: var(--glass-2); padding: 26px; display: flex; flex-direction: column; align-items: center; gap: 10px; color: var(--ink-2); text-align: center; transition: border-color .2s, background .2s; cursor: pointer; }
.drop:hover { border-color: var(--accent); color: var(--accent); }
.drop .di { width: 44px; height: 44px; border-radius: 13px; background: var(--fill); display: grid; place-items: center; color: var(--ink-3); transition: .2s; }
.drop:hover .di { background: var(--accent); color: #fff; }
.drop.over { border-color: var(--accent); color: var(--accent); background: rgba(var(--accent-rgb),.07); }
.drop.over .di { background: var(--accent); color: #fff; }
.drop.disabled { opacity: .5; pointer-events: none; }
.filedrop-btn { display: inline-flex; }
.filedrop-btn.disabled { pointer-events: none; }
.fileitem { display: flex; align-items: center; gap: 11px; padding: 11px 14px; border-radius: var(--r-sm); background: var(--fill); margin-top: 10px; }
.fileitem .ftag { font-size: 10.5px; font-weight: 750; color: var(--accent); background: rgba(var(--accent-rgb),.14); padding: 2px 7px; border-radius: 6px; }
.fileitem .fname { font-size: 13.5px; font-weight: 600; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* pattern preview / generate */
.preview-stage { aspect-ratio: 16 / 9; border-radius: var(--r-md); display: grid; place-items: center; overflow: hidden; box-shadow: inset 0 0 0 1px var(--glass-border); }
.preview-fg { box-shadow: 0 0 0 1px rgba(255,255,255,.15); }
.rgb3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.swatch-btn { width: 30px; height: 30px; border-radius: 8px; box-shadow: inset 0 0 0 1px var(--glass-border); flex: 0 0 auto; }
.row-between { display: flex; align-items: center; justify-content: space-between; }
.slider { -webkit-appearance: none; appearance: none; width: 100%; height: 6px; border-radius: 99px; background: var(--track); outline: none; }
.slider::-webkit-slider-thumb { -webkit-appearance: none; width: 22px; height: 22px; border-radius: 50%; background: #fff; box-shadow: 0 2px 6px rgba(0,0,0,.3), 0 0 0 1px rgba(0,0,0,.04); cursor: pointer; transition: transform .15s var(--spring); }
.slider::-webkit-slider-thumb:active { transform: scale(1.15); }

/* ===================== GLASS SELECT (anchored dropdown) ===================== */
.gsel { position: relative; width: 100%; }
.gsel-trigger { display: flex; align-items: center; gap: 8px; width: 100%; height: 44px; padding: 0 12px 0 14px;
  border-radius: var(--r-md); background: var(--field); border: 1px solid var(--glass-border); color: var(--ink);
  font-size: 15px; text-align: left; backdrop-filter: blur(8px);
  transition: border-color .2s, box-shadow .2s; }
[data-theme="light"] .gsel-trigger { border-color: var(--sep); }
.gsel-trigger:hover:not(:disabled) { border-color: var(--ink-4); }
.gsel-trigger.open { outline: none; border-color: var(--accent); box-shadow: 0 0 0 4px rgba(var(--accent-rgb),.16); }
.gsel-trigger:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 4px rgba(var(--accent-rgb),.16); }
.gsel-trigger:disabled { opacity: .5; cursor: default; }
.gsel-trigger.sm { height: 36px; font-size: 13.5px; padding: 0 10px 0 12px; }
.gsel-lead { flex: 0 0 auto; display: grid; place-items: center; color: var(--ink-3); }
.gsel-val { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gsel-val.ph { color: var(--ink-3); }
.gsel-clear { flex: 0 0 auto; width: 20px; height: 20px; border-radius: 99px; display: grid; place-items: center;
  color: var(--ink-3); transition: background .14s, color .14s; }
.gsel-clear:hover { background: var(--fill-press); color: var(--ink); }
.gsel-chev { flex: 0 0 auto; display: grid; place-items: center; color: var(--ink-3); transition: transform .2s var(--smooth); }
.gsel-trigger.open .gsel-chev { transform: rotate(180deg); }

.gsel-pop { position: fixed; z-index: 1100; display: flex; flex-direction: column; padding: 6px;
  border-radius: var(--r-md); background: var(--glass-strong);
  backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge); overflow: hidden; }
.gsel-search { height: 38px; margin: 2px 2px 6px; flex: 0 0 auto; }
.gsel-list { overflow-y: auto; min-height: 0; display: flex; flex-direction: column; gap: 2px; }
.gsel-group { padding: 8px 12px 4px; font-size: 11.5px; font-weight: 700; letter-spacing: .04em;
  color: var(--ink-3); text-transform: uppercase; }
.gsel-opt { display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%; padding: 9px 12px;
  border-radius: var(--r-sm); background: transparent; border: 1px solid transparent; color: var(--ink-2);
  font-size: 14px; text-align: left; transition: background .12s, color .12s; }
.gsel-opt[data-active="1"]:not(:disabled) { background: var(--fill); color: var(--ink); }
.gsel-opt.on { color: var(--accent); font-weight: 650; }
.gsel-opt:disabled { opacity: .45; cursor: default; }
.gsel-opt-l { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gsel-empty { padding: 18px 12px; text-align: center; color: var(--ink-3); font-size: 13px; }

/* ===================== GLASS SLIDER (marks + value bubble) ===================== */
.gsld { display: flex; flex-direction: column; }
.gsld.disabled { opacity: .5; pointer-events: none; }
.gsld-track { position: relative; padding: 8px 0; }
.gsld-bubble { position: absolute; top: -16px; padding: 2px 7px; border-radius: 7px; pointer-events: none;
  background: var(--glass-strong); border: 1px solid var(--glass-border); box-shadow: var(--sh-pop);
  font-size: 11.5px; font-weight: 650; color: var(--ink); white-space: nowrap;
  opacity: 0; transition: opacity .15s; }
.gsld:hover .gsld-bubble, .gsld:focus-within .gsld-bubble { opacity: 1; }
.gsld-marks { position: relative; height: 15px; }
.gsld-mark { position: absolute; top: 0; font-size: 11px; color: var(--ink-3); white-space: nowrap; }

/* ===================== FIELD GROUP (input + static addon) ===================== */
.field-grp { display: flex; align-items: stretch; min-width: 0; }
.field-grp > .input { border-radius: 0; min-width: 0; flex: 1; }
.field-grp > .input:first-child { border-radius: var(--r-md) 0 0 var(--r-md); }
.field-grp > .input:last-child { border-radius: 0 var(--r-md) var(--r-md) 0; }
.field-grp > .addon { display: grid; place-items: center; padding: 0 12px; flex: 0 0 auto;
  font-size: 13px; font-weight: 600; color: var(--ink-3); background: var(--fill-2);
  border: 1px solid var(--glass-border); white-space: nowrap; }
[data-theme="light"] .field-grp > .addon { border-color: var(--sep); }
.field-grp > .addon:first-child { border-radius: var(--r-md) 0 0 var(--r-md); border-right: 0; }
.field-grp > .addon:last-child { border-radius: 0 var(--r-md) var(--r-md) 0; border-left: 0; }
.numfield-input { font-variant-numeric: tabular-nums; }
.numfield-input:disabled { opacity: .5; cursor: default; }
.numfield-input.numfield-sm { height: 36px; font-size: 13.5px; padding: 0 10px; }
.field-grp.numfield-sm > .addon { padding: 0 10px; font-size: 12.5px; }

/* ===================== GLASS POPCONFIRM ===================== */
.pc-anchor { display: inline-flex; }
.pc-pop { position: fixed; z-index: 1100; display: flex; flex-direction: column; gap: 10px; padding: 14px;
  border-radius: var(--r-md); background: var(--glass-strong);
  backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge); }
.pc-title { font-size: 13.5px; font-weight: 650; color: var(--ink); line-height: 1.4; }
.pc-desc { font-size: 12.5px; color: var(--ink-3); line-height: 1.5; }
.pc-actions { display: flex; gap: 8px; justify-content: flex-end; }

/* ===================== GLASS PROGRESS ===================== */
.gprog { display: flex; flex-direction: column; gap: 6px; }
.gprog-track { height: 8px; border-radius: 99px; background: var(--track); overflow: hidden; }
.gprog-fill { height: 100%; border-radius: 99px; transition: width .3s; }
.gprog-label { font-size: 12.5px; color: var(--ink-3); }

/* ===================== GLASS NOTICE (inline alert) ===================== */
.notice { display: flex; gap: 10px; padding: 11px 13px; border-radius: var(--r-md); font-size: 13px; line-height: 1.5; }
.notice > svg { flex: 0 0 auto; margin-top: 1px; }
.notice .notice-body { flex: 1; min-width: 0; }
.notice.info { background: rgba(var(--accent-rgb),.1); color: var(--ink-2); }
.notice.info > svg { color: var(--accent); }
.notice.error { background: var(--red-soft); color: var(--red); }
.notice.warn { background: var(--amber-soft); color: var(--amber); }

/* ===================== GLASS DROPDOWN MENU ===================== */
.ddm-anchor { display: inline-flex; }
.ddm-pop { position: fixed; z-index: 1100; display: flex; flex-direction: column; gap: 2px; padding: 6px;
  border-radius: var(--r-md); background: var(--glass-strong);
  backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge); overflow: auto; }
.ddm-item { display: flex; align-items: center; gap: 9px; width: 100%; padding: 9px 11px; border-radius: var(--r-sm);
  background: transparent; color: var(--ink); font-size: 13.5px; text-align: left; transition: background .12s; }
.ddm-item:hover:not(:disabled) { background: var(--fill); }
.ddm-item:disabled { opacity: .4; cursor: default; }
.ddm-item.danger { color: var(--red); }
.ddm-icon { flex: 0 0 auto; display: grid; place-items: center; color: var(--ink-3); }
.ddm-label { flex: 1; min-width: 0; }

/* ===================== GLASS TOOLTIP ===================== */
.tt-anchor { display: inline-flex; }
.tt-pop { position: fixed; z-index: 1200; max-width: 260px; padding: 6px 10px; border-radius: 9px;
  background: var(--glass-strong); backdrop-filter: blur(24px) saturate(180%); -webkit-backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge);
  font-size: 12px; font-weight: 550; color: var(--ink); line-height: 1.45; pointer-events: none; }

/* ===================== GLASS MODAL ===================== */
.lgm-backdrop { position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 24px;
  background: rgba(10,10,15,.42); backdrop-filter: blur(8px) saturate(120%); -webkit-backdrop-filter: blur(8px) saturate(120%);
  animation: lgm-fade .2s var(--smooth); }
[data-theme="light"] .lgm-backdrop { background: rgba(40,40,55,.26); }
@keyframes lgm-fade { from { opacity: 0; } to { opacity: 1; } }
.lgm { width: 100%; max-width: 540px; max-height: 86vh; display: flex; flex-direction: column; border-radius: var(--r-xl);
  background: var(--glass-strong); backdrop-filter: blur(40px) saturate(180%); -webkit-backdrop-filter: blur(40px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge);
  animation: lgm-pop .34s var(--spring); overflow: hidden; }
@keyframes lgm-pop { from { transform: translateY(14px) scale(.97); } to { transform: none; } }
.lgm-head { display: flex; align-items: center; gap: 12px; padding: 18px 22px 14px; border-bottom: 1px solid var(--hair); flex: 0 0 auto; }
.lgm-title { font-size: 18px; font-weight: 740; letter-spacing: -.02em; }
.lgm-x { margin-left: auto; width: 32px; height: 32px; border-radius: 10px; display: grid; place-items: center; color: var(--ink-2);
  transition: background .18s, color .18s, transform .15s var(--spring); }
.lgm-x:hover { background: var(--fill); color: var(--ink); }
.lgm-x:active { transform: scale(.9); }
.lgm-body { padding: 20px 22px; overflow-y: auto; flex: 1; min-height: 0; }
.lgm-foot { display: flex; gap: 10px; justify-content: flex-end; padding: 14px 22px 18px; border-top: 1px solid var(--hair); flex: 0 0 auto; }

/* ---- Device info / generator settings: glass form controls in a morph window ---- */
.setform { display: flex; flex-direction: column; gap: 14px; }
.setfield { display: flex; flex-direction: column; gap: 7px; }
.setfield-l { font-size: 12px; font-weight: 650; color: var(--ink-3); }
.optlist { display: flex; flex-direction: column; gap: 6px; }
.optline { display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%; padding: 11px 14px;
  border-radius: var(--r-md); background: var(--fill-2); border: 1px solid transparent; color: var(--ink-2); font-size: 13.5px; text-align: left;
  transition: background .16s, border-color .16s, color .16s; }
.optline:hover { background: var(--fill); color: var(--ink); }
.optline.on { background: rgba(var(--accent-rgb), .12); border-color: rgba(var(--accent-rgb), .5); color: var(--accent); font-weight: 650; }
.optline:disabled { opacity: .55; cursor: default; }
.sethint { font-size: 12px; color: var(--ink-3); line-height: 1.55; }
.setactions { display: flex; gap: 10px; }
.setactions > .btn { flex: 1; }

/* ===================== GLASS TOAST (imperative message) ===================== */
/* Stacked top-center toasts (the in-house replacement for antd `message`). Above
   modals/popovers/tooltips (z 1000/1100/1200) so a toast fired from inside a
   dialog still reads. Mount <ToastViewport/> once; the queue is a module singleton. */
.toast-viewport { position: fixed; top: 18px; left: 50%; transform: translateX(-50%); z-index: 1300;
  display: flex; flex-direction: column; align-items: center; gap: 10px; pointer-events: none; }
.toast { display: flex; align-items: center; gap: 10px; max-width: min(460px, 92vw); padding: 11px 16px;
  border-radius: var(--r-md); background: var(--glass-strong);
  backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge);
  font-size: 13.5px; font-weight: 550; color: var(--ink); line-height: 1.45; pointer-events: auto; cursor: default; }
.toast > svg { flex: 0 0 auto; }
.toast.success > svg { color: var(--green); }
.toast.error > svg { color: var(--red); }
.toast.warning > svg { color: var(--amber); }
.toast.info > svg { color: var(--accent); }
.toast-body { min-width: 0; }

/* ===================== GLASS WINDOW (button → centered ~50% morph) ===================== */
/* The shell animates real viewport geometry (left/top/width/height) between the
   trigger button and centered window, so the visual corner radius stays fixed. */
/* The scrim is split from the positioning layer: a parent's opacity multiplies
   into every child, so animating one combined backdrop faded the shell and the
   stretching button face to ghost mid-morph ("shrinks into blank"). The layer
   never fades; only .gwin-dim does. */
.gwin-layer { position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 24px; }
.gwin-dim { position: absolute; inset: 0; background: rgba(10,10,15,.42); backdrop-filter: blur(8px) saturate(120%); -webkit-backdrop-filter: blur(8px) saturate(120%); }
[data-theme="light"] .gwin-dim { background: rgba(40,40,55,.26); }
.gwin { display: flex; flex-direction: column; position: relative;
  overflow: hidden; background: var(--glass-strong);
  backdrop-filter: blur(40px) saturate(180%); -webkit-backdrop-filter: blur(40px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge); }
/* MorphWindow splits the pop shadow onto .gwin-shadow — an underlay animating the
   same geometry — so it can bloom in while the shell grows; the shell keeps only
   the glass edge highlight (t=0 face ≈ the trigger button, which has no drop
   shadow). MorphAddCard keeps the plain .gwin combined shadow. */
.gwin.gwin-morph { box-shadow: inset 0 1px 0 var(--glass-edge); }
.gwin-shadow { box-shadow: var(--sh-pop); }
.gwin-panel { min-height: 0; flex: 1; display: flex; flex-direction: column; }
/* Stretching button face (MorphWindow): a .btn-styled overlay tracking the
   morph geometry ABOVE the shell — at the endpoints it IS the trigger button
   (same variant classes), mid-flight it dissolves. Geometry/radius arrive as
   per-frame inline styles, so kill the .btn transitions (they'd lag/fight the
   animation); inline width/height already beat .btn's fixed height. */
.gwin-btnface { position: fixed; pointer-events: none; transition: none !important; }
.gwin-head { display: flex; align-items: center; gap: 12px; padding: 15px 18px; border-bottom: 1px solid var(--hair); flex: 0 0 auto; }
.gwin-body { padding: 20px 22px 24px; overflow-y: auto; flex: 1; min-height: 0; }
/* Stacked form/detail content (设备信息 / 信号源设置): hairline-spaced sections. */
.gwin-body.gwin-form { display: flex; flex-direction: column; gap: 22px; }
/* Pin the 取消/应用 row to the window bottom so short forms don't float it mid-air
   (collapses to 0 and scrolls normally once content fills the height). */
.gwin-body.gwin-form .setactions { margin-top: auto; }
.calgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 12px; }

/* ===================== ADD-CARD MORPH (square → in-place panel) ===================== */
/* The glass add-tile grows IN PLACE into this panel (geometry spring on .gwin,
   anchored at the tile rect), then shrinks back. Dim is light + slight blur —
   keeps the "grew from the tile" feel rather than a heavy modal scrim. Split
   from the layer like MorphWindow: a fading parent would ghost the panel. */
.addmorph-dim { background: rgba(10,10,15,.28);
  backdrop-filter: blur(4px) saturate(115%); -webkit-backdrop-filter: blur(4px) saturate(115%); }
[data-theme="light"] .addmorph-dim { background: rgba(40,40,55,.16); }
.addmorph-body { padding: 16px 18px 18px; }
/* Match the resting glass card (--glass, not --glass-strong; --sh-card, not
   --sh-pop) so the morph grows from a pane identical to the tile and lands
   back as one — seamless glass-on-glass, no shadow jump at the swap. */
.gwin.addmorph { background: var(--glass); backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge); }
/* Collapsed face is just the tile's contents (the shell already supplies the
   glass); it crossfades out as the panel fades in. */
.addmorph-face { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  pointer-events: none; flex-direction: column; gap: 12px; color: var(--ink-2); }
.addmorph-face .pl { width: 50px; height: 50px; border-radius: 50%; background: var(--fill); color: var(--ink-2); display: grid; place-items: center; }
.addmorph-face .at { font-size: 15px; font-weight: 650; color: var(--ink); }
.addmorph-face .as { font-size: 12.5px; color: var(--ink-3); }

/* Add-panel option rows (discoverable devices / startable signal sources):
   roomy rows with hairline separators instead of a cramped flex gap. */
.addlist { display: flex; flex-direction: column; }
.addrow { display: flex; align-items: center; gap: 12px; padding: 12px 2px; }
.addrow + .addrow { border-top: 1px solid var(--sep); }
.addrow .arname { font-size: 13.5px; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.addrow .arsub { font-size: 11.5px; color: var(--ink-3); margin-top: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* device-info bits */
.dval-code { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.dval-code code { font-family: var(--mono, ui-monospace, SFMono-Regular, monospace); font-size: 12.5px; background: var(--fill); padding: 3px 9px; border-radius: 7px; color: var(--ink); max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.copyb { width: 26px; height: 26px; border-radius: 8px; display: grid; place-items: center; color: var(--ink-3); flex: 0 0 auto; transition: background .16s, color .16s, transform .12s var(--spring); }
.copyb:hover { background: var(--fill); color: var(--ink); }
.copyb:active { transform: scale(.88); }
.cap-pill { display: inline-flex; align-items: center; height: 26px; padding: 0 11px; border-radius: 8px; font-size: 12.5px; font-weight: 650; }
.kvgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 18px; }
.kvc { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; font-size: 12.5px; padding: 6px 0; border-bottom: 1px solid var(--sep); }
.kvc .k { color: var(--ink-2); }
.kvc .v { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ===================== DATABASE HUB (数据库) ===================== */
/* Records view: device rail (left navigation = device filter) + main column. */
.rec-grid { display: grid; grid-template-columns: 232px minmax(0, 1fr); gap: 18px; align-items: start; }
@media (max-width: 1180px) { .rec-grid { grid-template-columns: 1fr; } }
.rec-main { min-width: 0; display: flex; flex-direction: column; gap: 14px; }

/* entrance primitives — containers drop .hub-anim after the run (JS timeout)
   so `animation ... both` can't lock transform against card hover-lift. */
@keyframes hubRowIn { from { opacity: 0; transform: translateY(11px); } to { opacity: 1; transform: none; } }
@keyframes hubPop { from { opacity: 0; transform: scale(.86); } to { opacity: 1; transform: none; } }
@keyframes hubCount { 0% { transform: scale(1); } 40% { transform: scale(1.22); } 100% { transform: scale(1); } }
.hub-anim > * { animation: hubRowIn .5s var(--spring) both; animation-delay: calc(var(--i, 0) * 32ms); }
.hub-pop { animation: hubPop .45s var(--spring) both; }

.rail { position: sticky; top: 4px; display: flex; flex-direction: column; gap: 10px; }
.rail-list { position: relative; border-radius: var(--r-lg); padding: 8px; display: flex; flex-direction: column; gap: 2px;
  background: var(--glass); backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge); }
/* shared sliding highlight — one glass pad glides between rows; rows no
   longer paint their own .on background */
.rail-hl { position: absolute; left: 8px; right: 8px; top: 0; border-radius: 13px; z-index: 0; pointer-events: none;
  background: rgba(var(--accent-rgb), .15); border: 1px solid rgba(var(--accent-rgb), .2);
  box-shadow: inset 0 1px 0 var(--glass-edge);
  transition: transform .45s var(--spring), height .3s var(--smooth); }
.rail-sep { height: 1px; background: var(--sep); margin: 6px 10px; }
.rail-foot { display: flex; gap: 8px; }
.rrow { position: relative; z-index: 1; display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px; border-radius: 13px;
  text-align: left; color: var(--ink); transition: background .18s, transform .14s var(--spring); }
.rrow:hover:not(.on) { background: var(--fill); }
.rrow:active { transform: scale(.97); }
.rtile { width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; flex: 0 0 auto; overflow: hidden;
  background: var(--fill); color: var(--ink-2); transition: background .2s, color .2s, transform .3s var(--spring); }
.rtile.muted { color: var(--ink-3); }
.rtile img, .dbtile img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; display: block; }
.rrow.on .rtile { background: var(--accent); color: var(--on-accent); box-shadow: var(--sh-accent); transform: scale(1.04); }
.rname { flex: 1; min-width: 0; font-size: 13.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rrow.on .rname { color: var(--accent); font-weight: 700; }
.asdot { display: inline-block; width: 6px; height: 6px; border-radius: 99px; background: var(--accent);
  margin-left: 7px; vertical-align: 2px; box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .18);
  animation: hubPop .4s var(--spring) both; }
.rcount { font-size: 11px; font-weight: 700; color: var(--ink-3); background: var(--fill);
  height: 19px; min-width: 19px; padding: 0 6px; border-radius: 99px; display: grid; place-items: center; flex: 0 0 auto; }
.rrow.on .rcount { color: var(--accent); background: rgba(var(--accent-rgb), .16); }

/* Device hero above the records list (replaced the old identity band):
   the logo tile IS the icon-picker trigger; no meta sub-line by design. */
.hub-hero { display: flex; align-items: center; gap: 16px; padding: 18px 20px; flex-wrap: wrap; border-radius: var(--r-lg);
  background: var(--glass); backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge); }
.dbtile { width: 56px; height: 56px; border-radius: 16px; display: grid; place-items: center; flex: 0 0 auto; overflow: hidden;
  background: rgba(var(--accent-rgb), .15); color: var(--accent); }
.dbtile.muted { background: var(--fill); color: var(--ink-3); }
.dbtxt { min-width: 0; flex: 1; }
.dbname { font-size: 20px; font-weight: 720; letter-spacing: -.02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dbmeta { font-size: 12.5px; color: var(--ink-3); margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dbact { margin-left: auto; display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.dbact .spill { animation: hubPop .45s var(--spring) both; }
.spill .pulse { width: 6px; height: 6px; border-radius: 99px; background: currentColor; flex: 0 0 auto; animation: spillPulse 2.2s ease infinite; }
@keyframes spillPulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
.hub-modes { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; width: 100%; padding-top: 14px; border-top: 1px solid var(--hair); margin-top: 2px; }
.hm-label { font-size: 12px; font-weight: 700; color: var(--ink-3); margin-right: 4px; }

/* Picture-mode chips (28px glass pill; active = solid accent). The check
   glyph is always mounted so toggling morphs its width+scale in place. */
.mchip { display: inline-flex; align-items: center; height: 28px; padding: 0 12px; border-radius: var(--pill);
  font-size: 12.5px; font-weight: 650; color: var(--ink-2); flex: 0 0 auto;
  background: var(--glass-strong); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: inset 0 1px 0 var(--glass-edge);
  transition: transform .14s var(--spring), background .25s var(--smooth), color .25s, border-color .25s, box-shadow .3s; }
.mchip:hover { background: var(--fill-press); }
.mchip:active { transform: scale(.93); }
.mchip.on { background: var(--accent); border-color: transparent; color: var(--on-accent); box-shadow: 0 3px 12px rgba(var(--accent-rgb), .35); }
.mchip .ck { display: inline-flex; width: 0; opacity: 0; transform: scale(.3) rotate(-40deg); overflow: hidden;
  transition: width .32s var(--spring), opacity .2s, transform .32s var(--spring); }
.mchip.on .ck { width: 17px; opacity: 1; transform: none; }

/* Module filter chips — same liquid-pill mechanics as the EDID gamut chips,
   tinted by each module's brand colour (inline --gc). */
.fchip { display: inline-flex; align-items: center; gap: 7px; height: 30px; padding: 0 12px;
  border-radius: var(--pill); font-size: 12.5px; font-weight: 650; color: var(--ink-2); flex: 0 0 auto;
  background: var(--glass-strong); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: inset 0 1px 0 var(--glass-edge);
  transition: transform .14s var(--spring), background .25s var(--smooth), color .25s, box-shadow .3s var(--smooth), border-color .25s var(--smooth); }
.fchip .dot { width: 7px; height: 7px; border-radius: 99px; background: var(--gc, var(--accent)); opacity: .5;
  flex: 0 0 auto; transition: opacity .25s, box-shadow .3s, transform .3s var(--spring); }
.fchip:hover { background: var(--fill-press); }
.fchip:active { transform: scale(.92); }
.fchip.on { background: color-mix(in srgb, var(--gc, var(--accent)) 16%, transparent);
  border-color: color-mix(in srgb, var(--gc, var(--accent)) 50%, transparent);
  box-shadow: inset 0 1px 0 var(--glass-edge), 0 0 0 1px color-mix(in srgb, var(--gc, var(--accent)) 28%, transparent),
    0 4px 14px color-mix(in srgb, var(--gc, var(--accent)) 26%, transparent); }
[data-theme="dark"] .fchip.on { color: color-mix(in srgb, var(--gc, var(--accent)) 58%, #fff); }
[data-theme="light"] .fchip.on { color: color-mix(in srgb, var(--gc, var(--accent)) 72%, #000); }
.fchip.on .dot { opacity: 1; transform: scale(1.25); box-shadow: 0 0 7px 0 color-mix(in srgb, var(--gc, var(--accent)) 75%, transparent); }

/* Device icon picker (hero tile → glass popover: presets + upload). */
.pickwrap { position: relative; flex: 0 0 auto; }
button.dbtile { cursor: pointer; transition: transform .25s var(--spring), box-shadow .25s, background .2s; }
button.dbtile:hover { transform: scale(1.05); box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .28); }
button.dbtile:active { transform: scale(.95); }
.lpop { position: absolute; top: calc(100% + 10px); left: 0; z-index: 90; display: flex; align-items: center; gap: 4px;
  padding: 7px; border-radius: 14px; white-space: nowrap;
  background: var(--glass-strong); backdrop-filter: blur(30px) saturate(180%); -webkit-backdrop-filter: blur(30px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge);
  transform-origin: top left; animation: hubPop .35s var(--spring) both; }
.lpop .lopt { width: 36px; height: 36px; border-radius: 10px; display: grid; place-items: center; color: var(--ink-2);
  transition: background .16s, color .16s, transform .14s var(--spring); }
.lpop .lopt:hover { background: var(--fill-press); color: var(--ink); }
.lpop .lopt:active { transform: scale(.88); }
.lpop .lopt.on { background: rgba(var(--accent-rgb), .15); color: var(--accent); box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), .3); }
.lpop .lsep { width: 1px; height: 22px; background: var(--sep); margin: 0 5px; flex: 0 0 auto; }
.lpop .lup { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px; border-radius: 9px;
  font-size: 12.5px; font-weight: 650; color: var(--ink-2); transition: background .16s, color .16s; }
.lpop .lup:hover { background: var(--fill-press); color: var(--ink); }

/* Bare icon button (lighter than .btn.icon — table row actions, drawer chrome). */
.ib { width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; color: var(--ink-3);
  transition: background .15s, color .15s, transform .12s var(--spring); flex: 0 0 auto; }
.ib:hover { background: var(--fill-press); color: var(--ink); }
.ib:active { transform: scale(.88); }
.ib.lg { width: 36px; height: 36px; border-radius: 11px; }

/* Filter row: module chips left, search right (34px control scale). */
.ctool { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ctool .searchbox { margin-left: auto; }
.searchbox { position: relative; width: 230px; }
.searchbox .input { height: 34px; font-size: 13px; padding-left: 36px; border-radius: 12px; }
.searchbox .si { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); color: var(--ink-3); display: grid; }

/* Date-grouped records list (inside a .dpanel). Fixed 目标/指标/时间 columns
   keep every row strictly aligned; the actions overlay the time cell on
   hover instead of reserving an empty column. */
.group-h { display: flex; align-items: baseline; gap: 8px; padding: 18px 18px 8px; }
.group-h:first-child { padding-top: 14px; }
.group-h .gd { font-size: 12.5px; font-weight: 800; color: var(--ink-2); letter-spacing: .02em; }
.group-h .gcount { font-size: 11.5px; font-weight: 600; color: var(--ink-3); }
.lrow2 { display: grid; grid-template-columns: 34px minmax(160px, 1fr) 110px 240px 92px;
  align-items: center; gap: 10px; padding: 9px 18px; cursor: pointer; transition: background .15s; }
.lrow2:hover { background: var(--fill); }
.lrow2.sel { background: rgba(var(--accent-rgb), .08); }
.lrow2 + .lrow2 { border-top: 1px solid var(--hair); }
/* module glyph ⇄ checkbox crossfade in the row head */
.cellchip { position: relative; width: 30px; height: 30px; }
.cellchip .modchip, .cellchip .cbx-hit { position: absolute; inset: 0; margin: auto; }
.cellchip .modchip { transition: opacity .18s, transform .3s var(--spring); }
.cellchip .cbx-hit { display: grid; place-items: center; opacity: 0; transform: scale(.55);
  transition: opacity .18s, transform .3s var(--spring); }
.cellchip .cbx .box { width: 22px; height: 22px; border-radius: 7px; }
.lrow2:hover .cellchip .modchip, .lrow2.sel .cellchip .modchip { opacity: 0; transform: scale(.55); }
.lrow2:hover .cellchip .cbx-hit, .lrow2.sel .cellchip .cbx-hit,
.cellchip .cbx-hit:focus-within { opacity: 1; transform: none; }
.namecell { display: flex; align-items: center; gap: 11px; min-width: 0; }
.modchip { width: 30px; height: 30px; display: grid; place-items: center; border-radius: 9px; flex: 0 0 auto; }
.nc-txt { min-width: 0; display: flex; flex-direction: column; }
.nc-name { font-size: 14px; font-weight: 650; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 260px; }
.nc-sub { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ph-muted { color: var(--ink-3); font-weight: 500; }
.dim { color: var(--ink-2); }
.tgt { font-size: 13px; color: var(--ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* metric column: one type scale — qualifier 12.5px grey, value 13px semibold;
   the coverage bar hugs the column's right edge so bars align vertically */
.mline { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 650; min-width: 0; white-space: nowrap; }
.mline .mq { color: var(--ink-3); font-weight: 500; font-size: 12.5px; }
.mdot { width: 7px; height: 7px; border-radius: 99px; flex: 0 0 auto; }
.mbar { width: 52px; height: 4px; border-radius: 99px; background: var(--track); overflow: hidden; flex: 0 0 auto; margin-left: auto; margin-right: 2px; }
.mbar i { display: block; height: 100%; width: 0; border-radius: 99px; background: var(--mb, var(--accent));
  transition: width .9s var(--smooth) .25s; }
.mbar.ready i { width: var(--w); }
.cap-pill.hdr { background: var(--purple-soft); color: var(--purple); margin-left: 8px; height: 22px; font-size: 11px; padding: 0 8px; }
/* time ⇄ row actions crossfade in the same cell */
.endcell { position: relative; display: flex; align-items: center; justify-content: flex-end; min-width: 0; }
.endcell .rtime { font-size: 12.5px; color: var(--ink-3); font-variant-numeric: tabular-nums; transition: opacity .18s; }
.lrow2:hover .endcell .rtime { opacity: 0; }
.endcell .rowact { position: absolute; right: -4px; }
.rowact { display: flex; gap: 2px; justify-content: flex-end; opacity: 0; transform: translateX(8px);
  transition: opacity .2s, transform .3s var(--spring); }
.lrow2:hover .rowact, .rowact:focus-within { opacity: 1; transform: none; }

/* Device overview cards (全部设备 scope). */
.hubcards { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px; }
.hubcard { border-radius: var(--r-lg); padding: 18px; display: flex; flex-direction: column; gap: 14px; cursor: pointer; text-align: left;
  background: var(--glass); backdrop-filter: blur(34px) saturate(180%); -webkit-backdrop-filter: blur(34px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-card), inset 0 1px 0 var(--glass-edge);
  transition: transform .3s var(--spring), box-shadow .3s; }
.hubcard:hover { transform: translateY(-3px); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge); }
.hubcard:active { transform: translateY(-1px) scale(.99); }
.hubcard .dbtile { width: 44px; height: 44px; border-radius: 13px; }
.hubcard .dbname { font-size: 15.5px; }
.hc-top { display: flex; align-items: center; gap: 13px; }
.hc-stats { display: flex; gap: 20px; align-items: center; padding: 12px 2px; border-top: 1px solid var(--sep); border-bottom: 1px solid var(--sep); }
.hc-stat .k { font-size: 11px; color: var(--ink-3); font-weight: 650; }
.hc-stat .v { font-size: 14px; font-weight: 650; margin-top: 3px; font-variant-numeric: tabular-nums; }
.hc-mods { display: flex; gap: 5px; margin-left: auto; }
.hc-mods .mm { width: 22px; height: 22px; border-radius: 7px; display: grid; place-items: center; }
.hc-foot { display: flex; gap: 8px; }
.hc-foot .btn { flex: 1; }
.sect-h { font-size: 13px; font-weight: 800; color: var(--ink-2); letter-spacing: .02em; margin: 8px 2px -4px; }

/* Floating selection action bar. */
.selbar { position: fixed; left: 50%; bottom: 48px; display: flex; align-items: center; gap: 9px;
  padding: 9px 11px 9px 18px; border-radius: var(--pill); z-index: 800;
  background: var(--glass-strong); backdrop-filter: blur(40px) saturate(180%); -webkit-backdrop-filter: blur(40px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge);
  transform: translateX(-50%) translateY(120px); opacity: 0; pointer-events: none;
  transition: transform .45s var(--spring), opacity .3s; }
.selbar.show { transform: translateX(-50%); opacity: 1; pointer-events: auto; }
.selbar .selcount { font-size: 14px; font-weight: 700; margin-right: 4px; white-space: nowrap; display: inline-block; }
/* keyed remount on count change replays the pulse */
.selbar.show .selcount { animation: hubCount .35s var(--spring); }

/* Run detail drawer. */
.drawer-backdrop { position: fixed; inset: 0; z-index: 900; }
.drawer { position: fixed; top: calc(var(--tb-h) + 14px); right: 14px; bottom: 14px; width: 442px; z-index: 950;
  display: flex; flex-direction: column; border-radius: var(--r-xl);
  background: var(--glass-strong); backdrop-filter: blur(40px) saturate(180%); -webkit-backdrop-filter: blur(40px) saturate(180%);
  border: 1px solid var(--glass-border); box-shadow: var(--sh-pop), inset 0 1px 0 var(--glass-edge); }
.drawer.open { animation: drawer-in .5s var(--spring); }
@keyframes drawer-in { from { transform: translateX(calc(100% + 28px)); } to { transform: none; } }
.drawer-head { display: flex; align-items: center; gap: 12px; padding: 16px 18px 13px; border-bottom: 1px solid var(--hair); flex: 0 0 auto; }
.dh-name { font-size: 16.5px; font-weight: 720; letter-spacing: -.02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dh-sub { font-size: 12px; color: var(--ink-3); margin-top: 2px; }
.drawer-body { flex: 1; min-height: 0; overflow-y: auto; padding: 18px; display: flex; flex-direction: column; gap: 20px; }
.drawer-foot { padding: 13px 18px 16px; border-top: 1px solid var(--hair); display: flex; flex-direction: column; gap: 9px; flex: 0 0 auto; }
.derow2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
/* KPI cards pop in one after another while the drawer springs open */
.drawer.open .derow2 > * { animation: hubPop .5s var(--spring) both; animation-delay: calc(120ms + var(--di, 0) * 60ms); }
.destat.lead { background: color-mix(in srgb, var(--lead, var(--accent)) 12%, transparent);
  border-color: color-mix(in srgb, var(--lead, var(--accent)) 26%, transparent); }
.destat.lead .v { color: var(--lead, var(--accent)); }

/* ===================== ANALYZE DATA LIST (imported spectra / curves) ===================== */
/* One liquid-glass row design shared by 白点绘制 光谱 and 亮度曲线 曲线 lists:
   optional eye visibility toggle (tinted with the series colour), double-click
   rename, count badge, and a unified × remove (reddens on hover). */
.adlist { display: flex; flex-direction: column; gap: 8px; max-height: 460px; overflow: auto; padding-right: 2px; }
.adrow { display: flex; align-items: center; gap: 11px; min-height: 54px; padding: 9px 11px; border-radius: 14px;
  background: var(--glass-2); backdrop-filter: blur(20px) saturate(160%); -webkit-backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid var(--glass-border); box-shadow: inset 0 1px 0 var(--glass-edge);
  transition: background .18s, opacity .25s, transform .14s var(--spring); }
.adrow:hover { background: var(--fill); }
.adrow.muted { opacity: .5; }
.adeye { width: 32px; height: 32px; border-radius: 10px; display: grid; place-items: center; color: var(--ink-3); flex: 0 0 auto;
  transition: background .16s, color .16s, transform .14s var(--spring); }
.adeye:hover { background: var(--fill); }
.adeye:active { transform: scale(.9); }
.adeye.on { color: var(--accent); }
.adtile { width: 32px; height: 32px; border-radius: 10px; display: grid; place-items: center; flex: 0 0 auto;
  background: rgba(var(--accent-rgb), .14); color: var(--accent); }
.adbadge { font-size: 10.5px; font-weight: 750; color: var(--accent); background: rgba(var(--accent-rgb), .14);
  padding: 3px 8px; border-radius: 7px; flex: 0 0 auto; }
.adswatch { width: 11px; height: 11px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .32); flex: 0 0 auto; }
.adcopy { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.adname { font-size: 14px; font-weight: 650; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.adname.editable { cursor: text; }
.admeta { font-size: 11.5px; color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.aded { height: 32px; font-size: 13px; }
.adx { width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; color: var(--ink-3); flex: 0 0 auto;
  transition: background .16s, color .16s, transform .12s var(--spring); }
.adx:hover { background: var(--red-soft); color: var(--red); }
.adx:active { transform: scale(.88); }

@media (prefers-reduced-motion: reduce) {
  .mag-root *, .page, .stagger > *, .mesh .b { animation: none !important; transition-duration: .01ms !important; }
}

/* ============================================================
   LIBRARY-ONLY ADDITIONS — everything below exists only in
   @c0lor/ui, NOT in mag.css. When syncing from c0lor-MAG, keep
   this section as-is (append upstream changes above it).
   ============================================================ */

/* GlassRoot shell — the scaffolding c0lor-MAG assembles by hand (the .win
   frame in AppLayout + inline accent vars in App.tsx), packaged for consumers.
   position + isolation give the absolute .mesh a home and keep its z-index
   local; overflow:clip trims the mesh's -10% bleed without creating a scroll
   container (unlike overflow:hidden, clip can't be scrolled by focus jumps). */
.glass-root {
  position: relative; isolation: isolate; overflow: clip;
  min-height: 100vh;
  font-family: var(--font); background: var(--base); color: var(--ink);
  letter-spacing: -.012em; -webkit-font-smoothing: antialiased;
  transition: background .5s var(--smooth), color .5s var(--smooth);
}
/* Content must be positioned above the z0 mesh: unpositioned children would
   paint UNDER the positioned blob layer and pick up its blur glow. */
.glass-root > .glass-content { position: relative; z-index: 1; min-height: inherit; }
@media (prefers-reduced-motion: reduce) {
  .glass-root { transition: none; }
}

/* GTable clickable-row cursor. GTable emits `.rowtap` when `onRowClick` is set
   (documented in its JSDoc), but c0lor-MAG dropped `.gtable tr.rowtap` from
   mag.css in the 数据库 hub redesign (its records list moved to `.lrow2` rows).
   The exported component still owns that contract, so the library keeps the
   cursor here rather than shipping a documented-but-unstyled class. */
.gtable tr.rowtap { cursor: pointer; }
