File size: 141,967 Bytes
0a5d32e
 
 
 
 
 
 
 
 
 
 
3dc69f8
 
 
 
 
 
 
 
 
 
0a5d32e
b1b0036
0a5d32e
3dc69f8
0a5d32e
 
 
 
f5990cb
 
 
 
 
059e85b
 
f5990cb
059e85b
 
 
 
 
 
 
 
 
b1b0036
 
 
 
 
 
3dc69f8
 
 
 
 
 
f5990cb
 
 
b1b0036
3dc69f8
 
 
 
 
 
 
f5990cb
 
 
3dc69f8
b1b0036
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
 
3dc69f8
0a5d32e
 
 
 
 
 
 
 
3dc69f8
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3dc69f8
 
 
0a5d32e
 
 
 
f5990cb
 
 
 
 
3dc69f8
 
 
 
 
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3dc69f8
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
 
 
 
 
 
 
 
 
 
 
 
 
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e02ce54
 
 
 
 
 
 
 
 
 
 
 
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
 
 
 
059e85b
 
 
 
 
f5990cb
3dc69f8
0a5d32e
 
f1121d3
f5990cb
 
 
 
f1121d3
 
 
 
 
 
f5990cb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1b38599
 
 
 
 
f5990cb
 
 
 
 
 
 
 
 
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
f1c47a3
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f1c47a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a5d32e
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
f1c47a3
 
0a5d32e
 
 
 
 
 
 
 
 
 
 
f5990cb
f1c47a3
 
0a5d32e
 
 
 
 
 
 
 
f5990cb
f1c47a3
 
0a5d32e
 
 
 
f5990cb
f1c47a3
 
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
9615d53
f5990cb
 
 
9615d53
f5990cb
 
 
 
f1c47a3
 
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b1b0036
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
502ffd8
 
 
 
 
 
 
 
 
 
 
 
 
b1b0036
 
 
 
 
 
 
 
 
 
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686c7cd
 
 
 
 
 
 
 
 
 
 
 
 
0a5d32e
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
f5990cb
 
 
 
 
 
 
0a5d32e
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
f5990cb
 
 
0a5d32e
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
f5990cb
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6366111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e02ce54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a5d32e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
/* ═══════════════════════════════════════════════════════════════
   重譯文本分析系統 Agent — Refined Dark Academic Dashboard
   Palette: Sky-500 (#0ea5e9) + Indigo-400 (#818cf8) on Slate-950
   Typography: Inter (UI) + JetBrains Mono (data) + Crimson Pro (headings)
   WCAG AA compliant (4.5:1+)
   ═══════════════════════════════════════════════════════════════ */

@import url('https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@400;600;700&display=swap');

/* ── 1. TOKENS & RESET ─────────────────────────────────────────── */
/* 預設淺色;系統設定為深色模式時(或明確帶 .dark class)自動切換深色 */
/* 底色體系(2026-08-12 三修):千卉回報「純黑或純白背景面積很大,視覺壓力
   也變大」。量過三層才確定抱怨的來源不只對話欄——淺色的頁面底 #f8fafc 幾乎
   等於白,而真正的大面積純白是 --c-surface(表格資料列+所有 Gradio 區塊);
   深色則是 #020617 近乎純黑。改成三層結構,淺深共用同一套邏輯:
     底(--c-bg)        淡藍/帶藍深色,鋪滿頁面、canvas、導覽、對話欄
     浮起物(--c-bubble-bg)比底亮一階,對話氣泡
     資料紙張(--c-surface)淺色維持純白當表格底(資料密集區可讀性優先),
                          深色跟著抬亮
   淺色氣泡「比欄底亮一階、浮在上面」是千卉選的方案 2,剛好與深色本來就有的
   關係(氣泡比欄底亮)一致,兩個主題不再各講各的。 */
:root {
    --c-bg:              #f3f8fd;
    --c-surface:         #ffffff;
    --c-surface2:        #eef5fc;
    --c-surface3:        #ffffff;
    --c-surface3-border: #e2e8f0;
    --c-border:          rgba(2,132,199,.16);
    --c-border-hover:    rgba(2,132,199,.35);
    /* 2026-08-12(業主回報淺色對比不足):淺色的 --c-border 疊在白/近白底上
       只有約 1.2:1,遠低於 WCAG 1.4.11 對「UI 元件邊界」要求的 3:1,區塊外框
       跟水平分界線在淺色模式下等於看不見(深色當初已針對 .app-header/欄邊框/
       標題底線個別補過 rgba(148,163,184,.28),淺色從沒補)。這裡改成兩級線條,
       之後新增元件挑其中一級,不要再直接用 --c-border 畫可見邊界:
         --c-border-strong 結構線(區塊外框、欄位分隔、水平分界)
         --c-divider       細線(表格資料列線、對話氣泡邊框)
       深色值直接沿用原本散在各選擇器的硬寫值,深色外觀零改變。
       2026-08-12 二修(千卉看實機截圖後回饋「線條對比好高、蠻醜」):
       結構線從 #7c8798(對白 3.64:1)柔化到 #a3aebd(2.25:1)。取捨要
       講明白——2.25:1 沒有達到 WCAG 1.4.11 對「UI 元件邊界」的 3:1 門檻,
       是刻意的視覺決定;但比改版前的 1.2:1 高近一倍,業主回報的「看不清楚
       區塊」在這個亮度差已經解決。文字對比不受這條影響,仍全數通過 AA。
       細線 --c-divider 淺色 #b6c0cd 對氣泡底 1.64:1、對欄底 1.76:1,
       跟深色 rgba(148,163,184,.28) 疊 #0d1526 算出來的 1.67:1 幾乎相同,
       這就是「深淺兩個模式的氣泡邊框看起來一樣淡」的依據(用量測對齊,
       不是各自挑一個看起來順眼的值)。
       2026-08-12 四修(千卉指定:邊線與水平線改成跟「重新選擇研究情境」
       按鈕的邊框同一個色):結構線淺色改成 rgba(2,132,199,.30),也就是
       button.secondary 用的那個值,全站線條與次要按鈕從此同一個來源。
       疊在新的淺底 #f3f8fd 上約 1.46:1,比三修的 2.3:1 再淡一階——這是
       第三次往柔和方向調,已經明確離 WCAG 1.4.11 的 3:1 很遠,屬業主拍板
       的視覺取捨(文字對比不受影響,仍全數通過 AA)。
       2026-08-12 三修(千卉:淺色系的邊框不要太深,要維持亮麗的淺藍):
       #a3aebd/#b6c0cd 是中性灰藍,配上這輪的淡藍底會顯灰、顯髒。改走
       sky 相:結構線 #6ba3d6、細線 #a9cbe8、輸入框 #7fb0d9。**亮度級距
       不動**(結構線仍約 2.3:1、氣泡邊框仍約 1.6:1,跟前一輪柔化後的手感
       相同),換的是色相不是輕重——這樣既維持千卉要的柔和度,也讓線條跟
       底色同屬一個藍色家族。
       --c-border 本身維持原值不動:它還餵給 --block-border-color/
       --border-color-primary 這兩個 Gradio 原生元件用的變數,動了會牽連
       一整批沒盤點過的元件。 */
    --c-border-strong:   rgba(2,132,199,.30);
    --c-divider:         #a9cbe8;
    /* 對話氣泡邊框獨立一個 token:前一版讓它共用 --c-divider,結果深淺兩邊
       對不齊——淺色的 #a9cbe8 是對著「結構線那一級的深色值(alpha .28)」
       挑的,但深色的 --c-divider 其實是更淡的 .18,實測深色氣泡邊框只有
       1.37:1、淺色 1.61:1,兩個模式看起來不一樣。分成獨立 token 之後兩邊
       各自對齊到 1.61:1,--c-divider 也回歸單一用途(表格資料列細線)。 */
    --c-bubble-border:   #a9cbe8;
    /* 表格表頭底色與對話氣泡底色改成獨立 token(原本借用 --c-surface2/
       --c-surface):淺色時兩者疊在自己的鄰接底色上都只有 1.03–1.07:1,
       業主回報「表頭跟文字黏在一起」「看不清楚區塊的顏色」就是這個。 */
    --c-thead-bg:        #d8e8f6;
    --c-bubble-bg:       #ffffff;
    --c-accent:          #0284c7;
    --c-accent-strong:   #0369a1;
    --c-accent2:         #4f46e5;
    --c-accent2-strong:  #4338ca;
    --c-accent3:         #9333ea;
    --c-accent-rgb:      2,132,199;
    --c-accent2-rgb:     79,70,229;
    --c-accent3-rgb:     147,51,234;
    --c-bg-rgb:          255,255,255;
    --c-text:            #1e293b;
    --c-text-soft:       #334155;
    --c-text-muted:      #475569;
    --c-text-subtle:     #64748b;
    --c-success:         #047857;
    --c-success-rgb:     4,120,87;
    --c-warn:            #b45309;
    --c-user-bg:         #dcfce7;
    --c-user-border:     #86efac;
    --c-user-text:       #166534;
    --c-code-bg:         #e0f2fe;
    --c-code-text:       #b45309;
    --c-input-bg:        #ffffff;
    /* 2026-08-12:原本 rgba(2,132,199,.22) 疊白只有約 1.3:1,輸入框邊界同樣
       受 1.4.11 的 3:1 規範;語料 metadata 表單「欄位看不清楚」有一半是這條。 */
    --c-input-border:    #7fb0d9;
    --c-link-hover:      #075985;
    --c-scrollbar:       #cbd5e1;
    --c-scrollbar-hover: #94a3b8;

    --font-size-base: 0.875rem;

    --body-background-fill:      var(--c-bg) !important;
    --background-fill-primary:   var(--c-surface) !important;
    --background-fill-secondary: #dfeaf6 !important;
    --block-background-fill:     var(--c-surface) !important;
    --block-border-color:        var(--c-border) !important;
    --border-color-primary:      var(--c-border) !important;
    --color-accent:              var(--c-accent) !important;
    --color-accent-soft:         rgba(2,132,199,.08) !important;
    --body-text-color:           var(--c-text) !important;
    --block-label-text-color:    var(--c-text-muted) !important;
    --block-title-text-color:    var(--c-text-soft) !important;
    --input-background-fill:     var(--c-input-bg) !important;
    --input-border-color:        var(--c-input-border) !important;
    --button-primary-background-fill: var(--c-accent) !important;
    --button-primary-text-color: #fff !important;
    --shadow-drop:               0 1px 3px rgba(15,23,42,.10) !important;
}
/* 注意:系統深色模式的 @media 覆寫改放在 launch(head=...) 注入的獨立 <style>(見 _DARK_MODE_HEAD_CSS),
   因為 Gradio 會把 css= 內的選擇器自動加上 .gradio-container .contain 前綴,
   :root 一旦被包進 @media 只會留下加了前綴的版本,而 ":root" 不可能是任何元素的子孫,等於永遠不會命中。 */
/* 明確以 ?__theme=dark 網址參數強制深色時 */
.dark {
    --c-bg:              #0b1424;
    --c-surface:         #172136;
    --c-surface2:        #1b2740;
    --c-surface3:        #1e293b;
    --c-surface3-border: #334155;
    --c-border:          rgba(14,165,233,.11);
    --c-border-hover:    rgba(14,165,233,.30);
    /* 深色的結構線沿用原本散在 .app-header/.nav-col/.chat-col/標題底線
       各處的硬寫值 rgba(148,163,184,.28)(已通過實機檢視),token 化之後
       那些個別覆寫規則就整組刪掉了,避免同一個色值散落六處。 */
    --c-border-strong:   rgba(148,163,184,.28);
    --c-divider:         rgba(148,163,184,.18);
    /* .26 是實測對齊值:疊在深色氣泡底 #172136 上等於 1.61:1,跟淺色
       #a9cbe8 疊 #f5faff 的 1.61:1 相同(見 :root 的同名 token 註解)。 */
    --c-bubble-border:   rgba(148,163,184,.26);
    --c-thead-bg:        #1e2a42;
    --c-bubble-bg:       #172136;
    --c-accent:          #0ea5e9;
    --c-accent-strong:   #38bdf8;
    --c-accent2:         #818cf8;
    --c-accent2-strong:  #a5b4fc;
    --c-accent3:         #c084fc;
    --c-accent-rgb:      14,165,233;
    --c-accent2-rgb:     129,140,248;
    --c-accent3-rgb:     192,132,252;
    --c-bg-rgb:          9,14,26;
    --c-text:            #e2e8f0;
    --c-text-soft:       #cbd5e1;
    --c-text-muted:      #94a3b8;
    --c-text-subtle:     #475569;
    --c-success:         #34d399;
    --c-success-rgb:     52,211,153;
    --c-warn:            #fbbf24;
    --c-user-bg:         rgba(34,197,94,.18);
    --c-user-border:     rgba(34,197,94,.42);
    --c-user-text:       #bbf7d0;
    --c-code-bg:         rgba(14,165,233,.12);
    --c-code-text:       #fbbf24;
    --c-input-bg:        #1a2540;
    --c-input-border:    rgba(14,165,233,.15);
    --c-link-hover:      #7dd3fc;
    --c-scrollbar:       #1e3a5f;
    --c-scrollbar-hover: #334d6e;

    --body-background-fill:      var(--c-bg) !important;
    --background-fill-primary:   var(--c-surface) !important;
    --background-fill-secondary: #080f1c !important;
    --block-background-fill:     var(--c-surface) !important;
    --block-border-color:        var(--c-border) !important;
    --border-color-primary:      var(--c-border) !important;
    --color-accent:              var(--c-accent) !important;
    --color-accent-soft:         rgba(14,165,233,.08) !important;
    --body-text-color:           var(--c-text) !important;
    --block-label-text-color:    var(--c-text-muted) !important;
    --block-title-text-color:    var(--c-text-soft) !important;
    --input-background-fill:     var(--c-input-bg) !important;
    --input-border-color:        var(--c-input-border) !important;
    --button-primary-background-fill: var(--c-accent) !important;
    --button-primary-text-color: #fff !important;
    --shadow-drop:               0 1px 3px rgba(0,0,0,.5) !important;
}
/* width 一定要明確指定,否則容器會依內容伸縮(本來就沒設定過,這是造成
   對話視窗/按鈕/上傳卡片寬度在不同狀態間跳動不一致的根本原因)。
   原本 max-width:1680px + margin:0 auto 是舊版單欄/二欄置中版面留下的
   設定;Phase 8 三欄式工作區(nav-col/chat-col/canvas-col)改成滿版
   貼齊左右邊界(千卉要求 2026-07-22),改成 width:100%、拿掉
   max-width 上限與左右 padding。首頁 .homepage-col 自己有獨立的
   max-width:50rem + margin auto 置中,不受這裡影響,寬螢幕上還是會維持
   舒適閱讀寬度、不會被拉滿版。 */
.gradio-container {
    width: 100% !important;
    max-width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
}
/* .gradio-container 內部還包了一層 Gradio 自己的 .app(class="main fillable app")
   wrapper。原本以為是它的左右 padding 造成貼不到邊界,歸零後留白仍在;
   千卉 2026-07-23 截圖回報四邊仍有留白,重新用 devtools 沿著
   .main-layout-row 往上逐層量 computed style 才找到真因:.app 被 Gradio
   設了 `max-width:1280px` 搭配 auto 置中的 margin-left(量到 77.5px =
   (1435-1280)/2,抓包抓得很準),是 margin 不是 padding。真正修法是拿掉
   max-width 限制、margin 歸零,padding 那條規則留著無害但其實沒解決過
   問題。
   後續(同日):千卉截圖標出頂部/底部還有大片空白,實測 .app 本身還有
   `padding:16px 0px`(上下各16px,之前只清過左右)、.gradio-container
   也還有 `padding:0 0 24px`(底部24px)——這兩個之前為了「左右貼齊」
   只處理過左右方向,上下方向從沒動過,這次一併歸零。 */
.app {
    padding: 0 !important;
    max-width: 100% !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
}
/* 2026-07-23:千卉回報頂部列(D)沒有真正固定置頂、底部「透過API使用/
   設定」(G)也沒有固定置底。實測用 devtools 沿著 <footer> 往上量整條
   祖先鏈才發現:Gradio 原生把 gradio-app/.gradio-container/.app/
   .wrap 這四層全部設成 display:flex; flex-direction:column,.wrap 底下
   剛好就是「主內容(.contain)+footer」兩個 flex 子項——結構本來就是
   對的,問題是這條鏈的高度全部用 auto(依內容撐高),不是真正鎖定
   100vh。內容只要比視窗高一點點(哪怕只多15px),整個外層文件就會
   跟著捲動,把「理論上該固定」的頂部列/底部連結一起拖走——這才是
   「沒有真正置頂/置底」的根本原因,不是 sticky/fixed 沒設對。
   修法:把這條 Gradio 原生就是 flex column 的鏈路鎖死在 100vh,讓
   .contain(真正裝我們 gr.Blocks 內容的那層)變成唯一會捲動的區域,
   footer 用 flex-shrink:0 釘在最底。這樣完全不用碰 position:sticky/
   fixed(8-B 已經踩過 sticky 在 Gradio wrapper 裡貼不住的坑),延續
   這個檔案「結構性分離,不依賴 sticky」的既有原則,只是這次分離的
   層級是整個頁面殼,不只是單一元件。 */
html, body {
    height: 100% !important;
    margin: 0 !important;
}
gradio-app {
    display: block !important;
    height: 100vh !important;
    overflow: hidden !important;
}
.gradio-container {
    height: 100vh !important;
    overflow: hidden !important;
}
.app {
    height: 100% !important;
    overflow: hidden !important;
}
.wrap {
    height: 100% !important;
    overflow: hidden !important;
}
.contain {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    overflow-y: auto !important;
}
/* 2026-07-23:千卉確認頂部/底部貼齊後,決定整個 footer(透過API使用/
   設定連結列)不要了,直接拿掉,不只是隱藏內容——footer_links 參數也
   一併清空,避免 Gradio 還是生出一個空的 <footer> 佔用 flex 高度。 */
