@harshankur/viewcounter 3.1.0 → 3.2.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.
- package/.env.example +34 -10
- package/README.md +306 -136
- package/admin/css/admin.css +891 -198
- package/admin/index.html +13 -7
- package/admin/js/api.js +52 -6
- package/admin/js/appTabs.js +100 -0
- package/admin/js/charts.js +529 -189
- package/admin/js/constants.js +98 -9
- package/admin/js/dataTable.js +478 -0
- package/admin/js/format.js +58 -7
- package/admin/js/icons.js +168 -0
- package/admin/js/listbox.js +2 -1
- package/admin/js/logs.js +211 -60
- package/admin/js/main.js +201 -37
- package/admin/js/overview.js +905 -0
- package/admin/js/passwordPrompt.js +75 -0
- package/admin/js/table.js +12 -52
- package/admin/js/viewDialogs.js +30 -14
- package/admin/js/views.js +273 -207
- package/admin/locales/en.json +352 -63
- package/config/index.js +40 -5
- package/constants.js +126 -8
- package/db/AdminRepository.js +85 -159
- package/db/DatabaseManager.js +55 -7
- package/db/LogRepository.js +172 -35
- package/db/adminSchema.js +89 -4
- package/db/adminSessionStore.js +104 -0
- package/db/analysis.js +479 -0
- package/db/rejectionCounter.js +117 -0
- package/index.js +53 -19
- package/middleware/adminAuth.js +83 -43
- package/middleware/adminValidation.js +69 -3
- package/middleware/auth.js +2 -2
- package/middleware/security.js +26 -2
- package/middleware/validation.js +50 -2
- package/package.json +5 -2
- package/routes/admin.js +130 -22
- package/routes/analytics.js +197 -18
- package/tracker/tracker.js +191 -0
- package/utils/appIdUtils.js +1 -1
- package/utils/durationUtils.js +33 -0
- package/utils/errorUtils.js +4 -1
- package/utils/geoCity.js +87 -0
- package/utils/ipUtils.js +1 -1
- package/utils/privacyUtils.js +2 -2
- package/utils/referrerParser.js +23 -5
- package/utils/secretStore.js +1 -1
- package/utils/userAgentParser.js +52 -3
- package/utils/visitorContext.js +70 -0
- package/admin/js/insights.js +0 -192
package/admin/css/admin.css
CHANGED
|
@@ -233,6 +233,15 @@ textarea {
|
|
|
233
233
|
mix-blend-mode: overlay;
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
/* ---------- Icons ---------- */
|
|
237
|
+
|
|
238
|
+
/* Line icons take the size and colour of the text around them. */
|
|
239
|
+
.icon {
|
|
240
|
+
width: 1.1em;
|
|
241
|
+
height: 1.1em;
|
|
242
|
+
flex-shrink: 0;
|
|
243
|
+
}
|
|
244
|
+
|
|
236
245
|
/* ---------- Top bar ---------- */
|
|
237
246
|
|
|
238
247
|
.topbar {
|
|
@@ -281,12 +290,35 @@ textarea {
|
|
|
281
290
|
padding: 0.05rem 0.4rem;
|
|
282
291
|
}
|
|
283
292
|
|
|
293
|
+
.version-badge {
|
|
294
|
+
font-size: 0.72rem;
|
|
295
|
+
color: var(--text-muted);
|
|
296
|
+
font-variant-numeric: tabular-nums;
|
|
297
|
+
}
|
|
298
|
+
|
|
284
299
|
.topbar-actions {
|
|
285
300
|
display: flex;
|
|
286
301
|
align-items: center;
|
|
287
302
|
gap: 0.75rem;
|
|
288
303
|
}
|
|
289
304
|
|
|
305
|
+
.header-link {
|
|
306
|
+
display: inline-flex;
|
|
307
|
+
align-items: center;
|
|
308
|
+
gap: 0.4rem;
|
|
309
|
+
color: var(--text-muted);
|
|
310
|
+
font-size: 0.85rem;
|
|
311
|
+
font-weight: 600;
|
|
312
|
+
text-decoration: none;
|
|
313
|
+
padding: 0.35rem 0.5rem;
|
|
314
|
+
border-radius: var(--radius-small);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.header-link:hover {
|
|
318
|
+
color: var(--primary);
|
|
319
|
+
background: var(--row-hover);
|
|
320
|
+
}
|
|
321
|
+
|
|
290
322
|
.theme-switcher {
|
|
291
323
|
display: flex;
|
|
292
324
|
background: var(--code-bg);
|
|
@@ -321,12 +353,18 @@ textarea {
|
|
|
321
353
|
box-shadow: 0 0 12px var(--primary-glow);
|
|
322
354
|
}
|
|
323
355
|
|
|
356
|
+
.theme-btn .icon {
|
|
357
|
+
width: 16px;
|
|
358
|
+
height: 16px;
|
|
359
|
+
}
|
|
360
|
+
|
|
324
361
|
/* ---------- Layout ---------- */
|
|
325
362
|
|
|
363
|
+
/* Wide enough that a large screen shows more columns and wider charts. */
|
|
326
364
|
main {
|
|
327
|
-
max-width:
|
|
365
|
+
max-width: 1920px;
|
|
328
366
|
margin: 0 auto;
|
|
329
|
-
padding: 1.75rem 2rem
|
|
367
|
+
padding: 1.75rem 2rem 3rem;
|
|
330
368
|
}
|
|
331
369
|
|
|
332
370
|
/* ---------- Terminal window chrome ---------- */
|
|
@@ -733,7 +771,7 @@ textarea.input {
|
|
|
733
771
|
|
|
734
772
|
.tab {
|
|
735
773
|
display: inline-flex;
|
|
736
|
-
gap: 0.
|
|
774
|
+
gap: 0.5rem;
|
|
737
775
|
align-items: center;
|
|
738
776
|
padding: 0.7rem 1rem;
|
|
739
777
|
border: none;
|
|
@@ -745,8 +783,8 @@ textarea.input {
|
|
|
745
783
|
margin-bottom: -1px;
|
|
746
784
|
}
|
|
747
785
|
|
|
748
|
-
.tab .
|
|
749
|
-
opacity: 0.
|
|
786
|
+
.tab .icon {
|
|
787
|
+
opacity: 0.7;
|
|
750
788
|
}
|
|
751
789
|
|
|
752
790
|
.tab:hover {
|
|
@@ -758,7 +796,7 @@ textarea.input {
|
|
|
758
796
|
border-bottom-color: var(--primary);
|
|
759
797
|
}
|
|
760
798
|
|
|
761
|
-
.tab.active .
|
|
799
|
+
.tab.active .icon {
|
|
762
800
|
opacity: 1;
|
|
763
801
|
}
|
|
764
802
|
|
|
@@ -810,10 +848,6 @@ textarea.input {
|
|
|
810
848
|
transition: color 0.15s, border-color 0.15s, background-color 0.15s, box-shadow 0.2s;
|
|
811
849
|
}
|
|
812
850
|
|
|
813
|
-
.app-tab .prompt-char {
|
|
814
|
-
opacity: 0.4;
|
|
815
|
-
}
|
|
816
|
-
|
|
817
851
|
.app-tab:hover {
|
|
818
852
|
color: var(--text);
|
|
819
853
|
border-color: var(--border-strong);
|
|
@@ -826,10 +860,6 @@ textarea.input {
|
|
|
826
860
|
box-shadow: 0 0 12px var(--primary-glow);
|
|
827
861
|
}
|
|
828
862
|
|
|
829
|
-
.app-tab.active .prompt-char {
|
|
830
|
-
opacity: 1;
|
|
831
|
-
}
|
|
832
|
-
|
|
833
863
|
.app-tab.unavailable {
|
|
834
864
|
opacity: 0.55;
|
|
835
865
|
text-decoration: line-through;
|
|
@@ -854,95 +884,528 @@ textarea.input {
|
|
|
854
884
|
font-size: 0.85rem;
|
|
855
885
|
}
|
|
856
886
|
|
|
857
|
-
/*
|
|
858
|
-
|
|
859
|
-
.insights {
|
|
887
|
+
/* Each section says what it is and how it differs from the others. */
|
|
888
|
+
.panel-header {
|
|
860
889
|
display: flex;
|
|
861
890
|
flex-direction: column;
|
|
862
|
-
gap: 0.
|
|
863
|
-
transition: opacity 0.2s;
|
|
891
|
+
gap: 0.3rem;
|
|
864
892
|
}
|
|
865
893
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
opacity: 0.55;
|
|
894
|
+
.panel-title {
|
|
895
|
+
font-size: 1.25rem;
|
|
869
896
|
}
|
|
870
897
|
|
|
871
|
-
.
|
|
898
|
+
.panel-intro {
|
|
899
|
+
color: var(--text-muted);
|
|
900
|
+
font-size: 0.85rem;
|
|
901
|
+
max-width: 62rem;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
.filter-chips {
|
|
872
905
|
display: flex;
|
|
873
|
-
align-items: baseline;
|
|
874
|
-
gap: 0.9rem;
|
|
875
906
|
flex-wrap: wrap;
|
|
907
|
+
align-items: center;
|
|
908
|
+
gap: 0.4rem;
|
|
876
909
|
}
|
|
877
910
|
|
|
878
|
-
.
|
|
911
|
+
.filter-chips:empty {
|
|
912
|
+
display: none;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
.chip {
|
|
879
916
|
display: inline-flex;
|
|
880
917
|
align-items: center;
|
|
881
|
-
gap: 0.
|
|
918
|
+
gap: 0.35rem;
|
|
919
|
+
max-width: 22rem;
|
|
920
|
+
padding: 0.3rem 0.55rem;
|
|
921
|
+
font-size: 0.8rem;
|
|
922
|
+
font-weight: 600;
|
|
923
|
+
color: var(--primary);
|
|
924
|
+
background: var(--row-selected);
|
|
925
|
+
border: 1px solid var(--border-strong);
|
|
926
|
+
border-radius: 999px;
|
|
927
|
+
cursor: pointer;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
.chip:hover {
|
|
931
|
+
border-color: var(--primary);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
.chip-text {
|
|
935
|
+
overflow: hidden;
|
|
936
|
+
text-overflow: ellipsis;
|
|
937
|
+
white-space: nowrap;
|
|
938
|
+
min-width: 0;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
.link-btn {
|
|
942
|
+
display: inline-flex;
|
|
943
|
+
align-items: center;
|
|
944
|
+
gap: 0.3rem;
|
|
945
|
+
padding: 0;
|
|
882
946
|
border: none;
|
|
883
947
|
background: none;
|
|
948
|
+
color: var(--primary);
|
|
949
|
+
font-size: 0.82rem;
|
|
950
|
+
cursor: pointer;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
.link-btn:hover {
|
|
954
|
+
text-decoration: underline;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/* "How these are counted": definitions, folded away until wanted. */
|
|
958
|
+
.metric-help {
|
|
959
|
+
font-size: 0.82rem;
|
|
960
|
+
color: var(--text-muted);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
.metric-help summary {
|
|
884
964
|
cursor: pointer;
|
|
965
|
+
width: fit-content;
|
|
966
|
+
font-weight: 600;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
.metric-help summary:hover {
|
|
970
|
+
color: var(--text);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
.metric-help dl {
|
|
974
|
+
display: grid;
|
|
975
|
+
grid-template-columns: minmax(8rem, 12rem) minmax(0, 1fr);
|
|
976
|
+
gap: 0.4rem 1rem;
|
|
977
|
+
margin-top: 0.6rem;
|
|
978
|
+
max-width: 62rem;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
.metric-help dt {
|
|
982
|
+
color: var(--text);
|
|
983
|
+
font-weight: 600;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
.outcome-legend dt {
|
|
987
|
+
font-weight: 400;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/* ---------- Charts ---------- */
|
|
991
|
+
|
|
992
|
+
/* Headline numbers: label, value, change against the period before, sparkline. */
|
|
993
|
+
.stat-tiles {
|
|
994
|
+
display: grid;
|
|
995
|
+
grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
|
|
996
|
+
gap: 0.75rem;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/* The Overview's nine sit in even rows: all nine, five and four, three by
|
|
1000
|
+
three, or two by two, by the width they have, never seven and two. */
|
|
1001
|
+
.overview-tiles {
|
|
1002
|
+
container: tiles / inline-size;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
.overview-tiles .stat-tiles {
|
|
1006
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1007
|
+
gap: 0.6rem;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
@container tiles (width >= 34rem) {
|
|
1011
|
+
.overview-tiles .stat-tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
@container tiles (width >= 58rem) {
|
|
1015
|
+
.overview-tiles .stat-tiles { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
@container tiles (width >= 80rem) {
|
|
1019
|
+
.overview-tiles .stat-tiles { grid-template-columns: repeat(9, minmax(0, 1fr)); }
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
.stat-tile {
|
|
1023
|
+
display: flex;
|
|
1024
|
+
flex-direction: column;
|
|
1025
|
+
gap: 0.15rem;
|
|
1026
|
+
min-width: 0;
|
|
1027
|
+
padding: 0.85rem 1rem 0.7rem;
|
|
1028
|
+
font: inherit;
|
|
1029
|
+
color: inherit;
|
|
1030
|
+
text-align: left;
|
|
1031
|
+
background: var(--card-bg);
|
|
1032
|
+
border: 1px solid var(--border);
|
|
1033
|
+
border-radius: var(--radius-small);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
.stat-tile.selectable {
|
|
1037
|
+
cursor: pointer;
|
|
1038
|
+
transition: border-color 0.15s, box-shadow 0.2s;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
.stat-tile.selectable:hover {
|
|
1042
|
+
border-color: var(--border-strong);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
.stat-tile.active {
|
|
1046
|
+
border-color: var(--primary);
|
|
1047
|
+
box-shadow: inset 0 -2px 0 var(--primary), 0 0 12px var(--primary-glow);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
.stat-label {
|
|
1051
|
+
color: var(--text-muted);
|
|
1052
|
+
font-size: 0.78rem;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
.stat-value {
|
|
1056
|
+
font-size: 1.55rem;
|
|
885
1057
|
font-weight: 700;
|
|
886
|
-
|
|
887
|
-
|
|
1058
|
+
letter-spacing: -0.02em;
|
|
1059
|
+
font-variant-numeric: tabular-nums;
|
|
1060
|
+
white-space: nowrap;
|
|
1061
|
+
overflow: hidden;
|
|
1062
|
+
text-overflow: ellipsis;
|
|
888
1063
|
}
|
|
889
1064
|
|
|
890
|
-
.
|
|
1065
|
+
.stat-delta {
|
|
1066
|
+
display: flex;
|
|
1067
|
+
align-items: center;
|
|
1068
|
+
flex-wrap: wrap;
|
|
1069
|
+
gap: 0.25rem;
|
|
1070
|
+
min-height: 1.1rem;
|
|
1071
|
+
font-size: 0.74rem;
|
|
1072
|
+
color: var(--text-muted);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
.stat-delta-value {
|
|
1076
|
+
font-weight: 700;
|
|
1077
|
+
font-variant-numeric: tabular-nums;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/* Good and bad changes carry an arrow and a sign as well as a colour. */
|
|
1081
|
+
.stat-delta.tone-good .stat-delta-value,
|
|
1082
|
+
.stat-delta.tone-good .icon {
|
|
1083
|
+
color: var(--success);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
.stat-delta.tone-bad .stat-delta-value,
|
|
1087
|
+
.stat-delta.tone-bad .icon {
|
|
1088
|
+
color: var(--danger);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
.stat-delta .icon {
|
|
1092
|
+
width: 0.9rem;
|
|
1093
|
+
height: 0.9rem;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
.sparkline {
|
|
1097
|
+
display: block;
|
|
1098
|
+
width: 100%;
|
|
1099
|
+
height: 28px;
|
|
1100
|
+
/* On the tile's bottom edge, level across a row whatever the label's length. */
|
|
1101
|
+
margin-top: auto;
|
|
1102
|
+
padding-top: 0.3rem;
|
|
1103
|
+
overflow: visible;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
.sparkline-line {
|
|
1107
|
+
fill: none;
|
|
1108
|
+
stroke: var(--chart-series);
|
|
1109
|
+
stroke-width: 1.5;
|
|
1110
|
+
stroke-linejoin: round;
|
|
1111
|
+
stroke-linecap: round;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
.tracking-summary .sparkline {
|
|
1115
|
+
display: none;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/* The Overview */
|
|
1119
|
+
|
|
1120
|
+
.overview-content {
|
|
1121
|
+
display: flex;
|
|
1122
|
+
flex-direction: column;
|
|
1123
|
+
gap: 1rem;
|
|
1124
|
+
transition: opacity 0.2s;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/* Refetch keeps the frame: the last render stays, dimmed, until the new one. */
|
|
1128
|
+
.overview.loading .overview-content {
|
|
1129
|
+
opacity: 0.55;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
.overview-span {
|
|
891
1133
|
color: var(--text-muted);
|
|
892
1134
|
font-size: 0.8rem;
|
|
893
|
-
|
|
1135
|
+
margin-top: -0.4rem;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
.overview-top {
|
|
1139
|
+
display: grid;
|
|
1140
|
+
grid-template-columns: minmax(0, 2.2fr) minmax(0, 1fr);
|
|
1141
|
+
gap: 1rem;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
.overview-grid {
|
|
1145
|
+
display: grid;
|
|
1146
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1147
|
+
gap: 1rem;
|
|
1148
|
+
container: overview-grid / inline-size;
|
|
894
1149
|
}
|
|
895
1150
|
|
|
896
|
-
.
|
|
897
|
-
|
|
1151
|
+
.card-wide {
|
|
1152
|
+
grid-column: 1 / -1;
|
|
898
1153
|
}
|
|
899
1154
|
|
|
900
|
-
.
|
|
901
|
-
|
|
1155
|
+
.card-body {
|
|
1156
|
+
min-width: 0;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
.card-footer {
|
|
1160
|
+
margin-top: auto;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
.card-note {
|
|
902
1164
|
color: var(--text-muted);
|
|
1165
|
+
font-size: 0.75rem;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
.card-note a {
|
|
1169
|
+
color: inherit;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
.card-note a:hover {
|
|
1173
|
+
color: var(--primary);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
.chart-card-header .segmented {
|
|
1177
|
+
flex-wrap: wrap;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/* Ranked tables: the label with its bar behind it, then the measures. */
|
|
1181
|
+
|
|
1182
|
+
.rank-table {
|
|
1183
|
+
width: 100%;
|
|
1184
|
+
border-collapse: collapse;
|
|
1185
|
+
table-layout: fixed;
|
|
903
1186
|
font-size: 0.82rem;
|
|
904
1187
|
}
|
|
905
1188
|
|
|
906
|
-
.
|
|
1189
|
+
.rank-table th,
|
|
1190
|
+
.rank-table td {
|
|
1191
|
+
padding: 0.2rem 0.35rem;
|
|
1192
|
+
text-align: left;
|
|
1193
|
+
font-weight: 400;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
.rank-table thead th {
|
|
1197
|
+
color: var(--text-muted);
|
|
1198
|
+
font-size: 0.72rem;
|
|
1199
|
+
font-weight: 700;
|
|
1200
|
+
text-transform: uppercase;
|
|
1201
|
+
letter-spacing: 0.04em;
|
|
1202
|
+
padding-bottom: 0.35rem;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
.rank-table .rank-num {
|
|
1206
|
+
width: 5.2rem;
|
|
1207
|
+
text-align: right;
|
|
1208
|
+
font-variant-numeric: tabular-nums;
|
|
1209
|
+
white-space: nowrap;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
.rank-table thead .rank-num {
|
|
1213
|
+
white-space: normal;
|
|
1214
|
+
vertical-align: bottom;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
.rank-label-head {
|
|
1218
|
+
width: auto;
|
|
1219
|
+
vertical-align: bottom;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
.rank-label {
|
|
1223
|
+
padding-left: 0;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
.rank-select,
|
|
1227
|
+
.rank-static {
|
|
1228
|
+
position: relative;
|
|
907
1229
|
display: flex;
|
|
908
1230
|
flex-direction: column;
|
|
909
|
-
|
|
1231
|
+
justify-content: center;
|
|
1232
|
+
width: 100%;
|
|
1233
|
+
min-height: 1.9rem;
|
|
1234
|
+
padding: 0.2rem 1.6rem 0.2rem 0.5rem;
|
|
1235
|
+
font: inherit;
|
|
1236
|
+
color: var(--text);
|
|
1237
|
+
text-align: left;
|
|
1238
|
+
border: none;
|
|
1239
|
+
border-radius: 4px;
|
|
1240
|
+
background: transparent;
|
|
1241
|
+
overflow: hidden;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
.rank-select {
|
|
1245
|
+
cursor: pointer;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
.rank-bar {
|
|
1249
|
+
position: absolute;
|
|
1250
|
+
inset: 0 auto 0 0;
|
|
1251
|
+
background: var(--chart-series);
|
|
1252
|
+
opacity: 0.18;
|
|
1253
|
+
border-radius: 4px;
|
|
1254
|
+
pointer-events: none;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
.rank-text,
|
|
1258
|
+
.rank-detail {
|
|
1259
|
+
position: relative;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
.rank-detail {
|
|
1263
|
+
color: var(--text-muted);
|
|
1264
|
+
font-size: 0.72rem;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
.rank-filter-icon {
|
|
1268
|
+
position: absolute;
|
|
1269
|
+
right: 0.4rem;
|
|
1270
|
+
top: 50%;
|
|
1271
|
+
transform: translateY(-50%);
|
|
1272
|
+
color: var(--primary);
|
|
1273
|
+
opacity: 0;
|
|
1274
|
+
transition: opacity 0.15s;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
.rank-select:hover,
|
|
1278
|
+
.rank-select:focus-visible {
|
|
1279
|
+
background: var(--row-hover);
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
.rank-select:hover .rank-filter-icon,
|
|
1283
|
+
.rank-select:focus-visible .rank-filter-icon {
|
|
1284
|
+
opacity: 1;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
.rank-muted .rank-static,
|
|
1288
|
+
.rank-muted td {
|
|
1289
|
+
color: var(--text-muted);
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
.rank-muted .rank-bar {
|
|
1293
|
+
background: var(--text-muted);
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
/* Right now */
|
|
1297
|
+
|
|
1298
|
+
.live-card .chart-title {
|
|
1299
|
+
display: inline-flex;
|
|
1300
|
+
align-items: center;
|
|
1301
|
+
gap: 0.5rem;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
.live-dot {
|
|
1305
|
+
width: 0.55rem;
|
|
1306
|
+
height: 0.55rem;
|
|
1307
|
+
border-radius: 50%;
|
|
1308
|
+
background: var(--success);
|
|
1309
|
+
box-shadow: 0 0 0 0 var(--primary-glow);
|
|
1310
|
+
animation: pulse 2s ease-out infinite;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
.live-body {
|
|
1314
|
+
display: flex;
|
|
1315
|
+
flex-direction: column;
|
|
1316
|
+
gap: 0.8rem;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
.live-headline {
|
|
1320
|
+
display: flex;
|
|
1321
|
+
align-items: baseline;
|
|
1322
|
+
gap: 0.6rem;
|
|
1323
|
+
flex-wrap: wrap;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
.live-count {
|
|
1327
|
+
font-size: 2.2rem;
|
|
1328
|
+
font-weight: 700;
|
|
1329
|
+
letter-spacing: -0.03em;
|
|
1330
|
+
font-variant-numeric: tabular-nums;
|
|
1331
|
+
line-height: 1;
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
.live-caption {
|
|
1335
|
+
color: var(--text-muted);
|
|
1336
|
+
font-size: 0.8rem;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
.minute-svg {
|
|
1340
|
+
display: block;
|
|
1341
|
+
width: 100%;
|
|
1342
|
+
height: 56px;
|
|
1343
|
+
cursor: crosshair;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
.minute-svg:focus-visible {
|
|
1347
|
+
outline: 2px solid var(--primary);
|
|
1348
|
+
outline-offset: 3px;
|
|
1349
|
+
border-radius: 4px;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
.minute-bar {
|
|
1353
|
+
fill: var(--chart-series);
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
.minute-bar.active {
|
|
1357
|
+
fill: var(--primary-bright);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
.minute-axis {
|
|
1361
|
+
display: flex;
|
|
1362
|
+
justify-content: space-between;
|
|
1363
|
+
color: var(--text-muted);
|
|
1364
|
+
font-size: 0.7rem;
|
|
1365
|
+
margin-top: 0.2rem;
|
|
910
1366
|
}
|
|
911
1367
|
|
|
912
|
-
|
|
1368
|
+
/* Weekday by hour */
|
|
1369
|
+
|
|
1370
|
+
.heatmap {
|
|
913
1371
|
display: grid;
|
|
914
|
-
grid-template-columns: repeat(
|
|
915
|
-
gap:
|
|
1372
|
+
grid-template-columns: 3rem repeat(24, minmax(0, 1fr));
|
|
1373
|
+
gap: 2px;
|
|
1374
|
+
align-items: center;
|
|
916
1375
|
}
|
|
917
1376
|
|
|
918
|
-
.
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
border:
|
|
922
|
-
border-radius: var(--radius-small);
|
|
1377
|
+
.heatmap:focus-visible {
|
|
1378
|
+
outline: 2px solid var(--primary);
|
|
1379
|
+
outline-offset: 4px;
|
|
1380
|
+
border-radius: 4px;
|
|
923
1381
|
}
|
|
924
1382
|
|
|
925
|
-
.
|
|
1383
|
+
.heatmap-hour,
|
|
1384
|
+
.heatmap-day {
|
|
926
1385
|
color: var(--text-muted);
|
|
927
|
-
font-size: 0.
|
|
1386
|
+
font-size: 0.68rem;
|
|
1387
|
+
white-space: nowrap;
|
|
928
1388
|
}
|
|
929
1389
|
|
|
930
|
-
.
|
|
931
|
-
|
|
932
|
-
font-weight: 700;
|
|
933
|
-
letter-spacing: -0.02em;
|
|
1390
|
+
.heatmap-hour {
|
|
1391
|
+
overflow: visible;
|
|
934
1392
|
}
|
|
935
1393
|
|
|
936
|
-
.
|
|
937
|
-
display:
|
|
938
|
-
|
|
939
|
-
|
|
1394
|
+
.heatmap-cell {
|
|
1395
|
+
display: block;
|
|
1396
|
+
height: 1.35rem;
|
|
1397
|
+
border-radius: 3px;
|
|
940
1398
|
}
|
|
941
1399
|
|
|
942
|
-
.
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1400
|
+
.heatmap-cell.active {
|
|
1401
|
+
outline: 2px solid var(--text);
|
|
1402
|
+
outline-offset: -1px;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
.heatmap-body {
|
|
1406
|
+
display: flex;
|
|
1407
|
+
flex-direction: column;
|
|
1408
|
+
gap: 0.7rem;
|
|
946
1409
|
}
|
|
947
1410
|
|
|
948
1411
|
.chart-card {
|
|
@@ -1106,39 +1569,40 @@ textarea.input {
|
|
|
1106
1569
|
flex-shrink: 0;
|
|
1107
1570
|
}
|
|
1108
1571
|
|
|
1109
|
-
/*
|
|
1572
|
+
/* The country list beside the map */
|
|
1110
1573
|
|
|
1111
|
-
.bar-list,
|
|
1112
1574
|
.country-list {
|
|
1113
1575
|
display: flex;
|
|
1114
1576
|
flex-direction: column;
|
|
1115
|
-
gap: 0.
|
|
1577
|
+
gap: 0.2rem;
|
|
1116
1578
|
list-style: none;
|
|
1117
1579
|
}
|
|
1118
1580
|
|
|
1119
|
-
.bar-row,
|
|
1120
1581
|
.country-row {
|
|
1121
1582
|
display: grid;
|
|
1122
1583
|
grid-template-columns: minmax(0, 7.5rem) minmax(0, 1fr) auto;
|
|
1123
1584
|
align-items: center;
|
|
1124
1585
|
gap: 0.6rem;
|
|
1586
|
+
width: 100%;
|
|
1587
|
+
font: inherit;
|
|
1125
1588
|
font-size: 0.82rem;
|
|
1126
|
-
|
|
1589
|
+
color: inherit;
|
|
1590
|
+
text-align: left;
|
|
1591
|
+
padding: 0.2rem 0.3rem;
|
|
1592
|
+
border: none;
|
|
1127
1593
|
border-radius: 4px;
|
|
1128
|
-
|
|
1594
|
+
background: transparent;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
button.country-row {
|
|
1598
|
+
cursor: pointer;
|
|
1129
1599
|
}
|
|
1130
1600
|
|
|
1131
|
-
.bar-row:hover,
|
|
1132
|
-
.bar-row:focus-visible,
|
|
1133
1601
|
.country-row:hover,
|
|
1134
1602
|
.country-row:focus-visible {
|
|
1135
1603
|
background: var(--row-hover);
|
|
1136
1604
|
}
|
|
1137
1605
|
|
|
1138
|
-
.bar-other .bar-label {
|
|
1139
|
-
color: var(--text-muted);
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
1606
|
.bar-track {
|
|
1143
1607
|
display: block;
|
|
1144
1608
|
height: 10px;
|
|
@@ -1152,11 +1616,6 @@ textarea.input {
|
|
|
1152
1616
|
border-radius: 0 4px 4px 0;
|
|
1153
1617
|
}
|
|
1154
1618
|
|
|
1155
|
-
.bar-other .bar-fill {
|
|
1156
|
-
background: var(--text-muted);
|
|
1157
|
-
opacity: 0.6;
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
1619
|
.bar-value {
|
|
1161
1620
|
font-variant-numeric: tabular-nums;
|
|
1162
1621
|
color: var(--text);
|
|
@@ -1201,6 +1660,10 @@ textarea.input {
|
|
|
1201
1660
|
stroke-width: 2;
|
|
1202
1661
|
}
|
|
1203
1662
|
|
|
1663
|
+
.country.selectable {
|
|
1664
|
+
cursor: pointer;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1204
1667
|
.country.active {
|
|
1205
1668
|
stroke: var(--text);
|
|
1206
1669
|
stroke-width: 1;
|
|
@@ -1384,162 +1847,245 @@ input[type='checkbox'] {
|
|
|
1384
1847
|
cursor: pointer;
|
|
1385
1848
|
}
|
|
1386
1849
|
|
|
1387
|
-
.col-select { width: 2.6rem; }
|
|
1388
|
-
.col-time { width: 8.5rem; }
|
|
1389
|
-
.col-app { width: 8rem; }
|
|
1390
|
-
.col-page { width: auto; }
|
|
1391
|
-
.col-source { width: 9.5rem; }
|
|
1392
|
-
.col-device { width: 6.5rem; }
|
|
1393
|
-
.col-country { width: 5.5rem; }
|
|
1394
|
-
.col-client { width: 8rem; }
|
|
1395
|
-
.col-event { width: 7rem; }
|
|
1396
|
-
.col-status { width: 10.5rem; }
|
|
1397
|
-
.col-actions { width: 8.5rem; }
|
|
1398
|
-
|
|
1399
|
-
.log-table .col-action { width: 11rem; }
|
|
1400
|
-
.log-table .col-app { width: 9rem; }
|
|
1401
|
-
.log-table .col-rows { width: 5rem; }
|
|
1402
|
-
.log-table .col-fields { width: auto; }
|
|
1403
|
-
.log-table .col-session { width: 7rem; }
|
|
1404
|
-
.log-table .col-ip { width: 11rem; }
|
|
1405
|
-
.log-table .col-source { width: 8rem; }
|
|
1406
|
-
.log-table .col-event { width: 9rem; }
|
|
1407
|
-
.log-table .col-unique { width: 6rem; }
|
|
1408
|
-
.log-table .col-view { width: auto; }
|
|
1409
|
-
|
|
1410
1850
|
/*
|
|
1411
|
-
*
|
|
1412
|
-
*
|
|
1413
|
-
*
|
|
1414
|
-
*
|
|
1415
|
-
* leave the flexible column (page, fields, or view) at least 13rem:
|
|
1416
|
-
*
|
|
1417
|
-
* views, one app always 39.6 (select, time, source, status, actions);
|
|
1418
|
-
* + event 7 from 60, country 5.5 from 65.5,
|
|
1419
|
-
* device 6.5 from 72, client 8 from 80
|
|
1420
|
-
* views, All apps the same plus app 8, so each step comes 8rem later;
|
|
1421
|
-
* source also steps aside between the cards and 61
|
|
1422
|
-
* admin log always 45.5; + ip 11 from 57, session 7 from 64
|
|
1423
|
-
* view log always 40.5; + view ID 12 from 53
|
|
1851
|
+
* Columns (FRONTEND.md §12: reflow, never scroll sideways). dataTable.js
|
|
1852
|
+
* decides which of the admin's chosen columns fit the table's own width, in
|
|
1853
|
+
* their order, and sets their widths; what does not fit is under each row,
|
|
1854
|
+
* behind its "more" button. Below a card's width, each row is a card.
|
|
1424
1855
|
*/
|
|
1425
|
-
|
|
1426
|
-
|
|
1856
|
+
|
|
1857
|
+
.data-table th {
|
|
1858
|
+
position: relative;
|
|
1859
|
+
overflow: hidden;
|
|
1860
|
+
text-overflow: ellipsis;
|
|
1861
|
+
white-space: nowrap;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
.th-label {
|
|
1865
|
+
overflow: hidden;
|
|
1866
|
+
text-overflow: ellipsis;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
/* The draggable right edge of a column header. */
|
|
1870
|
+
.resize-handle {
|
|
1871
|
+
position: absolute;
|
|
1872
|
+
top: 0;
|
|
1873
|
+
right: 0;
|
|
1874
|
+
bottom: 0;
|
|
1875
|
+
width: 11px;
|
|
1876
|
+
cursor: col-resize;
|
|
1877
|
+
touch-action: none;
|
|
1427
1878
|
}
|
|
1428
1879
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1880
|
+
.resize-handle::after {
|
|
1881
|
+
content: '';
|
|
1882
|
+
position: absolute;
|
|
1883
|
+
top: 25%;
|
|
1884
|
+
bottom: 25%;
|
|
1885
|
+
right: 3px;
|
|
1886
|
+
width: 2px;
|
|
1887
|
+
border-radius: 1px;
|
|
1888
|
+
background: var(--border);
|
|
1889
|
+
transition: background-color 0.15s;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
.resize-handle:hover::after,
|
|
1893
|
+
.resize-handle:focus-visible::after,
|
|
1894
|
+
.resizing-column .resize-handle:active::after {
|
|
1895
|
+
background: var(--primary);
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
.resize-handle:focus-visible {
|
|
1899
|
+
outline: 2px solid var(--primary);
|
|
1900
|
+
outline-offset: -2px;
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
/* While an edge is dragged, the pointer and the text stay put. */
|
|
1904
|
+
body.resizing-column {
|
|
1905
|
+
cursor: col-resize;
|
|
1906
|
+
user-select: none;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
.data-table .col-select {
|
|
1910
|
+
padding-left: 0.6rem;
|
|
1911
|
+
padding-right: 0.3rem;
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
/* The "more" button's own 28px, and a little air. */
|
|
1915
|
+
.data-table .col-more {
|
|
1916
|
+
padding-left: 0.3rem;
|
|
1917
|
+
padding-right: 0.3rem;
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
.col-actions {
|
|
1921
|
+
white-space: nowrap;
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
/* What did not fit, under its row: labelled, in the admin's order. */
|
|
1925
|
+
.data-table tbody tr.row-more {
|
|
1926
|
+
height: auto;
|
|
1927
|
+
background: var(--code-bg);
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
.data-table tbody tr.row-more:hover {
|
|
1931
|
+
background: var(--code-bg);
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
.more-list {
|
|
1935
|
+
display: grid;
|
|
1936
|
+
grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
|
|
1937
|
+
gap: 0.6rem 1.25rem;
|
|
1938
|
+
padding: 0.2rem 0 0.3rem 2.4rem;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
.more-list dt {
|
|
1942
|
+
font-size: 0.68rem;
|
|
1943
|
+
font-weight: 700;
|
|
1944
|
+
text-transform: uppercase;
|
|
1945
|
+
letter-spacing: 0.06em;
|
|
1946
|
+
color: var(--text-muted);
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
.more-list dd {
|
|
1950
|
+
min-width: 0;
|
|
1951
|
+
margin-bottom: 0.1rem;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
/* Cards: below a table's width, each row stacks its fields, each labelled. */
|
|
1955
|
+
.data-table.cards thead {
|
|
1431
1956
|
display: none;
|
|
1432
1957
|
}
|
|
1433
1958
|
|
|
1434
|
-
|
|
1435
|
-
|
|
1959
|
+
.data-table.cards,
|
|
1960
|
+
.data-table.cards tbody,
|
|
1961
|
+
.data-table.cards tr,
|
|
1962
|
+
.data-table.cards td {
|
|
1963
|
+
display: block;
|
|
1964
|
+
width: 100%;
|
|
1436
1965
|
}
|
|
1437
1966
|
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1967
|
+
.data-table.cards tbody tr {
|
|
1968
|
+
height: auto;
|
|
1969
|
+
position: relative;
|
|
1970
|
+
padding: 0.6rem 0.8rem;
|
|
1971
|
+
border-bottom: 1px solid var(--border);
|
|
1441
1972
|
}
|
|
1442
1973
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1974
|
+
.data-table.cards td {
|
|
1975
|
+
border: none;
|
|
1976
|
+
padding: 0.15rem 0;
|
|
1445
1977
|
}
|
|
1446
1978
|
|
|
1447
|
-
|
|
1448
|
-
|
|
1979
|
+
.data-table.cards td[data-label] {
|
|
1980
|
+
display: grid;
|
|
1981
|
+
grid-template-columns: 6.5rem minmax(0, 1fr);
|
|
1982
|
+
column-gap: 0.75rem;
|
|
1983
|
+
/* From the top, not the baseline: a badge's baseline would otherwise make
|
|
1984
|
+
a card with badges a few pixels taller than one without. */
|
|
1985
|
+
align-items: start;
|
|
1449
1986
|
}
|
|
1450
1987
|
|
|
1451
|
-
|
|
1452
|
-
|
|
1988
|
+
.data-table.cards td[data-label]::before {
|
|
1989
|
+
content: attr(data-label);
|
|
1990
|
+
grid-row: 1 / span 2;
|
|
1991
|
+
padding-top: 0.2rem;
|
|
1992
|
+
font-size: 0.68rem;
|
|
1993
|
+
font-weight: 700;
|
|
1994
|
+
text-transform: uppercase;
|
|
1995
|
+
letter-spacing: 0.06em;
|
|
1996
|
+
color: var(--text-muted);
|
|
1453
1997
|
}
|
|
1454
1998
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1999
|
+
.data-table.cards td[data-label] > * {
|
|
2000
|
+
grid-column: 2;
|
|
1457
2001
|
}
|
|
1458
2002
|
|
|
1459
|
-
|
|
1460
|
-
|
|
2003
|
+
/* A badge keeps its own width; only text fills the value column. */
|
|
2004
|
+
.data-table.cards td[data-label] > .badge {
|
|
2005
|
+
justify-self: start;
|
|
1461
2006
|
}
|
|
1462
2007
|
|
|
1463
|
-
|
|
1464
|
-
|
|
2008
|
+
/* The left gutter of a views card holds its checkbox. */
|
|
2009
|
+
.views-table.cards tbody tr:not(.row-more) {
|
|
2010
|
+
padding-left: 2.8rem;
|
|
1465
2011
|
}
|
|
1466
2012
|
|
|
1467
|
-
|
|
1468
|
-
|
|
2013
|
+
.views-table.cards td.col-select {
|
|
2014
|
+
position: absolute;
|
|
2015
|
+
left: 0.8rem;
|
|
2016
|
+
top: 0.8rem;
|
|
2017
|
+
width: auto;
|
|
2018
|
+
padding: 0;
|
|
1469
2019
|
}
|
|
1470
2020
|
|
|
1471
|
-
|
|
1472
|
-
|
|
2021
|
+
/* A card's status line is full width, so every badge fits on one line;
|
|
2022
|
+
fixed, so a card with no badges is as tall as one with some. */
|
|
2023
|
+
.data-table.cards .badges {
|
|
2024
|
+
height: 1.4rem;
|
|
1473
2025
|
}
|
|
1474
2026
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
}
|
|
2027
|
+
.data-table.cards .col-more .btn {
|
|
2028
|
+
padding-left: 0;
|
|
2029
|
+
}
|
|
1479
2030
|
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
2031
|
+
.data-table.cards tr.row-more {
|
|
2032
|
+
padding-top: 0;
|
|
2033
|
+
}
|
|
1483
2034
|
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
}
|
|
2035
|
+
.data-table.cards .more-list {
|
|
2036
|
+
grid-template-columns: 6.5rem minmax(0, 1fr);
|
|
2037
|
+
column-gap: 0.75rem;
|
|
2038
|
+
row-gap: 0.3rem;
|
|
2039
|
+
padding: 0;
|
|
2040
|
+
}
|
|
1491
2041
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
position: relative;
|
|
1496
|
-
padding: 0.6rem 0.8rem 0.6rem 2.8rem;
|
|
1497
|
-
border-bottom: 1px solid var(--border);
|
|
1498
|
-
}
|
|
2042
|
+
.views-table.cards .more-list {
|
|
2043
|
+
padding-left: 2rem;
|
|
2044
|
+
}
|
|
1499
2045
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
2046
|
+
/* Choosing columns: a checkbox and two move buttons per column. */
|
|
2047
|
+
.column-chooser {
|
|
2048
|
+
list-style: none;
|
|
2049
|
+
display: flex;
|
|
2050
|
+
flex-direction: column;
|
|
2051
|
+
gap: 0.15rem;
|
|
2052
|
+
margin-bottom: 0.75rem;
|
|
2053
|
+
}
|
|
1504
2054
|
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
2055
|
+
.column-choice {
|
|
2056
|
+
display: grid;
|
|
2057
|
+
grid-template-columns: minmax(0, 1fr) auto auto;
|
|
2058
|
+
align-items: center;
|
|
2059
|
+
gap: 0.25rem;
|
|
2060
|
+
padding: 0.15rem 0.4rem;
|
|
2061
|
+
border-radius: 6px;
|
|
2062
|
+
}
|
|
1511
2063
|
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
.views-table .badges {
|
|
1515
|
-
height: 1.4rem;
|
|
1516
|
-
}
|
|
2064
|
+
.column-choice:hover {
|
|
2065
|
+
background: var(--row-hover);
|
|
1517
2066
|
}
|
|
1518
2067
|
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
2068
|
+
.column-choice-label {
|
|
2069
|
+
display: flex;
|
|
2070
|
+
align-items: center;
|
|
2071
|
+
gap: 0.6rem;
|
|
2072
|
+
min-width: 0;
|
|
2073
|
+
padding: 0.3rem 0;
|
|
2074
|
+
cursor: pointer;
|
|
2075
|
+
}
|
|
1524
2076
|
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
display: block;
|
|
1530
|
-
width: 100%;
|
|
1531
|
-
}
|
|
2077
|
+
.column-choice-note {
|
|
2078
|
+
color: var(--text-muted);
|
|
2079
|
+
font-size: 0.78rem;
|
|
2080
|
+
}
|
|
1532
2081
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
}
|
|
2082
|
+
.icon-btn:disabled {
|
|
2083
|
+
opacity: 0.3;
|
|
2084
|
+
pointer-events: none;
|
|
2085
|
+
}
|
|
1538
2086
|
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
padding: 0.15rem 0;
|
|
1542
|
-
}
|
|
2087
|
+
.toolbar-slot {
|
|
2088
|
+
display: inline-flex;
|
|
1543
2089
|
}
|
|
1544
2090
|
|
|
1545
2091
|
.cell-primary {
|
|
@@ -1597,6 +2143,7 @@ input[type='checkbox'] {
|
|
|
1597
2143
|
white-space: nowrap;
|
|
1598
2144
|
}
|
|
1599
2145
|
|
|
2146
|
+
.badge-success { color: var(--success); background: var(--row-selected); }
|
|
1600
2147
|
.badge-danger { color: var(--danger); background: var(--danger-bg); }
|
|
1601
2148
|
.badge-warning { color: var(--warning); background: var(--warning-bg); }
|
|
1602
2149
|
.badge-info { color: var(--info); background: var(--info-bg); }
|
|
@@ -1621,6 +2168,64 @@ input[type='checkbox'] {
|
|
|
1621
2168
|
font-size: 0.85rem;
|
|
1622
2169
|
}
|
|
1623
2170
|
|
|
2171
|
+
/* ---------- Footer ---------- */
|
|
2172
|
+
|
|
2173
|
+
.app-footer {
|
|
2174
|
+
max-width: 1920px;
|
|
2175
|
+
margin: 0 auto;
|
|
2176
|
+
padding: 1.5rem 2rem 2.5rem;
|
|
2177
|
+
border-top: 1px solid var(--border);
|
|
2178
|
+
color: var(--text-muted);
|
|
2179
|
+
font-size: 0.8rem;
|
|
2180
|
+
display: flex;
|
|
2181
|
+
flex-direction: column;
|
|
2182
|
+
gap: 0.6rem;
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
.footer-row {
|
|
2186
|
+
display: flex;
|
|
2187
|
+
flex-wrap: wrap;
|
|
2188
|
+
align-items: center;
|
|
2189
|
+
justify-content: space-between;
|
|
2190
|
+
gap: 0.75rem 1.5rem;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
.footer-copyright {
|
|
2194
|
+
color: var(--text);
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
.footer-links {
|
|
2198
|
+
display: flex;
|
|
2199
|
+
flex-wrap: wrap;
|
|
2200
|
+
gap: 0.4rem 1.1rem;
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
.footer-link {
|
|
2204
|
+
display: inline-flex;
|
|
2205
|
+
align-items: center;
|
|
2206
|
+
gap: 0.35rem;
|
|
2207
|
+
color: var(--text-muted);
|
|
2208
|
+
text-decoration: none;
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
.footer-link:hover {
|
|
2212
|
+
color: var(--primary);
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
.footer-legal {
|
|
2216
|
+
font-size: 0.75rem;
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
.footer-copyright a,
|
|
2220
|
+
.footer-credits a {
|
|
2221
|
+
color: inherit;
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
.footer-copyright a:hover,
|
|
2225
|
+
.footer-credits a:hover {
|
|
2226
|
+
color: var(--primary);
|
|
2227
|
+
}
|
|
2228
|
+
|
|
1624
2229
|
/* ---------- Modal (FRONTEND.md §1.1) ---------- */
|
|
1625
2230
|
|
|
1626
2231
|
#modal-root {
|
|
@@ -1678,6 +2283,20 @@ input[type='checkbox'] {
|
|
|
1678
2283
|
padding: 1rem 1.5rem 1.4rem;
|
|
1679
2284
|
}
|
|
1680
2285
|
|
|
2286
|
+
.detail-groups {
|
|
2287
|
+
display: flex;
|
|
2288
|
+
flex-direction: column;
|
|
2289
|
+
gap: 1.1rem;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
.detail-group-title {
|
|
2293
|
+
font-size: 0.75rem;
|
|
2294
|
+
text-transform: uppercase;
|
|
2295
|
+
letter-spacing: 0.06em;
|
|
2296
|
+
color: var(--secondary);
|
|
2297
|
+
margin-bottom: 0.45rem;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
1681
2300
|
.detail-list {
|
|
1682
2301
|
display: grid;
|
|
1683
2302
|
grid-template-columns: 11rem 1fr;
|
|
@@ -1782,6 +2401,12 @@ input[type='checkbox'] {
|
|
|
1782
2401
|
50% { opacity: 0; }
|
|
1783
2402
|
}
|
|
1784
2403
|
|
|
2404
|
+
@keyframes pulse {
|
|
2405
|
+
0% { box-shadow: 0 0 0 0 var(--primary-glow); }
|
|
2406
|
+
70% { box-shadow: 0 0 0 0.45rem transparent; }
|
|
2407
|
+
100% { box-shadow: 0 0 0 0 transparent; }
|
|
2408
|
+
}
|
|
2409
|
+
|
|
1785
2410
|
@keyframes fade-in {
|
|
1786
2411
|
from { opacity: 0; }
|
|
1787
2412
|
}
|
|
@@ -1809,15 +2434,34 @@ input[type='checkbox'] {
|
|
|
1809
2434
|
|
|
1810
2435
|
/* ---------- Narrow screens (FRONTEND.md §12: reflow, never scroll sideways) ---------- */
|
|
1811
2436
|
|
|
1812
|
-
|
|
1813
|
-
|
|
2437
|
+
/* The Overview reflows by its own width: two columns of cards, then one. */
|
|
2438
|
+
@container overview-grid (width < 56rem) {
|
|
2439
|
+
.overview-grid > .chart-card {
|
|
2440
|
+
grid-column: 1 / -1;
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
@media (max-width: 1100px) {
|
|
2445
|
+
.overview-top {
|
|
2446
|
+
grid-template-columns: minmax(0, 1fr);
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
.overview-grid {
|
|
1814
2450
|
grid-template-columns: minmax(0, 1fr);
|
|
1815
2451
|
}
|
|
1816
2452
|
}
|
|
1817
2453
|
|
|
1818
2454
|
@media (max-width: 960px) {
|
|
1819
2455
|
main {
|
|
1820
|
-
padding: 1.25rem 1rem
|
|
2456
|
+
padding: 1.25rem 1rem 3rem;
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
.app-footer {
|
|
2460
|
+
padding: 1.25rem 1rem 2rem;
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
.header-link span {
|
|
2464
|
+
display: none;
|
|
1821
2465
|
}
|
|
1822
2466
|
|
|
1823
2467
|
.topbar {
|
|
@@ -1869,7 +2513,56 @@ input[type='checkbox'] {
|
|
|
1869
2513
|
grid-template-columns: 1fr;
|
|
1870
2514
|
}
|
|
1871
2515
|
|
|
1872
|
-
.
|
|
2516
|
+
.metric-help dl {
|
|
2517
|
+
grid-template-columns: 1fr;
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
.logo-text,
|
|
2521
|
+
.version-badge {
|
|
2522
|
+
display: none;
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
.topbar {
|
|
2526
|
+
gap: 0.5rem;
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
.topbar-actions {
|
|
2530
|
+
gap: 0.4rem;
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
/* Sign out keeps its name for assistive technology, and shows its icon. */
|
|
2534
|
+
#logout-btn {
|
|
2535
|
+
padding: 0.4rem 0.55rem;
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
#logout-btn span {
|
|
2539
|
+
position: absolute;
|
|
2540
|
+
width: 1px;
|
|
2541
|
+
height: 1px;
|
|
2542
|
+
overflow: hidden;
|
|
2543
|
+
clip: rect(0 0 0 0);
|
|
2544
|
+
white-space: nowrap;
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
.rank-table .rank-share {
|
|
1873
2548
|
display: none;
|
|
1874
2549
|
}
|
|
2550
|
+
|
|
2551
|
+
.rank-table .rank-num {
|
|
2552
|
+
width: 4.2rem;
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2555
|
+
/* Hours stay readable: every sixth is labelled, cells get shorter. */
|
|
2556
|
+
.heatmap {
|
|
2557
|
+
grid-template-columns: 2.2rem repeat(24, minmax(0, 1fr));
|
|
2558
|
+
gap: 1px;
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
.heatmap-cell {
|
|
2562
|
+
height: 1rem;
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
.heatmap-hour:nth-child(6n + 5) {
|
|
2566
|
+
visibility: hidden;
|
|
2567
|
+
}
|
|
1875
2568
|
}
|