iobroker.kanban 0.3.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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +224 -0
  3. package/admin/i18n/de.json +100 -0
  4. package/admin/i18n/en.json +100 -0
  5. package/admin/i18n/es.json +100 -0
  6. package/admin/i18n/fr.json +100 -0
  7. package/admin/i18n/it.json +100 -0
  8. package/admin/i18n/nl.json +100 -0
  9. package/admin/i18n/pl.json +100 -0
  10. package/admin/i18n/pt.json +100 -0
  11. package/admin/i18n/ru.json +100 -0
  12. package/admin/i18n/uk.json +100 -0
  13. package/admin/i18n/zh-cn.json +100 -0
  14. package/admin/jsonConfig.json +668 -0
  15. package/admin/kanban.png +0 -0
  16. package/io-package.json +299 -0
  17. package/lib/cron.js +203 -0
  18. package/lib/dueat.js +77 -0
  19. package/lib/events.js +24 -0
  20. package/lib/holidays.js +167 -0
  21. package/lib/i18n-server.js +301 -0
  22. package/lib/notify.js +627 -0
  23. package/lib/scheduler.js +198 -0
  24. package/lib/server.js +799 -0
  25. package/lib/store.js +1500 -0
  26. package/main.js +382 -0
  27. package/package.json +58 -0
  28. package/www/css/app.css +1022 -0
  29. package/www/i18n/de.json +238 -0
  30. package/www/i18n/en.json +238 -0
  31. package/www/i18n/es.json +238 -0
  32. package/www/i18n/fr.json +238 -0
  33. package/www/i18n/it.json +238 -0
  34. package/www/i18n/nl.json +238 -0
  35. package/www/i18n/pl.json +238 -0
  36. package/www/i18n/pt.json +238 -0
  37. package/www/i18n/ru.json +238 -0
  38. package/www/i18n/uk.json +238 -0
  39. package/www/i18n/zh-cn.json +238 -0
  40. package/www/index.html +171 -0
  41. package/www/js/api.js +98 -0
  42. package/www/js/app.js +411 -0
  43. package/www/js/board.js +983 -0
  44. package/www/js/colorpicker.js +165 -0
  45. package/www/js/dialogs.js +1419 -0
  46. package/www/js/i18n.js +43 -0
  47. package/www/vendor/marked.min.js +69 -0
  48. package/www/vendor/purify.min.js +3 -0
  49. package/www/vendor/sortable.min.js +2 -0