footer {
    display: none !important;
}
/* 千卉截圖回報頂部列還是沒有真正貼齊頂端,量到 main-layout-row 距離
   .contain 頂端還有 72px 空隙——追出來是 .nav-col/.chat-col/.canvas-col
   原本寫死的 `calc(100vh - 150px)` 這個 magic number,是照舊版「頁面
   周圍還有一堆 padding/margin」的年代校準出來的高度預算。這次把
   .app/.gradio-container 的上下 padding 都清空、footer 也鎖定
   flex-shrink:0 之後,實際可用空間比 150px 這個預留值還多,多出來的
   空間沒地方去,就變成三欄跟外層容器之間看得到的空隙。
   修法:不要再猜一個固定 px 預算,改成整條鏈都用 flex 100% 往下傳,
   有多少空間就填多少,不會再跟實際版面脫節。.contain 底下只有一個
   直屬子元素(Gradio Blocks 的根 Column),這裡讓它 flex 撐滿
   .contain,裡面的 main-layout-row 再 flex 撐滿它,nav-col/chat-col/
   canvas-col 三欄改成 height:100%(相對 main-layout-row 的高度),
   不再各自寫死 calc(100vh - 150px)。 */
.contain > .column {
    display: flex !important;
    flex-direction: column !important;
    height: 100% !important;
    min-height: 0 !important;
}
.main-layout-row {
    flex: 1 1 auto !important;
    min-height: 0 !important;
}
*, *::before, *::after { box-sizing: border-box; }
/* 全域安全網(陷阱 13 第 3 點):Gradio 的 markdown 渲染統一包在
   <span class="md ...">,預設是 inline 卻常常裝 block 層級的 <p>/<ul> 等,
   會在內容最前面產生依 span 自己字級算出的隱形空隙。這裡統一改成 block,
   避免以後在還沒踩過的地方(如新的 gr.Markdown 元件)又重複這個問題;
   字級仍留給各自的選擇器決定,這裡不覆寫 font-size。 */
.md { display: block !important; }

/* ── 2. HEADER(卡片化,2026-07-23 精簡成單行)───────────────────── */
/* 2026-07-23 千卉實機回饋:原本整塊是「有外框+漸層底色+圓角」的卡片,
   看起來像一個獨立區塊漂在畫面上方。改成完全扁平:拿掉外框、底色、圓角,
   只留一條下邊框當工作區的水平分界;產品名與步驟列改上下兩行。 */
.app-header {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    flex-wrap: nowrap;
    gap: .9rem;
    text-align: left;
    padding: 1.5rem 1.25rem 1.1rem;
    position: relative;
    margin: 0 0 1.25rem;
    border: none;
    /* 頂部標題與內容的分界線,用結構線 token(--c-border-strong)。
       2026-08-12 之前這裡是 --c-border 加上「.dark .app-header 個別覆寫成
       rgba(148,163,184,.28)」的組合(深色太淡的 #17),淺色端則一直沒補、
       業主回報看不清楚。兩邊的值現在都收進 --c-border-strong,這裡不再需要
       任何深色個別覆寫。
       ── 保留的踩坑記錄(雖然這批 .dark 規則已刪,通則仍成立)──
       Gradio 會自動幫 CSS 選擇器加容器前綴,這個機制處理「.dark A, .dark B」
       這種逗號並列、且每個分支都以 .dark 開頭的選擇器時有 bug:只有第一個
       分支前綴正確,後面每個分支都被錯誤地多插一次 .dark(變成需要兩層
       .dark 祖先才能匹配,永遠選不到東西,等於整條規則除了第一分支都是死的)。
       已在 Gradio 6.20.0 實測。之後任何 .dark 開頭的規則要並列多個選擇器,
       都要拆成一條一個選擇器,不要用逗號合併。 */
    border-bottom: 1px solid var(--c-border-strong);
    background: none;
}
/* 注意:這三個選擇器的漸層文字底色(background-image: linear-gradient(...var()...))
   改在 launch(head=...) 注入的 <style> 裡設定(見 _DARK_MODE_HEAD_CSS)——
   這裡只保留其餘不受影響的樣式(字型、間距等)。
   踩坑(2026-07-22 千卉實機回報,首頁與 Canvas 標題皆會發生,須反白選取
   才看得到):真正的根因不是重繪/合成層問題(曾誤判為 Chromium 動態內容
   repaint bug,試過 transform:translateZ(0) 無效,已移除),而是瀏覽器
   對 `background` 這個簡寫屬性(shorthand)解析失敗——當值是
   `linear-gradient(...多個裸 var() 當色標...)` 且整條宣告掛
   `!important` 時,瀏覽器會把簡寫展開成多個子屬性(background-image/
   background-color/...),但 background-image 這個子屬性最終解析成空值
   (用 devtools 讀 CSSStyleDeclaration 已直接證實:getPropertyValue
   ('background-image') 回傳空字串,即使 getPropertyPriority 顯示
   'important'),導致完全沒有漸層可畫、文字又是 transparent,看起來就是
   整個標題消失;反白選取會強制瀏覽器重繪,這次改用其他路徑重新算出
   實際漸層才「顯現」。修法:不要用 `background` 簡寫,直接寫
   `background-image: linear-gradient(...) !important;` 這個子屬性本身,
   跳過簡寫展開的解析路徑——已用瀏覽器 devtools 即時修改驗證過,改長寫法
   後漸層立即正常顯示。*/
.app-header .title-line {
    font-family: 'Crimson Pro', 'Noto Serif TC', Georgia, serif !important;
    font-size: 1.3rem !important;
    font-weight: 700 !important;
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    margin: 0 !important;
    letter-spacing: -.01em;
    line-height: 1.2 !important;
    white-space: nowrap;
    flex: none;
}
.app-header .title-line .en {
    font-size: 1.05rem !important;
    font-weight: 600 !important;
}
.badge {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 9px;
    border-radius: 20px;
    font-size: 0.70rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    margin: 0 3px;
    vertical-align: middle;
}
.badge-llm1 {
    background: rgba(var(--c-accent-rgb),.10);
    color: var(--c-accent-strong);
    border: 1px solid rgba(var(--c-accent-rgb),.30);
}
.badge-llm2 {
    background: rgba(var(--c-accent2-rgb),.10);
    color: var(--c-accent2-strong);
    border: 1px solid rgba(var(--c-accent2-rgb),.30);
}
/* step indicators:2026-07-23 二度調整——header 改成上下兩行(產品名一行、
   步驟列一行),所以這裡不再需要 flex:1 去跟標題搶同一行的水平空間,改成
   自己佔滿整行寬度。兩行之間的間距一樣交給 .app-header 的 gap,不另設
   margin-top,避免兩邊疊加。 */
.app-header .steps {
    display: flex; justify-content: flex-start; flex-wrap: wrap;
    gap: 4px; margin: 0; width: 100%; min-width: 0;
}
.step-pill {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 4px 13px;
    border-radius: 20px;
    font-size: 0.72rem;
    font-weight: 600;
    background: rgba(var(--c-accent-rgb),.06);
    border: 1px solid rgba(var(--c-accent-rgb),.30);
    color: var(--c-text-soft);
    letter-spacing: .03em;
    transition: border-color .2s, background .2s;
}
.step-pill:hover {
    background: rgba(var(--c-accent-rgb),.12);
    border-color: rgba(var(--c-accent-rgb),.55);
}
.step-pill .step-num {
    width: 17px; height: 17px; border-radius: 50%;
    background: rgba(var(--c-accent-rgb),.22);
    color: var(--c-accent-strong);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 0.62rem; font-weight: 800;
}
/* 已完成步驟:綠色實心,取代原本永遠同一個樣子的裝飾用藥丸 */
.step-pill--done {
    background: rgba(var(--c-success-rgb),.14);
    border-color: rgba(var(--c-success-rgb),.4);
    color: var(--c-success);
}
.step-pill--done .step-num {
    background: var(--c-success);
    color: #fff;
    font-size: 0.68rem;
}
/* 目前所在步驟:強調外框 + 持續的呼吸動畫。單一步驟內部可能安靜運算好幾
   分鐘(如指標計算),沒有新的 log 出現時畫面會像凍住,加一個不間斷的
   輕微動畫給使用者「系統還在動」的持續訊號;respects prefers-reduced-motion
   (見全域 REDUCED MOTION 規則,會自動把動畫時長歸零) */
.step-pill--current {
    background: rgba(var(--c-accent-rgb),.16);
    border: 1px solid var(--c-accent);
    color: var(--c-accent-strong);
    font-weight: 700;
    animation: stepPulse 1.6s ease-in-out infinite;
}
.step-pill--current .step-num {
    background: var(--c-accent);
    color: #fff;
}
@keyframes stepPulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(var(--c-accent-rgb),.35); }
    50%      { box-shadow: 0 0 0 5px rgba(var(--c-accent-rgb),0); }
}
/* 執行失敗卡住的步驟:紅色警示樣式,且不套用 stepPulse 呼吸動畫——原本
   失敗時圓點會卡在「進行中」動畫永遠閃爍,讓使用者誤以為系統還在運算 */
.step-pill--failed {
    background: rgba(220,38,38,.14);
    border-color: rgba(220,38,38,.4);
    color: #dc2626;
    font-weight: 700;
}
.step-pill--failed .step-num {
    background: #dc2626;
    color: #fff;
}
/* ── 3. TABS ───────────────────────────────────────────────────── */
/* 2026-07-24(E1):這裡原本有一整組 `.tabs > .tab-nav` sticky/背景/hover/
   selected 樣式,但這版 Gradio 的實際 DOM 是 `.tabs > .tab-wrapper >
   .tab-container`、沒有任何 `.tab-nav` 節點,那組規則從寫下來就是死規則,
   已整組移除。 */
/* 2026-07-24(item 1):統計四子頁包在 .tabitem 裡,其 1.5rem 上 padding
   讓標題比「語言指標分析」頁(無 tabitem,走 .canvas-result margin)多縮
   24px,三頁標題上方留白不一致(實測 idx 42px vs stat 66px)。上 padding
   歸零讓統計頁標題對齊 idx;水平與下方 padding 保留。 */
/* 水平 padding 移到 .canvas-result 統一處理(見該處註解),這裡只留下方
   間距;上方 0 是為了跟「語言指標分析」頁標題頂端對齊(item 1)。 */
.tabitem { padding: 0 0 1.5rem !important; }
/* 2026-07-24(C1):統計比較分析的四個子頁改由左側導覽列驅動後,Canvas 內
   這排分頁按鈕列(敘述統計|差異性分析|…)跟左欄四個項目是同一組選項的兩套
   UI,重複。整排在視覺上隱藏,但 gr.Tabs 結構保留——巢狀分頁的切換仍靠 JS 去
   點這排按鈕(見 bindResultNav 的 tabButtons.click)。
   踩坑(連踩兩種隱藏法):
   ① display:none —— .click() 完全不觸發切換,這版 Gradio 的 Svelte on:click
      對移出渲染樹的元素不反應。
   ② 標準 visually-hidden(position:absolute + width:1px + clip)—— 也失敗,
      但原因不同:把 wrapper 縮成 1px 寬會觸發 Gradio 自己的分頁溢出邏輯,把
      四個分頁全塞進 overflow dropdown,可見容器只剩一顆溢出選單鈕,JS 選擇器
      (抓可見容器的 > button)就抓不到分頁按鈕了。
   正解:必須保留 width:100%,Gradio 才會把四個分頁留在可見容器、不觸發溢出;
   只用 height:0 + overflow:hidden + opacity:0 做視覺隱藏(不動寬度、不改
   position)。按鈕高度歸零但仍在渲染樹、寬度正常,.click() 照常切換。
   (實測:四顆按鈕在位、點第 4 顆→集群分析、點第 1 顆→敘述統計,正確。) */
#canvas_stat .tabs > .tab-wrapper {
    height: 0 !important;
    min-height: 0 !important;
    overflow: hidden !important;
    opacity: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
}

/* ── 4. CHATBOT ────────────────────────────────────────────────── */
.chatbot {
    border: 1px solid var(--c-border-strong) !important;
    border-radius: 14px !important;
    background: var(--c-surface) !important;
}
/* 主對話視窗:下方接技術執行紀錄 Accordion,兩者視覺上合併成一張卡片
   (只留上方圓角,底部邊框拿掉,改由 Accordion 頂端的邊框當作分界線) */
/* 2026-07-24(仿 Claude):對話視窗貼齊欄邊——拿掉外框、圓角、白色底,
   背景透明吃 chat-col 的底色,只有氣泡本身有底色。 */
.main-chat {
    background: transparent !important;
    border: none !important;
    border-radius: 0 !important;
}
.main-chat .bubble-wrap,
.main-chat div[role="log"] {
    background: transparent !important;
}
/* #2 消除重複滾軸:只讓 bubble-wrap 捲動,外層不捲 */
.main-chat [class*="wrapper"],
.main-chat > div > div:not([role="log"]) {
    overflow: hidden !important;
}
.main-chat div[role="log"].bubble-wrap {
    overflow-y: auto !important;
    overflow-x: hidden !important;
}
/* 2026-07-23:.main-chat 外層改用 flex-grow 決定高度(見「5b. COLUMN
   LAYOUT」區塊的 5:1 比例設定),中間這層 wrapper 也要跟著撐滿,
   高度才會一路傳到 bubble-wrap,捲動區域才會是「剩下的全部空間」
   而不是「內容多高就多高」。 */
.main-chat > div,
.main-chat [class*="wrapper"] {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    display: flex !important;
    flex-direction: column !important;
}

/* 訊息從頂部開始排列,消除空白 */
div[role="log"].bubble-wrap,
div[role="log"][aria-label="chatbot conversation"],
.main-chat .bubble-wrap,
.bubble-wrap {
    flex-direction: column !important;
    justify-content: flex-start !important;
    align-content: flex-start !important;
    align-items: flex-start !important;
    min-height: 0 !important;
    /* 2026-07-24 千卉:對話列的左右留白對齊導覽列(16px)。原本 .5rem(8px)
       讓氣泡比導覽列項目更貼邊,三欄留白不一致。 */
    padding: 16px !important;
}
/* 清除 Gradio 在 bubble-wrap 內加的空白 spacer div */
.bubble-wrap > div:empty { display: none !important; }
.bubble-wrap > :first-child { margin-top: 0 !important; padding-top: 0 !important; }

/* ── 隱藏訊息計數氣泡、頁碼、複製按鈕浮層 ── */
.main-chat .count,
.main-chat .message-count,
.main-chat [class*="count"],
.main-chat .page,
.main-chat .page-count,
.main-chat .scroll-hide,
.main-chat .copy-text-button,
.main-chat .copy-btn,
.main-chat button[title*="copy" i],
.main-chat button[aria-label*="copy" i],
.main-chat [data-testid*="copy"],
.main-chat .bubble-wrap > div:last-child > span:only-child {
    display: none !important;
}
/* 徹底隱藏對話視窗內所有複製/工具浮層圖示 */
.main-chat .icon-button-wrapper,
.main-chat [class*="icon-button"],
.main-chat .message-buttons,
.main-chat .message-row button,
.main-chat button[title*="複製"],
.main-chat button[aria-label*="複製"] {
    display: none !important;
}

/* ── 通用訊息氣泡 ── */
/* 不同時間產生的訊息之間如果間距太小,會分不出是好幾則獨立訊息還是同一則,
   拉大訊息列間距讓每則訊息之間有清楚的留白斷開 */
/* Gradio 預設給每個 message-row 左右各留 20px margin(原本應是保留給頭像用的
   空間,本專案沒有用頭像),導致 AI 訊息離左邊、使用者訊息離右邊都比預期遠,
   這裡歸零讓氣泡貼近對話視窗邊緣。另外 Gradio 還設了 max-width: calc(100% - 60px),
   AI 訊息貼左邊不受影響,但使用者訊息靠 margin-left:auto 貼右邊,這 60px
   缺口會整個落在最右側,一併覆寫掉 */
.main-chat .message-row {
    margin-bottom: 1.1rem !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
    width: 100% !important;
    max-width: 100% !important;
    align-self: stretch !important;
}
/* 2026-07-24(千卉回報氣泡留白遠超過 16px):這一層是「看不見」的外框,
   真正畫出氣泡底色與圓角的是內層 .md(見下方註解與本專案筆記)。它原本的
   水平 padding 1rem 會把可見氣泡再往內推 16px——bubble-wrap 已給 16px,
   疊起來可見氣泡實際落在 32px。水平內距歸零,可見氣泡才真的貼在 16px;
   氣泡內的文字留白由 .md 自己的 padding 提供。 */
/* 2026-07-30(2-C 第 6 項):對話氣泡文字改 12px(千卉指定)。字級固定寫在
   這裡跟下面 .message-row.bot .message/.message p/.message .md 四個地方
   ——不是重複設定,是 Gradio 這個氣泡結構本來就有巢狀 <p>/<span class="md">
   各自帶自己的字級(見下面兩則舊註解,實測不會沿用外層容器字級),四層
   都要一起改成 12px,畫面上才會真的變小;只改最外層這條,實際顯示的文字
   仍會被內層 .p/.md 的 var(--font-size-base) 蓋掉,完全看不出效果。 */
/* 2026-07-31(Batch G 第 3 項):業主回報 12px 太小,改 14px。四處連動改,
   理由同上——只改一處字級不一致,這裡跟下面 .message-row.bot .message/
   .message p/.message .md 四處都要同步改成 14px。 */
.main-chat .message-row .message {
    border-radius: 10px !important;
    font-size: 14px !important;
    line-height: 1.7 !important;
    padding: 0 !important;
    width: fit-content !important;
    max-width: 90% !important;
    transition: box-shadow .2s !important;
}
/* Gradio 在氣泡外層又包了一層同樣帶 .message class 的巢狀 div(非語意重複,
   是它自己的 panel-full-width 包裝),上面這條規則會連內層也命中,
   造成 padding 上下疊加兩次,視覺上像是文字最上面跟最下面多了一整行空白。
   這裡把內層那份歸零,只留外層真正的框線與留白 */
.main-chat .message-row .message .message {
    padding: 0 !important;
    border-radius: 0 !important;
    max-width: 100% !important;
}

/* 使用者訊息:淺灰底色跟白色頁面背景錯開,不用外框線,靠右對齊與置左的
   AI 訊息做區分 */
/* Gradio 實際輸出的內層元素同時帶 user/bot 與 message 兩個 class(非巢狀父子),
   用複合選擇器只命中該元素本身,避免命中內層 panel-full-width 造成雙重框線 */
