@cassius0924/dsh-usage-dashboard 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
1
  /** All plugin styles in one sheet, injected once at client load. */
2
- export declare const css = "\n.dsh-quota-root{position:absolute;right:16px;bottom:16px;z-index:2147483000;pointer-events:auto;opacity:1;visibility:visible;font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;font-size:13px;line-height:1.45;color:var(--dsw-alias-label-primary,#1f2328);transition:left .28s cubic-bezier(.22,1,.36,1),top .28s cubic-bezier(.22,1,.36,1),opacity .16s ease-out,visibility 0s linear}\n.dsh-quota-root.dsh-quota-root--hidden{opacity:0;visibility:hidden;pointer-events:none;transition:opacity .14s ease-out,visibility 0s linear .14s}\n.dsh-quota-root.dsh-quota-dragging{transition:none}\n.dsh-quota-card,.dsh-quota-card *{box-sizing:border-box}\n.dsh-quota-card{background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);padding:12px 14px;min-width:208px;max-width:270px;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none}\n.dsh-quota-root.dsh-quota-dragging .dsh-quota-card{cursor:grabbing;box-shadow:0 12px 32px rgba(0,0,0,.28)}\n.dsh-quota-header{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;min-width:0}\n.dsh-quota-title{font-weight:600;font-size:13px;color:var(--dsw-alias-label-primary,#1f2328);display:flex;align-items:center;gap:6px;min-width:0;overflow:hidden}\n.dsh-quota-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsh-quota-grip{color:var(--dsw-alias-label-secondary,#59636e);opacity:.7;font-size:11px;letter-spacing:0;cursor:grab;flex:none}\n.dsh-quota-dot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-state-success-primary,#2da44e);flex:none}\n.dsh-quota-dot--error{background:var(--dsw-alias-state-error-primary,#cf222e)}\n.dsh-quota-dot--warn{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dsh-quota-total--low{color:var(--dsw-alias-state-warning-primary,#9a6700)!important}\n.dsh-quota-dot--idle{background:var(--dsw-alias-label-secondary,#59636e);opacity:.6}\n.dsh-quota-collapsed-total-row{margin-top:6px}\n.dsh-quota-collapsed-total{font-weight:700;font-size:15px;color:var(--dsw-alias-state-business-primary,#0969da);font-variant-numeric:tabular-nums}\n.dsh-quota-actions{display:flex;gap:2px;flex:none}\n.dsh-quota-btn{border:none;background:transparent;cursor:pointer;color:var(--dsw-alias-label-secondary,#59636e);padding:3px 7px;border-radius:6px;font-size:13px;line-height:1}\n.dsh-quota-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#1f2328)}\n.dsh-quota-btn:active:not(:disabled){transform:scale(.92)}\n.dsh-quota-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}\n.dsh-quota-btn:disabled{opacity:.5;cursor:default}\n.dsh-quota-body{color:var(--dsw-alias-label-secondary,#59636e)}\n.dsh-quota-remaining-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e);margin-bottom:2px}\n.dsh-quota-total{font-size:24px;font-weight:700;color:var(--dsw-alias-label-primary,#1f2328);letter-spacing:-.02em;font-variant-numeric:tabular-nums}\n.dsh-quota-currency{font-size:14px;font-weight:600;margin-left:3px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dsh-quota-row{display:flex;justify-content:space-between;gap:12px;margin-top:6px;font-size:12px}\n.dsh-quota-label{color:var(--dsw-alias-label-secondary,#59636e)}\n.dsh-quota-value{color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dsh-quota-error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-message-cost{position:relative;display:inline-flex;align-items:center;order:10;gap:10px;height:28px;color:var(--dsw-alias-label-tertiary,#59636e);font-size:12px;line-height:20px;font-variant-numeric:tabular-nums;white-space:nowrap;cursor:help}\n.dq-message-cost-separator{cursor:default}\n.dq-message-cost-amount{text-decoration-line:underline;text-decoration-style:dotted;text-decoration-color:currentColor;text-underline-offset:4px}\n.dq-message-cost:hover{color:var(--dsw-alias-label-secondary,#3f4852)}\n.dq-message-cost:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px;border-radius:4px}\n.dq-message-cost::after{content:attr(data-dq-tooltip);position:absolute;left:0;top:calc(100% + 6px);z-index:2147483001;width:max-content;max-width:min(240px,calc(100vw - 24px));padding:6px 9px;border-radius:8px;background:var(--dsw-alias-bg-inverse,#1f2328);color:var(--dsw-alias-label-inverse,#fff);box-shadow:0 6px 18px rgba(0,0,0,.22);font-size:12px;font-weight:400;line-height:1.45;text-align:left;white-space:normal;pointer-events:none;opacity:0;visibility:hidden;transform:translateY(-3px);transition:opacity .12s ease-out,transform .12s cubic-bezier(.22,1,.36,1),visibility 0s linear .12s}\n.dq-message-cost:hover::after,.dq-message-cost:focus-visible::after{opacity:1;visibility:visible;transform:translateY(0);transition:opacity .12s ease-out,transform .12s cubic-bezier(.22,1,.36,1),visibility 0s linear}\n@media (hover:hover){\n [data-time-hover-root] .dq-message-cost{opacity:0;transition:opacity 80ms ease}\n [data-time-hover-root]:hover .dq-message-cost,[data-time-hover-root]:focus-within .dq-message-cost{opacity:1}\n}\n/* \u5361\u7247\u95F4\u8DDD\uFF08\u6B64\u5904 gap\uFF09\u523B\u610F\u5927\u4E8E\u5361\u5185\u5C0F\u8282\u95F4\u8DDD\uFF08margin-top:12/14/16px \u90A3\u4E00\u6863\uFF09\uFF0C\u5236\u9020\"\u5361\u7247\u5916\u758F\u3001\u5361\u7247\u5185\u5BC6\"\u7684\n \u758F\u5BC6\u5BF9\u6BD4\uFF1A24px \u76F8\u5BF9\u5361\u5185\u6700\u5927\u6863 16px \u4ECD\u6709 1.5 \u500D\u7EA7\u5DEE\uFF0C\u76F8\u5BF9\u5E38\u89C1\u6863 12px \u6709 2 \u500D\u7EA7\u5DEE\uFF0C\u4E0D\u4E0E .dq-card \u7684\n padding\uFF0816px/20px\uFF0C\u5361\u7247\u5185\u90E8\u547C\u5438\u611F\uFF0C\u8F6E\u6B21 36 \u5DF2\u5B9A\uFF09\u6DF7\u540C\u3002 */\n.dq-balance{display:flex;flex-direction:column;gap:24px;padding:20px 24px 48px;max-width:860px;margin:0 auto;font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}\n@media (max-width:620px){.dq-balance{box-sizing:border-box;max-width:calc(100vw - 56px);padding:16px 16px calc(var(--dq-composer-h,126px) + 16px)}}\n.dq-card{background:var(--dsw-alias-bg-layer-1,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-radius:12px;padding:16px}\n.dq-card-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:0 0 12px;letter-spacing:.02em}\n.dq-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}\n.dq-card-head .dq-card-title{margin:0}\n.dq-card-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;min-width:0}\n/* \u4E3B\u5361\u7247\uFF08\u4F59\u989D/\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017/\u6D88\u8017\u6982\u89C8/\u7528\u91CF\u8D8B\u52BF\uFF0C\u8F6E\u6B21 45 \u65B0\u589E\u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300D\uFF09\u89C6\u89C9\u6743\u91CD\u9AD8\u4E8E\u6B21\u7EA7\u5206\u6790\n \u5361\u7247\u4E0E\u5BFC\u822A\u7C7B\u5361\u7247\uFF1A\u53EA\u8C03\u6392\u7248\u5C5E\u6027\u2014\u2014\u5185\u8FB9\u8DDD\u3001\u6807\u9898\u5B57\u53F7\u5B57\u91CD\u4E0E\u53D6\u8272\u3001\u8FB9\u6846\u6DF1\u6D45\u2014\u2014\u4E0D\u5F15\u5165\u65B0\u914D\u8272\u4F53\u7CFB\u3002\u4E24\u6761\u89C4\u5219\n \u4E0E\u4E0A\u9762\u7684\u57FA\u7840\u89C4\u5219\u540C\u4E3A\u5355\u7C7B\u9009\u62E9\u5668\u4E14\u6E90\u7801\u987A\u5E8F\u66F4\u9760\u540E\uFF0C\u5929\u7136\u8986\u76D6\uFF0C\u4E0D\u9700\u8981 !important\u3002\u6B21\u7EA7\u5206\u6790\u5361\u7247\uFF08\u7528\u91CF\n \u70ED\u529B\u56FE/\u9AD8\u5CF0\u95F2\u65F6/\u7F13\u5B58\u547D\u4E2D/\u6A21\u578B\u6392\u884C/\u4F1A\u8BDD\u6392\u884C\uFF09\u4E0E\u5BFC\u822A\u7C7B\u5361\u7247\uFF08\u8BBE\u7F6E\uFF0C\u542B\u8F6E\u6B21 39 \u5E76\u5165\u7684\u5B98\u65B9\u5E73\u53F0\u94FE\u63A5\uFF09\u90FD\u4E0D\u52A0\n \u8FD9\u4E2A\u4FEE\u9970\u7C7B\uFF0C\u7EF4\u6301\u539F\u6709 .dq-card \u57FA\u7EBF\uFF0C\u5F62\u6210\u4E24\u6863\u6743\u91CD\u3002\u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300D\u5347\u7EA7\u4E3A\u4E3B\u5361\u7247\u7684\u7406\u7531\uFF1A\u5B83\u548C\u4F59\u989D\u4E00\u6837\u662F\n \u6253\u5F00\u8FD9\u4E2A\u300C\u989D\u5EA6\u300Dtab \u65F6\u4E0A\u4E0B\u6587\u6700\u76F4\u63A5\u76F8\u5173\u7684\u6570\u5B57\uFF08\u8FD9\u4E2A tab \u6302\u5728\u5177\u4F53\u4F1A\u8BDD\u5185\u90E8\uFF0C\u89C1 DESIGN_NOTES\u300C\u4FE1\u606F\u5C42\u7EA7\u300D\uFF09\uFF0C\n \u4E0D\u5C5E\u4E8E\u539F\u6709\u8D26\u6237\u5168\u5C40\u5C42\u7EA7 1-3 \u7684\u4EFB\u4F55\u4E00\u7EA7\uFF0C\u800C\u662F\u4E0E\u4E4B\u6B63\u4EA4\u7684\u53E6\u4E00\u4E2A\u7EF4\u5EA6\uFF0C\u4F46\u540C\u6837\u662F\u9996\u5C4F\u5FC5\u8BFB\uFF0C\u56E0\u6B64\u540C\u6837\u7ED9\n primary \u6743\u91CD\uFF0C\u4E0D\u65B0\u5F00\u7B2C\u4E09\u6863\u3002 */\n.dq-card--primary{padding:20px;border-color:var(--dsw-alias-border-l2,rgba(0,0,0,.12))}\n.dq-card--primary .dq-card-title{font-size:13px;font-weight:700;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-window-switch{display:inline-flex;align-items:center;gap:2px;padding:2px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.08))}\n.dq-window-btn{border:0;border-radius:6px;padding:4px 8px;background:transparent;color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;white-space:nowrap;cursor:pointer;transition:background .15s ease,color .15s ease,box-shadow .15s ease}\n.dq-window-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-window-btn--on{background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1f2328);box-shadow:0 1px 3px rgba(0,0,0,.12)}\n.dq-window-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n@media (max-width:620px){.dq-card-head{align-items:flex-start;flex-wrap:wrap}.dq-card-actions{width:100%;justify-content:space-between}}\n.dq-export{position:relative;flex:none}\n.dq-export-btn{min-width:58px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:4px 10px;font-size:12px;cursor:pointer;transition:border-color .15s ease,color .15s ease,background .15s ease}\n.dq-export-btn:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}\n.dq-export-btn--done{border-color:rgba(45,164,78,.35);color:var(--dsw-alias-state-success-primary,#1a7f37);background:rgba(45,164,78,.08)}\n.dq-export-menu{position:absolute;right:0;top:calc(100% + 6px);z-index:30;min-width:180px;padding:6px;background:var(--dsw-alias-bg-overlay,#fff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.16);display:flex;flex-direction:column;gap:2px}\n.dq-export-menu button{display:flex;align-items:center;justify-content:space-between;gap:16px;width:100%;border:0;border-radius:6px;padding:7px 8px;background:transparent;color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;text-align:left;cursor:pointer}\n.dq-export-menu button:hover,.dq-export-menu button:focus-visible{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06))}\n.dq-export-menu button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-1px}\n.dq-export-menu small{font-size:10.5px;font-weight:400;color:var(--dsw-alias-label-tertiary,#59636e);white-space:nowrap}\n/* Small-text tier system (\u8F6E\u6B21 41 \u6536\u655B): two whole-pixel tiers replace what used to be four values\n (11/11.5/12/12.5px) spaced only 0.5px apart \u2014 a gap browsers subpixel-round away, so it read as one\n size with extra maintenance cost, not an actual visual hierarchy. 11px = \"micro\" chrome the user\n scans rather than reads (dt-style stat/period labels, delta/status/coverage pills, share %, tags,\n the widget grip) \u2014 kept as-is. 12px = \"secondary text\" (section titles, hint/footnote/legend/sub\n prose, table cells, tooltip copy, alert/empty-state sentences, and filter/segmented-control button\n labels like .dq-window-btn/.dq-chart-switch-btn, which are the same control family and should share\n one size) \u2014 absorbed the old 11.5px and 12.5px selectors, all by rounding toward the nearer tier\n (11.5\u219212 grows slightly for legibility, safe because none of the affected containers are width-\n capped; 12.5\u219212 shrinks by 0.5px on two full-sentence blocks, imperceptible). Weight/color still do\n the job of separating titles from body text within the 12px tier, same as before this merge. Do not\n reintroduce a fractional-px value in this range; if a selector needs to stand apart from these two,\n it belongs in an already-established different tier (10px icon glyphs, 13px+ hero-adjacent text). */\n.dq-balance-grid{display:flex;flex-wrap:wrap;gap:12px 24px}\n.dq-stat{min-width:110px}\n.dq-stat-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e);margin-bottom:2px}\n.dq-stat-value{font-size:18px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-stat-value--ok{color:var(--dsw-alias-state-success-primary,#2da44e)}\n.dq-stat-value--bad{color:var(--dsw-alias-state-error-primary,#cf222e)}\n/* \u8F6E\u6B21 43: .dq-runway is a controlled <details> (see dashboard.tsx) \u2014 hover/toggle state\n drives the real open attribute now, so .dq-runway-detail only needs to react to [open];\n an unopened <details>'s non-summary children don't actually render in current Chromium no\n matter what display value author CSS gives them (:hover{display:block} alone is a dead end). */\n.dq-runway{position:relative}\n.dq-runway>summary{display:inline-block;list-style:none;border-radius:4px;cursor:pointer;text-decoration:underline dotted var(--dsw-alias-border-l2,rgba(0,0,0,.25));text-underline-offset:4px}\n.dq-runway>summary::-webkit-details-marker{display:none}\n.dq-runway>summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:3px}\n.dq-runway-detail{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;max-width:240px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:normal;font-size:12px;font-weight:500;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-runway[open] .dq-runway-detail{display:block}\n/* hero number for the balance, matching .dq-period-cost; relies on cascade order (declared after\n .dq-stat-value, same specificity) to win over the .dq-stat-value it's paired with in JSX \u2014 keep it below */\n/* \u8F6E\u6B21 47: .dq-remaining is a controlled <details> too now (see dashboard.tsx), same reason as\n .dq-runway above \u2014 an unopened <details>'s non-summary children don't actually render in current\n Chromium regardless of author display value, so the old :hover{display:flex} branch was dead code. */\n.dq-remaining{position:relative;font-size:22px;font-weight:680;letter-spacing:-.01em;line-height:1.25}\n.dq-remaining>summary{display:inline-block;list-style:none;border-radius:4px;cursor:pointer;text-decoration:underline dotted var(--dsw-alias-border-l2,rgba(0,0,0,.25));text-underline-offset:4px}\n.dq-remaining>summary::-webkit-details-marker{display:none}\n.dq-remaining>summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:3px}\n.dq-remaining-breakdown{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;flex-direction:column;gap:4px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:nowrap;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-remaining[open] .dq-remaining-breakdown{display:flex}\n.dq-remaining-granted{color:var(--dsw-alias-label-tertiary,#59636e);font-weight:400;font-size:11px}\n/* narrow screens: .dq-balance-grid wraps its 3 stats onto separate lines, so the absolutely-positioned\n .dq-remaining-breakdown/.dq-runway-detail popovers can float down over whichever stat now sits in the\n next row (e.g. \"\u4F59\u989D\u660E\u7EC6\" open covering \"\u9884\u8BA1\u53EF\u7528\"'s clickable summary) \u2014 force each stat onto its own\n full-width row and switch both popovers to normal in-flow layout so an open one pushes its own row\n taller instead of overlaying a sibling; desktop keeps the original hover-overlay behavior unchanged */\n@media (max-width:620px){\n .dq-balance-grid .dq-stat{flex:1 1 100%}\n .dq-remaining-breakdown,.dq-runway-detail{position:static;margin-top:6px;box-shadow:none;max-width:none;white-space:normal}\n}\n.dq-period-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0}\n.dq-period{min-width:0;padding:4px 16px}\n.dq-period:first-child{padding-left:0}\n.dq-period:last-child{padding-right:0}\n.dq-period+.dq-period{border-left:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n@media (max-width:620px){.dq-period-grid{grid-template-columns:1fr}.dq-period,.dq-period:first-child,.dq-period:last-child{padding:12px 0}.dq-period:first-child{padding-top:2px}.dq-period+.dq-period{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-left:0}}\n.dq-period-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:4px}\n.dq-period-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-period-cost{font-size:22px;font-weight:680;letter-spacing:-.01em;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;line-height:1.25}\n.dq-period-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);margin-top:2px;font-variant-numeric:tabular-nums;line-height:1.45}\n.dq-budget{margin-top:12px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-budget--unset{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-budget-action{border:0;background:transparent;padding:3px 4px;border-radius:5px;color:var(--dsw-alias-state-business-primary,#0969da);font:inherit;font-weight:600;white-space:nowrap;cursor:pointer}\n.dq-budget-action:hover{text-decoration:underline;text-underline-offset:3px}\n.dq-budget-action:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}\n.dq-budget-head,.dq-budget-meta{display:flex;align-items:center;justify-content:space-between;gap:12px}\n.dq-budget-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-budget-amount{font-size:12px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-budget-track{height:8px;margin-top:8px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}\n.dq-budget-fill{width:100%;height:100%;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);transform-origin:left center;transition:transform .35s cubic-bezier(.22,1,.36,1)}\n.dq-budget--risk .dq-budget-fill{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-budget--over .dq-budget-fill{background:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-budget-meta{margin-top:6px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-budget--risk .dq-budget-meta span:last-child{color:var(--dsw-alias-state-warning-primary,#9a6700);font-weight:600}\n.dq-budget--over .dq-budget-meta{color:var(--dsw-alias-state-error-primary,#cf222e);font-weight:600}\n@media (max-width:620px){.dq-budget-head,.dq-budget-meta,.dq-budget--unset{align-items:flex-start;flex-direction:column;gap:4px}}\n.dq-delta{display:inline-flex;align-items:center;gap:3px;font-size:11px;font-weight:600;padding:1px 6px;border-radius:999px;white-space:nowrap;cursor:help;font-variant-numeric:tabular-nums}\n.dq-delta--up{color:var(--dsw-alias-state-warning-primary,#9a6700);background:rgba(191,135,0,.14)}\n.dq-delta--down{color:var(--dsw-alias-state-success-primary,#1a7f37);background:rgba(45,164,78,.14)}\n.dq-delta--flat,.dq-delta--new{color:var(--dsw-alias-label-tertiary,#59636e);background:rgba(120,130,150,.14);font-weight:500}\n.dq-delta-label{font-weight:400;opacity:.8}\n.dq-pricing{margin-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));padding-top:10px}\n.dq-pricing-summary{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);cursor:pointer;list-style:none;width:fit-content;border-radius:6px;padding:2px 4px;margin-left:-4px}\n.dq-pricing-summary::-webkit-details-marker{display:none}\n.dq-pricing-summary::before{content:\"\u25B8\";display:inline-block;font-size:10px;opacity:.7;transition:transform .15s ease}\n.dq-pricing[open] .dq-pricing-summary::before{transform:rotate(90deg)}\n.dq-pricing-summary:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-pricing-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-pricing-now{font-size:11px;font-weight:600;padding:1px 7px;border-radius:999px;color:var(--dsw-alias-label-tertiary,#59636e);background:rgba(120,130,150,.14)}\n.dq-pricing-now--peak{color:var(--dsw-alias-state-warning-primary,#9a6700);background:rgba(191,135,0,.14)}\n.dq-pricing-body{margin-top:10px;overflow-x:auto}\n.dq-pricing-table{border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums;min-width:100%}\n.dq-pricing-table th{text-align:left;font-weight:500;color:var(--dsw-alias-label-tertiary,#59636e);padding:4px 12px 4px 0;white-space:nowrap;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-pricing-table td{padding:4px 12px 4px 0;color:var(--dsw-alias-label-primary,#1f2328);white-space:nowrap;vertical-align:top}\n.dq-pricing-table tbody tr+tr td{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.05))}\n.dq-pricing-foot{margin:8px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-coverage{margin-top:16px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-coverage-summary{display:flex;align-items:center;flex-wrap:wrap;gap:6px 9px;cursor:pointer;list-style:none;width:fit-content;max-width:100%;margin-left:-4px;padding:3px 4px;border-radius:6px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-coverage-summary::-webkit-details-marker{display:none}\n.dq-coverage-summary::before{content:\"\u25B8\";display:inline-block;font-size:10px;opacity:.7;transition:transform .15s ease}\n.dq-coverage[open] .dq-coverage-summary::before{transform:rotate(90deg)}\n.dq-coverage-summary:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-coverage-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-coverage-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-coverage-status{padding:1px 7px;border-radius:999px;background:rgba(45,164,78,.14);color:var(--dsw-alias-state-success-primary,#1a7f37);font-size:11px;font-weight:600}\n.dq-coverage--warn .dq-coverage-status{background:rgba(191,135,0,.14);color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-coverage-brief{font-size:12px;font-variant-numeric:tabular-nums}\n.dq-coverage-body{margin-top:10px;padding:11px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}\n.dq-coverage-metrics{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px 20px;margin:0}\n.dq-coverage-metrics div{min-width:0}\n.dq-coverage-metrics dt{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-coverage-metrics dd{margin:3px 0 0;font-size:13px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-coverage-range,.dq-coverage-note,.dq-coverage-warning{margin:9px 0 0;font-size:12px;line-height:1.55;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-coverage-range{font-variant-numeric:tabular-nums}\n.dq-coverage-warning{padding:8px 10px;border-radius:8px;background:rgba(191,135,0,.10);color:var(--dsw-alias-state-warning-primary,#7a5200)}\n@media (max-width:620px){.dq-coverage-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.dq-coverage-brief{flex-basis:100%;margin-left:19px}}\n.dq-peak-track{height:8px;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85;overflow:hidden}\n.dq-peak-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#bf8700);transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-peak-legend{display:flex;flex-wrap:wrap;gap:6px 20px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-peak-legend span{display:inline-flex;align-items:center;gap:6px}\n.dq-legend-swatch--peak{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-legend-swatch--offpeak{background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85}\n.dq-peak-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-top:14px}\n.dq-peak-delta{font-size:11px;font-weight:500;margin-left:8px;color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-peak-delta--save{color:var(--dsw-alias-state-success-primary,#1a7f37)}\n.dq-peak-foot{margin:10px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-cache-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-bottom:12px}\n.dq-muted-value{color:var(--dsw-alias-label-secondary,#59636e)!important;font-weight:550}\n.dq-stat-value--warn{color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-cache-track{height:8px;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#bf8700);overflow:hidden}\n.dq-cache-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-cache-legend{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-cache-legend span{display:inline-flex;align-items:center;gap:6px}\n.dq-legend-swatch--hit{background:var(--dsw-alias-state-success-primary,#2da44e)}\n.dq-legend-swatch--miss{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-cache-foot{margin:10px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-sessions{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:14px}\n.dq-session{display:flex;flex-direction:column;gap:5px;min-width:0}\n.dq-session-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}\n.dq-session-title{font-size:13px;font-weight:550;color:var(--dsw-alias-label-primary,#1f2328);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dq-session-cost{font-size:14px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;flex:none}\n.dq-session-track{height:6px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}\n.dq-session-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-business-primary,#4176e6);transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-session-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;line-height:1.5}\n/* The \u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300Dcard uses this class on a bare <p> (no flex-gap parent\n to lean on, unlike the .dq-session usage above), so it needs its own\n explicit spacing rather than falling back to the browser's implicit <p>\n margin \u2014 this project always declares spacing explicitly. */\np.dq-session-sub{margin:8px 0}\n.dq-session-foot{margin:12px 0 0;font-size:12px;color:var(--dsw-alias-label-tertiary,#59636e)}\n/* \u8F6E\u6B21 46: each ranking row is now a <details> whose <summary> wraps the existing\n head/track/sub trio unchanged (in a flex-column body div) plus a leading chevron \u2014\n same disclosure grammar as .dq-pricing/.dq-coverage above (hidden native marker,\n rotating glyph, :focus-visible ring), just applied to a multi-line summary instead\n of a single line. .dq-session-details needs its own min-width:0 because it is now\n the flex item inside .dq-session that the ellipsis chain must shrink through. */\n.dq-session-details{min-width:0}\n.dq-session-details>summary{list-style:none;cursor:pointer}\n.dq-session-details>summary::-webkit-details-marker{display:none}\n.dq-session-summary{display:flex;align-items:flex-start;gap:8px;border-radius:8px;padding:4px;margin:-4px}\n.dq-session-summary:hover{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.05))}\n.dq-session-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-session-chevron{flex:none;width:12px;margin-top:4px;font-size:10px;line-height:1;color:var(--dsw-alias-label-tertiary,#59636e);transition:transform .15s ease}\n.dq-session-details[open] .dq-session-chevron{transform:rotate(90deg)}\n.dq-session-summary-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:5px}\n/* Expanded body reuses the same shaded-inset language as .dq-coverage-body (10px\n gap from the summary, 11px/12px padding, 10px radius, bg-layer-2) rather than\n inventing a new \"nested panel\" look. SessionUsageCard renders inside unchanged. */\n.dq-session-expand{margin-top:10px;padding:11px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}\n.dq-rank{display:flex;flex-direction:column;gap:14px}\n.dq-rank-row{display:flex;flex-direction:column;gap:5px;min-width:0}\n.dq-rank-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}\n.dq-rank-name{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:550;color:var(--dsw-alias-label-primary,#1f2328);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dq-rank-cost{display:inline-flex;align-items:baseline;gap:8px;font-size:14px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;flex:none}\n.dq-rank-share{font-size:11px;font-weight:500;color:var(--dsw-alias-label-tertiary,#59636e);min-width:30px;text-align:right}\n.dq-rank-track{height:6px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}\n.dq-rank-fill{height:100%;border-radius:999px;transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-rank-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;line-height:1.5}\n.dq-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:10px;font-size:12px;line-height:1.6;color:var(--dsw-alias-state-warning-primary,#7a5200);background:rgba(191,135,0,.12);border:1px solid rgba(191,135,0,.35)}\n.dq-alert-icon{flex:none;width:18px;height:18px;border-radius:50%;background:var(--dsw-alias-state-warning-primary,#bf8700);color:#fff;font-weight:700;font-size:12px;line-height:18px;text-align:center}\n.dq-alert strong{font-weight:650}\n.dq-alert-link{margin-left:8px;color:inherit;text-decoration:underline;text-underline-offset:2px;white-space:nowrap}\n.dq-setting{display:flex;align-items:flex-start;gap:12px;margin-top:14px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-setting-label{font-size:13px;color:var(--dsw-alias-label-primary,#1f2328);padding-top:6px;flex:none}\n.dq-setting-control{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}\n.dq-setting-hint{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);line-height:1.5}\n.dq-number{width:82px;padding:5px 8px;font-size:13px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.16));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-number:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;border-color:transparent}\n.dq-links-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 8px}\n.dq-links{display:flex;flex-wrap:wrap;gap:8px}\n.dq-link{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-state-business-primary,#0969da);text-decoration:none;font-size:13px;font-weight:500;cursor:pointer}\n.dq-link:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.04))}\n/* .dq-toggle is the first settings control after the links row (\u8F6E\u6B21 39 merge) \u2014 its own\n border-top separates the \"\u5FEB\u6377\u5165\u53E3\" links (originally the standalone \u5B98\u65B9\u5E73\u53F0 card, renamed\n in \u8F6E\u6B21 39 fix-round-1 to read as part of \u8BBE\u7F6E's own navigation area) from \u8BBE\u7F6E controls\n inside the merged card, same 14px/14px/border-l1 recipe as .dq-setting so the two\n sub-sections read at equal weight. */\n.dq-toggle{display:flex;align-items:center;gap:10px;cursor:pointer;font-size:13px;margin-top:14px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-toggle input{width:16px;height:16px;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer}\n.dq-toggle-hint{margin:5px 0 0 26px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-widget-tabs{align-items:flex-start;flex-direction:column;gap:7px}\n.dq-widget-tab-list{display:flex;align-items:center;flex-wrap:wrap;gap:6px 14px}\n.dq-widget-tab{display:inline-flex;align-items:center;gap:7px;min-height:28px;color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;cursor:pointer}\n.dq-widget-tab input{width:14px;height:14px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer;flex:none}\n.dq-toggle input:focus-visible,.dq-model-item input:focus-visible,.dq-widget-tab input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-link:focus-visible,.dq-model-btn:focus-visible,.dq-chart-switch-btn:focus-visible,.dq-export-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-usage-totals{display:flex;flex-wrap:wrap;gap:12px 24px;margin-bottom:16px}\n/* value itself (number+unit, e.g. \"9999.9\u4E07\") must never break across lines inside its own column \u2014\n nowrap+ellipsis is a defensive floor for all widths; the actual fix for the realistic 320px 2-col\n case is the font-size drop below, which keeps every measured stress value comfortably under the\n 83px column width without ever hitting the ellipsis fallback (see round 38 PROGRESS entry) */\n.dq-usage-totals .dq-stat-value{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n@media (max-width:620px){.dq-usage-totals{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 24px}.dq-usage-totals .dq-stat{min-width:0}.dq-usage-totals .dq-stat-value{font-size:13px}}\n.dq-chart-peak{margin-left:10px;font-size:11px;font-weight:400;color:var(--dsw-alias-label-tertiary,#59636e);font-variant-numeric:tabular-nums}\n.dq-chart-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:16px 0 8px}\n.dq-chart-title:first-of-type{margin-top:0}\n.dq-chart-block-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:16px 0 8px}\n.dq-chart-block-head .dq-chart-title{margin:0}\n.dq-chart-controls{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex:none;flex-wrap:wrap}\n@media (max-width:620px){.dq-chart-block-head{align-items:flex-start;flex-direction:column}.dq-chart-controls{width:100%}}\n.dq-model-picker{position:relative}\n.dq-model-btn{border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:4px 10px;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:6px}\n.dq-model-btn:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}\n.dq-model-btn::after{content:\"\u25BE\";font-size:10px;opacity:.7}\n.dq-model-menu{position:absolute;right:0;top:calc(100% + 6px);z-index:30;min-width:190px;max-height:280px;overflow-y:auto;background:var(--dsw-alias-bg-overlay,#fff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.16);padding:6px;display:flex;flex-direction:column;gap:2px}\n.dq-model-item{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;font-size:12px;cursor:pointer;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-model-item:hover{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06))}\n.dq-model-item input{width:14px;height:14px;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer;flex:none}\n.dq-model-item span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dq-model-item--all{border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));padding-bottom:8px;margin-bottom:2px;border-radius:6px}\n.dq-model-tag{color:var(--dsw-alias-label-tertiary,#59636e);font-size:11px;flex:none}\n.dq-bar-group{display:flex;flex-direction:column-reverse;width:100%}\n.dq-bar-group .dq-bar{flex:0 0 auto;width:100%;border-radius:0}\n.dq-bar-group .dq-bar:first-child{border-radius:0 0 2px 2px}\n.dq-bar-group .dq-bar:last-child{border-radius:2px 2px 0 0}\n.dq-bar-group .dq-bar:not(:last-child){box-shadow:inset 0 1px 0 rgba(0,0,0,.18)}\n.dq-legend{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-legend-item{display:inline-flex;align-items:center;gap:6px}\n.dq-legend-swatch{width:10px;height:10px;border-radius:3px;flex:none}\n.dq-chart-switch{display:inline-flex;align-items:center;gap:2px;padding:2px;background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06));border-radius:999px;flex:none}\n.dq-chart-switch-btn{border:none;background:transparent;color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1;padding:5px 12px;border-radius:999px;cursor:pointer;transition:background .15s ease,color .15s ease}\n.dq-chart-switch-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-chart-switch-btn--on{background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1f2328);box-shadow:0 1px 3px rgba(0,0,0,.14)}\n.dq-chart-wrap{position:relative}\n.dq-bars-scroll{overflow-x:auto;overflow-y:hidden;padding-bottom:2px}\n.dq-tip{position:absolute;transform:translate(-50%,14px);z-index:40;pointer-events:none;background:var(--dsw-alias-bg-inverse,#1f2328);color:var(--dsw-alias-label-inverse,#fff);border-radius:8px;padding:7px 10px;font-size:12px;line-height:1.55;white-space:nowrap;box-shadow:0 6px 20px rgba(0,0,0,.28);font-variant-numeric:tabular-nums}\n.dq-tip--above{transform:translate(-50%,calc(-100% - 8px))}\n.dq-tip-head{font-weight:650;margin-bottom:2px}\n.dq-tip-row{opacity:.85}\n.dq-bars{display:flex;align-items:flex-end;gap:2px;width:100%}\n.dq-bar-col{flex:1 1 0;min-width:0;display:flex;flex-direction:column;justify-content:flex-end;align-items:stretch}\n.dq-bar{background:var(--dsw-alias-state-business-primary,#4176e6);border-radius:2px 2px 0 0;min-height:1px;transition:filter .12s ease}\n.dq-bar-col{cursor:default}\n.dq-bar-col,.dq-bar,.dq-heat-cell{scroll-margin-block-start:24px;scroll-margin-block-end:160px}\n.dq-bar-col:hover .dq-bar{filter:brightness(1.18) saturate(1.1)}\n.dq-bar-col:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-2px;border-radius:3px}\n.dq-bar:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;filter:brightness(1.18) saturate(1.1);position:relative;z-index:2}\n.dq-bar-col--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-2px;border-radius:3px}\n.dq-bar-col--pinned .dq-bar{filter:brightness(1.18) saturate(1.1)}\n.dq-bar--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;filter:brightness(1.18) saturate(1.1);position:relative;z-index:2}\n.dq-heat-cell:not(.dq-heat-cell--pad):hover{outline:1.5px solid var(--dsw-alias-label-primary,#1f2328);outline-offset:1px}\n.dq-heat-cell:not(.dq-heat-cell--pad):focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}\n.dq-heat-cell--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}\n.dq-bar-label{font-size:9px;color:var(--dsw-alias-label-tertiary,#59636e);text-align:center;margin-top:3px;overflow:visible;white-space:nowrap}\n.dq-heat{display:flex;flex-direction:column;gap:8px;max-width:100%}\n.dq-heat-scroll{overflow-x:auto;padding-bottom:2px}\n.dq-heatmap{display:flex;gap:3px}\n.dq-heat-week{display:flex;flex:1 1 0;min-width:6px;flex-direction:column;gap:3px}\n.dq-heat-cell{width:100%;aspect-ratio:1/1;border-radius:3px;flex:none}\n.dq-heat-cell--pad{background:transparent}\n.dq-heat-months{display:flex;gap:3px;margin-top:4px}\n.dq-heat-month{flex:1 1 0;min-width:6px;font-size:10px;line-height:14px;color:var(--dsw-alias-label-tertiary,#59636e);white-space:nowrap;overflow:visible}\n.dq-heat-key{width:12px;height:12px;border-radius:2px;flex:none}\n.dq-heat-scale{display:flex;align-items:center;gap:3px;font-size:10px;color:var(--dsw-alias-label-tertiary,#59636e);align-self:flex-end}\n.dq-heat-scale>span:first-child{margin-right:2px}\n.dq-heat-scale>span:last-child{margin-left:2px}\n.dq-muted{color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-warn{color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-crash{margin:10px 0 14px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.10));border-radius:8px;font-size:12px;line-height:1.5;color:var(--dsw-alias-state-error-primary,#cf222e);white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}\n.dq-empty{color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1.6;padding:6px 0}\n/* Error-state modifier for a card body that has nothing else to show (mirrors the .dq-stat-value--ok/\n --bad/--warn pattern: same layout as .dq-empty, declared after it so this color wins at equal\n specificity). Used by the current-session card when its own fetch failed and there is no prior\n data to fall back to \u2014 the status-row .dq-error/.dq-warn spans are for the account-wide balance/\n usage fetch, this is the per-card equivalent. */\n.dq-empty--error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-skel{background:linear-gradient(90deg,rgba(125,135,155,.10) 25%,rgba(125,135,155,.20) 37%,rgba(125,135,155,.10) 63%);background-size:400% 100%;border-radius:5px;animation:dq-shimmer 1.5s ease-in-out infinite}\n.dq-stat .dq-skel+.dq-skel{margin-top:6px}\n.dq-skel-bars{display:flex;align-items:flex-end;gap:2px;width:100%;height:120px;margin-top:10px}\n.dq-skel-bar{flex:1 1 0;min-width:0;border-radius:2px 2px 0 0}\n@keyframes dq-shimmer{0%{background-position:100% 50%}100%{background-position:0 50%}}\n.dq-status-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:20px}\n.dq-status-messages{display:flex;flex-direction:column;align-items:flex-start;gap:2px;min-width:0}\n.dq-status-row .dq-warn,.dq-status-row .dq-error{font-size:12px;line-height:1.45}\n.dq-sync{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-sync-dot{width:7px;height:7px;border-radius:50%;flex:none;background:var(--dsw-alias-label-tertiary,#8c959f)}\n.dq-sync--fresh .dq-sync-dot{background:var(--dsw-alias-state-success-primary,#2da44e)}\n.dq-sync--syncing .dq-sync-dot{background:var(--dsw-alias-state-business-primary,#4176e6);animation:dq-sync-pulse 1.2s ease-in-out infinite}\n.dq-sync--fallback{color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-sync--fallback .dq-sync-dot{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-sync--error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-sync--error .dq-sync-dot{background:var(--dsw-alias-state-error-primary,#cf222e)}\n@keyframes dq-sync-pulse{50%{opacity:.35;transform:scale(.8)}}\n.dq-refresh-btn{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:5px 10px;font-size:12px;cursor:pointer;transition:border-color .15s ease,color .15s ease}\n.dq-refresh-btn:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}\n.dq-refresh-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-refresh-btn:disabled{cursor:default;opacity:.6}\n.dq-refresh-icon{display:inline-block;line-height:1}\n.dq-refresh-icon--spin{animation:dq-spin .9s linear infinite}\n@keyframes dq-spin{to{transform:rotate(360deg)}}\n@media (prefers-reduced-motion:reduce){\n .dsh-quota-root,.dsh-quota-root *,\n .dsh-quota-root *::before,.dsh-quota-root *::after,\n .dq-message-cost,.dq-message-cost::after,\n .dq-balance,.dq-balance *,\n .dq-balance *::before,.dq-balance *::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}\n}\n";
2
+ export declare const css = "\n.dsh-quota-root{position:absolute;right:16px;bottom:16px;z-index:2147483000;pointer-events:auto;opacity:1;visibility:visible;font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;font-size:13px;line-height:1.45;color:var(--dsw-alias-label-primary,#1f2328);transition:left .28s cubic-bezier(.22,1,.36,1),top .28s cubic-bezier(.22,1,.36,1),opacity .16s ease-out,visibility 0s linear}\n.dsh-quota-root.dsh-quota-root--hidden{opacity:0;visibility:hidden;pointer-events:none;transition:opacity .14s ease-out,visibility 0s linear .14s}\n.dsh-quota-root.dsh-quota-dragging{transition:none}\n.dsh-quota-card,.dsh-quota-card *{box-sizing:border-box}\n.dsh-quota-card{background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);padding:12px 14px;min-width:208px;max-width:270px;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none}\n.dsh-quota-root.dsh-quota-dragging .dsh-quota-card{cursor:grabbing;box-shadow:0 12px 32px rgba(0,0,0,.28)}\n.dsh-quota-header{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;min-width:0}\n.dsh-quota-title{font-weight:600;font-size:13px;color:var(--dsw-alias-label-primary,#1f2328);display:flex;align-items:center;gap:6px;min-width:0;overflow:hidden}\n.dsh-quota-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsh-quota-grip{color:var(--dsw-alias-label-secondary,#59636e);opacity:.7;font-size:11px;letter-spacing:0;cursor:grab;flex:none}\n.dsh-quota-dot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-state-success-primary,#2da44e);flex:none}\n.dsh-quota-dot--error{background:var(--dsw-alias-state-error-primary,#cf222e)}\n.dsh-quota-dot--warn{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dsh-quota-total--low{color:var(--dsw-alias-state-warning-primary,#9a6700)!important}\n.dsh-quota-dot--idle{background:var(--dsw-alias-label-secondary,#59636e);opacity:.6}\n.dsh-quota-collapsed-total-row{margin-top:6px}\n.dsh-quota-collapsed-total{font-weight:700;font-size:15px;color:var(--dsw-alias-state-business-primary,#0969da);font-variant-numeric:tabular-nums}\n.dsh-quota-actions{display:flex;gap:2px;flex:none}\n.dsh-quota-btn{border:none;background:transparent;cursor:pointer;color:var(--dsw-alias-label-secondary,#59636e);padding:3px 7px;border-radius:6px;font-size:13px;line-height:1}\n.dsh-quota-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#1f2328)}\n.dsh-quota-btn:active:not(:disabled){transform:scale(.92)}\n.dsh-quota-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}\n.dsh-quota-btn:disabled{opacity:.5;cursor:default}\n.dsh-quota-body{color:var(--dsw-alias-label-secondary,#59636e)}\n.dsh-quota-remaining-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e);margin-bottom:2px}\n.dsh-quota-total{font-size:24px;font-weight:700;color:var(--dsw-alias-label-primary,#1f2328);letter-spacing:-.02em;font-variant-numeric:tabular-nums}\n.dsh-quota-currency{font-size:14px;font-weight:600;margin-left:3px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dsh-quota-row{display:flex;justify-content:space-between;gap:12px;margin-top:6px;font-size:12px}\n.dsh-quota-label{color:var(--dsw-alias-label-secondary,#59636e)}\n.dsh-quota-value{color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dsh-quota-error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-message-cost{position:relative;display:inline-flex;align-items:center;order:10;gap:10px;height:28px;color:var(--dsw-alias-label-tertiary,#59636e);font-size:12px;line-height:20px;font-variant-numeric:tabular-nums;white-space:nowrap;cursor:help}\n.dq-message-cost-separator{cursor:default}\n.dq-message-cost-amount{text-decoration-line:underline;text-decoration-style:dotted;text-decoration-color:currentColor;text-underline-offset:4px}\n.dq-message-cost:hover{color:var(--dsw-alias-label-secondary,#3f4852)}\n.dq-message-cost:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px;border-radius:4px}\n.dq-message-cost::after{content:attr(data-dq-tooltip);position:absolute;left:0;top:calc(100% + 6px);z-index:2147483001;width:max-content;max-width:min(240px,calc(100vw - 24px));padding:6px 9px;border-radius:8px;background:var(--dsw-alias-bg-inverse,#1f2328);color:var(--dsw-alias-label-inverse,#fff);box-shadow:0 6px 18px rgba(0,0,0,.22);font-size:12px;font-weight:400;line-height:1.45;text-align:left;white-space:normal;pointer-events:none;opacity:0;visibility:hidden;transform:translateY(-3px);transition:opacity .12s ease-out,transform .12s cubic-bezier(.22,1,.36,1),visibility 0s linear .12s}\n.dq-message-cost:hover::after,.dq-message-cost:focus-visible::after{opacity:1;visibility:visible;transform:translateY(0);transition:opacity .12s ease-out,transform .12s cubic-bezier(.22,1,.36,1),visibility 0s linear}\n@media (hover:hover){\n [data-time-hover-root] .dq-message-cost{opacity:0;transition:opacity 80ms ease}\n [data-time-hover-root]:hover .dq-message-cost,[data-time-hover-root]:focus-within .dq-message-cost{opacity:1}\n}\n/* \u5361\u7247\u95F4\u8DDD\uFF08\u6B64\u5904 gap\uFF09\u523B\u610F\u5927\u4E8E\u5361\u5185\u5C0F\u8282\u95F4\u8DDD\uFF08margin-top:12/14/16px \u90A3\u4E00\u6863\uFF09\uFF0C\u5236\u9020\"\u5361\u7247\u5916\u758F\u3001\u5361\u7247\u5185\u5BC6\"\u7684\n \u758F\u5BC6\u5BF9\u6BD4\uFF1A24px \u76F8\u5BF9\u5361\u5185\u6700\u5927\u6863 16px \u4ECD\u6709 1.5 \u500D\u7EA7\u5DEE\uFF0C\u76F8\u5BF9\u5E38\u89C1\u6863 12px \u6709 2 \u500D\u7EA7\u5DEE\uFF0C\u4E0D\u4E0E .dq-card \u7684\n padding\uFF0816px/20px\uFF0C\u5361\u7247\u5185\u90E8\u547C\u5438\u611F\uFF0C\u8F6E\u6B21 36 \u5DF2\u5B9A\uFF09\u6DF7\u540C\u3002 */\n/* Width parity with the host composer: the dashboard column reserves the\n same max-width as the conversation reading column (860px, \u8F6E\u6B21 42 kept it\n deliberately), but on narrower viewports the fixed 860px would overhang\n the input box \u2014 measured in dashboard.tsx as --dq-composer-w, capped at\n the reading-column width so wide viewports are unchanged. */\n.dq-balance{display:flex;flex-direction:column;gap:24px;padding:20px 24px 48px;max-width:min(860px,var(--dq-composer-w,860px));margin:0 auto;font-family:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;font-size:13px;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}\n@media (max-width:620px){.dq-balance{box-sizing:border-box;max-width:calc(100vw - 56px);padding:16px 16px calc(var(--dq-composer-h,126px) + 16px)}}\n.dq-card{background:var(--dsw-alias-bg-layer-1,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-radius:12px;padding:16px;min-width:0;max-width:100%}\n.dq-card-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:0 0 12px;letter-spacing:.02em}\n.dq-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}\n.dq-card-head .dq-card-title{margin:0}\n.dq-card-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;min-width:0}\n/* \u4E3B\u5361\u7247\uFF08\u4F59\u989D/\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017/\u6D88\u8017\u6982\u89C8/\u7528\u91CF\u8D8B\u52BF\uFF0C\u8F6E\u6B21 45 \u65B0\u589E\u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300D\uFF09\u89C6\u89C9\u6743\u91CD\u9AD8\u4E8E\u6B21\u7EA7\u5206\u6790\n \u5361\u7247\u4E0E\u5BFC\u822A\u7C7B\u5361\u7247\uFF1A\u53EA\u8C03\u6392\u7248\u5C5E\u6027\u2014\u2014\u5185\u8FB9\u8DDD\u3001\u6807\u9898\u5B57\u53F7\u5B57\u91CD\u4E0E\u53D6\u8272\u3001\u8FB9\u6846\u6DF1\u6D45\u2014\u2014\u4E0D\u5F15\u5165\u65B0\u914D\u8272\u4F53\u7CFB\u3002\u4E24\u6761\u89C4\u5219\n \u4E0E\u4E0A\u9762\u7684\u57FA\u7840\u89C4\u5219\u540C\u4E3A\u5355\u7C7B\u9009\u62E9\u5668\u4E14\u6E90\u7801\u987A\u5E8F\u66F4\u9760\u540E\uFF0C\u5929\u7136\u8986\u76D6\uFF0C\u4E0D\u9700\u8981 !important\u3002\u6B21\u7EA7\u5206\u6790\u5361\u7247\uFF08\u7528\u91CF\n \u70ED\u529B\u56FE/\u9AD8\u5CF0\u95F2\u65F6/\u7F13\u5B58\u547D\u4E2D/\u6A21\u578B\u6392\u884C/\u4F1A\u8BDD\u6392\u884C\uFF09\u4E0E\u5BFC\u822A\u7C7B\u5361\u7247\uFF08\u8BBE\u7F6E\uFF0C\u542B\u8F6E\u6B21 39 \u5E76\u5165\u7684\u5B98\u65B9\u5E73\u53F0\u94FE\u63A5\uFF09\u90FD\u4E0D\u52A0\n \u8FD9\u4E2A\u4FEE\u9970\u7C7B\uFF0C\u7EF4\u6301\u539F\u6709 .dq-card \u57FA\u7EBF\uFF0C\u5F62\u6210\u4E24\u6863\u6743\u91CD\u3002\u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300D\u5347\u7EA7\u4E3A\u4E3B\u5361\u7247\u7684\u7406\u7531\uFF1A\u5B83\u548C\u4F59\u989D\u4E00\u6837\u662F\n \u6253\u5F00\u8FD9\u4E2A\u300C\u989D\u5EA6\u300Dtab \u65F6\u4E0A\u4E0B\u6587\u6700\u76F4\u63A5\u76F8\u5173\u7684\u6570\u5B57\uFF08\u8FD9\u4E2A tab \u6302\u5728\u5177\u4F53\u4F1A\u8BDD\u5185\u90E8\uFF0C\u89C1 DESIGN_NOTES\u300C\u4FE1\u606F\u5C42\u7EA7\u300D\uFF09\uFF0C\n \u4E0D\u5C5E\u4E8E\u539F\u6709\u8D26\u6237\u5168\u5C40\u5C42\u7EA7 1-3 \u7684\u4EFB\u4F55\u4E00\u7EA7\uFF0C\u800C\u662F\u4E0E\u4E4B\u6B63\u4EA4\u7684\u53E6\u4E00\u4E2A\u7EF4\u5EA6\uFF0C\u4F46\u540C\u6837\u662F\u9996\u5C4F\u5FC5\u8BFB\uFF0C\u56E0\u6B64\u540C\u6837\u7ED9\n primary \u6743\u91CD\uFF0C\u4E0D\u65B0\u5F00\u7B2C\u4E09\u6863\u3002 */\n.dq-card--primary{padding:20px;border-color:var(--dsw-alias-border-l2,rgba(0,0,0,.12))}\n.dq-card--primary .dq-card-title{font-size:13px;font-weight:700;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-window-switch{display:inline-flex;align-items:center;gap:2px;padding:2px;border-radius:8px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.08))}\n.dq-window-btn{border:0;border-radius:6px;padding:4px 8px;background:transparent;color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;white-space:nowrap;cursor:pointer;transition:background .15s ease,color .15s ease,box-shadow .15s ease}\n.dq-window-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-window-btn--on{background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1f2328);box-shadow:0 1px 3px rgba(0,0,0,.12)}\n.dq-window-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n@media (max-width:620px){.dq-card-head{align-items:flex-start;flex-wrap:wrap}.dq-card-actions{width:100%;justify-content:space-between}}\n.dq-export{position:relative;flex:none}\n.dq-export-btn{min-width:58px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:4px 10px;font-size:12px;cursor:pointer;transition:border-color .15s ease,color .15s ease,background .15s ease}\n.dq-export-btn:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}\n.dq-export-btn--done{border-color:rgba(45,164,78,.35);color:var(--dsw-alias-state-success-primary,#1a7f37);background:rgba(45,164,78,.08)}\n.dq-export-menu{position:absolute;right:0;top:calc(100% + 6px);z-index:30;min-width:180px;padding:6px;background:var(--dsw-alias-bg-overlay,#fff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.16);display:flex;flex-direction:column;gap:2px}\n.dq-export-menu button{display:flex;align-items:center;justify-content:space-between;gap:16px;width:100%;border:0;border-radius:6px;padding:7px 8px;background:transparent;color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;text-align:left;cursor:pointer}\n.dq-export-menu button:hover,.dq-export-menu button:focus-visible{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06))}\n.dq-export-menu button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-1px}\n.dq-export-menu small{font-size:10.5px;font-weight:400;color:var(--dsw-alias-label-tertiary,#59636e);white-space:nowrap}\n/* Small-text tier system (\u8F6E\u6B21 41 \u6536\u655B): two whole-pixel tiers replace what used to be four values\n (11/11.5/12/12.5px) spaced only 0.5px apart \u2014 a gap browsers subpixel-round away, so it read as one\n size with extra maintenance cost, not an actual visual hierarchy. 11px = \"micro\" chrome the user\n scans rather than reads (dt-style stat/period labels, delta/status/coverage pills, share %, tags,\n the widget grip) \u2014 kept as-is. 12px = \"secondary text\" (section titles, hint/footnote/legend/sub\n prose, table cells, tooltip copy, alert/empty-state sentences, and filter/segmented-control button\n labels like .dq-window-btn/.dq-chart-switch-btn, which are the same control family and should share\n one size) \u2014 absorbed the old 11.5px and 12.5px selectors, all by rounding toward the nearer tier\n (11.5\u219212 grows slightly for legibility, safe because none of the affected containers are width-\n capped; 12.5\u219212 shrinks by 0.5px on two full-sentence blocks, imperceptible). Weight/color still do\n the job of separating titles from body text within the 12px tier, same as before this merge. Do not\n reintroduce a fractional-px value in this range; if a selector needs to stand apart from these two,\n it belongs in an already-established different tier (10px icon glyphs, 13px+ hero-adjacent text). */\n.dq-balance-grid{display:flex;flex-wrap:wrap;gap:12px 24px}\n.dq-stat{min-width:110px}\n.dq-stat-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e);margin-bottom:2px}\n.dq-stat-value{font-size:18px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-stat-value--ok{color:var(--dsw-alias-state-success-primary,#2da44e)}\n.dq-stat-value--bad{color:var(--dsw-alias-state-error-primary,#cf222e)}\n/* \u8F6E\u6B21 43: .dq-runway is a controlled <details> (see dashboard.tsx) \u2014 hover/toggle state\n drives the real open attribute now, so .dq-runway-detail only needs to react to [open];\n an unopened <details>'s non-summary children don't actually render in current Chromium no\n matter what display value author CSS gives them (:hover{display:block} alone is a dead end). */\n.dq-runway{position:relative}\n.dq-runway>summary{display:inline-block;list-style:none;border-radius:4px;cursor:pointer;text-decoration:underline dotted var(--dsw-alias-border-l2,rgba(0,0,0,.25));text-underline-offset:4px}\n.dq-runway>summary::-webkit-details-marker{display:none}\n.dq-runway>summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:3px}\n.dq-runway-detail{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;max-width:240px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:normal;font-size:12px;font-weight:500;line-height:1.5;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-runway[open] .dq-runway-detail{display:block}\n/* hero number for the balance, matching .dq-period-cost; relies on cascade order (declared after\n .dq-stat-value, same specificity) to win over the .dq-stat-value it's paired with in JSX \u2014 keep it below */\n/* \u8F6E\u6B21 47: .dq-remaining is a controlled <details> too now (see dashboard.tsx), same reason as\n .dq-runway above \u2014 an unopened <details>'s non-summary children don't actually render in current\n Chromium regardless of author display value, so the old :hover{display:flex} branch was dead code. */\n.dq-remaining{position:relative;font-size:22px;font-weight:680;letter-spacing:-.01em;line-height:1.25}\n.dq-remaining>summary{display:inline-block;list-style:none;border-radius:4px;cursor:pointer;text-decoration:underline dotted var(--dsw-alias-border-l2,rgba(0,0,0,.25));text-underline-offset:4px}\n.dq-remaining>summary::-webkit-details-marker{display:none}\n.dq-remaining>summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:3px}\n.dq-remaining-breakdown{display:none;position:absolute;top:calc(100% + 6px);left:0;z-index:20;flex-direction:column;gap:4px;padding:8px 10px;background:var(--dsw-alias-bg-overlay,#ffffff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.15);white-space:nowrap;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-remaining[open] .dq-remaining-breakdown{display:flex}\n.dq-remaining-granted{color:var(--dsw-alias-label-tertiary,#59636e);font-weight:400;font-size:11px}\n/* narrow screens: .dq-balance-grid wraps its 3 stats onto separate lines, so the absolutely-positioned\n .dq-remaining-breakdown/.dq-runway-detail popovers can float down over whichever stat now sits in the\n next row (e.g. \"\u4F59\u989D\u660E\u7EC6\" open covering \"\u9884\u8BA1\u53EF\u7528\"'s clickable summary) \u2014 force each stat onto its own\n full-width row and switch both popovers to normal in-flow layout so an open one pushes its own row\n taller instead of overlaying a sibling; desktop keeps the original hover-overlay behavior unchanged */\n@media (max-width:620px){\n .dq-balance-grid .dq-stat{flex:1 1 100%}\n .dq-remaining-breakdown,.dq-runway-detail{position:static;margin-top:6px;box-shadow:none;max-width:none;white-space:normal}\n}\n.dq-period-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0}\n.dq-period{min-width:0;padding:4px 16px}\n.dq-period:first-child{padding-left:0}\n.dq-period:last-child{padding-right:0}\n.dq-period+.dq-period{border-left:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n@media (max-width:620px){.dq-period-grid{grid-template-columns:1fr}.dq-period,.dq-period:first-child,.dq-period:last-child{padding:12px 0}.dq-period:first-child{padding-top:2px}.dq-period+.dq-period{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-left:0}}\n.dq-period-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:4px}\n.dq-period-label{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-period-cost{font-size:22px;font-weight:680;letter-spacing:-.01em;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;line-height:1.25}\n.dq-period-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);margin-top:2px;font-variant-numeric:tabular-nums;line-height:1.45}\n.dq-budget{margin-top:12px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-budget--unset{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-budget-action{border:0;background:transparent;padding:3px 4px;border-radius:5px;color:var(--dsw-alias-state-business-primary,#0969da);font:inherit;font-weight:600;white-space:nowrap;cursor:pointer}\n.dq-budget-action:hover{text-decoration:underline;text-underline-offset:3px}\n.dq-budget-action:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px}\n.dq-budget-head,.dq-budget-meta{display:flex;align-items:center;justify-content:space-between;gap:12px}\n.dq-budget-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-budget-amount{font-size:12px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-budget-track{height:8px;margin-top:8px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}\n.dq-budget-fill{width:100%;height:100%;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);transform-origin:left center;transition:transform .35s cubic-bezier(.22,1,.36,1)}\n.dq-budget--risk .dq-budget-fill{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-budget--over .dq-budget-fill{background:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-budget-meta{margin-top:6px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-budget--risk .dq-budget-meta span:last-child{color:var(--dsw-alias-state-warning-primary,#9a6700);font-weight:600}\n.dq-budget--over .dq-budget-meta{color:var(--dsw-alias-state-error-primary,#cf222e);font-weight:600}\n@media (max-width:620px){.dq-budget-head,.dq-budget-meta,.dq-budget--unset{align-items:flex-start;flex-direction:column;gap:4px}}\n.dq-delta{display:inline-flex;align-items:center;gap:3px;font-size:11px;font-weight:600;padding:1px 6px;border-radius:999px;white-space:nowrap;cursor:help;font-variant-numeric:tabular-nums}\n.dq-delta--up{color:var(--dsw-alias-state-warning-primary,#9a6700);background:rgba(191,135,0,.14)}\n.dq-delta--down{color:var(--dsw-alias-state-success-primary,#1a7f37);background:rgba(45,164,78,.14)}\n.dq-delta--flat,.dq-delta--new{color:var(--dsw-alias-label-tertiary,#59636e);background:rgba(120,130,150,.14);font-weight:500}\n.dq-delta-label{font-weight:400;opacity:.8}\n.dq-pricing{margin-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));padding-top:10px}\n.dq-pricing-summary{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);cursor:pointer;list-style:none;width:fit-content;border-radius:6px;padding:2px 4px;margin-left:-4px}\n.dq-pricing-summary::-webkit-details-marker{display:none}\n.dq-pricing-summary::before{content:\"\u25B8\";display:inline-block;font-size:10px;opacity:.7;transition:transform .15s ease}\n.dq-pricing[open] .dq-pricing-summary::before{transform:rotate(90deg)}\n.dq-pricing-summary:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-pricing-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-pricing-now{font-size:11px;font-weight:600;padding:1px 7px;border-radius:999px;color:var(--dsw-alias-label-tertiary,#59636e);background:rgba(120,130,150,.14)}\n.dq-pricing-now--peak{color:var(--dsw-alias-state-warning-primary,#9a6700);background:rgba(191,135,0,.14)}\n.dq-pricing-body{margin-top:10px;overflow-x:auto}\n.dq-pricing-table{border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums;min-width:100%}\n.dq-pricing-table th{text-align:left;font-weight:500;color:var(--dsw-alias-label-tertiary,#59636e);padding:4px 12px 4px 0;white-space:nowrap;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-pricing-table td{padding:4px 12px 4px 0;color:var(--dsw-alias-label-primary,#1f2328);white-space:nowrap;vertical-align:top}\n.dq-pricing-table tbody tr+tr td{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.05))}\n.dq-pricing-foot{margin:8px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-coverage{margin-top:16px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-coverage-summary{display:flex;align-items:center;flex-wrap:wrap;gap:6px 9px;cursor:pointer;list-style:none;width:fit-content;max-width:100%;margin-left:-4px;padding:3px 4px;border-radius:6px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-coverage-summary::-webkit-details-marker{display:none}\n.dq-coverage-summary::before{content:\"\u25B8\";display:inline-block;font-size:10px;opacity:.7;transition:transform .15s ease}\n.dq-coverage[open] .dq-coverage-summary::before{transform:rotate(90deg)}\n.dq-coverage-summary:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-coverage-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-coverage-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-coverage-status{padding:1px 7px;border-radius:999px;background:rgba(45,164,78,.14);color:var(--dsw-alias-state-success-primary,#1a7f37);font-size:11px;font-weight:600}\n.dq-coverage--warn .dq-coverage-status{background:rgba(191,135,0,.14);color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-coverage-brief{font-size:12px;font-variant-numeric:tabular-nums}\n.dq-coverage-body{margin-top:10px;padding:11px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}\n.dq-coverage-metrics{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px 20px;margin:0}\n.dq-coverage-metrics div{min-width:0}\n.dq-coverage-metrics dt{font-size:11px;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-coverage-metrics dd{margin:3px 0 0;font-size:13px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-coverage-range,.dq-coverage-note,.dq-coverage-warning{margin:9px 0 0;font-size:12px;line-height:1.55;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-coverage-range{font-variant-numeric:tabular-nums}\n.dq-coverage-warning{padding:8px 10px;border-radius:8px;background:rgba(191,135,0,.10);color:var(--dsw-alias-state-warning-primary,#7a5200)}\n@media (max-width:620px){.dq-coverage-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.dq-coverage-brief{flex-basis:100%;margin-left:19px}}\n.dq-peak-track{height:8px;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85;overflow:hidden}\n.dq-peak-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#bf8700);transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-peak-legend{display:flex;flex-wrap:wrap;gap:6px 20px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-peak-legend span{display:inline-flex;align-items:center;gap:6px}\n.dq-legend-swatch--peak{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-legend-swatch--offpeak{background:var(--dsw-alias-state-success-primary,#2da44e);opacity:.85}\n.dq-peak-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-top:14px}\n.dq-peak-delta{font-size:11px;font-weight:500;margin-left:8px;color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-peak-delta--save{color:var(--dsw-alias-state-success-primary,#1a7f37)}\n.dq-peak-foot{margin:10px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-cache-figures{display:flex;flex-wrap:wrap;gap:12px 32px;margin-bottom:12px}\n.dq-muted-value{color:var(--dsw-alias-label-secondary,#59636e)!important;font-weight:550}\n.dq-stat-value--warn{color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-cache-track{height:8px;border-radius:999px;background:var(--dsw-alias-state-warning-primary,#bf8700);overflow:hidden}\n.dq-cache-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-success-primary,#2da44e);transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-cache-legend{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-cache-legend span{display:inline-flex;align-items:center;gap:6px}\n.dq-legend-swatch--hit{background:var(--dsw-alias-state-success-primary,#2da44e)}\n.dq-legend-swatch--miss{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-cache-foot{margin:10px 0 0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-sessions{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:14px}\n.dq-session{display:flex;flex-direction:column;gap:5px;min-width:0}\n.dq-session-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}\n.dq-session-title{font-size:13px;font-weight:550;color:var(--dsw-alias-label-primary,#1f2328);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dq-session-cost{font-size:14px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;flex:none}\n.dq-session-track{height:6px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}\n.dq-session-fill{height:100%;border-radius:999px;background:var(--dsw-alias-state-business-primary,#4176e6);transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-session-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;line-height:1.5}\n/* The \u300C\u5F53\u524D\u4F1A\u8BDD\u6D88\u8017\u300Dcard uses this class on a bare <p> (no flex-gap parent\n to lean on, unlike the .dq-session usage above), so it needs its own\n explicit spacing rather than falling back to the browser's implicit <p>\n margin \u2014 this project always declares spacing explicitly. */\np.dq-session-sub{margin:8px 0}\n.dq-session-foot{margin:12px 0 0;font-size:12px;color:var(--dsw-alias-label-tertiary,#59636e)}\n/* \u8F6E\u6B21 46: each ranking row is now a <details> whose <summary> wraps the existing\n head/track/sub trio unchanged (in a flex-column body div) plus a leading chevron \u2014\n same disclosure grammar as .dq-pricing/.dq-coverage above (hidden native marker,\n rotating glyph, :focus-visible ring), just applied to a multi-line summary instead\n of a single line. .dq-session-details needs its own min-width:0 because it is now\n the flex item inside .dq-session that the ellipsis chain must shrink through. */\n.dq-session-details{min-width:0}\n.dq-session-details>summary{list-style:none;cursor:pointer}\n.dq-session-details>summary::-webkit-details-marker{display:none}\n.dq-session-summary{display:flex;align-items:flex-start;gap:8px;border-radius:8px;padding:4px;margin:-4px}\n.dq-session-summary:hover{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.05))}\n.dq-session-summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-session-chevron{flex:none;width:12px;margin-top:4px;font-size:10px;line-height:1;color:var(--dsw-alias-label-tertiary,#59636e);transition:transform .15s ease}\n.dq-session-details[open] .dq-session-chevron{transform:rotate(90deg)}\n.dq-session-summary-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:5px}\n/* Expanded body reuses the same shaded-inset language as .dq-coverage-body (10px\n gap from the summary, 11px/12px padding, 10px radius, bg-layer-2) rather than\n inventing a new \"nested panel\" look. SessionUsageCard renders inside unchanged. */\n.dq-session-expand{margin-top:10px;padding:11px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.06))}\n.dq-rank{display:flex;flex-direction:column;gap:14px}\n.dq-rank-row{display:flex;flex-direction:column;gap:5px;min-width:0}\n.dq-rank-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px}\n.dq-rank-name{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:550;color:var(--dsw-alias-label-primary,#1f2328);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dq-rank-cost{display:inline-flex;align-items:baseline;gap:8px;font-size:14px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums;flex:none}\n.dq-rank-share{font-size:11px;font-weight:500;color:var(--dsw-alias-label-tertiary,#59636e);min-width:30px;text-align:right}\n.dq-rank-track{height:6px;border-radius:999px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.12));overflow:hidden}\n.dq-rank-fill{height:100%;border-radius:999px;transition:width .35s cubic-bezier(.22,1,.36,1)}\n.dq-rank-sub{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums;line-height:1.5}\n.dq-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:10px;font-size:12px;line-height:1.6;color:var(--dsw-alias-state-warning-primary,#7a5200);background:rgba(191,135,0,.12);border:1px solid rgba(191,135,0,.35)}\n.dq-alert-icon{flex:none;width:18px;height:18px;border-radius:50%;background:var(--dsw-alias-state-warning-primary,#bf8700);color:#fff;font-weight:700;font-size:12px;line-height:18px;text-align:center}\n.dq-alert strong{font-weight:650}\n.dq-alert-link{margin-left:8px;color:inherit;text-decoration:underline;text-underline-offset:2px;white-space:nowrap}\n.dq-setting{display:flex;align-items:flex-start;gap:12px;margin-top:14px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-setting-label{font-size:13px;color:var(--dsw-alias-label-primary,#1f2328);padding-top:6px;flex:none}\n.dq-setting-control{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}\n.dq-setting-hint{font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);line-height:1.5}\n/* Page footer: the package name + version, injected at build time from\n package.json (see build.mjs) so it can never drift from the release. */\n.dq-footer{margin:18px 0 0;font-size:12px;text-align:center;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-number{width:82px;padding:5px 8px;font-size:13px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.16));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font-variant-numeric:tabular-nums}\n.dq-number:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;border-color:transparent}\n.dq-links-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2328);margin:0 0 8px}\n.dq-links{display:flex;flex-wrap:wrap;gap:8px}\n.dq-link{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));border-radius:8px;background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-state-business-primary,#0969da);text-decoration:none;font-size:13px;font-weight:500;cursor:pointer}\n.dq-link:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.04))}\n/* .dq-toggle is the first settings control after the links row (\u8F6E\u6B21 39 merge) \u2014 its own\n border-top separates the \"\u5FEB\u6377\u5165\u53E3\" links (originally the standalone \u5B98\u65B9\u5E73\u53F0 card, renamed\n in \u8F6E\u6B21 39 fix-round-1 to read as part of \u8BBE\u7F6E's own navigation area) from \u8BBE\u7F6E controls\n inside the merged card, same 14px/14px/border-l1 recipe as .dq-setting so the two\n sub-sections read at equal weight. */\n.dq-toggle{display:flex;align-items:center;gap:10px;cursor:pointer;font-size:13px;margin-top:14px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}\n.dq-toggle input{width:16px;height:16px;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer}\n.dq-toggle-hint{margin:5px 0 0 26px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-widget-tabs{align-items:flex-start;flex-direction:column;gap:7px}\n.dq-widget-tab-list{display:flex;align-items:center;flex-wrap:wrap;gap:6px 14px}\n.dq-widget-tab{display:inline-flex;align-items:center;gap:7px;min-height:28px;color:var(--dsw-alias-label-primary,#1f2328);font-size:12px;cursor:pointer}\n.dq-widget-tab input{width:14px;height:14px;margin:0;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer;flex:none}\n.dq-toggle input:focus-visible,.dq-model-item input:focus-visible,.dq-widget-tab input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-link:focus-visible,.dq-model-btn:focus-visible,.dq-chart-switch-btn:focus-visible,.dq-export-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-usage-totals{display:flex;flex-wrap:wrap;gap:12px 24px;margin-bottom:16px}\n/* value itself (number+unit, e.g. \"9999.9\u4E07\") must never break across lines inside its own column \u2014\n nowrap+ellipsis is a defensive floor for all widths; the actual fix for the realistic 320px 2-col\n case is the font-size drop below, which keeps every measured stress value comfortably under the\n 83px column width without ever hitting the ellipsis fallback (see round 38 PROGRESS entry) */\n.dq-usage-totals .dq-stat-value{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.dq-vision-share{display:inline-block;margin-left:6px;font-size:11px;font-weight:550;color:var(--dsw-alias-label-tertiary,#59636e)}\n.dq-vision-sessions{margin-top:12px;display:flex;flex-direction:column;gap:8px;min-width:0}\n/* The vision card's session rows reuse .dq-session-head, but \u2014 unlike the\n ranking card's rows \u2014 they sit directly under .dq-session with no\n .dq-session-summary-body wrapper to carry min-width:0. Without the chain\n below, a long session title (nowrap) sets the row's min-content width and\n can stretch the card past its siblings / the container. */\n.dq-vision-sessions .dq-session{min-width:0}\n.dq-vision-sessions .dq-session-head{min-width:0}\n.dq-vision-sessions .dq-session-title{flex:1 1 auto;min-width:0}\n@media (max-width:620px){.dq-usage-totals{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 24px}.dq-usage-totals .dq-stat{min-width:0}.dq-usage-totals .dq-stat-value{font-size:13px}}\n.dq-chart-peak{margin-left:10px;font-size:11px;font-weight:400;color:var(--dsw-alias-label-tertiary,#59636e);font-variant-numeric:tabular-nums}\n.dq-chart-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#59636e);margin:16px 0 8px}\n.dq-chart-title:first-of-type{margin-top:0}\n.dq-chart-block-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:16px 0 8px}\n.dq-chart-block-head .dq-chart-title{margin:0}\n.dq-chart-controls{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex:none;flex-wrap:wrap}\n@media (max-width:620px){.dq-chart-block-head{align-items:flex-start;flex-direction:column}.dq-chart-controls{width:100%}}\n.dq-model-picker{position:relative}\n.dq-model-btn{border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:4px 10px;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:6px}\n.dq-model-btn:hover{border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}\n.dq-model-btn::after{content:\"\u25BE\";font-size:10px;opacity:.7}\n.dq-model-menu{position:absolute;right:0;top:calc(100% + 6px);z-index:30;min-width:190px;max-height:280px;overflow-y:auto;background:var(--dsw-alias-bg-overlay,#fff);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.12));border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.16);padding:6px;display:flex;flex-direction:column;gap:2px}\n.dq-model-item{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:6px;font-size:12px;cursor:pointer;color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-model-item:hover{background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06))}\n.dq-model-item input{width:14px;height:14px;accent-color:var(--dsw-alias-state-business-primary,#0969da);cursor:pointer;flex:none}\n.dq-model-item span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dq-model-item--all{border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));padding-bottom:8px;margin-bottom:2px;border-radius:6px}\n.dq-model-tag{color:var(--dsw-alias-label-tertiary,#59636e);font-size:11px;flex:none}\n.dq-bar-group{display:flex;flex-direction:column-reverse;width:100%}\n.dq-bar-group .dq-bar{flex:0 0 auto;width:100%;border-radius:0}\n.dq-bar-group .dq-bar:first-child{border-radius:0 0 2px 2px}\n.dq-bar-group .dq-bar:last-child{border-radius:2px 2px 0 0}\n.dq-bar-group .dq-bar:not(:last-child){box-shadow:inset 0 1px 0 rgba(0,0,0,.18)}\n.dq-legend{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:8px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-legend-item{display:inline-flex;align-items:center;gap:6px}\n.dq-legend-swatch{width:10px;height:10px;border-radius:3px;flex:none}\n.dq-chart-switch{display:inline-flex;align-items:center;gap:2px;padding:2px;background:var(--dsw-alias-bg-layer-2,rgba(0,0,0,.06));border-radius:999px;flex:none}\n.dq-chart-switch-btn{border:none;background:transparent;color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1;padding:5px 12px;border-radius:999px;cursor:pointer;transition:background .15s ease,color .15s ease}\n.dq-chart-switch-btn:hover{color:var(--dsw-alias-label-primary,#1f2328)}\n.dq-chart-switch-btn--on{background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1f2328);box-shadow:0 1px 3px rgba(0,0,0,.14)}\n.dq-chart-wrap{position:relative}\n.dq-bars-scroll{overflow-x:auto;overflow-y:hidden;padding-bottom:2px}\n.dq-tip{position:absolute;transform:translate(-50%,14px);z-index:40;pointer-events:none;background:var(--dsw-alias-bg-inverse,#1f2328);color:var(--dsw-alias-label-inverse,#fff);border-radius:8px;padding:7px 10px;font-size:12px;line-height:1.55;white-space:nowrap;box-shadow:0 6px 20px rgba(0,0,0,.28);font-variant-numeric:tabular-nums}\n.dq-tip--above{transform:translate(-50%,calc(-100% - 8px))}\n.dq-tip-head{font-weight:650;margin-bottom:2px}\n.dq-tip-row{opacity:.85}\n.dq-bars{display:flex;align-items:flex-end;gap:2px;width:100%}\n.dq-bar-col{flex:1 1 0;min-width:0;display:flex;flex-direction:column;justify-content:flex-end;align-items:stretch}\n.dq-bar{background:var(--dsw-alias-state-business-primary,#4176e6);border-radius:2px 2px 0 0;min-height:1px;transition:filter .12s ease}\n.dq-bar-col{cursor:default}\n.dq-bar-col,.dq-bar,.dq-heat-cell{scroll-margin-block-start:24px;scroll-margin-block-end:160px}\n.dq-bar-col:hover .dq-bar{filter:brightness(1.18) saturate(1.1)}\n.dq-bar-col:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-2px;border-radius:3px}\n.dq-bar:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;filter:brightness(1.18) saturate(1.1);position:relative;z-index:2}\n.dq-bar-col--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:-2px;border-radius:3px}\n.dq-bar-col--pinned .dq-bar{filter:brightness(1.18) saturate(1.1)}\n.dq-bar--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;filter:brightness(1.18) saturate(1.1);position:relative;z-index:2}\n.dq-heat-cell:not(.dq-heat-cell--pad):hover{outline:1.5px solid var(--dsw-alias-label-primary,#1f2328);outline-offset:1px}\n.dq-heat-cell:not(.dq-heat-cell--pad):focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}\n.dq-heat-cell--pinned{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:1px;position:relative;z-index:2}\n.dq-bar-label{font-size:9px;color:var(--dsw-alias-label-tertiary,#59636e);text-align:center;margin-top:3px;overflow:visible;white-space:nowrap}\n.dq-heat{display:flex;flex-direction:column;gap:8px;max-width:100%}\n.dq-heat-scroll{overflow-x:auto;padding-bottom:2px}\n.dq-heatmap{display:flex;gap:3px}\n.dq-heat-week{display:flex;flex:1 1 0;min-width:6px;flex-direction:column;gap:3px}\n.dq-heat-cell{width:100%;aspect-ratio:1/1;border-radius:3px;flex:none}\n.dq-heat-cell--pad{background:transparent}\n.dq-heat-months{display:flex;gap:3px;margin-top:4px}\n.dq-heat-month{flex:1 1 0;min-width:6px;font-size:10px;line-height:14px;color:var(--dsw-alias-label-tertiary,#59636e);white-space:nowrap;overflow:visible}\n.dq-heat-key{width:12px;height:12px;border-radius:2px;flex:none}\n.dq-heat-scale{display:flex;align-items:center;gap:3px;font-size:10px;color:var(--dsw-alias-label-tertiary,#59636e);align-self:flex-end}\n.dq-heat-scale>span:first-child{margin-right:2px}\n.dq-heat-scale>span:last-child{margin-left:2px}\n.dq-muted{color:var(--dsw-alias-label-secondary,#59636e)}\n.dq-error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-warn{color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-crash{margin:10px 0 14px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2,rgba(120,130,150,.10));border-radius:8px;font-size:12px;line-height:1.5;color:var(--dsw-alias-state-error-primary,#cf222e);white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}\n.dq-empty{color:var(--dsw-alias-label-secondary,#59636e);font-size:12px;line-height:1.6;padding:6px 0}\n/* Error-state modifier for a card body that has nothing else to show (mirrors the .dq-stat-value--ok/\n --bad/--warn pattern: same layout as .dq-empty, declared after it so this color wins at equal\n specificity). Used by the current-session card when its own fetch failed and there is no prior\n data to fall back to \u2014 the status-row .dq-error/.dq-warn spans are for the account-wide balance/\n usage fetch, this is the per-card equivalent. */\n.dq-empty--error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-skel{background:linear-gradient(90deg,rgba(125,135,155,.10) 25%,rgba(125,135,155,.20) 37%,rgba(125,135,155,.10) 63%);background-size:400% 100%;border-radius:5px;animation:dq-shimmer 1.5s ease-in-out infinite}\n.dq-stat .dq-skel+.dq-skel{margin-top:6px}\n.dq-skel-bars{display:flex;align-items:flex-end;gap:2px;width:100%;height:120px;margin-top:10px}\n.dq-skel-bar{flex:1 1 0;min-width:0;border-radius:2px 2px 0 0}\n@keyframes dq-shimmer{0%{background-position:100% 50%}100%{background-position:0 50%}}\n.dq-status-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:20px}\n.dq-status-messages{display:flex;flex-direction:column;align-items:flex-start;gap:2px;min-width:0}\n.dq-status-row .dq-warn,.dq-status-row .dq-error{font-size:12px;line-height:1.45}\n.dq-sync{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}\n.dq-sync-dot{width:7px;height:7px;border-radius:50%;flex:none;background:var(--dsw-alias-label-tertiary,#8c959f)}\n.dq-sync--fresh .dq-sync-dot{background:var(--dsw-alias-state-success-primary,#2da44e)}\n.dq-sync--syncing .dq-sync-dot{background:var(--dsw-alias-state-business-primary,#4176e6);animation:dq-sync-pulse 1.2s ease-in-out infinite}\n.dq-sync--fallback{color:var(--dsw-alias-state-warning-primary,#9a6700)}\n.dq-sync--fallback .dq-sync-dot{background:var(--dsw-alias-state-warning-primary,#bf8700)}\n.dq-sync--error{color:var(--dsw-alias-state-error-primary,#cf222e)}\n.dq-sync--error .dq-sync-dot{background:var(--dsw-alias-state-error-primary,#cf222e)}\n@keyframes dq-sync-pulse{50%{opacity:.35;transform:scale(.8)}}\n.dq-refresh-btn{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.12));background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#59636e);border-radius:8px;padding:5px 10px;font-size:12px;cursor:pointer;transition:border-color .15s ease,color .15s ease}\n.dq-refresh-btn:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary,#0969da);color:var(--dsw-alias-state-business-primary,#0969da)}\n.dq-refresh-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#0969da);outline-offset:2px}\n.dq-refresh-btn:disabled{cursor:default;opacity:.6}\n.dq-refresh-icon{display:inline-block;line-height:1}\n.dq-refresh-icon--spin{animation:dq-spin .9s linear infinite}\n@keyframes dq-spin{to{transform:rotate(360deg)}}\n@media (prefers-reduced-motion:reduce){\n .dsh-quota-root,.dsh-quota-root *,\n .dsh-quota-root *::before,.dsh-quota-root *::after,\n .dq-message-cost,.dq-message-cost::after,\n .dq-balance,.dq-balance *,\n .dq-balance *::before,.dq-balance *::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}\n}\n";
@@ -6,13 +6,10 @@
6
6
  * sidebar collapse) instead of stranding itself at stale pixel coordinates.
