@greenblacksky/honmoon 0.0.0-stage → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +85 -2
  3. package/package.json +58 -4
  4. package/src/lib/audio/context.ts +29 -0
  5. package/src/lib/audio/metronome.svelte.ts +146 -0
  6. package/src/lib/audio/mic.ts +254 -0
  7. package/src/lib/audio/pitch.ts +133 -0
  8. package/src/lib/audio/rhythmplay.ts +152 -0
  9. package/src/lib/audio/synth.ts +409 -0
  10. package/src/lib/audio/voiceplay.ts +60 -0
  11. package/src/lib/drills/defs.ts +459 -0
  12. package/src/lib/drills/questions.ts +920 -0
  13. package/src/lib/drills/registry.svelte.ts +167 -0
  14. package/src/lib/drills/runtime.svelte.ts +629 -0
  15. package/src/lib/drills/verdict.ts +241 -0
  16. package/src/lib/index.ts +117 -0
  17. package/src/lib/learn/describe.ts +72 -0
  18. package/src/lib/learn/pools.ts +44 -0
  19. package/src/lib/learn/progress.ts +100 -0
  20. package/src/lib/learn/session.ts +304 -0
  21. package/src/lib/music/bass.ts +139 -0
  22. package/src/lib/music/clefs.ts +218 -0
  23. package/src/lib/music/intervals.ts +94 -0
  24. package/src/lib/music/notes.ts +133 -0
  25. package/src/lib/music/rhythm.ts +624 -0
  26. package/src/lib/music/scales.ts +560 -0
  27. package/src/lib/music/voice.ts +227 -0
  28. package/src/lib/music/warmups.ts +318 -0
  29. package/src/lib/nav.svelte.ts +53 -0
  30. package/src/lib/notation/fretboard.ts +187 -0
  31. package/src/lib/notation/glyphdata.ts +101 -0
  32. package/src/lib/notation/glyphs.ts +71 -0
  33. package/src/lib/notation/icons.ts +12 -0
  34. package/src/lib/notation/keyboard.ts +166 -0
  35. package/src/lib/notation/rhythmsvg.ts +341 -0
  36. package/src/lib/notation/staff.ts +288 -0
  37. package/src/lib/notation/svg.ts +37 -0
  38. package/src/lib/notation/tab.ts +80 -0
  39. package/src/lib/store/honmoon.svelte.ts +329 -0
  40. package/src/lib/store/normalize.ts +23 -0
  41. package/src/lib/store/sync.ts +43 -0
  42. package/src/lib/store/types.ts +101 -0
  43. package/src/lib/styles/honmoon.css +2064 -0
  44. package/src/lib/ui/AccRow.svelte +23 -0
  45. package/src/lib/ui/BackBar.svelte +23 -0
  46. package/src/lib/ui/BassRow.svelte +30 -0
  47. package/src/lib/ui/Chips.svelte +23 -0
  48. package/src/lib/ui/DataCard.svelte +66 -0
  49. package/src/lib/ui/Fretboard.svelte +71 -0
  50. package/src/lib/ui/Keyboard.svelte +54 -0
  51. package/src/lib/ui/MetronomePanel.svelte +101 -0
  52. package/src/lib/ui/RefBar.svelte +28 -0
  53. package/src/lib/ui/RhythmBar.svelte +9 -0
  54. package/src/lib/ui/Shell.svelte +96 -0
  55. package/src/lib/ui/StaffRun.svelte +46 -0
  56. package/src/lib/ui/Stat.svelte +13 -0
  57. package/src/lib/ui/actions.ts +36 -0
  58. package/src/lib/ui/keyfollow.svelte.ts +48 -0
  59. package/src/lib/ui/overlay.ts +15 -0
  60. package/src/lib/ui/refdata.ts +355 -0
  61. package/src/lib/ui/scaleboard.ts +420 -0
  62. package/src/lib/ui/screens/DegreesRef.svelte +405 -0
  63. package/src/lib/ui/screens/DrillScreen.svelte +1155 -0
  64. package/src/lib/ui/screens/Explorer.svelte +245 -0
  65. package/src/lib/ui/screens/FamilyScreen.svelte +280 -0
  66. package/src/lib/ui/screens/Glossary.svelte +65 -0
  67. package/src/lib/ui/screens/IntervalRef.svelte +37 -0
  68. package/src/lib/ui/screens/LearnIndex.svelte +44 -0
  69. package/src/lib/ui/screens/MetronomeScreen.svelte +17 -0
  70. package/src/lib/ui/screens/PracticeHome.svelte +159 -0
  71. package/src/lib/ui/screens/RhythmRef.svelte +239 -0
  72. package/src/lib/ui/screens/ScalesScreen.svelte +210 -0
  73. package/src/lib/ui/screens/SessionDone.svelte +119 -0
  74. package/src/lib/ui/screens/SetupScreen.svelte +548 -0
  75. package/src/lib/ui/screens/StaffMap.svelte +230 -0
  76. package/src/lib/ui/screens/StatsScreen.svelte +478 -0
  77. package/src/lib/ui/screens/TapCalibration.svelte +186 -0
  78. package/src/lib/ui/tapfix.ts +93 -0
  79. package/src/lib/ui/types.ts +40 -0
  80. package/src/lib/util/list.ts +4 -0
  81. package/src/lib/util/random.ts +23 -0
  82. package/src/lib/util/text.ts +50 -0