.main-chat .message-row.user .message,
.main-chat .message-row.user-row .user.message {
    background: transparent !important;
    border: none !important;
    border-radius: 0 !important;
    color: var(--c-text) !important;
    margin-left: auto !important;
    font-weight: 500 !important;
    box-shadow: none !important;
    max-width: 80% !important;
    text-align: right !important;
}
.main-chat .message-row.user,
.main-chat .message-row.user-row { margin-bottom: 1.1rem !important; }
/* 2026-08-22(業主:使用者泡泡外框/底色希望跟 AI 不同):真正畫出氣泡底色與
   邊框的是內層 .md(見上面「AI 訊息」區塊那條共用選擇器 .message-row .message
   .md),使用者與 AI 訊息目前吃到同一份 --c-bubble-bg/--c-bubble-border,
   看起來一樣。這裡只針對使用者泡泡的 .md 覆寫,改用專屬的 --c-user-bg/
   --c-user-border/--c-user-text(原本就有定義、只是從沒接到任何選擇器上)
   ——淺色是淡綠、深色是半透明綠,跟 AI 泡泡的中性藍灰做出區隔。 */
.main-chat .message-row.user .message .md,
.main-chat .message-row.user-row .user.message .md {
    background: var(--c-user-bg) !important;
    border: 1px solid var(--c-user-border) !important;
    color: var(--c-user-text) !important;
}

/* AI 訊息(主對話視窗):淺灰底色跟白色頁面背景錯開,不用外框線 */
.main-chat .message-row.bot .message,
.main-chat .message-row.bot-row .bot.message {
    background: transparent !important;
    border: none !important;
    border-radius: 0 !important;
    color: var(--c-text) !important;
    font-size: 14px !important;
    line-height: 1.7 !important;
    box-shadow: none !important;
    text-align: left !important;
}
/* Gradio 產生的 <p> 段落本身有自己的 font-size(實測為固定 16px),
   不會沿用上面設在外層容器的字級,且段落間距預設高達 16px,
   視覺上偏大又有明顯空行,這裡直接命中 <p> 覆寫 */
.main-chat .message-row .message p {
    font-size: 14px !important;
    line-height: 1.6 !important;
    margin: 0 0 .5rem !important;
}
.main-chat .message-row .message p:last-child { margin-bottom: 0 !important; }
/* 包住段落的 <span class="md prose"> 預設是 inline,卻裡面塞了 block 層級的
   <p>(不合法巢狀),瀏覽器會讓這個 span 依自己的字級(16px)先產生一個看
   不見的行高空隙,出現在第一段之前——這也是最上面「多一行空白」的真正來源。
   改成 block 並統一字級後,這段隱形空隙就會消失 */
.main-chat .message-row .message .md {
    display: block !important;
    font-size: 14px !important;
    line-height: 1.6 !important;
    padding: 0.9rem 1.1rem !important;
    /* 2026-07-24:氣泡底色改白(淺色)/較淺的 surface(深色)。原本是
       --c-surface2(淺灰),改成 --c-surface:淺色=白、深色=#0d1526(比
       chat-col 的 --c-bg #020617 淺一階),氣泡浮在欄底上。 */
    /* 2026-08-12(業主回報淺色看不清楚區塊):白氣泡疊在 chat-col 的
       --c-bg #f8fafc 上只有 1.07:1,等於沒有邊界;深色那一階(#0d1526 疊
       #020617)本來就夠,所以只動淺色值——改用 --c-bubble-bg(淺色 #eef2f7、
       深色維持 #0d1526)。填色差在近白區間拉不到 3:1(那要暗到不像對話框),
       填色之外還要一條邊框才看得出範圍。
       2026-08-12 二修(千卉:淺色氣泡也要淡邊框、深淺一致):邊框從結構線
       降到細線 --c-divider。淺色 #b6c0cd 對氣泡底 1.64:1、對欄底 1.76:1,
       深色 rgba(148,163,184,.28) 疊 #0d1526 是 1.67:1,兩個模式的邊框
       亮度差幾乎相同,看起來才是同一種「淡邊框」。 */
    background: var(--c-bubble-bg) !important;
    border: 1px solid var(--c-bubble-border) !important;
    border-radius: 10px !important;
}
/* ── AI 頭像(2026-08-13 四版,業主要求改成「彩虹弧線」圖示)───────
   gr.Chatbot(avatar_images=(None, ai-avatar.svg)) 的前端結構是
   .message-row > .avatar-container > img。Gradio 原生給 35px 圓、
   1px 邊框、margin-right: var(--spacing-xxl),這裡收成 28px 並改成
   紫→藍漸層圓底(跟標題字共用同一條 heading-gradient)。
   (中間版本一度試過純色底+固定色碼色譜光線,業主看實機覺得不好看,
   已改回這版:漸層畫在 CSS 不畫在 SVG 裡,理由同最初設計——avatar_images
   是 Python 端參數,同一個檔案沒辦法隨明暗主題換色;交給 CSS 就直接吃
   --c-accent-strong/--c-accent2/--c-accent3 這組已經會跟著主題切換的
   token,不用再另外補 .dark/@media 深色分支,SVG 檔本身只留白色三道
   同心弧(白色描邊疊在任一主題的漸層上都夠亮)。
   踩坑提醒:漸層一律寫 background-image 子屬性,不要用 background
   簡寫(本專案已踩過兩次,簡寫在這條 pipeline 下會解析成空值)。
   flex:none 是防「圖示被 flex 壓縮」那個舊坑(收合 icon bar 曾被壓成 8px)。 */
.main-chat .avatar-container {
    width: 28px !important;
    height: 28px !important;
    flex: none !important;
    align-self: flex-start !important;
    margin-right: 10px !important;
    margin-left: 0 !important;
    border: none !important;
    border-radius: 50% !important;
    background-image: linear-gradient(135deg, var(--c-accent-strong) 0%, var(--c-accent2) 55%, var(--c-accent3) 100%) !important;
}
.main-chat .avatar-container img {
    width: 100% !important;
    height: 100% !important;
    /* 2026-08-12 二版(業主:頭像圖案有點小):內距 5px→3px,圖案在 28px
       圓內的可用範圍從 18px 放大到 22px;SVG 本身的 path 也一併撐大並改成
       實心(見 assets/icons/ai-avatar.svg)。兩件事一起做才有感——單改內距
       只是把細線稿放大,單改 path 又會被內距框住。 */
    padding: 3px !important;
    object-fit: contain !important;
    opacity: 1 !important;
}
/* 頭像佔掉 38px(28+10)之後,氣泡的 90% 上限要從同一條 flex line 扣掉,
   否則長訊息會把對話欄撐出捲軸。 */
.main-chat .message-row.bot .message,
.main-chat .message-row.bot-row .bot.message {
    max-width: calc(90% - 38px) !important;
}
.main-chat .message-row.bot .message strong,
.main-chat .message-row.bot .message b,
.main-chat .message-row.bot-row .bot.message strong,
.main-chat .message-row.bot-row .bot.message b { color: var(--c-accent-strong) !important; font-weight: 700 !important; }
.main-chat .message-row.bot .message em,
.main-chat .message-row.bot .message i,
.main-chat .message-row.bot-row .bot.message em,
.main-chat .message-row.bot-row .bot.message i  { color: var(--c-accent2-strong) !important; }
.main-chat .message-row.bot .message code,
.main-chat .message-row.bot-row .bot.message code {
    background: var(--c-code-bg) !important;
    color: var(--c-code-text) !important;
    padding: 1px 6px !important;
    border-radius: 4px !important;
    font-family: 'JetBrains Mono', monospace !important;
    font-size: 0.82em !important;
}
.main-chat .message-row.bot .message a,
.main-chat .message-row.bot-row .bot.message a {
    color: var(--c-accent-strong) !important;
    text-decoration: underline !important;
    text-underline-offset: 2px !important;
}

/* 2026-07-23:技術執行紀錄從 Accordion 轉正成獨立頁面(canvas_exec_col),
   不再是接在對話視窗卡片下方的「抽屜」,改用跟 canvas_idx_col 等其他三個
   結果頁共用的 .canvas-result class(已在 gr.Column 宣告時掛上),這裡不用
   再另外寫專屬的卡片外框樣式,避免跟共用樣式重複定義出兩套外觀。 */

/* 執行視窗(簡易訊息顯示欄,非對話氣泡) */
/* 原本這裡還設了 max-height + overflow-y:auto 讓內部自己捲動,但整個頁面
   本身也會捲動,兩個捲動容器疊在一起、捲軸位置又離得很近,看起來像「兩條
   捲軸」造成混亂——拿掉內部捲動限制,讓內容自然撐開高度,只用頁面本身的
   捲軸,維持單一捲動體驗 */
/* 2026-07-24(#16):水平 padding 歸零——canvas-result 已提供 1.25rem 左內距
   讓內容跟標題(都在 96px)齊平,這裡原本再加 .75rem(12px) 會把 log 文字推到
   108px、比標題與其他結果頁多縮 12px。只留上下間距。 */
.exec-log {
    background: transparent !important;
    border: none !important;
    padding: .55rem 0 !important;
    margin-top: 0 !important;
}
.exec-log p {
    font-size: var(--font-size-base) !important;
    line-height: 1.5 !important;
    margin: 0 0 .3rem !important;
    padding: 0 !important;
    color: var(--c-text-muted) !important;
    font-family: 'JetBrains Mono', 'Noto Sans TC', monospace !important;
    white-space: pre-wrap !important;
    word-break: break-word !important;
}
.exec-log p:last-child { color: var(--c-text-soft) !important; }
.exec-log code {
    background: none !important;
    color: var(--c-accent-strong) !important;
    padding: 0 .15rem 0 0 !important;
    font-size: var(--font-size-base) !important;
}
/* 跟對話視窗一樣:包住段落的 <span class="md"> 預設 inline 卻裝 block 的
   <p>,會在最前面產生依自己字級算出的隱形空隙,這裡一併改成 block 並統一字級 */
.exec-log .md {
    display: block !important;
    font-size: var(--font-size-base) !important;
}

/* ── 5b. COLUMN LAYOUT ─────────────────────────────────────────── */
/* gap: 0 讓對話視窗跟緊接著的技術執行紀錄 Accordion 完全貼合,
   視覺上合併成一張卡片(見「融合」設計討論) */
/* Phase 8-B:三欄式工作區。nav-col 窄、固定寬度不隨視窗縮放;chat-col/
   canvas-col 各自獨立捲動(各自的內容不會因為對方變長而互相影響),高度
   統一扣掉頁面上方 Tab 列與外距抓一個概略值,之後看實際渲染再微調 */
/* 2026-07-23:跟 chat-col/canvas-col 一樣鎖定高度+自己捲動,理由是
   頁殼改成真 100vh 鎖死之後(見上方 .contain 那組規則),nav-col 如果
   內容比這個高度多(手風琴展開+8個導覽項目在矮視窗上有可能發生),
   沒有自己的 overflow 就會撐開 .main-layout-row,逼 .contain 整層捲動
   ——雖然不會再拖走頂部列/footer了(那兩個已經在 .contain 外面),但
   chat-col/canvas-col 會跟著被迫一起捲動,體驗還是不對。讓三欄各自
   獨立捲動,互不影響。 */
/* border-right:導覽欄與對話欄各在自己的右緣掛一條 1px 垂直分隔線
   (千卉 2026-07-23 指定),三欄之間才有明確的邊界。畫在欄位自己的
   邊框上、不是插入額外的分隔元件,這樣它會自動跟著欄高走完整條。

   padding 左右 8px(千卉 2026-07-23):欄裡的東西都不要貼著兩側邊緣。
   留白設在欄位本身而不是逐項設定,「開始新分析」按鈕、收合鈕、結果導覽
   項目才會全部吃到同一份,不會有人漏掉——先前只設在 .result-nav 上時,
   「開始新分析」因為是 .nav-col 的直接子項,就沒被涵蓋到。 */
.nav-col {
    flex: 0 0 220px !important;
    max-width: 220px !important;
    display: flex !important;
    flex-direction: column !important;
    /* Bug 4-B 修復(2026-07-30):跟 .chat-col/.canvas-col/.main-layout-row
       一樣補上 flex-wrap:nowrap。這裡沿用 Gradio .column 預設的 wrap 值,
       _populate_tabs 把下載捷徑那組(nav_dl_group)掛進來後,導覽列內容
       一旦超過這個固定 height,就會被摺到第二條 flex line(往右排到可視
       寬度外),再被下面的 overflow-x:hidden 裁掉——節點還在 DOM,只是渲染
       到看不見的地方,這正是「報告完成後導覽列項目整組消失」的根因。
       超高內容改走既有的 overflow-y:auto 垂直捲動,不要再被摺行。
       這條 flex-wrap:nowrap 是本檔的地雷區,2-B 這輪改版千萬不要移除
       ——見下一則註解。 */
    flex-wrap: nowrap !important;
    gap: .3rem !important;
    height: 100% !important;
    /* 2026-07-30(2-B 第 2 項)三段式改版:原本 overflow-y:auto 直接設在
       .nav-col 本身,讓「品牌字標+操作項目+(曾經在頂端的)明暗/收合鈕」
       整欄一起捲動。這輪把明暗切換/收合鈕搬到欄底常駐(置底+置右),
       如果還是讓 .nav-col 整欄捲動,內容一多,底部工具列會被捲動推出可視
       範圍——等於復發 Bug 4-B(「內容一多,重要項目跟著捲不見」)的同一種
       症狀,只是這次主角換成底部工具列而不是下載捷徑。修法:改成「上方
       品牌列(固定)+中間 .nav-items-scroll(flex:1,自己 overflow-y:auto)
       +底部 .nav-bottom-toolbar(固定)」三段式結構,捲動只發生在中間段,
       .nav-col 自己的 overflow 改成 hidden(三個直屬子項各自負責自己要不要
       溢出,nav-col 本身不再需要當捲動容器)。flex-wrap:nowrap 依然保留在
       這裡:三個直屬子項總高度若超過 100%,沒有這條會被摺到第二條 flex
       line,跟 Bug 4-B 是同一種機制,不能拿掉。 */
    overflow: hidden !important;
    border-right: 1px solid var(--c-border-strong) !important;
    /* 5-A:導覽列上下左右統一 16px 留白。所有按鈕/項目寬度撐滿這個內距框,
       右上收合鈕的右緣也就落在 16px 處,不再多出 ~20px 空隙。 */
    padding: 16px !important;
}
/* 中間可捲動項目區:研究情境(已隱藏的空殼)、回首頁連結、結果導覽都在
   這裡面。flex:1 讓它吃掉扣除頂端品牌列與底部工具列之後的剩餘高度;
   min-height:0 是關鍵——flex 子項預設 min-height:auto 會用內容高度當下限,
   讓這個子項無法縮小到比內容矮,overflow-y:auto 就永遠不會真的觸發捲動,
   而是把 .nav-col 撐爆(跟 Bug 4-B 的成因是同一條 flexbox 規範,只是換了
   一層)。flex-wrap:nowrap 同理沿用 .nav-col 的防禦寫法,避免這裡也被
   Gradio 預設的 .column flex-wrap:wrap 摺行。 */
.nav-items-scroll {
    display: flex !important;
    flex: 1 1 auto !important;
    flex-direction: column !important;
    flex-wrap: nowrap !important;
    min-height: 0 !important;
    gap: .3rem !important;
    overflow-y: auto !important;
    overflow-x: hidden !important;
}
/* 底部工具列(明暗切換+收合):flex:none 固定高度,不參與中間段的縮放,
   永遠貼在 .nav-col 底部;border-top 當作千卉指定的「上方水平分隔線」。
   justify-content:flex-end 靠右對齊(千卉指定「置底+置右」)。 */
#nav_bottom_toolbar, #nav_bottom_toolbar > .html-container { padding: 0 !important; margin: 0 !important; }
.nav-bottom-toolbar {
    display: flex !important;
    flex: none !important;
    align-items: center !important;
    justify-content: flex-end !important;
    gap: 2px !important;
    padding-top: .6rem !important;
    border-top: 1px solid var(--c-border-strong) !important;
}
/* 真因排查記錄:Gradio 的 .column 預設會帶 flex-wrap:wrap。平常沒設高度時
   不會有事,但這裡額外設了固定 height 讓內容用捲動取代撐高,一旦內容總高
   度超過這個 height,flex-wrap:wrap 會把「排不下」的子元件換到*右邊新開一
   欄*(水平並排),而不是照預期垂直捲動——這就是 canvas-col 一度出現內容
   跑到欄外、往右位移一整欄寬度的真正原因(不是 sticky、也不是 overflow-x
   單獨造成的,那兩個只是讓症狀被看見的地方)。修法:flex-wrap 明確關掉。 */
.chat-col {
    display: flex !important;
    flex-direction: column !important;
    flex-wrap: nowrap !important;
    /* 2026-07-24(仿 Claude 對話欄):對話視窗與輸入框直接貼齊欄邊、彼此
       之間也無間隙——gap 從 1rem 改 0。欄本身給一個底色(--c-bg),讓上面
       的白色氣泡與輸入框浮出來;右側分隔線保留。 */
    gap: 0 !important;
    height: 100% !important;
    overflow: hidden !important;
    min-width: 0 !important;
    background: var(--c-bg) !important;
    border-right: 1px solid var(--c-border-strong) !important;
}
/* 對話視窗:輸入框高度比例 5:1(千卉要求,2026-07-23)。用 flex-grow
   依 chat-col 當下的可用高度分配,不寫死 px,縮放視窗時比例仍會維持;
   chatbot 元件不再用 Gradio 原生 height= 參數(那是寫死 px),改讓外層
   wrapper 高度完全交給 flex 決定。訊息列表本身的捲動仍是 Gradio 原生
   行為(.bubble-wrap 內部自己有捲軸),外層 wrapper 用 overflow:hidden
   避免出現雙捲軸。 */
.main-chat {
    flex: 1 1 auto !important;   /* 2026-07-24:吃掉輸入框以外的全部高度(見 #chat_upload_zone) */
    min-height: 0 !important;
    height: auto !important;
    overflow: hidden !important;
    display: flex !important;
    flex-direction: column !important;
}
/* canvas-col 本身「不」捲動——它只負責把步驟列(固定)跟 canvas-scroll
   (捲動)疊在一起、限制在同一個總高度內。踩坑記錄:原本想讓步驟列用
   position:sticky 貼在 canvas-col 頂端,但 sticky 元素只能在「自己最近的
   容器」範圍內移動,而 Gradio 自動包在 gr.HTML() 外面的 wrapper 高度只比
   步驟列本身高一點點,捲動超過那一點高度後 sticky 就跟著捲走、貼不住。
   改成結構性分離:步驟列跟 canvas-scroll 是兩個獨立區塊,前者天生不參與
   捲動,後者自己 overflow-y:auto,兩者高度互不相干,不用依賴 sticky。 */
.canvas-col {
    display: flex !important;
    flex-direction: column !important;
    flex-wrap: nowrap !important;
    gap: 0 !important;
    height: 100% !important;
    overflow: hidden !important;
    min-width: 0 !important;
}
.canvas-col .app-header {
    flex-shrink: 0 !important;
}
/* 2-A(2026-07-30):頂部產品名+步驟列只在「AI 對話」頁顯示,其他結果頁
   (語言指標分析/敘述統計/差異性分析/監督式學習/非監督式學習/
   LLM輔助分析/技術執行紀錄)都不顯示——那幾頁已經佔滿 Canvas 寬度,
   步驟列這時只是重複資訊。data-page 屬性是 _APP_HEAD_SCRIPT 的
   applyActivePage() 寫在 .main-layout-row 上的既有機制,這裡純加一條
   CSS,不需要新增任何 Python wiring。用 :not([data-page="chat"]) 反選,
   不用把 5 個結果頁 key 都列一次。elem_id 鎖 app_header_html 的外層
   wrapper——elem_classes 會同時蓋在外層 .block 與內層 .prose 兩層 div 上
   (本檔多處踩過這坑,見 .upload-hint 那段完整記錄),id 選擇器沒有這個
   問題。 */
.main-layout-row:not([data-page="chat"]) #app_header_wrap {
    display: none !important;
}
/* 最後一段殘餘留白(10px):Gradio 幫所有 gr.HTML() 元件的內容包了一層
   .html-container,本身有 padding:10px 12px,這是全站每個 gr.HTML()
   共用的預設樣式(首頁卡片、運算中提示等都靠它),不能整個歸零,只精準
   鎖定「包住 .app-header 的那一個」html-container,不影響其他 gr.HTML()
   元件(例如同樣在 canvas-col 裡的 computing_html)。 */
.html-container:has(.app-header) {
    padding: 0 !important;
}
.canvas-scroll {
    display: flex !important;
    flex-direction: column !important;
    flex-wrap: nowrap !important;
    flex: 1 1 auto !important;
    min-height: 0 !important;
    overflow-x: hidden !important;
    overflow-y: auto !important;
    /* 2026-07-24 千卉:Canvas 內容左右等寬。水平內距統一收在這一層(20px,
       跟上方 .app-header 的左右 20px 一致),底下所有內容區塊都吃同一份——
       原本只有 .canvas-result 自己加 20px,而 upload_zone/metadata_zone/
       分析面板是 canvas-scroll 的直屬子項、左右各 0,同一欄裡兩種留白。
       .canvas-result 的水平 padding 因此歸零,避免疊成 40px。 */
    padding-left: 20px !important;
    padding-right: 20px !important;
}
/* 2026-07-23:頁面切換模型。點結果類 nav 項目時 JS 會在 .main-layout-row
   上加這個 class(見 _APP_HEAD_SCRIPT 的 applyActivePage()),對話欄收起、
   Canvas 佔滿剩下的寬度。canvas-col 的 flex 要蓋過 Gradio 自己依 scale=7
   算出的 inline flex-grow,才會真的補滿收起對話欄後空出來的寬度(同一類
   問題 8-E 的 min-width 已經踩過一次,這裡沿用同一個「stylesheet
   !important 蓋得過沒加 !important 的 inline style」原則)。
   踩坑(新案例,跟 8-B 的 .column flex-wrap 是同一大類但這次是 .row 本身
   從沒被設過):.main-layout-row(gr.Row)本身從來沒設過 flex-wrap:nowrap
   ——chat-col/canvas-col 各自「內部」的 flex-wrap:nowrap 只管它們自己的
   子元件怎麼排,不影響 main-layout-row 怎麼排這三欄本身。(訂正 2026-07-30:
   這裡原本誤寫成 nav-col 也在這份清單裡,實際上當時 nav-col 從未設過
   flex-wrap:nowrap——這正是 Bug 4-B「報告完成後導覽列整組消失」被忽略的
   直接原因,已於上方 .nav-col 規則補上,見該處 2026-07-30 註解。)平常
   三欄用 flex-grow 精準分配寬度、剛好不超版所以wrap從沒被觸發過;這次
   canvas-col 拿掉 max-width 上限、又改用 flex-basis:auto 之後,內容較寬
   的表格會把 canvas-col 的 intrinsic 寬度撐到跟 nav-col 放不進同一行,
   Gradio 預設的 flex-wrap:wrap 就把 canvas-col 換到 nav-col 下面另起
   一行——整個版面變成上下堆疊、頁面被撐到 1200px+ 高,捲動 canvas 內容
   時連 nav-col 都跟著被推出視窗外。修法:main-layout-row 本身也要明確
   nowrap,不能只顧內部三欄各自的 nowrap。 */
.main-layout-row {
    flex-wrap: nowrap !important;
}
.main-layout-row.chat-collapsed .chat-col {
    display: none !important;
}
.main-layout-row.chat-collapsed .canvas-col {
    flex: 1 1 auto !important;
    max-width: none !important;
    min-width: 0 !important;
}
/* Phase 8-C 踩坑:結果內容搬進 canvas-scroll 後,內容總高度遠超過
   canvas-col 給的固定 height,理論上該讓 canvas-scroll 自己捲動就好;但
   exec_log_accordion(技術執行紀錄)被壓縮成只剩標題列高度(22px),內容
   反而擠進一個內部小捲動框。原因:CSS flexbox 規範規定「flex 子項的
   automatic minimum size」只有在該子項 overflow:visible 時才會採用內容
   高度當下限,一旦子項 overflow 不是 visible(Gradio 會自動幫 Accordion
   這類元件的外層 wrapper 加上 inline overflow:auto,方便使用者拖曳調整
   大小),下限就變成 0,於是在總內容超出容器高度、瀏覽器要收縮子項時,
   這個子項就被壓到接近 0——同排的 upload_row/metadata_row/canvas_idx_col
   等純 gr.Column 因為維持預設 overflow:visible 沒有這個下限問題,才沒被
   壓縮。修法不是去動 Accordion 的 overflow(那是 Gradio 拖曳調整大小的
   功能,不想關掉),而是直接讓這個捲動容器裡的所有子項都不參與收縮 */
.canvas-scroll > * {
    flex-shrink: 0 !important;
}

/* ── 5d. HOMEPAGE(Phase 8:首頁 4 張情境卡片)──────────────────── */
/* 隱藏版情境按鈕:純作 JS 橋接目標(見 _APP_HEAD_SCRIPT),
   點卡片=模擬點這裡的按鈕,Python 端 .click() 綁定完全不動。
   gr.Button 的 elem_classes 直接掛在 <button> 標籤本身(不像
   gr.HTML() 會多一層 wrapper),所以這裡單純 display:none 就完全
   不佔版面,沒有下面 .nav-html-bridge 那個問題。 */
.sc-bridge-btn { display: none !important; }
/* 2026-07-23 踩坑:gr.HTML() 元件的實際內容跟 Gradio 自動產生的外層
   wrapper(.block)是兩個不同的 DOM 節點——只把內層內容(例如
   #mobile_nav_toggle/#nav_backdrop)設 display:none,wrapper 本身
   還是會依自己的預設 min-height/padding 佔用版面高度,疊加上跟相鄰
   元件的 flex gap,變成看得到但摸不著原因的空白區塊(千卉截圖回報
   頂部/底部大片空白時發現)。第一輪試過 display:contents 沒有真的
   清零(懷疑跟 elem_classes 同時疊在 .block 與 .prose 兩層有關,
   detail 見 create_app() 裡兩個 gr.HTML() 呼叫旁的完整記錄),改用
   最直接可靠的做法:wrapper 直接用 elem_id 鎖定、預設寫死
   display:none——一般 block 元素包一個 display:none 的子元素天生
   零高度,不用依賴 display:contents 這種較冷門的屬性。JS 對應改成
   切換這兩個 wrapper 本身的 display(見 _APP_HEAD_SCRIPT 的
   _syncMobileHamburger()),內層 #mobile_nav_toggle/#nav_backdrop
   自己原本的桌面/手機斷點 CSS 完全不用動。 */
/* 兩層踩坑疊在一起:①這個 wrapper 帶 Gradio 自己的 "padded" class,
   就算內層內容 display:none,wrapper 本身還是會因為 line-height留下
   約20px空氣行高②就算加 height:0 讓 wrapper 自己不佔高度,JS 為了
   手機斷點把它切成 display:block 時,它仍然是「有在排版流程裡」的
   flex 子項,外層 column 的 gap:16px 還是會在它前後插入間距——CSS
   flexbox 的 gap 只認「這個子項是不是 display:none」,不管它自己的
   box 高度是不是 0。用 devtools 逐一 display:none 排查才抓到這一層,
   單靠 height:0 治標不治本。
   真正修法:桌面寬度(>900px)這兩個 wrapper 必須是貨真價實的
   display:none(徹底退出 flex 排版,不只是零高度),用 !important
   蓋掉 JS 可能設的 inline display:block;手機寬度(<=900px)才放行讓
   JS 的 block/none 判斷生效。這樣桌面寬度不管 nav-col 存不存在,這兩個
   wrapper 永遠不佔用任何 flex 空間。 */
#mobile_nav_toggle_wrap, #nav_backdrop_wrap {
    display: none;
}
@media (min-width: 901px) {
    #mobile_nav_toggle_wrap, #nav_backdrop_wrap {
        display: none !important;
    }
}

.homepage-col { max-width: 50rem; margin: 2.5rem auto 3rem; padding: 0 1rem; }
.homepage-hero { text-align: center; margin-bottom: 2.4rem; }
/* 2026-07-30(1-C):業主指定移除首頁第一行「重譯文本量化分析平台」
   (原本的 .kicker),HTML 端已拿掉該 <span>,.kicker 本身連同兩側裝飾線
   的 CSS 規則一併移除(沒有任何元素會再套用它)。 */
/* 2026-07-27:老師確認「譯彩紛呈:重譯文本分析系統」是大系列品牌名稱,
   這一款子產品其實叫「II 分析引擎 SpectraEngine」——原本大系列名稱佔用
   最大最搶眼的 h1+漸層,子產品名反而縮在最小的一行,跟實際定位相反。
   重新分配視覺權重:大系列名稱降級成小字(series-name),子產品名稱升級
   成 h1 大標題+漸層。 */
/* 2026-07-30(1-D/1-E):業主指定放大到 18px/14px(原本 .95rem/.85rem,
   換算 16px 根基約 15.2px/13.6px)。直接寫死 px,不用 rem,避免驗收量測
   時因根字級假設不同而跟業主指定的精確數字對不上。 */
.homepage-hero .series-name {
    margin: .9rem 0 .1rem; font-size: 18px; font-weight: 600;
    color: var(--c-text-soft);
}
.homepage-hero .subtitle { margin: 0 0 1.1rem; font-size: 14px; color: var(--c-text-muted); }
.homepage-hero h1 {
    margin: 0 0 .6rem; font-size: 2.1rem; line-height: 1.3; font-weight: 700;
    /* 同 .app-header .title-line 的漸層文字踩坑(見 _DARK_MODE_HEAD_CSS 上方註解):
       background 簡寫在瀏覽器展開成子屬性時 background-image 會解析成
       空值,改直接寫 background-image 這個子屬性本身即可。這裡進一步採用
       .app-header .title-line 已驗證過、支援巢狀 <span class="en"> 的寫法
       (-webkit-text-fill-color 會被子元素繼承,color:transparent 單獨用
       在有巢狀 span 時未實測過是否可靠,兩個都寫上比較保險)。 */
    background-image: linear-gradient(100deg, var(--c-accent) 10%, var(--c-accent2) 90%);
    -webkit-background-clip: text; background-clip: text;
    color: transparent; -webkit-text-fill-color: transparent;
}
/* 2026-07-31(Batch E 第 1 項):業主回報這行沒有跟其他行對齊置中。
   量測證實:.lede 是唯一被覆寫成 text-align:left 的行,其餘行
   (series-name/subtitle/h1/footnote)都吃 .homepage-hero 本身的
   text-align:center。文字方塊(<p> 的 border box)四行寬度其實一致,
   問題出在「視覺文字置中」——.lede 文字換行後每行長度不同,
   text-align:left 讓每行從方塊左緣起排,量出來的文字視覺中心會比
   方塊中心明顯偏左(1280px 寬實測:文字視覺中心 x≈602,其他行
   x≈640,偏差近 40px,方向是「偏左」不是業主主觀感受的「偏右」,
   可能是她螢幕寬度或縮放比例不同所致)。拿掉這條 text-align:left
   覆寫,改回跟其他行一致的置中,四行的文字視覺中心才會真正對齊。 */
.homepage-hero .lede {
    margin: 1.8rem 0 0; text-align: center; font-size: var(--font-size-base);
    line-height: 1.8; color: var(--c-text-muted);
}
/* 1-B:首頁「開始研究分析」按鈕,進入工作區的唯一入口。這是真正的
   gr.Button(不是首頁卡片那種 HTML+隱藏按鈕橋接手法——單一按鈕不需要,
   見 create_app() 裡 start_analysis_btn 旁的說明),elem_classes 直接掛在
   <button> 標籤本身(同 .sc-bridge-btn 那則註解),用 elem_id 選擇器蓋過
   Gradio 預設樣式。藍紫色漸層沿用 h1 已在用的同一組色票(--c-accent/
   --c-accent2),不是另外發明新配色。width 用 fit-content+margin auto
   置中,不管外層 Gradio 自動包的 wrapper 是否撐滿欄寬都能正確置中。 */
#start_analysis_btn {
    display: flex !important;
    width: fit-content !important;
    margin: 2.2rem auto 0 !important;
    padding: .95rem 2.8rem !important;
    font-size: 1.05rem !important;
    font-weight: 700 !important;
    border: none !important;
    border-radius: 14px !important;
    color: #fff !important;
    background-image: linear-gradient(100deg, var(--c-accent) 10%, var(--c-accent2) 90%) !important;
    box-shadow: 0 16px 40px rgba(var(--c-accent2-rgb),.35) !important;
    transition: filter 150ms ease, box-shadow 150ms ease !important;
}
#start_analysis_btn:hover {
    filter: brightness(1.06);
    box-shadow: 0 20px 46px rgba(var(--c-accent2-rgb),.45) !important;
}
/* 3-A 追加:情境卡片搬進工作區後改垂直排列(業主指定「不要 2×2 矩陣」)。
   這個 class 現在只有工作區的研究選擇區塊在用(首頁卡片已移除,見 1-A),
   直接把兩欄矩陣改成單欄即可,不用另外開一個 class。 */
.scenario-grid { display: grid; grid-template-columns: 1fr; gap: 1rem; margin-top: 1.2rem; }
.scenario-card {
    all: unset; box-sizing: border-box; cursor: pointer;
    display: flex; gap: 1rem; align-items: flex-start; text-align: left;
    padding: 1.3rem 1.4rem;
    background: var(--c-surface);
    border: 1px solid rgba(var(--c-accent-rgb),.40) !important;
    border-radius: 16px;
    box-shadow: 0 16px 50px rgba(15,23,42,.20);
    transition: border-color 180ms ease, box-shadow 180ms ease, background-color 180ms ease;
}
.scenario-card:hover, .scenario-card:focus-visible {
    border-color: rgba(var(--c-accent-rgb),.7) !important;
    background: linear-gradient(180deg, rgba(var(--c-accent-rgb),.06) 0%, var(--c-surface) 100%);
}
.scenario-card:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 2px; }
.scenario-card .sc-index {
    flex: none; font-family: 'Crimson Pro', serif; font-weight: 600;
    font-size: 1.8rem; line-height: 1; padding-top: .1rem;
    color: rgba(var(--c-accent-rgb),.4); transition: color 180ms ease;
}
.scenario-card:hover .sc-index, .scenario-card:focus-visible .sc-index { color: var(--c-accent); }
.scenario-card .sc-body { min-width: 0; display: flex; flex-direction: column; }
.scenario-card .sc-title { margin: 0 0 .35rem; font-size: 1.02rem; font-weight: 700; color: var(--c-text); line-height: 1.4; }
.scenario-card .sc-desc { margin: 0; font-size: var(--font-size-base); color: var(--c-text-muted); line-height: 1.65; }
.homepage-secondary { display: flex; justify-content: center; margin-top: 2rem; }
.homepage-secondary .placeholder-link {
    display: inline-flex; align-items: center; gap: .4rem;
    font-size: var(--font-size-base); color: var(--c-accent); cursor: pointer;
}
.homepage-secondary .placeholder-link:hover { text-decoration: underline; }
.homepage-secondary .placeholder-link svg { width: 1rem; height: 1rem; opacity: 1; }
.homepage-footnote { margin-top: 2rem; text-align: center; font-size: .82rem; color: var(--c-text-subtle); }
@media (prefers-reduced-motion: reduce) { .scenario-card { transition: none; } }

/* ── 5e. NAV-COL 收合/手機漢堡抽屜(Phase 8-E)────────────────── */
/* gr.Button(icon=...) 這版 Gradio 實測是渲染成 <img src="...">,不是行內
   <svg>,CSS currentColor 對它沒有作用(img 不會繼承文字顏色)。因此
   assets/icons/*.svg 檔案裡直接把 stroke 寫死成淺色模式的 --c-text-muted
   (#475569),深色模式改用 filter 把整張圖反相+去彩,逼近深色模式的
   --c-text-muted (#94a3b8) 色調,這是唯一能讓「檔案路徑圖示」跟著主題
   變色的辦法(不用另外準備一份深色版 svg 檔案)。 */
img.button-icon { opacity: .82; }
.dark img.button-icon {
    filter: grayscale(1) invert(1) brightness(1.55) contrast(.85);
}
/* nav-col 收合/手機抽屜控制鈕:桌面收合鈕與手機關閉鈕互斥顯示,
   預設(>900px)只顯示收合鈕;@media (max-width:900px) 那組規則會反過來
   (見下方)。這兩顆是純 gr.HTML() 刻的按鈕,行內 SVG 用 _icon(),
   currentColor 正常運作,不受上面 img.button-icon 的限制。 */
/* 2026-07-24:頂端一列=品牌字標(左)+ 明暗/收合鈕(右)。原本只有一個
   靠右的收合鈕(.nav-collapse-row),改成 space-between 兩端對齊;字標與
   下方「開始新分析」的間距(5-D)由這裡的 margin-bottom 控制,拉近一點。 */
/* Gradio 幫 gr.HTML 外層 .block/.html-container 掛了 ~12px 預設 padding,
   會讓頂端列(品牌+收合鈕)比下方按鈕多縮 12px、右緣多 12px 空隙(就是
   千卉量到的那 20+px)。把這個 wrapper 的 padding 歸零,頂端列才跟按鈕
   一樣貼齊 nav-col 的 16px 內距框。 */
#nav_top_html, #nav_top_html > .html-container { padding: 0 !important; margin: 0 !important; }
.nav-top-row {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: .35rem;
}
/* 2026-07-30(2-B 第 1/2 項):頂端列現在只剩品牌字標——明暗切換/收合鈕
   搬到欄底新的 .nav-bottom-toolbar(見下方),.nav-top-actions 這個右側
   容器已無內容可放,但選擇器留著不刪:nav-top-row 的 justify-content:
   space-between 沒有第二個 flex item 時退化成單純靠左,不影響外觀,刪掉
   反而要動兩處收合樣式的選擇器,徒增風險。 */
.nav-top-actions { display: flex; align-items: center; gap: 2px; }
/* 2026-07-31(Batch E 第 2 項):業主改主意,品牌字標從 3 行改 2 行
   (「譯彩紛呈 II 分析引擎」/「SpectraEngine」),字級統一 1.1rem
   (原本 3 行是 .92rem/.78rem/.66rem 遞減,2 行不再需要遞減層次)。
   原本靠 flex column 讓三行各自不同字級仍能整齊靠左對齊
   (align-items:flex-start)的理由,2 行同字級一樣適用,結構不用改。
   漸層維持套在同一個外層元素上(不是各行各自 background-clip):
   background-image 畫在 .nav-brand 整個 box 上,兩行文字一起被裁切顯示,
   漸層才會像流過兩行的單一色帶,而不是每行各自重新起始一次相同的漸層
   (後者會讓每行左端都是同一個顏色,視覺上像兩條各自獨立的色塊)。 */
.nav-brand {
    display: flex; flex-direction: column; align-items: flex-start;
    font-weight: 700; letter-spacing: .2px; line-height: 1.2;
    background-image: linear-gradient(120deg, var(--c-accent-strong) 0%, var(--c-accent2) 55%, var(--c-accent3) 100%);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
}
.nav-brand .nav-brand-line1, .nav-brand .nav-brand-line2 { font-size: 1.1rem; }
.nav-drawer-close-row { display: none; justify-content: flex-end; margin-bottom: .5rem; }
.nav-collapse-toggle, .nav-drawer-close, .mobile-nav-toggle, .nav-icon-btn {
    all: unset; box-sizing: border-box; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; border-radius: 8px;
    color: var(--c-text-muted);
    transition: background-color 150ms ease-out, color 150ms ease-out;
}
.nav-collapse-toggle:hover, .nav-drawer-close:hover, .mobile-nav-toggle:hover, .nav-icon-btn:hover {
    background: rgba(var(--c-accent-rgb),.08); color: var(--c-text);
}
.nav-collapse-toggle:active, .nav-icon-btn:active {
    background: rgba(var(--c-accent-rgb),.16);
}
/* 明暗切換鈕:兩個圖示常駐 DOM,只用 CSS 切換顯示(預設淺色→顯示月亮,
   代表「點了會變深色」)。JS 只改 data-theme-icon,不動 DOM 結構。 */
#theme_toggle .ico-moon { display: inline-flex; }
#theme_toggle .ico-sun  { display: none; }
#theme_toggle[data-theme-icon="dark"] .ico-moon { display: none; }
#theme_toggle[data-theme-icon="dark"] .ico-sun  { display: inline-flex; }
.nav-collapse-toggle:focus-visible, .nav-drawer-close:focus-visible,
.mobile-nav-toggle:focus-visible, .nav-icon-btn:focus-visible {
    outline: 2px solid var(--c-accent); outline-offset: 2px;
}
.nav-collapse-toggle svg { transition: transform 200ms ease-out; }
.nav-col.collapsed .nav-collapse-toggle svg { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
    .nav-collapse-toggle svg { transition: none; }
    .nav-col { transition: none !important; }
}

/* 桌面收合成窄 icon bar:文字用 font-size:0 隱藏(不是 display:none,
   避免影響按鈕的 accessible name/可點擊區域),圖示尺寸另外鎖死不受
   font-size 影響。統計比較分析的 4 個子按鈕是純文字、沒有各自圖示,
   收合時整組 Accordion 直接隱藏(窄版 icon bar 塞不下二層選單);重置
   按鈕若在收合狀態被點擊,JS 會先自動展開 nav-col 再讓確認彈窗顯示
   (見 _APP_HEAD_SCRIPT setNavCollapsed 呼叫處),避免確認彈窗擠在
   60px 窄欄裡。這整組「收合外觀」規則只在桌面寬度生效(min-width:901px)
   ——手機沒有「窄 icon bar」這個中間態,nav-col 在手機是 off-canvas
   抽屜,要嘛整個滑出來、要嘛整個收在畫面外,即使 localStorage 殘留
   collapsed=1 也不該套用這組窄化樣式(見下方 900px 媒體查詢改用
   position:fixed 抽屜,兩者規則互斥,不需要互相 revert)。 */
.nav-col { transition: flex-basis 200ms ease-out, max-width 200ms ease-out; }
@media (min-width: 901px) {
    /* gr.Column(min_width=200) 會在元件上寫死 inline style="min-width:200px",
       這個 inline min-width 會蓋掉外部 stylesheet 的 max-width(flexbox
       規範:min-width 與 max-width 衝突時 min-width 優先),光設
       max-width:60px 收合欄寬不會有效果,撐在 200px 不動——必須額外用
       !important 覆寫 min-width 本身(stylesheet 的 !important 蓋得過
       沒加 !important 的 inline style,這點跟蓋不過 inline !important
       不一樣)。 */
    .nav-col.collapsed { flex: 0 0 60px !important; max-width: 60px !important; min-width: 60px !important; }
    /* 收合成 60px icon bar:頂端列只剩品牌字標(已無其他項目可置中,
       justify-content:center 留著也不影響),字標本身在 60px 裡塞不下、
       隱藏起來,展開後才出現。 */
    .nav-col.collapsed .nav-top-row { justify-content: center; }
    .nav-col.collapsed .nav-brand { display: none !important; }
    /* 2026-07-30(2-B 第 2 項):明暗切換/收合鈕搬到欄底 .nav-bottom-toolbar
       後,收合時的置中邏輯也跟著搬過來——明暗切換鈕在 60px 裡放不下(跟收合
       鈕擠在一起會超寬),一起隱藏,只留收合鈕自己置中,展開後才恢復兩顆
       並排靠右(見 .nav-bottom-toolbar 規則本身)。
       這裡一定要加 !important:.nav-bottom-toolbar 本身的 justify-content:
       flex-end 也掛了 !important(本檔到處都是這個坑,CSS cascade 裡
       !important 不管 specificity 高低都贏過沒加的規則),沒加會被蓋掉、
       收合時仍然靠右對齊,量出來就會跟預期的「置中」對不上。 */
    .nav-col.collapsed .nav-bottom-toolbar { justify-content: center !important; }
    .nav-col.collapsed #theme_toggle { display: none !important; }
    /* .reset-confirm 已於 2026-07-24 搬出 nav-col 改成全螢幕 modal,
       不再需要在收合狀態下隱藏;.result-nav-accordion 同日隨「統計比較
       分析」手風琴一起移除。 */
    .nav-col.collapsed .result-nav-divider { display: none !important; }
    /* 選擇器裡的 #result_nav_col 是為了贏過下方 nav-underline 樣式的
       `#result_nav_col .result-nav-btn`(1 個 id + 1 個 class = 1,1,0)。
       兩邊都掛 !important 時由 specificity 決勝,純 class 的
       `.nav-col.collapsed .result-nav-btn`(0,3,0)會輸,收合後的置中
       與 padding 就不會生效。加上 id 祖先後是 1,3,0,穩定勝出。 */
    /* 2026-07-24 回歸修正:為了統一字級加了 `#reset_btn`(1,0,0)之後,純 class
       的 `.nav-col.collapsed .reset-btn`(0,3,0)反而輸掉,收合時「開始新分析」
       的文字不再被 font-size:0 隱藏、把 60px 窄欄撐破。這裡改用 `#reset_btn`
       當選擇器(1,2,0)贏回來。呼應本檔既有註記:任何為了蓋過 Gradio 而加 id
       的改動,都要回頭盤同名 class 的其他規則有沒有被連帶壓掉。 */
    .nav-col.collapsed #reset_btn,
    .nav-col.collapsed #nav_guide_btn,
    .nav-col.collapsed #nav_about_btn,
    .nav-col.collapsed #result_nav_col .result-nav-btn {
        justify-content: center !important;
        padding: .45rem !important;   /* 2026-07-24 收窄:原本 .6rem 把圖示擠到 8px(見下方註解) */
        font-size: 0 !important;
    }
    /* 千卉回報「收合後看不見 icon」的真因(2026-07-24 量出來才發現):不是顏色
       太淡,是圖示被擠成 8px。60px 欄寬扣掉 nav 的 16px 左右內距只剩 28px,再扣
       按鈕自己的 .6rem 左右內距(19.2px),留給圖示的空間只剩 ~9px,而 img 是
       flex 子項、預設可被壓縮,就一路縮到 8px。
       修法三件:①收合時欄內距縮到 8px(展開仍維持 16px)②按鈕內距縮到 .45rem
       ③圖示 flex:none 明確不可壓縮。可用寬 = 60-16-14.4 ≈ 29.6px > 22px。
       另外 opacity 拉到 1(預設 .82 沒有文字陪襯時偏淡);disabled 的淡化來自
       按鈕本身 opacity:.38(父層),不受影響。 */
    .nav-col.collapsed { padding-left: 8px !important; padding-right: 8px !important; }
    .nav-col.collapsed .button-icon {
        width: 22px !important; height: 22px !important;
        flex: none !important; opacity: 1 !important;
    }
    .nav-col.collapsed .result-nav { padding-top: .8rem !important; }
}

/* 2026-07-24:這裡原本有一整組 #nav_stat_accordion 樣式(CSS ::before 掛
   chart-bar 圖示、label-wrap 的 justify-content 修正、深色模式 filter),
   隨「統計比較分析」Accordion 一起移除。四個子頁現在是平列的一般導覽項目,
   圖示走 gr.Button(icon=) 那條路,不需要這組特例。 */

/* 手機漢堡開關(mobile_nav_toggle)與抽屜遮罩(nav_backdrop):兩者都是
   放在 workspace_row 外層、一開始就 visible=True 的獨立元件(理由見
   create_app() 該處註解),CSS 預設整個不顯示,只在手機寬度且 nav-col
   已掛載(情境已選定)時,由 _APP_HEAD_SCRIPT 的 _syncMobileHamburger()
   切換 display,避免首頁或桌面寬度下平白多一顆浮動按鈕。 */
.mobile-nav-toggle {
    display: none;
    position: fixed; top: 14px; left: 14px; z-index: 45;
    background: var(--c-surface) !important;
    border: 1px solid var(--c-border-strong) !important;
    box-shadow: 0 4px 16px rgba(15,23,42,.18);
}
/* 2026-07-30:抽屜展開時藏起浮動漢堡鈕,見 setMobileDrawer() 註解——它跟
   nav-col 疊在同一個左上角,蓋住品牌字標。抽屜本身已有 nav_drawer_close
   可以關閉,這顆鈕開著時沒有作用。 */
.mobile-nav-toggle--hidden { display: none !important; }
.nav-backdrop {
    display: none;
    position: fixed; inset: 0; z-index: 30;
    background: rgba(2,6,23,.5);
    opacity: 0; pointer-events: none;
    transition: opacity 200ms ease-out;
}
.nav-backdrop.visible { opacity: 1; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) { .nav-backdrop { transition: none; } }

@media (max-width: 900px) {
    /* 手機寬度:nav-col 不再跟著 chat-col/canvas-col 上下堆疊(那條規則
       見「17. RESPONSIVE」,已把 nav-col 排除),改成 off-canvas 抽屜,
       預設收在畫面左側外面,漢堡鈕/背景遮罩點擊才滑入。 */
    .nav-col {
        position: fixed !important;
        top: 0; left: 0; bottom: 0;
        width: 78vw !important; max-width: 300px !important;
        flex: none !important; height: 100vh !important;
        overflow-y: auto !important;
        background: var(--c-surface) !important;
        box-shadow: 4px 0 24px rgba(15,23,42,.28) !important;
        z-index: 40;
        transform: translateX(-100%);
        transition: transform 220ms ease-out;
        padding: 1.1rem 1rem !important;
    }
    .nav-col.mobile-open { transform: translateX(0) !important; }
    /* 桌面收合是縮寬窄 icon bar 那組規則整段包在 @media(min-width:901px)
       裡(見「5e」上方),手機寬度完全不會命中,這裡不需要另外
       revert——即使 localStorage 殘留 collapsed=1,.nav-col.collapsed
       在手機寬度就是普通展開的抽屜外觀,不會被窄化。 */
    /* 手機抽屜沒有「窄 icon bar」中間態,收合鈕無意義 → 只隱藏收合鈕本身,
       品牌字標與明暗切換鈕仍留在抽屜頂端;關閉抽屜改用下方的 x(drawer-close)。 */
    #nav_collapse_toggle { display: none !important; }
    .nav-drawer-close-row { display: flex !important; }
    .mobile-nav-toggle { display: inline-flex; }
    .nav-backdrop { display: block; }
}

/* ── 6. UPLOAD AREA(醒目卡片)─────────────────────────────────── */
/* 注意:max-width + margin auto 是舊版單欄置中版面留下的樣式,現在
   upload-zone 是右側 action-col 裡的元件,要跟同一欄的 scenario-buttons
   同寬(撐滿欄寬),不能再置中收窄 */
/* 2026-07-23 千卉實機回饋:原本的強調色外框+大範圍陰影+漸層底色讓這張
   卡片像一個浮起來的彈窗。改成扁平——外框、陰影、底色全部拿掉,只保留
   圓角與內邊距維持內容的呼吸空間。彈出動畫保留,它負責的是「這塊剛剛才
   出現」的狀態提示,跟視覺重量無關。 */
/* 2026-07-31(Batch E 第 4-c 項):業主附圖標註「文本上傳」標題(這裡是
   ::before 產生的「📂 上傳已斷詞文本」偽標題)左緣比步驟列第一顆藥丸左緣
   往右縮排。實測量出來:canvas-scroll 本身已經統一給所有直屬子項 20px
   左內距(跟 .app-header 的 20px 左內距是同一個基準線,兩者天生就對齊),
   問題是這裡自己又疊了一層 1.5rem(24px)左右內距,把內容多推了 24px。
   拿掉左右內距,改吃 canvas-scroll 那層統一的 20px,跟步驟列基準線對齊
   (同時也順手把上方留白從「zone 自己的 1.4rem 頂部內距」壓掉,見下方
   padding 從 "1.4rem 1.5rem" 改成 "0 0 1.4rem" 的異動——只留底部內距
   維持内容與下一個區塊的呼吸空間,頂部改靠 margin-top 的 .6rem 加上標題
   本身既有的 margin 提供間距即可,不用內外雙重留白)。 */
.upload-zone {
    border: none !important;
    border-radius: 16px !important;
    background: none !important;
    padding: 0 0 1.4rem !important;
    margin: .6rem 0 0 !important;
    width: 100% !important;
    box-shadow: none !important;
    animation: popupIn .25s cubic-bezier(.2,.8,.3,1) !important;
}
@keyframes popupIn {
    from { opacity: 0; transform: translateY(-8px) scale(.985); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}
.upload-zone::before {
    content: "📂 上傳已斷詞文本";
    display: block;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--c-accent-strong);
    /* 2026-07-31(Batch G 第 2 項):業主回報「上傳已斷詞文本」標題上方
       留白比其他階段標題少。實測(header 底緣到標題文字頂緣的距離):
       研究選擇 h3 ≈ 32px、語料 Metadata 說明 h3 ≈ 68px、這裡(::before
       偽元素)只有 ≈ 9.6px(等於 .upload-zone 自己的 margin-top .6rem,
       偽元素本身預設沒有 margin-top,不像另外兩個標題是真的 <h3>、天生
       帶 Gradio markdown prose 的標題頂部留白 ≈22.4px)。收斂目標訂在
       32px(貼近「研究選擇」現有數值,改動最小),這裡補上 1.4rem
       (22.4px)頂部留白,湊出跟其他標題一致的視覺留白。 */
    margin-top: 1.4rem;
    margin-bottom: .9rem;
    letter-spacing: .02em;
}
/* 原本這裡有 .upload-zone:hover/:focus-within 加深 border-color 的規則,
   2026-07-23 卡片扁平化拿掉外框後就沒有東西可加深了,一併移除。 */
/* 對話輸入框(Phase 8:不再負責上傳,改回純展示用的常駐輸入框):
   刻意跟上面「醒目卡片」風格的 .upload-zone 拉開視覺重量差異,
   不用強調色框線/陰影/彈出動畫,也不掛 ::before 的標題文字,
   避免使用者誤以為這裡也是一個可操作的上傳入口 */
/* 2026-07-24(仿 Claude):輸入框貼齊欄邊——拿掉外框、圓角、內距、上方
   margin(原本 .6rem 是它跟對話視窗的間隙,也一起拿掉)。 */
.upload-zone-plain {
    border: none !important;
    border-radius: 0 !important;
    padding: 0 !important;
    margin: 0 !important;
    /* width 用 auto 不用 100%:下方 #chat_upload_zone 會給左右 margin 讓面板
       內縮,若寫死 100% 會連同 margin 一起超出欄寬(實測右側溢出 15px)。 */
    width: auto !important;
}
/* 輸入框本體:底色改成跟氣泡同一階(白/深色淺一階),去掉 Gradio 預設的
   外框與圓角,四邊貼齊 chat-col。頂端用 1px 細線跟對話視窗分界(取代原本
   靠間隙分隔)。 */
#chat_upload_zone .multimodal-textbox,
#chat_upload_zone .input-container,
#chat_upload_zone textarea {
    background: var(--c-surface) !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
}
/* 2026-07-24 千卉:輸入框右邊與下面要有留白。先前的 16px 只是「面板內部的
   內距」,白色面板本身仍貼齊欄邊(實測面板距欄 左1 右2 下1)。改成面板整個
   往內縮 16px,白底與欄邊之間真的空出來;不再需要頂端分隔線(面板浮在欄底
   色上,本身就是分界)。Gradio 的 .form 外層帶淡藍底+框線也一併中性化,
   否則會在面板外圍多一圈色帶。 */
