@threadcrumbs/common 0.1.5 → 0.1.6

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.
package/ui/rows.css ADDED
@@ -0,0 +1,403 @@
1
+ .term {
2
+ display: inline-block;
3
+ padding: 0;
4
+ background: none;
5
+ border: 0;
6
+ color: var(--text3);
7
+ font: 400 var(--fs-4) / 17px var(--cond);
8
+ text-align: left;
9
+ text-decoration: underline dotted transparent;
10
+ text-underline-offset: 3px;
11
+ white-space: nowrap;
12
+ cursor: help;
13
+ }
14
+ .term:hover,
15
+ .term[aria-expanded='true'] {
16
+ color: var(--text);
17
+ text-decoration-color: currentColor;
18
+ }
19
+ .i-wrap {
20
+ display: inline-flex;
21
+ flex: none;
22
+ border-radius: 50%;
23
+ cursor: help;
24
+ }
25
+ .i-wrap:empty {
26
+ display: none;
27
+ }
28
+
29
+ .grp-h {
30
+ position: sticky;
31
+ top: 0;
32
+ z-index: 2;
33
+ display: flex;
34
+ align-items: center;
35
+ gap: 6px;
36
+ padding: 9px 12px 5px var(--gl);
37
+ background: var(--bg);
38
+ color: var(--text2);
39
+ font: 600 var(--fs-4) / 1.24 var(--cond);
40
+ letter-spacing: 0.05em;
41
+ text-transform: uppercase;
42
+ }
43
+ .grp-h[data-kind='repo'] {
44
+ font-size: var(--fs-5);
45
+ letter-spacing: 0;
46
+ text-transform: none;
47
+ }
48
+ .grp-h[data-k='you'] .g-l {
49
+ color: var(--lamp);
50
+ }
51
+ .grp-h .ico {
52
+ color: var(--text3);
53
+ }
54
+ .grp-h .n {
55
+ color: var(--text3);
56
+ font-weight: 500;
57
+ letter-spacing: 0;
58
+ font-variant-numeric: tabular-nums;
59
+ }
60
+ .grp-h .hint {
61
+ margin-left: auto;
62
+ color: var(--text3);
63
+ font: 400 var(--fs-4) / 1.04 var(--cond);
64
+ letter-spacing: 0;
65
+ text-transform: none;
66
+ white-space: nowrap;
67
+ }
68
+ .grp-h .hint kbd {
69
+ display: inline-block;
70
+ margin-right: 3px;
71
+ padding: 2px 4px;
72
+ border: 1px solid var(--line);
73
+ border-radius: 3px;
74
+ font: 500 var(--fs-3) / 1.04 var(--mono);
75
+ }
76
+ @media (hover: none) {
77
+ .grp-h .hint {
78
+ display: none;
79
+ }
80
+ }
81
+ .grp-h .g-cols {
82
+ display: none;
83
+ letter-spacing: 0;
84
+ text-transform: none;
85
+ }
86
+ .grp-t {
87
+ display: inline-flex;
88
+ flex: 0 1 auto;
89
+ align-items: flex-start;
90
+ gap: 6px;
91
+ min-width: 0;
92
+ margin-left: -4px;
93
+ padding: 2px 6px 2px 0;
94
+ background: none;
95
+ border: 0;
96
+ border-radius: 5px;
97
+ color: inherit;
98
+ font: inherit;
99
+ letter-spacing: inherit;
100
+ text-align: left;
101
+ text-transform: inherit;
102
+ cursor: pointer;
103
+ }
104
+ .grp-t > svg {
105
+ flex: none;
106
+ margin-top: 2px;
107
+ color: var(--text3);
108
+ transition: transform 0.14s;
109
+ }
110
+ .grp-t[aria-expanded='true'] > svg {
111
+ transform: rotate(90deg);
112
+ }
113
+ .grp-t:hover .g-c:last-child > span {
114
+ text-decoration: underline;
115
+ text-decoration-color: var(--text3);
116
+ text-underline-offset: 3px;
117
+ }
118
+ .grp-t:focus-visible {
119
+ outline: 2px solid var(--accent-text);
120
+ outline-offset: 1px;
121
+ }
122
+ section[data-shut] .grp-h {
123
+ padding-bottom: 7px;
124
+ border-bottom: 1px solid var(--line2);
125
+ }
126
+ .g-flow {
127
+ display: flex;
128
+ flex-wrap: wrap;
129
+ align-items: center;
130
+ gap: 2px 8px;
131
+ min-width: 0;
132
+ }
133
+ .g-l {
134
+ display: inline-flex;
135
+ align-items: center;
136
+ gap: 5px;
137
+ min-width: 0;
138
+ }
139
+ .g-flow > .g-l {
140
+ display: contents;
141
+ }
142
+ .g-c {
143
+ display: inline-flex;
144
+ flex: none;
145
+ align-items: center;
146
+ gap: 5px;
147
+ white-space: nowrap;
148
+ }
149
+ .g-tallies {
150
+ display: inline-flex;
151
+ align-items: center;
152
+ gap: 8px;
153
+ margin-left: 4px;
154
+ letter-spacing: 0;
155
+ text-transform: none;
156
+ }
157
+ .g-tally {
158
+ display: inline-flex;
159
+ align-items: center;
160
+ gap: 3px;
161
+ }
162
+ .g-tally .lamp,
163
+ .g-tally .lamp svg {
164
+ width: 13px;
165
+ height: 13px;
166
+ }
167
+ .g-tally b {
168
+ color: var(--text2);
169
+ font: 600 var(--fs-5) / 1.04 var(--cond);
170
+ }
171
+ .g-tally[data-st='asking'] b {
172
+ color: var(--lamp);
173
+ }
174
+ .g-tally[data-st='error'] b {
175
+ color: var(--err);
176
+ }
177
+ @media (pointer: coarse) {
178
+ .grp-t {
179
+ min-height: 36px;
180
+ padding-block: 6px;
181
+ }
182
+ }
183
+
184
+ .row {
185
+ position: relative;
186
+ }
187
+ .row-main {
188
+ display: grid;
189
+ grid-template-columns: 18px minmax(0, 1fr) auto;
190
+ column-gap: 7px;
191
+ row-gap: 1px;
192
+ width: 100%;
193
+ margin: 0;
194
+ padding: 8px 12px 10px var(--gl);
195
+ background: none;
196
+ border: 0;
197
+ border-radius: 0;
198
+ color: var(--text);
199
+ font: inherit;
200
+ text-align: left;
201
+ }
202
+ .row-main:hover {
203
+ background: var(--raise);
204
+ }
205
+ .row-main:focus-visible {
206
+ outline-offset: -2px;
207
+ border-radius: var(--r-3);
208
+ }
209
+ .row-main > .row-where {
210
+ grid-row: 1;
211
+ grid-column: 1 / -1;
212
+ }
213
+ .row-main > .lamp {
214
+ grid-row: 2;
215
+ grid-column: 1;
216
+ margin-top: 2px;
217
+ }
218
+ .row-main > .row-text {
219
+ grid-row: 2;
220
+ grid-column: 2;
221
+ }
222
+ .row-main > .nums {
223
+ grid-row: 2;
224
+ grid-column: 3;
225
+ }
226
+ .row-main > .row-why {
227
+ grid-row: 3;
228
+ grid-column: 2 / -1;
229
+ }
230
+ .row-main > .row-meta {
231
+ grid-row: 4;
232
+ grid-column: 2 / -1;
233
+ }
234
+ .row-main > .row-plan {
235
+ grid-row: 5;
236
+ grid-column: 2 / -1;
237
+ justify-self: start;
238
+ max-width: 100%;
239
+ }
240
+ .row-where {
241
+ display: flex;
242
+ align-items: center;
243
+ gap: 0 10px;
244
+ min-width: 0;
245
+ margin-bottom: 2px;
246
+ overflow: hidden;
247
+ color: var(--text3);
248
+ font: 400 var(--fs-4) / 1.43 var(--cond);
249
+ white-space: nowrap;
250
+ }
251
+ .row-where .w {
252
+ flex: 0 1 auto;
253
+ overflow: hidden;
254
+ }
255
+ .row-where .w > span {
256
+ overflow: hidden;
257
+ text-overflow: ellipsis;
258
+ white-space: nowrap;
259
+ }
260
+ .row-where .w-folder {
261
+ color: var(--text2);
262
+ font-weight: 400;
263
+ }
264
+ .row-where .ico,
265
+ .row-meta .ico {
266
+ width: 12px;
267
+ height: 12px;
268
+ }
269
+ .row-h {
270
+ flex: none;
271
+ width: auto;
272
+ height: 15px;
273
+ margin-left: auto;
274
+ padding: 0 3px;
275
+ border: 0;
276
+ color: var(--text3);
277
+ font: 500 var(--fs-2) / 16px var(--mono);
278
+ }
279
+ .row-text {
280
+ display: flex;
281
+ flex-direction: column;
282
+ gap: 1px;
283
+ min-width: 0;
284
+ }
285
+ .row-title {
286
+ font: 500 var(--fs-8) / 1.36 var(--sans);
287
+ overflow-wrap: anywhere;
288
+ }
289
+ .row[data-st='idle'] .row-title {
290
+ color: var(--text2);
291
+ font-weight: 400;
292
+ }
293
+ .row-sub {
294
+ display: flex;
295
+ flex-wrap: nowrap;
296
+ align-items: baseline;
297
+ gap: 6px;
298
+ min-width: 0;
299
+ overflow: hidden;
300
+ white-space: nowrap;
301
+ }
302
+ .row-now {
303
+ display: inline-flex;
304
+ flex: none;
305
+ align-items: baseline;
306
+ gap: 6px;
307
+ color: var(--text2);
308
+ font: 400 var(--fs-5) / 1.43 var(--cond);
309
+ }
310
+ .now-h {
311
+ color: var(--text2);
312
+ font-weight: 400;
313
+ }
314
+ .row[data-st='asking'] .now-h,
315
+ .row[data-st='error'] .now-h {
316
+ color: var(--st);
317
+ font-weight: 500;
318
+ }
319
+ .now-v {
320
+ color: var(--text3);
321
+ font-weight: 400;
322
+ }
323
+ .now-d {
324
+ flex: 0 1 auto;
325
+ min-width: 0;
326
+ overflow: hidden;
327
+ color: var(--text2);
328
+ font: 400 var(--fs-4) / 1.43 var(--mono);
329
+ text-overflow: ellipsis;
330
+ }
331
+ .now-d.plain {
332
+ font: 400 var(--fs-5) / 1.43 var(--cond);
333
+ }
334
+ .now-d.rich {
335
+ display: inline-flex;
336
+ align-items: baseline;
337
+ gap: 6px;
338
+ font: 400 var(--fs-7) / 1.51 var(--cond);
339
+ }
340
+ .nd-tool {
341
+ flex: none;
342
+ color: var(--text);
343
+ font: 600 var(--fs-7) / 1.51 var(--cond);
344
+ }
345
+ .nd-dur {
346
+ flex: none;
347
+ color: var(--text3);
348
+ font: 400 var(--fs-6) / 1.51 var(--cond);
349
+ }
350
+ .nd-t {
351
+ min-width: 0;
352
+ overflow: hidden;
353
+ color: var(--text2);
354
+ font: 400 var(--fs-6) / 1.51 var(--mono);
355
+ text-overflow: ellipsis;
356
+ }
357
+ .row-why {
358
+ display: -webkit-box;
359
+ overflow: hidden;
360
+ color: var(--text);
361
+ font: 400 var(--fs-5) / 1.38 var(--cond);
362
+ overflow-wrap: anywhere;
363
+ white-space: normal;
364
+ -webkit-box-orient: vertical;
365
+ -webkit-line-clamp: 2;
366
+ }
367
+ .row-why.mono {
368
+ font: 400 var(--fs-4) / 1.43 var(--mono);
369
+ }
370
+ .row-meta {
371
+ display: none;
372
+ color: var(--text3);
373
+ font: 400 var(--fs-4) / 1.43 var(--cond);
374
+ overflow-wrap: anywhere;
375
+ }
376
+ .row-meta > span {
377
+ display: inline-flex;
378
+ align-items: center;
379
+ gap: 4px;
380
+ margin-right: 10px;
381
+ }
382
+ .nums {
383
+ display: grid;
384
+ justify-items: end;
385
+ gap: 1px;
386
+ min-width: 0;
387
+ font: 500 var(--fs-6) / 1.43 var(--cond);
388
+ font-variant-numeric: tabular-nums;
389
+ }
390
+ .nums .xtra {
391
+ display: none;
392
+ color: var(--text3);
393
+ }
394
+ .add {
395
+ color: var(--add);
396
+ }
397
+ .del {
398
+ color: var(--del);
399
+ }
400
+ .row-plan {
401
+ display: flex;
402
+ margin-top: 5px;
403
+ }
package/ui/rows.mjs ADDED
@@ -0,0 +1,172 @@
1
+ import { ago, callDuration, compactNumber, duration, modelName, percent, toolLabel } from './format.mjs'
2
+ import { esc, instAttrs, joinNames, plural, termButton } from './html.mjs'
3
+ import { IC, ICO, effortBars, effortName, harnessMark } from './icons.mjs'
4
+ import { planSpec, planTicks } from './pictures.mjs'
5
+ import { URGENCY_ORDER, lampHTML, statusOf } from './status.mjs'
6
+ import { repoName, whereHTML } from './where.mjs'
7
+
8
+ export const turnOf = (s) => (['asking', 'error', 'done'].includes(s.status) ? 'you' : ['working', 'delegating'].includes(s.status) ? 'run' : 'quiet')
9
+
10
+ export const groupKey = (group, g) => `${group}:${g.key}`
11
+
12
+ export const roughly = (ms) => duration(ms).replace(/ \d+s$/, '')
13
+ const turnStart = (s) => s.turnStartedAt || s.lastActivityAt
14
+
15
+ export function shownPlan(s) {
16
+ const p = s?.plan
17
+ return p && s.status === 'done' && p.done < p.total ? { ...p, done: p.total, current: '' } : p
18
+ }
19
+
20
+ export function waitingOn(s) {
21
+ const names = []
22
+ if (s.subagents) names.push(plural(s.subagents, 'subagent', 'subagents'))
23
+ const peers = s.awaiting || []
24
+ const name = (p) => p.title || 'another session'
25
+ if (peers.length > 2) names.push(name(peers[0]), plural(peers.length - 1, 'other session', 'other sessions'))
26
+ else names.push(...peers.map(name))
27
+ return names.length ? `Waiting on ${joinNames(names)}` : 'Waiting on agents'
28
+ }
29
+
30
+ export function nowParts(s, now) {
31
+ switch (s.status) {
32
+ case 'working':
33
+ return { h: 'Working', v: roughly(now - turnStart(s)) }
34
+ case 'delegating':
35
+ return { h: waitingOn(s), v: roughly(now - turnStart(s)) }
36
+ case 'asking':
37
+ return { h: 'Waiting for your answer', v: duration(now - s.lastActivityAt) }
38
+ case 'done':
39
+ return { h: 'Finished', v: ago(now - s.lastActivityAt) }
40
+ case 'error':
41
+ return { h: 'Stopped on an API error', v: ago(now - s.lastActivityAt) }
42
+ default:
43
+ return { h: 'Last active', v: ago(now - s.lastActivityAt) }
44
+ }
45
+ }
46
+
47
+ export function nowText(s, now) {
48
+ const p = nowParts(s, now)
49
+ return p.v ? `${p.h} ${p.v}` : p.h
50
+ }
51
+
52
+ export function nowHTML(s, now) {
53
+ const p = nowParts(s, now)
54
+ return `<span class="now-h">${esc(p.h)}</span>${p.v ? `<span class="now-v">${esc(p.v)}</span>` : ''}`
55
+ }
56
+
57
+ export function rowDetail(s, now) {
58
+ if (s.status === 'working') {
59
+ if (!s.tool) return { cls: 'rich', html: '<span class="nd-tool">Thinking</span>' }
60
+ const since = Number.isFinite(s.tool.startedAt) ? s.tool.startedAt : now
61
+ return {
62
+ cls: 'rich',
63
+ html: `<span class="nd-tool">${esc(toolLabel(s.tool.name))}</span><span class="nd-dur num" data-since="${since}">${esc(callDuration(now - since))}</span>${s.tool.target ? `<span class="nd-t">${esc(s.tool.target)}</span>` : ''}`,
64
+ }
65
+ }
66
+ if (s.status === 'delegating') {
67
+ const names = (s.workingSubagents || []).map((a) => a.name).filter(Boolean)
68
+ return names.length ? { cls: 'plain', html: esc(names.join(', ')) } : null
69
+ }
70
+ return null
71
+ }
72
+
73
+ export function errorShort(text) {
74
+ const m = /^(API Error:\s*\d+)\s*(\{.*\})\s*$/s.exec(text || '')
75
+ if (m) {
76
+ try {
77
+ const body = JSON.parse(m[2])
78
+ if (body?.error?.message) return `${m[1]} ${body.error.message}`
79
+ } catch {}
80
+ }
81
+ return text || ''
82
+ }
83
+
84
+ export function askLine(s) {
85
+ if ((s.status !== 'asking' && s.status !== 'error') || !s.ask) return null
86
+ const a = s.ask
87
+ if (a.type === 'question') {
88
+ const q = a.questions?.[0]
89
+ const text = q?.text || q?.header
90
+ return text ? { kind: 'question', text } : null
91
+ }
92
+ if (a.type === 'approval') {
93
+ if (a.target) return { kind: 'approval', text: a.target, mono: true }
94
+ return a.tool ? { kind: 'approval', text: toolLabel(a.tool), mono: false } : null
95
+ }
96
+ if (a.type === 'error') return { kind: 'error', text: errorShort(a.text) || 'The last turn failed.' }
97
+ return null
98
+ }
99
+
100
+ export const linesHTML = (l) => (l ? `<span class="add">+${compactNumber(l.added)}</span> <span class="del">−${compactNumber(l.removed)}</span>` : '—')
101
+
102
+ export function rowLabel(s, harnessName = (id) => id) {
103
+ const p = shownPlan(s)
104
+ const pct = s.context ? percent(s.context.used, s.context.limit) : null
105
+ return [
106
+ s.org,
107
+ s.project,
108
+ statusOf(s.status).name,
109
+ s.title,
110
+ harnessName(s.harness),
111
+ s.gitBranch,
112
+ modelName(s.model),
113
+ s.effort && `${effortName(s.effort)} effort`,
114
+ pct != null && `Context ${Math.round(pct)}%`,
115
+ p?.total && `Plan ${p.done} of ${p.total}`,
116
+ ]
117
+ .filter(Boolean)
118
+ .join(', ')
119
+ }
120
+
121
+ function ticksHTML(s) {
122
+ const p = shownPlan(s)
123
+ if (!p?.total || s.status === 'idle') return ''
124
+ return `<span class="row-plan" data-term="plan" data-rowtip ${instAttrs(planSpec(p), `${p.done} of ${p.total} steps`, p.current || 'All steps done')}>${planTicks(p, s.status)}</span>`
125
+ }
126
+
127
+ export function rowHTML(s, { sel = null, now = Date.now(), harnessName = (id) => id } = {}) {
128
+ const current = s.id === sel
129
+ const name = harnessName(s.harness)
130
+ const det = rowDetail(s, now)
131
+ const ask = askLine(s)
132
+ const meta = [
133
+ s.gitBranch && `<span>${ICO('branch')}${esc(s.gitBranch)}</span>`,
134
+ s.worktree && `<span>${ICO('worktree')}${esc(s.worktree)}</span>`,
135
+ s.model && `<span>${ICO('model')}${esc(modelName(s.model))}${effortBars(s.effort)}</span>`,
136
+ ]
137
+ .filter(Boolean)
138
+ .join('')
139
+ return `<li class="row" data-id="${esc(s.id)}" data-st="${esc(s.status)}" data-turn="${turnOf(s)}"${current ? ' data-current' : ''}>
140
+ <button type="button" class="row-main" data-act="select" data-key="${esc(s.id)}" data-peek="${esc(s.id)}" aria-label="${esc(rowLabel(s, harnessName))}"${current ? ' aria-current="true"' : ''}>
141
+ <span class="row-where">${whereHTML(s)}<span class="hmark row-h" title="${esc(name)}">${esc(harnessMark(s.harness, name))}</span></span>
142
+ ${lampHTML(s.status)}
143
+ <span class="row-text"><span class="row-title">${esc(s.title)}</span><span class="row-sub"><span class="row-now num" data-now="${esc(s.id)}">${nowHTML(s, now)}</span>${det ? `<span class="now-d ${det.cls}">${det.html}</span>` : ''}</span></span>
144
+ <span class="nums"><span class="xtra">${esc(compactNumber(s.tokens?.output))}</span><span class="xtra">${linesHTML(s.lines)}</span></span>
145
+ ${ask ? `<span class="row-why${ask.mono ? ' mono' : ''}">${esc(ask.text)}</span>` : ''}
146
+ <span class="row-meta">${meta}</span>
147
+ ${ticksHTML(s)}
148
+ </button>
149
+ </li>`
150
+ }
151
+
152
+ function groupLabel(g) {
153
+ if (g.kind !== 'repo') return `<span class="g-l">${esc(g.label)}</span>`
154
+ const org = g.org ? `<span class="g-c">${ICO('org')}<span>${esc(g.org)}</span></span>` : ''
155
+ return `<span class="g-l">${org}<span class="g-c" title="${esc(g.path || g.label)}">${ICO('folder')}<span>${esc(repoName(g.label, g.org))}</span></span></span>`
156
+ }
157
+
158
+ export function groupTally(g) {
159
+ if (g.kind === 'status') return ''
160
+ const counts = new Map()
161
+ for (const s of g.items) counts.set(s.status, (counts.get(s.status) || 0) + 1)
162
+ const parts = URGENCY_ORDER.filter((k) => counts.get(k)).map(
163
+ (k) => `<span class="g-tally" data-st="${k}" title="${esc(`${statusOf(k).name}: ${counts.get(k)}`)}">${lampHTML(k)}<b>${counts.get(k)}</b></span>`,
164
+ )
165
+ return parts.length ? `<span class="g-tallies">${parts.join('')}</span>` : ''
166
+ }
167
+
168
+ export function groupHeadHTML(g, first, { group = 'turn', shut = false } = {}) {
169
+ const key = groupKey(group, g)
170
+ const extra = first ? `<span class="hint"><kbd>N</kbd>next waiting</span><span class="g-cols">${termButton('output', 'Output')}${termButton('linesColumn', 'Lines')}</span>` : ''
171
+ return `<h2 class="grp-h" data-k="${esc(g.key)}" data-kind="${esc(g.kind)}"><button type="button" class="grp-t" data-act="grp-toggle" data-key="${esc(key)}" aria-expanded="${!shut}" aria-label="${esc(`${shut ? 'Show' : 'Hide'} ${g.label}, ${g.items.length}`)}">${IC.chev}<span class="g-flow">${groupLabel(g)}<span class="n">${g.items.length}</span>${shut ? groupTally(g) : ''}</span></button>${extra}</h2>`
172
+ }
package/ui/totals.css ADDED
@@ -0,0 +1,96 @@
1
+ .lamps {
2
+ display: flex;
3
+ flex: none;
4
+ align-items: stretch;
5
+ gap: 2px;
6
+ padding: 0 8px 6px calc(var(--gl) - 6px);
7
+ }
8
+ .lamp-btn {
9
+ display: inline-flex;
10
+ flex: 1 1 0;
11
+ align-items: center;
12
+ justify-content: center;
13
+ gap: 6px;
14
+ min-width: 0;
15
+ height: 30px;
16
+ padding: 0 4px;
17
+ background: none;
18
+ border: 0;
19
+ border-radius: var(--r-3);
20
+ color: var(--text);
21
+ font: 500 var(--fs-6) / 24px var(--cond);
22
+ white-space: nowrap;
23
+ }
24
+ .lamp-btn:hover {
25
+ background: var(--raise);
26
+ }
27
+ .lamp-btn[aria-pressed='true'] {
28
+ background: var(--plate);
29
+ color: var(--st);
30
+ box-shadow: inset 0 0 0 1px var(--st);
31
+ }
32
+ .lamp-btn[data-zero] {
33
+ color: var(--text3);
34
+ }
35
+ .lamp-btn[data-zero] .lamp {
36
+ opacity: 0.55;
37
+ }
38
+ .lamp-btn b {
39
+ font-size: inherit;
40
+ font-weight: 600;
41
+ line-height: 1.04;
42
+ font-variant-numeric: tabular-nums;
43
+ }
44
+ .lamp-btn .word {
45
+ display: none;
46
+ font-size: inherit;
47
+ font-weight: 400;
48
+ line-height: 1.04;
49
+ }
50
+ .lamp-btn .lamp {
51
+ transform: translateY(-0.5px);
52
+ }
53
+ .lamp-btn.total {
54
+ flex: 0 0 auto;
55
+ padding: 0 8px;
56
+ color: var(--text2);
57
+ }
58
+
59
+ .l-foot {
60
+ display: grid;
61
+ flex: none;
62
+ grid-template-columns: repeat(3, auto);
63
+ justify-content: space-between;
64
+ gap: 0 10px;
65
+ padding: 7px 12px 8px var(--gl);
66
+ border-top: 1px solid var(--line2);
67
+ }
68
+ .tot {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: 7px;
72
+ min-width: 0;
73
+ }
74
+ .tot-t {
75
+ display: flex;
76
+ flex-direction: column;
77
+ align-items: flex-start;
78
+ min-width: 0;
79
+ }
80
+ .tot b {
81
+ font: 600 var(--fs-7) / 19px var(--cond);
82
+ font-variant-numeric: tabular-nums;
83
+ white-space: nowrap;
84
+ }
85
+ .tot .term {
86
+ min-width: 28px;
87
+ }
88
+ @media (pointer: coarse) {
89
+ .tot .term {
90
+ padding-block: 3.5px;
91
+ margin-block: -3.5px;
92
+ }
93
+ }
94
+ .tot .i-wrap {
95
+ border-radius: 3px;
96
+ }