7
7
  */
8
8
  import { type ReactElement } from 'react';
9
+ import { type SessionListStateFace } from './sessions.ts';
9
10
  import type { ConversationViewsSource } from './views.ts';
10
- interface SessionListState {
11
- current?: string;
12
- }
13
- export type UseSessionsHook = <T>(selector: (state: SessionListState) => T) => T;
11
+ export type UseSessionsHook = <T>(selector: (state: SessionListStateFace) => T) => T;
14
12
  export declare function QuotaWidget(props: {
15
13
  useSessions: UseSessionsHook;
16
14
  views: ConversationViewsSource;
17
15
  }): ReactElement | null;
18
- export {};
@@ -23,10 +23,37 @@ export interface CredentialsFace {
23
23
  source: string;
24
24
  } | undefined>;
25
25
  }
26
+ /** One Platform wallet as returned by `deepseekAccount.getBalance`. */
27
+ export interface AccountWalletFace {
28
+ currency?: string;
29
+ balance?: string;
30
+ }
31
+ /** Balance outcome of `deepseekAccount.getBalance`: `ready` carries the
32
+ * recharge (`value`) and granted (`bonusWallets`) wallets, `failed` means the
33
+ * Platform query was rejected, `null` (the whole result) means signed out. */
34
+ export interface AccountBalanceFace {
35
+ status?: string;
36
+ value?: readonly AccountWalletFace[];
37
+ bonusWallets?: readonly AccountWalletFace[];
38
+ }
39
+ /** Identity the account service turns into Platform request headers. */
40
+ export interface AccountClientFace {
41
+ version: string;
42
+ locale: string;
43
+ timezoneOffsetSeconds: number;
44
+ }
45
+ /** Structural face of the host `deepseekAccount` service. Optional: hosts
46
+ * that only do API-key auth never mount the account plugin. */
47
+ export interface DeepSeekAccountFace {
48
+ getBalance(client: AccountClientFace): Promise<AccountBalanceFace | null>;
49
+ }
26
50
  /** Structural face of the `sessionPersistence` service. */
