-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstyles.css
More file actions
983 lines (974 loc) · 53.3 KB
/
Copy pathstyles.css
File metadata and controls
983 lines (974 loc) · 53.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
/* tugtile — markdown kanban (prototype, read-only renderer) */
/* Squircle (continuous-curvature) corners — progressive enhancement.
corner-shape is Chromium-only as of mid-2026; Safari/WebKit & Firefox ignore it and keep the
border-radius arc, so this is ZERO visual regression. Requires a non-zero border-radius (still
set per-surface below). Token lives here because corner-shape is NOT inherited but a custom
property IS. Applied only to finite-radius card/button/panel/input surfaces — never pills/circles. */
:root { --corner: squircle; }
:where(
.tugtile-brand, .tugtile__lane, .tugtile__tile, .tugtile__num, .tugtile__popup,
.tugtile__popup-d, .tugtile__popup-t, .tugtile__add-btn, .tugtile__add-input, .tugtile__add-ok,
.tugtile-prompt-field, .tugtile-iconbtn, .tugtile-ed-find-i, .tugtile__tile-edit,
.tugtile__lane-rename, .tugtile-viewcycle, .tugtile__archivebar, .tugtile__lane-chevron,
.tugtile-archive-row, .tugtile__addcol-btn
) { corner-shape: var(--corner); }
.tugtile {
height: 100%;
padding: 0;
position: relative; /* Contain the view-swap snapshot overlay */
}
/* Search/Undo/Redo live in the native back/forward container (repurposed by setupLeftActions), so they line up with the left-sidebar toggle above. */
.tugtile-leftacts { display: flex; align-items: center; gap: 2px; padding-left: 4px; } /* Nudge right to line up the first icon with the left-sidebar toggle above (auto-measure couldn't find the toggle on iPad) */
.tugtile-leftacts > * { margin: 0 !important; } /* Reset Obsidian's per-action margins so Search/Undo/Redo are evenly spaced and aligned */
/* Board⇄table swap: the outgoing snapshot slides up & out while the new view rises from below (two panels shuffling). */
.tugtile-viewsnap {
position: absolute; inset: 0; z-index: 3;
background: var(--background-primary); overflow: hidden; pointer-events: none;
animation: tugtile-view-out 0.38s cubic-bezier(.7, 0, .3, 1) forwards; /* ease-in-out: slow → fast → slow */
}
@keyframes tugtile-view-out { from { transform: translateY(0); } to { transform: translateY(-100%); } }
.tugtile--switching .tugtile__board,
.tugtile--switching .tugtile__table-wrap { animation: tugtile-view-in 0.38s cubic-bezier(.7, 0, .3, 1); }
@keyframes tugtile-view-in { from { transform: translateY(100%); } to { transform: translateY(0); } }
/* iOS26 border radius fix (merged from the old kanban-card-radius snippet): iOS26 stretches the core variable --input-radius
into a pill shape, deforming any input box or button using it. We override it locally within tugtile's scope (kanban view
and the two modals) instead of applying it globally to the body, keeping this OSS plugin clean. Remove once Obsidian/iOS is fixed. */
.tugtile,
.tugtile-edit-modal-full,
.tugtile-prompt-modal,
.tugtile-archive-modal { --input-radius: var(--radius-m) !important; }
/* Phone: the centered control (view-cycle · lock) sits in a top-bar in the content, not the cramped header */
/* Solid bg + z-index above the view-swap snapshot (z-index 3): the bar stays put on top while board/table
slide beneath it, so the up-slide animation never leaks through the control row. */
.tugtile__ctlbar { flex: 0 0 auto; width: 100%; box-sizing: border-box; display: flex; justify-content: center; padding: 2px 8px 8px; position: relative; z-index: 4; background: var(--background-primary); }
/* Phone: kill the view-content top padding so the control bar sits flush against the header — otherwise that
strip sits ABOVE the bar and the view-swap snapshot (which covers the whole content) leaks through it. */
body.is-phone .workspace-leaf-content[data-type="tugtile-board"] .view-content { padding: 0; } /* All sides: the left/right padding inset the content so the full-width bar still left gaps on both edges */
/* Greyed-out (disabled-looking) header action — used for undo/redo when their stack is empty, instead of hiding the button */
.tugtile-act-off { opacity: 0.35; pointer-events: none; }
/* Dimmed brand suffix appended to the centered header title: "<file name> · tugtile-ing" */
/* Centered header controls: [view-cycle] · [lock]. Sized up (font-size) to roughly match the right-hand toolbar icons. */
.tugtile-headerctl {
display: inline-flex; align-items: center; gap: 6px; vertical-align: middle;
font-size: 18px;
}
.tugtile-sep { color: var(--text-faint); } /* Neutral separator, NOT a button — equal button padding + the flex gap keep its two sides symmetric */
/* Brand suffix + lock icon = one button (t('brandSuffix') + padlock) that toggles the read-only lock */
.tugtile-brand {
display: inline-flex; align-items: center; gap: 5px;
color: var(--text-faint); font-weight: var(--font-normal);
cursor: pointer; border-radius: var(--radius-s); padding: 2px 6px;
}
.tugtile-brand:hover { background: var(--background-modifier-hover); color: var(--text-muted); }
/* TileEditModal (tugtile's big editor) control strip — scoped to the modal so marktile's own strip is untouched.
A uniform gray bar spanning the full width: ✕ pinned far left, ✓ far right, the mode·lock centered between. */
.tugtile-edit-modal-full .tugtile__ctlbar { background: var(--background-secondary); }
.tugtile-edit-modal-full .tugtile-headerctl { display: flex; width: 100%; }
.tugtile-edit-modal-full .tugtile-headerctl > .tugtile-iconbtn:first-child { margin-right: auto; } /* ✕ → far left */
.tugtile-edit-modal-full .tugtile-headerctl > .tugtile-iconbtn:last-child { margin-left: auto; } /* ✓ → far right */
.tugtile-lock-icon { display: inline-flex; }
.tugtile-lock-icon svg { width: 0.9em; height: 0.9em; }
/* Locked board (read-only): hide add/edit affordances, disable checkboxes, drop drag cursors. View-only actions (collapse, view switch, search) still work. */
.tugtile--locked .tugtile__add,
.tugtile--locked .tugtile__addcol,
.tugtile--locked .tugtile__tile-more,
.tugtile--locked .tugtile__lane-more { display: none; }
.tugtile--locked .tugtile__check,
.tugtile--locked .tugtile__row-title input { pointer-events: none; }
.tugtile--locked .tugtile__tile,
.tugtile--locked .tugtile__lane-head { cursor: default; }
.tugtile__empty {
padding: 16px;
color: var(--text-muted);
}
.tugtile__board {
display: flex;
gap: 12px;
align-items: flex-start;
height: 100%;
overflow-x: auto;
padding: 12px;
}
.tugtile__lane {
flex: 0 0 auto;
width: var(--tugtile-lane-width, 300px); /* Uniform fixed column width (configurable in settings); all lanes line up evenly */
max-height: 100%;
display: flex;
flex-direction: column;
background: var(--background-secondary);
border-radius: var(--radius-m);
padding: 8px;
overflow: hidden; /* Cleanly clips content into the changing lane width during collapse/expand (dragging uses a clone on body and is unaffected) */
--tg-d1: 0.26s; /* Stage 1: lane narrows + horizontal title tucks into the chevron (collapse) / widens + title grows back (expand) */
--tg-d2: 0.24s; /* Stage 2: vertical title grows down out of the chevron (collapse) / retracts up into it (expand) */
transition: width var(--tg-d1) cubic-bezier(.4, 0, .2, 1); /* Smooth width change on collapse/expand */
}
.tugtile__lane-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 4px 0 8px; /* Horizontal 0 → header rails line up with the card column (cards sit at the lane's own 8px padding on both sides) */
font-weight: 700;
cursor: grab; /* Header acts as the drag handle for the lane */
}
.tugtile__lane-head:active { cursor: grabbing; }
.tugtile__lane--ghost { opacity: 0.4; }
.tugtile__lane--chosen { box-shadow: 0 0 0 2px var(--interactive-accent); }
.tugtile__lane--drag { /* Slightly tilts and lifts the lane when dragging the entire column */
rotate: 2deg;
box-shadow: 0 16px 36px rgba(0, 0, 0, 0.5);
}
.tugtile__lane-count {
flex: 0 0 auto;
opacity: 0.55;
font-size: 0.85em;
transition: opacity var(--tg-d1) ease;
}
.tugtile__lane-count--over { /* WIP: Warning indicator when exceeding limits */
color: var(--text-error);
opacity: 1;
font-weight: 700;
}
.tugtile--hide-counts .tugtile__lane-count {
display: none;
}
.tugtile__list {
display: flex;
flex-direction: column;
gap: 8px;
overflow-y: auto;
/* Scrollbar gutter without changing the card layout: the list extends 8px to the right (eating the lane's right padding) via the negative margin, while padding-right keeps the cards in their original position. The iOS/iPadOS overlay scrollbar then sits in that right padding instead of on top of the cards. */
margin-right: -8px;
padding-right: 8px;
}
.tugtile__tile:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--interactive-accent); } /* keyboard-focused card (arrow keys move it) */
.tugtile__tile {
position: relative;
background: var(--background-primary);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
padding: 8px;
cursor: grab;
}
.tugtile__tile:active {
cursor: grabbing;
}
.tugtile__tile:hover {
border-color: var(--background-modifier-border-hover, var(--interactive-accent));
}
/* Dragging state (corresponding to the Sortable class in main.js) */
/* Placeholder left behind: faded out with a dashed border, similar to a Trello card slot */
.tugtile__tile--ghost {
opacity: 0.4;
background: var(--background-modifier-hover);
border-style: dashed;
}
.tugtile__tile--ghost > * { visibility: hidden; }
.tugtile__tile--chosen {
box-shadow: inset 0 0 0 2px var(--interactive-accent); /* Inner ring — an outer ring's left/right 2px gets clipped by the list's overflow (cards are full-width with no horizontal room) */
}
/* The card following the cursor feels "picked up": slightly tilted, scaled up, with a floating shadow.
Uses standalone rotate/scale properties (not transform) to avoid overwriting translations applied by Sortable. */
.tugtile__tile--drag {
opacity: 0.97;
rotate: 3deg;
scale: 1.03;
box-shadow: 0 14px 32px rgba(0, 0, 0, 0.5);
cursor: grabbing;
}
/* Index number: a rounded inline chip prepended to the card text.
Key detail: keeping font-size unscaled (matching body text size) aligns the number and adjacent text to the same baseline,
achieving natural vertical alignment without magic numbers, font, line-height, or scale issues. The background chip sits snug
around the number (slight horizontal, minimal vertical padding), centering itself on the first line and wrapping naturally. */
.tugtile__num {
display: inline-block;
margin-right: 0.4em;
padding: 0 0.4em;
background: var(--interactive-accent);
color: var(--text-on-accent);
border-radius: var(--radius-s);
font-weight: 700;
line-height: 1.25;
}
/* Floating control area in the top-right: checkbox + ⋯. Using float allows the first line of text to wrap around it, keeping content aligned to the top without wasting space. */
.tugtile__tile-ctrls {
float: right;
display: flex;
align-items: center;
gap: 6px;
margin: 0 0 2px 8px;
}
.tugtile__tile-body {
padding-top: 0.15em; /* Subtle top padding for breathing room (kept smaller than the control height so the first line still wraps around it) */
overflow-wrap: anywhere; /* Alphanumeric text/URLs do not overflow the lane */
contain: layout paint; /* The body's internal layout is self-contained, so animating the tile's height doesn't re-flow the rendered markdown every frame — kills the stutter on content-heavy tiles */
}
/* Long card collapse: hides the full text when folded, showing only the first line of the title (Approach C, without clipping) */
.tugtile__tile-title {
display: none;
padding-top: 0.15em;
font-weight: 600;
}
.tugtile__tile-title > :first-child { margin-top: 0; }
.tugtile__tile-title > :last-child { margin-bottom: 0; }
.tugtile__tile--folded .tugtile__tile-body { display: none; }
.tugtile__tile--folded .tugtile__tile-title { display: block; }
/* On expand the tile height snaps (one reflow), then the body slides + fades in on the GPU — no per-frame
layout, so it stays smooth no matter how much content the tile holds. */
.tugtile__tile--revealing .tugtile__tile-body { animation: tugtile-body-reveal 0.24s cubic-bezier(.4, 0, .2, 1) both; }
@keyframes tugtile-body-reveal {
from { opacity: 0; transform: translateY(-6px); }
to { opacity: 1; transform: none; }
}.tugtile__tile-body > :first-child {
margin-top: 0;
}
.tugtile__tile-body > :last-child {
margin-bottom: 0;
}
/* Date and time picker popup */
.tugtile__popup {
position: fixed;
z-index: 9999;
background: var(--background-primary);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
box-shadow: var(--shadow-l);
padding: 8px;
user-select: none;
}
.tugtile__popup-head {
display: flex;
align-items: center;
justify-content: space-between;
font-weight: 600;
padding: 2px 4px 8px;
}
.tugtile__popup-nav {
cursor: pointer;
padding: 0 8px;
display: inline-flex;
align-items: center;
}
.tugtile__popup-nav svg { width: 1.15em; height: 1.15em; }
.tugtile__popup-nav:hover { color: var(--text-accent); }
.tugtile__popup-grid {
display: grid;
grid-template-columns: repeat(7, 2em);
gap: 2px;
text-align: center;
}
.tugtile__popup-w { opacity: 0.5; font-size: 0.8em; }
.tugtile__popup-d {
cursor: pointer;
border-radius: var(--radius-s);
padding: 3px 0;
line-height: 1.4;
}
.tugtile__popup-d:hover { background: var(--background-modifier-hover); }
.tugtile__popup-d--today { color: var(--text-accent); font-weight: 700; }
.tugtile__popup--time {
max-height: 240px;
overflow-y: auto;
padding: 4px;
}
.tugtile__popup-t {
cursor: pointer;
padding: 4px 16px;
border-radius: var(--radius-s);
}
.tugtile__popup-t:hover { background: var(--background-modifier-hover); }
/* Tag chips at the bottom of the card (when move-tags-to-card-footer is enabled) */
.tugtile__tags {
margin-top: 6px;
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.tugtile__tag {
font-size: 0.78em;
padding: 1px 7px;
border-radius: 999px;
background: var(--background-modifier-hover);
color: var(--text-accent);
cursor: pointer;
}
.tugtile__tag:hover { filter: brightness(1.1); }
/* Date/time chips at the bottom of the card */
.tugtile__date {
margin-top: 6px;
font-size: 0.78em;
color: var(--text-muted);
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px; /* separates the date and time chips (replaces the old full-width-space spacer) */
}
.tugtile__date-d, .tugtile__date-t { display: inline-flex; align-items: center; gap: 3px; }
.tugtile__date svg { width: 1em; height: 1em; } /* Lucide calendar/clock, sized to the chip text */
.tugtile__archivebar-i { display: inline-flex; }
.tugtile__archivebar svg { width: 1.1em; height: 1.1em; } /* Lucide archive icon (was a 📥 emoji) */
/* Date status colors: red for overdue, accent for today, yellow for upcoming (≤7 days), and green for safe (>7 days); colored whenever a date exists */
.tugtile__date--overdue { color: var(--color-red); font-weight: 600; }
.tugtile__date--today { color: var(--text-accent); font-weight: 600; }
.tugtile__date--soon { color: var(--color-yellow); font-weight: 600; }
.tugtile__date--safe { color: var(--color-green); }
.tugtile__date--unknown { color: var(--text-muted); } /* Neutral color used when the date cannot be parsed */
/* Freeze container scrolling during dragging (adding .is-dragging in main.js onStart) to prevent native mobile scrolling from fighting the drag gesture */
.tugtile.is-dragging .tugtile__board,
.tugtile.is-dragging .tugtile__list {
overflow: hidden !important;
touch-action: none !important;
}
/* Add card button and input */
.tugtile__add {
padding-top: 8px; /* Match the 8px gap between cards so the add button keeps the same vertical rhythm */
}
.tugtile__add-btn {
width: 100%;
text-align: left;
background: transparent;
border: 1px dashed var(--background-modifier-border);
border-radius: var(--radius-m);
color: var(--text-muted);
padding: 8px; /* Match card padding and the add-lane button (consistent 8px rhythm); left-aligned "+" lines up with the card number chip */
cursor: pointer;
}
.tugtile__add-btn:hover {
background: var(--background-modifier-hover);
color: var(--text-normal);
}
.tugtile__add-input {
width: 100%;
min-height: 4em;
resize: vertical;
border-radius: var(--radius-m);
}
.tugtile__add-ok {
background: var(--interactive-accent);
color: var(--text-on-accent);
border: none;
border-radius: var(--radius-s);
padding: 4px 12px;
cursor: pointer;
}
/* ⋯ More actions */
.tugtile__tile-more {
color: var(--text-faint);
line-height: 1;
cursor: pointer;
opacity: 0.3;
display: inline-flex;
align-items: center;
}
.tugtile__tile-more svg { width: 1em; height: 1em; }
.tugtile__tile-more:hover {
opacity: 1;
color: var(--text-normal);
}
/* Card checkbox */
.tugtile__check {
margin: 0;
flex: none;
cursor: pointer;
}
/* "Undo" button for toast notifications (to restore deleted/archived items in one tap) */
.tugtile-toast-undo {
margin-left: 10px;
font-weight: 700;
text-decoration: underline;
cursor: pointer;
color: var(--text-accent);
}
/* Submit button for mobile editing */
.tugtile__edit-actions {
display: flex;
justify-content: flex-end;
margin-top: 6px;
}
/* Focused editing sheet (integrated with iOS 26 rounded sheet design; hides native ✕ to use our own left ❌ and right ✓) */
.tugtile-edit-modal-full {
width: 100vw;
max-width: 100vw;
height: 100dvh; /* Dynamic viewport: shrinks with the on-screen keyboard so the editor stays inside the visible area and the textarea can scroll. dvh is supported on every Obsidian-supported renderer (Electron + iOS/Android webview), so no 100vh fallback is needed. */
max-height: 100dvh;
border-radius: 0; /* Full-screen focus editor — nothing of the board shows behind */
/* Inset content past the iPad status bar / notch / home indicator; env() = 0 on desktop. The solid background still bleeds into these strips. */
padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
overflow: hidden;
transform-origin: center;
animation: tugtile-pop-in 0.4s cubic-bezier(.34, 1.56, .64, 1); /* Keep the bounce — full-screen ≠ unrefined */
}
/* Backdrop darkens to solid FAST (so the board vanishes quickly, not lingering behind the bounce); zero container padding */
.modal-container.tugtile-edit-host { padding: 0; align-items: flex-start; } /* Top-pinned, so shrinking the editor to the visual viewport (keyboard) keeps it above the keyboard. Class set in TileEditModal.onOpen — a direct container target, no parent-match selector. */
/* The status-bar / home-indicator strips show --background-secondary (white in light, grey in dark) and sit
outside the modal's reach. So instead of fighting to cover them, dye the WHOLE editor --background-secondary
to match — seamless in both modes (the old --background-primary only matched in light mode). */
.modal-container.tugtile-edit-host .modal-bg { background: var(--background-secondary); opacity: 1; animation: tugtile-bg-in 0.12s ease both; }
.tugtile-edit-modal-full,
.tugtile-edit-modal-full .tugtile-edit-modal { background: var(--background-secondary); }
/* macOS: push the editor down so its top bar (✕) isn't covered by the window traffic lights (class is .mod-macos) */
.mod-macos .tugtile-edit-modal-full { padding-top: 28px; }
@keyframes tugtile-bg-in { from { opacity: 0; } to { opacity: 1; } }
.tugtile-edit-modal-full.tugtile-ed-closing {
animation: tugtile-pop-out 0.28s ease-in forwards; /* Exit transition: reverse scaling animation */
}
/* Opacity reaches 1 early (~0.1s) so the editor covers the board fast — the scale still bounces to settle. Fixes the "laggy / see-through during the animation" feel. */
@keyframes tugtile-pop-in {
0% { opacity: 0; transform: scale(0.92); }
35% { opacity: 1; }
100% { opacity: 1; transform: scale(1); }
}
@keyframes tugtile-pop-out {
from { opacity: 1; transform: scale(1); }
to { opacity: 0; transform: scale(0.92); }
}
/* Small input modal (search / rename / add lane): sit near the top like Spotlight, so the virtual keyboard doesn't cover it. */
.modal-container.tugtile-prompt-host { align-items: flex-start; }
.tugtile-prompt-modal {
margin-top: 10vh;
transform-origin: center;
animation: tugtile-pop-in 0.4s cubic-bezier(.34, 1.56, .64, 1);
/* Make the modal PANEL invisible — we stop fighting its box/padding/alignment and just float the search row
(field + buttons, which carry their own backgrounds) over a frosted backdrop. */
background: transparent !important;
box-shadow: none !important;
border: none !important;
}
/* Phone: sit below the header toolbar instead of colliding with it (this one was safe; the box/padding
overrides that broke the layout — clobbering Obsidian's mobile safe-area — have been removed). */
body.is-phone .tugtile-prompt-modal { margin-top: calc(env(safe-area-inset-top) + 64px); }
.tugtile-prompt-modal.tugtile-ed-closing { animation: tugtile-pop-out 0.28s ease-in forwards; }
.tugtile-prompt-modal .modal-close-button { display: none; } /* Hides Obsidian's native ✕ button to use our custom ❌ button instead */
/* Our own inner spacing — replaces Obsidian's 16px .modal-content padding. NEVER touch .modal's box: that
carries the mobile safe-area insets (overriding it clipped the buttons off-screen). justify-content:center
vertically centres the one-line row, so any leftover modal height is balanced top/bottom (no top-heavy gap). */
.tugtile-prompt-modal .modal-content { padding: 8px; justify-content: center; }
/* Dim the board behind the search modal (Obsidian forces the prompt backdrop transparent — opacity:0 !important —
so without this the board shows at full brightness and crowds the field on a narrow phone). */
.modal-container.tugtile-prompt-host .modal-bg {
opacity: 1 !important;
background-color: rgba(0, 0, 0, 0.4);
-webkit-backdrop-filter: blur(16px);
backdrop-filter: blur(16px);
}
.tugtile-prompt-row { display: flex; align-items: center; gap: 8px; }
/* The input is wrapped in a rounded "field" so a leading icon (search) can sit inside it */
.tugtile-prompt-field {
flex: 1; min-width: 0;
display: flex; align-items: center; gap: 8px;
box-sizing: border-box; height: 40px; padding: 0 12px;
background: var(--background-modifier-form-field);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
overflow: hidden; /* Clip the input to the rounded box so it can never poke out of the corners */
}
.tugtile-prompt-field:focus-within { border-color: var(--interactive-accent); }
.tugtile-prompt-fieldicon { flex: 0 0 auto; display: inline-flex; align-items: center; color: var(--text-muted); }
.tugtile-prompt-fieldicon svg { width: 1.15em; height: 1.15em; }
/* The input is our OWN contenteditable element (not a native <input>), so Obsidian's input chrome never
applies — no overrides, no !important. The .tugtile-prompt-field IS the visible box; this is the bare
single-line text surface inside it. plaintext-only keeps paste/IME clean (no rich markup, CJK-safe). */
.tugtile-prompt-input {
flex: 1; min-width: 0;
outline: none;
white-space: pre; overflow-x: auto; overflow-y: hidden; /* single line: scroll sideways, never wrap */
font-size: 16px; color: var(--text-normal); /* 16px prevents iOS auto-zoom on focus */
-webkit-user-modify: read-write-plaintext-only;
}
.tugtile-prompt-input::-webkit-scrollbar { display: none; } /* hide the horizontal scrollbar on the one-line field */
.tugtile-prompt-input:empty::before {
content: attr(data-placeholder);
color: var(--text-faint);
pointer-events: none; /* placeholder text isn't selectable / doesn't block the caret */
}
/* .tugtile-prompt-x / -ok inherit from .tugtile-iconbtn (the ✓ colour override lives up there) */
.tugtile-edit-modal-full .modal-close-button { display: none; } /* Hides the native top-right ✕ button in favor of our custom ❌ button with safety checks */
.tugtile-edit-modal {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
padding: 0;
background: var(--background-primary); /* Opaque: in the marktile pane there's no modal-bg behind it, so without this the area the iOS keyboard reveals shows through as a black gap */
}
/* marktile lives in a leaf, not a modal — fill the pane opaquely and use the dynamic viewport so the editor
shrinks WITH the on-screen keyboard instead of leaving a black void below it. */
.workspace-leaf-content[data-type="marktile-editor"] .view-content {
padding: 0;
background: var(--background-primary);
}
.workspace-leaf-content[data-type="marktile-editor"] .tugtile-ed-scroll { max-height: 100dvh; }
.tugtile-ed-bar {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 10px;
border-bottom: 1px solid var(--background-modifier-border);
}
/* ── Single source of truth for EVERY icon button ──────────────────────────────────────────────
editor ✕/✓, toolbar tools, find/replace, search ✕/✓. 40×40, flat, non-flex centering. Two hard
rules baked in here so they can never drift again and re-break iPad:
1) NO display:flex on the button — iPad WebKit blanks an inline svg that's a flex item.
2) the svg is nested in a <span> (done in JS) — iPad WebKit won't render an svg that's a direct
button child. So a size rule on the svg is safe.
Per-button classes below only override what's genuinely unique (colour of ✓, hover colour of ✕). */
.tugtile-iconbtn {
flex: 0 0 auto;
-webkit-appearance: none; appearance: none;
position: relative;
width: 40px; height: 40px;
border: none; border-radius: var(--radius-m);
background: transparent; color: var(--text-muted);
cursor: pointer;
}
/* The icon's svg is wrapped in a <span> (the iPad svg-in-button fix). Absolutely-centre that span — true
pixel-centre, and NO flex on the button (flex on a <button> blanks the inline svg on iPad). */
.tugtile-iconbtn > span { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: block; }
.tugtile-iconbtn svg { width: 20px; height: 20px; display: block; }
.tugtile-iconbtn:hover { background: var(--background-modifier-hover); color: var(--text-normal); }
/* unique bits only */
.tugtile-ed-ok, .tugtile-ed-ok:hover { color: var(--color-green); }
.tugtile-ed-ok { font-weight: 700; }
.tugtile-ed-x:hover { color: var(--text-error); }
.tugtile-prompt-ok, .tugtile-prompt-ok:hover { color: var(--text-success, var(--interactive-accent)); }
.tugtile-ed-tools {
flex: 1; min-width: 0;
display: flex; flex-wrap: nowrap; gap: 4px; align-items: center;
justify-content: safe center; /* Centered in the bar (not crammed next to ✕); falls back to start-aligned + scrollable when it overflows */
overflow-x: auto;
}
/* .tugtile-ed-tool, .tugtile-ed-find-b, .tugtile-prompt-x/ok all inherit everything from .tugtile-iconbtn above */
/* Group divider in the editor toolbar */
.tugtile-ed-sep { flex: 0 0 auto; width: 1px; height: 20px; margin: 0 4px; background: var(--background-modifier-border); }
/* Phone: second toolbar row (format/insert tools), below the bar's essentials. Centered, scrolls if it overflows. */
.tugtile-ed-tools2 {
flex: 0 0 auto;
display: flex; flex-wrap: nowrap; gap: 2px; align-items: center;
justify-content: safe center;
overflow-x: auto; -webkit-overflow-scrolling: touch;
padding: 6px 10px;
border-bottom: 1px solid var(--background-modifier-border);
}
/* Thin horizontal scrollbar for the overflowing toolbar rows, styled via ::-webkit-scrollbar — Obsidian's
renderer is Chromium/WebKit on every platform, so this is the portable way to thin the bar (the standard
thin-scrollbar property is only partially supported by Obsidian's older builds). */
.tugtile-ed-tools::-webkit-scrollbar,
.tugtile-ed-tools2::-webkit-scrollbar { height: 6px; }
.tugtile-ed-tools::-webkit-scrollbar-thumb,
.tugtile-ed-tools2::-webkit-scrollbar-thumb { background: var(--background-modifier-border); border-radius: 3px; }
body.is-phone .tugtile-ed-sep { display: none; } /* no group separators in the compact phone rows */
/* Phone: pull the editor near the top so the toolbar hugs the Dynamic Island (4px breathing room below it) */
body.is-phone .tugtile-edit-modal-full { padding-top: 4px; }
body.is-phone .tugtile-ed-bar { padding-top: 6px; padding-bottom: 4px; }
/* Find / replace bar (below the toolbar) */
.tugtile-ed-find {
display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
padding: 6px 10px;
border-bottom: 1px solid var(--background-modifier-border);
background: var(--background-secondary);
}
.tugtile-ed-find-i {
flex: 1; min-width: 80px;
font-size: 16px; padding: 5px 8px; border-radius: var(--radius-s); /* 16px avoids iOS focus-zoom */
}
.tugtile-ed-find-n { flex: 0 0 auto; color: var(--text-muted); font-size: 0.85em; min-width: 1.6em; text-align: center; }
/* .tugtile-ed-find-b inherits everything from .tugtile-iconbtn */
/* Settings: compact checkbox grid for choosing which editor toolbar buttons show */
.tugtile-tools-pick { display: flex; flex-wrap: wrap; gap: 6px 12px; justify-content: flex-end; }
.tugtile-tools-pick .tugtile-tool-chk { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; }
.tugtile-tools-pick .tugtile-tool-chk input { margin: 0; }
.tugtile-tools-pick .tugtile-tool-chk-i { display: inline-flex; align-items: center; min-width: 1.2em; justify-content: center; }
.tugtile-tools-pick .tugtile-tool-chk-i svg { width: 1.15em; height: 1.15em; }
.tugtile-ed-scroll { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; }
/* Single contenteditable surface — the visible styled text IS the editable text (no overlay to keep aligned),
which is what lets a heading line render bigger while the literal '## ' markers stay in place. */
.tugtile-ed-rich {
min-height: 100%;
margin: 0; border: 0; outline: none;
padding: 14px 16px;
font-family: var(--font-text); font-size: 16px; line-height: 1.7;
white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word;
color: var(--text-normal); caret-color: var(--text-normal);
-webkit-user-modify: read-write-plaintext-only; /* Keep paste/IME plaintext so no rich markup leaks into the markdown */
}
.tugtile-ed-rich:focus, .tugtile-ed-rich:focus-visible { outline: none; box-shadow: none; }
.tugtile-ed-rich .tg-line { min-height: 1.7em; } /* Empty lines stay tappable and visible */
/* marktile IS a markdown editor, so its body font is monospace in BOTH modes (Seasoned keeps the
syntax highlighting, just on a monospace base). Scoped to .marktile-ed so tugtile's card editor
(a board surface, not a markdown editor) stays on the proportional --font-text. */
.marktile-ed .tugtile-ed-rich { font-family: var(--font-monospace); tab-size: 2; } /* default tab-size is 8 — far too wide; a tab shows as 2 cells (one space · rule · one space) */
/* Distinguish a literal tab from spaces: a 1px accent rule drawn INSIDE each 2-cell tab (one space · rule ·
one space) so it neither hugs the right edge nor touches the following text. A background gradient (not a
border) lets the rule sit at an arbitrary x. marktile + Seasoned only — Plain's neutralizer strips backgrounds
too. padding-block extends the rule's height (bg fills the padding box) to ~line-height so consecutive tab
lines join into one continuous vertical guide. Tune background-position (rule x) / padding-block (continuity). */
.marktile-ed .tg-tab {
background: linear-gradient(var(--text-accent), var(--text-accent)) no-repeat;
background-size: 1px 100%;
background-position: 1ch center; /* one cell in; tab is 2 cells, so one cell of space after the rule */
padding-block: 0.35em;
}
/* A leading tab on a heading line must NOT inflate the indent guide — its 1ch position / 100% height / 0.35em
padding are all font-size-relative, so on a grown heading the thin rule balloons. Counter-scale the tab's own
font-size by the heading's scale so its metrics fall back to the base text size (the guide stays thin). */
.marktile-ed .tg-h1 .tg-tab { font-size: calc(1em / 1.7); padding-block: 0.85em; }
.marktile-ed .tg-h2 .tg-tab { font-size: calc(1em / 1.45); padding-block: 0.68em; }
.marktile-ed .tg-h3 .tg-tab { font-size: calc(1em / 1.25); padding-block: 0.55em; }
.marktile-ed .tg-h4 .tg-tab { font-size: calc(1em / 1.12); padding-block: 0.45em; }
.marktile-ed .tg-h5 .tg-tab, .marktile-ed .tg-h6 .tg-tab { font-size: calc(1em / 1.05); padding-block: 0.4em; }
/* ── Table of contents (marktile only). A toggle side panel of H1–H3; click a heading to scroll to it.
Absolute overlay anchored (top set in JS) to the editor content, so the toolbar + the TOC toggle button
stay above/clickable. Desktop/iPad: a left sidebar that pushes the text right. Phone: a full-width overlay. */
.marktile-ed { position: relative; }
.marktile-ed .tugtile-ed-bar { position: relative; z-index: 6; } /* keep the toggle (top-left) above the panel */
.marktile-toc {
position: absolute; left: 0; bottom: 0; width: 240px; z-index: 5;
background: var(--background-secondary); border-right: 1px solid var(--background-modifier-border);
overflow-y: auto; padding: 0.6em 0;
transform: translateX(-100%); transition: transform 0.18s ease; /* slid out by default */
}
.marktile-toc-open .marktile-toc { transform: none; }
.marktile-ed .tugtile-ed-scroll { transition: padding-left 0.18s ease; }
.marktile-toc-open .tugtile-ed-scroll { padding-left: 240px; } /* clear the sidebar (desktop/iPad) */
.marktile-toc-open .tugtile-ed-toc { color: var(--text-accent); } /* toggle active state */
.marktile-toc-title { font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); padding: 0 1.1em 0.5em; }
.marktile-toc-list { display: flex; flex-direction: column; }
.marktile-toc-item { padding: 0 1.1em; line-height: 40px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* ~40px tap target (single-line, vertically centred) — button-like, easier to hit */
.marktile-toc-item:hover { background: var(--background-modifier-hover); }
.marktile-toc-l1 { font-weight: 600; }
.marktile-toc-l2 { padding-left: 2.2em; }
.marktile-toc-l3 { padding-left: 3.3em; color: var(--text-muted); font-size: 0.95em; }
.marktile-toc-empty { padding: 0.4em 1.1em; color: var(--text-muted); font-style: italic; }
/* drag-reorder feedback (borrowed from tugtile's tile Sortable, scaled to a TOC row) */
.marktile-toc-item--ghost { opacity: 0.4; background: var(--background-modifier-hover); } /* the gap left behind */
.marktile-toc-item--chosen { box-shadow: inset 0 0 0 2px var(--interactive-accent); } /* the picked row */
.marktile-toc-item--drag { /* the row following the finger (re-parented to <body> by fallbackOnBody) */
opacity: 0.97; background: var(--background-secondary); border-radius: 6px;
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.4); cursor: grabbing;
rotate: 3deg; scale: 1.03; /* "picked up" tilt — standalone props (NOT transform) so Sortable's drag translation isn't overwritten */
}
.is-phone .marktile-toc { width: 100%; border-right: none; } /* phone: full-width overlay (sidebar too cramped) */
.is-phone .marktile-toc-open .tugtile-ed-scroll { padding-left: 0; }
/* marktile viewcycle → Plain mode: neutralize ALL editor styling so it's a bare plain-text markdown editor
(uniform size/weight, no colours, markers shown as-is). Pure CSS — no rendering, no parser, fully portable. */
.tugtile-plain .tg-line,
.tugtile-plain .tg-line * {
font-size: 1em !important; font-weight: normal !important; font-style: normal !important;
text-decoration: none !important; color: var(--text-normal) !important; background: transparent !important;
font-family: var(--font-monospace) !important; line-height: inherit !important; /* monospace = raw-source feel */
border: 0 !important; padding-left: 0 !important; margin: 0 !important;
}
/* marktile lock → read-only. contenteditable=false alone is OVERRIDDEN on WebKit by the editor's
-webkit-user-modify: read-write-plaintext-only, so flip that to read-only here (the real switch). Also
disable the toolbar so a stray tap can't edit (Obsidian has no save step — whatever changes IS saved). */
.tugtile--locked .tugtile-ed-rich { -webkit-user-modify: read-only; user-modify: read-only; }
.tugtile--locked .tugtile-ed-tools,
.tugtile--locked .tugtile-ed-tools2,
.tugtile--locked .tugtile-ed-find { pointer-events: none; opacity: 0.4; } /* find/replace too — else Replace All bypasses contenteditable (B2) */
/* Phone: tall bottom padding gives scroll room so the last real line can always be scrolled up above the
on-screen keyboard (see keepCaretVisible) instead of being trapped under it. */
body.is-phone .tugtile-ed-rich { padding-bottom: 55vh; }
/* Headings: markers kept (## still shows), the line just renders larger */
.tugtile-ed-rich .tg-h { font-weight: 700; color: var(--text-accent); }
.tugtile-ed-rich .tg-h1 { font-size: 1.7em; line-height: 1.35; }
.tugtile-ed-rich .tg-h2 { font-size: 1.45em; line-height: 1.4; }
.tugtile-ed-rich .tg-h3 { font-size: 1.25em; }
.tugtile-ed-rich .tg-h4 { font-size: 1.12em; }
.tugtile-ed-rich .tg-h5, .tugtile-ed-rich .tg-h6 { font-size: 1.05em; }
.tugtile-ed-rich .tg-quote { color: var(--text-muted); }
.tugtile-ed-rich .tg-b { font-weight: 700; }
.tugtile-ed-rich .tg-i { font-style: italic; }
.tugtile-ed-rich .tg-strike { text-decoration: line-through; color: var(--text-muted); }
.tugtile-ed-rich .tg-check { font-family: var(--font-monospace); color: var(--text-accent); }
.tugtile-ed-rich .tg-check-done { color: var(--color-green); }
.tugtile-ed-rich .tg-task-done { color: var(--text-muted); } /* whole completed task line dims */
.tugtile-ed-rich .tg-code { font-family: var(--font-monospace); color: var(--text-accent); }
.tugtile-ed-rich .tg-tag,
.tugtile-ed-rich .tg-link { color: var(--text-accent); }
.tugtile-ed-rich .tg-date { color: var(--color-yellow); }
/* Seasoned "彩色" palette — opt-in via settings (seasonedColor → .marktile-palette-color on the editor mount).
Each syntax token gets its own hue instead of the single accent tint. Obsidian theme color vars (hex fallbacks
for the web host) so it tracks light/dark + custom themes. quote/strike/date/check keep their base styling. */
.marktile-palette-color .tugtile-ed-rich .tg-h { color: var(--color-orange, #d19a66); }
.marktile-palette-color .tugtile-ed-rich .tg-b { color: var(--color-yellow, #e5c07b); }
.marktile-palette-color .tugtile-ed-rich .tg-i { color: var(--color-purple, #c678dd); }
.marktile-palette-color .tugtile-ed-rich .tg-code { color: var(--color-green, #98c379); }
.marktile-palette-color .tugtile-ed-rich .tg-tag,
.marktile-palette-color .tugtile-ed-rich .tg-link { color: var(--color-cyan, #56b6c2); }
/* Textarea for card editing */
.tugtile__tile-edit {
width: 100%;
min-height: 4em;
resize: vertical;
border-radius: var(--radius-s);
}
/* Lane header container */
.tugtile__lane-title {
flex: 1;
cursor: text;
white-space: nowrap; /* Prevents the title from wrapping to expand the lane width, avoiding overlap with count/buttons */
overflow: hidden;
text-overflow: ellipsis;
transform-origin: left center; /* Tucks toward the chevron (left) on collapse */
transition: transform var(--tg-d1) ease, opacity var(--tg-d1) ease;
}
.tugtile__lane-title-v { display: none; } /* Vertical title clone — only rendered in the collapsed strip */
/* Collapse stage 1 / expand stage 2: lane is narrow, the horizontal title is tucked into the chevron, and the row trimmings fade */
.tugtile__lane--narrowing { width: 40px; }
.tugtile__lane--narrowing .tugtile__lane-title { transform: scaleX(0); opacity: 0; }
.tugtile__lane--narrowing .tugtile__lane-count,
.tugtile__lane--narrowing .tugtile__lane-more { opacity: 0; }
.tugtile__lane-rename {
flex: 1;
font-weight: 700;
border-radius: var(--radius-s);
}
/* Responsive board (opt-in, default off): on a narrow pane the horizontal lanes reflow into a single full-width vertical stack — the behaviour of the retired List view, now automatic via a container query. Drag-and-drop/editing logic is shared with the board view. */
.tugtile--rwd { container-type: inline-size; container-name: tugtileboard; }
@container tugtileboard (max-width: 640px) {
.tugtile__board { flex-direction: column; overflow-x: hidden; overflow-y: auto; align-items: stretch; }
.tugtile__lane { width: auto; max-width: none; flex: 0 0 auto; }
.tugtile__addcol { width: auto; }
/* Collapsed lane in vertical layout: keep a standard horizontal header (the board view's narrow vertical strip would become a full-width black box here). The board view's two-stage strip animation no-ops here — the horizontal title stays, the vertical clone is unused. */
.tugtile__lane--collapsed { min-width: 0; max-width: none; width: auto; }
.tugtile__lane--collapsed .tugtile__lane-head { flex-direction: row; height: auto; padding: 8px; overflow: visible; }
.tugtile__lane--collapsed .tugtile__lane-title { display: revert; writing-mode: horizontal-tb; max-height: none; transform: none; opacity: 1; }
.tugtile__lane--collapsed .tugtile__lane-title-v { display: none; }
.tugtile__lane--narrowing { width: auto; }
.tugtile__lane--narrowing .tugtile__lane-title { transform: none; opacity: 1; }
}
/* Centered header view-cycle control: "<icon> <name>" — click cycles board → table → markdown. Same padding as the lock button so the "·" separator sits symmetrically between them. */
.tugtile-viewcycle {
display: inline-flex; align-items: center; gap: 5px;
cursor: pointer; border-radius: var(--radius-s);
padding: 2px 6px;
}
.tugtile-viewcycle:hover { background: var(--background-modifier-hover); }
.tugtile-viewcycle-icon { display: inline-flex; }
.tugtile-viewcycle-icon svg { width: 1em; height: 1em; }
.tugtile-viewcycle-name { font-weight: var(--font-medium, 500); }
/* Table view */
.tugtile__table-wrap {
height: 100%;
overflow: auto;
padding: 0 12px; /* No top padding → the sticky header sits flush at the top, so rows can't peek above it while scrolling */
}
.tugtile__table {
width: 100%;
border-collapse: collapse;
}
.tugtile__table th,
.tugtile__table td {
text-align: left;
padding: var(--tugtile-row-pad, 8px) 10px; /* Vertical padding is the table density preset (settings) */
border-bottom: 1px solid var(--background-modifier-border);
vertical-align: top;
}
.tugtile__table th {
cursor: pointer;
position: sticky;
top: 0;
z-index: 2; /* Stay above scrolling rows */
background: var(--background-primary); /* Solid — rows scroll cleanly behind, no show-through */
box-shadow: 0 1px 0 var(--background-modifier-border);
user-select: none;
}
.tugtile__table th:hover { color: var(--text-accent); }
.tugtile__row:hover { background: var(--background-modifier-hover); }
.tugtile__row-title { cursor: text; }
/* Table rows: displays plain text where we manage styles (without markdown rendering), keeping font sizes uniform and truncating long text to avoid breaking the layout */
.tugtile__row-text { font-size: var(--font-text-size); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 60ch; display: inline-block; vertical-align: bottom; }
.tugtile__row-title input { margin-right: 6px; }
.tugtile__row-lane { color: var(--text-muted); white-space: nowrap; }
/* Tag cell stays a normal table cell (NOT display:flex — that breaks cell layout and collapses when empty). Chips flow inline-block instead. */
.tugtile__row-tags .tugtile__tag { display: inline-block; margin: 0 4px 2px 0; }
/* Drag-to-archive zone (only shown when dragging cards) */
.tugtile__archivebar {
display: none;
align-items: center;
justify-content: center;
gap: 8px;
margin: 0 12px 8px;
padding: 12px;
border: 2px dashed var(--background-modifier-border);
border-radius: var(--radius-m);
background: var(--background-secondary);
color: var(--text-muted);
font-weight: 600;
}
/* Lane collapse chevron: a visible rounded square button. Its left edge lines up with the tile (card) left edge below — both sit at the lane's inner padding (8px), so no extra offset is needed. Square size also gives a comfortable tap target. */
.tugtile__lane-chevron {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 1.6em;
height: 1.6em;
border-radius: var(--radius-s);
/* Opaque (translucent hover tint composited over the lane bg) so the vertical title retracting INTO the
chevron during collapse/expand is fully masked — a see-through button would leak that text. */
background: linear-gradient(var(--background-modifier-hover), var(--background-modifier-hover)), var(--background-secondary);
cursor: pointer;
color: var(--text-muted);
user-select: none;
position: relative;
z-index: 3; /* Highest in the lane header, so it sits over the retracting title */
}
.tugtile__lane-chevron:hover { background: linear-gradient(var(--background-modifier-border), var(--background-modifier-border)), var(--background-secondary); color: var(--text-normal); }
/* Only the inner glyph rotates (the button stays still). Rotating the same ▾ by -90° to ▸ rather than swapping glyphs keeps height perfectly consistent. */
.tugtile__lane-chevron-glyph {
display: inline-flex;
transition: transform 0.3s cubic-bezier(.34, 1.56, .64, 1); /* Springy overshoot */
}
.tugtile__lane-chevron-glyph svg { width: 1.1em; height: 1.1em; }
.tugtile__lane--collapsed .tugtile__lane-chevron-glyph { transform: rotate(-90deg); }
/* In the collapsed vertical strip: drop the margin AND pin the toggle to the top-left corner (align-self:
flex-start) instead of centering it — so it stays exactly where the expanded header's chevron was and
doesn't drift sideways when a lane collapses/expands. */
.tugtile__lane--collapsed .tugtile__lane-chevron { margin: 0; align-self: flex-start; }
/* Collapsed lanes: a fixed narrow column with vertical title orientation (upright CJK characters, truncated if too long) */
.tugtile__lane--collapsed {
width: 40px; /* Base lane sets a plain width + width transition, so collapsing to 40px animates smoothly */
max-height: 100%;
cursor: pointer;
}
.tugtile__lane--collapsed .tugtile__list,
.tugtile__lane--collapsed .tugtile__add,
.tugtile__lane--collapsed .tugtile__lane-more {
display: none;
}
.tugtile__lane--collapsed .tugtile__lane-head {
flex-direction: column;
align-items: center;
justify-content: flex-start; /* Reset the base header's space-between, which in a column would spread chevron/title/count apart and push the chevron off the top */
gap: 10px;
padding: 4px 0 8px; /* Top 4 matches the expanded header so the chevron doesn't jump down when a lane collapses */
height: 100%;
overflow: hidden;
}
.tugtile__lane--collapsed .tugtile__lane-title { display: none; } /* Horizontal title hidden in the strip; the vertical clone takes over */
.tugtile__lane--collapsed .tugtile__lane-title-v {
display: block;
writing-mode: vertical-rl;
text-orientation: mixed; /* Keeps CJK characters upright while rotating alphanumeric text */
white-space: nowrap; /* Prevents wrapping into multiple vertical lines, which is the primary cause of text overlap */
overflow: hidden;
text-overflow: ellipsis;
max-height: 100%;
flex: 0 1 auto;
cursor: pointer;
transform: scaleY(0); /* Grows down out of / shrinks up into the chevron */
opacity: 0;
transform-origin: top center;
/* Grow and retract are the SAME motion in reverse: scaleY + opacity, one shared easing + duration, both
directions — so "grow out" and "shrink in" read as one symmetric concept (a dissolve into the button). */
transition: transform var(--tg-d2) ease-in-out, opacity var(--tg-d2) ease-in-out;
}
.tugtile__lane--collapsed.tugtile__lane--grown .tugtile__lane-title-v { transform: scaleY(1); opacity: 1; }
/* Retract = the exact reverse of grow (same easing + duration), driven by a keyframe so it reliably animates
instead of snapping. scaleY + opacity dissolve up into the chevron in lockstep. */
.tugtile__lane--retracting .tugtile__lane-title-v { animation: tugtile-title-retract var(--tg-d2) ease-in-out both; }
@keyframes tugtile-title-retract {
from { transform: scaleY(1); opacity: 1; }
to { transform: scaleY(0); opacity: 0; }
}
.tugtile__lane--collapsed .tugtile__lane-count {
writing-mode: horizontal-tb;
flex: 0 0 auto;
margin-top: auto; /* Sink the count to the bottom of the strip (chevron + title stay grouped at the top) */
}
/* Lane ⋯ menu */
.tugtile__lane-more {
cursor: pointer;
color: var(--text-faint);
opacity: 0.4;
display: inline-flex;
align-items: center;
}
.tugtile__lane-more svg { width: 1em; height: 1em; }
.tugtile__lane-more:hover {
opacity: 1;
color: var(--text-normal);
}
/* Archive list modal */
.tugtile-archive-list {
display: flex;
flex-direction: column;
gap: 8px;
max-height: 60vh;
overflow-y: auto;
}
.tugtile-archive-row {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
}
.tugtile-archive-body {
flex: 1;
min-width: 0;
}
.tugtile-archive-body > :first-child { margin-top: 0; }
.tugtile-archive-body > :last-child { margin-bottom: 0; }
.tugtile-archive-actions {
flex: 0 0 auto;
display: flex;
gap: 6px;
}
/* Add lane container */
.tugtile__addcol {
flex: 0 0 auto;
width: var(--tugtile-lane-width, 300px); /* Same fixed width as a lane, so the add-lane button lines up with the columns */
/* No padding: the button's top lines up with the lane boxes (both at the board padding), and the board's 12px gap already spaces it from the last lane */
}
.tugtile__addcol-btn {
width: 100%;
text-align: left; /* Match the add-card button (consistent left-aligned "+ label") */
background: transparent;
border: 1px dashed var(--background-modifier-border);
border-radius: var(--radius-m);
color: var(--text-muted);
padding: 8px;
cursor: pointer;
}
.tugtile__addcol-btn:hover {
background: var(--background-modifier-hover);
color: var(--text-normal);
}
/* ── In-grid markdown tables — the "locked markers" editor (decorateTables in editor-core.js). SHARED by
marktile (inlined) and ejecta (this file is vendored as tile-core.css). Gate = .marktile-grid: hosts add it
in Seasoned + Rendered, drop it in Plain. The pipe `|` is the table's `##` — in Seasoned it stays VISIBLE
(dimmed, locked) so the markdown coexists with the layout; the .tugtile-preview overlay (Rendered) hides the
pipes into a real bordered grid. textContent is byte-identical either way → round-trip exact. (Dormant in
tugtile, which never sets .marktile-grid.) */
.marktile-grid .ej-trow { display: table-row; white-space: normal; }
.marktile-grid .ej-trow .ej-cell { display: table-cell; padding: 3px 12px; vertical-align: top; cursor: text; min-width: 36px; }
.marktile-grid .ej-trow .ej-pipe { display: table-cell; color: var(--text-faint, #9aa0a6); padding: 0 1px; } /* the locked pipe, dimmed — the visible "seasoning" in Seasoned */
.marktile-grid .ej-tsep .ej-cell { color: var(--text-faint, #9aa0a6); } /* the |---| separator row, dimmed (also shown in Seasoned) */
.marktile-grid .ej-trow .ej-cell[data-a=center] { text-align: center; }
.marktile-grid .ej-trow .ej-cell[data-a=right] { text-align: right; }
/* Rendered overlay: the markers give way to a real grid (pipes + separator gone, cells get borders). */
.tugtile-preview .ej-trow .ej-pipe { display: none; }
.tugtile-preview .ej-tsep { display: none; }
.tugtile-preview .ej-trow .ej-cell { border: 1px solid var(--background-modifier-border, #e4e6e9); padding: 6px 14px; }
.tugtile-preview .ej-trow.ej-thead .ej-cell { background: var(--background-secondary, #f6f7f9); font-weight: 700; }
/* Rendered hides EVERY marker, not just the table pipes — the `##`/`**`/`` ` `` markers (.tg-mk) give way too, so
the whole view reads as output (the third point of raw → coexist → output). Still fully editable; markup never
leaves the text. Dormant in tugtile (no .tugtile-preview). */
.tugtile-preview .tg-mk { display: none; }
/* table right-click menu (desktop: insert/delete row/column) */
.ej-tblmenu { position: fixed; z-index: 2000; min-width: 168px; background: var(--background-primary, #fff); border: 1px solid var(--background-modifier-border, #e4e6e9); border-radius: 10px; box-shadow: 0 10px 30px rgba(10, 22, 40, .18); padding: 5px; display: flex; flex-direction: column; }
.ej-tblmenu button { font: 13.5px var(--font-interface, system-ui, sans-serif); text-align: left; border: 0; background: transparent; border-radius: 7px; padding: 8px 12px; cursor: pointer; color: var(--text-normal, #1f2328); }
.ej-tblmenu button:hover:not(:disabled) { background: var(--background-modifier-hover, rgba(0, 0, 0, .06)); }
.ej-tblmenu button:disabled { opacity: .35; cursor: default; }
.ej-tblmenu hr { border: 0; border-top: 1px solid var(--background-modifier-border, #e4e6e9); margin: 4px 6px; }
/* Inline image thumbnails (decorateImages in editor-core.js) — the picture shows beside its still-editable source
line; the source markdown stays but dims. Hidden in Plain so power users see raw source only. Shared by marktile
+ ejecta; dormant in tugtile (never calls decorateImages). The first step of Rendered "growing". */
.ej-inlimg { display: block; max-width: min(280px, 100%); max-height: 220px; border-radius: 8px; margin: 4px 0 8px; border: 1px solid var(--background-modifier-border, #e4e6e9); }
.tg-line.ej-hasimg { color: var(--text-faint, #9aa0a6); font-size: 12px; }
.tugtile-plain .ej-inlimg { display: none; }
.tugtile-plain .tg-line.ej-hasimg { color: var(--text-normal, #1f2328); font-size: 1em; }