git-watchtower 2.2.2 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -63,9 +63,11 @@ function getDashboardCss() {
63
63
  border-bottom: 1px solid rgba(255,255,255,0.08);
64
64
  user-select: none;
65
65
  box-shadow: 0 1px 8px rgba(0,0,0,0.3);
66
+ /* position:relative so .casino-reels-header (absolute) anchors here. */
66
67
  position: relative;
67
68
  z-index: 10;
68
69
  }
70
+ .header-icon { display: inline-block; }
69
71
  .header-left {
70
72
  display: flex;
71
73
  align-items: center;
@@ -113,7 +115,10 @@ function getDashboardCss() {
113
115
  .layout {
114
116
  display: grid;
115
117
  grid-template-columns: 1fr 320px;
116
- grid-template-rows: 1fr auto;
118
+ /* Row 1: branch panel + sidebar.
119
+ Row 2: dashboard-stats (full-width).
120
+ Row 3: keyboard-shortcut footer (full-width). */
121
+ grid-template-rows: 1fr auto auto;
117
122
  height: calc(100vh - 49px);
118
123
  min-height: 0;
119
124
  gap: 0;
@@ -759,24 +764,6 @@ function getDashboardCss() {
759
764
  .info-label { color: var(--text-muted); font-weight: 500; }
760
765
  .info-value { color: var(--text); font-family: var(--font-mono); }
761
766
 
762
- /* ── Session Stats (footer) ──────────────────────────────────── */
763
- .stats-bar {
764
- display: flex;
765
- gap: 16px;
766
- align-items: center;
767
- font-size: 11px;
768
- color: var(--text-muted);
769
- font-family: var(--font-mono);
770
- flex-wrap: wrap;
771
- }
772
- .stats-bar .stat-item {
773
- display: flex;
774
- align-items: center;
775
- gap: 4px;
776
- }
777
- .stats-bar .stat-value { color: var(--text-dim); font-weight: 600; }
778
- .stats-bar .stat-label { font-family: var(--font); }
779
-
780
767
  /* ── Cleanup Modal ───────────────────────────────────────────── */
781
768
  .cleanup-branch-list {
782
769
  display: flex;
@@ -909,172 +896,238 @@ function getDashboardCss() {
909
896
  .pref-btn:hover { background: var(--bg-surface-hover); color: var(--text-dim); border-color: var(--text-muted); }
910
897
  .pref-btn.active { background: var(--accent-dim); color: #fff; border-color: var(--accent-dim); }
911
898
 
912
- /* ── Session Stats Card (sidebar) ─────────────────────────────── */
913
- .session-stats-card {
914
- padding: 10px 16px 12px;
915
- border-bottom: 1px solid var(--border-subtle);
899
+ /* ── Dashboard Stats Bar (always-on, above the keyboard footer) ──
900
+ This is the canonical place for live session stats. The same
901
+ element re-skins to "casino winnings" when state.casinoModeEnabled
902
+ flips on, so users get the same row in both modes. */
903
+ .dashboard-stats {
904
+ grid-column: 1 / -1;
905
+ padding: 8px 20px;
906
+ background: var(--bg-surface);
907
+ border-top: 1px solid var(--border);
908
+ display: flex;
909
+ flex-wrap: wrap;
910
+ gap: 8px 24px;
911
+ align-items: center;
912
+ justify-content: space-between;
916
913
  font-size: 11px;
917
914
  color: var(--text-dim);
918
- display: grid;
919
- grid-template-columns: auto 1fr;
920
- gap: 4px 10px;
915
+ transition: background 0.25s, border-color 0.25s, box-shadow 0.25s;
916
+ }
917
+ .dashboard-stats .stats-group {
918
+ display: flex;
919
+ flex-wrap: wrap;
920
+ align-items: center;
921
+ gap: 14px;
922
+ }
923
+ /* Title pill anchors the bar — makes it obvious these are global
924
+ session metrics, not row-specific. */
925
+ .dashboard-stats .stats-title {
926
+ font-size: 10px;
927
+ font-weight: 800;
928
+ letter-spacing: 1.2px;
929
+ text-transform: uppercase;
930
+ color: var(--text-muted);
931
+ padding: 3px 10px;
932
+ border-radius: 999px;
921
933
  background: var(--bg);
934
+ border: 1px solid var(--border);
935
+ display: inline-flex;
936
+ align-items: center;
937
+ gap: 6px;
938
+ }
939
+ .dashboard-stats .stat {
940
+ display: inline-flex;
941
+ align-items: baseline;
942
+ gap: 6px;
943
+ white-space: nowrap;
922
944
  }
923
- .session-stats-card .stat-k {
945
+ .dashboard-stats .stat-k {
924
946
  color: var(--text-muted);
925
947
  text-transform: uppercase;
926
948
  letter-spacing: 0.6px;
927
949
  font-size: 10px;
928
950
  font-weight: 600;
929
- align-self: center;
930
951
  }
931
- .session-stats-card .stat-v {
952
+ .dashboard-stats .stat-v {
932
953
  color: var(--text);
933
954
  font-family: var(--font-mono);
934
955
  font-size: 12px;
935
- text-align: right;
956
+ font-weight: 600;
936
957
  }
937
- .session-stats-card .stat-v .sep { color: var(--text-muted); }
938
- .session-stats-card .stat-v .added { color: var(--green); }
939
- .session-stats-card .stat-v .deleted { color: var(--red); }
940
- .session-stats-card .stat-v .accent { color: var(--accent); }
958
+ .dashboard-stats .stat-v .added { color: var(--green); }
959
+ .dashboard-stats .stat-v .deleted { color: var(--red); }
960
+ .dashboard-stats .stat-v .sep { color: var(--text-muted); }
961
+ .dashboard-stats .stat-v .accent { color: var(--accent); }
962
+ /* Casino skin: same row, neon-pulsed, brighter title. */
963
+ .dashboard-stats.casino-mode {
964
+ background: linear-gradient(90deg, #1a0a24 0%, #2a0a36 50%, #1a0a24 100%);
965
+ border-top: 2px solid #ff2d7a;
966
+ box-shadow: inset 0 0 24px rgba(255, 45, 122, 0.25);
967
+ }
968
+ .dashboard-stats.casino-mode .stats-title {
969
+ color: #ffd400;
970
+ background: rgba(177, 0, 150, 0.4);
971
+ border-color: #ff2d7a;
972
+ text-shadow: 0 0 6px rgba(255, 220, 64, 0.6);
973
+ }
974
+ .dashboard-stats.casino-mode .stat-k { color: #ffd400; }
975
+ .dashboard-stats.casino-mode .stat-v { color: var(--text); }
976
+ .dashboard-stats.casino-mode .stat-v .pos { color: #3fb950; }
977
+ .dashboard-stats.casino-mode .stat-v .neg { color: #f85149; }
978
+ .dashboard-stats.casino-mode .stat-v .gold { color: #ffd400; }
979
+ .dashboard-stats.casino-mode .stat-v .neon { color: #29d4ff; }
980
+
981
+ /* ── Casino Mode ────────────────────────────────────────────────
982
+ Edge strips, header reskin, header reels, win/loss overlays. The
983
+ stats live in .dashboard-stats above; nothing floats over the
984
+ dashboard content anymore. */
941
985
 
942
- /* ── Casino Mode ──────────────────────────────────────────────── */
943
986
  .casino-layer {
944
987
  position: fixed;
945
988
  inset: 0;
946
989
  pointer-events: none;
947
990
  z-index: 90;
948
991
  opacity: 0;
949
- transition: opacity 0.3s;
992
+ visibility: hidden;
993
+ transition: opacity 0.25s;
994
+ }
995
+ body.casino-active .casino-layer {
996
+ opacity: 1;
997
+ visibility: visible;
950
998
  }
951
- body.casino-active .casino-layer { opacity: 1; }
952
999
 
953
- /* Marquee: neon border that cycles hues around the viewport. Pure CSS
954
- so we don't burn a JS timer for something a single keyframe can do. */
955
- .casino-marquee {
1000
+ /* Marquee: four solid neon strips at the viewport edges. Each strip
1001
+ pulses its own hue so the whole frame chases colours together. */
1002
+ .casino-edge {
956
1003
  position: absolute;
957
- inset: 0;
958
- border: 4px solid transparent;
959
- border-radius: 0;
960
- box-shadow:
961
- inset 0 0 24px rgba(255, 64, 180, 0.45),
962
- inset 0 0 60px rgba(255, 220, 64, 0.2);
963
- background:
964
- linear-gradient(var(--bg), var(--bg)) padding-box,
965
- conic-gradient(
966
- from 0deg,
967
- #ff2d7a, #ffd400, #30ff9c, #29d4ff, #b070ff, #ff2d7a
968
- ) border-box;
969
- animation: casino-marquee-spin 6s linear infinite;
970
- }
971
- @keyframes casino-marquee-spin {
972
- to { filter: hue-rotate(360deg); }
973
- }
974
- /* Running chase lights along the top and bottom edges */
975
- .casino-marquee::before,
976
- .casino-marquee::after {
1004
+ background: #ff2d7a;
1005
+ box-shadow: 0 0 18px rgba(255, 45, 122, 0.7);
1006
+ animation: casino-edge-pulse 0.9s ease-in-out infinite;
1007
+ overflow: hidden;
1008
+ }
1009
+ .casino-edge.top { top: 0; left: 0; right: 0; height: 8px; }
1010
+ .casino-edge.bottom { bottom: 0; left: 0; right: 0; height: 8px; animation-delay: 0.45s; }
1011
+ .casino-edge.left { top: 0; bottom: 0; left: 0; width: 8px; animation-delay: 0.225s; }
1012
+ .casino-edge.right { top: 0; bottom: 0; right: 0; width: 8px; animation-delay: 0.675s; }
1013
+ @keyframes casino-edge-pulse {
1014
+ 0% { background: #ff2d7a; box-shadow: 0 0 18px rgba(255, 45, 122, 0.7); }
1015
+ 25% { background: #ffd400; box-shadow: 0 0 18px rgba(255, 220, 64, 0.7); }
1016
+ 50% { background: #29d4ff; box-shadow: 0 0 18px rgba(41, 212, 255, 0.7); }
1017
+ 75% { background: #b070ff; box-shadow: 0 0 18px rgba(176, 112, 255, 0.7); }
1018
+ 100% { background: #ff2d7a; box-shadow: 0 0 18px rgba(255, 45, 122, 0.7); }
1019
+ }
1020
+
1021
+ /* Chase-light stripes horizontal pattern on top/bottom, vertical
1022
+ pattern on left/right, all flowing in opposite directions so the
1023
+ marquee reads as a closed loop. */
1024
+ .casino-edge::after {
977
1025
  content: '';
978
1026
  position: absolute;
979
- left: 0;
980
- right: 0;
981
- height: 6px;
1027
+ inset: 0;
1028
+ }
1029
+ .casino-edge.top::after,
1030
+ .casino-edge.bottom::after {
982
1031
  background-image: repeating-linear-gradient(
983
1032
  90deg,
984
- #ffd400 0 10px,
985
- transparent 10px 20px,
986
- #ff2d7a 20px 30px,
987
- transparent 30px 40px,
988
- #29d4ff 40px 50px,
989
- transparent 50px 60px
1033
+ rgba(255, 255, 255, 0.85) 0 8px,
1034
+ transparent 8px 24px
1035
+ );
1036
+ background-size: 24px 100%;
1037
+ animation: casino-chase-x 0.9s linear infinite;
1038
+ }
1039
+ .casino-edge.bottom::after { animation-direction: reverse; }
1040
+ .casino-edge.left::after,
1041
+ .casino-edge.right::after {
1042
+ background-image: repeating-linear-gradient(
1043
+ 0deg,
1044
+ rgba(255, 255, 255, 0.85) 0 8px,
1045
+ transparent 8px 24px
990
1046
  );
991
- background-size: 60px 6px;
992
- opacity: 0.85;
1047
+ background-size: 100% 24px;
1048
+ animation: casino-chase-y 0.9s linear infinite;
993
1049
  }
994
- .casino-marquee::before { top: 0; animation: casino-chase-right 1.2s linear infinite; }
995
- .casino-marquee::after { bottom: 0; animation: casino-chase-left 1.2s linear infinite; }
996
- @keyframes casino-chase-right { to { background-position: 60px 0; } }
997
- @keyframes casino-chase-left { to { background-position: -60px 0; } }
1050
+ .casino-edge.right::after { animation-direction: reverse; }
1051
+ @keyframes casino-chase-x { to { background-position: 24px 0; } }
1052
+ @keyframes casino-chase-y { to { background-position: 0 24px; } }
998
1053
 
999
- /* Flashing "MAX ADDICTION" header badge */
1000
- .casino-badge {
1001
- position: absolute;
1002
- top: 10px;
1003
- right: 140px;
1004
- padding: 3px 10px;
1005
- border-radius: 10px;
1006
- font-size: 10px;
1007
- font-weight: 800;
1008
- letter-spacing: 0.8px;
1009
- text-transform: uppercase;
1010
- color: #fff200;
1011
- background: linear-gradient(90deg, #b10096, #ff2d7a);
1012
- border: 1px solid rgba(255, 255, 255, 0.25);
1013
- box-shadow: 0 0 14px rgba(255, 45, 122, 0.6);
1014
- animation: casino-badge-flash 0.6s steps(2, end) infinite;
1015
- }
1016
- @keyframes casino-badge-flash {
1017
- 0%, 100% {
1018
- background: linear-gradient(90deg, #b10096, #ff2d7a);
1019
- color: #fff200;
1020
- }
1021
- 50% {
1022
- background: linear-gradient(90deg, #ffd400, #ff9a00);
1023
- color: #b10096;
1024
- }
1054
+ /* Header reskin: rainbow text + animated icon + slot reels in-place. */
1055
+ body.casino-active .header-text {
1056
+ background: linear-gradient(
1057
+ 90deg,
1058
+ #ff2d7a, #ffd400, #30ff9c, #29d4ff, #b070ff, #ff2d7a
1059
+ );
1060
+ background-size: 200% 100%;
1061
+ -webkit-background-clip: text;
1062
+ background-clip: text;
1063
+ -webkit-text-fill-color: transparent;
1064
+ color: transparent;
1065
+ animation: casino-rainbow-slide 3s linear infinite;
1066
+ /* Drop shadow with a coloured tint compensates for transparent fill. */
1067
+ filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.4));
1068
+ }
1069
+ @keyframes casino-rainbow-slide {
1070
+ to { background-position: 200% 0; }
1071
+ }
1072
+ body.casino-active .header-icon {
1073
+ display: inline-block;
1074
+ animation: casino-icon-spin 2.4s linear infinite;
1075
+ }
1076
+ @keyframes casino-icon-spin {
1077
+ 0%, 100% { filter: hue-rotate(0deg) drop-shadow(0 0 4px rgba(255, 220, 64, 0.6)); }
1078
+ 50% { filter: hue-rotate(180deg) drop-shadow(0 0 6px rgba(255, 45, 122, 0.8)); }
1025
1079
  }
1026
1080
 
1027
- /* Slot reels — sits just under the header */
1028
- .casino-reels {
1081
+ /* Slot reels — sit centred inside the header banner, hidden until
1082
+ casino mode is on. Sized small enough to fit in the existing
1083
+ header height without disturbing surrounding controls. */
1084
+ .casino-reels-header {
1029
1085
  position: absolute;
1030
- top: 57px;
1086
+ top: 50%;
1031
1087
  left: 50%;
1032
- transform: translateX(-50%) translateY(-20px);
1088
+ transform: translate(-50%, -50%);
1033
1089
  display: flex;
1034
- gap: 6px;
1035
- padding: 8px 14px;
1036
- background: linear-gradient(180deg, #1a1022, #120818);
1037
- border: 1px solid rgba(255, 255, 255, 0.15);
1038
- border-radius: 10px;
1039
- box-shadow:
1040
- 0 10px 24px rgba(0, 0, 0, 0.45),
1041
- 0 0 18px rgba(255, 45, 122, 0.35);
1090
+ gap: 4px;
1091
+ padding: 4px 8px;
1092
+ background: rgba(20, 8, 30, 0.9);
1093
+ border: 1px solid #ff2d7a;
1094
+ border-radius: 8px;
1095
+ box-shadow: 0 0 14px rgba(255, 45, 122, 0.55);
1042
1096
  opacity: 0;
1043
1097
  visibility: hidden;
1044
- transition: opacity 0.2s, transform 0.25s;
1098
+ pointer-events: none;
1099
+ transition: opacity 0.25s;
1100
+ z-index: 1;
1045
1101
  }
1046
- .casino-reels.active {
1102
+ body.casino-active .casino-reels-header {
1047
1103
  opacity: 1;
1048
1104
  visibility: visible;
1049
- transform: translateX(-50%) translateY(0);
1050
1105
  }
1051
1106
  .casino-reel {
1052
- width: 40px;
1053
- height: 40px;
1107
+ width: 26px;
1108
+ height: 26px;
1054
1109
  display: flex;
1055
1110
  align-items: center;
1056
1111
  justify-content: center;
1057
- font-size: 22px;
1112
+ font-size: 16px;
1058
1113
  line-height: 1;
1059
1114
  background: #fff;
1060
- border-radius: 6px;
1061
- box-shadow:
1062
- inset 0 -6px 10px rgba(0, 0, 0, 0.1),
1063
- inset 0 2px 4px rgba(0, 0, 0, 0.1);
1115
+ border-radius: 4px;
1116
+ box-shadow: inset 0 -3px 6px rgba(0, 0, 0, 0.12), inset 0 1px 2px rgba(0, 0, 0, 0.1);
1064
1117
  }
1065
- .casino-reels.spinning .casino-reel {
1118
+ .casino-reels-header.spinning .casino-reel {
1066
1119
  animation: casino-reel-blur 0.1s linear infinite;
1067
1120
  }
1068
- .casino-reels.spinning .casino-reel[data-reel="1"] { animation-delay: 0.03s; }
1069
- .casino-reels.spinning .casino-reel[data-reel="2"] { animation-delay: 0.06s; }
1070
- .casino-reels.spinning .casino-reel[data-reel="3"] { animation-delay: 0.09s; }
1071
- .casino-reels.spinning .casino-reel[data-reel="4"] { animation-delay: 0.12s; }
1121
+ .casino-reels-header.spinning .casino-reel[data-reel="1"] { animation-delay: 0.03s; }
1122
+ .casino-reels-header.spinning .casino-reel[data-reel="2"] { animation-delay: 0.06s; }
1123
+ .casino-reels-header.spinning .casino-reel[data-reel="3"] { animation-delay: 0.09s; }
1124
+ .casino-reels-header.spinning .casino-reel[data-reel="4"] { animation-delay: 0.12s; }
1072
1125
  @keyframes casino-reel-blur {
1073
- 0% { transform: translateY(-2px); filter: blur(0.6px); }
1074
- 50% { transform: translateY(2px); filter: blur(0.6px); }
1075
- 100% { transform: translateY(-2px); filter: blur(0.6px); }
1126
+ 0% { transform: translateY(-1.5px); filter: blur(0.6px); }
1127
+ 50% { transform: translateY(1.5px); filter: blur(0.6px); }
1128
+ 100% { transform: translateY(-1.5px); filter: blur(0.6px); }
1076
1129
  }
1077
- .casino-reels.win .casino-reel {
1130
+ .casino-reels-header.win .casino-reel {
1078
1131
  animation: casino-reel-winflash 0.24s steps(2, end) infinite;
1079
1132
  }
1080
1133
  @keyframes casino-reel-winflash {
@@ -1083,22 +1136,22 @@ function getDashboardCss() {
1083
1136
  }
1084
1137
  .casino-reel-label {
1085
1138
  position: absolute;
1086
- top: calc(100% + 6px);
1139
+ top: calc(100% + 4px);
1087
1140
  left: 50%;
1088
1141
  transform: translateX(-50%);
1089
- font-size: 11px;
1142
+ font-size: 10px;
1090
1143
  font-weight: 800;
1091
1144
  letter-spacing: 1.2px;
1092
1145
  text-transform: uppercase;
1093
- color: var(--text-dim);
1146
+ color: #ffd400;
1094
1147
  white-space: nowrap;
1095
- text-shadow: 0 0 10px currentColor;
1148
+ text-shadow: 0 0 8px currentColor;
1096
1149
  opacity: 0;
1097
1150
  transition: opacity 0.2s;
1098
1151
  }
1099
- .casino-reels.result .casino-reel-label { opacity: 1; }
1152
+ .casino-reels-header.result .casino-reel-label { opacity: 1; }
1100
1153
 
1101
- /* Centered win / loss overlay flashing banner */
1154
+ /* Centred win / loss overlay banner — solid, opaque, hard to miss. */
1102
1155
  .casino-overlay {
1103
1156
  position: absolute;
1104
1157
  top: 45%;
@@ -1110,17 +1163,18 @@ function getDashboardCss() {
1110
1163
  letter-spacing: 2px;
1111
1164
  text-transform: uppercase;
1112
1165
  color: #fff200;
1113
- background: linear-gradient(135deg, #b10096, #ff2d7a);
1114
- border: 3px solid rgba(255, 255, 255, 0.35);
1166
+ background: #b10096;
1167
+ border: 3px solid #ffd400;
1115
1168
  border-radius: 14px;
1116
1169
  box-shadow:
1117
1170
  0 0 40px rgba(255, 45, 122, 0.7),
1118
1171
  0 0 80px rgba(255, 220, 64, 0.4);
1119
- text-shadow: 0 2px 0 rgba(0,0,0,0.3);
1172
+ text-shadow: 0 2px 0 rgba(0, 0, 0, 0.3);
1120
1173
  opacity: 0;
1121
1174
  visibility: hidden;
1122
1175
  pointer-events: none;
1123
1176
  transition: opacity 0.15s, transform 0.2s;
1177
+ z-index: 3;
1124
1178
  }
1125
1179
  .casino-overlay.active {
1126
1180
  opacity: 1;
@@ -1132,76 +1186,30 @@ function getDashboardCss() {
1132
1186
  0%, 100% { filter: brightness(1); }
1133
1187
  50% { filter: brightness(1.35) saturate(1.3); }
1134
1188
  }
1135
- .casino-overlay.level-small { background: linear-gradient(135deg, #116b2a, #3fb950); }
1136
- .casino-overlay.level-medium { background: linear-gradient(135deg, #7a5600, #ffd400); color: #2a1200; }
1137
- .casino-overlay.level-large { background: linear-gradient(135deg, #ba6a00, #ff9a00); color: #2a1200; }
1138
- .casino-overlay.level-huge { background: linear-gradient(135deg, #7a00ba, #bc8cff); }
1189
+ .casino-overlay.level-small { background: #238636; border-color: #3fb950; }
1190
+ .casino-overlay.level-medium { background: #ffd400; color: #2a1200; border-color: #ff9a00; }
1191
+ .casino-overlay.level-large { background: #ff9a00; color: #2a1200; border-color: #ffd400; }
1192
+ .casino-overlay.level-huge { background: #7a00ba; color: #fff; border-color: #bc8cff; }
1139
1193
  .casino-overlay.level-jackpot {
1140
- background: linear-gradient(135deg, #007a82, #29d4ff);
1141
- color: #fff;
1194
+ background: #29d4ff;
1195
+ color: #04293a;
1196
+ border-color: #fff;
1142
1197
  animation-duration: 0.12s;
1143
1198
  }
1144
1199
  .casino-overlay.level-mega {
1145
- background: linear-gradient(135deg, #b10000, #ff2d2d);
1200
+ background: #b10000;
1201
+ color: #fff200;
1202
+ border-color: #ffd400;
1146
1203
  animation-duration: 0.08s;
1147
1204
  font-size: 40px;
1148
1205
  }
1149
1206
  .casino-overlay.loss {
1150
- background: linear-gradient(135deg, #2a0000, #b10000);
1207
+ background: #b10000;
1151
1208
  color: #fff;
1152
1209
  font-size: 26px;
1210
+ border-color: #ff2d2d;
1153
1211
  }
1154
1212
 
1155
- /* Floating stats panel. Slides in from the right when casino is on. */
1156
- .casino-stats-panel {
1157
- position: absolute;
1158
- right: 16px;
1159
- bottom: 56px;
1160
- width: 340px;
1161
- padding: 12px 14px;
1162
- background: linear-gradient(160deg, #1a0a24, #0f0616);
1163
- border: 2px solid #ff2d7a;
1164
- border-radius: 10px;
1165
- box-shadow:
1166
- 0 0 20px rgba(255, 45, 122, 0.45),
1167
- 0 8px 28px rgba(0, 0, 0, 0.6);
1168
- color: var(--text);
1169
- font-size: 12px;
1170
- transform: translateX(calc(100% + 32px));
1171
- transition: transform 0.35s cubic-bezier(0.2, 0.9, 0.3, 1.1);
1172
- pointer-events: auto;
1173
- }
1174
- body.casino-active .casino-stats-panel { transform: translateX(0); }
1175
- .casino-stats-panel .cstats-title {
1176
- font-weight: 800;
1177
- letter-spacing: 1px;
1178
- text-transform: uppercase;
1179
- font-size: 11px;
1180
- color: #ffd400;
1181
- margin-bottom: 8px;
1182
- text-align: center;
1183
- text-shadow: 0 0 8px rgba(255, 220, 64, 0.6);
1184
- }
1185
- .casino-stats-panel .cstats-row {
1186
- display: flex;
1187
- justify-content: space-between;
1188
- align-items: center;
1189
- padding: 3px 0;
1190
- border-bottom: 1px dashed rgba(255, 255, 255, 0.08);
1191
- font-family: var(--font-mono);
1192
- }
1193
- .casino-stats-panel .cstats-row:last-child { border-bottom: none; }
1194
- .casino-stats-panel .cstats-k {
1195
- color: var(--text-dim);
1196
- font-family: var(--font);
1197
- font-size: 11px;
1198
- }
1199
- .casino-stats-panel .cstats-v { font-weight: 700; }
1200
- .casino-stats-panel .cstats-v.pos { color: #3fb950; }
1201
- .casino-stats-panel .cstats-v.neg { color: #f85149; }
1202
- .casino-stats-panel .cstats-v.gold { color: #ffd400; }
1203
- .casino-stats-panel .cstats-v.neon { color: #29d4ff; }
1204
-
1205
1213
  @media (max-width: 900px) {
1206
1214
  }
1207
1215
  `;
@@ -12,10 +12,23 @@ function getDashboardHtml() {
12
12
  return `
13
13
  <div class="header">
14
14
  <div class="header-left">
15
- <span class="header-title">&#x1f3f0; Git Watchtower</span>
15
+ <span class="header-title">
16
+ <span class="header-icon" id="header-icon">&#x1f3f0;</span>
17
+ <span class="header-text">Git Watchtower</span>
18
+ </span>
16
19
  <span class="header-version" id="version"></span>
17
20
  <span class="header-project" id="project-name">-</span>
18
21
  </div>
22
+ <!-- Slot reels live in the header so they fit inside the blue banner
23
+ without dropping over the branch list. Hidden until casino-active. -->
24
+ <div class="casino-reels-header" id="casino-reels">
25
+ <div class="casino-reel" data-reel="0">&#x1f352;</div>
26
+ <div class="casino-reel" data-reel="1">&#x1f34b;</div>
27
+ <div class="casino-reel" data-reel="2">&#x1f34a;</div>
28
+ <div class="casino-reel" data-reel="3">&#x1f347;</div>
29
+ <div class="casino-reel" data-reel="4">&#x1f3b0;</div>
30
+ <div class="casino-reel-label" id="casino-reel-label"></div>
31
+ </div>
19
32
  <div class="header-right">
20
33
  <button class="notif-btn" id="notif-btn" title="Enable desktop notifications">notifications</button>
21
34
  <span class="badge" id="status-badge">connecting</span>
@@ -39,10 +52,14 @@ function getDashboardHtml() {
39
52
 
40
53
  <div class="side-panel" id="side-panel">
41
54
  <div class="panel-header">Activity Log <button class="sidebar-toggle" id="sidebar-toggle" title="Toggle sidebar">&#x25b6;</button></div>
42
- <div class="session-stats-card" id="session-stats-card"></div>
43
55
  <div class="activity-log" id="activity-log"></div>
44
56
  </div>
45
57
 
58
+ <!-- Permanent stats row, sits above the keyboard-shortcut footer.
59
+ Hosts grounded session stats by default; the same element re-skins
60
+ to casino-style winnings when state.casinoModeEnabled flips on. -->
61
+ <div class="dashboard-stats" id="dashboard-stats"></div>
62
+
46
63
  <div class="footer" id="footer">
47
64
  <span><kbd>j</kbd><kbd>k</kbd> navigate</span>
48
65
  <span><kbd>Enter</kbd> switch</span>
@@ -55,29 +72,22 @@ function getDashboardHtml() {
55
72
  <span><kbd>S</kbd> stash</span>
56
73
  <span><kbd>d</kbd> cleanup</span>
57
74
  <span><kbd>h</kbd> history</span>
75
+ <span><kbd>c</kbd> casino</span>
58
76
  <span><kbd>Esc</kbd> close</span>
59
- <span class="stats-bar" id="stats-bar"></span>
60
77
  </div>
61
78
  </div>
62
79
 
63
- <!-- Casino Mode overlay layer. Everything inside is hidden by default and
64
- only becomes visible when body has the .casino-active class (driven by
65
- state.casinoModeEnabled). Pointer-events are off so it never blocks
66
- clicks on the real dashboard underneath. -->
80
+ <!-- Casino Mode overlay layer viewport-edge effects only. The reels
81
+ and stats live inline in the dashboard now (header / bottom bar);
82
+ this layer is just the marquee strips and the centred win/loss
83
+ banners. Pointer-events stay off so it never blocks clicks. -->
67
84
  <div class="casino-layer" id="casino-layer">
68
- <div class="casino-marquee" id="casino-marquee"></div>
69
- <div class="casino-reels" id="casino-reels">
70
- <div class="casino-reel" data-reel="0">&#x1f3b0;</div>
71
- <div class="casino-reel" data-reel="1">&#x1f3b0;</div>
72
- <div class="casino-reel" data-reel="2">&#x1f3b0;</div>
73
- <div class="casino-reel" data-reel="3">&#x1f3b0;</div>
74
- <div class="casino-reel" data-reel="4">&#x1f3b0;</div>
75
- <div class="casino-reel-label" id="casino-reel-label"></div>
76
- </div>
85
+ <div class="casino-edge top"></div>
86
+ <div class="casino-edge bottom"></div>
87
+ <div class="casino-edge left"></div>
88
+ <div class="casino-edge right"></div>
77
89
  <div class="casino-overlay" id="casino-win-overlay"></div>
78
90
  <div class="casino-overlay loss" id="casino-loss-overlay"></div>
79
- <div class="casino-badge" id="casino-badge">&#x1f3b0; MAX ADDICTION &#x1f3b0;</div>
80
- <div class="casino-stats-panel" id="casino-stats-panel"></div>
81
91
  </div>
82
92
 
83
93
  <div class="flash" id="flash"></div>