27
51
  export interface SessionHeaderFace {
28
52
  id?: string;
29
53
  sessionId?: string;
54
+ /** Handle-era `list()` returns snapshots; the header (and its id) rides
55
+ * one level deeper at `snapshot.header.id` instead of at the entry top. */
56
+ header?: SessionHeaderFace;
30
57
  }
31
58
  export interface TokenUsageFace {
32
59
  inputTokens?: number;
@@ -34,6 +61,25 @@ export interface TokenUsageFace {
34
61
  cacheReadTokens?: number;
35
62
  reasoningTokens?: number;
36
63
  }
64
+ /** Structural face of an `image` content block's attachment ref — only the
65
+ * fields the image-cost fold reads. Width/height drive the official token
66
+ * estimate; bytes is reported raw. */
67
+ export interface ImageAttachmentFace {
68
+ width?: number;
69
+ height?: number;
70
+ bytes?: number;
71
+ name?: string;
72
+ mediaType?: string;
73
+ }
74
+ /** Structural face of one content block, recursive to cover image blocks
75
+ * nested inside tool-result blocks (tool screenshots). */
76
+ export interface ContentBlockFace {
77
+ type?: string;
78
+ text?: string;
79
+ attachment?: ImageAttachmentFace;
80
+ toolCallId?: string;
81
+ content?: ContentBlockFace[];
82
+ }
37
83
  export interface SessionEventFace {
38
84
  type?: string;
39
85
  time?: number;
@@ -51,20 +97,37 @@ export interface SessionEventFace {
51
97
  step?: number;
52
98
  message?: {
53
99
  id?: string;
100
+ content?: ContentBlockFace[];
54
101
  };
55
102
  /** Payload of a `session/title` event. */
56
103
  title?: string;
104
+ /** Payload of a `user/message` event: the user message record itself,
105
+ * whose content blocks may carry `image` blocks. */
106
+ content?: ContentBlockFace[];
57
107
  };
58
108
  }
109
+ /** One open read channel onto a session log (`SessionPersistence.open`). */
110
+ export interface SessionHandleFace {
111
+ read(offset?: number, length?: number): Promise<{
112
+ events?: readonly SessionEventFace[];
113
+ }>;
114
+ close(): Promise<void>;
115
+ }
59
116
  export interface SessionPersistenceFace {
60
117
  list(): Promise<SessionHeaderFace[]>;
61
- readFrom(id: string, fromSeq: number): Promise<{
118
+ /** Legacy hosts: whole-log read from a seq. Removed in handle-era DSH. */
119
+ readFrom?(id: string, fromSeq: number): Promise<{
62
120
  events?: SessionEventFace[];
63
121
  }>;
122
+ /** Handle-era hosts: per-session read handle, preferred when present. */
123
+ open?(id: string, access: 'read'): Promise<SessionHandleFace>;
64
124
  }
65
125
  /** The host context this plugin's apply() receives. */
66
126
  export interface HostContext extends Context {
67
127
  webServer: WebServerFace;
68
128
  credentials: CredentialsFace;
69
129
  sessionPersistence: SessionPersistenceFace;
130
+ /** Present only when the host mounts DSH's DeepSeek account plugin. Read it
131
+ * through `ctx.get()` so API-key-only deployments keep loading. */
132
+ deepseekAccount?: DeepSeekAccountFace;
70
133
  }
@@ -98,24 +98,23 @@ export interface PricingRates {
98
98
  export interface PricingTier {
99
99
  model: string;
100
100
  peak: PricingRates;
101
- /** Null while flat pricing is in effect (before the peak/off-peak switch). */
102
- offPeak: PricingRates | null;
101
+ offPeak: PricingRates;
103
102
  }
104
103
  /** What the cost column was computed with, so the estimate can be audited. */
105
104
  export interface PricingInfo {
106
105
  currency: string;
107
- /** Date peak/off-peak pricing takes effect. */
106
+ /** Date peak/off-peak pricing took effect; earlier usage is costed at the
107
+ * flat rates that were actually charged then. */
108
108
  switchDate: string;
109
- splitActive: boolean;
110
109
  inPeakNow: boolean;
111
110
  peakWindows: string[];
112
111
  tiers: PricingTier[];
113
112
  }
114
113
  /**
115
114
  * How usage divides between DeepSeek's peak and off-peak windows, plus the
116
- * same usage re-priced two ways — so the 2026-08-17 change can be answered
117
- * before it lands ("what will this cost me") and after ("what would shifting
118
- * off-peak save me").
115
+ * same usage re-priced as if everything had landed off-peak — answering
116
+ * "what would shifting batch work off-peak save me" under the peak/off-peak
117
+ * table in effect since 2026-08-17.
119
118
  */
120
119
  export interface PeakSplit {
121
120
  peak: PeriodUsage;
@@ -136,6 +135,39 @@ export interface SessionCost {
136
135
  /** Epoch ms of the last usage record in this session. */
137
136
  lastActive: number;
138
137
  }
138
+ /**
139
+ * Multimodal (image) usage, folded from the same log replay: image blocks in
140
+ * user messages and tool results are counted and sized, then estimated with
141
+ * the official resizing rule (see `estimateImageTokens`). Each image is
142
+ * attributed to the next usage record — the model call whose prompt carried
143
+ * it — and priced at that call's input (cache-miss) rate, so the share answer
144
+ * "how much of the bill came from pictures" has a real number.
145
+ */
146
+ export interface VisionStats {
147
+ /** Image blocks counted in the scope. */
148
+ images: number;
149
+ /** Estimated image tokens under the official conversion rule. */
150
+ imageTokens: number;
151
+ /** Estimated CNY cost of those image tokens at the consuming calls' rates. */
152
+ cost: number;
153
+ /** Total image payload bytes when the attachment refs carried a size. */
154
+ bytes: number;
155
+ }
156
+ /** One day's image usage for the per-window / per-day series. */
157
+ export interface VisionDayPoint {
158
+ date: string;
159
+ images: number;
160
+ imageTokens: number;
161
+ cost: number;
162
+ }
163
+ /** One session's image usage, ranked for "which run kept sending pictures". */
164
+ export interface VisionSession {
165
+ id: string;
166
+ title: string;
167
+ images: number;
168
+ imageTokens: number;
169
+ cost: number;
170
+ }
139
171
  /**
140
172
  * What the local log replay could actually inspect. Totals can otherwise look
141
173
  * authoritative even when one corrupt session was skipped, so this metadata
@@ -163,11 +195,11 @@ export type UsageWindowDays = (typeof USAGE_WINDOW_DAYS)[number];
163
195
  * Session ids observed in this codebase (host-issued, e.g.
164
196
  * `session-484a1c14-c6fe-4d6a-abfd-a2d8d2f664d5`) are always plain
165
197
  * ASCII-safe tokens — no documented format contract exists for them outside
166
- * this repo (`SessionPersistenceFace`/`readFrom` is implemented by the DSH
167
- * host, not here), so this is a defensive charset whitelist rather than an
198
+ * this repo (`SessionPersistenceFace` is implemented by the DSH host, not
199
+ * here), so this is a defensive charset whitelist rather than an
168
200
  * exact-format check: letters, digits, `-`, `_` only, with a generous length
169
201
  * cap. It exists specifically to stop path-separator / `..` / null-byte
170
- * payloads from ever reaching `persistence.readFrom(id, 0)` — `id` on the
202
+ * payloads from ever reaching the session-log read — `id` on the
171
203
  * `/session` route arrives verbatim from the browser-controlled `?id=` query
172
204
  * parameter, unlike `fetchUsage()`'s session ids, which the host itself
173
205
  * enumerates via `persistence.list()`.
@@ -185,6 +217,10 @@ export interface UsageWindow {
185
217
  sessions: SessionCost[];
186
218
  /** All sessions that contributed usage inside this window. */
187
219
  sessionCount: number;
220
+ vision: VisionStats;
221
+ visionDaily: VisionDayPoint[];
222
+ /** Sessions that sent the most images inside this window, capped. */
223
+ visionSessions: VisionSession[];
188
224
  }
189
225
  export interface UsageData {
190
226
  daily: DailyUsage[];
@@ -201,6 +237,10 @@ export interface UsageData {
201
237
  sessionCount: number;
202
238
  coverage: UsageCoverage;
203
239
  windows: UsageWindow[];
240
+ vision: VisionStats;
241
+ visionDaily: VisionDayPoint[];
242
+ /** Sessions that sent the most images, capped — see SESSION_TOP_N. */
243
+ visionSessions: VisionSession[];
204
244
  }
205
245
  export interface UsageResponse {
206
246
  ok: boolean;
@@ -1,24 +1,53 @@
1
1
  /**
2
2
  * DeepSeek API price table and cost estimation.
3
3
  *
4
- * Two things the previous flat constant got wrong: every model was billed at
5
- * deepseek-v4-pro rates (flash is ~3x cheaper), and the 2026-08-17 switch to
6
- * peak/off-peak pricing was not modelled at all.
4
+ * Every record is priced with the table that was in force at the moment it was
5
+ * written — CNY per 1M tokens, from DeepSeek's official pricing page:
7
6
  *
8
- * Rates are CNY per 1M tokens, from DeepSeek's 2026-08-13 price announcement.
9
- * Peak window is Beijing time 09:00–12:00 and 14:00–18:00; off-peak is half of
10
- * peak. Usage recorded before the switch is still costed at the old flat rates,
11
- * so historical days keep the price that was actually charged.
7
+ * - until 2026-08-17 00:00 Beijing: one flat price per model;
8
+ * - 2026-08-17 00:00: peak/off-peak pricing — peak windows 09:00–12:00 and
9
+ * 14:00–18:00 every day, off-peak exactly half of peak;
10
+ * - 2026-09-10 12:00: the V4.1-Flash price cut (flash 0.04/2/8 peak), and the
11
+ * peak windows narrow to Monday–Friday;
12
+ * - 2026-09-14 12:00: `deepseek-v4-pro` requests are served by V4.1-Flash and
13
+ * billed at Flash rates until a V4.1 Pro ships.
14
+ *
15
+ * `deepseek-v4-flash` and `deepseek-v4-flash-vision-exp` are legacy names for
16
+ * the model now published as `deepseek-flash`; both keep billing at Flash
17
+ * rates. Images cost tokens rather than a multiplier of their own: they are
18
+ * converted by size and priced together with the text tokens. Historical days
19
+ * therefore keep the price that was actually charged.
12
20
  *
13
21
  * Editing this table is the one place to touch when DeepSeek changes prices.
14
22
  */
15
23
  import type { PricingInfo, PricingRates } from './contract.ts';
16
- /** 2026-08-17 00:00 Beijing time (UTC+8), when peak/off-peak pricing starts. */
24
+ /** 2026-08-17 00:00 Beijing time (UTC+8), when peak/off-peak pricing took effect. */
17
25
  export declare const PEAK_PRICING_FROM_MS: number;
18
- type Tier = 'pro' | 'flash';
26
+ /** 2026-09-10 12:00 Beijing, when the V4.1-Flash prices and the weekday-only
27
+ * peak windows took effect. */
28
+ export declare const V41_FLASH_PRICING_FROM_MS: number;
29
+ /** 2026-09-14 12:00 Beijing, from when `deepseek-v4-pro` requests are routed
30
+ * to V4.1-Flash and billed at Flash rates. */
31
+ export declare const PRO_ROUTED_TO_FLASH_FROM_MS: number;
32
+ type Tier = 'pro' | 'flash' | 'vision';
19
33
  /** Which price tier a model name falls into; anything unrecognised is billed
20
- * as pro, the conservative (more expensive) guess. */
34
+ * as pro, the conservative (more expensive) guess. Vision is matched first:
35
+ * `deepseek-v4-flash-vision-exp` also contains "flash". */
21
36
  export declare function tierOf(model: string): Tier;
37
+ /**
38
+ * Image token conversion (DeepSeek official rule, Vision guide):
39
+ * - Images below roughly 384×384 pixels are scaled UP preserving aspect
40
+ * ratio; larger images are scaled DOWN so their pixel count is roughly that
41
+ * of an 800×800 image.
42
+ * - Tokens are proportional to the (resized) pixel count, capped at 384
43
+ * tokens per image, so 2000×2000 and 5000×5000 cost the same.
44
+ * - Each image is counted independently; `detail: low` caps at 512×512.
45
+ *
46
+ * The estimate is for the "how much came from images" breakdown; the exact
47
+ * provider count is already folded into the usage record's `inputTokens`.
48
+ * Returns tokens (possibly fractional) — 0 for missing/invalid dimensions.
49
+ */
50
+ export declare function estimateImageTokens(width: number, height: number): number;
22
51
  /** Whether a moment falls in a peak window, judged in Beijing time so the
23
52
  * estimate does not drift with the machine's timezone. */
24
53
  export declare function isPeak(timeMs: number): boolean;
@@ -32,12 +61,14 @@ export declare function cacheSavingOf(timeMs: number, model: string, cache: numb
32
61
  /** Estimated CNY cost of one usage record. */
33
62
  export declare function costOf(timeMs: number, model: string, input: number, cache: number, output: number): number;
34
63
  /**
35
- * The same record priced under the peak/off-peak table regardless of when it
36
- * happened — `forceOffPeak` prices it as if it had landed in an idle window.
37
- * Used to answer "what will the new pricing cost me" before the switch and
38
- * "what would shifting off-peak save" after it.
64
+ * The same record priced as if it had landed in an idle window — used to
65
+ * answer "what would shifting work off-peak save me". Prices come from the
66
+ * newest table, the one in force now: the question is about scheduling the
67
+ * next batch, not about re-pricing history.
39
68
  */
40
69
  export declare function costUnderPeakEra(timeMs: number, model: string, input: number, cache: number, output: number, forceOffPeak?: boolean): number;
41
- /** The price table to show the user, describing whatever is in effect now. */
70
+ /** The price table to show the user: the two models the official docs list,
71
+ * so the rows match the page they come from — legacy names, image tokens and
72
+ * the pro routing live in the small print next to the table. */
42
73
  export declare function pricingInfo(nowMs: number): PricingInfo;
43
74
  export {};
@@ -1,6 +1,6 @@
1
1
  import type { BalanceResponse, SessionUsageResponse, UsageResponse } from './contract.ts';
2
- import type { CredentialsFace, SessionPersistenceFace } from './context.ts';
3
- export declare function fetchBalance(credentials: CredentialsFace | undefined): Promise<BalanceResponse>;
2
+ import type { CredentialsFace, DeepSeekAccountFace, SessionPersistenceFace } from './context.ts';
3
+ export declare function fetchBalance(credentials: CredentialsFace | undefined, account?: DeepSeekAccountFace): Promise<BalanceResponse>;
4
4
  export declare function fetchUsage(persistence: SessionPersistenceFace | undefined, nowMs?: number): Promise<UsageResponse>;
5
5
  /**
6
6
  * Usage for exactly one session — the 「额度」tab's own conversation, as
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cassius0924/dsh-usage-dashboard",
3
- "version": "0.5.0",
4
- "description": "DeepSeek 额度与用量仪表盘 —— DSH (DeepSeek Harness) Web 插件:余额还能撑几天、今日/本月花了多少、哪个模型和哪个会话最贵、缓存省了多少,以及 2026-08-17 峰谷定价后的账单预估",
3
+ "version": "0.6.1",
4
+ "description": "DeepSeek 额度与用量仪表盘 —— DSH (DeepSeek Harness) Web 插件:余额还能撑几天、今日/本月花了多少、哪个模型和哪个会话最贵、图片/多模态用量、缓存省了多少、高峰挪到闲时能省多少",
5
5
  "keywords": [
6
6
  "deepseek",
7
7
  "dsh",