-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstyles.css
More file actions
1029 lines (966 loc) · 56.3 KB
/
Copy pathstyles.css
File metadata and controls
1029 lines (966 loc) · 56.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Forage DeepMind brand system (forage-colorway skill, FDMAI-BRAND-001 v1.1).
Dark x Ember Gold (Deep Minds) is default; Light x Sage (Open) is now also
wired as a real toggle (2026-08-02, fifth pass, direct request) - both
tiers are defined IN the brand guidelines, this isn't inventing a palette.
Structure/motion layer (third pass): Material Design 3's published tokens
(spacing, shape, elevation, state-layer, easing/duration) applied natively.
Page-margin layer (fifth pass): Atlassian Design System's spacing-primitive
scale (8px base unit: atlassian.design/foundations/spacing), used for the
outer page gutters specifically ("similar to github" - a centered,
max-width shell with generous side margins), layered on top of the M3
component-level tokens rather than replacing them - two different design
systems solving two different problems (page layout vs. component
structure), both reskinned with Forage's own colors, no conflict. */
:root {
/* Locked Forage Deep Minds pairing (default/dark) - do not change on any
future UI pass without an explicit brand-guideline update. */
--bg: #1c1c1a; /* Deep Charcoal - primary dark surface */
--card: #262624; /* Near Black - elevated card/surface on dark */
--card-alt: #2a2a28; /* Charcoal Mist - nested surface (drawer, toast) */
--text: rgba(237, 233, 224, 0.88);
--text-strong: #ede9e0;
--ember: #c8a96e; /* Ember Gold - dark-surface accent ONLY (brand rule) */
--sage: #7a8c6e; /* Muted Sage - light-surface accent / secondary tag on dark */
--crimson: #c1286b; /* Rose Crimson - warnings/critical only, never decorative */
--stone: #b0ada4; /* Stone Gray - tags, footer, captions, metadata, both modes */
--parchment: #eae7dc; /* callout/code backgrounds */
--border-hint: rgba(200, 169, 110, 0.12);
/* Semantic accent - the brand guideline is explicit that Ember Gold never
appears as text/heading on a light surface ("dark surfaces only"), and
Sage is the light-surface accent instead. Every rule below that colors
TEXT (headings, links, CTAs) uses --accent-text, never --ember directly,
so the theme toggle can never accidentally put gold text on a light
background. --ember itself stays available for non-text decorative use
(borders, fills) that the guideline doesn't restrict. */
--accent-text: var(--ember);
--font-display: "Crimson Text", Georgia, serif;
--font-heading: "DM Sans", Arial, sans-serif;
--font-body: "DM Sans", Helvetica, sans-serif;
--font-mono: "JetBrains Mono", "Courier New", monospace;
/* M3 spacing scale (4dp baseline) - component-level spacing. */
--space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
--space-5: 20px; --space-6: 24px; --space-7: 28px; --space-8: 32px; --space-10: 40px;
/* Atlassian spacing-primitive scale (8px base) - PAGE-level margins only
(.hk-shell below). Kept as separate tokens from the M3 scale above on
purpose: same numeric family, different job (layout gutters vs.
component padding), matching how the source actually separates them. */
--gutter-sm: 24px; /* space.300 */
--gutter-lg: 40px; /* space.500 */
--gutter-xl: 64px; /* space.800 */
--radius-xs: 4px; --radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px;
--radius-xl: 28px; --radius-full: 999px;
--elevation-0: none;
--elevation-1: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.22);
--elevation-2: 0 2px 4px rgba(0, 0, 0, 0.32), 0 4px 10px rgba(0, 0, 0, 0.26);
--elevation-3: 0 4px 8px rgba(0, 0, 0, 0.34), 0 8px 20px rgba(0, 0, 0, 0.28);
--elevation-4: 0 6px 14px rgba(0, 0, 0, 0.4), 0 12px 32px rgba(0, 0, 0, 0.34);
--state-hover: rgba(200, 169, 110, 0.08);
--state-focus: rgba(200, 169, 110, 0.12);
--state-press: rgba(200, 169, 110, 0.16);
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
--ease-emphasized-decel: cubic-bezier(0.05, 0.7, 0.1, 1);
--duration-short: 150ms;
--duration-medium: 300ms;
/* Shared background-animation speed control (2026-08-02, direct request:
a scene-taskbar slider for "the one that moves randomly"). Both the
ambient particle field and the star-chase layer divide their per-element
base duration by this one value (scene-panel.ts writes it); 1 = normal
speed. The var()'s own fallback of 1 (used at each call site) covers the
brief moment before scene-panel.ts's init runs, so this root default is
documentation as much as function. */
--hk-speed: 1;
}
/* Forage Open - Light x Sage. Same named tokens, brand-guideline light
values - toggled via [data-theme="light"] on <html> (theme.ts), persisted
in localStorage, default is dark (guideline: "Dark mode is the default
presentation mode. Light mode is secondary."). */
:root[data-theme="light"] {
--bg: #f5f3ee; /* Warm Off-White */
--card: #ffffff; /* Pure White */
--card-alt: var(--parchment);
--text: rgba(28, 28, 26, 0.82);
--text-strong: #1c1c1a;
--accent-text: var(--sage); /* Ember is dark-surface-only per brand rule */
--border-hint: rgba(122, 140, 110, 0.22);
--state-hover: rgba(122, 140, 110, 0.08);
--state-focus: rgba(122, 140, 110, 0.14);
--state-press: rgba(122, 140, 110, 0.18);
}
/* Feminine - light pink x maroon (2026-08-02, direct request: "New theme
mode: Feminine - (light pink-maroon)"). NOT part of the locked Forage
brand guideline (FDMAI-BRAND-001 defines exactly two tiers: Deep Minds
dark x Ember Gold, Open light x Sage) - this is a deliberate,
explicitly-flagged personalization option for Adam's own dashboard, same
category as the mascot's purple (mascot.ts): a one-off outside the locked
palette, added because it was directly requested, never presented as
official Forage identity. --stone/--ember/--crimson stay the same
literal values as the other two themes (same pattern the Light theme
already uses) rather than inventing per-theme variants of those too. */
:root[data-theme="feminine"] {
--bg: #fdf1f5; /* soft light pink */
--card: #ffffff;
--card-alt: #fbe0ea; /* deeper pink-tinted nested surface */
--text: rgba(74, 20, 35, 0.82);
--text-strong: #4a1423; /* deep maroon */
--accent-text: #a23855; /* rose-maroon accent - headings/links/CTAs */
--border-hint: rgba(162, 56, 85, 0.18);
--state-hover: rgba(162, 56, 85, 0.08);
--state-focus: rgba(162, 56, 85, 0.14);
--state-press: rgba(162, 56, 85, 0.18);
}
* { box-sizing: border-box; }
html { color-scheme: dark; }
html[data-theme="light"], html[data-theme="feminine"] { color-scheme: light; }
body {
margin: 0; min-height: 100vh;
background: var(--bg);
color: var(--text); font-family: var(--font-body);
transition: background-color var(--duration-medium) var(--ease-standard), color var(--duration-medium) var(--ease-standard);
}
/* GitHub-style centered shell with generous side gutters - Atlassian's
space.300/500/800 tokens (24/40/64px), widening at larger viewports rather
than a single fixed value everywhere. */
.hk-shell { max-width: 1280px; margin: 0 auto; padding: 0 var(--gutter-sm); }
@media (min-width: 900px) { .hk-shell { padding: 0 var(--gutter-lg); } }
@media (min-width: 1440px) { .hk-shell { padding: 0 var(--gutter-xl); } }
/* ---------- Top ribbon + profile header (GitHub-profile-style) ---------- */
.hk-ribbon {
display: flex; align-items: center; justify-content: space-between;
padding: var(--space-4) 0; border-bottom: 1px solid var(--border-hint);
font-family: var(--font-mono); font-size: 12px; color: var(--stone);
}
.hk-ribbon-brand { color: var(--accent-text); font-weight: 500; letter-spacing: 0.04em; }
.hk-profile-header {
display: flex; gap: var(--space-6); align-items: flex-start;
padding: var(--space-7) 0 var(--space-6);
}
.hk-avatar-wrap {
position: relative; width: 88px; height: 88px; flex-shrink: 0;
border-radius: var(--radius-lg); overflow: hidden; cursor: pointer;
border: 1px solid var(--border-hint); box-shadow: var(--elevation-2);
}
.hk-avatar-wrap svg { display: block; width: 100%; height: 100%; }
.hk-avatar-overlay {
position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
background: rgba(0, 0, 0, 0.55); color: #fff; text-align: center; font-size: 10.5px;
font-family: var(--font-heading); font-weight: 600; letter-spacing: 0.03em; line-height: 1.4;
opacity: 0; transition: opacity var(--duration-short) var(--ease-standard);
padding: 6px;
}
.hk-avatar-wrap:hover .hk-avatar-overlay,
.hk-avatar-wrap:focus-visible .hk-avatar-overlay { opacity: 1; }
.hk-avatar-wrap:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--state-focus), 0 0 0 1px var(--accent-text); }
.hk-profile-body { min-width: 0; }
.hk-profile-name {
margin: 0; font-family: var(--font-display); font-weight: 400; font-size: 30px; color: var(--accent-text);
cursor: text; border-radius: var(--radius-xs); padding: 2px 6px; margin-left: -6px;
transition: background-color var(--duration-short) var(--ease-standard);
}
.hk-profile-name:hover { background: var(--state-hover); }
.hk-profile-name:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
.hk-profile-name[contenteditable="true"] {
background: var(--card-alt); outline: 2px solid var(--accent-text); outline-offset: 2px; cursor: text;
}
.hk-profile-bio { color: var(--text); font-size: 13.5px; max-width: 520px; margin-top: 4px; line-height: 1.6; }
.hk-theme-toggle {
display: inline-flex; align-items: center; gap: 6px;
background: var(--card); border: 1px solid var(--border-hint); color: var(--text);
padding: 7px 12px; border-radius: var(--radius-full); cursor: pointer;
font-size: 11.5px; font-family: var(--font-heading);
transition: border-color var(--duration-short) var(--ease-standard), background-color var(--duration-short) var(--ease-standard);
}
.hk-theme-toggle:hover { border-color: var(--accent-text); }
.hk-theme-toggle:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--state-focus), 0 0 0 1px var(--accent-text); }
.hk-page { padding: var(--space-2) 0 var(--space-5); position: relative; }
.hk-empty {
display: flex; flex-direction: column; align-items: center; justify-content: center;
min-height: 340px; gap: 14px; text-align: center;
}
.hk-empty-icon { font-size: 40px; opacity: 0.5; color: var(--accent-text); }
.hk-empty b { font-family: var(--font-heading); font-weight: 600; color: var(--text-strong); }
.hk-muted { color: var(--stone); font-size: 12.5px; max-width: 420px; font-family: var(--font-body); }
.hk-primary {
position: relative; overflow: hidden;
background: transparent; border: 1px solid var(--accent-text); color: var(--accent-text);
padding: 10px var(--space-4); border-radius: var(--radius-sm); cursor: pointer;
font-size: 13px; font-family: var(--font-heading);
transition: background-color var(--duration-short) var(--ease-standard),
box-shadow var(--duration-short) var(--ease-standard),
transform var(--duration-short) var(--ease-standard);
}
.hk-primary:hover { background: var(--state-hover); }
.hk-primary:active { transform: scale(0.98); background: var(--state-press); }
.hk-primary:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--state-focus), 0 0 0 1px var(--accent-text); }
.hk-primary::after {
content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
background: radial-gradient(circle, var(--state-press) 0%, transparent 70%);
opacity: 0; transform: scale(0.4);
transition: opacity var(--duration-medium) var(--ease-standard), transform var(--duration-medium) var(--ease-standard);
}
.hk-primary:active::after { opacity: 1; transform: scale(1.3); transition-duration: 60ms; }
.hk-toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.hk-source-banner {
display: flex; justify-content: space-between; align-items: center; gap: 12px;
padding: 10px var(--space-4); margin-bottom: 14px; border-radius: var(--radius-md);
background: var(--state-hover); border: 1px solid var(--border-hint);
font-size: 12px; color: var(--stone); font-family: var(--font-body);
}
.hk-link-btn {
position: relative;
background: none; border: none; color: var(--accent-text); cursor: pointer; font-size: 12px;
font-family: var(--font-heading); text-decoration: underline; white-space: nowrap; padding: 0;
transition: color var(--duration-short) var(--ease-standard);
}
.hk-link-btn:hover { color: var(--text-strong); }
.hk-link-btn:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 3px; border-radius: var(--radius-xs); }
.hk-card {
background: var(--card); border: 1px solid var(--border-hint);
border-radius: var(--radius-lg); padding: var(--space-4);
box-shadow: var(--elevation-1);
}
.hk-label {
font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--stone);
margin-bottom: 12px; font-family: var(--font-mono);
}
.hk-warn-row {
display: flex; gap: 10px; align-items: flex-start; padding: 10px; border: 1px solid var(--crimson);
border-radius: var(--radius-sm); background: rgba(193, 40, 107, 0.08); margin-bottom: 8px; font-size: 12.5px;
font-family: var(--font-body);
}
.hk-tag {
color: var(--crimson); font-weight: 600; white-space: nowrap; font-family: var(--font-mono);
text-transform: uppercase; font-size: 10.5px; letter-spacing: 0.08em;
}
/* ---------- Widget canvas: resizable, movable cards ---------- */
/* Free-form canvas (2026-08-02, direct request: "resizeable movable card...
moving, it's not static"). Each .hk-widget is absolutely positioned inside
this relatively-positioned canvas; native `resize:both` handles resizing
(zero JS, Chromium-native - fits ADR-0003's existing Chromium-only scope),
a small pointer-driven drag on the header handles moving. Position/size
persists per-widget in localStorage (src/dashboard/draggable.ts) so a
visitor's arrangement survives a reload; first-time visitors get the
built-in default layout below.
No side padding/max-width here, and deliberately not nested inside
.hk-shell (see index.html) - direct request: "remove the boundary space
so the cards can be places freely anywhere." A card's drag range is
computed from canvas.clientWidth (draggable.ts), so a full-bleed,
unconstrained canvas is what actually gives a card the whole browser
width to move in rather than just .hk-shell's centered content column. */
.hk-canvas { position: relative; width: 100%; min-height: 1300px; margin-bottom: var(--space-6); }
.hk-widget {
position: absolute; display: flex; flex-direction: column;
min-width: 220px; min-height: 160px;
resize: both; overflow: auto;
background: var(--card); border: 1px solid var(--border-hint);
border-radius: var(--radius-lg); box-shadow: var(--elevation-1);
animation: hk-rise-in var(--duration-medium) var(--ease-emphasized-decel) both;
}
.hk-widget-handle {
display: flex; align-items: center; justify-content: space-between; gap: 8px;
padding: var(--space-3) var(--space-4); cursor: grab;
border-bottom: 1px solid var(--border-hint); flex-shrink: 0;
background: var(--card-alt); border-radius: var(--radius-lg) var(--radius-lg) 0 0;
user-select: none; touch-action: none;
}
.hk-widget-handle:active { cursor: grabbing; }
.hk-widget-grip { color: var(--stone); font-size: 11px; letter-spacing: 0.14em; font-family: var(--font-mono); }
.hk-widget.dragging { box-shadow: var(--elevation-3); opacity: 0.96; }
.hk-widget-body { padding: var(--space-4); flex: 1; min-height: 0; }
/* Close (x) button + hidden state (2026-08-02, direct request: "fix close x
button in each card not showing"). Lives inside the drag handle, next to
the grip; createWidget() (draggable.ts) stops its pointerdown from
bubbling into the drag-start handler so a close-click never also starts a
drag. Hiding a card never removes it from the DOM - state (notepad text,
prayer countdown timers, etc.) survives a close/reopen within the same
page load. */
.hk-widget-handle-right { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.hk-widget-close {
cursor: pointer; color: var(--stone); font-size: 16px; line-height: 1;
width: 18px; height: 18px; flex-shrink: 0;
display: flex; align-items: center; justify-content: center;
border-radius: var(--radius-xs);
transition: color var(--duration-short) var(--ease-standard), background-color var(--duration-short) var(--ease-standard);
}
.hk-widget-close:hover { color: var(--crimson); background: var(--state-hover); }
.hk-widget-close:focus-visible { outline: 2px solid var(--crimson); outline-offset: 1px; }
.hk-widget-hidden { display: none !important; }
/* Editable widget title (2026-08-03, direct request: "ability to rename note
from note/note 1/2/3/4/5 to anything user wanted"). Opt-in per widget
(draggable.ts's editableTitle option) - only the 5 Notes cards use this.
Hover/focus affordance signals "click to rename" without any extra label
text; the [contenteditable="true"] state gets a visible field-like
treatment while actively renaming. */
.hk-widget-title-editable { cursor: text; border-radius: var(--radius-xs); padding: 2px 5px; margin: -2px -5px; }
.hk-widget-title-editable:hover { background: var(--state-hover); }
.hk-widget-title-editable:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 1px; }
.hk-widget-title-editable[contenteditable="true"] {
background: var(--card); color: var(--text-strong); outline: 2px solid var(--accent-text); outline-offset: 1px;
cursor: text; text-transform: none; letter-spacing: normal;
}
/* ---------- Prayer times ---------- */
.hk-prayer-list { display: flex; flex-direction: column; gap: 2px; }
.hk-prayer-row {
display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
padding: 7px 0; border-bottom: 1px solid var(--border-hint); font-size: 12.5px;
}
.hk-prayer-row:last-child { border-bottom: none; }
.hk-prayer-name { font-family: var(--font-heading); color: var(--text); }
.hk-prayer-time { font-family: var(--font-mono); color: var(--stone); font-variant-numeric: tabular-nums; }
.hk-prayer-row.next { background: var(--state-hover); border-radius: var(--radius-sm); padding-left: 8px; padding-right: 8px; margin: 0 -8px; }
.hk-prayer-row.next .hk-prayer-name { color: var(--accent-text); font-weight: 600; }
.hk-prayer-row.next .hk-prayer-time { color: var(--accent-text); }
.hk-prayer-row.voluntary .hk-prayer-name::after {
content: "recommended window"; margin-left: 8px; font-size: 9.5px; color: var(--stone);
font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 400;
}
.hk-prayer-note { margin-top: var(--space-3); font-size: 11px; color: var(--stone); line-height: 1.6; }
/* ---------- Time window / best-time-for (real content: Adam's Adaptive
Daily OS, Six-Window ROI Map) ---------- */
.hk-window-list { display: flex; flex-direction: column; gap: 8px; }
.hk-window-row {
display: flex; flex-direction: column; gap: 4px; padding: 8px 10px;
border-radius: var(--radius-sm); border: 1px solid transparent; font-size: 12px;
transition: background-color var(--duration-short) var(--ease-standard), border-color var(--duration-short) var(--ease-standard);
}
.hk-window-row.current { background: var(--state-hover); border-color: var(--border-hint); }
.hk-window-row.current .hk-window-name { color: var(--accent-text); }
.hk-window-row-top { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.hk-window-name { font-family: var(--font-heading); font-weight: 600; color: var(--text-strong); }
.hk-window-range { font-family: var(--font-mono); font-size: 10.5px; color: var(--stone); flex-shrink: 0; }
.hk-window-suggestion { font-size: 11.5px; color: var(--text); line-height: 1.55; }
.hk-window-suggestion b { color: var(--text-strong); font-family: var(--font-heading); }
/* ---------- Cognitive Currents / Unplanned Activity Check (Layer 3 of
Adam's Adaptive Daily OS, 2026-08-02, direct request: "just introduce set
of new cards then" - the mode-switch engine underneath the six windows,
built as its own pair of cards since it runs on a mood/vibe cue rather
than an hour range) ---------- */
.hk-current-toggle { display: flex; flex-direction: column; gap: 8px; margin: var(--space-3) 0; }
.hk-current-btn {
display: flex; flex-direction: column; gap: 3px; text-align: left;
background: var(--card-alt); border: 1px solid var(--border-hint); border-radius: var(--radius-md);
padding: 10px var(--space-3); cursor: pointer; color: var(--text);
transition: border-color var(--duration-short) var(--ease-standard), background-color var(--duration-short) var(--ease-standard);
}
.hk-current-btn b { font-family: var(--font-heading); font-size: 12.5px; color: var(--text-strong); }
.hk-current-btn span { font-size: 11.5px; color: var(--stone); line-height: 1.5; }
.hk-current-btn:hover { border-color: var(--accent-text); }
.hk-current-btn.active { border-color: var(--accent-text); background: var(--state-hover); }
.hk-current-btn.active b { color: var(--accent-text); }
.hk-current-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--state-focus), 0 0 0 1px var(--accent-text); }
.hk-cue-note { margin-top: var(--space-2); }
.hk-activity-checklist { display: flex; flex-direction: column; gap: 8px; margin: var(--space-3) 0; }
.hk-activity-check-row { display: flex; align-items: flex-start; gap: 8px; font-size: 12px; color: var(--text); cursor: pointer; line-height: 1.5; }
.hk-activity-check-row input { margin-top: 2px; accent-color: var(--accent-text); flex-shrink: 0; }
.hk-activity-verdict { min-height: 16px; font-size: 11.5px; color: var(--stone); margin-bottom: var(--space-2); font-family: var(--font-body); }
.hk-activity-verdict.fits { color: var(--accent-text); font-weight: 600; }
.hk-activity-note-row { display: flex; gap: 8px; margin-bottom: var(--space-3); }
.hk-activity-note-input {
flex: 1; background: var(--card-alt); border: 1px solid var(--border-hint); border-radius: var(--radius-sm);
padding: 8px 10px; color: var(--text); font-size: 12.5px; font-family: var(--font-body);
}
.hk-activity-note-input:focus-visible { outline: none; border-color: var(--accent-text); }
.hk-activity-log-btn { font-size: 12px; padding: 8px var(--space-3); white-space: nowrap; }
.hk-activity-list { display: flex; flex-direction: column; gap: 2px; max-height: 140px; overflow-y: auto; }
.hk-activity-entry {
display: flex; justify-content: space-between; gap: 10px; padding: 6px 0;
border-bottom: 1px solid var(--border-hint); font-size: 12px; color: var(--text);
}
.hk-activity-entry:last-child { border-bottom: none; }
.hk-activity-entry.fits .hk-meta { color: var(--accent-text); }
/* ---------- Notepad ---------- */
/* Row 1: Bold/Italic/Underline/Strikethrough + Clear (2026-08-03, direct
request, reference: a screenshot of the Windows Sticky Notes editing
toolbar - "adjust all sticky notes to have bold font, italic, underline,
strikethrough"). Same small-square-icon-button language as
.hk-widget-close. Clear stays in this row (not the scrollable swatch row
below) via margin-left:auto, so it's never scrolled out of reach. */
.hk-notepad-formatbar { display: flex; align-items: center; gap: 4px; margin-bottom: var(--space-2); }
.hk-notepad-format {
width: 24px; height: 22px; flex-shrink: 0; cursor: pointer;
border: 1px solid var(--border-hint); border-radius: var(--radius-xs);
background: var(--card-alt); color: var(--text-strong); font-size: 12px;
font-family: var(--font-heading); line-height: 1;
display: flex; align-items: center; justify-content: center;
transition: background-color var(--duration-short) var(--ease-standard), border-color var(--duration-short) var(--ease-standard);
}
.hk-notepad-format:hover { background: var(--state-hover); border-color: var(--accent-text); }
.hk-notepad-format:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--state-focus); }
.hk-notepad-format.hk-format-bold { font-weight: 700; }
.hk-notepad-format.hk-format-italic { font-style: italic; }
.hk-notepad-format.hk-format-underline { text-decoration: underline; }
.hk-notepad-format.hk-format-strike { text-decoration: line-through; }
.hk-notepad-clear {
margin-left: auto; background: none; border: none; color: var(--stone); cursor: pointer;
font-size: 10.5px; font-family: var(--font-mono); text-decoration: underline;
}
.hk-notepad-clear:hover { color: var(--crimson); }
/* Row 2: highlight swatches - a "no color" de-select swatch (direct
request: "add an option to de-select color") plus a 24-color palette
(direct request: "add highlighter color to 24"), in a horizontally
scrolling strip rather than wrapping ("scrollable if cant fit") - same
overflow-x pattern as .hk-heatmap-scroll's week columns. */
.hk-notepad-toolbar {
display: flex; align-items: center; gap: 6px; margin-bottom: var(--space-3);
overflow-x: auto; padding-bottom: 4px;
}
.hk-highlight-swatch {
width: 20px; height: 20px; flex-shrink: 0; border-radius: var(--radius-full); cursor: pointer;
border: 2px solid transparent; padding: 0; transition: border-color var(--duration-short) var(--ease-standard), transform var(--duration-short) var(--ease-standard);
}
.hk-highlight-swatch:hover { transform: scale(1.15); }
.hk-highlight-swatch.active { border-color: var(--text-strong); }
.hk-highlight-swatch:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--state-focus); }
.hk-highlight-swatch.none {
background: var(--card); color: var(--stone); font-size: 12px; line-height: 1;
display: flex; align-items: center; justify-content: center; border: 2px solid var(--border-hint);
}
.hk-highlight-swatch.none.active { border-color: var(--text-strong); color: var(--text-strong); }
.hk-notepad-area {
flex: 1; min-height: 90px; overflow-y: auto; outline: none;
background: var(--card-alt); border: 1px solid var(--border-hint); border-radius: var(--radius-md);
padding: var(--space-3); font-size: 13px; line-height: 1.7; color: var(--text);
font-family: var(--font-body);
}
.hk-notepad-area:empty::before {
content: attr(data-placeholder); color: var(--stone); font-style: italic;
}
.hk-notepad-area mark { border-radius: 2px; padding: 0 1px; }
/* ---------- Heatmap (legend/level coloring, shared with the calendar
grid below) ---------- */
.hk-heatmap-caption { margin-bottom: var(--space-3); }
.hk-heatmap-scroll { display: flex; gap: 4px; overflow-x: auto; padding-bottom: 4px; }
.hk-heatmap-dow-col, .hk-heatmap-week { display: flex; flex-direction: column; gap: 3px; flex-shrink: 0; }
.hk-heatmap-month-slot {
height: 12px; font-size: 9.5px; color: var(--stone); font-family: var(--font-mono);
white-space: nowrap; margin-bottom: 2px;
}
.hk-heatmap-dow {
height: 11px; width: 24px; font-size: 9px; color: var(--stone); font-family: var(--font-mono);
display: flex; align-items: center;
}
.hk-heatmap-cell {
width: 11px; height: 11px; border-radius: var(--radius-xs);
background: var(--card-alt); border: 1px solid var(--border-hint);
transition: transform var(--duration-short) var(--ease-standard);
}
.hk-heatmap-cell.empty { background: transparent; border-color: transparent; }
.hk-heatmap-cell.level-0 { background: var(--card-alt); }
.hk-heatmap-cell.level-1 { background: rgba(200, 169, 110, 0.25); border-color: rgba(200, 169, 110, 0.25); }
.hk-heatmap-cell.level-2 { background: rgba(200, 169, 110, 0.48); border-color: rgba(200, 169, 110, 0.48); }
.hk-heatmap-cell.level-3 { background: rgba(200, 169, 110, 0.72); border-color: rgba(200, 169, 110, 0.72); }
.hk-heatmap-cell.level-4 { background: var(--ember); border-color: var(--ember); }
.hk-heatmap-cell:not(.empty):hover { transform: scale(1.4); }
.hk-heatmap-legend {
display: flex; align-items: center; gap: 4px; margin-top: var(--space-3);
font-size: 10px; color: var(--stone); font-family: var(--font-mono);
}
/* ---------- Calendar (Activity card rebuilt as a month grid, 2026-08-02,
direct feedback: "seems too long to scroll. do the calendar type
instead" - replaces the earlier GitHub-strip layout; cell coloring above
(.hk-heatmap-cell level-N + the Less->More legend) stays shared, only the
grid layout around it is new). ---------- */
.hk-calendar-caption { margin-bottom: var(--space-3); }
.hk-calendar-header {
display: flex; align-items: center; justify-content: space-between;
margin-bottom: var(--space-3);
}
.hk-calendar-month-label {
font-family: var(--font-heading); font-weight: 600; font-size: 13px; color: var(--text-strong);
}
.hk-calendar-nav {
background: none; border: 1px solid var(--border-hint); color: var(--text);
width: 26px; height: 26px; border-radius: var(--radius-sm); cursor: pointer;
font-size: 14px; line-height: 1; font-family: var(--font-heading);
transition: border-color var(--duration-short) var(--ease-standard), color var(--duration-short) var(--ease-standard);
}
.hk-calendar-nav:hover { border-color: var(--accent-text); color: var(--accent-text); }
.hk-calendar-nav:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--state-focus), 0 0 0 1px var(--accent-text); }
.hk-calendar-dow-row, .hk-calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); }
.hk-calendar-dow-row { gap: 2px; margin-bottom: 4px; }
.hk-calendar-dow {
text-align: center; font-size: 9.5px; color: var(--stone); font-family: var(--font-mono);
text-transform: uppercase; letter-spacing: 0.04em;
}
.hk-calendar-grid { gap: 3px; }
.hk-calendar-cell {
position: relative; width: auto; height: auto; aspect-ratio: 1;
border-radius: var(--radius-xs); padding: 3px 4px;
display: flex; align-items: flex-start; justify-content: flex-start;
transition: transform var(--duration-short) var(--ease-standard);
}
.hk-calendar-cell.hk-heatmap-cell:hover { transform: none; }
.hk-calendar-cell[role="button"] { cursor: pointer; }
.hk-calendar-cell[role="button"]:hover { transform: scale(1.06); z-index: 1; }
.hk-calendar-cell:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 1px; }
.hk-calendar-daynum { font-size: 10px; font-family: var(--font-mono); color: var(--stone); }
.hk-calendar-cell.level-3 .hk-calendar-daynum,
.hk-calendar-cell.level-4 .hk-calendar-daynum { color: var(--text-strong); }
.hk-drawer {
position: fixed; right: 25px; bottom: 25px; width: 380px; height: 420px;
max-width: calc(100vw - 40px); max-height: calc(100vh - 50px);
min-width: 260px; min-height: 200px;
overflow: auto; resize: both;
background: var(--card-alt); border: 1px solid var(--accent-text); border-radius: var(--radius-lg); padding: var(--space-5);
display: none; box-shadow: var(--elevation-4); z-index: 99;
}
.hk-drawer.active { display: block; animation: hk-rise-in var(--duration-medium) var(--ease-emphasized-decel) both; }
.hk-drawer-handle { cursor: grab; margin: calc(var(--space-5) * -1) calc(var(--space-5) * -1) var(--space-3); padding: var(--space-2) var(--space-5); }
.hk-drawer-handle:active { cursor: grabbing; }
.hk-close {
position: absolute; top: 14px; right: 18px; cursor: pointer; color: var(--crimson);
border-radius: var(--radius-xs);
transition: transform var(--duration-short) var(--ease-standard), color var(--duration-short) var(--ease-standard);
}
.hk-close:hover { transform: scale(1.15); }
.hk-close:focus-visible { outline: 2px solid var(--crimson); outline-offset: 2px; }
.hk-evidence-row {
display: flex; flex-direction: column; gap: 4px; padding: 10px 0;
border-bottom: 1px solid var(--border-hint); font-size: 12px; font-family: var(--font-body);
}
.hk-evidence-row:last-child { border-bottom: none; }
.hk-src { color: var(--accent-text); font-family: var(--font-mono); font-size: 11.5px; }
.hk-meta { color: var(--stone); font-size: 11px; font-family: var(--font-mono); }
.hk-lock-toast {
position: fixed; right: 25px; bottom: 25px; max-width: 340px;
background: var(--card-alt); border: 1px solid var(--crimson); border-radius: var(--radius-md);
padding: var(--space-4) var(--space-5); display: none; box-shadow: var(--elevation-4); z-index: 99;
font-size: 12.5px; color: var(--text);
}
.hk-lock-toast.active { display: block; animation: hk-rise-in var(--duration-medium) var(--ease-emphasized-decel) both; }
.hk-lock-toast-btn {
position: relative; overflow: hidden;
display: block; margin-top: 10px; background: transparent; border: 1px solid var(--accent-text);
color: var(--accent-text); padding: 6px var(--space-3); border-radius: var(--radius-sm); cursor: pointer; font-size: 11.5px;
font-family: var(--font-heading);
transition: background-color var(--duration-short) var(--ease-standard), transform var(--duration-short) var(--ease-standard);
}
.hk-lock-toast-btn:hover { background: var(--state-hover); }
.hk-lock-toast-btn:active { transform: scale(0.98); }
.hk-lock-toast-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--state-focus), 0 0 0 1px var(--accent-text); }
.hk-footer {
text-align: center; padding: var(--space-5) 0 var(--space-7); color: var(--stone); font-size: 11.5px;
border-top: 1px solid var(--border-hint); margin-top: var(--space-5); font-family: var(--font-mono);
}
.hk-footer a { color: var(--accent-text); text-decoration: none; }
.hk-footer a:hover { text-decoration: underline; }
.hk-clock {
margin-top: 6px; font-family: var(--font-mono); font-size: 11px; color: var(--stone);
font-variant-numeric: tabular-nums; letter-spacing: 0.02em;
}
.hk-visitor-count {
margin-top: 2px; font-family: var(--font-mono); font-size: 10px; color: var(--stone);
font-variant-numeric: tabular-nums; letter-spacing: 0.02em; opacity: 0.75;
}
@keyframes hk-rise-in {
from { opacity: 0; translate: 0 8px; }
to { opacity: 1; translate: 0 0; }
}
/* ---------- Ambient background particles ---------- */
.hk-particles { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.hk-particle {
position: absolute;
left: var(--hk-p-left); top: var(--hk-p-top);
width: var(--hk-p-size); height: var(--hk-p-size);
border-radius: 50%;
background: var(--accent-text);
box-shadow: 0 0 6px 1px var(--state-press);
animation:
hk-particle-drift calc(var(--hk-p-drift-base) * 1s / var(--hk-speed, 1)) ease-in-out var(--hk-p-delay) infinite alternate,
hk-particle-glow calc(var(--hk-p-glow-base) * 1s / var(--hk-speed, 1)) ease-in-out var(--hk-p-delay) infinite alternate;
}
@keyframes hk-particle-drift {
from { transform: translate(0, 0); }
to { transform: translate(var(--hk-p-dx), var(--hk-p-dy)); }
}
@keyframes hk-particle-glow {
from { opacity: 0.15; }
to { opacity: 0.85; }
}
/* ---------- Star-chase (second background layer, 2026-08-02, direct
request: "one more background animation of this moving stars have two
groups, chasing each other like [space]ships shooting lasers"). Each
streak is rotated once to its travel angle, then translateX moves it
"forward" along that rotated axis - no trig needed in CSS. Shares
--hk-speed with the particle field above, so one slider drives both. */
.hk-starchase { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.hk-starchase-streak {
position: absolute;
left: var(--hk-s-left); top: var(--hk-s-top);
width: var(--hk-s-length); height: 2px; border-radius: var(--radius-full);
transform: rotate(var(--hk-s-angle));
transform-origin: left center;
animation: hk-star-shoot calc(var(--hk-s-duration-base) * 1s / var(--hk-speed, 1)) linear var(--hk-s-delay) infinite;
}
.hk-starchase-a { background: linear-gradient(90deg, transparent, var(--ember)); box-shadow: 0 0 6px 1px var(--ember); }
.hk-starchase-b { background: linear-gradient(90deg, transparent, var(--sage)); box-shadow: 0 0 6px 1px var(--sage); }
@keyframes hk-star-shoot {
0% { transform: rotate(var(--hk-s-angle)) translateX(0); opacity: 0; }
8% { opacity: 1; }
85% { opacity: 1; }
100% { transform: rotate(var(--hk-s-angle)) translateX(var(--hk-s-distance)); opacity: 0; }
}
/* ---------- Sakura (third background layer, 2026-08-02, direct request:
"new background animation sakura flower with its own animation"). Its
"own" motion, distinct from the other two layers: petals fall top to
bottom with a side-to-side sway and a slow full rotation, rather than
drifting in place (particles) or shooting in a straight diagonal
(starchase). Shares --hk-speed with both. */
.hk-sakura { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.hk-sakura-petal {
position: absolute; top: -20px;
left: var(--hk-sk-left);
width: var(--hk-sk-size); height: calc(var(--hk-sk-size) * 0.75);
border-radius: 60% 60% 60% 5%;
background: hsl(340deg 75% 84%);
filter: hue-rotate(var(--hk-sk-hue));
box-shadow: 0 0 4px rgba(190, 90, 130, 0.35);
animation: hk-sakura-fall calc(var(--hk-sk-fall-base) * 1s / var(--hk-speed, 1)) linear var(--hk-sk-delay) infinite;
}
@keyframes hk-sakura-fall {
0% { transform: translate(0, 0) rotate(0deg); opacity: 0; }
8% { opacity: 0.9; }
25% { transform: translate(var(--hk-sk-sway), 27vh) rotate(90deg); }
50% { transform: translate(0, 54vh) rotate(180deg); }
75% { transform: translate(calc(var(--hk-sk-sway) * -1), 81vh) rotate(270deg); }
92% { opacity: 0.9; }
100% { transform: translate(0, 112vh) rotate(360deg); opacity: 0; }
}
/* ---------- Fireworks (fourth background layer, 2026-08-02, direct
request: "fireworks background animation, with how many colors slider
(1-24 variations)"). Unlike the other three ambient layers, a burst is a
one-shot event, not an infinite loop - fireworks.ts's spawnBurst() builds
each burst's DOM fresh, this animation plays once (no `infinite`), and a
matching setTimeout in fireworks.ts removes the element once it's done.
Each spark is rotated once to its travel angle, then translateX moves it
outward along that rotated axis - same trick as starchase.ts. Doesn't
share --hk-speed - burst pacing is a timer concern in fireworks.ts, not
an animation-duration one. */
.hk-fireworks { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.hk-firework-burst { position: absolute; left: var(--hk-fw-left); top: var(--hk-fw-top); width: 0; height: 0; }
.hk-firework-spark {
position: absolute; left: 0; top: 0;
width: 4px; height: 4px; border-radius: 50%;
background: var(--hk-fw-color);
box-shadow: 0 0 6px 1px var(--hk-fw-color);
transform: rotate(var(--hk-fw-angle));
transform-origin: 0 0;
animation: hk-firework-spark 1.1s ease-out both;
}
@keyframes hk-firework-spark {
0% { transform: rotate(var(--hk-fw-angle)) translateX(0); opacity: 1; }
70% { opacity: 0.9; }
100% { transform: rotate(var(--hk-fw-angle)) translateX(var(--hk-fw-distance)); opacity: 0; }
}
/* ---------- Mascot (purple 8-bit "cousin" character, 2026-08-02, direct
request: "avatars similar like a claude code... 8-bit style... treat it
like a cousin of the claude code avatar"). Roams the whole viewport
(position:fixed, set by mascot.ts), independent of the card canvas. ---- */
.hk-mascot {
position: fixed; right: 28px; bottom: 28px; width: 64px; height: 64px; z-index: 60;
cursor: grab; touch-action: none; user-select: none;
filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.35));
transition: filter var(--duration-short) var(--ease-standard);
}
.hk-mascot svg { display: block; width: 100%; height: 100%; }
.hk-mascot:hover { filter: drop-shadow(0 6px 16px rgba(0, 0, 0, 0.45)); }
.hk-mascot-dragging { cursor: grabbing; z-index: 61; }
.hk-mascot-falling { animation: hk-mascot-leaf-fall 1.1s var(--ease-emphasized-decel) both; }
@keyframes hk-mascot-leaf-fall {
0% { transform: translate(0, 0) rotate(0deg); }
20% { transform: translate(14px, calc(var(--hk-mascot-fall-distance) * 0.2)) rotate(12deg); }
40% { transform: translate(-16px, calc(var(--hk-mascot-fall-distance) * 0.45)) rotate(-14deg); }
60% { transform: translate(12px, calc(var(--hk-mascot-fall-distance) * 0.68)) rotate(10deg); }
80% { transform: translate(-8px, calc(var(--hk-mascot-fall-distance) * 0.88)) rotate(-6deg); }
100% { transform: translate(0, var(--hk-mascot-fall-distance)) rotate(0deg); }
}
/* ---------- Dog avatar (second roaming character, 2026-08-02, direct
request: "add a new avatar, a dog moving across screen with jumping
action"). Two nested animations on two elements, since one element's
`transform` can't cleanly combine two independently-timed motions: the
outer .hk-dog crosses the viewport and flips to face its direction of
travel; the inner .hk-dog-hop bounces continuously the whole time - the
"jumping action." Draggable (dog-avatar.ts): picking it up kills the
crossing animation and switches to plain left/top for the drag; releasing
restarts the crossing animation at whatever vertical lane (top) it was
dropped at, persisted so it keeps that lane on reload. */
.hk-dog {
position: fixed; bottom: 18px; left: 0; width: 56px; height: 40px; z-index: 55;
cursor: grab; touch-action: none; user-select: none;
animation: hk-dog-cross 16s linear infinite;
}
.hk-dog.hk-dog-dragging { cursor: grabbing; z-index: 61; animation: none; }
.hk-dog svg { display: block; width: 100%; height: 100%; }
.hk-dog-hop { animation: hk-dog-hop 0.5s ease-in-out infinite; }
@keyframes hk-dog-cross {
0% { transform: translateX(-70px) scaleX(1); }
48% { transform: translateX(calc(100vw + 10px)) scaleX(1); }
50% { transform: translateX(calc(100vw + 10px)) scaleX(-1); }
98% { transform: translateX(-70px) scaleX(-1); }
100% { transform: translateX(-70px) scaleX(1); }
}
@keyframes hk-dog-hop {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-9px); }
}
/* ---------- Cat (chases the dog above, src/dashboard/cat.ts). Autonomous
position is JS-driven (cat.ts's setInterval tick reads the dog's live
getBoundingClientRect and steps toward it), not a CSS keyframe, since the
target itself moves independently. The `transition` on left/top smooths
out the discrete per-tick jumps between ticks into continuous motion (same
step cap/easing still happens in JS - nextCatPosition - this is purely
visual interpolation, matching the tick interval so one transition
finishes right as the next tick starts). transform (the direction-facing
flip, applied by cat.ts) gets its own transition so turning around reads
as a turn, not a snap. Draggable: picking it up pauses the chase tick;
releasing resumes it from wherever it was dropped (cat.ts's `pos` is the
same variable the drag handlers and the tick both read/write, so this
needs no separate "resume" step). */
.hk-cat {
position: fixed; bottom: 18px; right: 40px; width: 56px; height: 36px; z-index: 54;
cursor: grab; touch-action: none; user-select: none;
transform-origin: center;
transition: left 0.1s linear, top 0.1s linear, transform 0.15s ease-out;
}
.hk-cat.hk-cat-dragging { cursor: grabbing; z-index: 61; transition: none; }
.hk-cat svg { display: block; width: 100%; height: 100%; }
/* Continuous bounce, independent of the outer element's JS-driven
position/flip - same split as .hk-dog-hop, smaller and quicker to read as
a lighter cat-sized gait rather than the dog's jump. */
.hk-cat-hop { animation: hk-cat-hop 0.4s ease-in-out infinite; }
@keyframes hk-cat-hop {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
/* ---------- Creeper-head (fourth roaming character, src/dashboard/
creeper.ts). Draggable via draggable.ts's makeDraggable, same as any
widget card - no hover/fall behavior, unlike the mascot. */
.hk-creeper {
position: fixed; left: 28px; bottom: 28px; width: 64px; height: 64px; z-index: 59;
cursor: grab; touch-action: none; user-select: none;
filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.35));
}
.hk-creeper svg { display: block; width: 100%; height: 100%; }
.hk-creeper.dragging { cursor: grabbing; z-index: 61; }
/* ---------- Scene taskbar (auto-hide left panel, 2026-08-02, direct
request: "auto-hide taskbar on the left... enable *again after close) &
or disable each cards from the scene"). Pure-CSS auto-hide via :hover /
:focus-within, plus a .open class (toggled by clicking the tab) so
touch/keyboard users - who have no hover state - can reach it too.
Fully auto-hidden at rest (2026-08-02, direct follow-up request: "adjust
left taskbar to be fully auto-hidden") - the resting sliver used to be a
visible 34px labeled tab; it's now a fully transparent ~6px hover zone
flush with the screen edge, with nothing rendered until the panel is
actually entered/focused/opened, at which point the tab widens and its
chrome fades in together with the panel sliding into view. The tab stays
a real, focusable <button> throughout, so :focus-within still reaches it
for keyboard users even though it's visually invisible at rest. ---- */
.hk-scene-panel {
position: fixed; top: 0; left: 0; height: 100vh; z-index: 70;
display: flex; align-items: stretch;
transform: translateX(calc(-100% + 6px));
transition: transform var(--duration-medium) var(--ease-emphasized-decel);
}
.hk-scene-panel:hover, .hk-scene-panel:focus-within, .hk-scene-panel.open { transform: translateX(0); }
.hk-scene-tab {
width: 6px; flex-shrink: 0; background: transparent; border: none;
color: transparent; cursor: pointer; font-size: 15px; writing-mode: vertical-rl;
transition: width var(--duration-short) var(--ease-standard),
background-color var(--duration-short) var(--ease-standard),
color var(--duration-short) var(--ease-standard);
}
.hk-scene-panel:hover .hk-scene-tab,
.hk-scene-panel:focus-within .hk-scene-tab,
.hk-scene-panel.open .hk-scene-tab {
width: 34px; background: var(--card); color: var(--stone); border-right: 1px solid var(--border-hint);
}
.hk-scene-tab:hover { color: var(--accent-text); }
.hk-scene-tab:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent-text); }
.hk-scene-panel-inner {
width: 240px; flex-shrink: 0; overflow-y: auto;
background: var(--card); border-right: 1px solid var(--border-hint);
padding: var(--space-5) var(--space-4); box-shadow: var(--elevation-3);
}
.hk-scene-speed-label {
display: block; font-size: 11px; color: var(--stone); font-family: var(--font-mono);
margin-bottom: var(--space-4); text-transform: uppercase; letter-spacing: 0.06em;
}
.hk-scene-speed-slider { display: block; width: 100%; margin-top: 8px; accent-color: var(--accent-text); }
.hk-scene-cards-heading { margin-top: var(--space-4); }
.hk-scene-card-list { display: flex; flex-direction: column; gap: 2px; }
.hk-scene-card-row {
display: flex; align-items: center; gap: 8px; padding: 6px 4px; border-radius: var(--radius-sm);
font-size: 12px; color: var(--text); cursor: pointer;
transition: background-color var(--duration-short) var(--ease-standard);
}
.hk-scene-card-row:hover { background: var(--state-hover); }
.hk-scene-card-row input { accent-color: var(--accent-text); flex-shrink: 0; }
/* Row drag grip (2026-08-03, direct request: "ability to sort cards &
avatar order on the taskbar") - vanilla pointer-driven reorder, same
glyph/style language as every other drag grip in the app (widget cards,
the evidence drawer). `.dragging` is applied to whichever row is
currently being moved, for the same lifted/lightened feedback
.hk-widget.dragging already gives a card being repositioned on the
canvas. */
.hk-scene-row-grip {
color: var(--stone); font-size: 11px; letter-spacing: 0.1em; font-family: var(--font-mono);
cursor: grab; flex-shrink: 0; touch-action: none;
}
.hk-scene-row-grip:active { cursor: grabbing; }
.hk-scene-card-row.dragging { opacity: 0.6; background: var(--state-hover); }
/* Footer slot (2026-08-03, direct request: quote widget "under footer of
the taskbar"). A hairline separator distinguishes it from the card/avatar
list above without needing its own heading label. */
.hk-scene-panel-footer {
margin-top: var(--space-5); padding-top: var(--space-4); border-top: 1px solid var(--border-hint);
}
.hk-quote-widget { display: flex; flex-direction: column; gap: 6px; }
.hk-quote-category-btn {
align-self: flex-start; background: none; border: none; cursor: pointer; padding: 2px 0;
font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent-text);
font-family: var(--font-mono);
}
.hk-quote-category-btn:hover { text-decoration: underline; }
.hk-quote-category-btn:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
.hk-quote-text {
font-size: 12.5px; line-height: 1.5; color: var(--text); font-family: var(--font-body);
white-space: pre-line; /* multi-line quotes use real \n, e.g. the sunnah-of-silence list */
}
.hk-quote-text.hk-quote-poetic { font-style: italic; color: var(--text-strong); }
/* ---------- Tab bar + Tab 2 shell (2026-08-03, second product tab:
Cognitive Supplement Dashboard, Phase 0 only - see supplements.ts's
header for the full scope). ---------- */
.hk-tab-hidden { display: none; }
.hk-tab-bar {
display: flex; gap: 2px; padding: var(--space-2) var(--gutter-sm) 0;
background: var(--bg); border-bottom: 1px solid var(--border-hint);
font-family: var(--font-heading);
}
@media (min-width: 900px) { .hk-tab-bar { padding-left: var(--gutter-lg); padding-right: var(--gutter-lg); } }
@media (min-width: 1440px) { .hk-tab-bar { padding-left: var(--gutter-xl); padding-right: var(--gutter-xl); } }
.hk-tab-btn {
appearance: none; cursor: pointer; border: 1px solid var(--border-hint); border-bottom: none;
background: var(--card-alt); color: var(--stone); font-size: 12.5px; font-weight: 500;
padding: 9px var(--space-5); border-radius: var(--radius-sm) var(--radius-sm) 0 0;
transition: background-color var(--duration-short) var(--ease-standard), color var(--duration-short) var(--ease-standard);
}
.hk-tab-btn:hover { color: var(--text-strong); }
.hk-tab-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--state-focus), 0 0 0 1px var(--accent-text); }
.hk-tab-btn.active { background: var(--bg); color: var(--accent-text); }
/* Corrected 2026-08-03 (direct feedback: "supplement tab got no feminine,
light & dark mode") - the previous pass forced this panel to a hardcoded
black regardless of the theme toggle, reasoning it as a deliberate third
visual mode. That reasoning is retired: tab 2 now follows the SAME
data-theme cycle as tab 1 (dark/light/feminine) by simply not overriding
any themed variable - var(--bg)/var(--card)/var(--accent-text)/etc. all
fall through to whatever [data-theme] currently has them set to at
<html>. Under the default dark theme this still reads as a near-black
surface (Deep Charcoal, --bg's dark value), which is what "black
background" meant in the original request - it just isn't locked there
anymore once the visitor switches modes. */
.hk-tab2-panel {
background: var(--bg);
min-height: 100vh;
}
.hk-tab2-canvas { min-height: 600px; }
/* ---------- Supplement card (Phase 0 only - supplement-card.ts: a time the
user sets, a "taken today" checkbox, nothing else - no slider, no
drag-and-drop, no rules engine, no status effects, no graph). ---------- */
.hk-supplement-card { display: flex; flex-direction: column; gap: var(--space-3); }
.hk-supplement-info {
font-size: 11.5px; color: var(--text); line-height: 1.5;
padding-bottom: var(--space-2); border-bottom: 1px solid var(--border-hint);
}
.hk-supplement-time-row {
display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
font-size: 12.5px; color: var(--text); cursor: pointer;
}
.hk-supplement-time-input {
background: var(--card-alt); border: 1px solid var(--border-hint); border-radius: var(--radius-sm);
color: var(--text-strong); font-family: var(--font-mono); font-size: 12.5px; padding: 6px 8px;
}
.hk-supplement-time-input:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--state-focus), 0 0 0 1px var(--accent-text); }
.hk-supplement-note { font-size: 11px; color: var(--stone); line-height: 1.5; }
.hk-supplement-check-row {
display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--text); cursor: pointer;
padding-top: var(--space-2); border-top: 1px solid var(--border-hint);
}
.hk-supplement-check-row input { accent-color: var(--accent-text); flex-shrink: 0; width: 15px; height: 15px; }
/* ---------- Acute supplement card variant (Founder Override - currently
only L-Theanine's card uses these; see supplements.ts's header). ---------- */
.hk-supplement-duration-row {
display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
font-size: 12.5px; color: var(--text); cursor: pointer;
}
.hk-supplement-duration-input {
background: var(--card-alt); border: 1px solid var(--border-hint); border-radius: var(--radius-sm);
color: var(--text-strong); font-family: var(--font-mono); font-size: 12.5px; padding: 6px 8px; width: 70px;
}
.hk-supplement-duration-input:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--state-focus), 0 0 0 1px var(--accent-text); }
.hk-supplement-duration-input:disabled { opacity: 0.6; cursor: not-allowed; }
.hk-supplement-countdown {
font-family: var(--font-mono); font-size: 13px; color: var(--accent-text);
font-variant-numeric: tabular-nums; min-height: 1.2em;
}
.hk-supplement-acute-actions { display: flex; gap: var(--space-2); }
.hk-supplement-start-btn, .hk-supplement-cancel-btn {
flex: 1; padding: 7px var(--space-3); border-radius: var(--radius-sm); cursor: pointer;
font-size: 12px; font-family: var(--font-heading);
transition: background-color var(--duration-short) var(--ease-standard), border-color var(--duration-short) var(--ease-standard), color var(--duration-short) var(--ease-standard);
}
.hk-supplement-start-btn { background: transparent; border: 1px solid var(--accent-text); color: var(--accent-text); }
.hk-supplement-start-btn:hover { background: var(--state-hover); }
.hk-supplement-cancel-btn { background: transparent; border: 1px solid var(--border-hint); color: var(--stone); }
.hk-supplement-cancel-btn:hover { border-color: var(--crimson); color: var(--crimson); }
.hk-supplement-start-btn:focus-visible, .hk-supplement-cancel-btn:focus-visible {
outline: none; box-shadow: 0 0 0 3px var(--state-focus), 0 0 0 1px var(--accent-text);
}
/* ---------- Reminder toast (reminder.ts) - page-level, stacks bottom-right,
same visual language as .hk-lock-toast/.hk-drawer (card-alt surface,
accent border, .hk-close reused unmodified - see its own rule above).
The container's own pointer-events are disabled so its unused space
never blocks clicks on whatever's underneath; only actual toasts (sized
to their own content) re-enable it. Plain column direction + bottom-
anchored container means each newly-appended toast lands nearest the
anchor point and earlier ones get pushed upward - no reverse needed. ---------- */
.hk-reminder-toast-container {
position: fixed; bottom: 25px; right: 25px; z-index: 100;
display: flex; flex-direction: column; gap: var(--space-3);
max-width: 320px; pointer-events: none;
}
.hk-reminder-toast {
position: relative; pointer-events: auto;
background: var(--card-alt); border: 1px solid var(--accent-text); border-radius: var(--radius-md);
padding: var(--space-4) var(--space-6) var(--space-4) var(--space-4);
box-shadow: var(--elevation-4); font-size: 12.5px; color: var(--text); font-family: var(--font-body);
animation: hk-rise-in var(--duration-medium) var(--ease-emphasized-decel) both;
}
@media (prefers-reduced-motion: reduce) {
:root { --duration-short: 0.01ms; --duration-medium: 0.01ms; }