daily-focus 0.1.0
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/LICENSE +21 -0
- package/README.md +170 -0
- package/SETUP.md +501 -0
- package/dist/audit-4AUZVELT.js +215 -0
- package/dist/chunk-2PLWVW5T.js +987 -0
- package/dist/chunk-6VWWJZKJ.js +27 -0
- package/dist/chunk-EXGTFXNV.js +77 -0
- package/dist/chunk-G4K24I4M.js +425 -0
- package/dist/chunk-NDWZUZJE.js +179 -0
- package/dist/chunk-QKT2FAB5.js +132 -0
- package/dist/chunk-V2UN3JYL.js +140 -0
- package/dist/cli.js +203 -0
- package/dist/init-ZJCI2723.js +122 -0
- package/dist/sample-M7WNKQOV.js +7 -0
- package/dist/seed-7DIDKXT7.js +50 -0
- package/dist/server-B4FWZHS2.js +4814 -0
- package/dist/service-ILBMIZB7.js +240 -0
- package/package.json +66 -0
- package/prompts/README.md +122 -0
- package/prompts/assistant.md +62 -0
- package/prompts/morning-brief-personal.md +442 -0
- package/prompts/morning-brief-work.md +630 -0
- package/public/app.js +5058 -0
- package/public/index.html +36 -0
- package/public/style.css +2515 -0
- package/schema/items.schema.json +99 -0
- package/tools/dfcal/Info.plist +16 -0
- package/tools/dfcal/build.sh +52 -0
- package/tools/dfcal/main.swift +133 -0
package/public/style.css
ADDED
|
@@ -0,0 +1,2515 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tokens come from the validated reference palette: the eight categorical hues
|
|
3
|
+
* carry source identity, the four status colours are reserved for state only.
|
|
4
|
+
* Both modes are selected, not flipped — dark has its own steps.
|
|
5
|
+
*
|
|
6
|
+
* Source colour is never the only cue: every dot sits beside its source name in
|
|
7
|
+
* text, which is what lets the lighter light-mode hues (aqua, yellow, magenta)
|
|
8
|
+
* sit below 3:1 contrast without losing meaning.
|
|
9
|
+
*
|
|
10
|
+
* Each token names both its steps and `light-dark()` picks one, so the dark
|
|
11
|
+
* palette lives here and nowhere else. It used to be a second, hand-kept copy of
|
|
12
|
+
* this list inside a `prefers-color-scheme` block — twenty-two values one edit
|
|
13
|
+
* away from disagreeing with the ones above them, with nothing to notice.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
:root {
|
|
17
|
+
/*
|
|
18
|
+
* Both keywords, which is the state the toggle calls "system": `light-dark()`
|
|
19
|
+
* follows `prefers-color-scheme` and re-evaluates the moment the OS flips.
|
|
20
|
+
* This is also what renders before `index.html`'s stamp has run.
|
|
21
|
+
*/
|
|
22
|
+
color-scheme: light dark;
|
|
23
|
+
|
|
24
|
+
--page: light-dark(#f9f9f7, #0d0d0d);
|
|
25
|
+
--surface: light-dark(#fcfcfb, #1a1a19);
|
|
26
|
+
--surface-sunken: light-dark(#f2f1ed, #131312);
|
|
27
|
+
--ink-1: light-dark(#0b0b0b, #ffffff);
|
|
28
|
+
--ink-2: light-dark(#52514e, #c3c2b7);
|
|
29
|
+
--ink-3: #898781; /* Shared deliberately: the muted step clears both pages. */
|
|
30
|
+
--hairline: light-dark(#e1e0d9, #2c2c2a);
|
|
31
|
+
--border: light-dark(rgba(11, 11, 11, 0.1), rgba(255, 255, 255, 0.1));
|
|
32
|
+
/*
|
|
33
|
+
* Dark wants no shadow at all. Written as transparent steps rather than as a
|
|
34
|
+
* `none` somewhere else, because anywhere else means a selector that knows
|
|
35
|
+
* which mode it is in — and that is the duplicate copy coming back for one
|
|
36
|
+
* property.
|
|
37
|
+
*/
|
|
38
|
+
--shadow: 0 1px 2px light-dark(rgba(11, 11, 11, 0.05), transparent),
|
|
39
|
+
0 4px 16px light-dark(rgba(11, 11, 11, 0.04), transparent);
|
|
40
|
+
|
|
41
|
+
--good: #0ca30c;
|
|
42
|
+
--warning: #fab219;
|
|
43
|
+
--serious: #ec835a;
|
|
44
|
+
--critical: #d03b3b;
|
|
45
|
+
--good-ink: light-dark(#006300, #0ca30c);
|
|
46
|
+
|
|
47
|
+
--src-github: light-dark(#2a78d6, #3987e5);
|
|
48
|
+
--src-email: light-dark(#eb6834, #d95926);
|
|
49
|
+
--src-calendar: light-dark(#1baf7a, #199e70);
|
|
50
|
+
--src-jira: light-dark(#eda100, #c98500);
|
|
51
|
+
--src-slack: light-dark(#e87ba4, #d55181);
|
|
52
|
+
--src-workday: #008300; /* Shared: the green reads on both. */
|
|
53
|
+
--src-tasks: light-dark(#0b8ea8, #2ba8c4);
|
|
54
|
+
--src-atlassian: light-dark(#4a3aa7, #9085e9);
|
|
55
|
+
--src-reminders: light-dark(#8a5a2b, #c89058);
|
|
56
|
+
--src-messages: light-dark(#9b3fc0, #c07ae8);
|
|
57
|
+
--src-eboks: light-dark(#c42b3a, #e5535f);
|
|
58
|
+
--src-other: #898781;
|
|
59
|
+
|
|
60
|
+
--accent: light-dark(#2a78d6, #3987e5);
|
|
61
|
+
--accent-wash: light-dark(#e8f1fd, #14263a);
|
|
62
|
+
--focus-ring: light-dark(#2a78d6, #3987e5);
|
|
63
|
+
|
|
64
|
+
--radius: 10px;
|
|
65
|
+
--radius-sm: 6px;
|
|
66
|
+
--font: system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
* The two overrides, and the whole of what the toggle does. `data-theme="system"`
|
|
71
|
+
* matches neither and keeps both keywords above, so the third state is the
|
|
72
|
+
* absence of a choice rather than a third palette to maintain.
|
|
73
|
+
*/
|
|
74
|
+
:root[data-theme='light'] {
|
|
75
|
+
color-scheme: light;
|
|
76
|
+
}
|
|
77
|
+
:root[data-theme='dark'] {
|
|
78
|
+
color-scheme: dark;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
*,
|
|
82
|
+
*::before,
|
|
83
|
+
*::after {
|
|
84
|
+
box-sizing: border-box;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
* The UA stylesheet's `[hidden] { display: none }` loses to any class that sets
|
|
89
|
+
* display, so `.timer`, `.focus-bar` and `.toast` all rendered as empty boxes
|
|
90
|
+
* while "hidden". Assert it once here rather than remembering per component.
|
|
91
|
+
*/
|
|
92
|
+
[hidden] {
|
|
93
|
+
display: none !important;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
body {
|
|
97
|
+
margin: 0;
|
|
98
|
+
padding: 0 clamp(16px, 4vw, 40px) 64px;
|
|
99
|
+
background: var(--page);
|
|
100
|
+
color: var(--ink-1);
|
|
101
|
+
font-family: var(--font);
|
|
102
|
+
font-size: 15px;
|
|
103
|
+
line-height: 1.5;
|
|
104
|
+
-webkit-font-smoothing: antialiased;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
:focus-visible {
|
|
108
|
+
outline: 2px solid var(--focus-ring);
|
|
109
|
+
outline-offset: 2px;
|
|
110
|
+
border-radius: 3px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
a {
|
|
114
|
+
color: var(--accent);
|
|
115
|
+
text-underline-offset: 2px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.skip-link {
|
|
119
|
+
position: absolute;
|
|
120
|
+
left: -9999px;
|
|
121
|
+
}
|
|
122
|
+
.skip-link:focus {
|
|
123
|
+
left: 16px;
|
|
124
|
+
top: 16px;
|
|
125
|
+
z-index: 10;
|
|
126
|
+
padding: 8px 12px;
|
|
127
|
+
background: var(--surface);
|
|
128
|
+
border: 1px solid var(--border);
|
|
129
|
+
border-radius: var(--radius-sm);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* ---------- header ---------- */
|
|
133
|
+
|
|
134
|
+
.page-header {
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: flex-end;
|
|
137
|
+
justify-content: space-between;
|
|
138
|
+
gap: 16px;
|
|
139
|
+
flex-wrap: wrap;
|
|
140
|
+
max-width: 1280px;
|
|
141
|
+
margin: 0 auto;
|
|
142
|
+
padding: 28px 0 20px;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.page-header h1 {
|
|
146
|
+
margin: 0;
|
|
147
|
+
font-size: 22px;
|
|
148
|
+
font-weight: 600;
|
|
149
|
+
letter-spacing: -0.01em;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.page-header__date {
|
|
153
|
+
margin: 2px 0 0;
|
|
154
|
+
color: var(--ink-2);
|
|
155
|
+
font-size: 14px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.page-header__meta {
|
|
159
|
+
display: flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
gap: 8px;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.freshness {
|
|
165
|
+
color: var(--ink-3);
|
|
166
|
+
font-size: 13px;
|
|
167
|
+
}
|
|
168
|
+
.freshness[data-stale='true'] {
|
|
169
|
+
color: var(--critical);
|
|
170
|
+
font-weight: 500;
|
|
171
|
+
}
|
|
172
|
+
/*
|
|
173
|
+
* The readout as a link into the morning agent's runs: the same words, so the
|
|
174
|
+
* header gains nothing, and an underline on hover to say they go somewhere.
|
|
175
|
+
*/
|
|
176
|
+
.freshness__link {
|
|
177
|
+
padding: 0;
|
|
178
|
+
border: 0;
|
|
179
|
+
background: none;
|
|
180
|
+
font: inherit;
|
|
181
|
+
color: inherit;
|
|
182
|
+
cursor: pointer;
|
|
183
|
+
}
|
|
184
|
+
.freshness__link:hover {
|
|
185
|
+
text-decoration: underline;
|
|
186
|
+
text-decoration-color: var(--accent);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.icon-button {
|
|
190
|
+
width: 30px;
|
|
191
|
+
height: 30px;
|
|
192
|
+
display: grid;
|
|
193
|
+
place-items: center;
|
|
194
|
+
border: 1px solid var(--border);
|
|
195
|
+
border-radius: var(--radius-sm);
|
|
196
|
+
background: var(--surface);
|
|
197
|
+
color: var(--ink-2);
|
|
198
|
+
font-size: 14px;
|
|
199
|
+
cursor: pointer;
|
|
200
|
+
}
|
|
201
|
+
.icon-button:hover {
|
|
202
|
+
color: var(--ink-1);
|
|
203
|
+
border-color: var(--ink-3);
|
|
204
|
+
}
|
|
205
|
+
.icon-button__icon {
|
|
206
|
+
width: 15px;
|
|
207
|
+
height: 15px;
|
|
208
|
+
}
|
|
209
|
+
/* A toggle that is on, such as the gear while Settings is open: drawn as the selected tab is, in the accent. */
|
|
210
|
+
.icon-button[aria-pressed='true'] {
|
|
211
|
+
color: var(--accent);
|
|
212
|
+
border-color: var(--accent);
|
|
213
|
+
background: var(--accent-wash);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/*
|
|
217
|
+
* Lost connection to the server. A pill in the header, because it is about the
|
|
218
|
+
* whole page rather than the tab showing, and it heals by itself — a full-width
|
|
219
|
+
* banner claimed more attention than a state with nothing to do about it.
|
|
220
|
+
* Warning, not critical: nothing is lost, the page is just not moving.
|
|
221
|
+
*/
|
|
222
|
+
.connection {
|
|
223
|
+
display: inline-flex;
|
|
224
|
+
align-items: center;
|
|
225
|
+
gap: 6px;
|
|
226
|
+
height: 30px;
|
|
227
|
+
padding: 0 10px;
|
|
228
|
+
border: 1px solid var(--border);
|
|
229
|
+
border-radius: 999px;
|
|
230
|
+
background: var(--surface);
|
|
231
|
+
color: var(--ink-2);
|
|
232
|
+
font-size: 13px;
|
|
233
|
+
white-space: nowrap;
|
|
234
|
+
cursor: help;
|
|
235
|
+
}
|
|
236
|
+
.connection[hidden] {
|
|
237
|
+
display: none;
|
|
238
|
+
}
|
|
239
|
+
.connection__dot {
|
|
240
|
+
width: 8px;
|
|
241
|
+
height: 8px;
|
|
242
|
+
border-radius: 50%;
|
|
243
|
+
background: var(--warning);
|
|
244
|
+
animation: connection-pulse 1.4s ease-in-out infinite;
|
|
245
|
+
}
|
|
246
|
+
@keyframes connection-pulse {
|
|
247
|
+
50% {
|
|
248
|
+
opacity: 0.3;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
@media (prefers-reduced-motion: reduce) {
|
|
252
|
+
.connection__dot {
|
|
253
|
+
animation: none;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/* The readouts beside it describe the last update, not now, so they step back. */
|
|
257
|
+
body[data-offline='true'] .freshness,
|
|
258
|
+
body[data-offline='true'] .refresh-label {
|
|
259
|
+
opacity: 0.5;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/*
|
|
263
|
+
* One glyph per theme state, picked by the attribute the pre-paint stamp sets.
|
|
264
|
+
* Keyed off `:root` rather than off something the client writes later, so the
|
|
265
|
+
* icon arrives in the same paint as the colours it is describing.
|
|
266
|
+
*/
|
|
267
|
+
.theme-toggle__icon {
|
|
268
|
+
display: none;
|
|
269
|
+
width: 15px;
|
|
270
|
+
height: 15px;
|
|
271
|
+
}
|
|
272
|
+
:root[data-theme='system'] .theme-toggle__icon[data-theme-icon='system'],
|
|
273
|
+
:root[data-theme='light'] .theme-toggle__icon[data-theme-icon='light'],
|
|
274
|
+
:root[data-theme='dark'] .theme-toggle__icon[data-theme-icon='dark'],
|
|
275
|
+
/* No stamp at all means no script ran, and the OS is still deciding. */
|
|
276
|
+
:root:not([data-theme]) .theme-toggle__icon[data-theme-icon='system'] {
|
|
277
|
+
display: block;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/*
|
|
281
|
+
* Each board's source, last read and refresh icon, in the header beside the
|
|
282
|
+
* theme toggle and only on its own tab — where the brief's "updated … ago" sits
|
|
283
|
+
* on Today, so that side of the header always describes the tab showing.
|
|
284
|
+
*/
|
|
285
|
+
.board-refresh {
|
|
286
|
+
display: none;
|
|
287
|
+
}
|
|
288
|
+
body[data-view='board'] #board-refresh,
|
|
289
|
+
body[data-view='tickets'] #tickets-refresh {
|
|
290
|
+
display: contents;
|
|
291
|
+
}
|
|
292
|
+
body[data-view='board'] #freshness,
|
|
293
|
+
body[data-view='tickets'] #freshness,
|
|
294
|
+
body[data-view='board'] #brief-refresh,
|
|
295
|
+
body[data-view='tickets'] #brief-refresh {
|
|
296
|
+
display: none;
|
|
297
|
+
}
|
|
298
|
+
.brief-refresh {
|
|
299
|
+
display: contents;
|
|
300
|
+
}
|
|
301
|
+
/* Reads as the Today tab's "updated … ago" does, so the three tabs match. */
|
|
302
|
+
.refresh-label {
|
|
303
|
+
color: var(--ink-3);
|
|
304
|
+
font-size: 13px;
|
|
305
|
+
font-variant-numeric: tabular-nums;
|
|
306
|
+
}
|
|
307
|
+
.refresh-label[data-stale='true'] {
|
|
308
|
+
color: var(--critical);
|
|
309
|
+
font-weight: 500;
|
|
310
|
+
}
|
|
311
|
+
/*
|
|
312
|
+
* Two arrows chasing each other round a circle, stroked to match the theme
|
|
313
|
+
* toggle's glyphs. A mask rather than an image so it is painted in
|
|
314
|
+
* `currentColor`, and turns red with the button when the board is stale.
|
|
315
|
+
*/
|
|
316
|
+
.refresh-button__icon {
|
|
317
|
+
display: block;
|
|
318
|
+
width: 15px;
|
|
319
|
+
height: 15px;
|
|
320
|
+
background: currentColor;
|
|
321
|
+
--refresh-glyph: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'><path d='M3.5 12a8.5 8.5 0 0 1 14.5-6L20.5 8.5'/><path d='M20.5 3.5v5h-5'/><path d='M20.5 12a8.5 8.5 0 0 1-14.5 6L3.5 15.5'/><path d='M8.5 15.5h-5v5'/></svg>");
|
|
322
|
+
-webkit-mask: var(--refresh-glyph) center / contain no-repeat;
|
|
323
|
+
mask: var(--refresh-glyph) center / contain no-repeat;
|
|
324
|
+
}
|
|
325
|
+
.refresh-button[data-fetching='true'] {
|
|
326
|
+
cursor: progress;
|
|
327
|
+
}
|
|
328
|
+
.refresh-button[data-fetching='true'] .refresh-button__icon {
|
|
329
|
+
animation: refresh-spin 0.9s linear infinite;
|
|
330
|
+
}
|
|
331
|
+
.refresh-button[data-stale='true'] {
|
|
332
|
+
color: var(--critical);
|
|
333
|
+
border-color: var(--critical);
|
|
334
|
+
}
|
|
335
|
+
@keyframes refresh-spin {
|
|
336
|
+
to {
|
|
337
|
+
transform: rotate(360deg);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/* Still says it is busy without the motion: the icon dims instead of turning. */
|
|
341
|
+
@media (prefers-reduced-motion: reduce) {
|
|
342
|
+
.refresh-button[data-fetching='true'] .refresh-button__icon {
|
|
343
|
+
animation: none;
|
|
344
|
+
opacity: 0.45;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* ---------- focus timer ---------- */
|
|
349
|
+
|
|
350
|
+
.timer {
|
|
351
|
+
display: flex;
|
|
352
|
+
align-items: center;
|
|
353
|
+
gap: 12px;
|
|
354
|
+
max-width: 1280px;
|
|
355
|
+
margin: 0 auto 14px;
|
|
356
|
+
padding: 10px 14px;
|
|
357
|
+
background: var(--surface);
|
|
358
|
+
border: 1px solid var(--border);
|
|
359
|
+
border-left: 3px solid var(--accent);
|
|
360
|
+
border-radius: var(--radius);
|
|
361
|
+
box-shadow: var(--shadow);
|
|
362
|
+
font-size: 13.5px;
|
|
363
|
+
color: var(--ink-2);
|
|
364
|
+
}
|
|
365
|
+
/* Past the target the timer stops being encouraging and starts being a fact. */
|
|
366
|
+
.timer[data-state='overrun'] {
|
|
367
|
+
border-left-color: var(--serious);
|
|
368
|
+
}
|
|
369
|
+
.timer[data-state='idle'] {
|
|
370
|
+
border-left-color: var(--hairline);
|
|
371
|
+
color: var(--ink-3);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.timer__clock {
|
|
375
|
+
font-size: 20px;
|
|
376
|
+
font-weight: 600;
|
|
377
|
+
font-variant-numeric: tabular-nums;
|
|
378
|
+
color: var(--ink-1);
|
|
379
|
+
min-width: 62px;
|
|
380
|
+
}
|
|
381
|
+
.timer[data-state='overrun'] .timer__clock {
|
|
382
|
+
color: var(--serious);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.timer__title {
|
|
386
|
+
flex: 1;
|
|
387
|
+
min-width: 0;
|
|
388
|
+
color: var(--ink-1);
|
|
389
|
+
overflow-wrap: anywhere;
|
|
390
|
+
}
|
|
391
|
+
.timer__note {
|
|
392
|
+
color: var(--ink-3);
|
|
393
|
+
}
|
|
394
|
+
.timer__note--warn {
|
|
395
|
+
color: var(--critical);
|
|
396
|
+
}
|
|
397
|
+
.timer__tally {
|
|
398
|
+
color: var(--ink-3);
|
|
399
|
+
white-space: nowrap;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/*
|
|
403
|
+
* The bar after the timer stopped itself. Marked like the overrun state rather
|
|
404
|
+
* than the idle one: something happened that's worth a glance, even though the
|
|
405
|
+
* thing that happened was the dashboard being careful.
|
|
406
|
+
*/
|
|
407
|
+
.timer[data-state='stopped'] {
|
|
408
|
+
border-left-color: var(--serious);
|
|
409
|
+
}
|
|
410
|
+
.timer__away {
|
|
411
|
+
display: flex;
|
|
412
|
+
flex: 1;
|
|
413
|
+
align-items: center;
|
|
414
|
+
gap: 12px;
|
|
415
|
+
min-width: 0;
|
|
416
|
+
}
|
|
417
|
+
.timer__away-body {
|
|
418
|
+
display: flex;
|
|
419
|
+
flex-direction: column;
|
|
420
|
+
flex: 1;
|
|
421
|
+
min-width: 0;
|
|
422
|
+
}
|
|
423
|
+
.timer__away-text {
|
|
424
|
+
color: var(--ink-1);
|
|
425
|
+
overflow-wrap: anywhere;
|
|
426
|
+
}
|
|
427
|
+
.timer__away-detail {
|
|
428
|
+
color: var(--ink-3);
|
|
429
|
+
font-size: 12.5px;
|
|
430
|
+
overflow-wrap: anywhere;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/* ---------- banners ---------- */
|
|
434
|
+
|
|
435
|
+
.banners {
|
|
436
|
+
max-width: 1280px;
|
|
437
|
+
margin: 0 auto;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.banner {
|
|
441
|
+
display: flex;
|
|
442
|
+
gap: 10px;
|
|
443
|
+
align-items: flex-start;
|
|
444
|
+
padding: 10px 14px;
|
|
445
|
+
margin-bottom: 10px;
|
|
446
|
+
border-radius: var(--radius-sm);
|
|
447
|
+
border: 1px solid var(--border);
|
|
448
|
+
background: var(--surface);
|
|
449
|
+
font-size: 14px;
|
|
450
|
+
color: var(--ink-2);
|
|
451
|
+
}
|
|
452
|
+
.banner__icon {
|
|
453
|
+
flex: none;
|
|
454
|
+
line-height: 1.5;
|
|
455
|
+
}
|
|
456
|
+
.banner--critical {
|
|
457
|
+
border-left: 3px solid var(--critical);
|
|
458
|
+
}
|
|
459
|
+
.banner--critical .banner__icon {
|
|
460
|
+
color: var(--critical);
|
|
461
|
+
}
|
|
462
|
+
.banner--warning {
|
|
463
|
+
border-left: 3px solid var(--warning);
|
|
464
|
+
}
|
|
465
|
+
.banner--warning .banner__icon {
|
|
466
|
+
color: var(--serious);
|
|
467
|
+
}
|
|
468
|
+
.banner__text {
|
|
469
|
+
flex: 1;
|
|
470
|
+
min-width: 0;
|
|
471
|
+
}
|
|
472
|
+
.banner__action {
|
|
473
|
+
flex: none;
|
|
474
|
+
align-self: center;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/*
|
|
478
|
+
* A read that keeps failing: the banner's one line as a fold, with how long, why
|
|
479
|
+
* and a Retry inside. Shared with the agenda's note, which brings its own box.
|
|
480
|
+
*/
|
|
481
|
+
.failure {
|
|
482
|
+
display: block;
|
|
483
|
+
}
|
|
484
|
+
.failure__summary {
|
|
485
|
+
display: flex;
|
|
486
|
+
gap: 10px;
|
|
487
|
+
align-items: flex-start;
|
|
488
|
+
cursor: pointer;
|
|
489
|
+
list-style: none;
|
|
490
|
+
}
|
|
491
|
+
.failure__summary::-webkit-details-marker {
|
|
492
|
+
display: none;
|
|
493
|
+
}
|
|
494
|
+
.failure__summary::after {
|
|
495
|
+
content: '▸';
|
|
496
|
+
flex: none;
|
|
497
|
+
color: var(--ink-3);
|
|
498
|
+
}
|
|
499
|
+
.failure[open] > .failure__summary::after {
|
|
500
|
+
content: '▾';
|
|
501
|
+
}
|
|
502
|
+
.failure__summary:hover::after {
|
|
503
|
+
color: var(--ink-1);
|
|
504
|
+
}
|
|
505
|
+
.failure__message {
|
|
506
|
+
flex: 1;
|
|
507
|
+
min-width: 0;
|
|
508
|
+
overflow-wrap: anywhere;
|
|
509
|
+
}
|
|
510
|
+
/* Under the message, not under the icon. */
|
|
511
|
+
.banner .failure__body {
|
|
512
|
+
margin-left: 20px;
|
|
513
|
+
}
|
|
514
|
+
.failure__facts {
|
|
515
|
+
display: flex;
|
|
516
|
+
flex-wrap: wrap;
|
|
517
|
+
gap: 6px 12px;
|
|
518
|
+
align-items: center;
|
|
519
|
+
justify-content: space-between;
|
|
520
|
+
margin: 8px 0 0;
|
|
521
|
+
font-size: 13px;
|
|
522
|
+
color: var(--ink-3);
|
|
523
|
+
}
|
|
524
|
+
.agenda__note .failure__facts {
|
|
525
|
+
font-size: inherit;
|
|
526
|
+
}
|
|
527
|
+
.failure__retry[aria-busy='true'] {
|
|
528
|
+
cursor: progress;
|
|
529
|
+
}
|
|
530
|
+
.failure__detail {
|
|
531
|
+
margin: 8px 0 0;
|
|
532
|
+
padding: 8px 10px;
|
|
533
|
+
max-height: 16em;
|
|
534
|
+
overflow: auto;
|
|
535
|
+
border-radius: 6px;
|
|
536
|
+
background: var(--surface-sunken);
|
|
537
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
538
|
+
font-size: 12px;
|
|
539
|
+
line-height: 1.45;
|
|
540
|
+
color: var(--ink-2);
|
|
541
|
+
white-space: pre-wrap;
|
|
542
|
+
overflow-wrap: anywhere;
|
|
543
|
+
}
|
|
544
|
+
.agenda__note .failure__detail {
|
|
545
|
+
padding: 6px 8px;
|
|
546
|
+
background: var(--surface);
|
|
547
|
+
font-size: 10.5px;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/* A run of the morning agent the dashboard started, and what it reported. */
|
|
551
|
+
.agent-run__progress {
|
|
552
|
+
display: block;
|
|
553
|
+
margin-top: 2px;
|
|
554
|
+
color: var(--ink-3);
|
|
555
|
+
font-size: 13px;
|
|
556
|
+
overflow: hidden;
|
|
557
|
+
text-overflow: ellipsis;
|
|
558
|
+
white-space: nowrap;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/* A banner that is a way in, as a card is: the whole strip is the target. */
|
|
562
|
+
.banner--link {
|
|
563
|
+
cursor: pointer;
|
|
564
|
+
}
|
|
565
|
+
.banner--link:hover {
|
|
566
|
+
border-color: var(--ink-3);
|
|
567
|
+
}
|
|
568
|
+
.banner--link:focus-visible {
|
|
569
|
+
outline: 2px solid var(--accent);
|
|
570
|
+
outline-offset: 1px;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/* The run's panel: how it went, then the list of the others, folded away. */
|
|
574
|
+
.agent-run__status {
|
|
575
|
+
margin: 0 0 22px;
|
|
576
|
+
font-size: 13px;
|
|
577
|
+
color: var(--ink-2);
|
|
578
|
+
}
|
|
579
|
+
.agent-run__status--bad {
|
|
580
|
+
color: var(--serious);
|
|
581
|
+
}
|
|
582
|
+
.agent-run__list summary {
|
|
583
|
+
cursor: pointer;
|
|
584
|
+
font-size: 13px;
|
|
585
|
+
color: var(--ink-3);
|
|
586
|
+
}
|
|
587
|
+
/* What the agent said along the way, one fold per message, under its first line. */
|
|
588
|
+
.agent-run__messages {
|
|
589
|
+
display: flex;
|
|
590
|
+
flex-direction: column;
|
|
591
|
+
gap: 6px;
|
|
592
|
+
}
|
|
593
|
+
.agent-run__list > .agent-run__messages {
|
|
594
|
+
margin-top: 8px;
|
|
595
|
+
}
|
|
596
|
+
.agent-run__message {
|
|
597
|
+
border: 1px solid var(--hairline);
|
|
598
|
+
border-radius: var(--radius-sm);
|
|
599
|
+
background: var(--surface-sunken);
|
|
600
|
+
}
|
|
601
|
+
.agent-run__message summary {
|
|
602
|
+
padding: 6px 10px;
|
|
603
|
+
cursor: pointer;
|
|
604
|
+
font-size: 12.5px;
|
|
605
|
+
color: var(--ink-2);
|
|
606
|
+
overflow: hidden;
|
|
607
|
+
text-overflow: ellipsis;
|
|
608
|
+
white-space: nowrap;
|
|
609
|
+
}
|
|
610
|
+
.agent-run__message[open] summary {
|
|
611
|
+
color: var(--ink-3);
|
|
612
|
+
}
|
|
613
|
+
.agent-run__message-index,
|
|
614
|
+
.agent-run__message[open] .agent-run__message-line {
|
|
615
|
+
display: none;
|
|
616
|
+
}
|
|
617
|
+
.agent-run__message[open] .agent-run__message-index {
|
|
618
|
+
display: inline;
|
|
619
|
+
}
|
|
620
|
+
.agent-run__message .assistant__reply {
|
|
621
|
+
padding: 0 10px 8px;
|
|
622
|
+
}
|
|
623
|
+
.agent-run__runs {
|
|
624
|
+
margin: 8px 0 0;
|
|
625
|
+
padding: 0;
|
|
626
|
+
list-style: none;
|
|
627
|
+
}
|
|
628
|
+
.agent-run__runs li + li {
|
|
629
|
+
margin-top: 6px;
|
|
630
|
+
}
|
|
631
|
+
.agent-run__pick {
|
|
632
|
+
display: block;
|
|
633
|
+
width: 100%;
|
|
634
|
+
padding: 7px 10px;
|
|
635
|
+
border: 1px solid var(--hairline);
|
|
636
|
+
border-radius: var(--radius-sm);
|
|
637
|
+
background: var(--surface-sunken);
|
|
638
|
+
text-align: left;
|
|
639
|
+
font: inherit;
|
|
640
|
+
color: var(--ink-2);
|
|
641
|
+
cursor: pointer;
|
|
642
|
+
}
|
|
643
|
+
.agent-run__pick:hover {
|
|
644
|
+
border-color: var(--ink-3);
|
|
645
|
+
color: var(--ink-1);
|
|
646
|
+
}
|
|
647
|
+
.agent-run__pick-when {
|
|
648
|
+
display: block;
|
|
649
|
+
font-size: 12.5px;
|
|
650
|
+
}
|
|
651
|
+
.agent-run__pick-line {
|
|
652
|
+
display: block;
|
|
653
|
+
margin-top: 2px;
|
|
654
|
+
font-size: 12px;
|
|
655
|
+
color: var(--ink-3);
|
|
656
|
+
overflow: hidden;
|
|
657
|
+
text-overflow: ellipsis;
|
|
658
|
+
white-space: nowrap;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/* Explanation, not status — so it borrows the accent, not one of the four state hues. */
|
|
662
|
+
.banner--info {
|
|
663
|
+
border-left: 3px solid var(--accent);
|
|
664
|
+
}
|
|
665
|
+
.banner--info .banner__icon {
|
|
666
|
+
color: var(--accent);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/* ---------- layout ---------- */
|
|
670
|
+
|
|
671
|
+
.layout {
|
|
672
|
+
display: grid;
|
|
673
|
+
grid-template-columns: minmax(0, 1fr) 320px;
|
|
674
|
+
gap: 24px;
|
|
675
|
+
align-items: start;
|
|
676
|
+
max-width: 1280px;
|
|
677
|
+
margin: 0 auto;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
@media (max-width: 900px) {
|
|
681
|
+
.layout {
|
|
682
|
+
grid-template-columns: minmax(0, 1fr);
|
|
683
|
+
}
|
|
684
|
+
.layout__side {
|
|
685
|
+
order: -1;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/* ---------- focus mode ---------- */
|
|
690
|
+
|
|
691
|
+
/*
|
|
692
|
+
* Fourteen open rows are fourteen invitations to switch task. Focus mode answers
|
|
693
|
+
* the only question that matters mid-work — "what am I doing right now" — by
|
|
694
|
+
* leaving the objective, one item, and the clock, and taking away the menu.
|
|
695
|
+
*/
|
|
696
|
+
.focus-bar {
|
|
697
|
+
display: flex;
|
|
698
|
+
align-items: center;
|
|
699
|
+
justify-content: space-between;
|
|
700
|
+
gap: 12px;
|
|
701
|
+
margin: 0 0 16px;
|
|
702
|
+
padding: 9px 14px;
|
|
703
|
+
background: var(--surface);
|
|
704
|
+
border: 1px solid var(--border);
|
|
705
|
+
border-left: 3px solid var(--accent);
|
|
706
|
+
border-radius: var(--radius);
|
|
707
|
+
font-size: 13px;
|
|
708
|
+
color: var(--ink-2);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/* Scoped to the Today view: the board is a different question, and stays whole. */
|
|
712
|
+
body[data-focus-mode='true'][data-view='today'] .stats,
|
|
713
|
+
body[data-focus-mode='true'][data-view='today'] .headline,
|
|
714
|
+
body[data-focus-mode='true'][data-view='today'] #today .drawer,
|
|
715
|
+
body[data-focus-mode='true'][data-view='today'] #today .section__header,
|
|
716
|
+
/* every section except the first, and every item in it except the first */
|
|
717
|
+
body[data-focus-mode='true'][data-view='today'] #today .section:not(:first-of-type),
|
|
718
|
+
body[data-focus-mode='true'][data-view='today'] #today .section:first-of-type .item:not(:first-child) {
|
|
719
|
+
display: none;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
body[data-focus-mode='true'][data-view='today'] #today .item {
|
|
723
|
+
padding: 18px 20px;
|
|
724
|
+
}
|
|
725
|
+
body[data-focus-mode='true'][data-view='today'] #today .item__title {
|
|
726
|
+
font-size: 19px;
|
|
727
|
+
line-height: 1.35;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/* ---------- standing objective ---------- */
|
|
731
|
+
|
|
732
|
+
/*
|
|
733
|
+
* Deliberately heavier than the agent's headline below it. The headline changes
|
|
734
|
+
* daily; this doesn't, and it should be the first thing read on the page.
|
|
735
|
+
*/
|
|
736
|
+
.objective {
|
|
737
|
+
margin-bottom: 20px;
|
|
738
|
+
padding: 14px 16px;
|
|
739
|
+
background: var(--surface);
|
|
740
|
+
border: 1px solid var(--border);
|
|
741
|
+
border-left: 3px solid var(--accent);
|
|
742
|
+
border-radius: var(--radius);
|
|
743
|
+
box-shadow: var(--shadow);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.objective__label {
|
|
747
|
+
margin: 0 0 4px;
|
|
748
|
+
font-size: 11px;
|
|
749
|
+
font-weight: 600;
|
|
750
|
+
letter-spacing: 0.07em;
|
|
751
|
+
text-transform: uppercase;
|
|
752
|
+
color: var(--ink-3);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
.objective__text {
|
|
756
|
+
margin: 0;
|
|
757
|
+
font-size: 17px;
|
|
758
|
+
font-weight: 600;
|
|
759
|
+
line-height: 1.35;
|
|
760
|
+
letter-spacing: -0.01em;
|
|
761
|
+
color: var(--ink-1);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
.objective__blocker {
|
|
765
|
+
margin: 6px 0 0;
|
|
766
|
+
font-size: 13.5px;
|
|
767
|
+
color: var(--ink-2);
|
|
768
|
+
}
|
|
769
|
+
.objective__blocker-label {
|
|
770
|
+
color: var(--critical);
|
|
771
|
+
font-weight: 500;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
.objective__note {
|
|
775
|
+
margin: 8px 0 0;
|
|
776
|
+
font-size: 13px;
|
|
777
|
+
color: var(--ink-3);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/* A reminder, not a headline: no accent, no shadow, nothing competing with the brief. */
|
|
781
|
+
.objective--empty {
|
|
782
|
+
padding: 10px 16px;
|
|
783
|
+
background: transparent;
|
|
784
|
+
border-style: dashed;
|
|
785
|
+
border-left-width: 1px;
|
|
786
|
+
box-shadow: none;
|
|
787
|
+
}
|
|
788
|
+
.objective--empty .objective__note {
|
|
789
|
+
margin-top: 2px;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/* ---------- stat row ---------- */
|
|
793
|
+
|
|
794
|
+
.stats {
|
|
795
|
+
display: grid;
|
|
796
|
+
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
|
797
|
+
gap: 12px;
|
|
798
|
+
margin-bottom: 20px;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.stat {
|
|
802
|
+
padding: 12px 14px;
|
|
803
|
+
background: var(--surface);
|
|
804
|
+
border: 1px solid var(--border);
|
|
805
|
+
border-radius: var(--radius);
|
|
806
|
+
box-shadow: var(--shadow);
|
|
807
|
+
}
|
|
808
|
+
.stat__label {
|
|
809
|
+
margin: 0;
|
|
810
|
+
color: var(--ink-2);
|
|
811
|
+
font-size: 12px;
|
|
812
|
+
}
|
|
813
|
+
.stat__value {
|
|
814
|
+
margin: 2px 0 0;
|
|
815
|
+
font-size: 26px;
|
|
816
|
+
font-weight: 600;
|
|
817
|
+
line-height: 1.15;
|
|
818
|
+
letter-spacing: -0.02em;
|
|
819
|
+
}
|
|
820
|
+
.stat__value[data-tone='critical'] {
|
|
821
|
+
color: var(--critical);
|
|
822
|
+
}
|
|
823
|
+
.stat__value[data-tone='good'] {
|
|
824
|
+
color: var(--good-ink);
|
|
825
|
+
}
|
|
826
|
+
.stat__footnote {
|
|
827
|
+
margin: 3px 0 0;
|
|
828
|
+
color: var(--ink-3);
|
|
829
|
+
font-size: 11.5px;
|
|
830
|
+
line-height: 1.35;
|
|
831
|
+
/* The last thing completed can be a full title; keep the tile from growing. */
|
|
832
|
+
display: -webkit-box;
|
|
833
|
+
-webkit-line-clamp: 2;
|
|
834
|
+
-webkit-box-orient: vertical;
|
|
835
|
+
overflow: hidden;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
.headline {
|
|
839
|
+
margin: 0 0 20px;
|
|
840
|
+
padding: 12px 14px;
|
|
841
|
+
background: var(--surface);
|
|
842
|
+
border: 1px solid var(--border);
|
|
843
|
+
border-left: 3px solid var(--accent);
|
|
844
|
+
border-radius: var(--radius);
|
|
845
|
+
color: var(--ink-2);
|
|
846
|
+
font-size: 14px;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/* ---------- sections ---------- */
|
|
850
|
+
|
|
851
|
+
.section {
|
|
852
|
+
margin-bottom: 22px;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
.section__header {
|
|
856
|
+
display: flex;
|
|
857
|
+
align-items: baseline;
|
|
858
|
+
gap: 8px;
|
|
859
|
+
margin: 0 0 8px;
|
|
860
|
+
padding: 0 2px;
|
|
861
|
+
}
|
|
862
|
+
.section__title {
|
|
863
|
+
margin: 0;
|
|
864
|
+
font-size: 12px;
|
|
865
|
+
font-weight: 600;
|
|
866
|
+
letter-spacing: 0.06em;
|
|
867
|
+
text-transform: uppercase;
|
|
868
|
+
color: var(--ink-2);
|
|
869
|
+
}
|
|
870
|
+
.section__count {
|
|
871
|
+
color: var(--ink-3);
|
|
872
|
+
font-size: 12px;
|
|
873
|
+
font-variant-numeric: tabular-nums;
|
|
874
|
+
}
|
|
875
|
+
.section__dot {
|
|
876
|
+
width: 8px;
|
|
877
|
+
height: 8px;
|
|
878
|
+
border-radius: 50%;
|
|
879
|
+
flex: none;
|
|
880
|
+
align-self: center;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
.list {
|
|
884
|
+
list-style: none;
|
|
885
|
+
margin: 0;
|
|
886
|
+
padding: 0;
|
|
887
|
+
display: flex;
|
|
888
|
+
flex-direction: column;
|
|
889
|
+
gap: 2px; /* the surface gap doing the separating, not a border */
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/* ---------- item ---------- */
|
|
893
|
+
|
|
894
|
+
.item {
|
|
895
|
+
position: relative;
|
|
896
|
+
display: grid;
|
|
897
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
898
|
+
gap: 10px;
|
|
899
|
+
padding: 11px 13px;
|
|
900
|
+
background: var(--surface);
|
|
901
|
+
border: 1px solid var(--border);
|
|
902
|
+
border-radius: var(--radius);
|
|
903
|
+
box-shadow: var(--shadow);
|
|
904
|
+
scroll-margin: 90px;
|
|
905
|
+
}
|
|
906
|
+
/*
|
|
907
|
+
* Three states used to share one look: selected, priority and running were all
|
|
908
|
+
* "accent border", so the one that matters most was indistinguishable from a
|
|
909
|
+
* keyboard cursor. They're now ranked — selected is quietest, running loudest.
|
|
910
|
+
*/
|
|
911
|
+
.item[data-selected='true'] {
|
|
912
|
+
border-color: var(--ink-3);
|
|
913
|
+
box-shadow: 0 0 0 1px var(--ink-3);
|
|
914
|
+
}
|
|
915
|
+
.item[data-priority='true'] {
|
|
916
|
+
border-left: 3px solid var(--accent);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/* The one you committed to. Four reinforcing cues, because missing it is the
|
|
920
|
+
* whole failure mode: a thick bar, a wash, a ring, and a live clock on the row. */
|
|
921
|
+
.item[data-running='true'],
|
|
922
|
+
.item[data-running='true'][data-selected='true'] {
|
|
923
|
+
border-color: var(--accent);
|
|
924
|
+
border-left: 5px solid var(--accent);
|
|
925
|
+
background: var(--accent-wash);
|
|
926
|
+
box-shadow: 0 0 0 2px var(--accent);
|
|
927
|
+
}
|
|
928
|
+
.item[data-running='true'][data-overrun='true'],
|
|
929
|
+
.item[data-running='true'][data-overrun='true'][data-selected='true'] {
|
|
930
|
+
border-color: var(--serious);
|
|
931
|
+
border-left-color: var(--serious);
|
|
932
|
+
box-shadow: 0 0 0 2px var(--serious);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/* While a session runs, everything else recedes — the competing rows are the
|
|
936
|
+
* distraction the timer exists to resist. Hover brings one back so the list is
|
|
937
|
+
* still usable without stopping the clock. */
|
|
938
|
+
body[data-session-active='true'] #today .item:not([data-running='true']) {
|
|
939
|
+
opacity: 0.45;
|
|
940
|
+
}
|
|
941
|
+
body[data-session-active='true'] #today .item:not([data-running='true']):hover,
|
|
942
|
+
body[data-session-active='true'] #today .item:not([data-running='true']):focus-within {
|
|
943
|
+
opacity: 1;
|
|
944
|
+
}
|
|
945
|
+
.item[data-status='done'] .item__title,
|
|
946
|
+
.item[data-status='dismissed'] .item__title {
|
|
947
|
+
color: var(--ink-3);
|
|
948
|
+
text-decoration: line-through;
|
|
949
|
+
}
|
|
950
|
+
.item[data-pending='true'] {
|
|
951
|
+
opacity: 0.55;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
.item__mark {
|
|
955
|
+
padding-top: 4px;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
.item__dot {
|
|
959
|
+
width: 9px;
|
|
960
|
+
height: 9px;
|
|
961
|
+
border-radius: 50%;
|
|
962
|
+
display: block;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.item__body {
|
|
966
|
+
min-width: 0;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
.item__title {
|
|
970
|
+
margin: 0;
|
|
971
|
+
font-size: 14.5px;
|
|
972
|
+
font-weight: 500;
|
|
973
|
+
line-height: 1.4;
|
|
974
|
+
color: var(--ink-1);
|
|
975
|
+
overflow-wrap: anywhere;
|
|
976
|
+
}
|
|
977
|
+
.item__title a {
|
|
978
|
+
color: inherit;
|
|
979
|
+
text-decoration: none;
|
|
980
|
+
}
|
|
981
|
+
.item__title a:hover {
|
|
982
|
+
text-decoration: underline;
|
|
983
|
+
text-decoration-color: var(--accent);
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/*
|
|
987
|
+
* The `owner/repo` (and, on the board, `#number`) ahead of a title. Lighter than
|
|
988
|
+
* the title it sits on so it reads as an address rather than as the first words
|
|
989
|
+
* of the sentence — you find the row by its title and confirm it by this.
|
|
990
|
+
*/
|
|
991
|
+
.item__ref {
|
|
992
|
+
color: var(--ink-3);
|
|
993
|
+
font-weight: 400;
|
|
994
|
+
font-variant-numeric: tabular-nums;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
.item__meta {
|
|
998
|
+
display: flex;
|
|
999
|
+
flex-wrap: wrap;
|
|
1000
|
+
align-items: center;
|
|
1001
|
+
gap: 6px;
|
|
1002
|
+
margin-top: 5px;
|
|
1003
|
+
font-size: 12px;
|
|
1004
|
+
color: var(--ink-3);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
.item__source {
|
|
1008
|
+
color: var(--ink-2);
|
|
1009
|
+
font-weight: 500;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
.item__detail {
|
|
1013
|
+
margin: 6px 0 0;
|
|
1014
|
+
font-size: 13px;
|
|
1015
|
+
color: var(--ink-2);
|
|
1016
|
+
overflow-wrap: anywhere;
|
|
1017
|
+
}
|
|
1018
|
+
.item__detail code {
|
|
1019
|
+
padding: 1px 4px;
|
|
1020
|
+
background: var(--surface-sunken);
|
|
1021
|
+
border-radius: 3px;
|
|
1022
|
+
font-size: 0.92em;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/* A click anywhere on the card opens the panel, and the cursor says so. */
|
|
1026
|
+
.item {
|
|
1027
|
+
cursor: pointer;
|
|
1028
|
+
}
|
|
1029
|
+
/* The row the panel is open on: a bar on the edge the panel sits past. */
|
|
1030
|
+
.item[data-detail='true'] {
|
|
1031
|
+
border-right: 3px solid var(--accent);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/* pills */
|
|
1035
|
+
|
|
1036
|
+
.pill {
|
|
1037
|
+
display: inline-flex;
|
|
1038
|
+
align-items: center;
|
|
1039
|
+
gap: 4px;
|
|
1040
|
+
padding: 1px 7px;
|
|
1041
|
+
border-radius: 999px;
|
|
1042
|
+
border: 1px solid var(--border);
|
|
1043
|
+
font-size: 11.5px;
|
|
1044
|
+
line-height: 1.6;
|
|
1045
|
+
color: var(--ink-2);
|
|
1046
|
+
white-space: nowrap;
|
|
1047
|
+
}
|
|
1048
|
+
.pill--priority {
|
|
1049
|
+
border-color: transparent;
|
|
1050
|
+
background: var(--accent);
|
|
1051
|
+
color: #fff;
|
|
1052
|
+
font-weight: 600;
|
|
1053
|
+
font-variant-numeric: tabular-nums;
|
|
1054
|
+
}
|
|
1055
|
+
.pill--running {
|
|
1056
|
+
border-color: transparent;
|
|
1057
|
+
background: var(--accent);
|
|
1058
|
+
color: #fff;
|
|
1059
|
+
font-weight: 600;
|
|
1060
|
+
font-variant-numeric: tabular-nums;
|
|
1061
|
+
}
|
|
1062
|
+
.item[data-overrun='true'] .pill--running {
|
|
1063
|
+
background: var(--serious);
|
|
1064
|
+
color: #1a1a19;
|
|
1065
|
+
}
|
|
1066
|
+
.pill--objective {
|
|
1067
|
+
border-color: var(--accent);
|
|
1068
|
+
color: var(--accent);
|
|
1069
|
+
font-weight: 500;
|
|
1070
|
+
}
|
|
1071
|
+
.pill--overdue {
|
|
1072
|
+
border-color: var(--critical);
|
|
1073
|
+
color: var(--critical);
|
|
1074
|
+
font-weight: 500;
|
|
1075
|
+
}
|
|
1076
|
+
.pill--due {
|
|
1077
|
+
border-color: var(--serious);
|
|
1078
|
+
color: var(--ink-2);
|
|
1079
|
+
}
|
|
1080
|
+
.pill--age {
|
|
1081
|
+
border-color: var(--warning);
|
|
1082
|
+
color: var(--ink-2);
|
|
1083
|
+
}
|
|
1084
|
+
.pill--conflict {
|
|
1085
|
+
border-color: var(--critical);
|
|
1086
|
+
color: var(--critical);
|
|
1087
|
+
}
|
|
1088
|
+
.pill--tag {
|
|
1089
|
+
background: var(--surface-sunken);
|
|
1090
|
+
border-color: transparent;
|
|
1091
|
+
color: var(--ink-3);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/* actions */
|
|
1095
|
+
|
|
1096
|
+
.item__actions {
|
|
1097
|
+
position: absolute;
|
|
1098
|
+
top: 8px;
|
|
1099
|
+
right: 10px;
|
|
1100
|
+
display: flex;
|
|
1101
|
+
gap: 4px;
|
|
1102
|
+
opacity: 0;
|
|
1103
|
+
transition: opacity 90ms ease-out;
|
|
1104
|
+
background: var(--surface);
|
|
1105
|
+
padding-left: 6px;
|
|
1106
|
+
}
|
|
1107
|
+
.item:hover .item__actions,
|
|
1108
|
+
.item:focus-within .item__actions,
|
|
1109
|
+
.item[data-selected='true'] .item__actions {
|
|
1110
|
+
opacity: 1;
|
|
1111
|
+
}
|
|
1112
|
+
@media (hover: none) {
|
|
1113
|
+
.item__actions {
|
|
1114
|
+
opacity: 1;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
.button {
|
|
1119
|
+
padding: 3px 9px;
|
|
1120
|
+
border: 1px solid var(--border);
|
|
1121
|
+
border-radius: var(--radius-sm);
|
|
1122
|
+
background: var(--surface);
|
|
1123
|
+
color: var(--ink-2);
|
|
1124
|
+
font: inherit;
|
|
1125
|
+
font-size: 12px;
|
|
1126
|
+
cursor: pointer;
|
|
1127
|
+
white-space: nowrap;
|
|
1128
|
+
}
|
|
1129
|
+
.button:hover {
|
|
1130
|
+
color: var(--ink-1);
|
|
1131
|
+
border-color: var(--ink-3);
|
|
1132
|
+
}
|
|
1133
|
+
.button--primary {
|
|
1134
|
+
border-color: transparent;
|
|
1135
|
+
background: var(--accent);
|
|
1136
|
+
color: #fff;
|
|
1137
|
+
}
|
|
1138
|
+
.button--primary:hover {
|
|
1139
|
+
color: #fff;
|
|
1140
|
+
filter: brightness(1.08);
|
|
1141
|
+
}
|
|
1142
|
+
/* Not yet: it reads as waiting, not as the next thing to press. */
|
|
1143
|
+
.button:disabled,
|
|
1144
|
+
.button:disabled:hover {
|
|
1145
|
+
opacity: 0.45;
|
|
1146
|
+
cursor: not-allowed;
|
|
1147
|
+
filter: none;
|
|
1148
|
+
border-color: var(--border);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
/* note composer */
|
|
1152
|
+
|
|
1153
|
+
/* snooze menu */
|
|
1154
|
+
|
|
1155
|
+
.menu {
|
|
1156
|
+
position: absolute;
|
|
1157
|
+
top: 34px;
|
|
1158
|
+
right: 10px;
|
|
1159
|
+
z-index: 5;
|
|
1160
|
+
/* Wide enough that the date field below still shows its own placeholder. */
|
|
1161
|
+
min-width: 204px;
|
|
1162
|
+
padding: 5px;
|
|
1163
|
+
background: var(--surface);
|
|
1164
|
+
border: 1px solid var(--border);
|
|
1165
|
+
border-radius: var(--radius-sm);
|
|
1166
|
+
box-shadow: 0 6px 24px rgba(11, 11, 11, 0.14);
|
|
1167
|
+
}
|
|
1168
|
+
.menu button {
|
|
1169
|
+
display: block;
|
|
1170
|
+
width: 100%;
|
|
1171
|
+
padding: 5px 9px;
|
|
1172
|
+
border: 0;
|
|
1173
|
+
border-radius: 4px;
|
|
1174
|
+
background: none;
|
|
1175
|
+
color: var(--ink-2);
|
|
1176
|
+
font: inherit;
|
|
1177
|
+
font-size: 13px;
|
|
1178
|
+
text-align: left;
|
|
1179
|
+
cursor: pointer;
|
|
1180
|
+
}
|
|
1181
|
+
.menu button:hover {
|
|
1182
|
+
background: var(--surface-sunken);
|
|
1183
|
+
color: var(--ink-1);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/* The resolved date on the long-range presets, trailing its label. */
|
|
1187
|
+
.menu__when {
|
|
1188
|
+
float: right;
|
|
1189
|
+
padding-left: 12px;
|
|
1190
|
+
color: var(--ink-3);
|
|
1191
|
+
font-variant-numeric: tabular-nums;
|
|
1192
|
+
}
|
|
1193
|
+
.menu__date {
|
|
1194
|
+
display: flex;
|
|
1195
|
+
gap: 5px;
|
|
1196
|
+
padding: 5px 6px 2px;
|
|
1197
|
+
margin-top: 4px;
|
|
1198
|
+
border-top: 1px solid var(--hairline);
|
|
1199
|
+
}
|
|
1200
|
+
/*
|
|
1201
|
+
* The presets above are stacked, so `.menu button` makes them full-width. This
|
|
1202
|
+
* row is a flex line instead, and a 100% basis there lets "Set" claim the whole
|
|
1203
|
+
* width and squeeze the date field down to its calendar icon — an empty box.
|
|
1204
|
+
*/
|
|
1205
|
+
.menu__date button {
|
|
1206
|
+
flex: 0 0 auto;
|
|
1207
|
+
width: auto;
|
|
1208
|
+
text-align: center;
|
|
1209
|
+
}
|
|
1210
|
+
.menu__date input {
|
|
1211
|
+
flex: 1;
|
|
1212
|
+
min-width: 5.5em;
|
|
1213
|
+
padding: 3px 6px;
|
|
1214
|
+
border: 1px solid var(--border);
|
|
1215
|
+
border-radius: 4px;
|
|
1216
|
+
background: var(--page);
|
|
1217
|
+
color: var(--ink-1);
|
|
1218
|
+
font: inherit;
|
|
1219
|
+
font-size: 12px;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
/* ---------- agenda ---------- */
|
|
1223
|
+
|
|
1224
|
+
.agenda {
|
|
1225
|
+
padding: 14px;
|
|
1226
|
+
background: var(--surface);
|
|
1227
|
+
border: 1px solid var(--border);
|
|
1228
|
+
border-radius: var(--radius);
|
|
1229
|
+
box-shadow: var(--shadow);
|
|
1230
|
+
position: sticky;
|
|
1231
|
+
top: 16px;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
.agenda__title {
|
|
1235
|
+
margin: 0 0 10px;
|
|
1236
|
+
font-size: 12px;
|
|
1237
|
+
font-weight: 600;
|
|
1238
|
+
letter-spacing: 0.06em;
|
|
1239
|
+
text-transform: uppercase;
|
|
1240
|
+
color: var(--ink-2);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
/*
|
|
1244
|
+
* Why the agenda reads the way it does, when the calendar isn't the source.
|
|
1245
|
+
* Sits under the heading rather than in the page banners: it is about these rows.
|
|
1246
|
+
*/
|
|
1247
|
+
.agenda__note {
|
|
1248
|
+
margin: 0 0 10px;
|
|
1249
|
+
padding: 6px 8px;
|
|
1250
|
+
border-radius: 6px;
|
|
1251
|
+
font-size: 11px;
|
|
1252
|
+
line-height: 1.4;
|
|
1253
|
+
color: var(--ink-2);
|
|
1254
|
+
background: var(--surface-sunken);
|
|
1255
|
+
border-left: 2px solid var(--ink-3);
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/* Falling back to the brief is a louder thing than a merely stale read. */
|
|
1259
|
+
.agenda__note--stale {
|
|
1260
|
+
border-left-color: var(--warning);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
.agenda__list {
|
|
1264
|
+
list-style: none;
|
|
1265
|
+
margin: 0;
|
|
1266
|
+
padding: 0;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
.agenda__row {
|
|
1270
|
+
display: grid;
|
|
1271
|
+
/* Wide enough for a 12-hour "09:30 AM" without wrapping. */
|
|
1272
|
+
grid-template-columns: 64px minmax(0, 1fr);
|
|
1273
|
+
gap: 10px;
|
|
1274
|
+
padding: 6px 0;
|
|
1275
|
+
border-top: 1px solid var(--hairline);
|
|
1276
|
+
font-size: 13px;
|
|
1277
|
+
}
|
|
1278
|
+
.agenda__row:first-child {
|
|
1279
|
+
border-top: 0;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
.agenda__time {
|
|
1283
|
+
color: var(--ink-3);
|
|
1284
|
+
font-size: 12px;
|
|
1285
|
+
font-variant-numeric: tabular-nums; /* a column of times, so tabular is right here */
|
|
1286
|
+
white-space: nowrap;
|
|
1287
|
+
padding-top: 1px;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
.agenda__name {
|
|
1291
|
+
color: var(--ink-1);
|
|
1292
|
+
overflow-wrap: anywhere;
|
|
1293
|
+
}
|
|
1294
|
+
/* Event links take the row's ink so past events actually read as past. */
|
|
1295
|
+
.agenda__name a {
|
|
1296
|
+
color: inherit;
|
|
1297
|
+
text-decoration: none;
|
|
1298
|
+
}
|
|
1299
|
+
.agenda__name a:hover {
|
|
1300
|
+
text-decoration: underline;
|
|
1301
|
+
text-decoration-color: var(--accent);
|
|
1302
|
+
}
|
|
1303
|
+
.agenda__row[data-past='true'] .agenda__name,
|
|
1304
|
+
.agenda__row[data-past='true'] .agenda__time {
|
|
1305
|
+
color: var(--ink-3);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
/*
|
|
1309
|
+
* An event that sits on the day without taking any of it: a delivery window, a
|
|
1310
|
+
* booking. One step down in emphasis and deliberately not the `[data-past]`
|
|
1311
|
+
* dimming, because being home for the delivery is the entire point of it being
|
|
1312
|
+
* on the agenda — a 14:30 slot that reads as already over is a worse mistake
|
|
1313
|
+
* than one that reads as a meeting. The row also says "marked free" in text,
|
|
1314
|
+
* since ink is never the only cue here.
|
|
1315
|
+
*/
|
|
1316
|
+
.agenda__row[data-blocking='false'] .agenda__name {
|
|
1317
|
+
color: var(--ink-2);
|
|
1318
|
+
}
|
|
1319
|
+
.agenda__sub {
|
|
1320
|
+
display: block;
|
|
1321
|
+
color: var(--ink-3);
|
|
1322
|
+
font-size: 12px;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
.agenda__row--free .agenda__name {
|
|
1326
|
+
color: var(--good-ink);
|
|
1327
|
+
font-weight: 500;
|
|
1328
|
+
}
|
|
1329
|
+
.agenda__row--now {
|
|
1330
|
+
align-items: center;
|
|
1331
|
+
padding: 2px 0;
|
|
1332
|
+
border-top: 0;
|
|
1333
|
+
}
|
|
1334
|
+
.agenda__now-label {
|
|
1335
|
+
color: var(--critical);
|
|
1336
|
+
font-size: 11px;
|
|
1337
|
+
font-weight: 600;
|
|
1338
|
+
font-variant-numeric: tabular-nums;
|
|
1339
|
+
white-space: nowrap;
|
|
1340
|
+
}
|
|
1341
|
+
.agenda__now-line {
|
|
1342
|
+
height: 2px;
|
|
1343
|
+
background: var(--critical);
|
|
1344
|
+
border-radius: 1px;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
.empty {
|
|
1348
|
+
margin: 0;
|
|
1349
|
+
padding: 18px 4px;
|
|
1350
|
+
color: var(--ink-3);
|
|
1351
|
+
font-size: 13.5px;
|
|
1352
|
+
text-align: center;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
/* ---------- done drawer ---------- */
|
|
1356
|
+
|
|
1357
|
+
.drawer {
|
|
1358
|
+
margin-top: 8px;
|
|
1359
|
+
border: 1px solid var(--border);
|
|
1360
|
+
border-radius: var(--radius);
|
|
1361
|
+
background: var(--surface);
|
|
1362
|
+
}
|
|
1363
|
+
.drawer > summary {
|
|
1364
|
+
padding: 9px 13px;
|
|
1365
|
+
cursor: pointer;
|
|
1366
|
+
color: var(--ink-2);
|
|
1367
|
+
font-size: 13px;
|
|
1368
|
+
list-style: none;
|
|
1369
|
+
}
|
|
1370
|
+
.drawer > summary::-webkit-details-marker {
|
|
1371
|
+
display: none;
|
|
1372
|
+
}
|
|
1373
|
+
.drawer > summary::before {
|
|
1374
|
+
content: '▸ ';
|
|
1375
|
+
color: var(--ink-3);
|
|
1376
|
+
}
|
|
1377
|
+
.drawer[open] > summary::before {
|
|
1378
|
+
content: '▾ ';
|
|
1379
|
+
}
|
|
1380
|
+
.drawer .list {
|
|
1381
|
+
padding: 0 8px 8px;
|
|
1382
|
+
}
|
|
1383
|
+
.drawer .item {
|
|
1384
|
+
box-shadow: none;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
/* ---------- toast ---------- */
|
|
1388
|
+
|
|
1389
|
+
.toast {
|
|
1390
|
+
position: fixed;
|
|
1391
|
+
left: 50%;
|
|
1392
|
+
bottom: 22px;
|
|
1393
|
+
transform: translateX(-50%);
|
|
1394
|
+
z-index: 20;
|
|
1395
|
+
display: flex;
|
|
1396
|
+
align-items: center;
|
|
1397
|
+
gap: 12px;
|
|
1398
|
+
padding: 9px 14px;
|
|
1399
|
+
background: var(--ink-1);
|
|
1400
|
+
color: var(--page);
|
|
1401
|
+
border-radius: 999px;
|
|
1402
|
+
font-size: 13px;
|
|
1403
|
+
box-shadow: 0 6px 24px rgba(11, 11, 11, 0.25);
|
|
1404
|
+
}
|
|
1405
|
+
.toast button {
|
|
1406
|
+
border: 0;
|
|
1407
|
+
background: none;
|
|
1408
|
+
color: var(--page);
|
|
1409
|
+
font: inherit;
|
|
1410
|
+
font-weight: 600;
|
|
1411
|
+
text-decoration: underline;
|
|
1412
|
+
cursor: pointer;
|
|
1413
|
+
padding: 0;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
/* ---------- help dialog ---------- */
|
|
1417
|
+
|
|
1418
|
+
.help {
|
|
1419
|
+
max-width: 420px;
|
|
1420
|
+
padding: 22px;
|
|
1421
|
+
border: 1px solid var(--border);
|
|
1422
|
+
border-radius: var(--radius);
|
|
1423
|
+
background: var(--surface);
|
|
1424
|
+
color: var(--ink-1);
|
|
1425
|
+
}
|
|
1426
|
+
.help::backdrop {
|
|
1427
|
+
background: rgba(11, 11, 11, 0.4);
|
|
1428
|
+
}
|
|
1429
|
+
.help h2 {
|
|
1430
|
+
margin: 0 0 14px;
|
|
1431
|
+
font-size: 16px;
|
|
1432
|
+
}
|
|
1433
|
+
.help dl {
|
|
1434
|
+
margin: 0 0 14px;
|
|
1435
|
+
display: grid;
|
|
1436
|
+
gap: 6px;
|
|
1437
|
+
}
|
|
1438
|
+
.help dl > div {
|
|
1439
|
+
display: grid;
|
|
1440
|
+
grid-template-columns: 96px 1fr;
|
|
1441
|
+
gap: 10px;
|
|
1442
|
+
font-size: 13.5px;
|
|
1443
|
+
}
|
|
1444
|
+
.help dt {
|
|
1445
|
+
color: var(--ink-2);
|
|
1446
|
+
}
|
|
1447
|
+
.help dd {
|
|
1448
|
+
margin: 0;
|
|
1449
|
+
color: var(--ink-2);
|
|
1450
|
+
}
|
|
1451
|
+
.help__note {
|
|
1452
|
+
margin: 0 0 16px;
|
|
1453
|
+
font-size: 13px;
|
|
1454
|
+
color: var(--ink-3);
|
|
1455
|
+
}
|
|
1456
|
+
kbd {
|
|
1457
|
+
padding: 1px 5px;
|
|
1458
|
+
border: 1px solid var(--border);
|
|
1459
|
+
border-bottom-width: 2px;
|
|
1460
|
+
border-radius: 4px;
|
|
1461
|
+
background: var(--surface-sunken);
|
|
1462
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1463
|
+
font-size: 11.5px;
|
|
1464
|
+
color: var(--ink-1);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1468
|
+
* {
|
|
1469
|
+
transition: none !important;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
/* ---------- tabs ---------- */
|
|
1474
|
+
|
|
1475
|
+
/*
|
|
1476
|
+
* Two views, one page. Today is the brief; the board is the live list of pull
|
|
1477
|
+
* requests. They're tabs rather than one long page because the brief is edited
|
|
1478
|
+
* down to a short list on purpose, and a wall of PRs beneath it would undo that.
|
|
1479
|
+
*/
|
|
1480
|
+
.tabs {
|
|
1481
|
+
display: flex;
|
|
1482
|
+
gap: 4px;
|
|
1483
|
+
max-width: 1280px;
|
|
1484
|
+
margin: -6px auto 14px;
|
|
1485
|
+
border-bottom: 1px solid var(--hairline);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
.tab {
|
|
1489
|
+
display: inline-flex;
|
|
1490
|
+
align-items: center;
|
|
1491
|
+
gap: 7px;
|
|
1492
|
+
padding: 7px 12px 9px;
|
|
1493
|
+
margin-bottom: -1px;
|
|
1494
|
+
border: 0;
|
|
1495
|
+
border-bottom: 2px solid transparent;
|
|
1496
|
+
background: none;
|
|
1497
|
+
color: var(--ink-2);
|
|
1498
|
+
font: inherit;
|
|
1499
|
+
font-size: 13.5px;
|
|
1500
|
+
font-weight: 500;
|
|
1501
|
+
cursor: pointer;
|
|
1502
|
+
}
|
|
1503
|
+
.tab:hover {
|
|
1504
|
+
color: var(--ink-1);
|
|
1505
|
+
}
|
|
1506
|
+
.tab[aria-selected='true'] {
|
|
1507
|
+
color: var(--ink-1);
|
|
1508
|
+
border-bottom-color: var(--accent);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
.tab__badge {
|
|
1512
|
+
display: inline-grid;
|
|
1513
|
+
place-items: center;
|
|
1514
|
+
min-width: 18px;
|
|
1515
|
+
height: 18px;
|
|
1516
|
+
padding: 0 5px;
|
|
1517
|
+
border-radius: 999px;
|
|
1518
|
+
background: var(--critical);
|
|
1519
|
+
color: #fff;
|
|
1520
|
+
font-size: 11px;
|
|
1521
|
+
font-weight: 600;
|
|
1522
|
+
font-variant-numeric: tabular-nums;
|
|
1523
|
+
line-height: 1;
|
|
1524
|
+
cursor: help;
|
|
1525
|
+
}
|
|
1526
|
+
/*
|
|
1527
|
+
* Centring the line box doesn't centre the digits: where they sit inside it
|
|
1528
|
+
* depends on the font's ascent and descent, which differ by browser, and in
|
|
1529
|
+
* an 18px circle a pixel off is visible. Trimmed to cap height and baseline,
|
|
1530
|
+
* the line box is exactly the digits, so equal padding centres them. The
|
|
1531
|
+
* trim only applies to a block container's own lines, not to the anonymous
|
|
1532
|
+
* item a grid wraps its text in, hence inline-block.
|
|
1533
|
+
*/
|
|
1534
|
+
@supports (text-box: trim-both cap alphabetic) {
|
|
1535
|
+
.tab__badge {
|
|
1536
|
+
display: inline-block;
|
|
1537
|
+
height: auto;
|
|
1538
|
+
padding: calc((18px - 1cap) / 2) 5px;
|
|
1539
|
+
text-align: center;
|
|
1540
|
+
text-box: trim-both cap alphabetic;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
/*
|
|
1545
|
+
* Which view is showing. Focus mode is scoped to Today (see the rules above).
|
|
1546
|
+
*
|
|
1547
|
+
* One rule per panel per view rather than `:not()`, because a view name nobody
|
|
1548
|
+
* has written a rule for should show nothing rather than everything at once.
|
|
1549
|
+
*/
|
|
1550
|
+
body[data-view='board'] #today,
|
|
1551
|
+
body[data-view='tickets'] #today,
|
|
1552
|
+
body[data-view='settings'] #today,
|
|
1553
|
+
body[data-view='board'] .layout__side,
|
|
1554
|
+
body[data-view='tickets'] .layout__side,
|
|
1555
|
+
body[data-view='settings'] .layout__side {
|
|
1556
|
+
display: none;
|
|
1557
|
+
}
|
|
1558
|
+
body[data-view='today'] #board,
|
|
1559
|
+
body[data-view='tickets'] #board,
|
|
1560
|
+
body[data-view='settings'] #board {
|
|
1561
|
+
display: none;
|
|
1562
|
+
}
|
|
1563
|
+
body[data-view='today'] #tickets,
|
|
1564
|
+
body[data-view='board'] #tickets,
|
|
1565
|
+
body[data-view='settings'] #tickets {
|
|
1566
|
+
display: none;
|
|
1567
|
+
}
|
|
1568
|
+
body[data-view='today'] #settings,
|
|
1569
|
+
body[data-view='board'] #settings,
|
|
1570
|
+
body[data-view='tickets'] #settings {
|
|
1571
|
+
display: none;
|
|
1572
|
+
}
|
|
1573
|
+
body[data-view='board'] .layout,
|
|
1574
|
+
body[data-view='tickets'] .layout,
|
|
1575
|
+
body[data-view='settings'] .layout {
|
|
1576
|
+
grid-template-columns: minmax(0, 1fr);
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
/* ---------- the pull request board ---------- */
|
|
1580
|
+
|
|
1581
|
+
|
|
1582
|
+
/* The line under a waiting-on-you row that says why. Reads as the row's detail. */
|
|
1583
|
+
.item__reason {
|
|
1584
|
+
margin: 6px 0 0;
|
|
1585
|
+
font-size: 13px;
|
|
1586
|
+
color: var(--critical);
|
|
1587
|
+
overflow-wrap: anywhere;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
/*
|
|
1591
|
+
* Waiting on a gate or on checks is not a failure, so it doesn't get the red the
|
|
1592
|
+
* "your move" reasons use. Nothing has gone wrong; it just isn't finished.
|
|
1593
|
+
*/
|
|
1594
|
+
.item__reason--waiting {
|
|
1595
|
+
color: var(--ink-3);
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
.item__reason--waiting a {
|
|
1599
|
+
color: inherit;
|
|
1600
|
+
text-decoration: underline;
|
|
1601
|
+
text-decoration-style: dotted;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
.pill--good {
|
|
1605
|
+
border-color: var(--good);
|
|
1606
|
+
color: var(--good-ink);
|
|
1607
|
+
font-weight: 500;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
.item--pull[data-status='snoozed'] .item__title,
|
|
1611
|
+
.item--ticket[data-status='snoozed'] .item__title {
|
|
1612
|
+
color: var(--ink-3);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
/* ---------- the Jira ticket board ---------- */
|
|
1616
|
+
|
|
1617
|
+
/*
|
|
1618
|
+
* Two columns, by measurement rather than by taste: the rows were
|
|
1619
|
+
* 1200px wide for a summary that runs 520px at the median and 683px at the
|
|
1620
|
+
* longest, so half of every card was empty and thirty of them came to 2527px of
|
|
1621
|
+
* scrolling. At 460px a column the same thirty fit in half that.
|
|
1622
|
+
*
|
|
1623
|
+
* Multicol rather than a grid, which is what this was and which was wrong for a
|
|
1624
|
+
* board whose cards disagree about height. A grid lays out in *rows*, so a
|
|
1625
|
+
* one-line card beside a card carrying a note gets the note's height reserved
|
|
1626
|
+
* next to it and left empty — `align-items: start` stops it stretching but
|
|
1627
|
+
* cannot reclaim the row, and one noted card on a real board cost about a
|
|
1628
|
+
* hundred pixels of nothing. A column flow has no rows to align against: each
|
|
1629
|
+
* card starts where the one above it ended.
|
|
1630
|
+
*
|
|
1631
|
+
* `break-inside: avoid` so a card is never split down the middle of a column,
|
|
1632
|
+
* and the vertical rhythm is a margin rather than `gap`, which a multicol
|
|
1633
|
+
* container does not have.
|
|
1634
|
+
*
|
|
1635
|
+
* Never more than two, so the board keeps the shared page width and its edges
|
|
1636
|
+
* line up with the other tabs' however wide the display is. The 460px floor is
|
|
1637
|
+
* what drops it to one column on a narrow window rather than squeezing two.
|
|
1638
|
+
*
|
|
1639
|
+
* The abspos worry that picked the grid does not apply: `.item` is
|
|
1640
|
+
* `position: relative`, so `.item__actions` and the snooze `.menu` resolve
|
|
1641
|
+
* against the card and travel with it into its column. Only a child resolving
|
|
1642
|
+
* against the multicol container itself would land beside the wrong row.
|
|
1643
|
+
*/
|
|
1644
|
+
#tickets .list {
|
|
1645
|
+
display: block;
|
|
1646
|
+
columns: 2 460px;
|
|
1647
|
+
column-gap: 10px;
|
|
1648
|
+
}
|
|
1649
|
+
#tickets .list > .item {
|
|
1650
|
+
break-inside: avoid;
|
|
1651
|
+
margin-bottom: 4px;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
/*
|
|
1655
|
+
* A fixed gutter for the status, so every summary starts at the same x and the
|
|
1656
|
+
* statuses read as a column you can run an eye down. Wide enough for the longest
|
|
1657
|
+
* status seen (79px) with room; a longer one shrinks its own pill rather than
|
|
1658
|
+
* moving the summaries, which is the right way round for a board scanned by
|
|
1659
|
+
* column.
|
|
1660
|
+
*/
|
|
1661
|
+
#tickets .item--ticket {
|
|
1662
|
+
padding: 8px 11px;
|
|
1663
|
+
}
|
|
1664
|
+
#tickets .item__body {
|
|
1665
|
+
display: grid;
|
|
1666
|
+
grid-template-columns: 6rem minmax(0, 1fr);
|
|
1667
|
+
align-items: baseline;
|
|
1668
|
+
gap: 4px 9px;
|
|
1669
|
+
}
|
|
1670
|
+
#tickets .item__meta--status {
|
|
1671
|
+
grid-column: 1;
|
|
1672
|
+
grid-row: 1;
|
|
1673
|
+
margin-top: 0;
|
|
1674
|
+
flex-wrap: nowrap;
|
|
1675
|
+
}
|
|
1676
|
+
/*
|
|
1677
|
+
* The reserve on the right is the action strip's width. `.item__actions` is
|
|
1678
|
+
* absolutely positioned over the card's top right and opaque, so without this it
|
|
1679
|
+
* covers the tail of any summary long enough to reach it — on hover, and
|
|
1680
|
+
* permanently on the selected row, which is the row being read. Measured rather
|
|
1681
|
+
* than guessed: reserving the pull request board's three buttons cost 142px of
|
|
1682
|
+
* board height, and dropping Open — which only duplicated the link the summary
|
|
1683
|
+
* already is — brought it to 87px for the same "nothing is ever hidden".
|
|
1684
|
+
*/
|
|
1685
|
+
#tickets .item__title {
|
|
1686
|
+
grid-column: 2;
|
|
1687
|
+
grid-row: 1;
|
|
1688
|
+
padding-right: 104px;
|
|
1689
|
+
font-size: 13.5px;
|
|
1690
|
+
}
|
|
1691
|
+
/* The exceptions, the notes and the note form all span both columns. */
|
|
1692
|
+
#tickets .item__meta--extra,
|
|
1693
|
+
#tickets .item__notes,
|
|
1694
|
+
#tickets .note-form {
|
|
1695
|
+
grid-column: 1 / -1;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
/*
|
|
1699
|
+
* The switch between the ticket tab's two views: what is out of sync, and what
|
|
1700
|
+
* you are working on. A segmented control rather than a second row of tabs, so it
|
|
1701
|
+
* reads as a way of looking at this tab and not as more places to go — the tabs
|
|
1702
|
+
* above are underlined words, the status line below is a bordered bar, and this
|
|
1703
|
+
* is neither.
|
|
1704
|
+
*/
|
|
1705
|
+
/*
|
|
1706
|
+
* The switch and the key to the dots share a line: both describe the view
|
|
1707
|
+
* below rather than being part of it, and stacked they made a third tier under
|
|
1708
|
+
* the tabs, with the key hanging off the switch like a caption. The key goes
|
|
1709
|
+
* to the far end and wraps beneath the switch when the line runs out.
|
|
1710
|
+
*/
|
|
1711
|
+
.ticket-toolbar {
|
|
1712
|
+
display: flex;
|
|
1713
|
+
flex-wrap: wrap;
|
|
1714
|
+
align-items: center;
|
|
1715
|
+
justify-content: space-between;
|
|
1716
|
+
gap: 8px 24px;
|
|
1717
|
+
margin: 0 0 18px;
|
|
1718
|
+
}
|
|
1719
|
+
.mode-switch {
|
|
1720
|
+
display: inline-flex;
|
|
1721
|
+
gap: 2px;
|
|
1722
|
+
padding: 3px;
|
|
1723
|
+
background: var(--surface-sunken);
|
|
1724
|
+
border: 1px solid var(--hairline);
|
|
1725
|
+
border-radius: 999px;
|
|
1726
|
+
}
|
|
1727
|
+
.mode-switch__option {
|
|
1728
|
+
display: inline-flex;
|
|
1729
|
+
align-items: center;
|
|
1730
|
+
gap: 7px;
|
|
1731
|
+
padding: 5px 14px;
|
|
1732
|
+
border: 0;
|
|
1733
|
+
border-radius: 999px;
|
|
1734
|
+
background: none;
|
|
1735
|
+
color: var(--ink-2);
|
|
1736
|
+
font: inherit;
|
|
1737
|
+
font-size: 13px;
|
|
1738
|
+
font-weight: 500;
|
|
1739
|
+
cursor: pointer;
|
|
1740
|
+
}
|
|
1741
|
+
.mode-switch__option:hover {
|
|
1742
|
+
color: var(--ink-1);
|
|
1743
|
+
}
|
|
1744
|
+
.mode-switch__option[aria-selected='true'] {
|
|
1745
|
+
background: var(--surface);
|
|
1746
|
+
color: var(--ink-1);
|
|
1747
|
+
box-shadow: var(--shadow);
|
|
1748
|
+
}
|
|
1749
|
+
.mode-switch__count {
|
|
1750
|
+
color: var(--ink-3);
|
|
1751
|
+
font-size: 12px;
|
|
1752
|
+
font-variant-numeric: tabular-nums;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
/*
|
|
1756
|
+
* On a Working on row whose ticket is also out of sync. The border is the due
|
|
1757
|
+
* pill's, because this is the same kind of claim — something here wants doing —
|
|
1758
|
+
* and the text says what, so the colour is never the only thing carrying it.
|
|
1759
|
+
*/
|
|
1760
|
+
.pill--flag {
|
|
1761
|
+
border-color: var(--serious);
|
|
1762
|
+
background: transparent;
|
|
1763
|
+
color: var(--ink-2);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
/*
|
|
1767
|
+
* The key to the dots. Sits at the end of the switch's line, read once — and
|
|
1768
|
+
* carries each type's name in text beside its swatch, which is what lets the
|
|
1769
|
+
* dot be the only place the type appears on a row.
|
|
1770
|
+
*/
|
|
1771
|
+
.legend {
|
|
1772
|
+
display: flex;
|
|
1773
|
+
flex-wrap: wrap;
|
|
1774
|
+
gap: 4px 14px;
|
|
1775
|
+
/* A <p>, and its default margins made the switch's line taller whenever the key was on it. */
|
|
1776
|
+
margin: 0;
|
|
1777
|
+
font-size: 12px;
|
|
1778
|
+
color: var(--ink-3);
|
|
1779
|
+
}
|
|
1780
|
+
.legend__entry {
|
|
1781
|
+
display: inline-flex;
|
|
1782
|
+
align-items: center;
|
|
1783
|
+
gap: 6px;
|
|
1784
|
+
}
|
|
1785
|
+
.legend__dot {
|
|
1786
|
+
width: 9px;
|
|
1787
|
+
height: 9px;
|
|
1788
|
+
border-radius: 50%;
|
|
1789
|
+
display: block;
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
/*
|
|
1793
|
+
* A count, not an alarm. The board's badge is red because something is waiting
|
|
1794
|
+
* on you; nothing on the ticket tab ever is, and a red number that is there
|
|
1795
|
+
* every single morning is one you stop reading by the end of the week.
|
|
1796
|
+
*/
|
|
1797
|
+
.tab__badge--quiet {
|
|
1798
|
+
background: var(--surface-sunken);
|
|
1799
|
+
/* A shadow, not a border, so it adds nothing to the padding that centres the digits. */
|
|
1800
|
+
box-shadow: inset 0 0 0 1px var(--hairline);
|
|
1801
|
+
color: var(--ink-2);
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
/*
|
|
1805
|
+
* What to do about a court, under its rows rather than above them.
|
|
1806
|
+
*
|
|
1807
|
+
* Above the list it would be read once and then skipped past every morning on
|
|
1808
|
+
* the way to the rows; below, it is where the eye lands after running out of
|
|
1809
|
+
* rows, which is when "a pull request nobody has written yet is invisible from
|
|
1810
|
+
* here" is actually worth knowing.
|
|
1811
|
+
*/
|
|
1812
|
+
.court-hint {
|
|
1813
|
+
margin: -6px 0 18px;
|
|
1814
|
+
padding-left: 26px;
|
|
1815
|
+
font-size: 12.5px;
|
|
1816
|
+
line-height: 1.5;
|
|
1817
|
+
color: var(--ink-3);
|
|
1818
|
+
max-width: 68ch;
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
/*
|
|
1822
|
+
* The status doubles as the control that changes it, and looks exactly like the
|
|
1823
|
+
* pill it replaced — no underline, no marker. A persistent "this is clickable"
|
|
1824
|
+
* hint is ink spent every row of every morning to say something the row only
|
|
1825
|
+
* needs to say when you go looking for it, so the cursor and the hover carry it
|
|
1826
|
+
* instead. The rows are dense enough that finding out costs one hover.
|
|
1827
|
+
*/
|
|
1828
|
+
.pill--button {
|
|
1829
|
+
font: inherit;
|
|
1830
|
+
font-size: 11.5px;
|
|
1831
|
+
cursor: pointer;
|
|
1832
|
+
}
|
|
1833
|
+
.pill--button:hover {
|
|
1834
|
+
border-color: var(--ink-3);
|
|
1835
|
+
color: var(--ink-1);
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
/*
|
|
1839
|
+
* Anchored to the gutter rather than the card's right edge, unlike the park
|
|
1840
|
+
* menu: it belongs to the pill that opened it, and the two menus must not be
|
|
1841
|
+
* able to appear in the same place.
|
|
1842
|
+
*/
|
|
1843
|
+
.menu--status {
|
|
1844
|
+
top: 26px;
|
|
1845
|
+
right: auto;
|
|
1846
|
+
left: 0;
|
|
1847
|
+
min-width: 9rem;
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
/* The status the ticket claims — the row's own subject, so it carries weight. */
|
|
1851
|
+
.pill--status {
|
|
1852
|
+
border-color: var(--hairline);
|
|
1853
|
+
background: var(--surface-sunken);
|
|
1854
|
+
color: var(--ink-1);
|
|
1855
|
+
font-weight: 500;
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
/* An anchor styled as a button sits a hair lower than a real one; align them. */
|
|
1859
|
+
a.button {
|
|
1860
|
+
display: inline-flex;
|
|
1861
|
+
align-items: center;
|
|
1862
|
+
text-decoration: none;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
/* ---------- the assistant ---------- */
|
|
1866
|
+
|
|
1867
|
+
.pill--assistant {
|
|
1868
|
+
color: var(--accent);
|
|
1869
|
+
background: var(--accent-wash);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
/*
|
|
1873
|
+
* ---------- the assistant ----------
|
|
1874
|
+
*
|
|
1875
|
+
* A section of the item panel, so the transcript scrolls with the panel's body
|
|
1876
|
+
* and the field sits at its foot. It used to be a box under the card with its own
|
|
1877
|
+
* scroll region; now the panel is the box.
|
|
1878
|
+
*/
|
|
1879
|
+
|
|
1880
|
+
.assistant__turns {
|
|
1881
|
+
display: flex;
|
|
1882
|
+
flex-direction: column;
|
|
1883
|
+
gap: 12px;
|
|
1884
|
+
margin-bottom: 14px;
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
.assistant__request {
|
|
1888
|
+
margin: 0;
|
|
1889
|
+
font-size: 12.5px;
|
|
1890
|
+
color: var(--ink-2);
|
|
1891
|
+
}
|
|
1892
|
+
.assistant__request strong {
|
|
1893
|
+
color: var(--ink-1);
|
|
1894
|
+
font-weight: 600;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
.assistant__reply {
|
|
1898
|
+
margin-top: 5px;
|
|
1899
|
+
padding: 7px 10px;
|
|
1900
|
+
border-left: 2px solid var(--accent);
|
|
1901
|
+
background: var(--surface);
|
|
1902
|
+
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
|
|
1903
|
+
font-size: 13px;
|
|
1904
|
+
line-height: 1.45;
|
|
1905
|
+
color: var(--ink-1);
|
|
1906
|
+
overflow-wrap: anywhere;
|
|
1907
|
+
}
|
|
1908
|
+
.assistant__reply > * {
|
|
1909
|
+
margin: 0;
|
|
1910
|
+
}
|
|
1911
|
+
.assistant__reply > * + * {
|
|
1912
|
+
margin-top: 7px;
|
|
1913
|
+
}
|
|
1914
|
+
.assistant__reply ul,
|
|
1915
|
+
.assistant__reply ol {
|
|
1916
|
+
padding-left: 20px;
|
|
1917
|
+
}
|
|
1918
|
+
.assistant__reply li + li {
|
|
1919
|
+
margin-top: 3px;
|
|
1920
|
+
}
|
|
1921
|
+
.assistant__reply pre {
|
|
1922
|
+
padding: 7px 9px;
|
|
1923
|
+
border-radius: var(--radius-sm);
|
|
1924
|
+
background: var(--surface-sunken);
|
|
1925
|
+
font-size: 12px;
|
|
1926
|
+
overflow-x: auto;
|
|
1927
|
+
white-space: pre-wrap;
|
|
1928
|
+
}
|
|
1929
|
+
.assistant__reply code {
|
|
1930
|
+
font-size: 12px;
|
|
1931
|
+
}
|
|
1932
|
+
.assistant__reply blockquote {
|
|
1933
|
+
padding: 2px 10px;
|
|
1934
|
+
border-left: 2px solid var(--hairline);
|
|
1935
|
+
color: var(--ink-2);
|
|
1936
|
+
}
|
|
1937
|
+
.assistant__reply hr {
|
|
1938
|
+
border: 0;
|
|
1939
|
+
border-top: 1px solid var(--hairline);
|
|
1940
|
+
}
|
|
1941
|
+
.assistant__turn[data-status='running'] .assistant__reply {
|
|
1942
|
+
border-left-style: dashed;
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
.assistant__status {
|
|
1946
|
+
margin: 4px 0 0;
|
|
1947
|
+
font-size: 12px;
|
|
1948
|
+
color: var(--ink-3);
|
|
1949
|
+
}
|
|
1950
|
+
.assistant__status--bad {
|
|
1951
|
+
color: var(--critical);
|
|
1952
|
+
}
|
|
1953
|
+
.assistant[data-running='true'] .assistant__status:not(.assistant__status--bad)::after {
|
|
1954
|
+
content: '';
|
|
1955
|
+
display: inline-block;
|
|
1956
|
+
width: 8px;
|
|
1957
|
+
height: 8px;
|
|
1958
|
+
margin-left: 6px;
|
|
1959
|
+
border-radius: 50%;
|
|
1960
|
+
border: 1.5px solid var(--accent);
|
|
1961
|
+
border-top-color: transparent;
|
|
1962
|
+
animation: assistant-spin 0.9s linear infinite;
|
|
1963
|
+
vertical-align: -1px;
|
|
1964
|
+
}
|
|
1965
|
+
@keyframes assistant-spin {
|
|
1966
|
+
to {
|
|
1967
|
+
transform: rotate(360deg);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1971
|
+
.assistant[data-running='true'] .assistant__status::after {
|
|
1972
|
+
animation: none;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
.assistant__quick {
|
|
1977
|
+
display: flex;
|
|
1978
|
+
flex-wrap: wrap;
|
|
1979
|
+
gap: 6px;
|
|
1980
|
+
margin-bottom: 14px;
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
.assistant__form {
|
|
1984
|
+
display: flex;
|
|
1985
|
+
gap: 8px;
|
|
1986
|
+
align-items: flex-end;
|
|
1987
|
+
}
|
|
1988
|
+
.assistant__input {
|
|
1989
|
+
flex: 1;
|
|
1990
|
+
min-width: 0;
|
|
1991
|
+
padding: 8px 10px;
|
|
1992
|
+
border: 1px solid var(--border);
|
|
1993
|
+
border-radius: var(--radius-sm);
|
|
1994
|
+
background: var(--page);
|
|
1995
|
+
color: var(--ink-1);
|
|
1996
|
+
font: inherit;
|
|
1997
|
+
font-size: 13px;
|
|
1998
|
+
line-height: 1.4;
|
|
1999
|
+
resize: vertical;
|
|
2000
|
+
}
|
|
2001
|
+
.assistant__input:disabled {
|
|
2002
|
+
opacity: 0.6;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
/* ---------- the item panel ---------- */
|
|
2006
|
+
|
|
2007
|
+
/*
|
|
2008
|
+
* The panel takes the side column: on Today it stands in for the agenda while it
|
|
2009
|
+
* is open, and on the boards it brings the column back. In the grid rather than
|
|
2010
|
+
* over it, so the list is never covered, and sticky like the agenda so a long
|
|
2011
|
+
* list scrolls past a panel that stays put. Placed after the view rules above
|
|
2012
|
+
* on purpose: they and these share a specificity, and the panel being open has
|
|
2013
|
+
* to win.
|
|
2014
|
+
*/
|
|
2015
|
+
body[data-flyout='open'] .layout {
|
|
2016
|
+
grid-template-columns: minmax(0, 1fr) 420px;
|
|
2017
|
+
}
|
|
2018
|
+
body[data-flyout='open'] .layout__side {
|
|
2019
|
+
display: none;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
.flyout {
|
|
2023
|
+
position: sticky;
|
|
2024
|
+
top: 16px;
|
|
2025
|
+
display: flex;
|
|
2026
|
+
flex-direction: column;
|
|
2027
|
+
min-height: 0;
|
|
2028
|
+
max-height: calc(100vh - 32px);
|
|
2029
|
+
background: var(--surface);
|
|
2030
|
+
border: 1px solid var(--border);
|
|
2031
|
+
border-radius: var(--radius);
|
|
2032
|
+
box-shadow: var(--shadow);
|
|
2033
|
+
}
|
|
2034
|
+
.flyout[hidden] {
|
|
2035
|
+
display: none;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
.flyout__header {
|
|
2039
|
+
display: grid;
|
|
2040
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
2041
|
+
gap: 5px 10px;
|
|
2042
|
+
padding: 14px 16px 13px;
|
|
2043
|
+
border-bottom: 1px solid var(--hairline);
|
|
2044
|
+
}
|
|
2045
|
+
.flyout__where {
|
|
2046
|
+
display: flex;
|
|
2047
|
+
flex-wrap: wrap;
|
|
2048
|
+
align-items: center;
|
|
2049
|
+
gap: 6px;
|
|
2050
|
+
font-size: 12px;
|
|
2051
|
+
color: var(--ink-3);
|
|
2052
|
+
}
|
|
2053
|
+
.flyout__close {
|
|
2054
|
+
grid-column: 2;
|
|
2055
|
+
grid-row: 1 / 3;
|
|
2056
|
+
align-self: start;
|
|
2057
|
+
}
|
|
2058
|
+
.flyout__title {
|
|
2059
|
+
grid-column: 1;
|
|
2060
|
+
margin: 0;
|
|
2061
|
+
font-size: 15px;
|
|
2062
|
+
font-weight: 500;
|
|
2063
|
+
line-height: 1.4;
|
|
2064
|
+
color: var(--ink-1);
|
|
2065
|
+
overflow-wrap: anywhere;
|
|
2066
|
+
}
|
|
2067
|
+
.flyout__title a {
|
|
2068
|
+
color: inherit;
|
|
2069
|
+
text-decoration: none;
|
|
2070
|
+
}
|
|
2071
|
+
.flyout__title a:hover {
|
|
2072
|
+
text-decoration: underline;
|
|
2073
|
+
text-decoration-color: var(--accent);
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
.flyout__body {
|
|
2077
|
+
flex: 1;
|
|
2078
|
+
min-height: 0;
|
|
2079
|
+
overflow-y: auto;
|
|
2080
|
+
padding: 16px 16px 20px;
|
|
2081
|
+
}
|
|
2082
|
+
.flyout__detail {
|
|
2083
|
+
margin: 0 0 22px;
|
|
2084
|
+
font-size: 13px;
|
|
2085
|
+
color: var(--ink-2);
|
|
2086
|
+
overflow-wrap: anywhere;
|
|
2087
|
+
}
|
|
2088
|
+
.flyout__detail code {
|
|
2089
|
+
padding: 1px 4px;
|
|
2090
|
+
background: var(--surface-sunken);
|
|
2091
|
+
border-radius: 3px;
|
|
2092
|
+
font-size: 0.92em;
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
.flyout__section + .flyout__section {
|
|
2096
|
+
margin-top: 26px;
|
|
2097
|
+
}
|
|
2098
|
+
.flyout__label {
|
|
2099
|
+
margin: 0 0 10px;
|
|
2100
|
+
font-size: 11.5px;
|
|
2101
|
+
font-weight: 600;
|
|
2102
|
+
letter-spacing: 0.04em;
|
|
2103
|
+
text-transform: uppercase;
|
|
2104
|
+
color: var(--ink-3);
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
.flyout__notes {
|
|
2108
|
+
margin: 0 0 12px;
|
|
2109
|
+
padding: 7px 10px;
|
|
2110
|
+
border-left: 2px solid var(--hairline);
|
|
2111
|
+
background: var(--surface-sunken);
|
|
2112
|
+
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
|
|
2113
|
+
font-size: 12.5px;
|
|
2114
|
+
color: var(--ink-2);
|
|
2115
|
+
}
|
|
2116
|
+
.flyout__note {
|
|
2117
|
+
margin: 0;
|
|
2118
|
+
}
|
|
2119
|
+
.flyout__note + .flyout__note {
|
|
2120
|
+
margin-top: 4px;
|
|
2121
|
+
}
|
|
2122
|
+
.flyout__when {
|
|
2123
|
+
color: var(--ink-3);
|
|
2124
|
+
font-size: 11.5px;
|
|
2125
|
+
white-space: nowrap;
|
|
2126
|
+
}
|
|
2127
|
+
.flyout__empty {
|
|
2128
|
+
margin: 0 0 10px;
|
|
2129
|
+
font-size: 12.5px;
|
|
2130
|
+
color: var(--ink-3);
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
/*
|
|
2134
|
+
* The two forms share one button: the same size, sat on the same baseline as
|
|
2135
|
+
* the bottom of its field. The note field is one line and the assistant's is
|
|
2136
|
+
* not, which is deliberate (see `renderNoteForm`), so the buttons are what
|
|
2137
|
+
* make the two read as a pair.
|
|
2138
|
+
*/
|
|
2139
|
+
.flyout__note-form {
|
|
2140
|
+
display: flex;
|
|
2141
|
+
gap: 8px;
|
|
2142
|
+
align-items: flex-end;
|
|
2143
|
+
}
|
|
2144
|
+
.flyout__note-input {
|
|
2145
|
+
flex: 1;
|
|
2146
|
+
min-width: 0;
|
|
2147
|
+
height: 36px;
|
|
2148
|
+
padding: 0 10px;
|
|
2149
|
+
border: 1px solid var(--border);
|
|
2150
|
+
border-radius: var(--radius-sm);
|
|
2151
|
+
background: var(--page);
|
|
2152
|
+
color: var(--ink-1);
|
|
2153
|
+
font: inherit;
|
|
2154
|
+
font-size: 13px;
|
|
2155
|
+
}
|
|
2156
|
+
.flyout__submit {
|
|
2157
|
+
flex: none;
|
|
2158
|
+
height: 36px;
|
|
2159
|
+
padding: 0 14px;
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
/* Narrow: a sheet up from the bottom, over the list, since there is no side to sit at. */
|
|
2163
|
+
@media (max-width: 900px) {
|
|
2164
|
+
body[data-flyout='open'] .layout {
|
|
2165
|
+
grid-template-columns: minmax(0, 1fr);
|
|
2166
|
+
}
|
|
2167
|
+
.flyout {
|
|
2168
|
+
position: fixed;
|
|
2169
|
+
inset: auto 0 0 0;
|
|
2170
|
+
z-index: 15;
|
|
2171
|
+
max-height: 80vh;
|
|
2172
|
+
border-bottom: 0;
|
|
2173
|
+
border-radius: var(--radius) var(--radius) 0 0;
|
|
2174
|
+
}
|
|
2175
|
+
/* The sheet has the bottom, so a toast goes to the top rather than over its field. */
|
|
2176
|
+
body[data-flyout='open'] .toast {
|
|
2177
|
+
bottom: auto;
|
|
2178
|
+
top: 22px;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
/* ---------- links that are buttons ---------- */
|
|
2183
|
+
|
|
2184
|
+
.link-button {
|
|
2185
|
+
padding: 0;
|
|
2186
|
+
border: 0;
|
|
2187
|
+
background: none;
|
|
2188
|
+
color: var(--accent);
|
|
2189
|
+
font: inherit;
|
|
2190
|
+
cursor: pointer;
|
|
2191
|
+
}
|
|
2192
|
+
.link-button:hover {
|
|
2193
|
+
text-decoration: underline;
|
|
2194
|
+
}
|
|
2195
|
+
.objective__edit {
|
|
2196
|
+
margin-left: 8px;
|
|
2197
|
+
font-size: 11px;
|
|
2198
|
+
font-weight: 500;
|
|
2199
|
+
letter-spacing: normal;
|
|
2200
|
+
text-transform: none;
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
/* ---------- setup, before the first brief ---------- */
|
|
2204
|
+
|
|
2205
|
+
/*
|
|
2206
|
+
* The page a new store opens on. Built from the objective's card, since it does
|
|
2207
|
+
* the same job in its place: the first thing read, before anything else exists.
|
|
2208
|
+
*/
|
|
2209
|
+
.setup {
|
|
2210
|
+
padding: 18px 20px;
|
|
2211
|
+
background: var(--surface);
|
|
2212
|
+
border: 1px solid var(--border);
|
|
2213
|
+
border-left: 3px solid var(--accent);
|
|
2214
|
+
border-radius: var(--radius);
|
|
2215
|
+
box-shadow: var(--shadow);
|
|
2216
|
+
}
|
|
2217
|
+
.setup__title {
|
|
2218
|
+
margin: 0 0 6px;
|
|
2219
|
+
font-size: 18px;
|
|
2220
|
+
letter-spacing: -0.01em;
|
|
2221
|
+
}
|
|
2222
|
+
.setup__lede {
|
|
2223
|
+
margin: 0 0 16px;
|
|
2224
|
+
color: var(--ink-2);
|
|
2225
|
+
font-size: 13.5px;
|
|
2226
|
+
line-height: 1.5;
|
|
2227
|
+
}
|
|
2228
|
+
.setup__steps {
|
|
2229
|
+
display: grid;
|
|
2230
|
+
gap: 4px;
|
|
2231
|
+
margin: 0;
|
|
2232
|
+
padding: 0;
|
|
2233
|
+
list-style: none;
|
|
2234
|
+
}
|
|
2235
|
+
.setup__step {
|
|
2236
|
+
display: grid;
|
|
2237
|
+
grid-template-columns: 22px minmax(0, 1fr);
|
|
2238
|
+
gap: 10px;
|
|
2239
|
+
padding: 12px 0;
|
|
2240
|
+
border-top: 1px solid var(--hairline);
|
|
2241
|
+
}
|
|
2242
|
+
.setup__mark {
|
|
2243
|
+
font-size: 15px;
|
|
2244
|
+
line-height: 1.3;
|
|
2245
|
+
color: var(--ink-3);
|
|
2246
|
+
text-align: center;
|
|
2247
|
+
}
|
|
2248
|
+
.setup__step[data-done='true'] .setup__mark {
|
|
2249
|
+
color: var(--good-ink);
|
|
2250
|
+
}
|
|
2251
|
+
.setup__step[data-done='false'] .setup__mark {
|
|
2252
|
+
color: var(--accent);
|
|
2253
|
+
}
|
|
2254
|
+
.setup__step-title {
|
|
2255
|
+
margin: 0 0 4px;
|
|
2256
|
+
font-size: 14px;
|
|
2257
|
+
font-weight: 600;
|
|
2258
|
+
}
|
|
2259
|
+
.setup__body p {
|
|
2260
|
+
margin: 0 0 8px;
|
|
2261
|
+
color: var(--ink-2);
|
|
2262
|
+
font-size: 13px;
|
|
2263
|
+
line-height: 1.5;
|
|
2264
|
+
}
|
|
2265
|
+
.setup__step[data-done='true'] .setup__body p {
|
|
2266
|
+
color: var(--ink-3);
|
|
2267
|
+
}
|
|
2268
|
+
.setup__choices {
|
|
2269
|
+
display: flex;
|
|
2270
|
+
flex-wrap: wrap;
|
|
2271
|
+
gap: 8px;
|
|
2272
|
+
}
|
|
2273
|
+
.setup__boards {
|
|
2274
|
+
display: grid;
|
|
2275
|
+
gap: 8px;
|
|
2276
|
+
margin: 0;
|
|
2277
|
+
padding: 0;
|
|
2278
|
+
list-style: none;
|
|
2279
|
+
}
|
|
2280
|
+
.setup__board {
|
|
2281
|
+
display: grid;
|
|
2282
|
+
grid-template-columns: 16px 104px minmax(0, 1fr) auto;
|
|
2283
|
+
gap: 10px;
|
|
2284
|
+
align-items: baseline;
|
|
2285
|
+
font-size: 13px;
|
|
2286
|
+
}
|
|
2287
|
+
.setup__board-name {
|
|
2288
|
+
font-weight: 500;
|
|
2289
|
+
}
|
|
2290
|
+
.setup__board-text {
|
|
2291
|
+
color: var(--ink-2);
|
|
2292
|
+
}
|
|
2293
|
+
/*
|
|
2294
|
+
* The mark says how a board is doing; the words beside it say it again, so the
|
|
2295
|
+
* colour is never the only cue, and the words stay one colour for every state.
|
|
2296
|
+
*/
|
|
2297
|
+
.setup__board-mark {
|
|
2298
|
+
font-weight: 600;
|
|
2299
|
+
text-align: center;
|
|
2300
|
+
color: var(--ink-3);
|
|
2301
|
+
}
|
|
2302
|
+
.setup__board[data-health='ok'] .setup__board-mark {
|
|
2303
|
+
color: var(--good-ink);
|
|
2304
|
+
}
|
|
2305
|
+
.setup__board[data-health='problem'] .setup__board-mark {
|
|
2306
|
+
color: var(--serious);
|
|
2307
|
+
}
|
|
2308
|
+
.setup__board[data-health='todo'] .setup__board-mark {
|
|
2309
|
+
color: var(--accent);
|
|
2310
|
+
}
|
|
2311
|
+
.setup__body .setup__note {
|
|
2312
|
+
color: var(--ink-3);
|
|
2313
|
+
}
|
|
2314
|
+
.setup__body .setup__note--todo {
|
|
2315
|
+
color: var(--serious);
|
|
2316
|
+
}
|
|
2317
|
+
.setup__editor {
|
|
2318
|
+
margin-top: 12px;
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
/* ---------- settings ---------- */
|
|
2322
|
+
|
|
2323
|
+
.settings {
|
|
2324
|
+
display: grid;
|
|
2325
|
+
gap: 16px;
|
|
2326
|
+
max-width: 760px;
|
|
2327
|
+
padding-bottom: 72px;
|
|
2328
|
+
}
|
|
2329
|
+
.settings__head {
|
|
2330
|
+
display: flex;
|
|
2331
|
+
align-items: center;
|
|
2332
|
+
justify-content: space-between;
|
|
2333
|
+
}
|
|
2334
|
+
.settings__title {
|
|
2335
|
+
margin: 0;
|
|
2336
|
+
font-size: 18px;
|
|
2337
|
+
}
|
|
2338
|
+
.settings__problem {
|
|
2339
|
+
margin: 0;
|
|
2340
|
+
padding: 10px 14px;
|
|
2341
|
+
border: 1px solid var(--warning);
|
|
2342
|
+
border-radius: var(--radius-sm);
|
|
2343
|
+
font-size: 13px;
|
|
2344
|
+
}
|
|
2345
|
+
.settings-card {
|
|
2346
|
+
padding: 16px 18px;
|
|
2347
|
+
background: var(--surface);
|
|
2348
|
+
border: 1px solid var(--border);
|
|
2349
|
+
border-radius: var(--radius);
|
|
2350
|
+
box-shadow: var(--shadow);
|
|
2351
|
+
scroll-margin-top: 16px;
|
|
2352
|
+
}
|
|
2353
|
+
.settings-card__title {
|
|
2354
|
+
margin: 0 0 10px;
|
|
2355
|
+
font-size: 15px;
|
|
2356
|
+
}
|
|
2357
|
+
.settings-card__help {
|
|
2358
|
+
margin: 0 0 12px;
|
|
2359
|
+
color: var(--ink-2);
|
|
2360
|
+
font-size: 13px;
|
|
2361
|
+
line-height: 1.5;
|
|
2362
|
+
}
|
|
2363
|
+
.settings-card__warning {
|
|
2364
|
+
margin: 0 0 10px;
|
|
2365
|
+
color: var(--serious);
|
|
2366
|
+
font-size: 13px;
|
|
2367
|
+
}
|
|
2368
|
+
.settings-card__more {
|
|
2369
|
+
margin-top: 6px;
|
|
2370
|
+
}
|
|
2371
|
+
.settings-card__more > summary {
|
|
2372
|
+
cursor: pointer;
|
|
2373
|
+
color: var(--ink-2);
|
|
2374
|
+
font-size: 13px;
|
|
2375
|
+
padding: 4px 0;
|
|
2376
|
+
}
|
|
2377
|
+
.settings-card__more[open] > summary {
|
|
2378
|
+
margin-bottom: 10px;
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
.field {
|
|
2382
|
+
display: grid;
|
|
2383
|
+
gap: 4px;
|
|
2384
|
+
margin-bottom: 14px;
|
|
2385
|
+
}
|
|
2386
|
+
.settings-card > .field:last-child,
|
|
2387
|
+
.settings-card__more > .field:last-child {
|
|
2388
|
+
margin-bottom: 0;
|
|
2389
|
+
}
|
|
2390
|
+
.field__label {
|
|
2391
|
+
font-size: 13px;
|
|
2392
|
+
font-weight: 500;
|
|
2393
|
+
}
|
|
2394
|
+
.field__input {
|
|
2395
|
+
height: 32px;
|
|
2396
|
+
padding: 0 10px;
|
|
2397
|
+
border: 1px solid var(--border);
|
|
2398
|
+
border-radius: var(--radius-sm);
|
|
2399
|
+
background: var(--page);
|
|
2400
|
+
color: var(--ink-1);
|
|
2401
|
+
font: inherit;
|
|
2402
|
+
font-size: 13px;
|
|
2403
|
+
}
|
|
2404
|
+
.field__input:disabled {
|
|
2405
|
+
color: var(--ink-3);
|
|
2406
|
+
background: var(--surface-sunken);
|
|
2407
|
+
}
|
|
2408
|
+
.field[data-changed='true'] .field__input {
|
|
2409
|
+
border-color: var(--accent);
|
|
2410
|
+
}
|
|
2411
|
+
.field__help,
|
|
2412
|
+
.field__note {
|
|
2413
|
+
margin: 0;
|
|
2414
|
+
color: var(--ink-3);
|
|
2415
|
+
font-size: 12px;
|
|
2416
|
+
line-height: 1.45;
|
|
2417
|
+
}
|
|
2418
|
+
.field__note {
|
|
2419
|
+
color: var(--ink-2);
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
.objective-form {
|
|
2423
|
+
display: grid;
|
|
2424
|
+
gap: 2px;
|
|
2425
|
+
}
|
|
2426
|
+
.objective-form__actions {
|
|
2427
|
+
display: flex;
|
|
2428
|
+
justify-content: flex-end;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
.text-editor {
|
|
2432
|
+
display: grid;
|
|
2433
|
+
gap: 8px;
|
|
2434
|
+
}
|
|
2435
|
+
.text-editor__area {
|
|
2436
|
+
width: 100%;
|
|
2437
|
+
box-sizing: border-box;
|
|
2438
|
+
padding: 10px 12px;
|
|
2439
|
+
border: 1px solid var(--border);
|
|
2440
|
+
border-radius: var(--radius-sm);
|
|
2441
|
+
background: var(--page);
|
|
2442
|
+
color: var(--ink-1);
|
|
2443
|
+
font: 12.5px/1.5 ui-monospace, 'SF Mono', Menlo, monospace;
|
|
2444
|
+
resize: vertical;
|
|
2445
|
+
}
|
|
2446
|
+
.text-editor__bar {
|
|
2447
|
+
display: flex;
|
|
2448
|
+
align-items: center;
|
|
2449
|
+
justify-content: space-between;
|
|
2450
|
+
gap: 12px;
|
|
2451
|
+
}
|
|
2452
|
+
.text-editor__status {
|
|
2453
|
+
color: var(--ink-3);
|
|
2454
|
+
font-size: 12px;
|
|
2455
|
+
}
|
|
2456
|
+
.text-editor__conflict {
|
|
2457
|
+
display: flex;
|
|
2458
|
+
flex-wrap: wrap;
|
|
2459
|
+
align-items: center;
|
|
2460
|
+
gap: 8px;
|
|
2461
|
+
padding: 8px 12px;
|
|
2462
|
+
border: 1px solid var(--warning);
|
|
2463
|
+
border-radius: var(--radius-sm);
|
|
2464
|
+
}
|
|
2465
|
+
.text-editor__conflict p {
|
|
2466
|
+
flex: 1 1 100%;
|
|
2467
|
+
margin: 0;
|
|
2468
|
+
font-size: 13px;
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
.calendar-picker {
|
|
2472
|
+
margin: -4px 0 14px;
|
|
2473
|
+
}
|
|
2474
|
+
.calendar-picker__list {
|
|
2475
|
+
display: grid;
|
|
2476
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
2477
|
+
gap: 4px 12px;
|
|
2478
|
+
margin: 10px 0 0;
|
|
2479
|
+
padding: 0;
|
|
2480
|
+
list-style: none;
|
|
2481
|
+
font-size: 13px;
|
|
2482
|
+
}
|
|
2483
|
+
.calendar-picker__source {
|
|
2484
|
+
color: var(--ink-3);
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
/* Unsaved settings: a bar along the bottom, since the page is long and Save belongs to all of it. */
|
|
2488
|
+
.settings__bar {
|
|
2489
|
+
position: fixed;
|
|
2490
|
+
left: 50%;
|
|
2491
|
+
bottom: 20px;
|
|
2492
|
+
z-index: 10;
|
|
2493
|
+
display: flex;
|
|
2494
|
+
align-items: center;
|
|
2495
|
+
gap: 8px;
|
|
2496
|
+
max-width: min(720px, calc(100vw - 32px));
|
|
2497
|
+
padding: 10px 12px 10px 16px;
|
|
2498
|
+
transform: translateX(-50%);
|
|
2499
|
+
background: var(--surface);
|
|
2500
|
+
border: 1px solid var(--border);
|
|
2501
|
+
border-radius: var(--radius);
|
|
2502
|
+
box-shadow: 0 6px 24px rgba(11, 11, 11, 0.14);
|
|
2503
|
+
}
|
|
2504
|
+
.settings__bar[hidden] {
|
|
2505
|
+
display: none;
|
|
2506
|
+
}
|
|
2507
|
+
.settings__bar-text {
|
|
2508
|
+
flex: 1;
|
|
2509
|
+
margin: 0;
|
|
2510
|
+
font-size: 13px;
|
|
2511
|
+
color: var(--ink-2);
|
|
2512
|
+
}
|
|
2513
|
+
.settings__bar-text--error {
|
|
2514
|
+
color: var(--critical);
|
|
2515
|
+
}
|