#chat_upload_zone { margin: 0 16px 16px !important; border-top: none !important; }
#chat_upload_zone .form {
    background: transparent !important;
    border: none !important;
}
/* 2026-07-23:chat-col 改成 gap:1rem 的 flex 欄,跟 chatbot 之間的留白
   已經由 gap 統一處理,這裡不用再疊加 margin-top(舊註解提到的「技術
   執行紀錄」分組已在 Phase 8-B 搬去 canvas-col,跟這裡無關,是過期
   說明)。改成跟 .main-chat 同一套 5:1 flex-grow 比例分配高度。 */
/* 2026-07-24(#15):輸入框改「自然高度、貼齊底部」,不再用 flex:1 佔 1/7
   欄高——原本被撐成 153px 但內容只佔一小塊,底下留 61px 空縫(千卉標的
   下方縫隙)。對話視窗改 flex:1 1 auto 吃掉剩餘全部高度,輸入框 flex:0 0 auto
   縮到內容高度。 */
#chat_upload_zone {
    flex: 0 0 auto !important;
    min-height: 0 !important;
    display: flex !important;
    flex-direction: column !important;
}
/* Gradio MultimodalTextbox 的內層 padding 左右不對稱(full-container 12px、
   input-wrapper 右 4px、textarea 0)造成左 14/右 18 不一致(#15)。統一:
   full-container 對稱 12px、input-wrapper 歸零,textarea 左右緣一致。 */
/* 2026-07-24 千卉:左/下要跟右邊一樣的留白。原本 full-container 上下 10px、
   左右 12px,加上內層殘留造成 textarea 距欄 左14/右15/下17 不一致。改成
   四邊統一 14px,內層一律歸零,四邊留白就一致。 */
#chat_upload_zone .full-container { padding: 14px !important; }
#chat_upload_zone .input-wrapper,
#chat_upload_zone .input-row { padding: 0 !important; margin: 0 !important; }
#chat_upload_zone textarea { padding: 0 !important; }
/* textarea 只有 67px 但 input-row 是 72px(拿掉送出鈕後留下的槽位高度),
   下緣多 5px 空隙、跟左右留白對不齊。讓 textarea 撐滿整列高度即可。 */
#chat_upload_zone .input-row { align-items: stretch !important; }
#chat_upload_zone textarea { height: 100% !important; }
/* 格式要求直接寫在上傳卡片裡,跟卡片本身有明顯區隔但仍在同一張卡片內,
   使用者不用往上捲回對話視窗找 AI 說過的規則 */
/* 已經在 upload-zone 卡片裡了,卡片本身就有外框,這裡不再加第二層框線/底色。
   注意:Gradio 把 elem_classes 同時蓋在外層 wrapper(.block.upload-hint)
   跟內層 markdown 內容(.prose.upload-hint)兩個 div 上,凡是用裸的
   .upload-hint 當 selector,padding/margin/border 都會在兩層各套一次、
   疊加成兩倍間距或雙重邊框(已踩坑兩次:先是雙重分隔線,後是跟下面拖放框
   距離過遠)。統一寫法:只在 .block.upload-hint(外層)設定間距,
   .prose.upload-hint(內層)明確歸零,字級/顏色類不影響版面的規則才維持
   用裸 .upload-hint(重複套用無副作用) */
.upload-hint.block {
    padding: 0 0 .9rem !important;
    margin-bottom: .9rem !important;
}
.upload-hint.prose {
    padding: 0 !important;
    margin: 0 !important;
}
.upload-hint {
    font-size: var(--font-size-base) !important;
}
.upload-hint p, .upload-hint li { font-size: var(--font-size-base) !important; color: var(--c-text-soft) !important; }
.upload-hint strong { color: var(--c-accent-strong) !important; }
/* 同陷阱 13 第 3 點:<span class="md"> 預設 inline 卻裝 block 的 <p>,
   會在最前面產生依自己字級算出的隱形空隙,這裡一併修正 */
.upload-hint .md {
    display: block !important;
    font-size: var(--font-size-base) !important;
}
.upload-actions {
    margin-top: 1rem !important;
    gap: .6rem !important;
}
.upload-actions button { border-radius: 10px !important; font-weight: 700 !important; }
/* 「回首頁」連結(原「開始新分析」按鈕,2026-07-30 改名+改視覺語彙)與其
   確認對話框:換情境/回首頁共用同一套機制,elem_id="reset_btn" 與後端
   wiring 完全不變,只是文案/圖示/樣式改了。
   千卉這輪指定:樣式要跟 #result_nav_col .result-nav-btn 那組「文字連結」
   一致(無邊框、無底色、置左),不要另創一套——做法不是新增一個 class,
   是把 #reset_btn 併進下方 #result_nav_col .result-nav-btn 那幾條共用規則
   的選擇器清單(見該處),這裡只留 margin-top(它在 nav-items-scroll 裡
   排在已隱藏的 scenario-buttons 之後,用來跟上面留出一點呼吸空間,
   result-nav-btn 本身沒有這個需求,所以留在這裡單獨設,不搬過去)。 */
.reset-btn { margin-top: .8rem !important; }
/* 2026-07-24:確認框從 nav-col 內嵌卡片改成置中 modal。
   刻意不對 overlay 設 display——Gradio 的 visible=False 就是靠 display:none
   生效,這裡若寫 display:flex !important 會把「隱藏」本身蓋掉,對話框變成
   永遠開著。改用「維持 Gradio 給的 flex 排版 + 面板自己 margin:auto」達成
   水平垂直置中,完全不碰 display 這個屬性。 */
.reset-confirm-overlay {
    position: fixed !important;
    inset: 0 !important;
    z-index: 120 !important;
    padding: 1.5rem !important;
    background: rgba(15, 23, 42, .55) !important;
    backdrop-filter: blur(2px);
}
.reset-confirm-panel {
    margin: auto !important;          /* flex 容器內上下左右皆置中 */
    width: 100% !important;
    max-width: 400px !important;
    flex: none !important;            /* 不被 overlay 的 flex 拉伸成滿版高 */
    gap: 1.2rem !important;
    padding: 1.5rem !important;
    border: 1px solid var(--c-border-strong) !important;
    border-radius: 14px !important;
    background: var(--c-surface) !important;
    box-shadow: 0 20px 50px rgba(15, 23, 42, .3) !important;
}
/* 深色模式:頁面本身已經是深色(--c-surface #0d1526),同一階深灰疊上去
   幾乎看不出後面被壓暗,遮罩形同不存在。改用更黑的值才有「後面那層被關掉」
   的分離感。兩種寫法都給:.dark 是 Gradio 切主題時掛的 class,媒體查詢則
   對應本檔既有的 prefers-color-scheme token 機制(見 _DARK_MODE_HEAD_CSS)。 */
.dark .reset-confirm-overlay { background: rgba(0, 0, 0, .62) !important; }
@media (prefers-color-scheme: dark) {
    :root:not(.light) .reset-confirm-overlay { background: rgba(0, 0, 0, .62) !important; }
}
.reset-confirm-panel .reset-confirm-text p { margin: 0 !important; }
/* 橫式:兩顆按鈕同寬並排,窄視窗也不換行 */
.reset-confirm-actions {
    flex-wrap: nowrap !important;
    gap: .7rem !important;
}
.reset-confirm-actions button {
    border-radius: 10px !important;
    font-weight: 700 !important;
}
/* 2026-08-26:「關於」面板沿用 .reset-confirm-overlay 同一套置中 modal
   機制(同一個 elem_classes 掛在 overlay 上),但內容是說明文字不是
   一兩句確認詞,.reset-confirm-panel 的 400px 版寬會把整段文字擠成
   窄豆腐塊,所以另開 .about-panel 加寬;內容也比確認框長,視窗矮的時候
   (筆電、手機橫向)要能捲動,不能被 overlay 裁掉看不到下半段。文字
   對齊也從確認框的置中改左靠——長段落置中會很難讀。 */
.about-panel {
    margin: auto !important;
    width: 100% !important;
    max-width: 560px !important;
    max-height: min(80vh, 640px) !important;
    overflow-y: auto !important;
    flex: none !important;
    gap: 1rem !important;
    padding: 1.6rem 1.8rem !important;
    border: 1px solid var(--c-border-strong) !important;
    border-radius: 14px !important;
    background: var(--c-surface) !important;
    box-shadow: 0 20px 50px rgba(15, 23, 42, .3) !important;
    text-align: left !important;
}
.about-panel h2, .about-panel h3 {
    text-align: left !important;
}
.about-panel hr {
    border: none !important;
    border-top: 1px solid var(--c-border-strong) !important;
    margin: .6rem 0 !important;
}
.about-panel .about-close-row {
    display: flex !important;
    justify-content: flex-end !important;
}
.about-panel .about-close-row button {
    border-radius: 10px !important;
    font-weight: 700 !important;
}
/* Phase 8-D:結果導覽(情境選定後顯示在開始新分析按鈕下方),純文字
   標籤(暫不用圖示,等下一輪視覺打磨補 Heroicons SVG),維持跟
   scenario-buttons 一致的「貼齊左邊、無強調色底」低視覺重量風格,
   避免搶過主要操作按鈕的注意力 */
/* 左右留白已上移到 .nav-col(見該處註解),這裡不再重複設定,避免疊加成
   雙倍縮排。上緣的 border-top 因此會跟著欄位的 padding 內縮 8px,跟下方
   .result-nav-divider 本來就有的內縮一致。 */
.result-nav {
    margin-top: 1rem !important;
    padding-top: 1rem !important;
    border-top: 1px solid var(--c-border-strong) !important;
    gap: .3rem !important;
}
/* 2026-07-23:導覽項目改用 Bootstrap 5.3 `.nav-underline` 的視覺模型
   (getbootstrap.com/docs/5.3/components/navs-tabs/)——未選取的項目是
   純文字,沒有邊框也沒有底色;選取的項目只換文字顏色再加一條粗線。官方
   的線畫在下緣(橫向分頁用),這裡是垂直導覽,依慣例改畫在左緣。
   千卉回饋原本「看起來像很多按鈕」的真正原因不是這裡的樣式寫錯,而是
   下面這個選擇器少了 id 祖先:Gradio 自己的 secondary 按鈕樣式(Svelte
   編譯出 button.svelte-xxx.secondary 這種 type+class 組合)specificity
   比純 class 高,即使掛 !important 也贏不了,所以 border:none 一直沒生效、
   Gradio 預設的框線與底色整個透出來。子項目當初已經用 #result_nav_col
   修過(見下方註解),主項目漏掉了,這次補上。
   2026-07-30:「回首頁」(#reset_btn)併入這組選擇器清單,跟 nav_top_btn
   (AI對話)等結果導覽項目共用一模一樣的視覺語彙。#reset_btn 不在
   #result_nav_col 底下,直接用 id 自己的 specificity(1,0,0)就夠贏過
   Gradio 的 button.svelte-xxx.secondary,不需要額外的祖先。 */
#result_nav_col .result-nav-btn,
#reset_btn,
#nav_guide_btn,
#nav_about_btn {
    width: 100% !important;
    justify-content: flex-start !important;
    text-align: left !important;
    border: none !important;
    background: transparent !important;
    box-shadow: none !important;
}
/* 2026-07-24(5-B):改成 ChatGPT 側欄那種「填色」互動模型——未選純文字、
   hover 淡背景、按下更深、選取淡主色背景+主色文字。圓角讓填色區塊內縮、
   不貼齊欄邊。拿掉原本 Bootstrap nav-underline 的左緣線(千卉指定改填色)。 */
#result_nav_col .result-nav-btn,
#reset_btn,
#nav_guide_btn,
#nav_about_btn {
    padding: .5rem .7rem !important;
    font-size: var(--font-size-base) !important;
    font-weight: 500 !important;
    border-radius: 8px !important;
    color: var(--c-text-soft) !important;
    transition: background-color .15s ease-out, color .15s ease-out !important;
}
#result_nav_col .result-nav-btn:hover:not(:disabled),
#reset_btn:hover:not(:disabled),
#nav_guide_btn:hover:not(:disabled),
#nav_about_btn:hover:not(:disabled) {
    background: rgba(var(--c-accent-rgb),.08) !important;
    color: var(--c-text) !important;
}
#result_nav_col .result-nav-btn:active:not(:disabled),
#reset_btn:active:not(:disabled),
#nav_guide_btn:active:not(:disabled),
#nav_about_btn:active:not(:disabled) {
    background: rgba(var(--c-accent-rgb),.16) !important;
}
/* 歷史紀錄:這裡原本有一整組 .result-nav-subbtn 樣式,把統計比較分析底下
   的四頁做成「小一號字+灰一階+真縮排」的子選單(縮排寫法是 2026-07-23
   從 Bootstrap 官方 Sidebars 範例的 .btn-toggle-nav a 移植過來的,用
   margin-left 真的把元素往右推、寬度跟著縮,而不是只用 padding-left 讓
   文字位移)。同日千卉決定四頁改用跟其他導覽項目一模一樣的樣式並各自配
   圖示、左緣齊平,這組樣式整個不再需要,連同 class 一起移除。
   Gradio secondary 按鈕邊框那個 specificity 坑不會因此消失——它現在由
   上方 `#result_nav_col .result-nav-btn` 那組共用規則一併處理。 */
/* 2026-07-24:這裡原本有 .result-nav-accordion 與其 .label-wrap 的樣式
   (拿掉 Gradio .padded 的水平 12px、標題列對齊其他導覽項目),隨
   「統計比較分析」Accordion 一起移除。 */
/* 2026-07-23:頁面切換模型——目前使用者所在的那一頁在導覽列高亮,
   applyActivePage() JS 幫對應按鈕加/移除這個 class。視覺先求「看得出
   選中誰」的基本可用,細部樣式(跟 Bootstrap 借來的分組/層級規範)留給
   下一輪 nav-col 視覺重做一起處理,這裡不重複做兩次。 */
#result_nav_col .result-nav-btn.active {
    background: rgba(var(--c-accent-rgb),.14) !important;
    color: var(--c-accent-strong) !important;
    font-weight: 700 !important;
}
/* 技術執行紀錄:低優先度診斷工具,跟上面的分析結果用分隔線+降低視覺
   權重(字級與顏色都比其他導覽項目再低一階)區隔開來 */
.result-nav-divider {
    height: 1px;
    background: var(--c-border-strong);
    margin: .5rem .3rem;
}
/* 同樣要帶 #result_nav_col,否則純 class(0,1,0)會被上方
   `#result_nav_col .result-nav-btn`(1,1,0)的字級/顏色蓋掉,
   技術執行紀錄就不再是降權的樣子 */
/* 2026-07-24 千卉:導覽列所有項目字級一致(以「AI對話」的 .88rem 為基準)。
   技術執行紀錄原本 .82rem 比其他項目小一階,改回 .88rem;它的「低優先度」
   仍靠顏色(--c-text-muted)與上方分隔線表達,不再靠縮小字級。 */
/* 2026-08-12:「使用說明」加在 nav-items-scroll 最後一項之後,發現它被推到
   整個捲動區的最底部——Gradio 給 gr.Column 的預設是 flex:1 1 auto,
   result_nav_col 因此把剩餘高度全部吃掉(實測長到 729px,內容其實只有
   400 出頭)。業主要的是「最後一個項目下方」,不是「釘在欄位底部」,
   所以讓它只佔內容高度,使用說明就會緊跟在技術執行紀錄下面。 */
#result_nav_col {
    flex: 0 0 auto !important;
}
#result_nav_col .result-nav-btn--muted,
#nav_guide_btn.result-nav-btn--muted,
#nav_about_btn.result-nav-btn--muted {
    font-size: var(--font-size-base) !important;
    font-weight: 500 !important;
    color: var(--c-text-muted) !important;
}
/* 語料 Metadata 說明表單卡片:跟 .upload-zone 同批扁平化(2026-07-23),
   兩者是同一種「Canvas 裡的操作區塊」,樣式要保持一致 */
/* 2026-07-31(Batch E 第 4-c 項):同 .upload-zone 那則註解,左右內距
   歸零改吃 canvas-scroll 統一的 20px(原本 1.6rem 額外內距造成標題左緣
   跟步驟列對不齊),頂部內距也歸零壓掉多餘留白,只留底部內距。 */
/* 2026-07-31(Batch F):Batch E 把左內距整個歸零,副作用是標題文字直接
   貼死在 canvas-scroll 的 20px 基準線上(業主實測回報「標題字貼到左邊
   邊框了」)。這裡補回左內距 1.5rem(沿用 .analysis-panel 內距同一套
   數值,不另創),只補左邊、不動右邊/頂部/底部,維持 Batch E 顧到的
   「zone 本身的框(雖然這塊沒有可視邊框,但量測用的 bounding box)左緣
   仍落在 canvas-scroll 20px 基準線上」,只是內容(標題與底下的表單/
   卡片)整體再往內縮 1.5rem,跟步驟列基準線的關係從「內容緊貼」變回
   「內容有呼吸空間」。 */
/* 2026-07-31(Batch G 第 2 項):業主回報「語料 Metadata 說明」標題上方
   留白比其他階段標題多。實測發現根因不在 .metadata-zone 自己的
   margin-top(跟 .upload-zone/.scenario-select-zone 用同一份 .6rem),
   而是它上面那個兄弟節點 upload_progress_zone(3-C 加的上傳進度卡片,
   elem_id="upload_progress_zone"):格式驗證通過、換頁到語料說明時,
   它的內容被清空成空字串(見 on_upload_confirm 最後一個 yield),但
   Gradio 幫 gr.HTML() 包的 .html-container 就算內容是空字串,仍保留
   預設的 10px 12px 內距,等於平白多墊了 20px 高度的空白卡片在語料
   說明標題正上方,這是量出來的留白比研究選擇/文本上傳多出一大截
   的真正原因(不是 .metadata-zone 自己的留白設定有問題)。
   修法:不改動 upload_progress_zone 那個共用元件的空狀態渲染(那是
   跟「分析運算」進度卡共用的機制,改了會牽動另一個地方,超出這批
   範圍),改用負 margin-top 直接讓 .metadata-zone 往上收,抵銷掉這段
   多出來的空白卡片高度,讓最終畫面上的留白收斂到跟其他三個標題一致
   的 32px(研究選擇的現有數值,改動最小)。這個負值只在「upload_
   progress_zone 已清空、metadata-zone 剛顯示」這個時間點生效,兩者
   在畫面上都沒有可見邊框/背景,往上收不會露出接縫或裁切。 */
.metadata-zone {
    border: none !important;
    border-radius: 16px !important;
    background: none !important;
    padding: 0 0 1.4rem 1.5rem !important;
    margin: -1.65rem 0 1rem !important;
    box-shadow: none !important;
    animation: popupIn .25s cubic-bezier(.2,.8,.3,1) !important;
}
.metadata-zone .gr-box, .metadata-zone .form { background: transparent !important; }
/* 3-A:研究選擇區塊(工作區第 1 步),跟 .upload-zone/.metadata-zone
   同一種「Canvas 裡的操作區塊」,樣式沿用同一批扁平化卡片語彙,不另創
   一套視覺風格。 */
/* 2026-07-31(Batch E 第 4-c 項):同 .upload-zone,左右內距歸零、頂部
   內距歸零,讓「研究選擇」標題左緣跟步驟列第一顆藥丸左緣對齊、上方
   留白縮小。 */
/* 2026-07-31(Batch F):同 .metadata-zone 那則 Batch F 註解,左內距
   歸零的副作用是標題貼死在步驟列基準線上,補回 1.5rem 左內距(跟
   .analysis-panel 同一套數值)。zone 本身的 bounding box 左緣不受影響
   (padding 加在框內,不是 margin),仍對齊 canvas-scroll 20px 基準線,
   只有內容(標題與底下的情境卡片)整體往內縮。 */
.scenario-select-zone {
    border: none !important;
    border-radius: 16px !important;
    background: none !important;
    padding: 0 0 1.4rem 1.5rem !important;
    margin: .6rem 0 0 !important;
    width: 100% !important;
    box-shadow: none !important;
    animation: popupIn .25s cubic-bezier(.2,.8,.3,1) !important;
}
/* 2026-07-24 千卉:①「原文標題/作者/語言…」這些欄位標籤在深色模式吃到
   Gradio 自己的樣式(實測 #475569,對比只有 2.40:1,遠低於 AA 4.5),跟旁邊
   表格表頭「群組標籤」(--c-text-muted #94a3b8、6.92:1)落差很大。統一成
   --c-text-muted。②兩張表格沒撐滿容器(容器內寬 1069、表格只有 1051,右側
   空 18px),Gradio dataframe 的 table 寬度是依內容算的,補 width:100%。 */
#metadata_zone .container > span,
#metadata_zone label > span {
    color: var(--c-text-muted) !important;
}
#metadata_zone table { width: 100% !important; }
/* 2026-08-12(業主截圖標了五個 ✗):語料說明表單的欄位被 Gradio 自動包成
   白色卡片,跟下面「群組層資訊」那種「小標題+內容直接攤開」的樣子不一致。
   實測拆出來是兩層各做一半:
     .form(svelte-d5xbca)     底透明但有 1px 外框 → 卡片的框
     .block.padded(含 fieldset)白底 + 10px 12px 內距 → 卡片的白色填充
   兩層都要處理才會真的消失,只清其中一層會留下框或留下白塊。用 `>` 只打
   「.form 直屬的那層」,避免波及群組層/個體層那兩個 Dataframe(它們是
   metadata_zone 的直屬子項,不在 .form 裡面,外框要留著)。
   內距歸零之後,欄位左緣自然跟「原文資訊」小標題與下方表格對齊,這就是
   業主要的「靠左對齊」;欄位本身的邊框(--c-input-border)保留,截圖上
   打叉的是外面那層白卡片,不是輸入框自己。 */
#metadata_zone .form {
    border: none !important;
    background: transparent !important;
    box-shadow: none !important;
    overflow: visible !important;
    gap: 1rem !important;
    /* align-items 從預設的 stretch 改 flex-start,是「群組數量/比較方式」
       兩組 radio 被裁掉一半的真正解法(那是既有 bug,業主截圖裡就已經被裁,
       不是這批改動造成的)。原因:.form 是 flex 列,stretch 會把每個欄位的
       高度統一拉成整列的高度,而那個列高只算到 radio 的選項區(61px),
       沒有把上面的欄位標籤(20px+8px 下距)算進去,多出來的 28px 就被
       fieldset 自帶的 overflow:auto 裁掉。改成 flex-start 之後每個欄位各自
       用自己的內容高度,裁切消失。
       排除過但無效的方向(留記錄免得下次重試):改 overflow、加
       flex-shrink:0、min-height:fit-content/max-content、把 fieldset 改成
       flex column——前三個不影響那個被算錯的列高,最後一個會讓標籤跟選項
       變成左右並排。 */
    align-items: flex-start !important;
}
#metadata_zone .form > .block,
#metadata_zone .form > fieldset.block {
    background: transparent !important;
    border: none !important;
    box-shadow: none !important;
    padding: 0 !important;
    /* Gradio 給 fieldset 的 overflow:auto 是裁掉標籤的那把刀(真因見上面
       .form 的 align-items 註解),改 visible 當第二道保險。 */
    overflow: visible !important;
}
/* 2026-08-12(業主):「群組數量/比較方式」這兩個欄位標籤要跟「原文標題/
   作者」一樣是小灰字粗體。Gradio 給文字欄位標籤的是 .7rem/700/灰,但
   radio 的 fieldset 標籤走另一條規則,渲染成一般大小的深色字,兩種欄位
   放在同一張表單裡就不同調。這裡的 .7rem 不是新發明的字級,是對齊 Gradio
   既有的欄位標籤值(實測文字欄位標籤就是 11.2px=.7rem/700/
   --c-text-muted),所以不套 --font-size-base(那是內文用的,標籤比內文小
   一階是這個表單本來的層級設計)。 */
#metadata_zone fieldset.block > span {
    font-size: .7rem !important;
    font-weight: 700 !important;
    color: var(--c-text-muted) !important;
    margin-bottom: 8px !important;
}
/* 「比較結構」從 gr.Group 改成 gr.Column 之後(見 app_agent.py 同批註解),
   Gradio 那條藍色標題橫幅與外框就不存在了,這裡只補回跟其他小標題一致的
   上下節奏——它的 Markdown 用的是同一個 `**粗體**` 寫法,顏色/字級自動與
   「原文資訊」「群組層資訊」相同,不用另外設。 */
#meta_compare_group {
    background: transparent !important;
    border: none !important;
    padding: 0 !important;
    gap: 0 !important;
}
/* Phase 8-C:結果內容區塊(語言指標分析/統計比較分析/LLM輔助分析),
   從原本的 gr.Tab()(自帶 .tabitem padding)改成 gr.Column,這裡補上
   跟上面 upload-zone/metadata-zone 一致的垂直間距,避免緊貼在一起 */
/* 2026-07-24(左留白統一):水平內距集中在這裡(1.25rem),所有結果頁
   (語言指標分析/統計四子頁/LLM輔助分析/技術執行紀錄)左右緣一致。
   統計頁的 .tabitem 水平 padding 因此歸零,避免 canvas-result + tabitem
   疊加成雙重縮排(先前「語言指標分析」頁沒有 tabitem、內容貼左緣,跟其他頁
   對不齊,就是這個差異)。 */
/* 水平內距已上移到 .canvas-scroll 統一處理(見該處註解),這裡只留上方間距,
   否則會跟 canvas-scroll 疊成兩倍縮排。 */
.canvas-result { margin-top: 1.2rem !important; padding-left: 0 !important; padding-right: 0 !important; }
/* 2026-07-24:「分析運算」內容區塊——運算中顯示三個子階段進度,運算後變成
   分段摘要(標題+狀態+一句結論+查看入口)。取代舊的單行 .computing-zone
   「運算中…(步驟 N/3)」提示(元件與 elem_id 沿用 computing_zone)。
   這塊卡片同時也是 _build_upload_html「文本上傳」進度卡片共用的同一組
   CSS 語彙(見 .analysis-panel-title 下方 2026-07-31 註解),不是各自
   重複刻一份。 */
/* 2026-07-31(Batch E 第 4-c 項):這是唯一一個真正有外框/底色的「卡片」
   (其他內容區塊如 upload-zone/metadata-zone 都已扁平化、沒有可視邊框),
   computing_html/upload_progress_html 這兩個 gr.HTML() 元件預設會被
   Gradio 包一層 .html-container(10px 12px 內距,見 .html-container:
   has(.app-header) 那則註解),這裡比照同一招數精準鎖定「包住
   .analysis-panel 的那一個」歸零,讓卡片本身的左緣落在 canvas-scroll
   統一的 20px 基準線上,卡片左緣才能跟其他內容區塊(研究選擇/文本上傳/
   語料說明/結果頁)的左緣落在同一條線上。 */
.html-container:has(.analysis-panel) {
    padding: 0 !important;
}
/* 2026-07-31(Batch G 第 2 項):四個階段標題的上方留白統一收斂到 32px
   (量測基準:header 步驟列底緣到標題文字頂緣的距離,貼近「研究選擇」
   現有數值,改動最小)。這張卡片同時是「文本上傳」進度卡與「分析運算」
   內容區塊共用的樣式,原本 margin-top 1.2rem + padding-top 1.2rem
   (卡片外緣到標題文字共 ≈39px)比目標多,改窄 margin-top 到 .75rem,
   卡片外緣往上收,標題(.analysis-panel-title 是 margin:0 的 h2,
   不吃額外標題留白)跟著一起收斂到 32px 左右,不動 padding(左右/底部
   內距與卡片內 analysis-row 的呼吸空間維持不變,只調外距)。 */
.analysis-panel {
    border: 1px solid var(--c-border-strong) !important;
    border-radius: 14px !important;
    background: var(--c-surface2) !important;
    padding: 1.2rem 1.5rem 1.4rem !important;
    margin-top: .75rem !important;
}
/* 2026-07-31(Batch E 第 4-c 項,已被下方 Batch F 註解取代):卡片本身
   保留左右內距(1.5rem)維持內容呼吸空間,裡面的狀態列(analysis-row)
   都還需要跟卡片邊框有安全距離,不能整塊歸零(那樣會變成上面
   .upload-zone 那種完全扁平卡片,跟這裡刻意保留的「有外框的卡片」設計
   衝突)。當時改成只把標題本身用負 margin-left(等於卡片左內距的
   1.5rem)拉出去,讓標題文字獨自貼齊卡片左側邊框,卡片邊框本身又落在
   canvas-scroll 的 20px 基準線上(見上方 .html-container:has
   (.analysis-panel) 那則),兩者相加後標題文字左緣正好等於步驟列第一顆
   藥丸的左緣。 */
/* 2026-07-31(Batch F):業主實測回報「文本上傳」進度卡片(用的正是這個
   .analysis-panel-title)標題貼死在卡片邊框上,就是上面這個負
   margin-left 造成的,它把標題硬拉到跟卡片邊框零距離,等於故意抵銷掉
   .analysis-panel 自己的 1.5rem 左內距。業主裁示的修法是「卡片外框對齊
   步驟列、標題往內縮」,不是「標題貼齊步驟列、卡片跟著位移」,所以拿掉
   這個負 margin,讓標題乖乖吃卡片本身既有的 1.5rem 左內距即可,跟卡片
   內的 analysis-row 狀態列用的是同一份內距,兩者左緣自然對齊、不再各吃
   各的規則。 */
.analysis-panel-title {
    margin: 0 0 1rem 0 !important;
    font-size: 1.05rem !important;
    font-weight: 700 !important;
    color: var(--c-text) !important;
}
/* 2026-08-05:分析運算進行中的連線提醒,低視覺重量(跟標題/狀態列比起來
   是次要資訊),用 warn 色系底+左邊框做出「提示」語彙,不搶過主要內容。 */
.analysis-panel-hint {
    margin: 0 0 1rem 0 !important;
    padding: .6rem .8rem !important;
    border-left: 3px solid var(--c-warn) !important;
    border-radius: 4px !important;
    background: rgba(180,83,9,.08) !important;
    font-size: .82rem !important;
    line-height: 1.5 !important;
    color: var(--c-text-muted) !important;
}
.dark .analysis-panel-hint { background: rgba(251,191,36,.10) !important; }
.analysis-row {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    padding: .7rem 0;
    border-top: 1px solid var(--c-border-strong);
}
.analysis-row:first-of-type { border-top: none; padding-top: 0; }
.analysis-row-head { display: flex; align-items: center; gap: .55rem; }
.analysis-row-title {
    font-size: var(--font-size-base) !important;
    font-weight: 600 !important;
    color: var(--c-text) !important;
}
.analysis-row-body {
    padding-left: 1.65rem;   /* 對齊圖示右側,讓結論文字跟標題文字左緣一致 */
    font-size: var(--font-size-base);
    color: var(--c-text-soft);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem .9rem;
}
.analysis-row--pending .analysis-row-title,
.analysis-row--pending .analysis-row-body { color: var(--c-text-muted) !important; }
.analysis-entry {
    color: var(--c-accent-strong) !important;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
}
.analysis-entry:hover { text-decoration: underline; }
/* 狀態記號:完成 ✓、失敗 !、等待中 •,尺寸統一,跟旋轉動畫的 spinner 佔位一致 */
.analysis-mark {
    flex: none;
    width: 20px; height: 20px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%;
    font-size: .72rem; font-weight: 700; line-height: 1;
}
.analysis-mark--done   { background: rgba(34,197,94,.15);  color: #16a34a; }
.analysis-mark--failed { background: rgba(220,38,38,.15);  color: #dc2626; }
.analysis-mark--pending{ background: var(--c-surface); color: var(--c-text-muted);
                         border: 1px solid var(--c-border); }
/* 轉圈動畫:踩坑(2026-07-24)——border 這個「簡寫」在這版 Gradio 的
   pipeline 下整條不會生效(跟本檔 background 簡寫的踩坑同一類),實測
   border-width 算出來是 0px、圓圈整個沒被畫出來,只有同規則裡的
   border-top-color 長寫法有作用。改成全部拆成長寫法子屬性就正常。 */
.analysis-spinner {
    flex: none;
    width: 20px; height: 20px;
    border-radius: 50%;
    border-width: 2.5px !important;
    border-style: solid !important;
    border-color: rgba(var(--c-accent-rgb),.25) !important;
    border-top-color: var(--c-accent) !important;
    animation: computingSpin .8s linear infinite;
}
@keyframes computingSpin { to { transform: rotate(360deg); } }
/* 2026-07-24(B2):Gradio 對「正在串流輸出」的元件(block wrapper)自動加
   .generating class,帶一圈會呼吸的藍色邊框動畫。頂部步驟列與分析運算內容
   區塊都是同一個 generator 的輸出,各被套一圈,畫面上兩個藍框一起閃、很礙眼。
   這個 app 不需要這個效果,整站關掉。 */
.generating {
    border-color: transparent !important;
    animation: none !important;
    box-shadow: none !important;
}
/* 2026-07-24(A2):結果導覽項目在對應結果產出前是 disabled(見 create_app
   的 interactive=False 與 _populate_tabs 的閘門)。給明確的灰階+禁用游標,
   讓「還不能點」一眼看得出來,不是壞掉。 */
#result_nav_col .result-nav-btn:disabled,
#result_nav_col .result-nav-btn[disabled] {
    opacity: .38 !important;
    cursor: not-allowed !important;
}
.upload-area, [data-testid="droparea"] {
    border: 2px dashed rgba(var(--c-accent-rgb),.28) !important;
    border-radius: 12px !important;
    background: rgba(var(--c-accent-rgb),.03) !important;
    transition: border-color .2s ease, background .2s ease !important;
}
.upload-area:hover, [data-testid="droparea"]:hover {
    border-color: rgba(var(--c-accent-rgb),.5) !important;
    background: rgba(var(--c-accent-rgb),.08) !important;
}
.file-preview {
    border: 1px solid var(--c-border-strong) !important;
    border-radius: 10px !important;
    background: var(--c-surface) !important;
}
/* 修正:此版 Gradio 的 File(list) 元件在 flex 版面下,外層 holder 會因
   min-width:0 而整個縮成 0~4px 寬,導致內容(含下載連結)視覺上溢出到
   容器外,要橫向捲動才找得到下載按鈕。強制給滿版寬度,並固定「下載」欄
   在最前面、檔名欄自動換行,避免長檔名把下載連結擠出視野。 */
.file-preview-holder {
    width: 100% !important;
    flex: 1 1 auto !important;
    min-width: 0 !important;
}
.file-preview {
    table-layout: fixed !important;
    width: 100% !important;
}
.file-preview tr > :first-child {
    width: auto !important;
    white-space: normal !important;
    word-break: break-all !important;
}
.file-preview tr > :nth-child(2) {
    width: 92px !important;
    white-space: nowrap !important;
}
.file-preview tr > :last-child {
    width: 34px !important;
}

/* ── 6. BUTTONS ────────────────────────────────────────────────── */
button.primary, button[data-testid="submit"] {
    background: linear-gradient(135deg, #0284c7 0%, #4f46e5 100%) !important;
    border: none !important;
    border-radius: 9px !important;
    font-weight: 700 !important;
    font-size: var(--font-size-base) !important;
    color: #fff !important;
    box-shadow: 0 2px 12px rgba(2,132,199,.28) !important;
    transition: transform .18s ease, box-shadow .18s ease, opacity .18s !important;
    cursor: pointer !important;
    letter-spacing: .02em;
}
button.primary:hover { transform: translateY(-2px) !important; box-shadow: 0 6px 22px rgba(2,132,199,.40) !important; }
button.primary:active { transform: translateY(0) !important; opacity: .92 !important; }
button.primary:disabled { opacity: .5 !important; cursor: not-allowed !important; transform: none !important; }
button.secondary {
    background: rgba(var(--c-accent-rgb),.08) !important;
    border: 1px solid rgba(var(--c-accent-rgb),.30) !important;
    color: var(--c-accent-strong) !important;
    border-radius: 9px !important;
    font-weight: 600 !important;
    font-size: var(--font-size-base) !important;
    cursor: pointer !important;
    transition: background .18s ease, border-color .18s ease !important;
}
button.secondary:hover {
    background: rgba(var(--c-accent-rgb),.16) !important;
    border-color: rgba(var(--c-accent-rgb),.45) !important;
}

/* ── 7. INPUTS ─────────────────────────────────────────────────── */
textarea, input[type="text"] {
    background: var(--c-input-bg) !important;
    border: 1px solid var(--c-input-border) !important;
    border-radius: 9px !important;
    color: var(--c-text) !important;
    font-size: var(--font-size-base) !important;
    line-height: 1.6 !important;
    transition: border-color .18s ease, box-shadow .18s ease !important;
}
textarea:focus, input[type="text"]:focus {
    border-color: var(--c-accent) !important;
    box-shadow: 0 0 0 3px rgba(var(--c-accent-rgb),.16) !important;
    outline: none !important;
}

/* ── 8. DATAFRAME ──────────────────────────────────────────────── */
.table-wrap {
    border: 1px solid var(--c-border-strong) !important;
    border-radius: 12px !important;
    overflow: hidden !important;
}
/* 2026-08-12(業主回報語料 metadata 表單「文字跟表頭背景黏在一起」):
   表頭底色原本借用 --c-surface2,淺色是 #f1f5f9 疊在白色資料列上只有
   1.04:1,表頭等於沒有存在感;改用獨立 token --c-thead-bg(淺色 #e2e8f0,
   對白 1.19:1,表頭文字 --c-text-muted 在其上仍有 6.15:1 過 AA),深色沿用
   原本的 #111827 不變。表頭與資料列真正的分界靠下面那條 border-bottom,
   從 rgba(accent,.16)(約 1.2:1)換成結構線 token。 */
table thead tr { background: var(--c-thead-bg) !important; }
table thead th {
    background: transparent !important;
    color: var(--c-text-muted) !important;
    font-weight: 700 !important;
    font-size: 0.75rem !important;
    text-transform: uppercase !important;
    letter-spacing: 0.05em !important;
    border-bottom: 1px solid var(--c-border-strong) !important;
    padding: 10px 12px !important;
    white-space: nowrap;
}
table tbody td {
    background: var(--c-surface) !important;
    color: var(--c-text-soft) !important;
    font-size: var(--font-size-base) !important;
    /* 資料列之間的細線用 --c-divider(Tier 2):這種密集重複的線如果也拉到
       3:1,整張表會變成網格紙,反而更難讀。結構性的外框與表頭分界才走 Tier 1。 */
    border-color: var(--c-divider) !important;
    font-family: 'JetBrains Mono', monospace !important;
    padding: 7px 12px !important;
}
table tbody tr:nth-child(even) td { background: rgba(var(--c-accent-rgb),.025) !important; }
table tbody tr:hover td { background: rgba(var(--c-accent-rgb),.08) !important; }

/* ── 9. IMAGE FRAMES ───────────────────────────────────────────── */
.image-container, .image-frame {
    border: 1px solid var(--c-border-strong) !important;
    border-radius: 12px !important;
    overflow: hidden !important;
    background: var(--c-surface) !important;
}
.image-container img { display: block; width: 100%; height: auto; }

/* ── 10. RESULT SECTION CARDS ──────────────────────────────────── */
.result-card {
    background: var(--c-surface);
    border: 1px solid var(--c-border-strong);
    border-radius: 14px;
    padding: 1.25rem 1.5rem;
    margin-bottom: 1.25rem;
}
.result-card-title {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--c-text-subtle);
    margin: 0 0 .75rem;
    padding-bottom: .5rem;
    border-bottom: 1px solid var(--c-border-strong);
    display: flex; align-items: center; gap: 6px;
}
.result-card-title .dot {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--c-accent);
    display: inline-block;
}
.stat-pill {
    display: inline-flex; align-items: center; gap: 5px;
    background: rgba(var(--c-accent-rgb),.08);
    border: 1px solid rgba(var(--c-accent-rgb),.20);
    border-radius: 6px;
    padding: 3px 10px;
    font-size: 0.78rem;
    color: var(--c-accent-strong);
    font-weight: 600;
    margin: 2px;
}

