dsh-erpbot 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -29
- package/bot-identity.js +246 -0
- package/client.js +2438 -627
- package/cordis.patch.yml +128 -0
- package/docs/README.md +39 -0
- package/docs/acceptance.md +136 -0
- package/docs/architecture.md +161 -0
- package/docs/bot-agents-spec.md +205 -0
- package/docs/bot-collaboration-spec.md +161 -0
- package/docs/data-contract.md +262 -0
- package/docs/mcp-inventory-analysis.md +58 -0
- package/docs/requirements.md +234 -0
- package/index.js +270 -15
- package/package.json +6 -2
- package/scripts/check-bot-identity.mjs +71 -0
- package/scripts/lint-factories.mjs +315 -0
- package/scripts/lint-scopes.mjs +179 -0
- package/scripts/scaffold-bots.mjs +76 -0
- package/scripts/test-manifest.mjs +72 -0
- package/scripts/verify.mjs +159 -0
- package/spec/README.md +299 -0
- package/spec/base-content-spec.md +129 -0
- package/spec/cai-content-spec.md +257 -0
- package/spec/shui-content-spec.md +252 -0
- package/spec/spec-CORE.md +155 -0
- package/spec/tui-content-spec.md +259 -0
- package/spec/zi-content-spec.md +256 -0
- package/src/assistant/index.js +389 -4
- package/src/bot-data.js +25 -13
- package/src/bundle.json +1 -0
- package/src/cards/index.js +18 -4
- package/src/dashboard/index.js +65 -25
- package/src/entry.js +7 -7
- package/src/i18n/index.js +80 -60
- package/src/mount.js +19 -2
- package/src/nav.js +7 -5
- package/src/pages-data.js +29 -262
- package/src/shell/index.js +57 -33
- package/src/styles.js +396 -78
- package/src/workbench/index.js +1297 -85
package/src/styles.js
CHANGED
|
@@ -54,24 +54,27 @@
|
|
|
54
54
|
}
|
|
55
55
|
/* Only the primary icon group gives way in a short box; the settings/
|
|
56
56
|
language group at the bottom is always kept on screen instead of being
|
|
57
|
-
pushed out of the panel.
|
|
58
|
-
|
|
57
|
+
pushed out of the panel.
|
|
58
|
+
|
|
59
|
+
NOTE: this must NOT be ":first-of-type". The rail now opens with the brand
|
|
60
|
+
avatar slot, which is also a <div>, so ":first-of-type" would match the
|
|
61
|
+
brand instead of the button group and the scroll guard would silently
|
|
62
|
+
switch off — visible only as the button group pushing the wordmark out of
|
|
63
|
+
a short panel. */
|
|
64
|
+
.erp-app .t1-rail > .rail-group{
|
|
59
65
|
flex:0 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;
|
|
60
66
|
}
|
|
61
|
-
.erp-app .t1-rail > .rail-group
|
|
67
|
+
.erp-app .t1-rail > .rail-group::-webkit-scrollbar{width:0;height:0}
|
|
62
68
|
/* The bottom lockup (erpBot wordmark + 中/EN switch) is never squeezed out:
|
|
63
69
|
it keeps its natural height while only the icon group above may scroll. */
|
|
64
70
|
.erp-app .t1-rail .rail-foot{flex:none}
|
|
65
71
|
.erp-app .rail-spacer{flex:1 1 auto;min-height:0}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
rgba(198,203,225,.85) calc(100% - 34px),
|
|
73
|
-
rgba(198,203,225,0) 100%);
|
|
74
|
-
}
|
|
72
|
+
/* The vertical hairline between the rail and the content column is gone: the
|
|
73
|
+
rail already reads as its own column through the inset icon card and the
|
|
74
|
+
whitespace gutter, so the line was a redundant second edge. Kept as an
|
|
75
|
+
explicit display:none rather than deleting the rule, so the dark override
|
|
76
|
+
below stays paired with it. */
|
|
77
|
+
.erp-app .t1-rail::after{display:none}
|
|
75
78
|
.erp-app .rail-brand{
|
|
76
79
|
width:92px;flex:none;display:flex;align-items:center;justify-content:center;gap:5px;
|
|
77
80
|
white-space:nowrap;height:58px;
|
|
@@ -121,27 +124,6 @@
|
|
|
121
124
|
box-shadow:0 10px 18px -12px rgba(21,25,51,.95);
|
|
122
125
|
}
|
|
123
126
|
|
|
124
|
-
/* rail-top brand tile: the B mark, spanning the full width of the nav rail so
|
|
125
|
-
the brand block is exactly as wide as the icon column below it. */
|
|
126
|
-
.erp-app .rail-logo{
|
|
127
|
-
width:100%;flex:none;display:flex;align-items:center;justify-content:center;
|
|
128
|
-
margin-bottom:6px;
|
|
129
|
-
}
|
|
130
|
-
/* The B tile fills the rail's width: the brand block and the icon column below
|
|
131
|
-
share the same edges, so the mark reads as the header of the nav column. */
|
|
132
|
-
.erp-app .rail-logo .rail-mark{
|
|
133
|
-
width:100%;height:52px;flex:none;
|
|
134
|
-
display:grid;place-items:center;
|
|
135
|
-
border-radius:16px;
|
|
136
|
-
background:linear-gradient(140deg,#3f2fbe,#7c5cff 52%,#c48bfb);
|
|
137
|
-
color:#fff;font-size:26px;font-weight:800;line-height:1;letter-spacing:.02em;
|
|
138
|
-
box-shadow:0 10px 22px -12px rgba(94,72,232,.72);
|
|
139
|
-
}
|
|
140
|
-
.erp-app[data-theme='dark'] .rail-logo .rail-mark{
|
|
141
|
-
background:linear-gradient(140deg,#4a37d6,#8a68ff 52%,#cfa2ff);
|
|
142
|
-
box-shadow:0 10px 22px -12px rgba(0,0,0,.8);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
127
|
/* language switch: a two-segment pill (中 | EN) under the erpBot wordmark.
|
|
146
128
|
Both languages stay visible, the active segment is lit, and the inactive one
|
|
147
129
|
only reacts to hover. */
|
|
@@ -248,6 +230,13 @@
|
|
|
248
230
|
|
|
249
231
|
/* ---------------- stat cards ---------------- */
|
|
250
232
|
.erp-app .t1-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:13px}
|
|
233
|
+
.erp-app .mcp-bar{display:flex;align-items:center;gap:5px;padding:0 4px 9px;font-size:11px;color:var(--erp-dim)}
|
|
234
|
+
.erp-app .mcp-bar-label{opacity:.72}
|
|
235
|
+
.erp-app .mcp-dot{display:inline-flex;align-items:center}
|
|
236
|
+
.erp-app .mcp-dot .bot-dot{width:6px;height:6px;border-radius:50%;flex:none}
|
|
237
|
+
.erp-app .mcp-dot.ok .bot-dot{background:#4dba6a}
|
|
238
|
+
.erp-app .mcp-dot.warn .bot-dot{background:#eb943b}
|
|
239
|
+
.erp-app .mcp-dot.off .bot-dot{background:var(--erp-dim);opacity:.4}
|
|
251
240
|
.erp-app .stat{padding:13px 15px 11px;display:flex;flex-direction:column;gap:8px}
|
|
252
241
|
.erp-app .stat-top{display:flex;align-items:flex-start;justify-content:space-between;gap:6px}
|
|
253
242
|
.erp-app .stat-label{font-size:11.4px;font-weight:600;color:#8c93ac;letter-spacing:.005em}
|
|
@@ -551,10 +540,16 @@
|
|
|
551
540
|
inline-size container above), which is exactly the area the host hands us.
|
|
552
541
|
Width and height both get handled: narrow boxes reflow the grid, short boxes
|
|
553
542
|
shrink the fixed-height chart so the page still fits. */
|
|
554
|
-
@container erp (min-width:
|
|
555
|
-
/* Wide enough that the
|
|
543
|
+
@container erp (min-width:901px){
|
|
544
|
+
/* Wide enough that the board is still one screen: let the two content rows
|
|
556
545
|
share the leftover height instead of sitting at their natural size, so a
|
|
557
|
-
tall panel fills up and a short one compresses rather than being clipped.
|
|
546
|
+
tall panel fills up and a short one compresses rather than being clipped.
|
|
547
|
+
|
|
548
|
+
The floor used to be 1181px. Between 901 and 1180 the panel did NOT stack
|
|
549
|
+
(row 2 and row 3 were still side by side) yet the rows were plain "auto",
|
|
550
|
+
so any spare height just collected as dead space at the bottom of the
|
|
551
|
+
column — 191px of it at 1100x900. The split the rows need is the 900px
|
|
552
|
+
breakpoint below, not 1181, so the range moves with it. */
|
|
558
553
|
.erp-app .t1-main{
|
|
559
554
|
grid-template-rows:auto minmax(var(--erp-row2-min),1.42fr) minmax(var(--erp-row3-min),1fr);
|
|
560
555
|
}
|
|
@@ -756,8 +751,10 @@
|
|
|
756
751
|
.erp-app .rail-foot{gap:5px}
|
|
757
752
|
.erp-app .rail-lang{padding:1px}
|
|
758
753
|
.erp-app .rail-lang .lang-seg{min-width:21px;height:16px;padding:0 4px;font-size:8.2px}
|
|
759
|
-
|
|
760
|
-
|
|
754
|
+
/* short box: the avatar trades its breathing room for the icon column, but
|
|
755
|
+
keeps the ring — that ring is the only thing separating brand from a
|
|
756
|
+
navigation button, so it is the last thing to give up. */
|
|
757
|
+
.erp-app .rail-brand-slot{margin:0 0 6px}
|
|
761
758
|
}
|
|
762
759
|
/* The card art is the one element that cannot simply shrink: below a certain
|
|
763
760
|
height the 1.62:1 ratio squeezes it into a sliver too small to read. So when
|
|
@@ -793,16 +790,17 @@
|
|
|
793
790
|
}
|
|
794
791
|
.erp-app[data-theme='dark'] .erp-board{
|
|
795
792
|
background:#12141f;
|
|
793
|
+
/* the avatar's seam and dot halo read this, so it must follow the surface */
|
|
794
|
+
--erp-board-bg:#12141f;
|
|
796
795
|
}
|
|
797
796
|
.erp-app[data-theme='dark'] .erp-board::before{display:none}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
}
|
|
797
|
+
/* paired with the light rule above: the divider is off in both themes */
|
|
798
|
+
.erp-app[data-theme='dark'] .t1-rail::after{display:none}
|
|
801
799
|
.erp-app[data-theme='dark'] .rail-group{background:#171a2c;border-color:#242840;box-shadow:0 12px 26px -22px rgba(0,0,0,.8)}
|
|
802
800
|
.erp-app[data-theme='dark'] .rb-name{color:#eef0fa}
|
|
803
801
|
.erp-app[data-theme='dark'] .rail-btn{background:#1d2136;border-color:#2a2f4a;color:#98a0bd}
|
|
804
802
|
.erp-app[data-theme='dark'] .rail-btn:hover{background:#242944;color:#eef0fa;border-color:#3a4166}
|
|
805
|
-
.erp-app[data-theme='dark'] .rail-btn.active{background:#
|
|
803
|
+
.erp-app[data-theme='dark'] .rail-btn.active{background:#5b3cf0;border-color:#5b3cf0;color:#fff;box-shadow:0 10px 18px -12px rgba(91,60,240,.9)}
|
|
806
804
|
.erp-app[data-theme='dark'] .brand-name{color:#f0f2fb}
|
|
807
805
|
.erp-app[data-theme='dark'] .t1-greet b{color:#f0f2fb}
|
|
808
806
|
.erp-app[data-theme='dark'] .t1-greet small{color:#8b93b0}
|
|
@@ -1161,9 +1159,18 @@
|
|
|
1161
1159
|
.erp-app .visa.focus .mini-donut-center{
|
|
1162
1160
|
position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
|
|
1163
1161
|
justify-content:center;gap:2px;text-align:center;pointer-events:none;
|
|
1162
|
+
/* The centre is inset to the RING's inner diameter, not the ring's box: the
|
|
1163
|
+
stroke is 6/44 of the viewBox, so the usable hole is about 68% wide. Text
|
|
1164
|
+
that fills the whole box visually touches the arcs. */
|
|
1165
|
+
padding:0 16%;
|
|
1164
1166
|
}
|
|
1165
1167
|
.erp-app .visa.focus .mini-donut-center b{
|
|
1166
1168
|
font-size:clamp(13px,4cqi,18px);font-weight:800;letter-spacing:-.03em;font-variant-numeric:tabular-nums;
|
|
1169
|
+
/* The hole is roughly half the ring's outer width; anything wider spills
|
|
1170
|
+
across both arcs. compactMoney already shortens the figure, and this is
|
|
1171
|
+
the belt-and-braces so an unexpectedly long string shrinks to fit instead
|
|
1172
|
+
of overflowing. */
|
|
1173
|
+
max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
1167
1174
|
}
|
|
1168
1175
|
.erp-app .visa.focus .mini-donut-center span{
|
|
1169
1176
|
font-size:clamp(7px,2cqi,9px);opacity:.68;font-weight:600;
|
|
@@ -1197,16 +1204,33 @@
|
|
|
1197
1204
|
flex:1 1 0;min-width:0;max-width:none;justify-content:center;
|
|
1198
1205
|
}
|
|
1199
1206
|
|
|
1200
|
-
/* ---------------- donut row:
|
|
1207
|
+
/* ---------------- donut row: full-width ring + legend pair ----------------
|
|
1208
|
+
This row used to inherit max-width:46% from the generic .erp-app .donut
|
|
1209
|
+
rule (line ~441), which was written for the expense card where the ring
|
|
1210
|
+
shares its row with a legend that has its own column. Here it is the ONLY
|
|
1211
|
+
thing in the middle lane, so capping it at 46% left the row 121-218px wide
|
|
1212
|
+
while the card was 304-514px: the ring ate 96px and the legend got 0-30px,
|
|
1213
|
+
i.e. every label ellipsised away. The override below restores the full lane. */
|
|
1201
1214
|
.erp-app .visa.focus .visa-chart.donut{
|
|
1202
|
-
width:100%;justify-content:center;
|
|
1203
|
-
gap:clamp(
|
|
1215
|
+
width:100%;max-width:none;justify-content:center;
|
|
1216
|
+
gap:clamp(20px,4cqi,34px);
|
|
1217
|
+
/* The two floating arrows are positioned against the CARD (right:-13px), not
|
|
1218
|
+
against this row, so the row has to reserve their lane itself or the
|
|
1219
|
+
legend runs underneath the next arrow. The button is 26px and 13px of it
|
|
1220
|
+
hangs outside the card, so 30px of inset keeps ring and legend clear. */
|
|
1221
|
+
padding:0 30px;
|
|
1222
|
+
box-sizing:border-box;
|
|
1204
1223
|
}
|
|
1205
1224
|
.erp-app .visa.focus .mini-donut{
|
|
1206
|
-
width:clamp(
|
|
1225
|
+
width:clamp(96px,32cqi,168px);height:auto;max-height:100%;aspect-ratio:1/1;flex:none;
|
|
1207
1226
|
}
|
|
1227
|
+
/* The legend used to be width-limited to 26cqi, which on a narrow card left it
|
|
1228
|
+
with 9-27px — narrower than its own text, so every row ellipsised to nothing.
|
|
1229
|
+
It now takes the room the ring does not need, with a floor that keeps the
|
|
1230
|
+
label readable and lets the row wrap rather than crush. */
|
|
1208
1231
|
.erp-app .visa.focus .mini-legend{
|
|
1209
|
-
flex:
|
|
1232
|
+
flex:1 1 auto;width:auto;min-width:96px;max-width:min(230px,42cqi);
|
|
1233
|
+
justify-content:center;
|
|
1210
1234
|
}
|
|
1211
1235
|
|
|
1212
1236
|
/* ---------------- card anatomy: header / middle / footer ---------------- */
|
|
@@ -1465,6 +1489,12 @@
|
|
|
1465
1489
|
|
|
1466
1490
|
/* ---------------- nested pages: four KPIs + one section workbench ---------------- */
|
|
1467
1491
|
.erp-app .t1-main[data-shell='true']{grid-template-rows:auto minmax(0,1fr)}
|
|
1492
|
+
/* The team page has no KPI row: it is ONE card that must fill the whole column.
|
|
1493
|
+
It sets data-shell="team" (see shell/index.js), but only [data-shell='true']
|
|
1494
|
+
had a rule here, so the team card fell back to content height and left a
|
|
1495
|
+
large empty band under it. A single minmax row gives it the full height, and
|
|
1496
|
+
align-content must stop being "start" or the row keeps its content height. */
|
|
1497
|
+
.erp-app .t1-main[data-shell='team']{grid-template-rows:minmax(0,1fr);align-content:stretch}
|
|
1468
1498
|
.erp-app .workbench{display:flex;flex-direction:row;min-height:0;overflow:hidden;padding:0}
|
|
1469
1499
|
.erp-app .wb-nav{flex:none;width:200px;border-right:1px solid #f2f3fa;padding:12px 10px;display:flex;flex-direction:column;gap:2px;overflow-y:auto}
|
|
1470
1500
|
.erp-app .wb-hd{font-size:9.6px;font-weight:700;letter-spacing:.05em;color:#a6acc2;padding:2px 8px 8px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
@@ -1881,6 +1911,9 @@
|
|
|
1881
1911
|
.erp-app .bot-avatar{position:relative;width:40px;height:40px;border-radius:13px;flex:none;display:grid;place-items:center;background:linear-gradient(135deg,#3f2fbe,#7c5cff 62%,#c48bfb);color:#fff;font-weight:800;box-shadow:0 8px 18px -8px rgba(91,70,224,.6);transition:transform .2s,box-shadow .2s}
|
|
1882
1912
|
.erp-app .bot-avatar:hover{transform:translateY(-1px);box-shadow:0 10px 22px -8px rgba(91,70,224,.72)}
|
|
1883
1913
|
.erp-app .bot-avatar-t{font-size:14px;letter-spacing:.5px}
|
|
1914
|
+
/* Team avatar uses the users glyph instead of a Latin initial; the shared
|
|
1915
|
+
gradient stays so the team reads as a fifth member of the same family. */
|
|
1916
|
+
.erp-app .bot-avatar.team-avatar svg{width:18px;height:18px}
|
|
1884
1917
|
.erp-app .bot-avatar-state{position:absolute;right:-1px;bottom:-1px;width:10px;height:10px;border-radius:50%;background:#c3c7d6;border:2px solid #fff}
|
|
1885
1918
|
.erp-app .bot-avatar-state.live{background:#22b07d}
|
|
1886
1919
|
.erp-app .bot-avatar-state.warn{background:#e8a13c}
|
|
@@ -1904,10 +1937,35 @@
|
|
|
1904
1937
|
.erp-app .bot-act.on{background:#efecff;color:#5b46e0}
|
|
1905
1938
|
.erp-app .bot-act svg{width:15px;height:15px}
|
|
1906
1939
|
.erp-app .bot-body{position:relative;flex:1;min-height:0;display:flex;flex-direction:column;padding:0}
|
|
1940
|
+
/* Blank session: the official surface (BotChatSurface) stays mounted so the
|
|
1941
|
+
composer — the only input — remains pinned to the bottom. The welcome block
|
|
1942
|
+
is lifted out of the flex flow with absolute positioning, overlaying the
|
|
1943
|
+
empty message area ABOVE the composer instead of splitting the pane in half
|
|
1944
|
+
(which pushed the composer to the middle). */
|
|
1945
|
+
.erp-app .bot-body.is-blank>.bot-welcome{
|
|
1946
|
+
position:absolute;inset:0;padding:16px var(--erp-gutter) 120px;
|
|
1947
|
+
pointer-events:none;z-index:1;
|
|
1948
|
+
}
|
|
1949
|
+
.erp-app .bot-body.is-blank .bot-chips{pointer-events:auto}
|
|
1907
1950
|
.erp-app .bot-conversation{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}
|
|
1908
1951
|
.erp-app .bot-conversation>*{flex:1;min-height:0}
|
|
1909
|
-
|
|
1910
|
-
|
|
1952
|
+
/* Welcome state, centred in the empty conversation area.
|
|
1953
|
+
It used to sit at the very top: .bot-welcome is a sibling of the chat
|
|
1954
|
+
surface inside .bot-body (a column), and both were flex:1 with the
|
|
1955
|
+
welcome block ordered first, so when the session is blank the greeting
|
|
1956
|
+
hugged the header instead of the middle of the pane. Growing it and
|
|
1957
|
+
centring on the cross+main axis puts the orb, name and greeting where an
|
|
1958
|
+
empty conversation expects them — and it collapses back to flex:none as
|
|
1959
|
+
soon as the surface renders real messages. */
|
|
1960
|
+
.erp-app .bot-welcome{
|
|
1961
|
+
/* When the session is blank the chat surface contributes nothing, so the
|
|
1962
|
+
welcome block must take the whole body and centre inside it. It is the
|
|
1963
|
+
only child that grows; the surface stays at content height. */
|
|
1964
|
+
flex:1 1 auto;min-height:0;display:flex;flex-direction:column;
|
|
1965
|
+
align-items:center;justify-content:center;
|
|
1966
|
+
text-align:center;padding:16px var(--erp-gutter);
|
|
1967
|
+
}
|
|
1968
|
+
.erp-app .bot-orb{width:40px;height:40px;margin:0 0 10px;flex:none}
|
|
1911
1969
|
.erp-app .bot-welcome b{display:block;font-size:15px;color:#1a1d38}
|
|
1912
1970
|
.erp-app .bot-welcome-sub{display:block;margin:6px auto 0;max-width:560px;font-size:11.5px;color:#8b90a6;line-height:1.65}
|
|
1913
1971
|
.erp-app .bot-chips{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;margin-top:12px}
|
|
@@ -1940,11 +1998,47 @@
|
|
|
1940
1998
|
.erp-app .bot-tab.on{color:#4a35d0;font-weight:700;border-bottom-color:#6a54f5}
|
|
1941
1999
|
.erp-app .bot-dr-bd{flex:1;min-height:0;overflow:auto;padding:14px var(--erp-gutter)}
|
|
1942
2000
|
.erp-app .bot-dr-ft{flex:none;display:flex;align-items:center;gap:8px;padding:10px 16px;border-top:1px solid #eef0f8;font-size:10.5px;color:#9aa0b4}
|
|
2001
|
+
/* Write-back conflict strip — sits above the footer so it cannot be scrolled
|
|
2002
|
+
out of view while the user is deciding what to do. */
|
|
2003
|
+
.erp-app .bot-conflict{flex:none;padding:9px 16px;background:#fff8ec;border-top:1px solid #f4e4c8}
|
|
2004
|
+
.erp-app .bot-conflict .tx{font-size:10.5px;line-height:1.6;color:#8a5a12}
|
|
2005
|
+
.erp-app .bot-conflict .acts{display:flex;gap:6px;margin-top:7px}
|
|
2006
|
+
.erp-app .bot-conflict button{border-radius:7px;padding:4px 10px;font-size:10px;font-weight:650;cursor:pointer}
|
|
2007
|
+
.erp-app .bot-conflict .p{background:#b5741a;border:0;color:#fff}
|
|
2008
|
+
.erp-app .bot-conflict .p:hover{filter:brightness(1.08)}
|
|
2009
|
+
.erp-app .bot-conflict .g{background:#fff;border:1px solid #e6d4b4;color:#8a5a12}
|
|
2010
|
+
.erp-app .bot-conflict .g:hover{background:#fdf6ea}
|
|
2011
|
+
.erp-app[data-theme='dark'] .bot-conflict{background:#2b2317;border-top-color:#3d3120}
|
|
2012
|
+
.erp-app[data-theme='dark'] .bot-conflict .tx{color:#e0b878}
|
|
2013
|
+
.erp-app[data-theme='dark'] .bot-conflict .p{background:#c8871f;color:#171a2c}
|
|
2014
|
+
.erp-app[data-theme='dark'] .bot-conflict .g{background:#1d2136;border-color:#3d3120;color:#e0b878}
|
|
2015
|
+
.erp-app[data-theme='dark'] .bot-conflict .g:hover{background:#2b2317}
|
|
1943
2016
|
.erp-app .bot-link{margin-left:auto;color:#5b46e0;font-size:11px}
|
|
1944
2017
|
.erp-app .bot-idcard{display:flex;align-items:center;gap:10px;padding:10px;margin-bottom:12px;border:1px solid #efebff;border-radius:12px;background:linear-gradient(135deg,#f8f5ff,#fdfbff)}
|
|
1945
|
-
.
|
|
2018
|
+
/* Identity disc. The glyph used to be a CJK single character (税/账/司/军) set at
|
|
2019
|
+
13px inside 34px, which at this size smears into an unreadable blob — CJK
|
|
2020
|
+
strokes need roughly 20px+ to stay distinct, and there is no room for that
|
|
2021
|
+
here. It is now the bot's Latin initial: one thick stroke, crisp at any size,
|
|
2022
|
+
and it matches the brand avatar's monogram language. */
|
|
2023
|
+
.erp-app .bot-idcard-ava{
|
|
2024
|
+
width:34px;height:34px;border-radius:50%;flex:none;
|
|
2025
|
+
display:grid;place-items:center;color:#fff;
|
|
2026
|
+
font-weight:800;font-size:15px;line-height:1;letter-spacing:0;
|
|
2027
|
+
background:linear-gradient(135deg,#3f2fbe,#7c5cff 58%,#a06fe8);
|
|
2028
|
+
box-shadow:0 6px 14px -8px rgba(91,70,224,.72);
|
|
2029
|
+
}
|
|
2030
|
+
/* Per-bot accent. The accent field is set on every bot in bot-data (aurora-v1..v4) but
|
|
2031
|
+
had no matching rule, so all four identity cards rendered the same gradient. */
|
|
2032
|
+
.erp-app .bot-idcard-ava.aurora-v1{background:linear-gradient(135deg,#2e2a8f,#6a54f5 58%,#a99bff)}
|
|
2033
|
+
.erp-app .bot-idcard-ava.aurora-v2{background:linear-gradient(135deg,#123f6b,#2f8fd0 58%,#7fd3f2)}
|
|
2034
|
+
.erp-app .bot-idcard-ava.aurora-v3{background:linear-gradient(135deg,#5a2a6e,#a24fc4 58%,#e0a3f2)}
|
|
2035
|
+
.erp-app .bot-idcard-ava.aurora-v4{background:linear-gradient(135deg,#2f5a3e,#3f9a6a 58%,#8fd9b0)}
|
|
1946
2036
|
.erp-app .bot-idcard b{display:block;font-size:12px;color:#1a1d38}
|
|
1947
|
-
|
|
2037
|
+
/* Scoped to the text column: as .bot-idcard span this rule also matched the
|
|
2038
|
+
avatar disc (same element type, higher specificity than .bot-idcard-ava),
|
|
2039
|
+
forcing its glyph to 10.5px and adding margin-top:2px — which is why the
|
|
2040
|
+
initial looked like a smeared blob and sat off-centre. */
|
|
2041
|
+
.erp-app .bot-idcard > div span{display:block;font-size:10.5px;color:#8b90a6;margin-top:2px}
|
|
1948
2042
|
.erp-app .bot-field{margin-bottom:11px}
|
|
1949
2043
|
.erp-app .bot-field>label{display:block;font-size:10.5px;color:#8b90a6;margin-bottom:4px}
|
|
1950
2044
|
.erp-app .bot-ro{background:#fafafd;border:1px solid #f0f0f7;border-radius:9px;padding:8px 10px;color:#4a4f66;font-size:11px;line-height:1.6;white-space:pre-wrap}
|
|
@@ -1952,6 +2046,155 @@
|
|
|
1952
2046
|
.erp-app .bot-input:focus{border-color:#c9bcff;box-shadow:0 0 0 3px rgba(106,84,245,.08)}
|
|
1953
2047
|
.erp-app .bot-row{display:flex;gap:10px}
|
|
1954
2048
|
.erp-app .bot-row .bot-field{flex:1;min-width:0}
|
|
2049
|
+
|
|
2050
|
+
/* ================= Bot résumé pane =================
|
|
2051
|
+
The drawer stopped being a flat field list: it now reads AGENTS.md and renders
|
|
2052
|
+
a résumé — qualification blocks drawn from sections 3/5/9, a coverage radar
|
|
2053
|
+
computed from structural counts, and fields that can be edited in place.
|
|
2054
|
+
Everything here is scoped under .bot-sec / .bot-qual / .bot-radar so it cannot
|
|
2055
|
+
leak into other bot-page surfaces. */
|
|
2056
|
+
|
|
2057
|
+
/* sync badge in the identity card */
|
|
2058
|
+
.erp-app .bot-sync{
|
|
2059
|
+
flex:none;display:inline-flex;align-items:center;gap:4px;height:19px;padding:0 7px;
|
|
2060
|
+
border-radius:6px;font-size:9px;font-weight:700;white-space:nowrap;
|
|
2061
|
+
}
|
|
2062
|
+
.erp-app .bot-sync.ok{background:rgba(34,176,125,.14);color:#1a8f66}
|
|
2063
|
+
.erp-app .bot-sync.dirty{background:rgba(240,160,42,.16);color:#a2680f}
|
|
2064
|
+
.erp-app .bot-sync i{width:5px;height:5px;border-radius:50%;background:currentColor;flex:none}
|
|
2065
|
+
.erp-app[data-theme='dark'] .bot-sync.ok{background:rgba(61,220,151,.14);color:#3ddc97}
|
|
2066
|
+
.erp-app[data-theme='dark'] .bot-sync.dirty{background:rgba(240,160,42,.16);color:#f0a02a}
|
|
2067
|
+
|
|
2068
|
+
/* section header with a rule that fills the remaining width */
|
|
2069
|
+
.erp-app .bot-sec-hd{
|
|
2070
|
+
display:flex;align-items:center;gap:7px;margin-bottom:8px;
|
|
2071
|
+
font-size:9.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:#a6acc2;
|
|
2072
|
+
}
|
|
2073
|
+
.erp-app .bot-sec-hd .rule{flex:1;height:1px;background:#eef0f8}
|
|
2074
|
+
.erp-app .bot-sec-hd .cnt{
|
|
2075
|
+
order:2;letter-spacing:0;text-transform:none;font-size:9px;font-weight:600;color:#b0b6c9;
|
|
2076
|
+
}
|
|
2077
|
+
.erp-app[data-theme='dark'] .bot-sec-hd{color:#767e9c}
|
|
2078
|
+
.erp-app[data-theme='dark'] .bot-sec-hd .rule{background:#242840}
|
|
2079
|
+
.erp-app[data-theme='dark'] .bot-sec-hd .cnt{color:#6a7290}
|
|
2080
|
+
|
|
2081
|
+
/* qualification blocks (AGENTS.md sections 3 / 5 / 9) */
|
|
2082
|
+
.erp-app .bot-qual{border:1px solid #eef0f8;border-radius:11px;background:#fafafd;overflow:hidden}
|
|
2083
|
+
.erp-app .bot-qual+.bot-qual{margin-top:7px}
|
|
2084
|
+
.erp-app .bot-qual>summary{
|
|
2085
|
+
display:flex;align-items:center;gap:8px;padding:9px 11px;cursor:pointer;list-style:none;
|
|
2086
|
+
font-size:11px;font-weight:650;color:#3c4157;
|
|
2087
|
+
}
|
|
2088
|
+
.erp-app .bot-qual>summary::-webkit-details-marker{display:none}
|
|
2089
|
+
.erp-app .bot-qual>summary:hover{color:#1a1d38}
|
|
2090
|
+
.erp-app .bot-qual .ico{
|
|
2091
|
+
width:16px;height:16px;border-radius:5px;display:grid;place-items:center;flex:none;
|
|
2092
|
+
background:#efecff;color:#5b46e0;font-size:9px;font-weight:800;
|
|
2093
|
+
}
|
|
2094
|
+
.erp-app .bot-qual .chev{margin-left:auto;color:#a6acc2;font-size:9px;transition:transform .18s}
|
|
2095
|
+
.erp-app .bot-qual[open] .chev{transform:rotate(90deg)}
|
|
2096
|
+
.erp-app .bot-qual .bd{padding:0 11px 11px;font-size:10.5px;line-height:1.75;color:#5f647d;white-space:pre-wrap}
|
|
2097
|
+
.erp-app[data-theme='dark'] .bot-qual{background:#1a1d2e;border-color:#242840}
|
|
2098
|
+
.erp-app[data-theme='dark'] .bot-qual>summary{color:#c9cee6}
|
|
2099
|
+
.erp-app[data-theme='dark'] .bot-qual>summary:hover{color:#eef0fa}
|
|
2100
|
+
.erp-app[data-theme='dark'] .bot-qual .ico{background:#2a2350;color:#c4b6ff}
|
|
2101
|
+
.erp-app[data-theme='dark'] .bot-qual .bd{color:#9aa1bd}
|
|
2102
|
+
|
|
2103
|
+
/* coverage radar */
|
|
2104
|
+
.erp-app .bot-radar{border:1px solid #eef0f8;border-radius:11px;background:#fafafd;padding:11px 11px 8px}
|
|
2105
|
+
.erp-app .bot-radar-top{display:flex;align-items:baseline;gap:8px;margin-bottom:3px}
|
|
2106
|
+
.erp-app .bot-radar-top b{font-size:11px;font-weight:700;color:#1a1d38}
|
|
2107
|
+
.erp-app .bot-radar-top .note{margin-left:auto;font-size:9px;color:#b0b6c9}
|
|
2108
|
+
.erp-app .bot-radar-wrap{display:flex;align-items:center;gap:10px}
|
|
2109
|
+
.erp-app .bot-radar-wrap svg{display:block;flex:none}
|
|
2110
|
+
.erp-app .bot-legend{flex:1;min-width:0;display:flex;flex-direction:column;gap:5px}
|
|
2111
|
+
.erp-app .bot-legend .rl{
|
|
2112
|
+
display:flex;align-items:center;gap:7px;font-size:10.5px;cursor:pointer;
|
|
2113
|
+
border-radius:6px;padding:2px 3px;margin:-2px -3px;
|
|
2114
|
+
}
|
|
2115
|
+
.erp-app .bot-legend .rl:hover,.erp-app .bot-legend .rl.on{background:#f1f2f9}
|
|
2116
|
+
.erp-app .bot-legend .rl .dot{width:5px;height:5px;border-radius:50%;background:#6a54f5;flex:none}
|
|
2117
|
+
.erp-app .bot-legend .rl .n{flex:1;min-width:0;color:#5f647d;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
2118
|
+
.erp-app .bot-legend .rl .v{font-weight:750;color:#1a1d38;font-variant-numeric:tabular-nums}
|
|
2119
|
+
.erp-app .bot-legend .rl .bar{width:30px;height:3px;border-radius:2px;background:#eceef7;overflow:hidden;flex:none}
|
|
2120
|
+
.erp-app .bot-legend .rl .bar i{display:block;height:100%;background:linear-gradient(90deg,#6a54f5,#a98bfb)}
|
|
2121
|
+
.erp-app .bot-evidence{
|
|
2122
|
+
margin-top:6px;padding:8px 9px;border-radius:8px;background:#f4f5fb;border:1px solid #eef0f8;
|
|
2123
|
+
font-size:9.8px;line-height:1.7;color:#767e9c;
|
|
2124
|
+
}
|
|
2125
|
+
.erp-app .bot-evidence b{color:#4a4f66;font-weight:650}
|
|
2126
|
+
.erp-app .radar-fill{fill:rgba(106,84,245,.13);stroke:#6a54f5;stroke-width:1.5;stroke-linejoin:round}
|
|
2127
|
+
.erp-app .radar-grid{fill:none;stroke:#e4e7f3;stroke-width:1}
|
|
2128
|
+
.erp-app .radar-spoke{stroke:#e4e7f3;stroke-width:1}
|
|
2129
|
+
.erp-app .radar-node{fill:#6a54f5;stroke:#fafafd;stroke-width:1.4}
|
|
2130
|
+
.erp-app .radar-cap{font-size:8px;fill:#a6acc2;font-weight:600}
|
|
2131
|
+
.erp-app[data-theme='dark'] .bot-radar{background:#1a1d2e;border-color:#242840}
|
|
2132
|
+
.erp-app[data-theme='dark'] .bot-radar-top b{color:#eef0fa}
|
|
2133
|
+
.erp-app[data-theme='dark'] .bot-radar-top .note{color:#6a7290}
|
|
2134
|
+
.erp-app[data-theme='dark'] .bot-legend .rl:hover,.erp-app[data-theme='dark'] .bot-legend .rl.on{background:#20243a}
|
|
2135
|
+
.erp-app[data-theme='dark'] .bot-legend .rl .n{color:#9aa1bd}
|
|
2136
|
+
.erp-app[data-theme='dark'] .bot-legend .rl .v{color:#eef0fa}
|
|
2137
|
+
.erp-app[data-theme='dark'] .bot-legend .rl .bar{background:#2a2f4a}
|
|
2138
|
+
.erp-app[data-theme='dark'] .bot-evidence{background:#20243a;border-color:#2a2f4a;color:#8b93b0}
|
|
2139
|
+
.erp-app[data-theme='dark'] .bot-evidence b{color:#c9cee6}
|
|
2140
|
+
.erp-app[data-theme='dark'] .radar-grid,.erp-app[data-theme='dark'] .radar-spoke{stroke:#2a2f4a}
|
|
2141
|
+
.erp-app[data-theme='dark'] .radar-node{stroke:#1a1d2e}
|
|
2142
|
+
.erp-app[data-theme='dark'] .radar-cap{fill:#767e9c}
|
|
2143
|
+
|
|
2144
|
+
/* editable identity rows */
|
|
2145
|
+
.erp-app .bot-editable{border:1px solid #eef0f8;border-radius:11px;background:#fafafd;overflow:hidden}
|
|
2146
|
+
.erp-app .bot-erow{display:flex;align-items:flex-start;gap:8px;padding:9px 11px;border-bottom:1px solid #f2f3fa}
|
|
2147
|
+
.erp-app .bot-erow:last-child{border-bottom:0}
|
|
2148
|
+
.erp-app .bot-erow .lab{width:62px;flex:none;font-size:9.5px;color:#a6acc2;font-weight:650;padding-top:1px}
|
|
2149
|
+
.erp-app .bot-erow .val{flex:1;min-width:0;font-size:10.5px;line-height:1.7;color:#4a4f66;white-space:pre-wrap}
|
|
2150
|
+
.erp-app .bot-erow .ed{
|
|
2151
|
+
flex:none;border:0;background:none;color:#6a54f5;cursor:pointer;
|
|
2152
|
+
font-size:9.5px;font-weight:650;padding:0;
|
|
2153
|
+
}
|
|
2154
|
+
.erp-app .bot-erow .ed:hover{text-decoration:underline}
|
|
2155
|
+
.erp-app .bot-erow.edit-on{background:#f4f5fb;display:block}
|
|
2156
|
+
.erp-app .bot-erow textarea{
|
|
2157
|
+
width:100%;min-height:60px;padding:7px 8px;border-radius:8px;border:1px solid #e0e3ef;
|
|
2158
|
+
background:#fff;color:#3c4157;font:inherit;font-size:10.5px;line-height:1.7;resize:vertical;
|
|
2159
|
+
}
|
|
2160
|
+
.erp-app .bot-erow textarea:focus{outline:none;border-color:#c9bcff;box-shadow:0 0 0 3px rgba(106,84,245,.08)}
|
|
2161
|
+
.erp-app .bot-eacts{display:flex;gap:6px;margin-top:6px}
|
|
2162
|
+
.erp-app .bot-eacts .p{
|
|
2163
|
+
border:0;border-radius:7px;padding:5px 11px;font-size:10px;font-weight:650;cursor:pointer;
|
|
2164
|
+
background:#6a54f5;color:#fff;
|
|
2165
|
+
}
|
|
2166
|
+
.erp-app .bot-eacts .p:hover{filter:brightness(1.08)}
|
|
2167
|
+
.erp-app .bot-eacts .g{
|
|
2168
|
+
border:1px solid #e0e3ef;border-radius:7px;padding:5px 11px;font-size:10px;font-weight:650;
|
|
2169
|
+
cursor:pointer;background:#fff;color:#5f647d;
|
|
2170
|
+
}
|
|
2171
|
+
.erp-app .bot-eacts .g:hover{color:#1a1d38}
|
|
2172
|
+
.erp-app[data-theme='dark'] .bot-editable{background:#1a1d2e;border-color:#242840}
|
|
2173
|
+
.erp-app[data-theme='dark'] .bot-erow{border-bottom-color:#242840}
|
|
2174
|
+
.erp-app[data-theme='dark'] .bot-erow .lab{color:#6a7290}
|
|
2175
|
+
.erp-app[data-theme='dark'] .bot-erow .val{color:#b6bdcf}
|
|
2176
|
+
.erp-app[data-theme='dark'] .bot-erow.edit-on{background:#20243a}
|
|
2177
|
+
.erp-app[data-theme='dark'] .bot-erow textarea{background:#141726;border-color:#2f3550;color:#e7e9f6}
|
|
2178
|
+
.erp-app[data-theme='dark'] .bot-eacts .g{background:#20243a;border-color:#2f3550;color:#9aa1bd}
|
|
2179
|
+
.erp-app[data-theme='dark'] .bot-eacts .g:hover{color:#eef0fa}
|
|
2180
|
+
|
|
2181
|
+
/* red-line list (append-only) */
|
|
2182
|
+
.erp-app .bot-reds{border:1px solid #eef0f8;border-radius:11px;background:#fafafd;overflow:hidden}
|
|
2183
|
+
.erp-app .bot-reds ul{margin:0;padding:10px 11px 4px 26px;font-size:10.5px;line-height:1.9;color:#4a4f66}
|
|
2184
|
+
.erp-app .bot-reds li::marker{color:#e25563}
|
|
2185
|
+
.erp-app .bot-reds .ft{padding:0 11px 11px}
|
|
2186
|
+
.erp-app .bot-tags{display:flex;flex-wrap:wrap;gap:5px;margin-top:7px}
|
|
2187
|
+
.erp-app .bot-tags .tag{
|
|
2188
|
+
border:1px dashed #dfe2ee;background:none;color:#8b90a6;border-radius:6px;
|
|
2189
|
+
padding:3px 7px;font-size:9px;cursor:pointer;
|
|
2190
|
+
}
|
|
2191
|
+
.erp-app .bot-tags .tag:hover{border-color:#6a54f5;color:#5b46e0;background:#f6f3ff}
|
|
2192
|
+
.erp-app .bot-tags .tag:disabled{opacity:.45;cursor:not-allowed;border-style:solid;color:#22b07d;border-color:#c9ead9}
|
|
2193
|
+
.erp-app[data-theme='dark'] .bot-reds{background:#1a1d2e;border-color:#242840}
|
|
2194
|
+
.erp-app[data-theme='dark'] .bot-reds ul{color:#b6bdcf}
|
|
2195
|
+
.erp-app[data-theme='dark'] .bot-tags .tag{border-color:#2f3550;color:#8b93b0}
|
|
2196
|
+
.erp-app[data-theme='dark'] .bot-tags .tag:hover{background:#2a2350;border-color:#6a54f5;color:#c4b6ff}
|
|
2197
|
+
.erp-app[data-theme='dark'] .bot-tags .tag:disabled{color:#3ddc97;border-color:#1d4a38}
|
|
1955
2198
|
.erp-app .bot-hint{font-size:10.5px;color:#9aa0b4;line-height:1.6;margin-top:6px}
|
|
1956
2199
|
.erp-app .bot-skills-hd{display:flex;align-items:center;font-size:11px;color:#8b90a6;margin-bottom:6px}
|
|
1957
2200
|
.erp-app .bot-skills-src{margin-left:auto;color:#9aa0b4;font-size:10px}
|
|
@@ -2069,15 +2312,18 @@
|
|
|
2069
2312
|
@container erp (max-width:560px){
|
|
2070
2313
|
.erp-app .bot-role,.erp-app .bot-welcome-sub{display:none}
|
|
2071
2314
|
.erp-app .bot-avatar{width:34px;height:34px;border-radius:11px}
|
|
2072
|
-
.erp-app .bot-welcome{padding:12px var(--erp-gutter)
|
|
2315
|
+
.erp-app .bot-welcome{padding:12px var(--erp-gutter)}
|
|
2073
2316
|
}
|
|
2074
2317
|
@container board (max-height:560px){
|
|
2075
|
-
.erp-app .bot-welcome{padding:10px var(--erp-gutter)
|
|
2318
|
+
.erp-app .bot-welcome{padding:10px var(--erp-gutter)}
|
|
2076
2319
|
.erp-app .bot-orb{width:30px;height:30px;margin-bottom:4px}
|
|
2077
2320
|
.erp-app .bot-welcome-sub{display:none}
|
|
2078
2321
|
}
|
|
2079
2322
|
|
|
2080
2323
|
.erp-app[data-theme='dark'] .bot-hd,.erp-app[data-theme='dark'] .bot-dr-hd,.erp-app[data-theme='dark'] .bot-tabs,.erp-app[data-theme='dark'] .bot-dr-ft{border-color:#242840}
|
|
2324
|
+
.erp-app[data-theme='dark'] .bot-tab{color:#8b93b0}
|
|
2325
|
+
.erp-app[data-theme='dark'] .bot-tab:hover{color:#c9cee6}
|
|
2326
|
+
.erp-app[data-theme='dark'] .bot-tab.on{color:#c4b6ff;border-bottom-color:#8c72ff}
|
|
2081
2327
|
.erp-app[data-theme='dark'] .bot-name b,.erp-app[data-theme='dark'] .bot-welcome b,.erp-app[data-theme='dark'] .bot-idcard b,.erp-app[data-theme='dark'] .bot-dr-hd b,.erp-app[data-theme='dark'] .bot-cv-hd b,.erp-app[data-theme='dark'] .bot-cv-chip b,.erp-app[data-theme='dark'] .bot-skill-top b,.erp-app[data-theme='dark'] .bot-rel b{color:#eef0fa}
|
|
2082
2328
|
.erp-app[data-theme='dark'] .bot-role,.erp-app[data-theme='dark'] .bot-session-title,.erp-app[data-theme='dark'] .bot-welcome-sub,.erp-app[data-theme='dark'] .bot-hint,.erp-app[data-theme='dark'] .bot-empty{color:#9aa0b4}
|
|
2083
2329
|
.erp-app[data-theme='dark'] .bot-avatar-state{border-color:#171a2c}
|
|
@@ -2125,6 +2371,15 @@
|
|
|
2125
2371
|
.erp-app[data-theme='dark'] .rail-hub.on .rail-hub-ico,.erp-app[data-theme='dark'] .rail-base.on .rail-base-ico{background:#2a2350;border-color:#3b3470;color:#c4b6ff}
|
|
2126
2372
|
.erp-app[data-theme='dark'] .t1-rail.open .rail-bot .rail-label,.erp-app[data-theme='dark'] .t1-rail.open .rail-base .rail-label{color:#e6e8f5}
|
|
2127
2373
|
.erp-app[data-theme='dark'] .rail-divider{background:#2a2e48}
|
|
2374
|
+
/* The separator has to lift, not darken, on the dark card: a dimmer line would
|
|
2375
|
+
disappear into #171a2c, so it uses white at low alpha like the other edges. */
|
|
2376
|
+
.erp-app[data-theme='dark'] .rail-sep{
|
|
2377
|
+
background:linear-gradient(90deg,
|
|
2378
|
+
rgba(255,255,255,0) 0%,
|
|
2379
|
+
rgba(255,255,255,.22) 16%,
|
|
2380
|
+
rgba(255,255,255,.22) 84%,
|
|
2381
|
+
rgba(255,255,255,0) 100%);
|
|
2382
|
+
}
|
|
2128
2383
|
.erp-app[data-theme='dark'] .t1-rail.open .rail-bot.on{background:#1d2136}
|
|
2129
2384
|
.erp-app[data-theme='dark'] .rail-bot-dot{border-color:#171a2c}
|
|
2130
2385
|
.erp-app[data-theme='dark'] .rail-me-txt b{color:#eef0fa}
|
|
@@ -2141,6 +2396,10 @@
|
|
|
2141
2396
|
--erp-rail-btn:38px; /* icon button = brand tile footprint */
|
|
2142
2397
|
--erp-rail-pad:7px; /* white group's inner padding */
|
|
2143
2398
|
--erp-rail-gutter:8px; /* whitespace outside the group, both sides */
|
|
2399
|
+
/* The board's own surface colour, named so the brand avatar's seam and its
|
|
2400
|
+
status-dot halo can punch back to the page instead of hard-coding white
|
|
2401
|
+
(which shows as a bright ring in dark mode). Overridden in the dark block. */
|
|
2402
|
+
--erp-board-bg:#f6f7fb;
|
|
2144
2403
|
--erp-rail-w:calc(var(--erp-rail-btn) + var(--erp-rail-pad) * 2 + 2px + var(--erp-rail-gutter) * 2);
|
|
2145
2404
|
grid-template-columns:var(--erp-rail-w) minmax(0,1fr);
|
|
2146
2405
|
}
|
|
@@ -2149,29 +2408,72 @@
|
|
|
2149
2408
|
padding:12px var(--erp-rail-gutter) 18px;overflow:hidden;
|
|
2150
2409
|
background:transparent;box-shadow:none;border-right:0;border-radius:0;transition:none;
|
|
2151
2410
|
}
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2411
|
+
/* paired with the rule at the top of the file: the divider is off */
|
|
2412
|
+
.erp-app .t1-rail::after{display:none}
|
|
2413
|
+
/* ---------------- brand avatar (rail head) ----------------
|
|
2414
|
+
Three nested discs: ring → board-coloured seam → gradient face. The brand
|
|
2415
|
+
and the active page both used to be full-bleed saturated blocks, so they
|
|
2416
|
+
fought for the same focal slot; now the brand owns a structural signature
|
|
2417
|
+
(a ring) that no navigation button has, and colour only has to carry theme.
|
|
2418
|
+
|
|
2419
|
+
Sized off --erp-rail-btn so the footprint (46px) always fits the rail's
|
|
2420
|
+
54px content box and rescales with the existing breakpoints. */
|
|
2421
|
+
.erp-app .rail-brand-slot{
|
|
2422
|
+
flex:none;width:100%;display:flex;justify-content:center;
|
|
2423
|
+
margin:2px 0 10px;
|
|
2424
|
+
}
|
|
2425
|
+
.erp-app .brand-av{
|
|
2426
|
+
--brand-ring:2px;
|
|
2427
|
+
--brand-gap:2px;
|
|
2428
|
+
position:relative;display:grid;place-items:center;
|
|
2429
|
+
width:calc(var(--erp-rail-btn) + (var(--brand-ring) + var(--brand-gap)) * 2);
|
|
2430
|
+
height:calc(var(--erp-rail-btn) + (var(--brand-ring) + var(--brand-gap)) * 2);
|
|
2431
|
+
border-radius:50%;
|
|
2167
2432
|
background:linear-gradient(140deg,#3f2fbe,#7c5cff 52%,#c48bfb);
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
}
|
|
2171
|
-
.erp-app
|
|
2172
|
-
|
|
2173
|
-
|
|
2433
|
+
box-shadow:0 8px 18px -12px rgba(94,72,232,.8);
|
|
2434
|
+
cursor:pointer;transition:transform .2s,box-shadow .2s,filter .2s;
|
|
2435
|
+
}
|
|
2436
|
+
.erp-app .brand-av:hover{transform:scale(1.04);filter:brightness(1.06)}
|
|
2437
|
+
.erp-app .brand-av:focus-visible{
|
|
2438
|
+
outline:none;
|
|
2439
|
+
box-shadow:0 0 0 2px var(--erp-board-bg),0 0 0 4px #7c5cff;
|
|
2440
|
+
}
|
|
2441
|
+
.erp-app .brand-av-gap{
|
|
2442
|
+
width:calc(100% - var(--brand-ring) * 2);
|
|
2443
|
+
height:calc(100% - var(--brand-ring) * 2);
|
|
2444
|
+
border-radius:50%;background:var(--erp-board-bg);
|
|
2445
|
+
display:grid;place-items:center;
|
|
2174
2446
|
}
|
|
2447
|
+
/* The face's light end stops at #a06fe8 rather than #c48bfb: white on
|
|
2448
|
+
#c48bfb is only about 2.5:1, below even the 3:1 large-text floor. */
|
|
2449
|
+
.erp-app .brand-av-face{
|
|
2450
|
+
width:calc(100% - var(--brand-gap) * 2);
|
|
2451
|
+
height:calc(100% - var(--brand-gap) * 2);
|
|
2452
|
+
border-radius:50%;display:grid;place-items:center;color:#fff;
|
|
2453
|
+
background:linear-gradient(140deg,#241a7a,#5b3cf0 50%,#a06fe8);
|
|
2454
|
+
font-size:calc(var(--erp-rail-btn) * .58);font-weight:800;line-height:1;
|
|
2455
|
+
letter-spacing:.02em;
|
|
2456
|
+
box-shadow:inset 0 1px 0 rgba(255,255,255,.26),inset 0 -8px 14px rgba(16,18,40,.18);
|
|
2457
|
+
}
|
|
2458
|
+
/* The dot is anchored on the ring at 45 degrees, not on the box corner.
|
|
2459
|
+
Geometry: outer radius R = btn/2 + (ring + gap) = btn/2 + 4. The ring's
|
|
2460
|
+
midline is one half-ring inside that, R - ring/2 = btn/2 + 3, and the dot's
|
|
2461
|
+
CENTRE must land there along the diagonal, i.e. (btn/2 + 3)/sqrt(2) per axis.
|
|
2462
|
+
Converting centre to box edge collapses to 0.0415*btn + 1.88px for
|
|
2463
|
+
ring = gap = 2. Written proportionally instead of a fixed 4px so the dot
|
|
2464
|
+
stays on the ring at 38 / 34 / 30 — a hard-coded inset drifts inward and
|
|
2465
|
+
ends up half on the face at btn 30. */
|
|
2466
|
+
.erp-app .brand-dot{
|
|
2467
|
+
position:absolute;
|
|
2468
|
+
width:calc(var(--erp-rail-btn) * .21);
|
|
2469
|
+
height:calc(var(--erp-rail-btn) * .21);
|
|
2470
|
+
right:calc(var(--erp-rail-btn) * .0415 + 1.88px);
|
|
2471
|
+
bottom:calc(var(--erp-rail-btn) * .0415 + 1.88px);
|
|
2472
|
+
border-radius:50%;
|
|
2473
|
+
border:2px solid var(--erp-board-bg);
|
|
2474
|
+
background:#22c55e;
|
|
2475
|
+
}
|
|
2476
|
+
.erp-app .brand-dot.busy{background:#f0a02a}
|
|
2175
2477
|
/* Foot lockup and brand tile share the group's width, so the column reads as
|
|
2176
2478
|
one continuous stack: B tile → icon group → wordmark → 中/EN pill. */
|
|
2177
2479
|
.erp-app .rail-brand{width:100%;height:36px;display:flex;align-items:center;justify-content:center;gap:0;padding:0}
|
|
@@ -2188,13 +2490,26 @@
|
|
|
2188
2490
|
dead white gutter. */
|
|
2189
2491
|
.erp-app .rail-group{width:100%;display:flex;flex-direction:column;align-items:center;gap:7px;padding:var(--erp-rail-pad);border-radius:24px;background:#fff;border:1px solid #f1f2f9;box-shadow:0 12px 26px -22px rgba(24,30,66,.7),0 1px 2px rgba(24,30,66,.035)}
|
|
2190
2492
|
.erp-app .rail-group + .rail-group{margin-top:12px}
|
|
2191
|
-
.erp-app .t1-rail > .rail-group
|
|
2192
|
-
.erp-app .t1-rail > .rail-group
|
|
2493
|
+
.erp-app .t1-rail > .rail-group{flex:0 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden}
|
|
2494
|
+
.erp-app .t1-rail > .rail-group::-webkit-scrollbar{width:0;height:0}
|
|
2193
2495
|
.erp-app .rail-foot{width:100%;flex:none;display:flex;flex-direction:column;align-items:center;gap:7px;margin-bottom:2px}
|
|
2194
2496
|
.erp-app .rail-spacer{flex:1 1 auto;min-height:0}
|
|
2195
2497
|
.erp-app .rail-btn{width:var(--erp-rail-btn);height:var(--erp-rail-btn);border-radius:50%;display:grid;place-items:center;background:#f3f4f9;border:1px solid #e9ebf3;color:#828aa3}
|
|
2196
2498
|
.erp-app .rail-btn.active{background:#151933;color:#fff;border-color:#151933;box-shadow:0 10px 20px -10px rgba(21,25,51,.6)}
|
|
2197
2499
|
.erp-app .rail-btn svg{width:18px;height:18px}
|
|
2500
|
+
/* Group separator inside the icon card: a short hairline that marks where the
|
|
2501
|
+
business pages end and the infrastructure page (底座) begins. It is inset
|
|
2502
|
+
rather than full width so it reads as a grouping mark, not as a card edge,
|
|
2503
|
+
and it borrows the group's own padding so it never touches the card sides. */
|
|
2504
|
+
.erp-app .rail-sep{
|
|
2505
|
+
display:block;flex:none;width:calc(100% - 12px);
|
|
2506
|
+
height:1px;margin:6px 0;border-radius:1px;
|
|
2507
|
+
background:linear-gradient(90deg,
|
|
2508
|
+
rgba(122,134,166,0) 0%,
|
|
2509
|
+
rgba(122,134,166,.72) 16%,
|
|
2510
|
+
rgba(122,134,166,.72) 84%,
|
|
2511
|
+
rgba(122,134,166,0) 100%);
|
|
2512
|
+
}
|
|
2198
2513
|
/* The language pill fills the rail as well: its two segments split the width
|
|
2199
2514
|
evenly, so it no longer sits narrower than the blocks stacked above it. */
|
|
2200
2515
|
.erp-app .rail-lang{display:flex;width:100%;justify-content:center;gap:2px;padding:2px}
|
|
@@ -2207,13 +2522,16 @@
|
|
|
2207
2522
|
@container erp (max-width:900px){
|
|
2208
2523
|
.erp-app .erp-board{--erp-rail-btn:34px}
|
|
2209
2524
|
.erp-app .t1-rail{padding-bottom:12px}
|
|
2210
|
-
|
|
2525
|
+
/* Below this size the monogram stops counting as WCAG large text, so the 3:1
|
|
2526
|
+
large-text floor becomes a 4.5:1 body-text floor: flatten the light end of
|
|
2527
|
+
the face gradient to keep white legible instead of just shrinking it. */
|
|
2528
|
+
.erp-app .brand-av-face{background:linear-gradient(140deg,#241a7a,#4d31d8 60%,#7d54e0)}
|
|
2211
2529
|
.erp-app .rail-btn svg{width:16px;height:16px}
|
|
2212
2530
|
.erp-app .rail-lang .lang-seg{font-size:8.4px}
|
|
2213
2531
|
.erp-app .rail-greet{display:none}
|
|
2214
2532
|
}
|
|
2215
2533
|
@container erp (max-width:560px){
|
|
2216
2534
|
.erp-app .erp-board{--erp-rail-btn:30px;--erp-rail-pad:6px}
|
|
2217
|
-
.erp-app .
|
|
2535
|
+
.erp-app .brand-av{--brand-ring:1.5px;--brand-gap:1.5px}
|
|
2218
2536
|
.erp-app .rail-group{border-radius:20px}
|
|
2219
2537
|
}`
|