@@ -0,0 +1,1022 @@
1
+ /* ioBroker Kanban — Theming über CSS-Custom-Properties, data-theme am <html> */
2
+
3
+ :root {
4
+ --accent: #7E57C2;
5
+ --bg: #f2f2f5;
6
+ --surface: #ffffff;
7
+ --surface2: #e9e9ee;
8
+ --text: #1c1c1e;
9
+ --muted: #71717a;
10
+ --border: #d4d4d8;
11
+ --danger: #d32f2f;
12
+ --warn: #ef6c00;
13
+ --shadow: 0 1px 3px rgba(0, 0, 0, .12);
14
+ --radius: 10px;
15
+ /* Plastik: eine Lichtquelle links oben. Aufliegende Flaechen werfen ihren
16
+ Schatten nach rechts unten, eingelassene sind oben links dunkel und unten
17
+ rechts hell. Dazu ein Hauch Verlauf von oben nach unten. */
18
+ --card-shadow: 2px 4px 5px rgba(0, 0, 0, .45);
19
+ --card-edge: inset 1px 1px 0 rgba(255, 255, 255, .06);
20
+ --col-emboss: inset 1px 1px 2px rgba(0, 0, 0, .40), inset -1px -1px 0 rgba(255, 255, 255, .06);
21
+ --avatar-shadow: 2px 4px 4px rgba(0, 0, 0, .95);
22
+ --chip-shadow: 1px 2px 2px rgba(0, 0, 0, .90);
23
+ --ui-shadow: 1px 1px 2px rgba(0, 0, 0, .85);
24
+ --ui-emboss: inset 1px 1px 2px rgba(0, 0, 0, .40), inset -1px -1px 0 rgba(255, 255, 255, .35);
25
+ --count-emboss: inset 1px 1px 2px rgba(0, 0, 0, .20), inset -1px -1px 0 rgba(255, 255, 255, .20);
26
+ --sheen: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .10) 100%);
27
+ }
28
+
29
+ html[data-theme="dark"] {
30
+ --bg: #16161a;
31
+ --surface: #232329;
32
+ --surface2: #2d2d35;
33
+ --text: #ececf0;
34
+ --muted: #9d9da8;
35
+ --border: #3c3c46;
36
+ --shadow: 0 1px 3px rgba(0, 0, 0, .5);
37
+ }
38
+
39
+ * { box-sizing: border-box; }
40
+
41
+ /* Schlanke, themenkonforme Scrollbalken */
42
+ * { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
43
+ /* Native Bedienelemente (Datums-/Zeit-Picker, Dropdowns, Scrollbars) folgen dem Theme */
44
+ :root[data-theme="dark"] { color-scheme: dark; }
45
+ :root[data-theme="light"] { color-scheme: light; }
46
+
47
+ ::-webkit-scrollbar { width: 11px; height: 11px; }
48
+ ::-webkit-scrollbar-track { background: transparent; }
49
+ ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 6px; border: 3px solid transparent; background-clip: padding-box; }
50
+ ::-webkit-scrollbar-thumb:hover { background: var(--muted); border-radius: 6px; border: 3px solid transparent; background-clip: padding-box; }
51
+ ::-webkit-scrollbar-corner { background: transparent; }
52
+
53
+ html, body {
54
+ margin: 0;
55
+ height: 100%;
56
+ background: var(--bg);
57
+ color: var(--text);
58
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
59
+ font-size: 15px;
60
+ }
61
+
62
+ body { display: flex; flex-direction: column; }
63
+
64
+ /* ------------------------------------------------------------ Kopfzeile */
65
+
66
+ #topbar {
67
+ display: flex;
68
+ align-items: center;
69
+ gap: 8px;
70
+ padding: 8px 12px;
71
+ background: var(--surface);
72
+ border-bottom: 1px solid var(--border);
73
+ flex-wrap: wrap;
74
+ }
75
+
76
+ body.embed #topbar { display: none; }
77
+
78
+ .spacer { flex: 1; }
79
+
80
+ #boardSelect {
81
+ box-shadow: var(--ui-shadow);
82
+ font-size: 16px;
83
+ font-weight: 600;
84
+ padding: 6px 8px;
85
+ border: 1px solid var(--border);
86
+ border-radius: 8px;
87
+ background: var(--surface);
88
+ color: var(--text);
89
+ max-width: 40vw;
90
+ }
91
+
92
+ button {
93
+ font: inherit;
94
+ border: 1px solid var(--border);
95
+ background: var(--surface);
96
+ color: var(--text);
97
+ border-radius: 8px;
98
+ padding: 6px 12px;
99
+ cursor: pointer;
100
+ }
101
+
102
+ button:hover { background: var(--surface2); }
103
+
104
+ button.primary {
105
+ background: var(--accent);
106
+ border-color: var(--accent);
107
+ color: var(--accent-text, #fff);
108
+ font-weight: 600;
109
+ }
110
+
111
+ button.primary:hover { filter: brightness(1.1); background: var(--accent); }
112
+
113
+ button.danger { color: var(--danger); border-color: var(--danger); background: transparent; }
114
+
115
+ button.iconbtn { padding: 6px 10px; font-size: 16px; line-height: 1; }
116
+ button.iconbtn svg { width: 18px; height: 18px; display: block; }
117
+ button.iconbtn.active { background: var(--accent); color: var(--accent-text, #fff); border-color: var(--accent); }
118
+
119
+ button.linkbtn {
120
+ border: none;
121
+ background: none;
122
+ color: var(--accent);
123
+ padding: 4px 0;
124
+ text-align: left;
125
+ }
126
+
127
+ /* Benutzer-Chips */
128
+
129
+ #userChips { display: flex; gap: 4px; align-items: center; }
130
+ #topbar button { box-shadow: var(--ui-shadow); }
131
+
132
+ .user-chip {
133
+ display: inline-flex;
134
+ align-items: center;
135
+ gap: 6px;
136
+ border: 2px solid var(--border);
137
+ border-radius: 999px;
138
+ padding: 3px 10px 3px 4px;
139
+ cursor: pointer;
140
+ background: var(--surface);
141
+ user-select: none;
142
+ box-shadow: var(--ui-shadow);
143
+ }
144
+
145
+ /* Ausgeblendeter Benutzer: Chip wirkt eingedrueckt statt aufliegend */
146
+ .user-chip:not(.active) { box-shadow: var(--ui-emboss); }
147
+
148
+ .user-chip .avatar { width: 33px; height: 33px; font-size: 16px; }
149
+
150
+ .user-chip.active { border-color: var(--uc, var(--accent)); }
151
+
152
+ .avatar {
153
+ display: inline-flex;
154
+ align-items: center;
155
+ justify-content: center;
156
+ width: 24px;
157
+ height: 24px;
158
+ border-radius: 50%;
159
+ color: #fff;
160
+ font-size: 12px;
161
+ font-weight: 700;
162
+ flex: none;
163
+ overflow: hidden;
164
+ }
165
+ img.avatar, .avatar img { object-fit: cover; width: 100%; height: 100%; border-radius: 50%; }
166
+
167
+ /* Avatar-Verwaltung im Einstellungen-Dialog */
168
+ .avatar-edit { display: flex; align-items: center; gap: 8px; padding: 4px 0; }
169
+ .avatar-edit .avatar-prev { width: 32px; height: 32px; font-size: 13px; }
170
+ .avatar-edit .avatar-name { flex: 1; font-size: 14px; }
171
+
172
+ /* ------------------------------------------------------------ Board */
173
+
174
+ #board {
175
+ flex: 1;
176
+ display: flex;
177
+ gap: 12px;
178
+ padding: 12px;
179
+ overflow-x: auto;
180
+ align-items: flex-start;
181
+ }
182
+
183
+ .empty { color: var(--muted); padding: 24px; }
184
+
185
+ .column {
186
+ background: var(--surface2);
187
+ background-image: var(--sheen);
188
+ box-shadow: var(--col-emboss);
189
+ border-radius: var(--radius);
190
+ min-width: 280px;
191
+ flex: 1 1 280px; /* teilt die Bildschirmbreite unter allen Spalten auf; erst
192
+ wenn 280px je Spalte nicht mehr passen, wird gescrollt */
193
+ max-height: 100%;
194
+ display: flex;
195
+ flex-direction: column;
196
+ }
197
+
198
+ .column-head {
199
+ display: flex;
200
+ align-items: center;
201
+ gap: 8px;
202
+ padding: 10px 12px 6px;
203
+ font-weight: 700;
204
+ }
205
+
206
+ /* Das erste Icon im Spaltenkopf schiebt sich samt aller folgenden nach rechts;
207
+ die weiteren Icons stehen dann dicht daneben (sonst wuerde der freie Platz
208
+ mehrfach verteilt und z. B. das Auge landete in der Kopfmitte). */
209
+ .column-head > button:first-of-type { margin-left: auto; }
210
+
211
+ .column-head .count {
212
+ font-size: 12px;
213
+ font-weight: 600;
214
+ color: var(--muted);
215
+ background: var(--surface);
216
+ border-radius: 999px;
217
+ padding: 1px 8px;
218
+ box-shadow: var(--count-emboss);
219
+ }
220
+
221
+ .column.over-wip .column-head, .column.over-wip .count { color: var(--danger); }
222
+
223
+ .cards {
224
+ padding: 4px 8px 8px;
225
+ overflow-y: auto;
226
+ display: flex;
227
+ flex-direction: column;
228
+ gap: 8px;
229
+ min-height: 24px;
230
+ }
231
+
232
+ .column-foot { padding: 0 8px 10px; }
233
+ /* Karte anlegen: derselbe Stil wie "+ Karte" in der Kopfzeile, nur mit Plus.
234
+ Haengt in der Kartenliste (gleiche Breite wie die Karten) und bleibt beim
235
+ Scrollen unten sichtbar. */
236
+ .add-card-btn {
237
+ position: sticky;
238
+ bottom: 0;
239
+ z-index: 2;
240
+ display: flex;
241
+ align-items: center;
242
+ justify-content: center;
243
+ width: 25%;
244
+ margin: 0 auto;
245
+ padding: 5px 0;
246
+ border: 1px solid var(--accent);
247
+ border-radius: 8px;
248
+ background: var(--accent);
249
+ color: var(--accent-text, #fff);
250
+ box-shadow: var(--card-shadow);
251
+ }
252
+ .add-card-btn:hover { filter: brightness(1.1); background: var(--accent); }
253
+ .add-card-btn svg { width: 18px; height: 18px; display: block; }
254
+
255
+ .card {
256
+ position: relative;
257
+ background: var(--surface);
258
+ background-image: var(--sheen);
259
+ border-radius: 8px;
260
+ box-shadow: var(--card-shadow), var(--card-edge);
261
+ padding: 8px 10px;
262
+ cursor: grab;
263
+ -webkit-tap-highlight-color: transparent;
264
+ /* Beruehrung, die auf einer Karte beginnt, darf nur senkrecht scrollen.
265
+ Ohne diese Angabe uebernimmt der Browser eine Seitwaertsbewegung als
266
+ waagerechtes Scrollen von #board, bricht den laufenden Zug ab und die
267
+ Karte springt zurueck — auf dem Tablet liess sich deshalb nichts in eine
268
+ andere Spalte ziehen. Senkrechtes Scrollen der Spalte bleibt erhalten. */
269
+ touch-action: pan-y;
270
+ /* Ziehen heisst auf dem Touchscreen: aufliegen und halten. Genau das ist
271
+ fuer den Browser auch ein langer Druck — nach etwa einer Sekunde beginnt
272
+ er, Text zu markieren bzw. das Kontextmenue zu oeffnen, schickt
273
+ touchcancel und der Zug bricht ab. Die Karte fiel dadurch mitten in der
274
+ Bewegung zurueck. Markieren wird hier nicht gebraucht: Text bearbeitet
275
+ man im Karten-Dialog, und der ist davon nicht betroffen. */
276
+ user-select: none;
277
+ -webkit-user-select: none;
278
+ -webkit-touch-callout: none;
279
+ }
280
+
281
+ /* touch-action vererbt sich nicht: Der Finger liegt beim Ziehen meist auf einem
282
+ Kind der Karte (Titel, Badge, Symbol), nicht auf der Karte selbst. Ohne diese
283
+ Zeile gilt dort wieder die Voreinstellung. Symbole ausserdem nicht als Bild
284
+ behandeln, damit die WebView beim langen Druck nicht ihr Bildmenue anbietet. */
285
+ .card * {
286
+ touch-action: pan-y;
287
+ -webkit-touch-callout: none;
288
+ -webkit-user-drag: none;
289
+ }
290
+
291
+ /* Farbstreifen links - unabhaengig vom Kartenrahmen (nutzt --card-color) */
292
+ .card::before {
293
+ content: '';
294
+ position: absolute;
295
+ top: 0; bottom: 0; left: 0;
296
+ width: 4px;
297
+ background: var(--card-color, transparent);
298
+ border-radius: 8px 0 0 8px;
299
+ pointer-events: none;
300
+ }
301
+
302
+ /* Kartenfuss: Checklisten-Zaehler links, Aufklapp-Chevron mittig, Icons fuer
303
+ Beschreibung, Link und Wiederholung rechts. Nur der Chevron darf tiefer
304
+ ruecken, Zaehler und Icons halten den normalen Abstand zum Kartenrand. */
305
+ .card-foot {
306
+ display: grid;
307
+ grid-template-columns: 1fr auto 1fr;
308
+ align-items: center;
309
+ /* seitlich und unten buendig wie alle anderen Zeilen; nur der Chevron
310
+ darf tiefer ruecken (siehe unten) */
311
+ margin: 8px 0 0;
312
+ }
313
+ /* Die Icon-Flaechen sind groesser als die Symbole darin. Der negative Rand
314
+ schiebt die Flaeche so weit nach aussen, dass die Symbole selbst mit der
315
+ rechten Textkante fluchten. */
316
+ .card-foot-icons { margin-right: -4px; }
317
+ /* Ohne Checkliste haengen die Icons rechtsbuendig in der letzten Chip-Zeile */
318
+ .card-foot-icons.inline-icons { margin-left: auto; }
319
+ .card-foot .check-count { justify-self: start; }
320
+ .card-foot .card-check-toggle { grid-column: 2; justify-self: center; align-self: end; margin-bottom: -8px; }
321
+ /* z-index: die vergroesserte Trefferflaeche des Chevrons (::after) reicht seitlich
322
+ in diese Spalte hinein und darf die Icons nicht ueberdecken. */
323
+ .card-foot-icons { grid-column: 3; justify-self: end; display: flex; align-items: center; gap: 3px; position: relative; z-index: 2; }
324
+ /* Icon-Badges ohne Text: quadratische Flaeche statt liegender Pille */
325
+ .card-foot-icons .badge {
326
+ display: inline-flex;
327
+ align-items: center;
328
+ justify-content: center;
329
+ width: 22px;
330
+ height: 22px;
331
+ padding: 0;
332
+ }
333
+ .card-foot-icons .badge svg { width: 15px; height: 15px; vertical-align: 0; }
334
+ /* Icons im Kartenfuss und der Checklisten-Zaehler brauchen keine eigene Flaeche;
335
+ im dunklen Theme wirkt sie wie eine Umrandung um das Symbol. */
336
+ .card .card-foot-icons .badge, .card .check-count { background: none; }
337
+ .card-desc-btn { cursor: pointer; }
338
+ .card-desc-btn:hover { color: var(--accent); }
339
+
340
+ /* Checklisten-Aufklappbutton (Chevron): flache, breite Leiste. Das Icon behaelt
341
+ seine Groesse und ragt dank overflow:visible ueber die Buttonhoehe hinaus. */
342
+ .card-check-toggle {
343
+ position: relative;
344
+ background: none;
345
+ border: none;
346
+ border-radius: 4px;
347
+ padding: 0;
348
+ width: 90px;
349
+ height: 11px;
350
+ display: flex;
351
+ align-items: center;
352
+ justify-content: center;
353
+ overflow: visible;
354
+ font-size: 18px;
355
+ line-height: 0;
356
+ color: var(--muted);
357
+ cursor: pointer;
358
+ opacity: 0.8;
359
+ }
360
+ /* Treffer- und Hoverflaeche deutlich groesser als der Button selbst: seitlich je
361
+ 25 px, nach oben 8 px. Das laeuft ueber ein absolut positioniertes ::after, damit
362
+ weder das Grid der Fusszeile noch die Lage des Chevrons sich veraendern. */
363
+ .card-check-toggle::after {
364
+ content: '';
365
+ position: absolute;
366
+ left: -25px;
367
+ right: -25px;
368
+ top: -8px;
369
+ bottom: 0;
370
+ border-radius: 5px;
371
+ }
372
+ .card-check-toggle svg { position: relative; z-index: 1; } /* Chevron ueber der Hoverflaeche */
373
+ .card-check-toggle:hover { opacity: 1; color: var(--accent); }
374
+ .card-check-toggle:hover::after { background: var(--surface2); }
375
+
376
+ /* Ausgeklappte Checkliste, ueber der Fusszeile */
377
+ .card-checklist { display: flex; flex-direction: column; gap: 2px; margin-top: 8px; }
378
+ .card-checklist[hidden] { display: none; } /* [hidden] muss das display:flex schlagen */
379
+ .card-check-item {
380
+ display: flex;
381
+ align-items: center;
382
+ gap: 6px;
383
+ font-size: 13px;
384
+ cursor: pointer;
385
+ padding: 1px 0;
386
+ }
387
+ .card-check-item input { flex: none; }
388
+ .card-check-item.done .card-check-text { text-decoration: line-through; color: var(--muted); }
389
+
390
+ .card.dragging, .sortable-ghost { opacity: .5; }
391
+ /* Aufgenommene Karte: doppelter Versatz und Radius, deutlich dunkler */
392
+ .card.sortable-chosen, .card.sortable-drag { box-shadow: 4px 8px 10px rgba(0, 0, 0, .70), var(--card-edge); }
393
+
394
+ .card .title { font-weight: 600; overflow-wrap: anywhere; display: flex; align-items: baseline; gap: 5px; line-height: 1.3; }
395
+ /* Die Titelhoehe wird je Spalte angeglichen (siehe alignCardTitles in board.js):
396
+ alle Karten einer Spalte bekommen die Hoehe des laengsten Titels, damit
397
+ Prioritaet, Faelligkeit und Ort auf gleicher Hoehe beginnen, ohne dass bei
398
+ lauter einzeiligen Titeln eine leere Zeile entsteht. */
399
+
400
+ .card .badges {
401
+ display: flex;
402
+ flex-wrap: wrap;
403
+ gap: 6px;
404
+ align-items: center;
405
+ margin-top: 6px;
406
+ }
407
+ /* Labels beginnen immer in einer eigenen Zeile unter Prioritaet, Faelligkeit und Ort */
408
+ .card .labels-row { margin-top: 6px; }
409
+
410
+ .badge {
411
+ font-size: 12px;
412
+ border-radius: 6px;
413
+ padding: 1px 6px;
414
+ background: var(--surface2);
415
+ color: var(--muted);
416
+ white-space: nowrap;
417
+ }
418
+
419
+ .card .badges > .badge, .card .badges > .label-pill { box-shadow: var(--chip-shadow); }
420
+ .badge svg { display: inline-block; width: 14px; height: 14px; vertical-align: -2px; }
421
+ .card-check-toggle svg { width: 14px; height: 14px; }
422
+ .col-chevron svg { width: 13px; height: 13px; vertical-align: -2px; }
423
+ .badge.date-badge { color: var(--text); font-weight: 500; }
424
+ .badge.due-overdue { background: var(--danger); color: #fff; }
425
+ /* Link-Symbol sieht aus wie die anderen Icons im Kartenfuss: keine Umrandung,
426
+ gleiche Flaeche und Farbe, beim Zeigen die Akzentfarbe */
427
+ a.link-badge {
428
+ text-decoration: none;
429
+ cursor: pointer;
430
+ border: none;
431
+ background: var(--surface2);
432
+ color: var(--muted);
433
+ }
434
+ a.link-badge:hover { color: var(--accent, #7E57C2); }
435
+ .badge.due-soon { background: var(--warn); color: #fff; }
436
+ .badge.due-done { background: #2e7d32; color: #fff; } /* erledigt → grün */
437
+ .badge.prio-1 { background: var(--warn); color: #fff; font-weight: 700; }
438
+ .badge.prio-2 { background: var(--danger); color: #fff; font-weight: 700; }
439
+
440
+ .label-pill {
441
+ font-size: 11px;
442
+ border-radius: 999px;
443
+ padding: 1px 8px;
444
+ color: #fff;
445
+ font-weight: 600;
446
+ }
447
+
448
+ .card .avatars { display: inline-flex; margin-left: auto; }
449
+ .card .avatars .avatar { width: 30px; height: 30px; font-size: 15px; font-weight: 500; margin-left: -3px; box-shadow: 0 0 0 2px var(--uc, var(--surface)), var(--avatar-shadow); }
450
+
451
+ /* Zustaendige oben rechts im Titel, der Titeltext umfliesst sie. Eng gestapelt;
452
+ Hover oder Klick faechert sie nach links auf. Bewegt wird nur per transform,
453
+ damit sich der umflossene Bereich und der Zeilenumbruch nicht aendern. */
454
+ .card.has-avatars .title { display: block; }
455
+ .card.has-avatars .title .title-text { display: inline; }
456
+ .card.has-avatars .title .avatars { float: right; margin: 0 -2px 6px 6px; position: relative; z-index: 3; } /* -2px: rechts derselbe Abstand zum Kartenrand wie oben (8px) */
457
+ .card.has-avatars .badges, .card.has-avatars .done-stamp { clear: right; }
458
+ .card .title .avatars .avatar { margin-left: -14px; transition: transform .18s ease; }
459
+ .card .title .avatars .avatar:first-child { margin-left: 0; }
460
+ .card .title .avatars.open { z-index: 6; }
461
+ .card .title .avatars.open .avatar { transform: translateX(calc((var(--n) - 1 - var(--i)) * -11px)); }
462
+
463
+ /* Beschreibung nur lesend (Klick auf das Notiz-Icon der Karte) */
464
+ #descDialog .desc-title { margin: 0 0 10px; font-size: 15px; }
465
+ #descDialog .desc-body { max-height: 60vh; overflow: auto; }
466
+ #descDialog .desc-foot { display: flex; justify-content: flex-end; margin-top: 12px; }
467
+
468
+ /* ------------------------------------------------------------ Dialoge */
469
+
470
+ dialog {
471
+ border: 1px solid var(--border);
472
+ border-radius: 12px;
473
+ background: var(--surface);
474
+ color: var(--text);
475
+ box-shadow: 0 8px 40px rgba(0, 0, 0, .35);
476
+ padding: 18px;
477
+ /* auf großen Bildschirmen 60vw, aber nie zu schmal/überbreit */
478
+ width: min(calc(100vw - 32px), max(600px, 60vw));
479
+ max-height: calc(100vh - 48px);
480
+ }
481
+
482
+ dialog::backdrop { background: rgba(0, 0, 0, .45); }
483
+
484
+ dialog h3 { margin: 0 0 12px; }
485
+
486
+ dialog form { display: flex; flex-direction: column; gap: 10px; }
487
+
488
+ dialog label { display: flex; flex-direction: column; gap: 10px; font-size: 13px; font-weight: 600; color: var(--text); flex: 1; }
489
+ /* [hidden] muss das display:flex der Labels schlagen, sonst bleiben nicht
490
+ relevante Wiederholungs-Felder sichtbar */
491
+ dialog [hidden] { display: none !important; }
492
+ /* Inline-Label (z.B. Checkbox + Text nebeneinander statt gestapelt) */
493
+ dialog label.inline { flex-direction: row; align-items: center; gap: 6px; flex: 0 0 auto; white-space: nowrap; font-weight: 400; color: var(--muted); }
494
+ dialog button { font-size: 13px; }
495
+ #shareBody { display: flex; flex-direction: column; gap: 10px; }
496
+ #checklistEdit:empty { display: none; }
497
+
498
+ /* Ansicht-teilen: URL-Feld + Kopieren nebeneinander */
499
+ .share-url { display: flex; gap: 8px; align-items: stretch; }
500
+ .share-url-input { flex: 1; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px; }
501
+ .share-url button { flex: 0 0 auto; }
502
+ .share-cols { display: flex; flex-wrap: wrap; gap: 6px 14px; }
503
+ .board-members { margin: 4px 0 12px; }
504
+ .board-members-title { font-weight: 600; margin-bottom: 4px; font-size: 14px; }
505
+ .share-cols label.inline { font-size: 13px; }
506
+
507
+ dialog input, dialog textarea, dialog select {
508
+ font: inherit;
509
+ color: var(--text);
510
+ background: var(--bg);
511
+ border: 1px solid var(--border);
512
+ border-radius: 8px;
513
+ padding: 7px 9px;
514
+ }
515
+
516
+ dialog input:focus, dialog textarea:focus, dialog select:focus { outline: 2px solid var(--accent); border-color: transparent; }
517
+
518
+ dialog .row { display: flex; gap: 10px; flex-wrap: wrap; }
519
+
520
+ dialog footer { display: flex; gap: 8px; margin-top: 8px; }
521
+ /* Beschriftungen werden bei wenig Platz mit … gekuerzt statt umzubrechen */
522
+ dialog footer > button { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
523
+
524
+ .hint { font-weight: 400; color: var(--muted); font-size: 13px; }
525
+
526
+ .chip-pick { display: flex; flex-wrap: wrap; gap: 6px; }
527
+
528
+ .pick-chip {
529
+ border: 1px solid var(--border);
530
+ border-radius: 999px;
531
+ padding: 3px 10px;
532
+ cursor: pointer;
533
+ font-size: 13px;
534
+ user-select: none;
535
+ background: var(--bg);
536
+ }
537
+
538
+ .pick-chip.selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; }
539
+ .req-star { color: var(--danger); margin-left: 2px; font-weight: 700; }
540
+ .req-cap { display: inline-flex; align-items: baseline; align-self: flex-start; }
541
+ .req-legend { color: var(--danger); font-size: 10px; margin: 6px 0 0; }
542
+ .req-proxy { display: block; width: 100%; height: 1px; margin: -1px 0 0; padding: 0; border: 0; opacity: 0; pointer-events: none; }
543
+
544
+ .color-pick { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
545
+
546
+ .color-swatch {
547
+ width: 26px;
548
+ height: 26px;
549
+ border-radius: 50%;
550
+ cursor: pointer;
551
+ border: 2px solid transparent;
552
+ }
553
+
554
+ .color-swatch.selected { border-color: var(--text); }
555
+ .color-swatch.none { background: var(--surface2); position: relative; }
556
+ .color-swatch.none::after { content: "×"; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--muted); }
557
+
558
+ /* Freie Farbwahl: Swatch mit Regenbogen (öffnet den eigenen Colorpicker) */
559
+ .color-swatch.custom {
560
+ background: conic-gradient(red, #ff0, lime, cyan, blue, magenta, red);
561
+ cursor: pointer;
562
+ }
563
+
564
+ /* Klickbarer Farb-Swatch (Labels) – öffnet den Colorpicker */
565
+ .cp-trigger {
566
+ width: 26px;
567
+ height: 26px;
568
+ min-width: 26px;
569
+ border-radius: 50%;
570
+ border: 1px solid var(--border);
571
+ cursor: pointer;
572
+ display: inline-block;
573
+ vertical-align: middle;
574
+ }
575
+ .cp-trigger.empty { background: conic-gradient(red, #ff0, lime, cyan, blue, magenta, red); }
576
+
577
+ /* ---- Eingebetteter Colorpicker (colorpicker.js) ---- */
578
+ .cp-pop {
579
+ position: fixed;
580
+ z-index: 1000;
581
+ width: 220px;
582
+ background: var(--surface);
583
+ border: 1px solid var(--border);
584
+ border-radius: 10px;
585
+ box-shadow: var(--shadow);
586
+ padding: 10px;
587
+ display: flex;
588
+ flex-direction: column;
589
+ gap: 8px;
590
+ touch-action: none;
591
+ }
592
+ .cp-sv {
593
+ position: relative;
594
+ width: 100%;
595
+ height: 130px;
596
+ border-radius: 6px;
597
+ cursor: crosshair;
598
+ touch-action: none;
599
+ }
600
+ .cp-hue {
601
+ position: relative;
602
+ width: 100%;
603
+ height: 14px;
604
+ border-radius: 7px;
605
+ background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);
606
+ cursor: pointer;
607
+ touch-action: none;
608
+ }
609
+ .cp-cursor {
610
+ position: absolute;
611
+ width: 12px;
612
+ height: 12px;
613
+ border-radius: 50%;
614
+ border: 2px solid #fff;
615
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, .5);
616
+ transform: translate(-50%, -50%);
617
+ pointer-events: none;
618
+ }
619
+ .cp-hue-cursor {
620
+ position: absolute;
621
+ top: 50%;
622
+ width: 6px;
623
+ height: 20px;
624
+ border-radius: 3px;
625
+ background: #fff;
626
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, .5);
627
+ transform: translate(-50%, -50%);
628
+ pointer-events: none;
629
+ }
630
+ .cp-row { display: flex; align-items: center; gap: 8px; }
631
+ .cp-preview { width: 26px; height: 26px; border-radius: 6px; border: 1px solid var(--border); flex: none; }
632
+ .cp-hex { flex: 1; text-transform: uppercase; }
633
+ .cp-presets { display: flex; flex-wrap: wrap; gap: 5px; }
634
+ .cp-swatch {
635
+ width: 20px;
636
+ height: 20px;
637
+ border-radius: 50%;
638
+ cursor: pointer;
639
+ border: 1px solid var(--border);
640
+ }
641
+ .cp-swatch.none { position: relative; background: var(--surface2); }
642
+ .cp-swatch.none::after { content: "×"; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--muted); font-size: 12px; }
643
+
644
+ #checklistEdit { display: flex; flex-direction: column; gap: 4px; }
645
+
646
+ .check-item { display: flex; gap: 6px; align-items: center; }
647
+ .check-item input[type="text"] { flex: 1; }
648
+ .check-item .rm { border: none; background: none; color: var(--muted); font-size: 16px; padding: 2px 6px; }
649
+ /* Anfasser zum Umsortieren – erst ab zwei Punkten sichtbar, Platz bleibt reserviert */
650
+ .check-item .ck-grip { display: flex; align-items: center; color: var(--muted); cursor: grab; opacity: 0; pointer-events: none; }
651
+ #checklistEdit.multi .check-item .ck-grip { opacity: 0.75; pointer-events: auto; }
652
+ .check-item .ck-grip:hover { opacity: 1; color: var(--accent); }
653
+ .check-item .ck-grip:active { cursor: grabbing; }
654
+ .check-item .ck-grip svg { display: block; }
655
+ .check-item.ck-ghost { opacity: 0.4; }
656
+
657
+ .md-preview {
658
+ background: var(--bg);
659
+ border: 1px solid var(--border);
660
+ border-radius: 8px;
661
+ padding: 8px 10px;
662
+ font-size: 14px;
663
+ }
664
+
665
+ .md-preview p { margin: 4px 0; }
666
+
667
+ /* Board-Verwaltung */
668
+
669
+ #boardDialogBody { display: flex; flex-direction: column; gap: 10px; max-height: calc(100vh - 84px); }
670
+
671
+ /* Tab-Layout im Einstellungen-Dialog: Kopf + Tabs fest, Inhalt scrollt, Footer fest */
672
+ .tabbar { display: flex; gap: 2px; border-bottom: 1px solid var(--border); flex: none; }
673
+ .tab-btn {
674
+ background: none;
675
+ border: none;
676
+ border-radius: 0;
677
+ border-bottom: 2px solid transparent;
678
+ padding: 8px 14px;
679
+ color: var(--muted);
680
+ cursor: pointer;
681
+ font-size: 14px;
682
+ }
683
+ .tab-btn:hover { background: none; color: var(--text); }
684
+ .tab-btn.active { color: var(--text); border-bottom-color: var(--accent); }
685
+ .tab-panels { height: 380px; max-height: calc(100vh - 210px); overflow-y: auto; padding-right: 4px; }
686
+ .tab-panel { display: flex; flex-direction: column; gap: 10px; }
687
+ #boardDialogBody > footer { flex: none; border-top: 1px solid var(--border); padding-top: 10px; }
688
+
689
+ .col-edit {
690
+ display: flex;
691
+ gap: 6px;
692
+ align-items: center;
693
+ background: var(--bg);
694
+ border: 1px solid var(--border);
695
+ border-radius: 8px;
696
+ padding: 6px;
697
+ }
698
+
699
+ .col-edit .drag { cursor: grab; color: var(--muted); flex: 0 0 18px; text-align: center; padding: 0; }
700
+ .col-edit input[type="text"] { flex: 1; min-width: 80px; }
701
+ .col-edit input[type="number"] { flex: 0 0 60px; width: 60px; text-align: center; appearance: textfield; -moz-appearance: textfield; }
702
+ .col-edit input[type="number"]::-webkit-outer-spin-button,
703
+ .col-edit input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
704
+ .col-edit .col-chk { display: flex; justify-content: center; align-items: center; margin: 0; }
705
+ .col-edit .col-chk.c-new { flex: 0 0 58px; }
706
+ .col-edit .col-chk.c-done { flex: 0 0 76px; }
707
+ .col-edit .rm { flex: 0 0 30px; width: 30px; padding: 2px 0; }
708
+ .col-head { display: flex; gap: 6px; align-items: flex-end; padding: 0 7px 4px; margin-bottom: -6px; font-size: 12px; font-weight: 600; color: var(--muted); }
709
+ .col-head .h-drag { flex: 0 0 18px; }
710
+ .col-head .h-name { flex: 1; min-width: 80px; }
711
+ .col-head .h-num { flex: 0 0 60px; text-align: center; }
712
+ .col-head .h-new { flex: 0 0 58px; text-align: center; }
713
+ .col-head .h-done { flex: 0 0 76px; text-align: center; }
714
+ .col-head .h-rm { flex: 0 0 30px; }
715
+ .col-head span[title] { cursor: help; }
716
+ .col-more { font-size: 12px; color: var(--muted); padding: 4px 2px 2px; }
717
+
718
+ /* Label-Verwaltung (Board-Einstellungen) */
719
+ .label-edit {
720
+ display: flex;
721
+ gap: 6px;
722
+ align-items: center;
723
+ background: var(--bg);
724
+ border: 1px solid var(--border);
725
+ border-radius: 8px;
726
+ padding: 6px;
727
+ }
728
+ .label-edit input[type="text"] { flex: 1; min-width: 80px; }
729
+
730
+ /* nativer Colorpicker – kompakt und konsistent */
731
+ input[type="color"] {
732
+ width: 34px;
733
+ min-width: 34px;
734
+ height: 30px;
735
+ padding: 2px;
736
+ border: 1px solid var(--border);
737
+ border-radius: 6px;
738
+ background: var(--surface);
739
+ cursor: pointer;
740
+ }
741
+
742
+ /* Inline-Label-Anlegen im Karten-Dialog */
743
+ .label-new {
744
+ display: inline-flex;
745
+ gap: 4px;
746
+ align-items: center;
747
+ background: var(--bg);
748
+ border: 1px solid var(--border);
749
+ border-radius: 999px;
750
+ padding: 2px 4px;
751
+ }
752
+ .label-new input[type="text"] { width: 120px; padding: 2px 6px; font-size: 13px; }
753
+ .label-new input[type="color"] { width: 28px; min-width: 28px; height: 24px; }
754
+ button.mini {
755
+ padding: 2px 8px;
756
+ font-size: 14px;
757
+ line-height: 1;
758
+ border-radius: 6px;
759
+ }
760
+
761
+ /* ------------------------------------------------------------ Mobil */
762
+
763
+ @media (max-width: 600px) {
764
+ /* Spalten untereinander statt nebeneinander (Akkordeon) */
765
+ #board { flex-direction: column; overflow-x: hidden; overflow-y: auto; }
766
+ .column { min-width: 0; width: 100%; max-width: none; flex: none; max-height: none; }
767
+ .cards { overflow-y: visible; }
768
+ .column-head { cursor: pointer; user-select: none; }
769
+ .column.collapsed .cards, .column.collapsed .column-foot { display: none; }
770
+ html, body { overflow-x: hidden; max-width: 100%; }
771
+ #boardSelect { max-width: 40vw; }
772
+ /* Benutzer-Chips umbrechen statt horizontal ueberlaufen */
773
+ #userChips { flex-wrap: wrap; }
774
+
775
+ /* Dialoge vollfl\u00e4chig, Felder gestapelt, Footer sticky */
776
+ /* Dialoge als bodennahes Vollflaechen-Sheet: volle Breite, Hoehe = Inhalt (max. Schirm),
777
+ nur der Inhalt scrollt, Footer klebt unten, Buttons gleich gross nebeneinander */
778
+ dialog[open] {
779
+ width: 100%; max-width: 100%;
780
+ height: 100dvh; max-height: 100dvh; /* immer gleiche, volle Hoehe */
781
+ margin: 0; inset: 0;
782
+ padding: 0; border: none; border-radius: 0;
783
+ display: flex; flex-direction: column;
784
+ overflow: hidden; /* nichts ragt seitlich heraus */
785
+ }
786
+ #cardForm, #boardDialogBody, #shareBody {
787
+ margin: 0; padding: 14px 12px 0;
788
+ display: flex; flex-direction: column;
789
+ overflow-y: auto; -webkit-overflow-scrolling: touch;
790
+ min-height: 0; flex: 1 1 auto; max-height: none;
791
+ }
792
+ dialog .row { flex-direction: column; }
793
+ .share-url { flex-direction: column; }
794
+ .tab-panels { height: auto; max-height: none; overflow: visible; }
795
+ .tabbar { position: sticky; top: 0; background: var(--surface); z-index: 3; }
796
+
797
+ /* Footer klebt unten, volle Breite, gleich grosse Buttons nebeneinander */
798
+ dialog footer {
799
+ position: sticky; bottom: 0; z-index: 2;
800
+ margin: auto -12px 0; padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
801
+ background: var(--surface); border-top: 1px solid var(--border);
802
+ display: flex; gap: 8px; flex-wrap: nowrap;
803
+ }
804
+ dialog footer .spacer { display: none; } /* kein Auseinanderdruecken auf Mobil */
805
+ dialog footer > button { flex: 1 1 0; min-width: 0; }
806
+
807
+ .col-chevron { display: inline !important; } /* schlaegt die spaetere Basisregel */
808
+ }
809
+
810
+ /* Einklapp-Chevron im Spaltenkopf – nur auf schmalen Screens sichtbar */
811
+ .col-chevron { display: none; font-size: 12px; color: var(--muted); }
812
+
813
+ /* Hervorgehobene Karte (Benachrichtigungs-Link-Ziel \u201eBoard\u201c) */
814
+ .card-focus { outline: 2px solid var(--accent); }
815
+ .card-focus { animation: card-pulse 1s ease-in-out 2; }
816
+ @keyframes card-pulse { 0%, 100% { box-shadow: var(--shadow); } 50% { box-shadow: 0 0 0 4px var(--accent); } }
817
+
818
+ .col-toggle {
819
+ background: none;
820
+ border: none;
821
+ padding: 0 2px;
822
+ font-size: 16px;
823
+ line-height: 1;
824
+ cursor: pointer;
825
+ opacity: 0.85;
826
+ /* wie die Sortier-Icons daneben, nicht in der Textfarbe der Ueberschrift */
827
+ color: var(--muted);
828
+ }
829
+ .col-toggle:hover { background: none; opacity: 1; color: var(--accent); }
830
+ .col-toggle.off { opacity: 0.55; }
831
+ .col-toggle svg { display: block; }
832
+
833
+ /* Schnellablage-Ziele beim Ziehen einer Karte (unter der aufgenommenen Karte) */
834
+ /* Leiste quer ueber die volle Breite statt einer Spalte am rechten Rand: Dort
835
+ war der Weg vom aufgenommenen Kaertchen im ungenstigsten Fall die gesamte
836
+ Bildschirmbreite. Waagerecht verteilt liegt jedes Ziel hoechstens eine halbe
837
+ Breite entfernt, und die Reihenfolge entspricht der der Spalten. */
838
+ .quick-move {
839
+ position: fixed;
840
+ z-index: 900;
841
+ display: flex;
842
+ flex-direction: row;
843
+ flex-wrap: wrap;
844
+ justify-content: center;
845
+ gap: 6px;
846
+ pointer-events: none; /* die Leiste selbst fängt keine Klicks, nur die Zonen */
847
+ }
848
+ .quick-target {
849
+ pointer-events: auto;
850
+ position: relative;
851
+ flex: 1 1 110px;
852
+ max-width: 220px;
853
+ min-height: 64px;
854
+ display: flex;
855
+ align-items: center;
856
+ justify-content: center;
857
+ padding: 6px 10px;
858
+ border: 2px dashed var(--accent);
859
+ border-radius: 8px;
860
+ background: var(--surface);
861
+ color: var(--text);
862
+ font-weight: 600;
863
+ font-size: 13px;
864
+ box-shadow: var(--shadow);
865
+ opacity: 0.96;
866
+ }
867
+ .quick-label {
868
+ position: absolute;
869
+ inset: 0;
870
+ z-index: 1; /* bleibt ueber der hineingezogenen Karte lesbar */
871
+ display: flex;
872
+ align-items: center;
873
+ justify-content: center;
874
+ padding: 0 10px;
875
+ text-align: center;
876
+ pointer-events: none;
877
+ }
878
+ /* Die hineingezogene Karte tritt zurueck und wird flach gehalten, behaelt aber
879
+ ihre Box: SortableJS misst sie, ohne Geometrie faellt die Zone als Ziel aus. */
880
+ /* Falls SortableJS doch eine Karte in die Zone legt: flach und zurueckhaltend */
881
+ .quick-target > .card { opacity: 0.2; max-height: 36px; overflow: hidden; pointer-events: none; }
882
+
883
+ .quick-target.sortable-over,
884
+ .quick-target:hover {
885
+ background: var(--accent);
886
+ color: #fff;
887
+ border-style: solid;
888
+ }
889
+ .quick-target.quick-trash { border-color: var(--muted); }
890
+
891
+ /* ---- 0.3.0: Sortier-Umschalter je Spalte (Feature 1) ---- */
892
+ .col-sort {
893
+ background: none;
894
+ border: none;
895
+ padding: 0 2px;
896
+ line-height: 1;
897
+ cursor: pointer;
898
+ opacity: 0.85;
899
+ /* bewusst nicht 'inherit': sonst faerbt die WIP-Warnfarbe der Spalte die Icons rot */
900
+ color: var(--muted);
901
+ }
902
+ .col-sort:hover { opacity: 1; color: var(--accent); }
903
+ .col-sort svg { display: block; }
904
+ /* Auge und Sortier-Icons stehen im selben Raster wie der Rest des Kopfes (gap) */
905
+ .col-sort + .col-sort { margin-left: 6px; }
906
+ /* Richtungspfeil gehoert optisch zum Sortier-Button: eng zusammen */
907
+ .col-sort-dir { padding-right: 0; }
908
+ .col-sort-dir + .col-sort { margin-left: -3px; padding-left: 0; }
909
+ /* Auge in Erledigt-Spalten ebenfalls von der WIP-Warnfarbe entkoppeln */
910
+ .column.over-wip .column-head .col-toggle { color: var(--muted); }
911
+
912
+ .sort-menu {
913
+ position: fixed;
914
+ z-index: 1000;
915
+ background: var(--surface2);
916
+ border: 1px solid var(--border);
917
+ border-radius: 8px;
918
+ box-shadow: var(--shadow);
919
+ padding: 4px;
920
+ display: flex;
921
+ flex-direction: column;
922
+ min-width: 160px;
923
+ }
924
+ .sort-menu-title {
925
+ font-size: 11px;
926
+ font-weight: 700;
927
+ text-transform: uppercase;
928
+ letter-spacing: 0.04em;
929
+ color: var(--muted);
930
+ padding: 6px 10px 4px;
931
+ border-bottom: 1px solid var(--border);
932
+ margin-bottom: 4px;
933
+ }
934
+ .sort-item {
935
+ display: flex;
936
+ align-items: center;
937
+ gap: 8px;
938
+ background: none;
939
+ border: none;
940
+ text-align: left;
941
+ padding: 7px 10px;
942
+ border-radius: 6px;
943
+ cursor: pointer;
944
+ color: var(--text);
945
+ font-size: 13px;
946
+ }
947
+ .sort-item:hover { background: var(--bg); }
948
+ .sort-item.active { color: var(--accent); }
949
+ .sort-item svg { display: block; }
950
+
951
+ /* ---- 0.3.0: Grid-Sortiermodus - Anfasser links an der Karte ---- */
952
+ .card.has-grip { padding-left: 28px; }
953
+ .card-grip {
954
+ position: absolute;
955
+ left: 5px;
956
+ top: 50%;
957
+ transform: translateY(-50%);
958
+ display: flex;
959
+ align-items: center;
960
+ color: var(--muted);
961
+ cursor: grab;
962
+ opacity: 0.75;
963
+ }
964
+ .card-grip:hover { opacity: 1; color: var(--accent); }
965
+ .card-grip:active { cursor: grabbing; }
966
+
967
+ /* ---- 0.3.0: erledigte Karten (Feature 3+4) ---- */
968
+ /* Erledigte Karten treten zurueck: gedaempfter Titel (hell -> heller, dunkel -> dunkler) */
969
+ .card .title.title-done .title-text { text-decoration: line-through; color: var(--muted); }
970
+ .card .done-stamp { font-size: 13px; font-weight: 400; color: var(--muted); margin: 2px 0 0; }
971
+ .card-action { background: none; border: none; cursor: pointer; color: var(--muted); padding: 2px; opacity: 0.8; }
972
+ .card-action:hover { opacity: 1; color: var(--accent); }
973
+ .card-action svg { display: block; }
974
+ /* Kopier-Icon sitzt direkt hinter dem Kartentitel */
975
+ .card-copy { flex: 0 0 auto; padding: 0; line-height: 1; align-self: center; margin-left: 5px; }
976
+ .card-copy svg { width: 15px; height: 15px; }
977
+ /* Editor-Icon direkt hinter dem Titelende - nur hierueber oeffnet sich der Editor */
978
+ .card-edit { flex: 0 0 auto; padding: 0; line-height: 1; align-self: center; margin-left: 5px; }
979
+ .card-edit svg { width: 16px; height: 16px; }
980
+
981
+ /* ---- 0.3.0: Papierkorb-Spalte (Feature 5), feste graue Optik ---- */
982
+ .column.trash-col { --trash-bg: #56565e; --trash-card: #676770; --trash-fg: #f0f0f3; background: var(--trash-bg); }
983
+ [data-theme="dark"] .column.trash-col { --trash-bg: #43434b; --trash-card: #53535c; --trash-fg: #e8e8ee; }
984
+ .column.trash-col .column-head, .column.trash-col .col-toggle { color: var(--trash-fg); }
985
+ .column.trash-col .column-head .count { color: var(--trash-fg); background: rgba(255, 255, 255, 0.16); }
986
+ .card.card-trash { background: var(--trash-card); color: var(--trash-fg); border-color: rgba(0, 0, 0, 0.18); }
987
+ .card.card-trash .title { color: var(--trash-fg); }
988
+ .card.card-trash .trash-info { font-size: 12px; margin-top: 4px; color: var(--trash-fg); opacity: 0.85; }
989
+ .card.card-trash .trash-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 6px; }
990
+ .card.card-trash .card-action { color: var(--trash-fg); }
991
+ .card.card-trash .card-action:hover { color: var(--accent); }
992
+
993
+ /* ---- 0.3.0: gepaarte Editor-Felder (Feature 62) ---- */
994
+ dialog .row .field { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
995
+ dialog .icon-btn { padding: 5px 8px; line-height: 1; }
996
+ dialog .icon-btn svg { display: block; }
997
+ dialog label.inline-num { flex-direction: row; align-items: center; gap: 6px; }
998
+ dialog label.inline-num input { width: 80px; }
999
+
1000
+ /* ---- 0.3.0: Übertragen-Dialog (Feature 6) ---- */
1001
+ .transfer-mode { display: flex; gap: 8px; }
1002
+ .transfer-mode-btn { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1003
+ .transfer-mode-btn.active { background: var(--accent); border-color: var(--accent); color: var(--accent-text, #fff); }
1004
+ dialog button:disabled { opacity: 0.45; cursor: default; filter: grayscale(0.6); }
1005
+ .transfer-note { font-size: 12px; color: var(--muted); background: var(--surface2); border-radius: 6px; padding: 8px 10px; }
1006
+
1007
+ /* ---- 0.3.0: Kopfzeile der Board-Einstellungen (Feature 64) ---- */
1008
+ .board-toprow { align-items: center; gap: 8px; flex-wrap: wrap; }
1009
+ .board-toprow select { flex: 1 1 140px; min-width: 0; }
1010
+ .board-toprow .board-new-name { flex: 1 1 140px; min-width: 0; }
1011
+ .board-toprow .icon-btn { flex: 0 0 auto; }
1012
+ .board-toprow .icon-btn:disabled { opacity: 0.4; cursor: default; }
1013
+ @media (max-width: 600px) {
1014
+ .board-toprow { flex-direction: row; }
1015
+ }
1016
+
1017
+ /* Cron-Wiederholung: Klartext, naechste Termine, Fehlermeldung */
1018
+ .rec-cron-info { margin: 4px 0 2px; font-size: 12px; line-height: 1.5; }
1019
+ .rec-cron-info .cron-good { color: var(--accent); font-weight: 600; }
1020
+ .rec-cron-info .cron-next { color: var(--muted); }
1021
+ .rec-cron-info .cron-bad { color: var(--danger, #d32f2f); }
1022
+ input[name="recCron"] { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }