langcoach 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1988 @@
1
+ :root {
2
+ /* Notebook tokens: exact copy of NOTEBOOK_TOKENS (@langcoach/core theme.ts); test/theme-tokens.test.ts keeps them in sync. */
3
+ --lc-paper: #faf7f2;
4
+ --lc-rule: #efe7da;
5
+ --lc-border: #eadfcd;
6
+ --lc-shadow: 0 3px 0 #eadfcd;
7
+ --lc-streak-tile: #fff1e0;
8
+ --lc-words-tile: #eef6e8;
9
+ --lc-sticker: #fde68a;
10
+ --lc-sticker-rotate: -6deg;
11
+ --lc-xp-from: #fb923c;
12
+ --lc-xp-to: #facc15;
13
+ --lc-learned-bg: #dcfce7;
14
+ --lc-learned-fg: #166534;
15
+ --lc-weak-bg: #ffe4e6;
16
+ --lc-weak-fg: #9f1239;
17
+ --lc-radius: 14px;
18
+ --lc-serif: Georgia, "Times New Roman", serif;
19
+ --lc-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
20
+
21
+ --bg: var(--lc-paper);
22
+ --surface: #ffffff;
23
+ --surface-muted: #f6f0e6;
24
+ --border: var(--lc-border);
25
+ --text: #292524;
26
+ --text-muted: #6b5d4f;
27
+ --primary: #c2410c;
28
+ --primary-hover: #9a3412;
29
+ --primary-soft: var(--lc-streak-tile);
30
+ --on-primary: #ffffff;
31
+ --success: var(--lc-learned-fg);
32
+ --success-soft: var(--lc-learned-bg);
33
+ --warning: #b45309;
34
+ --danger: var(--lc-weak-fg);
35
+ --danger-soft: var(--lc-weak-bg);
36
+ --radius: var(--lc-radius);
37
+ --shadow: var(--lc-shadow);
38
+ --sticker-edge: #e5c76b;
39
+ --sticker-ink: #292524;
40
+ }
41
+
42
+ /* Warm dark variant of the same notebook: ink-stained paper, same roles. */
43
+ @media (prefers-color-scheme: dark) {
44
+ :root {
45
+ --lc-paper: #1c1814;
46
+ --lc-rule: #29221b;
47
+ --lc-border: #3d3329;
48
+ --lc-shadow: 0 3px 0 #0f0c09;
49
+ --lc-streak-tile: #33241a;
50
+ --lc-words-tile: #1f2a1d;
51
+ --lc-sticker: #fcd34d;
52
+ --lc-learned-bg: #173322;
53
+ --lc-learned-fg: #86efac;
54
+ --lc-weak-bg: #3b1a22;
55
+ --lc-weak-fg: #fda4af;
56
+
57
+ --surface: #26201a;
58
+ --surface-muted: #2f281f;
59
+ --text: #f3ece2;
60
+ --text-muted: #bcae9b;
61
+ --primary: #fb923c;
62
+ --primary-hover: #fdba74;
63
+ --on-primary: #1c1814;
64
+ --warning: #fbbf24;
65
+ --sticker-edge: #b45309;
66
+ }
67
+ }
68
+
69
+ * {
70
+ box-sizing: border-box;
71
+ }
72
+
73
+ body {
74
+ margin: 0;
75
+ font: 400 14px/1.45 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
76
+ background: var(--bg);
77
+ color: var(--text);
78
+ min-width: 280px;
79
+ }
80
+
81
+ .panel-shell {
82
+ display: flex;
83
+ flex-direction: column;
84
+ min-height: 100vh;
85
+ max-width: 520px;
86
+ margin: 0 auto;
87
+ }
88
+
89
+ .panel-header {
90
+ display: flex;
91
+ align-items: center;
92
+ justify-content: space-between;
93
+ padding: 12px 16px;
94
+ background: var(--surface);
95
+ border-bottom: 1px solid var(--border);
96
+ position: sticky;
97
+ top: 0;
98
+ z-index: 10;
99
+ }
100
+
101
+ .brand {
102
+ font-weight: 700;
103
+ font-size: 16px;
104
+ letter-spacing: -0.01em;
105
+ }
106
+
107
+ .header-pills {
108
+ display: flex;
109
+ gap: 8px;
110
+ align-items: center;
111
+ font-weight: 600;
112
+ font-size: 13px;
113
+ }
114
+
115
+ .pill {
116
+ display: inline-flex;
117
+ align-items: center;
118
+ gap: 4px;
119
+ padding: 4px 10px;
120
+ border-radius: 999px;
121
+ background: var(--surface-muted);
122
+ }
123
+
124
+ .tab-bar {
125
+ display: grid;
126
+ grid-template-columns: repeat(6, 1fr);
127
+ gap: 4px;
128
+ padding: 6px 10px;
129
+ background: var(--surface);
130
+ border-bottom: 1px solid var(--border);
131
+ }
132
+
133
+ .tab-btn {
134
+ appearance: none;
135
+ border: none;
136
+ background: transparent;
137
+ color: var(--text-muted);
138
+ font: inherit;
139
+ font-size: 12px;
140
+ font-weight: 600;
141
+ padding: 8px 4px;
142
+ border-radius: 8px;
143
+ cursor: pointer;
144
+ text-align: center;
145
+ overflow-wrap: anywhere;
146
+ }
147
+
148
+ .tab-btn[aria-current="page"] {
149
+ background: var(--primary-soft);
150
+ color: var(--primary);
151
+ }
152
+
153
+ .panel-main {
154
+ flex: 1;
155
+ padding: 16px;
156
+ display: flex;
157
+ flex-direction: column;
158
+ gap: 14px;
159
+ }
160
+
161
+ .card {
162
+ background: var(--surface);
163
+ border: 1px solid var(--border);
164
+ border-radius: var(--radius);
165
+ padding: 14px;
166
+ box-shadow: var(--shadow);
167
+ }
168
+
169
+ h1, h2, h3 {
170
+ margin: 0 0 6px;
171
+ line-height: 1.25;
172
+ }
173
+
174
+ h1 { font-size: 18px; }
175
+ h2 { font-size: 15px; }
176
+ h3 { font-size: 14px; }
177
+
178
+ .muted {
179
+ color: var(--text-muted);
180
+ font-size: 13px;
181
+ margin: 0;
182
+ }
183
+
184
+ .form-grid {
185
+ display: flex;
186
+ flex-direction: column;
187
+ gap: 12px;
188
+ }
189
+
190
+ .field {
191
+ display: flex;
192
+ flex-direction: column;
193
+ gap: 4px;
194
+ }
195
+
196
+ .field label {
197
+ font-weight: 600;
198
+ font-size: 13px;
199
+ }
200
+
201
+ .field-row {
202
+ display: flex;
203
+ align-items: center;
204
+ gap: 8px;
205
+ }
206
+
207
+ select, input[type="text"], input[type="time"] {
208
+ width: 100%;
209
+ padding: 8px 10px;
210
+ border-radius: 8px;
211
+ border: 1px solid var(--border);
212
+ background: var(--surface);
213
+ color: var(--text);
214
+ font: inherit;
215
+ }
216
+
217
+ button {
218
+ font: inherit;
219
+ font-weight: 600;
220
+ border-radius: 8px;
221
+ padding: 8px 12px;
222
+ cursor: pointer;
223
+ border: 1px solid var(--border);
224
+ background: var(--surface-muted);
225
+ color: var(--text);
226
+ }
227
+
228
+ button.primary {
229
+ background: var(--primary);
230
+ color: #ffffff;
231
+ border-color: var(--primary);
232
+ }
233
+
234
+ button.primary:hover {
235
+ background: var(--primary-hover);
236
+ }
237
+
238
+ button.danger {
239
+ background: var(--danger-soft);
240
+ color: var(--danger);
241
+ border-color: var(--danger);
242
+ }
243
+
244
+ button:focus-visible, select:focus-visible, input:focus-visible {
245
+ outline: 3px solid var(--primary);
246
+ outline-offset: 2px;
247
+ }
248
+
249
+ .today-summary {
250
+ display: flex;
251
+ align-items: center;
252
+ justify-content: space-between;
253
+ gap: 12px;
254
+ }
255
+
256
+ .ring-badge {
257
+ display: inline-flex;
258
+ align-items: center;
259
+ justify-content: center;
260
+ min-width: 56px;
261
+ height: 56px;
262
+ border-radius: 50%;
263
+ border: 4px solid var(--primary);
264
+ font-weight: 700;
265
+ font-size: 15px;
266
+ }
267
+
268
+ .task-list {
269
+ display: flex;
270
+ flex-direction: column;
271
+ gap: 8px;
272
+ list-style: none;
273
+ padding: 0;
274
+ margin: 0;
275
+ }
276
+
277
+ .task-item {
278
+ display: flex;
279
+ align-items: center;
280
+ justify-content: space-between;
281
+ gap: 10px;
282
+ padding: 10px 12px;
283
+ border-radius: 10px;
284
+ border: 1px solid var(--border);
285
+ background: var(--surface);
286
+ }
287
+
288
+ .task-item.done {
289
+ background: var(--success-soft);
290
+ border-color: var(--success);
291
+ }
292
+
293
+ .theme-chip {
294
+ display: flex;
295
+ flex-direction: column;
296
+ gap: 8px;
297
+ }
298
+
299
+ .theme-actions {
300
+ display: flex;
301
+ gap: 8px;
302
+ flex-wrap: wrap;
303
+ }
304
+
305
+ .flashcard {
306
+ display: flex;
307
+ flex-direction: column;
308
+ gap: 12px;
309
+ padding: 18px;
310
+ min-height: 180px;
311
+ }
312
+
313
+ .card-term {
314
+ font-size: 24px;
315
+ font-weight: 700;
316
+ overflow-wrap: anywhere;
317
+ }
318
+
319
+ .card-pron {
320
+ font-size: 14px;
321
+ color: var(--text-muted);
322
+ }
323
+
324
+ .card-back {
325
+ border-top: 1px solid var(--border);
326
+ padding-top: 12px;
327
+ display: flex;
328
+ flex-direction: column;
329
+ gap: 8px;
330
+ }
331
+
332
+ .rating-grid {
333
+ display: grid;
334
+ grid-template-columns: repeat(4, 1fr);
335
+ gap: 6px;
336
+ }
337
+
338
+ .stats-grid {
339
+ display: grid;
340
+ grid-template-columns: repeat(2, 1fr);
341
+ gap: 10px;
342
+ }
343
+
344
+ .stat-box {
345
+ padding: 12px;
346
+ border-radius: 10px;
347
+ border: 1px solid var(--border);
348
+ background: var(--surface);
349
+ }
350
+
351
+ .stat-value {
352
+ font-size: 20px;
353
+ font-weight: 700;
354
+ }
355
+
356
+ .ach-grid {
357
+ display: grid;
358
+ grid-template-columns: repeat(2, 1fr);
359
+ gap: 8px;
360
+ }
361
+
362
+ .ach-item {
363
+ padding: 10px;
364
+ border-radius: 10px;
365
+ border: 1px solid var(--border);
366
+ background: var(--surface-muted);
367
+ opacity: 0.7;
368
+ }
369
+
370
+ .ach-item.unlocked {
371
+ background: var(--success-soft);
372
+ border-color: var(--success);
373
+ opacity: 1;
374
+ }
375
+
376
+ .progress-bar {
377
+ height: 8px;
378
+ border-radius: 999px;
379
+ background: var(--surface-muted);
380
+ overflow: hidden;
381
+ margin-top: 6px;
382
+ }
383
+
384
+ .progress-fill {
385
+ height: 100%;
386
+ background: var(--primary);
387
+ }
388
+
389
+ /* Segmented control (List vs Cards toggle) */
390
+ .segmented-control {
391
+ display: grid;
392
+ grid-template-columns: 1fr 1fr;
393
+ gap: 4px;
394
+ background: var(--surface-muted);
395
+ padding: 4px;
396
+ border-radius: 10px;
397
+ border: 1px solid var(--border);
398
+ }
399
+
400
+ .segmented-btn {
401
+ background: transparent;
402
+ color: var(--text-muted);
403
+ border: none;
404
+ border-radius: 8px;
405
+ padding: 8px;
406
+ font-weight: 600;
407
+ cursor: pointer;
408
+ transition: all 0.15s ease;
409
+ }
410
+
411
+ .segmented-btn:hover {
412
+ color: var(--text);
413
+ background: rgba(0, 0, 0, 0.04);
414
+ }
415
+
416
+ .segmented-btn.active {
417
+ background: var(--surface);
418
+ color: var(--text);
419
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
420
+ }
421
+
422
+ /* Vocabulary List View */
423
+ .vocab-section {
424
+ padding: 12px;
425
+ }
426
+
427
+ .vocab-list {
428
+ display: flex;
429
+ flex-direction: column;
430
+ gap: 10px;
431
+ }
432
+
433
+ .vocab-item {
434
+ padding: 12px;
435
+ border-radius: 10px;
436
+ border: 1px solid var(--border);
437
+ background: var(--surface-muted);
438
+ display: flex;
439
+ flex-direction: column;
440
+ gap: 6px;
441
+ }
442
+
443
+ .vocab-row {
444
+ display: flex;
445
+ align-items: center;
446
+ justify-content: space-between;
447
+ gap: 8px;
448
+ }
449
+
450
+ .vocab-term-group {
451
+ display: flex;
452
+ align-items: center;
453
+ gap: 8px;
454
+ flex-wrap: wrap;
455
+ }
456
+
457
+ .vocab-term {
458
+ font-size: 16px;
459
+ font-weight: 700;
460
+ color: var(--text);
461
+ }
462
+
463
+ .status-tag {
464
+ font-size: 11px;
465
+ font-weight: 600;
466
+ padding: 2px 8px;
467
+ border-radius: 999px;
468
+ border: 1px solid var(--border);
469
+ }
470
+
471
+ .status-tag.learned {
472
+ background: var(--success-soft);
473
+ color: var(--success);
474
+ border-color: var(--success);
475
+ }
476
+
477
+ .status-tag.new {
478
+ background: var(--primary-soft);
479
+ color: var(--primary);
480
+ border-color: var(--primary);
481
+ }
482
+
483
+ .vocab-trans-row {
484
+ display: flex;
485
+ align-items: center;
486
+ justify-content: space-between;
487
+ gap: 8px;
488
+ padding: 4px 0;
489
+ }
490
+
491
+ .vocab-trans {
492
+ font-size: 14px;
493
+ font-weight: 500;
494
+ color: var(--text);
495
+ }
496
+
497
+ .vocab-meanings,
498
+ .card-meanings {
499
+ display: flex;
500
+ flex-direction: column;
501
+ gap: 2px;
502
+ min-width: 0;
503
+ }
504
+
505
+ .vocab-native {
506
+ font-size: 14px;
507
+ font-weight: 600;
508
+ color: var(--text);
509
+ }
510
+
511
+ .vocab-trans.secondary {
512
+ font-size: 12px;
513
+ font-weight: 400;
514
+ color: var(--text-muted);
515
+ }
516
+
517
+ .native-banner progress {
518
+ width: 100%;
519
+ }
520
+
521
+ .btn-subtle {
522
+ background: transparent;
523
+ border: none;
524
+ color: var(--primary);
525
+ font-size: 12px;
526
+ font-weight: 600;
527
+ cursor: pointer;
528
+ padding: 2px 6px;
529
+ border-radius: 6px;
530
+ }
531
+
532
+ .btn-subtle:hover {
533
+ background: var(--primary-soft);
534
+ }
535
+
536
+ .vocab-example {
537
+ font-size: 12px;
538
+ font-style: italic;
539
+ padding-left: 8px;
540
+ border-left: 2px solid var(--border);
541
+ }
542
+
543
+ .vocab-footer {
544
+ display: flex;
545
+ justify-content: flex-end;
546
+ margin-top: 4px;
547
+ }
548
+
549
+ .btn-sm {
550
+ font-size: 12px;
551
+ padding: 4px 10px;
552
+ border-radius: 6px;
553
+ }
554
+
555
+ .icon-btn {
556
+ background: transparent;
557
+ border: none;
558
+ font-size: 14px;
559
+ cursor: pointer;
560
+ padding: 2px 4px;
561
+ border-radius: 4px;
562
+ }
563
+
564
+ .icon-btn:hover {
565
+ background: var(--surface);
566
+ }
567
+
568
+ .error-text {
569
+ margin: 0;
570
+ color: var(--danger);
571
+ font-weight: 600;
572
+ }
573
+
574
+ .notice {
575
+ margin: 0;
576
+ padding: 8px 10px;
577
+ border-radius: 8px;
578
+ background: var(--surface-muted);
579
+ }
580
+
581
+ .notice.error {
582
+ background: var(--danger-soft);
583
+ color: var(--danger);
584
+ }
585
+
586
+ .confirm-box {
587
+ display: grid;
588
+ gap: 8px;
589
+ padding: 10px;
590
+ border: 1px solid var(--danger);
591
+ border-radius: 8px;
592
+ background: var(--danger-soft);
593
+ }
594
+
595
+ .mono {
596
+ font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
597
+ word-break: break-all;
598
+ }
599
+
600
+ /* ---------- Practice ---------- */
601
+ :root {
602
+ --px-green: #16a34a;
603
+ --px-green-soft: #dcfce7;
604
+ --px-green-ink: #14532d;
605
+ --px-red: #dc2626;
606
+ --px-red-soft: #fee2e2;
607
+ --px-red-ink: #7f1d1d;
608
+ --px-amber: #b45309;
609
+ --px-amber-soft: #fef3c7;
610
+ --px-amber-ink: #78350f;
611
+ --px-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
612
+ }
613
+
614
+ @media (prefers-color-scheme: dark) {
615
+ :root {
616
+ --px-green: #4ade80;
617
+ --px-green-soft: #12301f;
618
+ --px-green-ink: #bbf7d0;
619
+ --px-red: #f87171;
620
+ --px-red-soft: #3b1719;
621
+ --px-red-ink: #fecaca;
622
+ --px-amber: #fbbf24;
623
+ --px-amber-soft: #3a2a0c;
624
+ --px-amber-ink: #fde68a;
625
+ }
626
+ }
627
+
628
+ .practice-host[hidden] {
629
+ display: none;
630
+ }
631
+
632
+ .practice-cta {
633
+ display: flex;
634
+ align-items: center;
635
+ gap: 12px;
636
+ width: 100%;
637
+ min-height: 64px;
638
+ padding: 12px 16px;
639
+ border: none;
640
+ border-radius: 16px;
641
+ text-align: start;
642
+ box-shadow: 0 4px 0 rgba(0, 0, 0, 0.18);
643
+ transition: transform 150ms var(--px-ease), box-shadow 150ms var(--px-ease);
644
+ }
645
+ .practice-cta:hover {
646
+ transform: translateY(-1px);
647
+ }
648
+ .practice-cta:active {
649
+ transform: translateY(3px);
650
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.18);
651
+ }
652
+ .practice-cta-icon {
653
+ font-size: 28px;
654
+ }
655
+ .practice-cta-text {
656
+ display: flex;
657
+ flex-direction: column;
658
+ flex: 1;
659
+ gap: 2px;
660
+ }
661
+ .practice-cta-text strong {
662
+ font-size: 16px;
663
+ }
664
+ .practice-cta-text span {
665
+ font-weight: 500;
666
+ opacity: 0.92;
667
+ font-size: 13px;
668
+ }
669
+ .practice-cta-arrow {
670
+ font-size: 20px;
671
+ }
672
+
673
+ .px-start,
674
+ .px-end,
675
+ .px-empty {
676
+ display: flex;
677
+ flex-direction: column;
678
+ align-items: center;
679
+ text-align: center;
680
+ gap: 12px;
681
+ padding: 22px 18px;
682
+ border-radius: 18px;
683
+ position: relative;
684
+ overflow: hidden;
685
+ }
686
+ .px-hero,
687
+ .px-empty-icon {
688
+ font-size: 44px;
689
+ line-height: 1;
690
+ animation: px-pop 320ms var(--px-ease) both;
691
+ }
692
+ .px-stats,
693
+ .px-end-stats {
694
+ display: grid;
695
+ grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
696
+ gap: 10px;
697
+ width: 100%;
698
+ }
699
+ .px-stat,
700
+ .px-end-stat {
701
+ display: flex;
702
+ flex-direction: column;
703
+ gap: 2px;
704
+ padding: 10px 8px;
705
+ border-radius: 14px;
706
+ background: var(--surface-muted);
707
+ }
708
+ .px-stat strong,
709
+ .px-end-stat strong {
710
+ font-size: 24px;
711
+ line-height: 1.1;
712
+ }
713
+ .px-end-stat.xp strong {
714
+ color: var(--px-amber);
715
+ }
716
+ .px-end-stat .muted,
717
+ .px-stat .muted {
718
+ font-size: 12px;
719
+ }
720
+ .px-desc {
721
+ margin: 0;
722
+ max-width: 34ch;
723
+ }
724
+ .px-kinds {
725
+ list-style: none;
726
+ margin: 0;
727
+ padding: 0;
728
+ display: flex;
729
+ flex-wrap: wrap;
730
+ justify-content: center;
731
+ gap: 6px;
732
+ }
733
+ .px-kinds li {
734
+ font-size: 12px;
735
+ font-weight: 600;
736
+ padding: 4px 10px;
737
+ border-radius: 999px;
738
+ background: var(--primary-soft);
739
+ color: var(--primary);
740
+ }
741
+ .px-big-btn {
742
+ width: 100%;
743
+ min-height: 48px;
744
+ font-size: 16px;
745
+ border-radius: 14px;
746
+ box-shadow: 0 4px 0 rgba(0, 0, 0, 0.15);
747
+ transition: transform 150ms var(--px-ease), box-shadow 150ms var(--px-ease), background 150ms;
748
+ }
749
+ .px-big-btn:active:not(:disabled) {
750
+ transform: translateY(3px);
751
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.15);
752
+ }
753
+ /* Focused by script (tabindex=-1) so it is announced; no ring unless the keyboard put it there. */
754
+ .px-end-title:focus:not(:focus-visible) {
755
+ outline: none;
756
+ }
757
+ .px-end-actions {
758
+ display: grid;
759
+ gap: 10px;
760
+ width: 100%;
761
+ }
762
+ .px-goal {
763
+ display: flex;
764
+ align-items: center;
765
+ gap: 12px;
766
+ width: 100%;
767
+ padding: 10px 12px;
768
+ border-radius: 14px;
769
+ background: var(--surface-muted);
770
+ text-align: start;
771
+ }
772
+ .goal-ring-track {
773
+ fill: none;
774
+ stroke: var(--border);
775
+ }
776
+ .goal-ring-fill {
777
+ fill: none;
778
+ stroke: var(--primary);
779
+ stroke-linecap: round;
780
+ transition: stroke-dashoffset 600ms var(--px-ease);
781
+ }
782
+ .goal-ring.is-met .goal-ring-fill {
783
+ stroke: var(--px-green);
784
+ }
785
+ .goal-ring {
786
+ position: relative;
787
+ flex: none;
788
+ display: inline-grid;
789
+ place-items: center;
790
+ }
791
+ .goal-ring svg {
792
+ position: absolute;
793
+ inset: 0;
794
+ }
795
+ .goal-ring-text {
796
+ position: relative;
797
+ font-size: 12px;
798
+ font-weight: 800;
799
+ color: var(--text);
800
+ }
801
+ .goal-ring.is-met .goal-ring-text {
802
+ color: var(--px-green, var(--success));
803
+ }
804
+
805
+ /* run */
806
+ .px-run {
807
+ display: flex;
808
+ flex-direction: column;
809
+ gap: 12px;
810
+ /* room for the feedback sheet, so nothing jumps when it opens */
811
+ padding-bottom: 190px;
812
+ }
813
+ .px-top {
814
+ display: flex;
815
+ align-items: center;
816
+ gap: 10px;
817
+ min-height: 44px;
818
+ }
819
+ .px-quit {
820
+ min-width: 44px;
821
+ min-height: 44px;
822
+ font-size: 18px;
823
+ color: var(--text-muted);
824
+ }
825
+ .px-progress {
826
+ flex: 1;
827
+ height: 14px;
828
+ border-radius: 999px;
829
+ background: var(--surface-muted);
830
+ overflow: hidden;
831
+ }
832
+ .px-progress-fill {
833
+ height: 100%;
834
+ border-radius: 999px;
835
+ background: linear-gradient(90deg, var(--px-green), #22c55e);
836
+ transition: width 250ms var(--px-ease);
837
+ }
838
+ .px-combo {
839
+ font-size: 12px;
840
+ font-weight: 700;
841
+ color: var(--px-amber);
842
+ white-space: nowrap;
843
+ }
844
+ .px-combo.is-on {
845
+ animation: px-pop 250ms var(--px-ease) both;
846
+ }
847
+ .px-retry-tag {
848
+ margin: 0;
849
+ align-self: flex-start;
850
+ font-size: 12px;
851
+ font-weight: 700;
852
+ color: var(--px-amber-ink);
853
+ background: var(--px-amber-soft);
854
+ padding: 3px 10px;
855
+ border-radius: 999px;
856
+ }
857
+ .px-card {
858
+ border-radius: 18px;
859
+ padding: 18px 16px;
860
+ min-height: 360px;
861
+ animation: px-in 220ms var(--px-ease) both;
862
+ }
863
+ .ex {
864
+ display: flex;
865
+ flex-direction: column;
866
+ gap: 14px;
867
+ }
868
+ .ex-ask {
869
+ margin: 0;
870
+ font-weight: 700;
871
+ font-size: 15px;
872
+ color: var(--text-muted);
873
+ }
874
+ .ex-prompt {
875
+ display: flex;
876
+ flex-direction: column;
877
+ align-items: center;
878
+ gap: 4px;
879
+ min-height: 84px;
880
+ justify-content: center;
881
+ text-align: center;
882
+ }
883
+ .prompt-row {
884
+ display: flex;
885
+ align-items: center;
886
+ gap: 8px;
887
+ }
888
+ .prompt-term {
889
+ font-size: 28px;
890
+ font-weight: 800;
891
+ line-height: 1.2;
892
+ overflow-wrap: anywhere;
893
+ }
894
+ .prompt-meaning {
895
+ font-size: 22px;
896
+ }
897
+ .prompt-pron {
898
+ color: var(--text-muted);
899
+ font-style: italic;
900
+ }
901
+ .prompt-sentence {
902
+ margin: 0;
903
+ font-size: 19px;
904
+ font-weight: 600;
905
+ line-height: 1.5;
906
+ }
907
+ .prompt-sentence .gap {
908
+ display: inline-block;
909
+ min-width: 3em;
910
+ border-bottom: 3px solid var(--primary);
911
+ margin: 0 4px;
912
+ }
913
+ .prompt-hint {
914
+ margin: 0;
915
+ font-size: 13px;
916
+ }
917
+ .speak-btn {
918
+ font-size: 20px;
919
+ min-width: 44px;
920
+ min-height: 44px;
921
+ }
922
+ .play-big {
923
+ width: 84px;
924
+ height: 84px;
925
+ border-radius: 50%;
926
+ border: none;
927
+ font-size: 34px;
928
+ background: var(--primary);
929
+ color: #fff;
930
+ box-shadow: 0 5px 0 rgba(0, 0, 0, 0.2);
931
+ transition: transform 150ms var(--px-ease);
932
+ }
933
+ .play-big:active {
934
+ transform: translateY(3px) scale(0.98);
935
+ }
936
+
937
+ .option-grid {
938
+ display: grid;
939
+ grid-template-columns: 1fr 1fr;
940
+ gap: 10px;
941
+ }
942
+ .option {
943
+ position: relative;
944
+ display: flex;
945
+ align-items: center;
946
+ gap: 8px;
947
+ min-height: 56px;
948
+ padding: 10px 12px;
949
+ border-radius: 14px;
950
+ border: 2px solid var(--border);
951
+ border-bottom-width: 4px;
952
+ background: var(--surface);
953
+ color: var(--text);
954
+ text-align: start;
955
+ font-size: 15px;
956
+ transition: transform 150ms var(--px-ease), border-color 150ms, background 150ms, opacity 200ms;
957
+ }
958
+ .option:hover:not(:disabled) {
959
+ background: var(--surface-muted);
960
+ }
961
+ .option:active:not(:disabled) {
962
+ transform: translateY(2px);
963
+ }
964
+ .option.is-selected {
965
+ border-color: var(--primary);
966
+ background: var(--primary-soft);
967
+ }
968
+ .option.is-correct {
969
+ border-color: var(--px-green);
970
+ background: var(--px-green-soft);
971
+ color: var(--px-green-ink);
972
+ }
973
+ .option.is-wrong {
974
+ border-color: var(--px-red);
975
+ background: var(--px-red-soft);
976
+ color: var(--px-red-ink);
977
+ animation: px-shake 380ms ease both;
978
+ }
979
+ .option.is-dim {
980
+ opacity: 0.55;
981
+ }
982
+ .option:disabled {
983
+ cursor: default;
984
+ }
985
+ .option-key {
986
+ flex: none;
987
+ display: inline-grid;
988
+ place-items: center;
989
+ width: 22px;
990
+ height: 22px;
991
+ border-radius: 6px;
992
+ border: 1px solid var(--border);
993
+ font-size: 11px;
994
+ color: var(--text-muted);
995
+ }
996
+ .option-text {
997
+ overflow-wrap: anywhere;
998
+ }
999
+ .btn-check {
1000
+ min-height: 48px;
1001
+ font-size: 16px;
1002
+ border-radius: 14px;
1003
+ }
1004
+ .btn-check:disabled {
1005
+ opacity: 0.5;
1006
+ cursor: default;
1007
+ }
1008
+
1009
+ .match-grid {
1010
+ display: grid;
1011
+ grid-template-columns: 1fr 1fr;
1012
+ gap: 10px;
1013
+ }
1014
+ .match-col {
1015
+ display: flex;
1016
+ flex-direction: column;
1017
+ gap: 8px;
1018
+ }
1019
+ .match-item {
1020
+ justify-content: center;
1021
+ text-align: center;
1022
+ min-height: 48px;
1023
+ }
1024
+ .match-item.is-matched {
1025
+ opacity: 0;
1026
+ transform: scale(0.94);
1027
+ pointer-events: none;
1028
+ transition: opacity 250ms var(--px-ease), transform 250ms var(--px-ease);
1029
+ }
1030
+ .match-item.is-shake {
1031
+ border-color: var(--px-red);
1032
+ background: var(--px-red-soft);
1033
+ animation: px-shake 380ms ease both;
1034
+ }
1035
+
1036
+ .type-input {
1037
+ width: 100%;
1038
+ min-height: 52px;
1039
+ font-size: 18px;
1040
+ padding: 10px 14px;
1041
+ border-radius: 14px;
1042
+ border: 2px solid var(--border);
1043
+ background: var(--surface);
1044
+ color: var(--text);
1045
+ }
1046
+ .type-input:focus {
1047
+ border-color: var(--primary);
1048
+ outline: none;
1049
+ }
1050
+
1051
+ /* feedback sheet */
1052
+ .px-sheet {
1053
+ position: fixed;
1054
+ left: 0;
1055
+ right: 0;
1056
+ bottom: 0;
1057
+ z-index: 20;
1058
+ max-width: 520px;
1059
+ margin: 0 auto;
1060
+ padding: 16px 18px calc(16px + env(safe-area-inset-bottom, 0px));
1061
+ border-radius: 18px 18px 0 0;
1062
+ background: var(--surface);
1063
+ border-top: 1px solid var(--border);
1064
+ box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.12);
1065
+ transform: translateY(105%);
1066
+ visibility: hidden;
1067
+ transition: transform 220ms var(--px-ease), visibility 0s linear 220ms, background 150ms;
1068
+ }
1069
+ .px-sheet.is-open {
1070
+ transform: translateY(0);
1071
+ visibility: visible;
1072
+ transition: transform 220ms var(--px-ease), visibility 0s, background 150ms;
1073
+ }
1074
+ .px-sheet.is-correct {
1075
+ background: var(--px-green-soft);
1076
+ color: var(--px-green-ink);
1077
+ }
1078
+ .px-sheet.is-wrong {
1079
+ background: var(--px-red-soft);
1080
+ color: var(--px-red-ink);
1081
+ }
1082
+ .px-sheet.is-almost {
1083
+ background: var(--px-amber-soft);
1084
+ color: var(--px-amber-ink);
1085
+ }
1086
+ .px-sheet-head {
1087
+ display: flex;
1088
+ align-items: center;
1089
+ gap: 10px;
1090
+ font-size: 18px;
1091
+ }
1092
+ .px-sheet-icon {
1093
+ display: inline-grid;
1094
+ place-items: center;
1095
+ width: 32px;
1096
+ height: 32px;
1097
+ border-radius: 50%;
1098
+ font-weight: 800;
1099
+ color: #fff;
1100
+ background: var(--primary);
1101
+ }
1102
+ .px-sheet.is-correct .px-sheet-icon {
1103
+ background: var(--px-green);
1104
+ }
1105
+ .px-sheet.is-wrong .px-sheet-icon {
1106
+ background: var(--px-red);
1107
+ }
1108
+ .px-sheet.is-almost .px-sheet-icon {
1109
+ background: var(--px-amber);
1110
+ }
1111
+ .px-sheet-line {
1112
+ margin: 8px 0 0;
1113
+ }
1114
+ .px-answer {
1115
+ font-weight: 800;
1116
+ }
1117
+ .px-card-line {
1118
+ display: flex;
1119
+ flex-wrap: wrap;
1120
+ align-items: center;
1121
+ gap: 4px 8px;
1122
+ margin-top: 8px;
1123
+ }
1124
+ .px-term {
1125
+ font-weight: 800;
1126
+ font-size: 16px;
1127
+ }
1128
+ .px-card-line .icon-btn {
1129
+ min-width: 36px;
1130
+ min-height: 36px;
1131
+ font-size: 16px;
1132
+ }
1133
+ .px-continue {
1134
+ margin-top: 12px;
1135
+ width: 100%;
1136
+ min-height: 48px;
1137
+ font-size: 16px;
1138
+ border: none;
1139
+ border-radius: 14px;
1140
+ color: #fff;
1141
+ background: var(--primary);
1142
+ box-shadow: 0 4px 0 rgba(0, 0, 0, 0.18);
1143
+ }
1144
+ .px-sheet.is-correct .px-continue {
1145
+ background: var(--px-green);
1146
+ }
1147
+ .px-sheet.is-wrong .px-continue {
1148
+ background: var(--px-red);
1149
+ }
1150
+ .px-sheet.is-almost .px-continue {
1151
+ background: var(--px-amber);
1152
+ }
1153
+ @media (prefers-color-scheme: dark) {
1154
+ .px-continue,
1155
+ .px-sheet-icon {
1156
+ color: #0b1020;
1157
+ }
1158
+ }
1159
+
1160
+ /* confetti */
1161
+ .confetti {
1162
+ position: absolute;
1163
+ inset: 0;
1164
+ pointer-events: none;
1165
+ overflow: hidden;
1166
+ }
1167
+ .confetti-piece {
1168
+ position: absolute;
1169
+ top: -12px;
1170
+ height: 12px;
1171
+ border-radius: 2px;
1172
+ opacity: 0;
1173
+ animation: px-confetti 1.5s cubic-bezier(0.25, 0.6, 0.4, 1) forwards;
1174
+ }
1175
+
1176
+ @keyframes px-confetti {
1177
+ 0% {
1178
+ opacity: 1;
1179
+ transform: translate(0, 0) rotate(0);
1180
+ }
1181
+ 100% {
1182
+ opacity: 0;
1183
+ transform: translate(var(--drift, 0px), 420px) rotate(var(--spin, 360deg));
1184
+ }
1185
+ }
1186
+ @keyframes px-pop {
1187
+ 0% {
1188
+ transform: scale(0.6);
1189
+ opacity: 0;
1190
+ }
1191
+ 70% {
1192
+ transform: scale(1.08);
1193
+ opacity: 1;
1194
+ }
1195
+ 100% {
1196
+ transform: scale(1);
1197
+ }
1198
+ }
1199
+ @keyframes px-in {
1200
+ from {
1201
+ opacity: 0;
1202
+ transform: translateY(6px);
1203
+ }
1204
+ to {
1205
+ opacity: 1;
1206
+ transform: none;
1207
+ }
1208
+ }
1209
+ @keyframes px-shake {
1210
+ 0%,
1211
+ 100% {
1212
+ transform: translateX(0);
1213
+ }
1214
+ 20% {
1215
+ transform: translateX(-6px);
1216
+ }
1217
+ 40% {
1218
+ transform: translateX(6px);
1219
+ }
1220
+ 60% {
1221
+ transform: translateX(-4px);
1222
+ }
1223
+ 80% {
1224
+ transform: translateX(4px);
1225
+ }
1226
+ }
1227
+
1228
+ @media (prefers-reduced-motion: reduce) {
1229
+ .px-hero,
1230
+ .px-empty-icon,
1231
+ .px-combo.is-on,
1232
+ .px-card,
1233
+ .option.is-wrong,
1234
+ .match-item.is-shake {
1235
+ animation: none;
1236
+ }
1237
+ .px-sheet,
1238
+ .px-sheet.is-open,
1239
+ .px-progress-fill,
1240
+ .option,
1241
+ .match-item.is-matched,
1242
+ .goal-ring-fill,
1243
+ .practice-cta,
1244
+ .px-big-btn {
1245
+ transition: none;
1246
+ }
1247
+ .confetti {
1248
+ display: none;
1249
+ }
1250
+ }
1251
+
1252
+ /* ───────────── v2 Task 6: tokens, Words hub, Today, Progress, goal picker ───────────── */
1253
+ :root {
1254
+ --radius-lg: 18px;
1255
+ --accent-orange: #c2410c;
1256
+ --accent-orange-soft: #ffedd5;
1257
+ --accent-ice: #0369a1;
1258
+ --accent-ice-soft: #e0f2fe;
1259
+ --accent-violet: #6d28d9;
1260
+ --accent-violet-soft: #ede9fe;
1261
+ --star: #b45309;
1262
+ --focus: var(--primary);
1263
+ }
1264
+ @media (prefers-color-scheme: dark) {
1265
+ :root {
1266
+ --accent-orange: #fb923c;
1267
+ --accent-orange-soft: #3a2414;
1268
+ --accent-ice: #38bdf8;
1269
+ --accent-ice-soft: #0f2e42;
1270
+ --accent-violet: #a78bfa;
1271
+ --accent-violet-soft: #2a2148;
1272
+ --star: #fbbf24;
1273
+ }
1274
+ }
1275
+
1276
+ .sr-only {
1277
+ position: absolute !important;
1278
+ width: 1px;
1279
+ height: 1px;
1280
+ padding: 0;
1281
+ margin: -1px;
1282
+ overflow: hidden;
1283
+ clip: rect(0 0 0 0);
1284
+ white-space: nowrap;
1285
+ border: 0;
1286
+ }
1287
+
1288
+ /* Cohesive surfaces and targets */
1289
+ .card {
1290
+ border-radius: var(--radius-lg);
1291
+ }
1292
+ button,
1293
+ .tab-btn,
1294
+ .chip {
1295
+ min-height: 44px;
1296
+ }
1297
+ .icon-btn {
1298
+ min-width: 44px;
1299
+ min-height: 44px;
1300
+ font-size: 18px;
1301
+ display: inline-grid;
1302
+ place-items: center;
1303
+ }
1304
+ button.primary {
1305
+ border-radius: 12px;
1306
+ font-weight: 700;
1307
+ }
1308
+ button:focus-visible,
1309
+ select:focus-visible,
1310
+ input:focus-visible,
1311
+ a:focus-visible,
1312
+ .goal-option:focus-within {
1313
+ outline: 3px solid var(--focus);
1314
+ outline-offset: 2px;
1315
+ }
1316
+ button:disabled {
1317
+ cursor: not-allowed;
1318
+ opacity: 0.65;
1319
+ }
1320
+ .section-head {
1321
+ display: flex;
1322
+ align-items: center;
1323
+ justify-content: space-between;
1324
+ gap: 8px;
1325
+ }
1326
+ .empty-state {
1327
+ text-align: center;
1328
+ display: grid;
1329
+ gap: 8px;
1330
+ justify-items: center;
1331
+ }
1332
+ .empty-icon {
1333
+ font-size: 40px;
1334
+ }
1335
+
1336
+ /* Words hub */
1337
+ .words-tools {
1338
+ display: grid;
1339
+ gap: 10px;
1340
+ }
1341
+ .words-search {
1342
+ width: 100%;
1343
+ min-height: 44px;
1344
+ padding: 8px 14px;
1345
+ border-radius: 999px;
1346
+ border: 1px solid var(--border);
1347
+ background: var(--surface);
1348
+ color: var(--text);
1349
+ font: inherit;
1350
+ }
1351
+ .chip-row {
1352
+ display: flex;
1353
+ flex-wrap: wrap;
1354
+ gap: 6px;
1355
+ }
1356
+ .chip {
1357
+ border-radius: 999px;
1358
+ padding: 6px 12px;
1359
+ background: var(--surface);
1360
+ border: 1px solid var(--border);
1361
+ color: var(--text);
1362
+ font-size: 13px;
1363
+ }
1364
+ .chip.active {
1365
+ background: var(--primary);
1366
+ border-color: var(--primary);
1367
+ color: #fff;
1368
+ }
1369
+ .chip-count {
1370
+ display: inline-block;
1371
+ min-width: 20px;
1372
+ margin-inline-start: 4px;
1373
+ padding: 0 6px;
1374
+ border-radius: 999px;
1375
+ background: var(--surface-muted);
1376
+ color: var(--text);
1377
+ font-size: 12px;
1378
+ }
1379
+ .chip.active .chip-count {
1380
+ background: rgba(255, 255, 255, 0.25);
1381
+ color: #fff;
1382
+ }
1383
+ .words-group h2 {
1384
+ margin-bottom: 8px;
1385
+ }
1386
+ .words-group-count {
1387
+ font-weight: 500;
1388
+ font-size: 13px;
1389
+ }
1390
+ .vocab-list {
1391
+ list-style: none;
1392
+ margin: 0;
1393
+ padding: 0;
1394
+ }
1395
+ .word-actions {
1396
+ display: flex;
1397
+ gap: 2px;
1398
+ }
1399
+ .star {
1400
+ color: var(--text-muted);
1401
+ }
1402
+ .star.on {
1403
+ color: var(--star);
1404
+ }
1405
+ .word-footer {
1406
+ display: flex;
1407
+ align-items: center;
1408
+ justify-content: space-between;
1409
+ gap: 8px;
1410
+ margin-top: 8px;
1411
+ }
1412
+ .btn-know {
1413
+ border-radius: 999px;
1414
+ background: var(--success-soft);
1415
+ color: var(--success);
1416
+ border-color: transparent;
1417
+ }
1418
+ .btn-know.done,
1419
+ .btn-know:disabled {
1420
+ background: var(--surface-muted);
1421
+ color: var(--text-muted);
1422
+ opacity: 1;
1423
+ }
1424
+ .word-edit {
1425
+ margin-top: 8px;
1426
+ }
1427
+ .word-edit input {
1428
+ width: 100%;
1429
+ }
1430
+ .strength {
1431
+ display: inline-flex;
1432
+ gap: 3px;
1433
+ align-items: flex-end;
1434
+ }
1435
+ .strength-bar {
1436
+ width: 6px;
1437
+ height: 14px;
1438
+ border-radius: 3px;
1439
+ background: var(--border);
1440
+ }
1441
+ .strength-bar:nth-child(1) { height: 6px; }
1442
+ .strength-bar:nth-child(2) { height: 8px; }
1443
+ .strength-bar:nth-child(3) { height: 10px; }
1444
+ .strength-bar:nth-child(4) { height: 12px; }
1445
+ .strength.s1 .strength-bar.on,
1446
+ .strength.s2 .strength-bar.on { background: var(--accent-orange); }
1447
+ .strength.s3 .strength-bar.on { background: var(--primary); }
1448
+ .strength.s4 .strength-bar.on,
1449
+ .strength.s5 .strength-bar.on { background: var(--success); }
1450
+ .show-more {
1451
+ width: 100%;
1452
+ border-radius: 12px;
1453
+ background: var(--primary-soft);
1454
+ color: var(--primary);
1455
+ border-color: transparent;
1456
+ }
1457
+
1458
+ /* Today */
1459
+ .rollover-banner {
1460
+ display: flex;
1461
+ align-items: center;
1462
+ gap: 10px;
1463
+ padding: 4px 4px 4px 14px;
1464
+ border-radius: 14px;
1465
+ background: var(--accent-ice-soft);
1466
+ color: var(--text);
1467
+ border: 1px solid var(--accent-ice);
1468
+ animation: px-in 300ms var(--px-ease) both;
1469
+ }
1470
+ .rollover-text {
1471
+ flex: 1;
1472
+ font-weight: 600;
1473
+ }
1474
+ .today-hero {
1475
+ display: flex;
1476
+ align-items: center;
1477
+ justify-content: space-between;
1478
+ gap: 12px;
1479
+ background: linear-gradient(160deg, var(--surface) 55%, var(--primary-soft) 100%);
1480
+ }
1481
+ .today-goal {
1482
+ display: grid;
1483
+ justify-items: center;
1484
+ gap: 4px;
1485
+ text-align: center;
1486
+ }
1487
+ .today-goal-text {
1488
+ font-size: 12px;
1489
+ font-weight: 700;
1490
+ color: var(--text-muted);
1491
+ }
1492
+ .goal-met-pill {
1493
+ font-size: 12px;
1494
+ font-weight: 700;
1495
+ color: var(--success);
1496
+ }
1497
+ .streak-box {
1498
+ display: flex;
1499
+ align-items: center;
1500
+ gap: 8px;
1501
+ margin-top: 8px;
1502
+ }
1503
+ .streak-flame {
1504
+ font-size: 30px;
1505
+ line-height: 1;
1506
+ display: inline-block;
1507
+ transform-origin: 50% 90%;
1508
+ }
1509
+ .streak-flame.off {
1510
+ filter: grayscale(1);
1511
+ opacity: 0.5;
1512
+ }
1513
+ .streak-flame.animated {
1514
+ animation: lc-flicker 1.6s ease-in-out infinite;
1515
+ }
1516
+ .streak-text {
1517
+ display: grid;
1518
+ line-height: 1.1;
1519
+ }
1520
+ .streak-count {
1521
+ font-size: 22px;
1522
+ color: var(--accent-orange);
1523
+ }
1524
+ .freeze-pill {
1525
+ margin-inline-start: 8px;
1526
+ padding: 4px 10px;
1527
+ border-radius: 999px;
1528
+ background: var(--accent-ice-soft);
1529
+ color: var(--text);
1530
+ font-weight: 700;
1531
+ font-size: 13px;
1532
+ }
1533
+ .practice-cta.fresh {
1534
+ background: var(--accent-violet-soft);
1535
+ border-color: var(--accent-violet);
1536
+ }
1537
+ .scenario-list {
1538
+ list-style: none;
1539
+ padding: 0;
1540
+ margin: 8px 0 0;
1541
+ display: grid;
1542
+ gap: 8px;
1543
+ }
1544
+ .scenario-card {
1545
+ display: grid;
1546
+ gap: 8px;
1547
+ padding: 12px;
1548
+ border-radius: 14px;
1549
+ border: 1px solid var(--border);
1550
+ border-inline-start: 5px solid var(--primary);
1551
+ background: var(--surface);
1552
+ }
1553
+ .scenario-card.tone-1 { border-inline-start-color: var(--accent-orange); }
1554
+ .scenario-card.tone-2 { border-inline-start-color: var(--accent-violet); }
1555
+ .scenario-theme {
1556
+ font-weight: 700;
1557
+ font-size: 15px;
1558
+ }
1559
+ .scenario-status {
1560
+ margin: 0;
1561
+ font-size: 13px;
1562
+ color: var(--success);
1563
+ }
1564
+ .scenario-status:empty {
1565
+ display: none;
1566
+ }
1567
+ .scenario-status.warn {
1568
+ color: var(--warning);
1569
+ }
1570
+
1571
+ /* Progress */
1572
+ .level-card {
1573
+ display: flex;
1574
+ gap: 14px;
1575
+ align-items: center;
1576
+ }
1577
+ .level-badge {
1578
+ flex: none;
1579
+ width: 64px;
1580
+ height: 64px;
1581
+ border-radius: 20px;
1582
+ display: grid;
1583
+ place-items: center;
1584
+ font-size: 28px;
1585
+ font-weight: 800;
1586
+ box-shadow: var(--shadow);
1587
+ }
1588
+ .level-body {
1589
+ flex: 1;
1590
+ display: grid;
1591
+ gap: 4px;
1592
+ }
1593
+ .level-kicker {
1594
+ margin: 0;
1595
+ font-size: 12px;
1596
+ text-transform: uppercase;
1597
+ letter-spacing: 0.04em;
1598
+ }
1599
+ .level-name {
1600
+ margin: 0;
1601
+ font-size: 20px;
1602
+ }
1603
+ .level-bar {
1604
+ height: 10px;
1605
+ }
1606
+ .week-chart-wrap {
1607
+ margin: 8px 0 0;
1608
+ }
1609
+ .week-chart {
1610
+ position: relative;
1611
+ display: grid;
1612
+ grid-template-columns: repeat(7, 1fr);
1613
+ gap: 6px;
1614
+ height: 140px;
1615
+ align-items: end;
1616
+ }
1617
+ .week-goal-line {
1618
+ position: absolute;
1619
+ left: 0;
1620
+ right: 0;
1621
+ border-top: 2px dashed var(--accent-orange);
1622
+ pointer-events: none;
1623
+ }
1624
+ .week-col {
1625
+ display: grid;
1626
+ grid-template-rows: auto 1fr auto;
1627
+ justify-items: center;
1628
+ height: 100%;
1629
+ gap: 2px;
1630
+ }
1631
+ .week-bar {
1632
+ align-self: end;
1633
+ width: 70%;
1634
+ border-radius: 8px 8px 3px 3px;
1635
+ background: var(--primary-soft);
1636
+ border: 1px solid var(--primary);
1637
+ }
1638
+ .week-col.met .week-bar {
1639
+ background: var(--success);
1640
+ border-color: var(--success);
1641
+ }
1642
+ .week-col.today .week-day {
1643
+ color: var(--primary);
1644
+ font-weight: 800;
1645
+ }
1646
+ .week-xp,
1647
+ .week-day {
1648
+ font-size: 11px;
1649
+ color: var(--text-muted);
1650
+ }
1651
+ .week-xp {
1652
+ font-weight: 700;
1653
+ }
1654
+ ul.ach-grid {
1655
+ list-style: none;
1656
+ margin: 0;
1657
+ padding: 0;
1658
+ }
1659
+ .ach-item {
1660
+ display: flex;
1661
+ gap: 10px;
1662
+ align-items: flex-start;
1663
+ }
1664
+ .ach-icon {
1665
+ font-size: 22px;
1666
+ }
1667
+ .ach-item:not(.unlocked) .ach-icon {
1668
+ filter: grayscale(1);
1669
+ opacity: 0.6;
1670
+ }
1671
+ .ach-title {
1672
+ font-weight: 700;
1673
+ }
1674
+ .ach-bar {
1675
+ height: 6px;
1676
+ margin: 4px 0;
1677
+ }
1678
+
1679
+ /* Daily goal picker */
1680
+ .goal-picker {
1681
+ border: none;
1682
+ margin: 0;
1683
+ padding: 0;
1684
+ display: grid;
1685
+ grid-template-columns: repeat(2, 1fr);
1686
+ gap: 8px;
1687
+ }
1688
+ .goal-picker legend {
1689
+ font-weight: 700;
1690
+ margin-bottom: 6px;
1691
+ }
1692
+ .goal-option {
1693
+ position: relative;
1694
+ display: grid;
1695
+ gap: 2px;
1696
+ padding: 10px 12px;
1697
+ min-height: 44px;
1698
+ border-radius: 14px;
1699
+ border: 2px solid var(--border);
1700
+ background: var(--surface);
1701
+ cursor: pointer;
1702
+ }
1703
+ .goal-option input {
1704
+ position: absolute;
1705
+ opacity: 0;
1706
+ inset: 0;
1707
+ margin: 0;
1708
+ cursor: pointer;
1709
+ }
1710
+ .goal-option.active {
1711
+ border-color: var(--primary);
1712
+ background: var(--primary-soft);
1713
+ }
1714
+ .goal-icon {
1715
+ font-size: 20px;
1716
+ }
1717
+ .goal-name {
1718
+ font-weight: 700;
1719
+ }
1720
+ .goal-xp {
1721
+ font-size: 12px;
1722
+ }
1723
+
1724
+ .px-perfect-bonus {
1725
+ margin: 0;
1726
+ font-weight: 800;
1727
+ color: var(--px-green, var(--success));
1728
+ }
1729
+
1730
+ @keyframes lc-flicker {
1731
+ 0%,
1732
+ 100% {
1733
+ transform: scale(1) rotate(-2deg);
1734
+ }
1735
+ 50% {
1736
+ transform: scale(1.08) rotate(2deg);
1737
+ }
1738
+ }
1739
+
1740
+ @media (prefers-reduced-motion: reduce) {
1741
+ .streak-flame.animated,
1742
+ .rollover-banner {
1743
+ animation: none;
1744
+ }
1745
+ }
1746
+
1747
+ /* ───────────── Notebook look (sticker notebook, shared with the agent dashboard) ───────────── */
1748
+ :root {
1749
+ --radius-lg: var(--lc-radius);
1750
+ --focus: #c2410c;
1751
+ }
1752
+ @media (prefers-color-scheme: dark) {
1753
+ :root {
1754
+ --focus: #fdba74;
1755
+ }
1756
+ }
1757
+
1758
+ body {
1759
+ font-family: var(--lc-sans);
1760
+ background-color: var(--lc-paper);
1761
+ background-image: repeating-linear-gradient(transparent 0 27px, var(--lc-rule) 27px 28px);
1762
+ background-attachment: local;
1763
+ }
1764
+
1765
+ /* Header and tabs sit on the paper like a notebook's top strip. */
1766
+ .panel-header,
1767
+ .tab-bar {
1768
+ background: var(--lc-paper);
1769
+ border-bottom-color: var(--lc-border);
1770
+ }
1771
+ .brand {
1772
+ background: none;
1773
+ color: var(--text);
1774
+ font: italic 400 19px/1.2 var(--lc-serif);
1775
+ letter-spacing: 0;
1776
+ }
1777
+ .pill {
1778
+ background: var(--surface);
1779
+ border: 1px solid var(--lc-border);
1780
+ }
1781
+ /* One-line tabs: they share the width and scroll sideways only when a locale's labels are too long. */
1782
+ .tab-bar {
1783
+ display: flex;
1784
+ gap: 2px;
1785
+ padding: 6px 8px;
1786
+ overflow-x: auto;
1787
+ scrollbar-width: thin;
1788
+ }
1789
+ .tab-btn {
1790
+ flex: 1 0 auto;
1791
+ padding: 8px 6px;
1792
+ white-space: nowrap;
1793
+ overflow-wrap: normal;
1794
+ }
1795
+ .tab-btn[aria-current="page"] {
1796
+ background: var(--lc-sticker);
1797
+ color: var(--sticker-ink);
1798
+ box-shadow: 0 2px 0 var(--sticker-edge);
1799
+ }
1800
+
1801
+ /* Cards: white page cut-outs with a flat paper shadow. */
1802
+ .card,
1803
+ .stat-box,
1804
+ .task-item,
1805
+ .vocab-item,
1806
+ .scenario-card,
1807
+ .ach-item,
1808
+ .goal-option,
1809
+ .px-stat,
1810
+ .px-end-stat {
1811
+ border-color: var(--lc-border);
1812
+ box-shadow: var(--lc-shadow);
1813
+ }
1814
+ .card {
1815
+ border-radius: var(--lc-radius);
1816
+ }
1817
+ .task-item,
1818
+ .vocab-item,
1819
+ .stat-box,
1820
+ .ach-item {
1821
+ border-radius: 10px;
1822
+ }
1823
+ .vocab-item {
1824
+ background: var(--surface);
1825
+ }
1826
+
1827
+ h1,
1828
+ .level-name {
1829
+ font: italic 400 24px/1.2 var(--lc-serif);
1830
+ letter-spacing: 0.01em;
1831
+ }
1832
+ .level-name {
1833
+ font-size: 22px;
1834
+ }
1835
+
1836
+ button.primary,
1837
+ .chip.active,
1838
+ .play-big {
1839
+ color: var(--on-primary);
1840
+ }
1841
+ .chip.active .chip-count {
1842
+ background: color-mix(in srgb, var(--on-primary) 22%, transparent);
1843
+ color: var(--on-primary);
1844
+ }
1845
+ button:focus-visible,
1846
+ select:focus-visible,
1847
+ input:focus-visible,
1848
+ a:focus-visible,
1849
+ .tab-btn:focus-visible,
1850
+ .goal-option:focus-within {
1851
+ outline: 3px solid var(--focus);
1852
+ outline-offset: 2px;
1853
+ }
1854
+ /* Inset so the scrolling tab bar does not clip the ring. */
1855
+ .tab-btn:focus-visible {
1856
+ outline-offset: -3px;
1857
+ }
1858
+
1859
+ /* XP bars: orange to sun-yellow. */
1860
+ .progress-fill {
1861
+ background: linear-gradient(90deg, var(--lc-xp-from), var(--lc-xp-to));
1862
+ border-radius: 999px;
1863
+ }
1864
+ .progress-bar {
1865
+ background: color-mix(in srgb, var(--lc-border) 70%, var(--surface));
1866
+ }
1867
+
1868
+ /* Today: hero page with the streak as an orange tile. */
1869
+ .today-hero {
1870
+ background: var(--surface);
1871
+ align-items: stretch;
1872
+ }
1873
+ .streak-box {
1874
+ margin-top: 10px;
1875
+ padding: 10px 12px;
1876
+ border-radius: var(--lc-radius);
1877
+ background: var(--lc-streak-tile);
1878
+ border: 1px solid var(--lc-border);
1879
+ box-shadow: var(--lc-shadow);
1880
+ }
1881
+ .streak-count {
1882
+ font-size: 26px;
1883
+ font-weight: 800;
1884
+ color: var(--text);
1885
+ }
1886
+ .freeze-pill {
1887
+ background: var(--surface);
1888
+ border: 1px solid var(--lc-border);
1889
+ }
1890
+ .today-goal {
1891
+ align-content: center;
1892
+ }
1893
+ .task-item.done {
1894
+ background: var(--lc-learned-bg);
1895
+ border-color: color-mix(in srgb, var(--lc-learned-fg) 30%, var(--lc-learned-bg));
1896
+ color: var(--lc-learned-fg);
1897
+ }
1898
+ .task-item.done .muted {
1899
+ color: var(--lc-learned-fg);
1900
+ }
1901
+ .practice-cta {
1902
+ background: linear-gradient(135deg, var(--lc-xp-from), var(--lc-xp-to));
1903
+ color: #292524;
1904
+ border-radius: var(--lc-radius);
1905
+ box-shadow: 0 3px 0 #d97706;
1906
+ }
1907
+ .practice-cta:active {
1908
+ box-shadow: 0 1px 0 #d97706;
1909
+ }
1910
+ .practice-cta.fresh {
1911
+ background: var(--lc-words-tile);
1912
+ color: var(--text);
1913
+ border: 1px solid var(--lc-border);
1914
+ box-shadow: var(--lc-shadow);
1915
+ }
1916
+ .scenario-card {
1917
+ border-inline-start-color: var(--lc-xp-from);
1918
+ }
1919
+ .scenario-card.tone-1 { border-inline-start-color: var(--lc-learned-fg); }
1920
+ .scenario-card.tone-2 { border-inline-start-color: var(--lc-xp-to); }
1921
+ .rollover-banner {
1922
+ background: var(--lc-streak-tile);
1923
+ border-color: var(--lc-border);
1924
+ box-shadow: var(--lc-shadow);
1925
+ }
1926
+
1927
+ /* Words: learned / weak chips. */
1928
+ .status-tag.learned,
1929
+ .chip-mastered:not(.active),
1930
+ .btn-know.done,
1931
+ .btn-know:disabled {
1932
+ background: var(--lc-learned-bg);
1933
+ color: var(--lc-learned-fg);
1934
+ border-color: color-mix(in srgb, var(--lc-learned-fg) 25%, var(--lc-learned-bg));
1935
+ }
1936
+ .chip-weak:not(.active) {
1937
+ background: var(--lc-weak-bg);
1938
+ color: var(--lc-weak-fg);
1939
+ border-color: color-mix(in srgb, var(--lc-weak-fg) 25%, var(--lc-weak-bg));
1940
+ }
1941
+ .btn-know {
1942
+ background: var(--surface);
1943
+ color: var(--lc-learned-fg);
1944
+ border: 1px solid var(--lc-border);
1945
+ }
1946
+ .status-tag.new {
1947
+ background: var(--lc-streak-tile);
1948
+ color: var(--primary);
1949
+ border-color: var(--lc-border);
1950
+ }
1951
+ .show-more {
1952
+ background: var(--surface);
1953
+ border: 1px dashed var(--lc-border);
1954
+ }
1955
+ .words-search {
1956
+ border-color: var(--lc-border);
1957
+ }
1958
+ .strength.s3 .strength-bar.on { background: var(--lc-xp-from); }
1959
+
1960
+ /* Progress: the level is a tilted sticker. */
1961
+ .level-badge {
1962
+ background: var(--lc-sticker);
1963
+ color: var(--sticker-ink);
1964
+ border-radius: 10px;
1965
+ transform: rotate(var(--lc-sticker-rotate));
1966
+ box-shadow: 0 3px 0 var(--sticker-edge);
1967
+ }
1968
+ .stat-box:nth-child(1) { background: var(--lc-streak-tile); }
1969
+ .stat-box:nth-child(4) { background: var(--lc-words-tile); }
1970
+ .week-bar {
1971
+ background: var(--lc-streak-tile);
1972
+ border-color: var(--lc-xp-from);
1973
+ }
1974
+ .week-col.met .week-bar {
1975
+ background: linear-gradient(0deg, var(--lc-xp-from), var(--lc-xp-to));
1976
+ border-color: var(--lc-xp-from);
1977
+ }
1978
+ .week-goal-line {
1979
+ border-top-color: var(--text-muted);
1980
+ }
1981
+ .ach-item.unlocked {
1982
+ background: var(--lc-words-tile);
1983
+ border-color: var(--lc-border);
1984
+ }
1985
+ .goal-option.active {
1986
+ border-color: var(--primary);
1987
+ background: var(--lc-streak-tile);
1988
+ }