/* ── 11. MARKDOWN (result tabs) ────────────────────────────────── */
.prose h3, .md h3, .svelte-1ipelgc h3 {
    font-family: 'Crimson Pro', Georgia, serif !important;
    font-size: 1.25rem !important;
    font-weight: 600 !important;
    color: var(--c-text) !important;
    border-bottom: 1px solid var(--c-border-strong);
    padding-bottom: 0.45rem;
    margin: 1.4rem 0 .8rem !important;
}
.prose h4, .md h4 {
    font-size: 0.85rem !important;
    font-weight: 700 !important;
    color: var(--c-text-muted) !important;
    text-transform: uppercase;
    letter-spacing: .06em;
    margin: 1rem 0 .4rem !important;
}
.prose strong, .md strong { color: var(--c-accent-strong) !important; }
.prose em, .md em { color: var(--c-accent2-strong) !important; }
.prose code, .md code {
    background: var(--c-code-bg) !important;
    color: var(--c-code-text) !important;
    padding: 2px 6px;
    border-radius: 4px;
    font-family: 'JetBrains Mono', monospace !important;
    font-size: 0.82em;
}
.prose ul li, .md ul li { color: var(--c-text-soft); line-height: 1.75; }
.prose ol li, .md ol li { color: var(--c-text-soft); line-height: 1.75; }
.prose p { line-height: 1.75; color: var(--c-text-soft); }

/* h1/h2/h5/h6/表格/hr/blockquote 補齊——原本只有 h3/h4 有樣式,LLM 報告等
   自由格式 Markdown 若真的輸出到這幾個標題等級會吃到瀏覽器預設樣式。
   刻意只 scope 在 #canvas_report(LLM 輔助分析頁),不像 h3/h4 沿用全站
   .prose/.md 泛用寫法:踩過一次坑才發現 gr.HTML() 元件外層也會被 Gradio
   包一層 .prose class(不是只有 gr.Markdown() 才有),首頁標題就是用
   gr.HTML() 寫死的 <h1>,全站套用 h1 樣式會蓋掉首頁標題自己的漸層字設計
   (color: transparent 被這裡的 color:...!important 蓋掉)。 */
#canvas_report .prose h1, #canvas_report .md h1 {
    font-family: 'Crimson Pro', Georgia, serif !important;
    font-size: 1.6rem !important;
    font-weight: 700 !important;
    color: var(--c-text) !important;
    border-bottom: 1px solid var(--c-border-strong);
    padding-bottom: 0.5rem;
    margin: 1.6rem 0 .9rem !important;
}
#canvas_report .prose h2, #canvas_report .md h2 {
    font-family: 'Crimson Pro', Georgia, serif !important;
    font-size: 1.4rem !important;
    font-weight: 600 !important;
    color: var(--c-text) !important;
    border-bottom: 1px solid var(--c-border-strong);
    padding-bottom: 0.45rem;
    margin: 1.5rem 0 .8rem !important;
}
#canvas_report .prose h5, #canvas_report .md h5 {
    font-family: 'Crimson Pro', Georgia, serif !important;
    font-size: 0.95rem !important;
    font-weight: 600 !important;
    color: var(--c-text-muted) !important;
    margin: 1rem 0 .4rem !important;
}
#canvas_report .prose h6, #canvas_report .md h6 {
    font-size: 0.78rem !important;
    font-weight: 700 !important;
    color: var(--c-text-subtle) !important;
    text-transform: uppercase;
    letter-spacing: .06em;
    margin: .8rem 0 .3rem !important;
}

/* 表格——原本完全沒樣式,LLM 報告的表格吃瀏覽器預設白色框線,深色主題下很刺眼 */
#canvas_report .prose table, #canvas_report .md table {
    width: 100%; border-collapse: collapse; margin: 1rem 0;
    font-size: 0.82rem;
}
#canvas_report .prose th, #canvas_report .md th,
#canvas_report .prose td, #canvas_report .md td {
    border: 1px solid var(--c-divider);
    padding: .5rem .75rem; text-align: left;
    color: var(--c-text-soft);
}
#canvas_report .prose th, #canvas_report .md th {
    background: rgba(var(--c-accent-rgb),.08);
    color: var(--c-text); font-weight: 700;
}
#canvas_report .prose tr:nth-child(even) td, #canvas_report .md tr:nth-child(even) td { background: var(--c-surface2); }

/* 水平線——原本完全沒樣式,吃瀏覽器預設 */
#canvas_report .prose hr, #canvas_report .md hr { border: none; border-top: 1px solid var(--c-border-strong); margin: 1.5rem 0; }

/* 引言區塊(LLM 報告常用 > 標出結論/總結) */
#canvas_report .prose blockquote, #canvas_report .md blockquote {
    border-left: 3px solid var(--c-accent);
    padding: .6rem 1rem; margin: 1rem 0;
    background: rgba(var(--c-accent-rgb),.05);
    color: var(--c-text-soft); font-style: normal;
}
#canvas_report .prose blockquote p, #canvas_report .md blockquote p { margin: 0; }

/* LLM 報告頁專用:模型常把「章節標題」寫成整行粗體而非真正的 ##/### 語法,
   CSS 選不出「這行粗體是標題」還是「句中強調」,只能用「整行只有一顆粗體」
   這個結構特徵做近似辨識,並非真正標題樣式的替代品 */
#canvas_report .md p:has(> strong:only-child),
#canvas_report .prose p:has(> strong:only-child) {
    font-family: 'Crimson Pro', Georgia, serif;
    font-size: 1.15rem; font-weight: 600;
    color: var(--c-text);
    margin: 1.4rem 0 .6rem;
}
/* 報告開頭「研究情境/時間」metadata 兩行,視覺上當副標題處理 */
#canvas_report .md > p:first-of-type,
#canvas_report .prose > p:first-of-type {
    color: var(--c-text-muted); font-size: 0.85rem; margin-bottom: 1rem;
}

/* ── 12. LABELS ────────────────────────────────────────────────── */
/* 排除 Radio/Checkbox 選項的 <label><input>...<span>選項文字</span></label>:
   這些「藥丸」本身有自己的底色(未選中時剛好跟 --c-text-subtle 深色版同一種
   灰),套上這條「小字大寫、次要色」的欄位標籤樣式後文字顏色跟底色一樣,
   選項文字整個看不見。用 :has(input) 排除選項本身,只保留純欄位標籤(不含
   表單控制項的 label)套用這個樣式 */
.block > .wrap > label:not(:has(input)) > span,
.block > .label-wrap > span,
label:not(:has(input)) > span {
    color: var(--c-text-subtle) !important;
    font-weight: 700 !important;
    font-size: 0.70rem !important;
    text-transform: uppercase !important;
    letter-spacing: 0.08em !important;
}

/* ── 13. DOWNLOAD SECTION ──────────────────────────────────────── */
.download-bar {
    display: flex; align-items: center; gap: 10px;
    background: linear-gradient(135deg,rgba(var(--c-accent-rgb),.06),rgba(var(--c-accent2-rgb),.06));
    border: 1px solid rgba(var(--c-accent-rgb),.20);
    border-radius: 12px;
    padding: .8rem 1.1rem;
    margin-top: 1rem;
}
.download-bar svg { flex-shrink: 0; }

/* ── 14. SCROLLBAR ─────────────────────────────────────────────── */
/* 2026-07-31(Batch E 第 3 項):業主回報「每個區塊都有 scroll bar 很
   干擾視覺」,改成預設不顯示、滑鼠懸停在該區塊時才出現。
   刻意不用 display:none 或改 overflow 值:那類做法會讓捲動容器的
   內容寬度在有無捲軸間跳動(畫面撐一下縮一下),改成軌道/寬度全程
   保持不變(width 維持 5px、track 維持 transparent),只把 thumb 顏色
   在「未懸停」時設成 transparent、懸停該容器時才變回原本的
   --c-scrollbar/--c-scrollbar-hover——thumb 本身仍佔用同樣的 5px
   空間,只是視覺上透明,不會有任何版面位移。
   `:hover::-webkit-scrollbar-thumb` 這個寫法是給「目前被滑鼠懸停的
   那個元素」套用它自己的捲軸樣式,不需要額外掛在特定 class 上,
   全站任何可捲動區塊懸停時都會生效(.nav-col 的項目捲動區、
   .canvas-scroll、.main-chat 的訊息區、dataframe 內部捲動……)。 */
::-webkit-scrollbar { width: 5px; height: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: transparent; border-radius: 3px; }
:hover::-webkit-scrollbar-thumb { background: var(--c-scrollbar); }
:hover::-webkit-scrollbar-thumb:hover { background: var(--c-scrollbar-hover); }
/* 2026-08-12(業主回報「表格欄位錯版」)──────────────────────────────
   症狀:表格右側出現一條白色空帶,看起來像最後一欄被切掉。
   逐項量過全部 16 張表(語料說明 2 張+五個結果頁),**表頭與資料列的欄寬
   完全一致、沒有任何一欄真的錯位**,所以不是欄位對不齊,是捲軸佔掉的位置:
   Gradio 給 dataframe 的內部容器 .virtual-table-viewport 設了
   `max-height:500px; overflow-y:auto`,列數一多就出現垂直捲軸,佔掉 10px
   版面寬度,而 .table-wrap 的底色是白的 → 那 10px 就是白帶。實測不捲動時
   表格右緣只差 2px(框線),一捲動就跳到 12px,與業主截圖吻合。

   踩坑(值得記):一開始想沿用本檔第 14 節那套「平常透明、懸停才出現」的
   ::-webkit-scrollbar 規則,寫了帶 class 的版本卻完全沒生效。真因不是
   specificity——Gradio 在這個元件上設了**標準屬性** scrollbar-width:thin
   與 scrollbar-color,而 Chrome 只要看到標準屬性,就會整組忽略 legacy 的
   ::-webkit-scrollbar 偽元素。所以要改這個元件的捲軸,只能用標準屬性。

   **試過但不能用的修法(重要,別再走一次)**:把 max-height 拿掉讓表格
   整條攤開、交給 canvas 捲(比照 .exec-log 那個「不要兩層捲動」的既有決定)。
   實測會壞掉——Gradio 這個表格是**虛擬捲動**,只渲染視窗內的列,靠自己的
   捲動位置決定要畫哪幾列。拿掉 max-height 之後它不再自己捲,虛擬化拿不到
   捲動事件:敘述統計那張表變成「保留了 2832px 高度、卻只畫出 1 列」的空表。
   所以這個元件的內部捲動必須留著。

   最終修法:全部 dataframe 一律 scrollbar-width:none,捲軸不再佔版面寬度,
   白帶完全消失(實測 10px→0、表格右緣只剩 2px 框線)。滾輪/觸控/鍵盤
   照常可捲,虛擬化不受影響(已實測 scrollTop 有效、列照樣重新渲染)。
   範圍是逐表量完才定的——一開始想「只有語料表單藏捲軸、結果頁保留橫向
   提示」,量完發現方向剛好相反:會出現白帶的是敘述統計/差異性分析/
   監督式學習那三張長表(垂直捲動、不需橫捲),而唯一需要橫向捲軸的語言
   指標分析(76 欄)從來不出現垂直捲軸、也就沒有白帶。
   代價講明:語言指標分析那條橫向捲軸也跟著隱形,使用者要靠「右緣被切一半
   的欄位」+Shift+滾輪察覺還有欄位。覺得提示不夠時,把下面 #canvas_idx
   那兩行註解解開即可單獨還原它的捲軸。
   !important 是必要的:Gradio 在同一個元素上設 scrollbar-width:thin,
   不加就吃不到(語料表單那條是靠 #metadata_zone 的 id 才贏過它)。 */
.virtual-table-viewport {
    scrollbar-width: none !important;
}
/* 2026-08-14(業主回報「語言指標分析表格被截斷」):語言指標分析欄數多
   (目前實際顯示 62 項指標,欄數隨 HIDDEN_INDICATOR_CODES 增減),橫向
   捲軸被上面那條全站規則隱形後,使用者只能看到「右緣被切一半的欄位」,
   沒有提示還能左右拖曳看完整表格。單獨還原這裡的橫向捲軸(沿用第 14 節
   「預設透明、懸停才出現」那套視覺邏輯,只是這裡是 scrollbar-color 的
   標準屬性版本,不是 ::-webkit-scrollbar 偽元素)。
   !important 是必要的:上面那條全站規則本身也是 !important,CSS 的
   important 優先於一般宣告、跟選擇器精確度無關,這裡不加就贏不了它
   (實測套用後 scrollbar-width 電腦算出來還是 none,就是漏了這個)。
   2026-08-15:業主回報差異性分析表格(欄數也不少:指標+每組 N/M/SD+
   方法/統計量/p值/顯著/效果量名稱/效果量/效果大小)右側欄位一樣
   被切掉,同一顆坑,範圍擴大成 #canvas_stat(統計比較分析——敘述統計/
   差異性分析/監督式/非監督式共用這個容器)。敘述統計那張表欄數少、
   本來就不會超出容器寬度,橫向捲軸不會出現,套用這條規則對它沒有副作用。 */
#canvas_idx .virtual-table-viewport,
#canvas_stat .virtual-table-viewport { scrollbar-width: thin !important; scrollbar-color: transparent transparent !important; }
#canvas_idx .virtual-table-viewport:hover,
#canvas_stat .virtual-table-viewport:hover { scrollbar-color: var(--c-scrollbar) transparent !important; }

/* ── 15. FOCUS & ACCESSIBILITY ─────────────────────────────────── */
:focus-visible {
    outline: 2px solid var(--c-accent) !important;
    outline-offset: 2px !important;
    border-radius: 4px;
}
a { color: var(--c-accent-strong); transition: color .15s; }
a:hover { color: var(--c-link-hover); }

/* ── 16. REDUCED MOTION ────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
    }
}

/* ── 17. RESPONSIVE ────────────────────────────────────────────── */
/* 三欄式工作區(nav-col/chat-col/canvas-col)僅限桌面寬度;手機或窄視窗
   (≤900px)chat-col/canvas-col 改回上下堆疊,不再各自獨立捲動(避免
   小螢幕出現多層巢狀捲軸),改用頁面本身捲動。nav-col 這裡刻意不列
   進來:Phase 8-E 把它改成手機 off-canvas 抽屜(position:fixed,見
   「5e. NAV-COL 收合/手機漢堡抽屜」區塊),跟著上下堆疊會跟抽屜的
   fixed 定位互相打架。 */
@media (max-width: 900px) {
    .main-layout-row { flex-direction: column !important; }
    .main-layout-row .chat-col,
    .main-layout-row .canvas-col {
        width: 100% !important;
        max-width: 100% !important;
        flex: 1 1 auto !important;
        height: auto !important;
        overflow-y: visible !important;
    }
    .canvas-scroll { overflow-y: visible !important; min-height: 0 !important; }
}
@media (max-width: 768px) {
    .app-header .title-line { font-size: 1.1rem !important; }
    .tabitem { padding: 1rem .75rem !important; }
    .app-header .steps { gap: 3px; }
    .step-pill { font-size: 0.62rem; padding: 2px 7px; }
}