@@ -0,0 +1,2064 @@
1
+ /* Honmoon's look: one stylesheet
2
+ shared by all three apps. Colours are tokens on :root, with a dark set
3
+ that follows the device unless Setup forces one or the other
4
+ (data-theme on <html>). Fonts are loaded by each app's index.html. */
5
+ :root {
6
+ box-sizing: border-box;
7
+ padding-top: env(safe-area-inset-top, 0px);
8
+ padding-bottom: env(safe-area-inset-bottom, 0px);
9
+ }
10
+ html {
11
+ scroll-padding-top: env(safe-area-inset-top, 0px);
12
+ }
13
+ img {
14
+ max-width: 100%;
15
+ }
16
+ [hidden]:not([hidden='until-found' i]) {
17
+ display: none !important;
18
+ }
19
+ /* The palette. Everything an app draws takes its colour from these tokens.
20
+ The base is shared by all three apps; the accent is each app's own, set
21
+ once in its src/theme.css through the six --accent-*-l / -d tokens below
22
+ (light and dark). Progress colours (--good, --mid, --bad) are the same in
23
+ every app on purpose, so "getting there" always looks the same. */
24
+ :root {
25
+ /* accent hook: an app overrides these; the defaults are Honmoon gold */
26
+ --accent-l: #8a5d0a;
27
+ --accent-ink-l: #ffffff;
28
+ --accent-soft-l: #f1e3c4;
29
+ --accent-d: #ebb045;
30
+ --accent-ink-d: #1a1205;
31
+ --accent-soft-d: #382a1a;
32
+
33
+ --dot: #eee7f5;
34
+ --dot-ink: #1a1524;
35
+ --pk-w: #fcfbfe;
36
+ --pk-b: #262033;
37
+ --pk-line: #9a90ac;
38
+ --pk-w-ink: #574d68;
39
+ --pk-b-ink: #e6e0ef;
40
+ --pk-lit-ink: #fbfafd;
41
+ --bg: #eeebf2;
42
+ --surface: #fbfafd;
43
+ --raised: #ffffff;
44
+ --line: #dcd6e5;
45
+ --line-2: #c4bbd2;
46
+ --ink: #1a1524;
47
+ --ink-2: #574d68;
48
+ --ink-3: #7f7491;
49
+ --accent: var(--accent-l);
50
+ --accent-ink: var(--accent-ink-l);
51
+ --accent-soft: var(--accent-soft-l);
52
+ --good: #0b6e65;
53
+ --good-soft: #d3eeea;
54
+ --mid: #8a5d0a;
55
+ --bad: #b23a0a;
56
+ --bad-soft: #f9e0d3;
57
+ --staff: #1a1524;
58
+ --staff-line: #403850;
59
+ --board: #2e2240;
60
+ --board-2: #3b2c50;
61
+ --wire: #b4a9c6;
62
+ --string: #ccc3da;
63
+ --r: 10px;
64
+ --r-lg: 16px;
65
+ --f-disp: 'Saira Condensed', 'Arial Narrow', sans-serif;
66
+ --f-body: 'IBM Plex Sans', 'Segoe UI', system-ui, sans-serif;
67
+ --f-mono: 'IBM Plex Mono', ui-monospace, Menlo, monospace;
68
+ }
69
+ @media (prefers-color-scheme: dark) {
70
+ :root:not([data-theme='light']) {
71
+ --bg: #120f1a;
72
+ --surface: #1a1624;
73
+ --raised: #241e31;
74
+ --line: #322a42;
75
+ --line-2: #453a58;
76
+ --ink: #f1ecf7;
77
+ --ink-2: #b3a8c4;
78
+ --ink-3: #82779a;
79
+ --accent: var(--accent-d);
80
+ --accent-ink: var(--accent-ink-d);
81
+ --accent-soft: var(--accent-soft-d);
82
+ --good: #52cfb4;
83
+ --good-soft: #14302b;
84
+ --mid: #e8b04a;
85
+ --bad: #ff8466;
86
+ --bad-soft: #3d1f1c;
87
+ --pk-w: #c9c1d6;
88
+ --pk-b: #120f1a;
89
+ --pk-line: #6a5e80;
90
+ --pk-lit-ink: #120f1a;
91
+ --staff: #dcd4e8;
92
+ --staff-line: #8a809c;
93
+ --board: #241a2e;
94
+ --board-2: #2f2340;
95
+ --wire: #6a5e80;
96
+ --string: #8e83a3;
97
+ }
98
+ }
99
+ :root[data-theme='dark'] {
100
+ --bg: #120f1a;
101
+ --surface: #1a1624;
102
+ --raised: #241e31;
103
+ --line: #322a42;
104
+ --line-2: #453a58;
105
+ --ink: #f1ecf7;
106
+ --ink-2: #b3a8c4;
107
+ --ink-3: #82779a;
108
+ --accent: var(--accent-d);
109
+ --accent-ink: var(--accent-ink-d);
110
+ --accent-soft: var(--accent-soft-d);
111
+ --good: #52cfb4;
112
+ --good-soft: #14302b;
113
+ --mid: #e8b04a;
114
+ --bad: #ff8466;
115
+ --bad-soft: #3d1f1c;
116
+ --pk-w: #c9c1d6;
117
+ --pk-b: #120f1a;
118
+ --pk-line: #6a5e80;
119
+ --pk-lit-ink: #120f1a;
120
+ --staff: #dcd4e8;
121
+ --staff-line: #8a809c;
122
+ --board: #241a2e;
123
+ --board-2: #2f2340;
124
+ --wire: #6a5e80;
125
+ --string: #8e83a3;
126
+ }
127
+ * {
128
+ box-sizing: border-box;
129
+ }
130
+ html,
131
+ body {
132
+ height: 100%;
133
+ }
134
+ body {
135
+ margin: 0;
136
+ background: var(--bg);
137
+ color: var(--ink);
138
+ font-family: var(--f-body);
139
+ font-size: 15px;
140
+ line-height: 1.5;
141
+ -webkit-text-size-adjust: 100%;
142
+ }
143
+ button {
144
+ font: inherit;
145
+ color: inherit;
146
+ background: none;
147
+ border: none;
148
+ cursor: pointer;
149
+ }
150
+ :focus-visible {
151
+ outline: 2px solid var(--accent);
152
+ outline-offset: 2px;
153
+ border-radius: 4px;
154
+ }
155
+
156
+ #app {
157
+ height: 100%;
158
+ display: grid;
159
+ grid-template-rows: auto minmax(0, 1fr);
160
+ max-width: 860px;
161
+ margin: 0 auto;
162
+ }
163
+ header {
164
+ display: flex;
165
+ align-items: center;
166
+ gap: 10px;
167
+ padding: 10px 16px;
168
+ border-bottom: 1px solid var(--line);
169
+ background: var(--surface);
170
+ position: sticky;
171
+ top: env(safe-area-inset-top, 0px);
172
+ z-index: 5;
173
+ }
174
+ .brand {
175
+ font-family: var(--f-disp);
176
+ font-weight: 700;
177
+ font-size: 19px;
178
+ letter-spacing: 0.04em;
179
+ text-transform: uppercase;
180
+ margin-right: auto;
181
+ display: flex;
182
+ align-items: center;
183
+ gap: 9px;
184
+ min-width: 0;
185
+ white-space: nowrap;
186
+ overflow: hidden;
187
+ text-overflow: ellipsis;
188
+ }
189
+ /* the brand mark: the app's own colour, shading into all four Honmoon colours */
190
+ .brand .mk {
191
+ width: 10px;
192
+ height: 22px;
193
+ border-radius: 2px;
194
+ background: linear-gradient(var(--accent) 0 42%, #b88cff 62%, #4db2ff 76%, #ff5fa2 89%, #ebb045);
195
+ flex: none;
196
+ }
197
+ /* an app that brings its own mark (its weapon) shows that instead, in its accent */
198
+ .brand .mk.icon {
199
+ background: none;
200
+ border-radius: 0;
201
+ width: 26px;
202
+ height: 26px;
203
+ color: var(--accent);
204
+ }
205
+ .brand:has(.mk.icon) {
206
+ min-width: 26px;
207
+ } /* the name may give way to the tabs; the mark never does */
208
+ .brand .mk.icon svg {
209
+ display: block;
210
+ width: 100%;
211
+ height: 100%;
212
+ }
213
+ .hbtn {
214
+ font-family: var(--f-disp);
215
+ font-size: 14px;
216
+ letter-spacing: 0.07em;
217
+ text-transform: uppercase;
218
+ color: var(--ink-2);
219
+ padding: 6px 9px;
220
+ border-radius: 7px;
221
+ flex: none;
222
+ }
223
+ .hbtn[aria-current='true'] {
224
+ color: var(--accent);
225
+ background: var(--accent-soft);
226
+ }
227
+ .metchip {
228
+ color: var(--accent);
229
+ border: 1px solid var(--accent);
230
+ font-family: var(--f-mono);
231
+ font-size: 12px;
232
+ text-transform: none;
233
+ letter-spacing: 0;
234
+ padding: 4px 8px;
235
+ }
236
+ main {
237
+ overflow-y: auto;
238
+ overflow-x: hidden;
239
+ padding: 18px 16px 40px;
240
+ display: flex;
241
+ flex-direction: column;
242
+ gap: 18px;
243
+ }
244
+ /* main is a scrolling column of flex items. A child that is itself a scroll
245
+ container has an automatic minimum size of zero, so once the page is taller
246
+ than the screen the flex algorithm is free to shrink one to nothing — which
247
+ is how a staff or a fretboard can vanish on a long screen. Nothing here
248
+ should ever shrink; the column scrolls instead. */
249
+ main > * {
250
+ flex: none;
251
+ }
252
+ #app > header,
253
+ #app > main {
254
+ min-width: 0;
255
+ }
256
+ header {
257
+ overflow: hidden;
258
+ }
259
+
260
+ h2 {
261
+ font-family: var(--f-disp);
262
+ font-size: 13px;
263
+ letter-spacing: 0.12em;
264
+ text-transform: uppercase;
265
+ color: var(--ink-3);
266
+ margin: 0;
267
+ font-weight: 600;
268
+ }
269
+ .lead {
270
+ margin: 0;
271
+ color: var(--ink-2);
272
+ max-width: 62ch;
273
+ }
274
+ .card {
275
+ background: var(--surface);
276
+ border: 1px solid var(--line);
277
+ border-radius: var(--r-lg);
278
+ padding: 16px;
279
+ }
280
+ .stack {
281
+ display: flex;
282
+ flex-direction: column;
283
+ gap: 12px;
284
+ }
285
+ .sec {
286
+ display: flex;
287
+ flex-direction: column;
288
+ gap: 10px;
289
+ }
290
+
291
+ /* home */
292
+ .drills {
293
+ display: grid;
294
+ grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
295
+ gap: 10px;
296
+ }
297
+ .fold {
298
+ margin-top: 10px;
299
+ background: var(--surface);
300
+ border: 1px solid var(--line);
301
+ border-radius: var(--r);
302
+ padding: 10px 14px;
303
+ }
304
+ .foldhead {
305
+ display: flex;
306
+ width: 100%;
307
+ align-items: center;
308
+ gap: 8px;
309
+ background: none;
310
+ border: none;
311
+ padding: 2px 0;
312
+ font-size: 13px;
313
+ letter-spacing: 0.04em;
314
+ color: var(--ink-2);
315
+ text-align: left;
316
+ }
317
+ .foldhead .caret {
318
+ margin-left: auto;
319
+ color: var(--ink-3);
320
+ }
321
+ .drill.muted {
322
+ opacity: 0.62;
323
+ }
324
+ .offtag {
325
+ font-style: normal;
326
+ font-size: 11px;
327
+ letter-spacing: 0.04em;
328
+ color: var(--ink-3);
329
+ border: 1px solid var(--line-2);
330
+ border-radius: 5px;
331
+ padding: 1px 5px;
332
+ margin-left: 6px;
333
+ white-space: nowrap;
334
+ }
335
+ .drill {
336
+ display: grid;
337
+ grid-template-columns: auto 1fr auto;
338
+ gap: 12px;
339
+ align-items: center;
340
+ text-align: left;
341
+ background: var(--surface);
342
+ border: 1px solid var(--line);
343
+ border-radius: var(--r);
344
+ padding: 13px 14px;
345
+ transition:
346
+ border-color 0.12s,
347
+ transform 0.12s;
348
+ }
349
+ .steprow {
350
+ display: flex;
351
+ align-items: center;
352
+ gap: 2px;
353
+ padding: 2px 0;
354
+ }
355
+ .steprow .sn {
356
+ flex: none;
357
+ display: flex;
358
+ flex-direction: column;
359
+ align-items: center;
360
+ justify-content: center;
361
+ min-width: 46px;
362
+ padding: 7px 4px;
363
+ border-radius: 9px;
364
+ background: var(--surface);
365
+ border: 1px solid var(--line);
366
+ }
367
+ .steprow .sn.root {
368
+ background: var(--accent-soft);
369
+ border-color: var(--accent);
370
+ }
371
+ .steprow .sn b {
372
+ font-family: var(--f-mono);
373
+ font-size: 16px;
374
+ font-weight: 600;
375
+ line-height: 1.1;
376
+ }
377
+ .steprow .sn i {
378
+ font-family: var(--f-mono);
379
+ font-style: normal;
380
+ font-size: 11px;
381
+ color: var(--ink-3);
382
+ margin-top: 2px;
383
+ }
384
+ .steprow .sn.root i {
385
+ color: var(--accent);
386
+ }
387
+ .steprow .sg {
388
+ flex: none;
389
+ display: flex;
390
+ flex-direction: column;
391
+ align-items: center;
392
+ justify-content: center;
393
+ min-width: 34px;
394
+ padding: 0 3px;
395
+ color: var(--ink-3);
396
+ }
397
+ .steprow .sg b {
398
+ font-family: var(--f-disp);
399
+ font-size: 15px;
400
+ line-height: 1;
401
+ color: var(--ink-2);
402
+ border: 1px dashed var(--line-2);
403
+ border-radius: 999px;
404
+ width: 22px;
405
+ height: 22px;
406
+ display: grid;
407
+ place-items: center;
408
+ }
409
+ .steprow .sg i {
410
+ font-style: normal;
411
+ font-size: 9.5px;
412
+ letter-spacing: 0.02em;
413
+ margin-top: 3px;
414
+ white-space: nowrap;
415
+ }
416
+ .fams {
417
+ display: flex;
418
+ flex-direction: column;
419
+ gap: 9px;
420
+ }
421
+ .fam {
422
+ display: grid;
423
+ grid-template-columns: auto 1fr auto;
424
+ gap: 13px;
425
+ align-items: center;
426
+ text-align: left;
427
+ background: var(--raised);
428
+ border: 1px solid var(--line-2);
429
+ border-radius: var(--r);
430
+ padding: 15px 15px;
431
+ transition:
432
+ border-color 0.12s,
433
+ transform 0.12s;
434
+ }
435
+ .fam:active {
436
+ transform: scale(0.99);
437
+ }
438
+ .fam:hover {
439
+ border-color: var(--accent);
440
+ }
441
+ .fam .ic {
442
+ width: 38px;
443
+ height: 38px;
444
+ display: grid;
445
+ place-items: center;
446
+ border-radius: 9px;
447
+ background: var(--accent-soft);
448
+ color: var(--accent);
449
+ flex: none;
450
+ }
451
+ .fam .ic svg {
452
+ width: 20px;
453
+ height: 20px;
454
+ display: block;
455
+ }
456
+ .fam .txt {
457
+ display: block;
458
+ min-width: 0;
459
+ }
460
+ .fam .nm {
461
+ display: block;
462
+ font-family: var(--f-disp);
463
+ font-size: 20px;
464
+ letter-spacing: 0.02em;
465
+ line-height: 1.15;
466
+ }
467
+ .fam .sb {
468
+ display: block;
469
+ font-size: 13px;
470
+ color: var(--ink-2);
471
+ line-height: 1.45;
472
+ margin-top: 3px;
473
+ }
474
+ .fam .meta {
475
+ display: block;
476
+ font-family: var(--f-mono);
477
+ font-size: 11.5px;
478
+ color: var(--ink-3);
479
+ margin-top: 5px;
480
+ letter-spacing: 0.03em;
481
+ }
482
+ .fam .mast {
483
+ font-size: 19px;
484
+ color: var(--ink-3);
485
+ }
486
+ .drill:hover {
487
+ border-color: var(--accent);
488
+ }
489
+ .drill:active {
490
+ transform: scale(0.99);
491
+ }
492
+ .drill .ic {
493
+ width: 34px;
494
+ height: 34px;
495
+ display: grid;
496
+ place-items: center;
497
+ border-radius: 8px;
498
+ background: var(--accent-soft);
499
+ color: var(--accent);
500
+ flex: none;
501
+ }
502
+ .drill .ic svg {
503
+ width: 18px;
504
+ height: 18px;
505
+ display: block;
506
+ }
507
+ .drill.ref {
508
+ border-style: dashed;
509
+ }
510
+ .drill.ref .ic {
511
+ background: var(--line);
512
+ color: var(--ink-2);
513
+ }
514
+ .drill.ref .mast {
515
+ font-size: 16px;
516
+ color: var(--ink-3);
517
+ }
518
+ .drill .nm {
519
+ font-family: var(--f-disp);
520
+ font-size: 18px;
521
+ letter-spacing: 0.02em;
522
+ line-height: 1.15;
523
+ }
524
+ .drill .sb {
525
+ font-size: 12.5px;
526
+ color: var(--ink-3);
527
+ line-height: 1.3;
528
+ }
529
+ .drill .mast {
530
+ font-family: var(--f-mono);
531
+ font-size: 12px;
532
+ color: var(--ink-3);
533
+ font-variant-numeric: tabular-nums;
534
+ }
535
+
536
+ .levels {
537
+ display: flex;
538
+ gap: 6px;
539
+ flex-wrap: wrap;
540
+ }
541
+ .chip {
542
+ font-family: var(--f-disp);
543
+ font-size: 14px;
544
+ letter-spacing: 0.05em;
545
+ text-transform: uppercase;
546
+ padding: 7px 12px;
547
+ border: 1px solid var(--line-2);
548
+ border-radius: 99px;
549
+ color: var(--ink-2);
550
+ background: var(--surface);
551
+ }
552
+ .chip[aria-pressed='true'] {
553
+ background: var(--accent);
554
+ border-color: var(--accent);
555
+ color: var(--accent-ink);
556
+ }
557
+ .chip.sm {
558
+ font-size: 12.5px;
559
+ padding: 5px 10px;
560
+ }
561
+ .chip.tog[aria-pressed='true'] {
562
+ background: var(--accent-soft);
563
+ border-color: var(--accent);
564
+ color: var(--accent);
565
+ }
566
+ .chip.tog[aria-pressed='false'] {
567
+ opacity: 0.5;
568
+ text-decoration: line-through;
569
+ }
570
+ .tag {
571
+ font-family: var(--f-disp);
572
+ font-size: 11.5px;
573
+ letter-spacing: 0.09em;
574
+ text-transform: uppercase;
575
+ color: var(--accent);
576
+ background: var(--accent-soft);
577
+ border: 1px solid var(--accent);
578
+ border-radius: 99px;
579
+ padding: 2px 8px;
580
+ }
581
+
582
+ .sesscard {
583
+ border: 1.5px solid var(--accent);
584
+ }
585
+ .start {
586
+ background: var(--accent);
587
+ color: var(--accent-ink);
588
+ font-family: var(--f-disp);
589
+ font-size: 18px;
590
+ letter-spacing: 0.07em;
591
+ text-transform: uppercase;
592
+ padding: 13px 20px;
593
+ border-radius: 10px;
594
+ width: 100%;
595
+ transition: transform 0.1s;
596
+ }
597
+ .start:active {
598
+ transform: scale(0.99);
599
+ }
600
+ .met .metrow {
601
+ display: flex;
602
+ align-items: center;
603
+ gap: 10px;
604
+ flex-wrap: wrap;
605
+ }
606
+ .mbtn {
607
+ width: 38px;
608
+ height: 38px;
609
+ border: 1px solid var(--line-2);
610
+ border-radius: 9px;
611
+ font-family: var(--f-mono);
612
+ font-size: 20px;
613
+ line-height: 1;
614
+ display: grid;
615
+ place-items: center;
616
+ flex: none;
617
+ }
618
+ .mbtn:active {
619
+ background: var(--accent-soft);
620
+ border-color: var(--accent);
621
+ }
622
+ .bpm {
623
+ display: flex;
624
+ align-items: baseline;
625
+ gap: 5px;
626
+ font-family: var(--f-mono);
627
+ font-variant-numeric: tabular-nums;
628
+ }
629
+ .bpm .v {
630
+ font-size: 30px;
631
+ font-weight: 600;
632
+ min-width: 2.6ch;
633
+ text-align: center;
634
+ }
635
+ .bpm .u {
636
+ font-size: 12px;
637
+ color: var(--ink-3);
638
+ }
639
+ .metdot {
640
+ width: 15px;
641
+ height: 15px;
642
+ border-radius: 50%;
643
+ background: var(--line-2);
644
+ flex: none;
645
+ transition: none;
646
+ }
647
+ .metdot.hit {
648
+ background: var(--accent);
649
+ }
650
+ .metdot.hit.one {
651
+ background: var(--good);
652
+ box-shadow: 0 0 0 5px color-mix(in srgb, var(--good) 28%, transparent);
653
+ }
654
+ .slider {
655
+ width: 100%;
656
+ accent-color: var(--accent);
657
+ height: 26px;
658
+ }
659
+ /* scale-degree reference */
660
+ .steponly {
661
+ display: flex;
662
+ align-items: flex-start;
663
+ justify-content: center;
664
+ gap: 3px;
665
+ flex-wrap: nowrap;
666
+ margin: 6px 0;
667
+ }
668
+ .steponly .se {
669
+ flex: none;
670
+ font-family: var(--f-mono);
671
+ font-size: 13px;
672
+ font-weight: 600;
673
+ color: var(--accent);
674
+ border: 1px solid var(--accent);
675
+ background: var(--accent-soft);
676
+ border-radius: 6px;
677
+ width: 22px;
678
+ height: 26px;
679
+ line-height: 26px;
680
+ text-align: center;
681
+ }
682
+ .steponly .sg {
683
+ flex: 1 1 0;
684
+ min-width: 0;
685
+ display: flex;
686
+ flex-direction: column;
687
+ align-items: center;
688
+ color: var(--ink-3);
689
+ }
690
+ .steponly .sg b {
691
+ font-family: var(--f-disp);
692
+ font-size: 17px;
693
+ line-height: 1;
694
+ color: var(--ink);
695
+ border: 1px solid var(--line-2);
696
+ border-radius: 999px;
697
+ width: 28px;
698
+ height: 28px;
699
+ display: grid;
700
+ place-items: center;
701
+ }
702
+ .steponly .sg i {
703
+ font-style: normal;
704
+ font-size: 9.5px;
705
+ margin-top: 4px;
706
+ white-space: nowrap;
707
+ }
708
+ .sopt {
709
+ flex-direction: column;
710
+ align-items: stretch;
711
+ gap: 4px;
712
+ padding: 8px 10px;
713
+ }
714
+ .sopt .rn {
715
+ align-self: flex-start;
716
+ }
717
+ .sopt .dfit svg {
718
+ max-width: 100%;
719
+ }
720
+ .sopt .sname {
721
+ font-size: 13px;
722
+ color: var(--ink-2);
723
+ text-align: center;
724
+ }
725
+ .dfit svg {
726
+ display: block;
727
+ width: 100%;
728
+ height: auto;
729
+ max-width: 440px;
730
+ margin: 0 auto;
731
+ }
732
+ .druler {
733
+ display: grid;
734
+ grid-template-columns: repeat(12, minmax(0, 1fr));
735
+ gap: 3px;
736
+ }
737
+ .druler .dr {
738
+ display: flex;
739
+ flex-direction: column;
740
+ align-items: center;
741
+ justify-content: center;
742
+ min-height: 44px;
743
+ border-radius: 6px;
744
+ border: 1px solid var(--line);
745
+ background: var(--surface);
746
+ padding: 3px 0;
747
+ }
748
+ .druler .dr b {
749
+ font-family: var(--f-mono);
750
+ font-size: 12px;
751
+ line-height: 1.15;
752
+ text-align: center;
753
+ font-weight: 600;
754
+ }
755
+ .druler .dr i {
756
+ font-style: normal;
757
+ font-family: var(--f-mono);
758
+ font-size: 9.5px;
759
+ color: var(--ink-3);
760
+ margin-top: 3px;
761
+ }
762
+ .druler .dr.maj b {
763
+ color: var(--ink);
764
+ }
765
+ .druler .dr.alt {
766
+ background: transparent;
767
+ border-style: dashed;
768
+ }
769
+ .druler .dr.alt b {
770
+ color: var(--ink-2);
771
+ font-weight: 500;
772
+ font-size: 11.5px;
773
+ }
774
+ .druler .dr.mark {
775
+ border-color: var(--good);
776
+ border-style: solid;
777
+ background: var(--good-soft);
778
+ }
779
+ .druler .dr.mark b {
780
+ color: var(--ink);
781
+ }
782
+ .druler.compact .dr {
783
+ min-height: 30px;
784
+ background: transparent;
785
+ border: 1px dashed var(--line);
786
+ }
787
+ .druler.compact .dr.on {
788
+ border-style: solid;
789
+ }
790
+ .druler.compact .dr.on.maj {
791
+ background: var(--raised);
792
+ border-color: var(--line-2);
793
+ }
794
+ .druler.compact .dr.on.alt {
795
+ background: var(--good-soft);
796
+ border-color: var(--good);
797
+ }
798
+ .druler.compact .dr.on b {
799
+ color: var(--ink);
800
+ font-size: 11px;
801
+ font-weight: 600;
802
+ }
803
+ .drow {
804
+ display: flex;
805
+ flex-direction: column;
806
+ gap: 5px;
807
+ padding: 4px 0;
808
+ }
809
+ .drn {
810
+ display: flex;
811
+ align-items: baseline;
812
+ gap: 8px;
813
+ flex-wrap: wrap;
814
+ }
815
+ .drn b {
816
+ font-size: 14px;
817
+ color: var(--ink);
818
+ }
819
+ .drn span {
820
+ font-size: 12.5px;
821
+ color: var(--ink-3);
822
+ }
823
+ .dtab {
824
+ border-collapse: collapse;
825
+ font-family: var(--f-mono);
826
+ font-size: 13.5px;
827
+ margin: 2px 0;
828
+ }
829
+ .dtab th,
830
+ .dtab td {
831
+ padding: 6px 7px;
832
+ text-align: center;
833
+ border-bottom: 1px solid var(--line);
834
+ white-space: nowrap;
835
+ }
836
+ .dtab tr:first-child th {
837
+ color: var(--ink-3);
838
+ font-weight: 500;
839
+ font-size: 12.5px;
840
+ }
841
+ .dtab th:first-child {
842
+ text-align: left;
843
+ color: var(--ink-2);
844
+ font-weight: 600;
845
+ padding-left: 0;
846
+ font-family: var(--f-body);
847
+ font-size: 13px;
848
+ }
849
+ .dtab .hot {
850
+ background: var(--accent-soft);
851
+ color: var(--accent);
852
+ font-weight: 700;
853
+ }
854
+ .dtab tr:first-child th.hot {
855
+ color: var(--accent);
856
+ }
857
+ .dtab .corner {
858
+ font-family: var(--f-body);
859
+ font-size: 11px;
860
+ letter-spacing: 0.05em;
861
+ text-transform: uppercase;
862
+ color: var(--ink-3);
863
+ font-weight: 500;
864
+ }
865
+ .dform {
866
+ display: flex;
867
+ flex-direction: column;
868
+ gap: 3px;
869
+ padding: 10px 12px;
870
+ border-radius: 9px;
871
+ background: var(--raised);
872
+ border: 1px solid var(--line);
873
+ }
874
+ .dform b {
875
+ font-size: 13px;
876
+ color: var(--ink-3);
877
+ font-weight: 500;
878
+ }
879
+ .dform span {
880
+ font-family: var(--f-mono);
881
+ font-size: 19px;
882
+ letter-spacing: 0.06em;
883
+ color: var(--ink);
884
+ }
885
+ .dq {
886
+ border: 1px solid var(--line);
887
+ border-radius: 9px;
888
+ background: var(--raised);
889
+ padding: 0;
890
+ }
891
+ .dq > summary {
892
+ cursor: pointer;
893
+ list-style: none;
894
+ padding: 11px 13px;
895
+ font-size: 14.5px;
896
+ line-height: 1.45;
897
+ color: var(--ink);
898
+ }
899
+ .dq > summary::-webkit-details-marker {
900
+ display: none;
901
+ }
902
+ .dq > summary::after {
903
+ content: ' tap to check';
904
+ font-size: 11px;
905
+ letter-spacing: 0.04em;
906
+ color: var(--ink-3);
907
+ }
908
+ .dq[open] > summary::after {
909
+ content: '';
910
+ }
911
+ .dq > div {
912
+ padding: 0 13px 12px;
913
+ border-top: 1px dashed var(--line);
914
+ }
915
+ .dq > div b {
916
+ display: block;
917
+ font-family: var(--f-mono);
918
+ font-size: 18px;
919
+ color: var(--good);
920
+ margin-top: 9px;
921
+ }
922
+ .dq > div p {
923
+ margin: 4px 0 0;
924
+ font-size: 13.5px;
925
+ line-height: 1.5;
926
+ color: var(--ink-2);
927
+ }
928
+ .dtabs {
929
+ gap: 6px;
930
+ }
931
+ .gfind {
932
+ width: 100%;
933
+ box-sizing: border-box;
934
+ font: inherit;
935
+ font-size: 15px;
936
+ padding: 10px 12px;
937
+ border-radius: 9px;
938
+ border: 1px solid var(--line-2);
939
+ background: var(--bg);
940
+ color: var(--ink);
941
+ }
942
+ .gfind:focus {
943
+ outline: none;
944
+ border-color: var(--accent);
945
+ }
946
+ .gterm {
947
+ padding: 2px 0;
948
+ }
949
+ .gterm b {
950
+ font-family: var(--f-mono);
951
+ font-size: 14px;
952
+ font-weight: 600;
953
+ color: var(--ink);
954
+ }
955
+ .gterm p {
956
+ margin: 3px 0 0;
957
+ font-size: 14px;
958
+ line-height: 1.5;
959
+ color: var(--ink-2);
960
+ }
961
+ .gterm i {
962
+ color: var(--ink-2);
963
+ }
964
+ .vphase {
965
+ font-size: 13px;
966
+ letter-spacing: 0.04em;
967
+ padding: 8px 10px;
968
+ border-radius: 8px;
969
+ border: 1px solid var(--line);
970
+ background: var(--raised);
971
+ color: var(--ink-2);
972
+ text-align: center;
973
+ transition:
974
+ background 0.18s,
975
+ color 0.18s,
976
+ border-color 0.18s;
977
+ }
978
+ .vphase.cue {
979
+ background: var(--accent-soft);
980
+ border-color: var(--accent);
981
+ color: var(--accent);
982
+ }
983
+ .vphase.sing {
984
+ background: var(--good-soft);
985
+ border-color: var(--good);
986
+ color: var(--ink);
987
+ }
988
+ .vphase.play,
989
+ .vphase.rest {
990
+ background: var(--raised);
991
+ color: var(--ink-2);
992
+ }
993
+ .keys3 {
994
+ display: grid;
995
+ grid-template-columns: repeat(3, 1fr);
996
+ gap: 9px;
997
+ }
998
+ .keys3 .key {
999
+ font-family: var(--f-disp);
1000
+ font-size: 17px;
1001
+ letter-spacing: 0.04em;
1002
+ text-transform: uppercase;
1003
+ min-height: 56px;
1004
+ }
1005
+ .key.v-ok {
1006
+ border-color: var(--good);
1007
+ color: var(--good);
1008
+ }
1009
+ .key.v-mid {
1010
+ border-color: var(--line-2);
1011
+ }
1012
+ .key.v-no {
1013
+ border-color: var(--bad);
1014
+ color: var(--bad);
1015
+ }
1016
+ .rbar {
1017
+ position: relative;
1018
+ height: 26px;
1019
+ border-radius: 99px;
1020
+ background: var(--line);
1021
+ margin: 2px 0 4px;
1022
+ }
1023
+ .rbar i {
1024
+ position: absolute;
1025
+ top: 3px;
1026
+ bottom: 3px;
1027
+ width: 6px;
1028
+ border-radius: 99px;
1029
+ background: var(--accent);
1030
+ transform: translateX(-50%);
1031
+ transition: left 0.25s;
1032
+ }
1033
+ .rbar .rl,
1034
+ .rbar .rr {
1035
+ position: absolute;
1036
+ top: 5px;
1037
+ font-family: var(--f-mono);
1038
+ font-size: 11px;
1039
+ color: var(--ink-3);
1040
+ }
1041
+ .rbar .rl {
1042
+ left: 9px;
1043
+ }
1044
+ .rbar .rr {
1045
+ right: 9px;
1046
+ }
1047
+ .splitrow {
1048
+ display: flex;
1049
+ align-items: center;
1050
+ gap: 10px;
1051
+ }
1052
+ .splitrow .lbl {
1053
+ min-width: 52px;
1054
+ font-size: 13px;
1055
+ }
1056
+ .sbar {
1057
+ flex: 1;
1058
+ height: 9px;
1059
+ border-radius: 99px;
1060
+ background: var(--line);
1061
+ overflow: hidden;
1062
+ }
1063
+ .sbar i {
1064
+ display: block;
1065
+ height: 100%;
1066
+ background: var(--accent);
1067
+ border-radius: 99px;
1068
+ }
1069
+ .matwrap {
1070
+ width: 100%;
1071
+ overflow-x: auto;
1072
+ }
1073
+ table.mat {
1074
+ border-collapse: separate;
1075
+ border-spacing: 3px;
1076
+ font-family: var(--f-mono);
1077
+ font-size: 11px;
1078
+ }
1079
+ table.mat th {
1080
+ font-weight: 500;
1081
+ color: var(--ink-3);
1082
+ text-align: right;
1083
+ white-space: nowrap;
1084
+ padding-right: 4px;
1085
+ }
1086
+ table.mat thead th {
1087
+ text-align: center;
1088
+ padding: 0 0 2px;
1089
+ }
1090
+ table.mat td {
1091
+ width: 20px;
1092
+ height: 20px;
1093
+ border-radius: 4px;
1094
+ padding: 0;
1095
+ }
1096
+ .m0 {
1097
+ background: transparent;
1098
+ border: 1px solid var(--line-2);
1099
+ }
1100
+ .m1 {
1101
+ background: var(--bad);
1102
+ }
1103
+ .m2 {
1104
+ background: color-mix(in srgb, var(--mid) 45%, var(--surface));
1105
+ }
1106
+ .m3 {
1107
+ background: color-mix(in srgb, var(--mid) 80%, var(--surface));
1108
+ }
1109
+ .m5 {
1110
+ background: var(--good);
1111
+ }
1112
+ .legend b.m0,
1113
+ .legend b.m1,
1114
+ .legend b.m3,
1115
+ .legend b.m5 {
1116
+ border-radius: 3px;
1117
+ }
1118
+ .notesrow {
1119
+ display: flex;
1120
+ flex-wrap: wrap;
1121
+ gap: 7px;
1122
+ justify-content: center;
1123
+ }
1124
+ .nchip {
1125
+ display: flex;
1126
+ flex-direction: column;
1127
+ align-items: center;
1128
+ gap: 1px;
1129
+ border: 1px solid var(--line-2);
1130
+ border-radius: 8px;
1131
+ padding: 6px 10px;
1132
+ min-width: 44px;
1133
+ }
1134
+ .nchip b {
1135
+ font-family: var(--f-mono);
1136
+ font-size: 17px;
1137
+ font-weight: 600;
1138
+ }
1139
+ .nchip i {
1140
+ font-family: var(--f-mono);
1141
+ font-size: 10.5px;
1142
+ font-style: normal;
1143
+ color: var(--ink-3);
1144
+ }
1145
+ .nchip.root {
1146
+ border-color: var(--accent);
1147
+ background: var(--accent-soft);
1148
+ }
1149
+ .nchip.root b {
1150
+ color: var(--accent);
1151
+ }
1152
+ .scrow {
1153
+ display: grid;
1154
+ grid-template-columns: auto 1fr;
1155
+ gap: 8px 10px;
1156
+ align-items: center;
1157
+ padding: 9px 0;
1158
+ border-bottom: 1px solid var(--line);
1159
+ }
1160
+ .scrow:last-child {
1161
+ border-bottom: none;
1162
+ }
1163
+ .scrow .ivs {
1164
+ font-family: var(--f-mono);
1165
+ font-size: 12px;
1166
+ color: var(--ink-3);
1167
+ text-align: right;
1168
+ }
1169
+ .scrow .note {
1170
+ grid-column: 1/-1;
1171
+ margin: 0;
1172
+ }
1173
+ .scrow.on .ivs {
1174
+ color: var(--accent);
1175
+ }
1176
+ .degrow {
1177
+ display: flex;
1178
+ gap: 5px;
1179
+ flex-wrap: wrap;
1180
+ }
1181
+ .degchip {
1182
+ font-family: var(--f-mono);
1183
+ font-size: 12px;
1184
+ border: 1px solid var(--line-2);
1185
+ border-radius: 99px;
1186
+ padding: 2px 8px;
1187
+ }
1188
+ .tline {
1189
+ display: flex;
1190
+ align-items: flex-end;
1191
+ gap: 2px;
1192
+ height: 40px;
1193
+ }
1194
+ .tline i {
1195
+ flex: 1;
1196
+ background: var(--accent);
1197
+ border-radius: 1px;
1198
+ min-height: 2px;
1199
+ opacity: 0.9;
1200
+ }
1201
+ .prog {
1202
+ height: 5px;
1203
+ border-radius: 99px;
1204
+ background: var(--line);
1205
+ overflow: hidden;
1206
+ }
1207
+ .prog i {
1208
+ display: block;
1209
+ height: 100%;
1210
+ background: var(--accent);
1211
+ transition: width 0.25s;
1212
+ }
1213
+ .hint {
1214
+ font-size: 12.5px;
1215
+ color: var(--ink-3);
1216
+ }
1217
+ .statrow {
1218
+ display: grid;
1219
+ grid-template-columns: repeat(4, 1fr);
1220
+ gap: 1px;
1221
+ background: var(--line);
1222
+ border: 1px solid var(--line);
1223
+ border-radius: var(--r);
1224
+ overflow: hidden;
1225
+ }
1226
+ .statrow > div {
1227
+ background: var(--surface);
1228
+ padding: 11px 8px;
1229
+ text-align: center;
1230
+ }
1231
+ .statrow .v {
1232
+ font-family: var(--f-mono);
1233
+ font-size: 21px;
1234
+ font-weight: 600;
1235
+ font-variant-numeric: tabular-nums;
1236
+ line-height: 1.1;
1237
+ }
1238
+ .statrow .k {
1239
+ font-family: var(--f-disp);
1240
+ font-size: 11px;
1241
+ letter-spacing: 0.1em;
1242
+ text-transform: uppercase;
1243
+ color: var(--ink-3);
1244
+ margin-top: 2px;
1245
+ }
1246
+
1247
+ /* drill stage */
1248
+ .stage {
1249
+ background: var(--surface);
1250
+ border: 1px solid var(--line);
1251
+ border-radius: var(--r-lg);
1252
+ padding: 18px 14px;
1253
+ display: flex;
1254
+ flex-direction: column;
1255
+ align-items: center;
1256
+ gap: 14px;
1257
+ min-height: 190px;
1258
+ justify-content: center;
1259
+ }
1260
+ .stage.slim {
1261
+ min-height: 0;
1262
+ padding: 12px 14px;
1263
+ gap: 6px;
1264
+ }
1265
+ .ask {
1266
+ font-family: var(--f-disp);
1267
+ font-size: 14px;
1268
+ letter-spacing: 0.1em;
1269
+ text-transform: uppercase;
1270
+ color: var(--ink-3);
1271
+ text-align: center;
1272
+ }
1273
+ .bignote {
1274
+ font-family: var(--f-mono);
1275
+ font-size: clamp(46px, 17vw, 76px);
1276
+ font-weight: 600;
1277
+ line-height: 1;
1278
+ letter-spacing: -0.01em;
1279
+ }
1280
+ .sub {
1281
+ font-size: 13px;
1282
+ color: var(--ink-3);
1283
+ text-align: center;
1284
+ }
1285
+ .keys {
1286
+ display: grid;
1287
+ grid-template-columns: 1fr 1fr;
1288
+ gap: 9px;
1289
+ }
1290
+ .key {
1291
+ background: var(--raised);
1292
+ border: 1.5px solid var(--line-2);
1293
+ border-radius: var(--r);
1294
+ min-height: 62px;
1295
+ font-family: var(--f-mono);
1296
+ font-size: 22px;
1297
+ font-weight: 500;
1298
+ display: grid;
1299
+ place-items: center;
1300
+ transition:
1301
+ transform 0.1s,
1302
+ background 0.12s,
1303
+ border-color 0.12s;
1304
+ padding: 6px;
1305
+ }
1306
+ .key:active {
1307
+ transform: scale(0.97);
1308
+ }
1309
+ .key.ok {
1310
+ background: var(--good-soft);
1311
+ border-color: var(--good);
1312
+ color: var(--good);
1313
+ }
1314
+ .key.no {
1315
+ background: var(--bad-soft);
1316
+ border-color: var(--bad);
1317
+ color: var(--bad);
1318
+ }
1319
+ .key[disabled] {
1320
+ cursor: default;
1321
+ }
1322
+
1323
+ /* --- rhythm and key drills --- */
1324
+ .keychip {
1325
+ display: inline-block;
1326
+ font-family: var(--f-mono, IBM Plex Mono, monospace);
1327
+ font-size: 12px;
1328
+ letter-spacing: 0.09em;
1329
+ text-transform: uppercase;
1330
+ background: var(--accent);
1331
+ color: var(--accent-ink);
1332
+ padding: 4px 11px;
1333
+ border-radius: 999px;
1334
+ margin-bottom: 8px;
1335
+ }
1336
+ .ropts {
1337
+ display: flex;
1338
+ flex-direction: column;
1339
+ gap: 9px;
1340
+ margin: 4px 0;
1341
+ }
1342
+ .ropt {
1343
+ display: flex;
1344
+ align-items: center;
1345
+ gap: 10px;
1346
+ width: 100%;
1347
+ text-align: left;
1348
+ background: var(--card);
1349
+ border: 1px solid var(--line);
1350
+ border-radius: var(--r);
1351
+ padding: 6px 10px;
1352
+ overflow-x: auto;
1353
+ min-height: 74px;
1354
+ }
1355
+ .ropt .rn {
1356
+ flex: none;
1357
+ font-family: var(--f-disp);
1358
+ font-size: 15px;
1359
+ width: 24px;
1360
+ height: 24px;
1361
+ line-height: 24px;
1362
+ text-align: center;
1363
+ border-radius: 50%;
1364
+ background: var(--line);
1365
+ color: var(--ink);
1366
+ }
1367
+ .ropt svg {
1368
+ flex: none;
1369
+ }
1370
+ .ropt.ok {
1371
+ background: var(--good-soft);
1372
+ border-color: var(--good);
1373
+ }
1374
+ .ropt.ok .rn {
1375
+ background: var(--good);
1376
+ color: var(--bg);
1377
+ }
1378
+ .ropt.no {
1379
+ background: var(--bad-soft);
1380
+ border-color: var(--bad);
1381
+ }
1382
+ .ropt.no .rn {
1383
+ background: var(--bad);
1384
+ color: var(--bg);
1385
+ }
1386
+ .tappad {
1387
+ width: 100%;
1388
+ min-height: 150px;
1389
+ border-radius: var(--r);
1390
+ border: 2px dashed var(--line);
1391
+ background: var(--card);
1392
+ display: flex;
1393
+ align-items: center;
1394
+ justify-content: center;
1395
+ margin: 10px 0;
1396
+ touch-action: manipulation;
1397
+ -webkit-user-select: none;
1398
+ user-select: none;
1399
+ }
1400
+ .tappad span {
1401
+ font-family: var(--f-disp);
1402
+ font-size: 20px;
1403
+ letter-spacing: 0.06em;
1404
+ color: var(--ink-3);
1405
+ }
1406
+ .tappad.live {
1407
+ border-style: solid;
1408
+ border-color: var(--accent);
1409
+ background: var(--accent-soft, var(--card));
1410
+ }
1411
+ .tappad.live span {
1412
+ color: var(--accent);
1413
+ }
1414
+ .tappad.flash {
1415
+ background: var(--accent);
1416
+ border-color: var(--accent);
1417
+ }
1418
+ .tappad.flash span {
1419
+ color: var(--accent-ink);
1420
+ }
1421
+ .tsrow {
1422
+ display: flex;
1423
+ gap: 14px;
1424
+ align-items: flex-start;
1425
+ border-top: 1px solid var(--line);
1426
+ padding: 12px 0;
1427
+ }
1428
+ .tsrow:first-of-type {
1429
+ border-top: none;
1430
+ padding-top: 2px;
1431
+ }
1432
+ .tsnum {
1433
+ flex: none;
1434
+ display: flex;
1435
+ align-items: center;
1436
+ justify-content: center;
1437
+ min-width: 44px;
1438
+ }
1439
+ .tsnum svg {
1440
+ display: block;
1441
+ }
1442
+ .tempo {
1443
+ display: flex;
1444
+ align-items: center;
1445
+ gap: 10px;
1446
+ background: var(--surface);
1447
+ border: 1px solid var(--line);
1448
+ border-radius: var(--r);
1449
+ padding: 7px 10px;
1450
+ }
1451
+ .tbtn {
1452
+ width: 40px;
1453
+ height: 38px;
1454
+ flex: none;
1455
+ border: 1px solid var(--line-2);
1456
+ border-radius: 9px;
1457
+ background: var(--raised);
1458
+ font-size: 21px;
1459
+ line-height: 1;
1460
+ color: var(--ink);
1461
+ display: grid;
1462
+ place-items: center;
1463
+ }
1464
+ .tbtn:active {
1465
+ background: var(--accent);
1466
+ color: var(--accent-ink);
1467
+ }
1468
+ .tbtn[disabled] {
1469
+ opacity: 0.35;
1470
+ }
1471
+ .tempo .bv {
1472
+ font-family: var(--f-mono);
1473
+ font-size: 13px;
1474
+ color: var(--ink-3);
1475
+ }
1476
+ .tempo .bv b {
1477
+ font-family: var(--f-disp);
1478
+ font-size: 22px;
1479
+ color: var(--ink);
1480
+ letter-spacing: 0.02em;
1481
+ margin-right: 3px;
1482
+ }
1483
+ .tempo .best {
1484
+ font-family: var(--f-mono);
1485
+ font-size: 11.5px;
1486
+ color: var(--ink-3);
1487
+ text-align: right;
1488
+ }
1489
+ .tscap {
1490
+ text-align: center;
1491
+ font-size: 12.5px;
1492
+ color: var(--ink-3);
1493
+ margin: -6px 0 2px;
1494
+ }
1495
+ .tscap b {
1496
+ font-family: var(--f-mono);
1497
+ color: var(--ink-2);
1498
+ font-weight: 600;
1499
+ }
1500
+ .howbox {
1501
+ display: flex;
1502
+ gap: 10px;
1503
+ align-items: flex-start;
1504
+ background: var(--surface);
1505
+ border: 1px solid var(--line);
1506
+ border-radius: var(--r);
1507
+ padding: 11px 13px;
1508
+ }
1509
+ .howbox .q {
1510
+ flex: none;
1511
+ width: 21px;
1512
+ height: 21px;
1513
+ border-radius: 50%;
1514
+ background: var(--line);
1515
+ color: var(--ink-2);
1516
+ font-family: var(--f-disp);
1517
+ font-size: 14px;
1518
+ line-height: 21px;
1519
+ text-align: center;
1520
+ margin-top: 1px;
1521
+ }
1522
+ .howbox .ht {
1523
+ font-family: var(--f-disp);
1524
+ font-size: 13px;
1525
+ letter-spacing: 0.09em;
1526
+ text-transform: uppercase;
1527
+ color: var(--ink-3);
1528
+ margin-bottom: 3px;
1529
+ }
1530
+ .howbox p {
1531
+ margin: 0;
1532
+ font-size: 13.5px;
1533
+ line-height: 1.55;
1534
+ color: var(--ink-2);
1535
+ }
1536
+ .howbox b {
1537
+ color: var(--ink);
1538
+ font-weight: 600;
1539
+ }
1540
+ .keys.countopts {
1541
+ grid-template-columns: 1fr;
1542
+ }
1543
+ .keys.countopts .key {
1544
+ width: 100%;
1545
+ font-family: var(--f-mono);
1546
+ font-size: 15px;
1547
+ letter-spacing: 0.12em;
1548
+ padding: 12px 8px;
1549
+ white-space: nowrap;
1550
+ overflow-x: auto;
1551
+ }
1552
+ .rhwrap {
1553
+ overflow-x: auto;
1554
+ display: flex;
1555
+ padding: 2px 0;
1556
+ }
1557
+ /* margin:auto centres it and still lets a wide bar scroll to both ends,
1558
+ which justify-content:center does not; display:block on the svg keeps the
1559
+ stage from collapsing to a zero-height line box. */
1560
+ .rhwrap > .fbstage {
1561
+ flex: none;
1562
+ margin: 0 auto;
1563
+ }
1564
+ .rhwrap svg {
1565
+ display: block;
1566
+ }
1567
+ .dropbox {
1568
+ display: flex;
1569
+ flex-direction: column;
1570
+ align-items: center;
1571
+ gap: 8px;
1572
+ padding: 34px 16px;
1573
+ border-radius: var(--r);
1574
+ border: 1px solid var(--line);
1575
+ background: var(--card);
1576
+ }
1577
+ .dropstate {
1578
+ font-family: var(--f-disp);
1579
+ font-size: 40px;
1580
+ letter-spacing: 0.06em;
1581
+ }
1582
+ .dropstate.on {
1583
+ color: var(--ink-3);
1584
+ }
1585
+ .dropstate.off {
1586
+ color: var(--accent);
1587
+ }
1588
+ .clefgrid {
1589
+ display: flex;
1590
+ flex-direction: column;
1591
+ gap: 8px;
1592
+ }
1593
+ .clefrow {
1594
+ display: flex;
1595
+ align-items: flex-start;
1596
+ gap: 12px;
1597
+ border-top: 1px solid var(--line);
1598
+ padding: 10px 0;
1599
+ }
1600
+ .clefrow svg {
1601
+ flex: none;
1602
+ width: 44px;
1603
+ height: 58px;
1604
+ }
1605
+ .verdict {
1606
+ display: flex;
1607
+ align-items: center;
1608
+ gap: 10px;
1609
+ justify-content: space-between;
1610
+ border-radius: var(--r);
1611
+ padding: 11px 14px;
1612
+ min-height: 52px;
1613
+ border: 1px solid var(--line);
1614
+ }
1615
+ .verdict.ok {
1616
+ background: var(--good-soft);
1617
+ border-color: var(--good);
1618
+ }
1619
+ .verdict.no {
1620
+ background: var(--bad-soft);
1621
+ border-color: var(--bad);
1622
+ }
1623
+ .verdict .txt {
1624
+ font-family: var(--f-disp);
1625
+ font-size: 17px;
1626
+ letter-spacing: 0.03em;
1627
+ }
1628
+ .verdict.ok .txt {
1629
+ color: var(--good);
1630
+ }
1631
+ .verdict.no .txt {
1632
+ color: var(--bad);
1633
+ }
1634
+ .next {
1635
+ font-family: var(--f-disp);
1636
+ font-size: 15px;
1637
+ letter-spacing: 0.07em;
1638
+ text-transform: uppercase;
1639
+ background: var(--ink);
1640
+ color: var(--bg);
1641
+ padding: 9px 16px;
1642
+ border-radius: 8px;
1643
+ flex: none;
1644
+ }
1645
+
1646
+ .bar {
1647
+ display: flex;
1648
+ align-items: center;
1649
+ gap: 10px;
1650
+ flex-wrap: wrap;
1651
+ }
1652
+ .bar .spacer {
1653
+ margin-left: auto;
1654
+ }
1655
+ .tick {
1656
+ font-family: var(--f-mono);
1657
+ font-size: 13px;
1658
+ color: var(--ink-3);
1659
+ font-variant-numeric: tabular-nums;
1660
+ }
1661
+ .ghost {
1662
+ border: 1px solid var(--line-2);
1663
+ border-radius: 8px;
1664
+ padding: 7px 12px;
1665
+ font-family: var(--f-disp);
1666
+ font-size: 14px;
1667
+ letter-spacing: 0.06em;
1668
+ text-transform: uppercase;
1669
+ color: var(--ink-2);
1670
+ }
1671
+ .ghost.on {
1672
+ border-color: var(--accent);
1673
+ color: var(--accent);
1674
+ background: var(--accent-soft);
1675
+ }
1676
+
1677
+ .hswrap {
1678
+ width: 100%;
1679
+ overflow-x: auto;
1680
+ overflow-y: hidden;
1681
+ -webkit-overflow-scrolling: touch;
1682
+ scrollbar-width: thin;
1683
+ }
1684
+ .hswrap svg {
1685
+ display: block;
1686
+ height: auto;
1687
+ max-width: none;
1688
+ }
1689
+ .kbwrap {
1690
+ width: 100%;
1691
+ overflow-x: auto;
1692
+ overflow-y: hidden;
1693
+ -webkit-overflow-scrolling: touch;
1694
+ scrollbar-width: thin;
1695
+ }
1696
+ .kbwrap.fit {
1697
+ overflow: visible;
1698
+ display: flex;
1699
+ justify-content: center;
1700
+ }
1701
+ .kbwrap svg {
1702
+ display: block;
1703
+ height: auto;
1704
+ max-width: 100%;
1705
+ width: auto;
1706
+ }
1707
+ .kbstage {
1708
+ position: relative;
1709
+ display: inline-block;
1710
+ line-height: 0;
1711
+ max-width: 100%;
1712
+ }
1713
+ .kbstage .taplayer button.bk {
1714
+ border-radius: 0 0 4px 4px;
1715
+ }
1716
+ .kbstage .taplayer button.lit {
1717
+ background: color-mix(in srgb, var(--accent) 55%, transparent);
1718
+ }
1719
+ .kblegend {
1720
+ display: flex;
1721
+ gap: 12px;
1722
+ flex-wrap: wrap;
1723
+ justify-content: center;
1724
+ margin-top: 6px;
1725
+ }
1726
+ .fbwrap {
1727
+ width: 100%;
1728
+ overflow-x: auto;
1729
+ overflow-y: hidden;
1730
+ -webkit-overflow-scrolling: touch;
1731
+ scrollbar-width: thin;
1732
+ }
1733
+ .fbwrap svg {
1734
+ display: block;
1735
+ height: 196px;
1736
+ width: auto;
1737
+ max-width: none;
1738
+ }
1739
+ .fbwrap.fit {
1740
+ overflow: visible;
1741
+ display: flex;
1742
+ justify-content: center;
1743
+ }
1744
+ .fbwrap.fit svg {
1745
+ width: auto;
1746
+ height: auto;
1747
+ max-width: 100%;
1748
+ }
1749
+ .fbstage {
1750
+ position: relative;
1751
+ display: inline-block;
1752
+ line-height: 0;
1753
+ }
1754
+ /* Only the non-scrolling boards may be shrunk. In a scrolling one the stage
1755
+ must keep the SVG's full width, or the overlaid tap buttons are laid out
1756
+ against a narrower box than the board is drawn in and every target drifts
1757
+ further right than the note it belongs to. */
1758
+ .fbwrap.fit .fbstage {
1759
+ max-width: 100%;
1760
+ }
1761
+ .taplayer {
1762
+ position: absolute;
1763
+ inset: 0;
1764
+ }
1765
+ .taplayer button {
1766
+ position: absolute;
1767
+ padding: 0;
1768
+ margin: 0;
1769
+ border: none;
1770
+ background: transparent;
1771
+ border-radius: 7px;
1772
+ -webkit-tap-highlight-color: transparent;
1773
+ touch-action: manipulation;
1774
+ }
1775
+ .taplayer button:active {
1776
+ background: color-mix(in srgb, var(--accent) 45%, transparent);
1777
+ }
1778
+ .taplayer button:focus-visible {
1779
+ outline: 2px solid var(--accent);
1780
+ outline-offset: -3px;
1781
+ }
1782
+ .staffwrap {
1783
+ width: 100%;
1784
+ display: flex;
1785
+ justify-content: center;
1786
+ }
1787
+ .staffwrap svg {
1788
+ width: min(100%, 292px);
1789
+ height: auto;
1790
+ }
1791
+ .staffwrap.one svg {
1792
+ width: min(60%, 132px);
1793
+ }
1794
+
1795
+ .meter {
1796
+ width: 100%;
1797
+ height: 8px;
1798
+ border-radius: 99px;
1799
+ background: var(--line);
1800
+ overflow: hidden;
1801
+ }
1802
+ .meter i {
1803
+ display: block;
1804
+ height: 100%;
1805
+ background: var(--accent);
1806
+ width: 0%;
1807
+ transition: width 0.08s linear;
1808
+ }
1809
+ .tuner {
1810
+ font-family: var(--f-mono);
1811
+ font-variant-numeric: tabular-nums;
1812
+ display: flex;
1813
+ align-items: baseline;
1814
+ gap: 10px;
1815
+ justify-content: center;
1816
+ }
1817
+ .tuner .hz {
1818
+ font-size: 13px;
1819
+ color: var(--ink-3);
1820
+ }
1821
+ .tuner .nt {
1822
+ font-size: 24px;
1823
+ font-weight: 600;
1824
+ letter-spacing: 0.01em;
1825
+ }
1826
+ .cents {
1827
+ width: 100%;
1828
+ max-width: 280px;
1829
+ height: 26px;
1830
+ position: relative;
1831
+ border-radius: 6px;
1832
+ background: var(--bg);
1833
+ border: 1px solid var(--line);
1834
+ }
1835
+ .cents .mid {
1836
+ position: absolute;
1837
+ left: 50%;
1838
+ top: 3px;
1839
+ bottom: 3px;
1840
+ width: 2px;
1841
+ background: var(--line-2);
1842
+ transform: translateX(-50%);
1843
+ }
1844
+ .cents .ndl {
1845
+ position: absolute;
1846
+ top: 2px;
1847
+ bottom: 2px;
1848
+ width: 4px;
1849
+ border-radius: 2px;
1850
+ background: var(--accent);
1851
+ left: 50%;
1852
+ transform: translateX(-50%);
1853
+ transition: left 0.07s linear;
1854
+ }
1855
+
1856
+ table.opens {
1857
+ width: 100%;
1858
+ border-collapse: collapse;
1859
+ font-family: var(--f-mono);
1860
+ font-size: 14px;
1861
+ }
1862
+ table.opens td {
1863
+ padding: 8px 6px;
1864
+ border-bottom: 1px solid var(--line);
1865
+ }
1866
+ table.opens tr:last-child td {
1867
+ border-bottom: none;
1868
+ }
1869
+ table.opens .st {
1870
+ width: 38px;
1871
+ font-weight: 600;
1872
+ }
1873
+ table.opens .hit {
1874
+ text-align: right;
1875
+ color: var(--ink-3);
1876
+ }
1877
+ table.opens tr.done .hit {
1878
+ color: var(--good);
1879
+ }
1880
+
1881
+ .setrow {
1882
+ display: flex;
1883
+ align-items: center;
1884
+ gap: 10px;
1885
+ justify-content: space-between;
1886
+ flex-wrap: wrap;
1887
+ padding: 11px 0;
1888
+ border-bottom: 1px solid var(--line);
1889
+ }
1890
+ .setrow:last-child {
1891
+ border-bottom: none;
1892
+ }
1893
+ .setrow .lbl {
1894
+ font-size: 14px;
1895
+ }
1896
+ .setrow .hint {
1897
+ font-size: 12.5px;
1898
+ color: var(--ink-3);
1899
+ width: 100%;
1900
+ }
1901
+
1902
+ .spark {
1903
+ display: flex;
1904
+ align-items: flex-end;
1905
+ gap: 3px;
1906
+ height: 56px;
1907
+ }
1908
+ .spark i {
1909
+ flex: 1;
1910
+ background: var(--accent);
1911
+ border-radius: 2px 2px 0 0;
1912
+ min-height: 2px;
1913
+ opacity: 0.85;
1914
+ }
1915
+ .spark i.z {
1916
+ background: var(--line-2);
1917
+ opacity: 1;
1918
+ }
1919
+ .axis {
1920
+ display: flex;
1921
+ justify-content: space-between;
1922
+ font-family: var(--f-mono);
1923
+ font-size: 11px;
1924
+ color: var(--ink-3);
1925
+ margin-top: 5px;
1926
+ }
1927
+ .pcbars {
1928
+ display: grid;
1929
+ grid-template-columns: repeat(12, 1fr);
1930
+ gap: 4px;
1931
+ align-items: end;
1932
+ height: 74px;
1933
+ }
1934
+ .pcbars .b {
1935
+ display: flex;
1936
+ flex-direction: column;
1937
+ justify-content: flex-end;
1938
+ height: 100%;
1939
+ }
1940
+ .pcbars .b i {
1941
+ border-radius: 2px 2px 0 0;
1942
+ min-height: 3px;
1943
+ display: block;
1944
+ }
1945
+ .pclabels {
1946
+ display: grid;
1947
+ grid-template-columns: repeat(12, 1fr);
1948
+ gap: 4px;
1949
+ font-family: var(--f-mono);
1950
+ font-size: 10px;
1951
+ color: var(--ink-3);
1952
+ text-align: center;
1953
+ margin-top: 5px;
1954
+ }
1955
+ .legend {
1956
+ display: flex;
1957
+ gap: 14px;
1958
+ flex-wrap: wrap;
1959
+ font-size: 12px;
1960
+ color: var(--ink-3);
1961
+ align-items: center;
1962
+ }
1963
+ .legend span {
1964
+ display: flex;
1965
+ align-items: center;
1966
+ gap: 5px;
1967
+ }
1968
+ .legend b {
1969
+ width: 11px;
1970
+ height: 11px;
1971
+ border-radius: 3px;
1972
+ display: block;
1973
+ }
1974
+
1975
+ #oops {
1976
+ position: fixed;
1977
+ left: 0;
1978
+ right: 0;
1979
+ bottom: 0;
1980
+ z-index: 50;
1981
+ background: var(--bad-soft);
1982
+ color: var(--bad);
1983
+ border-top: 1px solid var(--bad);
1984
+ font-family: var(--f-mono);
1985
+ font-size: 11.5px;
1986
+ line-height: 1.4;
1987
+ padding: 9px 12px;
1988
+ padding-bottom: calc(9px + env(safe-area-inset-bottom, 0px));
1989
+ word-break: break-word;
1990
+ }
1991
+ .note {
1992
+ font-size: 12.5px;
1993
+ color: var(--ink-3);
1994
+ line-height: 1.45;
1995
+ max-width: 62ch;
1996
+ }
1997
+ .err {
1998
+ color: var(--bad);
1999
+ font-size: 13px;
2000
+ }
2001
+ .failbox {
2002
+ border: 1px solid var(--bad);
2003
+ background: var(--bad-soft);
2004
+ border-radius: var(--r);
2005
+ padding: 11px 13px;
2006
+ }
2007
+ .failbox .ft {
2008
+ font-family: var(--f-disp);
2009
+ font-size: 16px;
2010
+ letter-spacing: 0.03em;
2011
+ color: var(--bad);
2012
+ }
2013
+ @media (prefers-reduced-motion: reduce) {
2014
+ * {
2015
+ transition: none !important;
2016
+ animation: none !important;
2017
+ scroll-behavior: auto !important;
2018
+ }
2019
+ }
2020
+
2021
+ @media (max-width: 460px) {
2022
+ .statrow {
2023
+ grid-template-columns: repeat(2, 1fr);
2024
+ }
2025
+ header {
2026
+ gap: 3px;
2027
+ padding: 10px 12px;
2028
+ }
2029
+ .brand {
2030
+ font-size: 16px;
2031
+ gap: 7px;
2032
+ }
2033
+ .brand .mk {
2034
+ height: 19px;
2035
+ width: 9px;
2036
+ }
2037
+ .brand .mk.icon {
2038
+ height: 22px;
2039
+ width: 22px;
2040
+ }
2041
+ .brand:has(.mk.icon) {
2042
+ min-width: 22px;
2043
+ }
2044
+ .hbtn {
2045
+ font-size: 12.5px;
2046
+ padding: 6px 6px;
2047
+ letter-spacing: 0.04em;
2048
+ }
2049
+ main {
2050
+ padding: 16px 12px 32px;
2051
+ }
2052
+ }
2053
+ @media (max-width: 380px) {
2054
+ .hbtn {
2055
+ padding: 6px 4px;
2056
+ letter-spacing: 0.02em;
2057
+ }
2058
+ .metchip {
2059
+ padding: 4px 6px;
2060
+ }
2061
+ header {
2062
+ padding: 10px 9px;
2063
+ }
2064
+ }